|
8 | 8 |
|
9 | 9 | #include <linux/device.h>
|
10 | 10 | #include <linux/module.h>
|
| 11 | +#include <linux/of.h> |
| 12 | +#include <linux/platform_device.h> |
11 | 13 | #include <linux/pm_runtime.h>
|
| 14 | +#include <linux/property.h> |
12 | 15 | #include <linux/serial_core.h>
|
13 | 16 | #include <linux/spinlock.h>
|
14 | 17 |
|
@@ -82,6 +85,148 @@ void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
|
82 | 85 | }
|
83 | 86 | EXPORT_SYMBOL(uart_remove_one_port);
|
84 | 87 |
|
| 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 | + |
85 | 230 | static struct device_driver serial_port_driver = {
|
86 | 231 | .name = "port",
|
87 | 232 | .suppress_bind_attrs = true,
|
|
0 commit comments