| .. | .. |
|---|
| 22 | 22 | #include <linux/serial_core.h> |
|---|
| 23 | 23 | #include <linux/tty_flip.h> |
|---|
| 24 | 24 | #include <linux/types.h> |
|---|
| 25 | +#include <linux/idr.h> |
|---|
| 25 | 26 | |
|---|
| 26 | 27 | #define SERIAL_NAME "ttyMPS" |
|---|
| 27 | 28 | #define DRIVER_NAME "mps2-uart" |
|---|
| .. | .. |
|---|
| 65 | 66 | |
|---|
| 66 | 67 | #define MPS2_MAX_PORTS 3 |
|---|
| 67 | 68 | |
|---|
| 69 | +#define UART_PORT_COMBINED_IRQ BIT(0) |
|---|
| 70 | + |
|---|
| 68 | 71 | struct mps2_uart_port { |
|---|
| 69 | 72 | struct uart_port port; |
|---|
| 70 | 73 | struct clk *clk; |
|---|
| 71 | 74 | unsigned int tx_irq; |
|---|
| 72 | 75 | unsigned int rx_irq; |
|---|
| 76 | + unsigned int flags; |
|---|
| 73 | 77 | }; |
|---|
| 74 | 78 | |
|---|
| 75 | 79 | static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port) |
|---|
| .. | .. |
|---|
| 264 | 268 | return handled; |
|---|
| 265 | 269 | } |
|---|
| 266 | 270 | |
|---|
| 271 | +static irqreturn_t mps2_uart_combinedirq(int irq, void *data) |
|---|
| 272 | +{ |
|---|
| 273 | + if (mps2_uart_rxirq(irq, data) == IRQ_HANDLED) |
|---|
| 274 | + return IRQ_HANDLED; |
|---|
| 275 | + |
|---|
| 276 | + if (mps2_uart_txirq(irq, data) == IRQ_HANDLED) |
|---|
| 277 | + return IRQ_HANDLED; |
|---|
| 278 | + |
|---|
| 279 | + if (mps2_uart_oerrirq(irq, data) == IRQ_HANDLED) |
|---|
| 280 | + return IRQ_HANDLED; |
|---|
| 281 | + |
|---|
| 282 | + return IRQ_NONE; |
|---|
| 283 | +} |
|---|
| 284 | + |
|---|
| 267 | 285 | static int mps2_uart_startup(struct uart_port *port) |
|---|
| 268 | 286 | { |
|---|
| 269 | 287 | struct mps2_uart_port *mps_port = to_mps2_port(port); |
|---|
| .. | .. |
|---|
| 274 | 292 | |
|---|
| 275 | 293 | mps2_uart_write8(port, control, UARTn_CTRL); |
|---|
| 276 | 294 | |
|---|
| 277 | | - ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0, |
|---|
| 278 | | - MAKE_NAME(-rx), mps_port); |
|---|
| 279 | | - if (ret) { |
|---|
| 280 | | - dev_err(port->dev, "failed to register rxirq (%d)\n", ret); |
|---|
| 281 | | - return ret; |
|---|
| 282 | | - } |
|---|
| 295 | + if (mps_port->flags & UART_PORT_COMBINED_IRQ) { |
|---|
| 296 | + ret = request_irq(port->irq, mps2_uart_combinedirq, 0, |
|---|
| 297 | + MAKE_NAME(-combined), mps_port); |
|---|
| 283 | 298 | |
|---|
| 284 | | - ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0, |
|---|
| 285 | | - MAKE_NAME(-tx), mps_port); |
|---|
| 286 | | - if (ret) { |
|---|
| 287 | | - dev_err(port->dev, "failed to register txirq (%d)\n", ret); |
|---|
| 288 | | - goto err_free_rxirq; |
|---|
| 289 | | - } |
|---|
| 299 | + if (ret) { |
|---|
| 300 | + dev_err(port->dev, "failed to register combinedirq (%d)\n", ret); |
|---|
| 301 | + return ret; |
|---|
| 302 | + } |
|---|
| 303 | + } else { |
|---|
| 304 | + ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED, |
|---|
| 305 | + MAKE_NAME(-overrun), mps_port); |
|---|
| 290 | 306 | |
|---|
| 291 | | - ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED, |
|---|
| 292 | | - MAKE_NAME(-overrun), mps_port); |
|---|
| 307 | + if (ret) { |
|---|
| 308 | + dev_err(port->dev, "failed to register oerrirq (%d)\n", ret); |
|---|
| 309 | + return ret; |
|---|
| 310 | + } |
|---|
| 293 | 311 | |
|---|
| 294 | | - if (ret) { |
|---|
| 295 | | - dev_err(port->dev, "failed to register oerrirq (%d)\n", ret); |
|---|
| 296 | | - goto err_free_txirq; |
|---|
| 312 | + ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0, |
|---|
| 313 | + MAKE_NAME(-rx), mps_port); |
|---|
| 314 | + if (ret) { |
|---|
| 315 | + dev_err(port->dev, "failed to register rxirq (%d)\n", ret); |
|---|
| 316 | + goto err_free_oerrirq; |
|---|
| 317 | + } |
|---|
| 318 | + |
|---|
| 319 | + ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0, |
|---|
| 320 | + MAKE_NAME(-tx), mps_port); |
|---|
| 321 | + if (ret) { |
|---|
| 322 | + dev_err(port->dev, "failed to register txirq (%d)\n", ret); |
|---|
| 323 | + goto err_free_rxirq; |
|---|
| 324 | + } |
|---|
| 325 | + |
|---|
| 297 | 326 | } |
|---|
| 298 | 327 | |
|---|
| 299 | 328 | control |= UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP; |
|---|
| .. | .. |
|---|
| 302 | 331 | |
|---|
| 303 | 332 | return 0; |
|---|
| 304 | 333 | |
|---|
| 305 | | -err_free_txirq: |
|---|
| 306 | | - free_irq(mps_port->tx_irq, mps_port); |
|---|
| 307 | 334 | err_free_rxirq: |
|---|
| 308 | 335 | free_irq(mps_port->rx_irq, mps_port); |
|---|
| 336 | +err_free_oerrirq: |
|---|
| 337 | + free_irq(port->irq, mps_port); |
|---|
| 309 | 338 | |
|---|
| 310 | 339 | return ret; |
|---|
| 311 | 340 | } |
|---|
| .. | .. |
|---|
| 319 | 348 | |
|---|
| 320 | 349 | mps2_uart_write8(port, control, UARTn_CTRL); |
|---|
| 321 | 350 | |
|---|
| 322 | | - free_irq(mps_port->rx_irq, mps_port); |
|---|
| 323 | | - free_irq(mps_port->tx_irq, mps_port); |
|---|
| 351 | + if (!(mps_port->flags & UART_PORT_COMBINED_IRQ)) { |
|---|
| 352 | + free_irq(mps_port->rx_irq, mps_port); |
|---|
| 353 | + free_irq(mps_port->tx_irq, mps_port); |
|---|
| 354 | + } |
|---|
| 355 | + |
|---|
| 324 | 356 | free_irq(port->irq, mps_port); |
|---|
| 325 | 357 | } |
|---|
| 326 | 358 | |
|---|
| .. | .. |
|---|
| 397 | 429 | .verify_port = mps2_uart_verify_port, |
|---|
| 398 | 430 | }; |
|---|
| 399 | 431 | |
|---|
| 400 | | -static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS]; |
|---|
| 432 | +static DEFINE_IDR(ports_idr); |
|---|
| 401 | 433 | |
|---|
| 402 | 434 | #ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE |
|---|
| 403 | 435 | static void mps2_uart_console_putchar(struct uart_port *port, int ch) |
|---|
| .. | .. |
|---|
| 410 | 442 | |
|---|
| 411 | 443 | static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt) |
|---|
| 412 | 444 | { |
|---|
| 413 | | - struct uart_port *port = &mps2_uart_ports[co->index].port; |
|---|
| 445 | + struct mps2_uart_port *mps_port = idr_find(&ports_idr, co->index); |
|---|
| 446 | + struct uart_port *port = &mps_port->port; |
|---|
| 414 | 447 | |
|---|
| 415 | 448 | uart_console_write(port, s, cnt, mps2_uart_console_putchar); |
|---|
| 416 | 449 | } |
|---|
| .. | .. |
|---|
| 426 | 459 | if (co->index < 0 || co->index >= MPS2_MAX_PORTS) |
|---|
| 427 | 460 | return -ENODEV; |
|---|
| 428 | 461 | |
|---|
| 429 | | - mps_port = &mps2_uart_ports[co->index]; |
|---|
| 462 | + mps_port = idr_find(&ports_idr, co->index); |
|---|
| 463 | + |
|---|
| 464 | + if (!mps_port) |
|---|
| 465 | + return -ENODEV; |
|---|
| 430 | 466 | |
|---|
| 431 | 467 | if (options) |
|---|
| 432 | 468 | uart_parse_options(options, &baud, &parity, &bits, &flow); |
|---|
| .. | .. |
|---|
| 487 | 523 | .cons = MPS2_SERIAL_CONSOLE, |
|---|
| 488 | 524 | }; |
|---|
| 489 | 525 | |
|---|
| 490 | | -static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev) |
|---|
| 526 | +static int mps2_of_get_port(struct platform_device *pdev, |
|---|
| 527 | + struct mps2_uart_port *mps_port) |
|---|
| 491 | 528 | { |
|---|
| 492 | 529 | struct device_node *np = pdev->dev.of_node; |
|---|
| 493 | 530 | int id; |
|---|
| 494 | 531 | |
|---|
| 495 | 532 | if (!np) |
|---|
| 496 | | - return NULL; |
|---|
| 533 | + return -ENODEV; |
|---|
| 497 | 534 | |
|---|
| 498 | 535 | id = of_alias_get_id(np, "serial"); |
|---|
| 536 | + |
|---|
| 499 | 537 | if (id < 0) |
|---|
| 500 | | - id = 0; |
|---|
| 538 | + id = idr_alloc_cyclic(&ports_idr, (void *)mps_port, 0, MPS2_MAX_PORTS, GFP_KERNEL); |
|---|
| 539 | + else |
|---|
| 540 | + id = idr_alloc(&ports_idr, (void *)mps_port, id, MPS2_MAX_PORTS, GFP_KERNEL); |
|---|
| 501 | 541 | |
|---|
| 502 | | - if (WARN_ON(id >= MPS2_MAX_PORTS)) |
|---|
| 503 | | - return NULL; |
|---|
| 542 | + if (id < 0) |
|---|
| 543 | + return id; |
|---|
| 504 | 544 | |
|---|
| 505 | | - mps2_uart_ports[id].port.line = id; |
|---|
| 506 | | - return &mps2_uart_ports[id]; |
|---|
| 545 | + /* Only combined irq is presesnt */ |
|---|
| 546 | + if (platform_irq_count(pdev) == 1) |
|---|
| 547 | + mps_port->flags |= UART_PORT_COMBINED_IRQ; |
|---|
| 548 | + |
|---|
| 549 | + mps_port->port.line = id; |
|---|
| 550 | + |
|---|
| 551 | + return 0; |
|---|
| 507 | 552 | } |
|---|
| 508 | 553 | |
|---|
| 509 | | -static int mps2_init_port(struct mps2_uart_port *mps_port, |
|---|
| 510 | | - struct platform_device *pdev) |
|---|
| 554 | +static int mps2_init_port(struct platform_device *pdev, |
|---|
| 555 | + struct mps2_uart_port *mps_port) |
|---|
| 511 | 556 | { |
|---|
| 512 | 557 | struct resource *res; |
|---|
| 513 | 558 | int ret; |
|---|
| .. | .. |
|---|
| 519 | 564 | |
|---|
| 520 | 565 | mps_port->port.mapbase = res->start; |
|---|
| 521 | 566 | mps_port->port.mapsize = resource_size(res); |
|---|
| 522 | | - |
|---|
| 523 | | - mps_port->rx_irq = platform_get_irq(pdev, 0); |
|---|
| 524 | | - mps_port->tx_irq = platform_get_irq(pdev, 1); |
|---|
| 525 | | - mps_port->port.irq = platform_get_irq(pdev, 2); |
|---|
| 526 | | - |
|---|
| 527 | 567 | mps_port->port.iotype = UPIO_MEM; |
|---|
| 528 | 568 | mps_port->port.flags = UPF_BOOT_AUTOCONF; |
|---|
| 529 | 569 | mps_port->port.fifosize = 1; |
|---|
| .. | .. |
|---|
| 542 | 582 | |
|---|
| 543 | 583 | clk_disable_unprepare(mps_port->clk); |
|---|
| 544 | 584 | |
|---|
| 585 | + |
|---|
| 586 | + if (mps_port->flags & UART_PORT_COMBINED_IRQ) { |
|---|
| 587 | + mps_port->port.irq = platform_get_irq(pdev, 0); |
|---|
| 588 | + } else { |
|---|
| 589 | + mps_port->rx_irq = platform_get_irq(pdev, 0); |
|---|
| 590 | + mps_port->tx_irq = platform_get_irq(pdev, 1); |
|---|
| 591 | + mps_port->port.irq = platform_get_irq(pdev, 2); |
|---|
| 592 | + } |
|---|
| 593 | + |
|---|
| 545 | 594 | return ret; |
|---|
| 546 | 595 | } |
|---|
| 547 | 596 | |
|---|
| .. | .. |
|---|
| 550 | 599 | struct mps2_uart_port *mps_port; |
|---|
| 551 | 600 | int ret; |
|---|
| 552 | 601 | |
|---|
| 553 | | - mps_port = mps2_of_get_port(pdev); |
|---|
| 554 | | - if (!mps_port) |
|---|
| 555 | | - return -ENODEV; |
|---|
| 602 | + mps_port = devm_kzalloc(&pdev->dev, sizeof(struct mps2_uart_port), GFP_KERNEL); |
|---|
| 556 | 603 | |
|---|
| 557 | | - ret = mps2_init_port(mps_port, pdev); |
|---|
| 604 | + if (!mps_port) |
|---|
| 605 | + return -ENOMEM; |
|---|
| 606 | + |
|---|
| 607 | + ret = mps2_of_get_port(pdev, mps_port); |
|---|
| 608 | + if (ret) |
|---|
| 609 | + return ret; |
|---|
| 610 | + |
|---|
| 611 | + ret = mps2_init_port(pdev, mps_port); |
|---|
| 558 | 612 | if (ret) |
|---|
| 559 | 613 | return ret; |
|---|
| 560 | 614 | |
|---|