The following 2 patches implement a PM hook for platform 8250
UARTs and a sample PM implementation for a MIPS SoC.
Patch #1 hooks a new .pm callback in struct plat_serial8250_port to
the rest of serial_core's PM infrastructure,
Patch #2 implements uart power gating for Alchemy line of mips socs
using the new hook.
With these 2 patches serial console on my test system survives
suspend/resume cycles without having to resort to platform-specific
hacks in the PM code.
Thanks,
Manuel Lauss
Manuel Lauss (2):
8250: allow platform uarts to install PM callback.
Alchemy: UART PM through serial framework.
arch/mips/alchemy/common/platform.c | 17 +++++++++++++++++
arch/mips/alchemy/common/power.c | 32 --------------------------------
drivers/serial/8250.c | 31 ++++++++++++++++++++++++++++---
include/linux/serial_8250.h | 6 ++++++
4 files changed, 51 insertions(+), 35 deletions(-)
--
The 8250 UART driver provides rudimentary UART PM support and
a callback for systems to do more sophisticated power management.
However, there is no way yet for platform_device uarts to assign
a function to this internal callback.
This patch adds a callback to plat_8250_port and a function to
register this callback with 8250 driver internals.
Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
drivers/serial/8250.c | 31 ++++++++++++++++++++++++++++---
include/linux/serial_8250.h | 6 ++++++
2 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index c3db16b..c0c8e9b 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -2995,7 +2995,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
port.serial_out = p->serial_out;
port.dev = &dev->dev;
port.irqflags |= irqflag;
- ret = serial8250_register_port(&port);
+ ret = serial8250_register_port_with_pm(&port, p->pm);
if (ret < 0) {
dev_err(&dev->dev, "unable to register port at index %d "
"(IO%lx MEM%llx IRQ%d): %d\n", i,
@@ -3107,8 +3107,10 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
}
/**
- * serial8250_register_port - register a serial port
+ * serial8250_register_port_with_pm - register a serial port and its
+ * power management callback.
* @port: serial port template
+ * @pm: PM callback for this port, can be NULL.
*
* Configure the serial port specified by the request. If the
* port exists and is in use, it is hung up and unregistered
@@ -3119,7 +3121,9 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
*
* On success the port is ready to use and the line number is returned.
*/
-int serial8250_register_port(struct uart_port *port)
+int serial8250_register_port_with_pm(struct uart_port *port,
+ void(*pm)(struct uart_port *port, unsigned int state,
+ unsigned int old))
{
struct ...Hook up the Alchemy on-chip uarts with the platform 8250 PM callback
and enable/disable the uart blocks as needed.
Tested on Au1200.
Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
arch/mips/alchemy/common/platform.c | 17 +++++++++++++++++
arch/mips/alchemy/common/power.c | 32 --------------------------------
2 files changed, 17 insertions(+), 32 deletions(-)
diff --git a/arch/mips/alchemy/common/platform.c b/arch/mips/alchemy/common/platform.c
index 2580e77..70f4abd 100644
--- a/arch/mips/alchemy/common/platform.c
+++ b/arch/mips/alchemy/common/platform.c
@@ -21,6 +21,22 @@
#include <asm/mach-au1x00/au1100_mmc.h>
#include <asm/mach-au1x00/au1xxx_eth.h>
+static void alchemy_8250_pm(struct uart_port *port, unsigned int state,
+ unsigned int old_state)
+{
+ if (state == 0) { /* power on */
+ __raw_writel(0, port->membase + UART_MOD_CNTRL);
+ wmb();
+ __raw_writel(1, port->membase + UART_MOD_CNTRL);
+ wmb();
+ __raw_writel(3, port->membase + UART_MOD_CNTRL);
+ wmb();
+ } else if (state == 3) { /* power off */
+ __raw_writel(0, port->membase + UART_MOD_CNTRL);
+ wmb();
+ }
+}
+
#define PORT(_base, _irq) \
{ \
.mapbase = _base, \
@@ -30,6 +46,7 @@
.flags = UPF_SKIP_TEST | UPF_IOREMAP | \
UPF_FIXED_TYPE, \
.type = PORT_16550A, \
+ .pm = alchemy_8250_pm, \
}
static struct plat_serial8250_port au1x00_uart_data[] = {
diff --git a/arch/mips/alchemy/common/power.c b/arch/mips/alchemy/common/power.c
index 6ab7b42..8fbf6d0 100644
--- a/arch/mips/alchemy/common/power.c
+++ b/arch/mips/alchemy/common/power.c
@@ -52,11 +52,6 @@
* We only have to save/restore registers that aren't otherwise
* done as part of a driver pm_* function.
*/
-static unsigned int sleep_uart0_inten;
-static unsigned int sleep_uart0_fifoctl;
-static unsigned int sleep_uart0_linectl;
-static unsigned int sleep_uart0_clkdiv;
-static unsigned int sleep_uart0_enable;
static unsigned int sleep_usb[2];
...Hello. A *switch* statement seems more fitting here... WBR, Sergei --
Well, those are the only 2 values defined anyway, but I'll change it.
Thanks!
Manuel Lauss
--
Ping? Noone interested? On Wed, Mar 24, 2010 at 7:16 PM, Manuel Lauss --
