summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/8250
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/8250')
-rw-r--r--drivers/tty/serial/8250/8250.h3
-rw-r--r--drivers/tty/serial/8250/8250_alpha.c21
-rw-r--r--drivers/tty/serial/8250/8250_bcm7271.c115
-rw-r--r--drivers/tty/serial/8250/8250_core.c12
-rw-r--r--drivers/tty/serial/8250/8250_dma.c31
-rw-r--r--drivers/tty/serial/8250/8250_dw.c51
-rw-r--r--drivers/tty/serial/8250/8250_exar.c1051
-rw-r--r--drivers/tty/serial/8250/8250_lpc18xx.c2
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c10
-rw-r--r--drivers/tty/serial/8250/8250_of.c37
-rw-r--r--drivers/tty/serial/8250/8250_omap.c49
-rw-r--r--drivers/tty/serial/8250/8250_pci.c8
-rw-r--r--drivers/tty/serial/8250/8250_pci1xxxx.c50
-rw-r--r--drivers/tty/serial/8250/8250_pnp.c65
-rw-r--r--drivers/tty/serial/8250/8250_port.c29
-rw-r--r--drivers/tty/serial/8250/Makefile2
16 files changed, 1208 insertions, 328 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 1aa3e55c8b47..6a7b286f6f5a 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -293,9 +293,6 @@ static inline int serial8250_in_MCR(struct uart_8250_port *up)
return mctrl;
}
-bool alpha_jensen(void);
-void alpha_jensen_set_mctrl(struct uart_port *port, unsigned int mctrl);
-
#ifdef CONFIG_SERIAL_8250_PNP
int serial8250_pnp_init(void);
void serial8250_pnp_exit(void);
diff --git a/drivers/tty/serial/8250/8250_alpha.c b/drivers/tty/serial/8250/8250_alpha.c
deleted file mode 100644
index 58e70328aa4d..000000000000
--- a/drivers/tty/serial/8250/8250_alpha.c
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-
-#include <asm/machvec.h>
-#include "8250.h"
-
-bool alpha_jensen(void)
-{
- return !strcmp(alpha_mv.vector_name, "Jensen");
-}
-
-void alpha_jensen_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- /*
- * Digital did something really horribly wrong with the OUT1 and OUT2
- * lines on Alpha Jensen. The failure mode is that if either is
- * cleared, the machine locks up with endless interrupts.
- */
- mctrl |= TIOCM_OUT1 | TIOCM_OUT2;
-
- serial8250_do_set_mctrl(port, mctrl);
-}
diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c
index 5daa38d9c64e..2569ca69223f 100644
--- a/drivers/tty/serial/8250/8250_bcm7271.c
+++ b/drivers/tty/serial/8250/8250_bcm7271.c
@@ -413,20 +413,18 @@ static int stop_tx_dma(struct uart_8250_port *p)
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;
+ struct tty_port *tport = &p->port.state->port;
u32 tx_size;
if (uart_tx_stopped(&p->port) || priv->tx_running ||
- uart_circ_empty(xmit)) {
+ kfifo_is_empty(&tport->xmit_fifo)) {
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);
- uart_xmit_advance(&p->port, tx_size);
+ tx_size = uart_fifo_out(&p->port, priv->tx_buf, UART_XMIT_SIZE);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);
udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size);
@@ -540,7 +538,7 @@ 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;
+ struct tty_port *tport = &port_8250->port.state->port;
if (isr & UDMA_INTR_TX_ABORT) {
if (priv->tx_running)
@@ -548,7 +546,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr)
return;
}
priv->tx_running = false;
- if (!uart_circ_empty(xmit) && !uart_tx_stopped(up))
+ if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(up))
brcmuart_tx_dma(port_8250);
}
@@ -675,18 +673,46 @@ static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv)
clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate);
}
+static u32 find_quot(struct device *dev, u32 freq, u32 baud, u32 *percent)
+{
+ u32 quot;
+ u32 rate;
+ u64 hires_rate;
+ u64 hires_baud;
+ u64 hires_err;
+
+ rate = freq / 16;
+ quot = DIV_ROUND_CLOSEST(rate, baud);
+ if (!quot)
+ return 0;
+
+ /* increase resolution to get xx.xx percent */
+ hires_rate = div_u64((u64)rate * 10000, (u64)quot);
+ hires_baud = (u64)baud * 10000;
+
+ /* get the delta */
+ if (hires_rate > hires_baud)
+ hires_err = (hires_rate - hires_baud);
+ else
+ hires_err = (hires_baud - hires_rate);
+
+ *percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud);
+
+ dev_dbg(dev, "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n",
+ baud, freq, *percent / 100, *percent % 100);
+
+ return quot;
+}
+
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 freq;
u32 best_quot = 1;
- u32 rate;
- int best_index = -1;
- u64 hires_rate;
- u64 hires_baud;
- u64 hires_err;
+ u32 best_freq = 0;
int rc;
int i;
int real_baud;
@@ -695,44 +721,35 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
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;
+ /* Try default_mux_rate first */
+ quot = find_quot(up->dev, priv->default_mux_rate, baud, &percent);
+ if (quot) {
+ best_percent = percent;
+ best_freq = priv->default_mux_rate;
+ best_quot = quot;
+ }
+ /* If more than 1% error, find the closest match for specified baud */
+ if (best_percent > 100) {
+ for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) {
+ freq = priv->real_rates[i];
+ if (freq == 0 || freq == priv->default_mux_rate)
+ continue;
+ quot = find_quot(up->dev, freq, baud, &percent);
+ if (!quot)
+ continue;
+
+ if (percent < best_percent) {
+ best_percent = percent;
+ best_freq = freq;
+ best_quot = quot;
+ }
}
}
- if (best_index == -1) {
+ if (!best_freq) {
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);
+ rc = clk_set_rate(priv->baud_mux_clk, best_freq);
if (rc)
dev_err(up->dev, "Error selecting BAUD MUX clock\n");
@@ -741,8 +758,8 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
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);
+ real_baud = best_freq / 16 / best_quot;
+ dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", best_freq);
dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n",
baud, real_baud);
@@ -751,7 +768,7 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
i += (i / 2);
priv->char_wait = ns_to_ktime(i);
- up->uartclk = rate;
+ up->uartclk = best_freq;
}
static void brcmstb_set_termios(struct uart_port *up,
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index b62ad9006780..ff15022369e4 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -15,6 +15,7 @@
*/
#include <linux/acpi.h>
+#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
@@ -41,6 +42,8 @@
#include <asm/irq.h>
+#include "../serial_base.h" /* For serial_base_add_isa_preferred_console() */
+
#include "8250.h"
/*
@@ -280,7 +283,8 @@ static void serial8250_backup_timeout(struct timer_list *t)
*/
lsr = serial_lsr_in(up);
if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
- (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) &&
+ (!kfifo_is_empty(&up->port.state->port.xmit_fifo) ||
+ up->port.x_char) &&
(lsr & UART_LSR_THRE)) {
iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
iir |= UART_IIR_THRI;
@@ -508,10 +512,6 @@ static struct uart_8250_port *serial8250_setup_port(int index)
up->ops = &univ8250_driver_ops;
- if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
- (IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
- up->port.set_mctrl = alpha_jensen_set_mctrl;
-
serial8250_set_defaults(up);
return up;
@@ -563,6 +563,8 @@ static void __init serial8250_isa_init_ports(void)
port->irqflags |= irqflag;
if (serial8250_isa_config != NULL)
serial8250_isa_config(i, &up->port, &up->capabilities);
+
+ serial_base_add_isa_preferred_console(serial8250_reg.dev_name, i);
}
}
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 8b30ca8fdd3f..8a353e3cc3dd 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -15,7 +15,7 @@ static void __dma_tx_complete(void *param)
{
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
unsigned long flags;
int ret;
@@ -28,7 +28,7 @@ static void __dma_tx_complete(void *param)
uart_xmit_advance(&p->port, dma->tx_size);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);
ret = serial8250_tx_dma(p);
@@ -86,9 +86,10 @@ static void dma_rx_complete(void *param)
int serial8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
struct dma_async_tx_descriptor *desc;
struct uart_port *up = &p->port;
+ struct scatterlist sg;
int ret;
if (dma->tx_running) {
@@ -102,19 +103,27 @@ int serial8250_tx_dma(struct uart_8250_port *p)
uart_xchar_out(up, UART_TX);
}
- if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
+ if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {
/* We have been called from __dma_tx_complete() */
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,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ sg_init_table(&sg, 1);
+ /* kfifo can do more than one sg, we don't (quite yet) */
+ ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
+ UART_XMIT_SIZE, dma->tx_addr);
+
+ /* we already checked empty fifo above, so there should be something */
+ if (WARN_ON_ONCE(ret != 1))
+ return 0;
+
+ dma->tx_size = sg_dma_len(&sg);
+
+ desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
ret = -EBUSY;
goto err;
@@ -253,7 +262,7 @@ int serial8250_request_dma(struct uart_8250_port *p)
/* TX buffer */
dma->tx_addr = dma_map_single(dma->txchan->device->dev,
- p->port.state->xmit.buf,
+ p->port.state->port.xmit_buf,
UART_XMIT_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index a3acbf0f5da1..ba9f4dc4e71d 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -100,14 +100,18 @@ static void dw8250_force_idle(struct uart_port *p)
(void)p->serial_in(p, UART_RX);
}
-static void dw8250_check_lcr(struct uart_port *p, int value)
+static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
{
- void __iomem *offset = p->membase + (UART_LCR << p->regshift);
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
+ void __iomem *addr = p->membase + (offset << p->regshift);
int tries = 1000;
+ if (offset != UART_LCR || d->uart_16550_compatible)
+ return;
+
/* Make sure LCR write wasn't ignored */
while (tries--) {
- unsigned int lcr = p->serial_in(p, UART_LCR);
+ unsigned int lcr = p->serial_in(p, offset);
if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
return;
@@ -116,15 +120,15 @@ static void dw8250_check_lcr(struct uart_port *p, int value)
#ifdef CONFIG_64BIT
if (p->type == PORT_OCTEON)
- __raw_writeq(value & 0xff, offset);
+ __raw_writeq(value & 0xff, addr);
else
#endif
if (p->iotype == UPIO_MEM32)
- writel(value, offset);
+ writel(value, addr);
else if (p->iotype == UPIO_MEM32BE)
- iowrite32be(value, offset);
+ iowrite32be(value, addr);
else
- writeb(value, offset);
+ writeb(value, addr);
}
/*
* FIXME: this deadlocks if port->lock is already held
@@ -158,12 +162,8 @@ static void dw8250_tx_wait_empty(struct uart_port *p)
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = to_dw8250_data(p->private_data);
-
writeb(value, p->membase + (offset << p->regshift));
-
- if (offset == UART_LCR && !d->uart_16550_compatible)
- dw8250_check_lcr(p, value);
+ dw8250_check_lcr(p, offset, value);
}
static void dw8250_serial_out38x(struct uart_port *p, int offset, int value)
@@ -185,35 +185,26 @@ static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
#ifdef CONFIG_64BIT
static unsigned int dw8250_serial_inq(struct uart_port *p, int offset)
{
- unsigned int value;
-
- value = (u8)__raw_readq(p->membase + (offset << p->regshift));
+ u8 value = __raw_readq(p->membase + (offset << p->regshift));
return dw8250_modify_msr(p, offset, value);
}
static void dw8250_serial_outq(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = to_dw8250_data(p->private_data);
-
value &= 0xff;
__raw_writeq(value, p->membase + (offset << p->regshift));
/* Read back to ensure register write ordering. */
__raw_readq(p->membase + (UART_LCR << p->regshift));
- if (offset == UART_LCR && !d->uart_16550_compatible)
- dw8250_check_lcr(p, value);
+ dw8250_check_lcr(p, offset, value);
}
#endif /* CONFIG_64BIT */
static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = to_dw8250_data(p->private_data);
-
writel(value, p->membase + (offset << p->regshift));
-
- if (offset == UART_LCR && !d->uart_16550_compatible)
- dw8250_check_lcr(p, value);
+ dw8250_check_lcr(p, offset, value);
}
static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
@@ -225,12 +216,8 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
static void dw8250_serial_out32be(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = to_dw8250_data(p->private_data);
-
iowrite32be(value, p->membase + (offset << p->regshift));
-
- if (offset == UART_LCR && !d->uart_16550_compatible)
- dw8250_check_lcr(p, value);
+ dw8250_check_lcr(p, offset, value);
}
static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset)
@@ -356,9 +343,9 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
long rate;
int ret;
+ clk_disable_unprepare(d->clk);
rate = clk_round_rate(d->clk, newrate);
- if (rate > 0 && p->uartclk != rate) {
- clk_disable_unprepare(d->clk);
+ if (rate > 0) {
/*
* Note that any clock-notifer worker will block in
* serial8250_update_uartclk() until we are done.
@@ -366,8 +353,8 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
ret = clk_set_rate(d->clk, newrate);
if (!ret)
p->uartclk = rate;
- clk_prepare_enable(d->clk);
}
+ clk_prepare_enable(d->clk);
dw8250_do_set_termios(p, termios, old);
}
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index 0440df7de1ed..616128254bbd 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -6,6 +6,7 @@
*
* Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved.
*/
+#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -46,8 +47,50 @@
#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021
#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022
+#define PCI_VENDOR_ID_CONNECT_TECH 0x12c4
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO 0x0340
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A 0x0341
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B 0x0342
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS 0x0350
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A 0x0351
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B 0x0352
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS 0x0353
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A 0x0354
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B 0x0355
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO 0x0360
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A 0x0361
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B 0x0362
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP 0x0370
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232 0x0371
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485 0x0372
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP 0x0373
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP 0x0374
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP 0x0375
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS 0x0376
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT 0x0380
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT 0x0381
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO 0x0382
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO 0x0392
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP 0x03A0
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232 0x03A1
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485 0x03A2
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS 0x03A3
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XEG001 0x0602
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_BASE 0x1000
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_2 0x1002
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_4 0x1004
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_8 0x1008
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_12 0x100C
+#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_16 0x1010
+#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X 0x110c
+#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X 0x110d
+#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16 0x1110
+
#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358
#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358
+#define PCI_DEVICE_ID_EXAR_XR17V252 0x0252
+#define PCI_DEVICE_ID_EXAR_XR17V254 0x0254
+#define PCI_DEVICE_ID_EXAR_XR17V258 0x0258
#define PCI_SUBDEVICE_ID_USR_2980 0x0128
#define PCI_SUBDEVICE_ID_USR_2981 0x0129
@@ -86,12 +129,25 @@
#define UART_EXAR_DLD 0x02 /* Divisor Fractional */
#define UART_EXAR_DLD_485_POLARITY 0x80 /* RS-485 Enable Signal Polarity */
+/* EEPROM registers */
+#define UART_EXAR_REGB 0x8e
+#define UART_EXAR_REGB_EECK BIT(4)
+#define UART_EXAR_REGB_EECS BIT(5)
+#define UART_EXAR_REGB_EEDI BIT(6)
+#define UART_EXAR_REGB_EEDO BIT(7)
+#define UART_EXAR_REGB_EE_ADDR_SIZE 6
+#define UART_EXAR_REGB_EE_DATA_SIZE 16
+
+#define UART_EXAR_XR17C15X_PORT_OFFSET 0x200
+#define UART_EXAR_XR17V25X_PORT_OFFSET 0x200
+#define UART_EXAR_XR17V35X_PORT_OFFSET 0x400
+
/*
* IOT2040 MPIO wiring semantics:
*
* MPIO Port Function
* ---- ---- --------
- * 0 2 Mode bit 0
+ * 0 2 Mode bit 0
* 1 2 Mode bit 1
* 2 2 Terminate bus
* 3 - <reserved>
@@ -121,38 +177,300 @@
#define IOT2040_UARTS_ENABLE 0x03
#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */
+/* CTI EEPROM offsets */
+#define CTI_EE_OFF_XR17C15X_OSC_FREQ 0x04 /* 2 words */
+#define CTI_EE_OFF_XR17V25X_OSC_FREQ 0x08 /* 2 words */
+#define CTI_EE_OFF_XR17C15X_PART_NUM 0x0A /* 4 words */
+#define CTI_EE_OFF_XR17V25X_PART_NUM 0x0E /* 4 words */
+#define CTI_EE_OFF_XR17C15X_SERIAL_NUM 0x0E /* 1 word */
+#define CTI_EE_OFF_XR17V25X_SERIAL_NUM 0x12 /* 1 word */
+#define CTI_EE_OFF_XR17V35X_SERIAL_NUM 0x11 /* 2 word */
+#define CTI_EE_OFF_XR17V35X_BRD_FLAGS 0x13 /* 1 word */
+#define CTI_EE_OFF_XR17V35X_PORT_FLAGS 0x14 /* 1 word */
+
+#define CTI_EE_MASK_PORT_FLAGS_TYPE GENMASK(7, 0)
+#define CTI_EE_MASK_OSC_FREQ_LOWER GENMASK(15, 0)
+#define CTI_EE_MASK_OSC_FREQ_UPPER GENMASK(31, 16)
+
+#define CTI_FPGA_RS485_IO_REG 0x2008
+#define CTI_FPGA_CFG_INT_EN_REG 0x48
+#define CTI_FPGA_CFG_INT_EN_EXT_BIT BIT(15) /* External int enable bit */
+
+#define CTI_DEFAULT_PCI_OSC_FREQ 29491200
+#define CTI_DEFAULT_PCIE_OSC_FREQ 125000000
+#define CTI_DEFAULT_FPGA_OSC_FREQ 33333333
+
+/*
+ * CTI Serial port line types. These match the values stored in the first
+ * nibble of the CTI EEPROM port_flags word.
+ */
+enum cti_port_type {
+ CTI_PORT_TYPE_NONE = 0,
+ CTI_PORT_TYPE_RS232, // RS232 ONLY
+ CTI_PORT_TYPE_RS422_485, // RS422/RS485 ONLY
+ CTI_PORT_TYPE_RS232_422_485_HW, // RS232/422/485 HW ONLY Switchable
+ CTI_PORT_TYPE_RS232_422_485_SW, // RS232/422/485 SW ONLY Switchable
+ CTI_PORT_TYPE_RS232_422_485_4B, // RS232/422/485 HW/SW (4bit ex. BCG004)
+ CTI_PORT_TYPE_RS232_422_485_2B, // RS232/422/485 HW/SW (2bit ex. BBG008)
+ CTI_PORT_TYPE_MAX,
+};
+
+#define CTI_PORT_TYPE_VALID(_port_type) \
+ (((_port_type) > CTI_PORT_TYPE_NONE) && \
+ ((_port_type) < CTI_PORT_TYPE_MAX))
+
+#define CTI_PORT_TYPE_RS485(_port_type) \
+ (((_port_type) > CTI_PORT_TYPE_RS232) && \
+ ((_port_type) < CTI_PORT_TYPE_MAX))
+
struct exar8250;
struct exar8250_platform {
int (*rs485_config)(struct uart_port *port, struct ktermios *termios,
struct serial_rs485 *rs485);
const struct serial_rs485 *rs485_supported;
- int (*register_gpio)(struct pci_dev *, struct uart_8250_port *);
- void (*unregister_gpio)(struct uart_8250_port *);
+ int (*register_gpio)(struct pci_dev *pcidev, struct uart_8250_port *port);
+ void (*unregister_gpio)(struct uart_8250_port *port);
};
/**
* struct exar8250_board - board information
* @num_ports: number of serial ports
* @reg_shift: describes UART register mapping in PCI memory
- * @setup: quirk run at ->probe() stage
+ * @setup: quirk run at ->probe() stage for each port
* @exit: quirk run at ->remove() stage
*/
struct exar8250_board {
unsigned int num_ports;
unsigned int reg_shift;
- int (*setup)(struct exar8250 *, struct pci_dev *,
- struct uart_8250_port *, int);
+ int (*setup)(struct exar8250 *priv, struct pci_dev *pcidev,
+ struct uart_8250_port *port, int idx);
void (*exit)(struct pci_dev *pcidev);
};
struct exar8250 {
unsigned int nr;
+ unsigned int osc_freq;
struct exar8250_board *board;
void __iomem *virt;
int line[];
};
+static inline void exar_write_reg(struct exar8250 *priv,
+ unsigned int reg, u8 value)
+{
+ writeb(value, priv->virt + reg);
+}
+
+static inline u8 exar_read_reg(struct exar8250 *priv, unsigned int reg)
+{
+ return readb(priv->virt + reg);
+}
+
+static inline void exar_ee_select(struct exar8250 *priv)
+{
+ // Set chip select pin high to enable EEPROM reads/writes
+ exar_write_reg(priv, UART_EXAR_REGB, UART_EXAR_REGB_EECS);
+ // Min ~500ns delay needed between CS assert and EEPROM access
+ udelay(1);
+}
+
+static inline void exar_ee_deselect(struct exar8250 *priv)
+{
+ exar_write_reg(priv, UART_EXAR_REGB, 0x00);
+}
+
+static inline void exar_ee_write_bit(struct exar8250 *priv, u8 bit)
+{
+ u8 value = UART_EXAR_REGB_EECS;
+
+ if (bit)
+ value |= UART_EXAR_REGB_EEDI;
+
+ // Clock out the bit on the EEPROM interface
+ exar_write_reg(priv, UART_EXAR_REGB, value);
+ // 2us delay = ~500khz clock speed
+ udelay(2);
+
+ value |= UART_EXAR_REGB_EECK;
+
+ exar_write_reg(priv, UART_EXAR_REGB, value);
+ udelay(2);
+}
+
+static inline u8 exar_ee_read_bit(struct exar8250 *priv)
+{
+ u8 regb;
+ u8 value = UART_EXAR_REGB_EECS;
+
+ // Clock in the bit on the EEPROM interface
+ exar_write_reg(priv, UART_EXAR_REGB, value);
+ // 2us delay = ~500khz clock speed
+ udelay(2);
+
+ value |= UART_EXAR_REGB_EECK;
+
+ exar_write_reg(priv, UART_EXAR_REGB, value);
+ udelay(2);
+
+ regb = exar_read_reg(priv, UART_EXAR_REGB);
+
+ return (regb & UART_EXAR_REGB_EEDO ? 1 : 0);
+}
+
+/**
+ * exar_ee_read() - Read a word from the EEPROM
+ * @priv: Device's private structure
+ * @ee_addr: Offset of EEPROM to read word from
+ *
+ * Read a single 16bit word from an Exar UART's EEPROM.
+ * The type of the EEPROM is AT93C46D.
+ *
+ * Return: EEPROM word
+ */
+static u16 exar_ee_read(struct exar8250 *priv, u8 ee_addr)
+{
+ int i;
+ u16 data = 0;
+
+ exar_ee_select(priv);
+
+ // Send read command (opcode 110)
+ exar_ee_write_bit(priv, 1);
+ exar_ee_write_bit(priv, 1);
+ exar_ee_write_bit(priv, 0);
+
+ // Send address to read from
+ for (i = UART_EXAR_REGB_EE_ADDR_SIZE - 1; i >= 0; i--)
+ exar_ee_write_bit(priv, ee_addr & BIT(i));
+
+ // Read data 1 bit at a time starting with a dummy bit
+ for (i = UART_EXAR_REGB_EE_DATA_SIZE; i >= 0; i--) {
+ if (exar_ee_read_bit(priv))
+ data |= BIT(i);
+ }
+
+ exar_ee_deselect(priv);
+
+ return data;
+}
+
+/**
+ * exar_mpio_config_output() - Configure an Exar MPIO as an output
+ * @priv: Device's private structure
+ * @mpio_num: MPIO number/offset to configure
+ *
+ * Configure a single MPIO as an output and disable tristate. It is reccomended
+ * to set the level with exar_mpio_set_high()/exar_mpio_set_low() prior to
+ * calling this function to ensure default MPIO pin state.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int exar_mpio_config_output(struct exar8250 *priv,
+ unsigned int mpio_num)
+{
+ unsigned int mpio_offset;
+ u8 sel_reg; // MPIO Select register (input/output)
+ u8 tri_reg; // MPIO Tristate register
+ u8 value;
+
+ if (mpio_num < 8) {
+ sel_reg = UART_EXAR_MPIOSEL_7_0;
+ tri_reg = UART_EXAR_MPIO3T_7_0;
+ mpio_offset = mpio_num;
+ } else if (mpio_num >= 8 && mpio_num < 16) {
+ sel_reg = UART_EXAR_MPIOSEL_15_8;
+ tri_reg = UART_EXAR_MPIO3T_15_8;
+ mpio_offset = mpio_num - 8;
+ } else {
+ return -EINVAL;
+ }
+
+ // Disable MPIO pin tri-state
+ value = exar_read_reg(priv, tri_reg);
+ value &= ~BIT(mpio_offset);
+ exar_write_reg(priv, tri_reg, value);
+
+ value = exar_read_reg(priv, sel_reg);
+ value &= ~BIT(mpio_offset);
+ exar_write_reg(priv, sel_reg, value);
+
+ return 0;
+}
+
+/**
+ * _exar_mpio_set() - Set an Exar MPIO output high or low
+ * @priv: Device's private structure
+ * @mpio_num: MPIO number/offset to set
+ * @high: Set MPIO high if true, low if false
+ *
+ * Set a single MPIO high or low. exar_mpio_config_output() must also be called
+ * to configure the pin as an output.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int _exar_mpio_set(struct exar8250 *priv,
+ unsigned int mpio_num, bool high)
+{
+ unsigned int mpio_offset;
+ u8 lvl_reg;
+ u8 value;
+
+ if (mpio_num < 8) {
+ lvl_reg = UART_EXAR_MPIOLVL_7_0;
+ mpio_offset = mpio_num;
+ } else if (mpio_num >= 8 && mpio_num < 16) {
+ lvl_reg = UART_EXAR_MPIOLVL_15_8;
+ mpio_offset = mpio_num - 8;
+ } else {
+ return -EINVAL;
+ }
+
+ value = exar_read_reg(priv, lvl_reg);
+ if (high)
+ value |= BIT(mpio_offset);
+ else
+ value &= ~BIT(mpio_offset);
+ exar_write_reg(priv, lvl_reg, value);
+
+ return 0;
+}
+
+static int exar_mpio_set_low(struct exar8250 *priv, unsigned int mpio_num)
+{
+ return _exar_mpio_set(priv, mpio_num, false);
+}
+
+static int exar_mpio_set_high(struct exar8250 *priv, unsigned int mpio_num)
+{
+ return _exar_mpio_set(priv, mpio_num, true);
+}
+
+static int generic_rs485_config(struct uart_port *port, struct ktermios *termios,
+ struct serial_rs485 *rs485)
+{
+ bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED);
+ u8 __iomem *p = port->membase;
+ u8 value;
+
+ value = readb(p + UART_EXAR_FCTR);
+ if (is_rs485)
+ value |= UART_FCTR_EXAR_485;
+ else
+ value &= ~UART_FCTR_EXAR_485;
+
+ writeb(value, p + UART_EXAR_FCTR);
+
+ if (is_rs485)
+ writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
+
+ return 0;
+}
+
+static const struct serial_rs485 generic_rs485_supported = {
+ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND,
+};
+
static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old)
{
/*
@@ -214,7 +532,7 @@ static void exar_shutdown(struct uart_port *port)
{
bool tx_complete = false;
struct uart_8250_port *up = up_to_u8250p(port);
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
int i = 0;
u16 lsr;
@@ -225,7 +543,8 @@ static void exar_shutdown(struct uart_port *port)
else
tx_complete = false;
usleep_range(1000, 1100);
- } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000);
+ } while (!kfifo_is_empty(&tport->xmit_fifo) &&
+ !tx_complete && i++ < 1000);
serial8250_do_shutdown(port);
}
@@ -289,41 +608,546 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev,
writeb(32, p + UART_EXAR_TXTRG);
writeb(32, p + UART_EXAR_RXTRG);
+ /* Skip the initial (per device) setup */
+ if (idx)
+ return 0;
+
/*
* Setup Multipurpose Input/Output pins.
*/
+ switch (pcidev->device) {
+ case PCI_DEVICE_ID_COMMTECH_4222PCI335:
+ case PCI_DEVICE_ID_COMMTECH_4224PCI335:
+ writeb(0x78, p + UART_EXAR_MPIOLVL_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOINV_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOSEL_7_0);
+ break;
+ case PCI_DEVICE_ID_COMMTECH_2324PCI335:
+ case PCI_DEVICE_ID_COMMTECH_2328PCI335:
+ writeb(0x00, p + UART_EXAR_MPIOLVL_7_0);
+ writeb(0xc0, p + UART_EXAR_MPIOINV_7_0);
+ writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0);
+ break;
+ default:
+ break;
+ }
+ writeb(0x00, p + UART_EXAR_MPIOINT_7_0);
+ writeb(0x00, p + UART_EXAR_MPIO3T_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOOD_7_0);
+
+ return 0;
+}
+
+/**
+ * cti_tristate_disable() - Disable RS485 transciever tristate
+ * @priv: Device's private structure
+ * @port_num: Port number to set tristate off
+ *
+ * Most RS485 capable cards have a power on tristate jumper/switch that ensures
+ * the RS422/RS485 transceiver does not drive a multi-drop RS485 bus when it is
+ * not the master. When this jumper is installed the user must set the RS485
+ * mode to Full or Half duplex to disable tristate prior to using the port.
+ *
+ * Some Exar UARTs have an auto-tristate feature while others require setting
+ * an MPIO to disable the tristate.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int cti_tristate_disable(struct exar8250 *priv, unsigned int port_num)
+{
+ int ret;
+
+ ret = exar_mpio_set_high(priv, port_num);
+ if (ret)
+ return ret;
+
+ return exar_mpio_config_output(priv, port_num);
+}
+
+/**
+ * cti_plx_int_enable() - Enable UART interrupts to PLX bridge
+ * @priv: Device's private structure
+ *
+ * Some older CTI cards require MPIO_0 to be set low to enable the
+ * interrupts from the UART to the PLX PCI->PCIe bridge.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int cti_plx_int_enable(struct exar8250 *priv)
+{
+ int ret;
+
+ ret = exar_mpio_set_low(priv, 0);
+ if (ret)
+ return ret;
+
+ return exar_mpio_config_output(priv, 0);
+}
+
+/**
+ * cti_read_osc_freq() - Read the UART oscillator frequency from EEPROM
+ * @priv: Device's private structure
+ * @eeprom_offset: Offset where the oscillator frequency is stored
+ *
+ * CTI XR17x15X and XR17V25X cards have the serial boards oscillator frequency
+ * stored in the EEPROM. FPGA and XR17V35X based cards use the PCI/PCIe clock.
+ *
+ * Return: frequency on success, negative error code on failure
+ */
+static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset)
+{
+ u16 lower_word;
+ u16 upper_word;
+
+ lower_word = exar_ee_read(priv, eeprom_offset);
+ // Check if EEPROM word was blank
+ if (lower_word == 0xFFFF)
+ return -EIO;
+
+ upper_word = exar_ee_read(priv, (eeprom_offset + 1));
+ if (upper_word == 0xFFFF)
+ return -EIO;
+
+ return FIELD_PREP(CTI_EE_MASK_OSC_FREQ_LOWER, lower_word) |
+ FIELD_PREP(CTI_EE_MASK_OSC_FREQ_UPPER, upper_word);
+}
+
+/**
+ * cti_get_port_type_xr17c15x_xr17v25x() - Get port type of xr17c15x/xr17v25x
+ * @priv: Device's private structure
+ * @pcidev: Pointer to the PCI device for this port
+ * @port_num: Port to get type of
+ *
+ * CTI xr17c15x and xr17v25x based cards port types are based on PCI IDs.
+ *
+ * Return: port type on success, CTI_PORT_TYPE_NONE on failure
+ */
+static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ unsigned int port_num)
+{
+ switch (pcidev->subsystem_device) {
+ // RS232 only cards
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS:
+ return CTI_PORT_TYPE_RS232;
+ // 1x RS232, 1x RS422/RS485
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1:
+ return (port_num == 0) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485;
+ // 2x RS232, 2x RS422/RS485
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2:
+ return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485;
+ // 4x RS232, 4x RS422/RS485
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP:
+ return (port_num < 4) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485;
+ // RS232/RS422/RS485 HW (jumper) selectable
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP:
+ return CTI_PORT_TYPE_RS232_422_485_HW;
+ // RS422/RS485 HW (jumper) selectable
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485:
+ return CTI_PORT_TYPE_RS422_485;
+ // 6x RS232, 2x RS422/RS485
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP:
+ return (port_num < 6) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485;
+ // 2x RS232, 6x RS422/RS485
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP:
+ return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485;
+ default:
+ dev_err(&pcidev->dev, "unknown/unsupported device\n");
+ return CTI_PORT_TYPE_NONE;
+ }
+}
+
+/**
+ * cti_get_port_type_fpga() - Get the port type of a CTI FPGA card
+ * @priv: Device's private structure
+ * @pcidev: Pointer to the PCI device for this port
+ * @port_num: Port to get type of
+ *
+ * FPGA based cards port types are based on PCI IDs.
+ *
+ * Return: port type on success, CTI_PORT_TYPE_NONE on failure
+ */
+static enum cti_port_type cti_get_port_type_fpga(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ unsigned int port_num)
+{
+ switch (pcidev->device) {
+ case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X:
+ case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X:
+ case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16:
+ return CTI_PORT_TYPE_RS232_422_485_HW;
+ default:
+ dev_err(&pcidev->dev, "unknown/unsupported device\n");
+ return CTI_PORT_TYPE_NONE;
+ }
+}
+
+/**
+ * cti_get_port_type_xr17v35x() - Read port type from the EEPROM
+ * @priv: Device's private structure
+ * @pcidev: Pointer to the PCI device for this port
+ * @port_num: port offset
+ *
+ * CTI XR17V35X based cards have the port types stored in the EEPROM.
+ * This function reads the port type for a single port.
+ *
+ * Return: port type on success, CTI_PORT_TYPE_NONE on failure
+ */
+static enum cti_port_type cti_get_port_type_xr17v35x(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ unsigned int port_num)
+{
+ enum cti_port_type port_type;
+ u16 port_flags;
+ u8 offset;
+
+ offset = CTI_EE_OFF_XR17V35X_PORT_FLAGS + port_num;
+ port_flags = exar_ee_read(priv, offset);
+
+ port_type = FIELD_GET(CTI_EE_MASK_PORT_FLAGS_TYPE, port_flags);
+ if (CTI_PORT_TYPE_VALID(port_type))
+ return port_type;
+
+ /*
+ * If the port type is missing the card assume it is a
+ * RS232/RS422/RS485 card to be safe.
+ *
+ * There is one known board (BEG013) that only has 3 of 4 port types
+ * written to the EEPROM so this acts as a work around.
+ */
+ dev_warn(&pcidev->dev, "failed to get port %d type from EEPROM\n", port_num);
+
+ return CTI_PORT_TYPE_RS232_422_485_HW;
+}
+
+static int cti_rs485_config_mpio_tristate(struct uart_port *port,
+ struct ktermios *termios,
+ struct serial_rs485 *rs485)
+{
+ struct exar8250 *priv = (struct exar8250 *)port->private_data;
+ int ret;
+
+ ret = generic_rs485_config(port, termios, rs485);
+ if (ret)
+ return ret;
+
+ // Disable power-on RS485 tri-state via MPIO
+ return cti_tristate_disable(priv, port->port_id);
+}
+
+static void cti_board_init_osc_freq(struct exar8250 *priv, struct pci_dev *pcidev, u8 eeprom_offset)
+{
+ int osc_freq;
+
+ osc_freq = cti_read_osc_freq(priv, eeprom_offset);
+ if (osc_freq <= 0) {
+ dev_warn(&pcidev->dev, "failed to read OSC freq from EEPROM, using default\n");
+ osc_freq = CTI_DEFAULT_PCI_OSC_FREQ;
+ }
+
+ priv->osc_freq = osc_freq;
+}
+
+static int cti_port_setup_common(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ int idx, unsigned int offset,
+ struct uart_8250_port *port)
+{
+ int ret;
+
+ port->port.port_id = idx;
+ port->port.uartclk = priv->osc_freq;
+
+ ret = serial8250_pci_setup_port(pcidev, port, 0, offset, 0);
+ if (ret)
+ return ret;
+
+ port->port.private_data = (void *)priv;
+ port->port.pm = exar_pm;
+ port->port.shutdown = exar_shutdown;
+
+ return 0;
+}
+
+static int cti_board_init_fpga(struct exar8250 *priv, struct pci_dev *pcidev)
+{
+ int ret;
+ u16 cfg_val;
+
+ // FPGA OSC is fixed to the 33MHz PCI clock
+ priv->osc_freq = CTI_DEFAULT_FPGA_OSC_FREQ;
+
+ // Enable external interrupts in special cfg space register
+ ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT;
+ ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ // RS485 gate needs to be enabled; otherwise RTS/CTS will not work
+ exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01);
+
+ return 0;
+}
+
+static int cti_port_setup_fpga(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ struct uart_8250_port *port,
+ int idx)
+{
+ enum cti_port_type port_type;
+ unsigned int offset;
+ int ret;
+
if (idx == 0) {
- switch (pcidev->device) {
- case PCI_DEVICE_ID_COMMTECH_4222PCI335:
- case PCI_DEVICE_ID_COMMTECH_4224PCI335:
- writeb(0x78, p + UART_EXAR_MPIOLVL_7_0);
- writeb(0x00, p + UART_EXAR_MPIOINV_7_0);
- writeb(0x00, p + UART_EXAR_MPIOSEL_7_0);
+ ret = cti_board_init_fpga(priv, pcidev);
+ if (ret)
+ return ret;
+ }
+
+ port_type = cti_get_port_type_fpga(priv, pcidev, idx);
+
+ // FPGA shares port offsets with XR17C15X
+ offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET;
+ port->port.type = PORT_XR17D15X;
+
+ port->port.get_divisor = xr17v35x_get_divisor;
+ port->port.set_divisor = xr17v35x_set_divisor;
+ port->port.startup = xr17v35x_startup;
+
+ if (CTI_PORT_TYPE_RS485(port_type)) {
+ port->port.rs485_config = generic_rs485_config;
+ port->port.rs485_supported = generic_rs485_supported;
+ }
+
+ return cti_port_setup_common(priv, pcidev, idx, offset, port);
+}
+
+static void cti_board_init_xr17v35x(struct exar8250 *priv, struct pci_dev *pcidev)
+{
+ // XR17V35X uses the PCIe clock rather than an oscillator
+ priv->osc_freq = CTI_DEFAULT_PCIE_OSC_FREQ;
+}
+
+static int cti_port_setup_xr17v35x(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ struct uart_8250_port *port,
+ int idx)
+{
+ enum cti_port_type port_type;
+ unsigned int offset;
+ int ret;
+
+ if (idx == 0)
+ cti_board_init_xr17v35x(priv, pcidev);
+
+ port_type = cti_get_port_type_xr17v35x(priv, pcidev, idx);
+
+ offset = idx * UART_EXAR_XR17V35X_PORT_OFFSET;
+ port->port.type = PORT_XR17V35X;
+
+ port->port.get_divisor = xr17v35x_get_divisor;
+ port->port.set_divisor = xr17v35x_set_divisor;
+ port->port.startup = xr17v35x_startup;
+
+ switch (port_type) {
+ case CTI_PORT_TYPE_RS422_485:
+ case CTI_PORT_TYPE_RS232_422_485_HW:
+ port->port.rs485_config = cti_rs485_config_mpio_tristate;
+ port->port.rs485_supported = generic_rs485_supported;
+ break;
+ case CTI_PORT_TYPE_RS232_422_485_SW:
+ case CTI_PORT_TYPE_RS232_422_485_4B:
+ case CTI_PORT_TYPE_RS232_422_485_2B:
+ port->port.rs485_config = generic_rs485_config;
+ port->port.rs485_supported = generic_rs485_supported;
+ break;
+ default:
+ break;
+ }
+
+ ret = cti_port_setup_common(priv, pcidev, idx, offset, port);
+ if (ret)
+ return ret;
+
+ exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00);
+ exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD);
+ exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 128);
+ exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 128);
+
+ return 0;
+}
+
+static void cti_board_init_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev)
+{
+ cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17V25X_OSC_FREQ);
+
+ /* enable interrupts on cards that need the "PLX fix" */
+ switch (pcidev->subsystem_device) {
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B:
+ cti_plx_int_enable(priv);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cti_port_setup_xr17v25x(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ struct uart_8250_port *port,
+ int idx)
+{
+ enum cti_port_type port_type;
+ unsigned int offset;
+ int ret;
+
+ if (idx == 0)
+ cti_board_init_xr17v25x(priv, pcidev);
+
+ port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx);
+
+ offset = idx * UART_EXAR_XR17V25X_PORT_OFFSET;
+ port->port.type = PORT_XR17D15X;
+
+ // XR17V25X supports fractional baudrates
+ port->port.get_divisor = xr17v35x_get_divisor;
+ port->port.set_divisor = xr17v35x_set_divisor;
+ port->port.startup = xr17v35x_startup;
+
+ if (CTI_PORT_TYPE_RS485(port_type)) {
+ switch (pcidev->subsystem_device) {
+ // These cards support power on 485 tri-state via MPIO
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485:
+ port->port.rs485_config = cti_rs485_config_mpio_tristate;
break;
- case PCI_DEVICE_ID_COMMTECH_2324PCI335:
- case PCI_DEVICE_ID_COMMTECH_2328PCI335:
- writeb(0x00, p + UART_EXAR_MPIOLVL_7_0);
- writeb(0xc0, p + UART_EXAR_MPIOINV_7_0);
- writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0);
+ // Otherwise auto or no power on 485 tri-state support
+ default:
+ port->port.rs485_config = generic_rs485_config;
break;
}
- writeb(0x00, p + UART_EXAR_MPIOINT_7_0);
- writeb(0x00, p + UART_EXAR_MPIO3T_7_0);
- writeb(0x00, p + UART_EXAR_MPIOOD_7_0);
+
+ port->port.rs485_supported = generic_rs485_supported;
}
+ ret = cti_port_setup_common(priv, pcidev, idx, offset, port);
+ if (ret)
+ return ret;
+
+ exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00);
+ exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD);
+ exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 32);
+ exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 32);
+
return 0;
}
-static int
-pci_connect_tech_setup(struct exar8250 *priv, struct pci_dev *pcidev,
- struct uart_8250_port *port, int idx)
+static void cti_board_init_xr17c15x(struct exar8250 *priv, struct pci_dev *pcidev)
{
- unsigned int offset = idx * 0x200;
- unsigned int baud = 1843200;
+ cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17C15X_OSC_FREQ);
+
+ /* enable interrupts on cards that need the "PLX fix" */
+ switch (pcidev->subsystem_device) {
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B:
+ cti_plx_int_enable(priv);
+ break;
+ default:
+ break;
+ }
+}
- port->port.uartclk = baud * 16;
- return default_setup(priv, pcidev, idx, offset, port);
+static int cti_port_setup_xr17c15x(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ struct uart_8250_port *port,
+ int idx)
+{
+ enum cti_port_type port_type;
+ unsigned int offset;
+
+ if (idx == 0)
+ cti_board_init_xr17c15x(priv, pcidev);
+
+ port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx);
+
+ offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET;
+ port->port.type = PORT_XR17D15X;
+
+ if (CTI_PORT_TYPE_RS485(port_type)) {
+ switch (pcidev->subsystem_device) {
+ // These cards support power on 485 tri-state via MPIO
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP:
+ case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485:
+ port->port.rs485_config = cti_rs485_config_mpio_tristate;
+ break;
+ // Otherwise auto or no power on 485 tri-state support
+ default:
+ port->port.rs485_config = generic_rs485_config;
+ break;
+ }
+
+ port->port.rs485_supported = generic_rs485_supported;
+ }
+
+ return cti_port_setup_common(priv, pcidev, idx, offset, port);
}
static int
@@ -344,11 +1168,10 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p)
* devices will export them as GPIOs, so we pre-configure them safely
* as inputs.
*/
-
u8 dir = 0x00;
if ((pcidev->vendor == PCI_VENDOR_ID_EXAR) &&
- (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) {
+ (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) {
// Configure GPIO as inputs for Commtech adapters
dir = 0xff;
} else {
@@ -425,27 +1248,6 @@ static void xr17v35x_unregister_gpio(struct uart_8250_port *port)
port->port.private_data = NULL;
}
-static int generic_rs485_config(struct uart_port *port, struct ktermios *termios,
- struct serial_rs485 *rs485)
-{
- bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED);
- u8 __iomem *p = port->membase;
- u8 value;
-
- value = readb(p + UART_EXAR_FCTR);
- if (is_rs485)
- value |= UART_FCTR_EXAR_485;
- else
- value &= ~UART_FCTR_EXAR_485;
-
- writeb(value, p + UART_EXAR_FCTR);
-
- if (is_rs485)
- writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
-
- return 0;
-}
-
static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termios,
struct serial_rs485 *rs485)
{
@@ -459,35 +1261,32 @@ static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termio
if (ret)
return ret;
- if (rs485->flags & SER_RS485_ENABLED) {
- old_lcr = readb(p + UART_LCR);
+ if (!(rs485->flags & SER_RS485_ENABLED))
+ return 0;
- /* Set EFR[4]=1 to enable enhanced feature registers */
- efr = readb(p + UART_XR_EFR);
- efr |= UART_EFR_ECB;
- writeb(efr, p + UART_XR_EFR);
+ old_lcr = readb(p + UART_LCR);
- /* Set MCR to use DTR as Auto-RS485 Enable signal */
- writeb(UART_MCR_OUT1, p + UART_MCR);
+ /* Set EFR[4]=1 to enable enhanced feature registers */
+ efr = readb(p + UART_XR_EFR);
+ efr |= UART_EFR_ECB;
+ writeb(efr, p + UART_XR_EFR);
- /* Set LCR[7]=1 to enable access to DLD register */
- writeb(old_lcr | UART_LCR_DLAB, p + UART_LCR);
+ /* Set MCR to use DTR as Auto-RS485 Enable signal */
+ writeb(UART_MCR_OUT1, p + UART_MCR);
- /* Set DLD[7]=1 for inverted RS485 Enable logic */
- dld = readb(p + UART_EXAR_DLD);
- dld |= UART_EXAR_DLD_485_POLARITY;
- writeb(dld, p + UART_EXAR_DLD);
+ /* Set LCR[7]=1 to enable access to DLD register */
+ writeb(old_lcr | UART_LCR_DLAB, p + UART_LCR);
- writeb(old_lcr, p + UART_LCR);
- }
+ /* Set DLD[7]=1 for inverted RS485 Enable logic */
+ dld = readb(p + UART_EXAR_DLD);
+ dld |= UART_EXAR_DLD_485_POLARITY;
+ writeb(dld, p + UART_EXAR_DLD);
+
+ writeb(old_lcr, p + UART_LCR);
return 0;
}
-static const struct serial_rs485 generic_rs485_supported = {
- .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND,
-};
-
static const struct exar8250_platform exar8250_default_platform = {
.register_gpio = xr17v35x_register_gpio,
.unregister_gpio = xr17v35x_unregister_gpio,
@@ -672,6 +1471,35 @@ static irqreturn_t exar_misc_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static unsigned int exar_get_nr_ports(struct exar8250_board *board, struct pci_dev *pcidev)
+{
+ if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO)
+ return BIT(((pcidev->device & 0x38) >> 3) - 1);
+
+ // Check if board struct overrides number of ports
+ if (board->num_ports > 0)
+ return board->num_ports;
+
+ // Exar encodes # ports in last nibble of PCI Device ID ex. 0358
+ if (pcidev->vendor == PCI_VENDOR_ID_EXAR)
+ return pcidev->device & 0x0f;
+
+ // Handle CTI FPGA cards
+ if (pcidev->vendor == PCI_VENDOR_ID_CONNECT_TECH) {
+ switch (pcidev->device) {
+ case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X:
+ case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X:
+ return 12;
+ case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16:
+ return 16;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
static int
exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
{
@@ -691,12 +1519,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3);
- if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO)
- nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1);
- else if (board->num_ports)
- nr_ports = board->num_ports;
- else
- nr_ports = pcidev->device & 0x0f;
+ nr_ports = exar_get_nr_ports(board, pcidev);
+ if (nr_ports == 0)
+ return dev_err_probe(&pcidev->dev, -ENODEV, "failed to get number of ports\n");
priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL);
if (!priv)
@@ -729,7 +1554,7 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
for (i = 0; i < nr_ports && i < maxnr; i++) {
rc = board->setup(priv, pcidev, &uart, i);
if (rc) {
- dev_err(&pcidev->dev, "Failed to setup port %u\n", i);
+ dev_err_probe(&pcidev->dev, rc, "Failed to setup port %u\n", i);
break;
}
@@ -738,10 +1563,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
priv->line[i] = serial8250_register_8250_port(&uart);
if (priv->line[i] < 0) {
- dev_err(&pcidev->dev,
- "Couldn't register serial port %lx, irq %d, type %d, error %d\n",
- uart.port.iobase, uart.port.irq,
- uart.port.iotype, priv->line[i]);
+ dev_err_probe(&pcidev->dev, priv->line[i],
+ "Couldn't register serial port %lx, type %d, irq %d\n",
+ uart.port.iobase, uart.port.iotype, uart.port.irq);
break;
}
}
@@ -806,8 +1630,20 @@ static const struct exar8250_board pbn_fastcom335_8 = {
.setup = pci_fastcom335_setup,
};
-static const struct exar8250_board pbn_connect = {
- .setup = pci_connect_tech_setup,
+static const struct exar8250_board pbn_cti_xr17c15x = {
+ .setup = cti_port_setup_xr17c15x,
+};
+
+static const struct exar8250_board pbn_cti_xr17v25x = {
+ .setup = cti_port_setup_xr17v25x,
+};
+
+static const struct exar8250_board pbn_cti_xr17v35x = {
+ .setup = cti_port_setup_xr17v35x,
+};
+
+static const struct exar8250_board pbn_cti_fpga = {
+ .setup = cti_port_setup_fpga,
};
static const struct exar8250_board pbn_exar_ibm_saturn = {
@@ -854,13 +1690,13 @@ static const struct exar8250_board pbn_exar_XR17V8358 = {
.exit = pci_xr17v35x_exit,
};
-#define CONNECT_DEVICE(devid, sdevid, bd) { \
- PCI_DEVICE_SUB( \
- PCI_VENDOR_ID_EXAR, \
- PCI_DEVICE_ID_EXAR_##devid, \
- PCI_SUBVENDOR_ID_CONNECT_TECH, \
- PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_##sdevid), 0, 0, \
- (kernel_ulong_t)&bd \
+#define CTI_EXAR_DEVICE(devid, bd) { \
+ PCI_DEVICE_SUB( \
+ PCI_VENDOR_ID_EXAR, \
+ PCI_DEVICE_ID_EXAR_##devid, \
+ PCI_SUBVENDOR_ID_CONNECT_TECH, \
+ PCI_ANY_ID), 0, 0, \
+ (kernel_ulong_t)&bd \
}
#define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) }
@@ -869,7 +1705,7 @@ static const struct exar8250_board pbn_exar_XR17V8358 = {
PCI_DEVICE_SUB( \
PCI_VENDOR_ID_EXAR, \
PCI_DEVICE_ID_EXAR_##devid, \
- PCI_VENDOR_ID_IBM, \
+ PCI_SUBVENDOR_ID_IBM, \
PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \
(kernel_ulong_t)&bd \
}
@@ -892,18 +1728,23 @@ static const struct pci_device_id exar_pci_tbl[] = {
EXAR_DEVICE(ACCESSIO, COM_4SM, pbn_exar_XR17C15x),
EXAR_DEVICE(ACCESSIO, COM_8SM, pbn_exar_XR17C15x),
- CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect),
- CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect),
- CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect),
- CONNECT_DEVICE(XR17C152, UART_1_1, pbn_connect),
- CONNECT_DEVICE(XR17C154, UART_2_2, pbn_connect),
- CONNECT_DEVICE(XR17C158, UART_4_4, pbn_connect),
- CONNECT_DEVICE(XR17C152, UART_2, pbn_connect),
- CONNECT_DEVICE(XR17C154, UART_4, pbn_connect),
- CONNECT_DEVICE(XR17C158, UART_8, pbn_connect),
- CONNECT_DEVICE(XR17C152, UART_2_485, pbn_connect),
- CONNECT_DEVICE(XR17C154, UART_4_485, pbn_connect),
- CONNECT_DEVICE(XR17C158, UART_8_485, pbn_connect),
+ /* Connect Tech cards with Exar vendor/device PCI IDs */
+ CTI_EXAR_DEVICE(XR17C152, pbn_cti_xr17c15x),
+ CTI_EXAR_DEVICE(XR17C154, pbn_cti_xr17c15x),
+ CTI_EXAR_DEVICE(XR17C158, pbn_cti_xr17c15x),
+
+ CTI_EXAR_DEVICE(XR17V252, pbn_cti_xr17v25x),
+ CTI_EXAR_DEVICE(XR17V254, pbn_cti_xr17v25x),
+ CTI_EXAR_DEVICE(XR17V258, pbn_cti_xr17v25x),
+
+ CTI_EXAR_DEVICE(XR17V352, pbn_cti_xr17v35x),
+ CTI_EXAR_DEVICE(XR17V354, pbn_cti_xr17v35x),
+ CTI_EXAR_DEVICE(XR17V358, pbn_cti_xr17v35x),
+
+ /* Connect Tech cards with Connect Tech vendor/device PCI IDs (FPGA based) */
+ EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG00X, pbn_cti_fpga),
+ EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG01X, pbn_cti_fpga),
+ EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_16, pbn_cti_fpga),
IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn),
diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c
index 7984ee05af1d..47e1a056a60c 100644
--- a/drivers/tty/serial/8250/8250_lpc18xx.c
+++ b/drivers/tty/serial/8250/8250_lpc18xx.c
@@ -151,7 +151,7 @@ static int lpc18xx_serial_probe(struct platform_device *pdev)
ret = uart_read_port_properties(&uart.port);
if (ret)
- return ret;
+ goto dis_uart_clk;
uart.port.iotype = UPIO_MEM32;
uart.port.regshift = 2;
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index 9ff6bbe9c086..b9cca210e171 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -199,7 +199,7 @@ static int mtk8250_startup(struct uart_port *port)
if (up->dma) {
data->rx_status = DMA_RX_START;
- uart_circ_clear(&port->state->xmit);
+ kfifo_reset(&port->state->port.xmit_fifo);
}
#endif
memset(&port->icount, 0, sizeof(port->icount));
@@ -209,15 +209,19 @@ static int mtk8250_startup(struct uart_port *port)
static void mtk8250_shutdown(struct uart_port *port)
{
-#ifdef CONFIG_SERIAL_8250_DMA
struct uart_8250_port *up = up_to_u8250p(port);
struct mtk8250_data *data = port->private_data;
+ int irq = data->rx_wakeup_irq;
+#ifdef CONFIG_SERIAL_8250_DMA
if (up->dma)
data->rx_status = DMA_RX_SHUTDOWN;
#endif
- return serial8250_do_shutdown(port);
+ serial8250_do_shutdown(port);
+
+ if (irq >= 0)
+ serial8250_do_set_mctrl(&up->port, TIOCM_RTS);
}
static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 5d1dd992d8fb..e14f47ef1172 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -18,6 +18,7 @@
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/reset.h>
+#include <linux/notifier.h>
#include "8250.h"
@@ -26,6 +27,7 @@ struct of_serial_info {
struct reset_control *rst;
int type;
int line;
+ struct notifier_block clk_notifier;
};
/* Nuvoton NPCM timeout register */
@@ -58,6 +60,26 @@ static int npcm_setup(struct uart_port *port)
return 0;
}
+static inline struct of_serial_info *clk_nb_to_info(struct notifier_block *nb)
+{
+ return container_of(nb, struct of_serial_info, clk_notifier);
+}
+
+static int of_platform_serial_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct of_serial_info *info = clk_nb_to_info(nb);
+ struct uart_8250_port *port8250 = serial8250_get_port(info->line);
+ struct clk_notifier_data *ndata = data;
+
+ if (event == POST_RATE_CHANGE) {
+ serial8250_update_uartclk(&port8250->port, ndata->new_rate);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
/*
* Fill a struct uart_port for a given device node
*/
@@ -218,7 +240,19 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
info->type = port_type;
info->line = ret;
platform_set_drvdata(ofdev, info);
+
+ if (info->clk) {
+ info->clk_notifier.notifier_call = of_platform_serial_clk_notifier_cb;
+ ret = clk_notifier_register(info->clk, &info->clk_notifier);
+ if (ret) {
+ dev_err_probe(port8250.port.dev, ret, "Failed to set the clock notifier\n");
+ goto err_unregister;
+ }
+ }
+
return 0;
+err_unregister:
+ serial8250_unregister_port(info->line);
err_dispose:
pm_runtime_put_sync(&ofdev->dev);
pm_runtime_disable(&ofdev->dev);
@@ -234,6 +268,9 @@ static void of_platform_serial_remove(struct platform_device *ofdev)
{
struct of_serial_info *info = platform_get_drvdata(ofdev);
+ if (info->clk)
+ clk_notifier_unregister(info->clk, &info->clk_notifier);
+
serial8250_unregister_port(info->line);
reset_control_assert(info->rst);
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 66901d93089a..170639d12b2a 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -19,7 +19,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
@@ -1094,7 +1093,7 @@ static void omap_8250_dma_tx_complete(void *param)
{
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
unsigned long flags;
bool en_thri = false;
struct omap8250_priv *priv = p->port.private_data;
@@ -1113,10 +1112,10 @@ static void omap_8250_dma_tx_complete(void *param)
omap8250_restore_regs(p);
}
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);
- if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
+ if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&p->port)) {
int ret;
ret = omap_8250_tx_dma(p);
@@ -1138,14 +1137,15 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
struct omap8250_priv *priv = p->port.private_data;
- struct circ_buf *xmit = &p->port.state->xmit;
+ struct tty_port *tport = &p->port.state->port;
struct dma_async_tx_descriptor *desc;
- unsigned int skip_byte = 0;
+ struct scatterlist sg;
+ int skip_byte = -1;
int ret;
if (dma->tx_running)
return 0;
- if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
+ if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {
/*
* Even if no data, we need to return an error for the two cases
@@ -1160,8 +1160,18 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
return 0;
}
- dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ sg_init_table(&sg, 1);
+ ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
+ UART_XMIT_SIZE, dma->tx_addr);
+ if (ret != 1) {
+ serial8250_clear_THRI(p);
+ return 0;
+ }
+
+ dma->tx_size = sg_dma_len(&sg);
+
if (priv->habit & OMAP_DMA_TX_KICK) {
+ unsigned char c;
u8 tx_lvl;
/*
@@ -1188,12 +1198,17 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
ret = -EINVAL;
goto err;
}
- skip_byte = 1;
+ if (!kfifo_get(&tport->xmit_fifo, &c)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ skip_byte = c;
+ /* now we need to recompute due to kfifo_get */
+ kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
+ UART_XMIT_SIZE, dma->tx_addr);
}
- desc = dmaengine_prep_slave_single(dma->txchan,
- dma->tx_addr + xmit->tail + skip_byte,
- dma->tx_size - skip_byte, DMA_MEM_TO_DEV,
+ desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
ret = -EBUSY;
@@ -1215,11 +1230,13 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
dma->tx_err = 0;
serial8250_clear_THRI(p);
- if (skip_byte)
- serial_out(p, UART_TX, xmit->buf[xmit->tail]);
- return 0;
+ ret = 0;
+ goto out_skip;
err:
dma->tx_err = 1;
+out_skip:
+ if (skip_byte >= 0)
+ serial_out(p, UART_TX, skip_byte);
return ret;
}
@@ -1308,7 +1325,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
serial8250_modem_status(up);
if (status & UART_LSR_THRE && up->dma->tx_err) {
if (uart_tx_stopped(&up->port) ||
- uart_circ_empty(&up->port.state->xmit)) {
+ kfifo_is_empty(&up->port.state->port.xmit_fifo)) {
up->dma->tx_err = 0;
serial8250_tx_chars(up);
} else {
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 0d35c77fad9e..40af74b55933 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -4108,7 +4108,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES);
} else {
pci_dbg(dev, "Using legacy interrupts\n");
- rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY);
+ rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX);
}
if (rc < 0) {
kfree(priv);
@@ -5010,12 +5010,6 @@ static const struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_115200 },
- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_bt_2_115200 },
- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_b0_bt_2_115200 },
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_4_460800 },
diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c
index 2fbb5851f788..d3930bf32fe4 100644
--- a/drivers/tty/serial/8250/8250_pci1xxxx.c
+++ b/drivers/tty/serial/8250/8250_pci1xxxx.c
@@ -382,10 +382,10 @@ static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status)
}
static void pci1xxxx_process_write_data(struct uart_port *port,
- struct circ_buf *xmit,
int *data_empty_count,
u32 *valid_byte_count)
{
+ struct tty_port *tport = &port->state->port;
u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE;
/*
@@ -395,41 +395,36 @@ static void pci1xxxx_process_write_data(struct uart_port *port,
* one byte at a time.
*/
while (valid_burst_count) {
+ u32 c;
+
if (*data_empty_count - UART_BURST_SIZE < 0)
break;
- if (xmit->tail > (UART_XMIT_SIZE - UART_BURST_SIZE))
+ if (kfifo_len(&tport->xmit_fifo) < UART_BURST_SIZE)
+ break;
+ if (WARN_ON(kfifo_out(&tport->xmit_fifo, (u8 *)&c, sizeof(c)) !=
+ sizeof(c)))
break;
- writel(*(unsigned int *)&xmit->buf[xmit->tail],
- port->membase + UART_TX_BURST_FIFO);
+ writel(c, port->membase + UART_TX_BURST_FIFO);
*valid_byte_count -= UART_BURST_SIZE;
*data_empty_count -= UART_BURST_SIZE;
valid_burst_count -= UART_BYTE_SIZE;
-
- xmit->tail = (xmit->tail + UART_BURST_SIZE) &
- (UART_XMIT_SIZE - 1);
}
while (*valid_byte_count) {
- if (*data_empty_count - UART_BYTE_SIZE < 0)
+ u8 c;
+
+ if (!kfifo_get(&tport->xmit_fifo, &c))
break;
- writeb(xmit->buf[xmit->tail], port->membase +
- UART_TX_BYTE_FIFO);
+ writeb(c, port->membase + UART_TX_BYTE_FIFO);
*data_empty_count -= UART_BYTE_SIZE;
*valid_byte_count -= UART_BYTE_SIZE;
/*
- * When the tail of the circular buffer is reached, the next
- * byte is transferred to the beginning of the buffer.
- */
- xmit->tail = (xmit->tail + UART_BYTE_SIZE) &
- (UART_XMIT_SIZE - 1);
-
- /*
* If there are any pending burst count, data is handled by
* transmitting DWORDs at a time.
*/
- if (valid_burst_count && (xmit->tail <
- (UART_XMIT_SIZE - UART_BURST_SIZE)))
+ if (valid_burst_count &&
+ kfifo_len(&tport->xmit_fifo) >= UART_BURST_SIZE)
break;
}
}
@@ -437,11 +432,9 @@ static void pci1xxxx_process_write_data(struct uart_port *port,
static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
{
struct uart_8250_port *up = up_to_u8250p(port);
+ struct tty_port *tport = &port->state->port;
u32 valid_byte_count;
int data_empty_count;
- struct circ_buf *xmit;
-
- xmit = &port->state->xmit;
if (port->x_char) {
writeb(port->x_char, port->membase + UART_TX);
@@ -450,25 +443,25 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
return;
}
- if ((uart_tx_stopped(port)) || (uart_circ_empty(xmit))) {
+ if ((uart_tx_stopped(port)) || kfifo_is_empty(&tport->xmit_fifo)) {
port->ops->stop_tx(port);
} else {
data_empty_count = (pci1xxxx_read_burst_status(port) &
UART_BST_STAT_TX_COUNT_MASK) >> 8;
do {
- valid_byte_count = uart_circ_chars_pending(xmit);
+ valid_byte_count = kfifo_len(&tport->xmit_fifo);
- pci1xxxx_process_write_data(port, xmit,
+ pci1xxxx_process_write_data(port,
&data_empty_count,
&valid_byte_count);
port->icount.tx++;
- if (uart_circ_empty(xmit))
+ if (kfifo_is_empty(&tport->xmit_fifo))
break;
} while (data_empty_count && valid_byte_count);
}
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
/*
@@ -476,7 +469,8 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
* the HW can go idle. So we get here once again with empty FIFO and
* disable the interrupt and RPM in __stop_tx()
*/
- if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
+ if (kfifo_is_empty(&tport->xmit_fifo) &&
+ !(up->capabilities & UART_CAP_RPM))
port->ops->stop_tx(port);
}
diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c
index 1974bbadc975..7c06ae79d8e2 100644
--- a/drivers/tty/serial/8250/8250_pnp.c
+++ b/drivers/tty/serial/8250/8250_pnp.c
@@ -10,6 +10,7 @@
*/
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pm.h>
#include <linux/pnp.h>
#include <linux/string.h>
#include <linux/kernel.h>
@@ -434,7 +435,9 @@ static int
serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
struct uart_8250_port uart, *port;
- int ret, line, flags = dev_id->driver_data;
+ int ret, flags = dev_id->driver_data;
+ unsigned char iotype;
+ long line;
if (flags & UNKNOWN_DEV) {
ret = serial_pnp_guess_board(dev);
@@ -443,37 +446,46 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
}
memset(&uart, 0, sizeof(uart));
- if (pnp_irq_valid(dev, 0))
- uart.port.irq = pnp_irq(dev, 0);
if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) {
uart.port.iobase = pnp_port_start(dev, 2);
- uart.port.iotype = UPIO_PORT;
+ iotype = UPIO_PORT;
} else if (pnp_port_valid(dev, 0)) {
uart.port.iobase = pnp_port_start(dev, 0);
- uart.port.iotype = UPIO_PORT;
+ iotype = UPIO_PORT;
} else if (pnp_mem_valid(dev, 0)) {
uart.port.mapbase = pnp_mem_start(dev, 0);
- uart.port.iotype = UPIO_MEM;
+ uart.port.mapsize = pnp_mem_len(dev, 0);
+ iotype = UPIO_MEM;
uart.port.flags = UPF_IOREMAP;
} else
return -ENODEV;
- dev_dbg(&dev->dev,
- "Setup PNP port: port %#lx, mem %#llx, irq %u, type %u\n",
- uart.port.iobase, (unsigned long long)uart.port.mapbase,
- uart.port.irq, uart.port.iotype);
+ uart.port.uartclk = 1843200;
+ uart.port.dev = &dev->dev;
+ uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+
+ ret = uart_read_port_properties(&uart.port);
+ /* no interrupt -> fall back to polling */
+ if (ret == -ENXIO)
+ ret = 0;
+ if (ret)
+ return ret;
+
+ /*
+ * The previous call may not set iotype correctly when reg-io-width
+ * property is absent and it doesn't support IO port resource.
+ */
+ uart.port.iotype = iotype;
if (flags & CIR_PORT) {
uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
uart.port.type = PORT_8250_CIR;
}
- uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
- if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
- uart.port.flags |= UPF_SHARE_IRQ;
- uart.port.uartclk = 1843200;
- device_property_read_u32(&dev->dev, "clock-frequency", &uart.port.uartclk);
- uart.port.dev = &dev->dev;
+ dev_dbg(&dev->dev,
+ "Setup PNP port: port %#lx, mem %#llx, size %#llx, irq %u, type %u\n",
+ uart.port.iobase, (unsigned long long)uart.port.mapbase,
+ (unsigned long long)uart.port.mapsize, uart.port.irq, uart.port.iotype);
line = serial8250_register_8250_port(&uart);
if (line < 0 || (flags & CIR_PORT))
@@ -483,7 +495,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
if (uart_console(&port->port))
dev->capabilities |= PNP_CONSOLE;
- pnp_set_drvdata(dev, (void *)((long)line + 1));
+ pnp_set_drvdata(dev, (void *)line);
return 0;
}
@@ -492,38 +504,33 @@ static void serial_pnp_remove(struct pnp_dev *dev)
long line = (long)pnp_get_drvdata(dev);
dev->capabilities &= ~PNP_CONSOLE;
- if (line)
- serial8250_unregister_port(line - 1);
+ serial8250_unregister_port(line);
}
-static int __maybe_unused serial_pnp_suspend(struct device *dev)
+static int serial_pnp_suspend(struct device *dev)
{
long line = (long)dev_get_drvdata(dev);
- if (!line)
- return -ENODEV;
- serial8250_suspend_port(line - 1);
+ serial8250_suspend_port(line);
return 0;
}
-static int __maybe_unused serial_pnp_resume(struct device *dev)
+static int serial_pnp_resume(struct device *dev)
{
long line = (long)dev_get_drvdata(dev);
- if (!line)
- return -ENODEV;
- serial8250_resume_port(line - 1);
+ serial8250_resume_port(line);
return 0;
}
-static SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume);
static struct pnp_driver serial_pnp_driver = {
.name = "serial",
.probe = serial_pnp_probe,
.remove = serial_pnp_remove,
.driver = {
- .pm = &serial_pnp_pm_ops,
+ .pm = pm_sleep_ptr(&serial_pnp_pm_ops),
},
.id_table = pnp_dev_table,
};
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index fc9dd5d45295..893bc493f662 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -612,13 +612,6 @@ int serial8250_em485_config(struct uart_port *port, struct ktermios *termios,
{
struct uart_8250_port *up = up_to_u8250p(port);
- /* pick sane settings if the user hasn't */
- if (!!(rs485->flags & SER_RS485_RTS_ON_SEND) ==
- !!(rs485->flags & SER_RS485_RTS_AFTER_SEND)) {
- rs485->flags |= SER_RS485_RTS_ON_SEND;
- rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
- }
-
/*
* Both serial8250_em485_init() and serial8250_em485_destroy()
* are idempotent.
@@ -1630,7 +1623,7 @@ static void serial8250_start_tx(struct uart_port *port)
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);
- if (!port->x_char && uart_circ_empty(&port->state->xmit))
+ if (!port->x_char && kfifo_is_empty(&port->state->port.xmit_fifo))
return;
serial8250_rpm_get_tx(up);
@@ -1778,7 +1771,7 @@ EXPORT_SYMBOL_GPL(serial8250_rx_chars);
void serial8250_tx_chars(struct uart_8250_port *up)
{
struct uart_port *port = &up->port;
- struct circ_buf *xmit = &port->state->xmit;
+ struct tty_port *tport = &port->state->port;
int count;
if (port->x_char) {
@@ -1789,14 +1782,19 @@ void serial8250_tx_chars(struct uart_8250_port *up)
serial8250_stop_tx(port);
return;
}
- if (uart_circ_empty(xmit)) {
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
__stop_tx(up);
return;
}
count = up->tx_loadsz;
do {
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ unsigned char c;
+
+ if (!uart_fifo_get(port, &c))
+ break;
+
+ serial_out(up, UART_TX, c);
if (up->bugs & UART_BUG_TXRACE) {
/*
* The Aspeed BMC virtual UARTs have a bug where data
@@ -1809,9 +1807,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
*/
serial_in(up, UART_SCR);
}
- uart_xmit_advance(port, 1);
- if (uart_circ_empty(xmit))
- break;
+
if ((up->capabilities & UART_CAP_HFIFO) &&
!uart_lsr_tx_empty(serial_in(up, UART_LSR)))
break;
@@ -1821,7 +1817,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
break;
} while (--count > 0);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
/*
@@ -1829,7 +1825,8 @@ void serial8250_tx_chars(struct uart_8250_port *up)
* HW can go idle. So we get here once again with empty FIFO and disable
* the interrupt and RPM in __stop_tx()
*/
- if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
+ if (kfifo_is_empty(&tport->xmit_fifo) &&
+ !(up->capabilities & UART_CAP_RPM))
__stop_tx(up);
}
EXPORT_SYMBOL_GPL(serial8250_tx_chars);
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index ea2e81f58eac..69ac00270547 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -5,8 +5,6 @@
obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o
8250-y := 8250_core.o
-8250-$(CONFIG_ALPHA_GENERIC) += 8250_alpha.o
-8250-$(CONFIG_ALPHA_JENSEN) += 8250_alpha.o
8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
8250_base-y := 8250_port.o
8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o