diff options
Diffstat (limited to 'drivers/tty/serial')
51 files changed, 2040 insertions, 456 deletions
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index c33e02cbde93..61550f24a2d3 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -28,6 +28,10 @@ #define ASPEED_VUART_ADDRL 0x28 #define ASPEED_VUART_ADDRH 0x2c +#define ASPEED_VUART_DEFAULT_LPC_ADDR 0x3f8 +#define ASPEED_VUART_DEFAULT_SIRQ 4 +#define ASPEED_VUART_DEFAULT_SIRQ_POLARITY IRQ_TYPE_LEVEL_LOW + struct aspeed_vuart { struct device *dev; void __iomem *regs; @@ -72,22 +76,31 @@ static ssize_t lpc_address_show(struct device *dev, return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr); } +static int aspeed_vuart_set_lpc_address(struct aspeed_vuart *vuart, u32 addr) +{ + if (addr > U16_MAX) + return -EINVAL; + + writeb(addr >> 8, vuart->regs + ASPEED_VUART_ADDRH); + writeb(addr >> 0, vuart->regs + ASPEED_VUART_ADDRL); + + return 0; +} + static ssize_t lpc_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aspeed_vuart *vuart = dev_get_drvdata(dev); - unsigned long val; + u32 val; int err; - err = kstrtoul(buf, 0, &val); + err = kstrtou32(buf, 0, &val); if (err) return err; - writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH); - writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL); - - return count; + err = aspeed_vuart_set_lpc_address(vuart, val); + return err ? : count; } static DEVICE_ATTR_RW(lpc_address); @@ -105,27 +118,37 @@ static ssize_t sirq_show(struct device *dev, return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg); } +static int aspeed_vuart_set_sirq(struct aspeed_vuart *vuart, u32 sirq) +{ + u8 reg; + + if (sirq > (ASPEED_VUART_GCRB_HOST_SIRQ_MASK >> ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT)) + return -EINVAL; + + sirq <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + sirq &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + + reg = readb(vuart->regs + ASPEED_VUART_GCRB); + reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg |= sirq; + writeb(reg, vuart->regs + ASPEED_VUART_GCRB); + + return 0; +} + static ssize_t sirq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aspeed_vuart *vuart = dev_get_drvdata(dev); unsigned long val; int err; - u8 reg; err = kstrtoul(buf, 0, &val); if (err) return err; - val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; - val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; - - reg = readb(vuart->regs + ASPEED_VUART_GCRB); - reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; - reg |= val; - writeb(reg, vuart->regs + ASPEED_VUART_GCRB); - - return count; + err = aspeed_vuart_set_sirq(vuart, val); + return err ? : count; } static DEVICE_ATTR_RW(sirq); @@ -297,7 +320,6 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); unsigned int iir, lsr; - unsigned long flags; int space, count; iir = serial_port_in(port, UART_IIR); @@ -305,7 +327,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) if (iir & UART_IIR_NO_INT) return 0; - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); lsr = serial_port_in(port, UART_LSR); @@ -341,7 +363,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) if (lsr & UART_LSR_THRE) serial8250_tx_chars(up); - uart_unlock_and_check_sysrq(port, flags); + uart_unlock_and_check_sysrq(port); return 1; } @@ -367,6 +389,18 @@ static void aspeed_vuart_auto_configure_sirq_polarity( aspeed_vuart_set_sirq_polarity(vuart, (value & reg_mask) == 0); } +static int aspeed_vuart_map_irq_polarity(u32 dt) +{ + switch (dt) { + case IRQ_TYPE_LEVEL_LOW: + return 0; + case IRQ_TYPE_LEVEL_HIGH: + return 1; + default: + return -EINVAL; + } +} + static int aspeed_vuart_probe(struct platform_device *pdev) { struct of_phandle_args sirq_polarity_sense_args; @@ -374,8 +408,8 @@ static int aspeed_vuart_probe(struct platform_device *pdev) struct aspeed_vuart *vuart; struct device_node *np; struct resource *res; - u32 clk, prop; - int rc; + u32 clk, prop, sirq[2]; + int rc, sirq_polarity; np = pdev->dev.of_node; @@ -482,6 +516,37 @@ static int aspeed_vuart_probe(struct platform_device *pdev) of_node_put(sirq_polarity_sense_args.np); } + rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &prop); + if (rc < 0) + prop = ASPEED_VUART_DEFAULT_LPC_ADDR; + + rc = aspeed_vuart_set_lpc_address(vuart, prop); + if (rc < 0) { + dev_err(&pdev->dev, "invalid value in aspeed,lpc-io-reg property\n"); + goto err_clk_disable; + } + + rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", sirq, 2); + if (rc < 0) { + sirq[0] = ASPEED_VUART_DEFAULT_SIRQ; + sirq[1] = ASPEED_VUART_DEFAULT_SIRQ_POLARITY; + } + + rc = aspeed_vuart_set_sirq(vuart, sirq[0]); + if (rc < 0) { + dev_err(&pdev->dev, "invalid sirq number in aspeed,lpc-interrupts property\n"); + goto err_clk_disable; + } + + sirq_polarity = aspeed_vuart_map_irq_polarity(sirq[1]); + if (sirq_polarity < 0) { + dev_err(&pdev->dev, "invalid sirq polarity in aspeed,lpc-interrupts property\n"); + rc = sirq_polarity; + goto err_clk_disable; + } + + aspeed_vuart_set_sirq_polarity(vuart, sirq_polarity); + aspeed_vuart_set_enabled(vuart, true); aspeed_vuart_set_host_tx_discard(vuart, true); platform_set_drvdata(pdev, vuart); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c new file mode 100644 index 000000000000..725a450058f8 --- /dev/null +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -0,0 +1,1202 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, Broadcom */ +/* + * 8250-core based driver for Broadcom ns16550a UARTs + * + * This driver uses the standard 8250 driver core but adds additional + * optional features including the ability to use a baud rate clock + * mux for more accurate high speed baud rate selection and also + * an optional DMA engine. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/dma-mapping.h> +#include <linux/tty_flip.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/debugfs.h> + +#include "8250.h" + +/* Register definitions for UART DMA block. Version 1.1 or later. */ +#define UDMA_ARB_RX 0x00 +#define UDMA_ARB_TX 0x04 +#define UDMA_ARB_REQ 0x00000001 +#define UDMA_ARB_GRANT 0x00000002 + +#define UDMA_RX_REVISION 0x00 +#define UDMA_RX_REVISION_REQUIRED 0x00000101 +#define UDMA_RX_CTRL 0x04 +#define UDMA_RX_CTRL_BUF_CLOSE_MODE 0x00010000 +#define UDMA_RX_CTRL_MASK_WR_DONE 0x00008000 +#define UDMA_RX_CTRL_ENDIAN_OVERRIDE 0x00004000 +#define UDMA_RX_CTRL_ENDIAN 0x00002000 +#define UDMA_RX_CTRL_OE_IS_ERR 0x00001000 +#define UDMA_RX_CTRL_PE_IS_ERR 0x00000800 +#define UDMA_RX_CTRL_FE_IS_ERR 0x00000400 +#define UDMA_RX_CTRL_NUM_BUF_USED_MASK 0x000003c0 +#define UDMA_RX_CTRL_NUM_BUF_USED_SHIFT 6 +#define UDMA_RX_CTRL_BUF_CLOSE_CLK_SEL_SYS 0x00000020 +#define UDMA_RX_CTRL_BUF_CLOSE_ENA 0x00000010 +#define UDMA_RX_CTRL_TIMEOUT_CLK_SEL_SYS 0x00000008 +#define UDMA_RX_CTRL_TIMEOUT_ENA 0x00000004 +#define UDMA_RX_CTRL_ABORT 0x00000002 +#define UDMA_RX_CTRL_ENA 0x00000001 +#define UDMA_RX_STATUS 0x08 +#define UDMA_RX_STATUS_ACTIVE_BUF_MASK 0x0000000f +#define UDMA_RX_TRANSFER_LEN 0x0c +#define UDMA_RX_TRANSFER_TOTAL 0x10 +#define UDMA_RX_BUFFER_SIZE 0x14 +#define UDMA_RX_SRC_ADDR 0x18 +#define UDMA_RX_TIMEOUT 0x1c +#define UDMA_RX_BUFFER_CLOSE 0x20 +#define UDMA_RX_BLOCKOUT_COUNTER 0x24 +#define UDMA_RX_BUF0_PTR_LO 0x28 +#define UDMA_RX_BUF0_PTR_HI 0x2c +#define UDMA_RX_BUF0_STATUS 0x30 +#define UDMA_RX_BUFX_STATUS_OVERRUN_ERR 0x00000010 +#define UDMA_RX_BUFX_STATUS_FRAME_ERR 0x00000008 +#define UDMA_RX_BUFX_STATUS_PARITY_ERR 0x00000004 +#define UDMA_RX_BUFX_STATUS_CLOSE_EXPIRED 0x00000002 +#define UDMA_RX_BUFX_STATUS_DATA_RDY 0x00000001 +#define UDMA_RX_BUF0_DATA_LEN 0x34 +#define UDMA_RX_BUF1_PTR_LO 0x38 +#define UDMA_RX_BUF1_PTR_HI 0x3c +#define UDMA_RX_BUF1_STATUS 0x40 +#define UDMA_RX_BUF1_DATA_LEN 0x44 + +#define UDMA_TX_REVISION 0x00 +#define UDMA_TX_REVISION_REQUIRED 0x00000101 +#define UDMA_TX_CTRL 0x04 +#define UDMA_TX_CTRL_ENDIAN_OVERRIDE 0x00000080 +#define UDMA_TX_CTRL_ENDIAN 0x00000040 +#define UDMA_TX_CTRL_NUM_BUF_USED_MASK 0x00000030 +#define UDMA_TX_CTRL_NUM_BUF_USED_1 0x00000010 +#define UDMA_TX_CTRL_ABORT 0x00000002 +#define UDMA_TX_CTRL_ENA 0x00000001 +#define UDMA_TX_DST_ADDR 0x08 +#define UDMA_TX_BLOCKOUT_COUNTER 0x10 +#define UDMA_TX_TRANSFER_LEN 0x14 +#define UDMA_TX_TRANSFER_TOTAL 0x18 +#define UDMA_TX_STATUS 0x20 +#define UDMA_TX_BUF0_PTR_LO 0x24 +#define UDMA_TX_BUF0_PTR_HI 0x28 +#define UDMA_TX_BUF0_STATUS 0x2c +#define UDMA_TX_BUFX_LAST 0x00000002 +#define UDMA_TX_BUFX_EMPTY 0x00000001 +#define UDMA_TX_BUF0_DATA_LEN 0x30 +#define UDMA_TX_BUF0_DATA_SENT 0x34 +#define UDMA_TX_BUF1_PTR_LO 0x38 + +#define UDMA_INTR_STATUS 0x00 +#define UDMA_INTR_ARB_TX_GRANT 0x00040000 +#define UDMA_INTR_ARB_RX_GRANT 0x00020000 +#define UDMA_INTR_TX_ALL_EMPTY 0x00010000 +#define UDMA_INTR_TX_EMPTY_BUF1 0x00008000 +#define UDMA_INTR_TX_EMPTY_BUF0 0x00004000 +#define UDMA_INTR_TX_ABORT 0x00002000 +#define UDMA_INTR_TX_DONE 0x00001000 +#define UDMA_INTR_RX_ERROR 0x00000800 +#define UDMA_INTR_RX_TIMEOUT 0x00000400 +#define UDMA_INTR_RX_READY_BUF7 0x00000200 +#define UDMA_INTR_RX_READY_BUF6 0x00000100 +#define UDMA_INTR_RX_READY_BUF5 0x00000080 +#define UDMA_INTR_RX_READY_BUF4 0x00000040 +#define UDMA_INTR_RX_READY_BUF3 0x00000020 +#define UDMA_INTR_RX_READY_BUF2 0x00000010 +#define UDMA_INTR_RX_READY_BUF1 0x00000008 +#define UDMA_INTR_RX_READY_BUF0 0x00000004 +#define UDMA_INTR_RX_READY_MASK 0x000003fc +#define UDMA_INTR_RX_READY_SHIFT 2 +#define UDMA_INTR_RX_ABORT 0x00000002 +#define UDMA_INTR_RX_DONE 0x00000001 +#define UDMA_INTR_SET 0x04 +#define UDMA_INTR_CLEAR 0x08 +#define UDMA_INTR_MASK_STATUS 0x0c +#define UDMA_INTR_MASK_SET 0x10 +#define UDMA_INTR_MASK_CLEAR 0x14 + + +#define UDMA_RX_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_READY_BUF0 | \ + UDMA_INTR_RX_READY_BUF1 | \ + UDMA_INTR_RX_READY_BUF2 | \ + UDMA_INTR_RX_READY_BUF3 | \ + UDMA_INTR_RX_READY_BUF4 | \ + UDMA_INTR_RX_READY_BUF5 | \ + UDMA_INTR_RX_READY_BUF6 | \ + UDMA_INTR_RX_READY_BUF7 | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_RX_ERR_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_TX_INTERRUPTS ( \ + UDMA_INTR_TX_ABORT | \ + UDMA_INTR_TX_DONE) + +#define UDMA_IS_RX_INTERRUPT(status) ((status) & UDMA_RX_INTERRUPTS) +#define UDMA_IS_TX_INTERRUPT(status) ((status) & UDMA_TX_INTERRUPTS) + + +/* Current devices have 8 sets of RX buffer registers */ +#define UDMA_RX_BUFS_COUNT 8 +#define UDMA_RX_BUFS_REG_OFFSET (UDMA_RX_BUF1_PTR_LO - UDMA_RX_BUF0_PTR_LO) +#define UDMA_RX_BUFx_PTR_LO(x) (UDMA_RX_BUF0_PTR_LO + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_PTR_HI(x) (UDMA_RX_BUF0_PTR_HI + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_STATUS(x) (UDMA_RX_BUF0_STATUS + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_DATA_LEN(x) (UDMA_RX_BUF0_DATA_LEN + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) + +/* Current devices have 2 sets of TX buffer registers */ +#define UDMA_TX_BUFS_COUNT 2 +#define UDMA_TX_BUFS_REG_OFFSET (UDMA_TX_BUF1_PTR_LO - UDMA_TX_BUF0_PTR_LO) +#define UDMA_TX_BUFx_PTR_LO(x) (UDMA_TX_BUF0_PTR_LO + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_PTR_HI(x) (UDMA_TX_BUF0_PTR_HI + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_STATUS(x) (UDMA_TX_BUF0_STATUS + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_LEN(x) (UDMA_TX_BUF0_DATA_LEN + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_SENT(x) (UDMA_TX_BUF0_DATA_SENT + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define REGS_8250 0 +#define REGS_DMA_RX 1 +#define REGS_DMA_TX 2 +#define REGS_DMA_ISR 3 +#define REGS_DMA_ARB 4 +#define REGS_MAX 5 + +#define TX_BUF_SIZE 4096 +#define RX_BUF_SIZE 4096 +#define RX_BUFS_COUNT 2 +#define KHZ 1000 +#define MHZ(x) ((x) * KHZ * KHZ) + +static const u32 brcmstb_rate_table[] = { + MHZ(81), + MHZ(108), + MHZ(64), /* Actually 64285715 for some chips */ + MHZ(48), +}; + +static const u32 brcmstb_rate_table_7278[] = { + MHZ(81), + MHZ(108), + 0, + MHZ(48), +}; + +struct brcmuart_priv { + int line; + struct clk *baud_mux_clk; + unsigned long default_mux_rate; + u32 real_rates[ARRAY_SIZE(brcmstb_rate_table)]; + const u32 *rate_table; + ktime_t char_wait; + struct uart_port *up; + struct hrtimer hrt; + bool shutdown; + bool dma_enabled; + struct uart_8250_dma dma; + void __iomem *regs[REGS_MAX]; + dma_addr_t rx_addr; + void *rx_bufs; + size_t rx_size; + int rx_next_buf; + dma_addr_t tx_addr; + void *tx_buf; + size_t tx_size; + bool tx_running; + bool rx_running; + struct dentry *debugfs_dir; + + /* stats exposed through debugfs */ + u64 dma_rx_partial_buf; + u64 dma_rx_full_buf; + u32 rx_bad_timeout_late_char; + u32 rx_bad_timeout_no_char; + u32 rx_missing_close_timeout; + u32 rx_err; + u32 rx_timeout; + u32 rx_abort; +}; + +static struct dentry *brcmuart_debugfs_root; + +/* + * Register access routines + */ +static u32 udma_readl(struct brcmuart_priv *priv, + int reg_type, int offset) +{ + return readl(priv->regs[reg_type] + offset); +} + +static void udma_writel(struct brcmuart_priv *priv, + int reg_type, int offset, u32 value) +{ + writel(value, priv->regs[reg_type] + offset); +} + +static void udma_set(struct brcmuart_priv *priv, + int reg_type, int offset, u32 bits) +{ + void __iomem *reg = priv->regs[reg_type] + offset; + u32 value; + + value = readl(reg); + value |= bits; + writel(value, reg); +} + +static void udma_unset(struct brcmuart_priv *priv, + int reg_type, int offset, u32 bits) +{ + void __iomem *reg = priv->regs[reg_type] + offset; + u32 value; + + value = readl(reg); + value &= ~bits; + writel(value, reg); +} + +/* + * The UART DMA engine hardware can be used by multiple UARTS, but + * only one at a time. Sharing is not currently supported so + * the first UART to request the DMA engine will get it and any + * subsequent requests by other UARTS will fail. + */ +static int brcmuart_arbitration(struct brcmuart_priv *priv, bool acquire) +{ + u32 rx_grant; + u32 tx_grant; + int waits; + int ret = 0; + + if (acquire) { + udma_set(priv, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_set(priv, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + + waits = 1; + while (1) { + rx_grant = udma_readl(priv, REGS_DMA_ARB, UDMA_ARB_RX); + tx_grant = udma_readl(priv, REGS_DMA_ARB, UDMA_ARB_TX); + if (rx_grant & tx_grant & UDMA_ARB_GRANT) + return 0; + if (waits-- == 0) + break; + msleep(1); + } + ret = 1; + } + + udma_unset(priv, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_unset(priv, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + return ret; +} + +static void brcmuart_init_dma_hardware(struct brcmuart_priv *priv) +{ + u32 daddr; + u32 value; + int x; + + /* Start with all interrupts disabled */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, 0xffffffff); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFFER_SIZE, RX_BUF_SIZE); + + /* + * Setup buffer close to happen when 32 character times have + * elapsed since the last character was received. + */ + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFFER_CLOSE, 16*10*32); + value = (RX_BUFS_COUNT << UDMA_RX_CTRL_NUM_BUF_USED_SHIFT) + | UDMA_RX_CTRL_BUF_CLOSE_MODE + | UDMA_RX_CTRL_BUF_CLOSE_ENA; + udma_writel(priv, REGS_DMA_RX, UDMA_RX_CTRL, value); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BLOCKOUT_COUNTER, 0); + daddr = priv->rx_addr; + for (x = 0; x < RX_BUFS_COUNT; x++) { + + /* Set RX transfer length to 0 for unknown */ + udma_writel(priv, REGS_DMA_RX, UDMA_RX_TRANSFER_LEN, 0); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFx_PTR_LO(x), + lower_32_bits(daddr)); + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFx_PTR_HI(x), + upper_32_bits(daddr)); + daddr += RX_BUF_SIZE; + } + + daddr = priv->tx_addr; + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUFx_PTR_LO(0), + lower_32_bits(daddr)); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUFx_PTR_HI(0), + upper_32_bits(daddr)); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_CTRL, + UDMA_TX_CTRL_NUM_BUF_USED_1); + + /* clear all interrupts then enable them */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, 0xffffffff); + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_CLEAR, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); + +} + +static void start_rx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + int x; + + udma_unset(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + + /* Clear the RX ready bit for all buffers */ + for (x = 0; x < RX_BUFS_COUNT; x++) + udma_unset(priv, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(x), + UDMA_RX_BUFX_STATUS_DATA_RDY); + + /* always start with buffer 0 */ + udma_unset(priv, REGS_DMA_RX, UDMA_RX_STATUS, + UDMA_RX_STATUS_ACTIVE_BUF_MASK); + priv->rx_next_buf = 0; + + udma_set(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + priv->rx_running = true; +} + +static void stop_rx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + + /* If RX is running, set the RX ABORT */ + if (priv->rx_running) + udma_set(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ABORT); +} + +static int stop_tx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + u32 value; + + /* If TX is running, set the TX ABORT */ + value = udma_readl(priv, REGS_DMA_TX, UDMA_TX_CTRL); + if (value & UDMA_TX_CTRL_ENA) + udma_set(priv, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ABORT); + priv->tx_running = false; + return 0; +} + +/* + * NOTE: printk's in this routine will hang the system if this is + * the console tty + */ +static int brcmuart_tx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + struct circ_buf *xmit = &p->port.state->xmit; + u32 tx_size; + + if (uart_tx_stopped(&p->port) || priv->tx_running || + uart_circ_empty(xmit)) { + return 0; + } + tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + priv->dma.tx_err = 0; + memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size); + xmit->tail += tx_size; + xmit->tail &= UART_XMIT_SIZE - 1; + p->port.icount.tx += tx_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&p->port); + + udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUF0_DATA_LEN, tx_size); + udma_unset(priv, REGS_DMA_TX, UDMA_TX_BUF0_STATUS, UDMA_TX_BUFX_EMPTY); + udma_set(priv, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ENA); + priv->tx_running = true; + + return 0; +} + +static void brcmuart_rx_buf_done_isr(struct uart_port *up, int index) +{ + struct brcmuart_priv *priv = up->private_data; + struct tty_port *tty_port = &up->state->port; + u32 status; + u32 length; + u32 copied; + + /* Make sure we're still in sync with the hardware */ + status = udma_readl(priv, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(index)); + length = udma_readl(priv, REGS_DMA_RX, UDMA_RX_BUFx_DATA_LEN(index)); + + if ((status & UDMA_RX_BUFX_STATUS_DATA_RDY) == 0) { + dev_err(up->dev, "RX done interrupt but DATA_RDY not found\n"); + return; + } + if (status & (UDMA_RX_BUFX_STATUS_OVERRUN_ERR | + UDMA_RX_BUFX_STATUS_FRAME_ERR | + UDMA_RX_BUFX_STATUS_PARITY_ERR)) { + if (status & UDMA_RX_BUFX_STATUS_OVERRUN_ERR) { + up->icount.overrun++; + dev_warn(up->dev, "RX OVERRUN Error\n"); + } + if (status & UDMA_RX_BUFX_STATUS_FRAME_ERR) { + up->icount.frame++; + dev_warn(up->dev, "RX FRAMING Error\n"); + } + if (status & UDMA_RX_BUFX_STATUS_PARITY_ERR) { + up->icount.parity++; + dev_warn(up->dev, "RX PARITY Error\n"); + } + } + copied = (u32)tty_insert_flip_string( + tty_port, + priv->rx_bufs + (index * RX_BUF_SIZE), + length); + if (copied != length) { + dev_warn(up->dev, "Flip buffer overrun of %d bytes\n", + length - copied); + up->icount.overrun += length - copied; + } + up->icount.rx += length; + if (status & UDMA_RX_BUFX_STATUS_CLOSE_EXPIRED) + priv->dma_rx_partial_buf++; + else if (length != RX_BUF_SIZE) + /* + * This is a bug in the controller that doesn't cause + * any problems but will be fixed in the future. + */ + priv->rx_missing_close_timeout++; + else + priv->dma_rx_full_buf++; + + tty_flip_buffer_push(tty_port); +} + +static void brcmuart_rx_isr(struct uart_port *up, u32 rx_isr) +{ + struct brcmuart_priv *priv = up->private_data; + struct device *dev = up->dev; + u32 rx_done_isr; + u32 check_isr; + + rx_done_isr = (rx_isr & UDMA_INTR_RX_READY_MASK); + while (rx_done_isr) { + check_isr = UDMA_INTR_RX_READY_BUF0 << priv->rx_next_buf; + if (check_isr & rx_done_isr) { + brcmuart_rx_buf_done_isr(up, priv->rx_next_buf); + } else { + dev_err(dev, + "RX buffer ready out of sequence, restarting RX DMA\n"); + start_rx_dma(up_to_u8250p(up)); + break; + } + if (rx_isr & UDMA_RX_ERR_INTERRUPTS) { + if (rx_isr & UDMA_INTR_RX_ERROR) + priv->rx_err++; + if (rx_isr & UDMA_INTR_RX_TIMEOUT) { + priv->rx_timeout++; + dev_err(dev, "RX TIMEOUT Error\n"); + } + if (rx_isr & UDMA_INTR_RX_ABORT) + priv->rx_abort++; + priv->rx_running = false; + } + /* If not ABORT, re-enable RX buffer */ + if (!(rx_isr & UDMA_INTR_RX_ABORT)) + udma_unset(priv, REGS_DMA_RX, + UDMA_RX_BUFx_STATUS(priv->rx_next_buf), + UDMA_RX_BUFX_STATUS_DATA_RDY); + rx_done_isr &= ~check_isr; + priv->rx_next_buf++; + if (priv->rx_next_buf == RX_BUFS_COUNT) + priv->rx_next_buf = 0; + } +} + +static void brcmuart_tx_isr(struct uart_port *up, u32 isr) +{ + struct brcmuart_priv *priv = up->private_data; + struct device *dev = up->dev; + struct uart_8250_port *port_8250 = up_to_u8250p(up); + struct circ_buf *xmit = &port_8250->port.state->xmit; + + if (isr & UDMA_INTR_TX_ABORT) { + if (priv->tx_running) + dev_err(dev, "Unexpected TX_ABORT interrupt\n"); + return; + } + priv->tx_running = false; + if (!uart_circ_empty(xmit) && !uart_tx_stopped(up)) + brcmuart_tx_dma(port_8250); +} + +static irqreturn_t brcmuart_isr(int irq, void *dev_id) +{ + struct uart_port *up = dev_id; + struct device *dev = up->dev; + struct brcmuart_priv *priv = up->private_data; + unsigned long flags; + u32 interrupts; + u32 rval; + u32 tval; + + interrupts = udma_readl(priv, REGS_DMA_ISR, UDMA_INTR_STATUS); + if (interrupts == 0) + return IRQ_NONE; + + spin_lock_irqsave(&up->lock, flags); + + /* Clear all interrupts */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, interrupts); + + rval = UDMA_IS_RX_INTERRUPT(interrupts); + if (rval) + brcmuart_rx_isr(up, rval); + tval = UDMA_IS_TX_INTERRUPT(interrupts); + if (tval) + brcmuart_tx_isr(up, tval); + if ((rval | tval) == 0) + dev_warn(dev, "Spurious interrupt: 0x%x\n", interrupts); + + spin_unlock_irqrestore(&up->lock, flags); + return IRQ_HANDLED; +} + +static int brcmuart_startup(struct uart_port *port) +{ + int res; + struct uart_8250_port *up = up_to_u8250p(port); + struct brcmuart_priv *priv = up->port.private_data; + + priv->shutdown = false; + + /* + * prevent serial8250_do_startup() from allocating non-existent + * DMA resources + */ + up->dma = NULL; + + res = serial8250_do_startup(port); + if (!priv->dma_enabled) + return res; + /* + * Disable the Receive Data Interrupt because the DMA engine + * will handle this. + */ + up->ier &= ~UART_IER_RDI; + serial_port_out(port, UART_IER, up->ier); + + priv->tx_running = false; + priv->dma.rx_dma = NULL; + priv->dma.tx_dma = brcmuart_tx_dma; + up->dma = &priv->dma; + + brcmuart_init_dma_hardware(priv); + start_rx_dma(up); + return res; +} + +static void brcmuart_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct brcmuart_priv *priv = up->port.private_data; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + priv->shutdown = true; + if (priv->dma_enabled) { + stop_rx_dma(up); + stop_tx_dma(up); + /* disable all interrupts */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); + } + + /* + * prevent serial8250_do_shutdown() from trying to free + * DMA resources that we never alloc'd for this driver. + */ + up->dma = NULL; + + spin_unlock_irqrestore(&port->lock, flags); + serial8250_do_shutdown(port); +} + +/* + * Not all clocks run at the exact specified rate, so set each requested + * rate and then get the actual rate. + */ +static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv) +{ + int x; + int rc; + + priv->default_mux_rate = clk_get_rate(priv->baud_mux_clk); + for (x = 0; x < ARRAY_SIZE(priv->real_rates); x++) { + if (priv->rate_table[x] == 0) { + priv->real_rates[x] = 0; + continue; + } + rc = clk_set_rate(priv->baud_mux_clk, priv->rate_table[x]); + if (rc) { + dev_err(dev, "Error selecting BAUD MUX clock for %u\n", + priv->rate_table[x]); + priv->real_rates[x] = priv->rate_table[x]; + } else { + priv->real_rates[x] = clk_get_rate(priv->baud_mux_clk); + } + } + clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); +} + +static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, + u32 baud) +{ + u32 percent; + u32 best_percent = UINT_MAX; + u32 quot; + u32 best_quot = 1; + u32 rate; + int best_index = -1; + u64 hires_rate; + u64 hires_baud; + u64 hires_err; + int rc; + int i; + int real_baud; + + /* If the Baud Mux Clock was not specified, just return */ + if (priv->baud_mux_clk == NULL) + return; + + /* Find the closest match for specified baud */ + for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { + if (priv->real_rates[i] == 0) + continue; + rate = priv->real_rates[i] / 16; + quot = DIV_ROUND_CLOSEST(rate, baud); + if (!quot) + continue; + + /* increase resolution to get xx.xx percent */ + hires_rate = (u64)rate * 10000; + hires_baud = (u64)baud * 10000; + + hires_err = div_u64(hires_rate, (u64)quot); + + /* get the delta */ + if (hires_err > hires_baud) + hires_err = (hires_err - hires_baud); + else + hires_err = (hires_baud - hires_err); + + percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud); + dev_dbg(up->dev, + "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n", + baud, priv->real_rates[i], percent / 100, + percent % 100); + if (percent < best_percent) { + best_percent = percent; + best_index = i; + best_quot = quot; + } + } + if (best_index == -1) { + dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud); + return; + } + rate = priv->real_rates[best_index]; + rc = clk_set_rate(priv->baud_mux_clk, rate); + if (rc) + dev_err(up->dev, "Error selecting BAUD MUX clock\n"); + + /* Error over 3 percent will cause data errors */ + if (best_percent > 300) + dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n", + baud, percent / 100, percent % 100); + + real_baud = rate / 16 / best_quot; + dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate); + dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n", + baud, real_baud); + + /* calc nanoseconds for 1.5 characters time at the given baud rate */ + i = NSEC_PER_SEC / real_baud / 10; + i += (i / 2); + priv->char_wait = ns_to_ktime(i); + + up->uartclk = rate; +} + +static void brcmstb_set_termios(struct uart_port *up, + struct ktermios *termios, + struct ktermios *old) +{ + struct uart_8250_port *p8250 = up_to_u8250p(up); + struct brcmuart_priv *priv = up->private_data; + + if (priv->dma_enabled) + stop_rx_dma(p8250); + set_clock_mux(up, priv, tty_termios_baud_rate(termios)); + serial8250_do_set_termios(up, termios, old); + if (p8250->mcr & UART_MCR_AFE) + p8250->port.status |= UPSTAT_AUTOCTS; + if (priv->dma_enabled) + start_rx_dma(p8250); +} + +static int brcmuart_handle_irq(struct uart_port *p) +{ + unsigned int iir = serial_port_in(p, UART_IIR); + struct brcmuart_priv *priv = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int status; + unsigned long flags; + unsigned int ier; + unsigned int mcr; + int handled = 0; + + /* + * There's a bug in some 8250 cores where we get a timeout + * interrupt but there is no data ready. + */ + if (((iir & UART_IIR_ID) == UART_IIR_RX_TIMEOUT) && !(priv->shutdown)) { + spin_lock_irqsave(&p->lock, flags); + status = serial_port_in(p, UART_LSR); + if ((status & UART_LSR_DR) == 0) { + + ier = serial_port_in(p, UART_IER); + /* + * if Receive Data Interrupt is enabled and + * we're uing hardware flow control, deassert + * RTS and wait for any chars in the pipline to + * arrive and then check for DR again. + */ + if ((ier & UART_IER_RDI) && (up->mcr & UART_MCR_AFE)) { + ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_port_out(p, UART_IER, ier); + mcr = serial_port_in(p, UART_MCR); + mcr &= ~UART_MCR_RTS; + serial_port_out(p, UART_MCR, mcr); + hrtimer_start(&priv->hrt, priv->char_wait, + HRTIMER_MODE_REL); + } else { + serial_port_in(p, UART_RX); + } + + handled = 1; + } + spin_unlock_irqrestore(&p->lock, flags); + if (handled) + return 1; + } + return serial8250_handle_irq(p, iir); +} + +static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) +{ + struct brcmuart_priv *priv = container_of(t, struct brcmuart_priv, hrt); + struct uart_port *p = priv->up; + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int status; + unsigned long flags; + + if (priv->shutdown) + return HRTIMER_NORESTART; + + spin_lock_irqsave(&p->lock, flags); + status = serial_port_in(p, UART_LSR); + + /* + * If a character did not arrive after the timeout, clear the false + * receive timeout. + */ + if ((status & UART_LSR_DR) == 0) { + serial_port_in(p, UART_RX); + priv->rx_bad_timeout_no_char++; + } else { + priv->rx_bad_timeout_late_char++; + } + + /* re-enable receive unless upper layer has disabled it */ + if ((up->ier & (UART_IER_RLSI | UART_IER_RDI)) == + (UART_IER_RLSI | UART_IER_RDI)) { + status = serial_port_in(p, UART_IER); + status |= (UART_IER_RLSI | UART_IER_RDI); + serial_port_out(p, UART_IER, status); + status = serial_port_in(p, UART_MCR); + status |= UART_MCR_RTS; + serial_port_out(p, UART_MCR, status); + } + spin_unlock_irqrestore(&p->lock, flags); + return HRTIMER_NORESTART; +} + +static const struct of_device_id brcmuart_dt_ids[] = { + { + .compatible = "brcm,bcm7278-uart", + .data = brcmstb_rate_table_7278, + }, + { + .compatible = "brcm,bcm7271-uart", + .data = brcmstb_rate_table, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, brcmuart_dt_ids); + +static void brcmuart_free_bufs(struct device *dev, struct brcmuart_priv *priv) +{ + if (priv->rx_bufs) + dma_free_coherent(dev, priv->rx_size, priv->rx_bufs, + priv->rx_addr); + if (priv->tx_buf) + dma_free_coherent(dev, priv->tx_size, priv->tx_buf, + priv->tx_addr); +} + +static void brcmuart_throttle(struct uart_port *port) +{ + struct brcmuart_priv *priv = port->private_data; + + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, UDMA_RX_INTERRUPTS); +} + +static void brcmuart_unthrottle(struct uart_port *port) +{ + struct brcmuart_priv *priv = port->private_data; + + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_CLEAR, + UDMA_RX_INTERRUPTS); +} + +static int debugfs_stats_show(struct seq_file *s, void *unused) +{ + struct brcmuart_priv *priv = s->private; + + seq_printf(s, "rx_err:\t\t\t\t%u\n", + priv->rx_err); + seq_printf(s, "rx_timeout:\t\t\t%u\n", + priv->rx_timeout); + seq_printf(s, "rx_abort:\t\t\t%u\n", + priv->rx_abort); + seq_printf(s, "rx_bad_timeout_late_char:\t%u\n", + priv->rx_bad_timeout_late_char); + seq_printf(s, "rx_bad_timeout_no_char:\t\t%u\n", + priv->rx_bad_timeout_no_char); + seq_printf(s, "rx_missing_close_timeout:\t%u\n", + priv->rx_missing_close_timeout); + if (priv->dma_enabled) { + seq_printf(s, "dma_rx_partial_buf:\t\t%llu\n", + priv->dma_rx_partial_buf); + seq_printf(s, "dma_rx_full_buf:\t\t%llu\n", + priv->dma_rx_full_buf); + } + return 0; +} +DEFINE_SHOW_ATTRIBUTE(debugfs_stats); + +static void brcmuart_init_debugfs(struct brcmuart_priv *priv, + const char *device) +{ + priv->debugfs_dir = debugfs_create_dir(device, brcmuart_debugfs_root); + debugfs_create_file("stats", 0444, priv->debugfs_dir, priv, + &debugfs_stats_fops); +} + + +static int brcmuart_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = NULL; + struct uart_8250_port *new_port; + struct device *dev = &pdev->dev; + struct brcmuart_priv *priv; + struct clk *baud_mux_clk; + struct uart_8250_port up; + struct resource *irq; + void __iomem *membase = 0; + resource_size_t mapbase = 0; + u32 clk_rate = 0; + int ret; + int x; + int dma_irq; + static const char * const reg_names[REGS_MAX] = { + "uart", "dma_rx", "dma_tx", "dma_intr2", "dma_arb" + }; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(dev, "missing irq\n"); + return -EINVAL; + } + priv = devm_kzalloc(dev, sizeof(struct brcmuart_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + of_id = of_match_node(brcmuart_dt_ids, np); + if (!of_id || !of_id->data) + priv->rate_table = brcmstb_rate_table; + else + priv->rate_table = of_id->data; + + for (x = 0; x < REGS_MAX; x++) { + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, + reg_names[x]); + if (!regs) + break; + priv->regs[x] = devm_ioremap(dev, regs->start, + resource_size(regs)); + if (!priv->regs[x]) + return -ENOMEM; + if (x == REGS_8250) { + mapbase = regs->start; + membase = priv->regs[x]; + } + } + + /* We should have just the uart base registers or all the registers */ + if (x != 1 && x != REGS_MAX) { + dev_warn(dev, "%s registers not specified\n", reg_names[x]); + return -EINVAL; + } + + /* if the DMA registers were specified, try to enable DMA */ + if (x > REGS_DMA_RX) { + if (brcmuart_arbitration(priv, 1) == 0) { + u32 txrev = 0; + u32 rxrev = 0; + + txrev = udma_readl(priv, REGS_DMA_RX, UDMA_RX_REVISION); + rxrev = udma_readl(priv, REGS_DMA_TX, UDMA_TX_REVISION); + if ((txrev >= UDMA_TX_REVISION_REQUIRED) && + (rxrev >= UDMA_RX_REVISION_REQUIRED)) { + + /* Enable the use of the DMA hardware */ + priv->dma_enabled = true; + } else { + brcmuart_arbitration(priv, 0); + dev_err(dev, + "Unsupported DMA Hardware Revision\n"); + } + } else { + dev_err(dev, + "Timeout arbitrating for UART DMA hardware\n"); + } + } + + of_property_read_u32(np, "clock-frequency", &clk_rate); + + /* See if a Baud clock has been specified */ + baud_mux_clk = of_clk_get_by_name(np, "sw_baud"); + if (IS_ERR(baud_mux_clk)) { + if (PTR_ERR(baud_mux_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(dev, "BAUD MUX clock not specified\n"); + } else { + dev_dbg(dev, "BAUD MUX clock found\n"); + ret = clk_prepare_enable(baud_mux_clk); + if (ret) + return ret; + priv->baud_mux_clk = baud_mux_clk; + init_real_clk_rates(dev, priv); + clk_rate = priv->default_mux_rate; + } + + if (clk_rate == 0) { + dev_err(dev, "clock-frequency or clk not defined\n"); + return -EINVAL; + } + + dev_dbg(dev, "DMA is %senabled\n", priv->dma_enabled ? "" : "not "); + + memset(&up, 0, sizeof(up)); + up.port.type = PORT_16550A; + up.port.uartclk = clk_rate; + up.port.dev = dev; + up.port.mapbase = mapbase; + up.port.membase = membase; + up.port.irq = irq->start; + up.port.handle_irq = brcmuart_handle_irq; + up.port.regshift = 2; + up.port.iotype = of_device_is_big_endian(np) ? + UPIO_MEM32BE : UPIO_MEM32; + up.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF + | UPF_FIXED_PORT | UPF_FIXED_TYPE; + up.port.dev = dev; + up.port.private_data = priv; + up.capabilities = UART_CAP_FIFO | UART_CAP_AFE; + up.port.fifosize = 32; + + /* Check for a fixed line number */ + ret = of_alias_get_id(np, "serial"); + if (ret >= 0) + up.port.line = ret; + + /* setup HR timer */ + hrtimer_init(&priv->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + priv->hrt.function = brcmuart_hrtimer_func; + + up.port.shutdown = brcmuart_shutdown; + up.port.startup = brcmuart_startup; + up.port.throttle = brcmuart_throttle; + up.port.unthrottle = brcmuart_unthrottle; + up.port.set_termios = brcmstb_set_termios; + + if (priv->dma_enabled) { + priv->rx_size = RX_BUF_SIZE * RX_BUFS_COUNT; + priv->rx_bufs = dma_alloc_coherent(dev, + priv->rx_size, + &priv->rx_addr, GFP_KERNEL); + if (!priv->rx_bufs) + goto err; + priv->tx_size = UART_XMIT_SIZE; + priv->tx_buf = dma_alloc_coherent(dev, + priv->tx_size, + &priv->tx_addr, GFP_KERNEL); + if (!priv->tx_buf) + goto err; + } + + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(dev, "unable to register 8250 port\n"); + goto err; + } + priv->line = ret; + new_port = serial8250_get_port(ret); + priv->up = &new_port->port; + if (priv->dma_enabled) { + dma_irq = platform_get_irq_byname(pdev, "dma"); + if (dma_irq < 0) { + dev_err(dev, "no IRQ resource info\n"); + goto err1; + } + ret = devm_request_irq(dev, dma_irq, brcmuart_isr, + IRQF_SHARED, "uart DMA irq", &new_port->port); + if (ret) { + dev_err(dev, "unable to register IRQ handler\n"); + goto err1; + } + } + platform_set_drvdata(pdev, priv); + brcmuart_init_debugfs(priv, dev_name(&pdev->dev)); + return 0; + +err1: + serial8250_unregister_port(priv->line); +err: + brcmuart_free_bufs(dev, priv); + brcmuart_arbitration(priv, 0); + return -ENODEV; +} + +static int brcmuart_remove(struct platform_device *pdev) +{ + struct brcmuart_priv *priv = platform_get_drvdata(pdev); + + debugfs_remove_recursive(priv->debugfs_dir); + hrtimer_cancel(&priv->hrt); + serial8250_unregister_port(priv->line); + brcmuart_free_bufs(&pdev->dev, priv); + brcmuart_arbitration(priv, 0); + return 0; +} + +static int __maybe_unused brcmuart_suspend(struct device *dev) +{ + struct brcmuart_priv *priv = dev_get_drvdata(dev); + + serial8250_suspend_port(priv->line); + clk_disable_unprepare(priv->baud_mux_clk); + + return 0; +} + +static int __maybe_unused brcmuart_resume(struct device *dev) +{ + struct brcmuart_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->baud_mux_clk); + if (ret) + dev_err(dev, "Error enabling BAUD MUX clock\n"); + + /* + * The hardware goes back to it's default after suspend + * so get the "clk" back in sync. + */ + ret = clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); + if (ret) + dev_err(dev, "Error restoring default BAUD MUX clock\n"); + if (priv->dma_enabled) { + if (brcmuart_arbitration(priv, 1)) { + dev_err(dev, "Timeout arbitrating for DMA hardware on resume\n"); + return(-EBUSY); + } + brcmuart_init_dma_hardware(priv); + start_rx_dma(serial8250_get_port(priv->line)); + } + serial8250_resume_port(priv->line); + return 0; +} + +static const struct dev_pm_ops brcmuart_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(brcmuart_suspend, brcmuart_resume) +}; + +static struct platform_driver brcmuart_platform_driver = { + .driver = { + .name = "bcm7271-uart", + .pm = &brcmuart_dev_pm_ops, + .of_match_table = brcmuart_dt_ids, + }, + .probe = brcmuart_probe, + .remove = brcmuart_remove, +}; + +static int __init brcmuart_init(void) +{ + brcmuart_debugfs_root = debugfs_create_dir( + brcmuart_platform_driver.driver.name, NULL); + return platform_driver_register(&brcmuart_platform_driver); +} +module_init(brcmuart_init); + +static void __exit brcmuart_deinit(void) +{ + platform_driver_unregister(&brcmuart_platform_driver); + debugfs_remove_recursive(brcmuart_debugfs_root); +} +module_exit(brcmuart_deinit); + +MODULE_AUTHOR("Al Cooper"); +MODULE_DESCRIPTION("Broadcom NS16550A compatible serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 2d0e7c7e408d..2f49c580139b 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -354,7 +354,7 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) static void * __xr17v35x_register_gpio(struct pci_dev *pcidev, - const struct property_entry *properties) + const struct software_node *node) { struct platform_device *pdev; @@ -365,7 +365,7 @@ __xr17v35x_register_gpio(struct pci_dev *pcidev, pdev->dev.parent = &pcidev->dev; ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); - if (platform_device_add_properties(pdev, properties) < 0 || + if (device_add_software_node(&pdev->dev, node) < 0 || platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; @@ -380,12 +380,16 @@ static const struct property_entry exar_gpio_properties[] = { { } }; +static const struct software_node exar_gpio_node = { + .properties = exar_gpio_properties, +}; + static int xr17v35x_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) { if (pcidev->vendor == PCI_VENDOR_ID_EXAR) port->port.private_data = - __xr17v35x_register_gpio(pcidev, exar_gpio_properties); + __xr17v35x_register_gpio(pcidev, &exar_gpio_node); return 0; } @@ -457,6 +461,10 @@ static const struct property_entry iot2040_gpio_properties[] = { { } }; +static const struct software_node iot2040_gpio_node = { + .properties = iot2040_gpio_properties, +}; + static int iot2040_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) { @@ -468,7 +476,7 @@ static int iot2040_register_gpio(struct pci_dev *pcidev, writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); port->port.private_data = - __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties); + __xr17v35x_register_gpio(pcidev, &iot2040_gpio_node); return 0; } @@ -547,6 +555,7 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev) struct uart_8250_port *port = serial8250_get_port(priv->line[0]); struct platform_device *pdev = port->port.private_data; + device_remove_software_node(&pdev->dev); platform_device_unregister(pdev); port->port.private_data = NULL; } diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index fbcc90c31ca1..4e75d2e4f87c 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -30,15 +30,14 @@ struct fsl8250_data { int fsl8250_handle_irq(struct uart_port *port) { unsigned char lsr, orig_lsr; - unsigned long flags; unsigned int iir; struct uart_8250_port *up = up_to_u8250p(port); - spin_lock_irqsave(&up->port.lock, flags); + spin_lock(&up->port.lock); iir = port->serial_in(port, UART_IIR); if (iir & UART_IIR_NO_INT) { - spin_unlock_irqrestore(&up->port.lock, flags); + spin_unlock(&up->port.lock); return 0; } @@ -46,7 +45,7 @@ int fsl8250_handle_irq(struct uart_port *port) if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { up->lsr_saved_flags &= ~UART_LSR_BI; port->serial_in(port, UART_RX); - spin_unlock_irqrestore(&up->port.lock, flags); + spin_unlock(&up->port.lock); return 1; } @@ -82,7 +81,9 @@ int fsl8250_handle_irq(struct uart_port *port) serial8250_tx_chars(up); up->lsr_saved_flags = orig_lsr; - uart_unlock_and_check_sysrq(&up->port, flags); + + uart_unlock_and_check_sysrq(&up->port); + return 1; } EXPORT_SYMBOL_GPL(fsl8250_handle_irq); @@ -104,11 +105,8 @@ static int fsl8250_acpi_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - if (irq != -EPROBE_DEFER) - dev_err(dev, "cannot get irq\n"); + if (irq < 0) return irq; - } memset(&port8250, 0, sizeof(port8250)); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 65e9045dafe6..0b077b45d6a9 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -318,6 +318,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,wpcm450-uart", .data = (void *)PORT_NPCM, }, { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, { /* end of list */ }, }; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 23e0decde33e..8ac11eaeca51 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1143,7 +1143,6 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = up->port.private_data; unsigned char status; - unsigned long flags; u8 iir; serial8250_rpm_get(up); @@ -1154,7 +1153,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) return IRQ_HANDLED; } - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); status = serial_port_in(port, UART_LSR); @@ -1179,7 +1178,8 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) } } - uart_unlock_and_check_sysrq(port, flags); + uart_unlock_and_check_sysrq(port); + serial8250_rpm_put(up); return 1; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index b0af13074cd3..d45dab1ab316 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1466,13 +1466,11 @@ EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx); static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) { - struct uart_8250_em485 *em485; - struct uart_8250_port *p; + struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485, + stop_tx_timer); + struct uart_8250_port *p = em485->port; unsigned long flags; - em485 = container_of(t, struct uart_8250_em485, stop_tx_timer); - p = em485->port; - serial8250_rpm_get(p); spin_lock_irqsave(&p->port.lock, flags); if (em485->active_timer == &em485->stop_tx_timer) { @@ -1482,16 +1480,13 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) } spin_unlock_irqrestore(&p->port.lock, flags); serial8250_rpm_put(p); + return HRTIMER_NORESTART; } static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) { - long sec = msec / 1000; - long nsec = (msec % 1000) * 1000000; - ktime_t t = ktime_set(sec, nsec); - - hrtimer_start(hrt, t, HRTIMER_MODE_REL); + hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); } static void __stop_tx_rs485(struct uart_8250_port *p) @@ -1633,19 +1628,18 @@ static inline void start_tx_rs485(struct uart_port *port) static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t) { - struct uart_8250_em485 *em485; - struct uart_8250_port *p; + struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485, + start_tx_timer); + struct uart_8250_port *p = em485->port; unsigned long flags; - em485 = container_of(t, struct uart_8250_em485, start_tx_timer); - p = em485->port; - spin_lock_irqsave(&p->port.lock, flags); if (em485->active_timer == &em485->start_tx_timer) { __start_tx(&p->port); em485->active_timer = NULL; } spin_unlock_irqrestore(&p->port.lock, flags); + return HRTIMER_NORESTART; } @@ -1885,14 +1879,13 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) int serial8250_handle_irq(struct uart_port *port, unsigned int iir) { unsigned char status; - unsigned long flags; struct uart_8250_port *up = up_to_u8250p(port); bool skip_rx = false; if (iir & UART_IIR_NO_INT) return 0; - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); status = serial_port_in(port, UART_LSR); @@ -1918,7 +1911,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) (up->ier & UART_IER_THRI)) serial8250_tx_chars(up); - uart_unlock_and_check_sysrq(port, flags); + uart_unlock_and_check_sysrq(port); + return 1; } EXPORT_SYMBOL_GPL(serial8250_handle_irq); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 603137da4736..d1b3c2373fa4 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -15,8 +15,7 @@ config SERIAL_8250 here are those that are setting up dedicated Ethernet WWW/FTP servers, or users that have one of the various bus mice instead of a serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades multi serial port driver - does not need this driver built in for it to work.) + port for anything. To compile this driver as a module, choose M here: the module will be called 8250. @@ -226,7 +225,7 @@ config SERIAL_8250_MANY_PORTS serial port hardware which acts similar to standard serial port hardware. If you only use the standard COM 1/2/3/4 ports, you can say N here to save some memory. You can also say Y if you have an - "intelligent" multiport card such as Cyclades, Digiboards, etc. + "intelligent" multiport card such as Digiboards, etc. # # Multi-port serial cards @@ -404,7 +403,8 @@ config SERIAL_8250_RT288X config SERIAL_8250_OMAP tristate "Support for OMAP internal UART (8250 based driver)" - depends on SERIAL_8250 && (ARCH_OMAP2PLUS || ARCH_K3) + depends on SERIAL_8250 + depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST help If you have a machine based on an Texas Instruments OMAP CPU you can enable its onboard serial ports by enabling this option. @@ -440,7 +440,8 @@ config SERIAL_8250_LPC18XX config SERIAL_8250_MT6577 tristate "Mediatek serial port support" - depends on SERIAL_8250 && ARCH_MEDIATEK + depends on SERIAL_8250 + depends on ARCH_MEDIATEK || COMPILE_TEST help If you have a Mediatek based board and want to use the serial port, say Y to this option. If unsure, say N. @@ -510,6 +511,16 @@ config SERIAL_8250_TEGRA Select this option if you have machine with an NVIDIA Tegra SoC and wish to enable 8250 serial driver for the Tegra serial interfaces. +config SERIAL_8250_BCM7271 + tristate "Broadcom 8250 based serial port" + depends on SERIAL_8250 && (ARCH_BRCMSTB || COMPILE_TEST) + default ARCH_BRCMSTB + help + If you have a Broadcom STB based board and want to use the + enhanced features of the Broadcom 8250 based serial port, + including DMA support and high accuracy BAUD rates, say + Y to this option. If unsure, say N. + config SERIAL_OF_PLATFORM tristate "Devicetree based probing for 8250 ports" depends on SERIAL_8250 && OF diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index a8bfb654d490..b9bcd73c8997 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o +obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 35ff6627c61b..63ea9c4da3d5 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -456,11 +456,11 @@ static int simple_config(struct pcmcia_device *link) * its base address, then try to grab any standard serial port * address, and finally try to get any free port. */ - if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) - goto found_port; - - dev_warn(&link->dev, "no usable port range found, giving up\n"); - return -1; + ret = pcmcia_loop_config(link, simple_config_check_notpicky, NULL); + if (ret) { + dev_warn(&link->dev, "no usable port range found, giving up\n"); + return ret; + } found_port: if (info->multi && (info->manfid == MANFID_3COM)) @@ -474,7 +474,7 @@ found_port: ret = pcmcia_enable_device(link); if (ret != 0) - return -1; + return ret; return setup_serial(link, info, link->resource[0]->start, link->irq); } diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 0c4cd4a348f4..682f9171c82c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -20,7 +20,7 @@ comment "Non-8250 serial port support" config SERIAL_AMBA_PL010 tristate "ARM AMBA PL010 serial port support" - depends on ARM_AMBA + depends on ARM_AMBA || COMPILE_TEST select SERIAL_CORE help This selects the ARM(R) AMBA(R) PrimeCell PL010 UART. If you have @@ -198,7 +198,7 @@ config SERIAL_KGDB_NMI config SERIAL_MESON tristate "Meson serial port support" - depends on ARCH_MESON + depends on ARCH_MESON || COMPILE_TEST select SERIAL_CORE help This enables the driver for the on-chip UARTs of the Amlogic @@ -236,7 +236,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || ARCH_APPLE || COMPILE_TEST select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -278,7 +278,7 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_TEGRA tristate "NVIDIA Tegra20/30 SoC serial controller" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST select SERIAL_CORE help Support for the on-chip UARTs on the NVIDIA Tegra series SOCs @@ -289,7 +289,8 @@ config SERIAL_TEGRA config SERIAL_TEGRA_TCU tristate "NVIDIA Tegra Combined UART" - depends on ARCH_TEGRA && TEGRA_HSP_MBOX + depends on MAILBOX + depends on (ARCH_TEGRA && TEGRA_HSP_MBOX) || COMPILE_TEST select SERIAL_CORE help Support for the mailbox-based TCU (Tegra Combined UART) serial port. @@ -498,6 +499,7 @@ config SERIAL_IMX_EARLYCON bool "Earlycon on IMX serial port" depends on ARCH_MXC || COMPILE_TEST depends on OF + select SERIAL_CORE select SERIAL_EARLYCON select SERIAL_CORE_CONSOLE default y if SERIAL_IMX_CONSOLE @@ -851,7 +853,8 @@ config SERIAL_MPC52xx_CONSOLE_BAUD config SERIAL_ICOM tristate "IBM Multiport Serial Adapter" - depends on PCI && PPC_PSERIES + depends on PCI + depends on PPC_PSERIES || COMPILE_TEST select SERIAL_CORE select FW_LOADER help @@ -920,7 +923,7 @@ config SERIAL_JSM config SERIAL_MSM tristate "MSM on-chip serial port support" - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST select SERIAL_CORE config SERIAL_MSM_CONSOLE @@ -946,7 +949,7 @@ config SERIAL_QCOM_GENI_CONSOLE config SERIAL_VT8500 bool "VIA VT8500 on-chip serial port support" - depends on ARCH_VT8500 + depends on ARCH_VT8500 || COMPILE_TEST select SERIAL_CORE config SERIAL_VT8500_CONSOLE @@ -956,7 +959,7 @@ config SERIAL_VT8500_CONSOLE config SERIAL_OMAP tristate "OMAP serial port support" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST select SERIAL_CORE help If you have a machine based on an Texas Instruments OMAP CPU you diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d0ca9cf29b62..23c4e0e79694 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -131,9 +131,7 @@ static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) uart_insert_char(port, 0, 0, ch, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 0e487ce091ac..7c5f4e966b59 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -243,9 +243,7 @@ static void altera_uart_rx_chars(struct altera_uart *pp) flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void altera_uart_tx_chars(struct altera_uart *pp) diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 3f96edfe569c..e744b953ca34 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -159,9 +159,7 @@ static void pl010_rx_chars(struct uart_amba_port *uap) ignore_char: status = readb(uap->port.membase + UART01x_FR); } - spin_unlock(&uap->port.lock); tty_flip_buffer_push(&uap->port.state->port); - spin_lock(&uap->port.lock); } static void pl010_tx_chars(struct uart_amba_port *uap) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 4ead0c9048a8..78682c12156a 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -937,12 +937,10 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, fifotaken = pl011_fifo_to_tty(uap); } - spin_unlock(&uap->port.lock); dev_vdbg(uap->port.dev, "Took %d chars from DMA buffer and %d chars from the FIFO\n", dma_count, fifotaken); tty_flip_buffer_push(port); - spin_lock(&uap->port.lock); } static void pl011_dma_rx_irq(struct uart_amba_port *uap) diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index e8d56e899ec7..d8c937bdf3f9 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -117,9 +117,7 @@ static void apbuart_rx_chars(struct uart_port *port) status = UART_GET_STATUS(port); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void apbuart_tx_chars(struct uart_port *port) diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index c2be7cf91399..4379ca4842ae 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -385,9 +385,7 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) tty_insert_flip_char(port, ch, TTY_NORMAL); } while (max_count-- > 0); - spin_unlock(&up->port.lock); tty_flip_buffer_push(port); - spin_lock(&up->port.lock); } static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 17c3fc398fc6..1a9444b6b57e 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -236,9 +236,7 @@ static void arc_serial_rx_chars(struct uart_port *port, unsigned int status) if (!(uart_handle_sysrq_char(port, ch))) uart_insert_char(port, status, RXOERR, ch, flg); - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } while (!((status = UART_GET_STATUS(port)) & RXEMPTY)); } diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index a24e5c2b30bc..058886d9045b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1178,13 +1178,7 @@ static void atmel_rx_from_dma(struct uart_port *port) 1, DMA_FROM_DEVICE); - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); atmel_uart_writel(port, ATMEL_US_IER, ATMEL_US_TIMEOUT); } @@ -1576,13 +1570,7 @@ static void atmel_rx_from_ring(struct uart_port *port) uart_insert_char(port, status, ATMEL_US_OVRE, c.ch, flg); } - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static void atmel_release_rx_pdc(struct uart_port *port) @@ -1667,13 +1655,7 @@ static void atmel_rx_from_pdc(struct uart_port *port) } } while (head >= pdc->dma_size); - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); atmel_uart_writel(port, ATMEL_US_IER, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 5674da2b76f0..5fb0e84f7fd1 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -294,9 +294,7 @@ static void bcm_uart_do_rx(struct uart_port *port) } while (--max_count); - spin_unlock(&port->lock); tty_flip_buffer_push(tty_port); - spin_lock(&port->lock); } /* diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 94af7a5ea497..9e9abfc4824a 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -829,9 +829,7 @@ ignore_char: } icom_port->next_rcv = rcv_buff; - spin_unlock(&icom_port->uart_port.lock); tty_flip_buffer_push(port); - spin_lock(&icom_port->uart_port.lock); } static void process_interrupt(u16 port_int_reg, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 8257597d034d..7d5a8dfa3e91 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -394,11 +394,7 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2) static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) { - long sec = msec / MSEC_PER_SEC; - long nsec = (msec % MSEC_PER_SEC) * 1000000; - ktime_t t = ktime_set(sec, nsec); - - hrtimer_start(hrt, t, HRTIMER_MODE_REL); + hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); } /* called with port.lock taken and irqs off */ @@ -922,14 +918,8 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) struct imx_port *sport = dev_id; unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4; irqreturn_t ret = IRQ_NONE; - unsigned long flags = 0; - /* - * IRQs might not be disabled upon entering this interrupt handler, - * e.g. when interrupt handlers are forced to be threaded. To support - * this scenario as well, disable IRQs when acquiring the spinlock. - */ - spin_lock_irqsave(&sport->port.lock, flags); + spin_lock(&sport->port.lock); usr1 = imx_uart_readl(sport, USR1); usr2 = imx_uart_readl(sport, USR2); @@ -999,7 +989,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) ret = IRQ_HANDLED; } - spin_unlock_irqrestore(&sport->port.lock, flags); + spin_unlock(&sport->port.lock); return ret; } diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 512b77195e9f..8e42a7682c63 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -603,18 +603,22 @@ void jsm_input(struct jsm_channel *ch) if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { for (i = 0; i < s; i++) { + u8 chr = ch->ch_rqueue[tail + i]; + u8 error = ch->ch_equeue[tail + i]; + char flag = TTY_NORMAL; + /* - * Give the Linux ld the flags in the - * format it likes. + * Give the Linux ld the flags in the format it + * likes. */ - if (*(ch->ch_equeue + tail + i) & UART_LSR_BI) - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_BREAK); - else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE) - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_PARITY); - else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE) - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_FRAME); - else - tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_NORMAL); + if (error & UART_LSR_BI) + flag = TTY_BREAK; + else if (error & UART_LSR_PE) + flag = TTY_PARITY; + else if (error & UART_LSR_FE) + flag = TTY_FRAME; + + tty_insert_flip_char(port, chr, flag); } } else { tty_insert_flip_string(port, ch->ch_rqueue + tail, s); diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 6004c0c1d173..db059b66438e 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -373,9 +373,7 @@ int kgdb_unregister_nmi_console(void) if (ret) return ret; - ret = tty_unregister_driver(kgdb_nmi_tty_driver); - if (ret) - return ret; + tty_unregister_driver(kgdb_nmi_tty_driver); put_tty_driver(kgdb_nmi_tty_driver); return 0; diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index 64842f3539e1..0b06770642cb 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -270,8 +270,8 @@ static int liteuart_probe(struct platform_device *pdev) /* get membase */ port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (!port->membase) - return -ENXIO; + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); /* values not from device tree */ port->dev = &pdev->dev; diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 1fa098d7aec4..b199d7859961 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -273,9 +273,7 @@ static void __serial_lpc32xx_rx(struct uart_port *port) tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void __serial_lpc32xx_tx(struct uart_port *port) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 1b61d26bb7af..8534d6e45a1d 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -273,7 +273,7 @@ struct max310x_port { #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; #endif - struct max310x_one p[0]; + struct max310x_one p[]; }; static struct uart_driver max310x_uart = { diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 09c88c48fb7b..c7cec7d03620 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -319,9 +319,7 @@ static void mcf_rx_chars(struct mcf_uart *pp) uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } /****************************************************************************/ diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 69eeef9edfa5..529cd0289056 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -226,9 +226,7 @@ static void meson_receive_chars(struct uart_port *port) } while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)); - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index af1700445251..2704dc988e4a 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1421,9 +1421,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) } } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); return psc_ops->raw_rx_rdy(port); } diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 770c182e2208..fcef7a961430 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -757,9 +757,7 @@ static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr) count -= r_count; } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); if (misr & (UART_IMR_RXSTALE)) msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); @@ -819,9 +817,7 @@ static void msm_handle_rx(struct uart_port *port) tty_insert_flip_char(tport, c, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 76b94d0ff586..84e8158088cd 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -159,6 +159,8 @@ struct uart_omap_port { u32 calc_latency; struct work_struct qos_work; bool is_suspending; + + unsigned int rs485_tx_filter_count; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -302,7 +304,8 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_OMAP_SCR, up->scr); res = (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; - if (gpiod_get_value(up->rts_gpiod) != res) { + if (up->rts_gpiod && + gpiod_get_value(up->rts_gpiod) != res) { if (port->rs485.delay_rts_after_send > 0) mdelay( port->rs485.delay_rts_after_send); @@ -328,19 +331,6 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } - if ((port->rs485.flags & SER_RS485_ENABLED) && - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { - /* - * Empty the RX FIFO, we are not interested in anything - * received during the half-duplex transmission. - */ - serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_RCVR); - /* Re-enable RX interrupts */ - up->ier |= UART_IER_RLSI | UART_IER_RDI; - up->port.read_status_mask |= UART_LSR_DR; - serial_out(up, UART_IER, up->ier); - } - pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } @@ -366,6 +356,10 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) serial_out(up, UART_TX, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) + up->rs485_tx_filter_count++; + return; } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { @@ -377,6 +371,10 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); up->port.icount.tx++; + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) + up->rs485_tx_filter_count++; + if (uart_circ_empty(xmit)) break; } while (--count > 0); @@ -411,7 +409,7 @@ static void serial_omap_start_tx(struct uart_port *port) /* if rts not already enabled */ res = (port->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0; - if (gpiod_get_value(up->rts_gpiod) != res) { + if (up->rts_gpiod && gpiod_get_value(up->rts_gpiod) != res) { gpiod_set_value(up->rts_gpiod, res); if (port->rs485.delay_rts_before_send > 0) mdelay(port->rs485.delay_rts_before_send); @@ -420,7 +418,7 @@ static void serial_omap_start_tx(struct uart_port *port) if ((port->rs485.flags & SER_RS485_ENABLED) && !(port->rs485.flags & SER_RS485_RX_DURING_TX)) - serial_omap_stop_rx(port); + up->rs485_tx_filter_count = 0; serial_omap_enable_ier_thri(up); pm_runtime_mark_last_busy(up->dev); @@ -491,8 +489,13 @@ static void serial_omap_rlsi(struct uart_omap_port *up, unsigned int lsr) * Read one data character out to avoid stalling the receiver according * to the table 23-246 of the omap4 TRM. */ - if (likely(lsr & UART_LSR_DR)) + if (likely(lsr & UART_LSR_DR)) { serial_in(up, UART_RX); + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && + up->rs485_tx_filter_count) + up->rs485_tx_filter_count--; + } up->port.icount.rx++; flag = TTY_NORMAL; @@ -543,6 +546,13 @@ static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr) return; ch = serial_in(up, UART_RX); + if ((up->port.rs485.flags & SER_RS485_ENABLED) && + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && + up->rs485_tx_filter_count) { + up->rs485_tx_filter_count--; + return; + } + flag = TTY_NORMAL; up->port.icount.rx++; @@ -1407,18 +1417,13 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) /* store new config */ port->rs485 = *rs485; - /* - * Just as a precaution, only allow rs485 - * to be enabled if the gpio pin is valid - */ if (up->rts_gpiod) { /* enable / disable rts */ val = (port->rs485.flags & SER_RS485_ENABLED) ? SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND; val = (port->rs485.flags & val) ? 1 : 0; gpiod_set_value(up->rts_gpiod, val); - } else - port->rs485.flags &= ~SER_RS485_ENABLED; + } /* Enable interrupts */ up->ier = mode; diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index abc6042f0378..91f1eb0058d7 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -247,9 +247,7 @@ static void owl_uart_receive_chars(struct uart_port *port) stat = owl_uart_read(port, OWL_UART_STAT); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static irqreturn_t owl_uart_irq(int irq, void *dev_id) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index a7363bc66c11..f0351e6f0ef6 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -236,7 +236,6 @@ struct eg20t_port { void *rx_buf_virt; dma_addr_t rx_buf_dma; - struct dentry *debugfs; #define IRQ_NAME_SIZE 17 char irq_name[IRQ_NAME_SIZE]; @@ -292,8 +291,6 @@ static const int trigger_level_64[4] = { 1, 16, 32, 56 }; static const int trigger_level_16[4] = { 1, 4, 8, 14 }; static const int trigger_level_1[4] = { 1, 1, 1, 1 }; -#ifdef CONFIG_DEBUG_FS - #define PCH_REGS_BUFSIZE 1024 @@ -353,7 +350,6 @@ static const struct file_operations port_regs_ops = { .read = port_show_regs, .llseek = default_llseek, }; -#endif /* CONFIG_DEBUG_FS */ static const struct dmi_system_id pch_uart_dmi_table[] = { { @@ -1735,9 +1731,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, int fifosize; int port_type; struct pch_uart_driver_data *board; -#ifdef CONFIG_DEBUG_FS - char name[32]; /* for debugfs file name */ -#endif + char name[32]; board = &drv_dat[id->driver_data]; port_type = board->port_type; @@ -1813,11 +1807,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, if (ret < 0) goto init_port_hal_free; -#ifdef CONFIG_DEBUG_FS - snprintf(name, sizeof(name), "uart%d_regs", board->line_no); - priv->debugfs = debugfs_create_file(name, S_IFREG | S_IRUGO, - NULL, priv, &port_regs_ops); -#endif + snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); + debugfs_create_file(name, S_IFREG | S_IRUGO, NULL, priv, + &port_regs_ops); return priv; @@ -1835,10 +1827,10 @@ init_port_alloc_err: static void pch_uart_exit_port(struct eg20t_port *priv) { + char name[32]; -#ifdef CONFIG_DEBUG_FS - debugfs_remove(priv->debugfs); -#endif + snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); + debugfs_remove(debugfs_lookup(name, NULL)); uart_remove_one_port(&pch_uart_driver, &priv->port); free_page((unsigned long)priv->rxbuf.buf); } diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 0d85b55ea823..00bb88a71606 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -818,7 +818,6 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) u32 s_irq_status; u32 geni_status; struct uart_port *uport = dev; - unsigned long flags; bool drop_rx = false; struct tty_port *tport = &uport->state->port; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); @@ -826,7 +825,8 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) if (uport->suspended) return IRQ_NONE; - spin_lock_irqsave(&uport->lock, flags); + spin_lock(&uport->lock); + m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); geni_status = readl(uport->membase + SE_GENI_STATUS); @@ -861,7 +861,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) qcom_geni_serial_handle_rx(uport, drop_rx); out_unlock: - uart_unlock_and_check_sysrq(uport, flags); + uart_unlock_and_check_sysrq(uport); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index 85366e059258..d550d8fa2fab 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -398,9 +398,7 @@ static void rda_uart_receive_chars(struct uart_port *port) status = rda_uart_read(port, RDA_UART_STATUS); } - spin_unlock(&port->lock); tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } static irqreturn_t rda_interrupt(int irq, void *dev_id) diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 5690c09cc041..d60abffab70e 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -424,9 +424,7 @@ static void rp2_rx_chars(struct rp2_uart_port *up) up->port.icount.rx++; } - spin_unlock(&up->port.lock); tty_flip_buffer_push(port); - spin_lock(&up->port.lock); } static void rp2_tx_chars(struct rp2_uart_port *up) diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index f5fab1dd96bc..697b6a002a16 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -223,9 +223,7 @@ sa1100_rx_chars(struct sa1100_port *sport) UTSR0_TO_SM(UART_GET_UTSR0(sport)); } - spin_unlock(&sport->port.lock); tty_flip_buffer_push(&sport->port.state->port); - spin_lock(&sport->port.lock); } static void sa1100_tx_chars(struct sa1100_port *sport) diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 8ae3e03fbd8c..d9e4b67a12a0 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -56,9 +56,16 @@ /* flag to ignore all characters coming in */ #define RXSTAT_DUMMY_READ (0x10000000) +enum s3c24xx_port_type { + TYPE_S3C24XX, + TYPE_S3C6400, + TYPE_APPLE_S5L, +}; + struct s3c24xx_uart_info { char *name; - unsigned int type; + enum s3c24xx_port_type type; + unsigned int port_type; unsigned int fifosize; unsigned long rx_fifomask; unsigned long rx_fifoshift; @@ -70,6 +77,7 @@ struct s3c24xx_uart_info { unsigned long num_clks; unsigned long clksel_mask; unsigned long clksel_shift; + unsigned long ucon_mask; /* uart port features */ @@ -144,6 +152,8 @@ struct s3c24xx_uart_port { #endif }; +static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport); + /* conversion functions */ #define s3c24xx_dev_to_port(__dev) dev_get_drvdata(__dev) @@ -228,16 +238,6 @@ static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) return rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE; } -/* - * s3c64xx and later SoC's include the interrupt mask and status registers in - * the controller itself, unlike the s3c24xx SoC's which have these registers - * in the interrupt controller. Check if the port type is s3c64xx or higher. - */ -static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port) -{ - return to_ourport(port)->info->type == PORT_S3C6400; -} - static void s3c24xx_serial_rx_enable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); @@ -289,10 +289,17 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) if (!ourport->tx_enabled) return; - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_set_bit(port, S3C64XX_UINTM_TXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + s3c24xx_clear_bit(port, APPLE_S5L_UCON_TXTHRESH_ENA, S3C2410_UCON); + break; + default: disable_irq_nosync(ourport->tx_irq); + break; + } if (dma && dma->tx_chan && ourport->tx_in_progress == S3C24XX_TX_DMA) { dmaengine_pause(dma->tx_chan); @@ -353,10 +360,17 @@ static void enable_tx_dma(struct s3c24xx_uart_port *ourport) u32 ucon; /* Mask Tx interrupt */ - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_set_bit(port, S3C64XX_UINTM_TXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + WARN_ON(1); // No DMA + break; + default: disable_irq_nosync(ourport->tx_irq); + break; + } /* Enable tx dma mode */ ucon = rd_regl(port, S3C2410_UCON); @@ -386,13 +400,28 @@ static void enable_tx_pio(struct s3c24xx_uart_port *ourport) wr_regl(port, S3C2410_UCON, ucon); /* Unmask Tx interrupt */ - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_clear_bit(port, S3C64XX_UINTM_TXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + ucon |= APPLE_S5L_UCON_TXTHRESH_ENA_MSK; + wr_regl(port, S3C2410_UCON, ucon); + break; + default: enable_irq(ourport->tx_irq); + break; + } ourport->tx_mode = S3C24XX_TX_PIO; + + /* + * The Apple version only has edge triggered TX IRQs, so we need + * to kick off the process by sending some characters here. + */ + if (ourport->info->type == TYPE_APPLE_S5L) + s3c24xx_serial_tx_chars(ourport); } static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport) @@ -513,11 +542,19 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port) if (ourport->rx_enabled) { dev_dbg(port->dev, "stopping rx\n"); - if (s3c24xx_serial_has_interrupt_mask(port)) + switch (ourport->info->type) { + case TYPE_S3C6400: s3c24xx_set_bit(port, S3C64XX_UINTM_RXD, S3C64XX_UINTM); - else + break; + case TYPE_APPLE_S5L: + s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); + s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + break; + default: disable_irq_nosync(ourport->rx_irq); + break; + } ourport->rx_enabled = 0; } if (dma && dma->rx_chan) { @@ -651,14 +688,18 @@ static void enable_rx_pio(struct s3c24xx_uart_port *ourport) /* set Rx mode to DMA mode */ ucon = rd_regl(port, S3C2410_UCON); - ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK | - S3C64XX_UCON_EMPTYINT_EN | - S3C64XX_UCON_DMASUS_EN | - S3C64XX_UCON_TIMEOUT_EN | - S3C64XX_UCON_RXMODE_MASK); - ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT | - S3C64XX_UCON_TIMEOUT_EN | - S3C64XX_UCON_RXMODE_CPU; + ucon &= ~S3C64XX_UCON_RXMODE_MASK; + ucon |= S3C64XX_UCON_RXMODE_CPU; + + /* Apple types use these bits for IRQ masks */ + if (ourport->info->type != TYPE_APPLE_S5L) { + ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK | + S3C64XX_UCON_EMPTYINT_EN | + S3C64XX_UCON_DMASUS_EN | + S3C64XX_UCON_TIMEOUT_EN); + ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT | + S3C64XX_UCON_TIMEOUT_EN; + } wr_regl(port, S3C2410_UCON, ucon); ourport->rx_mode = S3C24XX_RX_PIO; @@ -674,13 +715,12 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) struct s3c24xx_uart_dma *dma = ourport->dma; struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port); struct tty_port *t = &port->state->port; - unsigned long flags; struct dma_tx_state state; utrstat = rd_regl(port, S3C2410_UTRSTAT); rd_regl(port, S3C2410_UFSTAT); - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) { s3c64xx_start_rx_dma(ourport); @@ -709,7 +749,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT); finish: - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock(&port->lock); return IRQ_HANDLED; } @@ -805,16 +845,15 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + spin_lock(&port->lock); s3c24xx_serial_rx_drain_fifo(ourport); - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock(&port->lock); return IRQ_HANDLED; } -static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id) +static irqreturn_t s3c24xx_serial_rx_irq(int irq, void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; @@ -823,16 +862,12 @@ static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id) return s3c24xx_serial_rx_chars_pio(dev_id); } -static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) +static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; struct circ_buf *xmit = &port->state->xmit; - unsigned long flags; int count, dma_count = 0; - spin_lock_irqsave(&port->lock, flags); - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); if (ourport->dma && ourport->dma->tx_chan && @@ -849,7 +884,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) wr_reg(port, S3C2410_UTXH, port->x_char); port->icount.tx++; port->x_char = 0; - goto out; + return; } /* if there isn't anything more to transmit, or the uart is now @@ -858,7 +893,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { s3c24xx_serial_stop_tx(port); - goto out; + return; } /* try and drain the buffer... */ @@ -880,7 +915,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (!count && dma_count) { s3c24xx_serial_start_tx_dma(ourport, dma_count); - goto out; + return; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { @@ -891,9 +926,18 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) if (uart_circ_empty(xmit)) s3c24xx_serial_stop_tx(port); +} -out: - spin_unlock_irqrestore(&port->lock, flags); +static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) +{ + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + + spin_lock(&port->lock); + + s3c24xx_serial_tx_chars(ourport); + + spin_unlock(&port->lock); return IRQ_HANDLED; } @@ -906,16 +950,37 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) irqreturn_t ret = IRQ_HANDLED; if (pend & S3C64XX_UINTM_RXD_MSK) { - ret = s3c24xx_serial_rx_chars(irq, id); + ret = s3c24xx_serial_rx_irq(irq, id); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); } if (pend & S3C64XX_UINTM_TXD_MSK) { - ret = s3c24xx_serial_tx_chars(irq, id); + ret = s3c24xx_serial_tx_irq(irq, id); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); } return ret; } +/* interrupt handler for Apple SoC's.*/ +static irqreturn_t apple_serial_handle_irq(int irq, void *id) +{ + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + unsigned int pend = rd_regl(port, S3C2410_UTRSTAT); + irqreturn_t ret = IRQ_NONE; + + if (pend & (APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO)) { + wr_regl(port, S3C2410_UTRSTAT, + APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO); + ret = s3c24xx_serial_rx_irq(irq, id); + } + if (pend & APPLE_S5L_UTRSTAT_TXTHRESH) { + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_TXTHRESH); + ret = s3c24xx_serial_tx_irq(irq, id); + } + + return ret; +} + static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); @@ -1098,27 +1163,62 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); if (ourport->tx_claimed) { - if (!s3c24xx_serial_has_interrupt_mask(port)) - free_irq(ourport->tx_irq, ourport); + free_irq(ourport->tx_irq, ourport); ourport->tx_enabled = 0; ourport->tx_claimed = 0; ourport->tx_mode = 0; } if (ourport->rx_claimed) { - if (!s3c24xx_serial_has_interrupt_mask(port)) - free_irq(ourport->rx_irq, ourport); + free_irq(ourport->rx_irq, ourport); ourport->rx_claimed = 0; ourport->rx_enabled = 0; } - /* Clear pending interrupts and mask all interrupts */ - if (s3c24xx_serial_has_interrupt_mask(port)) { - free_irq(port->irq, ourport); + if (ourport->dma) + s3c24xx_serial_release_dma(ourport); - wr_regl(port, S3C64XX_UINTP, 0xf); - wr_regl(port, S3C64XX_UINTM, 0xf); - } + ourport->tx_in_progress = 0; +} + +static void s3c64xx_serial_shutdown(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + ourport->tx_enabled = 0; + ourport->tx_mode = 0; + ourport->rx_enabled = 0; + + free_irq(port->irq, ourport); + + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTM, 0xf); + + if (ourport->dma) + s3c24xx_serial_release_dma(ourport); + + ourport->tx_in_progress = 0; +} + +static void apple_s5l_serial_shutdown(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + unsigned int ucon; + + ucon = rd_regl(port, S3C2410_UCON); + ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK); + wr_regl(port, S3C2410_UCON, ucon); + + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); + + free_irq(port->irq, ourport); + + ourport->tx_enabled = 0; + ourport->tx_mode = 0; + ourport->rx_enabled = 0; if (ourport->dma) s3c24xx_serial_release_dma(ourport); @@ -1133,7 +1233,7 @@ static int s3c24xx_serial_startup(struct uart_port *port) ourport->rx_enabled = 1; - ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, + ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_irq, 0, s3c24xx_serial_portname(port), ourport); if (ret != 0) { @@ -1147,7 +1247,7 @@ static int s3c24xx_serial_startup(struct uart_port *port) ourport->tx_enabled = 1; - ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, + ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_irq, 0, s3c24xx_serial_portname(port), ourport); if (ret) { @@ -1193,9 +1293,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) /* For compatibility with s3c24xx Soc's */ ourport->rx_enabled = 1; - ourport->rx_claimed = 1; ourport->tx_enabled = 0; - ourport->tx_claimed = 1; spin_lock_irqsave(&port->lock, flags); @@ -1215,6 +1313,45 @@ static int s3c64xx_serial_startup(struct uart_port *port) return ret; } +static int apple_s5l_serial_startup(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + unsigned long flags; + unsigned int ufcon; + int ret; + + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); + + ret = request_irq(port->irq, apple_serial_handle_irq, 0, + s3c24xx_serial_portname(port), ourport); + if (ret) { + dev_err(port->dev, "cannot get irq %d\n", port->irq); + return ret; + } + + /* For compatibility with s3c24xx Soc's */ + ourport->rx_enabled = 1; + ourport->tx_enabled = 0; + + spin_lock_irqsave(&port->lock, flags); + + ufcon = rd_regl(port, S3C2410_UFCON); + ufcon |= S3C2410_UFCON_RESETRX | S5PV210_UFCON_RXTRIG8; + if (!uart_console(port)) + ufcon |= S3C2410_UFCON_RESETTX; + wr_regl(port, S3C2410_UFCON, ufcon); + + enable_rx_pio(ourport); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Enable Rx Interrupt */ + s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); + s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + + return ret; +} + /* power power management control */ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, @@ -1535,41 +1672,26 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, static const char *s3c24xx_serial_type(struct uart_port *port) { - switch (port->type) { - case PORT_S3C2410: - return "S3C2410"; - case PORT_S3C2440: - return "S3C2440"; - case PORT_S3C2412: - return "S3C2412"; - case PORT_S3C6400: + struct s3c24xx_uart_port *ourport = to_ourport(port); + + switch (ourport->info->type) { + case TYPE_S3C24XX: + return "S3C24XX"; + case TYPE_S3C6400: return "S3C6400/10"; + case TYPE_APPLE_S5L: + return "APPLE S5L"; default: return NULL; } } -#define MAP_SIZE (0x100) - -static void s3c24xx_serial_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, MAP_SIZE); -} - -static int s3c24xx_serial_request_port(struct uart_port *port) -{ - const char *name = s3c24xx_serial_portname(port); - - return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY; -} - static void s3c24xx_serial_config_port(struct uart_port *port, int flags) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - if (flags & UART_CONFIG_TYPE && - s3c24xx_serial_request_port(port) == 0) - port->type = info->type; + if (flags & UART_CONFIG_TYPE) + port->type = info->port_type; } /* @@ -1580,7 +1702,7 @@ s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - if (ser->type != PORT_UNKNOWN && ser->type != info->type) + if (ser->type != PORT_UNKNOWN && ser->type != info->port_type) return -EINVAL; return 0; @@ -1608,7 +1730,7 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c); #endif -static struct uart_ops s3c24xx_serial_ops = { +static const struct uart_ops s3c24xx_serial_ops = { .pm = s3c24xx_serial_pm, .tx_empty = s3c24xx_serial_tx_empty, .get_mctrl = s3c24xx_serial_get_mctrl, @@ -1621,8 +1743,48 @@ static struct uart_ops s3c24xx_serial_ops = { .shutdown = s3c24xx_serial_shutdown, .set_termios = s3c24xx_serial_set_termios, .type = s3c24xx_serial_type, - .release_port = s3c24xx_serial_release_port, - .request_port = s3c24xx_serial_request_port, + .config_port = s3c24xx_serial_config_port, + .verify_port = s3c24xx_serial_verify_port, +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) + .poll_get_char = s3c24xx_serial_get_poll_char, + .poll_put_char = s3c24xx_serial_put_poll_char, +#endif +}; + +static const struct uart_ops s3c64xx_serial_ops = { + .pm = s3c24xx_serial_pm, + .tx_empty = s3c24xx_serial_tx_empty, + .get_mctrl = s3c24xx_serial_get_mctrl, + .set_mctrl = s3c24xx_serial_set_mctrl, + .stop_tx = s3c24xx_serial_stop_tx, + .start_tx = s3c24xx_serial_start_tx, + .stop_rx = s3c24xx_serial_stop_rx, + .break_ctl = s3c24xx_serial_break_ctl, + .startup = s3c64xx_serial_startup, + .shutdown = s3c64xx_serial_shutdown, + .set_termios = s3c24xx_serial_set_termios, + .type = s3c24xx_serial_type, + .config_port = s3c24xx_serial_config_port, + .verify_port = s3c24xx_serial_verify_port, +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) + .poll_get_char = s3c24xx_serial_get_poll_char, + .poll_put_char = s3c24xx_serial_put_poll_char, +#endif +}; + +static const struct uart_ops apple_s5l_serial_ops = { + .pm = s3c24xx_serial_pm, + .tx_empty = s3c24xx_serial_tx_empty, + .get_mctrl = s3c24xx_serial_get_mctrl, + .set_mctrl = s3c24xx_serial_set_mctrl, + .stop_tx = s3c24xx_serial_stop_tx, + .start_tx = s3c24xx_serial_start_tx, + .stop_rx = s3c24xx_serial_stop_rx, + .break_ctl = s3c24xx_serial_break_ctl, + .startup = apple_s5l_serial_startup, + .shutdown = apple_s5l_serial_shutdown, + .set_termios = s3c24xx_serial_set_termios, + .type = s3c24xx_serial_type, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, #if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) @@ -1706,14 +1868,9 @@ static void s3c24xx_serial_resetport(struct uart_port *port, { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); unsigned long ucon = rd_regl(port, S3C2410_UCON); - unsigned int ucon_mask; - ucon_mask = info->clksel_mask; - if (info->type == PORT_S3C2440) - ucon_mask |= S3C2440_UCON0_DIVMASK; - - ucon &= ucon_mask; - wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + ucon &= (info->clksel_mask | info->ucon_mask); + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); /* reset both fifos */ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); @@ -1868,10 +2025,6 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, /* setup info for port */ port->dev = &platdev->dev; - /* Startup sequence is different for s3c64xx and higher SoC's */ - if (s3c24xx_serial_has_interrupt_mask(port)) - s3c24xx_serial_ops.startup = s3c64xx_serial_startup; - port->uartclk = 1; if (cfg->uart_flags & UPF_CONS_FLOW) { @@ -1889,8 +2042,8 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, dev_dbg(port->dev, "resource %pR)\n", res); - port->membase = devm_ioremap(port->dev, res->start, resource_size(res)); - if (!port->membase) { + port->membase = devm_ioremap_resource(port->dev, res); + if (IS_ERR(port->membase)) { dev_err(port->dev, "failed to remap controller address\n"); return -EBUSY; } @@ -1905,11 +2058,16 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, ourport->tx_irq = ret + 1; } - if (!s3c24xx_serial_has_interrupt_mask(port)) { + switch (ourport->info->type) { + case TYPE_S3C24XX: ret = platform_get_irq(platdev, 1); if (ret > 0) ourport->tx_irq = ret; + break; + default: + break; } + /* * DMA is currently supported only on DT platforms, if DMA properties * are specified. @@ -1945,10 +2103,26 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, pr_warn("uart: failed to enable baudclk\n"); /* Keep all interrupts masked and cleared */ - if (s3c24xx_serial_has_interrupt_mask(port)) { + switch (ourport->info->type) { + case TYPE_S3C6400: wr_regl(port, S3C64XX_UINTM, 0xf); wr_regl(port, S3C64XX_UINTP, 0xf); wr_regl(port, S3C64XX_UINTSP, 0xf); + break; + case TYPE_APPLE_S5L: { + unsigned int ucon; + + ucon = rd_regl(port, S3C2410_UCON); + ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK); + wr_regl(port, S3C2410_UCON, ucon); + + wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); + break; + } + default: + break; } dev_dbg(port->dev, "port: map=%pa, mem=%p, irq=%d (%d,%d), clock=%u\n", @@ -2019,6 +2193,18 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) dev_get_platdata(&pdev->dev) : ourport->drv_data->def_cfg; + switch (ourport->info->type) { + case TYPE_S3C24XX: + ourport->port.ops = &s3c24xx_serial_ops; + break; + case TYPE_S3C6400: + ourport->port.ops = &s3c64xx_serial_ops; + break; + case TYPE_APPLE_S5L: + ourport->port.ops = &apple_s5l_serial_ops; + break; + } + if (np) { of_property_read_u32(np, "samsung,uart-fifosize", &ourport->port.fifosize); @@ -2142,7 +2328,8 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) if (port) { /* restore IRQ mask */ - if (s3c24xx_serial_has_interrupt_mask(port)) { + switch (ourport->info->type) { + case TYPE_S3C6400: { unsigned int uintm = 0xf; if (ourport->tx_enabled) @@ -2156,6 +2343,47 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) if (!IS_ERR(ourport->baudclk)) clk_disable_unprepare(ourport->baudclk); clk_disable_unprepare(ourport->clk); + break; + } + case TYPE_APPLE_S5L: { + unsigned int ucon; + int ret; + + ret = clk_prepare_enable(ourport->clk); + if (ret) { + dev_err(dev, "clk_enable clk failed: %d\n", ret); + return ret; + } + if (!IS_ERR(ourport->baudclk)) { + ret = clk_prepare_enable(ourport->baudclk); + if (ret) { + dev_err(dev, "clk_enable baudclk failed: %d\n", ret); + clk_disable_unprepare(ourport->clk); + return ret; + } + } + + ucon = rd_regl(port, S3C2410_UCON); + + ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK); + + if (ourport->tx_enabled) + ucon |= APPLE_S5L_UCON_TXTHRESH_ENA_MSK; + if (ourport->rx_enabled) + ucon |= APPLE_S5L_UCON_RXTHRESH_ENA_MSK | + APPLE_S5L_UCON_RXTO_ENA_MSK; + + wr_regl(port, S3C2410_UCON, ucon); + + if (!IS_ERR(ourport->baudclk)) + clk_disable_unprepare(ourport->baudclk); + clk_disable_unprepare(ourport->clk); + break; + } + default: + break; } } @@ -2380,7 +2608,8 @@ static struct console s3c24xx_serial_console = { static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C2410 UART", - .type = PORT_S3C2410, + .type = TYPE_S3C24XX, + .port_type = PORT_S3C2410, .fifosize = 16, .rx_fifomask = S3C2410_UFSTAT_RXMASK, .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, @@ -2407,7 +2636,8 @@ static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = { static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C2412 UART", - .type = PORT_S3C2412, + .type = TYPE_S3C24XX, + .port_type = PORT_S3C2412, .fifosize = 64, .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, @@ -2436,7 +2666,8 @@ static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = { static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C2440 UART", - .type = PORT_S3C2440, + .type = TYPE_S3C24XX, + .port_type = PORT_S3C2440, .fifosize = 64, .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, @@ -2449,6 +2680,7 @@ static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = { .num_clks = 4, .clksel_mask = S3C2412_UCON_CLKMASK, .clksel_shift = S3C2412_UCON_CLKSHIFT, + .ucon_mask = S3C2440_UCON0_DIVMASK, }, .def_cfg = &(struct s3c2410_uartcfg) { .ucon = S3C2410_UCON_DEFAULT, @@ -2464,7 +2696,8 @@ static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = { static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S3C6400 UART", - .type = PORT_S3C6400, + .type = TYPE_S3C6400, + .port_type = PORT_S3C6400, .fifosize = 64, .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, @@ -2492,7 +2725,8 @@ static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = { static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung S5PV210 UART", - .type = PORT_S3C6400, + .type = TYPE_S3C6400, + .port_type = PORT_S3C6400, .has_divslot = 1, .rx_fifomask = S5PV210_UFSTAT_RXMASK, .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, @@ -2520,7 +2754,8 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #define EXYNOS_COMMON_SERIAL_DRV_DATA \ .info = &(struct s3c24xx_uart_info) { \ .name = "Samsung Exynos UART", \ - .type = PORT_S3C6400, \ + .type = TYPE_S3C6400, \ + .port_type = PORT_S3C6400, \ .has_divslot = 1, \ .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ @@ -2556,6 +2791,34 @@ static struct s3c24xx_serial_drv_data exynos5433_serial_drv_data = { #define EXYNOS5433_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif +#ifdef CONFIG_ARCH_APPLE +static struct s3c24xx_serial_drv_data s5l_serial_drv_data = { + .info = &(struct s3c24xx_uart_info) { + .name = "Apple S5L UART", + .type = TYPE_APPLE_S5L, + .port_type = PORT_8250, + .fifosize = 16, + .rx_fifomask = S3C2410_UFSTAT_RXMASK, + .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2410_UFSTAT_RXFULL, + .tx_fifofull = S3C2410_UFSTAT_TXFULL, + .tx_fifomask = S3C2410_UFSTAT_TXMASK, + .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT, + .def_clk_sel = S3C2410_UCON_CLKSEL0, + .num_clks = 1, + .clksel_mask = 0, + .clksel_shift = 0, + }, + .def_cfg = &(struct s3c2410_uartcfg) { + .ucon = APPLE_S5L_UCON_DEFAULT, + .ufcon = S3C2410_UFCON_DEFAULT, + }, +}; +#define S5L_SERIAL_DRV_DATA ((kernel_ulong_t)&s5l_serial_drv_data) +#else +#define S5L_SERIAL_DRV_DATA ((kernel_ulong_t)NULL) +#endif + static const struct platform_device_id s3c24xx_serial_driver_ids[] = { { .name = "s3c2410-uart", @@ -2578,6 +2841,9 @@ static const struct platform_device_id s3c24xx_serial_driver_ids[] = { }, { .name = "exynos5433-uart", .driver_data = EXYNOS5433_SERIAL_DRV_DATA, + }, { + .name = "s5l-uart", + .driver_data = S5L_SERIAL_DRV_DATA, }, { }, }; @@ -2599,6 +2865,8 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = { .data = (void *)EXYNOS4210_SERIAL_DRV_DATA }, { .compatible = "samsung,exynos5433-uart", .data = (void *)EXYNOS5433_SERIAL_DRV_DATA }, + { .compatible = "apple,s5l-uart", + .data = (void *)S5L_SERIAL_DRV_DATA }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); @@ -2730,6 +2998,23 @@ OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart", s5pv210_early_console_setup); OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart", s5pv210_early_console_setup); + +/* Apple S5L */ +static int __init apple_s5l_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + /* Close enough to S3C2410 for earlycon... */ + device->port.private_data = &s3c2410_early_console_data; + +#ifdef CONFIG_ARM64 + /* ... but we need to override the existing fixmap entry as nGnRnE */ + __set_fixmap(FIX_EARLYCON_MEM_BASE, device->port.mapbase, + __pgprot(PROT_DEVICE_nGnRnE)); +#endif + return samsung_early_console_setup(device, opt); +} + +OF_EARLYCON_DECLARE(s5l, "apple,s5l-uart", apple_s5l_early_console_setup); #endif MODULE_ALIAS("platform:samsung-uart"); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index f86ec2d2635b..9adb8362578c 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1196,7 +1196,7 @@ static int sc16is7xx_probe(struct device *dev, ret = regmap_read(regmap, SC16IS7XX_LSR_REG << SC16IS7XX_REG_SHIFT, &val); if (ret < 0) - return ret; + return -EPROBE_DEFER; /* Alloc port structure */ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ba31e97d3d96..87f7127b57e6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -759,8 +759,6 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) struct uart_port *uport; int ret = -ENODEV; - memset(retinfo, 0, sizeof(*retinfo)); - /* * Ensure the state we copy is consistent and no hardware changes * occur as we go @@ -1305,7 +1303,7 @@ static int uart_set_rs485_config(struct uart_port *port, unsigned long flags; if (!port->rs485_config) - return -ENOIOCTLCMD; + return -ENOTTY; if (copy_from_user(&rs485, rs485_user, sizeof(*rs485_user))) return -EFAULT; @@ -1329,7 +1327,7 @@ static int uart_get_iso7816_config(struct uart_port *port, struct serial_iso7816 aux; if (!port->iso7816_config) - return -ENOIOCTLCMD; + return -ENOTTY; spin_lock_irqsave(&port->lock, flags); aux = port->iso7816; @@ -1349,7 +1347,7 @@ static int uart_set_iso7816_config(struct uart_port *port, unsigned long flags; if (!port->iso7816_config) - return -ENOIOCTLCMD; + return -ENOTTY; if (copy_from_user(&iso7816, iso7816_user, sizeof(*iso7816_user))) return -EFAULT; diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 7a07e7272de1..0a7e5b74bc1d 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -330,9 +330,9 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status) up->port.ignore_status_mask = next_ignore_status_mask; disr = sio_in(up, TXX9_SIDISR); } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); - spin_unlock(&up->port.lock); + tty_flip_buffer_push(&up->port.state->port); - spin_lock(&up->port.lock); + *status = disr; } diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e1179e74a2b8..ef37fdf37612 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2124,7 +2124,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) unsigned short scscr, scsptr; unsigned long flags; - /* check wheter the port has SCSPTR */ + /* check whether the port has SCSPTR */ if (!sci_getreg(port, SCSPTR)->size) { /* * Not supported by hardware. Most parts couple break and rx @@ -2609,21 +2609,10 @@ done: udelay(DIV_ROUND_UP(10 * 1000000, baud)); } - /* - * Calculate delay for 2 DMA buffers (4 FIFO). - * See serial_core.c::uart_update_timeout(). - * With 10 bits (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above - * function calculates 1 jiffie for the data plus 5 jiffies for the - * "slop(e)." Then below we calculate 5 jiffies (20ms) for 2 DMA - * buffers (4 FIFO sizes), but when performing a faster transfer, the - * value obtained by this formula is too small. Therefore, if the value - * is smaller than 20ms, use 20ms as the timeout value for DMA. - */ + /* Calculate delay for 2 DMA buffers (4 FIFO). */ s->rx_frame = (10000 * bits) / (baud / 100); #ifdef CONFIG_SERIAL_SH_SCI_DMA 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) diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index 328d5a78792f..0ac0371f943b 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -448,9 +448,7 @@ static void __ssp_receive_chars(struct sifive_serial_port *ssp) uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL); } - spin_unlock(&ssp->port.lock); tty_flip_buffer_push(&ssp->port.state->port); - spin_lock(&ssp->port.lock); } /** diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index b3675cf25a69..c2ae7b392b86 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -218,8 +218,7 @@ static void stm32_usart_receive_chars(struct uart_port *port, bool threaded) u32 sr; char flag; - if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) - pm_wakeup_event(tport->tty->dev, 0); + spin_lock(&port->lock); while (stm32_usart_pending_rx(port, &sr, &stm32_port->last_res, threaded)) { @@ -271,14 +270,14 @@ static void stm32_usart_receive_chars(struct uart_port *port, bool threaded) } } - if (uart_handle_sysrq_char(port, c)) + if (uart_prepare_sysrq_char(port, c)) continue; uart_insert_char(port, sr, USART_SR_ORE, c, flag); } - spin_unlock(&port->lock); + uart_unlock_and_check_sysrq(port); + tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void stm32_usart_tx_dma_complete(void *arg) @@ -286,12 +285,16 @@ static void stm32_usart_tx_dma_complete(void *arg) struct uart_port *port = arg; struct stm32_port *stm32port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + unsigned long flags; + dmaengine_terminate_async(stm32port->tx_ch); stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32port->tx_dma_busy = false; /* Let's see if we have pending data to send */ + spin_lock_irqsave(&port->lock, flags); stm32_usart_transmit_chars(port); + spin_unlock_irqrestore(&port->lock, flags); } static void stm32_usart_tx_interrupt_enable(struct uart_port *port) @@ -303,7 +306,7 @@ static void stm32_usart_tx_interrupt_enable(struct uart_port *port) * Enables TX FIFO threashold irq when FIFO is enabled, * or TX empty irq when FIFO is disabled */ - if (stm32_port->fifoen) + if (stm32_port->fifoen && stm32_port->txftcfg >= 0) stm32_usart_set_bits(port, ofs->cr3, USART_CR3_TXFTIE); else stm32_usart_set_bits(port, ofs->cr1, USART_CR1_TXEIE); @@ -314,7 +317,7 @@ static void stm32_usart_tx_interrupt_disable(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - if (stm32_port->fifoen) + if (stm32_port->fifoen && stm32_port->txftcfg >= 0) stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); else stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); @@ -455,29 +458,34 @@ static void stm32_usart_transmit_chars(struct uart_port *port) static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct tty_port *tport = &port->state->port; struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; - spin_lock(&port->lock); - sr = readl_relaxed(port->membase + ofs->isr); if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) writel_relaxed(USART_ICR_RTOCF, port->membase + ofs->icr); - if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) + if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) { + /* Clear wake up flag and disable wake up interrupt */ writel_relaxed(USART_ICR_WUCF, port->membase + ofs->icr); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); + if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) + pm_wakeup_event(tport->tty->dev, 0); + } if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) stm32_usart_receive_chars(port, false); - if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) + if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) { + spin_lock(&port->lock); stm32_usart_transmit_chars(port); - - spin_unlock(&port->lock); + spin_unlock(&port->lock); + } if (stm32_port->rx_ch) return IRQ_WAKE_THREAD; @@ -490,13 +498,9 @@ static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr) struct uart_port *port = ptr; struct stm32_port *stm32_port = to_stm32_port(port); - spin_lock(&port->lock); - if (stm32_port->rx_ch) stm32_usart_receive_chars(port, true); - spin_unlock(&port->lock); - return IRQ_HANDLED; } @@ -505,7 +509,10 @@ static unsigned int stm32_usart_tx_empty(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE; + if (readl_relaxed(port->membase + ofs->isr) & USART_SR_TC) + return TIOCSER_TEMT; + + return 0; } static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -584,6 +591,19 @@ static void stm32_usart_start_tx(struct uart_port *port) stm32_usart_transmit_chars(port); } +/* Flush the transmit buffer. */ +static void stm32_usart_flush_buffer(struct uart_port *port) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + if (stm32_port->tx_ch) { + dmaengine_terminate_async(stm32_port->tx_ch); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_port->tx_dma_busy = false; + } +} + /* Throttle the remote when input buffer is about to overflow. */ static void stm32_usart_throttle(struct uart_port *port) { @@ -634,33 +654,30 @@ static int stm32_usart_startup(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + const struct stm32_usart_config *cfg = &stm32_port->info->cfg; const char *name = to_platform_device(port->dev)->name; u32 val; int ret; ret = request_threaded_irq(port->irq, stm32_usart_interrupt, stm32_usart_threaded_interrupt, - IRQF_NO_SUSPEND, name, port); + IRQF_ONESHOT | IRQF_NO_SUSPEND, + name, port); if (ret) return ret; + if (stm32_port->swap) { + val = readl_relaxed(port->membase + ofs->cr2); + val |= USART_CR2_SWAP; + writel_relaxed(val, port->membase + ofs->cr2); + } + /* RX FIFO Flush */ if (ofs->rqr != UNDEF_REG) - stm32_usart_set_bits(port, ofs->rqr, USART_RQR_RXFRQ); + writel_relaxed(USART_RQR_RXFRQ, port->membase + ofs->rqr); - /* Tx and RX FIFO configuration */ - if (stm32_port->fifoen) { - val = readl_relaxed(port->membase + ofs->cr3); - val &= ~(USART_CR3_TXFTCFG_MASK | USART_CR3_RXFTCFG_MASK); - val |= USART_CR3_TXFTCFG_HALF << USART_CR3_TXFTCFG_SHIFT; - val |= USART_CR3_RXFTCFG_HALF << USART_CR3_RXFTCFG_SHIFT; - writel_relaxed(val, port->membase + ofs->cr3); - } - - /* RX FIFO enabling */ - val = stm32_port->cr1_irq | USART_CR1_RE; - if (stm32_port->fifoen) - val |= USART_CR1_FIFOEN; + /* RX enabling */ + val = stm32_port->cr1_irq | USART_CR1_RE | BIT(cfg->uart_enable_bit); stm32_usart_set_bits(port, ofs->cr1, val); return 0; @@ -691,6 +708,11 @@ static void stm32_usart_shutdown(struct uart_port *port) if (ret) dev_err(port->dev, "Transmission is not complete\n"); + /* flush RX & TX FIFO */ + if (ofs->rqr != UNDEF_REG) + writel_relaxed(USART_RQR_TXFRQ | USART_RQR_RXFRQ, + port->membase + ofs->rqr); + stm32_usart_clr_bits(port, ofs->cr1, val); free_irq(port->irq, port); @@ -737,8 +759,9 @@ static void stm32_usart_set_termios(struct uart_port *port, unsigned int baud, bits; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; - u32 cr1, cr2, cr3; + u32 cr1, cr2, cr3, isr; unsigned long flags; + int ret; if (!stm32_port->hw_flow_control) cflag &= ~CRTSCTS; @@ -747,21 +770,37 @@ static void stm32_usart_set_termios(struct uart_port *port, spin_lock_irqsave(&port->lock, flags); + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, + (isr & USART_SR_TC), + 10, 100000); + + /* Send the TC error message only when ISR_TC is not set. */ + if (ret) + dev_err(port->dev, "Transmission is not complete\n"); + /* Stop serial port and reset value */ writel_relaxed(0, port->membase + ofs->cr1); /* flush RX & TX FIFO */ if (ofs->rqr != UNDEF_REG) - stm32_usart_set_bits(port, ofs->rqr, - USART_RQR_TXFRQ | USART_RQR_RXFRQ); + writel_relaxed(USART_RQR_TXFRQ | USART_RQR_RXFRQ, + port->membase + ofs->rqr); cr1 = USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) cr1 |= USART_CR1_FIFOEN; - cr2 = 0; + cr2 = stm32_port->swap ? USART_CR2_SWAP : 0; + + /* Tx and RX FIFO configuration */ cr3 = readl_relaxed(port->membase + ofs->cr3); - cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTCFG_MASK | USART_CR3_RXFTIE - | USART_CR3_TXFTCFG_MASK; + cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTIE; + if (stm32_port->fifoen) { + if (stm32_port->txftcfg >= 0) + cr3 |= stm32_port->txftcfg << USART_CR3_TXFTCFG_SHIFT; + if (stm32_port->rxftcfg >= 0) + cr3 |= stm32_port->rxftcfg << USART_CR3_RXFTCFG_SHIFT; + } if (cflag & CSTOPB) cr2 |= USART_CR2_STOP_2B; @@ -790,7 +829,8 @@ static void stm32_usart_set_termios(struct uart_port *port, , bits); if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch || - stm32_port->fifoen)) { + (stm32_port->fifoen && + stm32_port->rxftcfg >= 0))) { if (cflag & CSTOPB) bits = bits + 3; /* 1 start bit + 2 stop bits */ else @@ -817,12 +857,6 @@ static void stm32_usart_set_termios(struct uart_port *port, cr3 |= USART_CR3_CTSE | USART_CR3_RTSE; } - /* Handle modem control interrupts */ - if (UART_ENABLE_MS(port, termios->c_cflag)) - stm32_usart_enable_ms(port); - else - stm32_usart_disable_ms(port); - usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud); /* @@ -892,12 +926,24 @@ static void stm32_usart_set_termios(struct uart_port *port, cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); } + /* Configure wake up from low power on start bit detection */ + if (stm32_port->wakeup_src) { + cr3 &= ~USART_CR3_WUS_MASK; + cr3 |= USART_CR3_WUS_START_BIT; + } + writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr2, port->membase + ofs->cr2); writel_relaxed(cr1, port->membase + ofs->cr1); stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); + + /* Handle modem control interrupts */ + if (UART_ENABLE_MS(port, termios->c_cflag)) + stm32_usart_enable_ms(port); + else + stm32_usart_disable_ms(port); } static const char *stm32_usart_type(struct uart_port *port) @@ -962,6 +1008,7 @@ static const struct uart_ops stm32_uart_ops = { .break_ctl = stm32_usart_break_ctl, .startup = stm32_usart_startup, .shutdown = stm32_usart_shutdown, + .flush_buffer = stm32_usart_flush_buffer, .set_termios = stm32_usart_set_termios, .pm = stm32_usart_pm, .type = stm32_usart_type, @@ -971,6 +1018,39 @@ static const struct uart_ops stm32_uart_ops = { .verify_port = stm32_usart_verify_port, }; +/* + * STM32H7 RX & TX FIFO threshold configuration (CR3 RXFTCFG / TXFTCFG) + * Note: 1 isn't a valid value in RXFTCFG / TXFTCFG. In this case, + * RXNEIE / TXEIE can be used instead of threshold irqs: RXFTIE / TXFTIE. + * So, RXFTCFG / TXFTCFG bitfields values are encoded as array index + 1. + */ +static const u32 stm32h7_usart_fifo_thresh_cfg[] = { 1, 2, 4, 8, 12, 14, 16 }; + +static void stm32_usart_get_ftcfg(struct platform_device *pdev, const char *p, + int *ftcfg) +{ + u32 bytes, i; + + /* DT option to get RX & TX FIFO threshold (default to 8 bytes) */ + if (of_property_read_u32(pdev->dev.of_node, p, &bytes)) + bytes = 8; + + for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++) + if (stm32h7_usart_fifo_thresh_cfg[i] >= bytes) + break; + if (i >= ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg)) + i = ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg) - 1; + + dev_dbg(&pdev->dev, "%s set to %d bytes\n", p, + stm32h7_usart_fifo_thresh_cfg[i]); + + /* Provide FIFO threshold ftcfg (1 is invalid: threshold irq unused) */ + if (i) + *ftcfg = i - 1; + else + *ftcfg = -EINVAL; +} + static void stm32_usart_deinit_port(struct stm32_port *stm32port) { clk_disable_unprepare(stm32port->clk); @@ -1000,13 +1080,19 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, if (ret) return ret; - if (stm32port->info->cfg.has_wakeup) { - stm32port->wakeirq = platform_get_irq_optional(pdev, 1); - if (stm32port->wakeirq <= 0 && stm32port->wakeirq != -ENXIO) - return stm32port->wakeirq ? : -ENODEV; - } + stm32port->wakeup_src = stm32port->info->cfg.has_wakeup && + of_property_read_bool(pdev->dev.of_node, "wakeup-source"); + + stm32port->swap = stm32port->info->cfg.has_swap && + of_property_read_bool(pdev->dev.of_node, "rx-tx-swap"); stm32port->fifoen = stm32port->info->cfg.has_fifo; + if (stm32port->fifoen) { + stm32_usart_get_ftcfg(pdev, "rx-threshold", + &stm32port->rxftcfg); + stm32_usart_get_ftcfg(pdev, "tx-threshold", + &stm32port->txftcfg); + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); port->membase = devm_ioremap_resource(&pdev->dev, res); @@ -1106,6 +1192,13 @@ static int stm32_usart_of_dma_rx_probe(struct stm32_port *stm32port, struct dma_async_tx_descriptor *desc = NULL; int ret; + /* + * Using DMA and threaded handler for the console could lead to + * deadlocks. + */ + if (uart_console(port)) + return -ENODEV; + /* Request DMA RX channel */ stm32port->rx_ch = dma_request_slave_channel(dev, "rx"); if (!stm32port->rx_ch) { @@ -1239,23 +1332,13 @@ static int stm32_usart_serial_probe(struct platform_device *pdev) if (ret) return ret; - if (stm32port->wakeirq > 0) { - ret = device_init_wakeup(&pdev->dev, true); - if (ret) - goto err_uninit; - - ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, - stm32port->wakeirq); + if (stm32port->wakeup_src) { + device_set_wakeup_capable(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, stm32port->port.irq); if (ret) goto err_nowup; - - device_set_wakeup_enable(&pdev->dev, false); } - ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); - if (ret) - goto err_wirq; - ret = stm32_usart_of_dma_rx_probe(stm32port, pdev); if (ret) dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n"); @@ -1269,19 +1352,47 @@ static int stm32_usart_serial_probe(struct platform_device *pdev) pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); + + ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); + if (ret) + goto err_port; + pm_runtime_put_sync(&pdev->dev); return 0; -err_wirq: - if (stm32port->wakeirq > 0) +err_port: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + if (stm32port->rx_ch) { + dmaengine_terminate_async(stm32port->rx_ch); + dma_release_channel(stm32port->rx_ch); + } + + if (stm32port->rx_dma_buf) + dma_free_coherent(&pdev->dev, + RX_BUF_L, stm32port->rx_buf, + stm32port->rx_dma_buf); + + if (stm32port->tx_ch) { + dmaengine_terminate_async(stm32port->tx_ch); + dma_release_channel(stm32port->tx_ch); + } + + if (stm32port->tx_dma_buf) + dma_free_coherent(&pdev->dev, + TX_BUF_L, stm32port->tx_buf, + stm32port->tx_dma_buf); + + if (stm32port->wakeup_src) dev_pm_clear_wake_irq(&pdev->dev); err_nowup: - if (stm32port->wakeirq > 0) - device_init_wakeup(&pdev->dev, false); + if (stm32port->wakeup_src) + device_set_wakeup_capable(&pdev->dev, false); -err_uninit: stm32_usart_deinit_port(stm32port); return ret; @@ -1295,11 +1406,20 @@ static int stm32_usart_serial_remove(struct platform_device *pdev) int err; pm_runtime_get_sync(&pdev->dev); + err = uart_remove_one_port(&stm32_usart_driver, port); + if (err) + return(err); + + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR); - if (stm32_port->rx_ch) + if (stm32_port->rx_ch) { + dmaengine_terminate_async(stm32_port->rx_ch); dma_release_channel(stm32_port->rx_ch); + } if (stm32_port->rx_dma_buf) dma_free_coherent(&pdev->dev, @@ -1308,27 +1428,24 @@ static int stm32_usart_serial_remove(struct platform_device *pdev) stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT); - if (stm32_port->tx_ch) + if (stm32_port->tx_ch) { + dmaengine_terminate_async(stm32_port->tx_ch); dma_release_channel(stm32_port->tx_ch); + } if (stm32_port->tx_dma_buf) dma_free_coherent(&pdev->dev, TX_BUF_L, stm32_port->tx_buf, stm32_port->tx_dma_buf); - if (stm32_port->wakeirq > 0) { + if (stm32_port->wakeup_src) { dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); } stm32_usart_deinit_port(stm32_port); - err = uart_remove_one_port(&stm32_usart_driver, port); - - pm_runtime_disable(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); - - return err; + return 0; } #ifdef CONFIG_SERIAL_STM32_CONSOLE @@ -1354,13 +1471,10 @@ static void stm32_usart_console_write(struct console *co, const char *s, u32 old_cr1, new_cr1; int locked = 1; - local_irq_save(flags); - if (port->sysrq) - locked = 0; - else if (oops_in_progress) - locked = spin_trylock(&port->lock); + if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); else - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); /* Save and disable interrupts, enable the transmitter */ old_cr1 = readl_relaxed(port->membase + ofs->cr1); @@ -1374,8 +1488,7 @@ static void stm32_usart_console_write(struct console *co, const char *s, writel_relaxed(old_cr1, port->membase + ofs->cr1); if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static int stm32_usart_console_setup(struct console *co, char *options) @@ -1436,23 +1549,20 @@ static void __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port, { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - const struct stm32_usart_config *cfg = &stm32_port->info->cfg; - u32 val; - if (stm32_port->wakeirq <= 0) + if (!stm32_port->wakeup_src) return; + /* + * Enable low-power wake-up and wake-up irq if argument is set to + * "enable", disable low-power wake-up and wake-up irq otherwise + */ if (enable) { - stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); stm32_usart_set_bits(port, ofs->cr1, USART_CR1_UESM); - val = readl_relaxed(port->membase + ofs->cr3); - val &= ~USART_CR3_WUS_MASK; - /* Enable Wake up interrupt from low power on start bit */ - val |= USART_CR3_WUS_START_BIT | USART_CR3_WUFIE; - writel_relaxed(val, port->membase + ofs->cr3); - stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_WUFIE); } else { stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_UESM); + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); } } @@ -1462,10 +1572,8 @@ static int __maybe_unused stm32_usart_serial_suspend(struct device *dev) uart_suspend_port(&stm32_usart_driver, port); - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev) || device_wakeup_path(dev)) stm32_usart_serial_en_wakeup(port, true); - else - stm32_usart_serial_en_wakeup(port, false); /* * When "no_console_suspend" is enabled, keep the pinctrl default state @@ -1474,7 +1582,7 @@ static int __maybe_unused stm32_usart_serial_suspend(struct device *dev) * capabilities. */ if (console_suspend_enabled || !uart_console(port)) { - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev) || device_wakeup_path(dev)) pinctrl_pm_select_idle_state(dev); else pinctrl_pm_select_sleep_state(dev); @@ -1489,7 +1597,7 @@ static int __maybe_unused stm32_usart_serial_resume(struct device *dev) pinctrl_pm_select_default_state(dev); - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev) || device_wakeup_path(dev)) stm32_usart_serial_en_wakeup(port, false); return uart_resume_port(&stm32_usart_driver, port); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index cb4f327c46db..07ac291328cd 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -25,6 +25,7 @@ struct stm32_usart_offsets { struct stm32_usart_config { u8 uart_enable_bit; /* USART_CR1_UE */ bool has_7bits_data; + bool has_swap; bool has_wakeup; bool has_fifo; int fifosize; @@ -76,6 +77,7 @@ struct stm32_usart_info stm32f7_info = { .cfg = { .uart_enable_bit = 0, .has_7bits_data = true, + .has_swap = true, .fifosize = 1, } }; @@ -97,6 +99,7 @@ struct stm32_usart_info stm32h7_info = { .cfg = { .uart_enable_bit = 0, .has_7bits_data = true, + .has_swap = true, .has_wakeup = true, .has_fifo = true, .fifosize = 16, @@ -127,9 +130,6 @@ struct stm32_usart_info stm32h7_info = { /* Dummy bits */ #define USART_SR_DUMMY_RX BIT(16) -/* USART_ICR (F7) */ -#define USART_CR_TC BIT(6) - /* USART_DR */ #define USART_DR_MASK GENMASK(8, 0) @@ -216,12 +216,6 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR3_TXFTCFG_MASK GENMASK(31, 29) /* H7 */ #define USART_CR3_TXFTCFG_SHIFT 29 /* H7 */ -/* TX FIFO threashold set to half of its depth */ -#define USART_CR3_TXFTCFG_HALF 0x2 - -/* RX FIFO threashold set to half of its depth */ -#define USART_CR3_RXFTCFG_HALF 0x2 - /* USART_GTPR */ #define USART_GTPR_PSC_MASK GENMASK(7, 0) #define USART_GTPR_GT_MASK GENMASK(15, 8) @@ -271,8 +265,11 @@ struct stm32_port { int last_res; bool tx_dma_busy; /* dma tx busy */ bool hw_flow_control; + bool swap; /* swap RX & TX pins */ bool fifoen; - int wakeirq; + int rxftcfg; /* RX FIFO threshold CFG */ + int txftcfg; /* TX FIFO threshold CFG */ + bool wakeup_src; int rdr_mask; /* receive data register mask */ struct mctrl_gpios *gpios; /* modem control gpios */ }; diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 319e5ceb6130..12c2468f2b0e 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -466,12 +466,8 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) if (status & UART_LSR_THRE) transmit_chars(up); - spin_unlock_irqrestore(&up->port.lock, flags); - tty_flip_buffer_push(&up->port.state->port); - spin_lock_irqsave(&up->port.lock, flags); - } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)); spin_unlock_irqrestore(&up->port.lock, flags); diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c index aaf8748a6147..52687c65ad74 100644 --- a/drivers/tty/serial/tegra-tcu.c +++ b/drivers/tty/serial/tegra-tcu.c @@ -282,6 +282,7 @@ static const struct of_device_id tegra_tcu_match[] = { { .compatible = "nvidia,tegra194-tcu" }, { } }; +MODULE_DEVICE_TABLE(of, tegra_tcu_match); static struct platform_driver tegra_tcu_driver = { .driver = { diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 2126e6e6dfd1..08941eabe7b1 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -87,9 +87,7 @@ static void timbuart_rx_chars(struct uart_port *port) tty_insert_flip_char(tport, ch, TTY_NORMAL); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); dev_dbg(port->dev, "%s - total read %d bytes\n", __func__, port->icount.rx); diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 764e992438b2..c5edd56ff830 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -184,9 +184,7 @@ static void handle_rx(struct uart_port *port) tty_insert_flip_char(tport, c, flag); } - spin_unlock(&port->lock); tty_flip_buffer_push(tport); - spin_lock(&port->lock); } static void handle_tx(struct uart_port *port) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index a14c5d996473..67a2db621e2b 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -301,9 +301,8 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) tty_insert_flip_char(&port->state->port, data, status); isrstatus = 0; } - spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); - spin_lock(&port->lock); } /** |