Skip to content

Commit e894b60

Browse files
andy-shevgregkh
authored andcommitted
serial: port: Introduce a common helper to read properties
Several serial drivers want to read the same or similar set of the port properties. Make a common helper for them. Signed-off-by: Andy Shevchenko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 79d713b commit e894b60

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

drivers/tty/serial/serial_port.c

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88

99
#include <linux/device.h>
1010
#include <linux/module.h>
11+
#include <linux/of.h>
12+
#include <linux/platform_device.h>
1113
#include <linux/pm_runtime.h>
14+
#include <linux/property.h>
1215
#include <linux/serial_core.h>
1316
#include <linux/spinlock.h>
1417

@@ -82,6 +85,148 @@ void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
8285
}
8386
EXPORT_SYMBOL(uart_remove_one_port);
8487

88+
/**
89+
* __uart_read_properties - read firmware properties of the given UART port
90+
* @port: corresponding port
91+
* @use_defaults: apply defaults (when %true) or validate the values (when %false)
92+
*
93+
* The following device properties are supported:
94+
* - clock-frequency (optional)
95+
* - fifo-size (optional)
96+
* - no-loopback-test (optional)
97+
* - reg-shift (defaults may apply)
98+
* - reg-offset (value may be validated)
99+
* - reg-io-width (defaults may apply or value may be validated)
100+
* - interrupts (OF only)
101+
* - serial [alias ID] (OF only)
102+
*
103+
* If the port->dev is of struct platform_device type the interrupt line
104+
* will be retrieved via platform_get_irq() call against that device.
105+
* Otherwise it will be assigned by fwnode_irq_get() call. In both cases
106+
* the index 0 of the resource is used.
107+
*
108+
* The caller is responsible to initialize the following fields of the @port
109+
* ->dev (must be valid)
110+
* ->flags
111+
* ->mapbase
112+
* ->mapsize
113+
* ->regshift (if @use_defaults is false)
114+
* before calling this function. Alternatively the above mentioned fields
115+
* may be zeroed, in such case the only ones, that have associated properties
116+
* found, will be set to the respective values.
117+
*
118+
* If no error happened, the ->irq, ->mapbase, ->mapsize will be altered.
119+
* The ->iotype is always altered.
120+
*
121+
* When @use_defaults is true and the respective property is not found
122+
* the following values will be applied:
123+
* ->regshift = 0
124+
* In this case IRQ must be provided, otherwise an error will be returned.
125+
*
126+
* When @use_defaults is false and the respective property is found
127+
* the following values will be validated:
128+
* - reg-io-width (->iotype)
129+
* - reg-offset (->mapsize against ->mapbase)
130+
*
131+
* Returns: 0 on success or negative errno on failure
132+
*/
133+
static int __uart_read_properties(struct uart_port *port, bool use_defaults)
134+
{
135+
struct device *dev = port->dev;
136+
u32 value;
137+
int ret;
138+
139+
/* Read optional UART functional clock frequency */
140+
device_property_read_u32(dev, "clock-frequency", &port->uartclk);
141+
142+
/* Read the registers alignment (default: 8-bit) */
143+
ret = device_property_read_u32(dev, "reg-shift", &value);
144+
if (ret)
145+
port->regshift = use_defaults ? 0 : port->regshift;
146+
else
147+
port->regshift = value;
148+
149+
/* Read the registers I/O access type (default: MMIO 8-bit) */
150+
ret = device_property_read_u32(dev, "reg-io-width", &value);
151+
if (ret) {
152+
port->iotype = UPIO_MEM;
153+
} else {
154+
switch (value) {
155+
case 1:
156+
port->iotype = UPIO_MEM;
157+
break;
158+
case 2:
159+
port->iotype = UPIO_MEM16;
160+
break;
161+
case 4:
162+
port->iotype = device_is_big_endian(dev) ? UPIO_MEM32BE : UPIO_MEM32;
163+
break;
164+
default:
165+
if (!use_defaults) {
166+
dev_err(dev, "Unsupported reg-io-width (%u)\n", value);
167+
return -EINVAL;
168+
}
169+
port->iotype = UPIO_UNKNOWN;
170+
break;
171+
}
172+
}
173+
174+
/* Read the address mapping base offset (default: no offset) */
175+
ret = device_property_read_u32(dev, "reg-offset", &value);
176+
if (ret)
177+
value = 0;
178+
179+
/* Check for shifted address mapping overflow */
180+
if (!use_defaults && port->mapsize < value) {
181+
dev_err(dev, "reg-offset %u exceeds region size %pa\n", value, &port->mapsize);
182+
return -EINVAL;
183+
}
184+
185+
port->mapbase += value;
186+
port->mapsize -= value;
187+
188+
/* Read optional FIFO size */
189+
device_property_read_u32(dev, "fifo-size", &port->fifosize);
190+
191+
if (device_property_read_bool(dev, "no-loopback-test"))
192+
port->flags |= UPF_SKIP_TEST;
193+
194+
/* Get index of serial line, if found in DT aliases */
195+
ret = of_alias_get_id(dev_of_node(dev), "serial");
196+
if (ret >= 0)
197+
port->line = ret;
198+
199+
if (dev_is_platform(dev))
200+
ret = platform_get_irq(to_platform_device(dev), 0);
201+
else
202+
ret = fwnode_irq_get(dev_fwnode(dev), 0);
203+
if (ret == -EPROBE_DEFER)
204+
return ret;
205+
if (ret > 0)
206+
port->irq = ret;
207+
else if (use_defaults)
208+
/* By default IRQ support is mandatory */
209+
return ret;
210+
else
211+
port->irq = 0;
212+
213+
port->flags |= UPF_SHARE_IRQ;
214+
215+
return 0;
216+
}
217+
218+
int uart_read_port_properties(struct uart_port *port)
219+
{
220+
return __uart_read_properties(port, true);
221+
}
222+
EXPORT_SYMBOL_GPL(uart_read_port_properties);
223+
224+
int uart_read_and_validate_port_properties(struct uart_port *port)
225+
{
226+
return __uart_read_properties(port, false);
227+
}
228+
EXPORT_SYMBOL_GPL(uart_read_and_validate_port_properties);
229+
85230
static struct device_driver serial_port_driver = {
86231
.name = "port",
87232
.suppress_bind_attrs = true,

include/linux/serial_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,8 @@ int uart_register_driver(struct uart_driver *uart);
963963
void uart_unregister_driver(struct uart_driver *uart);
964964
int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
965965
void uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
966+
int uart_read_port_properties(struct uart_port *port);
967+
int uart_read_and_validate_port_properties(struct uart_port *port);
966968
bool uart_match_port(const struct uart_port *port1,
967969
const struct uart_port *port2);
968970

0 commit comments

Comments
 (0)