diff options
Diffstat (limited to 'drivers/tty/serial/imx.c')
| -rw-r--r-- | drivers/tty/serial/imx.c | 148 | 
1 files changed, 133 insertions, 15 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 163fc9021f5..0b7fed746b2 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -102,6 +102,7 @@  #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 */ @@ -207,6 +208,12 @@ struct imx_port {  	struct imx_uart_data	*devdata;  }; +struct imx_port_ucrs { +	unsigned int	ucr1; +	unsigned int	ucr2; +	unsigned int	ucr3; +}; +  #ifdef CONFIG_IRDA  #define USE_IRDA(sport)	((sport)->use_irda)  #else @@ -260,6 +267,27 @@ static inline int is_imx21_uart(struct imx_port *sport)  }  /* + * Save and restore functions for UCR1, UCR2 and UCR3 registers + */ +static void imx_port_ucrs_save(struct uart_port *port, +			       struct imx_port_ucrs *ucr) +{ +	/* save control registers */ +	ucr->ucr1 = readl(port->membase + UCR1); +	ucr->ucr2 = readl(port->membase + UCR2); +	ucr->ucr3 = readl(port->membase + UCR3); +} + +static void imx_port_ucrs_restore(struct uart_port *port, +				  struct imx_port_ucrs *ucr) +{ +	/* restore control registers */ +	writel(ucr->ucr1, port->membase + UCR1); +	writel(ucr->ucr2, port->membase + UCR2); +	writel(ucr->ucr3, port->membase + UCR3); +} + +/*   * Handle any change of modem status signal since we were last called.   */  static void imx_mctrl_check(struct imx_port *sport) @@ -566,6 +594,9 @@ static irqreturn_t imx_int(int irq, void *dev_id)  	if (sts & USR1_RTSD)  		imx_rtsint(irq, dev_id); +	if (sts & USR1_AWAKE) +		writel(USR1_AWAKE, sport->port.membase + USR1); +  	return IRQ_HANDLED;  } @@ -901,6 +932,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,  			ucr2 |= UCR2_PROE;  	} +	del_timer_sync(&sport->timer); +  	/*  	 * Ask the core to calculate the divisor for us.  	 */ @@ -931,8 +964,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,  			sport->port.ignore_status_mask |= URXD_OVRRUN;  	} -	del_timer_sync(&sport->timer); -  	/*  	 * Update the per-port timeout.  	 */ @@ -1079,6 +1110,70 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)  	return ret;  } +#if defined(CONFIG_CONSOLE_POLL) +static int imx_poll_get_char(struct uart_port *port) +{ +	struct imx_port_ucrs old_ucr; +	unsigned int status; +	unsigned char c; + +	/* save control registers */ +	imx_port_ucrs_save(port, &old_ucr); + +	/* disable interrupts */ +	writel(UCR1_UARTEN, port->membase + UCR1); +	writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI), +	       port->membase + UCR2); +	writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN), +	       port->membase + UCR3); + +	/* poll */ +	do { +		status = readl(port->membase + USR2); +	} while (~status & USR2_RDR); + +	/* read */ +	c = readl(port->membase + URXD0); + +	/* restore control registers */ +	imx_port_ucrs_restore(port, &old_ucr); + +	return c; +} + +static void imx_poll_put_char(struct uart_port *port, unsigned char c) +{ +	struct imx_port_ucrs old_ucr; +	unsigned int status; + +	/* save control registers */ +	imx_port_ucrs_save(port, &old_ucr); + +	/* disable interrupts */ +	writel(UCR1_UARTEN, port->membase + UCR1); +	writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI), +	       port->membase + UCR2); +	writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN), +	       port->membase + UCR3); + +	/* drain */ +	do { +		status = readl(port->membase + USR1); +	} while (~status & USR1_TRDY); + +	/* write */ +	writel(c, port->membase + URTX0); + +	/* flush */ +	do { +		status = readl(port->membase + USR2); +	} while (~status & USR2_TXDC); + +	/* restore control registers */ +	imx_port_ucrs_restore(port, &old_ucr); +} +#endif +  static struct uart_ops imx_pops = {  	.tx_empty	= imx_tx_empty,  	.set_mctrl	= imx_set_mctrl, @@ -1096,6 +1191,10 @@ static struct uart_ops imx_pops = {  	.request_port	= imx_request_port,  	.config_port	= imx_config_port,  	.verify_port	= imx_verify_port, +#if defined(CONFIG_CONSOLE_POLL) +	.poll_get_char  = imx_poll_get_char, +	.poll_put_char  = imx_poll_put_char, +#endif  };  static struct imx_port *imx_ports[UART_NR]; @@ -1118,13 +1217,14 @@ static void  imx_console_write(struct console *co, const char *s, unsigned int count)  {  	struct imx_port *sport = imx_ports[co->index]; -	unsigned int old_ucr1, old_ucr2, ucr1; +	struct imx_port_ucrs old_ucr; +	unsigned int ucr1;  	/* -	 *	First, save UCR1/2 and then disable interrupts +	 *	First, save UCR1/2/3 and then disable interrupts  	 */ -	ucr1 = old_ucr1 = readl(sport->port.membase + UCR1); -	old_ucr2 = readl(sport->port.membase + UCR2); +	imx_port_ucrs_save(&sport->port, &old_ucr); +	ucr1 = old_ucr.ucr1;  	if (is_imx1_uart(sport))  		ucr1 |= IMX1_UCR1_UARTCLKEN; @@ -1133,18 +1233,17 @@ imx_console_write(struct console *co, const char *s, unsigned int count)  	writel(ucr1, sport->port.membase + UCR1); -	writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); +	writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2);  	uart_console_write(&sport->port, s, count, imx_console_putchar);  	/*  	 *	Finally, wait for transmitter to become empty -	 *	and restore UCR1/2 +	 *	and restore UCR1/2/3  	 */  	while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); -	writel(old_ucr1, sport->port.membase + UCR1); -	writel(old_ucr2, sport->port.membase + UCR2); +	imx_port_ucrs_restore(&sport->port, &old_ucr);  }  /* @@ -1269,6 +1368,12 @@ static struct uart_driver imx_reg = {  static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)  {  	struct imx_port *sport = platform_get_drvdata(dev); +	unsigned int val; + +	/* enable wakeup from i.MX UART */ +	val = readl(sport->port.membase + UCR3); +	val |= UCR3_AWAKEN; +	writel(val, sport->port.membase + UCR3);  	if (sport)  		uart_suspend_port(&imx_reg, &sport->port); @@ -1279,6 +1384,12 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)  static int serial_imx_resume(struct platform_device *dev)  {  	struct imx_port *sport = platform_get_drvdata(dev); +	unsigned int val; + +	/* disable wakeup from i.MX UART */ +	val = readl(sport->port.membase + UCR3); +	val &= ~UCR3_AWAKEN; +	writel(val, sport->port.membase + UCR3);  	if (sport)  		uart_resume_port(&imx_reg, &sport->port); @@ -1287,6 +1398,10 @@ static int serial_imx_resume(struct platform_device *dev)  }  #ifdef CONFIG_OF +/* + * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it + * could successfully get all information from dt or a negative errno. + */  static int serial_imx_probe_dt(struct imx_port *sport,  		struct platform_device *pdev)  { @@ -1296,12 +1411,13 @@ static int serial_imx_probe_dt(struct imx_port *sport,  	int ret;  	if (!np) -		return -ENODEV; +		/* 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, errno %d\n", ret); -		return -ENODEV; +		return ret;  	}  	sport->port.line = ret; @@ -1319,7 +1435,7 @@ static int serial_imx_probe_dt(struct imx_port *sport,  static inline int serial_imx_probe_dt(struct imx_port *sport,  		struct platform_device *pdev)  { -	return -ENODEV; +	return 1;  }  #endif @@ -1354,8 +1470,10 @@ static int serial_imx_probe(struct platform_device *pdev)  		return -ENOMEM;  	ret = serial_imx_probe_dt(sport, pdev); -	if (ret == -ENODEV) +	if (ret > 0)  		serial_imx_probe_pdata(sport, pdev); +	else if (ret < 0) +		goto free;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res) { @@ -1476,7 +1594,7 @@ static int __init imx_serial_init(void)  	if (ret != 0)  		uart_unregister_driver(&imx_reg); -	return 0; +	return ret;  }  static void __exit imx_serial_exit(void)  |