Commit d07e43d7 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'omap-serial' of git://git.linaro.org/people/rmk/linux-arm

Pull ARM OMAP serial updates from Russell King:
 "This series is a major reworking of the OMAP serial driver code fixing
  various bugs in the hardware-assisted flow control, extending up into
  serial_core for a couple of issues.  These fixes have been done as a
  set of progressive changes and transformations in the hope that no new
  bugs will be introduced by this series.

  The problems are many-fold, from the driver not being informed about
  updated settings, to the driver not knowing what the intentions of the
  upper layers are.

  The first four patches tackle the serial_core layer, allowing it to
  provide the necessary information to drivers, and the remaining
  patches allow the OMAP serial driver to take advantage of this.

  This brings hardware assisted RTS/CTS and XON/OFF flow control into a
  useful state.

  These patches have been in linux-next for most of the last cycle;
  indeed they predate the previous merge window.  They've also been
  posted to the OMAP people."

* 'omap-serial' of git://git.linaro.org/people/rmk/linux-arm: (21 commits)
  SERIAL: omap: fix hardware assisted flow control
  SERIAL: omap: simplify (2)
  SERIAL: omap: move xon/xoff setting earlier
  SERIAL: omap: always set TCR
  SERIAL: omap: simplify
  SERIAL: omap: don't read back LCR/MCR/EFR
  SERIAL: omap: serial_omap_configure_xonxoff() contents into set_termios
  SERIAL: omap: configure xon/xoff before setting modem control lines
  SERIAL: omap: remove OMAP_UART_SYSC_RESET and OMAP_UART_FIFO_CLR
  SERIAL: omap: move driver private definitions and structures to driver
  SERIAL: omap: remove 'irq_pending' bitfield
  SERIAL: omap: fix MCR TCRTLR bit handling
  SERIAL: omap: fix set_mctrl() breakage
  SERIAL: omap: no need to re-read EFR
  SERIAL: omap: remove setting of EFR SCD bit
  SERIAL: omap: allow hardware assisted IXANY mode to be disabled
  SERIAL: omap: allow hardware assisted rts/cts modes to be disabled
  SERIAL: core: add throttle/unthrottle callbacks for hardware assisted flow control
  SERIAL: core: add hardware assisted h/w flow control support
  SERIAL: core: add hardware assisted s/w flow control support
  ...

Conflicts:
	drivers/tty/serial/omap-serial.c
parents 1ebaf4f4 3af08bd7
...@@ -30,35 +30,6 @@ ...@@ -30,35 +30,6 @@
*/ */
#define OMAP_SERIAL_NAME "ttyO" #define OMAP_SERIAL_NAME "ttyO"
#define OMAP_MODE13X_SPEED 230400
#define OMAP_UART_SCR_TX_EMPTY 0x08
/* 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 0x04
/* Enable XON/XOFF flow control on input */
#define OMAP_UART_SW_RX 0x04
#define OMAP_UART_SYSC_RESET 0X07
#define OMAP_UART_TCR_TRIG 0X0F
#define OMAP_UART_SW_CLR 0XF0
#define OMAP_UART_FIFO_CLR 0X06
#define OMAP_UART_DMA_CH_FREE -1
#define OMAP_MAX_HSUART_PORTS 6
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
#define UART_ERRATA_i202_MDR1_ACCESS BIT(0)
#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
struct omap_uart_port_info { struct omap_uart_port_info {
bool dma_enabled; /* To specify DMA Mode */ bool dma_enabled; /* To specify DMA Mode */
unsigned int uartclk; /* UART clock rate */ unsigned int uartclk; /* UART clock rate */
...@@ -77,30 +48,4 @@ struct omap_uart_port_info { ...@@ -77,30 +48,4 @@ struct omap_uart_port_info {
void (*enable_wakeup)(struct device *, bool); void (*enable_wakeup)(struct device *, bool);
}; };
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;
};
#endif /* __OMAP_SERIAL_H__ */ #endif /* __OMAP_SERIAL_H__ */
...@@ -44,6 +44,8 @@ ...@@ -44,6 +44,8 @@
#include <plat/omap-serial.h> #include <plat/omap-serial.h>
#define OMAP_MAX_HSUART_PORTS 6
#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) #define UART_BUILD_REVISION(x, y) (((x) << 8) | (y))
#define OMAP_UART_REV_42 0x0402 #define OMAP_UART_REV_42 0x0402
...@@ -51,10 +53,14 @@ ...@@ -51,10 +53,14 @@
#define OMAP_UART_REV_52 0x0502 #define OMAP_UART_REV_52 0x0502
#define OMAP_UART_REV_63 0x0603 #define OMAP_UART_REV_63 0x0603
#define UART_ERRATA_i202_MDR1_ACCESS BIT(0)
#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/ #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
/* SCR register bitmasks */ /* SCR register bitmasks */
#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7)
#define OMAP_UART_SCR_TX_EMPTY (1 << 3)
/* FCR register bitmasks */ /* FCR register bitmasks */
#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) #define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6)
...@@ -71,6 +77,52 @@ ...@@ -71,6 +77,52 @@
#define OMAP_UART_MVR_MAJ_SHIFT 8 #define OMAP_UART_MVR_MAJ_SHIFT 8
#define OMAP_UART_MVR_MIN_MASK 0x3f #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_omap_port {
struct uart_port port; struct uart_port port;
struct uart_omap_dma uart_dma; struct uart_omap_dma uart_dma;
...@@ -99,7 +151,6 @@ struct uart_omap_port { ...@@ -99,7 +151,6 @@ struct uart_omap_port {
int context_loss_cnt; int context_loss_cnt;
u32 errata; u32 errata;
u8 wakeups_enabled; u8 wakeups_enabled;
unsigned int irq_pending:1;
int DTR_gpio; int DTR_gpio;
int DTR_inverted; int DTR_inverted;
...@@ -303,6 +354,34 @@ static void serial_omap_start_tx(struct uart_port *port) ...@@ -303,6 +354,34 @@ static void serial_omap_start_tx(struct uart_port *port)
pm_runtime_put_autosuspend(up->dev); pm_runtime_put_autosuspend(up->dev);
} }
static void serial_omap_throttle(struct uart_port *port)
{
struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags;
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 void serial_omap_unthrottle(struct uart_port *port)
{
struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags;
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) static unsigned int check_modem_status(struct uart_omap_port *up)
{ {
unsigned int status; unsigned int status;
...@@ -504,7 +583,7 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port) ...@@ -504,7 +583,7 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port)
static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
{ {
struct uart_omap_port *up = to_uart_omap_port(port); struct uart_omap_port *up = to_uart_omap_port(port);
unsigned char mcr = 0; unsigned char mcr = 0, old_mcr;
dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line); dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
if (mctrl & TIOCM_RTS) if (mctrl & TIOCM_RTS)
...@@ -519,8 +598,10 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) ...@@ -519,8 +598,10 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
mcr |= UART_MCR_LOOP; mcr |= UART_MCR_LOOP;
pm_runtime_get_sync(up->dev); pm_runtime_get_sync(up->dev);
up->mcr = serial_in(up, UART_MCR); old_mcr = serial_in(up, UART_MCR);
up->mcr |= 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); serial_out(up, UART_MCR, up->mcr);
pm_runtime_mark_last_busy(up->dev); pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev); pm_runtime_put_autosuspend(up->dev);
...@@ -654,61 +735,6 @@ static void serial_omap_shutdown(struct uart_port *port) ...@@ -654,61 +735,6 @@ static void serial_omap_shutdown(struct uart_port *port)
free_irq(up->port.irq, up); 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);
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);
}
static void serial_omap_uart_qos_work(struct work_struct *work) static void serial_omap_uart_qos_work(struct work_struct *work)
{ {
struct uart_omap_port *up = container_of(work, struct uart_omap_port, struct uart_omap_port *up = container_of(work, struct uart_omap_port,
...@@ -726,7 +752,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -726,7 +752,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
{ {
struct uart_omap_port *up = to_uart_omap_port(port); struct uart_omap_port *up = to_uart_omap_port(port);
unsigned char cval = 0; unsigned char cval = 0;
unsigned char efr = 0;
unsigned long flags = 0; unsigned long flags = 0;
unsigned int baud, quot; unsigned int baud, quot;
...@@ -836,11 +861,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -836,11 +861,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); 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_EFR, up->efr | UART_EFR_ECB);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); 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); serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
/* FIFO ENABLE, DMA MODE */ /* FIFO ENABLE, DMA MODE */
...@@ -859,9 +885,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -859,9 +885,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
serial_out(up, UART_OMAP_SCR, up->scr); 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_LCR, UART_LCR_CONF_MODE_A);
serial_out(up, UART_MCR, up->mcr); 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 */ /* Protocol, Baud Rate, and Interrupt Settings */
...@@ -871,8 +900,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -871,8 +900,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
serial_out(up, UART_OMAP_MDR1, up->mdr1); serial_out(up, UART_OMAP_MDR1, up->mdr1);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); 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_EFR, up->efr | UART_EFR_ECB);
serial_out(up, UART_LCR, 0); serial_out(up, UART_LCR, 0);
...@@ -899,29 +926,68 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -899,29 +926,68 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
else else
serial_out(up, UART_OMAP_MDR1, up->mdr1); 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);
/* 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]);
/* 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 (termios->c_cflag & CRTSCTS) { if (up->port.flags & UPF_SOFT_FLOW) {
efr |= (UART_EFR_CTS | UART_EFR_RTS); /* clear SW control mode bits */
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); up->efr &= OMAP_UART_SW_CLR;
up->mcr = serial_in(up, UART_MCR); /*
serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); * 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); * IXOFF Flag:
serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); * 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 */ * IXANY Flag:
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); * Enable any character to restart output.
serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); * Operation resumes after receiving any
serial_out(up, UART_LCR, cval); * 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); 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); spin_unlock_irqrestore(&up->port.lock, flags);
pm_runtime_mark_last_busy(up->dev); pm_runtime_mark_last_busy(up->dev);
...@@ -987,6 +1053,7 @@ static void serial_omap_config_port(struct uart_port *port, int flags) ...@@ -987,6 +1053,7 @@ static void serial_omap_config_port(struct uart_port *port, int flags)
dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
up->port.line); up->port.line);
up->port.type = PORT_OMAP; up->port.type = PORT_OMAP;
up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW;
} }
static int static int
...@@ -1190,6 +1257,8 @@ static struct uart_ops serial_omap_pops = { ...@@ -1190,6 +1257,8 @@ static struct uart_ops serial_omap_pops = {
.get_mctrl = serial_omap_get_mctrl, .get_mctrl = serial_omap_get_mctrl,
.stop_tx = serial_omap_stop_tx, .stop_tx = serial_omap_stop_tx,
.start_tx = serial_omap_start_tx, .start_tx = serial_omap_start_tx,
.throttle = serial_omap_throttle,
.unthrottle = serial_omap_unthrottle,
.stop_rx = serial_omap_stop_rx, .stop_rx = serial_omap_stop_rx,
.enable_ms = serial_omap_enable_ms, .enable_ms = serial_omap_enable_ms,
.break_ctl = serial_omap_break_ctl, .break_ctl = serial_omap_break_ctl,
......
...@@ -610,27 +610,50 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) ...@@ -610,27 +610,50 @@ static void uart_send_xchar(struct tty_struct *tty, char ch)
static void uart_throttle(struct tty_struct *tty) static void uart_throttle(struct tty_struct *tty)
{ {
struct uart_state *state = tty->driver_data; 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->throttle(port);
mask &= ~port->flags;
}
if (mask & UPF_SOFT_FLOW)
uart_send_xchar(tty, STOP_CHAR(tty)); uart_send_xchar(tty, STOP_CHAR(tty));
if (tty->termios.c_cflag & CRTSCTS) if (mask & UPF_HARD_FLOW)
uart_clear_mctrl(state->uart_port, TIOCM_RTS); uart_clear_mctrl(port, TIOCM_RTS);
} }
static void uart_unthrottle(struct tty_struct *tty) static void uart_unthrottle(struct tty_struct *tty)
{ {
struct uart_state *state = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port; 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) if (port->x_char)
port->x_char = 0; port->x_char = 0;
else else
uart_send_xchar(tty, START_CHAR(tty)); uart_send_xchar(tty, START_CHAR(tty));
} }
if (tty->termios.c_cflag & CRTSCTS) if (mask & UPF_HARD_FLOW)
uart_set_mctrl(port, TIOCM_RTS); uart_set_mctrl(port, TIOCM_RTS);
} }
...@@ -1214,9 +1237,22 @@ static void uart_set_termios(struct tty_struct *tty, ...@@ -1214,9 +1237,22 @@ static void uart_set_termios(struct tty_struct *tty,
struct ktermios *old_termios) struct ktermios *old_termios)
{ {
struct uart_state *state = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_port *uport = state->uart_port;
unsigned long flags; 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 * These are the bits that are used to setup various
...@@ -1224,11 +1260,11 @@ static void uart_set_termios(struct tty_struct *tty, ...@@ -1224,11 +1260,11 @@ static void uart_set_termios(struct tty_struct *tty,
* bits in c_cflag; c_[io]speed will always be set * bits in c_cflag; c_[io]speed will always be set
* appropriately by set_termios() in tty_ioctl.c * 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 && if ((cflag ^ old_termios->c_cflag) == 0 &&
tty->termios.c_ospeed == old_termios->c_ospeed && tty->termios.c_ospeed == old_termios->c_ospeed &&
tty->termios.c_ispeed == old_termios->c_ispeed && tty->termios.c_ispeed == old_termios->c_ispeed &&
RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) { ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 &&
!sw_changed) {
return; return;
} }
...@@ -1236,31 +1272,38 @@ static void uart_set_termios(struct tty_struct *tty, ...@@ -1236,31 +1272,38 @@ static void uart_set_termios(struct tty_struct *tty,
/* Handle transition to B0 status */ /* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) 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 */ /* Handle transition away from B0 status */
else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
unsigned int mask = TIOCM_DTR; unsigned int mask = TIOCM_DTR;
if (!(cflag & CRTSCTS) || if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags)) !test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS; 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 */ /* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & 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; tty->hw_stopped = 0;
__uart_start(tty); __uart_start(tty);
spin_unlock_irqrestore(&state->uart_port->lock, flags); spin_unlock_irqrestore(&uport->lock, flags);
} }
/* Handle turning on CRTSCTS */ /* Handle turning on CRTSCTS */
else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
spin_lock_irqsave(&state->uart_port->lock, flags); spin_lock_irqsave(&uport->lock, flags);
if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) { if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) {
tty->hw_stopped = 1; 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);
} }
} }
......
...@@ -46,6 +46,8 @@ struct uart_ops { ...@@ -46,6 +46,8 @@ struct uart_ops {
unsigned int (*get_mctrl)(struct uart_port *); unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *); void (*stop_tx)(struct uart_port *);
void (*start_tx)(struct uart_port *); void (*start_tx)(struct uart_port *);
void (*throttle)(struct uart_port *);
void (*unthrottle)(struct uart_port *);
void (*send_xchar)(struct uart_port *, char ch); void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *); void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *); void (*enable_ms)(struct uart_port *);
...@@ -163,6 +165,10 @@ struct uart_port { ...@@ -163,6 +165,10 @@ struct uart_port {
#define UPF_BUGGY_UART ((__force upf_t) (1 << 14)) #define UPF_BUGGY_UART ((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) #define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
#define UPF_HARD_FLOW ((__force upf_t) (1 << 21))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW ((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR ((__force upf_t) (1 << 25)) #define UPF_EXAR_EFR ((__force upf_t) (1 << 25))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment