aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/serial
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r--drivers/tty/serial/8250/8250_core.c62
-rw-r--r--drivers/tty/serial/8250/8250_dma.c26
-rw-r--r--drivers/tty/serial/8250/8250_dw.c198
-rw-r--r--drivers/tty/serial/8250/8250_early.c138
-rw-r--r--drivers/tty/serial/8250/8250_em.c32
-rw-r--r--drivers/tty/serial/8250/8250_gsc.c3
-rw-r--r--drivers/tty/serial/8250/8250_pci.c418
-rw-r--r--drivers/tty/serial/8250/8250_pnp.c1
-rw-r--r--drivers/tty/serial/8250/Kconfig10
-rw-r--r--drivers/tty/serial/8250/serial_cs.c1
-rw-r--r--drivers/tty/serial/Kconfig134
-rw-r--r--drivers/tty/serial/Makefile11
-rw-r--r--drivers/tty/serial/altera_jtaguart.c6
-rw-r--r--drivers/tty/serial/altera_uart.c11
-rw-r--r--drivers/tty/serial/amba-pl010.c22
-rw-r--r--drivers/tty/serial/amba-pl011.c165
-rw-r--r--drivers/tty/serial/apbuart.c2
-rw-r--r--drivers/tty/serial/ar933x_uart.c111
-rw-r--r--drivers/tty/serial/arc_uart.c39
-rw-r--r--drivers/tty/serial/atmel_serial.c1137
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c30
-rw-r--r--drivers/tty/serial/bfin_sport_uart.c17
-rw-r--r--drivers/tty/serial/bfin_uart.c34
-rw-r--r--drivers/tty/serial/clps711x.c464
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c40
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c2
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c1
-rw-r--r--drivers/tty/serial/crisv10.c114
-rw-r--r--drivers/tty/serial/dz.c2
-rw-r--r--drivers/tty/serial/earlycon-arm-semihost.c61
-rw-r--r--drivers/tty/serial/earlycon.c172
-rw-r--r--drivers/tty/serial/efm32-uart.c40
-rw-r--r--drivers/tty/serial/fsl_lpuart.c439
-rw-r--r--drivers/tty/serial/icom.c107
-rw-r--r--drivers/tty/serial/ifx6x60.c2
-rw-r--r--drivers/tty/serial/imx.c625
-rw-r--r--drivers/tty/serial/ioc4_serial.c4
-rw-r--r--drivers/tty/serial/ip22zilog.c6
-rw-r--r--drivers/tty/serial/kgdb_nmi.c60
-rw-r--r--drivers/tty/serial/kgdboc.c2
-rw-r--r--drivers/tty/serial/lantiq.c7
-rw-r--r--drivers/tty/serial/lpc32xx_hs.c7
-rw-r--r--drivers/tty/serial/m32r_sio.c13
-rw-r--r--drivers/tty/serial/max3100.c2
-rw-r--r--drivers/tty/serial/max310x.c1232
-rw-r--r--drivers/tty/serial/mcf.c11
-rw-r--r--drivers/tty/serial/men_z135_uart.c867
-rw-r--r--drivers/tty/serial/mfd.c27
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c183
-rw-r--r--drivers/tty/serial/mpsc.c17
-rw-r--r--drivers/tty/serial/mrst_max3110.c49
-rw-r--r--drivers/tty/serial/msm_serial.c444
-rw-r--r--drivers/tty/serial/msm_serial.h31
-rw-r--r--drivers/tty/serial/msm_serial_hs.c2
-rw-r--r--drivers/tty/serial/mux.c4
-rw-r--r--drivers/tty/serial/mxs-auart.c78
-rw-r--r--drivers/tty/serial/netx-serial.c10
-rw-r--r--drivers/tty/serial/nwpserial.c3
-rw-r--r--drivers/tty/serial/of_serial.c6
-rw-r--r--drivers/tty/serial/omap-serial.c423
-rw-r--r--drivers/tty/serial/pch_uart.c128
-rw-r--r--drivers/tty/serial/pmac_zilog.c13
-rw-r--r--drivers/tty/serial/pnx8xxx_uart.c7
-rw-r--r--drivers/tty/serial/pxa.c40
-rw-r--r--drivers/tty/serial/rp2.c4
-rw-r--r--drivers/tty/serial/sa1100.c10
-rw-r--r--drivers/tty/serial/samsung.c135
-rw-r--r--drivers/tty/serial/samsung.h28
-rw-r--r--drivers/tty/serial/sb1250-duart.c2
-rw-r--r--drivers/tty/serial/sc16is7xx.c1277
-rw-r--r--drivers/tty/serial/sc26xx.c749
-rw-r--r--drivers/tty/serial/sccnxp.c348
-rw-r--r--drivers/tty/serial/serial-tegra.c60
-rw-r--r--drivers/tty/serial/serial_core.c91
-rw-r--r--drivers/tty/serial/serial_ks8695.c2
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.c143
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.h110
-rw-r--r--drivers/tty/serial/serial_txx9.c11
-rw-r--r--drivers/tty/serial/sh-sci.c506
-rw-r--r--drivers/tty/serial/sh-sci.h2
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c1159
-rw-r--r--drivers/tty/serial/sirfsoc_uart.h504
-rw-r--r--drivers/tty/serial/st-asc.c932
-rw-r--r--drivers/tty/serial/sunhv.c22
-rw-r--r--drivers/tty/serial/sunsab.c21
-rw-r--r--drivers/tty/serial/sunsu.c18
-rw-r--r--drivers/tty/serial/sunzilog.c24
-rw-r--r--drivers/tty/serial/tilegx.c708
-rw-r--r--drivers/tty/serial/timbuart.c4
-rw-r--r--drivers/tty/serial/uartlite.c17
-rw-r--r--drivers/tty/serial/ucc_uart.c6
-rw-r--r--drivers/tty/serial/vr41xx_siu.c4
-rw-r--r--drivers/tty/serial/vt8500_serial.c8
-rw-r--r--drivers/tty/serial/xilinx_uartps.c1394
-rw-r--r--drivers/tty/serial/zs.c2
95 files changed, 12125 insertions, 4529 deletions
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 86c00b1c558..7a91c6d1eb7 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -555,7 +555,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
*/
if ((p->port.type == PORT_XR17V35X) ||
(p->port.type == PORT_XR17D15X)) {
- serial_out(p, UART_EXAR_SLEEP, 0xff);
+ serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
return;
}
@@ -1520,7 +1520,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
status = serial8250_rx_chars(up, status);
}
serial8250_modem_status(up);
- if (status & UART_LSR_THRE)
+ if (!up->dma && (status & UART_LSR_THRE))
serial8250_tx_chars(up);
spin_unlock_irqrestore(&port->lock, flags);
@@ -1694,6 +1694,10 @@ static int serial_link_irq_chain(struct uart_8250_port *up)
static void serial_unlink_irq_chain(struct uart_8250_port *up)
{
+ /*
+ * yes, some broken gcc emit "warning: 'i' may be used uninitialized"
+ * but no, we are not going to take a patch that assigns NULL below.
+ */
struct irq_info *i;
struct hlist_node *n;
struct hlist_head *h;
@@ -1922,13 +1926,8 @@ static void serial8250_put_poll_char(struct uart_port *port,
wait_for_xmitr(up, BOTH_EMPTY);
/*
* Send the character out.
- * If a LF, also do CR...
*/
serial_port_out(port, UART_TX, c);
- if (c == 10) {
- wait_for_xmitr(up, BOTH_EMPTY);
- serial_port_out(port, UART_TX, 13);
- }
/*
* Finally, wait for transmitter to become empty
@@ -2322,7 +2321,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
fcr = uart_config[port->type].fcr;
- if (baud < 2400 || fifo_bug) {
+ if ((baud < 2400 && !up->dma) || fifo_bug) {
fcr &= ~UART_FCR_TRIGGER_MASK;
fcr |= UART_FCR_TRIGGER_1;
}
@@ -2334,9 +2333,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
* the trigger, or the MCR RTS bit is cleared. In the case where
* the remote UART is not using CTS auto flow control, we must
* have sufficient FIFO entries for the latency of the remote
- * UART to respond. IOW, at least 32 bytes of FIFO.
+ * UART to respond. IOW, at least 32 bytes of FIFO. Also enable
+ * AFE if hw flow control is supported
*/
- if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) {
+ if ((up->capabilities & UART_CAP_AFE && (port->fifosize >= 32)) ||
+ (port->flags & UPF_HARD_FLOW)) {
up->mcr &= ~UART_MCR_AFE;
if (termios->c_cflag & CRTSCTS)
up->mcr |= UART_MCR_AFE;
@@ -2356,7 +2357,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (termios->c_iflag & INPCK)
port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= UART_LSR_BI;
/*
@@ -2433,6 +2434,24 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
serial_dl_write(up, quot);
/*
+ * XR17V35x UARTs have an extra fractional divisor register (DLD)
+ *
+ * We need to recalculate all of the registers, because DLM and DLL
+ * are already rounded to a whole integer.
+ *
+ * When recalculating we use a 32x clock instead of a 16x clock to
+ * allow 1-bit for rounding in the fractional part.
+ */
+ if (up->port.type == PORT_XR17V35X) {
+ unsigned int baud_x32 = (port->uartclk * 2) / baud;
+ u16 quot = baud_x32 / 32;
+ u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2);
+
+ serial_dl_write(up, quot);
+ serial_port_out(port, 0x2, quot_frac & 0xf);
+ }
+
+ /*
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
* is written without DLAB set, this mode will be disabled.
*/
@@ -2670,6 +2689,10 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (port->type == PORT_16550A && port->iotype == UPIO_AU)
up->bugs |= UART_BUG_NOMSR;
+ /* HW bugs may trigger IRQ while IIR == NO_INT */
+ if (port->type == PORT_TEGRA)
+ up->bugs |= UART_BUG_NOMSR;
+
if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
autoconfig_irq(up);
@@ -2860,14 +2883,10 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
touch_nmi_watchdog();
- local_irq_save(flags);
- if (port->sysrq) {
- /* serial8250_handle_irq() already took the lock */
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&port->lock);
- } else
- spin_lock(&port->lock);
+ if (port->sysrq || oops_in_progress)
+ locked = spin_trylock_irqsave(&port->lock, flags);
+ else
+ spin_lock_irqsave(&port->lock, flags);
/*
* First save the IER then disable the interrupts
@@ -2899,8 +2918,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
serial8250_modem_status(up);
if (locked)
- spin_unlock(&port->lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
}
static int __init serial8250_console_setup(struct console *co, char *options)
@@ -3062,7 +3080,7 @@ void serial8250_resume_port(int line)
*/
static int serial8250_probe(struct platform_device *dev)
{
- struct plat_serial8250_port *p = dev->dev.platform_data;
+ struct plat_serial8250_port *p = dev_get_platdata(&dev->dev);
struct uart_8250_port uart;
int ret, i, irqflag = 0;
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 7046769608d..148ffe4c232 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -20,12 +20,15 @@ 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;
-
- dma->tx_running = 0;
+ unsigned long flags;
dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
+ spin_lock_irqsave(&p->port.lock, flags);
+
+ dma->tx_running = 0;
+
xmit->tail += dma->tx_size;
xmit->tail &= UART_XMIT_SIZE - 1;
p->port.icount.tx += dma->tx_size;
@@ -35,6 +38,8 @@ static void __dma_tx_complete(void *param)
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port))
serial8250_tx_dma(p);
+
+ spin_unlock_irqrestore(&p->port.lock, flags);
}
static void __dma_rx_complete(void *param)
@@ -187,21 +192,28 @@ int serial8250_request_dma(struct uart_8250_port *p)
dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size,
&dma->rx_addr, GFP_KERNEL);
- if (!dma->rx_buf) {
- dma_release_channel(dma->rxchan);
- dma_release_channel(dma->txchan);
- return -ENOMEM;
- }
+ if (!dma->rx_buf)
+ goto err;
/* TX buffer */
dma->tx_addr = dma_map_single(dma->txchan->device->dev,
p->port.state->xmit.buf,
UART_XMIT_SIZE,
DMA_TO_DEVICE);
+ if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
+ dma_free_coherent(dma->rxchan->device->dev, dma->rx_size,
+ dma->rx_buf, dma->rx_addr);
+ goto err;
+ }
dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
return 0;
+err:
+ dma_release_channel(dma->rxchan);
+ dma_release_channel(dma->txchan);
+
+ return -ENOMEM;
}
EXPORT_SYMBOL_GPL(serial8250_request_dma);
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 76a8daadff4..51b307aab75 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -14,7 +14,6 @@
* raised, the LCR needs to be rewritten and the uart status register read.
*/
#include <linux/device.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/serial_8250.h>
@@ -56,28 +55,125 @@
struct dw8250_data {
- int last_lcr;
- int line;
- struct clk *clk;
- u8 usr_reg;
+ u8 usr_reg;
+ int last_mcr;
+ int line;
+ struct clk *clk;
+ struct uart_8250_dma dma;
};
+struct dw8250_acpi_desc {
+ void (*set_termios)(struct uart_port *p, struct ktermios *termios,
+ struct ktermios *old);
+};
+
+#define BYT_PRV_CLK 0x800
+#define BYT_PRV_CLK_EN (1 << 0)
+#define BYT_PRV_CLK_M_VAL_SHIFT 1
+#define BYT_PRV_CLK_N_VAL_SHIFT 16
+#define BYT_PRV_CLK_UPDATE (1 << 31)
+
+static void byt_set_termios(struct uart_port *p, struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int baud = tty_termios_baud_rate(termios);
+ unsigned int m, n;
+ u32 reg;
+
+ /*
+ * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the
+ * dividers must be adjusted.
+ *
+ * uartclk = (m / n) * 100 MHz, where m <= n
+ */
+ switch (baud) {
+ case 500000:
+ case 1000000:
+ case 2000000:
+ case 4000000:
+ m = 64;
+ n = 100;
+ p->uartclk = 64000000;
+ break;
+ case 3500000:
+ m = 56;
+ n = 100;
+ p->uartclk = 56000000;
+ break;
+ case 1500000:
+ case 3000000:
+ m = 48;
+ n = 100;
+ p->uartclk = 48000000;
+ break;
+ case 2500000:
+ m = 40;
+ n = 100;
+ p->uartclk = 40000000;
+ break;
+ default:
+ m = 2304;
+ n = 3125;
+ p->uartclk = 73728000;
+ }
+
+ /* Reset the clock */
+ reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
+ writel(reg, p->membase + BYT_PRV_CLK);
+ reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
+ writel(reg, p->membase + BYT_PRV_CLK);
+
+ serial8250_do_set_termios(p, termios, old);
+}
+
+static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
+{
+ struct dw8250_data *d = p->private_data;
+
+ /* If reading MSR, report CTS asserted when auto-CTS/RTS enabled */
+ if (offset == UART_MSR && d->last_mcr & UART_MCR_AFE) {
+ value |= UART_MSR_CTS;
+ value &= ~UART_MSR_DCTS;
+ }
+
+ return value;
+}
+
+static void dw8250_force_idle(struct uart_port *p)
+{
+ serial8250_clear_and_reinit_fifos(container_of
+ (p, struct uart_8250_port, port));
+ (void)p->serial_in(p, UART_RX);
+}
+
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
- if (offset == UART_LCR)
- d->last_lcr = value;
+ if (offset == UART_MCR)
+ d->last_mcr = value;
- offset <<= p->regshift;
- writeb(value, p->membase + offset);
+ writeb(value, p->membase + (offset << p->regshift));
+
+ /* Make sure LCR write wasn't ignored */
+ if (offset == UART_LCR) {
+ int tries = 1000;
+ while (tries--) {
+ unsigned int lcr = p->serial_in(p, UART_LCR);
+ if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
+ return;
+ dw8250_force_idle(p);
+ writeb(value, p->membase + (UART_LCR << p->regshift));
+ }
+ dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ }
}
static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
{
- offset <<= p->regshift;
+ unsigned int value = readb(p->membase + (offset << p->regshift));
- return readb(p->membase + offset);
+ return dw8250_modify_msr(p, offset, value);
}
/* Read Back (rb) version to ensure register access ording. */
@@ -91,18 +187,30 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
- if (offset == UART_LCR)
- d->last_lcr = value;
+ if (offset == UART_MCR)
+ d->last_mcr = value;
- offset <<= p->regshift;
- writel(value, p->membase + offset);
+ writel(value, p->membase + (offset << p->regshift));
+
+ /* Make sure LCR write wasn't ignored */
+ if (offset == UART_LCR) {
+ int tries = 1000;
+ while (tries--) {
+ unsigned int lcr = p->serial_in(p, UART_LCR);
+ if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
+ return;
+ dw8250_force_idle(p);
+ writel(value, p->membase + (UART_LCR << p->regshift));
+ }
+ dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ }
}
static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
{
- offset <<= p->regshift;
+ unsigned int value = readl(p->membase + (offset << p->regshift));
- return readl(p->membase + offset);
+ return dw8250_modify_msr(p, offset, value);
}
static int dw8250_handle_irq(struct uart_port *p)
@@ -113,9 +221,8 @@ static int dw8250_handle_irq(struct uart_port *p)
if (serial8250_handle_irq(p, iir)) {
return 1;
} else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
- /* Clear the USR and write the LCR again. */
+ /* Clear the USR */
(void)p->serial_in(p, d->usr_reg);
- p->serial_out(p, UART_LCR, d->last_lcr);
return 1;
}
@@ -135,6 +242,14 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
pm_runtime_put_sync_suspend(port->dev);
}
+static bool dw8250_dma_filter(struct dma_chan *chan, void *param)
+{
+ struct dw8250_data *data = param;
+
+ return chan->chan_id == data->dma.tx_chan_id ||
+ chan->chan_id == data->dma.rx_chan_id;
+}
+
static void dw8250_setup_port(struct uart_8250_port *up)
{
struct uart_port *p = &up->port;
@@ -222,11 +337,12 @@ static int dw8250_probe_of(struct uart_port *p,
return 0;
}
-#ifdef CONFIG_ACPI
-static int dw8250_probe_acpi(struct uart_8250_port *up)
+static int dw8250_probe_acpi(struct uart_8250_port *up,
+ struct dw8250_data *data)
{
const struct acpi_device_id *id;
struct uart_port *p = &up->port;
+ struct dw8250_acpi_desc *acpi_desc;
dw8250_setup_port(up);
@@ -239,24 +355,20 @@ static int dw8250_probe_acpi(struct uart_8250_port *up)
p->serial_out = dw8250_serial_out32;
p->regshift = 2;
- if (!p->uartclk)
- p->uartclk = (unsigned int)id->driver_data;
-
- up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL);
- if (!up->dma)
- return -ENOMEM;
+ up->dma = &data->dma;
up->dma->rxconf.src_maxburst = p->fifosize / 4;
up->dma->txconf.dst_maxburst = p->fifosize / 4;
+ acpi_desc = (struct dw8250_acpi_desc *)id->driver_data;
+ if (!acpi_desc)
+ return 0;
+
+ if (acpi_desc->set_termios)
+ p->set_termios = acpi_desc->set_termios;
+
return 0;
}
-#else
-static inline int dw8250_probe_acpi(struct uart_8250_port *up)
-{
- return -ENODEV;
-}
-#endif /* CONFIG_ACPI */
static int dw8250_probe(struct platform_device *pdev)
{
@@ -296,6 +408,12 @@ static int dw8250_probe(struct platform_device *pdev)
uart.port.uartclk = clk_get_rate(data->clk);
}
+ data->dma.rx_chan_id = -1;
+ data->dma.tx_chan_id = -1;
+ data->dma.rx_param = data;
+ data->dma.tx_param = data;
+ data->dma.fn = dw8250_dma_filter;
+
uart.port.iotype = UPIO_MEM;
uart.port.serial_in = dw8250_serial_in;
uart.port.serial_out = dw8250_serial_out;
@@ -306,7 +424,7 @@ static int dw8250_probe(struct platform_device *pdev)
if (err)
return err;
} else if (ACPI_HANDLE(&pdev->dev)) {
- err = dw8250_probe_acpi(&uart);
+ err = dw8250_probe_acpi(&uart, data);
if (err)
return err;
} else {
@@ -342,7 +460,7 @@ static int dw8250_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int dw8250_suspend(struct device *dev)
{
struct dw8250_data *data = dev_get_drvdata(dev);
@@ -360,7 +478,7 @@ static int dw8250_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_RUNTIME
static int dw8250_runtime_suspend(struct device *dev)
@@ -396,10 +514,16 @@ static const struct of_device_id dw8250_of_match[] = {
};
MODULE_DEVICE_TABLE(of, dw8250_of_match);
+static struct dw8250_acpi_desc byt_8250_desc = {
+ .set_termios = byt_set_termios,
+};
+
static const struct acpi_device_id dw8250_acpi_match[] = {
{ "INT33C4", 0 },
{ "INT33C5", 0 },
- { "80860F0A", 0 },
+ { "INT3434", 0 },
+ { "INT3435", 0 },
+ { "80860F0A", (kernel_ulong_t)&byt_8250_desc},
{ },
};
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index 721904f8efa..4858b8a99d3 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -35,18 +35,8 @@
#include <linux/serial_8250.h>
#include <asm/io.h>
#include <asm/serial.h>
-#ifdef CONFIG_FIX_EARLYCON_MEM
-#include <asm/pgtable.h>
-#include <asm/fixmap.h>
-#endif
-struct early_serial8250_device {
- struct uart_port port;
- char options[16]; /* e.g., 115200n8 */
- unsigned int baud;
-};
-
-static struct early_serial8250_device early_device;
+static struct earlycon_device *early_device;
unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset)
{
@@ -100,7 +90,7 @@ static void __init serial_putc(struct uart_port *port, int c)
static void __init early_serial8250_write(struct console *console,
const char *s, unsigned int count)
{
- struct uart_port *port = &early_device.port;
+ struct uart_port *port = &early_device->port;
unsigned int ier;
/* Save the IER and disable interrupts */
@@ -129,7 +119,7 @@ static unsigned int __init probe_baud(struct uart_port *port)
return (port->uartclk / 16) / quot;
}
-static void __init init_port(struct early_serial8250_device *device)
+static void __init init_port(struct earlycon_device *device)
{
struct uart_port *port = &device->port;
unsigned int divisor;
@@ -148,127 +138,45 @@ static void __init init_port(struct early_serial8250_device *device)
serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);
}
-static int __init parse_options(struct early_serial8250_device *device,
- char *options)
+static int __init early_serial8250_setup(struct earlycon_device *device,
+ const char *options)
{
- struct uart_port *port = &device->port;
- int mmio, mmio32, length;
-
- if (!options)
- return -ENODEV;
-
- port->uartclk = BASE_BAUD * 16;
-
- mmio = !strncmp(options, "mmio,", 5);
- mmio32 = !strncmp(options, "mmio32,", 7);
- if (mmio || mmio32) {
- port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
- port->mapbase = simple_strtoul(options + (mmio ? 5 : 7),
- &options, 0);
- if (mmio32)
- port->regshift = 2;
-#ifdef CONFIG_FIX_EARLYCON_MEM
- set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
- port->mapbase & PAGE_MASK);
- port->membase =
- (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
- port->membase += port->mapbase & ~PAGE_MASK;
-#else
- port->membase = ioremap_nocache(port->mapbase, 64);
- if (!port->membase) {
- printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n",
- __func__,
- (unsigned long long) port->mapbase);
- return -ENOMEM;
- }
-#endif
- } else if (!strncmp(options, "io,", 3)) {
- port->iotype = UPIO_PORT;
- port->iobase = simple_strtoul(options + 3, &options, 0);
- mmio = 0;
- } else
- return -EINVAL;
+ if (!(device->port.membase || device->port.iobase))
+ return 0;
- options = strchr(options, ',');
- if (options) {
- options++;
- device->baud = simple_strtoul(options, NULL, 0);
- length = min(strcspn(options, " "), sizeof(device->options));
- strlcpy(device->options, options, length);
- } else {
- device->baud = probe_baud(port);
+ if (!device->baud) {
+ device->baud = probe_baud(&device->port);
snprintf(device->options, sizeof(device->options), "%u",
- device->baud);
+ device->baud);
}
- if (mmio || mmio32)
- printk(KERN_INFO
- "Early serial console at MMIO%s 0x%llx (options '%s')\n",
- mmio32 ? "32" : "",
- (unsigned long long)port->mapbase,
- device->options);
- else
- printk(KERN_INFO
- "Early serial console at I/O port 0x%lx (options '%s')\n",
- port->iobase,
- device->options);
-
- return 0;
-}
-
-static struct console early_serial8250_console __initdata = {
- .name = "uart",
- .write = early_serial8250_write,
- .flags = CON_PRINTBUFFER | CON_BOOT,
- .index = -1,
-};
-
-static int __init early_serial8250_setup(char *options)
-{
- struct early_serial8250_device *device = &early_device;
- int err;
-
- if (device->port.membase || device->port.iobase)
- return 0;
-
- err = parse_options(device, options);
- if (err < 0)
- return err;
-
init_port(device);
+
+ early_device = device;
+ device->con->write = early_serial8250_write;
return 0;
}
+EARLYCON_DECLARE(uart8250, early_serial8250_setup);
+EARLYCON_DECLARE(uart, early_serial8250_setup);
int __init setup_early_serial8250_console(char *cmdline)
{
- char *options;
- int err;
+ char match[] = "uart8250";
- options = strstr(cmdline, "uart8250,");
- if (!options) {
- options = strstr(cmdline, "uart,");
- if (!options)
- return 0;
- }
-
- options = strchr(cmdline, ',') + 1;
- err = early_serial8250_setup(options);
- if (err < 0)
- return err;
-
- register_console(&early_serial8250_console);
+ if (cmdline && cmdline[4] == ',')
+ match[4] = '\0';
- return 0;
+ return setup_earlycon(cmdline, match, early_serial8250_setup);
}
int serial8250_find_port_for_earlycon(void)
{
- struct early_serial8250_device *device = &early_device;
- struct uart_port *port = &device->port;
+ struct earlycon_device *device = early_device;
+ struct uart_port *port = device ? &device->port : NULL;
int line;
int ret;
- if (!device->port.membase && !device->port.iobase)
+ if (!port || (!port->membase && !port->iobase))
return -ENODEV;
line = serial8250_find_port(port);
@@ -283,5 +191,3 @@ int serial8250_find_port_for_earlycon(void)
return ret;
}
-
-early_param("earlycon", setup_early_serial8250_console);
diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c
index 916cc19fbbd..56c87232b6a 100644
--- a/drivers/tty/serial/8250/8250_em.c
+++ b/drivers/tty/serial/8250/8250_em.c
@@ -18,7 +18,6 @@
*/
#include <linux/device.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/serial_8250.h>
@@ -95,25 +94,23 @@ static int serial8250_em_probe(struct platform_device *pdev)
struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
struct serial8250_em_priv *priv;
struct uart_8250_port up;
- int ret = -EINVAL;
+ int ret;
if (!regs || !irq) {
dev_err(&pdev->dev, "missing registers or irq\n");
- goto err0;
+ return -EINVAL;
}
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "unable to allocate private data\n");
- ret = -ENOMEM;
- goto err0;
+ return -ENOMEM;
}
- priv->sclk = clk_get(&pdev->dev, "sclk");
+ priv->sclk = devm_clk_get(&pdev->dev, "sclk");
if (IS_ERR(priv->sclk)) {
dev_err(&pdev->dev, "unable to get clock\n");
- ret = PTR_ERR(priv->sclk);
- goto err1;
+ return PTR_ERR(priv->sclk);
}
memset(&up, 0, sizeof(up));
@@ -124,7 +121,7 @@ static int serial8250_em_probe(struct platform_device *pdev)
up.port.dev = &pdev->dev;
up.port.private_data = priv;
- clk_enable(priv->sclk);
+ clk_prepare_enable(priv->sclk);
up.port.uartclk = clk_get_rate(priv->sclk);
up.port.iotype = UPIO_MEM32;
@@ -136,20 +133,13 @@ static int serial8250_em_probe(struct platform_device *pdev)
ret = serial8250_register_8250_port(&up);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register 8250 port\n");
- goto err2;
+ clk_disable_unprepare(priv->sclk);
+ return ret;
}
priv->line = ret;
platform_set_drvdata(pdev, priv);
return 0;
-
- err2:
- clk_disable(priv->sclk);
- clk_put(priv->sclk);
- err1:
- kfree(priv);
- err0:
- return ret;
}
static int serial8250_em_remove(struct platform_device *pdev)
@@ -157,9 +147,7 @@ static int serial8250_em_remove(struct platform_device *pdev)
struct serial8250_em_priv *priv = platform_get_drvdata(pdev);
serial8250_unregister_port(priv->line);
- clk_disable(priv->sclk);
- clk_put(priv->sclk);
- kfree(priv);
+ clk_disable_unprepare(priv->sclk);
return 0;
}
diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c
index bb91b4713eb..2e3ea1a70d7 100644
--- a/drivers/tty/serial/8250/8250_gsc.c
+++ b/drivers/tty/serial/8250/8250_gsc.c
@@ -31,9 +31,8 @@ static int __init serial_init_chip(struct parisc_device *dev)
int err;
#ifdef CONFIG_64BIT
- extern int iosapic_serial_irq(int cellnum);
if (!dev->irq && (dev->id.sversion == 0xad))
- dev->irq = iosapic_serial_irq(dev->mod_index-1);
+ dev->irq = iosapic_serial_irq(dev);
#endif
if (!dev->irq) {
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index c52948b368d..33137b3ba94 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -9,8 +9,8 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*/
+#undef DEBUG
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/kernel.h>
@@ -27,8 +27,6 @@
#include "8250.h"
-#undef SERIAL_DEBUG_PCI
-
/*
* init function returns:
* > 0 - number of ports
@@ -63,7 +61,7 @@ static int pci_default_setup(struct serial_private*,
static void moan_device(const char *str, struct pci_dev *dev)
{
- printk(KERN_WARNING
+ dev_err(&dev->dev,
"%s: %s\n"
"Please send the output of lspci -vv, this\n"
"message (0x%04x,0x%04x,0x%04x,0x%04x), the\n"
@@ -233,7 +231,7 @@ static int pci_inteli960ni_init(struct pci_dev *dev)
/* is firmware started? */
pci_read_config_dword(dev, 0x44, (void *)&oldval);
if (oldval == 0x00001000L) { /* RESET value */
- printk(KERN_DEBUG "Local i960 firmware missing");
+ dev_dbg(&dev->dev, "Local i960 firmware missing\n");
return -ENODEV;
}
return 0;
@@ -785,7 +783,8 @@ static int pci_netmos_9900_setup(struct serial_private *priv,
{
unsigned int bar;
- if ((priv->dev->subsystem_device & 0xff00) == 0x3000) {
+ if ((priv->dev->device != PCI_DEVICE_ID_NETMOS_9865) &&
+ (priv->dev->subsystem_device & 0xff00) == 0x3000) {
/* netmos apparently orders BARs by datasheet layout, so serial
* ports get BARs 0 and 3 (or 1 and 4 for memmapped)
*/
@@ -827,7 +826,7 @@ static int pci_netmos_9900_numports(struct pci_dev *dev)
if (sub_serports > 0) {
return sub_serports;
} else {
- printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n");
+ dev_err(&dev->dev, "NetMos/Mostech serial driver ignoring port on ambiguous config.\n");
return 0;
}
}
@@ -931,7 +930,7 @@ static int pci_ite887x_init(struct pci_dev *dev)
}
if (!inta_addr[i]) {
- printk(KERN_ERR "ite887x: could not find iobase\n");
+ dev_err(&dev->dev, "ite887x: could not find iobase\n");
return -ENODEV;
}
@@ -1024,9 +1023,9 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
/* Tornado device */
if (deviceID == 0x07000200) {
number_uarts = ioread8(p + 4);
- printk(KERN_DEBUG
+ dev_dbg(&dev->dev,
"%d ports detected on Oxford PCI Express device\n",
- number_uarts);
+ number_uarts);
}
pci_iounmap(dev, p);
return number_uarts;
@@ -1260,10 +1259,10 @@ static int pci_quatech_init(struct pci_dev *dev)
unsigned long base = pci_resource_start(dev, 0);
if (base) {
u32 tmp;
- outl(inl(base + 0x38), base + 0x38);
+ outl(inl(base + 0x38) | 0x00002000, base + 0x38);
tmp = inl(base + 0x3c);
outl(tmp | 0x01000000, base + 0x3c);
- outl(tmp, base + 0x3c);
+ outl(tmp &= ~0x01000000, base + 0x3c);
}
}
return 0;
@@ -1308,6 +1307,29 @@ static int pci_default_setup(struct serial_private *priv,
return setup_port(priv, port, bar, offset, board->reg_shift);
}
+static int pci_pericom_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ unsigned int bar, offset = board->first_offset, maxnr;
+
+ bar = FL_GET_BASE(board->flags);
+ if (board->flags & FL_BASE_BARS)
+ bar += idx;
+ else
+ offset += idx * board->uart_offset;
+
+ maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
+ (board->reg_shift + 3);
+
+ if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
+ return 1;
+
+ port->port.uartclk = 14745600;
+
+ return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
static int
ce4100_serial_setup(struct serial_private *priv,
const struct pciserial_board *board,
@@ -1324,6 +1346,141 @@ ce4100_serial_setup(struct serial_private *priv,
return ret;
}
+#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a
+#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c
+
+#define BYT_PRV_CLK 0x800
+#define BYT_PRV_CLK_EN (1 << 0)
+#define BYT_PRV_CLK_M_VAL_SHIFT 1
+#define BYT_PRV_CLK_N_VAL_SHIFT 16
+#define BYT_PRV_CLK_UPDATE (1 << 31)
+
+#define BYT_GENERAL_REG 0x808
+#define BYT_GENERAL_DIS_RTS_N_OVERRIDE (1 << 3)
+
+#define BYT_TX_OVF_INT 0x820
+#define BYT_TX_OVF_INT_MASK (1 << 1)
+
+static void
+byt_set_termios(struct uart_port *p, struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int baud = tty_termios_baud_rate(termios);
+ unsigned int m, n;
+ u32 reg;
+
+ /*
+ * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the
+ * dividers must be adjusted.
+ *
+ * uartclk = (m / n) * 100 MHz, where m <= n
+ */
+ switch (baud) {
+ case 500000:
+ case 1000000:
+ case 2000000:
+ case 4000000:
+ m = 64;
+ n = 100;
+ p->uartclk = 64000000;
+ break;
+ case 3500000:
+ m = 56;
+ n = 100;
+ p->uartclk = 56000000;
+ break;
+ case 1500000:
+ case 3000000:
+ m = 48;
+ n = 100;
+ p->uartclk = 48000000;
+ break;
+ case 2500000:
+ m = 40;
+ n = 100;
+ p->uartclk = 40000000;
+ break;
+ default:
+ m = 2304;
+ n = 3125;
+ p->uartclk = 73728000;
+ }
+
+ /* Reset the clock */
+ reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
+ writel(reg, p->membase + BYT_PRV_CLK);
+ reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
+ writel(reg, p->membase + BYT_PRV_CLK);
+
+ /*
+ * If auto-handshake mechanism is not enabled,
+ * disable rts_n override
+ */
+ reg = readl(p->membase + BYT_GENERAL_REG);
+ reg &= ~BYT_GENERAL_DIS_RTS_N_OVERRIDE;
+ if (termios->c_cflag & CRTSCTS)
+ reg |= BYT_GENERAL_DIS_RTS_N_OVERRIDE;
+ writel(reg, p->membase + BYT_GENERAL_REG);
+
+ serial8250_do_set_termios(p, termios, old);
+}
+
+static bool byt_dma_filter(struct dma_chan *chan, void *param)
+{
+ return chan->chan_id == *(int *)param;
+}
+
+static int
+byt_serial_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ struct uart_8250_dma *dma;
+ int ret;
+
+ dma = devm_kzalloc(port->port.dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ switch (priv->dev->device) {
+ case PCI_DEVICE_ID_INTEL_BYT_UART1:
+ dma->rx_chan_id = 3;
+ dma->tx_chan_id = 2;
+ break;
+ case PCI_DEVICE_ID_INTEL_BYT_UART2:
+ dma->rx_chan_id = 5;
+ dma->tx_chan_id = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dma->rxconf.slave_id = dma->rx_chan_id;
+ dma->rxconf.src_maxburst = 16;
+
+ dma->txconf.slave_id = dma->tx_chan_id;
+ dma->txconf.dst_maxburst = 16;
+
+ dma->fn = byt_dma_filter;
+ dma->rx_param = &dma->rx_chan_id;
+ dma->tx_param = &dma->tx_chan_id;
+
+ ret = pci_default_setup(priv, board, port, idx);
+ port->port.iotype = UPIO_MEM;
+ port->port.type = PORT_16550A;
+ port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+ port->port.set_termios = byt_set_termios;
+ port->port.fifosize = 64;
+ port->tx_loadsz = 64;
+ port->dma = dma;
+ port->capabilities = UART_CAP_FIFO | UART_CAP_AFE;
+
+ /* Disable Tx counter interrupts */
+ writel(BYT_TX_OVF_INT_MASK, port->port.membase + BYT_TX_OVF_INT);
+
+ return ret;
+}
+
static int
pci_omegapci_setup(struct serial_private *priv,
const struct pciserial_board *board,
@@ -1344,17 +1501,80 @@ pci_brcm_trumanage_setup(struct serial_private *priv,
return ret;
}
+static int pci_fintek_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ struct pci_dev *pdev = priv->dev;
+ unsigned long base;
+ unsigned long iobase;
+ unsigned long ciobase = 0;
+ u8 config_base;
+
+ /*
+ * We are supposed to be able to read these from the PCI config space,
+ * but the values there don't seem to match what we need to use, so
+ * just use these hard-coded values for now, as they are correct.
+ */
+ switch (idx) {
+ case 0: iobase = 0xe000; config_base = 0x40; break;
+ case 1: iobase = 0xe008; config_base = 0x48; break;
+ case 2: iobase = 0xe010; config_base = 0x50; break;
+ case 3: iobase = 0xe018; config_base = 0x58; break;
+ case 4: iobase = 0xe020; config_base = 0x60; break;
+ case 5: iobase = 0xe028; config_base = 0x68; break;
+ case 6: iobase = 0xe030; config_base = 0x70; break;
+ case 7: iobase = 0xe038; config_base = 0x78; break;
+ case 8: iobase = 0xe040; config_base = 0x80; break;
+ case 9: iobase = 0xe048; config_base = 0x88; break;
+ case 10: iobase = 0xe050; config_base = 0x90; break;
+ case 11: iobase = 0xe058; config_base = 0x98; break;
+ default:
+ /* Unknown number of ports, get out of here */
+ return -EINVAL;
+ }
+
+ if (idx < 4) {
+ base = pci_resource_start(priv->dev, 3);
+ ciobase = (int)(base + (0x8 * idx));
+ }
+
+ dev_dbg(&pdev->dev, "%s: idx=%d iobase=0x%lx ciobase=0x%lx config_base=0x%2x\n",
+ __func__, idx, iobase, ciobase, config_base);
+
+ /* Enable UART I/O port */
+ pci_write_config_byte(pdev, config_base + 0x00, 0x01);
+
+ /* Select 128-byte FIFO and 8x FIFO threshold */
+ pci_write_config_byte(pdev, config_base + 0x01, 0x33);
+
+ /* LSB UART */
+ pci_write_config_byte(pdev, config_base + 0x04, (u8)(iobase & 0xff));
+
+ /* MSB UART */
+ pci_write_config_byte(pdev, config_base + 0x05, (u8)((iobase & 0xff00) >> 8));
+
+ /* irq number, this usually fails, but the spec says to do it anyway. */
+ pci_write_config_byte(pdev, config_base + 0x06, pdev->irq);
+
+ port->port.iotype = UPIO_PORT;
+ port->port.iobase = iobase;
+ port->port.mapbase = 0;
+ port->port.membase = NULL;
+ port->port.regshift = 0;
+
+ return 0;
+}
+
static int skip_tx_en_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
{
port->port.flags |= UPF_NO_TXEN_TEST;
- printk(KERN_DEBUG "serial8250: skipping TxEn test for device "
- "[%04x:%04x] subsystem [%04x:%04x]\n",
- priv->dev->vendor,
- priv->dev->device,
- priv->dev->subsystem_vendor,
- priv->dev->subsystem_device);
+ dev_dbg(&priv->dev->dev,
+ "serial8250: skipping TxEn test for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ priv->dev->vendor, priv->dev->device,
+ priv->dev->subsystem_vendor, priv->dev->subsystem_device);
return pci_default_setup(priv, board, port, idx);
}
@@ -1533,6 +1753,8 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620
+#define PCI_DEVICE_ID_ADVANTECH_PCI3618 0x3618
+#define PCI_DEVICE_ID_ADVANTECH_PCIf618 0xf618
#define PCI_DEVICE_ID_TITAN_200I 0x8028
#define PCI_DEVICE_ID_TITAN_400I 0x8048
#define PCI_DEVICE_ID_TITAN_800I 0x8088
@@ -1545,6 +1767,7 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_TITAN_800E 0xA014
#define PCI_DEVICE_ID_TITAN_200EI 0xA016
#define PCI_DEVICE_ID_TITAN_200EISI 0xA017
+#define PCI_DEVICE_ID_TITAN_200V3 0xA306
#define PCI_DEVICE_ID_TITAN_400V3 0xA310
#define PCI_DEVICE_ID_TITAN_410V3 0xA312
#define PCI_DEVICE_ID_TITAN_800V3 0xA314
@@ -1557,6 +1780,7 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_WCH_CH352_2S 0x3253
#define PCI_DEVICE_ID_WCH_CH353_4S 0x3453
#define PCI_DEVICE_ID_WCH_CH353_2S1PF 0x5046
+#define PCI_DEVICE_ID_WCH_CH353_1S1P 0x5053
#define PCI_DEVICE_ID_WCH_CH353_2S1P 0x7053
#define PCI_VENDOR_ID_AGESTAR 0x5372
#define PCI_DEVICE_ID_AGESTAR_9375 0x6872
@@ -1565,6 +1789,7 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021
#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022
#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
+#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
@@ -1587,8 +1812,8 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
* ADDI-DATA GmbH communication cards <info@addi-data.com>
*/
{
- .vendor = PCI_VENDOR_ID_ADDIDATA_OLD,
- .device = PCI_DEVICE_ID_ADDIDATA_APCI7800,
+ .vendor = PCI_VENDOR_ID_AMCC,
+ .device = PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = addidata_apci7800_setup,
@@ -1661,6 +1886,20 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.setup = kt_serial_setup,
},
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_BYT_UART1,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = byt_serial_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_BYT_UART2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = byt_serial_setup,
+ },
/*
* ITE
*/
@@ -1825,6 +2064,31 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.exit = pci_plx9050_exit,
},
/*
+ * Pericom
+ */
+ {
+ .vendor = 0x12d8,
+ .device = 0x7952,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup,
+ },
+ {
+ .vendor = 0x12d8,
+ .device = 0x7954,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup,
+ },
+ {
+ .vendor = 0x12d8,
+ .device = 0x7958,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup,
+ },
+
+ /*
* PLX
*/
{
@@ -2149,6 +2413,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.setup = pci_omegapci_setup,
},
+ /* WCH CH353 1S1P card (16550 clone) */
+ {
+ .vendor = PCI_VENDOR_ID_WCH,
+ .device = PCI_DEVICE_ID_WCH_CH353_1S1P,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_wch_ch353_setup,
+ },
/* WCH CH353 2S1P card (16550 clone) */
{
.vendor = PCI_VENDOR_ID_WCH,
@@ -2254,6 +2526,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.setup = pci_brcm_trumanage_setup,
},
+ {
+ .vendor = 0x1c29,
+ .device = 0x1104,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fintek_setup,
+ },
+ {
+ .vendor = 0x1c29,
+ .device = 0x1108,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fintek_setup,
+ },
+ {
+ .vendor = 0x1c29,
+ .device = 0x1112,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fintek_setup,
+ },
/*
* Default "match everything" terminator entry
@@ -2448,9 +2741,13 @@ enum pci_board_num_t {
pbn_ADDIDATA_PCIe_4_3906250,
pbn_ADDIDATA_PCIe_8_3906250,
pbn_ce4100_1_115200,
+ pbn_byt,
pbn_omegapci,
pbn_NETMOS9900_2s_115200,
pbn_brcm_trumanage,
+ pbn_fintek_4,
+ pbn_fintek_8,
+ pbn_fintek_12,
};
/*
@@ -3184,6 +3481,17 @@ static struct pciserial_board pci_boards[] = {
.base_baud = 921600,
.reg_shift = 2,
},
+ /*
+ * Intel BayTrail HSUART reference clock is 44.2368 MHz at power-on,
+ * but is overridden by byt_set_termios.
+ */
+ [pbn_byt] = {
+ .flags = FL_BASE0,
+ .num_ports = 1,
+ .base_baud = 2764800,
+ .uart_offset = 0x80,
+ .reg_shift = 2,
+ },
[pbn_omegapci] = {
.flags = FL_BASE0,
.num_ports = 8,
@@ -3201,6 +3509,24 @@ static struct pciserial_board pci_boards[] = {
.reg_shift = 2,
.base_baud = 115200,
},
+ [pbn_fintek_4] = {
+ .num_ports = 4,
+ .uart_offset = 8,
+ .base_baud = 115200,
+ .first_offset = 0x40,
+ },
+ [pbn_fintek_8] = {
+ .num_ports = 8,
+ .uart_offset = 8,
+ .base_baud = 115200,
+ .first_offset = 0x40,
+ },
+ [pbn_fintek_12] = {
+ .num_ports = 12,
+ .uart_offset = 8,
+ .base_baud = 115200,
+ .first_offset = 0x40,
+ },
};
static const struct pci_device_id blacklist[] = {
@@ -3211,6 +3537,7 @@ static const struct pci_device_id blacklist[] = {
/* multi-io cards handled by parport_serial */
{ PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */
+ { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */
};
/*
@@ -3361,14 +3688,15 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
if (quirk->setup(priv, board, &uart, i))
break;
-#ifdef SERIAL_DEBUG_PCI
- printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n",
- uart.port.iobase, uart.port.irq, uart.port.iotype);
-#endif
+ dev_dbg(&dev->dev, "Setup PCI port: port %lx, irq %d, type %d\n",
+ uart.port.iobase, uart.port.irq, uart.port.iotype);
priv->line[i] = serial8250_register_8250_port(&uart);
if (priv->line[i] < 0) {
- printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
+ dev_err(&dev->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]);
break;
}
}
@@ -3461,7 +3789,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
}
if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
- printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
+ dev_err(&dev->dev, "invalid driver_data: %ld\n",
ent->driver_data);
return -EINVAL;
}
@@ -3519,8 +3847,6 @@ static void pciserial_remove_one(struct pci_dev *dev)
{
struct serial_private *priv = pci_get_drvdata(dev);
- pci_set_drvdata(dev, NULL);
-
pciserial_remove_ports(priv);
pci_disable_device(dev);
@@ -3554,7 +3880,7 @@ static int pciserial_resume_one(struct pci_dev *dev)
err = pci_enable_device(dev);
/* FIXME: We cannot simply error out here */
if (err)
- printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n");
+ dev_err(&dev->dev, "Unable to re-enable ports, trying to continue.\n");
pciserial_resume_ports(priv);
}
return 0;
@@ -3566,6 +3892,13 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620,
PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0,
pbn_b2_8_921600 },
+ /* Advantech also use 0x3618 and 0xf618 */
+ { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3618,
+ PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0,
+ pbn_b0_4_921600 },
+ { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIf618,
+ PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0,
+ pbn_b0_4_921600 },
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
PCI_SUBVENDOR_ID_CONNECT_TECH,
PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
@@ -4139,6 +4472,9 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_oxsemi_2_4000000 },
+ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200V3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
@@ -4697,8 +5033,8 @@ static struct pci_device_id serial_pci_tbl[] = {
0,
pbn_b0_1_115200 },
- { PCI_VENDOR_ID_ADDIDATA_OLD,
- PCI_DEVICE_ID_ADDIDATA_APCI7800,
+ { PCI_VENDOR_ID_AMCC,
+ PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800,
PCI_ANY_ID,
PCI_ANY_ID,
0,
@@ -4797,6 +5133,12 @@ static struct pci_device_id serial_pci_tbl[] = {
PCI_VENDOR_ID_IBM, 0x0299,
0, 0, pbn_b0_bt_2_115200 },
+ /*
+ * other NetMos 9835 devices are most likely handled by the
+ * parport_serial driver, check drivers/parport/parport_serial.c
+ * before adding them here.
+ */
+
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
0xA000, 0x1000,
0, 0, pbn_b0_1_115200 },
@@ -4841,6 +5183,15 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ce4100_1_115200 },
+ /* Intel BayTrail */
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART1,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
+ pbn_byt },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART2,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
+ pbn_byt },
/*
* Cronyx Omega PCI
@@ -4911,6 +5262,11 @@ static struct pci_device_id serial_pci_tbl[] = {
0,
0, pbn_exar_XR17V358 },
+ /* Fintek PCI serial cards */
+ { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 },
+ { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 },
+ { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 },
+
/*
* These entries match devices with class COMMUNICATION_SERIAL,
* COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c
index 35d9ab95c5c..682a2fbe5c0 100644
--- a/drivers/tty/serial/8250/8250_pnp.c
+++ b/drivers/tty/serial/8250/8250_pnp.c
@@ -12,7 +12,6 @@
* the Free Software Foundation; either version 2 of the License.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pnp.h>
#include <linux/string.h>
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index a1ba94d6488..349ee598b34 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -41,7 +41,7 @@ config SERIAL_8250_DEPRECATED_OPTIONS
accept kernel parameters in both forms like 8250_core.nr_uarts=4 and
8250.nr_uarts=4. We now renamed the module back to 8250, but if
anybody noticed in 3.7 and changed their userspace we still have to
- keep the 8350_core.* options around until they revert the changes
+ keep the 8250_core.* options around until they revert the changes
they already did.
If 8250 is built as a module, this adds 8250_core alias instead.
@@ -61,6 +61,7 @@ config SERIAL_8250_CONSOLE
bool "Console on 8250/16550 and compatible serial port"
depends on SERIAL_8250=y
select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
---help---
If you say Y here, it will be possible to use a serial port as the
system console (the system console is the device which receives all
@@ -90,11 +91,6 @@ config SERIAL_8250_CONSOLE
If unsure, say N.
-config FIX_EARLYCON_MEM
- bool
- depends on X86
- default y
-
config SERIAL_8250_GSC
tristate
depends on SERIAL_8250 && GSC
@@ -116,6 +112,8 @@ config SERIAL_8250_PCI
This builds standard PCI serial support. You may be able to
disable this feature if you only need legacy serial support.
Saves about 9K.
+ Note that serial ports on NetMos 9835 Multi-I/O cards are handled
+ by the parport_serial driver, enabled with CONFIG_PARPORT_SERIAL.
config SERIAL_8250_HP300
tristate
diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c
index 1b74b88e1e1..4d180c9423e 100644
--- a/drivers/tty/serial/8250/serial_cs.c
+++ b/drivers/tty/serial/8250/serial_cs.c
@@ -34,7 +34,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 5e3d68917ff..fb57159bad3 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -5,7 +5,14 @@
if TTY
menu "Serial drivers"
- depends on HAS_IOMEM && GENERIC_HARDIRQS
+ depends on HAS_IOMEM
+
+config SERIAL_EARLYCON
+ bool
+ help
+ Support for early consoles with the earlycon parameter. This enables
+ the console before standard serial driver is probed. The console is
+ enabled when early_param is processed.
source "drivers/tty/serial/8250/Kconfig"
@@ -53,6 +60,7 @@ config SERIAL_AMBA_PL011_CONSOLE
bool "Support for console on AMBA serial port"
depends on SERIAL_AMBA_PL011=y
select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
---help---
Say Y here if you wish to use an AMBA PrimeCell UART as the system
console (the system console is the device which receives all kernel
@@ -65,6 +73,18 @@ config SERIAL_AMBA_PL011_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
+config SERIAL_EARLYCON_ARM_SEMIHOST
+ bool "Early console using ARM semihosting"
+ depends on ARM64 || ARM
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Support for early debug console using ARM semihosting. This enables
+ the console before standard serial driver is probed. This is enabled
+ with "earlycon=smh" on the kernel command line. The console is
+ enabled when early_param is processed.
+
config SERIAL_SB1250_DUART
tristate "BCM1xxx on-chip DUART serial support"
depends on SIBYTE_SB1xxx_SOC=y
@@ -97,6 +117,7 @@ config SERIAL_ATMEL
bool "AT91 / AT32 on-chip serial port support"
depends on ARCH_AT91 || AVR32
select SERIAL_CORE
+ select SERIAL_MCTRL_GPIO
help
This enables the driver for the on-chip UARTs of the Atmel
AT91 and AT32 processors.
@@ -181,9 +202,8 @@ config SERIAL_KS8695_CONSOLE
config SERIAL_CLPS711X
tristate "CLPS711X serial port support"
- depends on ARCH_CLPS711X
+ depends on ARCH_CLPS711X || COMPILE_TEST
select SERIAL_CORE
- default y
help
This enables the driver for the on-chip UARTs of the Cirrus
Logic EP711x/EP721x/EP731x processors.
@@ -277,7 +297,7 @@ config SERIAL_TEGRA
select SERIAL_CORE
help
Support for the on-chip UARTs on the NVIDIA Tegra series SOCs
- providing /dev/ttyHS0, 1, 2, 3 and 4 (note, some machines may not
+ providing /dev/ttyTHS0, 1, 2, 3 and 4 (note, some machines may not
provide all of these ports, depending on how the serial port
are enabled). This driver uses the APB DMA to achieve higher baudrate
and better performance.
@@ -290,14 +310,14 @@ config SERIAL_MAX3100
MAX3100 chip support
config SERIAL_MAX310X
- bool "MAX310X support"
- depends on SPI
+ tristate "MAX310X support"
+ depends on SPI_MASTER
select SERIAL_CORE
- select REGMAP_SPI if SPI
+ select REGMAP_SPI if SPI_MASTER
default n
help
This selects support for an advanced UART from Maxim (Dallas).
- Supported ICs are MAX3107, MAX3108.
+ Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
Each IC contains 128 words each of receive and transmit FIFO
that can be controlled through I2C or high-speed SPI.
@@ -709,7 +729,7 @@ config SERIAL_IP22_ZILOG_CONSOLE
config SERIAL_SH_SCI
tristate "SuperH SCI(F) serial port support"
- depends on HAVE_CLK && (SUPERH || ARCH_SHMOBILE)
+ depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
select SERIAL_CORE
config SERIAL_SH_SCI_NR_UARTS
@@ -1025,7 +1045,7 @@ config SERIAL_SGI_IOC3
config SERIAL_MSM
bool "MSM on-chip serial port support"
- depends on ARCH_MSM
+ depends on ARCH_MSM || ARCH_QCOM
select SERIAL_CORE
config SERIAL_MSM_CONSOLE
@@ -1035,7 +1055,7 @@ config SERIAL_MSM_CONSOLE
config SERIAL_MSM_HS
tristate "MSM UART High Speed: Serial Driver"
- depends on ARCH_MSM
+ depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50
select SERIAL_CORE
help
If you have a machine based on MSM family of SoCs, you
@@ -1146,31 +1166,13 @@ config SERIAL_QE
This driver supports the QE serial ports on Freescale embedded
PowerPC that contain a QUICC Engine.
-config SERIAL_SC26XX
- tristate "SC2681/SC2692 serial port support"
- depends on SNI_RM
- select SERIAL_CORE
- help
- This is a driver for the onboard serial ports of
- older RM400 machines.
-
-config SERIAL_SC26XX_CONSOLE
- bool "Console on SC2681/SC2692 serial port"
- depends on SERIAL_SC26XX=y
- select SERIAL_CORE_CONSOLE
- help
- Support for Console on SC2681/SC2692 serial ports.
-
config SERIAL_SCCNXP
tristate "SCCNXP serial port support"
- depends on !SERIAL_SC26XX
select SERIAL_CORE
- default n
help
This selects support for an advanced UART from NXP (Philips).
Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92,
SC28L202, SCC68681 and SCC68692.
- Positioned as a replacement for the driver SC26XX.
config SERIAL_SCCNXP_CONSOLE
bool "Console on SCCNXP serial port"
@@ -1179,6 +1181,16 @@ config SERIAL_SCCNXP_CONSOLE
help
Support for console on SCCNXP serial ports.
+config SERIAL_SC16IS7XX
+ tristate "SC16IS7xx serial support"
+ depends on I2C
+ select SERIAL_CORE
+ select REGMAP_I2C if I2C
+ help
+ This selects support for SC16IS7xx serial ports.
+ Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752,
+ SC16IS760 and SC16IS762.
+
config SERIAL_BFIN_SPORT
tristate "Blackfin SPORT emulate UART"
depends on BLACKFIN
@@ -1245,6 +1257,7 @@ config SERIAL_BFIN_SPORT3_UART_CTSRTS
config SERIAL_TIMBERDALE
tristate "Support for timberdale UART"
select SERIAL_CORE
+ depends on X86_32 || COMPILE_TEST
---help---
Add support for UART controller on timberdale.
@@ -1341,7 +1354,7 @@ config SERIAL_IFX6X60
config SERIAL_PCH_UART
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART"
- depends on PCI
+ depends on PCI && (X86_32 || COMPILE_TEST)
select SERIAL_CORE
help
This driver is for PCH(Platform controller Hub) UART of Intel EG20T
@@ -1387,27 +1400,31 @@ config SERIAL_MXS_AUART_CONSOLE
Enable a MXS AUART port to be the system console.
config SERIAL_XILINX_PS_UART
- tristate "Xilinx PS UART support"
+ tristate "Cadence (Xilinx Zynq) UART support"
depends on OF
select SERIAL_CORE
help
- This driver supports the Xilinx PS UART port.
+ This driver supports the Cadence UART. It is found e.g. in Xilinx
+ Zynq.
config SERIAL_XILINX_PS_UART_CONSOLE
- bool "Xilinx PS UART console support"
+ bool "Cadence UART console support"
depends on SERIAL_XILINX_PS_UART=y
select SERIAL_CORE_CONSOLE
help
- Enable a Xilinx PS UART port to be the system console.
+ Enable a Cadence UART port to be the system console.
config SERIAL_AR933X
- bool "AR933X serial port support"
- depends on SOC_AR933X
+ tristate "AR933X serial port support"
+ depends on HAVE_CLK && SOC_AR933X
select SERIAL_CORE
help
If you have an Atheros AR933X SOC based board and want to use the
built-in UART of the SoC, say Y to this option.
+ To compile this driver as a module, choose M here: the
+ module will be called ar933x_uart.
+
config SERIAL_AR933X_CONSOLE
bool "Console on AR933X serial port"
depends on SERIAL_AR933X=y
@@ -1424,8 +1441,8 @@ config SERIAL_AR933X_NR_UARTS
to support.
config SERIAL_EFM32_UART
- tristate "EFM32 UART/USART port."
- depends on ARCH_EFM32
+ tristate "EFM32 UART/USART port"
+ depends on ARM && (ARCH_EFM32 || COMPILE_TEST)
select SERIAL_CORE
help
This driver support the USART and UART ports on
@@ -1436,6 +1453,15 @@ config SERIAL_EFM32_UART_CONSOLE
depends on SERIAL_EFM32_UART=y
select SERIAL_CORE_CONSOLE
+config SERIAL_TILEGX
+ tristate "TILE-Gx on-chip serial port support"
+ depends on TILEGX
+ select TILE_GXIO_UART
+ select SERIAL_CORE
+ ---help---
+ This device provides access to the on-chip UARTs on the TILE-Gx
+ processor.
+
config SERIAL_ARC
tristate "ARC UART driver support"
select SERIAL_CORE
@@ -1485,6 +1511,7 @@ config SERIAL_RP2_NR_UARTS
config SERIAL_FSL_LPUART
tristate "Freescale lpuart serial port support"
+ depends on HAS_DMA
select SERIAL_CORE
help
Support for the on-chip lpuart on some Freescale SOCs.
@@ -1497,6 +1524,37 @@ config SERIAL_FSL_LPUART_CONSOLE
If you have enabled the lpuart serial port on the Freescale SoCs,
you can make it the console by answering Y to this option.
+config SERIAL_ST_ASC
+ tristate "ST ASC serial port support"
+ select SERIAL_CORE
+ depends on ARM || COMPILE_TEST
+ help
+ This driver is for the on-chip Asychronous Serial Controller on
+ STMicroelectronics STi SoCs.
+ ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality.
+ It support all industry standard baud rates.
+
+ If unsure, say N.
+
+config SERIAL_ST_ASC_CONSOLE
+ bool "Support for console on ST ASC"
+ depends on SERIAL_ST_ASC=y
+ select SERIAL_CORE_CONSOLE
+
+config SERIAL_MEN_Z135
+ tristate "MEN 16z135 Support"
+ select SERIAL_CORE
+ depends on MCB
+ help
+ Say yes here to enable support for the MEN 16z135 High Speed UART IP-Core
+ on a MCB carrier.
+
+ This driver can also be build as a module. If so, the module will be called
+ men_z135_uart.ko
+
endmenu
+config SERIAL_MCTRL_GPIO
+ tristate
+
endif # TTY
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cf650f0cd6e..0080cc362e0 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -5,6 +5,9 @@
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
obj-$(CONFIG_SERIAL_21285) += 21285.o
+obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
+obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o
+
# These Sparc drivers have to appear before others such as 8250
# which share ttySx minor node space. Otherwise console device
# names change and other unplesantries.
@@ -47,8 +50,8 @@ obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
-obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o
obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
+obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o
obj-$(CONFIG_SERIAL_JSM) += jsm/
obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
@@ -65,6 +68,8 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o
+obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
@@ -86,3 +91,7 @@ obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
obj-$(CONFIG_SERIAL_RP2) += rp2.o
obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
+obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
+
+# GPIOLIB helpers for modem control lines
+obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c
index c6bdb943726..59b3da9bcc3 100644
--- a/drivers/tty/serial/altera_jtaguart.c
+++ b/drivers/tty/serial/altera_jtaguart.c
@@ -139,7 +139,9 @@ static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp)
uart_insert_char(port, 0, 0, ch, flag);
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
}
static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp)
@@ -408,7 +410,8 @@ static struct uart_driver altera_jtaguart_driver = {
static int altera_jtaguart_probe(struct platform_device *pdev)
{
- struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data;
+ struct altera_jtaguart_platform_uart *platp =
+ dev_get_platdata(&pdev->dev);
struct uart_port *port;
struct resource *res_irq, *res_mem;
int i = pdev->id;
@@ -470,6 +473,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev)
#ifdef CONFIG_OF
static struct of_device_id altera_jtaguart_match[] = {
{ .compatible = "ALTR,juart-1.0", },
+ { .compatible = "altr,juart-1.0", },
{},
};
MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index 1d46966e2a6..323376668b7 100644
--- a/drivers/tty/serial/altera_uart.c
+++ b/drivers/tty/serial/altera_uart.c
@@ -185,6 +185,12 @@ static void altera_uart_set_termios(struct uart_port *port,
uart_update_timeout(port, termios->c_cflag, baud);
altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
spin_unlock_irqrestore(&port->lock, flags);
+
+ /*
+ * FIXME: port->read_status_mask and port->ignore_status_mask
+ * need to be initialized based on termios settings for
+ * INPCK, IGNBRK, IGNPAR, PARMRK, BRKINT
+ */
}
static void altera_uart_rx_chars(struct altera_uart *pp)
@@ -231,7 +237,9 @@ static void altera_uart_rx_chars(struct altera_uart *pp)
flag);
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
}
static void altera_uart_tx_chars(struct altera_uart *pp)
@@ -534,7 +542,7 @@ static int altera_uart_get_of_uartclk(struct platform_device *pdev,
static int altera_uart_probe(struct platform_device *pdev)
{
- struct altera_uart_platform_uart *platp = pdev->dev.platform_data;
+ struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev);
struct uart_port *port;
struct resource *res_mem;
struct resource *res_irq;
@@ -613,6 +621,7 @@ static int altera_uart_remove(struct platform_device *pdev)
#ifdef CONFIG_OF
static struct of_device_id altera_uart_match[] = {
{ .compatible = "ALTR,uart-1.0", },
+ { .compatible = "altr,uart-1.0", },
{},
};
MODULE_DEVICE_TABLE(of, altera_uart_match);
diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c
index c3684051952..971af1e22d0 100644
--- a/drivers/tty/serial/amba-pl010.c
+++ b/drivers/tty/serial/amba-pl010.c
@@ -420,7 +420,7 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios,
uap->port.read_status_mask = UART01x_RSR_OE;
if (termios->c_iflag & INPCK)
uap->port.read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
uap->port.read_status_mask |= UART01x_RSR_BE;
/*
@@ -721,14 +721,13 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
uap->port.flags = UPF_BOOT_AUTOCONF;
uap->port.line = i;
uap->dev = dev;
- uap->data = dev->dev.platform_data;
+ uap->data = dev_get_platdata(&dev->dev);
amba_ports[i] = uap;
amba_set_drvdata(dev, uap);
ret = uart_add_one_port(&amba_reg, &uap->port);
if (ret) {
- amba_set_drvdata(dev, NULL);
amba_ports[i] = NULL;
clk_put(uap->clk);
unmap:
@@ -745,8 +744,6 @@ static int pl010_remove(struct amba_device *dev)
struct uart_amba_port *uap = amba_get_drvdata(dev);
int i;
- amba_set_drvdata(dev, NULL);
-
uart_remove_one_port(&amba_reg, &uap->port);
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
@@ -759,9 +756,10 @@ static int pl010_remove(struct amba_device *dev)
return 0;
}
-static int pl010_suspend(struct amba_device *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int pl010_suspend(struct device *dev)
{
- struct uart_amba_port *uap = amba_get_drvdata(dev);
+ struct uart_amba_port *uap = dev_get_drvdata(dev);
if (uap)
uart_suspend_port(&amba_reg, &uap->port);
@@ -769,15 +767,18 @@ static int pl010_suspend(struct amba_device *dev, pm_message_t state)
return 0;
}
-static int pl010_resume(struct amba_device *dev)
+static int pl010_resume(struct device *dev)
{
- struct uart_amba_port *uap = amba_get_drvdata(dev);
+ struct uart_amba_port *uap = dev_get_drvdata(dev);
if (uap)
uart_resume_port(&amba_reg, &uap->port);
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pl010_dev_pm_ops, pl010_suspend, pl010_resume);
static struct amba_id pl010_ids[] = {
{
@@ -792,12 +793,11 @@ MODULE_DEVICE_TABLE(amba, pl010_ids);
static struct amba_driver pl010_driver = {
.drv = {
.name = "uart-pl010",
+ .pm = &pl010_dev_pm_ops,
},
.id_table = pl010_ids,
.probe = pl010_probe,
.remove = pl010_remove,
- .suspend = pl010_suspend,
- .resume = pl010_resume,
};
static int __init pl010_init(void)
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 28b35ad9c6c..0e26dcbd5ea 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -112,8 +112,6 @@ static struct vendor_data vendor_st = {
.get_fifosize = get_fifosize_st,
};
-static struct uart_amba_port *amba_ports[UART_NR];
-
/* Deals with DMA transactions */
struct pl011_sgbuf {
@@ -265,7 +263,7 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
{
/* DMA is the sole user of the platform data right now */
- struct amba_pl011_data *plat = uap->port.dev->platform_data;
+ struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev);
struct dma_slave_config tx_conf = {
.dst_addr = uap->port.mapbase + UART01x_DR,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
@@ -305,7 +303,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
/* Optionally make use of an RX channel as well */
chan = dma_request_slave_channel(dev, "rx");
-
+
if (!chan && plat->dma_rx_param) {
chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
@@ -320,7 +318,7 @@ static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *
.src_addr = uap->port.mapbase + UART01x_DR,
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.direction = DMA_DEV_TO_MEM,
- .src_maxburst = uap->fifosize >> 1,
+ .src_maxburst = uap->fifosize >> 2,
.device_fc = false,
};
@@ -677,6 +675,8 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
* Locking: called with port lock held and IRQs disabled.
*/
static void pl011_dma_flush_buffer(struct uart_port *port)
+__releases(&uap->port.lock)
+__acquires(&uap->port.lock)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -967,6 +967,8 @@ static void pl011_dma_rx_poll(unsigned long args)
spin_lock_irqsave(&uap->port.lock, flags);
pl011_dma_rx_stop(uap);
+ uap->im |= UART011_RXIM;
+ writew(uap->im, uap->port.membase + UART011_IMSC);
spin_unlock_irqrestore(&uap->port.lock, flags);
uap->dmarx.running = false;
@@ -1198,6 +1200,8 @@ static void pl011_enable_ms(struct uart_port *port)
}
static void pl011_rx_chars(struct uart_amba_port *uap)
+__releases(&uap->port.lock)
+__acquires(&uap->port.lock)
{
pl011_fifo_to_tty(uap);
@@ -1212,8 +1216,8 @@ static void pl011_rx_chars(struct uart_amba_port *uap)
dev_dbg(uap->port.dev, "could not trigger RX DMA job "
"fall back to interrupt mode again\n");
uap->im |= UART011_RXIM;
+ writew(uap->im, uap->port.membase + UART011_IMSC);
} else {
- uap->im &= ~UART011_RXIM;
#ifdef CONFIG_DMA_ENGINE
/* Start Rx DMA poll */
if (uap->dmarx.poll_rate) {
@@ -1225,8 +1229,6 @@ static void pl011_rx_chars(struct uart_amba_port *uap)
}
#endif
}
-
- writew(uap->im, uap->port.membase + UART011_IMSC);
}
spin_lock(&uap->port.lock);
}
@@ -1497,10 +1499,10 @@ static int pl011_hwinit(struct uart_port *port)
uap->im = readw(uap->port.membase + UART011_IMSC);
writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
- if (uap->port.dev->platform_data) {
+ if (dev_get_platdata(uap->port.dev)) {
struct amba_pl011_data *plat;
- plat = uap->port.dev->platform_data;
+ plat = dev_get_platdata(uap->port.dev);
if (plat->init)
plat->init();
}
@@ -1509,10 +1511,25 @@ static int pl011_hwinit(struct uart_port *port)
return retval;
}
+static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
+{
+ writew(lcr_h, uap->port.membase + uap->lcrh_rx);
+ if (uap->lcrh_rx != uap->lcrh_tx) {
+ int i;
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10 times
+ */
+ for (i = 0; i < 10; ++i)
+ writew(0xff, uap->port.membase + UART011_MIS);
+ writew(lcr_h, uap->port.membase + uap->lcrh_tx);
+ }
+}
+
static int pl011_startup(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
- unsigned int cr;
+ unsigned int cr, lcr_h, fbrd, ibrd;
int retval;
retval = pl011_hwinit(port);
@@ -1531,32 +1548,36 @@ static int pl011_startup(struct uart_port *port)
writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS);
/*
- * Provoke TX FIFO interrupt into asserting.
+ * Provoke TX FIFO interrupt into asserting. Taking care to preserve
+ * baud rate and data format specified by FBRD, IBRD and LCRH as the
+ * UART may already be in use as a console.
*/
+ spin_lock_irq(&uap->port.lock);
+
+ fbrd = readw(uap->port.membase + UART011_FBRD);
+ ibrd = readw(uap->port.membase + UART011_IBRD);
+ lcr_h = readw(uap->port.membase + uap->lcrh_rx);
+
cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
writew(cr, uap->port.membase + UART011_CR);
writew(0, uap->port.membase + UART011_FBRD);
writew(1, uap->port.membase + UART011_IBRD);
- writew(0, uap->port.membase + uap->lcrh_rx);
- if (uap->lcrh_tx != uap->lcrh_rx) {
- int i;
- /*
- * Wait 10 PCLKs before writing LCRH_TX register,
- * to get this delay write read only register 10 times
- */
- for (i = 0; i < 10; ++i)
- writew(0xff, uap->port.membase + UART011_MIS);
- writew(0, uap->port.membase + uap->lcrh_tx);
- }
+ pl011_write_lcr_h(uap, 0);
writew(0, uap->port.membase + UART01x_DR);
while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
barrier();
+ writew(fbrd, uap->port.membase + UART011_FBRD);
+ writew(ibrd, uap->port.membase + UART011_IBRD);
+ pl011_write_lcr_h(uap, lcr_h);
+
/* restore RTS and DTR */
cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
writew(cr, uap->port.membase + UART011_CR);
+ spin_unlock_irq(&uap->port.lock);
+
/*
* initialise the old status of the modem signals
*/
@@ -1625,11 +1646,13 @@ static void pl011_shutdown(struct uart_port *port)
* it during startup().
*/
uap->autorts = false;
+ spin_lock_irq(&uap->port.lock);
cr = readw(uap->port.membase + UART011_CR);
uap->old_cr = cr;
cr &= UART011_CR_RTS | UART011_CR_DTR;
cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
writew(cr, uap->port.membase + UART011_CR);
+ spin_unlock_irq(&uap->port.lock);
/*
* disable break condition and fifos
@@ -1645,10 +1668,10 @@ static void pl011_shutdown(struct uart_port *port)
/* Optionally let pins go into sleep states */
pinctrl_pm_select_sleep_state(port->dev);
- if (uap->port.dev->platform_data) {
+ if (dev_get_platdata(uap->port.dev)) {
struct amba_pl011_data *plat;
- plat = uap->port.dev->platform_data;
+ plat = dev_get_platdata(uap->port.dev);
if (plat->exit)
plat->exit();
}
@@ -1721,7 +1744,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
port->read_status_mask = UART011_DR_OE | 255;
if (termios->c_iflag & INPCK)
port->read_status_mask |= UART011_DR_FE | UART011_DR_PE;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= UART011_DR_BE;
/*
@@ -1793,17 +1816,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
* UART011_FBRD & UART011_IBRD.
* ----------^----------^----------^----------^-----
*/
- writew(lcr_h, port->membase + uap->lcrh_rx);
- if (uap->lcrh_rx != uap->lcrh_tx) {
- int i;
- /*
- * Wait 10 PCLKs before writing LCRH_TX register,
- * to get this delay write read only register 10 times
- */
- for (i = 0; i < 10; ++i)
- writew(0xff, uap->port.membase + UART011_MIS);
- writew(lcr_h, port->membase + uap->lcrh_tx);
- }
+ pl011_write_lcr_h(uap, lcr_h);
writew(old_cr, port->membase + UART011_CR);
spin_unlock_irqrestore(&port->lock, flags);
@@ -2002,10 +2015,10 @@ static int __init pl011_console_setup(struct console *co, char *options)
if (ret)
return ret;
- if (uap->port.dev->platform_data) {
+ if (dev_get_platdata(uap->port.dev)) {
struct amba_pl011_data *plat;
- plat = uap->port.dev->platform_data;
+ plat = dev_get_platdata(uap->port.dev);
if (plat->init)
plat->init();
}
@@ -2032,6 +2045,35 @@ static struct console amba_console = {
};
#define AMBA_CONSOLE (&amba_console)
+
+static void pl011_putc(struct uart_port *port, int c)
+{
+ while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
+ ;
+ writeb(c, port->membase + UART01x_DR);
+ while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY)
+ ;
+}
+
+static void pl011_early_write(struct console *con, const char *s, unsigned n)
+{
+ struct earlycon_device *dev = con->data;
+
+ uart_console_write(&dev->port, s, n, pl011_putc);
+}
+
+static int __init pl011_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = pl011_early_write;
+ return 0;
+}
+EARLYCON_DECLARE(pl011, pl011_early_console_setup);
+OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
+
#else
#define AMBA_CONSOLE NULL
#endif
@@ -2141,10 +2183,19 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
amba_ports[i] = uap;
amba_set_drvdata(dev, uap);
+
+ if (!amba_reg.state) {
+ ret = uart_register_driver(&amba_reg);
+ if (ret < 0) {
+ pr_err("Failed to register AMBA-PL011 driver\n");
+ return ret;
+ }
+ }
+
ret = uart_add_one_port(&amba_reg, &uap->port);
if (ret) {
- amba_set_drvdata(dev, NULL);
amba_ports[i] = NULL;
+ uart_unregister_driver(&amba_reg);
pl011_dma_remove(uap);
}
out:
@@ -2154,24 +2205,27 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
static int pl011_remove(struct amba_device *dev)
{
struct uart_amba_port *uap = amba_get_drvdata(dev);
+ bool busy = false;
int i;
- amba_set_drvdata(dev, NULL);
-
uart_remove_one_port(&amba_reg, &uap->port);
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
if (amba_ports[i] == uap)
amba_ports[i] = NULL;
+ else if (amba_ports[i])
+ busy = true;
pl011_dma_remove(uap);
+ if (!busy)
+ uart_unregister_driver(&amba_reg);
return 0;
}
-#ifdef CONFIG_PM
-static int pl011_suspend(struct amba_device *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int pl011_suspend(struct device *dev)
{
- struct uart_amba_port *uap = amba_get_drvdata(dev);
+ struct uart_amba_port *uap = dev_get_drvdata(dev);
if (!uap)
return -EINVAL;
@@ -2179,9 +2233,9 @@ static int pl011_suspend(struct amba_device *dev, pm_message_t state)
return uart_suspend_port(&amba_reg, &uap->port);
}
-static int pl011_resume(struct amba_device *dev)
+static int pl011_resume(struct device *dev)
{
- struct uart_amba_port *uap = amba_get_drvdata(dev);
+ struct uart_amba_port *uap = dev_get_drvdata(dev);
if (!uap)
return -EINVAL;
@@ -2190,6 +2244,8 @@ static int pl011_resume(struct amba_device *dev)
}
#endif
+static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume);
+
static struct amba_id pl011_ids[] = {
{
.id = 0x00041011,
@@ -2209,34 +2265,23 @@ MODULE_DEVICE_TABLE(amba, pl011_ids);
static struct amba_driver pl011_driver = {
.drv = {
.name = "uart-pl011",
+ .pm = &pl011_dev_pm_ops,
},
.id_table = pl011_ids,
.probe = pl011_probe,
.remove = pl011_remove,
-#ifdef CONFIG_PM
- .suspend = pl011_suspend,
- .resume = pl011_resume,
-#endif
};
static int __init pl011_init(void)
{
- int ret;
printk(KERN_INFO "Serial: AMBA PL011 UART driver\n");
- ret = uart_register_driver(&amba_reg);
- if (ret == 0) {
- ret = amba_driver_register(&pl011_driver);
- if (ret)
- uart_unregister_driver(&amba_reg);
- }
- return ret;
+ return amba_driver_register(&pl011_driver);
}
static void __exit pl011_exit(void)
{
amba_driver_unregister(&pl011_driver);
- uart_unregister_driver(&amba_reg);
}
/*
diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c
index 6331464d910..de11ab8ffd9 100644
--- a/drivers/tty/serial/apbuart.c
+++ b/drivers/tty/serial/apbuart.c
@@ -125,7 +125,9 @@ static void apbuart_rx_chars(struct uart_port *port)
status = UART_GET_STATUS(port);
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
}
static void apbuart_tx_chars(struct uart_port *port)
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index 27f20c57abe..acd03af7cd5 100644
--- a/drivers/tty/serial/ar933x_uart.c
+++ b/drivers/tty/serial/ar933x_uart.c
@@ -17,6 +17,8 @@
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
@@ -24,11 +26,11 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/clk.h>
#include <asm/div64.h>
#include <asm/mach-ath79/ar933x_uart.h>
-#include <asm/mach-ath79/ar933x_uart_platform.h>
#define DRIVER_NAME "ar933x-uart"
@@ -47,8 +49,14 @@ struct ar933x_uart_port {
unsigned int ier; /* shadow Interrupt Enable Register */
unsigned int min_baud;
unsigned int max_baud;
+ struct clk *clk;
};
+static inline bool ar933x_uart_console_enabled(void)
+{
+ return config_enabled(CONFIG_SERIAL_AR933X_CONSOLE);
+}
+
static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
int offset)
{
@@ -322,7 +330,9 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
tty_insert_flip_char(port, ch, TTY_NORMAL);
} while (max_count-- > 0);
+ spin_unlock(&up->port.lock);
tty_flip_buffer_push(port);
+ spin_lock(&up->port.lock);
}
static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
@@ -497,8 +507,6 @@ static struct uart_ops ar933x_uart_ops = {
.verify_port = ar933x_uart_verify_port,
};
-#ifdef CONFIG_SERIAL_AR933X_CONSOLE
-
static struct ar933x_uart_port *
ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
@@ -597,80 +605,88 @@ static struct console ar933x_uart_console = {
static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
{
+ if (!ar933x_uart_console_enabled())
+ return;
+
ar933x_console_ports[up->port.line] = up;
}
-#define AR933X_SERIAL_CONSOLE (&ar933x_uart_console)
-
-#else
-
-static inline void ar933x_uart_add_console_port(struct ar933x_uart_port *up) {}
-
-#define AR933X_SERIAL_CONSOLE NULL
-
-#endif /* CONFIG_SERIAL_AR933X_CONSOLE */
-
static struct uart_driver ar933x_uart_driver = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = "ttyATH",
.nr = CONFIG_SERIAL_AR933X_NR_UARTS,
- .cons = AR933X_SERIAL_CONSOLE,
+ .cons = NULL, /* filled in runtime */
};
static int ar933x_uart_probe(struct platform_device *pdev)
{
- struct ar933x_uart_platform_data *pdata;
struct ar933x_uart_port *up;
struct uart_port *port;
struct resource *mem_res;
struct resource *irq_res;
+ struct device_node *np;
unsigned int baud;
int id;
int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata)
- return -EINVAL;
-
- id = pdev->id;
- if (id == -1)
- id = 0;
+ np = pdev->dev.of_node;
+ if (config_enabled(CONFIG_OF) && np) {
+ id = of_alias_get_id(np, "serial");
+ if (id < 0) {
+ dev_err(&pdev->dev, "unable to get alias id, err=%d\n",
+ id);
+ return id;
+ }
+ } else {
+ id = pdev->id;
+ if (id == -1)
+ id = 0;
+ }
if (id > CONFIG_SERIAL_AR933X_NR_UARTS)
return -EINVAL;
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem_res) {
- dev_err(&pdev->dev, "no MEM resource\n");
- return -EINVAL;
- }
-
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res) {
dev_err(&pdev->dev, "no IRQ resource\n");
return -EINVAL;
}
- up = kzalloc(sizeof(struct ar933x_uart_port), GFP_KERNEL);
+ up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port),
+ GFP_KERNEL);
if (!up)
return -ENOMEM;
+ up->clk = devm_clk_get(&pdev->dev, "uart");
+ if (IS_ERR(up->clk)) {
+ dev_err(&pdev->dev, "unable to get UART clock\n");
+ return PTR_ERR(up->clk);
+ }
+
port = &up->port;
- port->mapbase = mem_res->start;
- port->membase = ioremap(mem_res->start, AR933X_UART_REGS_SIZE);
- if (!port->membase) {
- ret = -ENOMEM;
- goto err_free_up;
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ port->membase = devm_ioremap_resource(&pdev->dev, mem_res);
+ if (IS_ERR(port->membase))
+ return PTR_ERR(port->membase);
+
+ ret = clk_prepare_enable(up->clk);
+ if (ret)
+ return ret;
+
+ port->uartclk = clk_get_rate(up->clk);
+ if (!port->uartclk) {
+ ret = -EINVAL;
+ goto err_disable_clk;
}
+ port->mapbase = mem_res->start;
port->line = id;
port->irq = irq_res->start;
port->dev = &pdev->dev;
port->type = PORT_AR933X;
port->iotype = UPIO_MEM32;
- port->uartclk = pdata->uartclk;
port->regshift = 2;
port->fifosize = AR933X_UART_FIFO_SIZE;
@@ -686,15 +702,13 @@ static int ar933x_uart_probe(struct platform_device *pdev)
ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
if (ret)
- goto err_unmap;
+ goto err_disable_clk;
platform_set_drvdata(pdev, up);
return 0;
-err_unmap:
- iounmap(up->port.membase);
-err_free_up:
- kfree(up);
+err_disable_clk:
+ clk_disable_unprepare(up->clk);
return ret;
}
@@ -703,23 +717,30 @@ static int ar933x_uart_remove(struct platform_device *pdev)
struct ar933x_uart_port *up;
up = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
if (up) {
uart_remove_one_port(&ar933x_uart_driver, &up->port);
- iounmap(up->port.membase);
- kfree(up);
+ clk_disable_unprepare(up->clk);
}
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id ar933x_uart_of_ids[] = {
+ { .compatible = "qca,ar9330-uart" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids);
+#endif
+
static struct platform_driver ar933x_uart_platform_driver = {
.probe = ar933x_uart_probe,
.remove = ar933x_uart_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ar933x_uart_of_ids),
},
};
@@ -727,7 +748,9 @@ static int __init ar933x_uart_init(void)
{
int ret;
- ar933x_uart_driver.nr = CONFIG_SERIAL_AR933X_NR_UARTS;
+ if (ar933x_uart_console_enabled())
+ ar933x_uart_driver.cons = &ar933x_uart_console;
+
ret = uart_register_driver(&ar933x_uart_driver);
if (ret)
goto err_out;
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index cbf1d155b7b..008c223eaf2 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -177,7 +177,7 @@ static void arc_serial_tx_chars(struct arc_uart_port *uart)
uart->port.icount.tx++;
uart->port.x_char = 0;
sent = 1;
- } else if (xmit->tail != xmit->head) { /* TODO: uart_circ_empty */
+ } else if (!uart_circ_empty(xmit)) {
ch = xmit->buf[xmit->tail];
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
uart->port.icount.tx++;
@@ -209,9 +209,9 @@ static void arc_serial_start_tx(struct uart_port *port)
arc_serial_tx_chars(uart);
}
-static void arc_serial_rx_chars(struct arc_uart_port *uart)
+static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status)
{
- unsigned int status, ch, flg = 0;
+ unsigned int ch, flg = 0;
/*
* UART has 4 deep RX-FIFO. Driver's recongnition of this fact
@@ -222,11 +222,11 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart)
* before RX-EMPTY=0, implies some sort of buffering going on in the
* controller, which is indeed the Rx-FIFO.
*/
- while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) {
-
- ch = UART_GET_DATA(uart);
- uart->port.icount.rx++;
-
+ do {
+ /*
+ * This could be an Rx Intr for err (no data),
+ * so check err and clear that Intr first
+ */
if (unlikely(status & (RXOERR | RXFERR))) {
if (status & RXOERR) {
uart->port.icount.overrun++;
@@ -242,14 +242,19 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart)
} else
flg = TTY_NORMAL;
- if (unlikely(uart_handle_sysrq_char(&uart->port, ch)))
- goto done;
+ if (status & RXEMPTY)
+ continue;
- uart_insert_char(&uart->port, status, RXOERR, ch, flg);
+ ch = UART_GET_DATA(uart);
+ uart->port.icount.rx++;
+
+ if (!(uart_handle_sysrq_char(&uart->port, ch)))
+ uart_insert_char(&uart->port, status, RXOERR, ch, flg);
-done:
+ spin_unlock(&uart->port.lock);
tty_flip_buffer_push(&uart->port.state->port);
- }
+ spin_lock(&uart->port.lock);
+ } while (!((status = UART_GET_STATUS(uart)) & RXEMPTY));
}
/*
@@ -292,11 +297,11 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id)
* notifications from the UART Controller.
* To demultiplex between the two, we check the relevant bits
*/
- if ((status & RXIENB) && !(status & RXEMPTY)) {
+ if (status & RXIENB) {
/* already in ISR, no need of xx_irqsave */
spin_lock(&uart->port.lock);
- arc_serial_rx_chars(uart);
+ arc_serial_rx_chars(uart, status);
spin_unlock(&uart->port.lock);
}
@@ -528,7 +533,7 @@ arc_uart_init_one(struct platform_device *pdev, int dev_id)
unsigned long *plat_data;
struct arc_uart_port *uart = &arc_uart_ports[dev_id];
- plat_data = ((unsigned long *)(pdev->dev.platform_data));
+ plat_data = dev_get_platdata(&pdev->dev);
if (!plat_data)
return -ENODEV;
@@ -773,6 +778,6 @@ module_init(arc_serial_init);
module_exit(arc_serial_exit);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("plat-arcfpga/uart");
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Vineet Gupta");
MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver");
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 691265faebb..c4f75031410 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -35,21 +35,21 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
#include <linux/uaccess.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/atmel.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/err.h>
+#include <linux/irq.h>
#include <asm/io.h>
#include <asm/ioctls.h>
-#ifdef CONFIG_ARM
-#include <mach/cpu.h>
-#include <asm/gpio.h>
-#endif
-
#define PDC_BUFFER_SIZE 512
/* Revisit: We should calculate this based on the actual port settings */
#define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */
@@ -60,6 +60,8 @@
#include <linux/serial_core.h>
+#include "serial_mctrl_gpio.h"
+
static void atmel_start_rx(struct uart_port *port);
static void atmel_stop_rx(struct uart_port *port);
@@ -98,6 +100,8 @@ static void atmel_stop_rx(struct uart_port *port);
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+#define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME)
+#define UART_GET_IP_VERSION(port) __raw_readl((port)->membase + ATMEL_US_VERSION)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -113,9 +117,6 @@ static void atmel_stop_rx(struct uart_port *port);
#define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
#define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR)
-static int (*atmel_open_hook)(struct uart_port *);
-static void (*atmel_close_hook)(struct uart_port *);
-
struct atmel_dma_buffer {
unsigned char *buf;
dma_addr_t dma_addr;
@@ -140,13 +141,25 @@ struct atmel_uart_port {
u32 backup_imr; /* IMR saved during suspend */
int break_active; /* break being received */
- short use_dma_rx; /* enable PDC receiver */
+ bool use_dma_rx; /* enable DMA receiver */
+ bool use_pdc_rx; /* enable PDC receiver */
short pdc_rx_idx; /* current PDC RX buffer */
struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */
- short use_dma_tx; /* enable PDC transmitter */
+ bool use_dma_tx; /* enable DMA transmitter */
+ bool use_pdc_tx; /* enable PDC transmitter */
struct atmel_dma_buffer pdc_tx; /* PDC transmitter */
+ spinlock_t lock_tx; /* port lock */
+ spinlock_t lock_rx; /* port lock */
+ struct dma_chan *chan_tx;
+ struct dma_chan *chan_rx;
+ struct dma_async_tx_descriptor *desc_tx;
+ struct dma_async_tx_descriptor *desc_rx;
+ dma_cookie_t cookie_tx;
+ dma_cookie_t cookie_rx;
+ struct scatterlist sg_tx;
+ struct scatterlist sg_rx;
struct tasklet_struct tasklet;
unsigned int irq_status;
unsigned int irq_status_prev;
@@ -154,7 +167,18 @@ struct atmel_uart_port {
struct circ_buf rx_ring;
struct serial_rs485 rs485; /* rs485 settings */
+ struct mctrl_gpios *gpios;
+ int gpio_irq[UART_GPIO_MAX];
unsigned int tx_done_mask;
+ bool ms_irq_enabled;
+ bool is_usart; /* usart or uart */
+ struct timer_list uart_timer; /* uart timer */
+ int (*prepare_rx)(struct uart_port *port);
+ int (*prepare_tx)(struct uart_port *port);
+ void (*schedule_rx)(struct uart_port *port);
+ void (*schedule_tx)(struct uart_port *port);
+ void (*release_rx)(struct uart_port *port);
+ void (*release_tx)(struct uart_port *port);
};
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -181,31 +205,89 @@ to_atmel_uart_port(struct uart_port *uart)
}
#ifdef CONFIG_SERIAL_ATMEL_PDC
-static bool atmel_use_dma_rx(struct uart_port *port)
+static bool atmel_use_pdc_rx(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- return atmel_port->use_dma_rx;
+ return atmel_port->use_pdc_rx;
}
-static bool atmel_use_dma_tx(struct uart_port *port)
+static bool atmel_use_pdc_tx(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- return atmel_port->use_dma_tx;
+ return atmel_port->use_pdc_tx;
}
#else
-static bool atmel_use_dma_rx(struct uart_port *port)
+static bool atmel_use_pdc_rx(struct uart_port *port)
{
return false;
}
-static bool atmel_use_dma_tx(struct uart_port *port)
+static bool atmel_use_pdc_tx(struct uart_port *port)
{
return false;
}
#endif
+static bool atmel_use_dma_tx(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+ return atmel_port->use_dma_tx;
+}
+
+static bool atmel_use_dma_rx(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+ return atmel_port->use_dma_rx;
+}
+
+static unsigned int atmel_get_lines_status(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int status, ret = 0;
+
+ status = UART_GET_CSR(port);
+
+ mctrl_gpio_get(atmel_port->gpios, &ret);
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_CTS))) {
+ if (ret & TIOCM_CTS)
+ status &= ~ATMEL_US_CTS;
+ else
+ status |= ATMEL_US_CTS;
+ }
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_DSR))) {
+ if (ret & TIOCM_DSR)
+ status &= ~ATMEL_US_DSR;
+ else
+ status |= ATMEL_US_DSR;
+ }
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_RI))) {
+ if (ret & TIOCM_RI)
+ status &= ~ATMEL_US_RI;
+ else
+ status |= ATMEL_US_RI;
+ }
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_DCD))) {
+ if (ret & TIOCM_CD)
+ status &= ~ATMEL_US_DCD;
+ else
+ status |= ATMEL_US_DCD;
+ }
+
+ return status;
+}
+
/* Enable or disable the rs485 support */
void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
{
@@ -233,7 +315,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
- if (atmel_use_dma_tx(port))
+ if (atmel_use_pdc_tx(port))
atmel_port->tx_done_mask = ATMEL_US_ENDTX |
ATMEL_US_TXBUFE;
else
@@ -265,21 +347,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
unsigned int mode;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
-#ifdef CONFIG_ARCH_AT91RM9200
- if (cpu_is_at91rm9200()) {
- /*
- * AT91RM9200 Errata #39: RTS0 is not internally connected
- * to PA21. We need to drive the pin manually.
- */
- if (port->mapbase == AT91RM9200_BASE_US0) {
- if (mctrl & TIOCM_RTS)
- at91_set_gpio_value(AT91_PIN_PA21, 0);
- else
- at91_set_gpio_value(AT91_PIN_PA21, 1);
- }
- }
-#endif
-
if (mctrl & TIOCM_RTS)
control |= ATMEL_US_RTSEN;
else
@@ -292,6 +359,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
UART_PUT_CR(port, control);
+ mctrl_gpio_set(atmel_port->gpios, mctrl);
+
/* Local loopback mode? */
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
if (mctrl & TIOCM_LOOP)
@@ -319,7 +388,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
*/
static u_int atmel_get_mctrl(struct uart_port *port)
{
- unsigned int status, ret = 0;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int ret = 0, status;
status = UART_GET_CSR(port);
@@ -335,7 +405,7 @@ static u_int atmel_get_mctrl(struct uart_port *port)
if (!(status & ATMEL_US_RI))
ret |= TIOCM_RI;
- return ret;
+ return mctrl_gpio_get(atmel_port->gpios, &ret);
}
/*
@@ -345,7 +415,7 @@ static void atmel_stop_tx(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_tx(port)) {
+ if (atmel_use_pdc_tx(port)) {
/* disable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
}
@@ -364,7 +434,7 @@ static void atmel_start_tx(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_tx(port)) {
+ if (atmel_use_pdc_tx(port)) {
if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
/* The transmitter is already running. Yes, we
really need this.*/
@@ -390,7 +460,7 @@ static void atmel_start_rx(struct uart_port *port)
UART_PUT_CR(port, ATMEL_US_RXEN);
- if (atmel_use_dma_rx(port)) {
+ if (atmel_use_pdc_rx(port)) {
/* enable PDC controller */
UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
port->read_status_mask);
@@ -407,7 +477,7 @@ static void atmel_stop_rx(struct uart_port *port)
{
UART_PUT_CR(port, ATMEL_US_RXDIS);
- if (atmel_use_dma_rx(port)) {
+ if (atmel_use_pdc_rx(port)) {
/* disable PDC receive */
UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
@@ -422,8 +492,38 @@ static void atmel_stop_rx(struct uart_port *port)
*/
static void atmel_enable_ms(struct uart_port *port)
{
- UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC
- | ATMEL_US_DCDIC | ATMEL_US_CTSIC);
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ uint32_t ier = 0;
+
+ /*
+ * Interrupt should not be enabled twice
+ */
+ if (atmel_port->ms_irq_enabled)
+ return;
+
+ atmel_port->ms_irq_enabled = true;
+
+ if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]);
+ else
+ ier |= ATMEL_US_CTSIC;
+
+ if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]);
+ else
+ ier |= ATMEL_US_DSRIC;
+
+ if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]);
+ else
+ ier |= ATMEL_US_RIIC;
+
+ if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]);
+ else
+ ier |= ATMEL_US_DCDIC;
+
+ UART_PUT_IER(port, ier);
}
/*
@@ -564,6 +664,369 @@ static void atmel_tx_chars(struct uart_port *port)
UART_PUT_IER(port, atmel_port->tx_done_mask);
}
+static void atmel_complete_tx_dma(void *arg)
+{
+ struct atmel_uart_port *atmel_port = arg;
+ struct uart_port *port = &atmel_port->uart;
+ struct circ_buf *xmit = &port->state->xmit;
+ struct dma_chan *chan = atmel_port->chan_tx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (chan)
+ dmaengine_terminate_all(chan);
+ xmit->tail += sg_dma_len(&atmel_port->sg_tx);
+ xmit->tail &= UART_XMIT_SIZE - 1;
+
+ port->icount.tx += sg_dma_len(&atmel_port->sg_tx);
+
+ spin_lock_irq(&atmel_port->lock_tx);
+ async_tx_ack(atmel_port->desc_tx);
+ atmel_port->cookie_tx = -EINVAL;
+ atmel_port->desc_tx = NULL;
+ spin_unlock_irq(&atmel_port->lock_tx);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ /* Do we really need this? */
+ if (!uart_circ_empty(xmit))
+ tasklet_schedule(&atmel_port->tasklet);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void atmel_release_tx_dma(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct dma_chan *chan = atmel_port->chan_tx;
+
+ if (chan) {
+ dmaengine_terminate_all(chan);
+ dma_release_channel(chan);
+ dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1,
+ DMA_MEM_TO_DEV);
+ }
+
+ atmel_port->desc_tx = NULL;
+ atmel_port->chan_tx = NULL;
+ atmel_port->cookie_tx = -EINVAL;
+}
+
+/*
+ * Called from tasklet with TXRDY interrupt is disabled.
+ */
+static void atmel_tx_dma(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct circ_buf *xmit = &port->state->xmit;
+ struct dma_chan *chan = atmel_port->chan_tx;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sg = &atmel_port->sg_tx;
+
+ /* Make sure we have an idle channel */
+ if (atmel_port->desc_tx != NULL)
+ return;
+
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+ /*
+ * DMA is idle now.
+ * Port xmit buffer is already mapped,
+ * and it is one page... Just adjust
+ * offsets and lengths. Since it is a circular buffer,
+ * we have to transmit till the end, and then the rest.
+ * Take the port lock to get a
+ * consistent xmit buffer state.
+ */
+ sg->offset = xmit->tail & (UART_XMIT_SIZE - 1);
+ sg_dma_address(sg) = (sg_dma_address(sg) &
+ ~(UART_XMIT_SIZE - 1))
+ + sg->offset;
+ sg_dma_len(sg) = CIRC_CNT_TO_END(xmit->head,
+ xmit->tail,
+ UART_XMIT_SIZE);
+ BUG_ON(!sg_dma_len(sg));
+
+ desc = dmaengine_prep_slave_sg(chan,
+ sg,
+ 1,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(port->dev, "Failed to send via dma!\n");
+ return;
+ }
+
+ dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
+
+ atmel_port->desc_tx = desc;
+ desc->callback = atmel_complete_tx_dma;
+ desc->callback_param = atmel_port;
+ atmel_port->cookie_tx = dmaengine_submit(desc);
+
+ } else {
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ /* DMA done, stop TX, start RX for RS485 */
+ atmel_start_rx(port);
+ }
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+}
+
+static int atmel_prepare_tx_dma(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ dma_cap_mask_t mask;
+ struct dma_slave_config config;
+ int ret, nent;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx");
+ if (atmel_port->chan_tx == NULL)
+ goto chan_err;
+ dev_info(port->dev, "using %s for tx DMA transfers\n",
+ dma_chan_name(atmel_port->chan_tx));
+
+ spin_lock_init(&atmel_port->lock_tx);
+ sg_init_table(&atmel_port->sg_tx, 1);
+ /* UART circular tx buffer is an aligned page. */
+ BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+ sg_set_page(&atmel_port->sg_tx,
+ virt_to_page(port->state->xmit.buf),
+ UART_XMIT_SIZE,
+ (int)port->state->xmit.buf & ~PAGE_MASK);
+ nent = dma_map_sg(port->dev,
+ &atmel_port->sg_tx,
+ 1,
+ DMA_MEM_TO_DEV);
+
+ if (!nent) {
+ dev_dbg(port->dev, "need to release resource of dma\n");
+ goto chan_err;
+ } else {
+ dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+ sg_dma_len(&atmel_port->sg_tx),
+ port->state->xmit.buf,
+ sg_dma_address(&atmel_port->sg_tx));
+ }
+
+ /* Configure the slave DMA */
+ memset(&config, 0, sizeof(config));
+ config.direction = DMA_MEM_TO_DEV;
+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ config.dst_addr = port->mapbase + ATMEL_US_THR;
+
+ ret = dmaengine_device_control(atmel_port->chan_tx,
+ DMA_SLAVE_CONFIG,
+ (unsigned long)&config);
+ if (ret) {
+ dev_err(port->dev, "DMA tx slave configuration failed\n");
+ goto chan_err;
+ }
+
+ return 0;
+
+chan_err:
+ dev_err(port->dev, "TX channel not available, switch to pio\n");
+ atmel_port->use_dma_tx = 0;
+ if (atmel_port->chan_tx)
+ atmel_release_tx_dma(port);
+ return -EINVAL;
+}
+
+static void atmel_flip_buffer_rx_dma(struct uart_port *port,
+ char *buf, size_t count)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct tty_port *tport = &port->state->port;
+
+ dma_sync_sg_for_cpu(port->dev,
+ &atmel_port->sg_rx,
+ 1,
+ DMA_DEV_TO_MEM);
+
+ tty_insert_flip_string(tport, buf, count);
+
+ dma_sync_sg_for_device(port->dev,
+ &atmel_port->sg_rx,
+ 1,
+ DMA_DEV_TO_MEM);
+ /*
+ * Drop the lock here since it might end up calling
+ * uart_start(), which takes the lock.
+ */
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(tport);
+ spin_lock(&port->lock);
+}
+
+static void atmel_complete_rx_dma(void *arg)
+{
+ struct uart_port *port = arg;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+ tasklet_schedule(&atmel_port->tasklet);
+}
+
+static void atmel_release_rx_dma(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct dma_chan *chan = atmel_port->chan_rx;
+
+ if (chan) {
+ dmaengine_terminate_all(chan);
+ dma_release_channel(chan);
+ dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1,
+ DMA_DEV_TO_MEM);
+ }
+
+ atmel_port->desc_rx = NULL;
+ atmel_port->chan_rx = NULL;
+ atmel_port->cookie_rx = -EINVAL;
+}
+
+static void atmel_rx_from_dma(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct circ_buf *ring = &atmel_port->rx_ring;
+ struct dma_chan *chan = atmel_port->chan_rx;
+ struct dma_tx_state state;
+ enum dma_status dmastat;
+ size_t pending, count;
+
+
+ /* Reset the UART timeout early so that we don't miss one */
+ UART_PUT_CR(port, ATMEL_US_STTTO);
+ dmastat = dmaengine_tx_status(chan,
+ atmel_port->cookie_rx,
+ &state);
+ /* Restart a new tasklet if DMA status is error */
+ if (dmastat == DMA_ERROR) {
+ dev_dbg(port->dev, "Get residue error, restart tasklet\n");
+ UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+ tasklet_schedule(&atmel_port->tasklet);
+ return;
+ }
+ /* current transfer size should no larger than dma buffer */
+ pending = sg_dma_len(&atmel_port->sg_rx) - state.residue;
+ BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx));
+
+ /*
+ * This will take the chars we have so far,
+ * ring->head will record the transfer size, only new bytes come
+ * will insert into the framework.
+ */
+ if (pending > ring->head) {
+ count = pending - ring->head;
+
+ atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count);
+
+ ring->head += count;
+ if (ring->head == sg_dma_len(&atmel_port->sg_rx))
+ ring->head = 0;
+
+ port->icount.rx += count;
+ }
+
+ UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+}
+
+static int atmel_prepare_rx_dma(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct dma_async_tx_descriptor *desc;
+ dma_cap_mask_t mask;
+ struct dma_slave_config config;
+ struct circ_buf *ring;
+ int ret, nent;
+
+ ring = &atmel_port->rx_ring;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx");
+ if (atmel_port->chan_rx == NULL)
+ goto chan_err;
+ dev_info(port->dev, "using %s for rx DMA transfers\n",
+ dma_chan_name(atmel_port->chan_rx));
+
+ spin_lock_init(&atmel_port->lock_rx);
+ sg_init_table(&atmel_port->sg_rx, 1);
+ /* UART circular rx buffer is an aligned page. */
+ BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+ sg_set_page(&atmel_port->sg_rx,
+ virt_to_page(ring->buf),
+ ATMEL_SERIAL_RINGSIZE,
+ (int)ring->buf & ~PAGE_MASK);
+ nent = dma_map_sg(port->dev,
+ &atmel_port->sg_rx,
+ 1,
+ DMA_DEV_TO_MEM);
+
+ if (!nent) {
+ dev_dbg(port->dev, "need to release resource of dma\n");
+ goto chan_err;
+ } else {
+ dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+ sg_dma_len(&atmel_port->sg_rx),
+ ring->buf,
+ sg_dma_address(&atmel_port->sg_rx));
+ }
+
+ /* Configure the slave DMA */
+ memset(&config, 0, sizeof(config));
+ config.direction = DMA_DEV_TO_MEM;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ config.src_addr = port->mapbase + ATMEL_US_RHR;
+
+ ret = dmaengine_device_control(atmel_port->chan_rx,
+ DMA_SLAVE_CONFIG,
+ (unsigned long)&config);
+ if (ret) {
+ dev_err(port->dev, "DMA rx slave configuration failed\n");
+ goto chan_err;
+ }
+ /*
+ * Prepare a cyclic dma transfer, assign 2 descriptors,
+ * each one is half ring buffer size
+ */
+ desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
+ sg_dma_address(&atmel_port->sg_rx),
+ sg_dma_len(&atmel_port->sg_rx),
+ sg_dma_len(&atmel_port->sg_rx)/2,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ desc->callback = atmel_complete_rx_dma;
+ desc->callback_param = port;
+ atmel_port->desc_rx = desc;
+ atmel_port->cookie_rx = dmaengine_submit(desc);
+
+ return 0;
+
+chan_err:
+ dev_err(port->dev, "RX channel not available, switch to pio\n");
+ atmel_port->use_dma_rx = 0;
+ if (atmel_port->chan_rx)
+ atmel_release_rx_dma(port);
+ return -EINVAL;
+}
+
+static void atmel_uart_timer_callback(unsigned long data)
+{
+ struct uart_port *port = (void *)data;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+ tasklet_schedule(&atmel_port->tasklet);
+ mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port));
+}
+
/*
* receive interrupt handler.
*/
@@ -572,7 +1035,7 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_rx(port)) {
+ if (atmel_use_pdc_rx(port)) {
/*
* PDC receive. Just schedule the tasklet and let it
* figure out the details.
@@ -591,6 +1054,13 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending)
atmel_pdc_rxerr(port, pending);
}
+ if (atmel_use_dma_rx(port)) {
+ if (pending & ATMEL_US_TIMEOUT) {
+ UART_PUT_IDR(port, ATMEL_US_TIMEOUT);
+ tasklet_schedule(&atmel_port->tasklet);
+ }
+ }
+
/* Interrupt receive */
if (pending & ATMEL_US_RXRDY)
atmel_rx_chars(port);
@@ -642,11 +1112,31 @@ atmel_handle_status(struct uart_port *port, unsigned int pending,
static irqreturn_t atmel_interrupt(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int status, pending, pass_counter = 0;
+ bool gpio_handled = false;
do {
- status = UART_GET_CSR(port);
+ status = atmel_get_lines_status(port);
pending = status & UART_GET_IMR(port);
+ if (!gpio_handled) {
+ /*
+ * Dealing with GPIO interrupt
+ */
+ if (irq == atmel_port->gpio_irq[UART_GPIO_CTS])
+ pending |= ATMEL_US_CTSIC;
+
+ if (irq == atmel_port->gpio_irq[UART_GPIO_DSR])
+ pending |= ATMEL_US_DSRIC;
+
+ if (irq == atmel_port->gpio_irq[UART_GPIO_RI])
+ pending |= ATMEL_US_RIIC;
+
+ if (irq == atmel_port->gpio_irq[UART_GPIO_DCD])
+ pending |= ATMEL_US_DCDIC;
+
+ gpio_handled = true;
+ }
if (!pending)
break;
@@ -658,10 +1148,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
return pass_counter ? IRQ_HANDLED : IRQ_NONE;
}
+static void atmel_release_tx_pdc(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+
+ dma_unmap_single(port->dev,
+ pdc->dma_addr,
+ pdc->dma_size,
+ DMA_TO_DEVICE);
+}
+
/*
* Called from tasklet with ENDTX and TXBUFE interrupts disabled.
*/
-static void atmel_tx_dma(struct uart_port *port)
+static void atmel_tx_pdc(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
struct circ_buf *xmit = &port->state->xmit;
@@ -710,6 +1211,23 @@ static void atmel_tx_dma(struct uart_port *port)
uart_write_wakeup(port);
}
+static int atmel_prepare_tx_pdc(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+ struct circ_buf *xmit = &port->state->xmit;
+
+ pdc->buf = xmit->buf;
+ pdc->dma_addr = dma_map_single(port->dev,
+ pdc->buf,
+ UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ pdc->dma_size = UART_XMIT_SIZE;
+ pdc->ofs = 0;
+
+ return 0;
+}
+
static void atmel_rx_from_ring(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
@@ -778,7 +1296,23 @@ static void atmel_rx_from_ring(struct uart_port *port)
spin_lock(&port->lock);
}
-static void atmel_rx_from_dma(struct uart_port *port)
+static void atmel_release_rx_pdc(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+ dma_unmap_single(port->dev,
+ pdc->dma_addr,
+ pdc->dma_size,
+ DMA_FROM_DEVICE);
+ kfree(pdc->buf);
+ }
+}
+
+static void atmel_rx_from_pdc(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
struct tty_port *tport = &port->state->port;
@@ -855,6 +1389,45 @@ static void atmel_rx_from_dma(struct uart_port *port)
UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
}
+static int atmel_prepare_rx_pdc(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+ pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
+ if (pdc->buf == NULL) {
+ if (i != 0) {
+ dma_unmap_single(port->dev,
+ atmel_port->pdc_rx[0].dma_addr,
+ PDC_BUFFER_SIZE,
+ DMA_FROM_DEVICE);
+ kfree(atmel_port->pdc_rx[0].buf);
+ }
+ atmel_port->use_pdc_rx = 0;
+ return -ENOMEM;
+ }
+ pdc->dma_addr = dma_map_single(port->dev,
+ pdc->buf,
+ PDC_BUFFER_SIZE,
+ DMA_FROM_DEVICE);
+ pdc->dma_size = PDC_BUFFER_SIZE;
+ pdc->ofs = 0;
+ }
+
+ atmel_port->pdc_rx_idx = 0;
+
+ UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
+ UART_PUT_RCR(port, PDC_BUFFER_SIZE);
+
+ UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
+ UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+
+ return 0;
+}
+
/*
* tasklet handling tty stuff outside the interrupt handler.
*/
@@ -868,10 +1441,7 @@ static void atmel_tasklet_func(unsigned long data)
/* The interrupt handler does not take the lock */
spin_lock(&port->lock);
- if (atmel_use_dma_tx(port))
- atmel_tx_dma(port);
- else
- atmel_tx_chars(port);
+ atmel_port->schedule_tx(port);
status = atmel_port->irq_status;
status_change = status ^ atmel_port->irq_status_prev;
@@ -893,19 +1463,204 @@ static void atmel_tasklet_func(unsigned long data)
atmel_port->irq_status_prev = status;
}
- if (atmel_use_dma_rx(port))
- atmel_rx_from_dma(port);
- else
- atmel_rx_from_ring(port);
+ atmel_port->schedule_rx(port);
spin_unlock(&port->lock);
}
+static int atmel_init_property(struct atmel_uart_port *atmel_port,
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
+
+ if (np) {
+ /* DMA/PDC usage specification */
+ if (of_get_property(np, "atmel,use-dma-rx", NULL)) {
+ if (of_get_property(np, "dmas", NULL)) {
+ atmel_port->use_dma_rx = true;
+ atmel_port->use_pdc_rx = false;
+ } else {
+ atmel_port->use_dma_rx = false;
+ atmel_port->use_pdc_rx = true;
+ }
+ } else {
+ atmel_port->use_dma_rx = false;
+ atmel_port->use_pdc_rx = false;
+ }
+
+ if (of_get_property(np, "atmel,use-dma-tx", NULL)) {
+ if (of_get_property(np, "dmas", NULL)) {
+ atmel_port->use_dma_tx = true;
+ atmel_port->use_pdc_tx = false;
+ } else {
+ atmel_port->use_dma_tx = false;
+ atmel_port->use_pdc_tx = true;
+ }
+ } else {
+ atmel_port->use_dma_tx = false;
+ atmel_port->use_pdc_tx = false;
+ }
+
+ } else {
+ atmel_port->use_pdc_rx = pdata->use_dma_rx;
+ atmel_port->use_pdc_tx = pdata->use_dma_tx;
+ atmel_port->use_dma_rx = false;
+ atmel_port->use_dma_tx = false;
+ }
+
+ return 0;
+}
+
+static void atmel_init_rs485(struct atmel_uart_port *atmel_port,
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
+
+ if (np) {
+ u32 rs485_delay[2];
+ /* rs485 properties */
+ if (of_property_read_u32_array(np, "rs485-rts-delay",
+ rs485_delay, 2) == 0) {
+ struct serial_rs485 *rs485conf = &atmel_port->rs485;
+
+ rs485conf->delay_rts_before_send = rs485_delay[0];
+ rs485conf->delay_rts_after_send = rs485_delay[1];
+ rs485conf->flags = 0;
+
+ if (of_get_property(np, "rs485-rx-during-tx", NULL))
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+ if (of_get_property(np, "linux,rs485-enabled-at-boot-time",
+ NULL))
+ rs485conf->flags |= SER_RS485_ENABLED;
+ }
+ } else {
+ atmel_port->rs485 = pdata->rs485;
+ }
+
+}
+
+static void atmel_set_ops(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+ if (atmel_use_dma_rx(port)) {
+ atmel_port->prepare_rx = &atmel_prepare_rx_dma;
+ atmel_port->schedule_rx = &atmel_rx_from_dma;
+ atmel_port->release_rx = &atmel_release_rx_dma;
+ } else if (atmel_use_pdc_rx(port)) {
+ atmel_port->prepare_rx = &atmel_prepare_rx_pdc;
+ atmel_port->schedule_rx = &atmel_rx_from_pdc;
+ atmel_port->release_rx = &atmel_release_rx_pdc;
+ } else {
+ atmel_port->prepare_rx = NULL;
+ atmel_port->schedule_rx = &atmel_rx_from_ring;
+ atmel_port->release_rx = NULL;
+ }
+
+ if (atmel_use_dma_tx(port)) {
+ atmel_port->prepare_tx = &atmel_prepare_tx_dma;
+ atmel_port->schedule_tx = &atmel_tx_dma;
+ atmel_port->release_tx = &atmel_release_tx_dma;
+ } else if (atmel_use_pdc_tx(port)) {
+ atmel_port->prepare_tx = &atmel_prepare_tx_pdc;
+ atmel_port->schedule_tx = &atmel_tx_pdc;
+ atmel_port->release_tx = &atmel_release_tx_pdc;
+ } else {
+ atmel_port->prepare_tx = NULL;
+ atmel_port->schedule_tx = &atmel_tx_chars;
+ atmel_port->release_tx = NULL;
+ }
+}
+
+/*
+ * Get ip name usart or uart
+ */
+static void atmel_get_ip_name(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ int name = UART_GET_IP_NAME(port);
+ u32 version;
+ int usart, uart;
+ /* usart and uart ascii */
+ usart = 0x55534152;
+ uart = 0x44424755;
+
+ atmel_port->is_usart = false;
+
+ if (name == usart) {
+ dev_dbg(port->dev, "This is usart\n");
+ atmel_port->is_usart = true;
+ } else if (name == uart) {
+ dev_dbg(port->dev, "This is uart\n");
+ atmel_port->is_usart = false;
+ } else {
+ /* fallback for older SoCs: use version field */
+ version = UART_GET_IP_VERSION(port);
+ switch (version) {
+ case 0x302:
+ case 0x10213:
+ dev_dbg(port->dev, "This version is usart\n");
+ atmel_port->is_usart = true;
+ break;
+ case 0x203:
+ case 0x10202:
+ dev_dbg(port->dev, "This version is uart\n");
+ atmel_port->is_usart = false;
+ break;
+ default:
+ dev_err(port->dev, "Not supported ip name nor version, set to uart\n");
+ }
+ }
+}
+
+static void atmel_free_gpio_irq(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ enum mctrl_gpio_idx i;
+
+ for (i = 0; i < UART_GPIO_MAX; i++)
+ if (atmel_port->gpio_irq[i] >= 0)
+ free_irq(atmel_port->gpio_irq[i], port);
+}
+
+static int atmel_request_gpio_irq(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ int *irq = atmel_port->gpio_irq;
+ enum mctrl_gpio_idx i;
+ int err = 0;
+
+ for (i = 0; (i < UART_GPIO_MAX) && !err; i++) {
+ if (irq[i] < 0)
+ continue;
+
+ irq_set_status_flags(irq[i], IRQ_NOAUTOEN);
+ err = request_irq(irq[i], atmel_interrupt, IRQ_TYPE_EDGE_BOTH,
+ "atmel_serial", port);
+ if (err)
+ dev_err(port->dev, "atmel_startup - Can't get %d irq\n",
+ irq[i]);
+ }
+
+ /*
+ * If something went wrong, rollback.
+ */
+ while (err && (--i >= 0))
+ if (irq[i] >= 0)
+ free_irq(irq[i], port);
+
+ return err;
+}
+
/*
* Perform initialization and enable port for reception
*/
static int atmel_startup(struct uart_port *port)
{
+ struct platform_device *pdev = to_platform_device(port->dev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
struct tty_struct *tty = port->state->port.tty;
int retval;
@@ -916,6 +1671,7 @@ static int atmel_startup(struct uart_port *port)
* handle an unexpected interrupt
*/
UART_PUT_IDR(port, -1);
+ atmel_port->ms_irq_enabled = false;
/*
* Allocate the IRQ
@@ -923,74 +1679,36 @@ static int atmel_startup(struct uart_port *port)
retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED,
tty ? tty->name : "atmel_serial", port);
if (retval) {
- printk("atmel_serial: atmel_startup - Can't get irq\n");
+ dev_err(port->dev, "atmel_startup - Can't get irq\n");
return retval;
}
/*
- * Initialize DMA (if necessary)
+ * Get the GPIO lines IRQ
*/
- if (atmel_use_dma_rx(port)) {
- int i;
-
- for (i = 0; i < 2; i++) {
- struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+ retval = atmel_request_gpio_irq(port);
+ if (retval)
+ goto free_irq;
- pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
- if (pdc->buf == NULL) {
- if (i != 0) {
- dma_unmap_single(port->dev,
- atmel_port->pdc_rx[0].dma_addr,
- PDC_BUFFER_SIZE,
- DMA_FROM_DEVICE);
- kfree(atmel_port->pdc_rx[0].buf);
- }
- free_irq(port->irq, port);
- return -ENOMEM;
- }
- pdc->dma_addr = dma_map_single(port->dev,
- pdc->buf,
- PDC_BUFFER_SIZE,
- DMA_FROM_DEVICE);
- pdc->dma_size = PDC_BUFFER_SIZE;
- pdc->ofs = 0;
- }
-
- atmel_port->pdc_rx_idx = 0;
-
- UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
- UART_PUT_RCR(port, PDC_BUFFER_SIZE);
+ /*
+ * Initialize DMA (if necessary)
+ */
+ atmel_init_property(atmel_port, pdev);
- UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
- UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+ if (atmel_port->prepare_rx) {
+ retval = atmel_port->prepare_rx(port);
+ if (retval < 0)
+ atmel_set_ops(port);
}
- if (atmel_use_dma_tx(port)) {
- struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
- struct circ_buf *xmit = &port->state->xmit;
- pdc->buf = xmit->buf;
- pdc->dma_addr = dma_map_single(port->dev,
- pdc->buf,
- UART_XMIT_SIZE,
- DMA_TO_DEVICE);
- pdc->dma_size = UART_XMIT_SIZE;
- pdc->ofs = 0;
- }
-
- /*
- * If there is a specific "open" function (to register
- * control line interrupts)
- */
- if (atmel_open_hook) {
- retval = atmel_open_hook(port);
- if (retval) {
- free_irq(port->irq, port);
- return retval;
- }
+ if (atmel_port->prepare_tx) {
+ retval = atmel_port->prepare_tx(port);
+ if (retval < 0)
+ atmel_set_ops(port);
}
/* Save current CSR for comparison in atmel_tasklet_func() */
- atmel_port->irq_status_prev = UART_GET_CSR(port);
+ atmel_port->irq_status_prev = atmel_get_lines_status(port);
atmel_port->irq_status = atmel_port->irq_status_prev;
/*
@@ -1000,20 +1718,47 @@ static int atmel_startup(struct uart_port *port)
/* enable xmit & rcvr */
UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
- if (atmel_use_dma_rx(port)) {
- /* set UART timeout */
- UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
- UART_PUT_CR(port, ATMEL_US_STTTO);
+ setup_timer(&atmel_port->uart_timer,
+ atmel_uart_timer_callback,
+ (unsigned long)port);
- UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+ if (atmel_use_pdc_rx(port)) {
+ /* set UART timeout */
+ if (!atmel_port->is_usart) {
+ mod_timer(&atmel_port->uart_timer,
+ jiffies + uart_poll_timeout(port));
+ /* set USART timeout */
+ } else {
+ UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
+ UART_PUT_CR(port, ATMEL_US_STTTO);
+
+ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+ }
/* enable PDC controller */
UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+ } else if (atmel_use_dma_rx(port)) {
+ /* set UART timeout */
+ if (!atmel_port->is_usart) {
+ mod_timer(&atmel_port->uart_timer,
+ jiffies + uart_poll_timeout(port));
+ /* set USART timeout */
+ } else {
+ UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
+ UART_PUT_CR(port, ATMEL_US_STTTO);
+
+ UART_PUT_IER(port, ATMEL_US_TIMEOUT);
+ }
} else {
/* enable receive only */
UART_PUT_IER(port, ATMEL_US_RXRDY);
}
return 0;
+
+free_irq:
+ free_irq(port->irq, port);
+
+ return retval;
}
/*
@@ -1022,54 +1767,51 @@ static int atmel_startup(struct uart_port *port)
static void atmel_shutdown(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
/*
- * Ensure everything is stopped.
+ * Prevent any tasklets being scheduled during
+ * cleanup
*/
- atmel_stop_rx(port);
- atmel_stop_tx(port);
+ del_timer_sync(&atmel_port->uart_timer);
/*
- * Shut-down the DMA.
+ * Clear out any scheduled tasklets before
+ * we destroy the buffers
*/
- if (atmel_use_dma_rx(port)) {
- int i;
+ tasklet_kill(&atmel_port->tasklet);
- for (i = 0; i < 2; i++) {
- struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+ /*
+ * Ensure everything is stopped and
+ * disable all interrupts, port and break condition.
+ */
+ atmel_stop_rx(port);
+ atmel_stop_tx(port);
- dma_unmap_single(port->dev,
- pdc->dma_addr,
- pdc->dma_size,
- DMA_FROM_DEVICE);
- kfree(pdc->buf);
- }
- }
- if (atmel_use_dma_tx(port)) {
- struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+ UART_PUT_CR(port, ATMEL_US_RSTSTA);
+ UART_PUT_IDR(port, -1);
- dma_unmap_single(port->dev,
- pdc->dma_addr,
- pdc->dma_size,
- DMA_TO_DEVICE);
- }
/*
- * Disable all interrupts, port and break condition.
+ * Shut-down the DMA.
*/
- UART_PUT_CR(port, ATMEL_US_RSTSTA);
- UART_PUT_IDR(port, -1);
+ if (atmel_port->release_rx)
+ atmel_port->release_rx(port);
+ if (atmel_port->release_tx)
+ atmel_port->release_tx(port);
/*
- * Free the interrupt
+ * Reset ring buffer pointers
*/
- free_irq(port->irq, port);
+ atmel_port->rx_ring.head = 0;
+ atmel_port->rx_ring.tail = 0;
/*
- * If there is a specific "close" function (to unregister
- * control line interrupts)
+ * Free the interrupts
*/
- if (atmel_close_hook)
- atmel_close_hook(port);
+ free_irq(port->irq, port);
+ atmel_free_gpio_irq(port);
+
+ atmel_port->ms_irq_enabled = false;
}
/*
@@ -1080,7 +1822,7 @@ static void atmel_flush_buffer(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_tx(port)) {
+ if (atmel_use_pdc_tx(port)) {
UART_PUT_TCR(port, 0);
atmel_port->pdc_tx.ofs = 0;
}
@@ -1117,7 +1859,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
clk_disable_unprepare(atmel_port->clk);
break;
default:
- printk(KERN_ERR "atmel_serial: unknown pm %d\n", state);
+ dev_err(port->dev, "atmel_serial: unknown pm %d\n", state);
}
}
@@ -1190,10 +1932,10 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
port->read_status_mask = ATMEL_US_OVRE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE);
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= ATMEL_US_RXBRK;
- if (atmel_use_dma_rx(port))
+ if (atmel_use_pdc_rx(port))
/* need to enable error interrupts */
UART_PUT_IER(port, port->read_status_mask);
@@ -1232,13 +1974,10 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
mode &= ~ATMEL_US_USMODE;
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
- dev_dbg(port->dev, "Setting UART to RS485\n");
if ((atmel_port->rs485.delay_rts_after_send) > 0)
UART_PUT_TTGR(port,
atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
- } else {
- dev_dbg(port->dev, "Setting UART to RS232\n");
}
/* set the parity, stop bits and data size */
@@ -1423,38 +2162,6 @@ static struct uart_ops atmel_pops = {
#endif
};
-static void atmel_of_init_port(struct atmel_uart_port *atmel_port,
- struct device_node *np)
-{
- u32 rs485_delay[2];
-
- /* DMA/PDC usage specification */
- if (of_get_property(np, "atmel,use-dma-rx", NULL))
- atmel_port->use_dma_rx = 1;
- else
- atmel_port->use_dma_rx = 0;
- if (of_get_property(np, "atmel,use-dma-tx", NULL))
- atmel_port->use_dma_tx = 1;
- else
- atmel_port->use_dma_tx = 0;
-
- /* rs485 properties */
- if (of_property_read_u32_array(np, "rs485-rts-delay",
- rs485_delay, 2) == 0) {
- struct serial_rs485 *rs485conf = &atmel_port->rs485;
-
- rs485conf->delay_rts_before_send = rs485_delay[0];
- rs485conf->delay_rts_after_send = rs485_delay[1];
- rs485conf->flags = 0;
-
- if (of_get_property(np, "rs485-rx-during-tx", NULL))
- rs485conf->flags |= SER_RS485_RX_DURING_TX;
-
- if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL))
- rs485conf->flags |= SER_RS485_ENABLED;
- }
-}
-
/*
* Configure the port from the platform device resource info.
*/
@@ -1463,15 +2170,12 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
{
int ret;
struct uart_port *port = &atmel_port->uart;
- struct atmel_uart_data *pdata = pdev->dev.platform_data;
+ struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
- if (pdev->dev.of_node) {
- atmel_of_init_port(atmel_port, pdev->dev.of_node);
- } else {
- atmel_port->use_dma_rx = pdata->use_dma_rx;
- atmel_port->use_dma_tx = pdata->use_dma_tx;
- atmel_port->rs485 = pdata->rs485;
- }
+ if (!atmel_init_property(atmel_port, pdev))
+ atmel_set_ops(port);
+
+ atmel_init_rs485(atmel_port, pdev);
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF;
@@ -1516,7 +2220,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
/* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
if (atmel_port->rs485.flags & SER_RS485_ENABLED)
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
- else if (atmel_use_dma_tx(port)) {
+ else if (atmel_use_pdc_tx(port)) {
port->fifosize = PDC_BUFFER_SIZE;
atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
} else {
@@ -1664,7 +2368,7 @@ static int __init atmel_console_init(void)
int ret;
if (atmel_default_console_device) {
struct atmel_uart_data *pdata =
- atmel_default_console_device->dev.platform_data;
+ dev_get_platdata(&atmel_default_console_device->dev);
int id = pdata->num;
struct atmel_uart_port *port = &atmel_ports[id];
@@ -1768,14 +2472,33 @@ static int atmel_serial_resume(struct platform_device *pdev)
#define atmel_serial_resume NULL
#endif
+static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev)
+{
+ enum mctrl_gpio_idx i;
+ struct gpio_desc *gpiod;
+
+ p->gpios = mctrl_gpio_init(dev, 0);
+ if (IS_ERR_OR_NULL(p->gpios))
+ return -1;
+
+ for (i = 0; i < UART_GPIO_MAX; i++) {
+ gpiod = mctrl_gpio_to_gpiod(p->gpios, i);
+ if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN))
+ p->gpio_irq[i] = gpiod_to_irq(gpiod);
+ else
+ p->gpio_irq[i] = -EINVAL;
+ }
+
+ return 0;
+}
+
static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *port;
struct device_node *np = pdev->dev.of_node;
- struct atmel_uart_data *pdata = pdev->dev.platform_data;
+ struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
void *data;
int ret = -ENODEV;
- struct pinctrl *pinctrl;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
@@ -1805,17 +2528,16 @@ static int atmel_serial_probe(struct platform_device *pdev)
port->backup_imr = 0;
port->uart.line = ret;
+ ret = atmel_init_gpios(port, &pdev->dev);
+ if (ret < 0)
+ dev_err(&pdev->dev, "%s",
+ "Failed to initialize GPIOs. The serial port may not work as expected");
+
ret = atmel_init_port(port, pdev);
if (ret)
goto err;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto err;
- }
-
- if (!atmel_use_dma_rx(&port->uart)) {
+ if (!atmel_use_pdc_rx(&port->uart)) {
ret = -ENOMEM;
data = kmalloc(sizeof(struct atmel_uart_char)
* ATMEL_SERIAL_RINGSIZE, GFP_KERNEL);
@@ -1847,6 +2569,11 @@ static int atmel_serial_probe(struct platform_device *pdev)
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
}
+ /*
+ * Get port name of usart or uart
+ */
+ atmel_get_ip_name(&port->uart);
+
return 0;
err_add_port:
@@ -1867,12 +2594,12 @@ static int atmel_serial_remove(struct platform_device *pdev)
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
int ret = 0;
+ tasklet_kill(&atmel_port->tasklet);
+
device_init_wakeup(&pdev->dev, 0);
- platform_set_drvdata(pdev, NULL);
ret = uart_remove_one_port(&atmel_uart, port);
- tasklet_kill(&atmel_port->tasklet);
kfree(atmel_port->rx_ring.buf);
/* "port" is allocated statically, so we shouldn't free it */
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index 6fa2ae77fff..231519022b7 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -29,10 +29,9 @@
#include <linux/sysrq.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
-
-#include <bcm63xx_irq.h>
-#include <bcm63xx_regs.h>
-#include <bcm63xx_io.h>
+#include <linux/serial_bcm63xx.h>
+#include <linux/io.h>
+#include <linux/of.h>
#define BCM63XX_NR_UARTS 2
@@ -81,13 +80,13 @@ static struct uart_port ports[BCM63XX_NR_UARTS];
static inline unsigned int bcm_uart_readl(struct uart_port *port,
unsigned int offset)
{
- return bcm_readl(port->membase + offset);
+ return __raw_readl(port->membase + offset);
}
static inline void bcm_uart_writel(struct uart_port *port,
unsigned int value, unsigned int offset)
{
- bcm_writel(value, port->membase + offset);
+ __raw_writel(value, port->membase + offset);
}
/*
@@ -302,7 +301,9 @@ static void bcm_uart_do_rx(struct uart_port *port)
} while (--max_count);
+ spin_unlock(&port->lock);
tty_flip_buffer_push(tty_port);
+ spin_lock(&port->lock);
}
/*
@@ -566,7 +567,7 @@ static void bcm_uart_set_termios(struct uart_port *port,
port->read_status_mask |= UART_FIFO_FRAMEERR_MASK;
port->read_status_mask |= UART_FIFO_PARERR_MASK;
}
- if (new->c_iflag & (BRKINT))
+ if (new->c_iflag & (IGNBRK | BRKINT))
port->read_status_mask |= UART_FIFO_BRKDET_MASK;
port->ignore_status_mask = 0;
@@ -589,7 +590,7 @@ static int bcm_uart_request_port(struct uart_port *port)
{
unsigned int size;
- size = RSET_UART_SIZE;
+ size = UART_REG_SIZE;
if (!request_mem_region(port->mapbase, size, "bcm63xx")) {
dev_err(port->dev, "Memory region busy\n");
return -EBUSY;
@@ -609,7 +610,7 @@ static int bcm_uart_request_port(struct uart_port *port)
*/
static void bcm_uart_release_port(struct uart_port *port)
{
- release_mem_region(port->mapbase, RSET_UART_SIZE);
+ release_mem_region(port->mapbase, UART_REG_SIZE);
iounmap(port->membase);
}
@@ -806,6 +807,9 @@ static int bcm_uart_probe(struct platform_device *pdev)
struct clk *clk;
int ret;
+ if (pdev->dev.of_node)
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "uart");
+
if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS)
return -EINVAL;
@@ -852,12 +856,17 @@ static int bcm_uart_remove(struct platform_device *pdev)
port = platform_get_drvdata(pdev);
uart_remove_one_port(&bcm_uart_driver, port);
- platform_set_drvdata(pdev, NULL);
/* mark port as free */
ports[pdev->id].membase = 0;
return 0;
}
+static const struct of_device_id bcm63xx_of_match[] = {
+ { .compatible = "brcm,bcm6345-uart" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_of_match);
+
/*
* platform driver stuff
*/
@@ -867,6 +876,7 @@ static struct platform_driver bcm_uart_platform_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "bcm63xx_uart",
+ .of_match_table = bcm63xx_of_match,
},
};
diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c
index 487c173b0f7..4f229703328 100644
--- a/drivers/tty/serial/bfin_sport_uart.c
+++ b/drivers/tty/serial/bfin_sport_uart.c
@@ -161,11 +161,12 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id)
if (!uart_handle_sysrq_char(&up->port, ch))
tty_insert_flip_char(port, ch, TTY_NORMAL);
}
- /* XXX this won't deadlock with lowlat? */
- tty_flip_buffer_push(port);
spin_unlock(&up->port.lock);
+ /* XXX this won't deadlock with lowlat? */
+ tty_flip_buffer_push(port);
+
return IRQ_HANDLED;
}
@@ -765,8 +766,8 @@ static int sport_uart_probe(struct platform_device *pdev)
return -ENOMEM;
}
- ret = peripheral_request_list(
- (unsigned short *)pdev->dev.platform_data, DRV_NAME);
+ ret = peripheral_request_list(dev_get_platdata(&pdev->dev),
+ DRV_NAME);
if (ret) {
dev_err(&pdev->dev,
"Fail to request SPORT peripherals\n");
@@ -842,8 +843,7 @@ static int sport_uart_probe(struct platform_device *pdev)
out_error_unmap:
iounmap(sport->port.membase);
out_error_free_peripherals:
- peripheral_free_list(
- (unsigned short *)pdev->dev.platform_data);
+ peripheral_free_list(dev_get_platdata(&pdev->dev));
out_error_free_mem:
kfree(sport);
bfin_sport_uart_ports[pdev->id] = NULL;
@@ -862,8 +862,7 @@ static int sport_uart_remove(struct platform_device *pdev)
if (sport) {
uart_remove_one_port(&sport_uart_reg, &sport->port);
iounmap(sport->port.membase);
- peripheral_free_list(
- (unsigned short *)pdev->dev.platform_data);
+ peripheral_free_list(dev_get_platdata(&pdev->dev));
kfree(sport);
bfin_sport_uart_ports[pdev->id] = NULL;
}
@@ -883,7 +882,7 @@ static struct platform_driver sport_uart_driver = {
};
#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
-static __initdata struct early_platform_driver early_sport_uart_driver = {
+static struct early_platform_driver early_sport_uart_driver __initdata = {
.class_str = CLASS_BFIN_SPORT_CONSOLE,
.pdrv = &sport_uart_driver,
.requested_id = EARLY_PLATFORM_ID_UNSET,
diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c
index 26a3be7ced7..ac86a20992e 100644
--- a/drivers/tty/serial/bfin_uart.c
+++ b/drivers/tty/serial/bfin_uart.c
@@ -41,10 +41,6 @@
# undef CONFIG_EARLY_PRINTK
#endif
-#ifdef CONFIG_SERIAL_BFIN_MODULE
-# undef CONFIG_EARLY_PRINTK
-#endif
-
/* UART name and device definitions */
#define BFIN_SERIAL_DEV_NAME "ttyBF"
#define BFIN_SERIAL_MAJOR 204
@@ -684,7 +680,7 @@ static int bfin_serial_startup(struct uart_port *port)
default:
uart_dma_ch_rx = uart_dma_ch_tx = 0;
break;
- };
+ }
if (uart_dma_ch_rx &&
request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) {
@@ -730,7 +726,7 @@ static int bfin_serial_startup(struct uart_port *port)
#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
if (uart->cts_pin >= 0) {
if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int,
- IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) {
+ 0, "BFIN_UART_MODEM_STATUS", uart)) {
uart->cts_pin = -1;
dev_info(port->dev, "Unable to attach BlackFin UART Modem Status interrupt.\n");
}
@@ -769,7 +765,7 @@ static void bfin_serial_shutdown(struct uart_port *port)
break;
default:
break;
- };
+ }
#endif
free_irq(uart->rx_irq, uart);
free_irq(uart->tx_irq, uart);
@@ -837,7 +833,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
port->read_status_mask = OE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= (FE | PE);
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= BI;
/*
@@ -1180,7 +1176,7 @@ bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int c
* don't let the common infrastructure play with things. (see calls to setup
* & earlysetup in ./kernel/printk.c:register_console()
*/
-static struct __initdata console bfin_early_serial_console = {
+static struct console bfin_early_serial_console __initdata = {
.name = "early_BFuart",
.write = bfin_earlyprintk_console_write,
.device = uart_console_device,
@@ -1244,7 +1240,8 @@ static int bfin_serial_probe(struct platform_device *pdev)
*/
#endif
ret = peripheral_request_list(
- (unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+ dev_get_platdata(&pdev->dev),
+ DRIVER_NAME);
if (ret) {
dev_err(&pdev->dev,
"fail to request bfin serial peripherals\n");
@@ -1361,8 +1358,7 @@ static int bfin_serial_probe(struct platform_device *pdev)
out_error_unmap:
iounmap(uart->port.membase);
out_error_free_peripherals:
- peripheral_free_list(
- (unsigned short *)pdev->dev.platform_data);
+ peripheral_free_list(dev_get_platdata(&pdev->dev));
out_error_free_mem:
kfree(uart);
bfin_serial_ports[pdev->id] = NULL;
@@ -1380,8 +1376,7 @@ static int bfin_serial_remove(struct platform_device *pdev)
if (uart) {
uart_remove_one_port(&bfin_serial_reg, &uart->port);
iounmap(uart->port.membase);
- peripheral_free_list(
- (unsigned short *)pdev->dev.platform_data);
+ peripheral_free_list(dev_get_platdata(&pdev->dev));
kfree(uart);
bfin_serial_ports[pdev->id] = NULL;
}
@@ -1401,7 +1396,7 @@ static struct platform_driver bfin_serial_driver = {
};
#if defined(CONFIG_SERIAL_BFIN_CONSOLE)
-static __initdata struct early_platform_driver early_bfin_serial_driver = {
+static struct early_platform_driver early_bfin_serial_driver __initdata = {
.class_str = CLASS_BFIN_CONSOLE,
.pdrv = &bfin_serial_driver,
.requested_id = EARLY_PLATFORM_ID_UNSET,
@@ -1435,8 +1430,8 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
return -ENOENT;
}
- ret = peripheral_request_list(
- (unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+ ret = peripheral_request_list(dev_get_platdata(&pdev->dev),
+ DRIVER_NAME);
if (ret) {
dev_err(&pdev->dev,
"fail to request bfin serial peripherals\n");
@@ -1466,8 +1461,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev)
return 0;
out_error_free_peripherals:
- peripheral_free_list(
- (unsigned short *)pdev->dev.platform_data);
+ peripheral_free_list(dev_get_platdata(&pdev->dev));
return ret;
}
@@ -1480,7 +1474,7 @@ static struct platform_driver bfin_earlyprintk_driver = {
},
};
-static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = {
+static struct early_platform_driver early_bfin_earlyprintk_driver __initdata = {
.class_str = CLASS_BFIN_EARLYPRINTK,
.pdrv = &bfin_earlyprintk_driver,
.requested_id = EARLY_PLATFORM_ID_UNSET,
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index bfb17968c8d..14aaea0d413 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -21,44 +21,66 @@
#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
-#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/io.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/ioport.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
-#include <mach/hardware.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/clps711x.h>
-#define UART_CLPS711X_NAME "uart-clps711x"
+#define UART_CLPS711X_DEVNAME "ttyCL"
#define UART_CLPS711X_NR 2
#define UART_CLPS711X_MAJOR 204
#define UART_CLPS711X_MINOR 40
-#define UBRLCR(port) ((port)->line ? UBRLCR2 : UBRLCR1)
-#define UARTDR(port) ((port)->line ? UARTDR2 : UARTDR1)
-#define SYSFLG(port) ((port)->line ? SYSFLG2 : SYSFLG1)
-#define SYSCON(port) ((port)->line ? SYSCON2 : SYSCON1)
-#define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1)
-#define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1)
+#define UARTDR_OFFSET (0x00)
+#define UBRLCR_OFFSET (0x40)
+
+#define UARTDR_FRMERR (1 << 8)
+#define UARTDR_PARERR (1 << 9)
+#define UARTDR_OVERR (1 << 10)
+
+#define UBRLCR_BAUD_MASK ((1 << 12) - 1)
+#define UBRLCR_BREAK (1 << 12)
+#define UBRLCR_PRTEN (1 << 13)
+#define UBRLCR_EVENPRT (1 << 14)
+#define UBRLCR_XSTOP (1 << 15)
+#define UBRLCR_FIFOEN (1 << 16)
+#define UBRLCR_WRDLEN5 (0 << 17)
+#define UBRLCR_WRDLEN6 (1 << 17)
+#define UBRLCR_WRDLEN7 (2 << 17)
+#define UBRLCR_WRDLEN8 (3 << 17)
+#define UBRLCR_WRDLEN_MASK (3 << 17)
struct clps711x_port {
- struct uart_driver uart;
- struct clk *uart_clk;
- struct uart_port port[UART_CLPS711X_NR];
- int tx_enabled[UART_CLPS711X_NR];
-#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
- struct console console;
-#endif
+ struct uart_port port;
+ unsigned int tx_enabled;
+ int rx_irq;
+ struct regmap *syscon;
+ bool use_ms;
+};
+
+static struct uart_driver clps711x_uart = {
+ .owner = THIS_MODULE,
+ .driver_name = UART_CLPS711X_DEVNAME,
+ .dev_name = UART_CLPS711X_DEVNAME,
+ .major = UART_CLPS711X_MAJOR,
+ .minor = UART_CLPS711X_MINOR,
+ .nr = UART_CLPS711X_NR,
};
static void uart_clps711x_stop_tx(struct uart_port *port)
{
struct clps711x_port *s = dev_get_drvdata(port->dev);
- if (s->tx_enabled[port->line]) {
- disable_irq(TX_IRQ(port));
- s->tx_enabled[port->line] = 0;
+ if (s->tx_enabled) {
+ disable_irq(port->irq);
+ s->tx_enabled = 0;
}
}
@@ -66,33 +88,27 @@ static void uart_clps711x_start_tx(struct uart_port *port)
{
struct clps711x_port *s = dev_get_drvdata(port->dev);
- if (!s->tx_enabled[port->line]) {
- enable_irq(TX_IRQ(port));
- s->tx_enabled[port->line] = 1;
+ if (!s->tx_enabled) {
+ s->tx_enabled = 1;
+ enable_irq(port->irq);
}
}
-static void uart_clps711x_stop_rx(struct uart_port *port)
-{
- disable_irq(RX_IRQ(port));
-}
-
-static void uart_clps711x_enable_ms(struct uart_port *port)
-{
- /* Do nothing */
-}
-
static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
- unsigned int status, ch, flg;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ unsigned int status, flg;
+ u16 ch;
for (;;) {
- status = clps_readl(SYSFLG(port));
- if (status & SYSFLG_URXFE)
+ u32 sysflg = 0;
+
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ if (sysflg & SYSFLG_URXFE)
break;
- ch = clps_readw(UARTDR(port));
+ ch = readw(port->membase + UARTDR_OFFSET);
status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR);
ch &= 0xff;
@@ -138,23 +154,29 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
struct circ_buf *xmit = &port->state->xmit;
if (port->x_char) {
- clps_writew(port->x_char, UARTDR(port));
+ writew(port->x_char, port->membase + UARTDR_OFFSET);
port->icount.tx++;
port->x_char = 0;
return IRQ_HANDLED;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- disable_irq_nosync(TX_IRQ(port));
- s->tx_enabled[port->line] = 0;
+ if (s->tx_enabled) {
+ disable_irq_nosync(port->irq);
+ s->tx_enabled = 0;
+ }
return IRQ_HANDLED;
}
while (!uart_circ_empty(xmit)) {
- clps_writew(xmit->buf[xmit->tail], UARTDR(port));
+ u32 sysflg = 0;
+
+ writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
- if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF))
+
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ if (sysflg & SYSFLG_UTXFF)
break;
}
@@ -166,20 +188,28 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
static unsigned int uart_clps711x_tx_empty(struct uart_port *port)
{
- return (clps_readl(SYSFLG(port) & SYSFLG_UBUSY)) ? 0 : TIOCSER_TEMT;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ u32 sysflg = 0;
+
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+
+ return (sysflg & SYSFLG_UBUSY) ? 0 : TIOCSER_TEMT;
}
static unsigned int uart_clps711x_get_mctrl(struct uart_port *port)
{
- unsigned int status, result = 0;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ unsigned int result = 0;
+
+ if (s->use_ms) {
+ u32 sysflg = 0;
- if (port->line == 0) {
- status = clps_readl(SYSFLG1);
- if (status & SYSFLG1_DCD)
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ if (sysflg & SYSFLG1_DCD)
result |= TIOCM_CAR;
- if (status & SYSFLG1_DSR)
+ if (sysflg & SYSFLG1_DSR)
result |= TIOCM_DSR;
- if (status & SYSFLG1_CTS)
+ if (sysflg & SYSFLG1_CTS)
result |= TIOCM_CTS;
} else
result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
@@ -194,65 +224,53 @@ static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl)
static void uart_clps711x_break_ctl(struct uart_port *port, int break_state)
{
- unsigned long flags;
unsigned int ubrlcr;
- spin_lock_irqsave(&port->lock, flags);
-
- ubrlcr = clps_readl(UBRLCR(port));
+ ubrlcr = readl(port->membase + UBRLCR_OFFSET);
if (break_state)
ubrlcr |= UBRLCR_BREAK;
else
ubrlcr &= ~UBRLCR_BREAK;
- clps_writel(ubrlcr, UBRLCR(port));
+ writel(ubrlcr, port->membase + UBRLCR_OFFSET);
+}
+
+static void uart_clps711x_set_ldisc(struct uart_port *port, int ld)
+{
+ if (!port->line) {
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
- spin_unlock_irqrestore(&port->lock, flags);
+ regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON1_SIREN,
+ (ld == N_IRDA) ? SYSCON1_SIREN : 0);
+ }
}
static int uart_clps711x_startup(struct uart_port *port)
{
struct clps711x_port *s = dev_get_drvdata(port->dev);
- int ret;
-
- s->tx_enabled[port->line] = 1;
- /* Allocate the IRQs */
- ret = devm_request_irq(port->dev, TX_IRQ(port), uart_clps711x_int_tx,
- 0, UART_CLPS711X_NAME " TX", port);
- if (ret)
- return ret;
-
- ret = devm_request_irq(port->dev, RX_IRQ(port), uart_clps711x_int_rx,
- 0, UART_CLPS711X_NAME " RX", port);
- if (ret) {
- devm_free_irq(port->dev, TX_IRQ(port), port);
- return ret;
- }
/* Disable break */
- clps_writel(clps_readl(UBRLCR(port)) & ~UBRLCR_BREAK, UBRLCR(port));
+ writel(readl(port->membase + UBRLCR_OFFSET) & ~UBRLCR_BREAK,
+ port->membase + UBRLCR_OFFSET);
/* Enable the port */
- clps_writel(clps_readl(SYSCON(port)) | SYSCON_UARTEN, SYSCON(port));
-
- return 0;
+ return regmap_update_bits(s->syscon, SYSCON_OFFSET,
+ SYSCON_UARTEN, SYSCON_UARTEN);
}
static void uart_clps711x_shutdown(struct uart_port *port)
{
- /* Free the interrupts */
- devm_free_irq(port->dev, TX_IRQ(port), port);
- devm_free_irq(port->dev, RX_IRQ(port), port);
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
/* Disable the port */
- clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port));
+ regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
}
static void uart_clps711x_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
- unsigned int ubrlcr, baud, quot;
- unsigned long flags;
+ u32 ubrlcr;
+ unsigned int baud, quot;
/* Mask termios capabilities we don't support */
termios->c_cflag &= ~CMSPAR;
@@ -291,8 +309,6 @@ static void uart_clps711x_set_termios(struct uart_port *port,
/* Enable FIFO */
ubrlcr |= UBRLCR_FIFOEN;
- spin_lock_irqsave(&port->lock, flags);
-
/* Set read status mask */
port->read_status_mask = UARTDR_OVERR;
if (termios->c_iflag & INPCK)
@@ -306,9 +322,7 @@ static void uart_clps711x_set_termios(struct uart_port *port,
uart_update_timeout(port, termios->c_cflag, baud);
- clps_writel(ubrlcr | (quot - 1), UBRLCR(port));
-
- spin_unlock_irqrestore(&port->lock, flags);
+ writel(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET);
}
static const char *uart_clps711x_type(struct uart_port *port)
@@ -322,14 +336,12 @@ static void uart_clps711x_config_port(struct uart_port *port, int flags)
port->type = PORT_CLPS711X;
}
-static void uart_clps711x_release_port(struct uart_port *port)
+static void uart_clps711x_nop_void(struct uart_port *port)
{
- /* Do nothing */
}
-static int uart_clps711x_request_port(struct uart_port *port)
+static int uart_clps711x_nop_int(struct uart_port *port)
{
- /* Do nothing */
return 0;
}
@@ -339,148 +351,190 @@ static const struct uart_ops uart_clps711x_ops = {
.get_mctrl = uart_clps711x_get_mctrl,
.stop_tx = uart_clps711x_stop_tx,
.start_tx = uart_clps711x_start_tx,
- .stop_rx = uart_clps711x_stop_rx,
- .enable_ms = uart_clps711x_enable_ms,
+ .stop_rx = uart_clps711x_nop_void,
+ .enable_ms = uart_clps711x_nop_void,
.break_ctl = uart_clps711x_break_ctl,
+ .set_ldisc = uart_clps711x_set_ldisc,
.startup = uart_clps711x_startup,
.shutdown = uart_clps711x_shutdown,
.set_termios = uart_clps711x_set_termios,
.type = uart_clps711x_type,
.config_port = uart_clps711x_config_port,
- .release_port = uart_clps711x_release_port,
- .request_port = uart_clps711x_request_port,
+ .release_port = uart_clps711x_nop_void,
+ .request_port = uart_clps711x_nop_int,
};
#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
static void uart_clps711x_console_putchar(struct uart_port *port, int ch)
{
- while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF)
- barrier();
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ u32 sysflg = 0;
- clps_writew(ch, UARTDR(port));
+ /* Wait for FIFO is not full */
+ do {
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ } while (sysflg & SYSFLG_UTXFF);
+
+ writew(ch, port->membase + UARTDR_OFFSET);
}
static void uart_clps711x_console_write(struct console *co, const char *c,
unsigned n)
{
- struct clps711x_port *s = (struct clps711x_port *)co->data;
- struct uart_port *port = &s->port[co->index];
- u32 syscon;
-
- /* Ensure that the port is enabled */
- syscon = clps_readl(SYSCON(port));
- clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
+ struct uart_port *port = clps711x_uart.state[co->index].uart_port;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ u32 sysflg = 0;
uart_console_write(port, c, n, uart_clps711x_console_putchar);
/* Wait for transmitter to become empty */
- while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY)
- barrier();
-
- /* Restore the uart state */
- clps_writel(syscon, SYSCON(port));
+ do {
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ } while (sysflg & SYSFLG_UBUSY);
}
-static void uart_clps711x_console_get_options(struct uart_port *port,
- int *baud, int *parity,
- int *bits)
+static int uart_clps711x_console_setup(struct console *co, char *options)
{
- if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
- unsigned int ubrlcr, quot;
+ int baud = 38400, bits = 8, parity = 'n', flow = 'n';
+ int ret, index = co->index;
+ struct clps711x_port *s;
+ struct uart_port *port;
+ unsigned int quot;
+ u32 ubrlcr;
- ubrlcr = clps_readl(UBRLCR(port));
+ if (index < 0 || index >= UART_CLPS711X_NR)
+ return -EINVAL;
- *parity = 'n';
- if (ubrlcr & UBRLCR_PRTEN) {
- if (ubrlcr & UBRLCR_EVENPRT)
- *parity = 'e';
- else
- *parity = 'o';
- }
+ port = clps711x_uart.state[index].uart_port;
+ if (!port)
+ return -ENODEV;
- if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
- *bits = 7;
- else
- *bits = 8;
+ s = dev_get_drvdata(port->dev);
- quot = ubrlcr & UBRLCR_BAUD_MASK;
- *baud = port->uartclk / (16 * (quot + 1));
- }
-}
+ if (!options) {
+ u32 syscon = 0;
-static int uart_clps711x_console_setup(struct console *co, char *options)
-{
- int baud = 38400, bits = 8, parity = 'n', flow = 'n';
- struct clps711x_port *s = (struct clps711x_port *)co->data;
- struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0];
+ regmap_read(s->syscon, SYSCON_OFFSET, &syscon);
+ if (syscon & SYSCON_UARTEN) {
+ ubrlcr = readl(port->membase + UBRLCR_OFFSET);
- if (options)
+ if (ubrlcr & UBRLCR_PRTEN) {
+ if (ubrlcr & UBRLCR_EVENPRT)
+ parity = 'e';
+ else
+ parity = 'o';
+ }
+
+ if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
+ bits = 7;
+
+ quot = ubrlcr & UBRLCR_BAUD_MASK;
+ baud = port->uartclk / (16 * (quot + 1));
+ }
+ } else
uart_parse_options(options, &baud, &parity, &bits, &flow);
- else
- uart_clps711x_console_get_options(port, &baud, &parity, &bits);
- return uart_set_options(port, co, baud, parity, bits, flow);
+ ret = uart_set_options(port, co, baud, parity, bits, flow);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(s->syscon, SYSCON_OFFSET,
+ SYSCON_UARTEN, SYSCON_UARTEN);
}
+
+static struct console clps711x_console = {
+ .name = UART_CLPS711X_DEVNAME,
+ .device = uart_console_device,
+ .write = uart_clps711x_console_write,
+ .setup = uart_clps711x_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
#endif
static int uart_clps711x_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
+ int ret, index = np ? of_alias_get_id(np, "serial") : pdev->id;
struct clps711x_port *s;
- int ret, i;
+ struct resource *res;
+ struct clk *uart_clk;
- s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL);
- if (!s) {
- dev_err(&pdev->dev, "Error allocating port structure\n");
+ if (index < 0 || index >= UART_CLPS711X_NR)
+ return -EINVAL;
+
+ s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
+ if (!s)
return -ENOMEM;
+
+ uart_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(uart_clk))
+ return PTR_ERR(uart_clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ s->port.membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(s->port.membase))
+ return PTR_ERR(s->port.membase);
+
+ s->port.irq = platform_get_irq(pdev, 0);
+ if (IS_ERR_VALUE(s->port.irq))
+ return s->port.irq;
+
+ s->rx_irq = platform_get_irq(pdev, 1);
+ if (IS_ERR_VALUE(s->rx_irq))
+ return s->rx_irq;
+
+ if (!np) {
+ char syscon_name[9];
+
+ sprintf(syscon_name, "syscon.%i", index + 1);
+ s->syscon = syscon_regmap_lookup_by_pdevname(syscon_name);
+ if (IS_ERR(s->syscon))
+ return PTR_ERR(s->syscon);
+
+ s->use_ms = !index;
+ } else {
+ s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(s->syscon))
+ return PTR_ERR(s->syscon);
+
+ if (!index)
+ s->use_ms = of_property_read_bool(np, "uart-use-ms");
}
+
+ s->port.line = index;
+ s->port.dev = &pdev->dev;
+ s->port.iotype = UPIO_MEM32;
+ s->port.mapbase = res->start;
+ s->port.type = PORT_CLPS711X;
+ s->port.fifosize = 16;
+ s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
+ s->port.uartclk = clk_get_rate(uart_clk);
+ s->port.ops = &uart_clps711x_ops;
+
platform_set_drvdata(pdev, s);
- s->uart_clk = devm_clk_get(&pdev->dev, "uart");
- if (IS_ERR(s->uart_clk)) {
- dev_err(&pdev->dev, "Can't get UART clocks\n");
- ret = PTR_ERR(s->uart_clk);
- goto err_out;
- }
+ ret = uart_add_one_port(&clps711x_uart, &s->port);
+ if (ret)
+ return ret;
- s->uart.owner = THIS_MODULE;
- s->uart.dev_name = "ttyCL";
- s->uart.major = UART_CLPS711X_MAJOR;
- s->uart.minor = UART_CLPS711X_MINOR;
- s->uart.nr = UART_CLPS711X_NR;
-#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
- s->uart.cons = &s->console;
- s->uart.cons->device = uart_console_device;
- s->uart.cons->write = uart_clps711x_console_write;
- s->uart.cons->setup = uart_clps711x_console_setup;
- s->uart.cons->flags = CON_PRINTBUFFER;
- s->uart.cons->index = -1;
- s->uart.cons->data = s;
- strcpy(s->uart.cons->name, "ttyCL");
-#endif
- ret = uart_register_driver(&s->uart);
- if (ret) {
- dev_err(&pdev->dev, "Registering UART driver failed\n");
- devm_clk_put(&pdev->dev, s->uart_clk);
- goto err_out;
- }
+ /* Disable port */
+ if (!uart_console(&s->port))
+ regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
- for (i = 0; i < UART_CLPS711X_NR; i++) {
- s->port[i].line = i;
- s->port[i].dev = &pdev->dev;
- s->port[i].irq = TX_IRQ(&s->port[i]);
- s->port[i].iobase = SYSCON(&s->port[i]);
- s->port[i].type = PORT_CLPS711X;
- s->port[i].fifosize = 16;
- s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
- s->port[i].uartclk = clk_get_rate(s->uart_clk);
- s->port[i].ops = &uart_clps711x_ops;
- WARN_ON(uart_add_one_port(&s->uart, &s->port[i]));
- }
+ s->tx_enabled = 1;
- return 0;
+ ret = devm_request_irq(&pdev->dev, s->port.irq, uart_clps711x_int_tx, 0,
+ dev_name(&pdev->dev), &s->port);
+ if (ret) {
+ uart_remove_one_port(&clps711x_uart, &s->port);
+ return ret;
+ }
-err_out:
- platform_set_drvdata(pdev, NULL);
+ ret = devm_request_irq(&pdev->dev, s->rx_irq, uart_clps711x_int_rx, 0,
+ dev_name(&pdev->dev), &s->port);
+ if (ret)
+ uart_remove_one_port(&clps711x_uart, &s->port);
return ret;
}
@@ -488,41 +542,47 @@ err_out:
static int uart_clps711x_remove(struct platform_device *pdev)
{
struct clps711x_port *s = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < UART_CLPS711X_NR; i++)
- uart_remove_one_port(&s->uart, &s->port[i]);
-
- devm_clk_put(&pdev->dev, s->uart_clk);
- uart_unregister_driver(&s->uart);
- platform_set_drvdata(pdev, NULL);
- return 0;
+ return uart_remove_one_port(&clps711x_uart, &s->port);
}
-static struct platform_driver clps711x_uart_driver = {
+static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {
+ { .compatible = "cirrus,clps711x-uart", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids);
+
+static struct platform_driver clps711x_uart_platform = {
.driver = {
- .name = UART_CLPS711X_NAME,
- .owner = THIS_MODULE,
+ .name = "clps711x-uart",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(clps711x_uart_dt_ids),
},
.probe = uart_clps711x_probe,
.remove = uart_clps711x_remove,
};
-module_platform_driver(clps711x_uart_driver);
-
-static struct platform_device clps711x_uart_device = {
- .name = UART_CLPS711X_NAME,
-};
static int __init uart_clps711x_init(void)
{
- return platform_device_register(&clps711x_uart_device);
+ int ret;
+
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+ clps711x_uart.cons = &clps711x_console;
+ clps711x_console.data = &clps711x_uart;
+#endif
+
+ ret = uart_register_driver(&clps711x_uart);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&clps711x_uart_platform);
}
module_init(uart_clps711x_init);
static void __exit uart_clps711x_exit(void)
{
- platform_device_unregister(&clps711x_uart_device);
+ platform_driver_unregister(&clps711x_uart_platform);
+ uart_unregister_driver(&clps711x_uart);
}
module_exit(uart_clps711x_exit);
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index f7672cae532..aa60e6d13ec 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -41,6 +41,8 @@
#include <linux/bootmem.h>
#include <linux/dma-mapping.h>
#include <linux/fs_uart_pd.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
@@ -969,7 +971,7 @@ static void cpm_uart_config_port(struct uart_port *port, int flags)
* Note that this is called with interrupts already disabled
*/
static void cpm_uart_early_write(struct uart_cpm_port *pinfo,
- const char *string, u_int count)
+ const char *string, u_int count, bool handle_linefeed)
{
unsigned int i;
cbd_t __iomem *bdp, *bdbase;
@@ -1011,7 +1013,7 @@ static void cpm_uart_early_write(struct uart_cpm_port *pinfo,
bdp++;
/* if a LF, also do CR... */
- if (*string == 10) {
+ if (handle_linefeed && *string == 10) {
while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0)
;
@@ -1109,7 +1111,7 @@ static void cpm_put_poll_char(struct uart_port *port,
static char ch[2];
ch[0] = (char)c;
- cpm_uart_early_write(pinfo, ch, 1);
+ cpm_uart_early_write(pinfo, ch, 1, false);
}
#endif /* CONFIG_CONSOLE_POLL */
@@ -1207,14 +1209,38 @@ static int cpm_uart_init_port(struct device_node *np,
pinfo->port.fifosize = pinfo->tx_nrfifos * pinfo->tx_fifosize;
spin_lock_init(&pinfo->port.lock);
- pinfo->port.irq = of_irq_to_resource(np, 0, NULL);
+ pinfo->port.irq = irq_of_parse_and_map(np, 0);
if (pinfo->port.irq == NO_IRQ) {
ret = -EINVAL;
goto out_pram;
}
- for (i = 0; i < NUM_GPIOS; i++)
- pinfo->gpios[i] = of_get_gpio(np, i);
+ for (i = 0; i < NUM_GPIOS; i++) {
+ int gpio;
+
+ pinfo->gpios[i] = -1;
+
+ gpio = of_get_gpio(np, i);
+
+ if (gpio_is_valid(gpio)) {
+ ret = gpio_request(gpio, "cpm_uart");
+ if (ret) {
+ pr_err("can't request gpio #%d: %d\n", i, ret);
+ continue;
+ }
+ if (i == GPIO_RTS || i == GPIO_DTR)
+ ret = gpio_direction_output(gpio, 0);
+ else
+ ret = gpio_direction_input(gpio);
+ if (ret) {
+ pr_err("can't set direction for gpio #%d: %d\n",
+ i, ret);
+ gpio_free(gpio);
+ continue;
+ }
+ pinfo->gpios[i] = gpio;
+ }
+ }
#ifdef CONFIG_PPC_EARLY_DEBUG_CPM
udbg_putc = NULL;
@@ -1249,7 +1275,7 @@ static void cpm_uart_console_write(struct console *co, const char *s,
spin_lock_irqsave(&pinfo->port.lock, flags);
}
- cpm_uart_early_write(pinfo, s, count);
+ cpm_uart_early_write(pinfo, s, count, true);
if (unlikely(nolock)) {
local_irq_restore(flags);
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
index 18f79575894..6d3b22e9324 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
@@ -29,7 +29,6 @@
#include <linux/tty.h>
#include <linux/gfp.h>
#include <linux/ioport.h>
-#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
@@ -45,6 +44,7 @@
#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include "cpm_uart.h"
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
index a4927e66e74..f46d2ca8720 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
@@ -29,7 +29,6 @@
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c
index 477f22f773f..d567ac5d3af 100644
--- a/drivers/tty/serial/crisv10.c
+++ b/drivers/tty/serial/crisv10.c
@@ -286,7 +286,6 @@ static struct e100_serial rs_table[] = {
#endif
}, /* ttyS0 */
-#ifndef CONFIG_SVINTO_SIM
{ .baud = DEF_BAUD,
.ioport = (unsigned char *)R_SERIAL1_CTRL,
.irq = 1U << 16, /* uses DMA 8 and 9 */
@@ -447,7 +446,6 @@ static struct e100_serial rs_table[] = {
.dma_in_enabled = 0
#endif
} /* ttyS3 */
-#endif
};
@@ -1035,7 +1033,6 @@ cflag_to_etrax_baud(unsigned int cflag)
static inline void
e100_dtr(struct e100_serial *info, int set)
{
-#ifndef CONFIG_SVINTO_SIM
unsigned char mask = e100_modem_pins[info->line].dtr_mask;
#ifdef SERIAL_DEBUG_IO
@@ -1060,7 +1057,6 @@ e100_dtr(struct e100_serial *info, int set)
info->line, *e100_modem_pins[info->line].dtr_shadow,
E100_DTR_GET(info));
#endif
-#endif
}
/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
@@ -1069,7 +1065,6 @@ e100_dtr(struct e100_serial *info, int set)
static inline void
e100_rts(struct e100_serial *info, int set)
{
-#ifndef CONFIG_SVINTO_SIM
unsigned long flags;
local_irq_save(flags);
info->rx_ctrl &= ~E100_RTS_MASK;
@@ -1079,7 +1074,6 @@ e100_rts(struct e100_serial *info, int set)
#ifdef SERIAL_DEBUG_IO
printk("ser%i rts %i\n", info->line, set);
#endif
-#endif
}
@@ -1087,7 +1081,6 @@ e100_rts(struct e100_serial *info, int set)
static inline void
e100_ri_out(struct e100_serial *info, int set)
{
-#ifndef CONFIG_SVINTO_SIM
/* RI is active low */
{
unsigned char mask = e100_modem_pins[info->line].ri_mask;
@@ -1099,12 +1092,10 @@ e100_ri_out(struct e100_serial *info, int set)
*e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow;
local_irq_restore(flags);
}
-#endif
}
static inline void
e100_cd_out(struct e100_serial *info, int set)
{
-#ifndef CONFIG_SVINTO_SIM
/* CD is active low */
{
unsigned char mask = e100_modem_pins[info->line].cd_mask;
@@ -1116,27 +1107,22 @@ e100_cd_out(struct e100_serial *info, int set)
*e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow;
local_irq_restore(flags);
}
-#endif
}
static inline void
e100_disable_rx(struct e100_serial *info)
{
-#ifndef CONFIG_SVINTO_SIM
/* disable the receiver */
info->ioport[REG_REC_CTRL] =
(info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
-#endif
}
static inline void
e100_enable_rx(struct e100_serial *info)
{
-#ifndef CONFIG_SVINTO_SIM
/* enable the receiver */
info->ioport[REG_REC_CTRL] =
(info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
-#endif
}
/* the rx DMA uses both the dma_descr and the dma_eop interrupts */
@@ -1554,24 +1540,6 @@ transmit_chars_dma(struct e100_serial *info)
unsigned int c, sentl;
struct etrax_dma_descr *descr;
-#ifdef CONFIG_SVINTO_SIM
- /* This will output too little if tail is not 0 always since
- * we don't reloop to send the other part. Anyway this SHOULD be a
- * no-op - transmit_chars_dma would never really be called during sim
- * since rs_write does not write into the xmit buffer then.
- */
- if (info->xmit.tail)
- printk("Error in serial.c:transmit_chars-dma(), tail!=0\n");
- if (info->xmit.head != info->xmit.tail) {
- SIMCOUT(info->xmit.buf + info->xmit.tail,
- CIRC_CNT(info->xmit.head,
- info->xmit.tail,
- SERIAL_XMIT_SIZE));
- info->xmit.head = info->xmit.tail; /* move back head */
- info->tr_running = 0;
- }
- return;
-#endif
/* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
*info->oclrintradr =
IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
@@ -1842,13 +1810,6 @@ static void receive_chars_dma(struct e100_serial *info)
struct tty_struct *tty;
unsigned char rstat;
-#ifdef CONFIG_SVINTO_SIM
- /* No receive in the simulator. Will probably be when the rest of
- * the serial interface works, and this piece will just be removed.
- */
- return;
-#endif
-
/* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
*info->iclrintradr =
IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
@@ -1934,12 +1895,6 @@ static int start_recv_dma(struct e100_serial *info)
static void
start_receive(struct e100_serial *info)
{
-#ifdef CONFIG_SVINTO_SIM
- /* No receive in the simulator. Will probably be when the rest of
- * the serial interface works, and this piece will just be removed.
- */
- return;
-#endif
if (info->uses_dma_in) {
/* reset the input dma channel to be sure it works */
@@ -1972,17 +1927,6 @@ tr_interrupt(int irq, void *dev_id)
int i;
int handled = 0;
-#ifdef CONFIG_SVINTO_SIM
- /* No receive in the simulator. Will probably be when the rest of
- * the serial interface works, and this piece will just be removed.
- */
- {
- const char *s = "What? tr_interrupt in simulator??\n";
- SIMCOUT(s,strlen(s));
- }
- return IRQ_HANDLED;
-#endif
-
/* find out the line that caused this irq and get it from rs_table */
ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */
@@ -2021,17 +1965,6 @@ rec_interrupt(int irq, void *dev_id)
int i;
int handled = 0;
-#ifdef CONFIG_SVINTO_SIM
- /* No receive in the simulator. Will probably be when the rest of
- * the serial interface works, and this piece will just be removed.
- */
- {
- const char *s = "What? rec_interrupt in simulator??\n";
- SIMCOUT(s,strlen(s));
- }
- return IRQ_HANDLED;
-#endif
-
/* find out the line that caused this irq and get it from rs_table */
ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */
@@ -2153,7 +2086,7 @@ static void flush_timeout_function(unsigned long data)
fast_timers[info->line].function = NULL;
serial_fast_timer_expired++;
- TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line));
+ TIMERD(DEBUG_LOG(info->line, "flush_timeout %i ", info->line));
TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired));
check_flush_timeout(info);
}
@@ -2173,10 +2106,6 @@ timed_flush_handler(unsigned long ptr)
struct e100_serial *info;
int i;
-#ifdef CONFIG_SVINTO_SIM
- return;
-#endif
-
for (i = 0; i < NR_PORTS; i++) {
info = rs_table + i;
if (info->uses_dma_in)
@@ -2729,25 +2658,6 @@ startup(struct e100_serial * info)
printk("starting up ttyS%d (xmit_buf 0x%p)...\n", info->line, info->xmit.buf);
#endif
-#ifdef CONFIG_SVINTO_SIM
- /* Bits and pieces collected from below. Better to have them
- in one ifdef:ed clause than to mix in a lot of ifdefs,
- right? */
- if (info->port.tty)
- clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
- info->xmit.head = info->xmit.tail = 0;
- info->first_recv_buffer = info->last_recv_buffer = NULL;
- info->recv_cnt = info->max_recv_cnt = 0;
-
- for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
- info->rec_descr[i].buf = NULL;
-
- /* No real action in the simulator, but may set info important
- to ioctl. */
- change_speed(info);
-#else
-
/*
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
@@ -2837,8 +2747,6 @@ startup(struct e100_serial * info)
e100_rts(info, 1);
e100_dtr(info, 1);
-#endif /* CONFIG_SVINTO_SIM */
-
info->port.flags |= ASYNC_INITIALIZED;
local_irq_restore(flags);
@@ -2857,7 +2765,6 @@ shutdown(struct e100_serial * info)
struct etrax_recv_buffer *buffer;
int i;
-#ifndef CONFIG_SVINTO_SIM
/* shut down the transmitter and receiver */
DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line));
e100_disable_rx(info);
@@ -2882,8 +2789,6 @@ shutdown(struct e100_serial * info)
info->tr_running = 0;
}
-#endif /* CONFIG_SVINTO_SIM */
-
if (!(info->port.flags & ASYNC_INITIALIZED))
return;
@@ -2995,17 +2900,12 @@ change_speed(struct e100_serial *info)
IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
r_alt_ser_baudrate_shadow &= ~mask;
r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
-#ifndef CONFIG_SVINTO_SIM
*R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
-#endif /* CONFIG_SVINTO_SIM */
info->baud = cflag_to_baud(cflag);
-#ifndef CONFIG_SVINTO_SIM
info->ioport[REG_BAUD] = cflag_to_etrax_baud(cflag);
-#endif /* CONFIG_SVINTO_SIM */
}
-#ifndef CONFIG_SVINTO_SIM
/* start with default settings and then fill in changes */
local_irq_save(flags);
/* 8 bit, no/even parity */
@@ -3073,7 +2973,6 @@ change_speed(struct e100_serial *info)
*((unsigned long *)&info->ioport[REG_XOFF]) = xoff;
local_irq_restore(flags);
-#endif /* !CONFIG_SVINTO_SIM */
update_char_time(info);
@@ -3122,11 +3021,6 @@ static int rs_raw_write(struct tty_struct *tty,
count, info->ioport[REG_STATUS]);
#endif
-#ifdef CONFIG_SVINTO_SIM
- /* Really simple. The output is here and now. */
- SIMCOUT(buf, count);
- return count;
-#endif
local_save_flags(flags);
DFLOW(DEBUG_LOG(info->line, "write count %i ", count));
DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty)));
@@ -3463,7 +3357,6 @@ static int
get_lsr_info(struct e100_serial * info, unsigned int *value)
{
unsigned int result = TIOCSER_TEMT;
-#ifndef CONFIG_SVINTO_SIM
unsigned long curr_time = jiffies;
unsigned long curr_time_usec = GET_JIFFIES_USEC();
unsigned long elapsed_usec =
@@ -3474,7 +3367,6 @@ get_lsr_info(struct e100_serial * info, unsigned int *value)
elapsed_usec < 2*info->char_time_usec) {
result = 0;
}
-#endif
if (copy_to_user(value, &result, sizeof(int)))
return -EFAULT;
@@ -3804,7 +3696,6 @@ rs_close(struct tty_struct *tty, struct file * filp)
e100_disable_serial_data_irq(info);
#endif
-#ifndef CONFIG_SVINTO_SIM
e100_disable_rx(info);
e100_disable_rx_irq(info);
@@ -3816,7 +3707,6 @@ rs_close(struct tty_struct *tty, struct file * filp)
*/
rs_wait_until_sent(tty, HZ);
}
-#endif
shutdown(info);
rs_flush_buffer(tty);
@@ -4479,7 +4369,6 @@ static int __init rs_init(void)
fast_timer_init();
#endif
-#ifndef CONFIG_SVINTO_SIM
#ifndef CONFIG_ETRAX_KGDB
/* Not needed in simulator. May only complicate stuff. */
/* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
@@ -4489,7 +4378,6 @@ static int __init rs_init(void)
panic("%s: Failed to request irq8", __func__);
#endif
-#endif /* CONFIG_SVINTO_SIM */
return 0;
}
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
index 2f2b2e538a5..cdbbc788230 100644
--- a/drivers/tty/serial/dz.c
+++ b/drivers/tty/serial/dz.c
@@ -625,7 +625,7 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,
dport->port.read_status_mask = DZ_OERR;
if (termios->c_iflag & INPCK)
dport->port.read_status_mask |= DZ_FERR | DZ_PERR;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
dport->port.read_status_mask |= DZ_BREAK;
/* characters to ignore */
diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-arm-semihost.c
new file mode 100644
index 00000000000..383db10fbb4
--- /dev/null
+++ b/drivers/tty/serial/earlycon-arm-semihost.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * Adapted for ARM and earlycon:
+ * Copyright (C) 2014 Linaro Ltd.
+ * Author: Rob Herring <robh@kernel.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+
+#ifdef CONFIG_THUMB2_KERNEL
+#define SEMIHOST_SWI "0xab"
+#else
+#define SEMIHOST_SWI "0x123456"
+#endif
+
+/*
+ * Semihosting-based debug console
+ */
+static void smh_putc(struct uart_port *port, int c)
+{
+#ifdef CONFIG_ARM64
+ asm volatile("mov x1, %0\n"
+ "mov x0, #3\n"
+ "hlt 0xf000\n"
+ : : "r" (&c) : "x0", "x1", "memory");
+#else
+ asm volatile("mov r1, %0\n"
+ "mov r0, #3\n"
+ "svc " SEMIHOST_SWI "\n"
+ : : "r" (&c) : "r0", "r1", "memory");
+#endif
+}
+
+static void smh_write(struct console *con, const char *s, unsigned n)
+{
+ struct earlycon_device *dev = con->data;
+ uart_console_write(&dev->port, s, n, smh_putc);
+}
+
+int __init early_smh_setup(struct earlycon_device *device, const char *opt)
+{
+ device->con->write = smh_write;
+ return 0;
+}
+EARLYCON_DECLARE(smh, early_smh_setup);
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
new file mode 100644
index 00000000000..a514ee6f540
--- /dev/null
+++ b/drivers/tty/serial/earlycon.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd.
+ * Author: Rob Herring <robh@kernel.org>
+ *
+ * Based on 8250 earlycon:
+ * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
+ * Bjorn Helgaas <bjorn.helgaas@hp.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/console.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/serial_core.h>
+#include <linux/sizes.h>
+#include <linux/mod_devicetable.h>
+
+#ifdef CONFIG_FIX_EARLYCON_MEM
+#include <asm/fixmap.h>
+#endif
+
+#include <asm/serial.h>
+
+static struct console early_con = {
+ .name = "uart", /* 8250 console switch requires this name */
+ .flags = CON_PRINTBUFFER | CON_BOOT,
+ .index = -1,
+};
+
+static struct earlycon_device early_console_dev = {
+ .con = &early_con,
+};
+
+static const struct of_device_id __earlycon_of_table_sentinel
+ __used __section(__earlycon_of_table_end);
+
+static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
+{
+ void __iomem *base;
+#ifdef CONFIG_FIX_EARLYCON_MEM
+ set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);
+ base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
+ base += paddr & ~PAGE_MASK;
+#else
+ base = ioremap(paddr, size);
+#endif
+ if (!base)
+ pr_err("%s: Couldn't map 0x%llx\n", __func__,
+ (unsigned long long)paddr);
+
+ return base;
+}
+
+static int __init parse_options(struct earlycon_device *device,
+ char *options)
+{
+ struct uart_port *port = &device->port;
+ int mmio, mmio32, length;
+ unsigned long addr;
+
+ if (!options)
+ return -ENODEV;
+
+ mmio = !strncmp(options, "mmio,", 5);
+ mmio32 = !strncmp(options, "mmio32,", 7);
+ if (mmio || mmio32) {
+ port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
+ options += mmio ? 5 : 7;
+ addr = simple_strtoul(options, NULL, 0);
+ port->mapbase = addr;
+ if (mmio32)
+ port->regshift = 2;
+ } else if (!strncmp(options, "io,", 3)) {
+ port->iotype = UPIO_PORT;
+ options += 3;
+ addr = simple_strtoul(options, NULL, 0);
+ port->iobase = addr;
+ mmio = 0;
+ } else if (!strncmp(options, "0x", 2)) {
+ port->iotype = UPIO_MEM;
+ addr = simple_strtoul(options, NULL, 0);
+ port->mapbase = addr;
+ } else {
+ return -EINVAL;
+ }
+
+ port->uartclk = BASE_BAUD * 16;
+
+ options = strchr(options, ',');
+ if (options) {
+ options++;
+ device->baud = simple_strtoul(options, NULL, 0);
+ length = min(strcspn(options, " ") + 1,
+ (size_t)(sizeof(device->options)));
+ strlcpy(device->options, options, length);
+ }
+
+ if (mmio || mmio32)
+ pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
+ mmio32 ? "32" : "",
+ (unsigned long long)port->mapbase,
+ device->options);
+ else
+ pr_info("Early serial console at I/O port 0x%lx (options '%s')\n",
+ port->iobase,
+ device->options);
+
+ return 0;
+}
+
+int __init setup_earlycon(char *buf, const char *match,
+ int (*setup)(struct earlycon_device *, const char *))
+{
+ int err;
+ size_t len;
+ struct uart_port *port = &early_console_dev.port;
+
+ if (!buf || !match || !setup)
+ return 0;
+
+ len = strlen(match);
+ if (strncmp(buf, match, len))
+ return 0;
+ if (buf[len] && (buf[len] != ','))
+ return 0;
+
+ buf += len + 1;
+
+ err = parse_options(&early_console_dev, buf);
+ /* On parsing error, pass the options buf to the setup function */
+ if (!err)
+ buf = NULL;
+
+ if (port->mapbase)
+ port->membase = earlycon_map(port->mapbase, 64);
+
+ early_console_dev.con->data = &early_console_dev;
+ err = setup(&early_console_dev, buf);
+ if (err < 0)
+ return err;
+ if (!early_console_dev.con->write)
+ return -ENODEV;
+
+ register_console(early_console_dev.con);
+ return 0;
+}
+
+int __init of_setup_earlycon(unsigned long addr,
+ int (*setup)(struct earlycon_device *, const char *))
+{
+ int err;
+ struct uart_port *port = &early_console_dev.port;
+
+ port->iotype = UPIO_MEM;
+ port->mapbase = addr;
+ port->uartclk = BASE_BAUD * 16;
+ port->membase = earlycon_map(addr, SZ_4K);
+
+ early_console_dev.con->data = &early_console_dev;
+ err = setup(&early_console_dev, NULL);
+ if (err < 0)
+ return err;
+ if (!early_console_dev.con->write)
+ return -ENODEV;
+
+
+ register_console(early_console_dev.con);
+ return 0;
+}
diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c
index 7d199c8e1a7..3b0ee9afd76 100644
--- a/drivers/tty/serial/efm32-uart.c
+++ b/drivers/tty/serial/efm32-uart.c
@@ -268,10 +268,10 @@ static irqreturn_t efm32_uart_rxirq(int irq, void *data)
handled = IRQ_HANDLED;
}
- tty_flip_buffer_push(tport);
-
spin_unlock(&port->lock);
+ tty_flip_buffer_push(tport);
+
return handled;
}
@@ -407,7 +407,7 @@ static void efm32_uart_set_termios(struct uart_port *port,
if (new->c_iflag & INPCK)
port->read_status_mask |=
UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR;
- if (new->c_iflag & (BRKINT | PARMRK))
+ if (new->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= SW_UARTn_RXDATAX_BERR;
port->ignore_status_mask = 0;
@@ -671,7 +671,10 @@ static int efm32_uart_probe_dt(struct platform_device *pdev,
if (!np)
return 1;
- ret = of_property_read_u32(np, "location", &location);
+ ret = of_property_read_u32(np, "efm32,location", &location);
+ if (ret)
+ /* fall back to old and (wrongly) generic property "location" */
+ ret = of_property_read_u32(np, "location", &location);
if (!ret) {
if (location > 5) {
dev_err(&pdev->dev, "invalid location\n");
@@ -698,6 +701,7 @@ static int efm32_uart_probe(struct platform_device *pdev)
{
struct efm32_uart_port *efm_port;
struct resource *res;
+ unsigned int line;
int ret;
efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
@@ -750,18 +754,21 @@ static int efm32_uart_probe(struct platform_device *pdev)
if (pdata)
efm_port->pdata = *pdata;
- }
+ } else if (ret < 0)
+ goto err_probe_dt;
+
+ line = efm_port->port.line;
- if (efm_port->port.line >= 0 &&
- efm_port->port.line < ARRAY_SIZE(efm32_uart_ports))
- efm32_uart_ports[efm_port->port.line] = efm_port;
+ if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+ efm32_uart_ports[line] = efm_port;
ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port);
if (ret) {
dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
- if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
- efm32_uart_ports[pdev->id] = NULL;
+ if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+ efm32_uart_ports[line] = NULL;
+err_probe_dt:
err_get_rxirq:
err_too_small:
err_get_base:
@@ -777,21 +784,23 @@ err_get_base:
static int efm32_uart_remove(struct platform_device *pdev)
{
struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
-
- platform_set_drvdata(pdev, NULL);
+ unsigned int line = efm_port->port.line;
uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
- if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
- efm32_uart_ports[pdev->id] = NULL;
+ if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+ efm32_uart_ports[line] = NULL;
kfree(efm_port);
return 0;
}
-static struct of_device_id efm32_uart_dt_ids[] = {
+static const struct of_device_id efm32_uart_dt_ids[] = {
{
+ .compatible = "energymicro,efm32-uart",
+ }, {
+ /* doesn't follow the "vendor,device" scheme, don't use */
.compatible = "efm32,uart",
}, {
/* sentinel */
@@ -833,6 +842,7 @@ static void __exit efm32_uart_exit(void)
platform_driver_unregister(&efm32_uart_driver);
uart_unregister_driver(&efm32_uart_reg);
}
+module_exit(efm32_uart_exit);
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
MODULE_DESCRIPTION("EFM32 UART/USART driver");
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 263cfaabe9e..49385c86cfb 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -13,14 +13,19 @@
#define SUPPORT_SYSRQ
#endif
-#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <linux/clk.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/console.h>
+#include <linux/of_dma.h>
#include <linux/serial_core.h>
+#include <linux/slab.h>
#include <linux/tty_flip.h>
/* All registers are 8-bit width */
@@ -112,6 +117,10 @@
#define UARTSFIFO_TXOF 0x02
#define UARTSFIFO_RXUF 0x01
+#define DMA_MAXBURST 16
+#define DMA_MAXBURST_MASK (DMA_MAXBURST - 1)
+#define FSL_UART_RX_DMA_BUFFER_SIZE 64
+
#define DRIVER_NAME "fsl-lpuart"
#define DEV_NAME "ttyLP"
#define UART_NR 6
@@ -121,6 +130,24 @@ struct lpuart_port {
struct clk *clk;
unsigned int txfifo_size;
unsigned int rxfifo_size;
+
+ bool lpuart_dma_use;
+ struct dma_chan *dma_tx_chan;
+ struct dma_chan *dma_rx_chan;
+ struct dma_async_tx_descriptor *dma_tx_desc;
+ struct dma_async_tx_descriptor *dma_rx_desc;
+ dma_addr_t dma_tx_buf_bus;
+ dma_addr_t dma_rx_buf_bus;
+ dma_cookie_t dma_tx_cookie;
+ dma_cookie_t dma_rx_cookie;
+ unsigned char *dma_tx_buf_virt;
+ unsigned char *dma_rx_buf_virt;
+ unsigned int dma_tx_bytes;
+ unsigned int dma_rx_bytes;
+ int dma_tx_in_progress;
+ int dma_rx_in_progress;
+ unsigned int dma_rx_timeout;
+ struct timer_list lpuart_timer;
};
static struct of_device_id lpuart_dt_ids[] = {
@@ -131,6 +158,10 @@ static struct of_device_id lpuart_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
+/* Forward declare this for the dma callbacks*/
+static void lpuart_dma_tx_complete(void *arg);
+static void lpuart_dma_rx_complete(void *arg);
+
static void lpuart_stop_tx(struct uart_port *port)
{
unsigned char temp;
@@ -152,6 +183,210 @@ static void lpuart_enable_ms(struct uart_port *port)
{
}
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
+ struct tty_port *tty, int count)
+{
+ int copied;
+
+ sport->port.icount.rx += count;
+
+ if (!tty) {
+ dev_err(sport->port.dev, "No tty port\n");
+ return;
+ }
+
+ dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
+ FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+ copied = tty_insert_flip_string(tty,
+ ((unsigned char *)(sport->dma_rx_buf_virt)), count);
+
+ if (copied != count) {
+ WARN_ON(1);
+ dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
+ }
+
+ dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+ FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+}
+
+static void lpuart_pio_tx(struct lpuart_port *sport)
+{
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ while (!uart_circ_empty(xmit) &&
+ readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
+ writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ sport->port.icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+
+ if (uart_circ_empty(xmit))
+ writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+ sport->port.membase + UARTCR5);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
+{
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ dma_addr_t tx_bus_addr;
+
+ dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ sport->dma_tx_bytes = count & ~(DMA_MAXBURST_MASK);
+ tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
+ sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
+ tx_bus_addr, sport->dma_tx_bytes,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+
+ if (!sport->dma_tx_desc) {
+ dev_err(sport->port.dev, "Not able to get desc for tx\n");
+ return -EIO;
+ }
+
+ sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
+ sport->dma_tx_desc->callback_param = sport;
+ sport->dma_tx_in_progress = 1;
+ sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
+ dma_async_issue_pending(sport->dma_tx_chan);
+
+ return 0;
+}
+
+static void lpuart_prepare_tx(struct lpuart_port *sport)
+{
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long count = CIRC_CNT_TO_END(xmit->head,
+ xmit->tail, UART_XMIT_SIZE);
+
+ if (!count)
+ return;
+
+ if (count < DMA_MAXBURST)
+ writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS,
+ sport->port.membase + UARTCR5);
+ else {
+ writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+ sport->port.membase + UARTCR5);
+ lpuart_dma_tx(sport, count);
+ }
+}
+
+static void lpuart_dma_tx_complete(void *arg)
+{
+ struct lpuart_port *sport = arg;
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long flags;
+
+ async_tx_ack(sport->dma_tx_desc);
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
+ sport->dma_tx_in_progress = 0;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+
+ lpuart_prepare_tx(sport);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_rx(struct lpuart_port *sport)
+{
+ dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+ FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+ sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
+ sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
+ if (!sport->dma_rx_desc) {
+ dev_err(sport->port.dev, "Not able to get desc for rx\n");
+ return -EIO;
+ }
+
+ sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+ sport->dma_rx_desc->callback_param = sport;
+ sport->dma_rx_in_progress = 1;
+ sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+ dma_async_issue_pending(sport->dma_rx_chan);
+
+ return 0;
+}
+
+static void lpuart_dma_rx_complete(void *arg)
+{
+ struct lpuart_port *sport = arg;
+ struct tty_port *port = &sport->port.state->port;
+ unsigned long flags;
+
+ async_tx_ack(sport->dma_rx_desc);
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ sport->dma_rx_in_progress = 0;
+ lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE);
+ tty_flip_buffer_push(port);
+ lpuart_dma_rx(sport);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void lpuart_timer_func(unsigned long data)
+{
+ struct lpuart_port *sport = (struct lpuart_port *)data;
+ struct tty_port *port = &sport->port.state->port;
+ struct dma_tx_state state;
+ unsigned long flags;
+ unsigned char temp;
+ int count;
+
+ del_timer(&sport->lpuart_timer);
+ dmaengine_pause(sport->dma_rx_chan);
+ dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state);
+ dmaengine_terminate_all(sport->dma_rx_chan);
+ count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
+ async_tx_ack(sport->dma_rx_desc);
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ sport->dma_rx_in_progress = 0;
+ lpuart_copy_rx_to_tty(sport, port, count);
+ tty_flip_buffer_push(port);
+ temp = readb(sport->port.membase + UARTCR5);
+ writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static inline void lpuart_prepare_rx(struct lpuart_port *sport)
+{
+ unsigned long flags;
+ unsigned char temp;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ init_timer(&sport->lpuart_timer);
+ sport->lpuart_timer.function = lpuart_timer_func;
+ sport->lpuart_timer.data = (unsigned long)sport;
+ sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+ add_timer(&sport->lpuart_timer);
+
+ lpuart_dma_rx(sport);
+ temp = readb(sport->port.membase + UARTCR5);
+ writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
@@ -172,14 +407,21 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
static void lpuart_start_tx(struct uart_port *port)
{
- struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ struct circ_buf *xmit = &sport->port.state->xmit;
unsigned char temp;
temp = readb(port->membase + UARTCR2);
writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
- if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
- lpuart_transmit_buffer(sport);
+ if (sport->lpuart_dma_use) {
+ if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
+ lpuart_prepare_tx(sport);
+ } else {
+ if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+ lpuart_transmit_buffer(sport);
+ }
}
static irqreturn_t lpuart_txint(int irq, void *dev_id)
@@ -279,12 +521,19 @@ static irqreturn_t lpuart_int(int irq, void *dev_id)
sts = readb(sport->port.membase + UARTSR1);
- if (sts & UARTSR1_RDRF)
- lpuart_rxint(irq, dev_id);
-
+ if (sts & UARTSR1_RDRF) {
+ if (sport->lpuart_dma_use)
+ lpuart_prepare_rx(sport);
+ else
+ lpuart_rxint(irq, dev_id);
+ }
if (sts & UARTSR1_TDRE &&
- !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS))
- lpuart_txint(irq, dev_id);
+ !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
+ if (sport->lpuart_dma_use)
+ lpuart_pio_tx(sport);
+ else
+ lpuart_txint(irq, dev_id);
+ }
return IRQ_HANDLED;
}
@@ -342,8 +591,10 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state)
static void lpuart_setup_watermark(struct lpuart_port *sport)
{
unsigned char val, cr2;
+ unsigned char cr2_saved;
cr2 = readb(sport->port.membase + UARTCR2);
+ cr2_saved = cr2;
cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE |
UARTCR2_RIE | UARTCR2_RE);
writeb(cr2, sport->port.membase + UARTCR2);
@@ -364,8 +615,154 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
- writeb(2, sport->port.membase + UARTTWFIFO);
+ writeb(0, sport->port.membase + UARTTWFIFO);
writeb(1, sport->port.membase + UARTRWFIFO);
+
+ /* Restore cr2 */
+ writeb(cr2_saved, sport->port.membase + UARTCR2);
+}
+
+static int lpuart_dma_tx_request(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ struct dma_chan *tx_chan;
+ struct dma_slave_config dma_tx_sconfig;
+ dma_addr_t dma_bus;
+ unsigned char *dma_buf;
+ int ret;
+
+ tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
+
+ if (!tx_chan) {
+ dev_err(sport->port.dev, "Dma tx channel request failed!\n");
+ return -ENODEV;
+ }
+
+ dma_bus = dma_map_single(tx_chan->device->dev,
+ sport->port.state->xmit.buf,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
+ dev_err(sport->port.dev, "dma_map_single tx failed\n");
+ dma_release_channel(tx_chan);
+ return -ENOMEM;
+ }
+
+ dma_buf = sport->port.state->xmit.buf;
+ dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+ dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_tx_sconfig.dst_maxburst = DMA_MAXBURST;
+ dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
+
+ if (ret < 0) {
+ dev_err(sport->port.dev,
+ "Dma slave config failed, err = %d\n", ret);
+ dma_release_channel(tx_chan);
+ return ret;
+ }
+
+ sport->dma_tx_chan = tx_chan;
+ sport->dma_tx_buf_virt = dma_buf;
+ sport->dma_tx_buf_bus = dma_bus;
+ sport->dma_tx_in_progress = 0;
+
+ return 0;
+}
+
+static int lpuart_dma_rx_request(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ struct dma_chan *rx_chan;
+ struct dma_slave_config dma_rx_sconfig;
+ dma_addr_t dma_bus;
+ unsigned char *dma_buf;
+ int ret;
+
+ rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
+
+ if (!rx_chan) {
+ dev_err(sport->port.dev, "Dma rx channel request failed!\n");
+ return -ENODEV;
+ }
+
+ dma_buf = devm_kzalloc(sport->port.dev,
+ FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
+
+ if (!dma_buf) {
+ dev_err(sport->port.dev, "Dma rx alloc failed\n");
+ dma_release_channel(rx_chan);
+ return -ENOMEM;
+ }
+
+ dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
+ FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
+ dev_err(sport->port.dev, "dma_map_single rx failed\n");
+ dma_release_channel(rx_chan);
+ return -ENOMEM;
+ }
+
+ dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+ dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_rx_sconfig.src_maxburst = 1;
+ dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
+
+ if (ret < 0) {
+ dev_err(sport->port.dev,
+ "Dma slave config failed, err = %d\n", ret);
+ dma_release_channel(rx_chan);
+ return ret;
+ }
+
+ sport->dma_rx_chan = rx_chan;
+ sport->dma_rx_buf_virt = dma_buf;
+ sport->dma_rx_buf_bus = dma_bus;
+ sport->dma_rx_in_progress = 0;
+
+ sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
+ FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
+ sport->rxfifo_size / 2;
+
+ if (sport->dma_rx_timeout < msecs_to_jiffies(20))
+ sport->dma_rx_timeout = msecs_to_jiffies(20);
+
+ return 0;
+}
+
+static void lpuart_dma_tx_free(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ struct dma_chan *dma_chan;
+
+ dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ dma_chan = sport->dma_tx_chan;
+ sport->dma_tx_chan = NULL;
+ sport->dma_tx_buf_bus = 0;
+ sport->dma_tx_buf_virt = NULL;
+ dma_release_channel(dma_chan);
+}
+
+static void lpuart_dma_rx_free(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ struct dma_chan *dma_chan;
+
+ dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
+ FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+ dma_chan = sport->dma_rx_chan;
+ sport->dma_rx_chan = NULL;
+ sport->dma_rx_buf_bus = 0;
+ sport->dma_rx_buf_virt = NULL;
+ dma_release_channel(dma_chan);
}
static int lpuart_startup(struct uart_port *port)
@@ -375,6 +772,15 @@ static int lpuart_startup(struct uart_port *port)
unsigned long flags;
unsigned char temp;
+ /*whether use dma support by dma request results*/
+ if (lpuart_dma_tx_request(port) || lpuart_dma_rx_request(port)) {
+ sport->lpuart_dma_use = false;
+ } else {
+ sport->lpuart_dma_use = true;
+ temp = readb(port->membase + UARTCR5);
+ writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
+ }
+
ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
DRIVER_NAME, sport);
if (ret)
@@ -409,6 +815,11 @@ static void lpuart_shutdown(struct uart_port *port)
spin_unlock_irqrestore(&port->lock, flags);
devm_free_irq(port->dev, port->irq, sport);
+
+ if (sport->lpuart_dma_use) {
+ lpuart_dma_tx_free(port);
+ lpuart_dma_rx_free(port);
+ }
}
static void
@@ -491,7 +902,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
sport->port.read_status_mask = 0;
if (termios->c_iflag & INPCK)
sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE);
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
sport->port.read_status_mask |= UARTSR1_FE;
/* characters to ignore */
@@ -858,7 +1269,7 @@ static int __init lpuart_serial_init(void)
if (ret)
uart_unregister_driver(&lpuart_reg);
- return 0;
+ return ret;
}
static void __exit lpuart_serial_exit(void)
diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index 18ed5aebb16..67423805e6d 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -105,7 +105,7 @@ static const struct pci_device_id icom_pci_table[] = {
{}
};
-struct lookup_proc_table start_proc[4] = {
+static struct lookup_proc_table start_proc[4] = {
{NULL, ICOM_CONTROL_START_A},
{NULL, ICOM_CONTROL_START_B},
{NULL, ICOM_CONTROL_START_C},
@@ -113,14 +113,14 @@ struct lookup_proc_table start_proc[4] = {
};
-struct lookup_proc_table stop_proc[4] = {
+static struct lookup_proc_table stop_proc[4] = {
{NULL, ICOM_CONTROL_STOP_A},
{NULL, ICOM_CONTROL_STOP_B},
{NULL, ICOM_CONTROL_STOP_C},
{NULL, ICOM_CONTROL_STOP_D}
};
-struct lookup_int_table int_mask_tbl[4] = {
+static struct lookup_int_table int_mask_tbl[4] = {
{NULL, ICOM_INT_MASK_PRC_A},
{NULL, ICOM_INT_MASK_PRC_B},
{NULL, ICOM_INT_MASK_PRC_C},
@@ -297,25 +297,25 @@ static void stop_processor(struct icom_port *icom_port)
spin_lock_irqsave(&icom_lock, flags);
port = icom_port->port;
+ if (port >= ARRAY_SIZE(stop_proc)) {
+ dev_err(&icom_port->adapter->pci_dev->dev,
+ "Invalid port assignment\n");
+ goto unlock;
+ }
+
if (port == 0 || port == 1)
stop_proc[port].global_control_reg = &icom_port->global_reg->control;
else
stop_proc[port].global_control_reg = &icom_port->global_reg->control_2;
+ temp = readl(stop_proc[port].global_control_reg);
+ temp = (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id;
+ writel(temp, stop_proc[port].global_control_reg);
- if (port < 4) {
- temp = readl(stop_proc[port].global_control_reg);
- temp =
- (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id;
- writel(temp, stop_proc[port].global_control_reg);
-
- /* write flush */
- readl(stop_proc[port].global_control_reg);
- } else {
- dev_err(&icom_port->adapter->pci_dev->dev,
- "Invalid port assignment\n");
- }
+ /* write flush */
+ readl(stop_proc[port].global_control_reg);
+unlock:
spin_unlock_irqrestore(&icom_lock, flags);
}
@@ -328,23 +328,25 @@ static void start_processor(struct icom_port *icom_port)
spin_lock_irqsave(&icom_lock, flags);
port = icom_port->port;
+ if (port >= ARRAY_SIZE(start_proc)) {
+ dev_err(&icom_port->adapter->pci_dev->dev,
+ "Invalid port assignment\n");
+ goto unlock;
+ }
+
if (port == 0 || port == 1)
start_proc[port].global_control_reg = &icom_port->global_reg->control;
else
start_proc[port].global_control_reg = &icom_port->global_reg->control_2;
- if (port < 4) {
- temp = readl(start_proc[port].global_control_reg);
- temp =
- (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id;
- writel(temp, start_proc[port].global_control_reg);
- /* write flush */
- readl(start_proc[port].global_control_reg);
- } else {
- dev_err(&icom_port->adapter->pci_dev->dev,
- "Invalid port assignment\n");
- }
+ temp = readl(start_proc[port].global_control_reg);
+ temp = (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id;
+ writel(temp, start_proc[port].global_control_reg);
+ /* write flush */
+ readl(start_proc[port].global_control_reg);
+
+unlock:
spin_unlock_irqrestore(&icom_lock, flags);
}
@@ -453,11 +455,11 @@ static void load_code(struct icom_port *icom_port)
for (index = 0; index < fw->size; index++)
new_page[index] = fw->data[index];
- release_firmware(fw);
-
writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length);
writel(temp_pci, &icom_port->dram->mac_load_addr);
+ release_firmware(fw);
+
/*Setting the syncReg to 0x80 causes adapter to start downloading
the personality code into adapter instruction RAM.
Once code is loaded, it will begin executing and, based on
@@ -557,6 +559,12 @@ static int startup(struct icom_port *icom_port)
*/
spin_lock_irqsave(&icom_lock, flags);
port = icom_port->port;
+ if (port >= ARRAY_SIZE(int_mask_tbl)) {
+ dev_err(&icom_port->adapter->pci_dev->dev,
+ "Invalid port assignment\n");
+ goto unlock;
+ }
+
if (port == 0 || port == 1)
int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
else
@@ -566,17 +574,14 @@ static int startup(struct icom_port *icom_port)
writew(0x00FF, icom_port->int_reg);
else
writew(0x3F00, icom_port->int_reg);
- if (port < 4) {
- temp = readl(int_mask_tbl[port].global_int_mask);
- writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
- /* write flush */
- readl(int_mask_tbl[port].global_int_mask);
- } else {
- dev_err(&icom_port->adapter->pci_dev->dev,
- "Invalid port assignment\n");
- }
+ temp = readl(int_mask_tbl[port].global_int_mask);
+ writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
+
+ /* write flush */
+ readl(int_mask_tbl[port].global_int_mask);
+unlock:
spin_unlock_irqrestore(&icom_lock, flags);
return 0;
}
@@ -595,21 +600,23 @@ static void shutdown(struct icom_port *icom_port)
* disable all interrupts
*/
port = icom_port->port;
+ if (port >= ARRAY_SIZE(int_mask_tbl)) {
+ dev_err(&icom_port->adapter->pci_dev->dev,
+ "Invalid port assignment\n");
+ goto unlock;
+ }
if (port == 0 || port == 1)
int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
else
int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
- if (port < 4) {
- temp = readl(int_mask_tbl[port].global_int_mask);
- writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
+ temp = readl(int_mask_tbl[port].global_int_mask);
+ writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
- /* write flush */
- readl(int_mask_tbl[port].global_int_mask);
- } else {
- dev_err(&icom_port->adapter->pci_dev->dev,
- "Invalid port assignment\n");
- }
+ /* write flush */
+ readl(int_mask_tbl[port].global_int_mask);
+
+unlock:
spin_unlock_irqrestore(&icom_lock, flags);
/*
@@ -834,7 +841,10 @@ ignore_char:
status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
}
icom_port->next_rcv = rcv_buff;
+
+ spin_unlock(&icom_port->uart_port.lock);
tty_flip_buffer_push(port);
+ spin_lock(&icom_port->uart_port.lock);
}
static void process_interrupt(u16 port_int_reg,
@@ -1087,8 +1097,7 @@ static void icom_close(struct uart_port *port)
/* stop receiver */
cmdReg = readb(&ICOM_PORT->dram->CmdReg);
- writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE,
- &ICOM_PORT->dram->CmdReg);
+ writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg);
shutdown(ICOM_PORT);
@@ -1567,7 +1576,7 @@ static int icom_probe(struct pci_dev *dev,
icom_port->uart_port.type = PORT_ICOM;
icom_port->uart_port.iotype = UPIO_MEM;
icom_port->uart_port.membase =
- (char *) icom_adapter->base_addr_pci;
+ (unsigned char __iomem *)icom_adapter->base_addr_pci;
icom_port->uart_port.fifosize = 16;
icom_port->uart_port.ops = &icom_ops;
icom_port->uart_port.line =
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index 8b1534c424a..59039097099 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -1008,7 +1008,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
return -ENODEV;
}
- pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data;
+ pl_data = dev_get_platdata(&spi->dev);
if (!pl_data) {
dev_err(&spi->dev, "missing platform data!");
return -ENODEV;
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 415cec62073..044e86d528a 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -47,11 +47,12 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/io.h>
+#include <linux/dma-mapping.h>
#include <asm/irq.h>
#include <linux/platform_data/serial-imx.h>
+#include <linux/platform_data/dma-imx.h>
/* Register definitions */
#define URXD0 0x0 /* Receiver Register */
@@ -83,6 +84,7 @@
#define UCR1_ADBR (1<<14) /* Auto detect baud rate */
#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */
#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
+#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_IREN (1<<7) /* Infrared interface enable */
@@ -91,6 +93,7 @@
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
+#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */
#define UCR1_DOZE (1<<1) /* Doze */
#define UCR1_UARTEN (1<<0) /* UART enabled */
#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
@@ -113,7 +116,7 @@
#define UCR3_DSR (1<<10) /* Data set ready */
#define UCR3_DCD (1<<9) /* Data carrier detect */
#define UCR3_RI (1<<8) /* Ring indicator */
-#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */
+#define UCR3_ADNIMP (1<<7) /* Autobaud Detection Not Improved */
#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
@@ -126,6 +129,7 @@
#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */
#define UCR4_WKEN (1<<7) /* Wake interrupt enable */
#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */
+#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */
#define UCR4_IRSC (1<<5) /* IR special case */
#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
@@ -187,6 +191,7 @@
enum imx_uart_type {
IMX1_UART,
IMX21_UART,
+ IMX6Q_UART,
};
/* device type dependent stuff */
@@ -209,6 +214,18 @@ struct imx_port {
struct clk *clk_ipg;
struct clk *clk_per;
const struct imx_uart_data *devdata;
+
+ /* DMA fields */
+ unsigned int dma_is_inited:1;
+ unsigned int dma_is_enabled:1;
+ unsigned int dma_is_rxing:1;
+ unsigned int dma_is_txing:1;
+ struct dma_chan *dma_chan_rx, *dma_chan_tx;
+ struct scatterlist rx_sgl, tx_sgl[2];
+ void *rx_buf;
+ unsigned int tx_bytes;
+ unsigned int dma_tx_nents;
+ wait_queue_head_t dma_wait;
};
struct imx_port_ucrs {
@@ -232,6 +249,10 @@ static struct imx_uart_data imx_uart_devdata[] = {
.uts_reg = IMX21_UTS,
.devtype = IMX21_UART,
},
+ [IMX6Q_UART] = {
+ .uts_reg = IMX21_UTS,
+ .devtype = IMX6Q_UART,
+ },
};
static struct platform_device_id imx_uart_devtype[] = {
@@ -242,12 +263,16 @@ static struct platform_device_id imx_uart_devtype[] = {
.name = "imx21-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
}, {
+ .name = "imx6q-uart",
+ .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],
+ }, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
static struct of_device_id imx_uart_dt_ids[] = {
+ { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
{ /* sentinel */ }
@@ -269,6 +294,10 @@ static inline int is_imx21_uart(struct imx_port *sport)
return sport->devdata->devtype == IMX21_UART;
}
+static inline int is_imx6q_uart(struct imx_port *sport)
+{
+ return sport->devdata->devtype == IMX6Q_UART;
+}
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
@@ -387,6 +416,13 @@ static void imx_stop_tx(struct uart_port *port)
return;
}
+ /*
+ * We are maybe in the SMP context, so if the DMA TX thread is running
+ * on other cpu, we have to wait for it to finish.
+ */
+ if (sport->dma_is_enabled && sport->dma_is_txing)
+ return;
+
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
}
@@ -399,8 +435,19 @@ static void imx_stop_rx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ /*
+ * We are maybe in the SMP context, so if the DMA TX thread is running
+ * on other cpu, we have to wait for it to finish.
+ */
+ if (sport->dma_is_enabled && sport->dma_is_rxing)
+ return;
+
temp = readl(sport->port.membase + UCR2);
writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
+
+ /* disable the `Receiver Ready Interrrupt` */
+ temp = readl(sport->port.membase + UCR1);
+ writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1);
}
/*
@@ -434,6 +481,84 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
imx_stop_tx(&sport->port);
}
+static void dma_tx_callback(void *data)
+{
+ struct imx_port *sport = data;
+ struct scatterlist *sgl = &sport->tx_sgl[0];
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long flags;
+
+ dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+
+ sport->dma_is_txing = 0;
+
+ /* update the stat */
+ spin_lock_irqsave(&sport->port.lock, flags);
+ xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
+ sport->port.icount.tx += sport->tx_bytes;
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
+
+ uart_write_wakeup(&sport->port);
+
+ if (waitqueue_active(&sport->dma_wait)) {
+ wake_up(&sport->dma_wait);
+ dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
+ return;
+ }
+}
+
+static void imx_dma_tx(struct imx_port *sport)
+{
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ struct scatterlist *sgl = sport->tx_sgl;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan = sport->dma_chan_tx;
+ struct device *dev = sport->port.dev;
+ enum dma_status status;
+ int ret;
+
+ status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL);
+ if (DMA_IN_PROGRESS == status)
+ return;
+
+ sport->tx_bytes = uart_circ_chars_pending(xmit);
+
+ if (xmit->tail > xmit->head && xmit->head > 0) {
+ sport->dma_tx_nents = 2;
+ sg_init_table(sgl, 2);
+ sg_set_buf(sgl, xmit->buf + xmit->tail,
+ UART_XMIT_SIZE - xmit->tail);
+ sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+ } else {
+ sport->dma_tx_nents = 1;
+ sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
+ }
+
+ ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+ if (ret == 0) {
+ dev_err(dev, "DMA mapping error for TX.\n");
+ return;
+ }
+ desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(dev, "We cannot prepare for the TX slave dma!\n");
+ return;
+ }
+ desc->callback = dma_tx_callback;
+ desc->callback_param = sport;
+
+ dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
+ uart_circ_chars_pending(xmit));
+ /* fire it */
+ sport->dma_is_txing = 1;
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+ return;
+}
+
/*
* interrupts disabled on entry
*/
@@ -442,6 +567,9 @@ static void imx_start_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ if (uart_circ_empty(&port->state->xmit))
+ return;
+
if (USE_IRDA(sport)) {
/* half duplex in IrDA mode; have to disable receive mode */
temp = readl(sport->port.membase + UCR4);
@@ -460,8 +588,10 @@ static void imx_start_tx(struct uart_port *port)
temp |= UCR4_OREN;
writel(temp, sport->port.membase + UCR4);
- temp = readl(sport->port.membase + UCR1);
- writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ if (!sport->dma_is_enabled) {
+ temp = readl(sport->port.membase + UCR1);
+ writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ }
if (USE_IRDA(sport)) {
temp = readl(sport->port.membase + UCR1);
@@ -473,6 +603,11 @@ static void imx_start_tx(struct uart_port *port)
writel(temp, sport->port.membase + UCR4);
}
+ if (sport->dma_is_enabled) {
+ imx_dma_tx(sport);
+ return;
+ }
+
if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
imx_transmit_buffer(sport);
}
@@ -588,6 +723,29 @@ out:
return IRQ_HANDLED;
}
+static int start_rx_dma(struct imx_port *sport);
+/*
+ * If the RXFIFO is filled with some data, and then we
+ * arise a DMA operation to receive them.
+ */
+static void imx_dma_rxint(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ temp = readl(sport->port.membase + USR2);
+ if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
+ sport->dma_is_rxing = 1;
+
+ /* disable the `Recerver Ready Interrrupt` */
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_RRDYEN);
+ writel(temp, sport->port.membase + UCR1);
+
+ /* tell the DMA to receive the data. */
+ start_rx_dma(sport);
+ }
+}
+
static irqreturn_t imx_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
@@ -596,8 +754,12 @@ static irqreturn_t imx_int(int irq, void *dev_id)
sts = readl(sport->port.membase + USR1);
- if (sts & USR1_RRDY)
- imx_rxint(irq, dev_id);
+ if (sts & USR1_RRDY) {
+ if (sport->dma_is_enabled)
+ imx_dma_rxint(sport);
+ else
+ imx_rxint(irq, dev_id);
+ }
if (sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
@@ -625,8 +787,15 @@ static irqreturn_t imx_int(int irq, void *dev_id)
static unsigned int imx_tx_empty(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
+ unsigned int ret;
+
+ ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
- return (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
+ /* If the TX DMA is working, return 0. */
+ if (sport->dma_is_enabled && sport->dma_is_txing)
+ ret = 0;
+
+ return ret;
}
/*
@@ -643,6 +812,9 @@ static unsigned int imx_get_mctrl(struct uart_port *port)
if (readl(sport->port.membase + UCR2) & UCR2_CTS)
tmp |= TIOCM_RTS;
+ if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP)
+ tmp |= TIOCM_LOOP;
+
return tmp;
}
@@ -654,9 +826,15 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
if (mctrl & TIOCM_RTS)
- temp |= UCR2_CTS;
+ if (!sport->dma_is_enabled)
+ temp |= UCR2_CTS;
writel(temp, sport->port.membase + UCR2);
+
+ temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
+ if (mctrl & TIOCM_LOOP)
+ temp |= UTS_LOOP;
+ writel(temp, sport->port.membase + uts_reg(sport));
}
/*
@@ -693,24 +871,222 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
return 0;
}
+#define RX_BUF_SIZE (PAGE_SIZE)
+static void imx_rx_dma_done(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ /* Enable this interrupt when the RXFIFO is empty. */
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_RRDYEN;
+ writel(temp, sport->port.membase + UCR1);
+
+ sport->dma_is_rxing = 0;
+
+ /* Is the shutdown waiting for us? */
+ if (waitqueue_active(&sport->dma_wait))
+ wake_up(&sport->dma_wait);
+}
+
+/*
+ * There are three kinds of RX DMA interrupts(such as in the MX6Q):
+ * [1] the RX DMA buffer is full.
+ * [2] the Aging timer expires(wait for 8 bytes long)
+ * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
+ *
+ * The [2] is trigger when a character was been sitting in the FIFO
+ * meanwhile [3] can wait for 32 bytes long when the RX line is
+ * on IDLE state and RxFIFO is empty.
+ */
+static void dma_rx_callback(void *data)
+{
+ struct imx_port *sport = data;
+ struct dma_chan *chan = sport->dma_chan_rx;
+ struct scatterlist *sgl = &sport->rx_sgl;
+ struct tty_port *port = &sport->port.state->port;
+ struct dma_tx_state state;
+ enum dma_status status;
+ unsigned int count;
+
+ /* unmap it first */
+ dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
+
+ status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
+ count = RX_BUF_SIZE - state.residue;
+ dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
+
+ if (count) {
+ tty_insert_flip_string(port, sport->rx_buf, count);
+ tty_flip_buffer_push(port);
+
+ start_rx_dma(sport);
+ } else
+ imx_rx_dma_done(sport);
+}
+
+static int start_rx_dma(struct imx_port *sport)
+{
+ struct scatterlist *sgl = &sport->rx_sgl;
+ struct dma_chan *chan = sport->dma_chan_rx;
+ struct device *dev = sport->port.dev;
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+
+ sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
+ ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);
+ if (ret == 0) {
+ dev_err(dev, "DMA mapping error for RX.\n");
+ return -EINVAL;
+ }
+ desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(dev, "We cannot prepare for the RX slave dma!\n");
+ return -EINVAL;
+ }
+ desc->callback = dma_rx_callback;
+ desc->callback_param = sport;
+
+ dev_dbg(dev, "RX: prepare for the DMA.\n");
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+ return 0;
+}
+
+static void imx_uart_dma_exit(struct imx_port *sport)
+{
+ if (sport->dma_chan_rx) {
+ dma_release_channel(sport->dma_chan_rx);
+ sport->dma_chan_rx = NULL;
+
+ kfree(sport->rx_buf);
+ sport->rx_buf = NULL;
+ }
+
+ if (sport->dma_chan_tx) {
+ dma_release_channel(sport->dma_chan_tx);
+ sport->dma_chan_tx = NULL;
+ }
+
+ sport->dma_is_inited = 0;
+}
+
+static int imx_uart_dma_init(struct imx_port *sport)
+{
+ struct dma_slave_config slave_config = {};
+ struct device *dev = sport->port.dev;
+ int ret;
+
+ /* Prepare for RX : */
+ sport->dma_chan_rx = dma_request_slave_channel(dev, "rx");
+ if (!sport->dma_chan_rx) {
+ dev_dbg(dev, "cannot get the DMA channel.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = sport->port.mapbase + URXD0;
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.src_maxburst = RXTL;
+ ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
+ if (ret) {
+ dev_err(dev, "error in RX dma configuration.\n");
+ goto err;
+ }
+
+ sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!sport->rx_buf) {
+ dev_err(dev, "cannot alloc DMA buffer.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Prepare for TX : */
+ sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
+ if (!sport->dma_chan_tx) {
+ dev_err(dev, "cannot get the TX DMA channel!\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = sport->port.mapbase + URTX0;
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.dst_maxburst = TXTL;
+ ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
+ if (ret) {
+ dev_err(dev, "error in TX dma configuration.");
+ goto err;
+ }
+
+ sport->dma_is_inited = 1;
+
+ return 0;
+err:
+ imx_uart_dma_exit(sport);
+ return ret;
+}
+
+static void imx_enable_dma(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ init_waitqueue_head(&sport->dma_wait);
+
+ /* set UCR1 */
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
+ /* wait for 32 idle frames for IDDMA interrupt */
+ UCR1_ICD_REG(3);
+ writel(temp, sport->port.membase + UCR1);
+
+ /* set UCR4 */
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_IDDMAEN;
+ writel(temp, sport->port.membase + UCR4);
+
+ sport->dma_is_enabled = 1;
+}
+
+static void imx_disable_dma(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ /* clear UCR1 */
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
+ writel(temp, sport->port.membase + UCR1);
+
+ /* clear UCR2 */
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~(UCR2_CTSC | UCR2_CTS);
+ writel(temp, sport->port.membase + UCR2);
+
+ /* clear UCR4 */
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~UCR4_IDDMAEN;
+ writel(temp, sport->port.membase + UCR4);
+
+ sport->dma_is_enabled = 0;
+}
+
/* half the RX buffer size */
#define CTSTL 16
static int imx_startup(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- int retval;
+ int retval, i;
unsigned long flags, temp;
- if (!uart_console(port)) {
- retval = clk_prepare_enable(sport->clk_per);
- if (retval)
- goto error_out1;
- retval = clk_prepare_enable(sport->clk_ipg);
- if (retval) {
- clk_disable_unprepare(sport->clk_per);
- goto error_out1;
- }
+ retval = clk_prepare_enable(sport->clk_per);
+ if (retval)
+ goto error_out1;
+ retval = clk_prepare_enable(sport->clk_ipg);
+ if (retval) {
+ clk_disable_unprepare(sport->clk_per);
+ goto error_out1;
}
imx_setup_ufcr(sport, 0);
@@ -729,17 +1105,15 @@ static int imx_startup(struct uart_port *port)
writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
- if (USE_IRDA(sport)) {
- /* reset fifo's and state machines */
- int i = 100;
- temp = readl(sport->port.membase + UCR2);
- temp &= ~UCR2_SRST;
- writel(temp, sport->port.membase + UCR2);
- while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) &&
- (--i > 0)) {
- udelay(1);
- }
- }
+ /* Reset fifo's and state machines */
+ i = 100;
+
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~UCR2_SRST;
+ writel(temp, sport->port.membase + UCR2);
+
+ while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
+ udelay(1);
/*
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
@@ -747,25 +1121,25 @@ static int imx_startup(struct uart_port *port)
*/
if (sport->txirq > 0) {
retval = request_irq(sport->rxirq, imx_rxint, 0,
- DRIVER_NAME, sport);
+ dev_name(port->dev), sport);
if (retval)
goto error_out1;
retval = request_irq(sport->txirq, imx_txint, 0,
- DRIVER_NAME, sport);
+ dev_name(port->dev), sport);
if (retval)
goto error_out2;
/* do not use RTS IRQ on IrDA */
if (!USE_IRDA(sport)) {
retval = request_irq(sport->rtsirq, imx_rtsint, 0,
- DRIVER_NAME, sport);
+ dev_name(port->dev), sport);
if (retval)
goto error_out3;
}
} else {
retval = request_irq(sport->port.irq, imx_int, 0,
- DRIVER_NAME, sport);
+ dev_name(port->dev), sport);
if (retval) {
free_irq(sport->port.irq, sport);
goto error_out1;
@@ -794,18 +1168,9 @@ static int imx_startup(struct uart_port *port)
temp |= UCR2_IRTS;
writel(temp, sport->port.membase + UCR2);
- if (USE_IRDA(sport)) {
- /* clear RX-FIFO */
- int i = 64;
- while ((--i > 0) &&
- (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) {
- barrier();
- }
- }
-
- if (is_imx21_uart(sport)) {
+ if (!is_imx1_uart(sport)) {
temp = readl(sport->port.membase + UCR3);
- temp |= IMX21_UCR3_RXDMUXSEL;
+ temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
writel(temp, sport->port.membase + UCR3);
}
@@ -833,7 +1198,7 @@ static int imx_startup(struct uart_port *port)
if (USE_IRDA(sport)) {
struct imxuart_platform_data *pdata;
- pdata = sport->port.dev->platform_data;
+ pdata = dev_get_platdata(sport->port.dev);
sport->irda_inv_rx = pdata->irda_inv_rx;
sport->irda_inv_tx = pdata->irda_inv_tx;
sport->trcv_delay = pdata->transceiver_delay;
@@ -859,6 +1224,15 @@ static void imx_shutdown(struct uart_port *port)
unsigned long temp;
unsigned long flags;
+ if (sport->dma_is_enabled) {
+ /* We have to wait for the DMA to finish. */
+ wait_event(sport->dma_wait,
+ !sport->dma_is_rxing && !sport->dma_is_txing);
+ imx_stop_rx(port);
+ imx_disable_dma(sport);
+ imx_uart_dma_exit(sport);
+ }
+
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
@@ -867,7 +1241,7 @@ static void imx_shutdown(struct uart_port *port)
if (USE_IRDA(sport)) {
struct imxuart_platform_data *pdata;
- pdata = sport->port.dev->platform_data;
+ pdata = dev_get_platdata(sport->port.dev);
if (pdata->irda_enable)
pdata->irda_enable(0);
}
@@ -901,9 +1275,17 @@ static void imx_shutdown(struct uart_port *port)
writel(temp, sport->port.membase + UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
- if (!uart_console(&sport->port)) {
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
+ clk_disable_unprepare(sport->clk_per);
+ clk_disable_unprepare(sport->clk_ipg);
+}
+
+static void imx_flush_buffer(struct uart_port *port)
+{
+ struct imx_port *sport = (struct imx_port *)port;
+
+ if (sport->dma_is_enabled) {
+ sport->tx_bytes = 0;
+ dmaengine_terminate_all(sport->dma_chan_tx);
}
}
@@ -947,6 +1329,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
if (sport->have_rtscts) {
ucr2 &= ~UCR2_IRTS;
ucr2 |= UCR2_CTSC;
+
+ /* Can we enable the DMA support? */
+ if (is_imx6q_uart(sport) && !uart_console(port)
+ && !sport->dma_is_inited)
+ imx_uart_dma_init(sport);
} else {
termios->c_cflag &= ~CRTSCTS;
}
@@ -1020,6 +1407,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
*/
div = 1;
} else {
+ /* custom-baudrate handling */
+ div = sport->port.uartclk / (baud * 16);
+ if (baud == 38400 && quot != div)
+ baud = sport->port.uartclk / (quot * 16);
+
div = sport->port.uartclk / (baud * 16);
if (div > 7)
div = 7;
@@ -1048,7 +1440,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
writel(num, sport->port.membase + UBIR);
writel(denom, sport->port.membase + UBMR);
- if (is_imx21_uart(sport))
+ if (!is_imx1_uart(sport))
writel(sport->port.uartclk / div / 1000,
sport->port.membase + IMX21_ONEMS);
@@ -1060,6 +1452,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
imx_enable_ms(&sport->port);
+ if (sport->dma_is_inited && !sport->dma_is_enabled)
+ imx_enable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
@@ -1071,44 +1465,13 @@ static const char *imx_type(struct uart_port *port)
}
/*
- * Release the memory region(s) being used by 'port'.
- */
-static void imx_release_port(struct uart_port *port)
-{
- struct platform_device *pdev = to_platform_device(port->dev);
- struct resource *mmres;
-
- mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mmres->start, resource_size(mmres));
-}
-
-/*
- * Request the memory region(s) being used by 'port'.
- */
-static int imx_request_port(struct uart_port *port)
-{
- struct platform_device *pdev = to_platform_device(port->dev);
- struct resource *mmres;
- void *ret;
-
- mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mmres)
- return -ENODEV;
-
- ret = request_mem_region(mmres->start, resource_size(mmres), "imx-uart");
-
- return ret ? 0 : -EBUSY;
-}
-
-/*
* Configure/autoconfigure the port.
*/
static void imx_config_port(struct uart_port *port, int flags)
{
struct imx_port *sport = (struct imx_port *)port;
- if (flags & UART_CONFIG_TYPE &&
- imx_request_port(&sport->port) == 0)
+ if (flags & UART_CONFIG_TYPE)
sport->port.type = PORT_IMX;
}
@@ -1131,7 +1494,7 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)
ret = -EINVAL;
if (sport->port.uartclk / 16 != ser->baud_base)
ret = -EINVAL;
- if ((void *)sport->port.mapbase != ser->iomem_base)
+ if (sport->port.mapbase != (unsigned long)ser->iomem_base)
ret = -EINVAL;
if (sport->port.iobase != ser->port)
ret = -EINVAL;
@@ -1215,10 +1578,9 @@ static struct uart_ops imx_pops = {
.break_ctl = imx_break_ctl,
.startup = imx_startup,
.shutdown = imx_shutdown,
+ .flush_buffer = imx_flush_buffer,
.set_termios = imx_set_termios,
.type = imx_type,
- .release_port = imx_release_port,
- .request_port = imx_request_port,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
#if defined(CONFIG_CONSOLE_POLL)
@@ -1251,6 +1613,16 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
unsigned int ucr1;
unsigned long flags = 0;
int locked = 1;
+ int retval;
+
+ retval = clk_enable(sport->clk_per);
+ if (retval)
+ return;
+ retval = clk_enable(sport->clk_ipg);
+ if (retval) {
+ clk_disable(sport->clk_per);
+ return;
+ }
if (sport->port.sysrq)
locked = 0;
@@ -1286,6 +1658,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ clk_disable(sport->clk_ipg);
+ clk_disable(sport->clk_per);
}
/*
@@ -1359,6 +1734,7 @@ imx_console_setup(struct console *co, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
+ int retval;
/*
* Check whether an invalid uart number has been specified, and
@@ -1371,6 +1747,11 @@ imx_console_setup(struct console *co, char *options)
if (sport == NULL)
return -ENODEV;
+ /* For setting the registers, we only need to enable the ipg clock. */
+ retval = clk_prepare_enable(sport->clk_ipg);
+ if (retval)
+ goto error_console;
+
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
@@ -1378,7 +1759,20 @@ imx_console_setup(struct console *co, char *options)
imx_setup_ufcr(sport, 0);
- return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+ retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
+
+ clk_disable(sport->clk_ipg);
+ if (retval) {
+ clk_unprepare(sport->clk_ipg);
+ goto error_console;
+ }
+
+ retval = clk_prepare(sport->clk_per);
+ if (retval)
+ clk_disable_unprepare(sport->clk_ipg);
+
+error_console:
+ return retval;
}
static struct uart_driver imx_reg;
@@ -1485,7 +1879,7 @@ static inline int serial_imx_probe_dt(struct imx_port *sport,
static void serial_imx_probe_pdata(struct imx_port *sport,
struct platform_device *pdev)
{
- struct imxuart_platform_data *pdata = pdev->dev.platform_data;
+ struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev);
sport->port.line = pdev->id;
sport->devdata = (struct imx_uart_data *) pdev->id_entry->driver_data;
@@ -1503,11 +1897,9 @@ static void serial_imx_probe_pdata(struct imx_port *sport,
static int serial_imx_probe(struct platform_device *pdev)
{
struct imx_port *sport;
- struct imxuart_platform_data *pdata;
void __iomem *base;
int ret = 0;
struct resource *res;
- struct pinctrl *pinctrl;
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
if (!sport)
@@ -1520,12 +1912,9 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
- if (!base)
- return -ENOMEM;
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
@@ -1543,13 +1932,6 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret);
- return ret;
- }
-
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {
ret = PTR_ERR(sport->clk_ipg);
@@ -1564,55 +1946,20 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
- clk_prepare_enable(sport->clk_per);
- clk_prepare_enable(sport->clk_ipg);
-
sport->port.uartclk = clk_get_rate(sport->clk_per);
imx_ports[sport->port.line] = sport;
- pdata = pdev->dev.platform_data;
- if (pdata && pdata->init) {
- ret = pdata->init(pdev);
- if (ret)
- goto clkput;
- }
-
- ret = uart_add_one_port(&imx_reg, &sport->port);
- if (ret)
- goto deinit;
platform_set_drvdata(pdev, sport);
- if (!uart_console(&sport->port)) {
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
- }
-
- return 0;
-deinit:
- if (pdata && pdata->exit)
- pdata->exit(pdev);
-clkput:
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
- return ret;
+ return uart_add_one_port(&imx_reg, &sport->port);
}
static int serial_imx_remove(struct platform_device *pdev)
{
- struct imxuart_platform_data *pdata;
struct imx_port *sport = platform_get_drvdata(pdev);
- pdata = pdev->dev.platform_data;
-
- platform_set_drvdata(pdev, NULL);
-
- uart_remove_one_port(&imx_reg, &sport->port);
-
- if (pdata && pdata->exit)
- pdata->exit(pdev);
-
- return 0;
+ return uart_remove_one_port(&imx_reg, &sport->port);
}
static struct platform_driver serial_imx_driver = {
diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c
index e2520abcb1c..1274499850f 100644
--- a/drivers/tty/serial/ioc4_serial.c
+++ b/drivers/tty/serial/ioc4_serial.c
@@ -297,7 +297,7 @@ struct ioc4_serial {
struct ioc4_uartregs uart_1;
struct ioc4_uartregs uart_2;
struct ioc4_uartregs uart_3;
-} ioc4_serial;
+};
/* UART clock speed */
#define IOC4_SER_XIN_CLK_66 66666667
@@ -2767,7 +2767,7 @@ ioc4_serial_core_attach(struct pci_dev *pdev, int port_type)
* called per card found from IOC4 master module.
* @idd: Master module data for this IOC4
*/
-int
+static int
ioc4_serial_attach_one(struct ioc4_driver_data *idd)
{
unsigned long tmp_addr1;
diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c
index cb3c81eb099..99b7b869786 100644
--- a/drivers/tty/serial/ip22zilog.c
+++ b/drivers/tty/serial/ip22zilog.c
@@ -603,6 +603,8 @@ static void ip22zilog_start_tx(struct uart_port *port)
} else {
struct circ_buf *xmit = &port->state->xmit;
+ if (uart_circ_empty(xmit))
+ return;
writeb(xmit->buf[xmit->tail], &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
@@ -832,7 +834,7 @@ ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag,
up->curregs[5] |= Tx8;
up->parity_mask = 0xff;
break;
- };
+ }
up->curregs[4] &= ~0x0c;
if (cflag & CSTOPB)
up->curregs[4] |= SB2;
@@ -850,7 +852,7 @@ ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag,
up->port.read_status_mask = Rx_OVR;
if (iflag & INPCK)
up->port.read_status_mask |= CRC_ERR | PAR_ERR;
- if (iflag & (BRKINT | PARMRK))
+ if (iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= BRK_ABRT;
up->port.ignore_status_mask = 0;
diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c
index 5dafcf1c227..cfadf2971b1 100644
--- a/drivers/tty/serial/kgdb_nmi.c
+++ b/drivers/tty/serial/kgdb_nmi.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/compiler.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/atomic.h>
@@ -45,13 +44,22 @@ MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)");
static bool kgdb_nmi_tty_enabled;
+static int kgdb_nmi_console_setup(struct console *co, char *options)
+{
+ /* The NMI console uses the dbg_io_ops to issue console messages. To
+ * avoid duplicate messages during kdb sessions we must inform kdb's
+ * I/O utilities that messages sent to the console will automatically
+ * be displayed on the dbg_io.
+ */
+ dbg_io_ops->is_console = true;
+
+ return 0;
+}
+
static void kgdb_nmi_console_write(struct console *co, const char *s, uint c)
{
int i;
- if (!kgdb_nmi_tty_enabled || atomic_read(&kgdb_active) >= 0)
- return;
-
for (i = 0; i < c; i++)
dbg_io_ops->write_char(s[i]);
}
@@ -66,6 +74,7 @@ static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx)
static struct console kgdb_nmi_console = {
.name = "ttyNMI",
+ .setup = kgdb_nmi_console_setup,
.write = kgdb_nmi_console_write,
.device = kgdb_nmi_console_device,
.flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
@@ -81,29 +90,10 @@ static struct console kgdb_nmi_console = {
struct kgdb_nmi_tty_priv {
struct tty_port port;
- struct tasklet_struct tlet;
+ struct timer_list timer;
STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
};
-static struct kgdb_nmi_tty_priv *kgdb_nmi_port_to_priv(struct tty_port *port)
-{
- return container_of(port, struct kgdb_nmi_tty_priv, port);
-}
-
-/*
- * Our debugging console is polled in a tasklet, so we'll check for input
- * every tick. In HZ-less mode, we should program the next tick. We have
- * to use the lowlevel stuff as no locks should be grabbed.
- */
-#ifdef CONFIG_HIGH_RES_TIMERS
-static void kgdb_tty_poke(void)
-{
- tick_program_event(ktime_get(), 0);
-}
-#else
-static inline void kgdb_tty_poke(void) {}
-#endif
-
static struct tty_port *kgdb_nmi_port;
static void kgdb_tty_recv(int ch)
@@ -114,14 +104,13 @@ static void kgdb_tty_recv(int ch)
if (!kgdb_nmi_port || ch < 0)
return;
/*
- * Can't use port->tty->driver_data as tty might be not there. Tasklet
+ * Can't use port->tty->driver_data as tty might be not there. Timer
* will check for tty and will get the ref, but here we don't have to
* do that, and actually, we can't: we're in NMI context, no locks are
* possible.
*/
- priv = kgdb_nmi_port_to_priv(kgdb_nmi_port);
+ priv = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port);
kfifo_in(&priv->fifo, &c, 1);
- kgdb_tty_poke();
}
static int kgdb_nmi_poll_one_knock(void)
@@ -205,7 +194,8 @@ static void kgdb_nmi_tty_receiver(unsigned long data)
struct kgdb_nmi_tty_priv *priv = (void *)data;
char ch;
- tasklet_schedule(&priv->tlet);
+ priv->timer.expires = jiffies + (HZ/100);
+ add_timer(&priv->timer);
if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo)))
return;
@@ -217,18 +207,22 @@ static void kgdb_nmi_tty_receiver(unsigned long data)
static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty)
{
- struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+ struct kgdb_nmi_tty_priv *priv =
+ container_of(port, struct kgdb_nmi_tty_priv, port);
kgdb_nmi_port = port;
- tasklet_schedule(&priv->tlet);
+ priv->timer.expires = jiffies + (HZ/100);
+ add_timer(&priv->timer);
+
return 0;
}
static void kgdb_nmi_tty_shutdown(struct tty_port *port)
{
- struct kgdb_nmi_tty_priv *priv = port->tty->driver_data;
+ struct kgdb_nmi_tty_priv *priv =
+ container_of(port, struct kgdb_nmi_tty_priv, port);
- tasklet_kill(&priv->tlet);
+ del_timer(&priv->timer);
kgdb_nmi_port = NULL;
}
@@ -247,7 +241,7 @@ static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty)
return -ENOMEM;
INIT_KFIFO(priv->fifo);
- tasklet_init(&priv->tlet, kgdb_nmi_tty_receiver, (unsigned long)priv);
+ setup_timer(&priv->timer, kgdb_nmi_tty_receiver, (unsigned long)priv);
tty_port_init(&priv->port);
priv->port.ops = &kgdb_nmi_tty_port_ops;
tty->driver_data = priv;
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 10020547c60..a260cde743e 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -45,7 +45,7 @@ static int kgdboc_reset_connect(struct input_handler *handler,
{
input_reset_device(dev);
- /* Retrun an error - we do not want to bind, just to reset */
+ /* Return an error - we do not want to bind, just to reset */
return -ENODEV;
}
diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c
index 15733da757c..88d01e0bb0c 100644
--- a/drivers/tty/serial/lantiq.c
+++ b/drivers/tty/serial/lantiq.c
@@ -318,7 +318,7 @@ lqasc_startup(struct uart_port *port)
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
int retval;
- if (ltq_port->clk)
+ if (!IS_ERR(ltq_port->clk))
clk_enable(ltq_port->clk);
port->uartclk = clk_get_rate(ltq_port->fpiclk);
@@ -386,7 +386,7 @@ lqasc_shutdown(struct uart_port *port)
port->membase + LTQ_ASC_RXFCON);
ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
port->membase + LTQ_ASC_TXFCON);
- if (ltq_port->clk)
+ if (!IS_ERR(ltq_port->clk))
clk_disable(ltq_port->clk);
}
@@ -636,6 +636,9 @@ lqasc_console_setup(struct console *co, char *options)
port = &ltq_port->port;
+ if (!IS_ERR(ltq_port->clk))
+ clk_enable(ltq_port->clk);
+
port->uartclk = clk_get_rate(ltq_port->fpiclk);
if (options)
diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c
index dffea6b2cd7..701644f0682 100644
--- a/drivers/tty/serial/lpc32xx_hs.c
+++ b/drivers/tty/serial/lpc32xx_hs.c
@@ -279,7 +279,10 @@ static void __serial_lpc32xx_rx(struct uart_port *port)
tmp = readl(LPC32XX_HSUART_FIFO(port->membase));
}
+
+ spin_unlock(&port->lock);
tty_flip_buffer_push(tport);
+ spin_lock(&port->lock);
}
static void __serial_lpc32xx_tx(struct uart_port *port)
@@ -351,10 +354,8 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id)
}
/* Data received? */
- if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) {
+ if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT))
__serial_lpc32xx_rx(port);
- tty_flip_buffer_push(tport);
- }
/* Transmit data request? */
if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) {
diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c
index bb1afa0922e..5702828fb62 100644
--- a/drivers/tty/serial/m32r_sio.c
+++ b/drivers/tty/serial/m32r_sio.c
@@ -266,9 +266,11 @@ static void m32r_sio_start_tx(struct uart_port *port)
if (!(up->ier & UART_IER_THRI)) {
up->ier |= UART_IER_THRI;
serial_out(up, UART_IER, up->ier);
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ if (!uart_circ_empty(xmit)) {
+ serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ up->port.icount.tx++;
+ }
}
while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY);
#else
@@ -368,7 +370,10 @@ static void receive_chars(struct uart_sio_port *up, int *status)
ignore_char:
*status = serial_in(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+
+ spin_unlock(&up->port.lock);
tty_flip_buffer_push(port);
+ spin_lock(&up->port.lock);
}
static void transmit_chars(struct uart_sio_port *up)
@@ -734,7 +739,7 @@ static void m32r_sio_set_termios(struct uart_port *port,
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (termios->c_iflag & INPCK)
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= UART_LSR_BI;
/*
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index 35866d5872a..79f9a9eff54 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -779,7 +779,7 @@ static int max3100_probe(struct spi_device *spi)
max3100s[i]->irq = spi->irq;
spin_lock_init(&max3100s[i]->conf_lock);
spi_set_drvdata(spi, max3100s[i]);
- pdata = spi->dev.platform_data;
+ pdata = dev_get_platdata(&spi->dev);
max3100s[i]->crystal = pdata->crystal;
max3100s[i]->loopback = pdata->loopback;
max3100s[i]->poll_time = pdata->poll_time * HZ / 1000;
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 8941e641894..ba285cd45b5 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -1,7 +1,7 @@
/*
- * Maxim (Dallas) MAX3107/8 serial driver
+ * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
*
- * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ * Copyright (C) 2012-2014 Alexander Shiyan <shc_work@mail.ru>
*
* Based on max3100.c, by Christian Pellegrin <chripell@evolware.org>
* Based on max3110.c, by Feng Tang <feng.tang@intel.com>
@@ -13,20 +13,23 @@
* (at your option) any later version.
*/
-/* TODO: MAX3109 support (Dual) */
-/* TODO: MAX14830 support (Quad) */
-
-#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
-#include <linux/regmap.h>
-#include <linux/gpio.h>
#include <linux/spi/spi.h>
-#include <linux/platform_data/max310x.h>
+#include <linux/uaccess.h>
+#define MAX310X_NAME "max310x"
#define MAX310X_MAJOR 204
#define MAX310X_MINOR 209
@@ -37,7 +40,8 @@
#define MAX310X_IRQSTS_REG (0x02) /* IRQ status */
#define MAX310X_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */
#define MAX310X_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */
-#define MAX310X_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */
+#define MAX310X_REG_05 (0x05)
+#define MAX310X_SPCHR_IRQEN_REG MAX310X_REG_05 /* Special char IRQ en */
#define MAX310X_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */
#define MAX310X_STS_IRQEN_REG (0x07) /* Status IRQ enable */
#define MAX310X_STS_IRQSTS_REG (0x08) /* Status IRQ status */
@@ -63,8 +67,15 @@
#define MAX310X_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */
#define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */
#define MAX310X_CLKSRC_REG (0x1e) /* Clock source */
-/* Only present in MAX3107 */
-#define MAX3107_REVID_REG (0x1f) /* Revision identification */
+#define MAX310X_REG_1F (0x1f)
+
+#define MAX310X_REVID_REG MAX310X_REG_1F /* Revision ID */
+
+#define MAX310X_GLOBALIRQ_REG MAX310X_REG_1F /* Global IRQ (RO) */
+#define MAX310X_GLOBALCMD_REG MAX310X_REG_1F /* Global Command (WO) */
+
+/* Extended registers */
+#define MAX310X_REVID_EXTREG MAX310X_REG_05 /* Revision ID */
/* IRQ register bits */
#define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
@@ -152,10 +163,6 @@
/* IRDA register bits */
#define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */
#define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */
-#define MAX310X_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */
-#define MAX310X_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */
-#define MAX310X_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */
-#define MAX310X_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */
/* Flow control trigger level register masks */
#define MAX310X_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */
@@ -211,26 +218,6 @@
* XOFF2
*/
-/* GPIO configuration register bits */
-#define MAX310X_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */
-#define MAX310X_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */
-#define MAX310X_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */
-#define MAX310X_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */
-#define MAX310X_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */
-#define MAX310X_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */
-#define MAX310X_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */
-#define MAX310X_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */
-
-/* GPIO DATA register bits */
-#define MAX310X_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */
-#define MAX310X_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */
-#define MAX310X_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */
-#define MAX310X_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */
-#define MAX310X_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */
-#define MAX310X_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */
-#define MAX310X_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */
-#define MAX310X_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */
-
/* PLL configuration register masks */
#define MAX310X_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */
#define MAX310X_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */
@@ -246,58 +233,209 @@
#define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */
#define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */
+/* Global commands */
+#define MAX310X_EXTREG_ENBL (0xce)
+#define MAX310X_EXTREG_DSBL (0xcd)
+
/* Misc definitions */
#define MAX310X_FIFO_SIZE (128)
+#define MAX310x_REV_MASK (0xfc)
/* MAX3107 specific */
#define MAX3107_REV_ID (0xa0)
-#define MAX3107_REV_MASK (0xfe)
-
-/* IRQ status bits definitions */
-#define MAX310X_IRQ_TX (MAX310X_IRQ_TXFIFO_BIT | \
- MAX310X_IRQ_TXEMPTY_BIT)
-#define MAX310X_IRQ_RX (MAX310X_IRQ_RXFIFO_BIT | \
- MAX310X_IRQ_RXEMPTY_BIT)
-
-/* Supported chip types */
-enum {
- MAX310X_TYPE_MAX3107 = 3107,
- MAX310X_TYPE_MAX3108 = 3108,
+
+/* MAX3109 specific */
+#define MAX3109_REV_ID (0xc0)
+
+/* MAX14830 specific */
+#define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */
+#define MAX14830_REV_ID (0xb0)
+
+struct max310x_devtype {
+ char name[9];
+ int nr;
+ int (*detect)(struct device *);
+ void (*power)(struct uart_port *, int);
};
-struct max310x_port {
- struct uart_driver uart;
+struct max310x_one {
struct uart_port port;
+ struct work_struct tx_work;
+ struct work_struct md_work;
+};
- const char *name;
- int uartclk;
-
- unsigned int nr_gpio;
+struct max310x_port {
+ struct uart_driver uart;
+ struct max310x_devtype *devtype;
+ struct regmap *regmap;
+ struct mutex mutex;
+ struct clk *clk;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
#endif
+ struct max310x_one p[0];
+};
- struct regmap *regmap;
- struct regmap_config regcfg;
+static u8 max310x_port_read(struct uart_port *port, u8 reg)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+ unsigned int val = 0;
- struct workqueue_struct *wq;
- struct work_struct tx_work;
+ regmap_read(s->regmap, port->iobase + reg, &val);
+
+ return val;
+}
+
+static void max310x_port_write(struct uart_port *port, u8 reg, u8 val)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+
+ regmap_write(s->regmap, port->iobase + reg, val);
+}
+
+static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+
+ regmap_update_bits(s->regmap, port->iobase + reg, mask, val);
+}
+
+static int max3107_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val);
+ if (ret)
+ return ret;
+
+ if (((val & MAX310x_REV_MASK) != MAX3107_REV_ID)) {
+ dev_err(dev,
+ "%s ID 0x%02x does not match\n", s->devtype->name, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int max3108_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ /* MAX3108 have not REV ID register, we just check default value
+ * from clocksource register to make sure everything works.
+ */
+ ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val);
+ if (ret)
+ return ret;
+
+ if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) {
+ dev_err(dev, "%s not present\n", s->devtype->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int max3109_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val);
+ if (ret)
+ return ret;
- struct mutex max310x_mutex;
+ if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) {
+ dev_err(dev,
+ "%s ID 0x%02x does not match\n", s->devtype->name, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void max310x_power(struct uart_port *port, int on)
+{
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_FORCESLEEP_BIT,
+ on ? 0 : MAX310X_MODE1_FORCESLEEP_BIT);
+ if (on)
+ msleep(50);
+}
- struct max310x_pdata *pdata;
+static int max14830_detect(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ unsigned int val = 0;
+ int ret;
+
+ ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG,
+ MAX310X_EXTREG_ENBL);
+ if (ret)
+ return ret;
+
+ regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val);
+ regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL);
+ if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) {
+ dev_err(dev,
+ "%s ID 0x%02x does not match\n", s->devtype->name, val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void max14830_power(struct uart_port *port, int on)
+{
+ max310x_port_update(port, MAX310X_BRGCFG_REG,
+ MAX14830_BRGCFG_CLKDIS_BIT,
+ on ? 0 : MAX14830_BRGCFG_CLKDIS_BIT);
+ if (on)
+ msleep(50);
+}
+
+static const struct max310x_devtype max3107_devtype = {
+ .name = "MAX3107",
+ .nr = 1,
+ .detect = max3107_detect,
+ .power = max310x_power,
+};
+
+static const struct max310x_devtype max3108_devtype = {
+ .name = "MAX3108",
+ .nr = 1,
+ .detect = max3108_detect,
+ .power = max310x_power,
+};
+
+static const struct max310x_devtype max3109_devtype = {
+ .name = "MAX3109",
+ .nr = 2,
+ .detect = max3109_detect,
+ .power = max310x_power,
+};
+
+static const struct max310x_devtype max14830_devtype = {
+ .name = "MAX14830",
+ .nr = 4,
+ .detect = max14830_detect,
+ .power = max14830_power,
};
-static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg)
+static bool max310x_reg_writeable(struct device *dev, unsigned int reg)
{
- switch (reg) {
+ switch (reg & 0x1f) {
case MAX310X_IRQSTS_REG:
case MAX310X_LSR_IRQSTS_REG:
case MAX310X_SPCHR_IRQSTS_REG:
case MAX310X_STS_IRQSTS_REG:
case MAX310X_TXFIFOLVL_REG:
case MAX310X_RXFIFOLVL_REG:
- case MAX3107_REVID_REG: /* Only available on MAX3107 */
return false;
default:
break;
@@ -308,7 +446,7 @@ static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg)
static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
{
- switch (reg) {
+ switch (reg & 0x1f) {
case MAX310X_RHR_REG:
case MAX310X_IRQSTS_REG:
case MAX310X_LSR_IRQSTS_REG:
@@ -317,6 +455,9 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
case MAX310X_TXFIFOLVL_REG:
case MAX310X_RXFIFOLVL_REG:
case MAX310X_GPIODATA_REG:
+ case MAX310X_BRGDIVLSB_REG:
+ case MAX310X_REG_05:
+ case MAX310X_REG_1F:
return true;
default:
break;
@@ -327,7 +468,7 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
static bool max310x_reg_precious(struct device *dev, unsigned int reg)
{
- switch (reg) {
+ switch (reg & 0x1f) {
case MAX310X_RHR_REG:
case MAX310X_IRQSTS_REG:
case MAX310X_SPCHR_IRQSTS_REG:
@@ -340,42 +481,33 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg)
return false;
}
-static void max310x_set_baud(struct max310x_port *s, int baud)
+static int max310x_set_baud(struct uart_port *port, int baud)
{
- unsigned int mode = 0, div = s->uartclk / baud;
+ unsigned int mode = 0, clk = port->uartclk, div = clk / baud;
+
+ /* Check for minimal value for divider */
+ if (div < 16)
+ div = 16;
- if (!(div / 16)) {
+ if (clk % baud && (div / 16) < 0x8000) {
/* Mode x2 */
mode = MAX310X_BRGCFG_2XMODE_BIT;
- div = (s->uartclk * 2) / baud;
+ clk = port->uartclk * 2;
+ div = clk / baud;
+
+ if (clk % baud && (div / 16) < 0x8000) {
+ /* Mode x4 */
+ mode = MAX310X_BRGCFG_4XMODE_BIT;
+ clk = port->uartclk * 4;
+ div = clk / baud;
+ }
}
- if (!(div / 16)) {
- /* Mode x4 */
- mode = MAX310X_BRGCFG_4XMODE_BIT;
- div = (s->uartclk * 4) / baud;
- }
+ max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8);
+ max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16);
+ max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode);
- regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG,
- ((div / 16) >> 8) & 0xff);
- regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff);
- regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode);
-}
-
-static void max310x_wait_pll(struct max310x_port *s)
-{
- int tryes = 1000;
-
- /* Wait for PLL only if crystal is used */
- if (!(s->pdata->driver_flags & MAX310X_EXT_CLK)) {
- unsigned int sts = 0;
-
- while (tryes--) {
- regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts);
- if (sts & MAX310X_STS_CLKREADY_BIT)
- break;
- }
- }
+ return DIV_ROUND_CLOSEST(clk, div);
}
static int max310x_update_best_err(unsigned long f, long *besterr)
@@ -391,18 +523,19 @@ static int max310x_update_best_err(unsigned long f, long *besterr)
return 1;
}
-static int max310x_set_ref_clk(struct max310x_port *s)
+static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq,
+ bool xtal)
{
unsigned int div, clksrc, pllcfg = 0;
long besterr = -1;
- unsigned long fdiv, fmul, bestfreq = s->pdata->frequency;
+ unsigned long fdiv, fmul, bestfreq = freq;
/* First, update error without PLL */
- max310x_update_best_err(s->pdata->frequency, &besterr);
+ max310x_update_best_err(freq, &besterr);
/* Try all possible PLL dividers */
for (div = 1; (div <= 63) && besterr; div++) {
- fdiv = DIV_ROUND_CLOSEST(s->pdata->frequency, div);
+ fdiv = DIV_ROUND_CLOSEST(freq, div);
/* Try multiplier 6 */
fmul = fdiv * 6;
@@ -435,10 +568,7 @@ static int max310x_set_ref_clk(struct max310x_port *s)
}
/* Configure clock source */
- if (s->pdata->driver_flags & MAX310X_EXT_CLK)
- clksrc = MAX310X_CLKSRC_EXTCLK_BIT;
- else
- clksrc = MAX310X_CLKSRC_CRYST_BIT;
+ clksrc = xtal ? MAX310X_CLKSRC_CRYST_BIT : MAX310X_CLKSRC_EXTCLK_BIT;
/* Configure PLL */
if (pllcfg) {
@@ -449,49 +579,49 @@ static int max310x_set_ref_clk(struct max310x_port *s)
regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
- if (pllcfg)
- max310x_wait_pll(s);
-
- dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq);
+ /* Wait for crystal */
+ if (pllcfg && xtal)
+ msleep(10);
return (int)bestfreq;
}
-static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
+static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
{
- unsigned int sts = 0, ch = 0, flag;
+ unsigned int sts, ch, flag;
- if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) {
- dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen);
+ if (unlikely(rxlen >= port->fifosize)) {
+ dev_warn_ratelimited(port->dev,
+ "Port %i: Possible RX FIFO overrun\n",
+ port->line);
+ port->icount.buf_overrun++;
/* Ensure sanity of RX level */
- rxlen = MAX310X_FIFO_SIZE;
+ rxlen = port->fifosize;
}
- dev_dbg(s->port.dev, "RX Len = %u\n", rxlen);
-
while (rxlen--) {
- regmap_read(s->regmap, MAX310X_RHR_REG, &ch);
- regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts);
+ ch = max310x_port_read(port, MAX310X_RHR_REG);
+ sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT |
MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT;
- s->port.icount.rx++;
+ port->icount.rx++;
flag = TTY_NORMAL;
if (unlikely(sts)) {
if (sts & MAX310X_LSR_RXBRK_BIT) {
- s->port.icount.brk++;
- if (uart_handle_break(&s->port))
+ port->icount.brk++;
+ if (uart_handle_break(port))
continue;
} else if (sts & MAX310X_LSR_RXPAR_BIT)
- s->port.icount.parity++;
+ port->icount.parity++;
else if (sts & MAX310X_LSR_FRERR_BIT)
- s->port.icount.frame++;
+ port->icount.frame++;
else if (sts & MAX310X_LSR_RXOVR_BIT)
- s->port.icount.overrun++;
+ port->icount.overrun++;
- sts &= s->port.read_status_mask;
+ sts &= port->read_status_mask;
if (sts & MAX310X_LSR_RXBRK_BIT)
flag = TTY_BREAK;
else if (sts & MAX310X_LSR_RXPAR_BIT)
@@ -502,129 +632,129 @@ static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen)
flag = TTY_OVERRUN;
}
- if (uart_handle_sysrq_char(s->port, ch))
+ if (uart_handle_sysrq_char(port, ch))
continue;
- if (sts & s->port.ignore_status_mask)
+ if (sts & port->ignore_status_mask)
continue;
- uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT,
- ch, flag);
+ uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag);
}
- tty_flip_buffer_push(&s->port.state->port);
+ tty_flip_buffer_push(&port->state->port);
}
-static void max310x_handle_tx(struct max310x_port *s)
+static void max310x_handle_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &s->port.state->xmit;
- unsigned int txlen = 0, to_send;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int txlen, to_send;
- if (unlikely(s->port.x_char)) {
- regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char);
- s->port.icount.tx++;
- s->port.x_char = 0;
+ if (unlikely(port->x_char)) {
+ max310x_port_write(port, MAX310X_THR_REG, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
return;
}
- if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
return;
/* Get length of data pending in circular buffer */
to_send = uart_circ_chars_pending(xmit);
if (likely(to_send)) {
/* Limit to size of TX FIFO */
- regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen);
- txlen = MAX310X_FIFO_SIZE - txlen;
+ txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+ txlen = port->fifosize - txlen;
to_send = (to_send > txlen) ? txlen : to_send;
- dev_dbg(s->port.dev, "TX Len = %u\n", to_send);
-
/* Add data to send */
- s->port.icount.tx += to_send;
+ port->icount.tx += to_send;
while (to_send--) {
- regmap_write(s->regmap, MAX310X_THR_REG,
- xmit->buf[xmit->tail]);
+ max310x_port_write(port, MAX310X_THR_REG,
+ xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- };
+ }
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&s->port);
+ uart_write_wakeup(port);
}
-static irqreturn_t max310x_ist(int irq, void *dev_id)
+static void max310x_port_irq(struct max310x_port *s, int portno)
{
- struct max310x_port *s = (struct max310x_port *)dev_id;
- unsigned int ists = 0, lsr = 0, rxlen = 0;
+ struct uart_port *port = &s->p[portno].port;
- mutex_lock(&s->max310x_mutex);
+ do {
+ unsigned int ists, lsr, rxlen;
- for (;;) {
/* Read IRQ status & RX FIFO level */
- regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists);
- regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr);
- regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen);
- if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen)
+ ists = max310x_port_read(port, MAX310X_IRQSTS_REG);
+ rxlen = max310x_port_read(port, MAX310X_RXFIFOLVL_REG);
+ if (!ists && !rxlen)
break;
- dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists);
-
- if (rxlen)
- max310x_handle_rx(s, rxlen);
- if (ists & MAX310X_IRQ_TX)
- max310x_handle_tx(s);
- if (ists & MAX310X_IRQ_CTS_BIT)
- uart_handle_cts_change(&s->port,
+ if (ists & MAX310X_IRQ_CTS_BIT) {
+ lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
+ uart_handle_cts_change(port,
!!(lsr & MAX310X_LSR_CTS_BIT));
- }
+ }
+ if (rxlen)
+ max310x_handle_rx(port, rxlen);
+ if (ists & MAX310X_IRQ_TXEMPTY_BIT) {
+ mutex_lock(&s->mutex);
+ max310x_handle_tx(port);
+ mutex_unlock(&s->mutex);
+ }
+ } while (1);
+}
+
+static irqreturn_t max310x_ist(int irq, void *dev_id)
+{
+ struct max310x_port *s = (struct max310x_port *)dev_id;
+
+ if (s->uart.nr > 1) {
+ do {
+ unsigned int val = ~0;
- mutex_unlock(&s->max310x_mutex);
+ WARN_ON_ONCE(regmap_read(s->regmap,
+ MAX310X_GLOBALIRQ_REG, &val));
+ val = ((1 << s->uart.nr) - 1) & ~val;
+ if (!val)
+ break;
+ max310x_port_irq(s, fls(val) - 1);
+ } while (1);
+ } else
+ max310x_port_irq(s, 0);
return IRQ_HANDLED;
}
static void max310x_wq_proc(struct work_struct *ws)
{
- struct max310x_port *s = container_of(ws, struct max310x_port, tx_work);
+ struct max310x_one *one = container_of(ws, struct max310x_one, tx_work);
+ struct max310x_port *s = dev_get_drvdata(one->port.dev);
- mutex_lock(&s->max310x_mutex);
- max310x_handle_tx(s);
- mutex_unlock(&s->max310x_mutex);
+ mutex_lock(&s->mutex);
+ max310x_handle_tx(&one->port);
+ mutex_unlock(&s->mutex);
}
static void max310x_start_tx(struct uart_port *port)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
-
- queue_work(s->wq, &s->tx_work);
-}
-
-static void max310x_stop_tx(struct uart_port *port)
-{
- /* Do nothing */
-}
+ struct max310x_one *one = container_of(port, struct max310x_one, port);
-static void max310x_stop_rx(struct uart_port *port)
-{
- /* Do nothing */
+ if (!work_pending(&one->tx_work))
+ schedule_work(&one->tx_work);
}
static unsigned int max310x_tx_empty(struct uart_port *port)
{
- unsigned int val = 0;
- struct max310x_port *s = container_of(port, struct max310x_port, port);
+ unsigned int lvl, sts;
- mutex_lock(&s->max310x_mutex);
- regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val);
- mutex_unlock(&s->max310x_mutex);
+ lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+ sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
- return val ? 0 : TIOCSER_TEMT;
-}
-
-static void max310x_enable_ms(struct uart_port *port)
-{
- /* Modem status not supported */
+ return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0;
}
static unsigned int max310x_get_mctrl(struct uart_port *port)
@@ -635,37 +765,39 @@ static unsigned int max310x_get_mctrl(struct uart_port *port)
return TIOCM_DSR | TIOCM_CAR;
}
+static void max310x_md_proc(struct work_struct *ws)
+{
+ struct max310x_one *one = container_of(ws, struct max310x_one, md_work);
+
+ max310x_port_update(&one->port, MAX310X_MODE2_REG,
+ MAX310X_MODE2_LOOPBACK_BIT,
+ (one->port.mctrl & TIOCM_LOOP) ?
+ MAX310X_MODE2_LOOPBACK_BIT : 0);
+}
+
static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- /* DCD and DSR are not wired and CTS/RTS is hadnled automatically
- * so do nothing
- */
+ struct max310x_one *one = container_of(port, struct max310x_one, port);
+
+ schedule_work(&one->md_work);
}
static void max310x_break_ctl(struct uart_port *port, int break_state)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
-
- mutex_lock(&s->max310x_mutex);
- regmap_update_bits(s->regmap, MAX310X_LCR_REG,
- MAX310X_LCR_TXBREAK_BIT,
- break_state ? MAX310X_LCR_TXBREAK_BIT : 0);
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_LCR_REG,
+ MAX310X_LCR_TXBREAK_BIT,
+ break_state ? MAX310X_LCR_TXBREAK_BIT : 0);
}
static void max310x_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
unsigned int lcr, flow = 0;
int baud;
- mutex_lock(&s->max310x_mutex);
-
/* Mask termios capabilities we don't support */
termios->c_cflag &= ~CMSPAR;
- termios->c_iflag &= ~IXANY;
/* Word size */
switch (termios->c_cflag & CSIZE) {
@@ -696,14 +828,14 @@ static void max310x_set_termios(struct uart_port *port,
lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */
/* Update LCR register */
- regmap_write(s->regmap, MAX310X_LCR_REG, lcr);
+ max310x_port_write(port, MAX310X_LCR_REG, lcr);
/* Set read status mask */
port->read_status_mask = MAX310X_LSR_RXOVR_BIT;
if (termios->c_iflag & INPCK)
port->read_status_mask |= MAX310X_LSR_RXPAR_BIT |
MAX310X_LSR_FRERR_BIT;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= MAX310X_LSR_RXBRK_BIT;
/* Set status ignore mask */
@@ -717,8 +849,8 @@ static void max310x_set_termios(struct uart_port *port,
MAX310X_LSR_RXBRK_BIT;
/* Configure flow control */
- regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]);
- regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
+ max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]);
+ max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
if (termios->c_cflag & CRTSCTS)
flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT |
MAX310X_FLOWCTRL_AUTORTS_BIT;
@@ -728,7 +860,7 @@ static void max310x_set_termios(struct uart_port *port,
if (termios->c_iflag & IXOFF)
flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
MAX310X_FLOWCTRL_SWFLOWEN_BIT;
- regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow);
+ max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow);
/* Get baud rate generator configuration */
baud = uart_get_baud_rate(port, termios, old,
@@ -736,103 +868,110 @@ static void max310x_set_termios(struct uart_port *port,
port->uartclk / 4);
/* Setup baudrate generator */
- max310x_set_baud(s, baud);
+ baud = max310x_set_baud(port, baud);
/* Update timeout according to new baud rate */
uart_update_timeout(port, termios->c_cflag, baud);
-
- mutex_unlock(&s->max310x_mutex);
}
-static int max310x_startup(struct uart_port *port)
+static int max310x_ioctl(struct uart_port *port, unsigned int cmd,
+ unsigned long arg)
{
- unsigned int val, line = port->line;
- struct max310x_port *s = container_of(port, struct max310x_port, port);
-
- if (s->pdata->suspend)
- s->pdata->suspend(0);
+#if defined(TIOCSRS485) && defined(TIOCGRS485)
+ struct serial_rs485 rs485;
+ unsigned int val;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485)))
+ return -EFAULT;
+ if (rs485.delay_rts_before_send > 0x0f ||
+ rs485.delay_rts_after_send > 0x0f)
+ return -ERANGE;
+ val = (rs485.delay_rts_before_send << 4) |
+ rs485.delay_rts_after_send;
+ max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val);
+ if (rs485.flags & SER_RS485_ENABLED) {
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TRNSCVCTRL_BIT,
+ MAX310X_MODE1_TRNSCVCTRL_BIT);
+ max310x_port_update(port, MAX310X_MODE2_REG,
+ MAX310X_MODE2_ECHOSUPR_BIT,
+ MAX310X_MODE2_ECHOSUPR_BIT);
+ } else {
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TRNSCVCTRL_BIT, 0);
+ max310x_port_update(port, MAX310X_MODE2_REG,
+ MAX310X_MODE2_ECHOSUPR_BIT, 0);
+ }
+ return 0;
+ case TIOCGRS485:
+ memset(&rs485, 0, sizeof(rs485));
+ val = max310x_port_read(port, MAX310X_MODE1_REG);
+ rs485.flags = (val & MAX310X_MODE1_TRNSCVCTRL_BIT) ?
+ SER_RS485_ENABLED : 0;
+ rs485.flags |= SER_RS485_RTS_ON_SEND;
+ val = max310x_port_read(port, MAX310X_HDPIXDELAY_REG);
+ rs485.delay_rts_before_send = val >> 4;
+ rs485.delay_rts_after_send = val & 0x0f;
+ if (copy_to_user((void __user *)arg, &rs485, sizeof(rs485)))
+ return -EFAULT;
+ return 0;
+ default:
+ break;
+ }
+#endif
- mutex_lock(&s->max310x_mutex);
+ return -ENOIOCTLCMD;
+}
- /* Configure baud rate, 9600 as default */
- max310x_set_baud(s, 9600);
+static int max310x_startup(struct uart_port *port)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+ unsigned int val;
- /* Configure LCR register, 8N1 mode by default */
- val = MAX310X_LCR_WORD_LEN_8;
- regmap_write(s->regmap, MAX310X_LCR_REG, val);
+ s->devtype->power(port, 1);
/* Configure MODE1 register */
- regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
- MAX310X_MODE1_TRNSCVCTRL_BIT,
- (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL)
- ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0);
-
- /* Configure MODE2 register */
- val = MAX310X_MODE2_RXEMPTINV_BIT;
- if (s->pdata->uart_flags[line] & MAX310X_LOOPBACK)
- val |= MAX310X_MODE2_LOOPBACK_BIT;
- if (s->pdata->uart_flags[line] & MAX310X_ECHO_SUPRESS)
- val |= MAX310X_MODE2_ECHOSUPR_BIT;
-
- /* Reset FIFOs */
- val |= MAX310X_MODE2_FIFORST_BIT;
- regmap_write(s->regmap, MAX310X_MODE2_REG, val);
-
- /* Configure FIFO trigger level register */
- /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */
- val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64);
- regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val);
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TRNSCVCTRL_BIT, 0);
+
+ /* Configure MODE2 register & Reset FIFOs*/
+ val = MAX310X_MODE2_RXEMPTINV_BIT | MAX310X_MODE2_FIFORST_BIT;
+ max310x_port_write(port, MAX310X_MODE2_REG, val);
+ max310x_port_update(port, MAX310X_MODE2_REG,
+ MAX310X_MODE2_FIFORST_BIT, 0);
/* Configure flow control levels */
/* Flow control halt level 96, resume level 48 */
- val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96);
- regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val);
-
- /* Clear timeout register */
- regmap_write(s->regmap, MAX310X_RXTO_REG, 0);
+ max310x_port_write(port, MAX310X_FLOWLVL_REG,
+ MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96));
- /* Configure LSR interrupt enable register */
- /* Enable RX timeout interrupt */
- val = MAX310X_LSR_RXTO_BIT;
- regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val);
+ /* Clear IRQ status register */
+ max310x_port_read(port, MAX310X_IRQSTS_REG);
- /* Clear FIFO reset */
- regmap_update_bits(s->regmap, MAX310X_MODE2_REG,
- MAX310X_MODE2_FIFORST_BIT, 0);
-
- /* Clear IRQ status register by reading it */
- regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val);
-
- /* Configure interrupt enable register */
- /* Enable CTS change interrupt */
- val = MAX310X_IRQ_CTS_BIT;
- /* Enable RX, TX interrupts */
- val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX;
- regmap_write(s->regmap, MAX310X_IRQEN_REG, val);
-
- mutex_unlock(&s->max310x_mutex);
+ /* Enable RX, TX, CTS change interrupts */
+ val = MAX310X_IRQ_RXEMPTY_BIT | MAX310X_IRQ_TXEMPTY_BIT;
+ max310x_port_write(port, MAX310X_IRQEN_REG, val | MAX310X_IRQ_CTS_BIT);
return 0;
}
static void max310x_shutdown(struct uart_port *port)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
+ struct max310x_port *s = dev_get_drvdata(port->dev);
/* Disable all interrupts */
- mutex_lock(&s->max310x_mutex);
- regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_write(port, MAX310X_IRQEN_REG, 0);
- if (s->pdata->suspend)
- s->pdata->suspend(1);
+ s->devtype->power(port, 0);
}
static const char *max310x_type(struct uart_port *port)
{
- struct max310x_port *s = container_of(port, struct max310x_port, port);
+ struct max310x_port *s = dev_get_drvdata(port->dev);
- return (port->type == PORT_MAX310X) ? s->name : NULL;
+ return (port->type == PORT_MAX310X) ? s->devtype->name : NULL;
}
static int max310x_request_port(struct uart_port *port)
@@ -841,134 +980,102 @@ static int max310x_request_port(struct uart_port *port)
return 0;
}
-static void max310x_release_port(struct uart_port *port)
-{
- /* Do nothing */
-}
-
static void max310x_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_MAX310X;
}
-static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser)
+static int max310x_verify_port(struct uart_port *port, struct serial_struct *s)
{
- if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X))
- return 0;
- if (ser->irq == port->irq)
- return 0;
+ if ((s->type != PORT_UNKNOWN) && (s->type != PORT_MAX310X))
+ return -EINVAL;
+ if (s->irq != port->irq)
+ return -EINVAL;
+
+ return 0;
+}
- return -EINVAL;
+static void max310x_null_void(struct uart_port *port)
+{
+ /* Do nothing */
}
-static struct uart_ops max310x_ops = {
+static const struct uart_ops max310x_ops = {
.tx_empty = max310x_tx_empty,
.set_mctrl = max310x_set_mctrl,
.get_mctrl = max310x_get_mctrl,
- .stop_tx = max310x_stop_tx,
+ .stop_tx = max310x_null_void,
.start_tx = max310x_start_tx,
- .stop_rx = max310x_stop_rx,
- .enable_ms = max310x_enable_ms,
+ .stop_rx = max310x_null_void,
+ .enable_ms = max310x_null_void,
.break_ctl = max310x_break_ctl,
.startup = max310x_startup,
.shutdown = max310x_shutdown,
.set_termios = max310x_set_termios,
.type = max310x_type,
.request_port = max310x_request_port,
- .release_port = max310x_release_port,
+ .release_port = max310x_null_void,
.config_port = max310x_config_port,
.verify_port = max310x_verify_port,
+ .ioctl = max310x_ioctl,
};
-#ifdef CONFIG_PM_SLEEP
-
-static int max310x_suspend(struct device *dev)
+static int __maybe_unused max310x_suspend(struct device *dev)
{
- int ret;
struct max310x_port *s = dev_get_drvdata(dev);
+ int i;
- dev_dbg(dev, "Suspend\n");
-
- ret = uart_suspend_port(&s->uart, &s->port);
-
- mutex_lock(&s->max310x_mutex);
-
- /* Enable sleep mode */
- regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
- MAX310X_MODE1_FORCESLEEP_BIT,
- MAX310X_MODE1_FORCESLEEP_BIT);
-
- mutex_unlock(&s->max310x_mutex);
-
- if (s->pdata->suspend)
- s->pdata->suspend(1);
+ for (i = 0; i < s->uart.nr; i++) {
+ uart_suspend_port(&s->uart, &s->p[i].port);
+ s->devtype->power(&s->p[i].port, 0);
+ }
- return ret;
+ return 0;
}
-static int max310x_resume(struct device *dev)
+static int __maybe_unused max310x_resume(struct device *dev)
{
struct max310x_port *s = dev_get_drvdata(dev);
+ int i;
- dev_dbg(dev, "Resume\n");
-
- if (s->pdata->suspend)
- s->pdata->suspend(0);
-
- mutex_lock(&s->max310x_mutex);
-
- /* Disable sleep mode */
- regmap_update_bits(s->regmap, MAX310X_MODE1_REG,
- MAX310X_MODE1_FORCESLEEP_BIT,
- 0);
-
- max310x_wait_pll(s);
-
- mutex_unlock(&s->max310x_mutex);
+ for (i = 0; i < s->uart.nr; i++) {
+ s->devtype->power(&s->p[i].port, 1);
+ uart_resume_port(&s->uart, &s->p[i].port);
+ }
- return uart_resume_port(&s->uart, &s->port);
+ return 0;
}
static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);
-#define MAX310X_PM_OPS (&max310x_pm_ops)
-
-#else
-#define MAX310X_PM_OPS NULL
-#endif
#ifdef CONFIG_GPIOLIB
static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- unsigned int val = 0;
+ unsigned int val;
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
- regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val);
- mutex_unlock(&s->max310x_mutex);
+ val = max310x_port_read(port, MAX310X_GPIODATA_REG);
- return !!((val >> 4) & (1 << offset));
+ return !!((val >> 4) & (1 << (offset % 4)));
}
static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
- regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ?
- 1 << offset : 0);
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4),
+ value ? 1 << (offset % 4) : 0);
}
static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
-
- regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0);
-
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0);
return 0;
}
@@ -977,288 +1084,289 @@ static int max310x_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
- mutex_lock(&s->max310x_mutex);
-
- regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset,
- 1 << offset);
- regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ?
- 1 << offset : 0);
-
- mutex_unlock(&s->max310x_mutex);
+ max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4),
+ value ? 1 << (offset % 4) : 0);
+ max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4),
+ 1 << (offset % 4));
return 0;
}
#endif
-/* Generic platform data */
-static struct max310x_pdata generic_plat_data = {
- .driver_flags = MAX310X_EXT_CLK,
- .uart_flags[0] = MAX310X_ECHO_SUPRESS,
- .frequency = 26000000,
-};
-
-static int max310x_probe(struct spi_device *spi)
+static int max310x_probe(struct device *dev, struct max310x_devtype *devtype,
+ struct regmap *regmap, int irq, unsigned long flags)
{
+ int i, ret, fmin, fmax, freq, uartclk;
+ struct clk *clk_osc, *clk_xtal;
struct max310x_port *s;
- struct device *dev = &spi->dev;
- int chiptype = spi_get_device_id(spi)->driver_data;
- struct max310x_pdata *pdata = dev->platform_data;
- unsigned int val = 0;
- int ret;
+ bool xtal = false;
- /* Check for IRQ */
- if (spi->irq <= 0) {
- dev_err(dev, "No IRQ specified\n");
- return -ENOTSUPP;
- }
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
/* Alloc port structure */
- s = devm_kzalloc(dev, sizeof(struct max310x_port), GFP_KERNEL);
+ s = devm_kzalloc(dev, sizeof(*s) +
+ sizeof(struct max310x_one) * devtype->nr, GFP_KERNEL);
if (!s) {
dev_err(dev, "Error allocating port structure\n");
return -ENOMEM;
}
- dev_set_drvdata(dev, s);
- if (!pdata) {
- dev_warn(dev, "No platform data supplied, using defaults\n");
- pdata = &generic_plat_data;
+ clk_osc = devm_clk_get(dev, "osc");
+ clk_xtal = devm_clk_get(dev, "xtal");
+ if (!IS_ERR(clk_osc)) {
+ s->clk = clk_osc;
+ fmin = 500000;
+ fmax = 35000000;
+ } else if (!IS_ERR(clk_xtal)) {
+ s->clk = clk_xtal;
+ fmin = 1000000;
+ fmax = 4000000;
+ xtal = true;
+ } else if (PTR_ERR(clk_osc) == -EPROBE_DEFER ||
+ PTR_ERR(clk_xtal) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else {
+ dev_err(dev, "Cannot get clock\n");
+ return -EINVAL;
}
- s->pdata = pdata;
-
- /* Individual chip settings */
- switch (chiptype) {
- case MAX310X_TYPE_MAX3107:
- s->name = "MAX3107";
- s->nr_gpio = 4;
- s->uart.nr = 1;
- s->regcfg.max_register = 0x1f;
- break;
- case MAX310X_TYPE_MAX3108:
- s->name = "MAX3108";
- s->nr_gpio = 4;
- s->uart.nr = 1;
- s->regcfg.max_register = 0x1e;
- break;
- default:
- dev_err(dev, "Unsupported chip type %i\n", chiptype);
- return -ENOTSUPP;
- }
-
- /* Check input frequency */
- if ((pdata->driver_flags & MAX310X_EXT_CLK) &&
- ((pdata->frequency < 500000) || (pdata->frequency > 35000000)))
- goto err_freq;
- /* Check frequency for quartz */
- if (!(pdata->driver_flags & MAX310X_EXT_CLK) &&
- ((pdata->frequency < 1000000) || (pdata->frequency > 4000000)))
- goto err_freq;
- mutex_init(&s->max310x_mutex);
+ ret = clk_prepare_enable(s->clk);
+ if (ret)
+ return ret;
- /* Setup SPI bus */
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
- spi->max_speed_hz = 26000000;
- spi_setup(spi);
-
- /* Setup regmap */
- s->regcfg.reg_bits = 8;
- s->regcfg.val_bits = 8;
- s->regcfg.read_flag_mask = 0x00;
- s->regcfg.write_flag_mask = 0x80;
- s->regcfg.cache_type = REGCACHE_RBTREE;
- s->regcfg.writeable_reg = max3107_8_reg_writeable;
- s->regcfg.volatile_reg = max310x_reg_volatile;
- s->regcfg.precious_reg = max310x_reg_precious;
- s->regmap = devm_regmap_init_spi(spi, &s->regcfg);
- if (IS_ERR(s->regmap)) {
- ret = PTR_ERR(s->regmap);
- dev_err(dev, "Failed to initialize register map\n");
- goto err_out;
+ freq = clk_get_rate(s->clk);
+ /* Check frequency limits */
+ if (freq < fmin || freq > fmax) {
+ ret = -ERANGE;
+ goto out_clk;
}
- /* Reset chip & check SPI function */
- ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT);
- if (ret) {
- dev_err(dev, "SPI transfer failed\n");
- goto err_out;
- }
- /* Clear chip reset */
- regmap_write(s->regmap, MAX310X_MODE2_REG, 0);
-
- switch (chiptype) {
- case MAX310X_TYPE_MAX3107:
- /* Check REV ID to ensure we are talking to what we expect */
- regmap_read(s->regmap, MAX3107_REVID_REG, &val);
- if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) {
- dev_err(dev, "%s ID 0x%02x does not match\n",
- s->name, val);
- ret = -ENODEV;
- goto err_out;
- }
- break;
- case MAX310X_TYPE_MAX3108:
- /* MAX3108 have not REV ID register, we just check default value
- * from clocksource register to make sure everything works.
- */
- regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val);
- if (val != (MAX310X_CLKSRC_EXTCLK_BIT |
- MAX310X_CLKSRC_PLLBYP_BIT)) {
- dev_err(dev, "%s not present\n", s->name);
- ret = -ENODEV;
- goto err_out;
- }
- break;
- }
-
- /* Board specific configure */
- if (pdata->init)
- pdata->init();
- if (pdata->suspend)
- pdata->suspend(0);
-
- /* Calculate referecne clock */
- s->uartclk = max310x_set_ref_clk(s);
-
- /* Disable all interrupts */
- regmap_write(s->regmap, MAX310X_IRQEN_REG, 0);
-
- /* Setup MODE1 register */
- val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */
- if (pdata->driver_flags & MAX310X_AUTOSLEEP)
- val = MAX310X_MODE1_AUTOSLEEP_BIT;
- regmap_write(s->regmap, MAX310X_MODE1_REG, val);
+ s->regmap = regmap;
+ s->devtype = devtype;
+ dev_set_drvdata(dev, s);
- /* Setup interrupt */
- ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(dev), s);
- if (ret) {
- dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq);
- goto err_out;
+ /* Check device to ensure we are talking to what we expect */
+ ret = devtype->detect(dev);
+ if (ret)
+ goto out_clk;
+
+ for (i = 0; i < devtype->nr; i++) {
+ unsigned int offs = i << 5;
+
+ /* Reset port */
+ regmap_write(s->regmap, MAX310X_MODE2_REG + offs,
+ MAX310X_MODE2_RST_BIT);
+ /* Clear port reset */
+ regmap_write(s->regmap, MAX310X_MODE2_REG + offs, 0);
+
+ /* Wait for port startup */
+ do {
+ regmap_read(s->regmap,
+ MAX310X_BRGDIVLSB_REG + offs, &ret);
+ } while (ret != 0x01);
+
+ regmap_update_bits(s->regmap, MAX310X_MODE1_REG + offs,
+ MAX310X_MODE1_AUTOSLEEP_BIT,
+ MAX310X_MODE1_AUTOSLEEP_BIT);
}
+ uartclk = max310x_set_ref_clk(s, freq, xtal);
+ dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk);
+
/* Register UART driver */
s->uart.owner = THIS_MODULE;
- s->uart.driver_name = dev_name(dev);
s->uart.dev_name = "ttyMAX";
s->uart.major = MAX310X_MAJOR;
s->uart.minor = MAX310X_MINOR;
+ s->uart.nr = devtype->nr;
ret = uart_register_driver(&s->uart);
if (ret) {
dev_err(dev, "Registering UART driver failed\n");
- goto err_out;
+ goto out_clk;
}
- /* Initialize workqueue for start TX */
- s->wq = create_freezable_workqueue(dev_name(dev));
- INIT_WORK(&s->tx_work, max310x_wq_proc);
-
- /* Initialize UART port data */
- s->port.line = 0;
- s->port.dev = dev;
- s->port.irq = spi->irq;
- s->port.type = PORT_MAX310X;
- s->port.fifosize = MAX310X_FIFO_SIZE;
- s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
- s->port.iotype = UPIO_PORT;
- s->port.membase = (void __iomem *)0xffffffff; /* Bogus value */
- s->port.uartclk = s->uartclk;
- s->port.ops = &max310x_ops;
- uart_add_one_port(&s->uart, &s->port);
-
#ifdef CONFIG_GPIOLIB
/* Setup GPIO cotroller */
- if (pdata->gpio_base) {
- s->gpio.owner = THIS_MODULE;
- s->gpio.dev = dev;
- s->gpio.label = dev_name(dev);
- s->gpio.direction_input = max310x_gpio_direction_input;
- s->gpio.get = max310x_gpio_get;
- s->gpio.direction_output= max310x_gpio_direction_output;
- s->gpio.set = max310x_gpio_set;
- s->gpio.base = pdata->gpio_base;
- s->gpio.ngpio = s->nr_gpio;
- s->gpio.can_sleep = 1;
- if (gpiochip_add(&s->gpio)) {
- /* Indicate that we should not call gpiochip_remove */
- s->gpio.base = 0;
- }
- } else
- dev_info(dev, "GPIO support not enabled\n");
+ s->gpio.owner = THIS_MODULE;
+ s->gpio.dev = dev;
+ s->gpio.label = dev_name(dev);
+ s->gpio.direction_input = max310x_gpio_direction_input;
+ s->gpio.get = max310x_gpio_get;
+ s->gpio.direction_output= max310x_gpio_direction_output;
+ s->gpio.set = max310x_gpio_set;
+ s->gpio.base = -1;
+ s->gpio.ngpio = devtype->nr * 4;
+ s->gpio.can_sleep = 1;
+ ret = gpiochip_add(&s->gpio);
+ if (ret)
+ goto out_uart;
#endif
- /* Go to suspend mode */
- if (pdata->suspend)
- pdata->suspend(1);
+ mutex_init(&s->mutex);
+
+ for (i = 0; i < devtype->nr; i++) {
+ /* Initialize port data */
+ s->p[i].port.line = i;
+ s->p[i].port.dev = dev;
+ s->p[i].port.irq = irq;
+ s->p[i].port.type = PORT_MAX310X;
+ s->p[i].port.fifosize = MAX310X_FIFO_SIZE;
+ s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY;
+ s->p[i].port.iotype = UPIO_PORT;
+ s->p[i].port.iobase = i * 0x20;
+ s->p[i].port.membase = (void __iomem *)~0;
+ s->p[i].port.uartclk = uartclk;
+ s->p[i].port.ops = &max310x_ops;
+ /* Disable all interrupts */
+ max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0);
+ /* Clear IRQ status register */
+ max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG);
+ /* Enable IRQ pin */
+ max310x_port_update(&s->p[i].port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_IRQSEL_BIT,
+ MAX310X_MODE1_IRQSEL_BIT);
+ /* Initialize queue for start TX */
+ INIT_WORK(&s->p[i].tx_work, max310x_wq_proc);
+ /* Initialize queue for changing mode */
+ INIT_WORK(&s->p[i].md_work, max310x_md_proc);
+ /* Register port */
+ uart_add_one_port(&s->uart, &s->p[i].port);
+ /* Go to suspend mode */
+ devtype->power(&s->p[i].port, 0);
+ }
- return 0;
+ /* Setup interrupt */
+ ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist,
+ IRQF_ONESHOT | flags, dev_name(dev), s);
+ if (!ret)
+ return 0;
+
+ dev_err(dev, "Unable to reguest IRQ %i\n", irq);
+
+ mutex_destroy(&s->mutex);
+
+#ifdef CONFIG_GPIOLIB
+ WARN_ON(gpiochip_remove(&s->gpio));
-err_freq:
- dev_err(dev, "Frequency parameter incorrect\n");
- ret = -EINVAL;
+out_uart:
+#endif
+ uart_unregister_driver(&s->uart);
-err_out:
- dev_set_drvdata(dev, NULL);
+out_clk:
+ clk_disable_unprepare(s->clk);
return ret;
}
-static int max310x_remove(struct spi_device *spi)
+static int max310x_remove(struct device *dev)
{
- struct device *dev = &spi->dev;
struct max310x_port *s = dev_get_drvdata(dev);
- int ret = 0;
+ int i, ret = 0;
+
+#ifdef CONFIG_GPIOLIB
+ ret = gpiochip_remove(&s->gpio);
+ if (ret)
+ return ret;
+#endif
- dev_dbg(dev, "Removing port\n");
+ for (i = 0; i < s->uart.nr; i++) {
+ cancel_work_sync(&s->p[i].tx_work);
+ cancel_work_sync(&s->p[i].md_work);
+ uart_remove_one_port(&s->uart, &s->p[i].port);
+ s->devtype->power(&s->p[i].port, 0);
+ }
- devm_free_irq(dev, s->port.irq, s);
+ mutex_destroy(&s->mutex);
+ uart_unregister_driver(&s->uart);
+ clk_disable_unprepare(s->clk);
- destroy_workqueue(s->wq);
+ return ret;
+}
- uart_remove_one_port(&s->uart, &s->port);
+static const struct of_device_id __maybe_unused max310x_dt_ids[] = {
+ { .compatible = "maxim,max3107", .data = &max3107_devtype, },
+ { .compatible = "maxim,max3108", .data = &max3108_devtype, },
+ { .compatible = "maxim,max3109", .data = &max3109_devtype, },
+ { .compatible = "maxim,max14830", .data = &max14830_devtype },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max310x_dt_ids);
+
+static struct regmap_config regcfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .write_flag_mask = 0x80,
+ .cache_type = REGCACHE_RBTREE,
+ .writeable_reg = max310x_reg_writeable,
+ .volatile_reg = max310x_reg_volatile,
+ .precious_reg = max310x_reg_precious,
+};
- uart_unregister_driver(&s->uart);
+#ifdef CONFIG_SPI_MASTER
+static int max310x_spi_probe(struct spi_device *spi)
+{
+ struct max310x_devtype *devtype;
+ unsigned long flags = 0;
+ struct regmap *regmap;
+ int ret;
-#ifdef CONFIG_GPIOLIB
- if (s->pdata->gpio_base) {
- ret = gpiochip_remove(&s->gpio);
- if (ret)
- dev_err(dev, "Failed to remove gpio chip: %d\n", ret);
+ /* Setup SPI bus */
+ spi->bits_per_word = 8;
+ spi->mode = spi->mode ? : SPI_MODE_0;
+ spi->max_speed_hz = spi->max_speed_hz ? : 26000000;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
+ if (spi->dev.of_node) {
+ const struct of_device_id *of_id =
+ of_match_device(max310x_dt_ids, &spi->dev);
+
+ devtype = (struct max310x_devtype *)of_id->data;
+ } else {
+ const struct spi_device_id *id_entry = spi_get_device_id(spi);
+
+ devtype = (struct max310x_devtype *)id_entry->driver_data;
+ flags = IRQF_TRIGGER_FALLING;
}
-#endif
- dev_set_drvdata(dev, NULL);
+ regcfg.max_register = devtype->nr * 0x20 - 1;
+ regmap = devm_regmap_init_spi(spi, &regcfg);
- if (s->pdata->suspend)
- s->pdata->suspend(1);
- if (s->pdata->exit)
- s->pdata->exit();
+ return max310x_probe(&spi->dev, devtype, regmap, spi->irq, flags);
+}
- return ret;
+static int max310x_spi_remove(struct spi_device *spi)
+{
+ return max310x_remove(&spi->dev);
}
static const struct spi_device_id max310x_id_table[] = {
- { "max3107", MAX310X_TYPE_MAX3107 },
- { "max3108", MAX310X_TYPE_MAX3108 },
+ { "max3107", (kernel_ulong_t)&max3107_devtype, },
+ { "max3108", (kernel_ulong_t)&max3108_devtype, },
+ { "max3109", (kernel_ulong_t)&max3109_devtype, },
+ { "max14830", (kernel_ulong_t)&max14830_devtype, },
{ }
};
MODULE_DEVICE_TABLE(spi, max310x_id_table);
-static struct spi_driver max310x_driver = {
+static struct spi_driver max310x_uart_driver = {
.driver = {
- .name = "max310x",
- .owner = THIS_MODULE,
- .pm = MAX310X_PM_OPS,
+ .name = MAX310X_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max310x_dt_ids),
+ .pm = &max310x_pm_ops,
},
- .probe = max310x_probe,
- .remove = max310x_remove,
+ .probe = max310x_spi_probe,
+ .remove = max310x_spi_remove,
.id_table = max310x_id_table,
};
-module_spi_driver(max310x_driver);
+module_spi_driver(max310x_uart_driver);
+#endif
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
MODULE_DESCRIPTION("MAX310X serial driver");
diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c
index 65be0c00c4b..a6f085717f9 100644
--- a/drivers/tty/serial/mcf.c
+++ b/drivers/tty/serial/mcf.c
@@ -24,6 +24,7 @@
#include <linux/serial_core.h>
#include <linux/io.h>
#include <linux/uaccess.h>
+#include <linux/platform_device.h>
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include <asm/mcfuart.h>
@@ -247,6 +248,12 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
mr1 |= MCFUART_MR1_PARITYNONE;
}
+ /*
+ * FIXME: port->read_status_mask and port->ignore_status_mask
+ * need to be initialized based on termios settings for
+ * INPCK, IGNBRK, IGNPAR, PARMRK, BRKINT
+ */
+
if (termios->c_cflag & CSTOPB)
mr2 |= MCFUART_MR2_STOP2;
else
@@ -324,7 +331,9 @@ static void mcf_rx_chars(struct mcf_uart *pp)
uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag);
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
}
/****************************************************************************/
@@ -644,7 +653,7 @@ static struct uart_driver mcf_driver = {
static int mcf_probe(struct platform_device *pdev)
{
- struct mcf_platform_uart *platp = pdev->dev.platform_data;
+ struct mcf_platform_uart *platp = dev_get_platdata(&pdev->dev);
struct uart_port *port;
int i;
diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c
new file mode 100644
index 00000000000..c9d18548783
--- /dev/null
+++ b/drivers/tty/serial/men_z135_uart.c
@@ -0,0 +1,867 @@
+/*
+ * MEN 16z135 High Speed UART
+ *
+ * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
+ * Author: Johannes Thumshirn <johannes.thumshirn@men.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/serial_core.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/tty_flip.h>
+#include <linux/bitops.h>
+#include <linux/mcb.h>
+
+#define MEN_Z135_MAX_PORTS 12
+#define MEN_Z135_BASECLK 29491200
+#define MEN_Z135_FIFO_SIZE 1024
+#define MEN_Z135_NUM_MSI_VECTORS 2
+#define MEN_Z135_FIFO_WATERMARK 1020
+
+#define MEN_Z135_STAT_REG 0x0
+#define MEN_Z135_RX_RAM 0x4
+#define MEN_Z135_TX_RAM 0x400
+#define MEN_Z135_RX_CTRL 0x800
+#define MEN_Z135_TX_CTRL 0x804
+#define MEN_Z135_CONF_REG 0x808
+#define MEN_Z135_UART_FREQ 0x80c
+#define MEN_Z135_BAUD_REG 0x810
+#define MENZ135_TIMEOUT 0x814
+
+#define MEN_Z135_MEM_SIZE 0x818
+
+#define IS_IRQ(x) ((x) & 1)
+#define IRQ_ID(x) (((x) >> 1) & 7)
+
+#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */
+#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */
+#define MEN_Z135_IER_RLSIEN BIT(2) /* Receiver Line Status IRQ */
+#define MEN_Z135_IER_MSIEN BIT(3) /* Modem Status IRQ */
+#define MEN_Z135_ALL_IRQS (MEN_Z135_IER_RXCIEN \
+ | MEN_Z135_IER_RLSIEN \
+ | MEN_Z135_IER_MSIEN \
+ | MEN_Z135_IER_TXCIEN)
+
+#define MEN_Z135_MCR_DTR BIT(24)
+#define MEN_Z135_MCR_RTS BIT(25)
+#define MEN_Z135_MCR_OUT1 BIT(26)
+#define MEN_Z135_MCR_OUT2 BIT(27)
+#define MEN_Z135_MCR_LOOP BIT(28)
+#define MEN_Z135_MCR_RCFC BIT(29)
+
+#define MEN_Z135_MSR_DCTS BIT(0)
+#define MEN_Z135_MSR_DDSR BIT(1)
+#define MEN_Z135_MSR_DRI BIT(2)
+#define MEN_Z135_MSR_DDCD BIT(3)
+#define MEN_Z135_MSR_CTS BIT(4)
+#define MEN_Z135_MSR_DSR BIT(5)
+#define MEN_Z135_MSR_RI BIT(6)
+#define MEN_Z135_MSR_DCD BIT(7)
+
+#define MEN_Z135_LCR_SHIFT 8 /* LCR shift mask */
+
+#define MEN_Z135_WL5 0 /* CS5 */
+#define MEN_Z135_WL6 1 /* CS6 */
+#define MEN_Z135_WL7 2 /* CS7 */
+#define MEN_Z135_WL8 3 /* CS8 */
+
+#define MEN_Z135_STB_SHIFT 2 /* Stopbits */
+#define MEN_Z135_NSTB1 0
+#define MEN_Z135_NSTB2 1
+
+#define MEN_Z135_PEN_SHIFT 3 /* Parity enable */
+#define MEN_Z135_PAR_DIS 0
+#define MEN_Z135_PAR_ENA 1
+
+#define MEN_Z135_PTY_SHIFT 4 /* Parity type */
+#define MEN_Z135_PTY_ODD 0
+#define MEN_Z135_PTY_EVN 1
+
+#define MEN_Z135_LSR_DR BIT(0)
+#define MEN_Z135_LSR_OE BIT(1)
+#define MEN_Z135_LSR_PE BIT(2)
+#define MEN_Z135_LSR_FE BIT(3)
+#define MEN_Z135_LSR_BI BIT(4)
+#define MEN_Z135_LSR_THEP BIT(5)
+#define MEN_Z135_LSR_TEXP BIT(6)
+#define MEN_Z135_LSR_RXFIFOERR BIT(7)
+
+#define MEN_Z135_IRQ_ID_MST 0
+#define MEN_Z135_IRQ_ID_TSA 1
+#define MEN_Z135_IRQ_ID_RDA 2
+#define MEN_Z135_IRQ_ID_RLS 3
+#define MEN_Z135_IRQ_ID_CTI 6
+
+#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
+
+#define BYTES_TO_ALIGN(x) ((x) & 0x3)
+
+static int line;
+
+static int txlvl = 5;
+module_param(txlvl, int, S_IRUGO);
+MODULE_PARM_DESC(txlvl, "TX IRQ trigger level 0-7, default 5 (128 byte)");
+
+static int rxlvl = 6;
+module_param(rxlvl, int, S_IRUGO);
+MODULE_PARM_DESC(rxlvl, "RX IRQ trigger level 0-7, default 6 (256 byte)");
+
+static int align;
+module_param(align, int, S_IRUGO);
+MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0");
+
+struct men_z135_port {
+ struct uart_port port;
+ struct mcb_device *mdev;
+ unsigned char *rxbuf;
+ u32 stat_reg;
+ spinlock_t lock;
+};
+#define to_men_z135(port) container_of((port), struct men_z135_port, port)
+
+/**
+ * men_z135_reg_set() - Set value in register
+ * @uart: The UART port
+ * @addr: Register address
+ * @val: value to set
+ */
+static inline void men_z135_reg_set(struct men_z135_port *uart,
+ u32 addr, u32 val)
+{
+ struct uart_port *port = &uart->port;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&uart->lock, flags);
+
+ reg = ioread32(port->membase + addr);
+ reg |= val;
+ iowrite32(reg, port->membase + addr);
+
+ spin_unlock_irqrestore(&uart->lock, flags);
+}
+
+/**
+ * men_z135_reg_clr() - Unset value in register
+ * @uart: The UART port
+ * @addr: Register address
+ * @val: value to clear
+ */
+static inline void men_z135_reg_clr(struct men_z135_port *uart,
+ u32 addr, u32 val)
+{
+ struct uart_port *port = &uart->port;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&uart->lock, flags);
+
+ reg = ioread32(port->membase + addr);
+ reg &= ~val;
+ iowrite32(reg, port->membase + addr);
+
+ spin_unlock_irqrestore(&uart->lock, flags);
+}
+
+/**
+ * men_z135_handle_modem_status() - Handle change of modem status
+ * @port: The UART port
+ *
+ * Handle change of modem status register. This is done by reading the "delta"
+ * versions of DCD (Data Carrier Detect) and CTS (Clear To Send).
+ */
+static void men_z135_handle_modem_status(struct men_z135_port *uart)
+{
+ if (uart->stat_reg & MEN_Z135_MSR_DDCD)
+ uart_handle_dcd_change(&uart->port,
+ uart->stat_reg & ~MEN_Z135_MSR_DCD);
+ if (uart->stat_reg & MEN_Z135_MSR_DCTS)
+ uart_handle_cts_change(&uart->port,
+ uart->stat_reg & ~MEN_Z135_MSR_CTS);
+}
+
+static void men_z135_handle_lsr(struct men_z135_port *uart)
+{
+ struct uart_port *port = &uart->port;
+ u8 lsr;
+
+ lsr = (uart->stat_reg >> 16) & 0xff;
+
+ if (lsr & MEN_Z135_LSR_OE)
+ port->icount.overrun++;
+ if (lsr & MEN_Z135_LSR_PE)
+ port->icount.parity++;
+ if (lsr & MEN_Z135_LSR_FE)
+ port->icount.frame++;
+ if (lsr & MEN_Z135_LSR_BI) {
+ port->icount.brk++;
+ uart_handle_break(port);
+ }
+}
+
+/**
+ * get_rx_fifo_content() - Get the number of bytes in RX FIFO
+ * @uart: The UART port
+ *
+ * Read RXC register from hardware and return current FIFO fill size.
+ */
+static u16 get_rx_fifo_content(struct men_z135_port *uart)
+{
+ struct uart_port *port = &uart->port;
+ u32 stat_reg;
+ u16 rxc;
+ u8 rxc_lo;
+ u8 rxc_hi;
+
+ stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
+ rxc_lo = stat_reg >> 24;
+ rxc_hi = (stat_reg & 0xC0) >> 6;
+
+ rxc = rxc_lo | (rxc_hi << 8);
+
+ return rxc;
+}
+
+/**
+ * men_z135_handle_rx() - RX tasklet routine
+ * @arg: Pointer to struct men_z135_port
+ *
+ * Copy from RX FIFO and acknowledge number of bytes copied.
+ */
+static void men_z135_handle_rx(struct men_z135_port *uart)
+{
+ struct uart_port *port = &uart->port;
+ struct tty_port *tport = &port->state->port;
+ int copied;
+ u16 size;
+ int room;
+
+ size = get_rx_fifo_content(uart);
+
+ if (size == 0)
+ return;
+
+ /* Avoid accidently accessing TX FIFO instead of RX FIFO. Last
+ * longword in RX FIFO cannot be read.(0x004-0x3FF)
+ */
+ if (size > MEN_Z135_FIFO_WATERMARK)
+ size = MEN_Z135_FIFO_WATERMARK;
+
+ room = tty_buffer_request_room(tport, size);
+ if (room != size)
+ dev_warn(&uart->mdev->dev,
+ "Not enough room in flip buffer, truncating to %d\n",
+ room);
+
+ if (room == 0)
+ return;
+
+ memcpy_fromio(uart->rxbuf, port->membase + MEN_Z135_RX_RAM, room);
+ /* Be sure to first copy all data and then acknowledge it */
+ mb();
+ iowrite32(room, port->membase + MEN_Z135_RX_CTRL);
+
+ copied = tty_insert_flip_string(tport, uart->rxbuf, room);
+ if (copied != room)
+ dev_warn(&uart->mdev->dev,
+ "Only copied %d instead of %d bytes\n",
+ copied, room);
+
+ port->icount.rx += copied;
+
+ tty_flip_buffer_push(tport);
+
+}
+
+/**
+ * men_z135_handle_tx() - TX tasklet routine
+ * @arg: Pointer to struct men_z135_port
+ *
+ */
+static void men_z135_handle_tx(struct men_z135_port *uart)
+{
+ struct uart_port *port = &uart->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ u32 txc;
+ u32 wptr;
+ int qlen;
+ int n;
+ int txfree;
+ int head;
+ int tail;
+ int s;
+
+ if (uart_circ_empty(xmit))
+ goto out;
+
+ if (uart_tx_stopped(port))
+ goto out;
+
+ if (port->x_char)
+ goto out;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ /* calculate bytes to copy */
+ qlen = uart_circ_chars_pending(xmit);
+ if (qlen <= 0)
+ goto out;
+
+ wptr = ioread32(port->membase + MEN_Z135_TX_CTRL);
+ txc = (wptr >> 16) & 0x3ff;
+ wptr &= 0x3ff;
+
+ if (txc > MEN_Z135_FIFO_WATERMARK)
+ txc = MEN_Z135_FIFO_WATERMARK;
+
+ txfree = MEN_Z135_FIFO_WATERMARK - txc;
+ if (txfree <= 0) {
+ pr_err("Not enough room in TX FIFO have %d, need %d\n",
+ txfree, qlen);
+ goto irq_en;
+ }
+
+ /* if we're not aligned, it's better to copy only 1 or 2 bytes and
+ * then the rest.
+ */
+ if (align && qlen >= 3 && BYTES_TO_ALIGN(wptr))
+ n = 4 - BYTES_TO_ALIGN(wptr);
+ else if (qlen > txfree)
+ n = txfree;
+ else
+ n = qlen;
+
+ if (n <= 0)
+ goto irq_en;
+
+ head = xmit->head & (UART_XMIT_SIZE - 1);
+ tail = xmit->tail & (UART_XMIT_SIZE - 1);
+
+ s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail;
+ n = min(n, s);
+
+ memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n);
+ xmit->tail = (xmit->tail + n) & (UART_XMIT_SIZE - 1);
+ mmiowb();
+
+ iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL);
+
+ port->icount.tx += n;
+
+irq_en:
+ if (!uart_circ_empty(xmit))
+ men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
+ else
+ men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
+
+out:
+ return;
+
+}
+
+/**
+ * men_z135_intr() - Handle legacy IRQs
+ * @irq: The IRQ number
+ * @data: Pointer to UART port
+ *
+ * Check IIR register to see which tasklet to start.
+ */
+static irqreturn_t men_z135_intr(int irq, void *data)
+{
+ struct men_z135_port *uart = (struct men_z135_port *)data;
+ struct uart_port *port = &uart->port;
+ int irq_id;
+
+ uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
+ /* IRQ pending is low active */
+ if (IS_IRQ(uart->stat_reg))
+ return IRQ_NONE;
+
+ irq_id = IRQ_ID(uart->stat_reg);
+ switch (irq_id) {
+ case MEN_Z135_IRQ_ID_MST:
+ men_z135_handle_modem_status(uart);
+ break;
+ case MEN_Z135_IRQ_ID_TSA:
+ men_z135_handle_tx(uart);
+ break;
+ case MEN_Z135_IRQ_ID_CTI:
+ dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n");
+ /* Fallthrough */
+ case MEN_Z135_IRQ_ID_RDA:
+ /* Reading data clears RX IRQ */
+ men_z135_handle_rx(uart);
+ break;
+ case MEN_Z135_IRQ_ID_RLS:
+ men_z135_handle_lsr(uart);
+ break;
+ default:
+ dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id);
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * men_z135_request_irq() - Request IRQ for 16z135 core
+ * @uart: z135 private uart port structure
+ *
+ * Request an IRQ for 16z135 to use. First try using MSI, if it fails
+ * fall back to using legacy interrupts.
+ */
+static int men_z135_request_irq(struct men_z135_port *uart)
+{
+ struct device *dev = &uart->mdev->dev;
+ struct uart_port *port = &uart->port;
+ int err = 0;
+
+ err = request_irq(port->irq, men_z135_intr, IRQF_SHARED,
+ "men_z135_intr", uart);
+ if (err)
+ dev_err(dev, "Error %d getting interrupt\n", err);
+
+ return err;
+}
+
+/**
+ * men_z135_tx_empty() - Handle tx_empty call
+ * @port: The UART port
+ *
+ * This function tests whether the TX FIFO and shifter for the port
+ * described by @port is empty.
+ */
+static unsigned int men_z135_tx_empty(struct uart_port *port)
+{
+ u32 wptr;
+ u16 txc;
+
+ wptr = ioread32(port->membase + MEN_Z135_TX_CTRL);
+ txc = (wptr >> 16) & 0x3ff;
+
+ if (txc == 0)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+/**
+ * men_z135_set_mctrl() - Set modem control lines
+ * @port: The UART port
+ * @mctrl: The modem control lines
+ *
+ * This function sets the modem control lines for a port described by @port
+ * to the state described by @mctrl
+ */
+static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct men_z135_port *uart = to_men_z135(port);
+ u32 conf_reg = 0;
+
+ if (mctrl & TIOCM_RTS)
+ conf_reg |= MEN_Z135_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ conf_reg |= MEN_Z135_MCR_DTR;
+ if (mctrl & TIOCM_OUT1)
+ conf_reg |= MEN_Z135_MCR_OUT1;
+ if (mctrl & TIOCM_OUT2)
+ conf_reg |= MEN_Z135_MCR_OUT2;
+ if (mctrl & TIOCM_LOOP)
+ conf_reg |= MEN_Z135_MCR_LOOP;
+
+ men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg);
+}
+
+/**
+ * men_z135_get_mctrl() - Get modem control lines
+ * @port: The UART port
+ *
+ * Retruns the current state of modem control inputs.
+ */
+static unsigned int men_z135_get_mctrl(struct uart_port *port)
+{
+ unsigned int mctrl = 0;
+ u32 stat_reg;
+ u8 msr;
+
+ stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG);
+
+ msr = ~((stat_reg >> 8) & 0xff);
+
+ if (msr & MEN_Z135_MSR_CTS)
+ mctrl |= TIOCM_CTS;
+ if (msr & MEN_Z135_MSR_DSR)
+ mctrl |= TIOCM_DSR;
+ if (msr & MEN_Z135_MSR_RI)
+ mctrl |= TIOCM_RI;
+ if (msr & MEN_Z135_MSR_DCD)
+ mctrl |= TIOCM_CAR;
+
+ return mctrl;
+}
+
+/**
+ * men_z135_stop_tx() - Stop transmitting characters
+ * @port: The UART port
+ *
+ * Stop transmitting characters. This might be due to CTS line becomming
+ * inactive or the tty layer indicating we want to stop transmission due to
+ * an XOFF character.
+ */
+static void men_z135_stop_tx(struct uart_port *port)
+{
+ struct men_z135_port *uart = to_men_z135(port);
+
+ men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN);
+}
+
+/**
+ * men_z135_start_tx() - Start transmitting characters
+ * @port: The UART port
+ *
+ * Start transmitting character. This actually doesn't transmit anything, but
+ * fires off the TX tasklet.
+ */
+static void men_z135_start_tx(struct uart_port *port)
+{
+ struct men_z135_port *uart = to_men_z135(port);
+
+ men_z135_handle_tx(uart);
+}
+
+/**
+ * men_z135_stop_rx() - Stop receiving characters
+ * @port: The UART port
+ *
+ * Stop receiving characters; the port is in the process of being closed.
+ */
+static void men_z135_stop_rx(struct uart_port *port)
+{
+ struct men_z135_port *uart = to_men_z135(port);
+
+ men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_RXCIEN);
+}
+
+/**
+ * men_z135_enable_ms() - Enable Modem Status
+ * port:
+ *
+ * Enable Modem Status IRQ.
+ */
+static void men_z135_enable_ms(struct uart_port *port)
+{
+ struct men_z135_port *uart = to_men_z135(port);
+
+ men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN);
+}
+
+static int men_z135_startup(struct uart_port *port)
+{
+ struct men_z135_port *uart = to_men_z135(port);
+ int err;
+ u32 conf_reg = 0;
+
+ err = men_z135_request_irq(uart);
+ if (err)
+ return -ENODEV;
+
+ conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG);
+
+ /* Activate all but TX space available IRQ */
+ conf_reg |= MEN_Z135_ALL_IRQS & ~MEN_Z135_IER_TXCIEN;
+ conf_reg &= ~(0xff << 16);
+ conf_reg |= (txlvl << 16);
+ conf_reg |= (rxlvl << 20);
+
+ iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
+
+ return 0;
+}
+
+static void men_z135_shutdown(struct uart_port *port)
+{
+ struct men_z135_port *uart = to_men_z135(port);
+ u32 conf_reg = 0;
+
+ conf_reg |= MEN_Z135_ALL_IRQS;
+
+ men_z135_reg_clr(uart, MEN_Z135_CONF_REG, conf_reg);
+
+ free_irq(uart->port.irq, uart);
+}
+
+static void men_z135_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int baud;
+ u32 conf_reg;
+ u32 bd_reg;
+ u32 uart_freq;
+ u8 lcr;
+
+ conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG);
+ lcr = LCR(conf_reg);
+
+ /* byte size */
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr |= MEN_Z135_WL5;
+ break;
+ case CS6:
+ lcr |= MEN_Z135_WL6;
+ break;
+ case CS7:
+ lcr |= MEN_Z135_WL7;
+ break;
+ case CS8:
+ lcr |= MEN_Z135_WL8;
+ break;
+ }
+
+ /* stop bits */
+ if (termios->c_cflag & CSTOPB)
+ lcr |= MEN_Z135_NSTB2 << MEN_Z135_STB_SHIFT;
+
+ /* parity */
+ if (termios->c_cflag & PARENB) {
+ lcr |= MEN_Z135_PAR_ENA << MEN_Z135_PEN_SHIFT;
+
+ if (termios->c_cflag & PARODD)
+ lcr |= MEN_Z135_PTY_ODD << MEN_Z135_PTY_SHIFT;
+ else
+ lcr |= MEN_Z135_PTY_EVN << MEN_Z135_PTY_SHIFT;
+ } else
+ lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT;
+
+ termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
+
+ conf_reg |= lcr << MEN_Z135_LCR_SHIFT;
+ iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG);
+
+ uart_freq = ioread32(port->membase + MEN_Z135_UART_FREQ);
+ if (uart_freq == 0)
+ uart_freq = MEN_Z135_BASECLK;
+
+ baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16);
+
+ spin_lock(&port->lock);
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
+ bd_reg = uart_freq / (4 * baud);
+ iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG);
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+ spin_unlock(&port->lock);
+}
+
+static const char *men_z135_type(struct uart_port *port)
+{
+ return KBUILD_MODNAME;
+}
+
+static void men_z135_release_port(struct uart_port *port)
+{
+ iounmap(port->membase);
+ port->membase = NULL;
+
+ release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE);
+}
+
+static int men_z135_request_port(struct uart_port *port)
+{
+ int size = MEN_Z135_MEM_SIZE;
+
+ if (!request_mem_region(port->mapbase, size, "men_z135_port"))
+ return -EBUSY;
+
+ port->membase = ioremap(port->mapbase, MEN_Z135_MEM_SIZE);
+ if (port->membase == NULL) {
+ release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void men_z135_config_port(struct uart_port *port, int type)
+{
+ port->type = PORT_MEN_Z135;
+ men_z135_request_port(port);
+}
+
+static int men_z135_verify_port(struct uart_port *port,
+ struct serial_struct *serinfo)
+{
+ return -EINVAL;
+}
+
+static struct uart_ops men_z135_ops = {
+ .tx_empty = men_z135_tx_empty,
+ .set_mctrl = men_z135_set_mctrl,
+ .get_mctrl = men_z135_get_mctrl,
+ .stop_tx = men_z135_stop_tx,
+ .start_tx = men_z135_start_tx,
+ .stop_rx = men_z135_stop_rx,
+ .enable_ms = men_z135_enable_ms,
+ .startup = men_z135_startup,
+ .shutdown = men_z135_shutdown,
+ .set_termios = men_z135_set_termios,
+ .type = men_z135_type,
+ .release_port = men_z135_release_port,
+ .request_port = men_z135_request_port,
+ .config_port = men_z135_config_port,
+ .verify_port = men_z135_verify_port,
+};
+
+static struct uart_driver men_z135_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = KBUILD_MODNAME,
+ .dev_name = "ttyHSU",
+ .major = 0,
+ .minor = 0,
+ .nr = MEN_Z135_MAX_PORTS,
+};
+
+/**
+ * men_z135_probe() - Probe a z135 instance
+ * @mdev: The MCB device
+ * @id: The MCB device ID
+ *
+ * men_z135_probe does the basic setup of hardware resources and registers the
+ * new uart port to the tty layer.
+ */
+static int men_z135_probe(struct mcb_device *mdev,
+ const struct mcb_device_id *id)
+{
+ struct men_z135_port *uart;
+ struct resource *mem;
+ struct device *dev;
+ int err;
+
+ dev = &mdev->dev;
+
+ uart = devm_kzalloc(dev, sizeof(struct men_z135_port), GFP_KERNEL);
+ if (!uart)
+ return -ENOMEM;
+
+ uart->rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!uart->rxbuf)
+ return -ENOMEM;
+
+ mem = &mdev->mem;
+
+ mcb_set_drvdata(mdev, uart);
+
+ uart->port.uartclk = MEN_Z135_BASECLK * 16;
+ uart->port.fifosize = MEN_Z135_FIFO_SIZE;
+ uart->port.iotype = UPIO_MEM;
+ uart->port.ops = &men_z135_ops;
+ uart->port.irq = mcb_get_irq(mdev);
+ uart->port.iotype = UPIO_MEM;
+ uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
+ uart->port.line = line++;
+ uart->port.dev = dev;
+ uart->port.type = PORT_MEN_Z135;
+ uart->port.mapbase = mem->start;
+ uart->port.membase = NULL;
+ uart->mdev = mdev;
+
+ spin_lock_init(&uart->port.lock);
+ spin_lock_init(&uart->lock);
+
+ err = uart_add_one_port(&men_z135_driver, &uart->port);
+ if (err)
+ goto err;
+
+ return 0;
+
+err:
+ free_page((unsigned long) uart->rxbuf);
+ dev_err(dev, "Failed to add UART: %d\n", err);
+
+ return err;
+}
+
+/**
+ * men_z135_remove() - Remove a z135 instance from the system
+ *
+ * @mdev: The MCB device
+ */
+static void men_z135_remove(struct mcb_device *mdev)
+{
+ struct men_z135_port *uart = mcb_get_drvdata(mdev);
+
+ line--;
+ uart_remove_one_port(&men_z135_driver, &uart->port);
+ free_page((unsigned long) uart->rxbuf);
+}
+
+static const struct mcb_device_id men_z135_ids[] = {
+ { .device = 0x87 },
+};
+MODULE_DEVICE_TABLE(mcb, men_z135_ids);
+
+static struct mcb_driver mcb_driver = {
+ .driver = {
+ .name = "z135-uart",
+ .owner = THIS_MODULE,
+ },
+ .probe = men_z135_probe,
+ .remove = men_z135_remove,
+ .id_table = men_z135_ids,
+};
+
+/**
+ * men_z135_init() - Driver Registration Routine
+ *
+ * men_z135_init is the first routine called when the driver is loaded. All it
+ * does is register with the legacy MEN Chameleon subsystem.
+ */
+static int __init men_z135_init(void)
+{
+ int err;
+
+ err = uart_register_driver(&men_z135_driver);
+ if (err) {
+ pr_err("Failed to register UART: %d\n", err);
+ return err;
+ }
+
+ err = mcb_register_driver(&mcb_driver);
+ if (err) {
+ pr_err("Failed to register MCB driver: %d\n", err);
+ uart_unregister_driver(&men_z135_driver);
+ return err;
+ }
+
+ return 0;
+}
+module_init(men_z135_init);
+
+/**
+ * men_z135_exit() - Driver Exit Routine
+ *
+ * men_z135_exit is called just before the driver is removed from memory.
+ */
+static void __exit men_z135_exit(void)
+{
+ mcb_unregister_driver(&mcb_driver);
+ uart_unregister_driver(&men_z135_driver);
+}
+module_exit(men_z135_exit);
+
+MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MEN 16z135 High Speed UART");
+MODULE_ALIAS("mcb:16z135");
diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c
index 4a82267af83..445799dc984 100644
--- a/drivers/tty/serial/mfd.c
+++ b/drivers/tty/serial/mfd.c
@@ -293,7 +293,7 @@ static void serial_hsu_enable_ms(struct uart_port *port)
serial_out(up, UART_IER, up->ier);
}
-void hsu_dma_tx(struct uart_hsu_port *up)
+static void hsu_dma_tx(struct uart_hsu_port *up)
{
struct circ_buf *xmit = &up->port.state->xmit;
struct hsu_dma_buffer *dbuf = &up->txbuf;
@@ -340,7 +340,8 @@ void hsu_dma_tx(struct uart_hsu_port *up)
}
/* The buffer is already cache coherent */
-void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf)
+static void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc,
+ struct hsu_dma_buffer *dbuf)
{
dbuf->ofs = 0;
@@ -386,7 +387,8 @@ static void serial_hsu_stop_tx(struct uart_port *port)
/* This is always called in spinlock protected mode, so
* modify timeout timer is safe here */
-void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
+static void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts,
+ unsigned long *flags)
{
struct hsu_dma_buffer *dbuf = &up->rxbuf;
struct hsu_dma_chan *chan = up->rxc;
@@ -438,7 +440,9 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
| (0x1 << 16)
| (0x1 << 24) /* timeout bit, see HSU Errata 1 */
);
+ spin_unlock_irqrestore(&up->port.lock, *flags);
tty_flip_buffer_push(tport);
+ spin_lock_irqsave(&up->port.lock, *flags);
chan_writel(chan, HSU_CH_CR, 0x3);
@@ -459,7 +463,8 @@ static void serial_hsu_stop_rx(struct uart_port *port)
}
}
-static inline void receive_chars(struct uart_hsu_port *up, int *status)
+static inline void receive_chars(struct uart_hsu_port *up, int *status,
+ unsigned long *flags)
{
unsigned int ch, flag;
unsigned int max_count = 256;
@@ -519,7 +524,10 @@ static inline void receive_chars(struct uart_hsu_port *up, int *status)
ignore_char:
*status = serial_in(up, UART_LSR);
} while ((*status & UART_LSR_DR) && max_count--);
+
+ spin_unlock_irqrestore(&up->port.lock, *flags);
tty_flip_buffer_push(&up->port.state->port);
+ spin_lock_irqsave(&up->port.lock, *flags);
}
static void transmit_chars(struct uart_hsu_port *up)
@@ -613,7 +621,7 @@ static irqreturn_t port_irq(int irq, void *dev_id)
lsr = serial_in(up, UART_LSR);
if (lsr & UART_LSR_DR)
- receive_chars(up, &lsr);
+ receive_chars(up, &lsr, &flags);
check_modem_status(up);
/* lsr will be renewed during the receive_chars */
@@ -643,7 +651,7 @@ static inline void dma_chan_irq(struct hsu_dma_chan *chan)
/* Rx channel */
if (chan->dirt == DMA_FROM_DEVICE)
- hsu_dma_rx(up, int_sts);
+ hsu_dma_rx(up, int_sts, &flags);
/* Tx channel */
if (chan->dirt == DMA_TO_DEVICE) {
@@ -969,7 +977,7 @@ serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios,
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (termios->c_iflag & INPCK)
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= UART_LSR_BI;
/* Characters to ignore */
@@ -1177,7 +1185,7 @@ static struct console serial_hsu_console = {
#define SERIAL_HSU_CONSOLE NULL
#endif
-struct uart_ops serial_hsu_pops = {
+static struct uart_ops serial_hsu_pops = {
.tx_empty = serial_hsu_tx_empty,
.set_mctrl = serial_hsu_set_mctrl,
.get_mctrl = serial_hsu_get_mctrl,
@@ -1445,7 +1453,6 @@ static void serial_hsu_remove(struct pci_dev *pdev)
uart_remove_one_port(&serial_hsu_reg, &up->port);
}
- pci_set_drvdata(pdev, NULL);
free_irq(pdev->irq, priv);
pci_disable_device(pdev);
}
@@ -1498,4 +1505,4 @@ module_init(hsu_pci_init);
module_exit(hsu_pci_exit);
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:medfield-hsu");
+MODULE_DEVICE_TABLE(pci, pci_ids);
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index e1280a20b7a..97888f4900e 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -107,6 +107,8 @@ struct psc_ops {
unsigned int (*set_baudrate)(struct uart_port *port,
struct ktermios *new,
struct ktermios *old);
+ int (*clock_alloc)(struct uart_port *port);
+ void (*clock_relse)(struct uart_port *port);
int (*clock)(struct uart_port *port, int enable);
int (*fifoc_init)(void);
void (*fifoc_uninit)(void);
@@ -419,6 +421,7 @@ struct psc_fifoc {
static struct psc_fifoc __iomem *psc_fifoc;
static unsigned int psc_fifoc_irq;
+static struct clk *psc_fifoc_clk;
static void mpc512x_psc_fifo_init(struct uart_port *port)
{
@@ -566,36 +569,73 @@ static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port,
/* Init PSC FIFO Controller */
static int __init mpc512x_psc_fifoc_init(void)
{
+ int err;
struct device_node *np;
+ struct clk *clk;
+
+ /* default error code, potentially overwritten by clock calls */
+ err = -ENODEV;
np = of_find_compatible_node(NULL, NULL,
"fsl,mpc5121-psc-fifo");
if (!np) {
pr_err("%s: Can't find FIFOC node\n", __func__);
- return -ENODEV;
+ goto out_err;
}
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ /* backwards compat with device trees that lack clock specs */
+ clk = clk_get_sys(np->name, "ipg");
+ }
+ if (IS_ERR(clk)) {
+ pr_err("%s: Can't lookup FIFO clock\n", __func__);
+ err = PTR_ERR(clk);
+ goto out_ofnode_put;
+ }
+ if (clk_prepare_enable(clk)) {
+ pr_err("%s: Can't enable FIFO clock\n", __func__);
+ clk_put(clk);
+ goto out_ofnode_put;
+ }
+ psc_fifoc_clk = clk;
+
psc_fifoc = of_iomap(np, 0);
if (!psc_fifoc) {
pr_err("%s: Can't map FIFOC\n", __func__);
- of_node_put(np);
- return -ENODEV;
+ goto out_clk_disable;
}
psc_fifoc_irq = irq_of_parse_and_map(np, 0);
- of_node_put(np);
if (psc_fifoc_irq == 0) {
pr_err("%s: Can't get FIFOC irq\n", __func__);
- iounmap(psc_fifoc);
- return -ENODEV;
+ goto out_unmap;
}
+ of_node_put(np);
return 0;
+
+out_unmap:
+ iounmap(psc_fifoc);
+out_clk_disable:
+ clk_disable_unprepare(psc_fifoc_clk);
+ clk_put(psc_fifoc_clk);
+out_ofnode_put:
+ of_node_put(np);
+out_err:
+ return err;
}
static void __exit mpc512x_psc_fifoc_uninit(void)
{
iounmap(psc_fifoc);
+
+ /* disable the clock, errors are not fatal */
+ if (psc_fifoc_clk) {
+ clk_disable_unprepare(psc_fifoc_clk);
+ clk_put(psc_fifoc_clk);
+ psc_fifoc_clk = NULL;
+ }
}
/* 512x specific interrupt handler. The caller holds the port lock */
@@ -616,31 +656,103 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port)
return IRQ_NONE;
}
-static int mpc512x_psc_clock(struct uart_port *port, int enable)
+static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM];
+static struct clk *psc_ipg_clk[MPC52xx_PSC_MAXNUM];
+
+/* called from within the .request_port() callback (allocation) */
+static int mpc512x_psc_alloc_clock(struct uart_port *port)
+{
+ int psc_num;
+ struct clk *clk;
+ int err;
+
+ psc_num = (port->mapbase & 0xf00) >> 8;
+
+ clk = devm_clk_get(port->dev, "mclk");
+ if (IS_ERR(clk)) {
+ dev_err(port->dev, "Failed to get MCLK!\n");
+ err = PTR_ERR(clk);
+ goto out_err;
+ }
+ err = clk_prepare_enable(clk);
+ if (err) {
+ dev_err(port->dev, "Failed to enable MCLK!\n");
+ goto out_err;
+ }
+ psc_mclk_clk[psc_num] = clk;
+
+ clk = devm_clk_get(port->dev, "ipg");
+ if (IS_ERR(clk)) {
+ dev_err(port->dev, "Failed to get IPG clock!\n");
+ err = PTR_ERR(clk);
+ goto out_err;
+ }
+ err = clk_prepare_enable(clk);
+ if (err) {
+ dev_err(port->dev, "Failed to enable IPG clock!\n");
+ goto out_err;
+ }
+ psc_ipg_clk[psc_num] = clk;
+
+ return 0;
+
+out_err:
+ if (psc_mclk_clk[psc_num]) {
+ clk_disable_unprepare(psc_mclk_clk[psc_num]);
+ psc_mclk_clk[psc_num] = NULL;
+ }
+ if (psc_ipg_clk[psc_num]) {
+ clk_disable_unprepare(psc_ipg_clk[psc_num]);
+ psc_ipg_clk[psc_num] = NULL;
+ }
+ return err;
+}
+
+/* called from within the .release_port() callback (release) */
+static void mpc512x_psc_relse_clock(struct uart_port *port)
{
- struct clk *psc_clk;
int psc_num;
- char clk_name[10];
+ struct clk *clk;
+
+ psc_num = (port->mapbase & 0xf00) >> 8;
+ clk = psc_mclk_clk[psc_num];
+ if (clk) {
+ clk_disable_unprepare(clk);
+ psc_mclk_clk[psc_num] = NULL;
+ }
+ if (psc_ipg_clk[psc_num]) {
+ clk_disable_unprepare(psc_ipg_clk[psc_num]);
+ psc_ipg_clk[psc_num] = NULL;
+ }
+}
+
+/* implementation of the .clock() callback (enable/disable) */
+static int mpc512x_psc_endis_clock(struct uart_port *port, int enable)
+{
+ int psc_num;
+ struct clk *psc_clk;
+ int ret;
if (uart_console(port))
return 0;
psc_num = (port->mapbase & 0xf00) >> 8;
- snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num);
- psc_clk = clk_get(port->dev, clk_name);
- if (IS_ERR(psc_clk)) {
+ psc_clk = psc_mclk_clk[psc_num];
+ if (!psc_clk) {
dev_err(port->dev, "Failed to get PSC clock entry!\n");
return -ENODEV;
}
- dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis");
-
- if (enable)
- clk_enable(psc_clk);
- else
+ dev_dbg(port->dev, "mclk %sable\n", enable ? "en" : "dis");
+ if (enable) {
+ ret = clk_enable(psc_clk);
+ if (ret)
+ dev_err(port->dev, "Failed to enable MCLK!\n");
+ return ret;
+ } else {
clk_disable(psc_clk);
-
- return 0;
+ return 0;
+ }
}
static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np)
@@ -873,7 +985,9 @@ static struct psc_ops mpc5125_psc_ops = {
.cw_disable_ints = mpc5125_psc_cw_disable_ints,
.cw_restore_ints = mpc5125_psc_cw_restore_ints,
.set_baudrate = mpc5125_psc_set_baudrate,
- .clock = mpc512x_psc_clock,
+ .clock_alloc = mpc512x_psc_alloc_clock,
+ .clock_relse = mpc512x_psc_relse_clock,
+ .clock = mpc512x_psc_endis_clock,
.fifoc_init = mpc512x_psc_fifoc_init,
.fifoc_uninit = mpc512x_psc_fifoc_uninit,
.get_irq = mpc512x_psc_get_irq,
@@ -906,7 +1020,9 @@ static struct psc_ops mpc512x_psc_ops = {
.cw_disable_ints = mpc512x_psc_cw_disable_ints,
.cw_restore_ints = mpc512x_psc_cw_restore_ints,
.set_baudrate = mpc512x_psc_set_baudrate,
- .clock = mpc512x_psc_clock,
+ .clock_alloc = mpc512x_psc_alloc_clock,
+ .clock_relse = mpc512x_psc_relse_clock,
+ .clock = mpc512x_psc_endis_clock,
.fifoc_init = mpc512x_psc_fifoc_init,
.fifoc_uninit = mpc512x_psc_fifoc_uninit,
.get_irq = mpc512x_psc_get_irq,
@@ -1166,6 +1282,9 @@ mpc52xx_uart_type(struct uart_port *port)
static void
mpc52xx_uart_release_port(struct uart_port *port)
{
+ if (psc_ops->clock_relse)
+ psc_ops->clock_relse(port);
+
/* remapped by us ? */
if (port->flags & UPF_IOREMAP) {
iounmap(port->membase);
@@ -1190,11 +1309,24 @@ mpc52xx_uart_request_port(struct uart_port *port)
err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc),
"mpc52xx_psc_uart") != NULL ? 0 : -EBUSY;
- if (err && (port->flags & UPF_IOREMAP)) {
+ if (err)
+ goto out_membase;
+
+ if (psc_ops->clock_alloc) {
+ err = psc_ops->clock_alloc(port);
+ if (err)
+ goto out_mapregion;
+ }
+
+ return 0;
+
+out_mapregion:
+ release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
+out_membase:
+ if (port->flags & UPF_IOREMAP) {
iounmap(port->membase);
port->membase = NULL;
}
-
return err;
}
@@ -1237,7 +1369,6 @@ static struct uart_ops mpc52xx_uart_ops = {
.shutdown = mpc52xx_uart_shutdown,
.set_termios = mpc52xx_uart_set_termios,
/* .pm = mpc52xx_uart_pm, Not supported yet */
-/* .set_wake = mpc52xx_uart_set_wake, Not supported yet */
.type = mpc52xx_uart_type,
.release_port = mpc52xx_uart_release_port,
.request_port = mpc52xx_uart_request_port,
@@ -1702,7 +1833,7 @@ mpc52xx_uart_of_remove(struct platform_device *op)
static int
mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
{
- struct uart_port *port = (struct uart_port *) platform_get_drvdata(op);
+ struct uart_port *port = platform_get_drvdata(op);
if (port)
uart_suspend_port(&mpc52xx_uart_driver, port);
@@ -1713,7 +1844,7 @@ mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
static int
mpc52xx_uart_of_resume(struct platform_device *op)
{
- struct uart_port *port = (struct uart_port *) platform_get_drvdata(op);
+ struct uart_port *port = platform_get_drvdata(op);
if (port)
uart_resume_port(&mpc52xx_uart_driver, port);
diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c
index bc24f493167..759c6a6fa74 100644
--- a/drivers/tty/serial/mpsc.c
+++ b/drivers/tty/serial/mpsc.c
@@ -934,7 +934,7 @@ static int serial_polled;
******************************************************************************
*/
-static int mpsc_rx_intr(struct mpsc_port_info *pi)
+static int mpsc_rx_intr(struct mpsc_port_info *pi, unsigned long *flags)
{
struct mpsc_rx_desc *rxre;
struct tty_port *port = &pi->port.state->port;
@@ -969,8 +969,11 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi)
#endif
/* Following use of tty struct directly is deprecated */
if (tty_buffer_request_room(port, bytes_in) < bytes_in) {
- if (port->low_latency)
+ if (port->low_latency) {
+ spin_unlock_irqrestore(&pi->port.lock, *flags);
tty_flip_buffer_push(port);
+ spin_lock_irqsave(&pi->port.lock, *flags);
+ }
/*
* If this failed then we will throw away the bytes
* but must do so to clear interrupts.
@@ -1080,7 +1083,9 @@ next_frame:
if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
mpsc_start_rx(pi);
+ spin_unlock_irqrestore(&pi->port.lock, *flags);
tty_flip_buffer_push(port);
+ spin_lock_irqsave(&pi->port.lock, *flags);
return rc;
}
@@ -1222,7 +1227,7 @@ static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id)
spin_lock_irqsave(&pi->port.lock, iflags);
mpsc_sdma_intr_ack(pi);
- if (mpsc_rx_intr(pi))
+ if (mpsc_rx_intr(pi, &iflags))
rc = IRQ_HANDLED;
if (mpsc_tx_intr(pi))
rc = IRQ_HANDLED;
@@ -1453,7 +1458,7 @@ static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios,
pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE
| SDMA_DESC_CMDSTAT_FR;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR;
/* Characters/events to ignore */
@@ -1884,7 +1889,7 @@ static int mpsc_shared_drv_probe(struct platform_device *dev)
if (dev->id == 0) {
if (!(rc = mpsc_shared_map_regs(dev))) {
pdata = (struct mpsc_shared_pdata *)
- dev->dev.platform_data;
+ dev_get_platdata(&dev->dev);
mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val;
mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val;
@@ -2025,7 +2030,7 @@ static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi,
{
struct mpsc_pdata *pdata;
- pdata = (struct mpsc_pdata *)pd->dev.platform_data;
+ pdata = dev_get_platdata(&pd->dev);
pi->port.uartclk = pdata->brg_clk_freq;
pi->port.iotype = UPIO_MEM;
diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c
index 9b6ef20420c..db0448ae59d 100644
--- a/drivers/tty/serial/mrst_max3110.c
+++ b/drivers/tty/serial/mrst_max3110.c
@@ -43,6 +43,7 @@
#include <linux/kthread.h>
#include <linux/spi/spi.h>
+#include <linux/pm.h>
#include "mrst_max3110.h"
@@ -61,6 +62,7 @@ struct uart_max3110 {
struct task_struct *main_thread;
struct task_struct *read_thread;
struct mutex thread_mutex;
+ struct mutex io_mutex;
u32 baud;
u16 cur_conf;
@@ -90,6 +92,7 @@ static int max3110_write_then_read(struct uart_max3110 *max,
struct spi_transfer x;
int ret;
+ mutex_lock(&max->io_mutex);
spi_message_init(&message);
memset(&x, 0, sizeof x);
x.len = len;
@@ -104,6 +107,7 @@ static int max3110_write_then_read(struct uart_max3110 *max,
/* Do the i/o */
ret = spi_sync(spi, &message);
+ mutex_unlock(&max->io_mutex);
return ret;
}
@@ -491,19 +495,9 @@ static int serial_m3110_startup(struct uart_port *port)
port->state->port.low_latency = 1;
if (max->irq) {
- max->read_thread = NULL;
- ret = request_irq(max->irq, serial_m3110_irq,
- IRQ_TYPE_EDGE_FALLING, "max3110", max);
- if (ret) {
- max->irq = 0;
- pr_err(PR_FMT "unable to allocate IRQ, polling\n");
- } else {
- /* Enable RX IRQ only */
- config |= WC_RXA_IRQ_ENABLE;
- }
- }
-
- if (max->irq == 0) {
+ /* Enable RX IRQ only */
+ config |= WC_RXA_IRQ_ENABLE;
+ } else {
/* If IRQ is disabled, start a read thread for input data */
max->read_thread =
kthread_run(max3110_read_thread, max, "max3110_read");
@@ -517,8 +511,6 @@ static int serial_m3110_startup(struct uart_port *port)
ret = max3110_out(max, config);
if (ret) {
- if (max->irq)
- free_irq(max->irq, max);
if (max->read_thread)
kthread_stop(max->read_thread);
max->read_thread = NULL;
@@ -540,9 +532,6 @@ static void serial_m3110_shutdown(struct uart_port *port)
max->read_thread = NULL;
}
- if (max->irq)
- free_irq(max->irq, max);
-
/* Disable interrupts from this port */
config = WC_TAG | WC_SW_SHDI;
max3110_out(max, config);
@@ -713,7 +702,7 @@ static void serial_m3110_enable_ms(struct uart_port *port)
{
}
-struct uart_ops serial_m3110_ops = {
+static struct uart_ops serial_m3110_ops = {
.tx_empty = serial_m3110_tx_empty,
.set_mctrl = serial_m3110_set_mctrl,
.get_mctrl = serial_m3110_get_mctrl,
@@ -749,7 +738,8 @@ static int serial_m3110_suspend(struct device *dev)
struct spi_device *spi = to_spi_device(dev);
struct uart_max3110 *max = spi_get_drvdata(spi);
- disable_irq(max->irq);
+ if (max->irq > 0)
+ disable_irq(max->irq);
uart_suspend_port(&serial_m3110_reg, &max->port);
max3110_out(max, max->cur_conf | WC_SW_SHDI);
return 0;
@@ -762,7 +752,8 @@ static int serial_m3110_resume(struct device *dev)
max3110_out(max, max->cur_conf);
uart_resume_port(&serial_m3110_reg, &max->port);
- enable_irq(max->irq);
+ if (max->irq > 0)
+ enable_irq(max->irq);
return 0;
}
@@ -803,6 +794,7 @@ static int serial_m3110_probe(struct spi_device *spi)
max->irq = (u16)spi->irq;
mutex_init(&max->thread_mutex);
+ mutex_init(&max->io_mutex);
max->word_7bits = 0;
max->parity = 0;
@@ -840,11 +832,21 @@ static int serial_m3110_probe(struct spi_device *spi)
goto err_kthread;
}
+ if (max->irq) {
+ ret = request_irq(max->irq, serial_m3110_irq,
+ IRQ_TYPE_EDGE_FALLING, "max3110", max);
+ if (ret) {
+ max->irq = 0;
+ dev_warn(&spi->dev,
+ "unable to allocate IRQ, will use polling method\n");
+ }
+ }
+
spi_set_drvdata(spi, max);
pmax = max;
/* Give membase a psudo value to pass serial_core's check */
- max->port.membase = (void *)0xff110000;
+ max->port.membase = (unsigned char __iomem *)0xff110000;
uart_add_one_port(&serial_m3110_reg, &max->port);
return 0;
@@ -867,6 +869,9 @@ static int serial_m3110_remove(struct spi_device *dev)
free_page((unsigned long)max->con_xmit.buf);
+ if (max->irq)
+ free_irq(max->irq, max);
+
if (max->main_thread)
kthread_stop(max->main_thread);
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 2c6cfb3cf03..72000a6d5af 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -39,22 +39,31 @@
#include "msm_serial.h"
+enum {
+ UARTDM_1P1 = 1,
+ UARTDM_1P2,
+ UARTDM_1P3,
+ UARTDM_1P4,
+};
+
struct msm_port {
struct uart_port uart;
char name[16];
struct clk *clk;
struct clk *pclk;
unsigned int imr;
- unsigned int *gsbi_base;
int is_uartdm;
unsigned int old_snap_state;
};
-static inline void wait_for_xmitr(struct uart_port *port, int bits)
+static inline void wait_for_xmitr(struct uart_port *port)
{
- if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY))
- while ((msm_read(port, UART_ISR) & bits) != bits)
- cpu_relax();
+ while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) {
+ if (msm_read(port, UART_ISR) & UART_ISR_TX_READY)
+ break;
+ udelay(1);
+ }
+ msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR);
}
static void msm_stop_tx(struct uart_port *port)
@@ -137,7 +146,10 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr)
count -= 4;
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(tport);
+ spin_lock(&port->lock);
+
if (misr & (UART_IMR_RXSTALE))
msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
@@ -189,52 +201,69 @@ static void handle_rx(struct uart_port *port)
tty_insert_flip_char(tport, c, flag);
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(tport);
+ spin_lock(&port->lock);
}
-static void reset_dm_count(struct uart_port *port)
+static void reset_dm_count(struct uart_port *port, int count)
{
- wait_for_xmitr(port, UART_ISR_TX_READY);
- msm_write(port, 1, UARTDM_NCF_TX);
+ wait_for_xmitr(port);
+ msm_write(port, count, UARTDM_NCF_TX);
+ msm_read(port, UARTDM_NCF_TX);
}
static void handle_tx(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
struct msm_port *msm_port = UART_TO_MSM(port);
- int sent_tx;
+ unsigned int tx_count, num_chars;
+ unsigned int tf_pointer = 0;
+
+ tx_count = uart_circ_chars_pending(xmit);
+ tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail,
+ port->fifosize);
if (port->x_char) {
if (msm_port->is_uartdm)
- reset_dm_count(port);
+ reset_dm_count(port, tx_count + 1);
msm_write(port, port->x_char,
msm_port->is_uartdm ? UARTDM_TF : UART_TF);
port->icount.tx++;
port->x_char = 0;
+ } else if (tx_count && msm_port->is_uartdm) {
+ reset_dm_count(port, tx_count);
}
- if (msm_port->is_uartdm)
- reset_dm_count(port);
+ while (tf_pointer < tx_count) {
+ int i;
+ char buf[4] = { 0 };
+ unsigned int *bf = (unsigned int *)&buf;
- while (msm_read(port, UART_SR) & UART_SR_TX_READY) {
- if (uart_circ_empty(xmit)) {
- /* disable tx interrupts */
- msm_port->imr &= ~UART_IMR_TXLEV;
- msm_write(port, msm_port->imr, UART_IMR);
+ if (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
break;
- }
- msm_write(port, xmit->buf[xmit->tail],
- msm_port->is_uartdm ? UARTDM_TF : UART_TF);
if (msm_port->is_uartdm)
- reset_dm_count(port);
+ num_chars = min(tx_count - tf_pointer,
+ (unsigned int)sizeof(buf));
+ else
+ num_chars = 1;
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- sent_tx = 1;
+ for (i = 0; i < num_chars; i++) {
+ buf[i] = xmit->buf[xmit->tail + i];
+ port->icount.tx++;
+ }
+
+ msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+ xmit->tail = (xmit->tail + num_chars) & (UART_XMIT_SIZE - 1);
+ tf_pointer += num_chars;
}
+ /* disable tx interrupts if nothing more to send */
+ if (uart_circ_empty(xmit))
+ msm_stop_tx(port);
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
@@ -286,6 +315,8 @@ static unsigned int msm_get_mctrl(struct uart_port *port)
static void msm_reset(struct uart_port *port)
{
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
/* reset everything */
msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
@@ -293,9 +324,13 @@ static void msm_reset(struct uart_port *port)
msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
+
+ /* Disable DM modes */
+ if (msm_port->is_uartdm)
+ msm_write(port, 0, UARTDM_DMEN);
}
-void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned int mr;
mr = msm_read(port, UART_MR1);
@@ -318,70 +353,60 @@ static void msm_break_ctl(struct uart_port *port, int break_ctl)
msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
}
+struct msm_baud_map {
+ u16 divisor;
+ u8 code;
+ u8 rxstale;
+};
+
+static const struct msm_baud_map *
+msm_find_best_baud(struct uart_port *port, unsigned int baud)
+{
+ unsigned int i, divisor;
+ const struct msm_baud_map *entry;
+ static const struct msm_baud_map table[] = {
+ { 1536, 0x00, 1 },
+ { 768, 0x11, 1 },
+ { 384, 0x22, 1 },
+ { 192, 0x33, 1 },
+ { 96, 0x44, 1 },
+ { 48, 0x55, 1 },
+ { 32, 0x66, 1 },
+ { 24, 0x77, 1 },
+ { 16, 0x88, 1 },
+ { 12, 0x99, 6 },
+ { 8, 0xaa, 6 },
+ { 6, 0xbb, 6 },
+ { 4, 0xcc, 6 },
+ { 3, 0xdd, 8 },
+ { 2, 0xee, 16 },
+ { 1, 0xff, 31 },
+ };
+
+ divisor = uart_get_divisor(port, baud);
+
+ for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++)
+ if (entry->divisor <= divisor)
+ break;
+
+ return entry; /* Default to smallest divider */
+}
+
static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
{
- unsigned int baud_code, rxstale, watermark;
+ unsigned int rxstale, watermark;
struct msm_port *msm_port = UART_TO_MSM(port);
+ const struct msm_baud_map *entry;
- switch (baud) {
- case 300:
- baud_code = UART_CSR_300;
- rxstale = 1;
- break;
- case 600:
- baud_code = UART_CSR_600;
- rxstale = 1;
- break;
- case 1200:
- baud_code = UART_CSR_1200;
- rxstale = 1;
- break;
- case 2400:
- baud_code = UART_CSR_2400;
- rxstale = 1;
- break;
- case 4800:
- baud_code = UART_CSR_4800;
- rxstale = 1;
- break;
- case 9600:
- baud_code = UART_CSR_9600;
- rxstale = 2;
- break;
- case 14400:
- baud_code = UART_CSR_14400;
- rxstale = 3;
- break;
- case 19200:
- baud_code = UART_CSR_19200;
- rxstale = 4;
- break;
- case 28800:
- baud_code = UART_CSR_28800;
- rxstale = 6;
- break;
- case 38400:
- baud_code = UART_CSR_38400;
- rxstale = 8;
- break;
- case 57600:
- baud_code = UART_CSR_57600;
- rxstale = 16;
- break;
- case 115200:
- default:
- baud_code = UART_CSR_115200;
- baud = 115200;
- rxstale = 31;
- break;
- }
+ entry = msm_find_best_baud(port, baud);
if (msm_port->is_uartdm)
msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
- msm_write(port, baud_code, UART_CSR);
+ msm_write(port, entry->code, UART_CSR);
/* RX stale watermark */
+ rxstale = entry->rxstale;
watermark = UART_IPR_STALE_LSB & rxstale;
watermark |= UART_IPR_RXSTALE_LAST;
watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2);
@@ -409,8 +434,7 @@ static void msm_init_clock(struct uart_port *port)
struct msm_port *msm_port = UART_TO_MSM(port);
clk_prepare_enable(msm_port->clk);
- if (!IS_ERR(msm_port->pclk))
- clk_prepare_enable(msm_port->pclk);
+ clk_prepare_enable(msm_port->pclk);
msm_serial_set_mnd_regs(port);
}
@@ -558,7 +582,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
port->read_status_mask = 0;
if (termios->c_iflag & INPCK)
port->read_status_mask |= UART_SR_PAR_FRAME_ERR;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= UART_SR_RX_BREAK;
uart_update_timeout(port, termios->c_cflag, baud);
@@ -574,9 +598,7 @@ static const char *msm_type(struct uart_port *port)
static void msm_release_port(struct uart_port *port)
{
struct platform_device *pdev = to_platform_device(port->dev);
- struct msm_port *msm_port = UART_TO_MSM(port);
struct resource *uart_resource;
- struct resource *gsbi_resource;
resource_size_t size;
uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -587,30 +609,12 @@ static void msm_release_port(struct uart_port *port)
release_mem_region(port->mapbase, size);
iounmap(port->membase);
port->membase = NULL;
-
- if (msm_port->gsbi_base) {
- iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base +
- GSBI_CONTROL);
-
- gsbi_resource = platform_get_resource(pdev,
- IORESOURCE_MEM, 1);
-
- if (unlikely(!gsbi_resource))
- return;
-
- size = resource_size(gsbi_resource);
- release_mem_region(gsbi_resource->start, size);
- iounmap(msm_port->gsbi_base);
- msm_port->gsbi_base = NULL;
- }
}
static int msm_request_port(struct uart_port *port)
{
- struct msm_port *msm_port = UART_TO_MSM(port);
struct platform_device *pdev = to_platform_device(port->dev);
struct resource *uart_resource;
- struct resource *gsbi_resource;
resource_size_t size;
int ret;
@@ -629,28 +633,8 @@ static int msm_request_port(struct uart_port *port)
goto fail_release_port;
}
- gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- /* Is this a GSBI-based port? */
- if (gsbi_resource) {
- size = resource_size(gsbi_resource);
-
- if (!request_mem_region(gsbi_resource->start, size,
- "msm_serial")) {
- ret = -EBUSY;
- goto fail_release_port;
- }
-
- msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
- if (!msm_port->gsbi_base) {
- ret = -EBUSY;
- goto fail_release_gsbi;
- }
- }
-
return 0;
-fail_release_gsbi:
- release_mem_region(gsbi_resource->start, size);
fail_release_port:
release_mem_region(port->mapbase, size);
return ret;
@@ -658,7 +642,6 @@ fail_release_port:
static void msm_config_port(struct uart_port *port, int flags)
{
- struct msm_port *msm_port = UART_TO_MSM(port);
int ret;
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_MSM;
@@ -666,10 +649,6 @@ static void msm_config_port(struct uart_port *port, int flags)
if (ret)
return;
}
-
- if (msm_port->is_uartdm)
- iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base +
- GSBI_CONTROL);
}
static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
@@ -689,19 +668,128 @@ static void msm_power(struct uart_port *port, unsigned int state,
switch (state) {
case 0:
clk_prepare_enable(msm_port->clk);
- if (!IS_ERR(msm_port->pclk))
- clk_prepare_enable(msm_port->pclk);
+ clk_prepare_enable(msm_port->pclk);
break;
case 3:
clk_disable_unprepare(msm_port->clk);
- if (!IS_ERR(msm_port->pclk))
- clk_disable_unprepare(msm_port->pclk);
+ clk_disable_unprepare(msm_port->pclk);
break;
default:
printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
}
}
+#ifdef CONFIG_CONSOLE_POLL
+static int msm_poll_init(struct uart_port *port)
+{
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ /* Enable single character mode on RX FIFO */
+ if (msm_port->is_uartdm >= UARTDM_1P4)
+ msm_write(port, UARTDM_DMEN_RX_SC_ENABLE, UARTDM_DMEN);
+
+ return 0;
+}
+
+static int msm_poll_get_char_single(struct uart_port *port)
+{
+ struct msm_port *msm_port = UART_TO_MSM(port);
+ unsigned int rf_reg = msm_port->is_uartdm ? UARTDM_RF : UART_RF;
+
+ if (!(msm_read(port, UART_SR) & UART_SR_RX_READY))
+ return NO_POLL_CHAR;
+ else
+ return msm_read(port, rf_reg) & 0xff;
+}
+
+static int msm_poll_get_char_dm_1p3(struct uart_port *port)
+{
+ int c;
+ static u32 slop;
+ static int count;
+ unsigned char *sp = (unsigned char *)&slop;
+
+ /* Check if a previous read had more than one char */
+ if (count) {
+ c = sp[sizeof(slop) - count];
+ count--;
+ /* Or if FIFO is empty */
+ } else if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) {
+ /*
+ * If RX packing buffer has less than a word, force stale to
+ * push contents into RX FIFO
+ */
+ count = msm_read(port, UARTDM_RXFS);
+ count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK;
+ if (count) {
+ msm_write(port, UART_CR_CMD_FORCE_STALE, UART_CR);
+ slop = msm_read(port, UARTDM_RF);
+ c = sp[0];
+ count--;
+ } else {
+ c = NO_POLL_CHAR;
+ }
+ /* FIFO has a word */
+ } else {
+ slop = msm_read(port, UARTDM_RF);
+ c = sp[0];
+ count = sizeof(slop) - 1;
+ }
+
+ return c;
+}
+
+static int msm_poll_get_char(struct uart_port *port)
+{
+ u32 imr;
+ int c;
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ /* Disable all interrupts */
+ imr = msm_read(port, UART_IMR);
+ msm_write(port, 0, UART_IMR);
+
+ if (msm_port->is_uartdm == UARTDM_1P3)
+ c = msm_poll_get_char_dm_1p3(port);
+ else
+ c = msm_poll_get_char_single(port);
+
+ /* Enable interrupts */
+ msm_write(port, imr, UART_IMR);
+
+ return c;
+}
+
+static void msm_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ u32 imr;
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ /* Disable all interrupts */
+ imr = msm_read(port, UART_IMR);
+ msm_write(port, 0, UART_IMR);
+
+ if (msm_port->is_uartdm)
+ reset_dm_count(port, 1);
+
+ /* Wait until FIFO is empty */
+ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
+ cpu_relax();
+
+ /* Write a character */
+ msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+
+ /* Wait until FIFO is empty */
+ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
+ cpu_relax();
+
+ /* Enable interrupts */
+ msm_write(port, imr, UART_IMR);
+
+ return;
+}
+#endif
+
static struct uart_ops msm_uart_pops = {
.tx_empty = msm_tx_empty,
.set_mctrl = msm_set_mctrl,
@@ -720,6 +808,11 @@ static struct uart_ops msm_uart_pops = {
.config_port = msm_config_port,
.verify_port = msm_verify_port,
.pm = msm_power,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_init = msm_poll_init,
+ .poll_get_char = msm_poll_get_char,
+ .poll_put_char = msm_poll_put_char,
+#endif
};
static struct msm_port msm_uart_ports[] = {
@@ -760,32 +853,63 @@ static inline struct uart_port *get_port_from_line(unsigned int line)
}
#ifdef CONFIG_SERIAL_MSM_CONSOLE
-
-static void msm_console_putchar(struct uart_port *port, int c)
-{
- struct msm_port *msm_port = UART_TO_MSM(port);
-
- if (msm_port->is_uartdm)
- reset_dm_count(port);
-
- while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
- ;
- msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
-}
-
static void msm_console_write(struct console *co, const char *s,
unsigned int count)
{
+ int i;
struct uart_port *port;
struct msm_port *msm_port;
+ int num_newlines = 0;
+ bool replaced = false;
BUG_ON(co->index < 0 || co->index >= UART_NR);
port = get_port_from_line(co->index);
msm_port = UART_TO_MSM(port);
+ /* Account for newlines that will get a carriage return added */
+ for (i = 0; i < count; i++)
+ if (s[i] == '\n')
+ num_newlines++;
+ count += num_newlines;
+
spin_lock(&port->lock);
- uart_console_write(port, s, count, msm_console_putchar);
+ if (msm_port->is_uartdm)
+ reset_dm_count(port, count);
+
+ i = 0;
+ while (i < count) {
+ int j;
+ unsigned int num_chars;
+ char buf[4] = { 0 };
+ unsigned int *bf = (unsigned int *)&buf;
+
+ if (msm_port->is_uartdm)
+ num_chars = min(count - i, (unsigned int)sizeof(buf));
+ else
+ num_chars = 1;
+
+ for (j = 0; j < num_chars; j++) {
+ char c = *s;
+
+ if (c == '\n' && !replaced) {
+ buf[j] = '\r';
+ j++;
+ replaced = true;
+ }
+ if (j < num_chars) {
+ buf[j] = c;
+ s++;
+ replaced = false;
+ }
+ }
+
+ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
+ cpu_relax();
+
+ msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+ i += num_chars;
+ }
spin_unlock(&port->lock);
}
@@ -859,11 +983,20 @@ static struct uart_driver msm_uart_driver = {
static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
-static int __init msm_serial_probe(struct platform_device *pdev)
+static const struct of_device_id msm_uartdm_table[] = {
+ { .compatible = "qcom,msm-uartdm-v1.1", .data = (void *)UARTDM_1P1 },
+ { .compatible = "qcom,msm-uartdm-v1.2", .data = (void *)UARTDM_1P2 },
+ { .compatible = "qcom,msm-uartdm-v1.3", .data = (void *)UARTDM_1P3 },
+ { .compatible = "qcom,msm-uartdm-v1.4", .data = (void *)UARTDM_1P4 },
+ { }
+};
+
+static int msm_serial_probe(struct platform_device *pdev)
{
struct msm_port *msm_port;
struct resource *resource;
struct uart_port *port;
+ const struct of_device_id *id;
int irq;
if (pdev->id == -1)
@@ -878,23 +1011,18 @@ static int __init msm_serial_probe(struct platform_device *pdev)
port->dev = &pdev->dev;
msm_port = UART_TO_MSM(port);
- if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
- msm_port->is_uartdm = 1;
+ id = of_match_device(msm_uartdm_table, &pdev->dev);
+ if (id)
+ msm_port->is_uartdm = (unsigned long)id->data;
else
msm_port->is_uartdm = 0;
- if (msm_port->is_uartdm) {
- msm_port->clk = devm_clk_get(&pdev->dev, "gsbi_uart_clk");
- msm_port->pclk = devm_clk_get(&pdev->dev, "gsbi_pclk");
- } else {
- msm_port->clk = devm_clk_get(&pdev->dev, "uart_clk");
- msm_port->pclk = ERR_PTR(-ENOENT);
- }
-
+ msm_port->clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(msm_port->clk))
return PTR_ERR(msm_port->clk);
if (msm_port->is_uartdm) {
+ msm_port->pclk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(msm_port->pclk))
return PTR_ERR(msm_port->pclk);
@@ -931,11 +1059,13 @@ static int msm_serial_remove(struct platform_device *pdev)
static struct of_device_id msm_match_table[] = {
{ .compatible = "qcom,msm-uart" },
+ { .compatible = "qcom,msm-uartdm" },
{}
};
static struct platform_driver msm_platform_driver = {
.remove = msm_serial_remove,
+ .probe = msm_serial_probe,
.driver = {
.name = "msm_serial",
.owner = THIS_MODULE,
@@ -951,7 +1081,7 @@ static int __init msm_serial_init(void)
if (unlikely(ret))
return ret;
- ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe);
+ ret = platform_driver_register(&msm_platform_driver);
if (unlikely(ret))
uart_unregister_driver(&msm_uart_driver);
diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h
index e4acef5de77..d98d45efdf8 100644
--- a/drivers/tty/serial/msm_serial.h
+++ b/drivers/tty/serial/msm_serial.h
@@ -38,19 +38,7 @@
#define UART_MR2_PARITY_MODE_SPACE 0x3
#define UART_MR2_PARITY_MODE 0x3
-#define UART_CSR 0x0008
-#define UART_CSR_115200 0xFF
-#define UART_CSR_57600 0xEE
-#define UART_CSR_38400 0xDD
-#define UART_CSR_28800 0xCC
-#define UART_CSR_19200 0xBB
-#define UART_CSR_14400 0xAA
-#define UART_CSR_9600 0x99
-#define UART_CSR_4800 0x77
-#define UART_CSR_2400 0x55
-#define UART_CSR_1200 0x44
-#define UART_CSR_600 0x33
-#define UART_CSR_300 0x22
+#define UART_CSR 0x0008
#define UART_TF 0x000C
#define UARTDM_TF 0x0070
@@ -71,6 +59,8 @@
#define UART_CR_CMD_RESET_RFR (14 << 4)
#define UART_CR_CMD_PROTECTION_EN (16 << 4)
#define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4)
+#define UART_CR_CMD_FORCE_STALE (4 << 8)
+#define UART_CR_CMD_RESET_TX_READY (3 << 8)
#define UART_CR_TX_DISABLE (1 << 3)
#define UART_CR_TX_ENABLE (1 << 2)
#define UART_CR_RX_DISABLE (1 << 1)
@@ -119,10 +109,13 @@
#define UART_ISR 0x0014
#define UART_ISR_TX_READY (1 << 7)
-#define GSBI_CONTROL 0x0
-#define GSBI_PROTOCOL_CODE 0x30
-#define GSBI_PROTOCOL_UART 0x40
-#define GSBI_PROTOCOL_IDLE 0x0
+#define UARTDM_RXFS 0x50
+#define UARTDM_RXFS_BUF_SHIFT 0x7
+#define UARTDM_RXFS_BUF_MASK 0x7
+
+#define UARTDM_DMEN 0x3C
+#define UARTDM_DMEN_RX_SC_ENABLE BIT(5)
+#define UARTDM_DMEN_TX_SC_ENABLE BIT(4)
#define UARTDM_DMRX 0x34
#define UARTDM_NCF_TX 0x40
@@ -151,6 +144,7 @@ static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port)
msm_write(port, 0xF1, UART_NREG);
msm_write(port, 0x0F, UART_DREG);
msm_write(port, 0x1A, UART_MNDREG);
+ port->uartclk = 1843200;
}
/*
@@ -162,6 +156,7 @@ static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port)
msm_write(port, 0xF6, UART_NREG);
msm_write(port, 0x0F, UART_DREG);
msm_write(port, 0x0A, UART_MNDREG);
+ port->uartclk = 1843200;
}
static inline
@@ -169,7 +164,7 @@ void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port)
{
if (port->uartclk == 19200000)
msm_serial_set_mnd_regs_tcxo(port);
- else
+ else if (port->uartclk == 4800000)
msm_serial_set_mnd_regs_tcxoby4(port);
}
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 4ca2f64861e..48e94961a9e 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -1618,7 +1618,7 @@ static int msm_hs_probe(struct platform_device *pdev)
struct msm_hs_port *msm_uport;
struct resource *resource;
const struct msm_serial_hs_platform_data *pdata =
- pdev->dev.platform_data;
+ dev_get_platdata(&pdev->dev);
if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c
index 7fd6aaaacd8..be127d0da32 100644
--- a/drivers/tty/serial/mux.c
+++ b/drivers/tty/serial/mux.c
@@ -29,7 +29,7 @@
#include <asm/irq.h>
#include <asm/parisc-device.h>
-#ifdef CONFIG_MAGIC_SYSRQ
+#if defined(CONFIG_SERIAL_MUX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#include <linux/sysrq.h>
#define SUPPORT_SYSRQ
#endif
@@ -613,7 +613,7 @@ static void __exit mux_exit(void)
{
/* Delete the Mux timer. */
if(port_cnt > 0) {
- del_timer(&mux_timer);
+ del_timer_sync(&mux_timer);
#ifdef CONFIG_SERIAL_MUX_CONSOLE
unregister_console(&mux_console);
#endif
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 4f5f161896a..86de4477d98 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -32,7 +32,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/of_device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
@@ -40,6 +39,7 @@
#include <asm/cacheflush.h>
#define MXS_AUART_PORTS 5
+#define MXS_AUART_FIFO_SIZE 16
#define AUART_CTRL0 0x00000000
#define AUART_CTRL0_SET 0x00000004
@@ -134,10 +134,10 @@ enum mxs_auart_type {
struct mxs_auart_port {
struct uart_port port;
-#define MXS_AUART_DMA_CONFIG 0x1
#define MXS_AUART_DMA_ENABLED 0x2
#define MXS_AUART_DMA_TX_SYNC 2 /* bit 2 */
#define MXS_AUART_DMA_RX_READY 3 /* bit 3 */
+#define MXS_AUART_RTSCTS 4 /* bit 4 */
unsigned long flags;
unsigned int ctrl;
enum mxs_auart_type devtype;
@@ -200,7 +200,7 @@ static void dma_tx_callback(void *param)
/* clear the bit used to serialize the DMA tx. */
clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
- smp_mb__after_clear_bit();
+ smp_mb__after_atomic();
/* wake up the possible processes. */
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -275,7 +275,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s)
mxs_auart_dma_tx(s, i);
} else {
clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
- smp_mb__after_clear_bit();
+ smp_mb__after_atomic();
}
return;
}
@@ -549,6 +549,9 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s)
s->flags |= MXS_AUART_DMA_ENABLED;
dev_dbg(s->dev, "enabled the DMA support.");
+ /* The DMA buffer is now the FIFO the TTY subsystem can use */
+ s->port.fifosize = UART_XMIT_SIZE;
+
return 0;
err_out:
@@ -601,7 +604,7 @@ static void mxs_auart_settermios(struct uart_port *u,
if (termios->c_iflag & INPCK)
u->read_status_mask |= AUART_STAT_PERR;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
u->read_status_mask |= AUART_STAT_BERR;
/*
@@ -640,7 +643,8 @@ static void mxs_auart_settermios(struct uart_port *u,
* we can only implement the DMA support for auart
* in mx28.
*/
- if (is_imx28_auart(s) && (s->flags & MXS_AUART_DMA_CONFIG)) {
+ if (is_imx28_auart(s)
+ && test_bit(MXS_AUART_RTSCTS, &s->flags)) {
if (!mxs_auart_dma_init(s))
/* enable DMA tranfer */
ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE
@@ -678,11 +682,18 @@ static void mxs_auart_settermios(struct uart_port *u,
static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
{
- u32 istatus, istat;
+ u32 istat;
struct mxs_auart_port *s = context;
u32 stat = readl(s->port.membase + AUART_STAT);
- istatus = istat = readl(s->port.membase + AUART_INTR);
+ istat = readl(s->port.membase + AUART_INTR);
+
+ /* ack irq */
+ writel(istat & (AUART_INTR_RTIS
+ | AUART_INTR_TXIS
+ | AUART_INTR_RXIS
+ | AUART_INTR_CTSMIS),
+ s->port.membase + AUART_INTR_CLR);
if (istat & AUART_INTR_CTSMIS) {
uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS);
@@ -702,12 +713,6 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
istat &= ~AUART_INTR_TXIS;
}
- writel(istatus & (AUART_INTR_RTIS
- | AUART_INTR_TXIS
- | AUART_INTR_RXIS
- | AUART_INTR_CTSMIS),
- s->port.membase + AUART_INTR_CLR);
-
return IRQ_HANDLED;
}
@@ -729,9 +734,12 @@ static void mxs_auart_reset(struct uart_port *u)
static int mxs_auart_startup(struct uart_port *u)
{
+ int ret;
struct mxs_auart_port *s = to_auart_port(u);
- clk_prepare_enable(s->clk);
+ ret = clk_prepare_enable(s->clk);
+ if (ret)
+ return ret;
writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR);
@@ -740,6 +748,9 @@ static int mxs_auart_startup(struct uart_port *u)
writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
u->membase + AUART_INTR);
+ /* Reset FIFO size (it could have changed if DMA was enabled) */
+ u->fifosize = MXS_AUART_FIFO_SIZE;
+
/*
* Enable fifo so all four bytes of a DMA word are written to
* output (otherwise, only the LSB is written, ie. 1 in 4 bytes)
@@ -850,7 +861,7 @@ auart_console_write(struct console *co, const char *str, unsigned int count)
struct mxs_auart_port *s;
struct uart_port *port;
unsigned int old_ctrl0, old_ctrl2;
- unsigned int to = 1000;
+ unsigned int to = 20000;
if (co->index >= MXS_AUART_PORTS || co->index < 0)
return;
@@ -871,18 +882,23 @@ auart_console_write(struct console *co, const char *str, unsigned int count)
uart_console_write(port, str, count, mxs_auart_console_putchar);
- /*
- * Finally, wait for transmitter to become empty
- * and restore the TCR
- */
+ /* Finally, wait for transmitter to become empty ... */
while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) {
+ udelay(1);
if (!to--)
break;
- udelay(1);
}
- writel(old_ctrl0, port->membase + AUART_CTRL0);
- writel(old_ctrl2, port->membase + AUART_CTRL2);
+ /*
+ * ... and restore the TCR if we waited long enough for the transmitter
+ * to be idle. This might keep the transmitter enabled although it is
+ * unused, but that is better than to disable it while it is still
+ * transmitting.
+ */
+ if (!(readl(port->membase + AUART_STAT) & AUART_STAT_BUSY)) {
+ writel(old_ctrl0, port->membase + AUART_CTRL0);
+ writel(old_ctrl2, port->membase + AUART_CTRL2);
+ }
clk_disable(s->clk);
}
@@ -944,7 +960,9 @@ auart_console_setup(struct console *co, char *options)
if (!s)
return -ENODEV;
- clk_prepare_enable(s->clk);
+ ret = clk_prepare_enable(s->clk);
+ if (ret)
+ return ret;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1002,7 +1020,8 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s,
}
s->port.line = ret;
- s->flags |= MXS_AUART_DMA_CONFIG;
+ if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
+ set_bit(MXS_AUART_RTSCTS, &s->flags);
return 0;
}
@@ -1015,7 +1034,6 @@ static int mxs_auart_probe(struct platform_device *pdev)
u32 version;
int ret = 0;
struct resource *r;
- struct pinctrl *pinctrl;
s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
if (!s) {
@@ -1029,12 +1047,6 @@ static int mxs_auart_probe(struct platform_device *pdev)
else if (ret < 0)
goto out_free;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto out_free;
- }
-
if (of_id) {
pdev->id_entry = of_id->data;
s->devtype = pdev->id_entry->driver_data;
@@ -1056,7 +1068,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->port.membase = ioremap(r->start, resource_size(r));
s->port.ops = &mxs_auart_ops;
s->port.iotype = UPIO_MEM;
- s->port.fifosize = 16;
+ s->port.fifosize = MXS_AUART_FIFO_SIZE;
s->port.uartclk = clk_get_rate(s->clk);
s->port.type = PORT_IMX;
s->port.dev = s->dev = &pdev->dev;
diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c
index b9a40ed70be..7a6745601d4 100644
--- a/drivers/tty/serial/netx-serial.c
+++ b/drivers/tty/serial/netx-serial.c
@@ -196,7 +196,7 @@ static void netx_txint(struct uart_port *port)
uart_write_wakeup(port);
}
-static void netx_rxint(struct uart_port *port)
+static void netx_rxint(struct uart_port *port, unsigned long *flags)
{
unsigned char rx, flg, status;
@@ -236,7 +236,9 @@ static void netx_rxint(struct uart_port *port)
uart_insert_char(port, status, SR_OE, rx, flg);
}
+ spin_unlock_irqrestore(&port->lock, *flags);
tty_flip_buffer_push(&port->state->port);
+ spin_lock_irqsave(&port->lock, *flags);
}
static irqreturn_t netx_int(int irq, void *dev_id)
@@ -250,7 +252,7 @@ static irqreturn_t netx_int(int irq, void *dev_id)
status = readl(port->membase + UART_IIR) & IIR_MASK;
while (status) {
if (status & IIR_RIS)
- netx_rxint(port);
+ netx_rxint(port, &flags);
if (status & IIR_TIS)
netx_txint(port);
if (status & IIR_MIS) {
@@ -417,7 +419,7 @@ netx_set_termios(struct uart_port *port, struct ktermios *termios,
}
port->read_status_mask = 0;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= SR_BE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= SR_PE | SR_FE;
@@ -693,8 +695,6 @@ static int serial_netx_remove(struct platform_device *pdev)
{
struct netx_port *sport = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (sport)
uart_remove_one_port(&netx_reg, &sport->port);
diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c
index 549c70a2a63..693bc6c2561 100644
--- a/drivers/tty/serial/nwpserial.c
+++ b/drivers/tty/serial/nwpserial.c
@@ -149,7 +149,10 @@ static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
tty_insert_flip_char(port, ch, TTY_NORMAL);
} while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR);
+ spin_unlock(&up->port.lock);
tty_flip_buffer_push(port);
+ spin_lock(&up->port.lock);
+
ret = IRQ_HANDLED;
/* clear interrupt */
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index 2caf9c6f614..68d4455f3cf 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -9,7 +9,6 @@
* 2 of the License, or (at your option) any later version.
*
*/
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
@@ -174,6 +173,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
{
struct uart_8250_port port8250;
memset(&port8250, 0, sizeof(port8250));
+ port.type = port_type;
port8250.port = port;
if (port.fifosize)
@@ -183,6 +183,10 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
"auto-flow-control"))
port8250.capabilities |= UART_CAP_AFE;
+ if (of_property_read_bool(ofdev->dev.of_node,
+ "has-hw-flow-control"))
+ port8250.port.flags |= UPF_HARD_FLOW;
+
ret = serial8250_register_8250_port(&port8250);
break;
}
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index b6d17287307..d017cec8a34 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -39,10 +39,13 @@
#include <linux/irq.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/gpio.h>
-#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
#include <linux/platform_data/serial-omap.h>
+#include <dt-bindings/gpio/gpio.h>
+
#define OMAP_MAX_HSUART_PORTS 6
#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y))
@@ -52,6 +55,11 @@
#define OMAP_UART_REV_52 0x0502
#define OMAP_UART_REV_63 0x0603
+#define OMAP_UART_TX_WAKEUP_EN BIT(7)
+
+/* Feature flags */
+#define OMAP_UART_WER_HAS_TX_WAKEUP BIT(0)
+
#define UART_ERRATA_i202_MDR1_ACCESS BIT(0)
#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
@@ -127,6 +135,7 @@ struct uart_omap_port {
struct uart_port port;
struct uart_omap_dma uart_dma;
struct device *dev;
+ int wakeirq;
unsigned char ier;
unsigned char lcr;
@@ -137,6 +146,7 @@ struct uart_omap_port {
unsigned char dlh;
unsigned char mdr1;
unsigned char scr;
+ unsigned char wer;
int use_dma;
/*
@@ -151,28 +161,25 @@ struct uart_omap_port {
int context_loss_cnt;
u32 errata;
u8 wakeups_enabled;
+ u32 features;
- int DTR_gpio;
- int DTR_inverted;
- int DTR_active;
+ struct serial_rs485 rs485;
+ int rts_gpio;
struct pm_qos_request pm_qos_request;
u32 latency;
u32 calc_latency;
struct work_struct qos_work;
- struct pinctrl *pins;
bool is_suspending;
};
-#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port)))
+#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port)))
static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
/* Forward declaration of functions */
static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1);
-static struct workqueue_struct *serial_omap_uart_wq;
-
static inline unsigned int serial_in(struct uart_omap_port *up, int offset)
{
offset <<= up->port.regshift;
@@ -195,7 +202,7 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
{
- struct omap_uart_port_info *pdata = up->dev->platform_data;
+ struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
if (!pdata || !pdata->get_context_loss_count)
return -EINVAL;
@@ -203,9 +210,27 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
return pdata->get_context_loss_count(up->dev);
}
+static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up,
+ bool enable)
+{
+ if (!up->wakeirq)
+ return;
+
+ if (enable)
+ enable_irq(up->wakeirq);
+ else
+ disable_irq_nosync(up->wakeirq);
+}
+
static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
{
- struct omap_uart_port_info *pdata = up->dev->platform_data;
+ struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
+
+ if (enable == up->wakeups_enabled)
+ return;
+
+ serial_omap_enable_wakeirq(up, enable);
+ up->wakeups_enabled = enable;
if (!pdata || !pdata->enable_wakeup)
return;
@@ -231,12 +256,12 @@ serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
unsigned int n16 = port->uartclk / (16 * baud);
int baudAbsDiff13 = baud - (port->uartclk / (13 * n13));
int baudAbsDiff16 = baud - (port->uartclk / (16 * n16));
- if(baudAbsDiff13 < 0)
+ if (baudAbsDiff13 < 0)
baudAbsDiff13 = -baudAbsDiff13;
- if(baudAbsDiff16 < 0)
+ if (baudAbsDiff16 < 0)
baudAbsDiff16 = -baudAbsDiff16;
- return (baudAbsDiff13 > baudAbsDiff16);
+ return (baudAbsDiff13 >= baudAbsDiff16);
}
/*
@@ -247,13 +272,13 @@ serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
static unsigned int
serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
{
- unsigned int divisor;
+ unsigned int mode;
if (!serial_omap_baud_is_mode16(port, baud))
- divisor = 13;
+ mode = 13;
else
- divisor = 16;
- return port->uartclk/(baud * divisor);
+ mode = 16;
+ return port->uartclk/(mode * baud);
}
static void serial_omap_enable_ms(struct uart_port *port)
@@ -272,13 +297,61 @@ static void serial_omap_enable_ms(struct uart_port *port)
static void serial_omap_stop_tx(struct uart_port *port)
{
struct uart_omap_port *up = to_uart_omap_port(port);
+ int res;
pm_runtime_get_sync(up->dev);
+
+ /* Handle RS-485 */
+ if (up->rs485.flags & SER_RS485_ENABLED) {
+ if (up->scr & OMAP_UART_SCR_TX_EMPTY) {
+ /* THR interrupt is fired when both TX FIFO and TX
+ * shift register are empty. This means there's nothing
+ * left to transmit now, so make sure the THR interrupt
+ * is fired when TX FIFO is below the trigger level,
+ * disable THR interrupts and toggle the RS-485 GPIO
+ * data direction pin if needed.
+ */
+ up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+ serial_out(up, UART_OMAP_SCR, up->scr);
+ res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
+ if (gpio_get_value(up->rts_gpio) != res) {
+ if (up->rs485.delay_rts_after_send > 0)
+ mdelay(up->rs485.delay_rts_after_send);
+ gpio_set_value(up->rts_gpio, res);
+ }
+ } else {
+ /* We're asked to stop, but there's still stuff in the
+ * UART FIFO, so make sure the THR interrupt is fired
+ * when both TX FIFO and TX shift register are empty.
+ * The next THR interrupt (if no transmission is started
+ * in the meantime) will indicate the end of a
+ * transmission. Therefore we _don't_ disable THR
+ * interrupts in this situation.
+ */
+ up->scr |= OMAP_UART_SCR_TX_EMPTY;
+ serial_out(up, UART_OMAP_SCR, up->scr);
+ return;
+ }
+ }
+
if (up->ier & UART_IER_THRI) {
up->ier &= ~UART_IER_THRI;
serial_out(up, UART_IER, up->ier);
}
+ if ((up->rs485.flags & SER_RS485_ENABLED) &&
+ !(up->rs485.flags & SER_RS485_RX_DURING_TX)) {
+ /*
+ * Empty the RX FIFO, we are not interested in anything
+ * received during the half-duplex transmission.
+ */
+ serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_RCVR);
+ /* Re-enable RX interrupts */
+ up->ier |= UART_IER_RLSI | UART_IER_RDI;
+ up->port.read_status_mask |= UART_LSR_DR;
+ serial_out(up, UART_IER, up->ier);
+ }
+
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
}
@@ -288,7 +361,7 @@ static void serial_omap_stop_rx(struct uart_port *port)
struct uart_omap_port *up = to_uart_omap_port(port);
pm_runtime_get_sync(up->dev);
- up->ier &= ~UART_IER_RLSI;
+ up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
up->port.read_status_mask &= ~UART_LSR_DR;
serial_out(up, UART_IER, up->ier);
pm_runtime_mark_last_busy(up->dev);
@@ -319,11 +392,8 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr)
break;
} while (--count > 0);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
- spin_unlock(&up->port.lock);
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
- spin_lock(&up->port.lock);
- }
if (uart_circ_empty(xmit))
serial_omap_stop_tx(&up->port);
@@ -340,8 +410,29 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
static void serial_omap_start_tx(struct uart_port *port)
{
struct uart_omap_port *up = to_uart_omap_port(port);
+ int res;
pm_runtime_get_sync(up->dev);
+
+ /* Handle RS-485 */
+ if (up->rs485.flags & SER_RS485_ENABLED) {
+ /* Fire THR interrupts when FIFO is below trigger level */
+ up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+ serial_out(up, UART_OMAP_SCR, up->scr);
+
+ /* if rts not already enabled */
+ res = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0;
+ if (gpio_get_value(up->rts_gpio) != res) {
+ gpio_set_value(up->rts_gpio, res);
+ if (up->rs485.delay_rts_before_send > 0)
+ mdelay(up->rs485.delay_rts_before_send);
+ }
+ }
+
+ if ((up->rs485.flags & SER_RS485_ENABLED) &&
+ !(up->rs485.flags & SER_RS485_RX_DURING_TX))
+ serial_omap_stop_rx(port);
+
serial_omap_enable_ier_thri(up);
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
@@ -597,16 +688,6 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
serial_out(up, UART_MCR, up->mcr);
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
-
- if (gpio_is_valid(up->DTR_gpio) &&
- !!(mctrl & TIOCM_DTR) != up->DTR_active) {
- up->DTR_active = !up->DTR_active;
- if (gpio_cansleep(up->DTR_gpio))
- schedule_work(&up->qos_work);
- else
- gpio_set_value(up->DTR_gpio,
- up->DTR_active != up->DTR_inverted);
- }
}
static void serial_omap_break_ctl(struct uart_port *port, int break_state)
@@ -641,6 +722,17 @@ static int serial_omap_startup(struct uart_port *port)
if (retval)
return retval;
+ /* Optional wake-up IRQ */
+ if (up->wakeirq) {
+ retval = request_irq(up->wakeirq, serial_omap_irq,
+ up->port.irqflags, up->name, up);
+ if (retval) {
+ free_irq(up->port.irq, up);
+ return retval;
+ }
+ disable_irq(up->wakeirq);
+ }
+
dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
pm_runtime_get_sync(up->dev);
@@ -683,7 +775,11 @@ static int serial_omap_startup(struct uart_port *port)
serial_out(up, UART_IER, up->ier);
/* Enable module level wake up */
- serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP);
+ up->wer = OMAP_UART_WER_MOD_WKUP;
+ if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP)
+ up->wer |= OMAP_UART_TX_WAKEUP_EN;
+
+ serial_out(up, UART_OMAP_WER, up->wer);
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
@@ -725,6 +821,8 @@ static void serial_omap_shutdown(struct uart_port *port)
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
free_irq(up->port.irq, up);
+ if (up->wakeirq)
+ free_irq(up->wakeirq, up);
}
static void serial_omap_uart_qos_work(struct work_struct *work)
@@ -733,9 +831,6 @@ static void serial_omap_uart_qos_work(struct work_struct *work)
qos_work);
pm_qos_update_request(&up->pm_qos_request, up->latency);
- if (gpio_is_valid(up->DTR_gpio))
- gpio_set_value_cansleep(up->DTR_gpio,
- up->DTR_active != up->DTR_inverted);
}
static void
@@ -876,7 +971,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
*/
/* Set receive FIFO threshold to 16 characters and
- * transmit FIFO threshold to 16 spaces
+ * transmit FIFO threshold to 32 spaces
*/
up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK;
@@ -998,15 +1093,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
}
-static int serial_omap_set_wake(struct uart_port *port, unsigned int state)
-{
- struct uart_omap_port *up = to_uart_omap_port(port);
-
- serial_omap_enable_wakeup(up, state);
-
- return 0;
-}
-
static void
serial_omap_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
@@ -1254,6 +1340,85 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up)
#endif
+/* Enable or disable the rs485 support */
+static void
+serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+ struct uart_omap_port *up = to_uart_omap_port(port);
+ unsigned long flags;
+ unsigned int mode;
+ int val;
+
+ pm_runtime_get_sync(up->dev);
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /* Disable interrupts from this port */
+ mode = up->ier;
+ up->ier = 0;
+ serial_out(up, UART_IER, 0);
+
+ /* store new config */
+ up->rs485 = *rs485conf;
+
+ /*
+ * Just as a precaution, only allow rs485
+ * to be enabled if the gpio pin is valid
+ */
+ if (gpio_is_valid(up->rts_gpio)) {
+ /* enable / disable rts */
+ val = (up->rs485.flags & SER_RS485_ENABLED) ?
+ SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND;
+ val = (up->rs485.flags & val) ? 1 : 0;
+ gpio_set_value(up->rts_gpio, val);
+ } else
+ up->rs485.flags &= ~SER_RS485_ENABLED;
+
+ /* Enable interrupts */
+ up->ier = mode;
+ serial_out(up, UART_IER, up->ier);
+
+ /* If RS-485 is disabled, make sure the THR interrupt is fired when
+ * TX FIFO is below the trigger level.
+ */
+ if (!(up->rs485.flags & SER_RS485_ENABLED) &&
+ (up->scr & OMAP_UART_SCR_TX_EMPTY)) {
+ up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
+ serial_out(up, UART_OMAP_SCR, up->scr);
+ }
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
+}
+
+static int
+serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf, (void __user *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ serial_omap_config_rs485(port, &rs485conf);
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((void __user *) arg,
+ &(to_uart_omap_port(port)->rs485),
+ sizeof(rs485conf)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
static struct uart_ops serial_omap_pops = {
.tx_empty = serial_omap_tx_empty,
.set_mctrl = serial_omap_set_mctrl,
@@ -1269,12 +1434,12 @@ static struct uart_ops serial_omap_pops = {
.shutdown = serial_omap_shutdown,
.set_termios = serial_omap_set_termios,
.pm = serial_omap_pm,
- .set_wake = serial_omap_set_wake,
.type = serial_omap_type,
.release_port = serial_omap_release_port,
.request_port = serial_omap_request_port,
.config_port = serial_omap_config_port,
.verify_port = serial_omap_verify_port,
+ .ioctl = serial_omap_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_put_char = serial_omap_poll_put_char,
.poll_get_char = serial_omap_poll_get_char,
@@ -1313,6 +1478,11 @@ static int serial_omap_suspend(struct device *dev)
uart_suspend_port(&serial_omap_reg, &up->port);
flush_work(&up->qos_work);
+ if (device_may_wakeup(dev))
+ serial_omap_enable_wakeup(up, true);
+ else
+ serial_omap_enable_wakeup(up, false);
+
return 0;
}
@@ -1320,6 +1490,9 @@ static int serial_omap_resume(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
+ if (device_may_wakeup(dev))
+ serial_omap_enable_wakeup(up, false);
+
uart_resume_port(&serial_omap_reg, &up->port);
return 0;
@@ -1334,7 +1507,7 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
u32 mvr, scheme;
u16 revision, major, minor;
- mvr = serial_in(up, UART_OMAP_MVER);
+ mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift));
/* Check revision register scheme */
scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
@@ -1373,9 +1546,11 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
case OMAP_UART_REV_52:
up->errata |= (UART_ERRATA_i202_MDR1_ACCESS |
UART_ERRATA_i291_DMA_FORCEIDLE);
+ up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
break;
case OMAP_UART_REV_63:
up->errata |= UART_ERRATA_i202_MDR1_ACCESS;
+ up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
break;
default:
break;
@@ -1395,62 +1570,98 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
return omap_up_info;
}
-static int serial_omap_probe(struct platform_device *pdev)
+static int serial_omap_probe_rs485(struct uart_omap_port *up,
+ struct device_node *np)
{
- struct uart_omap_port *up;
- struct resource *mem, *irq;
- struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
+ struct serial_rs485 *rs485conf = &up->rs485;
+ u32 rs485_delay[2];
+ enum of_gpio_flags flags;
int ret;
- if (pdev->dev.of_node)
- omap_up_info = of_get_uart_port_info(&pdev->dev);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- return -ENODEV;
- }
+ rs485conf->flags = 0;
+ up->rts_gpio = -EINVAL;
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
- dev_err(&pdev->dev, "no irq resource?\n");
- return -ENODEV;
- }
+ if (!np)
+ return 0;
- if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
- pdev->dev.driver->name)) {
- dev_err(&pdev->dev, "memory region already claimed\n");
- return -EBUSY;
- }
+ if (of_property_read_bool(np, "rs485-rts-active-high"))
+ rs485conf->flags |= SER_RS485_RTS_ON_SEND;
+ else
+ rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
- if (gpio_is_valid(omap_up_info->DTR_gpio) &&
- omap_up_info->DTR_present) {
- ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial");
+ /* check for tx enable gpio */
+ up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags);
+ if (gpio_is_valid(up->rts_gpio)) {
+ ret = devm_gpio_request(up->dev, up->rts_gpio, "omap-serial");
if (ret < 0)
return ret;
- ret = gpio_direction_output(omap_up_info->DTR_gpio,
- omap_up_info->DTR_inverted);
+ ret = gpio_direction_output(up->rts_gpio,
+ flags & SER_RS485_RTS_AFTER_SEND);
if (ret < 0)
return ret;
+ } else if (up->rts_gpio == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else {
+ up->rts_gpio = -EINVAL;
+ }
+
+ if (of_property_read_u32_array(np, "rs485-rts-delay",
+ rs485_delay, 2) == 0) {
+ rs485conf->delay_rts_before_send = rs485_delay[0];
+ rs485conf->delay_rts_after_send = rs485_delay[1];
+ }
+
+ if (of_property_read_bool(np, "rs485-rx-during-tx"))
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+ if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time"))
+ rs485conf->flags |= SER_RS485_ENABLED;
+
+ return 0;
+}
+
+static int serial_omap_probe(struct platform_device *pdev)
+{
+ struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev);
+ struct uart_omap_port *up;
+ struct resource *mem;
+ void __iomem *base;
+ int uartirq = 0;
+ int wakeirq = 0;
+ int ret;
+
+ /* The optional wakeirq may be specified in the board dts file */
+ if (pdev->dev.of_node) {
+ uartirq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (!uartirq)
+ return -EPROBE_DEFER;
+ wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1);
+ omap_up_info = of_get_uart_port_info(&pdev->dev);
+ pdev->dev.platform_data = omap_up_info;
+ } else {
+ uartirq = platform_get_irq(pdev, 0);
+ if (uartirq < 0)
+ return -EPROBE_DEFER;
}
up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
if (!up)
return -ENOMEM;
- if (gpio_is_valid(omap_up_info->DTR_gpio) &&
- omap_up_info->DTR_present) {
- up->DTR_gpio = omap_up_info->DTR_gpio;
- up->DTR_inverted = omap_up_info->DTR_inverted;
- } else
- up->DTR_gpio = -EINVAL;
- up->DTR_active = 0;
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
up->dev = &pdev->dev;
up->port.dev = &pdev->dev;
up->port.type = PORT_OMAP;
up->port.iotype = UPIO_MEM;
- up->port.irq = irq->start;
+ up->port.irq = uartirq;
+ up->wakeirq = wakeirq;
+ if (!up->wakeirq)
+ dev_info(up->port.dev, "no wakeirq for uart%d\n",
+ up->port.line);
up->port.regshift = 2;
up->port.fifosize = 64;
@@ -1468,48 +1679,40 @@ static int serial_omap_probe(struct platform_device *pdev)
goto err_port_line;
}
- up->pins = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(up->pins)) {
- dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n",
- up->port.line, PTR_ERR(up->pins));
- up->pins = NULL;
- }
+ ret = serial_omap_probe_rs485(up, pdev->dev.of_node);
+ if (ret < 0)
+ goto err_rs485;
sprintf(up->name, "OMAP UART%d", up->port.line);
up->port.mapbase = mem->start;
- up->port.membase = devm_ioremap(&pdev->dev, mem->start,
- resource_size(mem));
- if (!up->port.membase) {
- dev_err(&pdev->dev, "can't ioremap UART\n");
- ret = -ENOMEM;
- goto err_ioremap;
- }
-
+ up->port.membase = base;
up->port.flags = omap_up_info->flags;
up->port.uartclk = omap_up_info->uartclk;
if (!up->port.uartclk) {
up->port.uartclk = DEFAULT_CLK_SPEED;
- dev_warn(&pdev->dev, "No clock speed specified: using default:"
- "%d\n", DEFAULT_CLK_SPEED);
+ dev_warn(&pdev->dev,
+ "No clock speed specified: using default: %d\n",
+ DEFAULT_CLK_SPEED);
}
up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
pm_qos_add_request(&up->pm_qos_request,
PM_QOS_CPU_DMA_LATENCY, up->latency);
- serial_omap_uart_wq = create_singlethread_workqueue(up->name);
INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);
platform_set_drvdata(pdev, up);
- pm_runtime_enable(&pdev->dev);
if (omap_up_info->autosuspend_timeout == 0)
omap_up_info->autosuspend_timeout = -1;
+
device_init_wakeup(up->dev, true);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev,
omap_up_info->autosuspend_timeout);
pm_runtime_irq_safe(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
pm_runtime_get_sync(&pdev->dev);
omap_serial_fill_features_erratas(up);
@@ -1528,7 +1731,7 @@ static int serial_omap_probe(struct platform_device *pdev)
err_add_port:
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-err_ioremap:
+err_rs485:
err_port_line:
dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
pdev->id, __func__, ret);
@@ -1543,6 +1746,7 @@ static int serial_omap_remove(struct platform_device *dev)
pm_runtime_disable(up->dev);
uart_remove_one_port(&serial_omap_reg, &up->port);
pm_qos_remove_request(&up->pm_qos_request);
+ device_init_wakeup(&dev->dev, false);
return 0;
}
@@ -1609,6 +1813,7 @@ static void serial_omap_restore_context(struct uart_omap_port *up)
serial_omap_mdr1_errataset(up, up->mdr1);
else
serial_out(up, UART_OMAP_MDR1, up->mdr1);
+ serial_out(up, UART_OMAP_WER, up->wer);
}
static int serial_omap_runtime_suspend(struct device *dev)
@@ -1630,17 +1835,7 @@ static int serial_omap_runtime_suspend(struct device *dev)
up->context_loss_cnt = serial_omap_get_context_loss_count(up);
- if (device_may_wakeup(dev)) {
- if (!up->wakeups_enabled) {
- serial_omap_enable_wakeup(up, true);
- up->wakeups_enabled = true;
- }
- } else {
- if (up->wakeups_enabled) {
- serial_omap_enable_wakeup(up, false);
- up->wakeups_enabled = false;
- }
- }
+ serial_omap_enable_wakeup(up, true);
up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
schedule_work(&up->qos_work);
@@ -1654,6 +1849,8 @@ static int serial_omap_runtime_resume(struct device *dev)
int loss_cnt = serial_omap_get_context_loss_count(up);
+ serial_omap_enable_wakeup(up, false);
+
if (loss_cnt < 0) {
dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n",
loss_cnt);
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 572d48189de..0cb6a8e52bd 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -232,7 +232,7 @@ struct eg20t_port {
unsigned int iobase;
struct pci_dev *pdev;
int fifo_size;
- int uartclk;
+ unsigned int uartclk;
int start_tx;
int start_rx;
int tx_empty;
@@ -257,6 +257,8 @@ struct eg20t_port {
dma_addr_t rx_buf_dma;
struct dentry *debugfs;
+#define IRQ_NAME_SIZE 17
+ char irq_name[IRQ_NAME_SIZE];
/* protect the eg20t_port private structure and io access to membase */
spinlock_t lock;
@@ -373,35 +375,62 @@ static const struct file_operations port_regs_ops = {
};
#endif /* CONFIG_DEBUG_FS */
+static struct dmi_system_id pch_uart_dmi_table[] = {
+ {
+ .ident = "CM-iTC",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "CM-iTC"),
+ },
+ (void *)CMITC_UARTCLK,
+ },
+ {
+ .ident = "FRI2",
+ {
+ DMI_MATCH(DMI_BIOS_VERSION, "FRI2"),
+ },
+ (void *)FRI2_64_UARTCLK,
+ },
+ {
+ .ident = "Fish River Island II",
+ {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"),
+ },
+ (void *)FRI2_48_UARTCLK,
+ },
+ {
+ .ident = "COMe-mTT",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"),
+ },
+ (void *)NTC1_UARTCLK,
+ },
+ {
+ .ident = "nanoETXexpress-TT",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"),
+ },
+ (void *)NTC1_UARTCLK,
+ },
+ {
+ .ident = "MinnowBoard",
+ {
+ DMI_MATCH(DMI_BOARD_NAME, "MinnowBoard"),
+ },
+ (void *)MINNOW_UARTCLK,
+ },
+};
+
/* Return UART clock, checking for board specific clocks. */
-static int pch_uart_get_uartclk(void)
+static unsigned int pch_uart_get_uartclk(void)
{
- const char *cmp;
+ const struct dmi_system_id *d;
if (user_uartclk)
return user_uartclk;
- cmp = dmi_get_system_info(DMI_BOARD_NAME);
- if (cmp && strstr(cmp, "CM-iTC"))
- return CMITC_UARTCLK;
-
- cmp = dmi_get_system_info(DMI_BIOS_VERSION);
- if (cmp && strnstr(cmp, "FRI2", 4))
- return FRI2_64_UARTCLK;
-
- cmp = dmi_get_system_info(DMI_PRODUCT_NAME);
- if (cmp && strstr(cmp, "Fish River Island II"))
- return FRI2_48_UARTCLK;
-
- /* Kontron COMe-mTT10 (nanoETXexpress-TT) */
- cmp = dmi_get_system_info(DMI_BOARD_NAME);
- if (cmp && (strstr(cmp, "COMe-mTT") ||
- strstr(cmp, "nanoETXexpress-TT")))
- return NTC1_UARTCLK;
-
- cmp = dmi_get_system_info(DMI_BOARD_NAME);
- if (cmp && strstr(cmp, "MinnowBoard"))
- return MINNOW_UARTCLK;
+ d = dmi_first_match(pch_uart_dmi_table);
+ if (d)
+ return (unsigned long)d->driver_data;
return DEFAULT_UARTCLK;
}
@@ -422,7 +451,7 @@ static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv,
iowrite8(ier, priv->membase + UART_IER);
}
-static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
+static int pch_uart_hal_set_line(struct eg20t_port *priv, unsigned int baud,
unsigned int parity, unsigned int bits,
unsigned int stb)
{
@@ -457,7 +486,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
lcr |= bits;
lcr |= stb;
- dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n",
+ dev_dbg(priv->port.dev, "%s:baud = %u, div = %04x, lcr = %02x (%lu)\n",
__func__, baud, div, lcr, jiffies);
iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
iowrite8(dll, priv->membase + PCH_UART_DLL);
@@ -640,30 +669,21 @@ static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf)
static int dma_push_rx(struct eg20t_port *priv, int size)
{
- struct tty_struct *tty;
int room;
struct uart_port *port = &priv->port;
struct tty_port *tport = &port->state->port;
- port = &priv->port;
- tty = tty_port_tty_get(tport);
- if (!tty) {
- dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
- return 0;
- }
-
room = tty_buffer_request_room(tport, size);
if (room < size)
dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
size - room);
if (!room)
- return room;
+ return 0;
tty_insert_flip_string(tport, sg_virt(&priv->sg_rx), size);
port->icount.rx += room;
- tty_kref_put(tty);
return room;
}
@@ -1071,6 +1091,8 @@ static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr)
if (tty == NULL) {
for (i = 0; error_msg[i] != NULL; i++)
dev_err(&priv->pdev->dev, error_msg[i]);
+ } else {
+ tty_kref_put(tty);
}
}
@@ -1323,7 +1345,7 @@ static int pch_uart_startup(struct uart_port *port)
return ret;
ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, priv);
+ priv->irq_name, priv);
if (ret < 0)
return ret;
@@ -1363,9 +1385,8 @@ static void pch_uart_shutdown(struct uart_port *port)
static void pch_uart_set_termios(struct uart_port *port,
struct ktermios *termios, struct ktermios *old)
{
- int baud;
int rtn;
- unsigned int parity, bits, stb;
+ unsigned int baud, parity, bits, stb;
struct eg20t_port *priv;
unsigned long flags;
@@ -1489,15 +1510,20 @@ static int pch_uart_verify_port(struct uart_port *port,
__func__);
return -EOPNOTSUPP;
#endif
- dev_info(priv->port.dev, "PCH UART : Use DMA Mode\n");
- if (!priv->use_dma)
+ if (!priv->use_dma) {
pch_request_dma(port);
- priv->use_dma = 1;
+ if (priv->chan_rx)
+ priv->use_dma = 1;
+ }
+ dev_info(priv->port.dev, "PCH UART: %s\n",
+ priv->use_dma ?
+ "Use DMA Mode" : "No DMA");
}
return 0;
}
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_PCH_UART_CONSOLE)
/*
* Wait for transmitter & holding register to empty
*/
@@ -1528,6 +1554,7 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits)
}
}
}
+#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_PCH_UART_CONSOLE */
#ifdef CONFIG_CONSOLE_POLL
/*
@@ -1563,13 +1590,8 @@ static void pch_uart_put_poll_char(struct uart_port *port,
wait_for_xmitr(priv, UART_LSR_THRE);
/*
* Send the character out.
- * If a LF, also do CR...
*/
iowrite8(c, priv->membase + PCH_UART_THR);
- if (c == 10) {
- wait_for_xmitr(priv, UART_LSR_THRE);
- iowrite8(13, priv->membase + PCH_UART_THR);
- }
/*
* Finally, wait for transmitter to become empty
@@ -1593,7 +1615,6 @@ static struct uart_ops pch_uart_ops = {
.shutdown = pch_uart_shutdown,
.set_termios = pch_uart_set_termios,
/* .pm = pch_uart_pm, Not supported yet */
-/* .set_wake = pch_uart_set_wake, Not supported yet */
.type = pch_uart_type,
.release_port = pch_uart_release_port,
.request_port = pch_uart_request_port,
@@ -1738,7 +1759,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
int fifosize;
int port_type;
struct pch_uart_driver_data *board;
+#ifdef CONFIG_DEBUG_FS
char name[32]; /* for debugfs file name */
+#endif
board = &drv_dat[id->driver_data];
port_type = board->port_type;
@@ -1792,6 +1815,10 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
priv->port.line = board->line_no;
priv->trigger = PCH_UART_HAL_TRIGGER_M;
+ snprintf(priv->irq_name, IRQ_NAME_SIZE,
+ KBUILD_MODNAME ":" PCH_UART_DRIVER_DEVICE "%d",
+ priv->port.line);
+
spin_lock_init(&priv->port.lock);
pci_set_drvdata(pdev, priv);
@@ -1833,7 +1860,6 @@ static void pch_uart_exit_port(struct eg20t_port *priv)
debugfs_remove(priv->debugfs);
#endif
uart_remove_one_port(&pch_uart_driver, &priv->port);
- pci_set_drvdata(priv->pdev, NULL);
free_page((unsigned long)priv->rxbuf.buf);
}
@@ -1887,7 +1913,7 @@ static int pch_uart_pci_resume(struct pci_dev *pdev)
#define pch_uart_pci_resume NULL
#endif
-static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = {
+static const struct pci_device_id pch_uart_pci_id[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811),
.driver_data = pch_et20t_uart0},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812),
@@ -1975,6 +2001,8 @@ module_exit(pch_uart_module_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver");
+MODULE_DEVICE_TABLE(pci, pch_uart_pci_id);
+
module_param(default_baud, uint, S_IRUGO);
MODULE_PARM_DESC(default_baud,
"Default BAUD for initial driver state and console (default 9600)");
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index b1785f58b6e..f7ad5b90305 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -57,6 +57,8 @@
#include <linux/bitops.h>
#include <linux/sysrq.h>
#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -651,6 +653,8 @@ static void pmz_start_tx(struct uart_port *port)
} else {
struct circ_buf *xmit = &port->state->xmit;
+ if (uart_circ_empty(xmit))
+ goto out;
write_zsdata(uap, xmit->buf[xmit->tail]);
zssync(uap);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -659,6 +663,7 @@ static void pmz_start_tx(struct uart_port *port)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
}
+ out:
pmz_debug("pmz: start_tx() done.\n");
}
@@ -1072,7 +1077,7 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag,
uap->curregs[5] |= Tx8;
uap->parity_mask = 0xff;
break;
- };
+ }
uap->curregs[4] &= ~(SB_MASK);
if (cflag & CSTOPB)
uap->curregs[4] |= SB2;
@@ -1090,7 +1095,7 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag,
uap->port.read_status_mask = Rx_OVR;
if (iflag & INPCK)
uap->port.read_status_mask |= CRC_ERR | PAR_ERR;
- if (iflag & (BRKINT | PARMRK))
+ if (iflag & (IGNBRK | BRKINT | PARMRK))
uap->port.read_status_mask |= BRK_ABRT;
uap->port.ignore_status_mask = 0;
@@ -1798,7 +1803,6 @@ static int __exit pmz_detach(struct platform_device *pdev)
uart_remove_one_port(&pmz_uart_reg, &uap->port);
- platform_set_drvdata(pdev, NULL);
uap->port.dev = NULL;
return 0;
@@ -2051,6 +2055,9 @@ static int __init pmz_console_init(void)
/* Probe ports */
pmz_probe();
+ if (pmz_ports_count == 0)
+ return -ENODEV;
+
/* TODO: Autoprobe console based on OF */
/* pmz_console.index = i; */
register_console(&pmz_console);
diff --git a/drivers/tty/serial/pnx8xxx_uart.c b/drivers/tty/serial/pnx8xxx_uart.c
index 7e277a5384a..2ba24a45c97 100644
--- a/drivers/tty/serial/pnx8xxx_uart.c
+++ b/drivers/tty/serial/pnx8xxx_uart.c
@@ -237,7 +237,10 @@ static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport)
status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT));
}
+
+ spin_unlock(&sport->port.lock);
tty_flip_buffer_push(&sport->port.state->port);
+ spin_lock(&sport->port.lock);
}
static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport)
@@ -474,7 +477,7 @@ pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios,
sport->port.read_status_mask |=
FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) |
FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR);
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
sport->port.read_status_mask |=
ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK);
@@ -801,8 +804,6 @@ static int pnx8xxx_serial_remove(struct platform_device *pdev)
{
struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (sport)
uart_remove_one_port(&pnx8xxx_reg, &sport->port);
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index 05f504e0c27..c638c53cd2b 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -332,31 +332,6 @@ static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore(&up->port.lock, flags);
}
-#if 0
-static void serial_pxa_dma_init(struct pxa_uart *up)
-{
- up->rxdma =
- pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up);
- if (up->rxdma < 0)
- goto out;
- up->txdma =
- pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up);
- if (up->txdma < 0)
- goto err_txdma;
- up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL);
- if (!up->dmadesc)
- goto err_alloc;
-
- /* ... */
-err_alloc:
- pxa_free_dma(up->txdma);
-err_rxdma:
- pxa_free_dma(up->rxdma);
-out:
- return;
-}
-#endif
-
static int serial_pxa_startup(struct uart_port *port)
{
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
@@ -517,7 +492,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (termios->c_iflag & INPCK)
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= UART_LSR_BI;
/*
@@ -736,13 +711,8 @@ static void serial_pxa_put_poll_char(struct uart_port *port,
wait_for_xmitr(up);
/*
* Send the character out.
- * If a LF, also do CR...
*/
serial_out(up, UART_TX, c);
- if (c == 10) {
- wait_for_xmitr(up);
- serial_out(up, UART_TX, 13);
- }
/*
* Finally, wait for transmitter to become empty
@@ -790,7 +760,7 @@ static struct console serial_pxa_console = {
#define PXA_CONSOLE NULL
#endif
-struct uart_ops serial_pxa_pops = {
+static struct uart_ops serial_pxa_pops = {
.tx_empty = serial_pxa_tx_empty,
.set_mctrl = serial_pxa_set_mctrl,
.get_mctrl = serial_pxa_get_mctrl,
@@ -945,8 +915,6 @@ static int serial_pxa_remove(struct platform_device *dev)
{
struct uart_pxa_port *sport = platform_get_drvdata(dev);
- platform_set_drvdata(dev, NULL);
-
uart_remove_one_port(&serial_pxa_reg, &sport->port);
clk_unprepare(sport->clk);
@@ -970,7 +938,7 @@ static struct platform_driver serial_pxa_driver = {
},
};
-int __init serial_pxa_init(void)
+static int __init serial_pxa_init(void)
{
int ret;
@@ -985,7 +953,7 @@ int __init serial_pxa_init(void)
return ret;
}
-void __exit serial_pxa_exit(void)
+static void __exit serial_pxa_exit(void)
{
platform_driver_unregister(&serial_pxa_driver);
uart_unregister_driver(&serial_pxa_reg);
diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c
index a314a943f12..056f91b3a4c 100644
--- a/drivers/tty/serial/rp2.c
+++ b/drivers/tty/serial/rp2.c
@@ -427,7 +427,9 @@ static void rp2_rx_chars(struct rp2_uart_port *up)
up->port.icount.rx++;
}
+ spin_unlock(&up->port.lock);
tty_flip_buffer_push(port);
+ spin_lock(&up->port.lock);
}
static void rp2_tx_chars(struct rp2_uart_port *up)
@@ -808,7 +810,7 @@ static void rp2_remove(struct pci_dev *pdev)
rp2_remove_ports(card);
}
-static DEFINE_PCI_DEVICE_TABLE(rp2_pci_tbl) = {
+static const struct pci_device_id rp2_pci_tbl[] = {
/* RocketPort INFINITY cards */
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c
index af6b3e3ad24..753d4525b36 100644
--- a/drivers/tty/serial/sa1100.c
+++ b/drivers/tty/serial/sa1100.c
@@ -232,7 +232,10 @@ sa1100_rx_chars(struct sa1100_port *sport)
status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
UTSR0_TO_SM(UART_GET_UTSR0(sport));
}
+
+ spin_unlock(&sport->port.lock);
tty_flip_buffer_push(&sport->port.state->port);
+ spin_lock(&sport->port.lock);
}
static void sa1100_tx_chars(struct sa1100_port *sport)
@@ -644,7 +647,10 @@ void sa1100_register_uart_fns(struct sa1100_port_fns *fns)
sa1100_pops.set_mctrl = fns->set_mctrl;
sa1100_pops.pm = fns->pm;
- sa1100_pops.set_wake = fns->set_wake;
+ /*
+ * FIXME: fns->set_wake is unused - this should be called from
+ * the suspend() callback if device_may_wakeup(dev)) is set.
+ */
}
void __init sa1100_register_uart(int idx, int port)
@@ -864,8 +870,6 @@ static int sa1100_serial_remove(struct platform_device *pdev)
{
struct sa1100_port *sport = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (sport)
uart_remove_one_port(&sa1100_reg, &sport->port);
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 376079b9bd7..c1d3ebdf3b9 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -53,6 +53,29 @@
#include "samsung.h"
+#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \
+ defined(CONFIG_DEBUG_LL) && \
+ !defined(MODULE)
+
+extern void printascii(const char *);
+
+__printf(1, 2)
+static void dbg(const char *fmt, ...)
+{
+ va_list va;
+ char buff[256];
+
+ va_start(va, fmt);
+ vscnprintf(buff, sizeof(buff), fmt, va);
+ va_end(va);
+
+ printascii(buff);
+}
+
+#else
+#define dbg(fmt, ...) do { if (0) no_printk(fmt, ##__VA_ARGS__); } while (0)
+#endif
+
/* UART name and device definitions */
#define S3C24XX_SERIAL_NAME "ttySAC"
@@ -249,6 +272,8 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
ufcon |= S3C2410_UFCON_RESETRX;
wr_regl(port, S3C2410_UFCON, ufcon);
rx_enabled(port) = 1;
+ spin_unlock_irqrestore(&port->lock,
+ flags);
goto out;
}
continue;
@@ -297,10 +322,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
ignore_char:
continue;
}
+
+ spin_unlock_irqrestore(&port->lock, flags);
tty_flip_buffer_push(&port->state->port);
out:
- spin_unlock_irqrestore(&port->lock, flags);
return IRQ_HANDLED;
}
@@ -404,7 +430,14 @@ static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- /* todo - possibly remove AFC and do manual CTS */
+ unsigned int umcon = rd_regl(port, S3C2410_UMCON);
+
+ if (mctrl & TIOCM_RTS)
+ umcon |= S3C2410_UMCOM_RTS_LOW;
+ else
+ umcon &= ~S3C2410_UMCOM_RTS_LOW;
+
+ wr_regl(port, S3C2410_UMCON, umcon);
}
static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
@@ -458,8 +491,8 @@ static int s3c24xx_serial_startup(struct uart_port *port)
struct s3c24xx_uart_port *ourport = to_ourport(port);
int ret;
- dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
- port->mapbase, port->membase);
+ dbg("s3c24xx_serial_startup: port=%p (%08llx,%p)\n",
+ port, (unsigned long long)port->mapbase, port->membase);
rx_enabled(port) = 1;
@@ -504,8 +537,8 @@ static int s3c64xx_serial_startup(struct uart_port *port)
struct s3c24xx_uart_port *ourport = to_ourport(port);
int ret;
- dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n",
- port->mapbase, port->membase);
+ dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n",
+ port, (unsigned long long)port->mapbase, port->membase);
wr_regl(port, S3C64XX_UINTM, 0xf);
@@ -771,8 +804,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
if (termios->c_cflag & CSTOPB)
ulcon |= S3C2410_LCON_STOPB;
- umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
-
if (termios->c_cflag & PARENB) {
if (termios->c_cflag & PARODD)
ulcon |= S3C2410_LCON_PODD;
@@ -789,6 +820,15 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
wr_regl(port, S3C2410_ULCON, ulcon);
wr_regl(port, S3C2410_UBRDIV, quot);
+
+ umcon = rd_regl(port, S3C2410_UMCON);
+ if (termios->c_cflag & CRTSCTS) {
+ umcon |= S3C2410_UMCOM_AFC;
+ /* Disable RTS when RX FIFO contains 63 bytes */
+ umcon &= ~S3C2412_UMCON_AFC_8;
+ } else {
+ umcon &= ~S3C2410_UMCOM_AFC;
+ }
wr_regl(port, S3C2410_UMCON, umcon);
if (ourport->info->has_divslot)
@@ -1143,7 +1183,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
return -EINVAL;
}
- dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
+ dbg("resource %pR)\n", res);
port->membase = devm_ioremap(port->dev, res->start, resource_size(res));
if (!port->membase) {
@@ -1186,13 +1226,12 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
wr_regl(port, S3C64XX_UINTSP, 0xf);
}
- dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
+ dbg("port: map=%08x, mem=%p, irq=%d (%d,%d), clock=%u\n",
port->mapbase, port->membase, port->irq,
ourport->rx_irq, ourport->tx_irq, port->uartclk);
/* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport(port, cfg);
- clk_disable_unprepare(ourport->clk);
return 0;
}
@@ -1250,8 +1289,8 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
ourport->baudclk = ERR_PTR(-EINVAL);
ourport->info = ourport->drv_data->info;
- ourport->cfg = (pdev->dev.platform_data) ?
- (struct s3c2410_uartcfg *)pdev->dev.platform_data :
+ ourport->cfg = (dev_get_platdata(&pdev->dev)) ?
+ dev_get_platdata(&pdev->dev) :
ourport->drv_data->def_cfg;
ourport->port.fifosize = (ourport->info->fifosize) ?
@@ -1266,10 +1305,25 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
if (ret < 0)
goto probe_err;
+ if (!s3c24xx_uart_drv.state) {
+ ret = uart_register_driver(&s3c24xx_uart_drv);
+ if (ret < 0) {
+ pr_err("Failed to register Samsung UART driver\n");
+ return ret;
+ }
+ }
+
dbg("%s: adding port\n", __func__);
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
platform_set_drvdata(pdev, &ourport->port);
+ /*
+ * Deactivate the clock enabled in s3c24xx_serial_init_port here,
+ * so that a potential re-enablement through the pm-callback overlaps
+ * and keeps the clock enabled in this case.
+ */
+ clk_disable_unprepare(ourport->clk);
+
#ifdef CONFIG_SAMSUNG_CLOCK
ret = device_create_file(&pdev->dev, &dev_attr_clock_source);
if (ret < 0)
@@ -1298,6 +1352,8 @@ static int s3c24xx_serial_remove(struct platform_device *dev)
uart_remove_one_port(&s3c24xx_uart_drv, port);
}
+ uart_unregister_driver(&s3c24xx_uart_drv);
+
return 0;
}
@@ -1413,8 +1469,8 @@ static int s3c24xx_serial_get_poll_char(struct uart_port *port)
static void s3c24xx_serial_put_poll_char(struct uart_port *port,
unsigned char c)
{
- unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
- unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
+ unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
+ unsigned int ucon = rd_regl(port, S3C2410_UCON);
/* not possible to xmit on unconfigured port */
if (!s3c24xx_port_configured(ucon))
@@ -1422,7 +1478,7 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port,
while (!s3c24xx_serial_console_txrdy(port, ufcon))
cpu_relax();
- wr_regb(cons_uart, S3C2410_UTXH, c);
+ wr_regb(port, S3C2410_UTXH, c);
}
#endif /* CONFIG_CONSOLE_POLL */
@@ -1430,22 +1486,23 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port,
static void
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
{
- unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
- unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
-
- /* not possible to xmit on unconfigured port */
- if (!s3c24xx_port_configured(ucon))
- return;
+ unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
while (!s3c24xx_serial_console_txrdy(port, ufcon))
- barrier();
- wr_regb(cons_uart, S3C2410_UTXH, ch);
+ cpu_relax();
+ wr_regb(port, S3C2410_UTXH, ch);
}
static void
s3c24xx_serial_console_write(struct console *co, const char *s,
unsigned int count)
{
+ unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
+
+ /* not possible to xmit on unconfigured port */
+ if (!s3c24xx_port_configured(ucon))
+ return;
+
uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
}
@@ -1797,35 +1854,7 @@ static struct platform_driver samsung_serial_driver = {
},
};
-/* module initialisation code */
-
-static int __init s3c24xx_serial_modinit(void)
-{
- int ret;
-
- ret = uart_register_driver(&s3c24xx_uart_drv);
- if (ret < 0) {
- pr_err("Failed to register Samsung UART driver\n");
- return ret;
- }
-
- ret = platform_driver_register(&samsung_serial_driver);
- if (ret < 0) {
- pr_err("Failed to register platform driver\n");
- uart_unregister_driver(&s3c24xx_uart_drv);
- }
-
- return ret;
-}
-
-static void __exit s3c24xx_serial_modexit(void)
-{
- platform_driver_unregister(&samsung_serial_driver);
- uart_unregister_driver(&s3c24xx_uart_drv);
-}
-
-module_init(s3c24xx_serial_modinit);
-module_exit(s3c24xx_serial_modexit);
+module_platform_driver(samsung_serial_driver);
MODULE_ALIAS("platform:samsung-uart");
MODULE_DESCRIPTION("Samsung SoC Serial port driver");
diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h
index 00a499ecd38..eb071dd19b2 100644
--- a/drivers/tty/serial/samsung.h
+++ b/drivers/tty/serial/samsung.h
@@ -1,3 +1,6 @@
+#ifndef __SAMSUNG_H
+#define __SAMSUNG_H
+
/*
* Driver for Samsung SoC onboard UARTs.
*
@@ -63,12 +66,13 @@ struct s3c24xx_uart_port {
/* conversion functions */
-#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
+#define s3c24xx_dev_to_port(__dev) dev_get_drvdata(__dev)
/* register access controls */
#define portaddr(port, reg) ((port)->membase + (reg))
-#define portaddrl(port, reg) ((unsigned long *)((port)->membase + (reg)))
+#define portaddrl(port, reg) \
+ ((unsigned long *)(unsigned long)((port)->membase + (reg)))
#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
@@ -76,24 +80,4 @@ struct s3c24xx_uart_port {
#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))
-#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \
- defined(CONFIG_DEBUG_LL) && \
- !defined(MODULE)
-
-extern void printascii(const char *);
-
-static void dbg(const char *fmt, ...)
-{
- va_list va;
- char buff[256];
-
- va_start(va, fmt);
- vsprintf(buff, fmt, va);
- va_end(va);
-
- printascii(buff);
-}
-
-#else
-#define dbg(x...) do { } while (0)
#endif
diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
index a7cdec2962d..771f361c47e 100644
--- a/drivers/tty/serial/sb1250-duart.c
+++ b/drivers/tty/serial/sb1250-duart.c
@@ -596,7 +596,7 @@ static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios,
if (termios->c_iflag & INPCK)
uport->read_status_mask |= M_DUART_FRM_ERR |
M_DUART_PARITY_ERR;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
uport->read_status_mask |= M_DUART_RCVD_BRK;
uport->ignore_status_mask = 0;
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
new file mode 100644
index 00000000000..1b6a77c4b2c
--- /dev/null
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -0,0 +1,1277 @@
+/*
+ * SC16IS7xx tty serial driver - Copyright (C) 2014 GridPoint
+ * Author: Jon Ringle <jringle@gridpoint.com>
+ *
+ * Based on max310x.c, by Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.h>
+
+#define SC16IS7XX_NAME "sc16is7xx"
+
+/* SC16IS7XX register definitions */
+#define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */
+#define SC16IS7XX_THR_REG (0x00) /* TX FIFO */
+#define SC16IS7XX_IER_REG (0x01) /* Interrupt enable */
+#define SC16IS7XX_IIR_REG (0x02) /* Interrupt Identification */
+#define SC16IS7XX_FCR_REG (0x02) /* FIFO control */
+#define SC16IS7XX_LCR_REG (0x03) /* Line Control */
+#define SC16IS7XX_MCR_REG (0x04) /* Modem Control */
+#define SC16IS7XX_LSR_REG (0x05) /* Line Status */
+#define SC16IS7XX_MSR_REG (0x06) /* Modem Status */
+#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */
+#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */
+#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */
+#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_IOSTATE_REG (0x0b) /* I/O State
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_IOINTENA_REG (0x0c) /* I/O Interrupt Enable
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_EFCR_REG (0x0f) /* Extra Features Control */
+
+/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */
+#define SC16IS7XX_TCR_REG (0x06) /* Transmit control */
+#define SC16IS7XX_TLR_REG (0x07) /* Trigger level */
+
+/* Special Register set: Only if ((LCR[7] == 1) && (LCR != 0xBF)) */
+#define SC16IS7XX_DLL_REG (0x00) /* Divisor Latch Low */
+#define SC16IS7XX_DLH_REG (0x01) /* Divisor Latch High */
+
+/* Enhanced Register set: Only if (LCR == 0xBF) */
+#define SC16IS7XX_EFR_REG (0x02) /* Enhanced Features */
+#define SC16IS7XX_XON1_REG (0x04) /* Xon1 word */
+#define SC16IS7XX_XON2_REG (0x05) /* Xon2 word */
+#define SC16IS7XX_XOFF1_REG (0x06) /* Xoff1 word */
+#define SC16IS7XX_XOFF2_REG (0x07) /* Xoff2 word */
+
+/* IER register bits */
+#define SC16IS7XX_IER_RDI_BIT (1 << 0) /* Enable RX data interrupt */
+#define SC16IS7XX_IER_THRI_BIT (1 << 1) /* Enable TX holding register
+ * interrupt */
+#define SC16IS7XX_IER_RLSI_BIT (1 << 2) /* Enable RX line status
+ * interrupt */
+#define SC16IS7XX_IER_MSI_BIT (1 << 3) /* Enable Modem status
+ * interrupt */
+
+/* IER register bits - write only if (EFR[4] == 1) */
+#define SC16IS7XX_IER_SLEEP_BIT (1 << 4) /* Enable Sleep mode */
+#define SC16IS7XX_IER_XOFFI_BIT (1 << 5) /* Enable Xoff interrupt */
+#define SC16IS7XX_IER_RTSI_BIT (1 << 6) /* Enable nRTS interrupt */
+#define SC16IS7XX_IER_CTSI_BIT (1 << 7) /* Enable nCTS interrupt */
+
+/* FCR register bits */
+#define SC16IS7XX_FCR_FIFO_BIT (1 << 0) /* Enable FIFO */
+#define SC16IS7XX_FCR_RXRESET_BIT (1 << 1) /* Reset RX FIFO */
+#define SC16IS7XX_FCR_TXRESET_BIT (1 << 2) /* Reset TX FIFO */
+#define SC16IS7XX_FCR_RXLVLL_BIT (1 << 6) /* RX Trigger level LSB */
+#define SC16IS7XX_FCR_RXLVLH_BIT (1 << 7) /* RX Trigger level MSB */
+
+/* FCR register bits - write only if (EFR[4] == 1) */
+#define SC16IS7XX_FCR_TXLVLL_BIT (1 << 4) /* TX Trigger level LSB */
+#define SC16IS7XX_FCR_TXLVLH_BIT (1 << 5) /* TX Trigger level MSB */
+
+/* IIR register bits */
+#define SC16IS7XX_IIR_NO_INT_BIT (1 << 0) /* No interrupts pending */
+#define SC16IS7XX_IIR_ID_MASK 0x3e /* Mask for the interrupt ID */
+#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */
+#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */
+#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */
+#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */
+#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */
+#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state
+ * from active (LOW)
+ * to inactive (HIGH)
+ */
+/* LCR register bits */
+#define SC16IS7XX_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */
+#define SC16IS7XX_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1
+ *
+ * Word length bits table:
+ * 00 -> 5 bit words
+ * 01 -> 6 bit words
+ * 10 -> 7 bit words
+ * 11 -> 8 bit words
+ */
+#define SC16IS7XX_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit
+ *
+ * STOP length bit table:
+ * 0 -> 1 stop bit
+ * 1 -> 1-1.5 stop bits if
+ * word length is 5,
+ * 2 stop bits otherwise
+ */
+#define SC16IS7XX_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */
+#define SC16IS7XX_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */
+#define SC16IS7XX_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */
+#define SC16IS7XX_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */
+#define SC16IS7XX_LCR_DLAB_BIT (1 << 7) /* Divisor Latch enable */
+#define SC16IS7XX_LCR_WORD_LEN_5 (0x00)
+#define SC16IS7XX_LCR_WORD_LEN_6 (0x01)
+#define SC16IS7XX_LCR_WORD_LEN_7 (0x02)
+#define SC16IS7XX_LCR_WORD_LEN_8 (0x03)
+#define SC16IS7XX_LCR_CONF_MODE_A SC16IS7XX_LCR_DLAB_BIT /* Special
+ * reg set */
+#define SC16IS7XX_LCR_CONF_MODE_B 0xBF /* Enhanced
+ * reg set */
+
+/* MCR register bits */
+#define SC16IS7XX_MCR_DTR_BIT (1 << 0) /* DTR complement
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_MCR_RTS_BIT (1 << 1) /* RTS complement */
+#define SC16IS7XX_MCR_TCRTLR_BIT (1 << 2) /* TCR/TLR register enable */
+#define SC16IS7XX_MCR_LOOP_BIT (1 << 4) /* Enable loopback test mode */
+#define SC16IS7XX_MCR_XONANY_BIT (1 << 5) /* Enable Xon Any
+ * - write enabled
+ * if (EFR[4] == 1)
+ */
+#define SC16IS7XX_MCR_IRDA_BIT (1 << 6) /* Enable IrDA mode
+ * - write enabled
+ * if (EFR[4] == 1)
+ */
+#define SC16IS7XX_MCR_CLKSEL_BIT (1 << 7) /* Divide clock by 4
+ * - write enabled
+ * if (EFR[4] == 1)
+ */
+
+/* LSR register bits */
+#define SC16IS7XX_LSR_DR_BIT (1 << 0) /* Receiver data ready */
+#define SC16IS7XX_LSR_OE_BIT (1 << 1) /* Overrun Error */
+#define SC16IS7XX_LSR_PE_BIT (1 << 2) /* Parity Error */
+#define SC16IS7XX_LSR_FE_BIT (1 << 3) /* Frame Error */
+#define SC16IS7XX_LSR_BI_BIT (1 << 4) /* Break Interrupt */
+#define SC16IS7XX_LSR_BRK_ERROR_MASK 0x1E /* BI, FE, PE, OE bits */
+#define SC16IS7XX_LSR_THRE_BIT (1 << 5) /* TX holding register empty */
+#define SC16IS7XX_LSR_TEMT_BIT (1 << 6) /* Transmitter empty */
+#define SC16IS7XX_LSR_FIFOE_BIT (1 << 7) /* Fifo Error */
+
+/* MSR register bits */
+#define SC16IS7XX_MSR_DCTS_BIT (1 << 0) /* Delta CTS Clear To Send */
+#define SC16IS7XX_MSR_DDSR_BIT (1 << 1) /* Delta DSR Data Set Ready
+ * or (IO4)
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_MSR_DRI_BIT (1 << 2) /* Delta RI Ring Indicator
+ * or (IO7)
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_MSR_DCD_BIT (1 << 3) /* Delta CD Carrier Detect
+ * or (IO6)
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_MSR_CTS_BIT (1 << 0) /* CTS */
+#define SC16IS7XX_MSR_DSR_BIT (1 << 1) /* DSR (IO4)
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_MSR_RI_BIT (1 << 2) /* RI (IO7)
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_MSR_CD_BIT (1 << 3) /* CD (IO6)
+ * - only on 75x/76x
+ */
+#define SC16IS7XX_MSR_DELTA_MASK 0x0F /* Any of the delta bits! */
+
+/*
+ * TCR register bits
+ * TCR trigger levels are available from 0 to 60 characters with a granularity
+ * of four.
+ * The programmer must program the TCR such that TCR[3:0] > TCR[7:4]. There is
+ * no built-in hardware check to make sure this condition is met. Also, the TCR
+ * must be programmed with this condition before auto RTS or software flow
+ * control is enabled to avoid spurious operation of the device.
+ */
+#define SC16IS7XX_TCR_RX_HALT(words) ((((words) / 4) & 0x0f) << 0)
+#define SC16IS7XX_TCR_RX_RESUME(words) ((((words) / 4) & 0x0f) << 4)
+
+/*
+ * TLR register bits
+ * If TLR[3:0] or TLR[7:4] are logical 0, the selectable trigger levels via the
+ * FIFO Control Register (FCR) are used for the transmit and receive FIFO
+ * trigger levels. Trigger levels from 4 characters to 60 characters are
+ * available with a granularity of four.
+ *
+ * When the trigger level setting in TLR is zero, the SC16IS740/750/760 uses the
+ * trigger level setting defined in FCR. If TLR has non-zero trigger level value
+ * the trigger level defined in FCR is discarded. This applies to both transmit
+ * FIFO and receive FIFO trigger level setting.
+ *
+ * When TLR is used for RX trigger level control, FCR[7:6] should be left at the
+ * default state, that is, '00'.
+ */
+#define SC16IS7XX_TLR_TX_TRIGGER(words) ((((words) / 4) & 0x0f) << 0)
+#define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4)
+
+/* IOControl register bits (Only 750/760) */
+#define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0) /* Enable input latching */
+#define SC16IS7XX_IOCONTROL_GPIO_BIT (1 << 1) /* Enable GPIO[7:4] */
+#define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3) /* Software Reset */
+
+/* EFCR register bits */
+#define SC16IS7XX_EFCR_9BIT_MODE_BIT (1 << 0) /* Enable 9-bit or Multidrop
+ * mode (RS485) */
+#define SC16IS7XX_EFCR_RXDISABLE_BIT (1 << 1) /* Disable receiver */
+#define SC16IS7XX_EFCR_TXDISABLE_BIT (1 << 2) /* Disable transmitter */
+#define SC16IS7XX_EFCR_AUTO_RS485_BIT (1 << 4) /* Auto RS485 RTS direction */
+#define SC16IS7XX_EFCR_RTS_INVERT_BIT (1 << 5) /* RTS output inversion */
+#define SC16IS7XX_EFCR_IRDA_MODE_BIT (1 << 7) /* IrDA mode
+ * 0 = rate upto 115.2 kbit/s
+ * - Only 750/760
+ * 1 = rate upto 1.152 Mbit/s
+ * - Only 760
+ */
+
+/* EFR register bits */
+#define SC16IS7XX_EFR_AUTORTS_BIT (1 << 6) /* Auto RTS flow ctrl enable */
+#define SC16IS7XX_EFR_AUTOCTS_BIT (1 << 7) /* Auto CTS flow ctrl enable */
+#define SC16IS7XX_EFR_XOFF2_DETECT_BIT (1 << 5) /* Enable Xoff2 detection */
+#define SC16IS7XX_EFR_ENABLE_BIT (1 << 4) /* Enable enhanced functions
+ * and writing to IER[7:4],
+ * FCR[5:4], MCR[7:5]
+ */
+#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) /* SWFLOW bit 3 */
+#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) /* SWFLOW bit 2
+ *
+ * SWFLOW bits 3 & 2 table:
+ * 00 -> no transmitter flow
+ * control
+ * 01 -> transmitter generates
+ * XON2 and XOFF2
+ * 10 -> transmitter generates
+ * XON1 and XOFF1
+ * 11 -> transmitter generates
+ * XON1, XON2, XOFF1 and
+ * XOFF2
+ */
+#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) /* SWFLOW bit 2 */
+#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) /* SWFLOW bit 3
+ *
+ * SWFLOW bits 3 & 2 table:
+ * 00 -> no received flow
+ * control
+ * 01 -> receiver compares
+ * XON2 and XOFF2
+ * 10 -> receiver compares
+ * XON1 and XOFF1
+ * 11 -> receiver compares
+ * XON1, XON2, XOFF1 and
+ * XOFF2
+ */
+
+/* Misc definitions */
+#define SC16IS7XX_FIFO_SIZE (64)
+#define SC16IS7XX_REG_SHIFT 2
+
+struct sc16is7xx_devtype {
+ char name[10];
+ int nr_gpio;
+ int nr_uart;
+};
+
+struct sc16is7xx_one {
+ struct uart_port port;
+ struct work_struct tx_work;
+ struct work_struct md_work;
+
+ struct serial_rs485 rs485;
+};
+
+struct sc16is7xx_port {
+ struct uart_driver uart;
+ struct sc16is7xx_devtype *devtype;
+ struct regmap *regmap;
+ struct mutex mutex;
+ struct clk *clk;
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio;
+#endif
+ unsigned char buf[SC16IS7XX_FIFO_SIZE];
+ struct sc16is7xx_one p[0];
+};
+
+#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e)))
+
+static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ unsigned int val = 0;
+
+ regmap_read(s->regmap,
+ (reg << SC16IS7XX_REG_SHIFT) | port->line, &val);
+
+ return val;
+}
+
+static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+
+ regmap_write(s->regmap,
+ (reg << SC16IS7XX_REG_SHIFT) | port->line, val);
+}
+
+static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
+ u8 mask, u8 val)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+
+ regmap_update_bits(s->regmap,
+ (reg << SC16IS7XX_REG_SHIFT) | port->line,
+ mask, val);
+}
+
+
+static void sc16is7xx_power(struct uart_port *port, int on)
+{
+ sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
+ SC16IS7XX_IER_SLEEP_BIT,
+ on ? 0 : SC16IS7XX_IER_SLEEP_BIT);
+}
+
+static const struct sc16is7xx_devtype sc16is74x_devtype = {
+ .name = "SC16IS74X",
+ .nr_gpio = 0,
+ .nr_uart = 1,
+};
+
+static const struct sc16is7xx_devtype sc16is750_devtype = {
+ .name = "SC16IS750",
+ .nr_gpio = 8,
+ .nr_uart = 1,
+};
+
+static const struct sc16is7xx_devtype sc16is752_devtype = {
+ .name = "SC16IS752",
+ .nr_gpio = 8,
+ .nr_uart = 2,
+};
+
+static const struct sc16is7xx_devtype sc16is760_devtype = {
+ .name = "SC16IS760",
+ .nr_gpio = 8,
+ .nr_uart = 1,
+};
+
+static const struct sc16is7xx_devtype sc16is762_devtype = {
+ .name = "SC16IS762",
+ .nr_gpio = 8,
+ .nr_uart = 2,
+};
+
+static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg >> SC16IS7XX_REG_SHIFT) {
+ case SC16IS7XX_RHR_REG:
+ case SC16IS7XX_IIR_REG:
+ case SC16IS7XX_LSR_REG:
+ case SC16IS7XX_MSR_REG:
+ case SC16IS7XX_TXLVL_REG:
+ case SC16IS7XX_RXLVL_REG:
+ case SC16IS7XX_IOSTATE_REG:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool sc16is7xx_regmap_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg >> SC16IS7XX_REG_SHIFT) {
+ case SC16IS7XX_RHR_REG:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int sc16is7xx_set_baud(struct uart_port *port, int baud)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ u8 lcr;
+ u8 prescaler = 0;
+ unsigned long clk = port->uartclk, div = clk / 16 / baud;
+
+ if (div > 0xffff) {
+ prescaler = SC16IS7XX_MCR_CLKSEL_BIT;
+ div /= 4;
+ }
+
+ lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
+
+ /* Open the LCR divisors for configuration */
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
+ SC16IS7XX_LCR_CONF_MODE_B);
+
+ /* Enable enhanced features */
+ regcache_cache_bypass(s->regmap, true);
+ sc16is7xx_port_write(port, SC16IS7XX_EFR_REG,
+ SC16IS7XX_EFR_ENABLE_BIT);
+ regcache_cache_bypass(s->regmap, false);
+
+ /* Put LCR back to the normal mode */
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+
+ sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
+ SC16IS7XX_MCR_CLKSEL_BIT,
+ prescaler);
+
+ /* Open the LCR divisors for configuration */
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
+ SC16IS7XX_LCR_CONF_MODE_A);
+
+ /* Write the new divisor */
+ regcache_cache_bypass(s->regmap, true);
+ sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256);
+ sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256);
+ regcache_cache_bypass(s->regmap, false);
+
+ /* Put LCR back to the normal mode */
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+
+ return DIV_ROUND_CLOSEST(clk / 16, div);
+}
+
+static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
+ unsigned int iir)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ unsigned int lsr = 0, ch, flag, bytes_read, i;
+ bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false;
+
+ if (unlikely(rxlen >= sizeof(s->buf))) {
+ dev_warn_ratelimited(port->dev,
+ "Port %i: Possible RX FIFO overrun: %d\n",
+ port->line, rxlen);
+ port->icount.buf_overrun++;
+ /* Ensure sanity of RX level */
+ rxlen = sizeof(s->buf);
+ }
+
+ while (rxlen) {
+ /* Only read lsr if there are possible errors in FIFO */
+ if (read_lsr) {
+ lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
+ if (!(lsr & SC16IS7XX_LSR_FIFOE_BIT))
+ read_lsr = false; /* No errors left in FIFO */
+ } else
+ lsr = 0;
+
+ if (read_lsr) {
+ s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG);
+ bytes_read = 1;
+ } else {
+ regcache_cache_bypass(s->regmap, true);
+ regmap_raw_read(s->regmap, SC16IS7XX_RHR_REG,
+ s->buf, rxlen);
+ regcache_cache_bypass(s->regmap, false);
+ bytes_read = rxlen;
+ }
+
+ lsr &= SC16IS7XX_LSR_BRK_ERROR_MASK;
+
+ port->icount.rx++;
+ flag = TTY_NORMAL;
+
+ if (unlikely(lsr)) {
+ if (lsr & SC16IS7XX_LSR_BI_BIT) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ } else if (lsr & SC16IS7XX_LSR_PE_BIT)
+ port->icount.parity++;
+ else if (lsr & SC16IS7XX_LSR_FE_BIT)
+ port->icount.frame++;
+ else if (lsr & SC16IS7XX_LSR_OE_BIT)
+ port->icount.overrun++;
+
+ lsr &= port->read_status_mask;
+ if (lsr & SC16IS7XX_LSR_BI_BIT)
+ flag = TTY_BREAK;
+ else if (lsr & SC16IS7XX_LSR_PE_BIT)
+ flag = TTY_PARITY;
+ else if (lsr & SC16IS7XX_LSR_FE_BIT)
+ flag = TTY_FRAME;
+ else if (lsr & SC16IS7XX_LSR_OE_BIT)
+ flag = TTY_OVERRUN;
+ }
+
+ for (i = 0; i < bytes_read; ++i) {
+ ch = s->buf[i];
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+
+ if (lsr & port->ignore_status_mask)
+ continue;
+
+ uart_insert_char(port, lsr, SC16IS7XX_LSR_OE_BIT, ch,
+ flag);
+ }
+ rxlen -= bytes_read;
+ }
+
+ tty_flip_buffer_push(&port->state->port);
+}
+
+static void sc16is7xx_handle_tx(struct uart_port *port)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int txlen, to_send, i;
+
+ if (unlikely(port->x_char)) {
+ sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ return;
+
+ /* Get length of data pending in circular buffer */
+ to_send = uart_circ_chars_pending(xmit);
+ if (likely(to_send)) {
+ /* Limit to size of TX FIFO */
+ txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
+ to_send = (to_send > txlen) ? txlen : to_send;
+
+ /* Add data to send */
+ port->icount.tx += to_send;
+
+ /* Convert to linear buffer */
+ for (i = 0; i < to_send; ++i) {
+ s->buf[i] = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ }
+ regcache_cache_bypass(s->regmap, true);
+ regmap_raw_write(s->regmap, SC16IS7XX_THR_REG, s->buf, to_send);
+ regcache_cache_bypass(s->regmap, false);
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+}
+
+static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
+{
+ struct uart_port *port = &s->p[portno].port;
+
+ do {
+ unsigned int iir, msr, rxlen;
+
+ iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
+ if (iir & SC16IS7XX_IIR_NO_INT_BIT)
+ break;
+
+ iir &= SC16IS7XX_IIR_ID_MASK;
+
+ switch (iir) {
+ case SC16IS7XX_IIR_RDI_SRC:
+ case SC16IS7XX_IIR_RLSE_SRC:
+ case SC16IS7XX_IIR_RTOI_SRC:
+ case SC16IS7XX_IIR_XOFFI_SRC:
+ rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG);
+ if (rxlen)
+ sc16is7xx_handle_rx(port, rxlen, iir);
+ break;
+
+ case SC16IS7XX_IIR_CTSRTS_SRC:
+ msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG);
+ uart_handle_cts_change(port,
+ !!(msr & SC16IS7XX_MSR_CTS_BIT));
+ break;
+ case SC16IS7XX_IIR_THRI_SRC:
+ mutex_lock(&s->mutex);
+ sc16is7xx_handle_tx(port);
+ mutex_unlock(&s->mutex);
+ break;
+ default:
+ dev_err_ratelimited(port->dev,
+ "Port %i: Unexpected interrupt: %x",
+ port->line, iir);
+ break;
+ }
+ } while (1);
+}
+
+static irqreturn_t sc16is7xx_ist(int irq, void *dev_id)
+{
+ struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
+ int i;
+
+ for (i = 0; i < s->uart.nr; ++i)
+ sc16is7xx_port_irq(s, i);
+
+ return IRQ_HANDLED;
+}
+
+static void sc16is7xx_wq_proc(struct work_struct *ws)
+{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(ws, tx_work);
+ struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev);
+
+ mutex_lock(&s->mutex);
+ sc16is7xx_handle_tx(&one->port);
+ mutex_unlock(&s->mutex);
+}
+
+static void sc16is7xx_stop_tx(struct uart_port* port)
+{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+ struct circ_buf *xmit = &one->port.state->xmit;
+
+ /* handle rs485 */
+ if (one->rs485.flags & SER_RS485_ENABLED) {
+ /* do nothing if current tx not yet completed */
+ int lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
+ if (!(lsr & SC16IS7XX_LSR_TEMT_BIT))
+ return;
+
+ if (uart_circ_empty(xmit) &&
+ (one->rs485.delay_rts_after_send > 0))
+ mdelay(one->rs485.delay_rts_after_send);
+ }
+
+ sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
+ SC16IS7XX_IER_THRI_BIT,
+ 0);
+}
+
+static void sc16is7xx_stop_rx(struct uart_port* port)
+{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+
+ one->port.read_status_mask &= ~SC16IS7XX_LSR_DR_BIT;
+ sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
+ SC16IS7XX_LSR_DR_BIT,
+ 0);
+}
+
+static void sc16is7xx_start_tx(struct uart_port *port)
+{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+
+ /* handle rs485 */
+ if ((one->rs485.flags & SER_RS485_ENABLED) &&
+ (one->rs485.delay_rts_before_send > 0)) {
+ mdelay(one->rs485.delay_rts_before_send);
+ }
+
+ if (!work_pending(&one->tx_work))
+ schedule_work(&one->tx_work);
+}
+
+static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
+{
+ unsigned int lvl, lsr;
+
+ lvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
+ lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
+
+ return ((lsr & SC16IS7XX_LSR_THRE_BIT) && !lvl) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int sc16is7xx_get_mctrl(struct uart_port *port)
+{
+ /* DCD and DSR are not wired and CTS/RTS is handled automatically
+ * so just indicate DSR and CAR asserted
+ */
+ return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void sc16is7xx_md_proc(struct work_struct *ws)
+{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(ws, md_work);
+
+ sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG,
+ SC16IS7XX_MCR_LOOP_BIT,
+ (one->port.mctrl & TIOCM_LOOP) ?
+ SC16IS7XX_MCR_LOOP_BIT : 0);
+}
+
+static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+
+ schedule_work(&one->md_work);
+}
+
+static void sc16is7xx_break_ctl(struct uart_port *port, int break_state)
+{
+ sc16is7xx_port_update(port, SC16IS7XX_LCR_REG,
+ SC16IS7XX_LCR_TXBREAK_BIT,
+ break_state ? SC16IS7XX_LCR_TXBREAK_BIT : 0);
+}
+
+static void sc16is7xx_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ unsigned int lcr, flow = 0;
+ int baud;
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+
+ /* Word size */
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr = SC16IS7XX_LCR_WORD_LEN_5;
+ break;
+ case CS6:
+ lcr = SC16IS7XX_LCR_WORD_LEN_6;
+ break;
+ case CS7:
+ lcr = SC16IS7XX_LCR_WORD_LEN_7;
+ break;
+ case CS8:
+ lcr = SC16IS7XX_LCR_WORD_LEN_8;
+ break;
+ default:
+ lcr = SC16IS7XX_LCR_WORD_LEN_8;
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= CS8;
+ break;
+ }
+
+ /* Parity */
+ if (termios->c_cflag & PARENB) {
+ lcr |= SC16IS7XX_LCR_PARITY_BIT;
+ if (!(termios->c_cflag & PARODD))
+ lcr |= SC16IS7XX_LCR_EVENPARITY_BIT;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB)
+ lcr |= SC16IS7XX_LCR_STOPLEN_BIT; /* 2 stops */
+
+ /* Set read status mask */
+ port->read_status_mask = SC16IS7XX_LSR_OE_BIT;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= SC16IS7XX_LSR_PE_BIT |
+ SC16IS7XX_LSR_FE_BIT;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ port->read_status_mask |= SC16IS7XX_LSR_BI_BIT;
+
+ /* Set status ignore mask */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNBRK)
+ port->ignore_status_mask |= SC16IS7XX_LSR_BI_BIT;
+ if (!(termios->c_cflag & CREAD))
+ port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
+
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
+ SC16IS7XX_LCR_CONF_MODE_B);
+
+ /* Configure flow control */
+ regcache_cache_bypass(s->regmap, true);
+ sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
+ sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
+ if (termios->c_cflag & CRTSCTS)
+ flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
+ SC16IS7XX_EFR_AUTORTS_BIT;
+ if (termios->c_iflag & IXON)
+ flow |= SC16IS7XX_EFR_SWFLOW3_BIT;
+ if (termios->c_iflag & IXOFF)
+ flow |= SC16IS7XX_EFR_SWFLOW1_BIT;
+
+ sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, flow);
+ regcache_cache_bypass(s->regmap, false);
+
+ /* Update LCR register */
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+
+ /* Get baud rate generator configuration */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 4 / 0xffff,
+ port->uartclk / 16);
+
+ /* Setup baudrate generator */
+ baud = sc16is7xx_set_baud(port, baud);
+
+ /* Update timeout according to new baud rate */
+ uart_update_timeout(port, termios->c_cflag, baud);
+}
+
+#if defined(TIOCSRS485) && defined(TIOCGRS485)
+static void sc16is7xx_config_rs485(struct uart_port *port,
+ struct serial_rs485 *rs485)
+{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
+
+ one->rs485 = *rs485;
+
+ if (one->rs485.flags & SER_RS485_ENABLED) {
+ sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
+ SC16IS7XX_EFCR_AUTO_RS485_BIT,
+ SC16IS7XX_EFCR_AUTO_RS485_BIT);
+ } else {
+ sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
+ SC16IS7XX_EFCR_AUTO_RS485_BIT,
+ 0);
+ }
+}
+#endif
+
+static int sc16is7xx_ioctl(struct uart_port *port, unsigned int cmd,
+ unsigned long arg)
+{
+#if defined(TIOCSRS485) && defined(TIOCGRS485)
+ struct serial_rs485 rs485;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485)))
+ return -EFAULT;
+
+ sc16is7xx_config_rs485(port, &rs485);
+ return 0;
+ case TIOCGRS485:
+ if (copy_to_user((void __user *)arg,
+ &(to_sc16is7xx_one(port, port)->rs485),
+ sizeof(rs485)))
+ return -EFAULT;
+ return 0;
+ default:
+ break;
+ }
+#endif
+
+ return -ENOIOCTLCMD;
+}
+
+static int sc16is7xx_startup(struct uart_port *port)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+ unsigned int val;
+
+ sc16is7xx_power(port, 1);
+
+ /* Reset FIFOs*/
+ val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT;
+ sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val);
+ udelay(5);
+ sc16is7xx_port_write(port, SC16IS7XX_FCR_REG,
+ SC16IS7XX_FCR_FIFO_BIT);
+
+ /* Enable EFR */
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
+ SC16IS7XX_LCR_CONF_MODE_B);
+
+ regcache_cache_bypass(s->regmap, true);
+
+ /* Enable write access to enhanced features and internal clock div */
+ sc16is7xx_port_write(port, SC16IS7XX_EFR_REG,
+ SC16IS7XX_EFR_ENABLE_BIT);
+
+ /* Enable TCR/TLR */
+ sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
+ SC16IS7XX_MCR_TCRTLR_BIT,
+ SC16IS7XX_MCR_TCRTLR_BIT);
+
+ /* Configure flow control levels */
+ /* Flow control halt level 48, resume level 24 */
+ sc16is7xx_port_write(port, SC16IS7XX_TCR_REG,
+ SC16IS7XX_TCR_RX_RESUME(24) |
+ SC16IS7XX_TCR_RX_HALT(48));
+
+ regcache_cache_bypass(s->regmap, false);
+
+ /* Now, initialize the UART */
+ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8);
+
+ /* Enable the Rx and Tx FIFO */
+ sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
+ SC16IS7XX_EFCR_RXDISABLE_BIT |
+ SC16IS7XX_EFCR_TXDISABLE_BIT,
+ 0);
+
+ /* Enable RX, TX, CTS change interrupts */
+ val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT |
+ SC16IS7XX_IER_CTSI_BIT;
+ sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val);
+
+ return 0;
+}
+
+static void sc16is7xx_shutdown(struct uart_port *port)
+{
+ /* Disable all interrupts */
+ sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0);
+ /* Disable TX/RX */
+ sc16is7xx_port_write(port, SC16IS7XX_EFCR_REG,
+ SC16IS7XX_EFCR_RXDISABLE_BIT |
+ SC16IS7XX_EFCR_TXDISABLE_BIT);
+
+ sc16is7xx_power(port, 0);
+}
+
+static const char *sc16is7xx_type(struct uart_port *port)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
+
+ return (port->type == PORT_SC16IS7XX) ? s->devtype->name : NULL;
+}
+
+static int sc16is7xx_request_port(struct uart_port *port)
+{
+ /* Do nothing */
+ return 0;
+}
+
+static void sc16is7xx_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_SC16IS7XX;
+}
+
+static int sc16is7xx_verify_port(struct uart_port *port,
+ struct serial_struct *s)
+{
+ if ((s->type != PORT_UNKNOWN) && (s->type != PORT_SC16IS7XX))
+ return -EINVAL;
+ if (s->irq != port->irq)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void sc16is7xx_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ sc16is7xx_power(port, (state == UART_PM_STATE_ON) ? 1 : 0);
+}
+
+static void sc16is7xx_null_void(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static const struct uart_ops sc16is7xx_ops = {
+ .tx_empty = sc16is7xx_tx_empty,
+ .set_mctrl = sc16is7xx_set_mctrl,
+ .get_mctrl = sc16is7xx_get_mctrl,
+ .stop_tx = sc16is7xx_stop_tx,
+ .start_tx = sc16is7xx_start_tx,
+ .stop_rx = sc16is7xx_stop_rx,
+ .enable_ms = sc16is7xx_null_void,
+ .break_ctl = sc16is7xx_break_ctl,
+ .startup = sc16is7xx_startup,
+ .shutdown = sc16is7xx_shutdown,
+ .set_termios = sc16is7xx_set_termios,
+ .type = sc16is7xx_type,
+ .request_port = sc16is7xx_request_port,
+ .release_port = sc16is7xx_null_void,
+ .config_port = sc16is7xx_config_port,
+ .verify_port = sc16is7xx_verify_port,
+ .ioctl = sc16is7xx_ioctl,
+ .pm = sc16is7xx_pm,
+};
+
+#ifdef CONFIG_GPIOLIB
+static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned int val;
+ struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+ gpio);
+ struct uart_port *port = &s->p[0].port;
+
+ val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);
+
+ return !!(val & BIT(offset));
+}
+
+static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+ gpio);
+ struct uart_port *port = &s->p[0].port;
+
+ sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset),
+ val ? BIT(offset) : 0);
+}
+
+static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+ gpio);
+ struct uart_port *port = &s->p[0].port;
+
+ sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0);
+
+ return 0;
+}
+
+static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int val)
+{
+ struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port,
+ gpio);
+ struct uart_port *port = &s->p[0].port;
+
+ sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset),
+ val ? BIT(offset) : 0);
+ sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset),
+ BIT(offset));
+
+ return 0;
+}
+#endif
+
+static int sc16is7xx_probe(struct device *dev,
+ struct sc16is7xx_devtype *devtype,
+ struct regmap *regmap, int irq, unsigned long flags)
+{
+ unsigned long freq, *pfreq = dev_get_platdata(dev);
+ struct clk *clk;
+ int i, ret;
+ struct sc16is7xx_port *s;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Alloc port structure */
+ s = devm_kzalloc(dev, sizeof(*s) +
+ sizeof(struct sc16is7xx_one) * devtype->nr_uart,
+ GFP_KERNEL);
+ if (!s) {
+ dev_err(dev, "Error allocating port structure\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ if (pfreq)
+ freq = *pfreq;
+ else
+ return PTR_ERR(clk);
+ } else {
+ freq = clk_get_rate(clk);
+ }
+
+ s->regmap = regmap;
+ s->devtype = devtype;
+ dev_set_drvdata(dev, s);
+
+ /* Register UART driver */
+ s->uart.owner = THIS_MODULE;
+ s->uart.dev_name = "ttySC";
+ s->uart.nr = devtype->nr_uart;
+ ret = uart_register_driver(&s->uart);
+ if (ret) {
+ dev_err(dev, "Registering UART driver failed\n");
+ goto out_clk;
+ }
+
+#ifdef CONFIG_GPIOLIB
+ if (devtype->nr_gpio) {
+ /* Setup GPIO cotroller */
+ s->gpio.owner = THIS_MODULE;
+ s->gpio.dev = dev;
+ s->gpio.label = dev_name(dev);
+ s->gpio.direction_input = sc16is7xx_gpio_direction_input;
+ s->gpio.get = sc16is7xx_gpio_get;
+ s->gpio.direction_output = sc16is7xx_gpio_direction_output;
+ s->gpio.set = sc16is7xx_gpio_set;
+ s->gpio.base = -1;
+ s->gpio.ngpio = devtype->nr_gpio;
+ s->gpio.can_sleep = 1;
+ ret = gpiochip_add(&s->gpio);
+ if (ret)
+ goto out_uart;
+ }
+#endif
+
+ mutex_init(&s->mutex);
+
+ for (i = 0; i < devtype->nr_uart; ++i) {
+ /* Initialize port data */
+ s->p[i].port.line = i;
+ s->p[i].port.dev = dev;
+ s->p[i].port.irq = irq;
+ s->p[i].port.type = PORT_SC16IS7XX;
+ s->p[i].port.fifosize = SC16IS7XX_FIFO_SIZE;
+ s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY;
+ s->p[i].port.iotype = UPIO_PORT;
+ s->p[i].port.uartclk = freq;
+ s->p[i].port.ops = &sc16is7xx_ops;
+ /* Disable all interrupts */
+ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0);
+ /* Disable TX/RX */
+ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG,
+ SC16IS7XX_EFCR_RXDISABLE_BIT |
+ SC16IS7XX_EFCR_TXDISABLE_BIT);
+ /* Initialize queue for start TX */
+ INIT_WORK(&s->p[i].tx_work, sc16is7xx_wq_proc);
+ /* Initialize queue for changing mode */
+ INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc);
+ /* Register port */
+ uart_add_one_port(&s->uart, &s->p[i].port);
+ /* Go to suspend mode */
+ sc16is7xx_power(&s->p[i].port, 0);
+ }
+
+ /* Setup interrupt */
+ ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_ist,
+ IRQF_ONESHOT | flags, dev_name(dev), s);
+ if (!ret)
+ return 0;
+
+ mutex_destroy(&s->mutex);
+
+#ifdef CONFIG_GPIOLIB
+ if (devtype->nr_gpio)
+ WARN_ON(gpiochip_remove(&s->gpio));
+
+out_uart:
+#endif
+ uart_unregister_driver(&s->uart);
+
+out_clk:
+ if (!IS_ERR(s->clk))
+ clk_disable_unprepare(s->clk);
+
+ return ret;
+}
+
+static int sc16is7xx_remove(struct device *dev)
+{
+ struct sc16is7xx_port *s = dev_get_drvdata(dev);
+ int i, ret = 0;
+
+#ifdef CONFIG_GPIOLIB
+ if (s->devtype->nr_gpio) {
+ ret = gpiochip_remove(&s->gpio);
+ if (ret)
+ return ret;
+ }
+#endif
+
+ for (i = 0; i < s->uart.nr; i++) {
+ cancel_work_sync(&s->p[i].tx_work);
+ cancel_work_sync(&s->p[i].md_work);
+ uart_remove_one_port(&s->uart, &s->p[i].port);
+ sc16is7xx_power(&s->p[i].port, 0);
+ }
+
+ mutex_destroy(&s->mutex);
+ uart_unregister_driver(&s->uart);
+ if (!IS_ERR(s->clk))
+ clk_disable_unprepare(s->clk);
+
+ return ret;
+}
+
+static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = {
+ { .compatible = "nxp,sc16is740", .data = &sc16is74x_devtype, },
+ { .compatible = "nxp,sc16is741", .data = &sc16is74x_devtype, },
+ { .compatible = "nxp,sc16is750", .data = &sc16is750_devtype, },
+ { .compatible = "nxp,sc16is752", .data = &sc16is752_devtype, },
+ { .compatible = "nxp,sc16is760", .data = &sc16is760_devtype, },
+ { .compatible = "nxp,sc16is762", .data = &sc16is762_devtype, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids);
+
+static struct regmap_config regcfg = {
+ .reg_bits = 7,
+ .pad_bits = 1,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = sc16is7xx_regmap_volatile,
+ .precious_reg = sc16is7xx_regmap_precious,
+};
+
+static int sc16is7xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct sc16is7xx_devtype *devtype;
+ unsigned long flags = 0;
+ struct regmap *regmap;
+
+ if (i2c->dev.of_node) {
+ const struct of_device_id *of_id =
+ of_match_device(sc16is7xx_dt_ids, &i2c->dev);
+
+ devtype = (struct sc16is7xx_devtype *)of_id->data;
+ } else {
+ devtype = (struct sc16is7xx_devtype *)id->driver_data;
+ flags = IRQF_TRIGGER_FALLING;
+ }
+
+ regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) |
+ (devtype->nr_uart - 1);
+ regmap = devm_regmap_init_i2c(i2c, &regcfg);
+
+ return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq, flags);
+}
+
+static int sc16is7xx_i2c_remove(struct i2c_client *client)
+{
+ return sc16is7xx_remove(&client->dev);
+}
+
+static const struct i2c_device_id sc16is7xx_i2c_id_table[] = {
+ { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, },
+ { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, },
+ { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, },
+ { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, },
+ { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table);
+
+static struct i2c_driver sc16is7xx_i2c_uart_driver = {
+ .driver = {
+ .name = SC16IS7XX_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+ },
+ .probe = sc16is7xx_i2c_probe,
+ .remove = sc16is7xx_i2c_remove,
+ .id_table = sc16is7xx_i2c_id_table,
+};
+module_i2c_driver(sc16is7xx_i2c_uart_driver);
+MODULE_ALIAS("i2c:sc16is7xx");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");
+MODULE_DESCRIPTION("SC16IS7XX serial driver");
diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c
deleted file mode 100644
index 4b1434d53e9..00000000000
--- a/drivers/tty/serial/sc26xx.c
+++ /dev/null
@@ -1,749 +0,0 @@
-/*
- * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices.
- *
- * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de)
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/major.h>
-#include <linux/circ_buf.h>
-#include <linux/serial.h>
-#include <linux/sysrq.h>
-#include <linux/console.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-
-#warning "Please try migrate to use new driver SCCNXP and report the status" \
- "in the linux-serial mailing list."
-
-#if defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/serial_core.h>
-
-#define SC26XX_MAJOR 204
-#define SC26XX_MINOR_START 205
-#define SC26XX_NR 2
-
-struct uart_sc26xx_port {
- struct uart_port port[2];
- u8 dsr_mask[2];
- u8 cts_mask[2];
- u8 dcd_mask[2];
- u8 ri_mask[2];
- u8 dtr_mask[2];
- u8 rts_mask[2];
- u8 imr;
-};
-
-/* register common to both ports */
-#define RD_ISR 0x14
-#define RD_IPR 0x34
-
-#define WR_ACR 0x10
-#define WR_IMR 0x14
-#define WR_OPCR 0x34
-#define WR_OPR_SET 0x38
-#define WR_OPR_CLR 0x3C
-
-/* access common register */
-#define READ_SC(p, r) readb((p)->membase + RD_##r)
-#define WRITE_SC(p, r, v) writeb((v), (p)->membase + WR_##r)
-
-/* register per port */
-#define RD_PORT_MRx 0x00
-#define RD_PORT_SR 0x04
-#define RD_PORT_RHR 0x0c
-
-#define WR_PORT_MRx 0x00
-#define WR_PORT_CSR 0x04
-#define WR_PORT_CR 0x08
-#define WR_PORT_THR 0x0c
-
-/* SR bits */
-#define SR_BREAK (1 << 7)
-#define SR_FRAME (1 << 6)
-#define SR_PARITY (1 << 5)
-#define SR_OVERRUN (1 << 4)
-#define SR_TXRDY (1 << 2)
-#define SR_RXRDY (1 << 0)
-
-#define CR_RES_MR (1 << 4)
-#define CR_RES_RX (2 << 4)
-#define CR_RES_TX (3 << 4)
-#define CR_STRT_BRK (6 << 4)
-#define CR_STOP_BRK (7 << 4)
-#define CR_DIS_TX (1 << 3)
-#define CR_ENA_TX (1 << 2)
-#define CR_DIS_RX (1 << 1)
-#define CR_ENA_RX (1 << 0)
-
-/* ISR bits */
-#define ISR_RXRDYB (1 << 5)
-#define ISR_TXRDYB (1 << 4)
-#define ISR_RXRDYA (1 << 1)
-#define ISR_TXRDYA (1 << 0)
-
-/* IMR bits */
-#define IMR_RXRDY (1 << 1)
-#define IMR_TXRDY (1 << 0)
-
-/* access port register */
-static inline u8 read_sc_port(struct uart_port *p, u8 reg)
-{
- return readb(p->membase + p->line * 0x20 + reg);
-}
-
-static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val)
-{
- writeb(val, p->membase + p->line * 0x20 + reg);
-}
-
-#define READ_SC_PORT(p, r) read_sc_port(p, RD_PORT_##r)
-#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v)
-
-static void sc26xx_enable_irq(struct uart_port *port, int mask)
-{
- struct uart_sc26xx_port *up;
- int line = port->line;
-
- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
-
- up->imr |= mask << (line * 4);
- WRITE_SC(port, IMR, up->imr);
-}
-
-static void sc26xx_disable_irq(struct uart_port *port, int mask)
-{
- struct uart_sc26xx_port *up;
- int line = port->line;
-
- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
-
- up->imr &= ~(mask << (line * 4));
- WRITE_SC(port, IMR, up->imr);
-}
-
-static bool receive_chars(struct uart_port *port)
-{
- struct tty_port *tport = NULL;
- int limit = 10000;
- unsigned char ch;
- char flag;
- u8 status;
-
- /* FIXME what is this trying to achieve? */
- if (port->state != NULL) /* Unopened serial console */
- tport = &port->state->port;
-
- while (limit-- > 0) {
- status = READ_SC_PORT(port, SR);
- if (!(status & SR_RXRDY))
- break;
- ch = READ_SC_PORT(port, RHR);
-
- flag = TTY_NORMAL;
- port->icount.rx++;
-
- if (unlikely(status & (SR_BREAK | SR_FRAME |
- SR_PARITY | SR_OVERRUN))) {
- if (status & SR_BREAK) {
- status &= ~(SR_PARITY | SR_FRAME);
- port->icount.brk++;
- if (uart_handle_break(port))
- continue;
- } else if (status & SR_PARITY)
- port->icount.parity++;
- else if (status & SR_FRAME)
- port->icount.frame++;
- if (status & SR_OVERRUN)
- port->icount.overrun++;
-
- status &= port->read_status_mask;
- if (status & SR_BREAK)
- flag = TTY_BREAK;
- else if (status & SR_PARITY)
- flag = TTY_PARITY;
- else if (status & SR_FRAME)
- flag = TTY_FRAME;
- }
-
- if (uart_handle_sysrq_char(port, ch))
- continue;
-
- if (status & port->ignore_status_mask)
- continue;
-
- tty_insert_flip_char(tport, ch, flag);
- }
- return !!tport;
-}
-
-static void transmit_chars(struct uart_port *port)
-{
- struct circ_buf *xmit;
-
- if (!port->state)
- return;
-
- xmit = &port->state->xmit;
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- sc26xx_disable_irq(port, IMR_TXRDY);
- return;
- }
- while (!uart_circ_empty(xmit)) {
- if (!(READ_SC_PORT(port, SR) & SR_TXRDY))
- break;
-
- WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-}
-
-static irqreturn_t sc26xx_interrupt(int irq, void *dev_id)
-{
- struct uart_sc26xx_port *up = dev_id;
- unsigned long flags;
- bool push;
- u8 isr;
-
- spin_lock_irqsave(&up->port[0].lock, flags);
-
- push = false;
- isr = READ_SC(&up->port[0], ISR);
- if (isr & ISR_TXRDYA)
- transmit_chars(&up->port[0]);
- if (isr & ISR_RXRDYA)
- push = receive_chars(&up->port[0]);
-
- spin_unlock(&up->port[0].lock);
-
- if (push)
- tty_flip_buffer_push(&up->port[0].state->port);
-
- spin_lock(&up->port[1].lock);
-
- push = false;
- if (isr & ISR_TXRDYB)
- transmit_chars(&up->port[1]);
- if (isr & ISR_RXRDYB)
- push = receive_chars(&up->port[1]);
-
- spin_unlock_irqrestore(&up->port[1].lock, flags);
-
- if (push)
- tty_flip_buffer_push(&up->port[1].state->port);
-
- return IRQ_HANDLED;
-}
-
-/* port->lock is not held. */
-static unsigned int sc26xx_tx_empty(struct uart_port *port)
-{
- return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0;
-}
-
-/* port->lock held by caller. */
-static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- struct uart_sc26xx_port *up;
- int line = port->line;
-
- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
-
- if (up->dtr_mask[line]) {
- if (mctrl & TIOCM_DTR)
- WRITE_SC(port, OPR_SET, up->dtr_mask[line]);
- else
- WRITE_SC(port, OPR_CLR, up->dtr_mask[line]);
- }
- if (up->rts_mask[line]) {
- if (mctrl & TIOCM_RTS)
- WRITE_SC(port, OPR_SET, up->rts_mask[line]);
- else
- WRITE_SC(port, OPR_CLR, up->rts_mask[line]);
- }
-}
-
-/* port->lock is held by caller and interrupts are disabled. */
-static unsigned int sc26xx_get_mctrl(struct uart_port *port)
-{
- struct uart_sc26xx_port *up;
- int line = port->line;
- unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
- u8 ipr;
-
- port -= line;
- up = container_of(port, struct uart_sc26xx_port, port[0]);
- ipr = READ_SC(port, IPR) ^ 0xff;
-
- if (up->dsr_mask[line]) {
- mctrl &= ~TIOCM_DSR;
- mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0;
- }
- if (up->cts_mask[line]) {
- mctrl &= ~TIOCM_CTS;
- mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0;
- }
- if (up->dcd_mask[line]) {
- mctrl &= ~TIOCM_CAR;
- mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0;
- }
- if (up->ri_mask[line]) {
- mctrl &= ~TIOCM_RNG;
- mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0;
- }
- return mctrl;
-}
-
-/* port->lock held by caller. */
-static void sc26xx_stop_tx(struct uart_port *port)
-{
- return;
-}
-
-/* port->lock held by caller. */
-static void sc26xx_start_tx(struct uart_port *port)
-{
- struct circ_buf *xmit = &port->state->xmit;
-
- while (!uart_circ_empty(xmit)) {
- if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) {
- sc26xx_enable_irq(port, IMR_TXRDY);
- break;
- }
- WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-}
-
-/* port->lock held by caller. */
-static void sc26xx_stop_rx(struct uart_port *port)
-{
-}
-
-/* port->lock held by caller. */
-static void sc26xx_enable_ms(struct uart_port *port)
-{
-}
-
-/* port->lock is not held. */
-static void sc26xx_break_ctl(struct uart_port *port, int break_state)
-{
- if (break_state == -1)
- WRITE_SC_PORT(port, CR, CR_STRT_BRK);
- else
- WRITE_SC_PORT(port, CR, CR_STOP_BRK);
-}
-
-/* port->lock is not held. */
-static int sc26xx_startup(struct uart_port *port)
-{
- sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
- WRITE_SC(port, OPCR, 0);
-
- /* reset tx and rx */
- WRITE_SC_PORT(port, CR, CR_RES_RX);
- WRITE_SC_PORT(port, CR, CR_RES_TX);
-
- /* start rx/tx */
- WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
-
- /* enable irqs */
- sc26xx_enable_irq(port, IMR_RXRDY);
- return 0;
-}
-
-/* port->lock is not held. */
-static void sc26xx_shutdown(struct uart_port *port)
-{
- /* disable interrupst */
- sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
-
- /* stop tx/rx */
- WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX);
-}
-
-/* port->lock is not held. */
-static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
-{
- unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
- unsigned int quot = uart_get_divisor(port, baud);
- unsigned int iflag, cflag;
- unsigned long flags;
- u8 mr1, mr2, csr;
-
- spin_lock_irqsave(&port->lock, flags);
-
- while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)
- udelay(2);
-
- WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX);
-
- iflag = termios->c_iflag;
- cflag = termios->c_cflag;
-
- port->read_status_mask = SR_OVERRUN;
- if (iflag & INPCK)
- port->read_status_mask |= SR_PARITY | SR_FRAME;
- if (iflag & (BRKINT | PARMRK))
- port->read_status_mask |= SR_BREAK;
-
- port->ignore_status_mask = 0;
- if (iflag & IGNBRK)
- port->ignore_status_mask |= SR_BREAK;
- if ((cflag & CREAD) == 0)
- port->ignore_status_mask |= SR_BREAK | SR_FRAME |
- SR_PARITY | SR_OVERRUN;
-
- switch (cflag & CSIZE) {
- case CS5:
- mr1 = 0x00;
- break;
- case CS6:
- mr1 = 0x01;
- break;
- case CS7:
- mr1 = 0x02;
- break;
- default:
- case CS8:
- mr1 = 0x03;
- break;
- }
- mr2 = 0x07;
- if (cflag & CSTOPB)
- mr2 = 0x0f;
- if (cflag & PARENB) {
- if (cflag & PARODD)
- mr1 |= (1 << 2);
- } else
- mr1 |= (2 << 3);
-
- switch (baud) {
- case 50:
- csr = 0x00;
- break;
- case 110:
- csr = 0x11;
- break;
- case 134:
- csr = 0x22;
- break;
- case 200:
- csr = 0x33;
- break;
- case 300:
- csr = 0x44;
- break;
- case 600:
- csr = 0x55;
- break;
- case 1200:
- csr = 0x66;
- break;
- case 2400:
- csr = 0x88;
- break;
- case 4800:
- csr = 0x99;
- break;
- default:
- case 9600:
- csr = 0xbb;
- break;
- case 19200:
- csr = 0xcc;
- break;
- }
-
- WRITE_SC_PORT(port, CR, CR_RES_MR);
- WRITE_SC_PORT(port, MRx, mr1);
- WRITE_SC_PORT(port, MRx, mr2);
-
- WRITE_SC(port, ACR, 0x80);
- WRITE_SC_PORT(port, CSR, csr);
-
- /* reset tx and rx */
- WRITE_SC_PORT(port, CR, CR_RES_RX);
- WRITE_SC_PORT(port, CR, CR_RES_TX);
-
- WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
- while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)
- udelay(2);
-
- /* XXX */
- uart_update_timeout(port, cflag,
- (port->uartclk / (16 * quot)));
-
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static const char *sc26xx_type(struct uart_port *port)
-{
- return "SC26XX";
-}
-
-static void sc26xx_release_port(struct uart_port *port)
-{
-}
-
-static int sc26xx_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-static void sc26xx_config_port(struct uart_port *port, int flags)
-{
-}
-
-static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- return -EINVAL;
-}
-
-static struct uart_ops sc26xx_ops = {
- .tx_empty = sc26xx_tx_empty,
- .set_mctrl = sc26xx_set_mctrl,
- .get_mctrl = sc26xx_get_mctrl,
- .stop_tx = sc26xx_stop_tx,
- .start_tx = sc26xx_start_tx,
- .stop_rx = sc26xx_stop_rx,
- .enable_ms = sc26xx_enable_ms,
- .break_ctl = sc26xx_break_ctl,
- .startup = sc26xx_startup,
- .shutdown = sc26xx_shutdown,
- .set_termios = sc26xx_set_termios,
- .type = sc26xx_type,
- .release_port = sc26xx_release_port,
- .request_port = sc26xx_request_port,
- .config_port = sc26xx_config_port,
- .verify_port = sc26xx_verify_port,
-};
-
-static struct uart_port *sc26xx_port;
-
-#ifdef CONFIG_SERIAL_SC26XX_CONSOLE
-static void sc26xx_console_putchar(struct uart_port *port, char c)
-{
- unsigned long flags;
- int limit = 1000000;
-
- spin_lock_irqsave(&port->lock, flags);
-
- while (limit-- > 0) {
- if (READ_SC_PORT(port, SR) & SR_TXRDY) {
- WRITE_SC_PORT(port, THR, c);
- break;
- }
- udelay(2);
- }
-
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static void sc26xx_console_write(struct console *con, const char *s, unsigned n)
-{
- struct uart_port *port = sc26xx_port;
- int i;
-
- for (i = 0; i < n; i++) {
- if (*s == '\n')
- sc26xx_console_putchar(port, '\r');
- sc26xx_console_putchar(port, *s++);
- }
-}
-
-static int __init sc26xx_console_setup(struct console *con, char *options)
-{
- struct uart_port *port = sc26xx_port;
- int baud = 9600;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- if (port->type != PORT_SC26XX)
- return -1;
-
- printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index);
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
-
- return uart_set_options(port, con, baud, parity, bits, flow);
-}
-
-static struct uart_driver sc26xx_reg;
-static struct console sc26xx_console = {
- .name = "ttySC",
- .write = sc26xx_console_write,
- .device = uart_console_device,
- .setup = sc26xx_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &sc26xx_reg,
-};
-#define SC26XX_CONSOLE &sc26xx_console
-#else
-#define SC26XX_CONSOLE NULL
-#endif
-
-static struct uart_driver sc26xx_reg = {
- .owner = THIS_MODULE,
- .driver_name = "SC26xx",
- .dev_name = "ttySC",
- .major = SC26XX_MAJOR,
- .minor = SC26XX_MINOR_START,
- .nr = SC26XX_NR,
- .cons = SC26XX_CONSOLE,
-};
-
-static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos)
-{
- unsigned int bit = (flags >> bitpos) & 15;
-
- return bit ? (1 << (bit - 1)) : 0;
-}
-
-static void sc26xx_init_masks(struct uart_sc26xx_port *up,
- int line, unsigned int data)
-{
- up->dtr_mask[line] = sc26xx_flags2mask(data, 0);
- up->rts_mask[line] = sc26xx_flags2mask(data, 4);
- up->dsr_mask[line] = sc26xx_flags2mask(data, 8);
- up->cts_mask[line] = sc26xx_flags2mask(data, 12);
- up->dcd_mask[line] = sc26xx_flags2mask(data, 16);
- up->ri_mask[line] = sc26xx_flags2mask(data, 20);
-}
-
-static int sc26xx_probe(struct platform_device *dev)
-{
- struct resource *res;
- struct uart_sc26xx_port *up;
- unsigned int *sc26xx_data = dev->dev.platform_data;
- int err;
-
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- up = kzalloc(sizeof *up, GFP_KERNEL);
- if (unlikely(!up))
- return -ENOMEM;
-
- up->port[0].line = 0;
- up->port[0].ops = &sc26xx_ops;
- up->port[0].type = PORT_SC26XX;
- up->port[0].uartclk = (29491200 / 16); /* arbitrary */
-
- up->port[0].mapbase = res->start;
- up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40);
- up->port[0].iotype = UPIO_MEM;
- up->port[0].irq = platform_get_irq(dev, 0);
-
- up->port[0].dev = &dev->dev;
-
- sc26xx_init_masks(up, 0, sc26xx_data[0]);
-
- sc26xx_port = &up->port[0];
-
- up->port[1].line = 1;
- up->port[1].ops = &sc26xx_ops;
- up->port[1].type = PORT_SC26XX;
- up->port[1].uartclk = (29491200 / 16); /* arbitrary */
-
- up->port[1].mapbase = up->port[0].mapbase;
- up->port[1].membase = up->port[0].membase;
- up->port[1].iotype = UPIO_MEM;
- up->port[1].irq = up->port[0].irq;
-
- up->port[1].dev = &dev->dev;
-
- sc26xx_init_masks(up, 1, sc26xx_data[1]);
-
- err = uart_register_driver(&sc26xx_reg);
- if (err)
- goto out_free_port;
-
- sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor;
-
- err = uart_add_one_port(&sc26xx_reg, &up->port[0]);
- if (err)
- goto out_unregister_driver;
-
- err = uart_add_one_port(&sc26xx_reg, &up->port[1]);
- if (err)
- goto out_remove_port0;
-
- err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up);
- if (err)
- goto out_remove_ports;
-
- platform_set_drvdata(dev, up);
- return 0;
-
-out_remove_ports:
- uart_remove_one_port(&sc26xx_reg, &up->port[1]);
-out_remove_port0:
- uart_remove_one_port(&sc26xx_reg, &up->port[0]);
-
-out_unregister_driver:
- uart_unregister_driver(&sc26xx_reg);
-
-out_free_port:
- kfree(up);
- sc26xx_port = NULL;
- return err;
-}
-
-
-static int __exit sc26xx_driver_remove(struct platform_device *dev)
-{
- struct uart_sc26xx_port *up = platform_get_drvdata(dev);
-
- free_irq(up->port[0].irq, up);
-
- uart_remove_one_port(&sc26xx_reg, &up->port[0]);
- uart_remove_one_port(&sc26xx_reg, &up->port[1]);
-
- uart_unregister_driver(&sc26xx_reg);
-
- kfree(up);
- sc26xx_port = NULL;
-
- return 0;
-}
-
-static struct platform_driver sc26xx_driver = {
- .probe = sc26xx_probe,
- .remove = sc26xx_driver_remove,
- .driver = {
- .name = "SC26xx",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(sc26xx_driver);
-
-MODULE_AUTHOR("Thomas Bogendörfer");
-MODULE_DESCRIPTION("SC681/SC2692 serial driver");
-MODULE_VERSION("1.0");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:SC26xx");
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index c7730415541..e84b6a3bdd1 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -15,6 +15,7 @@
#define SUPPORT_SYSRQ
#endif
+#include <linux/clk.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -94,16 +95,17 @@
#define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0)
#define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0)
-/* Supported chip types */
-enum {
- SCCNXP_TYPE_SC2681 = 2681,
- SCCNXP_TYPE_SC2691 = 2691,
- SCCNXP_TYPE_SC2692 = 2692,
- SCCNXP_TYPE_SC2891 = 2891,
- SCCNXP_TYPE_SC2892 = 2892,
- SCCNXP_TYPE_SC28202 = 28202,
- SCCNXP_TYPE_SC68681 = 68681,
- SCCNXP_TYPE_SC68692 = 68692,
+#define SCCNXP_HAVE_IO 0x00000001
+#define SCCNXP_HAVE_MR0 0x00000002
+
+struct sccnxp_chip {
+ const char *name;
+ unsigned int nr;
+ unsigned long freq_min;
+ unsigned long freq_std;
+ unsigned long freq_max;
+ unsigned int flags;
+ unsigned int fifosize;
};
struct sccnxp_port {
@@ -111,16 +113,10 @@ struct sccnxp_port {
struct uart_port port[SCCNXP_MAX_UARTS];
bool opened[SCCNXP_MAX_UARTS];
- const char *name;
int irq;
-
u8 imr;
- u8 addr_mask;
- int freq_std;
- int flags;
-#define SCCNXP_HAVE_IO 0x00000001
-#define SCCNXP_HAVE_MR0 0x00000002
+ struct sccnxp_chip *chip;
#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
struct console console;
@@ -136,29 +132,94 @@ struct sccnxp_port {
struct regulator *regulator;
};
-static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift)
-{
- return readb(base + (reg << shift));
-}
+static const struct sccnxp_chip sc2681 = {
+ .name = "SC2681",
+ .nr = 2,
+ .freq_min = 1000000,
+ .freq_std = 3686400,
+ .freq_max = 4000000,
+ .flags = SCCNXP_HAVE_IO,
+ .fifosize = 3,
+};
-static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v)
-{
- writeb(v, base + (reg << shift));
-}
+static const struct sccnxp_chip sc2691 = {
+ .name = "SC2691",
+ .nr = 1,
+ .freq_min = 1000000,
+ .freq_std = 3686400,
+ .freq_max = 4000000,
+ .flags = 0,
+ .fifosize = 3,
+};
+
+static const struct sccnxp_chip sc2692 = {
+ .name = "SC2692",
+ .nr = 2,
+ .freq_min = 1000000,
+ .freq_std = 3686400,
+ .freq_max = 4000000,
+ .flags = SCCNXP_HAVE_IO,
+ .fifosize = 3,
+};
+
+static const struct sccnxp_chip sc2891 = {
+ .name = "SC2891",
+ .nr = 1,
+ .freq_min = 100000,
+ .freq_std = 3686400,
+ .freq_max = 8000000,
+ .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+ .fifosize = 16,
+};
+
+static const struct sccnxp_chip sc2892 = {
+ .name = "SC2892",
+ .nr = 2,
+ .freq_min = 100000,
+ .freq_std = 3686400,
+ .freq_max = 8000000,
+ .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+ .fifosize = 16,
+};
+
+static const struct sccnxp_chip sc28202 = {
+ .name = "SC28202",
+ .nr = 2,
+ .freq_min = 1000000,
+ .freq_std = 14745600,
+ .freq_max = 50000000,
+ .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
+ .fifosize = 256,
+};
+
+static const struct sccnxp_chip sc68681 = {
+ .name = "SC68681",
+ .nr = 2,
+ .freq_min = 1000000,
+ .freq_std = 3686400,
+ .freq_max = 4000000,
+ .flags = SCCNXP_HAVE_IO,
+ .fifosize = 3,
+};
+
+static const struct sccnxp_chip sc68692 = {
+ .name = "SC68692",
+ .nr = 2,
+ .freq_min = 1000000,
+ .freq_std = 3686400,
+ .freq_max = 4000000,
+ .flags = SCCNXP_HAVE_IO,
+ .fifosize = 3,
+};
static inline u8 sccnxp_read(struct uart_port *port, u8 reg)
{
- struct sccnxp_port *s = dev_get_drvdata(port->dev);
-
- return sccnxp_raw_read(port->membase, reg & s->addr_mask,
- port->regshift);
+ return readb(port->membase + (reg << port->regshift));
}
static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
{
- struct sccnxp_port *s = dev_get_drvdata(port->dev);
-
- sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v);
+ writeb(v, port->membase + (reg << port->regshift));
}
static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
@@ -224,13 +285,14 @@ static int sccnxp_set_baud(struct uart_port *port, int baud)
{
struct sccnxp_port *s = dev_get_drvdata(port->dev);
int div_std, tmp_baud, bestbaud = baud, besterr = -1;
+ struct sccnxp_chip *chip = s->chip;
u8 i, acr = 0, csr = 0, mr0 = 0;
/* Find best baud from table */
for (i = 0; baud_std[i].baud && besterr; i++) {
- if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0))
+ if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))
continue;
- div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud);
+ div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud);
tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std);
if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) {
acr = baud_std[i].acr;
@@ -240,7 +302,7 @@ static int sccnxp_set_baud(struct uart_port *port, int baud)
}
}
- if (s->flags & SCCNXP_HAVE_MR0) {
+ if (chip->flags & SCCNXP_HAVE_MR0) {
/* Enable FIFO, set half level for TX */
mr0 |= MR0_FIFO | MR0_TXLVL;
/* Update MR0 */
@@ -363,7 +425,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
sccnxp_disable_irq(port, IMR_TXRDY);
/* Set direction to input */
- if (s->flags & SCCNXP_HAVE_IO)
+ if (s->chip->flags & SCCNXP_HAVE_IO)
sccnxp_set_bit(port, DIR_OP, 0);
}
return;
@@ -412,9 +474,7 @@ static void sccnxp_timer(unsigned long data)
sccnxp_handle_events(s);
spin_unlock_irqrestore(&s->lock, flags);
- if (!timer_pending(&s->timer))
- mod_timer(&s->timer, jiffies +
- usecs_to_jiffies(s->pdata.poll_time_us));
+ mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us));
}
static irqreturn_t sccnxp_ist(int irq, void *dev_id)
@@ -437,7 +497,7 @@ static void sccnxp_start_tx(struct uart_port *port)
spin_lock_irqsave(&s->lock, flags);
/* Set direction to output */
- if (s->flags & SCCNXP_HAVE_IO)
+ if (s->chip->flags & SCCNXP_HAVE_IO)
sccnxp_set_bit(port, DIR_OP, 1);
sccnxp_enable_irq(port, IMR_TXRDY);
@@ -483,7 +543,7 @@ static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl)
struct sccnxp_port *s = dev_get_drvdata(port->dev);
unsigned long flags;
- if (!(s->flags & SCCNXP_HAVE_IO))
+ if (!(s->chip->flags & SCCNXP_HAVE_IO))
return;
spin_lock_irqsave(&s->lock, flags);
@@ -501,7 +561,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port)
struct sccnxp_port *s = dev_get_drvdata(port->dev);
unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
- if (!(s->flags & SCCNXP_HAVE_IO))
+ if (!(s->chip->flags & SCCNXP_HAVE_IO))
return mctrl;
spin_lock_irqsave(&s->lock, flags);
@@ -605,19 +665,21 @@ static void sccnxp_set_termios(struct uart_port *port,
port->read_status_mask = SR_OVR;
if (termios->c_iflag & INPCK)
port->read_status_mask |= SR_PE | SR_FE;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= SR_BRK;
/* Set status ignore mask */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNBRK)
port->ignore_status_mask |= SR_BRK;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= SR_PE;
if (!(termios->c_cflag & CREAD))
port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK;
/* Setup baudrate */
baud = uart_get_baud_rate(port, termios, old, 50,
- (s->flags & SCCNXP_HAVE_MR0) ?
+ (s->chip->flags & SCCNXP_HAVE_MR0) ?
230400 : 38400);
baud = sccnxp_set_baud(port, baud);
@@ -641,7 +703,7 @@ static int sccnxp_startup(struct uart_port *port)
spin_lock_irqsave(&s->lock, flags);
- if (s->flags & SCCNXP_HAVE_IO) {
+ if (s->chip->flags & SCCNXP_HAVE_IO) {
/* Outputs are controlled manually */
sccnxp_write(port, SCCNXP_OPCR_REG, 0);
}
@@ -681,7 +743,7 @@ static void sccnxp_shutdown(struct uart_port *port)
sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
/* Leave direction to input */
- if (s->flags & SCCNXP_HAVE_IO)
+ if (s->chip->flags & SCCNXP_HAVE_IO)
sccnxp_set_bit(port, DIR_OP, 0);
spin_unlock_irqrestore(&s->lock, flags);
@@ -691,7 +753,7 @@ static const char *sccnxp_type(struct uart_port *port)
{
struct sccnxp_port *s = dev_get_drvdata(port->dev);
- return (port->type == PORT_SC26XX) ? s->name : NULL;
+ return (port->type == PORT_SC26XX) ? s->chip->name : NULL;
}
static void sccnxp_release_port(struct uart_port *port)
@@ -778,19 +840,31 @@ static int sccnxp_console_setup(struct console *co, char *options)
}
#endif
+static const struct platform_device_id sccnxp_id_table[] = {
+ { .name = "sc2681", .driver_data = (kernel_ulong_t)&sc2681, },
+ { .name = "sc2691", .driver_data = (kernel_ulong_t)&sc2691, },
+ { .name = "sc2692", .driver_data = (kernel_ulong_t)&sc2692, },
+ { .name = "sc2891", .driver_data = (kernel_ulong_t)&sc2891, },
+ { .name = "sc2892", .driver_data = (kernel_ulong_t)&sc2892, },
+ { .name = "sc28202", .driver_data = (kernel_ulong_t)&sc28202, },
+ { .name = "sc68681", .driver_data = (kernel_ulong_t)&sc68681, },
+ { .name = "sc68692", .driver_data = (kernel_ulong_t)&sc68692, },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
+
static int sccnxp_probe(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- int chiptype = pdev->id_entry->driver_data;
struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
- int i, ret, fifosize, freq_min, freq_max;
+ int i, ret, uartclk;
struct sccnxp_port *s;
void __iomem *membase;
+ struct clk *clk;
- if (!res) {
- dev_err(&pdev->dev, "Missing memory resource data\n");
- return -EADDRNOTAVAIL;
- }
+ membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(membase))
+ return PTR_ERR(membase);
s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL);
if (!s) {
@@ -801,99 +875,38 @@ static int sccnxp_probe(struct platform_device *pdev)
spin_lock_init(&s->lock);
- /* Individual chip settings */
- switch (chiptype) {
- case SCCNXP_TYPE_SC2681:
- s->name = "SC2681";
- s->uart.nr = 2;
- s->freq_std = 3686400;
- s->addr_mask = 0x0f;
- s->flags = SCCNXP_HAVE_IO;
- fifosize = 3;
- freq_min = 1000000;
- freq_max = 4000000;
- break;
- case SCCNXP_TYPE_SC2691:
- s->name = "SC2691";
- s->uart.nr = 1;
- s->freq_std = 3686400;
- s->addr_mask = 0x07;
- s->flags = 0;
- fifosize = 3;
- freq_min = 1000000;
- freq_max = 4000000;
- break;
- case SCCNXP_TYPE_SC2692:
- s->name = "SC2692";
- s->uart.nr = 2;
- s->freq_std = 3686400;
- s->addr_mask = 0x0f;
- s->flags = SCCNXP_HAVE_IO;
- fifosize = 3;
- freq_min = 1000000;
- freq_max = 4000000;
- break;
- case SCCNXP_TYPE_SC2891:
- s->name = "SC2891";
- s->uart.nr = 1;
- s->freq_std = 3686400;
- s->addr_mask = 0x0f;
- s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
- fifosize = 16;
- freq_min = 100000;
- freq_max = 8000000;
- break;
- case SCCNXP_TYPE_SC2892:
- s->name = "SC2892";
- s->uart.nr = 2;
- s->freq_std = 3686400;
- s->addr_mask = 0x0f;
- s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
- fifosize = 16;
- freq_min = 100000;
- freq_max = 8000000;
- break;
- case SCCNXP_TYPE_SC28202:
- s->name = "SC28202";
- s->uart.nr = 2;
- s->freq_std = 14745600;
- s->addr_mask = 0x7f;
- s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0;
- fifosize = 256;
- freq_min = 1000000;
- freq_max = 50000000;
- break;
- case SCCNXP_TYPE_SC68681:
- s->name = "SC68681";
- s->uart.nr = 2;
- s->freq_std = 3686400;
- s->addr_mask = 0x0f;
- s->flags = SCCNXP_HAVE_IO;
- fifosize = 3;
- freq_min = 1000000;
- freq_max = 4000000;
- break;
- case SCCNXP_TYPE_SC68692:
- s->name = "SC68692";
- s->uart.nr = 2;
- s->freq_std = 3686400;
- s->addr_mask = 0x0f;
- s->flags = SCCNXP_HAVE_IO;
- fifosize = 3;
- freq_min = 1000000;
- freq_max = 4000000;
- break;
- default:
- dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype);
- ret = -ENOTSUPP;
+ s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data;
+
+ s->regulator = devm_regulator_get(&pdev->dev, "vcc");
+ if (!IS_ERR(s->regulator)) {
+ ret = regulator_enable(s->regulator);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to enable regulator: %i\n", ret);
+ return ret;
+ }
+ } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_out;
+ }
+ dev_notice(&pdev->dev, "Using default clock frequency\n");
+ uartclk = s->chip->freq_std;
+ } else
+ uartclk = clk_get_rate(clk);
+
+ /* Check input frequency */
+ if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) {
+ dev_err(&pdev->dev, "Frequency out of bounds\n");
+ ret = -EINVAL;
goto err_out;
}
- if (!pdata) {
- dev_warn(&pdev->dev,
- "No platform data supplied, using defaults\n");
- s->pdata.frequency = s->freq_std;
- } else
+ if (pdata)
memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
if (s->pdata.poll_time_us) {
@@ -911,34 +924,11 @@ static int sccnxp_probe(struct platform_device *pdev)
}
}
- /* Check input frequency */
- if ((s->pdata.frequency < freq_min) ||
- (s->pdata.frequency > freq_max)) {
- dev_err(&pdev->dev, "Frequency out of bounds\n");
- ret = -EINVAL;
- goto err_out;
- }
-
- s->regulator = devm_regulator_get(&pdev->dev, "VCC");
- if (!IS_ERR(s->regulator)) {
- ret = regulator_enable(s->regulator);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to enable regulator: %i\n", ret);
- return ret;
- }
- }
-
- membase = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(membase)) {
- ret = PTR_ERR(membase);
- goto err_out;
- }
-
s->uart.owner = THIS_MODULE;
s->uart.dev_name = "ttySC";
s->uart.major = SCCNXP_MAJOR;
s->uart.minor = SCCNXP_MINOR;
+ s->uart.nr = s->chip->nr;
#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
s->uart.cons = &s->console;
s->uart.cons->device = uart_console_device;
@@ -960,17 +950,17 @@ static int sccnxp_probe(struct platform_device *pdev)
s->port[i].dev = &pdev->dev;
s->port[i].irq = s->irq;
s->port[i].type = PORT_SC26XX;
- s->port[i].fifosize = fifosize;
+ s->port[i].fifosize = s->chip->fifosize;
s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
s->port[i].iotype = UPIO_MEM;
s->port[i].mapbase = res->start;
s->port[i].membase = membase;
s->port[i].regshift = s->pdata.reg_shift;
- s->port[i].uartclk = s->pdata.frequency;
+ s->port[i].uartclk = uartclk;
s->port[i].ops = &sccnxp_ops;
uart_add_one_port(&s->uart, &s->port[i]);
/* Set direction to input */
- if (s->flags & SCCNXP_HAVE_IO)
+ if (s->chip->flags & SCCNXP_HAVE_IO)
sccnxp_set_bit(&s->port[i], DIR_OP, 0);
}
@@ -996,8 +986,10 @@ static int sccnxp_probe(struct platform_device *pdev)
return 0;
}
+ uart_unregister_driver(&s->uart);
err_out:
- platform_set_drvdata(pdev, NULL);
+ if (!IS_ERR(s->regulator))
+ return regulator_disable(s->regulator);
return ret;
}
@@ -1016,7 +1008,6 @@ static int sccnxp_remove(struct platform_device *pdev)
uart_remove_one_port(&s->uart, &s->port[i]);
uart_unregister_driver(&s->uart);
- platform_set_drvdata(pdev, NULL);
if (!IS_ERR(s->regulator))
return regulator_disable(s->regulator);
@@ -1024,19 +1015,6 @@ static int sccnxp_remove(struct platform_device *pdev)
return 0;
}
-static const struct platform_device_id sccnxp_id_table[] = {
- { "sc2681", SCCNXP_TYPE_SC2681 },
- { "sc2691", SCCNXP_TYPE_SC2691 },
- { "sc2692", SCCNXP_TYPE_SC2692 },
- { "sc2891", SCCNXP_TYPE_SC2891 },
- { "sc2892", SCCNXP_TYPE_SC2892 },
- { "sc28202", SCCNXP_TYPE_SC28202 },
- { "sc68681", SCCNXP_TYPE_SC68681 },
- { "sc68692", SCCNXP_TYPE_SC68692 },
- { },
-};
-MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
-
static struct platform_driver sccnxp_uart_driver = {
.driver = {
.name = SCCNXP_NAME,
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index ee7c8123c37..d5c2a287b7e 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -34,6 +34,7 @@
#include <linux/of_device.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include <linux/serial_core.h>
@@ -44,8 +45,6 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
-#include <linux/clk/tegra.h>
-
#define TEGRA_UART_TYPE "TEGRA_UART"
#define TX_EMPTY_STATUS (UART_LSR_TEMT | UART_LSR_THRE)
#define BYTES_TO_ALIGN(x) ((unsigned long)(x) & 0x3)
@@ -103,6 +102,7 @@ struct tegra_uart_port {
const struct tegra_uart_chip_data *cdata;
struct clk *uart_clk;
+ struct reset_control *rst;
unsigned int current_baud;
/* Register shadow */
@@ -120,7 +120,6 @@ struct tegra_uart_port {
bool rx_timeout;
int rx_in_progress;
int symb_bit;
- int dma_req_sel;
struct dma_chan *rx_dma_chan;
struct dma_chan *tx_dma_chan;
@@ -571,7 +570,9 @@ static void tegra_uart_rx_dma_complete(void *args)
tegra_uart_handle_rx_pio(tup, port);
if (tty) {
+ spin_unlock_irqrestore(&u->lock, flags);
tty_flip_buffer_push(port);
+ spin_lock_irqsave(&u->lock, flags);
tty_kref_put(tty);
}
tegra_uart_start_rx_dma(tup);
@@ -583,11 +584,13 @@ static void tegra_uart_rx_dma_complete(void *args)
spin_unlock_irqrestore(&u->lock, flags);
}
-static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup)
+static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup,
+ unsigned long *flags)
{
struct dma_tx_state state;
struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
struct tty_port *port = &tup->uport.state->port;
+ struct uart_port *u = &tup->uport;
int count;
/* Deactivate flow control to stop sender */
@@ -604,7 +607,9 @@ static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup)
tegra_uart_handle_rx_pio(tup, port);
if (tty) {
+ spin_unlock_irqrestore(&u->lock, *flags);
tty_flip_buffer_push(port);
+ spin_lock_irqsave(&u->lock, *flags);
tty_kref_put(tty);
}
tegra_uart_start_rx_dma(tup);
@@ -671,7 +676,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
iir = tegra_uart_read(tup, UART_IIR);
if (iir & UART_IIR_NO_INT) {
if (is_rx_int) {
- tegra_uart_handle_rx_dma(tup);
+ tegra_uart_handle_rx_dma(tup, &flags);
if (tup->rx_in_progress) {
ier = tup->ier_shadow;
ier |= (UART_IER_RLSI | UART_IER_RTOIE |
@@ -726,7 +731,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
static void tegra_uart_stop_rx(struct uart_port *u)
{
struct tegra_uart_port *tup = to_tegra_uport(u);
- struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ struct tty_struct *tty;
struct tty_port *port = &u->state->port;
struct dma_tx_state state;
unsigned long ier;
@@ -738,6 +743,8 @@ static void tegra_uart_stop_rx(struct uart_port *u)
if (!tup->rx_in_progress)
return;
+ tty = tty_port_tty_get(&tup->uport.state->port);
+
tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */
ier = tup->ier_shadow;
@@ -824,9 +831,9 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
clk_prepare_enable(tup->uart_clk);
/* Reset the UART controller to clear all previous status.*/
- tegra_periph_reset_assert(tup->uart_clk);
+ reset_control_assert(tup->rst);
udelay(10);
- tegra_periph_reset_deassert(tup->uart_clk);
+ reset_control_deassert(tup->rst);
tup->rx_in_progress = 0;
tup->tx_in_progress = 0;
@@ -902,15 +909,14 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
dma_addr_t dma_phys;
int ret;
struct dma_slave_config dma_sconfig;
- dma_cap_mask_t mask;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_chan = dma_request_channel(mask, NULL, NULL);
- if (!dma_chan) {
+ dma_chan = dma_request_slave_channel_reason(tup->uport.dev,
+ dma_to_memory ? "rx" : "tx");
+ if (IS_ERR(dma_chan)) {
+ ret = PTR_ERR(dma_chan);
dev_err(tup->uport.dev,
- "Dma channel is not available, will try later\n");
- return -EPROBE_DEFER;
+ "DMA channel alloc failed: %d\n", ret);
+ return ret;
}
if (dma_to_memory) {
@@ -930,7 +936,6 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
dma_buf = tup->uport.state->xmit.buf;
}
- dma_sconfig.slave_id = tup->dma_req_sel;
if (dma_to_memory) {
dma_sconfig.src_addr = tup->uport.mapbase;
dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
@@ -1010,7 +1015,7 @@ static int tegra_uart_startup(struct uart_port *u)
goto fail_hw_init;
}
- ret = request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED,
+ ret = request_irq(u->irq, tegra_uart_isr, 0,
dev_name(u->dev), tup);
if (ret < 0) {
dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq);
@@ -1206,7 +1211,7 @@ static struct uart_driver tegra_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "tegra_hsuart",
.dev_name = "ttyTHS",
- .cons = 0,
+ .cons = NULL,
.nr = TEGRA_UART_MAXIMUM,
};
@@ -1214,17 +1219,8 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,
struct tegra_uart_port *tup)
{
struct device_node *np = pdev->dev.of_node;
- u32 of_dma[2];
int port;
- if (of_property_read_u32_array(np, "nvidia,dma-request-selector",
- of_dma, 2) >= 0) {
- tup->dma_req_sel = of_dma[1];
- } else {
- dev_err(&pdev->dev, "missing dma requestor in device tree\n");
- return -EINVAL;
- }
-
port = of_alias_get_id(np, "serial");
if (port < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", port);
@@ -1237,13 +1233,13 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,
return 0;
}
-struct tegra_uart_chip_data tegra20_uart_chip_data = {
+static struct tegra_uart_chip_data tegra20_uart_chip_data = {
.tx_fifo_full_status = false,
.allow_txfifo_reset_fifo_mode = true,
.support_clk_src_div = false,
};
-struct tegra_uart_chip_data tegra30_uart_chip_data = {
+static struct tegra_uart_chip_data tegra30_uart_chip_data = {
.tx_fifo_full_status = true,
.allow_txfifo_reset_fifo_mode = false,
.support_clk_src_div = true,
@@ -1312,6 +1308,12 @@ static int tegra_uart_probe(struct platform_device *pdev)
return PTR_ERR(tup->uart_clk);
}
+ tup->rst = devm_reset_control_get(&pdev->dev, "serial");
+ if (IS_ERR(tup->rst)) {
+ dev_err(&pdev->dev, "Couldn't get the reset\n");
+ return PTR_ERR(tup->rst);
+ }
+
u->iotype = UPIO_MEM32;
u->irq = platform_get_irq(pdev, 0);
u->regshift = 2;
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 28cdd282913..fbf6c5ad222 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -89,8 +89,7 @@ static void __uart_start(struct tty_struct *tty)
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
- if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
- !tty->stopped && !tty->hw_stopped)
+ if (!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);
}
@@ -138,6 +137,11 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
return 1;
/*
+ * Make sure the device is in D0 state.
+ */
+ uart_change_pm(state, UART_PM_STATE_ON);
+
+ /*
* Initialise and allocate the transmit and temporary
* buffer.
*/
@@ -170,8 +174,12 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
if (tty->termios.c_cflag & CBAUD)
uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
}
-
- if (tty_port_cts_enabled(port)) {
+ /*
+ * if hw support flow control without software intervention,
+ * then skip the below check
+ */
+ if (tty_port_cts_enabled(port) &&
+ !(uport->flags & UPF_HARD_FLOW)) {
spin_lock_irq(&uport->lock);
if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
tty->hw_stopped = 1;
@@ -826,25 +834,29 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
* If we fail to request resources for the
* new port, try to restore the old settings.
*/
- if (retval && old_type != PORT_UNKNOWN) {
+ if (retval) {
uport->iobase = old_iobase;
uport->type = old_type;
uport->hub6 = old_hub6;
uport->iotype = old_iotype;
uport->regshift = old_shift;
uport->mapbase = old_mapbase;
- retval = uport->ops->request_port(uport);
- /*
- * If we failed to restore the old settings,
- * we fail like this.
- */
- if (retval)
- uport->type = PORT_UNKNOWN;
- /*
- * We failed anyway.
- */
- retval = -EBUSY;
+ if (old_type != PORT_UNKNOWN) {
+ retval = uport->ops->request_port(uport);
+ /*
+ * If we failed to restore the old settings,
+ * we fail like this.
+ */
+ if (retval)
+ uport->type = PORT_UNKNOWN;
+
+ /*
+ * We failed anyway.
+ */
+ retval = -EBUSY;
+ }
+
/* Added to return the correct error -Ram Gupta */
goto exit;
}
@@ -1319,9 +1331,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
uport = state->uart_port;
port = &state->port;
- pr_debug("uart_close(%d) called\n", uport->line);
+ pr_debug("uart_close(%d) called\n", uport ? uport->line : -1);
- if (tty_port_close_start(port, tty, filp) == 0)
+ if (!port->count || tty_port_close_start(port, tty, filp) == 0)
return;
/*
@@ -1452,6 +1464,8 @@ static void uart_hangup(struct tty_struct *tty)
clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
spin_unlock_irqrestore(&port->lock, flags);
tty_port_tty_set(port, NULL);
+ if (!uart_console(state->uart_port))
+ uart_change_pm(state, UART_PM_STATE_OFF);
wake_up_interruptible(&port->open_wait);
wake_up_interruptible(&port->delta_msr_wait);
}
@@ -1570,12 +1584,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
}
/*
- * Make sure the device is in D0 state.
- */
- if (port->count == 1)
- uart_change_pm(state, UART_PM_STATE_ON);
-
- /*
* Start up the serial port.
*/
retval = uart_startup(tty, state, 0);
@@ -1762,7 +1770,7 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
}
/**
- * uart_parse_options - Parse serial port baud/parity/bits/flow contro.
+ * uart_parse_options - Parse serial port baud/parity/bits/flow control.
* @options: pointer to option string
* @baud: pointer to an 'int' variable for the baud rate.
* @parity: pointer to an 'int' variable for the parity.
@@ -1830,9 +1838,13 @@ uart_set_options(struct uart_port *port, struct console *co,
/*
* Ensure that the serial console lock is initialised
* early.
+ * If this port is a console, then the spinlock is already
+ * initialised.
*/
- spin_lock_init(&port->lock);
- lockdep_set_class(&port->lock, &port_lock_key);
+ if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
+ spin_lock_init(&port->lock);
+ lockdep_set_class(&port->lock, &port_lock_key);
+ }
memset(&termios, 0, sizeof(struct ktermios));
@@ -2095,12 +2107,12 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
break;
}
- printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n",
+ printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n",
port->dev ? dev_name(port->dev) : "",
port->dev ? ": " : "",
drv->dev_name,
drv->tty_driver->name_base + port->line,
- address, port->irq, uart_type(port));
+ address, port->irq, port->uartclk / 16, uart_type(port));
}
static void
@@ -2231,6 +2243,9 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
return;
port = state->uart_port;
+
+ if (ch == '\n')
+ port->ops->poll_put_char(port, '\r');
port->ops->poll_put_char(port, ch);
}
#endif
@@ -2605,7 +2620,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
/*
* Register the port whether it's detected or not. This allows
- * setserial to be used to alter this ports parameters.
+ * setserial to be used to alter this port's parameters.
*/
tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
uport->line, uport->dev, port, tty_dev_attr_groups);
@@ -2641,6 +2656,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state = drv->state + uport->line;
struct tty_port *port = &state->port;
+ struct tty_struct *tty;
int ret = 0;
BUG_ON(in_interrupt());
@@ -2669,8 +2685,17 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
*/
tty_unregister_device(drv->tty_driver, uport->line);
- if (port->tty)
+ tty = tty_port_tty_get(port);
+ if (tty) {
tty_vhangup(port->tty);
+ tty_kref_put(tty);
+ }
+
+ /*
+ * If the port is used as a console, unregister it
+ */
+ if (uart_console(uport))
+ unregister_console(uport->cons);
/*
* Free the port IO and memory resources, if any.
@@ -2754,7 +2779,9 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
uport->icount.cts++;
- if (tty_port_cts_enabled(port)) {
+ /* skip below code if the hw flow control is supported */
+ if (tty_port_cts_enabled(port) &&
+ !(uport->flags & UPF_HARD_FLOW)) {
if (tty->hw_stopped) {
if (status) {
tty->hw_stopped = 0;
diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c
index e1caa99e3d3..5c79bdab985 100644
--- a/drivers/tty/serial/serial_ks8695.c
+++ b/drivers/tty/serial/serial_ks8695.c
@@ -437,7 +437,7 @@ static void ks8695uart_set_termios(struct uart_port *port, struct ktermios *term
port->read_status_mask = URLS_URROE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= (URLS_URFE | URLS_URPE);
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= URLS_URBI;
/*
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
new file mode 100644
index 00000000000..bf9560ffe3f
--- /dev/null
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -0,0 +1,143 @@
+/*
+ * Helpers for controlling modem lines via GPIO
+ *
+ * Copyright (C) 2014 Paratronic S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <uapi/asm-generic/termios.h>
+
+#include "serial_mctrl_gpio.h"
+
+struct mctrl_gpios {
+ struct gpio_desc *gpio[UART_GPIO_MAX];
+};
+
+static const struct {
+ const char *name;
+ unsigned int mctrl;
+ bool dir_out;
+} mctrl_gpios_desc[UART_GPIO_MAX] = {
+ { "cts", TIOCM_CTS, false, },
+ { "dsr", TIOCM_DSR, false, },
+ { "dcd", TIOCM_CD, false, },
+ { "rng", TIOCM_RNG, false, },
+ { "rts", TIOCM_RTS, true, },
+ { "dtr", TIOCM_DTR, true, },
+ { "out1", TIOCM_OUT1, true, },
+ { "out2", TIOCM_OUT2, true, },
+};
+
+void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
+{
+ enum mctrl_gpio_idx i;
+
+ if (IS_ERR_OR_NULL(gpios))
+ return;
+
+ for (i = 0; i < UART_GPIO_MAX; i++)
+ if (!IS_ERR_OR_NULL(gpios->gpio[i]) &&
+ mctrl_gpios_desc[i].dir_out)
+ gpiod_set_value(gpios->gpio[i],
+ !!(mctrl & mctrl_gpios_desc[i].mctrl));
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_set);
+
+struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
+ enum mctrl_gpio_idx gidx)
+{
+ if (!IS_ERR_OR_NULL(gpios) && !IS_ERR_OR_NULL(gpios->gpio[gidx]))
+ return gpios->gpio[gidx];
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
+
+unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
+{
+ enum mctrl_gpio_idx i;
+
+ /*
+ * return it unchanged if the structure is not allocated
+ */
+ if (IS_ERR_OR_NULL(gpios))
+ return *mctrl;
+
+ for (i = 0; i < UART_GPIO_MAX; i++) {
+ if (!IS_ERR_OR_NULL(gpios->gpio[i]) &&
+ !mctrl_gpios_desc[i].dir_out) {
+ if (gpiod_get_value(gpios->gpio[i]))
+ *mctrl |= mctrl_gpios_desc[i].mctrl;
+ else
+ *mctrl &= ~mctrl_gpios_desc[i].mctrl;
+ }
+ }
+
+ return *mctrl;
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_get);
+
+struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx)
+{
+ struct mctrl_gpios *gpios;
+ enum mctrl_gpio_idx i;
+ int err;
+
+ gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
+ if (!gpios)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < UART_GPIO_MAX; i++) {
+ gpios->gpio[i] = devm_gpiod_get_index(dev,
+ mctrl_gpios_desc[i].name,
+ idx);
+
+ /*
+ * The GPIOs are maybe not all filled,
+ * this is not an error.
+ */
+ if (IS_ERR_OR_NULL(gpios->gpio[i]))
+ continue;
+
+ if (mctrl_gpios_desc[i].dir_out)
+ err = gpiod_direction_output(gpios->gpio[i], 0);
+ else
+ err = gpiod_direction_input(gpios->gpio[i]);
+ if (err) {
+ dev_dbg(dev, "Unable to set direction for %s GPIO",
+ mctrl_gpios_desc[i].name);
+ devm_gpiod_put(dev, gpios->gpio[i]);
+ gpios->gpio[i] = NULL;
+ }
+ }
+
+ return gpios;
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_init);
+
+void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
+{
+ enum mctrl_gpio_idx i;
+
+ if (IS_ERR_OR_NULL(gpios))
+ return;
+
+ for (i = 0; i < UART_GPIO_MAX; i++)
+ if (!IS_ERR_OR_NULL(gpios->gpio[i]))
+ devm_gpiod_put(dev, gpios->gpio[i]);
+ devm_kfree(dev, gpios);
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_free);
diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h
new file mode 100644
index 00000000000..400ba0494a1
--- /dev/null
+++ b/drivers/tty/serial/serial_mctrl_gpio.h
@@ -0,0 +1,110 @@
+/*
+ * Helpers for controlling modem lines via GPIO
+ *
+ * Copyright (C) 2014 Paratronic S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SERIAL_MCTRL_GPIO__
+#define __SERIAL_MCTRL_GPIO__
+
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+
+enum mctrl_gpio_idx {
+ UART_GPIO_CTS,
+ UART_GPIO_DSR,
+ UART_GPIO_DCD,
+ UART_GPIO_RNG,
+ UART_GPIO_RI = UART_GPIO_RNG,
+ UART_GPIO_RTS,
+ UART_GPIO_DTR,
+ UART_GPIO_OUT1,
+ UART_GPIO_OUT2,
+ UART_GPIO_MAX,
+};
+
+/*
+ * Opaque descriptor for modem lines controlled by GPIOs
+ */
+struct mctrl_gpios;
+
+#ifdef CONFIG_GPIOLIB
+
+/*
+ * Set state of the modem control output lines via GPIOs.
+ */
+void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl);
+
+/*
+ * Get state of the modem control output lines from GPIOs.
+ * The mctrl flags are updated and returned.
+ */
+unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl);
+
+/*
+ * Returns the associated struct gpio_desc to the modem line gidx
+ */
+struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
+ enum mctrl_gpio_idx gidx);
+
+/*
+ * Request and set direction of modem control lines GPIOs.
+ * devm_* functions are used, so there's no need to call mctrl_gpio_free().
+ * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on
+ * allocation error.
+ */
+struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx);
+
+/*
+ * Free the mctrl_gpios structure.
+ * Normally, this function will not be called, as the GPIOs will
+ * be disposed of by the resource management code.
+ */
+void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios);
+
+#else /* GPIOLIB */
+
+static inline
+void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
+{
+}
+
+static inline
+unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
+{
+ return *mctrl;
+}
+
+static inline
+struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
+ enum mctrl_gpio_idx gidx)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline
+struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline
+void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
+{
+}
+
+#endif /* GPIOLIB */
+
+#endif
diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c
index fe48a0c2b4c..ea8546092c7 100644
--- a/drivers/tty/serial/serial_txx9.c
+++ b/drivers/tty/serial/serial_txx9.c
@@ -535,13 +535,8 @@ static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c)
wait_for_xmitr(up);
/*
* Send the character out.
- * If a LF, also do CR...
*/
sio_out(up, TXX9_SITFIFO, c);
- if (c == 10) {
- wait_for_xmitr(up);
- sio_out(up, TXX9_SITFIFO, 13);
- }
/*
* Finally, wait for transmitter to become empty
@@ -702,7 +697,7 @@ serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios,
TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS;
if (termios->c_iflag & INPCK)
up->port.read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= TXX9_SIDISR_UBRK;
/*
@@ -1097,7 +1092,7 @@ static void serial_txx9_unregister_port(int line)
*/
static int serial_txx9_probe(struct platform_device *dev)
{
- struct uart_port *p = dev->dev.platform_data;
+ struct uart_port *p = dev_get_platdata(&dev->dev);
struct uart_port port;
int ret, i;
@@ -1220,8 +1215,6 @@ static void pciserial_txx9_remove_one(struct pci_dev *dev)
{
struct uart_txx9_port *up = pci_get_drvdata(dev);
- pci_set_drvdata(dev, NULL);
-
if (up) {
serial_txx9_unregister_port(up->port.line);
pci_disable_device(dev);
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 7477e0ea5cd..88236da0ddf 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -23,35 +23,35 @@
#undef DEBUG
-#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/ctype.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
#include <linux/errno.h>
-#include <linux/sh_dma.h>
-#include <linux/timer.h>
+#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/sysrq.h>
#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/console.h>
-#include <linux/platform_device.h>
-#include <linux/serial_sci.h>
#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/cpufreq.h>
-#include <linux/clk.h>
-#include <linux/ctype.h>
-#include <linux/err.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
+#include <linux/serial.h>
+#include <linux/serial_sci.h>
+#include <linux/sh_dma.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
+#include <linux/string.h>
+#include <linux/sysrq.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
#ifdef CONFIG_SUPERH
#include <asm/sh_bios.h>
@@ -59,11 +59,32 @@
#include "sh-sci.h"
+/* Offsets into the sci_port->irqs array */
+enum {
+ SCIx_ERI_IRQ,
+ SCIx_RXI_IRQ,
+ SCIx_TXI_IRQ,
+ SCIx_BRI_IRQ,
+ SCIx_NR_IRQS,
+
+ SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */
+};
+
+#define SCIx_IRQ_IS_MUXED(port) \
+ ((port)->irqs[SCIx_ERI_IRQ] == \
+ (port)->irqs[SCIx_RXI_IRQ]) || \
+ ((port)->irqs[SCIx_ERI_IRQ] && \
+ ((port)->irqs[SCIx_RXI_IRQ] < 0))
+
struct sci_port {
struct uart_port port;
/* Platform configuration */
struct plat_sci_port *cfg;
+ int overrun_bit;
+ unsigned int error_mask;
+ unsigned int sampling_rate;
+
/* Break timer */
struct timer_list break_timer;
@@ -74,8 +95,8 @@ struct sci_port {
/* Function clock */
struct clk *fclk;
+ int irqs[SCIx_NR_IRQS];
char *irqstr[SCIx_NR_IRQS];
- char *gpiostr[SCIx_NR_FNS];
struct dma_chan *chan_tx;
struct dma_chan *chan_rx;
@@ -407,7 +428,7 @@ static int sci_probe_regmap(struct plat_sci_port *cfg)
cfg->regtype = SCIx_HSCIF_REGTYPE;
break;
default:
- printk(KERN_ERR "Can't probe register map for given port\n");
+ pr_err("Can't probe register map for given port\n");
return -EINVAL;
}
@@ -421,9 +442,9 @@ static void sci_port_enable(struct sci_port *sci_port)
pm_runtime_get_sync(sci_port->port.dev);
- clk_enable(sci_port->iclk);
+ clk_prepare_enable(sci_port->iclk);
sci_port->port.uartclk = clk_get_rate(sci_port->iclk);
- clk_enable(sci_port->fclk);
+ clk_prepare_enable(sci_port->fclk);
}
static void sci_port_disable(struct sci_port *sci_port)
@@ -431,8 +452,16 @@ static void sci_port_disable(struct sci_port *sci_port)
if (!sci_port->port.dev)
return;
- clk_disable(sci_port->fclk);
- clk_disable(sci_port->iclk);
+ /* Cancel the break timer to ensure that the timer handler will not try
+ * to access the hardware with clocks and power disabled. Reset the
+ * break flag to make the break debouncing state machine ready for the
+ * next break.
+ */
+ del_timer_sync(&sci_port->break_timer);
+ sci_port->break_flag = 0;
+
+ clk_disable_unprepare(sci_port->fclk);
+ clk_disable_unprepare(sci_port->iclk);
pm_runtime_put_sync(sci_port->port.dev);
}
@@ -557,7 +586,7 @@ static inline int sci_rxd_in(struct uart_port *port)
return 1;
/* Cast for ARM damage */
- return !!__raw_readb((void __iomem *)s->cfg->port_reg);
+ return !!__raw_readb((void __iomem *)(uintptr_t)s->cfg->port_reg);
}
/* ********************************************************************** *
@@ -733,8 +762,6 @@ static void sci_break_timer(unsigned long data)
{
struct sci_port *port = (struct sci_port *)data;
- sci_port_enable(port);
-
if (sci_rxd_in(&port->port) == 0) {
port->break_flag = 1;
sci_schedule_break_timer(port);
@@ -744,8 +771,6 @@ static void sci_break_timer(unsigned long data)
sci_schedule_break_timer(port);
} else
port->break_flag = 0;
-
- sci_port_disable(port);
}
static int sci_handle_errors(struct uart_port *port)
@@ -755,19 +780,15 @@ static int sci_handle_errors(struct uart_port *port)
struct tty_port *tport = &port->state->port;
struct sci_port *s = to_sci_port(port);
- /*
- * Handle overruns, if supported.
- */
- if (s->cfg->overrun_bit != SCIx_NOT_SUPPORTED) {
- if (status & (1 << s->cfg->overrun_bit)) {
- port->icount.overrun++;
+ /* Handle overruns */
+ if (status & (1 << s->overrun_bit)) {
+ port->icount.overrun++;
- /* overrun error */
- if (tty_insert_flip_char(tport, 0, TTY_OVERRUN))
- copied++;
+ /* overrun error */
+ if (tty_insert_flip_char(tport, 0, TTY_OVERRUN))
+ copied++;
- dev_notice(port->dev, "overrun error");
- }
+ dev_notice(port->dev, "overrun error\n");
}
if (status & SCxSR_FER(port)) {
@@ -809,7 +830,7 @@ static int sci_handle_errors(struct uart_port *port)
if (tty_insert_flip_char(tport, 0, TTY_PARITY))
copied++;
- dev_notice(port->dev, "parity error");
+ dev_notice(port->dev, "parity error\n");
}
if (copied)
@@ -829,7 +850,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
if (!reg->size)
return 0;
- if ((serial_port_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) {
+ if ((serial_port_in(port, SCLSR) & (1 << s->overrun_bit))) {
serial_port_out(port, SCLSR, 0);
port->icount.overrun++;
@@ -890,7 +911,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
/* Disable future Rx interrupts */
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
disable_irq_nosync(irq);
- scr |= 0x4000;
+ scr |= SCSCR_RDRQE;
} else {
scr &= ~SCSCR_RIE;
}
@@ -1020,8 +1041,7 @@ static int sci_notifier(struct notifier_block *self,
sci_port = container_of(self, struct sci_port, freq_transition);
- if ((phase == CPUFREQ_POSTCHANGE) ||
- (phase == CPUFREQ_RESUMECHANGE)) {
+ if (phase == CPUFREQ_POSTCHANGE) {
struct uart_port *port = &sci_port->port;
spin_lock_irqsave(&port->lock, flags);
@@ -1075,19 +1095,19 @@ static int sci_request_irq(struct sci_port *port)
for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) {
struct sci_irq_desc *desc;
- unsigned int irq;
+ int irq;
if (SCIx_IRQ_IS_MUXED(port)) {
i = SCIx_MUX_IRQ;
irq = up->irq;
} else {
- irq = port->cfg->irqs[i];
+ irq = port->irqs[i];
/*
* Certain port types won't support all of the
* available interrupt sources.
*/
- if (unlikely(!irq))
+ if (unlikely(irq < 0))
continue;
}
@@ -1112,7 +1132,7 @@ static int sci_request_irq(struct sci_port *port)
out_noirq:
while (--i >= 0)
- free_irq(port->cfg->irqs[i], port);
+ free_irq(port->irqs[i], port);
out_nomem:
while (--j >= 0)
@@ -1130,16 +1150,16 @@ static void sci_free_irq(struct sci_port *port)
* IRQ first.
*/
for (i = 0; i < SCIx_NR_IRQS; i++) {
- unsigned int irq = port->cfg->irqs[i];
+ int irq = port->irqs[i];
/*
* Certain port types won't support all of the available
* interrupt sources.
*/
- if (unlikely(!irq))
+ if (unlikely(irq < 0))
continue;
- free_irq(port->cfg->irqs[i], port);
+ free_irq(port->irqs[i], port);
kfree(port->irqstr[i]);
if (SCIx_IRQ_IS_MUXED(port)) {
@@ -1149,67 +1169,6 @@ static void sci_free_irq(struct sci_port *port)
}
}
-static const char *sci_gpio_names[SCIx_NR_FNS] = {
- "sck", "rxd", "txd", "cts", "rts",
-};
-
-static const char *sci_gpio_str(unsigned int index)
-{
- return sci_gpio_names[index];
-}
-
-static void sci_init_gpios(struct sci_port *port)
-{
- struct uart_port *up = &port->port;
- int i;
-
- if (!port->cfg)
- return;
-
- for (i = 0; i < SCIx_NR_FNS; i++) {
- const char *desc;
- int ret;
-
- if (!port->cfg->gpios[i])
- continue;
-
- desc = sci_gpio_str(i);
-
- port->gpiostr[i] = kasprintf(GFP_KERNEL, "%s:%s",
- dev_name(up->dev), desc);
-
- /*
- * If we've failed the allocation, we can still continue
- * on with a NULL string.
- */
- if (!port->gpiostr[i])
- dev_notice(up->dev, "%s string allocation failure\n",
- desc);
-
- ret = gpio_request(port->cfg->gpios[i], port->gpiostr[i]);
- if (unlikely(ret != 0)) {
- dev_notice(up->dev, "failed %s gpio request\n", desc);
-
- /*
- * If we can't get the GPIO for whatever reason,
- * no point in keeping the verbose string around.
- */
- kfree(port->gpiostr[i]);
- }
- }
-}
-
-static void sci_free_gpios(struct sci_port *port)
-{
- int i;
-
- for (i = 0; i < SCIx_NR_FNS; i++)
- if (port->cfg->gpios[i]) {
- gpio_free(port->cfg->gpios[i]);
- kfree(port->gpiostr[i]);
- }
-}
-
static unsigned int sci_tx_empty(struct uart_port *port)
{
unsigned short status = serial_port_in(port, SCxSR);
@@ -1240,7 +1199,9 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
*/
reg = sci_getreg(port, SCFCR);
if (reg->size)
- serial_port_out(port, SCFCR, serial_port_in(port, SCFCR) | 1);
+ serial_port_out(port, SCFCR,
+ serial_port_in(port, SCFCR) |
+ SCFCR_LOOP);
}
}
@@ -1309,7 +1270,7 @@ static int sci_dma_rx_push(struct sci_port *s, size_t count)
}
if (room < count)
- dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
+ dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n",
count - room);
if (!room)
return room;
@@ -1330,7 +1291,8 @@ static void sci_dma_rx_complete(void *arg)
unsigned long flags;
int count;
- dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx);
+ dev_dbg(port->dev, "%s(%d) active #%d\n",
+ __func__, port->line, s->active_rx);
spin_lock_irqsave(&port->lock, flags);
@@ -1406,8 +1368,8 @@ static void sci_submit_rx(struct sci_port *s)
sci_rx_dma_release(s, true);
return;
}
- dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__,
- s->cookie_rx[i], i);
+ dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n",
+ __func__, s->cookie_rx[i], i);
}
s->active_rx = s->cookie_rx[0];
@@ -1433,7 +1395,7 @@ static void work_fn_rx(struct work_struct *work)
desc = s->desc_rx[new];
if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) !=
- DMA_SUCCESS) {
+ DMA_COMPLETE) {
/* Handle incomplete DMA receive */
struct dma_chan *chan = s->chan_rx;
struct shdma_desc *sh_desc = container_of(desc,
@@ -1442,7 +1404,7 @@ static void work_fn_rx(struct work_struct *work)
int count;
chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
- dev_dbg(port->dev, "Read %u bytes with cookie %d\n",
+ dev_dbg(port->dev, "Read %zu bytes with cookie %d\n",
sh_desc->partial, sh_desc->cookie);
spin_lock_irqsave(&port->lock, flags);
@@ -1466,8 +1428,8 @@ static void work_fn_rx(struct work_struct *work)
s->active_rx = s->cookie_rx[!new];
- dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__,
- s->cookie_rx[new], new, s->active_rx);
+ dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n",
+ __func__, s->cookie_rx[new], new, s->active_rx);
}
static void work_fn_tx(struct work_struct *work)
@@ -1520,8 +1482,8 @@ static void work_fn_tx(struct work_struct *work)
return;
}
- dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__,
- xmit->buf, xmit->tail, xmit->head, s->cookie_tx);
+ dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n",
+ __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx);
dma_async_issue_pending(chan);
}
@@ -1536,9 +1498,9 @@ static void sci_start_tx(struct uart_port *port)
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 new, scr = serial_port_in(port, SCSCR);
if (s->chan_tx)
- new = scr | 0x8000;
+ new = scr | SCSCR_TDRQE;
else
- new = scr & ~0x8000;
+ new = scr & ~SCSCR_TDRQE;
if (new != scr)
serial_port_out(port, SCSCR, new);
}
@@ -1565,7 +1527,7 @@ static void sci_stop_tx(struct uart_port *port)
ctrl = serial_port_in(port, SCSCR);
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
- ctrl &= ~0x8000;
+ ctrl &= ~SCSCR_TDRQE;
ctrl &= ~SCSCR_TIE;
@@ -1579,7 +1541,7 @@ static void sci_start_rx(struct uart_port *port)
ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port);
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
- ctrl &= ~0x4000;
+ ctrl &= ~SCSCR_RDRQE;
serial_port_out(port, SCSCR, ctrl);
}
@@ -1591,7 +1553,7 @@ static void sci_stop_rx(struct uart_port *port)
ctrl = serial_port_in(port, SCSCR);
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
- ctrl &= ~0x4000;
+ ctrl &= ~SCSCR_RDRQE;
ctrl &= ~port_rx_irq_mask(port);
@@ -1640,8 +1602,8 @@ static bool filter(struct dma_chan *chan, void *slave)
{
struct sh_dmae_slave *param = slave;
- dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__,
- param->shdma_slave.slave_id);
+ dev_dbg(chan->device->dev, "%s: slave ID %d\n",
+ __func__, param->shdma_slave.slave_id);
chan->private = &param->shdma_slave;
return true;
@@ -1654,8 +1616,8 @@ static void rx_timer_fn(unsigned long arg)
u16 scr = serial_port_in(port, SCSCR);
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
- scr &= ~0x4000;
- enable_irq(s->cfg->irqs[1]);
+ scr &= ~SCSCR_RDRQE;
+ enable_irq(s->irqs[SCIx_RXI_IRQ]);
}
serial_port_out(port, SCSCR, scr | SCSCR_RIE);
dev_dbg(port->dev, "DMA Rx timed out\n");
@@ -1670,8 +1632,7 @@ static void sci_request_dma(struct uart_port *port)
dma_cap_mask_t mask;
int nent;
- dev_dbg(port->dev, "%s: port %d\n", __func__,
- port->line);
+ dev_dbg(port->dev, "%s: port %d\n", __func__, port->line);
if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0)
return;
@@ -1691,16 +1652,18 @@ static void sci_request_dma(struct uart_port *port)
s->chan_tx = chan;
sg_init_table(&s->sg_tx, 1);
/* UART circular tx buffer is an aligned page. */
- BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+ BUG_ON((uintptr_t)port->state->xmit.buf & ~PAGE_MASK);
sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf),
- UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK);
+ UART_XMIT_SIZE,
+ (uintptr_t)port->state->xmit.buf & ~PAGE_MASK);
nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE);
if (!nent)
sci_tx_dma_release(s, false);
else
- dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
- sg_dma_len(&s->sg_tx),
- port->state->xmit.buf, sg_dma_address(&s->sg_tx));
+ dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n",
+ __func__,
+ sg_dma_len(&s->sg_tx), port->state->xmit.buf,
+ &sg_dma_address(&s->sg_tx));
s->sg_len_tx = nent;
@@ -1740,7 +1703,7 @@ static void sci_request_dma(struct uart_port *port)
sg_init_table(sg, 1);
sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx,
- (int)buf[i] & ~PAGE_MASK);
+ (uintptr_t)buf[i] & ~PAGE_MASK);
sg_dma_address(sg) = dma[i];
}
@@ -1808,21 +1771,11 @@ static void sci_shutdown(struct uart_port *port)
sci_free_irq(s);
}
-static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps,
+static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps,
unsigned long freq)
{
- switch (algo_id) {
- case SCBRR_ALGO_1:
- return ((freq + 16 * bps) / (16 * bps) - 1);
- case SCBRR_ALGO_2:
- return ((freq + 16 * bps) / (32 * bps) - 1);
- case SCBRR_ALGO_3:
- return (((freq * 2) + 16 * bps) / (16 * bps) - 1);
- case SCBRR_ALGO_4:
- return (((freq * 2) + 16 * bps) / (32 * bps) - 1);
- case SCBRR_ALGO_5:
- return (((freq * 1000 / 32) / bps) - 1);
- }
+ if (s->sampling_rate)
+ return DIV_ROUND_CLOSEST(freq, s->sampling_rate * bps) - 1;
/* Warn, but use a safe default */
WARN_ON(1);
@@ -1903,12 +1856,11 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
baud = uart_get_baud_rate(port, termios, old, 0, max_baud);
if (likely(baud && port->uartclk)) {
- if (s->cfg->scbrr_algo_id == SCBRR_ALGO_6) {
+ if (s->cfg->type == PORT_HSCIF) {
sci_baud_calc_hscif(baud, port->uartclk, &t, &srr,
&cks);
} else {
- t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud,
- port->uartclk);
+ t = sci_scbrr_calc(s, baud, port->uartclk);
for (cks = 0; t >= 256 && cks <= 3; cks++)
t >>= 2;
}
@@ -1921,13 +1873,13 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
smr_val = serial_port_in(port, SCSMR) & 3;
if ((termios->c_cflag & CSIZE) == CS7)
- smr_val |= 0x40;
+ smr_val |= SCSMR_CHR;
if (termios->c_cflag & PARENB)
- smr_val |= 0x20;
+ smr_val |= SCSMR_PE;
if (termios->c_cflag & PARODD)
- smr_val |= 0x30;
+ smr_val |= SCSMR_PE | SCSMR_ODD;
if (termios->c_cflag & CSTOPB)
- smr_val |= 0x08;
+ smr_val |= SCSMR_STOP;
uart_update_timeout(port, termios->c_cflag, baud);
@@ -1935,7 +1887,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
__func__, smr_val, cks, t, s->cfg->scscr);
if (t >= 0) {
- serial_port_out(port, SCSMR, (smr_val & ~3) | cks);
+ serial_port_out(port, SCSMR, (smr_val & ~SCSMR_CKS) | cks);
serial_port_out(port, SCBRR, t);
reg = sci_getreg(port, HSSRR);
if (reg->size)
@@ -1983,8 +1935,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
if (s->chan_rx) {
s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 /
port->fifosize / 2;
- dev_dbg(port->dev,
- "DMA Rx t-out %ums, tty t-out %u jiffies\n",
+ dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n",
s->rx_timeout * 1000 / HZ, port->timeout);
if (s->rx_timeout < msecs_to_jiffies(20))
s->rx_timeout = msecs_to_jiffies(20);
@@ -2003,7 +1954,7 @@ static void sci_pm(struct uart_port *port, unsigned int state,
struct sci_port *sci_port = to_sci_port(port);
switch (state) {
- case 3:
+ case UART_PM_STATE_OFF:
sci_port_disable(sci_port);
break;
default:
@@ -2068,7 +2019,7 @@ static int sci_remap_port(struct uart_port *port)
* need to do any remapping, just cast the cookie
* directly.
*/
- port->membase = (void __iomem *)port->mapbase;
+ port->membase = (void __iomem *)(uintptr_t)port->mapbase;
}
return 0;
@@ -2115,10 +2066,6 @@ static void sci_config_port(struct uart_port *port, int flags)
static int sci_verify_port(struct uart_port *port, struct serial_struct *ser)
{
- struct sci_port *s = to_sci_port(port);
-
- if (ser->irq != s->cfg->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs)
- return -EINVAL;
if (ser->baud_base < 2400)
/* No paper tape reader for Mitch.. */
return -EINVAL;
@@ -2151,11 +2098,13 @@ static struct uart_ops sci_uart_ops = {
};
static int sci_init_single(struct platform_device *dev,
- struct sci_port *sci_port,
- unsigned int index,
- struct plat_sci_port *p)
+ struct sci_port *sci_port, unsigned int index,
+ struct plat_sci_port *p, bool early)
{
struct uart_port *port = &sci_port->port;
+ const struct resource *res;
+ unsigned int sampling_rate;
+ unsigned int i;
int ret;
sci_port->cfg = p;
@@ -2164,31 +2113,76 @@ static int sci_init_single(struct platform_device *dev,
port->iotype = UPIO_MEM;
port->line = index;
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -ENOMEM;
+
+ port->mapbase = res->start;
+
+ for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i)
+ sci_port->irqs[i] = platform_get_irq(dev, i);
+
+ /* The SCI generates several interrupts. They can be muxed together or
+ * connected to different interrupt lines. In the muxed case only one
+ * interrupt resource is specified. In the non-muxed case three or four
+ * interrupt resources are specified, as the BRI interrupt is optional.
+ */
+ if (sci_port->irqs[0] < 0)
+ return -ENXIO;
+
+ if (sci_port->irqs[1] < 0) {
+ sci_port->irqs[1] = sci_port->irqs[0];
+ sci_port->irqs[2] = sci_port->irqs[0];
+ sci_port->irqs[3] = sci_port->irqs[0];
+ }
+
+ if (p->regtype == SCIx_PROBE_REGTYPE) {
+ ret = sci_probe_regmap(p);
+ if (unlikely(ret))
+ return ret;
+ }
+
switch (p->type) {
case PORT_SCIFB:
port->fifosize = 256;
+ sci_port->overrun_bit = 9;
+ sampling_rate = 16;
break;
case PORT_HSCIF:
port->fifosize = 128;
+ sampling_rate = 0;
+ sci_port->overrun_bit = 0;
break;
case PORT_SCIFA:
port->fifosize = 64;
+ sci_port->overrun_bit = 9;
+ sampling_rate = 16;
break;
case PORT_SCIF:
port->fifosize = 16;
+ if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) {
+ sci_port->overrun_bit = 9;
+ sampling_rate = 16;
+ } else {
+ sci_port->overrun_bit = 0;
+ sampling_rate = 32;
+ }
break;
default:
port->fifosize = 1;
+ sci_port->overrun_bit = 5;
+ sampling_rate = 32;
break;
}
- if (p->regtype == SCIx_PROBE_REGTYPE) {
- ret = sci_probe_regmap(p);
- if (unlikely(ret))
- return ret;
- }
+ /* SCIFA on sh7723 and sh7724 need a custom sampling rate that doesn't
+ * match the SoC datasheet, this should be investigated. Let platform
+ * data override the sampling rate for now.
+ */
+ sci_port->sampling_rate = p->sampling_rate ? p->sampling_rate
+ : sampling_rate;
- if (dev) {
+ if (!early) {
sci_port->iclk = clk_get(&dev->dev, "sci_ick");
if (IS_ERR(sci_port->iclk)) {
sci_port->iclk = clk_get(&dev->dev, "peripheral_clk");
@@ -2208,8 +2202,6 @@ static int sci_init_single(struct platform_device *dev,
port->dev = &dev->dev;
- sci_init_gpios(sci_port);
-
pm_runtime_enable(&dev->dev);
}
@@ -2220,32 +2212,22 @@ static int sci_init_single(struct platform_device *dev,
/*
* Establish some sensible defaults for the error detection.
*/
- if (!p->error_mask)
- p->error_mask = (p->type == PORT_SCI) ?
+ sci_port->error_mask = (p->type == PORT_SCI) ?
SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK;
/*
* Establish sensible defaults for the overrun detection, unless
* the part has explicitly disabled support for it.
*/
- if (p->overrun_bit != SCIx_NOT_SUPPORTED) {
- if (p->type == PORT_SCI)
- p->overrun_bit = 5;
- else if (p->scbrr_algo_id == SCBRR_ALGO_4)
- p->overrun_bit = 9;
- else
- p->overrun_bit = 0;
- /*
- * Make the error mask inclusive of overrun detection, if
- * supported.
- */
- p->error_mask |= (1 << p->overrun_bit);
- }
+ /*
+ * Make the error mask inclusive of overrun detection, if
+ * supported.
+ */
+ sci_port->error_mask |= 1 << sci_port->overrun_bit;
- port->mapbase = p->mapbase;
port->type = p->type;
- port->flags = p->flags;
+ port->flags = UPF_FIXED_PORT | p->flags;
port->regshift = p->regshift;
/*
@@ -2255,7 +2237,7 @@ static int sci_init_single(struct platform_device *dev,
*
* For the muxed case there's nothing more to do.
*/
- port->irq = p->irqs[SCIx_RXI_IRQ];
+ port->irq = sci_port->irqs[SCIx_RXI_IRQ];
port->irqflags = 0;
port->serial_in = sci_serial_in;
@@ -2270,8 +2252,6 @@ static int sci_init_single(struct platform_device *dev,
static void sci_cleanup_single(struct sci_port *port)
{
- sci_free_gpios(port);
-
clk_put(port->iclk);
clk_put(port->fclk);
@@ -2380,14 +2360,14 @@ static char early_serial_buf[32];
static int sci_probe_earlyprintk(struct platform_device *pdev)
{
- struct plat_sci_port *cfg = pdev->dev.platform_data;
+ struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev);
if (early_serial_console.data)
return -EEXIST;
early_serial_console.index = pdev->id;
- sci_init_single(NULL, &sci_ports[pdev->id], pdev->id, cfg);
+ sci_init_single(pdev, &sci_ports[pdev->id], pdev->id, cfg, true);
serial_console_setup(&early_serial_console, early_serial_buf);
@@ -2410,8 +2390,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev)
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
-static char banner[] __initdata =
- KERN_INFO "SuperH (H)SCI(F) driver initialized\n";
+static const char banner[] __initconst = "SuperH (H)SCI(F) driver initialized";
static struct uart_driver sci_uart_driver = {
.owner = THIS_MODULE,
@@ -2437,6 +2416,83 @@ static int sci_remove(struct platform_device *dev)
return 0;
}
+struct sci_port_info {
+ unsigned int type;
+ unsigned int regtype;
+};
+
+static const struct of_device_id of_sci_match[] = {
+ {
+ .compatible = "renesas,scif",
+ .data = &(const struct sci_port_info) {
+ .type = PORT_SCIF,
+ .regtype = SCIx_SH4_SCIF_REGTYPE,
+ },
+ }, {
+ .compatible = "renesas,scifa",
+ .data = &(const struct sci_port_info) {
+ .type = PORT_SCIFA,
+ .regtype = SCIx_SCIFA_REGTYPE,
+ },
+ }, {
+ .compatible = "renesas,scifb",
+ .data = &(const struct sci_port_info) {
+ .type = PORT_SCIFB,
+ .regtype = SCIx_SCIFB_REGTYPE,
+ },
+ }, {
+ .compatible = "renesas,hscif",
+ .data = &(const struct sci_port_info) {
+ .type = PORT_HSCIF,
+ .regtype = SCIx_HSCIF_REGTYPE,
+ },
+ }, {
+ /* Terminator */
+ },
+};
+MODULE_DEVICE_TABLE(of, of_sci_match);
+
+static struct plat_sci_port *
+sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ const struct sci_port_info *info;
+ struct plat_sci_port *p;
+ int id;
+
+ if (!IS_ENABLED(CONFIG_OF) || !np)
+ return NULL;
+
+ match = of_match_node(of_sci_match, pdev->dev.of_node);
+ if (!match)
+ return NULL;
+
+ info = match->data;
+
+ p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL);
+ if (!p) {
+ dev_err(&pdev->dev, "failed to allocate DT config data\n");
+ return NULL;
+ }
+
+ /* Get the line number for the aliases node. */
+ id = of_alias_get_id(np, "serial");
+ if (id < 0) {
+ dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
+ return NULL;
+ }
+
+ *dev_id = id;
+
+ p->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+ p->type = info->type;
+ p->regtype = info->regtype;
+ p->scscr = SCSCR_RE | SCSCR_TE;
+
+ return p;
+}
+
static int sci_probe_single(struct platform_device *dev,
unsigned int index,
struct plat_sci_port *p,
@@ -2446,15 +2502,13 @@ static int sci_probe_single(struct platform_device *dev,
/* Sanity check */
if (unlikely(index >= SCI_NPORTS)) {
- dev_notice(&dev->dev, "Attempting to register port "
- "%d when only %d are available.\n",
+ dev_notice(&dev->dev, "Attempting to register port %d when only %d are available\n",
index+1, SCI_NPORTS);
- dev_notice(&dev->dev, "Consider bumping "
- "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n");
+ dev_notice(&dev->dev, "Consider bumping CONFIG_SERIAL_SH_SCI_NR_UARTS!\n");
return -EINVAL;
}
- ret = sci_init_single(dev, sciport, index, p);
+ ret = sci_init_single(dev, sciport, index, p, false);
if (ret)
return ret;
@@ -2469,8 +2523,9 @@ static int sci_probe_single(struct platform_device *dev,
static int sci_probe(struct platform_device *dev)
{
- struct plat_sci_port *p = dev->dev.platform_data;
- struct sci_port *sp = &sci_ports[dev->id];
+ struct plat_sci_port *p;
+ struct sci_port *sp;
+ unsigned int dev_id;
int ret;
/*
@@ -2481,9 +2536,24 @@ static int sci_probe(struct platform_device *dev)
if (is_early_platform_device(dev))
return sci_probe_earlyprintk(dev);
+ if (dev->dev.of_node) {
+ p = sci_parse_dt(dev, &dev_id);
+ if (p == NULL)
+ return -EINVAL;
+ } else {
+ p = dev->dev.platform_data;
+ if (p == NULL) {
+ dev_err(&dev->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ dev_id = dev->id;
+ }
+
+ sp = &sci_ports[dev_id];
platform_set_drvdata(dev, sp);
- ret = sci_probe_single(dev, dev->id, p, sp);
+ ret = sci_probe_single(dev, dev_id, p, sp);
if (ret)
return ret;
@@ -2492,6 +2562,7 @@ static int sci_probe(struct platform_device *dev)
ret = cpufreq_register_notifier(&sp->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
if (unlikely(ret < 0)) {
+ uart_remove_one_port(&sci_uart_driver, &sp->port);
sci_cleanup_single(sp);
return ret;
}
@@ -2535,6 +2606,7 @@ static struct platform_driver sci_driver = {
.name = "sh-sci",
.owner = THIS_MODULE,
.pm = &sci_dev_pm_ops,
+ .of_match_table = of_match_ptr(of_sci_match),
},
};
@@ -2542,7 +2614,7 @@ static int __init sci_init(void)
{
int ret;
- printk(banner);
+ pr_info("%s\n", banner);
ret = uart_register_driver(&sci_uart_driver);
if (likely(ret == 0)) {
diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h
index 5aca7364634..d5db81a0a43 100644
--- a/drivers/tty/serial/sh-sci.h
+++ b/drivers/tty/serial/sh-sci.h
@@ -9,7 +9,7 @@
#define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER)
#define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK)
-#define SCxSR_ERRORS(port) (to_sci_port(port)->cfg->error_mask)
+#define SCxSR_ERRORS(port) (to_sci_port(port)->error_mask)
#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 1fd564b8194..9b4d71cff00 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -20,9 +20,12 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
-#include <linux/pinctrl/consumer.h>
#include "sirfsoc_uart.h"
@@ -32,6 +35,9 @@ static unsigned int
sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count);
static struct uart_driver sirfsoc_uart_drv;
+static void sirfsoc_uart_tx_dma_complete_callback(void *param);
+static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port);
+static void sirfsoc_uart_rx_dma_complete_callback(void *param);
static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = {
{4000000, 2359296},
{3500000, 1310721},
@@ -89,6 +95,13 @@ static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = {
.line = 4,
},
},
+ [5] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 5,
+ },
+ },
};
static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
@@ -99,21 +112,28 @@ static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port)
{
unsigned long reg;
- reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS);
- if (reg & SIRFUART_FIFOEMPTY_MASK(port))
- return TIOCSER_TEMT;
- else
- return 0;
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
+ reg = rd_regl(port, ureg->sirfsoc_tx_fifo_status);
+
+ return (reg & ufifo_st->ff_empty(port->line)) ? TIOCSER_TEMT : 0;
}
static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
- if (!(sirfport->ms_enabled)) {
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled)
goto cts_asserted;
- } else if (sirfport->hw_flow_ctrl) {
- if (!(rd_regl(port, SIRFUART_AFC_CTRL) &
- SIRFUART_CTS_IN_STATUS))
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ if (!(rd_regl(port, ureg->sirfsoc_afc_ctrl) &
+ SIRFUART_AFC_CTS_STATUS))
+ goto cts_asserted;
+ else
+ goto cts_deasserted;
+ } else {
+ if (!gpio_get_value(sirfport->cts_gpio))
goto cts_asserted;
else
goto cts_deasserted;
@@ -127,89 +147,278 @@ cts_asserted:
static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
unsigned int assert = mctrl & TIOCM_RTS;
unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0;
unsigned int current_val;
- if (sirfport->hw_flow_ctrl) {
- current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF;
+
+ if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled)
+ return;
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ current_val = rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0xFF;
val |= current_val;
- wr_regl(port, SIRFUART_AFC_CTRL, val);
+ wr_regl(port, ureg->sirfsoc_afc_ctrl, val);
+ } else {
+ if (!val)
+ gpio_set_value(sirfport->rts_gpio, 1);
+ else
+ gpio_set_value(sirfport->rts_gpio, 0);
}
}
static void sirfsoc_uart_stop_tx(struct uart_port *port)
{
- unsigned int regv;
- regv = rd_regl(port, SIRFUART_INT_EN);
- wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN);
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
+ if (sirfport->tx_dma_chan) {
+ if (sirfport->tx_dma_state == TX_DMA_RUNNING) {
+ dmaengine_pause(sirfport->tx_dma_chan);
+ sirfport->tx_dma_state = TX_DMA_PAUSE;
+ } else {
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) &
+ ~uint_en->sirfsoc_txfifo_empty_en);
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ uint_en->sirfsoc_txfifo_empty_en);
+ }
+ } else {
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) &
+ ~uint_en->sirfsoc_txfifo_empty_en);
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ uint_en->sirfsoc_txfifo_empty_en);
+ }
+}
+
+static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport)
+{
+ struct uart_port *port = &sirfport->port;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long tran_size;
+ unsigned long tran_start;
+ unsigned long pio_tx_size;
+
+ tran_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ tran_start = (unsigned long)(xmit->buf + xmit->tail);
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port) ||
+ !tran_size)
+ return;
+ if (sirfport->tx_dma_state == TX_DMA_PAUSE) {
+ dmaengine_resume(sirfport->tx_dma_chan);
+ return;
+ }
+ if (sirfport->tx_dma_state == TX_DMA_RUNNING)
+ return;
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg)&
+ ~(uint_en->sirfsoc_txfifo_empty_en));
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ uint_en->sirfsoc_txfifo_empty_en);
+ /*
+ * DMA requires buffer address and buffer length are both aligned with
+ * 4 bytes, so we use PIO for
+ * 1. if address is not aligned with 4bytes, use PIO for the first 1~3
+ * bytes, and move to DMA for the left part aligned with 4bytes
+ * 2. if buffer length is not aligned with 4bytes, use DMA for aligned
+ * part first, move to PIO for the left 1~3 bytes
+ */
+ if (tran_size < 4 || BYTES_TO_ALIGN(tran_start)) {
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP);
+ wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)|
+ SIRFUART_IO_MODE);
+ if (BYTES_TO_ALIGN(tran_start)) {
+ pio_tx_size = sirfsoc_uart_pio_tx_chars(sirfport,
+ BYTES_TO_ALIGN(tran_start));
+ tran_size -= pio_tx_size;
+ }
+ if (tran_size < 4)
+ sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg)|
+ uint_en->sirfsoc_txfifo_empty_en);
+ else
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ uint_en->sirfsoc_txfifo_empty_en);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
+ } else {
+ /* tx transfer mode switch into dma mode */
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP);
+ wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)&
+ ~SIRFUART_IO_MODE);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
+ tran_size &= ~(0x3);
+
+ sirfport->tx_dma_addr = dma_map_single(port->dev,
+ xmit->buf + xmit->tail,
+ tran_size, DMA_TO_DEVICE);
+ sirfport->tx_dma_desc = dmaengine_prep_slave_single(
+ sirfport->tx_dma_chan, sirfport->tx_dma_addr,
+ tran_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+ if (!sirfport->tx_dma_desc) {
+ dev_err(port->dev, "DMA prep slave single fail\n");
+ return;
+ }
+ sirfport->tx_dma_desc->callback =
+ sirfsoc_uart_tx_dma_complete_callback;
+ sirfport->tx_dma_desc->callback_param = (void *)sirfport;
+ sirfport->transfer_size = tran_size;
+
+ dmaengine_submit(sirfport->tx_dma_desc);
+ dma_async_issue_pending(sirfport->tx_dma_chan);
+ sirfport->tx_dma_state = TX_DMA_RUNNING;
+ }
}
-void sirfsoc_uart_start_tx(struct uart_port *port)
+static void sirfsoc_uart_start_tx(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
- unsigned long regv;
- sirfsoc_uart_pio_tx_chars(sirfport, 1);
- wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START);
- regv = rd_regl(port, SIRFUART_INT_EN);
- wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+ if (sirfport->tx_dma_chan)
+ sirfsoc_uart_tx_with_dma(sirfport);
+ else {
+ sirfsoc_uart_pio_tx_chars(sirfport, 1);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg)|
+ uint_en->sirfsoc_txfifo_empty_en);
+ else
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ uint_en->sirfsoc_txfifo_empty_en);
+ }
}
static void sirfsoc_uart_stop_rx(struct uart_port *port)
{
- unsigned long regv;
- wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
- regv = rd_regl(port, SIRFUART_INT_EN);
- wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN);
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+ if (sirfport->rx_dma_chan) {
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) &
+ ~(SIRFUART_RX_DMA_INT_EN(port, uint_en) |
+ uint_en->sirfsoc_rx_done_en));
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ SIRFUART_RX_DMA_INT_EN(port, uint_en)|
+ uint_en->sirfsoc_rx_done_en);
+ dmaengine_terminate_all(sirfport->rx_dma_chan);
+ } else {
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg)&
+ ~(SIRFUART_RX_IO_INT_EN(port, uint_en)));
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ SIRFUART_RX_IO_INT_EN(port, uint_en));
+ }
}
static void sirfsoc_uart_disable_ms(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
- unsigned long reg;
- sirfport->ms_enabled = 0;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
if (!sirfport->hw_flow_ctrl)
return;
- reg = rd_regl(port, SIRFUART_AFC_CTRL);
- wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF);
- reg = rd_regl(port, SIRFUART_INT_EN);
- wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN);
+ sirfport->ms_enabled = false;
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ wr_regl(port, ureg->sirfsoc_afc_ctrl,
+ rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg)&
+ ~uint_en->sirfsoc_cts_en);
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ uint_en->sirfsoc_cts_en);
+ } else
+ disable_irq(gpio_to_irq(sirfport->cts_gpio));
+}
+
+static irqreturn_t sirfsoc_uart_usp_cts_handler(int irq, void *dev_id)
+{
+ struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
+ struct uart_port *port = &sirfport->port;
+ spin_lock(&port->lock);
+ if (gpio_is_valid(sirfport->cts_gpio) && sirfport->ms_enabled)
+ uart_handle_cts_change(port,
+ !gpio_get_value(sirfport->cts_gpio));
+ spin_unlock(&port->lock);
+ return IRQ_HANDLED;
}
static void sirfsoc_uart_enable_ms(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
- unsigned long reg;
- unsigned long flg;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
if (!sirfport->hw_flow_ctrl)
return;
- flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN;
- reg = rd_regl(port, SIRFUART_AFC_CTRL);
- wr_regl(port, SIRFUART_AFC_CTRL, reg | flg);
- reg = rd_regl(port, SIRFUART_INT_EN);
- wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN);
- uart_handle_cts_change(port,
- !(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS));
- sirfport->ms_enabled = 1;
+ sirfport->ms_enabled = true;
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ wr_regl(port, ureg->sirfsoc_afc_ctrl,
+ rd_regl(port, ureg->sirfsoc_afc_ctrl) |
+ SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg)
+ | uint_en->sirfsoc_cts_en);
+ else
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ uint_en->sirfsoc_cts_en);
+ } else
+ enable_irq(gpio_to_irq(sirfport->cts_gpio));
}
static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state)
{
- unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL);
- if (break_state)
- ulcon |= SIRFUART_SET_BREAK;
- else
- ulcon &= ~SIRFUART_SET_BREAK;
- wr_regl(port, SIRFUART_LINE_CTRL, ulcon);
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ unsigned long ulcon = rd_regl(port, ureg->sirfsoc_line_ctrl);
+ if (break_state)
+ ulcon |= SIRFUART_SET_BREAK;
+ else
+ ulcon &= ~SIRFUART_SET_BREAK;
+ wr_regl(port, ureg->sirfsoc_line_ctrl, ulcon);
+ }
}
static unsigned int
sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
unsigned int ch, rx_count = 0;
-
- while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) &
- SIRFUART_FIFOEMPTY_MASK(port))) {
- ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ;
+ struct tty_struct *tty;
+ tty = tty_port_tty_get(&port->state->port);
+ if (!tty)
+ return -ENODEV;
+ while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
+ ufifo_st->ff_empty(port->line))) {
+ ch = rd_regl(port, ureg->sirfsoc_rx_fifo_data) |
+ SIRFUART_DUMMY_READ;
if (unlikely(uart_handle_sysrq_char(port, ch)))
continue;
uart_insert_char(port, 0, 0, ch, TTY_NORMAL);
@@ -218,8 +427,8 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
break;
}
+ sirfport->rx_io_count += rx_count;
port->icount.rx += rx_count;
- tty_flip_buffer_push(&port->state->port);
return rx_count;
}
@@ -228,13 +437,16 @@ static unsigned int
sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count)
{
struct uart_port *port = &sirfport->port;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
struct circ_buf *xmit = &port->state->xmit;
unsigned int num_tx = 0;
while (!uart_circ_empty(xmit) &&
- !(rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
- SIRFUART_FIFOFULL_MASK(port)) &&
+ !(rd_regl(port, ureg->sirfsoc_tx_fifo_status) &
+ ufifo_st->ff_full(port->line)) &&
count--) {
- wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_data,
+ xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
num_tx++;
@@ -244,6 +456,160 @@ sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count)
return num_tx;
}
+static void sirfsoc_uart_tx_dma_complete_callback(void *param)
+{
+ struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+ struct uart_port *port = &sirfport->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ xmit->tail = (xmit->tail + sirfport->transfer_size) &
+ (UART_XMIT_SIZE - 1);
+ port->icount.tx += sirfport->transfer_size;
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+ if (sirfport->tx_dma_addr)
+ dma_unmap_single(port->dev, sirfport->tx_dma_addr,
+ sirfport->transfer_size, DMA_TO_DEVICE);
+ sirfport->tx_dma_state = TX_DMA_IDLE;
+ sirfsoc_uart_tx_with_dma(sirfport);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void sirfsoc_uart_insert_rx_buf_to_tty(
+ struct sirfsoc_uart_port *sirfport, int count)
+{
+ struct uart_port *port = &sirfport->port;
+ struct tty_port *tport = &port->state->port;
+ int inserted;
+
+ inserted = tty_insert_flip_string(tport,
+ sirfport->rx_dma_items[sirfport->rx_completed].xmit.buf, count);
+ port->icount.rx += inserted;
+}
+
+static void sirfsoc_rx_submit_one_dma_desc(struct uart_port *port, int index)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+
+ sirfport->rx_dma_items[index].xmit.tail =
+ sirfport->rx_dma_items[index].xmit.head = 0;
+ sirfport->rx_dma_items[index].desc =
+ dmaengine_prep_slave_single(sirfport->rx_dma_chan,
+ sirfport->rx_dma_items[index].dma_addr, SIRFSOC_RX_DMA_BUF_SIZE,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+ if (!sirfport->rx_dma_items[index].desc) {
+ dev_err(port->dev, "DMA slave single fail\n");
+ return;
+ }
+ sirfport->rx_dma_items[index].desc->callback =
+ sirfsoc_uart_rx_dma_complete_callback;
+ sirfport->rx_dma_items[index].desc->callback_param = sirfport;
+ sirfport->rx_dma_items[index].cookie =
+ dmaengine_submit(sirfport->rx_dma_items[index].desc);
+ dma_async_issue_pending(sirfport->rx_dma_chan);
+}
+
+static void sirfsoc_rx_tmo_process_tl(unsigned long param)
+{
+ struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+ struct uart_port *port = &sirfport->port;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+ struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st;
+ unsigned int count;
+ unsigned long flags;
+ struct dma_tx_state tx_state;
+
+ spin_lock_irqsave(&port->lock, flags);
+ while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan,
+ sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) {
+ sirfsoc_uart_insert_rx_buf_to_tty(sirfport,
+ SIRFSOC_RX_DMA_BUF_SIZE);
+ sirfport->rx_completed++;
+ sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT;
+ }
+ count = CIRC_CNT(sirfport->rx_dma_items[sirfport->rx_issued].xmit.head,
+ sirfport->rx_dma_items[sirfport->rx_issued].xmit.tail,
+ SIRFSOC_RX_DMA_BUF_SIZE);
+ if (count > 0)
+ sirfsoc_uart_insert_rx_buf_to_tty(sirfport, count);
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
+ SIRFUART_IO_MODE);
+ sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
+ if (sirfport->rx_io_count == 4) {
+ sirfport->rx_io_count = 0;
+ wr_regl(port, ureg->sirfsoc_int_st_reg,
+ uint_st->sirfsoc_rx_done);
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) &
+ ~(uint_en->sirfsoc_rx_done_en));
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ uint_en->sirfsoc_rx_done_en);
+ sirfsoc_uart_start_next_rx_dma(port);
+ } else {
+ wr_regl(port, ureg->sirfsoc_int_st_reg,
+ uint_st->sirfsoc_rx_done);
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) |
+ (uint_en->sirfsoc_rx_done_en));
+ else
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ uint_en->sirfsoc_rx_done_en);
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+ tty_flip_buffer_push(&port->state->port);
+}
+
+static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport)
+{
+ struct uart_port *port = &sirfport->port;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+ struct dma_tx_state tx_state;
+ dmaengine_tx_status(sirfport->rx_dma_chan,
+ sirfport->rx_dma_items[sirfport->rx_issued].cookie, &tx_state);
+ dmaengine_terminate_all(sirfport->rx_dma_chan);
+ sirfport->rx_dma_items[sirfport->rx_issued].xmit.head =
+ SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) &
+ ~(uint_en->sirfsoc_rx_timeout_en));
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ uint_en->sirfsoc_rx_timeout_en);
+ tasklet_schedule(&sirfport->rx_tmo_process_tasklet);
+}
+
+static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport)
+{
+ struct uart_port *port = &sirfport->port;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+ struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st;
+
+ sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
+ if (sirfport->rx_io_count == 4) {
+ sirfport->rx_io_count = 0;
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) &
+ ~(uint_en->sirfsoc_rx_done_en));
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR,
+ uint_en->sirfsoc_rx_done_en);
+ wr_regl(port, ureg->sirfsoc_int_st_reg,
+ uint_st->sirfsoc_rx_timeout);
+ sirfsoc_uart_start_next_rx_dma(port);
+ }
+}
+
static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
{
unsigned long intr_status;
@@ -251,79 +617,201 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
unsigned long flag = TTY_NORMAL;
struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
struct uart_port *port = &sirfport->port;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
+ struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
struct uart_state *state = port->state;
struct circ_buf *xmit = &port->state->xmit;
spin_lock(&port->lock);
- intr_status = rd_regl(port, SIRFUART_INT_STATUS);
- wr_regl(port, SIRFUART_INT_STATUS, intr_status);
- intr_status &= rd_regl(port, SIRFUART_INT_EN);
- if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) {
- if (intr_status & SIRFUART_RXD_BREAK) {
+ intr_status = rd_regl(port, ureg->sirfsoc_int_st_reg);
+ wr_regl(port, ureg->sirfsoc_int_st_reg, intr_status);
+ intr_status &= rd_regl(port, ureg->sirfsoc_int_en_reg);
+ if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT(port, uint_st)))) {
+ if (intr_status & uint_st->sirfsoc_rxd_brk) {
+ port->icount.brk++;
if (uart_handle_break(port))
goto recv_char;
- uart_insert_char(port, intr_status,
- SIRFUART_RX_OFLOW, 0, TTY_BREAK);
- spin_unlock(&port->lock);
- return IRQ_HANDLED;
}
- if (intr_status & SIRFUART_RX_OFLOW)
+ if (intr_status & uint_st->sirfsoc_rx_oflow)
port->icount.overrun++;
- if (intr_status & SIRFUART_FRM_ERR) {
+ if (intr_status & uint_st->sirfsoc_frm_err) {
port->icount.frame++;
flag = TTY_FRAME;
}
- if (intr_status & SIRFUART_PARITY_ERR)
+ if (intr_status & uint_st->sirfsoc_parity_err)
flag = TTY_PARITY;
- wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
- wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
- wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START);
intr_status &= port->read_status_mask;
uart_insert_char(port, intr_status,
- SIRFUART_RX_OFLOW_INT, 0, flag);
+ uint_en->sirfsoc_rx_oflow_en, 0, flag);
}
recv_char:
- if (intr_status & SIRFUART_CTS_INT_EN) {
- cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) &
- SIRFUART_CTS_IN_STATUS);
- if (cts_status != 0) {
- uart_handle_cts_change(port, 1);
- } else {
- uart_handle_cts_change(port, 0);
- wake_up_interruptible(&state->port.delta_msr_wait);
- }
+ if ((sirfport->uart_reg->uart_type == SIRF_REAL_UART) &&
+ (intr_status & SIRFUART_CTS_INT_ST(uint_st)) &&
+ !sirfport->tx_dma_state) {
+ cts_status = rd_regl(port, ureg->sirfsoc_afc_ctrl) &
+ SIRFUART_AFC_CTS_STATUS;
+ if (cts_status != 0)
+ cts_status = 0;
+ else
+ cts_status = 1;
+ uart_handle_cts_change(port, cts_status);
+ wake_up_interruptible(&state->port.delta_msr_wait);
}
- if (intr_status & SIRFUART_RX_IO_INT_EN)
- sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT);
- if (intr_status & SIRFUART_TX_INT_EN) {
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- spin_unlock(&port->lock);
- return IRQ_HANDLED;
- } else {
- sirfsoc_uart_pio_tx_chars(sirfport,
+ if (sirfport->rx_dma_chan) {
+ if (intr_status & uint_st->sirfsoc_rx_timeout)
+ sirfsoc_uart_handle_rx_tmo(sirfport);
+ if (intr_status & uint_st->sirfsoc_rx_done)
+ sirfsoc_uart_handle_rx_done(sirfport);
+ } else {
+ if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st))
+ sirfsoc_uart_pio_rx_chars(port,
+ SIRFSOC_UART_IO_RX_MAX_CNT);
+ }
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&state->port);
+ spin_lock(&port->lock);
+ if (intr_status & uint_st->sirfsoc_txfifo_empty) {
+ if (sirfport->tx_dma_chan)
+ sirfsoc_uart_tx_with_dma(sirfport);
+ else {
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ spin_unlock(&port->lock);
+ return IRQ_HANDLED;
+ } else {
+ sirfsoc_uart_pio_tx_chars(sirfport,
SIRFSOC_UART_IO_TX_REASONABLE_CNT);
- if ((uart_circ_empty(xmit)) &&
- (rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
- SIRFUART_FIFOEMPTY_MASK(port)))
- sirfsoc_uart_stop_tx(port);
+ if ((uart_circ_empty(xmit)) &&
+ (rd_regl(port, ureg->sirfsoc_tx_fifo_status) &
+ ufifo_st->ff_empty(port->line)))
+ sirfsoc_uart_stop_tx(port);
+ }
}
}
spin_unlock(&port->lock);
+
return IRQ_HANDLED;
}
+static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param)
+{
+ struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+ struct uart_port *port = &sirfport->port;
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+ unsigned long flags;
+ struct dma_tx_state tx_state;
+ spin_lock_irqsave(&port->lock, flags);
+ while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan,
+ sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) {
+ sirfsoc_uart_insert_rx_buf_to_tty(sirfport,
+ SIRFSOC_RX_DMA_BUF_SIZE);
+ if (rd_regl(port, ureg->sirfsoc_int_en_reg) &
+ uint_en->sirfsoc_rx_timeout_en)
+ sirfsoc_rx_submit_one_dma_desc(port,
+ sirfport->rx_completed++);
+ else
+ sirfport->rx_completed++;
+ sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT;
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+ tty_flip_buffer_push(&port->state->port);
+}
+
+static void sirfsoc_uart_rx_dma_complete_callback(void *param)
+{
+ struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sirfport->port.lock, flags);
+ sirfport->rx_issued++;
+ sirfport->rx_issued %= SIRFSOC_RX_LOOP_BUF_CNT;
+ tasklet_schedule(&sirfport->rx_dma_complete_tasklet);
+ spin_unlock_irqrestore(&sirfport->port.lock, flags);
+}
+
+/* submit rx dma task into dmaengine */
+static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port)
+{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+ int i;
+ sirfport->rx_io_count = 0;
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) &
+ ~SIRFUART_IO_MODE);
+ for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
+ sirfsoc_rx_submit_one_dma_desc(port, i);
+ sirfport->rx_completed = sirfport->rx_issued = 0;
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) |
+ SIRFUART_RX_DMA_INT_EN(port, uint_en));
+ else
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ SIRFUART_RX_DMA_INT_EN(port, uint_en));
+}
+
static void sirfsoc_uart_start_rx(struct uart_port *port)
{
- unsigned long regv;
- regv = rd_regl(port, SIRFUART_INT_EN);
- wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN);
- wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
- wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
- wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
+
+ sirfport->rx_io_count = 0;
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START);
+ if (sirfport->rx_dma_chan)
+ sirfsoc_uart_start_next_rx_dma(port);
+ else {
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) |
+ SIRFUART_RX_IO_INT_EN(port, uint_en));
+ else
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ SIRFUART_RX_IO_INT_EN(port, uint_en));
+ }
+}
+
+static unsigned int
+sirfsoc_usp_calc_sample_div(unsigned long set_rate,
+ unsigned long ioclk_rate, unsigned long *sample_reg)
+{
+ unsigned long min_delta = ~0UL;
+ unsigned short sample_div;
+ unsigned long ioclk_div = 0;
+ unsigned long temp_delta;
+
+ for (sample_div = SIRF_MIN_SAMPLE_DIV;
+ sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) {
+ temp_delta = ioclk_rate -
+ (ioclk_rate + (set_rate * sample_div) / 2)
+ / (set_rate * sample_div) * set_rate * sample_div;
+
+ temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta;
+ if (temp_delta < min_delta) {
+ ioclk_div = (2 * ioclk_rate /
+ (set_rate * sample_div) + 1) / 2 - 1;
+ if (ioclk_div > SIRF_IOCLK_DIV_MAX)
+ continue;
+ min_delta = temp_delta;
+ *sample_reg = sample_div;
+ if (!temp_delta)
+ break;
+ }
+ }
+ return ioclk_div;
}
static unsigned int
-sirfsoc_calc_sample_div(unsigned long baud_rate,
- unsigned long ioclk_rate, unsigned long *setted_baud)
+sirfsoc_uart_calc_sample_div(unsigned long baud_rate,
+ unsigned long ioclk_rate, unsigned long *set_baud)
{
unsigned long min_delta = ~0UL;
unsigned short sample_div;
@@ -346,7 +834,7 @@ sirfsoc_calc_sample_div(unsigned long baud_rate,
regv = regv & (~SIRF_SAMPLE_DIV_MASK);
regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT);
min_delta = temp_delta;
- *setted_baud = baud_tmp;
+ *set_baud = baud_tmp;
}
}
return regv;
@@ -357,63 +845,93 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
struct ktermios *old)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
unsigned long config_reg = 0;
unsigned long baud_rate;
- unsigned long setted_baud;
+ unsigned long set_baud;
unsigned long flags;
unsigned long ic;
unsigned int clk_div_reg = 0;
- unsigned long temp_reg_val;
+ unsigned long txfifo_op_reg, ioclk_rate;
unsigned long rx_time_out;
int threshold_div;
- int temp;
+ u32 data_bit_len, stop_bit_len, len_val;
+ unsigned long sample_div_reg = 0xf;
+ ioclk_rate = port->uartclk;
switch (termios->c_cflag & CSIZE) {
default:
case CS8:
+ data_bit_len = 8;
config_reg |= SIRFUART_DATA_BIT_LEN_8;
break;
case CS7:
+ data_bit_len = 7;
config_reg |= SIRFUART_DATA_BIT_LEN_7;
break;
case CS6:
+ data_bit_len = 6;
config_reg |= SIRFUART_DATA_BIT_LEN_6;
break;
case CS5:
+ data_bit_len = 5;
config_reg |= SIRFUART_DATA_BIT_LEN_5;
break;
}
- if (termios->c_cflag & CSTOPB)
+ if (termios->c_cflag & CSTOPB) {
config_reg |= SIRFUART_STOP_BIT_LEN_2;
- baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000);
+ stop_bit_len = 2;
+ } else
+ stop_bit_len = 1;
+
spin_lock_irqsave(&port->lock, flags);
- port->read_status_mask = SIRFUART_RX_OFLOW_INT;
+ port->read_status_mask = uint_en->sirfsoc_rx_oflow_en;
port->ignore_status_mask = 0;
- /* read flags */
- if (termios->c_iflag & INPCK)
- port->read_status_mask |=
- SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
- if (termios->c_iflag & (BRKINT | PARMRK))
- port->read_status_mask |= SIRFUART_RXD_BREAK_INT;
- /* ignore flags */
- if (termios->c_iflag & IGNPAR)
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= uint_en->sirfsoc_frm_err_en |
+ uint_en->sirfsoc_parity_err_en;
+ } else {
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= uint_en->sirfsoc_frm_err_en;
+ }
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+ port->read_status_mask |= uint_en->sirfsoc_rxd_brk_en;
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |=
+ uint_en->sirfsoc_frm_err_en |
+ uint_en->sirfsoc_parity_err_en;
+ if (termios->c_cflag & PARENB) {
+ if (termios->c_cflag & CMSPAR) {
+ if (termios->c_cflag & PARODD)
+ config_reg |= SIRFUART_STICK_BIT_MARK;
+ else
+ config_reg |= SIRFUART_STICK_BIT_SPACE;
+ } else if (termios->c_cflag & PARODD) {
+ config_reg |= SIRFUART_STICK_BIT_ODD;
+ } else {
+ config_reg |= SIRFUART_STICK_BIT_EVEN;
+ }
+ }
+ } else {
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |=
+ uint_en->sirfsoc_frm_err_en;
+ if (termios->c_cflag & PARENB)
+ dev_warn(port->dev,
+ "USP-UART not support parity err\n");
+ }
+ if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |=
- SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
+ uint_en->sirfsoc_rxd_brk_en;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |=
+ uint_en->sirfsoc_rx_oflow_en;
+ }
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= SIRFUART_DUMMY_READ;
- /* enable parity if PARENB is set*/
- if (termios->c_cflag & PARENB) {
- if (termios->c_cflag & CMSPAR) {
- if (termios->c_cflag & PARODD)
- config_reg |= SIRFUART_STICK_BIT_MARK;
- else
- config_reg |= SIRFUART_STICK_BIT_SPACE;
- } else if (termios->c_cflag & PARODD) {
- config_reg |= SIRFUART_STICK_BIT_ODD;
- } else {
- config_reg |= SIRFUART_STICK_BIT_EVEN;
- }
- }
/* Hardware Flow Control Settings */
if (UART_ENABLE_MS(port, termios->c_cflag)) {
if (!sirfport->ms_enabled)
@@ -422,75 +940,107 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
if (sirfport->ms_enabled)
sirfsoc_uart_disable_ms(port);
}
-
- if (port->uartclk == 150000000) {
- /* common rate: fast calculation */
+ baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000);
+ if (ioclk_rate == 150000000) {
for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++)
if (baud_rate == baudrate_to_regv[ic].baud_rate)
clk_div_reg = baudrate_to_regv[ic].reg_val;
}
-
- setted_baud = baud_rate;
- /* arbitary rate setting */
- if (unlikely(clk_div_reg == 0))
- clk_div_reg = sirfsoc_calc_sample_div(baud_rate, port->uartclk,
- &setted_baud);
- wr_regl(port, SIRFUART_DIVISOR, clk_div_reg);
-
+ set_baud = baud_rate;
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ if (unlikely(clk_div_reg == 0))
+ clk_div_reg = sirfsoc_uart_calc_sample_div(baud_rate,
+ ioclk_rate, &set_baud);
+ wr_regl(port, ureg->sirfsoc_divisor, clk_div_reg);
+ } else {
+ clk_div_reg = sirfsoc_usp_calc_sample_div(baud_rate,
+ ioclk_rate, &sample_div_reg);
+ sample_div_reg--;
+ set_baud = ((ioclk_rate / (clk_div_reg+1) - 1) /
+ (sample_div_reg + 1));
+ /* setting usp mode 2 */
+ len_val = ((1 << SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET) |
+ (1 << SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET));
+ len_val |= ((clk_div_reg & SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK)
+ << SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET);
+ wr_regl(port, ureg->sirfsoc_mode2, len_val);
+ }
if (tty_termios_baud_rate(termios))
- tty_termios_encode_baud_rate(termios, setted_baud, setted_baud);
-
- /* set receive timeout */
- rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000);
- rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out;
- config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out);
- temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP);
- wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
- wr_regl(port, SIRFUART_TX_FIFO_OP,
- temp_reg_val & ~SIRFUART_TX_FIFO_START);
- wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO);
- wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO);
- wr_regl(port, SIRFUART_LINE_CTRL, config_reg);
-
+ tty_termios_encode_baud_rate(termios, set_baud, set_baud);
+ /* set receive timeout && data bits len */
+ rx_time_out = SIRFSOC_UART_RX_TIMEOUT(set_baud, 20000);
+ rx_time_out = SIRFUART_RECV_TIMEOUT_VALUE(rx_time_out);
+ txfifo_op_reg = rd_regl(port, ureg->sirfsoc_tx_fifo_op);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_STOP);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op,
+ (txfifo_op_reg & ~SIRFUART_FIFO_START));
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
+ config_reg |= SIRFUART_RECV_TIMEOUT(port, rx_time_out);
+ wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg);
+ } else {
+ /*tx frame ctrl*/
+ len_val = (data_bit_len - 1) << SIRFSOC_USP_TX_DATA_LEN_OFFSET;
+ len_val |= (data_bit_len + 1 + stop_bit_len - 1) <<
+ SIRFSOC_USP_TX_FRAME_LEN_OFFSET;
+ len_val |= ((data_bit_len - 1) <<
+ SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET);
+ len_val |= (((clk_div_reg & 0xc00) >> 10) <<
+ SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET);
+ wr_regl(port, ureg->sirfsoc_tx_frame_ctrl, len_val);
+ /*rx frame ctrl*/
+ len_val = (data_bit_len - 1) << SIRFSOC_USP_RX_DATA_LEN_OFFSET;
+ len_val |= (data_bit_len + 1 + stop_bit_len - 1) <<
+ SIRFSOC_USP_RX_FRAME_LEN_OFFSET;
+ len_val |= (data_bit_len - 1) <<
+ SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET;
+ len_val |= (((clk_div_reg & 0xf000) >> 12) <<
+ SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET);
+ wr_regl(port, ureg->sirfsoc_rx_frame_ctrl, len_val);
+ /*async param*/
+ wr_regl(port, ureg->sirfsoc_async_param_reg,
+ (SIRFUART_RECV_TIMEOUT(port, rx_time_out)) |
+ (sample_div_reg & SIRFSOC_USP_ASYNC_DIV2_MASK) <<
+ SIRFSOC_USP_ASYNC_DIV2_OFFSET);
+ }
+ if (sirfport->tx_dma_chan)
+ wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_DMA_MODE);
+ else
+ wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_IO_MODE);
+ if (sirfport->rx_dma_chan)
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_DMA_MODE);
+ else
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_IO_MODE);
/* Reset Rx/Tx FIFO Threshold level for proper baudrate */
- if (baud_rate < 1000000)
+ if (set_baud < 1000000)
threshold_div = 1;
else
threshold_div = 2;
- temp = port->line == 1 ? 16 : 64;
- wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div);
- wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div);
- temp_reg_val |= SIRFUART_TX_FIFO_START;
- wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val);
- uart_update_timeout(port, termios->c_cflag, baud_rate);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl,
+ SIRFUART_FIFO_THD(port) / threshold_div);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl,
+ SIRFUART_FIFO_THD(port) / threshold_div);
+ txfifo_op_reg |= SIRFUART_FIFO_START;
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, txfifo_op_reg);
+ uart_update_timeout(port, termios->c_cflag, set_baud);
sirfsoc_uart_start_rx(port);
- wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN);
+ wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_TX_EN | SIRFUART_RX_EN);
spin_unlock_irqrestore(&port->lock, flags);
}
-static void startup_uart_controller(struct uart_port *port)
+static void sirfsoc_uart_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
{
- unsigned long temp_regv;
- int temp;
- temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL);
- wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO);
- temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL);
- wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO);
- wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0);
- wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0);
- wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN);
- wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET);
- wr_regl(port, SIRFUART_TX_FIFO_OP, 0);
- wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
- wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
- temp = port->line == 1 ? 16 : 64;
- wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp);
- wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp);
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ if (!state)
+ clk_prepare_enable(sirfport->clk);
+ else
+ clk_disable_unprepare(sirfport->clk);
}
static int sirfsoc_uart_startup(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
unsigned int index = port->line;
int ret;
set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN);
@@ -504,8 +1054,58 @@ static int sirfsoc_uart_startup(struct uart_port *port)
index, port->irq);
goto irq_err;
}
- startup_uart_controller(port);
+
+ /* initial hardware settings */
+ wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl) |
+ SIRFUART_IO_MODE);
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
+ SIRFUART_IO_MODE);
+ wr_regl(port, ureg->sirfsoc_tx_dma_io_len, 0);
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_len, 0);
+ wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_RX_EN | SIRFUART_TX_EN);
+ if (sirfport->uart_reg->uart_type == SIRF_USP_UART)
+ wr_regl(port, ureg->sirfsoc_mode1,
+ SIRFSOC_USP_ENDIAN_CTRL_LSBF |
+ SIRFSOC_USP_EN);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_RESET);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_op, 0);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, SIRFUART_FIFO_THD(port));
+ wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, SIRFUART_FIFO_THD(port));
+ if (sirfport->rx_dma_chan)
+ wr_regl(port, ureg->sirfsoc_rx_fifo_level_chk,
+ SIRFUART_RX_FIFO_CHK_SC(port->line, 0x4) |
+ SIRFUART_RX_FIFO_CHK_LC(port->line, 0xe) |
+ SIRFUART_RX_FIFO_CHK_HC(port->line, 0x1b));
+ if (sirfport->tx_dma_chan) {
+ sirfport->tx_dma_state = TX_DMA_IDLE;
+ wr_regl(port, ureg->sirfsoc_tx_fifo_level_chk,
+ SIRFUART_TX_FIFO_CHK_SC(port->line, 0x1b) |
+ SIRFUART_TX_FIFO_CHK_LC(port->line, 0xe) |
+ SIRFUART_TX_FIFO_CHK_HC(port->line, 0x4));
+ }
+ sirfport->ms_enabled = false;
+ if (sirfport->uart_reg->uart_type == SIRF_USP_UART &&
+ sirfport->hw_flow_ctrl) {
+ set_irq_flags(gpio_to_irq(sirfport->cts_gpio),
+ IRQF_VALID | IRQF_NOAUTOEN);
+ ret = request_irq(gpio_to_irq(sirfport->cts_gpio),
+ sirfsoc_uart_usp_cts_handler, IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING, "usp_cts_irq", sirfport);
+ if (ret != 0) {
+ dev_err(port->dev, "UART-USP:request gpio irq fail\n");
+ goto init_rx_err;
+ }
+ }
+
enable_irq(port->irq);
+
+ return 0;
+init_rx_err:
+ free_irq(port->irq, sirfport);
irq_err:
return ret;
}
@@ -513,12 +1113,22 @@ irq_err:
static void sirfsoc_uart_shutdown(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
- wr_regl(port, SIRFUART_INT_EN, 0);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ if (!sirfport->is_marco)
+ wr_regl(port, ureg->sirfsoc_int_en_reg, 0);
+ else
+ wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
+
free_irq(port->irq, sirfport);
- if (sirfport->ms_enabled) {
+ if (sirfport->ms_enabled)
sirfsoc_uart_disable_ms(port);
- sirfport->ms_enabled = 0;
+ if (sirfport->uart_reg->uart_type == SIRF_USP_UART &&
+ sirfport->hw_flow_ctrl) {
+ gpio_set_value(sirfport->rts_gpio, 1);
+ free_irq(gpio_to_irq(sirfport->cts_gpio), sirfport);
}
+ if (sirfport->tx_dma_chan)
+ sirfport->tx_dma_state = TX_DMA_IDLE;
}
static const char *sirfsoc_uart_type(struct uart_port *port)
@@ -528,9 +1138,11 @@ static const char *sirfsoc_uart_type(struct uart_port *port)
static int sirfsoc_uart_request_port(struct uart_port *port)
{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_uart_param *uart_param = &sirfport->uart_reg->uart_param;
void *ret;
ret = request_mem_region(port->mapbase,
- SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME);
+ SIRFUART_MAP_SIZE, uart_param->port_name);
return ret ? 0 : -EBUSY;
}
@@ -559,6 +1171,7 @@ static struct uart_ops sirfsoc_uart_ops = {
.startup = sirfsoc_uart_startup,
.shutdown = sirfsoc_uart_shutdown,
.set_termios = sirfsoc_uart_set_termios,
+ .pm = sirfsoc_uart_pm,
.type = sirfsoc_uart_type,
.release_port = sirfsoc_uart_release_port,
.request_port = sirfsoc_uart_request_port,
@@ -566,32 +1179,45 @@ static struct uart_ops sirfsoc_uart_ops = {
};
#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE
-static int __init sirfsoc_uart_console_setup(struct console *co, char *options)
+static int __init
+sirfsoc_uart_console_setup(struct console *co, char *options)
{
unsigned int baud = 115200;
unsigned int bits = 8;
unsigned int parity = 'n';
unsigned int flow = 'n';
struct uart_port *port = &sirfsoc_uart_ports[co->index].port;
-
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
if (co->index < 0 || co->index >= SIRFSOC_UART_NR)
return -EINVAL;
if (!port->mapbase)
return -ENODEV;
+ /* enable usp in mode1 register */
+ if (sirfport->uart_reg->uart_type == SIRF_USP_UART)
+ wr_regl(port, ureg->sirfsoc_mode1, SIRFSOC_USP_EN |
+ SIRFSOC_USP_ENDIAN_CTRL_LSBF);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
port->cons = co;
+
+ /* default console tx/rx transfer using io mode */
+ sirfport->rx_dma_chan = NULL;
+ sirfport->tx_dma_chan = NULL;
return uart_set_options(port, co, baud, parity, bits, flow);
}
static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch)
{
+ struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+ struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status;
while (rd_regl(port,
- SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port))
+ ureg->sirfsoc_tx_fifo_status) & ufifo_st->ff_full(port->line))
cpu_relax();
- wr_regb(port, SIRFUART_TX_FIFO_DATA, ch);
+ wr_regl(port, ureg->sirfsoc_tx_fifo_data, ch);
}
static void sirfsoc_uart_console_write(struct console *co, const char *s,
@@ -633,27 +1259,89 @@ static struct uart_driver sirfsoc_uart_drv = {
#endif
};
-int sirfsoc_uart_probe(struct platform_device *pdev)
+static struct of_device_id sirfsoc_uart_ids[] = {
+ { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,},
+ { .compatible = "sirf,marco-uart", .data = &sirfsoc_uart},
+ { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp},
+ {}
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids);
+
+static int sirfsoc_uart_probe(struct platform_device *pdev)
{
struct sirfsoc_uart_port *sirfport;
struct uart_port *port;
struct resource *res;
int ret;
-
+ int i, j;
+ struct dma_slave_config slv_cfg = {
+ .src_maxburst = 2,
+ };
+ struct dma_slave_config tx_slv_cfg = {
+ .dst_maxburst = 2,
+ };
+ const struct of_device_id *match;
+
+ match = of_match_node(sirfsoc_uart_ids, pdev->dev.of_node);
if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) {
dev_err(&pdev->dev,
"Unable to find cell-index in uart node.\n");
ret = -EFAULT;
goto err;
}
-
+ if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart"))
+ pdev->id += ((struct sirfsoc_uart_register *)
+ match->data)->uart_param.register_uart_nr;
sirfport = &sirfsoc_uart_ports[pdev->id];
port = &sirfport->port;
port->dev = &pdev->dev;
port->private_data = sirfport;
+ sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data;
+
+ sirfport->hw_flow_ctrl = of_property_read_bool(pdev->dev.of_node,
+ "sirf,uart-has-rtscts");
+ if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart"))
+ sirfport->uart_reg->uart_type = SIRF_REAL_UART;
+ if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) {
+ sirfport->uart_reg->uart_type = SIRF_USP_UART;
+ if (!sirfport->hw_flow_ctrl)
+ goto usp_no_flow_control;
+ if (of_find_property(pdev->dev.of_node, "cts-gpios", NULL))
+ sirfport->cts_gpio = of_get_named_gpio(
+ pdev->dev.of_node, "cts-gpios", 0);
+ else
+ sirfport->cts_gpio = -1;
+ if (of_find_property(pdev->dev.of_node, "rts-gpios", NULL))
+ sirfport->rts_gpio = of_get_named_gpio(
+ pdev->dev.of_node, "rts-gpios", 0);
+ else
+ sirfport->rts_gpio = -1;
- if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL))
- sirfport->hw_flow_ctrl = 1;
+ if ((!gpio_is_valid(sirfport->cts_gpio) ||
+ !gpio_is_valid(sirfport->rts_gpio))) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev,
+ "Usp flow control must have cts and rts gpio");
+ goto err;
+ }
+ ret = devm_gpio_request(&pdev->dev, sirfport->cts_gpio,
+ "usp-cts-gpio");
+ if (ret) {
+ dev_err(&pdev->dev, "Unable request cts gpio");
+ goto err;
+ }
+ gpio_direction_input(sirfport->cts_gpio);
+ ret = devm_gpio_request(&pdev->dev, sirfport->rts_gpio,
+ "usp-rts-gpio");
+ if (ret) {
+ dev_err(&pdev->dev, "Unable request rts gpio");
+ goto err;
+ }
+ gpio_direction_output(sirfport->rts_gpio, 1);
+ }
+usp_no_flow_control:
+ if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart"))
+ sirfport->is_marco = true;
if (of_property_read_u32(pdev->dev.of_node,
"fifosize",
@@ -670,6 +1358,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
ret = -EFAULT;
goto err;
}
+ tasklet_init(&sirfport->rx_dma_complete_tasklet,
+ sirfsoc_uart_rx_dma_complete_tl, (unsigned long)sirfport);
+ tasklet_init(&sirfport->rx_tmo_process_tasklet,
+ sirfsoc_rx_tmo_process_tl, (unsigned long)sirfport);
port->mapbase = res->start;
port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!port->membase) {
@@ -685,20 +1377,11 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
}
port->irq = res->start;
- if (sirfport->hw_flow_ctrl) {
- sirfport->p = pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(sirfport->p)) {
- ret = PTR_ERR(sirfport->p);
- goto err;
- }
- }
-
sirfport->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(sirfport->clk)) {
ret = PTR_ERR(sirfport->clk);
- goto clk_err;
+ goto err;
}
- clk_prepare_enable(sirfport->clk);
port->uartclk = clk_get_rate(sirfport->clk);
port->ops = &sirfsoc_uart_ops;
@@ -711,15 +1394,34 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
goto port_err;
}
- return 0;
+ sirfport->rx_dma_chan = dma_request_slave_channel(port->dev, "rx");
+ for (i = 0; sirfport->rx_dma_chan && i < SIRFSOC_RX_LOOP_BUF_CNT; i++) {
+ sirfport->rx_dma_items[i].xmit.buf =
+ dma_alloc_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE,
+ &sirfport->rx_dma_items[i].dma_addr, GFP_KERNEL);
+ if (!sirfport->rx_dma_items[i].xmit.buf) {
+ dev_err(port->dev, "Uart alloc bufa failed\n");
+ ret = -ENOMEM;
+ goto alloc_coherent_err;
+ }
+ sirfport->rx_dma_items[i].xmit.head =
+ sirfport->rx_dma_items[i].xmit.tail = 0;
+ }
+ if (sirfport->rx_dma_chan)
+ dmaengine_slave_config(sirfport->rx_dma_chan, &slv_cfg);
+ sirfport->tx_dma_chan = dma_request_slave_channel(port->dev, "tx");
+ if (sirfport->tx_dma_chan)
+ dmaengine_slave_config(sirfport->tx_dma_chan, &tx_slv_cfg);
+ return 0;
+alloc_coherent_err:
+ for (j = 0; j < i; j++)
+ dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE,
+ sirfport->rx_dma_items[j].xmit.buf,
+ sirfport->rx_dma_items[j].dma_addr);
+ dma_release_channel(sirfport->rx_dma_chan);
port_err:
- clk_disable_unprepare(sirfport->clk);
clk_put(sirfport->clk);
-clk_err:
- platform_set_drvdata(pdev, NULL);
- if (sirfport->hw_flow_ctrl)
- pinctrl_put(sirfport->p);
err:
return ret;
}
@@ -728,48 +1430,55 @@ static int sirfsoc_uart_remove(struct platform_device *pdev)
{
struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
struct uart_port *port = &sirfport->port;
- platform_set_drvdata(pdev, NULL);
- if (sirfport->hw_flow_ctrl)
- pinctrl_put(sirfport->p);
- clk_disable_unprepare(sirfport->clk);
clk_put(sirfport->clk);
uart_remove_one_port(&sirfsoc_uart_drv, port);
+ if (sirfport->rx_dma_chan) {
+ int i;
+ dmaengine_terminate_all(sirfport->rx_dma_chan);
+ dma_release_channel(sirfport->rx_dma_chan);
+ for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
+ dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE,
+ sirfport->rx_dma_items[i].xmit.buf,
+ sirfport->rx_dma_items[i].dma_addr);
+ }
+ if (sirfport->tx_dma_chan) {
+ dmaengine_terminate_all(sirfport->tx_dma_chan);
+ dma_release_channel(sirfport->tx_dma_chan);
+ }
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int
-sirfsoc_uart_suspend(struct platform_device *pdev, pm_message_t state)
+sirfsoc_uart_suspend(struct device *pdev)
{
- struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+ struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev);
struct uart_port *port = &sirfport->port;
uart_suspend_port(&sirfsoc_uart_drv, port);
return 0;
}
-static int sirfsoc_uart_resume(struct platform_device *pdev)
+static int sirfsoc_uart_resume(struct device *pdev)
{
- struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+ struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev);
struct uart_port *port = &sirfport->port;
uart_resume_port(&sirfsoc_uart_drv, port);
return 0;
}
+#endif
-static struct of_device_id sirfsoc_uart_ids[] = {
- { .compatible = "sirf,prima2-uart", },
- { .compatible = "sirf,marco-uart", },
- {}
+static const struct dev_pm_ops sirfsoc_uart_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_uart_suspend, sirfsoc_uart_resume)
};
-MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids);
static struct platform_driver sirfsoc_uart_driver = {
.probe = sirfsoc_uart_probe,
.remove = sirfsoc_uart_remove,
- .suspend = sirfsoc_uart_suspend,
- .resume = sirfsoc_uart_resume,
.driver = {
.name = SIRFUART_PORT_NAME,
.owner = THIS_MODULE,
.of_match_table = sirfsoc_uart_ids,
+ .pm = &sirfsoc_uart_pm_ops,
},
};
diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h
index 85328ba0c4e..69a62ebd3af 100644
--- a/drivers/tty/serial/sirfsoc_uart.h
+++ b/drivers/tty/serial/sirfsoc_uart.h
@@ -6,31 +6,260 @@
* Licensed under GPLv2 or later.
*/
#include <linux/bitops.h>
+struct sirfsoc_uart_param {
+ const char *uart_name;
+ const char *port_name;
+ u32 uart_nr;
+ u32 register_uart_nr;
+};
+
+struct sirfsoc_register {
+ /* hardware uart specific */
+ u32 sirfsoc_line_ctrl;
+ u32 sirfsoc_divisor;
+ /* uart - usp common */
+ u32 sirfsoc_tx_rx_en;
+ u32 sirfsoc_int_en_reg;
+ u32 sirfsoc_int_st_reg;
+ u32 sirfsoc_tx_dma_io_ctrl;
+ u32 sirfsoc_tx_dma_io_len;
+ u32 sirfsoc_tx_fifo_ctrl;
+ u32 sirfsoc_tx_fifo_level_chk;
+ u32 sirfsoc_tx_fifo_op;
+ u32 sirfsoc_tx_fifo_status;
+ u32 sirfsoc_tx_fifo_data;
+ u32 sirfsoc_rx_dma_io_ctrl;
+ u32 sirfsoc_rx_dma_io_len;
+ u32 sirfsoc_rx_fifo_ctrl;
+ u32 sirfsoc_rx_fifo_level_chk;
+ u32 sirfsoc_rx_fifo_op;
+ u32 sirfsoc_rx_fifo_status;
+ u32 sirfsoc_rx_fifo_data;
+ u32 sirfsoc_afc_ctrl;
+ u32 sirfsoc_swh_dma_io;
+ /* hardware usp specific */
+ u32 sirfsoc_mode1;
+ u32 sirfsoc_mode2;
+ u32 sirfsoc_tx_frame_ctrl;
+ u32 sirfsoc_rx_frame_ctrl;
+ u32 sirfsoc_async_param_reg;
+};
+
+typedef u32 (*fifo_full_mask)(int line);
+typedef u32 (*fifo_empty_mask)(int line);
+
+struct sirfsoc_fifo_status {
+ fifo_full_mask ff_full;
+ fifo_empty_mask ff_empty;
+};
+
+struct sirfsoc_int_en {
+ u32 sirfsoc_rx_done_en;
+ u32 sirfsoc_tx_done_en;
+ u32 sirfsoc_rx_oflow_en;
+ u32 sirfsoc_tx_allout_en;
+ u32 sirfsoc_rx_io_dma_en;
+ u32 sirfsoc_tx_io_dma_en;
+ u32 sirfsoc_rxfifo_full_en;
+ u32 sirfsoc_txfifo_empty_en;
+ u32 sirfsoc_rxfifo_thd_en;
+ u32 sirfsoc_txfifo_thd_en;
+ u32 sirfsoc_frm_err_en;
+ u32 sirfsoc_rxd_brk_en;
+ u32 sirfsoc_rx_timeout_en;
+ u32 sirfsoc_parity_err_en;
+ u32 sirfsoc_cts_en;
+ u32 sirfsoc_rts_en;
+};
+
+struct sirfsoc_int_status {
+ u32 sirfsoc_rx_done;
+ u32 sirfsoc_tx_done;
+ u32 sirfsoc_rx_oflow;
+ u32 sirfsoc_tx_allout;
+ u32 sirfsoc_rx_io_dma;
+ u32 sirfsoc_tx_io_dma;
+ u32 sirfsoc_rxfifo_full;
+ u32 sirfsoc_txfifo_empty;
+ u32 sirfsoc_rxfifo_thd;
+ u32 sirfsoc_txfifo_thd;
+ u32 sirfsoc_frm_err;
+ u32 sirfsoc_rxd_brk;
+ u32 sirfsoc_rx_timeout;
+ u32 sirfsoc_parity_err;
+ u32 sirfsoc_cts;
+ u32 sirfsoc_rts;
+};
+
+enum sirfsoc_uart_type {
+ SIRF_REAL_UART,
+ SIRF_USP_UART,
+};
-/* UART Register Offset Define */
-#define SIRFUART_LINE_CTRL 0x0040
-#define SIRFUART_TX_RX_EN 0x004c
-#define SIRFUART_DIVISOR 0x0050
-#define SIRFUART_INT_EN 0x0054
-#define SIRFUART_INT_STATUS 0x0058
-#define SIRFUART_TX_DMA_IO_CTRL 0x0100
-#define SIRFUART_TX_DMA_IO_LEN 0x0104
-#define SIRFUART_TX_FIFO_CTRL 0x0108
-#define SIRFUART_TX_FIFO_LEVEL_CHK 0x010C
-#define SIRFUART_TX_FIFO_OP 0x0110
-#define SIRFUART_TX_FIFO_STATUS 0x0114
-#define SIRFUART_TX_FIFO_DATA 0x0118
-#define SIRFUART_RX_DMA_IO_CTRL 0x0120
-#define SIRFUART_RX_DMA_IO_LEN 0x0124
-#define SIRFUART_RX_FIFO_CTRL 0x0128
-#define SIRFUART_RX_FIFO_LEVEL_CHK 0x012C
-#define SIRFUART_RX_FIFO_OP 0x0130
-#define SIRFUART_RX_FIFO_STATUS 0x0134
-#define SIRFUART_RX_FIFO_DATA 0x0138
-#define SIRFUART_AFC_CTRL 0x0140
-#define SIRFUART_SWH_DMA_IO 0x0148
-
-/* UART Line Control Register */
+struct sirfsoc_uart_register {
+ struct sirfsoc_register uart_reg;
+ struct sirfsoc_int_en uart_int_en;
+ struct sirfsoc_int_status uart_int_st;
+ struct sirfsoc_fifo_status fifo_status;
+ struct sirfsoc_uart_param uart_param;
+ enum sirfsoc_uart_type uart_type;
+};
+
+u32 usp_ff_full(int line)
+{
+ return 0x80;
+}
+u32 usp_ff_empty(int line)
+{
+ return 0x100;
+}
+u32 uart_ff_full(int line)
+{
+ return (line == 1) ? (0x20) : (0x80);
+}
+u32 uart_ff_empty(int line)
+{
+ return (line == 1) ? (0x40) : (0x100);
+}
+struct sirfsoc_uart_register sirfsoc_usp = {
+ .uart_reg = {
+ .sirfsoc_mode1 = 0x0000,
+ .sirfsoc_mode2 = 0x0004,
+ .sirfsoc_tx_frame_ctrl = 0x0008,
+ .sirfsoc_rx_frame_ctrl = 0x000c,
+ .sirfsoc_tx_rx_en = 0x0010,
+ .sirfsoc_int_en_reg = 0x0014,
+ .sirfsoc_int_st_reg = 0x0018,
+ .sirfsoc_async_param_reg = 0x0024,
+ .sirfsoc_tx_dma_io_ctrl = 0x0100,
+ .sirfsoc_tx_dma_io_len = 0x0104,
+ .sirfsoc_tx_fifo_ctrl = 0x0108,
+ .sirfsoc_tx_fifo_level_chk = 0x010c,
+ .sirfsoc_tx_fifo_op = 0x0110,
+ .sirfsoc_tx_fifo_status = 0x0114,
+ .sirfsoc_tx_fifo_data = 0x0118,
+ .sirfsoc_rx_dma_io_ctrl = 0x0120,
+ .sirfsoc_rx_dma_io_len = 0x0124,
+ .sirfsoc_rx_fifo_ctrl = 0x0128,
+ .sirfsoc_rx_fifo_level_chk = 0x012c,
+ .sirfsoc_rx_fifo_op = 0x0130,
+ .sirfsoc_rx_fifo_status = 0x0134,
+ .sirfsoc_rx_fifo_data = 0x0138,
+ },
+ .uart_int_en = {
+ .sirfsoc_rx_done_en = BIT(0),
+ .sirfsoc_tx_done_en = BIT(1),
+ .sirfsoc_rx_oflow_en = BIT(2),
+ .sirfsoc_tx_allout_en = BIT(3),
+ .sirfsoc_rx_io_dma_en = BIT(4),
+ .sirfsoc_tx_io_dma_en = BIT(5),
+ .sirfsoc_rxfifo_full_en = BIT(6),
+ .sirfsoc_txfifo_empty_en = BIT(7),
+ .sirfsoc_rxfifo_thd_en = BIT(8),
+ .sirfsoc_txfifo_thd_en = BIT(9),
+ .sirfsoc_frm_err_en = BIT(10),
+ .sirfsoc_rx_timeout_en = BIT(11),
+ .sirfsoc_rxd_brk_en = BIT(15),
+ },
+ .uart_int_st = {
+ .sirfsoc_rx_done = BIT(0),
+ .sirfsoc_tx_done = BIT(1),
+ .sirfsoc_rx_oflow = BIT(2),
+ .sirfsoc_tx_allout = BIT(3),
+ .sirfsoc_rx_io_dma = BIT(4),
+ .sirfsoc_tx_io_dma = BIT(5),
+ .sirfsoc_rxfifo_full = BIT(6),
+ .sirfsoc_txfifo_empty = BIT(7),
+ .sirfsoc_rxfifo_thd = BIT(8),
+ .sirfsoc_txfifo_thd = BIT(9),
+ .sirfsoc_frm_err = BIT(10),
+ .sirfsoc_rx_timeout = BIT(11),
+ .sirfsoc_rxd_brk = BIT(15),
+ },
+ .fifo_status = {
+ .ff_full = usp_ff_full,
+ .ff_empty = usp_ff_empty,
+ },
+ .uart_param = {
+ .uart_name = "ttySiRF",
+ .port_name = "sirfsoc-uart",
+ .uart_nr = 2,
+ .register_uart_nr = 3,
+ },
+};
+
+struct sirfsoc_uart_register sirfsoc_uart = {
+ .uart_reg = {
+ .sirfsoc_line_ctrl = 0x0040,
+ .sirfsoc_tx_rx_en = 0x004c,
+ .sirfsoc_divisor = 0x0050,
+ .sirfsoc_int_en_reg = 0x0054,
+ .sirfsoc_int_st_reg = 0x0058,
+ .sirfsoc_tx_dma_io_ctrl = 0x0100,
+ .sirfsoc_tx_dma_io_len = 0x0104,
+ .sirfsoc_tx_fifo_ctrl = 0x0108,
+ .sirfsoc_tx_fifo_level_chk = 0x010c,
+ .sirfsoc_tx_fifo_op = 0x0110,
+ .sirfsoc_tx_fifo_status = 0x0114,
+ .sirfsoc_tx_fifo_data = 0x0118,
+ .sirfsoc_rx_dma_io_ctrl = 0x0120,
+ .sirfsoc_rx_dma_io_len = 0x0124,
+ .sirfsoc_rx_fifo_ctrl = 0x0128,
+ .sirfsoc_rx_fifo_level_chk = 0x012c,
+ .sirfsoc_rx_fifo_op = 0x0130,
+ .sirfsoc_rx_fifo_status = 0x0134,
+ .sirfsoc_rx_fifo_data = 0x0138,
+ .sirfsoc_afc_ctrl = 0x0140,
+ .sirfsoc_swh_dma_io = 0x0148,
+ },
+ .uart_int_en = {
+ .sirfsoc_rx_done_en = BIT(0),
+ .sirfsoc_tx_done_en = BIT(1),
+ .sirfsoc_rx_oflow_en = BIT(2),
+ .sirfsoc_tx_allout_en = BIT(3),
+ .sirfsoc_rx_io_dma_en = BIT(4),
+ .sirfsoc_tx_io_dma_en = BIT(5),
+ .sirfsoc_rxfifo_full_en = BIT(6),
+ .sirfsoc_txfifo_empty_en = BIT(7),
+ .sirfsoc_rxfifo_thd_en = BIT(8),
+ .sirfsoc_txfifo_thd_en = BIT(9),
+ .sirfsoc_frm_err_en = BIT(10),
+ .sirfsoc_rxd_brk_en = BIT(11),
+ .sirfsoc_rx_timeout_en = BIT(12),
+ .sirfsoc_parity_err_en = BIT(13),
+ .sirfsoc_cts_en = BIT(14),
+ .sirfsoc_rts_en = BIT(15),
+ },
+ .uart_int_st = {
+ .sirfsoc_rx_done = BIT(0),
+ .sirfsoc_tx_done = BIT(1),
+ .sirfsoc_rx_oflow = BIT(2),
+ .sirfsoc_tx_allout = BIT(3),
+ .sirfsoc_rx_io_dma = BIT(4),
+ .sirfsoc_tx_io_dma = BIT(5),
+ .sirfsoc_rxfifo_full = BIT(6),
+ .sirfsoc_txfifo_empty = BIT(7),
+ .sirfsoc_rxfifo_thd = BIT(8),
+ .sirfsoc_txfifo_thd = BIT(9),
+ .sirfsoc_frm_err = BIT(10),
+ .sirfsoc_rxd_brk = BIT(11),
+ .sirfsoc_rx_timeout = BIT(12),
+ .sirfsoc_parity_err = BIT(13),
+ .sirfsoc_cts = BIT(14),
+ .sirfsoc_rts = BIT(15),
+ },
+ .fifo_status = {
+ .ff_full = uart_ff_full,
+ .ff_empty = uart_ff_empty,
+ },
+ .uart_param = {
+ .uart_name = "ttySiRF",
+ .port_name = "sirfsoc_uart",
+ .uart_nr = 3,
+ .register_uart_nr = 0,
+ },
+};
+/* uart io ctrl */
#define SIRFUART_DATA_BIT_LEN_MASK 0x3
#define SIRFUART_DATA_BIT_LEN_5 BIT(0)
#define SIRFUART_DATA_BIT_LEN_6 1
@@ -50,98 +279,26 @@
#define SIRFUART_LOOP_BACK BIT(7)
#define SIRFUART_PARITY_MASK (7 << 3)
#define SIRFUART_DUMMY_READ BIT(16)
-
-#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000)
-#define SIRFUART_RECV_TIMEOUT_MASK (0xFFFF << 16)
-#define SIRFUART_RECV_TIMEOUT(x) (((x) & 0xFFFF) << 16)
-
-/* UART Auto Flow Control */
-#define SIRFUART_AFC_RX_THD_MASK 0x000000FF
+#define SIRFUART_AFC_CTRL_RX_THD 0x70
#define SIRFUART_AFC_RX_EN BIT(8)
#define SIRFUART_AFC_TX_EN BIT(9)
-#define SIRFUART_CTS_CTRL BIT(10)
-#define SIRFUART_RTS_CTRL BIT(11)
-#define SIRFUART_CTS_IN_STATUS BIT(12)
-#define SIRFUART_RTS_OUT_STATUS BIT(13)
-
-/* UART Interrupt Enable Register */
-#define SIRFUART_RX_DONE_INT BIT(0)
-#define SIRFUART_TX_DONE_INT BIT(1)
-#define SIRFUART_RX_OFLOW_INT BIT(2)
-#define SIRFUART_TX_ALLOUT_INT BIT(3)
-#define SIRFUART_RX_IO_DMA_INT BIT(4)
-#define SIRFUART_TX_IO_DMA_INT BIT(5)
-#define SIRFUART_RXFIFO_FULL_INT BIT(6)
-#define SIRFUART_TXFIFO_EMPTY_INT BIT(7)
-#define SIRFUART_RXFIFO_THD_INT BIT(8)
-#define SIRFUART_TXFIFO_THD_INT BIT(9)
-#define SIRFUART_FRM_ERR_INT BIT(10)
-#define SIRFUART_RXD_BREAK_INT BIT(11)
-#define SIRFUART_RX_TIMEOUT_INT BIT(12)
-#define SIRFUART_PARITY_ERR_INT BIT(13)
-#define SIRFUART_CTS_INT_EN BIT(14)
-#define SIRFUART_RTS_INT_EN BIT(15)
-
-/* UART Interrupt Status Register */
-#define SIRFUART_RX_DONE BIT(0)
-#define SIRFUART_TX_DONE BIT(1)
-#define SIRFUART_RX_OFLOW BIT(2)
-#define SIRFUART_TX_ALL_EMPTY BIT(3)
-#define SIRFUART_DMA_IO_RX_DONE BIT(4)
-#define SIRFUART_DMA_IO_TX_DONE BIT(5)
-#define SIRFUART_RXFIFO_FULL BIT(6)
-#define SIRFUART_TXFIFO_EMPTY BIT(7)
-#define SIRFUART_RXFIFO_THD_REACH BIT(8)
-#define SIRFUART_TXFIFO_THD_REACH BIT(9)
-#define SIRFUART_FRM_ERR BIT(10)
-#define SIRFUART_RXD_BREAK BIT(11)
-#define SIRFUART_RX_TIMEOUT BIT(12)
-#define SIRFUART_PARITY_ERR BIT(13)
-#define SIRFUART_CTS_CHANGE BIT(14)
-#define SIRFUART_RTS_CHANGE BIT(15)
-#define SIRFUART_PLUG_IN BIT(16)
-
-#define SIRFUART_ERR_INT_STAT \
- (SIRFUART_RX_OFLOW | \
- SIRFUART_FRM_ERR | \
- SIRFUART_RXD_BREAK | \
- SIRFUART_PARITY_ERR)
-#define SIRFUART_ERR_INT_EN \
- (SIRFUART_RX_OFLOW_INT | \
- SIRFUART_FRM_ERR_INT | \
- SIRFUART_RXD_BREAK_INT | \
- SIRFUART_PARITY_ERR_INT)
-#define SIRFUART_TX_INT_EN SIRFUART_TXFIFO_EMPTY_INT
-#define SIRFUART_RX_IO_INT_EN \
- (SIRFUART_RX_TIMEOUT_INT | \
- SIRFUART_RXFIFO_THD_INT | \
- SIRFUART_RXFIFO_FULL_INT | \
- SIRFUART_ERR_INT_EN)
-
+#define SIRFUART_AFC_CTS_CTRL BIT(10)
+#define SIRFUART_AFC_RTS_CTRL BIT(11)
+#define SIRFUART_AFC_CTS_STATUS BIT(12)
+#define SIRFUART_AFC_RTS_STATUS BIT(13)
/* UART FIFO Register */
-#define SIRFUART_TX_FIFO_STOP 0x0
-#define SIRFUART_TX_FIFO_RESET 0x1
-#define SIRFUART_TX_FIFO_START 0x2
-#define SIRFUART_RX_FIFO_STOP 0x0
-#define SIRFUART_RX_FIFO_RESET 0x1
-#define SIRFUART_RX_FIFO_START 0x2
-#define SIRFUART_TX_MODE_DMA 0
-#define SIRFUART_TX_MODE_IO 1
-#define SIRFUART_RX_MODE_DMA 0
-#define SIRFUART_RX_MODE_IO 1
-
-#define SIRFUART_RX_EN 0x1
-#define SIRFUART_TX_EN 0x2
+#define SIRFUART_FIFO_STOP 0x0
+#define SIRFUART_FIFO_RESET BIT(0)
+#define SIRFUART_FIFO_START BIT(1)
-/* Generic Definitions */
-#define SIRFSOC_UART_NAME "ttySiRF"
-#define SIRFSOC_UART_MAJOR 0
-#define SIRFSOC_UART_MINOR 0
-#define SIRFUART_PORT_NAME "sirfsoc-uart"
-#define SIRFUART_MAP_SIZE 0x200
-#define SIRFSOC_UART_NR 5
-#define SIRFSOC_PORT_TYPE 0xa5
+#define SIRFUART_RX_EN BIT(0)
+#define SIRFUART_TX_EN BIT(1)
+
+#define SIRFUART_IO_MODE BIT(0)
+#define SIRFUART_DMA_MODE 0x0
+/* Macro Specific*/
+#define SIRFUART_INT_EN_CLR 0x0060
/* Baud Rate Calculation */
#define SIRF_MIN_SAMPLE_DIV 0xf
#define SIRF_MAX_SAMPLE_DIV 0x3f
@@ -151,29 +308,138 @@
#define SIRF_SAMPLE_DIV_MASK 0x3f0000
#define SIRF_BAUD_RATE_SUPPORT_NR 18
+/* USP SPEC */
+#define SIRFSOC_USP_ENDIAN_CTRL_LSBF BIT(4)
+#define SIRFSOC_USP_EN BIT(5)
+#define SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET 0
+#define SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET 8
+#define SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK 0x3ff
+#define SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET 21
+#define SIRFSOC_USP_TX_DATA_LEN_OFFSET 0
+#define SIRFSOC_USP_TX_SYNC_LEN_OFFSET 8
+#define SIRFSOC_USP_TX_FRAME_LEN_OFFSET 16
+#define SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET 24
+#define SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET 30
+#define SIRFSOC_USP_RX_DATA_LEN_OFFSET 0
+#define SIRFSOC_USP_RX_FRAME_LEN_OFFSET 8
+#define SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET 16
+#define SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET 24
+#define SIRFSOC_USP_ASYNC_DIV2_MASK 0x3f
+#define SIRFSOC_USP_ASYNC_DIV2_OFFSET 16
+
+/* USP-UART Common */
+#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000)
+#define SIRFUART_RECV_TIMEOUT_VALUE(x) \
+ (((x) > 0xFFFF) ? 0xFFFF : ((x) & 0xFFFF))
+#define SIRFUART_RECV_TIMEOUT(port, x) \
+ (((port)->line > 2) ? (x & 0xFFFF) : ((x) & 0xFFFF) << 16)
+
+#define SIRFUART_FIFO_THD(port) ((port->line) == 1 ? 16 : 64)
+#define SIRFUART_ERR_INT_STAT(port, unit_st) \
+ (uint_st->sirfsoc_rx_oflow | \
+ uint_st->sirfsoc_frm_err | \
+ uint_st->sirfsoc_rxd_brk | \
+ ((port->line > 2) ? 0 : uint_st->sirfsoc_parity_err))
+#define SIRFUART_RX_IO_INT_EN(port, uint_en) \
+ (uint_en->sirfsoc_rx_timeout_en |\
+ uint_en->sirfsoc_rxfifo_thd_en |\
+ uint_en->sirfsoc_rxfifo_full_en |\
+ uint_en->sirfsoc_frm_err_en |\
+ uint_en->sirfsoc_rx_oflow_en |\
+ uint_en->sirfsoc_rxd_brk_en |\
+ ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en))
+#define SIRFUART_RX_IO_INT_ST(uint_st) \
+ (uint_st->sirfsoc_rx_timeout |\
+ uint_st->sirfsoc_rxfifo_thd |\
+ uint_st->sirfsoc_rxfifo_full)
+#define SIRFUART_CTS_INT_ST(uint_st) (uint_st->sirfsoc_cts)
+#define SIRFUART_RX_DMA_INT_EN(port, uint_en) \
+ (uint_en->sirfsoc_rx_timeout_en |\
+ uint_en->sirfsoc_frm_err_en |\
+ uint_en->sirfsoc_rx_oflow_en |\
+ uint_en->sirfsoc_rxd_brk_en |\
+ ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en))
+/* Generic Definitions */
+#define SIRFSOC_UART_NAME "ttySiRF"
+#define SIRFSOC_UART_MAJOR 0
+#define SIRFSOC_UART_MINOR 0
+#define SIRFUART_PORT_NAME "sirfsoc-uart"
+#define SIRFUART_MAP_SIZE 0x200
+#define SIRFSOC_UART_NR 6
+#define SIRFSOC_PORT_TYPE 0xa5
+
+/* Uart Common Use Macro*/
+#define SIRFSOC_RX_DMA_BUF_SIZE 256
+#define BYTES_TO_ALIGN(dma_addr) ((unsigned long)(dma_addr) & 0x3)
+#define LOOP_DMA_BUFA_FILL 1
+#define LOOP_DMA_BUFB_FILL 2
+#define TX_TRAN_PIO 1
+#define TX_TRAN_DMA 2
+/* Uart Fifo Level Chk */
+#define SIRFUART_TX_FIFO_SC_OFFSET 0
+#define SIRFUART_TX_FIFO_LC_OFFSET 10
+#define SIRFUART_TX_FIFO_HC_OFFSET 20
+#define SIRFUART_TX_FIFO_CHK_SC(line, value) ((((line) == 1) ? (value & 0x3) :\
+ (value & 0x1f)) << SIRFUART_TX_FIFO_SC_OFFSET)
+#define SIRFUART_TX_FIFO_CHK_LC(line, value) ((((line) == 1) ? (value & 0x3) :\
+ (value & 0x1f)) << SIRFUART_TX_FIFO_LC_OFFSET)
+#define SIRFUART_TX_FIFO_CHK_HC(line, value) ((((line) == 1) ? (value & 0x3) :\
+ (value & 0x1f)) << SIRFUART_TX_FIFO_HC_OFFSET)
+
+#define SIRFUART_RX_FIFO_CHK_SC SIRFUART_TX_FIFO_CHK_SC
+#define SIRFUART_RX_FIFO_CHK_LC SIRFUART_TX_FIFO_CHK_LC
+#define SIRFUART_RX_FIFO_CHK_HC SIRFUART_TX_FIFO_CHK_HC
+/* Indicate how many buffers used */
+#define SIRFSOC_RX_LOOP_BUF_CNT 2
+
/* For Fast Baud Rate Calculation */
struct sirfsoc_baudrate_to_regv {
unsigned int baud_rate;
unsigned int reg_val;
};
+enum sirfsoc_tx_state {
+ TX_DMA_IDLE,
+ TX_DMA_RUNNING,
+ TX_DMA_PAUSE,
+};
+
+struct sirfsoc_loop_buffer {
+ struct circ_buf xmit;
+ dma_cookie_t cookie;
+ struct dma_async_tx_descriptor *desc;
+ dma_addr_t dma_addr;
+};
+
struct sirfsoc_uart_port {
- unsigned char hw_flow_ctrl;
- unsigned char ms_enabled;
+ bool hw_flow_ctrl;
+ bool ms_enabled;
struct uart_port port;
- struct pinctrl *p;
struct clk *clk;
-};
+ /* for SiRFmarco, there are SET/CLR for UART_INT_EN */
+ bool is_marco;
+ struct sirfsoc_uart_register *uart_reg;
+ struct dma_chan *rx_dma_chan;
+ struct dma_chan *tx_dma_chan;
+ dma_addr_t tx_dma_addr;
+ struct dma_async_tx_descriptor *tx_dma_desc;
+ struct tasklet_struct rx_dma_complete_tasklet;
+ struct tasklet_struct rx_tmo_process_tasklet;
+ unsigned int rx_io_count;
+ unsigned long transfer_size;
+ enum sirfsoc_tx_state tx_dma_state;
+ unsigned int cts_gpio;
+ unsigned int rts_gpio;
-/* Hardware Flow Control */
-#define SIRFUART_AFC_CTRL_RX_THD 0x70
+ struct sirfsoc_loop_buffer rx_dma_items[SIRFSOC_RX_LOOP_BUF_CNT];
+ int rx_completed;
+ int rx_issued;
+};
/* Register Access Control */
#define portaddr(port, reg) ((port)->membase + (reg))
-#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
-#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))
/* UART Port Mask */
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
new file mode 100644
index 00000000000..f48b1cc07ee
--- /dev/null
+++ b/drivers/tty/serial/st-asc.c
@@ -0,0 +1,932 @@
+/*
+ * st-asc.c: ST Asynchronous serial controller (ASC) driver
+ *
+ * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#if defined(CONFIG_SERIAL_ST_ASC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/serial_core.h>
+#include <linux/clk.h>
+
+#define DRIVER_NAME "st-asc"
+#define ASC_SERIAL_NAME "ttyAS"
+#define ASC_FIFO_SIZE 16
+#define ASC_MAX_PORTS 8
+
+struct asc_port {
+ struct uart_port port;
+ struct clk *clk;
+ unsigned int hw_flow_control:1;
+ unsigned int force_m1:1;
+};
+
+static struct asc_port asc_ports[ASC_MAX_PORTS];
+static struct uart_driver asc_uart_driver;
+
+/*---- UART Register definitions ------------------------------*/
+
+/* Register offsets */
+
+#define ASC_BAUDRATE 0x00
+#define ASC_TXBUF 0x04
+#define ASC_RXBUF 0x08
+#define ASC_CTL 0x0C
+#define ASC_INTEN 0x10
+#define ASC_STA 0x14
+#define ASC_GUARDTIME 0x18
+#define ASC_TIMEOUT 0x1C
+#define ASC_TXRESET 0x20
+#define ASC_RXRESET 0x24
+#define ASC_RETRIES 0x28
+
+/* ASC_RXBUF */
+#define ASC_RXBUF_PE 0x100
+#define ASC_RXBUF_FE 0x200
+/**
+ * Some of status comes from higher bits of the character and some come from
+ * the status register. Combining both of them in to single status using dummy
+ * bits.
+ */
+#define ASC_RXBUF_DUMMY_RX 0x10000
+#define ASC_RXBUF_DUMMY_BE 0x20000
+#define ASC_RXBUF_DUMMY_OE 0x40000
+
+/* ASC_CTL */
+
+#define ASC_CTL_MODE_MSK 0x0007
+#define ASC_CTL_MODE_8BIT 0x0001
+#define ASC_CTL_MODE_7BIT_PAR 0x0003
+#define ASC_CTL_MODE_9BIT 0x0004
+#define ASC_CTL_MODE_8BIT_WKUP 0x0005
+#define ASC_CTL_MODE_8BIT_PAR 0x0007
+#define ASC_CTL_STOP_MSK 0x0018
+#define ASC_CTL_STOP_HALFBIT 0x0000
+#define ASC_CTL_STOP_1BIT 0x0008
+#define ASC_CTL_STOP_1_HALFBIT 0x0010
+#define ASC_CTL_STOP_2BIT 0x0018
+#define ASC_CTL_PARITYODD 0x0020
+#define ASC_CTL_LOOPBACK 0x0040
+#define ASC_CTL_RUN 0x0080
+#define ASC_CTL_RXENABLE 0x0100
+#define ASC_CTL_SCENABLE 0x0200
+#define ASC_CTL_FIFOENABLE 0x0400
+#define ASC_CTL_CTSENABLE 0x0800
+#define ASC_CTL_BAUDMODE 0x1000
+
+/* ASC_GUARDTIME */
+
+#define ASC_GUARDTIME_MSK 0x00FF
+
+/* ASC_INTEN */
+
+#define ASC_INTEN_RBE 0x0001
+#define ASC_INTEN_TE 0x0002
+#define ASC_INTEN_THE 0x0004
+#define ASC_INTEN_PE 0x0008
+#define ASC_INTEN_FE 0x0010
+#define ASC_INTEN_OE 0x0020
+#define ASC_INTEN_TNE 0x0040
+#define ASC_INTEN_TOI 0x0080
+#define ASC_INTEN_RHF 0x0100
+
+/* ASC_RETRIES */
+
+#define ASC_RETRIES_MSK 0x00FF
+
+/* ASC_RXBUF */
+
+#define ASC_RXBUF_MSK 0x03FF
+
+/* ASC_STA */
+
+#define ASC_STA_RBF 0x0001
+#define ASC_STA_TE 0x0002
+#define ASC_STA_THE 0x0004
+#define ASC_STA_PE 0x0008
+#define ASC_STA_FE 0x0010
+#define ASC_STA_OE 0x0020
+#define ASC_STA_TNE 0x0040
+#define ASC_STA_TOI 0x0080
+#define ASC_STA_RHF 0x0100
+#define ASC_STA_TF 0x0200
+#define ASC_STA_NKD 0x0400
+
+/* ASC_TIMEOUT */
+
+#define ASC_TIMEOUT_MSK 0x00FF
+
+/* ASC_TXBUF */
+
+#define ASC_TXBUF_MSK 0x01FF
+
+/*---- Inline function definitions ---------------------------*/
+
+static inline struct asc_port *to_asc_port(struct uart_port *port)
+{
+ return container_of(port, struct asc_port, port);
+}
+
+static inline u32 asc_in(struct uart_port *port, u32 offset)
+{
+ return readl(port->membase + offset);
+}
+
+static inline void asc_out(struct uart_port *port, u32 offset, u32 value)
+{
+ writel(value, port->membase + offset);
+}
+
+/*
+ * Some simple utility functions to enable and disable interrupts.
+ * Note that these need to be called with interrupts disabled.
+ */
+static inline void asc_disable_tx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE;
+ asc_out(port, ASC_INTEN, intenable);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+}
+
+static inline void asc_enable_tx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE;
+ asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline void asc_disable_rx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE;
+ asc_out(port, ASC_INTEN, intenable);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+}
+
+static inline void asc_enable_rx_interrupts(struct uart_port *port)
+{
+ u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE;
+ asc_out(port, ASC_INTEN, intenable);
+}
+
+static inline u32 asc_txfifo_is_empty(struct uart_port *port)
+{
+ return asc_in(port, ASC_STA) & ASC_STA_TE;
+}
+
+static inline u32 asc_txfifo_is_half_empty(struct uart_port *port)
+{
+ return asc_in(port, ASC_STA) & ASC_STA_THE;
+}
+
+static inline const char *asc_port_name(struct uart_port *port)
+{
+ return to_platform_device(port->dev)->name;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * This section contains code to support the use of the ASC as a
+ * generic serial port.
+ */
+
+static inline unsigned asc_hw_txroom(struct uart_port *port)
+{
+ u32 status = asc_in(port, ASC_STA);
+
+ if (status & ASC_STA_THE)
+ return port->fifosize / 2;
+ else if (!(status & ASC_STA_TF))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Start transmitting chars.
+ * This is called from both interrupt and task level.
+ * Either way interrupts are disabled.
+ */
+static void asc_transmit_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ int txroom;
+ unsigned char c;
+
+ txroom = asc_hw_txroom(port);
+
+ if ((txroom != 0) && port->x_char) {
+ c = port->x_char;
+ port->x_char = 0;
+ asc_out(port, ASC_TXBUF, c);
+ port->icount.tx++;
+ txroom = asc_hw_txroom(port);
+ }
+
+ if (uart_tx_stopped(port)) {
+ /*
+ * We should try and stop the hardware here, but I
+ * don't think the ASC has any way to do that.
+ */
+ asc_disable_tx_interrupts(port);
+ return;
+ }
+
+ if (uart_circ_empty(xmit)) {
+ asc_disable_tx_interrupts(port);
+ return;
+ }
+
+ if (txroom == 0)
+ return;
+
+ do {
+ c = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ asc_out(port, ASC_TXBUF, c);
+ port->icount.tx++;
+ txroom--;
+ } while ((txroom > 0) && (!uart_circ_empty(xmit)));
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ asc_disable_tx_interrupts(port);
+}
+
+static void asc_receive_chars(struct uart_port *port)
+{
+ struct tty_port *tport = &port->state->port;
+ unsigned long status;
+ unsigned long c = 0;
+ char flag;
+
+ if (port->irq_wake)
+ pm_wakeup_event(tport->tty->dev, 0);
+
+ while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) {
+ c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX;
+ flag = TTY_NORMAL;
+ port->icount.rx++;
+
+ if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) ||
+ status & ASC_STA_OE) {
+
+ if (c & ASC_RXBUF_FE) {
+ if (c == (ASC_RXBUF_FE | ASC_RXBUF_DUMMY_RX)) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ c |= ASC_RXBUF_DUMMY_BE;
+ } else {
+ port->icount.frame++;
+ }
+ } else if (c & ASC_RXBUF_PE) {
+ port->icount.parity++;
+ }
+ /*
+ * Reading any data from the RX FIFO clears the
+ * overflow error condition.
+ */
+ if (status & ASC_STA_OE) {
+ port->icount.overrun++;
+ c |= ASC_RXBUF_DUMMY_OE;
+ }
+
+ c &= port->read_status_mask;
+
+ if (c & ASC_RXBUF_DUMMY_BE)
+ flag = TTY_BREAK;
+ else if (c & ASC_RXBUF_PE)
+ flag = TTY_PARITY;
+ else if (c & ASC_RXBUF_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(port, c & 0xff))
+ continue;
+
+ uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag);
+ }
+
+ /* Tell the rest of the system the news. New characters! */
+ tty_flip_buffer_push(tport);
+}
+
+static irqreturn_t asc_interrupt(int irq, void *ptr)
+{
+ struct uart_port *port = ptr;
+ u32 status;
+
+ spin_lock(&port->lock);
+
+ status = asc_in(port, ASC_STA);
+
+ if (status & ASC_STA_RBF) {
+ /* Receive FIFO not empty */
+ asc_receive_chars(port);
+ }
+
+ if ((status & ASC_STA_THE) &&
+ (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) {
+ /* Transmitter FIFO at least half empty */
+ asc_transmit_chars(port);
+ }
+
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * UART Functions
+ */
+
+static unsigned int asc_tx_empty(struct uart_port *port)
+{
+ return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0;
+}
+
+static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /*
+ * This routine is used for seting signals of: DTR, DCD, CTS/RTS
+ * We use ASC's hardware for CTS/RTS, so don't need any for that.
+ * Some boards have DTR and DCD implemented using PIO pins,
+ * code to do this should be hooked in here.
+ */
+}
+
+static unsigned int asc_get_mctrl(struct uart_port *port)
+{
+ /*
+ * This routine is used for geting signals of: DTR, DCD, DSR, RI,
+ * and CTS/RTS
+ */
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+/* There are probably characters waiting to be transmitted. */
+static void asc_start_tx(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+
+ if (!uart_circ_empty(xmit))
+ asc_enable_tx_interrupts(port);
+}
+
+/* Transmit stop */
+static void asc_stop_tx(struct uart_port *port)
+{
+ asc_disable_tx_interrupts(port);
+}
+
+/* Receive stop */
+static void asc_stop_rx(struct uart_port *port)
+{
+ asc_disable_rx_interrupts(port);
+}
+
+/* Force modem status interrupts on */
+static void asc_enable_ms(struct uart_port *port)
+{
+ /* Nothing here yet .. */
+}
+
+/* Handle breaks - ignored by us */
+static void asc_break_ctl(struct uart_port *port, int break_state)
+{
+ /* Nothing here yet .. */
+}
+
+/*
+ * Enable port for reception.
+ */
+static int asc_startup(struct uart_port *port)
+{
+ if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND,
+ asc_port_name(port), port)) {
+ dev_err(port->dev, "cannot allocate irq.\n");
+ return -ENODEV;
+ }
+
+ asc_transmit_chars(port);
+ asc_enable_rx_interrupts(port);
+
+ return 0;
+}
+
+static void asc_shutdown(struct uart_port *port)
+{
+ asc_disable_tx_interrupts(port);
+ asc_disable_rx_interrupts(port);
+ free_irq(port->irq, port);
+}
+
+static void asc_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct asc_port *ascport = to_asc_port(port);
+ unsigned long flags = 0;
+ u32 ctl;
+
+ switch (state) {
+ case UART_PM_STATE_ON:
+ clk_prepare_enable(ascport->clk);
+ break;
+ case UART_PM_STATE_OFF:
+ /*
+ * Disable the ASC baud rate generator, which is as close as
+ * we can come to turning it off. Note this is not called with
+ * the port spinlock held.
+ */
+ spin_lock_irqsave(&port->lock, flags);
+ ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN;
+ asc_out(port, ASC_CTL, ctl);
+ spin_unlock_irqrestore(&port->lock, flags);
+ clk_disable_unprepare(ascport->clk);
+ break;
+ }
+}
+
+static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct asc_port *ascport = to_asc_port(port);
+ unsigned int baud;
+ u32 ctrl_val;
+ tcflag_t cflag;
+ unsigned long flags;
+
+ /* Update termios to reflect hardware capabilities */
+ termios->c_cflag &= ~(CMSPAR |
+ (ascport->hw_flow_control ? 0 : CRTSCTS));
+
+ port->uartclk = clk_get_rate(ascport->clk);
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ cflag = termios->c_cflag;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* read control register */
+ ctrl_val = asc_in(port, ASC_CTL);
+
+ /* stop serial port and reset value */
+ asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN));
+ ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE;
+
+ /* reset fifo rx & tx */
+ asc_out(port, ASC_TXRESET, 1);
+ asc_out(port, ASC_RXRESET, 1);
+
+ /* set character length */
+ if ((cflag & CSIZE) == CS7) {
+ ctrl_val |= ASC_CTL_MODE_7BIT_PAR;
+ } else {
+ ctrl_val |= (cflag & PARENB) ? ASC_CTL_MODE_8BIT_PAR :
+ ASC_CTL_MODE_8BIT;
+ }
+
+ /* set stop bit */
+ ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT;
+
+ /* odd parity */
+ if (cflag & PARODD)
+ ctrl_val |= ASC_CTL_PARITYODD;
+
+ /* hardware flow control */
+ if ((cflag & CRTSCTS))
+ ctrl_val |= ASC_CTL_CTSENABLE;
+
+ if ((baud < 19200) && !ascport->force_m1) {
+ asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud)));
+ } else {
+ /*
+ * MODE 1: recommended for high bit rates (above 19.2K)
+ *
+ * baudrate * 16 * 2^16
+ * ASCBaudRate = ------------------------
+ * inputclock
+ *
+ * However to keep the maths inside 32bits we divide top and
+ * bottom by 64. The +1 is to avoid a divide by zero if the
+ * input clock rate is something unexpected.
+ */
+ u32 counter = (baud * 16384) / ((port->uartclk / 64) + 1);
+ asc_out(port, ASC_BAUDRATE, counter);
+ ctrl_val |= ASC_CTL_BAUDMODE;
+ }
+
+ uart_update_timeout(port, cflag, baud);
+
+ ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE;
+ if (termios->c_iflag & INPCK)
+ ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+ ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE;
+
+ /*
+ * Characters to ignore
+ */
+ ascport->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE;
+ if (termios->c_iflag & IGNBRK) {
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE;
+ }
+
+ /*
+ * Ignore all characters if CREAD is not set.
+ */
+ if (!(termios->c_cflag & CREAD))
+ ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX;
+
+ /* Set the timeout */
+ asc_out(port, ASC_TIMEOUT, 20);
+
+ /* write final value and enable port */
+ asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN));
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *asc_type(struct uart_port *port)
+{
+ return (port->type == PORT_ASC) ? DRIVER_NAME : NULL;
+}
+
+static void asc_release_port(struct uart_port *port)
+{
+}
+
+static int asc_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set
+ * Set type field if successful
+ */
+static void asc_config_port(struct uart_port *port, int flags)
+{
+ if ((flags & UART_CONFIG_TYPE))
+ port->type = PORT_ASC;
+}
+
+static int
+asc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* No user changeable parameters */
+ return -EINVAL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context (i.e. kgdb).
+ */
+
+static int asc_get_poll_char(struct uart_port *port)
+{
+ if (!(asc_in(port, ASC_STA) & ASC_STA_RBF))
+ return NO_POLL_CHAR;
+
+ return asc_in(port, ASC_RXBUF);
+}
+
+static void asc_put_poll_char(struct uart_port *port, unsigned char c)
+{
+ while (!asc_txfifo_is_half_empty(port))
+ cpu_relax();
+ asc_out(port, ASC_TXBUF, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+/*---------------------------------------------------------------------*/
+
+static struct uart_ops asc_uart_ops = {
+ .tx_empty = asc_tx_empty,
+ .set_mctrl = asc_set_mctrl,
+ .get_mctrl = asc_get_mctrl,
+ .start_tx = asc_start_tx,
+ .stop_tx = asc_stop_tx,
+ .stop_rx = asc_stop_rx,
+ .enable_ms = asc_enable_ms,
+ .break_ctl = asc_break_ctl,
+ .startup = asc_startup,
+ .shutdown = asc_shutdown,
+ .set_termios = asc_set_termios,
+ .type = asc_type,
+ .release_port = asc_release_port,
+ .request_port = asc_request_port,
+ .config_port = asc_config_port,
+ .verify_port = asc_verify_port,
+ .pm = asc_pm,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = asc_get_poll_char,
+ .poll_put_char = asc_put_poll_char,
+#endif /* CONFIG_CONSOLE_POLL */
+};
+
+static int asc_init_port(struct asc_port *ascport,
+ struct platform_device *pdev)
+{
+ struct uart_port *port = &ascport->port;
+ struct resource *res;
+
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &asc_uart_ops;
+ port->fifosize = ASC_FIFO_SIZE;
+ port->dev = &pdev->dev;
+ port->irq = platform_get_irq(pdev, 0);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ port->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(port->membase))
+ return PTR_ERR(port->membase);
+ port->mapbase = res->start;
+
+ spin_lock_init(&port->lock);
+
+ ascport->clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (WARN_ON(IS_ERR(ascport->clk)))
+ return -EINVAL;
+ /* ensure that clk rate is correct by enabling the clk */
+ clk_prepare_enable(ascport->clk);
+ ascport->port.uartclk = clk_get_rate(ascport->clk);
+ WARN_ON(ascport->port.uartclk == 0);
+ clk_disable_unprepare(ascport->clk);
+
+ return 0;
+}
+
+static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int id;
+
+ if (!np)
+ return NULL;
+
+ id = of_alias_get_id(np, ASC_SERIAL_NAME);
+
+ if (id < 0)
+ id = 0;
+
+ if (WARN_ON(id >= ASC_MAX_PORTS))
+ return NULL;
+
+ asc_ports[id].hw_flow_control = of_property_read_bool(np,
+ "st,hw-flow-control");
+ asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1");
+ asc_ports[id].port.line = id;
+ return &asc_ports[id];
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id asc_match[] = {
+ { .compatible = "st,asc", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, asc_match);
+#endif
+
+static int asc_serial_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct asc_port *ascport;
+
+ ascport = asc_of_get_asc_port(pdev);
+ if (!ascport)
+ return -ENODEV;
+
+ ret = asc_init_port(ascport, pdev);
+ if (ret)
+ return ret;
+
+ ret = uart_add_one_port(&asc_uart_driver, &ascport->port);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, &ascport->port);
+
+ return 0;
+}
+
+static int asc_serial_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ return uart_remove_one_port(&asc_uart_driver, port);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int asc_serial_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ return uart_suspend_port(&asc_uart_driver, port);
+}
+
+static int asc_serial_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ return uart_resume_port(&asc_uart_driver, port);
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE
+static void asc_console_putchar(struct uart_port *port, int ch)
+{
+ unsigned int timeout = 1000000;
+
+ /* Wait for upto 1 second in case flow control is stopping us. */
+ while (--timeout && !asc_txfifo_is_half_empty(port))
+ udelay(1);
+
+ asc_out(port, ASC_TXBUF, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ */
+
+static void asc_console_write(struct console *co, const char *s, unsigned count)
+{
+ struct uart_port *port = &asc_ports[co->index].port;
+ unsigned long flags;
+ unsigned long timeout = 1000000;
+ int locked = 1;
+ u32 intenable;
+
+ local_irq_save(flags);
+ if (port->sysrq)
+ locked = 0; /* asc_interrupt has already claimed the lock */
+ else if (oops_in_progress)
+ locked = spin_trylock(&port->lock);
+ else
+ spin_lock(&port->lock);
+
+ /*
+ * Disable interrupts so we don't get the IRQ line bouncing
+ * up and down while interrupts are disabled.
+ */
+ intenable = asc_in(port, ASC_INTEN);
+ asc_out(port, ASC_INTEN, 0);
+ (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */
+
+ uart_console_write(port, s, count, asc_console_putchar);
+
+ while (--timeout && !asc_txfifo_is_empty(port))
+ udelay(1);
+
+ asc_out(port, ASC_INTEN, intenable);
+
+ if (locked)
+ spin_unlock(&port->lock);
+ local_irq_restore(flags);
+}
+
+static int asc_console_setup(struct console *co, char *options)
+{
+ struct asc_port *ascport;
+ int baud = 9600;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= ASC_MAX_PORTS)
+ return -ENODEV;
+
+ ascport = &asc_ports[co->index];
+
+ /*
+ * This driver does not support early console initialization
+ * (use ARM early printk support instead), so we only expect
+ * this to be called during the uart port registration when the
+ * driver gets probed and the port should be mapped at that point.
+ */
+ BUG_ON(ascport->port.mapbase == 0 || ascport->port.membase == NULL);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&ascport->port, co, baud, parity, bits, flow);
+}
+
+static struct console asc_console = {
+ .name = ASC_SERIAL_NAME,
+ .device = uart_console_device,
+ .write = asc_console_write,
+ .setup = asc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &asc_uart_driver,
+};
+
+#define ASC_SERIAL_CONSOLE (&asc_console)
+
+#else
+#define ASC_SERIAL_CONSOLE NULL
+#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */
+
+static struct uart_driver asc_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = ASC_SERIAL_NAME,
+ .major = 0,
+ .minor = 0,
+ .nr = ASC_MAX_PORTS,
+ .cons = ASC_SERIAL_CONSOLE,
+};
+
+static const struct dev_pm_ops asc_serial_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume)
+};
+
+static struct platform_driver asc_serial_driver = {
+ .probe = asc_serial_probe,
+ .remove = asc_serial_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &asc_serial_pm_ops,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(asc_match),
+ },
+};
+
+static int __init asc_init(void)
+{
+ int ret;
+ static char banner[] __initdata =
+ KERN_INFO "STMicroelectronics ASC driver initialized\n";
+
+ printk(banner);
+
+ ret = uart_register_driver(&asc_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&asc_serial_driver);
+ if (ret)
+ uart_unregister_driver(&asc_uart_driver);
+
+ return ret;
+}
+
+static void __exit asc_exit(void)
+{
+ platform_driver_unregister(&asc_serial_driver);
+ uart_unregister_driver(&asc_uart_driver);
+}
+
+module_init(asc_init);
+module_exit(asc_exit);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("STMicroelectronics (R&D) Limited");
+MODULE_DESCRIPTION("STMicroelectronics ASC serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index cf86e729532..dc697cee248 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -433,13 +433,10 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign
unsigned long flags;
int locked = 1;
- local_irq_save(flags);
- if (port->sysrq) {
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&port->lock);
- } else
- spin_lock(&port->lock);
+ if (port->sysrq || oops_in_progress)
+ locked = spin_trylock_irqsave(&port->lock, flags);
+ else
+ spin_lock_irqsave(&port->lock, flags);
while (n > 0) {
unsigned long ra = __pa(con_write_page);
@@ -470,8 +467,7 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign
}
if (locked)
- spin_unlock(&port->lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
}
static inline void sunhv_console_putchar(struct uart_port *port, char c)
@@ -492,7 +488,10 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig
unsigned long flags;
int i, locked = 1;
- local_irq_save(flags);
+ if (port->sysrq || oops_in_progress)
+ locked = spin_trylock_irqsave(&port->lock, flags);
+ else
+ spin_lock_irqsave(&port->lock, flags);
if (port->sysrq) {
locked = 0;
} else if (oops_in_progress) {
@@ -507,8 +506,7 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig
}
if (locked)
- spin_unlock(&port->lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
}
static struct console sunhv_console = {
diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
index 5d6136b2a04..2f57df9a71d 100644
--- a/drivers/tty/serial/sunsab.c
+++ b/drivers/tty/serial/sunsab.c
@@ -427,6 +427,9 @@ static void sunsab_start_tx(struct uart_port *port)
struct circ_buf *xmit = &up->port.state->xmit;
int i;
+ if (uart_circ_empty(xmit))
+ return;
+
up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);
writeb(up->interrupt_mask1, &up->regs->w.imr1);
@@ -719,7 +722,7 @@ static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cfla
if (iflag & INPCK)
up->port.read_status_mask |= (SAB82532_ISR0_PERR |
SAB82532_ISR0_FERR);
- if (iflag & (BRKINT | PARMRK))
+ if (iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8);
/*
@@ -844,20 +847,16 @@ static void sunsab_console_write(struct console *con, const char *s, unsigned n)
unsigned long flags;
int locked = 1;
- local_irq_save(flags);
- if (up->port.sysrq) {
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&up->port.lock);
- } else
- spin_lock(&up->port.lock);
+ if (up->port.sysrq || oops_in_progress)
+ locked = spin_trylock_irqsave(&up->port.lock, flags);
+ else
+ spin_lock_irqsave(&up->port.lock, flags);
uart_console_write(&up->port, s, n, sunsab_console_putchar);
sunsab_tec_wait(up);
if (locked)
- spin_unlock(&up->port.lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&up->port.lock, flags);
}
static int sunsab_console_setup(struct console *con, char *options)
@@ -894,7 +893,7 @@ static int sunsab_console_setup(struct console *con, char *options)
case B115200: baud = 115200; break;
case B230400: baud = 230400; break;
case B460800: baud = 460800; break;
- };
+ }
/*
* Temporary fix.
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index 699cc1b5f6a..5326ae195e5 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -522,7 +522,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break)
serio_interrupt(&up->serio, ch, 0);
#endif
break;
- };
+ }
}
} while (serial_in(up, UART_LSR) & UART_LSR_DR);
}
@@ -834,7 +834,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag,
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (iflag & INPCK)
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (iflag & (BRKINT | PARMRK))
+ if (iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= UART_LSR_BI;
/*
@@ -1295,13 +1295,10 @@ static void sunsu_console_write(struct console *co, const char *s,
unsigned int ier;
int locked = 1;
- local_irq_save(flags);
- if (up->port.sysrq) {
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&up->port.lock);
- } else
- spin_lock(&up->port.lock);
+ if (up->port.sysrq || oops_in_progress)
+ locked = spin_trylock_irqsave(&up->port.lock, flags);
+ else
+ spin_lock_irqsave(&up->port.lock, flags);
/*
* First save the UER then disable the interrupts
@@ -1319,8 +1316,7 @@ static void sunsu_console_write(struct console *co, const char *s,
serial_out(up, UART_IER, ier);
if (locked)
- spin_unlock(&up->port.lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&up->port.lock, flags);
}
/*
diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index 135a1520353..02df3940b95 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -319,7 +319,7 @@ static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up,
serio_interrupt(&up->serio, ch, 0);
#endif
break;
- };
+ }
}
}
@@ -703,6 +703,8 @@ static void sunzilog_start_tx(struct uart_port *port)
} else {
struct circ_buf *xmit = &port->state->xmit;
+ if (uart_circ_empty(xmit))
+ return;
writeb(xmit->buf[xmit->tail], &channel->data);
ZSDELAY();
ZS_WSYNC(channel);
@@ -897,7 +899,7 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag,
up->curregs[R5] |= Tx8;
up->parity_mask = 0xff;
break;
- };
+ }
up->curregs[R4] &= ~0x0c;
if (cflag & CSTOPB)
up->curregs[R4] |= SB2;
@@ -915,7 +917,7 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag,
up->port.read_status_mask = Rx_OVR;
if (iflag & INPCK)
up->port.read_status_mask |= CRC_ERR | PAR_ERR;
- if (iflag & (BRKINT | PARMRK))
+ if (iflag & (IGNBRK | BRKINT | PARMRK))
up->port.read_status_mask |= BRK_ABRT;
up->port.ignore_status_mask = 0;
@@ -1195,20 +1197,16 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count)
unsigned long flags;
int locked = 1;
- local_irq_save(flags);
- if (up->port.sysrq) {
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&up->port.lock);
- } else
- spin_lock(&up->port.lock);
+ if (up->port.sysrq || oops_in_progress)
+ locked = spin_trylock_irqsave(&up->port.lock, flags);
+ else
+ spin_lock_irqsave(&up->port.lock, flags);
uart_console_write(&up->port, s, count, sunzilog_putchar);
udelay(2);
if (locked)
- spin_unlock(&up->port.lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&up->port.lock, flags);
}
static int __init sunzilog_console_setup(struct console *con, char *options)
@@ -1239,7 +1237,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
default: case B9600: baud = 9600; break;
case B19200: baud = 19200; break;
case B38400: baud = 38400; break;
- };
+ }
brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c
new file mode 100644
index 00000000000..613ccf09dc2
--- /dev/null
+++ b/drivers/tty/serial/tilegx.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright 2013 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ *
+ * TILEGx UART driver.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <gxio/common.h>
+#include <gxio/iorpc_globals.h>
+#include <gxio/iorpc_uart.h>
+#include <gxio/kiorpc.h>
+
+#include <hv/drv_uart_intf.h>
+
+/*
+ * Use device name ttyS, major 4, minor 64-65.
+ * This is the usual serial port name, 8250 conventional range.
+ */
+#define TILEGX_UART_MAJOR TTY_MAJOR
+#define TILEGX_UART_MINOR 64
+#define TILEGX_UART_NAME "ttyS"
+#define DRIVER_NAME_STRING "TILEGx_Serial"
+#define TILEGX_UART_REF_CLK 125000000; /* REF_CLK is always 125 MHz. */
+
+struct tile_uart_port {
+ /* UART port. */
+ struct uart_port uart;
+
+ /* GXIO device context. */
+ gxio_uart_context_t context;
+
+ /* UART access mutex. */
+ struct mutex mutex;
+
+ /* CPU receiving interrupts. */
+ int irq_cpu;
+};
+
+static struct tile_uart_port tile_uart_ports[TILEGX_UART_NR];
+static struct uart_driver tilegx_uart_driver;
+
+
+/*
+ * Read UART rx fifo, and insert the chars into tty buffer.
+ */
+static void receive_chars(struct tile_uart_port *tile_uart,
+ struct tty_struct *tty)
+{
+ int i;
+ char c;
+ UART_FIFO_COUNT_t count;
+ gxio_uart_context_t *context = &tile_uart->context;
+ struct tty_port *port = tty->port;
+
+ count.word = gxio_uart_read(context, UART_FIFO_COUNT);
+ for (i = 0; i < count.rfifo_count; i++) {
+ c = (char)gxio_uart_read(context, UART_RECEIVE_DATA);
+ tty_insert_flip_char(port, c, TTY_NORMAL);
+ }
+}
+
+
+/*
+ * Drain the Rx FIFO, called by interrupt handler.
+ */
+static void handle_receive(struct tile_uart_port *tile_uart)
+{
+ struct tty_port *port = &tile_uart->uart.state->port;
+ struct tty_struct *tty = tty_port_tty_get(port);
+ gxio_uart_context_t *context = &tile_uart->context;
+
+ if (!tty)
+ return;
+
+ /* First read UART rx fifo. */
+ receive_chars(tile_uart, tty);
+
+ /* Reset RFIFO_WE interrupt. */
+ gxio_uart_write(context, UART_INTERRUPT_STATUS,
+ UART_INTERRUPT_MASK__RFIFO_WE_MASK);
+
+ /* Final read, if any chars comes between the first read and
+ * the interrupt reset.
+ */
+ receive_chars(tile_uart, tty);
+
+ spin_unlock(&tile_uart->uart.lock);
+ tty_flip_buffer_push(port);
+ spin_lock(&tile_uart->uart.lock);
+ tty_kref_put(tty);
+}
+
+
+/*
+ * Push one char to UART Write FIFO.
+ * Return 0 on success, -1 if write filo is full.
+ */
+static int tilegx_putchar(gxio_uart_context_t *context, char c)
+{
+ UART_FLAG_t flag;
+ flag.word = gxio_uart_read(context, UART_FLAG);
+ if (flag.wfifo_full)
+ return -1;
+
+ gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c);
+ return 0;
+}
+
+
+/*
+ * Send chars to UART Write FIFO; called by interrupt handler.
+ */
+static void handle_transmit(struct tile_uart_port *tile_uart)
+{
+ unsigned char ch;
+ struct uart_port *port;
+ struct circ_buf *xmit;
+ gxio_uart_context_t *context = &tile_uart->context;
+
+ /* First reset WFIFO_RE interrupt. */
+ gxio_uart_write(context, UART_INTERRUPT_STATUS,
+ UART_INTERRUPT_MASK__WFIFO_RE_MASK);
+
+ port = &tile_uart->uart;
+ xmit = &port->state->xmit;
+ if (port->x_char) {
+ if (tilegx_putchar(context, port->x_char))
+ return;
+ port->x_char = 0;
+ port->icount.tx++;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ return;
+
+ while (!uart_circ_empty(xmit)) {
+ ch = xmit->buf[xmit->tail];
+ if (tilegx_putchar(context, ch))
+ break;
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ /* Reset WFIFO_RE interrupt. */
+ gxio_uart_write(context, UART_INTERRUPT_STATUS,
+ UART_INTERRUPT_MASK__WFIFO_RE_MASK);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+}
+
+
+/*
+ * UART Interrupt handler.
+ */
+static irqreturn_t tilegx_interrupt(int irq, void *dev_id)
+{
+ unsigned long flags;
+ UART_INTERRUPT_STATUS_t intr_stat;
+ struct tile_uart_port *tile_uart;
+ gxio_uart_context_t *context;
+ struct uart_port *port = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ context = &tile_uart->context;
+ intr_stat.word = gxio_uart_read(context, UART_INTERRUPT_STATUS);
+
+ if (intr_stat.rfifo_we) {
+ handle_receive(tile_uart);
+ ret = IRQ_HANDLED;
+ }
+ if (intr_stat.wfifo_re) {
+ handle_transmit(tile_uart);
+ ret = IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+ return ret;
+}
+
+
+/*
+ * Return TIOCSER_TEMT when transmitter FIFO is empty.
+ */
+static u_int tilegx_tx_empty(struct uart_port *port)
+{
+ int ret;
+ UART_FLAG_t flag;
+ struct tile_uart_port *tile_uart;
+ gxio_uart_context_t *context;
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ if (!mutex_trylock(&tile_uart->mutex))
+ return 0;
+ context = &tile_uart->context;
+
+ flag.word = gxio_uart_read(context, UART_FLAG);
+ ret = (flag.wfifo_empty) ? TIOCSER_TEMT : 0;
+ mutex_unlock(&tile_uart->mutex);
+
+ return ret;
+}
+
+
+/*
+ * Set state of the modem control output lines.
+ */
+static void tilegx_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+ /* N/A */
+}
+
+
+/*
+ * Get state of the modem control input lines.
+ */
+static u_int tilegx_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+
+/*
+ * Stop transmitting.
+ */
+static void tilegx_stop_tx(struct uart_port *port)
+{
+ /* N/A */
+}
+
+
+/*
+ * Start transmitting.
+ */
+static void tilegx_start_tx(struct uart_port *port)
+{
+ unsigned char ch;
+ struct circ_buf *xmit;
+ struct tile_uart_port *tile_uart;
+ gxio_uart_context_t *context;
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ if (!mutex_trylock(&tile_uart->mutex))
+ return;
+ context = &tile_uart->context;
+ xmit = &port->state->xmit;
+ if (port->x_char) {
+ if (tilegx_putchar(context, port->x_char))
+ return;
+ port->x_char = 0;
+ port->icount.tx++;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ mutex_unlock(&tile_uart->mutex);
+ return;
+ }
+
+ while (!uart_circ_empty(xmit)) {
+ ch = xmit->buf[xmit->tail];
+ if (tilegx_putchar(context, ch))
+ break;
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Stop receiving - port is in process of being closed.
+ */
+static void tilegx_stop_rx(struct uart_port *port)
+{
+ int err;
+ struct tile_uart_port *tile_uart;
+ gxio_uart_context_t *context;
+ int cpu;
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ if (!mutex_trylock(&tile_uart->mutex))
+ return;
+
+ context = &tile_uart->context;
+ cpu = tile_uart->irq_cpu;
+ err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
+ KERNEL_PL, -1);
+ mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Enable modem status interrupts.
+ */
+static void tilegx_enable_ms(struct uart_port *port)
+{
+ /* N/A */
+}
+
+/*
+ * Control the transmission of a break signal.
+ */
+static void tilegx_break_ctl(struct uart_port *port, int break_state)
+{
+ /* N/A */
+}
+
+
+/*
+ * Perform initialization and enable port for reception.
+ */
+static int tilegx_startup(struct uart_port *port)
+{
+ struct tile_uart_port *tile_uart;
+ gxio_uart_context_t *context;
+ int ret = 0;
+ int cpu = raw_smp_processor_id(); /* pick an arbitrary cpu */
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ if (mutex_lock_interruptible(&tile_uart->mutex))
+ return -EBUSY;
+ context = &tile_uart->context;
+
+ /* Now open the hypervisor device if we haven't already. */
+ if (context->fd < 0) {
+ UART_INTERRUPT_MASK_t intr_mask;
+
+ /* Initialize UART device. */
+ ret = gxio_uart_init(context, port->line);
+ if (ret) {
+ ret = -ENXIO;
+ goto err;
+ }
+
+ /* Create our IRQs. */
+ port->irq = irq_alloc_hwirq(-1);
+ if (!port->irq)
+ goto err_uart_dest;
+ tile_irq_activate(port->irq, TILE_IRQ_PERCPU);
+
+ /* Register our IRQs. */
+ ret = request_irq(port->irq, tilegx_interrupt, 0,
+ tilegx_uart_driver.driver_name, port);
+ if (ret)
+ goto err_dest_irq;
+
+ /* Request that the hardware start sending us interrupts. */
+ tile_uart->irq_cpu = cpu;
+ ret = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
+ KERNEL_PL, port->irq);
+ if (ret)
+ goto err_free_irq;
+
+ /* Enable UART Tx/Rx Interrupt. */
+ intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK);
+ intr_mask.wfifo_re = 0;
+ intr_mask.rfifo_we = 0;
+ gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word);
+
+ /* Reset the Tx/Rx interrupt in case it's set. */
+ gxio_uart_write(context, UART_INTERRUPT_STATUS,
+ UART_INTERRUPT_MASK__WFIFO_RE_MASK |
+ UART_INTERRUPT_MASK__RFIFO_WE_MASK);
+ }
+
+ mutex_unlock(&tile_uart->mutex);
+ return ret;
+
+err_free_irq:
+ free_irq(port->irq, port);
+err_dest_irq:
+ irq_free_hwirq(port->irq);
+err_uart_dest:
+ gxio_uart_destroy(context);
+ ret = -ENXIO;
+err:
+ mutex_unlock(&tile_uart->mutex);
+ return ret;
+}
+
+
+/*
+ * Release kernel resources if it is the last close, disable the port,
+ * free IRQ and close the port.
+ */
+static void tilegx_shutdown(struct uart_port *port)
+{
+ int err;
+ UART_INTERRUPT_MASK_t intr_mask;
+ struct tile_uart_port *tile_uart;
+ gxio_uart_context_t *context;
+ int cpu;
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ if (mutex_lock_interruptible(&tile_uart->mutex))
+ return;
+ context = &tile_uart->context;
+
+ /* Disable UART Tx/Rx Interrupt. */
+ intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK);
+ intr_mask.wfifo_re = 1;
+ intr_mask.rfifo_we = 1;
+ gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word);
+
+ /* Request that the hardware stop sending us interrupts. */
+ cpu = tile_uart->irq_cpu;
+ err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
+ KERNEL_PL, -1);
+
+ if (port->irq > 0) {
+ free_irq(port->irq, port);
+ irq_free_hwirq(port->irq);
+ port->irq = 0;
+ }
+
+ gxio_uart_destroy(context);
+
+ mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Flush the buffer.
+ */
+static void tilegx_flush_buffer(struct uart_port *port)
+{
+ /* N/A */
+}
+
+
+/*
+ * Change the port parameters.
+ */
+static void tilegx_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ int err;
+ UART_DIVISOR_t divisor;
+ UART_TYPE_t type;
+ unsigned int baud;
+ struct tile_uart_port *tile_uart;
+ gxio_uart_context_t *context;
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ if (!mutex_trylock(&tile_uart->mutex))
+ return;
+ context = &tile_uart->context;
+
+ /* Open the hypervisor device if we haven't already. */
+ if (context->fd < 0) {
+ err = gxio_uart_init(context, port->line);
+ if (err) {
+ mutex_unlock(&tile_uart->mutex);
+ return;
+ }
+ }
+
+ divisor.word = gxio_uart_read(context, UART_DIVISOR);
+ type.word = gxio_uart_read(context, UART_TYPE);
+
+ /* Divisor. */
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+ divisor.divisor = uart_get_divisor(port, baud);
+
+ /* Byte size. */
+ if ((termios->c_cflag & CSIZE) == CS7)
+ type.dbits = UART_TYPE__DBITS_VAL_SEVEN_DBITS;
+ else
+ type.dbits = UART_TYPE__DBITS_VAL_EIGHT_DBITS;
+
+ /* Parity. */
+ if (termios->c_cflag & PARENB) {
+ /* Mark or Space parity. */
+ if (termios->c_cflag & CMSPAR)
+ if (termios->c_cflag & PARODD)
+ type.ptype = UART_TYPE__PTYPE_VAL_MARK;
+ else
+ type.ptype = UART_TYPE__PTYPE_VAL_SPACE;
+ else if (termios->c_cflag & PARODD)
+ type.ptype = UART_TYPE__PTYPE_VAL_ODD;
+ else
+ type.ptype = UART_TYPE__PTYPE_VAL_EVEN;
+ } else
+ type.ptype = UART_TYPE__PTYPE_VAL_NONE;
+
+ /* Stop bits. */
+ if (termios->c_cflag & CSTOPB)
+ type.sbits = UART_TYPE__SBITS_VAL_TWO_SBITS;
+ else
+ type.sbits = UART_TYPE__SBITS_VAL_ONE_SBITS;
+
+ /* Set the uart paramters. */
+ gxio_uart_write(context, UART_DIVISOR, divisor.word);
+ gxio_uart_write(context, UART_TYPE, type.word);
+
+ mutex_unlock(&tile_uart->mutex);
+}
+
+
+/*
+ * Return string describing the specified port.
+ */
+static const char *tilegx_type(struct uart_port *port)
+{
+ return port->type == PORT_TILEGX ? DRIVER_NAME_STRING : NULL;
+}
+
+
+/*
+ * Release the resources being used by 'port'.
+ */
+static void tilegx_release_port(struct uart_port *port)
+{
+ /* Nothing to release. */
+}
+
+
+/*
+ * Request the resources being used by 'port'.
+ */
+static int tilegx_request_port(struct uart_port *port)
+{
+ /* Always present. */
+ return 0;
+}
+
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void tilegx_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_TILEGX;
+}
+
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int tilegx_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_TILEGX))
+ return -EINVAL;
+
+ return 0;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int tilegx_poll_get_char(struct uart_port *port)
+{
+ UART_FIFO_COUNT_t count;
+ gxio_uart_context_t *context;
+ struct tile_uart_port *tile_uart;
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ context = &tile_uart->context;
+ count.word = gxio_uart_read(context, UART_FIFO_COUNT);
+ if (count.rfifo_count == 0)
+ return NO_POLL_CHAR;
+ return (char)gxio_uart_read(context, UART_RECEIVE_DATA);
+}
+
+static void tilegx_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ gxio_uart_context_t *context;
+ struct tile_uart_port *tile_uart;
+
+ tile_uart = container_of(port, struct tile_uart_port, uart);
+ context = &tile_uart->context;
+ gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+
+static const struct uart_ops tilegx_ops = {
+ .tx_empty = tilegx_tx_empty,
+ .set_mctrl = tilegx_set_mctrl,
+ .get_mctrl = tilegx_get_mctrl,
+ .stop_tx = tilegx_stop_tx,
+ .start_tx = tilegx_start_tx,
+ .stop_rx = tilegx_stop_rx,
+ .enable_ms = tilegx_enable_ms,
+ .break_ctl = tilegx_break_ctl,
+ .startup = tilegx_startup,
+ .shutdown = tilegx_shutdown,
+ .flush_buffer = tilegx_flush_buffer,
+ .set_termios = tilegx_set_termios,
+ .type = tilegx_type,
+ .release_port = tilegx_release_port,
+ .request_port = tilegx_request_port,
+ .config_port = tilegx_config_port,
+ .verify_port = tilegx_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = tilegx_poll_get_char,
+ .poll_put_char = tilegx_poll_put_char,
+#endif
+};
+
+
+static void tilegx_init_ports(void)
+{
+ int i;
+ struct uart_port *port;
+
+ for (i = 0; i < TILEGX_UART_NR; i++) {
+ port = &tile_uart_ports[i].uart;
+ port->ops = &tilegx_ops;
+ port->line = i;
+ port->type = PORT_TILEGX;
+ port->uartclk = TILEGX_UART_REF_CLK;
+ port->flags = UPF_BOOT_AUTOCONF;
+
+ tile_uart_ports[i].context.fd = -1;
+ mutex_init(&tile_uart_ports[i].mutex);
+ }
+}
+
+
+static struct uart_driver tilegx_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME_STRING,
+ .dev_name = TILEGX_UART_NAME,
+ .major = TILEGX_UART_MAJOR,
+ .minor = TILEGX_UART_MINOR,
+ .nr = TILEGX_UART_NR,
+};
+
+
+static int __init tilegx_init(void)
+{
+ int i;
+ int ret;
+ struct tty_driver *tty_drv;
+
+ ret = uart_register_driver(&tilegx_uart_driver);
+ if (ret)
+ return ret;
+ tty_drv = tilegx_uart_driver.tty_driver;
+ tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+ tty_drv->init_termios.c_ispeed = 115200;
+ tty_drv->init_termios.c_ospeed = 115200;
+
+ tilegx_init_ports();
+
+ for (i = 0; i < TILEGX_UART_NR; i++) {
+ struct uart_port *port = &tile_uart_ports[i].uart;
+ ret = uart_add_one_port(&tilegx_uart_driver, port);
+ }
+
+ return 0;
+}
+
+
+static void __exit tilegx_exit(void)
+{
+ int i;
+ struct uart_port *port;
+
+ for (i = 0; i < TILEGX_UART_NR; i++) {
+ port = &tile_uart_ports[i].uart;
+ uart_remove_one_port(&tilegx_uart_driver, port);
+ }
+
+ uart_unregister_driver(&tilegx_uart_driver);
+}
+
+
+module_init(tilegx_init);
+module_exit(tilegx_exit);
+
+MODULE_AUTHOR("Tilera Corporation");
+MODULE_DESCRIPTION("TILEGx serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c
index 6818410a2be..f87097acd8a 100644
--- a/drivers/tty/serial/timbuart.c
+++ b/drivers/tty/serial/timbuart.c
@@ -162,7 +162,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
dev_dbg(port->dev, "%s - leaving\n", __func__);
}
-void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier)
+static void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier)
{
if (isr & RXFLAGS) {
/* Some RX status is set */
@@ -184,7 +184,7 @@ void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier)
dev_dbg(port->dev, "%s - leaving\n", __func__);
}
-void timbuart_tasklet(unsigned long arg)
+static void timbuart_tasklet(unsigned long arg)
{
struct timbuart_port *uart = (struct timbuart_port *)arg;
u32 isr, ier = 0;
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 5f90ef24d47..dce27f34937 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -418,14 +418,23 @@ static struct uart_ops ulite_ops = {
#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
static void ulite_console_wait_tx(struct uart_port *port)
{
- int i;
u8 val;
-
- /* Spin waiting for TX fifo to have space available */
- for (i = 0; i < 100000; i++) {
+ unsigned long timeout;
+
+ /*
+ * Spin waiting for TX fifo to have space available.
+ * When using the Microblaze Debug Module this can take up to 1s
+ */
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (1) {
val = uart_in32(ULITE_STATUS, port);
if ((val & ULITE_STATUS_TXFULL) == 0)
break;
+ if (time_after(jiffies, timeout)) {
+ dev_warn(port->dev,
+ "timeout waiting for TX buffer empty\n");
+ break;
+ }
cpu_relax();
}
}
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 88317482b81..1c52074c38d 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -25,6 +25,8 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/dma-mapping.h>
@@ -269,7 +271,7 @@ static unsigned int qe_uart_tx_empty(struct uart_port *port)
return 1;
bdp++;
- };
+ }
}
/*
@@ -934,7 +936,7 @@ static void qe_uart_set_termios(struct uart_port *port,
port->read_status_mask = BD_SC_EMPTY | BD_SC_OV;
if (termios->c_iflag & INPCK)
port->read_status_mask |= BD_SC_FR | BD_SC_PR;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= BD_SC_BR;
/*
diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c
index f655997f44a..db0c8a4ab03 100644
--- a/drivers/tty/serial/vr41xx_siu.c
+++ b/drivers/tty/serial/vr41xx_siu.c
@@ -559,7 +559,7 @@ static void siu_set_termios(struct uart_port *port, struct ktermios *new,
port->read_status_mask = UART_LSR_THRE | UART_LSR_OE | UART_LSR_DR;
if (c_iflag & INPCK)
port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (c_iflag & (BRKINT | PARMRK))
+ if (c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= UART_LSR_BI;
port->ignore_status_mask = 0;
@@ -705,7 +705,7 @@ static int siu_init_ports(struct platform_device *pdev)
{
struct uart_port *port;
struct resource *res;
- int *type = pdev->dev.platform_data;
+ int *type = dev_get_platdata(&pdev->dev);
int i;
if (!type)
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 48af43de346..15ad6fcda88 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -170,7 +170,9 @@ static void handle_rx(struct uart_port *port)
tty_insert_flip_char(tport, c, flag);
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(tport);
+ spin_lock(&port->lock);
}
static void handle_tx(struct uart_port *port)
@@ -559,12 +561,13 @@ static int vt8500_serial_probe(struct platform_device *pdev)
if (!mmres || !irqres)
return -ENODEV;
- if (np)
+ if (np) {
port = of_alias_get_id(np, "serial");
if (port >= VT8500_MAX_PORTS)
port = -1;
- else
+ } else {
port = -1;
+ }
if (port < 0) {
/* calculate the port id */
@@ -630,7 +633,6 @@ static int vt8500_serial_remove(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
clk_disable_unprepare(vt8500_port->clk);
uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 7e4150aa69c..8809775e2ba 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1,7 +1,7 @@
/*
- * Xilinx PS UART driver
+ * Cadence UART driver (found in Xilinx Zynq)
*
- * 2011 (c) Xilinx Inc.
+ * 2011 - 2014 (C) Xilinx Inc.
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
@@ -9,103 +9,105 @@
* either version 2 of the License, or (at your option) any
* later version.
*
+ * This driver has originally been pushed by Xilinx using a Zynq-branding. This
+ * still shows in the naming of this file, the kconfig symbols and some symbols
+ * in the code.
*/
+#if defined(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
#include <linux/platform_device.h>
#include <linux/serial.h>
+#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
-#include <linux/console.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
-#define XUARTPS_TTY_NAME "ttyPS"
-#define XUARTPS_NAME "xuartps"
-#define XUARTPS_MAJOR 0 /* use dynamic node allocation */
-#define XUARTPS_MINOR 0 /* works best with devtmpfs */
-#define XUARTPS_NR_PORTS 2
-#define XUARTPS_FIFO_SIZE 16 /* FIFO size */
-#define XUARTPS_REGISTER_SPACE 0xFFF
+#define CDNS_UART_TTY_NAME "ttyPS"
+#define CDNS_UART_NAME "xuartps"
+#define CDNS_UART_MAJOR 0 /* use dynamic node allocation */
+#define CDNS_UART_MINOR 0 /* works best with devtmpfs */
+#define CDNS_UART_NR_PORTS 2
+#define CDNS_UART_FIFO_SIZE 64 /* FIFO size */
+#define CDNS_UART_REGISTER_SPACE 0xFFF
+
+#define cdns_uart_readl(offset) ioread32(port->membase + offset)
+#define cdns_uart_writel(val, offset) iowrite32(val, port->membase + offset)
+
+/* Rx Trigger level */
+static int rx_trigger_level = 56;
+module_param(rx_trigger_level, uint, S_IRUGO);
+MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes");
+
+/* Rx Timeout */
+static int rx_timeout = 10;
+module_param(rx_timeout, uint, S_IRUGO);
+MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
+
+/* Register offsets for the UART. */
+#define CDNS_UART_CR_OFFSET 0x00 /* Control Register */
+#define CDNS_UART_MR_OFFSET 0x04 /* Mode Register */
+#define CDNS_UART_IER_OFFSET 0x08 /* Interrupt Enable */
+#define CDNS_UART_IDR_OFFSET 0x0C /* Interrupt Disable */
+#define CDNS_UART_IMR_OFFSET 0x10 /* Interrupt Mask */
+#define CDNS_UART_ISR_OFFSET 0x14 /* Interrupt Status */
+#define CDNS_UART_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator */
+#define CDNS_UART_RXTOUT_OFFSET 0x1C /* RX Timeout */
+#define CDNS_UART_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level */
+#define CDNS_UART_MODEMCR_OFFSET 0x24 /* Modem Control */
+#define CDNS_UART_MODEMSR_OFFSET 0x28 /* Modem Status */
+#define CDNS_UART_SR_OFFSET 0x2C /* Channel Status */
+#define CDNS_UART_FIFO_OFFSET 0x30 /* FIFO */
+#define CDNS_UART_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider */
+#define CDNS_UART_FLOWDEL_OFFSET 0x38 /* Flow Delay */
+#define CDNS_UART_IRRX_PWIDTH_OFFSET 0x3C /* IR Min Received Pulse Width */
+#define CDNS_UART_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse Width */
+#define CDNS_UART_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level */
+
+/* Control Register Bit Definitions */
+#define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */
+#define CDNS_UART_CR_STARTBRK 0x00000080 /* Set TX break */
+#define CDNS_UART_CR_TX_DIS 0x00000020 /* TX disabled. */
+#define CDNS_UART_CR_TX_EN 0x00000010 /* TX enabled */
+#define CDNS_UART_CR_RX_DIS 0x00000008 /* RX disabled. */
+#define CDNS_UART_CR_RX_EN 0x00000004 /* RX enabled */
+#define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */
+#define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */
+#define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */
-#define xuartps_readl(offset) ioread32(port->membase + offset)
-#define xuartps_writel(val, offset) iowrite32(val, port->membase + offset)
-
-/********************************Register Map********************************/
-/** UART
- *
- * Register offsets for the UART.
- *
- */
-#define XUARTPS_CR_OFFSET 0x00 /* Control Register [8:0] */
-#define XUARTPS_MR_OFFSET 0x04 /* Mode Register [10:0] */
-#define XUARTPS_IER_OFFSET 0x08 /* Interrupt Enable [10:0] */
-#define XUARTPS_IDR_OFFSET 0x0C /* Interrupt Disable [10:0] */
-#define XUARTPS_IMR_OFFSET 0x10 /* Interrupt Mask [10:0] */
-#define XUARTPS_ISR_OFFSET 0x14 /* Interrupt Status [10:0]*/
-#define XUARTPS_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator [15:0] */
-#define XUARTPS_RXTOUT_OFFSET 0x1C /* RX Timeout [7:0] */
-#define XUARTPS_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level [5:0] */
-#define XUARTPS_MODEMCR_OFFSET 0x24 /* Modem Control [5:0] */
-#define XUARTPS_MODEMSR_OFFSET 0x28 /* Modem Status [8:0] */
-#define XUARTPS_SR_OFFSET 0x2C /* Channel Status [11:0] */
-#define XUARTPS_FIFO_OFFSET 0x30 /* FIFO [15:0] or [7:0] */
-#define XUARTPS_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider [7:0] */
-#define XUARTPS_FLOWDEL_OFFSET 0x38 /* Flow Delay [15:0] */
-#define XUARTPS_IRRX_PWIDTH_OFFSET 0x3C /* IR Minimum Received Pulse
- Width [15:0] */
-#define XUARTPS_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse
- Width [7:0] */
-#define XUARTPS_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level [5:0] */
-
-/** Control Register
- *
- * The Control register (CR) controls the major functions of the device.
- *
- * Control Register Bit Definitions
- */
-#define XUARTPS_CR_STOPBRK 0x00000100 /* Stop TX break */
-#define XUARTPS_CR_STARTBRK 0x00000080 /* Set TX break */
-#define XUARTPS_CR_TX_DIS 0x00000020 /* TX disabled. */
-#define XUARTPS_CR_TX_EN 0x00000010 /* TX enabled */
-#define XUARTPS_CR_RX_DIS 0x00000008 /* RX disabled. */
-#define XUARTPS_CR_RX_EN 0x00000004 /* RX enabled */
-#define XUARTPS_CR_TXRST 0x00000002 /* TX logic reset */
-#define XUARTPS_CR_RXRST 0x00000001 /* RX logic reset */
-#define XUARTPS_CR_RST_TO 0x00000040 /* Restart Timeout Counter */
-
-/** Mode Register
- *
+/*
+ * Mode Register:
* The mode register (MR) defines the mode of transfer as well as the data
* format. If this register is modified during transmission or reception,
* data validity cannot be guaranteed.
- *
- * Mode Register Bit Definitions
- *
*/
-#define XUARTPS_MR_CLKSEL 0x00000001 /* Pre-scalar selection */
-#define XUARTPS_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */
-#define XUARTPS_MR_CHMODE_NORM 0x00000000 /* Normal mode */
+#define CDNS_UART_MR_CLKSEL 0x00000001 /* Pre-scalar selection */
+#define CDNS_UART_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */
+#define CDNS_UART_MR_CHMODE_NORM 0x00000000 /* Normal mode */
-#define XUARTPS_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */
-#define XUARTPS_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */
+#define CDNS_UART_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */
+#define CDNS_UART_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */
-#define XUARTPS_MR_PARITY_NONE 0x00000020 /* No parity mode */
-#define XUARTPS_MR_PARITY_MARK 0x00000018 /* Mark parity mode */
-#define XUARTPS_MR_PARITY_SPACE 0x00000010 /* Space parity mode */
-#define XUARTPS_MR_PARITY_ODD 0x00000008 /* Odd parity mode */
-#define XUARTPS_MR_PARITY_EVEN 0x00000000 /* Even parity mode */
+#define CDNS_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */
+#define CDNS_UART_MR_PARITY_MARK 0x00000018 /* Mark parity mode */
+#define CDNS_UART_MR_PARITY_SPACE 0x00000010 /* Space parity mode */
+#define CDNS_UART_MR_PARITY_ODD 0x00000008 /* Odd parity mode */
+#define CDNS_UART_MR_PARITY_EVEN 0x00000000 /* Even parity mode */
-#define XUARTPS_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */
-#define XUARTPS_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */
-#define XUARTPS_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */
+#define CDNS_UART_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */
+#define CDNS_UART_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */
+#define CDNS_UART_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */
-/** Interrupt Registers
- *
+/*
+ * Interrupt Registers:
* Interrupt control logic uses the interrupt enable register (IER) and the
* interrupt disable register (IDR) to set the value of the bits in the
* interrupt mask register (IMR). The IMR determines whether to pass an
@@ -113,50 +115,65 @@
* Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an
* interrupt. IMR and ISR are read only, and IER and IDR are write only.
* Reading either IER or IDR returns 0x00.
- *
* All four registers have the same bit definitions.
*/
-#define XUARTPS_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */
-#define XUARTPS_IXR_PARITY 0x00000080 /* Parity error interrupt */
-#define XUARTPS_IXR_FRAMING 0x00000040 /* Framing error interrupt */
-#define XUARTPS_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */
-#define XUARTPS_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */
-#define XUARTPS_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */
-#define XUARTPS_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */
-#define XUARTPS_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */
-#define XUARTPS_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */
-#define XUARTPS_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */
-#define XUARTPS_IXR_MASK 0x00001FFF /* Valid bit mask */
-
-/** Channel Status Register
- *
+#define CDNS_UART_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */
+#define CDNS_UART_IXR_PARITY 0x00000080 /* Parity error interrupt */
+#define CDNS_UART_IXR_FRAMING 0x00000040 /* Framing error interrupt */
+#define CDNS_UART_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */
+#define CDNS_UART_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */
+#define CDNS_UART_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */
+#define CDNS_UART_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */
+#define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */
+#define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */
+#define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */
+#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */
+
+/* Goes in read_status_mask for break detection as the HW doesn't do it*/
+#define CDNS_UART_IXR_BRK 0x80000000
+
+/*
+ * Channel Status Register:
* The channel status register (CSR) is provided to enable the control logic
* to monitor the status of bits in the channel interrupt status register,
* even if these are masked out by the interrupt mask register.
*/
-#define XUARTPS_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
-#define XUARTPS_SR_TXEMPTY 0x00000008 /* TX FIFO empty */
-#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */
-#define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */
+#define CDNS_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
+#define CDNS_UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */
+#define CDNS_UART_SR_TXFULL 0x00000010 /* TX FIFO full */
+#define CDNS_UART_SR_RXTRIG 0x00000001 /* Rx Trigger */
+
+/* baud dividers min/max values */
+#define CDNS_UART_BDIV_MIN 4
+#define CDNS_UART_BDIV_MAX 255
+#define CDNS_UART_CD_MAX 65535
/**
- * struct xuartps - device data
- * @refclk Reference clock
- * @aperclk APB clock
+ * struct cdns_uart - device data
+ * @port: Pointer to the UART port
+ * @uartclk: Reference clock
+ * @pclk: APB clock
+ * @baud: Current baud rate
+ * @clk_rate_change_nb: Notifier block for clock changes
*/
-struct xuartps {
- struct clk *refclk;
- struct clk *aperclk;
+struct cdns_uart {
+ struct uart_port *port;
+ struct clk *uartclk;
+ struct clk *pclk;
+ unsigned int baud;
+ struct notifier_block clk_rate_change_nb;
};
+#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \
+ clk_rate_change_nb);
/**
- * xuartps_isr - Interrupt handler
+ * cdns_uart_isr - Interrupt handler
* @irq: Irq number
* @dev_id: Id of the port
*
- * Returns IRQHANDLED
- **/
-static irqreturn_t xuartps_isr(int irq, void *dev_id)
+ * Return: IRQHANDLED
+ */
+static irqreturn_t cdns_uart_isr(int irq, void *dev_id)
{
struct uart_port *port = (struct uart_port *)dev_id;
unsigned long flags;
@@ -169,33 +186,76 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
/* Read the interrupt status register to determine which
* interrupt(s) is/are active.
*/
- isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET);
+ isrstatus = cdns_uart_readl(CDNS_UART_ISR_OFFSET);
+
+ /*
+ * There is no hardware break detection, so we interpret framing
+ * error with all-zeros data as a break sequence. Most of the time,
+ * there's another non-zero byte at the end of the sequence.
+ */
+ if (isrstatus & CDNS_UART_IXR_FRAMING) {
+ while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) &
+ CDNS_UART_SR_RXEMPTY)) {
+ if (!cdns_uart_readl(CDNS_UART_FIFO_OFFSET)) {
+ port->read_status_mask |= CDNS_UART_IXR_BRK;
+ isrstatus &= ~CDNS_UART_IXR_FRAMING;
+ }
+ }
+ cdns_uart_writel(CDNS_UART_IXR_FRAMING, CDNS_UART_ISR_OFFSET);
+ }
/* drop byte with parity error if IGNPAR specified */
- if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY)
- isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT);
+ if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY)
+ isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT);
isrstatus &= port->read_status_mask;
isrstatus &= ~port->ignore_status_mask;
- if ((isrstatus & XUARTPS_IXR_TOUT) ||
- (isrstatus & XUARTPS_IXR_RXTRIG)) {
+ if ((isrstatus & CDNS_UART_IXR_TOUT) ||
+ (isrstatus & CDNS_UART_IXR_RXTRIG)) {
/* Receive Timeout Interrupt */
- while ((xuartps_readl(XUARTPS_SR_OFFSET) &
- XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) {
- data = xuartps_readl(XUARTPS_FIFO_OFFSET);
+ while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) &
+ CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {
+ data = cdns_uart_readl(CDNS_UART_FIFO_OFFSET);
+
+ /* Non-NULL byte after BREAK is garbage (99%) */
+ if (data && (port->read_status_mask &
+ CDNS_UART_IXR_BRK)) {
+ port->read_status_mask &= ~CDNS_UART_IXR_BRK;
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ }
+
+#ifdef SUPPORT_SYSRQ
+ /*
+ * uart_handle_sysrq_char() doesn't work if
+ * spinlocked, for some reason
+ */
+ if (port->sysrq) {
+ spin_unlock(&port->lock);
+ if (uart_handle_sysrq_char(port,
+ (unsigned char)data)) {
+ spin_lock(&port->lock);
+ continue;
+ }
+ spin_lock(&port->lock);
+ }
+#endif
+
port->icount.rx++;
- if (isrstatus & XUARTPS_IXR_PARITY) {
+ if (isrstatus & CDNS_UART_IXR_PARITY) {
port->icount.parity++;
status = TTY_PARITY;
- } else if (isrstatus & XUARTPS_IXR_FRAMING) {
+ } else if (isrstatus & CDNS_UART_IXR_FRAMING) {
port->icount.frame++;
status = TTY_FRAME;
- } else if (isrstatus & XUARTPS_IXR_OVERRUN)
+ } else if (isrstatus & CDNS_UART_IXR_OVERRUN) {
port->icount.overrun++;
+ }
- uart_insert_char(port, isrstatus, XUARTPS_IXR_OVERRUN,
+ uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN,
data, status);
}
spin_unlock(&port->lock);
@@ -204,10 +264,10 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
}
/* Dispatch an appropriate handler */
- if ((isrstatus & XUARTPS_IXR_TXEMPTY) == XUARTPS_IXR_TXEMPTY) {
+ if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) {
if (uart_circ_empty(&port->state->xmit)) {
- xuartps_writel(XUARTPS_IXR_TXEMPTY,
- XUARTPS_IDR_OFFSET);
+ cdns_uart_writel(CDNS_UART_IXR_TXEMPTY,
+ CDNS_UART_IDR_OFFSET);
} else {
numbytes = port->fifosize;
/* Break if no more data available in the UART buffer */
@@ -215,12 +275,12 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
if (uart_circ_empty(&port->state->xmit))
break;
/* Get the data from the UART circular buffer
- * and write it to the xuartps's TX_FIFO
+ * and write it to the cdns_uart's TX_FIFO
* register.
*/
- xuartps_writel(
+ cdns_uart_writel(
port->state->xmit.buf[port->state->xmit.
- tail], XUARTPS_FIFO_OFFSET);
+ tail], CDNS_UART_FIFO_OFFSET);
port->icount.tx++;
@@ -228,7 +288,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
* the buffer if it reaches limit.
*/
port->state->xmit.tail =
- (port->state->xmit.tail + 1) & \
+ (port->state->xmit.tail + 1) &
(UART_XMIT_SIZE - 1);
}
@@ -238,7 +298,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
}
}
- xuartps_writel(isrstatus, XUARTPS_ISR_OFFSET);
+ cdns_uart_writel(isrstatus, CDNS_UART_ISR_OFFSET);
/* be sure to release the lock and tty before leaving */
spin_unlock_irqrestore(&port->lock, flags);
@@ -247,97 +307,226 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
}
/**
- * xuartps_set_baud_rate - Calculate and set the baud rate
- * @port: Handle to the uart port structure
- * @baud: Baud rate to set
+ * cdns_uart_calc_baud_divs - Calculate baud rate divisors
+ * @clk: UART module input clock
+ * @baud: Desired baud rate
+ * @rbdiv: BDIV value (return value)
+ * @rcd: CD value (return value)
+ * @div8: Value for clk_sel bit in mod (return value)
+ * Return: baud rate, requested baud when possible, or actual baud when there
+ * was too much error, zero if no valid divisors are found.
*
- * Returns baud rate, requested baud when possible, or actual baud when there
- * was too much error
- **/
-static unsigned int xuartps_set_baud_rate(struct uart_port *port,
- unsigned int baud)
+ * Formula to obtain baud rate is
+ * baud_tx/rx rate = clk/CD * (BDIV + 1)
+ * input_clk = (Uart User Defined Clock or Apb Clock)
+ * depends on UCLKEN in MR Reg
+ * clk = input_clk or input_clk/8;
+ * depends on CLKS in MR reg
+ * CD and BDIV depends on values in
+ * baud rate generate register
+ * baud rate clock divisor register
+ */
+static unsigned int cdns_uart_calc_baud_divs(unsigned int clk,
+ unsigned int baud, u32 *rbdiv, u32 *rcd, int *div8)
{
- unsigned int sel_clk;
- unsigned int calc_baud = 0;
- unsigned int brgr_val, brdiv_val;
+ u32 cd, bdiv;
+ unsigned int calc_baud;
+ unsigned int bestbaud = 0;
unsigned int bauderror;
+ unsigned int besterror = ~0;
- /* Formula to obtain baud rate is
- * baud_tx/rx rate = sel_clk/CD * (BDIV + 1)
- * input_clk = (Uart User Defined Clock or Apb Clock)
- * depends on UCLKEN in MR Reg
- * sel_clk = input_clk or input_clk/8;
- * depends on CLKS in MR reg
- * CD and BDIV depends on values in
- * baud rate generate register
- * baud rate clock divisor register
- */
- sel_clk = port->uartclk;
- if (xuartps_readl(XUARTPS_MR_OFFSET) & XUARTPS_MR_CLKSEL)
- sel_clk = sel_clk / 8;
-
- /* Find the best values for baud generation */
- for (brdiv_val = 4; brdiv_val < 255; brdiv_val++) {
+ if (baud < clk / ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX)) {
+ *div8 = 1;
+ clk /= 8;
+ } else {
+ *div8 = 0;
+ }
- brgr_val = sel_clk / (baud * (brdiv_val + 1));
- if (brgr_val < 2 || brgr_val > 65535)
+ for (bdiv = CDNS_UART_BDIV_MIN; bdiv <= CDNS_UART_BDIV_MAX; bdiv++) {
+ cd = DIV_ROUND_CLOSEST(clk, baud * (bdiv + 1));
+ if (cd < 1 || cd > CDNS_UART_CD_MAX)
continue;
- calc_baud = sel_clk / (brgr_val * (brdiv_val + 1));
+ calc_baud = clk / (cd * (bdiv + 1));
if (baud > calc_baud)
bauderror = baud - calc_baud;
else
bauderror = calc_baud - baud;
- /* use the values when percent error is acceptable */
- if (((bauderror * 100) / baud) < 3) {
- calc_baud = baud;
- break;
+ if (besterror > bauderror) {
+ *rbdiv = bdiv;
+ *rcd = cd;
+ bestbaud = calc_baud;
+ besterror = bauderror;
}
}
+ /* use the values when percent error is acceptable */
+ if (((besterror * 100) / baud) < 3)
+ bestbaud = baud;
- /* Set the values for the new baud rate */
- xuartps_writel(brgr_val, XUARTPS_BAUDGEN_OFFSET);
- xuartps_writel(brdiv_val, XUARTPS_BAUDDIV_OFFSET);
+ return bestbaud;
+}
+
+/**
+ * cdns_uart_set_baud_rate - Calculate and set the baud rate
+ * @port: Handle to the uart port structure
+ * @baud: Baud rate to set
+ * Return: baud rate, requested baud when possible, or actual baud when there
+ * was too much error, zero if no valid divisors are found.
+ */
+static unsigned int cdns_uart_set_baud_rate(struct uart_port *port,
+ unsigned int baud)
+{
+ unsigned int calc_baud;
+ u32 cd = 0, bdiv = 0;
+ u32 mreg;
+ int div8;
+ struct cdns_uart *cdns_uart = port->private_data;
+
+ calc_baud = cdns_uart_calc_baud_divs(port->uartclk, baud, &bdiv, &cd,
+ &div8);
+
+ /* Write new divisors to hardware */
+ mreg = cdns_uart_readl(CDNS_UART_MR_OFFSET);
+ if (div8)
+ mreg |= CDNS_UART_MR_CLKSEL;
+ else
+ mreg &= ~CDNS_UART_MR_CLKSEL;
+ cdns_uart_writel(mreg, CDNS_UART_MR_OFFSET);
+ cdns_uart_writel(cd, CDNS_UART_BAUDGEN_OFFSET);
+ cdns_uart_writel(bdiv, CDNS_UART_BAUDDIV_OFFSET);
+ cdns_uart->baud = baud;
return calc_baud;
}
-/*----------------------Uart Operations---------------------------*/
+#ifdef CONFIG_COMMON_CLK
+/**
+ * cdns_uart_clk_notitifer_cb - Clock notifier callback
+ * @nb: Notifier block
+ * @event: Notify event
+ * @data: Notifier data
+ * Return: NOTIFY_OK or NOTIFY_DONE on success, NOTIFY_BAD on error.
+ */
+static int cdns_uart_clk_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ u32 ctrl_reg;
+ struct uart_port *port;
+ int locked = 0;
+ struct clk_notifier_data *ndata = data;
+ unsigned long flags = 0;
+ struct cdns_uart *cdns_uart = to_cdns_uart(nb);
+
+ port = cdns_uart->port;
+ if (port->suspended)
+ return NOTIFY_OK;
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ {
+ u32 bdiv, cd;
+ int div8;
+
+ /*
+ * Find out if current baud-rate can be achieved with new clock
+ * frequency.
+ */
+ if (!cdns_uart_calc_baud_divs(ndata->new_rate, cdns_uart->baud,
+ &bdiv, &cd, &div8)) {
+ dev_warn(port->dev, "clock rate change rejected\n");
+ return NOTIFY_BAD;
+ }
+
+ spin_lock_irqsave(&cdns_uart->port->lock, flags);
+
+ /* Disable the TX and RX to set baud rate */
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
+
+ spin_unlock_irqrestore(&cdns_uart->port->lock, flags);
+
+ return NOTIFY_OK;
+ }
+ case POST_RATE_CHANGE:
+ /*
+ * Set clk dividers to generate correct baud with new clock
+ * frequency.
+ */
+
+ spin_lock_irqsave(&cdns_uart->port->lock, flags);
+
+ locked = 1;
+ port->uartclk = ndata->new_rate;
+
+ cdns_uart->baud = cdns_uart_set_baud_rate(cdns_uart->port,
+ cdns_uart->baud);
+ /* fall through */
+ case ABORT_RATE_CHANGE:
+ if (!locked)
+ spin_lock_irqsave(&cdns_uart->port->lock, flags);
+
+ /* Set TX/RX Reset */
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
+
+ while (cdns_uart_readl(CDNS_UART_CR_OFFSET) &
+ (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))
+ cpu_relax();
+
+ /*
+ * Clear the RX disable and TX disable bits and then set the TX
+ * enable bit and RX enable bit to enable the transmitter and
+ * receiver.
+ */
+ cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET);
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS);
+ ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
+
+ spin_unlock_irqrestore(&cdns_uart->port->lock, flags);
+
+ return NOTIFY_OK;
+ default:
+ return NOTIFY_DONE;
+ }
+}
+#endif
/**
- * xuartps_start_tx - Start transmitting bytes
+ * cdns_uart_start_tx - Start transmitting bytes
* @port: Handle to the uart port structure
- *
- **/
-static void xuartps_start_tx(struct uart_port *port)
+ */
+static void cdns_uart_start_tx(struct uart_port *port)
{
unsigned int status, numbytes = port->fifosize;
if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port))
return;
- status = xuartps_readl(XUARTPS_CR_OFFSET);
+ status = cdns_uart_readl(CDNS_UART_CR_OFFSET);
/* Set the TX enable bit and clear the TX disable bit to enable the
* transmitter.
*/
- xuartps_writel((status & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN,
- XUARTPS_CR_OFFSET);
-
- while (numbytes-- && ((xuartps_readl(XUARTPS_SR_OFFSET)
- & XUARTPS_SR_TXFULL)) != XUARTPS_SR_TXFULL) {
+ cdns_uart_writel((status & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN,
+ CDNS_UART_CR_OFFSET);
+ while (numbytes-- && ((cdns_uart_readl(CDNS_UART_SR_OFFSET) &
+ CDNS_UART_SR_TXFULL)) != CDNS_UART_SR_TXFULL) {
/* Break if no more data available in the UART buffer */
if (uart_circ_empty(&port->state->xmit))
break;
/* Get the data from the UART circular buffer and
- * write it to the xuartps's TX_FIFO register.
+ * write it to the cdns_uart's TX_FIFO register.
*/
- xuartps_writel(
+ cdns_uart_writel(
port->state->xmit.buf[port->state->xmit.tail],
- XUARTPS_FIFO_OFFSET);
+ CDNS_UART_FIFO_OFFSET);
port->icount.tx++;
/* Adjust the tail of the UART buffer and wrap
@@ -346,173 +535,173 @@ static void xuartps_start_tx(struct uart_port *port)
port->state->xmit.tail = (port->state->xmit.tail + 1) &
(UART_XMIT_SIZE - 1);
}
-
+ cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_ISR_OFFSET);
/* Enable the TX Empty interrupt */
- xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_IER_OFFSET);
+ cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_IER_OFFSET);
if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
/**
- * xuartps_stop_tx - Stop TX
+ * cdns_uart_stop_tx - Stop TX
* @port: Handle to the uart port structure
- *
- **/
-static void xuartps_stop_tx(struct uart_port *port)
+ */
+static void cdns_uart_stop_tx(struct uart_port *port)
{
unsigned int regval;
- regval = xuartps_readl(XUARTPS_CR_OFFSET);
- regval |= XUARTPS_CR_TX_DIS;
+ regval = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ regval |= CDNS_UART_CR_TX_DIS;
/* Disable the transmitter */
- xuartps_writel(regval, XUARTPS_CR_OFFSET);
+ cdns_uart_writel(regval, CDNS_UART_CR_OFFSET);
}
/**
- * xuartps_stop_rx - Stop RX
+ * cdns_uart_stop_rx - Stop RX
* @port: Handle to the uart port structure
- *
- **/
-static void xuartps_stop_rx(struct uart_port *port)
+ */
+static void cdns_uart_stop_rx(struct uart_port *port)
{
unsigned int regval;
- regval = xuartps_readl(XUARTPS_CR_OFFSET);
- regval |= XUARTPS_CR_RX_DIS;
+ regval = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ regval |= CDNS_UART_CR_RX_DIS;
/* Disable the receiver */
- xuartps_writel(regval, XUARTPS_CR_OFFSET);
+ cdns_uart_writel(regval, CDNS_UART_CR_OFFSET);
}
/**
- * xuartps_tx_empty - Check whether TX is empty
+ * cdns_uart_tx_empty - Check whether TX is empty
* @port: Handle to the uart port structure
*
- * Returns TIOCSER_TEMT on success, 0 otherwise
- **/
-static unsigned int xuartps_tx_empty(struct uart_port *port)
+ * Return: TIOCSER_TEMT on success, 0 otherwise
+ */
+static unsigned int cdns_uart_tx_empty(struct uart_port *port)
{
unsigned int status;
- status = xuartps_readl(XUARTPS_ISR_OFFSET) & XUARTPS_IXR_TXEMPTY;
+ status = cdns_uart_readl(CDNS_UART_ISR_OFFSET) & CDNS_UART_IXR_TXEMPTY;
return status ? TIOCSER_TEMT : 0;
}
/**
- * xuartps_break_ctl - Based on the input ctl we have to start or stop
+ * cdns_uart_break_ctl - Based on the input ctl we have to start or stop
* transmitting char breaks
* @port: Handle to the uart port structure
* @ctl: Value based on which start or stop decision is taken
- *
- **/
-static void xuartps_break_ctl(struct uart_port *port, int ctl)
+ */
+static void cdns_uart_break_ctl(struct uart_port *port, int ctl)
{
unsigned int status;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
- status = xuartps_readl(XUARTPS_CR_OFFSET);
+ status = cdns_uart_readl(CDNS_UART_CR_OFFSET);
if (ctl == -1)
- xuartps_writel(XUARTPS_CR_STARTBRK | status,
- XUARTPS_CR_OFFSET);
+ cdns_uart_writel(CDNS_UART_CR_STARTBRK | status,
+ CDNS_UART_CR_OFFSET);
else {
- if ((status & XUARTPS_CR_STOPBRK) == 0)
- xuartps_writel(XUARTPS_CR_STOPBRK | status,
- XUARTPS_CR_OFFSET);
+ if ((status & CDNS_UART_CR_STOPBRK) == 0)
+ cdns_uart_writel(CDNS_UART_CR_STOPBRK | status,
+ CDNS_UART_CR_OFFSET);
}
spin_unlock_irqrestore(&port->lock, flags);
}
/**
- * xuartps_set_termios - termios operations, handling data length, parity,
+ * cdns_uart_set_termios - termios operations, handling data length, parity,
* stop bits, flow control, baud rate
* @port: Handle to the uart port structure
* @termios: Handle to the input termios structure
* @old: Values of the previously saved termios structure
- *
- **/
-static void xuartps_set_termios(struct uart_port *port,
+ */
+static void cdns_uart_set_termios(struct uart_port *port,
struct ktermios *termios, struct ktermios *old)
{
unsigned int cval = 0;
- unsigned int baud;
+ unsigned int baud, minbaud, maxbaud;
unsigned long flags;
unsigned int ctrl_reg, mode_reg;
spin_lock_irqsave(&port->lock, flags);
/* Empty the receive FIFO 1st before making changes */
- while ((xuartps_readl(XUARTPS_SR_OFFSET) &
- XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) {
- xuartps_readl(XUARTPS_FIFO_OFFSET);
+ while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) &
+ CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {
+ cdns_uart_readl(CDNS_UART_FIFO_OFFSET);
}
/* Disable the TX and RX to set baud rate */
- xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
- (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS),
- XUARTPS_CR_OFFSET);
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
- /* Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk */
- baud = uart_get_baud_rate(port, termios, old, 0, 10000000);
- baud = xuartps_set_baud_rate(port, baud);
+ /*
+ * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk
+ * min and max baud should be calculated here based on port->uartclk.
+ * this way we get a valid baud and can safely call set_baud()
+ */
+ minbaud = port->uartclk /
+ ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX * 8);
+ maxbaud = port->uartclk / (CDNS_UART_BDIV_MIN + 1);
+ baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud);
+ baud = cdns_uart_set_baud_rate(port, baud);
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
- /*
- * Update the per-port timeout.
- */
+ /* Update the per-port timeout. */
uart_update_timeout(port, termios->c_cflag, baud);
/* Set TX/RX Reset */
- xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
- (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST),
- XUARTPS_CR_OFFSET);
-
- ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET);
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
- /* Clear the RX disable and TX disable bits and then set the TX enable
+ /*
+ * Clear the RX disable and TX disable bits and then set the TX enable
* bit and RX enable bit to enable the transmitter and receiver.
*/
- xuartps_writel(
- (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS))
- | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN),
- XUARTPS_CR_OFFSET);
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS);
+ ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
- xuartps_writel(10, XUARTPS_RXTOUT_OFFSET);
+ cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET);
- port->read_status_mask = XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXTRIG |
- XUARTPS_IXR_OVERRUN | XUARTPS_IXR_TOUT;
+ port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG |
+ CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT;
port->ignore_status_mask = 0;
if (termios->c_iflag & INPCK)
- port->read_status_mask |= XUARTPS_IXR_PARITY |
- XUARTPS_IXR_FRAMING;
+ port->read_status_mask |= CDNS_UART_IXR_PARITY |
+ CDNS_UART_IXR_FRAMING;
if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= XUARTPS_IXR_PARITY |
- XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN;
+ port->ignore_status_mask |= CDNS_UART_IXR_PARITY |
+ CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN;
/* ignore all characters if CREAD is not set */
if ((termios->c_cflag & CREAD) == 0)
- port->ignore_status_mask |= XUARTPS_IXR_RXTRIG |
- XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY |
- XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN;
+ port->ignore_status_mask |= CDNS_UART_IXR_RXTRIG |
+ CDNS_UART_IXR_TOUT | CDNS_UART_IXR_PARITY |
+ CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN;
- mode_reg = xuartps_readl(XUARTPS_MR_OFFSET);
+ mode_reg = cdns_uart_readl(CDNS_UART_MR_OFFSET);
/* Handling Data Size */
switch (termios->c_cflag & CSIZE) {
case CS6:
- cval |= XUARTPS_MR_CHARLEN_6_BIT;
+ cval |= CDNS_UART_MR_CHARLEN_6_BIT;
break;
case CS7:
- cval |= XUARTPS_MR_CHARLEN_7_BIT;
+ cval |= CDNS_UART_MR_CHARLEN_7_BIT;
break;
default:
case CS8:
- cval |= XUARTPS_MR_CHARLEN_8_BIT;
+ cval |= CDNS_UART_MR_CHARLEN_8_BIT;
termios->c_cflag &= ~CSIZE;
termios->c_cflag |= CS8;
break;
@@ -520,124 +709,135 @@ static void xuartps_set_termios(struct uart_port *port,
/* Handling Parity and Stop Bits length */
if (termios->c_cflag & CSTOPB)
- cval |= XUARTPS_MR_STOPMODE_2_BIT; /* 2 STOP bits */
+ cval |= CDNS_UART_MR_STOPMODE_2_BIT; /* 2 STOP bits */
else
- cval |= XUARTPS_MR_STOPMODE_1_BIT; /* 1 STOP bit */
+ cval |= CDNS_UART_MR_STOPMODE_1_BIT; /* 1 STOP bit */
if (termios->c_cflag & PARENB) {
/* Mark or Space parity */
if (termios->c_cflag & CMSPAR) {
if (termios->c_cflag & PARODD)
- cval |= XUARTPS_MR_PARITY_MARK;
+ cval |= CDNS_UART_MR_PARITY_MARK;
else
- cval |= XUARTPS_MR_PARITY_SPACE;
- } else if (termios->c_cflag & PARODD)
- cval |= XUARTPS_MR_PARITY_ODD;
+ cval |= CDNS_UART_MR_PARITY_SPACE;
+ } else {
+ if (termios->c_cflag & PARODD)
+ cval |= CDNS_UART_MR_PARITY_ODD;
else
- cval |= XUARTPS_MR_PARITY_EVEN;
- } else
- cval |= XUARTPS_MR_PARITY_NONE;
- xuartps_writel(cval , XUARTPS_MR_OFFSET);
+ cval |= CDNS_UART_MR_PARITY_EVEN;
+ }
+ } else {
+ cval |= CDNS_UART_MR_PARITY_NONE;
+ }
+ cval |= mode_reg & 1;
+ cdns_uart_writel(cval, CDNS_UART_MR_OFFSET);
spin_unlock_irqrestore(&port->lock, flags);
}
/**
- * xuartps_startup - Called when an application opens a xuartps port
+ * cdns_uart_startup - Called when an application opens a cdns_uart port
* @port: Handle to the uart port structure
*
- * Returns 0 on success, negative error otherwise
- **/
-static int xuartps_startup(struct uart_port *port)
+ * Return: 0 on success, negative errno otherwise
+ */
+static int cdns_uart_startup(struct uart_port *port)
{
unsigned int retval = 0, status = 0;
- retval = request_irq(port->irq, xuartps_isr, 0, XUARTPS_NAME,
+ retval = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME,
(void *)port);
if (retval)
return retval;
/* Disable the TX and RX */
- xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS,
- XUARTPS_CR_OFFSET);
+ cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS,
+ CDNS_UART_CR_OFFSET);
/* Set the Control Register with TX/RX Enable, TX/RX Reset,
* no break chars.
*/
- xuartps_writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST,
- XUARTPS_CR_OFFSET);
+ cdns_uart_writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST,
+ CDNS_UART_CR_OFFSET);
- status = xuartps_readl(XUARTPS_CR_OFFSET);
+ status = cdns_uart_readl(CDNS_UART_CR_OFFSET);
/* Clear the RX disable and TX disable bits and then set the TX enable
* bit and RX enable bit to enable the transmitter and receiver.
*/
- xuartps_writel((status & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS))
- | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN |
- XUARTPS_CR_STOPBRK), XUARTPS_CR_OFFSET);
+ cdns_uart_writel((status & ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS))
+ | (CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN |
+ CDNS_UART_CR_STOPBRK), CDNS_UART_CR_OFFSET);
/* Set the Mode Register with normal mode,8 data bits,1 stop bit,
* no parity.
*/
- xuartps_writel(XUARTPS_MR_CHMODE_NORM | XUARTPS_MR_STOPMODE_1_BIT
- | XUARTPS_MR_PARITY_NONE | XUARTPS_MR_CHARLEN_8_BIT,
- XUARTPS_MR_OFFSET);
+ cdns_uart_writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT
+ | CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT,
+ CDNS_UART_MR_OFFSET);
- /* Set the RX FIFO Trigger level to 14 assuming FIFO size as 16 */
- xuartps_writel(14, XUARTPS_RXWM_OFFSET);
+ /*
+ * Set the RX FIFO Trigger level to use most of the FIFO, but it
+ * can be tuned with a module parameter
+ */
+ cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET);
- /* Receive Timeout register is enabled with value of 10 */
- xuartps_writel(10, XUARTPS_RXTOUT_OFFSET);
+ /*
+ * Receive Timeout register is enabled but it
+ * can be tuned with a module parameter
+ */
+ cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET);
/* Clear out any pending interrupts before enabling them */
- xuartps_writel(xuartps_readl(XUARTPS_ISR_OFFSET), XUARTPS_ISR_OFFSET);
+ cdns_uart_writel(cdns_uart_readl(CDNS_UART_ISR_OFFSET),
+ CDNS_UART_ISR_OFFSET);
/* Set the Interrupt Registers with desired interrupts */
- xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY |
- XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN |
- XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET);
+ cdns_uart_writel(CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_PARITY |
+ CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN |
+ CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT,
+ CDNS_UART_IER_OFFSET);
return retval;
}
/**
- * xuartps_shutdown - Called when an application closes a xuartps port
+ * cdns_uart_shutdown - Called when an application closes a cdns_uart port
* @port: Handle to the uart port structure
- *
- **/
-static void xuartps_shutdown(struct uart_port *port)
+ */
+static void cdns_uart_shutdown(struct uart_port *port)
{
int status;
/* Disable interrupts */
- status = xuartps_readl(XUARTPS_IMR_OFFSET);
- xuartps_writel(status, XUARTPS_IDR_OFFSET);
+ status = cdns_uart_readl(CDNS_UART_IMR_OFFSET);
+ cdns_uart_writel(status, CDNS_UART_IDR_OFFSET);
/* Disable the TX and RX */
- xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS,
- XUARTPS_CR_OFFSET);
+ cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS,
+ CDNS_UART_CR_OFFSET);
free_irq(port->irq, port);
}
/**
- * xuartps_type - Set UART type to xuartps port
+ * cdns_uart_type - Set UART type to cdns_uart port
* @port: Handle to the uart port structure
*
- * Returns string on success, NULL otherwise
- **/
-static const char *xuartps_type(struct uart_port *port)
+ * Return: string on success, NULL otherwise
+ */
+static const char *cdns_uart_type(struct uart_port *port)
{
- return port->type == PORT_XUARTPS ? XUARTPS_NAME : NULL;
+ return port->type == PORT_XUARTPS ? CDNS_UART_NAME : NULL;
}
/**
- * xuartps_verify_port - Verify the port params
+ * cdns_uart_verify_port - Verify the port params
* @port: Handle to the uart port structure
* @ser: Handle to the structure whose members are compared
*
- * Returns 0 if success otherwise -EINVAL
- **/
-static int xuartps_verify_port(struct uart_port *port,
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int cdns_uart_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
if (ser->type != PORT_UNKNOWN && ser->type != PORT_XUARTPS)
@@ -654,135 +854,170 @@ static int xuartps_verify_port(struct uart_port *port,
}
/**
- * xuartps_request_port - Claim the memory region attached to xuartps port,
- * called when the driver adds a xuartps port via
+ * cdns_uart_request_port - Claim the memory region attached to cdns_uart port,
+ * called when the driver adds a cdns_uart port via
* uart_add_one_port()
* @port: Handle to the uart port structure
*
- * Returns 0, -ENOMEM if request fails
- **/
-static int xuartps_request_port(struct uart_port *port)
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int cdns_uart_request_port(struct uart_port *port)
{
- if (!request_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE,
- XUARTPS_NAME)) {
+ if (!request_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE,
+ CDNS_UART_NAME)) {
return -ENOMEM;
}
- port->membase = ioremap(port->mapbase, XUARTPS_REGISTER_SPACE);
+ port->membase = ioremap(port->mapbase, CDNS_UART_REGISTER_SPACE);
if (!port->membase) {
dev_err(port->dev, "Unable to map registers\n");
- release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE);
+ release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE);
return -ENOMEM;
}
return 0;
}
/**
- * xuartps_release_port - Release the memory region attached to a xuartps
- * port, called when the driver removes a xuartps
- * port via uart_remove_one_port().
+ * cdns_uart_release_port - Release UART port
* @port: Handle to the uart port structure
*
- **/
-static void xuartps_release_port(struct uart_port *port)
+ * Release the memory region attached to a cdns_uart port. Called when the
+ * driver removes a cdns_uart port via uart_remove_one_port().
+ */
+static void cdns_uart_release_port(struct uart_port *port)
{
- release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE);
+ release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE);
iounmap(port->membase);
port->membase = NULL;
}
/**
- * xuartps_config_port - Configure xuartps, called when the driver adds a
- * xuartps port
+ * cdns_uart_config_port - Configure UART port
* @port: Handle to the uart port structure
* @flags: If any
- *
- **/
-static void xuartps_config_port(struct uart_port *port, int flags)
+ */
+static void cdns_uart_config_port(struct uart_port *port, int flags)
{
- if (flags & UART_CONFIG_TYPE && xuartps_request_port(port) == 0)
+ if (flags & UART_CONFIG_TYPE && cdns_uart_request_port(port) == 0)
port->type = PORT_XUARTPS;
}
/**
- * xuartps_get_mctrl - Get the modem control state
- *
+ * cdns_uart_get_mctrl - Get the modem control state
* @port: Handle to the uart port structure
*
- * Returns the modem control state
- *
- **/
-static unsigned int xuartps_get_mctrl(struct uart_port *port)
+ * Return: the modem control state
+ */
+static unsigned int cdns_uart_get_mctrl(struct uart_port *port)
{
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
}
-static void xuartps_set_mctrl(struct uart_port *port, unsigned int mctrl)
+static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
/* N/A */
}
-static void xuartps_enable_ms(struct uart_port *port)
+static void cdns_uart_enable_ms(struct uart_port *port)
{
/* N/A */
}
-/** The UART operations structure
- */
-static struct uart_ops xuartps_ops = {
- .set_mctrl = xuartps_set_mctrl,
- .get_mctrl = xuartps_get_mctrl,
- .enable_ms = xuartps_enable_ms,
-
- .start_tx = xuartps_start_tx, /* Start transmitting */
- .stop_tx = xuartps_stop_tx, /* Stop transmission */
- .stop_rx = xuartps_stop_rx, /* Stop reception */
- .tx_empty = xuartps_tx_empty, /* Transmitter busy? */
- .break_ctl = xuartps_break_ctl, /* Start/stop
- * transmitting break
- */
- .set_termios = xuartps_set_termios, /* Set termios */
- .startup = xuartps_startup, /* App opens xuartps */
- .shutdown = xuartps_shutdown, /* App closes xuartps */
- .type = xuartps_type, /* Set UART type */
- .verify_port = xuartps_verify_port, /* Verification of port
- * params
- */
- .request_port = xuartps_request_port, /* Claim resources
- * associated with a
- * xuartps port
- */
- .release_port = xuartps_release_port, /* Release resources
- * associated with a
- * xuartps port
- */
- .config_port = xuartps_config_port, /* Configure when driver
- * adds a xuartps port
- */
+#ifdef CONFIG_CONSOLE_POLL
+static int cdns_uart_poll_get_char(struct uart_port *port)
+{
+ u32 imr;
+ int c;
+
+ /* Disable all interrupts */
+ imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET);
+ cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET);
+
+ /* Check if FIFO is empty */
+ if (cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY)
+ c = NO_POLL_CHAR;
+ else /* Read a character */
+ c = (unsigned char) cdns_uart_readl(CDNS_UART_FIFO_OFFSET);
+
+ /* Enable interrupts */
+ cdns_uart_writel(imr, CDNS_UART_IER_OFFSET);
+
+ return c;
+}
+
+static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ u32 imr;
+
+ /* Disable all interrupts */
+ imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET);
+ cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET);
+
+ /* Wait until FIFO is empty */
+ while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY))
+ cpu_relax();
+
+ /* Write a character */
+ cdns_uart_writel(c, CDNS_UART_FIFO_OFFSET);
+
+ /* Wait until FIFO is empty */
+ while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY))
+ cpu_relax();
+
+ /* Enable interrupts */
+ cdns_uart_writel(imr, CDNS_UART_IER_OFFSET);
+
+ return;
+}
+#endif
+
+static struct uart_ops cdns_uart_ops = {
+ .set_mctrl = cdns_uart_set_mctrl,
+ .get_mctrl = cdns_uart_get_mctrl,
+ .enable_ms = cdns_uart_enable_ms,
+ .start_tx = cdns_uart_start_tx,
+ .stop_tx = cdns_uart_stop_tx,
+ .stop_rx = cdns_uart_stop_rx,
+ .tx_empty = cdns_uart_tx_empty,
+ .break_ctl = cdns_uart_break_ctl,
+ .set_termios = cdns_uart_set_termios,
+ .startup = cdns_uart_startup,
+ .shutdown = cdns_uart_shutdown,
+ .type = cdns_uart_type,
+ .verify_port = cdns_uart_verify_port,
+ .request_port = cdns_uart_request_port,
+ .release_port = cdns_uart_release_port,
+ .config_port = cdns_uart_config_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = cdns_uart_poll_get_char,
+ .poll_put_char = cdns_uart_poll_put_char,
+#endif
};
-static struct uart_port xuartps_port[2];
+static struct uart_port cdns_uart_port[2];
/**
- * xuartps_get_port - Configure the port from the platform device resource
- * info
+ * cdns_uart_get_port - Configure the port from platform device resource info
+ * @id: Port id
*
- * Returns a pointer to a uart_port or NULL for failure
- **/
-static struct uart_port *xuartps_get_port(void)
+ * Return: a pointer to a uart_port or NULL for failure
+ */
+static struct uart_port *cdns_uart_get_port(int id)
{
struct uart_port *port;
- int id;
- /* Find the next unused port */
- for (id = 0; id < XUARTPS_NR_PORTS; id++)
- if (xuartps_port[id].mapbase == 0)
- break;
+ /* Try the given port id if failed use default method */
+ if (cdns_uart_port[id].mapbase != 0) {
+ /* Find the next unused port */
+ for (id = 0; id < CDNS_UART_NR_PORTS; id++)
+ if (cdns_uart_port[id].mapbase == 0)
+ break;
+ }
- if (id >= XUARTPS_NR_PORTS)
+ if (id >= CDNS_UART_NR_PORTS)
return NULL;
- port = &xuartps_port[id];
+ port = &cdns_uart_port[id];
/* At this point, we've got an empty uart_port struct, initialize it */
spin_lock_init(&port->lock);
@@ -792,52 +1027,48 @@ static struct uart_port *xuartps_get_port(void)
port->type = PORT_UNKNOWN;
port->iotype = UPIO_MEM32;
port->flags = UPF_BOOT_AUTOCONF;
- port->ops = &xuartps_ops;
- port->fifosize = XUARTPS_FIFO_SIZE;
+ port->ops = &cdns_uart_ops;
+ port->fifosize = CDNS_UART_FIFO_SIZE;
port->line = id;
port->dev = NULL;
return port;
}
-/*-----------------------Console driver operations--------------------------*/
-
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
/**
- * xuartps_console_wait_tx - Wait for the TX to be full
+ * cdns_uart_console_wait_tx - Wait for the TX to be full
* @port: Handle to the uart port structure
- *
- **/
-static void xuartps_console_wait_tx(struct uart_port *port)
+ */
+static void cdns_uart_console_wait_tx(struct uart_port *port)
{
- while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY)
- != XUARTPS_SR_TXEMPTY)
+ while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)
+ != CDNS_UART_SR_TXEMPTY)
barrier();
}
/**
- * xuartps_console_putchar - write the character to the FIFO buffer
+ * cdns_uart_console_putchar - write the character to the FIFO buffer
* @port: Handle to the uart port structure
* @ch: Character to be written
- *
- **/
-static void xuartps_console_putchar(struct uart_port *port, int ch)
+ */
+static void cdns_uart_console_putchar(struct uart_port *port, int ch)
{
- xuartps_console_wait_tx(port);
- xuartps_writel(ch, XUARTPS_FIFO_OFFSET);
+ cdns_uart_console_wait_tx(port);
+ cdns_uart_writel(ch, CDNS_UART_FIFO_OFFSET);
}
/**
- * xuartps_console_write - perform write operation
- * @port: Handle to the uart port structure
+ * cdns_uart_console_write - perform write operation
+ * @co: Console handle
* @s: Pointer to character array
* @count: No of characters
- **/
-static void xuartps_console_write(struct console *co, const char *s,
+ */
+static void cdns_uart_console_write(struct console *co, const char *s,
unsigned int count)
{
- struct uart_port *port = &xuartps_port[co->index];
+ struct uart_port *port = &cdns_uart_port[co->index];
unsigned long flags;
- unsigned int imr;
+ unsigned int imr, ctrl;
int locked = 1;
if (oops_in_progress)
@@ -846,39 +1077,45 @@ static void xuartps_console_write(struct console *co, const char *s,
spin_lock_irqsave(&port->lock, flags);
/* save and disable interrupt */
- imr = xuartps_readl(XUARTPS_IMR_OFFSET);
- xuartps_writel(imr, XUARTPS_IDR_OFFSET);
-
- uart_console_write(port, s, count, xuartps_console_putchar);
- xuartps_console_wait_tx(port);
+ imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET);
+ cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET);
- /* restore interrupt state, it seems like there may be a h/w bug
- * in that the interrupt enable register should not need to be
- * written based on the data sheet
+ /*
+ * Make sure that the tx part is enabled. Set the TX enable bit and
+ * clear the TX disable bit to enable the transmitter.
*/
- xuartps_writel(~imr, XUARTPS_IDR_OFFSET);
- xuartps_writel(imr, XUARTPS_IER_OFFSET);
+ ctrl = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ cdns_uart_writel((ctrl & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN,
+ CDNS_UART_CR_OFFSET);
+
+ uart_console_write(port, s, count, cdns_uart_console_putchar);
+ cdns_uart_console_wait_tx(port);
+
+ cdns_uart_writel(ctrl, CDNS_UART_CR_OFFSET);
+
+ /* restore interrupt state */
+ cdns_uart_writel(imr, CDNS_UART_IER_OFFSET);
if (locked)
spin_unlock_irqrestore(&port->lock, flags);
}
/**
- * xuartps_console_setup - Initialize the uart to default config
+ * cdns_uart_console_setup - Initialize the uart to default config
* @co: Console handle
* @options: Initial settings of uart
*
- * Returns 0, -ENODEV if no device
- **/
-static int __init xuartps_console_setup(struct console *co, char *options)
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int __init cdns_uart_console_setup(struct console *co, char *options)
{
- struct uart_port *port = &xuartps_port[co->index];
+ struct uart_port *port = &cdns_uart_port[co->index];
int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
- if (co->index < 0 || co->index >= XUARTPS_NR_PORTS)
+ if (co->index < 0 || co->index >= CDNS_UART_NR_PORTS)
return -EINVAL;
if (!port->mapbase) {
@@ -892,89 +1129,208 @@ static int __init xuartps_console_setup(struct console *co, char *options)
return uart_set_options(port, co, baud, parity, bits, flow);
}
-static struct uart_driver xuartps_uart_driver;
+static struct uart_driver cdns_uart_uart_driver;
-static struct console xuartps_console = {
- .name = XUARTPS_TTY_NAME,
- .write = xuartps_console_write,
+static struct console cdns_uart_console = {
+ .name = CDNS_UART_TTY_NAME,
+ .write = cdns_uart_console_write,
.device = uart_console_device,
- .setup = xuartps_console_setup,
+ .setup = cdns_uart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */
- .data = &xuartps_uart_driver,
+ .data = &cdns_uart_uart_driver,
};
/**
- * xuartps_console_init - Initialization call
+ * cdns_uart_console_init - Initialization call
*
- * Returns 0 on success, negative error otherwise
- **/
-static int __init xuartps_console_init(void)
+ * Return: 0 on success, negative errno otherwise
+ */
+static int __init cdns_uart_console_init(void)
{
- register_console(&xuartps_console);
+ register_console(&cdns_uart_console);
return 0;
}
-console_initcall(xuartps_console_init);
+console_initcall(cdns_uart_console_init);
#endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */
-/** Structure Definitions
- */
-static struct uart_driver xuartps_uart_driver = {
- .owner = THIS_MODULE, /* Owner */
- .driver_name = XUARTPS_NAME, /* Driver name */
- .dev_name = XUARTPS_TTY_NAME, /* Node name */
- .major = XUARTPS_MAJOR, /* Major number */
- .minor = XUARTPS_MINOR, /* Minor number */
- .nr = XUARTPS_NR_PORTS, /* Number of UART ports */
+static struct uart_driver cdns_uart_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = CDNS_UART_NAME,
+ .dev_name = CDNS_UART_TTY_NAME,
+ .major = CDNS_UART_MAJOR,
+ .minor = CDNS_UART_MINOR,
+ .nr = CDNS_UART_NR_PORTS,
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
- .cons = &xuartps_console, /* Console */
+ .cons = &cdns_uart_console,
#endif
};
-/* ---------------------------------------------------------------------
- * Platform bus binding
+#ifdef CONFIG_PM_SLEEP
+/**
+ * cdns_uart_suspend - suspend event
+ * @device: Pointer to the device structure
+ *
+ * Return: 0
+ */
+static int cdns_uart_suspend(struct device *device)
+{
+ struct uart_port *port = dev_get_drvdata(device);
+ struct tty_struct *tty;
+ struct device *tty_dev;
+ int may_wake = 0;
+
+ /* Get the tty which could be NULL so don't assume it's valid */
+ tty = tty_port_tty_get(&port->state->port);
+ if (tty) {
+ tty_dev = tty->dev;
+ may_wake = device_may_wakeup(tty_dev);
+ tty_kref_put(tty);
+ }
+
+ /*
+ * Call the API provided in serial_core.c file which handles
+ * the suspend.
+ */
+ uart_suspend_port(&cdns_uart_uart_driver, port);
+ if (console_suspend_enabled && !may_wake) {
+ struct cdns_uart *cdns_uart = port->private_data;
+
+ clk_disable(cdns_uart->uartclk);
+ clk_disable(cdns_uart->pclk);
+ } else {
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&port->lock, flags);
+ /* Empty the receive FIFO 1st before making changes */
+ while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) &
+ CDNS_UART_SR_RXEMPTY))
+ cdns_uart_readl(CDNS_UART_FIFO_OFFSET);
+ /* set RX trigger level to 1 */
+ cdns_uart_writel(1, CDNS_UART_RXWM_OFFSET);
+ /* disable RX timeout interrups */
+ cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IDR_OFFSET);
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+ return 0;
+}
+
+/**
+ * cdns_uart_resume - Resume after a previous suspend
+ * @device: Pointer to the device structure
+ *
+ * Return: 0
*/
+static int cdns_uart_resume(struct device *device)
+{
+ struct uart_port *port = dev_get_drvdata(device);
+ unsigned long flags = 0;
+ u32 ctrl_reg;
+ struct tty_struct *tty;
+ struct device *tty_dev;
+ int may_wake = 0;
+
+ /* Get the tty which could be NULL so don't assume it's valid */
+ tty = tty_port_tty_get(&port->state->port);
+ if (tty) {
+ tty_dev = tty->dev;
+ may_wake = device_may_wakeup(tty_dev);
+ tty_kref_put(tty);
+ }
+
+ if (console_suspend_enabled && !may_wake) {
+ struct cdns_uart *cdns_uart = port->private_data;
+
+ clk_enable(cdns_uart->pclk);
+ clk_enable(cdns_uart->uartclk);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* Set TX/RX Reset */
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
+ while (cdns_uart_readl(CDNS_UART_CR_OFFSET) &
+ (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))
+ cpu_relax();
+
+ /* restore rx timeout value */
+ cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET);
+ /* Enable Tx/Rx */
+ ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET);
+ ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS);
+ ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN;
+ cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+ } else {
+ spin_lock_irqsave(&port->lock, flags);
+ /* restore original rx trigger level */
+ cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET);
+ /* enable RX timeout interrupt */
+ cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IER_OFFSET);
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+ return uart_resume_port(&cdns_uart_uart_driver, port);
+}
+#endif /* ! CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend,
+ cdns_uart_resume);
+
/**
- * xuartps_probe - Platform driver probe
+ * cdns_uart_probe - Platform driver probe
* @pdev: Pointer to the platform device structure
*
- * Returns 0 on success, negative error otherwise
- **/
-static int xuartps_probe(struct platform_device *pdev)
+ * Return: 0 on success, negative errno otherwise
+ */
+static int cdns_uart_probe(struct platform_device *pdev)
{
- int rc;
+ int rc, id;
struct uart_port *port;
struct resource *res, *res2;
- struct xuartps *xuartps_data;
+ struct cdns_uart *cdns_uart_data;
- xuartps_data = kzalloc(sizeof(*xuartps_data), GFP_KERNEL);
- if (!xuartps_data)
+ cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data),
+ GFP_KERNEL);
+ if (!cdns_uart_data)
return -ENOMEM;
- xuartps_data->aperclk = clk_get(&pdev->dev, "aper_clk");
- if (IS_ERR(xuartps_data->aperclk)) {
- dev_err(&pdev->dev, "aper_clk clock not found.\n");
- rc = PTR_ERR(xuartps_data->aperclk);
- goto err_out_free;
+ cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(cdns_uart_data->pclk)) {
+ cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk");
+ if (!IS_ERR(cdns_uart_data->pclk))
+ dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n");
+ }
+ if (IS_ERR(cdns_uart_data->pclk)) {
+ dev_err(&pdev->dev, "pclk clock not found.\n");
+ return PTR_ERR(cdns_uart_data->pclk);
+ }
+
+ cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk");
+ if (IS_ERR(cdns_uart_data->uartclk)) {
+ cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk");
+ if (!IS_ERR(cdns_uart_data->uartclk))
+ dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n");
}
- xuartps_data->refclk = clk_get(&pdev->dev, "ref_clk");
- if (IS_ERR(xuartps_data->refclk)) {
- dev_err(&pdev->dev, "ref_clk clock not found.\n");
- rc = PTR_ERR(xuartps_data->refclk);
- goto err_out_clk_put_aper;
+ if (IS_ERR(cdns_uart_data->uartclk)) {
+ dev_err(&pdev->dev, "uart_clk clock not found.\n");
+ return PTR_ERR(cdns_uart_data->uartclk);
}
- rc = clk_prepare_enable(xuartps_data->aperclk);
+ rc = clk_prepare_enable(cdns_uart_data->pclk);
if (rc) {
- dev_err(&pdev->dev, "Unable to enable APER clock.\n");
- goto err_out_clk_put;
+ dev_err(&pdev->dev, "Unable to enable pclk clock.\n");
+ return rc;
}
- rc = clk_prepare_enable(xuartps_data->refclk);
+ rc = clk_prepare_enable(cdns_uart_data->uartclk);
if (rc) {
dev_err(&pdev->dev, "Unable to enable device clock.\n");
- goto err_out_clk_dis_aper;
+ goto err_out_clk_dis_pclk;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -989,13 +1345,25 @@ static int xuartps_probe(struct platform_device *pdev)
goto err_out_clk_disable;
}
+#ifdef CONFIG_COMMON_CLK
+ cdns_uart_data->clk_rate_change_nb.notifier_call =
+ cdns_uart_clk_notifier_cb;
+ if (clk_notifier_register(cdns_uart_data->uartclk,
+ &cdns_uart_data->clk_rate_change_nb))
+ dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
+#endif
+ /* Look for a serialN alias */
+ id = of_alias_get_id(pdev->dev.of_node, "serial");
+ if (id < 0)
+ id = 0;
+
/* Initialize the port structure */
- port = xuartps_get_port();
+ port = cdns_uart_get_port(id);
if (!port) {
dev_err(&pdev->dev, "Cannot get uart_port structure\n");
rc = -ENODEV;
- goto err_out_clk_disable;
+ goto err_out_notif_unreg;
} else {
/* Register the port.
* This function also registers this device with the tty layer
@@ -1004,116 +1372,104 @@ static int xuartps_probe(struct platform_device *pdev)
port->mapbase = res->start;
port->irq = res2->start;
port->dev = &pdev->dev;
- port->uartclk = clk_get_rate(xuartps_data->refclk);
- port->private_data = xuartps_data;
+ port->uartclk = clk_get_rate(cdns_uart_data->uartclk);
+ port->private_data = cdns_uart_data;
+ cdns_uart_data->port = port;
platform_set_drvdata(pdev, port);
- rc = uart_add_one_port(&xuartps_uart_driver, port);
+ rc = uart_add_one_port(&cdns_uart_uart_driver, port);
if (rc) {
dev_err(&pdev->dev,
"uart_add_one_port() failed; err=%i\n", rc);
- goto err_out_clk_disable;
+ goto err_out_notif_unreg;
}
return 0;
}
+err_out_notif_unreg:
+#ifdef CONFIG_COMMON_CLK
+ clk_notifier_unregister(cdns_uart_data->uartclk,
+ &cdns_uart_data->clk_rate_change_nb);
+#endif
err_out_clk_disable:
- clk_disable_unprepare(xuartps_data->refclk);
-err_out_clk_dis_aper:
- clk_disable_unprepare(xuartps_data->aperclk);
-err_out_clk_put:
- clk_put(xuartps_data->refclk);
-err_out_clk_put_aper:
- clk_put(xuartps_data->aperclk);
-err_out_free:
- kfree(xuartps_data);
+ clk_disable_unprepare(cdns_uart_data->uartclk);
+err_out_clk_dis_pclk:
+ clk_disable_unprepare(cdns_uart_data->pclk);
return rc;
}
/**
- * xuartps_remove - called when the platform driver is unregistered
+ * cdns_uart_remove - called when the platform driver is unregistered
* @pdev: Pointer to the platform device structure
*
- * Returns 0 on success, negative error otherwise
- **/
-static int xuartps_remove(struct platform_device *pdev)
+ * Return: 0 on success, negative errno otherwise
+ */
+static int cdns_uart_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
- struct xuartps *xuartps_data = port->private_data;
+ struct cdns_uart *cdns_uart_data = port->private_data;
int rc;
- /* Remove the xuartps port from the serial core */
- rc = uart_remove_one_port(&xuartps_uart_driver, port);
+ /* Remove the cdns_uart port from the serial core */
+#ifdef CONFIG_COMMON_CLK
+ clk_notifier_unregister(cdns_uart_data->uartclk,
+ &cdns_uart_data->clk_rate_change_nb);
+#endif
+ rc = uart_remove_one_port(&cdns_uart_uart_driver, port);
port->mapbase = 0;
- clk_disable_unprepare(xuartps_data->refclk);
- clk_disable_unprepare(xuartps_data->aperclk);
- clk_put(xuartps_data->refclk);
- clk_put(xuartps_data->aperclk);
- kfree(xuartps_data);
+ clk_disable_unprepare(cdns_uart_data->uartclk);
+ clk_disable_unprepare(cdns_uart_data->pclk);
return rc;
}
/* Match table for of_platform binding */
-static struct of_device_id xuartps_of_match[] = {
+static struct of_device_id cdns_uart_of_match[] = {
{ .compatible = "xlnx,xuartps", },
+ { .compatible = "cdns,uart-r1p8", },
{}
};
-MODULE_DEVICE_TABLE(of, xuartps_of_match);
+MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
-static struct platform_driver xuartps_platform_driver = {
- .probe = xuartps_probe, /* Probe method */
- .remove = xuartps_remove, /* Detach method */
+static struct platform_driver cdns_uart_platform_driver = {
+ .probe = cdns_uart_probe,
+ .remove = cdns_uart_remove,
.driver = {
.owner = THIS_MODULE,
- .name = XUARTPS_NAME, /* Driver name */
- .of_match_table = xuartps_of_match,
+ .name = CDNS_UART_NAME,
+ .of_match_table = cdns_uart_of_match,
+ .pm = &cdns_uart_dev_pm_ops,
},
};
-/* ---------------------------------------------------------------------
- * Module Init and Exit
- */
-/**
- * xuartps_init - Initial driver registration call
- *
- * Returns whether the registration was successful or not
- **/
-static int __init xuartps_init(void)
+static int __init cdns_uart_init(void)
{
int retval = 0;
- /* Register the xuartps driver with the serial core */
- retval = uart_register_driver(&xuartps_uart_driver);
+ /* Register the cdns_uart driver with the serial core */
+ retval = uart_register_driver(&cdns_uart_uart_driver);
if (retval)
return retval;
/* Register the platform driver */
- retval = platform_driver_register(&xuartps_platform_driver);
+ retval = platform_driver_register(&cdns_uart_platform_driver);
if (retval)
- uart_unregister_driver(&xuartps_uart_driver);
+ uart_unregister_driver(&cdns_uart_uart_driver);
return retval;
}
-/**
- * xuartps_exit - Driver unregistration call
- **/
-static void __exit xuartps_exit(void)
+static void __exit cdns_uart_exit(void)
{
- /* The order of unregistration is important. Unregister the
- * UART driver before the platform driver crashes the system.
- */
-
/* Unregister the platform driver */
- platform_driver_unregister(&xuartps_platform_driver);
+ platform_driver_unregister(&cdns_uart_platform_driver);
- /* Unregister the xuartps driver */
- uart_unregister_driver(&xuartps_uart_driver);
+ /* Unregister the cdns_uart driver */
+ uart_unregister_driver(&cdns_uart_uart_driver);
}
-module_init(xuartps_init);
-module_exit(xuartps_exit);
+module_init(cdns_uart_init);
+module_exit(cdns_uart_exit);
-MODULE_DESCRIPTION("Driver for PS UART");
+MODULE_DESCRIPTION("Driver for Cadence UART");
MODULE_AUTHOR("Xilinx Inc.");
MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c
index 6a169877109..2b65bb7ffb8 100644
--- a/drivers/tty/serial/zs.c
+++ b/drivers/tty/serial/zs.c
@@ -923,7 +923,7 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios,
uport->read_status_mask = Rx_OVR;
if (termios->c_iflag & INPCK)
uport->read_status_mask |= FRM_ERR | PAR_ERR;
- if (termios->c_iflag & (BRKINT | PARMRK))
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
uport->read_status_mask |= Rx_BRK;
uport->ignore_status_mask = 0;