diff options
| author | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
|---|---|---|
| committer | Mark Brown <[email protected]> | 2015-10-12 18:09:27 +0100 | 
| commit | 79828b4fa835f73cdaf4bffa48696abdcbea9d02 (patch) | |
| tree | 5e0fa7156acb75ba603022bc807df8f2fedb97a8 /drivers/tty/serial/imx.c | |
| parent | 721b51fcf91898299d96f4b72cb9434cda29dce6 (diff) | |
| parent | 8c1a9d6323abf0fb1e5dad96cf3f1c783505ea5a (diff) | |
Merge remote-tracking branch 'asoc/fix/rt5645' into asoc-fix-rt5645
Diffstat (limited to 'drivers/tty/serial/imx.c')
| -rw-r--r-- | drivers/tty/serial/imx.c | 218 | 
1 files changed, 159 insertions, 59 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 2c90dc31bfaa..fe3d41cc8416 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -216,6 +216,8 @@ struct imx_port {  	unsigned int		tx_bytes;  	unsigned int		dma_tx_nents;  	wait_queue_head_t	dma_wait; +	unsigned int            saved_reg[10]; +	bool			context_saved;  };  struct imx_port_ucrs { @@ -700,7 +702,8 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)  		if (sport->port.ignore_status_mask & URXD_DUMMY_READ)  			goto out; -		tty_insert_flip_char(port, rx, flg); +		if (tty_insert_flip_char(port, rx, flg) == 0) +			sport->port.icount.buf_overrun++;  	}  out: @@ -766,7 +769,6 @@ static irqreturn_t imx_int(int irq, void *dev_id)  		writel(USR1_AWAKE, sport->port.membase + USR1);  	if (sts2 & USR2_ORE) { -		dev_err(sport->port.dev, "Rx FIFO overrun\n");  		sport->port.icount.overrun++;  		writel(USR2_ORE, sport->port.membase + USR2);  	} @@ -921,8 +923,13 @@ static void dma_rx_callback(void *data)  	dev_dbg(sport->port.dev, "We get %d bytes.\n", count);  	if (count) { -		if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) -			tty_insert_flip_string(port, sport->rx_buf, count); +		if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { +			int bytes = tty_insert_flip_string(port, sport->rx_buf, +					count); + +			if (bytes != count) +				sport->port.icount.buf_overrun++; +		}  		tty_flip_buffer_push(port);  		start_rx_dma(sport); @@ -1121,11 +1128,6 @@ static int imx_startup(struct uart_port *port)  	writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); -	/* Can we enable the DMA support? */ -	if (is_imx6q_uart(sport) && !uart_console(port) && -	    !sport->dma_is_inited) -		imx_uart_dma_init(sport); -  	spin_lock_irqsave(&sport->port.lock, flags);  	/* Reset fifo's and state machines */  	i = 100; @@ -1143,9 +1145,6 @@ static int imx_startup(struct uart_port *port)  	writel(USR1_RTSD, sport->port.membase + USR1);  	writel(USR2_ORE, sport->port.membase + USR2); -	if (sport->dma_is_inited && !sport->dma_is_enabled) -		imx_enable_dma(sport); -  	temp = readl(sport->port.membase + UCR1);  	temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; @@ -1316,6 +1315,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,  			} else {  				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;  		} @@ -1432,6 +1436,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);  } @@ -1625,12 +1631,12 @@ imx_console_write(struct console *co, const char *s, unsigned int count)  	int locked = 1;  	int retval; -	retval = clk_enable(sport->clk_per); +	retval = clk_prepare_enable(sport->clk_per);  	if (retval)  		return; -	retval = clk_enable(sport->clk_ipg); +	retval = clk_prepare_enable(sport->clk_ipg);  	if (retval) { -		clk_disable(sport->clk_per); +		clk_disable_unprepare(sport->clk_per);  		return;  	} @@ -1669,8 +1675,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count)  	if (locked)  		spin_unlock_irqrestore(&sport->port.lock, flags); -	clk_disable(sport->clk_ipg); -	clk_disable(sport->clk_per); +	clk_disable_unprepare(sport->clk_ipg); +	clk_disable_unprepare(sport->clk_per);  }  /* @@ -1771,15 +1777,7 @@ imx_console_setup(struct console *co, char *options)  	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); +	clk_disable_unprepare(sport->clk_ipg);  error_console:  	return retval; @@ -1811,36 +1809,6 @@ static struct uart_driver imx_reg = {  	.cons           = IMX_CONSOLE,  }; -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); - -	uart_suspend_port(&imx_reg, &sport->port); - -	return 0; -} - -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); - -	uart_resume_port(&imx_reg, &sport->port); - -	return 0; -} -  #ifdef CONFIG_OF  /*   * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it @@ -1902,7 +1870,7 @@ static int serial_imx_probe(struct platform_device *pdev)  {  	struct imx_port *sport;  	void __iomem *base; -	int ret = 0; +	int ret = 0, reg;  	struct resource *res;  	int txirq, rxirq, rtsirq; @@ -1957,6 +1925,19 @@ static int serial_imx_probe(struct platform_device *pdev)  	sport->port.uartclk = clk_get_rate(sport->clk_per); +	/* For register access, we only need to enable the ipg clock. */ +	ret = clk_prepare_enable(sport->clk_ipg); +	if (ret) +		return ret; + +	/* Disable interrupts before requesting them */ +	reg = readl_relaxed(sport->port.membase + UCR1); +	reg &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | +		 UCR1_TXMPTYEN | UCR1_RTSDEN); +	writel_relaxed(reg, sport->port.membase + UCR1); + +	clk_disable_unprepare(sport->clk_ipg); +  	/*  	 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later  	 * chips only have one interrupt. @@ -1992,16 +1973,135 @@ static int serial_imx_remove(struct platform_device *pdev)  	return uart_remove_one_port(&imx_reg, &sport->port);  } +static void serial_imx_restore_context(struct imx_port *sport) +{ +	if (!sport->context_saved) +		return; + +	writel(sport->saved_reg[4], sport->port.membase + UFCR); +	writel(sport->saved_reg[5], sport->port.membase + UESC); +	writel(sport->saved_reg[6], sport->port.membase + UTIM); +	writel(sport->saved_reg[7], sport->port.membase + UBIR); +	writel(sport->saved_reg[8], sport->port.membase + UBMR); +	writel(sport->saved_reg[9], sport->port.membase + IMX21_UTS); +	writel(sport->saved_reg[0], sport->port.membase + UCR1); +	writel(sport->saved_reg[1] | UCR2_SRST, sport->port.membase + UCR2); +	writel(sport->saved_reg[2], sport->port.membase + UCR3); +	writel(sport->saved_reg[3], sport->port.membase + UCR4); +	sport->context_saved = false; +} + +static void serial_imx_save_context(struct imx_port *sport) +{ +	/* Save necessary regs */ +	sport->saved_reg[0] = readl(sport->port.membase + UCR1); +	sport->saved_reg[1] = readl(sport->port.membase + UCR2); +	sport->saved_reg[2] = readl(sport->port.membase + UCR3); +	sport->saved_reg[3] = readl(sport->port.membase + UCR4); +	sport->saved_reg[4] = readl(sport->port.membase + UFCR); +	sport->saved_reg[5] = readl(sport->port.membase + UESC); +	sport->saved_reg[6] = readl(sport->port.membase + UTIM); +	sport->saved_reg[7] = readl(sport->port.membase + UBIR); +	sport->saved_reg[8] = readl(sport->port.membase + UBMR); +	sport->saved_reg[9] = readl(sport->port.membase + IMX21_UTS); +	sport->context_saved = true; +} + +static void serial_imx_enable_wakeup(struct imx_port *sport, bool on) +{ +	unsigned int val; + +	val = readl(sport->port.membase + UCR3); +	if (on) +		val |= UCR3_AWAKEN; +	else +		val &= ~UCR3_AWAKEN; +	writel(val, sport->port.membase + UCR3); + +	val = readl(sport->port.membase + UCR1); +	if (on) +		val |= UCR1_RTSDEN; +	else +		val &= ~UCR1_RTSDEN; +	writel(val, sport->port.membase + UCR1); +} + +static int imx_serial_port_suspend_noirq(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct imx_port *sport = platform_get_drvdata(pdev); +	int ret; + +	ret = clk_enable(sport->clk_ipg); +	if (ret) +		return ret; + +	serial_imx_save_context(sport); + +	clk_disable(sport->clk_ipg); + +	return 0; +} + +static int imx_serial_port_resume_noirq(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct imx_port *sport = platform_get_drvdata(pdev); +	int ret; + +	ret = clk_enable(sport->clk_ipg); +	if (ret) +		return ret; + +	serial_imx_restore_context(sport); + +	clk_disable(sport->clk_ipg); + +	return 0; +} + +static int imx_serial_port_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct imx_port *sport = platform_get_drvdata(pdev); + +	/* enable wakeup from i.MX UART */ +	serial_imx_enable_wakeup(sport, true); + +	uart_suspend_port(&imx_reg, &sport->port); + +	return 0; +} + +static int imx_serial_port_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct imx_port *sport = platform_get_drvdata(pdev); + +	/* disable wakeup from i.MX UART */ +	serial_imx_enable_wakeup(sport, false); + +	uart_resume_port(&imx_reg, &sport->port); + +	return 0; +} + +static const struct dev_pm_ops imx_serial_port_pm_ops = { +	.suspend_noirq = imx_serial_port_suspend_noirq, +	.resume_noirq = imx_serial_port_resume_noirq, +	.suspend = imx_serial_port_suspend, +	.resume = imx_serial_port_resume, +}; +  static struct platform_driver serial_imx_driver = {  	.probe		= serial_imx_probe,  	.remove		= serial_imx_remove, -	.suspend	= serial_imx_suspend, -	.resume		= serial_imx_resume,  	.id_table	= imx_uart_devtype,  	.driver		= {  		.name	= "imx-uart",  		.of_match_table = imx_uart_dt_ids, +		.pm	= &imx_serial_port_pm_ops,  	},  };  |