Skip to content

Commit 05c6869

Browse files
marcanjannau
authored andcommitted
tty: serial: samsung_tty: Support runtime PM
This allows idle UART devices to be suspended using the standard runtime-PM framework. The logic is modeled after stm32-usart. Signed-off-by: Hector Martin <marcan@marcan.st>
1 parent a38297e commit 05c6869

1 file changed

Lines changed: 56 additions & 33 deletions

File tree

drivers/tty/serial/samsung_tty.c

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <linux/module.h>
3535
#include <linux/of.h>
3636
#include <linux/platform_device.h>
37+
#include <linux/pm_runtime.h>
3738
#include <linux/serial.h>
3839
#include <linux/serial_core.h>
3940
#include <linux/serial_s3c.h>
@@ -1294,30 +1295,49 @@ static int apple_s5l_serial_startup(struct uart_port *port)
12941295
return ret;
12951296
}
12961297

1298+
static int __maybe_unused s3c24xx_serial_runtime_suspend(struct device *dev)
1299+
{
1300+
struct uart_port *port = dev_get_drvdata(dev);
1301+
struct s3c24xx_uart_port *ourport = to_ourport(port);
1302+
int timeout = 10000;
1303+
1304+
while (--timeout && !s3c24xx_serial_txempty_nofifo(port))
1305+
udelay(100);
1306+
1307+
if (!IS_ERR(ourport->baudclk))
1308+
clk_disable_unprepare(ourport->baudclk);
1309+
1310+
clk_disable_unprepare(ourport->clk);
1311+
return 0;
1312+
};
1313+
1314+
static int __maybe_unused s3c24xx_serial_runtime_resume(struct device *dev)
1315+
{
1316+
struct uart_port *port = dev_get_drvdata(dev);
1317+
struct s3c24xx_uart_port *ourport = to_ourport(port);
1318+
1319+
clk_prepare_enable(ourport->clk);
1320+
1321+
if (!IS_ERR(ourport->baudclk))
1322+
clk_prepare_enable(ourport->baudclk);
1323+
return 0;
1324+
};
1325+
12971326
static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
12981327
unsigned int old)
12991328
{
13001329
struct s3c24xx_uart_port *ourport = to_ourport(port);
1301-
int timeout = 10000;
13021330

13031331
ourport->pm_level = level;
13041332

13051333
switch (level) {
1306-
case 3:
1307-
while (--timeout && !s3c24xx_serial_txempty_nofifo(port))
1308-
udelay(100);
1309-
1310-
if (!IS_ERR(ourport->baudclk))
1311-
clk_disable_unprepare(ourport->baudclk);
1312-
1313-
clk_disable_unprepare(ourport->clk);
1334+
case UART_PM_STATE_OFF:
1335+
pm_runtime_mark_last_busy(port->dev);
1336+
pm_runtime_put_sync(port->dev);
13141337
break;
13151338

1316-
case 0:
1317-
clk_prepare_enable(ourport->clk);
1318-
1319-
if (!IS_ERR(ourport->baudclk))
1320-
clk_prepare_enable(ourport->baudclk);
1339+
case UART_PM_STATE_ON:
1340+
pm_runtime_get_sync(port->dev);
13211341
break;
13221342
default:
13231343
dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level);
@@ -2040,18 +2060,15 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
20402060
}
20412061
}
20422062

2063+
pm_runtime_get_noresume(&pdev->dev);
2064+
pm_runtime_set_active(&pdev->dev);
2065+
pm_runtime_enable(&pdev->dev);
2066+
20432067
dev_dbg(&pdev->dev, "%s: adding port\n", __func__);
20442068
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
20452069
platform_set_drvdata(pdev, &ourport->port);
20462070

2047-
/*
2048-
* Deactivate the clock enabled in s3c24xx_serial_init_port here,
2049-
* so that a potential re-enablement through the pm-callback overlaps
2050-
* and keeps the clock enabled in this case.
2051-
*/
2052-
clk_disable_unprepare(ourport->clk);
2053-
if (!IS_ERR(ourport->baudclk))
2054-
clk_disable_unprepare(ourport->baudclk);
2071+
pm_runtime_put_sync(&pdev->dev);
20552072

20562073
probe_index++;
20572074

@@ -2061,16 +2078,26 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
20612078
static void s3c24xx_serial_remove(struct platform_device *dev)
20622079
{
20632080
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
2081+
struct s3c24xx_uart_port *ourport = to_ourport(port);
20642082

20652083
if (port)
2084+
pm_runtime_get_sync(&dev->dev);
20662085
uart_remove_one_port(&s3c24xx_uart_drv, port);
20672086

2087+
clk_disable_unprepare(ourport->clk);
2088+
if (!IS_ERR(ourport->baudclk))
2089+
clk_disable_unprepare(ourport->baudclk);
2090+
2091+
pm_runtime_disable(&dev->dev);
2092+
pm_runtime_set_suspended(&dev->dev);
2093+
pm_runtime_put_noidle(&dev->dev);
2094+
20682095
uart_unregister_driver(&s3c24xx_uart_drv);
20692096
}
20702097

20712098
/* UART power management code */
2072-
#ifdef CONFIG_PM_SLEEP
2073-
static int s3c24xx_serial_suspend(struct device *dev)
2099+
2100+
static int __maybe_unused s3c24xx_serial_suspend(struct device *dev)
20742101
{
20752102
struct uart_port *port = s3c24xx_dev_to_port(dev);
20762103

@@ -2080,7 +2107,7 @@ static int s3c24xx_serial_suspend(struct device *dev)
20802107
return 0;
20812108
}
20822109

2083-
static int s3c24xx_serial_resume(struct device *dev)
2110+
static int __maybe_unused s3c24xx_serial_resume(struct device *dev)
20842111
{
20852112
struct uart_port *port = s3c24xx_dev_to_port(dev);
20862113
struct s3c24xx_uart_port *ourport = to_ourport(port);
@@ -2100,7 +2127,7 @@ static int s3c24xx_serial_resume(struct device *dev)
21002127
return 0;
21012128
}
21022129

2103-
static int s3c24xx_serial_resume_noirq(struct device *dev)
2130+
static int __maybe_unused s3c24xx_serial_resume_noirq(struct device *dev)
21042131
{
21052132
struct uart_port *port = s3c24xx_dev_to_port(dev);
21062133
struct s3c24xx_uart_port *ourport = to_ourport(port);
@@ -2172,13 +2199,9 @@ static int s3c24xx_serial_resume_noirq(struct device *dev)
21722199
static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
21732200
SET_SYSTEM_SLEEP_PM_OPS(s3c24xx_serial_suspend, s3c24xx_serial_resume)
21742201
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, s3c24xx_serial_resume_noirq)
2202+
SET_RUNTIME_PM_OPS(s3c24xx_serial_runtime_suspend,
2203+
s3c24xx_serial_runtime_resume, NULL)
21752204
};
2176-
#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops)
2177-
2178-
#else /* !CONFIG_PM_SLEEP */
2179-
2180-
#define SERIAL_SAMSUNG_PM_OPS NULL
2181-
#endif /* CONFIG_PM_SLEEP */
21822205

21832206
/* Console code */
21842207

@@ -2653,7 +2676,7 @@ static struct platform_driver samsung_serial_driver = {
26532676
.id_table = s3c24xx_serial_driver_ids,
26542677
.driver = {
26552678
.name = "samsung-uart",
2656-
.pm = SERIAL_SAMSUNG_PM_OPS,
2679+
.pm = &s3c24xx_serial_pm_ops,
26572680
.of_match_table = of_match_ptr(s3c24xx_uart_dt_match),
26582681
},
26592682
};

0 commit comments

Comments
 (0)