| .. | .. |
|---|
| 3 | 3 | * core.c - ChipIdea USB IP core family device controller |
|---|
| 4 | 4 | * |
|---|
| 5 | 5 | * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. |
|---|
| 6 | + * Copyright (C) 2020 NXP |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Author: David Lopo |
|---|
| 8 | | - */ |
|---|
| 9 | | - |
|---|
| 10 | | -/* |
|---|
| 11 | | - * Description: ChipIdea USB IP core family device controller |
|---|
| 9 | + * Peter Chen <peter.chen@nxp.com> |
|---|
| 12 | 10 | * |
|---|
| 13 | | - * This driver is composed of several blocks: |
|---|
| 14 | | - * - HW: hardware interface |
|---|
| 15 | | - * - DBG: debug facilities (optional) |
|---|
| 16 | | - * - UTIL: utilities |
|---|
| 17 | | - * - ISR: interrupts handling |
|---|
| 18 | | - * - ENDPT: endpoint operations (Gadget API) |
|---|
| 19 | | - * - GADGET: gadget operations (Gadget API) |
|---|
| 20 | | - * - BUS: bus glue code, bus abstraction layer |
|---|
| 21 | | - * |
|---|
| 22 | | - * Compile Options |
|---|
| 23 | | - * - STALL_IN: non-empty bulk-in pipes cannot be halted |
|---|
| 24 | | - * if defined mass storage compliance succeeds but with warnings |
|---|
| 25 | | - * => case 4: Hi > Dn |
|---|
| 26 | | - * => case 5: Hi > Di |
|---|
| 27 | | - * => case 8: Hi <> Do |
|---|
| 28 | | - * if undefined usbtest 13 fails |
|---|
| 29 | | - * - TRACE: enable function tracing (depends on DEBUG) |
|---|
| 30 | | - * |
|---|
| 31 | | - * Main Features |
|---|
| 32 | | - * - Chapter 9 & Mass Storage Compliance with Gadget File Storage |
|---|
| 33 | | - * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) |
|---|
| 34 | | - * - Normal & LPM support |
|---|
| 35 | | - * |
|---|
| 36 | | - * USBTEST Report |
|---|
| 37 | | - * - OK: 0-12, 13 (STALL_IN defined) & 14 |
|---|
| 38 | | - * - Not Supported: 15 & 16 (ISO) |
|---|
| 39 | | - * |
|---|
| 40 | | - * TODO List |
|---|
| 41 | | - * - Suspend & Remote Wakeup |
|---|
| 11 | + * Main Features: |
|---|
| 12 | + * - Four transfers are supported, usbtest is passed |
|---|
| 13 | + * - USB Certification for gadget: CH9 and Mass Storage are passed |
|---|
| 14 | + * - Low power mode |
|---|
| 15 | + * - USB wakeup |
|---|
| 42 | 16 | */ |
|---|
| 43 | 17 | #include <linux/delay.h> |
|---|
| 44 | 18 | #include <linux/device.h> |
|---|
| .. | .. |
|---|
| 53 | 27 | #include <linux/kernel.h> |
|---|
| 54 | 28 | #include <linux/slab.h> |
|---|
| 55 | 29 | #include <linux/pm_runtime.h> |
|---|
| 30 | +#include <linux/pinctrl/consumer.h> |
|---|
| 56 | 31 | #include <linux/usb/ch9.h> |
|---|
| 57 | 32 | #include <linux/usb/gadget.h> |
|---|
| 58 | 33 | #include <linux/usb/otg.h> |
|---|
| .. | .. |
|---|
| 180 | 155 | |
|---|
| 181 | 156 | /** |
|---|
| 182 | 157 | * hw_port_test_set: writes port test mode (execute without interruption) |
|---|
| 158 | + * @ci: the controller |
|---|
| 183 | 159 | * @mode: new value |
|---|
| 184 | 160 | * |
|---|
| 185 | 161 | * This function returns an error code |
|---|
| .. | .. |
|---|
| 271 | 247 | ci->rev = ci_get_revision(ci); |
|---|
| 272 | 248 | |
|---|
| 273 | 249 | dev_dbg(ci->dev, |
|---|
| 274 | | - "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n", |
|---|
| 250 | + "revision: %d, lpm: %d; cap: %px op: %px\n", |
|---|
| 275 | 251 | ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); |
|---|
| 276 | 252 | |
|---|
| 277 | 253 | /* setup lock mode ? */ |
|---|
| .. | .. |
|---|
| 522 | 498 | hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); |
|---|
| 523 | 499 | |
|---|
| 524 | 500 | if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { |
|---|
| 525 | | - pr_err("cannot enter in %s device mode", ci_role(ci)->name); |
|---|
| 526 | | - pr_err("lpm = %i", ci->hw_bank.lpm); |
|---|
| 501 | + dev_err(ci->dev, "cannot enter in %s device mode\n", |
|---|
| 502 | + ci_role(ci)->name); |
|---|
| 503 | + dev_err(ci->dev, "lpm = %i\n", ci->hw_bank.lpm); |
|---|
| 527 | 504 | return -ENODEV; |
|---|
| 528 | 505 | } |
|---|
| 529 | 506 | |
|---|
| .. | .. |
|---|
| 607 | 584 | return NOTIFY_DONE; |
|---|
| 608 | 585 | } |
|---|
| 609 | 586 | |
|---|
| 587 | +static enum usb_role ci_usb_role_switch_get(struct usb_role_switch *sw) |
|---|
| 588 | +{ |
|---|
| 589 | + struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); |
|---|
| 590 | + enum usb_role role; |
|---|
| 591 | + unsigned long flags; |
|---|
| 592 | + |
|---|
| 593 | + spin_lock_irqsave(&ci->lock, flags); |
|---|
| 594 | + role = ci_role_to_usb_role(ci); |
|---|
| 595 | + spin_unlock_irqrestore(&ci->lock, flags); |
|---|
| 596 | + |
|---|
| 597 | + return role; |
|---|
| 598 | +} |
|---|
| 599 | + |
|---|
| 600 | +static int ci_usb_role_switch_set(struct usb_role_switch *sw, |
|---|
| 601 | + enum usb_role role) |
|---|
| 602 | +{ |
|---|
| 603 | + struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); |
|---|
| 604 | + struct ci_hdrc_cable *cable = NULL; |
|---|
| 605 | + enum usb_role current_role = ci_role_to_usb_role(ci); |
|---|
| 606 | + enum ci_role ci_role = usb_role_to_ci_role(role); |
|---|
| 607 | + unsigned long flags; |
|---|
| 608 | + |
|---|
| 609 | + if ((ci_role != CI_ROLE_END && !ci->roles[ci_role]) || |
|---|
| 610 | + (current_role == role)) |
|---|
| 611 | + return 0; |
|---|
| 612 | + |
|---|
| 613 | + pm_runtime_get_sync(ci->dev); |
|---|
| 614 | + /* Stop current role */ |
|---|
| 615 | + spin_lock_irqsave(&ci->lock, flags); |
|---|
| 616 | + if (current_role == USB_ROLE_DEVICE) |
|---|
| 617 | + cable = &ci->platdata->vbus_extcon; |
|---|
| 618 | + else if (current_role == USB_ROLE_HOST) |
|---|
| 619 | + cable = &ci->platdata->id_extcon; |
|---|
| 620 | + |
|---|
| 621 | + if (cable) { |
|---|
| 622 | + cable->changed = true; |
|---|
| 623 | + cable->connected = false; |
|---|
| 624 | + ci_irq(ci); |
|---|
| 625 | + spin_unlock_irqrestore(&ci->lock, flags); |
|---|
| 626 | + if (ci->wq && role != USB_ROLE_NONE) |
|---|
| 627 | + flush_workqueue(ci->wq); |
|---|
| 628 | + spin_lock_irqsave(&ci->lock, flags); |
|---|
| 629 | + } |
|---|
| 630 | + |
|---|
| 631 | + cable = NULL; |
|---|
| 632 | + |
|---|
| 633 | + /* Start target role */ |
|---|
| 634 | + if (role == USB_ROLE_DEVICE) |
|---|
| 635 | + cable = &ci->platdata->vbus_extcon; |
|---|
| 636 | + else if (role == USB_ROLE_HOST) |
|---|
| 637 | + cable = &ci->platdata->id_extcon; |
|---|
| 638 | + |
|---|
| 639 | + if (cable) { |
|---|
| 640 | + cable->changed = true; |
|---|
| 641 | + cable->connected = true; |
|---|
| 642 | + ci_irq(ci); |
|---|
| 643 | + } |
|---|
| 644 | + spin_unlock_irqrestore(&ci->lock, flags); |
|---|
| 645 | + pm_runtime_put_sync(ci->dev); |
|---|
| 646 | + |
|---|
| 647 | + return 0; |
|---|
| 648 | +} |
|---|
| 649 | + |
|---|
| 650 | +static struct usb_role_switch_desc ci_role_switch = { |
|---|
| 651 | + .set = ci_usb_role_switch_set, |
|---|
| 652 | + .get = ci_usb_role_switch_get, |
|---|
| 653 | + .allow_userspace_control = true, |
|---|
| 654 | +}; |
|---|
| 655 | + |
|---|
| 610 | 656 | static int ci_get_platdata(struct device *dev, |
|---|
| 611 | 657 | struct ci_hdrc_platform_data *platdata) |
|---|
| 612 | 658 | { |
|---|
| .. | .. |
|---|
| 625 | 671 | |
|---|
| 626 | 672 | if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { |
|---|
| 627 | 673 | /* Get the vbus regulator */ |
|---|
| 628 | | - platdata->reg_vbus = devm_regulator_get(dev, "vbus"); |
|---|
| 674 | + platdata->reg_vbus = devm_regulator_get_optional(dev, "vbus"); |
|---|
| 629 | 675 | if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { |
|---|
| 630 | 676 | return -EPROBE_DEFER; |
|---|
| 631 | 677 | } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { |
|---|
| .. | .. |
|---|
| 732 | 778 | else |
|---|
| 733 | 779 | cable->connected = false; |
|---|
| 734 | 780 | } |
|---|
| 781 | + |
|---|
| 782 | + if (device_property_read_bool(dev, "usb-role-switch")) |
|---|
| 783 | + ci_role_switch.fwnode = dev->fwnode; |
|---|
| 784 | + |
|---|
| 785 | + platdata->pctl = devm_pinctrl_get(dev); |
|---|
| 786 | + if (!IS_ERR(platdata->pctl)) { |
|---|
| 787 | + struct pinctrl_state *p; |
|---|
| 788 | + |
|---|
| 789 | + p = pinctrl_lookup_state(platdata->pctl, "default"); |
|---|
| 790 | + if (!IS_ERR(p)) |
|---|
| 791 | + platdata->pins_default = p; |
|---|
| 792 | + |
|---|
| 793 | + p = pinctrl_lookup_state(platdata->pctl, "host"); |
|---|
| 794 | + if (!IS_ERR(p)) |
|---|
| 795 | + platdata->pins_host = p; |
|---|
| 796 | + |
|---|
| 797 | + p = pinctrl_lookup_state(platdata->pctl, "device"); |
|---|
| 798 | + if (!IS_ERR(p)) |
|---|
| 799 | + platdata->pins_device = p; |
|---|
| 800 | + } |
|---|
| 801 | + |
|---|
| 735 | 802 | return 0; |
|---|
| 736 | 803 | } |
|---|
| 737 | 804 | |
|---|
| .. | .. |
|---|
| 820 | 887 | } |
|---|
| 821 | 888 | EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); |
|---|
| 822 | 889 | |
|---|
| 890 | +/** |
|---|
| 891 | + * ci_hdrc_query_available_role: get runtime available operation mode |
|---|
| 892 | + * |
|---|
| 893 | + * The glue layer can get current operation mode (host/peripheral/otg) |
|---|
| 894 | + * This function should be called after ci core device has created. |
|---|
| 895 | + * |
|---|
| 896 | + * @pdev: the platform device of ci core. |
|---|
| 897 | + * |
|---|
| 898 | + * Return runtime usb_dr_mode. |
|---|
| 899 | + */ |
|---|
| 900 | +enum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev) |
|---|
| 901 | +{ |
|---|
| 902 | + struct ci_hdrc *ci = platform_get_drvdata(pdev); |
|---|
| 903 | + |
|---|
| 904 | + if (!ci) |
|---|
| 905 | + return USB_DR_MODE_UNKNOWN; |
|---|
| 906 | + if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) |
|---|
| 907 | + return USB_DR_MODE_OTG; |
|---|
| 908 | + else if (ci->roles[CI_ROLE_HOST]) |
|---|
| 909 | + return USB_DR_MODE_HOST; |
|---|
| 910 | + else if (ci->roles[CI_ROLE_GADGET]) |
|---|
| 911 | + return USB_DR_MODE_PERIPHERAL; |
|---|
| 912 | + else |
|---|
| 913 | + return USB_DR_MODE_UNKNOWN; |
|---|
| 914 | +} |
|---|
| 915 | +EXPORT_SYMBOL_GPL(ci_hdrc_query_available_role); |
|---|
| 916 | + |
|---|
| 823 | 917 | static inline void ci_role_destroy(struct ci_hdrc *ci) |
|---|
| 824 | 918 | { |
|---|
| 825 | 919 | ci_hdrc_gadget_destroy(ci); |
|---|
| .. | .. |
|---|
| 872 | 966 | strlen(ci->roles[role]->name))) |
|---|
| 873 | 967 | break; |
|---|
| 874 | 968 | |
|---|
| 875 | | - if (role == CI_ROLE_END || role == ci->role) |
|---|
| 969 | + if (role == CI_ROLE_END) |
|---|
| 876 | 970 | return -EINVAL; |
|---|
| 971 | + |
|---|
| 972 | + mutex_lock(&ci->mutex); |
|---|
| 973 | + |
|---|
| 974 | + if (role == ci->role) { |
|---|
| 975 | + mutex_unlock(&ci->mutex); |
|---|
| 976 | + return n; |
|---|
| 977 | + } |
|---|
| 877 | 978 | |
|---|
| 878 | 979 | pm_runtime_get_sync(dev); |
|---|
| 879 | 980 | disable_irq(ci->irq); |
|---|
| .. | .. |
|---|
| 883 | 984 | ci_handle_vbus_change(ci); |
|---|
| 884 | 985 | enable_irq(ci->irq); |
|---|
| 885 | 986 | pm_runtime_put_sync(dev); |
|---|
| 987 | + mutex_unlock(&ci->mutex); |
|---|
| 886 | 988 | |
|---|
| 887 | 989 | return (ret == 0) ? n : ret; |
|---|
| 888 | 990 | } |
|---|
| .. | .. |
|---|
| 892 | 994 | &dev_attr_role.attr, |
|---|
| 893 | 995 | NULL, |
|---|
| 894 | 996 | }; |
|---|
| 895 | | - |
|---|
| 896 | | -static const struct attribute_group ci_attr_group = { |
|---|
| 897 | | - .attrs = ci_attrs, |
|---|
| 898 | | -}; |
|---|
| 997 | +ATTRIBUTE_GROUPS(ci); |
|---|
| 899 | 998 | |
|---|
| 900 | 999 | static int ci_hdrc_probe(struct platform_device *pdev) |
|---|
| 901 | 1000 | { |
|---|
| .. | .. |
|---|
| 921 | 1020 | return -ENOMEM; |
|---|
| 922 | 1021 | |
|---|
| 923 | 1022 | spin_lock_init(&ci->lock); |
|---|
| 1023 | + mutex_init(&ci->mutex); |
|---|
| 924 | 1024 | ci->dev = dev; |
|---|
| 925 | 1025 | ci->platdata = dev_get_platdata(dev); |
|---|
| 926 | 1026 | ci->imx28_write_fix = !!(ci->platdata->flags & |
|---|
| .. | .. |
|---|
| 944 | 1044 | } else if (ci->platdata->usb_phy) { |
|---|
| 945 | 1045 | ci->usb_phy = ci->platdata->usb_phy; |
|---|
| 946 | 1046 | } else { |
|---|
| 947 | | - ci->usb_phy = devm_usb_get_phy_by_phandle(dev->parent, "phys", |
|---|
| 948 | | - 0); |
|---|
| 1047 | + /* Look for a generic PHY first */ |
|---|
| 949 | 1048 | ci->phy = devm_phy_get(dev->parent, "usb-phy"); |
|---|
| 950 | 1049 | |
|---|
| 951 | | - /* Fallback to grabbing any registered USB2 PHY */ |
|---|
| 952 | | - if (IS_ERR(ci->usb_phy) && |
|---|
| 953 | | - PTR_ERR(ci->usb_phy) != -EPROBE_DEFER) |
|---|
| 1050 | + if (PTR_ERR(ci->phy) == -EPROBE_DEFER) { |
|---|
| 1051 | + ret = -EPROBE_DEFER; |
|---|
| 1052 | + goto ulpi_exit; |
|---|
| 1053 | + } else if (IS_ERR(ci->phy)) { |
|---|
| 1054 | + ci->phy = NULL; |
|---|
| 1055 | + } |
|---|
| 1056 | + |
|---|
| 1057 | + /* Look for a legacy USB PHY from device-tree next */ |
|---|
| 1058 | + if (!ci->phy) { |
|---|
| 1059 | + ci->usb_phy = devm_usb_get_phy_by_phandle(dev->parent, |
|---|
| 1060 | + "phys", 0); |
|---|
| 1061 | + |
|---|
| 1062 | + if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) { |
|---|
| 1063 | + ret = -EPROBE_DEFER; |
|---|
| 1064 | + goto ulpi_exit; |
|---|
| 1065 | + } else if (IS_ERR(ci->usb_phy)) { |
|---|
| 1066 | + ci->usb_phy = NULL; |
|---|
| 1067 | + } |
|---|
| 1068 | + } |
|---|
| 1069 | + |
|---|
| 1070 | + /* Look for any registered legacy USB PHY as last resort */ |
|---|
| 1071 | + if (!ci->phy && !ci->usb_phy) { |
|---|
| 954 | 1072 | ci->usb_phy = devm_usb_get_phy(dev->parent, |
|---|
| 955 | 1073 | USB_PHY_TYPE_USB2); |
|---|
| 956 | 1074 | |
|---|
| 957 | | - /* if both generic PHY and USB PHY layers aren't enabled */ |
|---|
| 958 | | - if (PTR_ERR(ci->phy) == -ENOSYS && |
|---|
| 959 | | - PTR_ERR(ci->usb_phy) == -ENXIO) { |
|---|
| 1075 | + if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) { |
|---|
| 1076 | + ret = -EPROBE_DEFER; |
|---|
| 1077 | + goto ulpi_exit; |
|---|
| 1078 | + } else if (IS_ERR(ci->usb_phy)) { |
|---|
| 1079 | + ci->usb_phy = NULL; |
|---|
| 1080 | + } |
|---|
| 1081 | + } |
|---|
| 1082 | + |
|---|
| 1083 | + /* No USB PHY was found in the end */ |
|---|
| 1084 | + if (!ci->phy && !ci->usb_phy) { |
|---|
| 960 | 1085 | ret = -ENXIO; |
|---|
| 961 | 1086 | goto ulpi_exit; |
|---|
| 962 | 1087 | } |
|---|
| 963 | | - |
|---|
| 964 | | - if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) { |
|---|
| 965 | | - ret = -EPROBE_DEFER; |
|---|
| 966 | | - goto ulpi_exit; |
|---|
| 967 | | - } |
|---|
| 968 | | - |
|---|
| 969 | | - if (IS_ERR(ci->phy)) |
|---|
| 970 | | - ci->phy = NULL; |
|---|
| 971 | | - else if (IS_ERR(ci->usb_phy)) |
|---|
| 972 | | - ci->usb_phy = NULL; |
|---|
| 973 | 1088 | } |
|---|
| 974 | 1089 | |
|---|
| 975 | 1090 | ret = ci_usb_phy_init(ci); |
|---|
| 976 | 1091 | if (ret) { |
|---|
| 977 | 1092 | dev_err(dev, "unable to init phy: %d\n", ret); |
|---|
| 978 | | - return ret; |
|---|
| 1093 | + goto ulpi_exit; |
|---|
| 979 | 1094 | } |
|---|
| 980 | 1095 | |
|---|
| 981 | 1096 | ci->hw_bank.phys = res->start; |
|---|
| 982 | 1097 | |
|---|
| 983 | 1098 | ci->irq = platform_get_irq(pdev, 0); |
|---|
| 984 | 1099 | if (ci->irq < 0) { |
|---|
| 985 | | - dev_err(dev, "missing IRQ\n"); |
|---|
| 986 | 1100 | ret = ci->irq; |
|---|
| 987 | 1101 | goto deinit_phy; |
|---|
| 988 | 1102 | } |
|---|
| .. | .. |
|---|
| 1025 | 1139 | } |
|---|
| 1026 | 1140 | } |
|---|
| 1027 | 1141 | |
|---|
| 1142 | + if (ci_role_switch.fwnode) { |
|---|
| 1143 | + ci_role_switch.driver_data = ci; |
|---|
| 1144 | + ci->role_switch = usb_role_switch_register(dev, |
|---|
| 1145 | + &ci_role_switch); |
|---|
| 1146 | + if (IS_ERR(ci->role_switch)) { |
|---|
| 1147 | + ret = PTR_ERR(ci->role_switch); |
|---|
| 1148 | + goto deinit_otg; |
|---|
| 1149 | + } |
|---|
| 1150 | + } |
|---|
| 1151 | + |
|---|
| 1028 | 1152 | if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { |
|---|
| 1029 | 1153 | if (ci->is_otg) { |
|---|
| 1030 | 1154 | ci->role = ci_otg_role(ci); |
|---|
| .. | .. |
|---|
| 1046 | 1170 | |
|---|
| 1047 | 1171 | if (!ci_otg_is_fsm_mode(ci)) { |
|---|
| 1048 | 1172 | /* only update vbus status for peripheral */ |
|---|
| 1049 | | - if (ci->role == CI_ROLE_GADGET) |
|---|
| 1173 | + if (ci->role == CI_ROLE_GADGET) { |
|---|
| 1174 | + /* Pull down DP for possible charger detection */ |
|---|
| 1175 | + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); |
|---|
| 1050 | 1176 | ci_handle_vbus_change(ci); |
|---|
| 1177 | + } |
|---|
| 1051 | 1178 | |
|---|
| 1052 | 1179 | ret = ci_role_start(ci, ci->role); |
|---|
| 1053 | 1180 | if (ret) { |
|---|
| .. | .. |
|---|
| 1080 | 1207 | device_set_wakeup_capable(&pdev->dev, true); |
|---|
| 1081 | 1208 | dbg_create_files(ci); |
|---|
| 1082 | 1209 | |
|---|
| 1083 | | - ret = sysfs_create_group(&dev->kobj, &ci_attr_group); |
|---|
| 1084 | | - if (ret) |
|---|
| 1085 | | - goto remove_debug; |
|---|
| 1086 | | - |
|---|
| 1087 | 1210 | return 0; |
|---|
| 1088 | 1211 | |
|---|
| 1089 | | -remove_debug: |
|---|
| 1090 | | - dbg_remove_files(ci); |
|---|
| 1091 | 1212 | stop: |
|---|
| 1213 | + if (ci->role_switch) |
|---|
| 1214 | + usb_role_switch_unregister(ci->role_switch); |
|---|
| 1215 | +deinit_otg: |
|---|
| 1092 | 1216 | if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) |
|---|
| 1093 | 1217 | ci_hdrc_otg_destroy(ci); |
|---|
| 1094 | 1218 | deinit_gadget: |
|---|
| .. | .. |
|---|
| 1107 | 1231 | { |
|---|
| 1108 | 1232 | struct ci_hdrc *ci = platform_get_drvdata(pdev); |
|---|
| 1109 | 1233 | |
|---|
| 1234 | + if (ci->role_switch) |
|---|
| 1235 | + usb_role_switch_unregister(ci->role_switch); |
|---|
| 1236 | + |
|---|
| 1110 | 1237 | if (ci->supports_runtime_pm) { |
|---|
| 1111 | 1238 | pm_runtime_get_sync(&pdev->dev); |
|---|
| 1112 | 1239 | pm_runtime_disable(&pdev->dev); |
|---|
| .. | .. |
|---|
| 1114 | 1241 | } |
|---|
| 1115 | 1242 | |
|---|
| 1116 | 1243 | dbg_remove_files(ci); |
|---|
| 1117 | | - sysfs_remove_group(&ci->dev->kobj, &ci_attr_group); |
|---|
| 1118 | 1244 | ci_role_destroy(ci); |
|---|
| 1119 | 1245 | ci_hdrc_enter_lpm(ci, true); |
|---|
| 1120 | 1246 | ci_usb_phy_exit(ci); |
|---|
| .. | .. |
|---|
| 1317 | 1443 | .driver = { |
|---|
| 1318 | 1444 | .name = "ci_hdrc", |
|---|
| 1319 | 1445 | .pm = &ci_pm_ops, |
|---|
| 1446 | + .dev_groups = ci_groups, |
|---|
| 1320 | 1447 | }, |
|---|
| 1321 | 1448 | }; |
|---|
| 1322 | 1449 | |
|---|