diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -995,6 +995,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 		uart->port.regshift     = up->port.regshift;
 		uart->port.iotype       = up->port.iotype;
 		uart->port.flags        = up->port.flags | UPF_BOOT_AUTOCONF;
+		uart->port.supports_autosuspend = up->port.supports_autosuspend;
 		uart->bugs		= up->bugs;
 		uart->port.mapbase      = up->port.mapbase;
 		uart->port.mapsize      = up->port.mapsize;
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1338,6 +1338,7 @@ static int omap8250_probe(struct platform_device *pdev)
 	up.rs485_start_tx = serial8250_em485_start_tx;
 	up.rs485_stop_tx = serial8250_em485_stop_tx;
 	up.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
+	up.port.supports_autosuspend = 1;
 
 	ret = of_alias_get_id(np, "serial");
 	if (ret < 0) {
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -16,7 +16,9 @@
 #include <linux/console.h>
 #include <linux/gpio/consumer.h>
 #include <linux/of.h>
+#include <linux/pm_runtime.h>
 #include <linux/proc_fs.h>
+#include <linux/radix-tree.h>
 #include <linux/seq_file.h>
 #include <linux/device.h>
 #include <linux/serial.h> /* for serial_state and serial_icounter_struct */
@@ -30,6 +32,25 @@
 #include <linux/irq.h>
 #include <linux/uaccess.h>
 
+/*
+ * Serial port device specific data for serial core.
+ *
+ * Each port device can have multiple ports with struct uart_state allocated
+ * for each port. The array of ports is kept in struct uart_driver.
+ */
+struct serial_controller {
+	struct device *dev;			/* Serial port device */
+	struct uart_driver *drv;		/* For port specific uart_state */
+	struct kref ref;			/* Enable count for runtime PM */
+	unsigned long implements_pm_runtime:1;
+	unsigned long supports_autosuspend:1;
+};
+
+/*
+ * Serial core port device instances. Update protected by port_mutex.
+ */
+static RADIX_TREE(serial_core_devices, GFP_NOWAIT);
+
 /*
  * This is used to lock changes in serial line configuration.
  */
@@ -175,6 +196,125 @@ static void uart_port_dtr_rts(struct uart_port *uport, int raise)
 		uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
 }
 
+/* Called from uart_add_one_port() with port_mutex held */
+static int serial_core_pm_runtime_start(struct uart_port *port)
+{
+	struct uart_state *state = port->state;
+	struct serial_controller *controller = state->controller;
+	struct device *dev = port->dev;
+	int ret = 0;
+
+	if (kref_get_unless_zero(&controller->ref))
+		return 0;
+
+	/* Init controller device on first reference */
+	kref_init(&controller->ref);
+
+	/* Always enable autosuspend and consider child devices for serdev */
+	pm_runtime_use_autosuspend(dev);
+	pm_suspend_ignore_children(dev, false);
+
+	/*
+	 * If the port driver did not enable runtime PM in probe, do it now.
+	 * Devices that did not enable runtime PM get set active so we can
+	 * properly handle the returned errors for runtime PM calls.
+	 */
+	if (!pm_runtime_enabled(dev)) {
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+	} else {
+		controller->implements_pm_runtime = 1;
+	}
+
+	/*
+	 * Keep the port device enabled unless autosuspend is supported.
+	 * Released on port shutdown.
+	 */
+	if (!controller->supports_autosuspend) {
+		ret = pm_runtime_resume_and_get(dev);
+		if (ret < 0) {
+			pm_runtime_dont_use_autosuspend(dev);
+			pm_runtime_disable(dev);
+		}
+	}
+
+	return ret;
+}
+
+/* Clean up the runtime PM settings done on serial_core_register_port() */
+static void serial_core_pm_runtime_cleanup(struct kref *ref)
+{
+	struct serial_controller *controller =
+		 container_of(ref, struct serial_controller, ref);
+	struct device *dev = controller->dev;
+
+	pm_runtime_dont_use_autosuspend(dev);
+	pm_suspend_ignore_children(dev, true);
+	if (!controller->supports_autosuspend)
+		pm_runtime_put_sync(dev);
+	if (!controller->implements_pm_runtime) {
+		pm_runtime_set_suspended(dev);
+		pm_runtime_disable(dev);
+	}
+}
+
+/* Called from uart_remove_one_port() and on error path with port_mutex held */
+static void serial_core_unregister_port(struct uart_port *port)
+{
+	unsigned long idx = (unsigned long)port->dev;
+	struct serial_controller *controller;
+
+	/* Check for a registered controller, no struct device early on */
+	controller = radix_tree_lookup(&serial_core_devices, idx);
+	if (!controller)
+		return;
+
+	kref_put(&controller->ref, serial_core_pm_runtime_cleanup);
+
+	controller = radix_tree_delete(&serial_core_devices, idx);
+	kfree(controller);
+}
+
+/* Called from uart_add_one_port() with port_mutex held */
+static int serial_core_register_port(struct uart_port *port,
+				     struct uart_driver *drv)
+{
+	unsigned long idx = (unsigned long)port->dev;
+	struct serial_controller *controller;
+	int ret;
+
+	if (!idx)
+		return 0;
+
+	controller = radix_tree_lookup(&serial_core_devices, idx);
+	if (controller) {
+		port->state->controller = controller;
+		WARN_ON(port->supports_autosuspend != controller->supports_autosuspend);
+		return serial_core_pm_runtime_start(port);
+	}
+
+	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+	if (!controller)
+		return -ENOMEM;
+
+	controller->drv = drv;
+	controller->dev = port->dev;
+	controller->supports_autosuspend = port->supports_autosuspend;
+	port->state->controller = controller;
+
+	ret = radix_tree_insert(&serial_core_devices, idx, controller);
+	if (ret) {
+		kfree(controller);
+		return ret;
+	}
+
+	ret = serial_core_pm_runtime_start(port);
+	if (ret < 0)
+		serial_core_unregister_port(port);
+
+	return ret;
+}
+
 /*
  * Startup the port.  This will be called once per open.  All calls
  * will be serialised by the per-port mutex.
@@ -2956,6 +3096,10 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
 		goto out;
 	}
 
+	ret = serial_core_register_port(uport, drv);
+	if (ret)
+		goto out;
+
 	/*
 	 * If this port is in use as a console then the spinlock is already
 	 * initialised.
@@ -2979,6 +3123,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
 				    GFP_KERNEL);
 	if (!uport->tty_groups) {
 		ret = -ENOMEM;
+		serial_core_unregister_port(uport);
 		goto out;
 	}
 	uport->tty_groups[0] = &tty_dev_attr_group;
@@ -3048,6 +3193,9 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
 		goto out;
 	}
 	uport->flags |= UPF_DEAD;
+
+	serial_core_unregister_port(uport);
+
 	mutex_unlock(&port->mutex);
 
 	/*
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -250,6 +250,7 @@ struct uart_port {
 	unsigned char		hub6;			/* this should be in the 8250 driver */
 	unsigned char		suspended;
 	unsigned char		console_reinit;
+	unsigned char		supports_autosuspend;
 	const char		*name;			/* port name */
 	struct attribute_group	*attr_group;		/* port specific attributes */
 	const struct attribute_group **tty_groups;	/* all attributes (serial core use only) */
@@ -285,6 +286,8 @@ enum uart_pm_state {
  * This is the state information which is persistent across opens.
  */
 struct uart_state {
+	struct serial_controller *controller;
+
 	struct tty_port		port;
 
 	enum uart_pm_state	pm_state;
