* [PATCH 0/8] serial: imx: rework pm support and add runtime pm
@ 2015-08-09 18:19 Eduardo Valentin
2015-08-09 18:19 ` [PATCH 1/8] serial: imx: remove unbalanced clk_prepare Eduardo Valentin
` (7 more replies)
0 siblings, 8 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam; +Cc: Linux PM, Eduardo Valentin
Hello,
This is a patch series to introduce runtime pm in the imx serial driver.
The work is pretty straight forward. The idea is to get runtime pm
to handle ipg and per clocks, idling the device when possible,
configuring wakeups, and saving and restoring context when needed.
A minor refactoring was needed to get things done. Mainly because
the suspend and resume path shares several needed steps with
the runtime pm path. Therefore, several common helper functions
were created to avoid code duplication between these two pm features.
On top of this, I am also adding a simple pm qos requirement. The idea
is to skip deeper c-states when we know the uart port is going to have
latency requirements. The latency is computed based on baud rate and
buffer size.
Most of the code is a design copy of existing serial drivers.
This code has been tested with IMX6SL, mainly with UART1 used as console.
Comments and suggestions of improvements are welcome.
BR,
Eduardo Valentin (8):
serial: imx: remove unbalanced clk_prepare
serial: imx: introduce serial_imx_enable_wakeup()
serial: imx: allow waking up on RTSD
serial: imx: use dev_pm_ops
serial: imx: save and restore context in the suspend path
serial: imx: add a flag to indicate we are in the suspend path
serial: imx: add runtime pm support
serial: imx: add pm_qos request
drivers/tty/serial/imx.c | 342 ++++++++++++++++++++++++++++++++++++++---------
1 file changed, 276 insertions(+), 66 deletions(-)
--
2.5.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/8] serial: imx: remove unbalanced clk_prepare
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
2015-08-09 18:19 ` [PATCH 2/8] serial: imx: introduce serial_imx_enable_wakeup() Eduardo Valentin
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
The current code attempts to prepare clk_per and clk_ipg
before using the device. However, the result is an extra
prepare call on each clock. Here is the output of uart
clocks (only uart enabled and used as console):
$ grep uart /sys/kernel/debug/clk/clk_summary
uart_serial 1 2 80000000 0 0
uart 1 2 66000000 0 0
This patch balances the calls of prepares. The result is:
$ grep uart /sys/kernel/debug/clk/clk_summary
uart_serial 1 1 80000000 0 0
uart 1 1 66000000 0 0
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 54fdc78..b53455d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1624,12 +1624,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;
}
@@ -1668,8 +1668,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);
}
/*
@@ -1770,15 +1770,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;
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/8] serial: imx: introduce serial_imx_enable_wakeup()
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
2015-08-09 18:19 ` [PATCH 1/8] serial: imx: remove unbalanced clk_prepare Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
2015-08-09 18:19 ` [PATCH 3/8] serial: imx: allow waking up on RTSD Eduardo Valentin
` (5 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
This change is a code reorganization. Here we introduce
serial_imx_enable_wakeup() helper function to do
the job of configuring and preparing wakeup sources
on imx serial device. The idea is to allow other
parts of the code to call this function whenever
the device is known to go to idle.
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index b53455d..6c6da8c 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1802,15 +1802,24 @@ static struct uart_driver imx_reg = {
.cons = IMX_CONSOLE,
};
-static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
+static void serial_imx_enable_wakeup(struct imx_port *sport, bool on)
{
- 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;
+ if (on)
+ val |= UCR3_AWAKEN;
+ else
+ val &= ~UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
+}
+
+static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct imx_port *sport = platform_get_drvdata(dev);
+
+ /* enable wakeup from i.MX UART */
+ serial_imx_enable_wakeup(sport, true);
uart_suspend_port(&imx_reg, &sport->port);
@@ -1820,12 +1829,9 @@ 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);
+ serial_imx_enable_wakeup(sport, false);
uart_resume_port(&imx_reg, &sport->port);
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 3/8] serial: imx: allow waking up on RTSD
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
2015-08-09 18:19 ` [PATCH 1/8] serial: imx: remove unbalanced clk_prepare Eduardo Valentin
2015-08-09 18:19 ` [PATCH 2/8] serial: imx: introduce serial_imx_enable_wakeup() Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
2015-08-09 18:19 ` [PATCH 4/8] serial: imx: use dev_pm_ops Eduardo Valentin
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
This patch sets RTSDEN bit when going into idle (Stop mode).
We add the RTSDEN for the case RTS is sent from
the remote connection. This way we allow the system
to wakeup when RTS is received.
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 6c6da8c..70802be 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1812,6 +1812,13 @@ static void serial_imx_enable_wakeup(struct imx_port *sport, bool on)
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 serial_imx_suspend(struct platform_device *dev, pm_message_t state)
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 4/8] serial: imx: use dev_pm_ops
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
` (2 preceding siblings ...)
2015-08-09 18:19 ` [PATCH 3/8] serial: imx: allow waking up on RTSD Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
2015-08-09 18:19 ` [PATCH 5/8] serial: imx: save and restore context in the suspend path Eduardo Valentin
` (3 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
This is a simple reorganization to start using
struct dev_pm_ops instead of assigning .suspend
and .resume callbacks in platform driver.
The change allows further code to be added,
for example, runtime_pm.
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 70802be..a03855d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -39,6 +39,7 @@
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
+#include <linux/pm.h>
#include <asm/irq.h>
#include <linux/platform_data/serial-imx.h>
@@ -1821,9 +1822,9 @@ static void serial_imx_enable_wakeup(struct imx_port *sport, bool on)
writel(val, sport->port.membase + UCR1);
}
-static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
+static int serial_imx_suspend(struct device *dev)
{
- struct imx_port *sport = platform_get_drvdata(dev);
+ struct imx_port *sport = dev_get_drvdata(dev);
/* enable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, true);
@@ -1833,9 +1834,9 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
return 0;
}
-static int serial_imx_resume(struct platform_device *dev)
+static int serial_imx_resume(struct device *dev)
{
- struct imx_port *sport = platform_get_drvdata(dev);
+ struct imx_port *sport = dev_get_drvdata(dev);
/* disable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, false);
@@ -1996,15 +1997,18 @@ static int serial_imx_remove(struct platform_device *pdev)
return uart_remove_one_port(&imx_reg, &sport->port);
}
+static const struct dev_pm_ops serial_imx_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(serial_imx_suspend, serial_imx_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",
+ .pm = &serial_imx_dev_pm_ops,
.of_match_table = imx_uart_dt_ids,
},
};
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 5/8] serial: imx: save and restore context in the suspend path
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
` (3 preceding siblings ...)
2015-08-09 18:19 ` [PATCH 4/8] serial: imx: use dev_pm_ops Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
2015-08-10 3:30 ` Fabio Estevam
2015-08-09 18:19 ` [PATCH 6/8] serial: imx: add a flag to indicate we are " Eduardo Valentin
` (2 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
This change teaches the imx serial driver to save its
context and restore it across suspend and resume path.
To do so, it introduces serial_imx_restore_context()
and serial_imx_save_context() functions. They use
a shadow set of registers to save key registers
and restore them accordingly. These functions can
be reused on other situations, when the device
context is lost.
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index a03855d..494b182 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -217,6 +217,8 @@ struct imx_port {
unsigned int tx_bytes;
unsigned int dma_tx_nents;
wait_queue_head_t dma_wait;
+ unsigned int saved_reg[8];
+ bool context_saved;
};
struct imx_port_ucrs {
@@ -1803,6 +1805,36 @@ static struct uart_driver imx_reg = {
.cons = IMX_CONSOLE,
};
+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 + UBIR);
+ writel(sport->saved_reg[6], sport->port.membase + UBMR);
+ writel(sport->saved_reg[7], sport->port.membase + IMX21_UTS);
+ writel(sport->saved_reg[0], sport->port.membase + UCR1);
+ writel(sport->saved_reg[1] | 0x1, 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 + UBIR);
+ sport->saved_reg[6] = readl(sport->port.membase + UBMR);
+ sport->saved_reg[7] = 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;
@@ -1826,6 +1858,7 @@ static int serial_imx_suspend(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
+ serial_imx_save_context(sport);
/* enable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, true);
@@ -1841,6 +1874,7 @@ static int serial_imx_resume(struct device *dev)
/* disable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, false);
+ serial_imx_restore_context(sport);
uart_resume_port(&imx_reg, &sport->port);
return 0;
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 6/8] serial: imx: add a flag to indicate we are in the suspend path
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
` (4 preceding siblings ...)
2015-08-09 18:19 ` [PATCH 5/8] serial: imx: save and restore context in the suspend path Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
2015-08-09 18:19 ` [PATCH 7/8] serial: imx: add runtime pm support Eduardo Valentin
2015-08-09 18:19 ` [PATCH 8/8] serial: imx: add pm_qos request Eduardo Valentin
7 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
This is a simple flag that gets set across prepare and complete
callbacks in the suspend path. We the flag we may avoid
runtime pm idling at the same time.
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 494b182..def52e0 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -219,6 +219,8 @@ struct imx_port {
wait_queue_head_t dma_wait;
unsigned int saved_reg[8];
bool context_saved;
+
+ bool is_suspending;
};
struct imx_port_ucrs {
@@ -1805,6 +1807,22 @@ static struct uart_driver imx_reg = {
.cons = IMX_CONSOLE,
};
+static int serial_imx_prepare(struct device *dev)
+{
+ struct imx_port *sport = dev_get_drvdata(dev);
+
+ sport->is_suspending = true;
+
+ return 0;
+}
+
+static void serial_imx_complete(struct device *dev)
+{
+ struct imx_port *sport = dev_get_drvdata(dev);
+
+ sport->is_suspending = false;
+}
+
static void serial_imx_restore_context(struct imx_port *sport)
{
if (!sport->context_saved)
@@ -2033,6 +2051,8 @@ static int serial_imx_remove(struct platform_device *pdev)
static const struct dev_pm_ops serial_imx_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(serial_imx_suspend, serial_imx_resume)
+ .prepare = serial_imx_prepare,
+ .complete = serial_imx_complete,
};
static struct platform_driver serial_imx_driver = {
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 7/8] serial: imx: add runtime pm support
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
` (5 preceding siblings ...)
2015-08-09 18:19 ` [PATCH 6/8] serial: imx: add a flag to indicate we are " Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
2015-08-09 18:19 ` [PATCH 8/8] serial: imx: add pm_qos request Eduardo Valentin
7 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
This change introduces the runtime pm support on imx serial
driver. The objective is to be able to idle the uart
port whenever it is not in use while still being able
to wake it up when needed. The key changes in this patch are:
1. Move the clock handling to runtime pm. Both, ipg and per,
are now handled in the suspend and resume callbacks. Only
enabling and disabling the clocks are handled in runtime
suspend and resume, so we are able to use runtime pm
in IRQ context.
2. Clocks are prepared in probe and unprepared in remove,
so we do not need to prepare (may sleep) in runtime pm.
3. We mark the device activity based on uart and console
callbacks. Whenever the device is needed and we want to
access registers, we runtime_pm_get and then mark its
last usage when we are done. This is done also across
IRQs and DMA callbacks.
4. We reuse the infrastructure in place for suspend and
resume, so we do not need to redo wakeup configuration,
or context save and restore.
After this change, the clocks are still sane, in the sense
of having balanced clock prepare and enable.
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 209 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 163 insertions(+), 46 deletions(-)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index def52e0..e30d8c0 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -40,6 +40,8 @@
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeup.h>
#include <asm/irq.h>
#include <linux/platform_data/serial-imx.h>
@@ -220,6 +222,7 @@ struct imx_port {
unsigned int saved_reg[8];
bool context_saved;
+ struct device *dev;
bool is_suspending;
};
@@ -370,6 +373,7 @@ static void imx_stop_tx(struct uart_port *port)
if (sport->dma_is_enabled && sport->dma_is_txing)
return;
+ pm_runtime_get_sync(sport->dev);
temp = readl(port->membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1);
@@ -387,6 +391,8 @@ static void imx_stop_tx(struct uart_port *port)
temp &= ~UCR4_TCEN;
writel(temp, port->membase + UCR4);
}
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
/*
@@ -406,12 +412,15 @@ static void imx_stop_rx(struct uart_port *port)
}
}
+ pm_runtime_get_sync(sport->dev);
temp = readl(sport->port.membase + UCR2);
writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
/* disable the `Receiver Ready Interrrupt` */
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
/*
@@ -483,6 +492,7 @@ static void dma_tx_callback(void *data)
unsigned long flags;
unsigned long temp;
+ pm_runtime_get_sync(sport->dev);
spin_lock_irqsave(&sport->port.lock, flags);
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
@@ -507,13 +517,17 @@ static void dma_tx_callback(void *data)
if (waitqueue_active(&sport->dma_wait)) {
wake_up(&sport->dma_wait);
dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
- return;
+ goto mark_last;
}
spin_lock_irqsave(&sport->port.lock, flags);
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
imx_dma_tx(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
+
+mark_last:
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
static void imx_dma_tx(struct imx_port *sport)
@@ -580,6 +594,7 @@ static void imx_start_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ pm_runtime_get_sync(sport->dev);
if (port->rs485.flags & SER_RS485_ENABLED) {
/* enable transmitter and shifter empty irq */
temp = readl(port->membase + UCR2);
@@ -607,14 +622,18 @@ static void imx_start_tx(struct uart_port *port)
temp &= ~UCR1_TDMAEN;
temp |= UCR1_TXMPTYEN;
writel(temp, sport->port.membase + UCR1);
- return;
+ goto mark_last;
}
if (!uart_circ_empty(&port->state->xmit) &&
!uart_tx_stopped(port))
imx_dma_tx(sport);
- return;
+ goto mark_last;
}
+
+mark_last:
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
static irqreturn_t imx_rtsint(int irq, void *dev_id)
@@ -623,6 +642,7 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id)
unsigned int val;
unsigned long flags;
+ pm_runtime_get_sync(sport->dev);
spin_lock_irqsave(&sport->port.lock, flags);
writel(USR1_RTSD, sport->port.membase + USR1);
@@ -631,6 +651,9 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id)
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
spin_unlock_irqrestore(&sport->port.lock, flags);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
+
return IRQ_HANDLED;
}
@@ -639,9 +662,13 @@ static irqreturn_t imx_txint(int irq, void *dev_id)
struct imx_port *sport = dev_id;
unsigned long flags;
+ pm_runtime_get_sync(sport->dev);
spin_lock_irqsave(&sport->port.lock, flags);
imx_transmit_buffer(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
+
return IRQ_HANDLED;
}
@@ -652,6 +679,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
struct tty_port *port = &sport->port.state->port;
unsigned long flags, temp;
+ pm_runtime_get_sync(sport->dev);
spin_lock_irqsave(&sport->port.lock, flags);
while (readl(sport->port.membase + USR2) & USR2_RDR) {
@@ -711,6 +739,8 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
out:
spin_unlock_irqrestore(&sport->port.lock, flags);
tty_flip_buffer_push(port);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
return IRQ_HANDLED;
}
@@ -748,6 +778,7 @@ static irqreturn_t imx_int(int irq, void *dev_id)
unsigned int sts;
unsigned int sts2;
+ pm_runtime_get_sync(sport->dev);
sts = readl(sport->port.membase + USR1);
sts2 = readl(sport->port.membase + USR2);
@@ -775,6 +806,8 @@ static irqreturn_t imx_int(int irq, void *dev_id)
sport->port.icount.overrun++;
writel(USR2_ORE, sport->port.membase + USR2);
}
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
return IRQ_HANDLED;
}
@@ -787,11 +820,14 @@ static unsigned int imx_tx_empty(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned int ret;
+ pm_runtime_get_sync(sport->dev);
ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
/* If the TX DMA is working, return 0. */
if (sport->dma_is_enabled && sport->dma_is_txing)
ret = 0;
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
return ret;
}
@@ -804,6 +840,7 @@ static unsigned int imx_get_mctrl(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
+ pm_runtime_get_sync(sport->dev);
if (readl(sport->port.membase + USR1) & USR1_RTSS)
tmp |= TIOCM_CTS;
@@ -812,6 +849,8 @@ static unsigned int imx_get_mctrl(struct uart_port *port)
if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP)
tmp |= TIOCM_LOOP;
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
return tmp;
}
@@ -821,6 +860,7 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ pm_runtime_get_sync(sport->dev);
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_CTS | UCR2_CTSC);
@@ -833,6 +873,8 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
if (mctrl & TIOCM_LOOP)
temp |= UTS_LOOP;
writel(temp, sport->port.membase + uts_reg(sport));
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
/*
@@ -843,6 +885,7 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags, temp;
+ pm_runtime_get_sync(sport->dev);
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
@@ -853,6 +896,8 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
writel(temp, sport->port.membase + UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
#define TXTL 2 /* reset default */
@@ -910,6 +955,7 @@ static void dma_rx_callback(void *data)
enum dma_status status;
unsigned int count;
+ pm_runtime_get_sync(sport->dev);
/* unmap it first */
dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
@@ -946,6 +992,8 @@ static void dma_rx_callback(void *data)
*/
imx_rx_dma_done(sport);
}
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
static int start_rx_dma(struct imx_port *sport)
@@ -1101,18 +1149,10 @@ static void imx_disable_dma(struct imx_port *sport)
static int imx_startup(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- int retval, i;
+ int i;
unsigned long flags, temp;
- retval = clk_prepare_enable(sport->clk_per);
- if (retval)
- return retval;
- retval = clk_prepare_enable(sport->clk_ipg);
- if (retval) {
- clk_disable_unprepare(sport->clk_per);
- return retval;
- }
-
+ pm_runtime_get_sync(sport->dev);
imx_setup_ufcr(sport, 0);
/* disable the DREN bit (Data Ready interrupt enable) before
@@ -1169,6 +1209,8 @@ static int imx_startup(struct uart_port *port)
*/
imx_enable_ms(&sport->port);
spin_unlock_irqrestore(&sport->port.lock, flags);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
return 0;
}
@@ -1179,6 +1221,7 @@ static void imx_shutdown(struct uart_port *port)
unsigned long temp;
unsigned long flags;
+ pm_runtime_get_sync(sport->dev);
if (sport->dma_is_enabled) {
int ret;
@@ -1221,8 +1264,8 @@ static void imx_shutdown(struct uart_port *port)
writel(temp, sport->port.membase + UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
- clk_disable_unprepare(sport->clk_per);
- clk_disable_unprepare(sport->clk_ipg);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
static void imx_flush_buffer(struct uart_port *port)
@@ -1235,6 +1278,7 @@ static void imx_flush_buffer(struct uart_port *port)
if (!sport->dma_chan_tx)
return;
+ pm_runtime_get_sync(sport->dev);
sport->tx_bytes = 0;
dmaengine_terminate_all(sport->dma_chan_tx);
if (sport->dma_is_txing) {
@@ -1268,6 +1312,8 @@ static void imx_flush_buffer(struct uart_port *port)
writel(ubir, sport->port.membase + UBIR);
writel(ubmr, sport->port.membase + UBMR);
writel(uts, sport->port.membase + IMX21_UTS);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
static void
@@ -1282,6 +1328,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned long num, denom;
uint64_t tdiv64;
+ pm_runtime_get_sync(sport->dev);
/*
* We only support CS7 and CS8.
*/
@@ -1437,6 +1484,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
if (sport->dma_is_inited && !sport->dma_is_enabled)
imx_enable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
static const char *imx_type(struct uart_port *port)
@@ -1494,13 +1543,7 @@ static int imx_poll_init(struct uart_port *port)
unsigned long temp;
int retval;
- retval = clk_prepare_enable(sport->clk_ipg);
- if (retval)
- return retval;
- retval = clk_prepare_enable(sport->clk_per);
- if (retval)
- clk_disable_unprepare(sport->clk_ipg);
-
+ pm_runtime_get_sync(sport->dev);
imx_setup_ufcr(sport, 0);
spin_lock_irqsave(&sport->port.lock, flags);
@@ -1517,22 +1560,34 @@ static int imx_poll_init(struct uart_port *port)
writel(temp, sport->port.membase + UCR2);
spin_unlock_irqrestore(&sport->port.lock, flags);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
return 0;
}
static int imx_poll_get_char(struct uart_port *port)
{
- if (!(readl_relaxed(port->membase + USR2) & USR2_RDR))
- return NO_POLL_CHAR;
+ int ret;
+
+ pm_runtime_get_sync(sport->dev);
+ if (!(readl_relaxed(port->membase + USR2) & USR2_RDR)) {
+ ret = NO_POLL_CHAR;
+ goto mark_last;
+ }
- return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA;
+ ret = readl_relaxed(port->membase + URXD0) & URXD_RX_DATA;
+
+mark_last:
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
static void imx_poll_put_char(struct uart_port *port, unsigned char c)
{
unsigned int status;
+ pm_runtime_get_sync(sport->dev);
/* drain */
do {
status = readl_relaxed(port->membase + USR1);
@@ -1545,6 +1600,8 @@ static void imx_poll_put_char(struct uart_port *port, unsigned char c)
do {
status = readl_relaxed(port->membase + USR2);
} while (~status & USR2_TXDC);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
#endif
@@ -1565,6 +1622,7 @@ static int imx_rs485_config(struct uart_port *port,
if (rs485conf->flags & SER_RS485_ENABLED) {
unsigned long temp;
+ pm_runtime_get_sync(sport->dev);
/* disable transmitter */
temp = readl(sport->port.membase + UCR2);
temp &= ~UCR2_CTSC;
@@ -1573,6 +1631,8 @@ static int imx_rs485_config(struct uart_port *port,
else
temp |= UCR2_CTS;
writel(temp, sport->port.membase + UCR2);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
port->rs485 = *rs485conf;
@@ -1627,17 +1687,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
unsigned int ucr1;
unsigned long flags = 0;
int locked = 1;
- int retval;
-
- retval = clk_prepare_enable(sport->clk_per);
- if (retval)
- return;
- retval = clk_prepare_enable(sport->clk_ipg);
- if (retval) {
- clk_disable_unprepare(sport->clk_per);
- return;
- }
+ pm_runtime_get_sync(sport->dev);
if (sport->port.sysrq)
locked = 0;
else if (oops_in_progress)
@@ -1673,8 +1724,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags);
- clk_disable_unprepare(sport->clk_ipg);
- clk_disable_unprepare(sport->clk_per);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
}
/*
@@ -1761,10 +1812,7 @@ imx_console_setup(struct console *co, char *options)
if (sport == NULL)
return -ENODEV;
- /* For setting the registers, we only need to enable the ipg clock. */
- retval = clk_prepare_enable(sport->clk_ipg);
- if (retval)
- goto error_console;
+ pm_runtime_get_sync(sport->dev);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1775,9 +1823,8 @@ imx_console_setup(struct console *co, char *options)
retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
- clk_disable_unprepare(sport->clk_ipg);
-
-error_console:
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
return retval;
}
@@ -1881,6 +1928,8 @@ static int serial_imx_suspend(struct device *dev)
serial_imx_enable_wakeup(sport, true);
uart_suspend_port(&imx_reg, &sport->port);
+ clk_disable_unprepare(sport->clk_per);
+ clk_disable_unprepare(sport->clk_ipg);
return 0;
}
@@ -1889,6 +1938,8 @@ static int serial_imx_resume(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
+ clk_prepare_enable(sport->clk_per);
+ clk_prepare_enable(sport->clk_ipg);
/* disable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, false);
@@ -2037,20 +2088,86 @@ static int serial_imx_probe(struct platform_device *pdev)
imx_ports[sport->port.line] = sport;
+ sport->dev = &pdev->dev;
platform_set_drvdata(pdev, sport);
- return uart_add_one_port(&imx_reg, &sport->port);
+ device_init_wakeup(sport->dev, true);
+ pm_runtime_use_autosuspend(sport->dev);
+ pm_runtime_set_autosuspend_delay(sport->dev, 5000);
+
+ pm_runtime_irq_safe(sport->dev);
+ pm_runtime_enable(sport->dev);
+
+ clk_prepare(sport->clk_per);
+ clk_prepare(sport->clk_ipg);
+ pm_runtime_get_sync(sport->dev);
+ ret = uart_add_one_port(&imx_reg, &sport->port);
+ pm_runtime_mark_last_busy(sport->dev);
+ pm_runtime_put_autosuspend(sport->dev);
+
+ return ret;
}
static int serial_imx_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
+ int ret;
+
+ pm_runtime_put_sync(sport->dev);
+ pm_runtime_disable(sport->dev);
+ clk_unprepare(sport->clk_per);
+ clk_unprepare(sport->clk_ipg);
+ ret = uart_remove_one_port(&imx_reg, &sport->port);
+ device_init_wakeup(&pdev->dev, false);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int serial_imx_runtime_suspend(struct device *dev)
+{
+ struct imx_port *sport = dev_get_drvdata(dev);
+
+ if (!sport)
+ return -EINVAL;
+
+ /*
+ * When using 'no_console_suspend', the console UART must not be
+ * suspended. Since driver suspend is managed by runtime suspend,
+ * preventing runtime suspend (by returning error) will keep device
+ * active during suspend.
+ */
+ if (sport->is_suspending && !console_suspend_enabled &&
+ uart_console(&sport->port))
+ return -EBUSY;
+
+ serial_imx_save_context(sport);
+ serial_imx_enable_wakeup(sport, true);
- return uart_remove_one_port(&imx_reg, &sport->port);
+ clk_disable(sport->clk_per);
+ clk_disable(sport->clk_ipg);
+
+ return 0;
}
+static int serial_imx_runtime_resume(struct device *dev)
+{
+ struct imx_port *sport = dev_get_drvdata(dev);
+
+ clk_enable(sport->clk_per);
+ clk_enable(sport->clk_ipg);
+ serial_imx_enable_wakeup(sport, false);
+
+ serial_imx_restore_context(sport);
+
+ return 0;
+}
+#endif
+
static const struct dev_pm_ops serial_imx_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(serial_imx_suspend, serial_imx_resume)
+ SET_RUNTIME_PM_OPS(serial_imx_runtime_suspend,
+ serial_imx_runtime_resume, NULL)
.prepare = serial_imx_prepare,
.complete = serial_imx_complete,
};
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 8/8] serial: imx: add pm_qos request
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
` (6 preceding siblings ...)
2015-08-09 18:19 ` [PATCH 7/8] serial: imx: add runtime pm support Eduardo Valentin
@ 2015-08-09 18:19 ` Eduardo Valentin
7 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-09 18:19 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Fabio Stevam
Cc: Linux PM, Eduardo Valentin, linux-serial, linux-kernel
This change introduces pm_qos requests in the imx serial driver.
The idea is to skip deeper C-state in case we need a strict
latency requirement in the uart port. The latency is
computed based on the buffer size and the current baud rate.
We schedule a work queue to set the pm qos requirement.
Cc: Fabio Stevam <festevam@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
---
drivers/tty/serial/imx.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index e30d8c0..fe62349 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -42,6 +42,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeup.h>
+#include <linux/pm_qos.h>
#include <asm/irq.h>
#include <linux/platform_data/serial-imx.h>
@@ -223,6 +224,10 @@ struct imx_port {
bool context_saved;
struct device *dev;
+ struct pm_qos_request pm_qos_request;
+ u32 latency;
+ u32 calc_latency;
+ struct work_struct qos_work;
bool is_suspending;
};
@@ -1316,6 +1321,14 @@ static void imx_flush_buffer(struct uart_port *port)
pm_runtime_put_autosuspend(sport->dev);
}
+static void serial_imx_uart_qos_work(struct work_struct *work)
+{
+ struct imx_port *sport = container_of(work, struct imx_port,
+ qos_work);
+
+ pm_qos_update_request(&sport->pm_qos_request, sport->latency);
+}
+
static void
imx_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
@@ -1389,6 +1402,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
quot = uart_get_divisor(port, baud);
+ /* calculate wakeup latency constraint */
+ sport->calc_latency = (USEC_PER_SEC * sport->port.fifosize) /
+ (baud / 8);
+ sport->latency = sport->calc_latency;
+ schedule_work(&sport->qos_work);
+
spin_lock_irqsave(&sport->port.lock, flags);
sport->port.read_status_mask = 0;
@@ -1923,6 +1942,7 @@ static int serial_imx_suspend(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
+ flush_work(&sport->qos_work);
serial_imx_save_context(sport);
/* enable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, true);
@@ -2088,7 +2108,12 @@ static int serial_imx_probe(struct platform_device *pdev)
imx_ports[sport->port.line] = sport;
+ sport->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+ sport->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
sport->dev = &pdev->dev;
+ pm_qos_add_request(&sport->pm_qos_request, PM_QOS_CPU_DMA_LATENCY,
+ sport->latency);
+ INIT_WORK(&sport->qos_work, serial_imx_uart_qos_work);
platform_set_drvdata(pdev, sport);
device_init_wakeup(sport->dev, true);
@@ -2118,6 +2143,7 @@ static int serial_imx_remove(struct platform_device *pdev)
clk_unprepare(sport->clk_per);
clk_unprepare(sport->clk_ipg);
ret = uart_remove_one_port(&imx_reg, &sport->port);
+ pm_qos_remove_request(&sport->pm_qos_request);
device_init_wakeup(&pdev->dev, false);
return ret;
@@ -2144,6 +2170,8 @@ static int serial_imx_runtime_suspend(struct device *dev)
serial_imx_save_context(sport);
serial_imx_enable_wakeup(sport, true);
+ sport->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+ schedule_work(&sport->qos_work);
clk_disable(sport->clk_per);
clk_disable(sport->clk_ipg);
@@ -2158,6 +2186,8 @@ static int serial_imx_runtime_resume(struct device *dev)
clk_enable(sport->clk_ipg);
serial_imx_enable_wakeup(sport, false);
+ sport->latency = sport->calc_latency;
+ schedule_work(&sport->qos_work);
serial_imx_restore_context(sport);
return 0;
--
2.5.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 5/8] serial: imx: save and restore context in the suspend path
2015-08-09 18:19 ` [PATCH 5/8] serial: imx: save and restore context in the suspend path Eduardo Valentin
@ 2015-08-10 3:30 ` Fabio Estevam
2015-08-10 4:04 ` Eduardo Valentin
0 siblings, 1 reply; 13+ messages in thread
From: Fabio Estevam @ 2015-08-10 3:30 UTC (permalink / raw)
To: Eduardo Valentin
Cc: Greg Kroah-Hartman, Jiri Slaby, Linux PM,
linux-serial@vger.kernel.org, linux-kernel, Shenwei Wang
Hi Eduardo,
On Sun, Aug 9, 2015 at 3:19 PM, Eduardo Valentin <edubezval@gmail.com> wrote:
> This change teaches the imx serial driver to save its
> context and restore it across suspend and resume path.
> To do so, it introduces serial_imx_restore_context()
> and serial_imx_save_context() functions. They use
> a shadow set of registers to save key registers
> and restore them accordingly. These functions can
> be reused on other situations, when the device
> context is lost.
>
> Cc: Fabio Stevam <festevam@gmail.com>
s/Stevam/Estevam (applies to all the patches)
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Jiri Slaby <jslaby@suse.com>
> Cc: linux-serial@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
> ---
> drivers/tty/serial/imx.c | 34 ++++++++++++++++++++++++++++++++++
> 1 file changed, 34 insertions(+)
>
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index a03855d..494b182 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -217,6 +217,8 @@ struct imx_port {
> unsigned int tx_bytes;
> unsigned int dma_tx_nents;
> wait_queue_head_t dma_wait;
> + unsigned int saved_reg[8];
> + bool context_saved;
> };
>
> struct imx_port_ucrs {
> @@ -1803,6 +1805,36 @@ static struct uart_driver imx_reg = {
> .cons = IMX_CONSOLE,
> };
>
> +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 + UBIR);
> + writel(sport->saved_reg[6], sport->port.membase + UBMR);
> + writel(sport->saved_reg[7], sport->port.membase + IMX21_UTS);
> + writel(sport->saved_reg[0], sport->port.membase + UCR1);
> + writel(sport->saved_reg[1] | 0x1, 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 + UBIR);
> + sport->saved_reg[6] = readl(sport->port.membase + UBMR);
> + sport->saved_reg[7] = readl(sport->port.membase + IMX21_UTS);
> + sport->context_saved = true;
> +}
This seems to be similar to what Shenwei posted earlier:
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-July/360841.html
Regards,
Fabio Estevam
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 5/8] serial: imx: save and restore context in the suspend path
2015-08-10 3:30 ` Fabio Estevam
@ 2015-08-10 4:04 ` Eduardo Valentin
2015-08-10 12:17 ` Fabio Estevam
0 siblings, 1 reply; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-10 4:04 UTC (permalink / raw)
To: Fabio Estevam
Cc: Greg Kroah-Hartman, Jiri Slaby, Linux PM,
linux-serial@vger.kernel.org, linux-kernel, Shenwei Wang
On Mon, Aug 10, 2015 at 12:30:18AM -0300, Fabio Estevam wrote:
> Hi Eduardo,
>
> On Sun, Aug 9, 2015 at 3:19 PM, Eduardo Valentin <edubezval@gmail.com> wrote:
> > This change teaches the imx serial driver to save its
> > context and restore it across suspend and resume path.
> > To do so, it introduces serial_imx_restore_context()
> > and serial_imx_save_context() functions. They use
> > a shadow set of registers to save key registers
> > and restore them accordingly. These functions can
> > be reused on other situations, when the device
> > context is lost.
> >
> > Cc: Fabio Stevam <festevam@gmail.com>
>
> s/Stevam/Estevam (applies to all the patches)
Sorry, my bad here.
>
> > Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > Cc: Jiri Slaby <jslaby@suse.com>
> > Cc: linux-serial@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org
> > Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
> > ---
> > drivers/tty/serial/imx.c | 34 ++++++++++++++++++++++++++++++++++
> > 1 file changed, 34 insertions(+)
> >
> > diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> > index a03855d..494b182 100644
> > --- a/drivers/tty/serial/imx.c
> > +++ b/drivers/tty/serial/imx.c
> > @@ -217,6 +217,8 @@ struct imx_port {
> > unsigned int tx_bytes;
> > unsigned int dma_tx_nents;
> > wait_queue_head_t dma_wait;
> > + unsigned int saved_reg[8];
> > + bool context_saved;
> > };
> >
> > struct imx_port_ucrs {
> > @@ -1803,6 +1805,36 @@ static struct uart_driver imx_reg = {
> > .cons = IMX_CONSOLE,
> > };
> >
> > +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 + UBIR);
> > + writel(sport->saved_reg[6], sport->port.membase + UBMR);
> > + writel(sport->saved_reg[7], sport->port.membase + IMX21_UTS);
> > + writel(sport->saved_reg[0], sport->port.membase + UCR1);
> > + writel(sport->saved_reg[1] | 0x1, 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 + UBIR);
> > + sport->saved_reg[6] = readl(sport->port.membase + UBMR);
> > + sport->saved_reg[7] = readl(sport->port.membase + IMX21_UTS);
> > + sport->context_saved = true;
> > +}
>
> This seems to be similar to what Shenwei posted earlier:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2015-July/360841.html
OK. Do you know what is the status of that patch set?
did it get accepted by any tree so far?
>
> Regards,
>
> Fabio Estevam
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 5/8] serial: imx: save and restore context in the suspend path
2015-08-10 4:04 ` Eduardo Valentin
@ 2015-08-10 12:17 ` Fabio Estevam
2015-08-10 23:50 ` Eduardo Valentin
0 siblings, 1 reply; 13+ messages in thread
From: Fabio Estevam @ 2015-08-10 12:17 UTC (permalink / raw)
To: Eduardo Valentin
Cc: Greg Kroah-Hartman, Jiri Slaby, Linux PM,
linux-serial@vger.kernel.org, linux-kernel, Shenwei Wang
On Mon, Aug 10, 2015 at 1:04 AM, Eduardo Valentin <edubezval@gmail.com> wrote:
> OK. Do you know what is the status of that patch set?
>
> did it get accepted by any tree so far?
It is in linux-next.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 5/8] serial: imx: save and restore context in the suspend path
2015-08-10 12:17 ` Fabio Estevam
@ 2015-08-10 23:50 ` Eduardo Valentin
0 siblings, 0 replies; 13+ messages in thread
From: Eduardo Valentin @ 2015-08-10 23:50 UTC (permalink / raw)
To: Fabio Estevam
Cc: Greg Kroah-Hartman, Jiri Slaby, Linux PM,
linux-serial@vger.kernel.org, linux-kernel, Shenwei Wang
[-- Attachment #1: Type: text/plain, Size: 387 bytes --]
On Mon, Aug 10, 2015 at 09:17:55AM -0300, Fabio Estevam wrote:
> On Mon, Aug 10, 2015 at 1:04 AM, Eduardo Valentin <edubezval@gmail.com> wrote:
>
> > OK. Do you know what is the status of that patch set?
> >
> > did it get accepted by any tree so far?
>
> It is in linux-next.
Ok. I am rebasing my changes then. I will fix the naming issue too.
BR,
Eduardo Valentin
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2015-08-10 23:51 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-09 18:19 [PATCH 0/8] serial: imx: rework pm support and add runtime pm Eduardo Valentin
2015-08-09 18:19 ` [PATCH 1/8] serial: imx: remove unbalanced clk_prepare Eduardo Valentin
2015-08-09 18:19 ` [PATCH 2/8] serial: imx: introduce serial_imx_enable_wakeup() Eduardo Valentin
2015-08-09 18:19 ` [PATCH 3/8] serial: imx: allow waking up on RTSD Eduardo Valentin
2015-08-09 18:19 ` [PATCH 4/8] serial: imx: use dev_pm_ops Eduardo Valentin
2015-08-09 18:19 ` [PATCH 5/8] serial: imx: save and restore context in the suspend path Eduardo Valentin
2015-08-10 3:30 ` Fabio Estevam
2015-08-10 4:04 ` Eduardo Valentin
2015-08-10 12:17 ` Fabio Estevam
2015-08-10 23:50 ` Eduardo Valentin
2015-08-09 18:19 ` [PATCH 6/8] serial: imx: add a flag to indicate we are " Eduardo Valentin
2015-08-09 18:19 ` [PATCH 7/8] serial: imx: add runtime pm support Eduardo Valentin
2015-08-09 18:19 ` [PATCH 8/8] serial: imx: add pm_qos request Eduardo Valentin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).