diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-03-16 08:22:49 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-03-16 08:22:49 +0100 |
commit | a8ab3e76297ea85d92f4ee0833bd469816a13ccf (patch) | |
tree | 0bb5724a3d050a5c84203fc38b23447166035b9e /drivers | |
parent | 6a7c533d4a1854f54901a065d8c672e890400d8a (diff) | |
parent | 4a5dbd9009570d3b23fd1392c4d178bfe3538d2c (diff) |
Merge tag 'usb-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes:
USB: changes for v5.7 merge window
Lots of changes on dwc3 this time, most of them from Thinh fixing a
bunch of really old mishaps on the driver.
DWC2 got support for STM32MP15 and a couple RockChip SoCs while DWC3
learned about Amlogic A1 family.
Apart from these, we have a few spelling fixes and other minor
non-critical fixes all over the place.
Signed-off-by: Felipe Balbi <balbi@kernel.org>
* tag 'usb-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (41 commits)
dt-bindings: usb: add documentation for aspeed usb-vhub
ARM: dts: aspeed-g4: add vhub port and endpoint properties
ARM: dts: aspeed-g5: add vhub port and endpoint properties
ARM: dts: aspeed-g6: add usb functions
usb: gadget: aspeed: add ast2600 vhub support
usb: gadget: aspeed: read vhub properties from device tree
usb: gadget: aspeed: support per-vhub usb descriptors
usb: gadget: f_phonet: Replace zero-length array with flexible-array member
usb: gadget: composite: Inform controller driver of self-powered
usb: gadget: amd5536udc: fix spelling mistake "reserverd" -> "reserved"
udc: s3c-hsudc: Silence warning about supplies during deferred probe
usb: dwc2: Silence warning about supplies during deferred probe
dt-bindings: usb: dwc2: add compatible property for rk3368 usb
dt-bindings: usb: dwc2: add compatible property for rk3328 usb
usb: gadget: add raw-gadget interface
usb: dwc2: Implement set_selfpowered()
usb: dwc3: qcom: Replace <linux/clk-provider.h> by <linux/of_clk.h>
usb: dwc3: core: don't do suspend for device mode if already suspended
usb: dwc3: Rework resets initialization to be more flexible
usb: dwc3: Rework clock initialization to be more flexible
...
Diffstat (limited to 'drivers')
27 files changed, 3004 insertions, 160 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 968e03b89d04..99b0bdfe0012 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -411,6 +411,10 @@ enum dwc2_ep0_state { * register. * 0 - Deactivate the transceiver (default) * 1 - Activate the transceiver + * @activate_stm_id_vb_detection: Activate external ID pin and Vbus level + * detection using GGPIO register. + * 0 - Deactivate the external level detection (default) + * 1 - Activate the external level detection * @g_dma: Enables gadget dma usage (default: autodetect). * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). * @g_rx_fifo_size: The periodic rx fifo size for the device, in @@ -481,6 +485,7 @@ struct dwc2_core_params { bool service_interval; u8 hird_threshold; bool activate_stm_fs_transceiver; + bool activate_stm_id_vb_detection; bool ipg_isoc_en; u16 max_packet_count; u32 max_transfer_size; @@ -874,6 +879,8 @@ struct dwc2_hregs_backup { * removed once all SoCs support usb transceiver. * @supplies: Definition of USB power supplies * @vbus_supply: Regulator supplying vbus. + * @usb33d: Optional 3.3v regulator used on some stm32 devices to + * supply ID and VBUS detection hardware. * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth @@ -1061,6 +1068,7 @@ struct dwc2_hsotg { struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; struct regulator *vbus_supply; + struct regulator *usb33d; spinlock_t lock; void *priv; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 92ed32ec1607..12b98b466287 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1646,7 +1646,8 @@ static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg, switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: - status = 1 << USB_DEVICE_SELF_POWERED; + status = hsotg->gadget.is_selfpowered << + USB_DEVICE_SELF_POWERED; status |= hsotg->remote_wakeup_allowed << USB_DEVICE_REMOTE_WAKEUP; reply = cpu_to_le16(status); @@ -4528,6 +4529,26 @@ static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget) } /** + * dwc2_hsotg_set_selfpowered - set if device is self/bus powered + * @gadget: The usb gadget state + * @is_selfpowered: Whether the device is self-powered + * + * Set if the device is self or bus powered. + */ +static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget, + int is_selfpowered) +{ + struct dwc2_hsotg *hsotg = to_hsotg(gadget); + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + gadget->is_selfpowered = !!is_selfpowered; + spin_unlock_irqrestore(&hsotg->lock, flags); + + return 0; +} + +/** * dwc2_hsotg_pullup - connect/disconnect the USB PHY * @gadget: The usb gadget state * @is_on: Current state of the USB PHY @@ -4618,6 +4639,7 @@ static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA) static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { .get_frame = dwc2_hsotg_gadget_getframe, + .set_selfpowered = dwc2_hsotg_set_selfpowered, .udc_start = dwc2_hsotg_udc_start, .udc_stop = dwc2_hsotg_udc_stop, .pullup = dwc2_hsotg_pullup, diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 510e87ec0be8..c4027bbcedec 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -54,6 +54,12 @@ #define GOTGCTL_HSTSETHNPEN BIT(10) #define GOTGCTL_HNPREQ BIT(9) #define GOTGCTL_HSTNEGSCS BIT(8) +#define GOTGCTL_BVALOVAL BIT(7) +#define GOTGCTL_BVALOEN BIT(6) +#define GOTGCTL_AVALOVAL BIT(5) +#define GOTGCTL_AVALOEN BIT(4) +#define GOTGCTL_VBVALOVAL BIT(3) +#define GOTGCTL_VBVALOEN BIT(2) #define GOTGCTL_SESREQ BIT(1) #define GOTGCTL_SESREQSCS BIT(0) @@ -227,6 +233,8 @@ #define GPVNDCTL HSOTG_REG(0x0034) #define GGPIO HSOTG_REG(0x0038) #define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) +#define GGPIO_STM32_OTG_GCCFG_VBDEN BIT(21) +#define GGPIO_STM32_OTG_GCCFG_IDEN BIT(22) #define GUID HSOTG_REG(0x003c) #define GSNPSID HSOTG_REG(0x0040) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 31e090ac9f1e..8ccc83f7eb3f 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -163,6 +163,35 @@ static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg) p->host_perio_tx_fifo_size = 256; } +static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->speed = DWC2_SPEED_PARAM_FULL; + p->host_rx_fifo_size = 128; + p->host_nperio_tx_fifo_size = 96; + p->host_perio_tx_fifo_size = 96; + p->max_packet_count = 256; + p->phy_type = DWC2_PHY_TYPE_PARAM_FS; + p->i2c_enable = false; + p->activate_stm_fs_transceiver = true; + p->activate_stm_id_vb_detection = true; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; +} + +static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->activate_stm_id_vb_detection = true; + p->host_rx_fifo_size = 440; + p->host_nperio_tx_fifo_size = 256; + p->host_perio_tx_fifo_size = 256; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; +} + const struct of_device_id dwc2_of_match_table[] = { { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params }, { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, @@ -186,6 +215,10 @@ const struct of_device_id dwc2_of_match_table[] = { { .compatible = "st,stm32f4x9-hsotg" }, { .compatible = "st,stm32f7-hsotg", .data = dwc2_set_stm32f7_hsotg_params }, + { .compatible = "st,stm32mp15-fsotg", + .data = dwc2_set_stm32mp15_fsotg_params }, + { .compatible = "st,stm32mp15-hsotg", + .data = dwc2_set_stm32mp15_hsotg_params }, {}, }; MODULE_DEVICE_TABLE(of, dwc2_of_match_table); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 3c6ce09a6db5..8ea4a24637fa 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -285,7 +285,9 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies), hsotg->supplies); if (ret) { - dev_err(hsotg->dev, "failed to request supplies: %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(hsotg->dev, "failed to request supplies: %d\n", + ret); return ret; } return 0; @@ -312,6 +314,9 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); + if (hsotg->params.activate_stm_id_vb_detection) + regulator_disable(hsotg->usb33d); + if (hsotg->ll_hw_enabled) dwc2_lowlevel_hw_disable(hsotg); @@ -464,10 +469,35 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) goto error; + if (hsotg->params.activate_stm_id_vb_detection) { + u32 ggpio; + + hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d"); + if (IS_ERR(hsotg->usb33d)) { + retval = PTR_ERR(hsotg->usb33d); + if (retval != -EPROBE_DEFER) + dev_err(hsotg->dev, + "failed to request usb33d supply: %d\n", + retval); + goto error; + } + retval = regulator_enable(hsotg->usb33d); + if (retval) { + dev_err(hsotg->dev, + "failed to enable usb33d supply: %d\n", retval); + goto error; + } + + ggpio = dwc2_readl(hsotg, GGPIO); + ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(hsotg, ggpio, GGPIO); + } + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg); if (retval) - goto error; + goto error_init; hsotg->gadget_enabled = 1; } @@ -493,7 +523,7 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) { if (hsotg->gadget_enabled) dwc2_hsotg_remove(hsotg); - goto error; + goto error_init; } hsotg->hcd_enabled = 1; } @@ -509,6 +539,9 @@ static int dwc2_driver_probe(struct platform_device *dev) return 0; +error_init: + if (hsotg->params.activate_stm_id_vb_detection) + regulator_disable(hsotg->usb33d); error: dwc2_lowlevel_hw_disable(hsotg); return retval; @@ -523,6 +556,37 @@ static int __maybe_unused dwc2_suspend(struct device *dev) if (is_device_mode) dwc2_hsotg_suspend(dwc2); + if (dwc2->params.activate_stm_id_vb_detection) { + unsigned long flags; + u32 ggpio, gotgctl; + + /* + * Need to force the mode to the current mode to avoid Mode + * Mismatch Interrupt when ID detection will be disabled. + */ + dwc2_force_mode(dwc2, !is_device_mode); + + spin_lock_irqsave(&dwc2->lock, flags); + gotgctl = dwc2_readl(dwc2, GOTGCTL); + /* bypass debounce filter, enable overrides */ + gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; + gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN; + /* Force A / B session if needed */ + if (gotgctl & GOTGCTL_ASESVLD) + gotgctl |= GOTGCTL_AVALOVAL; + if (gotgctl & GOTGCTL_BSESVLD) + gotgctl |= GOTGCTL_BVALOVAL; + dwc2_writel(dwc2, gotgctl, GOTGCTL); + spin_unlock_irqrestore(&dwc2->lock, flags); + + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(dwc2, ggpio, GGPIO); + + regulator_disable(dwc2->usb33d); + } + if (dwc2->ll_hw_enabled && (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) { ret = __dwc2_lowlevel_hw_disable(dwc2); @@ -544,6 +608,34 @@ static int __maybe_unused dwc2_resume(struct device *dev) } dwc2->phy_off_for_suspend = false; + if (dwc2->params.activate_stm_id_vb_detection) { + unsigned long flags; + u32 ggpio, gotgctl; + + ret = regulator_enable(dwc2->usb33d); + if (ret) + return ret; + + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; + dwc2_writel(dwc2, ggpio, GGPIO); + + /* ID/VBUS detection startup time */ + usleep_range(5000, 7000); + + spin_lock_irqsave(&dwc2->lock, flags); + gotgctl = dwc2_readl(dwc2, GOTGCTL); + gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS; + gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | + GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL); + dwc2_writel(dwc2, gotgctl, GOTGCTL); + spin_unlock_irqrestore(&dwc2->lock, flags); + } + + /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ + dwc2_force_dr_mode(dwc2); + if (dwc2_is_device_mode(dwc2)) ret = dwc2_hsotg_resume(dwc2); diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 1d85c42b9c67..edc17155cb2b 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -289,12 +289,6 @@ done: return 0; } -static const struct clk_bulk_data dwc3_core_clks[] = { - { .id = "ref" }, - { .id = "bus_early" }, - { .id = "suspend" }, -}; - /* * dwc3_frame_length_adjustment - Adjusts frame length if required * @dwc3: Pointer to our controller context structure @@ -1029,6 +1023,9 @@ static int dwc3_core_init(struct dwc3 *dwc) if (dwc->dis_tx_ipgap_linecheck_quirk) reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + if (dwc->parkmode_disable_ss_quirk) + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); } @@ -1342,6 +1339,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,dis-del-phy-power-chg-quirk"); dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev, "snps,dis-tx-ipgap-linecheck-quirk"); + dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev, + "snps,parkmode-disable-ss-quirk"); dwc->tx_de_emphasis_quirk = device_property_read_bool(dev, "snps,tx_de_emphasis_quirk"); @@ -1441,11 +1440,6 @@ static int dwc3_probe(struct platform_device *pdev) if (!dwc) return -ENOMEM; - dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks), - GFP_KERNEL); - if (!dwc->clks) - return -ENOMEM; - dwc->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1476,22 +1470,23 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_get_properties(dwc); - dwc->reset = devm_reset_control_get_optional_shared(dev, NULL); + dwc->reset = devm_reset_control_array_get(dev, true, true); if (IS_ERR(dwc->reset)) return PTR_ERR(dwc->reset); if (dev->of_node) { - dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); - - ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks); + ret = devm_clk_bulk_get_all(dev, &dwc->clks); if (ret == -EPROBE_DEFER) return ret; /* * Clocks are optional, but new DT platforms should support all * clocks as required by the DT-binding. */ - if (ret) + if (ret < 0) dwc->num_clks = 0; + else + dwc->num_clks = ret; + } ret = reset_control_deassert(dwc->reset); @@ -1637,6 +1632,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: + if (pm_runtime_suspended(dwc->dev)) + break; spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_suspend(dwc); spin_unlock_irqrestore(&dwc->lock, flags); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 77c4a9abe365..6846eb0cba13 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -25,6 +25,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> +#include <linux/usb/role.h> #include <linux/ulpi/interface.h> #include <linux/phy/phy.h> @@ -249,6 +250,7 @@ #define DWC3_GUCTL_HSTINAUTORETRY BIT(14) /* Global User Control 1 Register */ +#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) @@ -953,6 +955,9 @@ struct dwc3_scratchpad_array { * @hsphy_mode: UTMI phy mode, one of following: * - USBPHY_INTERFACE_MODE_UTMI * - USBPHY_INTERFACE_MODE_UTMIW + * @role_sw: usb_role_switch handle + * @role_switch_default_mode: default operation mode of controller while + * usb role is USB_ROLE_NONE. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY @@ -1024,6 +1029,8 @@ struct dwc3_scratchpad_array { * change quirk. * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate * check during HS transmit. + * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed + * instances in park mode. * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value * 0 - -6dB de-emphasis @@ -1086,6 +1093,8 @@ struct dwc3 { struct extcon_dev *edev; struct notifier_block edev_nb; enum usb_phy_interface hsphy_mode; + struct usb_role_switch *role_sw; + enum usb_dr_mode role_switch_default_mode; u32 fladj; u32 irq_gadget; @@ -1215,6 +1224,7 @@ struct dwc3 { unsigned dis_u2_freeclk_exists_quirk:1; unsigned dis_del_phy_power_chg_quirk:1; unsigned dis_tx_ipgap_linecheck_quirk:1; + unsigned parkmode_disable_ss_quirk:1; unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index c946d64142ad..db68d48c2267 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -476,6 +476,92 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) return edev; } +#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) +#define ROLE_SWITCH 1 +static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + u32 mode; + + switch (role) { + case USB_ROLE_HOST: + mode = DWC3_GCTL_PRTCAP_HOST; + break; + case USB_ROLE_DEVICE: + mode = DWC3_GCTL_PRTCAP_DEVICE; + break; + default: + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) + mode = DWC3_GCTL_PRTCAP_HOST; + else + mode = DWC3_GCTL_PRTCAP_DEVICE; + break; + } + + dwc3_set_mode(dwc, mode); + return 0; +} + +static enum usb_role dwc3_usb_role_switch_get(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; + enum usb_role role; + + spin_lock_irqsave(&dwc->lock, flags); + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + role = USB_ROLE_HOST; + break; + case DWC3_GCTL_PRTCAP_DEVICE: + role = USB_ROLE_DEVICE; + break; + case DWC3_GCTL_PRTCAP_OTG: + role = dwc->current_otg_role; + break; + default: + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) + role = USB_ROLE_HOST; + else + role = USB_ROLE_DEVICE; + break; + } + spin_unlock_irqrestore(&dwc->lock, flags); + return role; +} + +static int dwc3_setup_role_switch(struct dwc3 *dwc) +{ + struct usb_role_switch_desc dwc3_role_switch = {NULL}; + const char *str; + u32 mode; + int ret; + + ret = device_property_read_string(dwc->dev, "role-switch-default-mode", + &str); + if (ret >= 0 && !strncmp(str, "host", strlen("host"))) { + dwc->role_switch_default_mode = USB_DR_MODE_HOST; + mode = DWC3_GCTL_PRTCAP_HOST; + } else { + dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL; + mode = DWC3_GCTL_PRTCAP_DEVICE; + } + + dwc3_role_switch.fwnode = dev_fwnode(dwc->dev); + dwc3_role_switch.set = dwc3_usb_role_switch_set; + dwc3_role_switch.get = dwc3_usb_role_switch_get; + dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); + if (IS_ERR(dwc->role_sw)) + return PTR_ERR(dwc->role_sw); + + dwc3_set_mode(dwc, mode); + return 0; +} +#else +#define ROLE_SWITCH 0 +#define dwc3_setup_role_switch(x) 0 +#endif + int dwc3_drd_init(struct dwc3 *dwc) { int ret, irq; @@ -484,7 +570,12 @@ int dwc3_drd_init(struct dwc3 *dwc) if (IS_ERR(dwc->edev)) return PTR_ERR(dwc->edev); - if (dwc->edev) { + if (ROLE_SWITCH && + device_property_read_bool(dwc->dev, "usb-role-switch")) { + ret = dwc3_setup_role_switch(dwc); + if (ret < 0) + return ret; + } else if (dwc->edev) { dwc->edev_nb.notifier_call = dwc3_drd_notifier; ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, &dwc->edev_nb); @@ -531,6 +622,9 @@ void dwc3_drd_exit(struct dwc3 *dwc) { unsigned long flags; + if (dwc->role_sw) + usb_role_switch_unregister(dwc->role_sw); + if (dwc->edev) extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, &dwc->edev_nb); diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 90bb022737da..48b68b6f0dc8 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -162,6 +162,12 @@ static const struct dwc3_exynos_driverdata exynos5250_drvdata = { .suspend_clk_idx = -1, }; +static const struct dwc3_exynos_driverdata exynos5420_drvdata = { + .clk_names = { "usbdrd30", "usbdrd30_susp_clk"}, + .num_clks = 2, + .suspend_clk_idx = 1, +}; + static const struct dwc3_exynos_driverdata exynos5433_drvdata = { .clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" }, .num_clks = 4, @@ -179,6 +185,9 @@ static const struct of_device_id exynos_dwc3_match[] = { .compatible = "samsung,exynos5250-dwusb3", .data = &exynos5250_drvdata, }, { + .compatible = "samsung,exynos5420-dwusb3", + .data = &exynos5420_drvdata, + }, { .compatible = "samsung,exynos5433-dwusb3", .data = &exynos5433_drvdata, }, { diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 3309ce90ca14..b81d085bc534 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -107,10 +107,37 @@ static const char *phy_names[PHY_COUNT] = { "usb2-phy0", "usb2-phy1", "usb3-phy0", }; +static struct clk_bulk_data meson_g12a_clocks[] = { + { .id = NULL }, +}; + +static struct clk_bulk_data meson_a1_clocks[] = { + { .id = "usb_ctrl" }, + { .id = "usb_bus" }, + { .id = "xtal_usb_ctrl" }, +}; + +struct dwc3_meson_g12a_drvdata { + bool otg_switch_supported; + struct clk_bulk_data *clks; + int num_clks; +}; + +static struct dwc3_meson_g12a_drvdata g12a_drvdata = { + .otg_switch_supported = true, + .clks = meson_g12a_clocks, + .num_clks = ARRAY_SIZE(meson_g12a_clocks), +}; + +static struct dwc3_meson_g12a_drvdata a1_drvdata = { + .otg_switch_supported = false, + .clks = meson_a1_clocks, + .num_clks = ARRAY_SIZE(meson_a1_clocks), +}; + struct dwc3_meson_g12a { struct device *dev; struct regmap *regmap; - struct clk *clk; struct reset_control *reset; struct phy *phys[PHY_COUNT]; enum usb_dr_mode otg_mode; @@ -120,6 +147,7 @@ struct dwc3_meson_g12a { struct regulator *vbus; struct usb_role_switch_desc switch_desc; struct usb_role_switch *role_switch; + const struct dwc3_meson_g12a_drvdata *drvdata; }; static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, @@ -151,7 +179,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) U2P_R0_POWER_ON_RESET, U2P_R0_POWER_ON_RESET); - if (i == USB2_OTG_PHY) { + if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) { regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, @@ -295,7 +323,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, { int ret; - if (!priv->phys[USB2_OTG_PHY]) + if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY]) return -EINVAL; if (mode == PHY_MODE_USB_HOST) @@ -381,14 +409,61 @@ static struct device *dwc3_meson_g12_find_child(struct device *dev, return &pdev->dev; } +static int dwc3_meson_g12a_otg_init(struct platform_device *pdev, + struct dwc3_meson_g12a *priv) +{ + enum phy_mode otg_id; + int ret, irq; + struct device *dev = &pdev->dev; + + if (!priv->drvdata->otg_switch_supported) + return 0; + + if (priv->otg_mode == USB_DR_MODE_OTG) { + /* Ack irq before registering */ + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_IRQ, 0); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + dwc3_meson_g12a_irq_thread, + IRQF_ONESHOT, pdev->name, priv); + if (ret) + return ret; + } + + /* Setup OTG mode corresponding to the ID pin */ + if (priv->otg_mode == USB_DR_MODE_OTG) { + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(dev, "Failed to switch OTG mode\n"); + } + } + + /* Setup role switcher */ + priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, + "snps,dwc3"); + priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); + priv->switch_desc.allow_userspace_control = true; + priv->switch_desc.set = dwc3_meson_g12a_role_set; + priv->switch_desc.get = dwc3_meson_g12a_role_get; + priv->switch_desc.driver_data = priv; + + priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); + if (IS_ERR(priv->role_switch)) + dev_warn(dev, "Unable to register Role Switch\n"); + + return 0; +} + static int dwc3_meson_g12a_probe(struct platform_device *pdev) { struct dwc3_meson_g12a *priv; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; void __iomem *base; - enum phy_mode otg_id; - int ret, i, irq; + int ret, i; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -410,17 +485,18 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) priv->vbus = NULL; } - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); + priv->drvdata = of_device_get_match_data(&pdev->dev); - ret = clk_prepare_enable(priv->clk); + ret = devm_clk_bulk_get(dev, + priv->drvdata->num_clks, + priv->drvdata->clks); if (ret) return ret; - devm_add_action_or_reset(dev, - (void(*)(void *))clk_disable_unprepare, - priv->clk); + ret = clk_bulk_prepare_enable(priv->drvdata->num_clks, + priv->drvdata->clks); + if (ret) + return ret; platform_set_drvdata(pdev, priv); priv->dev = dev; @@ -434,41 +510,28 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) ret = reset_control_reset(priv->reset); if (ret) - return ret; + goto err_disable_clks; ret = dwc3_meson_g12a_get_phys(priv); if (ret) - return ret; + goto err_disable_clks; if (priv->vbus) { ret = regulator_enable(priv->vbus); if (ret) - return ret; + goto err_disable_clks; } /* Get dr_mode */ priv->otg_mode = usb_get_dr_mode(dev); - if (priv->otg_mode == USB_DR_MODE_OTG) { - /* Ack irq before registering */ - regmap_update_bits(priv->regmap, USB_R5, - USB_R5_ID_DIG_IRQ, 0); - - irq = platform_get_irq(pdev, 0); - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - dwc3_meson_g12a_irq_thread, - IRQF_ONESHOT, pdev->name, priv); - if (ret) - return ret; - } - dwc3_meson_g12a_usb_init(priv); /* Init PHYs */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_init(priv->phys[i]); if (ret) - return ret; + goto err_disable_clks; } /* Set PHY Power */ @@ -479,32 +542,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) } ret = of_platform_populate(np, NULL, NULL, dev); - if (ret) { - clk_disable_unprepare(priv->clk); + if (ret) goto err_phys_power; - } - /* Setup OTG mode corresponding to the ID pin */ - if (priv->otg_mode == USB_DR_MODE_OTG) { - otg_id = dwc3_meson_g12a_get_id(priv); - if (otg_id != priv->otg_phy_mode) { - if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) - dev_warn(dev, "Failed to switch OTG mode\n"); - } - } - - /* Setup role switcher */ - priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, - "snps,dwc3"); - priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "sn |