John Wang | 493f858 | 2020-09-30 13:08:32 +0800 | [diff] [blame] | 1 | From dc37d698586a7ca4e4381ef7726249fa70d14c89 Mon Sep 17 00:00:00 2001 |
| 2 | From: Oskar Senft <osk@google.com> |
| 3 | Date: Wed, 8 Aug 2018 10:15:05 -0400 |
| 4 | Subject: [PATCH 3/5] misc: aspeed: Add Aspeed UART routing control driver. |
| 5 | |
| 6 | This driver adds sysfs files that allow the BMC userspace to configure |
| 7 | how UARTs and physical serial I/O ports are routed. |
| 8 | |
| 9 | Tested: Checked correct behavior (both read & write) on TYAN S7106 |
| 10 | board by manually changing routing settings and confirming that bits |
| 11 | flow as expected. Tested for UART1 and UART3 as this board doesn't have |
| 12 | the other UARTs wired up in a testable way. |
| 13 | |
| 14 | Signed-off-by: Oskar Senft <osk@google.com> |
| 15 | Signed-off-by: Yong Li <yong.b.li@linux.intel.com> |
| 16 | Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com> |
| 17 | Signed-off-by: John Wang <wangzhiqiang.bj@bytedance.com> |
| 18 | --- |
| 19 | .../stable/sysfs-driver-aspeed-uart-routing | 14 + |
| 20 | .../misc-devices/aspeed-uart-routing.txt | 49 +++ |
| 21 | drivers/misc/Kconfig | 6 + |
| 22 | drivers/misc/Makefile | 1 + |
| 23 | drivers/misc/aspeed-uart-routing.c | 383 ++++++++++++++++++ |
| 24 | 5 files changed, 453 insertions(+) |
| 25 | create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing |
| 26 | create mode 100644 Documentation/misc-devices/aspeed-uart-routing.txt |
| 27 | create mode 100644 drivers/misc/aspeed-uart-routing.c |
| 28 | |
| 29 | diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing |
| 30 | new file mode 100644 |
| 31 | index 000000000000..5068737d9c12 |
| 32 | --- /dev/null |
| 33 | +++ b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing |
| 34 | @@ -0,0 +1,14 @@ |
| 35 | +What: /sys/bus/platform/drivers/aspeed-uart-routing/*/io* |
| 36 | +Date: August 2018 |
| 37 | +Contact: Oskar Senft <osk@google.com> |
| 38 | +Description: Configures the input source for the specific physical |
| 39 | + serial I/O port. |
| 40 | +Users: OpenBMC. Proposed changes should be mailed to |
| 41 | + openbmc@lists.ozlabs.org |
| 42 | + |
| 43 | +What: /sys/bus/platform/drivers/aspeed-uart-routing/*/uart* |
| 44 | +Date: August 2018 |
| 45 | +Contact: Oskar Senft <osk@google.com> |
| 46 | +Description: Configures the input source for the specific UART. |
| 47 | +Users: OpenBMC. Proposed changes should be mailed to |
| 48 | + openbmc@lists.ozlabs.org |
| 49 | diff --git a/Documentation/misc-devices/aspeed-uart-routing.txt b/Documentation/misc-devices/aspeed-uart-routing.txt |
| 50 | new file mode 100644 |
| 51 | index 000000000000..cf1c2a466875 |
| 52 | --- /dev/null |
| 53 | +++ b/Documentation/misc-devices/aspeed-uart-routing.txt |
| 54 | @@ -0,0 +1,49 @@ |
| 55 | +Kernel driver aspeed-uart-routing |
| 56 | +================================= |
| 57 | + |
| 58 | +Supported chips: |
| 59 | +ASPEED AST2500/AST2600 |
| 60 | + |
| 61 | +Author: |
| 62 | +Google LLC |
| 63 | + |
| 64 | +Description |
| 65 | +----------- |
| 66 | + |
| 67 | +The Aspeed AST2500/AST2600 allows to dynamically route the inputs for the |
| 68 | +built-in UARTS and physical serial I/O ports. |
| 69 | + |
| 70 | +This allows, for example, to connect the output of UART to another UART. |
| 71 | +This can be used to enable host<->BMC communication via UARTs, e.g. to allow |
| 72 | +access to the host's serial console. |
| 73 | + |
| 74 | +This driver is for the BMC side. The sysfs files allow the BMC userspace |
| 75 | +which owns the system configuration policy, to configure how UARTs and |
| 76 | +physical serial I/O ports are routed. |
| 77 | + |
| 78 | +The driver provides the following files in sysfs: |
| 79 | +uart1 Configure the input signal to UART1. |
| 80 | +uart2 Configure the input signal to UART2. |
| 81 | +uart3 Configure the input signal to UART3. |
| 82 | +uart4 Configure the input signal to UART4. |
| 83 | +uart5 Configure the input signal to UART5. |
| 84 | +io1 Configure the input signal to physical serial port 1. |
| 85 | +io2 Configure the input signal to physical serial port 2. |
| 86 | +io3 Configure the input signal to physical serial port 3. |
| 87 | +io4 Configure the input signal to physical serial port 4. |
| 88 | +io5 Configure the input signal to physical serial port 5. |
| 89 | + |
| 90 | +When read, each file shows the list of available options with the currently |
| 91 | +selected option marked by square brackets "[]". The list of available options |
| 92 | +depends on the selected file. |
| 93 | + |
| 94 | +Example: |
| 95 | +$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 |
| 96 | +[io1] io2 io3 io4 uart2 uart3 uart4 io6 |
| 97 | + |
| 98 | +In this case, UART1 gets its input signal from IO1 (physical serial port 1). |
| 99 | + |
| 100 | +$ echo -n "uart3" \ |
| 101 | + >/sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 |
| 102 | +$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 |
| 103 | +io1 io2 io3 io4 uart2 [uart3] uart4 io6 |
| 104 | diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig |
| 105 | index d8626a0d3e31..48a519c59bdf 100644 |
| 106 | --- a/drivers/misc/Kconfig |
| 107 | +++ b/drivers/misc/Kconfig |
| 108 | @@ -451,6 +451,12 @@ config MCTP_LPC |
| 109 | Implements the MCTP LPC binding via KCS LPC IO cycles for control and |
| 110 | LPC FWH cycles for data |
| 111 | |
| 112 | +config ASPEED_UART_ROUTING |
| 113 | + tristate "Aspeed ast2500 UART routing control" |
| 114 | + help |
| 115 | + If you want to configure UART routing on Aspeed BMC platforms, enable |
| 116 | + this option. |
| 117 | + |
| 118 | config MISC_RTSX |
| 119 | tristate |
| 120 | default MISC_RTSX_PCI || MISC_RTSX_USB |
| 121 | diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile |
| 122 | index 183970192ced..4e67e21c2e65 100644 |
| 123 | --- a/drivers/misc/Makefile |
| 124 | +++ b/drivers/misc/Makefile |
| 125 | @@ -60,3 +60,4 @@ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o |
| 126 | obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o |
| 127 | obj-$(CONFIG_NPCM7XX_PCI_MBOX) += npcm7xx-pci-mbox.o |
| 128 | obj-$(CONFIG_MCTP_LPC) += mctp-lpc.o |
| 129 | +obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o |
| 130 | diff --git a/drivers/misc/aspeed-uart-routing.c b/drivers/misc/aspeed-uart-routing.c |
| 131 | new file mode 100644 |
| 132 | index 000000000000..21ef5d98c317 |
| 133 | --- /dev/null |
| 134 | +++ b/drivers/misc/aspeed-uart-routing.c |
| 135 | @@ -0,0 +1,383 @@ |
| 136 | +/* |
| 137 | + * UART Routing driver for Aspeed AST2500 |
| 138 | + * |
| 139 | + * Copyright (c) 2018 Google LLC |
| 140 | + * |
| 141 | + * This program is free software; you can redistribute it and/or |
| 142 | + * modify it under the terms of the GNU General Public License |
| 143 | + * version 2 as published by the Free Software Foundation. |
| 144 | + * |
| 145 | + * This program is distributed in the hope that it will be useful, |
| 146 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 147 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 148 | + * GNU General Public License for more details. |
| 149 | + */ |
| 150 | +#include <linux/device.h> |
| 151 | +#include <linux/module.h> |
| 152 | +#include <linux/of_address.h> |
| 153 | +#include <linux/of_platform.h> |
| 154 | + |
| 155 | +/* The Aspeed AST2500 allows to dynamically route the inputs for the built-in |
| 156 | + * UARTS and physical serial I/O ports. |
| 157 | + * |
| 158 | + * This allows, for example, to connect the output of UART to another UART. |
| 159 | + * This can be used to enable host<->BMC communication via UARTs, e.g. to allow |
| 160 | + * access to the host's serial console. |
| 161 | + * |
| 162 | + * This driver is for the BMC side. The sysfs files allow the BMC userspace |
| 163 | + * which owns the system configuration policy, to configure how UARTs and |
| 164 | + * physical serial I/O ports are routed. |
| 165 | + */ |
| 166 | + |
| 167 | +#define ASPEED_HICRA_IO1 "io1" |
| 168 | +#define ASPEED_HICRA_IO2 "io2" |
| 169 | +#define ASPEED_HICRA_IO3 "io3" |
| 170 | +#define ASPEED_HICRA_IO4 "io4" |
| 171 | +#define ASPEED_HICRA_IO5 "io5" |
| 172 | +#define ASPEED_HICRA_IO6 "io6" |
| 173 | +#define ASPEED_HICRA_UART1 "uart1" |
| 174 | +#define ASPEED_HICRA_UART2 "uart2" |
| 175 | +#define ASPEED_HICRA_UART3 "uart3" |
| 176 | +#define ASPEED_HICRA_UART4 "uart4" |
| 177 | +#define ASPEED_HICRA_UART5 "uart5" |
| 178 | + |
| 179 | +struct aspeed_uart_routing { |
| 180 | + struct device *dev; |
| 181 | + void __iomem *regs; |
| 182 | + spinlock_t lock; |
| 183 | +}; |
| 184 | + |
| 185 | +struct aspeed_uart_routing_selector { |
| 186 | + struct device_attribute dev_attr; |
| 187 | + int shift; |
| 188 | + int mask; |
| 189 | + const char * const options[]; |
| 190 | +}; |
| 191 | + |
| 192 | +#define to_routing_selector(_dev_attr) \ |
| 193 | + container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr) |
| 194 | + |
| 195 | + |
| 196 | +static ssize_t aspeed_uart_routing_show(struct device *dev, |
| 197 | + struct device_attribute *attr, |
| 198 | + char *buf); |
| 199 | + |
| 200 | +static ssize_t aspeed_uart_routing_store(struct device *dev, |
| 201 | + struct device_attribute *attr, |
| 202 | + const char *buf, size_t count); |
| 203 | + |
| 204 | +#define ROUTING_ATTR(_name) { \ |
| 205 | + .attr = {.name = _name, \ |
| 206 | + .mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO) }, \ |
| 207 | + .show = aspeed_uart_routing_show, \ |
| 208 | + .store = aspeed_uart_routing_store, \ |
| 209 | +} |
| 210 | + |
| 211 | +static struct aspeed_uart_routing_selector uart5_sel = { |
| 212 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART5), |
| 213 | + .shift = 28, |
| 214 | + .mask = 0xf, |
| 215 | + .options = { |
| 216 | + ASPEED_HICRA_IO5, // 0 |
| 217 | + ASPEED_HICRA_IO1, // 1 |
| 218 | + ASPEED_HICRA_IO2, // 2 |
| 219 | + ASPEED_HICRA_IO3, // 3 |
| 220 | + ASPEED_HICRA_IO4, // 4 |
| 221 | + ASPEED_HICRA_UART1, // 5 |
| 222 | + ASPEED_HICRA_UART2, // 6 |
| 223 | + ASPEED_HICRA_UART3, // 7 |
| 224 | + ASPEED_HICRA_UART4, // 8 |
| 225 | + ASPEED_HICRA_IO6, // 9 |
| 226 | + NULL, // NULL termination |
| 227 | + }, |
| 228 | +}; |
| 229 | + |
| 230 | +static struct aspeed_uart_routing_selector uart4_sel = { |
| 231 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART4), |
| 232 | + .shift = 25, |
| 233 | + .mask = 0x7, |
| 234 | + .options = { |
| 235 | + ASPEED_HICRA_IO4, // 0 |
| 236 | + ASPEED_HICRA_IO1, // 1 |
| 237 | + ASPEED_HICRA_IO2, // 2 |
| 238 | + ASPEED_HICRA_IO3, // 3 |
| 239 | + ASPEED_HICRA_UART1, // 4 |
| 240 | + ASPEED_HICRA_UART2, // 5 |
| 241 | + ASPEED_HICRA_UART3, // 6 |
| 242 | + ASPEED_HICRA_IO6, // 7 |
| 243 | + NULL, // NULL termination |
| 244 | + }, |
| 245 | +}; |
| 246 | + |
| 247 | +static struct aspeed_uart_routing_selector uart3_sel = { |
| 248 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART3), |
| 249 | + .shift = 22, |
| 250 | + .mask = 0x7, |
| 251 | + .options = { |
| 252 | + ASPEED_HICRA_IO3, // 0 |
| 253 | + ASPEED_HICRA_IO4, // 1 |
| 254 | + ASPEED_HICRA_IO1, // 2 |
| 255 | + ASPEED_HICRA_IO2, // 3 |
| 256 | + ASPEED_HICRA_UART4, // 4 |
| 257 | + ASPEED_HICRA_UART1, // 5 |
| 258 | + ASPEED_HICRA_UART2, // 6 |
| 259 | + ASPEED_HICRA_IO6, // 7 |
| 260 | + NULL, // NULL termination |
| 261 | + }, |
| 262 | +}; |
| 263 | + |
| 264 | +static struct aspeed_uart_routing_selector uart2_sel = { |
| 265 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART2), |
| 266 | + .shift = 19, |
| 267 | + .mask = 0x7, |
| 268 | + .options = { |
| 269 | + ASPEED_HICRA_IO2, // 0 |
| 270 | + ASPEED_HICRA_IO3, // 1 |
| 271 | + ASPEED_HICRA_IO4, // 2 |
| 272 | + ASPEED_HICRA_IO1, // 3 |
| 273 | + ASPEED_HICRA_UART3, // 4 |
| 274 | + ASPEED_HICRA_UART4, // 5 |
| 275 | + ASPEED_HICRA_UART1, // 6 |
| 276 | + ASPEED_HICRA_IO6, // 7 |
| 277 | + NULL, // NULL termination |
| 278 | + }, |
| 279 | +}; |
| 280 | + |
| 281 | +static struct aspeed_uart_routing_selector uart1_sel = { |
| 282 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART1), |
| 283 | + .shift = 16, |
| 284 | + .mask = 0x7, |
| 285 | + .options = { |
| 286 | + ASPEED_HICRA_IO1, // 0 |
| 287 | + ASPEED_HICRA_IO2, // 1 |
| 288 | + ASPEED_HICRA_IO3, // 2 |
| 289 | + ASPEED_HICRA_IO4, // 3 |
| 290 | + ASPEED_HICRA_UART2, // 4 |
| 291 | + ASPEED_HICRA_UART3, // 5 |
| 292 | + ASPEED_HICRA_UART4, // 6 |
| 293 | + ASPEED_HICRA_IO6, // 7 |
| 294 | + NULL, // NULL termination |
| 295 | + }, |
| 296 | +}; |
| 297 | + |
| 298 | +static struct aspeed_uart_routing_selector io5_sel = { |
| 299 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO5), |
| 300 | + .shift = 12, |
| 301 | + .mask = 0x7, |
| 302 | + .options = { |
| 303 | + ASPEED_HICRA_UART5, // 0 |
| 304 | + ASPEED_HICRA_UART1, // 1 |
| 305 | + ASPEED_HICRA_UART2, // 2 |
| 306 | + ASPEED_HICRA_UART3, // 3 |
| 307 | + ASPEED_HICRA_UART4, // 4 |
| 308 | + ASPEED_HICRA_IO1, // 5 |
| 309 | + ASPEED_HICRA_IO3, // 6 |
| 310 | + ASPEED_HICRA_IO6, // 7 |
| 311 | + NULL, // NULL termination |
| 312 | + }, |
| 313 | +}; |
| 314 | + |
| 315 | +static struct aspeed_uart_routing_selector io4_sel = { |
| 316 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO4), |
| 317 | + .shift = 9, |
| 318 | + .mask = 0x7, |
| 319 | + .options = { |
| 320 | + ASPEED_HICRA_UART4, // 0 |
| 321 | + ASPEED_HICRA_UART5, // 1 |
| 322 | + ASPEED_HICRA_UART1, // 2 |
| 323 | + ASPEED_HICRA_UART2, // 3 |
| 324 | + ASPEED_HICRA_UART3, // 4 |
| 325 | + ASPEED_HICRA_IO1, // 5 |
| 326 | + ASPEED_HICRA_IO2, // 6 |
| 327 | + ASPEED_HICRA_IO6, // 7 |
| 328 | + NULL, // NULL termination |
| 329 | + }, |
| 330 | +}; |
| 331 | + |
| 332 | +static struct aspeed_uart_routing_selector io3_sel = { |
| 333 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO3), |
| 334 | + .shift = 6, |
| 335 | + .mask = 0x7, |
| 336 | + .options = { |
| 337 | + ASPEED_HICRA_UART3, // 0 |
| 338 | + ASPEED_HICRA_UART4, // 1 |
| 339 | + ASPEED_HICRA_UART5, // 2 |
| 340 | + ASPEED_HICRA_UART1, // 3 |
| 341 | + ASPEED_HICRA_UART2, // 4 |
| 342 | + ASPEED_HICRA_IO1, // 5 |
| 343 | + ASPEED_HICRA_IO2, // 6 |
| 344 | + ASPEED_HICRA_IO6, // 7 |
| 345 | + NULL, // NULL termination |
| 346 | + }, |
| 347 | +}; |
| 348 | + |
| 349 | +static struct aspeed_uart_routing_selector io2_sel = { |
| 350 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO2), |
| 351 | + .shift = 3, |
| 352 | + .mask = 0x7, |
| 353 | + .options = { |
| 354 | + ASPEED_HICRA_UART2, // 0 |
| 355 | + ASPEED_HICRA_UART3, // 1 |
| 356 | + ASPEED_HICRA_UART4, // 2 |
| 357 | + ASPEED_HICRA_UART5, // 3 |
| 358 | + ASPEED_HICRA_UART1, // 4 |
| 359 | + ASPEED_HICRA_IO3, // 5 |
| 360 | + ASPEED_HICRA_IO4, // 6 |
| 361 | + ASPEED_HICRA_IO6, // 7 |
| 362 | + NULL, // NULL termination |
| 363 | + }, |
| 364 | +}; |
| 365 | + |
| 366 | +static struct aspeed_uart_routing_selector io1_sel = { |
| 367 | + .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO1), |
| 368 | + .shift = 0, |
| 369 | + .mask = 0x7, |
| 370 | + .options = { |
| 371 | + ASPEED_HICRA_UART1, // 0 |
| 372 | + ASPEED_HICRA_UART2, // 1 |
| 373 | + ASPEED_HICRA_UART3, // 2 |
| 374 | + ASPEED_HICRA_UART4, // 3 |
| 375 | + ASPEED_HICRA_UART5, // 4 |
| 376 | + ASPEED_HICRA_IO3, // 5 |
| 377 | + ASPEED_HICRA_IO4, // 6 |
| 378 | + ASPEED_HICRA_IO6, // 7 |
| 379 | + NULL, // NULL termination |
| 380 | + }, |
| 381 | +}; |
| 382 | + |
| 383 | + |
| 384 | +static struct attribute *aspeed_uart_routing_attrs[] = { |
| 385 | + &uart1_sel.dev_attr.attr, |
| 386 | + &uart2_sel.dev_attr.attr, |
| 387 | + &uart3_sel.dev_attr.attr, |
| 388 | + &uart4_sel.dev_attr.attr, |
| 389 | + &uart5_sel.dev_attr.attr, |
| 390 | + &io1_sel.dev_attr.attr, |
| 391 | + &io2_sel.dev_attr.attr, |
| 392 | + &io3_sel.dev_attr.attr, |
| 393 | + &io4_sel.dev_attr.attr, |
| 394 | + &io5_sel.dev_attr.attr, |
| 395 | + NULL, |
| 396 | +}; |
| 397 | + |
| 398 | +static const struct attribute_group aspeed_uart_routing_attr_group = { |
| 399 | + .attrs = aspeed_uart_routing_attrs, |
| 400 | +}; |
| 401 | + |
| 402 | +static ssize_t aspeed_uart_routing_show(struct device *dev, |
| 403 | + struct device_attribute *attr, |
| 404 | + char *buf) |
| 405 | +{ |
| 406 | + struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); |
| 407 | + struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); |
| 408 | + int val, pos, len; |
| 409 | + |
| 410 | + val = (readl(uart_routing->regs) >> sel->shift) & sel->mask; |
| 411 | + |
| 412 | + len = 0; |
| 413 | + for (pos = 0; sel->options[pos] != NULL; ++pos) { |
| 414 | + if (pos == val) { |
| 415 | + len += snprintf(buf + len, PAGE_SIZE - 1 - len, |
| 416 | + "[%s] ", sel->options[pos]); |
| 417 | + } else { |
| 418 | + len += snprintf(buf + len, PAGE_SIZE - 1 - len, |
| 419 | + "%s ", sel->options[pos]); |
| 420 | + } |
| 421 | + } |
| 422 | + |
| 423 | + if (val >= pos) { |
| 424 | + len += snprintf(buf + len, PAGE_SIZE - 1 - len, |
| 425 | + "[unknown(%d)]", val); |
| 426 | + } |
| 427 | + |
| 428 | + len += snprintf(buf + len, PAGE_SIZE - 1 - len, "\n"); |
| 429 | + |
| 430 | + return len; |
| 431 | +} |
| 432 | + |
| 433 | +static ssize_t aspeed_uart_routing_store(struct device *dev, |
| 434 | + struct device_attribute *attr, |
| 435 | + const char *buf, size_t count) |
| 436 | +{ |
| 437 | + struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev); |
| 438 | + struct aspeed_uart_routing_selector *sel = to_routing_selector(attr); |
| 439 | + int val; |
| 440 | + u32 reg; |
| 441 | + |
| 442 | + val = match_string(sel->options, -1, buf); |
| 443 | + if (val < 0) { |
| 444 | + dev_err(dev, "invalid value \"%s\"\n", buf); |
| 445 | + return -EINVAL; |
| 446 | + } |
| 447 | + |
| 448 | + spin_lock(&uart_routing->lock); |
| 449 | + reg = readl(uart_routing->regs); |
| 450 | + // Zero out existing value in specified bits. |
| 451 | + reg &= ~(sel->mask << sel->shift); |
| 452 | + // Set new value in specified bits. |
| 453 | + reg |= (val & sel->mask) << sel->shift; |
| 454 | + writel(reg, uart_routing->regs); |
| 455 | + spin_unlock(&uart_routing->lock); |
| 456 | + |
| 457 | + return count; |
| 458 | +} |
| 459 | + |
| 460 | +static int aspeed_uart_routing_probe(struct platform_device *pdev) |
| 461 | +{ |
| 462 | + struct aspeed_uart_routing *uart_routing; |
| 463 | + struct resource *res; |
| 464 | + int rc; |
| 465 | + |
| 466 | + uart_routing = devm_kzalloc(&pdev->dev, |
| 467 | + sizeof(*uart_routing), |
| 468 | + GFP_KERNEL); |
| 469 | + if (!uart_routing) |
| 470 | + return -ENOMEM; |
| 471 | + |
| 472 | + spin_lock_init(&uart_routing->lock); |
| 473 | + uart_routing->dev = &pdev->dev; |
| 474 | + |
| 475 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 476 | + uart_routing->regs = devm_ioremap_resource(&pdev->dev, res); |
| 477 | + if (IS_ERR(uart_routing->regs)) |
| 478 | + return PTR_ERR(uart_routing->regs); |
| 479 | + |
| 480 | + rc = sysfs_create_group(&uart_routing->dev->kobj, |
| 481 | + &aspeed_uart_routing_attr_group); |
| 482 | + if (rc < 0) |
| 483 | + return rc; |
| 484 | + |
| 485 | + platform_set_drvdata(pdev, uart_routing); |
| 486 | + |
| 487 | + return 0; |
| 488 | +} |
| 489 | + |
| 490 | +static int aspeed_uart_routing_remove(struct platform_device *pdev) |
| 491 | +{ |
| 492 | + struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev); |
| 493 | + |
| 494 | + sysfs_remove_group(&uart_routing->dev->kobj, |
| 495 | + &aspeed_uart_routing_attr_group); |
| 496 | + |
| 497 | + return 0; |
| 498 | +} |
| 499 | + |
| 500 | +static const struct of_device_id aspeed_uart_routing_table[] = { |
| 501 | + { .compatible = "aspeed,ast2500-uart-routing" }, |
| 502 | + { }, |
| 503 | +}; |
| 504 | + |
| 505 | +static struct platform_driver aspeed_uart_routing_driver = { |
| 506 | + .driver = { |
| 507 | + .name = "aspeed-uart-routing", |
| 508 | + .of_match_table = aspeed_uart_routing_table, |
| 509 | + }, |
| 510 | + .probe = aspeed_uart_routing_probe, |
| 511 | + .remove = aspeed_uart_routing_remove, |
| 512 | +}; |
| 513 | + |
| 514 | +module_platform_driver(aspeed_uart_routing_driver); |
| 515 | + |
| 516 | +MODULE_AUTHOR("Oskar Senft <osk@google.com>"); |
| 517 | +MODULE_LICENSE("GPL v2"); |
| 518 | +MODULE_DESCRIPTION("Driver to configure Aspeed UART routing"); |
| 519 | -- |
| 520 | 2.25.1 |
| 521 | |