diff options
Diffstat (limited to 'drivers/tty/serial/omap-serial.c')
| -rw-r--r-- | drivers/tty/serial/omap-serial.c | 259 | 
1 files changed, 164 insertions, 95 deletions
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6d3d26a607b..23f797eb7a2 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -44,6 +44,8 @@  #include <plat/omap-serial.h> +#define OMAP_MAX_HSUART_PORTS	6 +  #define UART_BUILD_REVISION(x, y)	(((x) << 8) | (y))  #define OMAP_UART_REV_42 0x0402 @@ -51,10 +53,14 @@  #define OMAP_UART_REV_52 0x0502  #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*/  /* SCR register bitmasks */  #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK		(1 << 7) +#define OMAP_UART_SCR_TX_EMPTY			(1 << 3)  /* FCR register bitmasks */  #define OMAP_UART_FCR_RX_FIFO_TRIG_MASK			(0x3 << 6) @@ -71,6 +77,52 @@  #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; @@ -96,10 +148,9 @@ struct uart_omap_port {  	unsigned char		msr_saved_flags;  	char			name[20];  	unsigned long		port_activity; -	u32			context_loss_cnt; +	int			context_loss_cnt;  	u32			errata;  	u8			wakeups_enabled; -	unsigned int		irq_pending:1;  	int			DTR_gpio;  	int			DTR_inverted; @@ -303,6 +354,34 @@ static void serial_omap_start_tx(struct uart_port *port)  	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)  {  	unsigned int status; @@ -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)  {  	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);  	if (mctrl & TIOCM_RTS) @@ -519,8 +598,10 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)  		mcr |= UART_MCR_LOOP;  	pm_runtime_get_sync(up->dev); -	up->mcr = serial_in(up, UART_MCR); -	up->mcr |= mcr; +	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_mark_last_busy(up->dev);  	pm_runtime_put_autosuspend(up->dev); @@ -654,65 +735,6 @@ static void serial_omap_shutdown(struct uart_port *port)  	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); -} -  static void serial_omap_uart_qos_work(struct work_struct *work)  {  	struct uart_omap_port *up = container_of(work, struct uart_omap_port, @@ -730,7 +752,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,  {  	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; @@ -840,11 +861,12 @@ 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 */ @@ -863,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_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 */ @@ -875,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_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); @@ -903,29 +926,68 @@ 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); + +	/* 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); -	if (termios->c_cflag & CRTSCTS) { -		efr |= (UART_EFR_CTS | UART_EFR_RTS); -		serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); +	serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); -		up->mcr = serial_in(up, UART_MCR); -		serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); +	if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { +		/* Enable AUTORTS and AUTOCTS */ +		up->efr |= UART_EFR_CTS | UART_EFR_RTS; -		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); +		/* 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_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); +		/* +		 * IXOFF Flag: +		 * Enable XON/XOFF flow control on output. +		 * Transmit XON1, XOFF1 +		 */ +		if (termios->c_iflag & IXOFF) +			up->efr |= OMAP_UART_SW_TX; + +		/* +		 * 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_mark_last_busy(up->dev); @@ -991,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",  							up->port.line);  	up->port.type = PORT_OMAP; +	up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW;  }  static int @@ -1081,7 +1144,7 @@ out:  #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; @@ -1194,6 +1257,8 @@ static struct uart_ops serial_omap_pops = {  	.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, @@ -1242,7 +1307,7 @@ static int serial_omap_resume(struct device *dev)  }  #endif -static void __devinit omap_serial_fill_features_erratas(struct uart_omap_port *up) +static void omap_serial_fill_features_erratas(struct uart_omap_port *up)  {  	u32 mvr, scheme;  	u16 revision, major, minor; @@ -1295,7 +1360,7 @@ static void __devinit omap_serial_fill_features_erratas(struct uart_omap_port *u  	}  } -static __devinit struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) +static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)  {  	struct omap_uart_port_info *omap_up_info; @@ -1308,7 +1373,7 @@ static __devinit struct omap_uart_port_info *of_get_uart_port_info(struct device  	return omap_up_info;  } -static int __devinit serial_omap_probe(struct platform_device *pdev) +static int serial_omap_probe(struct platform_device *pdev)  {  	struct uart_omap_port	*up;  	struct resource		*mem, *irq; @@ -1445,7 +1510,7 @@ err_port_line:  	return ret;  } -static int __devexit serial_omap_remove(struct platform_device *dev) +static int serial_omap_remove(struct platform_device *dev)  {  	struct uart_omap_port *up = platform_get_drvdata(dev); @@ -1556,11 +1621,15 @@ static int serial_omap_runtime_resume(struct device *dev)  {  	struct uart_omap_port *up = dev_get_drvdata(dev); -	u32 loss_cnt = serial_omap_get_context_loss_count(up); +	int loss_cnt = serial_omap_get_context_loss_count(up); -	if (up->context_loss_cnt != loss_cnt) +	if (loss_cnt < 0) { +		dev_err(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); @@ -1586,7 +1655,7 @@ MODULE_DEVICE_TABLE(of, omap_serial_of_match);  static struct platform_driver serial_omap_driver = {  	.probe          = serial_omap_probe, -	.remove         = __devexit_p(serial_omap_remove), +	.remove         = serial_omap_remove,  	.driver		= {  		.name	= DRIVER_NAME,  		.pm	= &serial_omap_dev_pm_ops,  |