Commit 90bb6bd3 authored by Shenwei Wang's avatar Shenwei Wang Committed by Greg Kroah-Hartman

Serial: imx: add dev_pm_ops to support suspend to ram/disk

When system goes into low power states like SUSPEND_MEM and
HIBERNATION, the hardware IP block may be powered off to reduce
the power consumption. This power down may cause problems on
some imx platforms, because the hardware settings are reset to
its power on default values which may differ from the ones when
it power off. This patch added the dev_pm_ops and implemented
two callbacks: suspend_noirq and resume_noirq, which will save
the necessory hardware parameters right before power down and
recover them before system uses the hardware.

Because added the dev_pm_ops, the old suspend/resume callbacks
under platform_driver will not be called any more. Changed their
prototypes and moved those two callbacks into dev_pm_ops too.
Signed-off-by: default avatarShenwei Wang <shenwei.wang@freescale.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e70e69bf
...@@ -216,6 +216,7 @@ struct imx_port { ...@@ -216,6 +216,7 @@ struct imx_port {
unsigned int tx_bytes; unsigned int tx_bytes;
unsigned int dma_tx_nents; unsigned int dma_tx_nents;
wait_queue_head_t dma_wait; wait_queue_head_t dma_wait;
unsigned int saved_reg[10];
}; };
struct imx_port_ucrs { struct imx_port_ucrs {
...@@ -1815,36 +1816,6 @@ static struct uart_driver imx_reg = { ...@@ -1815,36 +1816,6 @@ static struct uart_driver imx_reg = {
.cons = IMX_CONSOLE, .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 #ifdef CONFIG_OF
/* /*
* This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it
...@@ -2009,16 +1980,107 @@ static int serial_imx_remove(struct platform_device *pdev) ...@@ -2009,16 +1980,107 @@ static int serial_imx_remove(struct platform_device *pdev)
return uart_remove_one_port(&imx_reg, &sport->port); return uart_remove_one_port(&imx_reg, &sport->port);
} }
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;
/* 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);
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;
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);
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);
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 imx_serial_port_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
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;
}
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 = { static struct platform_driver serial_imx_driver = {
.probe = serial_imx_probe, .probe = serial_imx_probe,
.remove = serial_imx_remove, .remove = serial_imx_remove,
.suspend = serial_imx_suspend,
.resume = serial_imx_resume,
.id_table = imx_uart_devtype, .id_table = imx_uart_devtype,
.driver = { .driver = {
.name = "imx-uart", .name = "imx-uart",
.of_match_table = imx_uart_dt_ids, .of_match_table = imx_uart_dt_ids,
.pm = &imx_serial_port_pm_ops,
}, },
}; };
......
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