diff options
Diffstat (limited to 'drivers/usb')
36 files changed, 834 insertions, 355 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 8367d4f985c1..730d7eb449bb 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -395,6 +395,9 @@ enum dwc2_ep0_state { * (default when phy_type is UTMI+ or ULPI) * 1 - 6 MHz * (default when phy_type is Full Speed) + * @oc_disable: Flag to disable overcurrent condition. + * 0 - Allow overcurrent condition to get detected + * 1 - Disable overcurrent condtion to get detected * @ts_dline: Enable Term Select Dline pulsing * 0 - No (default) * 1 - Yes @@ -492,6 +495,7 @@ struct dwc2_core_params { bool dma_desc_fs_enable; bool host_support_fs_ls_low_power; bool host_ls_low_power_phy_clk; + bool oc_disable; u8 host_channels; u16 host_rx_fifo_size; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 0d8e09ccb59c..603c21612051 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3202,6 +3202,8 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg) call_gadget(hsotg, disconnect); hsotg->lx_state = DWC2_L3; + + usb_gadget_set_state(&hsotg->gadget, USB_STATE_NOTATTACHED); } /** @@ -4004,6 +4006,11 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) return -EINVAL; } + if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) { + dev_err(hsotg->dev, "%s: called in host mode?\n", __func__); + return -EINVAL; + } + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); spin_lock_irqsave(&hsotg->lock, flags); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index c2631145f404..e8fee0f969f0 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -213,6 +213,11 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); if (hsotg->params.phy_ulpi_ddr) usbcfg |= GUSBCFG_DDRSEL; + + /* Set external VBUS indicator as needed. */ + if (hsotg->params.oc_disable) + usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND | + GUSBCFG_INDICATORPASSTHROUGH); break; case DWC2_PHY_TYPE_PARAM_UTMI: /* UTMI+ interface */ @@ -3277,7 +3282,6 @@ static void dwc2_conn_id_status_change(struct work_struct *work) dwc2_core_init(hsotg, false); dwc2_enable_global_interrupts(hsotg); spin_lock_irqsave(&hsotg->lock, flags); - dwc2_hsotg_disconnect(hsotg); dwc2_hsotg_core_init_disconnected(hsotg, false); spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_hsotg_core_connect(hsotg); @@ -3296,8 +3300,12 @@ host: if (count > 250) dev_err(hsotg->dev, "Connection id status change timed out\n"); - hsotg->op_state = OTG_STATE_A_HOST; + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_hsotg_disconnect(hsotg); + spin_unlock_irqrestore(&hsotg->lock, flags); + + hsotg->op_state = OTG_STATE_A_HOST; /* Initialize the Core for Host mode */ dwc2_core_init(hsotg, false); dwc2_enable_global_interrupts(hsotg); diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index a3ffe97170ff..fe770a2834d2 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -136,6 +136,15 @@ static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg) p->activate_stm_fs_transceiver = true; } +static void dwc2_set_stm32f7xx_hsotg_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->host_rx_fifo_size = 622; + p->host_nperio_tx_fifo_size = 128; + p->host_perio_tx_fifo_size = 256; +} + 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 }, @@ -154,6 +163,8 @@ const struct of_device_id dwc2_of_match_table[] = { { .compatible = "st,stm32f4x9-fsotg", .data = dwc2_set_stm32f4x9_fsotg_params }, { .compatible = "st,stm32f4x9-hsotg" }, + { .compatible = "st,stm32f7xx-hsotg", + .data = dwc2_set_stm32f7xx_hsotg_params }, {}, }; MODULE_DEVICE_TABLE(of, dwc2_of_match_table); @@ -335,6 +346,9 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg) num); } } + + if (of_find_property(hsotg->dev->of_node, "disable-over-current", NULL)) + p->oc_disable = true; } static void dwc2_check_param_otg_cap(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 03474d3575ab..dabfa16fa267 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -156,9 +156,8 @@ static void __dwc3_set_mode(struct work_struct *work) } else { if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); - + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); + phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); } break; case DWC3_GCTL_PRTCAP_DEVICE: @@ -166,8 +165,8 @@ static void __dwc3_set_mode(struct work_struct *work) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) @@ -927,12 +926,13 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: + dwc->current_dr_role = DWC3_GCTL_PRTCAP_DEVICE; dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { @@ -942,12 +942,13 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) } break; case USB_DR_MODE_HOST: + dwc->current_dr_role = DWC3_GCTL_PRTCAP_HOST; dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); + phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); ret = dwc3_host_init(dwc); if (ret) { @@ -1293,21 +1294,19 @@ static int dwc3_suspend_common(struct dwc3 *dwc) { unsigned long flags; - switch (dwc->dr_mode) { - case USB_DR_MODE_PERIPHERAL: - case USB_DR_MODE_OTG: + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_DEVICE: spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_suspend(dwc); spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_core_exit(dwc); break; - case USB_DR_MODE_HOST: + case DWC3_GCTL_PRTCAP_HOST: default: /* do nothing */ break; } - dwc3_core_exit(dwc); - return 0; } @@ -1316,18 +1315,17 @@ static int dwc3_resume_common(struct dwc3 *dwc) unsigned long flags; int ret; - ret = dwc3_core_init(dwc); - if (ret) - return ret; + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_DEVICE: + ret = dwc3_core_init(dwc); + if (ret) + return ret; - switch (dwc->dr_mode) { - case USB_DR_MODE_PERIPHERAL: - case USB_DR_MODE_OTG: spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_resume(dwc); spin_unlock_irqrestore(&dwc->lock, flags); - /* FALLTHROUGH */ - case USB_DR_MODE_HOST: + break; + case DWC3_GCTL_PRTCAP_HOST: default: /* do nothing */ break; @@ -1338,7 +1336,7 @@ static int dwc3_resume_common(struct dwc3 *dwc) static int dwc3_runtime_checks(struct dwc3 *dwc) { - switch (dwc->dr_mode) { + switch (dwc->current_dr_role) { case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_OTG: if (dwc->connected) @@ -1381,19 +1379,17 @@ static int dwc3_runtime_resume(struct device *dev) if (ret) return ret; - switch (dwc->dr_mode) { - case USB_DR_MODE_PERIPHERAL: - case USB_DR_MODE_OTG: + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_DEVICE: dwc3_gadget_process_pending_events(dwc); break; - case USB_DR_MODE_HOST: + case DWC3_GCTL_PRTCAP_HOST: default: /* do nothing */ break; } pm_runtime_mark_last_busy(dev); - pm_runtime_put(dev); return 0; } @@ -1402,13 +1398,12 @@ static int dwc3_runtime_idle(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); - switch (dwc->dr_mode) { - case USB_DR_MODE_PERIPHERAL: - case USB_DR_MODE_OTG: + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_DEVICE: if (dwc3_runtime_checks(dwc)) return -EBUSY; break; - case USB_DR_MODE_HOST: + case DWC3_GCTL_PRTCAP_HOST: default: /* do nothing */ break; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index ea910acb4bb0..e33cc10121be 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -529,6 +529,7 @@ struct dwc3_event_buffer { * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @resource_index: Resource transfer index + * @frame_number: set to the frame number we want this transfer to start (ISOC) * @interval: the interval on which the ISOC transfer is started * @allocated_requests: number of requests allocated * @queued_requests: number of requests queued for transfer @@ -581,6 +582,7 @@ struct dwc3_ep { u8 resource_index; u32 allocated_requests; u32 queued_requests; + u32 frame_number; u32 interval; char name[20]; diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index a26d1fde0f5e..ceea1619f8aa 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -28,11 +28,13 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> struct dwc3_of_simple { struct device *dev; struct clk **clks; int num_clocks; + struct reset_control *resets; }; static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) @@ -95,10 +97,21 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) platform_set_drvdata(pdev, simple); simple->dev = dev; + simple->resets = of_reset_control_array_get_optional_exclusive(np); + if (IS_ERR(simple->resets)) { + ret = PTR_ERR(simple->resets); + dev_err(dev, "failed to get device resets, err=%d\n", ret); + return ret; + } + + ret = reset_control_deassert(simple->resets); + if (ret) + goto err_resetc_put; + ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, "clocks", "#clock-cells")); if (ret) - return ret; + goto err_resetc_assert; ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { @@ -107,7 +120,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) clk_put(simple->clks[i]); } - return ret; + goto err_resetc_assert; } pm_runtime_set_active(dev); @@ -115,6 +128,13 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) pm_runtime_get_sync(dev); return 0; + +err_resetc_assert: + reset_control_assert(simple->resets); + +err_resetc_put: + reset_control_put(simple->resets); + return ret; } static int dwc3_of_simple_remove(struct platform_device *pdev) @@ -123,12 +143,15 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; + of_platform_depopulate(dev); + for (i = 0; i < simple->num_clocks; i++) { clk_disable_unprepare(simple->clks[i]); clk_put(simple->clks[i]); } - of_platform_depopulate(dev); + reset_control_assert(simple->resets); + reset_control_put(simple->resets); pm_runtime_put_sync(dev); pm_runtime_disable(dev); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 54343fbd85ee..261ee805763f 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/pci.h> +#include <linux/workqueue.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/gpio/consumer.h> @@ -61,6 +62,7 @@ struct dwc3_pci { guid_t guid; unsigned int has_dsm_for_pm:1; + struct work_struct wakeup_work; }; static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; @@ -174,6 +176,22 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) return 0; } +#ifdef CONFIG_PM +static void dwc3_pci_resume_work(struct work_struct *work) +{ + struct dwc3_pci *dwc = container_of(work, struct dwc3_pci, wakeup_work); + struct platform_device *dwc3 = dwc->dwc3; + int ret; + + ret = pm_runtime_get_sync(&dwc3->dev); + if (ret) + return; + + pm_runtime_mark_last_busy(&dwc3->dev); + pm_runtime_put_sync_autosuspend(&dwc3->dev); +} +#endif + static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) { @@ -232,6 +250,9 @@ static int dwc3_pci_probe(struct pci_dev *pci, device_init_wakeup(dev, true); pci_set_drvdata(pci, dwc); pm_runtime_put(dev); +#ifdef CONFIG_PM + INIT_WORK(&dwc->wakeup_work, dwc3_pci_resume_work); +#endif return 0; err: @@ -243,6 +264,9 @@ static void dwc3_pci_remove(struct pci_dev *pci) { struct dwc3_pci *dwc = pci_get_drvdata(pci); +#ifdef CONFIG_PM + cancel_work_sync(&dwc->wakeup_work); +#endif device_init_wakeup(&pci->dev, false); pm_runtime_get(&pci->dev); platform_device_unregister(dwc->dwc3); @@ -318,14 +342,15 @@ static int dwc3_pci_runtime_suspend(struct device *dev) static int dwc3_pci_runtime_resume(struct device *dev) { struct dwc3_pci *dwc = dev_get_drvdata(dev); - struct platform_device *dwc3 = dwc->dwc3; int ret; ret = dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D0); if (ret) return ret; - return pm_runtime_get(&dwc3->dev); + queue_work(pm_wq, &dwc->wakeup_work); + + return 0; } #endif /* CONFIG_PM */ diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 75e6cb044eb2..a4dceae4bdc7 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -487,14 +487,10 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc, static int dwc3_ep0_handle_intf(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl, int set) { - enum usb_device_state state; u32 wValue; - u32 wIndex; int ret = 0; wValue = le16_to_cpu(ctrl->wValue); - wIndex = le16_to_cpu(ctrl->wIndex); - state = dwc->gadget.state; switch (wValue) { case USB_INTRF_FUNC_SUSPEND: @@ -517,14 +513,10 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl, int set) { struct dwc3_ep *dep; - enum usb_device_state state; u32 wValue; - u32 wIndex; int ret; wValue = le16_to_cpu(ctrl->wValue); - wIndex = le16_to_cpu(ctrl->wIndex); - state = dwc->gadget.state; switch (wValue) { case USB_ENDPOINT_HALT: @@ -551,10 +543,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, { u32 recip; int ret; - enum usb_device_state state; recip = ctrl->bRequestType & USB_RECIP_MASK; - state = dwc->gadget.state; switch (recip) { case USB_RECIP_DEVICE: @@ -712,12 +702,10 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) struct dwc3_ep *dep; enum usb_device_state state = dwc->gadget.state; u16 wLength; - u16 wValue; if (state == USB_STATE_DEFAULT) return -EINVAL; - wValue = le16_to_cpu(ctrl->wValue); wLength = le16_to_cpu(ctrl->wLength); if (wLength != 6) { @@ -842,9 +830,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, struct usb_request *ur; struct dwc3_trb *trb; struct dwc3_ep *ep0; - unsigned maxp; - unsigned remaining_ur_length; - void *buf; u32 transferred = 0; u32 status; u32 length; @@ -871,11 +856,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, } ur = &r->request; - buf = ur->buf; - remaining_ur_length = ur->length; length = trb->size & DWC3_TRB_SIZE_MASK; - maxp = ep0->endpoint.maxpacket; transferred = ur->length - length; ur->actual += transferred; @@ -1001,7 +983,6 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, } else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && req->request.length && req->request.zero) { u32 maxpacket; - u32 rem; ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request, dep->number); @@ -1009,7 +990,6 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, return; maxpacket = dep->endpoint.maxpacket; - rem = req->request.length % maxpacket; /* prepare normal TRB */ dwc3_ep0_prepare_one_trb(dep, req->request.dma, diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f064f1549333..66dc99b9525a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1151,9 +1151,6 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); - if (!dwc3_calc_trbs_left(dep)) - return; - /* * We can get in a situation where there's a request in the started list * but there weren't enough TRBs to fully kick it in the first time @@ -1194,7 +1191,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) } } -static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) +static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; struct dwc3_request *req; @@ -1202,6 +1199,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) int ret; u32 cmd; + if (!dwc3_calc_trbs_left(dep)) + return 0; + starting = !(dep->flags & DWC3_EP_BUSY); dwc3_prepare_trbs(dep); @@ -1216,8 +1216,10 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) if (starting) { params.param0 = upper_32_bits(req->trb_dma); params.param1 = lower_32_bits(req->trb_dma); - cmd = DWC3_DEPCMD_STARTTRANSFER | - DWC3_DEPCMD_PARAM(cmd_param); + cmd = DWC3_DEPCMD_STARTTRANSFER; + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) + cmd |= DWC3_DEPCMD_PARAM(dep->frame_number); } else { cmd = DWC3_DEPCMD_UPDATETRANSFER | DWC3_DEPCMD_PARAM(dep->resource_index); @@ -1258,8 +1260,6 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc) static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, struct dwc3_ep *dep, u32 cur_uf) { - u32 uf; - if (list_empty(&dep->pending_list)) { dev_info(dwc->dev, "%s: ran out of requests\n", dep->name); @@ -1271,9 +1271,8 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, * Schedule the first trb for one interval in the future or at * least 4 microframes. */ - uf = cur_uf + max_t(u32, 4, dep->interval); - - __dwc3_gadget_kick_transfer(dep, uf); + dep->frame_number = cur_uf + max_t(u32, 4, dep->interval); + __dwc3_gadget_kick_transfer(dep); } static void dwc3_gadget_start_isoc(struct dwc3 *dwc, @@ -1290,7 +1289,6 @@ static void dwc3_gadget_start_isoc(struct dwc3 *dwc, static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; - int ret = 0; if (!dep->endpoint.desc) { dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", @@ -1337,24 +1335,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) } if ((dep->flags & DWC3_EP_BUSY) && - !(dep->flags & DWC3_EP_MISSED_ISOC)) { - WARN_ON_ONCE(!dep->resource_index); - ret = __dwc3_gadget_kick_transfer(dep, - dep->resource_index); - } - - goto out; - } + !(dep->flags & DWC3_EP_MISSED_ISOC)) + goto out; - if (!dwc3_calc_trbs_left(dep)) return 0; + } - ret = __dwc3_gadget_kick_transfer(dep, 0); out: - if (ret == -EBUSY) - ret = 0; - - return ret; + return __dwc3_gadget_kick_transfer(dep); } static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, @@ -2347,7 +2335,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, req->request.actual = length - req->remaining; if ((req->request.actual < length) && req->num_pending_sgs) - return __dwc3_gadget_kick_transfer(dep, 0); + return __dwc3_gadget_kick_transfer(dep); dwc3_gadget_giveback(dep, req, status); @@ -2440,13 +2428,8 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, if (!dep->endpoint.desc) return; - if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - int ret; - - ret = __dwc3_gadget_kick_transfer(dep, 0); - if (!ret || ret == -EBUSY) - return; - } + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); } static void dwc3_endpoint_interrupt(struct dwc3 *dwc, @@ -2487,15 +2470,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERNOTREADY: - if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) dwc3_gadget_start_isoc(dwc, dep, event); - } else { - int ret; - - ret = __dwc3_gadget_kick_transfer(dep, 0); - if (!ret || ret == -EBUSY) - return; - } + else + __dwc3_gadget_kick_transfer(dep); break; case DWC3_DEPEVT_STREAMEVT: diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index a82e2bd5ea34..c9d741dfeff4 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1145,6 +1145,7 @@ static int usbg_submit_command(struct f_uas *fu, default: pr_debug_once("Unsupported prio_attr: %02x.\n", cmd_iu->prio_attr); + /* fall through */ case UAS_SIMPLE_TAG: cmd->prio_attr = TCM_SIMPLE_TAG; break; diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 4176216d54be..961457ef5a1c 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1078,6 +1078,7 @@ static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) default: pr_warn("%s: unexpected %s status %d\n", __func__, ep->name, req->status); + /* fall through */ case 0: /* normal completion */ spin_lock(&info->con_lock); diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 3e22b45687d3..66124024278b 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -354,7 +354,7 @@ static unsigned long uvcg_v4l2_get_unmapped_area(struct file *file, } #endif -struct v4l2_file_operations uvc_v4l2_fops = { +const struct v4l2_file_operations uvc_v4l2_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h index 2683b92fda65..ad6ca0671740 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.h +++ b/drivers/usb/gadget/function/uvc_v4l2.h @@ -17,6 +17,6 @@ #define __UVC_V4L2_H__ extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops; -extern struct v4l2_file_operations uvc_v4l2_fops; +extern const struct v4l2_file_operations uvc_v4l2_fops; #endif /* __UVC_V4L2_H__ */ diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index d41d07aae0ce..54b02eca0456 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -912,7 +912,7 @@ int usb_gadget_ep_match_desc(struct usb_gadget *gadget, return 0; type = usb_endpoint_type(desc); - max = 0x7ff & usb_endpoint_maxp(desc); + max = usb_endpoint_maxp(desc); if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) return 0; diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 8f0f69783c8b..664b64eae834 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -23,6 +23,8 @@ * * Having this all in one kernel can help some stages of development, * bypassing some hardware (and driver) issues. UML could help too. + * + * Note: The emulation does not include isochronous transfers! */ #include <linux/module.h> @@ -137,6 +139,9 @@ static const struct { .caps = _caps, \ } +/* we don't provide isochronous endpoints since we don't support them */ +#define TYPE_BULK_OR_INT (USB_EP_CAPS_TYPE_BULK | USB_EP_CAPS_TYPE_INT) + /* everyone has ep0 */ EP_INFO(ep0name, USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)), @@ -145,64 +150,72 @@ static const struct { USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)), EP_INFO("ep2out-bulk", USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)), +/* EP_INFO("ep3in-iso", USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_IN)), EP_INFO("ep4out-iso", USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_OUT)), +*/ EP_INFO("ep5in-int", USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)), EP_INFO("ep6in-bulk", USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)), EP_INFO("ep7out-bulk", USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)), +/* EP_INFO("ep8in-iso", USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_IN)), EP_INFO("ep9out-iso", USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_OUT)), +*/ EP_INFO("ep10in-int", USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)), EP_INFO("ep11in-bulk", USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)), EP_INFO("ep12out-bulk", USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)), +/* EP_INFO("ep13in-iso", USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_IN)), EP_INFO("ep14out-iso", USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, USB_EP_CAPS_DIR_OUT)), +*/ EP_INFO("ep15in-int", USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)), + /* or like sa1100: two fixed function endpoints */ EP_INFO("ep1out-bulk", USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep2in-bulk", USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)), + /* and now some generic EPs so we have enough in multi config */ EP_INFO("ep3out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep4in", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)), EP_INFO("ep5out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep6out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep7in", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)), EP_INFO("ep8out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep9in", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)), EP_INFO("ep10out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep11out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep12in", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)), EP_INFO("ep13out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), EP_INFO("ep14in", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)), EP_INFO("ep15out", - USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), + USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)), #undef EP_INFO }; @@ -1769,6 +1782,7 @@ static void dummy_timer(unsigned long _dum_hcd) int i; /* simplistic model for one frame's bandwidth */ + /* FIXME: account for transaction and packet overhead */ switch (dum->gadget.speed) { case USB_SPEED_LOW: total = 8/*bytes*/ * 12/*packets*/; @@ -1813,7 +1827,6 @@ restart: struct dummy_request *req; u8 address; struct dummy_ep *ep = NULL; - int type; int status = -EINPROGRESS; /* stop when we reach URBs queued after the timer interrupt */ @@ -1825,14 +1838,10 @@ restart: goto return_urb; else if (dum_hcd->rh_state != DUMMY_RH_RUNNING) continue; - type = usb_pipetype(urb->pipe); - /* used up this frame's non-periodic bandwidth? - * FIXME there's infinite bandwidth for control and - * periodic transfers ... unrealistic. - */ - if (total <= 0 && type == PIPE_BULK) - continue; + /* Used up this frame's bandwidth? */ + if (total <= 0) + break; /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); @@ -1930,13 +1939,17 @@ restart: limit = total; switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: - /* FIXME is it urb->interval since the last xfer? - * use urb->iso_frame_desc[i]. - * complete whether or not ep has requests queued. - * report random errors, to debug drivers. + /* + * We don't support isochronous. But if we did, + * here are some of the issues we'd have to face: + * + * Is it urb->interval since the last xfer? + * Use urb->iso_frame_desc[i]. + * Complete whether or not ep has requests queued. + * Report random errors, to debug drivers. */ limit = max(limit, periodic_bytes(dum, ep)); - status = -ENOSYS; + status = -EINVAL; /* fail all xfers */ break; case PIPE_INTERRUPT: diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 8433c22900dc..cccad51eb999 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -127,11 +127,15 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) mode = 0; max = get_unaligned_le16(&desc->wMaxPacketSize); switch (max) { - case 64: mode++; - case 32: mode++; - case 16: mode++; - case 8: mode <<= 3; - break; + case 64: + mode++; /* fall through */ + case 32: + mode++; /* fall through */ + case 16: + mode++; /* fall through */ + case 8: + mode <<= 3; + break; default: return -EINVAL; } diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 1f9941145746..48117a539146 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -1538,7 +1538,7 @@ static int gr_ep_enable(struct usb_ep *_ep, * Bits 10-0 set the max payload. 12-11 set the number of * additional transactions. */ - max = 0x7ff & usb_endpoint_maxp(desc); + max = usb_endpoint_maxp(desc); nt = usb_endpoint_maxp_mult(desc) - 1; buffer_size = GR_BUFFER_SIZE(epctrl); if (nt && (mode == 0 || mode == 2)) { diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 63a206122058..4d9a40f452e1 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -1,7 +1,7 @@ /* * Renesas USB3.0 Peripheral driver (USB gadget) * - * Copyright (C) 2015 Renesas Electronics Corporation + * Copyright (C) 2015-2017 Renesas Electronics Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sizes.h> @@ -334,6 +335,7 @@ struct renesas_usb3 { struct usb_gadget_driver *driver; struct extcon_dev *extcon; struct work_struct extcon_work; + struct phy *phy; struct renesas_usb3_ep *usb3_ep; int num_usb3_eps; @@ -2239,7 +2241,9 @@ static int renesas_usb3_start(struct usb_gadget *gadget, /* hook up the driver */ usb3->driver = driver; - pm_runtime_enable(usb3_to_dev(usb3)); + if (usb3->phy) + phy_init(usb3->phy); + pm_runtime_get_sync(usb3_to_dev(usb3)); renesas_usb3_init_controller(usb3); @@ -2256,8 +2260,10 @@ static int renesas_usb3_stop(struct usb_gadget *gadget) usb3->driver = NULL; renesas_usb3_stop_controller(usb3); + if (usb3->phy) + phy_exit(usb3->phy); + pm_runtime_put(usb3_to_dev(usb3)); - pm_runtime_disable(usb3_to_dev(usb3)); return 0; } @@ -2405,6 +2411,9 @@ static int renesas_usb3_remove(struct platform_device *pdev) renesas_usb3_dma_free_prd(usb3, &pdev->dev); __renesas_usb3_ep_free_request(usb3->ep0_req); + if (usb3->phy) + phy_put(usb3->phy); + pm_runtime_disable(usb3_to_dev(usb3)); return 0; } @@ -2560,20 +2569,15 @@ static int renesas_usb3_probe(struct platform_device *pdev) { struct renesas_usb3 *usb3; struct resource *res; - const struct of_device_id *match; int irq, ret; const struct renesas_usb3_priv *priv; const struct soc_device_attribute *attr; - match = of_match_node(usb3_of_match, pdev->dev.of_node); - if (!match) - return -ENODEV; - attr = soc_device_match(renesas_usb3_quirks_match); if (attr) priv = attr->data; else - priv = match->data; + priv = of_device_get_match_data(&pdev->dev); irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -2635,11 +2639,20 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (ret < 0) goto err_dev_create; + /* + * This is an optional. So, if this driver cannot get a phy, + * this driver will not handle a phy anymore. + */ + usb3->phy = devm_phy_get(&pdev->dev, "usb"); + if (IS_ERR(usb3->phy)) + usb3->phy = NULL; + usb3->workaround_for_vbus = priv->workaround_for_vbus; renesas_usb3_debugfs_init(usb3, &pdev->dev); - dev_info(&pdev->dev, "probed\n"); + dev_info(&pdev->dev, "probed%s\n", usb3->phy ? " with phy" : ""); + pm_runtime_enable(usb3_to_dev(usb3)); return 0; @@ -2655,11 +2668,49 @@ err_alloc_prd: return ret; } +#ifdef CONFIG_PM_SLEEP +static int renesas_usb3_suspend(struct device *dev) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + + /* Not started */ + if (!usb3->driver) + return 0; + + renesas_usb3_stop_controller(usb3); + if (usb3->phy) + phy_exit(usb3->phy); + pm_runtime_put(dev); + + return 0; +} + +static int renesas_usb3_resume(struct device *dev) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + + /* Not started */ + if (!usb3->driver) + return 0; + + if (usb3->phy) + phy_init(usb3->phy); + pm_runtime_get_sync(dev); + renesas_usb3_init_controller(usb3); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, + renesas_usb3_resume); + static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, .remove = renesas_usb3_remove, .driver = { .name = (char *)udc_name, + .pm = &renesas_usb3_pm_ops, .of_match_table = of_match_ptr(usb3_of_match), }, }; diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index b26fffc58446..d80e4e813248 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -46,6 +46,9 @@ struct mtu3_request; #define MU3D_EP_RXCR1(epnum) (U3D_RX1CSR1 + (((epnum) - 1) * 0x10)) #define MU3D_EP_RXCR2(epnum) (U3D_RX1CSR2 + (((epnum) - 1) * 0x10)) +#define USB_QMU_TQHIAR(epnum) (U3D_TXQHIAR1 + (((epnum) - 1) * 0x4)) +#define USB_QMU_RQHIAR(epnum) (U3D_RXQHIAR1 + (((epnum) - 1) * 0x4)) + #define USB_QMU_RQCSR(epnum) (U3D_RXQCSR1 + (((epnum) - 1) * 0x10)) #define USB_QMU_RQSAR(epnum) (U3D_RXQSAR1 + (((epnum) - 1) * 0x10)) #define USB_QMU_RQCPR(epnum) (U3D_RXQCPR1 + (((epnum) - 1) * 0x10)) @@ -91,6 +94,7 @@ enum mtu3_speed { MTU3_SPEED_FULL = 1, MTU3_SPEED_HIGH = 3, MTU3_SPEED_SUPER = 4, + MTU3_SPEED_SUPER_PLUS = 5, }; /** @@ -112,6 +116,19 @@ enum mtu3_g_ep0_state { }; /** + * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode + * by IDPIN signal. + * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG + * IDPIN signal. + * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. + */ +enum mtu3_dr_force_mode { + MTU3_DR_FORCE_NONE = 0, + MTU3_DR_FORCE_HOST, + MTU3_DR_FORCE_DEVICE, +}; + +/** * @base: the base address of fifo * @limit: the bitmap size in bits * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT @@ -138,23 +155,33 @@ struct mtu3_fifo_info { * Checksum value is calculated over the 16 bytes of the GPD by default; * @data_buf_len (RX ONLY): This value indicates the length of * the assigned data buffer + * @tx_ext_addr (TX ONLY): [3:0] are 4 extension bits of @buffer, + * [7:4] are 4 extension bits of @next_gpd * @next_gpd: Physical address of the next GPD * @buffer: Physical address of the data buffer * @buf_len: * (TX): This value indicates the length of the assigned data buffer * (RX): The total length of data received * @ext_len: reserved + * @rx_ext_addr(RX ONLY): [3:0] are 4 extension bits of @buffer, + * [7:4] are 4 extension bits of @next_gpd * @ext_flag: * bit5 (TX ONLY): Zero Length Packet (ZLP), */ struct qmu_gpd { __u8 flag; __u8 chksum; - __le16 data_buf_len; + union { + __le16 data_buf_len; + __le16 tx_ext_addr; + }; __le32 next_gpd; __le32 buffer; __le16 buf_len; - __u8 ext_len; + union { + __u8 ext_len; + __u8 rx_ext_addr; + }; __u8 ext_flag; } __packed; @@ -183,7 +210,6 @@ struct mtu3_gpd_ring { * xHCI driver initialization, it's necessary for system bootup * as device. * @is_u3_drd: whether port0 supports usb3.0 dual-role device or not -* @id_*: used to maually switch between host and device modes by idpin * @manual_drd_enabled: it's true when supports dual-role device by debugfs * to switch host/device modes depending on user input. */ @@ -194,10 +220,6 @@ struct otg_switch_mtk { struct notifier_block id_nb; struct delayed_work extcon_reg_dwork; bool is_u3_drd; - /* dual-role switch by debugfs */ - struct pinctrl *id_pinctrl; - struct pinctrl_state *id_float; - struct pinctrl_state *id_ground; bool manual_drd_enabled; }; @@ -206,14 +228,17 @@ struct otg_switch_mtk { * @ippc_base: register base address of IP Power and Clock interface (IPPC) * @vusb33: usb3.3V shared by device/host IP * @sys_clk: system clock of mtu3, shared by device/host IP + * @ref_clk: reference clock + * @mcu_clk: mcu_bus_ck clock for AHB bus etc + * @dma_clk: dma_bus_ck clock for AXI bus etc * @dr_mode: works in which mode: * host only, device only or dual-role mode * @u2_ports: number of usb2.0 host ports * @u3_ports: number of usb3.0 host ports + * @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to + * disable u3port0, bit1==1 to disable u3port1,... etc * @dbgfs_root: only used when supports manual dual-role switch via debugfs * @wakeup_en: it's true when supports remote wakeup in host mode - * @wk_deb_p0: port0's wakeup debounce clock - * @wk_deb_p1: it's optional, and depends on port1 is supported or not */ struct ssusb_mtk { struct device *dev; @@ -226,17 +251,18 @@ struct ssusb_mtk { struct regulator *vusb33; struct clk *sys_clk; struct clk *ref_clk; + struct clk *mcu_clk; + struct clk *dma_clk; /* otg */ struct otg_switch_mtk otg_switch; enum usb_dr_mode dr_mode; bool is_host; int u2_ports; int u3_ports; + int u3p_dis_msk; struct dentry *dbgfs_root; /* usb wakeup for host mode */ bool wakeup_en; - struct clk *wk_deb_p0; - struct clk *wk_deb_p1; struct regmap *pericfg; }; diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 99c65b0788ff..7c149a7da14e 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -17,6 +17,7 @@ * */ +#include <linux/dma-mapping.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_address.h> @@ -114,7 +115,9 @@ static int mtu3_device_enable(struct mtu3 *mtu) mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), (SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_HOST_SEL)); - mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); + + if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) + mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); return ssusb_check_clocks(mtu->ssusb, check_clk); } @@ -129,7 +132,10 @@ static void mtu3_device_disable(struct mtu3 *mtu) mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN); - mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); + + if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) + mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); + mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } @@ -236,7 +242,7 @@ void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set) void mtu3_dev_on_off(struct mtu3 *mtu, int is_on) { - if (mtu->is_u3_ip && (mtu->max_speed == USB_SPEED_SUPER)) + if (mtu->is_u3_ip && mtu->max_speed >= USB_SPEED_SUPER) mtu3_ss_func_set(mtu, is_on); else mtu3_hs_softconn_set(mtu, is_on); @@ -546,6 +552,9 @@ static void mtu3_set_speed(struct mtu3 *mtu) mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN); /* HS/FS detected by HW */ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); + } else if (mtu->max_speed == USB_SPEED_SUPER) { + mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0), + SSUSB_U3_PORT_SSP_SPEED); } dev_info(mtu->dev, "max_speed: %s\n", @@ -623,6 +632,10 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) udev_speed = USB_SPEED_SUPER; maxpkt = 512; break; + case MTU3_SPEED_SUPER_PLUS: + udev_speed = USB_SPEED_SUPER_PLUS; + maxpkt = 512; + break; default: udev_speed = USB_SPEED_UNKNOWN; break; @@ -759,7 +772,31 @@ static void mtu3_hw_exit(struct mtu3 *mtu) mtu3_mem_free(mtu); } -/*-------------------------------------------------------------------------*/ +/** + * we set 32-bit DMA mask by default, here check whether the controller + * supports 36-bit DMA or not, if it does, set 36-bit DMA mask. + */ +static int mtu3_set_dma_mask(struct mtu3 *mtu) +{ + struct device *dev = mtu->dev; + bool is_36bit = false; + int ret = 0; + u32 value; + + value = mtu3_readl(mtu->mac_base, U3D_MISC_CTRL); + if (value & DMA_ADDR_36BIT) { + is_36bit = true; + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + /* If set 36-bit DMA mask fails, fall back to 32-bit DMA mask */ + if (ret) { + is_36bit = false; + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + } + } + dev_info(dev, "dma mask: %s bits\n", is_36bit ? "36" : "32"); + + return ret; +} int ssusb_gadget_init(struct ssusb_mtk *ssusb) { @@ -774,9 +811,9 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) return -ENOMEM; mtu->irq = platform_get_irq(pdev, 0); - if (mtu->irq <= 0) { + if (mtu->irq < 0) { dev_err(dev, "fail to get irq number\n"); - return -ENODEV; + return mtu->irq; } dev_info(dev, "irq %d\n", mtu->irq); @@ -800,14 +837,15 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) case USB_SPEED_FULL: case USB_SPEED_HIGH: case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: break; default: dev_err(dev, "invalid max_speed: %s\n", usb_speed_string(mtu->max_speed)); /* fall through */ case USB_SPEED_UNKNOWN: - /* default as SS */ - mtu->max_speed = USB_SPEED_SUPER; + /* default as SSP */ + mtu->max_speed = USB_SPEED_SUPER_PLUS; break; } @@ -820,6 +858,12 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) return ret; } + ret = mtu3_set_dma_mask(mtu); + if (ret) { + dev_err(dev, "mtu3 set dma_mask failed:%d\n", ret); + goto dma_mask_err; + } + ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu); if (ret) { dev_err(dev, "request irq %d failed!\n", mtu->irq); @@ -845,6 +889,7 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) gadget_err: device_init_wakeup(dev, false); +dma_mask_err: irq_err: mtu3_hw_exit(mtu); ssusb->u3d = NULL; diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index 560256115b23..ec442cd5a1ad 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -261,21 +261,22 @@ static void extcon_register_dwork(struct work_struct *work) * depending on user input. * This is useful in special cases, such as uses TYPE-A receptacle but also * wants to support dual-role mode. - * It generates cable state changes by pulling up/down IDPIN and - * notifies driver to switch mode by "extcon-usb-gpio". - * NOTE: when use MICRO receptacle, should not enable this interface. */ static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - if (to_host) - pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground); - else - pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float); + if (to_host) { + ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); + ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF); + ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND); + } else { + ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE); + ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT); + ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID); + } } - static int ssusb_mode_show(struct seq_file *sf, void *unused) { struct ssusb_mtk *ssusb = sf->private; @@ -388,17 +389,45 @@ static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb) debugfs_remove_recursive(ssusb->dbgfs_root); } +void ssusb_set_force_mode(struct ssusb_mtk *ssusb, + enum mtu3_dr_force_mode mode) +{ + u32 value; + + value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0)); + switch (mode) { + case MTU3_DR_FORCE_DEVICE: + value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG; + break; + case MTU3_DR_FORCE_HOST: + value |= SSUSB_U2_PORT_FORCE_IDDIG; + value &= ~SSUSB_U2_PORT_RG_IDDIG; + break; + case MTU3_DR_FORCE_NONE: + value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG); + break; + default: + return; + } + mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value); +} + int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork); - - if (otg_sx->manual_drd_enabled) + if (otg_sx->manual_drd_enabled) { ssusb_debugfs_init(ssusb); - - /* It is enough to delay 1s for waiting for host initialization */ - schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ); + } else { + INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, + extcon_register_dwork); + + /* + * It is enough to delay 1s for waiting for + * host initialization + */ + schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ); + } return 0; } @@ -407,8 +436,8 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - cancel_delayed_work(&otg_sx->extcon_reg_dwork); - if (otg_sx->manual_drd_enabled) ssusb_debugfs_exit(ssusb); + else + cancel_delayed_work(&otg_sx->extcon_reg_dwork); } diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index 9b228b5811b0..0f0cbac00192 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -87,6 +87,8 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb) int ssusb_otg_switch_init(struct ssusb_mtk *ssusb); void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb); int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on); +void ssusb_set_force_mode(struct ssusb_mtk *ssusb, + enum mtu3_dr_force_mode mode); #else @@ -103,6 +105,10 @@ static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) return 0; } +static inline void +ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode) +{} + #endif #endif /* _MTU3_DR_H_ */ diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index 434fca58143c..b495471f689f 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -89,6 +89,7 @@ static int mtu3_ep_enable(struct mtu3_ep *mep) switch (mtu->g.speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) { interval = desc->bInterval; @@ -456,7 +457,7 @@ static int mtu3_gadget_wakeup(struct usb_gadget *gadget) return -EOPNOTSUPP; spin_lock_irqsave(&mtu->lock, flags); - if (mtu->g.speed == USB_SPEED_SUPER) { + if (mtu->g.speed >= USB_SPEED_SUPER) { mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT); } else { mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME); diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 958d74dd2b78..020b25314a68 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -212,8 +212,8 @@ ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup) case USB_RECIP_DEVICE: result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED; result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; - /* superspeed only */ - if (mtu->g.speed == USB_SPEED_SUPER) { + + if (mtu->g.speed >= USB_SPEED_SUPER) { result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED; result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED; } @@ -329,8 +329,8 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu, handled = handle_test_mode(mtu, setup); break; case USB_DEVICE_U1_ENABLE: - if (mtu->g.speed != USB_SPEED_SUPER || - mtu->g.state != USB_STATE_CONFIGURED) + if (mtu->g.speed < USB_SPEED_SUPER || + mtu->g.state != USB_STATE_CONFIGURED) break; lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); @@ -344,8 +344,8 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu, handled = 1; break; case USB_DEVICE_U2_ENABLE: - if (mtu->g.speed != USB_SPEED_SUPER || - mtu->g.state != USB_STATE_CONFIGURED) + if (mtu->g.speed < USB_SPEED_SUPER || + mtu->g.state != USB_STATE_CONFIGURED) break; lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); @@ -384,8 +384,8 @@ static int ep0_handle_feature(struct mtu3 *mtu, break; case USB_RECIP_INTERFACE: /* superspeed only */ - if ((value == USB_INTRF_FUNC_SUSPEND) - && (mtu->g.speed == USB_SPEED_SUPER)) { + if (value == USB_INTRF_FUNC_SUSPEND && + mtu->g.speed >= USB_SPEED_SUPER) { /* * forward the request because function drivers * should handle it diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index e42d308b8dc2..ec76b86dd887 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -79,20 +79,6 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, if (!ssusb->wakeup_en) return 0; - ssusb->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0"); - if (IS_ERR(ssusb->wk_deb_p0)) { - dev_err(dev, "fail to get wakeup_deb_p0\n"); - return PTR_ERR(ssusb->wk_deb_p0); - } - - if (of_property_read_bool(dn, "wakeup_deb_p1")) { - ssusb->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1"); - if (IS_ERR(ssusb->wk_deb_p1)) { - dev_err(dev, "fail to get wakeup_deb_p1\n"); - return PTR_ERR(ssusb->wk_deb_p1); - } - } - ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn, "mediatek,syscon-wakeup"); if (IS_ERR(ssusb->pericfg)) { @@ -103,36 +89,6 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, return 0; } -static int ssusb_wakeup_clks_enable(struct ssusb_mtk *ssusb) -{ - int ret; - - ret = clk_prepare_enable(ssusb->wk_deb_p0); - if (ret) { - dev_err(ssusb->dev, "failed to enable wk_deb_p0\n"); - goto usb_p0_err; - } - - ret = clk_prepare_enable(ssusb->wk_deb_p1); - if (ret) { - dev_err(ssusb->dev, "failed to enable wk_deb_p1\n"); - goto usb_p1_err; - } - - return 0; - -usb_p1_err: - clk_disable_unprepare(ssusb->wk_deb_p0); -usb_p0_err: - return -EINVAL; -} - -static void ssusb_wakeup_clks_disable(struct ssusb_mtk *ssusb) -{ - clk_disable_unprepare(ssusb->wk_deb_p1); - clk_disable_unprepare(ssusb->wk_deb_p0); -} - static void host_ports_num_get(struct ssusb_mtk *ssusb) { u32 xhci_cap; @@ -151,6 +107,7 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) void __iomem *ibase = ssusb->ippc_base; int num_u3p = ssusb->u3_ports; int num_u2p = ssusb->u2_ports; + int u3_ports_disabed; u32 check_clk; u32 value; int i; @@ -158,8 +115,14 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) /* power on host ip */ mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); - /* power on and enable all u3 ports */ + /* power on and enable u3 ports except skipped ones */ + u3_ports_disabed = 0; for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & ssusb->u3p_dis_msk) { + u3_ports_disabed++; + continue; + } + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS); value |= SSUSB_U3_PORT_HOST_SEL; @@ -175,7 +138,7 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) } check_clk = SSUSB_XHCI_RST_B_STS; - if (num_u3p) + if (num_u3p > u3_ports_disabed) check_clk = SSUSB_U3_MAC_RST_B_STS; return ssusb_check_clocks(ssusb, check_clk); @@ -190,8 +153,11 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) int ret; int i; - /* power down and disable all u3 ports */ + /* power down and disable u3 ports except skipped ones */ for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & ssusb->u3p_dis_msk) + continue; + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); value |= SSUSB_U3_PORT_PDN; value |= suspend ? 0 : SSUSB_U3_PORT_DIS; @@ -223,6 +189,8 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) static void ssusb_host_setup(struct ssusb_mtk *ssusb) { + struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + host_ports_num_get(ssusb); /* @@ -231,6 +199,9 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb) */ ssusb_host_enable(ssusb); + if (otg_sx->manual_drd_enabled) + ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); + /* if port0 supports dual-role, works as host mode by default */ ssusb_set_vbus(&ssusb->otg_switch, 1); } @@ -276,19 +247,14 @@ void ssusb_host_exit(struct ssusb_mtk *ssusb) int ssusb_wakeup_enable(struct ssusb_mtk *ssusb) { - int ret = 0; - - if (ssusb->wakeup_en) { - ret = ssusb_wakeup_clks_enable(ssusb); + if (ssusb->wakeup_en) ssusb_wakeup_ip_sleep_en(ssusb); - } - return ret; + + return 0; } void ssusb_wakeup_disable(struct ssusb_mtk *ssusb) { - if (ssusb->wakeup_en) { + if (ssusb->wakeup_en) ssusb_wakeup_ip_sleep_dis(ssusb); - ssusb_wakeup_clks_disable(ssusb); - } } diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index 06b29664470f..6953436a1688 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -58,6 +58,8 @@ #define U3D_QCR1 (SSUSB_DEV_BASE + 0x0404) #define U3D_QCR2 (SSUSB_DEV_BASE + 0x0408) #define U3D_QCR3 (SSUSB_DEV_BASE + 0x040C) +#define U3D_TXQHIAR1 (SSUSB_DEV_BASE + 0x0484) +#define U3D_RXQHIAR1 (SSUSB_DEV_BASE + 0x04C4) #define U3D_TXQCSR1 (SSUSB_DEV_BASE + 0x0510) #define U3D_TXQSAR1 (SSUSB_DEV_BASE + 0x0514) @@ -189,6 +191,13 @@ #define QMU_RX_COZ(x) (BIT(16) << (x)) #define QMU_RX_ZLP(x) (BIT(0) << (x)) +/* U3D_TXQHIAR1 */ +/* U3D_RXQHIAR1 */ +#define QMU_LAST_DONE_PTR_HI(x) (((x) >> 16) & 0xf) +#define QMU_CUR_GPD_ADDR_HI(x) (((x) >> 8) & 0xf) +#define QMU_START_ADDR_HI_MSK GENMASK(3, 0) +#define QMU_START_ADDR_HI(x) (((x) & 0xf) << 0) + /* U3D_TXQCSR1 */ /* U3D_RXQCSR1 */ #define QMU_Q_ACTIVE BIT(15) @@ -225,6 +234,7 @@ #define CAP_TX_EP_NUM(x) ((x) & 0x1f) /* U3D_MISC_CTRL */ +#define DMA_ADDR_36BIT BIT(31) #define VBUS_ON BIT(1) #define VBUS_FRC_EN BIT(0) @@ -457,11 +467,14 @@ #define SSUSB_VBUS_CHG_INT_B_EN BIT(6) /* U3D_SSUSB_U3_CTRL_0P */ +#define SSUSB_U3_PORT_SSP_SPEED BIT(9) #define SSUSB_U3_PORT_HOST_SEL BIT(2) #define SSUSB_U3_PORT_PDN BIT(1) #define SSUSB_U3_PORT_DIS BIT(0) /* U3D_SSUSB_U2_CTRL_0P */ +#define SSUSB_U2_PORT_RG_IDDIG BIT(12) +#define SSUSB_U2_PORT_FORCE_IDDIG BIT(11) #define SSUSB_U2_PORT_VBUSVALID BIT(9) #define SSUSB_U2_PORT_OTG_SEL BIT(7) #define SSUSB_U2_PORT_HOST BIT(2) diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 088e3e685c4f..9ff33579b42e 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -21,7 +21,6 @@ #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include "mtu3.h" @@ -110,15 +109,9 @@ static void ssusb_phy_power_off(struct ssusb_mtk *ssusb) phy_power_off(ssusb->phys[i]); } -static int ssusb_rscs_init(struct ssusb_mtk *ssusb) +static int ssusb_clks_enable(struct ssusb_mtk *ssusb) { - int ret = 0; - - ret = regulator_enable(ssusb->vusb33); - if (ret) { - dev_err(ssusb->dev, "failed to enable vusb33\n"); - goto vusb33_err; - } + int ret; ret = clk_prepare_enable(ssusb->sys_clk); if (ret) { @@ -132,6 +125,52 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb) goto ref_clk_err; } + ret = clk_prepare_enable(ssusb->mcu_clk); + if (ret) { + dev_err(ssusb->dev, "failed to enable mcu_clk\n"); + goto mcu_clk_err; + } + + ret = clk_prepare_enable(ssusb->dma_clk); + if (ret) { + dev_err(ssusb->dev, "failed to enable dma_clk\n"); + goto dma_clk_err; + } + + return 0; + +dma_clk_err: + clk_disable_unprepare(ssusb->mcu_clk); +mcu_clk_err: + clk_disable_unprepare(ssusb->ref_clk); +ref_clk_err: + clk_disable_unprepare(ssusb->sys_clk); +sys_clk_err: + return ret; +} + +static void ssusb_clks_disable(struct ssusb_mtk *ssusb) +{ + clk_disable_unprepare(ssusb->dma_clk); + clk_disable_unprepare(ssusb->mcu_clk); + clk_disable_unprepare(ssusb->ref_clk); + clk_disable_unprepare(ssusb->sys_clk); +} + +static int ssusb_rscs_init(struct ssusb_mtk *ssusb) +{ + int ret = 0; + + ret = regulator_enable(ssusb->vusb33); + if (ret) { + dev_err(ssusb->dev, "failed to enable vusb33\n"); + goto vusb33_err; + } + + ret = ssusb_clks_enable(ssusb); + if (ret) + goto clks_err; + ret = ssusb_phy_init(ssusb); if (ret) { dev_err(ssusb->dev, "failed to init phy\n"); @@ -149,20 +188,16 @@ static int ssusb_rscs_init(struct ssusb_mtk *ssusb) phy_err: ssusb_phy_exit(ssusb); phy_init_err: - clk_disable_unprepare(ssusb->ref_clk); -ref_clk_err: - clk_disable_unprepare(ssusb->sys_clk); -sys_clk_err: + ssusb_clks_disable(ssusb); +clks_err: regulator_disable(ssusb->vusb33); vusb33_err: - return ret; } static void ssusb_rscs_exit(struct ssusb_mtk *ssusb) { - clk_disable_unprepare(ssusb->sys_clk); - clk_disable_unprepare(ssusb->ref_clk); + ssusb_clks_disable(ssusb); regulator_disable(ssusb->vusb33); ssusb_phy_power_off(ssusb); ssusb_phy_exit(ssusb); @@ -176,31 +211,17 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); } -static int get_iddig_pinctrl(struct ssusb_mtk *ssusb) +/* ignore the error if the clock does not exist */ +static struct clk *get_optional_clk(struct device *dev, const char *id) { - struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + struct clk *opt_clk; - otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev); - if (IS_ERR(otg_sx->id_pinctrl)) { - dev_err(ssusb->dev, "Cannot find id pinctrl!\n"); - return PTR_ERR(otg_sx->id_pinctrl); - } + opt_clk = devm_clk_get(dev, id); + /* ignore error number except EPROBE_DEFER */ + if (IS_ERR(opt_clk) && (PTR_ERR(opt_clk) != -EPROBE_DEFER)) + opt_clk = NULL; - otg_sx->id_float = - pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float"); - if (IS_ERR(otg_sx->id_float)) { - dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n"); - return PTR_ERR(otg_sx->id_float); - } - - otg_sx->id_ground = - pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground"); - if (IS_ERR(otg_sx->id_ground)) { - dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n"); - return PTR_ERR(otg_sx->id_ground); - } - - return 0; + return opt_clk; } static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) @@ -225,18 +246,17 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) return PTR_ERR(ssusb->sys_clk); } - /* - * reference clock is usually a "fixed-clock", make it optional - * for backward compatibility and ignore the error if it does - * not exist. - */ - ssusb->ref_clk = devm_clk_get(dev, "ref_ck"); - if (IS_ERR(ssusb->ref_clk)) { - if (PTR_ERR(ssusb->ref_clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + ssusb->ref_clk = get_optional_clk(dev, "ref_ck"); + if (IS_ERR(ssusb->ref_clk)) + return PTR_ERR(ssusb->ref_clk); - ssusb->ref_clk = NULL; - } + ssusb->mcu_clk = get_optional_clk(dev, "mcu_ck"); + if (IS_ERR(ssusb->mcu_clk)) + return PTR_ERR(ssusb->mcu_clk); + + ssusb->dma_clk = get_optional_clk(dev, "dma_ck"); + if (IS_ERR(ssusb->dma_clk)) + return PTR_ERR(ssusb->dma_clk); ssusb->num_phys = of_count_phandle_with_args(node, "phys", "#phy-cells"); @@ -263,10 +283,8 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) return PTR_ERR(ssusb->ippc_base); ssusb->dr_mode = usb_get_dr_mode(dev); - if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) { - dev_err(dev, "dr_mode is error\n"); - return -EINVAL; - } + if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) + ssusb->dr_mode = USB_DR_MODE_OTG; if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL) return 0; @@ -276,10 +294,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) if (ret) return ret; - if (ssusb->dr_mode != USB_DR_MODE_OTG) - return 0; + /* optional property, ignore the error if it does not exist */ + of_property_read_u32(node, "mediatek,u3p-dis-msk", + &ssusb->u3p_dis_msk); - /* if dual-role mode is supported */ vbus = devm_regulator_get(&pdev->dev, "vbus"); if (IS_ERR(vbus)) { dev_err(dev, "failed to get vbus\n"); @@ -287,6 +305,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } otg_sx->vbus = vbus; + if (ssusb->dr_mode == USB_DR_MODE_HOST) + return 0; + + /* if dual-role mode is supported */ otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd"); otg_sx->manual_drd_enabled = of_property_read_bool(node, "enable-manual-drd"); @@ -297,15 +319,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) dev_err(ssusb->dev, "couldn't get extcon device\n"); return -EPROBE_DEFER; } - if (otg_sx->manual_drd_enabled) { - ret = get_iddig_pinctrl(ssusb); - if (ret) - return ret; - } } - dev_info(dev, "dr_mode: %d, is_u3_dr: %d\n", - ssusb->dr_mode, otg_sx->is_u3_drd); + dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n", + ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk, + otg_sx->manual_drd_enabled ? "manual" : "auto"); return 0; } @@ -447,8 +465,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev) ssusb_host_disable(ssusb, true); ssusb_phy_power_off(ssusb); - clk_disable_unprepare(ssusb->sys_clk); - clk_disable_unprepare(ssusb->ref_clk); + ssusb_clks_disable(ssusb); ssusb_wakeup_enable(ssusb); return 0; @@ -466,27 +483,21 @@ static int __maybe_unused mtu3_resume(struct device *dev) return 0; ssusb_wakeup_disable(ssusb); - ret = clk_prepare_enable(ssusb->sys_clk); - if (ret) - goto err_sys_clk; - - ret = clk_prepare_enable(ssusb->ref_clk); + ret = ssusb_clks_enable(ssusb); if (ret) - goto err_ref_clk; + goto clks_err; ret = ssusb_phy_power_on(ssusb); if (ret) - goto err_power_on; + goto phy_err; ssusb_host_enable(ssusb); return 0; -err_power_on: - clk_disable_unprepare(ssusb->ref_clk); -err_ref_clk: - clk_disable_unprepare(ssusb->sys_clk); -err_sys_clk: +phy_err: + ssusb_clks_disable(ssusb); +clks_err: return ret; } diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 7d9ba8a52368..42145a3f1422 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -40,7 +40,58 @@ #define GPD_FLAGS_IOC BIT(7) #define GPD_EXT_FLAG_ZLP BIT(5) +#define GPD_EXT_NGP(x) (((x) & 0xf) << 4) +#define GPD_EXT_BUF(x) (((x) & 0xf) << 0) +#define HILO_GEN64(hi, lo) (((u64)(hi) << 32) + (lo)) +#define HILO_DMA(hi, lo) \ + ((dma_addr_t)HILO_GEN64((le32_to_cpu(hi)), (le32_to_cpu(lo)))) + +static dma_addr_t read_txq_cur_addr(void __iomem *mbase, u8 epnum) +{ + u32 txcpr; + u32 txhiar; + + txcpr = mtu3_readl(mbase, USB_QMU_TQCPR(epnum)); + txhiar = mtu3_readl(mbase, USB_QMU_TQHIAR(epnum)); + + return HILO_DMA(QMU_CUR_GPD_ADDR_HI(txhiar), txcpr); +} + +static dma_addr_t read_rxq_cur_addr(void __iomem *mbase, u8 epnum) +{ + u32 rxcpr; + u32 rxhiar; + + rxcpr = mtu3_readl(mbase, USB_QMU_RQCPR(epnum)); + rxhiar = mtu3_readl(mbase, USB_QMU_RQHIAR(epnum)); + + return HILO_DMA(QMU_CUR_GPD_ADDR_HI(rxhiar), rxcpr); +} + +static void write_txq_start_addr(void __iomem *mbase, u8 epnum, dma_addr_t dma) +{ + u32 tqhiar; + + mtu3_writel(mbase, USB_QMU_TQSAR(epnum), + cpu_to_le32(lower_32_bits(dma))); + tqhiar = mtu3_readl(mbase, USB_QMU_TQHIAR(epnum)); + tqhiar &= ~QMU_START_ADDR_HI_MSK; + tqhiar |= QMU_START_ADDR_HI(upper_32_bits(dma)); + mtu3_writel(mbase, USB_QMU_TQHIAR(epnum), tqhiar); +} + +static void write_rxq_start_addr(void __iomem *mbase, u8 epnum, dma_addr_t dma) +{ + u32 rqhiar; + + mtu3_writel(mbase, USB_QMU_RQSAR(epnum), + cpu_to_le32(lower_32_bits(dma))); + rqhiar = mtu3_readl(mbase, USB_QMU_RQHIAR(epnum)); + rqhiar &= ~QMU_START_ADDR_HI_MSK; + rqhiar |= QMU_START_ADDR_HI(upper_32_bits(dma)); + mtu3_writel(mbase, USB_QMU_RQHIAR(epnum), rqhiar); +} static struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring, dma_addr_t dma_addr) @@ -193,21 +244,27 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) struct mtu3_gpd_ring *ring = &mep->gpd_ring; struct qmu_gpd *gpd = ring->enqueue; struct usb_request *req = &mreq->request; + dma_addr_t enq_dma; + u16 ext_addr; /* set all fields to zero as default value */ memset(gpd, 0, sizeof(*gpd)); - gpd->buffer = cpu_to_le32((u32)req->dma); + gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); + ext_addr = GPD_EXT_BUF(upper_32_bits(req->dma)); gpd->buf_len = cpu_to_le16(req->length); gpd->flag |= GPD_FLAGS_IOC; /* get the next GPD */ enq = advance_enq_gpd(ring); - dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p\n", - mep->epnum, gpd, enq); + enq_dma = gpd_virt_to_dma(ring, enq); + dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", + mep->epnum, gpd, enq, enq_dma); enq->flag &= ~GPD_FLAGS_HWO; - gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq)); + gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); + ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); + gpd->tx_ext_addr = cpu_to_le16(ext_addr); if (req->zero) gpd->ext_flag |= GPD_EXT_FLAG_ZLP; @@ -226,21 +283,27 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) struct mtu3_gpd_ring *ring = &mep->gpd_ring; struct qmu_gpd *gpd = ring->enqueue; struct usb_request *req = &mreq->request; + dma_addr_t enq_dma; + u16 ext_addr; /* set all fields to zero as default value */ memset(gpd, 0, sizeof(*gpd)); - gpd->buffer = cpu_to_le32((u32)req->dma); + gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); + ext_addr = GPD_EXT_BUF(upper_32_bits(req->dma)); gpd->data_buf_len = cpu_to_le16(req->length); gpd->flag |= GPD_FLAGS_IOC; /* get the next GPD */ enq = advance_enq_gpd(ring); - dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p\n", - mep->epnum, gpd, enq); + enq_dma = gpd_virt_to_dma(ring, enq); + dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", + mep->epnum, gpd, enq, enq_dma); enq->flag &= ~GPD_FLAGS_HWO; - gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq)); + gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); + ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); + gpd->rx_ext_addr = cpu_to_le16(ext_addr); gpd->chksum = qmu_calc_checksum((u8 *)gpd); gpd->flag |= GPD_FLAGS_HWO; @@ -267,8 +330,8 @@ int mtu3_qmu_start(struct mtu3_ep *mep) if (mep->is_in) { /* set QMU start address */ - mtu3_writel(mbase, USB_QMU_TQSAR(mep->epnum), ring->dma); - mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); + write_txq_start_addr(mbase, epnum, ring->dma); + mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_DMAREQEN); mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum)); /* send zero length packet according to ZLP flag in GPD */ mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum)); @@ -282,8 +345,8 @@ int mtu3_qmu_start(struct mtu3_ep *mep) mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START); } else { - mtu3_writel(mbase, USB_QMU_RQSAR(mep->epnum), ring->dma); - mtu3_setbits(mbase, MU3D_EP_RXCR0(mep->epnum), RX_DMAREQEN); + write_rxq_start_addr(mbase, epnum, ring->dma); + mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN); mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum)); /* don't expect ZLP */ mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum)); @@ -353,9 +416,9 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) struct mtu3_gpd_ring *ring = &mep->gpd_ring; void __iomem *mbase = mtu->mac_base; struct qmu_gpd *gpd_current = NULL; - dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum)); struct usb_request *req = NULL; struct mtu3_request *mreq; + dma_addr_t cur_gpd_dma; u32 txcsr = 0; int ret; @@ -365,7 +428,8 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) else return; - gpd_current = gpd_dma_to_virt(ring, gpd_dma); + cur_gpd_dma = read_txq_cur_addr(mbase, epnum); + gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); if (le16_to_cpu(gpd_current->buf_len) != 0) { dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum); @@ -408,12 +472,13 @@ static void qmu_done_tx(struct mtu3 *mtu, u8 epnum) void __iomem *mbase = mtu->mac_base; struct qmu_gpd *gpd = ring->dequeue; struct qmu_gpd *gpd_current = NULL; - dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum)); struct usb_request *request = NULL; struct mtu3_request *mreq; + dma_addr_t cur_gpd_dma; /*transfer phy address got from QMU register to virtual address */ - gpd_current = gpd_dma_to_virt(ring, gpd_dma); + cur_gpd_dma = read_txq_cur_addr(mbase, epnum); + gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", __func__, epnum, gpd, gpd_current, ring->enqueue); @@ -446,11 +511,12 @@ static void qmu_done_rx(struct mtu3 *mtu, u8 epnum) void __iomem *mbase = mtu->mac_base; struct qmu_gpd *gpd = ring->dequeue; struct qmu_gpd *gpd_current = NULL; - dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_RQCPR(epnum)); struct usb_request *req = NULL; struct mtu3_request *mreq; + dma_addr_t cur_gpd_dma; - gpd_current = gpd_dma_to_virt(ring, gpd_dma); + cur_gpd_dma = read_rxq_cur_addr(mbase, epnum); + gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", __func__, epnum, gpd, gpd_current, ring->enqueue); diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 89d6e7a5fdb7..1cc02eb3fc65 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -224,7 +224,7 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, int err = 0; u32 clk_rate = 0; - bool needs_vcc = false; + bool needs_vcc = false, needs_clk = false; if (dev->of_node) { struct device_node *node = dev->of_node; @@ -233,6 +233,7 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, clk_rate = 0; needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_clk = of_property_read_bool(node, "clocks"); nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); err = PTR_ERR_OR_ZERO(nop->gpiod_reset); @@ -275,6 +276,8 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, if (IS_ERR(nop->clk)) { dev_dbg(dev, "Can't get phy clock: %ld\n", PTR_ERR(nop->clk)); + if (needs_clk) + return PTR_ERR(nop->clk); } if (!IS_ERR(nop->clk) && clk_rate) { diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 3d0dd2f97415..8bc3403a1295 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1261,6 +1261,7 @@ static void msm_chg_detect_work(struct work_struct *w) /* fall through */ case USB_CHG_STATE_SECONDARY_DONE: motg->chg_state = USB_CHG_STATE_DETECTED; + /* fall through */ case USB_CHG_STATE_DETECTED: msm_chg_block_off(motg); dev_dbg(phy->dev, "charger = %d\n", motg->chg_type); diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 0e2f1a36d315..269fc030656e 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -67,11 +67,26 @@ #define ANADIG_ANA_MISC0_SET 0x154 #define ANADIG_ANA_MISC0_CLR 0x158 +#define ANADIG_USB1_CHRG_DETECT_SET 0x1b4 +#define ANADIG_USB1_CHRG_DETECT_CLR 0x1b8 +#define ANADIG_USB1_CHRG_DETECT_EN_B BIT(20) +#define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B BIT(19) +#define ANADIG_USB1_CHRG_DETECT_CHK_CONTACT BIT(18) + #define ANADIG_USB1_VBUS_DET_STAT 0x1c0 +#define ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) + +#define ANADIG_USB1_CHRG_DET_STAT 0x1d0 +#define ANADIG_USB1_CHRG_DET_STAT_DM_STATE BIT(2) +#define ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED BIT(1) +#define ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT BIT(0) + #define ANADIG_USB2_VBUS_DET_STAT 0x220 #define ANADIG_USB1_LOOPBACK_SET 0x1e4 #define ANADIG_USB1_LOOPBACK_CLR 0x1e8 +#define ANADIG_USB1_LOOPBACK_UTMI_TESTSTART BIT(0) + #define ANADIG_USB2_LOOPBACK_SET 0x244 #define ANADIG_USB2_LOOPBACK_CLR 0x248 @@ -479,6 +494,144 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, return 0; } +#define MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT 100 +static int mxs_charger_data_contact_detect(struct mxs_phy *x) +{ + struct regmap *regmap = x->regmap_anatop; + int i, stable_contact_count = 0; + u32 val; + + /* Check if vbus is valid */ + regmap_read(regmap, ANADIG_USB1_VBUS_DET_STAT, &val); + if (!(val & ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)) { + dev_err(x->phy.dev, "vbus is not valid\n"); + return -EINVAL; + } + + /* Enable charger detector */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR, + ANADIG_USB1_CHRG_DETECT_EN_B); + /* + * - Do not check whether a charger is connected to the USB port + * - Check whether the USB plug has been in contact with each other + */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, + ANADIG_USB1_CHRG_DETECT_CHK_CONTACT | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + + /* Check if plug is connected */ + for (i = 0; i < MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT; i++) { + regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); + if (val & ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT) { + stable_contact_count++; + if (stable_contact_count > 5) + /* Data pin makes contact */ + break; + else + usleep_range(5000, 10000); + } else { + stable_contact_count = 0; + usleep_range(5000, 6000); + } + } + + if (i == MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT) { + dev_err(x->phy.dev, + "Data pin can't make good contact.\n"); + /* Disable charger detector */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, + ANADIG_USB1_CHRG_DETECT_EN_B | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + return -ENXIO; + } + + return 0; +} + +static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x) +{ + struct regmap *regmap = x->regmap_anatop; + enum usb_charger_type chgr_type = UNKNOWN_TYPE; + u32 val; + + /* + * - Do check whether a charger is connected to the USB port + * - Do not Check whether the USB plug has been in contact with + * each other + */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR, + ANADIG_USB1_CHRG_DETECT_CHK_CONTACT | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + + msleep(100); + + /* Check if it is a charger */ + regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); + if (!(val & ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED)) { + chgr_type = SDP_TYPE; + dev_dbg(x->phy.dev, "It is a stardard downstream port\n"); + } + + /* Disable charger detector */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, + ANADIG_USB1_CHRG_DETECT_EN_B | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + + return chgr_type; +} + +/* + * It must be called after DP is pulled up, which is used to + * differentiate DCP and CDP. + */ +enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x) +{ + struct regmap *regmap = x->regmap_anatop; + int val; + + msleep(80); + + regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); + if (val & ANADIG_USB1_CHRG_DET_STAT_DM_STATE) { + dev_dbg(x->phy.dev, "It is a dedicate charging port\n"); + return DCP_TYPE; + } else { + dev_dbg(x->phy.dev, "It is a charging downstream port\n"); + return CDP_TYPE; + } +} + +static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + struct regmap *regmap = mxs_phy->regmap_anatop; + void __iomem *base = phy->io_priv; + enum usb_charger_type chgr_type = UNKNOWN_TYPE; + + if (mxs_charger_data_contact_detect(mxs_phy)) + return chgr_type; + + chgr_type = mxs_charger_primary_detection(mxs_phy); + + if (chgr_type != SDP_TYPE) { + /* Pull up DP via test */ + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_CLR); + regmap_write(regmap, ANADIG_USB1_LOOPBACK_SET, + ANADIG_USB1_LOOPBACK_UTMI_TESTSTART); + + chgr_type = mxs_charger_secondary_detection(mxs_phy); + + /* Stop the test */ + regmap_write(regmap, ANADIG_USB1_LOOPBACK_CLR, + ANADIG_USB1_LOOPBACK_UTMI_TESTSTART); + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_SET); + } + + return chgr_type; +} + static int mxs_phy_probe(struct platform_device *pdev) { struct resource *res; @@ -567,6 +720,7 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; mxs_phy->phy.type = USB_PHY_TYPE_USB2; mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup; + mxs_phy->phy.charger_detect = mxs_phy_charger_detect; mxs_phy->clk = clk; mxs_phy->data = of_id->data; diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 8babd318c0ed..1ec00eae339a 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -368,7 +368,8 @@ static int tahvo_usb_probe(struct platform_device *pdev) tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable); if (IS_ERR(tu->extcon)) { dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); - return -ENOMEM; + ret = PTR_ERR(tu->extcon); + goto err_disable_clk; } ret = devm_extcon_dev_register(&pdev->dev, tu->extcon); diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index f0ce304c5aaf..490ebc7df7b2 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -486,6 +486,10 @@ static const struct of_device_id usbhs_of_match[] = { .data = (void *)USBHS_TYPE_RCAR_GEN3, }, { + .compatible = "renesas,usbhs-r8a77995", + .data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL, + }, + { .compatible = "renesas,rcar-gen2-usbhs", .data = (void *)USBHS_TYPE_RCAR_GEN2, }, @@ -501,7 +505,6 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) { struct renesas_usbhs_platform_info *info; struct renesas_usbhs_driver_param *dparam; - const struct of_device_id *of_id = of_match_device(usbhs_of_match, dev); u32 tmp; int gpio; @@ -510,7 +513,7 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) return NULL; dparam = &info->driver_param; - dparam->type = of_id ? (uintptr_t)of_id->data : 0; + dparam->type = (uintptr_t)of_device_get_match_data(dev); if (!of_property_read_u32(dev->of_node, "renesas,buswait", &tmp)) dparam->buswait_bwait = tmp; gpio = of_get_named_gpio_flags(dev->of_node, "renesas,enable-gpio", 0, @@ -519,8 +522,12 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) dparam->enable_gpio = gpio; if (dparam->type == USBHS_TYPE_RCAR_GEN2 || - dparam->type == USBHS_TYPE_RCAR_GEN3) + dparam->type == USBHS_TYPE_RCAR_GEN3 || + dparam->type == USBHS_TYPE_RCAR_GEN3_WITH_PLL) { dparam->has_usb_dmac = 1; + dparam->pipe_configs = usbhsc_new_pipe; + dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + } return info; } @@ -577,17 +584,12 @@ static int usbhs_probe(struct platform_device *pdev) switch (priv->dparam.type) { case USBHS_TYPE_RCAR_GEN2: priv->pfunc = usbhs_rcar2_ops; - if (!priv->dparam.pipe_configs) { - priv->dparam.pipe_configs = usbhsc_new_pipe; - priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); - } break; case USBHS_TYPE_RCAR_GEN3: priv->pfunc = usbhs_rcar3_ops; - if (!priv->dparam.pipe_configs) { - priv->dparam.pipe_configs = usbhsc_new_pipe; - priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); - } + break; + case USBHS_TYPE_RCAR_GEN3_WITH_PLL: + priv->pfunc = usbhs_rcar3_with_pll_ops; break; default: if (!info->platform_callback.get_id) { diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index 02b67abfc2a1..f436e9d51127 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -15,24 +15,39 @@ #include "rcar3.h" #define LPSTS 0x102 +#define UGCTRL 0x180 /* 32-bit register */ #define UGCTRL2 0x184 /* 32-bit register */ +#define UGSTS 0x188 /* 32-bit register */ /* Low Power Status register (LPSTS) */ #define LPSTS_SUSPM 0x4000 +/* R-Car D3 only: USB General control register (UGCTRL) */ +#define UGCTRL_PLLRESET 0x00000001 +#define UGCTRL_CONNECT 0x00000004 + /* * USB General control register 2 (UGCTRL2) * Remarks: bit[31:11] and bit[9:6] should be 0 */ #define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */ +#define UGCTRL2_USB0SEL_HSUSB 0x00000020 #define UGCTRL2_USB0SEL_OTG 0x00000030 #define UGCTRL2_VBUSSEL 0x00000400 +/* R-Car D3 only: USB General status register (UGSTS) */ +#define UGSTS_LOCK 0x00000100 + static void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data) { iowrite32(data, priv->base + reg); } +static u32 usbhs_read32(struct usbhs_priv *priv, u32 reg) +{ + return ioread32(priv->base + reg); +} + static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, void __iomem *base, int enable) { @@ -52,6 +67,34 @@ static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, return 0; } +/* R-Car D3 needs to release UGCTRL.PLLRESET */ +static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + u32 val; + int timeout = 1000; + + if (enable) { + usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */ + usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | + UGCTRL2_USB0SEL_HSUSB); + + usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); + do { + val = usbhs_read32(priv, UGSTS); + udelay(1); + } while (!(val & UGSTS_LOCK) && timeout--); + usbhs_write32(priv, UGCTRL, UGCTRL_CONNECT); + } else { + usbhs_write32(priv, UGCTRL, 0); + usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0); + usbhs_write32(priv, UGCTRL, UGCTRL_PLLRESET); + } + + return 0; +} + static int usbhs_rcar3_get_id(struct platform_device *pdev) { return USBHS_GADGET; @@ -61,3 +104,8 @@ const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = { .power_ctrl = usbhs_rcar3_power_ctrl, .get_id = usbhs_rcar3_get_id, }; + +const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = { + .power_ctrl = usbhs_rcar3_power_and_pll_ctrl, + .get_id = usbhs_rcar3_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rcar3.h b/drivers/usb/renesas_usbhs/rcar3.h index 5f850b23ff18..7fe98175f94f 100644 --- a/drivers/usb/renesas_usbhs/rcar3.h +++ b/drivers/usb/renesas_usbhs/rcar3.h @@ -1,3 +1,4 @@ #include "common.h" extern const struct renesas_usbhs_platform_callback usbhs_rcar3_ops; +extern const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops; |