xref: /openbmc/linux/drivers/tty/serial/serial_port.c (revision da097dcc)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Serial core port device driver
4  *
5  * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
6  * Author: Tony Lindgren <tony@atomide.com>
7  */
8 
9 #include <linux/device.h>
10 #include <linux/module.h>
11 #include <linux/pm_runtime.h>
12 #include <linux/serial_core.h>
13 #include <linux/spinlock.h>
14 
15 #include "serial_base.h"
16 
17 #define SERIAL_PORT_AUTOSUSPEND_DELAY_MS	500
18 
19 /* Only considers pending TX for now. Caller must take care of locking */
20 static int __serial_port_busy(struct uart_port *port)
21 {
22 	return !uart_tx_stopped(port) &&
23 		uart_circ_chars_pending(&port->state->xmit);
24 }
25 
26 static int serial_port_runtime_resume(struct device *dev)
27 {
28 	struct serial_port_device *port_dev = to_serial_base_port_device(dev);
29 	struct uart_port *port;
30 	unsigned long flags;
31 
32 	port = port_dev->port;
33 
34 	if (port->flags & UPF_DEAD)
35 		goto out;
36 
37 	/* Flush any pending TX for the port */
38 	spin_lock_irqsave(&port->lock, flags);
39 	if (!port_dev->tx_enabled)
40 		goto unlock;
41 	if (__serial_port_busy(port))
42 		port->ops->start_tx(port);
43 
44 unlock:
45 	spin_unlock_irqrestore(&port->lock, flags);
46 
47 out:
48 	pm_runtime_mark_last_busy(dev);
49 
50 	return 0;
51 }
52 
53 static int serial_port_runtime_suspend(struct device *dev)
54 {
55 	struct serial_port_device *port_dev = to_serial_base_port_device(dev);
56 	struct uart_port *port = port_dev->port;
57 	unsigned long flags;
58 	bool busy;
59 
60 	if (port->flags & UPF_DEAD)
61 		return 0;
62 
63 	/*
64 	 * Nothing to do on pm_runtime_force_suspend(), see
65 	 * DEFINE_RUNTIME_DEV_PM_OPS.
66 	 */
67 	if (!pm_runtime_enabled(dev))
68 		return 0;
69 
70 	uart_port_lock_irqsave(port, &flags);
71 	if (!port_dev->tx_enabled) {
72 		uart_port_unlock_irqrestore(port, flags);
73 		return 0;
74 	}
75 
76 	busy = __serial_port_busy(port);
77 	if (busy)
78 		port->ops->start_tx(port);
79 	uart_port_unlock_irqrestore(port, flags);
80 
81 	if (busy)
82 		pm_runtime_mark_last_busy(dev);
83 
84 	return busy ? -EBUSY : 0;
85 }
86 
87 static void serial_base_port_set_tx(struct uart_port *port,
88 				    struct serial_port_device *port_dev,
89 				    bool enabled)
90 {
91 	unsigned long flags;
92 
93 	uart_port_lock_irqsave(port, &flags);
94 	port_dev->tx_enabled = enabled;
95 	uart_port_unlock_irqrestore(port, flags);
96 }
97 
98 void serial_base_port_startup(struct uart_port *port)
99 {
100 	struct serial_port_device *port_dev = port->port_dev;
101 
102 	serial_base_port_set_tx(port, port_dev, true);
103 }
104 
105 void serial_base_port_shutdown(struct uart_port *port)
106 {
107 	struct serial_port_device *port_dev = port->port_dev;
108 
109 	serial_base_port_set_tx(port, port_dev, false);
110 }
111 
112 static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm,
113 				 serial_port_runtime_suspend,
114 				 serial_port_runtime_resume, NULL);
115 
116 static int serial_port_probe(struct device *dev)
117 {
118 	pm_runtime_enable(dev);
119 	pm_runtime_set_autosuspend_delay(dev, SERIAL_PORT_AUTOSUSPEND_DELAY_MS);
120 	pm_runtime_use_autosuspend(dev);
121 
122 	return 0;
123 }
124 
125 static int serial_port_remove(struct device *dev)
126 {
127 	pm_runtime_dont_use_autosuspend(dev);
128 	pm_runtime_disable(dev);
129 
130 	return 0;
131 }
132 
133 /*
134  * Serial core port device init functions. Note that the physical serial
135  * port device driver may not have completed probe at this point.
136  */
137 int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
138 {
139 	return serial_ctrl_register_port(drv, port);
140 }
141 EXPORT_SYMBOL(uart_add_one_port);
142 
143 void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
144 {
145 	serial_ctrl_unregister_port(drv, port);
146 }
147 EXPORT_SYMBOL(uart_remove_one_port);
148 
149 static struct device_driver serial_port_driver = {
150 	.name = "port",
151 	.suppress_bind_attrs = true,
152 	.probe = serial_port_probe,
153 	.remove = serial_port_remove,
154 	.pm = pm_ptr(&serial_port_pm),
155 };
156 
157 int serial_base_port_init(void)
158 {
159 	return serial_base_driver_register(&serial_port_driver);
160 }
161 
162 void serial_base_port_exit(void)
163 {
164 	serial_base_driver_unregister(&serial_port_driver);
165 }
166 
167 MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
168 MODULE_DESCRIPTION("Serial controller port driver");
169 MODULE_LICENSE("GPL");
170