.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | | -/** |
---|
| 2 | +/* |
---|
3 | 3 | * drd.c - DesignWare USB3 DRD Controller Dual-role support |
---|
4 | 4 | * |
---|
5 | | - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com |
---|
| 5 | + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com |
---|
6 | 6 | * |
---|
7 | 7 | * Authors: Roger Quadros <rogerq@ti.com> |
---|
8 | 8 | */ |
---|
.. | .. |
---|
56 | 56 | spin_lock(&dwc->lock); |
---|
57 | 57 | if (dwc->otg_restart_host) { |
---|
58 | 58 | dwc3_otg_host_init(dwc); |
---|
59 | | - dwc->otg_restart_host = 0; |
---|
| 59 | + dwc->otg_restart_host = false; |
---|
60 | 60 | } |
---|
61 | 61 | |
---|
62 | 62 | spin_unlock(&dwc->lock); |
---|
.. | .. |
---|
82 | 82 | |
---|
83 | 83 | if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST && |
---|
84 | 84 | !(reg & DWC3_OEVT_DEVICEMODE)) |
---|
85 | | - dwc->otg_restart_host = 1; |
---|
| 85 | + dwc->otg_restart_host = true; |
---|
86 | 86 | dwc3_writel(dwc->regs, DWC3_OEVT, reg); |
---|
87 | 87 | ret = IRQ_WAKE_THREAD; |
---|
88 | 88 | } |
---|
.. | .. |
---|
139 | 139 | struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); |
---|
140 | 140 | int irq; |
---|
141 | 141 | |
---|
142 | | - irq = platform_get_irq_byname(dwc3_pdev, "otg"); |
---|
| 142 | + irq = platform_get_irq_byname_optional(dwc3_pdev, "otg"); |
---|
143 | 143 | if (irq > 0) |
---|
144 | 144 | goto out; |
---|
145 | 145 | |
---|
146 | 146 | if (irq == -EPROBE_DEFER) |
---|
147 | 147 | goto out; |
---|
148 | 148 | |
---|
149 | | - irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); |
---|
| 149 | + irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3"); |
---|
150 | 150 | if (irq > 0) |
---|
151 | 151 | goto out; |
---|
152 | 152 | |
---|
.. | .. |
---|
156 | 156 | irq = platform_get_irq(dwc3_pdev, 0); |
---|
157 | 157 | if (irq > 0) |
---|
158 | 158 | goto out; |
---|
159 | | - |
---|
160 | | - if (irq != -EPROBE_DEFER) |
---|
161 | | - dev_err(dwc->dev, "missing OTG IRQ\n"); |
---|
162 | 159 | |
---|
163 | 160 | if (!irq) |
---|
164 | 161 | irq = -EINVAL; |
---|
.. | .. |
---|
423 | 420 | id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); |
---|
424 | 421 | if (id < 0) |
---|
425 | 422 | id = 0; |
---|
| 423 | + |
---|
| 424 | +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) |
---|
| 425 | + if (extcon_get_state(dwc->edev, EXTCON_USB)) |
---|
| 426 | + dwc->desired_role_sw_mode = USB_DR_MODE_PERIPHERAL; |
---|
| 427 | +#endif |
---|
| 428 | + |
---|
426 | 429 | dwc3_set_mode(dwc, id ? |
---|
427 | 430 | DWC3_GCTL_PRTCAP_HOST : |
---|
428 | 431 | DWC3_GCTL_PRTCAP_DEVICE); |
---|
.. | .. |
---|
434 | 437 | { |
---|
435 | 438 | struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); |
---|
436 | 439 | |
---|
| 440 | +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) |
---|
| 441 | + if (extcon_get_state(dwc->edev, EXTCON_USB)) |
---|
| 442 | + dwc->desired_role_sw_mode = USB_DR_MODE_PERIPHERAL; |
---|
| 443 | + else if (extcon_get_state(dwc->edev, EXTCON_USB_HOST)) |
---|
| 444 | + dwc->desired_role_sw_mode = USB_DR_MODE_HOST; |
---|
| 445 | + else |
---|
| 446 | + dwc->desired_role_sw_mode = USB_DR_MODE_UNKNOWN; |
---|
| 447 | +#endif |
---|
| 448 | + |
---|
437 | 449 | dwc3_set_mode(dwc, event ? |
---|
438 | 450 | DWC3_GCTL_PRTCAP_HOST : |
---|
439 | 451 | DWC3_GCTL_PRTCAP_DEVICE); |
---|
.. | .. |
---|
444 | 456 | static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) |
---|
445 | 457 | { |
---|
446 | 458 | struct device *dev = dwc->dev; |
---|
447 | | - struct device_node *np_phy, *np_conn; |
---|
448 | | - struct extcon_dev *edev; |
---|
| 459 | + struct device_node *np_phy; |
---|
| 460 | + struct extcon_dev *edev = NULL; |
---|
449 | 461 | const char *name; |
---|
450 | 462 | |
---|
451 | 463 | if (device_property_read_bool(dev, "extcon")) |
---|
.. | .. |
---|
465 | 477 | return edev; |
---|
466 | 478 | } |
---|
467 | 479 | |
---|
| 480 | + /* |
---|
| 481 | + * Try to get an extcon device from the USB PHY controller's "port" |
---|
| 482 | + * node. Check if it has the "port" node first, to avoid printing the |
---|
| 483 | + * error message from underlying code, as it's a valid case: extcon |
---|
| 484 | + * device (and "port" node) may be missing in case of "usb-role-switch" |
---|
| 485 | + * or OTG mode. |
---|
| 486 | + */ |
---|
468 | 487 | np_phy = of_parse_phandle(dev->of_node, "phys", 0); |
---|
469 | | - np_conn = of_graph_get_remote_node(np_phy, -1, -1); |
---|
| 488 | + if (of_graph_is_present(np_phy)) { |
---|
| 489 | + struct device_node *np_conn; |
---|
470 | 490 | |
---|
471 | | - if (np_conn) |
---|
472 | | - edev = extcon_find_edev_by_node(np_conn); |
---|
473 | | - else |
---|
474 | | - edev = NULL; |
---|
475 | | - |
---|
476 | | - of_node_put(np_conn); |
---|
| 491 | + np_conn = of_graph_get_remote_node(np_phy, -1, -1); |
---|
| 492 | + if (np_conn) |
---|
| 493 | + edev = extcon_find_edev_by_node(np_conn); |
---|
| 494 | + of_node_put(np_conn); |
---|
| 495 | + } |
---|
477 | 496 | of_node_put(np_phy); |
---|
478 | 497 | |
---|
479 | 498 | return edev; |
---|
480 | 499 | } |
---|
481 | 500 | |
---|
| 501 | +#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) |
---|
| 502 | +#define ROLE_SWITCH 1 |
---|
| 503 | +static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, |
---|
| 504 | + enum usb_role role) |
---|
| 505 | +{ |
---|
| 506 | + struct dwc3 *dwc = usb_role_switch_get_drvdata(sw); |
---|
| 507 | + u32 mode; |
---|
| 508 | + |
---|
| 509 | +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) |
---|
| 510 | + dwc->desired_role_sw_mode = role; |
---|
| 511 | + phy_set_mode_ext(dwc->usb2_generic_phy, PHY_MODE_USB_OTG, role); |
---|
| 512 | +#endif |
---|
| 513 | + |
---|
| 514 | + switch (role) { |
---|
| 515 | + case USB_ROLE_HOST: |
---|
| 516 | + mode = DWC3_GCTL_PRTCAP_HOST; |
---|
| 517 | + break; |
---|
| 518 | + case USB_ROLE_DEVICE: |
---|
| 519 | + mode = DWC3_GCTL_PRTCAP_DEVICE; |
---|
| 520 | + break; |
---|
| 521 | + default: |
---|
| 522 | + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) |
---|
| 523 | + mode = DWC3_GCTL_PRTCAP_HOST; |
---|
| 524 | + else |
---|
| 525 | + mode = DWC3_GCTL_PRTCAP_DEVICE; |
---|
| 526 | + break; |
---|
| 527 | + } |
---|
| 528 | + |
---|
| 529 | + dwc3_set_mode(dwc, mode); |
---|
| 530 | + return 0; |
---|
| 531 | +} |
---|
| 532 | + |
---|
| 533 | +static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) |
---|
| 534 | +{ |
---|
| 535 | + struct dwc3 *dwc = usb_role_switch_get_drvdata(sw); |
---|
| 536 | + unsigned long flags; |
---|
| 537 | + enum usb_role role; |
---|
| 538 | + |
---|
| 539 | + spin_lock_irqsave(&dwc->lock, flags); |
---|
| 540 | + switch (dwc->current_dr_role) { |
---|
| 541 | + case DWC3_GCTL_PRTCAP_HOST: |
---|
| 542 | + role = USB_ROLE_HOST; |
---|
| 543 | + break; |
---|
| 544 | + case DWC3_GCTL_PRTCAP_DEVICE: |
---|
| 545 | + role = USB_ROLE_DEVICE; |
---|
| 546 | + break; |
---|
| 547 | + case DWC3_GCTL_PRTCAP_OTG: |
---|
| 548 | + role = dwc->current_otg_role; |
---|
| 549 | + break; |
---|
| 550 | + default: |
---|
| 551 | + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) |
---|
| 552 | + role = USB_ROLE_HOST; |
---|
| 553 | + else |
---|
| 554 | + role = USB_ROLE_DEVICE; |
---|
| 555 | + break; |
---|
| 556 | + } |
---|
| 557 | + spin_unlock_irqrestore(&dwc->lock, flags); |
---|
| 558 | + return role; |
---|
| 559 | +} |
---|
| 560 | + |
---|
| 561 | +static int dwc3_setup_role_switch(struct dwc3 *dwc) |
---|
| 562 | +{ |
---|
| 563 | + struct usb_role_switch_desc dwc3_role_switch = {NULL}; |
---|
| 564 | + const char *str; |
---|
| 565 | + u32 mode; |
---|
| 566 | + int ret; |
---|
| 567 | + |
---|
| 568 | + ret = device_property_read_string(dwc->dev, "role-switch-default-mode", |
---|
| 569 | + &str); |
---|
| 570 | + if (ret >= 0 && !strncmp(str, "host", strlen("host"))) { |
---|
| 571 | + dwc->role_switch_default_mode = USB_DR_MODE_HOST; |
---|
| 572 | + mode = DWC3_GCTL_PRTCAP_HOST; |
---|
| 573 | + } else { |
---|
| 574 | + dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL; |
---|
| 575 | + mode = DWC3_GCTL_PRTCAP_DEVICE; |
---|
| 576 | + } |
---|
| 577 | + |
---|
| 578 | + dwc3_role_switch.fwnode = dev_fwnode(dwc->dev); |
---|
| 579 | + dwc3_role_switch.set = dwc3_usb_role_switch_set; |
---|
| 580 | + dwc3_role_switch.get = dwc3_usb_role_switch_get; |
---|
| 581 | + dwc3_role_switch.driver_data = dwc; |
---|
| 582 | + dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); |
---|
| 583 | + if (IS_ERR(dwc->role_sw)) |
---|
| 584 | + return PTR_ERR(dwc->role_sw); |
---|
| 585 | + |
---|
| 586 | + dwc3_set_mode(dwc, mode); |
---|
| 587 | + return 0; |
---|
| 588 | +} |
---|
| 589 | +#else |
---|
| 590 | +#define ROLE_SWITCH 0 |
---|
| 591 | +#define dwc3_setup_role_switch(x) 0 |
---|
| 592 | +#endif |
---|
| 593 | + |
---|
482 | 594 | int dwc3_drd_init(struct dwc3 *dwc) |
---|
483 | 595 | { |
---|
484 | 596 | int ret, irq; |
---|
| 597 | + |
---|
| 598 | + if (ROLE_SWITCH && |
---|
| 599 | + device_property_read_bool(dwc->dev, "usb-role-switch")) |
---|
| 600 | + return dwc3_setup_role_switch(dwc); |
---|
485 | 601 | |
---|
486 | 602 | dwc->edev = dwc3_get_extcon(dwc); |
---|
487 | 603 | if (IS_ERR(dwc->edev)) |
---|
.. | .. |
---|
534 | 650 | { |
---|
535 | 651 | unsigned long flags; |
---|
536 | 652 | |
---|
| 653 | + if (dwc->role_sw) |
---|
| 654 | + usb_role_switch_unregister(dwc->role_sw); |
---|
| 655 | + |
---|
537 | 656 | if (dwc->edev) |
---|
538 | 657 | extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, |
---|
539 | 658 | &dwc->edev_nb); |
---|
.. | .. |
---|
560 | 679 | break; |
---|
561 | 680 | } |
---|
562 | 681 | |
---|
563 | | - if (!dwc->edev) |
---|
| 682 | + if (dwc->otg_irq) |
---|
564 | 683 | free_irq(dwc->otg_irq, dwc); |
---|
565 | 684 | } |
---|