aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/serial
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r--drivers/tty/serial/21285.c8
-rw-r--r--drivers/tty/serial/68328serial.c433
-rw-r--r--drivers/tty/serial/68328serial.h186
-rw-r--r--drivers/tty/serial/8250/8250.h151
-rw-r--r--drivers/tty/serial/8250/8250_acorn.c28
-rw-r--r--drivers/tty/serial/8250/8250_core.c (renamed from drivers/tty/serial/8250/8250.c)1433
-rw-r--r--drivers/tty/serial/8250/8250_dma.c244
-rw-r--r--drivers/tty/serial/8250/8250_dw.c489
-rw-r--r--drivers/tty/serial/8250/8250_early.c184
-rw-r--r--drivers/tty/serial/8250/8250_em.c174
-rw-r--r--drivers/tty/serial/8250/8250_gsc.c33
-rw-r--r--drivers/tty/serial/8250/8250_hp300.c56
-rw-r--r--drivers/tty/serial/8250/8250_mca.c61
-rw-r--r--drivers/tty/serial/8250/8250_pci.c1447
-rw-r--r--drivers/tty/serial/8250/8250_pnp.c72
-rw-r--r--drivers/tty/serial/8250/Kconfig90
-rw-r--r--drivers/tty/serial/8250/Makefile6
-rw-r--r--drivers/tty/serial/8250/serial_cs.c46
-rw-r--r--drivers/tty/serial/Kconfig322
-rw-r--r--drivers/tty/serial/Makefile23
-rw-r--r--drivers/tty/serial/altera_jtaguart.c20
-rw-r--r--drivers/tty/serial/altera_uart.c81
-rw-r--r--drivers/tty/serial/amba-pl010.c40
-rw-r--r--drivers/tty/serial/amba-pl011.c763
-rw-r--r--drivers/tty/serial/apbuart.c7
-rw-r--r--drivers/tty/serial/ar933x_uart.c222
-rw-r--r--drivers/tty/serial/arc_uart.c783
-rw-r--r--drivers/tty/serial/atmel_serial.c1221
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c46
-rw-r--r--drivers/tty/serial/bfin_sport_uart.c31
-rw-r--r--drivers/tty/serial/bfin_uart.c171
-rw-r--r--drivers/tty/serial/clps711x.c754
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c81
-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.c393
-rw-r--r--drivers/tty/serial/crisv10.h14
-rw-r--r--drivers/tty/serial/dz.c7
-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.c850
-rw-r--r--drivers/tty/serial/fsl_lpuart.c1285
-rw-r--r--drivers/tty/serial/icom.c137
-rw-r--r--drivers/tty/serial/ifx6x60.c274
-rw-r--r--drivers/tty/serial/ifx6x60.h2
-rw-r--r--drivers/tty/serial/imx.c952
-rw-r--r--drivers/tty/serial/ioc3_serial.c16
-rw-r--r--drivers/tty/serial/ioc4_serial.c25
-rw-r--r--drivers/tty/serial/ip22zilog.c36
-rw-r--r--drivers/tty/serial/jsm/jsm.h8
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c11
-rw-r--r--drivers/tty/serial/jsm/jsm_neo.c116
-rw-r--r--drivers/tty/serial/jsm/jsm_tty.c134
-rw-r--r--drivers/tty/serial/kgdb_nmi.c391
-rw-r--r--drivers/tty/serial/kgdboc.c16
-rw-r--r--drivers/tty/serial/lantiq.c106
-rw-r--r--drivers/tty/serial/lpc32xx_hs.c810
-rw-r--r--drivers/tty/serial/m32r_sio.c69
-rw-r--r--drivers/tty/serial/m32r_sio.h1
-rw-r--r--drivers/tty/serial/max3100.c71
-rw-r--r--drivers/tty/serial/max3107.c1215
-rw-r--r--drivers/tty/serial/max3107.h441
-rw-r--r--drivers/tty/serial/max310x.c1372
-rw-r--r--drivers/tty/serial/mcf.c94
-rw-r--r--drivers/tty/serial/men_z135_uart.c867
-rw-r--r--drivers/tty/serial/mfd.c71
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c641
-rw-r--r--drivers/tty/serial/mpsc.c30
-rw-r--r--drivers/tty/serial/mrst_max3110.c93
-rw-r--r--drivers/tty/serial/msm_serial.c485
-rw-r--r--drivers/tty/serial/msm_serial.h31
-rw-r--r--drivers/tty/serial/msm_serial_hs.c30
-rw-r--r--drivers/tty/serial/msm_smd_tty.c27
-rw-r--r--drivers/tty/serial/mux.c22
-rw-r--r--drivers/tty/serial/mxs-auart.c451
-rw-r--r--drivers/tty/serial/netx-serial.c14
-rw-r--r--drivers/tty/serial/nwpserial.c11
-rw-r--r--drivers/tty/serial/of_serial.c108
-rw-r--r--drivers/tty/serial/omap-serial.c1499
-rw-r--r--drivers/tty/serial/pch_uart.c562
-rw-r--r--drivers/tty/serial/pmac_zilog.c69
-rw-r--r--drivers/tty/serial/pnx8xxx_uart.c10
-rw-r--r--drivers/tty/serial/pxa.c163
-rw-r--r--drivers/tty/serial/rp2.c887
-rw-r--r--drivers/tty/serial/sa1100.c18
-rw-r--r--drivers/tty/serial/samsung.c341
-rw-r--r--drivers/tty/serial/samsung.h26
-rw-r--r--drivers/tty/serial/sb1250-duart.c5
-rw-r--r--drivers/tty/serial/sc16is7xx.c1277
-rw-r--r--drivers/tty/serial/sc26xx.c745
-rw-r--r--drivers/tty/serial/sccnxp.c1031
-rw-r--r--drivers/tty/serial/serial-tegra.c1402
-rw-r--r--drivers/tty/serial/serial_core.c659
-rw-r--r--drivers/tty/serial/serial_ks8695.c9
-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.c32
-rw-r--r--drivers/tty/serial/sh-sci.c1035
-rw-r--r--drivers/tty/serial/sh-sci.h12
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c1191
-rw-r--r--drivers/tty/serial/sirfsoc_uart.h505
-rw-r--r--drivers/tty/serial/sn_console.c21
-rw-r--r--drivers/tty/serial/st-asc.c932
-rw-r--r--drivers/tty/serial/suncore.c2
-rw-r--r--drivers/tty/serial/suncore.h33
-rw-r--r--drivers/tty/serial/sunhv.c71
-rw-r--r--drivers/tty/serial/sunsab.c68
-rw-r--r--drivers/tty/serial/sunsu.c88
-rw-r--r--drivers/tty/serial/sunzilog.c98
-rw-r--r--drivers/tty/serial/tilegx.c708
-rw-r--r--drivers/tty/serial/timbuart.c16
-rw-r--r--drivers/tty/serial/uartlite.c153
-rw-r--r--drivers/tty/serial/ucc_uart.c27
-rw-r--r--drivers/tty/serial/vr41xx_siu.c20
-rw-r--r--drivers/tty/serial/vt8500_serial.c108
-rw-r--r--drivers/tty/serial/xilinx_uartps.c1457
-rw-r--r--drivers/tty/serial/zs.c6
117 files changed, 27392 insertions, 9816 deletions
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index 1b37626e8f1..c7e8b60b617 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -16,6 +16,7 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
+#include <asm/system_info.h>
#include <asm/hardware/dec21285.h>
#include <mach/hardware.h>
@@ -84,7 +85,6 @@ static void serial21285_enable_ms(struct uart_port *port)
static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
- struct tty_struct *tty = port->state->port.tty;
unsigned int status, ch, flag, rxs, max_count = 256;
status = *CSR_UARTFLG;
@@ -114,7 +114,7 @@ static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)
status = *CSR_UARTFLG;
}
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&port->state->port);
return IRQ_HANDLED;
}
@@ -331,7 +331,7 @@ static int serial21285_verify_port(struct uart_port *port, struct serial_struct
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)
ret = -EINVAL;
- if (ser->irq != NO_IRQ)
+ if (ser->irq <= 0)
ret = -EINVAL;
if (ser->baud_base != port->uartclk / 16)
ret = -EINVAL;
@@ -360,7 +360,7 @@ static struct uart_ops serial21285_ops = {
static struct uart_port serial21285_port = {
.mapbase = 0x42000160,
.iotype = UPIO_MEM,
- .irq = NO_IRQ,
+ .irq = 0,
.fifosize = 16,
.ops = &serial21285_ops,
.flags = UPF_BOOT_AUTOCONF,
diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c
index a88ef9782a4..5dc9c4bfa66 100644
--- a/drivers/tty/serial/68328serial.c
+++ b/drivers/tty/serial/68328serial.c
@@ -14,9 +14,9 @@
* 2.4/2.5 port David McCullough
*/
-#include <asm/dbg.h>
#include <linux/module.h>
#include <linux/errno.h>
+#include <linux/serial.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
@@ -39,7 +39,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
@@ -57,8 +56,6 @@
#endif /* CONFIG_M68VZ328 */
#endif /* CONFIG_M68EZ328 */
-#include "68328serial.h"
-
/* Turn off usage of real serial interrupt code, to "support" Copilot */
#ifdef CONFIG_XCOPILOT_BUGS
#undef USE_INTS
@@ -66,33 +63,82 @@
#define USE_INTS
#endif
-static struct m68k_serial m68k_soft[NR_PORTS];
+/*
+ * I believe this is the optimal setting that reduces the number of interrupts.
+ * At high speeds the output might become a little "bursted" (use USTCNT_TXHE
+ * if that bothers you), but in most cases it will not, since we try to
+ * transmit characters every time rs_interrupt is called. Thus, quite often
+ * you'll see that a receive interrupt occures before the transmit one.
+ * -- Vladimir Gurevich
+ */
+#define USTCNT_TX_INTR_MASK (USTCNT_TXEE)
-static unsigned int uart_irqs[NR_PORTS] = UART_IRQ_DEFNS;
+/*
+ * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special
+ * "Old data interrupt" which occures whenever the data stay in the FIFO
+ * longer than 30 bits time. This allows us to use FIFO without compromising
+ * latency. '328 does not have this feature and without the real 328-based
+ * board I would assume that RXRE is the safest setting.
+ *
+ * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of
+ * interrupts. RXFE (receive queue full) causes the system to lose data
+ * at least at 115200 baud
+ *
+ * If your board is busy doing other stuff, you might consider to use
+ * RXRE (data ready intrrupt) instead.
+ *
+ * The other option is to make these INTR masks run-time configurable, so
+ * that people can dynamically adapt them according to the current usage.
+ * -- Vladimir Gurevich
+ */
-/* multiple ports are contiguous in memory */
-m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR;
+/* (es) */
+#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328)
+#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN)
+#elif defined(CONFIG_M68328)
+#define USTCNT_RX_INTR_MASK (USTCNT_RXRE)
+#else
+#error Please, define the Rx interrupt events for your CPU
+#endif
+/* (/es) */
-struct tty_struct m68k_ttys;
-struct m68k_serial *m68k_consinfo = 0;
+/*
+ * This is our internal structure for each serial port's state.
+ */
+struct m68k_serial {
+ struct tty_port tport;
+ char is_cons; /* Is this our console. */
+ int magic;
+ int baud_base;
+ int port;
+ int irq;
+ int type; /* UART type */
+ int custom_divisor;
+ int x_char; /* xon/xoff character */
+ int line;
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+};
-#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */
+#define SERIAL_MAGIC 0x5301
-struct tty_driver *serial_driver;
+/*
+ * Define the number of ports supported and their irqs.
+ */
+#define NR_PORTS 1
+
+static struct m68k_serial m68k_soft[NR_PORTS];
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
+static unsigned int uart_irqs[NR_PORTS] = { UART_IRQ_NUM };
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
- */
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
+/* multiple ports are contiguous in memory */
+m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR;
-#define RS_ISR_PASS_LIMIT 256
+struct tty_driver *serial_driver;
-static void change_speed(struct m68k_serial *info);
+static void change_speed(struct m68k_serial *info, struct tty_struct *tty);
/*
* Setup for console. Argument comes from the boot command line.
@@ -144,17 +190,6 @@ static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 0 };
-/* Sets or clears DTR/RTS on the requested line */
-static inline void m68k_rtsdtr(struct m68k_serial *ss, int set)
-{
- if (set) {
- /* set the RTS/CTS line */
- } else {
- /* clear it */
- }
- return;
-}
-
/* Utility routines */
static inline int get_baud(struct m68k_serial *ss)
{
@@ -190,7 +225,8 @@ static void rs_stop(struct tty_struct *tty)
static int rs_put_char(char ch)
{
- int flags, loops = 0;
+ unsigned long flags;
+ int loops = 0;
local_irq_save(flags);
@@ -225,28 +261,8 @@ static void rs_start(struct tty_struct *tty)
local_irq_restore(flags);
}
-/* Drop into either the boot monitor or kadb upon receiving a break
- * from keyboard/console input.
- */
-static void batten_down_hatches(void)
-{
- /* Drop into the debugger */
-}
-
-static void status_handle(struct m68k_serial *info, unsigned short status)
-{
- /* If this is console input and this is a
- * 'break asserted' status change interrupt
- * see if we can drop into the debugger
- */
- if((status & URX_BREAK) && info->break_abort)
- batten_down_hatches();
- return;
-}
-
static void receive_chars(struct m68k_serial *info, unsigned short rx)
{
- struct tty_struct *tty = info->tty;
m68328_uart *uart = &uart_addr[info->line];
unsigned char ch, flag;
@@ -260,7 +276,6 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx)
if(info->is_cons) {
if(URX_BREAK & rx) { /* whee, break received */
- status_handle(info, rx);
return;
#ifdef CONFIG_MAGIC_SYSRQ
} else if (ch == 0x10) { /* ^P */
@@ -276,33 +291,24 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx)
}
}
- if(!tty)
- goto clear_and_exit;
-
flag = TTY_NORMAL;
- if(rx & URX_PARITY_ERROR) {
+ if (rx & URX_PARITY_ERROR)
flag = TTY_PARITY;
- status_handle(info, rx);
- } else if(rx & URX_OVRUN) {
+ else if (rx & URX_OVRUN)
flag = TTY_OVERRUN;
- status_handle(info, rx);
- } else if(rx & URX_FRAME_ERROR) {
+ else if (rx & URX_FRAME_ERROR)
flag = TTY_FRAME;
- status_handle(info, rx);
- }
- tty_insert_flip_char(tty, ch, flag);
+
+ tty_insert_flip_char(&info->tport, ch, flag);
#ifndef CONFIG_XCOPILOT_BUGS
} while((rx = uart->urx.w) & URX_DATA_READY);
#endif
- tty_schedule_flip(tty);
-
-clear_and_exit:
- return;
+ tty_schedule_flip(&info->tport);
}
-static void transmit_chars(struct m68k_serial *info)
+static void transmit_chars(struct m68k_serial *info, struct tty_struct *tty)
{
m68328_uart *uart = &uart_addr[info->line];
@@ -313,7 +319,7 @@ static void transmit_chars(struct m68k_serial *info)
goto clear_and_return;
}
- if((info->xmit_cnt <= 0) || info->tty->stopped) {
+ if ((info->xmit_cnt <= 0) || !tty || tty->stopped) {
/* That's peculiar... TX ints off */
uart->ustcnt &= ~USTCNT_TX_INTR_MASK;
goto clear_and_return;
@@ -341,6 +347,7 @@ clear_and_return:
irqreturn_t rs_interrupt(int irq, void *dev_id)
{
struct m68k_serial *info = dev_id;
+ struct tty_struct *tty = tty_port_tty_get(&info->tport);
m68328_uart *uart;
unsigned short rx;
unsigned short tx;
@@ -351,20 +358,24 @@ irqreturn_t rs_interrupt(int irq, void *dev_id)
#ifdef USE_INTS
tx = uart->utx.w;
- if (rx & URX_DATA_READY) receive_chars(info, rx);
- if (tx & UTX_TX_AVAIL) transmit_chars(info);
+ if (rx & URX_DATA_READY)
+ receive_chars(info, rx);
+ if (tx & UTX_TX_AVAIL)
+ transmit_chars(info, tty);
#else
- receive_chars(info, rx);
+ receive_chars(info, rx);
#endif
+ tty_kref_put(tty);
+
return IRQ_HANDLED;
}
-static int startup(struct m68k_serial * info)
+static int startup(struct m68k_serial *info, struct tty_struct *tty)
{
m68328_uart *uart = &uart_addr[info->line];
unsigned long flags;
- if (info->flags & S_INITIALIZED)
+ if (info->tport.flags & ASYNC_INITIALIZED)
return 0;
if (!info->xmit_buf) {
@@ -381,7 +392,6 @@ static int startup(struct m68k_serial * info)
*/
uart->ustcnt = USTCNT_UEN;
- info->xmit_fifo_size = 1;
uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN;
(void)uart->urx.w;
@@ -395,17 +405,17 @@ static int startup(struct m68k_serial * info)
uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK;
#endif
- if (info->tty)
- clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ if (tty)
+ clear_bit(TTY_IO_ERROR, &tty->flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
/*
* and set the speed of the serial port
*/
- change_speed(info);
+ change_speed(info, tty);
- info->flags |= S_INITIALIZED;
+ info->tport.flags |= ASYNC_INITIALIZED;
local_irq_restore(flags);
return 0;
}
@@ -414,13 +424,13 @@ static int startup(struct m68k_serial * info)
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
-static void shutdown(struct m68k_serial * info)
+static void shutdown(struct m68k_serial *info, struct tty_struct *tty)
{
m68328_uart *uart = &uart_addr[info->line];
unsigned long flags;
uart->ustcnt = 0; /* All off! */
- if (!(info->flags & S_INITIALIZED))
+ if (!(info->tport.flags & ASYNC_INITIALIZED))
return;
local_irq_save(flags);
@@ -430,10 +440,10 @@ static void shutdown(struct m68k_serial * info)
info->xmit_buf = 0;
}
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
+ if (tty)
+ set_bit(TTY_IO_ERROR, &tty->flags);
- info->flags &= ~S_INITIALIZED;
+ info->tport.flags &= ~ASYNC_INITIALIZED;
local_irq_restore(flags);
}
@@ -489,7 +499,7 @@ struct {
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
-static void change_speed(struct m68k_serial *info)
+static void change_speed(struct m68k_serial *info, struct tty_struct *tty)
{
m68328_uart *uart = &uart_addr[info->line];
unsigned short port;
@@ -497,9 +507,7 @@ static void change_speed(struct m68k_serial *info)
unsigned cflag;
int i;
- if (!info->tty || !info->tty->termios)
- return;
- cflag = info->tty->termios->c_cflag;
+ cflag = tty->termios.c_cflag;
if (!(port = info->port))
return;
@@ -511,7 +519,6 @@ static void change_speed(struct m68k_serial *info)
i = (i & ~CBAUDEX) + B38400;
}
- info->baud = baud_table[i];
uart->ubaud = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) |
PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale);
@@ -602,7 +609,7 @@ static void rs_set_ldisc(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->name, "rs_set_ldisc"))
return;
- info->is_cons = (tty->termios->c_line == N_TTY);
+ info->is_cons = (tty->termios.c_line == N_TTY);
printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off");
}
@@ -622,8 +629,7 @@ static void rs_flush_chars(struct tty_struct *tty)
/* Enable transmitter */
local_irq_save(flags);
- if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
- !info->xmit_buf) {
+ if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) {
local_irq_restore(flags);
return;
}
@@ -689,7 +695,7 @@ static int rs_write(struct tty_struct * tty,
total += c;
}
- if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+ if (info->xmit_cnt && !tty->stopped) {
/* Enable transmitter */
local_irq_disable();
#ifndef USE_INTS
@@ -808,10 +814,10 @@ static int get_serial_info(struct m68k_serial * info,
tmp.line = info->line;
tmp.port = info->port;
tmp.irq = info->irq;
- tmp.flags = info->flags;
+ tmp.flags = info->tport.flags;
tmp.baud_base = info->baud_base;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
+ tmp.close_delay = info->tport.close_delay;
+ tmp.closing_wait = info->tport.closing_wait;
tmp.custom_divisor = info->custom_divisor;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
@@ -819,9 +825,10 @@ static int get_serial_info(struct m68k_serial * info,
return 0;
}
-static int set_serial_info(struct m68k_serial * info,
+static int set_serial_info(struct m68k_serial *info, struct tty_struct *tty,
struct serial_struct * new_info)
{
+ struct tty_port *port = &info->tport;
struct serial_struct new_serial;
struct m68k_serial old_info;
int retval = 0;
@@ -835,17 +842,17 @@ static int set_serial_info(struct m68k_serial * info,
if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.baud_base != info->baud_base) ||
(new_serial.type != info->type) ||
- (new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~S_USR_MASK) !=
- (info->flags & ~S_USR_MASK)))
+ (new_serial.close_delay != port->close_delay) ||
+ ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ (port->flags & ~ASYNC_USR_MASK)))
return -EPERM;
- info->flags = ((info->flags & ~S_USR_MASK) |
- (new_serial.flags & S_USR_MASK));
+ port->flags = ((port->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
info->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
- if (info->count > 1)
+ if (port->count > 1)
return -EBUSY;
/*
@@ -854,14 +861,14 @@ static int set_serial_info(struct m68k_serial * info,
*/
info->baud_base = new_serial.baud_base;
- info->flags = ((info->flags & ~S_FLAGS) |
- (new_serial.flags & S_FLAGS));
+ port->flags = ((port->flags & ~ASYNC_FLAGS) |
+ (new_serial.flags & ASYNC_FLAGS));
info->type = new_serial.type;
- info->close_delay = new_serial.close_delay;
- info->closing_wait = new_serial.closing_wait;
+ port->close_delay = new_serial.close_delay;
+ port->closing_wait = new_serial.closing_wait;
check_and_exit:
- retval = startup(info);
+ retval = startup(info, tty);
return retval;
}
@@ -947,7 +954,7 @@ static int rs_ioctl(struct tty_struct *tty,
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
- return set_serial_info(info,
+ return set_serial_info(info, tty,
(struct serial_struct *) arg);
case TIOCSERGETLSR: /* Get line status register */
return get_lsr_info(info, (unsigned int *) arg);
@@ -966,13 +973,11 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
- change_speed(info);
+ change_speed(info, tty);
if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- tty->hw_stopped = 0;
+ !(tty->termios.c_cflag & CRTSCTS))
rs_start(tty);
- }
}
@@ -989,10 +994,11 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
static void rs_close(struct tty_struct *tty, struct file * filp)
{
struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
+ struct tty_port *port = &info->tport;
m68328_uart *uart = &uart_addr[info->line];
unsigned long flags;
- if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
+ if (serial_paranoia_check(info, tty->name, "rs_close"))
return;
local_irq_save(flags);
@@ -1002,7 +1008,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
return;
}
- if ((tty->count == 1) && (info->count != 1)) {
+ if ((tty->count == 1) && (port->count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. Info->count should always
@@ -1011,26 +1017,26 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
* serial port won't be shutdown.
*/
printk("rs_close: bad serial port count; tty->count is 1, "
- "info->count is %d\n", info->count);
- info->count = 1;
+ "port->count is %d\n", port->count);
+ port->count = 1;
}
- if (--info->count < 0) {
+ if (--port->count < 0) {
printk("rs_close: bad serial port count for ttyS%d: %d\n",
- info->line, info->count);
- info->count = 0;
+ info->line, port->count);
+ port->count = 0;
}
- if (info->count) {
+ if (port->count) {
local_irq_restore(flags);
return;
}
- info->flags |= S_CLOSING;
+ port->flags |= ASYNC_CLOSING;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
- if (info->closing_wait != S_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, info->closing_wait);
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, port->closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
@@ -1041,32 +1047,30 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
uart->ustcnt &= ~USTCNT_RXEN;
uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK);
- shutdown(info);
+ shutdown(info, tty);
rs_flush_buffer(tty);
tty_ldisc_flush(tty);
tty->closing = 0;
- info->event = 0;
- info->tty = NULL;
+ tty_port_tty_set(&info->tport, NULL);
#warning "This is not and has never been valid so fix it"
#if 0
if (tty->ldisc.num != ldiscs[N_TTY].num) {
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
tty->ldisc = ldiscs[N_TTY];
- tty->termios->c_line = N_TTY;
+ tty->termios.c_line = N_TTY;
if (tty->ldisc.open)
(tty->ldisc.open)(tty);
}
#endif
- if (info->blocked_open) {
- if (info->close_delay) {
- msleep_interruptible(jiffies_to_msecs(info->close_delay));
- }
- wake_up_interruptible(&info->open_wait);
+ if (port->blocked_open) {
+ if (port->close_delay)
+ msleep_interruptible(jiffies_to_msecs(port->close_delay));
+ wake_up_interruptible(&port->open_wait);
}
- info->flags &= ~(S_NORMAL_ACTIVE|S_CLOSING);
- wake_up_interruptible(&info->close_wait);
+ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+ wake_up_interruptible(&port->close_wait);
local_irq_restore(flags);
}
@@ -1081,107 +1085,14 @@ void rs_hangup(struct tty_struct *tty)
return;
rs_flush_buffer(tty);
- shutdown(info);
- info->event = 0;
- info->count = 0;
- info->flags &= ~S_NORMAL_ACTIVE;
- info->tty = NULL;
- wake_up_interruptible(&info->open_wait);
+ shutdown(info, tty);
+ info->tport.count = 0;
+ info->tport.flags &= ~ASYNC_NORMAL_ACTIVE;
+ tty_port_tty_set(&info->tport, NULL);
+ wake_up_interruptible(&info->tport.open_wait);
}
/*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
- struct m68k_serial *info)
-{
- DECLARE_WAITQUEUE(wait, current);
- int retval;
- int do_clocal = 0;
-
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (info->flags & S_CLOSING) {
- interruptible_sleep_on(&info->close_wait);
-#ifdef SERIAL_DO_RESTART
- if (info->flags & S_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- info->flags |= S_NORMAL_ACTIVE;
- return 0;
- }
-
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
- * rs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&info->open_wait, &wait);
-
- info->count--;
- info->blocked_open++;
- while (1) {
- local_irq_disable();
- m68k_rtsdtr(info, 1);
- local_irq_enable();
- current->state = TASK_INTERRUPTIBLE;
- if (tty_hung_up_p(filp) ||
- !(info->flags & S_INITIALIZED)) {
-#ifdef SERIAL_DO_RESTART
- if (info->flags & S_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
- if (!(info->flags & S_CLOSING) && do_clocal)
- break;
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- tty_unlock();
- schedule();
- tty_lock();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- info->count++;
- info->blocked_open--;
-
- if (retval)
- return retval;
- info->flags |= S_NORMAL_ACTIVE;
- return 0;
-}
-
-/*
* This routine is called whenever a serial port is opened. It
* enables interrupts for a serial port, linking in its S structure into
* the IRQ chain. It also performs the serial-specific
@@ -1190,30 +1101,25 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
int rs_open(struct tty_struct *tty, struct file * filp)
{
struct m68k_serial *info;
- int retval, line;
-
- line = tty->index;
-
- if (line >= NR_PORTS || line < 0) /* we have exactly one */
- return -ENODEV;
+ int retval;
- info = &m68k_soft[line];
+ info = &m68k_soft[tty->index];
if (serial_paranoia_check(info, tty->name, "rs_open"))
return -ENODEV;
- info->count++;
+ info->tport.count++;
tty->driver_data = info;
- info->tty = tty;
+ tty_port_tty_set(&info->tport, tty);
/*
* Start up serial port
*/
- retval = startup(info);
+ retval = startup(info, tty);
if (retval)
return retval;
- return block_til_ready(tty, filp, info);
+ return tty_port_block_til_ready(&info->tport, tty, filp);
}
/* Finally, routines used to initialize the serial driver. */
@@ -1241,11 +1147,15 @@ static const struct tty_operations rs_ops = {
.set_ldisc = rs_set_ldisc,
};
+static const struct tty_port_operations rs_port_ops = {
+};
+
/* rs_init inits the driver */
static int __init
rs68328_init(void)
{
- int flags, i;
+ unsigned long flags;
+ int i;
struct m68k_serial *info;
serial_driver = alloc_tty_driver(NR_PORTS);
@@ -1268,30 +1178,18 @@ rs68328_init(void)
serial_driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(serial_driver, &rs_ops);
- if (tty_register_driver(serial_driver)) {
- put_tty_driver(serial_driver);
- printk(KERN_ERR "Couldn't register serial driver\n");
- return -ENOMEM;
- }
-
local_irq_save(flags);
for(i=0;i<NR_PORTS;i++) {
info = &m68k_soft[i];
+ tty_port_init(&info->tport);
+ info->tport.ops = &rs_port_ops;
info->magic = SERIAL_MAGIC;
info->port = (int) &uart_addr[i];
- info->tty = NULL;
info->irq = uart_irqs[i];
info->custom_divisor = 16;
- info->close_delay = 50;
- info->closing_wait = 3000;
info->x_char = 0;
- info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
- init_waitqueue_head(&info->open_wait);
- init_waitqueue_head(&info->close_wait);
info->line = i;
info->is_cons = 1; /* Means shortcuts work */
@@ -1309,8 +1207,19 @@ rs68328_init(void)
0,
"M68328_UART", info))
panic("Unable to attach 68328 serial interrupt\n");
+
+ tty_port_link_device(&info->tport, serial_driver, i);
}
local_irq_restore(flags);
+
+ if (tty_register_driver(serial_driver)) {
+ put_tty_driver(serial_driver);
+ for (i = 0; i < NR_PORTS; i++)
+ tty_port_destroy(&m68k_soft[i].tport);
+ printk(KERN_ERR "Couldn't register serial driver\n");
+ return -ENOMEM;
+ }
+
return 0;
}
diff --git a/drivers/tty/serial/68328serial.h b/drivers/tty/serial/68328serial.h
deleted file mode 100644
index 3d2faabd766..00000000000
--- a/drivers/tty/serial/68328serial.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/* 68328serial.h: Definitions for the mc68328 serial driver.
- *
- * Copyright (C) 1995 David S. Miller <davem@caip.rutgers.edu>
- * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
- * Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@uclinux.org>
- * Copyright (C) 1999 Vladimir Gurevich <vgurevic@cisco.com>
- *
- * VZ Support/Fixes Evan Stawnyczy <e@lineo.ca>
- */
-
-#ifndef _MC683XX_SERIAL_H
-#define _MC683XX_SERIAL_H
-
-
-struct serial_struct {
- int type;
- int line;
- int port;
- int irq;
- int flags;
- int xmit_fifo_size;
- int custom_divisor;
- int baud_base;
- unsigned short close_delay;
- char reserved_char[2];
- int hub6; /* FIXME: We don't have AT&T Hub6 boards! */
- unsigned short closing_wait; /* time to wait before closing */
- unsigned short closing_wait2; /* no longer used... */
- int reserved[4];
-};
-
-/*
- * For the close wait times, 0 means wait forever for serial port to
- * flush its output. 65535 means don't wait at all.
- */
-#define S_CLOSING_WAIT_INF 0
-#define S_CLOSING_WAIT_NONE 65535
-
-/*
- * Definitions for S_struct (and serial_struct) flags field
- */
-#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
- on the callout port */
-#define S_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
-#define S_SAK 0x0004 /* Secure Attention Key (Orange book) */
-#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define S_SPD_MASK 0x0030
-#define S_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
-
-#define S_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
-#define S_SPD_CUST 0x0030 /* Use user-specified divisor */
-
-#define S_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
-#define S_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
-#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define S_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
-#define S_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
-
-#define S_FLAGS 0x0FFF /* Possible legal S flags */
-#define S_USR_MASK 0x0430 /* Legal flags that non-privileged
- * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define S_INITIALIZED 0x80000000 /* Serial port was initialized */
-#define S_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
-#define S_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
-#define S_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
-#define S_CLOSING 0x08000000 /* Serial port is closing */
-#define S_CTS_FLOW 0x04000000 /* Do CTS flow control */
-#define S_CHECK_CD 0x02000000 /* i.e., CLOCAL */
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-
-/*
- * I believe this is the optimal setting that reduces the number of interrupts.
- * At high speeds the output might become a little "bursted" (use USTCNT_TXHE
- * if that bothers you), but in most cases it will not, since we try to
- * transmit characters every time rs_interrupt is called. Thus, quite often
- * you'll see that a receive interrupt occures before the transmit one.
- * -- Vladimir Gurevich
- */
-#define USTCNT_TX_INTR_MASK (USTCNT_TXEE)
-
-/*
- * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special
- * "Old data interrupt" which occures whenever the data stay in the FIFO
- * longer than 30 bits time. This allows us to use FIFO without compromising
- * latency. '328 does not have this feature and without the real 328-based
- * board I would assume that RXRE is the safest setting.
- *
- * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of
- * interrupts. RXFE (receive queue full) causes the system to lose data
- * at least at 115200 baud
- *
- * If your board is busy doing other stuff, you might consider to use
- * RXRE (data ready intrrupt) instead.
- *
- * The other option is to make these INTR masks run-time configurable, so
- * that people can dynamically adapt them according to the current usage.
- * -- Vladimir Gurevich
- */
-
-/* (es) */
-#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328)
-#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN)
-#elif defined(CONFIG_M68328)
-#define USTCNT_RX_INTR_MASK (USTCNT_RXRE)
-#else
-#error Please, define the Rx interrupt events for your CPU
-#endif
-/* (/es) */
-
-/*
- * This is our internal structure for each serial port's state.
- *
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-struct m68k_serial {
- char soft_carrier; /* Use soft carrier on this channel */
- char break_abort; /* Is serial console in, so process brk/abrt */
- char is_cons; /* Is this our console. */
-
- /* We need to know the current clock divisor
- * to read the bps rate the chip has currently
- * loaded.
- */
- unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
- int baud;
- int magic;
- int baud_base;
- int port;
- int irq;
- int flags; /* defined in tty.h */
- int type; /* UART type */
- struct tty_struct *tty;
- int read_status_mask;
- int ignore_status_mask;
- int timeout;
- int xmit_fifo_size;
- int custom_divisor;
- int x_char; /* xon/xoff character */
- int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
- unsigned long event;
- unsigned long last_active;
- int line;
- int count; /* # of fd on device */
- int blocked_open; /* # of blocked opens */
- unsigned char *xmit_buf;
- int xmit_head;
- int xmit_tail;
- int xmit_cnt;
- wait_queue_head_t open_wait;
- wait_queue_head_t close_wait;
-};
-
-
-#define SERIAL_MAGIC 0x5301
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP 0
-
-/*
- * Define the number of ports supported and their irqs.
- */
-#define NR_PORTS 1
-#define UART_IRQ_DEFNS {UART_IRQ_NUM}
-
-#endif /* __KERNEL__ */
-#endif /* !(_MC683XX_SERIAL_H) */
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index ae027be57e2..1ebf8538b4f 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -12,31 +12,34 @@
*/
#include <linux/serial_8250.h>
+#include <linux/dmaengine.h>
-struct uart_8250_port {
- struct uart_port port;
- struct timer_list timer; /* "no irq" timer */
- struct list_head list; /* ports on this IRQ */
- unsigned short capabilities; /* port capabilities */
- unsigned short bugs; /* port bugs */
- unsigned int tx_loadsz; /* transmit fifo load size */
- unsigned char acr;
- unsigned char ier;
- unsigned char lcr;
- unsigned char mcr;
- unsigned char mcr_mask; /* mask of user bits */
- unsigned char mcr_force; /* mask of forced bits */
- unsigned char cur_iotype; /* Running I/O type */
-
- /*
- * Some bits in registers are cleared on a read, so they must
- * be saved whenever the register is read but the bits will not
- * be immediately processed.
- */
-#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
- unsigned char lsr_saved_flags;
-#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
- unsigned char msr_saved_flags;
+struct uart_8250_dma {
+ dma_filter_fn fn;
+ void *rx_param;
+ void *tx_param;
+
+ int rx_chan_id;
+ int tx_chan_id;
+
+ struct dma_slave_config rxconf;
+ struct dma_slave_config txconf;
+
+ struct dma_chan *rxchan;
+ struct dma_chan *txchan;
+
+ dma_addr_t rx_addr;
+ dma_addr_t tx_addr;
+
+ dma_cookie_t rx_cookie;
+ dma_cookie_t tx_cookie;
+
+ void *rx_buf;
+
+ size_t rx_size;
+ size_t tx_size;
+
+ unsigned char tx_running:1;
};
struct old_serial_port {
@@ -52,9 +55,6 @@ struct old_serial_port {
unsigned long irqflags;
};
-/*
- * This replaces serial_uart_config in include/linux/serial.h
- */
struct serial8250_config {
const char *name;
unsigned short fifo_size;
@@ -69,11 +69,13 @@ struct serial8250_config {
#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */
+#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */
#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */
#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */
#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */
#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */
+#define UART_BUG_PARITY (1 << 4) /* UART mishandles parity if FIFO enabled */
#define PROBE_RSA (1 << 0)
#define PROBE_ANY (~0)
@@ -86,6 +88,28 @@ struct serial8250_config {
#define SERIAL8250_SHARE_IRQS 0
#endif
+static inline int serial_in(struct uart_8250_port *up, int offset)
+{
+ return up->port.serial_in(&up->port, offset);
+}
+
+static inline void serial_out(struct uart_8250_port *up, int offset, int value)
+{
+ up->port.serial_out(&up->port, offset, value);
+}
+
+void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p);
+
+static inline int serial_dl_read(struct uart_8250_port *up)
+{
+ return up->dl_read(up);
+}
+
+static inline void serial_dl_write(struct uart_8250_port *up, int value)
+{
+ up->dl_write(up, value);
+}
+
#if defined(__alpha__) && !defined(CONFIG_PCI)
/*
* Digital did something really horribly wrong with the OUT1 and OUT2
@@ -93,13 +117,72 @@ struct serial8250_config {
* is cleared, the machine locks up with endless interrupts.
*/
#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1)
-#elif defined(CONFIG_SBC8560)
-/*
- * WindRiver did something similarly broken on their SBC8560 board. The
- * UART tristates its IRQ output while OUT2 is clear, but they pulled
- * the interrupt line _up_ instead of down, so if we register the IRQ
- * while the UART is in that state, we die in an IRQ storm. */
-#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2)
#else
#define ALPHA_KLUDGE_MCR 0
#endif
+
+#ifdef CONFIG_SERIAL_8250_PNP
+int serial8250_pnp_init(void);
+void serial8250_pnp_exit(void);
+#else
+static inline int serial8250_pnp_init(void) { return 0; }
+static inline void serial8250_pnp_exit(void) { }
+#endif
+
+#ifdef CONFIG_ARCH_OMAP1
+static inline int is_omap1_8250(struct uart_8250_port *pt)
+{
+ int res;
+
+ switch (pt->port.mapbase) {
+ case OMAP1_UART1_BASE:
+ case OMAP1_UART2_BASE:
+ case OMAP1_UART3_BASE:
+ res = 1;
+ break;
+ default:
+ res = 0;
+ break;
+ }
+
+ return res;
+}
+
+static inline int is_omap1510_8250(struct uart_8250_port *pt)
+{
+ if (!cpu_is_omap1510())
+ return 0;
+
+ return is_omap1_8250(pt);
+}
+#else
+static inline int is_omap1_8250(struct uart_8250_port *pt)
+{
+ return 0;
+}
+static inline int is_omap1510_8250(struct uart_8250_port *pt)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_SERIAL_8250_DMA
+extern int serial8250_tx_dma(struct uart_8250_port *);
+extern int serial8250_rx_dma(struct uart_8250_port *, unsigned int iir);
+extern int serial8250_request_dma(struct uart_8250_port *);
+extern void serial8250_release_dma(struct uart_8250_port *);
+#else
+static inline int serial8250_tx_dma(struct uart_8250_port *p)
+{
+ return -1;
+}
+static inline int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
+{
+ return -1;
+}
+static inline int serial8250_request_dma(struct uart_8250_port *p)
+{
+ return -1;
+}
+static inline void serial8250_release_dma(struct uart_8250_port *p) { }
+#endif
diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c
index b0ce8c56f1a..549aa07c0d2 100644
--- a/drivers/tty/serial/8250/8250_acorn.c
+++ b/drivers/tty/serial/8250/8250_acorn.c
@@ -38,12 +38,12 @@ struct serial_card_info {
void __iomem *vaddr;
};
-static int __devinit
+static int
serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct serial_card_info *info;
struct serial_card_type *type = id->data;
- struct uart_port port;
+ struct uart_8250_port uart;
unsigned long bus_addr;
unsigned int i;
@@ -62,25 +62,25 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
ecard_set_drvdata(ec, info);
- memset(&port, 0, sizeof(struct uart_port));
- port.irq = ec->irq;
- port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
- port.uartclk = type->uartclk;
- port.iotype = UPIO_MEM;
- port.regshift = 2;
- port.dev = &ec->dev;
+ memset(&uart, 0, sizeof(struct uart_8250_port));
+ uart.port.irq = ec->irq;
+ uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+ uart.port.uartclk = type->uartclk;
+ uart.port.iotype = UPIO_MEM;
+ uart.port.regshift = 2;
+ uart.port.dev = &ec->dev;
for (i = 0; i < info->num_ports; i ++) {
- port.membase = info->vaddr + type->offset[i];
- port.mapbase = bus_addr + type->offset[i];
+ uart.port.membase = info->vaddr + type->offset[i];
+ uart.port.mapbase = bus_addr + type->offset[i];
- info->ports[i] = serial8250_register_port(&port);
+ info->ports[i] = serial8250_register_8250_port(&uart);
}
return 0;
}
-static void __devexit serial_card_remove(struct expansion_card *ec)
+static void serial_card_remove(struct expansion_card *ec)
{
struct serial_card_info *info = ecard_get_drvdata(ec);
int i;
@@ -116,7 +116,7 @@ static const struct ecard_id serial_cids[] = {
static struct ecard_driver serial_card_driver = {
.probe = serial_card_probe,
- .remove = __devexit_p(serial_card_remove),
+ .remove = serial_card_remove,
.id_table = serial_cids,
.drv = {
.name = "8250_acorn",
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250_core.c
index 9b7336fcfbb..7a91c6d1eb7 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -38,16 +38,15 @@
#include <linux/nmi.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#ifdef CONFIG_SPARC
+#include <linux/sunserialcore.h>
+#endif
#include <asm/io.h>
#include <asm/irq.h>
#include "8250.h"
-#ifdef CONFIG_SPARC
-#include "../suncore.h"
-#endif
-
/*
* Configuration:
* share_irqs - whether we pass IRQF_SHARED to request_irq(). This option
@@ -86,13 +85,6 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-/*
- * We default to IRQ0 for the "no irq" hack. Some
- * machine types want others as well - they're free
- * to redefine this in their header file.
- */
-#define is_real_interrupt(irq) ((irq) != 0)
-
#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
#define CONFIG_SERIAL_DETECT_IRQ 1
#endif
@@ -247,13 +239,6 @@ static const struct serial8250_config uart_config[] = {
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE,
},
- [PORT_RM9000] = {
- .name = "RM9000",
- .fifo_size = 16,
- .tx_loadsz = 16,
- .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
- .flags = UART_CAP_FIFO,
- },
[PORT_OCTEON] = {
.name = "OCTEON",
.fifo_size = 64,
@@ -288,13 +273,74 @@ static const struct serial8250_config uart_config[] = {
.fifo_size = 64,
.tx_loadsz = 64,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
- .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR |
+ UART_CAP_SLEEP,
+ },
+ [PORT_XR17V35X] = {
+ .name = "XR17V35X",
+ .fifo_size = 256,
+ .tx_loadsz = 256,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11 |
+ UART_FCR_T_TRIG_11,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR |
+ UART_CAP_SLEEP,
+ },
+ [PORT_LPC3220] = {
+ .name = "LPC3220",
+ .fifo_size = 64,
+ .tx_loadsz = 32,
+ .fcr = UART_FCR_DMA_SELECT | UART_FCR_ENABLE_FIFO |
+ UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00,
+ .flags = UART_CAP_FIFO,
+ },
+ [PORT_BRCM_TRUMANAGE] = {
+ .name = "TruManage",
+ .fifo_size = 1,
+ .tx_loadsz = 1024,
+ .flags = UART_CAP_HFIFO,
+ },
+ [PORT_8250_CIR] = {
+ .name = "CIR port"
+ },
+ [PORT_ALTR_16550_F32] = {
+ .name = "Altera 16550 FIFO32",
+ .fifo_size = 32,
+ .tx_loadsz = 32,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
+ [PORT_ALTR_16550_F64] = {
+ .name = "Altera 16550 FIFO64",
+ .fifo_size = 64,
+ .tx_loadsz = 64,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
+ [PORT_ALTR_16550_F128] = {
+ .name = "Altera 16550 FIFO128",
+ .fifo_size = 128,
+ .tx_loadsz = 128,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
},
};
-#if defined(CONFIG_MIPS_ALCHEMY)
+/* Uart divisor latch read */
+static int default_serial_dl_read(struct uart_8250_port *up)
+{
+ return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8;
+}
-/* Au1x00 UART hardware has a weird register layout */
+/* Uart divisor latch write */
+static void default_serial_dl_write(struct uart_8250_port *up, int value)
+{
+ serial_out(up, UART_DLL, value & 0xff);
+ serial_out(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X)
+
+/* Au1x00/RT288x UART hardware has a weird register layout */
static const u8 au_io_in_map[] = {
[UART_RX] = 0,
[UART_IER] = 2,
@@ -313,135 +359,92 @@ static const u8 au_io_out_map[] = {
[UART_MCR] = 6,
};
-/* sane hardware needs no mapping */
-static inline int map_8250_in_reg(struct uart_port *p, int offset)
+static unsigned int au_serial_in(struct uart_port *p, int offset)
{
- if (p->iotype != UPIO_AU)
- return offset;
- return au_io_in_map[offset];
+ offset = au_io_in_map[offset] << p->regshift;
+ return __raw_readl(p->membase + offset);
}
-static inline int map_8250_out_reg(struct uart_port *p, int offset)
+static void au_serial_out(struct uart_port *p, int offset, int value)
{
- if (p->iotype != UPIO_AU)
- return offset;
- return au_io_out_map[offset];
+ offset = au_io_out_map[offset] << p->regshift;
+ __raw_writel(value, p->membase + offset);
}
-#elif defined(CONFIG_SERIAL_8250_RM9K)
-
-static const u8
- regmap_in[8] = {
- [UART_RX] = 0x00,
- [UART_IER] = 0x0c,
- [UART_IIR] = 0x14,
- [UART_LCR] = 0x1c,
- [UART_MCR] = 0x20,
- [UART_LSR] = 0x24,
- [UART_MSR] = 0x28,
- [UART_SCR] = 0x2c
- },
- regmap_out[8] = {
- [UART_TX] = 0x04,
- [UART_IER] = 0x0c,
- [UART_FCR] = 0x18,
- [UART_LCR] = 0x1c,
- [UART_MCR] = 0x20,
- [UART_LSR] = 0x24,
- [UART_MSR] = 0x28,
- [UART_SCR] = 0x2c
- };
-
-static inline int map_8250_in_reg(struct uart_port *p, int offset)
+/* Au1x00 haven't got a standard divisor latch */
+static int au_serial_dl_read(struct uart_8250_port *up)
{
- if (p->iotype != UPIO_RM9000)
- return offset;
- return regmap_in[offset];
+ return __raw_readl(up->port.membase + 0x28);
}
-static inline int map_8250_out_reg(struct uart_port *p, int offset)
+static void au_serial_dl_write(struct uart_8250_port *up, int value)
{
- if (p->iotype != UPIO_RM9000)
- return offset;
- return regmap_out[offset];
+ __raw_writel(value, up->port.membase + 0x28);
}
-#else
-
-/* sane hardware needs no mapping */
-#define map_8250_in_reg(up, offset) (offset)
-#define map_8250_out_reg(up, offset) (offset)
-
#endif
static unsigned int hub6_serial_in(struct uart_port *p, int offset)
{
- offset = map_8250_in_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
outb(p->hub6 - 1 + offset, p->iobase);
return inb(p->iobase + 1);
}
static void hub6_serial_out(struct uart_port *p, int offset, int value)
{
- offset = map_8250_out_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
outb(p->hub6 - 1 + offset, p->iobase);
outb(value, p->iobase + 1);
}
static unsigned int mem_serial_in(struct uart_port *p, int offset)
{
- offset = map_8250_in_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
return readb(p->membase + offset);
}
static void mem_serial_out(struct uart_port *p, int offset, int value)
{
- offset = map_8250_out_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
writeb(value, p->membase + offset);
}
static void mem32_serial_out(struct uart_port *p, int offset, int value)
{
- offset = map_8250_out_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
writel(value, p->membase + offset);
}
static unsigned int mem32_serial_in(struct uart_port *p, int offset)
{
- offset = map_8250_in_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
return readl(p->membase + offset);
}
-static unsigned int au_serial_in(struct uart_port *p, int offset)
-{
- offset = map_8250_in_reg(p, offset) << p->regshift;
- return __raw_readl(p->membase + offset);
-}
-
-static void au_serial_out(struct uart_port *p, int offset, int value)
-{
- offset = map_8250_out_reg(p, offset) << p->regshift;
- __raw_writel(value, p->membase + offset);
-}
-
static unsigned int io_serial_in(struct uart_port *p, int offset)
{
- offset = map_8250_in_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
return inb(p->iobase + offset);
}
static void io_serial_out(struct uart_port *p, int offset, int value)
{
- offset = map_8250_out_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
outb(value, p->iobase + offset);
}
static int serial8250_default_handle_irq(struct uart_port *port);
+static int exar_handle_irq(struct uart_port *port);
static void set_io_from_upio(struct uart_port *p)
{
struct uart_8250_port *up =
container_of(p, struct uart_8250_port, port);
+
+ up->dl_read = default_serial_dl_read;
+ up->dl_write = default_serial_dl_write;
+
switch (p->iotype) {
case UPIO_HUB6:
p->serial_in = hub6_serial_in;
@@ -453,16 +456,19 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = mem_serial_out;
break;
- case UPIO_RM9000:
case UPIO_MEM32:
p->serial_in = mem32_serial_in;
p->serial_out = mem32_serial_out;
break;
+#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X)
case UPIO_AU:
p->serial_in = au_serial_in;
p->serial_out = au_serial_out;
+ up->dl_read = au_serial_dl_read;
+ up->dl_write = au_serial_dl_write;
break;
+#endif
default:
p->serial_in = io_serial_in;
@@ -475,9 +481,8 @@ static void set_io_from_upio(struct uart_port *p)
}
static void
-serial_out_sync(struct uart_8250_port *up, int offset, int value)
+serial_port_out_sync(struct uart_port *p, int offset, int value)
{
- struct uart_port *p = &up->port;
switch (p->iotype) {
case UPIO_MEM:
case UPIO_MEM32:
@@ -490,72 +495,6 @@ serial_out_sync(struct uart_8250_port *up, int offset, int value)
}
}
-#define serial_in(up, offset) \
- (up->port.serial_in(&(up)->port, (offset)))
-#define serial_out(up, offset, value) \
- (up->port.serial_out(&(up)->port, (offset), (value)))
-/*
- * We used to support using pause I/O for certain machines. We
- * haven't supported this for a while, but just in case it's badly
- * needed for certain old 386 machines, I've left these #define's
- * in....
- */
-#define serial_inp(up, offset) serial_in(up, offset)
-#define serial_outp(up, offset, value) serial_out(up, offset, value)
-
-/* Uart divisor latch read */
-static inline int _serial_dl_read(struct uart_8250_port *up)
-{
- return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
-}
-
-/* Uart divisor latch write */
-static inline void _serial_dl_write(struct uart_8250_port *up, int value)
-{
- serial_outp(up, UART_DLL, value & 0xff);
- serial_outp(up, UART_DLM, value >> 8 & 0xff);
-}
-
-#if defined(CONFIG_MIPS_ALCHEMY)
-/* Au1x00 haven't got a standard divisor latch */
-static int serial_dl_read(struct uart_8250_port *up)
-{
- if (up->port.iotype == UPIO_AU)
- return __raw_readl(up->port.membase + 0x28);
- else
- return _serial_dl_read(up);
-}
-
-static void serial_dl_write(struct uart_8250_port *up, int value)
-{
- if (up->port.iotype == UPIO_AU)
- __raw_writel(value, up->port.membase + 0x28);
- else
- _serial_dl_write(up, value);
-}
-#elif defined(CONFIG_SERIAL_8250_RM9K)
-static int serial_dl_read(struct uart_8250_port *up)
-{
- return (up->port.iotype == UPIO_RM9000) ?
- (((__raw_readl(up->port.membase + 0x10) << 8) |
- (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) :
- _serial_dl_read(up);
-}
-
-static void serial_dl_write(struct uart_8250_port *up, int value)
-{
- if (up->port.iotype == UPIO_RM9000) {
- __raw_writel(value, up->port.membase + 0x08);
- __raw_writel(value >> 8, up->port.membase + 0x10);
- } else {
- _serial_dl_write(up, value);
- }
-}
-#else
-#define serial_dl_read(up) _serial_dl_read(up)
-#define serial_dl_write(up, value) _serial_dl_write(up, value)
-#endif
-
/*
* For the 16C950
*/
@@ -583,13 +522,23 @@ static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
static void serial8250_clear_fifos(struct uart_8250_port *p)
{
if (p->capabilities & UART_CAP_FIFO) {
- serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
- serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO |
+ serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
- serial_outp(p, UART_FCR, 0);
+ serial_out(p, UART_FCR, 0);
}
}
+void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p)
+{
+ unsigned char fcr;
+
+ serial8250_clear_fifos(p);
+ fcr = uart_config[p->port.type].fcr;
+ serial_out(p, UART_FCR, fcr);
+}
+EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos);
+
/*
* IER sleep support. UARTs which have EFRs need the "extended
* capability" bit enabled. Note that on XR16C850s, we need to
@@ -597,17 +546,30 @@ static void serial8250_clear_fifos(struct uart_8250_port *p)
*/
static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
{
+ /*
+ * Exar UARTs have a SLEEP register that enables or disables
+ * each UART to enter sleep mode separately. On the XR17V35x the
+ * register is accessible to each UART at the UART_EXAR_SLEEP
+ * offset but the UART channel may only write to the corresponding
+ * bit.
+ */
+ if ((p->port.type == PORT_XR17V35X) ||
+ (p->port.type == PORT_XR17D15X)) {
+ serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
+ return;
+ }
+
if (p->capabilities & UART_CAP_SLEEP) {
if (p->capabilities & UART_CAP_EFR) {
- serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_outp(p, UART_EFR, UART_EFR_ECB);
- serial_outp(p, UART_LCR, 0);
+ serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(p, UART_EFR, UART_EFR_ECB);
+ serial_out(p, UART_LCR, 0);
}
- serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0);
+ serial_out(p, UART_IER, sleep ? UART_IERX_SLEEP : 0);
if (p->capabilities & UART_CAP_EFR) {
- serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_outp(p, UART_EFR, 0);
- serial_outp(p, UART_LCR, 0);
+ serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(p, UART_EFR, 0);
+ serial_out(p, UART_LCR, 0);
}
}
}
@@ -622,12 +584,12 @@ static int __enable_rsa(struct uart_8250_port *up)
unsigned char mode;
int result;
- mode = serial_inp(up, UART_RSA_MSR);
+ mode = serial_in(up, UART_RSA_MSR);
result = mode & UART_RSA_MSR_FIFO;
if (!result) {
- serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
- mode = serial_inp(up, UART_RSA_MSR);
+ serial_out(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
+ mode = serial_in(up, UART_RSA_MSR);
result = mode & UART_RSA_MSR_FIFO;
}
@@ -646,7 +608,7 @@ static void enable_rsa(struct uart_8250_port *up)
spin_unlock_irq(&up->port.lock);
}
if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
- serial_outp(up, UART_RSA_FRR, 0);
+ serial_out(up, UART_RSA_FRR, 0);
}
}
@@ -665,12 +627,12 @@ static void disable_rsa(struct uart_8250_port *up)
up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
spin_lock_irq(&up->port.lock);
- mode = serial_inp(up, UART_RSA_MSR);
+ mode = serial_in(up, UART_RSA_MSR);
result = !(mode & UART_RSA_MSR_FIFO);
if (!result) {
- serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
- mode = serial_inp(up, UART_RSA_MSR);
+ serial_out(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
+ mode = serial_in(up, UART_RSA_MSR);
result = !(mode & UART_RSA_MSR_FIFO);
}
@@ -691,28 +653,28 @@ static int size_fifo(struct uart_8250_port *up)
unsigned short old_dl;
int count;
- old_lcr = serial_inp(up, UART_LCR);
- serial_outp(up, UART_LCR, 0);
- old_fcr = serial_inp(up, UART_FCR);
- old_mcr = serial_inp(up, UART_MCR);
- serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+ old_lcr = serial_in(up, UART_LCR);
+ serial_out(up, UART_LCR, 0);
+ old_fcr = serial_in(up, UART_FCR);
+ old_mcr = serial_in(up, UART_MCR);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
- serial_outp(up, UART_MCR, UART_MCR_LOOP);
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_out(up, UART_MCR, UART_MCR_LOOP);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
old_dl = serial_dl_read(up);
serial_dl_write(up, 0x0001);
- serial_outp(up, UART_LCR, 0x03);
+ serial_out(up, UART_LCR, 0x03);
for (count = 0; count < 256; count++)
- serial_outp(up, UART_TX, count);
+ serial_out(up, UART_TX, count);
mdelay(20);/* FIXME - schedule_timeout */
- for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) &&
+ for (count = 0; (serial_in(up, UART_LSR) & UART_LSR_DR) &&
(count < 256); count++)
- serial_inp(up, UART_RX);
- serial_outp(up, UART_FCR, old_fcr);
- serial_outp(up, UART_MCR, old_mcr);
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_in(up, UART_RX);
+ serial_out(up, UART_FCR, old_fcr);
+ serial_out(up, UART_MCR, old_mcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
serial_dl_write(up, old_dl);
- serial_outp(up, UART_LCR, old_lcr);
+ serial_out(up, UART_LCR, old_lcr);
return count;
}
@@ -727,20 +689,20 @@ static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p)
unsigned char old_dll, old_dlm, old_lcr;
unsigned int id;
- old_lcr = serial_inp(p, UART_LCR);
- serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_A);
+ old_lcr = serial_in(p, UART_LCR);
+ serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A);
- old_dll = serial_inp(p, UART_DLL);
- old_dlm = serial_inp(p, UART_DLM);
+ old_dll = serial_in(p, UART_DLL);
+ old_dlm = serial_in(p, UART_DLM);
- serial_outp(p, UART_DLL, 0);
- serial_outp(p, UART_DLM, 0);
+ serial_out(p, UART_DLL, 0);
+ serial_out(p, UART_DLM, 0);
- id = serial_inp(p, UART_DLL) | serial_inp(p, UART_DLM) << 8;
+ id = serial_in(p, UART_DLL) | serial_in(p, UART_DLM) << 8;
- serial_outp(p, UART_DLL, old_dll);
- serial_outp(p, UART_DLM, old_dlm);
- serial_outp(p, UART_LCR, old_lcr);
+ serial_out(p, UART_DLL, old_dll);
+ serial_out(p, UART_DLM, old_dlm);
+ serial_out(p, UART_LCR, old_lcr);
return id;
}
@@ -850,11 +812,11 @@ static void autoconfig_8250(struct uart_8250_port *up)
up->port.type = PORT_8250;
scratch = serial_in(up, UART_SCR);
- serial_outp(up, UART_SCR, 0xa5);
+ serial_out(up, UART_SCR, 0xa5);
status1 = serial_in(up, UART_SCR);
- serial_outp(up, UART_SCR, 0x5a);
+ serial_out(up, UART_SCR, 0x5a);
status2 = serial_in(up, UART_SCR);
- serial_outp(up, UART_SCR, scratch);
+ serial_out(up, UART_SCR, scratch);
if (status1 == 0xa5 && status2 == 0x5a)
up->port.type = PORT_16450;
@@ -885,7 +847,7 @@ static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
} else {
status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
status |= 0x10; /* 1.625 divisor for baud_base --> 921600 */
- serial_outp(up, 0x04, status);
+ serial_out(up, 0x04, status);
}
return 1;
}
@@ -905,12 +867,33 @@ static void autoconfig_16550a(struct uart_8250_port *up)
up->capabilities |= UART_CAP_FIFO;
/*
+ * XR17V35x UARTs have an extra divisor register, DLD
+ * that gets enabled with when DLAB is set which will
+ * cause the device to incorrectly match and assign
+ * port type to PORT_16650. The EFR for this UART is
+ * found at offset 0x09. Instead check the Deice ID (DVID)
+ * register for a 2, 4 or 8 port UART.
+ */
+ if (up->port.flags & UPF_EXAR_EFR) {
+ status1 = serial_in(up, UART_EXAR_DVID);
+ if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) {
+ DEBUG_AUTOCONF("Exar XR17V35x ");
+ up->port.type = PORT_XR17V35X;
+ up->capabilities |= UART_CAP_AFE | UART_CAP_EFR |
+ UART_CAP_SLEEP;
+
+ return;
+ }
+
+ }
+
+ /*
* Check for presence of the EFR when DLAB is set.
* Only ST16C650V1 UARTs pass this test.
*/
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
if (serial_in(up, UART_EFR) == 0) {
- serial_outp(up, UART_EFR, 0xA8);
+ serial_out(up, UART_EFR, 0xA8);
if (serial_in(up, UART_EFR) != 0) {
DEBUG_AUTOCONF("EFRv1 ");
up->port.type = PORT_16650;
@@ -918,7 +901,7 @@ static void autoconfig_16550a(struct uart_8250_port *up)
} else {
DEBUG_AUTOCONF("Motorola 8xxx DUART ");
}
- serial_outp(up, UART_EFR, 0);
+ serial_out(up, UART_EFR, 0);
return;
}
@@ -926,7 +909,7 @@ static void autoconfig_16550a(struct uart_8250_port *up)
* Maybe it requires 0xbf to be written to the LCR.
* (other ST16C650V2 UARTs, TI16C752A, etc)
*/
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) {
DEBUG_AUTOCONF("EFRv2 ");
autoconfig_has_efr(up);
@@ -940,23 +923,23 @@ static void autoconfig_16550a(struct uart_8250_port *up)
* switch back to bank 2, read it from EXCR1 again and check
* it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2
*/
- serial_outp(up, UART_LCR, 0);
+ serial_out(up, UART_LCR, 0);
status1 = serial_in(up, UART_MCR);
- serial_outp(up, UART_LCR, 0xE0);
+ serial_out(up, UART_LCR, 0xE0);
status2 = serial_in(up, 0x02); /* EXCR1 */
if (!((status2 ^ status1) & UART_MCR_LOOP)) {
- serial_outp(up, UART_LCR, 0);
- serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP);
- serial_outp(up, UART_LCR, 0xE0);
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_MCR, status1 ^ UART_MCR_LOOP);
+ serial_out(up, UART_LCR, 0xE0);
status2 = serial_in(up, 0x02); /* EXCR1 */
- serial_outp(up, UART_LCR, 0);
- serial_outp(up, UART_MCR, status1);
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_MCR, status1);
if ((status2 ^ status1) & UART_MCR_LOOP) {
unsigned short quot;
- serial_outp(up, UART_LCR, 0xE0);
+ serial_out(up, UART_LCR, 0xE0);
quot = serial_dl_read(up);
quot <<= 3;
@@ -964,7 +947,7 @@ static void autoconfig_16550a(struct uart_8250_port *up)
if (ns16550a_goto_highspeed(up))
serial_dl_write(up, quot);
- serial_outp(up, UART_LCR, 0);
+ serial_out(up, UART_LCR, 0);
up->port.uartclk = 921600*16;
up->port.type = PORT_NS16550A;
@@ -979,15 +962,15 @@ static void autoconfig_16550a(struct uart_8250_port *up)
* Try setting it with and without DLAB set. Cheap clones
* set bit 5 without DLAB set.
*/
- serial_outp(up, UART_LCR, 0);
- serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
status1 = serial_in(up, UART_IIR) >> 5;
- serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A);
- serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
status2 = serial_in(up, UART_IIR) >> 5;
- serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
- serial_outp(up, UART_LCR, 0);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_out(up, UART_LCR, 0);
DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
@@ -1006,13 +989,13 @@ static void autoconfig_16550a(struct uart_8250_port *up)
* already a 1 and maybe locked there before we even start start.
*/
iersave = serial_in(up, UART_IER);
- serial_outp(up, UART_IER, iersave & ~UART_IER_UUE);
+ serial_out(up, UART_IER, iersave & ~UART_IER_UUE);
if (!(serial_in(up, UART_IER) & UART_IER_UUE)) {
/*
* OK it's in a known zero state, try writing and reading
* without disturbing the current state of the other bits.
*/
- serial_outp(up, UART_IER, iersave | UART_IER_UUE);
+ serial_out(up, UART_IER, iersave | UART_IER_UUE);
if (serial_in(up, UART_IER) & UART_IER_UUE) {
/*
* It's an Xscale.
@@ -1030,14 +1013,18 @@ static void autoconfig_16550a(struct uart_8250_port *up)
*/
DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
}
- serial_outp(up, UART_IER, iersave);
+ serial_out(up, UART_IER, iersave);
/*
* Exar uarts have EFR in a weird location
*/
if (up->port.flags & UPF_EXAR_EFR) {
+ DEBUG_AUTOCONF("Exar XR17D15x ");
up->port.type = PORT_XR17D15X;
- up->capabilities |= UART_CAP_AFE | UART_CAP_EFR;
+ up->capabilities |= UART_CAP_AFE | UART_CAP_EFR |
+ UART_CAP_SLEEP;
+
+ return;
}
/*
@@ -1061,24 +1048,26 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
{
unsigned char status1, scratch, scratch2, scratch3;
unsigned char save_lcr, save_mcr;
+ struct uart_port *port = &up->port;
unsigned long flags;
+ unsigned int old_capabilities;
- if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
+ if (!port->iobase && !port->mapbase && !port->membase)
return;
DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ",
- serial_index(&up->port), up->port.iobase, up->port.membase);
+ serial_index(port), port->iobase, port->membase);
/*
* We really do need global IRQs disabled here - we're going to
* be frobbing the chips IRQ enable register to see if it exists.
*/
- spin_lock_irqsave(&up->port.lock, flags);
+ spin_lock_irqsave(&port->lock, flags);
up->capabilities = 0;
up->bugs = 0;
- if (!(up->port.flags & UPF_BUGGY_UART)) {
+ if (!(port->flags & UPF_BUGGY_UART)) {
/*
* Do a simple existence test first; if we fail this,
* there's no point trying anything else.
@@ -1092,8 +1081,8 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
* Note: this is safe as long as MCR bit 4 is clear
* and the device is in "PC" mode.
*/
- scratch = serial_inp(up, UART_IER);
- serial_outp(up, UART_IER, 0);
+ scratch = serial_in(up, UART_IER);
+ serial_out(up, UART_IER, 0);
#ifdef __i386__
outb(0xff, 0x080);
#endif
@@ -1101,17 +1090,18 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
* Mask out IER[7:4] bits for test as some UARTs (e.g. TL
* 16C754B) allow only to modify them if an EFR bit is set.
*/
- scratch2 = serial_inp(up, UART_IER) & 0x0f;
- serial_outp(up, UART_IER, 0x0F);
+ scratch2 = serial_in(up, UART_IER) & 0x0f;
+ serial_out(up, UART_IER, 0x0F);
#ifdef __i386__
outb(0, 0x080);
#endif
- scratch3 = serial_inp(up, UART_IER) & 0x0f;
- serial_outp(up, UART_IER, scratch);
+ scratch3 = serial_in(up, UART_IER) & 0x0f;
+ serial_out(up, UART_IER, scratch);
if (scratch2 != 0 || scratch3 != 0x0F) {
/*
* We failed; there's nothing here
*/
+ spin_unlock_irqrestore(&port->lock, flags);
DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
scratch2, scratch3);
goto out;
@@ -1130,11 +1120,12 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
* manufacturer would be stupid enough to design a board
* that conflicts with COM 1-4 --- we hope!
*/
- if (!(up->port.flags & UPF_SKIP_TEST)) {
- serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
- status1 = serial_inp(up, UART_MSR) & 0xF0;
- serial_outp(up, UART_MCR, save_mcr);
+ if (!(port->flags & UPF_SKIP_TEST)) {
+ serial_out(up, UART_MCR, UART_MCR_LOOP | 0x0A);
+ status1 = serial_in(up, UART_MSR) & 0xF0;
+ serial_out(up, UART_MCR, save_mcr);
if (status1 != 0x90) {
+ spin_unlock_irqrestore(&port->lock, flags);
DEBUG_AUTOCONF("LOOP test failed (%02x) ",
status1);
goto out;
@@ -1150,24 +1141,22 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
* We also initialise the EFR (if any) to zero for later. The
* EFR occupies the same register location as the FCR and IIR.
*/
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_outp(up, UART_EFR, 0);
- serial_outp(up, UART_LCR, 0);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, 0);
+ serial_out(up, UART_LCR, 0);
- serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(up, UART_IIR) >> 6;
- DEBUG_AUTOCONF("iir=%d ", scratch);
-
switch (scratch) {
case 0:
autoconfig_8250(up);
break;
case 1:
- up->port.type = PORT_UNKNOWN;
+ port->type = PORT_UNKNOWN;
break;
case 2:
- up->port.type = PORT_16550;
+ port->type = PORT_16550;
break;
case 3:
autoconfig_16550a(up);
@@ -1178,102 +1167,104 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
/*
* Only probe for RSA ports if we got the region.
*/
- if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) {
+ if (port->type == PORT_16550A && probeflags & PROBE_RSA) {
int i;
for (i = 0 ; i < probe_rsa_count; ++i) {
- if (probe_rsa[i] == up->port.iobase &&
- __enable_rsa(up)) {
- up->port.type = PORT_RSA;
+ if (probe_rsa[i] == port->iobase && __enable_rsa(up)) {
+ port->type = PORT_RSA;
break;
}
}
}
#endif
- serial_outp(up, UART_LCR, save_lcr);
+ serial_out(up, UART_LCR, save_lcr);
- if (up->capabilities != uart_config[up->port.type].flags) {
- printk(KERN_WARNING
- "ttyS%d: detected caps %08x should be %08x\n",
- serial_index(&up->port), up->capabilities,
- uart_config[up->port.type].flags);
- }
+ port->fifosize = uart_config[up->port.type].fifo_size;
+ old_capabilities = up->capabilities;
+ up->capabilities = uart_config[port->type].flags;
+ up->tx_loadsz = uart_config[port->type].tx_loadsz;
- up->port.fifosize = uart_config[up->port.type].fifo_size;
- up->capabilities = uart_config[up->port.type].flags;
- up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
-
- if (up->port.type == PORT_UNKNOWN)
- goto out;
+ if (port->type == PORT_UNKNOWN)
+ goto out_lock;
/*
* Reset the UART.
*/
#ifdef CONFIG_SERIAL_8250_RSA
- if (up->port.type == PORT_RSA)
- serial_outp(up, UART_RSA_FRR, 0);
+ if (port->type == PORT_RSA)
+ serial_out(up, UART_RSA_FRR, 0);
#endif
- serial_outp(up, UART_MCR, save_mcr);
+ serial_out(up, UART_MCR, save_mcr);
serial8250_clear_fifos(up);
serial_in(up, UART_RX);
if (up->capabilities & UART_CAP_UUE)
- serial_outp(up, UART_IER, UART_IER_UUE);
+ serial_out(up, UART_IER, UART_IER_UUE);
else
- serial_outp(up, UART_IER, 0);
+ serial_out(up, UART_IER, 0);
- out:
- spin_unlock_irqrestore(&up->port.lock, flags);
- DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name);
+out_lock:
+ spin_unlock_irqrestore(&port->lock, flags);
+ if (up->capabilities != old_capabilities) {
+ printk(KERN_WARNING
+ "ttyS%d: detected caps %08x should be %08x\n",
+ serial_index(port), old_capabilities,
+ up->capabilities);
+ }
+out:
+ DEBUG_AUTOCONF("iir=%d ", scratch);
+ DEBUG_AUTOCONF("type=%s\n", uart_config[port->type].name);
}
static void autoconfig_irq(struct uart_8250_port *up)
{
+ struct uart_port *port = &up->port;
unsigned char save_mcr, save_ier;
unsigned char save_ICP = 0;
unsigned int ICP = 0;
unsigned long irqs;
int irq;
- if (up->port.flags & UPF_FOURPORT) {
- ICP = (up->port.iobase & 0xfe0) | 0x1f;
+ if (port->flags & UPF_FOURPORT) {
+ ICP = (port->iobase & 0xfe0) | 0x1f;
save_ICP = inb_p(ICP);
outb_p(0x80, ICP);
- (void) inb_p(ICP);
+ inb_p(ICP);
}
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
- save_mcr = serial_inp(up, UART_MCR);
- save_ier = serial_inp(up, UART_IER);
- serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
+ save_mcr = serial_in(up, UART_MCR);
+ save_ier = serial_in(up, UART_IER);
+ serial_out(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
irqs = probe_irq_on();
- serial_outp(up, UART_MCR, 0);
+ serial_out(up, UART_MCR, 0);
udelay(10);
- if (up->port.flags & UPF_FOURPORT) {
- serial_outp(up, UART_MCR,
+ if (port->flags & UPF_FOURPORT) {
+ serial_out(up, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS);
} else {
- serial_outp(up, UART_MCR,
+ serial_out(up, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
}
- serial_outp(up, UART_IER, 0x0f); /* enable all intrs */
- (void)serial_inp(up, UART_LSR);
- (void)serial_inp(up, UART_RX);
- (void)serial_inp(up, UART_IIR);
- (void)serial_inp(up, UART_MSR);
- serial_outp(up, UART_TX, 0xFF);
+ serial_out(up, UART_IER, 0x0f); /* enable all intrs */
+ serial_in(up, UART_LSR);
+ serial_in(up, UART_RX);
+ serial_in(up, UART_IIR);
+ serial_in(up, UART_MSR);
+ serial_out(up, UART_TX, 0xFF);
udelay(20);
irq = probe_irq_off(irqs);
- serial_outp(up, UART_MCR, save_mcr);
- serial_outp(up, UART_IER, save_ier);
+ serial_out(up, UART_MCR, save_mcr);
+ serial_out(up, UART_IER, save_ier);
- if (up->port.flags & UPF_FOURPORT)
+ if (port->flags & UPF_FOURPORT)
outb_p(save_ICP, ICP);
- up->port.irq = (irq > 0) ? irq : 0;
+ port->irq = (irq > 0) ? irq : 0;
}
static inline void __stop_tx(struct uart_8250_port *p)
@@ -1294,7 +1285,7 @@ static void serial8250_stop_tx(struct uart_port *port)
/*
* We really want to stop the transmitter from sending.
*/
- if (up->port.type == PORT_16C950) {
+ if (port->type == PORT_16C950) {
up->acr |= UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
@@ -1305,17 +1296,17 @@ static void serial8250_start_tx(struct uart_port *port)
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
- if (!(up->ier & UART_IER_THRI)) {
+ if (up->dma && !serial8250_tx_dma(up)) {
+ return;
+ } else if (!(up->ier & UART_IER_THRI)) {
up->ier |= UART_IER_THRI;
- serial_out(up, UART_IER, up->ier);
+ serial_port_out(port, UART_IER, up->ier);
if (up->bugs & UART_BUG_TXEN) {
unsigned char lsr;
lsr = serial_in(up, UART_LSR);
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
- if ((up->port.type == PORT_RM9000) ?
- (lsr & UART_LSR_THRE) :
- (lsr & UART_LSR_TEMT))
+ if (lsr & UART_LSR_TEMT)
serial8250_tx_chars(up);
}
}
@@ -1323,7 +1314,7 @@ static void serial8250_start_tx(struct uart_port *port)
/*
* Re-enable the transmitter if we disabled it.
*/
- if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
+ if (port->type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
up->acr &= ~UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
@@ -1336,7 +1327,7 @@ static void serial8250_stop_rx(struct uart_port *port)
up->ier &= ~UART_IER_RLSI;
up->port.read_status_mask &= ~UART_LSR_DR;
- serial_out(up, UART_IER, up->ier);
+ serial_port_out(port, UART_IER, up->ier);
}
static void serial8250_enable_ms(struct uart_port *port)
@@ -1349,28 +1340,7 @@ static void serial8250_enable_ms(struct uart_port *port)
return;
up->ier |= UART_IER_MSI;
- serial_out(up, UART_IER, up->ier);
-}
-
-/*
- * Clear the Tegra rx fifo after a break
- *
- * FIXME: This needs to become a port specific callback once we have a
- * framework for this
- */
-static void clear_rx_fifo(struct uart_8250_port *up)
-{
- unsigned int status, tmout = 10000;
- do {
- status = serial_in(up, UART_LSR);
- if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
- status = serial_in(up, UART_RX);
- else
- break;
- if (--tmout == 0)
- break;
- udelay(1);
- } while (1);
+ serial_port_out(port, UART_IER, up->ier);
}
/*
@@ -1381,14 +1351,14 @@ static void clear_rx_fifo(struct uart_8250_port *up)
unsigned char
serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
{
- struct tty_struct *tty = up->port.state->port.tty;
+ struct uart_port *port = &up->port;
unsigned char ch;
int max_count = 256;
char flag;
do {
if (likely(lsr & UART_LSR_DR))
- ch = serial_inp(up, UART_RX);
+ ch = serial_in(up, UART_RX);
else
/*
* Intel 82571 has a Serial Over Lan device that will
@@ -1400,44 +1370,34 @@ serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
ch = 0;
flag = TTY_NORMAL;
- up->port.icount.rx++;
+ port->icount.rx++;
lsr |= up->lsr_saved_flags;
up->lsr_saved_flags = 0;
if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
- /*
- * For statistics only
- */
if (lsr & UART_LSR_BI) {
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
- up->port.icount.brk++;
- /*
- * If tegra port then clear the rx fifo to
- * accept another break/character.
- */
- if (up->port.type == PORT_TEGRA)
- clear_rx_fifo(up);
-
+ port->icount.brk++;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
- if (uart_handle_break(&up->port))
+ if (uart_handle_break(port))
goto ignore_char;
} else if (lsr & UART_LSR_PE)
- up->port.icount.parity++;
+ port->icount.parity++;
else if (lsr & UART_LSR_FE)
- up->port.icount.frame++;
+ port->icount.frame++;
if (lsr & UART_LSR_OE)
- up->port.icount.overrun++;
+ port->icount.overrun++;
/*
* Mask off conditions which should be ignored.
*/
- lsr &= up->port.read_status_mask;
+ lsr &= port->read_status_mask;
if (lsr & UART_LSR_BI) {
DEBUG_INTR("handling break....");
@@ -1447,34 +1407,35 @@ serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
else if (lsr & UART_LSR_FE)
flag = TTY_FRAME;
}
- if (uart_handle_sysrq_char(&up->port, ch))
+ if (uart_handle_sysrq_char(port, ch))
goto ignore_char;
- uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
+ uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
ignore_char:
- lsr = serial_inp(up, UART_LSR);
+ lsr = serial_in(up, UART_LSR);
} while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
- spin_unlock(&up->port.lock);
- tty_flip_buffer_push(tty);
- spin_lock(&up->port.lock);
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
return lsr;
}
EXPORT_SYMBOL_GPL(serial8250_rx_chars);
void serial8250_tx_chars(struct uart_8250_port *up)
{
- struct circ_buf *xmit = &up->port.state->xmit;
+ struct uart_port *port = &up->port;
+ struct circ_buf *xmit = &port->state->xmit;
int count;
- if (up->port.x_char) {
- serial_outp(up, UART_TX, up->port.x_char);
- up->port.icount.tx++;
- up->port.x_char = 0;
+ if (port->x_char) {
+ serial_out(up, UART_TX, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
return;
}
- if (uart_tx_stopped(&up->port)) {
- serial8250_stop_tx(&up->port);
+ if (uart_tx_stopped(port)) {
+ serial8250_stop_tx(port);
return;
}
if (uart_circ_empty(xmit)) {
@@ -1486,13 +1447,18 @@ void serial8250_tx_chars(struct uart_8250_port *up)
do {
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ port->icount.tx++;
if (uart_circ_empty(xmit))
break;
+ if (up->capabilities & UART_CAP_HFIFO) {
+ if ((serial_port_in(port, UART_LSR) & BOTH_EMPTY) !=
+ BOTH_EMPTY)
+ break;
+ }
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
+ uart_write_wakeup(port);
DEBUG_INTR("THRE...");
@@ -1503,22 +1469,23 @@ EXPORT_SYMBOL_GPL(serial8250_tx_chars);
unsigned int serial8250_modem_status(struct uart_8250_port *up)
{
+ struct uart_port *port = &up->port;
unsigned int status = serial_in(up, UART_MSR);
status |= up->msr_saved_flags;
up->msr_saved_flags = 0;
if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
- up->port.state != NULL) {
+ port->state != NULL) {
if (status & UART_MSR_TERI)
- up->port.icount.rng++;
+ port->icount.rng++;
if (status & UART_MSR_DDSR)
- up->port.icount.dsr++;
+ port->icount.dsr++;
if (status & UART_MSR_DDCD)
- uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+ uart_handle_dcd_change(port, status & UART_MSR_DCD);
if (status & UART_MSR_DCTS)
- uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+ uart_handle_cts_change(port, status & UART_MSR_CTS);
- wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+ wake_up_interruptible(&port->state->port.delta_msr_wait);
}
return status;
@@ -1534,37 +1501,66 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
unsigned long flags;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
+ int dma_err = 0;
if (iir & UART_IIR_NO_INT)
return 0;
- spin_lock_irqsave(&up->port.lock, flags);
+ spin_lock_irqsave(&port->lock, flags);
- status = serial_inp(up, UART_LSR);
+ status = serial_port_in(port, UART_LSR);
DEBUG_INTR("status = %x...", status);
- if (status & (UART_LSR_DR | UART_LSR_BI))
- status = serial8250_rx_chars(up, status);
+ if (status & (UART_LSR_DR | UART_LSR_BI)) {
+ if (up->dma)
+ dma_err = serial8250_rx_dma(up, iir);
+
+ if (!up->dma || dma_err)
+ 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(&up->port.lock, flags);
+ spin_unlock_irqrestore(&port->lock, flags);
return 1;
}
EXPORT_SYMBOL_GPL(serial8250_handle_irq);
static int serial8250_default_handle_irq(struct uart_port *port)
{
- struct uart_8250_port *up =
- container_of(port, struct uart_8250_port, port);
- unsigned int iir = serial_in(up, UART_IIR);
+ unsigned int iir = serial_port_in(port, UART_IIR);
return serial8250_handle_irq(port, iir);
}
/*
+ * These Exar UARTs have an extra interrupt indicator that could
+ * fire for a few unimplemented interrupts. One of which is a
+ * wakeup event when coming out of sleep. Put this here just
+ * to be on the safe side that these interrupts don't go unhandled.
+ */
+static int exar_handle_irq(struct uart_port *port)
+{
+ unsigned char int0, int1, int2, int3;
+ unsigned int iir = serial_port_in(port, UART_IIR);
+ int ret;
+
+ ret = serial8250_handle_irq(port, iir);
+
+ if ((port->type == PORT_XR17V35X) ||
+ (port->type == PORT_XR17D15X)) {
+ int0 = serial_port_in(port, 0x80);
+ int1 = serial_port_in(port, 0x81);
+ int2 = serial_port_in(port, 0x82);
+ int3 = serial_port_in(port, 0x83);
+ }
+
+ return ret;
+}
+
+/*
* This is the serial driver's interrupt routine.
*
* Arjan thinks the old way was overly complex, so it got simplified.
@@ -1592,13 +1588,11 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
do {
struct uart_8250_port *up;
struct uart_port *port;
- bool skip;
up = list_entry(l, struct uart_8250_port, list);
port = &up->port;
- skip = pass_counter && up->port.flags & UPF_IIR_ONCE;
- if (!skip && port->handle_irq(port)) {
+ if (port->handle_irq(port)) {
handled = 1;
end = NULL;
} else if (end == NULL)
@@ -1700,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;
@@ -1750,7 +1748,7 @@ static void serial8250_backup_timeout(unsigned long data)
* Must disable interrupts or else we risk racing with the interrupt
* based handler.
*/
- if (is_real_interrupt(up->port.irq)) {
+ if (up->port.irq) {
ier = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0);
}
@@ -1775,7 +1773,7 @@ static void serial8250_backup_timeout(unsigned long data)
if (!(iir & UART_IIR_NO_INT))
serial8250_tx_chars(up);
- if (is_real_interrupt(up->port.irq))
+ if (up->port.irq)
serial_out(up, UART_IER, ier);
spin_unlock_irqrestore(&up->port.lock, flags);
@@ -1792,10 +1790,10 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
unsigned long flags;
unsigned int lsr;
- spin_lock_irqsave(&up->port.lock, flags);
- lsr = serial_in(up, UART_LSR);
+ spin_lock_irqsave(&port->lock, flags);
+ lsr = serial_port_in(port, UART_LSR);
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
- spin_unlock_irqrestore(&up->port.lock, flags);
+ spin_unlock_irqrestore(&port->lock, flags);
return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
}
@@ -1840,7 +1838,7 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
- serial_out(up, UART_MCR, mcr);
+ serial_port_out(port, UART_MCR, mcr);
}
static void serial8250_break_ctl(struct uart_port *port, int break_state)
@@ -1849,13 +1847,13 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
container_of(port, struct uart_8250_port, port);
unsigned long flags;
- spin_lock_irqsave(&up->port.lock, flags);
+ spin_lock_irqsave(&port->lock, flags);
if (break_state == -1)
up->lcr |= UART_LCR_SBC;
else
up->lcr &= ~UART_LCR_SBC;
- serial_out(up, UART_LCR, up->lcr);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ serial_port_out(port, UART_LCR, up->lcr);
+ spin_unlock_irqrestore(&port->lock, flags);
}
/*
@@ -1900,14 +1898,12 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
static int serial8250_get_poll_char(struct uart_port *port)
{
- struct uart_8250_port *up =
- container_of(port, struct uart_8250_port, port);
- unsigned char lsr = serial_inp(up, UART_LSR);
+ unsigned char lsr = serial_port_in(port, UART_LSR);
if (!(lsr & UART_LSR_DR))
return NO_POLL_CHAR;
- return serial_inp(up, UART_RX);
+ return serial_port_in(port, UART_RX);
}
@@ -1921,29 +1917,24 @@ static void serial8250_put_poll_char(struct uart_port *port,
/*
* First save the IER then disable the interrupts
*/
- ier = serial_in(up, UART_IER);
+ ier = serial_port_in(port, UART_IER);
if (up->capabilities & UART_CAP_UUE)
- serial_out(up, UART_IER, UART_IER_UUE);
+ serial_port_out(port, UART_IER, UART_IER_UUE);
else
- serial_out(up, UART_IER, 0);
+ serial_port_out(port, UART_IER, 0);
wait_for_xmitr(up, BOTH_EMPTY);
/*
* Send the character out.
- * If a LF, also do CR...
*/
- serial_out(up, UART_TX, c);
- if (c == 10) {
- wait_for_xmitr(up, BOTH_EMPTY);
- serial_out(up, UART_TX, 13);
- }
+ serial_port_out(port, UART_TX, c);
/*
* Finally, wait for transmitter to become empty
* and restore the IER
*/
wait_for_xmitr(up, BOTH_EMPTY);
- serial_out(up, UART_IER, ier);
+ serial_port_out(port, UART_IER, ier);
}
#endif /* CONFIG_CONSOLE_POLL */
@@ -1956,25 +1947,31 @@ static int serial8250_startup(struct uart_port *port)
unsigned char lsr, iir;
int retval;
- up->port.fifosize = uart_config[up->port.type].fifo_size;
- up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
- up->capabilities = uart_config[up->port.type].flags;
+ if (port->type == PORT_8250_CIR)
+ return -ENODEV;
+
+ if (!port->fifosize)
+ port->fifosize = uart_config[port->type].fifo_size;
+ if (!up->tx_loadsz)
+ up->tx_loadsz = uart_config[port->type].tx_loadsz;
+ if (!up->capabilities)
+ up->capabilities = uart_config[port->type].flags;
up->mcr = 0;
- if (up->port.iotype != up->cur_iotype)
+ if (port->iotype != up->cur_iotype)
set_io_from_upio(port);
- if (up->port.type == PORT_16C950) {
+ if (port->type == PORT_16C950) {
/* Wake up and initialize UART */
up->acr = 0;
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_outp(up, UART_EFR, UART_EFR_ECB);
- serial_outp(up, UART_IER, 0);
- serial_outp(up, UART_LCR, 0);
+ serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_port_out(port, UART_EFR, UART_EFR_ECB);
+ serial_port_out(port, UART_IER, 0);
+ serial_port_out(port, UART_LCR, 0);
serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_outp(up, UART_EFR, UART_EFR_ECB);
- serial_outp(up, UART_LCR, 0);
+ serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_port_out(port, UART_EFR, UART_EFR_ECB);
+ serial_port_out(port, UART_LCR, 0);
}
#ifdef CONFIG_SERIAL_8250_RSA
@@ -1994,41 +1991,43 @@ static int serial8250_startup(struct uart_port *port)
/*
* Clear the interrupt registers.
*/
- (void) serial_inp(up, UART_LSR);
- (void) serial_inp(up, UART_RX);
- (void) serial_inp(up, UART_IIR);
- (void) serial_inp(up, UART_MSR);
+ serial_port_in(port, UART_LSR);
+ serial_port_in(port, UART_RX);
+ serial_port_in(port, UART_IIR);
+ serial_port_in(port, UART_MSR);
/*
* At this point, there's no way the LSR could still be 0xff;
* if it is, then bail out, because there's likely no UART
* here.
*/
- if (!(up->port.flags & UPF_BUGGY_UART) &&
- (serial_inp(up, UART_LSR) == 0xff)) {
+ if (!(port->flags & UPF_BUGGY_UART) &&
+ (serial_port_in(port, UART_LSR) == 0xff)) {
printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
- serial_index(&up->port));
+ serial_index(port));
return -ENODEV;
}
/*
* For a XR16C850, we need to set the trigger levels
*/
- if (up->port.type == PORT_16850) {
+ if (port->type == PORT_16850) {
unsigned char fctr;
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
- serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX);
- serial_outp(up, UART_TRG, UART_TRG_96);
- serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX);
- serial_outp(up, UART_TRG, UART_TRG_96);
+ fctr = serial_in(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
+ serial_port_out(port, UART_FCTR,
+ fctr | UART_FCTR_TRGD | UART_FCTR_RX);
+ serial_port_out(port, UART_TRG, UART_TRG_96);
+ serial_port_out(port, UART_FCTR,
+ fctr | UART_FCTR_TRGD | UART_FCTR_TX);
+ serial_port_out(port, UART_TRG, UART_TRG_96);
- serial_outp(up, UART_LCR, 0);
+ serial_port_out(port, UART_LCR, 0);
}
- if (is_real_interrupt(up->port.irq)) {
+ if (port->irq) {
unsigned char iir1;
/*
* Test for UARTs that do not reassert THRE when the
@@ -2038,29 +2037,31 @@ static int serial8250_startup(struct uart_port *port)
* the interrupt is enabled. Delays are necessary to
* allow register changes to become visible.
*/
- spin_lock_irqsave(&up->port.lock, flags);
+ spin_lock_irqsave(&port->lock, flags);
if (up->port.irqflags & IRQF_SHARED)
- disable_irq_nosync(up->port.irq);
+ disable_irq_nosync(port->irq);
wait_for_xmitr(up, UART_LSR_THRE);
- serial_out_sync(up, UART_IER, UART_IER_THRI);
+ serial_port_out_sync(port, UART_IER, UART_IER_THRI);
udelay(1); /* allow THRE to set */
- iir1 = serial_in(up, UART_IIR);
- serial_out(up, UART_IER, 0);
- serial_out_sync(up, UART_IER, UART_IER_THRI);
+ iir1 = serial_port_in(port, UART_IIR);
+ serial_port_out(port, UART_IER, 0);
+ serial_port_out_sync(port, UART_IER, UART_IER_THRI);
udelay(1); /* allow a working UART time to re-assert THRE */
- iir = serial_in(up, UART_IIR);
- serial_out(up, UART_IER, 0);
+ iir = serial_port_in(port, UART_IIR);
+ serial_port_out(port, UART_IER, 0);
- if (up->port.irqflags & IRQF_SHARED)
- enable_irq(up->port.irq);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ if (port->irqflags & IRQF_SHARED)
+ enable_irq(port->irq);
+ spin_unlock_irqrestore(&port->lock, flags);
/*
- * If the interrupt is not reasserted, setup a timer to
- * kick the UART on a regular basis.
+ * If the interrupt is not reasserted, or we otherwise
+ * don't trust the iir, setup a timer to kick the UART
+ * on a regular basis.
*/
- if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) {
+ if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) ||
+ up->port.flags & UPF_BUG_THRE) {
up->bugs |= UART_BUG_THRE;
pr_debug("ttyS%d - using backup timer\n",
serial_index(port));
@@ -2083,7 +2084,7 @@ static int serial8250_startup(struct uart_port *port)
* hardware interrupt, we use a timer-based system. The original
* driver used to do this with IRQ0.
*/
- if (!is_real_interrupt(up->port.irq)) {
+ if (!port->irq) {
up->timer.data = (unsigned long)up;
mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
} else {
@@ -2095,20 +2096,20 @@ static int serial8250_startup(struct uart_port *port)
/*
* Now, initialize the UART
*/
- serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+ serial_port_out(port, UART_LCR, UART_LCR_WLEN8);
- spin_lock_irqsave(&up->port.lock, flags);
+ spin_lock_irqsave(&port->lock, flags);
if (up->port.flags & UPF_FOURPORT) {
- if (!is_real_interrupt(up->port.irq))
+ if (!up->port.irq)
up->port.mctrl |= TIOCM_OUT1;
} else
/*
* Most PC uarts need OUT2 raised to enable interrupts.
*/
- if (is_real_interrupt(up->port.irq))
+ if (port->irq)
up->port.mctrl |= TIOCM_OUT2;
- serial8250_set_mctrl(&up->port, up->port.mctrl);
+ serial8250_set_mctrl(port, port->mctrl);
/* Serial over Lan (SoL) hack:
Intel 8257x Gigabit ethernet chips have a
@@ -2128,10 +2129,10 @@ static int serial8250_startup(struct uart_port *port)
* Do a quick test to see if we receive an
* interrupt when we enable the TX irq.
*/
- serial_outp(up, UART_IER, UART_IER_THRI);
- lsr = serial_in(up, UART_LSR);
- iir = serial_in(up, UART_IIR);
- serial_outp(up, UART_IER, 0);
+ serial_port_out(port, UART_IER, UART_IER_THRI);
+ lsr = serial_port_in(port, UART_LSR);
+ iir = serial_port_in(port, UART_IIR);
+ serial_port_out(port, UART_IER, 0);
if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
if (!(up->bugs & UART_BUG_TXEN)) {
@@ -2144,36 +2145,48 @@ static int serial8250_startup(struct uart_port *port)
}
dont_test_tx_en:
- spin_unlock_irqrestore(&up->port.lock, flags);
+ spin_unlock_irqrestore(&port->lock, flags);
/*
* Clear the interrupt registers again for luck, and clear the
* saved flags to avoid getting false values from polling
* routines or the previous session.
*/
- serial_inp(up, UART_LSR);
- serial_inp(up, UART_RX);
- serial_inp(up, UART_IIR);
- serial_inp(up, UART_MSR);
+ serial_port_in(port, UART_LSR);
+ serial_port_in(port, UART_RX);
+ serial_port_in(port, UART_IIR);
+ serial_port_in(port, UART_MSR);
up->lsr_saved_flags = 0;
up->msr_saved_flags = 0;
/*
+ * Request DMA channels for both RX and TX.
+ */
+ if (up->dma) {
+ retval = serial8250_request_dma(up);
+ if (retval) {
+ pr_warn_ratelimited("ttyS%d - failed to request DMA\n",
+ serial_index(port));
+ up->dma = NULL;
+ }
+ }
+
+ /*
* Finally, enable interrupts. Note: Modem status interrupts
* are set via set_termios(), which will be occurring imminently
* anyway, so we don't enable them here.
*/
up->ier = UART_IER_RLSI | UART_IER_RDI;
- serial_outp(up, UART_IER, up->ier);
+ serial_port_out(port, UART_IER, up->ier);
- if (up->port.flags & UPF_FOURPORT) {
+ if (port->flags & UPF_FOURPORT) {
unsigned int icp;
/*
* Enable interrupts on the AST Fourport board
*/
- icp = (up->port.iobase & 0xfe0) | 0x01f;
+ icp = (port->iobase & 0xfe0) | 0x01f;
outb_p(0x80, icp);
- (void) inb_p(icp);
+ inb_p(icp);
}
return 0;
@@ -2189,23 +2202,27 @@ static void serial8250_shutdown(struct uart_port *port)
* Disable interrupts from this port
*/
up->ier = 0;
- serial_outp(up, UART_IER, 0);
+ serial_port_out(port, UART_IER, 0);
- spin_lock_irqsave(&up->port.lock, flags);
- if (up->port.flags & UPF_FOURPORT) {
+ if (up->dma)
+ serial8250_release_dma(up);
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (port->flags & UPF_FOURPORT) {
/* reset interrupts on the AST Fourport board */
- inb((up->port.iobase & 0xfe0) | 0x1f);
- up->port.mctrl |= TIOCM_OUT1;
+ inb((port->iobase & 0xfe0) | 0x1f);
+ port->mctrl |= TIOCM_OUT1;
} else
- up->port.mctrl &= ~TIOCM_OUT2;
+ port->mctrl &= ~TIOCM_OUT2;
- serial8250_set_mctrl(&up->port, up->port.mctrl);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ serial8250_set_mctrl(port, port->mctrl);
+ spin_unlock_irqrestore(&port->lock, flags);
/*
* Disable break condition and FIFOs
*/
- serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+ serial_port_out(port, UART_LCR,
+ serial_port_in(port, UART_LCR) & ~UART_LCR_SBC);
serial8250_clear_fifos(up);
#ifdef CONFIG_SERIAL_8250_RSA
@@ -2219,11 +2236,11 @@ static void serial8250_shutdown(struct uart_port *port)
* Read data port to reset things, and then unlink from
* the IRQ chain.
*/
- (void) serial_in(up, UART_RX);
+ serial_port_in(port, UART_RX);
del_timer_sync(&up->timer);
up->timer.function = serial8250_timeout;
- if (is_real_interrupt(up->port.irq))
+ if (port->irq)
serial_unlink_irq_chain(up);
}
@@ -2256,6 +2273,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned char cval, fcr = 0;
unsigned long flags;
unsigned int baud, quot;
+ int fifo_bug = 0;
switch (termios->c_cflag & CSIZE) {
case CS5:
@@ -2275,8 +2293,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
if (termios->c_cflag & CSTOPB)
cval |= UART_LCR_STOP;
- if (termios->c_cflag & PARENB)
+ if (termios->c_cflag & PARENB) {
cval |= UART_LCR_PARITY;
+ if (up->bugs & UART_BUG_PARITY)
+ fifo_bug = 1;
+ }
if (!(termios->c_cflag & PARODD))
cval |= UART_LCR_EPAR;
#ifdef CMSPAR
@@ -2298,11 +2319,12 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
quot++;
- if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
- if (baud < 2400)
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
- else
- fcr = uart_config[up->port.type].fcr;
+ if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
+ fcr = uart_config[port->type].fcr;
+ if ((baud < 2400 && !up->dma) || fifo_bug) {
+ fcr &= ~UART_FCR_TRIGGER_MASK;
+ fcr |= UART_FCR_TRIGGER_1;
+ }
}
/*
@@ -2311,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 && up->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;
@@ -2323,40 +2347,40 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
- spin_lock_irqsave(&up->port.lock, flags);
+ spin_lock_irqsave(&port->lock, flags);
/*
* Update the per-port timeout.
*/
uart_update_timeout(port, termios->c_cflag, baud);
- up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ 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))
- up->port.read_status_mask |= UART_LSR_BI;
+ port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+ port->read_status_mask |= UART_LSR_BI;
/*
* Characteres to ignore
*/
- up->port.ignore_status_mask = 0;
+ port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
if (termios->c_iflag & IGNBRK) {
- up->port.ignore_status_mask |= UART_LSR_BI;
+ port->ignore_status_mask |= UART_LSR_BI;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_OE;
+ port->ignore_status_mask |= UART_LSR_OE;
}
/*
* ignore all characters if CREAD is not set
*/
if ((termios->c_cflag & CREAD) == 0)
- up->port.ignore_status_mask |= UART_LSR_DR;
+ port->ignore_status_mask |= UART_LSR_DR;
/*
* CTS flow control flag and modem status interrupts
@@ -2370,7 +2394,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
if (up->capabilities & UART_CAP_RTOIE)
up->ier |= UART_IER_RTOIE;
- serial_out(up, UART_IER, up->ier);
+ serial_port_out(port, UART_IER, up->ier);
if (up->capabilities & UART_CAP_EFR) {
unsigned char efr = 0;
@@ -2382,51 +2406,68 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
if (termios->c_cflag & CRTSCTS)
efr |= UART_EFR_CTS;
- serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
- if (up->port.flags & UPF_EXAR_EFR)
- serial_outp(up, UART_XR_EFR, efr);
+ serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
+ if (port->flags & UPF_EXAR_EFR)
+ serial_port_out(port, UART_XR_EFR, efr);
else
- serial_outp(up, UART_EFR, efr);
+ serial_port_out(port, UART_EFR, efr);
}
-#ifdef CONFIG_ARCH_OMAP
/* Workaround to enable 115200 baud on OMAP1510 internal ports */
- if (cpu_is_omap1510() && is_omap_port(up)) {
+ if (is_omap1510_8250(up)) {
if (baud == 115200) {
quot = 1;
- serial_out(up, UART_OMAP_OSC_12M_SEL, 1);
+ serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1);
} else
- serial_out(up, UART_OMAP_OSC_12M_SEL, 0);
+ serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0);
}
-#endif
- if (up->capabilities & UART_NATSEMI) {
- /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */
- serial_outp(up, UART_LCR, 0xe0);
- } else {
- serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
- }
+ /*
+ * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2,
+ * otherwise just set DLAB
+ */
+ if (up->capabilities & UART_NATSEMI)
+ serial_port_out(port, UART_LCR, 0xe0);
+ else
+ serial_port_out(port, UART_LCR, cval | UART_LCR_DLAB);
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.
*/
- if (up->port.type == PORT_16750)
- serial_outp(up, UART_FCR, fcr);
+ if (port->type == PORT_16750)
+ serial_port_out(port, UART_FCR, fcr);
- serial_outp(up, UART_LCR, cval); /* reset DLAB */
+ serial_port_out(port, UART_LCR, cval); /* reset DLAB */
up->lcr = cval; /* Save LCR */
- if (up->port.type != PORT_16750) {
- if (fcr & UART_FCR_ENABLE_FIFO) {
- /* emulated UARTs (Lucent Venus 167x) need two steps */
- serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
- }
- serial_outp(up, UART_FCR, fcr); /* set fcr */
- }
- serial8250_set_mctrl(&up->port, up->port.mctrl);
- spin_unlock_irqrestore(&up->port.lock, flags);
+ if (port->type != PORT_16750) {
+ /* emulated UARTs (Lucent Venus 167x) need two steps */
+ if (fcr & UART_FCR_ENABLE_FIFO)
+ serial_port_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_port_out(port, UART_FCR, fcr); /* set fcr */
+ }
+ serial8250_set_mctrl(port, port->mctrl);
+ spin_unlock_irqrestore(&port->lock, flags);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
@@ -2478,10 +2519,9 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
{
if (pt->port.iotype == UPIO_AU)
return 0x1000;
-#ifdef CONFIG_ARCH_OMAP
- if (is_omap_port(pt))
+ if (is_omap1_8250(pt))
return 0x16 << pt->port.regshift;
-#endif
+
return 8 << pt->port.regshift;
}
@@ -2491,26 +2531,26 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
static int serial8250_request_std_resource(struct uart_8250_port *up)
{
unsigned int size = serial8250_port_size(up);
+ struct uart_port *port = &up->port;
int ret = 0;
- switch (up->port.iotype) {
+ switch (port->iotype) {
case UPIO_AU:
case UPIO_TSI:
case UPIO_MEM32:
case UPIO_MEM:
- if (!up->port.mapbase)
+ if (!port->mapbase)
break;
- if (!request_mem_region(up->port.mapbase, size, "serial")) {
+ if (!request_mem_region(port->mapbase, size, "serial")) {
ret = -EBUSY;
break;
}
- if (up->port.flags & UPF_IOREMAP) {
- up->port.membase = ioremap_nocache(up->port.mapbase,
- size);
- if (!up->port.membase) {
- release_mem_region(up->port.mapbase, size);
+ if (port->flags & UPF_IOREMAP) {
+ port->membase = ioremap_nocache(port->mapbase, size);
+ if (!port->membase) {
+ release_mem_region(port->mapbase, size);
ret = -ENOMEM;
}
}
@@ -2518,7 +2558,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up)
case UPIO_HUB6:
case UPIO_PORT:
- if (!request_region(up->port.iobase, size, "serial"))
+ if (!request_region(port->iobase, size, "serial"))
ret = -EBUSY;
break;
}
@@ -2528,26 +2568,27 @@ static int serial8250_request_std_resource(struct uart_8250_port *up)
static void serial8250_release_std_resource(struct uart_8250_port *up)
{
unsigned int size = serial8250_port_size(up);
+ struct uart_port *port = &up->port;
- switch (up->port.iotype) {
+ switch (port->iotype) {
case UPIO_AU:
case UPIO_TSI:
case UPIO_MEM32:
case UPIO_MEM:
- if (!up->port.mapbase)
+ if (!port->mapbase)
break;
- if (up->port.flags & UPF_IOREMAP) {
- iounmap(up->port.membase);
- up->port.membase = NULL;
+ if (port->flags & UPF_IOREMAP) {
+ iounmap(port->membase);
+ port->membase = NULL;
}
- release_mem_region(up->port.mapbase, size);
+ release_mem_region(port->mapbase, size);
break;
case UPIO_HUB6:
case UPIO_PORT:
- release_region(up->port.iobase, size);
+ release_region(port->iobase, size);
break;
}
}
@@ -2556,12 +2597,13 @@ static int serial8250_request_rsa_resource(struct uart_8250_port *up)
{
unsigned long start = UART_RSA_BASE << up->port.regshift;
unsigned int size = 8 << up->port.regshift;
+ struct uart_port *port = &up->port;
int ret = -EINVAL;
- switch (up->port.iotype) {
+ switch (port->iotype) {
case UPIO_HUB6:
case UPIO_PORT:
- start += up->port.iobase;
+ start += port->iobase;
if (request_region(start, size, "serial-rsa"))
ret = 0;
else
@@ -2576,11 +2618,12 @@ static void serial8250_release_rsa_resource(struct uart_8250_port *up)
{
unsigned long offset = UART_RSA_BASE << up->port.regshift;
unsigned int size = 8 << up->port.regshift;
+ struct uart_port *port = &up->port;
- switch (up->port.iotype) {
+ switch (port->iotype) {
case UPIO_HUB6:
case UPIO_PORT:
- release_region(up->port.iobase + offset, size);
+ release_region(port->iobase + offset, size);
break;
}
}
@@ -2591,7 +2634,7 @@ static void serial8250_release_port(struct uart_port *port)
container_of(port, struct uart_8250_port, port);
serial8250_release_std_resource(up);
- if (up->port.type == PORT_RSA)
+ if (port->type == PORT_RSA)
serial8250_release_rsa_resource(up);
}
@@ -2599,10 +2642,13 @@ static int serial8250_request_port(struct uart_port *port)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
- int ret = 0;
+ int ret;
+
+ if (port->type == PORT_8250_CIR)
+ return -ENODEV;
ret = serial8250_request_std_resource(up);
- if (ret == 0 && up->port.type == PORT_RSA) {
+ if (ret == 0 && port->type == PORT_RSA) {
ret = serial8250_request_rsa_resource(up);
if (ret < 0)
serial8250_release_std_resource(up);
@@ -2618,6 +2664,9 @@ static void serial8250_config_port(struct uart_port *port, int flags)
int probeflags = PROBE_ANY;
int ret;
+ if (port->type == PORT_8250_CIR)
+ return;
+
/*
* Find the region that we can probe for. This in turn
* tells us whether we can probe for the type of port.
@@ -2630,23 +2679,32 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (ret < 0)
probeflags &= ~PROBE_RSA;
- if (up->port.iotype != up->cur_iotype)
+ if (port->iotype != up->cur_iotype)
set_io_from_upio(port);
if (flags & UART_CONFIG_TYPE)
autoconfig(up, probeflags);
/* if access method is AU, it is a 16550 with a quirk */
- if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU)
+ 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 (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
+ if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
autoconfig_irq(up);
- if (up->port.type != PORT_RSA && probeflags & PROBE_RSA)
+ if (port->type != PORT_RSA && probeflags & PROBE_RSA)
serial8250_release_rsa_resource(up);
- if (up->port.type == PORT_UNKNOWN)
+ if (port->type == PORT_UNKNOWN)
serial8250_release_std_resource(up);
+
+ /* Fixme: probably not the best place for this */
+ if ((port->type == PORT_XR17V35X) ||
+ (port->type == PORT_XR17D15X))
+ port->handle_irq = exar_handle_irq;
}
static int
@@ -2717,14 +2775,19 @@ static void __init serial8250_isa_init_ports(void)
return;
first = 0;
+ if (nr_uarts > UART_NR)
+ nr_uarts = UART_NR;
+
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
+ struct uart_port *port = &up->port;
- up->port.line = i;
- spin_lock_init(&up->port.lock);
+ port->line = i;
+ spin_lock_init(&port->lock);
init_timer(&up->timer);
up->timer.function = serial8250_timeout;
+ up->cur_iotype = 0xFF;
/*
* ALPHA_KLUDGE_MCR needs to be killed.
@@ -2732,7 +2795,7 @@ static void __init serial8250_isa_init_ports(void)
up->mcr_mask = ~ALPHA_KLUDGE_MCR;
up->mcr_force = ALPHA_KLUDGE_MCR;
- up->port.ops = &serial8250_pops;
+ port->ops = &serial8250_pops;
}
if (share_irqs)
@@ -2741,17 +2804,19 @@ static void __init serial8250_isa_init_ports(void)
for (i = 0, up = serial8250_ports;
i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
i++, up++) {
- up->port.iobase = old_serial_port[i].port;
- up->port.irq = irq_canonicalize(old_serial_port[i].irq);
- up->port.irqflags = old_serial_port[i].irqflags;
- up->port.uartclk = old_serial_port[i].baud_base * 16;
- up->port.flags = old_serial_port[i].flags;
- up->port.hub6 = old_serial_port[i].hub6;
- up->port.membase = old_serial_port[i].iomem_base;
- up->port.iotype = old_serial_port[i].io_type;
- up->port.regshift = old_serial_port[i].iomem_reg_shift;
- set_io_from_upio(&up->port);
- up->port.irqflags |= irqflag;
+ struct uart_port *port = &up->port;
+
+ port->iobase = old_serial_port[i].port;
+ port->irq = irq_canonicalize(old_serial_port[i].irq);
+ port->irqflags = old_serial_port[i].irqflags;
+ port->uartclk = old_serial_port[i].baud_base * 16;
+ port->flags = old_serial_port[i].flags;
+ port->hub6 = old_serial_port[i].hub6;
+ port->membase = old_serial_port[i].iomem_base;
+ port->iotype = old_serial_port[i].io_type;
+ port->regshift = old_serial_port[i].iomem_reg_shift;
+ set_io_from_upio(port);
+ port->irqflags |= irqflag;
if (serial8250_isa_config != NULL)
serial8250_isa_config(i, &up->port, &up->capabilities);
@@ -2762,9 +2827,12 @@ static void
serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type)
{
up->port.type = type;
- up->port.fifosize = uart_config[type].fifo_size;
- up->capabilities = uart_config[type].flags;
- up->tx_loadsz = uart_config[type].tx_loadsz;
+ if (!up->port.fifosize)
+ up->port.fifosize = uart_config[type].fifo_size;
+ if (!up->tx_loadsz)
+ up->tx_loadsz = uart_config[type].tx_loadsz;
+ if (!up->capabilities)
+ up->capabilities = uart_config[type].flags;
}
static void __init
@@ -2774,13 +2842,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
- up->cur_iotype = 0xFF;
- }
- serial8250_isa_init_ports();
-
- for (i = 0; i < nr_uarts; i++) {
- struct uart_8250_port *up = &serial8250_ports[i];
+ if (up->port.dev)
+ continue;
up->port.dev = dev;
@@ -2799,7 +2863,7 @@ static void serial8250_console_putchar(struct uart_port *port, int ch)
container_of(port, struct uart_8250_port, port);
wait_for_xmitr(up, UART_LSR_THRE);
- serial_out(up, UART_TX, ch);
+ serial_port_out(port, UART_TX, ch);
}
/*
@@ -2812,39 +2876,36 @@ static void
serial8250_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_8250_port *up = &serial8250_ports[co->index];
+ struct uart_port *port = &up->port;
unsigned long flags;
unsigned int ier;
int locked = 1;
touch_nmi_watchdog();
- local_irq_save(flags);
- if (up->port.sysrq) {
- /* serial8250_handle_irq() already took the lock */
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&up->port.lock);
- } else
- spin_lock(&up->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
*/
- ier = serial_in(up, UART_IER);
+ ier = serial_port_in(port, UART_IER);
if (up->capabilities & UART_CAP_UUE)
- serial_out(up, UART_IER, UART_IER_UUE);
+ serial_port_out(port, UART_IER, UART_IER_UUE);
else
- serial_out(up, UART_IER, 0);
+ serial_port_out(port, UART_IER, 0);
- uart_console_write(&up->port, s, count, serial8250_console_putchar);
+ uart_console_write(port, s, count, serial8250_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and restore the IER
*/
wait_for_xmitr(up, BOTH_EMPTY);
- serial_out(up, UART_IER, ier);
+ serial_port_out(port, UART_IER, ier);
/*
* The receive handling will happen properly because the
@@ -2857,8 +2918,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
serial8250_modem_status(up);
if (locked)
- spin_unlock(&up->port.lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
}
static int __init serial8250_console_setup(struct console *co, char *options)
@@ -2904,9 +2964,6 @@ static struct console serial8250_console = {
static int __init serial8250_console_init(void)
{
- if (nr_uarts > UART_NR)
- nr_uarts = UART_NR;
-
serial8250_isa_init_ports();
register_console(&serial8250_console);
return 0;
@@ -3002,17 +3059,18 @@ void serial8250_suspend_port(int line)
void serial8250_resume_port(int line)
{
struct uart_8250_port *up = &serial8250_ports[line];
+ struct uart_port *port = &up->port;
if (up->capabilities & UART_NATSEMI) {
/* Ensure it's still in high speed mode */
- serial_outp(up, UART_LCR, 0xE0);
+ serial_port_out(port, UART_LCR, 0xE0);
ns16550a_goto_highspeed(up);
- serial_outp(up, UART_LCR, 0);
- up->port.uartclk = 921600*16;
+ serial_port_out(port, UART_LCR, 0);
+ port->uartclk = 921600*16;
}
- uart_resume_port(&serial8250_reg, &up->port);
+ uart_resume_port(&serial8250_reg, port);
}
/*
@@ -3020,38 +3078,39 @@ void serial8250_resume_port(int line)
* list is terminated with a zero flags entry, which means we expect
* all entries to have at least UPF_BOOT_AUTOCONF set.
*/
-static int __devinit serial8250_probe(struct platform_device *dev)
+static int serial8250_probe(struct platform_device *dev)
{
- struct plat_serial8250_port *p = dev->dev.platform_data;
- struct uart_port port;
+ struct plat_serial8250_port *p = dev_get_platdata(&dev->dev);
+ struct uart_8250_port uart;
int ret, i, irqflag = 0;
- memset(&port, 0, sizeof(struct uart_port));
+ memset(&uart, 0, sizeof(uart));
if (share_irqs)
irqflag = IRQF_SHARED;
for (i = 0; p && p->flags != 0; p++, i++) {
- port.iobase = p->iobase;
- port.membase = p->membase;
- port.irq = p->irq;
- port.irqflags = p->irqflags;
- port.uartclk = p->uartclk;
- port.regshift = p->regshift;
- port.iotype = p->iotype;
- port.flags = p->flags;
- port.mapbase = p->mapbase;
- port.hub6 = p->hub6;
- port.private_data = p->private_data;
- port.type = p->type;
- port.serial_in = p->serial_in;
- port.serial_out = p->serial_out;
- port.handle_irq = p->handle_irq;
- port.set_termios = p->set_termios;
- port.pm = p->pm;
- port.dev = &dev->dev;
- port.irqflags |= irqflag;
- ret = serial8250_register_port(&port);
+ uart.port.iobase = p->iobase;
+ uart.port.membase = p->membase;
+ uart.port.irq = p->irq;
+ uart.port.irqflags = p->irqflags;
+ uart.port.uartclk = p->uartclk;
+ uart.port.regshift = p->regshift;
+ uart.port.iotype = p->iotype;
+ uart.port.flags = p->flags;
+ uart.port.mapbase = p->mapbase;
+ uart.port.hub6 = p->hub6;
+ uart.port.private_data = p->private_data;
+ uart.port.type = p->type;
+ uart.port.serial_in = p->serial_in;
+ uart.port.serial_out = p->serial_out;
+ uart.port.handle_irq = p->handle_irq;
+ uart.port.handle_break = p->handle_break;
+ uart.port.set_termios = p->set_termios;
+ uart.port.pm = p->pm;
+ uart.port.dev = &dev->dev;
+ uart.port.irqflags |= irqflag;
+ ret = serial8250_register_8250_port(&uart);
if (ret < 0) {
dev_err(&dev->dev, "unable to register port at index %d "
"(IO%lx MEM%llx IRQ%d): %d\n", i,
@@ -3065,7 +3124,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
/*
* Remove serial ports registered against a platform device.
*/
-static int __devexit serial8250_remove(struct platform_device *dev)
+static int serial8250_remove(struct platform_device *dev)
{
int i;
@@ -3108,7 +3167,7 @@ static int serial8250_resume(struct platform_device *dev)
static struct platform_driver serial8250_isa_driver = {
.probe = serial8250_probe,
- .remove = __devexit_p(serial8250_remove),
+ .remove = serial8250_remove,
.suspend = serial8250_suspend,
.resume = serial8250_resume,
.driver = {
@@ -3124,7 +3183,7 @@ static struct platform_driver serial8250_isa_driver = {
static struct platform_device *serial8250_isa_devs;
/*
- * serial8250_register_port and serial8250_unregister_port allows for
+ * serial8250_register_8250_port and serial8250_unregister_port allows for
* 16x50 serial ports to be configured at run-time, to support PCMCIA
* modems and PCI multiport cards.
*/
@@ -3163,8 +3222,8 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
}
/**
- * serial8250_register_port - register a serial port
- * @port: serial port template
+ * serial8250_register_8250_port - register a serial port
+ * @up: serial port template
*
* Configure the serial port specified by the request. If the
* port exists and is in use, it is hung up and unregistered
@@ -3175,50 +3234,68 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
*
* On success the port is ready to use and the line number is returned.
*/
-int serial8250_register_port(struct uart_port *port)
+int serial8250_register_8250_port(struct uart_8250_port *up)
{
struct uart_8250_port *uart;
int ret = -ENOSPC;
- if (port->uartclk == 0)
+ if (up->port.uartclk == 0)
return -EINVAL;
mutex_lock(&serial_mutex);
- uart = serial8250_find_match_or_unused(port);
- if (uart) {
- uart_remove_one_port(&serial8250_reg, &uart->port);
-
- uart->port.iobase = port->iobase;
- uart->port.membase = port->membase;
- uart->port.irq = port->irq;
- uart->port.irqflags = port->irqflags;
- uart->port.uartclk = port->uartclk;
- uart->port.fifosize = port->fifosize;
- uart->port.regshift = port->regshift;
- uart->port.iotype = port->iotype;
- uart->port.flags = port->flags | UPF_BOOT_AUTOCONF;
- uart->port.mapbase = port->mapbase;
- uart->port.private_data = port->private_data;
- if (port->dev)
- uart->port.dev = port->dev;
-
- if (port->flags & UPF_FIXED_TYPE)
- serial8250_init_fixed_type_port(uart, port->type);
+ uart = serial8250_find_match_or_unused(&up->port);
+ if (uart && uart->port.type != PORT_8250_CIR) {
+ if (uart->port.dev)
+ uart_remove_one_port(&serial8250_reg, &uart->port);
+
+ uart->port.iobase = up->port.iobase;
+ uart->port.membase = up->port.membase;
+ uart->port.irq = up->port.irq;
+ uart->port.irqflags = up->port.irqflags;
+ uart->port.uartclk = up->port.uartclk;
+ uart->port.fifosize = up->port.fifosize;
+ uart->port.regshift = up->port.regshift;
+ uart->port.iotype = up->port.iotype;
+ uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF;
+ uart->bugs = up->bugs;
+ uart->port.mapbase = up->port.mapbase;
+ uart->port.private_data = up->port.private_data;
+ uart->port.fifosize = up->port.fifosize;
+ uart->tx_loadsz = up->tx_loadsz;
+ uart->capabilities = up->capabilities;
+
+ /* Take tx_loadsz from fifosize if it wasn't set separately */
+ if (uart->port.fifosize && !uart->tx_loadsz)
+ uart->tx_loadsz = uart->port.fifosize;
+
+ if (up->port.dev)
+ uart->port.dev = up->port.dev;
+
+ if (up->port.flags & UPF_FIXED_TYPE)
+ serial8250_init_fixed_type_port(uart, up->port.type);
set_io_from_upio(&uart->port);
/* Possibly override default I/O functions. */
- if (port->serial_in)
- uart->port.serial_in = port->serial_in;
- if (port->serial_out)
- uart->port.serial_out = port->serial_out;
- if (port->handle_irq)
- uart->port.handle_irq = port->handle_irq;
+ if (up->port.serial_in)
+ uart->port.serial_in = up->port.serial_in;
+ if (up->port.serial_out)
+ uart->port.serial_out = up->port.serial_out;
+ if (up->port.handle_irq)
+ uart->port.handle_irq = up->port.handle_irq;
/* Possibly override set_termios call */
- if (port->set_termios)
- uart->port.set_termios = port->set_termios;
- if (port->pm)
- uart->port.pm = port->pm;
+ if (up->port.set_termios)
+ uart->port.set_termios = up->port.set_termios;
+ if (up->port.pm)
+ uart->port.pm = up->port.pm;
+ if (up->port.handle_break)
+ uart->port.handle_break = up->port.handle_break;
+ if (up->dl_read)
+ uart->dl_read = up->dl_read;
+ if (up->dl_write)
+ uart->dl_write = up->dl_write;
+ if (up->dma)
+ uart->dma = up->dma;
if (serial8250_isa_config != NULL)
serial8250_isa_config(0, &uart->port,
@@ -3232,7 +3309,7 @@ int serial8250_register_port(struct uart_port *port)
return ret;
}
-EXPORT_SYMBOL(serial8250_register_port);
+EXPORT_SYMBOL(serial8250_register_8250_port);
/**
* serial8250_unregister_port - remove a 16x50 serial port at runtime
@@ -3264,8 +3341,7 @@ static int __init serial8250_init(void)
{
int ret;
- if (nr_uarts > UART_NR)
- nr_uarts = UART_NR;
+ serial8250_isa_init_ports();
printk(KERN_INFO "Serial: 8250/16550 driver, "
"%d ports, IRQ sharing %sabled\n", nr_uarts,
@@ -3280,11 +3356,15 @@ static int __init serial8250_init(void)
if (ret)
goto out;
+ ret = serial8250_pnp_init();
+ if (ret)
+ goto unreg_uart_drv;
+
serial8250_isa_devs = platform_device_alloc("serial8250",
PLAT8250_DEV_LEGACY);
if (!serial8250_isa_devs) {
ret = -ENOMEM;
- goto unreg_uart_drv;
+ goto unreg_pnp;
}
ret = platform_device_add(serial8250_isa_devs);
@@ -3300,6 +3380,8 @@ static int __init serial8250_init(void)
platform_device_del(serial8250_isa_devs);
put_dev:
platform_device_put(serial8250_isa_devs);
+unreg_pnp:
+ serial8250_pnp_exit();
unreg_uart_drv:
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
@@ -3324,6 +3406,8 @@ static void __exit serial8250_exit(void)
platform_driver_unregister(&serial8250_isa_driver);
platform_device_unregister(isa_dev);
+ serial8250_pnp_exit();
+
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
#else
@@ -3355,3 +3439,34 @@ module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444);
MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
#endif
MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
+
+#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS
+#ifndef MODULE
+/* This module was renamed to 8250_core in 3.7. Keep the old "8250" name
+ * working as well for the module options so we don't break people. We
+ * need to keep the names identical and the convenient macros will happily
+ * refuse to let us do that by failing the build with redefinition errors
+ * of global variables. So we stick them inside a dummy function to avoid
+ * those conflicts. The options still get parsed, and the redefined
+ * MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive.
+ *
+ * This is hacky. I'm sorry.
+ */
+static void __used s8250_options(void)
+{
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "8250_core."
+
+ module_param_cb(share_irqs, &param_ops_uint, &share_irqs, 0644);
+ module_param_cb(nr_uarts, &param_ops_uint, &nr_uarts, 0644);
+ module_param_cb(skip_txen_test, &param_ops_uint, &skip_txen_test, 0644);
+#ifdef CONFIG_SERIAL_8250_RSA
+ __module_param_call(MODULE_PARAM_PREFIX, probe_rsa,
+ &param_array_ops, .arr = &__param_arr_probe_rsa,
+ 0444, -1);
+#endif
+}
+#else
+MODULE_ALIAS("8250_core");
+#endif
+#endif
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
new file mode 100644
index 00000000000..148ffe4c232
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -0,0 +1,244 @@
+/*
+ * 8250_dma.c - DMA Engine API support for 8250.c
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * 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/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/dma-mapping.h>
+
+#include "8250.h"
+
+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;
+ 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;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&p->port);
+
+ 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)
+{
+ struct uart_8250_port *p = param;
+ struct uart_8250_dma *dma = p->dma;
+ struct tty_port *tty_port = &p->port.state->port;
+ struct dma_tx_state state;
+ int count;
+
+ dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
+ dma->rx_size, DMA_FROM_DEVICE);
+
+ dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+ dmaengine_terminate_all(dma->rxchan);
+
+ count = dma->rx_size - state.residue;
+
+ tty_insert_flip_string(tty_port, dma->rx_buf, count);
+ p->port.icount.rx += count;
+
+ tty_flip_buffer_push(tty_port);
+}
+
+int serial8250_tx_dma(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+ struct circ_buf *xmit = &p->port.state->xmit;
+ struct dma_async_tx_descriptor *desc;
+
+ if (uart_tx_stopped(&p->port) || dma->tx_running ||
+ uart_circ_empty(xmit))
+ return 0;
+
+ dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+ desc = dmaengine_prep_slave_single(dma->txchan,
+ dma->tx_addr + xmit->tail,
+ dma->tx_size, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -EBUSY;
+
+ dma->tx_running = 1;
+
+ desc->callback = __dma_tx_complete;
+ desc->callback_param = p;
+
+ dma->tx_cookie = dmaengine_submit(desc);
+
+ dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ dma_async_issue_pending(dma->txchan);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(serial8250_tx_dma);
+
+int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
+{
+ struct uart_8250_dma *dma = p->dma;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_tx_state state;
+ int dma_status;
+
+ dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+
+ switch (iir & 0x3f) {
+ case UART_IIR_RLSI:
+ /* 8250_core handles errors and break interrupts */
+ return -EIO;
+ case UART_IIR_RX_TIMEOUT:
+ /*
+ * If RCVR FIFO trigger level was not reached, complete the
+ * transfer and let 8250_core copy the remaining data.
+ */
+ if (dma_status == DMA_IN_PROGRESS) {
+ dmaengine_pause(dma->rxchan);
+ __dma_rx_complete(p);
+ }
+ return -ETIMEDOUT;
+ default:
+ break;
+ }
+
+ if (dma_status)
+ return 0;
+
+ desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
+ dma->rx_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = __dma_rx_complete;
+ desc->callback_param = p;
+
+ dma->rx_cookie = dmaengine_submit(desc);
+
+ dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
+ dma->rx_size, DMA_FROM_DEVICE);
+
+ dma_async_issue_pending(dma->rxchan);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(serial8250_rx_dma);
+
+int serial8250_request_dma(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+ dma_cap_mask_t mask;
+
+ /* Default slave configuration parameters */
+ dma->rxconf.direction = DMA_DEV_TO_MEM;
+ dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma->rxconf.src_addr = p->port.mapbase + UART_RX;
+
+ dma->txconf.direction = DMA_MEM_TO_DEV;
+ dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma->txconf.dst_addr = p->port.mapbase + UART_TX;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Get a channel for RX */
+ dma->rxchan = dma_request_slave_channel_compat(mask,
+ dma->fn, dma->rx_param,
+ p->port.dev, "rx");
+ if (!dma->rxchan)
+ return -ENODEV;
+
+ dmaengine_slave_config(dma->rxchan, &dma->rxconf);
+
+ /* Get a channel for TX */
+ dma->txchan = dma_request_slave_channel_compat(mask,
+ dma->fn, dma->tx_param,
+ p->port.dev, "tx");
+ if (!dma->txchan) {
+ dma_release_channel(dma->rxchan);
+ return -ENODEV;
+ }
+
+ dmaengine_slave_config(dma->txchan, &dma->txconf);
+
+ /* RX buffer */
+ if (!dma->rx_size)
+ dma->rx_size = PAGE_SIZE;
+
+ dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size,
+ &dma->rx_addr, GFP_KERNEL);
+ 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);
+
+void serial8250_release_dma(struct uart_8250_port *p)
+{
+ struct uart_8250_dma *dma = p->dma;
+
+ if (!dma)
+ return;
+
+ /* Release RX resources */
+ dmaengine_terminate_all(dma->rxchan);
+ dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf,
+ dma->rx_addr);
+ dma_release_channel(dma->rxchan);
+ dma->rxchan = NULL;
+
+ /* Release TX resources */
+ dmaengine_terminate_all(dma->txchan);
+ dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ dma_release_channel(dma->txchan);
+ dma->txchan = NULL;
+ dma->tx_running = 0;
+
+ dev_dbg_ratelimited(p->port.dev, "dma channels released\n");
+}
+EXPORT_SYMBOL_GPL(serial8250_release_dma);
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index f574eef3075..51b307aab75 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -2,6 +2,7 @@
* Synopsys DesignWare 8250 driver.
*
* Copyright 2011 Picochip, Jamie Iles.
+ * Copyright 2013 Intel Corporation
*
* 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
@@ -13,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>
@@ -24,51 +24,195 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/byteorder.h>
+
+#include "8250.h"
+
+/* Offsets for the DesignWare specific registers */
+#define DW_UART_USR 0x1f /* UART Status Register */
+#define DW_UART_CPR 0xf4 /* Component Parameter Register */
+#define DW_UART_UCV 0xf8 /* UART Component Version */
+
+/* Component Parameter Register bits */
+#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
+#define DW_UART_CPR_AFCE_MODE (1 << 4)
+#define DW_UART_CPR_THRE_MODE (1 << 5)
+#define DW_UART_CPR_SIR_MODE (1 << 6)
+#define DW_UART_CPR_SIR_LP_MODE (1 << 7)
+#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8)
+#define DW_UART_CPR_FIFO_ACCESS (1 << 9)
+#define DW_UART_CPR_FIFO_STAT (1 << 10)
+#define DW_UART_CPR_SHADOW (1 << 11)
+#define DW_UART_CPR_ENCODED_PARMS (1 << 12)
+#define DW_UART_CPR_DMA_EXTRA (1 << 13)
+#define DW_UART_CPR_FIFO_MODE (0xff << 16)
+/* Helper for fifo size calculation */
+#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16)
+
struct dw8250_data {
- int last_lcr;
- int line;
+ 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;
+
+ writeb(value, p->membase + (offset << p->regshift));
- offset <<= p->regshift;
- writeb(value, p->membase + offset);
+ /* 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. */
+static void dw8250_serial_out_rb(struct uart_port *p, int offset, int value)
+{
+ dw8250_serial_out(p, offset, value);
+ dw8250_serial_in(p, UART_LCR);
}
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;
+
+ writel(value, p->membase + (offset << p->regshift));
- offset <<= p->regshift;
- writel(value, p->membase + offset);
+ /* 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);
}
-/* Offset for the DesignWare's UART Status Register. */
-#define UART_USR 0x1f
-
static int dw8250_handle_irq(struct uart_port *p)
{
struct dw8250_data *d = p->private_data;
@@ -77,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. */
- (void)p->serial_in(p, UART_USR);
- p->serial_out(p, d->last_lcr, UART_LCR);
+ /* Clear the USR */
+ (void)p->serial_in(p, d->usr_reg);
return 1;
}
@@ -87,94 +230,314 @@ static int dw8250_handle_irq(struct uart_port *p)
return 0;
}
-static int __devinit dw8250_probe(struct platform_device *pdev)
+static void
+dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
{
- struct uart_port port = {};
- struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- struct device_node *np = pdev->dev.of_node;
- u32 val;
- struct dw8250_data *data;
+ if (!state)
+ pm_runtime_get_sync(port->dev);
- if (!regs || !irq) {
- dev_err(&pdev->dev, "no registers/irq defined\n");
- return -EINVAL;
+ serial8250_do_pm(port, state, old);
+
+ if (state)
+ 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;
+ u32 reg = readl(p->membase + DW_UART_UCV);
+
+ /*
+ * If the Component Version Register returns zero, we know that
+ * ADDITIONAL_FEATURES are not enabled. No need to go any further.
+ */
+ if (!reg)
+ return;
+
+ dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n",
+ (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
+
+ reg = readl(p->membase + DW_UART_CPR);
+ if (!reg)
+ return;
+
+ /* Select the type based on fifo */
+ if (reg & DW_UART_CPR_FIFO_MODE) {
+ p->type = PORT_16550A;
+ p->flags |= UPF_FIXED_TYPE;
+ p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
+ up->tx_loadsz = p->fifosize;
+ up->capabilities = UART_CAP_FIFO;
}
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- port.private_data = data;
-
- spin_lock_init(&port.lock);
- port.mapbase = regs->start;
- port.irq = irq->start;
- port.handle_irq = dw8250_handle_irq;
- port.type = PORT_8250;
- port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
- UPF_FIXED_PORT | UPF_FIXED_TYPE;
- port.dev = &pdev->dev;
-
- port.iotype = UPIO_MEM;
- port.serial_in = dw8250_serial_in;
- port.serial_out = dw8250_serial_out;
- if (!of_property_read_u32(np, "reg-io-width", &val)) {
+ if (reg & DW_UART_CPR_AFCE_MODE)
+ up->capabilities |= UART_CAP_AFE;
+}
+
+static int dw8250_probe_of(struct uart_port *p,
+ struct dw8250_data *data)
+{
+ struct device_node *np = p->dev->of_node;
+ u32 val;
+ bool has_ucv = true;
+
+ if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
+#ifdef __BIG_ENDIAN
+ /*
+ * Low order bits of these 64-bit registers, when
+ * accessed as a byte, are 7 bytes further down in the
+ * address space in big endian mode.
+ */
+ p->membase += 7;
+#endif
+ p->serial_out = dw8250_serial_out_rb;
+ p->flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+ p->type = PORT_OCTEON;
+ data->usr_reg = 0x27;
+ has_ucv = false;
+ } else if (!of_property_read_u32(np, "reg-io-width", &val)) {
switch (val) {
case 1:
break;
case 4:
- port.iotype = UPIO_MEM32;
- port.serial_in = dw8250_serial_in32;
- port.serial_out = dw8250_serial_out32;
+ p->iotype = UPIO_MEM32;
+ p->serial_in = dw8250_serial_in32;
+ p->serial_out = dw8250_serial_out32;
break;
default:
- dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n",
- val);
+ dev_err(p->dev, "unsupported reg-io-width (%u)\n", val);
return -EINVAL;
}
}
+ if (has_ucv)
+ dw8250_setup_port(container_of(p, struct uart_8250_port, port));
if (!of_property_read_u32(np, "reg-shift", &val))
- port.regshift = val;
+ p->regshift = val;
+
+ /* clock got configured through clk api, all done */
+ if (p->uartclk)
+ return 0;
+ /* try to find out clock frequency from DT as fallback */
if (of_property_read_u32(np, "clock-frequency", &val)) {
- dev_err(&pdev->dev, "no clock-frequency property set\n");
+ dev_err(p->dev, "clk or clock-frequency not defined\n");
+ return -EINVAL;
+ }
+ p->uartclk = val;
+
+ return 0;
+}
+
+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);
+
+ id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
+ if (!id)
+ return -ENODEV;
+
+ p->iotype = UPIO_MEM32;
+ p->serial_in = dw8250_serial_in32;
+ p->serial_out = dw8250_serial_out32;
+ p->regshift = 2;
+
+ 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;
+}
+
+static int dw8250_probe(struct platform_device *pdev)
+{
+ struct uart_8250_port uart = {};
+ struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ struct dw8250_data *data;
+ int err;
+
+ if (!regs || !irq) {
+ dev_err(&pdev->dev, "no registers/irq defined\n");
return -EINVAL;
}
- port.uartclk = val;
- data->line = serial8250_register_port(&port);
+ spin_lock_init(&uart.port.lock);
+ uart.port.mapbase = regs->start;
+ uart.port.irq = irq->start;
+ uart.port.handle_irq = dw8250_handle_irq;
+ uart.port.pm = dw8250_do_pm;
+ uart.port.type = PORT_8250;
+ uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
+ uart.port.dev = &pdev->dev;
+
+ uart.port.membase = devm_ioremap(&pdev->dev, regs->start,
+ resource_size(regs));
+ if (!uart.port.membase)
+ return -ENOMEM;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->usr_reg = DW_UART_USR;
+ data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(data->clk)) {
+ clk_prepare_enable(data->clk);
+ 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;
+ uart.port.private_data = data;
+
+ if (pdev->dev.of_node) {
+ err = dw8250_probe_of(&uart.port, data);
+ if (err)
+ return err;
+ } else if (ACPI_HANDLE(&pdev->dev)) {
+ err = dw8250_probe_acpi(&uart, data);
+ if (err)
+ return err;
+ } else {
+ return -ENODEV;
+ }
+
+ data->line = serial8250_register_8250_port(&uart);
if (data->line < 0)
return data->line;
platform_set_drvdata(pdev, data);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
return 0;
}
-static int __devexit dw8250_remove(struct platform_device *pdev)
+static int dw8250_remove(struct platform_device *pdev)
{
struct dw8250_data *data = platform_get_drvdata(pdev);
+ pm_runtime_get_sync(&pdev->dev);
+
serial8250_unregister_port(data->line);
+ if (!IS_ERR(data->clk))
+ clk_disable_unprepare(data->clk);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
return 0;
}
-static const struct of_device_id dw8250_match[] = {
+#ifdef CONFIG_PM_SLEEP
+static int dw8250_suspend(struct device *dev)
+{
+ struct dw8250_data *data = dev_get_drvdata(dev);
+
+ serial8250_suspend_port(data->line);
+
+ return 0;
+}
+
+static int dw8250_resume(struct device *dev)
+{
+ struct dw8250_data *data = dev_get_drvdata(dev);
+
+ serial8250_resume_port(data->line);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int dw8250_runtime_suspend(struct device *dev)
+{
+ struct dw8250_data *data = dev_get_drvdata(dev);
+
+ if (!IS_ERR(data->clk))
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+static int dw8250_runtime_resume(struct device *dev)
+{
+ struct dw8250_data *data = dev_get_drvdata(dev);
+
+ if (!IS_ERR(data->clk))
+ clk_prepare_enable(data->clk);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops dw8250_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume)
+ SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL)
+};
+
+static const struct of_device_id dw8250_of_match[] = {
{ .compatible = "snps,dw-apb-uart" },
+ { .compatible = "cavium,octeon-3860-uart" },
{ /* Sentinel */ }
};
-MODULE_DEVICE_TABLE(of, dw8250_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 },
+ { "INT3434", 0 },
+ { "INT3435", 0 },
+ { "80860F0A", (kernel_ulong_t)&byt_8250_desc},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
static struct platform_driver dw8250_platform_driver = {
.driver = {
.name = "dw-apb-uart",
.owner = THIS_MODULE,
- .of_match_table = dw8250_match,
+ .pm = &dw8250_pm_ops,
+ .of_match_table = dw8250_of_match,
+ .acpi_match_table = ACPI_PTR(dw8250_acpi_match),
},
.probe = dw8250_probe,
- .remove = __devexit_p(dw8250_remove),
+ .remove = dw8250_remove,
};
module_platform_driver(dw8250_platform_driver);
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index eaafb98debe..4858b8a99d3 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -35,20 +35,10 @@
#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 earlycon_device *early_device;
-static struct early_serial8250_device early_device;
-
-static unsigned int __init serial_in(struct uart_port *port, int offset)
+unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset)
{
switch (port->iotype) {
case UPIO_MEM:
@@ -62,7 +52,7 @@ static unsigned int __init serial_in(struct uart_port *port, int offset)
}
}
-static void __init serial_out(struct uart_port *port, int offset, int value)
+void __weak __init serial8250_early_out(struct uart_port *port, int offset, int value)
{
switch (port->iotype) {
case UPIO_MEM:
@@ -84,7 +74,7 @@ static void __init wait_for_xmitr(struct uart_port *port)
unsigned int status;
for (;;) {
- status = serial_in(port, UART_LSR);
+ status = serial8250_early_in(port, UART_LSR);
if ((status & BOTH_EMPTY) == BOTH_EMPTY)
return;
cpu_relax();
@@ -94,24 +84,24 @@ static void __init wait_for_xmitr(struct uart_port *port)
static void __init serial_putc(struct uart_port *port, int c)
{
wait_for_xmitr(port);
- serial_out(port, UART_TX, c);
+ serial8250_early_out(port, UART_TX, 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 */
- ier = serial_in(port, UART_IER);
- serial_out(port, UART_IER, 0);
+ ier = serial8250_early_in(port, UART_IER);
+ serial8250_early_out(port, UART_IER, 0);
uart_console_write(port, s, count, serial_putc);
/* Wait for transmitter to become empty and restore the IER */
wait_for_xmitr(port);
- serial_out(port, UART_IER, ier);
+ serial8250_early_out(port, UART_IER, ier);
}
static unsigned int __init probe_baud(struct uart_port *port)
@@ -119,156 +109,74 @@ static unsigned int __init probe_baud(struct uart_port *port)
unsigned char lcr, dll, dlm;
unsigned int quot;
- lcr = serial_in(port, UART_LCR);
- serial_out(port, UART_LCR, lcr | UART_LCR_DLAB);
- dll = serial_in(port, UART_DLL);
- dlm = serial_in(port, UART_DLM);
- serial_out(port, UART_LCR, lcr);
+ lcr = serial8250_early_in(port, UART_LCR);
+ serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB);
+ dll = serial8250_early_in(port, UART_DLL);
+ dlm = serial8250_early_in(port, UART_DLM);
+ serial8250_early_out(port, UART_LCR, lcr);
quot = (dlm << 8) | dll;
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;
unsigned char c;
- serial_out(port, UART_LCR, 0x3); /* 8n1 */
- serial_out(port, UART_IER, 0); /* no interrupt */
- serial_out(port, UART_FCR, 0); /* no fifo */
- serial_out(port, UART_MCR, 0x3); /* DTR + RTS */
-
- divisor = port->uartclk / (16 * device->baud);
- c = serial_in(port, UART_LCR);
- serial_out(port, UART_LCR, c | UART_LCR_DLAB);
- serial_out(port, UART_DLL, divisor & 0xff);
- serial_out(port, UART_DLM, (divisor >> 8) & 0xff);
- serial_out(port, UART_LCR, c & ~UART_LCR_DLAB);
+ serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */
+ serial8250_early_out(port, UART_IER, 0); /* no interrupt */
+ serial8250_early_out(port, UART_FCR, 0); /* no fifo */
+ serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */
+
+ divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
+ c = serial8250_early_in(port, UART_LCR);
+ serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);
+ serial8250_early_out(port, UART_DLL, divisor & 0xff);
+ serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);
+ 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));
- strncpy(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
new file mode 100644
index 00000000000..56c87232b6a
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_em.c
@@ -0,0 +1,174 @@
+/*
+ * Renesas Emma Mobile 8250 driver
+ *
+ * Copyright (C) 2012 Magnus Damm
+ *
+ * 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
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include "8250.h"
+
+#define UART_DLL_EM 9
+#define UART_DLM_EM 10
+
+struct serial8250_em_priv {
+ struct clk *sclk;
+ int line;
+};
+
+static void serial8250_em_serial_out(struct uart_port *p, int offset, int value)
+{
+ switch (offset) {
+ case UART_TX: /* TX @ 0x00 */
+ writeb(value, p->membase);
+ break;
+ case UART_FCR: /* FCR @ 0x0c (+1) */
+ case UART_LCR: /* LCR @ 0x10 (+1) */
+ case UART_MCR: /* MCR @ 0x14 (+1) */
+ case UART_SCR: /* SCR @ 0x20 (+1) */
+ writel(value, p->membase + ((offset + 1) << 2));
+ break;
+ case UART_IER: /* IER @ 0x04 */
+ value &= 0x0f; /* only 4 valid bits - not Xscale */
+ /* fall-through */
+ case UART_DLL_EM: /* DLL @ 0x24 (+9) */
+ case UART_DLM_EM: /* DLM @ 0x28 (+9) */
+ writel(value, p->membase + (offset << 2));
+ }
+}
+
+static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset)
+{
+ switch (offset) {
+ case UART_RX: /* RX @ 0x00 */
+ return readb(p->membase);
+ case UART_MCR: /* MCR @ 0x14 (+1) */
+ case UART_LSR: /* LSR @ 0x18 (+1) */
+ case UART_MSR: /* MSR @ 0x1c (+1) */
+ case UART_SCR: /* SCR @ 0x20 (+1) */
+ return readl(p->membase + ((offset + 1) << 2));
+ case UART_IER: /* IER @ 0x04 */
+ case UART_IIR: /* IIR @ 0x08 */
+ case UART_DLL_EM: /* DLL @ 0x24 (+9) */
+ case UART_DLM_EM: /* DLM @ 0x28 (+9) */
+ return readl(p->membase + (offset << 2));
+ }
+ return 0;
+}
+
+static int serial8250_em_serial_dl_read(struct uart_8250_port *up)
+{
+ return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8;
+}
+
+static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value)
+{
+ serial_out(up, UART_DLL_EM, value & 0xff);
+ serial_out(up, UART_DLM_EM, value >> 8 & 0xff);
+}
+
+static int serial8250_em_probe(struct platform_device *pdev)
+{
+ struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ struct serial8250_em_priv *priv;
+ struct uart_8250_port up;
+ int ret;
+
+ if (!regs || !irq) {
+ dev_err(&pdev->dev, "missing registers or irq\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "unable to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ priv->sclk = devm_clk_get(&pdev->dev, "sclk");
+ if (IS_ERR(priv->sclk)) {
+ dev_err(&pdev->dev, "unable to get clock\n");
+ return PTR_ERR(priv->sclk);
+ }
+
+ memset(&up, 0, sizeof(up));
+ up.port.mapbase = regs->start;
+ up.port.irq = irq->start;
+ up.port.type = PORT_UNKNOWN;
+ up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP;
+ up.port.dev = &pdev->dev;
+ up.port.private_data = priv;
+
+ clk_prepare_enable(priv->sclk);
+ up.port.uartclk = clk_get_rate(priv->sclk);
+
+ up.port.iotype = UPIO_MEM32;
+ up.port.serial_in = serial8250_em_serial_in;
+ up.port.serial_out = serial8250_em_serial_out;
+ up.dl_read = serial8250_em_serial_dl_read;
+ up.dl_write = serial8250_em_serial_dl_write;
+
+ ret = serial8250_register_8250_port(&up);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register 8250 port\n");
+ clk_disable_unprepare(priv->sclk);
+ return ret;
+ }
+
+ priv->line = ret;
+ platform_set_drvdata(pdev, priv);
+ return 0;
+}
+
+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_unprepare(priv->sclk);
+ return 0;
+}
+
+static const struct of_device_id serial8250_em_dt_ids[] = {
+ { .compatible = "renesas,em-uart", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, serial8250_em_dt_ids);
+
+static struct platform_driver serial8250_em_platform_driver = {
+ .driver = {
+ .name = "serial8250-em",
+ .of_match_table = serial8250_em_dt_ids,
+ .owner = THIS_MODULE,
+ },
+ .probe = serial8250_em_probe,
+ .remove = serial8250_em_remove,
+};
+
+module_platform_driver(serial8250_em_platform_driver);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c
index d8c0ffbfa6e..2e3ea1a70d7 100644
--- a/drivers/tty/serial/8250/8250_gsc.c
+++ b/drivers/tty/serial/8250/8250_gsc.c
@@ -26,10 +26,15 @@
static int __init serial_init_chip(struct parisc_device *dev)
{
- struct uart_port port;
+ struct uart_8250_port uart;
unsigned long address;
int err;
+#ifdef CONFIG_64BIT
+ if (!dev->irq && (dev->id.sversion == 0xad))
+ dev->irq = iosapic_serial_irq(dev);
+#endif
+
if (!dev->irq) {
/* We find some unattached serial ports by walking native
* busses. These should be silently ignored. Otherwise,
@@ -48,21 +53,22 @@ static int __init serial_init_chip(struct parisc_device *dev)
if (dev->id.sversion != 0x8d)
address += 0x800;
- memset(&port, 0, sizeof(port));
- port.iotype = UPIO_MEM;
+ memset(&uart, 0, sizeof(uart));
+ uart.port.iotype = UPIO_MEM;
/* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */
- port.uartclk = 7272727;
- port.mapbase = address;
- port.membase = ioremap_nocache(address, 16);
- port.irq = dev->irq;
- port.flags = UPF_BOOT_AUTOCONF;
- port.dev = &dev->dev;
-
- err = serial8250_register_port(&port);
+ uart.port.uartclk = (dev->id.sversion != 0xad) ?
+ 7272727 : 1843200;
+ uart.port.mapbase = address;
+ uart.port.membase = ioremap_nocache(address, 16);
+ uart.port.irq = dev->irq;
+ uart.port.flags = UPF_BOOT_AUTOCONF;
+ uart.port.dev = &dev->dev;
+
+ err = serial8250_register_8250_port(&uart);
if (err < 0) {
printk(KERN_WARNING
- "serial8250_register_port returned error %d\n", err);
- iounmap(port.membase);
+ "serial8250_register_8250_port returned error %d\n", err);
+ iounmap(uart.port.membase);
return err;
}
@@ -73,6 +79,7 @@ static struct parisc_device_id serial_tbl[] = {
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 },
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c },
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d },
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x000ad },
{ 0 }
};
diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c
index c13438c9301..5bdaf271d39 100644
--- a/drivers/tty/serial/8250/8250_hp300.c
+++ b/drivers/tty/serial/8250/8250_hp300.c
@@ -36,9 +36,9 @@ static struct hp300_port *hp300_ports;
#ifdef CONFIG_HPDCA
-static int __devinit hpdca_init_one(struct dio_dev *d,
+static int hpdca_init_one(struct dio_dev *d,
const struct dio_device_id *ent);
-static void __devexit hpdca_remove_one(struct dio_dev *d);
+static void hpdca_remove_one(struct dio_dev *d);
static struct dio_device_id hpdca_dio_tbl[] = {
{ DIO_ID_DCA0 },
@@ -52,7 +52,7 @@ static struct dio_driver hpdca_driver = {
.name = "hpdca",
.id_table = hpdca_dio_tbl,
.probe = hpdca_init_one,
- .remove = __devexit_p(hpdca_remove_one),
+ .remove = hpdca_remove_one,
};
#endif
@@ -159,10 +159,10 @@ int __init hp300_setup_serial_console(void)
#endif /* CONFIG_SERIAL_8250_CONSOLE */
#ifdef CONFIG_HPDCA
-static int __devinit hpdca_init_one(struct dio_dev *d,
+static int hpdca_init_one(struct dio_dev *d,
const struct dio_device_id *ent)
{
- struct uart_port port;
+ struct uart_8250_port uart;
int line;
#ifdef CONFIG_SERIAL_8250_CONSOLE
@@ -171,22 +171,22 @@ static int __devinit hpdca_init_one(struct dio_dev *d,
return 0;
}
#endif
- memset(&port, 0, sizeof(struct uart_port));
+ memset(&uart, 0, sizeof(uart));
/* Memory mapped I/O */
- port.iotype = UPIO_MEM;
- port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
- port.irq = d->ipl;
- port.uartclk = HPDCA_BAUD_BASE * 16;
- port.mapbase = (d->resource.start + UART_OFFSET);
- port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
- port.regshift = 1;
- port.dev = &d->dev;
- line = serial8250_register_port(&port);
+ uart.port.iotype = UPIO_MEM;
+ uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
+ uart.port.irq = d->ipl;
+ uart.port.uartclk = HPDCA_BAUD_BASE * 16;
+ uart.port.mapbase = (d->resource.start + UART_OFFSET);
+ uart.port.membase = (char *)(uart.port.mapbase + DIO_VIRADDRBASE);
+ uart.port.regshift = 1;
+ uart.port.dev = &d->dev;
+ line = serial8250_register_8250_port(&uart);
if (line < 0) {
printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
- " irq %d failed\n", d->scode, port.irq);
+ " irq %d failed\n", d->scode, uart.port.irq);
return -ENOMEM;
}
@@ -210,7 +210,7 @@ static int __init hp300_8250_init(void)
#ifdef CONFIG_HPAPCI
int line;
unsigned long base;
- struct uart_port uport;
+ struct uart_8250_port uart;
struct hp300_port *port;
int i;
#endif
@@ -248,26 +248,26 @@ static int __init hp300_8250_init(void)
if (!port)
return -ENOMEM;
- memset(&uport, 0, sizeof(struct uart_port));
+ memset(&uart, 0, sizeof(uart));
base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
/* Memory mapped I/O */
- uport.iotype = UPIO_MEM;
- uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
+ uart.port.iotype = UPIO_MEM;
+ uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
| UPF_BOOT_AUTOCONF;
/* XXX - no interrupt support yet */
- uport.irq = 0;
- uport.uartclk = HPAPCI_BAUD_BASE * 16;
- uport.mapbase = base;
- uport.membase = (char *)(base + DIO_VIRADDRBASE);
- uport.regshift = 2;
+ uart.port.irq = 0;
+ uart.port.uartclk = HPAPCI_BAUD_BASE * 16;
+ uart.port.mapbase = base;
+ uart.port.membase = (char *)(base + DIO_VIRADDRBASE);
+ uart.port.regshift = 2;
- line = serial8250_register_port(&uport);
+ line = serial8250_register_8250_port(&uart);
if (line < 0) {
printk(KERN_NOTICE "8250_hp300: register_serial() APCI"
- " %d irq %d failed\n", i, uport.irq);
+ " %d irq %d failed\n", i, uart.port.irq);
kfree(port);
continue;
}
@@ -288,7 +288,7 @@ static int __init hp300_8250_init(void)
}
#ifdef CONFIG_HPDCA
-static void __devexit hpdca_remove_one(struct dio_dev *d)
+static void hpdca_remove_one(struct dio_dev *d)
{
int line;
diff --git a/drivers/tty/serial/8250/8250_mca.c b/drivers/tty/serial/8250/8250_mca.c
deleted file mode 100644
index d20abf04541..00000000000
--- a/drivers/tty/serial/8250/8250_mca.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2005 Russell King.
- * Data taken from include/asm-i386/serial.h
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/mca.h>
-#include <linux/serial_8250.h>
-
-/*
- * FIXME: Should we be doing AUTO_IRQ here?
- */
-#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
-#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ
-#else
-#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST
-#endif
-
-#define PORT(_base,_irq) \
- { \
- .iobase = _base, \
- .irq = _irq, \
- .uartclk = 1843200, \
- .iotype = UPIO_PORT, \
- .flags = MCA_FLAGS, \
- }
-
-static struct plat_serial8250_port mca_data[] = {
- PORT(0x3220, 3),
- PORT(0x3228, 3),
- PORT(0x4220, 3),
- PORT(0x4228, 3),
- PORT(0x5220, 3),
- PORT(0x5228, 3),
- { },
-};
-
-static struct platform_device mca_device = {
- .name = "serial8250",
- .id = PLAT8250_DEV_MCA,
- .dev = {
- .platform_data = mca_data,
- },
-};
-
-static int __init mca_init(void)
-{
- if (!MCA_bus)
- return -ENODEV;
- return platform_device_register(&mca_device);
-}
-
-module_init(mca_init);
-
-MODULE_AUTHOR("Russell King");
-MODULE_DESCRIPTION("8250 serial probe module for MCA ports");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index da2b0b0a183..33137b3ba94 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -9,14 +9,15 @@
* 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>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/tty.h>
+#include <linux/serial_reg.h>
#include <linux/serial_core.h>
#include <linux/8250_pci.h>
#include <linux/bitops.h>
@@ -26,8 +27,6 @@
#include "8250.h"
-#undef SERIAL_DEBUG_PCI
-
/*
* init function returns:
* > 0 - number of ports
@@ -43,7 +42,7 @@ struct pci_serial_quirk {
int (*init)(struct pci_dev *dev);
int (*setup)(struct serial_private *,
const struct pciserial_board *,
- struct uart_port *, int);
+ struct uart_8250_port *, int);
void (*exit)(struct pci_dev *dev);
};
@@ -58,11 +57,11 @@ struct serial_private {
};
static int pci_default_setup(struct serial_private*,
- const struct pciserial_board*, struct uart_port*, int);
+ const struct pciserial_board*, struct uart_8250_port *, int);
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"
@@ -73,7 +72,7 @@ static void moan_device(const char *str, struct pci_dev *dev)
}
static int
-setup_port(struct serial_private *priv, struct uart_port *port,
+setup_port(struct serial_private *priv, struct uart_8250_port *port,
int bar, int offset, int regshift)
{
struct pci_dev *dev = priv->dev;
@@ -92,17 +91,17 @@ setup_port(struct serial_private *priv, struct uart_port *port,
if (!priv->remapped_bar[bar])
return -ENOMEM;
- port->iotype = UPIO_MEM;
- port->iobase = 0;
- port->mapbase = base + offset;
- port->membase = priv->remapped_bar[bar] + offset;
- port->regshift = regshift;
+ port->port.iotype = UPIO_MEM;
+ port->port.iobase = 0;
+ port->port.mapbase = base + offset;
+ port->port.membase = priv->remapped_bar[bar] + offset;
+ port->port.regshift = regshift;
} else {
- port->iotype = UPIO_PORT;
- port->iobase = base + offset;
- port->mapbase = 0;
- port->membase = NULL;
- port->regshift = 0;
+ port->port.iotype = UPIO_PORT;
+ port->port.iobase = base + offset;
+ port->port.mapbase = 0;
+ port->port.membase = NULL;
+ port->port.regshift = 0;
}
return 0;
}
@@ -112,7 +111,7 @@ setup_port(struct serial_private *priv, struct uart_port *port,
*/
static int addidata_apci7800_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
unsigned int bar = 0, offset = board->first_offset;
bar = FL_GET_BASE(board->flags);
@@ -139,7 +138,7 @@ static int addidata_apci7800_setup(struct serial_private *priv,
*/
static int
afavlab_setup(struct serial_private *priv, const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
unsigned int bar, offset = board->first_offset;
@@ -194,7 +193,7 @@ static int pci_hp_diva_init(struct pci_dev *dev)
static int
pci_hp_diva_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
unsigned int offset = board->first_offset;
unsigned int bar = FL_GET_BASE(board->flags);
@@ -232,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;
@@ -287,7 +286,7 @@ static int pci_plx9050_init(struct pci_dev *dev)
return 0;
}
-static void __devexit pci_plx9050_exit(struct pci_dev *dev)
+static void pci_plx9050_exit(struct pci_dev *dev)
{
u8 __iomem *p;
@@ -312,7 +311,7 @@ static void __devexit pci_plx9050_exit(struct pci_dev *dev)
#define NI8420_INT_ENABLE_REG 0x38
#define NI8420_INT_ENABLE_BIT 0x2000
-static void __devexit pci_ni8420_exit(struct pci_dev *dev)
+static void pci_ni8420_exit(struct pci_dev *dev)
{
void __iomem *p;
unsigned long base, len;
@@ -344,7 +343,7 @@ static void __devexit pci_ni8420_exit(struct pci_dev *dev)
#define MITE_LCIMR2_CLR_CPU_IE (1 << 30)
-static void __devexit pci_ni8430_exit(struct pci_dev *dev)
+static void pci_ni8430_exit(struct pci_dev *dev)
{
void __iomem *p;
unsigned long base, len;
@@ -369,7 +368,7 @@ static void __devexit pci_ni8430_exit(struct pci_dev *dev)
/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
static int
sbs_setup(struct serial_private *priv, const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
unsigned int bar, offset = board->first_offset;
@@ -421,7 +420,7 @@ static int sbs_init(struct pci_dev *dev)
* Disables the global interrupt of PMC-OctalPro
*/
-static void __devexit sbs_exit(struct pci_dev *dev)
+static void sbs_exit(struct pci_dev *dev)
{
u8 __iomem *p;
@@ -524,7 +523,7 @@ static int pci_siig_init(struct pci_dev *dev)
static int pci_siig_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0;
@@ -618,7 +617,7 @@ static int pci_timedia_init(struct pci_dev *dev)
static int
pci_timedia_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
unsigned int bar = 0, offset = board->first_offset;
@@ -652,7 +651,7 @@ pci_timedia_setup(struct serial_private *priv,
static int
titan_400l_800l_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
unsigned int bar, offset = board->first_offset;
@@ -753,7 +752,7 @@ static int pci_ni8430_init(struct pci_dev *dev)
static int
pci_ni8430_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
void __iomem *p;
unsigned long base, len;
@@ -780,11 +779,12 @@ pci_ni8430_setup(struct serial_private *priv,
static int pci_netmos_9900_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
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)
*/
@@ -826,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;
}
}
@@ -930,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;
}
@@ -990,7 +990,7 @@ static int pci_ite887x_init(struct pci_dev *dev)
return ret;
}
-static void __devexit pci_ite887x_exit(struct pci_dev *dev)
+static void pci_ite887x_exit(struct pci_dev *dev)
{
u32 ioport;
/* the ioport is bit 0-15 in POSIO0R */
@@ -1023,18 +1023,272 @@ 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;
}
-static int
-pci_default_setup(struct serial_private *priv,
+static int pci_asix_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ port->bugs |= UART_BUG_PARITY;
+ return pci_default_setup(priv, board, port, idx);
+}
+
+/* Quatech devices have their own extra interface features */
+
+struct quatech_feature {
+ u16 devid;
+ bool amcc;
+};
+
+#define QPCR_TEST_FOR1 0x3F
+#define QPCR_TEST_GET1 0x00
+#define QPCR_TEST_FOR2 0x40
+#define QPCR_TEST_GET2 0x40
+#define QPCR_TEST_FOR3 0x80
+#define QPCR_TEST_GET3 0x40
+#define QPCR_TEST_FOR4 0xC0
+#define QPCR_TEST_GET4 0x80
+
+#define QOPR_CLOCK_X1 0x0000
+#define QOPR_CLOCK_X2 0x0001
+#define QOPR_CLOCK_X4 0x0002
+#define QOPR_CLOCK_X8 0x0003
+#define QOPR_CLOCK_RATE_MASK 0x0003
+
+
+static struct quatech_feature quatech_cards[] = {
+ { PCI_DEVICE_ID_QUATECH_QSC100, 1 },
+ { PCI_DEVICE_ID_QUATECH_DSC100, 1 },
+ { PCI_DEVICE_ID_QUATECH_DSC100E, 0 },
+ { PCI_DEVICE_ID_QUATECH_DSC200, 1 },
+ { PCI_DEVICE_ID_QUATECH_DSC200E, 0 },
+ { PCI_DEVICE_ID_QUATECH_ESC100D, 1 },
+ { PCI_DEVICE_ID_QUATECH_ESC100M, 1 },
+ { PCI_DEVICE_ID_QUATECH_QSCP100, 1 },
+ { PCI_DEVICE_ID_QUATECH_DSCP100, 1 },
+ { PCI_DEVICE_ID_QUATECH_QSCP200, 1 },
+ { PCI_DEVICE_ID_QUATECH_DSCP200, 1 },
+ { PCI_DEVICE_ID_QUATECH_ESCLP100, 0 },
+ { PCI_DEVICE_ID_QUATECH_QSCLP100, 0 },
+ { PCI_DEVICE_ID_QUATECH_DSCLP100, 0 },
+ { PCI_DEVICE_ID_QUATECH_SSCLP100, 0 },
+ { PCI_DEVICE_ID_QUATECH_QSCLP200, 0 },
+ { PCI_DEVICE_ID_QUATECH_DSCLP200, 0 },
+ { PCI_DEVICE_ID_QUATECH_SSCLP200, 0 },
+ { PCI_DEVICE_ID_QUATECH_SPPXP_100, 0 },
+ { 0, }
+};
+
+static int pci_quatech_amcc(u16 devid)
+{
+ struct quatech_feature *qf = &quatech_cards[0];
+ while (qf->devid) {
+ if (qf->devid == devid)
+ return qf->amcc;
+ qf++;
+ }
+ pr_err("quatech: unknown port type '0x%04X'.\n", devid);
+ return 0;
+};
+
+static int pci_quatech_rqopr(struct uart_8250_port *port)
+{
+ unsigned long base = port->port.iobase;
+ u8 LCR, val;
+
+ LCR = inb(base + UART_LCR);
+ outb(0xBF, base + UART_LCR);
+ val = inb(base + UART_SCR);
+ outb(LCR, base + UART_LCR);
+ return val;
+}
+
+static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr)
+{
+ unsigned long base = port->port.iobase;
+ u8 LCR, val;
+
+ LCR = inb(base + UART_LCR);
+ outb(0xBF, base + UART_LCR);
+ val = inb(base + UART_SCR);
+ outb(qopr, base + UART_SCR);
+ outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_rqmcr(struct uart_8250_port *port)
+{
+ unsigned long base = port->port.iobase;
+ u8 LCR, val, qmcr;
+
+ LCR = inb(base + UART_LCR);
+ outb(0xBF, base + UART_LCR);
+ val = inb(base + UART_SCR);
+ outb(val | 0x10, base + UART_SCR);
+ qmcr = inb(base + UART_MCR);
+ outb(val, base + UART_SCR);
+ outb(LCR, base + UART_LCR);
+
+ return qmcr;
+}
+
+static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr)
+{
+ unsigned long base = port->port.iobase;
+ u8 LCR, val;
+
+ LCR = inb(base + UART_LCR);
+ outb(0xBF, base + UART_LCR);
+ val = inb(base + UART_SCR);
+ outb(val | 0x10, base + UART_SCR);
+ outb(qmcr, base + UART_MCR);
+ outb(val, base + UART_SCR);
+ outb(LCR, base + UART_LCR);
+}
+
+static int pci_quatech_has_qmcr(struct uart_8250_port *port)
+{
+ unsigned long base = port->port.iobase;
+ u8 LCR, val;
+
+ LCR = inb(base + UART_LCR);
+ outb(0xBF, base + UART_LCR);
+ val = inb(base + UART_SCR);
+ if (val & 0x20) {
+ outb(0x80, UART_LCR);
+ if (!(inb(UART_SCR) & 0x20)) {
+ outb(LCR, base + UART_LCR);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int pci_quatech_test(struct uart_8250_port *port)
+{
+ u8 reg;
+ u8 qopr = pci_quatech_rqopr(port);
+ pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1);
+ reg = pci_quatech_rqopr(port) & 0xC0;
+ if (reg != QPCR_TEST_GET1)
+ return -EINVAL;
+ pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR2);
+ reg = pci_quatech_rqopr(port) & 0xC0;
+ if (reg != QPCR_TEST_GET2)
+ return -EINVAL;
+ pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR3);
+ reg = pci_quatech_rqopr(port) & 0xC0;
+ if (reg != QPCR_TEST_GET3)
+ return -EINVAL;
+ pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR4);
+ reg = pci_quatech_rqopr(port) & 0xC0;
+ if (reg != QPCR_TEST_GET4)
+ return -EINVAL;
+
+ pci_quatech_wqopr(port, qopr);
+ return 0;
+}
+
+static int pci_quatech_clock(struct uart_8250_port *port)
+{
+ u8 qopr, reg, set;
+ unsigned long clock;
+
+ if (pci_quatech_test(port) < 0)
+ return 1843200;
+
+ qopr = pci_quatech_rqopr(port);
+
+ pci_quatech_wqopr(port, qopr & ~QOPR_CLOCK_X8);
+ reg = pci_quatech_rqopr(port);
+ if (reg & QOPR_CLOCK_X8) {
+ clock = 1843200;
+ goto out;
+ }
+ pci_quatech_wqopr(port, qopr | QOPR_CLOCK_X8);
+ reg = pci_quatech_rqopr(port);
+ if (!(reg & QOPR_CLOCK_X8)) {
+ clock = 1843200;
+ goto out;
+ }
+ reg &= QOPR_CLOCK_X8;
+ if (reg == QOPR_CLOCK_X2) {
+ clock = 3685400;
+ set = QOPR_CLOCK_X2;
+ } else if (reg == QOPR_CLOCK_X4) {
+ clock = 7372800;
+ set = QOPR_CLOCK_X4;
+ } else if (reg == QOPR_CLOCK_X8) {
+ clock = 14745600;
+ set = QOPR_CLOCK_X8;
+ } else {
+ clock = 1843200;
+ set = QOPR_CLOCK_X1;
+ }
+ qopr &= ~QOPR_CLOCK_RATE_MASK;
+ qopr |= set;
+
+out:
+ pci_quatech_wqopr(port, qopr);
+ return clock;
+}
+
+static int pci_quatech_rs422(struct uart_8250_port *port)
+{
+ u8 qmcr;
+ int rs422 = 0;
+
+ if (!pci_quatech_has_qmcr(port))
+ return 0;
+ qmcr = pci_quatech_rqmcr(port);
+ pci_quatech_wqmcr(port, 0xFF);
+ if (pci_quatech_rqmcr(port))
+ rs422 = 1;
+ pci_quatech_wqmcr(port, qmcr);
+ return rs422;
+}
+
+static int pci_quatech_init(struct pci_dev *dev)
+{
+ if (pci_quatech_amcc(dev->device)) {
+ unsigned long base = pci_resource_start(dev, 0);
+ if (base) {
+ u32 tmp;
+ outl(inl(base + 0x38) | 0x00002000, base + 0x38);
+ tmp = inl(base + 0x3c);
+ outl(tmp | 0x01000000, base + 0x3c);
+ outl(tmp &= ~0x01000000, base + 0x3c);
+ }
+ }
+ return 0;
+}
+
+static int pci_quatech_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
+{
+ /* Needed by pci_quatech calls below */
+ port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags));
+ /* Set up the clocking */
+ port->port.uartclk = pci_quatech_clock(port);
+ /* For now just warn about RS422 */
+ if (pci_quatech_rs422(port))
+ pr_warn("quatech: software control of RS422 features not currently supported.\n");
+ return pci_default_setup(priv, board, port, idx);
+}
+
+static void pci_quatech_exit(struct pci_dev *dev)
+{
+}
+
+static int pci_default_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;
@@ -1053,18 +1307,176 @@ 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,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
int ret;
- ret = setup_port(priv, port, 0, 0, board->reg_shift);
- port->iotype = UPIO_MEM32;
- port->type = PORT_XSCALE;
- port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
- port->regshift = 2;
+ ret = setup_port(priv, port, idx, 0, board->reg_shift);
+ port->port.iotype = UPIO_MEM32;
+ port->port.type = PORT_XSCALE;
+ port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+ port->port.regshift = 2;
+
+ 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;
}
@@ -1072,31 +1484,144 @@ ce4100_serial_setup(struct serial_private *priv,
static int
pci_omegapci_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
return setup_port(priv, port, 2, idx * 8, 0);
}
+static int
+pci_brcm_trumanage_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ int ret = pci_default_setup(priv, board, port, idx);
+
+ port->port.type = PORT_BRCM_TRUMANAGE;
+ port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+ 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_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
- 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);
+ port->port.flags |= UPF_NO_TXEN_TEST;
+ 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);
}
+static void kt_handle_break(struct uart_port *p)
+{
+ struct uart_8250_port *up =
+ container_of(p, struct uart_8250_port, port);
+ /*
+ * On receipt of a BI, serial device in Intel ME (Intel
+ * management engine) needs to have its fifos cleared for sane
+ * SOL (Serial Over Lan) output.
+ */
+ serial8250_clear_and_reinit_fifos(up);
+}
+
+static unsigned int kt_serial_in(struct uart_port *p, int offset)
+{
+ struct uart_8250_port *up =
+ container_of(p, struct uart_8250_port, port);
+ unsigned int val;
+
+ /*
+ * When the Intel ME (management engine) gets reset its serial
+ * port registers could return 0 momentarily. Functions like
+ * serial8250_console_write, read and save the IER, perform
+ * some operation and then restore it. In order to avoid
+ * setting IER register inadvertently to 0, if the value read
+ * is 0, double check with ier value in uart_8250_port and use
+ * that instead. up->ier should be the same value as what is
+ * currently configured.
+ */
+ val = inb(p->iobase + offset);
+ if (offset == UART_IER) {
+ if (val == 0)
+ val = up->ier;
+ }
+ return val;
+}
+
static int kt_serial_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
- port->flags |= UPF_IIR_ONCE;
+ port->port.flags |= UPF_BUG_THRE;
+ port->port.serial_in = kt_serial_in;
+ port->port.handle_break = kt_handle_break;
return skip_tx_en_setup(priv, board, port, idx);
}
@@ -1112,22 +1637,108 @@ static int pci_eg20t_init(struct pci_dev *dev)
static int
pci_xr17c154_setup(struct serial_private *priv,
const struct pciserial_board *board,
- struct uart_port *port, int idx)
+ struct uart_8250_port *port, int idx)
{
- port->flags |= UPF_EXAR_EFR;
+ port->port.flags |= UPF_EXAR_EFR;
return pci_default_setup(priv, board, port, idx);
}
-static int try_enable_msi(struct pci_dev *dev)
+static int
+pci_xr17v35x_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
{
- /* use msi if available, but fallback to legacy otherwise */
- pci_enable_msi(dev);
- return 0;
+ u8 __iomem *p;
+
+ p = pci_ioremap_bar(priv->dev, 0);
+ if (p == NULL)
+ return -ENOMEM;
+
+ port->port.flags |= UPF_EXAR_EFR;
+
+ /*
+ * Setup Multipurpose Input/Output pins.
+ */
+ if (idx == 0) {
+ writeb(0x00, p + 0x8f); /*MPIOINT[7:0]*/
+ writeb(0x00, p + 0x90); /*MPIOLVL[7:0]*/
+ writeb(0x00, p + 0x91); /*MPIO3T[7:0]*/
+ writeb(0x00, p + 0x92); /*MPIOINV[7:0]*/
+ writeb(0x00, p + 0x93); /*MPIOSEL[7:0]*/
+ writeb(0x00, p + 0x94); /*MPIOOD[7:0]*/
+ writeb(0x00, p + 0x95); /*MPIOINT[15:8]*/
+ writeb(0x00, p + 0x96); /*MPIOLVL[15:8]*/
+ writeb(0x00, p + 0x97); /*MPIO3T[15:8]*/
+ writeb(0x00, p + 0x98); /*MPIOINV[15:8]*/
+ writeb(0x00, p + 0x99); /*MPIOSEL[15:8]*/
+ writeb(0x00, p + 0x9a); /*MPIOOD[15:8]*/
+ }
+ writeb(0x00, p + UART_EXAR_8XMODE);
+ writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR);
+ writeb(128, p + UART_EXAR_TXTRG);
+ writeb(128, p + UART_EXAR_RXTRG);
+ iounmap(p);
+
+ return pci_default_setup(priv, board, port, idx);
}
-static void disable_msi(struct pci_dev *dev)
+#define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004
+#define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002
+#define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a
+#define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b
+
+static int
+pci_fastcom335_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
{
- pci_disable_msi(dev);
+ u8 __iomem *p;
+
+ p = pci_ioremap_bar(priv->dev, 0);
+ if (p == NULL)
+ return -ENOMEM;
+
+ port->port.flags |= UPF_EXAR_EFR;
+
+ /*
+ * Setup Multipurpose Input/Output pins.
+ */
+ if (idx == 0) {
+ switch (priv->dev->device) {
+ case PCI_DEVICE_ID_COMMTECH_4222PCI335:
+ case PCI_DEVICE_ID_COMMTECH_4224PCI335:
+ writeb(0x78, p + 0x90); /* MPIOLVL[7:0] */
+ writeb(0x00, p + 0x92); /* MPIOINV[7:0] */
+ writeb(0x00, p + 0x93); /* MPIOSEL[7:0] */
+ break;
+ case PCI_DEVICE_ID_COMMTECH_2324PCI335:
+ case PCI_DEVICE_ID_COMMTECH_2328PCI335:
+ writeb(0x00, p + 0x90); /* MPIOLVL[7:0] */
+ writeb(0xc0, p + 0x92); /* MPIOINV[7:0] */
+ writeb(0xc0, p + 0x93); /* MPIOSEL[7:0] */
+ break;
+ }
+ writeb(0x00, p + 0x8f); /* MPIOINT[7:0] */
+ writeb(0x00, p + 0x91); /* MPIO3T[7:0] */
+ writeb(0x00, p + 0x94); /* MPIOOD[7:0] */
+ }
+ writeb(0x00, p + UART_EXAR_8XMODE);
+ writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR);
+ writeb(32, p + UART_EXAR_TXTRG);
+ writeb(32, p + UART_EXAR_RXTRG);
+ iounmap(p);
+
+ return pci_default_setup(priv, board, port, idx);
+}
+
+static int
+pci_wch_ch353_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ port->port.flags |= UPF_FIXED_TYPE;
+ port->port.type = PORT_16550A;
+ return pci_default_setup(priv, board, port, idx);
}
#define PCI_VENDOR_ID_SBSMODULARIO 0x124B
@@ -1137,9 +1748,13 @@ static void disable_msi(struct pci_dev *dev)
#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208
#define PCI_SUBDEVICE_ID_POCTAL232 0x0308
#define PCI_SUBDEVICE_ID_POCTAL422 0x0408
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_00 0x2500
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_30 0x2530
#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
@@ -1152,6 +1767,7 @@ static void disable_msi(struct pci_dev *dev)
#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
@@ -1160,9 +1776,28 @@ static void disable_msi(struct pci_dev *dev)
#define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6
#define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001
#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d
+#define PCI_VENDOR_ID_WCH 0x4348
+#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
+#define PCI_VENDOR_ID_ASIX 0x9710
+#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020
+#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
+
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
+#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588
/*
* Master list of serial port init/setup/exit quirks.
@@ -1177,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,
@@ -1249,9 +1884,21 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.device = PCI_DEVICE_ID_INTEL_PATSBURG_KT,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .init = try_enable_msi,
.setup = kt_serial_setup,
- .exit = disable_msi,
+ },
+ {
+ .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
@@ -1263,7 +1910,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ite887x_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ite887x_exit),
+ .exit = pci_ite887x_exit,
},
/*
* National Instruments
@@ -1275,7 +1922,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1284,7 +1931,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1293,7 +1940,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1302,7 +1949,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1311,7 +1958,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1320,7 +1967,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1329,7 +1976,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1338,7 +1985,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1347,7 +1994,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1356,7 +2003,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1365,7 +2012,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1374,7 +2021,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8420_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_ni8420_exit),
+ .exit = pci_ni8420_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
@@ -1383,7 +2030,17 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8430_setup,
- .exit = __devexit_p(pci_ni8430_exit),
+ .exit = pci_ni8430_exit,
+ },
+ /* Quatech */
+ {
+ .vendor = PCI_VENDOR_ID_QUATECH,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_quatech_init,
+ .setup = pci_quatech_setup,
+ .exit = pci_quatech_exit,
},
/*
* Panacom
@@ -1395,7 +2052,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_plx9050_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_plx9050_exit),
+ .exit = pci_plx9050_exit,
},
{
.vendor = PCI_VENDOR_ID_PANACOM,
@@ -1404,9 +2061,34 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.init = pci_plx9050_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_plx9050_exit),
+ .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
*/
{
@@ -1423,7 +2105,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_SUBDEVICE_ID_EXSYS_4055,
.init = pci_plx9050_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_plx9050_exit),
+ .exit = pci_plx9050_exit,
},
{
.vendor = PCI_VENDOR_ID_PLX,
@@ -1432,16 +2114,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2,
.init = pci_plx9050_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_plx9050_exit),
- },
- {
- .vendor = PCI_VENDOR_ID_PLX,
- .device = PCI_DEVICE_ID_PLX_9050,
- .subvendor = PCI_VENDOR_ID_PLX,
- .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584,
- .init = pci_plx9050_init,
- .setup = pci_default_setup,
- .exit = __devexit_p(pci_plx9050_exit),
+ .exit = pci_plx9050_exit,
},
{
.vendor = PCI_VENDOR_ID_PLX,
@@ -1450,7 +2123,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_DEVICE_ID_PLX_ROMULUS,
.init = pci_plx9050_init,
.setup = pci_default_setup,
- .exit = __devexit_p(pci_plx9050_exit),
+ .exit = pci_plx9050_exit,
},
/*
* SBS Technologies, Inc., PMC-OCTALPRO 232
@@ -1462,7 +2135,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_SUBDEVICE_ID_OCTPRO232,
.init = sbs_init,
.setup = sbs_setup,
- .exit = __devexit_p(sbs_exit),
+ .exit = sbs_exit,
},
/*
* SBS Technologies, Inc., PMC-OCTALPRO 422
@@ -1474,7 +2147,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_SUBDEVICE_ID_OCTPRO422,
.init = sbs_init,
.setup = sbs_setup,
- .exit = __devexit_p(sbs_exit),
+ .exit = sbs_exit,
},
/*
* SBS Technologies, Inc., P-Octal 232
@@ -1486,7 +2159,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_SUBDEVICE_ID_POCTAL232,
.init = sbs_init,
.setup = sbs_setup,
- .exit = __devexit_p(sbs_exit),
+ .exit = sbs_exit,
},
/*
* SBS Technologies, Inc., P-Octal 422
@@ -1498,7 +2171,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_SUBDEVICE_ID_POCTAL422,
.init = sbs_init,
.setup = sbs_setup,
- .exit = __devexit_p(sbs_exit),
+ .exit = sbs_exit,
},
/*
* SIIG cards - these may be called via parport_serial
@@ -1548,6 +2221,23 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.setup = pci_timedia_setup,
},
/*
+ * SUNIX (Timedia) cards
+ * Do not "probe" for these cards as there is at least one combination
+ * card that should be handled by parport_pc that doesn't match the
+ * rule in pci_timedia_probe.
+ * It is part number is MIO5079A but its subdevice ID is 0x0102.
+ * There are some boards with part number SER5037AL that report
+ * subdevice ID 0x0002.
+ */
+ {
+ .vendor = PCI_VENDOR_ID_SUNIX,
+ .device = PCI_DEVICE_ID_SUNIX_1999,
+ .subvendor = PCI_VENDOR_ID_SUNIX,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_timedia_init,
+ .setup = pci_timedia_setup,
+ },
+ /*
* Exar cards
*/
{
@@ -1571,6 +2261,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.setup = pci_xr17c154_setup,
},
+ {
+ .vendor = PCI_VENDOR_ID_EXAR,
+ .device = PCI_DEVICE_ID_EXAR_XR17V352,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_xr17v35x_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_EXAR,
+ .device = PCI_DEVICE_ID_EXAR_XR17V354,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_xr17v35x_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_EXAR,
+ .device = PCI_DEVICE_ID_EXAR_XR17V358,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_xr17v35x_setup,
+ },
/*
* Xircom cards
*/
@@ -1623,54 +2334,72 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8811,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8812,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8813,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x8814,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = 0x10DB,
.device = 0x8027,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = 0x10DB,
.device = 0x8028,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = 0x10DB,
.device = 0x8029,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = 0x10DB,
.device = 0x800C,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
{
.vendor = 0x10DB,
.device = 0x800D,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
.init = pci_eg20t_init,
.setup = pci_default_setup,
},
@@ -1683,7 +2412,142 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subvendor = PCI_ANY_ID,
.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,
+ .device = PCI_DEVICE_ID_WCH_CH353_2S1P,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_wch_ch353_setup,
+ },
+ /* WCH CH353 4S card (16550 clone) */
+ {
+ .vendor = PCI_VENDOR_ID_WCH,
+ .device = PCI_DEVICE_ID_WCH_CH353_4S,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_wch_ch353_setup,
+ },
+ /* WCH CH353 2S1PF card (16550 clone) */
+ {
+ .vendor = PCI_VENDOR_ID_WCH,
+ .device = PCI_DEVICE_ID_WCH_CH353_2S1PF,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_wch_ch353_setup,
+ },
+ /* WCH CH352 2S card (16550 clone) */
+ {
+ .vendor = PCI_VENDOR_ID_WCH,
+ .device = PCI_DEVICE_ID_WCH_CH352_2S,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_wch_ch353_setup,
+ },
+ /*
+ * ASIX devices with FIFO bug
+ */
+ {
+ .vendor = PCI_VENDOR_ID_ASIX,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_asix_setup,
+ },
+ /*
+ * Commtech, Inc. Fastcom adapters
+ *
+ */
+ {
+ .vendor = PCI_VENDOR_ID_COMMTECH,
+ .device = PCI_DEVICE_ID_COMMTECH_4222PCI335,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fastcom335_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_COMMTECH,
+ .device = PCI_DEVICE_ID_COMMTECH_4224PCI335,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fastcom335_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_COMMTECH,
+ .device = PCI_DEVICE_ID_COMMTECH_2324PCI335,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fastcom335_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_COMMTECH,
+ .device = PCI_DEVICE_ID_COMMTECH_2328PCI335,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fastcom335_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_COMMTECH,
+ .device = PCI_DEVICE_ID_COMMTECH_4222PCIE,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_xr17v35x_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_COMMTECH,
+ .device = PCI_DEVICE_ID_COMMTECH_4224PCIE,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_xr17v35x_setup,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_COMMTECH,
+ .device = PCI_DEVICE_ID_COMMTECH_4228PCIE,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_xr17v35x_setup,
+ },
+ /*
+ * Broadcom TruManage (NetXtreme)
+ */
+ {
+ .vendor = PCI_VENDOR_ID_BROADCOM,
+ .device = PCI_DEVICE_ID_BROADCOM_TRUMANAGE,
+ .subvendor = PCI_ANY_ID,
+ .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
*/
@@ -1760,6 +2624,10 @@ enum pci_board_num_t {
pbn_b0_4_1152000,
+ pbn_b0_2_1152000_200,
+ pbn_b0_4_1152000_200,
+ pbn_b0_8_1152000_200,
+
pbn_b0_2_1843200,
pbn_b0_4_1843200,
@@ -1844,7 +2712,6 @@ enum pci_board_num_t {
pbn_panacom,
pbn_panacom2,
pbn_panacom4,
- pbn_exsys_4055,
pbn_plx_romulus,
pbn_oxsemi,
pbn_oxsemi_1_4000000,
@@ -1860,6 +2727,9 @@ enum pci_board_num_t {
pbn_exar_XR17C152,
pbn_exar_XR17C154,
pbn_exar_XR17C158,
+ pbn_exar_XR17V352,
+ pbn_exar_XR17V354,
+ pbn_exar_XR17V358,
pbn_exar_ibm_saturn,
pbn_pasemi_1682M,
pbn_ni8430_2,
@@ -1871,8 +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,
};
/*
@@ -1885,7 +2760,7 @@ enum pci_board_num_t {
* see first lines of serial_in() and serial_out() in 8250.c
*/
-static struct pciserial_board pci_boards[] __devinitdata = {
+static struct pciserial_board pci_boards[] = {
[pbn_default] = {
.flags = FL_BASE0,
.num_ports = 1,
@@ -1955,6 +2830,27 @@ static struct pciserial_board pci_boards[] __devinitdata = {
.uart_offset = 8,
},
+ [pbn_b0_2_1152000_200] = {
+ .flags = FL_BASE0,
+ .num_ports = 2,
+ .base_baud = 1152000,
+ .uart_offset = 0x200,
+ },
+
+ [pbn_b0_4_1152000_200] = {
+ .flags = FL_BASE0,
+ .num_ports = 4,
+ .base_baud = 1152000,
+ .uart_offset = 0x200,
+ },
+
+ [pbn_b0_8_1152000_200] = {
+ .flags = FL_BASE0,
+ .num_ports = 8,
+ .base_baud = 1152000,
+ .uart_offset = 0x200,
+ },
+
[pbn_b0_2_1843200] = {
.flags = FL_BASE0,
.num_ports = 2,
@@ -2350,13 +3246,6 @@ static struct pciserial_board pci_boards[] __devinitdata = {
.reg_shift = 7,
},
- [pbn_exsys_4055] = {
- .flags = FL_BASE2,
- .num_ports = 4,
- .base_baud = 115200,
- .uart_offset = 8,
- },
-
/* I think this entry is broken - the first_offset looks wrong --rmk */
[pbn_plx_romulus] = {
.flags = FL_BASE2,
@@ -2485,6 +3374,30 @@ static struct pciserial_board pci_boards[] __devinitdata = {
.base_baud = 921600,
.uart_offset = 0x200,
},
+ [pbn_exar_XR17V352] = {
+ .flags = FL_BASE0,
+ .num_ports = 2,
+ .base_baud = 7812500,
+ .uart_offset = 0x400,
+ .reg_shift = 0,
+ .first_offset = 0,
+ },
+ [pbn_exar_XR17V354] = {
+ .flags = FL_BASE0,
+ .num_ports = 4,
+ .base_baud = 7812500,
+ .uart_offset = 0x400,
+ .reg_shift = 0,
+ .first_offset = 0,
+ },
+ [pbn_exar_XR17V358] = {
+ .flags = FL_BASE0,
+ .num_ports = 8,
+ .base_baud = 7812500,
+ .uart_offset = 0x400,
+ .reg_shift = 0,
+ .first_offset = 0,
+ },
[pbn_exar_ibm_saturn] = {
.flags = FL_BASE0,
.num_ports = 1,
@@ -2563,9 +3476,20 @@ static struct pciserial_board pci_boards[] __devinitdata = {
.first_offset = 0x1000,
},
[pbn_ce4100_1_115200] = {
+ .flags = FL_BASE_BARS,
+ .num_ports = 2,
+ .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 = 921600,
+ .base_baud = 2764800,
+ .uart_offset = 0x80,
.reg_shift = 2,
},
[pbn_omegapci] = {
@@ -2579,12 +3503,41 @@ static struct pciserial_board pci_boards[] __devinitdata = {
.num_ports = 2,
.base_baud = 115200,
},
+ [pbn_brcm_trumanage] = {
+ .flags = FL_BASE0,
+ .num_ports = 1,
+ .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 softmodem_blacklist[] = {
+static const struct pci_device_id blacklist[] = {
+ /* softmodems */
{ PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
{ PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */
{ PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */
+
+ /* multi-io cards handled by parport_serial */
+ { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */
+ { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */
};
/*
@@ -2592,10 +3545,10 @@ static const struct pci_device_id softmodem_blacklist[] = {
* guess what the configuration might be, based on the pitiful PCI
* serial specs. Returns 0 on success, 1 on failure.
*/
-static int __devinit
+static int
serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
{
- const struct pci_device_id *blacklist;
+ const struct pci_device_id *bldev;
int num_iomem, num_port, first_port = -1, i;
/*
@@ -2612,13 +3565,13 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
/*
* Do not access blacklisted devices that are known not to
- * feature serial ports.
+ * feature serial ports or are handled by other modules.
*/
- for (blacklist = softmodem_blacklist;
- blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist);
- blacklist++) {
- if (dev->vendor == blacklist->vendor &&
- dev->device == blacklist->device)
+ for (bldev = blacklist;
+ bldev < blacklist + ARRAY_SIZE(blacklist);
+ bldev++) {
+ if (dev->vendor == bldev->vendor &&
+ dev->device == bldev->device)
return -ENODEV;
}
@@ -2685,7 +3638,7 @@ serial_pci_matches(const struct pciserial_board *board,
struct serial_private *
pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
{
- struct uart_port serial_port;
+ struct uart_8250_port uart;
struct serial_private *priv;
struct pci_serial_quirk *quirk;
int rc, nr_ports, i;
@@ -2725,24 +3678,25 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
priv->dev = dev;
priv->quirk = quirk;
- memset(&serial_port, 0, sizeof(struct uart_port));
- serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
- serial_port.uartclk = board->base_baud * 16;
- serial_port.irq = get_pci_irq(dev, board);
- serial_port.dev = &dev->dev;
+ memset(&uart, 0, sizeof(uart));
+ uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+ uart.port.uartclk = board->base_baud * 16;
+ uart.port.irq = get_pci_irq(dev, board);
+ uart.port.dev = &dev->dev;
for (i = 0; i < nr_ports; i++) {
- if (quirk->setup(priv, board, &serial_port, i))
+ 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",
- serial_port.iobase, serial_port.irq, serial_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_port(&serial_port);
+ 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;
}
}
@@ -2789,6 +3743,12 @@ void pciserial_suspend_ports(struct serial_private *priv)
for (i = 0; i < priv->nr; i++)
if (priv->line[i] >= 0)
serial8250_suspend_port(priv->line[i]);
+
+ /*
+ * Ensure that every init quirk is properly torn down
+ */
+ if (priv->quirk->exit)
+ priv->quirk->exit(priv->dev);
}
EXPORT_SYMBOL_GPL(pciserial_suspend_ports);
@@ -2812,7 +3772,7 @@ EXPORT_SYMBOL_GPL(pciserial_resume_ports);
* Probe one serial board. Unfortunately, there is no rhyme nor reason
* to the arrangement of serial ports on a PCI card.
*/
-static int __devinit
+static int
pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
{
struct pci_serial_quirk *quirk;
@@ -2829,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;
}
@@ -2883,12 +3843,10 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
return rc;
}
-static void __devexit pciserial_remove_one(struct pci_dev *dev)
+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);
@@ -2922,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;
@@ -2934,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,
@@ -3102,7 +4067,12 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_VENDOR_ID_PLX,
PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0,
- pbn_b0_4_115200 },
+ pbn_b2_4_115200 },
+ /* Unknown card - subdevice 0x1588 */
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+ PCI_VENDOR_ID_PLX,
+ PCI_SUBDEVICE_ID_UNKNOWN_0x1588, 0, 0,
+ pbn_b2_8_115200 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_KEYSPAN,
PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
@@ -3144,7 +4114,7 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_EXSYS,
PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0,
- pbn_exsys_4055 },
+ pbn_b2_4_115200 },
/*
* Megawolf Romulus PCI Serial Card, from Mike Hudson
* (Exoray@isys.ca)
@@ -3152,18 +4122,70 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
0x10b5, 0x106a, 0, 0,
pbn_plx_romulus },
+ /*
+ * Quatech cards. These actually have configurable clocks but for
+ * now we just use the default.
+ *
+ * 100 series are RS232, 200 series RS422,
+ */
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b1_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b1_4_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_8_115200 },
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_8_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b1_4_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b1_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b1_4_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b1_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_4_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_1_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_4_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_2_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b2_1_115200 },
+ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_b0_8_115200 },
+
{ PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
0, 0,
@@ -3183,8 +4205,11 @@ static struct pci_device_id serial_pci_tbl[] = {
* For now just used the hex ID 0x950a.
*/
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
- PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0,
- pbn_b0_2_115200 },
+ PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00,
+ 0, 0, pbn_b0_2_115200 },
+ { PCI_VENDOR_ID_OXSEMI, 0x950a,
+ PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30,
+ 0, 0, pbn_b0_2_115200 },
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_1130000 },
@@ -3447,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 },
@@ -3545,6 +4573,19 @@ static struct pci_device_id serial_pci_tbl[] = {
pbn_b0_bt_1_921600 },
/*
+ * SUNIX (TIMEDIA)
+ */
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+ PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00,
+ pbn_b0_bt_1_921600 },
+
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+ PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
+ pbn_b0_bt_1_921600 },
+
+ /*
* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
*/
{ PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028,
@@ -3718,6 +4759,21 @@ static struct pci_device_id serial_pci_tbl[] = {
PCI_ANY_ID, PCI_ANY_ID,
0,
0, pbn_exar_XR17C158 },
+ /*
+ * Exar Corp. XR17V35[248] Dual/Quad/Octal PCIe UARTs
+ */
+ { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V352,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_exar_XR17V352 },
+ { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V354,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_exar_XR17V354 },
+ { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V358,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_exar_XR17V358 },
/*
* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
@@ -3977,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,
@@ -4077,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 },
@@ -4121,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
@@ -4130,6 +5201,73 @@ static struct pci_device_id serial_pci_tbl[] = {
pbn_omegapci },
/*
+ * Broadcom TruManage
+ */
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_brcm_trumanage },
+
+ /*
+ * AgeStar as-prs2-009
+ */
+ { PCI_VENDOR_ID_AGESTAR, PCI_DEVICE_ID_AGESTAR_9375,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, pbn_b0_bt_2_115200 },
+
+ /*
+ * WCH CH353 series devices: The 2S1P is handled by parport_serial
+ * so not listed here.
+ */
+ { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_4S,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, pbn_b0_bt_4_115200 },
+
+ { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_2S1PF,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, pbn_b0_bt_2_115200 },
+
+ { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH352_2S,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, pbn_b0_bt_2_115200 },
+
+ /*
+ * Commtech, Inc. Fastcom adapters
+ */
+ { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCI335,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_b0_2_1152000_200 },
+ { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCI335,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_b0_4_1152000_200 },
+ { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2324PCI335,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_b0_4_1152000_200 },
+ { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2328PCI335,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_b0_8_1152000_200 },
+ { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCIE,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_exar_XR17V352 },
+ { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCIE,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_exar_XR17V354 },
+ { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4228PCIE,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 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
*/
@@ -4187,7 +5325,7 @@ static void serial8250_io_resume(struct pci_dev *dev)
pciserial_resume_ports(priv);
}
-static struct pci_error_handlers serial8250_err_handler = {
+static const struct pci_error_handlers serial8250_err_handler = {
.error_detected = serial8250_io_error_detected,
.slot_reset = serial8250_io_slot_reset,
.resume = serial8250_io_resume,
@@ -4196,7 +5334,7 @@ static struct pci_error_handlers serial8250_err_handler = {
static struct pci_driver serial_pci_driver = {
.name = "serial",
.probe = pciserial_init_one,
- .remove = __devexit_p(pciserial_remove_one),
+ .remove = pciserial_remove_one,
#ifdef CONFIG_PM
.suspend = pciserial_suspend_one,
.resume = pciserial_resume_one,
@@ -4205,18 +5343,7 @@ static struct pci_driver serial_pci_driver = {
.err_handler = &serial8250_err_handler,
};
-static int __init serial8250_pci_init(void)
-{
- return pci_register_driver(&serial_pci_driver);
-}
-
-static void __exit serial8250_pci_exit(void)
-{
- pci_unregister_driver(&serial_pci_driver);
-}
-
-module_init(serial8250_pci_init);
-module_exit(serial8250_pci_exit);
+module_pci_driver(serial_pci_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c
index a2f236510ff..682a2fbe5c0 100644
--- a/drivers/tty/serial/8250/8250_pnp.c
+++ b/drivers/tty/serial/8250/8250_pnp.c
@@ -1,5 +1,5 @@
/*
- * Probe module for 8250/16550-type ISAPNP serial ports.
+ * Probe for 8250/16550-type ISAPNP serial ports.
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
@@ -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>
@@ -25,7 +24,7 @@
#include "8250.h"
#define UNKNOWN_DEV 0x3000
-
+#define CIR_PORT 0x0800
static const struct pnp_device_id pnp_dev_table[] = {
/* Archtek America Corp. */
@@ -362,19 +361,22 @@ static const struct pnp_device_id pnp_dev_table[] = {
{ "PNPCXXX", UNKNOWN_DEV },
/* More unknown PnP modems */
{ "PNPDXXX", UNKNOWN_DEV },
+ /* Winbond CIR port, should not be probed. We should keep track
+ of it to prevent the legacy serial driver from probing it */
+ { "WEC1022", CIR_PORT },
{ "", 0 }
};
MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
-static char *modem_names[] __devinitdata = {
+static char *modem_names[] = {
"MODEM", "Modem", "modem", "FAX", "Fax", "fax",
"56K", "56k", "K56", "33.6", "28.8", "14.4",
"33,600", "28,800", "14,400", "33.600", "28.800", "14.400",
"33600", "28800", "14400", "V.90", "V.34", "V.32", NULL
};
-static int __devinit check_name(char *name)
+static int check_name(char *name)
{
char **tmp;
@@ -385,7 +387,7 @@ static int __devinit check_name(char *name)
return 0;
}
-static int __devinit check_resources(struct pnp_dev *dev)
+static int check_resources(struct pnp_dev *dev)
{
resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8};
int i;
@@ -409,7 +411,7 @@ static int __devinit check_resources(struct pnp_dev *dev)
* PnP modems, alternatively we must hardcode all modems in pnp_devices[]
* table.
*/
-static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
+static int serial_pnp_guess_board(struct pnp_dev *dev)
{
if (!(check_name(pnp_dev_name(dev)) ||
(dev->card && check_name(dev->card->name))))
@@ -421,52 +423,59 @@ static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
return -ENODEV;
}
-static int __devinit
+static int
serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
- struct uart_port port;
+ struct uart_8250_port uart;
int ret, line, flags = dev_id->driver_data;
if (flags & UNKNOWN_DEV) {
- ret = serial_pnp_guess_board(dev, &flags);
+ ret = serial_pnp_guess_board(dev);
if (ret < 0)
return ret;
}
- memset(&port, 0, sizeof(struct uart_port));
+ memset(&uart, 0, sizeof(uart));
if (pnp_irq_valid(dev, 0))
- port.irq = pnp_irq(dev, 0);
- if (pnp_port_valid(dev, 0)) {
- port.iobase = pnp_port_start(dev, 0);
- port.iotype = UPIO_PORT;
+ uart.port.irq = pnp_irq(dev, 0);
+ if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) {
+ uart.port.iobase = pnp_port_start(dev, 2);
+ uart.port.iotype = UPIO_PORT;
+ } else if (pnp_port_valid(dev, 0)) {
+ uart.port.iobase = pnp_port_start(dev, 0);
+ uart.port.iotype = UPIO_PORT;
} else if (pnp_mem_valid(dev, 0)) {
- port.mapbase = pnp_mem_start(dev, 0);
- port.iotype = UPIO_MEM;
- port.flags = UPF_IOREMAP;
+ uart.port.mapbase = pnp_mem_start(dev, 0);
+ uart.port.iotype = UPIO_MEM;
+ uart.port.flags = UPF_IOREMAP;
} else
return -ENODEV;
#ifdef SERIAL_DEBUG_PNP
printk(KERN_DEBUG
"Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
- port.iobase, port.mapbase, port.irq, port.iotype);
+ uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype);
#endif
+ if (flags & CIR_PORT) {
+ uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
+ uart.port.type = PORT_8250_CIR;
+ }
- port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+ uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
- port.flags |= UPF_SHARE_IRQ;
- port.uartclk = 1843200;
- port.dev = &dev->dev;
+ uart.port.flags |= UPF_SHARE_IRQ;
+ uart.port.uartclk = 1843200;
+ uart.port.dev = &dev->dev;
- line = serial8250_register_port(&port);
- if (line < 0)
+ line = serial8250_register_8250_port(&uart);
+ if (line < 0 || (flags & CIR_PORT))
return -ENODEV;
pnp_set_drvdata(dev, (void *)((long)line + 1));
return 0;
}
-static void __devexit serial_pnp_remove(struct pnp_dev *dev)
+static void serial_pnp_remove(struct pnp_dev *dev)
{
long line = (long)pnp_get_drvdata(dev);
if (line)
@@ -501,24 +510,19 @@ static int serial_pnp_resume(struct pnp_dev *dev)
static struct pnp_driver serial_pnp_driver = {
.name = "serial",
.probe = serial_pnp_probe,
- .remove = __devexit_p(serial_pnp_remove),
+ .remove = serial_pnp_remove,
.suspend = serial_pnp_suspend,
.resume = serial_pnp_resume,
.id_table = pnp_dev_table,
};
-static int __init serial8250_pnp_init(void)
+int serial8250_pnp_init(void)
{
return pnp_register_driver(&serial_pnp_driver);
}
-static void __exit serial8250_pnp_exit(void)
+void serial8250_pnp_exit(void)
{
pnp_unregister_driver(&serial_pnp_driver);
}
-module_init(serial8250_pnp_init);
-module_exit(serial8250_pnp_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 591f8018e7d..349ee598b34 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -12,9 +12,8 @@ config SERIAL_8250
here are those that are setting up dedicated Ethernet WWW/FTP
servers, or users that have one of the various bus mice instead of a
serial mouse and don't intend to use their machine's standard serial
- port for anything. (Note that the Cyclades and Stallion multi
- serial port drivers do not need this driver built in for them to
- work.)
+ port for anything. (Note that the Cyclades multi serial port driver
+ does not need this driver built in for it to work.)
To compile this driver as a module, choose M here: the
module will be called 8250.
@@ -33,10 +32,36 @@ config SERIAL_8250
Most people will say Y or M here, so that they can use serial mice,
modems and similar devices connecting to the standard serial ports.
+config SERIAL_8250_DEPRECATED_OPTIONS
+ bool "Support 8250_core.* kernel options (DEPRECATED)"
+ depends on SERIAL_8250
+ default y
+ ---help---
+ In 3.7 we renamed 8250 to 8250_core by mistake, so now we have to
+ 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 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.
+
+ If you did not notice yet and/or you have userspace from pre-3.7, it
+ is safe (and recommended) to say N here.
+
+config SERIAL_8250_PNP
+ bool "8250/16550 PNP device support" if EXPERT
+ depends on SERIAL_8250 && PNP
+ default y
+ ---help---
+ This builds standard PNP serial support. You may be able to
+ disable this feature if you only need legacy serial support.
+
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
@@ -66,16 +91,19 @@ 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
default SERIAL_8250
+config SERIAL_8250_DMA
+ bool "DMA support for 16550 compatible UART controllers" if EXPERT
+ depends on SERIAL_8250 && DMADEVICES=y
+ default SERIAL_8250
+ help
+ This builds DMA support that can be used with 8250/16650
+ compatible UART controllers that support DMA signaling.
+
config SERIAL_8250_PCI
tristate "8250/16550 PCI device support" if EXPERT
depends on SERIAL_8250 && PCI
@@ -84,14 +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.
-
-config SERIAL_8250_PNP
- tristate "8250/16550 PNP device support" if EXPERT
- depends on SERIAL_8250 && PNP
- default SERIAL_8250
- help
- This builds standard PNP serial support. You may be able to
- disable this feature if you only need legacy serial support.
+ 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
@@ -241,15 +263,6 @@ config SERIAL_8250_RSA
help
::: To be written :::
-config SERIAL_8250_MCA
- tristate "Support 8250-type ports on MCA buses"
- depends on SERIAL_8250 != n && MCA
- help
- Say Y here if you have a MCA serial ports.
-
- To compile this driver as a module, choose M here: the module
- will be called 8250_mca.
-
config SERIAL_8250_ACORN
tristate "Acorn expansion card serial port support"
depends on ARCH_ACORN && SERIAL_8250
@@ -258,15 +271,6 @@ config SERIAL_8250_ACORN
system, say Y to this option. The driver can handle 1, 2, or 3 port
cards. If unsure, say N.
-config SERIAL_8250_RM9K
- bool "Support for MIPS RM9xxx integrated serial port"
- depends on SERIAL_8250 != n && SERIAL_RM9000
- select SERIAL_8250_SHARE_IRQ
- help
- Selecting this option will add support for the integrated serial
- port hardware found on MIPS RM9122 and similar processors.
- If unsure, say N.
-
config SERIAL_8250_FSL
bool
depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550
@@ -274,7 +278,23 @@ config SERIAL_8250_FSL
config SERIAL_8250_DW
tristate "Support for Synopsys DesignWare 8250 quirks"
- depends on SERIAL_8250 && OF
+ depends on SERIAL_8250
help
Selecting this option will enable handling of the extra features
present in the Synopsys DesignWare APB UART.
+
+config SERIAL_8250_EM
+ tristate "Support for Emma Mobile integrated serial port"
+ depends on SERIAL_8250 && ARM && HAVE_CLK
+ help
+ Selecting this option will add support for the integrated serial
+ port hardware found on the Emma Mobile line of processors.
+ If unsure, say N.
+
+config SERIAL_8250_RT288X
+ bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support"
+ depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883)
+ help
+ If you have a Ralink RT288x/RT305x SoC based board and want to use the
+ serial port, say Y to this option. The driver can handle up to 2 serial
+ ports. If unsure, say N.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 867bba73890..36d68d05430 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -3,7 +3,9 @@
#
obj-$(CONFIG_SERIAL_8250) += 8250.o
-obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
+8250-y := 8250_core.o
+8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
+8250-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
@@ -15,6 +17,6 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o
obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
-obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
+obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c
index 86090605a84..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>
@@ -43,7 +42,6 @@
#include <linux/delay.h>
#include <linux/major.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
@@ -74,7 +72,7 @@ struct serial_quirk {
unsigned int prodid;
int multi; /* 1 = multifunction, > 1 = # ports */
void (*config)(struct pcmcia_device *);
- void (*setup)(struct pcmcia_device *, struct uart_port *);
+ void (*setup)(struct pcmcia_device *, struct uart_8250_port *);
void (*wakeup)(struct pcmcia_device *);
int (*post)(struct pcmcia_device *);
};
@@ -106,9 +104,9 @@ struct serial_cfg_mem {
* Elan VPU16551 UART with 14.7456MHz oscillator
* manfid 0x015D, 0x4C45
*/
-static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port)
+static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_8250_port *uart)
{
- port->uartclk = 14745600;
+ uart->port.uartclk = 14745600;
}
static int quirk_post_ibm(struct pcmcia_device *link)
@@ -344,25 +342,25 @@ static void serial_detach(struct pcmcia_device *link)
static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
unsigned int iobase, int irq)
{
- struct uart_port port;
+ struct uart_8250_port uart;
int line;
- memset(&port, 0, sizeof (struct uart_port));
- port.iobase = iobase;
- port.irq = irq;
- port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
- port.uartclk = 1843200;
- port.dev = &handle->dev;
+ memset(&uart, 0, sizeof(uart));
+ uart.port.iobase = iobase;
+ uart.port.irq = irq;
+ uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
+ uart.port.uartclk = 1843200;
+ uart.port.dev = &handle->dev;
if (buggy_uart)
- port.flags |= UPF_BUGGY_UART;
+ uart.port.flags |= UPF_BUGGY_UART;
if (info->quirk && info->quirk->setup)
- info->quirk->setup(handle, &port);
+ info->quirk->setup(handle, &uart);
- line = serial8250_register_port(&port);
+ line = serial8250_register_8250_port(&uart);
if (line < 0) {
- printk(KERN_NOTICE "serial_cs: serial8250_register_port() at "
- "0x%04lx, irq %d failed\n", (u_long)iobase, irq);
+ pr_err("serial_cs: serial8250_register_8250_port() at 0x%04lx, irq %d failed\n",
+ (unsigned long)iobase, irq);
return -EINVAL;
}
@@ -853,18 +851,6 @@ static struct pcmcia_driver serial_cs_driver = {
.suspend = serial_suspend,
.resume = serial_resume,
};
-
-static int __init init_serial_cs(void)
-{
- return pcmcia_register_driver(&serial_cs_driver);
-}
-
-static void __exit exit_serial_cs(void)
-{
- pcmcia_unregister_driver(&serial_cs_driver);
-}
-
-module_init(init_serial_cs);
-module_exit(exit_serial_cs);
+module_pcmcia_driver(serial_cs_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 2de99248dfa..fb57159bad3 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -2,9 +2,18 @@
# Serial device configuration
#
+if TTY
+
menu "Serial drivers"
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"
comment "Non-8250 serial port support"
@@ -51,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
@@ -63,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
@@ -93,8 +115,9 @@ config SERIAL_SB1250_DUART_CONSOLE
config SERIAL_ATMEL
bool "AT91 / AT32 on-chip serial port support"
- depends on (ARM && ARCH_AT91) || AVR32
+ 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.
@@ -141,6 +164,25 @@ config SERIAL_ATMEL_TTYAT
Say Y if you have an external 8250/16C550 UART. If unsure, say N.
+config SERIAL_KGDB_NMI
+ bool "Serial console over KGDB NMI debugger port"
+ depends on KGDB_SERIAL_CONSOLE
+ help
+ This special driver allows you to temporary use NMI debugger port
+ as a normal console (assuming that the port is attached to KGDB).
+
+ Unlike KDB's disable_nmi command, with this driver you are always
+ able to go back to the debugger using KGDB escape sequence ($3#33).
+ This is because this console driver processes the input in NMI
+ context, and thus is able to intercept the magic sequence.
+
+ Note that since the console interprets input and uses polling
+ communication methods, for things like PPP you still must fully
+ detach debugger port from the KGDB NMI (i.e. disable_nmi), and
+ use raw console.
+
+ If unsure, say N.
+
config SERIAL_KS8695
bool "Micrel KS8695 (Centaur) serial port support"
depends on ARCH_KS8695
@@ -160,10 +202,11 @@ config SERIAL_KS8695_CONSOLE
config SERIAL_CLPS711X
tristate "CLPS711X serial port support"
- depends on ARM && ARCH_CLPS711X
+ depends on ARCH_CLPS711X || COMPILE_TEST
select SERIAL_CORE
help
- ::: To be written :::
+ This enables the driver for the on-chip UARTs of the Cirrus
+ Logic EP711x/EP721x/EP731x processors.
config SERIAL_CLPS711X_CONSOLE
bool "Support for console on CLPS711X serial port"
@@ -173,13 +216,11 @@ config SERIAL_CLPS711X_CONSOLE
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
- "console=ttyCL1". (Try "man bootparam" or see the documentation of
- your boot loader (lilo or loadlin) about how to pass options to the
- kernel at boot time.)
+ "console=ttyCL1".
config SERIAL_SAMSUNG
tristate "Samsung SoC serial support"
- depends on ARM && PLAT_SAMSUNG
+ depends on PLAT_SAMSUNG
select SERIAL_CORE
help
Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
@@ -189,15 +230,15 @@ config SERIAL_SAMSUNG
config SERIAL_SAMSUNG_UARTS_4
bool
- depends on ARM && PLAT_SAMSUNG
- default y if !(CPU_S3C2410 || SERIAL_S3C2412 || CPU_S3C2440 || CPU_S3C2442)
+ depends on PLAT_SAMSUNG
+ default y if !(CPU_S3C2410 || CPU_S3C2412 || CPU_S3C2440 || CPU_S3C2442)
help
Internal node for the common case of 4 Samsung compatible UARTs
config SERIAL_SAMSUNG_UARTS
int
- depends on ARM && PLAT_SAMSUNG
- default 6 if ARCH_S5P6450
+ depends on PLAT_SAMSUNG
+ default 6 if CPU_S5P6450
default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416
default 3
help
@@ -230,7 +271,7 @@ config SERIAL_SAMSUNG_CONSOLE
config SERIAL_SIRFSOC
tristate "SiRF SoC Platform Serial port support"
- depends on ARM && ARCH_PRIMA2
+ depends on ARCH_SIRF
select SERIAL_CORE
help
Support for the on-chip UART on the CSR SiRFprimaII series,
@@ -250,6 +291,17 @@ config SERIAL_SIRFSOC_CONSOLE
your boot loader about how to pass options to the kernel at
boot time.)
+config SERIAL_TEGRA
+ tristate "NVIDIA Tegra20/30 SoC serial controller"
+ depends on ARCH_TEGRA && TEGRA20_APB_DMA
+ select SERIAL_CORE
+ help
+ Support for the on-chip UARTs on the NVIDIA Tegra series SOCs
+ 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.
+
config SERIAL_MAX3100
tristate "MAX3100 support"
depends on SPI
@@ -257,12 +309,19 @@ config SERIAL_MAX3100
help
MAX3100 chip support
-config SERIAL_MAX3107
- tristate "MAX3107 support"
- depends on SPI
+config SERIAL_MAX310X
+ tristate "MAX310X support"
+ depends on SPI_MASTER
select SERIAL_CORE
+ select REGMAP_SPI if SPI_MASTER
+ default n
help
- MAX3107 chip support
+ This selects support for an advanced UART from Maxim (Dallas).
+ 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.
+
+ Say Y here if you want to support this ICs.
config SERIAL_DZ
bool "DECstation DZ serial driver"
@@ -321,7 +380,7 @@ config SERIAL_ZS_CONSOLE
config SERIAL_21285
tristate "DC21285 serial port support"
- depends on ARM && FOOTBRIDGE
+ depends on FOOTBRIDGE
select SERIAL_CORE
help
If you have a machine based on a 21285 (Footbridge) StrongARM(R)/
@@ -345,7 +404,7 @@ config SERIAL_21285_CONSOLE
config SERIAL_MPSC
bool "Marvell MPSC serial port support"
- depends on PPC32 && MV64X60
+ depends on MV64X60
select SERIAL_CORE
help
Say Y here if you want to use the Marvell MPSC serial controller.
@@ -382,7 +441,7 @@ config SERIAL_PXA_CONSOLE
config SERIAL_SA1100
bool "SA1100 serial port support"
- depends on ARM && ARCH_SA1100
+ depends on ARCH_SA1100
select SERIAL_CORE
help
If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you
@@ -512,7 +571,7 @@ config BFIN_UART3_CTSRTS
Enable hardware flow control in the driver.
config SERIAL_IMX
- bool "IMX serial port support"
+ tristate "IMX serial port support"
depends on ARCH_MXC
select SERIAL_CORE
select RATIONAL
@@ -522,22 +581,21 @@ config SERIAL_IMX
config SERIAL_IMX_CONSOLE
bool "Console on IMX serial port"
- depends on SERIAL_IMX
+ depends on SERIAL_IMX=y
select SERIAL_CORE_CONSOLE
help
- If you have enabled the serial port on the Motorola IMX
+ If you have enabled the serial port on the Freescale IMX
CPU you can make it the console by answering Y to this option.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
- "console=ttySA0". (Try "man bootparam" or see the documentation of
- your boot loader (lilo or loadlin) about how to pass options to the
- kernel at boot time.)
+ "console=ttymxc0". (Try "man bootparam" or see the documentation of
+ your bootloader about how to pass options to the kernel at boot time.)
config SERIAL_UARTLITE
tristate "Xilinx uartlite serial port support"
- depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE
+ depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE || ARCH_ZYNQ
select SERIAL_CORE
help
Say Y here if you want to use the Xilinx uartlite serial controller.
@@ -671,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
@@ -686,23 +744,42 @@ config SERIAL_SH_SCI_CONSOLE
config SERIAL_SH_SCI_DMA
bool "DMA support"
- depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL
+ depends on SERIAL_SH_SCI && SH_DMAE
config SERIAL_PNX8XXX
bool "Enable PNX8XXX SoCs' UART Support"
- depends on MIPS && (SOC_PNX8550 || SOC_PNX833X)
+ depends on SOC_PNX833X
select SERIAL_CORE
help
- If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330
- and you want to use serial ports, say Y. Otherwise, say N.
+ If you have a MIPS-based Philips SoC such as PNX8330 and you want
+ to use serial ports, say Y. Otherwise, say N.
config SERIAL_PNX8XXX_CONSOLE
bool "Enable PNX8XX0 serial console"
depends on SERIAL_PNX8XXX
select SERIAL_CORE_CONSOLE
help
- If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330
- and you want to use serial console, say Y. Otherwise, say N.
+ If you have a MIPS-based Philips SoC such as PNX8330 and you want
+ to use serial console, say Y. Otherwise, say N.
+
+config SERIAL_HS_LPC32XX
+ tristate "LPC32XX high speed serial port support"
+ depends on ARCH_LPC32XX && OF
+ select SERIAL_CORE
+ help
+ Support for the LPC32XX high speed serial ports (up to 900kbps).
+ Those are UARTs completely different from the Standard UARTs on the
+ LPC32XX SoC.
+ Choose M or Y here to build this driver.
+
+config SERIAL_HS_LPC32XX_CONSOLE
+ bool "Enable LPC32XX high speed UART serial console"
+ depends on SERIAL_HS_LPC32XX
+ select SERIAL_CORE_CONSOLE
+ help
+ If you would like to be able to use one of the high speed serial
+ ports on the LPC32XX as the console, you can do so by answering
+ Y to this option.
config SERIAL_CORE
tristate
@@ -853,7 +930,7 @@ config SERIAL_MPC52xx_CONSOLE_BAUD
config SERIAL_ICOM
tristate "IBM Multiport Serial Adapter"
- depends on PCI && (PPC_ISERIES || PPC_PSERIES)
+ depends on PCI && PPC_PSERIES
select SERIAL_CORE
select FW_LOADER
help
@@ -968,7 +1045,7 @@ config SERIAL_SGI_IOC3
config SERIAL_MSM
bool "MSM on-chip serial port support"
- depends on ARM && ARCH_MSM
+ depends on ARCH_MSM || ARCH_QCOM
select SERIAL_CORE
config SERIAL_MSM_CONSOLE
@@ -978,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
@@ -990,7 +1067,7 @@ config SERIAL_MSM_HS
config SERIAL_VT8500
bool "VIA VT8500 on-chip serial port support"
- depends on ARM && ARCH_VT8500
+ depends on ARCH_VT8500
select SERIAL_CORE
config SERIAL_VT8500_CONSOLE
@@ -1000,7 +1077,7 @@ config SERIAL_VT8500_CONSOLE
config SERIAL_NETX
tristate "NetX serial port support"
- depends on ARM && ARCH_NETX
+ depends on ARCH_NETX
select SERIAL_CORE
help
If you have a machine based on a Hilscher NetX SoC you
@@ -1041,7 +1118,7 @@ config SERIAL_OMAP
config SERIAL_OMAP_CONSOLE
bool "Console on OMAP serial port"
- depends on SERIAL_OMAP
+ depends on SERIAL_OMAP=y
select SERIAL_CORE_CONSOLE
help
Select this option if you would like to use omap serial port as
@@ -1089,20 +1166,30 @@ 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
+config SERIAL_SCCNXP
+ tristate "SCCNXP serial port support"
select SERIAL_CORE
help
- This is a driver for the onboard serial ports of
- older RM400 machines.
+ This selects support for an advanced UART from NXP (Philips).
+ Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92,
+ SC28L202, SCC68681 and SCC68692.
-config SERIAL_SC26XX_CONSOLE
- bool "Console on SC2681/SC2692 serial port"
- depends on SERIAL_SC26XX=y
+config SERIAL_SCCNXP_CONSOLE
+ bool "Console on SCCNXP serial port"
+ depends on SERIAL_SCCNXP=y
select SERIAL_CORE_CONSOLE
help
- Support for Console on SC2681/SC2692 serial ports.
+ 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"
@@ -1170,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.
@@ -1260,13 +1348,13 @@ config SERIAL_ALTERA_UART_CONSOLE
config SERIAL_IFX6X60
tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
- depends on GPIOLIB && SPI && EXPERIMENTAL
+ depends on GPIOLIB && SPI
help
Support for the IFX6x60 modem devices on Intel MID platforms.
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
@@ -1312,26 +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
@@ -1347,4 +1440,121 @@ config SERIAL_AR933X_NR_UARTS
Set this to the number of serial ports you want the driver
to support.
+config SERIAL_EFM32_UART
+ 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
+ Energy Micro's efm32 SoCs.
+
+config SERIAL_EFM32_UART_CONSOLE
+ bool "EFM32 UART/USART console support"
+ 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
+ help
+ Driver for on-chip UART for ARC(Synopsys) for the legacy
+ FPGA Boards (ML50x/ARCAngel4)
+
+config SERIAL_ARC_CONSOLE
+ bool "Console on ARC UART"
+ depends on SERIAL_ARC=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Enable system Console on ARC UART
+
+config SERIAL_ARC_NR_PORTS
+ int "Number of ARC UART ports"
+ depends on SERIAL_ARC
+ range 1 3
+ default "1"
+ help
+ Set this to the number of serial ports you want the driver
+ to support.
+
+config SERIAL_RP2
+ tristate "Comtrol RocketPort EXPRESS/INFINITY support"
+ depends on PCI
+ select SERIAL_CORE
+ help
+ This driver supports the Comtrol RocketPort EXPRESS and
+ RocketPort INFINITY families of PCI/PCIe multiport serial adapters.
+ These adapters use a "RocketPort 2" ASIC that is not compatible
+ with the original RocketPort driver (CONFIG_ROCKETPORT).
+
+ To compile this driver as a module, choose M here: the
+ module will be called rp2.
+
+ If you want to compile this driver into the kernel, say Y here. If
+ you don't have a suitable RocketPort card installed, say N.
+
+config SERIAL_RP2_NR_UARTS
+ int "Maximum number of RocketPort EXPRESS/INFINITY ports"
+ depends on SERIAL_RP2
+ default "32"
+ help
+ If multiple cards are present, the default limit of 32 ports may
+ need to be increased.
+
+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.
+
+config SERIAL_FSL_LPUART_CONSOLE
+ bool "Console on Freescale lpuart serial port"
+ depends on SERIAL_FSL_LPUART=y
+ select SERIAL_CORE_CONSOLE
+ help
+ 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 fef32e10c85..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.
@@ -28,12 +31,13 @@ obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o
obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
-obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
+obj-$(CONFIG_SERIAL_MAX310X) += max310x.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
obj-$(CONFIG_SERIAL_MCF) += mcf.o
obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o
+obj-$(CONFIG_SERIAL_HS_LPC32XX) += lpc32xx_hs.o
obj-$(CONFIG_SERIAL_DZ) += dz.o
obj-$(CONFIG_SERIAL_ZS) += zs.o
obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
@@ -46,7 +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
@@ -59,14 +64,17 @@ obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
+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
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
-obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
@@ -77,4 +85,13 @@ obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o
obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
+obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o
obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o
+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 530181e49f6..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);
}
- tty_flip_buffer_push(port->state->port.tty);
+ 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)
@@ -406,9 +408,10 @@ static struct uart_driver altera_jtaguart_driver = {
.cons = ALTERA_JTAGUART_CONSOLE,
};
-static int __devinit altera_jtaguart_probe(struct platform_device *pdev)
+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;
@@ -453,7 +456,7 @@ static int __devinit altera_jtaguart_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit altera_jtaguart_remove(struct platform_device *pdev)
+static int altera_jtaguart_remove(struct platform_device *pdev)
{
struct uart_port *port;
int i = pdev->id;
@@ -470,6 +473,7 @@ static int __devexit 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);
@@ -477,7 +481,7 @@ MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
static struct platform_driver altera_jtaguart_platform_driver = {
.probe = altera_jtaguart_probe,
- .remove = __devexit_p(altera_jtaguart_remove),
+ .remove = altera_jtaguart_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
@@ -493,11 +497,9 @@ static int __init altera_jtaguart_init(void)
if (rc)
return rc;
rc = platform_driver_register(&altera_jtaguart_platform_driver);
- if (rc) {
+ if (rc)
uart_unregister_driver(&altera_jtaguart_driver);
- return rc;
- }
- return 0;
+ return rc;
}
static void __exit altera_jtaguart_exit(void)
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index 1d04c5037f2..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);
}
- tty_flip_buffer_push(port->state->port.tty);
+ 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)
@@ -377,6 +385,26 @@ static int altera_uart_verify_port(struct uart_port *port,
return 0;
}
+#ifdef CONFIG_CONSOLE_POLL
+static int altera_uart_poll_get_char(struct uart_port *port)
+{
+ while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+ ALTERA_UART_STATUS_RRDY_MSK))
+ cpu_relax();
+
+ return altera_uart_readl(port, ALTERA_UART_RXDATA_REG);
+}
+
+static void altera_uart_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+ ALTERA_UART_STATUS_TRDY_MSK))
+ cpu_relax();
+
+ altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG);
+}
+#endif
+
/*
* Define the basic serial functions we support.
*/
@@ -397,35 +425,16 @@ static struct uart_ops altera_uart_ops = {
.release_port = altera_uart_release_port,
.config_port = altera_uart_config_port,
.verify_port = altera_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = altera_uart_poll_get_char,
+ .poll_put_char = altera_uart_poll_put_char,
+#endif
};
static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS];
#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE)
-int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp)
-{
- struct uart_port *port;
- int i;
-
- for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) {
- port = &altera_uart_ports[i].port;
-
- port->line = i;
- port->type = PORT_ALTERA_UART;
- port->mapbase = platp[i].mapbase;
- port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE);
- port->iotype = SERIAL_IO_MEM;
- port->irq = platp[i].irq;
- port->uartclk = platp[i].uartclk;
- port->flags = UPF_BOOT_AUTOCONF;
- port->ops = &altera_uart_ops;
- port->private_data = platp;
- }
-
- return 0;
-}
-
static void altera_uart_console_putc(struct uart_port *port, const char c)
{
while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
@@ -531,9 +540,9 @@ static int altera_uart_get_of_uartclk(struct platform_device *pdev,
}
#endif /* CONFIG_OF */
-static int __devinit altera_uart_probe(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;
@@ -555,7 +564,7 @@ static int __devinit altera_uart_probe(struct platform_device *pdev)
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_mem)
port->mapbase = res_mem->start;
- else if (platp->mapbase)
+ else if (platp)
port->mapbase = platp->mapbase;
else
return -EINVAL;
@@ -563,7 +572,7 @@ static int __devinit altera_uart_probe(struct platform_device *pdev)
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res_irq)
port->irq = res_irq->start;
- else if (platp->irq)
+ else if (platp)
port->irq = platp->irq;
/* Check platform data first so we can override device node data */
@@ -590,20 +599,19 @@ static int __devinit altera_uart_probe(struct platform_device *pdev)
port->ops = &altera_uart_ops;
port->flags = UPF_BOOT_AUTOCONF;
- dev_set_drvdata(&pdev->dev, port);
+ platform_set_drvdata(pdev, port);
uart_add_one_port(&altera_uart_driver, port);
return 0;
}
-static int __devexit altera_uart_remove(struct platform_device *pdev)
+static int altera_uart_remove(struct platform_device *pdev)
{
- struct uart_port *port = dev_get_drvdata(&pdev->dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
if (port) {
uart_remove_one_port(&altera_uart_driver, port);
- dev_set_drvdata(&pdev->dev, NULL);
port->mapbase = 0;
}
@@ -613,6 +621,7 @@ static int __devexit 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);
@@ -620,7 +629,7 @@ MODULE_DEVICE_TABLE(of, altera_uart_match);
static struct platform_driver altera_uart_platform_driver = {
.probe = altera_uart_probe,
- .remove = __devexit_p(altera_uart_remove),
+ .remove = altera_uart_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
@@ -636,11 +645,9 @@ static int __init altera_uart_init(void)
if (rc)
return rc;
rc = platform_driver_register(&altera_uart_platform_driver);
- if (rc) {
+ if (rc)
uart_unregister_driver(&altera_uart_driver);
- return rc;
- }
- return 0;
+ return rc;
}
static void __exit altera_uart_exit(void)
diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c
index 0d91a540bf1..971af1e22d0 100644
--- a/drivers/tty/serial/amba-pl010.c
+++ b/drivers/tty/serial/amba-pl010.c
@@ -116,7 +116,6 @@ static void pl010_enable_ms(struct uart_port *port)
static void pl010_rx_chars(struct uart_amba_port *uap)
{
- struct tty_struct *tty = uap->port.state->port.tty;
unsigned int status, ch, flag, rsr, max_count = 256;
status = readb(uap->port.membase + UART01x_FR);
@@ -165,7 +164,7 @@ static void pl010_rx_chars(struct uart_amba_port *uap)
status = readb(uap->port.membase + UART01x_FR);
}
spin_unlock(&uap->port.lock);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&uap->port.state->port);
spin_lock(&uap->port.lock);
}
@@ -312,16 +311,12 @@ static int pl010_startup(struct uart_port *port)
struct uart_amba_port *uap = (struct uart_amba_port *)port;
int retval;
- retval = clk_prepare(uap->clk);
- if (retval)
- goto out;
-
/*
* Try to enable the clock producer.
*/
- retval = clk_enable(uap->clk);
+ retval = clk_prepare_enable(uap->clk);
if (retval)
- goto clk_unprep;
+ goto out;
uap->port.uartclk = clk_get_rate(uap->clk);
@@ -346,9 +341,7 @@ static int pl010_startup(struct uart_port *port)
return 0;
clk_dis:
- clk_disable(uap->clk);
- clk_unprep:
- clk_unprepare(uap->clk);
+ clk_disable_unprepare(uap->clk);
out:
return retval;
}
@@ -375,8 +368,7 @@ static void pl010_shutdown(struct uart_port *port)
/*
* Shut down the clock producer
*/
- clk_disable(uap->clk);
- clk_unprepare(uap->clk);
+ clk_disable_unprepare(uap->clk);
}
static void
@@ -428,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;
/*
@@ -729,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:
@@ -753,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++)
@@ -767,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);
@@ -777,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[] = {
{
@@ -800,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 6800f5f2624..0e26dcbd5ea 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -29,6 +29,7 @@
* and hooked into this driver.
*/
+
#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
@@ -51,9 +52,12 @@
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/delay.h>
-
-#include <asm/io.h>
-#include <asm/sizes.h>
+#include <linux/types.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/sizes.h>
+#include <linux/io.h>
#define UART_NR 14
@@ -66,62 +70,48 @@
#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
#define UART_DUMMY_DR_RX (1 << 16)
-
-#define UART_WA_SAVE_NR 14
-
-static void pl011_lockup_wa(unsigned long data);
-static const u32 uart_wa_reg[UART_WA_SAVE_NR] = {
- ST_UART011_DMAWM,
- ST_UART011_TIMEOUT,
- ST_UART011_LCRH_RX,
- UART011_IBRD,
- UART011_FBRD,
- ST_UART011_LCRH_TX,
- UART011_IFLS,
- ST_UART011_XFCR,
- ST_UART011_XON1,
- ST_UART011_XON2,
- ST_UART011_XOFF1,
- ST_UART011_XOFF2,
- UART011_CR,
- UART011_IMSC
-};
-
-static u32 uart_wa_regdata[UART_WA_SAVE_NR];
-static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0);
-
/* There is by now at least one vendor with differing details, so handle it */
struct vendor_data {
unsigned int ifls;
- unsigned int fifosize;
unsigned int lcrh_tx;
unsigned int lcrh_rx;
bool oversampling;
- bool interrupt_may_hang; /* vendor-specific */
bool dma_threshold;
+ bool cts_event_workaround;
+
+ unsigned int (*get_fifosize)(struct amba_device *dev);
};
+static unsigned int get_fifosize_arm(struct amba_device *dev)
+{
+ return amba_rev(dev) < 3 ? 16 : 32;
+}
+
static struct vendor_data vendor_arm = {
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
- .fifosize = 16,
.lcrh_tx = UART011_LCRH,
.lcrh_rx = UART011_LCRH,
.oversampling = false,
.dma_threshold = false,
+ .cts_event_workaround = false,
+ .get_fifosize = get_fifosize_arm,
};
+static unsigned int get_fifosize_st(struct amba_device *dev)
+{
+ return 64;
+}
+
static struct vendor_data vendor_st = {
.ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
- .fifosize = 64,
.lcrh_tx = ST_UART011_LCRH_TX,
.lcrh_rx = ST_UART011_LCRH_RX,
.oversampling = true,
- .interrupt_may_hang = true,
.dma_threshold = true,
+ .cts_event_workaround = true,
+ .get_fifosize = get_fifosize_st,
};
-static struct uart_amba_port *amba_ports[UART_NR];
-
/* Deals with DMA transactions */
struct pl011_sgbuf {
@@ -137,6 +127,12 @@ struct pl011_dmarx_data {
struct pl011_sgbuf sgbuf_b;
dma_cookie_t cookie;
bool running;
+ struct timer_list timer;
+ unsigned int last_residue;
+ unsigned long last_jiffies;
+ bool auto_poll_rate;
+ unsigned int poll_rate;
+ unsigned int poll_timeout;
};
struct pl011_dmatx_data {
@@ -162,7 +158,6 @@ struct uart_amba_port {
unsigned int old_cr; /* state during shutdown */
bool autorts;
char type[12];
- bool interrupt_may_hang; /* vendor-specific */
#ifdef CONFIG_DMA_ENGINE
/* DMA stuff */
bool using_tx_dma;
@@ -240,16 +235,18 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap)
static int pl011_sgbuf_init(struct dma_chan *chan, struct pl011_sgbuf *sg,
enum dma_data_direction dir)
{
- sg->buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL);
+ dma_addr_t dma_addr;
+
+ sg->buf = dma_alloc_coherent(chan->device->dev,
+ PL011_DMA_BUFFER_SIZE, &dma_addr, GFP_KERNEL);
if (!sg->buf)
return -ENOMEM;
- sg_init_one(&sg->sg, sg->buf, PL011_DMA_BUFFER_SIZE);
+ sg_init_table(&sg->sg, 1);
+ sg_set_page(&sg->sg, phys_to_page(dma_addr),
+ PL011_DMA_BUFFER_SIZE, offset_in_page(dma_addr));
+ sg_dma_address(&sg->sg) = dma_addr;
- if (dma_map_sg(chan->device->dev, &sg->sg, 1, dir) != 1) {
- kfree(sg->buf);
- return -EINVAL;
- }
return 0;
}
@@ -257,38 +254,45 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
enum dma_data_direction dir)
{
if (sg->buf) {
- dma_unmap_sg(chan->device->dev, &sg->sg, 1, dir);
- kfree(sg->buf);
+ dma_free_coherent(chan->device->dev,
+ PL011_DMA_BUFFER_SIZE, sg->buf,
+ sg_dma_address(&sg->sg));
}
}
-static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
+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,
.direction = DMA_MEM_TO_DEV,
.dst_maxburst = uap->fifosize >> 1,
+ .device_fc = false,
};
struct dma_chan *chan;
dma_cap_mask_t mask;
- /* We need platform data */
- if (!plat || !plat->dma_filter) {
- dev_info(uap->port.dev, "no DMA platform data\n");
- return;
- }
-
- /* Try to acquire a generic DMA engine slave TX channel */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
+ chan = dma_request_slave_channel(dev, "tx");
- chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param);
if (!chan) {
- dev_err(uap->port.dev, "no TX DMA channel!\n");
- return;
+ /* We need platform data */
+ if (!plat || !plat->dma_filter) {
+ dev_info(uap->port.dev, "no DMA platform data\n");
+ return;
+ }
+
+ /* Try to acquire a generic DMA engine slave TX channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chan = dma_request_channel(mask, plat->dma_filter,
+ plat->dma_tx_param);
+ if (!chan) {
+ dev_err(uap->port.dev, "no TX DMA channel!\n");
+ return;
+ }
}
dmaengine_slave_config(chan, &tx_conf);
@@ -298,23 +302,52 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
dma_chan_name(uap->dmatx.chan));
/* Optionally make use of an RX channel as well */
- if (plat->dma_rx_param) {
- struct dma_slave_config rx_conf = {
- .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,
- };
+ 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);
+
if (!chan) {
dev_err(uap->port.dev, "no RX DMA channel!\n");
return;
}
+ }
+
+ if (chan) {
+ struct dma_slave_config rx_conf = {
+ .src_addr = uap->port.mapbase + UART01x_DR,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .direction = DMA_DEV_TO_MEM,
+ .src_maxburst = uap->fifosize >> 2,
+ .device_fc = false,
+ };
dmaengine_slave_config(chan, &rx_conf);
uap->dmarx.chan = chan;
+ if (plat && plat->dma_rx_poll_enable) {
+ /* Set poll rate if specified. */
+ if (plat->dma_rx_poll_rate) {
+ uap->dmarx.auto_poll_rate = false;
+ uap->dmarx.poll_rate = plat->dma_rx_poll_rate;
+ } else {
+ /*
+ * 100 ms defaults to poll rate if not
+ * specified. This will be adjusted with
+ * the baud rate at set_termios.
+ */
+ uap->dmarx.auto_poll_rate = true;
+ uap->dmarx.poll_rate = 100;
+ }
+ /* 3 secs defaults poll_timeout if not specified. */
+ if (plat->dma_rx_poll_timeout)
+ uap->dmarx.poll_timeout =
+ plat->dma_rx_poll_timeout;
+ else
+ uap->dmarx.poll_timeout = 3000;
+ } else
+ uap->dmarx.auto_poll_rate = false;
+
dev_info(uap->port.dev, "DMA channel RX %s\n",
dma_chan_name(uap->dmarx.chan));
}
@@ -330,6 +363,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
struct dma_uap {
struct list_head node;
struct uart_amba_port *uap;
+ struct device *dev;
};
static LIST_HEAD(pl011_dma_uarts);
@@ -340,7 +374,7 @@ static int __init pl011_dma_initcall(void)
list_for_each_safe(node, tmp, &pl011_dma_uarts) {
struct dma_uap *dmau = list_entry(node, struct dma_uap, node);
- pl011_dma_probe_initcall(dmau->uap);
+ pl011_dma_probe_initcall(dmau->dev, dmau->uap);
list_del(node);
kfree(dmau);
}
@@ -349,18 +383,19 @@ static int __init pl011_dma_initcall(void)
device_initcall(pl011_dma_initcall);
-static void pl011_dma_probe(struct uart_amba_port *uap)
+static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL);
if (dmau) {
dmau->uap = uap;
+ dmau->dev = dev;
list_add_tail(&dmau->node, &pl011_dma_uarts);
}
}
#else
-static void pl011_dma_probe(struct uart_amba_port *uap)
+static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
- pl011_dma_probe_initcall(uap);
+ pl011_dma_probe_initcall(dev, uap);
}
#endif
@@ -481,7 +516,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
return -EBUSY;
}
- desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV,
+ desc = dmaengine_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE);
@@ -640,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;
@@ -664,7 +701,6 @@ static void pl011_dma_rx_callback(void *data);
static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap)
{
struct dma_chan *rxchan = uap->dmarx.chan;
- struct dma_device *dma_dev;
struct pl011_dmarx_data *dmarx = &uap->dmarx;
struct dma_async_tx_descriptor *desc;
struct pl011_sgbuf *sgbuf;
@@ -675,8 +711,7 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap)
/* Start the RX DMA job */
sgbuf = uap->dmarx.use_buf_b ?
&uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a;
- dma_dev = rxchan->device;
- desc = rxchan->device->device_prep_slave_sg(rxchan, &sgbuf->sg, 1,
+ desc = dmaengine_prep_slave_sg(rxchan, &sgbuf->sg, 1,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
/*
@@ -715,28 +750,33 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap,
u32 pending, bool use_buf_b,
bool readfifo)
{
- struct tty_struct *tty = uap->port.state->port.tty;
+ struct tty_port *port = &uap->port.state->port;
struct pl011_sgbuf *sgbuf = use_buf_b ?
&uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a;
- struct device *dev = uap->dmarx.chan->device->dev;
int dma_count = 0;
u32 fifotaken = 0; /* only used for vdbg() */
- /* Pick everything from the DMA first */
+ struct pl011_dmarx_data *dmarx = &uap->dmarx;
+ int dmataken = 0;
+
+ if (uap->dmarx.poll_rate) {
+ /* The data can be taken by polling */
+ dmataken = sgbuf->sg.length - dmarx->last_residue;
+ /* Recalculate the pending size */
+ if (pending >= dmataken)
+ pending -= dmataken;
+ }
+
+ /* Pick the remain data from the DMA */
if (pending) {
- /* Sync in buffer */
- dma_sync_sg_for_cpu(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE);
/*
* First take all chars in the DMA pipe, then look in the FIFO.
* Note that tty_insert_flip_buf() tries to take as many chars
* as it can.
*/
- dma_count = tty_insert_flip_string(uap->port.state->port.tty,
- sgbuf->buf, pending);
-
- /* Return buffer to device */
- dma_sync_sg_for_device(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE);
+ dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken,
+ pending);
uap->port.icount.rx += dma_count;
if (dma_count < pending)
@@ -744,6 +784,10 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap,
"couldn't insert all characters (TTY is full?)\n");
}
+ /* Reset the last_residue for Rx DMA poll */
+ if (uap->dmarx.poll_rate)
+ dmarx->last_residue = sgbuf->sg.length;
+
/*
* Only continue with trying to read the FIFO if all DMA chars have
* been taken first.
@@ -771,7 +815,7 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap,
dev_vdbg(uap->port.dev,
"Took %d chars from DMA buffer and %d chars from the FIFO\n",
dma_count, fifotaken);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(port);
spin_lock(&uap->port.lock);
}
@@ -827,7 +871,12 @@ static void pl011_dma_rx_callback(void *data)
{
struct uart_amba_port *uap = data;
struct pl011_dmarx_data *dmarx = &uap->dmarx;
+ struct dma_chan *rxchan = dmarx->chan;
bool lastbuf = dmarx->use_buf_b;
+ struct pl011_sgbuf *sgbuf = dmarx->use_buf_b ?
+ &dmarx->sgbuf_b : &dmarx->sgbuf_a;
+ size_t pending;
+ struct dma_tx_state state;
int ret;
/*
@@ -838,11 +887,21 @@ static void pl011_dma_rx_callback(void *data)
* we immediately trigger the next DMA job.
*/
spin_lock_irq(&uap->port.lock);
+ /*
+ * Rx data can be taken by the UART interrupts during
+ * the DMA irq handler. So we check the residue here.
+ */
+ rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state);
+ pending = sgbuf->sg.length - state.residue;
+ BUG_ON(pending > PL011_DMA_BUFFER_SIZE);
+ /* Then we terminate the transfer - we now know our residue */
+ dmaengine_terminate_all(rxchan);
+
uap->dmarx.running = false;
dmarx->use_buf_b = !lastbuf;
ret = pl011_dma_rx_trigger_dma(uap);
- pl011_dma_rx_chars(uap, PL011_DMA_BUFFER_SIZE, lastbuf, false);
+ pl011_dma_rx_chars(uap, pending, lastbuf, false);
spin_unlock_irq(&uap->port.lock);
/*
* Do this check after we picked the DMA chars so we don't
@@ -868,6 +927,59 @@ static inline void pl011_dma_rx_stop(struct uart_amba_port *uap)
writew(uap->dmacr, uap->port.membase + UART011_DMACR);
}
+/*
+ * Timer handler for Rx DMA polling.
+ * Every polling, It checks the residue in the dma buffer and transfer
+ * data to the tty. Also, last_residue is updated for the next polling.
+ */
+static void pl011_dma_rx_poll(unsigned long args)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)args;
+ struct tty_port *port = &uap->port.state->port;
+ struct pl011_dmarx_data *dmarx = &uap->dmarx;
+ struct dma_chan *rxchan = uap->dmarx.chan;
+ unsigned long flags = 0;
+ unsigned int dmataken = 0;
+ unsigned int size = 0;
+ struct pl011_sgbuf *sgbuf;
+ int dma_count;
+ struct dma_tx_state state;
+
+ sgbuf = dmarx->use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a;
+ rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state);
+ if (likely(state.residue < dmarx->last_residue)) {
+ dmataken = sgbuf->sg.length - dmarx->last_residue;
+ size = dmarx->last_residue - state.residue;
+ dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken,
+ size);
+ if (dma_count == size)
+ dmarx->last_residue = state.residue;
+ dmarx->last_jiffies = jiffies;
+ }
+ tty_flip_buffer_push(port);
+
+ /*
+ * If no data is received in poll_timeout, the driver will fall back
+ * to interrupt mode. We will retrigger DMA at the first interrupt.
+ */
+ if (jiffies_to_msecs(jiffies - dmarx->last_jiffies)
+ > uap->dmarx.poll_timeout) {
+
+ 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;
+ dmaengine_terminate_all(rxchan);
+ del_timer(&uap->dmarx.timer);
+ } else {
+ mod_timer(&uap->dmarx.timer,
+ jiffies + msecs_to_jiffies(uap->dmarx.poll_rate));
+ }
+}
+
static void pl011_dma_startup(struct uart_amba_port *uap)
{
int ret;
@@ -930,6 +1042,16 @@ skip_rx:
if (pl011_dma_rx_trigger_dma(uap))
dev_dbg(uap->port.dev, "could not trigger initial "
"RX DMA job, fall back to interrupt mode\n");
+ if (uap->dmarx.poll_rate) {
+ init_timer(&(uap->dmarx.timer));
+ uap->dmarx.timer.function = pl011_dma_rx_poll;
+ uap->dmarx.timer.data = (unsigned long)uap;
+ mod_timer(&uap->dmarx.timer,
+ jiffies +
+ msecs_to_jiffies(uap->dmarx.poll_rate));
+ uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE;
+ uap->dmarx.last_jiffies = jiffies;
+ }
}
}
@@ -965,6 +1087,8 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap)
/* Clean up the RX DMA */
pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_a, DMA_FROM_DEVICE);
pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_b, DMA_FROM_DEVICE);
+ if (uap->dmarx.poll_rate)
+ del_timer_sync(&uap->dmarx.timer);
uap->using_rx_dma = false;
}
}
@@ -979,10 +1103,9 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
return uap->using_rx_dma && uap->dmarx.running;
}
-
#else
/* Blank functions if the DMA engine is not available */
-static inline void pl011_dma_probe(struct uart_amba_port *uap)
+static inline void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
}
@@ -1038,69 +1161,6 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
#define pl011_dma_flush_buffer NULL
#endif
-
-/*
- * pl011_lockup_wa
- * This workaround aims to break the deadlock situation
- * when after long transfer over uart in hardware flow
- * control, uart interrupt registers cannot be cleared.
- * Hence uart transfer gets blocked.
- *
- * It is seen that during such deadlock condition ICR
- * don't get cleared even on multiple write. This leads
- * pass_counter to decrease and finally reach zero. This
- * can be taken as trigger point to run this UART_BT_WA.
- *
- */
-static void pl011_lockup_wa(unsigned long data)
-{
- struct uart_amba_port *uap = amba_ports[0];
- void __iomem *base = uap->port.membase;
- struct circ_buf *xmit = &uap->port.state->xmit;
- struct tty_struct *tty = uap->port.state->port.tty;
- int buf_empty_retries = 200;
- int loop;
-
- /* Stop HCI layer from submitting data for tx */
- tty->hw_stopped = 1;
- while (!uart_circ_empty(xmit)) {
- if (buf_empty_retries-- == 0)
- break;
- udelay(100);
- }
-
- /* Backup registers */
- for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
- uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]);
-
- /* Disable UART so that FIFO data is flushed out */
- writew(0x00, uap->port.membase + UART011_CR);
-
- /* Soft reset UART module */
- if (uap->port.dev->platform_data) {
- struct amba_pl011_data *plat;
-
- plat = uap->port.dev->platform_data;
- if (plat->reset)
- plat->reset();
- }
-
- /* Restore registers */
- for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
- writew(uart_wa_regdata[loop] ,
- uap->port.membase + uart_wa_reg[loop]);
-
- /* Initialise the old status of the modem signals */
- uap->old_status = readw(uap->port.membase + UART01x_FR) &
- UART01x_FR_MODEM_ANY;
-
- if (readl(base + UART011_MIS) & 0x2)
- printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n");
-
- /* Start Tx/Rx */
- tty->hw_stopped = 0;
-}
-
static void pl011_stop_tx(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -1140,13 +1200,13 @@ 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)
{
- struct tty_struct *tty = uap->port.state->port.tty;
-
pl011_fifo_to_tty(uap);
spin_unlock(&uap->port.lock);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&uap->port.state->port);
/*
* If we were temporarily out of DMA mode for a while,
* attempt to switch back to DMA mode again.
@@ -1156,9 +1216,19 @@ 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;
- } else
- uap->im &= ~UART011_RXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ writew(uap->im, uap->port.membase + UART011_IMSC);
+ } else {
+#ifdef CONFIG_DMA_ENGINE
+ /* Start Rx DMA poll */
+ if (uap->dmarx.poll_rate) {
+ uap->dmarx.last_jiffies = jiffies;
+ uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE;
+ mod_timer(&uap->dmarx.timer,
+ jiffies +
+ msecs_to_jiffies(uap->dmarx.poll_rate));
+ }
+#endif
+ }
}
spin_lock(&uap->port.lock);
}
@@ -1229,12 +1299,25 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
unsigned long flags;
unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
int handled = 0;
+ unsigned int dummy_read;
spin_lock_irqsave(&uap->port.lock, flags);
-
status = readw(uap->port.membase + UART011_MIS);
if (status) {
do {
+ if (uap->vendor->cts_event_workaround) {
+ /* workaround to make sure that all bits are unlocked.. */
+ writew(0x00, uap->port.membase + UART011_ICR);
+
+ /*
+ * WA: introduce 26ns(1 uart clk) delay before W1C;
+ * single apb access will incur 2 pclk(133.12Mhz) delay,
+ * so add 2 dummy reads
+ */
+ dummy_read = readw(uap->port.membase + UART011_ICR);
+ dummy_read = readw(uap->port.membase + UART011_ICR);
+ }
+
writew(status & ~(UART011_TXIS|UART011_RTIS|
UART011_RXIS),
uap->port.membase + UART011_ICR);
@@ -1251,11 +1334,8 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
if (status & UART011_TXIS)
pl011_tx_chars(uap);
- if (pass_counter-- == 0) {
- if (uap->interrupt_may_hang)
- tasklet_schedule(&pl011_lockup_tlet);
+ if (pass_counter-- == 0)
break;
- }
status = readw(uap->port.membase + UART011_MIS);
} while (status != 0);
@@ -1267,14 +1347,14 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-static unsigned int pl01x_tx_empty(struct uart_port *port)
+static unsigned int pl011_tx_empty(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int status = readw(uap->port.membase + UART01x_FR);
return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
}
-static unsigned int pl01x_get_mctrl(struct uart_port *port)
+static unsigned int pl011_get_mctrl(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int result = 0;
@@ -1337,11 +1417,40 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
}
#ifdef CONFIG_CONSOLE_POLL
-static int pl010_get_poll_char(struct uart_port *port)
+
+static void pl011_quiesce_irqs(struct uart_port *port)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
+ unsigned char __iomem *regs = uap->port.membase;
+
+ writew(readw(regs + UART011_MIS), regs + UART011_ICR);
+ /*
+ * There is no way to clear TXIM as this is "ready to transmit IRQ", so
+ * we simply mask it. start_tx() will unmask it.
+ *
+ * Note we can race with start_tx(), and if the race happens, the
+ * polling user might get another interrupt just after we clear it.
+ * But it should be OK and can happen even w/o the race, e.g.
+ * controller immediately got some new data and raised the IRQ.
+ *
+ * And whoever uses polling routines assumes that it manages the device
+ * (including tx queue), so we're also fine with start_tx()'s caller
+ * side.
+ */
+ writew(readw(regs + UART011_IMSC) & ~UART011_TXIM, regs + UART011_IMSC);
+}
+
+static int pl011_get_poll_char(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int status;
+ /*
+ * The caller might need IRQs lowered, e.g. if used with KDB NMI
+ * debugger.
+ */
+ pl011_quiesce_irqs(port);
+
status = readw(uap->port.membase + UART01x_FR);
if (status & UART01x_FR_RXFE)
return NO_POLL_CHAR;
@@ -1349,7 +1458,7 @@ static int pl010_get_poll_char(struct uart_port *port)
return readw(uap->port.membase + UART01x_DR);
}
-static void pl010_put_poll_char(struct uart_port *port,
+static void pl011_put_poll_char(struct uart_port *port,
unsigned char ch)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -1362,25 +1471,73 @@ static void pl010_put_poll_char(struct uart_port *port,
#endif /* CONFIG_CONSOLE_POLL */
-static int pl011_startup(struct uart_port *port)
+static int pl011_hwinit(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
- unsigned int cr;
int retval;
- retval = clk_prepare(uap->clk);
- if (retval)
- goto out;
+ /* Optionaly enable pins to be muxed in and configured */
+ pinctrl_pm_select_default_state(port->dev);
/*
* Try to enable the clock producer.
*/
- retval = clk_enable(uap->clk);
+ retval = clk_prepare_enable(uap->clk);
if (retval)
- goto clk_unprep;
+ goto out;
uap->port.uartclk = clk_get_rate(uap->clk);
+ /* Clear pending error and receive interrupts */
+ writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
+ UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
+
+ /*
+ * Save interrupts enable mask, and enable RX interrupts in case if
+ * the interrupt is used for NMI entry.
+ */
+ uap->im = readw(uap->port.membase + UART011_IMSC);
+ writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
+
+ if (dev_get_platdata(uap->port.dev)) {
+ struct amba_pl011_data *plat;
+
+ plat = dev_get_platdata(uap->port.dev);
+ if (plat->init)
+ plat->init();
+ }
+ return 0;
+ out:
+ 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, lcr_h, fbrd, ibrd;
+ int retval;
+
+ retval = pl011_hwinit(port);
+ if (retval)
+ goto clk_dis;
+
+ writew(uap->im, uap->port.membase + UART011_IMSC);
+
/*
* Allocate the IRQ
*/
@@ -1391,35 +1548,35 @@ 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);
- /* Clear pending error interrupts */
- writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS,
- uap->port.membase + UART011_ICR);
+ spin_unlock_irq(&uap->port.lock);
/*
* initialise the old status of the modem signals
@@ -1435,27 +1592,19 @@ static int pl011_startup(struct uart_port *port)
* as well.
*/
spin_lock_irq(&uap->port.lock);
+ /* Clear out any spuriously appearing RX interrupts */
+ writew(UART011_RTIS | UART011_RXIS,
+ uap->port.membase + UART011_ICR);
uap->im = UART011_RTIM;
if (!pl011_dma_rx_running(uap))
uap->im |= UART011_RXIM;
writew(uap->im, uap->port.membase + UART011_IMSC);
spin_unlock_irq(&uap->port.lock);
- if (uap->port.dev->platform_data) {
- struct amba_pl011_data *plat;
-
- plat = uap->port.dev->platform_data;
- if (plat->init)
- plat->init();
- }
-
return 0;
clk_dis:
- clk_disable(uap->clk);
- clk_unprep:
- clk_unprepare(uap->clk);
- out:
+ clk_disable_unprepare(uap->clk);
return retval;
}
@@ -1497,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
@@ -1513,13 +1664,14 @@ static void pl011_shutdown(struct uart_port *port)
/*
* Shut down the clock producer
*/
- clk_disable(uap->clk);
- clk_unprepare(uap->clk);
+ clk_disable_unprepare(uap->clk);
+ /* 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();
}
@@ -1545,6 +1697,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
*/
baud = uart_get_baud_rate(port, termios, old, 0,
port->uartclk / clkdiv);
+#ifdef CONFIG_DMA_ENGINE
+ /*
+ * Adjust RX DMA polling rate with baud rate if not specified.
+ */
+ if (uap->dmarx.auto_poll_rate)
+ uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud);
+#endif
if (baud > port->uartclk/16)
quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud);
@@ -1585,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;
/*
@@ -1635,26 +1794,29 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
old_cr &= ~ST_UART011_CR_OVSFACT;
}
+ /*
+ * Workaround for the ST Micro oversampling variants to
+ * increase the bitrate slightly, by lowering the divisor,
+ * to avoid delayed sampling of start bit at high speeds,
+ * else we see data corruption.
+ */
+ if (uap->vendor->oversampling) {
+ if ((baud >= 3000000) && (baud < 3250000) && (quot > 1))
+ quot -= 1;
+ else if ((baud > 3250000) && (quot > 2))
+ quot -= 2;
+ }
/* Set baud rate */
writew(quot & 0x3f, port->membase + UART011_FBRD);
writew(quot >> 6, port->membase + UART011_IBRD);
/*
* ----------v----------v----------v----------v-----
- * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+ * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER
+ * 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);
@@ -1669,7 +1831,7 @@ static const char *pl011_type(struct uart_port *port)
/*
* Release the memory region(s) being used by 'port'
*/
-static void pl010_release_port(struct uart_port *port)
+static void pl011_release_port(struct uart_port *port)
{
release_mem_region(port->mapbase, SZ_4K);
}
@@ -1677,7 +1839,7 @@ static void pl010_release_port(struct uart_port *port)
/*
* Request the memory region(s) being used by 'port'
*/
-static int pl010_request_port(struct uart_port *port)
+static int pl011_request_port(struct uart_port *port)
{
return request_mem_region(port->mapbase, SZ_4K, "uart-pl011")
!= NULL ? 0 : -EBUSY;
@@ -1686,18 +1848,18 @@ static int pl010_request_port(struct uart_port *port)
/*
* Configure/autoconfigure the port.
*/
-static void pl010_config_port(struct uart_port *port, int flags)
+static void pl011_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_AMBA;
- pl010_request_port(port);
+ pl011_request_port(port);
}
}
/*
* verify the new serial_struct (for TIOCSSERIAL).
*/
-static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
+static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser)
{
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
@@ -1710,9 +1872,9 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
}
static struct uart_ops amba_pl011_pops = {
- .tx_empty = pl01x_tx_empty,
+ .tx_empty = pl011_tx_empty,
.set_mctrl = pl011_set_mctrl,
- .get_mctrl = pl01x_get_mctrl,
+ .get_mctrl = pl011_get_mctrl,
.stop_tx = pl011_stop_tx,
.start_tx = pl011_start_tx,
.stop_rx = pl011_stop_rx,
@@ -1723,13 +1885,14 @@ static struct uart_ops amba_pl011_pops = {
.flush_buffer = pl011_dma_flush_buffer,
.set_termios = pl011_set_termios,
.type = pl011_type,
- .release_port = pl010_release_port,
- .request_port = pl010_request_port,
- .config_port = pl010_config_port,
- .verify_port = pl010_verify_port,
+ .release_port = pl011_release_port,
+ .request_port = pl011_request_port,
+ .config_port = pl011_config_port,
+ .verify_port = pl011_verify_port,
#ifdef CONFIG_CONSOLE_POLL
- .poll_get_char = pl010_get_poll_char,
- .poll_put_char = pl010_put_poll_char,
+ .poll_init = pl011_hwinit,
+ .poll_get_char = pl011_get_poll_char,
+ .poll_put_char = pl011_put_poll_char,
#endif
};
@@ -1845,14 +2008,17 @@ static int __init pl011_console_setup(struct console *co, char *options)
if (!uap)
return -ENODEV;
+ /* Allow pins to be muxed in and configured */
+ pinctrl_pm_select_default_state(uap->port.dev);
+
ret = clk_prepare(uap->clk);
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();
}
@@ -1879,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
@@ -1893,6 +2088,38 @@ static struct uart_driver amba_reg = {
.cons = AMBA_CONSOLE,
};
+static int pl011_probe_dt_alias(int index, struct device *dev)
+{
+ struct device_node *np;
+ static bool seen_dev_with_alias = false;
+ static bool seen_dev_without_alias = false;
+ int ret = index;
+
+ if (!IS_ENABLED(CONFIG_OF))
+ return ret;
+
+ np = dev->of_node;
+ if (!np)
+ return ret;
+
+ ret = of_alias_get_id(np, "serial");
+ if (IS_ERR_VALUE(ret)) {
+ seen_dev_without_alias = true;
+ ret = index;
+ } else {
+ seen_dev_with_alias = true;
+ if (ret >= ARRAY_SIZE(amba_ports) || amba_ports[ret] != NULL) {
+ dev_warn(dev, "requested serial port %d not available.\n", ret);
+ ret = index;
+ }
+ }
+
+ if (seen_dev_with_alias && seen_dev_without_alias)
+ dev_warn(dev, "aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n");
+
+ return ret;
+}
+
static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
{
struct uart_amba_port *uap;
@@ -1909,30 +2136,33 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
goto out;
}
- uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL);
+ uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port),
+ GFP_KERNEL);
if (uap == NULL) {
ret = -ENOMEM;
goto out;
}
- base = ioremap(dev->res.start, resource_size(&dev->res));
+ i = pl011_probe_dt_alias(i, &dev->dev);
+
+ base = devm_ioremap(&dev->dev, dev->res.start,
+ resource_size(&dev->res));
if (!base) {
ret = -ENOMEM;
- goto free;
+ goto out;
}
- uap->clk = clk_get(&dev->dev, NULL);
+ uap->clk = devm_clk_get(&dev->dev, NULL);
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
- goto unmap;
+ goto out;
}
uap->vendor = vendor;
uap->lcrh_rx = vendor->lcrh_rx;
uap->lcrh_tx = vendor->lcrh_tx;
uap->old_cr = 0;
- uap->fifosize = vendor->fifosize;
- uap->interrupt_may_hang = vendor->interrupt_may_hang;
+ uap->fifosize = vendor->get_fifosize(dev);
uap->port.dev = &dev->dev;
uap->port.mapbase = dev->res.start;
uap->port.membase = base;
@@ -1942,23 +2172,31 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
uap->port.ops = &amba_pl011_pops;
uap->port.flags = UPF_BOOT_AUTOCONF;
uap->port.line = i;
- pl011_dma_probe(uap);
+ pl011_dma_probe(&dev->dev, uap);
+
+ /* Ensure interrupts from this UART are masked and cleared */
+ writew(0, uap->port.membase + UART011_IMSC);
+ writew(0xffff, uap->port.membase + UART011_ICR);
snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev));
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);
- clk_put(uap->clk);
- unmap:
- iounmap(base);
- free:
- kfree(uap);
}
out:
return ret;
@@ -1967,27 +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);
- iounmap(uap->port.membase);
- clk_put(uap->clk);
- kfree(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;
@@ -1995,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;
@@ -2006,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,
@@ -2025,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 7162f70d926..de11ab8ffd9 100644
--- a/drivers/tty/serial/apbuart.c
+++ b/drivers/tty/serial/apbuart.c
@@ -78,7 +78,6 @@ static void apbuart_enable_ms(struct uart_port *port)
static void apbuart_rx_chars(struct uart_port *port)
{
- struct tty_struct *tty = port->state->port.tty;
unsigned int status, ch, rsr, flag;
unsigned int max_chars = port->fifosize;
@@ -126,7 +125,9 @@ static void apbuart_rx_chars(struct uart_port *port)
status = UART_GET_STATUS(port);
}
- tty_flip_buffer_push(tty);
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
}
static void apbuart_tx_chars(struct uart_port *port)
@@ -554,7 +555,7 @@ static struct uart_driver grlib_apbuart_driver = {
/* OF Platform Driver */
/* ======================================================================== */
-static int __devinit apbuart_probe(struct platform_device *op)
+static int apbuart_probe(struct platform_device *op)
{
int i;
struct uart_port *port = NULL;
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index e4f60e2b87f..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,12 +26,20 @@
#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"
+#define AR933X_UART_MAX_SCALE 0xff
+#define AR933X_UART_MAX_STEP 0xffff
+
+#define AR933X_UART_MIN_BAUD 300
+#define AR933X_UART_MAX_BAUD 3000000
+
#define AR933X_DUMMY_STATUS_RD 0x01
static struct uart_driver ar933x_uart_driver;
@@ -37,8 +47,16 @@ static struct uart_driver ar933x_uart_driver;
struct ar933x_uart_port {
struct uart_port 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)
{
@@ -162,6 +180,57 @@ static void ar933x_uart_enable_ms(struct uart_port *port)
{
}
+/*
+ * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17))
+ */
+static unsigned long ar933x_uart_get_baud(unsigned int clk,
+ unsigned int scale,
+ unsigned int step)
+{
+ u64 t;
+ u32 div;
+
+ div = (2 << 16) * (scale + 1);
+ t = clk;
+ t *= step;
+ t += (div / 2);
+ do_div(t, div);
+
+ return t;
+}
+
+static void ar933x_uart_get_scale_step(unsigned int clk,
+ unsigned int baud,
+ unsigned int *scale,
+ unsigned int *step)
+{
+ unsigned int tscale;
+ long min_diff;
+
+ *scale = 0;
+ *step = 0;
+
+ min_diff = baud;
+ for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) {
+ u64 tstep;
+ int diff;
+
+ tstep = baud * (tscale + 1);
+ tstep *= (2 << 16);
+ do_div(tstep, clk);
+
+ if (tstep > AR933X_UART_MAX_STEP)
+ break;
+
+ diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud);
+ if (diff < min_diff) {
+ min_diff = diff;
+ *scale = tscale;
+ *step = tstep;
+ }
+ }
+}
+
static void ar933x_uart_set_termios(struct uart_port *port,
struct ktermios *new,
struct ktermios *old)
@@ -169,7 +238,7 @@ static void ar933x_uart_set_termios(struct uart_port *port,
struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
unsigned int cs;
unsigned long flags;
- unsigned int baud, scale;
+ unsigned int baud, scale, step;
/* Only CS8 is supported */
new->c_cflag &= ~CSIZE;
@@ -191,8 +260,8 @@ static void ar933x_uart_set_termios(struct uart_port *port,
/* Mark/space parity is not supported */
new->c_cflag &= ~CMSPAR;
- baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
- scale = (port->uartclk / (16 * baud)) - 1;
+ baud = uart_get_baud_rate(port, new, old, up->min_baud, up->max_baud);
+ ar933x_uart_get_scale_step(port->uartclk, baud, &scale, &step);
/*
* Ok, we're now changing the port state. Do it with
@@ -200,6 +269,10 @@ static void ar933x_uart_set_termios(struct uart_port *port,
*/
spin_lock_irqsave(&up->port.lock, flags);
+ /* disable the UART */
+ ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG,
+ AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S);
+
/* Update the per-port timeout. */
uart_update_timeout(port, new->c_cflag, baud);
@@ -210,7 +283,7 @@ static void ar933x_uart_set_termios(struct uart_port *port,
up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD;
ar933x_uart_write(up, AR933X_UART_CLOCK_REG,
- scale << AR933X_UART_CLOCK_SCALE_S | 8192);
+ scale << AR933X_UART_CLOCK_SCALE_S | step);
/* setup configuration register */
ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs);
@@ -219,6 +292,11 @@ static void ar933x_uart_set_termios(struct uart_port *port,
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_HOST_INT_EN);
+ /* reenable the UART */
+ ar933x_uart_rmw(up, AR933X_UART_CS_REG,
+ AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S,
+ AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
+
spin_unlock_irqrestore(&up->port.lock, flags);
if (tty_termios_baud_rate(new))
@@ -227,10 +305,9 @@ static void ar933x_uart_set_termios(struct uart_port *port,
static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
{
- struct tty_struct *tty;
+ struct tty_port *port = &up->port.state->port;
int max_count = 256;
- tty = tty_port_tty_get(&up->port.state->port);
do {
unsigned int rdata;
unsigned char ch;
@@ -243,11 +320,6 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
ar933x_uart_write(up, AR933X_UART_DATA_REG,
AR933X_UART_DATA_RX_CSR);
- if (!tty) {
- /* discard the data if no tty available */
- continue;
- }
-
up->port.icount.rx++;
ch = rdata & AR933X_UART_DATA_TX_RX_MASK;
@@ -255,13 +327,12 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
continue;
if ((up->port.ignore_status_mask & AR933X_DUMMY_STATUS_RD) == 0)
- tty_insert_flip_char(tty, ch, TTY_NORMAL);
+ tty_insert_flip_char(port, ch, TTY_NORMAL);
} while (max_count-- > 0);
- if (tty) {
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
- }
+ 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)
@@ -401,6 +472,8 @@ static void ar933x_uart_config_port(struct uart_port *port, int flags)
static int ar933x_uart_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
+ struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+
if (ser->type != PORT_UNKNOWN &&
ser->type != PORT_AR933X)
return -EINVAL;
@@ -408,7 +481,8 @@ static int ar933x_uart_verify_port(struct uart_port *port,
if (ser->irq < 0 || ser->irq >= NR_IRQS)
return -EINVAL;
- if (ser->baud_base < 28800)
+ if (ser->baud_base < up->min_baud ||
+ ser->baud_base > up->max_baud)
return -EINVAL;
return 0;
@@ -433,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];
@@ -533,122 +605,142 @@ 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 __devinit ar933x_uart_probe(struct platform_device *pdev)
+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;
port->ops = &ar933x_uart_ops;
+ baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1);
+ up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD);
+
+ baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
+ up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
+
ar933x_uart_add_console_port(up);
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;
}
-static int __devexit ar933x_uart_remove(struct platform_device *pdev)
+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 = __devexit_p(ar933x_uart_remove),
+ .remove = ar933x_uart_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ar933x_uart_of_ids),
},
};
@@ -656,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
new file mode 100644
index 00000000000..008c223eaf2
--- /dev/null
+++ b/drivers/tty/serial/arc_uart.c
@@ -0,0 +1,783 @@
+/*
+ * ARC On-Chip(fpga) UART Driver
+ *
+ * Copyright (C) 2010-2012 Synopsys, Inc. (www.synopsys.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.
+ *
+ * vineetg: July 10th 2012
+ * -Decoupled the driver from arch/arc
+ * +Using platform_get_resource() for irq/membase (thx to bfin_uart.c)
+ * +Using early_platform_xxx() for early console (thx to mach-shmobile/xxx)
+ *
+ * Vineetg: Aug 21st 2010
+ * -Is uart_tx_stopped() not done in tty write path as it has already been
+ * taken care of, in serial core
+ *
+ * Vineetg: Aug 18th 2010
+ * -New Serial Core based ARC UART driver
+ * -Derived largely from blackfin driver albiet with some major tweaks
+ *
+ * TODO:
+ * -check if sysreq works
+ */
+
+#if defined(CONFIG_SERIAL_ARC_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/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+/*************************************
+ * ARC UART Hardware Specs
+ ************************************/
+#define ARC_UART_TX_FIFO_SIZE 1
+
+/*
+ * UART Register set (this is not a Standards Compliant IP)
+ * Also each reg is Word aligned, but only 8 bits wide
+ */
+#define R_ID0 0
+#define R_ID1 4
+#define R_ID2 8
+#define R_ID3 12
+#define R_DATA 16
+#define R_STS 20
+#define R_BAUDL 24
+#define R_BAUDH 28
+
+/* Bits for UART Status Reg (R/W) */
+#define RXIENB 0x04 /* Receive Interrupt Enable */
+#define TXIENB 0x40 /* Transmit Interrupt Enable */
+
+#define RXEMPTY 0x20 /* Receive FIFO Empty: No char receivede */
+#define TXEMPTY 0x80 /* Transmit FIFO Empty, thus char can be written into */
+
+#define RXFULL 0x08 /* Receive FIFO full */
+#define RXFULL1 0x10 /* Receive FIFO has space for 1 char (tot space=4) */
+
+#define RXFERR 0x01 /* Frame Error: Stop Bit not detected */
+#define RXOERR 0x02 /* OverFlow Err: Char recv but RXFULL still set */
+
+/* Uart bit fiddling helpers: lowest level */
+#define RBASE(uart, reg) (uart->port.membase + reg)
+#define UART_REG_SET(u, r, v) writeb((v), RBASE(u, r))
+#define UART_REG_GET(u, r) readb(RBASE(u, r))
+
+#define UART_REG_OR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) | (v))
+#define UART_REG_CLR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) & ~(v))
+
+/* Uart bit fiddling helpers: API level */
+#define UART_SET_DATA(uart, val) UART_REG_SET(uart, R_DATA, val)
+#define UART_GET_DATA(uart) UART_REG_GET(uart, R_DATA)
+
+#define UART_SET_BAUDH(uart, val) UART_REG_SET(uart, R_BAUDH, val)
+#define UART_SET_BAUDL(uart, val) UART_REG_SET(uart, R_BAUDL, val)
+
+#define UART_CLR_STATUS(uart, val) UART_REG_CLR(uart, R_STS, val)
+#define UART_GET_STATUS(uart) UART_REG_GET(uart, R_STS)
+
+#define UART_ALL_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB|TXIENB)
+#define UART_RX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB)
+#define UART_TX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, TXIENB)
+
+#define UART_ALL_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB|TXIENB)
+#define UART_RX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB)
+#define UART_TX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, TXIENB)
+
+#define ARC_SERIAL_DEV_NAME "ttyARC"
+
+struct arc_uart_port {
+ struct uart_port port;
+ unsigned long baud;
+ int is_emulated; /* H/w vs. Instruction Set Simulator */
+};
+
+#define to_arc_port(uport) container_of(uport, struct arc_uart_port, port)
+
+static struct arc_uart_port arc_uart_ports[CONFIG_SERIAL_ARC_NR_PORTS];
+
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+static struct console arc_console;
+#endif
+
+#define DRIVER_NAME "arc-uart"
+
+static struct uart_driver arc_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = ARC_SERIAL_DEV_NAME,
+ .major = 0,
+ .minor = 0,
+ .nr = CONFIG_SERIAL_ARC_NR_PORTS,
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+ .cons = &arc_console,
+#endif
+};
+
+static void arc_serial_stop_rx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ UART_RX_IRQ_DISABLE(uart);
+}
+
+static void arc_serial_stop_tx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ cpu_relax();
+
+ UART_TX_IRQ_DISABLE(uart);
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int arc_serial_tx_empty(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+ unsigned int stat;
+
+ stat = UART_GET_STATUS(uart);
+ if (stat & TXEMPTY)
+ return TIOCSER_TEMT;
+
+ return 0;
+}
+
+/*
+ * Driver internal routine, used by both tty(serial core) as well as tx-isr
+ * -Called under spinlock in either cases
+ * -also tty->stopped has already been checked
+ * = by uart_start( ) before calling us
+ * = tx_ist checks that too before calling
+ */
+static void arc_serial_tx_chars(struct arc_uart_port *uart)
+{
+ struct circ_buf *xmit = &uart->port.state->xmit;
+ int sent = 0;
+ unsigned char ch;
+
+ if (unlikely(uart->port.x_char)) {
+ UART_SET_DATA(uart, uart->port.x_char);
+ uart->port.icount.tx++;
+ uart->port.x_char = 0;
+ sent = 1;
+ } else if (!uart_circ_empty(xmit)) {
+ ch = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ uart->port.icount.tx++;
+ while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ cpu_relax();
+ UART_SET_DATA(uart, ch);
+ sent = 1;
+ }
+
+ /*
+ * If num chars in xmit buffer are too few, ask tty layer for more.
+ * By Hard ISR to schedule processing in software interrupt part
+ */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&uart->port);
+
+ if (sent)
+ UART_TX_IRQ_ENABLE(uart);
+}
+
+/*
+ * port is locked and interrupts are disabled
+ * uart_start( ) calls us under the port spinlock irqsave
+ */
+static void arc_serial_start_tx(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ arc_serial_tx_chars(uart);
+}
+
+static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status)
+{
+ unsigned int ch, flg = 0;
+
+ /*
+ * UART has 4 deep RX-FIFO. Driver's recongnition of this fact
+ * is very subtle. Here's how ...
+ * Upon getting a RX-Intr, such that RX-EMPTY=0, meaning data available,
+ * driver reads the DATA Reg and keeps doing that in a loop, until
+ * RX-EMPTY=1. Multiple chars being avail, with a single Interrupt,
+ * before RX-EMPTY=0, implies some sort of buffering going on in the
+ * controller, which is indeed the Rx-FIFO.
+ */
+ 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++;
+ flg = TTY_OVERRUN;
+ UART_CLR_STATUS(uart, RXOERR);
+ }
+
+ if (status & RXFERR) {
+ uart->port.icount.frame++;
+ flg = TTY_FRAME;
+ UART_CLR_STATUS(uart, RXFERR);
+ }
+ } else
+ flg = TTY_NORMAL;
+
+ if (status & RXEMPTY)
+ continue;
+
+ 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);
+
+ 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));
+}
+
+/*
+ * A note on the Interrupt handling state machine of this driver
+ *
+ * kernel printk writes funnel thru the console driver framework and in order
+ * to keep things simple as well as efficient, it writes to UART in polled
+ * mode, in one shot, and exits.
+ *
+ * OTOH, Userland output (via tty layer), uses interrupt based writes as there
+ * can be undeterministic delay between char writes.
+ *
+ * Thus Rx-interrupts are always enabled, while tx-interrupts are by default
+ * disabled.
+ *
+ * When tty has some data to send out, serial core calls driver's start_tx
+ * which
+ * -checks-if-tty-buffer-has-char-to-send
+ * -writes-data-to-uart
+ * -enable-tx-intr
+ *
+ * Once data bits are pushed out, controller raises the Tx-room-avail-Interrupt.
+ * The first thing Tx ISR does is disable further Tx interrupts (as this could
+ * be the last char to send, before settling down into the quiet polled mode).
+ * It then calls the exact routine used by tty layer write to send out any
+ * more char in tty buffer. In case of sending, it re-enables Tx-intr. In case
+ * of no data, it remains disabled.
+ * This is how the transmit state machine is dynamically switched on/off
+ */
+
+static irqreturn_t arc_serial_isr(int irq, void *dev_id)
+{
+ struct arc_uart_port *uart = dev_id;
+ unsigned int status;
+
+ status = UART_GET_STATUS(uart);
+
+ /*
+ * Single IRQ for both Rx (data available) Tx (room available) Interrupt
+ * notifications from the UART Controller.
+ * To demultiplex between the two, we check the relevant bits
+ */
+ if (status & RXIENB) {
+
+ /* already in ISR, no need of xx_irqsave */
+ spin_lock(&uart->port.lock);
+ arc_serial_rx_chars(uart, status);
+ spin_unlock(&uart->port.lock);
+ }
+
+ if ((status & TXIENB) && (status & TXEMPTY)) {
+
+ /* Unconditionally disable further Tx-Interrupts.
+ * will be enabled by tx_chars() if needed.
+ */
+ UART_TX_IRQ_DISABLE(uart);
+
+ spin_lock(&uart->port.lock);
+
+ if (!uart_tx_stopped(&uart->port))
+ arc_serial_tx_chars(uart);
+
+ spin_unlock(&uart->port.lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int arc_serial_get_mctrl(struct uart_port *port)
+{
+ /*
+ * Pretend we have a Modem status reg and following bits are
+ * always set, to satify the serial core state machine
+ * (DSR) Data Set Ready
+ * (CTS) Clear To Send
+ * (CAR) Carrier Detect
+ */
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void arc_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* MCR not present */
+}
+
+/* Enable Modem Status Interrupts */
+
+static void arc_serial_enable_ms(struct uart_port *port)
+{
+ /* MSR not present */
+}
+
+static void arc_serial_break_ctl(struct uart_port *port, int break_state)
+{
+ /* ARC UART doesn't support sending Break signal */
+}
+
+static int arc_serial_startup(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ /* Before we hook up the ISR, Disable all UART Interrupts */
+ UART_ALL_IRQ_DISABLE(uart);
+
+ if (request_irq(uart->port.irq, arc_serial_isr, 0, "arc uart rx-tx",
+ uart)) {
+ dev_warn(uart->port.dev, "Unable to attach ARC UART intr\n");
+ return -EBUSY;
+ }
+
+ UART_RX_IRQ_ENABLE(uart); /* Only Rx IRQ enabled to begin with */
+
+ return 0;
+}
+
+/* This is not really needed */
+static void arc_serial_shutdown(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+ free_irq(uart->port.irq, uart);
+}
+
+static void
+arc_serial_set_termios(struct uart_port *port, struct ktermios *new,
+ struct ktermios *old)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+ unsigned int baud, uartl, uarth, hw_val;
+ unsigned long flags;
+
+ /*
+ * Use the generic handler so that any specially encoded baud rates
+ * such as SPD_xx flags or "%B0" can be handled
+ * Max Baud I suppose will not be more than current 115K * 4
+ * Formula for ARC UART is: hw-val = ((CLK/(BAUD*4)) -1)
+ * spread over two 8-bit registers
+ */
+ baud = uart_get_baud_rate(port, new, old, 0, 460800);
+
+ hw_val = port->uartclk / (uart->baud * 4) - 1;
+ uartl = hw_val & 0xFF;
+ uarth = (hw_val >> 8) & 0xFF;
+
+ /*
+ * UART ISS(Instruction Set simulator) emulation has a subtle bug:
+ * A existing value of Baudh = 0 is used as a indication to startup
+ * it's internal state machine.
+ * Thus if baudh is set to 0, 2 times, it chokes.
+ * This happens with BAUD=115200 and the formaula above
+ * Until that is fixed, when running on ISS, we will set baudh to !0
+ */
+ if (uart->is_emulated)
+ uarth = 1;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ UART_ALL_IRQ_DISABLE(uart);
+
+ UART_SET_BAUDL(uart, uartl);
+ UART_SET_BAUDH(uart, uarth);
+
+ UART_RX_IRQ_ENABLE(uart);
+
+ /*
+ * UART doesn't support Parity/Hardware Flow Control;
+ * Only supports 8N1 character size
+ */
+ new->c_cflag &= ~(CMSPAR|CRTSCTS|CSIZE);
+ new->c_cflag |= CS8;
+
+ if (old)
+ tty_termios_copy_hw(new, old);
+
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(new))
+ tty_termios_encode_baud_rate(new, baud, baud);
+
+ uart_update_timeout(port, new->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *arc_serial_type(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ return uart->port.type == PORT_ARC ? DRIVER_NAME : NULL;
+}
+
+static void arc_serial_release_port(struct uart_port *port)
+{
+}
+
+static int arc_serial_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int
+arc_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ if (port->type != PORT_UNKNOWN && ser->type != PORT_ARC)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void arc_serial_config_port(struct uart_port *port, int flags)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ if (flags & UART_CONFIG_TYPE)
+ uart->port.type = PORT_ARC;
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE)
+
+static void arc_serial_poll_putchar(struct uart_port *port, unsigned char chr)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+
+ while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ cpu_relax();
+
+ UART_SET_DATA(uart, chr);
+}
+#endif
+
+#ifdef CONFIG_CONSOLE_POLL
+static int arc_serial_poll_getchar(struct uart_port *port)
+{
+ struct arc_uart_port *uart = to_arc_port(port);
+ unsigned char chr;
+
+ while (!(UART_GET_STATUS(uart) & RXEMPTY))
+ cpu_relax();
+
+ chr = UART_GET_DATA(uart);
+ return chr;
+}
+#endif
+
+static struct uart_ops arc_serial_pops = {
+ .tx_empty = arc_serial_tx_empty,
+ .set_mctrl = arc_serial_set_mctrl,
+ .get_mctrl = arc_serial_get_mctrl,
+ .stop_tx = arc_serial_stop_tx,
+ .start_tx = arc_serial_start_tx,
+ .stop_rx = arc_serial_stop_rx,
+ .enable_ms = arc_serial_enable_ms,
+ .break_ctl = arc_serial_break_ctl,
+ .startup = arc_serial_startup,
+ .shutdown = arc_serial_shutdown,
+ .set_termios = arc_serial_set_termios,
+ .type = arc_serial_type,
+ .release_port = arc_serial_release_port,
+ .request_port = arc_serial_request_port,
+ .config_port = arc_serial_config_port,
+ .verify_port = arc_serial_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_put_char = arc_serial_poll_putchar,
+ .poll_get_char = arc_serial_poll_getchar,
+#endif
+};
+
+static int
+arc_uart_init_one(struct platform_device *pdev, int dev_id)
+{
+ struct resource *res, *res2;
+ unsigned long *plat_data;
+ struct arc_uart_port *uart = &arc_uart_ports[dev_id];
+
+ plat_data = dev_get_platdata(&pdev->dev);
+ if (!plat_data)
+ return -ENODEV;
+
+ uart->is_emulated = !!plat_data[0]; /* workaround ISS bug */
+
+ if (is_early_platform_device(pdev)) {
+ uart->port.uartclk = plat_data[1];
+ uart->baud = plat_data[2];
+ } else {
+ struct device_node *np = pdev->dev.of_node;
+ u32 val;
+
+ if (of_property_read_u32(np, "clock-frequency", &val)) {
+ dev_err(&pdev->dev, "clock-frequency property NOTset\n");
+ return -EINVAL;
+ }
+ uart->port.uartclk = val;
+
+ if (of_property_read_u32(np, "current-speed", &val)) {
+ dev_err(&pdev->dev, "current-speed property NOT set\n");
+ return -EINVAL;
+ }
+ uart->baud = val;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res2)
+ return -ENODEV;
+
+ uart->port.mapbase = res->start;
+ uart->port.membase = ioremap_nocache(res->start, resource_size(res));
+ if (!uart->port.membase)
+ /* No point of dev_err since UART itself is hosed here */
+ return -ENXIO;
+
+ uart->port.irq = res2->start;
+ uart->port.dev = &pdev->dev;
+ uart->port.iotype = UPIO_MEM;
+ uart->port.flags = UPF_BOOT_AUTOCONF;
+ uart->port.line = dev_id;
+ uart->port.ops = &arc_serial_pops;
+
+ uart->port.fifosize = ARC_UART_TX_FIFO_SIZE;
+
+ /*
+ * uart_insert_char( ) uses it in decideding whether to ignore a
+ * char or not. Explicitly setting it here, removes the subtelty
+ */
+ uart->port.ignore_status_mask = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+
+static int arc_serial_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index < 0 || co->index >= CONFIG_SERIAL_ARC_NR_PORTS)
+ return -ENODEV;
+
+ /*
+ * The uart port backing the console (e.g. ttyARC1) might not have been
+ * init yet. If so, defer the console setup to after the port.
+ */
+ port = &arc_uart_ports[co->index].port;
+ if (!port->membase)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ /*
+ * Serial core will call port->ops->set_termios( )
+ * which will set the baud reg
+ */
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static void arc_serial_console_putchar(struct uart_port *port, int ch)
+{
+ arc_serial_poll_putchar(port, (unsigned char)ch);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void arc_serial_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *port = &arc_uart_ports[co->index].port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ uart_console_write(port, s, count, arc_serial_console_putchar);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static struct console arc_console = {
+ .name = ARC_SERIAL_DEV_NAME,
+ .write = arc_serial_console_write,
+ .device = uart_console_device,
+ .setup = arc_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &arc_uart_driver
+};
+
+static __init void early_serial_write(struct console *con, const char *s,
+ unsigned int n)
+{
+ struct uart_port *port = &arc_uart_ports[con->index].port;
+ unsigned int i;
+
+ for (i = 0; i < n; i++, s++) {
+ if (*s == '\n')
+ arc_serial_poll_putchar(port, '\r');
+ arc_serial_poll_putchar(port, *s);
+ }
+}
+
+static struct console arc_early_serial_console __initdata = {
+ .name = "early_ARCuart",
+ .write = early_serial_write,
+ .flags = CON_PRINTBUFFER | CON_BOOT,
+ .index = -1
+};
+
+static int __init arc_serial_probe_earlyprintk(struct platform_device *pdev)
+{
+ int dev_id = pdev->id < 0 ? 0 : pdev->id;
+ int rc;
+
+ arc_early_serial_console.index = dev_id;
+
+ rc = arc_uart_init_one(pdev, dev_id);
+ if (rc)
+ panic("early console init failed\n");
+
+ arc_serial_console_setup(&arc_early_serial_console, NULL);
+
+ register_console(&arc_early_serial_console);
+ return 0;
+}
+#endif /* CONFIG_SERIAL_ARC_CONSOLE */
+
+static int arc_serial_probe(struct platform_device *pdev)
+{
+ int rc, dev_id;
+ struct device_node *np = pdev->dev.of_node;
+
+ /* no device tree device */
+ if (!np)
+ return -ENODEV;
+
+ dev_id = of_alias_get_id(np, "serial");
+ if (dev_id < 0)
+ dev_id = 0;
+
+ rc = arc_uart_init_one(pdev, dev_id);
+ if (rc)
+ return rc;
+
+ rc = uart_add_one_port(&arc_uart_driver, &arc_uart_ports[dev_id].port);
+ return rc;
+}
+
+static int arc_serial_remove(struct platform_device *pdev)
+{
+ /* This will never be called */
+ return 0;
+}
+
+static const struct of_device_id arc_uart_dt_ids[] = {
+ { .compatible = "snps,arc-uart" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, arc_uart_dt_ids);
+
+static struct platform_driver arc_platform_driver = {
+ .probe = arc_serial_probe,
+ .remove = arc_serial_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = arc_uart_dt_ids,
+ },
+};
+
+#ifdef CONFIG_SERIAL_ARC_CONSOLE
+
+static struct platform_driver early_arc_platform_driver __initdata = {
+ .probe = arc_serial_probe_earlyprintk,
+ .remove = arc_serial_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+/*
+ * Register an early platform driver of "earlyprintk" class.
+ * ARCH platform code installs the driver and probes the early devices
+ * The installation could rely on user specifying earlyprintk=xyx in cmd line
+ * or it could be done independently, for all "earlyprintk" class drivers.
+ * [see arch/arc/plat-arcfpga/platform.c]
+ */
+early_platform_init("earlyprintk", &early_arc_platform_driver);
+
+#endif /* CONFIG_SERIAL_ARC_CONSOLE */
+
+static int __init arc_serial_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&arc_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&arc_platform_driver);
+ if (ret)
+ uart_unregister_driver(&arc_uart_driver);
+
+ return ret;
+}
+
+static void __exit arc_serial_exit(void)
+{
+ platform_driver_unregister(&arc_platform_driver);
+ uart_unregister_driver(&arc_uart_driver);
+}
+
+module_init(arc_serial_init);
+module_exit(arc_serial_exit);
+
+MODULE_LICENSE("GPL");
+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 10605ecc99a..c4f75031410 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -35,22 +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/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>
-#include <asm/mach/serial_at91.h>
-#include <mach/board.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 */
@@ -61,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);
@@ -99,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)
@@ -114,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;
@@ -141,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;
@@ -155,11 +167,22 @@ 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];
-static unsigned long atmel_ports_in_use;
+static DECLARE_BITMAP(atmel_ports_in_use, ATMEL_MAX_UART);
#ifdef SUPPORT_SYSRQ
static struct console atmel_console;
@@ -182,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)
{
@@ -234,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
@@ -266,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
@@ -293,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)
@@ -320,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);
@@ -336,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);
}
/*
@@ -346,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);
}
@@ -365,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.*/
@@ -389,7 +458,9 @@ static void atmel_start_rx(struct uart_port *port)
{
UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */
- if (atmel_use_dma_rx(port)) {
+ UART_PUT_CR(port, ATMEL_US_RXEN);
+
+ if (atmel_use_pdc_rx(port)) {
/* enable PDC controller */
UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
port->read_status_mask);
@@ -404,7 +475,9 @@ static void atmel_start_rx(struct uart_port *port)
*/
static void atmel_stop_rx(struct uart_port *port)
{
- if (atmel_use_dma_rx(port)) {
+ UART_PUT_CR(port, ATMEL_US_RXDIS);
+
+ 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 |
@@ -419,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);
}
/*
@@ -561,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.
*/
@@ -569,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.
@@ -588,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);
@@ -639,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;
@@ -655,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;
@@ -707,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);
@@ -771,14 +1292,30 @@ static void atmel_rx_from_ring(struct uart_port *port)
* uart_start(), which takes the lock.
*/
spin_unlock(&port->lock);
- tty_flip_buffer_push(port->state->port.tty);
+ tty_flip_buffer_push(&port->state->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);
- struct tty_struct *tty = port->state->port.tty;
+ 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;
struct atmel_dma_buffer *pdc;
int rx_idx = atmel_port->pdc_rx_idx;
unsigned int head;
@@ -817,7 +1354,8 @@ static void atmel_rx_from_dma(struct uart_port *port)
*/
count = head - tail;
- tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count);
+ tty_insert_flip_string(tport, pdc->buf + pdc->ofs,
+ count);
dma_sync_single_for_device(port->dev, pdc->dma_addr,
pdc->dma_size, DMA_FROM_DEVICE);
@@ -845,12 +1383,51 @@ static void atmel_rx_from_dma(struct uart_port *port)
* uart_start(), which takes the lock.
*/
spin_unlock(&port->lock);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
spin_lock(&port->lock);
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.
*/
@@ -864,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;
@@ -889,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;
@@ -912,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
@@ -919,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];
-
- 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;
- }
+ retval = atmel_request_gpio_irq(port);
+ if (retval)
+ goto free_irq;
- 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);
- }
- if (atmel_use_dma_tx(port)) {
- struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
- struct circ_buf *xmit = &port->state->xmit;
+ /*
+ * Initialize DMA (if necessary)
+ */
+ atmel_init_property(atmel_port, pdev);
- 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 (atmel_port->prepare_rx) {
+ retval = atmel_port->prepare_rx(port);
+ if (retval < 0)
+ atmel_set_ops(port);
}
- /*
- * 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;
/*
@@ -996,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;
}
/*
@@ -1018,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;
}
/*
@@ -1076,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;
}
@@ -1096,7 +1842,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
* Enable the peripheral clock for this serial port.
* This is called on uart_open() or a resume event.
*/
- clk_enable(atmel_port->clk);
+ clk_prepare_enable(atmel_port->clk);
/* re-enable interrupts if we disabled some on suspend */
UART_PUT_IER(port, atmel_port->backup_imr);
@@ -1110,10 +1856,10 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
* Disable the peripheral clock for this serial port.
* This is called on uart_close() or a suspend event.
*/
- clk_disable(atmel_port->clk);
+ 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);
}
}
@@ -1186,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);
@@ -1228,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 */
@@ -1419,54 +2162,20 @@ static struct uart_ops atmel_pops = {
#endif
};
-static void __devinit 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.
*/
-static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
+static int atmel_init_port(struct atmel_uart_port *atmel_port,
struct platform_device *pdev)
{
+ 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;
@@ -1492,40 +2201,37 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
/* for console, the clock could already be configured */
if (!atmel_port->clk) {
atmel_port->clk = clk_get(&pdev->dev, "usart");
- clk_enable(atmel_port->clk);
+ if (IS_ERR(atmel_port->clk)) {
+ ret = PTR_ERR(atmel_port->clk);
+ atmel_port->clk = NULL;
+ return ret;
+ }
+ ret = clk_prepare_enable(atmel_port->clk);
+ if (ret) {
+ clk_put(atmel_port->clk);
+ atmel_port->clk = NULL;
+ return ret;
+ }
port->uartclk = clk_get_rate(atmel_port->clk);
- clk_disable(atmel_port->clk);
+ clk_disable_unprepare(atmel_port->clk);
/* only enable clock when USART is in use */
}
/* 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 {
atmel_port->tx_done_mask = ATMEL_US_TXRDY;
}
-}
-/*
- * Register board-specific modem-control line handlers.
- */
-void __init atmel_register_uart_fns(struct atmel_port_fns *fns)
-{
- if (fns->enable_ms)
- atmel_pops.enable_ms = fns->enable_ms;
- if (fns->get_mctrl)
- atmel_pops.get_mctrl = fns->get_mctrl;
- if (fns->set_mctrl)
- atmel_pops.set_mctrl = fns->set_mctrl;
- atmel_open_hook = fns->open;
- atmel_close_hook = fns->close;
- atmel_pops.pm = fns->pm;
- atmel_pops.set_wake = fns->set_wake;
+ return 0;
}
+struct platform_device *atmel_default_console_device; /* the serial console device */
+
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
static void atmel_console_putchar(struct uart_port *port, int ch)
{
@@ -1612,6 +2318,7 @@ static void __init atmel_console_get_options(struct uart_port *port, int *baud,
static int __init atmel_console_setup(struct console *co, char *options)
{
+ int ret;
struct uart_port *port = &atmel_ports[co->index].uart;
int baud = 115200;
int bits = 8;
@@ -1623,7 +2330,9 @@ static int __init atmel_console_setup(struct console *co, char *options)
return -ENODEV;
}
- clk_enable(atmel_ports[co->index].clk);
+ ret = clk_prepare_enable(atmel_ports[co->index].clk);
+ if (ret)
+ return ret;
UART_PUT_IDR(port, -1);
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
@@ -1656,9 +2365,10 @@ static struct console atmel_console = {
*/
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];
@@ -1666,7 +2376,9 @@ static int __init atmel_console_init(void)
port->uart.line = id;
add_preferred_console(ATMEL_DEVICENAME, id, NULL);
- atmel_init_port(port, atmel_default_console_device);
+ ret = atmel_init_port(port, atmel_default_console_device);
+ if (ret)
+ return ret;
register_console(&atmel_console);
}
@@ -1760,11 +2472,31 @@ static int atmel_serial_resume(struct platform_device *pdev)
#define atmel_serial_resume NULL
#endif
-static int __devinit atmel_serial_probe(struct platform_device *pdev)
+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;
@@ -1779,15 +2511,14 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
if (ret < 0)
/* port id not found in platform data nor device-tree aliases:
* auto-enumerate it */
- ret = find_first_zero_bit(&atmel_ports_in_use,
- sizeof(atmel_ports_in_use));
+ ret = find_first_zero_bit(atmel_ports_in_use, ATMEL_MAX_UART);
- if (ret > ATMEL_MAX_UART) {
+ if (ret >= ATMEL_MAX_UART) {
ret = -ENODEV;
goto err;
}
- if (test_and_set_bit(ret, &atmel_ports_in_use)) {
+ if (test_and_set_bit(ret, atmel_ports_in_use)) {
/* port already in use */
ret = -EBUSY;
goto err;
@@ -1797,9 +2528,16 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
port->backup_imr = 0;
port->uart.line = ret;
- atmel_init_port(port, pdev);
+ 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;
- 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);
@@ -1817,9 +2555,9 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
&& ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) {
/*
* The serial core enabled the clock for us, so undo
- * the clk_enable() in atmel_console_setup()
+ * the clk_prepare_enable() in atmel_console_setup()
*/
- clk_disable(port->clk);
+ clk_disable_unprepare(port->clk);
}
#endif
@@ -1831,6 +2569,11 @@ static int __devinit 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:
@@ -1845,23 +2588,23 @@ err:
return ret;
}
-static int __devexit atmel_serial_remove(struct platform_device *pdev)
+static int atmel_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(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 */
- clear_bit(port->line, &atmel_ports_in_use);
+ clear_bit(port->line, atmel_ports_in_use);
clk_put(atmel_port->clk);
@@ -1870,7 +2613,7 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
static struct platform_driver atmel_serial_driver = {
.probe = atmel_serial_probe,
- .remove = __devexit_p(atmel_serial_remove),
+ .remove = atmel_serial_remove,
.suspend = atmel_serial_suspend,
.resume = atmel_serial_resume,
.driver = {
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index c0b68b9cad9..231519022b7 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -29,11 +29,9 @@
#include <linux/sysrq.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
-
-#include <bcm63xx_clk.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
@@ -82,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);
}
/*
@@ -235,14 +233,13 @@ static const char *bcm_uart_type(struct uart_port *port)
*/
static void bcm_uart_do_rx(struct uart_port *port)
{
- struct tty_struct *tty;
+ struct tty_port *tty_port = &port->state->port;
unsigned int max_count;
/* limit number of char read in interrupt, should not be
* higher than fifo size anyway since we're much faster than
* serial port */
max_count = 32;
- tty = port->state->port.tty;
do {
unsigned int iestat, c, cstat;
char flag;
@@ -261,7 +258,7 @@ static void bcm_uart_do_rx(struct uart_port *port)
bcm_uart_writel(port, val, UART_CTL_REG);
port->icount.overrun++;
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tty_port, 0, TTY_OVERRUN);
}
if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY)))
@@ -300,11 +297,13 @@ static void bcm_uart_do_rx(struct uart_port *port)
if ((cstat & port->ignore_status_mask) == 0)
- tty_insert_flip_char(tty, c, flag);
+ tty_insert_flip_char(tty_port, c, flag);
} while (--max_count);
- tty_flip_buffer_push(tty);
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(tty_port);
+ spin_lock(&port->lock);
}
/*
@@ -568,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;
@@ -591,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;
@@ -611,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);
}
@@ -801,13 +800,16 @@ static struct uart_driver bcm_uart_driver = {
/*
* platform driver probe/remove callback
*/
-static int __devinit bcm_uart_probe(struct platform_device *pdev)
+static int bcm_uart_probe(struct platform_device *pdev)
{
struct resource *res_mem, *res_irq;
struct uart_port *port;
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;
@@ -848,27 +850,33 @@ static int __devinit bcm_uart_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit bcm_uart_remove(struct platform_device *pdev)
+static int bcm_uart_remove(struct platform_device *pdev)
{
struct uart_port *port;
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
*/
static struct platform_driver bcm_uart_platform_driver = {
.probe = bcm_uart_probe,
- .remove = __devexit_p(bcm_uart_remove),
+ .remove = bcm_uart_remove,
.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 7fbc3a08f10..4f229703328 100644
--- a/drivers/tty/serial/bfin_sport_uart.c
+++ b/drivers/tty/serial/bfin_sport_uart.c
@@ -149,7 +149,7 @@ static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate)
static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id)
{
struct sport_uart_port *up = dev_id;
- struct tty_struct *tty = up->port.state->port.tty;
+ struct tty_port *port = &up->port.state->port;
unsigned int ch;
spin_lock(&up->port.lock);
@@ -159,12 +159,14 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id)
up->port.icount.rx++;
if (!uart_handle_sysrq_char(&up->port, ch))
- tty_insert_flip_char(tty, ch, TTY_NORMAL);
+ tty_insert_flip_char(port, ch, TTY_NORMAL);
}
- tty_flip_buffer_push(tty);
spin_unlock(&up->port.lock);
+ /* XXX this won't deadlock with lowlat? */
+ tty_flip_buffer_push(port);
+
return IRQ_HANDLED;
}
@@ -182,7 +184,6 @@ static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id)
static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
{
struct sport_uart_port *up = dev_id;
- struct tty_struct *tty = up->port.state->port.tty;
unsigned int stat = SPORT_GET_STAT(up);
spin_lock(&up->port.lock);
@@ -190,7 +191,7 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
/* Overflow in RX FIFO */
if (stat & ROVF) {
up->port.icount.overrun++;
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(&up->port.state->port, 0, TTY_OVERRUN);
SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */
}
/* These should not happen */
@@ -205,6 +206,8 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
SSYNC();
spin_unlock(&up->port.lock);
+ /* XXX we don't push the overrun bit to TTY? */
+
return IRQ_HANDLED;
}
@@ -740,7 +743,7 @@ static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = {
};
#endif
-static int __devinit sport_uart_probe(struct platform_device *pdev)
+static int sport_uart_probe(struct platform_device *pdev)
{
struct resource *res;
struct sport_uart_port *sport;
@@ -763,8 +766,8 @@ static int __devinit 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");
@@ -840,8 +843,7 @@ static int __devinit 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;
@@ -850,7 +852,7 @@ out_error_free_mem:
return ret;
}
-static int __devexit sport_uart_remove(struct platform_device *pdev)
+static int sport_uart_remove(struct platform_device *pdev)
{
struct sport_uart_port *sport = platform_get_drvdata(pdev);
@@ -860,8 +862,7 @@ static int __devexit 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;
}
@@ -871,7 +872,7 @@ static int __devexit sport_uart_remove(struct platform_device *pdev)
static struct platform_driver sport_uart_driver = {
.probe = sport_uart_probe,
- .remove = __devexit_p(sport_uart_remove),
+ .remove = sport_uart_remove,
.driver = {
.name = DRV_NAME,
#ifdef CONFIG_PM
@@ -881,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 26953bfa692..ac86a20992e 100644
--- a/drivers/tty/serial/bfin_uart.c
+++ b/drivers/tty/serial/bfin_uart.c
@@ -1,7 +1,7 @@
/*
* Blackfin On-Chip Serial Driver
*
- * Copyright 2006-2010 Analog Devices Inc.
+ * Copyright 2006-2011 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
@@ -35,20 +35,12 @@
#include <asm/portmux.h>
#include <asm/cacheflush.h>
#include <asm/dma.h>
-
-#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase)
-#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr)
-#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v))
#include <asm/bfin_serial.h>
#ifdef CONFIG_SERIAL_BFIN_MODULE
# 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
@@ -166,7 +158,7 @@ static void bfin_serial_stop_tx(struct uart_port *port)
uart->tx_count = 0;
uart->tx_done = 1;
#else
-#ifdef CONFIG_BF54x
+#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
/* Clear TFI bit */
UART_PUT_LSR(uart, TFI);
#endif
@@ -186,7 +178,7 @@ static void bfin_serial_start_tx(struct uart_port *port)
* To avoid losting RX interrupt, we reset IR function
* before sending data.
*/
- if (tty->termios->c_line == N_IRDA)
+ if (tty->termios.c_line == N_IRDA)
bfin_serial_reset_irda(port);
#ifdef CONFIG_SERIAL_BFIN_DMA
@@ -227,7 +219,6 @@ static void bfin_serial_enable_ms(struct uart_port *port)
#ifdef CONFIG_SERIAL_BFIN_PIO
static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
{
- struct tty_struct *tty = NULL;
unsigned int status, ch, flg;
static struct timeval anomaly_start = { .tv_sec = 0 };
@@ -246,11 +237,9 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
return;
}
- if (!uart->port.state || !uart->port.state->port.tty)
+ if (!uart->port.state)
return;
#endif
- tty = uart->port.state->port.tty;
-
if (ANOMALY_05000363) {
/* The BF533 (and BF561) family of processors have a nice anomaly
* where they continuously generate characters for a "single" break.
@@ -329,7 +318,7 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
uart_insert_char(&uart->port, status, OE, ch, flg);
ignore_char:
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&uart->port.state->port);
}
static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
@@ -337,7 +326,7 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
struct circ_buf *xmit = &uart->port.state->xmit;
if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
-#ifdef CONFIG_BF54x
+#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
/* Clear TFI bit */
UART_PUT_LSR(uart, TFI);
#endif
@@ -430,7 +419,6 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
{
- struct tty_struct *tty = uart->port.state->port.tty;
int i, flg, status;
status = UART_GET_LSR(uart);
@@ -475,15 +463,15 @@ static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
}
dma_ignore_char:
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&uart->port.state->port);
}
void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
{
int x_pos, pos;
+ unsigned long flags;
- dma_disable_irq_nosync(uart->rx_dma_channel);
- spin_lock_bh(&uart->rx_lock);
+ spin_lock_irqsave(&uart->rx_lock, flags);
/* 2D DMA RX buffer ring is used. Because curr_y_count and
* curr_x_count can't be read as an atomic operation,
@@ -514,8 +502,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
}
- spin_unlock_bh(&uart->rx_lock);
- dma_enable_irq(uart->rx_dma_channel);
+ spin_unlock_irqrestore(&uart->rx_lock, flags);
mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
}
@@ -535,11 +522,13 @@ static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
* when start a new tx.
*/
UART_CLEAR_IER(uart, ETBEI);
- xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
uart->port.icount.tx += uart->tx_count;
+ if (!(xmit->tail == 0 && xmit->head == 0)) {
+ xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&uart->port);
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&uart->port);
+ }
bfin_serial_dma_tx_chars(uart);
}
@@ -551,7 +540,7 @@ static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;
- unsigned short irqstat;
+ unsigned int irqstat;
int x_pos, pos;
spin_lock(&uart->rx_lock);
@@ -584,7 +573,7 @@ static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
static unsigned int bfin_serial_tx_empty(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned short lsr;
+ unsigned int lsr;
lsr = UART_GET_LSR(uart);
if (lsr & TEMT)
@@ -596,7 +585,7 @@ static unsigned int bfin_serial_tx_empty(struct uart_port *port)
static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- u16 lcr = UART_GET_LCR(uart);
+ u32 lcr = UART_GET_LCR(uart);
if (break_state)
lcr |= SB;
else
@@ -691,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) {
@@ -737,13 +726,13 @@ 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");
}
/* CTS RTS PINs are negative assertive. */
- UART_PUT_MCR(uart, ACTS);
+ UART_PUT_MCR(uart, UART_GET_MCR(uart) | ACTS);
UART_SET_IER(uart, EDSSI);
}
#endif
@@ -776,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);
@@ -801,7 +790,8 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
unsigned long flags;
unsigned int baud, quot;
- unsigned short val, ier, lcr = 0;
+ unsigned int ier, lcr = 0;
+ unsigned long timeout;
switch (termios->c_cflag & CSIZE) {
case CS8:
@@ -817,7 +807,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
lcr = WLS(5);
break;
default:
- printk(KERN_ERR "%s: word lengh not supported\n",
+ printk(KERN_ERR "%s: word length not supported\n",
__func__);
}
@@ -843,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;
/*
@@ -871,28 +861,33 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15);
+ /* Wait till the transfer buffer is empty */
+ timeout = jiffies + msecs_to_jiffies(10);
+ while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT))
+ if (time_after(jiffies, timeout)) {
+ dev_warn(port->dev, "timeout waiting for TX buffer empty\n");
+ break;
+ }
+
/* Disable UART */
ier = UART_GET_IER(uart);
+ UART_PUT_GCTL(uart, UART_GET_GCTL(uart) & ~UCEN);
UART_DISABLE_INTS(uart);
- /* Set DLAB in LCR to Access DLL and DLH */
+ /* Set DLAB in LCR to Access CLK */
UART_SET_DLAB(uart);
- UART_PUT_DLL(uart, quot & 0xFF);
- UART_PUT_DLH(uart, (quot >> 8) & 0xFF);
+ UART_PUT_CLK(uart, quot);
SSYNC();
/* Clear DLAB in LCR to Access THR RBR IER */
UART_CLEAR_DLAB(uart);
- UART_PUT_LCR(uart, lcr);
+ UART_PUT_LCR(uart, (UART_GET_LCR(uart) & ~LCR_MASK) | lcr);
/* Enable UART */
UART_ENABLE_INTS(uart, ier);
-
- val = UART_GET_GCTL(uart);
- val |= UCEN;
- UART_PUT_GCTL(uart, val);
+ UART_PUT_GCTL(uart, UART_GET_GCTL(uart) | UCEN);
/* Port speed changed, update the per-port timeout. */
uart_update_timeout(port, termios->c_cflag, baud);
@@ -952,17 +947,17 @@ bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
static void bfin_serial_set_ldisc(struct uart_port *port, int ld)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned short val;
+ unsigned int val;
switch (ld) {
case N_IRDA:
val = UART_GET_GCTL(uart);
- val |= (IREN | RPOLC);
+ val |= (UMOD_IRDA | RPOLC);
UART_PUT_GCTL(uart, val);
break;
default:
val = UART_GET_GCTL(uart);
- val &= ~(IREN | RPOLC);
+ val &= ~(UMOD_MASK | RPOLC);
UART_PUT_GCTL(uart, val);
}
}
@@ -970,13 +965,13 @@ static void bfin_serial_set_ldisc(struct uart_port *port, int ld)
static void bfin_serial_reset_irda(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned short val;
+ unsigned int val;
val = UART_GET_GCTL(uart);
- val &= ~(IREN | RPOLC);
+ val &= ~(UMOD_MASK | RPOLC);
UART_PUT_GCTL(uart, val);
SSYNC();
- val |= (IREN | RPOLC);
+ val |= (UMOD_IRDA | RPOLC);
UART_PUT_GCTL(uart, val);
SSYNC();
}
@@ -1012,24 +1007,6 @@ static int bfin_serial_poll_get_char(struct uart_port *port)
}
#endif
-#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
- defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
-static void bfin_kgdboc_port_shutdown(struct uart_port *port)
-{
- if (kgdboc_break_enabled) {
- kgdboc_break_enabled = 0;
- bfin_serial_shutdown(port);
- }
-}
-
-static int bfin_kgdboc_port_startup(struct uart_port *port)
-{
- kgdboc_port_line = port->line;
- kgdboc_break_enabled = !bfin_serial_startup(port);
- return 0;
-}
-#endif
-
static struct uart_ops bfin_serial_pops = {
.tx_empty = bfin_serial_tx_empty,
.set_mctrl = bfin_serial_set_mctrl,
@@ -1048,11 +1025,6 @@ static struct uart_ops bfin_serial_pops = {
.request_port = bfin_serial_request_port,
.config_port = bfin_serial_config_port,
.verify_port = bfin_serial_verify_port,
-#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
- defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
- .kgdboc_port_startup = bfin_kgdboc_port_startup,
- .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown,
-#endif
#ifdef CONFIG_CONSOLE_POLL
.poll_put_char = bfin_serial_poll_put_char,
.poll_get_char = bfin_serial_poll_get_char,
@@ -1068,12 +1040,12 @@ static void __init
bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
int *parity, int *bits)
{
- unsigned short status;
+ unsigned int status;
status = UART_GET_IER(uart) & (ERBFI | ETBEI);
if (status == (ERBFI | ETBEI)) {
/* ok, the port was enabled */
- u16 lcr, dlh, dll;
+ u32 lcr, clk;
lcr = UART_GET_LCR(uart);
@@ -1084,30 +1056,17 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
else
*parity = 'o';
}
- switch (lcr & 0x03) {
- case 0:
- *bits = 5;
- break;
- case 1:
- *bits = 6;
- break;
- case 2:
- *bits = 7;
- break;
- case 3:
- *bits = 8;
- break;
- }
- /* Set DLAB in LCR to Access DLL and DLH */
+ *bits = ((lcr & WLS_MASK) >> WLS_OFFSET) + 5;
+
+ /* Set DLAB in LCR to Access CLK */
UART_SET_DLAB(uart);
- dll = UART_GET_DLL(uart);
- dlh = UART_GET_DLH(uart);
+ clk = UART_GET_CLK(uart);
/* Clear DLAB in LCR to Access THR RBR IER */
UART_CLEAR_DLAB(uart);
- *baud = get_sclk() / (16*(dll | dlh << 8));
+ *baud = get_sclk() / (16*clk);
}
pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits);
}
@@ -1217,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,
@@ -1281,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");
@@ -1398,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;
@@ -1408,7 +1367,7 @@ out_error_free_mem:
return ret;
}
-static int __devexit bfin_serial_remove(struct platform_device *pdev)
+static int bfin_serial_remove(struct platform_device *pdev)
{
struct bfin_serial_port *uart = platform_get_drvdata(pdev);
@@ -1417,8 +1376,7 @@ static int __devexit 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;
}
@@ -1428,7 +1386,7 @@ static int __devexit bfin_serial_remove(struct platform_device *pdev)
static struct platform_driver bfin_serial_driver = {
.probe = bfin_serial_probe,
- .remove = __devexit_p(bfin_serial_remove),
+ .remove = bfin_serial_remove,
.suspend = bfin_serial_suspend,
.resume = bfin_serial_resume,
.driver = {
@@ -1438,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,
@@ -1472,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");
@@ -1503,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;
}
@@ -1517,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 e6c3dbd781d..14aaea0d413 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -10,15 +10,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
@@ -26,276 +17,268 @@
#endif
#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/spinlock.h>
#include <linux/device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
+#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/serial.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 <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/clps711x.h>
+
+#define UART_CLPS711X_DEVNAME "ttyCL"
+#define UART_CLPS711X_NR 2
+#define UART_CLPS711X_MAJOR 204
+#define UART_CLPS711X_MINOR 40
+
+#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_port port;
+ unsigned int tx_enabled;
+ int rx_irq;
+ struct regmap *syscon;
+ bool use_ms;
+};
-#include <mach/hardware.h>
-#include <asm/irq.h>
-#include <asm/hardware/clps7111.h>
-
-#define UART_NR 2
-
-#define SERIAL_CLPS711X_MAJOR 204
-#define SERIAL_CLPS711X_MINOR 40
-#define SERIAL_CLPS711X_NR UART_NR
-
-/*
- * We use the relevant SYSCON register as a base address for these ports.
- */
-#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1)
-#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1)
-#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1)
-#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1)
-
-#define TX_IRQ(port) ((port)->irq)
-#define RX_IRQ(port) ((port)->irq + 1)
-
-#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)
-
-#define tx_enabled(port) ((port)->unused[0])
+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 clps711xuart_stop_tx(struct uart_port *port)
+static void uart_clps711x_stop_tx(struct uart_port *port)
{
- if (tx_enabled(port)) {
- disable_irq(TX_IRQ(port));
- tx_enabled(port) = 0;
- }
-}
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
-static void clps711xuart_start_tx(struct uart_port *port)
-{
- if (!tx_enabled(port)) {
- enable_irq(TX_IRQ(port));
- tx_enabled(port) = 1;
+ if (s->tx_enabled) {
+ disable_irq(port->irq);
+ s->tx_enabled = 0;
}
}
-static void clps711xuart_stop_rx(struct uart_port *port)
+static void uart_clps711x_start_tx(struct uart_port *port)
{
- disable_irq(RX_IRQ(port));
-}
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
-static void clps711xuart_enable_ms(struct uart_port *port)
-{
+ if (!s->tx_enabled) {
+ s->tx_enabled = 1;
+ enable_irq(port->irq);
+ }
}
-static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id)
+static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
- struct tty_struct *tty = port->state->port.tty;
- unsigned int status, ch, flg;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ unsigned int status, flg;
+ u16 ch;
- status = clps_readl(SYSFLG(port));
- while (!(status & SYSFLG_URXFE)) {
- ch = clps_readl(UARTDR(port));
+ for (;;) {
+ u32 sysflg = 0;
- port->icount.rx++;
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ if (sysflg & SYSFLG_URXFE)
+ break;
+
+ ch = readw(port->membase + UARTDR_OFFSET);
+ status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR);
+ ch &= 0xff;
+ port->icount.rx++;
flg = TTY_NORMAL;
- /*
- * Note that the error handling code is
- * out of the main execution path
- */
- if (unlikely(ch & UART_ANY_ERR)) {
- if (ch & UARTDR_PARERR)
+ if (unlikely(status)) {
+ if (status & UARTDR_PARERR)
port->icount.parity++;
- else if (ch & UARTDR_FRMERR)
+ else if (status & UARTDR_FRMERR)
port->icount.frame++;
- if (ch & UARTDR_OVERR)
+ else if (status & UARTDR_OVERR)
port->icount.overrun++;
- ch &= port->read_status_mask;
+ status &= port->read_status_mask;
- if (ch & UARTDR_PARERR)
+ if (status & UARTDR_PARERR)
flg = TTY_PARITY;
- else if (ch & UARTDR_FRMERR)
+ else if (status & UARTDR_FRMERR)
flg = TTY_FRAME;
-
-#ifdef SUPPORT_SYSRQ
- port->sysrq = 0;
-#endif
+ else if (status & UARTDR_OVERR)
+ flg = TTY_OVERRUN;
}
if (uart_handle_sysrq_char(port, ch))
- goto ignore_char;
+ continue;
- /*
- * CHECK: does overrun affect the current character?
- * ASSUMPTION: it does not.
- */
- uart_insert_char(port, ch, UARTDR_OVERR, ch, flg);
+ if (status & port->ignore_status_mask)
+ continue;
- ignore_char:
- status = clps_readl(SYSFLG(port));
+ uart_insert_char(port, status, UARTDR_OVERR, ch, flg);
}
- tty_flip_buffer_push(tty);
+
+ tty_flip_buffer_push(&port->state->port);
+
return IRQ_HANDLED;
}
-static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id)
+static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
struct circ_buf *xmit = &port->state->xmit;
- int count;
if (port->x_char) {
- clps_writel(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)) {
- clps711xuart_stop_tx(port);
+ if (s->tx_enabled) {
+ disable_irq_nosync(port->irq);
+ s->tx_enabled = 0;
+ }
return IRQ_HANDLED;
}
- count = port->fifosize >> 1;
- do {
- clps_writel(xmit->buf[xmit->tail], UARTDR(port));
+ while (!uart_circ_empty(xmit)) {
+ 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 (uart_circ_empty(xmit))
+
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ if (sysflg & SYSFLG_UTXFF)
break;
- } while (--count > 0);
+ }
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- if (uart_circ_empty(xmit))
- clps711xuart_stop_tx(port);
-
return IRQ_HANDLED;
}
-static unsigned int clps711xuart_tx_empty(struct uart_port *port)
+static unsigned int uart_clps711x_tx_empty(struct uart_port *port)
{
- unsigned int status = clps_readl(SYSFLG(port));
- return status & 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 clps711xuart_get_mctrl(struct uart_port *port)
+static unsigned int uart_clps711x_get_mctrl(struct uart_port *port)
{
- unsigned int port_addr;
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
unsigned int result = 0;
- unsigned int status;
- port_addr = SYSFLG(port);
- if (port_addr == SYSFLG1) {
- status = clps_readl(SYSFLG1);
- if (status & SYSFLG1_DCD)
+ if (s->use_ms) {
+ u32 sysflg = 0;
+
+ 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;
return result;
}
-static void
-clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
+static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
+ /* Do nothing */
}
-static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
+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));
- if (break_state == -1)
+ ubrlcr = readl(port->membase + UBRLCR_OFFSET);
+ if (break_state)
ubrlcr |= UBRLCR_BREAK;
else
ubrlcr &= ~UBRLCR_BREAK;
- clps_writel(ubrlcr, UBRLCR(port));
- spin_unlock_irqrestore(&port->lock, flags);
+ writel(ubrlcr, port->membase + UBRLCR_OFFSET);
}
-static int clps711xuart_startup(struct uart_port *port)
+static void uart_clps711x_set_ldisc(struct uart_port *port, int ld)
{
- unsigned int syscon;
- int retval;
-
- tx_enabled(port) = 1;
-
- /*
- * Allocate the IRQs
- */
- retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
- "clps711xuart_tx", port);
- if (retval)
- return retval;
-
- retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
- "clps711xuart_rx", port);
- if (retval) {
- free_irq(TX_IRQ(port), port);
- return retval;
+ if (!port->line) {
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+
+ regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON1_SIREN,
+ (ld == N_IRDA) ? SYSCON1_SIREN : 0);
}
+}
- /*
- * enable the port
- */
- syscon = clps_readl(SYSCON(port));
- syscon |= SYSCON_UARTEN;
- clps_writel(syscon, SYSCON(port));
+static int uart_clps711x_startup(struct uart_port *port)
+{
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
- return 0;
+ /* Disable break */
+ writel(readl(port->membase + UBRLCR_OFFSET) & ~UBRLCR_BREAK,
+ port->membase + UBRLCR_OFFSET);
+
+ /* Enable the port */
+ return regmap_update_bits(s->syscon, SYSCON_OFFSET,
+ SYSCON_UARTEN, SYSCON_UARTEN);
}
-static void clps711xuart_shutdown(struct uart_port *port)
+static void uart_clps711x_shutdown(struct uart_port *port)
{
- unsigned int ubrlcr, syscon;
-
- /*
- * Free the interrupt
- */
- free_irq(TX_IRQ(port), port); /* TX interrupt */
- free_irq(RX_IRQ(port), port); /* RX interrupt */
-
- /*
- * disable the port
- */
- syscon = clps_readl(SYSCON(port));
- syscon &= ~SYSCON_UARTEN;
- clps_writel(syscon, SYSCON(port));
-
- /*
- * disable break condition and fifos
- */
- ubrlcr = clps_readl(UBRLCR(port));
- ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
- clps_writel(ubrlcr, UBRLCR(port));
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+
+ /* Disable the port */
+ regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
}
-static void
-clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+static void uart_clps711x_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
{
- unsigned int ubrlcr, baud, quot;
- unsigned long flags;
-
- /*
- * We don't implement CREAD.
- */
- termios->c_cflag |= CREAD;
-
- /*
- * Ask the core to calculate the divisor for us.
- */
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ u32 ubrlcr;
+ unsigned int baud, quot;
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+ termios->c_iflag &= ~(BRKINT | IGNBRK);
+
+ /* Ask the core to calculate the divisor for us */
+ baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096,
+ port->uartclk / 16);
quot = uart_get_divisor(port, baud);
switch (termios->c_cflag & CSIZE) {
@@ -308,270 +291,301 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
case CS7:
ubrlcr = UBRLCR_WRDLEN7;
break;
- default: // CS8
+ case CS8:
+ default:
ubrlcr = UBRLCR_WRDLEN8;
break;
}
+
if (termios->c_cflag & CSTOPB)
ubrlcr |= UBRLCR_XSTOP;
+
if (termios->c_cflag & PARENB) {
ubrlcr |= UBRLCR_PRTEN;
if (!(termios->c_cflag & PARODD))
ubrlcr |= UBRLCR_EVENPRT;
}
- if (port->fifosize > 1)
- ubrlcr |= UBRLCR_FIFOEN;
- spin_lock_irqsave(&port->lock, flags);
-
- /*
- * Update the per-port timeout.
- */
- uart_update_timeout(port, termios->c_cflag, baud);
+ /* Enable FIFO */
+ ubrlcr |= UBRLCR_FIFOEN;
+ /* Set read status mask */
port->read_status_mask = UARTDR_OVERR;
if (termios->c_iflag & INPCK)
port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
- /*
- * Characters to ignore
- */
+ /* Set status ignore mask */
port->ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
- if (termios->c_iflag & IGNBRK) {
- /*
- * If we're ignoring parity and break indicators,
- * ignore overruns to (for real raw support).
- */
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= UARTDR_OVERR;
- }
-
- quot -= 1;
+ if (!(termios->c_cflag & CREAD))
+ port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR |
+ UARTDR_FRMERR;
- clps_writel(ubrlcr | quot, UBRLCR(port));
+ uart_update_timeout(port, termios->c_cflag, baud);
- spin_unlock_irqrestore(&port->lock, flags);
+ writel(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET);
}
-static const char *clps711xuart_type(struct uart_port *port)
+static const char *uart_clps711x_type(struct uart_port *port)
{
- return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
+ return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL;
}
-/*
- * Configure/autoconfigure the port.
- */
-static void clps711xuart_config_port(struct uart_port *port, int flags)
+static void uart_clps711x_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_CLPS711X;
}
-static void clps711xuart_release_port(struct uart_port *port)
+static void uart_clps711x_nop_void(struct uart_port *port)
{
}
-static int clps711xuart_request_port(struct uart_port *port)
+static int uart_clps711x_nop_int(struct uart_port *port)
{
return 0;
}
-static struct uart_ops clps711x_pops = {
- .tx_empty = clps711xuart_tx_empty,
- .set_mctrl = clps711xuart_set_mctrl_null,
- .get_mctrl = clps711xuart_get_mctrl,
- .stop_tx = clps711xuart_stop_tx,
- .start_tx = clps711xuart_start_tx,
- .stop_rx = clps711xuart_stop_rx,
- .enable_ms = clps711xuart_enable_ms,
- .break_ctl = clps711xuart_break_ctl,
- .startup = clps711xuart_startup,
- .shutdown = clps711xuart_shutdown,
- .set_termios = clps711xuart_set_termios,
- .type = clps711xuart_type,
- .config_port = clps711xuart_config_port,
- .release_port = clps711xuart_release_port,
- .request_port = clps711xuart_request_port,
-};
-
-static struct uart_port clps711x_ports[UART_NR] = {
- {
- .iobase = SYSCON1,
- .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
- .uartclk = 3686400,
- .fifosize = 16,
- .ops = &clps711x_pops,
- .line = 0,
- .flags = UPF_BOOT_AUTOCONF,
- },
- {
- .iobase = SYSCON2,
- .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */
- .uartclk = 3686400,
- .fifosize = 16,
- .ops = &clps711x_pops,
- .line = 1,
- .flags = UPF_BOOT_AUTOCONF,
- }
+static const struct uart_ops uart_clps711x_ops = {
+ .tx_empty = uart_clps711x_tx_empty,
+ .set_mctrl = uart_clps711x_set_mctrl,
+ .get_mctrl = uart_clps711x_get_mctrl,
+ .stop_tx = uart_clps711x_stop_tx,
+ .start_tx = uart_clps711x_start_tx,
+ .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_nop_void,
+ .request_port = uart_clps711x_nop_int,
};
#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
-static void clps711xuart_console_putchar(struct uart_port *port, int ch)
+static void uart_clps711x_console_putchar(struct uart_port *port, int ch)
{
- while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF)
- barrier();
- clps_writel(ch, UARTDR(port));
+ struct clps711x_port *s = dev_get_drvdata(port->dev);
+ u32 sysflg = 0;
+
+ /* Wait for FIFO is not full */
+ do {
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ } while (sysflg & SYSFLG_UTXFF);
+
+ writew(ch, port->membase + UARTDR_OFFSET);
}
-/*
- * Print a string to the serial port trying not to disturb
- * any possible real use of the port...
- *
- * The console_lock must be held when we get here.
- *
- * Note that this is called with interrupts already disabled
- */
-static void
-clps711xuart_console_write(struct console *co, const char *s,
- unsigned int count)
+static void uart_clps711x_console_write(struct console *co, const char *c,
+ unsigned n)
{
- struct uart_port *port = clps711x_ports + co->index;
- unsigned int status, 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, s, count, clps711xuart_console_putchar);
+ uart_console_write(port, c, n, uart_clps711x_console_putchar);
- /*
- * Finally, wait for transmitter to become empty
- * and restore the uart state.
- */
+ /* Wait for transmitter to become empty */
do {
- status = clps_readl(SYSFLG(port));
- } while (status & SYSFLG_UBUSY);
-
- clps_writel(syscon, SYSCON(port));
+ regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
+ } while (sysflg & SYSFLG_UBUSY);
}
-static void __init
-clps711xuart_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 __init clps711xuart_console_setup(struct console *co, char *options)
-{
- struct uart_port *port;
- int baud = 38400;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- /*
- * Check whether an invalid uart number has been specified, and
- * if so, search for the first available port that does have
- * console support.
- */
- port = uart_get_console(clps711x_ports, UART_NR, co);
-
- if (options)
+ regmap_read(s->syscon, SYSCON_OFFSET, &syscon);
+ if (syscon & SYSCON_UARTEN) {
+ ubrlcr = readl(port->membase + UBRLCR_OFFSET);
+
+ 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
- clps711xuart_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 uart_driver clps711x_reg;
static struct console clps711x_console = {
- .name = "ttyCL",
- .write = clps711xuart_console_write,
- .device = uart_console_device,
- .setup = clps711xuart_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &clps711x_reg,
+ .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 __init clps711xuart_console_init(void)
+static int uart_clps711x_probe(struct platform_device *pdev)
{
- register_console(&clps711x_console);
- return 0;
-}
-console_initcall(clps711xuart_console_init);
+ struct device_node *np = pdev->dev.of_node;
+ int ret, index = np ? of_alias_get_id(np, "serial") : pdev->id;
+ struct clps711x_port *s;
+ struct resource *res;
+ struct clk *uart_clk;
+
+ 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");
+ }
-#define CLPS711X_CONSOLE &clps711x_console
-#else
-#define CLPS711X_CONSOLE NULL
-#endif
+ 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;
-static struct uart_driver clps711x_reg = {
- .driver_name = "ttyCL",
- .dev_name = "ttyCL",
- .major = SERIAL_CLPS711X_MAJOR,
- .minor = SERIAL_CLPS711X_MINOR,
- .nr = UART_NR,
+ platform_set_drvdata(pdev, s);
- .cons = CLPS711X_CONSOLE,
-};
+ ret = uart_add_one_port(&clps711x_uart, &s->port);
+ if (ret)
+ return ret;
-static int __init clps711xuart_init(void)
-{
- int ret, i;
+ /* Disable port */
+ if (!uart_console(&s->port))
+ regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
- printk(KERN_INFO "Serial: CLPS711x driver\n");
+ s->tx_enabled = 1;
- ret = uart_register_driver(&clps711x_reg);
- if (ret)
+ 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;
+ }
- for (i = 0; i < UART_NR; i++)
- uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
+ 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 0;
+ return ret;
}
-static void __exit clps711xuart_exit(void)
+static int uart_clps711x_remove(struct platform_device *pdev)
{
- int i;
+ struct clps711x_port *s = platform_get_drvdata(pdev);
- for (i = 0; i < UART_NR; i++)
- uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
+ return uart_remove_one_port(&clps711x_uart, &s->port);
+}
- uart_unregister_driver(&clps711x_reg);
+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 = "clps711x-uart",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(clps711x_uart_dt_ids),
+ },
+ .probe = uart_clps711x_probe,
+ .remove = uart_clps711x_remove,
+};
+
+static int __init uart_clps711x_init(void)
+{
+ 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);
-module_init(clps711xuart_init);
-module_exit(clps711xuart_exit);
+static void __exit uart_clps711x_exit(void)
+{
+ platform_driver_unregister(&clps711x_uart_platform);
+ uart_unregister_driver(&clps711x_uart);
+}
+module_exit(uart_clps711x_exit);
MODULE_AUTHOR("Deep Blue Solutions Ltd");
-MODULE_DESCRIPTION("CLPS-711x generic serial driver");
+MODULE_DESCRIPTION("CLPS711X serial driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index b418947b710..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>
@@ -71,7 +73,7 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
/**************************************************************/
-#define HW_BUF_SPD_THRESHOLD 9600
+#define HW_BUF_SPD_THRESHOLD 2400
/*
* Check, if transmit buffers are processed
@@ -245,7 +247,7 @@ static void cpm_uart_int_rx(struct uart_port *port)
int i;
unsigned char ch;
u8 *cp;
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
cbd_t __iomem *bdp;
u16 status;
@@ -276,7 +278,7 @@ static void cpm_uart_int_rx(struct uart_port *port)
/* If we have not enough room in tty flip buffer, then we try
* later, which will be the next rx-interrupt or a timeout
*/
- if(tty_buffer_request_room(tty, i) < i) {
+ if (tty_buffer_request_room(tport, i) < i) {
printk(KERN_WARNING "No room in flip buffer\n");
return;
}
@@ -302,7 +304,7 @@ static void cpm_uart_int_rx(struct uart_port *port)
}
#endif
error_return:
- tty_insert_flip_char(tty, ch, flg);
+ tty_insert_flip_char(tport, ch, flg);
} /* End while (i--) */
@@ -322,7 +324,7 @@ static void cpm_uart_int_rx(struct uart_port *port)
pinfo->rx_cur = bdp;
/* activate BH processing */
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
return;
@@ -417,6 +419,7 @@ static int cpm_uart_startup(struct uart_port *port)
clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR);
clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
}
+ cpm_uart_initbd(pinfo);
cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
}
/* Install interrupt handler. */
@@ -500,16 +503,28 @@ static void cpm_uart_set_termios(struct uart_port *port,
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
smc_t __iomem *smcp = pinfo->smcp;
scc_t __iomem *sccp = pinfo->sccp;
+ int maxidl;
pr_debug("CPM uart[%d]:set_termios\n", port->line);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
- if (baud <= HW_BUF_SPD_THRESHOLD ||
- (pinfo->port.state && pinfo->port.state->port.tty->low_latency))
+ if (baud < HW_BUF_SPD_THRESHOLD ||
+ (pinfo->port.state && pinfo->port.state->port.low_latency))
pinfo->rx_fifosize = 1;
else
pinfo->rx_fifosize = RX_BUF_SIZE;
+ /* MAXIDL is the timeout after which a receive buffer is closed
+ * when not full if no more characters are received.
+ * We calculate it from the baudrate so that the duration is
+ * always the same at standard rates: about 4ms.
+ */
+ maxidl = baud / 2400;
+ if (maxidl < 1)
+ maxidl = 1;
+ if (maxidl > 0x10)
+ maxidl = 0x10;
+
/* Character length programmed into the mode register is the
* sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
* 1 or 2 stop bits, minus 1.
@@ -610,6 +625,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
* SMC/SCC receiver is disabled.
*/
out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
+ out_be16(&pinfo->smcup->smc_maxidl, maxidl);
/* Set the mode register. We want to keep a copy of the
* enables, because we want to put them back if they were
@@ -622,6 +638,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
SMCMR_SM_UART | prev_mode);
} else {
out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
+ out_be16(&pinfo->sccup->scc_maxidl, maxidl);
out_be16(&sccp->scc_psmr, (sbits << 12) | scval);
}
@@ -798,7 +815,7 @@ static void cpm_uart_init_scc(struct uart_cpm_port *pinfo)
cpm_set_scc_fcr(sup);
out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
- out_be16(&sup->scc_maxidl, pinfo->rx_fifosize);
+ out_be16(&sup->scc_maxidl, 0x10);
out_be16(&sup->scc_brkcr, 1);
out_be16(&sup->scc_parec, 0);
out_be16(&sup->scc_frmec, 0);
@@ -872,7 +889,7 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
/* Using idle character time requires some additional tuning. */
out_be16(&up->smc_mrblr, pinfo->rx_fifosize);
- out_be16(&up->smc_maxidl, pinfo->rx_fifosize);
+ out_be16(&up->smc_maxidl, 0x10);
out_be16(&up->smc_brklen, 0);
out_be16(&up->smc_brkec, 0);
out_be16(&up->smc_brkcr, 1);
@@ -954,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;
@@ -996,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)
;
@@ -1094,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 */
@@ -1192,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;
@@ -1234,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);
@@ -1358,7 +1399,7 @@ static struct uart_driver cpm_reg = {
static int probe_index;
-static int __devinit cpm_uart_probe(struct platform_device *ofdev)
+static int cpm_uart_probe(struct platform_device *ofdev)
{
int index = probe_index++;
struct uart_cpm_port *pinfo = &cpm_uart_ports[index];
@@ -1369,7 +1410,7 @@ static int __devinit cpm_uart_probe(struct platform_device *ofdev)
if (index >= UART_NR)
return -ENODEV;
- dev_set_drvdata(&ofdev->dev, pinfo);
+ platform_set_drvdata(ofdev, pinfo);
/* initialize the device pointer for the port */
pinfo->port.dev = &ofdev->dev;
@@ -1381,9 +1422,9 @@ static int __devinit cpm_uart_probe(struct platform_device *ofdev)
return uart_add_one_port(&cpm_reg, &pinfo->port);
}
-static int __devexit cpm_uart_remove(struct platform_device *ofdev)
+static int cpm_uart_remove(struct platform_device *ofdev)
{
- struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev);
+ struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev);
return uart_remove_one_port(&cpm_reg, &pinfo->port);
}
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 1dfba7b779c..d567ac5d3af 100644
--- a/drivers/tty/serial/crisv10.c
+++ b/drivers/tty/serial/crisv10.c
@@ -34,9 +34,9 @@ static char *serial_version = "$Revision: 1.25 $";
#include <asm/irq.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <arch/svinto.h>
+#include <arch/system.h>
/* non-arch dependent serial structures are in linux/serial.h */
#include <linux/serial.h>
@@ -169,7 +169,6 @@ static int get_lsr_info(struct e100_serial *info, unsigned int *value);
#define DEF_BAUD 115200 /* 115.2 kbit/s */
-#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
#define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */
/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
#define DEF_TX 0x80 /* or SERIAL_CTRL_B */
@@ -246,7 +245,6 @@ static struct e100_serial rs_table[] = {
.ifirstadr = R_DMA_CH7_FIRST,
.icmdadr = R_DMA_CH7_CMD,
.idescradr = R_DMA_CH7_DESCR,
- .flags = STD_FLAGS,
.rx_ctrl = DEF_RX,
.tx_ctrl = DEF_TX,
.iseteop = 2,
@@ -288,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 */
@@ -300,7 +297,6 @@ static struct e100_serial rs_table[] = {
.ifirstadr = R_DMA_CH9_FIRST,
.icmdadr = R_DMA_CH9_CMD,
.idescradr = R_DMA_CH9_DESCR,
- .flags = STD_FLAGS,
.rx_ctrl = DEF_RX,
.tx_ctrl = DEF_TX,
.iseteop = 3,
@@ -356,7 +352,6 @@ static struct e100_serial rs_table[] = {
.ifirstadr = R_DMA_CH3_FIRST,
.icmdadr = R_DMA_CH3_CMD,
.idescradr = R_DMA_CH3_DESCR,
- .flags = STD_FLAGS,
.rx_ctrl = DEF_RX,
.tx_ctrl = DEF_TX,
.iseteop = 0,
@@ -410,7 +405,6 @@ static struct e100_serial rs_table[] = {
.ifirstadr = R_DMA_CH5_FIRST,
.icmdadr = R_DMA_CH5_CMD,
.idescradr = R_DMA_CH5_DESCR,
- .flags = STD_FLAGS,
.rx_ctrl = DEF_RX,
.tx_ctrl = DEF_TX,
.iseteop = 1,
@@ -452,7 +446,6 @@ static struct e100_serial rs_table[] = {
.dma_in_enabled = 0
#endif
} /* ttyS3 */
-#endif
};
@@ -952,23 +945,10 @@ static const struct control_pins e100_modem_pins[NR_PORTS] =
/* Input */
#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask)
-
-/*
- * tmp_buf is used as a temporary buffer by serial_write. We need to
- * lock it in case the memcpy_fromfs blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char *tmp_buf;
-static DEFINE_MUTEX(tmp_buf_mutex);
-
/* Calculate the chartime depending on baudrate, numbor of bits etc. */
static void update_char_time(struct e100_serial * info)
{
- tcflag_t cflags = info->port.tty->termios->c_cflag;
+ tcflag_t cflags = info->port.tty->termios.c_cflag;
int bits;
/* calc. number of bits / data byte */
@@ -1053,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
@@ -1078,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
@@ -1087,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;
@@ -1097,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
}
@@ -1105,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;
@@ -1117,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;
@@ -1134,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 */
@@ -1486,7 +1454,7 @@ rs_stop(struct tty_struct *tty)
xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char,
STOP_CHAR(info->port.tty));
xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
- if (tty->termios->c_iflag & IXON ) {
+ if (tty->termios.c_iflag & IXON ) {
xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
}
@@ -1509,7 +1477,7 @@ rs_start(struct tty_struct *tty)
info->xmit.tail,SERIAL_XMIT_SIZE)));
xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
- if (tty->termios->c_iflag & IXON ) {
+ if (tty->termios.c_iflag & IXON ) {
xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
}
@@ -1572,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) |
@@ -1773,8 +1723,7 @@ add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char fl
info->icount.rx++;
} else {
- struct tty_struct *tty = info->port.tty;
- tty_insert_flip_char(tty, data, flag);
+ tty_insert_flip_char(&info->port, data, flag);
info->icount.rx++;
}
@@ -1861,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) |
@@ -1953,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 */
@@ -1991,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 */
@@ -2040,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 */
@@ -2118,22 +2032,15 @@ static int force_eop_if_needed(struct e100_serial *info)
static void flush_to_flip_buffer(struct e100_serial *info)
{
- struct tty_struct *tty;
struct etrax_recv_buffer *buffer;
unsigned long flags;
local_irq_save(flags);
- tty = info->port.tty;
-
- if (!tty) {
- local_irq_restore(flags);
- return;
- }
while ((buffer = info->first_recv_buffer) != NULL) {
unsigned int count = buffer->length;
- tty_insert_flip_string(tty, buffer->buffer, count);
+ tty_insert_flip_string(&info->port, buffer->buffer, count);
info->recv_cnt -= count;
if (count == buffer->length) {
@@ -2152,7 +2059,7 @@ static void flush_to_flip_buffer(struct e100_serial *info)
local_irq_restore(flags);
/* This includes a check for low-latency */
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&info->port);
}
static void check_flush_timeout(struct e100_serial *info)
@@ -2179,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);
}
@@ -2199,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)
@@ -2284,16 +2187,9 @@ TODO: The break will be delayed until an F or V character is received.
*/
-static
-struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
+static void handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
{
unsigned long data_read;
- struct tty_struct *tty = info->port.tty;
-
- if (!tty) {
- printk("!NO TTY!\n");
- return info;
- }
/* Read data and status at the same time */
data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]);
@@ -2351,8 +2247,7 @@ more_data:
data_in, data_read);
char flag = TTY_NORMAL;
if (info->errorcode == ERRCODE_INSERT_BREAK) {
- struct tty_struct *tty = info->port.tty;
- tty_insert_flip_char(tty, 0, flag);
+ tty_insert_flip_char(&info->port, 0, flag);
info->icount.rx++;
}
@@ -2366,7 +2261,7 @@ more_data:
info->icount.frame++;
flag = TTY_FRAME;
}
- tty_insert_flip_char(tty, data, flag);
+ tty_insert_flip_char(&info->port, data, flag);
info->errorcode = 0;
}
info->break_detected_cnt = 0;
@@ -2382,7 +2277,7 @@ more_data:
log_int(rdpc(), 0, 0);
}
);
- tty_insert_flip_char(tty,
+ tty_insert_flip_char(&info->port,
IO_EXTRACT(R_SERIAL0_READ, data_in, data_read),
TTY_NORMAL);
} else {
@@ -2397,11 +2292,10 @@ more_data:
goto more_data;
}
- tty_flip_buffer_push(info->port.tty);
- return info;
+ tty_flip_buffer_push(&info->port);
}
-static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
+static void handle_ser_rx_interrupt(struct e100_serial *info)
{
unsigned char rstat;
@@ -2410,7 +2304,8 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
#endif
/* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
if (!info->uses_dma_in) {
- return handle_ser_rx_interrupt_no_dma(info);
+ handle_ser_rx_interrupt_no_dma(info);
+ return;
}
/* DMA is used */
rstat = info->ioport[REG_STATUS];
@@ -2517,7 +2412,6 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
/* Restarting the DMA never hurts */
*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
START_FLUSH_FAST_TIMER(info, "ser_int");
- return info;
} /* handle_ser_rx_interrupt */
static void handle_ser_tx_interrupt(struct e100_serial *info)
@@ -2562,8 +2456,7 @@ static void handle_ser_tx_interrupt(struct e100_serial *info)
}
/* Normal char-by-char interrupt */
if (info->xmit.head == info->xmit.tail
- || info->port.tty->stopped
- || info->port.tty->hw_stopped) {
+ || info->port.tty->stopped) {
DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n",
info->port.tty->stopped));
e100_disable_serial_tx_ready_irq(info);
@@ -2750,7 +2643,7 @@ startup(struct e100_serial * info)
/* if it was already initialized, skip this */
- if (info->flags & ASYNC_INITIALIZED) {
+ if (info->port.flags & ASYNC_INITIALIZED) {
local_irq_restore(flags);
free_page(xmit_page);
return 0;
@@ -2765,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())
@@ -2873,9 +2747,7 @@ startup(struct e100_serial * info)
e100_rts(info, 1);
e100_dtr(info, 1);
-#endif /* CONFIG_SVINTO_SIM */
-
- info->flags |= ASYNC_INITIALIZED;
+ info->port.flags |= ASYNC_INITIALIZED;
local_irq_restore(flags);
return 0;
@@ -2893,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);
@@ -2918,9 +2789,7 @@ shutdown(struct e100_serial * info)
info->tr_running = 0;
}
-#endif /* CONFIG_SVINTO_SIM */
-
- if (!(info->flags & ASYNC_INITIALIZED))
+ if (!(info->port.flags & ASYNC_INITIALIZED))
return;
#ifdef SERIAL_DEBUG_OPEN
@@ -2942,7 +2811,7 @@ shutdown(struct e100_serial * info)
descr[i].buf = 0;
}
- if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) {
+ if (!info->port.tty || (info->port.tty->termios.c_cflag & HUPCL)) {
/* hang up DTR and RTS if HUPCL is enabled */
e100_dtr(info, 0);
e100_rts(info, 0); /* could check CRTSCTS before doing this */
@@ -2951,7 +2820,7 @@ shutdown(struct e100_serial * info)
if (info->port.tty)
set_bit(TTY_IO_ERROR, &info->port.tty->flags);
- info->flags &= ~ASYNC_INITIALIZED;
+ info->port.flags &= ~ASYNC_INITIALIZED;
local_irq_restore(flags);
}
@@ -2966,17 +2835,17 @@ change_speed(struct e100_serial *info)
unsigned long flags;
/* first some safety checks */
- if (!info->port.tty || !info->port.tty->termios)
+ if (!info->port.tty)
return;
if (!info->ioport)
return;
- cflag = info->port.tty->termios->c_cflag;
+ cflag = info->port.tty->termios.c_cflag;
/* possibly, the tx/rx should be disabled first to do this safely */
/* change baud-rate and write it to the hardware */
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+ if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
/* Special baudrate */
u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
unsigned long alt_source =
@@ -3031,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 */
@@ -3101,7 +2965,7 @@ change_speed(struct e100_serial *info)
info->ioport[REG_REC_CTRL] = info->rx_ctrl;
xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty));
xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
- if (info->port.tty->termios->c_iflag & IXON ) {
+ if (info->port.tty->termios.c_iflag & IXON ) {
DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n",
STOP_CHAR(info->port.tty)));
xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
@@ -3109,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);
@@ -3126,7 +2989,6 @@ rs_flush_chars(struct tty_struct *tty)
if (info->tr_running ||
info->xmit.head == info->xmit.tail ||
tty->stopped ||
- tty->hw_stopped ||
!info->xmit.buf)
return;
@@ -3150,7 +3012,7 @@ static int rs_raw_write(struct tty_struct *tty,
/* first some sanity checks */
- if (!tty || !info->xmit.buf || !tmp_buf)
+ if (!info->xmit.buf)
return 0;
#ifdef SERIAL_DEBUG_DATA
@@ -3159,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)));
@@ -3204,7 +3061,6 @@ static int rs_raw_write(struct tty_struct *tty,
if (info->xmit.head != info->xmit.tail &&
!tty->stopped &&
- !tty->hw_stopped &&
!info->tr_running) {
start_transmit(info);
}
@@ -3368,7 +3224,7 @@ rs_throttle(struct tty_struct * tty)
DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty)));
/* Do RTS before XOFF since XOFF might take some time */
- if (tty->termios->c_cflag & CRTSCTS) {
+ if (tty->termios.c_cflag & CRTSCTS) {
/* Turn off RTS line */
e100_rts(info, 0);
}
@@ -3390,7 +3246,7 @@ rs_unthrottle(struct tty_struct * tty)
DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
/* Do RTS before XOFF since XOFF might take some time */
- if (tty->termios->c_cflag & CRTSCTS) {
+ if (tty->termios.c_cflag & CRTSCTS) {
/* Assert RTS line */
e100_rts(info, 1);
}
@@ -3428,10 +3284,10 @@ get_serial_info(struct e100_serial * info,
tmp.line = info->line;
tmp.port = (int)info->ioport;
tmp.irq = info->irq;
- tmp.flags = info->flags;
+ tmp.flags = info->port.flags;
tmp.baud_base = info->baud_base;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
+ tmp.close_delay = info->port.close_delay;
+ tmp.closing_wait = info->port.closing_wait;
tmp.custom_divisor = info->custom_divisor;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
@@ -3453,16 +3309,16 @@ set_serial_info(struct e100_serial *info,
if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.type != info->type) ||
- (new_serial.close_delay != info->close_delay) ||
+ (new_serial.close_delay != info->port.close_delay) ||
((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->flags & ~ASYNC_USR_MASK)))
+ (info->port.flags & ~ASYNC_USR_MASK)))
return -EPERM;
- info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+ info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
goto check_and_exit;
}
- if (info->count > 1)
+ if (info->port.count > 1)
return -EBUSY;
/*
@@ -3471,16 +3327,16 @@ set_serial_info(struct e100_serial *info,
*/
info->baud_base = new_serial.baud_base;
- info->flags = ((info->flags & ~ASYNC_FLAGS) |
+ info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
info->custom_divisor = new_serial.custom_divisor;
info->type = new_serial.type;
- info->close_delay = new_serial.close_delay;
- info->closing_wait = new_serial.closing_wait;
- info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ info->port.close_delay = new_serial.close_delay;
+ info->port.closing_wait = new_serial.closing_wait;
+ info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
check_and_exit:
- if (info->flags & ASYNC_INITIALIZED) {
+ if (info->port.flags & ASYNC_INITIALIZED) {
change_speed(info);
} else
retval = startup(info);
@@ -3501,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 =
@@ -3512,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;
@@ -3761,10 +3615,8 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- tty->hw_stopped = 0;
+ !(tty->termios.c_cflag & CRTSCTS))
rs_start(tty);
- }
}
@@ -3800,7 +3652,7 @@ rs_close(struct tty_struct *tty, struct file * filp)
printk("[%d] rs_close ttyS%d, count = %d\n", current->pid,
info->line, info->count);
#endif
- if ((tty->count == 1) && (info->count != 1)) {
+ if ((tty->count == 1) && (info->port.count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. Info->count should always
@@ -3810,32 +3662,32 @@ rs_close(struct tty_struct *tty, struct file * filp)
*/
printk(KERN_ERR
"rs_close: bad serial port count; tty->count is 1, "
- "info->count is %d\n", info->count);
- info->count = 1;
+ "info->count is %d\n", info->port.count);
+ info->port.count = 1;
}
- if (--info->count < 0) {
+ if (--info->port.count < 0) {
printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n",
- info->line, info->count);
- info->count = 0;
+ info->line, info->port.count);
+ info->port.count = 0;
}
- if (info->count) {
+ if (info->port.count) {
local_irq_restore(flags);
return;
}
- info->flags |= ASYNC_CLOSING;
+ info->port.flags |= ASYNC_CLOSING;
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
- if (info->flags & ASYNC_NORMAL_ACTIVE)
- info->normal_termios = *tty->termios;
+ if (info->port.flags & ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = tty->termios;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
- if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, info->closing_wait);
+ if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->port.closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the serial receiver and the DMA receive interrupt.
@@ -3844,11 +3696,10 @@ 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);
- if (info->flags & ASYNC_INITIALIZED) {
+ if (info->port.flags & ASYNC_INITIALIZED) {
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
@@ -3856,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);
@@ -3864,13 +3714,13 @@ rs_close(struct tty_struct *tty, struct file * filp)
tty->closing = 0;
info->event = 0;
info->port.tty = NULL;
- if (info->blocked_open) {
- if (info->close_delay)
- schedule_timeout_interruptible(info->close_delay);
- wake_up_interruptible(&info->open_wait);
+ if (info->port.blocked_open) {
+ if (info->port.close_delay)
+ schedule_timeout_interruptible(info->port.close_delay);
+ wake_up_interruptible(&info->port.open_wait);
}
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
- wake_up_interruptible(&info->close_wait);
+ info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+ wake_up_interruptible(&info->port.close_wait);
local_irq_restore(flags);
/* port closed */
@@ -3963,10 +3813,10 @@ rs_hangup(struct tty_struct *tty)
rs_flush_buffer(tty);
shutdown(info);
info->event = 0;
- info->count = 0;
- info->flags &= ~ASYNC_NORMAL_ACTIVE;
+ info->port.count = 0;
+ info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
info->port.tty = NULL;
- wake_up_interruptible(&info->open_wait);
+ wake_up_interruptible(&info->port.open_wait);
}
/*
@@ -3988,11 +3838,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
* until it's done, and then try again.
*/
if (tty_hung_up_p(filp) ||
- (info->flags & ASYNC_CLOSING)) {
- wait_event_interruptible_tty(info->close_wait,
- !(info->flags & ASYNC_CLOSING));
+ (info->port.flags & ASYNC_CLOSING)) {
+ wait_event_interruptible_tty(tty, info->port.close_wait,
+ !(info->port.flags & ASYNC_CLOSING));
#ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
+ if (info->port.flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
@@ -4007,34 +3857,34 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
- info->flags |= ASYNC_NORMAL_ACTIVE;
+ info->port.flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
- if (tty->termios->c_cflag & CLOCAL) {
+ if (tty->termios.c_cflag & CLOCAL) {
do_clocal = 1;
}
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
+ * this loop, info->port.count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
- add_wait_queue(&info->open_wait, &wait);
+ add_wait_queue(&info->port.open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready before block: ttyS%d, count = %d\n",
- info->line, info->count);
+ info->line, info->port.count);
#endif
local_irq_save(flags);
if (!tty_hung_up_p(filp)) {
extra_count++;
- info->count--;
+ info->port.count--;
}
local_irq_restore(flags);
- info->blocked_open++;
+ info->port.blocked_open++;
while (1) {
local_irq_save(flags);
/* assert RTS and DTR */
@@ -4043,9 +3893,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
local_irq_restore(flags);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
- !(info->flags & ASYNC_INITIALIZED)) {
+ !(info->port.flags & ASYNC_INITIALIZED)) {
#ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
+ if (info->port.flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
@@ -4054,7 +3904,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
#endif
break;
}
- if (!(info->flags & ASYNC_CLOSING) && do_clocal)
+ if (!(info->port.flags & ASYNC_CLOSING) && do_clocal)
/* && (do_clocal || DCD_IS_ASSERTED) */
break;
if (signal_pending(current)) {
@@ -4063,24 +3913,24 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
}
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready blocking: ttyS%d, count = %d\n",
- info->line, info->count);
+ info->line, info->port.count);
#endif
- tty_unlock();
+ tty_unlock(tty);
schedule();
- tty_lock();
+ tty_lock(tty);
}
set_current_state(TASK_RUNNING);
- remove_wait_queue(&info->open_wait, &wait);
+ remove_wait_queue(&info->port.open_wait, &wait);
if (extra_count)
- info->count++;
- info->blocked_open--;
+ info->port.count++;
+ info->port.blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttyS%d, count = %d\n",
- info->line, info->count);
+ info->line, info->port.count);
#endif
if (retval)
return retval;
- info->flags |= ASYNC_NORMAL_ACTIVE;
+ info->port.flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
@@ -4105,54 +3955,33 @@ static int
rs_open(struct tty_struct *tty, struct file * filp)
{
struct e100_serial *info;
- int retval, line;
- unsigned long page;
+ int retval;
int allocated_resources = 0;
- /* find which port we want to open */
- line = tty->index;
-
- if (line < 0 || line >= NR_PORTS)
- return -ENODEV;
-
- /* find the corresponding e100_serial struct in the table */
- info = rs_table + line;
-
- /* don't allow the opening of ports that are not enabled in the HW config */
+ info = rs_table + tty->index;
if (!info->enabled)
return -ENODEV;
#ifdef SERIAL_DEBUG_OPEN
printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name,
- info->count);
+ info->port.count);
#endif
- info->count++;
+ info->port.count++;
tty->driver_data = info;
info->port.tty = tty;
- info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
- if (!tmp_buf) {
- page = get_zeroed_page(GFP_KERNEL);
- if (!page) {
- return -ENOMEM;
- }
- if (tmp_buf)
- free_page(page);
- else
- tmp_buf = (unsigned char *) page;
- }
+ info->port.low_latency = !!(info->port.flags & ASYNC_LOW_LATENCY);
/*
* If the port is in the middle of closing, bail out now
*/
if (tty_hung_up_p(filp) ||
- (info->flags & ASYNC_CLOSING)) {
- wait_event_interruptible_tty(info->close_wait,
- !(info->flags & ASYNC_CLOSING));
+ (info->port.flags & ASYNC_CLOSING)) {
+ wait_event_interruptible_tty(tty, info->port.close_wait,
+ !(info->port.flags & ASYNC_CLOSING));
#ifdef SERIAL_DO_RESTART
- return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ return ((info->port.flags & ASYNC_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS);
#else
return -EAGAIN;
@@ -4162,7 +3991,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
/*
* If DMA is enabled try to allocate the irq's.
*/
- if (info->count == 1) {
+ if (info->port.count == 1) {
allocated_resources = 1;
if (info->dma_in_enabled) {
if (request_irq(info->dma_in_irq_nbr,
@@ -4235,7 +4064,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
if (allocated_resources)
deinit_port(info);
- /* FIXME Decrease count info->count here too? */
+ /* FIXME Decrease count info->port.count here too? */
return retval;
}
@@ -4252,8 +4081,8 @@ rs_open(struct tty_struct *tty, struct file * filp)
return retval;
}
- if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
- *tty->termios = info->normal_termios;
+ if ((info->port.count == 1) && (info->port.flags & ASYNC_SPLIT_TERMIOS)) {
+ tty->termios = info->normal_termios;
change_speed(info);
}
@@ -4305,9 +4134,6 @@ static void seq_line_info(struct seq_file *m, struct e100_serial *info)
if (info->port.tty->stopped)
seq_printf(m, " stopped:%i",
(int)info->port.tty->stopped);
- if (info->port.tty->hw_stopped)
- seq_printf(m, " hw_stopped:%i",
- (int)info->port.tty->hw_stopped);
}
{
@@ -4477,14 +4303,12 @@ static int __init rs_init(void)
B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
driver->init_termios.c_ispeed = 115200;
driver->init_termios.c_ospeed = 115200;
- driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(driver, &rs_ops);
serial_driver = driver;
- if (tty_register_driver(driver))
- panic("Couldn't register serial driver\n");
- /* do some initializing for the separate ports */
+ /* do some initializing for the separate ports */
for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
if (info->enabled) {
if (cris_request_io_interface(info->io_if,
@@ -4496,6 +4320,7 @@ static int __init rs_init(void)
info->enabled = 0;
}
}
+ tty_port_init(&info->port);
info->uses_dma_in = 0;
info->uses_dma_out = 0;
info->line = i;
@@ -4505,16 +4330,9 @@ static int __init rs_init(void)
info->forced_eop = 0;
info->baud_base = DEF_BAUD_BASE;
info->custom_divisor = 0;
- info->flags = 0;
- info->close_delay = 5*HZ/10;
- info->closing_wait = 30*HZ;
info->x_char = 0;
info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
info->normal_termios = driver->init_termios;
- init_waitqueue_head(&info->open_wait);
- init_waitqueue_head(&info->close_wait);
info->xmit.buf = NULL;
info->xmit.tail = info->xmit.head = 0;
info->first_recv_buffer = info->last_recv_buffer = NULL;
@@ -4535,7 +4353,12 @@ static int __init rs_init(void)
printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
serial_driver->name, info->line, info->ioport);
}
+ tty_port_link_device(&info->port, driver, i);
}
+
+ if (tty_register_driver(driver))
+ panic("Couldn't register serial driver\n");
+
#ifdef CONFIG_ETRAX_FAST_TIMER
#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
memset(fast_timers, 0, sizeof(fast_timers));
@@ -4546,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... */
@@ -4556,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/crisv10.h b/drivers/tty/serial/crisv10.h
index ea0beb46a10..7599014ae03 100644
--- a/drivers/tty/serial/crisv10.h
+++ b/drivers/tty/serial/crisv10.h
@@ -53,8 +53,6 @@ struct e100_serial {
volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */
volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */
- int flags; /* defined in tty.h */
-
u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
u8 iseteop; /* bit number for R_SET_EOP for the input dma */
@@ -88,19 +86,10 @@ struct e100_serial {
volatile int tr_running; /* 1 if output is running */
- struct tty_struct *tty;
- int read_status_mask;
- int ignore_status_mask;
int x_char; /* xon/xoff character */
- int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
unsigned long event;
- unsigned long last_active;
int line;
int type; /* PORT_ETRAX */
- int count; /* # of fd on device */
- int blocked_open; /* # of blocked opens */
struct circ_buf xmit;
struct etrax_recv_buffer *first_recv_buffer;
struct etrax_recv_buffer *last_recv_buffer;
@@ -110,9 +99,6 @@ struct e100_serial {
struct work_struct work;
struct async_icount icount; /* error-statistics etc.*/
struct ktermios normal_termios;
- struct ktermios callout_termios;
- wait_queue_head_t open_wait;
- wait_queue_head_t close_wait;
unsigned long char_time_usec; /* The time for 1 char, in usecs */
unsigned long flush_time_usec; /* How often we should flush */
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
index e3699a84049..cdbbc788230 100644
--- a/drivers/tty/serial/dz.c
+++ b/drivers/tty/serial/dz.c
@@ -52,7 +52,6 @@
#include <linux/atomic.h>
#include <asm/bootinfo.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/dec/interrupts.h>
#include <asm/dec/kn01.h>
@@ -188,7 +187,6 @@ static inline void dz_receive_chars(struct dz_mux *mux)
{
struct uart_port *uport;
struct dz_port *dport = &mux->dport[0];
- struct tty_struct *tty = NULL;
struct uart_icount *icount;
int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 };
unsigned char ch, flag;
@@ -198,7 +196,6 @@ static inline void dz_receive_chars(struct dz_mux *mux)
while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) {
dport = &mux->dport[LINE(status)];
uport = &dport->port;
- tty = uport->state->port.tty; /* point to the proper dev */
ch = UCHAR(status); /* grab the char */
flag = TTY_NORMAL;
@@ -250,7 +247,7 @@ static inline void dz_receive_chars(struct dz_mux *mux)
}
for (i = 0; i < DZ_NB_PORT; i++)
if (lines_rx[i])
- tty_flip_buffer_push(mux->dport[i].port.state->port.tty);
+ tty_flip_buffer_push(&mux->dport[i].port.state->port);
}
/*
@@ -628,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
new file mode 100644
index 00000000000..3b0ee9afd76
--- /dev/null
+++ b/drivers/tty/serial/efm32-uart.c
@@ -0,0 +1,850 @@
+#if defined(CONFIG_SERIAL_EFM32_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/platform_data/efm32-uart.h>
+
+#define DRIVER_NAME "efm32-uart"
+#define DEV_NAME "ttyefm"
+
+#define UARTn_CTRL 0x00
+#define UARTn_CTRL_SYNC 0x0001
+#define UARTn_CTRL_TXBIL 0x1000
+
+#define UARTn_FRAME 0x04
+#define UARTn_FRAME_DATABITS__MASK 0x000f
+#define UARTn_FRAME_DATABITS(n) ((n) - 3)
+#define UARTn_FRAME_PARITY_NONE 0x0000
+#define UARTn_FRAME_PARITY_EVEN 0x0200
+#define UARTn_FRAME_PARITY_ODD 0x0300
+#define UARTn_FRAME_STOPBITS_HALF 0x0000
+#define UARTn_FRAME_STOPBITS_ONE 0x1000
+#define UARTn_FRAME_STOPBITS_TWO 0x3000
+
+#define UARTn_CMD 0x0c
+#define UARTn_CMD_RXEN 0x0001
+#define UARTn_CMD_RXDIS 0x0002
+#define UARTn_CMD_TXEN 0x0004
+#define UARTn_CMD_TXDIS 0x0008
+
+#define UARTn_STATUS 0x10
+#define UARTn_STATUS_TXENS 0x0002
+#define UARTn_STATUS_TXC 0x0020
+#define UARTn_STATUS_TXBL 0x0040
+#define UARTn_STATUS_RXDATAV 0x0080
+
+#define UARTn_CLKDIV 0x14
+
+#define UARTn_RXDATAX 0x18
+#define UARTn_RXDATAX_RXDATA__MASK 0x01ff
+#define UARTn_RXDATAX_PERR 0x4000
+#define UARTn_RXDATAX_FERR 0x8000
+/*
+ * This is a software only flag used for ignore_status_mask and
+ * read_status_mask! It's used for breaks that the hardware doesn't report
+ * explicitly.
+ */
+#define SW_UARTn_RXDATAX_BERR 0x2000
+
+#define UARTn_TXDATA 0x34
+
+#define UARTn_IF 0x40
+#define UARTn_IF_TXC 0x0001
+#define UARTn_IF_TXBL 0x0002
+#define UARTn_IF_RXDATAV 0x0004
+#define UARTn_IF_RXOF 0x0010
+
+#define UARTn_IFS 0x44
+#define UARTn_IFC 0x48
+#define UARTn_IEN 0x4c
+
+#define UARTn_ROUTE 0x54
+#define UARTn_ROUTE_LOCATION__MASK 0x0700
+#define UARTn_ROUTE_LOCATION(n) (((n) << 8) & UARTn_ROUTE_LOCATION__MASK)
+#define UARTn_ROUTE_RXPEN 0x0001
+#define UARTn_ROUTE_TXPEN 0x0002
+
+struct efm32_uart_port {
+ struct uart_port port;
+ unsigned int txirq;
+ struct clk *clk;
+ struct efm32_uart_pdata pdata;
+};
+#define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port)
+#define efm_debug(efm_port, format, arg...) \
+ dev_dbg(efm_port->port.dev, format, ##arg)
+
+static void efm32_uart_write32(struct efm32_uart_port *efm_port,
+ u32 value, unsigned offset)
+{
+ writel_relaxed(value, efm_port->port.membase + offset);
+}
+
+static u32 efm32_uart_read32(struct efm32_uart_port *efm_port,
+ unsigned offset)
+{
+ return readl_relaxed(efm_port->port.membase + offset);
+}
+
+static unsigned int efm32_uart_tx_empty(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+ u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+ if (status & UARTn_STATUS_TXC)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+static void efm32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* sorry, neither handshaking lines nor loop functionallity */
+}
+
+static unsigned int efm32_uart_get_mctrl(struct uart_port *port)
+{
+ /* sorry, no handshaking lines available */
+ return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void efm32_uart_stop_tx(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+ u32 ien = efm32_uart_read32(efm_port, UARTn_IEN);
+
+ efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+ ien &= ~(UARTn_IF_TXC | UARTn_IF_TXBL);
+ efm32_uart_write32(efm_port, ien, UARTn_IEN);
+}
+
+static void efm32_uart_tx_chars(struct efm32_uart_port *efm_port)
+{
+ struct uart_port *port = &efm_port->port;
+ struct circ_buf *xmit = &port->state->xmit;
+
+ while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+ UARTn_STATUS_TXBL) {
+ if (port->x_char) {
+ port->icount.tx++;
+ efm32_uart_write32(efm_port, port->x_char,
+ UARTn_TXDATA);
+ port->x_char = 0;
+ continue;
+ }
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+ port->icount.tx++;
+ efm32_uart_write32(efm_port, xmit->buf[xmit->tail],
+ UARTn_TXDATA);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ } else
+ break;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (!port->x_char && uart_circ_empty(xmit) &&
+ efm32_uart_read32(efm_port, UARTn_STATUS) &
+ UARTn_STATUS_TXC)
+ efm32_uart_stop_tx(port);
+}
+
+static void efm32_uart_start_tx(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+ u32 ien;
+
+ efm32_uart_write32(efm_port,
+ UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IFC);
+ ien = efm32_uart_read32(efm_port, UARTn_IEN);
+ efm32_uart_write32(efm_port,
+ ien | UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IEN);
+ efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+ efm32_uart_tx_chars(efm_port);
+}
+
+static void efm32_uart_stop_rx(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+
+ efm32_uart_write32(efm_port, UARTn_CMD_RXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_enable_ms(struct uart_port *port)
+{
+ /* no handshake lines, no modem status interrupts */
+}
+
+static void efm32_uart_break_ctl(struct uart_port *port, int ctl)
+{
+ /* not possible without fiddling with gpios */
+}
+
+static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port)
+{
+ struct uart_port *port = &efm_port->port;
+
+ while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+ UARTn_STATUS_RXDATAV) {
+ u32 rxdata = efm32_uart_read32(efm_port, UARTn_RXDATAX);
+ int flag = 0;
+
+ /*
+ * This is a reserved bit and I only saw it read as 0. But to be
+ * sure not to be confused too much by new devices adhere to the
+ * warning in the reference manual that reserverd bits might
+ * read as 1 in the future.
+ */
+ rxdata &= ~SW_UARTn_RXDATAX_BERR;
+
+ port->icount.rx++;
+
+ if ((rxdata & UARTn_RXDATAX_FERR) &&
+ !(rxdata & UARTn_RXDATAX_RXDATA__MASK)) {
+ rxdata |= SW_UARTn_RXDATAX_BERR;
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ } else if (rxdata & UARTn_RXDATAX_PERR)
+ port->icount.parity++;
+ else if (rxdata & UARTn_RXDATAX_FERR)
+ port->icount.frame++;
+
+ rxdata &= port->read_status_mask;
+
+ if (rxdata & SW_UARTn_RXDATAX_BERR)
+ flag = TTY_BREAK;
+ else if (rxdata & UARTn_RXDATAX_PERR)
+ flag = TTY_PARITY;
+ else if (rxdata & UARTn_RXDATAX_FERR)
+ flag = TTY_FRAME;
+ else if (uart_handle_sysrq_char(port,
+ rxdata & UARTn_RXDATAX_RXDATA__MASK))
+ continue;
+
+ if ((rxdata & port->ignore_status_mask) == 0)
+ tty_insert_flip_char(&port->state->port,
+ rxdata & UARTn_RXDATAX_RXDATA__MASK, flag);
+ }
+}
+
+static irqreturn_t efm32_uart_rxirq(int irq, void *data)
+{
+ struct efm32_uart_port *efm_port = data;
+ u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+ int handled = IRQ_NONE;
+ struct uart_port *port = &efm_port->port;
+ struct tty_port *tport = &port->state->port;
+
+ spin_lock(&port->lock);
+
+ if (irqflag & UARTn_IF_RXDATAV) {
+ efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IFC);
+ efm32_uart_rx_chars(efm_port);
+
+ handled = IRQ_HANDLED;
+ }
+
+ if (irqflag & UARTn_IF_RXOF) {
+ efm32_uart_write32(efm_port, UARTn_IF_RXOF, UARTn_IFC);
+ port->icount.overrun++;
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+
+ handled = IRQ_HANDLED;
+ }
+
+ spin_unlock(&port->lock);
+
+ tty_flip_buffer_push(tport);
+
+ return handled;
+}
+
+static irqreturn_t efm32_uart_txirq(int irq, void *data)
+{
+ struct efm32_uart_port *efm_port = data;
+ u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+
+ /* TXBL doesn't need to be cleared */
+ if (irqflag & UARTn_IF_TXC)
+ efm32_uart_write32(efm_port, UARTn_IF_TXC, UARTn_IFC);
+
+ if (irqflag & (UARTn_IF_TXC | UARTn_IF_TXBL)) {
+ efm32_uart_tx_chars(efm_port);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+}
+
+static int efm32_uart_startup(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+ int ret;
+
+ ret = clk_enable(efm_port->clk);
+ if (ret) {
+ efm_debug(efm_port, "failed to enable clk\n");
+ goto err_clk_enable;
+ }
+ port->uartclk = clk_get_rate(efm_port->clk);
+
+ /* Enable pins at configured location */
+ efm32_uart_write32(efm_port,
+ UARTn_ROUTE_LOCATION(efm_port->pdata.location) |
+ UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN,
+ UARTn_ROUTE);
+
+ ret = request_irq(port->irq, efm32_uart_rxirq, 0,
+ DRIVER_NAME, efm_port);
+ if (ret) {
+ efm_debug(efm_port, "failed to register rxirq\n");
+ goto err_request_irq_rx;
+ }
+
+ /* disable all irqs */
+ efm32_uart_write32(efm_port, 0, UARTn_IEN);
+
+ ret = request_irq(efm_port->txirq, efm32_uart_txirq, 0,
+ DRIVER_NAME, efm_port);
+ if (ret) {
+ efm_debug(efm_port, "failed to register txirq\n");
+ free_irq(port->irq, efm_port);
+err_request_irq_rx:
+
+ clk_disable(efm_port->clk);
+ } else {
+ efm32_uart_write32(efm_port,
+ UARTn_IF_RXDATAV | UARTn_IF_RXOF, UARTn_IEN);
+ efm32_uart_write32(efm_port, UARTn_CMD_RXEN, UARTn_CMD);
+ }
+
+err_clk_enable:
+ return ret;
+}
+
+static void efm32_uart_shutdown(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+
+ efm32_uart_write32(efm_port, 0, UARTn_IEN);
+ free_irq(port->irq, efm_port);
+
+ clk_disable(efm_port->clk);
+}
+
+static void efm32_uart_set_termios(struct uart_port *port,
+ struct ktermios *new, struct ktermios *old)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+ unsigned long flags;
+ unsigned baud;
+ u32 clkdiv;
+ u32 frame = 0;
+
+ /* no modem control lines */
+ new->c_cflag &= ~(CRTSCTS | CMSPAR);
+
+ baud = uart_get_baud_rate(port, new, old,
+ DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192),
+ DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+ switch (new->c_cflag & CSIZE) {
+ case CS5:
+ frame |= UARTn_FRAME_DATABITS(5);
+ break;
+ case CS6:
+ frame |= UARTn_FRAME_DATABITS(6);
+ break;
+ case CS7:
+ frame |= UARTn_FRAME_DATABITS(7);
+ break;
+ case CS8:
+ frame |= UARTn_FRAME_DATABITS(8);
+ break;
+ }
+
+ if (new->c_cflag & CSTOPB)
+ /* the receiver only verifies the first stop bit */
+ frame |= UARTn_FRAME_STOPBITS_TWO;
+ else
+ frame |= UARTn_FRAME_STOPBITS_ONE;
+
+ if (new->c_cflag & PARENB) {
+ if (new->c_cflag & PARODD)
+ frame |= UARTn_FRAME_PARITY_ODD;
+ else
+ frame |= UARTn_FRAME_PARITY_EVEN;
+ } else
+ frame |= UARTn_FRAME_PARITY_NONE;
+
+ /*
+ * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25.
+ * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow.
+ */
+ clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ efm32_uart_write32(efm_port,
+ UARTn_CMD_TXDIS | UARTn_CMD_RXDIS, UARTn_CMD);
+
+ port->read_status_mask = UARTn_RXDATAX_RXDATA__MASK;
+ if (new->c_iflag & INPCK)
+ port->read_status_mask |=
+ UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR;
+ if (new->c_iflag & (IGNBRK | BRKINT | PARMRK))
+ port->read_status_mask |= SW_UARTn_RXDATAX_BERR;
+
+ port->ignore_status_mask = 0;
+ if (new->c_iflag & IGNPAR)
+ port->ignore_status_mask |=
+ UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR;
+ if (new->c_iflag & IGNBRK)
+ port->ignore_status_mask |= SW_UARTn_RXDATAX_BERR;
+
+ uart_update_timeout(port, new->c_cflag, baud);
+
+ efm32_uart_write32(efm_port, UARTn_CTRL_TXBIL, UARTn_CTRL);
+ efm32_uart_write32(efm_port, frame, UARTn_FRAME);
+ efm32_uart_write32(efm_port, clkdiv, UARTn_CLKDIV);
+
+ efm32_uart_write32(efm_port, UARTn_CMD_TXEN | UARTn_CMD_RXEN,
+ UARTn_CMD);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *efm32_uart_type(struct uart_port *port)
+{
+ return port->type == PORT_EFMUART ? "efm32-uart" : NULL;
+}
+
+static void efm32_uart_release_port(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+
+ clk_unprepare(efm_port->clk);
+ clk_put(efm_port->clk);
+ iounmap(port->membase);
+}
+
+static int efm32_uart_request_port(struct uart_port *port)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+ int ret;
+
+ port->membase = ioremap(port->mapbase, 60);
+ if (!efm_port->port.membase) {
+ ret = -ENOMEM;
+ efm_debug(efm_port, "failed to remap\n");
+ goto err_ioremap;
+ }
+
+ efm_port->clk = clk_get(port->dev, NULL);
+ if (IS_ERR(efm_port->clk)) {
+ ret = PTR_ERR(efm_port->clk);
+ efm_debug(efm_port, "failed to get clock\n");
+ goto err_clk_get;
+ }
+
+ ret = clk_prepare(efm_port->clk);
+ if (ret) {
+ clk_put(efm_port->clk);
+err_clk_get:
+
+ iounmap(port->membase);
+err_ioremap:
+ return ret;
+ }
+ return 0;
+}
+
+static void efm32_uart_config_port(struct uart_port *port, int type)
+{
+ if (type & UART_CONFIG_TYPE &&
+ !efm32_uart_request_port(port))
+ port->type = PORT_EFMUART;
+}
+
+static int efm32_uart_verify_port(struct uart_port *port,
+ struct serial_struct *serinfo)
+{
+ int ret = 0;
+
+ if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUART)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static struct uart_ops efm32_uart_pops = {
+ .tx_empty = efm32_uart_tx_empty,
+ .set_mctrl = efm32_uart_set_mctrl,
+ .get_mctrl = efm32_uart_get_mctrl,
+ .stop_tx = efm32_uart_stop_tx,
+ .start_tx = efm32_uart_start_tx,
+ .stop_rx = efm32_uart_stop_rx,
+ .enable_ms = efm32_uart_enable_ms,
+ .break_ctl = efm32_uart_break_ctl,
+ .startup = efm32_uart_startup,
+ .shutdown = efm32_uart_shutdown,
+ .set_termios = efm32_uart_set_termios,
+ .type = efm32_uart_type,
+ .release_port = efm32_uart_release_port,
+ .request_port = efm32_uart_request_port,
+ .config_port = efm32_uart_config_port,
+ .verify_port = efm32_uart_verify_port,
+};
+
+static struct efm32_uart_port *efm32_uart_ports[5];
+
+#ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE
+static void efm32_uart_console_putchar(struct uart_port *port, int ch)
+{
+ struct efm32_uart_port *efm_port = to_efm_port(port);
+ unsigned int timeout = 0x400;
+ u32 status;
+
+ while (1) {
+ status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+ if (status & UARTn_STATUS_TXBL)
+ break;
+ if (!timeout--)
+ return;
+ }
+ efm32_uart_write32(efm_port, ch, UARTn_TXDATA);
+}
+
+static void efm32_uart_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct efm32_uart_port *efm_port = efm32_uart_ports[co->index];
+ u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+ unsigned int timeout = 0x400;
+
+ if (!(status & UARTn_STATUS_TXENS))
+ efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+ uart_console_write(&efm_port->port, s, count,
+ efm32_uart_console_putchar);
+
+ /* Wait for the transmitter to become empty */
+ while (1) {
+ u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+ if (status & UARTn_STATUS_TXC)
+ break;
+ if (!timeout--)
+ break;
+ }
+
+ if (!(status & UARTn_STATUS_TXENS))
+ efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port,
+ int *baud, int *parity, int *bits)
+{
+ u32 ctrl = efm32_uart_read32(efm_port, UARTn_CTRL);
+ u32 route, clkdiv, frame;
+
+ if (ctrl & UARTn_CTRL_SYNC)
+ /* not operating in async mode */
+ return;
+
+ route = efm32_uart_read32(efm_port, UARTn_ROUTE);
+ if (!(route & UARTn_ROUTE_TXPEN))
+ /* tx pin not routed */
+ return;
+
+ clkdiv = efm32_uart_read32(efm_port, UARTn_CLKDIV);
+
+ *baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk,
+ 16 * (4 + (clkdiv >> 6)));
+
+ frame = efm32_uart_read32(efm_port, UARTn_FRAME);
+ if (frame & UARTn_FRAME_PARITY_ODD)
+ *parity = 'o';
+ else if (frame & UARTn_FRAME_PARITY_EVEN)
+ *parity = 'e';
+ else
+ *parity = 'n';
+
+ *bits = (frame & UARTn_FRAME_DATABITS__MASK) -
+ UARTn_FRAME_DATABITS(4) + 4;
+
+ efm_debug(efm_port, "get_opts: options=%d%c%d\n",
+ *baud, *parity, *bits);
+}
+
+static int efm32_uart_console_setup(struct console *co, char *options)
+{
+ struct efm32_uart_port *efm_port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret;
+
+ if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_uart_ports)) {
+ unsigned i;
+ for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) {
+ if (efm32_uart_ports[i]) {
+ pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
+ i, co->index);
+ co->index = i;
+ break;
+ }
+ }
+ }
+
+ efm_port = efm32_uart_ports[co->index];
+ if (!efm_port) {
+ pr_warn("efm32-console: No port at %d\n", co->index);
+ return -ENODEV;
+ }
+
+ ret = clk_prepare(efm_port->clk);
+ if (ret) {
+ dev_warn(efm_port->port.dev,
+ "console: clk_prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ efm_port->port.uartclk = clk_get_rate(efm_port->clk);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ efm32_uart_console_get_options(efm_port,
+ &baud, &parity, &bits);
+
+ return uart_set_options(&efm_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver efm32_uart_reg;
+
+static struct console efm32_uart_console = {
+ .name = DEV_NAME,
+ .write = efm32_uart_console_write,
+ .device = uart_console_device,
+ .setup = efm32_uart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &efm32_uart_reg,
+};
+
+#else
+#define efm32_uart_console (*(struct console *)NULL)
+#endif /* ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE / else */
+
+static struct uart_driver efm32_uart_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = DEV_NAME,
+ .nr = ARRAY_SIZE(efm32_uart_ports),
+ .cons = &efm32_uart_console,
+};
+
+static int efm32_uart_probe_dt(struct platform_device *pdev,
+ struct efm32_uart_port *efm_port)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 location;
+ int ret;
+
+ if (!np)
+ return 1;
+
+ 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");
+ return -EINVAL;
+ }
+ efm_debug(efm_port, "using location %u\n", location);
+ efm_port->pdata.location = location;
+ } else {
+ efm_debug(efm_port, "fall back to location 0\n");
+ }
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
+ return ret;
+ } else {
+ efm_port->port.line = ret;
+ return 0;
+ }
+
+}
+
+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);
+ if (!efm_port) {
+ dev_dbg(&pdev->dev, "failed to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "failed to determine base address\n");
+ goto err_get_base;
+ }
+
+ if (resource_size(res) < 60) {
+ ret = -EINVAL;
+ dev_dbg(&pdev->dev, "memory resource too small\n");
+ goto err_too_small;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
+ dev_dbg(&pdev->dev, "failed to get rx irq\n");
+ goto err_get_rxirq;
+ }
+
+ efm_port->port.irq = ret;
+
+ ret = platform_get_irq(pdev, 1);
+ if (ret <= 0)
+ ret = efm_port->port.irq + 1;
+
+ efm_port->txirq = ret;
+
+ efm_port->port.dev = &pdev->dev;
+ efm_port->port.mapbase = res->start;
+ efm_port->port.type = PORT_EFMUART;
+ efm_port->port.iotype = UPIO_MEM32;
+ efm_port->port.fifosize = 2;
+ efm_port->port.ops = &efm32_uart_pops;
+ efm_port->port.flags = UPF_BOOT_AUTOCONF;
+
+ ret = efm32_uart_probe_dt(pdev, efm_port);
+ if (ret > 0) {
+ /* not created by device tree */
+ const struct efm32_uart_pdata *pdata = dev_get_platdata(&pdev->dev);
+
+ efm_port->port.line = pdev->id;
+
+ if (pdata)
+ efm_port->pdata = *pdata;
+ } else if (ret < 0)
+ goto err_probe_dt;
+
+ line = efm_port->port.line;
+
+ 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 (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:
+ kfree(efm_port);
+ } else {
+ platform_set_drvdata(pdev, efm_port);
+ dev_dbg(&pdev->dev, "\\o/\n");
+ }
+
+ return ret;
+}
+
+static int efm32_uart_remove(struct platform_device *pdev)
+{
+ struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
+ unsigned int line = efm_port->port.line;
+
+ uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
+
+ if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports))
+ efm32_uart_ports[line] = NULL;
+
+ kfree(efm_port);
+
+ return 0;
+}
+
+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 */
+ }
+};
+MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids);
+
+static struct platform_driver efm32_uart_driver = {
+ .probe = efm32_uart_probe,
+ .remove = efm32_uart_remove,
+
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = efm32_uart_dt_ids,
+ },
+};
+
+static int __init efm32_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&efm32_uart_reg);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&efm32_uart_driver);
+ if (ret)
+ uart_unregister_driver(&efm32_uart_reg);
+
+ pr_info("EFM32 UART/USART driver\n");
+
+ return ret;
+}
+module_init(efm32_uart_init);
+
+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");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
new file mode 100644
index 00000000000..49385c86cfb
--- /dev/null
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -0,0 +1,1285 @@
+/*
+ * Freescale lpuart serial port driver
+ *
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ * 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_FSL_LPUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#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/module.h>
+#include <linux/of.h>
+#include <linux/of_device.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 */
+#define UARTBDH 0x00
+#define UARTBDL 0x01
+#define UARTCR1 0x02
+#define UARTCR2 0x03
+#define UARTSR1 0x04
+#define UARTCR3 0x06
+#define UARTDR 0x07
+#define UARTCR4 0x0a
+#define UARTCR5 0x0b
+#define UARTMODEM 0x0d
+#define UARTPFIFO 0x10
+#define UARTCFIFO 0x11
+#define UARTSFIFO 0x12
+#define UARTTWFIFO 0x13
+#define UARTTCFIFO 0x14
+#define UARTRWFIFO 0x15
+
+#define UARTBDH_LBKDIE 0x80
+#define UARTBDH_RXEDGIE 0x40
+#define UARTBDH_SBR_MASK 0x1f
+
+#define UARTCR1_LOOPS 0x80
+#define UARTCR1_RSRC 0x20
+#define UARTCR1_M 0x10
+#define UARTCR1_WAKE 0x08
+#define UARTCR1_ILT 0x04
+#define UARTCR1_PE 0x02
+#define UARTCR1_PT 0x01
+
+#define UARTCR2_TIE 0x80
+#define UARTCR2_TCIE 0x40
+#define UARTCR2_RIE 0x20
+#define UARTCR2_ILIE 0x10
+#define UARTCR2_TE 0x08
+#define UARTCR2_RE 0x04
+#define UARTCR2_RWU 0x02
+#define UARTCR2_SBK 0x01
+
+#define UARTSR1_TDRE 0x80
+#define UARTSR1_TC 0x40
+#define UARTSR1_RDRF 0x20
+#define UARTSR1_IDLE 0x10
+#define UARTSR1_OR 0x08
+#define UARTSR1_NF 0x04
+#define UARTSR1_FE 0x02
+#define UARTSR1_PE 0x01
+
+#define UARTCR3_R8 0x80
+#define UARTCR3_T8 0x40
+#define UARTCR3_TXDIR 0x20
+#define UARTCR3_TXINV 0x10
+#define UARTCR3_ORIE 0x08
+#define UARTCR3_NEIE 0x04
+#define UARTCR3_FEIE 0x02
+#define UARTCR3_PEIE 0x01
+
+#define UARTCR4_MAEN1 0x80
+#define UARTCR4_MAEN2 0x40
+#define UARTCR4_M10 0x20
+#define UARTCR4_BRFA_MASK 0x1f
+#define UARTCR4_BRFA_OFF 0
+
+#define UARTCR5_TDMAS 0x80
+#define UARTCR5_RDMAS 0x20
+
+#define UARTMODEM_RXRTSE 0x08
+#define UARTMODEM_TXRTSPOL 0x04
+#define UARTMODEM_TXRTSE 0x02
+#define UARTMODEM_TXCTSE 0x01
+
+#define UARTPFIFO_TXFE 0x80
+#define UARTPFIFO_FIFOSIZE_MASK 0x7
+#define UARTPFIFO_TXSIZE_OFF 4
+#define UARTPFIFO_RXFE 0x08
+#define UARTPFIFO_RXSIZE_OFF 0
+
+#define UARTCFIFO_TXFLUSH 0x80
+#define UARTCFIFO_RXFLUSH 0x40
+#define UARTCFIFO_RXOFE 0x04
+#define UARTCFIFO_TXOFE 0x02
+#define UARTCFIFO_RXUFE 0x01
+
+#define UARTSFIFO_TXEMPT 0x80
+#define UARTSFIFO_RXEMPT 0x40
+#define UARTSFIFO_RXOF 0x04
+#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
+
+struct lpuart_port {
+ struct uart_port 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[] = {
+ {
+ .compatible = "fsl,vf610-lpuart",
+ },
+ { /* sentinel */ }
+};
+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;
+
+ temp = readb(port->membase + UARTCR2);
+ temp &= ~(UARTCR2_TIE | UARTCR2_TCIE);
+ writeb(temp, port->membase + UARTCR2);
+}
+
+static void lpuart_stop_rx(struct uart_port *port)
+{
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTCR2);
+ writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2);
+}
+
+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;
+
+ 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))
+ lpuart_stop_tx(&sport->port);
+}
+
+static void lpuart_start_tx(struct uart_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 (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)
+{
+ struct lpuart_port *sport = dev_id;
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (sport->port.x_char) {
+ writeb(sport->port.x_char, sport->port.membase + UARTDR);
+ goto out;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+ lpuart_stop_tx(&sport->port);
+ goto out;
+ }
+
+ lpuart_transmit_buffer(sport);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+
+out:
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lpuart_rxint(int irq, void *dev_id)
+{
+ struct lpuart_port *sport = dev_id;
+ unsigned int flg, ignored = 0;
+ struct tty_port *port = &sport->port.state->port;
+ unsigned long flags;
+ unsigned char rx, sr;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) {
+ flg = TTY_NORMAL;
+ sport->port.icount.rx++;
+ /*
+ * to clear the FE, OR, NF, FE, PE flags,
+ * read SR1 then read DR
+ */
+ sr = readb(sport->port.membase + UARTSR1);
+ rx = readb(sport->port.membase + UARTDR);
+
+ if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
+ continue;
+
+ if (sr & (UARTSR1_PE | UARTSR1_OR | UARTSR1_FE)) {
+ if (sr & UARTSR1_PE)
+ sport->port.icount.parity++;
+ else if (sr & UARTSR1_FE)
+ sport->port.icount.frame++;
+
+ if (sr & UARTSR1_OR)
+ sport->port.icount.overrun++;
+
+ if (sr & sport->port.ignore_status_mask) {
+ if (++ignored > 100)
+ goto out;
+ continue;
+ }
+
+ sr &= sport->port.read_status_mask;
+
+ if (sr & UARTSR1_PE)
+ flg = TTY_PARITY;
+ else if (sr & UARTSR1_FE)
+ flg = TTY_FRAME;
+
+ if (sr & UARTSR1_OR)
+ flg = TTY_OVERRUN;
+
+#ifdef SUPPORT_SYSRQ
+ sport->port.sysrq = 0;
+#endif
+ }
+
+ tty_insert_flip_char(port, rx, flg);
+ }
+
+out:
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ tty_flip_buffer_push(port);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lpuart_int(int irq, void *dev_id)
+{
+ struct lpuart_port *sport = dev_id;
+ unsigned char sts;
+
+ sts = readb(sport->port.membase + UARTSR1);
+
+ 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)) {
+ if (sport->lpuart_dma_use)
+ lpuart_pio_tx(sport);
+ else
+ lpuart_txint(irq, dev_id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* return TIOCSER_TEMT when transmitter is not busy */
+static unsigned int lpuart_tx_empty(struct uart_port *port)
+{
+ return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
+ TIOCSER_TEMT : 0;
+}
+
+static unsigned int lpuart_get_mctrl(struct uart_port *port)
+{
+ unsigned int temp = 0;
+ unsigned char reg;
+
+ reg = readb(port->membase + UARTMODEM);
+ if (reg & UARTMODEM_TXCTSE)
+ temp |= TIOCM_CTS;
+
+ if (reg & UARTMODEM_RXRTSE)
+ temp |= TIOCM_RTS;
+
+ return temp;
+}
+
+static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTMODEM) &
+ ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
+
+ if (mctrl & TIOCM_RTS)
+ temp |= UARTMODEM_RXRTSE;
+
+ if (mctrl & TIOCM_CTS)
+ temp |= UARTMODEM_TXCTSE;
+
+ writeb(temp, port->membase + UARTMODEM);
+}
+
+static void lpuart_break_ctl(struct uart_port *port, int break_state)
+{
+ unsigned char temp;
+
+ temp = readb(port->membase + UARTCR2) & ~UARTCR2_SBK;
+
+ if (break_state != 0)
+ temp |= UARTCR2_SBK;
+
+ writeb(temp, port->membase + UARTCR2);
+}
+
+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);
+
+ /* determine FIFO size and enable FIFO mode */
+ val = readb(sport->port.membase + UARTPFIFO);
+
+ sport->txfifo_size = 0x1 << (((val >> UARTPFIFO_TXSIZE_OFF) &
+ UARTPFIFO_FIFOSIZE_MASK) + 1);
+
+ sport->rxfifo_size = 0x1 << (((val >> UARTPFIFO_RXSIZE_OFF) &
+ UARTPFIFO_FIFOSIZE_MASK) + 1);
+
+ writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
+ sport->port.membase + UARTPFIFO);
+
+ /* flush Tx and Rx FIFO */
+ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
+ sport->port.membase + UARTCFIFO);
+
+ 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)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ int ret;
+ 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)
+ return ret;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ lpuart_setup_watermark(sport);
+
+ temp = readb(sport->port.membase + UARTCR2);
+ temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
+ writeb(temp, sport->port.membase + UARTCR2);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ return 0;
+}
+
+static void lpuart_shutdown(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ unsigned char temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* disable Rx/Tx and interrupts */
+ temp = readb(port->membase + UARTCR2);
+ temp &= ~(UARTCR2_TE | UARTCR2_RE |
+ UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE);
+ writeb(temp, port->membase + UARTCR2);
+
+ 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
+lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ unsigned long flags;
+ unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+ unsigned int baud;
+ unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+ unsigned int sbr, brfa;
+
+ cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
+ old_cr2 = readb(sport->port.membase + UARTCR2);
+ cr4 = readb(sport->port.membase + UARTCR4);
+ bdh = readb(sport->port.membase + UARTBDH);
+ modem = readb(sport->port.membase + UARTMODEM);
+ /*
+ * only support CS8 and CS7, and for CS7 must enable PE.
+ * supported mode:
+ * - (7,e/o,1)
+ * - (8,n,1)
+ * - (8,m/s,1)
+ * - (8,e/o,1)
+ */
+ while ((termios->c_cflag & CSIZE) != CS8 &&
+ (termios->c_cflag & CSIZE) != CS7) {
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= old_csize;
+ old_csize = CS8;
+ }
+
+ if ((termios->c_cflag & CSIZE) == CS8 ||
+ (termios->c_cflag & CSIZE) == CS7)
+ cr1 = old_cr1 & ~UARTCR1_M;
+
+ if (termios->c_cflag & CMSPAR) {
+ if ((termios->c_cflag & CSIZE) != CS8) {
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= CS8;
+ }
+ cr1 |= UARTCR1_M;
+ }
+
+ if (termios->c_cflag & CRTSCTS) {
+ modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
+ } else {
+ termios->c_cflag &= ~CRTSCTS;
+ modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ termios->c_cflag &= ~CSTOPB;
+
+ /* parity must be enabled when CS7 to match 8-bits format */
+ if ((termios->c_cflag & CSIZE) == CS7)
+ termios->c_cflag |= PARENB;
+
+ if ((termios->c_cflag & PARENB)) {
+ if (termios->c_cflag & CMSPAR) {
+ cr1 &= ~UARTCR1_PE;
+ cr1 |= UARTCR1_M;
+ } else {
+ cr1 |= UARTCR1_PE;
+ if ((termios->c_cflag & CSIZE) == CS8)
+ cr1 |= UARTCR1_M;
+ if (termios->c_cflag & PARODD)
+ cr1 |= UARTCR1_PT;
+ else
+ cr1 &= ~UARTCR1_PT;
+ }
+ }
+
+ /* ask the core to calculate the divisor */
+ baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ sport->port.read_status_mask = 0;
+ if (termios->c_iflag & INPCK)
+ sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE);
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+ sport->port.read_status_mask |= UARTSR1_FE;
+
+ /* characters to ignore */
+ sport->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ sport->port.ignore_status_mask |= UARTSR1_PE;
+ if (termios->c_iflag & IGNBRK) {
+ sport->port.ignore_status_mask |= UARTSR1_FE;
+ /*
+ * if we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ sport->port.ignore_status_mask |= UARTSR1_OR;
+ }
+
+ /* update the per-port timeout */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ /* wait transmit engin complete */
+ while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
+ barrier();
+
+ /* disable transmit and receive */
+ writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE),
+ sport->port.membase + UARTCR2);
+
+ sbr = sport->port.uartclk / (16 * baud);
+ brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2) / baud;
+ bdh &= ~UARTBDH_SBR_MASK;
+ bdh |= (sbr >> 8) & 0x1F;
+ cr4 &= ~UARTCR4_BRFA_MASK;
+ brfa &= UARTCR4_BRFA_MASK;
+ writeb(cr4 | brfa, sport->port.membase + UARTCR4);
+ writeb(bdh, sport->port.membase + UARTBDH);
+ writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+ writeb(cr1, sport->port.membase + UARTCR1);
+ writeb(modem, sport->port.membase + UARTMODEM);
+
+ /* restore control register */
+ writeb(old_cr2, sport->port.membase + UARTCR2);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *lpuart_type(struct uart_port *port)
+{
+ return "FSL_LPUART";
+}
+
+static void lpuart_release_port(struct uart_port *port)
+{
+ /* nothing to do */
+}
+
+static int lpuart_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/* configure/autoconfigure the port */
+static void lpuart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_LPUART;
+}
+
+static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ int ret = 0;
+
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_LPUART)
+ ret = -EINVAL;
+ if (port->irq != ser->irq)
+ ret = -EINVAL;
+ if (ser->io_type != UPIO_MEM)
+ ret = -EINVAL;
+ if (port->uartclk / 16 != ser->baud_base)
+ ret = -EINVAL;
+ if (port->iobase != ser->port)
+ ret = -EINVAL;
+ if (ser->hub6 != 0)
+ ret = -EINVAL;
+ return ret;
+}
+
+static struct uart_ops lpuart_pops = {
+ .tx_empty = lpuart_tx_empty,
+ .set_mctrl = lpuart_set_mctrl,
+ .get_mctrl = lpuart_get_mctrl,
+ .stop_tx = lpuart_stop_tx,
+ .start_tx = lpuart_start_tx,
+ .stop_rx = lpuart_stop_rx,
+ .enable_ms = lpuart_enable_ms,
+ .break_ctl = lpuart_break_ctl,
+ .startup = lpuart_startup,
+ .shutdown = lpuart_shutdown,
+ .set_termios = lpuart_set_termios,
+ .type = lpuart_type,
+ .request_port = lpuart_request_port,
+ .release_port = lpuart_release_port,
+ .config_port = lpuart_config_port,
+ .verify_port = lpuart_verify_port,
+};
+
+static struct lpuart_port *lpuart_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE
+static void lpuart_console_putchar(struct uart_port *port, int ch)
+{
+ while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE))
+ barrier();
+
+ writeb(ch, port->membase + UARTDR);
+}
+
+static void
+lpuart_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct lpuart_port *sport = lpuart_ports[co->index];
+ unsigned char old_cr2, cr2;
+
+ /* first save CR2 and then disable interrupts */
+ cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
+ cr2 |= (UARTCR2_TE | UARTCR2_RE);
+ cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE);
+ writeb(cr2, sport->port.membase + UARTCR2);
+
+ uart_console_write(&sport->port, s, count, lpuart_console_putchar);
+
+ /* wait for transmitter finish complete and restore CR2 */
+ while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
+ barrier();
+
+ writeb(old_cr2, sport->port.membase + UARTCR2);
+}
+
+/*
+ * if the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+lpuart_console_get_options(struct lpuart_port *sport, int *baud,
+ int *parity, int *bits)
+{
+ unsigned char cr, bdh, bdl, brfa;
+ unsigned int sbr, uartclk, baud_raw;
+
+ cr = readb(sport->port.membase + UARTCR2);
+ cr &= UARTCR2_TE | UARTCR2_RE;
+ if (!cr)
+ return;
+
+ /* ok, the port was enabled */
+
+ cr = readb(sport->port.membase + UARTCR1);
+
+ *parity = 'n';
+ if (cr & UARTCR1_PE) {
+ if (cr & UARTCR1_PT)
+ *parity = 'o';
+ else
+ *parity = 'e';
+ }
+
+ if (cr & UARTCR1_M)
+ *bits = 9;
+ else
+ *bits = 8;
+
+ bdh = readb(sport->port.membase + UARTBDH);
+ bdh &= UARTBDH_SBR_MASK;
+ bdl = readb(sport->port.membase + UARTBDL);
+ sbr = bdh;
+ sbr <<= 8;
+ sbr |= bdl;
+ brfa = readb(sport->port.membase + UARTCR4);
+ brfa &= UARTCR4_BRFA_MASK;
+
+ uartclk = clk_get_rate(sport->clk);
+ /*
+ * baud = mod_clk/(16*(sbr[13]+(brfa)/32)
+ */
+ baud_raw = uartclk / (16 * (sbr + brfa / 32));
+
+ if (*baud != baud_raw)
+ printk(KERN_INFO "Serial: Console lpuart rounded baud rate"
+ "from %d to %d\n", baud_raw, *baud);
+}
+
+static int __init lpuart_console_setup(struct console *co, char *options)
+{
+ struct lpuart_port *sport;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ /*
+ * check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index == -1 || co->index >= ARRAY_SIZE(lpuart_ports))
+ co->index = 0;
+
+ sport = lpuart_ports[co->index];
+ if (sport == NULL)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ lpuart_console_get_options(sport, &baud, &parity, &bits);
+
+ lpuart_setup_watermark(sport);
+
+ return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver lpuart_reg;
+static struct console lpuart_console = {
+ .name = DEV_NAME,
+ .write = lpuart_console_write,
+ .device = uart_console_device,
+ .setup = lpuart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &lpuart_reg,
+};
+
+#define LPUART_CONSOLE (&lpuart_console)
+#else
+#define LPUART_CONSOLE NULL
+#endif
+
+static struct uart_driver lpuart_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = DEV_NAME,
+ .nr = ARRAY_SIZE(lpuart_ports),
+ .cons = LPUART_CONSOLE,
+};
+
+static int lpuart_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct lpuart_port *sport;
+ struct resource *res;
+ int ret;
+
+ sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
+ if (!sport)
+ return -ENOMEM;
+
+ pdev->dev.coherent_dma_mask = 0;
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
+ }
+ sport->port.line = ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ sport->port.mapbase = res->start;
+ sport->port.membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sport->port.membase))
+ return PTR_ERR(sport->port.membase);
+
+ sport->port.dev = &pdev->dev;
+ sport->port.type = PORT_LPUART;
+ sport->port.iotype = UPIO_MEM;
+ sport->port.irq = platform_get_irq(pdev, 0);
+ sport->port.ops = &lpuart_pops;
+ sport->port.flags = UPF_BOOT_AUTOCONF;
+
+ sport->clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(sport->clk)) {
+ ret = PTR_ERR(sport->clk);
+ dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sport->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret);
+ return ret;
+ }
+
+ sport->port.uartclk = clk_get_rate(sport->clk);
+
+ lpuart_ports[sport->port.line] = sport;
+
+ platform_set_drvdata(pdev, &sport->port);
+
+ ret = uart_add_one_port(&lpuart_reg, &sport->port);
+ if (ret) {
+ clk_disable_unprepare(sport->clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lpuart_remove(struct platform_device *pdev)
+{
+ struct lpuart_port *sport = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&lpuart_reg, &sport->port);
+
+ clk_disable_unprepare(sport->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int lpuart_suspend(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+
+ uart_suspend_port(&lpuart_reg, &sport->port);
+
+ return 0;
+}
+
+static int lpuart_resume(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+
+ uart_resume_port(&lpuart_reg, &sport->port);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume);
+
+static struct platform_driver lpuart_driver = {
+ .probe = lpuart_probe,
+ .remove = lpuart_remove,
+ .driver = {
+ .name = "fsl-lpuart",
+ .owner = THIS_MODULE,
+ .of_match_table = lpuart_dt_ids,
+ .pm = &lpuart_pm_ops,
+ },
+};
+
+static int __init lpuart_serial_init(void)
+{
+ int ret;
+
+ pr_info("serial: Freescale lpuart driver\n");
+
+ ret = uart_register_driver(&lpuart_reg);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&lpuart_driver);
+ if (ret)
+ uart_unregister_driver(&lpuart_reg);
+
+ return ret;
+}
+
+static void __exit lpuart_serial_exit(void)
+{
+ platform_driver_unregister(&lpuart_driver);
+ uart_unregister_driver(&lpuart_reg);
+}
+
+module_init(lpuart_serial_init);
+module_exit(lpuart_serial_exit);
+
+MODULE_DESCRIPTION("Freescale lpuart serial port driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index d55709a7a75..67423805e6d 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -52,7 +52,6 @@
#include <linux/firmware.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -106,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},
@@ -114,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},
@@ -176,7 +175,7 @@ static void free_port_memory(struct icom_port *icom_port)
}
}
-static int __devinit get_port_memory(struct icom_port *icom_port)
+static int get_port_memory(struct icom_port *icom_port)
{
int index;
unsigned long stgAddr;
@@ -298,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);
}
@@ -329,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);
}
@@ -454,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
@@ -506,7 +507,7 @@ static void load_code(struct icom_port *icom_port)
/* Stop processor */
stop_processor(icom_port);
- dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n");
+ dev_err(&icom_port->adapter->pci_dev->dev,"Port not operational\n");
}
if (new_page != NULL)
@@ -558,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
@@ -567,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;
}
@@ -596,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);
/*
@@ -735,7 +741,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
{
short int count, rcv_buff;
- struct tty_struct *tty = icom_port->uart_port.state->port.tty;
+ struct tty_port *port = &icom_port->uart_port.state->port;
unsigned short int status;
struct uart_icount *icount;
unsigned long offset;
@@ -762,7 +768,7 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
/* Block copy all but the last byte as this may have status */
if (count > 0) {
first = icom_port->recv_buf[offset];
- tty_insert_flip_string(tty, icom_port->recv_buf + offset, count - 1);
+ tty_insert_flip_string(port, icom_port->recv_buf + offset, count - 1);
}
icount = &icom_port->uart_port.icount;
@@ -813,7 +819,7 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
}
- tty_insert_flip_char(tty, *(icom_port->recv_buf + offset + count - 1), flag);
+ tty_insert_flip_char(port, *(icom_port->recv_buf + offset + count - 1), flag);
if (status & SA_FLAGS_OVERRUN)
/*
@@ -821,7 +827,7 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
* reported immediately, and doesn't
* affect the current character
*/
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
ignore_char:
icom_port->statStg->rcv[rcv_buff].flags = 0;
icom_port->statStg->rcv[rcv_buff].leLength = 0;
@@ -835,7 +841,10 @@ ignore_char:
status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
}
icom_port->next_rcv = rcv_buff;
- tty_flip_buffer_push(tty);
+
+ 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,
@@ -1088,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);
@@ -1315,7 +1323,7 @@ static struct uart_driver icom_uart_driver = {
.cons = ICOM_CONSOLE,
};
-static int __devinit icom_init_ports(struct icom_adapter *icom_adapter)
+static int icom_init_ports(struct icom_adapter *icom_adapter)
{
u32 subsystem_id = icom_adapter->subsystem_id;
int i;
@@ -1382,7 +1390,7 @@ static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *i
0x8024 + 2 - 2 * (icom_port->port - 2);
}
}
-static int __devinit icom_load_ports(struct icom_adapter *icom_adapter)
+static int icom_load_ports(struct icom_adapter *icom_adapter)
{
struct icom_port *icom_port;
int port_num;
@@ -1408,7 +1416,7 @@ static int __devinit icom_load_ports(struct icom_adapter *icom_adapter)
return 0;
}
-static int __devinit icom_alloc_adapter(struct icom_adapter
+static int icom_alloc_adapter(struct icom_adapter
**icom_adapter_ref)
{
int adapter_count = 0;
@@ -1416,8 +1424,7 @@ static int __devinit icom_alloc_adapter(struct icom_adapter
struct icom_adapter *cur_adapter_entry;
struct list_head *tmp;
- icom_adapter = (struct icom_adapter *)
- kzalloc(sizeof(struct icom_adapter), GFP_KERNEL);
+ icom_adapter = kzalloc(sizeof(struct icom_adapter), GFP_KERNEL);
if (!icom_adapter) {
return -ENOMEM;
@@ -1488,7 +1495,7 @@ static void icom_kref_release(struct kref *kref)
icom_remove_adapter(icom_adapter);
}
-static int __devinit icom_probe(struct pci_dev *dev,
+static int icom_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
int index;
@@ -1569,7 +1576,7 @@ static int __devinit 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 =
@@ -1597,7 +1604,7 @@ probe_exit0:
return retval;
}
-static void __devexit icom_remove(struct pci_dev *dev)
+static void icom_remove(struct pci_dev *dev)
{
struct icom_adapter *icom_adapter;
struct list_head *tmp;
@@ -1618,7 +1625,7 @@ static struct pci_driver icom_pci_driver = {
.name = ICOM_DRIVER_NAME,
.id_table = icom_pci_table,
.probe = icom_probe,
- .remove = __devexit_p(icom_remove),
+ .remove = icom_remove,
};
static int __init icom_init(void)
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index 7e925e20cba..59039097099 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -60,20 +60,27 @@
#include <linux/pm_runtime.h>
#include <linux/spi/ifx_modem.h>
#include <linux/delay.h>
+#include <linux/reboot.h>
#include "ifx6x60.h"
#define IFX_SPI_MORE_MASK 0x10
-#define IFX_SPI_MORE_BIT 12 /* bit position in u16 */
-#define IFX_SPI_CTS_BIT 13 /* bit position in u16 */
+#define IFX_SPI_MORE_BIT 4 /* bit position in u8 */
+#define IFX_SPI_CTS_BIT 6 /* bit position in u8 */
#define IFX_SPI_MODE SPI_MODE_1
#define IFX_SPI_TTY_ID 0
#define IFX_SPI_TIMEOUT_SEC 2
#define IFX_SPI_HEADER_0 (-1)
#define IFX_SPI_HEADER_F (-2)
+#define PO_POST_DELAY 200
+#define IFX_MDM_RST_PMU 4
+
/* forward reference */
static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev);
+static int ifx_modem_reboot_callback(struct notifier_block *nfb,
+ unsigned long event, void *data);
+static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev);
/* local variables */
static int spi_bpw = 16; /* 8, 16 or 32 bit word length */
@@ -81,6 +88,29 @@ static struct tty_driver *tty_drv;
static struct ifx_spi_device *saved_ifx_dev;
static struct lock_class_key ifx_spi_key;
+static struct notifier_block ifx_modem_reboot_notifier_block = {
+ .notifier_call = ifx_modem_reboot_callback,
+};
+
+static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev)
+{
+ gpio_set_value(IFX_MDM_RST_PMU, 1);
+ msleep(PO_POST_DELAY);
+
+ return 0;
+}
+
+static int ifx_modem_reboot_callback(struct notifier_block *nfb,
+ unsigned long event, void *data)
+{
+ if (saved_ifx_dev)
+ ifx_modem_power_off(saved_ifx_dev);
+ else
+ pr_warn("no ifx modem active;\n");
+
+ return NOTIFY_OK;
+}
+
/* GPIO/GPE settings */
/**
@@ -152,26 +182,67 @@ ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val)
}
/**
- * swap_buf
+ * swap_buf_8
* @buf: our buffer
* @len : number of bytes (not words) in the buffer
* @end: end of buffer
*
* Swap the contents of a buffer into big endian format
*/
-static inline void swap_buf(u16 *buf, int len, void *end)
+static inline void swap_buf_8(unsigned char *buf, int len, void *end)
+{
+ /* don't swap buffer if SPI word width is 8 bits */
+ return;
+}
+
+/**
+ * swap_buf_16
+ * @buf: our buffer
+ * @len : number of bytes (not words) in the buffer
+ * @end: end of buffer
+ *
+ * Swap the contents of a buffer into big endian format
+ */
+static inline void swap_buf_16(unsigned char *buf, int len, void *end)
{
int n;
+ u16 *buf_16 = (u16 *)buf;
len = ((len + 1) >> 1);
- if ((void *)&buf[len] > end) {
- pr_err("swap_buf: swap exceeds boundary (%p > %p)!",
- &buf[len], end);
+ if ((void *)&buf_16[len] > end) {
+ pr_err("swap_buf_16: swap exceeds boundary (%p > %p)!",
+ &buf_16[len], end);
return;
}
for (n = 0; n < len; n++) {
- *buf = cpu_to_be16(*buf);
- buf++;
+ *buf_16 = cpu_to_be16(*buf_16);
+ buf_16++;
+ }
+}
+
+/**
+ * swap_buf_32
+ * @buf: our buffer
+ * @len : number of bytes (not words) in the buffer
+ * @end: end of buffer
+ *
+ * Swap the contents of a buffer into big endian format
+ */
+static inline void swap_buf_32(unsigned char *buf, int len, void *end)
+{
+ int n;
+
+ u32 *buf_32 = (u32 *)buf;
+ len = (len + 3) >> 2;
+
+ if ((void *)&buf_32[len] > end) {
+ pr_err("swap_buf_32: swap exceeds boundary (%p > %p)!\n",
+ &buf_32[len], end);
+ return;
+ }
+ for (n = 0; n < len; n++) {
+ *buf_32 = cpu_to_be32(*buf_32);
+ buf_32++;
}
}
@@ -190,9 +261,7 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev)
if (!val) {
if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING,
&ifx_dev->flags)) {
- ifx_dev->spi_timer.expires =
- jiffies + IFX_SPI_TIMEOUT_SEC*HZ;
- add_timer(&ifx_dev->spi_timer);
+ mod_timer(&ifx_dev->spi_timer,jiffies + IFX_SPI_TIMEOUT_SEC*HZ);
}
}
@@ -201,23 +270,6 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev)
}
/**
- * ifx_spi_hangup - hang up an IFX device
- * @ifx_dev: our SPI device
- *
- * Hang up the tty attached to the IFX device if one is currently
- * open. If not take no action
- */
-static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev)
-{
- struct tty_port *pport = &ifx_dev->tty_port;
- struct tty_struct *tty = tty_port_tty_get(pport);
- if (tty) {
- tty_hangup(tty);
- tty_kref_put(tty);
- }
-}
-
-/**
* ifx_spi_timeout - SPI timeout
* @arg: our SPI device
*
@@ -229,7 +281,7 @@ static void ifx_spi_timeout(unsigned long arg)
struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg;
dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***");
- ifx_spi_ttyhangup(ifx_dev);
+ tty_port_tty_hangup(&ifx_dev->tty_port, false);
mrdy_set_low(ifx_dev);
clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
}
@@ -374,25 +426,6 @@ static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count,
}
/**
- * ifx_spi_wakeup_serial - SPI space made
- * @port_data: our SPI device
- *
- * We have emptied the FIFO enough that we want to get more data
- * queued into it. Poke the line discipline via tty_wakeup so that
- * it will feed us more bits
- */
-static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev)
-{
- struct tty_struct *tty;
-
- tty = tty_port_tty_get(&ifx_dev->tty_port);
- if (!tty)
- return;
- tty_wakeup(tty);
- tty_kref_put(tty);
-}
-
-/**
* ifx_spi_prepare_tx_buffer - prepare transmit frame
* @ifx_dev: our SPI device
*
@@ -412,7 +445,6 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
unsigned char *tx_buffer;
tx_buffer = ifx_dev->tx_buffer;
- memset(tx_buffer, 0, IFX_SPI_TRANSFER_SIZE);
/* make room for required SPI header */
tx_buffer += IFX_SPI_HEADER_OVERHEAD;
@@ -438,7 +470,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
tx_count += temp_count;
if (temp_count == queue_length)
/* poke port to get more data */
- ifx_spi_wakeup_serial(ifx_dev);
+ tty_port_tty_wakeup(&ifx_dev->tty_port);
else /* more data in port, use next SPI message */
ifx_dev->spi_more = 1;
}
@@ -449,7 +481,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
tx_count-IFX_SPI_HEADER_OVERHEAD,
ifx_dev->spi_more);
/* swap actual data in the buffer */
- swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count,
+ ifx_dev->swap_buf((ifx_dev->tx_buffer), tx_count,
&ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]);
return tx_count;
}
@@ -469,9 +501,17 @@ static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf,
{
struct ifx_spi_device *ifx_dev = tty->driver_data;
unsigned char *tmp_buf = (unsigned char *)buf;
- int tx_count = kfifo_in_locked(&ifx_dev->tx_fifo, tmp_buf, count,
- &ifx_dev->fifo_lock);
- mrdy_assert(ifx_dev);
+ unsigned long flags;
+ bool is_fifo_empty;
+ int tx_count;
+
+ spin_lock_irqsave(&ifx_dev->fifo_lock, flags);
+ is_fifo_empty = kfifo_is_empty(&ifx_dev->tx_fifo);
+ tx_count = kfifo_in(&ifx_dev->tx_fifo, tmp_buf, count);
+ spin_unlock_irqrestore(&ifx_dev->fifo_lock, flags);
+ if (is_fifo_empty)
+ mrdy_assert(ifx_dev);
+
return tx_count;
}
@@ -530,11 +570,18 @@ static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty)
/* clear any old data; can't do this in 'close' */
kfifo_reset(&ifx_dev->tx_fifo);
+ /* clear any flag which may be set in port shutdown procedure */
+ clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags);
+ clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags);
+
/* put port data into this tty */
tty->driver_data = ifx_dev;
/* allows flip string push from int context */
- tty->low_latency = 1;
+ port->low_latency = 1;
+
+ /* set flag to allows data transfer */
+ set_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags);
return 0;
}
@@ -551,7 +598,9 @@ static void ifx_port_shutdown(struct tty_port *port)
struct ifx_spi_device *ifx_dev =
container_of(port, struct ifx_spi_device, tty_port);
+ clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags);
mrdy_set_low(ifx_dev);
+ del_timer(&ifx_dev->spi_timer);
clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
tasklet_kill(&ifx_dev->io_work_tasklet);
}
@@ -584,12 +633,8 @@ static const struct tty_operations ifx_spi_serial_ops = {
static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev,
unsigned char *chars, size_t size)
{
- struct tty_struct *tty = tty_port_tty_get(&ifx_dev->tty_port);
- if (!tty)
- return;
- tty_insert_flip_string(tty, chars, size);
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_insert_flip_string(&ifx_dev->tty_port, chars, size);
+ tty_flip_buffer_push(&ifx_dev->tty_port);
}
/**
@@ -602,8 +647,6 @@ static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev,
static void ifx_spi_complete(void *ctx)
{
struct ifx_spi_device *ifx_dev = ctx;
- struct tty_struct *tty;
- struct tty_ldisc *ldisc = NULL;
int length;
int actual_length;
unsigned char more;
@@ -617,7 +660,7 @@ static void ifx_spi_complete(void *ctx)
if (!ifx_dev->spi_msg.status) {
/* check header validity, get comm flags */
- swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
+ ifx_dev->swap_buf(ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
&ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]);
decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer,
&length, &more, &cts);
@@ -636,7 +679,8 @@ static void ifx_spi_complete(void *ctx)
actual_length = min((unsigned int)length,
ifx_dev->spi_msg.actual_length);
- swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD),
+ ifx_dev->swap_buf(
+ (ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD),
actual_length,
&ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]);
ifx_spi_insert_flip_string(
@@ -680,15 +724,7 @@ complete_exit:
*/
ifx_spi_power_state_clear(ifx_dev,
IFX_SPI_POWER_DATA_PENDING);
- tty = tty_port_tty_get(&ifx_dev->tty_port);
- if (tty) {
- ldisc = tty_ldisc_ref(tty);
- if (ldisc) {
- ldisc->ops->write_wakeup(tty);
- tty_ldisc_deref(ldisc);
- }
- tty_kref_put(tty);
- }
+ tty_port_tty_wakeup(&ifx_dev->tty_port);
}
}
}
@@ -705,7 +741,8 @@ static void ifx_spi_io(unsigned long data)
int retval;
struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *) data;
- if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) {
+ if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags) &&
+ test_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags)) {
if (ifx_dev->gpio.unack_srdy_int_nb > 0)
ifx_dev->gpio.unack_srdy_int_nb--;
@@ -723,7 +760,8 @@ static void ifx_spi_io(unsigned long data)
ifx_dev->spi_xfer.cs_change = 0;
ifx_dev->spi_xfer.speed_hz = ifx_dev->spi_dev->max_speed_hz;
/* ifx_dev->spi_xfer.speed_hz = 390625; */
- ifx_dev->spi_xfer.bits_per_word = spi_bpw;
+ ifx_dev->spi_xfer.bits_per_word =
+ ifx_dev->spi_dev->bits_per_word;
ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer;
ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer;
@@ -773,6 +811,7 @@ static void ifx_spi_free_port(struct ifx_spi_device *ifx_dev)
{
if (ifx_dev->tty_dev)
tty_unregister_device(tty_drv, ifx_dev->minor);
+ tty_port_destroy(&ifx_dev->tty_port);
kfifo_free(&ifx_dev->tx_fifo);
}
@@ -800,16 +839,18 @@ static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev)
tty_port_init(pport);
pport->ops = &ifx_tty_port_ops;
ifx_dev->minor = IFX_SPI_TTY_ID;
- ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor,
- &ifx_dev->spi_dev->dev);
+ ifx_dev->tty_dev = tty_port_register_device(pport, tty_drv,
+ ifx_dev->minor, &ifx_dev->spi_dev->dev);
if (IS_ERR(ifx_dev->tty_dev)) {
dev_dbg(&ifx_dev->spi_dev->dev,
"%s: registering tty device failed", __func__);
ret = PTR_ERR(ifx_dev->tty_dev);
- goto error_ret;
+ goto error_port;
}
return 0;
+error_port:
+ tty_port_destroy(pport);
error_ret:
ifx_spi_free_port(ifx_dev);
return ret;
@@ -826,7 +867,7 @@ error_ret:
static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev)
{
if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) {
- del_timer_sync(&ifx_dev->spi_timer);
+ del_timer(&ifx_dev->spi_timer);
clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
}
@@ -875,7 +916,7 @@ static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev)
set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state);
if (!solreset) {
/* unsolicited reset */
- ifx_spi_ttyhangup(ifx_dev);
+ tty_port_tty_hangup(&ifx_dev->tty_port, false);
}
} else {
/* exited reset */
@@ -967,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;
@@ -1001,6 +1042,14 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
return -ENODEV;
}
+ /* init swap_buf function according to word width configuration */
+ if (spi->bits_per_word == 32)
+ ifx_dev->swap_buf = swap_buf_32;
+ else if (spi->bits_per_word == 16)
+ ifx_dev->swap_buf = swap_buf_16;
+ else
+ ifx_dev->swap_buf = swap_buf_8;
+
/* ensure SPI protocol flags are initialized to enable transfer */
ifx_dev->spi_more = 0;
ifx_dev->spi_slave_cts = 0;
@@ -1219,6 +1268,9 @@ static int ifx_spi_spi_remove(struct spi_device *spi)
static void ifx_spi_spi_shutdown(struct spi_device *spi)
{
+ struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
+
+ ifx_modem_power_off(ifx_dev);
}
/*
@@ -1227,30 +1279,6 @@ static void ifx_spi_spi_shutdown(struct spi_device *spi)
*/
/**
- * ifx_spi_spi_suspend - suspend SPI on system suspend
- * @dev: device being suspended
- *
- * Suspend the SPI side. No action needed on Intel MID platforms, may
- * need extending for other systems.
- */
-static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return 0;
-}
-
-/**
- * ifx_spi_spi_resume - resume SPI side on system resume
- * @dev: device being suspended
- *
- * Suspend the SPI side. No action needed on Intel MID platforms, may
- * need extending for other systems.
- */
-static int ifx_spi_spi_resume(struct spi_device *spi)
-{
- return 0;
-}
-
-/**
* ifx_spi_pm_suspend - suspend modem on system suspend
* @dev: device being suspended
*
@@ -1331,16 +1359,14 @@ static const struct spi_device_id ifx_id_table[] = {
MODULE_DEVICE_TABLE(spi, ifx_id_table);
/* spi operations */
-static const struct spi_driver ifx_spi_driver = {
+static struct spi_driver ifx_spi_driver = {
.driver = {
.name = DRVNAME,
.pm = &ifx_spi_pm,
.owner = THIS_MODULE},
.probe = ifx_spi_spi_probe,
.shutdown = ifx_spi_spi_shutdown,
- .remove = __devexit_p(ifx_spi_spi_remove),
- .suspend = ifx_spi_spi_suspend,
- .resume = ifx_spi_spi_resume,
+ .remove = ifx_spi_spi_remove,
.id_table = ifx_id_table
};
@@ -1354,7 +1380,9 @@ static void __exit ifx_spi_exit(void)
{
/* unregister */
tty_unregister_driver(tty_drv);
+ put_tty_driver(tty_drv);
spi_unregister_driver((void *)&ifx_spi_driver);
+ unregister_reboot_notifier(&ifx_modem_reboot_notifier_block);
}
/**
@@ -1375,12 +1403,9 @@ static int __init ifx_spi_init(void)
return -ENOMEM;
}
- tty_drv->magic = TTY_DRIVER_MAGIC;
- tty_drv->owner = THIS_MODULE;
tty_drv->driver_name = DRVNAME;
tty_drv->name = TTYNAME;
tty_drv->minor_start = IFX_SPI_TTY_ID;
- tty_drv->num = 1;
tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
tty_drv->subtype = SERIAL_TYPE_NORMAL;
tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
@@ -1392,16 +1417,31 @@ static int __init ifx_spi_init(void)
if (result) {
pr_err("%s: tty_register_driver failed(%d)",
DRVNAME, result);
- put_tty_driver(tty_drv);
- return result;
+ goto err_free_tty;
}
result = spi_register_driver((void *)&ifx_spi_driver);
if (result) {
pr_err("%s: spi_register_driver failed(%d)",
DRVNAME, result);
- tty_unregister_driver(tty_drv);
+ goto err_unreg_tty;
+ }
+
+ result = register_reboot_notifier(&ifx_modem_reboot_notifier_block);
+ if (result) {
+ pr_err("%s: register ifx modem reboot notifier failed(%d)",
+ DRVNAME, result);
+ goto err_unreg_spi;
}
+
+ return 0;
+err_unreg_spi:
+ spi_unregister_driver((void *)&ifx_spi_driver);
+err_unreg_tty:
+ tty_unregister_driver(tty_drv);
+err_free_tty:
+ put_tty_driver(tty_drv);
+
return result;
}
diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h
index e8464baf9e7..4fbddc29783 100644
--- a/drivers/tty/serial/ifx6x60.h
+++ b/drivers/tty/serial/ifx6x60.h
@@ -41,6 +41,7 @@
#define IFX_SPI_STATE_IO_IN_PROGRESS 1
#define IFX_SPI_STATE_IO_READY 2
#define IFX_SPI_STATE_TIMER_PENDING 3
+#define IFX_SPI_STATE_IO_AVAILABLE 4
/* flow control bitfields */
#define IFX_SPI_DCD 0
@@ -124,6 +125,7 @@ struct ifx_spi_device {
#define MR_INPROGRESS 1
#define MR_COMPLETE 2
wait_queue_head_t mdm_reset_wait;
+ void (*swap_buf)(unsigned char *buf, int len, void *end);
};
#endif /* _IFX6X60_H */
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 0b7fed746b2..044e86d528a 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -47,10 +47,12 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
-#include <asm/io.h>
#include <asm/irq.h>
-#include <mach/imx-uart.h>
+#include <linux/platform_data/serial-imx.h>
+#include <linux/platform_data/dma-imx.h>
/* Register definitions */
#define URXD0 0x0 /* Receiver Register */
@@ -72,103 +74,106 @@
#define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
/* UART Control Register Bit Fields.*/
-#define URXD_CHARRDY (1<<15)
-#define URXD_ERR (1<<14)
-#define URXD_OVRRUN (1<<13)
-#define URXD_FRMERR (1<<12)
-#define URXD_BRK (1<<11)
-#define URXD_PRERR (1<<10)
-#define UCR1_ADEN (1<<15) /* Auto detect interrupt */
-#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_RRDYEN (1<<9) /* Recv ready interrupt enable */
-#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
-#define UCR1_IREN (1<<7) /* Infrared interface enable */
-#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */
-#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
-#define UCR1_SNDBRK (1<<4) /* Send break */
-#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
-#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
-#define UCR1_DOZE (1<<1) /* Doze */
-#define UCR1_UARTEN (1<<0) /* UART enabled */
-#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
-#define UCR2_IRTS (1<<14) /* Ignore RTS pin */
-#define UCR2_CTSC (1<<13) /* CTS pin control */
-#define UCR2_CTS (1<<12) /* Clear to send */
-#define UCR2_ESCEN (1<<11) /* Escape enable */
-#define UCR2_PREN (1<<8) /* Parity enable */
-#define UCR2_PROE (1<<7) /* Parity odd/even */
-#define UCR2_STPB (1<<6) /* Stop */
-#define UCR2_WS (1<<5) /* Word size */
-#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */
-#define UCR2_ATEN (1<<3) /* Aging Timer Enable */
-#define UCR2_TXEN (1<<2) /* Transmitter enabled */
-#define UCR2_RXEN (1<<1) /* Receiver enabled */
-#define UCR2_SRST (1<<0) /* SW reset */
-#define UCR3_DTREN (1<<13) /* DTR interrupt enable */
-#define UCR3_PARERREN (1<<12) /* Parity enable */
-#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */
-#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_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 */
-#define IMX21_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select */
-#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */
-#define UCR3_BPEN (1<<0) /* Preset registers enable */
-#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */
-#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */
-#define UCR4_INVR (1<<9) /* Inverted infrared reception */
-#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_IRSC (1<<5) /* IR special case */
-#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
-#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
-#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */
-#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
-#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
-#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
-#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
-#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
-#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
-#define USR1_RTSS (1<<14) /* RTS pin status */
-#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */
-#define USR1_RTSD (1<<12) /* RTS delta */
-#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */
-#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */
-#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */
-#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */
-#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */
-#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */
-#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */
-#define USR2_ADET (1<<15) /* Auto baud rate detect complete */
-#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */
-#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */
-#define USR2_IDLE (1<<12) /* Idle condition */
-#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */
-#define USR2_WAKE (1<<7) /* Wake */
-#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */
-#define USR2_TXDC (1<<3) /* Transmitter complete */
-#define USR2_BRCD (1<<2) /* Break condition */
-#define USR2_ORE (1<<1) /* Overrun error */
-#define USR2_RDR (1<<0) /* Recv data ready */
-#define UTS_FRCPERR (1<<13) /* Force parity error */
-#define UTS_LOOP (1<<12) /* Loop tx and rx */
-#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */
-#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */
-#define UTS_TXFULL (1<<4) /* TxFIFO full */
-#define UTS_RXFULL (1<<3) /* RxFIFO full */
-#define UTS_SOFTRST (1<<0) /* Software reset */
+#define URXD_CHARRDY (1<<15)
+#define URXD_ERR (1<<14)
+#define URXD_OVRRUN (1<<13)
+#define URXD_FRMERR (1<<12)
+#define URXD_BRK (1<<11)
+#define URXD_PRERR (1<<10)
+#define UCR1_ADEN (1<<15) /* Auto detect interrupt */
+#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 */
+#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */
+#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
+#define UCR1_SNDBRK (1<<4) /* Send break */
+#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
+#define 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 */
+#define UCR2_IRTS (1<<14) /* Ignore RTS pin */
+#define UCR2_CTSC (1<<13) /* CTS pin control */
+#define UCR2_CTS (1<<12) /* Clear to send */
+#define UCR2_ESCEN (1<<11) /* Escape enable */
+#define UCR2_PREN (1<<8) /* Parity enable */
+#define UCR2_PROE (1<<7) /* Parity odd/even */
+#define UCR2_STPB (1<<6) /* Stop */
+#define UCR2_WS (1<<5) /* Word size */
+#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */
+#define UCR2_ATEN (1<<3) /* Aging Timer Enable */
+#define UCR2_TXEN (1<<2) /* Transmitter enabled */
+#define UCR2_RXEN (1<<1) /* Receiver enabled */
+#define UCR2_SRST (1<<0) /* SW reset */
+#define UCR3_DTREN (1<<13) /* DTR interrupt enable */
+#define UCR3_PARERREN (1<<12) /* Parity enable */
+#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */
+#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_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 */
+#define IMX21_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select */
+#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */
+#define UCR3_BPEN (1<<0) /* Preset registers enable */
+#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */
+#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */
+#define UCR4_INVR (1<<9) /* Inverted infrared reception */
+#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 */
+#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */
+#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
+#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
+#define UFCR_DCEDTE (1<<6) /* DCE/DTE mode select */
+#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
+#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
+#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
+#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
+#define USR1_RTSS (1<<14) /* RTS pin status */
+#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */
+#define USR1_RTSD (1<<12) /* RTS delta */
+#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */
+#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */
+#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */
+#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */
+#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */
+#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */
+#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */
+#define USR2_ADET (1<<15) /* Auto baud rate detect complete */
+#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */
+#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */
+#define USR2_IDLE (1<<12) /* Idle condition */
+#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */
+#define USR2_WAKE (1<<7) /* Wake */
+#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */
+#define USR2_TXDC (1<<3) /* Transmitter complete */
+#define USR2_BRCD (1<<2) /* Break condition */
+#define USR2_ORE (1<<1) /* Overrun error */
+#define USR2_RDR (1<<0) /* Recv data ready */
+#define UTS_FRCPERR (1<<13) /* Force parity error */
+#define UTS_LOOP (1<<12) /* Loop tx and rx */
+#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */
+#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */
+#define UTS_TXFULL (1<<4) /* TxFIFO full */
+#define UTS_RXFULL (1<<3) /* RxFIFO full */
+#define UTS_SOFTRST (1<<0) /* Software reset */
/* We've been assigned a range on the "Low-density serial ports" major */
-#define SERIAL_IMX_MAJOR 207
-#define MINOR_START 16
+#define SERIAL_IMX_MAJOR 207
+#define MINOR_START 16
#define DEV_NAME "ttymxc"
-#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS
/*
* This determines how often we check the modem status signals
@@ -186,6 +191,7 @@
enum imx_uart_type {
IMX1_UART,
IMX21_UART,
+ IMX6Q_UART,
};
/* device type dependent stuff */
@@ -198,14 +204,28 @@ struct imx_port {
struct uart_port port;
struct timer_list timer;
unsigned int old_status;
- int txirq,rxirq,rtsirq;
+ int txirq, rxirq, rtsirq;
unsigned int have_rtscts:1;
+ unsigned int dte_mode:1;
unsigned int use_irda:1;
unsigned int irda_inv_rx:1;
unsigned int irda_inv_tx:1;
unsigned short trcv_delay; /* transceiver delay */
- struct clk *clk;
- struct imx_uart_data *devdata;
+ 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 {
@@ -229,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[] = {
@@ -239,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 */ }
@@ -266,9 +294,14 @@ 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
*/
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE)
static void imx_port_ucrs_save(struct uart_port *port,
struct imx_port_ucrs *ucr)
{
@@ -286,6 +319,7 @@ static void imx_port_ucrs_restore(struct uart_port *port,
writel(ucr->ucr2, port->membase + UCR2);
writel(ucr->ucr3, port->membase + UCR3);
}
+#endif
/*
* Handle any change of modem status signal since we were last called.
@@ -382,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);
}
@@ -394,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);
+ 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);
}
/*
@@ -429,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
*/
@@ -437,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);
@@ -447,9 +580,18 @@ static void imx_start_tx(struct uart_port *port)
temp &= ~(UCR1_RRDYEN);
writel(temp, sport->port.membase + UCR1);
}
+ /* Clear any pending ORE flag before enabling interrupt */
+ temp = readl(sport->port.membase + USR2);
+ writel(temp | USR2_ORE, sport->port.membase + USR2);
- temp = readl(sport->port.membase + UCR1);
- writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_OREN;
+ writel(temp, sport->port.membase + UCR4);
+
+ 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);
@@ -461,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);
}
@@ -488,9 +635,8 @@ static irqreturn_t imx_txint(int irq, void *dev_id)
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
- spin_lock_irqsave(&sport->port.lock,flags);
- if (sport->port.x_char)
- {
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (sport->port.x_char) {
/* Send next char */
writel(sport->port.x_char, sport->port.membase + URTX0);
goto out;
@@ -507,18 +653,18 @@ static irqreturn_t imx_txint(int irq, void *dev_id)
uart_write_wakeup(&sport->port);
out:
- spin_unlock_irqrestore(&sport->port.lock,flags);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
return IRQ_HANDLED;
}
static irqreturn_t imx_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned int rx,flg,ignored = 0;
- struct tty_struct *tty = sport->port.state->port.tty;
+ unsigned int rx, flg, ignored = 0;
+ struct tty_port *port = &sport->port.state->port;
unsigned long flags, temp;
- spin_lock_irqsave(&sport->port.lock,flags);
+ spin_lock_irqsave(&sport->port.lock, flags);
while (readl(sport->port.membase + USR2) & USR2_RDR) {
flg = TTY_NORMAL;
@@ -568,24 +714,52 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
#endif
}
- tty_insert_flip_char(tty, rx, flg);
+ tty_insert_flip_char(port, rx, flg);
}
out:
- spin_unlock_irqrestore(&sport->port.lock,flags);
- tty_flip_buffer_push(tty);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ tty_flip_buffer_push(port);
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;
unsigned int sts;
+ unsigned int sts2;
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)
@@ -597,6 +771,13 @@ static irqreturn_t imx_int(int irq, void *dev_id)
if (sts & USR1_AWAKE)
writel(USR1_AWAKE, sport->port.membase + USR1);
+ sts2 = readl(sport->port.membase + USR2);
+ if (sts2 & USR2_ORE) {
+ dev_err(sport->port.dev, "Rx FIFO overrun\n");
+ sport->port.icount.overrun++;
+ writel(sts2 | USR2_ORE, sport->port.membase + USR2);
+ }
+
return IRQ_HANDLED;
}
@@ -606,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;
- return (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
+ ret = (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;
}
/*
@@ -624,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;
}
@@ -635,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));
}
/*
@@ -652,7 +849,7 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
- if ( break_state != 0 )
+ if (break_state != 0)
temp |= UCR1_SNDBRK;
writel(temp, sport->port.membase + UCR1);
@@ -666,23 +863,212 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
{
unsigned int val;
- unsigned int ufcr_rfdiv;
- /* set receiver / transmitter trigger level.
- * RFDIV is set such way to satisfy requested uartclk value
- */
- val = TXTL << 10 | RXTL;
- ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2)
- / sport->port.uartclk;
+ /* set receiver / transmitter trigger level */
+ val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
+ val |= TXTL << UFCR_TXTL_SHF | RXTL;
+ writel(val, sport->port.membase + UFCR);
+ return 0;
+}
+
+#define RX_BUF_SIZE (PAGE_SIZE)
+static void imx_rx_dma_done(struct imx_port *sport)
+{
+ unsigned long temp;
- if(!ufcr_rfdiv)
- ufcr_rfdiv = 1;
+ /* Enable this interrupt when the RXFIFO is empty. */
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_RRDYEN;
+ writel(temp, sport->port.membase + UCR1);
- val |= UFCR_RFDIV_REG(ufcr_rfdiv);
+ sport->dma_is_rxing = 0;
- writel(val, sport->port.membase + UFCR);
+ /* 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 */
@@ -691,9 +1077,18 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
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;
+ 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);
/* disable the DREN bit (Data Ready interrupt enable) before
@@ -705,22 +1100,20 @@ static int imx_startup(struct uart_port *port)
temp |= UCR4_IRSC;
/* set the trigger level for CTS */
- temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF);
- temp |= CTSTL<< UCR4_CTSTL_SHF;
+ temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
+ temp |= CTSTL << UCR4_CTSTL_SHF;
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
@@ -728,34 +1121,32 @@ 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,
- (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
- IRQF_TRIGGER_FALLING |
- IRQF_TRIGGER_RISING,
- DRIVER_NAME, sport);
+ retval = request_irq(sport->rtsirq, imx_rtsint, 0,
+ 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;
}
}
+ spin_lock_irqsave(&sport->port.lock, flags);
/*
* Finally, clear and enable interrupts
*/
@@ -773,20 +1164,13 @@ static int imx_startup(struct uart_port *port)
temp = readl(sport->port.membase + UCR2);
temp |= (UCR2_RXEN | UCR2_TXEN);
+ if (!sport->have_rtscts)
+ 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);
}
@@ -809,13 +1193,12 @@ static int imx_startup(struct uart_port *port)
/*
* Enable modem status interrupts
*/
- spin_lock_irqsave(&sport->port.lock,flags);
imx_enable_ms(&sport->port);
- spin_unlock_irqrestore(&sport->port.lock,flags);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
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;
@@ -839,14 +1222,26 @@ static void imx_shutdown(struct uart_port *port)
{
struct imx_port *sport = (struct imx_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);
writel(temp, sport->port.membase + UCR2);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
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);
}
@@ -871,12 +1266,27 @@ static void imx_shutdown(struct uart_port *port)
* Disable all interrupts, port and break condition.
*/
+ spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1);
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
if (USE_IRDA(sport))
temp &= ~(UCR1_IREN);
writel(temp, sport->port.membase + UCR1);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ clk_disable_unprepare(sport->clk_per);
+ clk_disable_unprepare(sport->clk_ipg);
+}
+
+static void imx_flush_buffer(struct uart_port *port)
+{
+ struct imx_port *sport = (struct imx_port *)port;
+
+ if (sport->dma_is_enabled) {
+ sport->tx_bytes = 0;
+ dmaengine_terminate_all(sport->dma_chan_tx);
+ }
}
static void
@@ -916,9 +1326,14 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
ucr2 = UCR2_SRST | UCR2_IRTS;
if (termios->c_cflag & CRTSCTS) {
- if( sport->have_rtscts ) {
+ 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;
}
@@ -976,12 +1391,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
sport->port.membase + UCR1);
- while ( !(readl(sport->port.membase + USR2) & USR2_TXDC))
+ while (!(readl(sport->port.membase + USR2) & USR2_TXDC))
barrier();
/* then, disable everything */
old_txrxen = readl(sport->port.membase + UCR2);
- writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN),
+ writel(old_txrxen & ~(UCR2_TXEN | UCR2_RXEN),
sport->port.membase + UCR2);
old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
@@ -992,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;
@@ -1013,12 +1433,14 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
ufcr = readl(sport->port.membase + UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
+ if (sport->dte_mode)
+ ufcr |= UFCR_DCEDTE;
writel(ufcr, sport->port.membase + UFCR);
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);
@@ -1030,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);
}
@@ -1041,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;
}
@@ -1101,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;
@@ -1185,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)
@@ -1219,6 +1611,25 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
struct imx_port *sport = imx_ports[co->index];
struct imx_port_ucrs old_ucr;
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;
+ else if (oops_in_progress)
+ locked = spin_trylock_irqsave(&sport->port.lock, flags);
+ else
+ spin_lock_irqsave(&sport->port.lock, flags);
/*
* First, save UCR1/2/3 and then disable interrupts
@@ -1244,6 +1655,12 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
imx_port_ucrs_restore(&sport->port, &old_ucr);
+
+ if (locked)
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ clk_disable(sport->clk_ipg);
+ clk_disable(sport->clk_per);
}
/*
@@ -1257,7 +1674,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) {
/* ok, the port was enabled */
- unsigned int ucr2, ubir,ubmr, uartclk;
+ unsigned int ucr2, ubir, ubmr, uartclk;
unsigned int baud_raw;
unsigned int ucfr_rfdiv;
@@ -1285,7 +1702,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
else
ucfr_rfdiv = 6 - ucfr_rfdiv;
- uartclk = clk_get_rate(sport->clk);
+ uartclk = clk_get_rate(sport->clk_per);
uartclk /= ucfr_rfdiv;
{ /*
@@ -1303,8 +1720,8 @@ imx_console_get_options(struct imx_port *sport, int *baud,
*baud = (baud_raw + 50) / 100 * 100;
}
- if(*baud != baud_raw)
- printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n",
+ if (*baud != baud_raw)
+ pr_info("Console IMX rounded baud rate from %d to %d\n",
baud_raw, *baud);
}
}
@@ -1317,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
@@ -1326,9 +1744,14 @@ imx_console_setup(struct console *co, char *options)
if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
co->index = 0;
sport = imx_ports[co->index];
- if(sport == NULL)
+ 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
@@ -1336,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;
@@ -1375,8 +1811,7 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
val |= UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
- if (sport)
- uart_suspend_port(&imx_reg, &sport->port);
+ uart_suspend_port(&imx_reg, &sport->port);
return 0;
}
@@ -1391,8 +1826,7 @@ static int serial_imx_resume(struct platform_device *dev)
val &= ~UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
- if (sport)
- uart_resume_port(&imx_reg, &sport->port);
+ uart_resume_port(&imx_reg, &sport->port);
return 0;
}
@@ -1427,6 +1861,9 @@ static int serial_imx_probe_dt(struct imx_port *sport,
if (of_get_property(np, "fsl,irda-mode", NULL))
sport->use_irda = 1;
+ if (of_get_property(np, "fsl,dte-mode", NULL))
+ sport->dte_mode = 1;
+
sport->devdata = of_id->data;
return 0;
@@ -1442,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;
@@ -1460,12 +1897,11 @@ 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;
- sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+ sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
if (!sport)
return -ENOMEM;
@@ -1473,19 +1909,12 @@ static int serial_imx_probe(struct platform_device *pdev)
if (ret > 0)
serial_imx_probe_pdata(sport, pdev);
else if (ret < 0)
- goto free;
+ return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto free;
- }
-
- base = ioremap(res->start, PAGE_SIZE);
- if (!base) {
- ret = -ENOMEM;
- goto free;
- }
+ 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;
@@ -1503,67 +1932,34 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
- sport->clk = clk_get(&pdev->dev, "uart");
- if (IS_ERR(sport->clk)) {
- ret = PTR_ERR(sport->clk);
- goto unmap;
+ sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(sport->clk_ipg)) {
+ ret = PTR_ERR(sport->clk_ipg);
+ dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
+ return ret;
}
- clk_enable(sport->clk);
- sport->port.uartclk = clk_get_rate(sport->clk);
-
- imx_ports[sport->port.line] = sport;
-
- pdata = pdev->dev.platform_data;
- if (pdata && pdata->init) {
- ret = pdata->init(pdev);
- if (ret)
- goto clkput;
+ sport->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(sport->clk_per)) {
+ ret = PTR_ERR(sport->clk_per);
+ dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
+ return ret;
}
- ret = uart_add_one_port(&imx_reg, &sport->port);
- if (ret)
- goto deinit;
- platform_set_drvdata(pdev, &sport->port);
+ sport->port.uartclk = clk_get_rate(sport->clk_per);
- return 0;
-deinit:
- if (pdata && pdata->exit)
- pdata->exit(pdev);
-clkput:
- clk_put(sport->clk);
- clk_disable(sport->clk);
-unmap:
- iounmap(sport->port.membase);
-free:
- kfree(sport);
+ imx_ports[sport->port.line] = sport;
- return ret;
+ platform_set_drvdata(pdev, sport);
+
+ 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);
-
- if (sport) {
- uart_remove_one_port(&imx_reg, &sport->port);
- clk_put(sport->clk);
- }
-
- clk_disable(sport->clk);
-
- if (pdata && pdata->exit)
- pdata->exit(pdev);
-
- iounmap(sport->port.membase);
- kfree(sport);
-
- return 0;
+ return uart_remove_one_port(&imx_reg, &sport->port);
}
static struct platform_driver serial_imx_driver = {
@@ -1584,7 +1980,7 @@ static int __init imx_serial_init(void)
{
int ret;
- printk(KERN_INFO "Serial: IMX driver\n");
+ pr_info("Serial: IMX driver\n");
ret = uart_register_driver(&imx_reg);
if (ret)
diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c
index 758ff310f7f..6e4c715c5d2 100644
--- a/drivers/tty/serial/ioc3_serial.c
+++ b/drivers/tty/serial/ioc3_serial.c
@@ -1000,7 +1000,7 @@ ioc3_change_speed(struct uart_port *the_port,
the_port->ignore_status_mask = N_ALL_INPUT;
- state->port.tty->low_latency = 1;
+ state->port.low_latency = 1;
if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~(N_PARITY_ERROR
@@ -1120,13 +1120,14 @@ static inline int do_read(struct uart_port *the_port, char *buf, int len)
struct ioc3_port *port = get_ioc3_port(the_port);
struct ring *inring;
struct ring_entry *entry;
- struct port_hooks *hooks = port->ip_hooks;
+ struct port_hooks *hooks;
int byte_num;
char *sc;
int loop_counter;
BUG_ON(!(len >= 0));
BUG_ON(!port);
+ hooks = port->ip_hooks;
/* There is a nasty timing issue in the IOC3. When the rx_timer
* expires or the rx_high condition arises, we take an interrupt.
@@ -1392,7 +1393,6 @@ static inline int do_read(struct uart_port *the_port, char *buf, int len)
*/
static int receive_chars(struct uart_port *the_port)
{
- struct tty_struct *tty;
unsigned char ch[MAX_CHARS];
int read_count = 0, read_room, flip = 0;
struct uart_state *state = the_port->state;
@@ -1402,25 +1402,23 @@ static int receive_chars(struct uart_port *the_port)
/* Make sure all the pointers are "good" ones */
if (!state)
return 0;
- if (!state->port.tty)
- return 0;
if (!(port->ip_flags & INPUT_ENABLE))
return 0;
spin_lock_irqsave(&the_port->lock, pflags);
- tty = state->port.tty;
read_count = do_read(the_port, ch, MAX_CHARS);
if (read_count > 0) {
flip = 1;
- read_room = tty_insert_flip_string(tty, ch, read_count);
+ read_room = tty_insert_flip_string(&state->port, ch,
+ read_count);
the_port->icount.rx += read_count;
}
spin_unlock_irqrestore(&the_port->lock, pflags);
if (flip)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&state->port);
return read_count;
}
@@ -2009,7 +2007,7 @@ static int ioc3uart_remove(struct ioc3_submodule *is,
* @idd: ioc3 driver data for this card
*/
-static int __devinit
+static int
ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
{
struct pci_dev *pdev = idd->pdev;
diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c
index 6b36c1554d7..1274499850f 100644
--- a/drivers/tty/serial/ioc4_serial.c
+++ b/drivers/tty/serial/ioc4_serial.c
@@ -16,7 +16,6 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
-#include <linux/serialP.h>
#include <linux/circ_buf.h>
#include <linux/serial_reg.h>
#include <linux/module.h>
@@ -298,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
@@ -975,7 +974,7 @@ intr_connect(struct ioc4_soft *soft, int type,
BUG_ON(!((type == IOC4_SIO_INTR_TYPE)
|| (type == IOC4_OTHER_INTR_TYPE)));
- i = atomic_inc(&soft-> is_intr_type[type].is_num_intrs) - 1;
+ i = atomic_inc_return(&soft-> is_intr_type[type].is_num_intrs) - 1;
BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0)));
/* Save off the lower level interrupt handler */
@@ -1741,7 +1740,7 @@ ioc4_change_speed(struct uart_port *the_port,
the_port->ignore_status_mask = N_ALL_INPUT;
- state->port.tty->low_latency = 1;
+ state->port.low_latency = 1;
if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~(N_PARITY_ERROR
@@ -1804,7 +1803,7 @@ static inline int ic4_startup_local(struct uart_port *the_port)
ioc4_set_proto(port, the_port->mapbase);
/* set the speed of the serial port */
- ioc4_change_speed(the_port, state->port.tty->termios,
+ ioc4_change_speed(the_port, &state->port.tty->termios,
(struct ktermios *)0);
return 0;
@@ -2070,13 +2069,14 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf,
struct ioc4_port *port = get_ioc4_port(the_port, 0);
struct ring *inring;
struct ring_entry *entry;
- struct hooks *hooks = port->ip_hooks;
+ struct hooks *hooks;
int byte_num;
char *sc;
int loop_counter;
BUG_ON(!(len >= 0));
BUG_ON(!port);
+ hooks = port->ip_hooks;
/* There is a nasty timing issue in the IOC4. When the rx_timer
* expires or the rx_high condition arises, we take an interrupt.
@@ -2340,7 +2340,6 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf,
*/
static void receive_chars(struct uart_port *the_port)
{
- struct tty_struct *tty;
unsigned char ch[IOC4_MAX_CHARS];
int read_count, request_count = IOC4_MAX_CHARS;
struct uart_icount *icount;
@@ -2350,26 +2349,23 @@ static void receive_chars(struct uart_port *the_port)
/* Make sure all the pointers are "good" ones */
if (!state)
return;
- if (!state->port.tty)
- return;
spin_lock_irqsave(&the_port->lock, pflags);
- tty = state->port.tty;
- request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS);
+ request_count = tty_buffer_request_room(&state->port, IOC4_MAX_CHARS);
if (request_count > 0) {
icount = &the_port->icount;
read_count = do_read(the_port, ch, request_count);
if (read_count > 0) {
- tty_insert_flip_string(tty, ch, read_count);
+ tty_insert_flip_string(&state->port, ch, read_count);
icount->rx += read_count;
}
}
spin_unlock_irqrestore(&the_port->lock, pflags);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&state->port);
}
/**
@@ -2771,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;
@@ -2883,6 +2879,7 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd)
/* error exits that give back resources */
out5:
ioc4_serial_remove_one(idd);
+ return ret;
out4:
kfree(soft);
out3:
diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c
index 7b1cda59ebb..99b7b869786 100644
--- a/drivers/tty/serial/ip22zilog.c
+++ b/drivers/tty/serial/ip22zilog.c
@@ -248,17 +248,12 @@ static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up,
#define Rx_BRK 0x0100 /* BREAK event software flag. */
#define Rx_SYS 0x0200 /* SysRq event software flag. */
-static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
+static bool ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
struct zilog_channel *channel)
{
- struct tty_struct *tty;
unsigned char ch, flag;
unsigned int r1;
-
- tty = NULL;
- if (up->port.state != NULL &&
- up->port.state->port.tty != NULL)
- tty = up->port.state->port.tty;
+ bool push = up->port.state != NULL;
for (;;) {
ch = readb(&channel->control);
@@ -312,10 +307,10 @@ static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up
if (uart_handle_sysrq_char(&up->port, ch))
continue;
- if (tty)
+ if (push)
uart_insert_char(&up->port, r1, Rx_OVR, ch, flag);
}
- return tty;
+ return push;
}
static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
@@ -438,21 +433,20 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
while (up) {
struct zilog_channel *channel
= ZILOG_CHANNEL_FROM_PORT(&up->port);
- struct tty_struct *tty;
unsigned char r3;
+ bool push = false;
spin_lock(&up->port.lock);
r3 = read_zsreg(channel, R3);
/* Channel A */
- tty = NULL;
if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
writeb(RES_H_IUS, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
if (r3 & CHARxIP)
- tty = ip22zilog_receive_chars(up, channel);
+ push = ip22zilog_receive_chars(up, channel);
if (r3 & CHAEXT)
ip22zilog_status_handle(up, channel);
if (r3 & CHATxIP)
@@ -460,22 +454,22 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
}
spin_unlock(&up->port.lock);
- if (tty)
- tty_flip_buffer_push(tty);
+ if (push)
+ tty_flip_buffer_push(&up->port.state->port);
/* Channel B */
up = up->next;
channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+ push = false;
spin_lock(&up->port.lock);
- tty = NULL;
if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
writeb(RES_H_IUS, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
if (r3 & CHBRxIP)
- tty = ip22zilog_receive_chars(up, channel);
+ push = ip22zilog_receive_chars(up, channel);
if (r3 & CHBEXT)
ip22zilog_status_handle(up, channel);
if (r3 & CHBTxIP)
@@ -483,8 +477,8 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
}
spin_unlock(&up->port.lock);
- if (tty)
- tty_flip_buffer_push(tty);
+ if (push)
+ tty_flip_buffer_push(&up->port.state->port);
up = up->next;
}
@@ -609,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);
@@ -838,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;
@@ -856,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/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h
index 529bec6edaf..844d5e4eb1a 100644
--- a/drivers/tty/serial/jsm/jsm.h
+++ b/drivers/tty/serial/jsm/jsm.h
@@ -57,9 +57,11 @@ enum {
DBG_CARR = 0x10000,
};
-#define jsm_printk(nlevel, klevel, pdev, fmt, args...) \
- if ((DBG_##nlevel & jsm_debug)) \
- dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
+#define jsm_dbg(nlevel, pdev, fmt, ...) \
+do { \
+ if (DBG_##nlevel & jsm_debug) \
+ dev_dbg(pdev->dev, fmt, ##__VA_ARGS__); \
+} while (0)
#define MAXLINES 256
#define MAXPORTS 8
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index 7545fe1b992..a47d882d674 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -54,7 +54,7 @@ static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev);
static void jsm_io_resume(struct pci_dev *pdev);
-static struct pci_error_handlers jsm_err_handler = {
+static const struct pci_error_handlers jsm_err_handler = {
.error_detected = jsm_io_error_detected,
.slot_reset = jsm_io_slot_reset,
.resume = jsm_io_resume,
@@ -64,7 +64,7 @@ int jsm_debug;
module_param(jsm_debug, int, 0);
MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
-static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc = 0;
struct jsm_board *brd;
@@ -107,8 +107,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device
brd->irq = pdev->irq;
- jsm_printk(INIT, INFO, &brd->pci_dev,
- "jsm_found_board - NEO adapter\n");
+ jsm_dbg(INIT, &brd->pci_dev, "jsm_found_board - NEO adapter\n");
/* get the PCI Base Address Registers */
brd->membase = pci_resource_start(pdev, 0);
@@ -179,7 +178,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device
return rc;
}
-static void __devexit jsm_remove_one(struct pci_dev *pdev)
+static void jsm_remove_one(struct pci_dev *pdev)
{
struct jsm_board *brd = pci_get_drvdata(pdev);
int i = 0;
@@ -218,7 +217,7 @@ static struct pci_driver jsm_driver = {
.name = "jsm",
.id_table = jsm_pci_tbl,
.probe = jsm_probe_one,
- .remove = __devexit_p(jsm_remove_one),
+ .remove = jsm_remove_one,
.err_handler = &jsm_err_handler,
};
diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c
index 81dfafa11b0..dfaf4882641 100644
--- a/drivers/tty/serial/jsm/jsm_neo.c
+++ b/drivers/tty/serial/jsm/jsm_neo.c
@@ -52,7 +52,7 @@ static void neo_set_cts_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
/* Turn on auto CTS flow control */
ier |= (UART_17158_IER_CTSDSR);
@@ -83,7 +83,7 @@ static void neo_set_rts_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
/* Turn on auto RTS flow control */
ier |= (UART_17158_IER_RTSDTR);
@@ -123,7 +123,7 @@ static void neo_set_ixon_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
/* Turn off auto CTS flow control */
ier &= ~(UART_17158_IER_CTSDSR);
@@ -160,7 +160,7 @@ static void neo_set_ixoff_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
/* Turn off auto RTS flow control */
ier &= ~(UART_17158_IER_RTSDTR);
@@ -198,7 +198,7 @@ static void neo_set_no_input_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
/* Turn off auto RTS flow control */
ier &= ~(UART_17158_IER_RTSDTR);
@@ -237,7 +237,7 @@ static void neo_set_no_output_flow_control(struct jsm_channel *ch)
ier = readb(&ch->ch_neo_uart->ier);
efr = readb(&ch->ch_neo_uart->efr);
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
/* Turn off auto CTS flow control */
ier &= ~(UART_17158_IER_CTSDSR);
@@ -276,7 +276,7 @@ static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch)
if (ch->ch_c_cflag & CRTSCTS)
return;
- jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(PARAM, &ch->ch_bd->pci_dev, "start\n");
/* Tell UART what start/stop chars it should be looking for */
writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
@@ -455,7 +455,7 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
* I hope thats okay with everyone? Yes? Good.
*/
while (qleft < 1) {
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
"Queue full, dropping DATA:%x LSR:%x\n",
ch->ch_rqueue[tail], ch->ch_equeue[tail]);
@@ -467,8 +467,8 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
ch->ch_equeue[head] = (u8) linestatus;
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]);
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "DATA/LSR pair: %x %x\n",
+ ch->ch_rqueue[head], ch->ch_equeue[head]);
/* Ditch any remaining linestatus value. */
linestatus = 0;
@@ -521,8 +521,8 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
ch->ch_cached_lsr &= ~(UART_LSR_THRE);
writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx);
- jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev,
- "Tx data: %x\n", circ->buf[circ->tail]);
+ jsm_dbg(WRITE, &ch->ch_bd->pci_dev,
+ "Tx data: %x\n", circ->buf[circ->tail]);
circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1);
ch->ch_txcount++;
}
@@ -575,8 +575,9 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
{
u8 msignals = signals;
- jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
- "neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals);
+ jsm_dbg(MSIGS, &ch->ch_bd->pci_dev,
+ "neo_parse_modem: port: %d msignals: %x\n",
+ ch->ch_portnum, msignals);
/* Scrub off lower bits. They signify delta's, which I don't care about */
/* Keep DDCD and DDSR though */
@@ -606,8 +607,8 @@ static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
else
ch->ch_mistat &= ~UART_MSR_CTS;
- jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
- "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
+ jsm_dbg(MSIGS, &ch->ch_bd->pci_dev,
+ "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
ch->ch_portnum,
!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
@@ -649,8 +650,8 @@ static void neo_flush_uart_write(struct jsm_channel *ch)
/* Check to see if the UART feels it completely flushed the FIFO. */
tmp = readb(&ch->ch_neo_uart->isr_fcr);
if (tmp & 4) {
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
- "Still flushing TX UART... i: %d\n", i);
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev,
+ "Still flushing TX UART... i: %d\n", i);
udelay(10);
}
else
@@ -681,8 +682,8 @@ static void neo_flush_uart_read(struct jsm_channel *ch)
/* Check to see if the UART feels it completely flushed the FIFO. */
tmp = readb(&ch->ch_neo_uart->isr_fcr);
if (tmp & 2) {
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
- "Still flushing RX UART... i: %d\n", i);
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev,
+ "Still flushing RX UART... i: %d\n", i);
udelay(10);
}
else
@@ -705,8 +706,9 @@ static void neo_clear_break(struct jsm_channel *ch, int force)
writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
ch->ch_flags &= ~(CH_BREAK_SENDING);
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
- "clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies);
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev,
+ "clear break Finishing UART_LCR_SBC! finished: %lx\n",
+ jiffies);
/* flush write operation */
neo_pci_posting_flush(ch->ch_bd);
@@ -748,8 +750,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
*/
isr &= ~(UART_17158_IIR_FIFO_ENABLED);
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "%s:%d isr: %x\n", __FILE__, __LINE__, isr);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d isr: %x\n",
+ __FILE__, __LINE__, isr);
if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
/* Read data from uart -> queue */
@@ -772,8 +774,9 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
if (isr & UART_17158_IIR_XONXOFF) {
cause = readb(&ch->ch_neo_uart->xoffchar1);
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Port %d. Got ISR_XONXOFF: cause:%x\n",
+ port, cause);
/*
* Since the UART detected either an XON or
@@ -786,17 +789,19 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
if (brd->channels[port]->ch_flags & CH_STOP) {
ch->ch_flags &= ~(CH_STOP);
}
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Port %d. XON detected in incoming data\n", port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Port %d. XON detected in incoming data\n",
+ port);
}
else if (cause == UART_17158_XOFF_DETECT) {
if (!(brd->channels[port]->ch_flags & CH_STOP)) {
ch->ch_flags |= CH_STOP;
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Setting CH_STOP\n");
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Setting CH_STOP\n");
}
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "Port: %d. XOFF detected in incoming data\n", port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "Port: %d. XOFF detected in incoming data\n",
+ port);
}
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
}
@@ -825,8 +830,8 @@ static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
}
/* Parse any modem signal changes */
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "MOD_STAT: sending to parse_modem_sigs\n");
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "MOD_STAT: sending to parse_modem_sigs\n");
neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
}
}
@@ -849,8 +854,8 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
linestatus = readb(&ch->ch_neo_uart->lsr);
- jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
- "%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d port: %d linestatus: %x\n",
+ __FILE__, __LINE__, port, linestatus);
ch->ch_cached_lsr |= linestatus;
@@ -869,7 +874,7 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
*to do the special RX+LSR read for this FIFO load.
*/
if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
"%s:%d Port: %d Got an RX error, need to parse LSR\n",
__FILE__, __LINE__, port);
@@ -880,20 +885,21 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
if (linestatus & UART_LSR_PE) {
ch->ch_err_parity++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. PAR ERR!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_FE) {
ch->ch_err_frame++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d Port: %d. FRM ERR!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_BI) {
ch->ch_err_break++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "%s:%d Port: %d. BRK INTR!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_OE) {
@@ -904,8 +910,9 @@ static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
* Probably we should eventually have an orun stat in our driver...
*/
ch->ch_err_overrun++;
- jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
- "%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port);
+ jsm_dbg(INTR, &ch->ch_bd->pci_dev,
+ "%s:%d Port: %d. Rx Overrun!\n",
+ __FILE__, __LINE__, port);
}
if (linestatus & UART_LSR_THRE) {
@@ -1128,11 +1135,11 @@ static irqreturn_t neo_intr(int irq, void *voidbrd)
*/
uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
- jsm_printk(INTR, INFO, &brd->pci_dev,
- "%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll);
+ jsm_dbg(INTR, &brd->pci_dev, "%s:%d uart_poll: %x\n",
+ __FILE__, __LINE__, uart_poll);
if (!uart_poll) {
- jsm_printk(INTR, INFO, &brd->pci_dev,
+ jsm_dbg(INTR, &brd->pci_dev,
"Kernel interrupted to me, but no pending interrupts...\n");
spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
return IRQ_NONE;
@@ -1158,15 +1165,15 @@ static irqreturn_t neo_intr(int irq, void *voidbrd)
continue;
}
- jsm_printk(INTR, INFO, &brd->pci_dev,
- "%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type);
+ jsm_dbg(INTR, &brd->pci_dev, "%s:%d port: %x type: %x\n",
+ __FILE__, __LINE__, port, type);
/* Remove this port + type from uart_poll */
uart_poll &= ~(jsm_offset_table[port]);
if (!type) {
/* If no type, just ignore it, and move onto next port */
- jsm_printk(INTR, ERR, &brd->pci_dev,
+ jsm_dbg(INTR, &brd->pci_dev,
"Interrupt with no type! port: %d\n", port);
continue;
}
@@ -1231,15 +1238,16 @@ static irqreturn_t neo_intr(int irq, void *voidbrd)
* these once and awhile.
* Its harmless, just ignore it and move on.
*/
- jsm_printk(INTR, ERR, &brd->pci_dev,
- "%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type);
+ jsm_dbg(INTR, &brd->pci_dev,
+ "%s:%d Unknown Interrupt type: %x\n",
+ __FILE__, __LINE__, type);
continue;
}
}
spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
- jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n");
+ jsm_dbg(INTR, &brd->pci_dev, "finish\n");
return IRQ_HANDLED;
}
diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c
index 434bd881fca..27bb75070c9 100644
--- a/drivers/tty/serial/jsm/jsm_tty.c
+++ b/drivers/tty/serial/jsm/jsm_tty.c
@@ -43,7 +43,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch)
unsigned char mstat;
unsigned result;
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "start\n");
mstat = (ch->ch_mostat | ch->ch_mistat);
@@ -62,7 +62,7 @@ static inline int jsm_get_mstat(struct jsm_channel *ch)
if (mstat & UART_MSR_DCD)
result |= TIOCM_CD;
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n");
return result;
}
@@ -79,14 +79,14 @@ static unsigned int jsm_tty_get_mctrl(struct uart_port *port)
int result;
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
result = jsm_get_mstat(channel);
if (result < 0)
return -ENXIO;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
return result;
}
@@ -100,7 +100,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
if (mctrl & TIOCM_RTS)
channel->ch_mostat |= UART_MCR_RTS;
@@ -114,7 +114,7 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
channel->ch_bd->bd_ops->assert_modem_signals(channel);
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
udelay(10);
}
@@ -135,23 +135,23 @@ static void jsm_tty_start_tx(struct uart_port *port)
{
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
channel->ch_flags &= ~(CH_STOP);
jsm_tty_write(port);
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
}
static void jsm_tty_stop_tx(struct uart_port *port)
{
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");
channel->ch_flags |= (CH_STOP);
- jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
}
static void jsm_tty_send_xchar(struct uart_port *port, char ch)
@@ -161,7 +161,7 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch)
struct ktermios *termios;
spin_lock_irqsave(&port->lock, lock_flags);
- termios = port->state->port.tty->termios;
+ termios = &port->state->port.tty->termios;
if (ch == termios->c_cc[VSTART])
channel->ch_bd->bd_ops->send_start_character(channel);
@@ -216,16 +216,16 @@ static int jsm_tty_open(struct uart_port *port)
if (!channel->ch_rqueue) {
channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL);
if (!channel->ch_rqueue) {
- jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
- "unable to allocate read queue buf");
+ jsm_dbg(INIT, &channel->ch_bd->pci_dev,
+ "unable to allocate read queue buf\n");
return -ENOMEM;
}
}
if (!channel->ch_equeue) {
channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL);
if (!channel->ch_equeue) {
- jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
- "unable to allocate error queue buf");
+ jsm_dbg(INIT, &channel->ch_bd->pci_dev,
+ "unable to allocate error queue buf\n");
return -ENOMEM;
}
}
@@ -234,7 +234,7 @@ static int jsm_tty_open(struct uart_port *port)
/*
* Initialize if neither terminal is open.
*/
- jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev,
+ jsm_dbg(OPEN, &channel->ch_bd->pci_dev,
"jsm_open: initializing channel in open...\n");
/*
@@ -250,7 +250,7 @@ static int jsm_tty_open(struct uart_port *port)
channel->ch_cached_lsr = 0;
channel->ch_stops_sent = 0;
- termios = port->state->port.tty->termios;
+ termios = &port->state->port.tty->termios;
channel->ch_c_cflag = termios->c_cflag;
channel->ch_c_iflag = termios->c_iflag;
channel->ch_c_oflag = termios->c_oflag;
@@ -270,7 +270,7 @@ static int jsm_tty_open(struct uart_port *port)
channel->ch_open_count++;
- jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n");
return 0;
}
@@ -280,10 +280,10 @@ static void jsm_tty_close(struct uart_port *port)
struct ktermios *ts;
struct jsm_channel *channel = (struct jsm_channel *)port;
- jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n");
+ jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n");
bd = channel->ch_bd;
- ts = port->state->port.tty->termios;
+ ts = &port->state->port.tty->termios;
channel->ch_flags &= ~(CH_STOPI);
@@ -293,7 +293,7 @@ static void jsm_tty_close(struct uart_port *port)
* If we have HUPCL set, lower DTR and RTS
*/
if (channel->ch_c_cflag & HUPCL) {
- jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev,
+ jsm_dbg(CLOSE, &channel->ch_bd->pci_dev,
"Close. HUPCL set, dropping DTR/RTS\n");
/* Drop RTS/DTR */
@@ -304,7 +304,7 @@ static void jsm_tty_close(struct uart_port *port)
/* Turn off UART interrupts for this port */
channel->ch_bd->bd_ops->uart_off(channel);
- jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "finish\n");
}
static void jsm_tty_set_termios(struct uart_port *port,
@@ -371,7 +371,7 @@ static struct uart_ops jsm_ops = {
* Init the tty subsystem. Called once per board after board has been
* downloaded and init'ed.
*/
-int __devinit jsm_tty_init(struct jsm_board *brd)
+int jsm_tty_init(struct jsm_board *brd)
{
int i;
void __iomem *vaddr;
@@ -380,7 +380,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
if (!brd)
return -ENXIO;
- jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+ jsm_dbg(INIT, &brd->pci_dev, "start\n");
/*
* Initialize board structure elements.
@@ -401,9 +401,9 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
*/
brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL);
if (!brd->channels[i]) {
- jsm_printk(CORE, ERR, &brd->pci_dev,
+ jsm_dbg(CORE, &brd->pci_dev,
"%s:%d Unable to allocate memory for channel struct\n",
- __FILE__, __LINE__);
+ __FILE__, __LINE__);
}
}
}
@@ -431,7 +431,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd)
init_waitqueue_head(&ch->ch_flags_wait);
}
- jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+ jsm_dbg(INIT, &brd->pci_dev, "finish\n");
return 0;
}
@@ -444,7 +444,7 @@ int jsm_uart_port_init(struct jsm_board *brd)
if (!brd)
return -ENXIO;
- jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+ jsm_dbg(INIT, &brd->pci_dev, "start\n");
/*
* Initialize board structure elements.
@@ -481,7 +481,7 @@ int jsm_uart_port_init(struct jsm_board *brd)
printk(KERN_INFO "jsm: Port %d added\n", i);
}
- jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+ jsm_dbg(INIT, &brd->pci_dev, "finish\n");
return 0;
}
@@ -493,7 +493,7 @@ int jsm_remove_uart_port(struct jsm_board *brd)
if (!brd)
return -ENXIO;
- jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+ jsm_dbg(INIT, &brd->pci_dev, "start\n");
/*
* Initialize board structure elements.
@@ -513,7 +513,7 @@ int jsm_remove_uart_port(struct jsm_board *brd)
uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
}
- jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+ jsm_dbg(INIT, &brd->pci_dev, "finish\n");
return 0;
}
@@ -521,6 +521,7 @@ void jsm_input(struct jsm_channel *ch)
{
struct jsm_board *bd;
struct tty_struct *tp;
+ struct tty_port *port;
u32 rmask;
u16 head;
u16 tail;
@@ -531,12 +532,13 @@ void jsm_input(struct jsm_channel *ch)
int s = 0;
int i = 0;
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n");
if (!ch)
return;
- tp = ch->uart_port.state->port.tty;
+ port = &ch->uart_port.state->port;
+ tp = port->tty;
bd = ch->ch_bd;
if(!bd)
@@ -560,17 +562,18 @@ void jsm_input(struct jsm_channel *ch)
return;
}
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n");
/*
*If the device is not open, or CREAD is off, flush
*input data and return immediately.
*/
if (!tp ||
- !(tp->termios->c_cflag & CREAD) ) {
+ !(tp->termios.c_cflag & CREAD) ) {
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum);
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "input. dropping %d bytes on port %d...\n",
+ data_len, ch->ch_portnum);
ch->ch_r_head = tail;
/* Force queue flow control to be released, if needed */
@@ -585,21 +588,15 @@ void jsm_input(struct jsm_channel *ch)
*/
if (ch->ch_flags & CH_STOPI) {
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
"Port %d throttled, not reading any data. head: %x tail: %x\n",
ch->ch_portnum, head, tail);
return;
}
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n");
- if (data_len <= 0) {
- spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n");
- return;
- }
-
- len = tty_buffer_request_room(tp, data_len);
+ len = tty_buffer_request_room(port, data_len);
n = len;
/*
@@ -628,16 +625,16 @@ void jsm_input(struct jsm_channel *ch)
* format it likes.
*/
if (*(ch->ch_equeue +tail +i) & UART_LSR_BI)
- tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_BREAK);
+ tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_BREAK);
else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE)
- tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_PARITY);
+ tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_PARITY);
else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE)
- tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_FRAME);
+ tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_FRAME);
else
- tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_NORMAL);
+ tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_NORMAL);
}
} else {
- tty_insert_flip_string(tp, ch->ch_rqueue + tail, s) ;
+ tty_insert_flip_string(port, ch->ch_rqueue + tail, s);
}
tail += s;
n -= s;
@@ -651,9 +648,9 @@ void jsm_input(struct jsm_channel *ch)
spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
/* Tell the tty layer its okay to "eat" the data now */
- tty_flip_buffer_push(tp);
+ tty_flip_buffer_push(port);
- jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+ jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n");
}
static void jsm_carrier(struct jsm_channel *ch)
@@ -663,7 +660,7 @@ static void jsm_carrier(struct jsm_channel *ch)
int virt_carrier = 0;
int phys_carrier = 0;
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n");
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "start\n");
if (!ch)
return;
@@ -673,16 +670,16 @@ static void jsm_carrier(struct jsm_channel *ch)
return;
if (ch->ch_mistat & UART_MSR_DCD) {
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
- "mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "mistat: %x D_CD: %x\n",
+ ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
phys_carrier = 1;
}
if (ch->ch_c_cflag & CLOCAL)
virt_carrier = 1;
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
- "DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier);
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "DCD: physical: %d virt: %d\n",
+ phys_carrier, virt_carrier);
/*
* Test for a VIRTUAL carrier transition to HIGH.
@@ -694,8 +691,7 @@ static void jsm_carrier(struct jsm_channel *ch)
* for carrier in the open routine.
*/
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
- "carrier: virt DCD rose\n");
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: virt DCD rose\n");
if (waitqueue_active(&(ch->ch_flags_wait)))
wake_up_interruptible(&ch->ch_flags_wait);
@@ -711,7 +707,7 @@ static void jsm_carrier(struct jsm_channel *ch)
* for carrier in the open routine.
*/
- jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+ jsm_dbg(CARR, &ch->ch_bd->pci_dev,
"carrier: physical DCD rose\n");
if (waitqueue_active(&(ch->ch_flags_wait)))
@@ -790,8 +786,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
bd_ops->disable_receiver(ch);
ch->ch_flags |= (CH_RECEIVER_OFF);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Internal queue hit hilevel mark (%d)! Turning off interrupts\n",
qleft);
}
}
@@ -800,8 +796,9 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
bd_ops->send_stop_character(ch);
ch->ch_stops_sent++;
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Sending stop char! Times sent: %x\n",
+ ch->ch_stops_sent);
}
}
}
@@ -827,8 +824,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
if (ch->ch_flags & CH_RECEIVER_OFF) {
bd_ops->enable_receiver(ch);
ch->ch_flags &= ~(CH_RECEIVER_OFF);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
- "Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Internal queue hit lowlevel mark (%d)! Turning on interrupts\n",
qleft);
}
}
@@ -836,7 +833,8 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
ch->ch_stops_sent = 0;
bd_ops->send_start_character(ch);
- jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
+ jsm_dbg(READ, &ch->ch_bd->pci_dev,
+ "Sending start char!\n");
}
}
}
diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c
new file mode 100644
index 00000000000..cfadf2971b1
--- /dev/null
+++ b/drivers/tty/serial/kgdb_nmi.c
@@ -0,0 +1,391 @@
+/*
+ * KGDB NMI serial console
+ *
+ * Copyright 2010 Google, Inc.
+ * Arve Hjønnevåg <arve@android.com>
+ * Colin Cross <ccross@android.com>
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <anton.vorontsov@linaro.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/atomic.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <linux/kfifo.h>
+#include <linux/kgdb.h>
+#include <linux/kdb.h>
+
+static int kgdb_nmi_knock = 1;
+module_param_named(knock, kgdb_nmi_knock, int, 0600);
+MODULE_PARM_DESC(knock, "if set to 1 (default), the special '$3#33' command " \
+ "must be used to enter the debugger; when set to 0, " \
+ "hitting return key is enough to enter the debugger; " \
+ "when set to -1, the debugger is entered immediately " \
+ "upon NMI");
+
+static char *kgdb_nmi_magic = "$3#33";
+module_param_named(magic, kgdb_nmi_magic, charp, 0600);
+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;
+
+ for (i = 0; i < c; i++)
+ dbg_io_ops->write_char(s[i]);
+}
+
+static struct tty_driver *kgdb_nmi_tty_driver;
+
+static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx)
+{
+ *idx = co->index;
+ return kgdb_nmi_tty_driver;
+}
+
+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,
+ .index = -1,
+};
+
+/*
+ * This is usually the maximum rate on debug ports. We make fifo large enough
+ * to make copy-pasting to the terminal usable.
+ */
+#define KGDB_NMI_BAUD 115200
+#define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ)
+
+struct kgdb_nmi_tty_priv {
+ struct tty_port port;
+ struct timer_list timer;
+ STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
+};
+
+static struct tty_port *kgdb_nmi_port;
+
+static void kgdb_tty_recv(int ch)
+{
+ struct kgdb_nmi_tty_priv *priv;
+ char c = ch;
+
+ if (!kgdb_nmi_port || ch < 0)
+ return;
+ /*
+ * 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 = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port);
+ kfifo_in(&priv->fifo, &c, 1);
+}
+
+static int kgdb_nmi_poll_one_knock(void)
+{
+ static int n;
+ int c = -1;
+ const char *magic = kgdb_nmi_magic;
+ size_t m = strlen(magic);
+ bool printch = 0;
+
+ c = dbg_io_ops->read_char();
+ if (c == NO_POLL_CHAR)
+ return c;
+
+ if (!kgdb_nmi_knock && (c == '\r' || c == '\n')) {
+ return 1;
+ } else if (c == magic[n]) {
+ n = (n + 1) % m;
+ if (!n)
+ return 1;
+ printch = 1;
+ } else {
+ n = 0;
+ }
+
+ if (kgdb_nmi_tty_enabled) {
+ kgdb_tty_recv(c);
+ return 0;
+ }
+
+ if (printch) {
+ kdb_printf("%c", c);
+ return 0;
+ }
+
+ kdb_printf("\r%s %s to enter the debugger> %*s",
+ kgdb_nmi_knock ? "Type" : "Hit",
+ kgdb_nmi_knock ? magic : "<return>", (int)m, "");
+ while (m--)
+ kdb_printf("\b");
+ return 0;
+}
+
+/**
+ * kgdb_nmi_poll_knock - Check if it is time to enter the debugger
+ *
+ * "Serial ports are often noisy, especially when muxed over another port (we
+ * often use serial over the headset connector). Noise on the async command
+ * line just causes characters that are ignored, on a command line that blocked
+ * execution noise would be catastrophic." -- Colin Cross
+ *
+ * So, this function implements KGDB/KDB knocking on the serial line: we won't
+ * enter the debugger until we receive a known magic phrase (which is actually
+ * "$3#33", known as "escape to KDB" command. There is also a relaxed variant
+ * of knocking, i.e. just pressing the return key is enough to enter the
+ * debugger. And if knocking is disabled, the function always returns 1.
+ */
+bool kgdb_nmi_poll_knock(void)
+{
+ if (kgdb_nmi_knock < 0)
+ return 1;
+
+ while (1) {
+ int ret;
+
+ ret = kgdb_nmi_poll_one_knock();
+ if (ret == NO_POLL_CHAR)
+ return 0;
+ else if (ret == 1)
+ break;
+ }
+ return 1;
+}
+
+/*
+ * The tasklet is cheap, it does not cause wakeups when reschedules itself,
+ * instead it waits for the next tick.
+ */
+static void kgdb_nmi_tty_receiver(unsigned long data)
+{
+ struct kgdb_nmi_tty_priv *priv = (void *)data;
+ char ch;
+
+ priv->timer.expires = jiffies + (HZ/100);
+ add_timer(&priv->timer);
+
+ if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo)))
+ return;
+
+ while (kfifo_out(&priv->fifo, &ch, 1))
+ tty_insert_flip_char(&priv->port, ch, TTY_NORMAL);
+ tty_flip_buffer_push(&priv->port);
+}
+
+static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv =
+ container_of(port, struct kgdb_nmi_tty_priv, port);
+
+ kgdb_nmi_port = port;
+ 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 =
+ container_of(port, struct kgdb_nmi_tty_priv, port);
+
+ del_timer(&priv->timer);
+ kgdb_nmi_port = NULL;
+}
+
+static const struct tty_port_operations kgdb_nmi_tty_port_ops = {
+ .activate = kgdb_nmi_tty_activate,
+ .shutdown = kgdb_nmi_tty_shutdown,
+};
+
+static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ INIT_KFIFO(priv->fifo);
+ 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;
+
+ ret = tty_port_install(&priv->port, drv, tty);
+ if (ret) {
+ pr_err("%s: can't install tty port: %d\n", __func__, ret);
+ goto err;
+ }
+ return 0;
+err:
+ tty_port_destroy(&priv->port);
+ kfree(priv);
+ return ret;
+}
+
+static void kgdb_nmi_tty_cleanup(struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty->driver_data = NULL;
+ tty_port_destroy(&priv->port);
+ kfree(priv);
+}
+
+static int kgdb_nmi_tty_open(struct tty_struct *tty, struct file *file)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ return tty_port_open(&priv->port, tty, file);
+}
+
+static void kgdb_nmi_tty_close(struct tty_struct *tty, struct file *file)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty_port_close(&priv->port, tty, file);
+}
+
+static void kgdb_nmi_tty_hangup(struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty_port_hangup(&priv->port);
+}
+
+static int kgdb_nmi_tty_write_room(struct tty_struct *tty)
+{
+ /* Actually, we can handle any amount as we use polled writes. */
+ return 2048;
+}
+
+static int kgdb_nmi_tty_write(struct tty_struct *tty, const unchar *buf, int c)
+{
+ int i;
+
+ for (i = 0; i < c; i++)
+ dbg_io_ops->write_char(buf[i]);
+ return c;
+}
+
+static const struct tty_operations kgdb_nmi_tty_ops = {
+ .open = kgdb_nmi_tty_open,
+ .close = kgdb_nmi_tty_close,
+ .install = kgdb_nmi_tty_install,
+ .cleanup = kgdb_nmi_tty_cleanup,
+ .hangup = kgdb_nmi_tty_hangup,
+ .write_room = kgdb_nmi_tty_write_room,
+ .write = kgdb_nmi_tty_write,
+};
+
+static int kgdb_nmi_enable_console(int argc, const char *argv[])
+{
+ kgdb_nmi_tty_enabled = !(argc == 1 && !strcmp(argv[1], "off"));
+ return 0;
+}
+
+int kgdb_register_nmi_console(void)
+{
+ int ret;
+
+ if (!arch_kgdb_ops.enable_nmi)
+ return 0;
+
+ kgdb_nmi_tty_driver = alloc_tty_driver(1);
+ if (!kgdb_nmi_tty_driver) {
+ pr_err("%s: cannot allocate tty\n", __func__);
+ return -ENOMEM;
+ }
+ kgdb_nmi_tty_driver->driver_name = "ttyNMI";
+ kgdb_nmi_tty_driver->name = "ttyNMI";
+ kgdb_nmi_tty_driver->num = 1;
+ kgdb_nmi_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ kgdb_nmi_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ kgdb_nmi_tty_driver->flags = TTY_DRIVER_REAL_RAW;
+ kgdb_nmi_tty_driver->init_termios = tty_std_termios;
+ tty_termios_encode_baud_rate(&kgdb_nmi_tty_driver->init_termios,
+ KGDB_NMI_BAUD, KGDB_NMI_BAUD);
+ tty_set_operations(kgdb_nmi_tty_driver, &kgdb_nmi_tty_ops);
+
+ ret = tty_register_driver(kgdb_nmi_tty_driver);
+ if (ret) {
+ pr_err("%s: can't register tty driver: %d\n", __func__, ret);
+ goto err_drv_reg;
+ }
+
+ ret = kdb_register("nmi_console", kgdb_nmi_enable_console, "[off]",
+ "switch to Linux NMI console", 0);
+ if (ret) {
+ pr_err("%s: can't register kdb command: %d\n", __func__, ret);
+ goto err_kdb_reg;
+ }
+
+ register_console(&kgdb_nmi_console);
+ arch_kgdb_ops.enable_nmi(1);
+
+ return 0;
+err_kdb_reg:
+ tty_unregister_driver(kgdb_nmi_tty_driver);
+err_drv_reg:
+ put_tty_driver(kgdb_nmi_tty_driver);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kgdb_register_nmi_console);
+
+int kgdb_unregister_nmi_console(void)
+{
+ int ret;
+
+ if (!arch_kgdb_ops.enable_nmi)
+ return 0;
+ arch_kgdb_ops.enable_nmi(0);
+
+ kdb_unregister("nmi_console");
+
+ ret = unregister_console(&kgdb_nmi_console);
+ if (ret)
+ return ret;
+
+ ret = tty_unregister_driver(kgdb_nmi_tty_driver);
+ if (ret)
+ return ret;
+ put_tty_driver(kgdb_nmi_tty_driver);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kgdb_unregister_nmi_console);
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 2b42a01a81c..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;
}
@@ -97,7 +97,8 @@ static void kgdboc_restore_input(void)
static int kgdboc_register_kbd(char **cptr)
{
- if (strncmp(*cptr, "kbd", 3) == 0) {
+ if (strncmp(*cptr, "kbd", 3) == 0 ||
+ strncmp(*cptr, "kdb", 3) == 0) {
if (kdb_poll_idx < KDB_POLL_FUNC_MAX) {
kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char;
kdb_poll_idx++;
@@ -122,7 +123,7 @@ static void kgdboc_unregister_kbd(void)
i--;
}
}
- flush_work_sync(&kgdboc_restore_input_work);
+ flush_work(&kgdboc_restore_input_work);
}
#else /* ! CONFIG_KDB_KEYBOARD */
#define kgdboc_register_kbd(x) 0
@@ -145,6 +146,8 @@ __setup("kgdboc=", kgdboc_option_setup);
static void cleanup_kgdboc(void)
{
+ if (kgdb_unregister_nmi_console())
+ return;
kgdboc_unregister_kbd();
if (configured == 1)
kgdb_unregister_io_module(&kgdboc_io_ops);
@@ -198,11 +201,18 @@ do_register:
if (err)
goto noconfig;
+ err = kgdb_register_nmi_console();
+ if (err)
+ goto nmi_con_failed;
+
configured = 1;
return 0;
+nmi_con_failed:
+ kgdb_unregister_io_module(&kgdboc_io_ops);
noconfig:
+ kgdboc_unregister_kbd();
config[0] = 0;
configured = 0;
cleanup_kgdboc();
diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c
index 96c1cacc736..88d01e0bb0c 100644
--- a/drivers/tty/serial/lantiq.c
+++ b/drivers/tty/serial/lantiq.c
@@ -31,16 +31,19 @@
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/gpio.h>
#include <lantiq_soc.h>
#define PORT_LTQ_ASC 111
#define MAXPORTS 2
#define UART_DUMMY_UER_RX 1
-#define DRVNAME "ltq_asc"
+#define DRVNAME "lantiq,asc"
#ifdef __BIG_ENDIAN
#define LTQ_ASC_TBUF (0x0020 + 3)
#define LTQ_ASC_RBUF (0x0024 + 3)
@@ -114,6 +117,9 @@ static DEFINE_SPINLOCK(ltq_asc_lock);
struct ltq_uart_port {
struct uart_port port;
+ /* clock used to derive divider */
+ struct clk *fpiclk;
+ /* clock gating of the ASC core */
struct clk *clk;
unsigned int tx_irq;
unsigned int rx_irq;
@@ -156,21 +162,16 @@ lqasc_enable_ms(struct uart_port *port)
static int
lqasc_rx_chars(struct uart_port *port)
{
- struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+ struct tty_port *tport = &port->state->port;
unsigned int ch = 0, rsr = 0, fifocnt;
- if (!tty) {
- dev_dbg(port->dev, "%s:tty is busy now", __func__);
- return -EBUSY;
- }
- fifocnt =
- ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK;
+ fifocnt = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK;
while (fifocnt--) {
u8 flag = TTY_NORMAL;
ch = ltq_r8(port->membase + LTQ_ASC_RBUF);
rsr = (ltq_r32(port->membase + LTQ_ASC_STATE)
& ASCSTATE_ANY) | UART_DUMMY_UER_RX;
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
port->icount.rx++;
/*
@@ -202,7 +203,7 @@ lqasc_rx_chars(struct uart_port *port)
}
if ((rsr & port->ignore_status_mask) == 0)
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(tport, ch, flag);
if (rsr & ASCSTATE_ROE)
/*
@@ -210,11 +211,12 @@ lqasc_rx_chars(struct uart_port *port)
* immediately, and doesn't affect the current
* character
*/
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
}
+
if (ch != 0)
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_flip_buffer_push(tport);
+
return 0;
}
@@ -316,7 +318,9 @@ lqasc_startup(struct uart_port *port)
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
int retval;
- port->uartclk = clk_get_rate(ltq_port->clk);
+ if (!IS_ERR(ltq_port->clk))
+ clk_enable(ltq_port->clk);
+ port->uartclk = clk_get_rate(ltq_port->fpiclk);
ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
port->membase + LTQ_ASC_CLC);
@@ -382,6 +386,8 @@ lqasc_shutdown(struct uart_port *port)
port->membase + LTQ_ASC_RXFCON);
ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
port->membase + LTQ_ASC_TXFCON);
+ if (!IS_ERR(ltq_port->clk))
+ clk_disable(ltq_port->clk);
}
static void
@@ -630,7 +636,10 @@ lqasc_console_setup(struct console *co, char *options)
port = &ltq_port->port;
- port->uartclk = clk_get_rate(ltq_port->clk);
+ if (!IS_ERR(ltq_port->clk))
+ clk_enable(ltq_port->clk);
+
+ port->uartclk = clk_get_rate(ltq_port->fpiclk);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -668,37 +677,32 @@ static struct uart_driver lqasc_reg = {
static int __init
lqasc_probe(struct platform_device *pdev)
{
+ struct device_node *node = pdev->dev.of_node;
struct ltq_uart_port *ltq_port;
struct uart_port *port;
- struct resource *mmres, *irqres;
- int tx_irq, rx_irq, err_irq;
- struct clk *clk;
+ struct resource *mmres, irqres[3];
+ int line = 0;
int ret;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!mmres || !irqres)
+ ret = of_irq_to_resource_table(node, irqres, 3);
+ if (!mmres || (ret != 3)) {
+ dev_err(&pdev->dev,
+ "failed to get memory/irq for serial port\n");
return -ENODEV;
+ }
- if (pdev->id >= MAXPORTS)
- return -EBUSY;
+ /* check if this is the console port */
+ if (mmres->start != CPHYSADDR(LTQ_EARLY_ASC))
+ line = 1;
- if (lqasc_port[pdev->id] != NULL)
+ if (lqasc_port[line]) {
+ dev_err(&pdev->dev, "port %d already allocated\n", line);
return -EBUSY;
-
- clk = clk_get(&pdev->dev, "fpi");
- if (IS_ERR(clk)) {
- pr_err("failed to get fpi clk\n");
- return -ENOENT;
}
- tx_irq = platform_get_irq_byname(pdev, "tx");
- rx_irq = platform_get_irq_byname(pdev, "rx");
- err_irq = platform_get_irq_byname(pdev, "err");
- if ((tx_irq < 0) | (rx_irq < 0) | (err_irq < 0))
- return -ENODEV;
-
- ltq_port = kzalloc(sizeof(struct ltq_uart_port), GFP_KERNEL);
+ ltq_port = devm_kzalloc(&pdev->dev, sizeof(struct ltq_uart_port),
+ GFP_KERNEL);
if (!ltq_port)
return -ENOMEM;
@@ -709,19 +713,26 @@ lqasc_probe(struct platform_device *pdev)
port->ops = &lqasc_pops;
port->fifosize = 16;
port->type = PORT_LTQ_ASC,
- port->line = pdev->id;
+ port->line = line;
port->dev = &pdev->dev;
-
- port->irq = tx_irq; /* unused, just to be backward-compatibe */
+ /* unused, just to be backward-compatible */
+ port->irq = irqres[0].start;
port->mapbase = mmres->start;
- ltq_port->clk = clk;
+ ltq_port->fpiclk = clk_get_fpi();
+ if (IS_ERR(ltq_port->fpiclk)) {
+ pr_err("failed to get fpi clk\n");
+ return -ENOENT;
+ }
+
+ /* not all asc ports have clock gates, lets ignore the return code */
+ ltq_port->clk = clk_get(&pdev->dev, NULL);
- ltq_port->tx_irq = tx_irq;
- ltq_port->rx_irq = rx_irq;
- ltq_port->err_irq = err_irq;
+ ltq_port->tx_irq = irqres[0].start;
+ ltq_port->rx_irq = irqres[1].start;
+ ltq_port->err_irq = irqres[2].start;
- lqasc_port[pdev->id] = ltq_port;
+ lqasc_port[line] = ltq_port;
platform_set_drvdata(pdev, ltq_port);
ret = uart_add_one_port(&lqasc_reg, port);
@@ -729,10 +740,17 @@ lqasc_probe(struct platform_device *pdev)
return ret;
}
+static const struct of_device_id ltq_asc_match[] = {
+ { .compatible = DRVNAME },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltq_asc_match);
+
static struct platform_driver lqasc_driver = {
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
+ .of_match_table = ltq_asc_match,
},
};
diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c
new file mode 100644
index 00000000000..701644f0682
--- /dev/null
+++ b/drivers/tty/serial/lpc32xx_hs.c
@@ -0,0 +1,810 @@
+/*
+ * High Speed Serial Ports on NXP LPC32xx SoC
+ *
+ * Authors: Kevin Wells <kevin.wells@nxp.com>
+ * Roland Stigge <stigge@antcom.de>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ * Copyright (C) 2012 Roland Stigge
+ *
+ * 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/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/nmi.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <mach/platform.h>
+#include <mach/hardware.h>
+
+/*
+ * High Speed UART register offsets
+ */
+#define LPC32XX_HSUART_FIFO(x) ((x) + 0x00)
+#define LPC32XX_HSUART_LEVEL(x) ((x) + 0x04)
+#define LPC32XX_HSUART_IIR(x) ((x) + 0x08)
+#define LPC32XX_HSUART_CTRL(x) ((x) + 0x0C)
+#define LPC32XX_HSUART_RATE(x) ((x) + 0x10)
+
+#define LPC32XX_HSU_BREAK_DATA (1 << 10)
+#define LPC32XX_HSU_ERROR_DATA (1 << 9)
+#define LPC32XX_HSU_RX_EMPTY (1 << 8)
+
+#define LPC32XX_HSU_TX_LEV(n) (((n) >> 8) & 0xFF)
+#define LPC32XX_HSU_RX_LEV(n) ((n) & 0xFF)
+
+#define LPC32XX_HSU_TX_INT_SET (1 << 6)
+#define LPC32XX_HSU_RX_OE_INT (1 << 5)
+#define LPC32XX_HSU_BRK_INT (1 << 4)
+#define LPC32XX_HSU_FE_INT (1 << 3)
+#define LPC32XX_HSU_RX_TIMEOUT_INT (1 << 2)
+#define LPC32XX_HSU_RX_TRIG_INT (1 << 1)
+#define LPC32XX_HSU_TX_INT (1 << 0)
+
+#define LPC32XX_HSU_HRTS_INV (1 << 21)
+#define LPC32XX_HSU_HRTS_TRIG_8B (0x0 << 19)
+#define LPC32XX_HSU_HRTS_TRIG_16B (0x1 << 19)
+#define LPC32XX_HSU_HRTS_TRIG_32B (0x2 << 19)
+#define LPC32XX_HSU_HRTS_TRIG_48B (0x3 << 19)
+#define LPC32XX_HSU_HRTS_EN (1 << 18)
+#define LPC32XX_HSU_TMO_DISABLED (0x0 << 16)
+#define LPC32XX_HSU_TMO_INACT_4B (0x1 << 16)
+#define LPC32XX_HSU_TMO_INACT_8B (0x2 << 16)
+#define LPC32XX_HSU_TMO_INACT_16B (0x3 << 16)
+#define LPC32XX_HSU_HCTS_INV (1 << 15)
+#define LPC32XX_HSU_HCTS_EN (1 << 14)
+#define LPC32XX_HSU_OFFSET(n) ((n) << 9)
+#define LPC32XX_HSU_BREAK (1 << 8)
+#define LPC32XX_HSU_ERR_INT_EN (1 << 7)
+#define LPC32XX_HSU_RX_INT_EN (1 << 6)
+#define LPC32XX_HSU_TX_INT_EN (1 << 5)
+#define LPC32XX_HSU_RX_TL1B (0x0 << 2)
+#define LPC32XX_HSU_RX_TL4B (0x1 << 2)
+#define LPC32XX_HSU_RX_TL8B (0x2 << 2)
+#define LPC32XX_HSU_RX_TL16B (0x3 << 2)
+#define LPC32XX_HSU_RX_TL32B (0x4 << 2)
+#define LPC32XX_HSU_RX_TL48B (0x5 << 2)
+#define LPC32XX_HSU_TX_TLEMPTY (0x0 << 0)
+#define LPC32XX_HSU_TX_TL0B (0x0 << 0)
+#define LPC32XX_HSU_TX_TL4B (0x1 << 0)
+#define LPC32XX_HSU_TX_TL8B (0x2 << 0)
+#define LPC32XX_HSU_TX_TL16B (0x3 << 0)
+
+#define MODNAME "lpc32xx_hsuart"
+
+struct lpc32xx_hsuart_port {
+ struct uart_port port;
+};
+
+#define FIFO_READ_LIMIT 128
+#define MAX_PORTS 3
+#define LPC32XX_TTY_NAME "ttyTX"
+static struct lpc32xx_hsuart_port lpc32xx_hs_ports[MAX_PORTS];
+
+#ifdef CONFIG_SERIAL_HS_LPC32XX_CONSOLE
+static void wait_for_xmit_empty(struct uart_port *port)
+{
+ unsigned int timeout = 10000;
+
+ do {
+ if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL(
+ port->membase))) == 0)
+ break;
+ if (--timeout == 0)
+ break;
+ udelay(1);
+ } while (1);
+}
+
+static void wait_for_xmit_ready(struct uart_port *port)
+{
+ unsigned int timeout = 10000;
+
+ while (1) {
+ if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL(
+ port->membase))) < 32)
+ break;
+ if (--timeout == 0)
+ break;
+ udelay(1);
+ }
+}
+
+static void lpc32xx_hsuart_console_putchar(struct uart_port *port, int ch)
+{
+ wait_for_xmit_ready(port);
+ writel((u32)ch, LPC32XX_HSUART_FIFO(port->membase));
+}
+
+static void lpc32xx_hsuart_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct lpc32xx_hsuart_port *up = &lpc32xx_hs_ports[co->index];
+ unsigned long flags;
+ int locked = 1;
+
+ touch_nmi_watchdog();
+ 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);
+
+ uart_console_write(&up->port, s, count, lpc32xx_hsuart_console_putchar);
+ wait_for_xmit_empty(&up->port);
+
+ if (locked)
+ spin_unlock(&up->port.lock);
+ local_irq_restore(flags);
+}
+
+static int __init lpc32xx_hsuart_console_setup(struct console *co,
+ char *options)
+{
+ struct uart_port *port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= MAX_PORTS)
+ co->index = 0;
+
+ port = &lpc32xx_hs_ports[co->index].port;
+ if (!port->membase)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver lpc32xx_hsuart_reg;
+static struct console lpc32xx_hsuart_console = {
+ .name = LPC32XX_TTY_NAME,
+ .write = lpc32xx_hsuart_console_write,
+ .device = uart_console_device,
+ .setup = lpc32xx_hsuart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &lpc32xx_hsuart_reg,
+};
+
+static int __init lpc32xx_hsuart_console_init(void)
+{
+ register_console(&lpc32xx_hsuart_console);
+ return 0;
+}
+console_initcall(lpc32xx_hsuart_console_init);
+
+#define LPC32XX_HSUART_CONSOLE (&lpc32xx_hsuart_console)
+#else
+#define LPC32XX_HSUART_CONSOLE NULL
+#endif
+
+static struct uart_driver lpc32xx_hs_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = MODNAME,
+ .dev_name = LPC32XX_TTY_NAME,
+ .nr = MAX_PORTS,
+ .cons = LPC32XX_HSUART_CONSOLE,
+};
+static int uarts_registered;
+
+static unsigned int __serial_get_clock_div(unsigned long uartclk,
+ unsigned long rate)
+{
+ u32 div, goodrate, hsu_rate, l_hsu_rate, comprate;
+ u32 rate_diff;
+
+ /* Find the closest divider to get the desired clock rate */
+ div = uartclk / rate;
+ goodrate = hsu_rate = (div / 14) - 1;
+ if (hsu_rate != 0)
+ hsu_rate--;
+
+ /* Tweak divider */
+ l_hsu_rate = hsu_rate + 3;
+ rate_diff = 0xFFFFFFFF;
+
+ while (hsu_rate < l_hsu_rate) {
+ comprate = uartclk / ((hsu_rate + 1) * 14);
+ if (abs(comprate - rate) < rate_diff) {
+ goodrate = hsu_rate;
+ rate_diff = abs(comprate - rate);
+ }
+
+ hsu_rate++;
+ }
+ if (hsu_rate > 0xFF)
+ hsu_rate = 0xFF;
+
+ return goodrate;
+}
+
+static void __serial_uart_flush(struct uart_port *port)
+{
+ u32 tmp;
+ int cnt = 0;
+
+ while ((readl(LPC32XX_HSUART_LEVEL(port->membase)) > 0) &&
+ (cnt++ < FIFO_READ_LIMIT))
+ tmp = readl(LPC32XX_HSUART_FIFO(port->membase));
+}
+
+static void __serial_lpc32xx_rx(struct uart_port *port)
+{
+ struct tty_port *tport = &port->state->port;
+ unsigned int tmp, flag;
+
+ /* Read data from FIFO and push into terminal */
+ tmp = readl(LPC32XX_HSUART_FIFO(port->membase));
+ while (!(tmp & LPC32XX_HSU_RX_EMPTY)) {
+ flag = TTY_NORMAL;
+ port->icount.rx++;
+
+ if (tmp & LPC32XX_HSU_ERROR_DATA) {
+ /* Framing error */
+ writel(LPC32XX_HSU_FE_INT,
+ LPC32XX_HSUART_IIR(port->membase));
+ port->icount.frame++;
+ flag = TTY_FRAME;
+ tty_insert_flip_char(tport, 0, TTY_FRAME);
+ }
+
+ tty_insert_flip_char(tport, (tmp & 0xFF), flag);
+
+ 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)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int tmp;
+
+ if (port->x_char) {
+ writel((u32)port->x_char, LPC32XX_HSUART_FIFO(port->membase));
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ goto exit_tx;
+
+ /* Transfer data */
+ while (LPC32XX_HSU_TX_LEV(readl(
+ LPC32XX_HSUART_LEVEL(port->membase))) < 64) {
+ writel((u32) xmit->buf[xmit->tail],
+ LPC32XX_HSUART_FIFO(port->membase));
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+exit_tx:
+ if (uart_circ_empty(xmit)) {
+ tmp = readl(LPC32XX_HSUART_CTRL(port->membase));
+ tmp &= ~LPC32XX_HSU_TX_INT_EN;
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+ }
+}
+
+static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ struct tty_port *tport = &port->state->port;
+ u32 status;
+
+ spin_lock(&port->lock);
+
+ /* Read UART status and clear latched interrupts */
+ status = readl(LPC32XX_HSUART_IIR(port->membase));
+
+ if (status & LPC32XX_HSU_BRK_INT) {
+ /* Break received */
+ writel(LPC32XX_HSU_BRK_INT, LPC32XX_HSUART_IIR(port->membase));
+ port->icount.brk++;
+ uart_handle_break(port);
+ }
+
+ /* Framing error */
+ if (status & LPC32XX_HSU_FE_INT)
+ writel(LPC32XX_HSU_FE_INT, LPC32XX_HSUART_IIR(port->membase));
+
+ if (status & LPC32XX_HSU_RX_OE_INT) {
+ /* Receive FIFO overrun */
+ writel(LPC32XX_HSU_RX_OE_INT,
+ LPC32XX_HSUART_IIR(port->membase));
+ port->icount.overrun++;
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+ tty_schedule_flip(tport);
+ }
+
+ /* Data received? */
+ if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT))
+ __serial_lpc32xx_rx(port);
+
+ /* Transmit data request? */
+ if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) {
+ writel(LPC32XX_HSU_TX_INT, LPC32XX_HSUART_IIR(port->membase));
+ __serial_lpc32xx_tx(port);
+ }
+
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* port->lock is not held. */
+static unsigned int serial_lpc32xx_tx_empty(struct uart_port *port)
+{
+ unsigned int ret = 0;
+
+ if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL(port->membase))) == 0)
+ ret = TIOCSER_TEMT;
+
+ return ret;
+}
+
+/* port->lock held by caller. */
+static void serial_lpc32xx_set_mctrl(struct uart_port *port,
+ unsigned int mctrl)
+{
+ /* No signals are supported on HS UARTs */
+}
+
+/* port->lock is held by caller and interrupts are disabled. */
+static unsigned int serial_lpc32xx_get_mctrl(struct uart_port *port)
+{
+ /* No signals are supported on HS UARTs */
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+/* port->lock held by caller. */
+static void serial_lpc32xx_stop_tx(struct uart_port *port)
+{
+ u32 tmp;
+
+ tmp = readl(LPC32XX_HSUART_CTRL(port->membase));
+ tmp &= ~LPC32XX_HSU_TX_INT_EN;
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+}
+
+/* port->lock held by caller. */
+static void serial_lpc32xx_start_tx(struct uart_port *port)
+{
+ u32 tmp;
+
+ __serial_lpc32xx_tx(port);
+ tmp = readl(LPC32XX_HSUART_CTRL(port->membase));
+ tmp |= LPC32XX_HSU_TX_INT_EN;
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+}
+
+/* port->lock held by caller. */
+static void serial_lpc32xx_stop_rx(struct uart_port *port)
+{
+ u32 tmp;
+
+ tmp = readl(LPC32XX_HSUART_CTRL(port->membase));
+ tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN);
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+
+ writel((LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT |
+ LPC32XX_HSU_FE_INT), LPC32XX_HSUART_IIR(port->membase));
+}
+
+/* port->lock held by caller. */
+static void serial_lpc32xx_enable_ms(struct uart_port *port)
+{
+ /* Modem status is not supported */
+}
+
+/* port->lock is not held. */
+static void serial_lpc32xx_break_ctl(struct uart_port *port,
+ int break_state)
+{
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&port->lock, flags);
+ tmp = readl(LPC32XX_HSUART_CTRL(port->membase));
+ if (break_state != 0)
+ tmp |= LPC32XX_HSU_BREAK;
+ else
+ tmp &= ~LPC32XX_HSU_BREAK;
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* LPC3250 Errata HSUART.1: Hang workaround via loopback mode on inactivity */
+static void lpc32xx_loopback_set(resource_size_t mapbase, int state)
+{
+ int bit;
+ u32 tmp;
+
+ switch (mapbase) {
+ case LPC32XX_HS_UART1_BASE:
+ bit = 0;
+ break;
+ case LPC32XX_HS_UART2_BASE:
+ bit = 1;
+ break;
+ case LPC32XX_HS_UART7_BASE:
+ bit = 6;
+ break;
+ default:
+ WARN(1, "lpc32xx_hs: Warning: Unknown port at %08x\n", mapbase);
+ return;
+ }
+
+ tmp = readl(LPC32XX_UARTCTL_CLOOP);
+ if (state)
+ tmp |= (1 << bit);
+ else
+ tmp &= ~(1 << bit);
+ writel(tmp, LPC32XX_UARTCTL_CLOOP);
+}
+
+/* port->lock is not held. */
+static int serial_lpc32xx_startup(struct uart_port *port)
+{
+ int retval;
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ __serial_uart_flush(port);
+
+ writel((LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT |
+ LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT),
+ LPC32XX_HSUART_IIR(port->membase));
+
+ writel(0xFF, LPC32XX_HSUART_RATE(port->membase));
+
+ /*
+ * Set receiver timeout, HSU offset of 20, no break, no interrupts,
+ * and default FIFO trigger levels
+ */
+ tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B |
+ LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B;
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+
+ lpc32xx_loopback_set(port->mapbase, 0); /* get out of loopback mode */
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ retval = request_irq(port->irq, serial_lpc32xx_interrupt,
+ 0, MODNAME, port);
+ if (!retval)
+ writel((tmp | LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN),
+ LPC32XX_HSUART_CTRL(port->membase));
+
+ return retval;
+}
+
+/* port->lock is not held. */
+static void serial_lpc32xx_shutdown(struct uart_port *port)
+{
+ u32 tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B |
+ LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B;
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+
+ lpc32xx_loopback_set(port->mapbase, 1); /* go to loopback mode */
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ free_irq(port->irq, port);
+}
+
+/* port->lock is not held. */
+static void serial_lpc32xx_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned long flags;
+ unsigned int baud, quot;
+ u32 tmp;
+
+ /* Always 8-bit, no parity, 1 stop bit */
+ termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
+ termios->c_cflag |= CS8;
+
+ termios->c_cflag &= ~(HUPCL | CMSPAR | CLOCAL | CRTSCTS);
+
+ baud = uart_get_baud_rate(port, termios, old, 0,
+ port->uartclk / 14);
+
+ quot = __serial_get_clock_div(port->uartclk, baud);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* Ignore characters? */
+ tmp = readl(LPC32XX_HSUART_CTRL(port->membase));
+ if ((termios->c_cflag & CREAD) == 0)
+ tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN);
+ else
+ tmp |= LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN;
+ writel(tmp, LPC32XX_HSUART_CTRL(port->membase));
+
+ writel(quot, LPC32XX_HSUART_RATE(port->membase));
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+static const char *serial_lpc32xx_type(struct uart_port *port)
+{
+ return MODNAME;
+}
+
+static void serial_lpc32xx_release_port(struct uart_port *port)
+{
+ if ((port->iotype == UPIO_MEM32) && (port->mapbase)) {
+ if (port->flags & UPF_IOREMAP) {
+ iounmap(port->membase);
+ port->membase = NULL;
+ }
+
+ release_mem_region(port->mapbase, SZ_4K);
+ }
+}
+
+static int serial_lpc32xx_request_port(struct uart_port *port)
+{
+ int ret = -ENODEV;
+
+ if ((port->iotype == UPIO_MEM32) && (port->mapbase)) {
+ ret = 0;
+
+ if (!request_mem_region(port->mapbase, SZ_4K, MODNAME))
+ ret = -EBUSY;
+ else if (port->flags & UPF_IOREMAP) {
+ port->membase = ioremap(port->mapbase, SZ_4K);
+ if (!port->membase) {
+ release_mem_region(port->mapbase, SZ_4K);
+ ret = -ENOMEM;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void serial_lpc32xx_config_port(struct uart_port *port, int uflags)
+{
+ int ret;
+
+ ret = serial_lpc32xx_request_port(port);
+ if (ret < 0)
+ return;
+ port->type = PORT_UART00;
+ port->fifosize = 64;
+
+ __serial_uart_flush(port);
+
+ writel((LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT |
+ LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT),
+ LPC32XX_HSUART_IIR(port->membase));
+
+ writel(0xFF, LPC32XX_HSUART_RATE(port->membase));
+
+ /* Set receiver timeout, HSU offset of 20, no break, no interrupts,
+ and default FIFO trigger levels */
+ writel(LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B |
+ LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B,
+ LPC32XX_HSUART_CTRL(port->membase));
+}
+
+static int serial_lpc32xx_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ int ret = 0;
+
+ if (ser->type != PORT_UART00)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static struct uart_ops serial_lpc32xx_pops = {
+ .tx_empty = serial_lpc32xx_tx_empty,
+ .set_mctrl = serial_lpc32xx_set_mctrl,
+ .get_mctrl = serial_lpc32xx_get_mctrl,
+ .stop_tx = serial_lpc32xx_stop_tx,
+ .start_tx = serial_lpc32xx_start_tx,
+ .stop_rx = serial_lpc32xx_stop_rx,
+ .enable_ms = serial_lpc32xx_enable_ms,
+ .break_ctl = serial_lpc32xx_break_ctl,
+ .startup = serial_lpc32xx_startup,
+ .shutdown = serial_lpc32xx_shutdown,
+ .set_termios = serial_lpc32xx_set_termios,
+ .type = serial_lpc32xx_type,
+ .release_port = serial_lpc32xx_release_port,
+ .request_port = serial_lpc32xx_request_port,
+ .config_port = serial_lpc32xx_config_port,
+ .verify_port = serial_lpc32xx_verify_port,
+};
+
+/*
+ * Register a set of serial devices attached to a platform device
+ */
+static int serial_hs_lpc32xx_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_hsuart_port *p = &lpc32xx_hs_ports[uarts_registered];
+ int ret = 0;
+ struct resource *res;
+
+ if (uarts_registered >= MAX_PORTS) {
+ dev_err(&pdev->dev,
+ "Error: Number of possible ports exceeded (%d)!\n",
+ uarts_registered + 1);
+ return -ENXIO;
+ }
+
+ memset(p, 0, sizeof(*p));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Error getting mem resource for HS UART port %d\n",
+ uarts_registered);
+ return -ENXIO;
+ }
+ p->port.mapbase = res->start;
+ p->port.membase = NULL;
+
+ p->port.irq = platform_get_irq(pdev, 0);
+ if (p->port.irq < 0) {
+ dev_err(&pdev->dev, "Error getting irq for HS UART port %d\n",
+ uarts_registered);
+ return p->port.irq;
+ }
+
+ p->port.iotype = UPIO_MEM32;
+ p->port.uartclk = LPC32XX_MAIN_OSC_FREQ;
+ p->port.regshift = 2;
+ p->port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP;
+ p->port.dev = &pdev->dev;
+ p->port.ops = &serial_lpc32xx_pops;
+ p->port.line = uarts_registered++;
+ spin_lock_init(&p->port.lock);
+
+ /* send port to loopback mode by default */
+ lpc32xx_loopback_set(p->port.mapbase, 1);
+
+ ret = uart_add_one_port(&lpc32xx_hs_reg, &p->port);
+
+ platform_set_drvdata(pdev, p);
+
+ return ret;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int serial_hs_lpc32xx_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&lpc32xx_hs_reg, &p->port);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int serial_hs_lpc32xx_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev);
+
+ uart_suspend_port(&lpc32xx_hs_reg, &p->port);
+
+ return 0;
+}
+
+static int serial_hs_lpc32xx_resume(struct platform_device *pdev)
+{
+ struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev);
+
+ uart_resume_port(&lpc32xx_hs_reg, &p->port);
+
+ return 0;
+}
+#else
+#define serial_hs_lpc32xx_suspend NULL
+#define serial_hs_lpc32xx_resume NULL
+#endif
+
+static const struct of_device_id serial_hs_lpc32xx_dt_ids[] = {
+ { .compatible = "nxp,lpc3220-hsuart" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids);
+
+static struct platform_driver serial_hs_lpc32xx_driver = {
+ .probe = serial_hs_lpc32xx_probe,
+ .remove = serial_hs_lpc32xx_remove,
+ .suspend = serial_hs_lpc32xx_suspend,
+ .resume = serial_hs_lpc32xx_resume,
+ .driver = {
+ .name = MODNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = serial_hs_lpc32xx_dt_ids,
+ },
+};
+
+static int __init lpc32xx_hsuart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&lpc32xx_hs_reg);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&serial_hs_lpc32xx_driver);
+ if (ret)
+ uart_unregister_driver(&lpc32xx_hs_reg);
+
+ return ret;
+}
+
+static void __exit lpc32xx_hsuart_exit(void)
+{
+ platform_driver_unregister(&serial_hs_lpc32xx_driver);
+ uart_unregister_driver(&lpc32xx_hs_reg);
+}
+
+module_init(lpc32xx_hsuart_init);
+module_exit(lpc32xx_hsuart_exit);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("NXP LPC32XX High Speed UART driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c
index 94a6792bf97..5702828fb62 100644
--- a/drivers/tty/serial/m32r_sio.c
+++ b/drivers/tty/serial/m32r_sio.c
@@ -38,15 +38,12 @@
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/serial.h>
-#include <linux/serialP.h>
#include <linux/delay.h>
#include <asm/m32r.h>
#include <asm/io.h>
#include <asm/irq.h>
-#define PORT_M32R_BASE PORT_M32R_SIO
-#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1)
#define BAUD_RATE 115200
#include <linux/serial_core.h>
@@ -70,13 +67,6 @@
#define PASS_LIMIT 256
-/*
- * We default to IRQ0 for the "no irq" hack. Some
- * machine types want others as well - they're free
- * to redefine this in their header file.
- */
-#define is_real_interrupt(irq) ((irq) != 0)
-
#define BASE_BAUD 115200
/* Standard COM flags */
@@ -140,22 +130,6 @@ struct irq_info {
static struct irq_info irq_lists[NR_IRQS];
-/*
- * Here we define the default xmit fifo size used for each type of UART.
- */
-static const struct serial_uart_config uart_config[] = {
- [PORT_UNKNOWN] = {
- .name = "unknown",
- .dfl_xmit_fifo_size = 1,
- .flags = 0,
- },
- [PORT_INDEX(PORT_M32R_SIO)] = {
- .name = "M32RSIO",
- .dfl_xmit_fifo_size = 1,
- .flags = 0,
- },
-};
-
#ifdef CONFIG_SERIAL_M32R_PLDSIO
#define __sio_in(x) inw((unsigned long)(x))
@@ -292,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
@@ -326,7 +302,7 @@ static void m32r_sio_enable_ms(struct uart_port *port)
static void receive_chars(struct uart_sio_port *up, int *status)
{
- struct tty_struct *tty = up->port.state->port.tty;
+ struct tty_port *port = &up->port.state->port;
unsigned char ch;
unsigned char flag;
int max_count = 256;
@@ -381,7 +357,7 @@ static void receive_chars(struct uart_sio_port *up, int *status)
if (uart_handle_sysrq_char(&up->port, ch))
goto ignore_char;
if ((*status & up->port.ignore_status_mask) == 0)
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(port, ch, flag);
if (*status & UART_LSR_OE) {
/*
@@ -389,12 +365,15 @@ static void receive_chars(struct uart_sio_port *up, int *status)
* immediately, and doesn't affect the current
* character.
*/
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
}
ignore_char:
*status = serial_in(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
- tty_flip_buffer_push(tty);
+
+ spin_unlock(&up->port.lock);
+ tty_flip_buffer_push(port);
+ spin_lock(&up->port.lock);
}
static void transmit_chars(struct uart_sio_port *up)
@@ -640,7 +619,7 @@ static int m32r_sio_startup(struct uart_port *port)
* hardware interrupt, we use a timer-based system. The original
* driver used to do this with IRQ0.
*/
- if (!is_real_interrupt(up->port.irq)) {
+ if (!up->port.irq) {
unsigned int timeout = up->port.timeout;
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
@@ -687,7 +666,7 @@ static void m32r_sio_shutdown(struct uart_port *port)
sio_init();
- if (!is_real_interrupt(up->port.irq))
+ if (!up->port.irq)
del_timer_sync(&up->timer);
else
serial_unlink_irq_chain(up);
@@ -760,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;
/*
@@ -915,8 +894,7 @@ static void m32r_sio_config_port(struct uart_port *port, int unused)
spin_lock_irqsave(&up->port.lock, flags);
- up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1);
- up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
+ up->port.fifosize = 1;
spin_unlock_irqrestore(&up->port.lock, flags);
}
@@ -924,23 +902,11 @@ static void m32r_sio_config_port(struct uart_port *port, int unused)
static int
m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser)
{
- if (ser->irq >= nr_irqs || ser->irq < 0 ||
- ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
- ser->type >= ARRAY_SIZE(uart_config))
+ if (ser->irq >= nr_irqs || ser->irq < 0 || ser->baud_base < 9600)
return -EINVAL;
return 0;
}
-static const char *
-m32r_sio_type(struct uart_port *port)
-{
- int type = port->type;
-
- if (type >= ARRAY_SIZE(uart_config))
- type = 0;
- return uart_config[type].name;
-}
-
static struct uart_ops m32r_sio_pops = {
.tx_empty = m32r_sio_tx_empty,
.set_mctrl = m32r_sio_set_mctrl,
@@ -954,7 +920,6 @@ static struct uart_ops m32r_sio_pops = {
.shutdown = m32r_sio_shutdown,
.set_termios = m32r_sio_set_termios,
.pm = m32r_sio_pm,
- .type = m32r_sio_type,
.release_port = m32r_sio_release_port,
.request_port = m32r_sio_request_port,
.config_port = m32r_sio_config_port,
diff --git a/drivers/tty/serial/m32r_sio.h b/drivers/tty/serial/m32r_sio.h
index e9b7e11793b..8129824496c 100644
--- a/drivers/tty/serial/m32r_sio.h
+++ b/drivers/tty/serial/m32r_sio.h
@@ -15,6 +15,7 @@
* (at your option) any later version.
*/
+#include <linux/pci.h>
struct m32r_sio_probe {
struct module *owner;
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index b4902b99cfd..79f9a9eff54 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -179,8 +179,7 @@ static void max3100_work(struct work_struct *w);
static void max3100_dowork(struct max3100_port *s)
{
- if (!s->force_end_work && !work_pending(&s->work) &&
- !freezing(current) && !s->suspending)
+ if (!s->force_end_work && !freezing(current) && !s->suspending)
queue_work(s->workqueue, &s->work);
}
@@ -311,8 +310,8 @@ static void max3100_work(struct work_struct *w)
}
}
- if (rxchars > 16 && s->port.state->port.tty != NULL) {
- tty_flip_buffer_push(s->port.state->port.tty);
+ if (rxchars > 16) {
+ tty_flip_buffer_push(&s->port.state->port);
rxchars = 0;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -324,8 +323,8 @@ static void max3100_work(struct work_struct *w)
(!uart_circ_empty(xmit) &&
!uart_tx_stopped(&s->port))));
- if (rxchars > 0 && s->port.state->port.tty != NULL)
- tty_flip_buffer_push(s->port.state->port.tty);
+ if (rxchars > 0)
+ tty_flip_buffer_push(&s->port.state->port);
}
static irqreturn_t max3100_irq(int irqno, void *dev_id)
@@ -530,7 +529,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios,
MAX3100_STATUS_OE;
/* we are sending char from a workqueue so enable */
- s->port.state->port.tty->low_latency = 1;
+ s->port.state->port.low_latency = 1;
if (s->poll_time > 0)
del_timer_sync(&s->timer);
@@ -742,7 +741,7 @@ static struct uart_driver max3100_uart_driver = {
};
static int uart_driver_registered;
-static int __devinit max3100_probe(struct spi_device *spi)
+static int max3100_probe(struct spi_device *spi)
{
int i, retval;
struct plat_max3100 *pdata;
@@ -779,8 +778,8 @@ static int __devinit max3100_probe(struct spi_device *spi)
max3100s[i]->spi = spi;
max3100s[i]->irq = spi->irq;
spin_lock_init(&max3100s[i]->conf_lock);
- dev_set_drvdata(&spi->dev, max3100s[i]);
- pdata = spi->dev.platform_data;
+ spi_set_drvdata(spi, max3100s[i]);
+ 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;
@@ -818,23 +817,25 @@ static int __devinit max3100_probe(struct spi_device *spi)
return 0;
}
-static int __devexit max3100_remove(struct spi_device *spi)
+static int max3100_remove(struct spi_device *spi)
{
- struct max3100_port *s = dev_get_drvdata(&spi->dev);
+ struct max3100_port *s = spi_get_drvdata(spi);
int i;
mutex_lock(&max3100s_lock);
/* find out the index for the chip we are removing */
for (i = 0; i < MAX_MAX3100; i++)
- if (max3100s[i] == s)
+ if (max3100s[i] == s) {
+ dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
+ uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
+ kfree(max3100s[i]);
+ max3100s[i] = NULL;
break;
+ }
- dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
- uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
- kfree(max3100s[i]);
- max3100s[i] = NULL;
-
+ WARN_ON(i == MAX_MAX3100);
+
/* check if this is the last chip we have */
for (i = 0; i < MAX_MAX3100; i++)
if (max3100s[i]) {
@@ -848,11 +849,11 @@ static int __devexit max3100_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
-static int max3100_suspend(struct spi_device *spi, pm_message_t state)
+static int max3100_suspend(struct device *dev)
{
- struct max3100_port *s = dev_get_drvdata(&spi->dev);
+ struct max3100_port *s = dev_get_drvdata(dev);
dev_dbg(&s->spi->dev, "%s\n", __func__);
@@ -873,9 +874,9 @@ static int max3100_suspend(struct spi_device *spi, pm_message_t state)
return 0;
}
-static int max3100_resume(struct spi_device *spi)
+static int max3100_resume(struct device *dev)
{
- struct max3100_port *s = dev_get_drvdata(&spi->dev);
+ struct max3100_port *s = dev_get_drvdata(dev);
dev_dbg(&s->spi->dev, "%s\n", __func__);
@@ -893,34 +894,24 @@ static int max3100_resume(struct spi_device *spi)
return 0;
}
+static SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume);
+#define MAX3100_PM_OPS (&max3100_pm_ops)
+
#else
-#define max3100_suspend NULL
-#define max3100_resume NULL
+#define MAX3100_PM_OPS NULL
#endif
static struct spi_driver max3100_driver = {
.driver = {
.name = "max3100",
.owner = THIS_MODULE,
+ .pm = MAX3100_PM_OPS,
},
-
.probe = max3100_probe,
- .remove = __devexit_p(max3100_remove),
- .suspend = max3100_suspend,
- .resume = max3100_resume,
+ .remove = max3100_remove,
};
-static int __init max3100_init(void)
-{
- return spi_register_driver(&max3100_driver);
-}
-module_init(max3100_init);
-
-static void __exit max3100_exit(void)
-{
- spi_unregister_driver(&max3100_driver);
-}
-module_exit(max3100_exit);
+module_spi_driver(max3100_driver);
MODULE_DESCRIPTION("MAX3100 driver");
MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c
deleted file mode 100644
index 17c7ba805d9..00000000000
--- a/drivers/tty/serial/max3107.c
+++ /dev/null
@@ -1,1215 +0,0 @@
-/*
- * max3107.c - spi uart protocol driver for Maxim 3107
- * Based on max3100.c
- * by Christian Pellegrin <chripell@evolware.org>
- * and max3110.c
- * by Feng Tang <feng.tang@intel.com>
- *
- * Copyright (C) Aavamobile 2009
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/serial_core.h>
-#include <linux/serial.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/gpio.h>
-#include <linux/spi/spi.h>
-#include <linux/freezer.h>
-#include <linux/module.h>
-#include "max3107.h"
-
-static const struct baud_table brg26_ext[] = {
- { 300, MAX3107_BRG26_B300 },
- { 600, MAX3107_BRG26_B600 },
- { 1200, MAX3107_BRG26_B1200 },
- { 2400, MAX3107_BRG26_B2400 },
- { 4800, MAX3107_BRG26_B4800 },
- { 9600, MAX3107_BRG26_B9600 },
- { 19200, MAX3107_BRG26_B19200 },
- { 57600, MAX3107_BRG26_B57600 },
- { 115200, MAX3107_BRG26_B115200 },
- { 230400, MAX3107_BRG26_B230400 },
- { 460800, MAX3107_BRG26_B460800 },
- { 921600, MAX3107_BRG26_B921600 },
- { 0, 0 }
-};
-
-static const struct baud_table brg13_int[] = {
- { 300, MAX3107_BRG13_IB300 },
- { 600, MAX3107_BRG13_IB600 },
- { 1200, MAX3107_BRG13_IB1200 },
- { 2400, MAX3107_BRG13_IB2400 },
- { 4800, MAX3107_BRG13_IB4800 },
- { 9600, MAX3107_BRG13_IB9600 },
- { 19200, MAX3107_BRG13_IB19200 },
- { 57600, MAX3107_BRG13_IB57600 },
- { 115200, MAX3107_BRG13_IB115200 },
- { 230400, MAX3107_BRG13_IB230400 },
- { 460800, MAX3107_BRG13_IB460800 },
- { 921600, MAX3107_BRG13_IB921600 },
- { 0, 0 }
-};
-
-static u32 get_new_brg(int baud, struct max3107_port *s)
-{
- int i;
- const struct baud_table *baud_tbl = s->baud_tbl;
-
- for (i = 0; i < 13; i++) {
- if (baud == baud_tbl[i].baud)
- return baud_tbl[i].new_brg;
- }
-
- return 0;
-}
-
-/* Perform SPI transfer for write/read of device register(s) */
-int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len)
-{
- struct spi_message spi_msg;
- struct spi_transfer spi_xfer;
-
- /* Initialize SPI ,message */
- spi_message_init(&spi_msg);
-
- /* Initialize SPI transfer */
- memset(&spi_xfer, 0, sizeof spi_xfer);
- spi_xfer.len = len;
- spi_xfer.tx_buf = tx;
- spi_xfer.rx_buf = rx;
- spi_xfer.speed_hz = MAX3107_SPI_SPEED;
-
- /* Add SPI transfer to SPI message */
- spi_message_add_tail(&spi_xfer, &spi_msg);
-
-#ifdef DBG_TRACE_SPI_DATA
- {
- int i;
- pr_info("tx len %d:\n", spi_xfer.len);
- for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
- pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]);
- pr_info("\n");
- }
-#endif
-
- /* Perform synchronous SPI transfer */
- if (spi_sync(s->spi, &spi_msg)) {
- dev_err(&s->spi->dev, "spi_sync failure\n");
- return -EIO;
- }
-
-#ifdef DBG_TRACE_SPI_DATA
- if (spi_xfer.rx_buf) {
- int i;
- pr_info("rx len %d:\n", spi_xfer.len);
- for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
- pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]);
- pr_info("\n");
- }
-#endif
- return 0;
-}
-EXPORT_SYMBOL_GPL(max3107_rw);
-
-/* Puts received data to circular buffer */
-static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data,
- int len)
-{
- struct uart_port *port = &s->port;
- struct tty_struct *tty;
-
- if (!port->state)
- return;
-
- tty = port->state->port.tty;
- if (!tty)
- return;
-
- /* Insert received data */
- tty_insert_flip_string(tty, data, len);
- /* Update RX counter */
- port->icount.rx += len;
-}
-
-/* Handle data receiving */
-static void max3107_handlerx(struct max3107_port *s, u16 rxlvl)
-{
- int i;
- int j;
- int len; /* SPI transfer buffer length */
- u16 *buf;
- u8 *valid_str;
-
- if (!s->rx_enabled)
- /* RX is disabled */
- return;
-
- if (rxlvl == 0) {
- /* RX fifo is empty */
- return;
- } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) {
- dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl);
- /* Ensure sanity of RX level */
- rxlvl = MAX3107_RX_FIFO_SIZE;
- }
- if ((s->rxbuf == 0) || (s->rxstr == 0)) {
- dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n");
- return;
- }
- buf = s->rxbuf;
- valid_str = s->rxstr;
- while (rxlvl) {
- pr_debug("rxlvl %d\n", rxlvl);
- /* Clear buffer */
- memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2));
- len = 0;
- if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) {
- /* First disable RX FIFO interrupt */
- pr_debug("Disabling RX INT\n");
- buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
- s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT;
- buf[0] |= s->irqen_reg;
- len++;
- }
- /* Just increase the length by amount of words in FIFO since
- * buffer was zeroed and SPI transfer of 0x0000 means reading
- * from RX FIFO
- */
- len += rxlvl;
- /* Append RX level query */
- buf[len] = MAX3107_RXFIFOLVL_REG;
- len++;
-
- /* Perform the SPI transfer */
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) {
- dev_err(&s->spi->dev, "SPI transfer for RX h failed\n");
- return;
- }
-
- /* Skip RX FIFO interrupt disabling word if it was added */
- j = ((len - 1) - rxlvl);
- /* Read received words */
- for (i = 0; i < rxlvl; i++, j++)
- valid_str[i] = (u8)buf[j];
- put_data_to_circ_buf(s, valid_str, rxlvl);
- /* Get new RX level */
- rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK);
- }
-
- if (s->rx_enabled) {
- /* RX still enabled, re-enable RX FIFO interrupt */
- pr_debug("Enabling RX INT\n");
- buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
- s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
- buf[0] |= s->irqen_reg;
- if (max3107_rw(s, (u8 *)buf, NULL, 2))
- dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n");
- }
-
- /* Push the received data to receivers */
- if (s->port.state->port.tty)
- tty_flip_buffer_push(s->port.state->port.tty);
-}
-
-
-/* Handle data sending */
-static void max3107_handletx(struct max3107_port *s)
-{
- struct circ_buf *xmit = &s->port.state->xmit;
- int i;
- unsigned long flags;
- int len; /* SPI transfer buffer length */
- u16 *buf;
-
- if (!s->tx_fifo_empty)
- /* Don't send more data before previous data is sent */
- return;
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
- /* No data to send or TX is stopped */
- return;
-
- if (!s->txbuf) {
- dev_warn(&s->spi->dev, "Txbuf isn't ready\n");
- return;
- }
- buf = s->txbuf;
- /* Get length of data pending in circular buffer */
- len = uart_circ_chars_pending(xmit);
- if (len) {
- /* Limit to size of TX FIFO */
- if (len > MAX3107_TX_FIFO_SIZE)
- len = MAX3107_TX_FIFO_SIZE;
-
- pr_debug("txlen %d\n", len);
-
- /* Update TX counter */
- s->port.icount.tx += len;
-
- /* TX FIFO will no longer be empty */
- s->tx_fifo_empty = 0;
-
- i = 0;
- if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) {
- /* First disable TX empty interrupt */
- pr_debug("Disabling TE INT\n");
- buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
- s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT;
- buf[i] |= s->irqen_reg;
- i++;
- len++;
- }
- /* Add data to send */
- spin_lock_irqsave(&s->port.lock, flags);
- for ( ; i < len ; i++) {
- buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG);
- buf[i] |= ((u16)xmit->buf[xmit->tail] &
- MAX3107_SPI_TX_DATA_MASK);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- }
- spin_unlock_irqrestore(&s->port.lock, flags);
- if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) {
- /* Enable TX empty interrupt */
- pr_debug("Enabling TE INT\n");
- buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
- s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT;
- buf[i] |= s->irqen_reg;
- i++;
- len++;
- }
- if (!s->tx_enabled) {
- /* Enable TX */
- pr_debug("Enable TX\n");
- buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
- spin_lock_irqsave(&s->data_lock, flags);
- s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT;
- buf[i] |= s->mode1_reg;
- spin_unlock_irqrestore(&s->data_lock, flags);
- s->tx_enabled = 1;
- i++;
- len++;
- }
-
- /* Perform the SPI transfer */
- if (max3107_rw(s, (u8 *)buf, NULL, len*2)) {
- dev_err(&s->spi->dev,
- "SPI transfer TX handling failed\n");
- return;
- }
- }
-
- /* Indicate wake up if circular buffer is getting low on data */
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&s->port);
-
-}
-
-/* Handle interrupts
- * Also reads and returns current RX FIFO level
- */
-static u16 handle_interrupt(struct max3107_port *s)
-{
- u16 buf[4]; /* Buffer for SPI transfers */
- u8 irq_status;
- u16 rx_level;
- unsigned long flags;
-
- /* Read IRQ status register */
- buf[0] = MAX3107_IRQSTS_REG;
- /* Read status IRQ status register */
- buf[1] = MAX3107_STS_IRQSTS_REG;
- /* Read LSR IRQ status register */
- buf[2] = MAX3107_LSR_IRQSTS_REG;
- /* Query RX level */
- buf[3] = MAX3107_RXFIFOLVL_REG;
-
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) {
- dev_err(&s->spi->dev,
- "SPI transfer for INTR handling failed\n");
- return 0;
- }
-
- irq_status = (u8)buf[0];
- pr_debug("IRQSTS %x\n", irq_status);
- rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK);
-
- if (irq_status & MAX3107_IRQ_LSR_BIT) {
- /* LSR interrupt */
- if (buf[2] & MAX3107_LSR_RXTO_BIT)
- /* RX timeout interrupt,
- * handled by normal RX handling
- */
- pr_debug("RX TO INT\n");
- }
-
- if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) {
- /* Tx empty interrupt,
- * disable TX and set tx_fifo_empty flag
- */
- pr_debug("TE INT, disabling TX\n");
- buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
- spin_lock_irqsave(&s->data_lock, flags);
- s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
- buf[0] |= s->mode1_reg;
- spin_unlock_irqrestore(&s->data_lock, flags);
- if (max3107_rw(s, (u8 *)buf, NULL, 2))
- dev_err(&s->spi->dev, "SPI transfer TX dis failed\n");
- s->tx_enabled = 0;
- s->tx_fifo_empty = 1;
- }
-
- if (irq_status & MAX3107_IRQ_RXFIFO_BIT)
- /* RX FIFO interrupt,
- * handled by normal RX handling
- */
- pr_debug("RFIFO INT\n");
-
- /* Return RX level */
- return rx_level;
-}
-
-/* Trigger work thread*/
-static void max3107_dowork(struct max3107_port *s)
-{
- if (!work_pending(&s->work) && !freezing(current) && !s->suspended)
- queue_work(s->workqueue, &s->work);
- else
- dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n");
-}
-
-/* Work thread */
-static void max3107_work(struct work_struct *w)
-{
- struct max3107_port *s = container_of(w, struct max3107_port, work);
- u16 rxlvl = 0;
- int len; /* SPI transfer buffer length */
- u16 buf[5]; /* Buffer for SPI transfers */
- unsigned long flags;
-
- /* Start by reading current RX FIFO level */
- buf[0] = MAX3107_RXFIFOLVL_REG;
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
- dev_err(&s->spi->dev, "SPI transfer RX lev failed\n");
- rxlvl = 0;
- } else {
- rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK);
- }
-
- do {
- pr_debug("rxlvl %d\n", rxlvl);
-
- /* Handle RX */
- max3107_handlerx(s, rxlvl);
- rxlvl = 0;
-
- if (s->handle_irq) {
- /* Handle pending interrupts
- * We also get new RX FIFO level since new data may
- * have been received while pushing received data to
- * receivers
- */
- s->handle_irq = 0;
- rxlvl = handle_interrupt(s);
- }
-
- /* Handle TX */
- max3107_handletx(s);
-
- /* Handle configuration changes */
- len = 0;
- spin_lock_irqsave(&s->data_lock, flags);
- if (s->mode1_commit) {
- pr_debug("mode1_commit\n");
- buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
- buf[len++] |= s->mode1_reg;
- s->mode1_commit = 0;
- }
- if (s->lcr_commit) {
- pr_debug("lcr_commit\n");
- buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG);
- buf[len++] |= s->lcr_reg;
- s->lcr_commit = 0;
- }
- if (s->brg_commit) {
- pr_debug("brg_commit\n");
- buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG);
- buf[len++] |= ((s->brg_cfg >> 16) &
- MAX3107_SPI_TX_DATA_MASK);
- buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG);
- buf[len++] |= ((s->brg_cfg >> 8) &
- MAX3107_SPI_TX_DATA_MASK);
- buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG);
- buf[len++] |= ((s->brg_cfg) & 0xff);
- s->brg_commit = 0;
- }
- spin_unlock_irqrestore(&s->data_lock, flags);
-
- if (len > 0) {
- if (max3107_rw(s, (u8 *)buf, NULL, len * 2))
- dev_err(&s->spi->dev,
- "SPI transfer config failed\n");
- }
-
- /* Reloop if interrupt handling indicated data in RX FIFO */
- } while (rxlvl);
-
-}
-
-/* Set sleep mode */
-static void max3107_set_sleep(struct max3107_port *s, int mode)
-{
- u16 buf[1]; /* Buffer for SPI transfer */
- unsigned long flags;
- pr_debug("enter, mode %d\n", mode);
-
- buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
- spin_lock_irqsave(&s->data_lock, flags);
- switch (mode) {
- case MAX3107_DISABLE_FORCED_SLEEP:
- s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT;
- break;
- case MAX3107_ENABLE_FORCED_SLEEP:
- s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT;
- break;
- case MAX3107_DISABLE_AUTOSLEEP:
- s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT;
- break;
- case MAX3107_ENABLE_AUTOSLEEP:
- s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT;
- break;
- default:
- spin_unlock_irqrestore(&s->data_lock, flags);
- dev_warn(&s->spi->dev, "invalid sleep mode\n");
- return;
- }
- buf[0] |= s->mode1_reg;
- spin_unlock_irqrestore(&s->data_lock, flags);
-
- if (max3107_rw(s, (u8 *)buf, NULL, 2))
- dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n");
-
- if (mode == MAX3107_DISABLE_AUTOSLEEP ||
- mode == MAX3107_DISABLE_FORCED_SLEEP)
- msleep(MAX3107_WAKEUP_DELAY);
-}
-
-/* Perform full register initialization */
-static void max3107_register_init(struct max3107_port *s)
-{
- u16 buf[11]; /* Buffer for SPI transfers */
-
- /* 1. Configure baud rate, 9600 as default */
- s->baud = 9600;
- /* the below is default*/
- if (s->ext_clk) {
- s->brg_cfg = MAX3107_BRG26_B9600;
- s->baud_tbl = (struct baud_table *)brg26_ext;
- } else {
- s->brg_cfg = MAX3107_BRG13_IB9600;
- s->baud_tbl = (struct baud_table *)brg13_int;
- }
-
- if (s->pdata->init)
- s->pdata->init(s);
-
- buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG)
- | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK);
- buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG)
- | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK);
- buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG)
- | ((s->brg_cfg) & 0xff);
-
- /* 2. Configure LCR register, 8N1 mode by default */
- s->lcr_reg = MAX3107_LCR_WORD_LEN_8;
- buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG)
- | s->lcr_reg;
-
- /* 3. Configure MODE 1 register */
- s->mode1_reg = 0;
- /* Enable IRQ pin */
- s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT;
- /* Disable TX */
- s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
- s->tx_enabled = 0;
- /* RX is enabled */
- s->rx_enabled = 1;
- buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG)
- | s->mode1_reg;
-
- /* 4. Configure MODE 2 register */
- buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
- if (s->loopback) {
- /* Enable loopback */
- buf[5] |= MAX3107_MODE2_LOOPBACK_BIT;
- }
- /* Reset FIFOs */
- buf[5] |= MAX3107_MODE2_FIFORST_BIT;
- s->tx_fifo_empty = 1;
-
- /* 5. Configure FIFO trigger level register */
- buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG);
- /* RX FIFO trigger for 16 words, TX FIFO trigger not used */
- buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0));
-
- /* 6. Configure flow control levels */
- buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG);
- /* Flow control halt level 96, resume level 48 */
- buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96));
-
- /* 7. Configure flow control */
- buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG);
- /* Enable auto CTS and auto RTS flow control */
- buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT);
-
- /* 8. Configure RX timeout register */
- buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG);
- /* Timeout after 48 character intervals */
- buf[9] |= 0x0030;
-
- /* 9. Configure LSR interrupt enable register */
- buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG);
- /* Enable RX timeout interrupt */
- buf[10] |= MAX3107_LSR_RXTO_BIT;
-
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, NULL, 22))
- dev_err(&s->spi->dev, "SPI transfer for init failed\n");
-
- /* 10. Clear IRQ status register by reading it */
- buf[0] = MAX3107_IRQSTS_REG;
-
- /* 11. Configure interrupt enable register */
- /* Enable LSR interrupt */
- s->irqen_reg = MAX3107_IRQ_LSR_BIT;
- /* Enable RX FIFO interrupt */
- s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
- buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG)
- | s->irqen_reg;
-
- /* 12. Clear FIFO reset that was set in step 6 */
- buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
- if (s->loopback) {
- /* Keep loopback enabled */
- buf[2] |= MAX3107_MODE2_LOOPBACK_BIT;
- }
-
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6))
- dev_err(&s->spi->dev, "SPI transfer for init failed\n");
-
-}
-
-/* IRQ handler */
-static irqreturn_t max3107_irq(int irqno, void *dev_id)
-{
- struct max3107_port *s = dev_id;
-
- if (irqno != s->spi->irq) {
- /* Unexpected IRQ */
- return IRQ_NONE;
- }
-
- /* Indicate irq */
- s->handle_irq = 1;
-
- /* Trigger work thread */
- max3107_dowork(s);
-
- return IRQ_HANDLED;
-}
-
-/* HW suspension function
- *
- * Currently autosleep is used to decrease current consumption, alternative
- * approach would be to set the chip to reset mode if UART is not being
- * used but that would mess the GPIOs
- *
- */
-void max3107_hw_susp(struct max3107_port *s, int suspend)
-{
- pr_debug("enter, suspend %d\n", suspend);
-
- if (suspend) {
- /* Suspend requested,
- * enable autosleep to decrease current consumption
- */
- s->suspended = 1;
- max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP);
- } else {
- /* Resume requested,
- * disable autosleep
- */
- s->suspended = 0;
- max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP);
- }
-}
-EXPORT_SYMBOL_GPL(max3107_hw_susp);
-
-/* Modem status IRQ enabling */
-static void max3107_enable_ms(struct uart_port *port)
-{
- /* Modem status not supported */
-}
-
-/* Data send function */
-static void max3107_start_tx(struct uart_port *port)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
-
- /* Trigger work thread for sending data */
- max3107_dowork(s);
-}
-
-/* Function for checking that there is no pending transfers */
-static unsigned int max3107_tx_empty(struct uart_port *port)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
-
- pr_debug("returning %d\n",
- (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit)));
- return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit);
-}
-
-/* Function for stopping RX */
-static void max3107_stop_rx(struct uart_port *port)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
- unsigned long flags;
-
- /* Set RX disabled in MODE 1 register */
- spin_lock_irqsave(&s->data_lock, flags);
- s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT;
- s->mode1_commit = 1;
- spin_unlock_irqrestore(&s->data_lock, flags);
- /* Set RX disabled */
- s->rx_enabled = 0;
- /* Trigger work thread for doing the actual configuration change */
- max3107_dowork(s);
-}
-
-/* Function for returning control pin states */
-static unsigned int max3107_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;
-}
-
-/* Function for setting control pin states */
-static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- /* DCD and DSR are not wired and CTS/RTS is hadnled automatically
- * so do nothing
- */
-}
-
-/* Function for configuring UART parameters */
-static void max3107_set_termios(struct uart_port *port,
- struct ktermios *termios,
- struct ktermios *old)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
- struct tty_struct *tty;
- int baud;
- u16 new_lcr = 0;
- u32 new_brg = 0;
- unsigned long flags;
-
- if (!port->state)
- return;
-
- tty = port->state->port.tty;
- if (!tty)
- return;
-
- /* Get new LCR register values */
- /* Word size */
- if ((termios->c_cflag & CSIZE) == CS7)
- new_lcr |= MAX3107_LCR_WORD_LEN_7;
- else
- new_lcr |= MAX3107_LCR_WORD_LEN_8;
-
- /* Parity */
- if (termios->c_cflag & PARENB) {
- new_lcr |= MAX3107_LCR_PARITY_BIT;
- if (!(termios->c_cflag & PARODD))
- new_lcr |= MAX3107_LCR_EVENPARITY_BIT;
- }
-
- /* Stop bits */
- if (termios->c_cflag & CSTOPB) {
- /* 2 stop bits */
- new_lcr |= MAX3107_LCR_STOPLEN_BIT;
- }
-
- /* Mask termios capabilities we don't support */
- termios->c_cflag &= ~CMSPAR;
-
- /* Set status ignore mask */
- s->port.ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- s->port.ignore_status_mask |= MAX3107_ALL_ERRORS;
-
- /* Set low latency to immediately handle pushed data */
- s->port.state->port.tty->low_latency = 1;
-
- /* Get new baud rate generator configuration */
- baud = tty_get_baud_rate(tty);
-
- spin_lock_irqsave(&s->data_lock, flags);
- new_brg = get_new_brg(baud, s);
- /* if can't find the corrent config, use previous */
- if (!new_brg) {
- baud = s->baud;
- new_brg = s->brg_cfg;
- }
- spin_unlock_irqrestore(&s->data_lock, flags);
- tty_termios_encode_baud_rate(termios, baud, baud);
- s->baud = baud;
-
- /* Update timeout according to new baud rate */
- uart_update_timeout(port, termios->c_cflag, baud);
-
- spin_lock_irqsave(&s->data_lock, flags);
- if (s->lcr_reg != new_lcr) {
- s->lcr_reg = new_lcr;
- s->lcr_commit = 1;
- }
- if (s->brg_cfg != new_brg) {
- s->brg_cfg = new_brg;
- s->brg_commit = 1;
- }
- spin_unlock_irqrestore(&s->data_lock, flags);
-
- /* Trigger work thread for doing the actual configuration change */
- max3107_dowork(s);
-}
-
-/* Port shutdown function */
-static void max3107_shutdown(struct uart_port *port)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
-
- if (s->suspended && s->pdata->hw_suspend)
- s->pdata->hw_suspend(s, 0);
-
- /* Free the interrupt */
- free_irq(s->spi->irq, s);
-
- if (s->workqueue) {
- /* Flush and destroy work queue */
- flush_workqueue(s->workqueue);
- destroy_workqueue(s->workqueue);
- s->workqueue = NULL;
- }
-
- /* Suspend HW */
- if (s->pdata->hw_suspend)
- s->pdata->hw_suspend(s, 1);
-}
-
-/* Port startup function */
-static int max3107_startup(struct uart_port *port)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
-
- /* Initialize work queue */
- s->workqueue = create_freezable_workqueue("max3107");
- if (!s->workqueue) {
- dev_err(&s->spi->dev, "Workqueue creation failed\n");
- return -EBUSY;
- }
- INIT_WORK(&s->work, max3107_work);
-
- /* Setup IRQ */
- if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING,
- "max3107", s)) {
- dev_err(&s->spi->dev, "IRQ reguest failed\n");
- destroy_workqueue(s->workqueue);
- s->workqueue = NULL;
- return -EBUSY;
- }
-
- /* Resume HW */
- if (s->pdata->hw_suspend)
- s->pdata->hw_suspend(s, 0);
-
- /* Init registers */
- max3107_register_init(s);
-
- return 0;
-}
-
-/* Port type function */
-static const char *max3107_type(struct uart_port *port)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
- return s->spi->modalias;
-}
-
-/* Port release function */
-static void max3107_release_port(struct uart_port *port)
-{
- /* Do nothing */
-}
-
-/* Port request function */
-static int max3107_request_port(struct uart_port *port)
-{
- /* Do nothing */
- return 0;
-}
-
-/* Port config function */
-static void max3107_config_port(struct uart_port *port, int flags)
-{
- struct max3107_port *s = container_of(port, struct max3107_port, port);
- s->port.type = PORT_MAX3107;
-}
-
-/* Port verify function */
-static int max3107_verify_port(struct uart_port *port,
- struct serial_struct *ser)
-{
- if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107)
- return 0;
-
- return -EINVAL;
-}
-
-/* Port stop TX function */
-static void max3107_stop_tx(struct uart_port *port)
-{
- /* Do nothing */
-}
-
-/* Port break control function */
-static void max3107_break_ctl(struct uart_port *port, int break_state)
-{
- /* We don't support break control, do nothing */
-}
-
-
-/* Port functions */
-static struct uart_ops max3107_ops = {
- .tx_empty = max3107_tx_empty,
- .set_mctrl = max3107_set_mctrl,
- .get_mctrl = max3107_get_mctrl,
- .stop_tx = max3107_stop_tx,
- .start_tx = max3107_start_tx,
- .stop_rx = max3107_stop_rx,
- .enable_ms = max3107_enable_ms,
- .break_ctl = max3107_break_ctl,
- .startup = max3107_startup,
- .shutdown = max3107_shutdown,
- .set_termios = max3107_set_termios,
- .type = max3107_type,
- .release_port = max3107_release_port,
- .request_port = max3107_request_port,
- .config_port = max3107_config_port,
- .verify_port = max3107_verify_port,
-};
-
-/* UART driver data */
-static struct uart_driver max3107_uart_driver = {
- .owner = THIS_MODULE,
- .driver_name = "ttyMAX",
- .dev_name = "ttyMAX",
- .nr = 1,
-};
-
-static int driver_registered = 0;
-
-
-
-/* 'Generic' platform data */
-static struct max3107_plat generic_plat_data = {
- .loopback = 0,
- .ext_clk = 1,
- .hw_suspend = max3107_hw_susp,
- .polled_mode = 0,
- .poll_time = 0,
-};
-
-
-/*******************************************************************/
-
-/**
- * max3107_probe - SPI bus probe entry point
- * @spi: the spi device
- *
- * SPI wants us to probe this device and if appropriate claim it.
- * Perform any platform specific requirements and then initialise
- * the device.
- */
-
-int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata)
-{
- struct max3107_port *s;
- u16 buf[2]; /* Buffer for SPI transfers */
- int retval;
-
- pr_info("enter max3107 probe\n");
-
- /* Allocate port structure */
- s = kzalloc(sizeof(*s), GFP_KERNEL);
- if (!s) {
- pr_err("Allocating port structure failed\n");
- return -ENOMEM;
- }
-
- s->pdata = pdata;
-
- /* SPI Rx buffer
- * +2 for RX FIFO interrupt
- * disabling and RX level query
- */
- s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL);
- if (!s->rxbuf) {
- pr_err("Allocating RX buffer failed\n");
- retval = -ENOMEM;
- goto err_free4;
- }
- s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL);
- if (!s->rxstr) {
- pr_err("Allocating RX buffer failed\n");
- retval = -ENOMEM;
- goto err_free3;
- }
- /* SPI Tx buffer
- * SPI transfer buffer
- * +3 for TX FIFO empty
- * interrupt disabling and
- * enabling and TX enabling
- */
- s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL);
- if (!s->txbuf) {
- pr_err("Allocating TX buffer failed\n");
- retval = -ENOMEM;
- goto err_free2;
- }
- /* Initialize shared data lock */
- spin_lock_init(&s->data_lock);
-
- /* SPI intializations */
- dev_set_drvdata(&spi->dev, s);
- spi->mode = SPI_MODE_0;
- spi->dev.platform_data = pdata;
- spi->bits_per_word = 16;
- s->ext_clk = pdata->ext_clk;
- s->loopback = pdata->loopback;
- spi_setup(spi);
- s->spi = spi;
-
- /* Check REV ID to ensure we are talking to what we expect */
- buf[0] = MAX3107_REVID_REG;
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
- dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n");
- retval = -EIO;
- goto err_free1;
- }
- if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 &&
- (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) {
- dev_err(&s->spi->dev, "REVID %x does not match\n",
- (buf[0] & MAX3107_SPI_RX_DATA_MASK));
- retval = -ENODEV;
- goto err_free1;
- }
-
- /* Disable all interrupts */
- buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000);
- buf[0] |= 0x0000;
-
- /* Configure clock source */
- buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG);
- if (s->ext_clk) {
- /* External clock */
- buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT;
- }
-
- /* PLL bypass ON */
- buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT;
-
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
- dev_err(&s->spi->dev, "SPI transfer for init failed\n");
- retval = -EIO;
- goto err_free1;
- }
-
- /* Register UART driver */
- if (!driver_registered) {
- retval = uart_register_driver(&max3107_uart_driver);
- if (retval) {
- dev_err(&s->spi->dev, "Registering UART driver failed\n");
- goto err_free1;
- }
- driver_registered = 1;
- }
-
- /* Initialize UART port data */
- s->port.fifosize = 128;
- s->port.ops = &max3107_ops;
- s->port.line = 0;
- s->port.dev = &spi->dev;
- s->port.uartclk = 9600;
- s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
- s->port.irq = s->spi->irq;
- s->port.type = PORT_MAX3107;
-
- /* Add UART port */
- retval = uart_add_one_port(&max3107_uart_driver, &s->port);
- if (retval < 0) {
- dev_err(&s->spi->dev, "Adding UART port failed\n");
- goto err_free1;
- }
-
- if (pdata->configure) {
- retval = pdata->configure(s);
- if (retval < 0)
- goto err_free1;
- }
-
- /* Go to suspend mode */
- if (pdata->hw_suspend)
- pdata->hw_suspend(s, 1);
-
- return 0;
-
-err_free1:
- kfree(s->txbuf);
-err_free2:
- kfree(s->rxstr);
-err_free3:
- kfree(s->rxbuf);
-err_free4:
- kfree(s);
- return retval;
-}
-EXPORT_SYMBOL_GPL(max3107_probe);
-
-/* Driver remove function */
-int max3107_remove(struct spi_device *spi)
-{
- struct max3107_port *s = dev_get_drvdata(&spi->dev);
-
- pr_info("enter max3107 remove\n");
-
- /* Remove port */
- if (uart_remove_one_port(&max3107_uart_driver, &s->port))
- dev_warn(&s->spi->dev, "Removing UART port failed\n");
-
-
- /* Free TxRx buffer */
- kfree(s->rxbuf);
- kfree(s->rxstr);
- kfree(s->txbuf);
-
- /* Free port structure */
- kfree(s);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(max3107_remove);
-
-/* Driver suspend function */
-int max3107_suspend(struct spi_device *spi, pm_message_t state)
-{
-#ifdef CONFIG_PM
- struct max3107_port *s = dev_get_drvdata(&spi->dev);
-
- pr_debug("enter suspend\n");
-
- /* Suspend UART port */
- uart_suspend_port(&max3107_uart_driver, &s->port);
-
- /* Go to suspend mode */
- if (s->pdata->hw_suspend)
- s->pdata->hw_suspend(s, 1);
-#endif /* CONFIG_PM */
- return 0;
-}
-EXPORT_SYMBOL_GPL(max3107_suspend);
-
-/* Driver resume function */
-int max3107_resume(struct spi_device *spi)
-{
-#ifdef CONFIG_PM
- struct max3107_port *s = dev_get_drvdata(&spi->dev);
-
- pr_debug("enter resume\n");
-
- /* Resume from suspend */
- if (s->pdata->hw_suspend)
- s->pdata->hw_suspend(s, 0);
-
- /* Resume UART port */
- uart_resume_port(&max3107_uart_driver, &s->port);
-#endif /* CONFIG_PM */
- return 0;
-}
-EXPORT_SYMBOL_GPL(max3107_resume);
-
-static int max3107_probe_generic(struct spi_device *spi)
-{
- return max3107_probe(spi, &generic_plat_data);
-}
-
-/* Spi driver data */
-static struct spi_driver max3107_driver = {
- .driver = {
- .name = "max3107",
- .owner = THIS_MODULE,
- },
- .probe = max3107_probe_generic,
- .remove = __devexit_p(max3107_remove),
- .suspend = max3107_suspend,
- .resume = max3107_resume,
-};
-
-/* Driver init function */
-static int __init max3107_init(void)
-{
- pr_info("enter max3107 init\n");
- return spi_register_driver(&max3107_driver);
-}
-
-/* Driver exit function */
-static void __exit max3107_exit(void)
-{
- pr_info("enter max3107 exit\n");
- /* Unregister UART driver */
- if (driver_registered)
- uart_unregister_driver(&max3107_uart_driver);
- spi_unregister_driver(&max3107_driver);
-}
-
-module_init(max3107_init);
-module_exit(max3107_exit);
-
-MODULE_DESCRIPTION("MAX3107 driver");
-MODULE_AUTHOR("Aavamobile");
-MODULE_ALIAS("spi:max3107");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/max3107.h b/drivers/tty/serial/max3107.h
deleted file mode 100644
index 8415fc723b9..00000000000
--- a/drivers/tty/serial/max3107.h
+++ /dev/null
@@ -1,441 +0,0 @@
-/*
- * max3107.h - spi uart protocol driver header for Maxim 3107
- *
- * Copyright (C) Aavamobile 2009
- * Based on serial_max3100.h by Christian Pellegrin
- *
- * 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.
- */
-
-#ifndef _MAX3107_H
-#define _MAX3107_H
-
-/* Serial error status definitions */
-#define MAX3107_PARITY_ERROR 1
-#define MAX3107_FRAME_ERROR 2
-#define MAX3107_OVERRUN_ERROR 4
-#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \
- MAX3107_FRAME_ERROR | \
- MAX3107_OVERRUN_ERROR)
-
-/* GPIO definitions */
-#define MAX3107_GPIO_BASE 88
-#define MAX3107_GPIO_COUNT 4
-
-
-/* GPIO connected to chip's reset pin */
-#define MAX3107_RESET_GPIO 87
-
-
-/* Chip reset delay */
-#define MAX3107_RESET_DELAY 10
-
-/* Chip wakeup delay */
-#define MAX3107_WAKEUP_DELAY 50
-
-
-/* Sleep mode definitions */
-#define MAX3107_DISABLE_FORCED_SLEEP 0
-#define MAX3107_ENABLE_FORCED_SLEEP 1
-#define MAX3107_DISABLE_AUTOSLEEP 2
-#define MAX3107_ENABLE_AUTOSLEEP 3
-
-
-/* Definitions for register access with SPI transfers
- *
- * SPI transfer format:
- *
- * Master to slave bits xzzzzzzzyyyyyyyy
- * Slave to master bits aaaaaaaabbbbbbbb
- *
- * where:
- * x = 0 for reads, 1 for writes
- * z = register address
- * y = new register value if write, 0 if read
- * a = unspecified
- * b = register value if read, unspecified if write
- */
-
-/* SPI speed */
-#define MAX3107_SPI_SPEED (3125000 * 2)
-
-/* Write bit */
-#define MAX3107_WRITE_BIT (1 << 15)
-
-/* SPI TX data mask */
-#define MAX3107_SPI_RX_DATA_MASK (0x00ff)
-
-/* SPI RX data mask */
-#define MAX3107_SPI_TX_DATA_MASK (0x00ff)
-
-/* Register access masks */
-#define MAX3107_RHR_REG (0x0000) /* RX FIFO */
-#define MAX3107_THR_REG (0x0000) /* TX FIFO */
-#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */
-#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */
-#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */
-#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */
-#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */
-#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */
-#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */
-#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */
-#define MAX3107_MODE1_REG (0x0900) /* MODE1 */
-#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */
-#define MAX3107_LCR_REG (0x0b00) /* LCR */
-#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */
-#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */
-#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */
-#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */
-#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */
-#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */
-#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */
-#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */
-#define MAX3107_XON1_REG (0x1400) /* XON1 character */
-#define MAX3107_XON2_REG (0x1500) /* XON2 character */
-#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */
-#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */
-#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */
-#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */
-#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */
-#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */
-#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */
-#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */
-#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */
-#define MAX3107_REVID_REG (0x1f00) /* Revision identification */
-
-/* IRQ register bits */
-#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
-#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */
-#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */
-#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */
-#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */
-#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */
-#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */
-#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */
-
-/* LSR register bits */
-#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */
-#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */
-#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */
-#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */
-#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */
-#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */
-#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */
-#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */
-
-/* Special character register bits */
-#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */
-#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */
-#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */
-#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */
-#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */
-#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */
-#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */
-#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */
-
-/* Status register bits */
-#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */
-#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */
-#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */
-#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */
-#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */
-#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */
-#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */
-#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */
-
-/* MODE1 register bits */
-#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */
-#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */
-#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */
-#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */
-#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */
-#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */
-#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */
-#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */
-
-/* MODE2 register bits */
-#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */
-#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */
-#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */
-#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */
-#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */
-#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */
-#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */
-#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */
-
-/* LCR register bits */
-#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */
-#define MAX3107_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 MAX3107_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 MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */
-#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */
-#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */
-#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */
-#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */
-#define MAX3107_LCR_WORD_LEN_5 (0x0000)
-#define MAX3107_LCR_WORD_LEN_6 (0x0001)
-#define MAX3107_LCR_WORD_LEN_7 (0x0002)
-#define MAX3107_LCR_WORD_LEN_8 (0x0003)
-
-
-/* IRDA register bits */
-#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */
-#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */
-#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */
-#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */
-#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */
-#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */
-#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */
-#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */
-
-/* Flow control trigger level register masks */
-#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */
-#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */
-#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f)
-#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4)
-
-/* FIFO interrupt trigger level register masks */
-#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */
-#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */
-#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f)
-#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4)
-
-/* Flow control register bits */
-#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */
-#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */
-#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs
- * are used in conjunction with
- * XOFF2 for definition of
- * special character */
-#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */
-#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */
-#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1
- *
- * SWFLOW bits 1 & 0 table:
- * 00 -> no transmitter flow
- * control
- * 01 -> receiver compares
- * XON2 and XOFF2
- * and controls
- * transmitter
- * 10 -> receiver compares
- * XON1 and XOFF1
- * and controls
- * transmitter
- * 11 -> receiver compares
- * XON1, XON2, XOFF1 and
- * XOFF2 and controls
- * transmitter
- */
-#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */
-#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3
- *
- * SWFLOW bits 3 & 2 table:
- * 00 -> no received flow
- * control
- * 01 -> transmitter generates
- * XON2 and XOFF2
- * 10 -> transmitter generates
- * XON1 and XOFF1
- * 11 -> transmitter generates
- * XON1, XON2, XOFF1 and
- * XOFF2
- */
-
-/* GPIO configuration register bits */
-#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */
-#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */
-#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */
-#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */
-#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */
-#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */
-#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */
-#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */
-
-/* GPIO DATA register bits */
-#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */
-#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */
-#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */
-#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */
-#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */
-#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */
-#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */
-#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */
-
-/* PLL configuration register masks */
-#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */
-#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */
-
-/* Baud rate generator configuration register masks and bits */
-#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of
- * Baud rate generator divisor
- */
-#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */
-#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */
-#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */
-#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */
-
-/* Clock source register bits */
-#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */
-#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */
-#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */
-#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */
-#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */
-#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */
-#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */
-#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */
-
-
-/* HW definitions */
-#define MAX3107_RX_FIFO_SIZE 128
-#define MAX3107_TX_FIFO_SIZE 128
-#define MAX3107_REVID1 0x00a0
-#define MAX3107_REVID2 0x00a1
-
-
-/* Baud rate generator configuration values for external clock 13MHz */
-#define MAX3107_BRG13_B300 (0x0A9400 | 0x05)
-#define MAX3107_BRG13_B600 (0x054A00 | 0x03)
-#define MAX3107_BRG13_B1200 (0x02A500 | 0x01)
-#define MAX3107_BRG13_B2400 (0x015200 | 0x09)
-#define MAX3107_BRG13_B4800 (0x00A900 | 0x04)
-#define MAX3107_BRG13_B9600 (0x005400 | 0x0A)
-#define MAX3107_BRG13_B19200 (0x002A00 | 0x05)
-#define MAX3107_BRG13_B38400 (0x001500 | 0x03)
-#define MAX3107_BRG13_B57600 (0x000E00 | 0x02)
-#define MAX3107_BRG13_B115200 (0x000700 | 0x01)
-#define MAX3107_BRG13_B230400 (0x000300 | 0x08)
-#define MAX3107_BRG13_B460800 (0x000100 | 0x0c)
-#define MAX3107_BRG13_B921600 (0x000100 | 0x1c)
-
-/* Baud rate generator configuration values for external clock 26MHz */
-#define MAX3107_BRG26_B300 (0x152800 | 0x0A)
-#define MAX3107_BRG26_B600 (0x0A9400 | 0x05)
-#define MAX3107_BRG26_B1200 (0x054A00 | 0x03)
-#define MAX3107_BRG26_B2400 (0x02A500 | 0x01)
-#define MAX3107_BRG26_B4800 (0x015200 | 0x09)
-#define MAX3107_BRG26_B9600 (0x00A900 | 0x04)
-#define MAX3107_BRG26_B19200 (0x005400 | 0x0A)
-#define MAX3107_BRG26_B38400 (0x002A00 | 0x05)
-#define MAX3107_BRG26_B57600 (0x001C00 | 0x03)
-#define MAX3107_BRG26_B115200 (0x000E00 | 0x02)
-#define MAX3107_BRG26_B230400 (0x000700 | 0x01)
-#define MAX3107_BRG26_B460800 (0x000300 | 0x08)
-#define MAX3107_BRG26_B921600 (0x000100 | 0x0C)
-
-/* Baud rate generator configuration values for internal clock */
-#define MAX3107_BRG13_IB300 (0x008000 | 0x00)
-#define MAX3107_BRG13_IB600 (0x004000 | 0x00)
-#define MAX3107_BRG13_IB1200 (0x002000 | 0x00)
-#define MAX3107_BRG13_IB2400 (0x001000 | 0x00)
-#define MAX3107_BRG13_IB4800 (0x000800 | 0x00)
-#define MAX3107_BRG13_IB9600 (0x000400 | 0x00)
-#define MAX3107_BRG13_IB19200 (0x000200 | 0x00)
-#define MAX3107_BRG13_IB38400 (0x000100 | 0x00)
-#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B)
-#define MAX3107_BRG13_IB115200 (0x000000 | 0x05)
-#define MAX3107_BRG13_IB230400 (0x000000 | 0x03)
-#define MAX3107_BRG13_IB460800 (0x000000 | 0x00)
-#define MAX3107_BRG13_IB921600 (0x000000 | 0x00)
-
-
-struct baud_table {
- int baud;
- u32 new_brg;
-};
-
-struct max3107_port {
- /* UART port structure */
- struct uart_port port;
-
- /* SPI device structure */
- struct spi_device *spi;
-
-#if defined(CONFIG_GPIOLIB)
- /* GPIO chip structure */
- struct gpio_chip chip;
-#endif
-
- /* Workqueue that does all the magic */
- struct workqueue_struct *workqueue;
- struct work_struct work;
-
- /* Lock for shared data */
- spinlock_t data_lock;
-
- /* Device configuration */
- int ext_clk; /* 1 if external clock used */
- int loopback; /* Current loopback mode state */
- int baud; /* Current baud rate */
-
- /* State flags */
- int suspended; /* Indicates suspend mode */
- int tx_fifo_empty; /* Flag for TX FIFO state */
- int rx_enabled; /* Flag for receiver state */
- int tx_enabled; /* Flag for transmitter state */
-
- u16 irqen_reg; /* Current IRQ enable register value */
- /* Shared data */
- u16 mode1_reg; /* Current mode1 register value*/
- int mode1_commit; /* Flag for setting new mode1 register value */
- u16 lcr_reg; /* Current LCR register value */
- int lcr_commit; /* Flag for setting new LCR register value */
- u32 brg_cfg; /* Current Baud rate generator config */
- int brg_commit; /* Flag for setting new baud rate generator
- * config
- */
- struct baud_table *baud_tbl;
- int handle_irq; /* Indicates that IRQ should be handled */
-
- /* Rx buffer and str*/
- u16 *rxbuf;
- u8 *rxstr;
- /* Tx buffer*/
- u16 *txbuf;
-
- struct max3107_plat *pdata; /* Platform data */
-};
-
-/* Platform data structure */
-struct max3107_plat {
- /* Loopback mode enable */
- int loopback;
- /* External clock enable */
- int ext_clk;
- /* Called during the register initialisation */
- void (*init)(struct max3107_port *s);
- /* Called when the port is found and configured */
- int (*configure)(struct max3107_port *s);
- /* HW suspend function */
- void (*hw_suspend) (struct max3107_port *s, int suspend);
- /* Polling mode enable */
- int polled_mode;
- /* Polling period if polling mode enabled */
- int poll_time;
-};
-
-extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len);
-extern void max3107_hw_susp(struct max3107_port *s, int suspend);
-extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata);
-extern int max3107_remove(struct spi_device *spi);
-extern int max3107_suspend(struct spi_device *spi, pm_message_t state);
-extern int max3107_resume(struct spi_device *spi);
-
-#endif /* _LINUX_SERIAL_MAX3107_H */
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
new file mode 100644
index 00000000000..ba285cd45b5
--- /dev/null
+++ b/drivers/tty/serial/max310x.c
@@ -0,0 +1,1372 @@
+/*
+ * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
+ *
+ * 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>
+ * Based on max3107.c, by Aavamobile
+ *
+ * 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/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/spi/spi.h>
+#include <linux/uaccess.h>
+
+#define MAX310X_NAME "max310x"
+#define MAX310X_MAJOR 204
+#define MAX310X_MINOR 209
+
+/* MAX310X register definitions */
+#define MAX310X_RHR_REG (0x00) /* RX FIFO */
+#define MAX310X_THR_REG (0x00) /* TX FIFO */
+#define MAX310X_IRQEN_REG (0x01) /* IRQ enable */
+#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_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 */
+#define MAX310X_MODE1_REG (0x09) /* MODE1 */
+#define MAX310X_MODE2_REG (0x0a) /* MODE2 */
+#define MAX310X_LCR_REG (0x0b) /* LCR */
+#define MAX310X_RXTO_REG (0x0c) /* RX timeout */
+#define MAX310X_HDPIXDELAY_REG (0x0d) /* Auto transceiver delays */
+#define MAX310X_IRDA_REG (0x0e) /* IRDA settings */
+#define MAX310X_FLOWLVL_REG (0x0f) /* Flow control levels */
+#define MAX310X_FIFOTRIGLVL_REG (0x10) /* FIFO IRQ trigger levels */
+#define MAX310X_TXFIFOLVL_REG (0x11) /* TX FIFO level */
+#define MAX310X_RXFIFOLVL_REG (0x12) /* RX FIFO level */
+#define MAX310X_FLOWCTRL_REG (0x13) /* Flow control */
+#define MAX310X_XON1_REG (0x14) /* XON1 character */
+#define MAX310X_XON2_REG (0x15) /* XON2 character */
+#define MAX310X_XOFF1_REG (0x16) /* XOFF1 character */
+#define MAX310X_XOFF2_REG (0x17) /* XOFF2 character */
+#define MAX310X_GPIOCFG_REG (0x18) /* GPIO config */
+#define MAX310X_GPIODATA_REG (0x19) /* GPIO data */
+#define MAX310X_PLLCFG_REG (0x1a) /* PLL config */
+#define MAX310X_BRGCFG_REG (0x1b) /* Baud rate generator conf */
+#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 */
+#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 */
+#define MAX310X_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */
+#define MAX310X_IRQ_STS_BIT (1 << 2) /* Status interrupt */
+#define MAX310X_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */
+#define MAX310X_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */
+#define MAX310X_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */
+#define MAX310X_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */
+#define MAX310X_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */
+
+/* LSR register bits */
+#define MAX310X_LSR_RXTO_BIT (1 << 0) /* RX timeout */
+#define MAX310X_LSR_RXOVR_BIT (1 << 1) /* RX overrun */
+#define MAX310X_LSR_RXPAR_BIT (1 << 2) /* RX parity error */
+#define MAX310X_LSR_FRERR_BIT (1 << 3) /* Frame error */
+#define MAX310X_LSR_RXBRK_BIT (1 << 4) /* RX break */
+#define MAX310X_LSR_RXNOISE_BIT (1 << 5) /* RX noise */
+#define MAX310X_LSR_CTS_BIT (1 << 7) /* CTS pin state */
+
+/* Special character register bits */
+#define MAX310X_SPCHR_XON1_BIT (1 << 0) /* XON1 character */
+#define MAX310X_SPCHR_XON2_BIT (1 << 1) /* XON2 character */
+#define MAX310X_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */
+#define MAX310X_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */
+#define MAX310X_SPCHR_BREAK_BIT (1 << 4) /* RX break */
+#define MAX310X_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */
+
+/* Status register bits */
+#define MAX310X_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */
+#define MAX310X_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */
+#define MAX310X_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */
+#define MAX310X_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */
+#define MAX310X_STS_CLKREADY_BIT (1 << 5) /* Clock ready */
+#define MAX310X_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */
+
+/* MODE1 register bits */
+#define MAX310X_MODE1_RXDIS_BIT (1 << 0) /* RX disable */
+#define MAX310X_MODE1_TXDIS_BIT (1 << 1) /* TX disable */
+#define MAX310X_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */
+#define MAX310X_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */
+#define MAX310X_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */
+#define MAX310X_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */
+#define MAX310X_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */
+#define MAX310X_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */
+
+/* MODE2 register bits */
+#define MAX310X_MODE2_RST_BIT (1 << 0) /* Chip reset */
+#define MAX310X_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */
+#define MAX310X_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */
+#define MAX310X_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */
+#define MAX310X_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */
+#define MAX310X_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */
+#define MAX310X_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */
+#define MAX310X_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */
+
+/* LCR register bits */
+#define MAX310X_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */
+#define MAX310X_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 MAX310X_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 MAX310X_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */
+#define MAX310X_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */
+#define MAX310X_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */
+#define MAX310X_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */
+#define MAX310X_LCR_RTS_BIT (1 << 7) /* RTS pin control */
+#define MAX310X_LCR_WORD_LEN_5 (0x00)
+#define MAX310X_LCR_WORD_LEN_6 (0x01)
+#define MAX310X_LCR_WORD_LEN_7 (0x02)
+#define MAX310X_LCR_WORD_LEN_8 (0x03)
+
+/* IRDA register bits */
+#define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */
+#define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */
+
+/* Flow control trigger level register masks */
+#define MAX310X_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */
+#define MAX310X_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */
+#define MAX310X_FLOWLVL_HALT(words) ((words / 8) & 0x0f)
+#define MAX310X_FLOWLVL_RES(words) (((words / 8) & 0x0f) << 4)
+
+/* FIFO interrupt trigger level register masks */
+#define MAX310X_FIFOTRIGLVL_TX_MASK (0x0f) /* TX FIFO trigger level */
+#define MAX310X_FIFOTRIGLVL_RX_MASK (0xf0) /* RX FIFO trigger level */
+#define MAX310X_FIFOTRIGLVL_TX(words) ((words / 8) & 0x0f)
+#define MAX310X_FIFOTRIGLVL_RX(words) (((words / 8) & 0x0f) << 4)
+
+/* Flow control register bits */
+#define MAX310X_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */
+#define MAX310X_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */
+#define MAX310X_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs
+ * are used in conjunction with
+ * XOFF2 for definition of
+ * special character */
+#define MAX310X_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */
+#define MAX310X_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */
+#define MAX310X_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1
+ *
+ * SWFLOW bits 1 & 0 table:
+ * 00 -> no transmitter flow
+ * control
+ * 01 -> receiver compares
+ * XON2 and XOFF2
+ * and controls
+ * transmitter
+ * 10 -> receiver compares
+ * XON1 and XOFF1
+ * and controls
+ * transmitter
+ * 11 -> receiver compares
+ * XON1, XON2, XOFF1 and
+ * XOFF2 and controls
+ * transmitter
+ */
+#define MAX310X_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */
+#define MAX310X_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3
+ *
+ * SWFLOW bits 3 & 2 table:
+ * 00 -> no received flow
+ * control
+ * 01 -> transmitter generates
+ * XON2 and XOFF2
+ * 10 -> transmitter generates
+ * XON1 and XOFF1
+ * 11 -> transmitter generates
+ * XON1, XON2, XOFF1 and
+ * XOFF2
+ */
+
+/* PLL configuration register masks */
+#define MAX310X_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */
+#define MAX310X_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */
+
+/* Baud rate generator configuration register bits */
+#define MAX310X_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */
+#define MAX310X_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */
+
+/* Clock source register bits */
+#define MAX310X_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */
+#define MAX310X_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */
+#define MAX310X_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */
+#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)
+
+/* 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_one {
+ struct uart_port port;
+ struct work_struct tx_work;
+ struct work_struct md_work;
+};
+
+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];
+};
+
+static u8 max310x_port_read(struct uart_port *port, u8 reg)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+ unsigned int val = 0;
+
+ 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;
+
+ 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);
+}
+
+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 max310x_reg_writeable(struct device *dev, unsigned int 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:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static bool max310x_reg_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg & 0x1f) {
+ case MAX310X_RHR_REG:
+ 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 MAX310X_GPIODATA_REG:
+ case MAX310X_BRGDIVLSB_REG:
+ case MAX310X_REG_05:
+ case MAX310X_REG_1F:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool max310x_reg_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg & 0x1f) {
+ case MAX310X_RHR_REG:
+ case MAX310X_IRQSTS_REG:
+ case MAX310X_SPCHR_IRQSTS_REG:
+ case MAX310X_STS_IRQSTS_REG:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int max310x_set_baud(struct uart_port *port, int baud)
+{
+ unsigned int mode = 0, clk = port->uartclk, div = clk / baud;
+
+ /* Check for minimal value for divider */
+ if (div < 16)
+ div = 16;
+
+ if (clk % baud && (div / 16) < 0x8000) {
+ /* Mode x2 */
+ mode = MAX310X_BRGCFG_2XMODE_BIT;
+ 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;
+ }
+ }
+
+ 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);
+
+ return DIV_ROUND_CLOSEST(clk, div);
+}
+
+static int max310x_update_best_err(unsigned long f, long *besterr)
+{
+ /* Use baudrate 115200 for calculate error */
+ long err = f % (115200 * 16);
+
+ if ((*besterr < 0) || (*besterr > err)) {
+ *besterr = err;
+ return 0;
+ }
+
+ return 1;
+}
+
+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 = freq;
+
+ /* First, update error without PLL */
+ max310x_update_best_err(freq, &besterr);
+
+ /* Try all possible PLL dividers */
+ for (div = 1; (div <= 63) && besterr; div++) {
+ fdiv = DIV_ROUND_CLOSEST(freq, div);
+
+ /* Try multiplier 6 */
+ fmul = fdiv * 6;
+ if ((fdiv >= 500000) && (fdiv <= 800000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (0 << 6) | div;
+ bestfreq = fmul;
+ }
+ /* Try multiplier 48 */
+ fmul = fdiv * 48;
+ if ((fdiv >= 850000) && (fdiv <= 1200000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (1 << 6) | div;
+ bestfreq = fmul;
+ }
+ /* Try multiplier 96 */
+ fmul = fdiv * 96;
+ if ((fdiv >= 425000) && (fdiv <= 1000000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (2 << 6) | div;
+ bestfreq = fmul;
+ }
+ /* Try multiplier 144 */
+ fmul = fdiv * 144;
+ if ((fdiv >= 390000) && (fdiv <= 667000))
+ if (!max310x_update_best_err(fmul, &besterr)) {
+ pllcfg = (3 << 6) | div;
+ bestfreq = fmul;
+ }
+ }
+
+ /* Configure clock source */
+ clksrc = xtal ? MAX310X_CLKSRC_CRYST_BIT : MAX310X_CLKSRC_EXTCLK_BIT;
+
+ /* Configure PLL */
+ if (pllcfg) {
+ clksrc |= MAX310X_CLKSRC_PLL_BIT;
+ regmap_write(s->regmap, MAX310X_PLLCFG_REG, pllcfg);
+ } else
+ clksrc |= MAX310X_CLKSRC_PLLBYP_BIT;
+
+ regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc);
+
+ /* Wait for crystal */
+ if (pllcfg && xtal)
+ msleep(10);
+
+ return (int)bestfreq;
+}
+
+static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
+{
+ unsigned int sts, ch, flag;
+
+ 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 = port->fifosize;
+ }
+
+ while (rxlen--) {
+ 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;
+
+ port->icount.rx++;
+ flag = TTY_NORMAL;
+
+ if (unlikely(sts)) {
+ if (sts & MAX310X_LSR_RXBRK_BIT) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ } else if (sts & MAX310X_LSR_RXPAR_BIT)
+ port->icount.parity++;
+ else if (sts & MAX310X_LSR_FRERR_BIT)
+ port->icount.frame++;
+ else if (sts & MAX310X_LSR_RXOVR_BIT)
+ port->icount.overrun++;
+
+ sts &= port->read_status_mask;
+ if (sts & MAX310X_LSR_RXBRK_BIT)
+ flag = TTY_BREAK;
+ else if (sts & MAX310X_LSR_RXPAR_BIT)
+ flag = TTY_PARITY;
+ else if (sts & MAX310X_LSR_FRERR_BIT)
+ flag = TTY_FRAME;
+ else if (sts & MAX310X_LSR_RXOVR_BIT)
+ flag = TTY_OVERRUN;
+ }
+
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+
+ if (sts & port->ignore_status_mask)
+ continue;
+
+ uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag);
+ }
+
+ tty_flip_buffer_push(&port->state->port);
+}
+
+static void max310x_handle_tx(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int txlen, to_send;
+
+ 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(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 = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+ txlen = port->fifosize - txlen;
+ to_send = (to_send > txlen) ? txlen : to_send;
+
+ /* Add data to send */
+ port->icount.tx += to_send;
+ while (to_send--) {
+ 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(port);
+}
+
+static void max310x_port_irq(struct max310x_port *s, int portno)
+{
+ struct uart_port *port = &s->p[portno].port;
+
+ do {
+ unsigned int ists, lsr, rxlen;
+
+ /* Read IRQ status & RX FIFO level */
+ ists = max310x_port_read(port, MAX310X_IRQSTS_REG);
+ rxlen = max310x_port_read(port, MAX310X_RXFIFOLVL_REG);
+ if (!ists && !rxlen)
+ break;
+
+ 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;
+
+ 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_one *one = container_of(ws, struct max310x_one, tx_work);
+ struct max310x_port *s = dev_get_drvdata(one->port.dev);
+
+ mutex_lock(&s->mutex);
+ max310x_handle_tx(&one->port);
+ mutex_unlock(&s->mutex);
+}
+
+static void max310x_start_tx(struct uart_port *port)
+{
+ struct max310x_one *one = container_of(port, struct max310x_one, port);
+
+ if (!work_pending(&one->tx_work))
+ schedule_work(&one->tx_work);
+}
+
+static unsigned int max310x_tx_empty(struct uart_port *port)
+{
+ unsigned int lvl, sts;
+
+ lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+ sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
+
+ return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int max310x_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 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)
+{
+ 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)
+{
+ 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)
+{
+ 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 = MAX310X_LCR_WORD_LEN_5;
+ break;
+ case CS6:
+ lcr = MAX310X_LCR_WORD_LEN_6;
+ break;
+ case CS7:
+ lcr = MAX310X_LCR_WORD_LEN_7;
+ break;
+ case CS8:
+ default:
+ lcr = MAX310X_LCR_WORD_LEN_8;
+ break;
+ }
+
+ /* Parity */
+ if (termios->c_cflag & PARENB) {
+ lcr |= MAX310X_LCR_PARITY_BIT;
+ if (!(termios->c_cflag & PARODD))
+ lcr |= MAX310X_LCR_EVENPARITY_BIT;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB)
+ lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */
+
+ /* Update LCR register */
+ 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 & (IGNBRK | BRKINT | PARMRK))
+ port->read_status_mask |= MAX310X_LSR_RXBRK_BIT;
+
+ /* Set status ignore mask */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNBRK)
+ port->ignore_status_mask |= MAX310X_LSR_RXBRK_BIT;
+ if (!(termios->c_cflag & CREAD))
+ port->ignore_status_mask |= MAX310X_LSR_RXPAR_BIT |
+ MAX310X_LSR_RXOVR_BIT |
+ MAX310X_LSR_FRERR_BIT |
+ MAX310X_LSR_RXBRK_BIT;
+
+ /* Configure flow control */
+ 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;
+ if (termios->c_iflag & IXON)
+ flow |= MAX310X_FLOWCTRL_SWFLOW3_BIT |
+ MAX310X_FLOWCTRL_SWFLOWEN_BIT;
+ if (termios->c_iflag & IXOFF)
+ flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
+ MAX310X_FLOWCTRL_SWFLOWEN_BIT;
+ max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow);
+
+ /* Get baud rate generator configuration */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ port->uartclk / 4);
+
+ /* Setup baudrate generator */
+ baud = max310x_set_baud(port, baud);
+
+ /* Update timeout according to new baud rate */
+ uart_update_timeout(port, termios->c_cflag, baud);
+}
+
+static int max310x_ioctl(struct uart_port *port, unsigned int cmd,
+ unsigned long arg)
+{
+#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
+
+ return -ENOIOCTLCMD;
+}
+
+static int max310x_startup(struct uart_port *port)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+ unsigned int val;
+
+ s->devtype->power(port, 1);
+
+ /* Configure MODE1 register */
+ 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 */
+ max310x_port_write(port, MAX310X_FLOWLVL_REG,
+ MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96));
+
+ /* Clear IRQ status register */
+ max310x_port_read(port, MAX310X_IRQSTS_REG);
+
+ /* 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 = dev_get_drvdata(port->dev);
+
+ /* Disable all interrupts */
+ max310x_port_write(port, MAX310X_IRQEN_REG, 0);
+
+ s->devtype->power(port, 0);
+}
+
+static const char *max310x_type(struct uart_port *port)
+{
+ struct max310x_port *s = dev_get_drvdata(port->dev);
+
+ return (port->type == PORT_MAX310X) ? s->devtype->name : NULL;
+}
+
+static int max310x_request_port(struct uart_port *port)
+{
+ /* Do nothing */
+ return 0;
+}
+
+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 *s)
+{
+ if ((s->type != PORT_UNKNOWN) && (s->type != PORT_MAX310X))
+ return -EINVAL;
+ if (s->irq != port->irq)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void max310x_null_void(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+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_null_void,
+ .start_tx = max310x_start_tx,
+ .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_null_void,
+ .config_port = max310x_config_port,
+ .verify_port = max310x_verify_port,
+ .ioctl = max310x_ioctl,
+};
+
+static int __maybe_unused max310x_suspend(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ int i;
+
+ 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 0;
+}
+
+static int __maybe_unused max310x_resume(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ int i;
+
+ 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 0;
+}
+
+static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);
+
+#ifdef CONFIG_GPIOLIB
+static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned int val;
+ struct max310x_port *s = container_of(chip, struct max310x_port, gpio);
+ struct uart_port *port = &s->p[offset / 4].port;
+
+ val = max310x_port_read(port, MAX310X_GPIODATA_REG);
+
+ 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;
+
+ 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;
+
+ max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0);
+
+ return 0;
+}
+
+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;
+
+ 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
+
+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;
+ bool xtal = false;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Alloc port structure */
+ 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;
+ }
+
+ 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;
+ }
+
+ ret = clk_prepare_enable(s->clk);
+ if (ret)
+ return ret;
+
+ freq = clk_get_rate(s->clk);
+ /* Check frequency limits */
+ if (freq < fmin || freq > fmax) {
+ ret = -ERANGE;
+ goto out_clk;
+ }
+
+ s->regmap = regmap;
+ s->devtype = devtype;
+ dev_set_drvdata(dev, s);
+
+ /* 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.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 out_clk;
+ }
+
+#ifdef CONFIG_GPIOLIB
+ /* Setup GPIO cotroller */
+ 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
+
+ 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);
+ }
+
+ /* 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));
+
+out_uart:
+#endif
+ uart_unregister_driver(&s->uart);
+
+out_clk:
+ clk_disable_unprepare(s->clk);
+
+ return ret;
+}
+
+static int max310x_remove(struct device *dev)
+{
+ struct max310x_port *s = dev_get_drvdata(dev);
+ int i, ret = 0;
+
+#ifdef CONFIG_GPIOLIB
+ 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);
+ s->devtype->power(&s->p[i].port, 0);
+ }
+
+ mutex_destroy(&s->mutex);
+ uart_unregister_driver(&s->uart);
+ clk_disable_unprepare(s->clk);
+
+ return ret;
+}
+
+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,
+};
+
+#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;
+
+ /* 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;
+ }
+
+ regcfg.max_register = devtype->nr * 0x20 - 1;
+ regmap = devm_regmap_init_spi(spi, &regcfg);
+
+ return max310x_probe(&spi->dev, devtype, regmap, spi->irq, flags);
+}
+
+static int max310x_spi_remove(struct spi_device *spi)
+{
+ return max310x_remove(&spi->dev);
+}
+
+static const struct spi_device_id max310x_id_table[] = {
+ { "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_uart_driver = {
+ .driver = {
+ .name = MAX310X_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max310x_dt_ids),
+ .pm = &max310x_pm_ops,
+ },
+ .probe = max310x_spi_probe,
+ .remove = max310x_spi_remove,
+ .id_table = max310x_id_table,
+};
+module_spi_driver(max310x_uart_driver);
+#endif
+
+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 9afca093d6e..a6f085717f9 100644
--- a/drivers/tty/serial/mcf.c
+++ b/drivers/tty/serial/mcf.c
@@ -23,6 +23,8 @@
#include <linux/serial.h>
#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>
@@ -55,6 +57,7 @@ struct mcf_uart {
struct uart_port port;
unsigned int sigs; /* Local copy of line sigs */
unsigned char imr; /* Local IMR mirror */
+ struct serial_rs485 rs485; /* RS485 settings */
};
/****************************************************************************/
@@ -101,6 +104,12 @@ static void mcf_start_tx(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+ if (pp->rs485.flags & SER_RS485_ENABLED) {
+ /* Enable Transmitter */
+ writeb(MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR);
+ /* Manually assert RTS */
+ writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);
+ }
pp->imr |= MCFUART_UIR_TXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
}
@@ -196,6 +205,7 @@ static void mcf_shutdown(struct uart_port *port)
static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
unsigned long flags;
unsigned int baud, baudclk;
#if defined(CONFIG_M5272)
@@ -238,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
@@ -248,6 +264,11 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
mr2 |= MCFUART_MR2_TXCTS;
}
+ if (pp->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ mr2 |= MCFUART_MR2_TXRTS;
+ }
+
spin_lock_irqsave(&port->lock, flags);
uart_update_timeout(port, termios->c_cflag, baud);
writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);
@@ -310,7 +331,9 @@ static void mcf_rx_chars(struct mcf_uart *pp)
uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag);
}
- tty_flip_buffer_push(port->state->port.tty);
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
}
/****************************************************************************/
@@ -342,6 +365,10 @@ static void mcf_tx_chars(struct mcf_uart *pp)
if (xmit->head == xmit->tail) {
pp->imr &= ~MCFUART_UIR_TXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
+ /* Disable TX to negate RTS automatically */
+ if (pp->rs485.flags & SER_RS485_ENABLED)
+ writeb(MCFUART_UCR_TXDISABLE,
+ port->membase + MCFUART_UCR);
}
}
@@ -418,6 +445,58 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser)
/****************************************************************************/
+/* Enable or disable the RS485 support */
+static void mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
+{
+ struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+ unsigned long flags;
+ unsigned char mr1, mr2;
+
+ spin_lock_irqsave(&port->lock, flags);
+ /* Get mode registers */
+ mr1 = readb(port->membase + MCFUART_UMR);
+ mr2 = readb(port->membase + MCFUART_UMR);
+ if (rs485->flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ /* Automatically negate RTS after TX completes */
+ mr2 |= MCFUART_MR2_TXRTS;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ mr2 &= ~MCFUART_MR2_TXRTS;
+ }
+ writeb(mr1, port->membase + MCFUART_UMR);
+ writeb(mr2, port->membase + MCFUART_UMR);
+ pp->rs485 = *rs485;
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int mcf_ioctl(struct uart_port *port, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case TIOCSRS485: {
+ struct serial_rs485 rs485;
+ if (copy_from_user(&rs485, (struct serial_rs485 *)arg,
+ sizeof(struct serial_rs485)))
+ return -EFAULT;
+ mcf_config_rs485(port, &rs485);
+ break;
+ }
+ case TIOCGRS485: {
+ struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+ if (copy_to_user((struct serial_rs485 *)arg, &pp->rs485,
+ sizeof(struct serial_rs485)))
+ return -EFAULT;
+ break;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+
/*
* Define the basic serial functions we support.
*/
@@ -438,6 +517,7 @@ static const struct uart_ops mcf_uart_ops = {
.release_port = mcf_release_port,
.config_port = mcf_config_port,
.verify_port = mcf_verify_port,
+ .ioctl = mcf_ioctl,
};
static struct mcf_uart mcf_ports[4];
@@ -571,9 +651,9 @@ static struct uart_driver mcf_driver = {
/****************************************************************************/
-static int __devinit mcf_probe(struct platform_device *pdev)
+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;
@@ -599,7 +679,7 @@ static int __devinit mcf_probe(struct platform_device *pdev)
/****************************************************************************/
-static int __devexit mcf_remove(struct platform_device *pdev)
+static int mcf_remove(struct platform_device *pdev)
{
struct uart_port *port;
int i;
@@ -617,7 +697,7 @@ static int __devexit mcf_remove(struct platform_device *pdev)
static struct platform_driver mcf_platform_driver = {
.probe = mcf_probe,
- .remove = __devexit_p(mcf_remove),
+ .remove = mcf_remove,
.driver = {
.name = "mcfuart",
.owner = THIS_MODULE,
@@ -636,8 +716,10 @@ static int __init mcf_init(void)
if (rc)
return rc;
rc = platform_driver_register(&mcf_platform_driver);
- if (rc)
+ if (rc) {
+ uart_unregister_driver(&mcf_driver);
return rc;
+ }
return 0;
}
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 a9234ba8f8d..445799dc984 100644
--- a/drivers/tty/serial/mfd.c
+++ b/drivers/tty/serial/mfd.c
@@ -21,6 +21,10 @@
* be triggered
*/
+#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/console.h>
@@ -36,6 +40,7 @@
#include <linux/serial_mfd.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
+#include <linux/nmi.h>
#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
@@ -127,11 +132,6 @@ static inline void serial_out(struct uart_hsu_port *up, int offset, int value)
#define HSU_REGS_BUFSIZE 1024
-static int hsu_show_regs_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
static ssize_t port_show_regs(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -231,14 +231,14 @@ static ssize_t dma_show_regs(struct file *file, char __user *user_buf,
static const struct file_operations port_regs_ops = {
.owner = THIS_MODULE,
- .open = hsu_show_regs_open,
+ .open = simple_open,
.read = port_show_regs,
.llseek = default_llseek,
};
static const struct file_operations dma_regs_ops = {
.owner = THIS_MODULE,
- .open = hsu_show_regs_open,
+ .open = simple_open,
.read = dma_show_regs,
.llseek = default_llseek,
};
@@ -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,17 +387,15 @@ 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;
struct uart_port *port = &up->port;
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
int count;
- if (!tty)
- return;
-
/*
* First need to know how many is already transferred,
* then check if its a timeout DMA irq, and return
@@ -427,7 +426,7 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
* explicitly set tail to 0. So head will
* always be greater than tail.
*/
- tty_insert_flip_string(tty, dbuf->buf, count);
+ tty_insert_flip_string(tport, dbuf->buf, count);
port->icount.rx += count;
dma_sync_single_for_device(up->port.dev, dbuf->dma_addr,
@@ -441,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 */
);
- tty_flip_buffer_push(tty);
+ 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);
@@ -462,15 +463,12 @@ 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)
{
- struct tty_struct *tty = up->port.state->port.tty;
unsigned int ch, flag;
unsigned int max_count = 256;
- if (!tty)
- return;
-
do {
ch = serial_in(up, UART_RX);
flag = TTY_NORMAL;
@@ -526,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--);
- tty_flip_buffer_push(tty);
+
+ 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)
@@ -620,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 */
@@ -650,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) {
@@ -976,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 */
@@ -1118,6 +1119,8 @@ serial_hsu_console_write(struct console *co, const char *s, unsigned int count)
unsigned int ier;
int locked = 1;
+ touch_nmi_watchdog();
+
local_irq_save(flags);
if (up->port.sysrq)
locked = 0;
@@ -1182,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,
@@ -1257,13 +1260,8 @@ static int serial_hsu_resume(struct pci_dev *pdev)
#ifdef CONFIG_PM_RUNTIME
static int serial_hsu_runtime_idle(struct device *dev)
{
- int err;
-
- err = pm_schedule_suspend(dev, 500);
- if (err)
- return -EBUSY;
-
- return 0;
+ pm_schedule_suspend(dev, 500);
+ return -EBUSY;
}
static int serial_hsu_runtime_suspend(struct device *dev)
@@ -1455,13 +1453,12 @@ 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);
}
/* First 3 are UART ports, and the 4th is the DMA */
-static const struct pci_device_id pci_ids[] __devinitconst = {
+static const struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) },
@@ -1473,7 +1470,7 @@ static struct pci_driver hsu_pci_driver = {
.name = "HSU serial",
.id_table = pci_ids,
.probe = serial_hsu_probe,
- .remove = __devexit_p(serial_hsu_remove),
+ .remove = serial_hsu_remove,
.suspend = serial_hsu_suspend,
.resume = serial_hsu_resume,
.driver = {
@@ -1508,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 1093a88a1fe..97888f4900e 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -84,16 +84,6 @@ static void mpc52xx_uart_of_enumerate(void);
static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id);
static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port);
-
-/* Simple macro to test if a port is console or not. This one is taken
- * for serial_core.c and maybe should be moved to serial_core.h ? */
-#ifdef CONFIG_SERIAL_CORE_CONSOLE
-#define uart_console(port) \
- ((port)->cons && (port)->cons->index == (port)->line)
-#else
-#define uart_console(port) (0)
-#endif
-
/* ======================================================================== */
/* PSC fifo operations for isolating differences between 52xx and 512x */
/* ======================================================================== */
@@ -117,11 +107,22 @@ 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);
void (*get_irq)(struct uart_port *, struct device_node *);
irqreturn_t (*handle_irq)(struct uart_port *port);
+ u16 (*get_status)(struct uart_port *port);
+ u8 (*get_ipcr)(struct uart_port *port);
+ void (*command)(struct uart_port *port, u8 cmd);
+ void (*set_mode)(struct uart_port *port, u8 mr1, u8 mr2);
+ void (*set_rts)(struct uart_port *port, int state);
+ void (*enable_ms)(struct uart_port *port);
+ void (*set_sicr)(struct uart_port *port, u32 val);
+ void (*set_imr)(struct uart_port *port, u16 val);
+ u8 (*get_mr1)(struct uart_port *port);
};
/* setting the prescaler and divisor reg is common for all chips */
@@ -134,6 +135,65 @@ static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
out_8(&psc->ctlr, divisor & 0xff);
}
+static u16 mpc52xx_psc_get_status(struct uart_port *port)
+{
+ return in_be16(&PSC(port)->mpc52xx_psc_status);
+}
+
+static u8 mpc52xx_psc_get_ipcr(struct uart_port *port)
+{
+ return in_8(&PSC(port)->mpc52xx_psc_ipcr);
+}
+
+static void mpc52xx_psc_command(struct uart_port *port, u8 cmd)
+{
+ out_8(&PSC(port)->command, cmd);
+}
+
+static void mpc52xx_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2)
+{
+ out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1);
+ out_8(&PSC(port)->mode, mr1);
+ out_8(&PSC(port)->mode, mr2);
+}
+
+static void mpc52xx_psc_set_rts(struct uart_port *port, int state)
+{
+ if (state)
+ out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS);
+ else
+ out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS);
+}
+
+static void mpc52xx_psc_enable_ms(struct uart_port *port)
+{
+ struct mpc52xx_psc __iomem *psc = PSC(port);
+
+ /* clear D_*-bits by reading them */
+ in_8(&psc->mpc52xx_psc_ipcr);
+ /* enable CTS and DCD as IPC interrupts */
+ out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
+
+ port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
+ out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void mpc52xx_psc_set_sicr(struct uart_port *port, u32 val)
+{
+ out_be32(&PSC(port)->sicr, val);
+}
+
+static void mpc52xx_psc_set_imr(struct uart_port *port, u16 val)
+{
+ out_be16(&PSC(port)->mpc52xx_psc_imr, val);
+}
+
+static u8 mpc52xx_psc_get_mr1(struct uart_port *port)
+{
+ out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1);
+ return in_8(&PSC(port)->mode);
+}
+
#ifdef CONFIG_PPC_MPC52xx
#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1))
static void mpc52xx_psc_fifo_init(struct uart_port *port)
@@ -262,8 +322,9 @@ static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port,
port->uartclk / 4);
divisor = (port->uartclk + 2 * baud) / (4 * baud);
- /* select the proper prescaler and set the divisor */
- if (divisor > 0xffff) {
+ /* select the proper prescaler and set the divisor
+ * prefer high prescaler for more tolerance on low baudrates */
+ if (divisor > 0xffff || baud <= 115200) {
divisor = (divisor + 4) / 8;
prescaler = 0xdd00; /* /32 */
} else
@@ -303,6 +364,15 @@ static struct psc_ops mpc52xx_psc_ops = {
.set_baudrate = mpc5200_psc_set_baudrate,
.get_irq = mpc52xx_psc_get_irq,
.handle_irq = mpc52xx_psc_handle_irq,
+ .get_status = mpc52xx_psc_get_status,
+ .get_ipcr = mpc52xx_psc_get_ipcr,
+ .command = mpc52xx_psc_command,
+ .set_mode = mpc52xx_psc_set_mode,
+ .set_rts = mpc52xx_psc_set_rts,
+ .enable_ms = mpc52xx_psc_enable_ms,
+ .set_sicr = mpc52xx_psc_set_sicr,
+ .set_imr = mpc52xx_psc_set_imr,
+ .get_mr1 = mpc52xx_psc_get_mr1,
};
static struct psc_ops mpc5200b_psc_ops = {
@@ -324,6 +394,15 @@ static struct psc_ops mpc5200b_psc_ops = {
.set_baudrate = mpc5200b_psc_set_baudrate,
.get_irq = mpc52xx_psc_get_irq,
.handle_irq = mpc52xx_psc_handle_irq,
+ .get_status = mpc52xx_psc_get_status,
+ .get_ipcr = mpc52xx_psc_get_ipcr,
+ .command = mpc52xx_psc_command,
+ .set_mode = mpc52xx_psc_set_mode,
+ .set_rts = mpc52xx_psc_set_rts,
+ .enable_ms = mpc52xx_psc_enable_ms,
+ .set_sicr = mpc52xx_psc_set_sicr,
+ .set_imr = mpc52xx_psc_set_imr,
+ .get_mr1 = mpc52xx_psc_get_mr1,
};
#endif /* CONFIG_MPC52xx */
@@ -342,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)
{
@@ -489,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 == NO_IRQ) {
+ 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 */
@@ -539,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)
{
- struct clk *psc_clk;
int psc_num;
- char clk_name[10];
+ 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)
+{
+ int psc_num;
+ 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_clk", 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)
@@ -571,6 +760,248 @@ static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np)
port->irqflags = IRQF_SHARED;
port->irq = psc_fifoc_irq;
}
+#endif
+
+#ifdef CONFIG_PPC_MPC512x
+
+#define PSC_5125(port) ((struct mpc5125_psc __iomem *)((port)->membase))
+#define FIFO_5125(port) ((struct mpc512x_psc_fifo __iomem *)(PSC_5125(port)+1))
+
+static void mpc5125_psc_fifo_init(struct uart_port *port)
+{
+ /* /32 prescaler */
+ out_8(&PSC_5125(port)->mpc52xx_psc_clock_select, 0xdd);
+
+ out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE);
+ out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE);
+ out_be32(&FIFO_5125(port)->txalarm, 1);
+ out_be32(&FIFO_5125(port)->tximr, 0);
+
+ out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE);
+ out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE);
+ out_be32(&FIFO_5125(port)->rxalarm, 1);
+ out_be32(&FIFO_5125(port)->rximr, 0);
+
+ out_be32(&FIFO_5125(port)->tximr, MPC512x_PSC_FIFO_ALARM);
+ out_be32(&FIFO_5125(port)->rximr, MPC512x_PSC_FIFO_ALARM);
+}
+
+static int mpc5125_psc_raw_rx_rdy(struct uart_port *port)
+{
+ return !(in_be32(&FIFO_5125(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY);
+}
+
+static int mpc5125_psc_raw_tx_rdy(struct uart_port *port)
+{
+ return !(in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_FULL);
+}
+
+static int mpc5125_psc_rx_rdy(struct uart_port *port)
+{
+ return in_be32(&FIFO_5125(port)->rxsr) &
+ in_be32(&FIFO_5125(port)->rximr) & MPC512x_PSC_FIFO_ALARM;
+}
+
+static int mpc5125_psc_tx_rdy(struct uart_port *port)
+{
+ return in_be32(&FIFO_5125(port)->txsr) &
+ in_be32(&FIFO_5125(port)->tximr) & MPC512x_PSC_FIFO_ALARM;
+}
+
+static int mpc5125_psc_tx_empty(struct uart_port *port)
+{
+ return in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_EMPTY;
+}
+
+static void mpc5125_psc_stop_rx(struct uart_port *port)
+{
+ unsigned long rx_fifo_imr;
+
+ rx_fifo_imr = in_be32(&FIFO_5125(port)->rximr);
+ rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM;
+ out_be32(&FIFO_5125(port)->rximr, rx_fifo_imr);
+}
+
+static void mpc5125_psc_start_tx(struct uart_port *port)
+{
+ unsigned long tx_fifo_imr;
+
+ tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr);
+ tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM;
+ out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr);
+}
+
+static void mpc5125_psc_stop_tx(struct uart_port *port)
+{
+ unsigned long tx_fifo_imr;
+
+ tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr);
+ tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM;
+ out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr);
+}
+
+static void mpc5125_psc_rx_clr_irq(struct uart_port *port)
+{
+ out_be32(&FIFO_5125(port)->rxisr, in_be32(&FIFO_5125(port)->rxisr));
+}
+
+static void mpc5125_psc_tx_clr_irq(struct uart_port *port)
+{
+ out_be32(&FIFO_5125(port)->txisr, in_be32(&FIFO_5125(port)->txisr));
+}
+
+static void mpc5125_psc_write_char(struct uart_port *port, unsigned char c)
+{
+ out_8(&FIFO_5125(port)->txdata_8, c);
+}
+
+static unsigned char mpc5125_psc_read_char(struct uart_port *port)
+{
+ return in_8(&FIFO_5125(port)->rxdata_8);
+}
+
+static void mpc5125_psc_cw_disable_ints(struct uart_port *port)
+{
+ port->read_status_mask =
+ in_be32(&FIFO_5125(port)->tximr) << 16 |
+ in_be32(&FIFO_5125(port)->rximr);
+ out_be32(&FIFO_5125(port)->tximr, 0);
+ out_be32(&FIFO_5125(port)->rximr, 0);
+}
+
+static void mpc5125_psc_cw_restore_ints(struct uart_port *port)
+{
+ out_be32(&FIFO_5125(port)->tximr,
+ (port->read_status_mask >> 16) & 0x7f);
+ out_be32(&FIFO_5125(port)->rximr, port->read_status_mask & 0x7f);
+}
+
+static inline void mpc5125_set_divisor(struct mpc5125_psc __iomem *psc,
+ u8 prescaler, unsigned int divisor)
+{
+ /* select prescaler */
+ out_8(&psc->mpc52xx_psc_clock_select, prescaler);
+ out_8(&psc->ctur, divisor >> 8);
+ out_8(&psc->ctlr, divisor & 0xff);
+}
+
+static unsigned int mpc5125_psc_set_baudrate(struct uart_port *port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+ unsigned int baud;
+ unsigned int divisor;
+
+ /*
+ * Calculate with a /16 prescaler here.
+ */
+
+ /* uartclk contains the ips freq */
+ baud = uart_get_baud_rate(port, new, old,
+ port->uartclk / (16 * 0xffff) + 1,
+ port->uartclk / 16);
+ divisor = (port->uartclk + 8 * baud) / (16 * baud);
+
+ /* enable the /16 prescaler and set the divisor */
+ mpc5125_set_divisor(PSC_5125(port), 0xdd, divisor);
+ return baud;
+}
+
+/*
+ * MPC5125 have compatible PSC FIFO Controller.
+ * Special init not needed.
+ */
+static u16 mpc5125_psc_get_status(struct uart_port *port)
+{
+ return in_be16(&PSC_5125(port)->mpc52xx_psc_status);
+}
+
+static u8 mpc5125_psc_get_ipcr(struct uart_port *port)
+{
+ return in_8(&PSC_5125(port)->mpc52xx_psc_ipcr);
+}
+
+static void mpc5125_psc_command(struct uart_port *port, u8 cmd)
+{
+ out_8(&PSC_5125(port)->command, cmd);
+}
+
+static void mpc5125_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2)
+{
+ out_8(&PSC_5125(port)->mr1, mr1);
+ out_8(&PSC_5125(port)->mr2, mr2);
+}
+
+static void mpc5125_psc_set_rts(struct uart_port *port, int state)
+{
+ if (state & TIOCM_RTS)
+ out_8(&PSC_5125(port)->op1, MPC52xx_PSC_OP_RTS);
+ else
+ out_8(&PSC_5125(port)->op0, MPC52xx_PSC_OP_RTS);
+}
+
+static void mpc5125_psc_enable_ms(struct uart_port *port)
+{
+ struct mpc5125_psc __iomem *psc = PSC_5125(port);
+
+ /* clear D_*-bits by reading them */
+ in_8(&psc->mpc52xx_psc_ipcr);
+ /* enable CTS and DCD as IPC interrupts */
+ out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
+
+ port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
+ out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void mpc5125_psc_set_sicr(struct uart_port *port, u32 val)
+{
+ out_be32(&PSC_5125(port)->sicr, val);
+}
+
+static void mpc5125_psc_set_imr(struct uart_port *port, u16 val)
+{
+ out_be16(&PSC_5125(port)->mpc52xx_psc_imr, val);
+}
+
+static u8 mpc5125_psc_get_mr1(struct uart_port *port)
+{
+ return in_8(&PSC_5125(port)->mr1);
+}
+
+static struct psc_ops mpc5125_psc_ops = {
+ .fifo_init = mpc5125_psc_fifo_init,
+ .raw_rx_rdy = mpc5125_psc_raw_rx_rdy,
+ .raw_tx_rdy = mpc5125_psc_raw_tx_rdy,
+ .rx_rdy = mpc5125_psc_rx_rdy,
+ .tx_rdy = mpc5125_psc_tx_rdy,
+ .tx_empty = mpc5125_psc_tx_empty,
+ .stop_rx = mpc5125_psc_stop_rx,
+ .start_tx = mpc5125_psc_start_tx,
+ .stop_tx = mpc5125_psc_stop_tx,
+ .rx_clr_irq = mpc5125_psc_rx_clr_irq,
+ .tx_clr_irq = mpc5125_psc_tx_clr_irq,
+ .write_char = mpc5125_psc_write_char,
+ .read_char = mpc5125_psc_read_char,
+ .cw_disable_ints = mpc5125_psc_cw_disable_ints,
+ .cw_restore_ints = mpc5125_psc_cw_restore_ints,
+ .set_baudrate = mpc5125_psc_set_baudrate,
+ .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,
+ .handle_irq = mpc512x_psc_handle_irq,
+ .get_status = mpc5125_psc_get_status,
+ .get_ipcr = mpc5125_psc_get_ipcr,
+ .command = mpc5125_psc_command,
+ .set_mode = mpc5125_psc_set_mode,
+ .set_rts = mpc5125_psc_set_rts,
+ .enable_ms = mpc5125_psc_enable_ms,
+ .set_sicr = mpc5125_psc_set_sicr,
+ .set_imr = mpc5125_psc_set_imr,
+ .get_mr1 = mpc5125_psc_get_mr1,
+};
static struct psc_ops mpc512x_psc_ops = {
.fifo_init = mpc512x_psc_fifo_init,
@@ -589,15 +1020,27 @@ 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,
.handle_irq = mpc512x_psc_handle_irq,
+ .get_status = mpc52xx_psc_get_status,
+ .get_ipcr = mpc52xx_psc_get_ipcr,
+ .command = mpc52xx_psc_command,
+ .set_mode = mpc52xx_psc_set_mode,
+ .set_rts = mpc52xx_psc_set_rts,
+ .enable_ms = mpc52xx_psc_enable_ms,
+ .set_sicr = mpc52xx_psc_set_sicr,
+ .set_imr = mpc52xx_psc_set_imr,
+ .get_mr1 = mpc52xx_psc_get_mr1,
};
-#endif
+#endif /* CONFIG_PPC_MPC512x */
+
-static struct psc_ops *psc_ops;
+static const struct psc_ops *psc_ops;
/* ======================================================================== */
/* UART operations */
@@ -612,17 +1055,14 @@ mpc52xx_uart_tx_empty(struct uart_port *port)
static void
mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- if (mctrl & TIOCM_RTS)
- out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS);
- else
- out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS);
+ psc_ops->set_rts(port, mctrl & TIOCM_RTS);
}
static unsigned int
mpc52xx_uart_get_mctrl(struct uart_port *port)
{
unsigned int ret = TIOCM_DSR;
- u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+ u8 status = psc_ops->get_ipcr(port);
if (!(status & MPC52xx_PSC_CTS))
ret |= TIOCM_CTS;
@@ -672,15 +1112,7 @@ mpc52xx_uart_stop_rx(struct uart_port *port)
static void
mpc52xx_uart_enable_ms(struct uart_port *port)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
-
- /* clear D_*-bits by reading them */
- in_8(&psc->mpc52xx_psc_ipcr);
- /* enable CTS and DCD as IPC interrupts */
- out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
-
- port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
- out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+ psc_ops->enable_ms(port);
}
static void
@@ -690,9 +1122,9 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl)
spin_lock_irqsave(&port->lock, flags);
if (ctl == -1)
- out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK);
+ psc_ops->command(port, MPC52xx_PSC_START_BRK);
else
- out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK);
+ psc_ops->command(port, MPC52xx_PSC_STOP_BRK);
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -700,7 +1132,6 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl)
static int
mpc52xx_uart_startup(struct uart_port *port)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
int ret;
if (psc_ops->clock) {
@@ -716,15 +1147,15 @@ mpc52xx_uart_startup(struct uart_port *port)
return ret;
/* Reset/activate the port, clear and enable interrupts */
- out_8(&psc->command, MPC52xx_PSC_RST_RX);
- out_8(&psc->command, MPC52xx_PSC_RST_TX);
+ psc_ops->command(port, MPC52xx_PSC_RST_RX);
+ psc_ops->command(port, MPC52xx_PSC_RST_TX);
- out_be32(&psc->sicr, 0); /* UART mode DCD ignored */
+ psc_ops->set_sicr(port, 0); /* UART mode DCD ignored */
psc_ops->fifo_init(port);
- out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
- out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_TX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_RX_ENABLE);
return 0;
}
@@ -732,19 +1163,20 @@ mpc52xx_uart_startup(struct uart_port *port)
static void
mpc52xx_uart_shutdown(struct uart_port *port)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
-
/* Shut down the port. Leave TX active if on a console port */
- out_8(&psc->command, MPC52xx_PSC_RST_RX);
+ psc_ops->command(port, MPC52xx_PSC_RST_RX);
if (!uart_console(port))
- out_8(&psc->command, MPC52xx_PSC_RST_TX);
+ psc_ops->command(port, MPC52xx_PSC_RST_TX);
port->read_status_mask = 0;
- out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+ psc_ops->set_imr(port, port->read_status_mask);
if (psc_ops->clock)
psc_ops->clock(port, 0);
+ /* Disable interrupt */
+ psc_ops->cw_disable_ints(port);
+
/* Release interrupt */
free_irq(port->irq, port);
}
@@ -753,7 +1185,6 @@ static void
mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
struct ktermios *old)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
unsigned long flags;
unsigned char mr1, mr2;
unsigned int j;
@@ -774,11 +1205,15 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
}
if (new->c_cflag & PARENB) {
+ if (new->c_cflag & CMSPAR)
+ mr1 |= MPC52xx_PSC_MODE_PARFORCE;
+
+ /* With CMSPAR, PARODD also means high parity (same as termios) */
mr1 |= (new->c_cflag & PARODD) ?
MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
- } else
+ } else {
mr1 |= MPC52xx_PSC_MODE_PARNONE;
-
+ }
mr2 = 0;
@@ -813,13 +1248,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
"Some chars may have been lost.\n");
/* Reset the TX & RX */
- out_8(&psc->command, MPC52xx_PSC_RST_RX);
- out_8(&psc->command, MPC52xx_PSC_RST_TX);
+ psc_ops->command(port, MPC52xx_PSC_RST_RX);
+ psc_ops->command(port, MPC52xx_PSC_RST_TX);
/* Send new mode settings */
- out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
- out_8(&psc->mode, mr1);
- out_8(&psc->mode, mr2);
+ psc_ops->set_mode(port, mr1, mr2);
baud = psc_ops->set_baudrate(port, new, old);
/* Update the per-port timeout */
@@ -829,8 +1262,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
mpc52xx_uart_enable_ms(port);
/* Reenable TX & RX */
- out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
- out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_TX_ENABLE);
+ psc_ops->command(port, MPC52xx_PSC_RX_ENABLE);
/* We're all set, release the lock */
spin_unlock_irqrestore(&port->lock, flags);
@@ -849,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);
@@ -873,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;
}
@@ -920,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,
@@ -936,7 +1384,7 @@ static struct uart_ops mpc52xx_uart_ops = {
static inline int
mpc52xx_uart_int_rx_chars(struct uart_port *port)
{
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
unsigned char ch, flag;
unsigned short status;
@@ -958,7 +1406,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port)
flag = TTY_NORMAL;
port->icount.rx++;
- status = in_be16(&PSC(port)->mpc52xx_psc_status);
+ status = psc_ops->get_status(port);
if (status & (MPC52xx_PSC_SR_PE |
MPC52xx_PSC_SR_FE |
@@ -978,23 +1426,23 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port)
}
/* Clear error condition */
- out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT);
+ psc_ops->command(port, MPC52xx_PSC_RST_ERR_STAT);
}
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(tport, ch, flag);
if (status & MPC52xx_PSC_SR_OE) {
/*
* Overrun is special, since it's
* reported immediately, and doesn't
* affect the current character
*/
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
port->icount.overrun++;
}
}
spin_unlock(&port->lock);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
spin_lock(&port->lock);
return psc_ops->raw_rx_rdy(port);
@@ -1061,7 +1509,7 @@ mpc5xxx_uart_process_int(struct uart_port *port)
if (psc_ops->tx_rdy(port))
keepgoing |= mpc52xx_uart_int_tx_chars(port);
- status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+ status = psc_ops->get_ipcr(port);
if (status & MPC52xx_PSC_D_DCD)
uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD));
@@ -1102,14 +1550,12 @@ static void __init
mpc52xx_console_get_options(struct uart_port *port,
int *baud, int *parity, int *bits, int *flow)
{
- struct mpc52xx_psc __iomem *psc = PSC(port);
unsigned char mr1;
pr_debug("mpc52xx_console_get_options(port=%p)\n", port);
/* Read the mode registers */
- out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
- mr1 = in_8(&psc->mode);
+ mr1 = psc_ops->get_mr1(port);
/* CT{U,L}R are write-only ! */
*baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD;
@@ -1299,11 +1745,12 @@ static struct of_device_id mpc52xx_uart_of_match[] = {
#endif
#ifdef CONFIG_PPC_MPC512x
{ .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, },
+ { .compatible = "fsl,mpc5125-psc-uart", .data = &mpc5125_psc_ops, },
#endif
{},
};
-static int __devinit mpc52xx_uart_of_probe(struct platform_device *op)
+static int mpc52xx_uart_of_probe(struct platform_device *op)
{
int idx = -1;
unsigned int uartclk;
@@ -1354,7 +1801,7 @@ static int __devinit mpc52xx_uart_of_probe(struct platform_device *op)
}
psc_ops->get_irq(port, op->dev.of_node);
- if (port->irq == NO_IRQ) {
+ if (port->irq == 0) {
dev_dbg(&op->dev, "Could not get irq\n");
return -EINVAL;
}
@@ -1367,15 +1814,14 @@ static int __devinit mpc52xx_uart_of_probe(struct platform_device *op)
if (ret)
return ret;
- dev_set_drvdata(&op->dev, (void *)port);
+ platform_set_drvdata(op, (void *)port);
return 0;
}
static int
mpc52xx_uart_of_remove(struct platform_device *op)
{
- struct uart_port *port = dev_get_drvdata(&op->dev);
- dev_set_drvdata(&op->dev, NULL);
+ struct uart_port *port = platform_get_drvdata(op);
if (port)
uart_remove_one_port(&mpc52xx_uart_driver, port);
@@ -1387,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 *) dev_get_drvdata(&op->dev);
+ struct uart_port *port = platform_get_drvdata(op);
if (port)
uart_suspend_port(&mpc52xx_uart_driver, port);
@@ -1398,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 *) dev_get_drvdata(&op->dev);
+ struct uart_port *port = platform_get_drvdata(op);
if (port)
uart_resume_port(&mpc52xx_uart_driver, port);
@@ -1492,18 +1938,23 @@ mpc52xx_uart_init(void)
if (psc_ops && psc_ops->fifoc_init) {
ret = psc_ops->fifoc_init();
if (ret)
- return ret;
+ goto err_init;
}
ret = platform_driver_register(&mpc52xx_uart_of_driver);
if (ret) {
printk(KERN_ERR "%s: platform_driver_register failed (%i)\n",
__FILE__, ret);
- uart_unregister_driver(&mpc52xx_uart_driver);
- return ret;
+ goto err_reg;
}
return 0;
+err_reg:
+ if (psc_ops && psc_ops->fifoc_uninit)
+ psc_ops->fifoc_uninit();
+err_init:
+ uart_unregister_driver(&mpc52xx_uart_driver);
+ return ret;
}
static void __exit
diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c
index 6a9c6605666..759c6a6fa74 100644
--- a/drivers/tty/serial/mpsc.c
+++ b/drivers/tty/serial/mpsc.c
@@ -934,10 +934,10 @@ 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_struct *tty = pi->port.state->port.tty;
+ struct tty_port *port = &pi->port.state->port;
u32 cmdstat, bytes_in, i;
int rc = 0;
u8 *bp;
@@ -968,10 +968,12 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi)
}
#endif
/* Following use of tty struct directly is deprecated */
- if (unlikely(tty_buffer_request_room(tty, bytes_in)
- < bytes_in)) {
- if (tty->low_latency)
- tty_flip_buffer_push(tty);
+ if (tty_buffer_request_room(port, bytes_in) < bytes_in) {
+ 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.
@@ -1040,10 +1042,10 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi)
| SDMA_DESC_CMDSTAT_FR
| SDMA_DESC_CMDSTAT_OR)))
&& !(cmdstat & pi->port.ignore_status_mask)) {
- tty_insert_flip_char(tty, *bp, flag);
+ tty_insert_flip_char(port, *bp, flag);
} else {
for (i=0; i<bytes_in; i++)
- tty_insert_flip_char(tty, *bp++, TTY_NORMAL);
+ tty_insert_flip_char(port, *bp++, TTY_NORMAL);
pi->port.icount.rx += bytes_in;
}
@@ -1081,7 +1083,9 @@ next_frame:
if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
mpsc_start_rx(pi);
- tty_flip_buffer_push(tty);
+ spin_unlock_irqrestore(&pi->port.lock, *flags);
+ tty_flip_buffer_push(port);
+ spin_lock_irqsave(&pi->port.lock, *flags);
return rc;
}
@@ -1223,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;
@@ -1454,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 */
@@ -1885,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;
@@ -2026,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 df2a2240a3a..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;
}
@@ -339,7 +343,7 @@ static int
receive_chars(struct uart_max3110 *max, unsigned short *str, int len)
{
struct uart_port *port = &max->port;
- struct tty_struct *tty;
+ struct tty_port *tport;
char buf[M3110_RX_FIFO_DEPTH];
int r, w, usable;
@@ -347,9 +351,7 @@ receive_chars(struct uart_max3110 *max, unsigned short *str, int len)
if (!port->state)
return 0;
- tty = tty_port_tty_get(&port->state->port);
- if (!tty)
- return 0;
+ tport = &port->state->port;
for (r = 0, w = 0; r < len; r++) {
if (str[r] & MAX3110_BREAK &&
@@ -364,20 +366,17 @@ receive_chars(struct uart_max3110 *max, unsigned short *str, int len)
}
}
- if (!w) {
- tty_kref_put(tty);
+ if (!w)
return 0;
- }
for (r = 0; w; r += usable, w -= usable) {
- usable = tty_buffer_request_room(tty, w);
+ usable = tty_buffer_request_room(tport, w);
if (usable) {
- tty_insert_flip_string(tty, buf + r, usable);
+ tty_insert_flip_string(tport, buf + r, usable);
port->icount.rx += usable;
}
}
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_flip_buffer_push(tport);
return r;
}
@@ -493,22 +492,12 @@ static int serial_m3110_startup(struct uart_port *port)
| WC_BAUD_DR2;
/* as we use thread to handle tx/rx, need set low latency */
- port->state->port.tty->low_latency = 1;
+ 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");
@@ -522,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;
@@ -545,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);
@@ -718,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,
@@ -748,32 +732,40 @@ static struct uart_driver serial_m3110_reg = {
.cons = &serial_m3110_console,
};
-#ifdef CONFIG_PM
-static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+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;
}
-static int serial_m3110_resume(struct spi_device *spi)
+static int serial_m3110_resume(struct device *dev)
{
+ struct spi_device *spi = to_spi_device(dev);
struct uart_max3110 *max = spi_get_drvdata(spi);
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;
}
+
+static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend,
+ serial_m3110_resume);
+#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops)
+
#else
-#define serial_m3110_suspend NULL
-#define serial_m3110_resume NULL
+#define SERIAL_M3110_PM_OPS NULL
#endif
-static int __devinit serial_m3110_probe(struct spi_device *spi)
+static int serial_m3110_probe(struct spi_device *spi)
{
struct uart_max3110 *max;
void *buffer;
@@ -802,6 +794,7 @@ static int __devinit 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;
@@ -839,11 +832,21 @@ static int __devinit 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;
@@ -855,7 +858,7 @@ err_get_page:
return ret;
}
-static int __devexit serial_m3110_remove(struct spi_device *dev)
+static int serial_m3110_remove(struct spi_device *dev)
{
struct uart_max3110 *max = spi_get_drvdata(dev);
@@ -866,6 +869,9 @@ static int __devexit 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);
@@ -877,11 +883,10 @@ static struct spi_driver uart_max3110_driver = {
.driver = {
.name = "spi_max3111",
.owner = THIS_MODULE,
+ .pm = SERIAL_M3110_PM_OPS,
},
.probe = serial_m3110_probe,
- .remove = __devexit_p(serial_m3110_remove),
- .suspend = serial_m3110_suspend,
- .resume = serial_m3110_resume,
+ .remove = serial_m3110_remove,
};
static int __init serial_m3110_init(void)
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 8131e2c2801..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)
@@ -91,14 +100,14 @@ static void msm_enable_ms(struct uart_port *port)
static void handle_rx_dm(struct uart_port *port, unsigned int misr)
{
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
unsigned int sr;
int count = 0;
struct msm_port *msm_port = UART_TO_MSM(port);
if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) {
port->icount.overrun++;
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
}
@@ -132,12 +141,15 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr)
port->icount.frame++;
/* TODO: handle sysrq */
- tty_insert_flip_string(tty, (char *) &c,
+ tty_insert_flip_string(tport, (char *)&c,
(count > 4) ? 4 : count);
count -= 4;
}
- tty_flip_buffer_push(tty);
+ 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);
@@ -146,7 +158,7 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr)
static void handle_rx(struct uart_port *port)
{
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
unsigned int sr;
/*
@@ -155,7 +167,7 @@ static void handle_rx(struct uart_port *port)
*/
if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) {
port->icount.overrun++;
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
}
@@ -186,55 +198,72 @@ static void handle_rx(struct uart_port *port)
}
if (!uart_handle_sysrq_char(port, c))
- tty_insert_flip_char(tty, c, flag);
+ tty_insert_flip_char(tport, c, flag);
}
- tty_flip_buffer_push(tty);
+ 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);
@@ -408,9 +433,8 @@ static void msm_init_clock(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
- clk_enable(msm_port->clk);
- if (!IS_ERR(msm_port->pclk))
- clk_enable(msm_port->pclk);
+ clk_prepare_enable(msm_port->clk);
+ clk_prepare_enable(msm_port->pclk);
msm_serial_set_mnd_regs(port);
}
@@ -486,7 +510,7 @@ static void msm_shutdown(struct uart_port *port)
msm_port->imr = 0;
msm_write(port, 0, UART_IMR); /* disable interrupts */
- clk_disable(msm_port->clk);
+ clk_disable_unprepare(msm_port->clk);
free_irq(port->irq, 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)
@@ -688,20 +667,129 @@ static void msm_power(struct uart_port *port, unsigned int state,
switch (state) {
case 0:
- clk_enable(msm_port->clk);
- if (!IS_ERR(msm_port->pclk))
- clk_enable(msm_port->pclk);
+ clk_prepare_enable(msm_port->clk);
+ clk_prepare_enable(msm_port->pclk);
break;
case 3:
- clk_disable(msm_port->clk);
- if (!IS_ERR(msm_port->pclk))
- clk_disable(msm_port->pclk);
+ clk_disable_unprepare(msm_port->clk);
+ 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,25 +1011,23 @@ 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 = clk_get(&pdev->dev, "gsbi_uart_clk");
- msm_port->pclk = clk_get(&pdev->dev, "gsbi_pclk");
- } else {
- msm_port->clk = 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 (unlikely(IS_ERR(msm_port->clk) || (IS_ERR(msm_port->pclk) &&
- msm_port->is_uartdm)))
- 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);
- if (msm_port->is_uartdm)
- clk_set_rate(msm_port->clk, 7372800);
+ clk_set_rate(msm_port->clk, 1843200);
+ }
port->uartclk = clk_get_rate(msm_port->clk);
printk(KERN_INFO "uartclk = %d\n", port->uartclk);
@@ -917,22 +1048,24 @@ static int __init msm_serial_probe(struct platform_device *pdev)
return uart_add_one_port(&msm_uart_driver, port);
}
-static int __devexit msm_serial_remove(struct platform_device *pdev)
+static int msm_serial_remove(struct platform_device *pdev)
{
- struct msm_port *msm_port = platform_get_drvdata(pdev);
+ struct uart_port *port = platform_get_drvdata(pdev);
- clk_put(msm_port->clk);
+ uart_remove_one_port(&msm_uart_driver, port);
return 0;
}
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,
@@ -948,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 5e85e1e14c4..48e94961a9e 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -50,7 +50,6 @@
#include <linux/atomic.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <mach/hardware.h>
#include <mach/dma.h>
@@ -402,7 +401,7 @@ static int msm_hs_request_port(struct uart_port *port)
return 0;
}
-static int __devexit msm_hs_remove(struct platform_device *pdev)
+static int msm_hs_remove(struct platform_device *pdev)
{
struct msm_hs_port *msm_uport;
@@ -908,7 +907,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr,
unsigned int error_f = 0;
unsigned long flags;
unsigned int flush;
- struct tty_struct *tty;
+ struct tty_port *port;
struct uart_port *uport;
struct msm_hs_port *msm_uport;
@@ -918,7 +917,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr,
spin_lock_irqsave(&uport->lock, flags);
clk_enable(msm_uport->clk);
- tty = uport->state->port.tty;
+ port = &uport->state->port;
msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
@@ -927,7 +926,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr,
/* overflow is not connect to data in a FIFO */
if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) &&
(uport->read_status_mask & CREAD))) {
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
uport->icount.buf_overrun++;
error_f = 1;
}
@@ -940,7 +939,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr,
uport->icount.parity++;
error_f = 1;
if (uport->ignore_status_mask & IGNPAR)
- tty_insert_flip_char(tty, 0, TTY_PARITY);
+ tty_insert_flip_char(port, 0, TTY_PARITY);
}
if (error_f)
@@ -960,7 +959,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr,
rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
if (0 != (uport->read_status_mask & CREAD)) {
- retval = tty_insert_flip_string(tty, msm_uport->rx.buffer,
+ retval = tty_insert_flip_string(port, msm_uport->rx.buffer,
rx_count);
BUG_ON(retval != rx_count);
}
@@ -980,9 +979,8 @@ static void msm_hs_tty_flip_buffer_work(struct work_struct *work)
{
struct msm_hs_port *msm_uport =
container_of(work, struct msm_hs_port, rx.tty_work);
- struct tty_struct *tty = msm_uport->uport.state->port.tty;
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&msm_uport->uport.state->port);
}
/*
@@ -1345,7 +1343,6 @@ static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev)
unsigned long flags;
struct msm_hs_port *msm_uport = dev;
struct uart_port *uport = &msm_uport->uport;
- struct tty_struct *tty = NULL;
spin_lock_irqsave(&uport->lock, flags);
if (msm_uport->clk_state == MSM_HS_CLK_OFF) {
@@ -1362,8 +1359,7 @@ static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev)
* optionally inject char into tty rx */
msm_hs_request_clock_on_locked(uport);
if (msm_uport->rx_wakeup.inject_rx) {
- tty = uport->state->port.tty;
- tty_insert_flip_char(tty,
+ tty_insert_flip_char(&uport->state->port,
msm_uport->rx_wakeup.rx_to_inject,
TTY_NORMAL);
queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work);
@@ -1401,7 +1397,7 @@ static int msm_hs_startup(struct uart_port *uport)
/* do not let tty layer execute RX in global workqueue, use a
* dedicated workqueue managed by this driver */
- uport->state->port.tty->low_latency = 1;
+ uport->state->port.low_latency = 1;
/* turn on uart clk */
ret = msm_hs_init_clk_locked(uport);
@@ -1522,7 +1518,7 @@ err_msm_hs_init_clk:
}
/* Initialize tx and rx data structures */
-static int __devinit uartdm_init_port(struct uart_port *uport)
+static int uartdm_init_port(struct uart_port *uport)
{
int ret = 0;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
@@ -1615,14 +1611,14 @@ err_tx_command_ptr_ptr:
return ret;
}
-static int __devinit msm_hs_probe(struct platform_device *pdev)
+static int msm_hs_probe(struct platform_device *pdev)
{
int ret;
struct uart_port *uport;
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);
@@ -1839,7 +1835,7 @@ static const struct dev_pm_ops msm_hs_dev_pm_ops = {
static struct platform_driver msm_serial_hs_platform_driver = {
.probe = msm_hs_probe,
- .remove = __devexit_p(msm_hs_remove),
+ .remove = msm_hs_remove,
.driver = {
.name = "msm_serial_hs",
.owner = THIS_MODULE,
diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c
index 4f41dcdcb77..1238ac370bf 100644
--- a/drivers/tty/serial/msm_smd_tty.c
+++ b/drivers/tty/serial/msm_smd_tty.c
@@ -70,7 +70,7 @@ static void smd_tty_notify(void *priv, unsigned event)
if (avail == 0)
break;
- avail = tty_prepare_flip_string(tty, &ptr, avail);
+ avail = tty_prepare_flip_string(&info->port, &ptr, avail);
if (smd_read(info->ch, ptr, avail) != avail) {
/* shouldn't be possible since we're in interrupt
@@ -80,7 +80,7 @@ static void smd_tty_notify(void *priv, unsigned event)
pr_err("OOPS - smd_tty_buffer mismatch?!");
}
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&info->port);
}
/* XXX only when writable and necessary */
@@ -90,13 +90,13 @@ static void smd_tty_notify(void *priv, unsigned event)
static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty)
{
+ struct smd_tty_info *info = container_of(tport, struct smd_tty_info,
+ port);
int i, res = 0;
- int n = tty->index;
const char *name = NULL;
- struct smd_tty_info *info = smd_tty + n;
for (i = 0; i < smd_tty_channels_len; i++) {
- if (smd_tty_channels[i].id == n) {
+ if (smd_tty_channels[i].id == tty->index) {
name = smd_tty_channels[i].name;
break;
}
@@ -117,17 +117,13 @@ static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty)
static void smd_tty_port_shutdown(struct tty_port *tport)
{
- struct smd_tty_info *info;
- struct tty_struct *tty = tty_port_tty_get(tport);
+ struct smd_tty_info *info = container_of(tport, struct smd_tty_info,
+ port);
- info = tty->driver_data;
if (info->ch) {
smd_close(info->ch);
info->ch = 0;
}
-
- tty->driver_data = 0;
- tty_kref_put(tty);
}
static int smd_tty_open(struct tty_struct *tty, struct file *f)
@@ -203,7 +199,6 @@ static int __init smd_tty_init(void)
if (smd_tty_driver == 0)
return -ENOMEM;
- smd_tty_driver->owner = THIS_MODULE;
smd_tty_driver->driver_name = "smd_tty_driver";
smd_tty_driver->name = "smd";
smd_tty_driver->major = 0;
@@ -224,9 +219,11 @@ static int __init smd_tty_init(void)
return ret;
for (i = 0; i < smd_tty_channels_len; i++) {
- tty_port_init(&smd_tty[smd_tty_channels[i].id].port);
- smd_tty[smd_tty_channels[i].id].port.ops = &smd_tty_port_ops;
- tty_register_device(smd_tty_driver, smd_tty_channels[i].id, 0);
+ struct tty_port *port = &smd_tty[smd_tty_channels[i].id].port;
+ tty_port_init(port);
+ port->ops = &smd_tty_port_ops;
+ tty_port_register_device(port, smd_tty_driver,
+ smd_tty_channels[i].id, NULL);
}
return 0;
diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c
index 06f6aefd5ba..be127d0da32 100644
--- a/drivers/tty/serial/mux.c
+++ b/drivers/tty/serial/mux.c
@@ -17,7 +17,6 @@
*/
#include <linux/module.h>
-#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
@@ -30,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
@@ -243,8 +242,8 @@ static void mux_write(struct uart_port *port)
*/
static void mux_read(struct uart_port *port)
{
+ struct tty_port *tport = &port->state->port;
int data;
- struct tty_struct *tty = port->state->port.tty;
__u32 start_count = port->icount.rx;
while(1) {
@@ -267,12 +266,11 @@ static void mux_read(struct uart_port *port)
if (uart_handle_sysrq_char(port, data & 0xffu))
continue;
- tty_insert_flip_char(tty, data & 0xFF, TTY_NORMAL);
+ tty_insert_flip_char(tport, data & 0xFF, TTY_NORMAL);
}
- if (start_count != port->icount.rx) {
- tty_flip_buffer_push(tty);
- }
+ if (start_count != port->icount.rx)
+ tty_flip_buffer_push(tport);
}
/**
@@ -499,7 +497,7 @@ static int __init mux_probe(struct parisc_device *dev)
port->membase = ioremap_nocache(port->mapbase, MUX_LINE_OFFSET);
port->iotype = UPIO_MEM;
port->type = PORT_MUX;
- port->irq = NO_IRQ;
+ port->irq = 0;
port->uartclk = 0;
port->fifosize = MUX_FIFO_SIZE;
port->ops = &mux_pops;
@@ -521,7 +519,7 @@ static int __init mux_probe(struct parisc_device *dev)
return 0;
}
-static int __devexit mux_remove(struct parisc_device *dev)
+static int mux_remove(struct parisc_device *dev)
{
int i, j;
int port_count = (long)dev_get_drvdata(&dev->dev);
@@ -572,14 +570,14 @@ static struct parisc_driver builtin_serial_mux_driver = {
.name = "builtin_serial_mux",
.id_table = builtin_mux_tbl,
.probe = mux_probe,
- .remove = __devexit_p(mux_remove),
+ .remove = mux_remove,
};
static struct parisc_driver serial_mux_driver = {
.name = "serial_mux",
.id_table = mux_tbl,
.probe = mux_probe,
- .remove = __devexit_p(mux_remove),
+ .remove = mux_remove,
};
/**
@@ -615,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 55fd362b987..86de4477d98 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -32,10 +32,14 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <asm/cacheflush.h>
#define MXS_AUART_PORTS 5
+#define MXS_AUART_FIFO_SIZE 16
#define AUART_CTRL0 0x00000000
#define AUART_CTRL0_SET 0x00000004
@@ -69,8 +73,18 @@
#define AUART_CTRL0_SFTRST (1 << 31)
#define AUART_CTRL0_CLKGATE (1 << 30)
+#define AUART_CTRL0_RXTO_ENABLE (1 << 27)
+#define AUART_CTRL0_RXTIMEOUT(v) (((v) & 0x7ff) << 16)
+#define AUART_CTRL0_XFER_COUNT(v) ((v) & 0xffff)
+
+#define AUART_CTRL1_XFER_COUNT(v) ((v) & 0xffff)
+
+#define AUART_CTRL2_DMAONERR (1 << 26)
+#define AUART_CTRL2_TXDMAE (1 << 25)
+#define AUART_CTRL2_RXDMAE (1 << 24)
#define AUART_CTRL2_CTSEN (1 << 15)
+#define AUART_CTRL2_RTSEN (1 << 14)
#define AUART_CTRL2_RTS (1 << 11)
#define AUART_CTRL2_RXE (1 << 9)
#define AUART_CTRL2_TXE (1 << 8)
@@ -108,29 +122,165 @@
#define AUART_STAT_BERR (1 << 18)
#define AUART_STAT_PERR (1 << 17)
#define AUART_STAT_FERR (1 << 16)
+#define AUART_STAT_RXCOUNT_MASK 0xffff
static struct uart_driver auart_driver;
+enum mxs_auart_type {
+ IMX23_AUART,
+ IMX28_AUART,
+};
+
struct mxs_auart_port {
struct uart_port port;
- unsigned int flags;
+#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;
unsigned int irq;
struct clk *clk;
struct device *dev;
+
+ /* for DMA */
+ struct scatterlist tx_sgl;
+ struct dma_chan *tx_dma_chan;
+ void *tx_dma_buf;
+
+ struct scatterlist rx_sgl;
+ struct dma_chan *rx_dma_chan;
+ void *rx_dma_buf;
+};
+
+static struct platform_device_id mxs_auart_devtype[] = {
+ { .name = "mxs-auart-imx23", .driver_data = IMX23_AUART },
+ { .name = "mxs-auart-imx28", .driver_data = IMX28_AUART },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, mxs_auart_devtype);
+
+static struct of_device_id mxs_auart_dt_ids[] = {
+ {
+ .compatible = "fsl,imx28-auart",
+ .data = &mxs_auart_devtype[IMX28_AUART]
+ }, {
+ .compatible = "fsl,imx23-auart",
+ .data = &mxs_auart_devtype[IMX23_AUART]
+ }, { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids);
+
+static inline int is_imx28_auart(struct mxs_auart_port *s)
+{
+ return s->devtype == IMX28_AUART;
+}
+
+static inline bool auart_dma_enabled(struct mxs_auart_port *s)
+{
+ return s->flags & MXS_AUART_DMA_ENABLED;
+}
static void mxs_auart_stop_tx(struct uart_port *u);
#define to_auart_port(u) container_of(u, struct mxs_auart_port, port)
-static inline void mxs_auart_tx_chars(struct mxs_auart_port *s)
+static void mxs_auart_tx_chars(struct mxs_auart_port *s);
+
+static void dma_tx_callback(void *param)
+{
+ struct mxs_auart_port *s = param;
+ struct circ_buf *xmit = &s->port.state->xmit;
+
+ dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE);
+
+ /* clear the bit used to serialize the DMA tx. */
+ clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
+ smp_mb__after_atomic();
+
+ /* wake up the possible processes. */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+
+ mxs_auart_tx_chars(s);
+}
+
+static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sgl = &s->tx_sgl;
+ struct dma_chan *channel = s->tx_dma_chan;
+ u32 pio;
+
+ /* [1] : send PIO. Note, the first pio word is CTRL1. */
+ pio = AUART_CTRL1_XFER_COUNT(size);
+ desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)&pio,
+ 1, DMA_TRANS_NONE, 0);
+ if (!desc) {
+ dev_err(s->dev, "step 1 error\n");
+ return -EINVAL;
+ }
+
+ /* [2] : set DMA buffer. */
+ sg_init_one(sgl, s->tx_dma_buf, size);
+ dma_map_sg(s->dev, sgl, 1, DMA_TO_DEVICE);
+ desc = dmaengine_prep_slave_sg(channel, sgl,
+ 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(s->dev, "step 2 error\n");
+ return -EINVAL;
+ }
+
+ /* [3] : submit the DMA */
+ desc->callback = dma_tx_callback;
+ desc->callback_param = s;
+ dmaengine_submit(desc);
+ dma_async_issue_pending(channel);
+ return 0;
+}
+
+static void mxs_auart_tx_chars(struct mxs_auart_port *s)
{
struct circ_buf *xmit = &s->port.state->xmit;
+ if (auart_dma_enabled(s)) {
+ u32 i = 0;
+ int size;
+ void *buffer = s->tx_dma_buf;
+
+ if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags))
+ return;
+
+ while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
+ size = min_t(u32, UART_XMIT_SIZE - i,
+ CIRC_CNT_TO_END(xmit->head,
+ xmit->tail,
+ UART_XMIT_SIZE));
+ memcpy(buffer + i, xmit->buf + xmit->tail, size);
+ xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1);
+
+ i += size;
+ if (i >= UART_XMIT_SIZE)
+ break;
+ }
+
+ if (uart_tx_stopped(&s->port))
+ mxs_auart_stop_tx(&s->port);
+
+ if (i) {
+ mxs_auart_dma_tx(s, i);
+ } else {
+ clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
+ smp_mb__after_atomic();
+ }
+ return;
+ }
+
+
while (!(readl(s->port.membase + AUART_STAT) &
AUART_STAT_TXFF)) {
if (s->port.x_char) {
@@ -209,7 +359,6 @@ out:
static void mxs_auart_rx_chars(struct mxs_auart_port *s)
{
- struct tty_struct *tty = s->port.state->port.tty;
u32 stat = 0;
for (;;) {
@@ -220,7 +369,7 @@ static void mxs_auart_rx_chars(struct mxs_auart_port *s)
}
writel(stat, s->port.membase + AUART_STAT);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&s->port.state->port);
}
static int mxs_auart_request_port(struct uart_port *u)
@@ -257,9 +406,14 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
u32 ctrl = readl(u->membase + AUART_CTRL2);
- ctrl &= ~AUART_CTRL2_RTS;
- if (mctrl & TIOCM_RTS)
- ctrl |= AUART_CTRL2_RTS;
+ ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS);
+ if (mctrl & TIOCM_RTS) {
+ if (tty_port_cts_enabled(&u->state->port))
+ ctrl |= AUART_CTRL2_RTSEN;
+ else
+ ctrl |= AUART_CTRL2_RTS;
+ }
+
s->ctrl = mctrl;
writel(ctrl, u->membase + AUART_CTRL2);
}
@@ -281,10 +435,136 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u)
return mctrl;
}
+static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s);
+static void dma_rx_callback(void *arg)
+{
+ struct mxs_auart_port *s = (struct mxs_auart_port *) arg;
+ struct tty_port *port = &s->port.state->port;
+ int count;
+ u32 stat;
+
+ dma_unmap_sg(s->dev, &s->rx_sgl, 1, DMA_FROM_DEVICE);
+
+ stat = readl(s->port.membase + AUART_STAT);
+ stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR |
+ AUART_STAT_PERR | AUART_STAT_FERR);
+
+ count = stat & AUART_STAT_RXCOUNT_MASK;
+ tty_insert_flip_string(port, s->rx_dma_buf, count);
+
+ writel(stat, s->port.membase + AUART_STAT);
+ tty_flip_buffer_push(port);
+
+ /* start the next DMA for RX. */
+ mxs_auart_dma_prep_rx(s);
+}
+
+static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sgl = &s->rx_sgl;
+ struct dma_chan *channel = s->rx_dma_chan;
+ u32 pio[1];
+
+ /* [1] : send PIO */
+ pio[0] = AUART_CTRL0_RXTO_ENABLE
+ | AUART_CTRL0_RXTIMEOUT(0x80)
+ | AUART_CTRL0_XFER_COUNT(UART_XMIT_SIZE);
+ desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
+ 1, DMA_TRANS_NONE, 0);
+ if (!desc) {
+ dev_err(s->dev, "step 1 error\n");
+ return -EINVAL;
+ }
+
+ /* [2] : send DMA request */
+ sg_init_one(sgl, s->rx_dma_buf, UART_XMIT_SIZE);
+ dma_map_sg(s->dev, sgl, 1, DMA_FROM_DEVICE);
+ desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(s->dev, "step 2 error\n");
+ return -1;
+ }
+
+ /* [3] : submit the DMA, but do not issue it. */
+ desc->callback = dma_rx_callback;
+ desc->callback_param = s;
+ dmaengine_submit(desc);
+ dma_async_issue_pending(channel);
+ return 0;
+}
+
+static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s)
+{
+ if (s->tx_dma_chan) {
+ dma_release_channel(s->tx_dma_chan);
+ s->tx_dma_chan = NULL;
+ }
+ if (s->rx_dma_chan) {
+ dma_release_channel(s->rx_dma_chan);
+ s->rx_dma_chan = NULL;
+ }
+
+ kfree(s->tx_dma_buf);
+ kfree(s->rx_dma_buf);
+ s->tx_dma_buf = NULL;
+ s->rx_dma_buf = NULL;
+}
+
+static void mxs_auart_dma_exit(struct mxs_auart_port *s)
+{
+
+ writel(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR,
+ s->port.membase + AUART_CTRL2_CLR);
+
+ mxs_auart_dma_exit_channel(s);
+ s->flags &= ~MXS_AUART_DMA_ENABLED;
+ clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
+ clear_bit(MXS_AUART_DMA_RX_READY, &s->flags);
+}
+
+static int mxs_auart_dma_init(struct mxs_auart_port *s)
+{
+ if (auart_dma_enabled(s))
+ return 0;
+
+ /* init for RX */
+ s->rx_dma_chan = dma_request_slave_channel(s->dev, "rx");
+ if (!s->rx_dma_chan)
+ goto err_out;
+ s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!s->rx_dma_buf)
+ goto err_out;
+
+ /* init for TX */
+ s->tx_dma_chan = dma_request_slave_channel(s->dev, "tx");
+ if (!s->tx_dma_chan)
+ goto err_out;
+ s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!s->tx_dma_buf)
+ goto err_out;
+
+ /* set the flags */
+ 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:
+ mxs_auart_dma_exit_channel(s);
+ return -EINVAL;
+
+}
+
static void mxs_auart_settermios(struct uart_port *u,
struct ktermios *termios,
struct ktermios *old)
{
+ struct mxs_auart_port *s = to_auart_port(u);
u32 bm, ctrl, ctrl2, div;
unsigned int cflag, baud;
@@ -324,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;
/*
@@ -356,10 +636,24 @@ static void mxs_auart_settermios(struct uart_port *u,
ctrl |= AUART_LINECTRL_STP2;
/* figure out the hardware flow control settings */
- if (cflag & CRTSCTS)
- ctrl2 |= AUART_CTRL2_CTSEN;
- else
- ctrl2 &= ~AUART_CTRL2_CTSEN;
+ if (cflag & CRTSCTS) {
+ /*
+ * The DMA has a bug(see errata:2836) in mx23.
+ * So we can not implement the DMA for auart in mx23,
+ * we can only implement the DMA support for auart
+ * in mx28.
+ */
+ 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
+ | AUART_CTRL2_DMAONERR;
+ }
+ ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN;
+ } else {
+ ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN);
+ }
/* set baud rate */
baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk);
@@ -369,15 +663,37 @@ static void mxs_auart_settermios(struct uart_port *u,
writel(ctrl, u->membase + AUART_LINECTRL);
writel(ctrl2, u->membase + AUART_CTRL2);
+
+ uart_update_timeout(u, termios->c_cflag, baud);
+
+ /* prepare for the DMA RX. */
+ if (auart_dma_enabled(s) &&
+ !test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) {
+ if (!mxs_auart_dma_prep_rx(s)) {
+ /* Disable the normal RX interrupt. */
+ writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN,
+ u->membase + AUART_INTR_CLR);
+ } else {
+ mxs_auart_dma_exit(s);
+ dev_err(s->dev, "We can not start up the DMA.\n");
+ }
+ }
}
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);
@@ -387,7 +703,8 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
}
if (istat & (AUART_INTR_RTIS | AUART_INTR_RXIS)) {
- mxs_auart_rx_chars(s);
+ if (!auart_dma_enabled(s))
+ mxs_auart_rx_chars(s);
istat &= ~(AUART_INTR_RTIS | AUART_INTR_RXIS);
}
@@ -396,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;
}
@@ -423,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);
@@ -434,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)
@@ -447,13 +764,16 @@ static void mxs_auart_shutdown(struct uart_port *u)
{
struct mxs_auart_port *s = to_auart_port(u);
- writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR);
+ if (auart_dma_enabled(s))
+ mxs_auart_dma_exit(s);
- writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET);
+ writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR);
writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
u->membase + AUART_INTR_CLR);
+ writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET);
+
clk_disable_unprepare(s->clk);
}
@@ -541,9 +861,9 @@ 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)
+ if (co->index >= MXS_AUART_PORTS || co->index < 0)
return;
s = auart_port[co->index];
@@ -562,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);
}
@@ -635,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);
@@ -672,8 +999,37 @@ static struct uart_driver auart_driver = {
#endif
};
-static int __devinit mxs_auart_probe(struct platform_device *pdev)
+/*
+ * This function returns 1 if pdev isn't a device instatiated by dt, 0 if it
+ * could successfully get all information from dt or a negative errno.
+ */
+static int serial_mxs_probe_dt(struct mxs_auart_port *s,
+ struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np)
+ /* no device tree device */
+ return 1;
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
+ return ret;
+ }
+ s->port.line = ret;
+
+ if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
+ set_bit(MXS_AUART_RTSCTS, &s->flags);
+
+ return 0;
+}
+
+static int mxs_auart_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mxs_auart_dt_ids, &pdev->dev);
struct mxs_auart_port *s;
u32 version;
int ret = 0;
@@ -685,6 +1041,17 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev)
goto out;
}
+ ret = serial_mxs_probe_dt(s, pdev);
+ if (ret > 0)
+ s->port.line = pdev->id < 0 ? 0 : pdev->id;
+ else if (ret < 0)
+ goto out_free;
+
+ if (of_id) {
+ pdev->id_entry = of_id->data;
+ s->devtype = pdev->id_entry->driver_data;
+ }
+
s->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(s->clk)) {
ret = PTR_ERR(s->clk);
@@ -701,13 +1068,11 @@ static int __devinit 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.line = pdev->id < 0 ? 0 : pdev->id;
- 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 = get_device(&pdev->dev);
+ s->port.dev = s->dev = &pdev->dev;
- s->flags = 0;
s->ctrl = 0;
s->irq = platform_get_irq(pdev, 0);
@@ -718,7 +1083,7 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, s);
- auart_port[pdev->id] = s;
+ auart_port[s->port.line] = s;
mxs_auart_reset(&s->port);
@@ -744,7 +1109,7 @@ out:
return ret;
}
-static int __devexit mxs_auart_remove(struct platform_device *pdev)
+static int mxs_auart_remove(struct platform_device *pdev)
{
struct mxs_auart_port *s = platform_get_drvdata(pdev);
@@ -761,10 +1126,11 @@ static int __devexit mxs_auart_remove(struct platform_device *pdev)
static struct platform_driver mxs_auart_driver = {
.probe = mxs_auart_probe,
- .remove = __devexit_p(mxs_auart_remove),
+ .remove = mxs_auart_remove,
.driver = {
.name = "mxs-auart",
.owner = THIS_MODULE,
+ .of_match_table = mxs_auart_dt_ids,
},
};
@@ -797,3 +1163,4 @@ module_init(mxs_auart_init);
module_exit(mxs_auart_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Freescale MXS application uart driver");
+MODULE_ALIAS("platform:mxs-auart");
diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c
index d40da78e7c8..7a6745601d4 100644
--- a/drivers/tty/serial/netx-serial.c
+++ b/drivers/tty/serial/netx-serial.c
@@ -196,10 +196,9 @@ 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;
- struct tty_struct *tty = port->state->port.tty;
while (!(readl(port->membase + UART_FR) & FR_RXFE)) {
rx = readl(port->membase + UART_DR);
@@ -237,8 +236,9 @@ static void netx_rxint(struct uart_port *port)
uart_insert_char(port, status, SR_OE, rx, flg);
}
- tty_flip_buffer_push(tty);
- return;
+ 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)
@@ -252,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) {
@@ -419,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;
@@ -695,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 dd4c31d1aee..693bc6c2561 100644
--- a/drivers/tty/serial/nwpserial.c
+++ b/drivers/tty/serial/nwpserial.c
@@ -128,7 +128,7 @@ static void nwpserial_config_port(struct uart_port *port, int flags)
static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
{
struct nwpserial_port *up = dev_id;
- struct tty_struct *tty = up->port.state->port.tty;
+ struct tty_port *port = &up->port.state->port;
irqreturn_t ret;
unsigned int iir;
unsigned char ch;
@@ -146,10 +146,13 @@ static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
up->port.icount.rx++;
ch = dcr_read(up->dcr_host, UART_RX);
if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID)
- tty_insert_flip_char(tty, ch, TTY_NORMAL);
+ tty_insert_flip_char(port, ch, TTY_NORMAL);
} while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR);
- tty_flip_buffer_push(tty);
+ spin_unlock(&up->port.lock);
+ tty_flip_buffer_push(port);
+ spin_lock(&up->port.lock);
+
ret = IRQ_HANDLED;
/* clear interrupt */
@@ -199,7 +202,7 @@ static void nwpserial_shutdown(struct uart_port *port)
dcr_write(up->dcr_host, UART_IER, up->ier);
/* free irq */
- free_irq(up->port.irq, port);
+ free_irq(up->port.irq, up);
}
static int nwpserial_verify_port(struct uart_port *port,
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index e8c9cee07d0..68d4455f3cf 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -9,26 +9,53 @@
* 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>
#include <linux/serial_core.h>
-#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/nwpserial.h>
+#include <linux/clk.h>
+
+#include "8250/8250.h"
struct of_serial_info {
+ struct clk *clk;
int type;
int line;
};
+#ifdef CONFIG_ARCH_TEGRA
+void tegra_serial_handle_break(struct uart_port *p)
+{
+ unsigned int status, tmout = 10000;
+
+ do {
+ status = p->serial_in(p, UART_LSR);
+ if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
+ status = p->serial_in(p, UART_RX);
+ else
+ break;
+ if (--tmout == 0)
+ break;
+ udelay(1);
+ } while (1);
+}
+#else
+static inline void tegra_serial_handle_break(struct uart_port *port)
+{
+}
+#endif
+
/*
* Fill a struct uart_port for a given device node
*/
-static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
- int type, struct uart_port *port)
+static int of_platform_serial_setup(struct platform_device *ofdev,
+ int type, struct uart_port *port,
+ struct of_serial_info *info)
{
struct resource resource;
struct device_node *np = ofdev->dev.of_node;
@@ -37,8 +64,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
memset(port, 0, sizeof *port);
if (of_property_read_u32(np, "clock-frequency", &clk)) {
- dev_warn(&ofdev->dev, "no clock-frequency property set\n");
- return -ENODEV;
+
+ /* Get clk rate through clk driver if present */
+ info->clk = clk_get(&ofdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ dev_warn(&ofdev->dev,
+ "clk or clock-frequency not defined\n");
+ return PTR_ERR(info->clk);
+ }
+
+ clk_prepare_enable(info->clk);
+ clk = clk_get_rate(info->clk);
}
/* If current-speed was set, then try not to change it. */
if (of_property_read_u32(np, "current-speed", &spd) == 0)
@@ -47,7 +83,7 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
ret = of_address_to_resource(np, 0, &resource);
if (ret) {
dev_warn(&ofdev->dev, "invalid address\n");
- return ret;
+ goto out;
}
spin_lock_init(&port->lock);
@@ -61,6 +97,10 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
if (of_property_read_u32(np, "reg-shift", &prop) == 0)
port->regshift = prop;
+ /* Check for fifo size */
+ if (of_property_read_u32(np, "fifo-size", &prop) == 0)
+ port->fifosize = prop;
+
port->irq = irq_of_parse_and_map(np, 0);
port->iotype = UPIO_MEM;
if (of_property_read_u32(np, "reg-io-width", &prop) == 0) {
@@ -74,7 +114,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
default:
dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n",
prop);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
}
@@ -82,16 +123,27 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
port->uartclk = clk;
port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP
| UPF_FIXED_PORT | UPF_FIXED_TYPE;
+
+ if (of_find_property(np, "no-loopback-test", NULL))
+ port->flags |= UPF_SKIP_TEST;
+
port->dev = &ofdev->dev;
+ if (type == PORT_TEGRA)
+ port->handle_break = tegra_serial_handle_break;
+
return 0;
+out:
+ if (info->clk)
+ clk_disable_unprepare(info->clk);
+ return ret;
}
/*
* Try to register a serial port
*/
static struct of_device_id of_platform_serial_table[];
-static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
+static int of_platform_serial_probe(struct platform_device *ofdev)
{
const struct of_device_id *match;
struct of_serial_info *info;
@@ -111,15 +163,33 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
return -ENOMEM;
port_type = (unsigned long)match->data;
- ret = of_platform_serial_setup(ofdev, port_type, &port);
+ ret = of_platform_serial_setup(ofdev, port_type, &port, info);
if (ret)
goto out;
switch (port_type) {
#ifdef CONFIG_SERIAL_8250
case PORT_8250 ... PORT_MAX_8250:
- ret = serial8250_register_port(&port);
+ {
+ struct uart_8250_port port8250;
+ memset(&port8250, 0, sizeof(port8250));
+ port.type = port_type;
+ port8250.port = port;
+
+ if (port.fifosize)
+ port8250.capabilities = UART_CAP_FIFO;
+
+ if (of_property_read_bool(ofdev->dev.of_node,
+ "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;
+ }
#endif
#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
case PORT_NWPSERIAL:
@@ -138,7 +208,7 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
info->type = port_type;
info->line = ret;
- dev_set_drvdata(&ofdev->dev, info);
+ platform_set_drvdata(ofdev, info);
return 0;
out:
kfree(info);
@@ -151,7 +221,7 @@ out:
*/
static int of_platform_serial_remove(struct platform_device *ofdev)
{
- struct of_serial_info *info = dev_get_drvdata(&ofdev->dev);
+ struct of_serial_info *info = platform_get_drvdata(ofdev);
switch (info->type) {
#ifdef CONFIG_SERIAL_8250
case PORT_8250 ... PORT_MAX_8250:
@@ -167,6 +237,9 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
/* need to add code for these */
break;
}
+
+ if (info->clk)
+ clk_disable_unprepare(info->clk);
kfree(info);
return 0;
}
@@ -174,7 +247,7 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
/*
* A few common types, add more as needed.
*/
-static struct of_device_id __devinitdata of_platform_serial_table[] = {
+static struct of_device_id of_platform_serial_table[] = {
{ .compatible = "ns8250", .data = (void *)PORT_8250, },
{ .compatible = "ns16450", .data = (void *)PORT_16450, },
{ .compatible = "ns16550a", .data = (void *)PORT_16550A, },
@@ -182,6 +255,13 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = {
{ .compatible = "ns16750", .data = (void *)PORT_16750, },
{ .compatible = "ns16850", .data = (void *)PORT_16850, },
{ .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, },
+ { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, },
+ { .compatible = "altr,16550-FIFO32",
+ .data = (void *)PORT_ALTR_16550_F32, },
+ { .compatible = "altr,16550-FIFO64",
+ .data = (void *)PORT_ALTR_16550_F64, },
+ { .compatible = "altr,16550-FIFO128",
+ .data = (void *)PORT_ALTR_16550_F128, },
#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
{ .compatible = "ibm,qpace-nwp-serial",
.data = (void *)PORT_NWPSERIAL, },
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index f80904145fd..d017cec8a34 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -32,37 +32,154 @@
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
+#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/serial_core.h>
#include <linux/irq.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/serial-omap.h>
-#include <plat/dma.h>
-#include <plat/dmtimer.h>
-#include <plat/omap-serial.h>
+#include <dt-bindings/gpio/gpio.h>
+
+#define OMAP_MAX_HSUART_PORTS 6
+
+#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y))
+
+#define OMAP_UART_REV_42 0x0402
+#define OMAP_UART_REV_46 0x0406
+#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)
#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
/* SCR register bitmasks */
#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7)
+#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6)
+#define OMAP_UART_SCR_TX_EMPTY (1 << 3)
/* FCR register bitmasks */
-#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6
#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6)
+#define OMAP_UART_FCR_TX_FIFO_TRIG_MASK (0x3 << 4)
+
+/* MVR register bitmasks */
+#define OMAP_UART_MVR_SCHEME_SHIFT 30
+
+#define OMAP_UART_LEGACY_MVR_MAJ_MASK 0xf0
+#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT 4
+#define OMAP_UART_LEGACY_MVR_MIN_MASK 0x0f
+
+#define OMAP_UART_MVR_MAJ_MASK 0x700
+#define OMAP_UART_MVR_MAJ_SHIFT 8
+#define OMAP_UART_MVR_MIN_MASK 0x3f
+
+#define OMAP_UART_DMA_CH_FREE -1
+
+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
+#define OMAP_MODE13X_SPEED 230400
+
+/* WER = 0x7F
+ * Enable module level wakeup in WER reg
+ */
+#define OMAP_UART_WER_MOD_WKUP 0X7F
+
+/* Enable XON/XOFF flow control on output */
+#define OMAP_UART_SW_TX 0x08
+
+/* Enable XON/XOFF flow control on input */
+#define OMAP_UART_SW_RX 0x02
+
+#define OMAP_UART_SW_CLR 0xF0
+
+#define OMAP_UART_TCR_TRIG 0x0F
+
+struct uart_omap_dma {
+ u8 uart_dma_tx;
+ u8 uart_dma_rx;
+ int rx_dma_channel;
+ int tx_dma_channel;
+ dma_addr_t rx_buf_dma_phys;
+ dma_addr_t tx_buf_dma_phys;
+ unsigned int uart_base;
+ /*
+ * Buffer for rx dma.It is not required for tx because the buffer
+ * comes from port structure.
+ */
+ unsigned char *rx_buf;
+ unsigned int prev_rx_dma_pos;
+ int tx_buf_size;
+ int tx_dma_used;
+ int rx_dma_used;
+ spinlock_t tx_lock;
+ spinlock_t rx_lock;
+ /* timer to poll activity on rx dma */
+ struct timer_list rx_timer;
+ unsigned int rx_buf_size;
+ unsigned int rx_poll_rate;
+ unsigned int rx_timeout;
+};
+
+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;
+ unsigned char mcr;
+ unsigned char fcr;
+ unsigned char efr;
+ unsigned char dll;
+ unsigned char dlh;
+ unsigned char mdr1;
+ unsigned char scr;
+ unsigned char wer;
+
+ int use_dma;
+ /*
+ * Some bits in registers are cleared on a read, so they must
+ * be saved whenever the register is read but the bits will not
+ * be immediately processed.
+ */
+ unsigned int lsr_break_flag;
+ unsigned char msr_saved_flags;
+ char name[20];
+ unsigned long port_activity;
+ int context_loss_cnt;
+ u32 errata;
+ u8 wakeups_enabled;
+ u32 features;
+
+ struct serial_rs485 rs485;
+ int rts_gpio;
+
+ struct pm_qos_request pm_qos_request;
+ u32 latency;
+ u32 calc_latency;
+ struct work_struct qos_work;
+ bool is_suspending;
+};
+
+#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 uart_tx_dma_callback(int lch, u16 ch_status, void *data);
-static void serial_omap_rxdma_poll(unsigned long uart_no);
-static int serial_omap_start_rxdma(struct uart_omap_port *up);
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;
@@ -83,172 +200,175 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
serial_out(up, UART_FCR, 0);
}
+static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
+{
+ struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
+
+ if (!pdata || !pdata->get_context_loss_count)
+ return -EINVAL;
+
+ 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 = 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;
+
+ pdata->enable_wakeup(up->dev, enable);
+}
+
+/*
+ * serial_omap_baud_is_mode16 - check if baud rate is MODE16X
+ * @port: uart port info
+ * @baud: baudrate for which mode needs to be determined
+ *
+ * Returns true if baud rate is MODE16X and false if MODE13X
+ * Original table in OMAP TRM named "UART Mode Baud Rates, Divisor Values,
+ * and Error Rates" determines modes not for all common baud rates.
+ * E.g. for 1000000 baud rate mode must be 16x, but according to that
+ * table it's determined as 13x.
+ */
+static bool
+serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
+{
+ unsigned int n13 = port->uartclk / (13 * 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)
+ baudAbsDiff13 = -baudAbsDiff13;
+ if (baudAbsDiff16 < 0)
+ baudAbsDiff16 = -baudAbsDiff16;
+
+ return (baudAbsDiff13 >= baudAbsDiff16);
+}
+
/*
* serial_omap_get_divisor - calculate divisor value
* @port: uart port info
* @baud: baudrate for which divisor needs to be calculated.
- *
- * We have written our own function to get the divisor so as to support
- * 13x mode. 3Mbps Baudrate as an different divisor.
- * Reference OMAP TRM Chapter 17:
- * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates
- * referring to oversampling - divisor value
- * baudrate 460,800 to 3,686,400 all have divisor 13
- * except 3,000,000 which has divisor value 16
*/
static unsigned int
serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
{
- unsigned int divisor;
+ unsigned int mode;
- if (baud > OMAP_MODE13X_SPEED && baud != 3000000)
- divisor = 13;
+ if (!serial_omap_baud_is_mode16(port, baud))
+ mode = 13;
else
- divisor = 16;
- return port->uartclk/(baud * divisor);
-}
-
-static void serial_omap_stop_rxdma(struct uart_omap_port *up)
-{
- if (up->uart_dma.rx_dma_used) {
- del_timer(&up->uart_dma.rx_timer);
- omap_stop_dma(up->uart_dma.rx_dma_channel);
- omap_free_dma(up->uart_dma.rx_dma_channel);
- up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
- up->uart_dma.rx_dma_used = false;
- pm_runtime_mark_last_busy(&up->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
- }
+ mode = 16;
+ return port->uartclk/(mode * baud);
}
static void serial_omap_enable_ms(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->port.line);
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
up->ier |= UART_IER_MSI;
serial_out(up, UART_IER, up->ier);
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
static void serial_omap_stop_tx(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
- struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
-
- if (up->use_dma &&
- up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) {
- /*
- * Check if dma is still active. If yes do nothing,
- * return. Else stop dma
- */
- if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel))
+ 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;
- omap_stop_dma(up->uart_dma.tx_dma_channel);
- omap_free_dma(up->uart_dma.tx_dma_channel);
- up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
- pm_runtime_mark_last_busy(&up->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
+ }
}
- pm_runtime_get_sync(&up->pdev->dev);
if (up->ier & UART_IER_THRI) {
up->ier &= ~UART_IER_THRI;
serial_out(up, UART_IER, up->ier);
}
- if (!up->use_dma && pdata->set_forceidle)
- pdata->set_forceidle(up->pdev);
+ 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->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
static void serial_omap_stop_rx(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
- pm_runtime_get_sync(&up->pdev->dev);
- if (up->use_dma)
- serial_omap_stop_rxdma(up);
- up->ier &= ~UART_IER_RLSI;
+ pm_runtime_get_sync(up->dev);
+ 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->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
-static inline void receive_chars(struct uart_omap_port *up,
- unsigned int *status)
-{
- struct tty_struct *tty = up->port.state->port.tty;
- unsigned int flag, lsr = *status;
- unsigned char ch = 0;
- int max_count = 256;
-
- do {
- if (likely(lsr & UART_LSR_DR))
- ch = serial_in(up, UART_RX);
- flag = TTY_NORMAL;
- up->port.icount.rx++;
-
- if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
- /*
- * For statistics only
- */
- if (lsr & UART_LSR_BI) {
- lsr &= ~(UART_LSR_FE | UART_LSR_PE);
- up->port.icount.brk++;
- /*
- * We do the SysRQ and SAK checking
- * here because otherwise the break
- * may get masked by ignore_status_mask
- * or read_status_mask.
- */
- if (uart_handle_break(&up->port))
- goto ignore_char;
- } else if (lsr & UART_LSR_PE) {
- up->port.icount.parity++;
- } else if (lsr & UART_LSR_FE) {
- up->port.icount.frame++;
- }
-
- if (lsr & UART_LSR_OE)
- up->port.icount.overrun++;
-
- /*
- * Mask off conditions which should be ignored.
- */
- lsr &= up->port.read_status_mask;
-
-#ifdef CONFIG_SERIAL_OMAP_CONSOLE
- if (up->port.line == up->port.cons->index) {
- /* Recover the break flag from console xmit */
- lsr |= up->lsr_break_flag;
- }
-#endif
- if (lsr & UART_LSR_BI)
- flag = TTY_BREAK;
- else if (lsr & UART_LSR_PE)
- flag = TTY_PARITY;
- else if (lsr & UART_LSR_FE)
- flag = TTY_FRAME;
- }
-
- if (uart_handle_sysrq_char(&up->port, ch))
- goto ignore_char;
- uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
-ignore_char:
- lsr = serial_in(up, UART_LSR);
- } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
- spin_unlock(&up->port.lock);
- tty_flip_buffer_push(tty);
- spin_lock(&up->port.lock);
-}
-
-static void transmit_chars(struct uart_omap_port *up)
+static void transmit_chars(struct uart_omap_port *up, unsigned int lsr)
{
struct circ_buf *xmit = &up->port.state->xmit;
int count;
@@ -289,70 +409,61 @@ 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 = (struct uart_omap_port *)port;
- struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
- struct circ_buf *xmit;
- unsigned int start;
- int ret = 0;
-
- if (!up->use_dma) {
- pm_runtime_get_sync(&up->pdev->dev);
- serial_omap_enable_ier_thri(up);
- if (pdata->set_noidle)
- pdata->set_noidle(up->pdev);
- pm_runtime_mark_last_busy(&up->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
- return;
+ 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->uart_dma.tx_dma_used)
- return;
+ if ((up->rs485.flags & SER_RS485_ENABLED) &&
+ !(up->rs485.flags & SER_RS485_RX_DURING_TX))
+ serial_omap_stop_rx(port);
- xmit = &up->port.state->xmit;
+ serial_omap_enable_ier_thri(up);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
+}
- if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) {
- pm_runtime_get_sync(&up->pdev->dev);
- ret = omap_request_dma(up->uart_dma.uart_dma_tx,
- "UART Tx DMA",
- (void *)uart_tx_dma_callback, up,
- &(up->uart_dma.tx_dma_channel));
+static void serial_omap_throttle(struct uart_port *port)
+{
+ struct uart_omap_port *up = to_uart_omap_port(port);
+ unsigned long flags;
- if (ret < 0) {
- serial_omap_enable_ier_thri(up);
- return;
- }
- }
- spin_lock(&(up->uart_dma.tx_lock));
- up->uart_dma.tx_dma_used = true;
- spin_unlock(&(up->uart_dma.tx_lock));
+ pm_runtime_get_sync(up->dev);
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
+ serial_out(up, UART_IER, up->ier);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
+}
- start = up->uart_dma.tx_buf_dma_phys +
- (xmit->tail & (UART_XMIT_SIZE - 1));
+static void serial_omap_unthrottle(struct uart_port *port)
+{
+ struct uart_omap_port *up = to_uart_omap_port(port);
+ unsigned long flags;
- up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit);
- /*
- * It is a circular buffer. See if the buffer has wounded back.
- * If yes it will have to be transferred in two separate dma
- * transfers
- */
- if (start + up->uart_dma.tx_buf_size >=
- up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE)
- up->uart_dma.tx_buf_size =
- (up->uart_dma.tx_buf_dma_phys +
- UART_XMIT_SIZE) - start;
-
- omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- up->uart_dma.uart_base, 0, 0);
- omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0,
- OMAP_DMA_AMODE_POST_INC, start, 0, 0);
- omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel,
- OMAP_DMA_DATA_TYPE_S8,
- up->uart_dma.tx_buf_size, 1,
- OMAP_DMA_SYNC_ELEMENT,
- up->uart_dma.uart_dma_tx, 0);
- /* FIXME: Cache maintenance needed here? */
- omap_start_dma(up->uart_dma.tx_dma_channel);
+ pm_runtime_get_sync(up->dev);
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->ier |= UART_IER_RLSI | UART_IER_RDI;
+ serial_out(up, UART_IER, up->ier);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
static unsigned int check_modem_status(struct uart_omap_port *up)
@@ -383,76 +494,161 @@ static unsigned int check_modem_status(struct uart_omap_port *up)
return status;
}
+static void serial_omap_rlsi(struct uart_omap_port *up, unsigned int lsr)
+{
+ unsigned int flag;
+ unsigned char ch = 0;
+
+ if (likely(lsr & UART_LSR_DR))
+ ch = serial_in(up, UART_RX);
+
+ up->port.icount.rx++;
+ flag = TTY_NORMAL;
+
+ if (lsr & UART_LSR_BI) {
+ flag = TTY_BREAK;
+ lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+ up->port.icount.brk++;
+ /*
+ * We do the SysRQ and SAK checking
+ * here because otherwise the break
+ * may get masked by ignore_status_mask
+ * or read_status_mask.
+ */
+ if (uart_handle_break(&up->port))
+ return;
+
+ }
+
+ if (lsr & UART_LSR_PE) {
+ flag = TTY_PARITY;
+ up->port.icount.parity++;
+ }
+
+ if (lsr & UART_LSR_FE) {
+ flag = TTY_FRAME;
+ up->port.icount.frame++;
+ }
+
+ if (lsr & UART_LSR_OE)
+ up->port.icount.overrun++;
+
+#ifdef CONFIG_SERIAL_OMAP_CONSOLE
+ if (up->port.line == up->port.cons->index) {
+ /* Recover the break flag from console xmit */
+ lsr |= up->lsr_break_flag;
+ }
+#endif
+ uart_insert_char(&up->port, lsr, UART_LSR_OE, 0, flag);
+}
+
+static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr)
+{
+ unsigned char ch = 0;
+ unsigned int flag;
+
+ if (!(lsr & UART_LSR_DR))
+ return;
+
+ ch = serial_in(up, UART_RX);
+ flag = TTY_NORMAL;
+ up->port.icount.rx++;
+
+ if (uart_handle_sysrq_char(&up->port, ch))
+ return;
+
+ uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
+}
+
/**
* serial_omap_irq() - This handles the interrupt from one port
* @irq: uart port irq number
* @dev_id: uart port info
*/
-static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
+static irqreturn_t serial_omap_irq(int irq, void *dev_id)
{
struct uart_omap_port *up = dev_id;
unsigned int iir, lsr;
- unsigned long flags;
+ unsigned int type;
+ irqreturn_t ret = IRQ_NONE;
+ int max_count = 256;
- pm_runtime_get_sync(&up->pdev->dev);
- iir = serial_in(up, UART_IIR);
- if (iir & UART_IIR_NO_INT) {
- pm_runtime_mark_last_busy(&up->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
- return IRQ_NONE;
- }
+ spin_lock(&up->port.lock);
+ pm_runtime_get_sync(up->dev);
- spin_lock_irqsave(&up->port.lock, flags);
- lsr = serial_in(up, UART_LSR);
- if (iir & UART_IIR_RLSI) {
- if (!up->use_dma) {
- if (lsr & UART_LSR_DR)
- receive_chars(up, &lsr);
- } else {
- up->ier &= ~(UART_IER_RDI | UART_IER_RLSI);
- serial_out(up, UART_IER, up->ier);
- if ((serial_omap_start_rxdma(up) != 0) &&
- (lsr & UART_LSR_DR))
- receive_chars(up, &lsr);
+ do {
+ iir = serial_in(up, UART_IIR);
+ if (iir & UART_IIR_NO_INT)
+ break;
+
+ ret = IRQ_HANDLED;
+ lsr = serial_in(up, UART_LSR);
+
+ /* extract IRQ type from IIR register */
+ type = iir & 0x3e;
+
+ switch (type) {
+ case UART_IIR_MSI:
+ check_modem_status(up);
+ break;
+ case UART_IIR_THRI:
+ transmit_chars(up, lsr);
+ break;
+ case UART_IIR_RX_TIMEOUT:
+ /* FALLTHROUGH */
+ case UART_IIR_RDI:
+ serial_omap_rdi(up, lsr);
+ break;
+ case UART_IIR_RLSI:
+ serial_omap_rlsi(up, lsr);
+ break;
+ case UART_IIR_CTS_RTS_DSR:
+ /* simply try again */
+ break;
+ case UART_IIR_XOFF:
+ /* FALLTHROUGH */
+ default:
+ break;
}
- }
+ } while (!(iir & UART_IIR_NO_INT) && max_count--);
- check_modem_status(up);
- if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI))
- transmit_chars(up);
+ spin_unlock(&up->port.lock);
- spin_unlock_irqrestore(&up->port.lock, flags);
- pm_runtime_mark_last_busy(&up->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
+ tty_flip_buffer_push(&up->port.state->port);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
up->port_activity = jiffies;
- return IRQ_HANDLED;
+
+ return ret;
}
static unsigned int serial_omap_tx_empty(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags = 0;
unsigned int ret = 0;
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->port.line);
spin_lock_irqsave(&up->port.lock, flags);
ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
spin_unlock_irqrestore(&up->port.lock, flags);
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
return ret;
}
static unsigned int serial_omap_get_mctrl(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned int status;
unsigned int ret = 0;
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
status = check_modem_status(up);
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->port.line);
@@ -469,8 +665,8 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port)
static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
- unsigned char mcr = 0;
+ struct uart_omap_port *up = to_uart_omap_port(port);
+ unsigned char mcr = 0, old_mcr;
dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
if (mctrl & TIOCM_RTS)
@@ -484,20 +680,23 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
if (mctrl & TIOCM_LOOP)
mcr |= UART_MCR_LOOP;
- pm_runtime_get_sync(&up->pdev->dev);
- up->mcr = serial_in(up, UART_MCR);
- up->mcr |= mcr;
+ pm_runtime_get_sync(up->dev);
+ old_mcr = serial_in(up, UART_MCR);
+ old_mcr &= ~(UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_OUT1 |
+ UART_MCR_DTR | UART_MCR_RTS);
+ up->mcr = old_mcr | mcr;
serial_out(up, UART_MCR, up->mcr);
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
static void serial_omap_break_ctl(struct uart_port *port, int break_state)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags = 0;
dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->port.line);
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
spin_lock_irqsave(&up->port.lock, flags);
if (break_state == -1)
up->lcr |= UART_LCR_SBC;
@@ -505,12 +704,13 @@ static void serial_omap_break_ctl(struct uart_port *port, int break_state)
up->lcr &= ~UART_LCR_SBC;
serial_out(up, UART_LCR, up->lcr);
spin_unlock_irqrestore(&up->port.lock, flags);
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
static int serial_omap_startup(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags = 0;
int retval;
@@ -522,9 +722,20 @@ 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->pdev->dev);
+ pm_runtime_get_sync(up->dev);
/*
* Clear the FIFO buffers and disable them.
* (they will be reenabled in set_termios())
@@ -555,20 +766,6 @@ static int serial_omap_startup(struct uart_port *port)
spin_unlock_irqrestore(&up->port.lock, flags);
up->msr_saved_flags = 0;
- if (up->use_dma) {
- free_page((unsigned long)up->port.state->xmit.buf);
- up->port.state->xmit.buf = dma_alloc_coherent(NULL,
- UART_XMIT_SIZE,
- (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys),
- 0);
- init_timer(&(up->uart_dma.rx_timer));
- up->uart_dma.rx_timer.function = serial_omap_rxdma_poll;
- up->uart_dma.rx_timer.data = up->port.line;
- /* Currently the buffer size is 4KB. Can increase it */
- up->uart_dma.rx_buf = dma_alloc_coherent(NULL,
- up->uart_dma.rx_buf_size,
- (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0);
- }
/*
* Finally, enable interrupts. Note: Modem status interrupts
* are set via set_termios(), which will be occurring imminently
@@ -578,22 +775,26 @@ 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->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
up->port_activity = jiffies;
return 0;
}
static void serial_omap_shutdown(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags = 0;
dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->port.line);
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
/*
* Disable interrupts from this port
*/
@@ -616,79 +817,12 @@ static void serial_omap_shutdown(struct uart_port *port)
*/
if (serial_in(up, UART_LSR) & UART_LSR_DR)
(void) serial_in(up, UART_RX);
- if (up->use_dma) {
- dma_free_coherent(up->port.dev,
- UART_XMIT_SIZE, up->port.state->xmit.buf,
- up->uart_dma.tx_buf_dma_phys);
- up->port.state->xmit.buf = NULL;
- serial_omap_stop_rx(port);
- dma_free_coherent(up->port.dev,
- up->uart_dma.rx_buf_size, up->uart_dma.rx_buf,
- up->uart_dma.rx_buf_dma_phys);
- up->uart_dma.rx_buf = NULL;
- }
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
free_irq(up->port.irq, up);
-}
-
-static inline void
-serial_omap_configure_xonxoff
- (struct uart_omap_port *up, struct ktermios *termios)
-{
- up->lcr = serial_in(up, UART_LCR);
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- up->efr = serial_in(up, UART_EFR);
- serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB);
-
- serial_out(up, UART_XON1, termios->c_cc[VSTART]);
- serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
-
- /* clear SW control mode bits */
- up->efr &= OMAP_UART_SW_CLR;
-
- /*
- * IXON Flag:
- * Enable XON/XOFF flow control on output.
- * Transmit XON1, XOFF1
- */
- if (termios->c_iflag & IXON)
- up->efr |= OMAP_UART_SW_TX;
-
- /*
- * IXOFF Flag:
- * Enable XON/XOFF flow control on input.
- * Receiver compares XON1, XOFF1.
- */
- if (termios->c_iflag & IXOFF)
- up->efr |= OMAP_UART_SW_RX;
-
- serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
-
- up->mcr = serial_in(up, UART_MCR);
-
- /*
- * IXANY Flag:
- * Enable any character to restart output.
- * Operation resumes after receiving any
- * character after recognition of the XOFF character
- */
- if (termios->c_iflag & IXANY)
- up->mcr |= UART_MCR_XONANY;
-
- serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
- /* Enable special char function UARTi.EFR_REG[5] and
- * load the new software flow control mode IXON or IXOFF
- * and restore the UARTi.EFR_REG[4] ENHANCED_EN value.
- */
- serial_out(up, UART_EFR, up->efr | UART_EFR_SCD);
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
-
- serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR);
- serial_out(up, UART_LCR, up->lcr);
+ if (up->wakeirq)
+ free_irq(up->wakeirq, up);
}
static void serial_omap_uart_qos_work(struct work_struct *work)
@@ -703,9 +837,8 @@ static void
serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned char cval = 0;
- unsigned char efr = 0;
unsigned long flags = 0;
unsigned int baud, quot;
@@ -731,6 +864,8 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
cval |= UART_LCR_PARITY;
if (!(termios->c_cflag & PARODD))
cval |= UART_LCR_EPAR;
+ if (termios->c_cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
/*
* Ask the core to calculate the divisor for us.
@@ -750,14 +885,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 |
UART_FCR_ENABLE_FIFO;
- if (up->use_dma)
- up->fcr |= UART_FCR_DMA_SELECT;
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
spin_lock_irqsave(&up->port.lock, flags);
/*
@@ -802,7 +935,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
serial_out(up, UART_IER, up->ier);
serial_out(up, UART_LCR, cval); /* reset DLAB */
up->lcr = cval;
- up->scr = OMAP_UART_SCR_TX_EMPTY;
+ up->scr = 0;
/* FIFOs and DMA Settings */
@@ -817,33 +950,45 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- up->efr = serial_in(up, UART_EFR);
+ up->efr = serial_in(up, UART_EFR) & ~UART_EFR_ECB;
+ up->efr &= ~UART_EFR_SCD;
serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
- up->mcr = serial_in(up, UART_MCR);
+ up->mcr = serial_in(up, UART_MCR) & ~UART_MCR_TCRTLR;
serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
/* FIFO ENABLE, DMA MODE */
up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;
+ /*
+ * NOTE: Setting OMAP_UART_SCR_RX_TRIG_GRANU1_MASK
+ * sets Enables the granularity of 1 for TRIGGER RX
+ * level. Along with setting RX FIFO trigger level
+ * to 1 (as noted below, 16 characters) and TLR[3:0]
+ * to zero this will result RX FIFO threshold level
+ * to 1 character, instead of 16 as noted in comment
+ * below.
+ */
- if (up->use_dma) {
- serial_out(up, UART_TI752_TLR, 0);
- up->scr |= UART_FCR_TRIGGER_4;
- } else {
- /* Set receive FIFO threshold to 1 byte */
- up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
- up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
- }
+ /* Set receive FIFO threshold to 16 characters and
+ * transmit FIFO threshold to 32 spaces
+ */
+ up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
+ up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK;
+ up->fcr |= UART_FCR6_R_TRIGGER_16 | UART_FCR6_T_TRIGGER_24 |
+ UART_FCR_ENABLE_FIFO;
serial_out(up, UART_FCR, up->fcr);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_OMAP_SCR, up->scr);
- serial_out(up, UART_EFR, up->efr);
+ /* Reset UART_MCR_TCRTLR: this must be done with the EFR_ECB bit set */
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
serial_out(up, UART_MCR, up->mcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, up->efr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
/* Protocol, Baud Rate, and Interrupt Settings */
@@ -853,8 +998,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
serial_out(up, UART_OMAP_MDR1, up->mdr1);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
-
- up->efr = serial_in(up, UART_EFR);
serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
serial_out(up, UART_LCR, 0);
@@ -871,7 +1014,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
serial_out(up, UART_EFR, up->efr);
serial_out(up, UART_LCR, cval);
- if (baud > 230400 && baud != 3000000)
+ if (!serial_omap_baud_is_mode16(port, baud))
up->mdr1 = UART_OMAP_MDR1_13X_MODE;
else
up->mdr1 = UART_OMAP_MDR1_16X_MODE;
@@ -881,32 +1024,72 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
else
serial_out(up, UART_OMAP_MDR1, up->mdr1);
- /* Hardware Flow Control Configuration */
+ /* Configure flow control */
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- if (termios->c_cflag & CRTSCTS) {
- efr |= (UART_EFR_CTS | UART_EFR_RTS);
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ /* XON1/XOFF1 accessible mode B, TCRTLR=0, ECB=0 */
+ serial_out(up, UART_XON1, termios->c_cc[VSTART]);
+ serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
- up->mcr = serial_in(up, UART_MCR);
- serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+ /* Enable access to TCR/TLR */
+ serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+
+ serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+
+ if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
+ /* Enable AUTORTS and AUTOCTS */
+ up->efr |= UART_EFR_CTS | UART_EFR_RTS;
+
+ /* Ensure MCR RTS is asserted */
+ up->mcr |= UART_MCR_RTS;
+ } else {
+ /* Disable AUTORTS and AUTOCTS */
+ up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS);
+ }
+
+ if (up->port.flags & UPF_SOFT_FLOW) {
+ /* clear SW control mode bits */
+ up->efr &= OMAP_UART_SW_CLR;
+
+ /*
+ * IXON Flag:
+ * Enable XON/XOFF flow control on input.
+ * Receiver compares XON1, XOFF1.
+ */
+ if (termios->c_iflag & IXON)
+ up->efr |= OMAP_UART_SW_RX;
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- up->efr = serial_in(up, UART_EFR);
- serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+ /*
+ * IXOFF Flag:
+ * Enable XON/XOFF flow control on output.
+ * Transmit XON1, XOFF1
+ */
+ if (termios->c_iflag & IXOFF)
+ up->efr |= OMAP_UART_SW_TX;
- serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
- serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
- serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS);
- serial_out(up, UART_LCR, cval);
+ /*
+ * IXANY Flag:
+ * Enable any character to restart output.
+ * Operation resumes after receiving any
+ * character after recognition of the XOFF character
+ */
+ if (termios->c_iflag & IXANY)
+ up->mcr |= UART_MCR_XONANY;
+ else
+ up->mcr &= ~UART_MCR_XONANY;
}
+ serial_out(up, UART_MCR, up->mcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, up->efr);
+ serial_out(up, UART_LCR, up->lcr);
serial_omap_set_mctrl(&up->port, up->port.mctrl);
- /* Software Flow Control Configuration */
- serial_omap_configure_xonxoff(up, termios);
spin_unlock_irqrestore(&up->port.lock, flags);
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
}
@@ -914,12 +1097,12 @@ static void
serial_omap_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned char efr;
dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->port.line);
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
efr = serial_in(up, UART_EFR);
serial_out(up, UART_EFR, efr | UART_EFR_ECB);
@@ -930,14 +1113,15 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
- if (!device_may_wakeup(&up->pdev->dev)) {
+ if (!device_may_wakeup(up->dev)) {
if (!state)
- pm_runtime_forbid(&up->pdev->dev);
+ pm_runtime_forbid(up->dev);
else
- pm_runtime_allow(&up->pdev->dev);
+ pm_runtime_allow(up->dev);
}
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
static void serial_omap_release_port(struct uart_port *port)
@@ -953,11 +1137,12 @@ static int serial_omap_request_port(struct uart_port *port)
static void serial_omap_config_port(struct uart_port *port, int flags)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
up->port.line);
up->port.type = PORT_OMAP;
+ up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW;
}
static int
@@ -971,7 +1156,7 @@ serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser)
static const char *
serial_omap_type(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->port.line);
return up->name;
@@ -1014,26 +1199,33 @@ static inline void wait_for_xmitr(struct uart_omap_port *up)
static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
wait_for_xmitr(up);
serial_out(up, UART_TX, ch);
- pm_runtime_put(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
}
static int serial_omap_poll_get_char(struct uart_port *port)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
unsigned int status;
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
status = serial_in(up, UART_LSR);
- if (!(status & UART_LSR_DR))
- return NO_POLL_CHAR;
+ if (!(status & UART_LSR_DR)) {
+ status = NO_POLL_CHAR;
+ goto out;
+ }
status = serial_in(up, UART_RX);
- pm_runtime_put(&up->pdev->dev);
+
+out:
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
+
return status;
}
@@ -1041,13 +1233,13 @@ static int serial_omap_poll_get_char(struct uart_port *port)
#ifdef CONFIG_SERIAL_OMAP_CONSOLE
-static struct uart_omap_port *serial_omap_console_ports[4];
+static struct uart_omap_port *serial_omap_console_ports[OMAP_MAX_HSUART_PORTS];
static struct uart_driver serial_omap_reg;
static void serial_omap_console_putchar(struct uart_port *port, int ch)
{
- struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct uart_omap_port *up = to_uart_omap_port(port);
wait_for_xmitr(up);
serial_out(up, UART_TX, ch);
@@ -1062,7 +1254,7 @@ serial_omap_console_write(struct console *co, const char *s,
unsigned int ier;
int locked = 1;
- pm_runtime_get_sync(&up->pdev->dev);
+ pm_runtime_get_sync(up->dev);
local_irq_save(flags);
if (up->port.sysrq)
@@ -1096,8 +1288,8 @@ serial_omap_console_write(struct console *co, const char *s,
if (up->msr_saved_flags)
check_modem_status(up);
- pm_runtime_mark_last_busy(&up->pdev->dev);
- pm_runtime_put_autosuspend(&up->pdev->dev);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
if (locked)
spin_unlock(&up->port.lock);
local_irq_restore(flags);
@@ -1148,12 +1340,93 @@ 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,
.get_mctrl = serial_omap_get_mctrl,
.stop_tx = serial_omap_stop_tx,
.start_tx = serial_omap_start_tx,
+ .throttle = serial_omap_throttle,
+ .unthrottle = serial_omap_unthrottle,
.stop_rx = serial_omap_stop_rx,
.enable_ms = serial_omap_enable_ms,
.break_ctl = serial_omap_break_ctl,
@@ -1166,6 +1439,7 @@ static struct uart_ops serial_omap_pops = {
.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,
@@ -1181,169 +1455,106 @@ static struct uart_driver serial_omap_reg = {
};
#ifdef CONFIG_PM_SLEEP
-static int serial_omap_suspend(struct device *dev)
+static int serial_omap_prepare(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- if (up) {
- uart_suspend_port(&serial_omap_reg, &up->port);
- flush_work_sync(&up->qos_work);
- }
+ up->is_suspending = true;
return 0;
}
-static int serial_omap_resume(struct device *dev)
+static void serial_omap_complete(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- if (up)
- uart_resume_port(&serial_omap_reg, &up->port);
- return 0;
+ up->is_suspending = false;
}
-#endif
-static void serial_omap_rxdma_poll(unsigned long uart_no)
+static int serial_omap_suspend(struct device *dev)
{
- struct uart_omap_port *up = ui[uart_no];
- unsigned int curr_dma_pos, curr_transmitted_size;
- int ret = 0;
-
- curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel);
- if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) ||
- (curr_dma_pos == 0)) {
- if (jiffies_to_msecs(jiffies - up->port_activity) <
- up->uart_dma.rx_timeout) {
- mod_timer(&up->uart_dma.rx_timer, jiffies +
- usecs_to_jiffies(up->uart_dma.rx_poll_rate));
- } else {
- serial_omap_stop_rxdma(up);
- up->ier |= (UART_IER_RDI | UART_IER_RLSI);
- serial_out(up, UART_IER, up->ier);
- }
- return;
- }
+ struct uart_omap_port *up = dev_get_drvdata(dev);
- curr_transmitted_size = curr_dma_pos -
- up->uart_dma.prev_rx_dma_pos;
- up->port.icount.rx += curr_transmitted_size;
- tty_insert_flip_string(up->port.state->port.tty,
- up->uart_dma.rx_buf +
- (up->uart_dma.prev_rx_dma_pos -
- up->uart_dma.rx_buf_dma_phys),
- curr_transmitted_size);
- tty_flip_buffer_push(up->port.state->port.tty);
- up->uart_dma.prev_rx_dma_pos = curr_dma_pos;
- if (up->uart_dma.rx_buf_size +
- up->uart_dma.rx_buf_dma_phys == curr_dma_pos) {
- ret = serial_omap_start_rxdma(up);
- if (ret < 0) {
- serial_omap_stop_rxdma(up);
- up->ier |= (UART_IER_RDI | UART_IER_RLSI);
- serial_out(up, UART_IER, up->ier);
- }
- } else {
- mod_timer(&up->uart_dma.rx_timer, jiffies +
- usecs_to_jiffies(up->uart_dma.rx_poll_rate));
- }
- up->port_activity = jiffies;
-}
+ uart_suspend_port(&serial_omap_reg, &up->port);
+ flush_work(&up->qos_work);
-static void uart_rx_dma_callback(int lch, u16 ch_status, void *data)
-{
- return;
-}
-
-static int serial_omap_start_rxdma(struct uart_omap_port *up)
-{
- int ret = 0;
-
- if (up->uart_dma.rx_dma_channel == -1) {
- pm_runtime_get_sync(&up->pdev->dev);
- ret = omap_request_dma(up->uart_dma.uart_dma_rx,
- "UART Rx DMA",
- (void *)uart_rx_dma_callback, up,
- &(up->uart_dma.rx_dma_channel));
- if (ret < 0)
- return ret;
+ if (device_may_wakeup(dev))
+ serial_omap_enable_wakeup(up, true);
+ else
+ serial_omap_enable_wakeup(up, false);
- omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- up->uart_dma.uart_base, 0, 0);
- omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0,
- OMAP_DMA_AMODE_POST_INC,
- up->uart_dma.rx_buf_dma_phys, 0, 0);
- omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel,
- OMAP_DMA_DATA_TYPE_S8,
- up->uart_dma.rx_buf_size, 1,
- OMAP_DMA_SYNC_ELEMENT,
- up->uart_dma.uart_dma_rx, 0);
- }
- up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys;
- /* FIXME: Cache maintenance needed here? */
- omap_start_dma(up->uart_dma.rx_dma_channel);
- mod_timer(&up->uart_dma.rx_timer, jiffies +
- usecs_to_jiffies(up->uart_dma.rx_poll_rate));
- up->uart_dma.rx_dma_used = true;
- return ret;
+ return 0;
}
-static void serial_omap_continue_tx(struct uart_omap_port *up)
+static int serial_omap_resume(struct device *dev)
{
- struct circ_buf *xmit = &up->port.state->xmit;
- unsigned int start = up->uart_dma.tx_buf_dma_phys
- + (xmit->tail & (UART_XMIT_SIZE - 1));
+ struct uart_omap_port *up = dev_get_drvdata(dev);
- if (uart_circ_empty(xmit))
- return;
+ if (device_may_wakeup(dev))
+ serial_omap_enable_wakeup(up, false);
- up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit);
- /*
- * It is a circular buffer. See if the buffer has wounded back.
- * If yes it will have to be transferred in two separate dma
- * transfers
- */
- if (start + up->uart_dma.tx_buf_size >=
- up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE)
- up->uart_dma.tx_buf_size =
- (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start;
- omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- up->uart_dma.uart_base, 0, 0);
- omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0,
- OMAP_DMA_AMODE_POST_INC, start, 0, 0);
- omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel,
- OMAP_DMA_DATA_TYPE_S8,
- up->uart_dma.tx_buf_size, 1,
- OMAP_DMA_SYNC_ELEMENT,
- up->uart_dma.uart_dma_tx, 0);
- /* FIXME: Cache maintenance needed here? */
- omap_start_dma(up->uart_dma.tx_dma_channel);
+ uart_resume_port(&serial_omap_reg, &up->port);
+
+ return 0;
}
+#else
+#define serial_omap_prepare NULL
+#define serial_omap_complete NULL
+#endif /* CONFIG_PM_SLEEP */
-static void uart_tx_dma_callback(int lch, u16 ch_status, void *data)
+static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
{
- struct uart_omap_port *up = (struct uart_omap_port *)data;
- struct circ_buf *xmit = &up->port.state->xmit;
+ u32 mvr, scheme;
+ u16 revision, major, minor;
- xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \
- (UART_XMIT_SIZE - 1);
- up->port.icount.tx += up->uart_dma.tx_buf_size;
+ mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift));
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
+ /* Check revision register scheme */
+ scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
- if (uart_circ_empty(xmit)) {
- spin_lock(&(up->uart_dma.tx_lock));
- serial_omap_stop_tx(&up->port);
- up->uart_dma.tx_dma_used = false;
- spin_unlock(&(up->uart_dma.tx_lock));
- } else {
- omap_stop_dma(up->uart_dma.tx_dma_channel);
- serial_omap_continue_tx(up);
+ switch (scheme) {
+ case 0: /* Legacy Scheme: OMAP2/3 */
+ /* MINOR_REV[0:4], MAJOR_REV[4:7] */
+ major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >>
+ OMAP_UART_LEGACY_MVR_MAJ_SHIFT;
+ minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK);
+ break;
+ case 1:
+ /* New Scheme: OMAP4+ */
+ /* MINOR_REV[0:5], MAJOR_REV[8:10] */
+ major = (mvr & OMAP_UART_MVR_MAJ_MASK) >>
+ OMAP_UART_MVR_MAJ_SHIFT;
+ minor = (mvr & OMAP_UART_MVR_MIN_MASK);
+ break;
+ default:
+ dev_warn(up->dev,
+ "Unknown %s revision, defaulting to highest\n",
+ up->name);
+ /* highest possible revision */
+ major = 0xff;
+ minor = 0xff;
+ }
+
+ /* normalize revision for the driver */
+ revision = UART_BUILD_REVISION(major, minor);
+
+ switch (revision) {
+ case OMAP_UART_REV_46:
+ up->errata |= (UART_ERRATA_i202_MDR1_ACCESS |
+ UART_ERRATA_i291_DMA_FORCEIDLE);
+ break;
+ 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;
}
- up->port_activity = jiffies;
- return;
}
static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
@@ -1359,56 +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, *dma_tx, *dma_rx;
- struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
- int ret = -ENOSPC;
+ 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);
+ rs485conf->flags = 0;
+ up->rts_gpio = -EINVAL;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- return -ENODEV;
- }
+ if (!np)
+ return 0;
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
- dev_err(&pdev->dev, "no irq resource?\n");
- return -ENODEV;
- }
+ 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 (!request_mem_region(mem->start, resource_size(mem),
- pdev->dev.driver->name)) {
- dev_err(&pdev->dev, "memory region already claimed\n");
- return -EBUSY;
+ /* 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(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;
}
- dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
- if (!dma_rx) {
- ret = -EINVAL;
- goto err;
+ 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];
}
- dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
- if (!dma_tx) {
- ret = -EINVAL;
- goto err;
- }
+ 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;
- up = kzalloc(sizeof(*up), GFP_KERNEL);
- if (up == NULL) {
- ret = -ENOMEM;
- goto do_release_region;
+ /* 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->pdev = pdev;
+
+ up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
+ if (!up)
+ return -ENOMEM;
+
+ 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;
@@ -1423,71 +1676,65 @@ static int serial_omap_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n",
up->port.line);
ret = -ENODEV;
- goto err;
+ goto err_port_line;
}
+ 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 = ioremap(mem->start, resource_size(mem));
- if (!up->port.membase) {
- dev_err(&pdev->dev, "can't ioremap UART\n");
- ret = -ENOMEM;
- goto err;
- }
-
+ 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);
- }
- up->uart_dma.uart_base = mem->start;
- up->errata = omap_up_info->errata;
-
- if (omap_up_info->dma_enabled) {
- up->uart_dma.uart_dma_tx = dma_tx->start;
- up->uart_dma.uart_dma_rx = dma_rx->start;
- up->use_dma = 1;
- up->uart_dma.rx_buf_size = omap_up_info->dma_rx_buf_size;
- up->uart_dma.rx_timeout = omap_up_info->dma_rx_timeout;
- up->uart_dma.rx_poll_rate = omap_up_info->dma_rx_poll_rate;
- spin_lock_init(&(up->uart_dma.tx_lock));
- spin_lock_init(&(up->uart_dma.rx_lock));
- up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
- up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
+ 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);
+ 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);
+
ui[up->port.line] = up;
serial_omap_add_console_port(up);
ret = uart_add_one_port(&serial_omap_reg, &up->port);
if (ret != 0)
- goto do_release_region;
+ goto err_add_port;
- pm_runtime_put(&pdev->dev);
- platform_set_drvdata(pdev, up);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
return 0;
-err:
+
+err_add_port:
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+err_rs485:
+err_port_line:
dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
pdev->id, __func__, ret);
-do_release_region:
- release_mem_region(mem->start, resource_size(mem));
return ret;
}
@@ -1495,15 +1742,12 @@ static int serial_omap_remove(struct platform_device *dev)
{
struct uart_omap_port *up = platform_get_drvdata(dev);
- if (up) {
- pm_runtime_disable(&up->pdev->dev);
- uart_remove_one_port(&serial_omap_reg, &up->port);
- pm_qos_remove_request(&up->pm_qos_request);
+ pm_runtime_put_sync(up->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);
- kfree(up);
- }
-
- platform_set_drvdata(dev, NULL);
return 0;
}
@@ -1533,7 +1777,7 @@ static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1)
timeout--;
if (!timeout) {
/* Should *never* happen. we warn and carry on */
- dev_crit(&up->pdev->dev, "Errata i202: timedout %x\n",
+ dev_crit(up->dev, "Errata i202: timedout %x\n",
serial_in(up, UART_LSR));
break;
}
@@ -1569,38 +1813,29 @@ 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)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- struct omap_uart_port_info *pdata = dev->platform_data;
if (!up)
return -EINVAL;
- if (!pdata || !pdata->enable_wakeup)
- return 0;
-
- if (pdata->get_context_loss_count)
- up->context_loss_cnt = pdata->get_context_loss_count(dev);
+ /*
+ * When using 'no_console_suspend', the console UART must not be
+ * suspended. Since driver suspend is managed by runtime suspend,
+ * preventing runtime suspend (by returning error) will keep device
+ * active during suspend.
+ */
+ if (up->is_suspending && !console_suspend_enabled &&
+ uart_console(&up->port))
+ return -EBUSY;
- if (device_may_wakeup(dev)) {
- if (!up->wakeups_enabled) {
- pdata->enable_wakeup(up->pdev, true);
- up->wakeups_enabled = true;
- }
- } else {
- if (up->wakeups_enabled) {
- pdata->enable_wakeup(up->pdev, false);
- up->wakeups_enabled = false;
- }
- }
+ up->context_loss_cnt = serial_omap_get_context_loss_count(up);
- /* Errata i291 */
- if (up->use_dma && pdata->set_forceidle &&
- (up->errata & UART_ERRATA_i291_DMA_FORCEIDLE))
- pdata->set_forceidle(up->pdev);
+ serial_omap_enable_wakeup(up, true);
up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
schedule_work(&up->qos_work);
@@ -1611,24 +1846,20 @@ static int serial_omap_runtime_suspend(struct device *dev)
static int serial_omap_runtime_resume(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- struct omap_uart_port_info *pdata = dev->platform_data;
-
- if (up) {
- if (pdata->get_context_loss_count) {
- u32 loss_cnt = pdata->get_context_loss_count(dev);
- if (up->context_loss_cnt != loss_cnt)
- serial_omap_restore_context(up);
- }
+ int loss_cnt = serial_omap_get_context_loss_count(up);
- /* Errata i291 */
- if (up->use_dma && pdata->set_noidle &&
- (up->errata & UART_ERRATA_i291_DMA_FORCEIDLE))
- pdata->set_noidle(up->pdev);
+ serial_omap_enable_wakeup(up, false);
- up->latency = up->calc_latency;
- schedule_work(&up->qos_work);
+ if (loss_cnt < 0) {
+ dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n",
+ loss_cnt);
+ serial_omap_restore_context(up);
+ } else if (up->context_loss_cnt != loss_cnt) {
+ serial_omap_restore_context(up);
}
+ up->latency = up->calc_latency;
+ schedule_work(&up->qos_work);
return 0;
}
@@ -1638,6 +1869,8 @@ static const struct dev_pm_ops serial_omap_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume)
SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend,
serial_omap_runtime_resume, NULL)
+ .prepare = serial_omap_prepare,
+ .complete = serial_omap_complete,
};
#if defined(CONFIG_OF)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 17ae65762d1..0cb6a8e52bd 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -14,21 +14,25 @@
*along with this program; if not, write to the Free Software
*Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
+#if defined(CONFIG_SERIAL_PCH_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
#include <linux/kernel.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/dmi.h>
-#include <linux/console.h>
#include <linux/nmi.h>
#include <linux/delay.h>
+#include <linux/debugfs.h>
#include <linux/dmaengine.h>
#include <linux/pch_dma.h>
@@ -38,6 +42,7 @@ enum {
PCH_UART_HANDLED_RX_ERR_INT_SHIFT,
PCH_UART_HANDLED_RX_TRG_INT_SHIFT,
PCH_UART_HANDLED_MS_INT_SHIFT,
+ PCH_UART_HANDLED_LS_INT_SHIFT,
};
enum {
@@ -62,6 +67,8 @@ enum {
PCH_UART_HANDLED_RX_TRG_INT_SHIFT)<<1))
#define PCH_UART_HANDLED_MS_INT (1<<((PCH_UART_HANDLED_MS_INT_SHIFT)<<1))
+#define PCH_UART_HANDLED_LS_INT (1<<((PCH_UART_HANDLED_LS_INT_SHIFT)<<1))
+
#define PCH_UART_RBR 0x00
#define PCH_UART_THR 0x00
@@ -144,6 +151,8 @@ enum {
#define PCH_UART_DLL 0x00
#define PCH_UART_DLM 0x01
+#define PCH_UART_BRCSR 0x0E
+
#define PCH_UART_IID_RLS (PCH_UART_IIR_REI)
#define PCH_UART_IID_RDR (PCH_UART_IIR_RRI)
#define PCH_UART_IID_RDR_TO (PCH_UART_IIR_RRI | PCH_UART_IIR_TOI)
@@ -203,7 +212,12 @@ enum {
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-#define DEFAULT_BAUD_RATE 1843200 /* 1.8432MHz */
+#define DEFAULT_UARTCLK 1843200 /* 1.8432 MHz */
+#define CMITC_UARTCLK 192000000 /* 192.0000 MHz */
+#define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */
+#define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */
+#define NTC1_UARTCLK 64000000 /* 64.0000 MHz */
+#define MINNOW_UARTCLK 50000000 /* 50.0000 MHz */
struct pch_uart_buffer {
unsigned char *buf;
@@ -218,11 +232,10 @@ struct eg20t_port {
unsigned int iobase;
struct pci_dev *pdev;
int fifo_size;
- int base_baud;
+ unsigned int uartclk;
int start_tx;
int start_rx;
int tx_empty;
- int int_dis_flag;
int trigger;
int trigger_level;
struct pch_uart_buffer rxbuf;
@@ -230,7 +243,6 @@ struct eg20t_port {
unsigned int fcr;
unsigned int mcr;
unsigned int use_dma;
- unsigned int use_dma_flag;
struct dma_async_tx_descriptor *desc_tx;
struct dma_async_tx_descriptor *desc_rx;
struct pch_dma_slave param_tx;
@@ -243,6 +255,13 @@ struct eg20t_port {
int tx_dma_use;
void *rx_buf_virt;
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;
};
/**
@@ -287,26 +306,133 @@ static struct pch_uart_driver_data drv_dat[] = {
static struct eg20t_port *pch_uart_ports[PCH_UART_NR];
#endif
static unsigned int default_baud = 9600;
+static unsigned int user_uartclk = 0;
static const int trigger_level_256[4] = { 1, 64, 128, 224 };
static const int trigger_level_64[4] = { 1, 16, 32, 56 };
static const int trigger_level_16[4] = { 1, 4, 8, 14 };
static const int trigger_level_1[4] = { 1, 1, 1, 1 };
-static void pch_uart_hal_request(struct pci_dev *pdev, int fifosize,
- int base_baud)
+#ifdef CONFIG_DEBUG_FS
+
+#define PCH_REGS_BUFSIZE 1024
+
+
+static ssize_t port_show_regs(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
{
- struct eg20t_port *priv = pci_get_drvdata(pdev);
+ struct eg20t_port *priv = file->private_data;
+ char *buf;
+ u32 len = 0;
+ ssize_t ret;
+ unsigned char lcr;
+
+ buf = kzalloc(PCH_REGS_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
- priv->trigger_level = 1;
- priv->fcr = 0;
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "PCH EG20T port[%d] regs:\n", priv->port.line);
+
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "=================================\n");
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "IER: \t0x%02x\n", ioread8(priv->membase + UART_IER));
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "IIR: \t0x%02x\n", ioread8(priv->membase + UART_IIR));
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "LCR: \t0x%02x\n", ioread8(priv->membase + UART_LCR));
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "MCR: \t0x%02x\n", ioread8(priv->membase + UART_MCR));
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "LSR: \t0x%02x\n", ioread8(priv->membase + UART_LSR));
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "MSR: \t0x%02x\n", ioread8(priv->membase + UART_MSR));
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "BRCSR: \t0x%02x\n",
+ ioread8(priv->membase + PCH_UART_BRCSR));
+
+ lcr = ioread8(priv->membase + UART_LCR);
+ iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "DLL: \t0x%02x\n", ioread8(priv->membase + UART_DLL));
+ len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+ "DLM: \t0x%02x\n", ioread8(priv->membase + UART_DLM));
+ iowrite8(lcr, priv->membase + UART_LCR);
+
+ if (len > PCH_REGS_BUFSIZE)
+ len = PCH_REGS_BUFSIZE;
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
}
-static unsigned int get_msr(struct eg20t_port *priv, void __iomem *base)
+static const struct file_operations port_regs_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = port_show_regs,
+ .llseek = default_llseek,
+};
+#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 unsigned int pch_uart_get_uartclk(void)
{
- unsigned int msr = ioread8(base + UART_MSR);
- priv->dmsr |= msr & PCH_UART_MSR_DELTA;
+ const struct dmi_system_id *d;
+
+ if (user_uartclk)
+ return user_uartclk;
- return msr;
+ d = dmi_first_match(pch_uart_dmi_table);
+ if (d)
+ return (unsigned long)d->driver_data;
+
+ return DEFAULT_UARTCLK;
}
static void pch_uart_hal_enable_interrupt(struct eg20t_port *priv,
@@ -325,14 +451,14 @@ 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)
{
unsigned int dll, dlm, lcr;
int div;
- div = DIV_ROUND_CLOSEST(priv->base_baud / 16, baud);
+ div = DIV_ROUND_CLOSEST(priv->uartclk / 16, baud);
if (div < 0 || USHRT_MAX <= div) {
dev_err(priv->port.dev, "Invalid Baud(div=0x%x)\n", div);
return -EINVAL;
@@ -360,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);
@@ -442,8 +568,9 @@ static int pch_uart_hal_set_fifo(struct eg20t_port *priv,
static u8 pch_uart_hal_get_modem(struct eg20t_port *priv)
{
- priv->dmsr = 0;
- return get_msr(priv, priv->membase);
+ unsigned int msr = ioread8(priv->membase + UART_MSR);
+ priv->dmsr = msr & PCH_UART_MSR_DELTA;
+ return (u8)msr;
}
static void pch_uart_hal_write(struct eg20t_port *priv,
@@ -463,25 +590,35 @@ static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf,
{
int i;
u8 rbr, lsr;
+ struct uart_port *port = &priv->port;
lsr = ioread8(priv->membase + UART_LSR);
for (i = 0, lsr = ioread8(priv->membase + UART_LSR);
- i < rx_size && lsr & UART_LSR_DR;
+ i < rx_size && lsr & (UART_LSR_DR | UART_LSR_BI);
lsr = ioread8(priv->membase + UART_LSR)) {
rbr = ioread8(priv->membase + PCH_UART_RBR);
+
+ if (lsr & UART_LSR_BI) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ }
+#ifdef SUPPORT_SYSRQ
+ if (port->sysrq) {
+ if (uart_handle_sysrq_char(port, rbr))
+ continue;
+ }
+#endif
+
buf[i++] = rbr;
}
return i;
}
-static unsigned int pch_uart_hal_get_iid(struct eg20t_port *priv)
+static unsigned char pch_uart_hal_get_iid(struct eg20t_port *priv)
{
- unsigned int iir;
- int ret;
-
- iir = ioread8(priv->membase + UART_IIR);
- ret = (iir & (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP));
- return ret;
+ return ioread8(priv->membase + UART_IIR) &\
+ (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP);
}
static u8 pch_uart_hal_get_line_status(struct eg20t_port *priv)
@@ -505,26 +642,18 @@ static void pch_uart_hal_set_break(struct eg20t_port *priv, int on)
static int push_rx(struct eg20t_port *priv, const unsigned char *buf,
int size)
{
- struct uart_port *port;
- struct tty_struct *tty;
-
- port = &priv->port;
- tty = tty_port_tty_get(&port->state->port);
- if (!tty) {
- dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
- return -EBUSY;
- }
+ struct uart_port *port = &priv->port;
+ struct tty_port *tport = &port->state->port;
- tty_insert_flip_string(tty, buf, size);
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_insert_flip_string(tport, buf, size);
+ tty_flip_buffer_push(tport);
return 0;
}
static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf)
{
- int ret;
+ int ret = 0;
struct uart_port *port = &priv->port;
if (port->x_char) {
@@ -533,8 +662,6 @@ static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf)
buf[0] = port->x_char;
port->x_char = 0;
ret = 1;
- } else {
- ret = 0;
}
return ret;
@@ -542,29 +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(&port->state->port);
- if (!tty) {
- dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
- return 0;
- }
-
- room = tty_buffer_request_room(tty, size);
+ 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(tty, sg_virt(&priv->sg_rx), size);
+ tty_insert_flip_string(tport, sg_virt(&priv->sg_rx), size);
port->icount.rx += room;
- tty_kref_put(tty);
return room;
}
@@ -582,10 +701,13 @@ static void pch_free_dma(struct uart_port *port)
dma_release_channel(priv->chan_rx);
priv->chan_rx = NULL;
}
- if (sg_dma_address(&priv->sg_rx))
- dma_free_coherent(port->dev, port->fifosize,
- sg_virt(&priv->sg_rx),
- sg_dma_address(&priv->sg_rx));
+
+ if (priv->rx_buf_dma) {
+ dma_free_coherent(port->dev, port->fifosize, priv->rx_buf_virt,
+ priv->rx_buf_dma);
+ priv->rx_buf_virt = NULL;
+ priv->rx_buf_dma = 0;
+ }
return;
}
@@ -656,21 +778,15 @@ static void pch_dma_rx_complete(void *arg)
{
struct eg20t_port *priv = arg;
struct uart_port *port = &priv->port;
- struct tty_struct *tty = tty_port_tty_get(&port->state->port);
int count;
- if (!tty) {
- dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
- return;
- }
-
dma_sync_sg_for_cpu(port->dev, &priv->sg_rx, 1, DMA_FROM_DEVICE);
count = dma_push_rx(priv, priv->trigger_level);
if (count)
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_flip_buffer_push(&port->state->port);
async_tx_ack(priv->desc_rx);
- pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT);
+ pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT |
+ PCH_UART_HAL_RX_ERR_INT);
}
static void pch_dma_tx_complete(void *arg)
@@ -725,7 +841,8 @@ static int handle_rx_to(struct eg20t_port *priv)
int rx_size;
int ret;
if (!priv->start_rx) {
- pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT);
+ pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT |
+ PCH_UART_HAL_RX_ERR_INT);
return 0;
}
buf = &priv->rxbuf;
@@ -763,7 +880,7 @@ static int dma_handle_rx(struct eg20t_port *priv)
sg_dma_address(sg) = priv->rx_buf_dma;
- desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx,
+ desc = dmaengine_prep_slave_sg(priv->chan_rx,
sg, 1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -890,6 +1007,10 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
priv->tx_dma_use = 1;
priv->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+ if (!priv->sg_tx_p) {
+ dev_err(priv->port.dev, "%s:kzalloc Failed\n", __func__);
+ return 0;
+ }
sg_init_table(priv->sg_tx_p, num); /* Initialize SG table */
sg = priv->sg_tx_p;
@@ -922,7 +1043,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
sg_dma_len(sg) = size;
}
- desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx,
+ desc = dmaengine_prep_slave_sg(priv->chan_tx,
priv->sg_tx_p, nent, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
@@ -944,23 +1065,35 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr)
{
- u8 fcr = ioread8(priv->membase + UART_FCR);
-
- /* Reset FIFO */
- fcr |= UART_FCR_CLEAR_RCVR;
- iowrite8(fcr, priv->membase + UART_FCR);
+ struct uart_port *port = &priv->port;
+ struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+ char *error_msg[5] = {};
+ int i = 0;
if (lsr & PCH_UART_LSR_ERR)
- dev_err(&priv->pdev->dev, "Error data in FIFO\n");
+ error_msg[i++] = "Error data in FIFO\n";
- if (lsr & UART_LSR_FE)
- dev_err(&priv->pdev->dev, "Framing Error\n");
+ if (lsr & UART_LSR_FE) {
+ port->icount.frame++;
+ error_msg[i++] = " Framing Error\n";
+ }
- if (lsr & UART_LSR_PE)
- dev_err(&priv->pdev->dev, "Parity Error\n");
+ if (lsr & UART_LSR_PE) {
+ port->icount.parity++;
+ error_msg[i++] = " Parity Error\n";
+ }
+
+ if (lsr & UART_LSR_OE) {
+ port->icount.overrun++;
+ error_msg[i++] = " Overrun Error\n";
+ }
- if (lsr & UART_LSR_OE)
- dev_err(&priv->pdev->dev, "Overrun Error\n");
+ if (tty == NULL) {
+ for (i = 0; error_msg[i] != NULL; i++)
+ dev_err(&priv->pdev->dev, error_msg[i]);
+ } else {
+ tty_kref_put(tty);
+ }
}
static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
@@ -969,12 +1102,17 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
unsigned int handled;
u8 lsr;
int ret = 0;
- unsigned int iid;
+ unsigned char iid;
unsigned long flags;
+ int next = 1;
+ u8 msr;
- spin_lock_irqsave(&priv->port.lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
handled = 0;
- while ((iid = pch_uart_hal_get_iid(priv)) > 1) {
+ while (next) {
+ iid = pch_uart_hal_get_iid(priv);
+ if (iid & PCH_UART_IIR_IP) /* No Interrupt */
+ break;
switch (iid) {
case PCH_UART_IID_RLS: /* Receiver Line Status */
lsr = pch_uart_hal_get_line_status(priv);
@@ -982,16 +1120,20 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
UART_LSR_PE | UART_LSR_OE)) {
pch_uart_err_ir(priv, lsr);
ret = PCH_UART_HANDLED_RX_ERR_INT;
+ } else {
+ ret = PCH_UART_HANDLED_LS_INT;
}
break;
case PCH_UART_IID_RDR: /* Received Data Ready */
if (priv->use_dma) {
pch_uart_hal_disable_interrupt(priv,
- PCH_UART_HAL_RX_INT);
+ PCH_UART_HAL_RX_INT |
+ PCH_UART_HAL_RX_ERR_INT);
ret = dma_handle_rx(priv);
if (!ret)
pch_uart_hal_enable_interrupt(priv,
- PCH_UART_HAL_RX_INT);
+ PCH_UART_HAL_RX_INT |
+ PCH_UART_HAL_RX_ERR_INT);
} else {
ret = handle_rx(priv);
}
@@ -1008,22 +1150,24 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
ret = handle_tx(priv);
break;
case PCH_UART_IID_MS: /* Modem Status */
- ret = PCH_UART_HANDLED_MS_INT;
+ msr = pch_uart_hal_get_modem(priv);
+ next = 0; /* MS ir prioirty is the lowest. So, MS ir
+ means final interrupt */
+ if ((msr & UART_MSR_ANY_DELTA) == 0)
+ break;
+ ret |= PCH_UART_HANDLED_MS_INT;
break;
default: /* Never junp to this label */
- dev_err(priv->port.dev, "%s:iid=%d (%lu)\n", __func__,
+ dev_err(priv->port.dev, "%s:iid=%02x (%lu)\n", __func__,
iid, jiffies);
ret = -1;
+ next = 0;
break;
}
handled |= (unsigned int)ret;
}
- if (handled == 0 && iid <= 1) {
- if (priv->int_dis_flag)
- priv->int_dis_flag = 0;
- }
- spin_unlock_irqrestore(&priv->port.lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
return IRQ_RETVAL(handled);
}
@@ -1032,14 +1176,12 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
static unsigned int pch_uart_tx_empty(struct uart_port *port)
{
struct eg20t_port *priv;
- int ret;
+
priv = container_of(port, struct eg20t_port, port);
if (priv->tx_empty)
- ret = TIOCSER_TEMT;
+ return TIOCSER_TEMT;
else
- ret = 0;
-
- return ret;
+ return 0;
}
/* Returns the current state of modem control inputs. */
@@ -1117,8 +1259,8 @@ static void pch_uart_stop_rx(struct uart_port *port)
struct eg20t_port *priv;
priv = container_of(port, struct eg20t_port, port);
priv->start_rx = 0;
- pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT);
- priv->int_dis_flag = 1;
+ pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT |
+ PCH_UART_HAL_RX_ERR_INT);
}
/* Enable the modem status interrupts. */
@@ -1136,9 +1278,9 @@ static void pch_uart_break_ctl(struct uart_port *port, int ctl)
unsigned long flags;
priv = container_of(port, struct eg20t_port, port);
- spin_lock_irqsave(&port->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
pch_uart_hal_set_break(priv, ctl);
- spin_unlock_irqrestore(&port->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
/* Grab any interrupt resources and initialise any low level driver state. */
@@ -1153,9 +1295,9 @@ static int pch_uart_startup(struct uart_port *port)
priv->tx_empty = 1;
if (port->uartclk)
- priv->base_baud = port->uartclk;
+ priv->uartclk = port->uartclk;
else
- port->uartclk = priv->base_baud;
+ port->uartclk = priv->uartclk;
pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
ret = pch_uart_hal_set_line(priv, default_baud,
@@ -1173,6 +1315,7 @@ static int pch_uart_startup(struct uart_port *port)
break;
case 16:
fifo_size = PCH_UART_HAL_FIFO16;
+ break;
case 1:
default:
fifo_size = PCH_UART_HAL_FIFO_DIS;
@@ -1202,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;
@@ -1210,7 +1353,8 @@ static int pch_uart_startup(struct uart_port *port)
pch_request_dma(port);
priv->start_rx = 1;
- pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT);
+ pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT |
+ PCH_UART_HAL_RX_ERR_INT);
uart_update_timeout(port, CS8, default_baud);
return 0;
@@ -1241,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;
@@ -1268,14 +1411,13 @@ static void pch_uart_set_termios(struct uart_port *port,
stb = PCH_UART_HAL_STB1;
if (termios->c_cflag & PARENB) {
- if (!(termios->c_cflag & PARODD))
+ if (termios->c_cflag & PARODD)
parity = PCH_UART_HAL_PARITY_ODD;
else
parity = PCH_UART_HAL_PARITY_EVEN;
- } else {
+ } else
parity = PCH_UART_HAL_PARITY_NONE;
- }
/* Only UART0 has auto hardware flow function */
if ((termios->c_cflag & CRTSCTS) && (priv->fifo_size == 256))
@@ -1287,7 +1429,8 @@ static void pch_uart_set_termios(struct uart_port *port,
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
- spin_lock_irqsave(&port->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
+ spin_lock(&port->lock);
uart_update_timeout(port, termios->c_cflag, baud);
rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb);
@@ -1300,7 +1443,8 @@ static void pch_uart_set_termios(struct uart_port *port,
tty_termios_encode_baud_rate(termios, baud, baud);
out:
- spin_unlock_irqrestore(&port->lock, flags);
+ spin_unlock(&port->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static const char *pch_uart_type(struct uart_port *port)
@@ -1366,37 +1510,20 @@ static int pch_uart_verify_port(struct uart_port *port,
__func__);
return -EOPNOTSUPP;
#endif
- priv->use_dma = 1;
- priv->use_dma_flag = 1;
- dev_info(priv->port.dev, "PCH UART : Use DMA Mode\n");
+ if (!priv->use_dma) {
+ pch_request_dma(port);
+ 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;
}
-static struct uart_ops pch_uart_ops = {
- .tx_empty = pch_uart_tx_empty,
- .set_mctrl = pch_uart_set_mctrl,
- .get_mctrl = pch_uart_get_mctrl,
- .stop_tx = pch_uart_stop_tx,
- .start_tx = pch_uart_start_tx,
- .stop_rx = pch_uart_stop_rx,
- .enable_ms = pch_uart_enable_ms,
- .break_ctl = pch_uart_break_ctl,
- .startup = pch_uart_startup,
- .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,
- .config_port = pch_uart_config_port,
- .verify_port = pch_uart_verify_port
-};
-
-#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
-
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_PCH_UART_CONSOLE)
/*
* Wait for transmitter & holding register to empty
*/
@@ -1427,6 +1554,79 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits)
}
}
}
+#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_PCH_UART_CONSOLE */
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for communicate via uart while
+ * in an interrupt or debug context.
+ */
+static int pch_uart_get_poll_char(struct uart_port *port)
+{
+ struct eg20t_port *priv =
+ container_of(port, struct eg20t_port, port);
+ u8 lsr = ioread8(priv->membase + UART_LSR);
+
+ if (!(lsr & UART_LSR_DR))
+ return NO_POLL_CHAR;
+
+ return ioread8(priv->membase + PCH_UART_RBR);
+}
+
+
+static void pch_uart_put_poll_char(struct uart_port *port,
+ unsigned char c)
+{
+ unsigned int ier;
+ struct eg20t_port *priv =
+ container_of(port, struct eg20t_port, port);
+
+ /*
+ * First save the IER then disable the interrupts
+ */
+ ier = ioread8(priv->membase + UART_IER);
+ pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
+
+ wait_for_xmitr(priv, UART_LSR_THRE);
+ /*
+ * Send the character out.
+ */
+ iowrite8(c, priv->membase + PCH_UART_THR);
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the IER
+ */
+ wait_for_xmitr(priv, BOTH_EMPTY);
+ iowrite8(ier, priv->membase + UART_IER);
+}
+#endif /* CONFIG_CONSOLE_POLL */
+
+static struct uart_ops pch_uart_ops = {
+ .tx_empty = pch_uart_tx_empty,
+ .set_mctrl = pch_uart_set_mctrl,
+ .get_mctrl = pch_uart_get_mctrl,
+ .stop_tx = pch_uart_stop_tx,
+ .start_tx = pch_uart_start_tx,
+ .stop_rx = pch_uart_stop_rx,
+ .enable_ms = pch_uart_enable_ms,
+ .break_ctl = pch_uart_break_ctl,
+ .startup = pch_uart_startup,
+ .shutdown = pch_uart_shutdown,
+ .set_termios = pch_uart_set_termios,
+/* .pm = pch_uart_pm, Not supported yet */
+ .type = pch_uart_type,
+ .release_port = pch_uart_release_port,
+ .request_port = pch_uart_request_port,
+ .config_port = pch_uart_config_port,
+ .verify_port = pch_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = pch_uart_get_poll_char,
+ .poll_put_char = pch_uart_put_poll_char,
+#endif
+};
+
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
static void pch_console_putchar(struct uart_port *port, int ch)
{
@@ -1447,10 +1647,10 @@ static void
pch_console_write(struct console *co, const char *s, unsigned int count)
{
struct eg20t_port *priv;
-
unsigned long flags;
+ int priv_locked = 1;
+ int port_locked = 1;
u8 ier;
- int locked = 1;
priv = pch_uart_ports[co->index];
@@ -1458,12 +1658,17 @@ pch_console_write(struct console *co, const char *s, unsigned int count)
local_irq_save(flags);
if (priv->port.sysrq) {
- /* serial8250_handle_port() already took the lock */
- locked = 0;
+ /* call to uart_handle_sysrq_char already took the priv lock */
+ priv_locked = 0;
+ /* serial8250_handle_port() already took the port lock */
+ port_locked = 0;
} else if (oops_in_progress) {
- locked = spin_trylock(&priv->port.lock);
- } else
+ priv_locked = spin_trylock(&priv->lock);
+ port_locked = spin_trylock(&priv->port.lock);
+ } else {
+ spin_lock(&priv->lock);
spin_lock(&priv->port.lock);
+ }
/*
* First save the IER then disable the interrupts
@@ -1481,15 +1686,17 @@ pch_console_write(struct console *co, const char *s, unsigned int count)
wait_for_xmitr(priv, BOTH_EMPTY);
iowrite8(ier, priv->membase + UART_IER);
- if (locked)
+ if (port_locked)
spin_unlock(&priv->port.lock);
+ if (priv_locked)
+ spin_unlock(&priv->lock);
local_irq_restore(flags);
}
static int __init pch_console_setup(struct console *co, char *options)
{
struct uart_port *port;
- int baud = 9600;
+ int baud = default_baud;
int bits = 8;
int parity = 'n';
int flow = 'n';
@@ -1506,8 +1713,7 @@ static int __init pch_console_setup(struct console *co, char *options)
if (!port || (!port->iobase && !port->membase))
return -ENODEV;
- /* setup uartclock */
- port->uartclk = DEFAULT_BAUD_RATE;
+ port->uartclk = pch_uart_get_uartclk();
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1530,7 +1736,7 @@ static struct console pch_console = {
#define PCH_CONSOLE (&pch_console)
#else
#define PCH_CONSOLE NULL
-#endif
+#endif /* CONFIG_SERIAL_PCH_UART_CONSOLE */
static struct uart_driver pch_uart_driver = {
.owner = THIS_MODULE,
@@ -1550,10 +1756,12 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
unsigned int iobase;
unsigned int mapbase;
unsigned char *rxbuf;
- int fifosize, base_baud;
+ int fifosize;
int port_type;
struct pch_uart_driver_data *board;
- const char *board_name;
+#ifdef CONFIG_DEBUG_FS
+ char name[32]; /* for debugfs file name */
+#endif
board = &drv_dat[id->driver_data];
port_type = board->port_type;
@@ -1566,13 +1774,6 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
if (!rxbuf)
goto init_port_free_txbuf;
- base_baud = DEFAULT_BAUD_RATE;
-
- /* quirk for CM-iTC board */
- board_name = dmi_get_system_info(DMI_BOARD_NAME);
- if (board_name && strstr(board_name, "CM-iTC"))
- base_baud = 192000000; /* 192.0MHz */
-
switch (port_type) {
case PORT_UNKNOWN:
fifosize = 256; /* EG20T/ML7213: UART0 */
@@ -1586,6 +1787,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
}
pci_enable_msi(pdev);
+ pci_set_master(pdev);
+
+ spin_lock_init(&priv->lock);
iobase = pci_resource_start(pdev, 0);
mapbase = pci_resource_start(pdev, 1);
@@ -1597,7 +1801,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
priv->rxbuf.size = PAGE_SIZE;
priv->fifo_size = fifosize;
- priv->base_baud = base_baud;
+ priv->uartclk = pch_uart_get_uartclk();
priv->port_type = PORT_MAX_8250 + port_type + 1;
priv->port.dev = &pdev->dev;
priv->port.iobase = iobase;
@@ -1611,10 +1815,15 @@ 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);
- pch_uart_hal_request(pdev, fifosize, base_baud);
+ priv->trigger_level = 1;
+ priv->fcr = 0;
#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
pch_uart_ports[board->line_no] = priv;
@@ -1623,6 +1832,12 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
if (ret < 0)
goto init_port_hal_free;
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof(name), "uart%d_regs", board->line_no);
+ priv->debugfs = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, priv, &port_regs_ops);
+#endif
+
return priv;
init_port_hal_free:
@@ -1639,16 +1854,18 @@ init_port_alloc_err:
static void pch_uart_exit_port(struct eg20t_port *priv)
{
+
+#ifdef CONFIG_DEBUG_FS
+ if (priv->debugfs)
+ 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);
}
static void pch_uart_pci_remove(struct pci_dev *pdev)
{
- struct eg20t_port *priv;
-
- priv = (struct eg20t_port *)pci_get_drvdata(pdev);
+ struct eg20t_port *priv = pci_get_drvdata(pdev);
pci_disable_msi(pdev);
@@ -1696,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),
@@ -1722,7 +1939,7 @@ static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = {
{0,},
};
-static int __devinit pch_uart_pci_probe(struct pci_dev *pdev,
+static int pch_uart_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret;
@@ -1752,7 +1969,7 @@ static struct pci_driver pch_uart_pci_driver = {
.name = "pch_uart",
.id_table = pch_uart_pci_id,
.probe = pch_uart_pci_probe,
- .remove = __devexit_p(pch_uart_pci_remove),
+ .remove = pch_uart_pci_remove,
.suspend = pch_uart_pci_suspend,
.resume = pch_uart_pci_resume,
};
@@ -1784,4 +2001,11 @@ 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)");
+module_param(user_uartclk, uint, S_IRUGO);
+MODULE_PARM_DESC(user_uartclk,
+ "Override UART default or board specific UART clock");
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index e9c2dfe471a..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>
@@ -227,19 +229,19 @@ static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable)
write_zsreg(uap, R1, uap->curregs[1]);
}
-static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
+static bool pmz_receive_chars(struct uart_pmac_port *uap)
{
- struct tty_struct *tty = NULL;
+ struct tty_port *port;
unsigned char ch, r1, drop, error, flag;
int loops = 0;
/* Sanity check, make sure the old bug is no longer happening */
- if (uap->port.state == NULL || uap->port.state->port.tty == NULL) {
+ if (uap->port.state == NULL) {
WARN_ON(1);
(void)read_zsdata(uap);
- return NULL;
+ return false;
}
- tty = uap->port.state->port.tty;
+ port = &uap->port.state->port;
while (1) {
error = 0;
@@ -309,10 +311,10 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
if (uap->port.ignore_status_mask == 0xff ||
(r1 & uap->port.ignore_status_mask) == 0) {
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(port, ch, flag);
}
if (r1 & Rx_OVR)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
next_char:
/* We can get stuck in an infinite loop getting char 0 when the
* line is in a wrong HW state, we break that here.
@@ -328,11 +330,11 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
break;
}
- return tty;
+ return true;
flood:
pmz_interrupt_control(uap, 0);
pmz_error("pmz: rx irq flood !\n");
- return tty;
+ return true;
}
static void pmz_status_handle(struct uart_pmac_port *uap)
@@ -453,7 +455,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id)
struct uart_pmac_port *uap_a;
struct uart_pmac_port *uap_b;
int rc = IRQ_NONE;
- struct tty_struct *tty;
+ bool push;
u8 r3;
uap_a = pmz_get_port_A(uap);
@@ -466,10 +468,10 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id)
pmz_debug("irq, r3: %x\n", r3);
#endif
/* Channel A */
- tty = NULL;
+ push = false;
if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
if (!ZS_IS_OPEN(uap_a)) {
- pmz_debug("ChanA interrupt while open !\n");
+ pmz_debug("ChanA interrupt while not open !\n");
goto skip_a;
}
write_zsreg(uap_a, R0, RES_H_IUS);
@@ -477,24 +479,24 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id)
if (r3 & CHAEXT)
pmz_status_handle(uap_a);
if (r3 & CHARxIP)
- tty = pmz_receive_chars(uap_a);
+ push = pmz_receive_chars(uap_a);
if (r3 & CHATxIP)
pmz_transmit_chars(uap_a);
rc = IRQ_HANDLED;
}
skip_a:
spin_unlock(&uap_a->port.lock);
- if (tty != NULL)
- tty_flip_buffer_push(tty);
+ if (push)
+ tty_flip_buffer_push(&uap->port.state->port);
if (!uap_b)
goto out;
spin_lock(&uap_b->port.lock);
- tty = NULL;
+ push = false;
if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
- if (!ZS_IS_OPEN(uap_a)) {
- pmz_debug("ChanB interrupt while open !\n");
+ if (!ZS_IS_OPEN(uap_b)) {
+ pmz_debug("ChanB interrupt while not open !\n");
goto skip_b;
}
write_zsreg(uap_b, R0, RES_H_IUS);
@@ -502,15 +504,15 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id)
if (r3 & CHBEXT)
pmz_status_handle(uap_b);
if (r3 & CHBRxIP)
- tty = pmz_receive_chars(uap_b);
+ push = pmz_receive_chars(uap_b);
if (r3 & CHBTxIP)
pmz_transmit_chars(uap_b);
rc = IRQ_HANDLED;
}
skip_b:
spin_unlock(&uap_b->port.lock);
- if (tty != NULL)
- tty_flip_buffer_push(tty);
+ if (push)
+ tty_flip_buffer_push(&uap->port.state->port);
out:
return rc;
@@ -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;
@@ -1348,10 +1353,16 @@ static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser)
static int pmz_poll_get_char(struct uart_port *port)
{
struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+ int tries = 2;
- while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0)
- udelay(5);
- return read_zsdata(uap);
+ while (tries) {
+ if ((read_zsreg(uap, R0) & Rx_CH_AV) != 0)
+ return read_zsdata(uap);
+ if (tries--)
+ udelay(5);
+ }
+
+ return NO_POLL_CHAR;
}
static void pmz_poll_put_char(struct uart_port *port, unsigned char c)
@@ -1506,7 +1517,7 @@ no_dma:
* fixed up interrupt info, but we use the device-tree directly
* here due to early probing so we need the fixup too.
*/
- if (uap->port.irq == NO_IRQ &&
+ if (uap->port.irq == 0 &&
np->parent && np->parent->parent &&
of_device_is_compatible(np->parent->parent, "gatwick")) {
/* IRQs on gatwick are offset by 64 */
@@ -1792,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;
@@ -2045,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 0aa75a97531..2ba24a45c97 100644
--- a/drivers/tty/serial/pnx8xxx_uart.c
+++ b/drivers/tty/serial/pnx8xxx_uart.c
@@ -181,7 +181,6 @@ static void pnx8xxx_enable_ms(struct uart_port *port)
static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport)
{
- struct tty_struct *tty = sport->port.state->port.tty;
unsigned int status, ch, flg;
status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
@@ -238,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));
}
- tty_flip_buffer_push(tty);
+
+ 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)
@@ -475,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);
@@ -802,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 5c8e3bba6c8..c638c53cd2b 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -36,6 +36,7 @@
#include <linux/circ_buf.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -44,6 +45,8 @@
#include <linux/io.h>
#include <linux/slab.h>
+#define PXA_NAME_LEN 8
+
struct uart_pxa_port {
struct uart_port port;
unsigned char ier;
@@ -51,7 +54,7 @@ struct uart_pxa_port {
unsigned char mcr;
unsigned int lsr_break_flag;
struct clk *clk;
- char *name;
+ char name[PXA_NAME_LEN];
};
static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
@@ -95,7 +98,6 @@ static void serial_pxa_stop_rx(struct uart_port *port)
static inline void receive_chars(struct uart_pxa_port *up, int *status)
{
- struct tty_struct *tty = up->port.state->port.tty;
unsigned int ch, flag;
int max_count = 256;
@@ -165,7 +167,7 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status)
ignore_char:
*status = serial_in(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&up->port.state->port);
/* work around Errata #20 according to
* Intel(R) PXA27x Processor Family
@@ -330,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;
@@ -515,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;
/*
@@ -579,9 +556,9 @@ serial_pxa_pm(struct uart_port *port, unsigned int state,
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
if (!state)
- clk_enable(up->clk);
+ clk_prepare_enable(up->clk);
else
- clk_disable(up->clk);
+ clk_disable_unprepare(up->clk);
}
static void serial_pxa_release_port(struct uart_port *port)
@@ -667,8 +644,17 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_pxa_port *up = serial_pxa_ports[co->index];
unsigned int ier;
+ unsigned long flags;
+ int locked = 1;
clk_enable(up->clk);
+ 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);
/*
* First save the IER then disable the interrupts
@@ -685,9 +671,59 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
wait_for_xmitr(up);
serial_out(up, UART_IER, ier);
+ if (locked)
+ spin_unlock(&up->port.lock);
+ local_irq_restore(flags);
clk_disable(up->clk);
+
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int serial_pxa_get_poll_char(struct uart_port *port)
+{
+ struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+ unsigned char lsr = serial_in(up, UART_LSR);
+
+ while (!(lsr & UART_LSR_DR))
+ lsr = serial_in(up, UART_LSR);
+
+ return serial_in(up, UART_RX);
}
+
+static void serial_pxa_put_poll_char(struct uart_port *port,
+ unsigned char c)
+{
+ unsigned int ier;
+ struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+ /*
+ * First save the IER then disable the interrupts
+ */
+ ier = serial_in(up, UART_IER);
+ serial_out(up, UART_IER, UART_IER_UUE);
+
+ wait_for_xmitr(up);
+ /*
+ * Send the character out.
+ */
+ serial_out(up, UART_TX, c);
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the IER
+ */
+ wait_for_xmitr(up);
+ serial_out(up, UART_IER, ier);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
static int __init
serial_pxa_console_setup(struct console *co, char *options)
{
@@ -724,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,
@@ -742,6 +778,10 @@ struct uart_ops serial_pxa_pops = {
.request_port = serial_pxa_request_port,
.config_port = serial_pxa_config_port,
.verify_port = serial_pxa_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = serial_pxa_get_poll_char,
+ .poll_put_char = serial_pxa_put_poll_char,
+#endif
};
static struct uart_driver serial_pxa_reg = {
@@ -781,6 +821,31 @@ static const struct dev_pm_ops serial_pxa_pm_ops = {
};
#endif
+static struct of_device_id serial_pxa_dt_ids[] = {
+ { .compatible = "mrvl,pxa-uart", },
+ { .compatible = "mrvl,mmp-uart", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
+
+static int serial_pxa_probe_dt(struct platform_device *pdev,
+ struct uart_pxa_port *sport)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np)
+ return 1;
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
+ }
+ sport->port.line = ret;
+ return 0;
+}
+
static int serial_pxa_probe(struct platform_device *dev)
{
struct uart_pxa_port *sport;
@@ -802,26 +867,28 @@ static int serial_pxa_probe(struct platform_device *dev)
goto err_free;
}
+ ret = clk_prepare(sport->clk);
+ if (ret) {
+ clk_put(sport->clk);
+ goto err_free;
+ }
+
sport->port.type = PORT_PXA;
sport->port.iotype = UPIO_MEM;
sport->port.mapbase = mmres->start;
sport->port.irq = irqres->start;
sport->port.fifosize = 64;
sport->port.ops = &serial_pxa_pops;
- sport->port.line = dev->id;
sport->port.dev = &dev->dev;
sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
sport->port.uartclk = clk_get_rate(sport->clk);
- switch (dev->id) {
- case 0: sport->name = "FFUART"; break;
- case 1: sport->name = "BTUART"; break;
- case 2: sport->name = "STUART"; break;
- case 3: sport->name = "HWUART"; break;
- default:
- sport->name = "???";
- break;
- }
+ ret = serial_pxa_probe_dt(dev, sport);
+ if (ret > 0)
+ sport->port.line = dev->id;
+ else if (ret < 0)
+ goto err_clk;
+ snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
sport->port.membase = ioremap(mmres->start, resource_size(mmres));
if (!sport->port.membase) {
@@ -829,7 +896,7 @@ static int serial_pxa_probe(struct platform_device *dev)
goto err_clk;
}
- serial_pxa_ports[dev->id] = sport;
+ serial_pxa_ports[sport->port.line] = sport;
uart_add_one_port(&serial_pxa_reg, &sport->port);
platform_set_drvdata(dev, sport);
@@ -837,6 +904,7 @@ static int serial_pxa_probe(struct platform_device *dev)
return 0;
err_clk:
+ clk_unprepare(sport->clk);
clk_put(sport->clk);
err_free:
kfree(sport);
@@ -847,9 +915,9 @@ 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);
clk_put(sport->clk);
kfree(sport);
@@ -866,10 +934,11 @@ static struct platform_driver serial_pxa_driver = {
#ifdef CONFIG_PM
.pm = &serial_pxa_pm_ops,
#endif
+ .of_match_table = serial_pxa_dt_ids,
},
};
-int __init serial_pxa_init(void)
+static int __init serial_pxa_init(void)
{
int ret;
@@ -884,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
new file mode 100644
index 00000000000..056f91b3a4c
--- /dev/null
+++ b/drivers/tty/serial/rp2.c
@@ -0,0 +1,887 @@
+/*
+ * Driver for Comtrol RocketPort EXPRESS/INFINITY cards
+ *
+ * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
+ *
+ * Inspired by, and loosely based on:
+ *
+ * ar933x_uart.c
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * rocketport_infinity_express-linux-1.20.tar.gz
+ * Copyright (C) 2004-2011 Comtrol, Inc.
+ *
+ * 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/bitops.h>
+#include <linux/compiler.h>
+#include <linux/completion.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#define DRV_NAME "rp2"
+
+#define RP2_FW_NAME "rp2.fw"
+#define RP2_UCODE_BYTES 0x3f
+
+#define PORTS_PER_ASIC 16
+#define ALL_PORTS_MASK (BIT(PORTS_PER_ASIC) - 1)
+
+#define UART_CLOCK 44236800
+#define DEFAULT_BAUD_DIV (UART_CLOCK / (9600 * 16))
+#define FIFO_SIZE 512
+
+/* BAR0 registers */
+#define RP2_FPGA_CTL0 0x110
+#define RP2_FPGA_CTL1 0x11c
+#define RP2_IRQ_MASK 0x1ec
+#define RP2_IRQ_MASK_EN_m BIT(0)
+#define RP2_IRQ_STATUS 0x1f0
+
+/* BAR1 registers */
+#define RP2_ASIC_SPACING 0x1000
+#define RP2_ASIC_OFFSET(i) ((i) << ilog2(RP2_ASIC_SPACING))
+
+#define RP2_PORT_BASE 0x000
+#define RP2_PORT_SPACING 0x040
+
+#define RP2_UCODE_BASE 0x400
+#define RP2_UCODE_SPACING 0x80
+
+#define RP2_CLK_PRESCALER 0xc00
+#define RP2_CH_IRQ_STAT 0xc04
+#define RP2_CH_IRQ_MASK 0xc08
+#define RP2_ASIC_IRQ 0xd00
+#define RP2_ASIC_IRQ_EN_m BIT(20)
+#define RP2_GLOBAL_CMD 0xd0c
+#define RP2_ASIC_CFG 0xd04
+
+/* port registers */
+#define RP2_DATA_DWORD 0x000
+
+#define RP2_DATA_BYTE 0x008
+#define RP2_DATA_BYTE_ERR_PARITY_m BIT(8)
+#define RP2_DATA_BYTE_ERR_OVERRUN_m BIT(9)
+#define RP2_DATA_BYTE_ERR_FRAMING_m BIT(10)
+#define RP2_DATA_BYTE_BREAK_m BIT(11)
+
+/* This lets uart_insert_char() drop bytes received on a !CREAD port */
+#define RP2_DUMMY_READ BIT(16)
+
+#define RP2_DATA_BYTE_EXCEPTION_MASK (RP2_DATA_BYTE_ERR_PARITY_m | \
+ RP2_DATA_BYTE_ERR_OVERRUN_m | \
+ RP2_DATA_BYTE_ERR_FRAMING_m | \
+ RP2_DATA_BYTE_BREAK_m)
+
+#define RP2_RX_FIFO_COUNT 0x00c
+#define RP2_TX_FIFO_COUNT 0x00e
+
+#define RP2_CHAN_STAT 0x010
+#define RP2_CHAN_STAT_RXDATA_m BIT(0)
+#define RP2_CHAN_STAT_DCD_m BIT(3)
+#define RP2_CHAN_STAT_DSR_m BIT(4)
+#define RP2_CHAN_STAT_CTS_m BIT(5)
+#define RP2_CHAN_STAT_RI_m BIT(6)
+#define RP2_CHAN_STAT_OVERRUN_m BIT(13)
+#define RP2_CHAN_STAT_DSR_CHANGED_m BIT(16)
+#define RP2_CHAN_STAT_CTS_CHANGED_m BIT(17)
+#define RP2_CHAN_STAT_CD_CHANGED_m BIT(18)
+#define RP2_CHAN_STAT_RI_CHANGED_m BIT(22)
+#define RP2_CHAN_STAT_TXEMPTY_m BIT(25)
+
+#define RP2_CHAN_STAT_MS_CHANGED_MASK (RP2_CHAN_STAT_DSR_CHANGED_m | \
+ RP2_CHAN_STAT_CTS_CHANGED_m | \
+ RP2_CHAN_STAT_CD_CHANGED_m | \
+ RP2_CHAN_STAT_RI_CHANGED_m)
+
+#define RP2_TXRX_CTL 0x014
+#define RP2_TXRX_CTL_MSRIRQ_m BIT(0)
+#define RP2_TXRX_CTL_RXIRQ_m BIT(2)
+#define RP2_TXRX_CTL_RX_TRIG_s 3
+#define RP2_TXRX_CTL_RX_TRIG_m (0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_1 (0x1 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_256 (0x2 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_TRIG_448 (0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_RX_EN_m BIT(5)
+#define RP2_TXRX_CTL_RTSFLOW_m BIT(6)
+#define RP2_TXRX_CTL_DTRFLOW_m BIT(7)
+#define RP2_TXRX_CTL_TX_TRIG_s 16
+#define RP2_TXRX_CTL_TX_TRIG_m (0x3 << RP2_TXRX_CTL_RX_TRIG_s)
+#define RP2_TXRX_CTL_DSRFLOW_m BIT(18)
+#define RP2_TXRX_CTL_TXIRQ_m BIT(19)
+#define RP2_TXRX_CTL_CTSFLOW_m BIT(23)
+#define RP2_TXRX_CTL_TX_EN_m BIT(24)
+#define RP2_TXRX_CTL_RTS_m BIT(25)
+#define RP2_TXRX_CTL_DTR_m BIT(26)
+#define RP2_TXRX_CTL_LOOP_m BIT(27)
+#define RP2_TXRX_CTL_BREAK_m BIT(28)
+#define RP2_TXRX_CTL_CMSPAR_m BIT(29)
+#define RP2_TXRX_CTL_nPARODD_m BIT(30)
+#define RP2_TXRX_CTL_PARENB_m BIT(31)
+
+#define RP2_UART_CTL 0x018
+#define RP2_UART_CTL_MODE_s 0
+#define RP2_UART_CTL_MODE_m (0x7 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_MODE_rs232 (0x1 << RP2_UART_CTL_MODE_s)
+#define RP2_UART_CTL_FLUSH_RX_m BIT(3)
+#define RP2_UART_CTL_FLUSH_TX_m BIT(4)
+#define RP2_UART_CTL_RESET_CH_m BIT(5)
+#define RP2_UART_CTL_XMIT_EN_m BIT(6)
+#define RP2_UART_CTL_DATABITS_s 8
+#define RP2_UART_CTL_DATABITS_m (0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_8 (0x3 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_7 (0x2 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_6 (0x1 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_DATABITS_5 (0x0 << RP2_UART_CTL_DATABITS_s)
+#define RP2_UART_CTL_STOPBITS_m BIT(10)
+
+#define RP2_BAUD 0x01c
+
+/* ucode registers */
+#define RP2_TX_SWFLOW 0x02
+#define RP2_TX_SWFLOW_ena 0x81
+#define RP2_TX_SWFLOW_dis 0x9d
+
+#define RP2_RX_SWFLOW 0x0c
+#define RP2_RX_SWFLOW_ena 0x81
+#define RP2_RX_SWFLOW_dis 0x8d
+
+#define RP2_RX_FIFO 0x37
+#define RP2_RX_FIFO_ena 0x08
+#define RP2_RX_FIFO_dis 0x81
+
+static struct uart_driver rp2_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = DRV_NAME,
+ .dev_name = "ttyRP",
+ .nr = CONFIG_SERIAL_RP2_NR_UARTS,
+};
+
+struct rp2_card;
+
+struct rp2_uart_port {
+ struct uart_port port;
+ int idx;
+ int ignore_rx;
+ struct rp2_card *card;
+ void __iomem *asic_base;
+ void __iomem *base;
+ void __iomem *ucode;
+};
+
+struct rp2_card {
+ struct pci_dev *pdev;
+ struct rp2_uart_port *ports;
+ int n_ports;
+ int initialized_ports;
+ int minor_start;
+ int smpte;
+ void __iomem *bar0;
+ void __iomem *bar1;
+ spinlock_t card_lock;
+ struct completion fw_loaded;
+};
+
+#define RP_ID(prod) PCI_VDEVICE(RP, (prod))
+#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0))
+
+static inline void rp2_decode_cap(const struct pci_device_id *id,
+ int *ports, int *smpte)
+{
+ *ports = id->driver_data >> 8;
+ *smpte = id->driver_data & 0xff;
+}
+
+static DEFINE_SPINLOCK(rp2_minor_lock);
+static int rp2_minor_next;
+
+static int rp2_alloc_ports(int n_ports)
+{
+ int ret = -ENOSPC;
+
+ spin_lock(&rp2_minor_lock);
+ if (rp2_minor_next + n_ports <= CONFIG_SERIAL_RP2_NR_UARTS) {
+ /* sorry, no support for hot unplugging individual cards */
+ ret = rp2_minor_next;
+ rp2_minor_next += n_ports;
+ }
+ spin_unlock(&rp2_minor_lock);
+
+ return ret;
+}
+
+static inline struct rp2_uart_port *port_to_up(struct uart_port *port)
+{
+ return container_of(port, struct rp2_uart_port, port);
+}
+
+static void rp2_rmw(struct rp2_uart_port *up, int reg,
+ u32 clr_bits, u32 set_bits)
+{
+ u32 tmp = readl(up->base + reg);
+ tmp &= ~clr_bits;
+ tmp |= set_bits;
+ writel(tmp, up->base + reg);
+}
+
+static void rp2_rmw_clr(struct rp2_uart_port *up, int reg, u32 val)
+{
+ rp2_rmw(up, reg, val, 0);
+}
+
+static void rp2_rmw_set(struct rp2_uart_port *up, int reg, u32 val)
+{
+ rp2_rmw(up, reg, 0, val);
+}
+
+static void rp2_mask_ch_irq(struct rp2_uart_port *up, int ch_num,
+ int is_enabled)
+{
+ unsigned long flags, irq_mask;
+
+ spin_lock_irqsave(&up->card->card_lock, flags);
+
+ irq_mask = readl(up->asic_base + RP2_CH_IRQ_MASK);
+ if (is_enabled)
+ irq_mask &= ~BIT(ch_num);
+ else
+ irq_mask |= BIT(ch_num);
+ writel(irq_mask, up->asic_base + RP2_CH_IRQ_MASK);
+
+ spin_unlock_irqrestore(&up->card->card_lock, flags);
+}
+
+static unsigned int rp2_uart_tx_empty(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ unsigned long tx_fifo_bytes, flags;
+
+ /*
+ * This should probably check the transmitter, not the FIFO.
+ * But the TXEMPTY bit doesn't seem to work unless the TX IRQ is
+ * enabled.
+ */
+ spin_lock_irqsave(&up->port.lock, flags);
+ tx_fifo_bytes = readw(up->base + RP2_TX_FIFO_COUNT);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ return tx_fifo_bytes ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int rp2_uart_get_mctrl(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ u32 status;
+
+ status = readl(up->base + RP2_CHAN_STAT);
+ return ((status & RP2_CHAN_STAT_DCD_m) ? TIOCM_CAR : 0) |
+ ((status & RP2_CHAN_STAT_DSR_m) ? TIOCM_DSR : 0) |
+ ((status & RP2_CHAN_STAT_CTS_m) ? TIOCM_CTS : 0) |
+ ((status & RP2_CHAN_STAT_RI_m) ? TIOCM_RI : 0);
+}
+
+static void rp2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ rp2_rmw(port_to_up(port), RP2_TXRX_CTL,
+ RP2_TXRX_CTL_DTR_m | RP2_TXRX_CTL_RTS_m | RP2_TXRX_CTL_LOOP_m,
+ ((mctrl & TIOCM_DTR) ? RP2_TXRX_CTL_DTR_m : 0) |
+ ((mctrl & TIOCM_RTS) ? RP2_TXRX_CTL_RTS_m : 0) |
+ ((mctrl & TIOCM_LOOP) ? RP2_TXRX_CTL_LOOP_m : 0));
+}
+
+static void rp2_uart_start_tx(struct uart_port *port)
+{
+ rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_tx(struct uart_port *port)
+{
+ rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m);
+}
+
+static void rp2_uart_stop_rx(struct uart_port *port)
+{
+ rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_RXIRQ_m);
+}
+
+static void rp2_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ rp2_rmw(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_BREAK_m,
+ break_state ? RP2_TXRX_CTL_BREAK_m : 0);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_uart_enable_ms(struct uart_port *port)
+{
+ rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m);
+}
+
+static void __rp2_uart_set_termios(struct rp2_uart_port *up,
+ unsigned long cfl,
+ unsigned long ifl,
+ unsigned int baud_div)
+{
+ /* baud rate divisor (calculated elsewhere). 0 = divide-by-1 */
+ writew(baud_div - 1, up->base + RP2_BAUD);
+
+ /* data bits and stop bits */
+ rp2_rmw(up, RP2_UART_CTL,
+ RP2_UART_CTL_STOPBITS_m | RP2_UART_CTL_DATABITS_m,
+ ((cfl & CSTOPB) ? RP2_UART_CTL_STOPBITS_m : 0) |
+ (((cfl & CSIZE) == CS8) ? RP2_UART_CTL_DATABITS_8 : 0) |
+ (((cfl & CSIZE) == CS7) ? RP2_UART_CTL_DATABITS_7 : 0) |
+ (((cfl & CSIZE) == CS6) ? RP2_UART_CTL_DATABITS_6 : 0) |
+ (((cfl & CSIZE) == CS5) ? RP2_UART_CTL_DATABITS_5 : 0));
+
+ /* parity and hardware flow control */
+ rp2_rmw(up, RP2_TXRX_CTL,
+ RP2_TXRX_CTL_PARENB_m | RP2_TXRX_CTL_nPARODD_m |
+ RP2_TXRX_CTL_CMSPAR_m | RP2_TXRX_CTL_DTRFLOW_m |
+ RP2_TXRX_CTL_DSRFLOW_m | RP2_TXRX_CTL_RTSFLOW_m |
+ RP2_TXRX_CTL_CTSFLOW_m,
+ ((cfl & PARENB) ? RP2_TXRX_CTL_PARENB_m : 0) |
+ ((cfl & PARODD) ? 0 : RP2_TXRX_CTL_nPARODD_m) |
+ ((cfl & CMSPAR) ? RP2_TXRX_CTL_CMSPAR_m : 0) |
+ ((cfl & CRTSCTS) ? (RP2_TXRX_CTL_RTSFLOW_m |
+ RP2_TXRX_CTL_CTSFLOW_m) : 0));
+
+ /* XON/XOFF software flow control */
+ writeb((ifl & IXON) ? RP2_TX_SWFLOW_ena : RP2_TX_SWFLOW_dis,
+ up->ucode + RP2_TX_SWFLOW);
+ writeb((ifl & IXOFF) ? RP2_RX_SWFLOW_ena : RP2_RX_SWFLOW_dis,
+ up->ucode + RP2_RX_SWFLOW);
+}
+
+static void rp2_uart_set_termios(struct uart_port *port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ unsigned long flags;
+ unsigned int baud, baud_div;
+
+ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+ baud_div = uart_get_divisor(port, baud);
+
+ if (tty_termios_baud_rate(new))
+ tty_termios_encode_baud_rate(new, baud, baud);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* ignore all characters if CREAD is not set */
+ port->ignore_status_mask = (new->c_cflag & CREAD) ? 0 : RP2_DUMMY_READ;
+
+ __rp2_uart_set_termios(up, new->c_cflag, new->c_iflag, baud_div);
+ uart_update_timeout(port, new->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rp2_rx_chars(struct rp2_uart_port *up)
+{
+ u16 bytes = readw(up->base + RP2_RX_FIFO_COUNT);
+ struct tty_port *port = &up->port.state->port;
+
+ for (; bytes != 0; bytes--) {
+ u32 byte = readw(up->base + RP2_DATA_BYTE) | RP2_DUMMY_READ;
+ char ch = byte & 0xff;
+
+ if (likely(!(byte & RP2_DATA_BYTE_EXCEPTION_MASK))) {
+ if (!uart_handle_sysrq_char(&up->port, ch))
+ uart_insert_char(&up->port, byte, 0, ch,
+ TTY_NORMAL);
+ } else {
+ char flag = TTY_NORMAL;
+
+ if (byte & RP2_DATA_BYTE_BREAK_m)
+ flag = TTY_BREAK;
+ else if (byte & RP2_DATA_BYTE_ERR_FRAMING_m)
+ flag = TTY_FRAME;
+ else if (byte & RP2_DATA_BYTE_ERR_PARITY_m)
+ flag = TTY_PARITY;
+ uart_insert_char(&up->port, byte,
+ RP2_DATA_BYTE_ERR_OVERRUN_m, ch, flag);
+ }
+ 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)
+{
+ u16 max_tx = FIFO_SIZE - readw(up->base + RP2_TX_FIFO_COUNT);
+ struct circ_buf *xmit = &up->port.state->xmit;
+
+ if (uart_tx_stopped(&up->port)) {
+ rp2_uart_stop_tx(&up->port);
+ return;
+ }
+
+ for (; max_tx != 0; max_tx--) {
+ if (up->port.x_char) {
+ writeb(up->port.x_char, up->base + RP2_DATA_BYTE);
+ up->port.x_char = 0;
+ up->port.icount.tx++;
+ continue;
+ }
+ if (uart_circ_empty(xmit)) {
+ rp2_uart_stop_tx(&up->port);
+ break;
+ }
+ writeb(xmit->buf[xmit->tail], up->base + RP2_DATA_BYTE);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ up->port.icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+}
+
+static void rp2_ch_interrupt(struct rp2_uart_port *up)
+{
+ u32 status;
+
+ spin_lock(&up->port.lock);
+
+ /*
+ * The IRQ status bits are clear-on-write. Other status bits in
+ * this register aren't, so it's harmless to write to them.
+ */
+ status = readl(up->base + RP2_CHAN_STAT);
+ writel(status, up->base + RP2_CHAN_STAT);
+
+ if (status & RP2_CHAN_STAT_RXDATA_m)
+ rp2_rx_chars(up);
+ if (status & RP2_CHAN_STAT_TXEMPTY_m)
+ rp2_tx_chars(up);
+ if (status & RP2_CHAN_STAT_MS_CHANGED_MASK)
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+
+ spin_unlock(&up->port.lock);
+}
+
+static int rp2_asic_interrupt(struct rp2_card *card, unsigned int asic_id)
+{
+ void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+ int ch, handled = 0;
+ unsigned long status = readl(base + RP2_CH_IRQ_STAT) &
+ ~readl(base + RP2_CH_IRQ_MASK);
+
+ for_each_set_bit(ch, &status, PORTS_PER_ASIC) {
+ rp2_ch_interrupt(&card->ports[ch]);
+ handled++;
+ }
+ return handled;
+}
+
+static irqreturn_t rp2_uart_interrupt(int irq, void *dev_id)
+{
+ struct rp2_card *card = dev_id;
+ int handled;
+
+ handled = rp2_asic_interrupt(card, 0);
+ if (card->n_ports >= PORTS_PER_ASIC)
+ handled += rp2_asic_interrupt(card, 1);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline void rp2_flush_fifos(struct rp2_uart_port *up)
+{
+ rp2_rmw_set(up, RP2_UART_CTL,
+ RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+ readl(up->base + RP2_UART_CTL);
+ udelay(10);
+ rp2_rmw_clr(up, RP2_UART_CTL,
+ RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m);
+}
+
+static int rp2_uart_startup(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+
+ rp2_flush_fifos(up);
+ rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m, RP2_TXRX_CTL_RXIRQ_m);
+ rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_RX_TRIG_m,
+ RP2_TXRX_CTL_RX_TRIG_1);
+ rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+ rp2_mask_ch_irq(up, up->idx, 1);
+
+ return 0;
+}
+
+static void rp2_uart_shutdown(struct uart_port *port)
+{
+ struct rp2_uart_port *up = port_to_up(port);
+ unsigned long flags;
+
+ rp2_uart_break_ctl(port, 0);
+
+ spin_lock_irqsave(&port->lock, flags);
+ rp2_mask_ch_irq(up, up->idx, 0);
+ rp2_rmw(up, RP2_CHAN_STAT, 0, 0);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *rp2_uart_type(struct uart_port *port)
+{
+ return (port->type == PORT_RP2) ? "RocketPort 2 UART" : NULL;
+}
+
+static void rp2_uart_release_port(struct uart_port *port)
+{
+ /* Nothing to release ... */
+}
+
+static int rp2_uart_request_port(struct uart_port *port)
+{
+ /* UARTs always present */
+ return 0;
+}
+
+static void rp2_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_RP2;
+}
+
+static int rp2_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_RP2)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct uart_ops rp2_uart_ops = {
+ .tx_empty = rp2_uart_tx_empty,
+ .set_mctrl = rp2_uart_set_mctrl,
+ .get_mctrl = rp2_uart_get_mctrl,
+ .stop_tx = rp2_uart_stop_tx,
+ .start_tx = rp2_uart_start_tx,
+ .stop_rx = rp2_uart_stop_rx,
+ .enable_ms = rp2_uart_enable_ms,
+ .break_ctl = rp2_uart_break_ctl,
+ .startup = rp2_uart_startup,
+ .shutdown = rp2_uart_shutdown,
+ .set_termios = rp2_uart_set_termios,
+ .type = rp2_uart_type,
+ .release_port = rp2_uart_release_port,
+ .request_port = rp2_uart_request_port,
+ .config_port = rp2_uart_config_port,
+ .verify_port = rp2_uart_verify_port,
+};
+
+static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id)
+{
+ void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id);
+ u32 clk_cfg;
+
+ writew(1, base + RP2_GLOBAL_CMD);
+ readw(base + RP2_GLOBAL_CMD);
+ msleep(100);
+ writel(0, base + RP2_CLK_PRESCALER);
+
+ /* TDM clock configuration */
+ clk_cfg = readw(base + RP2_ASIC_CFG);
+ clk_cfg = (clk_cfg & ~BIT(8)) | BIT(9);
+ writew(clk_cfg, base + RP2_ASIC_CFG);
+
+ /* IRQ routing */
+ writel(ALL_PORTS_MASK, base + RP2_CH_IRQ_MASK);
+ writel(RP2_ASIC_IRQ_EN_m, base + RP2_ASIC_IRQ);
+}
+
+static void rp2_init_card(struct rp2_card *card)
+{
+ writel(4, card->bar0 + RP2_FPGA_CTL0);
+ writel(0, card->bar0 + RP2_FPGA_CTL1);
+
+ rp2_reset_asic(card, 0);
+ if (card->n_ports >= PORTS_PER_ASIC)
+ rp2_reset_asic(card, 1);
+
+ writel(RP2_IRQ_MASK_EN_m, card->bar0 + RP2_IRQ_MASK);
+}
+
+static void rp2_init_port(struct rp2_uart_port *up, const struct firmware *fw)
+{
+ int i;
+
+ writel(RP2_UART_CTL_RESET_CH_m, up->base + RP2_UART_CTL);
+ readl(up->base + RP2_UART_CTL);
+ udelay(1);
+
+ writel(0, up->base + RP2_TXRX_CTL);
+ writel(0, up->base + RP2_UART_CTL);
+ readl(up->base + RP2_UART_CTL);
+ udelay(1);
+
+ rp2_flush_fifos(up);
+
+ for (i = 0; i < min_t(int, fw->size, RP2_UCODE_BYTES); i++)
+ writeb(fw->data[i], up->ucode + i);
+
+ __rp2_uart_set_termios(up, CS8 | CREAD | CLOCAL, 0, DEFAULT_BAUD_DIV);
+ rp2_uart_set_mctrl(&up->port, 0);
+
+ writeb(RP2_RX_FIFO_ena, up->ucode + RP2_RX_FIFO);
+ rp2_rmw(up, RP2_UART_CTL, RP2_UART_CTL_MODE_m,
+ RP2_UART_CTL_XMIT_EN_m | RP2_UART_CTL_MODE_rs232);
+ rp2_rmw_set(up, RP2_TXRX_CTL,
+ RP2_TXRX_CTL_TX_EN_m | RP2_TXRX_CTL_RX_EN_m);
+}
+
+static void rp2_remove_ports(struct rp2_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->initialized_ports; i++)
+ uart_remove_one_port(&rp2_uart_driver, &card->ports[i].port);
+ card->initialized_ports = 0;
+}
+
+static void rp2_fw_cb(const struct firmware *fw, void *context)
+{
+ struct rp2_card *card = context;
+ resource_size_t phys_base;
+ int i, rc = -ENOENT;
+
+ if (!fw) {
+ dev_err(&card->pdev->dev, "cannot find '%s' firmware image\n",
+ RP2_FW_NAME);
+ goto no_fw;
+ }
+
+ phys_base = pci_resource_start(card->pdev, 1);
+
+ for (i = 0; i < card->n_ports; i++) {
+ struct rp2_uart_port *rp = &card->ports[i];
+ struct uart_port *p;
+ int j = (unsigned)i % PORTS_PER_ASIC;
+
+ rp->asic_base = card->bar1;
+ rp->base = card->bar1 + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+ rp->ucode = card->bar1 + RP2_UCODE_BASE + j*RP2_UCODE_SPACING;
+ rp->card = card;
+ rp->idx = j;
+
+ p = &rp->port;
+ p->line = card->minor_start + i;
+ p->dev = &card->pdev->dev;
+ p->type = PORT_RP2;
+ p->iotype = UPIO_MEM32;
+ p->uartclk = UART_CLOCK;
+ p->regshift = 2;
+ p->fifosize = FIFO_SIZE;
+ p->ops = &rp2_uart_ops;
+ p->irq = card->pdev->irq;
+ p->membase = rp->base;
+ p->mapbase = phys_base + RP2_PORT_BASE + j*RP2_PORT_SPACING;
+
+ if (i >= PORTS_PER_ASIC) {
+ rp->asic_base += RP2_ASIC_SPACING;
+ rp->base += RP2_ASIC_SPACING;
+ rp->ucode += RP2_ASIC_SPACING;
+ p->mapbase += RP2_ASIC_SPACING;
+ }
+
+ rp2_init_port(rp, fw);
+ rc = uart_add_one_port(&rp2_uart_driver, p);
+ if (rc) {
+ dev_err(&card->pdev->dev,
+ "error registering port %d: %d\n", i, rc);
+ rp2_remove_ports(card);
+ break;
+ }
+ card->initialized_ports++;
+ }
+
+ release_firmware(fw);
+no_fw:
+ /*
+ * rp2_fw_cb() is called from a workqueue long after rp2_probe()
+ * has already returned success. So if something failed here,
+ * we'll just leave the now-dormant device in place until somebody
+ * unbinds it.
+ */
+ if (rc)
+ dev_warn(&card->pdev->dev, "driver initialization failed\n");
+
+ complete(&card->fw_loaded);
+}
+
+static int rp2_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct rp2_card *card;
+ struct rp2_uart_port *ports;
+ void __iomem * const *bars;
+ int rc;
+
+ card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ pci_set_drvdata(pdev, card);
+ spin_lock_init(&card->card_lock);
+ init_completion(&card->fw_loaded);
+
+ rc = pcim_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pcim_iomap_regions_request_all(pdev, 0x03, DRV_NAME);
+ if (rc)
+ return rc;
+
+ bars = pcim_iomap_table(pdev);
+ card->bar0 = bars[0];
+ card->bar1 = bars[1];
+ card->pdev = pdev;
+
+ rp2_decode_cap(id, &card->n_ports, &card->smpte);
+ dev_info(&pdev->dev, "found new card with %d ports\n", card->n_ports);
+
+ card->minor_start = rp2_alloc_ports(card->n_ports);
+ if (card->minor_start < 0) {
+ dev_err(&pdev->dev,
+ "too many ports (try increasing CONFIG_SERIAL_RP2_NR_UARTS)\n");
+ return -EINVAL;
+ }
+
+ rp2_init_card(card);
+
+ ports = devm_kzalloc(&pdev->dev, sizeof(*ports) * card->n_ports,
+ GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+ card->ports = ports;
+
+ rc = devm_request_irq(&pdev->dev, pdev->irq, rp2_uart_interrupt,
+ IRQF_SHARED, DRV_NAME, card);
+ if (rc)
+ return rc;
+
+ /*
+ * Only catastrophic errors (e.g. ENOMEM) are reported here.
+ * If the FW image is missing, we'll find out in rp2_fw_cb()
+ * and print an error message.
+ */
+ rc = request_firmware_nowait(THIS_MODULE, 1, RP2_FW_NAME, &pdev->dev,
+ GFP_KERNEL, card, rp2_fw_cb);
+ if (rc)
+ return rc;
+ dev_dbg(&pdev->dev, "waiting for firmware blob...\n");
+
+ return 0;
+}
+
+static void rp2_remove(struct pci_dev *pdev)
+{
+ struct rp2_card *card = pci_get_drvdata(pdev);
+
+ wait_for_completion(&card->fw_loaded);
+ rp2_remove_ports(card);
+}
+
+static const struct pci_device_id rp2_pci_tbl[] = {
+
+ /* RocketPort INFINITY cards */
+
+ { RP_ID(0x0040), RP_CAP(8, 0) }, /* INF Octa, RJ45, selectable */
+ { RP_ID(0x0041), RP_CAP(32, 0) }, /* INF 32, ext interface */
+ { RP_ID(0x0042), RP_CAP(8, 0) }, /* INF Octa, ext interface */
+ { RP_ID(0x0043), RP_CAP(16, 0) }, /* INF 16, ext interface */
+ { RP_ID(0x0044), RP_CAP(4, 0) }, /* INF Quad, DB, selectable */
+ { RP_ID(0x0045), RP_CAP(8, 0) }, /* INF Octa, DB, selectable */
+ { RP_ID(0x0046), RP_CAP(4, 0) }, /* INF Quad, ext interface */
+ { RP_ID(0x0047), RP_CAP(4, 0) }, /* INF Quad, RJ45 */
+ { RP_ID(0x004a), RP_CAP(4, 0) }, /* INF Plus, Quad */
+ { RP_ID(0x004b), RP_CAP(8, 0) }, /* INF Plus, Octa */
+ { RP_ID(0x004c), RP_CAP(8, 0) }, /* INF III, Octa */
+ { RP_ID(0x004d), RP_CAP(4, 0) }, /* INF III, Quad */
+ { RP_ID(0x004e), RP_CAP(2, 0) }, /* INF Plus, 2, RS232 */
+ { RP_ID(0x004f), RP_CAP(2, 1) }, /* INF Plus, 2, SMPTE */
+ { RP_ID(0x0050), RP_CAP(4, 0) }, /* INF Plus, Quad, RJ45 */
+ { RP_ID(0x0051), RP_CAP(8, 0) }, /* INF Plus, Octa, RJ45 */
+ { RP_ID(0x0052), RP_CAP(8, 1) }, /* INF Octa, SMPTE */
+
+ /* RocketPort EXPRESS cards */
+
+ { RP_ID(0x0060), RP_CAP(8, 0) }, /* EXP Octa, RJ45, selectable */
+ { RP_ID(0x0061), RP_CAP(32, 0) }, /* EXP 32, ext interface */
+ { RP_ID(0x0062), RP_CAP(8, 0) }, /* EXP Octa, ext interface */
+ { RP_ID(0x0063), RP_CAP(16, 0) }, /* EXP 16, ext interface */
+ { RP_ID(0x0064), RP_CAP(4, 0) }, /* EXP Quad, DB, selectable */
+ { RP_ID(0x0065), RP_CAP(8, 0) }, /* EXP Octa, DB, selectable */
+ { RP_ID(0x0066), RP_CAP(4, 0) }, /* EXP Quad, ext interface */
+ { RP_ID(0x0067), RP_CAP(4, 0) }, /* EXP Quad, RJ45 */
+ { RP_ID(0x0068), RP_CAP(8, 0) }, /* EXP Octa, RJ11 */
+ { RP_ID(0x0072), RP_CAP(8, 1) }, /* EXP Octa, SMPTE */
+ { }
+};
+MODULE_DEVICE_TABLE(pci, rp2_pci_tbl);
+
+static struct pci_driver rp2_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = rp2_pci_tbl,
+ .probe = rp2_probe,
+ .remove = rp2_remove,
+};
+
+static int __init rp2_uart_init(void)
+{
+ int rc;
+
+ rc = uart_register_driver(&rp2_uart_driver);
+ if (rc)
+ return rc;
+
+ rc = pci_register_driver(&rp2_pci_driver);
+ if (rc) {
+ uart_unregister_driver(&rp2_uart_driver);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void __exit rp2_uart_exit(void)
+{
+ pci_unregister_driver(&rp2_pci_driver);
+ uart_unregister_driver(&rp2_uart_driver);
+}
+
+module_init(rp2_uart_init);
+module_exit(rp2_uart_exit);
+
+MODULE_DESCRIPTION("Comtrol RocketPort EXPRESS/INFINITY driver");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(RP2_FW_NAME);
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c
index ef7a21a6a01..753d4525b36 100644
--- a/drivers/tty/serial/sa1100.c
+++ b/drivers/tty/serial/sa1100.c
@@ -29,6 +29,7 @@
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
+#include <linux/platform_data/sa11x0-serial.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -38,7 +39,7 @@
#include <asm/irq.h>
#include <mach/hardware.h>
-#include <asm/mach/serial_sa1100.h>
+#include <mach/irqs.h>
/* We've been assigned a range on the "Low-density serial ports" major */
#define SERIAL_SA1100_MAJOR 204
@@ -187,7 +188,6 @@ static void sa1100_enable_ms(struct uart_port *port)
static void
sa1100_rx_chars(struct sa1100_port *sport)
{
- struct tty_struct *tty = sport->port.state->port.tty;
unsigned int status, ch, flg;
status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
@@ -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));
}
- tty_flip_buffer_push(tty);
+
+ 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)
@@ -636,7 +639,7 @@ static void __init sa1100_init_ports(void)
PPSR |= PPC_TXD1 | PPC_TXD3;
}
-void __devinit sa1100_register_uart_fns(struct sa1100_port_fns *fns)
+void sa1100_register_uart_fns(struct sa1100_port_fns *fns)
{
if (fns->get_mctrl)
sa1100_pops.get_mctrl = fns->get_mctrl;
@@ -644,7 +647,10 @@ void __devinit 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 c55e5fb16fa..c1d3ebdf3b9 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -39,6 +39,7 @@
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
+#include <linux/serial_s3c.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
@@ -46,14 +47,35 @@
#include <asm/irq.h>
-#include <mach/hardware.h>
-#include <mach/map.h>
-
-#include <plat/regs-serial.h>
+#ifdef CONFIG_SAMSUNG_CLOCK
#include <plat/clock.h>
+#endif
#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"
@@ -82,7 +104,7 @@ static inline const char *s3c24xx_serial_portname(struct uart_port *port)
static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
- return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
+ return rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE;
}
/*
@@ -221,10 +243,12 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port;
- struct tty_struct *tty = port->state->port.tty;
unsigned int ufcon, ch, flag, ufstat, uerstat;
+ unsigned long flags;
int max_count = 64;
+ spin_lock_irqsave(&port->lock, flags);
+
while (max_count-- > 0) {
ufcon = rd_regl(port, S3C2410_UFCON);
ufstat = rd_regl(port, S3C2410_UFSTAT);
@@ -248,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;
@@ -268,7 +294,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
dbg("break!\n");
port->icount.brk++;
if (uart_handle_break(port))
- goto ignore_char;
+ goto ignore_char;
}
if (uerstat & S3C2410_UERSTAT_FRAME)
@@ -296,7 +322,9 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
ignore_char:
continue;
}
- tty_flip_buffer_push(tty);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+ tty_flip_buffer_push(&port->state->port);
out:
return IRQ_HANDLED;
@@ -307,8 +335,11 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
int count = 256;
+ spin_lock_irqsave(&port->lock, flags);
+
if (port->x_char) {
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
@@ -336,13 +367,17 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
port->icount.tx++;
}
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
+ spin_unlock(&port->lock);
uart_write_wakeup(port);
+ spin_lock(&port->lock);
+ }
if (uart_circ_empty(xmit))
s3c24xx_serial_stop_tx(port);
out:
+ spin_unlock_irqrestore(&port->lock, flags);
return IRQ_HANDLED;
}
@@ -352,10 +387,8 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
unsigned int pend = rd_regl(port, S3C64XX_UINTP);
- unsigned long flags;
irqreturn_t ret = IRQ_HANDLED;
- spin_lock_irqsave(&port->lock, flags);
if (pend & S3C64XX_UINTM_RXD_MSK) {
ret = s3c24xx_serial_rx_chars(irq, id);
wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);
@@ -364,7 +397,6 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
ret = s3c24xx_serial_tx_chars(irq, id);
wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);
}
- spin_unlock_irqrestore(&port->lock, flags);
return ret;
}
@@ -398,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)
@@ -440,6 +479,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port)
/* Clear pending interrupts and mask all interrupts */
if (s3c24xx_serial_has_interrupt_mask(port)) {
+ free_irq(port->irq, ourport);
+
wr_regl(port, S3C64XX_UINTP, 0xf);
wr_regl(port, S3C64XX_UINTM, 0xf);
}
@@ -450,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;
@@ -459,7 +500,7 @@ static int s3c24xx_serial_startup(struct uart_port *port)
s3c24xx_serial_portname(port), ourport);
if (ret != 0) {
- printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
+ dev_err(port->dev, "cannot get irq %d\n", ourport->rx_irq);
return ret;
}
@@ -473,7 +514,7 @@ static int s3c24xx_serial_startup(struct uart_port *port)
s3c24xx_serial_portname(port), ourport);
if (ret) {
- printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
+ dev_err(port->dev, "cannot get irq %d\n", ourport->tx_irq);
goto err;
}
@@ -496,13 +537,15 @@ 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);
ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
s3c24xx_serial_portname(port), ourport);
if (ret) {
- printk(KERN_ERR "cannot get irq %d\n", port->irq);
+ dev_err(port->dev, "cannot get irq %d\n", port->irq);
return ret;
}
@@ -529,21 +572,21 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
switch (level) {
case 3:
- if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
- clk_disable(ourport->baudclk);
+ if (!IS_ERR(ourport->baudclk))
+ clk_disable_unprepare(ourport->baudclk);
- clk_disable(ourport->clk);
+ clk_disable_unprepare(ourport->clk);
break;
case 0:
- clk_enable(ourport->clk);
+ clk_prepare_enable(ourport->clk);
- if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
- clk_enable(ourport->baudclk);
+ if (!IS_ERR(ourport->baudclk))
+ clk_prepare_enable(ourport->baudclk);
break;
default:
- printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level);
+ dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level);
}
}
@@ -604,7 +647,6 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
char clkname[MAX_CLK_NAME_LENGTH];
int calc_deviation, deviation = (1 << 30) - 1;
- *best_clk = NULL;
clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel :
ourport->info->def_clk_sel;
for (cnt = 0; cnt < info->num_clks; cnt++) {
@@ -613,7 +655,7 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
sprintf(clkname, "clk_uart_baud%d", cnt);
clk = clk_get(ourport->port.dev, clkname);
- if (IS_ERR_OR_NULL(clk))
+ if (IS_ERR(clk))
continue;
rate = clk_get_rate(clk);
@@ -684,7 +726,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
{
struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
struct s3c24xx_uart_port *ourport = to_ourport(port);
- struct clk *clk = NULL;
+ struct clk *clk = ERR_PTR(-EINVAL);
unsigned long flags;
unsigned int baud, quot, clk_sel = 0;
unsigned int ulcon;
@@ -705,7 +747,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
quot = port->custom_divisor;
- if (!clk)
+ if (IS_ERR(clk))
return;
/* check to see if we need to change clock source */
@@ -713,12 +755,12 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
if (ourport->baudclk != clk) {
s3c24xx_serial_setsource(port, clk_sel);
- if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
- clk_disable(ourport->baudclk);
- ourport->baudclk = NULL;
+ if (!IS_ERR(ourport->baudclk)) {
+ clk_disable_unprepare(ourport->baudclk);
+ ourport->baudclk = ERR_PTR(-EINVAL);
}
- clk_enable(clk);
+ clk_prepare_enable(clk);
ourport->baudclk = clk;
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
@@ -762,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;
@@ -780,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)
@@ -877,11 +926,24 @@ s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
static struct console s3c24xx_serial_console;
+static int __init s3c24xx_serial_console_init(void)
+{
+ register_console(&s3c24xx_serial_console);
+ return 0;
+}
+console_initcall(s3c24xx_serial_console_init);
+
#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
#else
#define S3C24XX_SERIAL_CONSOLE NULL
#endif
+#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
+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);
+#endif
+
static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
@@ -900,6 +962,10 @@ static struct uart_ops s3c24xx_serial_ops = {
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
+#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
+ .poll_get_char = s3c24xx_serial_get_poll_char,
+ .poll_put_char = s3c24xx_serial_put_poll_char,
+#endif
};
static struct uart_driver s3c24xx_uart_drv = {
@@ -1013,10 +1079,10 @@ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
* a disturbance in the clock-rate over the change.
*/
- if (IS_ERR(port->clk))
+ if (IS_ERR(port->baudclk))
goto exit;
- if (port->baudclk_rate == clk_get_rate(port->clk))
+ if (port->baudclk_rate == clk_get_rate(port->baudclk))
goto exit;
if (val == CPUFREQ_PRECHANGE) {
@@ -1035,10 +1101,10 @@ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
if (tty == NULL)
goto exit;
- termios = tty->termios;
+ termios = &tty->termios;
if (termios == NULL) {
- printk(KERN_WARNING "%s: no termios?\n", __func__);
+ dev_warn(uport->dev, "%s: no termios?\n", __func__);
goto exit;
}
@@ -1113,14 +1179,19 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
if (res == NULL) {
- printk(KERN_ERR "failed to find memory resource for uart\n");
+ dev_err(port->dev, "failed to find memory resource for uart\n");
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) {
+ dev_err(port->dev, "failed to remap controller address\n");
+ return -EBUSY;
+ }
port->mapbase = res->start;
- port->membase = S3C_VA_UART + (res->start & 0xfffff);
ret = platform_get_irq(platdev, 0);
if (ret < 0)
port->irq = 0;
@@ -1129,12 +1200,24 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
ourport->rx_irq = ret;
ourport->tx_irq = ret + 1;
}
-
+
ret = platform_get_irq(platdev, 1);
if (ret > 0)
ourport->tx_irq = ret;
ourport->clk = clk_get(&platdev->dev, "uart");
+ if (IS_ERR(ourport->clk)) {
+ pr_err("%s: Controller clock not found\n",
+ dev_name(&platdev->dev));
+ return PTR_ERR(ourport->clk);
+ }
+
+ ret = clk_prepare_enable(ourport->clk);
+ if (ret) {
+ pr_err("uart: clock failed to prepare+enable: %d\n", ret);
+ clk_put(ourport->clk);
+ return ret;
+ }
/* Keep all interrupts masked and cleared */
if (s3c24xx_serial_has_interrupt_mask(port)) {
@@ -1143,7 +1226,7 @@ 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);
@@ -1152,6 +1235,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
return 0;
}
+#ifdef CONFIG_SAMSUNG_CLOCK
static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1159,11 +1243,15 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
struct uart_port *port = s3c24xx_dev_to_port(dev);
struct s3c24xx_uart_port *ourport = to_ourport(port);
- return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->baudclk->name);
+ if (IS_ERR(ourport->baudclk))
+ return -EINVAL;
+
+ return snprintf(buf, PAGE_SIZE, "* %s\n",
+ ourport->baudclk->name ?: "(null)");
}
static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
-
+#endif
/* Device driver serial port probe */
@@ -1199,9 +1287,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
return -ENODEV;
}
+ 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) ?
@@ -1216,13 +1305,30 @@ 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)
dev_err(&pdev->dev, "failed to add clock source attr.\n");
+#endif
ret = s3c24xx_serial_cpufreq_register(ourport);
if (ret < 0)
@@ -1234,16 +1340,20 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
return ret;
}
-static int __devexit s3c24xx_serial_remove(struct platform_device *dev)
+static int s3c24xx_serial_remove(struct platform_device *dev)
{
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
if (port) {
s3c24xx_serial_cpufreq_deregister(to_ourport(port));
+#ifdef CONFIG_SAMSUNG_CLOCK
device_remove_file(&dev->dev, &dev_attr_clock_source);
+#endif
uart_remove_one_port(&s3c24xx_uart_drv, port);
}
+ uart_unregister_driver(&s3c24xx_uart_drv);
+
return 0;
}
@@ -1265,9 +1375,9 @@ static int s3c24xx_serial_resume(struct device *dev)
struct s3c24xx_uart_port *ourport = to_ourport(port);
if (port) {
- clk_enable(ourport->clk);
+ clk_prepare_enable(ourport->clk);
s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
- clk_disable(ourport->clk);
+ clk_disable_unprepare(ourport->clk);
uart_resume_port(&s3c24xx_uart_drv, port);
}
@@ -1275,9 +1385,29 @@ static int s3c24xx_serial_resume(struct device *dev)
return 0;
}
+static int s3c24xx_serial_resume_noirq(struct device *dev)
+{
+ struct uart_port *port = s3c24xx_dev_to_port(dev);
+
+ if (port) {
+ /* restore IRQ mask */
+ if (s3c24xx_serial_has_interrupt_mask(port)) {
+ unsigned int uintm = 0xf;
+ if (tx_enabled(port))
+ uintm &= ~S3C64XX_UINTM_TXD_MSK;
+ if (rx_enabled(port))
+ uintm &= ~S3C64XX_UINTM_RXD_MSK;
+ wr_regl(port, S3C64XX_UINTM, uintm);
+ }
+ }
+
+ return 0;
+}
+
static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
.suspend = s3c24xx_serial_suspend,
.resume = s3c24xx_serial_resume,
+ .resume_noirq = s3c24xx_serial_resume_noirq,
};
#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops)
@@ -1311,19 +1441,68 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
}
+static bool
+s3c24xx_port_configured(unsigned int ucon)
+{
+ /* consider the serial port configured if the tx/rx mode set */
+ return (ucon & 0xf) != 0;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int s3c24xx_serial_get_poll_char(struct uart_port *port)
+{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+ unsigned int ufstat;
+
+ ufstat = rd_regl(port, S3C2410_UFSTAT);
+ if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
+ return NO_POLL_CHAR;
+
+ return rd_regb(port, S3C2410_URXH);
+}
+
+static void s3c24xx_serial_put_poll_char(struct uart_port *port,
+ unsigned char c)
+{
+ 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))
+ return;
+
+ while (!s3c24xx_serial_console_txrdy(port, ufcon))
+ cpu_relax();
+ wr_regb(port, S3C2410_UTXH, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
static void
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
{
- unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
+ 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);
}
@@ -1347,9 +1526,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud,
"registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
port, ulcon, ucon, ubrdiv);
- if ((ucon & 0xf) != 0) {
- /* consider the serial port configured if the tx/rx mode set */
-
+ if (s3c24xx_port_configured(ucon)) {
switch (ulcon & S3C2410_LCON_CSMASK) {
case S3C2410_LCON_CS5:
*bits = 5;
@@ -1386,7 +1563,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud,
sprintf(clk_name, "clk_uart_baud%d", clk_sel);
clk = clk_get(port->dev, clk_name);
- if (!IS_ERR(clk) && clk != NULL)
+ if (!IS_ERR(clk))
rate = clk_get_rate(clk);
else
rate = 1;
@@ -1507,7 +1684,7 @@ static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {
#endif
#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \
- defined(CONFIG_CPU_S3C2443)
+ defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2442)
static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {
.info = &(struct s3c24xx_uart_info) {
.name = "Samsung S3C2440 UART",
@@ -1593,8 +1770,7 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
#define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
#endif
-#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \
- defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
+#if defined(CONFIG_ARCH_EXYNOS)
static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
.info = &(struct s3c24xx_uart_info) {
.name = "Samsung Exynos4 UART",
@@ -1649,49 +1825,36 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);
#ifdef CONFIG_OF
static const struct of_device_id s3c24xx_uart_dt_match[] = {
+ { .compatible = "samsung,s3c2410-uart",
+ .data = (void *)S3C2410_SERIAL_DRV_DATA },
+ { .compatible = "samsung,s3c2412-uart",
+ .data = (void *)S3C2412_SERIAL_DRV_DATA },
+ { .compatible = "samsung,s3c2440-uart",
+ .data = (void *)S3C2440_SERIAL_DRV_DATA },
+ { .compatible = "samsung,s3c6400-uart",
+ .data = (void *)S3C6400_SERIAL_DRV_DATA },
+ { .compatible = "samsung,s5pv210-uart",
+ .data = (void *)S5PV210_SERIAL_DRV_DATA },
{ .compatible = "samsung,exynos4210-uart",
.data = (void *)EXYNOS4210_SERIAL_DRV_DATA },
{},
};
MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
-#else
-#define s3c24xx_uart_dt_match NULL
#endif
static struct platform_driver samsung_serial_driver = {
.probe = s3c24xx_serial_probe,
- .remove = __devexit_p(s3c24xx_serial_remove),
+ .remove = s3c24xx_serial_remove,
.id_table = s3c24xx_serial_driver_ids,
.driver = {
.name = "samsung-uart",
.owner = THIS_MODULE,
.pm = SERIAL_SAMSUNG_PM_OPS,
- .of_match_table = s3c24xx_uart_dt_match,
+ .of_match_table = of_match_ptr(s3c24xx_uart_dt_match),
},
};
-/* module initialisation code */
-
-static int __init s3c24xx_serial_modinit(void)
-{
- int ret;
-
- ret = uart_register_driver(&s3c24xx_uart_drv);
- if (ret < 0) {
- printk(KERN_ERR "failed to register UART driver\n");
- return -1;
- }
-
- return platform_driver_register(&samsung_serial_driver);
-}
-
-static void __exit s3c24xx_serial_modexit(void)
-{
- 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 1a4bca3e417..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,22 +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))
-#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG
-
-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 0be8a2f00d0..771f361c47e 100644
--- a/drivers/tty/serial/sb1250-duart.c
+++ b/drivers/tty/serial/sb1250-duart.c
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/major.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
@@ -383,7 +384,7 @@ static void sbd_receive_chars(struct sbd_port *sport)
uart_insert_char(uport, status, M_DUART_OVRUN_ERR, ch, flag);
}
- tty_flip_buffer_push(uport->state->port.tty);
+ tty_flip_buffer_push(&uport->state->port);
}
static void sbd_transmit_chars(struct sbd_port *sport)
@@ -595,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 e0b4b0a30a5..00000000000
--- a/drivers/tty/serial/sc26xx.c
+++ /dev/null
@@ -1,745 +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>
-
-#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 struct tty_struct *receive_chars(struct uart_port *port)
-{
- struct tty_struct *tty = NULL;
- int limit = 10000;
- unsigned char ch;
- char flag;
- u8 status;
-
- if (port->state != NULL) /* Unopened serial console */
- tty = port->state->port.tty;
-
- 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(tty, ch, flag);
- }
- return tty;
-}
-
-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;
- struct tty_struct *tty;
- unsigned long flags;
- u8 isr;
-
- spin_lock_irqsave(&up->port[0].lock, flags);
-
- tty = NULL;
- isr = READ_SC(&up->port[0], ISR);
- if (isr & ISR_TXRDYA)
- transmit_chars(&up->port[0]);
- if (isr & ISR_RXRDYA)
- tty = receive_chars(&up->port[0]);
-
- spin_unlock(&up->port[0].lock);
-
- if (tty)
- tty_flip_buffer_push(tty);
-
- spin_lock(&up->port[1].lock);
-
- tty = NULL;
- if (isr & ISR_TXRDYB)
- transmit_chars(&up->port[1]);
- if (isr & ISR_RXRDYB)
- tty = receive_chars(&up->port[1]);
-
- spin_unlock_irqrestore(&up->port[1].lock, flags);
-
- if (tty)
- tty_flip_buffer_push(tty);
-
- 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 __devinit 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 __devinit 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;
-
- dev_set_drvdata(&dev->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 = dev_get_drvdata(&dev->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;
-
- dev_set_drvdata(&dev->dev, NULL);
- return 0;
-}
-
-static struct platform_driver sc26xx_driver = {
- .probe = sc26xx_probe,
- .remove = __devexit_p(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
new file mode 100644
index 00000000000..e84b6a3bdd1
--- /dev/null
+++ b/drivers/tty/serial/sccnxp.c
@@ -0,0 +1,1031 @@
+/*
+ * NXP (Philips) SCC+++(SCN+++) serial driver
+ *
+ * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#if defined(CONFIG_SERIAL_SCCNXP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/serial-sccnxp.h>
+#include <linux/regulator/consumer.h>
+
+#define SCCNXP_NAME "uart-sccnxp"
+#define SCCNXP_MAJOR 204
+#define SCCNXP_MINOR 205
+
+#define SCCNXP_MR_REG (0x00)
+# define MR0_BAUD_NORMAL (0 << 0)
+# define MR0_BAUD_EXT1 (1 << 0)
+# define MR0_BAUD_EXT2 (5 << 0)
+# define MR0_FIFO (1 << 3)
+# define MR0_TXLVL (1 << 4)
+# define MR1_BITS_5 (0 << 0)
+# define MR1_BITS_6 (1 << 0)
+# define MR1_BITS_7 (2 << 0)
+# define MR1_BITS_8 (3 << 0)
+# define MR1_PAR_EVN (0 << 2)
+# define MR1_PAR_ODD (1 << 2)
+# define MR1_PAR_NO (4 << 2)
+# define MR2_STOP1 (7 << 0)
+# define MR2_STOP2 (0xf << 0)
+#define SCCNXP_SR_REG (0x01)
+#define SCCNXP_CSR_REG SCCNXP_SR_REG
+# define SR_RXRDY (1 << 0)
+# define SR_FULL (1 << 1)
+# define SR_TXRDY (1 << 2)
+# define SR_TXEMT (1 << 3)
+# define SR_OVR (1 << 4)
+# define SR_PE (1 << 5)
+# define SR_FE (1 << 6)
+# define SR_BRK (1 << 7)
+#define SCCNXP_CR_REG (0x02)
+# define CR_RX_ENABLE (1 << 0)
+# define CR_RX_DISABLE (1 << 1)
+# define CR_TX_ENABLE (1 << 2)
+# define CR_TX_DISABLE (1 << 3)
+# define CR_CMD_MRPTR1 (0x01 << 4)
+# define CR_CMD_RX_RESET (0x02 << 4)
+# define CR_CMD_TX_RESET (0x03 << 4)
+# define CR_CMD_STATUS_RESET (0x04 << 4)
+# define CR_CMD_BREAK_RESET (0x05 << 4)
+# define CR_CMD_START_BREAK (0x06 << 4)
+# define CR_CMD_STOP_BREAK (0x07 << 4)
+# define CR_CMD_MRPTR0 (0x0b << 4)
+#define SCCNXP_RHR_REG (0x03)
+#define SCCNXP_THR_REG SCCNXP_RHR_REG
+#define SCCNXP_IPCR_REG (0x04)
+#define SCCNXP_ACR_REG SCCNXP_IPCR_REG
+# define ACR_BAUD0 (0 << 7)
+# define ACR_BAUD1 (1 << 7)
+# define ACR_TIMER_MODE (6 << 4)
+#define SCCNXP_ISR_REG (0x05)
+#define SCCNXP_IMR_REG SCCNXP_ISR_REG
+# define IMR_TXRDY (1 << 0)
+# define IMR_RXRDY (1 << 1)
+# define ISR_TXRDY(x) (1 << ((x * 4) + 0))
+# define ISR_RXRDY(x) (1 << ((x * 4) + 1))
+#define SCCNXP_IPR_REG (0x0d)
+#define SCCNXP_OPCR_REG SCCNXP_IPR_REG
+#define SCCNXP_SOP_REG (0x0e)
+#define SCCNXP_ROP_REG (0x0f)
+
+/* Route helpers */
+#define MCTRL_MASK(sig) (0xf << (sig))
+#define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0)
+#define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0)
+
+#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 {
+ struct uart_driver uart;
+ struct uart_port port[SCCNXP_MAX_UARTS];
+ bool opened[SCCNXP_MAX_UARTS];
+
+ int irq;
+ u8 imr;
+
+ struct sccnxp_chip *chip;
+
+#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
+ struct console console;
+#endif
+
+ spinlock_t lock;
+
+ bool poll;
+ struct timer_list timer;
+
+ struct sccnxp_pdata pdata;
+
+ struct regulator *regulator;
+};
+
+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 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)
+{
+ return readb(port->membase + (reg << port->regshift));
+}
+
+static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
+{
+ writeb(v, port->membase + (reg << port->regshift));
+}
+
+static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
+{
+ return sccnxp_read(port, (port->line << 3) + reg);
+}
+
+static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
+{
+ sccnxp_write(port, (port->line << 3) + reg, v);
+}
+
+static int sccnxp_update_best_err(int a, int b, int *besterr)
+{
+ int err = abs(a - b);
+
+ if ((*besterr < 0) || (*besterr > err)) {
+ *besterr = err;
+ return 0;
+ }
+
+ return 1;
+}
+
+static const struct {
+ u8 csr;
+ u8 acr;
+ u8 mr0;
+ int baud;
+} baud_std[] = {
+ { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, },
+ { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, },
+ { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, },
+ { 2, ACR_BAUD0, MR0_BAUD_NORMAL, 134, },
+ { 3, ACR_BAUD1, MR0_BAUD_NORMAL, 150, },
+ { 3, ACR_BAUD0, MR0_BAUD_NORMAL, 200, },
+ { 4, ACR_BAUD0, MR0_BAUD_NORMAL, 300, },
+ { 0, ACR_BAUD1, MR0_BAUD_EXT1, 450, },
+ { 1, ACR_BAUD0, MR0_BAUD_EXT2, 880, },
+ { 3, ACR_BAUD1, MR0_BAUD_EXT1, 900, },
+ { 5, ACR_BAUD0, MR0_BAUD_NORMAL, 600, },
+ { 7, ACR_BAUD0, MR0_BAUD_NORMAL, 1050, },
+ { 2, ACR_BAUD0, MR0_BAUD_EXT2, 1076, },
+ { 6, ACR_BAUD0, MR0_BAUD_NORMAL, 1200, },
+ { 10, ACR_BAUD1, MR0_BAUD_NORMAL, 1800, },
+ { 7, ACR_BAUD1, MR0_BAUD_NORMAL, 2000, },
+ { 8, ACR_BAUD0, MR0_BAUD_NORMAL, 2400, },
+ { 5, ACR_BAUD1, MR0_BAUD_EXT1, 3600, },
+ { 9, ACR_BAUD0, MR0_BAUD_NORMAL, 4800, },
+ { 10, ACR_BAUD0, MR0_BAUD_NORMAL, 7200, },
+ { 11, ACR_BAUD0, MR0_BAUD_NORMAL, 9600, },
+ { 8, ACR_BAUD0, MR0_BAUD_EXT1, 14400, },
+ { 12, ACR_BAUD1, MR0_BAUD_NORMAL, 19200, },
+ { 9, ACR_BAUD0, MR0_BAUD_EXT1, 28800, },
+ { 12, ACR_BAUD0, MR0_BAUD_NORMAL, 38400, },
+ { 11, ACR_BAUD0, MR0_BAUD_EXT1, 57600, },
+ { 12, ACR_BAUD1, MR0_BAUD_EXT1, 115200, },
+ { 12, ACR_BAUD0, MR0_BAUD_EXT1, 230400, },
+ { 0, 0, 0, 0 }
+};
+
+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 && !(chip->flags & SCCNXP_HAVE_MR0))
+ continue;
+ 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;
+ csr = baud_std[i].csr;
+ mr0 = baud_std[i].mr0;
+ bestbaud = tmp_baud;
+ }
+ }
+
+ if (chip->flags & SCCNXP_HAVE_MR0) {
+ /* Enable FIFO, set half level for TX */
+ mr0 |= MR0_FIFO | MR0_TXLVL;
+ /* Update MR0 */
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR0);
+ sccnxp_port_write(port, SCCNXP_MR_REG, mr0);
+ }
+
+ sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE);
+ sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr);
+
+ if (baud != bestbaud)
+ dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
+ baud, bestbaud);
+
+ return bestbaud;
+}
+
+static void sccnxp_enable_irq(struct uart_port *port, int mask)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+ s->imr |= mask << (port->line * 4);
+ sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
+}
+
+static void sccnxp_disable_irq(struct uart_port *port, int mask)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+ s->imr &= ~(mask << (port->line * 4));
+ sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
+}
+
+static void sccnxp_set_bit(struct uart_port *port, int sig, int state)
+{
+ u8 bitmask;
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+ if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(sig)) {
+ bitmask = 1 << MCTRL_OBIT(s->pdata.mctrl_cfg[port->line], sig);
+ if (state)
+ sccnxp_write(port, SCCNXP_SOP_REG, bitmask);
+ else
+ sccnxp_write(port, SCCNXP_ROP_REG, bitmask);
+ }
+}
+
+static void sccnxp_handle_rx(struct uart_port *port)
+{
+ u8 sr;
+ unsigned int ch, flag;
+
+ for (;;) {
+ sr = sccnxp_port_read(port, SCCNXP_SR_REG);
+ if (!(sr & SR_RXRDY))
+ break;
+ sr &= SR_PE | SR_FE | SR_OVR | SR_BRK;
+
+ ch = sccnxp_port_read(port, SCCNXP_RHR_REG);
+
+ port->icount.rx++;
+ flag = TTY_NORMAL;
+
+ if (unlikely(sr)) {
+ if (sr & SR_BRK) {
+ port->icount.brk++;
+ sccnxp_port_write(port, SCCNXP_CR_REG,
+ CR_CMD_BREAK_RESET);
+ if (uart_handle_break(port))
+ continue;
+ } else if (sr & SR_PE)
+ port->icount.parity++;
+ else if (sr & SR_FE)
+ port->icount.frame++;
+ else if (sr & SR_OVR) {
+ port->icount.overrun++;
+ sccnxp_port_write(port, SCCNXP_CR_REG,
+ CR_CMD_STATUS_RESET);
+ }
+
+ sr &= port->read_status_mask;
+ if (sr & SR_BRK)
+ flag = TTY_BREAK;
+ else if (sr & SR_PE)
+ flag = TTY_PARITY;
+ else if (sr & SR_FE)
+ flag = TTY_FRAME;
+ else if (sr & SR_OVR)
+ flag = TTY_OVERRUN;
+ }
+
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+
+ if (sr & port->ignore_status_mask)
+ continue;
+
+ uart_insert_char(port, sr, SR_OVR, ch, flag);
+ }
+
+ tty_flip_buffer_push(&port->state->port);
+}
+
+static void sccnxp_handle_tx(struct uart_port *port)
+{
+ u8 sr;
+ struct circ_buf *xmit = &port->state->xmit;
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+ if (unlikely(port->x_char)) {
+ sccnxp_port_write(port, SCCNXP_THR_REG, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ /* Disable TX if FIFO is empty */
+ if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) {
+ sccnxp_disable_irq(port, IMR_TXRDY);
+
+ /* Set direction to input */
+ if (s->chip->flags & SCCNXP_HAVE_IO)
+ sccnxp_set_bit(port, DIR_OP, 0);
+ }
+ return;
+ }
+
+ while (!uart_circ_empty(xmit)) {
+ sr = sccnxp_port_read(port, SCCNXP_SR_REG);
+ if (!(sr & SR_TXRDY))
+ break;
+
+ sccnxp_port_write(port, SCCNXP_THR_REG, 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 void sccnxp_handle_events(struct sccnxp_port *s)
+{
+ int i;
+ u8 isr;
+
+ do {
+ isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG);
+ isr &= s->imr;
+ if (!isr)
+ break;
+
+ for (i = 0; i < s->uart.nr; i++) {
+ if (s->opened[i] && (isr & ISR_RXRDY(i)))
+ sccnxp_handle_rx(&s->port[i]);
+ if (s->opened[i] && (isr & ISR_TXRDY(i)))
+ sccnxp_handle_tx(&s->port[i]);
+ }
+ } while (1);
+}
+
+static void sccnxp_timer(unsigned long data)
+{
+ struct sccnxp_port *s = (struct sccnxp_port *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ sccnxp_handle_events(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us));
+}
+
+static irqreturn_t sccnxp_ist(int irq, void *dev_id)
+{
+ struct sccnxp_port *s = (struct sccnxp_port *)dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ sccnxp_handle_events(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void sccnxp_start_tx(struct uart_port *port)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ /* Set direction to output */
+ if (s->chip->flags & SCCNXP_HAVE_IO)
+ sccnxp_set_bit(port, DIR_OP, 1);
+
+ sccnxp_enable_irq(port, IMR_TXRDY);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void sccnxp_stop_tx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static void sccnxp_stop_rx(struct uart_port *port)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static unsigned int sccnxp_tx_empty(struct uart_port *port)
+{
+ u8 val;
+ unsigned long flags;
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+ spin_lock_irqsave(&s->lock, flags);
+ val = sccnxp_port_read(port, SCCNXP_SR_REG);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return (val & SR_TXEMT) ? TIOCSER_TEMT : 0;
+}
+
+static void sccnxp_enable_ms(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+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->chip->flags & SCCNXP_HAVE_IO))
+ return;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR);
+ sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static unsigned int sccnxp_get_mctrl(struct uart_port *port)
+{
+ u8 bitmask, ipr;
+ unsigned long flags;
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
+
+ if (!(s->chip->flags & SCCNXP_HAVE_IO))
+ return mctrl;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG);
+
+ if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DSR_IP)) {
+ bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+ DSR_IP);
+ mctrl &= ~TIOCM_DSR;
+ mctrl |= (ipr & bitmask) ? TIOCM_DSR : 0;
+ }
+ if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(CTS_IP)) {
+ bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+ CTS_IP);
+ mctrl &= ~TIOCM_CTS;
+ mctrl |= (ipr & bitmask) ? TIOCM_CTS : 0;
+ }
+ if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DCD_IP)) {
+ bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+ DCD_IP);
+ mctrl &= ~TIOCM_CAR;
+ mctrl |= (ipr & bitmask) ? TIOCM_CAR : 0;
+ }
+ if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(RNG_IP)) {
+ bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
+ RNG_IP);
+ mctrl &= ~TIOCM_RNG;
+ mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0;
+ }
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return mctrl;
+}
+
+static void sccnxp_break_ctl(struct uart_port *port, int break_state)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ sccnxp_port_write(port, SCCNXP_CR_REG, break_state ?
+ CR_CMD_START_BREAK : CR_CMD_STOP_BREAK);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void sccnxp_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ unsigned long flags;
+ u8 mr1, mr2;
+ int baud;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+
+ /* Disable RX & TX, reset break condition, status and FIFOs */
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET |
+ CR_RX_DISABLE | CR_TX_DISABLE);
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
+
+ /* Word size */
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ mr1 = MR1_BITS_5;
+ break;
+ case CS6:
+ mr1 = MR1_BITS_6;
+ break;
+ case CS7:
+ mr1 = MR1_BITS_7;
+ break;
+ case CS8:
+ default:
+ mr1 = MR1_BITS_8;
+ break;
+ }
+
+ /* Parity */
+ if (termios->c_cflag & PARENB) {
+ if (termios->c_cflag & PARODD)
+ mr1 |= MR1_PAR_ODD;
+ } else
+ mr1 |= MR1_PAR_NO;
+
+ /* Stop bits */
+ mr2 = (termios->c_cflag & CSTOPB) ? MR2_STOP2 : MR2_STOP1;
+
+ /* Update desired format */
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR1);
+ sccnxp_port_write(port, SCCNXP_MR_REG, mr1);
+ sccnxp_port_write(port, SCCNXP_MR_REG, mr2);
+
+ /* Set read status mask */
+ port->read_status_mask = SR_OVR;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= SR_PE | SR_FE;
+ 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->chip->flags & SCCNXP_HAVE_MR0) ?
+ 230400 : 38400);
+ baud = sccnxp_set_baud(port, baud);
+
+ /* Update timeout according to new baud rate */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ /* Report actual baudrate back to core */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
+ /* Enable RX & TX */
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static int sccnxp_startup(struct uart_port *port)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ if (s->chip->flags & SCCNXP_HAVE_IO) {
+ /* Outputs are controlled manually */
+ sccnxp_write(port, SCCNXP_OPCR_REG, 0);
+ }
+
+ /* Reset break condition, status and FIFOs */
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET);
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
+
+ /* Enable RX & TX */
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
+
+ /* Enable RX interrupt */
+ sccnxp_enable_irq(port, IMR_RXRDY);
+
+ s->opened[port->line] = 1;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return 0;
+}
+
+static void sccnxp_shutdown(struct uart_port *port)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ s->opened[port->line] = 0;
+
+ /* Disable interrupts */
+ sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
+
+ /* Disable TX & RX */
+ sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
+
+ /* Leave direction to input */
+ if (s->chip->flags & SCCNXP_HAVE_IO)
+ sccnxp_set_bit(port, DIR_OP, 0);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static const char *sccnxp_type(struct uart_port *port)
+{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
+ return (port->type == PORT_SC26XX) ? s->chip->name : NULL;
+}
+
+static void sccnxp_release_port(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+static int sccnxp_request_port(struct uart_port *port)
+{
+ /* Do nothing */
+ return 0;
+}
+
+static void sccnxp_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_SC26XX;
+}
+
+static int sccnxp_verify_port(struct uart_port *port, struct serial_struct *s)
+{
+ if ((s->type == PORT_UNKNOWN) || (s->type == PORT_SC26XX))
+ return 0;
+ if (s->irq == port->irq)
+ return 0;
+
+ return -EINVAL;
+}
+
+static const struct uart_ops sccnxp_ops = {
+ .tx_empty = sccnxp_tx_empty,
+ .set_mctrl = sccnxp_set_mctrl,
+ .get_mctrl = sccnxp_get_mctrl,
+ .stop_tx = sccnxp_stop_tx,
+ .start_tx = sccnxp_start_tx,
+ .stop_rx = sccnxp_stop_rx,
+ .enable_ms = sccnxp_enable_ms,
+ .break_ctl = sccnxp_break_ctl,
+ .startup = sccnxp_startup,
+ .shutdown = sccnxp_shutdown,
+ .set_termios = sccnxp_set_termios,
+ .type = sccnxp_type,
+ .release_port = sccnxp_release_port,
+ .request_port = sccnxp_request_port,
+ .config_port = sccnxp_config_port,
+ .verify_port = sccnxp_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
+static void sccnxp_console_putchar(struct uart_port *port, int c)
+{
+ int tryes = 100000;
+
+ while (tryes--) {
+ if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXRDY) {
+ sccnxp_port_write(port, SCCNXP_THR_REG, c);
+ break;
+ }
+ barrier();
+ }
+}
+
+static void sccnxp_console_write(struct console *co, const char *c, unsigned n)
+{
+ struct sccnxp_port *s = (struct sccnxp_port *)co->data;
+ struct uart_port *port = &s->port[co->index];
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ uart_console_write(port, c, n, sccnxp_console_putchar);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static int sccnxp_console_setup(struct console *co, char *options)
+{
+ struct sccnxp_port *s = (struct sccnxp_port *)co->data;
+ struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0];
+ int baud = 9600, bits = 8, parity = 'n', flow = 'n';
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+#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);
+ struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
+ int i, ret, uartclk;
+ struct sccnxp_port *s;
+ void __iomem *membase;
+ struct clk *clk;
+
+ 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) {
+ dev_err(&pdev->dev, "Error allocating port structure\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, s);
+
+ spin_lock_init(&s->lock);
+
+ 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)
+ memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
+
+ if (s->pdata.poll_time_us) {
+ dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n",
+ s->pdata.poll_time_us);
+ s->poll = 1;
+ }
+
+ if (!s->poll) {
+ s->irq = platform_get_irq(pdev, 0);
+ if (s->irq < 0) {
+ dev_err(&pdev->dev, "Missing irq resource data\n");
+ ret = -ENXIO;
+ 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;
+ s->uart.cons->write = sccnxp_console_write;
+ s->uart.cons->setup = sccnxp_console_setup;
+ s->uart.cons->flags = CON_PRINTBUFFER;
+ s->uart.cons->index = -1;
+ s->uart.cons->data = s;
+ strcpy(s->uart.cons->name, "ttySC");
+#endif
+ ret = uart_register_driver(&s->uart);
+ if (ret) {
+ dev_err(&pdev->dev, "Registering UART driver failed\n");
+ goto err_out;
+ }
+
+ for (i = 0; i < s->uart.nr; i++) {
+ s->port[i].line = i;
+ s->port[i].dev = &pdev->dev;
+ s->port[i].irq = s->irq;
+ s->port[i].type = PORT_SC26XX;
+ 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 = uartclk;
+ s->port[i].ops = &sccnxp_ops;
+ uart_add_one_port(&s->uart, &s->port[i]);
+ /* Set direction to input */
+ if (s->chip->flags & SCCNXP_HAVE_IO)
+ sccnxp_set_bit(&s->port[i], DIR_OP, 0);
+ }
+
+ /* Disable interrupts */
+ s->imr = 0;
+ sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0);
+
+ if (!s->poll) {
+ ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL,
+ sccnxp_ist,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev_name(&pdev->dev), s);
+ if (!ret)
+ return 0;
+
+ dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq);
+ } else {
+ init_timer(&s->timer);
+ setup_timer(&s->timer, sccnxp_timer, (unsigned long)s);
+ mod_timer(&s->timer, jiffies +
+ usecs_to_jiffies(s->pdata.poll_time_us));
+ return 0;
+ }
+
+ uart_unregister_driver(&s->uart);
+err_out:
+ if (!IS_ERR(s->regulator))
+ return regulator_disable(s->regulator);
+
+ return ret;
+}
+
+static int sccnxp_remove(struct platform_device *pdev)
+{
+ int i;
+ struct sccnxp_port *s = platform_get_drvdata(pdev);
+
+ if (!s->poll)
+ devm_free_irq(&pdev->dev, s->irq, s);
+ else
+ del_timer_sync(&s->timer);
+
+ for (i = 0; i < s->uart.nr; i++)
+ uart_remove_one_port(&s->uart, &s->port[i]);
+
+ uart_unregister_driver(&s->uart);
+
+ if (!IS_ERR(s->regulator))
+ return regulator_disable(s->regulator);
+
+ return 0;
+}
+
+static struct platform_driver sccnxp_uart_driver = {
+ .driver = {
+ .name = SCCNXP_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = sccnxp_probe,
+ .remove = sccnxp_remove,
+ .id_table = sccnxp_id_table,
+};
+module_platform_driver(sccnxp_uart_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("SCCNXP serial driver");
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
new file mode 100644
index 00000000000..d5c2a287b7e
--- /dev/null
+++ b/drivers/tty/serial/serial-tegra.c
@@ -0,0 +1,1402 @@
+/*
+ * serial_tegra.c
+ *
+ * High-speed serial driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#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>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.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)
+
+#define TEGRA_UART_RX_DMA_BUFFER_SIZE 4096
+#define TEGRA_UART_LSR_TXFIFO_FULL 0x100
+#define TEGRA_UART_IER_EORD 0x20
+#define TEGRA_UART_MCR_RTS_EN 0x40
+#define TEGRA_UART_MCR_CTS_EN 0x20
+#define TEGRA_UART_LSR_ANY (UART_LSR_OE | UART_LSR_BI | \
+ UART_LSR_PE | UART_LSR_FE)
+#define TEGRA_UART_IRDA_CSR 0x08
+#define TEGRA_UART_SIR_ENABLED 0x80
+
+#define TEGRA_UART_TX_PIO 1
+#define TEGRA_UART_TX_DMA 2
+#define TEGRA_UART_MIN_DMA 16
+#define TEGRA_UART_FIFO_SIZE 32
+
+/*
+ * Tx fifo trigger level setting in tegra uart is in
+ * reverse way then conventional uart.
+ */
+#define TEGRA_UART_TX_TRIG_16B 0x00
+#define TEGRA_UART_TX_TRIG_8B 0x10
+#define TEGRA_UART_TX_TRIG_4B 0x20
+#define TEGRA_UART_TX_TRIG_1B 0x30
+
+#define TEGRA_UART_MAXIMUM 5
+
+/* Default UART setting when started: 115200 no parity, stop, 8 data bits */
+#define TEGRA_UART_DEFAULT_BAUD 115200
+#define TEGRA_UART_DEFAULT_LSR UART_LCR_WLEN8
+
+/* Tx transfer mode */
+#define TEGRA_TX_PIO 1
+#define TEGRA_TX_DMA 2
+
+/**
+ * tegra_uart_chip_data: SOC specific data.
+ *
+ * @tx_fifo_full_status: Status flag available for checking tx fifo full.
+ * @allow_txfifo_reset_fifo_mode: allow_tx fifo reset with fifo mode or not.
+ * Tegra30 does not allow this.
+ * @support_clk_src_div: Clock source support the clock divider.
+ */
+struct tegra_uart_chip_data {
+ bool tx_fifo_full_status;
+ bool allow_txfifo_reset_fifo_mode;
+ bool support_clk_src_div;
+};
+
+struct tegra_uart_port {
+ struct uart_port uport;
+ const struct tegra_uart_chip_data *cdata;
+
+ struct clk *uart_clk;
+ struct reset_control *rst;
+ unsigned int current_baud;
+
+ /* Register shadow */
+ unsigned long fcr_shadow;
+ unsigned long mcr_shadow;
+ unsigned long lcr_shadow;
+ unsigned long ier_shadow;
+ bool rts_active;
+
+ int tx_in_progress;
+ unsigned int tx_bytes;
+
+ bool enable_modem_interrupt;
+
+ bool rx_timeout;
+ int rx_in_progress;
+ int symb_bit;
+
+ struct dma_chan *rx_dma_chan;
+ struct dma_chan *tx_dma_chan;
+ dma_addr_t rx_dma_buf_phys;
+ dma_addr_t tx_dma_buf_phys;
+ unsigned char *rx_dma_buf_virt;
+ unsigned char *tx_dma_buf_virt;
+ struct dma_async_tx_descriptor *tx_dma_desc;
+ struct dma_async_tx_descriptor *rx_dma_desc;
+ dma_cookie_t tx_cookie;
+ dma_cookie_t rx_cookie;
+ int tx_bytes_requested;
+ int rx_bytes_requested;
+};
+
+static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
+static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup);
+
+static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup,
+ unsigned long reg)
+{
+ return readl(tup->uport.membase + (reg << tup->uport.regshift));
+}
+
+static inline void tegra_uart_write(struct tegra_uart_port *tup, unsigned val,
+ unsigned long reg)
+{
+ writel(val, tup->uport.membase + (reg << tup->uport.regshift));
+}
+
+static inline struct tegra_uart_port *to_tegra_uport(struct uart_port *u)
+{
+ return container_of(u, struct tegra_uart_port, uport);
+}
+
+static unsigned int tegra_uart_get_mctrl(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ /*
+ * RI - Ring detector is active
+ * CD/DCD/CAR - Carrier detect is always active. For some reason
+ * linux has different names for carrier detect.
+ * DSR - Data Set ready is active as the hardware doesn't support it.
+ * Don't know if the linux support this yet?
+ * CTS - Clear to send. Always set to active, as the hardware handles
+ * CTS automatically.
+ */
+ if (tup->enable_modem_interrupt)
+ return TIOCM_RI | TIOCM_CD | TIOCM_DSR | TIOCM_CTS;
+ return TIOCM_CTS;
+}
+
+static void set_rts(struct tegra_uart_port *tup, bool active)
+{
+ unsigned long mcr;
+
+ mcr = tup->mcr_shadow;
+ if (active)
+ mcr |= TEGRA_UART_MCR_RTS_EN;
+ else
+ mcr &= ~TEGRA_UART_MCR_RTS_EN;
+ if (mcr != tup->mcr_shadow) {
+ tegra_uart_write(tup, mcr, UART_MCR);
+ tup->mcr_shadow = mcr;
+ }
+ return;
+}
+
+static void set_dtr(struct tegra_uart_port *tup, bool active)
+{
+ unsigned long mcr;
+
+ mcr = tup->mcr_shadow;
+ if (active)
+ mcr |= UART_MCR_DTR;
+ else
+ mcr &= ~UART_MCR_DTR;
+ if (mcr != tup->mcr_shadow) {
+ tegra_uart_write(tup, mcr, UART_MCR);
+ tup->mcr_shadow = mcr;
+ }
+ return;
+}
+
+static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned long mcr;
+ int dtr_enable;
+
+ mcr = tup->mcr_shadow;
+ tup->rts_active = !!(mctrl & TIOCM_RTS);
+ set_rts(tup, tup->rts_active);
+
+ dtr_enable = !!(mctrl & TIOCM_DTR);
+ set_dtr(tup, dtr_enable);
+ return;
+}
+
+static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned long lcr;
+
+ lcr = tup->lcr_shadow;
+ if (break_ctl)
+ lcr |= UART_LCR_SBC;
+ else
+ lcr &= ~UART_LCR_SBC;
+ tegra_uart_write(tup, lcr, UART_LCR);
+ tup->lcr_shadow = lcr;
+}
+
+/* Wait for a symbol-time. */
+static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup,
+ unsigned int syms)
+{
+ if (tup->current_baud)
+ udelay(DIV_ROUND_UP(syms * tup->symb_bit * 1000000,
+ tup->current_baud));
+}
+
+static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
+{
+ unsigned long fcr = tup->fcr_shadow;
+
+ if (tup->cdata->allow_txfifo_reset_fifo_mode) {
+ fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ tegra_uart_write(tup, fcr, UART_FCR);
+ } else {
+ fcr &= ~UART_FCR_ENABLE_FIFO;
+ tegra_uart_write(tup, fcr, UART_FCR);
+ udelay(60);
+ fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ tegra_uart_write(tup, fcr, UART_FCR);
+ fcr |= UART_FCR_ENABLE_FIFO;
+ tegra_uart_write(tup, fcr, UART_FCR);
+ }
+
+ /* Dummy read to ensure the write is posted */
+ tegra_uart_read(tup, UART_SCR);
+
+ /* Wait for the flush to propagate. */
+ tegra_uart_wait_sym_time(tup, 1);
+}
+
+static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
+{
+ unsigned long rate;
+ unsigned int divisor;
+ unsigned long lcr;
+ int ret;
+
+ if (tup->current_baud == baud)
+ return 0;
+
+ if (tup->cdata->support_clk_src_div) {
+ rate = baud * 16;
+ ret = clk_set_rate(tup->uart_clk, rate);
+ if (ret < 0) {
+ dev_err(tup->uport.dev,
+ "clk_set_rate() failed for rate %lu\n", rate);
+ return ret;
+ }
+ divisor = 1;
+ } else {
+ rate = clk_get_rate(tup->uart_clk);
+ divisor = DIV_ROUND_CLOSEST(rate, baud * 16);
+ }
+
+ lcr = tup->lcr_shadow;
+ lcr |= UART_LCR_DLAB;
+ tegra_uart_write(tup, lcr, UART_LCR);
+
+ tegra_uart_write(tup, divisor & 0xFF, UART_TX);
+ tegra_uart_write(tup, ((divisor >> 8) & 0xFF), UART_IER);
+
+ lcr &= ~UART_LCR_DLAB;
+ tegra_uart_write(tup, lcr, UART_LCR);
+
+ /* Dummy read to ensure the write is posted */
+ tegra_uart_read(tup, UART_SCR);
+
+ tup->current_baud = baud;
+
+ /* wait two character intervals at new rate */
+ tegra_uart_wait_sym_time(tup, 2);
+ return 0;
+}
+
+static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup,
+ unsigned long lsr)
+{
+ char flag = TTY_NORMAL;
+
+ if (unlikely(lsr & TEGRA_UART_LSR_ANY)) {
+ if (lsr & UART_LSR_OE) {
+ /* Overrrun error */
+ flag |= TTY_OVERRUN;
+ tup->uport.icount.overrun++;
+ dev_err(tup->uport.dev, "Got overrun errors\n");
+ } else if (lsr & UART_LSR_PE) {
+ /* Parity error */
+ flag |= TTY_PARITY;
+ tup->uport.icount.parity++;
+ dev_err(tup->uport.dev, "Got Parity errors\n");
+ } else if (lsr & UART_LSR_FE) {
+ flag |= TTY_FRAME;
+ tup->uport.icount.frame++;
+ dev_err(tup->uport.dev, "Got frame errors\n");
+ } else if (lsr & UART_LSR_BI) {
+ dev_err(tup->uport.dev, "Got Break\n");
+ tup->uport.icount.brk++;
+ /* If FIFO read error without any data, reset Rx FIFO */
+ if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE))
+ tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR);
+ }
+ }
+ return flag;
+}
+
+static int tegra_uart_request_port(struct uart_port *u)
+{
+ return 0;
+}
+
+static void tegra_uart_release_port(struct uart_port *u)
+{
+ /* Nothing to do here */
+}
+
+static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes)
+{
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ int i;
+
+ for (i = 0; i < max_bytes; i++) {
+ BUG_ON(uart_circ_empty(xmit));
+ if (tup->cdata->tx_fifo_full_status) {
+ unsigned long lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL))
+ break;
+ }
+ tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ tup->uport.icount.tx++;
+ }
+}
+
+static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup,
+ unsigned int bytes)
+{
+ if (bytes > TEGRA_UART_MIN_DMA)
+ bytes = TEGRA_UART_MIN_DMA;
+
+ tup->tx_in_progress = TEGRA_UART_TX_PIO;
+ tup->tx_bytes = bytes;
+ tup->ier_shadow |= UART_IER_THRI;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+}
+
+static void tegra_uart_tx_dma_complete(void *args)
+{
+ struct tegra_uart_port *tup = args;
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ struct dma_tx_state state;
+ unsigned long flags;
+ int count;
+
+ dmaengine_tx_status(tup->tx_dma_chan, tup->rx_cookie, &state);
+ count = tup->tx_bytes_requested - state.residue;
+ async_tx_ack(tup->tx_dma_desc);
+ spin_lock_irqsave(&tup->uport.lock, flags);
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ tup->tx_in_progress = 0;
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&tup->uport);
+ tegra_uart_start_next_tx(tup);
+ spin_unlock_irqrestore(&tup->uport.lock, flags);
+}
+
+static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup,
+ unsigned long count)
+{
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ dma_addr_t tx_phys_addr;
+
+ dma_sync_single_for_device(tup->uport.dev, tup->tx_dma_buf_phys,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ tup->tx_bytes = count & ~(0xF);
+ tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail;
+ tup->tx_dma_desc = dmaengine_prep_slave_single(tup->tx_dma_chan,
+ tx_phys_addr, tup->tx_bytes, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT);
+ if (!tup->tx_dma_desc) {
+ dev_err(tup->uport.dev, "Not able to get desc for Tx\n");
+ return -EIO;
+ }
+
+ tup->tx_dma_desc->callback = tegra_uart_tx_dma_complete;
+ tup->tx_dma_desc->callback_param = tup;
+ tup->tx_in_progress = TEGRA_UART_TX_DMA;
+ tup->tx_bytes_requested = tup->tx_bytes;
+ tup->tx_cookie = dmaengine_submit(tup->tx_dma_desc);
+ dma_async_issue_pending(tup->tx_dma_chan);
+ return 0;
+}
+
+static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
+{
+ unsigned long tail;
+ unsigned long count;
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+
+ tail = (unsigned long)&xmit->buf[xmit->tail];
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ if (!count)
+ return;
+
+ if (count < TEGRA_UART_MIN_DMA)
+ tegra_uart_start_pio_tx(tup, count);
+ else if (BYTES_TO_ALIGN(tail) > 0)
+ tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail));
+ else
+ tegra_uart_start_tx_dma(tup, count);
+}
+
+/* Called by serial core driver with u->lock taken. */
+static void tegra_uart_start_tx(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct circ_buf *xmit = &u->state->xmit;
+
+ if (!uart_circ_empty(xmit) && !tup->tx_in_progress)
+ tegra_uart_start_next_tx(tup);
+}
+
+static unsigned int tegra_uart_tx_empty(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+ if (!tup->tx_in_progress) {
+ unsigned long lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
+ ret = TIOCSER_TEMT;
+ }
+ spin_unlock_irqrestore(&u->lock, flags);
+ return ret;
+}
+
+static void tegra_uart_stop_tx(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+ struct dma_tx_state state;
+ int count;
+
+ dmaengine_terminate_all(tup->tx_dma_chan);
+ dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state);
+ count = tup->tx_bytes_requested - state.residue;
+ async_tx_ack(tup->tx_dma_desc);
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ tup->tx_in_progress = 0;
+ return;
+}
+
+static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup)
+{
+ struct circ_buf *xmit = &tup->uport.state->xmit;
+
+ tegra_uart_fill_tx_fifo(tup, tup->tx_bytes);
+ tup->tx_in_progress = 0;
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&tup->uport);
+ tegra_uart_start_next_tx(tup);
+ return;
+}
+
+static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup,
+ struct tty_port *tty)
+{
+ do {
+ char flag = TTY_NORMAL;
+ unsigned long lsr = 0;
+ unsigned char ch;
+
+ lsr = tegra_uart_read(tup, UART_LSR);
+ if (!(lsr & UART_LSR_DR))
+ break;
+
+ flag = tegra_uart_decode_rx_error(tup, lsr);
+ ch = (unsigned char) tegra_uart_read(tup, UART_RX);
+ tup->uport.icount.rx++;
+
+ if (!uart_handle_sysrq_char(&tup->uport, ch) && tty)
+ tty_insert_flip_char(tty, ch, flag);
+ } while (1);
+
+ return;
+}
+
+static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup,
+ struct tty_port *tty, int count)
+{
+ int copied;
+
+ tup->uport.icount.rx += count;
+ if (!tty) {
+ dev_err(tup->uport.dev, "No tty port\n");
+ return;
+ }
+ dma_sync_single_for_cpu(tup->uport.dev, tup->rx_dma_buf_phys,
+ TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+ copied = tty_insert_flip_string(tty,
+ ((unsigned char *)(tup->rx_dma_buf_virt)), count);
+ if (copied != count) {
+ WARN_ON(1);
+ dev_err(tup->uport.dev, "RxData copy to tty layer failed\n");
+ }
+ dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys,
+ TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+}
+
+static void tegra_uart_rx_dma_complete(void *args)
+{
+ struct tegra_uart_port *tup = args;
+ struct uart_port *u = &tup->uport;
+ int count = tup->rx_bytes_requested;
+ struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ struct tty_port *port = &u->state->port;
+ unsigned long flags;
+
+ async_tx_ack(tup->rx_dma_desc);
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* Deactivate flow control to stop sender */
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ /* If we are here, DMA is stopped */
+ if (count)
+ tegra_uart_copy_rx_to_tty(tup, port, count);
+
+ 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);
+
+ /* Activate flow control to start transfer */
+ if (tup->rts_active)
+ set_rts(tup, true);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+}
+
+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 */
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ dmaengine_terminate_all(tup->rx_dma_chan);
+ dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+ count = tup->rx_bytes_requested - state.residue;
+
+ /* If we are here, DMA is stopped */
+ if (count)
+ tegra_uart_copy_rx_to_tty(tup, port, count);
+
+ 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);
+
+ if (tup->rts_active)
+ set_rts(tup, true);
+}
+
+static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup)
+{
+ unsigned int count = TEGRA_UART_RX_DMA_BUFFER_SIZE;
+
+ tup->rx_dma_desc = dmaengine_prep_slave_single(tup->rx_dma_chan,
+ tup->rx_dma_buf_phys, count, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!tup->rx_dma_desc) {
+ dev_err(tup->uport.dev, "Not able to get desc for Rx\n");
+ return -EIO;
+ }
+
+ tup->rx_dma_desc->callback = tegra_uart_rx_dma_complete;
+ tup->rx_dma_desc->callback_param = tup;
+ dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys,
+ count, DMA_TO_DEVICE);
+ tup->rx_bytes_requested = count;
+ tup->rx_cookie = dmaengine_submit(tup->rx_dma_desc);
+ dma_async_issue_pending(tup->rx_dma_chan);
+ return 0;
+}
+
+static void tegra_uart_handle_modem_signal_change(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned long msr;
+
+ msr = tegra_uart_read(tup, UART_MSR);
+ if (!(msr & UART_MSR_ANY_DELTA))
+ return;
+
+ if (msr & UART_MSR_TERI)
+ tup->uport.icount.rng++;
+ if (msr & UART_MSR_DDSR)
+ tup->uport.icount.dsr++;
+ /* We may only get DDCD when HW init and reset */
+ if (msr & UART_MSR_DDCD)
+ uart_handle_dcd_change(&tup->uport, msr & UART_MSR_DCD);
+ /* Will start/stop_tx accordingly */
+ if (msr & UART_MSR_DCTS)
+ uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS);
+ return;
+}
+
+static irqreturn_t tegra_uart_isr(int irq, void *data)
+{
+ struct tegra_uart_port *tup = data;
+ struct uart_port *u = &tup->uport;
+ unsigned long iir;
+ unsigned long ier;
+ bool is_rx_int = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+ while (1) {
+ iir = tegra_uart_read(tup, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ if (is_rx_int) {
+ tegra_uart_handle_rx_dma(tup, &flags);
+ if (tup->rx_in_progress) {
+ ier = tup->ier_shadow;
+ ier |= (UART_IER_RLSI | UART_IER_RTOIE |
+ TEGRA_UART_IER_EORD);
+ tup->ier_shadow = ier;
+ tegra_uart_write(tup, ier, UART_IER);
+ }
+ }
+ spin_unlock_irqrestore(&u->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ switch ((iir >> 1) & 0x7) {
+ case 0: /* Modem signal change interrupt */
+ tegra_uart_handle_modem_signal_change(u);
+ break;
+
+ case 1: /* Transmit interrupt only triggered when using PIO */
+ tup->ier_shadow &= ~UART_IER_THRI;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ tegra_uart_handle_tx_pio(tup);
+ break;
+
+ case 4: /* End of data */
+ case 6: /* Rx timeout */
+ case 2: /* Receive */
+ if (!is_rx_int) {
+ is_rx_int = true;
+ /* Disable Rx interrupts */
+ ier = tup->ier_shadow;
+ ier |= UART_IER_RDI;
+ tegra_uart_write(tup, ier, UART_IER);
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI |
+ UART_IER_RTOIE | TEGRA_UART_IER_EORD);
+ tup->ier_shadow = ier;
+ tegra_uart_write(tup, ier, UART_IER);
+ }
+ break;
+
+ case 3: /* Receive error */
+ tegra_uart_decode_rx_error(tup,
+ tegra_uart_read(tup, UART_LSR));
+ break;
+
+ case 5: /* break nothing to handle */
+ case 7: /* break nothing to handle */
+ break;
+ }
+ }
+}
+
+static void tegra_uart_stop_rx(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct tty_struct *tty;
+ struct tty_port *port = &u->state->port;
+ struct dma_tx_state state;
+ unsigned long ier;
+ int count;
+
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ 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;
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE |
+ TEGRA_UART_IER_EORD);
+ tup->ier_shadow = ier;
+ tegra_uart_write(tup, ier, UART_IER);
+ tup->rx_in_progress = 0;
+ if (tup->rx_dma_chan) {
+ dmaengine_terminate_all(tup->rx_dma_chan);
+ dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+ async_tx_ack(tup->rx_dma_desc);
+ count = tup->rx_bytes_requested - state.residue;
+ tegra_uart_copy_rx_to_tty(tup, port, count);
+ tegra_uart_handle_rx_pio(tup, port);
+ } else {
+ tegra_uart_handle_rx_pio(tup, port);
+ }
+ if (tty) {
+ tty_flip_buffer_push(port);
+ tty_kref_put(tty);
+ }
+ return;
+}
+
+static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
+{
+ unsigned long flags;
+ unsigned long char_time = DIV_ROUND_UP(10000000, tup->current_baud);
+ unsigned long fifo_empty_time = tup->uport.fifosize * char_time;
+ unsigned long wait_time;
+ unsigned long lsr;
+ unsigned long msr;
+ unsigned long mcr;
+
+ /* Disable interrupts */
+ tegra_uart_write(tup, 0, UART_IER);
+
+ lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) {
+ msr = tegra_uart_read(tup, UART_MSR);
+ mcr = tegra_uart_read(tup, UART_MCR);
+ if ((mcr & TEGRA_UART_MCR_CTS_EN) && (msr & UART_MSR_CTS))
+ dev_err(tup->uport.dev,
+ "Tx Fifo not empty, CTS disabled, waiting\n");
+
+ /* Wait for Tx fifo to be empty */
+ while ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) {
+ wait_time = min(fifo_empty_time, 100lu);
+ udelay(wait_time);
+ fifo_empty_time -= wait_time;
+ if (!fifo_empty_time) {
+ msr = tegra_uart_read(tup, UART_MSR);
+ mcr = tegra_uart_read(tup, UART_MCR);
+ if ((mcr & TEGRA_UART_MCR_CTS_EN) &&
+ (msr & UART_MSR_CTS))
+ dev_err(tup->uport.dev,
+ "Slave not ready\n");
+ break;
+ }
+ lsr = tegra_uart_read(tup, UART_LSR);
+ }
+ }
+
+ spin_lock_irqsave(&tup->uport.lock, flags);
+ /* Reset the Rx and Tx FIFOs */
+ tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR);
+ tup->current_baud = 0;
+ spin_unlock_irqrestore(&tup->uport.lock, flags);
+
+ clk_disable_unprepare(tup->uart_clk);
+}
+
+static int tegra_uart_hw_init(struct tegra_uart_port *tup)
+{
+ int ret;
+
+ tup->fcr_shadow = 0;
+ tup->mcr_shadow = 0;
+ tup->lcr_shadow = 0;
+ tup->ier_shadow = 0;
+ tup->current_baud = 0;
+
+ clk_prepare_enable(tup->uart_clk);
+
+ /* Reset the UART controller to clear all previous status.*/
+ reset_control_assert(tup->rst);
+ udelay(10);
+ reset_control_deassert(tup->rst);
+
+ tup->rx_in_progress = 0;
+ tup->tx_in_progress = 0;
+
+ /*
+ * Set the trigger level
+ *
+ * For PIO mode:
+ *
+ * For receive, this will interrupt the CPU after that many number of
+ * bytes are received, for the remaining bytes the receive timeout
+ * interrupt is received. Rx high watermark is set to 4.
+ *
+ * For transmit, if the trasnmit interrupt is enabled, this will
+ * interrupt the CPU when the number of entries in the FIFO reaches the
+ * low watermark. Tx low watermark is set to 16 bytes.
+ *
+ * For DMA mode:
+ *
+ * Set the Tx trigger to 16. This should match the DMA burst size that
+ * programmed in the DMA registers.
+ */
+ tup->fcr_shadow = UART_FCR_ENABLE_FIFO;
+ tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+ tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B;
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
+
+ /*
+ * Initialize the UART with default configuration
+ * (115200, N, 8, 1) so that the receive DMA buffer may be
+ * enqueued
+ */
+ tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
+ tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
+ tup->fcr_shadow |= UART_FCR_DMA_SELECT;
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
+
+ ret = tegra_uart_start_rx_dma(tup);
+ if (ret < 0) {
+ dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
+ return ret;
+ }
+ tup->rx_in_progress = 1;
+
+ /*
+ * Enable IE_RXS for the receive status interrupts like line errros.
+ * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd.
+ *
+ * If using DMA mode, enable EORD instead of receive interrupt which
+ * will interrupt after the UART is done with the receive instead of
+ * the interrupt when the FIFO "threshold" is reached.
+ *
+ * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when
+ * the DATA is sitting in the FIFO and couldn't be transferred to the
+ * DMA as the DMA size alignment(4 bytes) is not met. EORD will be
+ * triggered when there is a pause of the incomming data stream for 4
+ * characters long.
+ *
+ * For pauses in the data which is not aligned to 4 bytes, we get
+ * both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first
+ * then the EORD.
+ */
+ tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ return 0;
+}
+
+static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
+ bool dma_to_memory)
+{
+ struct dma_chan *dma_chan;
+ unsigned char *dma_buf;
+ dma_addr_t dma_phys;
+ int ret;
+ struct dma_slave_config dma_sconfig;
+
+ 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 alloc failed: %d\n", ret);
+ return ret;
+ }
+
+ if (dma_to_memory) {
+ dma_buf = dma_alloc_coherent(tup->uport.dev,
+ TEGRA_UART_RX_DMA_BUFFER_SIZE,
+ &dma_phys, GFP_KERNEL);
+ if (!dma_buf) {
+ dev_err(tup->uport.dev,
+ "Not able to allocate the dma buffer\n");
+ dma_release_channel(dma_chan);
+ return -ENOMEM;
+ }
+ } else {
+ dma_phys = dma_map_single(tup->uport.dev,
+ tup->uport.state->xmit.buf, UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ dma_buf = tup->uport.state->xmit.buf;
+ }
+
+ if (dma_to_memory) {
+ dma_sconfig.src_addr = tup->uport.mapbase;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_maxburst = 4;
+ } else {
+ dma_sconfig.dst_addr = tup->uport.mapbase;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_maxburst = 16;
+ }
+
+ ret = dmaengine_slave_config(dma_chan, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(tup->uport.dev,
+ "Dma slave config failed, err = %d\n", ret);
+ goto scrub;
+ }
+
+ if (dma_to_memory) {
+ tup->rx_dma_chan = dma_chan;
+ tup->rx_dma_buf_virt = dma_buf;
+ tup->rx_dma_buf_phys = dma_phys;
+ } else {
+ tup->tx_dma_chan = dma_chan;
+ tup->tx_dma_buf_virt = dma_buf;
+ tup->tx_dma_buf_phys = dma_phys;
+ }
+ return 0;
+
+scrub:
+ dma_release_channel(dma_chan);
+ return ret;
+}
+
+static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup,
+ bool dma_to_memory)
+{
+ struct dma_chan *dma_chan;
+
+ if (dma_to_memory) {
+ dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE,
+ tup->rx_dma_buf_virt, tup->rx_dma_buf_phys);
+ dma_chan = tup->rx_dma_chan;
+ tup->rx_dma_chan = NULL;
+ tup->rx_dma_buf_phys = 0;
+ tup->rx_dma_buf_virt = NULL;
+ } else {
+ dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ dma_chan = tup->tx_dma_chan;
+ tup->tx_dma_chan = NULL;
+ tup->tx_dma_buf_phys = 0;
+ tup->tx_dma_buf_virt = NULL;
+ }
+ dma_release_channel(dma_chan);
+}
+
+static int tegra_uart_startup(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ int ret;
+
+ ret = tegra_uart_dma_channel_allocate(tup, false);
+ if (ret < 0) {
+ dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = tegra_uart_dma_channel_allocate(tup, true);
+ if (ret < 0) {
+ dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret);
+ goto fail_rx_dma;
+ }
+
+ ret = tegra_uart_hw_init(tup);
+ if (ret < 0) {
+ dev_err(u->dev, "Uart HW init failed, err = %d\n", ret);
+ goto fail_hw_init;
+ }
+
+ 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);
+ goto fail_hw_init;
+ }
+ return 0;
+
+fail_hw_init:
+ tegra_uart_dma_channel_free(tup, true);
+fail_rx_dma:
+ tegra_uart_dma_channel_free(tup, false);
+ return ret;
+}
+
+static void tegra_uart_shutdown(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ tegra_uart_hw_deinit(tup);
+
+ tup->rx_in_progress = 0;
+ tup->tx_in_progress = 0;
+
+ tegra_uart_dma_channel_free(tup, true);
+ tegra_uart_dma_channel_free(tup, false);
+ free_irq(u->irq, tup);
+}
+
+static void tegra_uart_enable_ms(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ if (tup->enable_modem_interrupt) {
+ tup->ier_shadow |= UART_IER_MSI;
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ }
+}
+
+static void tegra_uart_set_termios(struct uart_port *u,
+ struct ktermios *termios, struct ktermios *oldtermios)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+ unsigned int baud;
+ unsigned long flags;
+ unsigned int lcr;
+ int symb_bit = 1;
+ struct clk *parent_clk = clk_get_parent(tup->uart_clk);
+ unsigned long parent_clk_rate = clk_get_rate(parent_clk);
+ int max_divider = (tup->cdata->support_clk_src_div) ? 0x7FFF : 0xFFFF;
+
+ max_divider *= 16;
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* Changing configuration, it is safe to stop any rx now */
+ if (tup->rts_active)
+ set_rts(tup, false);
+
+ /* Clear all interrupts as configuration is going to be change */
+ tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER);
+ tegra_uart_read(tup, UART_IER);
+ tegra_uart_write(tup, 0, UART_IER);
+ tegra_uart_read(tup, UART_IER);
+
+ /* Parity */
+ lcr = tup->lcr_shadow;
+ lcr &= ~UART_LCR_PARITY;
+
+ /* CMSPAR isn't supported by this driver */
+ termios->c_cflag &= ~CMSPAR;
+
+ if ((termios->c_cflag & PARENB) == PARENB) {
+ symb_bit++;
+ if (termios->c_cflag & PARODD) {
+ lcr |= UART_LCR_PARITY;
+ lcr &= ~UART_LCR_EPAR;
+ lcr &= ~UART_LCR_SPAR;
+ } else {
+ lcr |= UART_LCR_PARITY;
+ lcr |= UART_LCR_EPAR;
+ lcr &= ~UART_LCR_SPAR;
+ }
+ }
+
+ lcr &= ~UART_LCR_WLEN8;
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr |= UART_LCR_WLEN5;
+ symb_bit += 5;
+ break;
+ case CS6:
+ lcr |= UART_LCR_WLEN6;
+ symb_bit += 6;
+ break;
+ case CS7:
+ lcr |= UART_LCR_WLEN7;
+ symb_bit += 7;
+ break;
+ default:
+ lcr |= UART_LCR_WLEN8;
+ symb_bit += 8;
+ break;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB) {
+ lcr |= UART_LCR_STOP;
+ symb_bit += 2;
+ } else {
+ lcr &= ~UART_LCR_STOP;
+ symb_bit++;
+ }
+
+ tegra_uart_write(tup, lcr, UART_LCR);
+ tup->lcr_shadow = lcr;
+ tup->symb_bit = symb_bit;
+
+ /* Baud rate. */
+ baud = uart_get_baud_rate(u, termios, oldtermios,
+ parent_clk_rate/max_divider,
+ parent_clk_rate/16);
+ spin_unlock_irqrestore(&u->lock, flags);
+ tegra_set_baudrate(tup, baud);
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* Flow control */
+ if (termios->c_cflag & CRTSCTS) {
+ tup->mcr_shadow |= TEGRA_UART_MCR_CTS_EN;
+ tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN;
+ tegra_uart_write(tup, tup->mcr_shadow, UART_MCR);
+ /* if top layer has asked to set rts active then do so here */
+ if (tup->rts_active)
+ set_rts(tup, true);
+ } else {
+ tup->mcr_shadow &= ~TEGRA_UART_MCR_CTS_EN;
+ tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN;
+ tegra_uart_write(tup, tup->mcr_shadow, UART_MCR);
+ }
+
+ /* update the port timeout based on new settings */
+ uart_update_timeout(u, termios->c_cflag, baud);
+
+ /* Make sure all write has completed */
+ tegra_uart_read(tup, UART_IER);
+
+ /* Reenable interrupt */
+ tegra_uart_write(tup, tup->ier_shadow, UART_IER);
+ tegra_uart_read(tup, UART_IER);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+}
+
+/*
+ * Flush any TX data submitted for DMA and PIO. Called when the
+ * TX circular buffer is reset.
+ */
+static void tegra_uart_flush_buffer(struct uart_port *u)
+{
+ struct tegra_uart_port *tup = to_tegra_uport(u);
+
+ tup->tx_bytes = 0;
+ if (tup->tx_dma_chan)
+ dmaengine_terminate_all(tup->tx_dma_chan);
+ return;
+}
+
+static const char *tegra_uart_type(struct uart_port *u)
+{
+ return TEGRA_UART_TYPE;
+}
+
+static struct uart_ops tegra_uart_ops = {
+ .tx_empty = tegra_uart_tx_empty,
+ .set_mctrl = tegra_uart_set_mctrl,
+ .get_mctrl = tegra_uart_get_mctrl,
+ .stop_tx = tegra_uart_stop_tx,
+ .start_tx = tegra_uart_start_tx,
+ .stop_rx = tegra_uart_stop_rx,
+ .flush_buffer = tegra_uart_flush_buffer,
+ .enable_ms = tegra_uart_enable_ms,
+ .break_ctl = tegra_uart_break_ctl,
+ .startup = tegra_uart_startup,
+ .shutdown = tegra_uart_shutdown,
+ .set_termios = tegra_uart_set_termios,
+ .type = tegra_uart_type,
+ .request_port = tegra_uart_request_port,
+ .release_port = tegra_uart_release_port,
+};
+
+static struct uart_driver tegra_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "tegra_hsuart",
+ .dev_name = "ttyTHS",
+ .cons = NULL,
+ .nr = TEGRA_UART_MAXIMUM,
+};
+
+static int tegra_uart_parse_dt(struct platform_device *pdev,
+ struct tegra_uart_port *tup)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int port;
+
+ port = of_alias_get_id(np, "serial");
+ if (port < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", port);
+ return port;
+ }
+ tup->uport.line = port;
+
+ tup->enable_modem_interrupt = of_property_read_bool(np,
+ "nvidia,enable-modem-interrupt");
+ return 0;
+}
+
+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,
+};
+
+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,
+};
+
+static struct of_device_id tegra_uart_of_match[] = {
+ {
+ .compatible = "nvidia,tegra30-hsuart",
+ .data = &tegra30_uart_chip_data,
+ }, {
+ .compatible = "nvidia,tegra20-hsuart",
+ .data = &tegra20_uart_chip_data,
+ }, {
+ },
+};
+MODULE_DEVICE_TABLE(of, tegra_uart_of_match);
+
+static int tegra_uart_probe(struct platform_device *pdev)
+{
+ struct tegra_uart_port *tup;
+ struct uart_port *u;
+ struct resource *resource;
+ int ret;
+ const struct tegra_uart_chip_data *cdata;
+ const struct of_device_id *match;
+
+ match = of_match_device(tegra_uart_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ cdata = match->data;
+
+ tup = devm_kzalloc(&pdev->dev, sizeof(*tup), GFP_KERNEL);
+ if (!tup) {
+ dev_err(&pdev->dev, "Failed to allocate memory for tup\n");
+ return -ENOMEM;
+ }
+
+ ret = tegra_uart_parse_dt(pdev, tup);
+ if (ret < 0)
+ return ret;
+
+ u = &tup->uport;
+ u->dev = &pdev->dev;
+ u->ops = &tegra_uart_ops;
+ u->type = PORT_TEGRA;
+ u->fifosize = 32;
+ tup->cdata = cdata;
+
+ platform_set_drvdata(pdev, tup);
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!resource) {
+ dev_err(&pdev->dev, "No IO memory resource\n");
+ return -ENODEV;
+ }
+
+ u->mapbase = resource->start;
+ u->membase = devm_ioremap_resource(&pdev->dev, resource);
+ if (IS_ERR(u->membase))
+ return PTR_ERR(u->membase);
+
+ tup->uart_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tup->uart_clk)) {
+ dev_err(&pdev->dev, "Couldn't get the clock\n");
+ 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;
+ ret = uart_add_one_port(&tegra_uart_driver, u);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to add uart port, err %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int tegra_uart_remove(struct platform_device *pdev)
+{
+ struct tegra_uart_port *tup = platform_get_drvdata(pdev);
+ struct uart_port *u = &tup->uport;
+
+ uart_remove_one_port(&tegra_uart_driver, u);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_uart_suspend(struct device *dev)
+{
+ struct tegra_uart_port *tup = dev_get_drvdata(dev);
+ struct uart_port *u = &tup->uport;
+
+ return uart_suspend_port(&tegra_uart_driver, u);
+}
+
+static int tegra_uart_resume(struct device *dev)
+{
+ struct tegra_uart_port *tup = dev_get_drvdata(dev);
+ struct uart_port *u = &tup->uport;
+
+ return uart_resume_port(&tegra_uart_driver, u);
+}
+#endif
+
+static const struct dev_pm_ops tegra_uart_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_uart_suspend, tegra_uart_resume)
+};
+
+static struct platform_driver tegra_uart_platform_driver = {
+ .probe = tegra_uart_probe,
+ .remove = tegra_uart_remove,
+ .driver = {
+ .name = "serial-tegra",
+ .of_match_table = tegra_uart_of_match,
+ .pm = &tegra_uart_pm_ops,
+ },
+};
+
+static int __init tegra_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&tegra_uart_driver);
+ if (ret < 0) {
+ pr_err("Could not register %s driver\n",
+ tegra_uart_driver.driver_name);
+ return ret;
+ }
+
+ ret = platform_driver_register(&tegra_uart_platform_driver);
+ if (ret < 0) {
+ pr_err("Uart platform driver register failed, e = %d\n", ret);
+ uart_unregister_driver(&tegra_uart_driver);
+ return ret;
+ }
+ return 0;
+}
+
+static void __exit tegra_uart_exit(void)
+{
+ pr_info("Unloading tegra uart driver\n");
+ platform_driver_unregister(&tegra_uart_platform_driver);
+ uart_unregister_driver(&tegra_uart_driver);
+}
+
+module_init(tegra_uart_init);
+module_exit(tegra_uart_exit);
+
+MODULE_ALIAS("platform:serial-tegra");
+MODULE_DESCRIPTION("High speed UART driver for tegra chipset");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 13056180adf..fbf6c5ad222 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -50,16 +50,11 @@ static struct lock_class_key port_lock_key;
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
-#ifdef CONFIG_SERIAL_CORE_CONSOLE
-#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line)
-#else
-#define uart_console(port) (0)
-#endif
-
static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
struct ktermios *old_termios);
static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
-static void uart_change_pm(struct uart_state *state, int pm_state);
+static void uart_change_pm(struct uart_state *state,
+ enum uart_pm_state pm_state);
static void uart_port_shutdown(struct tty_port *port);
@@ -94,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);
}
@@ -143,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.
*/
@@ -159,7 +158,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
retval = uport->ops->startup(uport);
if (retval == 0) {
if (uart_console(uport) && uport->cons->cflag) {
- tty->termios->c_cflag = uport->cons->cflag;
+ tty->termios.c_cflag = uport->cons->cflag;
uport->cons->cflag = 0;
}
/*
@@ -172,11 +171,15 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
* Setup the RTS and DTR signals once the
* port is open and ready to respond.
*/
- if (tty->termios->c_cflag & CBAUD)
+ if (tty->termios.c_cflag & CBAUD)
uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
}
-
- if (port->flags & ASYNC_CTS_FLOW) {
+ /*
+ * 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;
@@ -240,7 +243,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
/*
* Turn off DTR and RTS early.
*/
- if (!tty || (tty->termios->c_cflag & HUPCL))
+ if (!tty || (tty->termios.c_cflag & HUPCL))
uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
uart_port_shutdown(port);
@@ -440,10 +443,10 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
* If we have no tty, termios, or the port does not exist,
* then we can't set the parameters for this port.
*/
- if (!tty || !tty->termios || uport->type == PORT_UNKNOWN)
+ if (!tty || uport->type == PORT_UNKNOWN)
return;
- termios = tty->termios;
+ termios = &tty->termios;
/*
* Set flags based on termios cflag
@@ -610,105 +613,126 @@ static void uart_send_xchar(struct tty_struct *tty, char ch)
static void uart_throttle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
+ struct uart_port *port = state->uart_port;
+ uint32_t mask = 0;
if (I_IXOFF(tty))
+ mask |= UPF_SOFT_FLOW;
+ if (tty->termios.c_cflag & CRTSCTS)
+ mask |= UPF_HARD_FLOW;
+
+ if (port->flags & mask) {
+ port->ops->throttle(port);
+ mask &= ~port->flags;
+ }
+
+ if (mask & UPF_SOFT_FLOW)
uart_send_xchar(tty, STOP_CHAR(tty));
- if (tty->termios->c_cflag & CRTSCTS)
- uart_clear_mctrl(state->uart_port, TIOCM_RTS);
+ if (mask & UPF_HARD_FLOW)
+ uart_clear_mctrl(port, TIOCM_RTS);
}
static void uart_unthrottle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
+ uint32_t mask = 0;
- if (I_IXOFF(tty)) {
+ if (I_IXOFF(tty))
+ mask |= UPF_SOFT_FLOW;
+ if (tty->termios.c_cflag & CRTSCTS)
+ mask |= UPF_HARD_FLOW;
+
+ if (port->flags & mask) {
+ port->ops->unthrottle(port);
+ mask &= ~port->flags;
+ }
+
+ if (mask & UPF_SOFT_FLOW) {
if (port->x_char)
port->x_char = 0;
else
uart_send_xchar(tty, START_CHAR(tty));
}
- if (tty->termios->c_cflag & CRTSCTS)
+ if (mask & UPF_HARD_FLOW)
uart_set_mctrl(port, TIOCM_RTS);
}
-static int uart_get_info(struct uart_state *state,
- struct serial_struct __user *retinfo)
+static void do_uart_get_info(struct tty_port *port,
+ struct serial_struct *retinfo)
{
+ struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport = state->uart_port;
- struct tty_port *port = &state->port;
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
- /* Ensure the state we copy is consistent and no hardware changes
- occur as we go */
- mutex_lock(&port->mutex);
+ memset(retinfo, 0, sizeof(*retinfo));
- tmp.type = uport->type;
- tmp.line = uport->line;
- tmp.port = uport->iobase;
+ retinfo->type = uport->type;
+ retinfo->line = uport->line;
+ retinfo->port = uport->iobase;
if (HIGH_BITS_OFFSET)
- tmp.port_high = (long) uport->iobase >> HIGH_BITS_OFFSET;
- tmp.irq = uport->irq;
- tmp.flags = uport->flags;
- tmp.xmit_fifo_size = uport->fifosize;
- tmp.baud_base = uport->uartclk / 16;
- tmp.close_delay = jiffies_to_msecs(port->close_delay) / 10;
- tmp.closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ retinfo->port_high = (long) uport->iobase >> HIGH_BITS_OFFSET;
+ retinfo->irq = uport->irq;
+ retinfo->flags = uport->flags;
+ retinfo->xmit_fifo_size = uport->fifosize;
+ retinfo->baud_base = uport->uartclk / 16;
+ retinfo->close_delay = jiffies_to_msecs(port->close_delay) / 10;
+ retinfo->closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
jiffies_to_msecs(port->closing_wait) / 10;
- tmp.custom_divisor = uport->custom_divisor;
- tmp.hub6 = uport->hub6;
- tmp.io_type = uport->iotype;
- tmp.iomem_reg_shift = uport->regshift;
- tmp.iomem_base = (void *)(unsigned long)uport->mapbase;
+ retinfo->custom_divisor = uport->custom_divisor;
+ retinfo->hub6 = uport->hub6;
+ retinfo->io_type = uport->iotype;
+ retinfo->iomem_reg_shift = uport->regshift;
+ retinfo->iomem_base = (void *)(unsigned long)uport->mapbase;
+}
+static void uart_get_info(struct tty_port *port,
+ struct serial_struct *retinfo)
+{
+ /* Ensure the state we copy is consistent and no hardware changes
+ occur as we go */
+ mutex_lock(&port->mutex);
+ do_uart_get_info(port, retinfo);
mutex_unlock(&port->mutex);
+}
+
+static int uart_get_info_user(struct tty_port *port,
+ struct serial_struct __user *retinfo)
+{
+ struct serial_struct tmp;
+ uart_get_info(port, &tmp);
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
-static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
- struct serial_struct __user *newinfo)
+static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
+ struct uart_state *state,
+ struct serial_struct *new_info)
{
- struct serial_struct new_serial;
struct uart_port *uport = state->uart_port;
- struct tty_port *port = &state->port;
unsigned long new_port;
unsigned int change_irq, change_port, closing_wait;
unsigned int old_custom_divisor, close_delay;
upf_t old_flags, new_flags;
int retval = 0;
- if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
- return -EFAULT;
-
- new_port = new_serial.port;
+ new_port = new_info->port;
if (HIGH_BITS_OFFSET)
- new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
+ new_port += (unsigned long) new_info->port_high << HIGH_BITS_OFFSET;
- new_serial.irq = irq_canonicalize(new_serial.irq);
- close_delay = msecs_to_jiffies(new_serial.close_delay * 10);
- closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ new_info->irq = irq_canonicalize(new_info->irq);
+ close_delay = msecs_to_jiffies(new_info->close_delay * 10);
+ closing_wait = new_info->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
- msecs_to_jiffies(new_serial.closing_wait * 10);
+ msecs_to_jiffies(new_info->closing_wait * 10);
- /*
- * This semaphore protects port->count. It is also
- * very useful to prevent opens. Also, take the
- * port configuration semaphore to make sure that a
- * module insertion/removal doesn't change anything
- * under us.
- */
- mutex_lock(&port->mutex);
change_irq = !(uport->flags & UPF_FIXED_PORT)
- && new_serial.irq != uport->irq;
+ && new_info->irq != uport->irq;
/*
* Since changing the 'type' of the port changes its resource
@@ -717,29 +741,29 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
*/
change_port = !(uport->flags & UPF_FIXED_PORT)
&& (new_port != uport->iobase ||
- (unsigned long)new_serial.iomem_base != uport->mapbase ||
- new_serial.hub6 != uport->hub6 ||
- new_serial.io_type != uport->iotype ||
- new_serial.iomem_reg_shift != uport->regshift ||
- new_serial.type != uport->type);
+ (unsigned long)new_info->iomem_base != uport->mapbase ||
+ new_info->hub6 != uport->hub6 ||
+ new_info->io_type != uport->iotype ||
+ new_info->iomem_reg_shift != uport->regshift ||
+ new_info->type != uport->type);
old_flags = uport->flags;
- new_flags = new_serial.flags;
+ new_flags = new_info->flags;
old_custom_divisor = uport->custom_divisor;
if (!capable(CAP_SYS_ADMIN)) {
retval = -EPERM;
if (change_irq || change_port ||
- (new_serial.baud_base != uport->uartclk / 16) ||
+ (new_info->baud_base != uport->uartclk / 16) ||
(close_delay != port->close_delay) ||
(closing_wait != port->closing_wait) ||
- (new_serial.xmit_fifo_size &&
- new_serial.xmit_fifo_size != uport->fifosize) ||
+ (new_info->xmit_fifo_size &&
+ new_info->xmit_fifo_size != uport->fifosize) ||
(((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0))
goto exit;
uport->flags = ((uport->flags & ~UPF_USR_MASK) |
(new_flags & UPF_USR_MASK));
- uport->custom_divisor = new_serial.custom_divisor;
+ uport->custom_divisor = new_info->custom_divisor;
goto check_and_exit;
}
@@ -747,10 +771,10 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
* Ask the low level driver to verify the settings.
*/
if (uport->ops->verify_port)
- retval = uport->ops->verify_port(uport, &new_serial);
+ retval = uport->ops->verify_port(uport, new_info);
- if ((new_serial.irq >= nr_irqs) || (new_serial.irq < 0) ||
- (new_serial.baud_base < 9600))
+ if ((new_info->irq >= nr_irqs) || (new_info->irq < 0) ||
+ (new_info->baud_base < 9600))
retval = -EINVAL;
if (retval)
@@ -790,11 +814,11 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
uport->ops->release_port(uport);
uport->iobase = new_port;
- uport->type = new_serial.type;
- uport->hub6 = new_serial.hub6;
- uport->iotype = new_serial.io_type;
- uport->regshift = new_serial.iomem_reg_shift;
- uport->mapbase = (unsigned long)new_serial.iomem_base;
+ uport->type = new_info->type;
+ uport->hub6 = new_info->hub6;
+ uport->iotype = new_info->io_type;
+ uport->regshift = new_info->iomem_reg_shift;
+ uport->mapbase = (unsigned long)new_info->iomem_base;
/*
* Claim and map the new regions
@@ -810,44 +834,46 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
* 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;
}
}
if (change_irq)
- uport->irq = new_serial.irq;
+ uport->irq = new_info->irq;
if (!(uport->flags & UPF_FIXED_PORT))
- uport->uartclk = new_serial.baud_base * 16;
+ uport->uartclk = new_info->baud_base * 16;
uport->flags = (uport->flags & ~UPF_CHANGE_MASK) |
(new_flags & UPF_CHANGE_MASK);
- uport->custom_divisor = new_serial.custom_divisor;
+ uport->custom_divisor = new_info->custom_divisor;
port->close_delay = close_delay;
port->closing_wait = closing_wait;
- if (new_serial.xmit_fifo_size)
- uport->fifosize = new_serial.xmit_fifo_size;
- if (port->tty)
- port->tty->low_latency =
- (uport->flags & UPF_LOW_LATENCY) ? 1 : 0;
+ if (new_info->xmit_fifo_size)
+ uport->fifosize = new_info->xmit_fifo_size;
+ port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0;
check_and_exit:
retval = 0;
@@ -873,6 +899,28 @@ static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
} else
retval = uart_startup(tty, state, 1);
exit:
+ return retval;
+}
+
+static int uart_set_info_user(struct tty_struct *tty, struct uart_state *state,
+ struct serial_struct __user *newinfo)
+{
+ struct serial_struct new_serial;
+ struct tty_port *port = &state->port;
+ int retval;
+
+ if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+ return -EFAULT;
+
+ /*
+ * This semaphore protects port->count. It is also
+ * very useful to prevent opens. Also, take the
+ * port configuration semaphore to make sure that a
+ * module insertion/removal doesn't change anything
+ * under us.
+ */
+ mutex_lock(&port->mutex);
+ retval = uart_set_info(tty, port, state, &new_serial);
mutex_unlock(&port->mutex);
return retval;
}
@@ -1115,11 +1163,11 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd,
*/
switch (cmd) {
case TIOCGSERIAL:
- ret = uart_get_info(state, uarg);
+ ret = uart_get_info_user(port, uarg);
break;
case TIOCSSERIAL:
- ret = uart_set_info(tty, state, uarg);
+ ret = uart_set_info_user(tty, state, uarg);
break;
case TIOCSERCONFIG:
@@ -1187,16 +1235,29 @@ static void uart_set_ldisc(struct tty_struct *tty)
struct uart_port *uport = state->uart_port;
if (uport->ops->set_ldisc)
- uport->ops->set_ldisc(uport, tty->termios->c_line);
+ uport->ops->set_ldisc(uport, tty->termios.c_line);
}
static void uart_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct uart_state *state = tty->driver_data;
+ struct uart_port *uport = state->uart_port;
unsigned long flags;
- unsigned int cflag = tty->termios->c_cflag;
+ unsigned int cflag = tty->termios.c_cflag;
+ unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK;
+ bool sw_changed = false;
+ /*
+ * Drivers doing software flow control also need to know
+ * about changes to these input settings.
+ */
+ if (uport->flags & UPF_SOFT_FLOW) {
+ iflag_mask |= IXANY|IXON|IXOFF;
+ sw_changed =
+ tty->termios.c_cc[VSTART] != old_termios->c_cc[VSTART] ||
+ tty->termios.c_cc[VSTOP] != old_termios->c_cc[VSTOP];
+ }
/*
* These are the bits that are used to setup various
@@ -1204,11 +1265,11 @@ static void uart_set_termios(struct tty_struct *tty,
* bits in c_cflag; c_[io]speed will always be set
* appropriately by set_termios() in tty_ioctl.c
*/
-#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
if ((cflag ^ old_termios->c_cflag) == 0 &&
- tty->termios->c_ospeed == old_termios->c_ospeed &&
- tty->termios->c_ispeed == old_termios->c_ispeed &&
- RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) {
+ tty->termios.c_ospeed == old_termios->c_ospeed &&
+ tty->termios.c_ispeed == old_termios->c_ispeed &&
+ ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 &&
+ !sw_changed) {
return;
}
@@ -1216,38 +1277,46 @@ static void uart_set_termios(struct tty_struct *tty,
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
- uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);
+ uart_clear_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
/* Handle transition away from B0 status */
else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
unsigned int mask = TIOCM_DTR;
if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS;
- uart_set_mctrl(state->uart_port, mask);
+ uart_set_mctrl(uport, mask);
}
+ /*
+ * If the port is doing h/w assisted flow control, do nothing.
+ * We assume that tty->hw_stopped has never been set.
+ */
+ if (uport->flags & UPF_HARD_FLOW)
+ return;
+
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
- spin_lock_irqsave(&state->uart_port->lock, flags);
+ spin_lock_irqsave(&uport->lock, flags);
tty->hw_stopped = 0;
__uart_start(tty);
- spin_unlock_irqrestore(&state->uart_port->lock, flags);
+ spin_unlock_irqrestore(&uport->lock, flags);
}
/* Handle turning on CRTSCTS */
else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
- spin_lock_irqsave(&state->uart_port->lock, flags);
- if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {
+ spin_lock_irqsave(&uport->lock, flags);
+ if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) {
tty->hw_stopped = 1;
- state->uart_port->ops->stop_tx(state->uart_port);
+ uport->ops->stop_tx(uport);
}
- spin_unlock_irqrestore(&state->uart_port->lock, flags);
+ spin_unlock_irqrestore(&uport->lock, flags);
}
}
/*
- * In 2.4.5, calls to this will be serialized via the BKL in
- * linux/drivers/char/tty_io.c:tty_release()
- * linux/drivers/char/tty_io.c:do_tty_handup()
+ * Calls to uart_close() are serialised via the tty_lock in
+ * drivers/tty/tty_io.c:tty_release()
+ * drivers/tty/tty_io.c:do_tty_hangup()
+ * This runs from a workqueue and can sleep for a _short_ time only.
*/
static void uart_close(struct tty_struct *tty, struct file *filp)
{
@@ -1262,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;
/*
@@ -1302,7 +1371,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
spin_lock_irqsave(&port->lock, flags);
} else if (!uart_console(uport)) {
spin_unlock_irqrestore(&port->lock, flags);
- uart_change_pm(state, 3);
+ uart_change_pm(state, UART_PM_STATE_OFF);
spin_lock_irqsave(&port->lock, flags);
}
@@ -1374,10 +1443,9 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
}
/*
- * This is called with the BKL held in
- * linux/drivers/char/tty_io.c:do_tty_hangup()
- * We're called from the eventd thread, so we can sleep for
- * a _short_ time only.
+ * Calls to uart_hangup() are serialised by the tty_lock in
+ * drivers/tty/tty_io.c:do_tty_hangup()
+ * This runs from a workqueue and can sleep for a _short_ time only.
*/
static void uart_hangup(struct tty_struct *tty)
{
@@ -1396,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);
}
@@ -1458,8 +1528,8 @@ static void uart_dtr_rts(struct tty_port *port, int onoff)
}
/*
- * calls to uart_open are serialised by the BKL in
- * fs/char_dev.c:chrdev_open()
+ * Calls to uart_open are serialised by the tty_lock in
+ * drivers/tty/tty_io.c:tty_open()
* Note that if this fails, then uart_close() _will_ be called.
*
* In time, we want to scrap the "opening nonpresent ports"
@@ -1501,7 +1571,8 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
*/
tty->driver_data = state;
state->uart_port->state = state;
- tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
+ state->port.low_latency =
+ (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty_port_tty_set(port, tty);
/*
@@ -1513,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, 0);
-
- /*
* Start up the serial port.
*/
retval = uart_startup(tty, state, 0);
@@ -1557,7 +1622,7 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i)
{
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
- int pm_state;
+ enum uart_pm_state pm_state;
struct uart_port *uport = state->uart_port;
char stat_buf[32];
unsigned int status;
@@ -1582,12 +1647,12 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i)
if (capable(CAP_SYS_ADMIN)) {
mutex_lock(&port->mutex);
pm_state = state->pm_state;
- if (pm_state)
- uart_change_pm(state, 0);
+ if (pm_state != UART_PM_STATE_ON)
+ uart_change_pm(state, UART_PM_STATE_ON);
spin_lock_irq(&uport->lock);
status = uport->ops->get_mctrl(uport);
spin_unlock_irq(&uport->lock);
- if (pm_state)
+ if (pm_state != UART_PM_STATE_ON)
uart_change_pm(state, pm_state);
mutex_unlock(&port->mutex);
@@ -1648,7 +1713,7 @@ static int uart_proc_show(struct seq_file *m, void *v)
static int uart_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, uart_proc_show, PDE(inode)->data);
+ return single_open(file, uart_proc_show, PDE_DATA(inode));
}
static const struct file_operations uart_proc_fops = {
@@ -1705,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.
@@ -1773,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));
@@ -1834,7 +1903,8 @@ EXPORT_SYMBOL_GPL(uart_set_options);
*
* Locking: port->mutex has to be held
*/
-static void uart_change_pm(struct uart_state *state, int pm_state)
+static void uart_change_pm(struct uart_state *state,
+ enum uart_pm_state pm_state)
{
struct uart_port *port = state->uart_port;
@@ -1877,6 +1947,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
mutex_unlock(&port->mutex);
return 0;
}
+ put_device(tty_dev);
+
if (console_suspend_enabled || !uart_console(uport))
uport->suspended = 1;
@@ -1919,7 +1991,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
console_stop(uport->cons);
if (console_suspend_enabled || !uart_console(uport))
- uart_change_pm(state, 3);
+ uart_change_pm(state, UART_PM_STATE_OFF);
mutex_unlock(&port->mutex);
@@ -1942,9 +2014,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
disable_irq_wake(uport->irq);
uport->irq_wake = 0;
}
+ put_device(tty_dev);
mutex_unlock(&port->mutex);
return 0;
}
+ put_device(tty_dev);
uport->suspended = 0;
/*
@@ -1960,11 +2034,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
/*
* If that's unset, use the tty termios setting.
*/
- if (port->tty && port->tty->termios && termios.c_cflag == 0)
- termios = *(port->tty->termios);
+ if (port->tty && termios.c_cflag == 0)
+ termios = port->tty->termios;
if (console_suspend_enabled)
- uart_change_pm(state, 0);
+ uart_change_pm(state, UART_PM_STATE_ON);
uport->ops->set_termios(uport, &termios, NULL);
if (console_suspend_enabled)
console_start(uport->cons);
@@ -1974,7 +2048,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
const struct uart_ops *ops = uport->ops;
int ret;
- uart_change_pm(state, 0);
+ uart_change_pm(state, UART_PM_STATE_ON);
spin_lock_irq(&uport->lock);
ops->set_mctrl(uport, 0);
spin_unlock_irq(&uport->lock);
@@ -2033,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
@@ -2074,7 +2148,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
uart_report_port(drv, port);
/* Power up port for set_mctrl() */
- uart_change_pm(state, 0);
+ uart_change_pm(state, UART_PM_STATE_ON);
/*
* Ensure that the modem control lines are de-activated.
@@ -2098,7 +2172,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
* console if we have one.
*/
if (!uart_console(port))
- uart_change_pm(state, 3);
+ uart_change_pm(state, UART_PM_STATE_OFF);
}
}
@@ -2113,6 +2187,7 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
+ int ret;
if (!state || !state->uart_port)
return -1;
@@ -2121,6 +2196,22 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
if (!(port->ops->poll_get_char && port->ops->poll_put_char))
return -1;
+ if (port->ops->poll_init) {
+ struct tty_port *tport = &state->port;
+
+ ret = 0;
+ mutex_lock(&tport->mutex);
+ /*
+ * We don't set ASYNCB_INITIALIZED as we only initialized the
+ * hw, e.g. state->xmit is still uninitialized.
+ */
+ if (!test_bit(ASYNCB_INITIALIZED, &tport->flags))
+ ret = port->ops->poll_init(port);
+ mutex_unlock(&tport->mutex);
+ if (ret)
+ return ret;
+ }
+
if (options) {
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(port, NULL, baud, parity, bits, flow);
@@ -2152,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
@@ -2230,7 +2324,6 @@ int uart_register_driver(struct uart_driver *drv)
drv->tty_driver = normal;
- normal->owner = drv->owner;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
@@ -2261,6 +2354,8 @@ int uart_register_driver(struct uart_driver *drv)
if (retval >= 0)
return retval;
+ for (i = 0; i < drv->nr; i++)
+ tty_port_destroy(&drv->state[i].port);
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
@@ -2280,9 +2375,14 @@ out:
void uart_unregister_driver(struct uart_driver *drv)
{
struct tty_driver *p = drv->tty_driver;
+ unsigned int i;
+
tty_unregister_driver(p);
put_tty_driver(p);
+ for (i = 0; i < drv->nr; i++)
+ tty_port_destroy(&drv->state[i].port);
kfree(drv->state);
+ drv->state = NULL;
drv->tty_driver = NULL;
}
@@ -2293,6 +2393,182 @@ struct tty_driver *uart_console_device(struct console *co, int *index)
return p->tty_driver;
}
+static ssize_t uart_get_attr_uartclk(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16);
+}
+
+static ssize_t uart_get_attr_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.type);
+}
+static ssize_t uart_get_attr_line(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.line);
+}
+
+static ssize_t uart_get_attr_port(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+ unsigned long ioaddr;
+
+ uart_get_info(port, &tmp);
+ ioaddr = tmp.port;
+ if (HIGH_BITS_OFFSET)
+ ioaddr |= (unsigned long)tmp.port_high << HIGH_BITS_OFFSET;
+ return snprintf(buf, PAGE_SIZE, "0x%lX\n", ioaddr);
+}
+
+static ssize_t uart_get_attr_irq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.irq);
+}
+
+static ssize_t uart_get_attr_flags(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "0x%X\n", tmp.flags);
+}
+
+static ssize_t uart_get_attr_xmit_fifo_size(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.xmit_fifo_size);
+}
+
+
+static ssize_t uart_get_attr_close_delay(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.close_delay);
+}
+
+
+static ssize_t uart_get_attr_closing_wait(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.closing_wait);
+}
+
+static ssize_t uart_get_attr_custom_divisor(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.custom_divisor);
+}
+
+static ssize_t uart_get_attr_io_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.io_type);
+}
+
+static ssize_t uart_get_attr_iomem_base(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)tmp.iomem_base);
+}
+
+static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct serial_struct tmp;
+ struct tty_port *port = dev_get_drvdata(dev);
+
+ uart_get_info(port, &tmp);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift);
+}
+
+static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL);
+static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL);
+static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL);
+static DEVICE_ATTR(irq, S_IRUSR | S_IRGRP, uart_get_attr_irq, NULL);
+static DEVICE_ATTR(flags, S_IRUSR | S_IRGRP, uart_get_attr_flags, NULL);
+static DEVICE_ATTR(xmit_fifo_size, S_IRUSR | S_IRGRP, uart_get_attr_xmit_fifo_size, NULL);
+static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL);
+static DEVICE_ATTR(close_delay, S_IRUSR | S_IRGRP, uart_get_attr_close_delay, NULL);
+static DEVICE_ATTR(closing_wait, S_IRUSR | S_IRGRP, uart_get_attr_closing_wait, NULL);
+static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divisor, NULL);
+static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL);
+static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL);
+static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL);
+
+static struct attribute *tty_dev_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_line.attr,
+ &dev_attr_port.attr,
+ &dev_attr_irq.attr,
+ &dev_attr_flags.attr,
+ &dev_attr_xmit_fifo_size.attr,
+ &dev_attr_uartclk.attr,
+ &dev_attr_close_delay.attr,
+ &dev_attr_closing_wait.attr,
+ &dev_attr_custom_divisor.attr,
+ &dev_attr_io_type.attr,
+ &dev_attr_iomem_base.attr,
+ &dev_attr_iomem_reg_shift.attr,
+ NULL,
+ };
+
+static const struct attribute_group tty_dev_attr_group = {
+ .attrs = tty_dev_attrs,
+ };
+
+static const struct attribute_group *tty_dev_attr_groups[] = {
+ &tty_dev_attr_group,
+ NULL
+ };
+
+
/**
* uart_add_one_port - attach a driver-defined port structure
* @drv: pointer to the uart low level driver structure for this port
@@ -2326,7 +2602,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
}
state->uart_port = uport;
- state->pm_state = -1;
+ state->pm_state = UART_PM_STATE_UNDEFINED;
uport->cons = drv->cons;
uport->state = state;
@@ -2344,9 +2620,10 @@ 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_register_device(drv->tty_driver, uport->line, uport->dev);
+ tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
+ uport->line, uport->dev, port, tty_dev_attr_groups);
if (likely(!IS_ERR(tty_dev))) {
device_set_wakeup_capable(tty_dev, 1);
} else {
@@ -2379,6 +2656,8 @@ 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());
@@ -2393,6 +2672,11 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
* succeeding while we shut down the port.
*/
mutex_lock(&port->mutex);
+ if (!state->uart_port) {
+ mutex_unlock(&port->mutex);
+ ret = -EINVAL;
+ goto out;
+ }
uport->flags |= UPF_DEAD;
mutex_unlock(&port->mutex);
@@ -2401,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.
@@ -2416,9 +2709,10 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
uport->type = PORT_UNKNOWN;
state->uart_port = NULL;
+out:
mutex_unlock(&port_mutex);
- return 0;
+ return ret;
}
/*
@@ -2452,31 +2746,24 @@ EXPORT_SYMBOL(uart_match_port);
*/
void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
{
- struct uart_state *state = uport->state;
- struct tty_port *port = &state->port;
- struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
- struct pps_event_time ts;
+ struct tty_port *port = &uport->state->port;
+ struct tty_struct *tty = port->tty;
+ struct tty_ldisc *ld = tty ? tty_ldisc_ref(tty) : NULL;
- if (ld && ld->ops->dcd_change)
- pps_get_ts(&ts);
+ if (ld) {
+ if (ld->ops->dcd_change)
+ ld->ops->dcd_change(tty, status);
+ tty_ldisc_deref(ld);
+ }
uport->icount.dcd++;
-#ifdef CONFIG_HARD_PPS
- if ((uport->flags & UPF_HARDPPS_CD) && status)
- hardpps();
-#endif
if (port->flags & ASYNC_CHECK_CD) {
if (status)
wake_up_interruptible(&port->open_wait);
- else if (port->tty)
- tty_hangup(port->tty);
+ else if (tty)
+ tty_hangup(tty);
}
-
- if (ld && ld->ops->dcd_change)
- ld->ops->dcd_change(port->tty, status, &ts);
- if (ld)
- tty_ldisc_deref(ld);
}
EXPORT_SYMBOL_GPL(uart_handle_dcd_change);
@@ -2492,7 +2779,9 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
uport->icount.cts++;
- if (port->flags & ASYNC_CTS_FLOW) {
+ /* 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;
@@ -2524,17 +2813,19 @@ EXPORT_SYMBOL_GPL(uart_handle_cts_change);
void uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
if ((status & port->ignore_status_mask & ~overrun) == 0)
- tty_insert_flip_char(tty, ch, flag);
+ if (tty_insert_flip_char(tport, ch, flag) == 0)
+ ++port->icount.buf_overrun;
/*
* Overrun is special. Since it's reported immediately,
* it doesn't affect the current character.
*/
if (status & ~port->ignore_status_mask & overrun)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ if (tty_insert_flip_char(tport, 0, TTY_OVERRUN) == 0)
+ ++port->icount.buf_overrun;
}
EXPORT_SYMBOL_GPL(uart_insert_char);
diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c
index 7c13639c597..5c79bdab985 100644
--- a/drivers/tty/serial/serial_ks8695.c
+++ b/drivers/tty/serial/serial_ks8695.c
@@ -153,7 +153,6 @@ static void ks8695uart_disable_ms(struct uart_port *port)
static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
- struct tty_struct *tty = port->state->port.tty;
unsigned int status, ch, lsr, flg, max_count = 256;
status = UART_GET_LSR(port); /* clears pending LSR interrupts */
@@ -200,7 +199,7 @@ static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id)
ignore_char:
status = UART_GET_LSR(port);
}
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&port->state->port);
return IRQ_HANDLED;
}
@@ -438,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;
/*
@@ -548,8 +547,8 @@ static struct uart_ops ks8695uart_pops = {
static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = {
{
- .membase = (void *) KS8695_UART_VA,
- .mapbase = KS8695_UART_VA,
+ .membase = KS8695_UART_VA,
+ .mapbase = KS8695_UART_PA,
.iotype = SERIAL_IO_MEM,
.irq = KS8695_IRQ_UART_TX,
.uartclk = KS8695_CLOCK_RATE * 16,
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 34bd345da77..ea8546092c7 100644
--- a/drivers/tty/serial/serial_txx9.c
+++ b/drivers/tty/serial/serial_txx9.c
@@ -277,7 +277,6 @@ static void serial_txx9_initialize(struct uart_port *port)
static inline void
receive_chars(struct uart_txx9_port *up, unsigned int *status)
{
- struct tty_struct *tty = up->port.state->port.tty;
unsigned char ch;
unsigned int disr = *status;
int max_count = 256;
@@ -346,7 +345,7 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status)
disr = sio_in(up, TXX9_SIDISR);
} while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0));
spin_unlock(&up->port.lock);
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&up->port.state->port);
spin_lock(&up->port.lock);
*status = disr;
}
@@ -466,7 +465,7 @@ static void serial_txx9_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore(&up->port.lock, flags);
}
-#if defined(CONFIG_SERIAL_TXX9_CONSOLE) || (CONFIG_CONSOLE_POLL)
+#if defined(CONFIG_SERIAL_TXX9_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
/*
* Wait for transmitter & holding register to empty
*/
@@ -536,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
@@ -703,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;
/*
@@ -1030,7 +1024,7 @@ static DEFINE_MUTEX(serial_txx9_mutex);
*
* On success the port is ready to use and the line number is returned.
*/
-static int __devinit serial_txx9_register_port(struct uart_port *port)
+static int serial_txx9_register_port(struct uart_port *port)
{
int i;
struct uart_txx9_port *uart;
@@ -1078,7 +1072,7 @@ static int __devinit serial_txx9_register_port(struct uart_port *port)
* Remove one serial port. This may not be called from interrupt
* context. We hand the port back to the our control.
*/
-static void __devexit serial_txx9_unregister_port(int line)
+static void serial_txx9_unregister_port(int line)
{
struct uart_txx9_port *uart = &serial_txx9_ports[line];
@@ -1096,9 +1090,9 @@ static void __devexit serial_txx9_unregister_port(int line)
/*
* Register a set of serial devices attached to a platform device.
*/
-static int __devinit serial_txx9_probe(struct platform_device *dev)
+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;
@@ -1126,7 +1120,7 @@ static int __devinit serial_txx9_probe(struct platform_device *dev)
/*
* Remove serial ports registered against a platform device.
*/
-static int __devexit serial_txx9_remove(struct platform_device *dev)
+static int serial_txx9_remove(struct platform_device *dev)
{
int i;
@@ -1171,7 +1165,7 @@ static int serial_txx9_resume(struct platform_device *dev)
static struct platform_driver serial_txx9_plat_driver = {
.probe = serial_txx9_probe,
- .remove = __devexit_p(serial_txx9_remove),
+ .remove = serial_txx9_remove,
#ifdef CONFIG_PM
.suspend = serial_txx9_suspend,
.resume = serial_txx9_resume,
@@ -1187,7 +1181,7 @@ static struct platform_driver serial_txx9_plat_driver = {
* Probe one serial board. Unfortunately, there is no rhyme nor reason
* to the arrangement of serial ports on a PCI card.
*/
-static int __devinit
+static int
pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
{
struct uart_port port;
@@ -1217,12 +1211,10 @@ pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
return 0;
}
-static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev)
+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);
@@ -1261,7 +1253,7 @@ static const struct pci_device_id serial_txx9_pci_tbl[] = {
static struct pci_driver serial_txx9_pci_driver = {
.name = "serial_txx9",
.probe = pciserial_txx9_init_one,
- .remove = __devexit_p(pciserial_txx9_remove_one),
+ .remove = pciserial_txx9_remove_one,
#ifdef CONFIG_PM
.suspend = pciserial_txx9_suspend_one,
.resume = pciserial_txx9_resume_one,
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 61b7fd2729c..88236da0ddf 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -23,34 +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/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>
@@ -58,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;
@@ -73,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;
@@ -98,12 +120,6 @@ struct sci_port {
#endif
struct notifier_block freq_transition;
-
-#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
- unsigned short saved_smr;
- unsigned short saved_fcr;
- unsigned char saved_brr;
-#endif
};
/* Function prototypes */
@@ -151,6 +167,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = sci_reg_invalid,
[SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -170,6 +187,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = sci_reg_invalid,
[SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -188,6 +206,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = sci_reg_invalid,
[SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -201,11 +220,12 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCxSR] = { 0x14, 16 },
[SCxRDR] = { 0x60, 8 },
[SCFCR] = { 0x18, 16 },
- [SCFDR] = { 0x1c, 16 },
- [SCTFDR] = sci_reg_invalid,
- [SCRFDR] = sci_reg_invalid,
+ [SCFDR] = sci_reg_invalid,
+ [SCTFDR] = { 0x38, 16 },
+ [SCRFDR] = { 0x3c, 16 },
[SCSPTR] = sci_reg_invalid,
[SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -225,6 +245,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = { 0x20, 16 },
[SCLSR] = { 0x24, 16 },
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -243,6 +264,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = sci_reg_invalid,
[SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -261,6 +283,26 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = { 0x20, 16 },
[SCLSR] = { 0x24, 16 },
+ [HSSRR] = sci_reg_invalid,
+ },
+
+ /*
+ * Common HSCIF definitions.
+ */
+ [SCIx_HSCIF_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x0c, 8 },
+ [SCxSR] = { 0x10, 16 },
+ [SCxRDR] = { 0x14, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = { 0x20, 16 },
+ [SCLSR] = { 0x24, 16 },
+ [HSSRR] = { 0x40, 16 },
},
/*
@@ -280,6 +322,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = sci_reg_invalid,
[SCLSR] = { 0x24, 16 },
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -299,6 +342,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = { 0x20, 16 },
[SCSPTR] = { 0x24, 16 },
[SCLSR] = { 0x28, 16 },
+ [HSSRR] = sci_reg_invalid,
},
/*
@@ -318,6 +362,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[SCRFDR] = sci_reg_invalid,
[SCSPTR] = sci_reg_invalid,
[SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
},
};
@@ -355,9 +400,6 @@ static void sci_serial_out(struct uart_port *p, int offset, int value)
WARN(1, "Invalid register access\n");
}
-#define sci_in(up, offset) (up->serial_in(up, offset))
-#define sci_out(up, offset, value) (up->serial_out(up, offset, value))
-
static int sci_probe_regmap(struct plat_sci_port *cfg)
{
switch (cfg->type) {
@@ -382,8 +424,11 @@ static int sci_probe_regmap(struct plat_sci_port *cfg)
*/
cfg->regtype = SCIx_SH4_SCIF_REGTYPE;
break;
+ case PORT_HSCIF:
+ 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;
}
@@ -397,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)
@@ -407,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);
}
@@ -422,9 +475,9 @@ static int sci_poll_get_char(struct uart_port *port)
int c;
do {
- status = sci_in(port, SCxSR);
+ status = serial_port_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) {
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
+ serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
continue;
}
break;
@@ -433,11 +486,11 @@ static int sci_poll_get_char(struct uart_port *port)
if (!(status & SCxSR_RDxF(port)))
return NO_POLL_CHAR;
- c = sci_in(port, SCxRDR);
+ c = serial_port_in(port, SCxRDR);
/* Dummy read */
- sci_in(port, SCxSR);
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+ serial_port_in(port, SCxSR);
+ serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
return c;
}
@@ -448,11 +501,11 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
unsigned short status;
do {
- status = sci_in(port, SCxSR);
+ status = serial_port_in(port, SCxSR);
} while (!(status & SCxSR_TDxE(port)));
- sci_out(port, SCxTDR, c);
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
+ serial_port_out(port, SCxTDR, c);
+ serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
}
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
@@ -480,10 +533,10 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
((!(cflag & CRTSCTS)))) {
unsigned short status;
- status = sci_in(port, SCSPTR);
+ status = serial_port_in(port, SCSPTR);
status &= ~SCSPTR_CTSIO;
status |= SCSPTR_RTSIO;
- sci_out(port, SCSPTR, status); /* Set RTS = 1 */
+ serial_port_out(port, SCSPTR, status); /* Set RTS = 1 */
}
}
@@ -493,13 +546,13 @@ static int sci_txfill(struct uart_port *port)
reg = sci_getreg(port, SCTFDR);
if (reg->size)
- return sci_in(port, SCTFDR) & 0xff;
+ return serial_port_in(port, SCTFDR) & ((port->fifosize << 1) - 1);
reg = sci_getreg(port, SCFDR);
if (reg->size)
- return sci_in(port, SCFDR) >> 8;
+ return serial_port_in(port, SCFDR) >> 8;
- return !(sci_in(port, SCxSR) & SCI_TDRE);
+ return !(serial_port_in(port, SCxSR) & SCI_TDRE);
}
static int sci_txroom(struct uart_port *port)
@@ -513,13 +566,13 @@ static int sci_rxfill(struct uart_port *port)
reg = sci_getreg(port, SCRFDR);
if (reg->size)
- return sci_in(port, SCRFDR) & 0xff;
+ return serial_port_in(port, SCRFDR) & ((port->fifosize << 1) - 1);
reg = sci_getreg(port, SCFDR);
if (reg->size)
- return sci_in(port, SCFDR) & ((port->fifosize << 1) - 1);
+ return serial_port_in(port, SCFDR) & ((port->fifosize << 1) - 1);
- return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
+ return (serial_port_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
}
/*
@@ -532,7 +585,8 @@ static inline int sci_rxd_in(struct uart_port *port)
if (s->cfg->port_reg <= 0)
return 1;
- return !!__raw_readb(s->cfg->port_reg);
+ /* Cast for ARM damage */
+ return !!__raw_readb((void __iomem *)(uintptr_t)s->cfg->port_reg);
}
/* ********************************************************************** *
@@ -547,14 +601,14 @@ static void sci_transmit_chars(struct uart_port *port)
unsigned short ctrl;
int count;
- status = sci_in(port, SCxSR);
+ status = serial_port_in(port, SCxSR);
if (!(status & SCxSR_TDxE(port))) {
- ctrl = sci_in(port, SCSCR);
+ ctrl = serial_port_in(port, SCSCR);
if (uart_circ_empty(xmit))
ctrl &= ~SCSCR_TIE;
else
ctrl |= SCSCR_TIE;
- sci_out(port, SCSCR, ctrl);
+ serial_port_out(port, SCSCR, ctrl);
return;
}
@@ -573,27 +627,27 @@ static void sci_transmit_chars(struct uart_port *port)
break;
}
- sci_out(port, SCxTDR, c);
+ serial_port_out(port, SCxTDR, c);
port->icount.tx++;
} while (--count > 0);
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+ serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit)) {
sci_stop_tx(port);
} else {
- ctrl = sci_in(port, SCSCR);
+ ctrl = serial_port_in(port, SCSCR);
if (port->type != PORT_SCI) {
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+ serial_port_in(port, SCxSR); /* Dummy read */
+ serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
}
ctrl |= SCSCR_TIE;
- sci_out(port, SCSCR, ctrl);
+ serial_port_out(port, SCSCR, ctrl);
}
}
@@ -603,35 +657,35 @@ static void sci_transmit_chars(struct uart_port *port)
static void sci_receive_chars(struct uart_port *port)
{
struct sci_port *sci_port = to_sci_port(port);
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
int i, count, copied = 0;
unsigned short status;
unsigned char flag;
- status = sci_in(port, SCxSR);
+ status = serial_port_in(port, SCxSR);
if (!(status & SCxSR_RDxF(port)))
return;
while (1) {
/* Don't copy more bytes than there is room for in the buffer */
- count = tty_buffer_request_room(tty, sci_rxfill(port));
+ count = tty_buffer_request_room(tport, sci_rxfill(port));
/* If for any reason we can't copy more data, we're done! */
if (count == 0)
break;
if (port->type == PORT_SCI) {
- char c = sci_in(port, SCxRDR);
+ char c = serial_port_in(port, SCxRDR);
if (uart_handle_sysrq_char(port, c) ||
sci_port->break_flag)
count = 0;
else
- tty_insert_flip_char(tty, c, TTY_NORMAL);
+ tty_insert_flip_char(tport, c, TTY_NORMAL);
} else {
for (i = 0; i < count; i++) {
- char c = sci_in(port, SCxRDR);
+ char c = serial_port_in(port, SCxRDR);
- status = sci_in(port, SCxSR);
+ status = serial_port_in(port, SCxSR);
#if defined(CONFIG_CPU_SH3)
/* Skip "chars" during break */
if (sci_port->break_flag) {
@@ -668,12 +722,12 @@ static void sci_receive_chars(struct uart_port *port)
} else
flag = TTY_NORMAL;
- tty_insert_flip_char(tty, c, flag);
+ tty_insert_flip_char(tport, c, flag);
}
}
- sci_in(port, SCxSR); /* dummy read */
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+ serial_port_in(port, SCxSR); /* dummy read */
+ serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
copied += count;
port->icount.rx += count;
@@ -681,10 +735,10 @@ static void sci_receive_chars(struct uart_port *port)
if (copied) {
/* Tell the rest of the system the news. New characters! */
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
} else {
- sci_in(port, SCxSR); /* dummy read */
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+ serial_port_in(port, SCxSR); /* dummy read */
+ serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
}
}
@@ -708,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);
@@ -719,30 +771,24 @@ 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)
{
int copied = 0;
- unsigned short status = sci_in(port, SCxSR);
- struct tty_struct *tty = port->state->port.tty;
+ unsigned short status = serial_port_in(port, SCxSR);
+ 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(tty, 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)) {
@@ -762,7 +808,7 @@ static int sci_handle_errors(struct uart_port *port)
dev_dbg(port->dev, "BREAK detected\n");
- if (tty_insert_flip_char(tty, 0, TTY_BREAK))
+ if (tty_insert_flip_char(tport, 0, TTY_BREAK))
copied++;
}
@@ -770,7 +816,7 @@ static int sci_handle_errors(struct uart_port *port)
/* frame error */
port->icount.frame++;
- if (tty_insert_flip_char(tty, 0, TTY_FRAME))
+ if (tty_insert_flip_char(tport, 0, TTY_FRAME))
copied++;
dev_notice(port->dev, "frame error\n");
@@ -781,21 +827,21 @@ static int sci_handle_errors(struct uart_port *port)
/* parity error */
port->icount.parity++;
- if (tty_insert_flip_char(tty, 0, TTY_PARITY))
+ 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)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
return copied;
}
static int sci_handle_fifo_overrun(struct uart_port *port)
{
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
struct sci_port *s = to_sci_port(port);
struct plat_sci_reg *reg;
int copied = 0;
@@ -804,13 +850,13 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
if (!reg->size)
return 0;
- if ((sci_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) {
- sci_out(port, SCLSR, 0);
+ if ((serial_port_in(port, SCLSR) & (1 << s->overrun_bit))) {
+ serial_port_out(port, SCLSR, 0);
port->icount.overrun++;
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
- tty_flip_buffer_push(tty);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+ tty_flip_buffer_push(tport);
dev_notice(port->dev, "overrun error\n");
copied++;
@@ -822,8 +868,8 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
static int sci_handle_breaks(struct uart_port *port)
{
int copied = 0;
- unsigned short status = sci_in(port, SCxSR);
- struct tty_struct *tty = port->state->port.tty;
+ unsigned short status = serial_port_in(port, SCxSR);
+ struct tty_port *tport = &port->state->port;
struct sci_port *s = to_sci_port(port);
if (uart_handle_break(port))
@@ -838,14 +884,14 @@ static int sci_handle_breaks(struct uart_port *port)
port->icount.brk++;
/* Notify of BREAK */
- if (tty_insert_flip_char(tty, 0, TTY_BREAK))
+ if (tty_insert_flip_char(tport, 0, TTY_BREAK))
copied++;
dev_dbg(port->dev, "BREAK detected\n");
}
if (copied)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
copied += sci_handle_fifo_overrun(port);
@@ -859,19 +905,19 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
struct sci_port *s = to_sci_port(port);
if (s->chan_rx) {
- u16 scr = sci_in(port, SCSCR);
- u16 ssr = sci_in(port, SCxSR);
+ u16 scr = serial_port_in(port, SCSCR);
+ u16 ssr = serial_port_in(port, SCxSR);
/* 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;
}
- sci_out(port, SCSCR, scr);
+ serial_port_out(port, SCSCR, scr);
/* Clear current interrupt */
- sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port)));
+ serial_port_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port)));
dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n",
jiffies, s->rx_timeout);
mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
@@ -909,15 +955,15 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
if (port->type == PORT_SCI) {
if (sci_handle_errors(port)) {
/* discard character in rx buffer */
- sci_in(port, SCxSR);
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+ serial_port_in(port, SCxSR);
+ serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
}
} else {
sci_handle_fifo_overrun(port);
sci_rx_interrupt(irq, ptr);
}
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
+ serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
/* Kick the transmission */
sci_tx_interrupt(irq, ptr);
@@ -931,7 +977,7 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr)
/* Handle BREAKs */
sci_handle_breaks(port);
- sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
+ serial_port_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
return IRQ_HANDLED;
}
@@ -955,8 +1001,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
struct sci_port *s = to_sci_port(port);
irqreturn_t ret = IRQ_NONE;
- ssr_status = sci_in(port, SCxSR);
- scr_status = sci_in(port, SCSCR);
+ ssr_status = serial_port_in(port, SCxSR);
+ scr_status = serial_port_in(port, SCSCR);
err_enabled = scr_status & port_rx_irq_mask(port);
/* Tx Interrupt */
@@ -995,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);
@@ -1050,13 +1095,21 @@ 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];
+ } else {
+ irq = port->irqs[i];
+
+ /*
+ * Certain port types won't support all of the
+ * available interrupt sources.
+ */
+ if (unlikely(irq < 0))
+ continue;
+ }
desc = sci_irq_desc + i;
port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s",
@@ -1079,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)
@@ -1097,80 +1150,28 @@ static void sci_free_irq(struct sci_port *port)
* IRQ first.
*/
for (i = 0; i < SCIx_NR_IRQS; i++) {
- free_irq(port->cfg->irqs[i], port);
- kfree(port->irqstr[i]);
-
- if (SCIx_IRQ_IS_MUXED(port)) {
- /* If there's only one IRQ, we're done. */
- return;
- }
- }
-}
-
-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 __devinit 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);
+ int irq = port->irqs[i];
/*
- * If we've failed the allocation, we can still continue
- * on with a NULL string.
+ * Certain port types won't support all of the available
+ * interrupt sources.
*/
- if (!port->gpiostr[i])
- dev_notice(up->dev, "%s string allocation failure\n",
- desc);
+ if (unlikely(irq < 0))
+ continue;
- ret = gpio_request(port->cfg->gpios[i], port->gpiostr[i]);
- if (unlikely(ret != 0)) {
- dev_notice(up->dev, "failed %s gpio request\n", desc);
+ free_irq(port->irqs[i], port);
+ kfree(port->irqstr[i]);
- /*
- * If we can't get the GPIO for whatever reason,
- * no point in keeping the verbose string around.
- */
- kfree(port->gpiostr[i]);
+ if (SCIx_IRQ_IS_MUXED(port)) {
+ /* If there's only one IRQ, we're done. */
+ return;
}
}
}
-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 = sci_in(port, SCxSR);
+ unsigned short status = serial_port_in(port, SCxSR);
unsigned short in_tx_fifo = sci_txfill(port);
return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
@@ -1198,7 +1199,9 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
*/
reg = sci_getreg(port, SCFCR);
if (reg->size)
- sci_out(port, SCFCR, sci_in(port, SCFCR) | 1);
+ serial_port_out(port, SCFCR,
+ serial_port_in(port, SCFCR) |
+ SCFCR_LOOP);
}
}
@@ -1229,30 +1232,33 @@ static void sci_dma_tx_complete(void *arg)
port->icount.tx += sg_dma_len(&s->sg_tx);
async_tx_ack(s->desc_tx);
- s->cookie_tx = -EINVAL;
s->desc_tx = NULL;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (!uart_circ_empty(xmit)) {
+ s->cookie_tx = 0;
schedule_work(&s->work_tx);
- } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
- u16 ctrl = sci_in(port, SCSCR);
- sci_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+ } else {
+ s->cookie_tx = -EINVAL;
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+ u16 ctrl = serial_port_in(port, SCSCR);
+ serial_port_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+ }
}
spin_unlock_irqrestore(&port->lock, flags);
}
/* Locking: called with port lock held */
-static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty,
- size_t count)
+static int sci_dma_rx_push(struct sci_port *s, size_t count)
{
struct uart_port *port = &s->port;
+ struct tty_port *tport = &port->state->port;
int i, active, room;
- room = tty_buffer_request_room(tty, count);
+ room = tty_buffer_request_room(tport, count);
if (s->active_rx == s->cookie_rx[0]) {
active = 0;
@@ -1264,13 +1270,13 @@ static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty,
}
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;
for (i = 0; i < room; i++)
- tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i],
+ tty_insert_flip_char(tport, ((u8 *)sg_virt(&s->sg_rx[active]))[i],
TTY_NORMAL);
port->icount.rx += room;
@@ -1282,22 +1288,22 @@ static void sci_dma_rx_complete(void *arg)
{
struct sci_port *s = arg;
struct uart_port *port = &s->port;
- struct tty_struct *tty = port->state->port.tty;
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);
- count = sci_dma_rx_push(s, tty, s->buf_len_rx);
+ count = sci_dma_rx_push(s, s->buf_len_rx);
mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
spin_unlock_irqrestore(&port->lock, flags);
if (count)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&port->state->port);
schedule_work(&s->work_rx);
}
@@ -1338,7 +1344,7 @@ static void sci_submit_rx(struct sci_port *s)
struct scatterlist *sg = &s->sg_rx[i];
struct dma_async_tx_descriptor *desc;
- desc = chan->device->device_prep_slave_sg(chan,
+ desc = dmaengine_prep_slave_sg(chan,
sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (desc) {
@@ -1362,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];
@@ -1389,25 +1395,24 @@ 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 tty_struct *tty = port->state->port.tty;
struct dma_chan *chan = s->chan_rx;
- struct sh_desc *sh_desc = container_of(desc, struct sh_desc,
- async_tx);
+ struct shdma_desc *sh_desc = container_of(desc,
+ struct shdma_desc, async_tx);
unsigned long flags;
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);
- count = sci_dma_rx_push(s, tty, sh_desc->partial);
+ count = sci_dma_rx_push(s, sh_desc->partial);
spin_unlock_irqrestore(&port->lock, flags);
if (count)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&port->state->port);
sci_submit_rx(s);
@@ -1423,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)
@@ -1453,7 +1458,7 @@ static void work_fn_tx(struct work_struct *work)
BUG_ON(!sg_dma_len(sg));
- desc = chan->device->device_prep_slave_sg(chan,
+ desc = dmaengine_prep_slave_sg(chan,
sg, s->sg_len_tx, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
@@ -1477,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);
}
@@ -1491,24 +1496,26 @@ static void sci_start_tx(struct uart_port *port)
#ifdef CONFIG_SERIAL_SH_SCI_DMA
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
- u16 new, scr = sci_in(port, SCSCR);
+ 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)
- sci_out(port, SCSCR, new);
+ serial_port_out(port, SCSCR, new);
}
if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
- s->cookie_tx < 0)
+ s->cookie_tx < 0) {
+ s->cookie_tx = 0;
schedule_work(&s->work_tx);
+ }
#endif
if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
- ctrl = sci_in(port, SCSCR);
- sci_out(port, SCSCR, ctrl | SCSCR_TIE);
+ ctrl = serial_port_in(port, SCSCR);
+ serial_port_out(port, SCSCR, ctrl | SCSCR_TIE);
}
}
@@ -1517,40 +1524,40 @@ static void sci_stop_tx(struct uart_port *port)
unsigned short ctrl;
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
- ctrl = sci_in(port, SCSCR);
+ ctrl = serial_port_in(port, SCSCR);
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
- ctrl &= ~0x8000;
+ ctrl &= ~SCSCR_TDRQE;
ctrl &= ~SCSCR_TIE;
- sci_out(port, SCSCR, ctrl);
+ serial_port_out(port, SCSCR, ctrl);
}
static void sci_start_rx(struct uart_port *port)
{
unsigned short ctrl;
- ctrl = sci_in(port, SCSCR) | port_rx_irq_mask(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;
- sci_out(port, SCSCR, ctrl);
+ serial_port_out(port, SCSCR, ctrl);
}
static void sci_stop_rx(struct uart_port *port)
{
unsigned short ctrl;
- ctrl = sci_in(port, SCSCR);
+ 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);
- sci_out(port, SCSCR, ctrl);
+ serial_port_out(port, SCSCR, ctrl);
}
static void sci_enable_ms(struct uart_port *port)
@@ -1562,10 +1569,32 @@ static void sci_enable_ms(struct uart_port *port)
static void sci_break_ctl(struct uart_port *port, int break_state)
{
- /*
- * Not supported by hardware. Most parts couple break and rx
- * interrupts together, with break detection always enabled.
- */
+ struct sci_port *s = to_sci_port(port);
+ struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
+ unsigned short scscr, scsptr;
+
+ /* check wheter the port has SCSPTR */
+ if (!reg->size) {
+ /*
+ * Not supported by hardware. Most parts couple break and rx
+ * interrupts together, with break detection always enabled.
+ */
+ return;
+ }
+
+ scsptr = serial_port_in(port, SCSPTR);
+ scscr = serial_port_in(port, SCSCR);
+
+ if (break_state == -1) {
+ scsptr = (scsptr | SCSPTR_SPB2IO) & ~SCSPTR_SPB2DT;
+ scscr &= ~SCSCR_TE;
+ } else {
+ scsptr = (scsptr | SCSPTR_SPB2DT) & ~SCSPTR_SPB2IO;
+ scscr |= SCSCR_TE;
+ }
+
+ serial_port_out(port, SCSPTR, scsptr);
+ serial_port_out(port, SCSCR, scscr);
}
#ifdef CONFIG_SERIAL_SH_SCI_DMA
@@ -1573,10 +1602,10 @@ 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->slave_id);
+ dev_dbg(chan->device->dev, "%s: slave ID %d\n",
+ __func__, param->shdma_slave.slave_id);
- chan->private = param;
+ chan->private = &param->shdma_slave;
return true;
}
@@ -1584,13 +1613,13 @@ static void rx_timer_fn(unsigned long arg)
{
struct sci_port *s = (struct sci_port *)arg;
struct uart_port *port = &s->port;
- u16 scr = sci_in(port, SCSCR);
+ 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]);
}
- sci_out(port, SCSCR, scr | SCSCR_RIE);
+ serial_port_out(port, SCSCR, scr | SCSCR_RIE);
dev_dbg(port->dev, "DMA Rx timed out\n");
schedule_work(&s->work_rx);
}
@@ -1603,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;
@@ -1615,7 +1643,7 @@ static void sci_request_dma(struct uart_port *port)
param = &s->param_tx;
/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */
- param->slave_id = s->cfg->dma_slave_tx;
+ param->shdma_slave.slave_id = s->cfg->dma_slave_tx;
s->cookie_tx = -EINVAL;
chan = dma_request_channel(mask, filter, param);
@@ -1624,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;
@@ -1643,7 +1673,7 @@ static void sci_request_dma(struct uart_port *port)
param = &s->param_rx;
/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */
- param->slave_id = s->cfg->dma_slave_rx;
+ param->shdma_slave.slave_id = s->cfg->dma_slave_rx;
chan = dma_request_channel(mask, filter, param);
dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan);
@@ -1673,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];
}
@@ -1706,22 +1736,21 @@ static inline void sci_free_dma(struct uart_port *port)
static int sci_startup(struct uart_port *port)
{
struct sci_port *s = to_sci_port(port);
+ unsigned long flags;
int ret;
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
- pm_runtime_put_noidle(port->dev);
-
- sci_port_enable(s);
-
ret = sci_request_irq(s);
if (unlikely(ret < 0))
return ret;
sci_request_dma(port);
+ spin_lock_irqsave(&port->lock, flags);
sci_start_tx(port);
sci_start_rx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
@@ -1729,35 +1758,24 @@ static int sci_startup(struct uart_port *port)
static void sci_shutdown(struct uart_port *port)
{
struct sci_port *s = to_sci_port(port);
+ unsigned long flags;
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+ spin_lock_irqsave(&port->lock, flags);
sci_stop_rx(port);
sci_stop_tx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
sci_free_dma(port);
sci_free_irq(s);
-
- sci_port_disable(s);
-
- pm_runtime_get_noresume(port->dev);
}
-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);
@@ -1765,20 +1783,56 @@ static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps,
return ((freq + 16 * bps) / (32 * bps) - 1);
}
+/* calculate sample rate, BRR, and clock select for HSCIF */
+static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq,
+ int *brr, unsigned int *srr,
+ unsigned int *cks)
+{
+ int sr, c, br, err;
+ int min_err = 1000; /* 100% */
+
+ /* Find the combination of sample rate and clock select with the
+ smallest deviation from the desired baud rate. */
+ for (sr = 8; sr <= 32; sr++) {
+ for (c = 0; c <= 3; c++) {
+ /* integerized formulas from HSCIF documentation */
+ br = freq / (sr * (1 << (2 * c + 1)) * bps) - 1;
+ if (br < 0 || br > 255)
+ continue;
+ err = freq / ((br + 1) * bps * sr *
+ (1 << (2 * c + 1)) / 1000) - 1000;
+ if (min_err > err) {
+ min_err = err;
+ *brr = br;
+ *srr = sr - 1;
+ *cks = c;
+ }
+ }
+ }
+
+ if (min_err == 1000) {
+ WARN_ON(1);
+ /* use defaults */
+ *brr = 255;
+ *srr = 15;
+ *cks = 0;
+ }
+}
+
static void sci_reset(struct uart_port *port)
{
struct plat_sci_reg *reg;
unsigned int status;
do {
- status = sci_in(port, SCxSR);
+ status = serial_port_in(port, SCxSR);
} while (!(status & SCxSR_TEND(port)));
- sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */
+ serial_port_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */
reg = sci_getreg(port, SCFCR);
if (reg->size)
- sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST);
+ serial_port_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST);
}
static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
@@ -1786,8 +1840,9 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
{
struct sci_port *s = to_sci_port(port);
struct plat_sci_reg *reg;
- unsigned int baud, smr_val, max_baud;
+ unsigned int baud, smr_val, max_baud, cks = 0;
int t = -1;
+ unsigned int srr = 15;
/*
* earlyprintk comes here early on with port->uartclk set to zero.
@@ -1800,47 +1855,52 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
max_baud = port->uartclk ? port->uartclk / 16 : 115200;
baud = uart_get_baud_rate(port, termios, old, 0, max_baud);
- if (likely(baud && port->uartclk))
- t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, port->uartclk);
+ if (likely(baud && port->uartclk)) {
+ if (s->cfg->type == PORT_HSCIF) {
+ sci_baud_calc_hscif(baud, port->uartclk, &t, &srr,
+ &cks);
+ } else {
+ t = sci_scbrr_calc(s, baud, port->uartclk);
+ for (cks = 0; t >= 256 && cks <= 3; cks++)
+ t >>= 2;
+ }
+ }
sci_port_enable(s);
sci_reset(port);
- smr_val = sci_in(port, SCSMR) & 3;
+ 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);
- sci_out(port, SCSMR, smr_val);
-
- dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t,
- s->cfg->scscr);
-
- if (t > 0) {
- if (t >= 256) {
- sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1);
- t >>= 2;
- } else
- sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3);
+ dev_dbg(port->dev, "%s: SMR %x, cks %x, t %x, SCSCR %x\n",
+ __func__, smr_val, cks, t, s->cfg->scscr);
- sci_out(port, SCBRR, t);
+ if (t >= 0) {
+ serial_port_out(port, SCSMR, (smr_val & ~SCSMR_CKS) | cks);
+ serial_port_out(port, SCBRR, t);
+ reg = sci_getreg(port, HSSRR);
+ if (reg->size)
+ serial_port_out(port, HSSRR, srr | HSCIF_SRE);
udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */
- }
+ } else
+ serial_port_out(port, SCSMR, smr_val);
sci_init_pins(port, termios->c_cflag);
reg = sci_getreg(port, SCFCR);
if (reg->size) {
- unsigned short ctrl = sci_in(port, SCFCR);
+ unsigned short ctrl = serial_port_in(port, SCFCR);
if (s->cfg->capabilities & SCIx_HAVE_RTSCTS) {
if (termios->c_cflag & CRTSCTS)
@@ -1856,10 +1916,10 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
*/
ctrl &= ~(SCFCR_RFRST | SCFCR_TFRST);
- sci_out(port, SCFCR, ctrl);
+ serial_port_out(port, SCFCR, ctrl);
}
- sci_out(port, SCSCR, s->cfg->scscr);
+ serial_port_out(port, SCSCR, s->cfg->scscr);
#ifdef CONFIG_SERIAL_SH_SCI_DMA
/*
@@ -1875,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);
@@ -1889,6 +1948,21 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
sci_port_disable(s);
}
+static void sci_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct sci_port *sci_port = to_sci_port(port);
+
+ switch (state) {
+ case UART_PM_STATE_OFF:
+ sci_port_disable(sci_port);
+ break;
+ default:
+ sci_port_enable(sci_port);
+ break;
+ }
+}
+
static const char *sci_type(struct uart_port *port)
{
switch (port->type) {
@@ -1902,6 +1976,8 @@ static const char *sci_type(struct uart_port *port)
return "scifa";
case PORT_SCIFB:
return "scifb";
+ case PORT_HSCIF:
+ return "hscif";
}
return NULL;
@@ -1915,7 +1991,10 @@ static inline unsigned long sci_port_size(struct uart_port *port)
* from platform resource data at such a time that ports begin to
* behave more erratically.
*/
- return 64;
+ if (port->type == PORT_HSCIF)
+ return 96;
+ else
+ return 64;
}
static int sci_remap_port(struct uart_port *port)
@@ -1940,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;
@@ -1987,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;
@@ -2010,6 +2085,7 @@ static struct uart_ops sci_uart_ops = {
.startup = sci_startup,
.shutdown = sci_shutdown,
.set_termios = sci_set_termios,
+ .pm = sci_pm,
.type = sci_type,
.release_port = sci_release_port,
.request_port = sci_request_port,
@@ -2021,12 +2097,14 @@ static struct uart_ops sci_uart_ops = {
#endif
};
-static int __devinit sci_init_single(struct platform_device *dev,
- struct sci_port *sci_port,
- unsigned int index,
- struct plat_sci_port *p)
+static int sci_init_single(struct platform_device *dev,
+ 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;
@@ -2035,28 +2113,76 @@ static int __devinit 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");
@@ -2076,10 +2202,6 @@ static int __devinit sci_init_single(struct platform_device *dev,
port->dev = &dev->dev;
- sci_init_gpios(sci_port);
-
- pm_runtime_irq_safe(&dev->dev);
- pm_runtime_get_noresume(&dev->dev);
pm_runtime_enable(&dev->dev);
}
@@ -2090,32 +2212,22 @@ static int __devinit 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;
/*
@@ -2125,7 +2237,7 @@ static int __devinit 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;
@@ -2138,6 +2250,14 @@ static int __devinit sci_init_single(struct platform_device *dev,
return 0;
}
+static void sci_cleanup_single(struct sci_port *port)
+{
+ clk_put(port->iclk);
+ clk_put(port->fclk);
+
+ pm_runtime_disable(port->port.dev);
+}
+
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static void serial_console_putchar(struct uart_port *port, int ch)
{
@@ -2153,21 +2273,38 @@ static void serial_console_write(struct console *co, const char *s,
{
struct sci_port *sci_port = &sci_ports[co->index];
struct uart_port *port = &sci_port->port;
- unsigned short bits;
+ unsigned short bits, ctrl;
+ unsigned long flags;
+ int locked = 1;
- sci_port_enable(sci_port);
+ local_irq_save(flags);
+ if (port->sysrq)
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock(&port->lock);
+ else
+ spin_lock(&port->lock);
+
+ /* first save the SCSCR then disable the interrupts */
+ ctrl = serial_port_in(port, SCSCR);
+ serial_port_out(port, SCSCR, sci_port->cfg->scscr);
uart_console_write(port, s, count, serial_console_putchar);
/* wait until fifo is empty and last bit has been transmitted */
bits = SCxSR_TDxE(port) | SCxSR_TEND(port);
- while ((sci_in(port, SCxSR) & bits) != bits)
+ while ((serial_port_in(port, SCxSR) & bits) != bits)
cpu_relax();
- sci_port_disable(sci_port);
+ /* restore the SCSCR */
+ serial_port_out(port, SCSCR, ctrl);
+
+ if (locked)
+ spin_unlock(&port->lock);
+ local_irq_restore(flags);
}
-static int __devinit serial_console_setup(struct console *co, char *options)
+static int serial_console_setup(struct console *co, char *options)
{
struct sci_port *sci_port;
struct uart_port *port;
@@ -2196,13 +2333,9 @@ static int __devinit serial_console_setup(struct console *co, char *options)
if (unlikely(ret != 0))
return ret;
- sci_port_enable(sci_port);
-
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
- sci_port_disable(sci_port);
-
return uart_set_options(port, co, baud, parity, bits, flow);
}
@@ -2225,16 +2358,16 @@ static struct console early_serial_console = {
static char early_serial_buf[32];
-static int __devinit sci_probe_earlyprintk(struct platform_device *pdev)
+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);
@@ -2245,62 +2378,19 @@ static int __devinit sci_probe_earlyprintk(struct platform_device *pdev)
return 0;
}
-#define uart_console(port) ((port)->cons->index == (port)->line)
-
-static int sci_runtime_suspend(struct device *dev)
-{
- struct sci_port *sci_port = dev_get_drvdata(dev);
- struct uart_port *port = &sci_port->port;
-
- if (uart_console(port)) {
- struct plat_sci_reg *reg;
-
- sci_port->saved_smr = sci_in(port, SCSMR);
- sci_port->saved_brr = sci_in(port, SCBRR);
-
- reg = sci_getreg(port, SCFCR);
- if (reg->size)
- sci_port->saved_fcr = sci_in(port, SCFCR);
- else
- sci_port->saved_fcr = 0;
- }
- return 0;
-}
-
-static int sci_runtime_resume(struct device *dev)
-{
- struct sci_port *sci_port = dev_get_drvdata(dev);
- struct uart_port *port = &sci_port->port;
-
- if (uart_console(port)) {
- sci_reset(port);
- sci_out(port, SCSMR, sci_port->saved_smr);
- sci_out(port, SCBRR, sci_port->saved_brr);
-
- if (sci_port->saved_fcr)
- sci_out(port, SCFCR, sci_port->saved_fcr);
-
- sci_out(port, SCSCR, sci_port->cfg->scscr);
- }
- return 0;
-}
-
#define SCI_CONSOLE (&serial_console)
#else
-static inline int __devinit sci_probe_earlyprintk(struct platform_device *pdev)
+static inline int sci_probe_earlyprintk(struct platform_device *pdev)
{
return -EINVAL;
}
#define SCI_CONSOLE NULL
-#define sci_runtime_suspend NULL
-#define sci_runtime_resume NULL
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
-static char banner[] __initdata =
- KERN_INFO "SuperH 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,
@@ -2319,18 +2409,91 @@ static int sci_remove(struct platform_device *dev)
cpufreq_unregister_notifier(&port->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
- sci_free_gpios(port);
-
uart_remove_one_port(&sci_uart_driver, &port->port);
- clk_put(port->iclk);
- clk_put(port->fclk);
+ sci_cleanup_single(port);
- pm_runtime_disable(&dev->dev);
return 0;
}
-static int __devinit sci_probe_single(struct platform_device *dev,
+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,
struct sci_port *sciport)
@@ -2339,25 +2502,30 @@ static int __devinit 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");
- return 0;
+ 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;
- return uart_add_one_port(&sci_uart_driver, &sciport->port);
+ ret = uart_add_one_port(&sci_uart_driver, &sciport->port);
+ if (ret) {
+ sci_cleanup_single(sciport);
+ return ret;
+ }
+
+ return 0;
}
-static int __devinit sci_probe(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;
/*
@@ -2368,28 +2536,42 @@ static int __devinit 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)
- goto err_unreg;
+ return ret;
sp->freq_transition.notifier_call = sci_notifier;
ret = cpufreq_register_notifier(&sp->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
- if (unlikely(ret < 0))
- goto err_unreg;
+ if (unlikely(ret < 0)) {
+ uart_remove_one_port(&sci_uart_driver, &sp->port);
+ sci_cleanup_single(sp);
+ return ret;
+ }
#ifdef CONFIG_SH_STANDARD_BIOS
sh_bios_gdb_detach();
#endif
return 0;
-
-err_unreg:
- sci_remove(dev);
- return ret;
}
static int sci_suspend(struct device *dev)
@@ -2413,8 +2595,6 @@ static int sci_resume(struct device *dev)
}
static const struct dev_pm_ops sci_dev_pm_ops = {
- .runtime_suspend = sci_runtime_suspend,
- .runtime_resume = sci_runtime_resume,
.suspend = sci_suspend,
.resume = sci_resume,
};
@@ -2426,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),
},
};
@@ -2433,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)) {
@@ -2461,4 +2642,4 @@ module_exit(sci_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sh-sci");
MODULE_AUTHOR("Paul Mundt");
-MODULE_DESCRIPTION("SuperH SCI(F) serial driver");
+MODULE_DESCRIPTION("SuperH (H)SCI(F) serial driver");
diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h
index a1a2d364f92..d5db81a0a43 100644
--- a/drivers/tty/serial/sh-sci.h
+++ b/drivers/tty/serial/sh-sci.h
@@ -9,21 +9,19 @@
#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) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH73A0) || \
- defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
defined(CONFIG_ARCH_SH7372) || \
defined(CONFIG_ARCH_R8A7740)
-# define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc)
-# define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73)
-# define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf)
-# define SCxSR_BREAK_CLEAR(port) (sci_in(port, SCxSR) & 0xffe3)
+# define SCxSR_RDxF_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfffc)
+# define SCxSR_ERROR_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfd73)
+# define SCxSR_TDxE_CLEAR(port) (serial_port_in(port, SCxSR) & 0xffdf)
+# define SCxSR_BREAK_CLEAR(port) (serial_port_in(port, SCxSR) & 0xffe3)
#else
# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc)
# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073)
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index a60523fee11..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/pinmux.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},
@@ -75,6 +81,27 @@ static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = {
.line = 2,
},
},
+ [3] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 3,
+ },
+ },
+ [4] = {
+ .port = {
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF,
+ .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)
@@ -85,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;
@@ -113,94 +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;
struct tty_struct *tty;
-
tty = tty_port_tty_get(&port->state->port);
if (!tty)
return -ENODEV;
-
- while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) &
- SIRFUART_FIFOEMPTY_MASK(port))) {
- ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ;
+ 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);
@@ -209,9 +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(tty);
- tty_kref_put(tty);
return rx_count;
}
@@ -220,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++;
@@ -236,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;
@@ -243,75 +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;
- 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) {
+ spin_lock(&port->lock);
+ 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);
- 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)) {
- 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;
@@ -334,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;
@@ -345,65 +845,93 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
struct ktermios *old)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
- unsigned long ioclk_rate;
+ 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;
- ioclk_rate = 150000000;
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)
@@ -412,72 +940,107 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
if (sirfport->ms_enabled)
sirfsoc_uart_disable_ms(port);
}
-
- /* common rate: fast calculation */
- 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, ioclk_rate,
- &setted_baud);
- wr_regl(port, SIRFUART_DIVISOR, clk_div_reg);
-
+ 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;
+ }
+ 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);
@@ -491,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;
}
@@ -500,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)
@@ -515,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;
}
@@ -546,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,
@@ -553,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,
@@ -620,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;
-
- if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL))
- sirfport->hw_flow_ctrl = 1;
+ 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 ((!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",
@@ -657,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) {
@@ -668,18 +1373,16 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
if (res == NULL) {
dev_err(&pdev->dev, "Insufficient resources.\n");
ret = -EFAULT;
- goto irq_err;
+ goto err;
}
port->irq = res->start;
- if (sirfport->hw_flow_ctrl) {
- sirfport->pmx = pinmux_get(&pdev->dev, NULL);
- ret = IS_ERR(sirfport->pmx);
- if (ret)
- goto pmx_err;
-
- pinmux_enable(sirfport->pmx);
+ sirfport->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sirfport->clk)) {
+ ret = PTR_ERR(sirfport->clk);
+ goto err;
}
+ port->uartclk = clk_get_rate(sirfport->clk);
port->ops = &sirfsoc_uart_ops;
spin_lock_init(&port->lock);
@@ -691,17 +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:
- platform_set_drvdata(pdev, NULL);
- if (sirfport->hw_flow_ctrl) {
- pinmux_disable(sirfport->pmx);
- pinmux_put(sirfport->pmx);
- }
-pmx_err:
-irq_err:
- devm_iounmap(&pdev->dev, port->membase);
+ clk_put(sirfport->clk);
err:
return ret;
}
@@ -710,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) {
- pinmux_disable(sirfport->pmx);
- pinmux_put(sirfport->pmx);
- }
- devm_iounmap(&pdev->dev, port->membase);
+ 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[] __devinitdata = {
- { .compatible = "sirf,prima2-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_serial_of_match);
static struct platform_driver sirfsoc_uart_driver = {
.probe = sirfsoc_uart_probe,
- .remove = __devexit_p(sirfsoc_uart_remove),
- .suspend = sirfsoc_uart_suspend,
- .resume = sirfsoc_uart_resume,
+ .remove = sirfsoc_uart_remove,
.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 fc64260fa93..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 3
-#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,28 +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 pinmux *pmx;
-};
+ 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/sn_console.c b/drivers/tty/serial/sn_console.c
index 238c7df73ef..f51ffdc696f 100644
--- a/drivers/tty/serial/sn_console.c
+++ b/drivers/tty/serial/sn_console.c
@@ -457,26 +457,22 @@ static int sn_debug_printf(const char *fmt, ...)
static void
sn_receive_chars(struct sn_cons_port *port, unsigned long flags)
{
+ struct tty_port *tport = NULL;
int ch;
- struct tty_struct *tty;
if (!port) {
- printk(KERN_ERR "sn_receive_chars - port NULL so can't receieve\n");
+ printk(KERN_ERR "sn_receive_chars - port NULL so can't receive\n");
return;
}
if (!port->sc_ops) {
- printk(KERN_ERR "sn_receive_chars - port->sc_ops NULL so can't receieve\n");
+ printk(KERN_ERR "sn_receive_chars - port->sc_ops NULL so can't receive\n");
return;
}
if (port->sc_port.state) {
/* The serial_core stuffs are initialized, use them */
- tty = port->sc_port.state->port.tty;
- }
- else {
- /* Not registered yet - can't pass to tty layer. */
- tty = NULL;
+ tport = &port->sc_port.state->port;
}
while (port->sc_ops->sal_input_pending()) {
@@ -516,15 +512,15 @@ sn_receive_chars(struct sn_cons_port *port, unsigned long flags)
#endif /* CONFIG_MAGIC_SYSRQ */
/* record the character to pass up to the tty layer */
- if (tty) {
- if(tty_insert_flip_char(tty, ch, TTY_NORMAL) == 0)
+ if (tport) {
+ if (tty_insert_flip_char(tport, ch, TTY_NORMAL) == 0)
break;
}
port->sc_port.icount.rx++;
}
- if (tty)
- tty_flip_buffer_push(tty);
+ if (tport)
+ tty_flip_buffer_push(tport);
}
/**
@@ -743,6 +739,7 @@ static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port)
spin_lock_irqsave(&port->sc_port.lock, flags);
port->sc_port.irq = SGI_UART_VECTOR;
port->sc_ops = &intr_ops;
+ irq_set_handler(port->sc_port.irq, handle_level_irq);
/* turn on receive interrupts */
ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV);
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/suncore.c b/drivers/tty/serial/suncore.c
index 6381a0282ee..6e4ac8db2d7 100644
--- a/drivers/tty/serial/suncore.c
+++ b/drivers/tty/serial/suncore.c
@@ -17,11 +17,11 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
#include <linux/init.h>
#include <asm/prom.h>
-#include "suncore.h"
static int sunserial_current_minor = 64;
diff --git a/drivers/tty/serial/suncore.h b/drivers/tty/serial/suncore.h
deleted file mode 100644
index db2057936c3..00000000000
--- a/drivers/tty/serial/suncore.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* suncore.h
- *
- * Generic SUN serial/kbd/ms layer. Based entirely
- * upon drivers/sbus/char/sunserial.h which is:
- *
- * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
- *
- * Port to new UART layer is:
- *
- * Copyright (C) 2002 David S. Miller (davem@redhat.com)
- */
-
-#ifndef _SERIAL_SUN_H
-#define _SERIAL_SUN_H
-
-/* Serial keyboard defines for L1-A processing... */
-#define SUNKBD_RESET 0xff
-#define SUNKBD_L1 0x01
-#define SUNKBD_UP 0x80
-#define SUNKBD_A 0x4d
-
-extern unsigned int suncore_mouse_baud_cflag_next(unsigned int, int *);
-extern int suncore_mouse_baud_detection(unsigned char, int);
-
-extern int sunserial_register_minors(struct uart_driver *, int);
-extern void sunserial_unregister_minors(struct uart_driver *, int);
-
-extern int sunserial_console_match(struct console *, struct device_node *,
- struct uart_driver *, int, bool);
-extern void sunserial_console_termios(struct console *,
- struct device_node *);
-
-#endif /* !(_SERIAL_SUN_H) */
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index c0b7246d733..dc697cee248 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -23,14 +23,14 @@
#include <asm/spitfire.h>
#include <asm/prom.h>
#include <asm/irq.h>
+#include <asm/setup.h>
#if defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
-
-#include "suncore.h"
+#include <linux/sunserialcore.h>
#define CON_BREAK ((long)-1)
#define CON_HUP ((long)-2)
@@ -72,7 +72,7 @@ static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit)
}
}
-static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty)
+static int receive_chars_getchar(struct uart_port *port)
{
int saw_console_brk = 0;
int limit = 10000;
@@ -99,7 +99,7 @@ static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty)
uart_handle_dcd_change(port, 1);
}
- if (tty == NULL) {
+ if (port->state == NULL) {
uart_handle_sysrq_char(port, c);
continue;
}
@@ -109,13 +109,13 @@ static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty)
if (uart_handle_sysrq_char(port, c))
continue;
- tty_insert_flip_char(tty, c, TTY_NORMAL);
+ tty_insert_flip_char(&port->state->port, c, TTY_NORMAL);
}
return saw_console_brk;
}
-static int receive_chars_read(struct uart_port *port, struct tty_struct *tty)
+static int receive_chars_read(struct uart_port *port)
{
int saw_console_brk = 0;
int limit = 10000;
@@ -152,12 +152,13 @@ static int receive_chars_read(struct uart_port *port, struct tty_struct *tty)
for (i = 0; i < bytes_read; i++)
uart_handle_sysrq_char(port, con_read_page[i]);
- if (tty == NULL)
+ if (port->state == NULL)
continue;
port->icount.rx += bytes_read;
- tty_insert_flip_string(tty, con_read_page, bytes_read);
+ tty_insert_flip_string(&port->state->port, con_read_page,
+ bytes_read);
}
return saw_console_brk;
@@ -165,7 +166,7 @@ static int receive_chars_read(struct uart_port *port, struct tty_struct *tty)
struct sunhv_ops {
void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit);
- int (*receive_chars)(struct uart_port *port, struct tty_struct *tty);
+ int (*receive_chars)(struct uart_port *port);
};
static struct sunhv_ops bychar_ops = {
@@ -180,17 +181,17 @@ static struct sunhv_ops bywrite_ops = {
static struct sunhv_ops *sunhv_ops = &bychar_ops;
-static struct tty_struct *receive_chars(struct uart_port *port)
+static struct tty_port *receive_chars(struct uart_port *port)
{
- struct tty_struct *tty = NULL;
+ struct tty_port *tport = NULL;
if (port->state != NULL) /* Unopened serial console */
- tty = port->state->port.tty;
+ tport = &port->state->port;
- if (sunhv_ops->receive_chars(port, tty))
+ if (sunhv_ops->receive_chars(port))
sun_do_break();
- return tty;
+ return tport;
}
static void transmit_chars(struct uart_port *port)
@@ -213,16 +214,16 @@ static void transmit_chars(struct uart_port *port)
static irqreturn_t sunhv_interrupt(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
- struct tty_struct *tty;
+ struct tty_port *tport;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
- tty = receive_chars(port);
+ tport = receive_chars(port);
transmit_chars(port);
spin_unlock_irqrestore(&port->lock, flags);
- if (tty)
- tty_flip_buffer_push(tty);
+ if (tport)
+ tty_flip_buffer_push(tport);
return IRQ_HANDLED;
}
@@ -432,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);
@@ -469,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)
@@ -491,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) {
@@ -506,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 = {
@@ -519,7 +518,7 @@ static struct console sunhv_console = {
.data = &sunhv_reg,
};
-static int __devinit hv_probe(struct platform_device *op)
+static int hv_probe(struct platform_device *op)
{
struct uart_port *port;
unsigned long minor;
@@ -576,7 +575,7 @@ static int __devinit hv_probe(struct platform_device *op)
if (err)
goto out_remove_port;
- dev_set_drvdata(&op->dev, port);
+ platform_set_drvdata(op, port);
return 0;
@@ -598,9 +597,9 @@ out_free_port:
return err;
}
-static int __devexit hv_remove(struct platform_device *dev)
+static int hv_remove(struct platform_device *dev)
{
- struct uart_port *port = dev_get_drvdata(&dev->dev);
+ struct uart_port *port = platform_get_drvdata(dev);
free_irq(port->irq, port);
@@ -611,8 +610,6 @@ static int __devexit hv_remove(struct platform_device *dev)
kfree(port);
sunhv_port = NULL;
- dev_set_drvdata(&dev->dev, NULL);
-
return 0;
}
@@ -636,7 +633,7 @@ static struct platform_driver hv_driver = {
.of_match_table = hv_match,
},
.probe = hv_probe,
- .remove = __devexit_p(hv_remove),
+ .remove = hv_remove,
};
static int __init sunhv_init(void)
diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
index b5fa2a57b9d..2f57df9a71d 100644
--- a/drivers/tty/serial/sunsab.c
+++ b/drivers/tty/serial/sunsab.c
@@ -37,14 +37,15 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
+#include <asm/setup.h>
#if defined(CONFIG_SERIAL_SUNSAB_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
-#include "suncore.h"
#include "sunsab.h"
struct uart_sunsab_port {
@@ -106,11 +107,11 @@ static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up)
udelay(1);
}
-static struct tty_struct *
+static struct tty_port *
receive_chars(struct uart_sunsab_port *up,
union sab82532_irq_status *stat)
{
- struct tty_struct *tty = NULL;
+ struct tty_port *port = NULL;
unsigned char buf[32];
int saw_console_brk = 0;
int free_fifo = 0;
@@ -118,7 +119,7 @@ receive_chars(struct uart_sunsab_port *up,
int i;
if (up->port.state != NULL) /* Unopened serial console */
- tty = up->port.state->port.tty;
+ port = &up->port.state->port;
/* Read number of BYTES (Character + Status) available. */
if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
@@ -135,7 +136,7 @@ receive_chars(struct uart_sunsab_port *up,
if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
sunsab_cec_wait(up);
writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr);
- return tty;
+ return port;
}
if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
@@ -159,11 +160,6 @@ receive_chars(struct uart_sunsab_port *up,
for (i = 0; i < count; i++) {
unsigned char ch = buf[i], flag;
- if (tty == NULL) {
- uart_handle_sysrq_char(&up->port, ch);
- continue;
- }
-
flag = TTY_NORMAL;
up->port.icount.rx++;
@@ -207,20 +203,20 @@ receive_chars(struct uart_sunsab_port *up,
flag = TTY_FRAME;
}
- if (uart_handle_sysrq_char(&up->port, ch))
+ if (uart_handle_sysrq_char(&up->port, ch) || !port)
continue;
if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&
(stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0)
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(port, ch, flag);
if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
}
if (saw_console_brk)
sun_do_break();
- return tty;
+ return port;
}
static void sunsab_stop_tx(struct uart_port *);
@@ -303,7 +299,7 @@ static void check_status(struct uart_sunsab_port *up,
static irqreturn_t sunsab_interrupt(int irq, void *dev_id)
{
struct uart_sunsab_port *up = dev_id;
- struct tty_struct *tty;
+ struct tty_port *port = NULL;
union sab82532_irq_status status;
unsigned long flags;
unsigned char gis;
@@ -317,12 +313,11 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id)
if (gis & 2)
status.sreg.isr1 = readb(&up->regs->r.isr1);
- tty = NULL;
if (status.stat) {
if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) ||
(status.sreg.isr1 & SAB82532_ISR1_BRK))
- tty = receive_chars(up, &status);
+ port = receive_chars(up, &status);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & SAB82532_ISR1_CSC))
check_status(up, &status);
@@ -332,8 +327,8 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id)
spin_unlock_irqrestore(&up->port.lock, flags);
- if (tty)
- tty_flip_buffer_push(tty);
+ if (port)
+ tty_flip_buffer_push(port);
return IRQ_HANDLED;
}
@@ -432,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);
@@ -724,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);
/*
@@ -849,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)
@@ -899,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.
@@ -953,7 +947,7 @@ static inline struct console *SUNSAB_CONSOLE(void)
#define sunsab_console_init() do { } while (0)
#endif
-static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
+static int sunsab_init_one(struct uart_sunsab_port *up,
struct platform_device *op,
unsigned long offset,
int line)
@@ -1006,7 +1000,7 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
return 0;
}
-static int __devinit sab_probe(struct platform_device *op)
+static int sab_probe(struct platform_device *op)
{
static int inst;
struct uart_sunsab_port *up;
@@ -1042,7 +1036,7 @@ static int __devinit sab_probe(struct platform_device *op)
if (err)
goto out3;
- dev_set_drvdata(&op->dev, &up[0]);
+ platform_set_drvdata(op, &up[0]);
inst++;
@@ -1062,9 +1056,9 @@ out:
return err;
}
-static int __devexit sab_remove(struct platform_device *op)
+static int sab_remove(struct platform_device *op)
{
- struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);
+ struct uart_sunsab_port *up = platform_get_drvdata(op);
uart_remove_one_port(&sunsab_reg, &up[1].port);
uart_remove_one_port(&sunsab_reg, &up[0].port);
@@ -1075,8 +1069,6 @@ static int __devexit sab_remove(struct platform_device *op)
up[0].port.membase,
sizeof(union sab82532_async_regs));
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
@@ -1099,7 +1091,7 @@ static struct platform_driver sab_driver = {
.of_match_table = sab_match,
},
.probe = sab_probe,
- .remove = __devexit_p(sab_remove),
+ .remove = sab_remove,
};
static int __init sunsab_init(void)
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index ad0f8f5f6ea..5326ae195e5 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -41,14 +41,14 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
+#include <asm/setup.h>
#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
-
-#include "suncore.h"
+#include <linux/sunserialcore.h>
/* We are on a NS PC87303 clocked with 24.0 MHz, which results
* in a UART clock of 1.8462 MHz.
@@ -58,10 +58,16 @@
enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT };
static char *su_typev[] = { "su(???)", "su(mouse)", "su(kbd)", "su(serial)" };
+struct serial_uart_config {
+ char *name;
+ int dfl_xmit_fifo_size;
+ int flags;
+};
+
/*
* Here we define the default xmit fifo size used for each type of UART.
*/
-static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = {
+static const struct serial_uart_config uart_config[] = {
{ "unknown", 1, 0 },
{ "8250", 1, 0 },
{ "16450", 1, 0 },
@@ -309,10 +315,10 @@ static void sunsu_enable_ms(struct uart_port *port)
spin_unlock_irqrestore(&up->port.lock, flags);
}
-static struct tty_struct *
+static void
receive_chars(struct uart_sunsu_port *up, unsigned char *status)
{
- struct tty_struct *tty = up->port.state->port.tty;
+ struct tty_port *port = &up->port.state->port;
unsigned char ch, flag;
int max_count = 256;
int saw_console_brk = 0;
@@ -370,22 +376,20 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status)
if (uart_handle_sysrq_char(&up->port, ch))
goto ignore_char;
if ((*status & up->port.ignore_status_mask) == 0)
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(port, ch, flag);
if (*status & UART_LSR_OE)
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character.
*/
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
ignore_char:
*status = serial_inp(up, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
if (saw_console_brk)
sun_do_break();
-
- return tty;
}
static void transmit_chars(struct uart_sunsu_port *up)
@@ -454,20 +458,16 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id)
spin_lock_irqsave(&up->port.lock, flags);
do {
- struct tty_struct *tty;
-
status = serial_inp(up, UART_LSR);
- tty = NULL;
if (status & UART_LSR_DR)
- tty = receive_chars(up, &status);
+ receive_chars(up, &status);
check_modem_status(up);
if (status & UART_LSR_THRE)
transmit_chars(up);
spin_unlock_irqrestore(&up->port.lock, flags);
- if (tty)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&up->port.state->port);
spin_lock_irqsave(&up->port.lock, flags);
@@ -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;
/*
@@ -968,6 +968,7 @@ static struct uart_ops sunsu_pops = {
#define UART_NR 4
static struct uart_sunsu_port sunsu_ports[UART_NR];
+static int nr_inst; /* Number of already registered ports */
#ifdef CONFIG_SERIO
@@ -1179,7 +1180,7 @@ static struct uart_driver sunsu_reg = {
.major = TTY_MAJOR,
};
-static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up)
+static int sunsu_kbd_ms_init(struct uart_sunsu_port *up)
{
int quot, baud;
#ifdef CONFIG_SERIO
@@ -1294,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
@@ -1318,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);
}
/*
@@ -1337,13 +1334,8 @@ static int __init sunsu_console_setup(struct console *co, char *options)
printk("Console: ttyS%d (SU)\n",
(sunsu_reg.minor - 64) + co->index);
- /*
- * Check whether an invalid uart number has been specified, and
- * if so, search for the first available port that does have
- * console support.
- */
- if (co->index >= UART_NR)
- co->index = 0;
+ if (co->index > nr_inst)
+ return -ENODEV;
port = &sunsu_ports[co->index].port;
/*
@@ -1385,7 +1377,7 @@ static inline struct console *SUNSU_CONSOLE(void)
#define sunsu_serial_console_init() do { } while (0)
#endif
-static enum su_type __devinit su_get_type(struct device_node *dp)
+static enum su_type su_get_type(struct device_node *dp)
{
struct device_node *ap = of_find_node_by_path("/aliases");
@@ -1406,9 +1398,8 @@ static enum su_type __devinit su_get_type(struct device_node *dp)
return SU_PORT_PORT;
}
-static int __devinit su_probe(struct platform_device *op)
+static int su_probe(struct platform_device *op)
{
- static int inst;
struct device_node *dp = op->dev.of_node;
struct uart_sunsu_port *up;
struct resource *rp;
@@ -1418,16 +1409,16 @@ static int __devinit su_probe(struct platform_device *op)
type = su_get_type(dp);
if (type == SU_PORT_PORT) {
- if (inst >= UART_NR)
+ if (nr_inst >= UART_NR)
return -EINVAL;
- up = &sunsu_ports[inst];
+ up = &sunsu_ports[nr_inst];
} else {
up = kzalloc(sizeof(*up), GFP_KERNEL);
if (!up)
return -ENOMEM;
}
- up->port.line = inst;
+ up->port.line = nr_inst;
spin_lock_init(&up->port.lock);
@@ -1459,7 +1450,9 @@ static int __devinit su_probe(struct platform_device *op)
kfree(up);
return err;
}
- dev_set_drvdata(&op->dev, up);
+ platform_set_drvdata(op, up);
+
+ nr_inst++;
return 0;
}
@@ -1486,9 +1479,9 @@ static int __devinit su_probe(struct platform_device *op)
if (err)
goto out_unmap;
- dev_set_drvdata(&op->dev, up);
+ platform_set_drvdata(op, up);
- inst++;
+ nr_inst++;
return 0;
@@ -1497,9 +1490,9 @@ out_unmap:
return err;
}
-static int __devexit su_remove(struct platform_device *op)
+static int su_remove(struct platform_device *op)
{
- struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
+ struct uart_sunsu_port *up = platform_get_drvdata(op);
bool kbdms = false;
if (up->su_type == SU_PORT_MS ||
@@ -1519,8 +1512,6 @@ static int __devexit su_remove(struct platform_device *op)
if (kbdms)
kfree(up);
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
@@ -1550,7 +1541,7 @@ static struct platform_driver su_driver = {
.of_match_table = su_match,
},
.probe = su_probe,
- .remove = __devexit_p(su_remove),
+ .remove = su_remove,
};
static int __init sunsu_init(void)
@@ -1595,6 +1586,7 @@ static int __init sunsu_init(void)
static void __exit sunsu_exit(void)
{
+ platform_driver_unregister(&su_driver);
if (sunsu_reg.nr)
sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr);
}
diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index 8e916e76b7b..02df3940b95 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -37,14 +37,15 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
+#include <asm/setup.h>
#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
-#include "suncore.h"
#include "sunzilog.h"
/* On 32-bit sparcs we need to delay after register accesses
@@ -318,21 +319,19 @@ static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up,
serio_interrupt(&up->serio, ch, 0);
#endif
break;
- };
+ }
}
}
-static struct tty_struct *
+static struct tty_port *
sunzilog_receive_chars(struct uart_sunzilog_port *up,
struct zilog_channel __iomem *channel)
{
- struct tty_struct *tty;
+ struct tty_port *port = NULL;
unsigned char ch, r1, flag;
- tty = NULL;
- if (up->port.state != NULL && /* Unopened serial console */
- up->port.state->port.tty != NULL) /* Keyboard || mouse */
- tty = up->port.state->port.tty;
+ if (up->port.state != NULL) /* Unopened serial console */
+ port = &up->port.state->port;
for (;;) {
@@ -365,11 +364,6 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up,
continue;
}
- if (tty == NULL) {
- uart_handle_sysrq_char(&up->port, ch);
- continue;
- }
-
/* A real serial line, record the character and status. */
flag = TTY_NORMAL;
up->port.icount.rx++;
@@ -394,18 +388,18 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up,
else if (r1 & CRC_ERR)
flag = TTY_FRAME;
}
- if (uart_handle_sysrq_char(&up->port, ch))
+ if (uart_handle_sysrq_char(&up->port, ch) || !port)
continue;
if (up->port.ignore_status_mask == 0xff ||
(r1 & up->port.ignore_status_mask) == 0) {
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(port, ch, flag);
}
if (r1 & Rx_OVR)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
}
- return tty;
+ return port;
}
static void sunzilog_status_handle(struct uart_sunzilog_port *up,
@@ -538,21 +532,21 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id)
while (up) {
struct zilog_channel __iomem *channel
= ZILOG_CHANNEL_FROM_PORT(&up->port);
- struct tty_struct *tty;
+ struct tty_port *port;
unsigned char r3;
spin_lock(&up->port.lock);
r3 = read_zsreg(channel, R3);
/* Channel A */
- tty = NULL;
+ port = NULL;
if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
writeb(RES_H_IUS, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
if (r3 & CHARxIP)
- tty = sunzilog_receive_chars(up, channel);
+ port = sunzilog_receive_chars(up, channel);
if (r3 & CHAEXT)
sunzilog_status_handle(up, channel);
if (r3 & CHATxIP)
@@ -560,22 +554,22 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id)
}
spin_unlock(&up->port.lock);
- if (tty)
- tty_flip_buffer_push(tty);
+ if (port)
+ tty_flip_buffer_push(port);
/* Channel B */
up = up->next;
channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
spin_lock(&up->port.lock);
- tty = NULL;
+ port = NULL;
if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
writeb(RES_H_IUS, &channel->control);
ZSDELAY();
ZS_WSYNC(channel);
if (r3 & CHBRxIP)
- tty = sunzilog_receive_chars(up, channel);
+ port = sunzilog_receive_chars(up, channel);
if (r3 & CHBEXT)
sunzilog_status_handle(up, channel);
if (r3 & CHBTxIP)
@@ -583,8 +577,8 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id)
}
spin_unlock(&up->port.lock);
- if (tty)
- tty_flip_buffer_push(tty);
+ if (port)
+ tty_flip_buffer_push(port);
up = up->next;
}
@@ -709,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);
@@ -903,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;
@@ -921,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;
@@ -1201,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)
@@ -1245,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);
@@ -1281,7 +1273,7 @@ static inline struct console *SUNZILOG_CONSOLE(void)
#define SUNZILOG_CONSOLE() (NULL)
#endif
-static void __devinit sunzilog_init_kbdms(struct uart_sunzilog_port *up)
+static void sunzilog_init_kbdms(struct uart_sunzilog_port *up)
{
int baud, brg;
@@ -1301,7 +1293,7 @@ static void __devinit sunzilog_init_kbdms(struct uart_sunzilog_port *up)
}
#ifdef CONFIG_SERIO
-static void __devinit sunzilog_register_serio(struct uart_sunzilog_port *up)
+static void sunzilog_register_serio(struct uart_sunzilog_port *up)
{
struct serio *serio = &up->serio;
@@ -1330,7 +1322,7 @@ static void __devinit sunzilog_register_serio(struct uart_sunzilog_port *up)
}
#endif
-static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
+static void sunzilog_init_hw(struct uart_sunzilog_port *up)
{
struct zilog_channel __iomem *channel;
unsigned long flags;
@@ -1397,9 +1389,9 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
#endif
}
-static int zilog_irq = -1;
+static int zilog_irq;
-static int __devinit zs_probe(struct platform_device *op)
+static int zs_probe(struct platform_device *op)
{
static int kbm_inst, uart_inst;
int inst;
@@ -1425,7 +1417,7 @@ static int __devinit zs_probe(struct platform_device *op)
rp = sunzilog_chip_regs[inst];
- if (zilog_irq == -1)
+ if (!zilog_irq)
zilog_irq = op->archdata.irqs[0];
up = &sunzilog_port_table[inst * 2];
@@ -1501,12 +1493,12 @@ static int __devinit zs_probe(struct platform_device *op)
kbm_inst++;
}
- dev_set_drvdata(&op->dev, &up[0]);
+ platform_set_drvdata(op, &up[0]);
return 0;
}
-static void __devexit zs_remove_one(struct uart_sunzilog_port *up)
+static void zs_remove_one(struct uart_sunzilog_port *up)
{
if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) {
#ifdef CONFIG_SERIO
@@ -1516,9 +1508,9 @@ static void __devexit zs_remove_one(struct uart_sunzilog_port *up)
uart_remove_one_port(&sunzilog_reg, &up->port);
}
-static int __devexit zs_remove(struct platform_device *op)
+static int zs_remove(struct platform_device *op)
{
- struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev);
+ struct uart_sunzilog_port *up = platform_get_drvdata(op);
struct zilog_layout __iomem *regs;
zs_remove_one(&up[0]);
@@ -1527,8 +1519,6 @@ static int __devexit zs_remove(struct platform_device *op)
regs = sunzilog_chip_regs[up[0].port.line / 2];
of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout));
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
@@ -1547,7 +1537,7 @@ static struct platform_driver zs_driver = {
.of_match_table = zs_match,
},
.probe = zs_probe,
- .remove = __devexit_p(zs_remove),
+ .remove = zs_remove,
};
static int __init sunzilog_init(void)
@@ -1580,7 +1570,7 @@ static int __init sunzilog_init(void)
if (err)
goto out_unregister_uart;
- if (zilog_irq != -1) {
+ if (zilog_irq) {
struct uart_sunzilog_port *up = sunzilog_irq_chain;
err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED,
"zs", sunzilog_irq_chain);
@@ -1621,7 +1611,7 @@ static void __exit sunzilog_exit(void)
{
platform_driver_unregister(&zs_driver);
- if (zilog_irq != -1) {
+ if (zilog_irq) {
struct uart_sunzilog_port *up = sunzilog_irq_chain;
/* Disable Interrupts */
@@ -1637,7 +1627,7 @@ static void __exit sunzilog_exit(void)
}
free_irq(zilog_irq, sunzilog_irq_chain);
- zilog_irq = -1;
+ zilog_irq = 0;
}
if (sunzilog_reg.nr) {
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 70f97491d8f..f87097acd8a 100644
--- a/drivers/tty/serial/timbuart.c
+++ b/drivers/tty/serial/timbuart.c
@@ -91,16 +91,16 @@ static void timbuart_flush_buffer(struct uart_port *port)
static void timbuart_rx_chars(struct uart_port *port)
{
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
while (ioread32(port->membase + TIMBUART_ISR) & RXDP) {
u8 ch = ioread8(port->membase + TIMBUART_RXFIFO);
port->icount.rx++;
- tty_insert_flip_char(tty, ch, TTY_NORMAL);
+ tty_insert_flip_char(tport, ch, TTY_NORMAL);
}
spin_unlock(&port->lock);
- tty_flip_buffer_push(port->state->port.tty);
+ tty_flip_buffer_push(tport);
spin_lock(&port->lock);
dev_dbg(port->dev, "%s - total read %d bytes\n",
@@ -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;
@@ -426,7 +426,7 @@ static struct uart_driver timbuart_driver = {
.nr = 1
};
-static int __devinit timbuart_probe(struct platform_device *dev)
+static int timbuart_probe(struct platform_device *dev)
{
int err, irq;
struct timbuart_port *uart;
@@ -492,7 +492,7 @@ err_mem:
return err;
}
-static int __devexit timbuart_remove(struct platform_device *dev)
+static int timbuart_remove(struct platform_device *dev)
{
struct timbuart_port *uart = platform_get_drvdata(dev);
@@ -510,7 +510,7 @@ static struct platform_driver timbuart_platform_driver = {
.owner = THIS_MODULE,
},
.probe = timbuart_probe,
- .remove = __devexit_p(timbuart_remove),
+ .remove = timbuart_remove,
};
module_platform_driver(timbuart_platform_driver);
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 6cd414341d5..dce27f34937 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -19,7 +19,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
@@ -34,7 +34,7 @@
* Register definitions
*
* For register details see datasheet:
- * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf
+ * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf
*/
#define ULITE_RX 0x00
@@ -57,6 +57,54 @@
#define ULITE_CONTROL_RST_RX 0x02
#define ULITE_CONTROL_IE 0x10
+struct uartlite_reg_ops {
+ u32 (*in)(void __iomem *addr);
+ void (*out)(u32 val, void __iomem *addr);
+};
+
+static u32 uartlite_inbe32(void __iomem *addr)
+{
+ return ioread32be(addr);
+}
+
+static void uartlite_outbe32(u32 val, void __iomem *addr)
+{
+ iowrite32be(val, addr);
+}
+
+static struct uartlite_reg_ops uartlite_be = {
+ .in = uartlite_inbe32,
+ .out = uartlite_outbe32,
+};
+
+static u32 uartlite_inle32(void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
+static void uartlite_outle32(u32 val, void __iomem *addr)
+{
+ iowrite32(val, addr);
+}
+
+static struct uartlite_reg_ops uartlite_le = {
+ .in = uartlite_inle32,
+ .out = uartlite_outle32,
+};
+
+static inline u32 uart_in32(u32 offset, struct uart_port *port)
+{
+ struct uartlite_reg_ops *reg_ops = port->private_data;
+
+ return reg_ops->in(port->membase + offset);
+}
+
+static inline void uart_out32(u32 val, u32 offset, struct uart_port *port)
+{
+ struct uartlite_reg_ops *reg_ops = port->private_data;
+
+ reg_ops->out(val, port->membase + offset);
+}
static struct uart_port ulite_ports[ULITE_NR_UARTS];
@@ -66,7 +114,7 @@ static struct uart_port ulite_ports[ULITE_NR_UARTS];
static int ulite_receive(struct uart_port *port, int stat)
{
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
unsigned char ch = 0;
char flag = TTY_NORMAL;
@@ -77,7 +125,7 @@ static int ulite_receive(struct uart_port *port, int stat)
/* stats */
if (stat & ULITE_STATUS_RXVALID) {
port->icount.rx++;
- ch = ioread32be(port->membase + ULITE_RX);
+ ch = uart_in32(ULITE_RX, port);
if (stat & ULITE_STATUS_PARITY)
port->icount.parity++;
@@ -103,13 +151,13 @@ static int ulite_receive(struct uart_port *port, int stat)
stat &= ~port->ignore_status_mask;
if (stat & ULITE_STATUS_RXVALID)
- tty_insert_flip_char(tty, ch, flag);
+ tty_insert_flip_char(tport, ch, flag);
if (stat & ULITE_STATUS_FRAME)
- tty_insert_flip_char(tty, 0, TTY_FRAME);
+ tty_insert_flip_char(tport, 0, TTY_FRAME);
if (stat & ULITE_STATUS_OVERRUN)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
return 1;
}
@@ -122,7 +170,7 @@ static int ulite_transmit(struct uart_port *port, int stat)
return 0;
if (port->x_char) {
- iowrite32be(port->x_char, port->membase + ULITE_TX);
+ uart_out32(port->x_char, ULITE_TX, port);
port->x_char = 0;
port->icount.tx++;
return 1;
@@ -131,7 +179,7 @@ static int ulite_transmit(struct uart_port *port, int stat)
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
return 0;
- iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX);
+ uart_out32(xmit->buf[xmit->tail], ULITE_TX, port);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
port->icount.tx++;
@@ -148,7 +196,7 @@ static irqreturn_t ulite_isr(int irq, void *dev_id)
int busy, n = 0;
do {
- int stat = ioread32be(port->membase + ULITE_STATUS);
+ int stat = uart_in32(ULITE_STATUS, port);
busy = ulite_receive(port, stat);
busy |= ulite_transmit(port, stat);
n++;
@@ -156,7 +204,7 @@ static irqreturn_t ulite_isr(int irq, void *dev_id)
/* work done? */
if (n > 1) {
- tty_flip_buffer_push(port->state->port.tty);
+ tty_flip_buffer_push(&port->state->port);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
@@ -169,7 +217,7 @@ static unsigned int ulite_tx_empty(struct uart_port *port)
unsigned int ret;
spin_lock_irqsave(&port->lock, flags);
- ret = ioread32be(port->membase + ULITE_STATUS);
+ ret = uart_in32(ULITE_STATUS, port);
spin_unlock_irqrestore(&port->lock, flags);
return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0;
@@ -192,7 +240,7 @@ static void ulite_stop_tx(struct uart_port *port)
static void ulite_start_tx(struct uart_port *port)
{
- ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS));
+ ulite_transmit(port, uart_in32(ULITE_STATUS, port));
}
static void ulite_stop_rx(struct uart_port *port)
@@ -216,22 +264,21 @@ static int ulite_startup(struct uart_port *port)
{
int ret;
- ret = request_irq(port->irq, ulite_isr,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port);
+ ret = request_irq(port->irq, ulite_isr, IRQF_SHARED, "uartlite", port);
if (ret)
return ret;
- iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX,
- port->membase + ULITE_CONTROL);
- iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
+ uart_out32(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX,
+ ULITE_CONTROL, port);
+ uart_out32(ULITE_CONTROL_IE, ULITE_CONTROL, port);
return 0;
}
static void ulite_shutdown(struct uart_port *port)
{
- iowrite32be(0, port->membase + ULITE_CONTROL);
- ioread32be(port->membase + ULITE_CONTROL); /* dummy */
+ uart_out32(0, ULITE_CONTROL, port);
+ uart_in32(ULITE_CONTROL, port); /* dummy */
free_irq(port->irq, port);
}
@@ -282,6 +329,8 @@ static void ulite_release_port(struct uart_port *port)
static int ulite_request_port(struct uart_port *port)
{
+ int ret;
+
pr_debug("ulite console: port=%p; port->mapbase=%llx\n",
port, (unsigned long long) port->mapbase);
@@ -297,6 +346,14 @@ static int ulite_request_port(struct uart_port *port)
return -EBUSY;
}
+ port->private_data = &uartlite_be;
+ ret = uart_in32(ULITE_CONTROL, port);
+ uart_out32(ULITE_CONTROL_RST_TX, ULITE_CONTROL, port);
+ ret = uart_in32(ULITE_STATUS, port);
+ /* Endianess detection */
+ if ((ret & ULITE_STATUS_TXEMPTY) != ULITE_STATUS_TXEMPTY)
+ port->private_data = &uartlite_le;
+
return 0;
}
@@ -315,20 +372,19 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
#ifdef CONFIG_CONSOLE_POLL
static int ulite_get_poll_char(struct uart_port *port)
{
- if (!(ioread32be(port->membase + ULITE_STATUS)
- & ULITE_STATUS_RXVALID))
+ if (!(uart_in32(ULITE_STATUS, port) & ULITE_STATUS_RXVALID))
return NO_POLL_CHAR;
- return ioread32be(port->membase + ULITE_RX);
+ return uart_in32(ULITE_RX, port);
}
static void ulite_put_poll_char(struct uart_port *port, unsigned char ch)
{
- while (ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_TXFULL)
+ while (uart_in32(ULITE_STATUS, port) & ULITE_STATUS_TXFULL)
cpu_relax();
/* write char to device */
- iowrite32be(ch, port->membase + ULITE_TX);
+ uart_out32(ch, ULITE_TX, port);
}
#endif
@@ -362,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++) {
- val = ioread32be(port->membase + ULITE_STATUS);
+ 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();
}
}
@@ -377,7 +442,7 @@ static void ulite_console_wait_tx(struct uart_port *port)
static void ulite_console_putchar(struct uart_port *port, int ch)
{
ulite_console_wait_tx(port);
- iowrite32be(ch, port->membase + ULITE_TX);
+ uart_out32(ch, ULITE_TX, port);
}
static void ulite_console_write(struct console *co, const char *s,
@@ -394,8 +459,8 @@ static void ulite_console_write(struct console *co, const char *s,
spin_lock_irqsave(&port->lock, flags);
/* save and disable interrupt */
- ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE;
- iowrite32be(0, port->membase + ULITE_CONTROL);
+ ier = uart_in32(ULITE_STATUS, port) & ULITE_STATUS_IE;
+ uart_out32(0, ULITE_CONTROL, port);
uart_console_write(port, s, count, ulite_console_putchar);
@@ -403,13 +468,13 @@ static void ulite_console_write(struct console *co, const char *s,
/* restore interrupt state */
if (ier)
- iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
+ uart_out32(ULITE_CONTROL_IE, ULITE_CONTROL, port);
if (locked)
spin_unlock_irqrestore(&port->lock, flags);
}
-static int __devinit ulite_console_setup(struct console *co, char *options)
+static int ulite_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 9600;
@@ -487,7 +552,7 @@ static struct uart_driver ulite_uart_driver = {
*
* Returns: 0 on success, <0 otherwise
*/
-static int __devinit ulite_assign(struct device *dev, int id, u32 base, int irq)
+static int ulite_assign(struct device *dev, int id, u32 base, int irq)
{
struct uart_port *port;
int rc;
@@ -543,7 +608,7 @@ static int __devinit ulite_assign(struct device *dev, int id, u32 base, int irq)
*
* @dev: pointer to device structure
*/
-static int __devexit ulite_release(struct device *dev)
+static int ulite_release(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
int rc = 0;
@@ -563,7 +628,7 @@ static int __devexit ulite_release(struct device *dev)
#if defined(CONFIG_OF)
/* Match table for of_platform binding */
-static struct of_device_id ulite_of_match[] __devinitdata = {
+static struct of_device_id ulite_of_match[] = {
{ .compatible = "xlnx,opb-uartlite-1.00.b", },
{ .compatible = "xlnx,xps-uartlite-1.00.a", },
{}
@@ -571,7 +636,7 @@ static struct of_device_id ulite_of_match[] __devinitdata = {
MODULE_DEVICE_TABLE(of, ulite_of_match);
#endif /* CONFIG_OF */
-static int __devinit ulite_probe(struct platform_device *pdev)
+static int ulite_probe(struct platform_device *pdev)
{
struct resource *res, *res2;
int id = pdev->id;
@@ -594,7 +659,7 @@ static int __devinit ulite_probe(struct platform_device *pdev)
return ulite_assign(&pdev->dev, id, res->start, res2->start);
}
-static int __devexit ulite_remove(struct platform_device *pdev)
+static int ulite_remove(struct platform_device *pdev)
{
return ulite_release(&pdev->dev);
}
@@ -604,7 +669,7 @@ MODULE_ALIAS("platform:uartlite");
static struct platform_driver ulite_platform_driver = {
.probe = ulite_probe,
- .remove = __devexit_p(ulite_remove),
+ .remove = ulite_remove,
.driver = {
.owner = THIS_MODULE,
.name = "uartlite",
@@ -616,7 +681,7 @@ static struct platform_driver ulite_platform_driver = {
* Module setup/teardown
*/
-int __init ulite_init(void)
+static int __init ulite_init(void)
{
int ret;
@@ -635,11 +700,11 @@ int __init ulite_init(void)
err_plat:
uart_unregister_driver(&ulite_uart_driver);
err_uart:
- printk(KERN_ERR "registering uartlite driver failed: err=%i", ret);
+ pr_err("registering uartlite driver failed: err=%i", ret);
return ret;
}
-void __exit ulite_exit(void)
+static void __exit ulite_exit(void)
{
platform_driver_unregister(&ulite_platform_driver);
uart_unregister_driver(&ulite_uart_driver);
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 2ebe606a2db..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++;
- };
+ }
}
/*
@@ -469,7 +471,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port)
int i;
unsigned char ch, *cp;
struct uart_port *port = &qe_port->port;
- struct tty_struct *tty = port->state->port.tty;
+ struct tty_port *tport = &port->state->port;
struct qe_bd *bdp;
u16 status;
unsigned int flg;
@@ -491,7 +493,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port)
/* If we don't have enough room in RX buffer for the entire BD,
* then we try later, which will be the next RX interrupt.
*/
- if (tty_buffer_request_room(tty, i) < i) {
+ if (tty_buffer_request_room(tport, i) < i) {
dev_dbg(port->dev, "ucc-uart: no room in RX buffer\n");
return;
}
@@ -512,7 +514,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port)
continue;
error_return:
- tty_insert_flip_char(tty, ch, flg);
+ tty_insert_flip_char(tport, ch, flg);
}
@@ -530,7 +532,7 @@ error_return:
qe_port->rx_cur = bdp;
/* Activate BH processing */
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(tport);
return;
@@ -560,7 +562,7 @@ handle_error:
/* Overrun does not affect the current character ! */
if (status & BD_SC_OV)
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
#ifdef SUPPORT_SYSRQ
port->sysrq = 0;
#endif
@@ -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;
/*
@@ -1360,7 +1362,7 @@ static int ucc_uart_probe(struct platform_device *ofdev)
}
qe_port->port.irq = irq_of_parse_and_map(np, 0);
- if (qe_port->port.irq == NO_IRQ) {
+ if (qe_port->port.irq == 0) {
dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n",
qe_port->ucc_num + 1);
ret = -EINVAL;
@@ -1451,7 +1453,7 @@ static int ucc_uart_probe(struct platform_device *ofdev)
goto out_np;
}
- dev_set_drvdata(&ofdev->dev, qe_port);
+ platform_set_drvdata(ofdev, qe_port);
dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n",
qe_port->ucc_num + 1, qe_port->port.line);
@@ -1471,13 +1473,12 @@ out_free:
static int ucc_uart_remove(struct platform_device *ofdev)
{
- struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev);
+ struct uart_qe_port *qe_port = platform_get_drvdata(ofdev);
dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line);
uart_remove_one_port(&ucc_uart_driver, &qe_port->port);
- dev_set_drvdata(&ofdev->dev, NULL);
kfree(qe_port);
return 0;
@@ -1518,9 +1519,11 @@ static int __init ucc_uart_init(void)
}
ret = platform_driver_register(&ucc_uart_of_driver);
- if (ret)
+ if (ret) {
printk(KERN_ERR
"ucc-uart: could not register platform driver\n");
+ uart_unregister_driver(&ucc_uart_driver);
+ }
return ret;
}
diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c
index 83148e79ca1..db0c8a4ab03 100644
--- a/drivers/tty/serial/vr41xx_siu.c
+++ b/drivers/tty/serial/vr41xx_siu.c
@@ -61,7 +61,7 @@
static struct uart_port siu_uart_ports[SIU_PORTS_MAX] = {
[0 ... SIU_PORTS_MAX-1] = {
.lock = __SPIN_LOCK_UNLOCKED(siu_uart_ports->lock),
- .irq = -1,
+ .irq = 0,
},
};
@@ -171,7 +171,7 @@ static inline unsigned int siu_check_type(struct uart_port *port)
{
if (port->line == 0)
return PORT_VR41XX_SIU;
- if (port->line == 1 && port->irq != -1)
+ if (port->line == 1 && port->irq)
return PORT_VR41XX_DSIU;
return PORT_UNKNOWN;
@@ -313,12 +313,10 @@ static void siu_break_ctl(struct uart_port *port, int ctl)
static inline void receive_chars(struct uart_port *port, uint8_t *status)
{
- struct tty_struct *tty;
uint8_t lsr, ch;
char flag;
int max_count = RX_MAX_COUNT;
- tty = port->state->port.tty;
lsr = *status;
do {
@@ -365,7 +363,7 @@ static inline void receive_chars(struct uart_port *port, uint8_t *status)
lsr = siu_read(port, UART_LSR);
} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&port->state->port);
*status = lsr;
}
@@ -561,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;
@@ -707,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)
@@ -823,7 +821,7 @@ static struct console siu_console = {
.data = &siu_uart_driver,
};
-static int __devinit siu_console_init(void)
+static int siu_console_init(void)
{
struct uart_port *port;
int i;
@@ -867,7 +865,7 @@ static struct uart_driver siu_uart_driver = {
.cons = SERIAL_VR41XX_CONSOLE,
};
-static int __devinit siu_probe(struct platform_device *dev)
+static int siu_probe(struct platform_device *dev)
{
struct uart_port *port;
int num, i, retval;
@@ -901,7 +899,7 @@ static int __devinit siu_probe(struct platform_device *dev)
return 0;
}
-static int __devexit siu_remove(struct platform_device *dev)
+static int siu_remove(struct platform_device *dev)
{
struct uart_port *port;
int i;
@@ -952,7 +950,7 @@ static int siu_resume(struct platform_device *dev)
static struct platform_driver siu_device_driver = {
.probe = siu_probe,
- .remove = __devexit_p(siu_remove),
+ .remove = siu_remove,
.suspend = siu_suspend,
.resume = siu_resume,
.driver = {
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 026cb9ea5cd..15ad6fcda88 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -34,6 +34,8 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/err.h>
/*
* UART Register offsets
@@ -76,6 +78,8 @@
#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR)
+#define VT8500_MAX_PORTS 6
+
struct vt8500_port {
struct uart_port uart;
char name[16];
@@ -83,6 +87,13 @@ struct vt8500_port {
unsigned int ier;
};
+/*
+ * we use this variable to keep track of which ports
+ * have been allocated as we can't use pdev->id in
+ * devicetree
+ */
+static unsigned long vt8500_ports_in_use;
+
static inline void vt8500_write(struct uart_port *port, unsigned int val,
unsigned int off)
{
@@ -126,22 +137,14 @@ static void vt8500_enable_ms(struct uart_port *port)
static void handle_rx(struct uart_port *port)
{
- struct tty_struct *tty = tty_port_tty_get(&port->state->port);
- if (!tty) {
- /* Discard data: no tty available */
- int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8;
- u16 ch;
- while (count--)
- ch = readw(port->membase + VT8500_RXFIFO);
- return;
- }
+ struct tty_port *tport = &port->state->port;
/*
* Handle overrun
*/
if ((vt8500_read(port, VT8500_URISR) & RXOVER)) {
port->icount.overrun++;
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
}
/* and now the main RX loop */
@@ -164,11 +167,12 @@ static void handle_rx(struct uart_port *port)
port->icount.rx++;
if (!uart_handle_sysrq_char(port, c))
- tty_insert_flip_char(tty, c, flag);
+ tty_insert_flip_char(tport, c, flag);
}
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(tport);
+ spin_lock(&port->lock);
}
static void handle_tx(struct uart_port *port)
@@ -431,7 +435,7 @@ static int vt8500_verify_port(struct uart_port *port,
return 0;
}
-static struct vt8500_port *vt8500_uart_ports[4];
+static struct vt8500_port *vt8500_uart_ports[VT8500_MAX_PORTS];
static struct uart_driver vt8500_uart_driver;
#ifdef CONFIG_SERIAL_VT8500_CONSOLE
@@ -544,71 +548,109 @@ static struct uart_driver vt8500_uart_driver = {
.cons = VT8500_CONSOLE,
};
-static int __init vt8500_serial_probe(struct platform_device *pdev)
+static int vt8500_serial_probe(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port;
struct resource *mmres, *irqres;
+ struct device_node *np = pdev->dev.of_node;
int ret;
+ int port;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!mmres || !irqres)
return -ENODEV;
- vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+ if (np) {
+ port = of_alias_get_id(np, "serial");
+ if (port >= VT8500_MAX_PORTS)
+ port = -1;
+ } else {
+ port = -1;
+ }
+
+ if (port < 0) {
+ /* calculate the port id */
+ port = find_first_zero_bit(&vt8500_ports_in_use,
+ sizeof(vt8500_ports_in_use));
+ }
+
+ if (port >= VT8500_MAX_PORTS)
+ return -ENODEV;
+
+ /* reserve the port id */
+ if (test_and_set_bit(port, &vt8500_ports_in_use)) {
+ /* port already in use - shouldn't really happen */
+ return -EBUSY;
+ }
+
+ vt8500_port = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_port),
+ GFP_KERNEL);
if (!vt8500_port)
return -ENOMEM;
+ vt8500_port->uart.membase = devm_ioremap_resource(&pdev->dev, mmres);
+ if (IS_ERR(vt8500_port->uart.membase))
+ return PTR_ERR(vt8500_port->uart.membase);
+
+ vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
+ if (IS_ERR(vt8500_port->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return -EINVAL;
+ }
+
+ ret = clk_prepare_enable(vt8500_port->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable clock\n");
+ return ret;
+ }
+
vt8500_port->uart.type = PORT_VT8500;
vt8500_port->uart.iotype = UPIO_MEM;
vt8500_port->uart.mapbase = mmres->start;
vt8500_port->uart.irq = irqres->start;
vt8500_port->uart.fifosize = 16;
vt8500_port->uart.ops = &vt8500_uart_pops;
- vt8500_port->uart.line = pdev->id;
+ vt8500_port->uart.line = port;
vt8500_port->uart.dev = &pdev->dev;
vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- vt8500_port->uart.uartclk = 24000000;
+
+ vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
snprintf(vt8500_port->name, sizeof(vt8500_port->name),
"VT8500 UART%d", pdev->id);
- vt8500_port->uart.membase = ioremap(mmres->start, resource_size(mmres));
- if (!vt8500_port->uart.membase) {
- ret = -ENOMEM;
- goto err;
- }
-
- vt8500_uart_ports[pdev->id] = vt8500_port;
+ vt8500_uart_ports[port] = vt8500_port;
uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
platform_set_drvdata(pdev, vt8500_port);
return 0;
-
-err:
- kfree(vt8500_port);
- return ret;
}
-static int __devexit vt8500_serial_remove(struct platform_device *pdev)
+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);
- kfree(vt8500_port);
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "via,vt8500-uart", },
+ {}
+};
+
static struct platform_driver vt8500_platform_driver = {
.probe = vt8500_serial_probe,
.remove = vt8500_serial_remove,
.driver = {
.name = "vt8500_serial",
.owner = THIS_MODULE,
+ .of_match_table = wmt_dt_ids,
},
};
@@ -642,4 +684,4 @@ module_exit(vt8500_serial_exit);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("Driver for vt8500 serial device");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index b627363352e..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,101 +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 xuartps_readl(offset) ioread32(port->membase + offset)
-#define xuartps_writel(val, offset) iowrite32(val, port->membase + offset)
+#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 */
-/********************************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
@@ -111,98 +115,159 @@
* 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 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 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;
- struct tty_struct *tty;
unsigned long flags;
unsigned int isrstatus, numbytes;
unsigned int data;
char status = TTY_NORMAL;
- /* Get the tty which could be NULL so don't assume it's valid */
- tty = tty_port_tty_get(&port->state->port);
-
spin_lock_irqsave(&port->lock, flags);
/* 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++;
+ }
- if (tty)
- uart_insert_char(port, isrstatus,
- XUARTPS_IXR_OVERRUN, data,
- status);
+ uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN,
+ data, status);
}
spin_unlock(&port->lock);
- if (tty)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&port->state->port);
spin_lock(&port->lock);
}
/* 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 */
@@ -210,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++;
@@ -223,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);
}
@@ -233,107 +298,235 @@ 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);
- tty_kref_put(tty);
return IRQ_HANDLED;
}
/**
- * 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;
+
+ return bestbaud;
+}
- /* Set the values for the new baud rate */
- xuartps_writel(brgr_val, XUARTPS_BAUDGEN_OFFSET);
- xuartps_writel(brdiv_val, XUARTPS_BAUDDIV_OFFSET);
+/**
+ * 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
@@ -342,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 = 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);
- ctrl_reg = xuartps_readl(XUARTPS_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;
@@ -516,125 +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 */
+ 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);
- xuartps_writel(~(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY |
- XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN |
- XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT), XUARTPS_IDR_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)
@@ -651,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);
@@ -789,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)
@@ -843,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);
+ imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET);
+ cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET);
- uart_console_write(port, s, count, xuartps_console_putchar);
- xuartps_console_wait_tx(port);
-
- /* 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) {
@@ -889,91 +1129,241 @@ 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 __devinit 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;
- int clk = 0;
+ struct cdns_uart *cdns_uart_data;
-#ifdef CONFIG_OF
- const unsigned int *prop;
+ cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data),
+ GFP_KERNEL);
+ if (!cdns_uart_data)
+ return -ENOMEM;
- prop = of_get_property(pdev->dev.of_node, "clock", NULL);
- if (prop)
- clk = be32_to_cpup(prop);
-#else
- clk = *((unsigned int *)(pdev->dev.platform_data));
-#endif
- if (!clk) {
- dev_err(&pdev->dev, "no clock specified\n");
- return -ENODEV;
+ 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");
+ }
+ 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(cdns_uart_data->pclk);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to enable pclk clock.\n");
+ return rc;
+ }
+ 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_pclk;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
+ if (!res) {
+ rc = -ENODEV;
+ goto err_out_clk_disable;
+ }
res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res2)
- return -ENODEV;
+ if (!res2) {
+ rc = -ENODEV;
+ 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");
- return -ENODEV;
+ rc = -ENODEV;
+ goto err_out_notif_unreg;
} else {
/* Register the port.
* This function also registers this device with the tty layer
@@ -982,135 +1372,104 @@ static int __devinit xuartps_probe(struct platform_device *pdev)
port->mapbase = res->start;
port->irq = res2->start;
port->dev = &pdev->dev;
- port->uartclk = clk;
- dev_set_drvdata(&pdev->dev, port);
- rc = uart_add_one_port(&xuartps_uart_driver, port);
+ 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(&cdns_uart_uart_driver, port);
if (rc) {
dev_err(&pdev->dev,
"uart_add_one_port() failed; err=%i\n", rc);
- dev_set_drvdata(&pdev->dev, NULL);
- return rc;
+ goto err_out_notif_unreg;
}
return 0;
}
-}
-/**
- * xuartps_remove - called when the platform driver is unregistered
- * @pdev: Pointer to the platform device structure
- *
- * Returns 0 on success, negative error otherwise
- **/
-static int __devexit xuartps_remove(struct platform_device *pdev)
-{
- struct uart_port *port = dev_get_drvdata(&pdev->dev);
- int rc = 0;
-
- /* Remove the xuartps port from the serial core */
- if (port) {
- rc = uart_remove_one_port(&xuartps_uart_driver, port);
- dev_set_drvdata(&pdev->dev, NULL);
- port->mapbase = 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(cdns_uart_data->uartclk);
+err_out_clk_dis_pclk:
+ clk_disable_unprepare(cdns_uart_data->pclk);
+
return rc;
}
/**
- * xuartps_suspend - suspend event
+ * cdns_uart_remove - called when the platform driver is unregistered
* @pdev: Pointer to the platform device structure
- * @state: State of the device
*
- * Returns 0
- **/
-static int xuartps_suspend(struct platform_device *pdev, pm_message_t state)
+ * Return: 0 on success, negative errno otherwise
+ */
+static int cdns_uart_remove(struct platform_device *pdev)
{
- /* Call the API provided in serial_core.c file which handles
- * the suspend.
- */
- uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]);
- return 0;
-}
+ struct uart_port *port = platform_get_drvdata(pdev);
+ struct cdns_uart *cdns_uart_data = port->private_data;
+ int rc;
-/**
- * xuartps_resume - Resume after a previous suspend
- * @pdev: Pointer to the platform device structure
- *
- * Returns 0
- **/
-static int xuartps_resume(struct platform_device *pdev)
-{
- uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]);
- return 0;
+ /* 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(cdns_uart_data->uartclk);
+ clk_disable_unprepare(cdns_uart_data->pclk);
+ return rc;
}
/* Match table for of_platform binding */
-
-#ifdef CONFIG_OF
-static struct of_device_id xuartps_of_match[] __devinitdata = {
+static struct of_device_id cdns_uart_of_match[] = {
{ .compatible = "xlnx,xuartps", },
+ { .compatible = "cdns,uart-r1p8", },
{}
};
-MODULE_DEVICE_TABLE(of, xuartps_of_match);
-#else
-#define xuartps_of_match NULL
-#endif
+MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
-static struct platform_driver xuartps_platform_driver = {
- .probe = xuartps_probe, /* Probe method */
- .remove = __exit_p(xuartps_remove), /* Detach method */
- .suspend = xuartps_suspend, /* Suspend */
- .resume = xuartps_resume, /* Resume after a suspend */
+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 b7455b52608..2b65bb7ffb8 100644
--- a/drivers/tty/serial/zs.c
+++ b/drivers/tty/serial/zs.c
@@ -57,6 +57,7 @@
#include <linux/ioport.h>
#include <linux/irqflags.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/major.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
@@ -67,7 +68,6 @@
#include <linux/types.h>
#include <linux/atomic.h>
-#include <asm/system.h>
#include <asm/dec/interrupts.h>
#include <asm/dec/ioasic_addrs.h>
@@ -603,7 +603,7 @@ static void zs_receive_chars(struct zs_port *zport)
uart_insert_char(uport, status, Rx_OVR, ch, flag);
}
- tty_flip_buffer_push(uport->state->port.tty);
+ tty_flip_buffer_push(&uport->state->port);
}
static void zs_raw_transmit_chars(struct zs_port *zport)
@@ -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;