summaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/8250
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-06-03 11:08:40 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-06-03 11:08:40 -0700
commit932c2989b59008e530ffcc7c7e6ef507a28b28ca (patch)
tree5d51787f2ace2b9a2fd2393662818fd14aa219be /drivers/tty/serial/8250
parent4ad680f083ec360e0991c453e18a38ed9ae500d7 (diff)
parent25e02ba60f0fbe65ba07553b5b2b8867726273c4 (diff)
downloadlinux-932c2989b59008e530ffcc7c7e6ef507a28b28ca.tar.bz2
Merge tag 'tty-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty and serial driver updates from Greg KH: "Here is the big set of tty and serial driver updates for 5.19-rc1. Lots of tiny cleanups in here, the major stuff is: - termbit cleanups and unification by Ilpo. A much needed change that goes a long way to making things simpler for all of the different arches - tty documentation cleanups and movements to their own place in the documentation tree - old tty driver cleanups and fixes from Jiri to bring some existing drivers into the modern world - RS485 cleanups and unifications to make it easier for individual drivers to support this mode instead of having to duplicate logic in each driver - Lots of 8250 driver updates and additions - new device id additions - n_gsm continued fixes and cleanups - other minor serial driver updates and cleanups All of these have been in linux-next for weeks with no reported issues" * tag 'tty-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (166 commits) tty: Rework receive flow control char logic pcmcia: synclink_cs: Don't allow CS5-6 serial: stm32-usart: Correct CSIZE, bits, and parity serial: st-asc: Sanitize CSIZE and correct PARENB for CS7 serial: sifive: Sanitize CSIZE and c_iflag serial: sh-sci: Don't allow CS5-6 serial: txx9: Don't allow CS5-6 serial: rda-uart: Don't allow CS5-6 serial: digicolor-usart: Don't allow CS5-6 serial: uartlite: Fix BRKINT clearing serial: cpm_uart: Fix build error without CONFIG_SERIAL_CPM_CONSOLE serial: core: Do stop_rx in suspend path for console if console_suspend is disabled tty: serial: qcom-geni-serial: Remove uart frequency table. Instead, find suitable frequency with call to clk_round_rate. dt-bindings: serial: renesas,em-uart: Add RZ/V2M clock to access the registers serial: 8250_fintek: Check SER_RS485_RTS_* only with RS485 Revert "serial: 8250_mtk: Make sure to select the right FEATURE_SEL" serial: msm_serial: disable interrupts in __msm_console_write() serial: meson: acquire port->lock in startup() serial: 8250_dw: Use dev_err_probe() serial: 8250_dw: Use devm_add_action_or_reset() ...
Diffstat (limited to 'drivers/tty/serial/8250')
-rw-r--r--drivers/tty/serial/8250/8250.h41
-rw-r--r--drivers/tty/serial/8250/8250_aspeed_vuart.c2
-rw-r--r--drivers/tty/serial/8250/8250_core.c1
-rw-r--r--drivers/tty/serial/8250/8250_dma.c7
-rw-r--r--drivers/tty/serial/8250/8250_dw.c229
-rw-r--r--drivers/tty/serial/8250/8250_dwlib.c116
-rw-r--r--drivers/tty/serial/8250/8250_dwlib.h51
-rw-r--r--drivers/tty/serial/8250/8250_fintek.c8
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c7
-rw-r--r--drivers/tty/serial/8250/8250_of.c2
-rw-r--r--drivers/tty/serial/8250/8250_pci.c480
-rw-r--r--drivers/tty/serial/8250/8250_port.c140
-rw-r--r--drivers/tty/serial/8250/8250_pxa.c1
-rw-r--r--drivers/tty/serial/8250/Kconfig2
14 files changed, 784 insertions, 303 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index a8830e15a22c..696030cfcb09 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -17,6 +17,8 @@
struct uart_8250_dma {
int (*tx_dma)(struct uart_8250_port *p);
int (*rx_dma)(struct uart_8250_port *p);
+ void (*prepare_tx_dma)(struct uart_8250_port *p);
+ void (*prepare_rx_dma)(struct uart_8250_port *p);
/* Filter function */
dma_filter_fn fn;
@@ -83,6 +85,7 @@ struct serial8250_config {
#define UART_CAP_MINI BIT(17) /* Mini UART on BCM283X family lacks:
* STOP PARITY EPAR SPAR WLEN5 WLEN6
*/
+#define UART_CAP_NOTEMT BIT(18) /* UART without interrupt on TEMT available */
#define UART_BUG_QUOT BIT(0) /* UART has buggy quot LSB */
#define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */
@@ -120,6 +123,28 @@ static inline void serial_out(struct uart_8250_port *up, int offset, int value)
up->port.serial_out(&up->port, offset, value);
}
+/*
+ * For the 16C950
+ */
+static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
+{
+ serial_out(up, UART_SCR, offset);
+ serial_out(up, UART_ICR, value);
+}
+
+static unsigned int __maybe_unused serial_icr_read(struct uart_8250_port *up,
+ int offset)
+{
+ unsigned int value;
+
+ serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
+ serial_out(up, UART_SCR, offset);
+ value = serial_in(up, UART_ICR);
+ serial_icr_write(up, UART_ACR, up->acr);
+
+ return value;
+}
+
void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p);
static inline int serial_dl_read(struct uart_8250_port *up)
@@ -302,6 +327,22 @@ extern int serial8250_rx_dma(struct uart_8250_port *);
extern void serial8250_rx_dma_flush(struct uart_8250_port *);
extern int serial8250_request_dma(struct uart_8250_port *);
extern void serial8250_release_dma(struct uart_8250_port *);
+
+static inline void serial8250_do_prepare_tx_dma(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+
+ if (dma->prepare_tx_dma)
+ dma->prepare_tx_dma(p);
+}
+
+static inline void serial8250_do_prepare_rx_dma(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+
+ if (dma->prepare_rx_dma)
+ dma->prepare_rx_dma(p);
+}
#else
static inline int serial8250_tx_dma(struct uart_8250_port *p)
{
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c
index 93fe10c680fb..9d2a7856784f 100644
--- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
+++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
@@ -429,6 +429,8 @@ static int aspeed_vuart_probe(struct platform_device *pdev)
timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
memset(&port, 0, sizeof(port));
port.port.private_data = vuart;
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 01d30f6ed8fb..cfbd2de0ca6e 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -32,7 +32,6 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
-#include <linux/pm_runtime.h>
#include <linux/io.h>
#ifdef CONFIG_SPARC
#include <linux/sunserialcore.h>
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index b3c3f7e5851a..7133fceed35e 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -34,7 +34,7 @@ static void __dma_tx_complete(void *param)
uart_write_wakeup(&p->port);
ret = serial8250_tx_dma(p);
- if (ret)
+ if (ret || !dma->tx_running)
serial8250_set_THRI(p);
spin_unlock_irqrestore(&p->port.lock, flags);
@@ -80,12 +80,13 @@ int serial8250_tx_dma(struct uart_8250_port *p)
if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
/* We have been called from __dma_tx_complete() */
- serial8250_rpm_put_tx(p);
return 0;
}
dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ serial8250_do_prepare_tx_dma(p);
+
desc = dmaengine_prep_slave_single(dma->txchan,
dma->tx_addr + xmit->tail,
dma->tx_size, DMA_MEM_TO_DEV,
@@ -123,6 +124,8 @@ int serial8250_rx_dma(struct uart_8250_port *p)
if (dma->rx_running)
return 0;
+ serial8250_do_prepare_rx_dma(p);
+
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
dma->rx_size, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 1769808031c5..f57bbd32ef11 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -12,13 +12,13 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>
#include <linux/of.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/workqueue.h>
#include <linux/notifier.h>
#include <linux/slab.h>
@@ -33,30 +33,28 @@
/* Offsets for the DesignWare specific registers */
#define DW_UART_USR 0x1f /* UART Status Register */
+#define DW_UART_DMASA 0xa8 /* DMA Software Ack */
+
+#define OCTEON_UART_USR 0x27 /* UART Status Register */
+
+#define RZN1_UART_TDMACR 0x10c /* DMA Control Register Transmit Mode */
+#define RZN1_UART_RDMACR 0x110 /* DMA Control Register Receive Mode */
/* DesignWare specific register fields */
#define DW_UART_MCR_SIRE BIT(6)
-struct dw8250_data {
- struct dw8250_port_data data;
+/* Renesas specific register fields */
+#define RZN1_UART_xDMACR_DMA_EN BIT(0)
+#define RZN1_UART_xDMACR_1_WORD_BURST (0 << 1)
+#define RZN1_UART_xDMACR_4_WORD_BURST (1 << 1)
+#define RZN1_UART_xDMACR_8_WORD_BURST (3 << 1)
+#define RZN1_UART_xDMACR_BLK_SZ(x) ((x) << 3)
- u8 usr_reg;
- int msr_mask_on;
- int msr_mask_off;
- struct clk *clk;
- struct clk *pclk;
- struct notifier_block clk_notifier;
- struct work_struct clk_work;
- struct reset_control *rst;
-
- unsigned int skip_autocfg:1;
- unsigned int uart_16550_compatible:1;
-};
-
-static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data)
-{
- return container_of(data, struct dw8250_data, data);
-}
+/* Quirks */
+#define DW_UART_QUIRK_OCTEON BIT(0)
+#define DW_UART_QUIRK_ARMADA_38X BIT(1)
+#define DW_UART_QUIRK_SKIP_SET_RATE BIT(2)
+#define DW_UART_QUIRK_IS_DMA_FC BIT(3)
static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb)
{
@@ -238,6 +236,8 @@ static int dw8250_handle_irq(struct uart_port *p)
struct uart_8250_port *up = up_to_u8250p(p);
struct dw8250_data *d = to_dw8250_data(p->private_data);
unsigned int iir = p->serial_in(p, UART_IIR);
+ bool rx_timeout = (iir & 0x3f) == UART_IIR_RX_TIMEOUT;
+ unsigned int quirks = d->pdata->quirks;
unsigned int status;
unsigned long flags;
@@ -251,7 +251,7 @@ static int dw8250_handle_irq(struct uart_port *p)
* This problem has only been observed so far when not in DMA mode
* so we limit the workaround only to non-DMA mode.
*/
- if (!up->dma && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT)) {
+ if (!up->dma && rx_timeout) {
spin_lock_irqsave(&p->lock, flags);
status = p->serial_in(p, UART_LSR);
@@ -261,12 +261,21 @@ static int dw8250_handle_irq(struct uart_port *p)
spin_unlock_irqrestore(&p->lock, flags);
}
+ /* Manually stop the Rx DMA transfer when acting as flow controller */
+ if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) {
+ status = p->serial_in(p, UART_LSR);
+ if (status & (UART_LSR_DR | UART_LSR_BI)) {
+ dw8250_writel_ext(p, RZN1_UART_RDMACR, 0);
+ dw8250_writel_ext(p, DW_UART_DMASA, 1);
+ }
+ }
+
if (serial8250_handle_irq(p, iir))
return 1;
if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
/* Clear the USR */
- (void)p->serial_in(p, d->usr_reg);
+ (void)p->serial_in(p, d->pdata->usr_reg);
return 1;
}
@@ -384,11 +393,48 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
return param == chan->device->dev;
}
+static u32 dw8250_rzn1_get_dmacr_burst(int max_burst)
+{
+ if (max_burst >= 8)
+ return RZN1_UART_xDMACR_8_WORD_BURST;
+ else if (max_burst >= 4)
+ return RZN1_UART_xDMACR_4_WORD_BURST;
+ else
+ return RZN1_UART_xDMACR_1_WORD_BURST;
+}
+
+static void dw8250_prepare_tx_dma(struct uart_8250_port *p)
+{
+ struct uart_port *up = &p->port;
+ struct uart_8250_dma *dma = p->dma;
+ u32 val;
+
+ dw8250_writel_ext(up, RZN1_UART_TDMACR, 0);
+ val = dw8250_rzn1_get_dmacr_burst(dma->txconf.dst_maxburst) |
+ RZN1_UART_xDMACR_BLK_SZ(dma->tx_size) |
+ RZN1_UART_xDMACR_DMA_EN;
+ dw8250_writel_ext(up, RZN1_UART_TDMACR, val);
+}
+
+static void dw8250_prepare_rx_dma(struct uart_8250_port *p)
+{
+ struct uart_port *up = &p->port;
+ struct uart_8250_dma *dma = p->dma;
+ u32 val;
+
+ dw8250_writel_ext(up, RZN1_UART_RDMACR, 0);
+ val = dw8250_rzn1_get_dmacr_burst(dma->rxconf.src_maxburst) |
+ RZN1_UART_xDMACR_BLK_SZ(dma->rx_size) |
+ RZN1_UART_xDMACR_DMA_EN;
+ dw8250_writel_ext(up, RZN1_UART_RDMACR, val);
+}
+
static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
{
struct device_node *np = p->dev->of_node;
if (np) {
+ unsigned int quirks = data->pdata->quirks;
int id;
/* get index of serial line, if found in DT aliases */
@@ -396,12 +442,11 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
if (id >= 0)
p->line = id;
#ifdef CONFIG_64BIT
- if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
+ if (quirks & DW_UART_QUIRK_OCTEON) {
p->serial_in = dw8250_serial_inq;
p->serial_out = dw8250_serial_outq;
p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
p->type = PORT_OCTEON;
- data->usr_reg = 0x27;
data->skip_autocfg = true;
}
#endif
@@ -412,10 +457,16 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
p->serial_out = dw8250_serial_out32be;
}
- if (of_device_is_compatible(np, "marvell,armada-38x-uart"))
+ if (quirks & DW_UART_QUIRK_ARMADA_38X)
p->serial_out = dw8250_serial_out38x;
- if (of_device_is_compatible(np, "starfive,jh7100-uart"))
+ if (quirks & DW_UART_QUIRK_SKIP_SET_RATE)
p->set_termios = dw8250_do_set_termios;
+ if (quirks & DW_UART_QUIRK_IS_DMA_FC) {
+ data->data.dma.txconf.device_fc = 1;
+ data->data.dma.rxconf.device_fc = 1;
+ data->data.dma.prepare_tx_dma = dw8250_prepare_tx_dma;
+ data->data.dma.prepare_rx_dma = dw8250_prepare_rx_dma;
+ }
} else if (acpi_dev_present("APMC0D08", NULL, -1)) {
p->iotype = UPIO_MEM32;
@@ -433,21 +484,30 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
}
}
+static void dw8250_clk_disable_unprepare(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static void dw8250_reset_control_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
static int dw8250_probe(struct platform_device *pdev)
{
struct uart_8250_port uart = {}, *up = &uart;
- struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct uart_port *p = &up->port;
struct device *dev = &pdev->dev;
struct dw8250_data *data;
+ struct resource *regs;
int irq;
int err;
u32 val;
- if (!regs) {
- dev_err(dev, "no registers defined\n");
- return -EINVAL;
- }
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return dev_err_probe(dev, -EINVAL, "no registers defined\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -476,7 +536,7 @@ static int dw8250_probe(struct platform_device *pdev)
return -ENOMEM;
data->data.dma.fn = dw8250_fallback_dma_filter;
- data->usr_reg = DW_UART_USR;
+ data->pdata = device_get_match_data(p->dev);
p->private_data = &data->data;
data->uart_16550_compatible = device_property_read_bool(dev,
@@ -532,37 +592,41 @@ static int dw8250_probe(struct platform_device *pdev)
err = clk_prepare_enable(data->clk);
if (err)
- dev_warn(dev, "could not enable optional baudclk: %d\n", err);
+ return dev_err_probe(dev, err, "could not enable optional baudclk\n");
+
+ err = devm_add_action_or_reset(dev, dw8250_clk_disable_unprepare, data->clk);
+ if (err)
+ return err;
if (data->clk)
p->uartclk = clk_get_rate(data->clk);
/* If no clock rate is defined, fail. */
- if (!p->uartclk) {
- dev_err(dev, "clock rate not defined\n");
- err = -EINVAL;
- goto err_clk;
- }
+ if (!p->uartclk)
+ return dev_err_probe(dev, -EINVAL, "clock rate not defined\n");
data->pclk = devm_clk_get_optional(dev, "apb_pclk");
- if (IS_ERR(data->pclk)) {
- err = PTR_ERR(data->pclk);
- goto err_clk;
- }
+ if (IS_ERR(data->pclk))
+ return PTR_ERR(data->pclk);
err = clk_prepare_enable(data->pclk);
- if (err) {
- dev_err(dev, "could not enable apb_pclk\n");
- goto err_clk;
- }
+ if (err)
+ return dev_err_probe(dev, err, "could not enable apb_pclk\n");
+
+ err = devm_add_action_or_reset(dev, dw8250_clk_disable_unprepare, data->pclk);
+ if (err)
+ return err;
data->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
- if (IS_ERR(data->rst)) {
- err = PTR_ERR(data->rst);
- goto err_pclk;
- }
+ if (IS_ERR(data->rst))
+ return PTR_ERR(data->rst);
+
reset_control_deassert(data->rst);
+ err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst);
+ if (err)
+ return err;
+
dw8250_quirks(p, data);
/* If the Busy Functionality is not implemented, don't handle it */
@@ -580,10 +644,8 @@ static int dw8250_probe(struct platform_device *pdev)
}
data->data.line = serial8250_register_8250_port(up);
- if (data->data.line < 0) {
- err = data->data.line;
- goto err_reset;
- }
+ if (data->data.line < 0)
+ return data->data.line;
/*
* Some platforms may provide a reference clock shared between several
@@ -593,9 +655,8 @@ static int dw8250_probe(struct platform_device *pdev)
if (data->clk) {
err = clk_notifier_register(data->clk, &data->clk_notifier);
if (err)
- dev_warn(p->dev, "Failed to set the clock notifier\n");
- else
- queue_work(system_unbound_wq, &data->clk_work);
+ return dev_err_probe(dev, err, "Failed to set the clock notifier\n");
+ queue_work(system_unbound_wq, &data->clk_work);
}
platform_set_drvdata(pdev, data);
@@ -604,17 +665,6 @@ static int dw8250_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
return 0;
-
-err_reset:
- reset_control_assert(data->rst);
-
-err_pclk:
- clk_disable_unprepare(data->pclk);
-
-err_clk:
- clk_disable_unprepare(data->clk);
-
- return err;
}
static int dw8250_remove(struct platform_device *pdev)
@@ -632,12 +682,6 @@ static int dw8250_remove(struct platform_device *pdev)
serial8250_unregister_port(data->data.line);
- reset_control_assert(data->rst);
-
- clk_disable_unprepare(data->pclk);
-
- clk_disable_unprepare(data->clk);
-
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
@@ -693,12 +737,37 @@ static const struct dev_pm_ops dw8250_pm_ops = {
SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL)
};
+static const struct dw8250_platform_data dw8250_dw_apb = {
+ .usr_reg = DW_UART_USR,
+};
+
+static const struct dw8250_platform_data dw8250_octeon_3860_data = {
+ .usr_reg = OCTEON_UART_USR,
+ .quirks = DW_UART_QUIRK_OCTEON,
+};
+
+static const struct dw8250_platform_data dw8250_armada_38x_data = {
+ .usr_reg = DW_UART_USR,
+ .quirks = DW_UART_QUIRK_ARMADA_38X,
+};
+
+static const struct dw8250_platform_data dw8250_renesas_rzn1_data = {
+ .usr_reg = DW_UART_USR,
+ .cpr_val = 0x00012f32,
+ .quirks = DW_UART_QUIRK_IS_DMA_FC,
+};
+
+static const struct dw8250_platform_data dw8250_starfive_jh7100_data = {
+ .usr_reg = DW_UART_USR,
+ .quirks = DW_UART_QUIRK_SKIP_SET_RATE,
+};
+
static const struct of_device_id dw8250_of_match[] = {
- { .compatible = "snps,dw-apb-uart" },
- { .compatible = "cavium,octeon-3860-uart" },
- { .compatible = "marvell,armada-38x-uart" },
- { .compatible = "renesas,rzn1-uart" },
- { .compatible = "starfive,jh7100-uart" },
+ { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
+ { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
+ { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
+ { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data },
+ { .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw8250_of_match);
diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c
index 622d3b0d89e7..fbabfdd8c7b8 100644
--- a/drivers/tty/serial/8250/8250_dwlib.c
+++ b/drivers/tty/serial/8250/8250_dwlib.c
@@ -2,19 +2,32 @@
/* Synopsys DesignWare 8250 library. */
#include <linux/bitops.h>
+#include <linux/bitfield.h>
#include <linux/device.h>
-#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/property.h>
#include <linux/serial_8250.h>
#include <linux/serial_core.h>
#include "8250_dwlib.h"
/* Offsets for the DesignWare specific registers */
+#define DW_UART_TCR 0xac /* Transceiver Control Register (RS485) */
+#define DW_UART_DE_EN 0xb0 /* Driver Output Enable Register */
+#define DW_UART_RE_EN 0xb4 /* Receiver Output Enable Register */
#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */
#define DW_UART_CPR 0xf4 /* Component Parameter Register */
#define DW_UART_UCV 0xf8 /* UART Component Version */
+/* Transceiver Control Register bits */
+#define DW_UART_TCR_RS485_EN BIT(0)
+#define DW_UART_TCR_RE_POL BIT(1)
+#define DW_UART_TCR_DE_POL BIT(2)
+#define DW_UART_TCR_XFER_MODE GENMASK(4, 3)
+#define DW_UART_TCR_XFER_MODE_DE_DURING_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 0)
+#define DW_UART_TCR_XFER_MODE_SW_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 1)
+#define DW_UART_TCR_XFER_MODE_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 2)
+
/* Component Parameter Register bits */
#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
#define DW_UART_CPR_AFCE_MODE (1 << 4)
@@ -32,21 +45,6 @@
/* Helper for FIFO size calculation */
#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16)
-static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
-{
- if (p->iotype == UPIO_MEM32BE)
- return ioread32be(p->membase + offset);
- return readl(p->membase + offset);
-}
-
-static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
-{
- if (p->iotype == UPIO_MEM32BE)
- iowrite32be(reg, p->membase + offset);
- else
- writel(reg, p->membase + offset);
-}
-
/*
* divisor = div(I) + div(F)
* "I" means integer, "F" means fractional
@@ -87,11 +85,87 @@ void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct
}
EXPORT_SYMBOL_GPL(dw8250_do_set_termios);
+static int dw8250_rs485_config(struct uart_port *p, struct serial_rs485 *rs485)
+{
+ u32 tcr;
+
+ tcr = dw8250_readl_ext(p, DW_UART_TCR);
+ tcr &= ~DW_UART_TCR_XFER_MODE;
+
+ if (rs485->flags & SER_RS485_ENABLED) {
+ /* Clear unsupported flags. */
+ rs485->flags &= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX |
+ SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND;
+ tcr |= DW_UART_TCR_RS485_EN;
+
+ if (rs485->flags & SER_RS485_RX_DURING_TX) {
+ tcr |= DW_UART_TCR_XFER_MODE_DE_DURING_RE;
+ } else {
+ /* HW does not support same DE level for tx and rx */
+ if (!(rs485->flags & SER_RS485_RTS_ON_SEND) ==
+ !(rs485->flags & SER_RS485_RTS_AFTER_SEND))
+ return -EINVAL;
+
+ tcr |= DW_UART_TCR_XFER_MODE_DE_OR_RE;
+ }
+ dw8250_writel_ext(p, DW_UART_DE_EN, 1);
+ dw8250_writel_ext(p, DW_UART_RE_EN, 1);
+ } else {
+ rs485->flags = 0;
+
+ tcr &= ~DW_UART_TCR_RS485_EN;
+ }
+
+ /* Reset to default polarity */
+ tcr |= DW_UART_TCR_DE_POL;
+ tcr &= ~DW_UART_TCR_RE_POL;
+
+ if (!(rs485->flags & SER_RS485_RTS_ON_SEND))
+ tcr &= ~DW_UART_TCR_DE_POL;
+ if (device_property_read_bool(p->dev, "rs485-rx-active-high"))
+ tcr |= DW_UART_TCR_RE_POL;
+
+ dw8250_writel_ext(p, DW_UART_TCR, tcr);
+
+ rs485->delay_rts_before_send = 0;
+ rs485->delay_rts_after_send = 0;
+
+ p->rs485 = *rs485;
+
+ return 0;
+}
+
+/*
+ * Tests if RE_EN register can have non-zero value to see if RS-485 HW support
+ * is present.
+ */
+static bool dw8250_detect_rs485_hw(struct uart_port *p)
+{
+ u32 reg;
+
+ dw8250_writel_ext(p, DW_UART_RE_EN, 1);
+ reg = dw8250_readl_ext(p, DW_UART_RE_EN);
+ dw8250_writel_ext(p, DW_UART_RE_EN, 0);
+ return reg;
+}
+
void dw8250_setup_port(struct uart_port *p)
{
+ struct dw8250_port_data *pd = p->private_data;
+ struct dw8250_data *data = to_dw8250_data(pd);
struct uart_8250_port *up = up_to_u8250p(p);
u32 reg;
+ pd->hw_rs485_support = dw8250_detect_rs485_hw(p);
+ if (pd->hw_rs485_support) {
+ p->rs485_config = dw8250_rs485_config;
+ } else {
+ p->rs485_config = serial8250_em485_config;
+ up->rs485_start_tx = serial8250_em485_start_tx;
+ up->rs485_stop_tx = serial8250_em485_stop_tx;
+ }
+ up->capabilities |= UART_CAP_NOTEMT;
+
/*
* If the Component Version Register returns zero, we know that
* ADDITIONAL_FEATURES are not enabled. No need to go any further.
@@ -108,14 +182,16 @@ void dw8250_setup_port(struct uart_port *p)
dw8250_writel_ext(p, DW_UART_DLF, 0);
if (reg) {
- struct dw8250_port_data *d = p->private_data;
-
- d->dlf_size = fls(reg);
+ pd->dlf_size = fls(reg);
p->get_divisor = dw8250_get_divisor;
p->set_divisor = dw8250_set_divisor;
}
reg = dw8250_readl_ext(p, DW_UART_CPR);
+ if (!reg) {
+ reg = data->pdata->cpr_val;
+ dev_dbg(p->dev, "CPR is not available, using 0x%08x instead\n", reg);
+ }
if (!reg)
return;
@@ -124,7 +200,7 @@ void dw8250_setup_port(struct uart_port *p)
p->type = PORT_16550A;
p->flags |= UPF_FIXED_TYPE;
p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
- up->capabilities = UART_CAP_FIFO;
+ up->capabilities = UART_CAP_FIFO | UART_CAP_NOTEMT;
}
if (reg & DW_UART_CPR_AFCE_MODE)
diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h
index 83d528e5cc21..055bfdc87985 100644
--- a/drivers/tty/serial/8250/8250_dwlib.h
+++ b/drivers/tty/serial/8250/8250_dwlib.h
@@ -1,10 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Synopsys DesignWare 8250 library header file. */
+#include <linux/io.h>
+#include <linux/notifier.h>
#include <linux/types.h>
+#include <linux/workqueue.h>
#include "8250.h"
+struct clk;
+struct reset_control;
+
struct dw8250_port_data {
/* Port properties */
int line;
@@ -14,7 +20,52 @@ struct dw8250_port_data {
/* Hardware configuration */
u8 dlf_size;
+
+ /* RS485 variables */
+ bool hw_rs485_support;
+};
+
+struct dw8250_platform_data {
+ u8 usr_reg;
+ u32 cpr_val;
+ unsigned int quirks;
+};
+
+struct dw8250_data {
+ struct dw8250_port_data data;
+ const struct dw8250_platform_data *pdata;
+
+ int msr_mask_on;
+ int msr_mask_off;
+ struct clk *clk;
+ struct clk *pclk;
+ struct notifier_block clk_notifier;
+ struct work_struct clk_work;
+ struct reset_control *rst;
+
+ unsigned int skip_autocfg:1;
+ unsigned int uart_16550_compatible:1;
};
void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, struct ktermios *old);
void dw8250_setup_port(struct uart_port *p);
+
+static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data)
+{
+ return container_of(data, struct dw8250_data, data);
+}
+
+static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
+{
+ if (p->iotype == UPIO_MEM32BE)
+ return ioread32be(p->membase + offset);
+ return readl(p->membase + offset);
+}
+
+static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
+{
+ if (p->iotype == UPIO_MEM32BE)
+ iowrite32be(reg, p->membase + offset);
+ else
+ writel(reg, p->membase + offset);
+}
diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c
index 251f0018ae8c..dba5950b8d0e 100644
--- a/drivers/tty/serial/8250/8250_fintek.c
+++ b/drivers/tty/serial/8250/8250_fintek.c
@@ -200,12 +200,12 @@ static int fintek_8250_rs485_config(struct uart_port *port,
if (!pdata)
return -EINVAL;
- /* Hardware do not support same RTS level on send and receive */
- if (!(rs485->flags & SER_RS485_RTS_ON_SEND) ==
- !(rs485->flags & SER_RS485_RTS_AFTER_SEND))
- return -EINVAL;
if (rs485->flags & SER_RS485_ENABLED) {
+ /* Hardware do not support same RTS level on send and receive */
+ if (!(rs485->flags & SER_RS485_RTS_ON_SEND) ==
+ !(rs485->flags & SER_RS485_RTS_AFTER_SEND))
+ return -EINVAL;
memset(rs485->padding, 0, sizeof(rs485->padding));
config |= RS485_URA;
} else {
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index 21053db93ff1..54051ec7b499 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -54,9 +54,6 @@
#define MTK_UART_TX_TRIGGER 1
#define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE
-#define MTK_UART_FEATURE_SEL 39 /* Feature Selection register */
-#define MTK_UART_FEAT_NEWRMAP BIT(0) /* Use new register map */
-
#define MTK_UART_XON1 40 /* I/O: Xon character 1 */
#define MTK_UART_XOFF1 42 /* I/O: Xoff character 1 */
@@ -575,10 +572,6 @@ static int mtk8250_probe(struct platform_device *pdev)
uart.dma = data->dma;
#endif
- /* Set AP UART new register map */
- writel(MTK_UART_FEAT_NEWRMAP, uart.port.membase +
- (MTK_UART_FEATURE_SEL << uart.port.regshift));
-
/* Disable Rate Fix function */
writel(0x0, uart.port.membase +
(MTK_UART_RATE_FIX << uart.port.regshift));
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index be8626234627..5a699a1aa79c 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -326,6 +326,8 @@ static const struct of_device_id of_platform_serial_table[] = {
.data = (void *)PORT_ALTR_16550_F64, },
{ .compatible = "altr,16550-FIFO128",
.data = (void *)PORT_ALTR_16550_F128, },
+ { .compatible = "fsl,16550-FIFO64",
+ .data = (void *)PORT_16550A_FSL64, },
{ .compatible = "mediatek,mtk-btif",
.data = (void *)PORT_MTK_BTIF, },
{ .compatible = "mrvl,mmp-uart",
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index a293e9f107d0..a17619db7939 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -11,6 +11,7 @@
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/kernel.h>
+#include <linux/math.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/tty.h>
@@ -994,41 +995,29 @@ static void pci_ite887x_exit(struct pci_dev *dev)
}
/*
- * EndRun Technologies.
- * Determine the number of ports available on the device.
+ * Oxford Semiconductor Inc.
+ * Check if an OxSemi device is part of the Tornado range of devices.
*/
#define PCI_VENDOR_ID_ENDRUN 0x7401
#define PCI_DEVICE_ID_ENDRUN_1588 0xe100
-static int pci_endrun_init(struct pci_dev *dev)
+static bool pci_oxsemi_tornado_p(struct pci_dev *dev)
{
- u8 __iomem *p;
- unsigned long deviceID;
- unsigned int number_uarts = 0;
+ /* OxSemi Tornado devices are all 0xCxxx */
+ if (dev->vendor == PCI_VENDOR_ID_OXSEMI &&
+ (dev->device & 0xf000) != 0xc000)
+ return false;
- /* EndRun device is all 0xexxx */
+ /* EndRun devices are all 0xExxx */
if (dev->vendor == PCI_VENDOR_ID_ENDRUN &&
- (dev->device & 0xf000) != 0xe000)
- return 0;
-
- p = pci_iomap(dev, 0, 5);
- if (p == NULL)
- return -ENOMEM;
+ (dev->device & 0xf000) != 0xe000)
+ return false;
- deviceID = ioread32(p);
- /* EndRun device */
- if (deviceID == 0x07000200) {
- number_uarts = ioread8(p + 4);
- pci_dbg(dev, "%d ports detected on EndRun PCI Express device\n", number_uarts);
- }
- pci_iounmap(dev, p);
- return number_uarts;
+ return true;
}
/*
- * Oxford Semiconductor Inc.
- * Check that device is part of the Tornado range of devices, then determine
- * the number of ports available on the device.
+ * Determine the number of ports available on a Tornado device.
*/
static int pci_oxsemi_tornado_init(struct pci_dev *dev)
{
@@ -1036,9 +1025,7 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
unsigned long deviceID;
unsigned int number_uarts = 0;
- /* OxSemi Tornado devices are all 0xCxxx */
- if (dev->vendor == PCI_VENDOR_ID_OXSEMI &&
- (dev->device & 0xF000) != 0xC000)
+ if (!pci_oxsemi_tornado_p(dev))
return 0;
p = pci_iomap(dev, 0, 5);
@@ -1049,12 +1036,217 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
/* Tornado device */
if (deviceID == 0x07000200) {
number_uarts = ioread8(p + 4);
- pci_dbg(dev, "%d ports detected on Oxford PCI Express device\n", number_uarts);
+ pci_dbg(dev, "%d ports detected on %s PCI Express device\n",
+ number_uarts,
+ dev->vendor == PCI_VENDOR_ID_ENDRUN ?
+ "EndRun" : "Oxford");
}
pci_iounmap(dev, p);
return number_uarts;
}
+/* Tornado-specific constants for the TCR and CPR registers; see below. */
+#define OXSEMI_TORNADO_TCR_MASK 0xf
+#define OXSEMI_TORNADO_CPR_MASK 0x1ff
+#define OXSEMI_TORNADO_CPR_MIN 0x008
+#define OXSEMI_TORNADO_CPR_DEF 0x10f
+
+/*
+ * Determine the oversampling rate, the clock prescaler, and the clock
+ * divisor for the requested baud rate. The clock rate is 62.5 MHz,
+ * which is four times the baud base, and the prescaler increments in
+ * steps of 1/8. Therefore to make calculations on integers we need
+ * to use a scaled clock rate, which is the baud base multiplied by 32
+ * (or our assumed UART clock rate multiplied by 2).
+ *
+ * The allowed oversampling rates are from 4 up to 16 inclusive (values
+ * from 0 to 3 inclusive map to 16). Likewise the clock prescaler allows
+ * values between 1.000 and 63.875 inclusive (operation for values from
+ * 0.000 to 0.875 has not been specified). The clock divisor is the usual
+ * unsigned 16-bit integer.
+ *
+ * For the most accurate baud rate we use a table of predetermined
+ * oversampling rates and clock prescalers that records all possible
+ * products of the two parameters in the range from 4 up to 255 inclusive,
+ * and additionally 335 for the 1500000bps rate, with the prescaler scaled
+ * by 8. The table is sorted by the decreasing value of the oversampling
+ * rate and ties are resolved by sorting by the decreasing value of the
+ * product. This way preference is given to higher oversampling rates.
+ *
+ * We iterate over the table and choose the product of an oversampling
+ * rate and a clock prescaler that gives the lowest integer division
+ * result deviation, or if an exact integer divider is found we stop
+ * looking for it right away. We do some fixup if the resulting clock
+ * divisor required would be out of its unsigned 16-bit integer range.
+ *
+ * Finally we abuse the supposed fractional part returned to encode the
+ * 4-bit value of the oversampling rate and the 9-bit value of the clock
+ * prescaler which will end up in the TCR and CPR/CPR2 registers.
+ */
+static unsigned int pci_oxsemi_tornado_get_divisor(struct uart_port *port,
+ unsigned int baud,
+ unsigned int *frac)
+{
+ static u8 p[][2] = {
+ { 16, 14, }, { 16, 13, }, { 16, 12, }, { 16, 11, },
+ { 16, 10, }, { 16, 9, }, { 16, 8, }, { 15, 17, },
+ { 15, 16, }, { 15, 15, }, { 15, 14, }, { 15, 13, },
+ { 15, 12, }, { 15, 11, }, { 15, 10, }, { 15, 9, },
+ { 15, 8, }, { 14, 18, }, { 14, 17, }, { 14, 14, },
+ { 14, 13, }, { 14, 12, }, { 14, 11, }, { 14, 10, },
+ { 14, 9, }, { 14, 8, }, { 13, 19, }, { 13, 18, },
+ { 13, 17, }, { 13, 13, }, { 13, 12, }, { 13, 11, },
+ { 13, 10, }, { 13, 9, }, { 13, 8, }, { 12, 19, },
+ { 12, 18, }, { 12, 17, }, { 12, 11, }, { 12, 9, },
+ { 12, 8, }, { 11, 23, }, { 11, 22, }, { 11, 21, },
+ { 11, 20, }, { 11, 19, }, { 11, 18, }, { 11, 17, },
+ { 11, 11, }, { 11, 10, }, { 11, 9, }, { 11, 8, },
+ { 10, 25, }, { 10, 23, }, { 10, 20, }, { 10, 19, },
+ { 10, 17, }, { 10, 10, }, { 10, 9, }, { 10, 8, },
+ { 9, 27, }, { 9, 23, }, { 9, 21, }, { 9, 19, },
+ { 9, 18, }, { 9, 17, }, { 9, 9, }, { 9, 8, },
+ { 8, 31, }, { 8, 29, }, { 8, 23, }, { 8, 19, },
+ { 8, 17, }, { 8, 8, }, { 7, 35, }, { 7, 31, },
+ { 7, 29, }, { 7, 25, }, { 7, 23, }, { 7, 21, },
+ { 7, 19, }, { 7, 17, }, { 7, 15, }, { 7, 14, },
+ { 7, 13, }, { 7, 12, }, { 7, 11, }, { 7, 10, },
+ { 7, 9, }, { 7, 8, }, { 6, 41, }, { 6, 37, },
+ { 6, 31, }, { 6, 29, }, { 6, 23, }, { 6, 19, },
+ { 6, 17, }, { 6, 13, }, { 6, 11, }, { 6, 10, },
+ { 6, 9, }, { 6, 8, }, { 5, 67, }, { 5, 47, },
+ { 5, 43, }, { 5, 41, }, { 5, 37, }, { 5, 31, },
+ { 5, 29, }, { 5, 25, }, { 5, 23, }, { 5, 19, },
+ { 5, 17, }, { 5, 15, }, { 5, 13, }, { 5, 11, },
+ { 5, 10, }, { 5, 9, }, { 5, 8, }, { 4, 61, },
+ { 4, 59, }, { 4, 53, }, { 4, 47, }, { 4, 43, },
+ { 4, 41, }, { 4, 37, }, { 4, 31, }, { 4, 29, },
+ { 4, 23, }, { 4, 19, }, { 4, 17, }, { 4, 13, },
+ { 4, 9, }, { 4, 8, },
+ };
+ /* Scale the quotient for comparison to get the fractional part. */
+ const unsigned int quot_scale = 65536;
+ unsigned int sclk = port->uartclk * 2;
+ unsigned int sdiv = DIV_ROUND_CLOSEST(sclk, baud);
+ unsigned int best_squot;
+ unsigned int squot;
+ unsigned int quot;
+ u16 cpr;
+ u8 tcr;
+ int i;
+
+ /* Old custom speed handling. */
+ if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) {
+ unsigned int cust_div = port->custom_divisor;
+
+ quot = cust_div & UART_DIV_MAX;
+ tcr = (cust_div >> 16) & OXSEMI_TORNADO_TCR_MASK;
+ cpr = (cust_div >> 20) & OXSEMI_TORNADO_CPR_MASK;
+ if (cpr < OXSEMI_TORNADO_CPR_MIN)
+ cpr = OXSEMI_TORNADO_CPR_DEF;
+ } else {
+ best_squot = quot_scale;
+ for (i = 0; i < ARRAY_SIZE(p); i++) {
+ unsigned int spre;
+ unsigned int srem;
+ u8 cp;
+ u8 tc;
+
+ tc = p[i][0];
+ cp = p[i][1];
+ spre = tc * cp;
+
+ srem = sdiv % spre;
+ if (srem > spre / 2)
+ srem = spre - srem;
+ squot = DIV_ROUND_CLOSEST(srem * quot_scale, spre);
+
+ if (srem == 0) {
+ tcr = tc;
+ cpr = cp;
+ quot = sdiv / spre;
+ break;
+ } else if (squot < best_squot) {
+ best_squot = squot;
+ tcr = tc;
+ cpr = cp;
+ quot = DIV_ROUND_CLOSEST(sdiv, spre);
+ }
+ }
+ while (tcr <= (OXSEMI_TORNADO_TCR_MASK + 1) >> 1 &&
+ quot % 2 == 0) {
+ quot >>= 1;
+ tcr <<= 1;
+ }
+ while (quot > UART_DIV_MAX) {
+ if (tcr <= (OXSEMI_TORNADO_TCR_MASK + 1) >> 1) {
+ quot >>= 1;
+ tcr <<= 1;
+ } else if (cpr <= OXSEMI_TORNADO_CPR_MASK >> 1) {
+ quot >>= 1;
+ cpr <<= 1;
+ } else {
+ quot = quot * cpr / OXSEMI_TORNADO_CPR_MASK;
+ cpr = OXSEMI_TORNADO_CPR_MASK;
+ }
+ }
+ }
+
+ *frac = (cpr << 8) | (tcr & OXSEMI_TORNADO_TCR_MASK);
+ return quot;
+}
+
+/*
+ * Set the oversampling rate in the transmitter clock cycle register (TCR),
+ * the clock prescaler in the clock prescaler register (CPR and CPR2), and
+ * the clock divisor in the divisor latch (DLL and DLM). Note that for
+ * backwards compatibility any write to CPR clears CPR2 and therefore CPR
+ * has to be written first, followed by CPR2, which occupies the location
+ * of CKS used with earlier UART designs.
+ */
+static void pci_oxsemi_tornado_set_divisor(struct uart_port *port,
+ unsigned int baud,
+ unsigned int quot,
+ unsigned int quot_frac)
+{
+ struct uart_8250_port *up = up_to_u8250p(port);
+ u8 cpr2 = quot_frac >> 16;
+ u8 cpr = quot_frac >> 8;
+ u8 tcr = quot_frac;
+
+ serial_icr_write(up, UART_TCR, tcr);
+ serial_icr_write(up, UART_CPR, cpr);
+ serial_icr_write(up, UART_CKS, cpr2);
+ serial8250_do_set_divisor(port, baud, quot, 0);
+}
+
+/*
+ * For Tornado devices we force MCR[7] set for the Divide-by-M N/8 baud rate
+ * generator prescaler (CPR and CPR2). Otherwise no prescaler would be used.
+ */
+static void pci_oxsemi_tornado_set_mctrl(struct uart_port *port,
+ unsigned int mctrl)
+{
+ struct uart_8250_port *up = up_to_u8250p(port);
+
+ up->mcr |= UART_MCR_CLKSEL;
+ serial8250_do_set_mctrl(port, mctrl);
+}
+
+static int pci_oxsemi_tornado_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *up, int idx)
+{
+ struct pci_dev *dev = priv->dev;
+
+ if (pci_oxsemi_tornado_p(dev)) {
+ up->port.get_divisor = pci_oxsemi_tornado_get_divisor;
+ up->port.set_divisor = pci_oxsemi_tornado_set_divisor;
+ up->port.set_mctrl = pci_oxsemi_tornado_set_mctrl;
+ }
+
+ return pci_default_setup(priv, board, up, idx);
+}
+
static int pci_asix_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
@@ -2244,7 +2436,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .init = pci_endrun_init,
+ .init = pci_oxsemi_tornado_init,
.setup = pci_default_setup,
},
/*
@@ -2256,7 +2448,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_oxsemi_tornado_init,
- .setup = pci_default_setup,
+ .setup = pci_oxsemi_tornado_setup,
},
{
.vendor = PCI_VENDOR_ID_MAINPINE,
@@ -2264,7 +2456,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_oxsemi_tornado_init,
- .setup = pci_default_setup,
+ .setup = pci_oxsemi_tornado_setup,
},
{
.vendor = PCI_VENDOR_ID_DIGI,
@@ -2272,7 +2464,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
.subvendor = PCI_SUBVENDOR_ID_IBM,
.subdevice = PCI_ANY_ID,
.init = pci_oxsemi_tornado_init,
- .setup = pci_default_setup,
+ .setup = pci_oxsemi_tornado_setup,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
@@ -2589,7 +2781,7 @@ enum pci_board_num_t {
pbn_b0_2_1843200,
pbn_b0_4_1843200,
- pbn_b0_1_3906250,
+ pbn_b0_1_15625000,
pbn_b0_bt_1_115200,
pbn_b0_bt_2_115200,
@@ -2667,12 +2859,11 @@ enum pci_board_num_t {
pbn_panacom2,
pbn_panacom4,
pbn_plx_romulus,
- pbn_endrun_2_3906250,
pbn_oxsemi,
- pbn_oxsemi_1_3906250,
- pbn_oxsemi_2_3906250,
- pbn_oxsemi_4_3906250,
- pbn_oxsemi_8_3906250,
+ pbn_oxsemi_1_15625000,
+ pbn_oxsemi_2_15625000,
+ pbn_oxsemi_4_15625000,
+ pbn_oxsemi_8_15625000,
pbn_intel_i960,
pbn_sgi_ioc3,
pbn_computone_4,
@@ -2815,10 +3006,10 @@ static struct pciserial_board pci_boards[] = {
.uart_offset = 8,
},
- [pbn_b0_1_3906250] = {
+ [pbn_b0_1_15625000] = {
.flags = FL_BASE0,
.num_ports = 1,
- .base_baud = 3906250,
+ .base_baud = 15625000,
.uart_offset = 8,
},
@@ -3190,20 +3381,6 @@ static struct pciserial_board pci_boards[] = {
},
/*
- * EndRun Technologies
- * Uses the size of PCI Base region 0 to
- * signal now many ports are available
- * 2 port 952 Uart support
- */
- [pbn_endrun_2_3906250] = {
- .flags = FL_BASE0,
- .num_ports = 2,
- .base_baud = 3906250,
- .uart_offset = 0x200,
- .first_offset = 0x1000,
- },
-
- /*
* This board uses the size of PCI Base region 0 to
* signal now many ports are available
*/
@@ -3213,31 +3390,31 @@ static struct pciserial_board pci_boards[] = {
.base_baud = 115200,
.uart_offset = 8,
},
- [pbn_oxsemi_1_3906250] = {
+ [pbn_oxsemi_1_15625000] = {
.flags = FL_BASE0,
.num_ports = 1,
- .base_baud = 3906250,
+ .base_baud = 15625000,
.uart_offset = 0x200,
.first_offset = 0x1000,
},
- [pbn_oxsemi_2_3906250] = {
+ [pbn_oxsemi_2_15625000] = {
.flags = FL_BASE0,
.num_ports = 2,
- .base_baud = 3906250,
+ .base_baud = 15625000,
.uart_offset = 0x200,
.first_offset = 0x1000,
},
- [pbn_oxsemi_4_3906250] = {
+ [pbn_oxsemi_4_15625000] = {
.flags = FL_BASE0,
.num_ports = 4,
- .base_baud = 3906250,
+ .base_baud = 15625000,
.uart_offset = 0x200,
.first_offset = 0x1000,
},
- [pbn_oxsemi_8_3906250] = {
+ [pbn_oxsemi_8_15625000] = {
.flags = FL_BASE0,
.num_ports = 8,
- .base_baud = 3906250,
+ .base_baud = 15625000,
.uart_offset = 0x200,
.first_offset = 0x1000,
},
@@ -3518,6 +3695,12 @@ static struct pciserial_board pci_boards[] = {
},
};
+#define REPORT_CONFIG(option) \
+ (IS_ENABLED(CONFIG_##option) ? 0 : (kernel_ulong_t)&#option)
+#define REPORT_8250_CONFIG(option) \
+ (IS_ENABLED(CONFIG_SERIAL_8250_##option) ? \
+ 0 : (kernel_ulong_t)&"SERIAL_8250_"#option)
+
static const struct pci_device_id blacklist[] = {
/* softmodems */
{ PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
@@ -3525,40 +3708,43 @@ static const struct pci_device_id blacklist[] = {
{ PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */
/* multi-io cards handled by parport_serial */
- { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */
- { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */
- { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */
+ /* WCH CH353 2S1P */
+ { PCI_DEVICE(0x4348, 0x7053), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), },
+ /* WCH CH353 1S1P */
+ { PCI_DEVICE(0x4348, 0x5053), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), },
+ /* WCH CH382 2S1P */
+ { PCI_DEVICE(0x1c00, 0x3250), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), },
/* Intel platforms with MID UART */
- { PCI_VDEVICE(INTEL, 0x081b), },
- { PCI_VDEVICE(INTEL, 0x081c), },
- { PCI_VDEVICE(INTEL, 0x081d), },
- { PCI_VDEVICE(INTEL, 0x1191), },
- { PCI_VDEVICE(INTEL, 0x18d8), },
- { PCI_VDEVICE(INTEL, 0x19d8), },
+ { PCI_VDEVICE(INTEL, 0x081b), REPORT_8250_CONFIG(MID), },
+ { PCI_VDEVICE(INTEL, 0x081c), REPORT_8250_CONFIG(MID), },
+ { PCI_VDEVICE(INTEL, 0x081d), REPORT_8250_CONFIG(MID), },
+ { PCI_VDEVICE(INTEL, 0x1191), REPORT_8250_CONFIG(MID), },
+ { PCI_VDEVICE(INTEL, 0x18d8), REPORT_8250_CONFIG(MID), },
+ { PCI_VDEVICE(INTEL, 0x19d8), REPORT_8250_CONFIG(MID), },
/* Intel platforms with DesignWare UART */
- { PCI_VDEVICE(INTEL, 0x0936), },
- { PCI_VDEVICE(INTEL, 0x0f0a), },
- { PCI_VDEVICE(INTEL, 0x0f0c), },
- { PCI_VDEVICE(INTEL, 0x228a), },
- { PCI_VDEVICE(INTEL, 0x228c), },
- { PCI_VDEVICE(INTEL, 0x4b96), },
- { PCI_VDEVICE(INTEL, 0x4b97), },
- { PCI_VDEVICE(INTEL, 0x4b98), },
- { PCI_VDEVICE(INTEL, 0x4b99), },
- { PCI_VDEVICE(INTEL, 0x4b9a), },
- { PCI_VDEVICE(INTEL, 0x4b9b), },
- { PCI_VDEVICE(INTEL, 0x9ce3), },
- { PCI_VDEVICE(INTEL, 0x9ce4), },
+ { PCI_VDEVICE(INTEL, 0x0936), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x0f0a), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x0f0c), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x228a), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x228c), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x4b96), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x4b97), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x4b98), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x4b99), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x4b9a), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x4b9b), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x9ce3), REPORT_8250_CONFIG(LPSS), },
+ { PCI_VDEVICE(INTEL, 0x9ce4), REPORT_8250_CONFIG(LPSS), },
/* Exar devices */
- { PCI_VDEVICE(EXAR, PCI_ANY_ID), },
- { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), },
+ { PCI_VDEVICE(EXAR, PCI_ANY_ID), REPORT_8250_CONFIG(EXAR), },
+ { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), REPORT_8250_CONFIG(EXAR), },
/* Pericom devices */
- { PCI_VDEVICE(PERICOM, PCI_ANY_ID), },
- { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), },
+ { PCI_VDEVICE(PERICOM, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), },
+ { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), },
/* End of the black list */
{ }
@@ -3840,8 +4026,12 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
board = &pci_boards[ent->driver_data];
exclude = pci_match_id(blacklist, dev);
- if (exclude)
+ if (exclude) {
+ if (exclude->driver_data)
+ pci_warn(dev, "ignoring port, enable %s to handle\n",
+ (const char *)exclude->driver_data);
return -ENODEV;
+ }
rc = pcim_enable_device(dev);
pci_save_state(dev);
@@ -4110,13 +4300,6 @@ static const struct pci_device_id serial_pci_tbl[] = {
0x10b5, 0x106a, 0, 0,
pbn_plx_romulus },
/*
- * EndRun Technologies. PCI express device range.
- * EndRun PTP/1588 has 2 Native UARTs.
- */
- { PCI_VENDOR_ID_ENDRUN, PCI_DEVICE_ID_ENDRUN_1588,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_endrun_2_3906250 },
- /*
* Quatech cards. These actually have configurable clocks but for
* now we just use the default.
*
@@ -4225,158 +4408,165 @@ static const struct pci_device_id serial_pci_tbl[] = {
*/
{ PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_1_3906250 },
+ pbn_b0_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_2_3906250 },
+ pbn_oxsemi_2_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_2_3906250 },
+ pbn_oxsemi_2_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_4_3906250 },
+ pbn_oxsemi_4_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_4_3906250 },
+ pbn_oxsemi_4_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_8_3906250 },
+ pbn_oxsemi_8_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_8_3906250 },
+ pbn_oxsemi_8_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
/*
* Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado
*/
{ PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */
PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0,
- pbn_oxsemi_1_3906250 },
+ pbn_oxsemi_1_15625000 },
{ PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */
PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0,
- pbn_oxsemi_2_3906250 },
+ pbn_oxsemi_2_15625000 },
{ PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */
PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0,
- pbn_oxsemi_4_3906250 },
+ pbn_oxsemi_4_15625000 },
{ PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */
PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0,
- pbn_oxsemi_8_3906250 },
+ pbn_oxsemi_8_15625000 },
/*
* Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado
*/
{ PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM,
PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0,
- pbn_oxsemi_2_3906250 },
+ pbn_oxsemi_2_15625000 },
+ /*
+ * EndRun Technologies. PCI express device range.
+ * EndRun PTP/1588 has 2 Native UARTs utilizing OxSemi 952.
+ */
+ { PCI_VENDOR_ID_ENDRUN, PCI_DEVICE_ID_ENDRUN_1588,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_oxsemi_2_15625000 },
/*
* SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards,
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 1fbd5bf264be..78b6dedc43e6 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -263,7 +263,7 @@ static const struct serial8250_config uart_config[] = {
.tx_loadsz = 63,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
UART_FCR7_64BYTE,
- .flags = UART_CAP_FIFO,
+ .flags = UART_CAP_FIFO | UART_CAP_NOTEMT,
},
[PORT_RT2880] = {
.name = "Palmchip BK-3103",
@@ -538,27 +538,6 @@ serial_port_out_sync(struct uart_port *p, int offset, int value)
}
/*
- * For the 16C950
- */
-static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
-{
- serial_out(up, UART_SCR, offset);
- serial_out(up, UART_ICR, value);
-}
-
-static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
-{
- unsigned int value;
-
- serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
- serial_out(up, UART_SCR, offset);
- value = serial_in(up, UART_ICR);
- serial_icr_write(up, UART_ACR, up->acr);
-
- return value;
-}
-
-/*
* FIFO support.
*/
static void serial8250_clear_fifos(struct uart_8250_port *p)
@@ -1504,18 +1483,19 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec)
hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL);
}
-static void __stop_tx_rs485(struct uart_8250_port *p)
+static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay)
{
struct uart_8250_em485 *em485 = p->em485;
+ stop_delay += (u64)p->port.rs485.delay_rts_after_send * NSEC_PER_MSEC;
+
/*
* rs485_stop_tx() is going to set RTS according to config
* AND flush RX FIFO if required.
*/
- if (p->port.rs485.delay_rts_after_send > 0) {
+ if (stop_delay > 0) {
em485->active_timer = &em485->stop_tx_timer;
- start_hrtimer_ms(&em485->stop_tx_timer,
- p->port.rs485.delay_rts_after_send);
+ hrtimer_start(&em485->stop_tx_timer, ns_to_ktime(stop_delay), HRTIMER_MODE_REL);
} else {
p->rs485_stop_tx(p);
em485->active_timer = NULL;
@@ -1535,16 +1515,32 @@ static inline void __stop_tx(struct uart_8250_port *p)
if (em485) {
unsigned char lsr = serial_in(p, UART_LSR);
+ u64 stop_delay = 0;
+
+ if (!(lsr & UART_LSR_THRE))
+ return;
/*
* To provide required timeing and allow FIFO transfer,
* __stop_tx_rs485() must be called only when both FIFO and
- * shift register are empty. It is for device driver to enable
- * interrupt on TEMT.
+ * shift register are empty. The device driver should either
+ * enable interrupt on TEMT or set UART_CAP_NOTEMT that will
+ * enlarge stop_tx_timer by the tx time of one frame to cover
+ * for emptying of the shift register.
*/
- if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
- return;
+ if (!(lsr & UART_LSR_TEMT)) {
+ if (!(p->capabilities & UART_CAP_NOTEMT))
+ return;
+ /*
+ * RTS might get deasserted too early with the normal
+ * frame timing formula. It seems to suggest THRE might
+ * get asserted already during tx of the stop bit
+ * rather than after it is fully sent.
+ * Roughly estimate 1 extra bit here with / 7.
+ */
+ stop_delay = p->port.frame_time + DIV_ROUND_UP(p->port.frame_time, 7);
+ }
- __stop_tx_rs485(p);
+ __stop_tx_rs485(p, stop_delay);
}
__do_stop_tx(p);
}
@@ -1948,9 +1944,12 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
status = serial8250_rx_chars(up, status);
}
serial8250_modem_status(up);
- if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE) &&
- (up->ier & UART_IER_THRI))
- serial8250_tx_chars(up);
+ if ((status & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) {
+ if (!up->dma || up->dma->tx_err)
+ serial8250_tx_chars(up);
+ else
+ __stop_tx(up);
+ }
uart_unlock_and_check_sysrq_irqrestore(port, flags);
@@ -2077,10 +2076,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
serial8250_rpm_put(up);
}
-/*
- * Wait for transmitter & holding register to empty
- */
-static void wait_for_xmitr(struct uart_8250_port *up, int bits)
+static void wait_for_lsr(struct uart_8250_port *up, int bits)
{
unsigned int status, tmout = 10000;
@@ -2097,6 +2093,16 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
udelay(1);
touch_nmi_watchdog();
}
+}
+
+/*
+ * Wait for transmitter & holding register to empty
+ */
+static void wait_for_xmitr(struct uart_8250_port *up, int bits)
+{
+ unsigned int tmout;
+
+ wait_for_lsr(up, bits);
/* Wait up to 1s for flow control if necessary */
if (up->port.flags & UPF_CONS_FLOW) {
@@ -2614,10 +2620,8 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
}
if (!(c_cflag & PARODD))
cval |= UART_LCR_EPAR;
-#ifdef CMSPAR
if (c_cflag & CMSPAR)
cval |= UART_LCR_SPAR;
-#endif
return cval;
}
@@ -3333,6 +3337,35 @@ static void serial8250_console_restore(struct uart_8250_port *up)
}
/*
+ * Print a string to the serial port using the device FIFO
+ *
+ * It sends fifosize bytes and then waits for the fifo
+ * to get empty.
+ */
+static void serial8250_console_fifo_write(struct uart_8250_port *up,
+ const char *s, unsigned int count)
+{
+ int i;
+ const char *end = s + count;
+ unsigned int fifosize = up->tx_loadsz;
+ bool cr_sent = false;
+
+ while (s != end) {
+ wait_for_lsr(up, UART_LSR_THRE);
+
+ for (i = 0; i < fifosize && s != end; ++i) {
+ if (*s == '\n' && !cr_sent) {
+ serial_out(up, UART_TX, '\r');
+ cr_sent = true;
+ } else {
+ serial_out(up, UART_TX, *s++);
+ cr_sent = false;
+ }
+ }
+ }
+}
+
+/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*
@@ -3347,7 +3380,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
struct uart_8250_em485 *em485 = up->em485;
struct uart_port *port = &up->port;
unsigned long flags;
- unsigned int ier;
+ unsigned int ier, use_fifo;
int locked = 1;
touch_nmi_watchdog();
@@ -3379,7 +3412,30 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
mdelay(port->rs485.delay_rts_before_send);
}
- uart_console_write(port, s, count, serial8250_console_putchar);
+ use_fifo = (up->capabilities & UART_CAP_FIFO) &&
+ /*
+ * BCM283x requires to check the fifo
+ * after each byte.
+ */
+ !(up->capabilities & UART_CAP_MINI) &&
+ /*
+ * tx_loadsz contains the transmit fifo size
+ */
+ up->tx_loadsz > 1 &&
+ (up->fcr & UART_FCR_ENABLE_FIFO) &&
+ port->state &&
+ test_bit(TTY_PORT_INITIALIZED, &port->state->port.iflags) &&
+ /*
+ * After we put a data in the fifo, the controller will send
+ * it regardless of the CTS state. Therefore, only use fifo
+ * if we don't use control flow.
+ */
+ !(up->port.flags & UPF_CONS_FLOW);
+
+ if (likely(use_fifo))
+ serial8250_console_fifo_write(up, s, count);
+ else
+ uart_console_write(port, s, count, serial8250_console_putchar);
/*
* Finally, wait for transmitter to become empty
diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c
index 33ca98bfa5b3..795e55142d4c 100644
--- a/drivers/tty/serial/8250/8250_pxa.c
+++ b/drivers/tty/serial/8250/8250_pxa.c
@@ -22,7 +22,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
-#include <linux/pm_runtime.h>
#include "8250.h"
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index cd93ea6eed65..fdb6c4188695 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -380,7 +380,7 @@ config SERIAL_8250_DW
config SERIAL_8250_EM
tristate "Support for Emma Mobile integrated serial port"
depends on SERIAL_8250 && HAVE_CLK
- depends on (ARM && ARCH_RENESAS) || COMPILE_TEST
+ depends on ARCH_RENESAS || COMPILE_TEST
help
Selecting this option will add support for the integrated serial
port hardware found on the Emma Mobile line of processors.