From ffc4ea79bc06f42283da10ea06bb17b9a3e2b2b4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 26 Sep 2017 15:16:05 -0400 Subject: USB: dummy-hcd: bandwidth limits for non-bulk transfers Part of the emulation performed by dummy-hcd is accounting for bandwidth utilization. The total amount of data transferred in a single frame is supposed to be no larger than an actual USB connection could accommodate. Currently the driver performs bandwidth limiting only for bulk transfers; control and periodic transfers are effectively unlimited. (Presumably drivers were not expected to request extremely large control or interrupt transfers.) This patch improves the situation somewhat by restricting them as well. The emulation still isn't perfect. On a real system, even 0-length transfers use some bandwidth because of transaction overhead (IN, OUT, ACK, NACK packets) and packet overhead (SYNC, PID, bit stuffing, CRC, EOP). Adding in those factors is left as an exercise for a later patch. Signed-off-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index b17618a55f1b..d177d63e16d7 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1766,6 +1766,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*/; @@ -1810,7 +1811,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 */ @@ -1822,14 +1822,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); -- cgit v1.2.3 From c9f20aafc939cc4091757c4d033feb010445f15d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 26 Sep 2017 15:15:58 -0400 Subject: USB: dummy-hcd: remove unsupported isochronous endpoints The dummy-hcd driver doesn't support emulation of isochronous transfers. Therefore it doesn't need to export isochronous endpoint descriptors; they can be commented out. Also, the comments in the source code don't express clearly enough the fact that isochronous isn't supported. They need to be more explicit. Finally, change the error status value we use (in theory) for isochronous URBs. checkpatch complains about ENOSYS; EINVAL is more appropriate (it is documented to mean "ISO madness"). Signed-off-by: Alan Stern Reported-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 53 +++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index d177d63e16d7..671338827951 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 @@ -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 }; @@ -1923,13 +1936,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: -- cgit v1.2.3 From f9c5d1dbda8ee8757a987a55d6cf21b4eb443548 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 18 Aug 2017 18:32:02 +0200 Subject: usb: phy: phy-generic: propagate clk_get error if clock is required If the clock handle is given in the DT, it means the clock is required for proper operation of the PHY. In that case a failure to obtain the clock must be propagated to stop the driver from probing. This fixes working with clocks, which request probe deferral. Signed-off-by: Lucas Stach Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') 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) { -- cgit v1.2.3 From ccb94ebf9e65ca722c98df3885c2e3dfe832260c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 5 Sep 2017 14:28:46 +0300 Subject: usb: dwc3: gadget: check for lack of TRBs a bit earlier This will let us call __dwc3_gadget_kick_transfer() unconditionally. No functional changes, cleanup only. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f064f1549333..a943f6e00fe1 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 @@ -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); -- cgit v1.2.3 From 64e01080299784692ff291099cd8a5ee72e66d98 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 5 Sep 2017 14:32:55 +0300 Subject: usb: dwc3: gadget: simplify __dwc3_gadget_ep_queue() There is more possibility for sharing code if we just realise that now __dwc3_gadget_kic_transfer() knows to break out early if there are no TRBs left. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a943f6e00fe1..850fda013768 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1290,7 +1290,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 +1336,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, 0); } static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, -- cgit v1.2.3 From 502a37b98a7bd45a198139e52671833469720c38 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 5 Sep 2017 14:36:13 +0300 Subject: usb: dwc3: gadget: cache frame number in struct dwc3_ep This is in preparation to simplifying prototype of __dwc3_gadget_kick_transfer(). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') 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/gadget.c b/drivers/usb/dwc3/gadget.c index 850fda013768..2c0b9d0e2b05 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1258,8 +1258,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 +1269,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, dep->frame_number); } static void dwc3_gadget_start_isoc(struct dwc3 *dwc, -- cgit v1.2.3 From 7fdca766499b1d361689ca9402543c027c6a1e87 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 5 Sep 2017 14:41:34 +0300 Subject: usb: dwc3: gadget: simplify __dwc3_gadget_kick_transfer() prototype Now that all the information we need sits in struct dwc3_ep, we can start taking only a pointer to struct dwc3_ep as an argument. This allows us to clean the code up a bit. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2c0b9d0e2b05..66dc99b9525a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1191,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; @@ -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); @@ -1270,7 +1272,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, * least 4 microframes. */ dep->frame_number = cur_uf + max_t(u32, 4, dep->interval); - __dwc3_gadget_kick_transfer(dep, dep->frame_number); + __dwc3_gadget_kick_transfer(dep); } static void dwc3_gadget_start_isoc(struct dwc3 *dwc, @@ -1340,7 +1342,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) } out: - return __dwc3_gadget_kick_transfer(dep, 0); + return __dwc3_gadget_kick_transfer(dep); } static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, @@ -2333,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); @@ -2426,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, @@ -2473,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: -- cgit v1.2.3 From e93650994a955f8cd966916c4e980e822123d07a Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 4 Sep 2017 23:14:02 +0800 Subject: usb: phy: mxs: add usb charger type detection mxs phy has data pin contact and usb charger detector blocks which can be controlled by software to detect charger type for SDP, CDP and DCP. Reviewed-by: Baolin Wang Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 154 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) (limited to 'drivers') 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; -- cgit v1.2.3 From a877b8e553fd2808e8693c75e0d945f413ccf5b6 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 3 Oct 2017 20:09:13 +0900 Subject: usb: renesas_usbhs: unify Gen2/3 pipe_config setting This patch unifies the Gen2 and Gen3 pipe_config setting on usbhs_parse_dt(). Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index f0ce304c5aaf..2a860e496b4f 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -519,8 +519,11 @@ 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->has_usb_dmac = 1; + dparam->pipe_configs = usbhsc_new_pipe; + dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + } return info; } @@ -577,17 +580,9 @@ 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; default: if (!info->platform_callback.get_id) { -- cgit v1.2.3 From 0f38672c629b79fa2b929d2c391bc063a08279eb Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 3 Oct 2017 20:09:14 +0900 Subject: usb: renesas_usbhs: add support for R-Car D3 This patch adds support for R-Car D3. This SoC needs to release the PLL reset by the UGCTRL register. So, since this is not the same as other R-Car Gen3 SoCs, this patch adds a new type as "USBHS_TYPE_RCAR_GEN3_WITH_PLL". Acked-by: Rob Herring Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/renesas_usbhs.txt | 1 + drivers/usb/renesas_usbhs/common.c | 10 ++++- drivers/usb/renesas_usbhs/rcar3.c | 48 ++++++++++++++++++++++ drivers/usb/renesas_usbhs/rcar3.h | 1 + include/linux/usb/renesas_usbhs.h | 5 ++- 5 files changed, 62 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index 9e18e000339e..e79f6e43061a 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -10,6 +10,7 @@ Required properties: - "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device - "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device - "renesas,usbhs-r8a7796" for r8a7796 (R-Car M3-W) compatible device + - "renesas,usbhs-r8a77995" for r8a77995 (R-Car D3) compatible device - "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device - "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 2a860e496b4f..3e92a784c5c3 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -485,6 +485,10 @@ static const struct of_device_id usbhs_of_match[] = { .compatible = "renesas,usbhs-r8a7796", .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, @@ -519,7 +523,8 @@ 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); @@ -584,6 +589,9 @@ static int usbhs_probe(struct platform_device *pdev) case USBHS_TYPE_RCAR_GEN3: priv->pfunc = usbhs_rcar3_ops; break; + case USBHS_TYPE_RCAR_GEN3_WITH_PLL: + priv->pfunc = usbhs_rcar3_with_pll_ops; + break; default: if (!info->platform_callback.get_id) { dev_err(&pdev->dev, "no platform callbacks"); 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; diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 00a47d058d83..d2f41e4dd7a8 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -183,8 +183,9 @@ struct renesas_usbhs_driver_param { #define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */ }; -#define USBHS_TYPE_RCAR_GEN2 1 -#define USBHS_TYPE_RCAR_GEN3 2 +#define USBHS_TYPE_RCAR_GEN2 1 +#define USBHS_TYPE_RCAR_GEN3 2 +#define USBHS_TYPE_RCAR_GEN3_WITH_PLL 3 /* * option: -- cgit v1.2.3 From cf06df3fae286b795c1abf59c4b493ebf30a7a9f Mon Sep 17 00:00:00 2001 From: Kazuya Mizuguchi Date: Fri, 29 Sep 2017 20:44:59 +0900 Subject: usb: gadget: udc: renesas_usb3: move pm_runtime_{en,dis}able() This patch moves pm_runtime_{en,dis}able() call timing to renesas_usb3_{probe,remove}() for supporting PM_SLEEP feature in the future. Signed-off-by: Kazuya Mizuguchi [shimoda: Revise the commit log] Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/renesas_usb3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 63a206122058..740497c43bd3 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2239,7 +2239,6 @@ static int renesas_usb3_start(struct usb_gadget *gadget, /* hook up the driver */ usb3->driver = driver; - pm_runtime_enable(usb3_to_dev(usb3)); pm_runtime_get_sync(usb3_to_dev(usb3)); renesas_usb3_init_controller(usb3); @@ -2257,7 +2256,6 @@ static int renesas_usb3_stop(struct usb_gadget *gadget) renesas_usb3_stop_controller(usb3); pm_runtime_put(usb3_to_dev(usb3)); - pm_runtime_disable(usb3_to_dev(usb3)); return 0; } @@ -2405,6 +2403,7 @@ 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); + pm_runtime_disable(usb3_to_dev(usb3)); return 0; } @@ -2640,6 +2639,7 @@ static int renesas_usb3_probe(struct platform_device *pdev) renesas_usb3_debugfs_init(usb3, &pdev->dev); dev_info(&pdev->dev, "probed\n"); + pm_runtime_enable(usb3_to_dev(usb3)); return 0; -- cgit v1.2.3 From 90d588642a7ff598533f68c2f56ee64657a40186 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 29 Sep 2017 20:45:00 +0900 Subject: usb: gadget: udc: renesas_usb3: Add suspend/resume functions This patch adds support suspend/resume functions Signed-off-by: Kazuya Mizuguchi [shimoda: add the commit log] Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/renesas_usb3.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 740497c43bd3..75afc4c4bbd8 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2655,11 +2655,45 @@ 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); + 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; + + 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), }, }; -- cgit v1.2.3 From 279d4bc6406022461713cd6a3e5411336d2ff26b Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 29 Sep 2017 20:45:01 +0900 Subject: usb: gadget: udc: renesas_usb3: add support for generic phy This patch adds support for generic phy as an optional. If you want to use a generic phy (e.g. phy-rcar-gen3-usb3 driver) on this driver, you have to do "insmod phy-rcar-gen3-usb3.ko" first for now. Acked-by: Rob Herring Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/renesas_usb3.txt | 4 ++++ drivers/usb/gadget/udc/renesas_usb3.c | 26 ++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/usb/renesas_usb3.txt b/Documentation/devicetree/bindings/usb/renesas_usb3.txt index e28025883b79..87a45e2f9b7f 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usb3.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usb3.txt @@ -15,6 +15,10 @@ Required properties: - interrupts: Interrupt specifier for the USB3.0 Peripheral - clocks: clock phandle and specifier pair +Optional properties: + - phys: phandle + phy specifier pair + - phy-names: must be "usb" + Example of R-Car H3 ES1.x: usb3_peri0: usb@ee020000 { compatible = "renesas,r8a7795-usb3-peri", diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 75afc4c4bbd8..0cbf909182a2 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 #include #include +#include #include #include #include @@ -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,6 +2241,9 @@ static int renesas_usb3_start(struct usb_gadget *gadget, /* hook up the driver */ usb3->driver = driver; + if (usb3->phy) + phy_init(usb3->phy); + pm_runtime_get_sync(usb3_to_dev(usb3)); renesas_usb3_init_controller(usb3); @@ -2255,6 +2260,9 @@ 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)); return 0; @@ -2403,6 +2411,8 @@ 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; @@ -2634,11 +2644,19 @@ 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; @@ -2665,6 +2683,8 @@ static int renesas_usb3_suspend(struct device *dev) return 0; renesas_usb3_stop_controller(usb3); + if (usb3->phy) + phy_exit(usb3->phy); pm_runtime_put(dev); return 0; @@ -2678,6 +2698,8 @@ static int renesas_usb3_resume(struct device *dev) if (!usb3->driver) return 0; + if (usb3->phy) + phy_init(usb3->phy); pm_runtime_get_sync(dev); renesas_usb3_init_controller(usb3); -- cgit v1.2.3 From b61e47b44882f6e578ef7c14197ea90a2703b5a0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 4 Oct 2017 14:26:48 +0200 Subject: usb: renesas_usbhs: Use of_device_get_match_data() helper Use the of_device_get_match_data() helper instead of open coding. Reviewed-by: Simon Horman Reviewed-by: Yoshihiro Shimoda Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 3e92a784c5c3..490ebc7df7b2 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -505,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; @@ -514,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, -- cgit v1.2.3 From ca02a5af650cf3addb004196c2ab713b020445ef Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 4 Oct 2017 14:23:31 +0200 Subject: usb: gadget: udc: renesas_usb3: Use of_device_get_match_data() helper Use the of_device_get_match_data() helper instead of open coding, postponing the matching until when it's really needed. Note that the renesas_usb3 driver is used with DT only, so there's always a valid match. Reviewed-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/renesas_usb3.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 0cbf909182a2..4d9a40f452e1 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2569,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) { -- cgit v1.2.3 From 87e981d51a85005b2793d8745e8671765a5a2b6b Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 27 Sep 2017 22:57:07 +0530 Subject: usb: gadget: f_uvc: make uvc_v4l2_fops const Make this const as it is only stored in the const field of a structure video_device in the file referencing it. Make the declaration const too. Done using Coccinelle. Signed-off-by: Bhumika Goyal Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_v4l2.c | 2 +- drivers/usb/gadget/function/uvc_v4l2.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') 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__ */ -- cgit v1.2.3 From d8fae8b936824b3ffe55a0408b84022244603a32 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Thu, 17 Aug 2017 11:33:01 +0200 Subject: usb: dwc2: add support for STM32F7xx USB OTG HS This patch adds the dwc2_set_params function for STM32F7xx USB OTG HS. Signed-off-by: Amelie Delaunay Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index a3ffe97170ff..015d23ebcf5a 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); -- cgit v1.2.3 From 86763723eee87bfe561cdabae2dd1c2e97933bd8 Mon Sep 17 00:00:00 2001 From: Christos Gkekas Date: Sat, 14 Oct 2017 12:44:11 +0100 Subject: usb: dwc3: ep0: Clean up unused variables Many variables in ep0 are set but never used, so should be removed. Signed-off-by: Christos Gkekas Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'drivers') 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, -- cgit v1.2.3 From c162ff0aaaac456ef29aebd1e9d4d3e305cd3279 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:37 +0800 Subject: usb: mtu3: fix error return code in ssusb_gadget_init() When failing to get IRQ number, platform_get_irq() may return -EPROBE_DEFER, but we ignore it and always return -ENODEV, so fix it. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 99c65b0788ff..947579842ad7 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -774,9 +774,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); -- cgit v1.2.3 From 076f1a8903d5dadf224f17be63a25bd75d860659 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:38 +0800 Subject: usb: mtu3: support option to disable usb3 ports Add support to disable specific usb3 ports, it's useful when usb3 phy is shared with PCIe or SATA, because we should disable the corresponding usb3 port if the phy is used by PCIe or SATA. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3.h | 3 +++ drivers/usb/mtu3/mtu3_host.c | 16 +++++++++++++--- drivers/usb/mtu3/mtu3_plat.c | 8 ++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index b26fffc58446..112723d6e7bc 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -210,6 +210,8 @@ struct otg_switch_mtk { * 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 @@ -232,6 +234,7 @@ struct ssusb_mtk { 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; diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index e42d308b8dc2..4dd9508a60b5 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -151,6 +151,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 +159,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 +182,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 +197,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; diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 088e3e685c4f..9edad30c8ae5 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -276,6 +276,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) if (ret) return ret; + /* optional property, ignore the error if it does not exist */ + of_property_read_u32(node, "mediatek,u3p-dis-msk", + &ssusb->u3p_dis_msk); + if (ssusb->dr_mode != USB_DR_MODE_OTG) return 0; @@ -304,8 +308,8 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } } - 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\n", + ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk); return 0; } -- cgit v1.2.3 From d90223ac2aa7a71dcbdcbf075ac2c3bd59245f0a Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:39 +0800 Subject: usb: mtu3: remove dummy wakeup debounce clocks The wakeup debounce clocks for each ports in fact are not needed, so remove them. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3.h | 4 ---- drivers/usb/mtu3/mtu3_host.c | 57 ++++---------------------------------------- 2 files changed, 4 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 112723d6e7bc..6d3278e46431 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -214,8 +214,6 @@ struct otg_switch_mtk { * 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; @@ -238,8 +236,6 @@ struct ssusb_mtk { 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_host.c b/drivers/usb/mtu3/mtu3_host.c index 4dd9508a60b5..edcc59148171 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; @@ -286,19 +242,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); - } } -- cgit v1.2.3 From a316da82f8d2b9823a2292a7bf84e029fdb3de08 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:40 +0800 Subject: usb: mtu3: add optional mcu and dma bus clocks There are mcu_bus and dma_bus clocks needed to be turned on/off by driver on some SoCs, so add them as optional ones Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3.h | 5 ++ drivers/usb/mtu3/mtu3_plat.c | 121 +++++++++++++++++++++++++++++-------------- 2 files changed, 86 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 6d3278e46431..2795294ec92a 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -206,6 +206,9 @@ 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 @@ -226,6 +229,8 @@ 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; diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 9edad30c8ae5..fb8992011bde 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -110,15 +110,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 +126,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 +189,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); @@ -203,6 +239,19 @@ static int get_iddig_pinctrl(struct ssusb_mtk *ssusb) return 0; } +/* ignore the error if the clock does not exist */ +static struct clk *get_optional_clk(struct device *dev, const char *id) +{ + struct clk *opt_clk; + + 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; + + return opt_clk; +} + static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) { struct device_node *node = pdev->dev.of_node; @@ -225,18 +274,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"); @@ -451,8 +499,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; @@ -470,27 +517,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; } -- cgit v1.2.3 From 1a46dfea0841d5ebc08fafe7b3f60d33581f8e27 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:41 +0800 Subject: usb: mtu3: support 36-bit DMA address add support for 36-bit DMA address [ Felipe Balbi: fix printk format for dma_addr_t ] Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3.h | 17 ++++++- drivers/usb/mtu3/mtu3_core.c | 34 +++++++++++++- drivers/usb/mtu3/mtu3_hw_regs.h | 10 ++++ drivers/usb/mtu3/mtu3_qmu.c | 102 +++++++++++++++++++++++++++++++++------- 4 files changed, 142 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 2795294ec92a..ef2dc92a2109 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)) @@ -138,23 +141,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; diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 947579842ad7..cd4528f5f337 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -17,6 +17,7 @@ * */ +#include #include #include #include @@ -759,7 +760,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) { @@ -820,6 +845,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 +876,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_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index 06b29664470f..b6059752dc12 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) 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); -- cgit v1.2.3 From c776f2c3e81308977e95a228b0665e3d5c63dff3 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:42 +0800 Subject: usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch In order to keep manual DRD switch independent on IDDIG interrupt, make use of FORCE/RG_IDDIG instead of IDDIG EINT interrupt to implement manual DRD switch function. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3.h | 18 ++++++++---- drivers/usb/mtu3/mtu3_dr.c | 61 ++++++++++++++++++++++++++++++----------- drivers/usb/mtu3/mtu3_dr.h | 6 ++++ drivers/usb/mtu3/mtu3_host.c | 5 ++++ drivers/usb/mtu3/mtu3_hw_regs.h | 2 ++ drivers/usb/mtu3/mtu3_plat.c | 38 ++----------------------- 6 files changed, 74 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index ef2dc92a2109..b0c2b5dca045 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -114,6 +114,19 @@ enum mtu3_g_ep0_state { MU3D_EP0_STATE_STALL, }; +/** + * 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 @@ -196,7 +209,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. */ @@ -207,10 +219,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; }; 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_host.c b/drivers/usb/mtu3/mtu3_host.c index edcc59148171..ec76b86dd887 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -189,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); /* @@ -197,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); } diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index b6059752dc12..a7e35f6ad90a 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -472,6 +472,8 @@ #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 fb8992011bde..1e473b068650 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include "mtu3.h" @@ -212,33 +211,6 @@ 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) -{ - struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - - 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); - } - - 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; -} - /* ignore the error if the clock does not exist */ static struct clk *get_optional_clk(struct device *dev, const char *id) { @@ -349,15 +321,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, u3p_dis_msk:%x\n", - ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk); + 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; } -- cgit v1.2.3 From 4d79e042ed8b45e01bcec90de6b0c79c6c29d2b5 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:43 +0800 Subject: usb: mtu3: add support for usb3.1 IP Support SuperSpeedPlus for usb3.1 device IP Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3.h | 1 + drivers/usb/mtu3/mtu3_core.c | 14 +++++++++++--- drivers/usb/mtu3/mtu3_gadget.c | 3 ++- drivers/usb/mtu3/mtu3_gadget_ep0.c | 16 ++++++++-------- drivers/usb/mtu3/mtu3_hw_regs.h | 1 + 5 files changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index b0c2b5dca045..d80e4e813248 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -94,6 +94,7 @@ enum mtu3_speed { MTU3_SPEED_FULL = 1, MTU3_SPEED_HIGH = 3, MTU3_SPEED_SUPER = 4, + MTU3_SPEED_SUPER_PLUS = 5, }; /** diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index cd4528f5f337..67f7a309aba7 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -237,7 +237,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); @@ -547,6 +547,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", @@ -624,6 +627,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; @@ -825,14 +832,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; } 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_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index a7e35f6ad90a..6953436a1688 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -467,6 +467,7 @@ #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) -- cgit v1.2.3 From 6638ec515f2cce73ba996a725fe351fd643e27c8 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:44 +0800 Subject: usb: mtu3: get optional vbus for host only mode When dr_mode is set as USB_DR_MODE_HOST, it's better to try to get optional vbus, this can increase flexibility, although we can set vbus as always on for regulator or put it in host driver to turn it on. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_plat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 1e473b068650..7ca81f4e78a3 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -300,10 +300,6 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) of_property_read_u32(node, "mediatek,u3p-dis-msk", &ssusb->u3p_dis_msk); - if (ssusb->dr_mode != USB_DR_MODE_OTG) - return 0; - - /* 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"); @@ -311,6 +307,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"); -- cgit v1.2.3 From dd9d2f3aa3f8908fc3133041ccba45792549f25c Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:45 +0800 Subject: usb: mtu3: set invalid dr_mode as dual-role mode Treat dr_mode of USB_DR_MODE_UNKNOWN as USB_DR_MODE_OTG to enhance functional robustness. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_plat.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 7ca81f4e78a3..9ff33579b42e 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -283,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; -- cgit v1.2.3 From 4da72e6d2afb24c9cd5707eef7947c2f22dd03fc Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 13 Oct 2017 17:10:46 +0800 Subject: usb: mtu3: set otg_sel for u2port only if works as dual-role mode When set otg_sel(SSUSB_U2_PORT_OTG_SEL) for u2port which supports dual-role mode, the controller will automatically switch mode between host and device according to IDDIG signal. But if the u2port only supports device mode, and no IDDIG pin is provided, setting otg_sel may cause failure of detection by host. So set it only for dual-role mode. Signed-off-by: Chunfeng Yun Signed-off-by: Felipe Balbi --- drivers/usb/mtu3/mtu3_core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 67f7a309aba7..7c149a7da14e 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -115,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); } @@ -130,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); } -- cgit v1.2.3 From b11633c42a766cb3c824e3583163b9adf67501fe Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 16 Oct 2017 08:57:18 -0500 Subject: usb: dwc2: disable erroneous overcurrent condition For the case where an external VBUS is used, we should enable the external VBUS comparator in the driver. This would prevent an unnecessary overcurrent error which would then disable the host port. This patch uses the standard 'disable-over-current' binding to allow of the option of disabling the over-current condition. Reviewed-by: Marek Vasut Signed-off-by: Dinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 4 ++++ drivers/usb/dwc2/hcd.c | 5 +++++ drivers/usb/dwc2/params.c | 3 +++ 3 files changed, 12 insertions(+) (limited to 'drivers') 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/hcd.c b/drivers/usb/dwc2/hcd.c index c2631145f404..5e2033616aca 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 */ diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 015d23ebcf5a..fe770a2834d2 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -346,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) -- cgit v1.2.3 From d6d9c2a3eac279d0bf406573706b7e7cf9d7fdda Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Wed, 19 Jul 2017 17:59:06 +0200 Subject: usb: dwc3: of-simple: Re-order resource handling in remove Move clock handling after of_platform_depopulate to achieve a sequence that is reverse of the probe sequence. Cc: Felipe Balbi Signed-off-by: Vivek Gautam Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-of-simple.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index a26d1fde0f5e..e129c3278081 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -123,13 +123,13 @@ 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); - pm_runtime_put_sync(dev); pm_runtime_disable(dev); -- cgit v1.2.3 From 06c47e6286d5afdd7b2e7e3c207f6906b3be3d4d Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 19 Oct 2017 13:47:43 +0200 Subject: usb: dwc3: of-simple: Add support to get resets for the device Add support to get a list of resets available for the device. These resets must be kept de-asserted until the device is in use. Signed-off-by: Vivek Gautam [p.zabel@pengutronix.de: switch to hidden reset control array] Signed-off-by: Philipp Zabel Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-of-simple.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e129c3278081..ceea1619f8aa 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -28,11 +28,13 @@ #include #include #include +#include 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) @@ -130,6 +150,9 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) clk_put(simple->clks[i]); } + reset_control_assert(simple->resets); + reset_control_put(simple->resets); + pm_runtime_put_sync(dev); pm_runtime_disable(dev); -- cgit v1.2.3 From 99bcb23851ffa19d8ad13cd35100990559aac82c Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Fri, 20 Oct 2017 16:29:13 +0900 Subject: usb: gadget: udc: remove duplicate & operation usb_endpoint_maxp() has an inline keyword and searches for bits[10:0] by & operation with 0x7ff. So, we can remove the duplicate & operation with 0x7ff. Signed-off-by: Jaejoong Kim Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') 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; -- cgit v1.2.3 From 7521d47960d6a7f3b5f9f26d4baf5f97e050d7ac Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Fri, 20 Oct 2017 16:29:14 +0900 Subject: usb: gadget: udc: gr: remove duplicate & operation usb_endpoint_maxp() has an inline keyword and searches for bits[10:0] by & operation with 0x7ff. So, we can remove the duplicate & operation with 0x7ff. Signed-off-by: Jaejoong Kim Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/gr_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') 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)) { -- cgit v1.2.3 From 1c236d411d8b163adca49009857e7c2745185038 Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Fri, 20 Oct 2017 16:29:15 +0900 Subject: usb: misc: usbtest: remove duplicate & operation usb_endpoint_maxp() has an inline keyword and searches for bits[10:0] by & operation with 0x7ff. So, we can remove the duplicate & operation with 0x7ff. Signed-off-by: Jaejoong Kim Signed-off-by: Felipe Balbi --- drivers/usb/misc/usbtest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index eee82ca55b7b..2d4306cd6e0e 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1908,7 +1908,7 @@ static struct urb *iso_alloc_urb( if (bytes < 0 || !desc) return NULL; - maxp = 0x7ff & usb_endpoint_maxp(desc); + maxp = usb_endpoint_maxp(desc); maxp *= usb_endpoint_maxp_mult(desc); packets = DIV_ROUND_UP(bytes, maxp); -- cgit v1.2.3 From 457b16d4b6bb7705269306e43e25d88ffa52f8cb Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 23 Oct 2017 22:45:36 -0500 Subject: usb: gadget: goku_udc: mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Addresses-Coverity-ID: 145713 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/goku_udc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') 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; } -- cgit v1.2.3 From d4acce95ffd337eab84746762196d0599992792a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 23 Oct 2017 22:32:44 -0500 Subject: usb: gadget: f_tcm: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Addresses-Coverity-ID: 703128 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_tcm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') 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; -- cgit v1.2.3 From 58a636ec0362d99f4edaaad4f3c682bf124e799e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 23 Oct 2017 22:21:09 -0500 Subject: usb: phy: phy-msm-usb: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Addresses-Coverity-ID: 1222118 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') 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); -- cgit v1.2.3 From fdb5e4fa1aedb91d900cd7719a0bfb92b4122c53 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 23 Oct 2017 22:15:46 -0500 Subject: usb: gadget: serial: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Addresses-Coverity-ID: 1350962 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') 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); -- cgit v1.2.3 From ce035409bfa892a2fabb89720b542e1b335c3426 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 21 Oct 2017 01:02:07 +0300 Subject: usb: phy: tahvo: fix error handling in tahvo_usb_probe() If devm_extcon_dev_allocate() fails, we should disable clk before return. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Fixes: 860d2686fda7 ("usb: phy: tahvo: Use devm_extcon_dev_[allocate|register]() and replace deprecated API") Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tahvo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') 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); -- cgit v1.2.3 From 689bf72c6e0dc97493ba14d82f6762456f8f244a Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Wed, 27 Sep 2017 16:49:20 +0530 Subject: usb: dwc3: Don't reinitialize core during host bus-suspend/resume Driver powers-off PHYs and reinitializes DWC3 core and gadget on resume. While this works fine for gadget mode but in host mode there is not re-initialization of host stack. Also, resetting bus as part of bus_suspend/resume is not correct which could affect (or disconnect) connected devices. Fix this by not reinitializing core on suspend/resume in host mode for HOST only and OTG/drd configurations. Signed-off-by: Manu Gautam Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 03474d3575ab..f75613fd0de0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -927,6 +927,7 @@ 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) @@ -942,6 +943,7 @@ 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) @@ -1293,21 +1295,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 +1316,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 +1337,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,12 +1380,11 @@ 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; @@ -1402,13 +1400,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; -- cgit v1.2.3 From 8eed00b237a2844dbe6bc2bac5c0b2657f9f194b Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Wed, 27 Sep 2017 16:49:21 +0530 Subject: usb: dwc3: pci: Runtime resume child device from wq Driver currently resumes and increments pm usage_count of its child device (dwc3 main) from its runtime_resume handler. This requires dwc3 runtime_resume to perform pm_runtime_put to decrement the pm usage_count. However runtime_put from dwc3 happens for non pci drivers (e.g. dwc3-if-simple.c) as well which results in dwc3 pm usage_count becoming negative after couple of runtime suspend resume iterations. Fix this by performing runtime_get/put from dwc3-pci driver only using workqueue. Signed-off-by: Manu Gautam Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 1 - drivers/usb/dwc3/dwc3-pci.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f75613fd0de0..23bb1925c9aa 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1391,7 +1391,6 @@ static int dwc3_runtime_resume(struct device *dev) } pm_runtime_mark_last_busy(dev); - pm_runtime_put(dev); return 0; } 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 #include #include +#include #include #include #include @@ -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 */ -- cgit v1.2.3 From 644cbbc3ab4d29a7513f227893d8df3246eada97 Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Wed, 27 Sep 2017 16:49:22 +0530 Subject: usb: dwc3: core: Notify current USB mode to USB3 PHY as well Driver currently notifies only USB2 PHY on USB mode change. Extend this to USB3 PHY so that PHY drivers based on the mode can release system resources - clocks, regulators etc. Additionally Qualcomm QMP and QUSB2 PHY drivers need to override VBUS signal in PHY wrapper in device mode as USB VBUS line is not connected to PHYs. Also, remove NULL checks for PHY when calling phy_set_mode as PHY ops already check this. Signed-off-by: Manu Gautam Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 23bb1925c9aa..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) @@ -932,8 +931,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) 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) { @@ -948,8 +947,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) 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) { -- cgit v1.2.3 From d2471d4a24dfbff5e463d382e2c6fec7d7e25a09 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 23 Oct 2017 14:32:48 -0700 Subject: usb: dwc2: Improve gadget state disconnection handling In the earlier commit dad3f793f20f ("usb: dwc2: Make sure we disconnect the gadget state"), I was trying to fix up the fact that we somehow weren't disconnecting the gadget state, so that when the OTG port was plugged in the second time we would get warnings about the state tracking being wrong. (This seems to be due to a quirk of the HiKey board where we do not ever get any otg interrupts, particularly the session end detected signal. Instead we only see status change interrupt.) The fix there was somewhat simple, as it just made sure to call dwc2_hsotg_disconnect() before we connected things up in OTG mode, ensuring the state handling didn't throw errors. But in looking at a different issue I was seeing with UDC state handling, I realized that it would be much better to call dwc2_hsotg_disconnect when we get the state change signal moving to host mode. Thus, this patch removes the earlier disconnect call I added and moves it (and the needed locking) to the host mode transition. Cc: Wei Xu Cc: Guodong Xu Cc: Amit Pundir Cc: YongQin Liu Cc: John Youn Cc: Minas Harutyunyan Cc: Douglas Anderson Cc: Chen Yu Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Acked-by: Minas Harutyunyan Tested-by: Minas Harutyunyan Signed-off-by: John Stultz Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 5e2033616aca..e8fee0f969f0 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3282,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); @@ -3301,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); -- cgit v1.2.3 From 9b481092c2a31a6b630aff9c28f0145bf6683787 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 23 Oct 2017 14:32:49 -0700 Subject: usb: dwc2: Error out of dwc2_hsotg_ep_disable() if we're in host mode We've found that while in host mode, using Android, if one runs the command: stop adbd The existing usb devices being utilized in host mode are disconnected. This is most visible with usb networking devices. This seems to be due to adbd closing the file: /dev/usb-ffs/adb/ep0 Which calls ffs_ep0_release() and the following backtrace: [] dwc2_hsotg_ep_disable+0x148/0x150 [] dwc2_hsotg_udc_stop+0x60/0x110 [] usb_gadget_remove_driver+0x58/0x78 [] usb_gadget_unregister_driver+0x74/0xe8 [] unregister_gadget+0x28/0x58 [] unregister_gadget_item+0x2c/0x40 [] ffs_data_clear+0xe8/0xf8 [] ffs_data_reset+0x20/0x58 [] ffs_data_closed+0x98/0xe8 [] ffs_ep0_release+0x20/0x30 Then when dwc2_hsotg_ep_disable() is called, we call kill_all_requests() which causes a bunch of the following messages: dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode dwc2 f72c0000.usb: Mode Mismatch Interrupt: currently in Host mode init: Service 'adbd' (pid 1915) killed by signal 9 init: Sending signal 9 to service 'adbd' (pid 1915) process group... init: Successfully killed process cgroup uid 0 pid 1915 in 0ms init: processing action (init.svc.adbd=stopped) from (/init.usb.configfs.rc:15) dwc2 f72c0000.usb: dwc2_hc_chhltd_intr_dma: Channel 8 - ChHltd set, but reason is unknown dwc2 f72c0000.usb: hcint 0x00000002, intsts 0x04200029 dwc2 f72c0000.usb: dwc2_hc_chhltd_intr_dma: Channel 12 - ChHltd set, but reason is unknown dwc2 f72c0000.usb: hcint 0x00000002, intsts 0x04200029 dwc2 f72c0000.usb: dwc2_hc_chhltd_intr_dma: Channel 15 - ChHltd set, but reason is unknown dwc2 f72c0000.usb: hcint 0x00000002, intsts 0x04200029 dwc2 f72c0000.usb: dwc2_hc_chhltd_intr_dma: Channel 3 - ChHltd set, but reason is unknown dwc2 f72c0000.usb: hcint 0x00000002, intsts 0x04200029 dwc2 f72c0000.usb: dwc2_hc_chhltd_intr_dma: Channel 4 - ChHltd set, but reason is unknown dwc2 f72c0000.usb: hcint 0x00000002, intsts 0x04200029 dwc2 f72c0000.usb: dwc2_update_urb_state_abn(): trimming xfer length And the usb devices connected are basically hung at this point. It seems like if we're in host mode, we probably shouldn't run the dwc2_hostg_ep_disable logic, so this patch returns an error in that case. With this patch (along with the previous patch in this set), we avoid the mismatched interrupts and connected usb devices continue to function. I'm not sure if some other solution would be better here, but this seems to work, so I wanted to send it out for input on what the right approach should be. Cc: Wei Xu Cc: Guodong Xu Cc: Amit Pundir Cc: YongQin Liu Cc: John Youn Cc: Minas Harutyunyan Cc: Douglas Anderson Cc: Chen Yu Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Acked-by: Minas Harutyunyan Tested-by: Minas Harutyunyan Reported-by: YongQin Liu Signed-off-by: John Stultz Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 0d8e09ccb59c..7fd0e38c94e0 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4004,6 +4004,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); -- cgit v1.2.3 From ce2b21a4e5ce042c0a42c9db8fa9e0f849427d5e Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 23 Oct 2017 14:32:50 -0700 Subject: usb: dwc2: Fix UDC state tracking It has been noticed that the dwc2 udc state reporting doesn't seem to work (at least on HiKey boards). Where after the initial setup, the sysfs /sys/class/udc/f72c0000.usb/state file would report "configured" no matter the state of the OTG port. This patch adds a call so that we report to the UDC layer when the gadget device is disconnected. This patch does depend on the previous patch ("usb: dwc2: Improve gadget state disconnection handling") in this patch set in order to properly work. Cc: Wei Xu Cc: Guodong Xu Cc: Amit Pundir Cc: YongQin Liu Cc: John Youn Cc: Minas Harutyunyan Cc: Douglas Anderson Cc: Chen Yu Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Acked-by: Minas Harutyunyan Tested-by: Minas Harutyunyan Reported-by: Amit Pundir Signed-off-by: John Stultz Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 7fd0e38c94e0..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); } /** -- cgit v1.2.3