Commit | Line | Data |
---|---|---|
84a9582f TL |
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 */ | |
559c7ff4 | 38 | uart_port_lock_irqsave(port, &flags); |
84a9582f TL |
39 | if (__serial_port_busy(port)) |
40 | port->ops->start_tx(port); | |
559c7ff4 | 41 | uart_port_unlock_irqrestore(port, flags); |
84a9582f TL |
42 | |
43 | out: | |
44 | pm_runtime_mark_last_busy(dev); | |
45 | ||
46 | return 0; | |
47 | } | |
48 | ||
43066e32 YY |
49 | static int serial_port_runtime_suspend(struct device *dev) |
50 | { | |
51 | struct serial_port_device *port_dev = to_serial_base_port_device(dev); | |
52 | struct uart_port *port = port_dev->port; | |
53 | unsigned long flags; | |
54 | bool busy; | |
55 | ||
56 | if (port->flags & UPF_DEAD) | |
57 | return 0; | |
58 | ||
59 | uart_port_lock_irqsave(port, &flags); | |
60 | busy = __serial_port_busy(port); | |
61 | if (busy) | |
62 | port->ops->start_tx(port); | |
63 | uart_port_unlock_irqrestore(port, flags); | |
64 | ||
65 | if (busy) | |
66 | pm_runtime_mark_last_busy(dev); | |
67 | ||
68 | return busy ? -EBUSY : 0; | |
69 | } | |
70 | ||
84a9582f | 71 | static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm, |
43066e32 YY |
72 | serial_port_runtime_suspend, |
73 | serial_port_runtime_resume, NULL); | |
84a9582f TL |
74 | |
75 | static int serial_port_probe(struct device *dev) | |
76 | { | |
77 | pm_runtime_enable(dev); | |
78 | pm_runtime_set_autosuspend_delay(dev, SERIAL_PORT_AUTOSUSPEND_DELAY_MS); | |
79 | pm_runtime_use_autosuspend(dev); | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static int serial_port_remove(struct device *dev) | |
85 | { | |
86 | pm_runtime_dont_use_autosuspend(dev); | |
87 | pm_runtime_disable(dev); | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
92 | /* | |
93 | * Serial core port device init functions. Note that the physical serial | |
94 | * port device driver may not have completed probe at this point. | |
95 | */ | |
96 | int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) | |
97 | { | |
98 | return serial_ctrl_register_port(drv, port); | |
99 | } | |
100 | EXPORT_SYMBOL(uart_add_one_port); | |
101 | ||
102 | void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) | |
103 | { | |
104 | serial_ctrl_unregister_port(drv, port); | |
105 | } | |
106 | EXPORT_SYMBOL(uart_remove_one_port); | |
107 | ||
108 | static struct device_driver serial_port_driver = { | |
109 | .name = "port", | |
110 | .suppress_bind_attrs = true, | |
111 | .probe = serial_port_probe, | |
112 | .remove = serial_port_remove, | |
113 | .pm = pm_ptr(&serial_port_pm), | |
114 | }; | |
115 | ||
116 | int serial_base_port_init(void) | |
117 | { | |
118 | return serial_base_driver_register(&serial_port_driver); | |
119 | } | |
120 | ||
121 | void serial_base_port_exit(void) | |
122 | { | |
123 | serial_base_driver_unregister(&serial_port_driver); | |
124 | } | |
125 | ||
126 | MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); | |
127 | MODULE_DESCRIPTION("Serial controller port driver"); | |
128 | MODULE_LICENSE("GPL"); |