From 4552a0ca61cb2133bc77dc432ea20538ea28638a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 May 2012 09:19:31 +0300 Subject: usb: dwc3: fix a WARN and a comment we're now have DWC3_EP0_BOUNCE_SIZE to tell us the actual size of the bufer. Let's use that instead of ep0 wMaxPacketSize. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 9e8a3dce69f..e6ca218ef13 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -821,14 +821,14 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, return; } - WARN_ON(req->request.length > dep->endpoint.maxpacket); + WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE); dwc->ep0_bounced = true; /* - * REVISIT in case request length is bigger than EP0 - * wMaxPacketSize, we will need two chained TRBs to handle - * the transfer. + * REVISIT in case request length is bigger than + * DWC3_EP0_BOUNCE_SIZE we will need two chained + * TRBs to handle the transfer. */ ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, dwc->ep0_bounce_addr, dep->endpoint.maxpacket, -- cgit v1.2.3-70-g09d2 From a0807881af93646b5d94b5594119df609e756945 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 4 May 2012 13:03:54 +0300 Subject: usb: dwc3: handle pending unaligned Control OUT data phase correctly When DWC3_EP_PENDING_REQUEST flag is set for a Control OUT Data phase transfer, we would be missing the proper handling for unaligned OUT requests, thus hanging a transfer. Since proper handling is already done on dwc3_ep0_do_control_data(), we simply re-factor that function so it can be re-used from __dwc3_gadget_ep0_queue(). Reported-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 72 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e6ca218ef13..15ec36eb461 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -55,6 +55,8 @@ #include "io.h" static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); +static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, + struct dwc3_ep *dep, struct dwc3_request *req); static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) { @@ -150,9 +152,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, return 0; } - ret = dwc3_ep0_start_trans(dwc, direction, - req->request.dma, req->request.length, - DWC3_TRBCTL_CONTROL_DATA); + __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); + dep->flags &= ~(DWC3_EP_PENDING_REQUEST | DWC3_EP0_DIR_IN); } else if (dwc->delayed_status) { @@ -787,35 +788,23 @@ static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, dwc3_ep0_out_start(dwc); } -static void dwc3_ep0_do_control_data(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) +static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, + struct dwc3_ep *dep, struct dwc3_request *req) { - struct dwc3_ep *dep; - struct dwc3_request *req; int ret; - dep = dwc->eps[0]; - - if (list_empty(&dep->request_list)) { - dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); - dep->flags |= DWC3_EP_PENDING_REQUEST; - - if (event->endpoint_number) - dep->flags |= DWC3_EP0_DIR_IN; - return; - } - - req = next_request(&dep->request_list); - req->direction = !!event->endpoint_number; + req->direction = !!dep->number; if (req->request.length == 0) { - ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + ret = dwc3_ep0_start_trans(dwc, dep->number, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA); } else if ((req->request.length % dep->endpoint.maxpacket) - && (event->endpoint_number == 0)) { + && (dep->number == 0)) { + u32 transfer_size; + ret = usb_gadget_map_request(&dwc->gadget, &req->request, - event->endpoint_number); + dep->number); if (ret) { dev_dbg(dwc->dev, "failed to map request\n"); return; @@ -823,6 +812,9 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE); + transfer_size = roundup(req->request.length, + (u32) dep->endpoint.maxpacket); + dwc->ep0_bounced = true; /* @@ -830,25 +822,47 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, * DWC3_EP0_BOUNCE_SIZE we will need two chained * TRBs to handle the transfer. */ - ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, - dwc->ep0_bounce_addr, dep->endpoint.maxpacket, + ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc->ep0_bounce_addr, transfer_size, DWC3_TRBCTL_CONTROL_DATA); } else { ret = usb_gadget_map_request(&dwc->gadget, &req->request, - event->endpoint_number); + dep->number); if (ret) { dev_dbg(dwc->dev, "failed to map request\n"); return; } - ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, - req->request.dma, req->request.length, - DWC3_TRBCTL_CONTROL_DATA); + ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, + req->request.length, DWC3_TRBCTL_CONTROL_DATA); } WARN_ON(ret < 0); } +static void dwc3_ep0_do_control_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + struct dwc3_request *req; + + dep = dwc->eps[0]; + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); + dep->flags |= DWC3_EP_PENDING_REQUEST; + + if (event->endpoint_number) + dep->flags |= DWC3_EP0_DIR_IN; + return; + } + + req = next_request(&dep->request_list); + dep = dwc->eps[event->endpoint_number]; + + __dwc3_ep0_do_control_data(dwc, dep, req); +} + static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; -- cgit v1.2.3-70-g09d2 From c74c6d4a024d95e81283ee4c38be6fa7baee27f9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 4 May 2012 13:08:22 +0300 Subject: usb: dwc3: ep0: switch over to IS_ALIGNED IS_ALIGNED provides a much faster operation for checking proper size alignment then a modulo operation. Let's use it. Reported-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 15ec36eb461..477127aecb9 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -799,7 +799,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = dwc3_ep0_start_trans(dwc, dep->number, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA); - } else if ((req->request.length % dep->endpoint.maxpacket) + } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && (dep->number == 0)) { u32 transfer_size; -- cgit v1.2.3-70-g09d2 From 788a23f49686d82fa95b83ac7752d6322f1dad44 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 21 May 2012 14:22:41 +0300 Subject: usb: dwc3: ep0: align on function signature On our Transfer Not Ready handlers, only dwc3_ep0_do_control_status() had a different list of parameters. Align on the parameters in order to keep consistency. No functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 477127aecb9..75d5722db93 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -54,7 +54,7 @@ #include "gadget.h" #include "io.h" -static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); +static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req); @@ -160,7 +160,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, dwc->delayed_status = false; if (dwc->ep0state == EP0_STATUS_PHASE) - dwc3_ep0_do_control_status(dwc, 1); + __dwc3_ep0_do_control_status(dwc, dwc->eps[1]); else dev_dbg(dwc->dev, "too early for delayed status\n"); } @@ -875,10 +875,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) dwc->ctrl_req_addr, 0, type); } -static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) +static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) { - struct dwc3_ep *dep = dwc->eps[epnum]; - if (dwc->resize_fifos) { dev_dbg(dwc->dev, "starting to resize fifos\n"); dwc3_gadget_resize_tx_fifos(dwc); @@ -888,6 +886,14 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) WARN_ON(dwc3_ep0_start_control_status(dep)); } +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + __dwc3_ep0_do_control_status(dwc, dep); +} + static void dwc3_ep0_xfernotready(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { @@ -988,7 +994,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, return; } - dwc3_ep0_do_control_status(dwc, event->endpoint_number); + dwc3_ep0_do_control_status(dwc, event); } } -- cgit v1.2.3-70-g09d2 From 33b84c2c06d25a3321326d91438e106b4fd5ad9f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 21 May 2012 14:35:17 +0300 Subject: usb: dwc3: ep0: fix a typo in comment s/has/have. No functional changes, just a typo fix on a code comment. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 75d5722db93..e31bcd65e7e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -900,7 +900,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, dwc->setup_packet_pending = true; /* - * This part is very tricky: If we has just handled + * This part is very tricky: If we have just handled * XferNotReady(Setup) and we're now expecting a * XferComplete but, instead, we receive another * XferNotReady(Setup), we should STALL and restart -- cgit v1.2.3-70-g09d2 From a092532483e3200a53c8b1170b3988cc668c07ef Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 22 May 2012 10:24:11 +0300 Subject: usb: dwc3: gadget: remove trailing semicolon That semicolon doesn't do anything, it's not needed and should be removed. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3df1a1973b0..045799356a2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1032,7 +1032,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) dev_dbg(dwc->dev, "%s: failed to kick transfers\n", dep->name); } - }; + } return 0; } -- cgit v1.2.3-70-g09d2 From 2c61a8efce75951148fa07f2cd7ec53ad9d9fbb3 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Wed, 15 Feb 2012 18:56:58 -0800 Subject: usb: dwc3: add definitions for new registers This patch adds definitions for some new registers that have been added to later versions of the controller, up to v2.10a. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 94 +++++++++++++++++++++++++++++++++++++---------- drivers/usb/dwc3/gadget.h | 5 +++ 2 files changed, 80 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f69c877add0..4d5c63dae14 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -67,6 +67,7 @@ #define DWC3_DEVICE_EVENT_CONNECT_DONE 2 #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 #define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_HIBER_REQ 5 #define DWC3_DEVICE_EVENT_EOPF 6 #define DWC3_DEVICE_EVENT_SOF 7 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 @@ -171,28 +172,36 @@ #define DWC3_GCTL_PRTCAP_DEVICE 2 #define DWC3_GCTL_PRTCAP_OTG 3 -#define DWC3_GCTL_CORESOFTRESET (1 << 11) -#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) -#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) -#define DWC3_GCTL_DISSCRAMBLE (1 << 3) -#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) +#define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) +#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) +#define DWC3_GCTL_DISSCRAMBLE (1 << 3) +#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1) +#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) /* Global USB2 PHY Configuration Register */ -#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) -#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) +#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) /* Global USB3 PIPE Control Register */ -#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) -#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) +#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) /* Global TX Fifo Size Register */ -#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) -#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) +#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) /* Global HWPARAMS1 Register */ #define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 +#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 +#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) +#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) + +/* Global HWPARAMS4 Register */ +#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) +#define DWC3_MAX_HIBER_SCRATCHBUFS 15 /* Device Configuration Register */ #define DWC3_DCFG_LPM_CAP (1 << 22) @@ -206,6 +215,8 @@ #define DWC3_DCFG_LOWSPEED (2 << 0) #define DWC3_DCFG_FULLSPEED1 (3 << 0) +#define DWC3_DCFG_LPM_CAP (1 << 22) + /* Device Control Register */ #define DWC3_DCTL_RUN_STOP (1 << 31) #define DWC3_DCTL_CSFTRST (1 << 30) @@ -216,14 +227,20 @@ #define DWC3_DCTL_APPL1RES (1 << 23) -#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) -#define DWC3_DCTL_TRGTULST(n) ((n) << 17) - -#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) -#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) -#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) -#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) -#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) +/* These apply for core versions 1.87a and earlier */ +#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) +#define DWC3_DCTL_TRGTULST(n) ((n) << 17) +#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) +#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) +#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) +#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) +#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DCTL_KEEP_CONNECT (1 << 19) +#define DWC3_DCTL_L1_HIBER_EN (1 << 18) +#define DWC3_DCTL_CRS (1 << 17) +#define DWC3_DCTL_CSS (1 << 16) #define DWC3_DCTL_INITU2ENA (1 << 12) #define DWC3_DCTL_ACCEPTU2ENA (1 << 11) @@ -249,6 +266,7 @@ #define DWC3_DEVTEN_ERRTICERREN (1 << 9) #define DWC3_DEVTEN_SOFEN (1 << 7) #define DWC3_DEVTEN_EOPFEN (1 << 6) +#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5) #define DWC3_DEVTEN_WKUPEVTEN (1 << 4) #define DWC3_DEVTEN_ULSTCNGEN (1 << 3) #define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) @@ -256,7 +274,15 @@ #define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) /* Device Status Register */ +#define DWC3_DSTS_DCNRD (1 << 29) + +/* This applies for core versions 1.87a and earlier */ #define DWC3_DSTS_PWRUPREQ (1 << 24) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DSTS_RSS (1 << 25) +#define DWC3_DSTS_SSS (1 << 24) + #define DWC3_DSTS_COREIDLE (1 << 23) #define DWC3_DSTS_DEVCTRLHLT (1 << 22) @@ -280,6 +306,11 @@ #define DWC3_DGCMD_SET_LMP 0x01 #define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 #define DWC3_DGCMD_XMIT_FUNCTION 0x03 + +/* These apply for core versions 1.94a and later */ +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04 +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05 + #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 #define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a #define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c @@ -287,6 +318,15 @@ #define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1) #define DWC3_DGCMD_CMDACT (1 << 10) +#define DWC3_DGCMD_CMDIOC (1 << 8) + +/* Device Generic Command Parameter Register */ +#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0) +#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0) +#define DWC3_DGCMDPAR_RX_FIFO (0 << 5) +#define DWC3_DGCMDPAR_TX_FIFO (1 << 5) +#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0) +#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0) /* Device Endpoint Command Register */ #define DWC3_DEPCMD_PARAM_SHIFT 16 @@ -303,7 +343,10 @@ #define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) #define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) #define DWC3_DEPCMD_SETSTALL (0x04 << 0) +/* This applies for core versions 1.90a and earlier */ #define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +/* This applies for core versions 1.94a and later */ +#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0) #define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) #define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) @@ -437,6 +480,8 @@ enum dwc3_link_state { DWC3_LINK_STATE_HRESET = 0x09, DWC3_LINK_STATE_CMPLY = 0x0a, DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_RESET = 0x0e, + DWC3_LINK_STATE_RESUME = 0x0f, DWC3_LINK_STATE_MASK = 0x0f, }; @@ -455,6 +500,7 @@ enum dwc3_device_state { #define DWC3_TRBSTS_OK 0 #define DWC3_TRBSTS_MISSED_ISOC 1 #define DWC3_TRBSTS_SETUP_PENDING 2 +#define DWC3_TRB_STS_XFER_IN_PROG 4 /* TRB Control */ #define DWC3_TRB_CTRL_HWO (1 << 0) @@ -543,6 +589,14 @@ struct dwc3_request { unsigned queued:1; }; +/* + * struct dwc3_scratchpad_array - hibernation scratchpad array + * (format defined by hw) + */ +struct dwc3_scratchpad_array { + __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS]; +}; + /** * struct dwc3 - representation of our controller * @ctrl_req: usb control request which is used for ep0 @@ -624,8 +678,10 @@ struct dwc3 { #define DWC3_REVISION_180A 0x5533180a #define DWC3_REVISION_183A 0x5533183a #define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_187A 0x5533187a #define DWC3_REVISION_188A 0x5533188a #define DWC3_REVISION_190A 0x5533190a +#define DWC3_REVISION_194A 0x5533194a #define DWC3_REVISION_200A 0x5533200a #define DWC3_REVISION_202A 0x5533202a #define DWC3_REVISION_210A 0x5533210a diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 95ef6a2f776..39ced0b0851 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -66,7 +66,12 @@ struct dwc3; #define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) #define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) #define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) +/* This applies for core versions earlier than 1.94a */ #define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) +/* These apply for core versions 1.94a and later */ +#define DWC3_DEPCFG_ACTION_INIT (0 << 30) +#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30) +#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30) /* DEPXFERCFG parameter 0 */ #define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) -- cgit v1.2.3-70-g09d2 From aed430e5139af3e2c251f52db7fc6c5e2ce2b714 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 27 Apr 2012 12:52:01 +0300 Subject: usb: dwc3: gadget: reinitialize retries retries is used twice without being reinitialized. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 045799356a2..c21516aef5e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -108,6 +108,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) dwc3_writel(dwc->regs, DWC3_DCTL, reg); /* wait for a change in DSTS */ + retries = 10000; while (--retries) { reg = dwc3_readl(dwc->regs, DWC3_DSTS); -- cgit v1.2.3-70-g09d2 From d7a46a8dfc45191b39daa19c05d3ff74d1881f15 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 27 Apr 2012 12:54:05 +0300 Subject: usb: dwc3: gadget: rename phy_power() to phy_suspend() those two functions don't power PHYs, they simply put them in suspend state. Rename to reflect better what functions actually do. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c21516aef5e..852bde5cc13 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1894,30 +1894,30 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc->setup_packet_pending = false; } -static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) +static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend) { u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - if (on) - reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; - else + if (suspend) reg |= DWC3_GUSB3PIPECTL_SUSPHY; + else + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); } -static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on) +static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend) { u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - if (on) - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - else + if (suspend) reg |= DWC3_GUSB2PHYCFG_SUSPHY; + else + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); } @@ -1962,9 +1962,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) /* after reset -> Default State */ dwc->dev_state = DWC3_DEFAULT_STATE; - /* Enable PHYs */ - dwc3_gadget_usb2_phy_power(dwc, true); - dwc3_gadget_usb3_phy_power(dwc, true); + /* Resume PHYs */ + dwc3_gadget_usb2_phy_suspend(dwc, false); + dwc3_gadget_usb3_phy_suspend(dwc, false); if (dwc->gadget.speed != USB_SPEED_UNKNOWN) dwc3_disconnect_gadget(dwc); @@ -2010,16 +2010,16 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) dwc3_writel(dwc->regs, DWC3_GCTL, reg); } -static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed) +static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed) { switch (speed) { case USB_SPEED_SUPER: - dwc3_gadget_usb2_phy_power(dwc, false); + dwc3_gadget_usb2_phy_suspend(dwc, true); break; case USB_SPEED_HIGH: case USB_SPEED_FULL: case USB_SPEED_LOW: - dwc3_gadget_usb3_phy_power(dwc, false); + dwc3_gadget_usb3_phy_suspend(dwc, true); break; } } @@ -2082,8 +2082,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) break; } - /* Disable unneded PHY */ - dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); + /* Suspend unneded PHY */ + dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed); dep = dwc->eps[0]; ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); -- cgit v1.2.3-70-g09d2 From 802fde983e8a3391e059bd41fc272993ae642816 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 27 Apr 2012 13:10:52 +0300 Subject: usb: dwc3: support new revisions of DWC3 core Recent cores (>= 1.94a) have a set of new features, commands and a slightly different programming model. This patch aims to support those changes. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 83 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 852bde5cc13..04048333ec1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) int retries = 10000; u32 reg; + /* + * Wait until device controller is ready. Only applies to 1.94a and + * later RTL. + */ + if (dwc->revision >= DWC3_REVISION_194A) { + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (reg & DWC3_DSTS_DCNRD) + udelay(5); + else + break; + } + + if (retries <= 0) + return -ETIMEDOUT; + } + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; @@ -107,6 +124,13 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) reg |= DWC3_DCTL_ULSTCHNGREQ(state); dwc3_writel(dwc->regs, DWC3_DCTL, reg); + /* + * The following code is racy when called from dwc3_gadget_wakeup, + * and is not needed, at least on newer versions + */ + if (dwc->revision >= DWC3_REVISION_194A) + return 0; + /* wait for a change in DSTS */ retries = 10000; while (--retries) { @@ -266,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd) return "Clear Stall"; case DWC3_DEPCMD_SETSTALL: return "Set Stall"; - case DWC3_DEPCMD_GETSEQNUMBER: - return "Get Data Sequence Number"; + case DWC3_DEPCMD_GETEPSTATE: + return "Get Endpoint State"; case DWC3_DEPCMD_SETTRANSFRESOURCE: return "Set Endpoint Transfer Resource"; case DWC3_DEPCMD_SETEPCONFIG: @@ -1280,9 +1304,12 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) goto out; } - /* write zeroes to Link Change Request */ - reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + /* Recent versions do this automatically */ + if (dwc->revision < DWC3_REVISION_194A) { + /* write zeroes to Link Change Request */ + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } /* poll until Link State changes to ON */ timeout = jiffies + msecs_to_jiffies(100); @@ -1326,9 +1353,14 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { - reg &= ~DWC3_DCTL_TRGTULST_MASK; - reg |= (DWC3_DCTL_RUN_STOP - | DWC3_DCTL_TRGTULST_RX_DET); + if (dwc->revision <= DWC3_REVISION_187A) { + reg &= ~DWC3_DCTL_TRGTULST_MASK; + reg |= DWC3_DCTL_TRGTULST_RX_DET; + } + + if (dwc->revision >= DWC3_REVISION_194A) + reg &= ~DWC3_DCTL_KEEP_CONNECT; + reg |= DWC3_DCTL_RUN_STOP; } else { reg &= ~DWC3_DCTL_RUN_STOP; } @@ -1468,6 +1500,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g, return 0; } + static const struct usb_gadget_ops dwc3_gadget_ops = { .get_frame = dwc3_gadget_get_frame, .wakeup = dwc3_gadget_wakeup, @@ -1962,9 +1995,12 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) /* after reset -> Default State */ dwc->dev_state = DWC3_DEFAULT_STATE; - /* Resume PHYs */ - dwc3_gadget_usb2_phy_suspend(dwc, false); - dwc3_gadget_usb3_phy_suspend(dwc, false); + /* Recent versions support automatic phy suspend and don't need this */ + if (dwc->revision < DWC3_REVISION_194A) { + /* Resume PHYs */ + dwc3_gadget_usb2_phy_suspend(dwc, false); + dwc3_gadget_usb3_phy_suspend(dwc, false); + } if (dwc->gadget.speed != USB_SPEED_UNKNOWN) dwc3_disconnect_gadget(dwc); @@ -2082,8 +2118,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) break; } - /* Suspend unneded PHY */ - dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed); + /* Recent versions support automatic phy suspend and don't need this */ + if (dwc->revision < DWC3_REVISION_194A) { + /* Suspend unneeded PHY */ + dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed); + } dep = dwc->eps[0]; ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); @@ -2389,6 +2428,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) DWC3_DEVTEN_DISCONNEVTEN); dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); + /* Enable USB2 LPM and automatic phy suspend only on recent versions */ + if (dwc->revision >= DWC3_REVISION_194A) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg |= DWC3_DCFG_LPM_CAP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); + + /* TODO: This should be configurable */ + reg |= DWC3_DCTL_HIRD_THRES(31); + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc3_gadget_usb2_phy_suspend(dwc, true); + dwc3_gadget_usb3_phy_suspend(dwc, true); + } + ret = device_register(&dwc->gadget.dev); if (ret) { dev_err(dwc->dev, "failed to register gadget device\n"); -- cgit v1.2.3-70-g09d2 From 7acd85e0eb2ed300edf123178445237059b35fb9 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 27 Apr 2012 14:28:02 +0300 Subject: usb: dwc3: core: make sure evt->lpos is correctly initialized The same event buffers will be reused when coming out of hibernation, so we must reinitialize them properly to avoid any mistakes. While at that, also take dwc3_event_buffers_setup() out of __devinit section. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 1040bdb8dc8..49c060205c9 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -255,7 +255,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) +static int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; int n; @@ -266,6 +266,8 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) evt->buf, (unsigned long long) evt->dma, evt->length); + evt->lpos = 0; + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), lower_32_bits(evt->dma)); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), @@ -285,6 +287,9 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) for (n = 0; n < dwc->num_event_buffers; n++) { evt = dwc->ev_buffs[n]; + + evt->lpos = 0; + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); -- cgit v1.2.3-70-g09d2 From fcc023c726b5879d8f3f0f0f48c45d09055272c4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 May 2012 10:27:56 +0300 Subject: usb: dwc3: gadget: prevent DCTL register corruption If we don't read out the contents of the register (in order to reinitialize 'reg' variable) we will be writing unknown contents to the DCTL register whenever we try to use dwc3_gadget_wakeup() function. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 04048333ec1..867c476853e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1307,6 +1307,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) /* Recent versions do this automatically */ if (dwc->revision < DWC3_REVISION_194A) { /* write zeroes to Link Change Request */ + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; dwc3_writel(dwc->regs, DWC3_DCTL, reg); } -- cgit v1.2.3-70-g09d2 From c4430a26948b84c0d820e83f8c640229f960d3b6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 May 2012 10:30:01 +0300 Subject: usb: dwc3: gadget: disable U1/U2 on disconnect If we get a disconnect IRQ, we should take the core out of low power mode so we can reconnect afterwards. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 867c476853e..054ee5ec5c1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1906,11 +1906,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { + int reg; + dev_vdbg(dwc->dev, "%s\n", __func__); -#if 0 - XXX - U1/U2 is powersave optimization. Skip it for now. Anyway we need to - enable it before we can disable it. reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; @@ -1918,7 +1916,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) reg &= ~DWC3_DCTL_INITU2ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); -#endif dwc3_stop_active_transfers(dwc); dwc3_disconnect_gadget(dwc); -- cgit v1.2.3-70-g09d2 From 5cbe8c220c1e126dd0855ad57fe4491b267132b9 Mon Sep 17 00:00:00 2001 From: Gerard CAUVY Date: Thu, 24 May 2012 12:47:36 +0300 Subject: usb: dwc3: gadget: move AcceptU1Ena and AcceptU2Ena to Reset IRQ According to the databook, the DWC3 Core will reset those bits to 0 on USB Bus Reset. This means we must re-enable those bits on every reset interrupt. Because we will always get a Reset interrupt after loading a gadget driver, we can, instead of re-enabling something that was just lost, move the handling of those bits to the Reset Interrupt. This patch fixes USB30CV U1/U2 Test. Signed-off-by: Gerard CAUVY Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 054ee5ec5c1..a9fc7c4a5a2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2006,6 +2006,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA); + reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->test_mode = false; @@ -2410,10 +2411,6 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) reg |= DWC3_DCFG_LPM_CAP; dwc3_writel(dwc->regs, DWC3_DCFG, reg); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); - /* Enable all but Start and End of Frame IRQs */ reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | DWC3_DEVTEN_EVNTOVERFLOWEN | -- cgit v1.2.3-70-g09d2 From ef21ede65ee3e0dfd8f2cb37de8892816e2f1257 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 31 May 2012 10:29:49 +0300 Subject: usb: dwc3: ep0: simplify error handling on dwc3_ep0_inspect_setup There's no need for returning early. Instead, we can call dwc3_ep0_stall_and_restart() conditionally. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e31bcd65e7e..83fcf392dda 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -641,11 +641,11 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { struct usb_ctrlrequest *ctrl = dwc->ctrl_req; - int ret; + int ret = -EINVAL; u32 len; if (!dwc->gadget_driver) - goto err; + goto out; len = le16_to_cpu(ctrl->wLength); if (!len) { @@ -666,11 +666,9 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, if (ret == USB_GADGET_DELAYED_STATUS) dwc->delayed_status = true; - if (ret >= 0) - return; - -err: - dwc3_ep0_stall_and_restart(dwc); +out: + if (ret < 0) + dwc3_ep0_stall_and_restart(dwc); } static void dwc3_ep0_complete_data(struct dwc3 *dwc, -- cgit v1.2.3-70-g09d2 From c8cf7af452ad2eb49cad13450090cdc432b5a06d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 31 May 2012 11:00:28 +0300 Subject: usb: dwc3: ep0: be careful with endianness on SetSEL command USB is always little endian, but this driver could run on non little endian cpus. Let's be carefull with that. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 83fcf392dda..3185fe14591 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -515,8 +515,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) dwc->u1sel = timing.u1sel; dwc->u1pel = timing.u1pel; - dwc->u2sel = timing.u2sel; - dwc->u2pel = timing.u2pel; + dwc->u2sel = le16_to_cpu(timing.u2sel); + dwc->u2pel = le16_to_cpu(timing.u2pel); reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (reg & DWC3_DCTL_INITU2ENA) -- cgit v1.2.3-70-g09d2 From 85a781019b9b4b935f6b1792a1f48f1a3aee988f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 31 May 2012 12:32:37 +0300 Subject: usb: dwc3: ep0: rename dwc3_ep0_complete_req to dwc3_ep0_complete_status That's a much more intuitive name as that function is only called at the completion of a Status Phase. It also matches dwc3_ep0_complete_data() for the completion of Data Phase. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 3185fe14591..90eb1ba6545 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -722,7 +722,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, } } -static void dwc3_ep0_complete_req(struct dwc3 *dwc, +static void dwc3_ep0_complete_status(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { struct dwc3_request *r; @@ -773,7 +773,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, case EP0_STATUS_PHASE: dev_vdbg(dwc->dev, "Status Phase\n"); - dwc3_ep0_complete_req(dwc, event); + dwc3_ep0_complete_status(dwc, event); break; default: WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); -- cgit v1.2.3-70-g09d2 From ae3725716edf5a73ab66831c0808542c21fa3bba Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 21 May 2012 12:01:11 -0700 Subject: usb: gadget: omap_udc: Remove omap2 support There are no active users of this code for omap2 as the boards in use have either TUSB or MUSB controller. While at it, also fix warnings related to uninitialized dc_clk and hhc_clk. Cc: linux-usb@vger.kernel.org Cc: Kyungmin Park Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi --- drivers/usb/gadget/omap_udc.c | 113 +++++++++--------------------------------- 1 file changed, 23 insertions(+), 90 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 7ba32469c5b..24d81dcb7b4 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -61,9 +61,6 @@ #define DMA_ADDR_INVALID (~(dma_addr_t)0) -#define OMAP2_DMA_CH(ch) (((ch) - 1) << 1) -#define OMAP24XX_DMA(name, ch) (OMAP24XX_DMA_##name + OMAP2_DMA_CH(ch)) - /* * The OMAP UDC needs _very_ early endpoint setup: before enabling the * D+ pullup to allow enumeration. That's too early for the gadget @@ -536,12 +533,8 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req) : OMAP_DMA_SYNC_ELEMENT; int dma_trigger = 0; - if (cpu_is_omap24xx()) - dma_trigger = OMAP24XX_DMA(USB_W2FC_TX0, ep->dma_channel); - /* measure length in either bytes or packets */ if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC) - || (cpu_is_omap24xx() && length < ep->maxpacket) || (cpu_is_omap15xx() && length < ep->maxpacket)) { txdma_ctrl = UDC_TXN_EOT | length; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, @@ -600,28 +593,14 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) int dma_trigger = 0; u16 w; - if (cpu_is_omap24xx()) - dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel); - - /* NOTE: we filtered out "short reads" before, so we know - * the buffer has only whole numbers of packets. - * except MODE SELECT(6) sent the 24 bytes data in OMAP24XX DMA mode - */ - if (cpu_is_omap24xx() && packets < ep->maxpacket) { - omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, - packets, 1, OMAP_DMA_SYNC_ELEMENT, - dma_trigger, 0); - req->dma_bytes = packets; - } else { - /* set up this DMA transfer, enable the fifo, start */ - packets /= ep->ep.maxpacket; - packets = min(packets, (unsigned)UDC_RXN_TC + 1); - req->dma_bytes = packets * ep->ep.maxpacket; - omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, - ep->ep.maxpacket >> 1, packets, - OMAP_DMA_SYNC_ELEMENT, - dma_trigger, 0); - } + /* set up this DMA transfer, enable the fifo, start */ + packets /= ep->ep.maxpacket; + packets = min(packets, (unsigned)UDC_RXN_TC + 1); + req->dma_bytes = packets * ep->ep.maxpacket; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, + ep->ep.maxpacket >> 1, packets, + OMAP_DMA_SYNC_ELEMENT, + dma_trigger, 0); omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, 0, 0); @@ -760,10 +739,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ep->dma_channel = channel; if (is_in) { - if (cpu_is_omap24xx()) - dma_channel = OMAP24XX_DMA(USB_W2FC_TX0, channel); - else - dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel; + dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel; status = omap_request_dma(dma_channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { @@ -780,11 +756,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) 0, 0); } } else { - if (cpu_is_omap24xx()) - dma_channel = OMAP24XX_DMA(USB_W2FC_RX0, channel); - else - dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel; - + dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel; status = omap_request_dma(dma_channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { @@ -808,7 +780,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ); /* channel type P: hw synch (fifo) */ - if (cpu_class_is_omap1() && !cpu_is_omap15xx()) + if (!cpu_is_omap15xx()) omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P); } @@ -928,13 +900,11 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* this isn't bogus, but OMAP DMA isn't the only hardware to * have a hard time with partial packet reads... reject it. - * Except OMAP2 can handle the small packets. */ if (use_dma && ep->has_dma && ep->bEndpointAddress != 0 && (ep->bEndpointAddress & USB_DIR_IN) == 0 - && !cpu_class_is_omap2() && (req->req.length % ep->ep.maxpacket) != 0) { DBG("%s, no partial packet OUT reads\n", __func__); return -EMSGSIZE; @@ -2090,10 +2060,6 @@ static inline int machine_without_vbus_sense(void) { return (machine_is_omap_innovator() || machine_is_omap_osk() - || machine_is_omap_apollon() -#ifndef CONFIG_MACH_OMAP_H4_OTG - || machine_is_omap_h4() -#endif || machine_is_sx1() || cpu_is_omap7xx() /* No known omap7xx boards with vbus sense */ ); @@ -2307,12 +2273,9 @@ static int proc_otg_show(struct seq_file *s) u32 trans = 0; char *ctrl_name = "(UNKNOWN)"; - /* XXX This needs major revision for OMAP2+ */ tmp = omap_readl(OTG_REV); - if (cpu_class_is_omap1()) { - ctrl_name = "tranceiver_ctrl"; - trans = omap_readw(USB_TRANSCEIVER_CTRL); - } + ctrl_name = "tranceiver_ctrl"; + trans = omap_readw(USB_TRANSCEIVER_CTRL); seq_printf(s, "\nOTG rev %d.%d, %s %05x\n", tmp >> 4, tmp & 0xf, ctrl_name, trans); tmp = omap_readw(OTG_SYSCON_1); @@ -2399,14 +2362,12 @@ static int proc_udc_show(struct seq_file *s, void *_) HMC, udc->transceiver ? udc->transceiver->label - : ((cpu_is_omap1710() || cpu_is_omap24xx()) + : (cpu_is_omap1710() ? "external" : "(none)")); - if (cpu_class_is_omap1()) { - seq_printf(s, "ULPD control %04x req %04x status %04x\n", - omap_readw(ULPD_CLOCK_CTRL), - omap_readw(ULPD_SOFT_REQ), - omap_readw(ULPD_STATUS_REQ)); - } + seq_printf(s, "ULPD control %04x req %04x status %04x\n", + omap_readw(ULPD_CLOCK_CTRL), + omap_readw(ULPD_SOFT_REQ), + omap_readw(ULPD_STATUS_REQ)); /* OTG controller registers */ if (!cpu_is_omap15xx()) @@ -2591,7 +2552,7 @@ omap_ep_setup(char *name, u8 addr, u8 type, * and ignored for PIO-IN on newer chips * (for more reliable behavior) */ - if (!use_dma || cpu_is_omap15xx() || cpu_is_omap24xx()) + if (!use_dma || cpu_is_omap15xx()) dbuf = 0; switch (maxp) { @@ -2795,8 +2756,8 @@ static int __init omap_udc_probe(struct platform_device *pdev) struct usb_phy *xceiv = NULL; const char *type = NULL; struct omap_usb_config *config = pdev->dev.platform_data; - struct clk *dc_clk; - struct clk *hhc_clk; + struct clk *dc_clk = NULL; + struct clk *hhc_clk = NULL; /* NOTE: "knows" the order of the resources! */ if (!request_mem_region(pdev->resource[0].start, @@ -2816,16 +2777,6 @@ static int __init omap_udc_probe(struct platform_device *pdev) udelay(100); } - if (cpu_is_omap24xx()) { - dc_clk = clk_get(&pdev->dev, "usb_fck"); - hhc_clk = clk_get(&pdev->dev, "usb_l4_ick"); - BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk)); - /* can't use omap_udc_enable_clock yet */ - clk_enable(dc_clk); - clk_enable(hhc_clk); - udelay(100); - } - if (cpu_is_omap7xx()) { dc_clk = clk_get(&pdev->dev, "usb_dc_ck"); hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck"); @@ -2875,14 +2826,6 @@ static int __init omap_udc_probe(struct platform_device *pdev) hmc = HMC_1610; - if (cpu_is_omap24xx()) { - /* this could be transceiverless in one of the - * "we don't need to know" modes. - */ - type = "external"; - goto known; - } - switch (hmc) { case 0: /* POWERUP DEFAULT == 0 */ case 4: @@ -2921,7 +2864,7 @@ bad_on_1710: goto cleanup0; } } -known: + INFO("hmc mode %d, %s transceiver\n", hmc, type); /* a "gadget" abstracts/virtualizes the controller */ @@ -2975,16 +2918,6 @@ known: clk_disable(dc_clk); } - if (cpu_is_omap24xx()) { - udc->dc_clk = dc_clk; - udc->hhc_clk = hhc_clk; - /* FIXME OMAP2 don't release hhc & dc clock */ -#if 0 - clk_disable(hhc_clk); - clk_disable(dc_clk); -#endif - } - create_proc_file(); status = device_add(&udc->gadget.dev); if (status) @@ -3013,7 +2946,7 @@ cleanup0: if (xceiv) usb_put_transceiver(xceiv); - if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { + if (cpu_is_omap16xx() || cpu_is_omap7xx()) { clk_disable(hhc_clk); clk_disable(dc_clk); clk_put(hhc_clk); -- cgit v1.2.3-70-g09d2 From 80dd1358d33e6c85018e1e1c5d78b29ac466221a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 May 2012 14:26:09 +0300 Subject: usb: gadget: omap_udc: make checkpatch.pl happy This patch is just a cleanup patch to make checkpatch.pl a little happier with the omap_udc.c driver. No functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/omap_udc.c | 201 +++++++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 24d81dcb7b4..aa2d174a252 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -36,9 +36,9 @@ #include #include #include +#include #include -#include #include #include #include @@ -84,14 +84,14 @@ #ifdef USE_ISO static unsigned fifo_mode = 3; #else -static unsigned fifo_mode = 0; +static unsigned fifo_mode; #endif /* "modprobe omap_udc fifo_mode=42", or else as a kernel * boot parameter "omap_udc:fifo_mode=42" */ -module_param (fifo_mode, uint, 0); -MODULE_PARM_DESC (fifo_mode, "endpoint configuration"); +module_param(fifo_mode, uint, 0); +MODULE_PARM_DESC(fifo_mode, "endpoint configuration"); #ifdef USE_DMA static bool use_dma = 1; @@ -99,8 +99,8 @@ static bool use_dma = 1; /* "modprobe omap_udc use_dma=y", or else as a kernel * boot parameter "omap_udc:use_dma=y" */ -module_param (use_dma, bool, 0); -MODULE_PARM_DESC (use_dma, "enable/disable DMA"); +module_param(use_dma, bool, 0); +MODULE_PARM_DESC(use_dma, "enable/disable DMA"); #else /* !USE_DMA */ /* save a bit of code */ @@ -108,8 +108,8 @@ MODULE_PARM_DESC (use_dma, "enable/disable DMA"); #endif /* !USE_DMA */ -static const char driver_name [] = "omap_udc"; -static const char driver_desc [] = DRIVER_DESC; +static const char driver_name[] = "omap_udc"; +static const char driver_desc[] = DRIVER_DESC; /*-------------------------------------------------------------------------*/ @@ -247,7 +247,7 @@ static int omap_ep_disable(struct usb_ep *_ep) spin_lock_irqsave(&ep->udc->lock, flags); ep->ep.desc = NULL; - nuke (ep, -ESHUTDOWN); + nuke(ep, -ESHUTDOWN); ep->ep.maxpacket = ep->maxpacket; ep->has_dma = 0; omap_writew(UDC_SET_HALT, UDC_CTRL); @@ -270,7 +270,7 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) req = kzalloc(sizeof(*req), gfp_flags); if (req) { req->req.dma = DMA_ADDR_INVALID; - INIT_LIST_HEAD (&req->queue); + INIT_LIST_HEAD(&req->queue); } return &req->req; } @@ -281,7 +281,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) struct omap_req *req = container_of(_req, struct omap_req, req); if (_req) - kfree (req); + kfree(req); } /*-------------------------------------------------------------------------*/ @@ -361,10 +361,10 @@ write_packet(u8 *buf, struct omap_req *req, unsigned max) return len; } -// FIXME change r/w fifo calling convention +/* FIXME change r/w fifo calling convention */ -// return: 0 = still running, 1 = completed, negative = errno +/* return: 0 = still running, 1 = completed, negative = errno */ static int write_fifo(struct omap_ep *ep, struct omap_req *req) { u8 *buf; @@ -426,7 +426,7 @@ read_packet(u8 *buf, struct omap_req *req, unsigned avail) return len; } -// return: 0 = still running, 1 = queue empty, negative = errno +/* return: 0 = still running, 1 = queue empty, negative = errno */ static int read_fifo(struct omap_ep *ep, struct omap_req *req) { u8 *buf; @@ -662,7 +662,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) } omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC); - if (!list_empty (&ep->queue)) { + if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); next_in_dma(ep, req); @@ -681,7 +681,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) } omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC); - if (!list_empty (&ep->queue)) { + if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); next_out_dma(ep, req); @@ -954,7 +954,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) int is_in; if (ep->bEndpointAddress == 0) { - if (!udc->ep0_pending || !list_empty (&ep->queue)) { + if (!udc->ep0_pending || !list_empty(&ep->queue)) { spin_unlock_irqrestore(&udc->lock, flags); return -EL2HLT; } @@ -981,7 +981,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) * always an IN ... even for IN transfers, * a weird case which seem to stall OMAP. */ - omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); + omap_writew(UDC_EP_SEL | UDC_EP_DIR, + UDC_EP_NUM); omap_writew(UDC_CLR_EP, UDC_CTRL); omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); omap_writew(UDC_EP_DIR, UDC_EP_NUM); @@ -993,7 +994,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* non-empty DATA stage */ } else if (is_in) { - omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); + omap_writew(UDC_EP_SEL | UDC_EP_DIR, + UDC_EP_NUM); } else { if (udc->ep0_setup) goto irq_wait; @@ -1041,7 +1043,7 @@ static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) spin_lock_irqsave(&ep->udc->lock, flags); /* make sure it's actually queued on this endpoint */ - list_for_each_entry (req, &ep->queue, queue) { + list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) break; } @@ -1148,8 +1150,8 @@ static struct usb_ep_ops omap_ep_ops = { .dequeue = omap_ep_dequeue, .set_halt = omap_ep_set_halt, - // fifo_status ... report bytes in fifo - // fifo_flush ... flush fifo + /* fifo_status ... report bytes in fifo */ + /* fifo_flush ... flush fifo */ }; /*-------------------------------------------------------------------------*/ @@ -1379,7 +1381,7 @@ static void udc_quiesce(struct omap_udc *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; nuke(&udc->ep[0], -ESHUTDOWN); - list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) nuke(ep, -ESHUTDOWN); } @@ -1495,7 +1497,8 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) /* read next OUT packet of request, maybe * reactiviting the fifo; stall on errors. */ - if (!req || (stat = read_fifo(ep0, req)) < 0) { + stat = read_fifo(ep0, req); + if (!req || stat < 0) { omap_writew(UDC_STALL_CMD, UDC_SYSCON2); udc->ep0_pending = 0; stat = 0; @@ -1628,7 +1631,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) /* this has rude side-effects (aborts) and * can't really work if DMA-IN is active */ - DBG("%s host set_halt, NYET \n", ep->name); + DBG("%s host set_halt, NYET\n", ep->name); goto do_stall; } use_ep(ep, 0); @@ -1719,7 +1722,7 @@ delegate: */ udc->ep0_setup = 1; spin_unlock(&udc->lock); - status = udc->driver->setup (&udc->gadget, &u.r); + status = udc->driver->setup(&udc->gadget, &u.r); spin_lock(&udc->lock); udc->ep0_setup = 0; } @@ -1764,7 +1767,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) VDBG("connect\n"); if (!udc->transceiver) pullup_enable(udc); - // if (driver->connect) call it + /* if (driver->connect) call it */ } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { udc->gadget.speed = USB_SPEED_UNKNOWN; if (!udc->transceiver) @@ -1796,7 +1799,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) } if (change & UDC_SUS) { if (udc->gadget.speed != USB_SPEED_UNKNOWN) { - // FIXME tell isp1301 to suspend/resume (?) + /* FIXME tell isp1301 to suspend/resume (?) */ if (devstat & UDC_SUS) { VDBG("suspend\n"); update_otg(udc); @@ -1999,7 +2002,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) spin_lock_irqsave(&udc->lock, flags); /* handle all non-DMA ISO transfers */ - list_for_each_entry (ep, &udc->iso, iso) { + list_for_each_entry(ep, &udc->iso, iso) { u16 stat; struct omap_req *req; @@ -2058,11 +2061,11 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) static inline int machine_without_vbus_sense(void) { - return (machine_is_omap_innovator() + return machine_is_omap_innovator() || machine_is_omap_osk() || machine_is_sx1() - || cpu_is_omap7xx() /* No known omap7xx boards with vbus sense */ - ); + /* No known omap7xx boards with vbus sense */ + || cpu_is_omap7xx(); } static int omap_udc_start(struct usb_gadget_driver *driver, @@ -2076,7 +2079,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver, if (!udc) return -ENODEV; if (!driver - // FIXME if otg, check: driver->is_otg + /* FIXME if otg, check: driver->is_otg */ || driver->max_speed < USB_SPEED_FULL || !bind || !driver->setup) return -EINVAL; @@ -2088,7 +2091,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver, } /* reset state */ - list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { ep->irqs = 0; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) continue; @@ -2126,7 +2129,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver, if (status < 0) { ERR("can't bind to transceiver\n"); if (driver->unbind) { - driver->unbind (&udc->gadget); + driver->unbind(&udc->gadget); udc->gadget.dev.driver = NULL; udc->driver = NULL; } @@ -2134,9 +2137,9 @@ static int omap_udc_start(struct usb_gadget_driver *driver, } } else { if (can_pullup(udc)) - pullup_enable (udc); + pullup_enable(udc); else - pullup_disable (udc); + pullup_disable(udc); } /* boards that don't have VBUS sensing can't autogate 48MHz; @@ -2195,7 +2198,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver) static const char proc_filename[] = "driver/udc"; #define FOURBITS "%s%s%s%s" -#define EIGHTBITS FOURBITS FOURBITS +#define EIGHTBITS "%s%s%s%s%s%s%s%s" static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) { @@ -2217,12 +2220,21 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", ep->name, buf, ep->double_buf ? "dbuf " : "", - ({char *s; switch(ep->ackwait){ - case 0: s = ""; break; - case 1: s = "(ackw) "; break; - case 2: s = "(ackw2) "; break; - default: s = "(?) "; break; - } s;}), + ({ char *s; + switch (ep->ackwait) { + case 0: + s = ""; + break; + case 1: + s = "(ackw) "; + break; + case 2: + s = "(ackw2) "; + break; + default: + s = "(?) "; + break; + } s; }), ep->irqs, stat_flg, (stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "", (stat_flg & UDC_MISS_IN) ? "miss_in " : "", @@ -2238,10 +2250,10 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) (stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "", (stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : ""); - if (list_empty (&ep->queue)) + if (list_empty(&ep->queue)) seq_printf(s, "\t(queue empty)\n"); else - list_for_each_entry (req, &ep->queue, queue) { + list_for_each_entry(req, &ep->queue, queue) { unsigned length = req->req.actual; if (use_dma && buf[0]) { @@ -2259,11 +2271,16 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) static char *trx_mode(unsigned m, int enabled) { switch (m) { - case 0: return enabled ? "*6wire" : "unused"; - case 1: return "4wire"; - case 2: return "3wire"; - case 3: return "6wire"; - default: return "unknown"; + case 0: + return enabled ? "*6wire" : "unused"; + case 1: + return "4wire"; + case 2: + return "3wire"; + case 3: + return "6wire"; + default: + return "unknown"; } } @@ -2295,7 +2312,7 @@ static int proc_otg_show(struct seq_file *s) " b_ase_brst=%d hmc=%d\n", tmp, (tmp & OTG_EN) ? " otg_en" : "", (tmp & USBX_SYNCHRO) ? " synchro" : "", - // much more SRP stuff + /* much more SRP stuff */ (tmp & SRP_DATA) ? " srp_data" : "", (tmp & SRP_VBUS) ? " srp_vbus" : "", (tmp & OTG_PADEN) ? " otg_paden" : "", @@ -2383,7 +2400,7 @@ static int proc_udc_show(struct seq_file *s, void *_) (tmp & UDC_SELF_PWR) ? " self_pwr" : "", (tmp & UDC_SOFF_DIS) ? " soff_dis" : "", (tmp & UDC_PULLUP_EN) ? " PULLUP" : ""); - // syscon2 is write-only + /* syscon2 is write-only */ /* UDC controller registers */ if (!(tmp & UDC_PULLUP_EN)) { @@ -2467,7 +2484,7 @@ static int proc_udc_show(struct seq_file *s, void *_) if (tmp & UDC_ATT) { proc_ep_show(s, &udc->ep[0]); if (tmp & UDC_ADD) { - list_for_each_entry (ep, &udc->gadget.ep_list, + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { if (ep->ep.desc) proc_ep_show(s, ep); @@ -2536,14 +2553,29 @@ omap_ep_setup(char *name, u8 addr, u8 type, /* chip setup ... bit values are same for IN, OUT */ if (type == USB_ENDPOINT_XFER_ISOC) { switch (maxp) { - case 8: epn_rxtx = 0 << 12; break; - case 16: epn_rxtx = 1 << 12; break; - case 32: epn_rxtx = 2 << 12; break; - case 64: epn_rxtx = 3 << 12; break; - case 128: epn_rxtx = 4 << 12; break; - case 256: epn_rxtx = 5 << 12; break; - case 512: epn_rxtx = 6 << 12; break; - default: BUG(); + case 8: + epn_rxtx = 0 << 12; + break; + case 16: + epn_rxtx = 1 << 12; + break; + case 32: + epn_rxtx = 2 << 12; + break; + case 64: + epn_rxtx = 3 << 12; + break; + case 128: + epn_rxtx = 4 << 12; + break; + case 256: + epn_rxtx = 5 << 12; + break; + case 512: + epn_rxtx = 6 << 12; + break; + default: + BUG(); } epn_rxtx |= UDC_EPN_RX_ISO; dbuf = 1; @@ -2556,11 +2588,20 @@ omap_ep_setup(char *name, u8 addr, u8 type, dbuf = 0; switch (maxp) { - case 8: epn_rxtx = 0 << 12; break; - case 16: epn_rxtx = 1 << 12; break; - case 32: epn_rxtx = 2 << 12; break; - case 64: epn_rxtx = 3 << 12; break; - default: BUG(); + case 8: + epn_rxtx = 0 << 12; + break; + case 16: + epn_rxtx = 1 << 12; + break; + case 32: + epn_rxtx = 2 << 12; + break; + case 64: + epn_rxtx = 3 << 12; + break; + default: + BUG(); } if (dbuf && addr) epn_rxtx |= UDC_EPN_RX_DB; @@ -2600,7 +2641,7 @@ omap_ep_setup(char *name, u8 addr, u8 type, ep->ep.name = ep->name; ep->ep.ops = &omap_ep_ops; ep->ep.maxpacket = ep->maxpacket = maxp; - list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list); + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); return buf; } @@ -2608,7 +2649,7 @@ omap_ep_setup(char *name, u8 addr, u8 type, static void omap_udc_release(struct device *dev) { complete(udc->done); - kfree (udc); + kfree(udc); udc = NULL; } @@ -2626,13 +2667,13 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) omap_writew(0, UDC_TXDMA_CFG); /* UDC_PULLUP_EN gates the chip clock */ - // OTG_SYSCON_1 |= DEV_IDLE_EN; + /* OTG_SYSCON_1 |= DEV_IDLE_EN; */ udc = kzalloc(sizeof(*udc), GFP_KERNEL); if (!udc) return -ENOMEM; - spin_lock_init (&udc->lock); + spin_lock_init(&udc->lock); udc->gadget.ops = &omap_gadget_ops; udc->gadget.ep0 = &udc->ep[0].ep; @@ -2662,13 +2703,13 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) omap_writew(0, UDC_EP_TX(tmp)); } -#define OMAP_BULK_EP(name,addr) \ +#define OMAP_BULK_EP(name, addr) \ buf = omap_ep_setup(name "-bulk", addr, \ USB_ENDPOINT_XFER_BULK, buf, 64, 1); -#define OMAP_INT_EP(name,addr, maxp) \ +#define OMAP_INT_EP(name, addr, maxp) \ buf = omap_ep_setup(name "-int", addr, \ USB_ENDPOINT_XFER_INT, buf, maxp, 0); -#define OMAP_ISO_EP(name,addr, maxp) \ +#define OMAP_ISO_EP(name, addr, maxp) \ buf = omap_ep_setup(name "-iso", addr, \ USB_ENDPOINT_XFER_ISOC, buf, maxp, 1); @@ -2869,11 +2910,11 @@ bad_on_1710: /* a "gadget" abstracts/virtualizes the controller */ status = omap_udc_setup(pdev, xceiv); - if (status) { + if (status) goto cleanup0; - } + xceiv = NULL; - // "udc" is now valid + /* "udc" is now valid */ pullup_disable(udc); #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) udc->gadget.is_otg = (config->otg != 0); @@ -2887,7 +2928,7 @@ bad_on_1710: /* USB general purpose IRQ: ep0, state changes, dma, etc */ status = request_irq(pdev->resource[1].start, omap_udc_irq, - IRQF_SAMPLE_RANDOM, driver_name, udc); + 0, driver_name, udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[1].start, status); @@ -2896,7 +2937,7 @@ bad_on_1710: /* USB "non-iso" IRQ (PIO for all but ep0) */ status = request_irq(pdev->resource[2].start, omap_udc_pio_irq, - IRQF_SAMPLE_RANDOM, "omap_udc pio", udc); + 0, "omap_udc pio", udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[2].start, status); @@ -2939,7 +2980,7 @@ cleanup2: free_irq(pdev->resource[1].start, udc); cleanup1: - kfree (udc); + kfree(udc); udc = NULL; cleanup0: -- cgit v1.2.3-70-g09d2 From 5b6d84b78930a4fa3f8380dc0ec4ac427d620201 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 May 2012 14:27:52 +0300 Subject: usb: gadget: omap_udc: remove useless print that print isn't needed at all. Remove it and move the use_dma reinitialization to probe() function. Note that ideally we would drop all cpu_is_* and machine_is_* checks from this driver instead. Later patches will come to get rid of those. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/omap_udc.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index aa2d174a252..5e7b8912ef3 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2800,6 +2800,9 @@ static int __init omap_udc_probe(struct platform_device *pdev) struct clk *dc_clk = NULL; struct clk *hhc_clk = NULL; + if (cpu_is_omap7xx()) + use_dma = 0; + /* NOTE: "knows" the order of the resources! */ if (!request_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1, @@ -3096,16 +3099,6 @@ static struct platform_driver udc_driver = { static int __init udc_init(void) { - /* Disable DMA for omap7xx -- it doesn't work right. */ - if (cpu_is_omap7xx()) - use_dma = 0; - - INFO("%s, version: " DRIVER_VERSION -#ifdef USE_ISO - " (iso)" -#endif - "%s\n", driver_desc, - use_dma ? " (dma)" : ""); return platform_driver_probe(&udc_driver, omap_udc_probe); } module_init(udc_init); -- cgit v1.2.3-70-g09d2 From dc1737cdd75b692dbdec0e0e22439a0ce1089488 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 May 2012 14:33:30 +0300 Subject: usb: gadget: omap_udc: let it work as a module this also helps removing a few lines of boilerplate code. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/omap_udc.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 5e7b8912ef3..814aafbed00 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2535,7 +2535,7 @@ static inline void remove_proc_file(void) {} * UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that * capability yet though. */ -static unsigned __init +static unsigned __devinit omap_ep_setup(char *name, u8 addr, u8 type, unsigned buf, unsigned maxp, int dbuf) { @@ -2653,7 +2653,7 @@ static void omap_udc_release(struct device *dev) udc = NULL; } -static int __init +static int __devinit omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) { unsigned tmp, buf; @@ -2790,7 +2790,7 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) return 0; } -static int __init omap_udc_probe(struct platform_device *pdev) +static int __devinit omap_udc_probe(struct platform_device *pdev) { int status = -ENODEV; int hmc; @@ -3003,7 +3003,7 @@ cleanup0: return status; } -static int __exit omap_udc_remove(struct platform_device *pdev) +static int __devexit omap_udc_remove(struct platform_device *pdev) { DECLARE_COMPLETION_ONSTACK(done); @@ -3088,7 +3088,8 @@ static int omap_udc_resume(struct platform_device *dev) /*-------------------------------------------------------------------------*/ static struct platform_driver udc_driver = { - .remove = __exit_p(omap_udc_remove), + .probe = omap_udc_probe, + .remove = __devexit_p(omap_udc_remove), .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { @@ -3097,17 +3098,7 @@ static struct platform_driver udc_driver = { }, }; -static int __init udc_init(void) -{ - return platform_driver_probe(&udc_driver, omap_udc_probe); -} -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} -module_exit(udc_exit); +module_platform_driver(udc_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 70617db7ad7395498e6bc54c634199bf895426c6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 May 2012 14:36:42 +0300 Subject: usb: gadget: omap_udc: remove possiblity of NULL pointer de-reference when allocating a request, it's better programming practice to make sure we return NULL if allocation failed. This will ensure that, if struct usb_request isn't the first member on our structure, we don't cheat the gadget driver into thinking allocating worked because pointer isn't 0. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/omap_udc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 814aafbed00..f13bcdc7567 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -268,10 +268,12 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) struct omap_req *req; req = kzalloc(sizeof(*req), gfp_flags); - if (req) { - req->req.dma = DMA_ADDR_INVALID; - INIT_LIST_HEAD(&req->queue); - } + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + return &req->req; } -- cgit v1.2.3-70-g09d2 From 23673d7d26d81bd17cde3ed74c57b3f39585325f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 May 2012 14:38:32 +0300 Subject: usb: gadget: omap_udc: kfree(NULL) is safe we don't need to check for _req because kfree(NULL) is safe. Also, if someone actually passes a NULL pointer to be freed by usb_ep_free_request(), he deserves any issue he faces. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/omap_udc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index f13bcdc7567..50e8490867e 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -282,8 +282,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) { struct omap_req *req = container_of(_req, struct omap_req, req); - if (_req) - kfree(req); + kfree(req); } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-70-g09d2 From dd8e93814a357d8f032a93b6d17216436c762e9f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 May 2012 14:42:56 +0300 Subject: usb: gadget: omap_udc: use generic map/unmap routines This patch makes use of the generic map/unmap routines on the omap_udc driver. Removes some useless and duplicated code. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/omap_udc.c | 46 +++++++------------------------------------ 1 file changed, 7 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 50e8490867e..b6cc8b19f39 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -59,8 +59,6 @@ #define DRIVER_DESC "OMAP UDC driver" #define DRIVER_VERSION "4 October 2004" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - /* * The OMAP UDC needs _very_ early endpoint setup: before enabling the * D+ pullup to allow enumeration. That's too early for the gadget @@ -271,7 +269,6 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) if (!req) return NULL; - req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; @@ -290,6 +287,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) static void done(struct omap_ep *ep, struct omap_req *req, int status) { + struct omap_udc *udc = ep->udc; unsigned stopped = ep->stopped; list_del_init(&req->queue); @@ -299,22 +297,9 @@ done(struct omap_ep *ep, struct omap_req *req, int status) else status = req->req.status; - if (use_dma && ep->has_dma) { - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - (ep->bEndpointAddress & USB_DIR_IN) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - (ep->bEndpointAddress & USB_DIR_IN) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - } + if (use_dma && ep->has_dma) + usb_gadget_unmap_request(&udc->gadget, &req->req, + (ep->bEndpointAddress & USB_DIR_IN)); #ifndef USB_TRACE if (status && status != -ESHUTDOWN) @@ -915,26 +900,9 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - if (use_dma && ep->has_dma) { - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single( - ep->udc->gadget.dev.parent, - req->req.buf, - req->req.length, - (ep->bEndpointAddress & USB_DIR_IN) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 1; - } else { - dma_sync_single_for_device( - ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - (ep->bEndpointAddress & USB_DIR_IN) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 0; - } - } + if (use_dma && ep->has_dma) + usb_gadget_map_request(&udc->gadget, &req->req, + (ep->bEndpointAddress & USB_DIR_IN)); VDBG("%s queue req %p, len %d buf %p\n", ep->ep.name, _req, _req->length, _req->buf); -- cgit v1.2.3-70-g09d2 From 338edabcd879320f0f86cc701161b95277be6b5d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 18 May 2012 14:33:46 +0530 Subject: usb: s3c-hsotg: Use devm_* functions in s3c-hsotg.c file devm_* functions are used to replace kzalloc, request_mem_region, ioremap and request_irq functions in probe call. With the usage of devm_* functions explicit freeing and unmapping is not required. Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 51 +++++++++--------------------------------- 1 file changed, 11 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index f4abb0ed987..d37585105bb 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -136,7 +136,6 @@ struct s3c_hsotg_ep { * @driver: USB gadget driver * @plat: The platform specific configuration data. * @regs: The memory area mapped for accessing registers. - * @regs_res: The resource that was allocated when claiming register space. * @irq: The IRQ number we are using * @supplies: Definition of USB power supplies * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. @@ -158,7 +157,6 @@ struct s3c_hsotg { struct s3c_hsotg_plat *plat; void __iomem *regs; - struct resource *regs_res; int irq; struct clk *clk; @@ -3477,7 +3475,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) return -EINVAL; } - hsotg = kzalloc(sizeof(struct s3c_hsotg), GFP_KERNEL); + hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); if (!hsotg) { dev_err(dev, "cannot get memory\n"); return -ENOMEM; @@ -3489,46 +3487,33 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) hsotg->clk = clk_get(&pdev->dev, "otg"); if (IS_ERR(hsotg->clk)) { dev_err(dev, "cannot get otg clock\n"); - ret = PTR_ERR(hsotg->clk); - goto err_mem; + return PTR_ERR(hsotg->clk); } platform_set_drvdata(pdev, hsotg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "cannot find register resource 0\n"); - ret = -EINVAL; - goto err_clk; - } - - hsotg->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!hsotg->regs_res) { - dev_err(dev, "cannot reserve registers\n"); - ret = -ENOENT; - goto err_clk; - } - hsotg->regs = ioremap(res->start, resource_size(res)); + hsotg->regs = devm_request_and_ioremap(&pdev->dev, res); if (!hsotg->regs) { dev_err(dev, "cannot map registers\n"); ret = -ENXIO; - goto err_regs_res; + goto err_clk; } ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(dev, "cannot find IRQ\n"); - goto err_regs; + goto err_clk; } hsotg->irq = ret; - ret = request_irq(ret, s3c_hsotg_irq, 0, dev_name(dev), hsotg); + ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, + dev_name(dev), hsotg); if (ret < 0) { dev_err(dev, "cannot claim IRQ\n"); - goto err_regs; + goto err_clk; } dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq); @@ -3558,7 +3543,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) hsotg->supplies); if (ret) { dev_err(dev, "failed to request supplies: %d\n", ret); - goto err_irq; + goto err_clk; } ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), @@ -3642,19 +3627,11 @@ err_ep_mem: err_supplies: s3c_hsotg_phy_disable(hsotg); regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); -err_irq: - free_irq(hsotg->irq, hsotg); -err_regs: - iounmap(hsotg->regs); - -err_regs_res: - release_resource(hsotg->regs_res); - kfree(hsotg->regs_res); + err_clk: clk_disable_unprepare(hsotg->clk); clk_put(hsotg->clk); -err_mem: - kfree(hsotg); + return ret; } @@ -3675,12 +3652,6 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev) usb_gadget_unregister_driver(hsotg->driver); } - free_irq(hsotg->irq, hsotg); - iounmap(hsotg->regs); - - release_resource(hsotg->regs_res); - kfree(hsotg->regs_res); - s3c_hsotg_phy_disable(hsotg); regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); -- cgit v1.2.3-70-g09d2 From fa84c57516f6da93ebc2601348043e9bcb7b4b4d Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sat, 26 May 2012 16:32:39 -0700 Subject: usb: gadget: f_mass_storage: change default value of the removable parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit changes the default value of the removable module parameter from “y” to “n”. This comes with line with file_storag's default and seems to be a better default. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_mass_storage.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index f67b453740b..72116fa49ff 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -124,7 +124,7 @@ * backing storage. * ro=b[,b...] Default false, boolean for read-only access. * removable=b[,b...] - * Default true, boolean for removable media. + * Default false, boolean for removable media. * cdrom=b[,b...] Default false, boolean for whether to emulate * a CD-ROM drive. * nofua=b[,b...] Default false, booleans for ignore FUA flag @@ -3167,8 +3167,7 @@ fsg_config_from_params(struct fsg_config *cfg, for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) { lun->ro = !!params->ro[i]; lun->cdrom = !!params->cdrom[i]; - lun->removable = /* Removable by default */ - params->removable_count <= i || params->removable[i]; + lun->removable = !!params->removable[i]; lun->filename = params->file_count > i && params->file[i][0] ? params->file[i] @@ -3203,4 +3202,3 @@ fsg_common_from_params(struct fsg_common *common, fsg_config_from_params(&cfg, params); return fsg_common_init(common, cdev, &cfg); } - -- cgit v1.2.3-70-g09d2 From 560f1187990c6c548756bf2f1183aa19d9c97413 Mon Sep 17 00:00:00 2001 From: Koen Beel Date: Wed, 30 May 2012 20:43:37 +0200 Subject: usb: gadget: ffs: add HID descriptor support. When writing the descriptors to the ep0 file of functionfs, the HID descriptors where not recognized which caused the initialization from user space to fail. Signed-off-by: Koen Beel Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index dcd1c7fbb01..8adc79d1b40 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -1671,6 +1672,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len, } break; + case HID_DT_HID: + pr_vdebug("hid descriptor\n"); + if (length != sizeof(struct hid_descriptor)) + goto inv_length; + break; + case USB_DT_OTG: if (length != sizeof(struct usb_otg_descriptor)) goto inv_length; -- cgit v1.2.3-70-g09d2 From 0a00790ff4755f2e3eab907a43725cff59fc074b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 30 May 2012 13:25:57 -0700 Subject: usb: gadget: pch_udc: Fix likely misuse of | for & Using | with a constant is always true. Likely this should have be &. Signed-off-by: Joe Perches Signed-off-by: Felipe Balbi --- drivers/usb/gadget/pch_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 1cfcc9ecbfb..f4fb71c9ae0 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -2208,7 +2208,7 @@ static void pch_udc_complete_receiver(struct pch_udc_ep *ep) return; } if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE) - if (td->status | PCH_UDC_DMA_LAST) { + if (td->status & PCH_UDC_DMA_LAST) { count = td->status & PCH_UDC_RXTX_BYTES; break; } -- cgit v1.2.3-70-g09d2 From 3de6e63f7845e99981e489ddccf70b3ec02fe00a Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Fri, 1 Jun 2012 15:08:54 +0530 Subject: usb: gadget: uvc: Fix string descriptor STALL issue when multiple uvc functions are added to a configuration This patch solved the string descriptor STALL issue when we add multiple UVC functions in a single configuration using a 'webcam.c' like composite driver. Signed-off-by: Bhupesh Sharma Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_uvc.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 2022fe49214..054c35a22fb 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -619,22 +619,28 @@ uvc_bind_config(struct usb_configuration *c, uvc->desc.fs_streaming = fs_streaming; uvc->desc.hs_streaming = hs_streaming; - /* Allocate string descriptor numbers. */ - if ((ret = usb_string_id(c->cdev)) < 0) - goto error; - uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret; - uvc_iad.iFunction = ret; - - if ((ret = usb_string_id(c->cdev)) < 0) - goto error; - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret; - uvc_control_intf.iInterface = ret; - - if ((ret = usb_string_id(c->cdev)) < 0) - goto error; - uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret; - uvc_streaming_intf_alt0.iInterface = ret; - uvc_streaming_intf_alt1.iInterface = ret; + /* maybe allocate device-global string IDs, and patch descriptors */ + if (uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id == 0) { + /* Allocate string descriptor numbers. */ + ret = usb_string_id(c->cdev); + if (ret < 0) + goto error; + uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret; + uvc_iad.iFunction = ret; + + ret = usb_string_id(c->cdev); + if (ret < 0) + goto error; + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret; + uvc_control_intf.iInterface = ret; + + ret = usb_string_id(c->cdev); + if (ret < 0) + goto error; + uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret; + uvc_streaming_intf_alt0.iInterface = ret; + uvc_streaming_intf_alt1.iInterface = ret; + } /* Register the function. */ uvc->func.name = "uvc"; -- cgit v1.2.3-70-g09d2 From 5797663674b0d802f275761e71593b32b0849242 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Fri, 1 Jun 2012 15:08:55 +0530 Subject: usb: gadget: uvc: Use macro for interrupt endpoint status size instead of using a MAGIC number This patch adds a MACRO for the UVC video control status (interrupt) endpoint and removes the magic number which was being used earlier. Some UDCs have issues supporting an interrupt IN endpoint having a max packet size less than a particular value (say 32). It is easier in that case to simply change the MACRO value instead of changing the max packet size value at a number of locations. Signed-off-by: Bhupesh Sharma Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_uvc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 054c35a22fb..dd7d7a98ed4 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -59,6 +59,8 @@ static struct usb_gadget_strings *uvc_function_strings[] = { #define UVC_INTF_VIDEO_CONTROL 0 #define UVC_INTF_VIDEO_STREAMING 1 +#define STATUS_BYTECOUNT 16 /* 16 bytes status */ + static struct usb_interface_assoc_descriptor uvc_iad __initdata = { .bLength = sizeof(uvc_iad), .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, @@ -87,7 +89,7 @@ static struct usb_endpoint_descriptor uvc_control_ep __initdata = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(16), + .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), .bInterval = 8, }; @@ -95,7 +97,7 @@ static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = { .bLength = UVC_DT_CONTROL_ENDPOINT_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubType = UVC_EP_INTERRUPT, - .wMaxTransferSize = cpu_to_le16(16), + .wMaxTransferSize = cpu_to_le16(STATUS_BYTECOUNT), }; static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { -- cgit v1.2.3-70-g09d2 From fbcaba0e3dcec8451cccdc1fa92fcddbde2bc3f2 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Fri, 1 Jun 2012 15:08:56 +0530 Subject: usb: gadget: uvc: Add super-speed support to UVC webcam gadget This patch adds super-speed support to UVC webcam gadget. Also in this patch: - We add the configurability to pass bInterval, bMaxBurst, mult factors for video streaming endpoint (ISOC IN) through module parameters. - We use config_ep_by_speed helper routine to configure video streaming endpoint. Signed-off-by: Bhupesh Sharma Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_uvc.c | 241 +++++++++++++++++++++++++++++++++++++++----- drivers/usb/gadget/f_uvc.h | 8 +- drivers/usb/gadget/uvc.h | 4 +- drivers/usb/gadget/webcam.c | 29 +++++- 4 files changed, 247 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index dd7d7a98ed4..2a8bf0655c6 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -29,6 +29,25 @@ unsigned int uvc_gadget_trace_param; +/*-------------------------------------------------------------------------*/ + +/* module parameters specific to the Video streaming endpoint */ +static unsigned streaming_interval = 1; +module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_interval, "1 - 16"); + +static unsigned streaming_maxpacket = 1024; +module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); + +static unsigned streaming_mult; +module_param(streaming_mult, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_mult, "0 - 2 (hs/ss only)"); + +static unsigned streaming_maxburst; +module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); + /* -------------------------------------------------------------------------- * Function descriptors */ @@ -84,7 +103,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_control_ep __initdata = { +static struct usb_endpoint_descriptor uvc_fs_control_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -124,7 +143,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_streaming_ep = { +static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -133,15 +152,72 @@ static struct usb_endpoint_descriptor uvc_streaming_ep = { .bInterval = 1, }; +static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 1, +}; + +/* super speed support */ +static struct usb_endpoint_descriptor uvc_ss_control_ep __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = 8, +}; + +static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { + .bLength = sizeof uvc_ss_control_comp, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), +}; + +static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { + .bLength = sizeof uvc_ss_streaming_comp, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), +}; + static const struct usb_descriptor_header * const uvc_fs_streaming[] = { (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, - (struct usb_descriptor_header *) &uvc_streaming_ep, + (struct usb_descriptor_header *) &uvc_fs_streaming_ep, NULL, }; static const struct usb_descriptor_header * const uvc_hs_streaming[] = { (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, - (struct usb_descriptor_header *) &uvc_streaming_ep, + (struct usb_descriptor_header *) &uvc_hs_streaming_ep, + NULL, +}; + +static const struct usb_descriptor_header * const uvc_ss_streaming[] = { + (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, + (struct usb_descriptor_header *) &uvc_ss_streaming_ep, + (struct usb_descriptor_header *) &uvc_ss_streaming_comp, NULL, }; @@ -217,6 +293,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) struct uvc_device *uvc = to_uvc(f); struct v4l2_event v4l2_event; struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; + int ret; INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); @@ -264,7 +341,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) return 0; if (uvc->video.ep) { - uvc->video.ep->desc = &uvc_streaming_ep; + ret = config_ep_by_speed(f->config->cdev->gadget, + &(uvc->func), uvc->video.ep); + if (ret) + return ret; usb_ep_enable(uvc->video.ep); } @@ -370,9 +450,11 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) { struct uvc_input_header_descriptor *uvc_streaming_header; struct uvc_header_descriptor *uvc_control_header; + const struct uvc_descriptor_header * const *uvc_control_desc; const struct uvc_descriptor_header * const *uvc_streaming_cls; const struct usb_descriptor_header * const *uvc_streaming_std; const struct usb_descriptor_header * const *src; + static struct usb_endpoint_descriptor *uvc_control_ep; struct usb_descriptor_header **dst; struct usb_descriptor_header **hdr; unsigned int control_size; @@ -381,10 +463,29 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) unsigned int bytes; void *mem; - uvc_streaming_cls = (speed == USB_SPEED_FULL) - ? uvc->desc.fs_streaming : uvc->desc.hs_streaming; - uvc_streaming_std = (speed == USB_SPEED_FULL) - ? uvc_fs_streaming : uvc_hs_streaming; + switch (speed) { + case USB_SPEED_SUPER: + uvc_control_desc = uvc->desc.ss_control; + uvc_streaming_cls = uvc->desc.ss_streaming; + uvc_streaming_std = uvc_ss_streaming; + uvc_control_ep = &uvc_ss_control_ep; + break; + + case USB_SPEED_HIGH: + uvc_control_desc = uvc->desc.fs_control; + uvc_streaming_cls = uvc->desc.hs_streaming; + uvc_streaming_std = uvc_hs_streaming; + uvc_control_ep = &uvc_fs_control_ep; + break; + + case USB_SPEED_FULL: + default: + uvc_control_desc = uvc->desc.fs_control; + uvc_streaming_cls = uvc->desc.fs_streaming; + uvc_streaming_std = uvc_fs_streaming; + uvc_control_ep = &uvc_fs_control_ep; + break; + } /* Descriptors layout * @@ -402,16 +503,24 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) control_size = 0; streaming_size = 0; bytes = uvc_iad.bLength + uvc_control_intf.bLength - + uvc_control_ep.bLength + uvc_control_cs_ep.bLength + + uvc_control_ep->bLength + uvc_control_cs_ep.bLength + uvc_streaming_intf_alt0.bLength; - n_desc = 5; - for (src = (const struct usb_descriptor_header**)uvc->desc.control; *src; ++src) { + if (speed == USB_SPEED_SUPER) { + bytes += uvc_ss_control_comp.bLength; + n_desc = 6; + } else { + n_desc = 5; + } + + for (src = (const struct usb_descriptor_header **)uvc_control_desc; + *src; ++src) { control_size += (*src)->bLength; bytes += (*src)->bLength; n_desc++; } - for (src = (const struct usb_descriptor_header**)uvc_streaming_cls; *src; ++src) { + for (src = (const struct usb_descriptor_header **)uvc_streaming_cls; + *src; ++src) { streaming_size += (*src)->bLength; bytes += (*src)->bLength; n_desc++; @@ -435,12 +544,15 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_header = mem; UVC_COPY_DESCRIPTORS(mem, dst, - (const struct usb_descriptor_header**)uvc->desc.control); + (const struct usb_descriptor_header **)uvc_control_desc); uvc_control_header->wTotalLength = cpu_to_le16(control_size); uvc_control_header->bInCollection = 1; uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf; - UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep); + UVC_COPY_DESCRIPTOR(mem, dst, uvc_control_ep); + if (speed == USB_SPEED_SUPER) + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp); + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep); UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0); @@ -448,7 +560,8 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) UVC_COPY_DESCRIPTORS(mem, dst, (const struct usb_descriptor_header**)uvc_streaming_cls); uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size); - uvc_streaming_header->bEndpointAddress = uvc_streaming_ep.bEndpointAddress; + uvc_streaming_header->bEndpointAddress = + uvc_fs_streaming_ep.bEndpointAddress; UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std); @@ -484,6 +597,7 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) kfree(f->descriptors); kfree(f->hs_descriptors); + kfree(f->ss_descriptors); kfree(uvc); } @@ -498,8 +612,26 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) INFO(cdev, "uvc_function_bind\n"); + /* sanity check the streaming endpoint module parameters */ + if (streaming_interval < 1) + streaming_interval = 1; + if (streaming_interval > 16) + streaming_interval = 16; + if (streaming_mult > 2) + streaming_mult = 2; + if (streaming_maxburst > 15) + streaming_maxburst = 15; + + /* + * fill in the FS video streaming specific descriptors from the + * module parameters + */ + uvc_fs_streaming_ep.wMaxPacketSize = streaming_maxpacket > 1023 ? + 1023 : streaming_maxpacket; + uvc_fs_streaming_ep.bInterval = streaming_interval; + /* Allocate endpoints. */ - ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); + ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_control_ep); if (!ep) { INFO(cdev, "Unable to allocate control EP\n"); goto error; @@ -507,7 +639,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc->control_ep = ep; ep->driver_data = uvc; - ep = usb_ep_autoconfig(cdev->gadget, &uvc_streaming_ep); + ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); if (!ep) { INFO(cdev, "Unable to allocate streaming EP\n"); goto error; @@ -528,9 +660,52 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; - /* Copy descriptors. */ + /* sanity check the streaming endpoint module parameters */ + if (streaming_maxpacket > 1024) + streaming_maxpacket = 1024; + + /* Copy descriptors for FS. */ f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); - f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); + + /* support high speed hardware */ + if (gadget_is_dualspeed(cdev->gadget)) { + /* + * Fill in the HS descriptors from the module parameters for the + * Video Streaming endpoint. + * NOTE: We assume that the user knows what they are doing and + * won't give parameters that their UDC doesn't support. + */ + uvc_hs_streaming_ep.wMaxPacketSize = streaming_maxpacket; + uvc_hs_streaming_ep.wMaxPacketSize |= streaming_mult << 11; + uvc_hs_streaming_ep.bInterval = streaming_interval; + uvc_hs_streaming_ep.bEndpointAddress = + uvc_fs_streaming_ep.bEndpointAddress; + + /* Copy descriptors. */ + f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); + } + + /* support super speed hardware */ + if (gadget_is_superspeed(c->cdev->gadget)) { + /* + * Fill in the SS descriptors from the module parameters for the + * Video Streaming endpoint. + * NOTE: We assume that the user knows what they are doing and + * won't give parameters that their UDC doesn't support. + */ + uvc_ss_streaming_ep.wMaxPacketSize = streaming_maxpacket; + uvc_ss_streaming_ep.bInterval = streaming_interval; + uvc_ss_streaming_comp.bmAttributes = streaming_mult; + uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; + uvc_ss_streaming_comp.wBytesPerInterval = + streaming_maxpacket * (streaming_mult + 1) * + (streaming_maxburst + 1); + uvc_ss_streaming_ep.bEndpointAddress = + uvc_fs_streaming_ep.bEndpointAddress; + + /* Copy descriptors. */ + f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); + } /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); @@ -585,9 +760,11 @@ error: */ int __init uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *control, + const struct uvc_descriptor_header * const *fs_control, + const struct uvc_descriptor_header * const *ss_control, const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming) + const struct uvc_descriptor_header * const *hs_streaming, + const struct uvc_descriptor_header * const *ss_streaming) { struct uvc_device *uvc; int ret = 0; @@ -605,21 +782,31 @@ uvc_bind_config(struct usb_configuration *c, uvc->state = UVC_STATE_DISCONNECTED; /* Validate the descriptors. */ - if (control == NULL || control[0] == NULL || - control[0]->bDescriptorSubType != UVC_VC_HEADER) + if (fs_control == NULL || fs_control[0] == NULL || + fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) + goto error; + + if (ss_control == NULL || ss_control[0] == NULL || + ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) goto error; if (fs_streaming == NULL || fs_streaming[0] == NULL || - fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; if (hs_streaming == NULL || hs_streaming[0] == NULL || - hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + goto error; + + if (ss_streaming == NULL || ss_streaming[0] == NULL || + ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; - uvc->desc.control = control; + uvc->desc.fs_control = fs_control; + uvc->desc.ss_control = ss_control; uvc->desc.fs_streaming = fs_streaming; uvc->desc.hs_streaming = hs_streaming; + uvc->desc.ss_streaming = ss_streaming; /* maybe allocate device-global string IDs, and patch descriptors */ if (uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id == 0) { diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h index abf83293513..c3d258d3018 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/f_uvc.h @@ -17,9 +17,11 @@ #include extern int uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *control, - const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming); + const struct uvc_descriptor_header * const *fs_control, + const struct uvc_descriptor_header * const *hs_control, + const struct uvc_descriptor_header * const *fs_streaming, + const struct uvc_descriptor_header * const *hs_streaming, + const struct uvc_descriptor_header * const *ss_streaming); #endif /* _F_UVC_H_ */ diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index ca4e03a1c73..93b0c119111 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -153,9 +153,11 @@ struct uvc_device /* Descriptors */ struct { - const struct uvc_descriptor_header * const *control; + const struct uvc_descriptor_header * const *fs_control; + const struct uvc_descriptor_header * const *ss_control; const struct uvc_descriptor_header * const *fs_streaming; const struct uvc_descriptor_header * const *hs_streaming; + const struct uvc_descriptor_header * const *ss_streaming; } desc; unsigned int control_intf; diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index 668fe128f2e..120e134e805 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -272,7 +272,15 @@ static const struct uvc_color_matching_descriptor uvc_color_matching = { .bMatrixCoefficients = 4, }; -static const struct uvc_descriptor_header * const uvc_control_cls[] = { +static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = { + (const struct uvc_descriptor_header *) &uvc_control_header, + (const struct uvc_descriptor_header *) &uvc_camera_terminal, + (const struct uvc_descriptor_header *) &uvc_processing, + (const struct uvc_descriptor_header *) &uvc_output_terminal, + NULL, +}; + +static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { (const struct uvc_descriptor_header *) &uvc_control_header, (const struct uvc_descriptor_header *) &uvc_camera_terminal, (const struct uvc_descriptor_header *) &uvc_processing, @@ -304,6 +312,18 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { NULL, }; +static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { + (const struct uvc_descriptor_header *) &uvc_input_header, + (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, + (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, +}; + /* -------------------------------------------------------------------------- * USB configuration */ @@ -311,8 +331,9 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { static int __init webcam_config_bind(struct usb_configuration *c) { - return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls, - uvc_hs_streaming_cls); + return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls, + uvc_fs_streaming_cls, uvc_hs_streaming_cls, + uvc_ss_streaming_cls); } static struct usb_configuration webcam_config_driver = { @@ -373,7 +394,7 @@ static struct usb_composite_driver webcam_driver = { .name = "g_webcam", .dev = &webcam_device_descriptor, .strings = webcam_device_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .unbind = webcam_unbind, }; -- cgit v1.2.3-70-g09d2 From 206dd69a62a06197b308fa0decfd0b649fcc2811 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 21 May 2012 12:42:54 +0530 Subject: USB: DWC3: Generate interrupt on each TRB as default option Currently in case of isoc, interrupt is programmed after each TRB_NUM/4 ie 8th TRB. A TRB is programmed against each submitted request from gadget. If we do not want to limit the minimum number of necessary request to be submitted from gadget then we must receive interrupt on each TRB submission. There can be such situation with a gadget working with ping-pong buffer. If a gadget does not want to receive interrupt after each request completion then it may set no_interrupt flag. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a9fc7c4a5a2..be87e82b36c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -766,8 +766,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, case USB_ENDPOINT_XFER_ISOC: trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; - /* IOC every DWC3_TRB_NUM / 4 so we can refill */ - if (!(cur_slot % (DWC3_TRB_NUM / 4))) + if (!req->request.no_interrupt) trb->ctrl |= DWC3_TRB_CTRL_IOC; break; -- cgit v1.2.3-70-g09d2 From 389f2828bb2a5f2acde9bdeffde02743684c1d86 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 21 May 2012 12:46:26 +0530 Subject: USB: DWC3: Correct DWC3_TRB_SIZE_TRBSTS definition Correct define for DWC3_TRB_SIZE_TRBSTS. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4d5c63dae14..ce43b1f5f34 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -495,7 +495,7 @@ enum dwc3_device_state { #define DWC3_TRB_SIZE_MASK (0x00ffffff) #define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) #define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24) -#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28) >> 28)) +#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28) #define DWC3_TRBSTS_OK 0 #define DWC3_TRBSTS_MISSED_ISOC 1 -- cgit v1.2.3-70-g09d2 From d05b81824e672a48b3566634622849866de2b787 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 21 May 2012 14:51:30 +0530 Subject: USB: DWC3: Correct DWC3_DSTS_SOFFN_MASK definition SOF Number is bit16:3 of DSTS. Correct the mask accordingly. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index ce43b1f5f34..e402022b9e1 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -291,7 +291,7 @@ #define DWC3_DSTS_RXFIFOEMPTY (1 << 17) -#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3) +#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3) #define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) #define DWC3_DSTS_CONNECTSPD (7 << 0) -- cgit v1.2.3-70-g09d2 From d6d6ec7b8854ab62f7f3907656d5d29eb47532e7 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Fri, 25 May 2012 18:54:56 +0530 Subject: usb: dwc3: Fix missed isoc IN transaction If an IN transfer is missed on isoc endpoint, then driver must insure that next ep_queue is properly handled. This patch fixes this issue by starting a new transfer for next queued request. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 ++ drivers/usb/dwc3/gadget.c | 73 +++++++++++++++++++++++++++++++---------------- 2 files changed, 52 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e402022b9e1..fcb8be2ec1c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -405,6 +405,7 @@ struct dwc3_event_buffer { * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @res_trans_idx: Resource transfer index + * @current_uf: Current uf received through last event parameter * @interval: the intervall on which the ISOC transfer is started * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX @@ -428,6 +429,7 @@ struct dwc3_ep { #define DWC3_EP_WEDGE (1 << 2) #define DWC3_EP_BUSY (1 << 4) #define DWC3_EP_PENDING_REQUEST (1 << 5) +#define DWC3_EP_MISSED_ISOC (1 << 6) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN (1 << 31) @@ -437,6 +439,7 @@ struct dwc3_ep { u8 number; u8 type; u8 res_trans_idx; + u16 current_uf; u32 interval; char name[20]; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index be87e82b36c..0cab69e5138 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -990,6 +990,34 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, return 0; } +static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, + struct dwc3_ep *dep, u32 cur_uf) +{ + u32 uf; + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", + dep->name); + return; + } + + /* 4 micro frames in the future */ + uf = cur_uf + dep->interval * 4; + + __dwc3_gadget_kick_transfer(dep, uf, 1); +} + +static void dwc3_gadget_start_isoc(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) +{ + u32 cur_uf, mask; + + mask = ~(dep->interval - 1); + cur_uf = event->parameters & mask; + + __dwc3_gadget_start_isoc(dwc, dep, cur_uf); +} + static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; @@ -1019,8 +1047,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) list_add_tail(&req->list, &dep->request_list); - if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY)) - dep->flags |= DWC3_EP_PENDING_REQUEST; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + if (dep->flags & DWC3_EP_BUSY) { + dep->flags |= DWC3_EP_PENDING_REQUEST; + } else if (dep->flags & DWC3_EP_MISSED_ISOC) { + __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf); + dep->flags &= ~DWC3_EP_MISSED_ISOC; + } + } /* * There are two special cases: @@ -1591,6 +1625,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_trb *trb; unsigned int count; unsigned int s_pkt = 0; + unsigned int trb_status; do { req = next_request(&dep->req_queued); @@ -1616,9 +1651,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, if (dep->direction) { if (count) { - dev_err(dwc->dev, "incomplete IN transfer %s\n", - dep->name); - status = -ECONNRESET; + trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); + if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { + dev_dbg(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + dep->current_uf = event->parameters & + ~(dep->interval - 1); + dep->flags |= DWC3_EP_MISSED_ISOC; + } else { + dev_err(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + status = -ECONNRESET; + } } } else { if (count && (event->status & DEPEVT_STATUS_SHORT)) @@ -1690,25 +1734,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, } } -static void dwc3_gadget_start_isoc(struct dwc3 *dwc, - struct dwc3_ep *dep, const struct dwc3_event_depevt *event) -{ - u32 uf, mask; - - if (list_empty(&dep->request_list)) { - dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", - dep->name); - return; - } - - mask = ~(dep->interval - 1); - uf = event->parameters & mask; - /* 4 micro frames in the future */ - uf += dep->interval * 4; - - __dwc3_gadget_kick_transfer(dep, uf, 1); -} - static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { -- cgit v1.2.3-70-g09d2 From 70b674bfeb735557fc03cdc125ffe6d34218fcfd Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Sun, 3 Jun 2012 19:43:19 +0530 Subject: usb: dwc3: Fix break from cleanup request loop If event status says that its last completed TRB but TRB is still owned by HW then break from the loop, because we are not going to get correct TRB status from trb control/size register. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0cab69e5138..854f3a3c883 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1681,7 +1681,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, if (s_pkt) break; if ((event->status & DEPEVT_STATUS_LST) && - (trb->ctrl & DWC3_TRB_CTRL_LST)) + (trb->ctrl & (DWC3_TRB_CTRL_LST | + DWC3_TRB_CTRL_HWO))) break; if ((event->status & DEPEVT_STATUS_IOC) && (trb->ctrl & DWC3_TRB_CTRL_IOC)) -- cgit v1.2.3-70-g09d2 From abf710e655fe843afe0ab6a56e344567d933b018 Mon Sep 17 00:00:00 2001 From: Vikram Pandita Date: Fri, 18 May 2012 13:48:04 -0700 Subject: usb: musb: handle nuked ep dma interrupt User can trigger disabling of gadget at run time while the transfers are going on. Eg: 1: rmmod of musb driver while transfers are going on Eg: 2: On android doing: echo 0 > /sys/class/android_usb/android0/enable While a big file transfer is going on via PTP/MTP. In such a case, musb_gadget_disable() calls nuke() but the dma interrupt may still happen for an endpoint since hw would raise the interrupt in anycase. This can result in a NULL pointer access crash: [ 314.030426] PC is at txstate+0x74/0x20c [ 314.034759] LR is at musb_g_tx+0x140/0x204 [ 314.039489] pc : [] lr : [] psr: 20000193 [ 314.039520] sp : c783bc68 ip : 00000002 fp : c783bc9c [ 314.052429] r10: 00000018 r9 : 00000000 r8 : 00000200 [ 314.058258] r7 : 00000000 r6 : fc0ab130 r5 : c781a410 r4 : c6caf640 [ 314.065643] r3 : 00000000 r2 : 00000000 r1 : 00000000 r0 : c781a000 [ 315.083251] Backtrace: [ 315.086242] [] (txstate+0x0/0x20c) from [] (musb_g_tx+0x140/0x204) [ 315.095123] [] (musb_g_tx+0x0/0x204) from [] (musb_dma_completion+0x40/0x54) [ 315.104980] [] (musb_dma_completion+0x0/0x54) from [] (dma_controller_irq+0x118/0x184) [ 315.115661] [] (dma_controller_irq+0x0/0x184) from [] (handle_irq_event_percpu+0x54/0x188) So put protection in code to handle possiblity of getting an interrupt for an endpoint that might have been already nuked. Reported-by: Todd Poynor Signed-off-by: Vikram Pandita Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index f42c29b11f7..695c892e70e 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -328,6 +328,13 @@ static void txstate(struct musb *musb, struct musb_request *req) musb_ep = req->ep; + /* Check if EP is disabled */ + if (!musb_ep->desc) { + dev_dbg(musb->controller, "ep:%s disabled - ignore request\n", + musb_ep->end_point.name); + return; + } + /* we shouldn't get here while DMA is active ... but we do ... */ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { dev_dbg(musb->controller, "dma pending...\n"); @@ -650,6 +657,13 @@ static void rxstate(struct musb *musb, struct musb_request *req) len = musb_ep->packet_sz; + /* Check if EP is disabled */ + if (!musb_ep->desc) { + dev_dbg(musb->controller, "ep:%s disabled - ignore request\n", + musb_ep->end_point.name); + return; + } + /* We shouldn't get here while DMA is active, but we do... */ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { dev_dbg(musb->controller, "DMA pending...\n"); -- cgit v1.2.3-70-g09d2 From 2ed9127cff9a255b7671b8d3d938109f68a87961 Mon Sep 17 00:00:00 2001 From: Mantesh Sarasetti Date: Fri, 1 Jun 2012 14:54:30 +0300 Subject: usb: musb: host: Disable MUSB DMA mode incase of DMA channel request failure Currently in case of MUSB DMA channel request failure we are not clearing MUSB_RXCSR_DMAENAB, MUSB_RXCSR_H_AUTOREQ and MUSB_RXCSR_AUTOCLEAR bits of MUSB RXCSR of MUSB DMA. Which is causing failure in receipt of data packets in next transfer. Fix is to disable the MUSB DMA mode and related bits incase of DMA channel request fails Signed-off-by: Mantesh Sarashetti Signed-off-by: Ruslan Bilovol Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_host.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index ef8d744800a..33fc45151b8 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1736,7 +1736,11 @@ void musb_host_rx(struct musb *musb, u8 epnum) c->channel_release(dma); hw_ep->rx_channel = NULL; dma = NULL; - /* REVISIT reset CSR */ + val = musb_readw(epio, MUSB_RXCSR); + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + musb_writew(epio, MUSB_RXCSR, val); } } #endif /* Mentor DMA */ -- cgit v1.2.3-70-g09d2 From 79d6680f017a8084ae746f5d845a4fdc4f3a5e43 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Fri, 20 Apr 2012 15:29:20 -0600 Subject: usb: otg: isp1301_omap: resolve unused variable warning from gcc Resolve this build warning: drivers/usb/otg/isp1301_omap.c: In function 'isp1301_set_peripheral': drivers/usb/otg/isp1301_omap.c:1340:6: warning: unused variable 'l' This shows up when building with the 'omap1_defconfig' and '5912osk_testconfig' configs from git://git.pwsan.com/omap_kconfigs. Compile-tested only. Cc: Heikki Krogerus Cc: Felipe Balbi Signed-off-by: Paul Walmsley Signed-off-by: Felipe Balbi --- drivers/usb/otg/isp1301_omap.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 70cf5d7bca4..33cd709b084 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1336,9 +1336,6 @@ static int isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); -#ifndef CONFIG_USB_OTG - u32 l; -#endif if (!otg || isp != the_transceiver) return -ENODEV; @@ -1365,10 +1362,14 @@ isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) otg->gadget = gadget; // FIXME update its refcount - l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS); - l |= OTG_ID; - omap_writel(l, OTG_CTRL); + { + u32 l; + + l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS); + l |= OTG_ID; + omap_writel(l, OTG_CTRL); + } power_up(isp); isp->phy.state = OTG_STATE_B_IDLE; -- cgit v1.2.3-70-g09d2 From 6dbc10c61d5841f64945fd94977fa8fa1c0192da Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 17 Feb 2012 11:58:14 +0200 Subject: usb: dwc3: gadget: drop useless code We never set CMDIOC bit for Start Transfer command, so that code will never be used. Tested-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 854f3a3c883..056ace8ab97 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1765,9 +1765,6 @@ static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, case DWC3_DEPCMD_ENDTRANSFER: dwc3_process_ep_cmd_complete(dep, event); break; - case DWC3_DEPCMD_STARTTRANSFER: - dep->res_trans_idx = param & 0x7f; - break; default: printk(KERN_ERR "%s() unknown /unexpected type: %d\n", __func__, cmd_type); -- cgit v1.2.3-70-g09d2 From ea53b8828c50b7a5138a8931c41b2671682c86b5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 17 Feb 2012 12:10:04 +0200 Subject: usb: dwc3: gadget: don't wait for ep cmd IRQ That IRQ is causing way too much trouble. We have a different handling which was agreed with IP provider and has been tested with FPGA and OMAP5. Tested-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 66 +++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 056ace8ab97..0f19978d484 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -555,9 +555,34 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; - if (!list_empty(&dep->req_queued)) + if (!list_empty(&dep->req_queued)) { dwc3_stop_active_transfer(dwc, dep->number); + /* + * NOTICE: We are violating what the Databook says about the + * EndTransfer command. Ideally we would _always_ wait for the + * EndTransfer Command Completion IRQ, but that's causing too + * much trouble synchronizing between us and gadget driver. + * + * We have discussed this with the IP Provider and it was + * suggested to giveback all requests here, but give HW some + * extra time to synchronize with the interconnect. We're using + * an arbitraty 100us delay for that. + * + * Note also that a similar handling was tested by Synopsys + * (thanks a lot Paul) and nothing bad has come out of it. + * In short, what we're doing is: + * + * - Issue EndTransfer WITH CMDIOC bit set + * - Wait 100us + * - giveback all requests to gadget driver + */ + udelay(100); + + req = next_request(&dep->req_queued); + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } + while (!list_empty(&dep->request_list)) { req = next_request(&dep->request_list); @@ -1735,43 +1760,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, } } -static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) -{ - struct dwc3 *dwc = dep->dwc; - struct dwc3_event_depevt mod_ev = *event; - - /* - * We were asked to remove one request. It is possible that this - * request and a few others were started together and have the same - * transfer index. Since we stopped the complete endpoint we don't - * know how many requests were already completed (and not yet) - * reported and how could be done (later). We purge them all until - * the end of the list. - */ - mod_ev.status = DEPEVT_STATUS_LST; - dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN); - dep->flags &= ~DWC3_EP_BUSY; - /* pending requests are ignored and are queued on XferNotReady */ -} - -static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) -{ - u32 param = event->parameters; - u32 cmd_type = (param >> 8) & ((1 << 5) - 1); - - switch (cmd_type) { - case DWC3_DEPCMD_ENDTRANSFER: - dwc3_process_ep_cmd_complete(dep, event); - break; - default: - printk(KERN_ERR "%s() unknown /unexpected type: %d\n", - __func__, cmd_type); - break; - }; -} - static void dwc3_endpoint_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { @@ -1853,7 +1841,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); break; case DWC3_DEPEVT_EPCMDCMPLT: - dwc3_ep_cmd_compl(dep, event); + dev_vdbg(dwc->dev, "Endpoint Command Complete\n"); break; } } -- cgit v1.2.3-70-g09d2 From 1e2360eadaa33c09bb72afb14f74b6c6ab2aca66 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Jun 2012 09:18:12 +0300 Subject: usb: dwc3: gadget: don't call stop_active_transfers() on disconnect In case we get disconnected, we will call gadget driver's disconnect method, which should make sure to disable all endpoints. At that point we will call stop_active_transfers() to make sure we didn't leave any pending request on the controller. Tested-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0f19978d484..52d0f67e33d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1927,7 +1927,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) reg &= ~DWC3_DCTL_INITU2ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); - dwc3_stop_active_transfers(dwc); dwc3_disconnect_gadget(dwc); dwc->start_config_issued = false; -- cgit v1.2.3-70-g09d2 From 3336abb5b608300550136ddab7a7101b30d45fa7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Jun 2012 09:19:35 +0300 Subject: usb: dwc3: gadget: ignore endpoint IRQs when endpoint is disabled By the time we're disabling the endpoint, HW could already have posted more events to our event buffer. In that case, we will receive endpoint events for a disabled endpoint. In order to protect ourselves from that situation, we simply ignore endpoint interrupts whenever the endpoint is disabled. Tested-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 52d0f67e33d..8e00756ee52 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1768,6 +1768,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dep = dwc->eps[epnum]; + if (!(dep->flags & DWC3_EP_ENABLED)) + return; + dev_vdbg(dwc->dev, "%s: %s\n", dep->name, dwc3_ep_event_string(event->endpoint_event)); -- cgit v1.2.3-70-g09d2 From 7e39b817ee64a8b67282c76f52aaabddc8a5cd26 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 6 Jun 2012 19:18:29 +0530 Subject: usb: dwc3: Correct DWC3_DCTL_HIRD_THRES definition The definition of DWC3_DCTL_HIRD_THRES macro is completely wrong. It will only work for when we want to read the register's contents for that bitfield. Change the macro so that it can be used to writing to the register, and when we need to read, we can add extra right shift of 24 bits. Signed-off-by: Pratyush Anand [ balbi@ti.com: add a commit log ] Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index fcb8be2ec1c..d3e56cfb29c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -223,7 +223,7 @@ #define DWC3_DCTL_LSFTRST (1 << 29) #define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) -#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24) +#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24) #define DWC3_DCTL_APPL1RES (1 << 23) -- cgit v1.2.3-70-g09d2 From dcae3573b98b2205961bfa5821427f5664422cd8 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 6 Jun 2012 19:36:17 +0530 Subject: usb: dwc3: resume phy during gadget initialization on recent cores It is needed to enumerate recent cores like 2.10a. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8e00756ee52..38cd13ffae9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2449,8 +2449,8 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DCTL, reg); - dwc3_gadget_usb2_phy_suspend(dwc, true); - dwc3_gadget_usb3_phy_suspend(dwc, true); + dwc3_gadget_usb2_phy_suspend(dwc, false); + dwc3_gadget_usb3_phy_suspend(dwc, false); } ret = device_register(&dwc->gadget.dev); -- cgit v1.2.3-70-g09d2 From b785ea7ce662c47f6208071320638a4813722803 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Jun 2012 10:20:23 +0300 Subject: usb: gadget: composite: fix ep->maxburst initialization bMaxBurst field on endpoint companion descriptor is supposed to contain the number of burst minus 1. When passing that to controller drivers, we should be passing the real number instead (by incrementing 1). While doing that, also fix the assumption on dwc3 that value comes decremented by one. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- drivers/usb/gadget/composite.c | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3df1a1973b0..cc978d65a80 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -414,7 +414,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)) - | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst); + | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1); params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 390749bbb0c..3f72110da1b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -117,6 +117,7 @@ int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f, struct usb_ep *_ep) { + struct usb_composite_dev *cdev = get_gadget_data(g); struct usb_endpoint_descriptor *chosen_desc = NULL; struct usb_descriptor_header **speed_desc = NULL; @@ -180,10 +181,12 @@ ep_found: _ep->mult = comp_desc->bmAttributes & 0x3; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: - _ep->maxburst = comp_desc->bMaxBurst; + _ep->maxburst = comp_desc->bMaxBurst + 1; break; default: - /* Do nothing for control endpoints */ + if (comp_desc->bMaxBurst != 0) + ERROR(cdev, "ep0 bMaxBurst must be 0\n"); + _ep->maxburst = 1; break; } } -- cgit v1.2.3-70-g09d2 From 1c7f6cd3f97b038e875094a8433e239f03d10955 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 25 May 2012 20:14:53 -0300 Subject: usb: fsl_mxc_udc.c : Use clk_prepare_enable/clk_disable_unprepare Prepare the clock before enabling it. Cc: Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Felipe Balbi --- drivers/usb/gadget/fsl_mxc_udc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c index dcbc0a2e48d..f4b5109feb3 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -40,7 +40,7 @@ int fsl_udc_clk_init(struct platform_device *pdev) if (IS_ERR(mxc_ahb_clk)) return PTR_ERR(mxc_ahb_clk); - ret = clk_enable(mxc_ahb_clk); + ret = clk_prepare_enable(mxc_ahb_clk); if (ret < 0) { dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n"); goto eenahb; @@ -65,7 +65,7 @@ int fsl_udc_clk_init(struct platform_device *pdev) } } - ret = clk_enable(mxc_usb_clk); + ret = clk_prepare_enable(mxc_usb_clk); if (ret < 0) { dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n"); goto eenusb; @@ -79,7 +79,7 @@ eclkrate: mxc_usb_clk = NULL; egusb: if (!cpu_is_mx35()) - clk_disable(mxc_ahb_clk); + clk_disable_unprepare(mxc_ahb_clk); eenahb: if (!cpu_is_mx35()) clk_put(mxc_ahb_clk); @@ -104,7 +104,7 @@ void fsl_udc_clk_finalize(struct platform_device *pdev) /* ULPI transceivers don't need usbpll */ if (pdata->phy_mode == FSL_USB2_PHY_ULPI) { - clk_disable(mxc_usb_clk); + clk_disable_unprepare(mxc_usb_clk); clk_put(mxc_usb_clk); mxc_usb_clk = NULL; } @@ -113,11 +113,11 @@ void fsl_udc_clk_finalize(struct platform_device *pdev) void fsl_udc_clk_release(void) { if (mxc_usb_clk) { - clk_disable(mxc_usb_clk); + clk_disable_unprepare(mxc_usb_clk); clk_put(mxc_usb_clk); } if (!cpu_is_mx35()) { - clk_disable(mxc_ahb_clk); + clk_disable_unprepare(mxc_ahb_clk); clk_put(mxc_ahb_clk); } } -- cgit v1.2.3-70-g09d2 From 8194fea0fc308a7cdfc9333584d69cb5d428d47f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 25 May 2012 20:14:54 -0300 Subject: usb: imx_udc.c: Use clk_prepare_enable/clk_disable_unprepare Prepare the clock before enabling it. Cc: Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Felipe Balbi --- drivers/usb/gadget/imx_udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 54034f84f99..dc5334856af 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1432,7 +1432,7 @@ static int __init imx_udc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't get USB clock\n"); goto fail2; } - clk_enable(clk); + clk_prepare_enable(clk); if (clk_get_rate(clk) != 48000000) { D_INI(&pdev->dev, @@ -1496,7 +1496,7 @@ fail4: free_irq(imx_usb->usbd_int[i], imx_usb); fail3: clk_put(clk); - clk_disable(clk); + clk_disable_unprepare(clk); fail2: iounmap(base); fail1: @@ -1521,7 +1521,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev) free_irq(imx_usb->usbd_int[i], imx_usb); clk_put(imx_usb->clk); - clk_disable(imx_usb->clk); + clk_disable_unprepare(imx_usb->clk); iounmap(imx_usb->base); release_mem_region(imx_usb->res->start, resource_size(imx_usb->res)); -- cgit v1.2.3-70-g09d2 From 2b97fb5a605635684f757080cdc405d8173838d5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 25 May 2012 20:14:55 -0300 Subject: usb: imx21-hcd.c: Use clk_prepare_enable/clk_disable_unprepare Prepare the clock before enabling it. Cc: Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Felipe Balbi --- drivers/usb/host/imx21-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index ff471c1c165..f19e2690c23 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1811,7 +1811,7 @@ static int imx21_remove(struct platform_device *pdev) usb_remove_hcd(hcd); if (res != NULL) { - clk_disable(imx21->clk); + clk_disable_unprepare(imx21->clk); clk_put(imx21->clk); iounmap(imx21->regs); release_mem_region(res->start, resource_size(res)); @@ -1884,7 +1884,7 @@ static int imx21_probe(struct platform_device *pdev) ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000)); if (ret) goto failed_clock_set; - ret = clk_enable(imx21->clk); + ret = clk_prepare_enable(imx21->clk); if (ret) goto failed_clock_enable; @@ -1900,7 +1900,7 @@ static int imx21_probe(struct platform_device *pdev) return 0; failed_add_hcd: - clk_disable(imx21->clk); + clk_disable_unprepare(imx21->clk); failed_clock_enable: failed_clock_set: clk_put(imx21->clk); -- cgit v1.2.3-70-g09d2 From c2e935a7db6e7354e9dd138b7f6f4c53affc09d9 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Wed, 13 Jun 2012 20:34:12 +0800 Subject: USB: move transceiver from ehci_hcd and ohci_hcd to hcd and rename it as phy - to decrease redundant since both ehci_hcd and ohci_hcd have the same variable - it helps access phy in usb core code - phy is more meaningful than transceiver Signed-off-by: Richard Zhao Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 21 ++++++++++----------- drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci.h | 4 ---- drivers/usb/host/ohci-omap.c | 27 ++++++++++++++------------- drivers/usb/host/ohci.h | 5 ----- include/linux/usb/hcd.h | 6 ++++++ 6 files changed, 31 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54..3379945b095 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,19 +142,19 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); - dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", - hcd, ehci, ehci->transceiver); + hcd->phy = usb_get_transceiver(); + dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, phy=0x%p\n", + hcd, ehci, hcd->phy); - if (ehci->transceiver) { - retval = otg_set_host(ehci->transceiver->otg, + if (hcd->phy) { + retval = otg_set_host(hcd->phy->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_transceiver(hcd->phy); goto err4; } } else { - dev_err(&pdev->dev, "can't find transceiver\n"); + dev_err(&pdev->dev, "can't find phy\n"); retval = -ENODEV; goto err4; } @@ -190,11 +190,10 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - if (ehci->transceiver) { - otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + if (hcd->phy) { + otg_set_host(hcd->phy->otg, NULL); + usb_put_transceiver(hcd->phy); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index fc9e7cc6ac9..dd5eef6af6d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -724,7 +724,7 @@ static int ehci_hub_control ( #ifdef CONFIG_USB_OTG if ((hcd->self.otg_port == (wIndex + 1)) && hcd->self.b_hnp_enable) { - otg_start_hnp(ehci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); break; } #endif diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2694ed6558d..85c3572155d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -175,10 +175,6 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif - /* - * OTG controllers and transceivers need software interaction - */ - struct usb_phy *transceiver; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5..eccddb46139 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -167,14 +167,15 @@ static int omap_1510_local_bus_init(void) static void start_hnp(struct ohci_hcd *ohci) { - const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; + struct usb_hcd *hcd = ohci_to_hcd(ohci); + const unsigned port = hcd->self.otg_port - 1; unsigned long flags; u32 l; - otg_start_hnp(ohci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); local_irq_save(flags); - ohci->transceiver->state = OTG_STATE_A_SUSPEND; + hcd->phy->state = OTG_STATE_A_SUSPEND; writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); l = omap_readl(OTG_CTRL); l &= ~OTG_A_BUSREQ; @@ -211,18 +212,18 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); - if (ohci->transceiver) { - int status = otg_set_host(ohci->transceiver->otg, + hcd->phy = usb_get_transceiver(); + if (hcd->phy) { + int status = otg_set_host(hcd->phy->otg, &ohci_to_hcd(ohci)->self); - dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", - ohci->transceiver->label, status); + dev_dbg(hcd->self.controller, "init %s phy, status %d\n", + hcd->phy->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_transceiver(hcd->phy); return status; } } else { - dev_err(hcd->self.controller, "can't find transceiver\n"); + dev_err(hcd->self.controller, "can't find phy\n"); return -ENODEV; } ohci->start_hnp = start_hnp; @@ -403,9 +404,9 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) struct ohci_hcd *ohci = hcd_to_ohci (hcd); usb_remove_hcd(hcd); - if (ohci->transceiver) { - (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + if (hcd->phy) { + (void) otg_set_host(hcd->phy->otg, 0); + usb_put_transceiver(hcd->phy); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 1b19aea25a2..d3299143d9e 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -372,11 +372,6 @@ struct ohci_hcd { struct ed *ed_controltail; /* last in ctrl list */ struct ed *periodic [NUM_INTS]; /* shadow int_table */ - /* - * OTG controllers and transceivers need software interaction; - * other external transceivers should be software-transparent - */ - struct usb_phy *transceiver; void (*start_hnp)(struct ohci_hcd *ohci); /* diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 7f855d50cdf..c532cbeabfb 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -93,6 +93,12 @@ struct usb_hcd { */ const struct hc_driver *driver; /* hw-specific hooks */ + /* + * OTG and some Host controllers need software interaction with phys; + * other external phys should be software-transparent + */ + struct usb_phy *phy; + /* Flags that need to be manipulated atomically because they can * change while the host controller is running. Always use * set_bit() or clear_bit() to change their values. -- cgit v1.2.3-70-g09d2 From a276400d14bb5b98a5db1d87bbee7b1621abe4a1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 May 2012 12:24:59 +0200 Subject: USB: option: use usb_{get,set}_serial_data Use usb_{get,set}_serial_data to access usb-serial data. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 1aae9028cd0..6cb40ec5253 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1347,12 +1347,15 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) return -ENODEV; - data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); + data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; data->send_setup = option_send_setup; spin_lock_init(&data->susp_lock); data->private = (void *)id->driver_info; + + usb_set_serial_data(serial, data); + return 0; } @@ -1419,8 +1422,7 @@ static void option_instat_callback(struct urb *urb) static int option_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - struct usb_wwan_intf_private *intfdata = - (struct usb_wwan_intf_private *) serial->private; + struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); struct option_port_private *portdata; int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber; int val = 0; -- cgit v1.2.3-70-g09d2 From 378fac2a46decbcd16b42044303ba8a1a3f8001e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 May 2012 12:25:00 +0200 Subject: USB: option: clean up probe coding style Clean up option probe by introducing intermediate variables and fixing up comments. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 6ff37e67adf..ccf7f3c9a7c 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1306,33 +1306,41 @@ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { struct usb_wwan_intf_private *data; + struct usb_interface_descriptor *iface_desc = + &serial->interface->cur_altsetting->desc; + struct usb_device_descriptor *dev_desc = &serial->dev->descriptor; - /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ - if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && - serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && - serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8) + /* + * D-Link DWM 652 still exposes CD-Rom emulation interface in modem + * mode. + */ + if (dev_desc->idVendor == DLINK_VENDOR_ID && + dev_desc->idProduct == DLINK_PRODUCT_DWM_652 && + iface_desc->bInterfaceClass == 0x08) return -ENODEV; /* Bandrich modem and AT command interface is 0xff */ - if ((serial->dev->descriptor.idVendor == BANDRICH_VENDOR_ID || - serial->dev->descriptor.idVendor == PIRELLI_VENDOR_ID) && - serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) + if ((dev_desc->idVendor == BANDRICH_VENDOR_ID || + dev_desc->idVendor == PIRELLI_VENDOR_ID) && + iface_desc->bInterfaceClass != 0xff) return -ENODEV; - - /* Don't bind reserved interfaces (like network ones) which often have + /* + * Don't bind reserved interfaces (like network ones) which often have * the same class/subclass/protocol as the serial interfaces. Look at * the Windows driver .INF files for reserved interface numbers. */ if (is_blacklisted( - serial->interface->cur_altsetting->desc.bInterfaceNumber, + iface_desc->bInterfaceNumber, OPTION_BLACKLIST_RESERVED_IF, (const struct option_blacklist_info *) id->driver_info)) return -ENODEV; - - /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */ - if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID && - serial->dev->descriptor.idProduct == SAMSUNG_PRODUCT_GT_B3730 && - serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) + /* + * Don't bind network interface on Samsung GT-B3730, it is handled by + * a separate module. + */ + if (dev_desc->idVendor == SAMSUNG_VENDOR_ID && + dev_desc->idProduct == SAMSUNG_PRODUCT_GT_B3730 && + iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA) return -ENODEV; data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); -- cgit v1.2.3-70-g09d2 From e463c6dda8f5ee0baa6a5400e45d300287339536 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 May 2012 12:25:01 +0200 Subject: USB: option: handle send_setup blacklisting at probe Determine whether to use send_setup at probe time rather than at every call to send_setup. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ccf7f3c9a7c..9520c4c5474 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1276,6 +1276,10 @@ static struct usb_serial_driver * const serial_drivers[] = { static bool debug; +struct option_private { + u8 bInterfaceNumber; +}; + module_usb_serial_driver(serial_drivers, option_ids); static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason, @@ -1306,6 +1310,7 @@ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { struct usb_wwan_intf_private *data; + struct option_private *priv; struct usb_interface_descriptor *iface_desc = &serial->interface->cur_altsetting->desc; struct usb_device_descriptor *dev_desc = &serial->dev->descriptor; @@ -1346,9 +1351,22 @@ static int option_probe(struct usb_serial *serial, data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; - data->send_setup = option_send_setup; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + kfree(data); + return -ENOMEM; + } + + priv->bInterfaceNumber = iface_desc->bInterfaceNumber; + data->private = priv; + + if (!is_blacklisted(iface_desc->bInterfaceNumber, + OPTION_BLACKLIST_SENDSETUP, + (struct option_blacklist_info *)id->driver_info)) { + data->send_setup = option_send_setup; + } spin_lock_init(&data->susp_lock); - data->private = (void *)id->driver_info; usb_set_serial_data(serial, data); @@ -1357,11 +1375,13 @@ static int option_probe(struct usb_serial *serial, static void option_release(struct usb_serial *serial) { - struct usb_wwan_intf_private *priv = usb_get_serial_data(serial); + struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); + struct option_private *priv = intfdata->private; usb_wwan_release(serial); kfree(priv); + kfree(intfdata); } static void option_instat_callback(struct urb *urb) @@ -1429,16 +1449,10 @@ static int option_send_setup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); + struct option_private *priv = intfdata->private; struct usb_wwan_port_private *portdata; - int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber; int val = 0; - if (is_blacklisted(ifNum, OPTION_BLACKLIST_SENDSETUP, - (struct option_blacklist_info *) intfdata->private)) { - dbg("No send_setup on blacklisted interface #%d\n", ifNum); - return -EIO; - } - portdata = usb_get_serial_port_data(port); if (portdata->dtr_state) @@ -1446,9 +1460,9 @@ static int option_send_setup(struct usb_serial_port *port) if (portdata->rts_state) val |= 0x02; - return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); + return usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, priv->bInterfaceNumber, NULL, + 0, USB_CTRL_SET_TIMEOUT); } MODULE_AUTHOR(DRIVER_AUTHOR); -- cgit v1.2.3-70-g09d2 From fc8ef481df1bd6dac5d9c44b744f8050fcb6a5fe Mon Sep 17 00:00:00 2001 From: Jeffrin Jose Date: Wed, 30 May 2012 18:05:47 +0530 Subject: USB: storage: fixed space issues in coding style. Fixed space issues in coding style found by checkpatch.pl tool in drivers/usb/storage/protocol.c Signed-off-by: Jeffrin Jose Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/protocol.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 82dd834709c..5dfb4c36a1b 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -66,7 +66,7 @@ void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us) * NOTE: This only works because a scsi_cmnd struct field contains * a unsigned char cmnd[16], so we know we have storage available */ - for (; srb->cmd_len<12; srb->cmd_len++) + for (; srb->cmd_len < 12; srb->cmd_len++) srb->cmnd[srb->cmd_len] = 0; /* send the command to the transport layer */ @@ -76,14 +76,14 @@ void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us) void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us) { /* fix some commands -- this is a form of mode translation - * UFI devices only accept 12 byte long commands + * UFI devices only accept 12 byte long commands * * NOTE: This only works because a scsi_cmnd struct field contains * a unsigned char cmnd[16], so we know we have storage available */ /* Pad the ATAPI command with zeros */ - for (; srb->cmd_len<12; srb->cmd_len++) + for (; srb->cmd_len < 12; srb->cmd_len++) srb->cmnd[srb->cmd_len] = 0; /* set command length to 12 bytes (this affects the transport layer) */ -- cgit v1.2.3-70-g09d2 From 7e97243c2080ecae7129e83635227fdebd4feef6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 25 Apr 2012 17:22:37 -0700 Subject: USB: rename the usb misc class from "usb" to "usbmisc" This class was not named properly years ago, and it turns out that tools like udev can't properly see the devices in this class after booting due to the fact that there is a bus with the same name in the system. Changing this to "usbmisc" fixes this problem, and it solves the problem for the future when we want to unify classes and busses. Reported-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index e673b26e598..e5387a47ef6 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -92,7 +92,7 @@ static int init_usb_class(void) } kref_init(&usb_class->kref); - usb_class->class = class_create(THIS_MODULE, "usb"); + usb_class->class = class_create(THIS_MODULE, "usbmisc"); if (IS_ERR(usb_class->class)) { result = IS_ERR(usb_class->class); printk(KERN_ERR "class_create failed for usb devices\n"); -- cgit v1.2.3-70-g09d2 From 81df2d594340dcb6d1a02191976be88a1ca8120c Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 18 May 2012 21:27:43 +0200 Subject: USB: allow match on bInterfaceNumber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some composite USB devices provide multiple interfaces with different functions, all using "vendor-specific" for class/subclass/protocol. Another OS use interface numbers to match the driver and interface. It seems these devices are designed with that in mind - using static interface numbers for the different functions. This adds support for matching against the bInterfaceNumber, allowing such devices to be supported without having to resort to testing against interface number whitelists and/or blacklists in the probe. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 9 +++++++-- drivers/usb/core/message.c | 5 +++-- drivers/usb/core/sysfs.c | 5 +++-- include/linux/mod_devicetable.h | 7 +++++++ include/linux/usb.h | 16 ++++++++++++++++ scripts/mod/file2alias.c | 5 ++++- 6 files changed, 40 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f536aebc958..23d7bbd199a 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -622,14 +622,15 @@ int usb_match_one_id(struct usb_interface *interface, if (!usb_match_device(dev, id)) return 0; - /* The interface class, subclass, and protocol should never be + /* The interface class, subclass, protocol and number should never be * checked for a match if the device class is Vendor Specific, * unless the match record specifies the Vendor ID. */ if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC && !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | - USB_DEVICE_ID_MATCH_INT_PROTOCOL))) + USB_DEVICE_ID_MATCH_INT_PROTOCOL | + USB_DEVICE_ID_MATCH_INT_NUMBER))) return 0; if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && @@ -644,6 +645,10 @@ int usb_match_one_id(struct usb_interface *interface, (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) return 0; + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) && + (id->bInterfaceNumber != intf->desc.bInterfaceNumber)) + return 0; + return 1; } EXPORT_SYMBOL_GPL(usb_match_one_id); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b548cf1dbc6..ca7fc392fd9 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1559,7 +1559,7 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODALIAS=usb:" - "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", + "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02Xin%02X", le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct), le16_to_cpu(usb_dev->descriptor.bcdDevice), @@ -1568,7 +1568,8 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) usb_dev->descriptor.bDeviceProtocol, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) + alt->desc.bInterfaceProtocol, + alt->desc.bInterfaceNumber)) return -ENOMEM; return 0; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 9a56e3adf47..777f03c3772 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -840,7 +840,7 @@ static ssize_t show_modalias(struct device *dev, alt = intf->cur_altsetting; return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" - "ic%02Xisc%02Xip%02X\n", + "ic%02Xisc%02Xip%02Xin%02X\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), le16_to_cpu(udev->descriptor.bcdDevice), @@ -849,7 +849,8 @@ static ssize_t show_modalias(struct device *dev, udev->descriptor.bDeviceProtocol, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol); + alt->desc.bInterfaceProtocol, + alt->desc.bInterfaceNumber); } static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 5db93821f9c..7771d453e5f 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -78,6 +78,9 @@ struct ieee1394_device_id { * of a given interface; other interfaces may support other classes. * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass. * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass. + * @bInterfaceNumber: Number of interface; composite devices may use + * fixed interface numbers to differentiate between vendor-specific + * interfaces. * @driver_info: Holds information used by the driver. Usually it holds * a pointer to a descriptor understood by the driver, or perhaps * device flags. @@ -115,6 +118,9 @@ struct usb_device_id { __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; + /* Used for vendor-specific interface matches */ + __u8 bInterfaceNumber; + /* not matched against */ kernel_ulong_t driver_info; }; @@ -130,6 +136,7 @@ struct usb_device_id { #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 +#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400 #define HID_ANY_ID (~0) #define HID_BUS_ANY 0xffff diff --git a/include/linux/usb.h b/include/linux/usb.h index dea39dc551d..f717fbdaee8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -776,6 +776,22 @@ static inline int usb_make_path(struct usb_device *dev, char *buf, size_t size) .idProduct = (prod), \ .bInterfaceProtocol = (pr) +/** + * USB_DEVICE_INTERFACE_NUMBER - describe a usb device with a specific interface number + * @vend: the 16 bit USB Vendor ID + * @prod: the 16 bit USB Product ID + * @num: bInterfaceNumber value + * + * This macro is used to create a struct usb_device_id that matches a + * specific interface number of devices. + */ +#define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_NUMBER, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceNumber = (num) + /** * USB_DEVICE_INFO - macro used to describe a class of usb devices * @cl: bDeviceClass value diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 5759751a1f6..7ed6864ef65 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -156,7 +156,7 @@ static void device_id_check(const char *modname, const char *device_id, } /* USB is special because the bcdDevice can be matched against a numeric range */ -/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipN" */ +/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */ static void do_usb_entry(struct usb_device_id *id, unsigned int bcdDevice_initial, int bcdDevice_initial_digits, unsigned char range_lo, unsigned char range_hi, @@ -210,6 +210,9 @@ static void do_usb_entry(struct usb_device_id *id, ADD(alias, "ip", id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL, id->bInterfaceProtocol); + ADD(alias, "in", + id->match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER, + id->bInterfaceNumber); add_wildcard(alias); buf_printf(&mod->dev_table_buf, -- cgit v1.2.3-70-g09d2 From 4d4847b8502ec093864936a4669318675c0ced22 Mon Sep 17 00:00:00 2001 From: Jeffrin Jose Date: Sun, 20 May 2012 00:56:58 +0530 Subject: usb: early: fixed coding style issue related to : operator Fixed a space issue relating to ":" operator found by checkpatch.pl tool in drivers/usb/early/ehci-dbgp.c Signed-off-by: Jeffrin Jose Signed-off-by: Greg Kroah-Hartman --- drivers/usb/early/ehci-dbgp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index 1fc8f124980..ee0ebacf822 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -334,7 +334,7 @@ static int dbgp_control_msg(unsigned devnum, int requesttype, int ret; read = (requesttype & USB_DIR_IN) != 0; - if (size > (read ? DBGP_MAX_PACKET:0)) + if (size > (read ? DBGP_MAX_PACKET : 0)) return -1; /* Compute the control message */ -- cgit v1.2.3-70-g09d2 From e714fad054e219b52eee17591d95d725888a95c4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 22 May 2012 11:36:59 +0200 Subject: usb-core: Set intfdata to NULL if a driver's probe method failed Ensure that intfdata always is NULL if no driver is bound: 1) drvdata is for a driver to store a pointer to driver specific data 2) If no driver is bound, there is no driver specific data associated with the device 3) Thus logically drvdata should be NULL if no driver is bound. We already set intfdata to NULL when a driver is unbound, to ensure that intfdata will be NULL even if the drivers disconnect method does not properly clear it. This ensures that intfdata will also be NULL after a failed probe, even if the driver's probe method left a (likely dangling) pointer in there. Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 23d7bbd199a..69781016a26 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -367,6 +367,7 @@ static int usb_probe_interface(struct device *dev) return error; err: + usb_set_intfdata(intf, NULL); intf->needs_remote_wakeup = 0; intf->condition = USB_INTERFACE_UNBOUND; usb_cancel_queued_reset(intf); -- cgit v1.2.3-70-g09d2 From 6b03b13336ee5d8da7bda8799c9ed990e3daedcc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Jun 2012 13:24:42 +0300 Subject: usb: otg: twl: add missing IRQF_ONESHOT this patch fixes the following warning: [ 2.825378] genirq: Threaded irq requested \ with handler=NULL and !ONESHOT for irq 363 Signed-off-by: Felipe Balbi --- drivers/usb/otg/twl4030-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index c4a86da858e..02979306bf8 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -651,8 +651,8 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) */ twl->irq_enabled = true; status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "twl4030_usb", twl); + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "twl4030_usb", twl); if (status < 0) { dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq, status); -- cgit v1.2.3-70-g09d2 From 1a12af1a751311e129ff8e8ca18f83613b78a83c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 12 Jun 2012 12:42:17 +0200 Subject: usb: gadget: mass_storage: remove unused options This commit removes thread_name and lun_name_format fields from the fsg_config structure. Those fields are not used by any in-tree code and their usefulness is rather theoretical. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_mass_storage.c | 50 ++++++++----------------------------- 1 file changed, 10 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 72116fa49ff..9b29e8eab18 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -75,25 +75,6 @@ * ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12) * commands for this LUN shall be ignored. * - * lun_name_format A printf-like format for names of the LUN - * devices. This determines how the - * directory in sysfs will be named. - * Unless you are using several MSFs in - * a single gadget (as opposed to single - * MSF in many configurations) you may - * leave it as NULL (in which case - * "lun%d" will be used). In the format - * you can use "%d" to index LUNs for - * MSF's with more than one LUN. (Beware - * that there is only one integer given - * as an argument for the format and - * specifying invalid format may cause - * unspecified behaviour.) - * thread_name Name of the kernel thread process used by the - * MSF. You can safely set it to NULL - * (in which case default "file-storage" - * will be used). - * * vendor_name * product_name * release Information used as a reply to INQUIRY @@ -155,15 +136,14 @@ * a buffer from being used by more than one endpoint. * * - * The pathnames of the backing files and the ro settings are - * available in the attribute files "file" and "ro" in the lun (or - * to be more precise in a directory which name comes from - * "lun_name_format" option!) subdirectory of the gadget's sysfs - * directory. If the "removable" option is set, writing to these - * files will simulate ejecting/loading the medium (writing an empty - * line means eject) and adjusting a write-enable tab. Changes to the - * ro setting are not allowed when the medium is loaded or if CD-ROM - * emulation is being used. + * The pathnames of the backing files, the ro settings and nofua + * settings are available in the attribute files "file", "ro" and + * "nofua" in the lun subdirectory of the gadget's sysfs directory. + * If the "removable" option is set, writing to these files will + * simulate ejecting/loading the medium (writing an empty line means + * eject) and adjusting a write-enable tab. Changes to the ro setting + * are not allowed when the medium is loaded or if CD-ROM emulation is + * being used. * * When a LUN receive an "eject" SCSI request (Start/Stop Unit), * if the LUN is removable, the backing file is released to simulate @@ -417,9 +397,6 @@ struct fsg_config { char nofua; } luns[FSG_MAX_LUNS]; - const char *lun_name_format; - const char *thread_name; - /* Callback functions. */ const struct fsg_operations *ops; /* Gadget's private data. */ @@ -2792,11 +2769,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, curlun->dev.parent = &gadget->dev; /* curlun->dev.driver = &fsg_driver.driver; XXX */ dev_set_drvdata(&curlun->dev, &common->filesem); - dev_set_name(&curlun->dev, - cfg->lun_name_format - ? cfg->lun_name_format - : "lun%d", - i); + dev_set_name(&curlun->dev, "lun%d", i); rc = device_register(&curlun->dev); if (rc) { @@ -2878,8 +2851,7 @@ buffhds_first_it: /* Tell the thread to start working */ common->thread_task = - kthread_create(fsg_main_thread, common, - cfg->thread_name ?: "file-storage"); + kthread_create(fsg_main_thread, common, "file-storage"); if (IS_ERR(common->thread_task)) { rc = PTR_ERR(common->thread_task); goto error_release; @@ -3175,8 +3147,6 @@ fsg_config_from_params(struct fsg_config *cfg, } /* Let MSF use defaults */ - cfg->lun_name_format = 0; - cfg->thread_name = 0; cfg->vendor_name = 0; cfg->product_name = 0; cfg->release = 0xffff; -- cgit v1.2.3-70-g09d2 From a8287a4ed543494c121050dc453972902637e6de Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 12 Jun 2012 12:42:18 +0200 Subject: usb: gadget: mass_storage: add documentation This commit adds Documentation/usb/mass-storage.txt file. It contains description of how to use the mass storage gadget from user space. It elaborates on madule parameters and sysfs interface more then it was written in the comments in the source code. Signed-off-by: Michal Nazarewicz Acked-by: Alan Stern Signed-off-by: Felipe Balbi --- Documentation/usb/mass-storage.txt | 226 ++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_mass_storage.c | 69 ++--------- 2 files changed, 233 insertions(+), 62 deletions(-) create mode 100644 Documentation/usb/mass-storage.txt (limited to 'drivers') diff --git a/Documentation/usb/mass-storage.txt b/Documentation/usb/mass-storage.txt new file mode 100644 index 00000000000..e9b9334627b --- /dev/null +++ b/Documentation/usb/mass-storage.txt @@ -0,0 +1,226 @@ +* Overview + + Mass Storage Gadget (or MSG) acts as a USB Mass Storage device, + appearing to the host as a disk or a CD-ROM drive. It supports + multiple logical units (LUNs). Backing storage for each LUN is + provided by a regular file or a block device, access can be limited + to read-only, and gadget can indicate that it is removable and/or + CD-ROM (the latter implies read-only access). + + Its requirements are modest; only a bulk-in and a bulk-out endpoint + are needed. The memory requirement amounts to two 16K buffers. + Support is included for full-speed, high-speed and SuperSpeed + operation. + + Note that the driver is slightly non-portable in that it assumes + a single memory/DMA buffer will be useable for bulk-in and bulk-out + endpoints. With most device controllers this is not an issue, but + there may be some with hardware restrictions that prevent a buffer + from being used by more than one endpoint. + + This document describes how to use the gadget from user space, its + relation to mass storage function (or MSF) and different gadgets + using it, and how it differs from File Storage Gadget (or FSG). It + will talk only briefly about how to use MSF within composite + gadgets. + +* Module parameters + + The mass storage gadget accepts the following mass storage specific + module parameters: + + - file=filename[,filename...] + + This parameter lists paths to files or block devices used for + backing storage for each logical unit. There may be at most + FSG_MAX_LUNS (8) LUNs set. If more files are specified, they will + be silently ignored. See also “luns” parameter. + + *BEWARE* that if a file is used as a backing storage, it may not + be modified by any other process. This is because the host + assumes the data does not change without its knowledge. It may be + read, but (if the logical unit is writable) due to buffering on + the host side, the contents are not well defined. + + The size of the logical unit will be rounded down to a full + logical block. The logical block size is 2048 bytes for LUNs + simulating CD-ROM, block size of the device if the backing file is + a block device, or 512 bytes otherwise. + + - removable=b[,b...] + + This parameter specifies whether each logical unit should be + removable. “b” here is either “y”, “Y” or “1” for true or “n”, + “N” or “0” for false. + + If this option is set for a logical unit, gadget will accept an + “eject” SCSI request (Start/Stop Unit). When it is sent, the + backing file will be closed to simulate ejection and the logical + unit will not be mountable by the host until a new backing file is + specified by userspace on the device (see “sysfs entries” + section). + + If a logical unit is not removable (the default), a backing file + must be specified for it with the “file” parameter as the module + is loaded. The same applies if the module is built in, no + exceptions. + + The default value of the flag is false, *HOWEVER* it used to be + true. This has been changed to better match File Storage Gadget + and because it seems like a saner default after all. Thus to + maintain compatibility with older kernels, it's best to specify + the default values. Also, if one relied on old default, explicit + “n” needs to be specified now. + + Note that “removable” means the logical unit's media can be + ejected or removed (as is true for a CD-ROM drive or a card + reader). It does *not* mean that the entire gadget can be + unplugged from the host; the proper term for that is + “hot-unpluggable”. + + - cdrom=b[,b...] + + This parameter specifies whether each logical unit should simulate + CD-ROM. The default is false. + + - ro=b[,b...] + + This parameter specifies whether each logical unit should be + reported as read only. This will prevent host from modifying the + backing files. + + Note that if this flag for given logical unit is false but the + backing file could not be opened in read/write mode, the gadget + will fall back to read only mode anyway. + + The default value for non-CD-ROM logical units is false; for + logical units simulating CD-ROM it is forced to true. + + - nofua=b[,b...] + + This parameter specifies whether FUA flag should be ignored in SCSI + Write10 and Write12 commands sent to given logical units. + + MS Windows mounts removable storage in “Removal optimised mode” by + default. All the writes to the media are synchronous, which is + achieved by setting the FUA (Force Unit Access) bit in SCSI + Write(10,12) commands. This forces each write to wait until the + data has actually been written out and prevents I/O requests + aggregation in block layer dramatically decreasing performance. + + Note that this may mean that if the device is powered from USB and + the user unplugs the device without unmounting it first (which at + least some Windows users do), the data may be lost. + + The default value is false. + + - luns=N + + This parameter specifies number of logical units the gadget will + have. It is limited by FSG_MAX_LUNS (8) and higher value will be + capped. + + If this parameter is provided, and the number of files specified + in “file” argument is greater then the value of “luns”, all excess + files will be ignored. + + If this parameter is not present, the number of logical units will + be deduced from the number of files specified in the “file” + parameter. If the file parameter is missing as well, one is + assumed. + + - stall=b + + Specifies whether the gadget is allowed to halt bulk endpoints. + The default is determined according to the type of USB device + controller, but usually true. + + In addition to the above, the gadget also accepts the following + parameters defined by the composite framework (they are common to + all composite gadgets so just a quick listing): + + - idVendor -- USB Vendor ID (16 bit integer) + - idProduct -- USB Product ID (16 bit integer) + - bcdDevice -- USB Device version (BCD) (16 bit integer) + - iManufacturer -- USB Manufacturer string (string) + - iProduct -- USB Product string (string) + - iSerialNumber -- SerialNumber string (sting) + +* sysfs entries + + For each logical unit, the gadget creates a directory in the sysfs + hierarchy. Inside of it the following three files are created: + + - file + + When read it returns the path to the backing file for the given + logical unit. If there is no backing file (possible only if the + logical unit is removable), the content is empty. + + When written into, it changes the backing file for given logical + unit. This change can be performed even if given logical unit is + not specified as removable (but that may look strange to the + host). It may fail, however, if host disallowed medium removal + with the Prevent-Allow Medium Removal SCSI command. + + - ro + + Reflects the state of ro flag for the given logical unit. It can + be read any time, and written to when there is no backing file + open for given logical unit. + + - nofua + + Reflects the state of nofua flag for given logical unit. It can + be read and written. + + Other then those, as usual, the values of module parameters can be + read from /sys/module/g_mass_storage/parameters/* files. + +* Other gadgets using mass storage function + + The Mass Storage Gadget uses the Mass Storage Function to handle + mass storage protocol. As a composite function, MSF may be used by + other gadgets as well (eg. g_multi and acm_ms). + + All of the information in previous sections are valid for other + gadgets using MSF, except that support for mass storage related + module parameters may be missing, or the parameters may have + a prefix. To figure out whether any of this is true one needs to + consult the gadget's documentation or its source code. + + For examples of how to include mass storage function in gadgets, one + may take a look at mass_storage.c, acm_ms.c and multi.c (sorted by + complexity). + +* Relation to file storage gadget + + The Mass Storage Function and thus the Mass Storage Gadget has been + based on the File Storage Gadget. The difference between the two is + that MSG is a composite gadget (ie. uses the composite framework) + while file storage gadget is a traditional gadget. From userspace + point of view this distinction does not really matter, but from + kernel hacker's point of view, this means that (i) MSG does not + duplicate code needed for handling basic USB protocol commands and + (ii) MSF can be used in any other composite gadget. + + Because of that, File Storage Gadget has been deprecated and + scheduled to be removed in Linux 3.8. All users need to transition + to the Mass Storage Gadget by that time. The two gadgets behave + mostly the same from the outside except: + + 1. In FSG the “removable” and “cdrom” module parameters set the flag + for all logical units whereas in MSG they accept a list of y/n + values for each logical unit. If one uses only a single logical + unit this does not matter, but if there are more, the y/n value + needs to be repeated for each logical unit. + + 2. FSG's “serial”, “vendor”, “product” and “release” module + parameters are handled in MSG by the composite layer's parameters + named respectively: “iSerialnumber”, “idVendor”, “idProduct” and + “bcdDevice”. + + 3. MSG does not support FSG's test mode, thus “transport”, + “protocol” and “buflen” FSG's module parameters are not + supported. MSG always uses SCSI protocol with bulk only + transport mode and 16 KiB buffers. diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 9b29e8eab18..7ad4f819752 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -44,12 +44,12 @@ * function for a USB device, it also illustrates a technique of * double-buffering for increased throughput. * - * Function supports multiple logical units (LUNs). Backing storage - * for each LUN is provided by a regular file or a block device. - * Access for each LUN can be limited to read-only. Moreover, the - * function can indicate that LUN is removable and/or CD-ROM. (The - * later implies read-only access.) - * + * For more information about MSF and in particular its module + * parameters and sysfs interface read the + * file. + */ + +/* * MSF is configured by specifying a fsg_config structure. It has the * following fields: * @@ -95,61 +95,6 @@ * data track and no audio tracks; hence there need be only one * backing file per LUN. * - * - * MSF includes support for module parameters. If gadget using it - * decides to use it, the following module parameters will be - * available: - * - * file=filename[,filename...] - * Names of the files or block devices used for - * backing storage. - * ro=b[,b...] Default false, boolean for read-only access. - * removable=b[,b...] - * Default false, boolean for removable media. - * cdrom=b[,b...] Default false, boolean for whether to emulate - * a CD-ROM drive. - * nofua=b[,b...] Default false, booleans for ignore FUA flag - * in SCSI WRITE(10,12) commands - * luns=N Default N = number of filenames, number of - * LUNs to support. - * stall Default determined according to the type of - * USB device controller (usually true), - * boolean to permit the driver to halt - * bulk endpoints. - * - * The module parameters may be prefixed with some string. You need - * to consult gadget's documentation or source to verify whether it is - * using those module parameters and if it does what are the prefixes - * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is - * the prefix). - * - * - * Requirements are modest; only a bulk-in and a bulk-out endpoint are - * needed. The memory requirement amounts to two 16K buffers, size - * configurable by a parameter. Support is included for both - * full-speed and high-speed operation. - * - * Note that the driver is slightly non-portable in that it assumes a - * single memory/DMA buffer will be useable for bulk-in, bulk-out, and - * interrupt-in endpoints. With most device controllers this isn't an - * issue, but there may be some with hardware restrictions that prevent - * a buffer from being used by more than one endpoint. - * - * - * The pathnames of the backing files, the ro settings and nofua - * settings are available in the attribute files "file", "ro" and - * "nofua" in the lun subdirectory of the gadget's sysfs directory. - * If the "removable" option is set, writing to these files will - * simulate ejecting/loading the medium (writing an empty line means - * eject) and adjusting a write-enable tab. Changes to the ro setting - * are not allowed when the medium is loaded or if CD-ROM emulation is - * being used. - * - * When a LUN receive an "eject" SCSI request (Start/Stop Unit), - * if the LUN is removable, the backing file is released to simulate - * ejection. - * - * * This function is heavily based on "File-backed Storage Gadget" by * Alan Stern which in turn is heavily based on "Gadget Zero" by David * Brownell. The driver's SCSI command interface was based on the @@ -191,7 +136,7 @@ * In normal operation the main thread is started during the gadget's * fsg_bind() callback and stopped during fsg_unbind(). But it can * also exit when it receives a signal, and there's no point leaving - * the gadget running when the thread is dead. At of this moment, MSF + * the gadget running when the thread is dead. As of this moment, MSF * provides no way to deregister the gadget when thread dies -- maybe * a callback functions is needed. * -- cgit v1.2.3-70-g09d2 From 99c515005857ff7d6cd5c2ba272ccab5dc0ea648 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 13 Jun 2012 12:54:45 +0200 Subject: usb: gadget: hidg: register OUT INT endpoint for SET_REPORT The hidg function driver currently handles its SET_REPORT calls via EP0. This is the implicit behaviour when no OUT interrupt endpoint is configured and generally works fine. The problem is that due to EP0's role in the gadget framework, we cannot hold back packets and control traffic flow to sync it to the char device, and hence there's a high risk of loosing packets with this implementation. This patch adds an OUT interrupt endpoint to the interface and queues a fix number of request to catch SET_REPORT events. According to the specs, host drivers should always use the dedicated OUT endpoint when present. The char device's read implementation was rewritten to retrieve data from the list of completed output requests. Signed-off-by: Daniel Mack Cc: Felipe Balbi Cc: Greg Kroah-Hartman Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_hid.c | 208 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 165 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 3b3932c5536..16a8b1c15c6 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -26,6 +26,12 @@ static struct class *hidg_class; /*-------------------------------------------------------------------------*/ /* HID gadget struct */ +struct f_hidg_req_list { + struct usb_request *req; + unsigned int pos; + struct list_head list; +}; + struct f_hidg { /* configuration */ unsigned char bInterfaceSubClass; @@ -35,10 +41,10 @@ struct f_hidg { unsigned short report_length; /* recv report */ - char *set_report_buff; - unsigned short set_report_length; + struct list_head completed_out_req; spinlock_t spinlock; wait_queue_head_t read_queue; + unsigned int qlen; /* send report */ struct mutex lock; @@ -49,7 +55,9 @@ struct f_hidg { int minor; struct cdev cdev; struct usb_function func; + struct usb_ep *in_ep; + struct usb_ep *out_ep; }; static inline struct f_hidg *func_to_hidg(struct usb_function *f) @@ -65,7 +73,7 @@ static struct usb_interface_descriptor hidg_interface_desc = { .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ .bAlternateSetting = 0, - .bNumEndpoints = 1, + .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_HID, /* .bInterfaceSubClass = DYNAMIC */ /* .bInterfaceProtocol = DYNAMIC */ @@ -96,10 +104,23 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { */ }; +static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + /*.wMaxPacketSize = DYNAMIC */ + .bInterval = 4, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; + static struct usb_descriptor_header *hidg_hs_descriptors[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, + (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, NULL, }; @@ -117,10 +138,23 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { */ }; +static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + /*.wMaxPacketSize = DYNAMIC */ + .bInterval = 10, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; + static struct usb_descriptor_header *hidg_fs_descriptors[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, + (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, NULL, }; @@ -130,9 +164,11 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = { static ssize_t f_hidg_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr) { - struct f_hidg *hidg = file->private_data; - char *tmp_buff = NULL; - unsigned long flags; + struct f_hidg *hidg = file->private_data; + struct f_hidg_req_list *list; + struct usb_request *req; + unsigned long flags; + int ret; if (!count) return 0; @@ -142,8 +178,9 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, spin_lock_irqsave(&hidg->spinlock, flags); -#define READ_COND (hidg->set_report_buff != NULL) +#define READ_COND (!list_empty(&hidg->completed_out_req)) + /* wait for at least one buffer to complete */ while (!READ_COND) { spin_unlock_irqrestore(&hidg->spinlock, flags); if (file->f_flags & O_NONBLOCK) @@ -155,19 +192,34 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, spin_lock_irqsave(&hidg->spinlock, flags); } - - count = min_t(unsigned, count, hidg->set_report_length); - tmp_buff = hidg->set_report_buff; - hidg->set_report_buff = NULL; - + /* pick the first one */ + list = list_first_entry(&hidg->completed_out_req, + struct f_hidg_req_list, list); + req = list->req; + count = min_t(unsigned int, count, req->actual - list->pos); spin_unlock_irqrestore(&hidg->spinlock, flags); - if (tmp_buff != NULL) { - /* copy to user outside spinlock */ - count -= copy_to_user(buffer, tmp_buff, count); - kfree(tmp_buff); - } else - count = -ENOMEM; + /* copy to user outside spinlock */ + count -= copy_to_user(buffer, req->buf + list->pos, count); + list->pos += count; + + /* + * if this request is completely handled and transfered to + * userspace, remove its entry from the list and requeue it + * again. Otherwise, we will revisit it again upon the next + * call, taking into account its current read position. + */ + if (list->pos == req->actual) { + spin_lock_irqsave(&hidg->spinlock, flags); + list_del(&list->list); + kfree(list); + spin_unlock_irqrestore(&hidg->spinlock, flags); + + req->length = hidg->report_length; + ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); + if (ret < 0) + return ret; + } return count; } @@ -282,28 +334,37 @@ static int f_hidg_open(struct inode *inode, struct file *fd) /*-------------------------------------------------------------------------*/ /* usb_function */ -static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) +static struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length) { - struct f_hidg *hidg = (struct f_hidg *)req->context; - - if (req->status != 0 || req->buf == NULL || req->actual == 0) { - ERROR(hidg->func.config->cdev, "%s FAILED\n", __func__); - return; + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = length; + req->buf = kmalloc(length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } } + return req; +} - spin_lock(&hidg->spinlock); - - hidg->set_report_buff = krealloc(hidg->set_report_buff, - req->actual, GFP_ATOMIC); +static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_hidg *hidg = (struct f_hidg *) req->context; + struct f_hidg_req_list *req_list; + unsigned long flags; - if (hidg->set_report_buff == NULL) { - spin_unlock(&hidg->spinlock); + req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); + if (!req_list) return; - } - hidg->set_report_length = req->actual; - memcpy(hidg->set_report_buff, req->buf, req->actual); - spin_unlock(&hidg->spinlock); + req_list->req = req; + + spin_lock_irqsave(&hidg->spinlock, flags); + list_add_tail(&req_list->list, &hidg->completed_out_req); + spin_unlock_irqrestore(&hidg->spinlock, flags); wake_up(&hidg->read_queue); } @@ -344,9 +405,7 @@ static int hidg_setup(struct usb_function *f, case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_SET_REPORT): VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); - req->context = hidg; - req->complete = hidg_set_report_complete; - goto respond; + goto stall; break; case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 @@ -403,16 +462,25 @@ respond: static void hidg_disable(struct usb_function *f) { struct f_hidg *hidg = func_to_hidg(f); + struct f_hidg_req_list *list, *next; usb_ep_disable(hidg->in_ep); hidg->in_ep->driver_data = NULL; + + usb_ep_disable(hidg->out_ep); + hidg->out_ep->driver_data = NULL; + + list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { + list_del(&list->list); + kfree(list); + } } static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct usb_composite_dev *cdev = f->config->cdev; struct f_hidg *hidg = func_to_hidg(f); - int status = 0; + int i, status = 0; VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); @@ -429,11 +497,55 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } status = usb_ep_enable(hidg->in_ep); if (status < 0) { - ERROR(cdev, "Enable endpoint FAILED!\n"); + ERROR(cdev, "Enable IN endpoint FAILED!\n"); goto fail; } hidg->in_ep->driver_data = hidg; } + + + if (hidg->out_ep != NULL) { + /* restart endpoint */ + if (hidg->out_ep->driver_data != NULL) + usb_ep_disable(hidg->out_ep); + + status = config_ep_by_speed(f->config->cdev->gadget, f, + hidg->out_ep); + if (status) { + ERROR(cdev, "config_ep_by_speed FAILED!\n"); + goto fail; + } + status = usb_ep_enable(hidg->out_ep); + if (status < 0) { + ERROR(cdev, "Enable IN endpoint FAILED!\n"); + goto fail; + } + hidg->out_ep->driver_data = hidg; + + /* + * allocate a bunch of read buffers and queue them all at once. + */ + for (i = 0; i < hidg->qlen && status == 0; i++) { + struct usb_request *req = + hidg_alloc_ep_req(hidg->out_ep, + hidg->report_length); + if (req) { + req->complete = hidg_set_report_complete; + req->context = hidg; + status = usb_ep_queue(hidg->out_ep, req, + GFP_ATOMIC); + if (status) + ERROR(cdev, "%s queue req --> %d\n", + hidg->out_ep->name, status); + } else { + usb_ep_disable(hidg->out_ep); + hidg->out_ep->driver_data = NULL; + status = -ENOMEM; + goto fail; + } + } + } + fail: return status; } @@ -470,13 +582,18 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) ep->driver_data = c->cdev; /* claim */ hidg->in_ep = ep; + ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); + if (!ep) + goto fail; + ep->driver_data = c->cdev; /* claim */ + hidg->out_ep = ep; + /* preallocate request and buffer */ status = -ENOMEM; hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); if (!hidg->req) goto fail; - hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); if (!hidg->req->buf) goto fail; @@ -486,12 +603,12 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; hidg_desc.desc[0].wDescriptorLength = cpu_to_le16(hidg->report_desc_length); - hidg->set_report_buff = NULL; - /* copy descriptors */ f->descriptors = usb_copy_descriptors(hidg_fs_descriptors); if (!f->descriptors) @@ -500,6 +617,8 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) if (gadget_is_dualspeed(c->cdev->gadget)) { hidg_hs_in_ep_desc.bEndpointAddress = hidg_fs_in_ep_desc.bEndpointAddress; + hidg_hs_out_ep_desc.bEndpointAddress = + hidg_fs_out_ep_desc.bEndpointAddress; f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors); if (!f->hs_descriptors) goto fail; @@ -509,6 +628,7 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) spin_lock_init(&hidg->spinlock); init_waitqueue_head(&hidg->write_queue); init_waitqueue_head(&hidg->read_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); /* create char device */ cdev_init(&hidg->cdev, &f_hidg_fops); @@ -553,7 +673,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_descriptors(f->descriptors); kfree(hidg->report_desc); - kfree(hidg->set_report_buff); kfree(hidg); } @@ -624,6 +743,9 @@ int __init hidg_bind_config(struct usb_configuration *c, hidg->func.disable = hidg_disable; hidg->func.setup = hidg_setup; + /* this could me made configurable at some point */ + hidg->qlen = 4; + status = usb_add_function(c, &hidg->func); if (status) kfree(hidg); -- cgit v1.2.3-70-g09d2 From 22258f4906aa87e0c0debcad22cb292453e2ebfb Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 14 Jun 2012 10:02:24 +0200 Subject: usb: hsotg: samsung: Replace endpoint specific locks with a global lock The endpoint specific locks are replaced with a global lock. This is crucial for running s3c-hsotg driver on a SMP SoC. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 54 ++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index d37585105bb..b10791282f1 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -112,7 +112,6 @@ struct s3c_hsotg_ep { struct s3c_hsotg_req *req; struct dentry *debugfs; - spinlock_t lock; unsigned long total_data; unsigned int size_loaded; @@ -156,6 +155,8 @@ struct s3c_hsotg { struct usb_gadget_driver *driver; struct s3c_hsotg_plat *plat; + spinlock_t lock; + void __iomem *regs; int irq; struct clk *clk; @@ -901,6 +902,8 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, ep->name, req, req->length, req->buf, req->no_interrupt, req->zero, req->short_not_ok); + spin_lock_irqsave(&hs->lock, irqflags); + /* initialise status of the request */ INIT_LIST_HEAD(&hs_req->queue); req->actual = 0; @@ -913,15 +916,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, return ret; } - spin_lock_irqsave(&hs_ep->lock, irqflags); - first = list_empty(&hs_ep->queue); list_add_tail(&hs_req->queue, &hs_ep->queue); if (first) s3c_hsotg_start_req(hs, hs_ep, hs_req, false); - spin_unlock_irqrestore(&hs_ep->lock, irqflags); + spin_unlock_irqrestore(&hs->lock, irqflags); return 0; } @@ -1381,9 +1382,9 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg, */ if (hs_req->req.complete) { - spin_unlock(&hs_ep->lock); + spin_unlock(&hsotg->lock); hs_req->req.complete(&hs_ep->ep, &hs_req->req); - spin_lock(&hs_ep->lock); + spin_lock(&hsotg->lock); } /* @@ -1418,9 +1419,9 @@ static void s3c_hsotg_complete_request_lock(struct s3c_hsotg *hsotg, { unsigned long flags; - spin_lock_irqsave(&hs_ep->lock, flags); + spin_lock_irqsave(&hsotg->lock, flags); s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result); - spin_unlock_irqrestore(&hs_ep->lock, flags); + spin_unlock_irqrestore(&hsotg->lock, flags); } /** @@ -1442,6 +1443,8 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) int max_req; int read_ptr; + spin_lock(&hsotg->lock); + if (!hs_req) { u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx)); int ptr; @@ -1454,11 +1457,10 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) for (ptr = 0; ptr < size; ptr += 4) (void)readl(fifo); + spin_unlock(&hsotg->lock); return; } - spin_lock(&hs_ep->lock); - to_read = size; read_ptr = hs_req->req.actual; max_req = hs_req->req.length - read_ptr; @@ -1486,7 +1488,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) */ readsl(fifo, hs_req->req.buf + read_ptr, to_read); - spin_unlock(&hs_ep->lock); + spin_unlock(&hsotg->lock); } /** @@ -2123,7 +2125,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, struct s3c_hsotg_req *req, *treq; unsigned long flags; - spin_lock_irqsave(&ep->lock, flags); + spin_lock_irqsave(&hsotg->lock, flags); list_for_each_entry_safe(req, treq, &ep->queue, queue) { /* @@ -2138,7 +2140,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, result); } - spin_unlock_irqrestore(&ep->lock, flags); + spin_unlock_irqrestore(&hsotg->lock, flags); } #define call_gadget(_hs, _entry) \ @@ -2602,7 +2604,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n", __func__, epctrl, epctrl_reg); - spin_lock_irqsave(&hs_ep->lock, flags); + spin_lock_irqsave(&hsotg->lock, flags); epctrl &= ~(DxEPCTL_EPType_MASK | DxEPCTL_MPS_MASK); epctrl |= DxEPCTL_MPS(mps); @@ -2681,7 +2683,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); out: - spin_unlock_irqrestore(&hs_ep->lock, flags); + spin_unlock_irqrestore(&hsotg->lock, flags); return ret; } @@ -2711,7 +2713,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); - spin_lock_irqsave(&hs_ep->lock, flags); + spin_lock_irqsave(&hsotg->lock, flags); ctrl = readl(hsotg->regs + epctrl_reg); ctrl &= ~DxEPCTL_EPEna; @@ -2724,7 +2726,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) /* disable endpoint interrupts */ s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); - spin_unlock_irqrestore(&hs_ep->lock, flags); + spin_unlock_irqrestore(&hsotg->lock, flags); return 0; } @@ -2759,15 +2761,15 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); - spin_lock_irqsave(&hs_ep->lock, flags); + spin_lock_irqsave(&hs->lock, flags); if (!on_list(hs_ep, hs_req)) { - spin_unlock_irqrestore(&hs_ep->lock, flags); + spin_unlock_irqrestore(&hs->lock, flags); return -EINVAL; } s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET); - spin_unlock_irqrestore(&hs_ep->lock, flags); + spin_unlock_irqrestore(&hs->lock, flags); return 0; } @@ -2789,7 +2791,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); - spin_lock_irqsave(&hs_ep->lock, irqflags); + spin_lock_irqsave(&hs->lock, irqflags); /* write both IN and OUT control registers */ @@ -2825,7 +2827,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) writel(epctl, hs->regs + epreg); - spin_unlock_irqrestore(&hs_ep->lock, irqflags); + spin_unlock_irqrestore(&hs->lock, irqflags); return 0; } @@ -3061,8 +3063,6 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg, INIT_LIST_HEAD(&hs_ep->queue); INIT_LIST_HEAD(&hs_ep->ep.ep_list); - spin_lock_init(&hs_ep->lock); - /* add to the list of endpoints known by the gadget driver */ if (epnum) list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list); @@ -3340,7 +3340,7 @@ static int ep_show(struct seq_file *seq, void *v) seq_printf(seq, "request list (%p,%p):\n", ep->queue.next, ep->queue.prev); - spin_lock_irqsave(&ep->lock, flags); + spin_lock_irqsave(&hsotg->lock, flags); list_for_each_entry(req, &ep->queue, queue) { if (--show_limit < 0) { @@ -3355,7 +3355,7 @@ static int ep_show(struct seq_file *seq, void *v) req->req.actual, req->req.status); } - spin_unlock_irqrestore(&ep->lock, flags); + spin_unlock_irqrestore(&hsotg->lock, flags); return 0; } @@ -3507,6 +3507,8 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) goto err_clk; } + spin_lock_init(&hsotg->lock); + hsotg->irq = ret; ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, -- cgit v1.2.3-70-g09d2 From 2b19a52cc8a31ede990323d46a7faeeeba76bb8f Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 14 Jun 2012 10:02:25 +0200 Subject: usb: hsotg: samsung: Protect the udc_stop routine with spinlock Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index b10791282f1..ee1fe2b8b07 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2988,6 +2988,7 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { struct s3c_hsotg *hsotg = to_hsotg(gadget); + unsigned long flags = 0; int ep; if (!hsotg) @@ -3000,6 +3001,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, for (ep = 0; ep < hsotg->num_of_eps; ep++) s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_phy_disable(hsotg); regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); @@ -3007,6 +3010,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, hsotg->gadget.speed = USB_SPEED_UNKNOWN; hsotg->gadget.dev.driver = NULL; + spin_unlock_irqrestore(&hsotg->lock, flags); + dev_info(hsotg->dev, "unregistered gadget driver '%s'\n", driver->driver.name); -- cgit v1.2.3-70-g09d2 From 5ad1d3160973c588db806072968d7a2a93cb2a80 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 14 Jun 2012 10:02:26 +0200 Subject: usb: hsotg: samsung: smp Provide *_lock functions abstraction layer for SMP SoCs For SMP processors the spin_lock_irqsave is _only_ able to disable interrupt on a core on which it is executed. Therefore there may be a situation when other cores raise s3c-hsotg IRQ. Then there are several places where critical sections can be overwritten. To protect the above thread, a spin_lock in the interrupt handler has been added. Due to coherent memory view (especially L1 cache) the spin lock variable control access to IRQ handler only for one CPU core. In this way serialization to access this driver is provided and hence several spin_lock_* routines could be removed from IRQ handler's related functions. The complete_request_lock function has been removed since all its calls are performed from interrupt (spin lock protected) context. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 97 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index ee1fe2b8b07..d208c46341d 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -895,15 +895,12 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, struct s3c_hsotg_req *hs_req = our_req(req); struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct s3c_hsotg *hs = hs_ep->parent; - unsigned long irqflags; bool first; dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", ep->name, req, req->length, req->buf, req->no_interrupt, req->zero, req->short_not_ok); - spin_lock_irqsave(&hs->lock, irqflags); - /* initialise status of the request */ INIT_LIST_HEAD(&hs_req->queue); req->actual = 0; @@ -922,11 +919,24 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, if (first) s3c_hsotg_start_req(hs, hs_ep, hs_req, false); - spin_unlock_irqrestore(&hs->lock, irqflags); - return 0; } +static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req, + gfp_t gfp_flags) +{ + struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct s3c_hsotg *hs = hs_ep->parent; + unsigned long flags = 0; + int ret = 0; + + spin_lock_irqsave(&hs->lock, flags); + ret = s3c_hsotg_ep_queue(ep, req, gfp_flags); + spin_unlock_irqrestore(&hs->lock, flags); + + return ret; +} + static void s3c_hsotg_ep_free_request(struct usb_ep *ep, struct usb_request *req) { @@ -1402,28 +1412,6 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg, } } -/** - * s3c_hsotg_complete_request_lock - complete a request given to us (locked) - * @hsotg: The device state. - * @hs_ep: The endpoint the request was on. - * @hs_req: The request to complete. - * @result: The result code (0 => Ok, otherwise errno) - * - * See s3c_hsotg_complete_request(), but called with the endpoint's - * lock held. - */ -static void s3c_hsotg_complete_request_lock(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req, - int result) -{ - unsigned long flags; - - spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result); - spin_unlock_irqrestore(&hsotg->lock, flags); -} - /** * s3c_hsotg_rx_data - receive data from the FIFO for an endpoint * @hsotg: The device state. @@ -1443,7 +1431,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) int max_req; int read_ptr; - spin_lock(&hsotg->lock); if (!hs_req) { u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx)); @@ -1457,7 +1444,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) for (ptr = 0; ptr < size; ptr += 4) (void)readl(fifo); - spin_unlock(&hsotg->lock); return; } @@ -1487,8 +1473,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) * alignment of the data. */ readsl(fifo, hs_req->req.buf + read_ptr, to_read); - - spin_unlock(&hsotg->lock); } /** @@ -1609,7 +1593,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, s3c_hsotg_send_zlp(hsotg, hs_req); } - s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, result); + s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result); } /** @@ -1864,7 +1848,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg, /* Finish ZLP handling for IN EP0 transactions */ if (hsotg->eps[0].sent_zlp) { dev_dbg(hsotg->dev, "zlp packet received\n"); - s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0); + s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); return; } @@ -1915,7 +1899,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg, dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__); s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); } else - s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0); + s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); } /** @@ -2123,9 +2107,6 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, int result, bool force) { struct s3c_hsotg_req *req, *treq; - unsigned long flags; - - spin_lock_irqsave(&hsotg->lock, flags); list_for_each_entry_safe(req, treq, &ep->queue, queue) { /* @@ -2139,14 +2120,15 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, s3c_hsotg_complete_request(hsotg, ep, req, result); } - - spin_unlock_irqrestore(&hsotg->lock, flags); } #define call_gadget(_hs, _entry) \ if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \ - (_hs)->driver && (_hs)->driver->_entry) \ - (_hs)->driver->_entry(&(_hs)->gadget); + (_hs)->driver && (_hs)->driver->_entry) { \ + spin_unlock(&_hs->lock); \ + (_hs)->driver->_entry(&(_hs)->gadget); \ + spin_lock(&_hs->lock); \ + } /** * s3c_hsotg_disconnect - disconnect service @@ -2388,6 +2370,7 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw) u32 gintsts; u32 gintmsk; + spin_lock(&hsotg->lock); irq_retry: gintsts = readl(hsotg->regs + GINTSTS); gintmsk = readl(hsotg->regs + GINTMSK); @@ -2557,6 +2540,8 @@ irq_retry: if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) goto irq_retry; + spin_unlock(&hsotg->lock); + return IRQ_HANDLED; } @@ -2710,10 +2695,10 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + spin_lock_irqsave(&hsotg->lock, flags); /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); - spin_lock_irqsave(&hsotg->lock, flags); ctrl = readl(hsotg->regs + epctrl_reg); ctrl &= ~DxEPCTL_EPEna; @@ -2784,15 +2769,12 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct s3c_hsotg *hs = hs_ep->parent; int index = hs_ep->index; - unsigned long irqflags; u32 epreg; u32 epctl; u32 xfertype; dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); - spin_lock_irqsave(&hs->lock, irqflags); - /* write both IN and OUT control registers */ epreg = DIEPCTL(index); @@ -2827,19 +2809,36 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) writel(epctl, hs->regs + epreg); - spin_unlock_irqrestore(&hs->lock, irqflags); - return 0; } +/** + * s3c_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held + * @ep: The endpoint to set halt. + * @value: Set or unset the halt. + */ +static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) +{ + struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct s3c_hsotg *hs = hs_ep->parent; + unsigned long flags = 0; + int ret = 0; + + spin_lock_irqsave(&hs->lock, flags); + ret = s3c_hsotg_ep_sethalt(ep, value); + spin_unlock_irqrestore(&hs->lock, flags); + + return ret; +} + static struct usb_ep_ops s3c_hsotg_ep_ops = { .enable = s3c_hsotg_ep_enable, .disable = s3c_hsotg_ep_disable, .alloc_request = s3c_hsotg_ep_alloc_request, .free_request = s3c_hsotg_ep_free_request, - .queue = s3c_hsotg_ep_queue, + .queue = s3c_hsotg_ep_queue_lock, .dequeue = s3c_hsotg_ep_dequeue, - .set_halt = s3c_hsotg_ep_sethalt, + .set_halt = s3c_hsotg_ep_sethalt_lock, /* note, don't believe we have any call for the fifo routines */ }; -- cgit v1.2.3-70-g09d2 From 1591633ed6c4c3994944f31ddb59bc072a2ed0ca Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Fri, 15 Jun 2012 11:54:36 +0530 Subject: usb: dwc3: giveback all queued request when ep disabled or reset received In case of ep_disable and reset interrupt is received and, still there was at least one request queued for dma transfer, then endpoint is stopped first. Once endpoint is stopped, callback for all queued request must be called. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 38cd13ffae9..f6fb42da2e6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -579,8 +579,11 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) */ udelay(100); - req = next_request(&dep->req_queued); - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + while (!list_empty(&dep->req_queued)) { + req = next_request(&dep->req_queued); + + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } } while (!list_empty(&dep->request_list)) { -- cgit v1.2.3-70-g09d2 From 71c421dbd9d87ffe6229816a04b759ec5613014d Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 15 Jun 2012 12:01:33 +0200 Subject: usb: correct trivial typo in drivers/usb/host/Kconfig Correct "Enbale" -> "Enable", in the desc for USB_HCD_BCMA and USB_HCD_SSB. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 83e58df29fe..18ba33da34e 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -652,7 +652,7 @@ config USB_HCD_BCMA select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD help - Enbale support for the EHCI and OCHI host controller on an bcma bus. + Enable support for the EHCI and OCHI host controller on an bcma bus. It converts the bcma driver into two platform device drivers for ehci and ohci. @@ -664,7 +664,7 @@ config USB_HCD_SSB select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD help - Enbale support for the EHCI and OCHI host controller on an bcma bus. + Enable support for the EHCI and OCHI host controller on an bcma bus. It converts the bcma driver into two platform device drivers for ehci and ohci. -- cgit v1.2.3-70-g09d2 From 996a776544cc8a71653ffa95a89c317f33d2cb4a Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Tue, 19 Jun 2012 10:34:06 -0400 Subject: USB: serial: Remove unused s_priv variable s_priv was set but never used in keyspan_open() and keyspan_close(), remove it. This also makes the serial variable in keyspan_open() unused since it's only use was to set s_priv, so it is also removed. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index a1b99243dac..9a0ca835590 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1036,15 +1036,12 @@ static int keyspan_write_room(struct tty_struct *tty) static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port) { struct keyspan_port_private *p_priv; - struct keyspan_serial_private *s_priv; - struct usb_serial *serial = port->serial; const struct keyspan_device_details *d_details; int i, err; int baud_rate, device_port; struct urb *urb; unsigned int cflag = 0; - s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); d_details = p_priv->device_details; @@ -1130,10 +1127,8 @@ static void keyspan_close(struct usb_serial_port *port) { int i; struct usb_serial *serial = port->serial; - struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; - s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); p_priv->rts_state = 0; -- cgit v1.2.3-70-g09d2 From c50f2af8e1a33e95e4d3b7af9db05ed9096c6072 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Tue, 19 Jun 2012 10:34:07 -0400 Subject: USB: serial: Remove unused serial_priv variable qt2_open() and qt2_close() both set a serial_priv variable but never used it. Remove the variable from the functions. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 8dd88ebe986..151670b6b72 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -345,7 +345,6 @@ static void qt2_set_termios(struct tty_struct *tty, static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial; - struct qt2_serial_private *serial_priv; struct qt2_port_private *port_priv; u8 *data; u16 device_port; @@ -357,7 +356,6 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) serial = port->serial; port_priv = usb_get_serial_port_data(port); - serial_priv = usb_get_serial_data(serial); /* set the port to RS232 mode */ status = qt2_control_msg(serial->dev, QT2_GET_SET_QMCR, @@ -417,13 +415,11 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) static void qt2_close(struct usb_serial_port *port) { struct usb_serial *serial; - struct qt2_serial_private *serial_priv; struct qt2_port_private *port_priv; unsigned long flags; int i; serial = port->serial; - serial_priv = usb_get_serial_data(serial); port_priv = usb_get_serial_port_data(port); port_priv->is_open = false; -- cgit v1.2.3-70-g09d2 From 1a86e156e249e5d5c7b8dd4bf93701bb6ccb3cd9 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Wed, 20 Jun 2012 11:53:23 +0200 Subject: USB: cdc-wdm: QMI devices are now handled by qmi_wwan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qmi_wwan has been changed to drive both the control and data interface for all QMI/wwan devices, using cdc-wdm as a subdriver. Remove the stale device ID entries from cdc-wdm. >From now on new QMI/wwan devices will only need to be added to the qmi_wwan driver, regardless of the USB descriptor layout Note that this is not appropriate for stable/longterm kernels despite being a device ID patch. Cc: Oliver Neukum Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 8fd398dffce..25e7d72f339 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -32,8 +32,6 @@ #define DRIVER_AUTHOR "Oliver Neukum" #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" -#define HUAWEI_VENDOR_ID 0x12D1 - static const struct usb_device_id wdm_ids[] = { { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | @@ -41,29 +39,6 @@ static const struct usb_device_id wdm_ids[] = { .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM }, - { - /* - * Huawei E392, E398 and possibly other Qualcomm based modems - * embed the Qualcomm QMI protocol inside CDC on CDC ECM like - * control interfaces. Userspace access to this is required - * to configure the accompanying data interface - */ - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = HUAWEI_VENDOR_ID, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */ - }, - { - /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */ - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = HUAWEI_VENDOR_ID, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 57, /* NOTE: CDC ECM control interface! */ - }, { } }; -- cgit v1.2.3-70-g09d2 From 78091dc2f6f04b03131218df590c877cadcd9379 Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Wed, 20 Jun 2012 09:02:23 -0300 Subject: USB: ohci-nxp: add usbd and otg clock initialization The ohci-nxp was assuming the clock was enabled by the board init or bootloader and just enabling the pll. This enables the usbd and otg clocks this periferal also needs. Signed-off-by: Alexandre Pereira da Silva Acked-by: Roland Stigge Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-nxp.c | 88 ++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 1e364ec962f..a446386bf77 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -43,16 +43,6 @@ #define USB_HOST_NEED_CLK_EN (1 << 21) #define PAD_CONTROL_LAST_DRIVEN (1 << 19) -#define USB_OTG_CLK_CTRL IO_ADDRESS(USB_CONFIG_BASE + 0xFF4) -#define USB_OTG_CLK_STAT IO_ADDRESS(USB_CONFIG_BASE + 0xFF8) - -/* USB_OTG_CLK_CTRL bit defines */ -#define AHB_M_CLOCK_ON (1 << 4) -#define OTG_CLOCK_ON (1 << 3) -#define I2C_CLOCK_ON (1 << 2) -#define DEV_CLOCK_ON (1 << 1) -#define HOST_CLOCK_ON (1 << 0) - #define USB_OTG_STAT_CONTROL IO_ADDRESS(USB_CONFIG_BASE + 0x110) /* USB_OTG_STAT_CONTROL bit defines */ @@ -72,7 +62,9 @@ static struct i2c_client *isp1301_i2c_client; extern int usb_disabled(void); -static struct clk *usb_clk; +static struct clk *usb_pll_clk; +static struct clk *usb_dev_clk; +static struct clk *usb_otg_clk; static void isp1301_configure_pnx4008(void) { @@ -249,8 +241,6 @@ static const struct hc_driver ohci_nxp_hc_driver = { .start_port_reset = ohci_start_port_reset, }; -#define USB_CLOCK_MASK (AHB_M_CLOCK_ON| OTG_CLOCK_ON | HOST_CLOCK_ON | I2C_CLOCK_ON) - static void nxp_set_usb_bits(void) { if (machine_is_pnx4008()) { @@ -327,41 +317,63 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) /* Enable AHB slave USB clock, needed for further USB clock control */ __raw_writel(USB_SLAVE_HCLK_EN | PAD_CONTROL_LAST_DRIVEN, USB_CTRL); - isp1301_configure(); - /* Enable USB PLL */ - usb_clk = clk_get(&pdev->dev, "ck_pll5"); - if (IS_ERR(usb_clk)) { + usb_pll_clk = clk_get(&pdev->dev, "ck_pll5"); + if (IS_ERR(usb_pll_clk)) { dev_err(&pdev->dev, "failed to acquire USB PLL\n"); - ret = PTR_ERR(usb_clk); + ret = PTR_ERR(usb_pll_clk); goto out1; } - ret = clk_enable(usb_clk); + ret = clk_enable(usb_pll_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB PLL\n"); goto out2; } - ret = clk_set_rate(usb_clk, 48000); + ret = clk_set_rate(usb_pll_clk, 48000); if (ret < 0) { dev_err(&pdev->dev, "failed to set USB clock rate\n"); goto out3; } + /* Enable USB device clock */ + usb_dev_clk = clk_get(&pdev->dev, "ck_usbd"); + if (IS_ERR(usb_dev_clk)) { + dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); + ret = PTR_ERR(usb_dev_clk); + goto out4; + } + + ret = clk_enable(usb_dev_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); + goto out5; + } + + /* Enable USB otg clocks */ + usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg"); + if (IS_ERR(usb_otg_clk)) { + dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); + ret = PTR_ERR(usb_dev_clk); + goto out6; + } + __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL); - /* Set to enable all needed USB clocks */ - __raw_writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL); + ret = clk_enable(usb_otg_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); + goto out7; + } - while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) != - USB_CLOCK_MASK) ; + isp1301_configure(); hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Failed to allocate HC buffer\n"); ret = -ENOMEM; - goto out3; + goto out8; } /* Set all USB bits in the Start Enable register */ @@ -371,14 +383,14 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "Failed to get MEM resource\n"); ret = -ENOMEM; - goto out4; + goto out8; } hcd->regs = devm_request_and_ioremap(&pdev->dev, res); if (!hcd->regs) { dev_err(&pdev->dev, "Failed to devm_request_and_ioremap\n"); ret = -ENOMEM; - goto out4; + goto out8; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); @@ -386,7 +398,7 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -ENXIO; - goto out4; + goto out8; } nxp_start_hc(); @@ -400,13 +412,21 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev) return ret; nxp_stop_hc(); -out4: +out8: nxp_unset_usb_bits(); usb_put_hcd(hcd); +out7: + clk_disable(usb_otg_clk); +out6: + clk_put(usb_otg_clk); +out5: + clk_disable(usb_dev_clk); +out4: + clk_put(usb_dev_clk); out3: - clk_disable(usb_clk); + clk_disable(usb_pll_clk); out2: - clk_put(usb_clk); + clk_put(usb_pll_clk); out1: isp1301_i2c_client = NULL; out: @@ -422,8 +442,10 @@ static int usb_hcd_nxp_remove(struct platform_device *pdev) release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); nxp_unset_usb_bits(); - clk_disable(usb_clk); - clk_put(usb_clk); + clk_disable(usb_pll_clk); + clk_put(usb_pll_clk); + clk_disable(usb_dev_clk); + clk_put(usb_dev_clk); i2c_unregister_device(isp1301_i2c_client); isp1301_i2c_client = NULL; -- cgit v1.2.3-70-g09d2 From 3d4eb9dfa3e89a09cdaaf3d0465479475e4afb0c Mon Sep 17 00:00:00 2001 From: Yu Xu Date: Fri, 15 Jun 2012 21:45:08 +0800 Subject: usb: gadget: mv: Add USB 3.0 device driver for Marvell PXA2128 chip. It supports Marvell USB 3.0 device controller for PXA2128 chip. Signed-off-by: Yu Xu Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 9 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/mv_u3d.h | 320 ++++++ drivers/usb/gadget/mv_u3d_core.c | 2098 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 2428 insertions(+) create mode 100644 drivers/usb/gadget/mv_u3d.h create mode 100644 drivers/usb/gadget/mv_u3d_core.c (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bddc8fd9a7b..5e242c34088 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -321,6 +321,15 @@ config USB_MV_UDC USB2.0 OTG controller, which can be configured as high speed or full speed USB peripheral. +config USB_MV_U3D + tristate "MARVELL PXA2128 USB 3.0 controller" + depends on CPU_MMP3 + select USB_GADGET_DUALSPEED + select USB_GADGET_SUPERSPEED + help + MARVELL PXA2128 Processor series include a super speed USB3.0 device + controller, which support super speed USB peripheral. + # # Controllers available in both integrated and discrete versions # diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1811513f1c2..3fd8cd09d2c 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_MV_UDC) += mv_udc.o mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o # # USB gadget drivers diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/mv_u3d.h new file mode 100644 index 00000000000..e32a787ac37 --- /dev/null +++ b/drivers/usb/gadget/mv_u3d.h @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef __MV_U3D_H +#define __MV_U3D_H + +#define MV_U3D_EP_CONTEXT_ALIGNMENT 32 +#define MV_U3D_TRB_ALIGNMENT 16 +#define MV_U3D_DMA_BOUNDARY 4096 +#define MV_U3D_EP0_MAX_PKT_SIZE 512 + +/* ep0 transfer state */ +#define MV_U3D_WAIT_FOR_SETUP 0 +#define MV_U3D_DATA_STATE_XMIT 1 +#define MV_U3D_DATA_STATE_NEED_ZLP 2 +#define MV_U3D_WAIT_FOR_OUT_STATUS 3 +#define MV_U3D_DATA_STATE_RECV 4 +#define MV_U3D_STATUS_STAGE 5 + +#define MV_U3D_EP_MAX_LENGTH_TRANSFER 0x10000 + +/* USB3 Interrupt Status */ +#define MV_U3D_USBINT_SETUP 0x00000001 +#define MV_U3D_USBINT_RX_COMPLETE 0x00000002 +#define MV_U3D_USBINT_TX_COMPLETE 0x00000004 +#define MV_U3D_USBINT_UNDER_RUN 0x00000008 +#define MV_U3D_USBINT_RXDESC_ERR 0x00000010 +#define MV_U3D_USBINT_TXDESC_ERR 0x00000020 +#define MV_U3D_USBINT_RX_TRB_COMPLETE 0x00000040 +#define MV_U3D_USBINT_TX_TRB_COMPLETE 0x00000080 +#define MV_U3D_USBINT_VBUS_VALID 0x00010000 +#define MV_U3D_USBINT_STORAGE_CMD_FULL 0x00020000 +#define MV_U3D_USBINT_LINK_CHG 0x01000000 + +/* USB3 Interrupt Enable */ +#define MV_U3D_INTR_ENABLE_SETUP 0x00000001 +#define MV_U3D_INTR_ENABLE_RX_COMPLETE 0x00000002 +#define MV_U3D_INTR_ENABLE_TX_COMPLETE 0x00000004 +#define MV_U3D_INTR_ENABLE_UNDER_RUN 0x00000008 +#define MV_U3D_INTR_ENABLE_RXDESC_ERR 0x00000010 +#define MV_U3D_INTR_ENABLE_TXDESC_ERR 0x00000020 +#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE 0x00000040 +#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE 0x00000080 +#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR 0x00000100 +#define MV_U3D_INTR_ENABLE_VBUS_VALID 0x00010000 +#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL 0x00020000 +#define MV_U3D_INTR_ENABLE_LINK_CHG 0x01000000 +#define MV_U3D_INTR_ENABLE_PRIME_STATUS 0x02000000 + +/* USB3 Link Change */ +#define MV_U3D_LINK_CHANGE_LINK_UP 0x00000001 +#define MV_U3D_LINK_CHANGE_SUSPEND 0x00000002 +#define MV_U3D_LINK_CHANGE_RESUME 0x00000004 +#define MV_U3D_LINK_CHANGE_WRESET 0x00000008 +#define MV_U3D_LINK_CHANGE_HRESET 0x00000010 +#define MV_U3D_LINK_CHANGE_VBUS_INVALID 0x00000020 +#define MV_U3D_LINK_CHANGE_INACT 0x00000040 +#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0 0x00000080 +#define MV_U3D_LINK_CHANGE_U1 0x00000100 +#define MV_U3D_LINK_CHANGE_U2 0x00000200 +#define MV_U3D_LINK_CHANGE_U3 0x00000400 + +/* bridge setting */ +#define MV_U3D_BRIDGE_SETTING_VBUS_VALID (1 << 16) + +/* Command Register Bit Masks */ +#define MV_U3D_CMD_RUN_STOP 0x00000001 +#define MV_U3D_CMD_CTRL_RESET 0x00000002 + +/* ep control register */ +#define MV_U3D_EPXCR_EP_TYPE_CONTROL 0 +#define MV_U3D_EPXCR_EP_TYPE_ISOC 1 +#define MV_U3D_EPXCR_EP_TYPE_BULK 2 +#define MV_U3D_EPXCR_EP_TYPE_INT 3 +#define MV_U3D_EPXCR_EP_ENABLE_SHIFT 4 +#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT 12 +#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT 16 +#define MV_U3D_USB_BULK_BURST_OUT 6 +#define MV_U3D_USB_BULK_BURST_IN 14 + +#define MV_U3D_EPXCR_EP_FLUSH (1 << 7) +#define MV_U3D_EPXCR_EP_HALT (1 << 1) +#define MV_U3D_EPXCR_EP_INIT (1) + +/* TX/RX Status Register */ +#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT 24 +#define MV_U3D_COMPLETE_INVALID 0 +#define MV_U3D_COMPLETE_SUCCESS 1 +#define MV_U3D_COMPLETE_BUFF_ERR 2 +#define MV_U3D_COMPLETE_SHORT_PACKET 3 +#define MV_U3D_COMPLETE_TRB_ERR 5 +#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK (0xFFFFFF) + +#define MV_U3D_USB_LINK_BYPASS_VBUS 0x8 + +#define MV_U3D_LTSSM_PHY_INIT_DONE 0x80000000 +#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE 0x40000000 + +#define MV_U3D_USB3_OP_REGS_OFFSET 0x100 +#define MV_U3D_USB3_PHY_OFFSET 0xB800 + +#define DCS_ENABLE 0x1 + +/* timeout */ +#define MV_U3D_RESET_TIMEOUT 10000 +#define MV_U3D_FLUSH_TIMEOUT 100000 +#define MV_U3D_OWN_TIMEOUT 10000 +#define LOOPS_USEC_SHIFT 4 +#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) +#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) + +/* ep direction */ +#define MV_U3D_EP_DIR_IN 1 +#define MV_U3D_EP_DIR_OUT 0 +#define mv_u3d_ep_dir(ep) (((ep)->ep_num == 0) ? \ + ((ep)->u3d->ep0_dir) : ((ep)->direction)) + +/* usb capability registers */ +struct mv_u3d_cap_regs { + u32 rsvd[5]; + u32 dboff; /* doorbell register offset */ + u32 rtsoff; /* runtime register offset */ + u32 vuoff; /* vendor unique register offset */ +}; + +/* operation registers */ +struct mv_u3d_op_regs { + u32 usbcmd; /* Command register */ + u32 rsvd1[11]; + u32 dcbaapl; /* Device Context Base Address low register */ + u32 dcbaaph; /* Device Context Base Address high register */ + u32 rsvd2[243]; + u32 portsc; /* port status and control register*/ + u32 portlinkinfo; /* port link info register*/ + u32 rsvd3[9917]; + u32 doorbell; /* doorbell register */ +}; + +/* control enpoint enable registers */ +struct epxcr { + u32 epxoutcr0; /* ep out control 0 register */ + u32 epxoutcr1; /* ep out control 1 register */ + u32 epxincr0; /* ep in control 0 register */ + u32 epxincr1; /* ep in control 1 register */ +}; + +/* transfer status registers */ +struct xferstatus { + u32 curdeqlo; /* current TRB pointer low */ + u32 curdeqhi; /* current TRB pointer high */ + u32 statuslo; /* transfer status low */ + u32 statushi; /* transfer status high */ +}; + +/* vendor unique control registers */ +struct mv_u3d_vuc_regs { + u32 ctrlepenable; /* control endpoint enable register */ + u32 setuplock; /* setup lock register */ + u32 endcomplete; /* endpoint transfer complete register */ + u32 intrcause; /* interrupt cause register */ + u32 intrenable; /* interrupt enable register */ + u32 trbcomplete; /* TRB complete register */ + u32 linkchange; /* link change register */ + u32 rsvd1[5]; + u32 trbunderrun; /* TRB underrun register */ + u32 rsvd2[43]; + u32 bridgesetting; /* bridge setting register */ + u32 rsvd3[7]; + struct xferstatus txst[16]; /* TX status register */ + struct xferstatus rxst[16]; /* RX status register */ + u32 ltssm; /* LTSSM control register */ + u32 pipe; /* PIPE control register */ + u32 linkcr0; /* link control 0 register */ + u32 linkcr1; /* link control 1 register */ + u32 rsvd6[60]; + u32 mib0; /* MIB0 counter register */ + u32 usblink; /* usb link control register */ + u32 ltssmstate; /* LTSSM state register */ + u32 linkerrorcause; /* link error cause register */ + u32 rsvd7[60]; + u32 devaddrtiebrkr; /* device address and tie breaker */ + u32 itpinfo0; /* ITP info 0 register */ + u32 itpinfo1; /* ITP info 1 register */ + u32 rsvd8[61]; + struct epxcr epcr[16]; /* ep control register */ + u32 rsvd9[64]; + u32 phyaddr; /* PHY address register */ + u32 phydata; /* PHY data register */ +}; + +/* Endpoint context structure */ +struct mv_u3d_ep_context { + u32 rsvd0; + u32 rsvd1; + u32 trb_addr_lo; /* TRB address low 32 bit */ + u32 trb_addr_hi; /* TRB address high 32 bit */ + u32 rsvd2; + u32 rsvd3; + struct usb_ctrlrequest setup_buffer; /* setup data buffer */ +}; + +/* TRB control data structure */ +struct mv_u3d_trb_ctrl { + u32 own:1; /* owner of TRB */ + u32 rsvd1:3; + u32 chain:1; /* associate this TRB with the + next TRB on the Ring */ + u32 ioc:1; /* interrupt on complete */ + u32 rsvd2:4; + u32 type:6; /* TRB type */ +#define TYPE_NORMAL 1 +#define TYPE_DATA 3 +#define TYPE_LINK 6 + u32 dir:1; /* Working at data stage of control endpoint + operation. 0 is OUT and 1 is IN. */ + u32 rsvd3:15; +}; + +/* TRB data structure + * For multiple TRB, all the TRBs' physical address should be continuous. + */ +struct mv_u3d_trb_hw { + u32 buf_addr_lo; /* data buffer address low 32 bit */ + u32 buf_addr_hi; /* data buffer address high 32 bit */ + u32 trb_len; /* transfer length */ + struct mv_u3d_trb_ctrl ctrl; /* TRB control data */ +}; + +/* TRB structure */ +struct mv_u3d_trb { + struct mv_u3d_trb_hw *trb_hw; /* point to the trb_hw structure */ + dma_addr_t trb_dma; /* dma address for this trb_hw */ + struct list_head trb_list; /* trb list */ +}; + +/* device data structure */ +struct mv_u3d { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + spinlock_t lock; /* device lock */ + struct completion *done; + struct device *dev; + int irq; + + /* usb controller registers */ + struct mv_u3d_cap_regs __iomem *cap_regs; + struct mv_u3d_op_regs __iomem *op_regs; + struct mv_u3d_vuc_regs __iomem *vuc_regs; + void __iomem *phy_regs; + + unsigned int max_eps; + struct mv_u3d_ep_context *ep_context; + size_t ep_context_size; + dma_addr_t ep_context_dma; + + struct dma_pool *trb_pool; /* for TRB data structure */ + struct mv_u3d_ep *eps; + + struct mv_u3d_req *status_req; /* ep0 status request */ + struct usb_ctrlrequest local_setup_buff; /* store setup data*/ + + unsigned int resume_state; /* USB state to resume */ + unsigned int usb_state; /* USB current state */ + unsigned int ep0_state; /* Endpoint zero state */ + unsigned int ep0_dir; + + unsigned int dev_addr; /* device address */ + + unsigned int errors; + + unsigned softconnect:1; + unsigned vbus_active:1; /* vbus is active or not */ + unsigned remote_wakeup:1; /* support remote wakeup */ + unsigned clock_gating:1; /* clock gating or not */ + unsigned active:1; /* udc is active or not */ + unsigned vbus_valid_detect:1; /* udc vbus detection */ + + struct mv_usb_addon_irq *vbus; + unsigned int power; + + struct clk *clk; +}; + +/* endpoint data structure */ +struct mv_u3d_ep { + struct usb_ep ep; + struct mv_u3d *u3d; + struct list_head queue; /* ep request queued hardware */ + struct list_head req_list; /* list of ep request */ + struct mv_u3d_ep_context *ep_context; /* ep context */ + u32 direction; + char name[14]; + u32 processing; /* there is ep request + queued on haredware */ + spinlock_t req_lock; /* ep lock */ + unsigned wedge:1; + unsigned enabled:1; + unsigned ep_type:2; + unsigned ep_num:8; +}; + +/* request data structure */ +struct mv_u3d_req { + struct usb_request req; + struct mv_u3d_ep *ep; + struct list_head queue; /* ep requst queued on hardware */ + struct list_head list; /* ep request list */ + struct list_head trb_list; /* trb list of a request */ + + struct mv_u3d_trb *trb_head; /* point to first trb of a request */ + unsigned trb_count; /* TRB number in the chain */ + unsigned chain; /* TRB chain or not */ +}; + +#endif diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c new file mode 100644 index 00000000000..8cfd5b028db --- /dev/null +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -0,0 +1,2098 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mv_u3d.h" + +#define DRIVER_DESC "Marvell PXA USB3.0 Device Controller driver" + +static const char driver_name[] = "mv_u3d"; +static const char driver_desc[] = DRIVER_DESC; + +static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status); +static void mv_u3d_stop_activity(struct mv_u3d *u3d, + struct usb_gadget_driver *driver); + +/* for endpoint 0 operations */ +static const struct usb_endpoint_descriptor mv_u3d_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = MV_U3D_EP0_MAX_PKT_SIZE, +}; + +static void mv_u3d_ep0_reset(struct mv_u3d *u3d) +{ + struct mv_u3d_ep *ep; + u32 epxcr; + int i; + + for (i = 0; i < 2; i++) { + ep = &u3d->eps[i]; + ep->u3d = u3d; + + /* ep0 ep context, ep0 in and out share the same ep context */ + ep->ep_context = &u3d->ep_context[1]; + } + + /* reset ep state machine */ + /* reset ep0 out */ + epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); + + epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE + << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | MV_U3D_EPXCR_EP_TYPE_CONTROL); + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr1); + + /* reset ep0 in */ + epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); + + epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE + << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | MV_U3D_EPXCR_EP_TYPE_CONTROL); + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr1); +} + +static void mv_u3d_ep0_stall(struct mv_u3d *u3d) +{ + u32 tmp; + dev_dbg(u3d->dev, "%s\n", __func__); + + /* set TX and RX to stall */ + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); + tmp |= MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); + + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); + tmp |= MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); + + /* update ep0 state */ + u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; +} + +static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, + struct mv_u3d_req *curr_req) +{ + struct mv_u3d_trb *curr_trb; + dma_addr_t cur_deq_lo; + struct mv_u3d_ep_context *curr_ep_context; + int trb_complete, actual, remaining_length; + int direction, ep_num; + int retval = 0; + u32 tmp, status, length; + + curr_ep_context = &u3d->ep_context[index]; + direction = index % 2; + ep_num = index / 2; + + trb_complete = 0; + actual = curr_req->req.length; + + while (!list_empty(&curr_req->trb_list)) { + curr_trb = list_entry(curr_req->trb_list.next, + struct mv_u3d_trb, trb_list); + if (!curr_trb->trb_hw->ctrl.own) { + dev_err(u3d->dev, "%s, TRB own error!\n", + u3d->eps[index].name); + return 1; + } + + curr_trb->trb_hw->ctrl.own = 0; + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo); + cur_deq_lo = + ioread32(&u3d->vuc_regs->rxst[ep_num].curdeqlo); + } else { + tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo); + cur_deq_lo = + ioread32(&u3d->vuc_regs->txst[ep_num].curdeqlo); + } + + status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT; + length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK; + + if (status == MV_U3D_COMPLETE_SUCCESS || + (status == MV_U3D_COMPLETE_SHORT_PACKET && + direction == MV_U3D_EP_DIR_OUT)) { + remaining_length += length; + actual -= remaining_length; + } else { + dev_err(u3d->dev, + "complete_tr error: ep=%d %s: error = 0x%x\n", + index >> 1, direction ? "SEND" : "RECV", + status); + retval = -EPROTO; + } + + list_del_init(&curr_trb->trb_list); + } + if (retval) + return retval; + + curr_req->req.actual = actual; + return 0; +} + +/* + * mv_u3d_done() - retire a request; caller blocked irqs + * @status : request status to be set, only works when + * request is still in progress. + */ +static +void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status) +{ + struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d; + + dev_dbg(u3d->dev, "mv_u3d_done: remove req->queue\n"); + /* Removed the req from ep queue */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* Free trb for the request */ + if (!req->chain) + dma_pool_free(u3d->trb_pool, + req->trb_head->trb_hw, req->trb_head->trb_dma); + else { + dma_unmap_single(ep->u3d->gadget.dev.parent, + (dma_addr_t)req->trb_head->trb_dma, + req->trb_count * sizeof(struct mv_u3d_trb_hw), + DMA_BIDIRECTIONAL); + kfree(req->trb_head->trb_hw); + } + kfree(req->trb_head); + + usb_gadget_unmap_request(&u3d->gadget, &req->req, mv_u3d_ep_dir(ep)); + + if (status && (status != -ESHUTDOWN)) { + dev_dbg(u3d->dev, "complete %s req %p stat %d len %u/%u", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + } + + spin_unlock(&ep->u3d->lock); + /* + * complete() is from gadget layer, + * eg fsg->bulk_in_complete() + */ + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + + spin_lock(&ep->u3d->lock); +} + +static int mv_u3d_queue_trb(struct mv_u3d_ep *ep, struct mv_u3d_req *req) +{ + u32 tmp, direction; + struct mv_u3d *u3d; + struct mv_u3d_ep_context *ep_context; + int retval = 0; + + u3d = ep->u3d; + direction = mv_u3d_ep_dir(ep); + + /* ep0 in and out share the same ep context slot 1*/ + if (ep->ep_num == 0) + ep_context = &(u3d->ep_context[1]); + else + ep_context = &(u3d->ep_context[ep->ep_num * 2 + direction]); + + /* check if the pipe is empty or not */ + if (!list_empty(&ep->queue)) { + dev_err(u3d->dev, "add trb to non-empty queue!\n"); + retval = -ENOMEM; + WARN_ON(1); + } else { + ep_context->rsvd0 = cpu_to_le32(1); + ep_context->rsvd1 = 0; + + /* Configure the trb address and set the DCS bit. + * Both DCS bit and own bit in trb should be set. + */ + ep_context->trb_addr_lo = + cpu_to_le32(req->trb_head->trb_dma | DCS_ENABLE); + ep_context->trb_addr_hi = 0; + + /* Ensure that updates to the EP Context will + * occure before Ring Bell. + */ + wmb(); + + /* ring bell the ep */ + if (ep->ep_num == 0) + tmp = 0x1; + else + tmp = ep->ep_num * 2 + + ((direction == MV_U3D_EP_DIR_OUT) ? 0 : 1); + + iowrite32(tmp, &u3d->op_regs->doorbell); + } + return retval; +} + +static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, + unsigned *length, dma_addr_t *dma) +{ + u32 temp; + unsigned int direction; + struct mv_u3d_trb *trb; + struct mv_u3d_trb_hw *trb_hw; + struct mv_u3d *u3d; + + /* how big will this transfer be? */ + *length = req->req.length - req->req.actual; + BUG_ON(*length > (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); + + u3d = req->ep->u3d; + + trb = kzalloc(sizeof(*trb), GFP_ATOMIC); + if (!trb) { + dev_err(u3d->dev, "%s, trb alloc fail\n", __func__); + return NULL; + } + + /* + * Be careful that no _GFP_HIGHMEM is set, + * or we can not use dma_to_virt + * cannot use GFP_KERNEL in spin lock + */ + trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma); + if (!trb_hw) { + dev_err(u3d->dev, + "%s, dma_pool_alloc fail\n", __func__); + return NULL; + } + trb->trb_dma = *dma; + trb->trb_hw = trb_hw; + + /* initialize buffer page pointers */ + temp = (u32)(req->req.dma + req->req.actual); + + trb_hw->buf_addr_lo = cpu_to_le32(temp); + trb_hw->buf_addr_hi = 0; + trb_hw->trb_len = cpu_to_le32(*length); + trb_hw->ctrl.own = 1; + + if (req->ep->ep_num == 0) + trb_hw->ctrl.type = TYPE_DATA; + else + trb_hw->ctrl.type = TYPE_NORMAL; + + req->req.actual += *length; + + direction = mv_u3d_ep_dir(req->ep); + if (direction == MV_U3D_EP_DIR_IN) + trb_hw->ctrl.dir = 1; + else + trb_hw->ctrl.dir = 0; + + /* Enable interrupt for the last trb of a request */ + if (!req->req.no_interrupt) + trb_hw->ctrl.ioc = 1; + + trb_hw->ctrl.chain = 0; + + wmb(); + return trb; +} + +static int mv_u3d_build_trb_chain(struct mv_u3d_req *req, unsigned *length, + struct mv_u3d_trb *trb, int *is_last) +{ + u32 temp; + unsigned int direction; + struct mv_u3d *u3d; + + /* how big will this transfer be? */ + *length = min(req->req.length - req->req.actual, + (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); + + u3d = req->ep->u3d; + + trb->trb_dma = 0; + + /* initialize buffer page pointers */ + temp = (u32)(req->req.dma + req->req.actual); + + trb->trb_hw->buf_addr_lo = cpu_to_le32(temp); + trb->trb_hw->buf_addr_hi = 0; + trb->trb_hw->trb_len = cpu_to_le32(*length); + trb->trb_hw->ctrl.own = 1; + + if (req->ep->ep_num == 0) + trb->trb_hw->ctrl.type = TYPE_DATA; + else + trb->trb_hw->ctrl.type = TYPE_NORMAL; + + req->req.actual += *length; + + direction = mv_u3d_ep_dir(req->ep); + if (direction == MV_U3D_EP_DIR_IN) + trb->trb_hw->ctrl.dir = 1; + else + trb->trb_hw->ctrl.dir = 0; + + /* zlp is needed if req->req.zero is set */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) + *is_last = 1; + else + *is_last = 0; + + /* Enable interrupt for the last trb of a request */ + if (*is_last && !req->req.no_interrupt) + trb->trb_hw->ctrl.ioc = 1; + + if (*is_last) + trb->trb_hw->ctrl.chain = 0; + else { + trb->trb_hw->ctrl.chain = 1; + dev_dbg(u3d->dev, "chain trb\n"); + } + + wmb(); + + return 0; +} + +/* generate TRB linked list for a request + * usb controller only supports continous trb chain, + * that trb structure physical address should be continous. + */ +static int mv_u3d_req_to_trb(struct mv_u3d_req *req) +{ + unsigned count; + int is_last; + struct mv_u3d_trb *trb; + struct mv_u3d_trb_hw *trb_hw; + struct mv_u3d *u3d; + dma_addr_t dma; + unsigned length; + unsigned trb_num; + + u3d = req->ep->u3d; + + INIT_LIST_HEAD(&req->trb_list); + + length = req->req.length - req->req.actual; + /* normally the request transfer length is less than 16KB. + * we use buil_trb_one() to optimize it. + */ + if (length <= (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER) { + trb = mv_u3d_build_trb_one(req, &count, &dma); + list_add_tail(&trb->trb_list, &req->trb_list); + req->trb_head = trb; + req->trb_count = 1; + req->chain = 0; + } else { + trb_num = length / MV_U3D_EP_MAX_LENGTH_TRANSFER; + if (length % MV_U3D_EP_MAX_LENGTH_TRANSFER) + trb_num++; + + trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC); + if (!trb) { + dev_err(u3d->dev, + "%s, trb alloc fail\n", __func__); + return -ENOMEM; + } + + trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); + if (!trb_hw) { + dev_err(u3d->dev, + "%s, trb_hw alloc fail\n", __func__); + return -ENOMEM; + } + + do { + trb->trb_hw = trb_hw; + if (mv_u3d_build_trb_chain(req, &count, + trb, &is_last)) { + dev_err(u3d->dev, + "%s, mv_u3d_build_trb_chain fail\n", + __func__); + return -EIO; + } + + list_add_tail(&trb->trb_list, &req->trb_list); + req->trb_count++; + trb++; + trb_hw++; + } while (!is_last); + + req->trb_head = list_entry(req->trb_list.next, + struct mv_u3d_trb, trb_list); + req->trb_head->trb_dma = dma_map_single(u3d->gadget.dev.parent, + req->trb_head->trb_hw, + trb_num * sizeof(*trb_hw), + DMA_BIDIRECTIONAL); + + req->chain = 1; + } + + return 0; +} + +static int +mv_u3d_start_queue(struct mv_u3d_ep *ep) +{ + struct mv_u3d *u3d = ep->u3d; + struct mv_u3d_req *req; + int ret; + + if (!list_empty(&ep->req_list) && !ep->processing) + req = list_entry(ep->req_list.next, struct mv_u3d_req, list); + else + return 0; + + ep->processing = 1; + + /* set up dma mapping */ + ret = usb_gadget_map_request(&u3d->gadget, &req->req, + mv_u3d_ep_dir(ep)); + if (ret) + return ret; + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->trb_count = 0; + + /* build trbs and push them to device queue */ + if (!mv_u3d_req_to_trb(req)) { + ret = mv_u3d_queue_trb(ep, req); + if (ret) { + ep->processing = 0; + return ret; + } + } else { + ep->processing = 0; + dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__); + return -ENOMEM; + } + + /* irq handler advances the queue */ + if (req) + list_add_tail(&req->queue, &ep->queue); + + return 0; +} + +static int mv_u3d_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct mv_u3d *u3d; + struct mv_u3d_ep *ep; + struct mv_u3d_ep_context *ep_context; + u16 max = 0; + unsigned maxburst = 0; + u32 epxcr, direction; + + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + + if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + direction = mv_u3d_ep_dir(ep); + max = le16_to_cpu(desc->wMaxPacketSize); + + if (!_ep->maxburst) + _ep->maxburst = 1; + maxburst = _ep->maxburst; + + /* Get the endpoint context address */ + ep_context = (struct mv_u3d_ep_context *)ep->ep_context; + + /* Set the max burst size */ + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + if (maxburst > 16) { + dev_dbg(u3d->dev, + "max burst should not be greater " + "than 16 on bulk ep\n"); + maxburst = 1; + _ep->maxburst = maxburst; + } + dev_dbg(u3d->dev, + "maxburst: %d on bulk %s\n", maxburst, ep->name); + break; + case USB_ENDPOINT_XFER_CONTROL: + /* control transfer only supports maxburst as one */ + maxburst = 1; + _ep->maxburst = maxburst; + break; + case USB_ENDPOINT_XFER_INT: + if (maxburst != 1) { + dev_dbg(u3d->dev, + "max burst should be 1 on int ep " + "if transfer size is not 1024\n"); + maxburst = 1; + _ep->maxburst = maxburst; + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (maxburst != 1) { + dev_dbg(u3d->dev, + "max burst should be 1 on isoc ep " + "if transfer size is not 1024\n"); + maxburst = 1; + _ep->maxburst = maxburst; + } + break; + default: + goto en_done; + } + + ep->ep.maxpacket = max; + ep->ep.desc = desc; + ep->enabled = 1; + + /* Enable the endpoint for Rx or Tx and set the endpoint type */ + if (direction == MV_U3D_EP_DIR_OUT) { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + + epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); + } else { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + + epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); + } + + return 0; +en_done: + return -EINVAL; +} + +static int mv_u3d_ep_disable(struct usb_ep *_ep) +{ + struct mv_u3d *u3d; + struct mv_u3d_ep *ep; + struct mv_u3d_ep_context *ep_context; + u32 epxcr, direction; + + if (!_ep) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + if (!ep->ep.desc) + return -EINVAL; + + u3d = ep->u3d; + + /* Get the endpoint context address */ + ep_context = ep->ep_context; + + direction = mv_u3d_ep_dir(ep); + + /* nuke all pending requests (does flush) */ + mv_u3d_nuke(ep, -ESHUTDOWN); + + /* Disable the endpoint for Rx or Tx and reset the endpoint type */ + if (direction == MV_U3D_EP_DIR_OUT) { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); + epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | USB_ENDPOINT_XFERTYPE_MASK); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); + } else { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr1); + epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | USB_ENDPOINT_XFERTYPE_MASK); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); + } + + ep->enabled = 0; + + ep->ep.desc = NULL; + return 0; +} + +static struct usb_request * +mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct mv_u3d_req *req = NULL; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void mv_u3d_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct mv_u3d_req *req = container_of(_req, struct mv_u3d_req, req); + + kfree(req); +} + +static void mv_u3d_ep_fifo_flush(struct usb_ep *_ep) +{ + struct mv_u3d *u3d; + u32 direction; + struct mv_u3d_ep *ep = container_of(_ep, struct mv_u3d_ep, ep); + unsigned int loops; + u32 tmp; + + /* if endpoint is not enabled, cannot flush endpoint */ + if (!ep->enabled) + return; + + u3d = ep->u3d; + direction = mv_u3d_ep_dir(ep); + + /* ep0 need clear bit after flushing fifo. */ + if (!ep->ep_num) { + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); + udelay(10); + tmp &= ~MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); + } else { + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); + udelay(10); + tmp &= ~MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); + } + return; + } + + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + + /* Wait until flushing completed */ + loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); + while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0) & + MV_U3D_EPXCR_EP_FLUSH) { + /* + * EP_FLUSH bit should be cleared to indicate this + * operation is complete + */ + if (loops == 0) { + dev_dbg(u3d->dev, + "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, + direction ? "in" : "out"); + return; + } + loops--; + udelay(LOOPS_USEC); + } + } else { /* EP_DIR_IN */ + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + + /* Wait until flushing completed */ + loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); + while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0) & + MV_U3D_EPXCR_EP_FLUSH) { + /* + * EP_FLUSH bit should be cleared to indicate this + * operation is complete + */ + if (loops == 0) { + dev_dbg(u3d->dev, + "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, + direction ? "in" : "out"); + return; + } + loops--; + udelay(LOOPS_USEC); + } + } +} + +/* queues (submits) an I/O request to an endpoint */ +static int +mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct mv_u3d_ep *ep; + struct mv_u3d_req *req; + struct mv_u3d *u3d; + unsigned long flags; + int is_first_req = 0; + + if (unlikely(!_ep || !_req)) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + + req = container_of(_req, struct mv_u3d_req, req); + + if (!ep->ep_num + && u3d->ep0_state == MV_U3D_STATUS_STAGE + && !_req->length) { + dev_dbg(u3d->dev, "ep0 status stage\n"); + u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; + return 0; + } + + dev_dbg(u3d->dev, "%s: %s, req: 0x%x\n", + __func__, _ep->name, (u32)req); + + /* catch various bogus parameters */ + if (!req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + dev_err(u3d->dev, + "%s, bad params, _req: 0x%x," + "req->req.complete: 0x%x, req->req.buf: 0x%x," + "list_empty: 0x%x\n", + __func__, (u32)_req, + (u32)req->req.complete, (u32)req->req.buf, + (u32)list_empty(&req->queue)); + return -EINVAL; + } + if (unlikely(!ep->ep.desc)) { + dev_err(u3d->dev, "%s, bad ep\n", __func__); + return -EINVAL; + } + if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + } + + if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) { + dev_err(u3d->dev, + "bad params of driver/speed\n"); + return -ESHUTDOWN; + } + + req->ep = ep; + + /* Software list handles usb request. */ + spin_lock_irqsave(&ep->req_lock, flags); + is_first_req = list_empty(&ep->req_list); + list_add_tail(&req->list, &ep->req_list); + spin_unlock_irqrestore(&ep->req_lock, flags); + if (!is_first_req) { + dev_dbg(u3d->dev, "list is not empty\n"); + return 0; + } + + dev_dbg(u3d->dev, "call mv_u3d_start_queue from usb_ep_queue\n"); + spin_lock_irqsave(&u3d->lock, flags); + mv_u3d_start_queue(ep); + spin_unlock_irqrestore(&u3d->lock, flags); + return 0; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct mv_u3d_ep *ep; + struct mv_u3d_req *req; + struct mv_u3d *u3d; + struct mv_u3d_ep_context *ep_context; + struct mv_u3d_req *next_req; + + unsigned long flags; + int ret = 0; + + if (!_ep || !_req) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + + spin_lock_irqsave(&ep->u3d->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + ret = -EINVAL; + goto out; + } + + /* The request is in progress, or completed but not dequeued */ + if (ep->queue.next == &req->queue) { + _req->status = -ECONNRESET; + mv_u3d_ep_fifo_flush(_ep); + + /* The request isn't the last request in this ep queue */ + if (req->queue.next != &ep->queue) { + dev_dbg(u3d->dev, + "it is the last request in this ep queue\n"); + ep_context = ep->ep_context; + next_req = list_entry(req->queue.next, + struct mv_u3d_req, queue); + + /* Point first TRB of next request to the EP context. */ + iowrite32((u32) next_req->trb_head, + &ep_context->trb_addr_lo); + } else { + struct mv_u3d_ep_context *ep_context; + ep_context = ep->ep_context; + ep_context->trb_addr_lo = 0; + ep_context->trb_addr_hi = 0; + } + + } else + WARN_ON(1); + + mv_u3d_done(ep, req, -ECONNRESET); + + /* remove the req from the ep req list */ + if (!list_empty(&ep->req_list)) { + struct mv_u3d_req *curr_req; + curr_req = list_entry(ep->req_list.next, + struct mv_u3d_req, list); + if (curr_req == req) { + list_del_init(&req->list); + ep->processing = 0; + } + } + +out: + spin_unlock_irqrestore(&ep->u3d->lock, flags); + return ret; +} + +static void +mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall) +{ + u32 tmp; + struct mv_u3d_ep *ep = u3d->eps; + + dev_dbg(u3d->dev, "%s\n", __func__); + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + if (stall) + tmp |= MV_U3D_EPXCR_EP_HALT; + else + tmp &= ~MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + } else { + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + if (stall) + tmp |= MV_U3D_EPXCR_EP_HALT; + else + tmp &= ~MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + } +} + +static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) +{ + struct mv_u3d_ep *ep; + unsigned long flags = 0; + int status = 0; + struct mv_u3d *u3d; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + if (!ep->ep.desc) { + status = -EINVAL; + goto out; + } + + if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* + * Attempt to halt IN ep will fail if any transfer requests + * are still queue + */ + if (halt && (mv_u3d_ep_dir(ep) == MV_U3D_EP_DIR_IN) + && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + spin_lock_irqsave(&ep->u3d->lock, flags); + mv_u3d_ep_set_stall(u3d, ep->ep_num, mv_u3d_ep_dir(ep), halt); + if (halt && wedge) + ep->wedge = 1; + else if (!halt) + ep->wedge = 0; + spin_unlock_irqrestore(&ep->u3d->lock, flags); + + if (ep->ep_num == 0) + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; +out: + return status; +} + +static int mv_u3d_ep_set_halt(struct usb_ep *_ep, int halt) +{ + return mv_u3d_ep_set_halt_wedge(_ep, halt, 0); +} + +static int mv_u3d_ep_set_wedge(struct usb_ep *_ep) +{ + return mv_u3d_ep_set_halt_wedge(_ep, 1, 1); +} + +static struct usb_ep_ops mv_u3d_ep_ops = { + .enable = mv_u3d_ep_enable, + .disable = mv_u3d_ep_disable, + + .alloc_request = mv_u3d_alloc_request, + .free_request = mv_u3d_free_request, + + .queue = mv_u3d_ep_queue, + .dequeue = mv_u3d_ep_dequeue, + + .set_wedge = mv_u3d_ep_set_wedge, + .set_halt = mv_u3d_ep_set_halt, + .fifo_flush = mv_u3d_ep_fifo_flush, +}; + +static void mv_u3d_controller_stop(struct mv_u3d *u3d) +{ + u32 tmp; + + if (!u3d->clock_gating && u3d->vbus_valid_detect) + iowrite32(MV_U3D_INTR_ENABLE_VBUS_VALID, + &u3d->vuc_regs->intrenable); + else + iowrite32(0, &u3d->vuc_regs->intrenable); + iowrite32(~0x0, &u3d->vuc_regs->endcomplete); + iowrite32(~0x0, &u3d->vuc_regs->trbunderrun); + iowrite32(~0x0, &u3d->vuc_regs->trbcomplete); + iowrite32(~0x0, &u3d->vuc_regs->linkchange); + iowrite32(0x1, &u3d->vuc_regs->setuplock); + + /* Reset the RUN bit in the command register to stop USB */ + tmp = ioread32(&u3d->op_regs->usbcmd); + tmp &= ~MV_U3D_CMD_RUN_STOP; + iowrite32(tmp, &u3d->op_regs->usbcmd); + dev_dbg(u3d->dev, "after u3d_stop, USBCMD 0x%x\n", + ioread32(&u3d->op_regs->usbcmd)); +} + +static void mv_u3d_controller_start(struct mv_u3d *u3d) +{ + u32 usbintr; + u32 temp; + + /* enable link LTSSM state machine */ + temp = ioread32(&u3d->vuc_regs->ltssm); + temp |= MV_U3D_LTSSM_PHY_INIT_DONE; + iowrite32(temp, &u3d->vuc_regs->ltssm); + + /* Enable interrupts */ + usbintr = MV_U3D_INTR_ENABLE_LINK_CHG | MV_U3D_INTR_ENABLE_TXDESC_ERR | + MV_U3D_INTR_ENABLE_RXDESC_ERR | MV_U3D_INTR_ENABLE_TX_COMPLETE | + MV_U3D_INTR_ENABLE_RX_COMPLETE | MV_U3D_INTR_ENABLE_SETUP | + (u3d->vbus_valid_detect ? MV_U3D_INTR_ENABLE_VBUS_VALID : 0); + iowrite32(usbintr, &u3d->vuc_regs->intrenable); + + /* Enable ctrl ep */ + iowrite32(0x1, &u3d->vuc_regs->ctrlepenable); + + /* Set the Run bit in the command register */ + iowrite32(MV_U3D_CMD_RUN_STOP, &u3d->op_regs->usbcmd); + dev_dbg(u3d->dev, "after u3d_start, USBCMD 0x%x\n", + ioread32(&u3d->op_regs->usbcmd)); +} + +static int mv_u3d_controller_reset(struct mv_u3d *u3d) +{ + unsigned int loops; + u32 tmp; + + /* Stop the controller */ + tmp = ioread32(&u3d->op_regs->usbcmd); + tmp &= ~MV_U3D_CMD_RUN_STOP; + iowrite32(tmp, &u3d->op_regs->usbcmd); + + /* Reset the controller to get default values */ + iowrite32(MV_U3D_CMD_CTRL_RESET, &u3d->op_regs->usbcmd); + + /* wait for reset to complete */ + loops = LOOPS(MV_U3D_RESET_TIMEOUT); + while (ioread32(&u3d->op_regs->usbcmd) & MV_U3D_CMD_CTRL_RESET) { + if (loops == 0) { + dev_err(u3d->dev, + "Wait for RESET completed TIMEOUT\n"); + return -ETIMEDOUT; + } + loops--; + udelay(LOOPS_USEC); + } + + /* Configure the Endpoint Context Address */ + iowrite32(u3d->ep_context_dma, &u3d->op_regs->dcbaapl); + iowrite32(0, &u3d->op_regs->dcbaaph); + + return 0; +} + +static int mv_u3d_enable(struct mv_u3d *u3d) +{ + struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + int retval; + + if (u3d->active) + return 0; + + if (!u3d->clock_gating) { + u3d->active = 1; + return 0; + } + + dev_dbg(u3d->dev, "enable u3d\n"); + clk_enable(u3d->clk); + if (pdata->phy_init) { + retval = pdata->phy_init(u3d->phy_regs); + if (retval) { + dev_err(u3d->dev, + "init phy error %d\n", retval); + clk_disable(u3d->clk); + return retval; + } + } + u3d->active = 1; + + return 0; +} + +static void mv_u3d_disable(struct mv_u3d *u3d) +{ + struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + if (u3d->clock_gating && u3d->active) { + dev_dbg(u3d->dev, "disable u3d\n"); + if (pdata->phy_deinit) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); + u3d->active = 0; + } +} + +static int mv_u3d_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct mv_u3d *u3d; + unsigned long flags; + int retval = 0; + + u3d = container_of(gadget, struct mv_u3d, gadget); + + spin_lock_irqsave(&u3d->lock, flags); + + u3d->vbus_active = (is_active != 0); + dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, u3d->softconnect, u3d->vbus_active); + /* + * 1. external VBUS detect: we can disable/enable clock on demand. + * 2. UDC VBUS detect: we have to enable clock all the time. + * 3. No VBUS detect: we have to enable clock all the time. + */ + if (u3d->driver && u3d->softconnect && u3d->vbus_active) { + retval = mv_u3d_enable(u3d); + if (retval == 0) { + /* + * after clock is disabled, we lost all the register + * context. We have to re-init registers + */ + mv_u3d_controller_reset(u3d); + mv_u3d_ep0_reset(u3d); + mv_u3d_controller_start(u3d); + } + } else if (u3d->driver && u3d->softconnect) { + if (!u3d->active) + goto out; + + /* stop all the transfer in queue*/ + mv_u3d_stop_activity(u3d, u3d->driver); + mv_u3d_controller_stop(u3d); + mv_u3d_disable(u3d); + } + +out: + spin_unlock_irqrestore(&u3d->lock, flags); + return retval; +} + +/* constrain controller's VBUS power usage + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int mv_u3d_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); + + u3d->power = mA; + + return 0; +} + +static int mv_u3d_pullup(struct usb_gadget *gadget, int is_on) +{ + struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&u3d->lock, flags); + + dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, u3d->softconnect, u3d->vbus_active); + u3d->softconnect = (is_on != 0); + if (u3d->driver && u3d->softconnect && u3d->vbus_active) { + retval = mv_u3d_enable(u3d); + if (retval == 0) { + /* + * after clock is disabled, we lost all the register + * context. We have to re-init registers + */ + mv_u3d_controller_reset(u3d); + mv_u3d_ep0_reset(u3d); + mv_u3d_controller_start(u3d); + } + } else if (u3d->driver && u3d->vbus_active) { + /* stop all the transfer in queue*/ + mv_u3d_stop_activity(u3d, u3d->driver); + mv_u3d_controller_stop(u3d); + mv_u3d_disable(u3d); + } + + spin_unlock_irqrestore(&u3d->lock, flags); + + return retval; +} + +static int mv_u3d_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); + struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + unsigned long flags; + + if (u3d->driver) + return -EBUSY; + + spin_lock_irqsave(&u3d->lock, flags); + + if (!u3d->clock_gating) { + clk_enable(u3d->clk); + if (pdata->phy_init) + pdata->phy_init(u3d->phy_regs); + } + + /* hook up the driver ... */ + driver->driver.bus = NULL; + u3d->driver = driver; + u3d->gadget.dev.driver = &driver->driver; + + u3d->ep0_dir = USB_DIR_OUT; + + spin_unlock_irqrestore(&u3d->lock, flags); + + u3d->vbus_valid_detect = 1; + + return 0; +} + +static int mv_u3d_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); + struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + unsigned long flags; + + u3d->vbus_valid_detect = 0; + spin_lock_irqsave(&u3d->lock, flags); + + /* enable clock to access controller register */ + clk_enable(u3d->clk); + if (pdata->phy_init) + pdata->phy_init(u3d->phy_regs); + + mv_u3d_controller_stop(u3d); + /* stop all usb activities */ + u3d->gadget.speed = USB_SPEED_UNKNOWN; + mv_u3d_stop_activity(u3d, driver); + mv_u3d_disable(u3d); + + if (pdata->phy_deinit) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); + + spin_unlock_irqrestore(&u3d->lock, flags); + + u3d->gadget.dev.driver = NULL; + u3d->driver = NULL; + + return 0; +} + +/* device controller usb_gadget_ops structure */ +static const struct usb_gadget_ops mv_u3d_ops = { + /* notify controller that VBUS is powered or not */ + .vbus_session = mv_u3d_vbus_session, + + /* constrain controller's VBUS power usage */ + .vbus_draw = mv_u3d_vbus_draw, + + .pullup = mv_u3d_pullup, + .udc_start = mv_u3d_start, + .udc_stop = mv_u3d_stop, +}; + +static int mv_u3d_eps_init(struct mv_u3d *u3d) +{ + struct mv_u3d_ep *ep; + char name[14]; + int i; + + /* initialize ep0, ep0 in/out use eps[1] */ + ep = &u3d->eps[1]; + ep->u3d = u3d; + strncpy(ep->name, "ep0", sizeof(ep->name)); + ep->ep.name = ep->name; + ep->ep.ops = &mv_u3d_ep_ops; + ep->wedge = 0; + ep->ep.maxpacket = MV_U3D_EP0_MAX_PKT_SIZE; + ep->ep_num = 0; + ep->ep.desc = &mv_u3d_ep0_desc; + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->req_list); + ep->ep_type = USB_ENDPOINT_XFER_CONTROL; + + /* add ep0 ep_context */ + ep->ep_context = &u3d->ep_context[1]; + + /* initialize other endpoints */ + for (i = 2; i < u3d->max_eps * 2; i++) { + ep = &u3d->eps[i]; + if (i & 1) { + snprintf(name, sizeof(name), "ep%din", i >> 1); + ep->direction = MV_U3D_EP_DIR_IN; + } else { + snprintf(name, sizeof(name), "ep%dout", i >> 1); + ep->direction = MV_U3D_EP_DIR_OUT; + } + ep->u3d = u3d; + strncpy(ep->name, name, sizeof(ep->name)); + ep->ep.name = ep->name; + + ep->ep.ops = &mv_u3d_ep_ops; + ep->ep.maxpacket = (unsigned short) ~0; + ep->ep_num = i / 2; + + INIT_LIST_HEAD(&ep->queue); + list_add_tail(&ep->ep.ep_list, &u3d->gadget.ep_list); + + INIT_LIST_HEAD(&ep->req_list); + spin_lock_init(&ep->req_lock); + ep->ep_context = &u3d->ep_context[i]; + } + + return 0; +} + +/* delete all endpoint requests, called with spinlock held */ +static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status) +{ + /* endpoint fifo flush */ + mv_u3d_ep_fifo_flush(&ep->ep); + + while (!list_empty(&ep->queue)) { + struct mv_u3d_req *req = NULL; + req = list_entry(ep->queue.next, struct mv_u3d_req, queue); + mv_u3d_done(ep, req, status); + } +} + +/* stop all USB activities */ +static +void mv_u3d_stop_activity(struct mv_u3d *u3d, struct usb_gadget_driver *driver) +{ + struct mv_u3d_ep *ep; + + mv_u3d_nuke(&u3d->eps[1], -ESHUTDOWN); + + list_for_each_entry(ep, &u3d->gadget.ep_list, ep.ep_list) { + mv_u3d_nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&u3d->lock); + driver->disconnect(&u3d->gadget); + spin_lock(&u3d->lock); + } +} + +static void mv_u3d_irq_process_error(struct mv_u3d *u3d) +{ + /* Increment the error count */ + u3d->errors++; + dev_err(u3d->dev, "%s\n", __func__); +} + +static void mv_u3d_irq_process_link_change(struct mv_u3d *u3d) +{ + u32 linkchange; + + linkchange = ioread32(&u3d->vuc_regs->linkchange); + iowrite32(linkchange, &u3d->vuc_regs->linkchange); + + dev_dbg(u3d->dev, "linkchange: 0x%x\n", linkchange); + + if (linkchange & MV_U3D_LINK_CHANGE_LINK_UP) { + dev_dbg(u3d->dev, "link up: ltssm state: 0x%x\n", + ioread32(&u3d->vuc_regs->ltssmstate)); + + u3d->usb_state = USB_STATE_DEFAULT; + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; + u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; + + /* set speed */ + u3d->gadget.speed = USB_SPEED_SUPER; + } + + if (linkchange & MV_U3D_LINK_CHANGE_SUSPEND) { + dev_dbg(u3d->dev, "link suspend\n"); + u3d->resume_state = u3d->usb_state; + u3d->usb_state = USB_STATE_SUSPENDED; + } + + if (linkchange & MV_U3D_LINK_CHANGE_RESUME) { + dev_dbg(u3d->dev, "link resume\n"); + u3d->usb_state = u3d->resume_state; + u3d->resume_state = 0; + } + + if (linkchange & MV_U3D_LINK_CHANGE_WRESET) { + dev_dbg(u3d->dev, "warm reset\n"); + u3d->usb_state = USB_STATE_POWERED; + } + + if (linkchange & MV_U3D_LINK_CHANGE_HRESET) { + dev_dbg(u3d->dev, "hot reset\n"); + u3d->usb_state = USB_STATE_DEFAULT; + } + + if (linkchange & MV_U3D_LINK_CHANGE_INACT) + dev_dbg(u3d->dev, "inactive\n"); + + if (linkchange & MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0) + dev_dbg(u3d->dev, "ss.disabled\n"); + + if (linkchange & MV_U3D_LINK_CHANGE_VBUS_INVALID) { + dev_dbg(u3d->dev, "vbus invalid\n"); + u3d->usb_state = USB_STATE_ATTACHED; + u3d->vbus_valid_detect = 1; + /* if external vbus detect is not supported, + * we handle it here. + */ + if (!u3d->vbus) { + spin_unlock(&u3d->lock); + mv_u3d_vbus_session(&u3d->gadget, 0); + spin_lock(&u3d->lock); + } + } +} + +static void mv_u3d_ch9setaddress(struct mv_u3d *u3d, + struct usb_ctrlrequest *setup) +{ + u32 tmp; + + if (u3d->usb_state != USB_STATE_DEFAULT) { + dev_err(u3d->dev, + "%s, cannot setaddr in this state (%d)\n", + __func__, u3d->usb_state); + goto err; + } + + u3d->dev_addr = (u8)setup->wValue; + + dev_dbg(u3d->dev, "%s: 0x%x\n", __func__, u3d->dev_addr); + + if (u3d->dev_addr > 127) { + dev_err(u3d->dev, + "%s, u3d address is wrong (out of range)\n", __func__); + u3d->dev_addr = 0; + goto err; + } + + /* update usb state */ + u3d->usb_state = USB_STATE_ADDRESS; + + /* set the new address */ + tmp = ioread32(&u3d->vuc_regs->devaddrtiebrkr); + tmp &= ~0x7F; + tmp |= (u32)u3d->dev_addr; + iowrite32(tmp, &u3d->vuc_regs->devaddrtiebrkr); + + return; +err: + mv_u3d_ep0_stall(u3d); +} + +static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup) +{ + if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + if (setup->bRequest == USB_REQ_SET_CONFIGURATION) + return 1; + + return 0; +} + +static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num, + struct usb_ctrlrequest *setup) +{ + bool delegate = false; + + mv_u3d_nuke(&u3d->eps[ep_num * 2 + MV_U3D_EP_DIR_IN], -ESHUTDOWN); + + dev_dbg(u3d->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", + setup->bRequestType, setup->bRequest, + setup->wValue, setup->wIndex, setup->wLength); + + /* We process some stardard setup requests here */ + if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (setup->bRequest) { + case USB_REQ_GET_STATUS: + delegate = true; + break; + + case USB_REQ_SET_ADDRESS: + mv_u3d_ch9setaddress(u3d, setup); + break; + + case USB_REQ_CLEAR_FEATURE: + delegate = true; + break; + + case USB_REQ_SET_FEATURE: + delegate = true; + break; + + default: + delegate = true; + } + } else + delegate = true; + + /* delegate USB standard requests to the gadget driver */ + if (delegate == true) { + /* USB requests handled by gadget */ + if (setup->wLength) { + /* DATA phase from gadget, STATUS phase from u3d */ + u3d->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? MV_U3D_EP_DIR_IN : MV_U3D_EP_DIR_OUT; + spin_unlock(&u3d->lock); + if (u3d->driver->setup(&u3d->gadget, + &u3d->local_setup_buff) < 0) { + dev_err(u3d->dev, "setup error!\n"); + mv_u3d_ep0_stall(u3d); + } + spin_lock(&u3d->lock); + } else { + /* no DATA phase, STATUS phase from gadget */ + u3d->ep0_dir = MV_U3D_EP_DIR_IN; + u3d->ep0_state = MV_U3D_STATUS_STAGE; + spin_unlock(&u3d->lock); + if (u3d->driver->setup(&u3d->gadget, + &u3d->local_setup_buff) < 0) + mv_u3d_ep0_stall(u3d); + spin_lock(&u3d->lock); + } + + if (mv_u3d_is_set_configuration(setup)) { + dev_dbg(u3d->dev, "u3d configured\n"); + u3d->usb_state = USB_STATE_CONFIGURED; + } + } +} + +static void mv_u3d_get_setup_data(struct mv_u3d *u3d, u8 ep_num, u8 *buffer_ptr) +{ + struct mv_u3d_ep_context *epcontext; + + epcontext = &u3d->ep_context[ep_num * 2 + MV_U3D_EP_DIR_IN]; + + /* Copy the setup packet to local buffer */ + memcpy(buffer_ptr, (u8 *) &epcontext->setup_buffer, 8); +} + +static void mv_u3d_irq_process_setup(struct mv_u3d *u3d) +{ + u32 tmp, i; + /* Process all Setup packet received interrupts */ + tmp = ioread32(&u3d->vuc_regs->setuplock); + if (tmp) { + for (i = 0; i < u3d->max_eps; i++) { + if (tmp & (1 << i)) { + mv_u3d_get_setup_data(u3d, i, + (u8 *)(&u3d->local_setup_buff)); + mv_u3d_handle_setup_packet(u3d, i, + &u3d->local_setup_buff); + } + } + } + + iowrite32(tmp, &u3d->vuc_regs->setuplock); +} + +static void mv_u3d_irq_process_tr_complete(struct mv_u3d *u3d) +{ + u32 tmp, bit_pos; + int i, ep_num = 0, direction = 0; + struct mv_u3d_ep *curr_ep; + struct mv_u3d_req *curr_req, *temp_req; + int status; + + tmp = ioread32(&u3d->vuc_regs->endcomplete); + + dev_dbg(u3d->dev, "tr_complete: ep: 0x%x\n", tmp); + if (!tmp) + return; + iowrite32(tmp, &u3d->vuc_regs->endcomplete); + + for (i = 0; i < u3d->max_eps * 2; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_pos = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & tmp)) + continue; + + if (i == 0) + curr_ep = &u3d->eps[1]; + else + curr_ep = &u3d->eps[i]; + + /* remove req out of ep request list after completion */ + dev_dbg(u3d->dev, "tr comp: check req_list\n"); + spin_lock(&curr_ep->req_lock); + if (!list_empty(&curr_ep->req_list)) { + struct mv_u3d_req *req; + req = list_entry(curr_ep->req_list.next, + struct mv_u3d_req, list); + list_del_init(&req->list); + curr_ep->processing = 0; + } + spin_unlock(&curr_ep->req_lock); + + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, + &curr_ep->queue, queue) { + status = mv_u3d_process_ep_req(u3d, i, curr_req); + if (status) + break; + /* write back status to req */ + curr_req->req.status = status; + + /* ep0 request completion */ + if (ep_num == 0) { + mv_u3d_done(curr_ep, curr_req, 0); + break; + } else { + mv_u3d_done(curr_ep, curr_req, status); + } + } + + dev_dbg(u3d->dev, "call mv_u3d_start_queue from ep complete\n"); + mv_u3d_start_queue(curr_ep); + } +} + +static irqreturn_t mv_u3d_irq(int irq, void *dev) +{ + struct mv_u3d *u3d = (struct mv_u3d *)dev; + u32 status, intr; + u32 bridgesetting; + u32 trbunderrun; + + spin_lock(&u3d->lock); + + status = ioread32(&u3d->vuc_regs->intrcause); + intr = ioread32(&u3d->vuc_regs->intrenable); + status &= intr; + + if (status == 0) { + spin_unlock(&u3d->lock); + dev_err(u3d->dev, "irq error!\n"); + return IRQ_NONE; + } + + if (status & MV_U3D_USBINT_VBUS_VALID) { + bridgesetting = ioread32(&u3d->vuc_regs->bridgesetting); + if (bridgesetting & MV_U3D_BRIDGE_SETTING_VBUS_VALID) { + /* write vbus valid bit of bridge setting to clear */ + bridgesetting = MV_U3D_BRIDGE_SETTING_VBUS_VALID; + iowrite32(bridgesetting, &u3d->vuc_regs->bridgesetting); + dev_dbg(u3d->dev, "vbus valid\n"); + + u3d->usb_state = USB_STATE_POWERED; + u3d->vbus_valid_detect = 0; + /* if external vbus detect is not supported, + * we handle it here. + */ + if (!u3d->vbus) { + spin_unlock(&u3d->lock); + mv_u3d_vbus_session(&u3d->gadget, 1); + spin_lock(&u3d->lock); + } + } else + dev_err(u3d->dev, "vbus bit is not set\n"); + } + + /* RX data is already in the 16KB FIFO.*/ + if (status & MV_U3D_USBINT_UNDER_RUN) { + trbunderrun = ioread32(&u3d->vuc_regs->trbunderrun); + dev_err(u3d->dev, "under run, ep%d\n", trbunderrun); + iowrite32(trbunderrun, &u3d->vuc_regs->trbunderrun); + mv_u3d_irq_process_error(u3d); + } + + if (status & (MV_U3D_USBINT_RXDESC_ERR | MV_U3D_USBINT_TXDESC_ERR)) { + /* write one to clear */ + iowrite32(status & (MV_U3D_USBINT_RXDESC_ERR + | MV_U3D_USBINT_TXDESC_ERR), + &u3d->vuc_regs->intrcause); + dev_err(u3d->dev, "desc err 0x%x\n", status); + mv_u3d_irq_process_error(u3d); + } + + if (status & MV_U3D_USBINT_LINK_CHG) + mv_u3d_irq_process_link_change(u3d); + + if (status & MV_U3D_USBINT_TX_COMPLETE) + mv_u3d_irq_process_tr_complete(u3d); + + if (status & MV_U3D_USBINT_RX_COMPLETE) + mv_u3d_irq_process_tr_complete(u3d); + + if (status & MV_U3D_USBINT_SETUP) + mv_u3d_irq_process_setup(u3d); + + spin_unlock(&u3d->lock); + return IRQ_HANDLED; +} + +static void mv_u3d_gadget_release(struct device *dev) +{ + dev_dbg(dev, "%s\n", __func__); +} + +static __devexit int mv_u3d_remove(struct platform_device *dev) +{ + struct mv_u3d *u3d = platform_get_drvdata(dev); + + BUG_ON(u3d == NULL); + + usb_del_gadget_udc(&u3d->gadget); + + /* free memory allocated in probe */ + if (u3d->trb_pool) + dma_pool_destroy(u3d->trb_pool); + + if (u3d->ep_context) + dma_free_coherent(&dev->dev, u3d->ep_context_size, + u3d->ep_context, u3d->ep_context_dma); + + kfree(u3d->eps); + + if (u3d->irq) + free_irq(u3d->irq, &dev->dev); + + if (u3d->cap_regs) + iounmap(u3d->cap_regs); + u3d->cap_regs = NULL; + + kfree(u3d->status_req); + + clk_put(u3d->clk); + + device_unregister(&u3d->gadget.dev); + + platform_set_drvdata(dev, NULL); + + kfree(u3d); + + return 0; +} + +static int mv_u3d_probe(struct platform_device *dev) +{ + struct mv_u3d *u3d = NULL; + struct mv_usb_platform_data *pdata = dev->dev.platform_data; + int retval = 0; + struct resource *r; + size_t size; + + if (!dev->dev.platform_data) { + dev_err(&dev->dev, "missing platform_data\n"); + retval = -ENODEV; + goto err_pdata; + } + + u3d = kzalloc(sizeof(*u3d), GFP_KERNEL); + if (!u3d) { + dev_err(&dev->dev, "failed to allocate memory for u3d\n"); + retval = -ENOMEM; + goto err_alloc_private; + } + + spin_lock_init(&u3d->lock); + + platform_set_drvdata(dev, u3d); + + u3d->dev = &dev->dev; + u3d->vbus = pdata->vbus; + + u3d->clk = clk_get(&dev->dev, pdata->clkname[0]); + if (IS_ERR(u3d->clk)) { + retval = PTR_ERR(u3d->clk); + goto err_get_clk; + } + + r = platform_get_resource_byname(dev, IORESOURCE_MEM, "capregs"); + if (!r) { + dev_err(&dev->dev, "no I/O memory resource defined\n"); + retval = -ENODEV; + goto err_get_cap_regs; + } + + u3d->cap_regs = (struct mv_u3d_cap_regs __iomem *) + ioremap(r->start, resource_size(r)); + if (!u3d->cap_regs) { + dev_err(&dev->dev, "failed to map I/O memory\n"); + retval = -EBUSY; + goto err_map_cap_regs; + } else { + dev_dbg(&dev->dev, "cap_regs address: 0x%x/0x%x\n", + (unsigned int)r->start, (unsigned int)u3d->cap_regs); + } + + /* we will access controller register, so enable the u3d controller */ + clk_enable(u3d->clk); + + if (pdata->phy_init) { + retval = pdata->phy_init(u3d->phy_regs); + if (retval) { + dev_err(&dev->dev, "init phy error %d\n", retval); + goto err_u3d_enable; + } + } + + u3d->op_regs = (struct mv_u3d_op_regs __iomem *)((u32)u3d->cap_regs + + MV_U3D_USB3_OP_REGS_OFFSET); + + u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)((u32)u3d->cap_regs + + ioread32(&u3d->cap_regs->vuoff)); + + u3d->max_eps = 16; + + /* + * some platform will use usb to download image, it may not disconnect + * usb gadget before loading kernel. So first stop u3d here. + */ + mv_u3d_controller_stop(u3d); + iowrite32(0xFFFFFFFF, &u3d->vuc_regs->intrcause); + + if (pdata->phy_deinit) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); + + size = u3d->max_eps * sizeof(struct mv_u3d_ep_context) * 2; + size = (size + MV_U3D_EP_CONTEXT_ALIGNMENT - 1) + & ~(MV_U3D_EP_CONTEXT_ALIGNMENT - 1); + u3d->ep_context = dma_alloc_coherent(&dev->dev, size, + &u3d->ep_context_dma, GFP_KERNEL); + if (!u3d->ep_context) { + dev_err(&dev->dev, "allocate ep context memory failed\n"); + retval = -ENOMEM; + goto err_alloc_ep_context; + } + u3d->ep_context_size = size; + + /* create TRB dma_pool resource */ + u3d->trb_pool = dma_pool_create("u3d_trb", + &dev->dev, + sizeof(struct mv_u3d_trb_hw), + MV_U3D_TRB_ALIGNMENT, + MV_U3D_DMA_BOUNDARY); + + if (!u3d->trb_pool) { + retval = -ENOMEM; + goto err_alloc_trb_pool; + } + + size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2; + u3d->eps = kzalloc(size, GFP_KERNEL); + if (!u3d->eps) { + dev_err(&dev->dev, "allocate ep memory failed\n"); + retval = -ENOMEM; + goto err_alloc_eps; + } + + /* initialize ep0 status request structure */ + u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL); + if (!u3d->status_req) { + dev_err(&dev->dev, "allocate status_req memory failed\n"); + retval = -ENOMEM; + goto err_alloc_status_req; + } + INIT_LIST_HEAD(&u3d->status_req->queue); + + /* allocate a small amount of memory to get valid address */ + u3d->status_req->req.buf = (char *)u3d->status_req + + sizeof(struct mv_u3d_req); + u3d->status_req->req.dma = virt_to_phys(u3d->status_req->req.buf); + + u3d->resume_state = USB_STATE_NOTATTACHED; + u3d->usb_state = USB_STATE_ATTACHED; + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; + u3d->remote_wakeup = 0; + + r = platform_get_resource(dev, IORESOURCE_IRQ, 0); + if (!r) { + dev_err(&dev->dev, "no IRQ resource defined\n"); + retval = -ENODEV; + goto err_get_irq; + } + u3d->irq = r->start; + if (request_irq(u3d->irq, mv_u3d_irq, + IRQF_DISABLED | IRQF_SHARED, driver_name, u3d)) { + u3d->irq = 0; + dev_err(&dev->dev, "Request irq %d for u3d failed\n", + u3d->irq); + retval = -ENODEV; + goto err_request_irq; + } + + /* initialize gadget structure */ + u3d->gadget.ops = &mv_u3d_ops; /* usb_gadget_ops */ + u3d->gadget.ep0 = &u3d->eps[1].ep; /* gadget ep0 */ + INIT_LIST_HEAD(&u3d->gadget.ep_list); /* ep_list */ + u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ + + /* the "gadget" abstracts/virtualizes the controller */ + dev_set_name(&u3d->gadget.dev, "gadget"); + u3d->gadget.dev.parent = &dev->dev; + u3d->gadget.dev.dma_mask = dev->dev.dma_mask; + u3d->gadget.dev.release = mv_u3d_gadget_release; + u3d->gadget.name = driver_name; /* gadget name */ + + retval = device_register(&u3d->gadget.dev); + if (retval) + goto err_register_gadget_device; + + mv_u3d_eps_init(u3d); + + /* external vbus detection */ + if (u3d->vbus) { + u3d->clock_gating = 1; + dev_err(&dev->dev, "external vbus detection\n"); + } + + if (!u3d->clock_gating) + u3d->vbus_active = 1; + + /* enable usb3 controller vbus detection */ + u3d->vbus_valid_detect = 1; + + retval = usb_add_gadget_udc(&dev->dev, &u3d->gadget); + if (retval) + goto err_unregister; + + dev_dbg(&dev->dev, "successful probe usb3 device %s clock gating.\n", + u3d->clock_gating ? "with" : "without"); + + return 0; + +err_unregister: + device_unregister(&u3d->gadget.dev); +err_register_gadget_device: + free_irq(u3d->irq, &dev->dev); +err_request_irq: +err_get_irq: + kfree(u3d->status_req); +err_alloc_status_req: + kfree(u3d->eps); +err_alloc_eps: + dma_pool_destroy(u3d->trb_pool); +err_alloc_trb_pool: + dma_free_coherent(&dev->dev, u3d->ep_context_size, + u3d->ep_context, u3d->ep_context_dma); +err_alloc_ep_context: + if (pdata->phy_deinit) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); +err_u3d_enable: + iounmap(u3d->cap_regs); +err_map_cap_regs: +err_get_cap_regs: +err_get_clk: + clk_put(u3d->clk); + platform_set_drvdata(dev, NULL); + kfree(u3d); +err_alloc_private: +err_pdata: + return retval; +} + +#ifdef CONFIG_PM +static int mv_u3d_suspend(struct device *dev) +{ + struct mv_u3d *u3d = dev_get_drvdata(dev); + + /* + * only cable is unplugged, usb can suspend. + * So do not care about clock_gating == 1, it is handled by + * vbus session. + */ + if (!u3d->clock_gating) { + mv_u3d_controller_stop(u3d); + + spin_lock_irq(&u3d->lock); + /* stop all usb activities */ + mv_u3d_stop_activity(u3d, u3d->driver); + spin_unlock_irq(&u3d->lock); + + mv_u3d_disable(u3d); + } + + return 0; +} + +static int mv_u3d_resume(struct device *dev) +{ + struct mv_u3d *u3d = dev_get_drvdata(dev); + int retval; + + if (!u3d->clock_gating) { + retval = mv_u3d_enable(u3d); + if (retval) + return retval; + + if (u3d->driver && u3d->softconnect) { + mv_u3d_controller_reset(u3d); + mv_u3d_ep0_reset(u3d); + mv_u3d_controller_start(u3d); + } + } + + return 0; +} + +SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume); +#endif + +static void mv_u3d_shutdown(struct platform_device *dev) +{ + struct mv_u3d *u3d = dev_get_drvdata(&dev->dev); + u32 tmp; + + tmp = ioread32(&u3d->op_regs->usbcmd); + tmp &= ~MV_U3D_CMD_RUN_STOP; + iowrite32(tmp, &u3d->op_regs->usbcmd); +} + +static struct platform_driver mv_u3d_driver = { + .probe = mv_u3d_probe, + .remove = __exit_p(mv_u3d_remove), + .shutdown = mv_u3d_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "mv-u3d", +#ifdef CONFIG_PM + .pm = &mv_u3d_pm_ops, +#endif + }, +}; + +module_platform_driver(mv_u3d_driver); +MODULE_ALIAS("platform:mv-u3d"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Yu Xu "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From ba789170d251a3c47854cee8a492d8ba7345f468 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 15 Jun 2012 18:39:02 -0300 Subject: usb: gadget: fsl_mxc_udc: do not depend on grouped clocks With the new common clock infrastructure, the following clocks should be used on i.MX drivers: ipg, per and ahb. Adapt fsl_mxc_udc to follow this new behaviour to fix the following probe error: Freescale High-Speed USB SOC Device Controller driver (Apr 20, 2007) fsl-usb2-udc fsl-usb2-udc: clk_get("usb") failed fsl-usb2-udc: probe of fsl-usb2-udc failed with error -2 Reported-by: Christoph Fritz Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Signed-off-by: Felipe Balbi --- drivers/usb/gadget/fsl_mxc_udc.c | 74 +++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c index f4b5109feb3..1b0f086426b 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -21,7 +21,8 @@ #include static struct clk *mxc_ahb_clk; -static struct clk *mxc_usb_clk; +static struct clk *mxc_per_clk; +static struct clk *mxc_ipg_clk; /* workaround ENGcm09152 for i.MX35 */ #define USBPHYCTRL_OTGBASE_OFFSET 0x608 @@ -35,28 +36,31 @@ int fsl_udc_clk_init(struct platform_device *pdev) pdata = pdev->dev.platform_data; - if (!cpu_is_mx35() && !cpu_is_mx25()) { - mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb"); - if (IS_ERR(mxc_ahb_clk)) - return PTR_ERR(mxc_ahb_clk); + mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(mxc_ipg_clk)) { + dev_err(&pdev->dev, "clk_get(\"ipg\") failed\n"); + return PTR_ERR(mxc_ipg_clk); + } - ret = clk_prepare_enable(mxc_ahb_clk); - if (ret < 0) { - dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n"); - goto eenahb; - } + mxc_ahb_clk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(mxc_ahb_clk)) { + dev_err(&pdev->dev, "clk_get(\"ahb\") failed\n"); + return PTR_ERR(mxc_ahb_clk); } - /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */ - mxc_usb_clk = clk_get(&pdev->dev, "usb"); - if (IS_ERR(mxc_usb_clk)) { - dev_err(&pdev->dev, "clk_get(\"usb\") failed\n"); - ret = PTR_ERR(mxc_usb_clk); - goto egusb; + mxc_per_clk = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(mxc_per_clk)) { + dev_err(&pdev->dev, "clk_get(\"per\") failed\n"); + return PTR_ERR(mxc_per_clk); } + clk_prepare_enable(mxc_ipg_clk); + clk_prepare_enable(mxc_ahb_clk); + clk_prepare_enable(mxc_per_clk); + + /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */ if (!cpu_is_mx51()) { - freq = clk_get_rate(mxc_usb_clk); + freq = clk_get_rate(mxc_per_clk); if (pdata->phy_mode != FSL_USB2_PHY_ULPI && (freq < 59999000 || freq > 60001000)) { dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq); @@ -65,24 +69,13 @@ int fsl_udc_clk_init(struct platform_device *pdev) } } - ret = clk_prepare_enable(mxc_usb_clk); - if (ret < 0) { - dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n"); - goto eenusb; - } - return 0; -eenusb: eclkrate: - clk_put(mxc_usb_clk); - mxc_usb_clk = NULL; -egusb: - if (!cpu_is_mx35()) - clk_disable_unprepare(mxc_ahb_clk); -eenahb: - if (!cpu_is_mx35()) - clk_put(mxc_ahb_clk); + clk_disable_unprepare(mxc_ipg_clk); + clk_disable_unprepare(mxc_ahb_clk); + clk_disable_unprepare(mxc_per_clk); + mxc_per_clk = NULL; return ret; } @@ -104,20 +97,15 @@ void fsl_udc_clk_finalize(struct platform_device *pdev) /* ULPI transceivers don't need usbpll */ if (pdata->phy_mode == FSL_USB2_PHY_ULPI) { - clk_disable_unprepare(mxc_usb_clk); - clk_put(mxc_usb_clk); - mxc_usb_clk = NULL; + clk_disable_unprepare(mxc_per_clk); + mxc_per_clk = NULL; } } void fsl_udc_clk_release(void) { - if (mxc_usb_clk) { - clk_disable_unprepare(mxc_usb_clk); - clk_put(mxc_usb_clk); - } - if (!cpu_is_mx35()) { - clk_disable_unprepare(mxc_ahb_clk); - clk_put(mxc_ahb_clk); - } + if (mxc_per_clk) + clk_disable_unprepare(mxc_per_clk); + clk_disable_unprepare(mxc_ahb_clk); + clk_disable_unprepare(mxc_ipg_clk); } -- cgit v1.2.3-70-g09d2 From 508566998b8de0a95e39da9d35893df9e212328d Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Wed, 20 Jun 2012 09:03:15 -0300 Subject: usb: gadget: lpc32xx_udc: move clock init to clock framework Moves clock initialization to the clock framework. Signed-off-by: Alexandre Pereira da Silva Acked-by: Roland Stigge Signed-off-by: Felipe Balbi --- drivers/usb/gadget/lpc32xx_udc.c | 84 ++++++++++++---------------------------- 1 file changed, 24 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 262acfd53e3..d227d93ce89 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -164,6 +164,7 @@ struct lpc32xx_udc { int udp_irq[4]; struct clk *usb_pll_clk; struct clk *usb_slv_clk; + struct clk *usb_otg_clk; /* DMA support */ u32 *udca_v_base; @@ -226,33 +227,15 @@ static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g) #define UDCA_BUFF_SIZE (128) /* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will - * be replaced with an inremap()ed pointer, see USB_OTG_CLK_CTRL() + * be replaced with an inremap()ed pointer * */ #define USB_CTRL IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64) -#define USB_CLOCK_MASK (AHB_M_CLOCK_ON | OTG_CLOCK_ON | \ - DEV_CLOCK_ON | I2C_CLOCK_ON) /* USB_CTRL bit defines */ #define USB_SLAVE_HCLK_EN (1 << 24) #define USB_HOST_NEED_CLK_EN (1 << 21) #define USB_DEV_NEED_CLK_EN (1 << 22) -#define USB_OTG_CLK_CTRL(udc) ((udc)->udp_baseaddr + 0xFF4) -#define USB_OTG_CLK_STAT(udc) ((udc)->udp_baseaddr + 0xFF8) - -/* USB_OTG_CLK_CTRL bit defines */ -#define AHB_M_CLOCK_ON (1 << 4) -#define OTG_CLOCK_ON (1 << 3) -#define I2C_CLOCK_ON (1 << 2) -#define DEV_CLOCK_ON (1 << 1) -#define HOST_CLOCK_ON (1 << 0) - -#define USB_OTG_STAT_CONTROL(udc) (udc->udp_baseaddr + 0x110) - -/* USB_OTG_STAT_CONTROL bit defines */ -#define TRANSPARENT_I2C_EN (1 << 7) -#define HOST_EN (1 << 0) - /********************************************************************** * USB device controller register offsets **********************************************************************/ @@ -676,7 +659,7 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc) ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD); /* Enable usb_need_clk clock after transceiver is initialized */ - writel((readl(USB_CTRL) | (1 << 22)), USB_CTRL); + writel((readl(USB_CTRL) | USB_DEV_NEED_CLK_EN), USB_CTRL); dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n", i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00)); @@ -1009,11 +992,8 @@ static void udc_dd_free(struct lpc32xx_udc *udc, struct lpc32xx_usbd_dd_gad *dd) /* Enables or disables most of the USB system clocks when low power mode is * needed. Clocks are typically started on a connection event, and disabled * when a cable is disconnected */ -#define OTGOFF_CLK_MASK (AHB_M_CLOCK_ON | I2C_CLOCK_ON) static void udc_clk_set(struct lpc32xx_udc *udc, int enable) { - int to = 1000; - if (enable != 0) { if (udc->clocked) return; @@ -1027,14 +1007,7 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable) writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN, USB_CTRL); - /* Set to enable all needed USB OTG clocks */ - writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL(udc)); - - while (((readl(USB_OTG_CLK_STAT(udc)) & USB_CLOCK_MASK) != - USB_CLOCK_MASK) && (to > 0)) - to--; - if (!to) - dev_dbg(udc->dev, "Cannot enable USB OTG clocking\n"); + clk_enable(udc->usb_otg_clk); } else { if (!udc->clocked) return; @@ -1046,19 +1019,11 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable) /* 48MHz PLL dpwn */ clk_disable(udc->usb_pll_clk); - /* Enable the USB device clock */ + /* Disable the USB device clock */ writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN, USB_CTRL); - /* Set to enable all needed USB OTG clocks */ - writel(OTGOFF_CLK_MASK, USB_OTG_CLK_CTRL(udc)); - - while (((readl(USB_OTG_CLK_STAT(udc)) & - OTGOFF_CLK_MASK) != - OTGOFF_CLK_MASK) && (to > 0)) - to--; - if (!to) - dev_dbg(udc->dev, "Cannot disable USB OTG clocking\n"); + clk_disable(udc->usb_otg_clk); } } @@ -3238,6 +3203,12 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) retval = PTR_ERR(udc->usb_slv_clk); goto usb_clk_get_fail; } + udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg"); + if (IS_ERR(udc->usb_otg_clk)) { + dev_err(udc->dev, "failed to acquire USB otg clock\n"); + retval = PTR_ERR(udc->usb_slv_clk); + goto usb_otg_clk_get_fail; + } /* Setup PLL clock to 48MHz */ retval = clk_enable(udc->usb_pll_clk); @@ -3261,15 +3232,12 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) goto usb_clk_enable_fail; } - /* Set to enable all needed USB OTG clocks */ - writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL(udc)); - - i = 1000; - while (((readl(USB_OTG_CLK_STAT(udc)) & USB_CLOCK_MASK) != - USB_CLOCK_MASK) && (i > 0)) - i--; - if (!i) - dev_dbg(udc->dev, "USB OTG clocks not correctly enabled\n"); + /* Enable USB OTG clock */ + retval = clk_enable(udc->usb_otg_clk); + if (retval < 0) { + dev_err(udc->dev, "failed to start USB otg clock\n"); + goto usb_otg_clk_enable_fail; + } /* Setup deferred workqueue data */ udc->poweron = udc->pullup = 0; @@ -3389,12 +3357,16 @@ dma_alloc_fail: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); i2c_fail: + clk_disable(udc->usb_otg_clk); +usb_otg_clk_enable_fail: clk_disable(udc->usb_slv_clk); usb_clk_enable_fail: pll_set_fail: clk_disable(udc->usb_pll_clk); pll_enable_fail: clk_put(udc->usb_slv_clk); +usb_otg_clk_get_fail: + clk_put(udc->usb_otg_clk); usb_clk_get_fail: clk_put(udc->usb_pll_clk); pll_get_fail: @@ -3432,6 +3404,8 @@ static int __devexit lpc32xx_udc_remove(struct platform_device *pdev) device_unregister(&udc->gadget.dev); + clk_disable(udc->usb_otg_clk); + clk_put(udc->usb_otg_clk); clk_disable(udc->usb_slv_clk); clk_put(udc->usb_slv_clk); clk_disable(udc->usb_pll_clk); @@ -3445,7 +3419,6 @@ static int __devexit lpc32xx_udc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg) { - int to = 1000; struct lpc32xx_udc *udc = platform_get_drvdata(pdev); if (udc->clocked) { @@ -3460,15 +3433,6 @@ static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg) on resume */ udc->clocked = 1; - /* Kill OTG and I2C clocks */ - writel(0, USB_OTG_CLK_CTRL(udc)); - while (((readl(USB_OTG_CLK_STAT(udc)) & OTGOFF_CLK_MASK) != - OTGOFF_CLK_MASK) && (to > 0)) - to--; - if (!to) - dev_dbg(udc->dev, - "USB OTG clocks not correctly enabled\n"); - /* Kill global USB clock */ clk_disable(udc->usb_slv_clk); } -- cgit v1.2.3-70-g09d2 From 6f47209b271661ecd5929397cbe646ff247f01b6 Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Fri, 22 Jun 2012 14:16:19 +1000 Subject: usb: gadget: acm_ms: need to set max_speed Failing to set max_speed prevents g_acm_ms working with many drivers which check for driver->max_speed < USB_SPEED_FULL, including pxa25x_udc Signed-off-by: Steve Bennett Signed-off-by: Felipe Balbi --- drivers/usb/gadget/acm_ms.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index fdb7aec3bd0..75b8a691fd0 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -235,6 +235,7 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) static struct usb_composite_driver acm_ms_driver = { .name = "g_acm_ms", .dev = &device_desc, + .max_speed = USB_SPEED_SUPER, .strings = dev_strings, .unbind = __exit_p(acm_ms_unbind), }; -- cgit v1.2.3-70-g09d2 From f87cabf4d56e1fc5d08434df9d54ef3450a756f0 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 18 Jun 2012 14:37:19 +0200 Subject: usb: gadget: storage_common: remove FSG_BUFFHD_STATIC_BUFFER support Since f_mass_storage stopped using FSG_BUFFHD_STATIC_BUFFER (because it caused buffers not to be page aligned which did not work well with at least some UDCs), no code was using it. Removing not to bloat the code too much. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/storage_common.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 8081ca3a70a..8a8157f96a2 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -37,12 +37,6 @@ * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. */ -/* - * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included - * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN - * characters rather then a pointer to void. - */ - /* * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers * sets the number of pipeline buffers (length of the fsg_buffhd array). @@ -260,11 +254,7 @@ enum fsg_buffer_state { }; struct fsg_buffhd { -#ifdef FSG_BUFFHD_STATIC_BUFFER - char buf[FSG_BUFLEN]; -#else void *buf; -#endif enum fsg_buffer_state state; struct fsg_buffhd *next; -- cgit v1.2.3-70-g09d2 From d6e16a89578fcc8834be634c85c5c5ddc2d13229 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 18 Jun 2012 14:37:20 +0200 Subject: usb: gadget: mass_storage: fail fsg_store_file() early if colud not open file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, when a new value is stored to the “file” sysfs entry, fsg_store_file() will release existing backing file and only then attempt to open a new one. If that fails, no new backing file is open. This commit changes the fsg_lun_open() so that it closes existing backing file only after the new backing file has been successfully opened. With that change, fsg_store_file() may use it to perform an atomic open operation with guarantee that logical unit will either point to the new backing file or still to the old one. Signed-off-by: Michal Nazarewicz Acked-by: Alan Stern --- drivers/usb/gadget/storage_common.c | 52 +++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 8a8157f96a2..e576678fb62 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -617,6 +617,16 @@ static struct usb_gadget_strings fsg_stringtab = { * the caller must own fsg->filesem for writing. */ +static void fsg_lun_close(struct fsg_lun *curlun) +{ + if (curlun->filp) { + LDBG(curlun, "close backing file\n"); + fput(curlun->filp); + curlun->filp = NULL; + } +} + + static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) { int ro; @@ -626,6 +636,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) loff_t size; loff_t num_sectors; loff_t min_sectors; + unsigned int blkbits; + unsigned int blksize; /* R/W if we can, R/O if we must */ ro = curlun->initially_ro; @@ -670,17 +682,17 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) } if (curlun->cdrom) { - curlun->blksize = 2048; - curlun->blkbits = 11; + blksize = 2048; + blkbits = 11; } else if (inode->i_bdev) { - curlun->blksize = bdev_logical_block_size(inode->i_bdev); - curlun->blkbits = blksize_bits(curlun->blksize); + blksize = bdev_logical_block_size(inode->i_bdev); + blkbits = blksize_bits(blksize); } else { - curlun->blksize = 512; - curlun->blkbits = 9; + blksize = 512; + blkbits = 9; } - num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */ + num_sectors = size >> blkbits; /* File size in logic-block-size blocks */ min_sectors = 1; if (curlun->cdrom) { min_sectors = 300; /* Smallest track is 300 frames */ @@ -697,7 +709,12 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) goto out; } + if (fsg_lun_is_open(curlun)) + fsg_lun_close(curlun); + get_file(filp); + curlun->blksize = blksize; + curlun->blkbits = blkbits; curlun->ro = ro; curlun->filp = filp; curlun->file_length = size; @@ -711,16 +728,6 @@ out: } -static void fsg_lun_close(struct fsg_lun *curlun) -{ - if (curlun->filp) { - LDBG(curlun, "close backing file\n"); - fput(curlun->filp); - curlun->filp = NULL; - } -} - - /*-------------------------------------------------------------------------*/ /* @@ -871,19 +878,18 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, if (count > 0 && buf[count-1] == '\n') ((char *) buf)[count-1] = 0; /* Ugh! */ - /* Eject current medium */ - down_write(filesem); - if (fsg_lun_is_open(curlun)) { - fsg_lun_close(curlun); - curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; - } /* Load new medium */ + down_write(filesem); if (count > 0 && buf[0]) { + /* fsg_lun_open() will close existing file if any. */ rc = fsg_lun_open(curlun, buf); if (rc == 0) curlun->unit_attention_data = SS_NOT_READY_TO_READY_TRANSITION; + } else if (fsg_lun_is_open(curlun)) { + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; } up_write(filesem); return (rc < 0 ? rc : count); -- cgit v1.2.3-70-g09d2 From 45c396ce6bdad60ee94e6eed8cc7f09678651102 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 18 Jun 2012 11:32:37 +0530 Subject: usb: dwc3: Remove duplicate header file inclusion module.h header file was included twice. Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-exynos.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index d1903019808..b8f00389fa3 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include "core.h" -- cgit v1.2.3-70-g09d2 From 45627ac6a4f063d19b0bd9863d20ac1dabda99a7 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Thu, 21 Jun 2012 17:44:28 +0530 Subject: USB: DWC3: Put 100 ms delay for phy to be stable Before taking core out of reset phy must be stable. So wait for 100ms after clear phy reset. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 49c060205c9..ac151e9acf2 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -148,6 +148,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc) reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + mdelay(100); + /* After PHYs are stable we can take Core out of reset state */ reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_CORESOFTRESET; -- cgit v1.2.3-70-g09d2 From 58a0f23fddd0a71d8fa22e6ef7627298a8ed29a6 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Thu, 21 Jun 2012 17:44:29 +0530 Subject: USB: DWC3: Issue device soft reset before core soft reset Synopsys specification clearly states under section "Device Power-On or Soft Reset" that DCTL.CSftRst=1 should be first step. So, just follow what specification says. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ac151e9acf2..c34452a7304 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -335,8 +335,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) } dwc->revision = reg; - dwc3_core_soft_reset(dwc); - /* issue device SoftReset too */ timeout = jiffies + msecs_to_jiffies(500); dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); @@ -354,6 +352,8 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) cpu_relax(); } while (true); + dwc3_core_soft_reset(dwc); + dwc3_cache_hwparams(dwc); reg = dwc3_readl(dwc->regs, DWC3_GCTL); -- cgit v1.2.3-70-g09d2 From a188b6897e3dca82dd6f5beceabf1fc62b9786d9 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 22 Jun 2012 09:29:56 +0200 Subject: usb: gadget: hsotg: pullup method implementation for s3c-hsotg UDC driver This commit adds pullup method implementation for UDC s3c-hsotg driver. It is needed for e.g. CCG - Configurable Composite Gadget, when user space configuration change request device disconnection from USB bus (done via calling usb_gadget_connect/disconnect, which calls UDC's pullup method). Implementation of pullup method has caused removal of phy_enable and core_init methods from udc_start to pullup. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index d208c46341d..75a28e6d376 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2963,9 +2963,6 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, goto err; } - s3c_hsotg_phy_enable(hsotg); - - s3c_hsotg_core_init(hsotg); hsotg->last_rst = jiffies; dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name); return 0; @@ -3028,10 +3025,40 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget) return s3c_hsotg_read_frameno(to_hsotg(gadget)); } +/** + * s3c_hsotg_pullup - connect/disconnect the USB PHY + * @gadget: The usb gadget state + * @is_on: Current state of the USB PHY + * + * Connect/Disconnect the USB PHY pullup + */ +static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) +{ + struct s3c_hsotg *hsotg = to_hsotg(gadget); + unsigned long flags = 0; + + dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on); + + spin_lock_irqsave(&hsotg->lock, flags); + if (is_on) { + s3c_hsotg_phy_enable(hsotg); + s3c_hsotg_core_init(hsotg); + } else { + s3c_hsotg_disconnect(hsotg); + s3c_hsotg_phy_disable(hsotg); + } + + hsotg->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock_irqrestore(&hsotg->lock, flags); + + return 0; +} + static struct usb_gadget_ops s3c_hsotg_gadget_ops = { .get_frame = s3c_hsotg_gadget_getframe, .udc_start = s3c_hsotg_udc_start, .udc_stop = s3c_hsotg_udc_stop, + .pullup = s3c_hsotg_pullup, }; /** -- cgit v1.2.3-70-g09d2 From 3daf74d78dfcd552445c35f1b82a271ca305ceb9 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Sat, 23 Jun 2012 02:23:08 +0530 Subject: usb: dwc3: remove WARN_ON from dwc_stop_active_transfer Now we are sure that, if res_trans_idx is zero, then endpoint has been stopped. So it's safe to just return if endpoint is already stopped. No need to generate warning anymore. While doing so, it's better to return when res_trans_idx is zero and decrease one level of indentation. Signed-off-by: Pratyush Anand [ balbi@ti.com: slightly changed commit log ] Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f6fb42da2e6..9ac4835d7b6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1870,16 +1870,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) dep = dwc->eps[epnum]; - WARN_ON(!dep->res_trans_idx); - if (dep->res_trans_idx) { - cmd = DWC3_DEPCMD_ENDTRANSFER; - cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; - cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); - memset(¶ms, 0, sizeof(params)); - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); - WARN_ON_ONCE(ret); - dep->res_trans_idx = 0; - } + if (!dep->res_trans_idx) + return; + + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + WARN_ON_ONCE(ret); + dep->res_trans_idx = 0; } static void dwc3_stop_active_transfers(struct dwc3 *dwc) -- cgit v1.2.3-70-g09d2 From b511e5e76bf488cd3eec1048483a3898393a4dca Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Jun 2012 12:00:50 +0300 Subject: usb: dwc3: gadget: split special cases of ep_queue() It makes it easier to read and also avoids setting DWC3_EP_PENDING_REQUEST just so the next branch evaluates true. No functional changes otherwise. Cc: Pratyush Anand Cc: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 50 ++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9ac4835d7b6..1c98aee051a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1075,17 +1075,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) list_add_tail(&req->list, &dep->request_list); - if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - if (dep->flags & DWC3_EP_BUSY) { - dep->flags |= DWC3_EP_PENDING_REQUEST; - } else if (dep->flags & DWC3_EP_MISSED_ISOC) { - __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf); - dep->flags &= ~DWC3_EP_MISSED_ISOC; - } - } - /* - * There are two special cases: + * There are a few special cases: * * 1. XferNotReady with empty list of requests. We need to kick the * transfer here in that situation, otherwise we will be NAKing @@ -1094,24 +1085,29 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * able to receive the data until the next request is queued. * The following code is handling exactly that. * - * 2. XferInProgress on Isoc EP with an active transfer. We need to - * kick the transfer here after queuing a request, otherwise the - * core may not see the modified TRB(s). */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { int ret; - int start_trans = 1; - u8 trans_idx = dep->res_trans_idx; - if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && - (dep->flags & DWC3_EP_BUSY)) { - start_trans = 0; - WARN_ON_ONCE(!trans_idx); - } else { - trans_idx = 0; + ret = __dwc3_gadget_kick_transfer(dep, 0, true); + if (ret && ret != -EBUSY) { + struct dwc3 *dwc = dep->dwc; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); } + } - ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans); + /* + * 2. XferInProgress on Isoc EP with an active transfer. We need to + * kick the transfer here after queuing a request, otherwise the + * core may not see the modified TRB(s). + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + (dep->flags & DWC3_EP_BUSY)) { + WARN_ON_ONCE(!dep->res_trans_idx); + ret = __dwc3_gadget_kick_transfer(dep, dep->res_trans_idx, + false); if (ret && ret != -EBUSY) { struct dwc3 *dwc = dep->dwc; @@ -1120,6 +1116,16 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) } } + /* + * 3. Missed ISOC Handling. We need to start isoc transfer on the saved + * uframe number. + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + (dep->flags & DWC3_EP_MISSED_ISOC)) { + __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf); + dep->flags &= ~DWC3_EP_MISSED_ISOC; + } + return 0; } -- cgit v1.2.3-70-g09d2 From b4996a8631e80a2aaddb5c487ff6b9ad37315f70 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Jun 2012 12:04:13 +0300 Subject: usb: dwc3: rename res_trans_idx to resource_index resource_index is more human readable. No functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 4 ++-- drivers/usb/dwc3/ep0.c | 4 ++-- drivers/usb/dwc3/gadget.c | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d3e56cfb29c..151eca876df 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -404,7 +404,7 @@ struct dwc3_event_buffer { * @current_trb: index of current used trb * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK - * @res_trans_idx: Resource transfer index + * @resource_index: Resource transfer index * @current_uf: Current uf received through last event parameter * @interval: the intervall on which the ISOC transfer is started * @name: a human readable name e.g. ep1out-bulk @@ -438,7 +438,7 @@ struct dwc3_ep { u8 number; u8 type; - u8 res_trans_idx; + u8 resource_index; u16 current_uf; u32 interval; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 90eb1ba6545..9a8f26cef0a 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -113,7 +113,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, } dep->flags |= DWC3_EP_BUSY; - dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, dep->number); dwc->ep0_next_event = DWC3_EP0_COMPLETE; @@ -757,7 +757,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; dep->flags &= ~DWC3_EP_BUSY; - dep->res_trans_idx = 0; + dep->resource_index = 0; dwc->setup_packet_pending = false; switch (dwc->ep0state) { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1c98aee051a..1b5fbdd859c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1010,9 +1010,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, dep->flags |= DWC3_EP_BUSY; if (start_new) { - dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, dep->number); - WARN_ON_ONCE(!dep->res_trans_idx); + WARN_ON_ONCE(!dep->resource_index); } return 0; @@ -1105,8 +1105,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) */ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY)) { - WARN_ON_ONCE(!dep->res_trans_idx); - ret = __dwc3_gadget_kick_transfer(dep, dep->res_trans_idx, + WARN_ON_ONCE(!dep->resource_index); + ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index, false); if (ret && ret != -EBUSY) { struct dwc3 *dwc = dep->dwc; @@ -1790,7 +1790,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: - dep->res_trans_idx = 0; + dep->resource_index = 0; if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", @@ -1876,16 +1876,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) dep = dwc->eps[epnum]; - if (!dep->res_trans_idx) + if (!dep->resource_index) return; cmd = DWC3_DEPCMD_ENDTRANSFER; cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; - cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); + cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); WARN_ON_ONCE(ret); - dep->res_trans_idx = 0; + dep->resource_index = 0; } static void dwc3_stop_active_transfers(struct dwc3 *dwc) -- cgit v1.2.3-70-g09d2 From 721002ec1dd55a52425455826af49cf8853b2d4f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:45 +0530 Subject: usb: otg: utils: rename function name in OTG utils _transceiver() in otg.c is replaced with _phy. usb_set_transceiver is replaced with usb_add_phy to make it similar to other usb standard function names like usb_add_hcd. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 6 +++--- drivers/power/isp1704_charger.c | 6 +++--- drivers/power/pda_power.c | 6 +++--- drivers/power/twl4030_charger.c | 6 +++--- drivers/usb/chipidea/udc.c | 8 ++++---- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 6 +++--- drivers/usb/gadget/pxa25x_udc.c | 6 +++--- drivers/usb/gadget/pxa27x_udc.c | 4 ++-- drivers/usb/gadget/s3c-hsudc.c | 4 ++-- drivers/usb/host/ehci-fsl.c | 6 +++--- drivers/usb/host/ehci-msm.c | 6 +++--- drivers/usb/host/ehci-mv.c | 6 +++--- drivers/usb/host/ehci-tegra.c | 6 +++--- drivers/usb/host/ohci-omap.c | 6 +++--- drivers/usb/musb/am35x.c | 4 ++-- drivers/usb/musb/blackfin.c | 4 ++-- drivers/usb/musb/da8xx.c | 4 ++-- drivers/usb/musb/davinci.c | 6 +++--- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_dsps.c | 6 +++--- drivers/usb/musb/omap2430.c | 4 ++-- drivers/usb/musb/tusb6010.c | 6 +++--- drivers/usb/musb/ux500.c | 4 ++-- drivers/usb/otg/ab8500-usb.c | 4 ++-- drivers/usb/otg/fsl_otg.c | 6 +++--- drivers/usb/otg/gpio_vbus.c | 4 ++-- drivers/usb/otg/isp1301_omap.c | 4 ++-- drivers/usb/otg/msm_otg.c | 6 +++--- drivers/usb/otg/mv_otg.c | 6 +++--- drivers/usb/otg/nop-usb-xceiv.c | 4 ++-- drivers/usb/otg/otg.c | 32 ++++++++++++++++---------------- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 10 +++++----- 36 files changed, 103 insertions(+), 103 deletions(-) (limited to 'drivers') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index d2303d0b7c7..cf5ffc4d104 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2517,7 +2517,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) dev_err(di->dev, "%s mask and set failed\n", __func__); usb_unregister_notifier(di->usb_phy, &di->nb); - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); /* Delete the work queue */ destroy_workqueue(di->charger_wq); @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_transceiver(); + di->usb_phy = usb_get_phy(); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; @@ -2747,7 +2747,7 @@ free_irq: free_irq(irq, di); } put_usb_phy: - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); free_usb: power_supply_unregister(&di->usb_chg.psy); free_ac: diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index e5ccd297977..50773ae6f72 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_transceiver(); + isp->phy = usb_get_phy(); if (!isp->phy) goto fail0; @@ -475,7 +475,7 @@ fail2: power_supply_unregister(&isp->psy); fail1: isp1704_charger_set_power(isp, 0); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); fail0: kfree(isp); @@ -490,7 +490,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) usb_unregister_notifier(isp->phy, &isp->nb); power_supply_unregister(&isp->psy); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); isp1704_charger_set_power(isp, 0); kfree(isp); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 214468f4444..e0f206b0775 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_transceiver(); + transceiver = usb_get_phy(); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } @@ -409,7 +409,7 @@ usb_supply_failed: free_irq(ac_irq->start, &pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif ac_irq_failed: if (pdata->is_ac_online) @@ -444,7 +444,7 @@ static int pda_power_remove(struct platform_device *pdev) power_supply_unregister(&pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif if (ac_draw) { regulator_put(ac_draw); diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fdad850c77d..fcddd115cc0 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_transceiver(); + bci->transceiver = usb_get_phy(); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); @@ -509,7 +509,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) fail_unmask_interrupts: if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); fail_bci_irq: @@ -540,7 +540,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 51f96942dc5..4468f2c2ddd 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { @@ -1731,7 +1731,7 @@ static int udc_start(struct ci13xxx *udc) remove_trans: if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dev_err(dev, "error = %i\n", retval); @@ -1741,7 +1741,7 @@ unreg_device: device_unregister(&udc->gadget.dev); put_transceiver: if (udc->transceiver) - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); free_pools: dma_pool_destroy(udc->td_pool); free_qh_pool: @@ -1774,7 +1774,7 @@ static void udc_stop(struct ci13xxx *udc) if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dbg_remove_files(&udc->gadget.dev); device_unregister(&udc->gadget.dev); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 28316858208..d7038509b95 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_transceiver(); + udc_controller->transceiver = usb_get_phy(); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index dbcd1329495..5d779955d5a 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 7ba32469c5b..74b9bb8099e 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_transceiver(); + xceiv = usb_get_phy(); if (xceiv) type = xceiv->label; else if (config->otg) { @@ -3011,7 +3011,7 @@ cleanup1: cleanup0: if (xceiv) - usb_put_transceiver(xceiv); + usb_put_phy(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { clk_disable(hhc_clk); @@ -3041,7 +3041,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) pullup_disable(udc); if (udc->transceiver) { - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; } omap_writew(0, UDC_SYSCON1); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index d7c8cb3bf75..a658e446cab 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_transceiver(); + dev->transceiver = usb_get_phy(); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, @@ -2238,7 +2238,7 @@ lubbock_fail0: gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } clk_put(dev->clk); @@ -2280,7 +2280,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) clk_put(dev->clk); if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 98acb3ab9e1..b982304a49c 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { @@ -2543,7 +2543,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev) if (gpio_is_valid(gpio)) gpio_free(gpio); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; platform_set_drvdata(_dev, NULL); diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 36c6836eeb0..9ad33395f56 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_transceiver(); + hsudc->transceiver = usb_get_phy(); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; @@ -1386,7 +1386,7 @@ err_remap: release_mem_region(res->start, resource_size(res)); err_res: if (hsudc->transceiver) - usb_put_transceiver(hsudc->transceiver); + usb_put_phy(hsudc->transceiver); regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); err_supplies: diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54..0e8976a0ed5 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); + ehci->transceiver = usb_get_phy(); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); @@ -150,7 +150,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, retval = otg_set_host(ehci->transceiver->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); goto err4; } } else { @@ -194,7 +194,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, if (ehci->transceiver) { otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 9803a55fd5f..7badd5db398 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_transceiver(); + phy = usb_get_phy(); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; @@ -169,7 +169,7 @@ static int ehci_msm_probe(struct platform_device *pdev) return 0; put_transceiver: - usb_put_transceiver(phy); + usb_put_phy(phy); unmap: iounmap(hcd->regs); put_hcd: @@ -187,7 +187,7 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); otg_set_host(phy->otg, NULL); - usb_put_transceiver(phy); + usb_put_phy(phy); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index a936bbcff8f..24f838fe25a 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_transceiver(); + ehci_mv->otg = usb_get_phy(); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); @@ -303,7 +303,7 @@ err_set_vbus: #ifdef CONFIG_USB_OTG_UTILS err_put_transceiver: if (ehci_mv->otg) - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); #endif err_disable_clk: mv_ehci_disable(ehci_mv); @@ -333,7 +333,7 @@ static int mv_ehci_remove(struct platform_device *pdev) if (ehci_mv->otg) { otg_set_host(ehci_mv->otg->otg, NULL); - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); } if (ehci_mv->mode == MV_USB_MODE_HOST) { diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 68548236ec4..ee17d19b1b8 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_transceiver(); + tegra->transceiver = usb_get_phy(); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } @@ -775,7 +775,7 @@ fail: #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif tegra_usb_phy_close(tegra->phy); @@ -810,7 +810,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5..c2c1f55889a 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,14 +211,14 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); + ohci->transceiver = usb_get_phy(); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", ohci->transceiver->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); return status; } } else { @@ -405,7 +405,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) usb_remove_hcd(hcd); if (ohci->transceiver) { (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 9f3eda91ea4..a75989bbb3d 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -406,7 +406,7 @@ static int am35x_musb_exit(struct musb *musb) if (data->set_phy_power) data->set_phy_power(0); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index a087ed6c3be..522a4a263df 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; @@ -440,7 +440,7 @@ static int bfin_musb_exit(struct musb *musb) { gpio_free(musb->config->gpio_vrsel); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 8bd9566f3fb..61868d604b2 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto fail; @@ -458,7 +458,7 @@ static int da8xx_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 768b4b55c81..441f776366f 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto unregister; @@ -443,7 +443,7 @@ static int davinci_musb_init(struct musb *musb) return 0; fail: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); unregister: usb_nop_xceiv_unregister(); return -ENODEV; @@ -493,7 +493,7 @@ static int davinci_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index db3dff854b7..26f1befb489 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1909,7 +1909,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* The musb_platform_init() call: * - adjusts musb->mregs and musb->isr if needed, * - may initialize an integrated tranceiver - * - initializes musb->xceiv, usually by otg_get_transceiver() + * - initializes musb->xceiv, usually by otg_get_phy() * - stops powering VBUS * * There are various transceiver configurations. Blackfin, diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 23db42db761..716c113608f 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -409,7 +409,7 @@ static int dsps_musb_init(struct musb *musb) return 0; err0: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return status; } @@ -430,7 +430,7 @@ static int dsps_musb_exit(struct musb *musb) data->set_phy_power(0); /* NOP driver needs change if supporting dual instance */ - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c7785e81254..e16dbbf7f30 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -391,7 +391,7 @@ static int omap2430_musb_exit(struct musb *musb) cancel_work_sync(&musb->otg_notifier_work); omap2430_low_level_exit(musb); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index de1355946a8..a004736186f 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -1130,7 +1130,7 @@ done: if (sync) iounmap(sync); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); } return ret; @@ -1146,7 +1146,7 @@ static int tusb_musb_exit(struct musb *musb) iounmap(musb->sync_va); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index aa09dd417b9..53006b113b1 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -48,7 +48,7 @@ static int ux500_musb_init(struct musb *musb) static int ux500_musb_exit(struct musb *musb) { - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index a84af677dc5..672e28c8143 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_set_transceiver(&ab->phy); + err = usb_add_phy(&ab->phy); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_set_transceiver(NULL); + usb_add_phy(NULL); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index be4a63e8302..73561edd81d 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_set_transceiver(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_transceiver(); + struct usb_phy *otg_trans = usb_get_phy(); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index bde6298a969..9b3c264cdb5 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_set_transceiver(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 33cd709b084..b74df3fec56 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_set_transceiver(&isp->phy); + status = usb_add_phy(&isp->phy); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_set_transceiver(NULL); + usb_add_phy(NULL); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 1d0347c247d..dd606c01003 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,9 +1555,9 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_set_transceiver(&motg->phy); + ret = usb_add_phy(&motg->phy); if (ret) { - dev_err(&pdev->dev, "usb_set_transceiver failed\n"); + dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; } @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 6cc6c3ffbb8..18e90fe1fbd 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_set_transceiver(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_set_transceiver(NULL); + usb_add_phy(NULL); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 58b26df6afd..33000dae720 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_set_transceiver(&nop->phy); + err = usb_add_phy(&nop->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 801e597a154..300a995cfdb 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -18,53 +18,53 @@ static struct usb_phy *phy; /** - * usb_get_transceiver - find the (single) USB transceiver + * usb_get_phy - find the (single) USB PHY * - * Returns the transceiver driver, after getting a refcount to it; or - * null if there is no such transceiver. The caller is responsible for - * calling usb_put_transceiver() to release that count. + * Returns the phy driver, after getting a refcount to it; or + * null if there is no such phy. The caller is responsible for + * calling usb_put_phy() to release that count. * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_transceiver(void) +struct usb_phy *usb_get_phy(void) { if (phy) get_device(phy->dev); return phy; } -EXPORT_SYMBOL(usb_get_transceiver); +EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_transceiver - release the (single) USB transceiver - * @x: the transceiver returned by usb_get_transceiver() + * usb_put_phy - release the (single) USB PHY + * @x: the phy returned by usb_get_phy() * - * Releases a refcount the caller received from usb_get_transceiver(). + * Releases a refcount the caller received from usb_get_phy(). * * For use by USB host and peripheral drivers. */ -void usb_put_transceiver(struct usb_phy *x) +void usb_put_phy(struct usb_phy *x) { if (x) put_device(x->dev); } -EXPORT_SYMBOL(usb_put_transceiver); +EXPORT_SYMBOL(usb_put_phy); /** - * usb_set_transceiver - declare the (single) USB transceiver - * @x: the USB transceiver to be used; or NULL + * usb_add_phy - declare the (single) USB PHY + * @x: the USB phy to be used; or NULL * - * This call is exclusively for use by transceiver drivers, which + * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_set_transceiver(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x) { if (phy && x) return -EBUSY; phy = x; return 0; } -EXPORT_SYMBOL(usb_set_transceiver); +EXPORT_SYMBOL(usb_add_phy); const char *otg_state_string(enum usb_otg_state state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 02979306bf8..01022c891e2 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index d2a9a8e691b..a8be20878eb 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 38ab3f46346..0e739c81052 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -121,7 +121,7 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_set_transceiver(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,16 +172,16 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_transceiver(void); -extern void usb_put_transceiver(struct usb_phy *); +extern struct usb_phy *usb_get_phy(void); +extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_transceiver(void) +static inline struct usb_phy *usb_get_phy(void) { return NULL; } -static inline void usb_put_transceiver(struct usb_phy *x) +static inline void usb_put_phy(struct usb_phy *x) { } -- cgit v1.2.3-70-g09d2 From 662dca54ca67c92b7aa14b9a2ec54acacf33ce45 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:46 +0530 Subject: usb: otg: support for multiple transceivers by a single controller Add a linked list for keeping multiple PHY instances with different types so that we can have separate USB2 and USB3 PHYs on one single board. _get_phy_ has been changed so that the controller gets the transceiver by type. _remove_phy_ has been added to let the phy be removed from the phy list. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 2 +- drivers/power/isp1704_charger.c | 2 +- drivers/power/pda_power.c | 2 +- drivers/power/twl4030_charger.c | 2 +- drivers/usb/chipidea/udc.c | 2 +- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 2 +- drivers/usb/gadget/pxa25x_udc.c | 2 +- drivers/usb/gadget/pxa27x_udc.c | 2 +- drivers/usb/gadget/s3c-hsudc.c | 2 +- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mv.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ohci-omap.c | 2 +- drivers/usb/musb/am35x.c | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/omap2430.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/musb/ux500.c | 2 +- drivers/usb/otg/ab8500-usb.c | 4 +- drivers/usb/otg/fsl_otg.c | 6 +-- drivers/usb/otg/gpio_vbus.c | 4 +- drivers/usb/otg/isp1301_omap.c | 4 +- drivers/usb/otg/msm_otg.c | 4 +- drivers/usb/otg/mv_otg.c | 6 +-- drivers/usb/otg/nop-usb-xceiv.c | 4 +- drivers/usb/otg/otg.c | 96 ++++++++++++++++++++++++++++++++++----- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 29 ++++++++++-- 35 files changed, 152 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index cf5ffc4d104..6bd6f1c4196 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_phy(); + di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 50773ae6f72..090e5f9e72c 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_phy(); + isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!isp->phy) goto fail0; diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index e0f206b0775..7602d49e4d8 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_phy(); + transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fcddd115cc0..13f9db2e853 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(); + bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4468f2c2ddd..a06d28b119f 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index d7038509b95..0808820ba49 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_phy(); + udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 5d779955d5a..75ff41a5c95 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 74b9bb8099e..cf8bf26f12e 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_phy(); + xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (xceiv) type = xceiv->label; else if (config->otg) { diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index a658e446cab..cc0b1e63dca 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_phy(); + dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index b982304a49c..8f744aab962 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 9ad33395f56..22326f27446 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_phy(); + hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 0e8976a0ed5..ba290589d85 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_phy(); + ehci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 7badd5db398..c7615fb93db 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_phy(); + phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 24f838fe25a..ef7aa0df40a 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_phy(); + ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ee17d19b1b8..14df2f5cf6a 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_phy(); + tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index c2c1f55889a..92a77dfd193 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,7 +211,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_phy(); + ohci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index a75989bbb3d..4a8cbf0e8d5 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 522a4a263df..452940986d6 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 61868d604b2..d731c80c4fe 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto fail; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 441f776366f..582268de3fa 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto unregister; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 716c113608f..92603e498e6 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e16dbbf7f30..e279cf32772 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index a004736186f..8ddf3d5f7cd 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 53006b113b1..46cf80a8cac 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 672e28c8143..ae8ad561f08 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_add_phy(&ab->phy); + err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_add_phy(NULL); + usb_remove_phy(&ab->phy); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 73561edd81d..23c798cb2d7 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_add_phy(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_phy(); + struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_add_phy(NULL); + usb_remove_phy(&fsl_otg_dev->phy); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 9b3c264cdb5..a67ffe22179 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_add_phy(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_add_phy(NULL); + usb_remove_phy(&gpio_vbus->phy); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index b74df3fec56..75cea4ab098 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_add_phy(&isp->phy); + status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_add_phy(NULL); + usb_remove_phy(&the_transceiver->phy); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index dd606c01003..9f5fc906041 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,7 +1555,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_add_phy(&motg->phy); + ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); if (ret) { dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_add_phy(NULL); + usb_remove_phy(phy); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 18e90fe1fbd..3f124e8f579 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_add_phy(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 33000dae720..803f958f413 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_add_phy(&nop->phy); + err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_add_phy(NULL); + usb_remove_phy(&nop->phy); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 300a995cfdb..a23065820ea 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -11,14 +11,32 @@ #include #include +#include #include #include -static struct usb_phy *phy; +static LIST_HEAD(phy_list); +static DEFINE_SPINLOCK(phy_lock); + +static struct usb_phy *__usb_find_phy(struct list_head *list, + enum usb_phy_type type) +{ + struct usb_phy *phy = NULL; + + list_for_each_entry(phy, list, head) { + if (phy->type != type) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} /** - * usb_get_phy - find the (single) USB PHY + * usb_get_phy - find the USB PHY + * @type - the type of the phy the controller requires * * Returns the phy driver, after getting a refcount to it; or * null if there is no such phy. The caller is responsible for @@ -26,16 +44,30 @@ static struct usb_phy *phy; * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_phy(void) +struct usb_phy *usb_get_phy(enum usb_phy_type type) { - if (phy) - get_device(phy->dev); + struct usb_phy *phy = NULL; + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + + phy = __usb_find_phy(&phy_list, type); + if (IS_ERR(phy)) { + pr_err("unable to find transceiver of type %s\n", + usb_phy_type_string(type)); + return phy; + } + + get_device(phy->dev); + + spin_unlock_irqrestore(&phy_lock, flags); + return phy; } EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_phy - release the (single) USB PHY + * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() * * Releases a refcount the caller received from usb_get_phy(). @@ -50,22 +82,62 @@ void usb_put_phy(struct usb_phy *x) EXPORT_SYMBOL(usb_put_phy); /** - * usb_add_phy - declare the (single) USB PHY + * usb_add_phy - declare the USB PHY * @x: the USB phy to be used; or NULL + * @type - the type of this PHY * * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_add_phy(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) { - if (phy && x) - return -EBUSY; - phy = x; - return 0; + int ret = 0; + unsigned long flags; + struct usb_phy *phy; + + if (x && x->type != USB_PHY_TYPE_UNDEFINED) { + dev_err(x->dev, "not accepting initialized PHY %s\n", x->label); + return -EINVAL; + } + + spin_lock_irqsave(&phy_lock, flags); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->type == type) { + ret = -EBUSY; + dev_err(x->dev, "transceiver type %s already exists\n", + usb_phy_type_string(type)); + goto out; + } + } + + x->type = type; + list_add_tail(&x->head, &phy_list); + +out: + spin_unlock_irqrestore(&phy_lock, flags); + return ret; } EXPORT_SYMBOL(usb_add_phy); +/** + * usb_remove_phy - remove the OTG PHY + * @x: the USB OTG PHY to be removed; + * + * This reverts the effects of usb_add_phy + */ +void usb_remove_phy(struct usb_phy *x) +{ + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + if (x) + list_del(&x->head); + spin_unlock_irqrestore(&phy_lock, flags); +} +EXPORT_SYMBOL(usb_remove_phy); + const char *otg_state_string(enum usb_otg_state state) { switch (state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 01022c891e2..25a09fabbe3 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index a8be20878eb..dbee00aea75 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 0e739c81052..1def65fb57d 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -43,6 +43,13 @@ enum usb_phy_events { USB_EVENT_ENUMERATED, /* gadget driver enumerated */ }; +/* associate a type with PHY */ +enum usb_phy_type { + USB_PHY_TYPE_UNDEFINED, + USB_PHY_TYPE_USB2, + USB_PHY_TYPE_USB3, +}; + struct usb_phy; /* for transceivers connected thru an ULPI interface, the user must @@ -89,6 +96,7 @@ struct usb_phy { const char *label; unsigned int flags; + enum usb_phy_type type; enum usb_otg_state state; enum usb_phy_events last_event; @@ -105,6 +113,9 @@ struct usb_phy { u16 port_status; u16 port_change; + /* to support controllers that have multiple transceivers */ + struct list_head head; + /* initialize/shutdown the OTG controller */ int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); @@ -121,7 +132,8 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_add_phy(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); +extern void usb_remove_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,11 +184,11 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_phy(void); +extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_phy(void) +static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { return NULL; } @@ -276,4 +288,15 @@ usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); +static inline const char *usb_phy_type_string(enum usb_phy_type type) +{ + switch (type) { + case USB_PHY_TYPE_USB2: + return "USB2 PHY"; + case USB_PHY_TYPE_USB3: + return "USB3 PHY"; + default: + return "UNKNOWN PHY TYPE"; + } +} #endif /* __LINUX_USB_OTG_H */ -- cgit v1.2.3-70-g09d2 From 410219dcd2ba8d8b4bcfa9c232f35bf505bc021a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:47 +0530 Subject: usb: otg: utils: devres: Add API's to associate a device with the phy Used devres API's to associate the phy with a device so that on driver detach, release function is invoked on the devres data(usb_phy) and devres data(usb_phy) is released. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/otg/otg.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/otg.h | 13 +++++++++++ 2 files changed, 75 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index a23065820ea..0fa4d8c1b1e 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -34,6 +35,48 @@ static struct usb_phy *__usb_find_phy(struct list_head *list, return ERR_PTR(-ENODEV); } +static void devm_usb_phy_release(struct device *dev, void *res) +{ + struct usb_phy *phy = *(struct usb_phy **)res; + + usb_put_phy(phy); +} + +static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) +{ + return res == match_data; +} + +/** + * devm_usb_get_phy - find the USB PHY + * @dev - device that requests this phy + * @type - the type of the phy the controller requires + * + * Gets the phy using usb_get_phy(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) +{ + struct usb_phy **ptr, *phy; + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + phy = usb_get_phy(type); + if (phy) { + *ptr = phy; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy); + /** * usb_get_phy - find the USB PHY * @type - the type of the phy the controller requires @@ -66,6 +109,25 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type) } EXPORT_SYMBOL(usb_get_phy); +/** + * devm_usb_put_phy - release the USB PHY + * @dev - device that wants to release this phy + * @phy - the phy returned by devm_usb_get_phy() + * + * destroys the devres associated with this phy and invokes usb_put_phy + * to release the phy. + * + * For use by USB host and peripheral drivers. + */ +void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) +{ + int r; + + r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy); + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); +} +EXPORT_SYMBOL(devm_usb_put_phy); + /** * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 1def65fb57d..0cb2ec2e50c 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -185,7 +185,10 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS extern struct usb_phy *usb_get_phy(enum usb_phy_type type); +extern struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); +extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern const char *otg_state_string(enum usb_otg_state state); #else static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) @@ -193,10 +196,20 @@ static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) return NULL; } +static inline struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type) +{ + return NULL; +} + static inline void usb_put_phy(struct usb_phy *x) { } +static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x) +{ +} + static inline const char *otg_state_string(enum usb_otg_state state) { return NULL; -- cgit v1.2.3-70-g09d2 From 1e5acb8d6113a0f159257845e153d5b870ca618a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:40:51 +0530 Subject: usb: musb: move work_struct(otg_notifier_work) from core to omap glue Commit 712d8e(fixes pm_runtime calls while atomic by using a work queue. musb pm_runtime_get_sync call happens in interrupt context on cable attach case. That can result in re-enabling the interrupts and cause side affect. To avoid this deferred processing is used) While the issue and the work queue implementation is specific to omap (omap2430.c), the work_struct is defined as a member of struct musb (musb_core.h). Hence moved the work_struct from musb_core to omap glue. Cc: Vikram Pandita Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.h | 2 -- drivers/usb/musb/omap2430.c | 24 +++++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index f4a40f001c8..dbcdeea30f0 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -327,7 +327,6 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; - struct work_struct otg_notifier_work; u16 hwvers; /* this hub status bit is reserved by USB 2.0 and not seen by usbcore */ @@ -373,7 +372,6 @@ struct musb { u16 int_tx; struct usb_phy *xceiv; - u8 xceiv_event; int nIrq; unsigned irq_wake:1; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e279cf32772..f40c8053a29 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -41,6 +41,8 @@ struct omap2430_glue { struct device *dev; struct platform_device *musb; + u8 xceiv_event; + struct work_struct omap_musb_mailbox_work; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) @@ -226,22 +228,26 @@ static inline void omap2430_low_level_init(struct musb *musb) static int musb_otg_notifications(struct notifier_block *nb, unsigned long event, void *unused) { - struct musb *musb = container_of(nb, struct musb, nb); + struct musb *musb = container_of(nb, struct musb, nb); + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - musb->xceiv_event = event; - schedule_work(&musb->otg_notifier_work); + glue->xceiv_event = event; + schedule_work(&glue->omap_musb_mailbox_work); return NOTIFY_OK; } -static void musb_otg_notifier_work(struct work_struct *data_notifier_work) +static void omap_musb_mailbox_work(struct work_struct *data_notifier_work) { - struct musb *musb = container_of(data_notifier_work, struct musb, otg_notifier_work); + struct omap2430_glue *glue = container_of(data_notifier_work, + struct omap2430_glue, omap_musb_mailbox_work); + struct musb *musb = glue_to_musb(glue); struct device *dev = musb->controller; struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; - switch (musb->xceiv_event) { + switch (glue->xceiv_event) { case USB_EVENT_ID: dev_dbg(musb->controller, "ID GND\n"); @@ -298,8 +304,6 @@ static int omap2430_musb_init(struct musb *musb) return -ENODEV; } - INIT_WORK(&musb->otg_notifier_work, musb_otg_notifier_work); - status = pm_runtime_get_sync(dev); if (status < 0) { dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status); @@ -388,7 +392,6 @@ static void omap2430_musb_disable(struct musb *musb) static int omap2430_musb_exit(struct musb *musb) { del_timer_sync(&musb_idle_timer); - cancel_work_sync(&musb->otg_notifier_work); omap2430_low_level_exit(musb); usb_put_phy(musb->xceiv); @@ -441,6 +444,8 @@ static int __devinit omap2430_probe(struct platform_device *pdev) platform_set_drvdata(pdev, glue); + INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work); + ret = platform_device_add_resources(musb, pdev->resource, pdev->num_resources); if (ret) { @@ -478,6 +483,7 @@ static int __devexit omap2430_remove(struct platform_device *pdev) { struct omap2430_glue *glue = platform_get_drvdata(pdev); + cancel_work_sync(&glue->omap_musb_mailbox_work); platform_device_del(glue->musb); platform_device_put(glue->musb); kfree(glue); -- cgit v1.2.3-70-g09d2 From c9721438c009adf8e81d376839ed037c53b9b8d9 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:40:52 +0530 Subject: usb: musb: twl: use mailbox API to send VBUS or ID events The atomic notifier from twl4030/twl6030 to notifiy VBUS and ID events, is replaced by a direct call to omap musb blue. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/musb/omap2430.c | 94 +++++++++++++++++++++++++++---------------- drivers/usb/otg/twl4030-usb.c | 46 ++++++++++----------- drivers/usb/otg/twl6030-usb.c | 47 ++++++++++------------ include/linux/usb/musb-omap.h | 30 ++++++++++++++ 4 files changed, 133 insertions(+), 84 deletions(-) create mode 100644 include/linux/usb/musb-omap.h (limited to 'drivers') diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index f40c8053a29..063687085d1 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "musb_core.h" #include "omap2430.h" @@ -41,11 +42,13 @@ struct omap2430_glue { struct device *dev; struct platform_device *musb; - u8 xceiv_event; + enum omap_musb_vbus_id_status status; struct work_struct omap_musb_mailbox_work; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) +struct omap2430_glue *_glue; + static struct timer_list musb_idle_timer; static void musb_do_idle(unsigned long _musb) @@ -225,54 +228,58 @@ static inline void omap2430_low_level_init(struct musb *musb) musb_writel(musb->mregs, OTG_FORCESTDBY, l); } -static int musb_otg_notifications(struct notifier_block *nb, - unsigned long event, void *unused) +void omap_musb_mailbox(enum omap_musb_vbus_id_status status) { - struct musb *musb = container_of(nb, struct musb, nb); - struct device *dev = musb->controller; - struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + struct omap2430_glue *glue = _glue; + struct musb *musb = glue_to_musb(glue); - glue->xceiv_event = event; - schedule_work(&glue->omap_musb_mailbox_work); + glue->status = status; + if (!musb) { + dev_err(glue->dev, "musb core is not yet ready\n"); + return; + } - return NOTIFY_OK; + schedule_work(&glue->omap_musb_mailbox_work); } +EXPORT_SYMBOL_GPL(omap_musb_mailbox); -static void omap_musb_mailbox_work(struct work_struct *data_notifier_work) +static void omap_musb_set_mailbox(struct omap2430_glue *glue) { - struct omap2430_glue *glue = container_of(data_notifier_work, - struct omap2430_glue, omap_musb_mailbox_work); struct musb *musb = glue_to_musb(glue); struct device *dev = musb->controller; struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; - switch (glue->xceiv_event) { - case USB_EVENT_ID: - dev_dbg(musb->controller, "ID GND\n"); + switch (glue->status) { + case OMAP_MUSB_ID_GROUND: + dev_dbg(dev, "ID GND\n"); + musb->xceiv->last_event = USB_EVENT_ID; if (!is_otg_enabled(musb) || musb->gadget_driver) { - pm_runtime_get_sync(musb->controller); + pm_runtime_get_sync(dev); usb_phy_init(musb->xceiv); omap2430_musb_set_vbus(musb, 1); } break; - case USB_EVENT_VBUS: - dev_dbg(musb->controller, "VBUS Connect\n"); + case OMAP_MUSB_VBUS_VALID: + dev_dbg(dev, "VBUS Connect\n"); + musb->xceiv->last_event = USB_EVENT_VBUS; if (musb->gadget_driver) - pm_runtime_get_sync(musb->controller); + pm_runtime_get_sync(dev); usb_phy_init(musb->xceiv); break; - case USB_EVENT_NONE: - dev_dbg(musb->controller, "VBUS Disconnect\n"); + case OMAP_MUSB_ID_FLOAT: + case OMAP_MUSB_VBUS_OFF: + dev_dbg(dev, "VBUS Disconnect\n"); + musb->xceiv->last_event = USB_EVENT_NONE; if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) if (musb->gadget_driver) { - pm_runtime_mark_last_busy(musb->controller); - pm_runtime_put_autosuspend(musb->controller); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } if (data->interface_type == MUSB_INTERFACE_UTMI) { @@ -282,15 +289,24 @@ static void omap_musb_mailbox_work(struct work_struct *data_notifier_work) usb_phy_shutdown(musb->xceiv); break; default: - dev_dbg(musb->controller, "ID float\n"); + dev_dbg(dev, "ID float\n"); } } + +static void omap_musb_mailbox_work(struct work_struct *mailbox_work) +{ + struct omap2430_glue *glue = container_of(mailbox_work, + struct omap2430_glue, omap_musb_mailbox_work); + omap_musb_set_mailbox(glue); +} + static int omap2430_musb_init(struct musb *musb) { u32 l; int status = 0; struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *plat = dev->platform_data; struct omap_musb_board_data *data = plat->board_data; @@ -330,14 +346,11 @@ static int omap2430_musb_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - musb->nb.notifier_call = musb_otg_notifications; - status = usb_register_notifier(musb->xceiv, &musb->nb); - - if (status) - dev_dbg(musb->controller, "notification register failed\n"); - setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); + if (glue->status != OMAP_MUSB_UNKNOWN) + omap_musb_set_mailbox(glue); + pm_runtime_put_noidle(musb->controller); return 0; @@ -350,12 +363,13 @@ static void omap2430_musb_enable(struct musb *musb) u8 devctl; unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; - switch (musb->xceiv->last_event) { + switch (glue->status) { - case USB_EVENT_ID: + case OMAP_MUSB_ID_GROUND: usb_phy_init(musb->xceiv); if (data->interface_type != MUSB_INTERFACE_UTMI) break; @@ -374,7 +388,7 @@ static void omap2430_musb_enable(struct musb *musb) } break; - case USB_EVENT_VBUS: + case OMAP_MUSB_VBUS_VALID: usb_phy_init(musb->xceiv); break; @@ -385,7 +399,10 @@ static void omap2430_musb_enable(struct musb *musb) static void omap2430_musb_disable(struct musb *musb) { - if (musb->xceiv->last_event) + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + + if (glue->status != OMAP_MUSB_UNKNOWN) usb_phy_shutdown(musb->xceiv); } @@ -439,11 +456,18 @@ static int __devinit omap2430_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->musb = musb; + glue->status = OMAP_MUSB_UNKNOWN; pdata->platform_ops = &omap2430_ops; platform_set_drvdata(pdev, glue); + /* + * REVISIT if we ever have two instances of the wrapper, we will be + * in big trouble + */ + _glue = glue; + INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work); ret = platform_device_add_resources(musb, pdev->resource, @@ -552,7 +576,7 @@ static int __init omap2430_init(void) { return platform_driver_register(&omap2430_driver); } -module_init(omap2430_init); +subsys_initcall(omap2430_init); static void __exit omap2430_exit(void) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 25a09fabbe3..a7b809e217e 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -33,11 +33,11 @@ #include #include #include +#include #include #include #include #include -#include #include /* Register defines */ @@ -159,7 +159,7 @@ struct twl4030_usb { enum twl4030_usb_mode usb_mode; int irq; - u8 linkstat; + enum omap_musb_vbus_id_status linkstat; bool vbus_supplied; u8 asleep; bool irq_enabled; @@ -246,10 +246,11 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ -static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl) +static enum omap_musb_vbus_id_status + twl4030_usb_linkstat(struct twl4030_usb *twl) { int status; - int linkstat = USB_EVENT_NONE; + enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; struct usb_otg *otg = twl->phy.otg; twl->vbus_supplied = false; @@ -273,24 +274,24 @@ static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl) twl->vbus_supplied = true; if (status & BIT(2)) - linkstat = USB_EVENT_ID; + linkstat = OMAP_MUSB_ID_GROUND; else - linkstat = USB_EVENT_VBUS; - } else - linkstat = USB_EVENT_NONE; + linkstat = OMAP_MUSB_VBUS_VALID; + } else { + if (twl->linkstat != OMAP_MUSB_UNKNOWN) + linkstat = OMAP_MUSB_VBUS_OFF; + } dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", status, status, linkstat); - twl->phy.last_event = linkstat; - /* REVISIT this assumes host and peripheral controllers * are registered, and that both are active... */ spin_lock_irq(&twl->lock); twl->linkstat = linkstat; - if (linkstat == USB_EVENT_ID) { + if (linkstat == OMAP_MUSB_ID_GROUND) { otg->default_a = true; twl->phy.state = OTG_STATE_A_IDLE; } else { @@ -501,10 +502,10 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; - int status; + enum omap_musb_vbus_id_status status; status = twl4030_usb_linkstat(twl); - if (status >= 0) { + if (status > 0) { /* FIXME add a set_power() method so that B-devices can * configure the charger appropriately. It's not always * correct to consume VBUS power, and how much current to @@ -516,13 +517,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - if (status == USB_EVENT_NONE) + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) twl4030_phy_suspend(twl, 0); else twl4030_phy_resume(twl); - atomic_notifier_call_chain(&twl->phy.notifier, status, - twl->phy.otg->gadget); + omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -531,11 +532,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) static void twl4030_usb_phy_init(struct twl4030_usb *twl) { - int status; + enum omap_musb_vbus_id_status status; status = twl4030_usb_linkstat(twl); - if (status >= 0) { - if (status == USB_EVENT_NONE) { + if (status > 0) { + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) { __twl4030_phy_power(twl, 0); twl->asleep = 1; } else { @@ -543,8 +545,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl) twl->asleep = 0; } - atomic_notifier_call_chain(&twl->phy.notifier, status, - twl->phy.otg->gadget); + omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); } @@ -613,6 +614,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) twl->usb_mode = pdata->usb_mode; twl->vbus_supplied = false; twl->asleep = 1; + twl->linkstat = OMAP_MUSB_UNKNOWN; twl->phy.dev = twl->dev; twl->phy.label = "twl4030"; @@ -639,8 +641,6 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); - /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. * diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index dbee00aea75..6c758836cfb 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -26,10 +26,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -100,7 +100,7 @@ struct twl6030_usb { int irq1; int irq2; - u8 linkstat; + enum omap_musb_vbus_id_status linkstat; u8 asleep; bool irq_enabled; bool vbus_enable; @@ -147,7 +147,7 @@ static int twl6030_phy_init(struct usb_phy *x) dev = twl->dev; pdata = dev->platform_data; - if (twl->linkstat == USB_EVENT_ID) + if (twl->linkstat == OMAP_MUSB_ID_GROUND) pdata->phy_power(twl->dev, 1, 1); else pdata->phy_power(twl->dev, 0, 1); @@ -235,13 +235,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev, spin_lock_irqsave(&twl->lock, flags); switch (twl->linkstat) { - case USB_EVENT_VBUS: + case OMAP_MUSB_VBUS_VALID: ret = snprintf(buf, PAGE_SIZE, "vbus\n"); break; - case USB_EVENT_ID: + case OMAP_MUSB_ID_GROUND: ret = snprintf(buf, PAGE_SIZE, "id\n"); break; - case USB_EVENT_NONE: + case OMAP_MUSB_VBUS_OFF: ret = snprintf(buf, PAGE_SIZE, "none\n"); break; default: @@ -257,7 +257,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; struct usb_otg *otg = twl->phy.otg; - int status; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 vbus_state, hw_state; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); @@ -268,22 +268,20 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) if (vbus_state & VBUS_DET) { regulator_enable(twl->usb3v3); twl->asleep = 1; - status = USB_EVENT_VBUS; + status = OMAP_MUSB_VBUS_VALID; otg->default_a = false; twl->phy.state = OTG_STATE_B_IDLE; twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, - status, otg->gadget); + omap_musb_mailbox(status); } else { - status = USB_EVENT_NONE; - twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, - status, otg->gadget); - if (twl->asleep) { - regulator_disable(twl->usb3v3); - twl->asleep = 0; + if (twl->linkstat != OMAP_MUSB_UNKNOWN) { + status = OMAP_MUSB_VBUS_OFF; + twl->linkstat = status; + omap_musb_mailbox(status); + if (twl->asleep) { + regulator_disable(twl->usb3v3); + twl->asleep = 0; + } } } } @@ -296,7 +294,7 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; struct usb_otg *otg = twl->phy.otg; - int status = USB_EVENT_NONE; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 hw_state; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); @@ -308,13 +306,11 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1); twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x10); - status = USB_EVENT_ID; + status = OMAP_MUSB_ID_GROUND; otg->default_a = true; twl->phy.state = OTG_STATE_A_IDLE; twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, status, - otg->gadget); + omap_musb_mailbox(status); } else { twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x10); @@ -419,6 +415,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->irq1 = platform_get_irq(pdev, 0); twl->irq2 = platform_get_irq(pdev, 1); twl->features = pdata->features; + twl->linkstat = OMAP_MUSB_UNKNOWN; twl->phy.dev = twl->dev; twl->phy.label = "twl6030"; @@ -449,8 +446,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); - INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); twl->irq_enabled = true; diff --git a/include/linux/usb/musb-omap.h b/include/linux/usb/musb-omap.h new file mode 100644 index 00000000000..7774c5986f0 --- /dev/null +++ b/include/linux/usb/musb-omap.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-2012 by Texas Instruments + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + */ + +#ifndef __MUSB_OMAP_H__ +#define __MUSB_OMAP_H__ + +enum omap_musb_vbus_id_status { + OMAP_MUSB_UNKNOWN = 0, + OMAP_MUSB_ID_GROUND, + OMAP_MUSB_ID_FLOAT, + OMAP_MUSB_VBUS_VALID, + OMAP_MUSB_VBUS_OFF, +}; + +#if (defined(CONFIG_USB_MUSB_OMAP2PLUS) || \ + defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE)) +void omap_musb_mailbox(enum omap_musb_vbus_id_status status); +#else +static inline void omap_musb_mailbox(enum omap_musb_vbus_id_status status) +{ +} +#endif + +#endif /* __MUSB_OMAP_H__ */ -- cgit v1.2.3-70-g09d2 From c83a8542b5e3c5b30825955a68b1cc8bd24b122a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:40:53 +0530 Subject: usb: musb: move otg specific initializations from twl to glue Moved otg specific state(OTG_STATE_B_IDLE, OTG_STATE_A_IDLE) initializations from twl to glue. These initializations are removed from twl4030 and twl6030 and moved to the mailbox API defined in glue. This is part of the cleanup in preparation to make use of usb2 phy driver. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/musb/omap2430.c | 5 +++++ drivers/usb/otg/twl4030-usb.c | 8 -------- drivers/usb/otg/twl6030-usb.c | 6 ------ 3 files changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 063687085d1..c4dc92bd7e8 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -249,11 +249,14 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) struct device *dev = musb->controller; struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; + struct usb_otg *otg = musb->xceiv->otg; switch (glue->status) { case OMAP_MUSB_ID_GROUND: dev_dbg(dev, "ID GND\n"); + otg->default_a = true; + musb->xceiv->state = OTG_STATE_A_IDLE; musb->xceiv->last_event = USB_EVENT_ID; if (!is_otg_enabled(musb) || musb->gadget_driver) { pm_runtime_get_sync(dev); @@ -265,6 +268,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) case OMAP_MUSB_VBUS_VALID: dev_dbg(dev, "VBUS Connect\n"); + otg->default_a = false; + musb->xceiv->state = OTG_STATE_B_IDLE; musb->xceiv->last_event = USB_EVENT_VBUS; if (musb->gadget_driver) pm_runtime_get_sync(dev); diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index a7b809e217e..4d0d98bc40c 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -251,7 +251,6 @@ static enum omap_musb_vbus_id_status { int status; enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; - struct usb_otg *otg = twl->phy.otg; twl->vbus_supplied = false; @@ -291,13 +290,6 @@ static enum omap_musb_vbus_id_status spin_lock_irq(&twl->lock); twl->linkstat = linkstat; - if (linkstat == OMAP_MUSB_ID_GROUND) { - otg->default_a = true; - twl->phy.state = OTG_STATE_A_IDLE; - } else { - otg->default_a = false; - twl->phy.state = OTG_STATE_B_IDLE; - } spin_unlock_irq(&twl->lock); return linkstat; diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 6c758836cfb..66cfea73555 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -256,7 +256,6 @@ static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; - struct usb_otg *otg = twl->phy.otg; enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 vbus_state, hw_state; @@ -269,8 +268,6 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) regulator_enable(twl->usb3v3); twl->asleep = 1; status = OMAP_MUSB_VBUS_VALID; - otg->default_a = false; - twl->phy.state = OTG_STATE_B_IDLE; twl->linkstat = status; omap_musb_mailbox(status); } else { @@ -293,7 +290,6 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; - struct usb_otg *otg = twl->phy.otg; enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 hw_state; @@ -307,8 +303,6 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x10); status = OMAP_MUSB_ID_GROUND; - otg->default_a = true; - twl->phy.state = OTG_STATE_A_IDLE; twl->linkstat = status; omap_musb_mailbox(status); } else { -- cgit v1.2.3-70-g09d2 From b1183c242a60764afbdfaf39396405b7afa1106c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:40:54 +0530 Subject: usb: musb: omap: use devres API to allocate resources used devres API while allocating memory resource and while getting usb phy so that these resources are released automatically on driver detach. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/musb/omap2430.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c4dc92bd7e8..2813490ba63 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -319,7 +319,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -416,7 +416,6 @@ static int omap2430_musb_exit(struct musb *musb) del_timer_sync(&musb_idle_timer); omap2430_low_level_exit(musb); - usb_put_phy(musb->xceiv); return 0; } @@ -443,7 +442,7 @@ static int __devinit omap2430_probe(struct platform_device *pdev) struct omap2430_glue *glue; int ret = -ENOMEM; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; @@ -452,7 +451,7 @@ static int __devinit omap2430_probe(struct platform_device *pdev) musb = platform_device_alloc("musb-hdrc", -1); if (!musb) { dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err1; + goto err0; } musb->dev.parent = &pdev->dev; @@ -479,13 +478,13 @@ static int __devinit omap2430_probe(struct platform_device *pdev) pdev->num_resources); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err2; + goto err1; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err2; + goto err1; } pm_runtime_enable(&pdev->dev); @@ -493,16 +492,13 @@ static int __devinit omap2430_probe(struct platform_device *pdev) ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err2; + goto err1; } return 0; -err2: - platform_device_put(musb); - err1: - kfree(glue); + platform_device_put(musb); err0: return ret; @@ -515,7 +511,6 @@ static int __devexit omap2430_remove(struct platform_device *pdev) cancel_work_sync(&glue->omap_musb_mailbox_work); platform_device_del(glue->musb); platform_device_put(glue->musb); - kfree(glue); return 0; } -- cgit v1.2.3-70-g09d2 From b8a3efa3a363720687d21228d6b23b988a223bbb Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:40:55 +0530 Subject: usb: otg: twl: use devres API to allocate resources used devres API while allocating memory resource in twl4030 and twl6030 so that these resources are released automatically on driver detach. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/otg/twl4030-usb.c | 15 +++------------ drivers/usb/otg/twl6030-usb.c | 16 +++------------- 2 files changed, 6 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 4d0d98bc40c..523cad5bfea 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -591,15 +591,13 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) return -EINVAL; } - twl = kzalloc(sizeof *twl, GFP_KERNEL); + twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL); if (!twl) return -ENOMEM; - otg = kzalloc(sizeof *otg, GFP_KERNEL); - if (!otg) { - kfree(twl); + otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL); + if (!otg) return -ENOMEM; - } twl->dev = &pdev->dev; twl->irq = platform_get_irq(pdev, 0); @@ -623,8 +621,6 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) err = twl4030_usb_ldo_init(twl); if (err) { dev_err(&pdev->dev, "ldo init failed\n"); - kfree(otg); - kfree(twl); return err; } usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); @@ -648,8 +644,6 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) if (status < 0) { dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq, status); - kfree(otg); - kfree(twl); return status; } @@ -693,9 +687,6 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev) regulator_put(twl->usb1v8); regulator_put(twl->usb3v1); - kfree(twl->phy.otg); - kfree(twl); - return 0; } diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 66cfea73555..600c27a42ff 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -395,15 +395,13 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; pdata = dev->platform_data; - twl = kzalloc(sizeof *twl, GFP_KERNEL); + twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); if (!twl) return -ENOMEM; - otg = kzalloc(sizeof *otg, GFP_KERNEL); - if (!otg) { - kfree(twl); + otg = devm_kzalloc(dev, sizeof *otg, GFP_KERNEL); + if (!otg) return -ENOMEM; - } twl->dev = &pdev->dev; twl->irq1 = platform_get_irq(pdev, 0); @@ -430,8 +428,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) err = twl6030_usb_ldo_init(twl); if (err) { dev_err(&pdev->dev, "ldo init failed\n"); - kfree(otg); - kfree(twl); return err; } usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); @@ -450,8 +446,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq1, status); device_remove_file(twl->dev, &dev_attr_vbus); - kfree(otg); - kfree(twl); return status; } @@ -463,8 +457,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->irq2, status); free_irq(twl->irq1, twl); device_remove_file(twl->dev, &dev_attr_vbus); - kfree(otg); - kfree(twl); return status; } @@ -495,8 +487,6 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev) pdata->phy_exit(twl->dev); device_remove_file(twl->dev, &dev_attr_vbus); cancel_work_sync(&twl->set_vbus_work); - kfree(twl->phy.otg); - kfree(twl); return 0; } -- cgit v1.2.3-70-g09d2 From 31bde1ceaa873bcaecd49e829bfabceacc4c512d Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Sun, 24 Jun 2012 21:11:22 -0700 Subject: usb: gadget: Fix g_ether interface link status A "usb0" interface that has never been connected to a host has an unknown operstate, and therefore the IFF_RUNNING flag is (incorrectly) asserted when queried by ifconfig, ifplugd, etc. This is a result of calling netif_carrier_off() too early in the probe function; it should be called after register_netdev(). Similar problems have been fixed in many other drivers, e.g.: e826eafa6 (bonding: Call netif_carrier_off after register_netdevice) 0d672e9f8 (drivers/net: Call netif_carrier_off at the end of the probe) 6a3c869a6 (cxgb4: fix reported state of interfaces without link) Fix is to move netif_carrier_off() to the end of the function. Cc: stable@vger.kernel.org Signed-off-by: Kevin Cernekee Signed-off-by: Felipe Balbi --- drivers/usb/gadget/u_ether.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 47cf48b51c9..5b46f022d0f 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -798,12 +798,6 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], SET_ETHTOOL_OPS(net, &ops); - /* two kinds of host-initiated state changes: - * - iff DATA transfer is active, carrier is "on" - * - tx queueing enabled if open *and* carrier is "on" - */ - netif_carrier_off(net); - dev->gadget = g; SET_NETDEV_DEV(net, &g->dev); SET_NETDEV_DEVTYPE(net, &gadget_type); @@ -817,6 +811,12 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], INFO(dev, "HOST MAC %pM\n", dev->host_mac); the_dev = dev; + + /* two kinds of host-initiated state changes: + * - iff DATA transfer is active, carrier is "on" + * - tx queueing enabled if open *and* carrier is "on" + */ + netif_carrier_off(net); } return status; -- cgit v1.2.3-70-g09d2 From 9be2395186b3504aa5e337bd3284afb4966caae7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 25 Jun 2012 17:09:28 +0300 Subject: usb: dwc3: gadget: remove duplicated code from __dwc3_gadget_ep_set_halt whenever we want to stall ep0, we always call dwc3_ep0_stall_and_restart() which makes sure to send ep0state properly rendering the code in __dwc3_gadget_ep_set_halt() duplicated. Reported-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1b5fbdd859c..d98549a7b8b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1209,15 +1209,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) memset(¶ms, 0x00, sizeof(params)); if (value) { - if (dep->number == 0 || dep->number == 1) { - /* - * Whenever EP0 is stalled, we will restart - * the state machine, thus moving back to - * Setup Phase - */ - dwc->ep0state = EP0_SETUP_PHASE; - } - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_SETSTALL, ¶ms); if (ret) -- cgit v1.2.3-70-g09d2 From 5c81ababecd7ddae08ca944a65d8030cd393013c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 25 Jun 2012 19:30:49 +0300 Subject: usb: dwc3: ep0: prevent starting transfers twice on ep0 In case we try to start an invalid test mode, we will call dwc3_ep0_stall_and_restart() but we will also call dwc3_ep0_out_start() which will start a second transfer on ep0. Let's prevent any problems by returning early in the error case. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 9a8f26cef0a..8244eb53082 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -744,6 +744,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, dev_dbg(dwc->dev, "Invalid Test #%d\n", dwc->test_mode_nr); dwc3_ep0_stall_and_restart(dwc); + return; } } -- cgit v1.2.3-70-g09d2 From c621a81edecdee85da32c566c21836332c764fda Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:48 +0200 Subject: Revert "usb/uas: make sure data urb is gone if we receive status before that" This reverts commit e4d8318a85779b25b880187b1b1c44e797bd7d4b. This patch makes uas.c call usb_unlink_urb on data urbs. The data urbs get freed in the completion callback. This is illegal according to the usb_unlink_urb documentation. This patch also makes the code expect the data completion callback being called before the status completion callback. This isn't guaranteed to be the case, even though the actual data transfer should be finished by the time the status is received. Background: The ehci irq handler for example only know that there are finished transfers, it then has go check the QHs & TDs to see which transfers did actually finish. It has no way to figure in which order the transfers did complete. The xhci driver can call the callbacks in completion order thanks to the event queue. This does nicely explain why the driver is solid on a (usb2) xhci port whereas it goes crazy on ehci in my testing. Signed-off-by: Gerd Hoffmann Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 90 ++++++++--------------------------------------- 1 file changed, 15 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 8ec8a6e66f5..f98ba40352c 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -58,9 +58,6 @@ enum { SUBMIT_DATA_OUT_URB = (1 << 5), ALLOC_CMD_URB = (1 << 6), SUBMIT_CMD_URB = (1 << 7), - COMPLETED_DATA_IN = (1 << 8), - COMPLETED_DATA_OUT = (1 << 9), - DATA_COMPLETES_CMD = (1 << 10), }; /* Overrides scsi_pointer */ @@ -114,7 +111,6 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) { struct sense_iu *sense_iu = urb->transfer_buffer; struct scsi_device *sdev = cmnd->device; - struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; if (urb->actual_length > 16) { unsigned len = be16_to_cpup(&sense_iu->len); @@ -132,15 +128,13 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; - if (!(cmdinfo->state & DATA_COMPLETES_CMD)) - cmnd->scsi_done(cmnd); + cmnd->scsi_done(cmnd); } static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) { struct sense_iu_old *sense_iu = urb->transfer_buffer; struct scsi_device *sdev = cmnd->device; - struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; if (urb->actual_length > 8) { unsigned len = be16_to_cpup(&sense_iu->len) - 2; @@ -158,8 +152,7 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; - if (!(cmdinfo->state & DATA_COMPLETES_CMD)) - cmnd->scsi_done(cmnd); + cmnd->scsi_done(cmnd); } static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, @@ -184,7 +177,6 @@ static void uas_stat_cmplt(struct urb *urb) struct Scsi_Host *shost = urb->context; struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct scsi_cmnd *cmnd; - struct uas_cmd_info *cmdinfo; u16 tag; int ret; @@ -210,32 +202,12 @@ static void uas_stat_cmplt(struct urb *urb) dev_err(&urb->dev->dev, "failed submit status urb\n"); return; } - cmdinfo = (void *)&cmnd->SCp; switch (iu->iu_id) { case IU_ID_STATUS: if (devinfo->cmnd == cmnd) devinfo->cmnd = NULL; - if (!(cmdinfo->state & COMPLETED_DATA_IN) && - cmdinfo->data_in_urb) { - if (devinfo->use_streams) { - cmdinfo->state |= DATA_COMPLETES_CMD; - usb_unlink_urb(cmdinfo->data_in_urb); - } else { - usb_free_urb(cmdinfo->data_in_urb); - } - } - if (!(cmdinfo->state & COMPLETED_DATA_OUT) && - cmdinfo->data_out_urb) { - if (devinfo->use_streams) { - cmdinfo->state |= DATA_COMPLETES_CMD; - usb_unlink_urb(cmdinfo->data_in_urb); - } else { - usb_free_urb(cmdinfo->data_out_urb); - } - } - if (urb->actual_length < 16) devinfo->uas_sense_old = 1; if (devinfo->uas_sense_old) @@ -264,59 +236,27 @@ static void uas_stat_cmplt(struct urb *urb) dev_err(&urb->dev->dev, "failed submit status urb\n"); } -static void uas_data_out_cmplt(struct urb *urb) -{ - struct scsi_cmnd *cmnd = urb->context; - struct scsi_data_buffer *sdb = scsi_out(cmnd); - struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; - - cmdinfo->state |= COMPLETED_DATA_OUT; - - sdb->resid = sdb->length - urb->actual_length; - usb_free_urb(urb); - - if (cmdinfo->state & DATA_COMPLETES_CMD) - cmnd->scsi_done(cmnd); -} - -static void uas_data_in_cmplt(struct urb *urb) +static void uas_data_cmplt(struct urb *urb) { - struct scsi_cmnd *cmnd = urb->context; - struct scsi_data_buffer *sdb = scsi_in(cmnd); - struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; - - cmdinfo->state |= COMPLETED_DATA_IN; - + struct scsi_data_buffer *sdb = urb->context; sdb->resid = sdb->length - urb->actual_length; usb_free_urb(urb); - - if (cmdinfo->state & DATA_COMPLETES_CMD) - cmnd->scsi_done(cmnd); } static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, - unsigned int pipe, struct scsi_cmnd *cmnd, - enum dma_data_direction dir) + unsigned int pipe, u16 stream_id, + struct scsi_data_buffer *sdb, + enum dma_data_direction dir) { - struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct usb_device *udev = devinfo->udev; struct urb *urb = usb_alloc_urb(0, gfp); - struct scsi_data_buffer *sdb; - usb_complete_t complete_fn; - u16 stream_id = cmdinfo->stream; if (!urb) goto out; - if (dir == DMA_FROM_DEVICE) { - sdb = scsi_in(cmnd); - complete_fn = uas_data_in_cmplt; - } else { - sdb = scsi_out(cmnd); - complete_fn = uas_data_out_cmplt; - } - usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, - complete_fn, cmnd); - urb->stream_id = stream_id; + usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, + sdb); + if (devinfo->use_streams) + urb->stream_id = stream_id; urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; urb->sg = sdb->table.sgl; out: @@ -418,8 +358,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & ALLOC_DATA_IN_URB) { cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp, - devinfo->data_in_pipe, cmnd, - DMA_FROM_DEVICE); + devinfo->data_in_pipe, cmdinfo->stream, + scsi_in(cmnd), DMA_FROM_DEVICE); if (!cmdinfo->data_in_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_IN_URB; @@ -436,8 +376,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & ALLOC_DATA_OUT_URB) { cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp, - devinfo->data_out_pipe, cmnd, - DMA_TO_DEVICE); + devinfo->data_out_pipe, cmdinfo->stream, + scsi_out(cmnd), DMA_TO_DEVICE); if (!cmdinfo->data_out_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_OUT_URB; -- cgit v1.2.3-70-g09d2 From db32de11f75673c2a0f2651ae58b0f25b8c5b0eb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:49 +0200 Subject: Revert "usb/uas: one only one status URB/host on stream-less connection" This reverts commit ceb3f91fd53c9fbd7b292fc2754ba4efffeeeedb. IMO the real bug is assigning status urbs to scsi requests. First there is no such link in the non-stream case. Also there isn't nessesarely a scsi request in the first place, for example when submitting task management requests. This patch just papers over the real bug and introduces different status urb handling in the stream/non-stream case for no good reason. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 70 +++++++---------------------------------------- 1 file changed, 10 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index f98ba40352c..36279a46fd2 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -46,7 +46,6 @@ struct uas_dev_info { unsigned use_streams:1; unsigned uas_sense_old:1; struct scsi_cmnd *cmnd; - struct urb *status_urb; /* used only if stream support is available */ }; enum { @@ -65,7 +64,6 @@ struct uas_cmd_info { unsigned int state; unsigned int stream; struct urb *cmd_urb; - /* status_urb is used only if stream support isn't available */ struct urb *status_urb; struct urb *data_in_urb; struct urb *data_out_urb; @@ -129,6 +127,7 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) cmnd->result = sense_iu->status; cmnd->scsi_done(cmnd); + usb_free_urb(urb); } static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) @@ -153,6 +152,7 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) cmnd->result = sense_iu->status; cmnd->scsi_done(cmnd); + usb_free_urb(urb); } static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, @@ -161,7 +161,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; int err; - cmdinfo->state = direction; + cmdinfo->state = direction | SUBMIT_STATUS_URB; err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); if (err) { spin_lock(&uas_work_lock); @@ -178,12 +178,10 @@ static void uas_stat_cmplt(struct urb *urb) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct scsi_cmnd *cmnd; u16 tag; - int ret; if (urb->status) { dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status); - if (devinfo->use_streams) - usb_free_urb(urb); + usb_free_urb(urb); return; } @@ -193,13 +191,7 @@ static void uas_stat_cmplt(struct urb *urb) else cmnd = scsi_host_find_tag(shost, tag - 1); if (!cmnd) { - if (devinfo->use_streams) { - usb_free_urb(urb); - return; - } - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - dev_err(&urb->dev->dev, "failed submit status urb\n"); + usb_free_urb(urb); return; } @@ -225,15 +217,6 @@ static void uas_stat_cmplt(struct urb *urb) scmd_printk(KERN_ERR, cmnd, "Bogus IU (%d) received on status pipe\n", iu->iu_id); } - - if (devinfo->use_streams) { - usb_free_urb(urb); - return; - } - - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - dev_err(&urb->dev->dev, "failed submit status urb\n"); } static void uas_data_cmplt(struct urb *urb) @@ -264,7 +247,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, } static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, - struct Scsi_Host *shost, u16 stream_id) + struct scsi_cmnd *cmnd, u16 stream_id) { struct usb_device *udev = devinfo->udev; struct urb *urb = usb_alloc_urb(0, gfp); @@ -278,7 +261,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto free; usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu), - uas_stat_cmplt, shost); + uas_stat_cmplt, cmnd->device->host); urb->stream_id = stream_id; urb->transfer_flags |= URB_FREE_BUFFER; out: @@ -340,8 +323,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; if (cmdinfo->state & ALLOC_STATUS_URB) { - cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, - cmnd->device->host, cmdinfo->stream); + cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd, + cmdinfo->stream); if (!cmdinfo->status_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_STATUS_URB; @@ -450,8 +433,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, } if (!devinfo->use_streams) { - cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB | - ALLOC_STATUS_URB | SUBMIT_STATUS_URB); + cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB); cmdinfo->stream = 0; } @@ -662,29 +644,6 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) } } -static int uas_alloc_status_urb(struct uas_dev_info *devinfo, - struct Scsi_Host *shost) -{ - if (devinfo->use_streams) { - devinfo->status_urb = NULL; - return 0; - } - - devinfo->status_urb = uas_alloc_sense_urb(devinfo, GFP_KERNEL, - shost, 0); - if (!devinfo->status_urb) - goto err_s_urb; - - if (usb_submit_urb(devinfo->status_urb, GFP_KERNEL)) - goto err_submit_urb; - - return 0; -err_submit_urb: - usb_free_urb(devinfo->status_urb); -err_s_urb: - return -ENOMEM; -} - static void uas_free_streams(struct uas_dev_info *devinfo) { struct usb_device *udev = devinfo->udev; @@ -739,17 +698,10 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) shost->hostdata[0] = (unsigned long)devinfo; - result = uas_alloc_status_urb(devinfo, shost); - if (result) - goto err_alloc_status; - scsi_scan_host(shost); usb_set_intfdata(intf, shost); return result; -err_alloc_status: - scsi_remove_host(shost); - shost = NULL; deconfig_eps: uas_free_streams(devinfo); free: @@ -777,8 +729,6 @@ static void uas_disconnect(struct usb_interface *intf) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; scsi_remove_host(shost); - usb_kill_urb(devinfo->status_urb); - usb_free_urb(devinfo->status_urb); uas_free_streams(devinfo); kfree(devinfo); } -- cgit v1.2.3-70-g09d2 From e9bd7e1a2d34de3b0991c81080d56dc408110833 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:50 +0200 Subject: uas: fix sense urb handling Stop reusing sense urbs, just allocate a fresh one each time and free it when done. Stop storing a sense urb pointer in the scsi request, all you can do with it is misusing. For example requeuing the sense urb, then f*ck it up by picking the wrong one in case tagged requests don't finish in the same order you've submitted them. Also note that (not-yet supported) task management ops don't have a scsi request in the first place. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 49 +++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 36279a46fd2..b0127381969 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -49,7 +49,6 @@ struct uas_dev_info { }; enum { - ALLOC_STATUS_URB = (1 << 0), SUBMIT_STATUS_URB = (1 << 1), ALLOC_DATA_IN_URB = (1 << 2), SUBMIT_DATA_IN_URB = (1 << 3), @@ -64,7 +63,6 @@ struct uas_cmd_info { unsigned int state; unsigned int stream; struct urb *cmd_urb; - struct urb *status_urb; struct urb *data_in_urb; struct urb *data_out_urb; struct list_head list; @@ -127,7 +125,6 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) cmnd->result = sense_iu->status; cmnd->scsi_done(cmnd); - usb_free_urb(urb); } static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) @@ -152,7 +149,6 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) cmnd->result = sense_iu->status; cmnd->scsi_done(cmnd); - usb_free_urb(urb); } static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, @@ -217,6 +213,7 @@ static void uas_stat_cmplt(struct urb *urb) scmd_printk(KERN_ERR, cmnd, "Bogus IU (%d) received on status pipe\n", iu->iu_id); } + usb_free_urb(urb); } static void uas_data_cmplt(struct urb *urb) @@ -247,7 +244,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, } static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, - struct scsi_cmnd *cmnd, u16 stream_id) + struct Scsi_Host *shost, u16 stream_id) { struct usb_device *udev = devinfo->udev; struct urb *urb = usb_alloc_urb(0, gfp); @@ -261,7 +258,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto free; usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu), - uas_stat_cmplt, cmnd->device->host); + uas_stat_cmplt, shost); urb->stream_id = stream_id; urb->transfer_flags |= URB_FREE_BUFFER; out: @@ -317,24 +314,35 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, * daft to me. */ -static int uas_submit_urbs(struct scsi_cmnd *cmnd, - struct uas_dev_info *devinfo, gfp_t gfp) +static int uas_submit_sense_urb(struct Scsi_Host *shost, + gfp_t gfp, unsigned int stream) { - struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + struct urb *urb; - if (cmdinfo->state & ALLOC_STATUS_URB) { - cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd, - cmdinfo->stream); - if (!cmdinfo->status_urb) - return SCSI_MLQUEUE_DEVICE_BUSY; - cmdinfo->state &= ~ALLOC_STATUS_URB; + urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream); + if (!urb) + return SCSI_MLQUEUE_DEVICE_BUSY; + if (usb_submit_urb(urb, gfp)) { + shost_printk(KERN_INFO, shost, + "sense urb submission failure\n"); + usb_free_urb(urb); + return SCSI_MLQUEUE_DEVICE_BUSY; } + return 0; +} + +static int uas_submit_urbs(struct scsi_cmnd *cmnd, + struct uas_dev_info *devinfo, gfp_t gfp) +{ + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + int err; if (cmdinfo->state & SUBMIT_STATUS_URB) { - if (usb_submit_urb(cmdinfo->status_urb, gfp)) { - scmd_printk(KERN_INFO, cmnd, - "sense urb submission failure\n"); - return SCSI_MLQUEUE_DEVICE_BUSY; + err = uas_submit_sense_urb(cmnd->device->host, gfp, + cmdinfo->stream); + if (err) { + return err; } cmdinfo->state &= ~SUBMIT_STATUS_URB; } @@ -417,7 +425,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, cmnd->scsi_done = done; - cmdinfo->state = ALLOC_STATUS_URB | SUBMIT_STATUS_URB | + cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; switch (cmnd->sc_data_direction) { @@ -441,7 +449,6 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, if (err) { /* If we did nothing, give up now */ if (cmdinfo->state & SUBMIT_STATUS_URB) { - usb_free_urb(cmdinfo->status_urb); return SCSI_MLQUEUE_DEVICE_BUSY; } spin_lock(&uas_work_lock); -- cgit v1.2.3-70-g09d2 From b1d6769333496b05818fe6cec72ef7f7504ef9e4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:51 +0200 Subject: uas: keep track of command state, finish scsi cmd when really done. Set state bits after submitting data urbs & command urbs, so we know what is in flight. Clear data bits when the data urb is finished, clear command bit when we see the status urb for the command. Finish the scsi command after running both status and data completion handlers for the command. Add a cmd status logging function for debugging purposes. Hook it into the error handler, so we see in the log what status a command is in which the scsi layer wants cancel. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 86 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index b0127381969..b589b2e0e92 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -56,6 +56,10 @@ enum { SUBMIT_DATA_OUT_URB = (1 << 5), ALLOC_CMD_URB = (1 << 6), SUBMIT_CMD_URB = (1 << 7), + COMMAND_INFLIGHT = (1 << 8), + DATA_IN_URB_INFLIGHT = (1 << 9), + DATA_OUT_URB_INFLIGHT = (1 << 10), + COMMAND_COMPLETED = (1 << 11), }; /* Overrides scsi_pointer */ @@ -124,7 +128,6 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; - cmnd->scsi_done(cmnd); } static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) @@ -148,16 +151,51 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; +} + +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller) +{ + struct uas_cmd_info *ci = (void *)&cmnd->SCp; + + scmd_printk(KERN_INFO, cmnd, "%s %p tag %d, inflight:" + "%s%s%s%s%s%s%s%s%s%s%s\n", + caller, cmnd, cmnd->request->tag, + (ci->state & SUBMIT_STATUS_URB) ? " s-st" : "", + (ci->state & ALLOC_DATA_IN_URB) ? " a-in" : "", + (ci->state & SUBMIT_DATA_IN_URB) ? " s-in" : "", + (ci->state & ALLOC_DATA_OUT_URB) ? " a-out" : "", + (ci->state & SUBMIT_DATA_OUT_URB) ? " s-out" : "", + (ci->state & ALLOC_CMD_URB) ? " a-cmd" : "", + (ci->state & SUBMIT_CMD_URB) ? " s-cmd" : "", + (ci->state & COMMAND_INFLIGHT) ? " CMD" : "", + (ci->state & DATA_IN_URB_INFLIGHT) ? " IN" : "", + (ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT" : "", + (ci->state & COMMAND_COMPLETED) ? " done" : ""); +} + +static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) +{ + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + + if (cmdinfo->state & (COMMAND_INFLIGHT | + DATA_IN_URB_INFLIGHT | + DATA_OUT_URB_INFLIGHT)) + return -EBUSY; + BUG_ON(cmdinfo->state & COMMAND_COMPLETED); + cmdinfo->state |= COMMAND_COMPLETED; + usb_free_urb(cmdinfo->data_in_urb); + usb_free_urb(cmdinfo->data_out_urb); cmnd->scsi_done(cmnd); + return 0; } static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, - unsigned direction) + unsigned direction) { struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; int err; - cmdinfo->state = direction | SUBMIT_STATUS_URB; + cmdinfo->state |= direction | SUBMIT_STATUS_URB; err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); if (err) { spin_lock(&uas_work_lock); @@ -173,6 +211,7 @@ static void uas_stat_cmplt(struct urb *urb) struct Scsi_Host *shost = urb->context; struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct scsi_cmnd *cmnd; + struct uas_cmd_info *cmdinfo; u16 tag; if (urb->status) { @@ -190,6 +229,7 @@ static void uas_stat_cmplt(struct urb *urb) usb_free_urb(urb); return; } + cmdinfo = (void *)&cmnd->SCp; switch (iu->iu_id) { case IU_ID_STATUS: @@ -202,6 +242,8 @@ static void uas_stat_cmplt(struct urb *urb) uas_sense_old(urb, cmnd); else uas_sense(urb, cmnd); + cmdinfo->state &= ~COMMAND_INFLIGHT; + uas_try_complete(cmnd, __func__); break; case IU_ID_READ_READY: uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB); @@ -218,23 +260,36 @@ static void uas_stat_cmplt(struct urb *urb) static void uas_data_cmplt(struct urb *urb) { - struct scsi_data_buffer *sdb = urb->context; + struct scsi_cmnd *cmnd = urb->context; + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + struct scsi_data_buffer *sdb = NULL; + + if (cmdinfo->data_in_urb == urb) { + sdb = scsi_in(cmnd); + cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; + } else if (cmdinfo->data_out_urb == urb) { + sdb = scsi_out(cmnd); + cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; + } + BUG_ON(sdb == NULL); sdb->resid = sdb->length - urb->actual_length; - usb_free_urb(urb); + uas_try_complete(cmnd, __func__); } static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, - unsigned int pipe, u16 stream_id, - struct scsi_data_buffer *sdb, - enum dma_data_direction dir) + unsigned int pipe, u16 stream_id, + struct scsi_cmnd *cmnd, + enum dma_data_direction dir) { struct usb_device *udev = devinfo->udev; struct urb *urb = usb_alloc_urb(0, gfp); + struct scsi_data_buffer *sdb = (dir == DMA_FROM_DEVICE) + ? scsi_in(cmnd) : scsi_out(cmnd); if (!urb) goto out; - usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, - sdb); + usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, + uas_data_cmplt, cmnd); if (devinfo->use_streams) urb->stream_id = stream_id; urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; @@ -350,7 +405,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & ALLOC_DATA_IN_URB) { cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp, devinfo->data_in_pipe, cmdinfo->stream, - scsi_in(cmnd), DMA_FROM_DEVICE); + cmnd, DMA_FROM_DEVICE); if (!cmdinfo->data_in_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_IN_URB; @@ -363,12 +418,13 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_IN_URB; + cmdinfo->state |= DATA_IN_URB_INFLIGHT; } if (cmdinfo->state & ALLOC_DATA_OUT_URB) { cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp, devinfo->data_out_pipe, cmdinfo->stream, - scsi_out(cmnd), DMA_TO_DEVICE); + cmnd, DMA_TO_DEVICE); if (!cmdinfo->data_out_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_OUT_URB; @@ -381,6 +437,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; + cmdinfo->state |= DATA_OUT_URB_INFLIGHT; } if (cmdinfo->state & ALLOC_CMD_URB) { @@ -398,6 +455,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, return SCSI_MLQUEUE_DEVICE_BUSY; } cmdinfo->state &= ~SUBMIT_CMD_URB; + cmdinfo->state |= COMMAND_INFLIGHT; } return 0; @@ -464,9 +522,7 @@ static DEF_SCSI_QCMD(uas_queuecommand) static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) { - struct scsi_device *sdev = cmnd->device; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); + uas_log_cmd_state(cmnd, __func__); /* XXX: Send ABORT TASK Task Management command */ return FAILED; -- cgit v1.2.3-70-g09d2 From 8aac863e9295c42683b5b39ab65e17711e21b34c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:52 +0200 Subject: uas: improve error handling (1) Handle data pipe errors: When the data urb failed we didn't transfer anything, update scsi_cmnd accordingly. (2) Cancel data transfers when we got back an error on the status pipe. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index b589b2e0e92..d8b7bc6ea14 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -242,6 +242,13 @@ static void uas_stat_cmplt(struct urb *urb) uas_sense_old(urb, cmnd); else uas_sense(urb, cmnd); + if (cmnd->result != 0) { + /* cancel data transfers on error */ + if (cmdinfo->state & DATA_IN_URB_INFLIGHT) + usb_unlink_urb(cmdinfo->data_in_urb); + if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) + usb_unlink_urb(cmdinfo->data_out_urb); + } cmdinfo->state &= ~COMMAND_INFLIGHT; uas_try_complete(cmnd, __func__); break; @@ -272,7 +279,12 @@ static void uas_data_cmplt(struct urb *urb) cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; } BUG_ON(sdb == NULL); - sdb->resid = sdb->length - urb->actual_length; + if (urb->status) { + /* error: no data transfered */ + sdb->resid = sdb->length; + } else { + sdb->resid = sdb->length - urb->actual_length; + } uas_try_complete(cmnd, __func__); } -- cgit v1.2.3-70-g09d2 From bdd000fb34202530e5cd11260d06f57e2daf63c9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:53 +0200 Subject: uas: track urbs, kill inflight urbs on disconnect. Use separate anchors for data and sense urbs, which I think will be useful when implementing error handling. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index d8b7bc6ea14..875829a5618 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -41,6 +41,8 @@ struct sense_iu_old { struct uas_dev_info { struct usb_interface *intf; struct usb_device *udev; + struct usb_anchor sense_urbs; + struct usb_anchor data_urbs; int qdepth; unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; @@ -396,6 +398,7 @@ static int uas_submit_sense_urb(struct Scsi_Host *shost, usb_free_urb(urb); return SCSI_MLQUEUE_DEVICE_BUSY; } + usb_anchor_urb(urb, &devinfo->sense_urbs); return 0; } @@ -431,6 +434,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } cmdinfo->state &= ~SUBMIT_DATA_IN_URB; cmdinfo->state |= DATA_IN_URB_INFLIGHT; + usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); } if (cmdinfo->state & ALLOC_DATA_OUT_URB) { @@ -450,6 +454,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, } cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; cmdinfo->state |= DATA_OUT_URB_INFLIGHT; + usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); } if (cmdinfo->state & ALLOC_CMD_URB) { @@ -761,6 +766,8 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->intf = intf; devinfo->udev = udev; + init_usb_anchor(&devinfo->sense_urbs); + init_usb_anchor(&devinfo->data_urbs); uas_configure_endpoints(devinfo); result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2); @@ -804,6 +811,8 @@ static void uas_disconnect(struct usb_interface *intf) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; scsi_remove_host(shost); + usb_kill_anchored_urbs(&devinfo->sense_urbs); + usb_kill_anchored_urbs(&devinfo->data_urbs); uas_free_streams(devinfo); kfree(devinfo); } -- cgit v1.2.3-70-g09d2 From 023b515e5b304122f3802abaa68d1da46fdf48b8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:54 +0200 Subject: uas: task mgmt & error handling Add task management support, wind up in abort and device reset error handlers. Cancel all in-flight urbs in bus reset handler. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 160 +++++++++++++++++++++++++++++++++++++--------- include/linux/usb/uas.h | 40 ++++++++++++ 2 files changed, 171 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 875829a5618..638cd64f961 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -43,7 +43,8 @@ struct uas_dev_info { struct usb_device *udev; struct usb_anchor sense_urbs; struct usb_anchor data_urbs; - int qdepth; + int qdepth, resetting; + struct response_ui response; unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; unsigned uas_sense_old:1; @@ -68,6 +69,7 @@ enum { struct uas_cmd_info { unsigned int state; unsigned int stream; + unsigned int aborted; struct urb *cmd_urb; struct urb *data_in_urb; struct urb *data_out_urb; @@ -222,16 +224,24 @@ static void uas_stat_cmplt(struct urb *urb) return; } + if (devinfo->resetting) { + usb_free_urb(urb); + return; + } + tag = be16_to_cpup(&iu->tag) - 1; if (tag == 0) cmnd = devinfo->cmnd; else cmnd = scsi_host_find_tag(shost, tag - 1); if (!cmnd) { - usb_free_urb(urb); - return; + if (iu->iu_id != IU_ID_RESPONSE) { + usb_free_urb(urb); + return; + } + } else { + cmdinfo = (void *)&cmnd->SCp; } - cmdinfo = (void *)&cmnd->SCp; switch (iu->iu_id) { case IU_ID_STATUS: @@ -260,6 +270,10 @@ static void uas_stat_cmplt(struct urb *urb) case IU_ID_WRITE_READY: uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; + case IU_ID_RESPONSE: + /* store results for uas_eh_task_mgmt() */ + memcpy(&devinfo->response, iu, sizeof(devinfo->response)); + break; default: scmd_printk(KERN_ERR, cmnd, "Bogus IU (%d) received on status pipe\n", iu->iu_id); @@ -287,6 +301,9 @@ static void uas_data_cmplt(struct urb *urb) } else { sdb->resid = sdb->length - urb->actual_length; } + if (cmdinfo->aborted) { + return; + } uas_try_complete(cmnd, __func__); } @@ -377,6 +394,51 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, return NULL; } +static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, + u8 function, u16 stream_id) +{ + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + struct usb_device *udev = devinfo->udev; + struct urb *urb = usb_alloc_urb(0, gfp); + struct task_mgmt_iu *iu; + int err = -ENOMEM; + + if (!urb) + goto err; + + iu = kzalloc(sizeof(*iu), gfp); + if (!iu) + goto err; + + iu->iu_id = IU_ID_TASK_MGMT; + iu->tag = cpu_to_be16(stream_id); + int_to_scsilun(cmnd->device->lun, &iu->lun); + + iu->function = function; + switch (function) { + case TMF_ABORT_TASK: + if (blk_rq_tagged(cmnd->request)) + iu->task_tag = cpu_to_be16(cmnd->request->tag + 2); + else + iu->task_tag = cpu_to_be16(1); + break; + } + + usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), + usb_free_urb, NULL); + urb->transfer_flags |= URB_FREE_BUFFER; + + err = usb_submit_urb(urb, gfp); + if (err) + goto err; + + return 0; + +err: + usb_free_urb(urb); + return err; +} + /* * Why should I request the Status IU before sending the Command IU? Spec * says to, but also says the device may receive them in any order. Seems @@ -502,6 +564,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; + cmdinfo->aborted = 0; switch (cmnd->sc_data_direction) { case DMA_FROM_DEVICE: @@ -537,34 +600,66 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, static DEF_SCSI_QCMD(uas_queuecommand) -static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) +static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, + const char *fname, u8 function) { - uas_log_cmd_state(cmnd, __func__); + struct Scsi_Host *shost = cmnd->device->host; + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + u16 tag = 9999; /* FIXME */ -/* XXX: Send ABORT TASK Task Management command */ - return FAILED; + memset(&devinfo->response, 0, sizeof(devinfo->response)); + if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) { + shost_printk(KERN_INFO, shost, + "%s: %s: submit sense urb failed\n", + __func__, fname); + return FAILED; + } + if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { + shost_printk(KERN_INFO, shost, + "%s: %s: submit task mgmt urb failed\n", + __func__, fname); + return FAILED; + } + if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) { + shost_printk(KERN_INFO, shost, + "%s: %s timed out\n", __func__, fname); + return FAILED; + } + if (be16_to_cpu(devinfo->response.tag) != tag) { + shost_printk(KERN_INFO, shost, + "%s: %s failed (wrong tag %d/%d)\n", __func__, + fname, be16_to_cpu(devinfo->response.tag), tag); + return FAILED; + } + if (devinfo->response.response_code != RC_TMF_COMPLETE) { + shost_printk(KERN_INFO, shost, + "%s: %s failed (rc 0x%x)\n", __func__, + fname, devinfo->response.response_code); + return FAILED; + } + return SUCCESS; } -static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) { - struct scsi_device *sdev = cmnd->device; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + int ret; -/* XXX: Send LOGICAL UNIT RESET Task Management command */ - return FAILED; + uas_log_cmd_state(cmnd, __func__); + cmdinfo->aborted = 1; + ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); + if (cmdinfo->state & DATA_IN_URB_INFLIGHT) + usb_kill_urb(cmdinfo->data_in_urb); + if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) + usb_kill_urb(cmdinfo->data_out_urb); + return ret; } -static int uas_eh_target_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) { - struct scsi_device *sdev = cmnd->device; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); - -/* XXX: Can we reset just the one USB interface? - * Would calling usb_set_interface() have the right effect? - */ - return FAILED; + sdev_printk(KERN_INFO, cmnd->device, "%s\n", __func__); + return uas_eh_task_mgmt(cmnd, "LOGICAL UNIT RESET", + TMF_LOGICAL_UNIT_RESET); } static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) @@ -572,14 +667,21 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) struct scsi_device *sdev = cmnd->device; struct uas_dev_info *devinfo = sdev->hostdata; struct usb_device *udev = devinfo->udev; + int err; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); + devinfo->resetting = 1; + usb_kill_anchored_urbs(&devinfo->sense_urbs); + usb_kill_anchored_urbs(&devinfo->data_urbs); + err = usb_reset_device(udev); + devinfo->resetting = 0; - if (usb_reset_device(udev)) - return SUCCESS; + if (err) { + shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__); + return FAILED; + } - return FAILED; + shost_printk(KERN_INFO, sdev->host, "%s success\n", __func__); + return SUCCESS; } static int uas_slave_alloc(struct scsi_device *sdev) @@ -604,7 +706,6 @@ static struct scsi_host_template uas_host_template = { .slave_configure = uas_slave_configure, .eh_abort_handler = uas_eh_abort_handler, .eh_device_reset_handler = uas_eh_device_reset_handler, - .eh_target_reset_handler = uas_eh_target_reset_handler, .eh_bus_reset_handler = uas_eh_bus_reset_handler, .can_queue = 65536, /* Is there a limit on the _host_ ? */ .this_id = -1, @@ -766,6 +867,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->intf = intf; devinfo->udev = udev; + devinfo->resetting = 0; init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); uas_configure_endpoints(devinfo); diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h index 9a988e41369..5499ab5c94b 100644 --- a/include/linux/usb/uas.h +++ b/include/linux/usb/uas.h @@ -20,6 +20,28 @@ enum { IU_ID_WRITE_READY = 0x07, }; +enum { + TMF_ABORT_TASK = 0x01, + TMF_ABORT_TASK_SET = 0x02, + TMF_CLEAR_TASK_SET = 0x04, + TMF_LOGICAL_UNIT_RESET = 0x08, + TMF_I_T_NEXUS_RESET = 0x10, + TMF_CLEAR_ACA = 0x40, + TMF_QUERY_TASK = 0x80, + TMF_QUERY_TASK_SET = 0x81, + TMF_QUERY_ASYNC_EVENT = 0x82, +}; + +enum { + RC_TMF_COMPLETE = 0x00, + RC_INVALID_INFO_UNIT = 0x02, + RC_TMF_NOT_SUPPORTED = 0x04, + RC_TMF_FAILED = 0x05, + RC_TMF_SUCCEEDED = 0x08, + RC_INCORRECT_LUN = 0x09, + RC_OVERLAPPED_TAG = 0x0a, +}; + struct command_iu { __u8 iu_id; __u8 rsvd1; @@ -32,6 +54,16 @@ struct command_iu { __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ }; +struct task_mgmt_iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __u8 function; + __u8 rsvd2; + __be16 task_tag; + struct scsi_lun lun; +}; + /* * Also used for the Read Ready and Write Ready IUs since they have the * same first four bytes @@ -47,6 +79,14 @@ struct sense_iu { __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; +struct response_ui { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __be16 add_response_info; + __u8 response_code; +}; + struct usb_pipe_usage_descriptor { __u8 bLength; __u8 bDescriptorType; -- cgit v1.2.3-70-g09d2 From cf61fdb944534ffa84a824bb8c31a3826cdc169d Mon Sep 17 00:00:00 2001 From: Guilherme Maciel Ferreira Date: Sat, 23 Jun 2012 21:24:30 -0300 Subject: USB: FHCI: Reusing QUICC Engine USB Controller registers from immap_qe.h The struct fhci_regs (in drivers/usb/host/fhci.h) is basically a redefinition of the struct qe_usb_ctlr (in arch/powerpc/include/asm/immap_qe.h). The qe_usb_ctlr struct is preferrable once it uses accurately the registers' names found in the Freescale's QUICC Engine Block Reference Manuals (QEIWRM.pdf Rev.4.4 Chapter 19 for MPC836xE series and MPC8323ERM.pdf Rev.2 Chapter 36 for MPC832xE series), making easier to map the FHCI device driver to the hardware manual. Also, as the FHCI driver uses the USB Controller registers, the name qe_usb_ctlr is a more precise representation of the hardware than fhci_regs. Signed-off-by: Guilherme Maciel Ferreira Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fhci-dbg.c | 12 ++++++------ drivers/usb/host/fhci-hcd.c | 32 ++++++++++++++++---------------- drivers/usb/host/fhci-hub.c | 16 ++++++++-------- drivers/usb/host/fhci-sched.c | 30 +++++++++++++++--------------- drivers/usb/host/fhci-tds.c | 14 +++++++------- drivers/usb/host/fhci.h | 22 ++-------------------- 6 files changed, 54 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index 6fe55004911..f238cb37305 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -41,7 +41,7 @@ void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) static int fhci_dfs_regs_show(struct seq_file *s, void *v) { struct fhci_hcd *fhci = s->private; - struct fhci_regs __iomem *regs = fhci->regs; + struct qe_usb_ctlr __iomem *regs = fhci->regs; seq_printf(s, "mode: 0x%x\n" "addr: 0x%x\n" @@ -50,11 +50,11 @@ static int fhci_dfs_regs_show(struct seq_file *s, void *v) "status: 0x%x\n" "SOF timer: %d\n" "frame number: %d\n" "lines status: 0x%x\n", - in_8(®s->usb_mod), in_8(®s->usb_addr), - in_8(®s->usb_comm), in_be16(®s->usb_ep[0]), - in_be16(®s->usb_event), in_be16(®s->usb_mask), - in_8(®s->usb_status), in_be16(®s->usb_sof_tmr), - in_be16(®s->usb_frame_num), + in_8(®s->usb_usmod), in_8(®s->usb_usadr), + in_8(®s->usb_uscom), in_be16(®s->usb_usep[0]), + in_be16(®s->usb_usber), in_be16(®s->usb_usbmr), + in_8(®s->usb_usbs), in_be16(®s->usb_ussft), + in_be16(®s->usb_usfrn), fhci_ioports_check_bus_state(fhci)); return 0; diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index d2623747b48..7da1a26bed2 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -40,8 +40,8 @@ void fhci_start_sof_timer(struct fhci_hcd *fhci) /* clear frame_n */ out_be16(&fhci->pram->frame_num, 0); - out_be16(&fhci->regs->usb_sof_tmr, 0); - setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + out_be16(&fhci->regs->usb_ussft, 0); + setbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE); fhci_dbg(fhci, "<- %s\n", __func__); } @@ -50,7 +50,7 @@ void fhci_stop_sof_timer(struct fhci_hcd *fhci) { fhci_dbg(fhci, "-> %s\n", __func__); - clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + clrbits8(&fhci->regs->usb_usmod, USB_MODE_SFTE); gtm_stop_timer16(fhci->timer); fhci_dbg(fhci, "<- %s\n", __func__); @@ -58,7 +58,7 @@ void fhci_stop_sof_timer(struct fhci_hcd *fhci) u16 fhci_get_sof_timer_count(struct fhci_usb *usb) { - return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12); + return be16_to_cpu(in_be16(&usb->fhci->regs->usb_ussft) / 12); } /* initialize the endpoint zero */ @@ -88,8 +88,8 @@ void fhci_usb_enable_interrupt(struct fhci_usb *usb) enable_irq(fhci_to_hcd(fhci)->irq); /* initialize the event register and mask register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); /* enable the timer interrupts */ enable_irq(fhci->timer->irq); @@ -109,7 +109,7 @@ void fhci_usb_disable_interrupt(struct fhci_usb *usb) /* disable the usb interrupt */ disable_irq_nosync(fhci_to_hcd(fhci)->irq); - out_be16(&usb->fhci->regs->usb_mask, 0); + out_be16(&usb->fhci->regs->usb_usbmr, 0); } usb->intr_nesting_cnt++; } @@ -119,9 +119,9 @@ static u32 fhci_usb_enable(struct fhci_hcd *fhci) { struct fhci_usb *usb = fhci->usb_lld; - out_be16(&usb->fhci->regs->usb_event, 0xffff); - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); - setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); + setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN); mdelay(100); @@ -141,7 +141,7 @@ static u32 fhci_usb_disable(struct fhci_hcd *fhci) usb->port_status == FHCI_PORT_LOW) fhci_device_disconnected_interrupt(fhci); - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN); return 0; } @@ -285,13 +285,13 @@ static int fhci_usb_init(struct fhci_hcd *fhci) USB_E_IDLE_MASK | USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK); - out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN); + out_8(&usb->fhci->regs->usb_usmod, USB_MODE_HOST | USB_MODE_EN); /* clearing the mask register */ - out_be16(&usb->fhci->regs->usb_mask, 0); + out_be16(&usb->fhci->regs->usb_usbmr, 0); /* initialing the event register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) { fhci_usb_free(usb); @@ -745,8 +745,8 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) } /* Clear and disable any pending interrupts. */ - out_be16(&fhci->regs->usb_event, 0xffff); - out_be16(&fhci->regs->usb_mask, 0); + out_be16(&fhci->regs->usb_usber, 0xffff); + out_be16(&fhci->regs->usb_usbmr, 0); ret = usb_add_hcd(hcd, usb_irq, 0); if (ret < 0) diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c index 348fe62e94f..6af2512f837 100644 --- a/drivers/usb/host/fhci-hub.c +++ b/drivers/usb/host/fhci-hub.c @@ -97,7 +97,7 @@ void fhci_port_disable(struct fhci_hcd *fhci) /* Enable IDLE since we want to know if something comes along */ usb->saved_msk |= USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); /* check if during the disconnection process attached new device */ if (port_status == FHCI_PORT_WAITING) @@ -158,21 +158,21 @@ void fhci_port_reset(void *lld) fhci_stop_sof_timer(fhci); /* disable the USB controller */ - mode = in_8(&fhci->regs->usb_mod); - out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN)); + mode = in_8(&fhci->regs->usb_usmod); + out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN)); /* disable idle interrupts */ - mask = in_be16(&fhci->regs->usb_mask); - out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK)); + mask = in_be16(&fhci->regs->usb_usbmr); + out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK)); fhci_io_port_generate_reset(fhci); /* enable interrupt on this endpoint */ - out_be16(&fhci->regs->usb_mask, mask); + out_be16(&fhci->regs->usb_usbmr, mask); /* enable the USB controller */ - mode = in_8(&fhci->regs->usb_mod); - out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN); + mode = in_8(&fhci->regs->usb_usmod); + out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN); fhci_start_sof_timer(fhci); fhci_dbg(fhci, "<- %s\n", __func__); diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index 2df851b4bc7..2dc8a40e39d 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -132,8 +132,8 @@ void fhci_flush_all_transmissions(struct fhci_usb *usb) u8 mode; struct td *td; - mode = in_8(&usb->fhci->regs->usb_mod); - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + mode = in_8(&usb->fhci->regs->usb_usmod); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_EN); fhci_flush_bds(usb); @@ -147,9 +147,9 @@ void fhci_flush_all_transmissions(struct fhci_usb *usb) usb->actual_frame->frame_status = FRAME_END_TRANSMISSION; /* reset the event register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); /* enable the USB controller */ - out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); + out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN); } /* @@ -414,7 +414,7 @@ static void sof_interrupt(struct fhci_hcd *fhci) usb->port_status = FHCI_PORT_FULL; /* Disable IDLE */ usb->saved_msk &= ~USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); } gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false); @@ -433,14 +433,14 @@ void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci) fhci_dbg(fhci, "-> %s\n", __func__); fhci_usb_disable_interrupt(usb); - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS); usb->port_status = FHCI_PORT_DISABLED; fhci_stop_sof_timer(fhci); /* Enable IDLE since we want to know if something comes along */ usb->saved_msk |= USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION; usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION; @@ -473,7 +473,7 @@ void fhci_device_connected_interrupt(struct fhci_hcd *fhci) } usb->port_status = FHCI_PORT_LOW; - setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + setbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS); usb->vroot_hub->port.wPortStatus |= (USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_CONNECTION); @@ -491,7 +491,7 @@ void fhci_device_connected_interrupt(struct fhci_hcd *fhci) } usb->port_status = FHCI_PORT_FULL; - clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + clrbits8(&usb->fhci->regs->usb_usmod, USB_MODE_LSS); usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; usb->vroot_hub->port.wPortStatus |= @@ -535,7 +535,7 @@ static void abort_transmission(struct fhci_usb *usb) /* issue stop Tx command */ qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0); /* flush Tx FIFOs */ - out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO); + out_8(&usb->fhci->regs->usb_uscom, USB_CMD_FLUSH_FIFO | EP_ZERO); udelay(1000); /* reset Tx BDs */ fhci_flush_bds(usb); @@ -555,11 +555,11 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) usb = fhci->usb_lld; - usb_er |= in_be16(&usb->fhci->regs->usb_event) & - in_be16(&usb->fhci->regs->usb_mask); + usb_er |= in_be16(&usb->fhci->regs->usb_usber) & + in_be16(&usb->fhci->regs->usb_usbmr); /* clear event bits for next time */ - out_be16(&usb->fhci->regs->usb_event, usb_er); + out_be16(&usb->fhci->regs->usb_usber, usb_er); fhci_dbg_isr(fhci, usb_er); @@ -573,7 +573,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) /* Turn on IDLE since we want to disconnect */ usb->saved_msk |= USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_event, + out_be16(&usb->fhci->regs->usb_usber, usb->saved_msk); } else if (usb->port_status == FHCI_PORT_DISABLED) { if (fhci_ioports_check_bus_state(fhci) == 1) @@ -611,7 +611,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) /* XXX usb->port_status = FHCI_PORT_WAITING; */ /* Disable IDLE */ usb->saved_msk &= ~USB_E_IDLE_MASK; - out_be16(&usb->fhci->regs->usb_mask, + out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); } else { fhci_dbg_isr(fhci, -1); diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c index c5ed8819929..1498061f0ae 100644 --- a/drivers/usb/host/fhci-tds.c +++ b/drivers/usb/host/fhci-tds.c @@ -249,7 +249,7 @@ void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep, u8 rt; /* set the endpoint registers according to the endpoint */ - out_be16(&usb->fhci->regs->usb_ep[0], + out_be16(&usb->fhci->regs->usb_usep[0], USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE); out_be16(&usb->fhci->pram->ep_ptr[0], cpm_muram_offset(ep->ep_pram_ptr)); @@ -463,7 +463,7 @@ u32 fhci_host_transaction(struct fhci_usb *usb, cq_put(&ep->conf_frame_Q, pkt); if (cq_howmany(&ep->conf_frame_Q) == 1) - out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO); return 0; } @@ -535,8 +535,8 @@ void fhci_flush_actual_frame(struct fhci_usb *usb) struct endpoint *ep = usb->ep0; /* disable the USB controller */ - mode = in_8(&usb->fhci->regs->usb_mod); - out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN); + mode = in_8(&usb->fhci->regs->usb_usmod); + out_8(&usb->fhci->regs->usb_usmod, mode & ~USB_MODE_EN); tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr); td = cpm_muram_addr(tb_ptr); @@ -571,9 +571,9 @@ void fhci_flush_actual_frame(struct fhci_usb *usb) usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION; /* reset the event register */ - out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_usber, 0xffff); /* enable the USB controller */ - out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); + out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN); } /* handles Tx confirm and Tx error interrupt */ @@ -613,7 +613,7 @@ void fhci_host_transmit_actual_frame(struct fhci_usb *usb) /* start transmit only if we have something in the TDs */ if (in_be16(&td->status) & TD_R) - out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO); if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) { out_be32(&old_td->buf_ptr, 0); diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index dc6939a44a1..7cc1c32dc36 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -28,6 +28,7 @@ #include #include #include +#include #define USB_CLOCK 48000000 @@ -173,25 +174,6 @@ #define USB_E_TXB_MASK 0x0002 #define USB_E_RXB_MASK 0x0001 -/* Freescale USB Host controller registers */ -struct fhci_regs { - u8 usb_mod; /* mode register */ - u8 usb_addr; /* address register */ - u8 usb_comm; /* command register */ - u8 reserved1[1]; - __be16 usb_ep[4]; /* endpoint register */ - u8 reserved2[4]; - __be16 usb_event; /* event register */ - u8 reserved3[2]; - __be16 usb_mask; /* mask register */ - u8 reserved4[1]; - u8 usb_status; /* status register */ - __be16 usb_sof_tmr; /* Start Of Frame timer */ - u8 reserved5[2]; - __be16 usb_frame_num; /* frame number register */ - u8 reserved6[1]; -}; - /* Freescale USB HOST */ struct fhci_pram { __be16 ep_ptr[4]; /* Endpoint porter reg */ @@ -267,7 +249,7 @@ struct fhci_hcd { int gpios[NUM_GPIOS]; bool alow_gpios[NUM_GPIOS]; - struct fhci_regs __iomem *regs; /* I/O memory used to communicate */ + struct qe_usb_ctlr __iomem *regs; /* I/O memory used to communicate */ struct fhci_pram __iomem *pram; /* Parameter RAM */ struct gtm_timer *timer; -- cgit v1.2.3-70-g09d2 From a46af4ebf9ffec35eea0390e89935197b833dc61 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 25 Jun 2012 12:19:03 -0400 Subject: USB: EHCI: define extension registers like normal ones This patch (as1562) cleans up the definitions of the EHCI extended registers to be consistent with the definitions of the standard registers. This makes the code look a lot nicer, with no functional change. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 15 +++++---------- drivers/usb/host/ehci-hub.c | 26 ++++++++------------------ include/linux/usb/ehci_def.h | 28 +++++++++++++++++++++------- 3 files changed, 34 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38c78b..c49fc1e7895 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -203,11 +203,9 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr, /* check TDI/ARC silicon is in host mode */ static int tdi_in_host_mode (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); return (tmp & 3) == USBMODE_CM_HC; } @@ -303,11 +301,9 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, /* put TDI/ARC silicon into EHCI mode */ static void tdi_reset (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); tmp |= USBMODE_CM_HC; /* The default byte access to MMR space is LE after * controller reset. Set the required endian mode @@ -315,7 +311,7 @@ static void tdi_reset (struct ehci_hcd *ehci) */ if (ehci_big_endian_mmio(ehci)) tmp |= USBMODE_BE; - ehci_writel(ehci, tmp, reg_ptr); + ehci_writel(ehci, tmp, &ehci->regs->usbmode); } /* reset a non-running (STS_HALT == 1) controller */ @@ -339,9 +335,8 @@ static int ehci_reset (struct ehci_hcd *ehci) if (ehci->has_hostpc) { ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS, - (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX)); - ehci_writel(ehci, TXFIFO_DEFAULT, - (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING)); + &ehci->regs->usbmode_ex); + ehci_writel(ehci, TXFIFO_DEFAULT, &ehci->regs->txfill_tuning); } if (retval) return retval; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index dd5eef6af6d..db05e358677 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -149,10 +149,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); } @@ -185,10 +183,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); } @@ -285,11 +281,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; u32 t3; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); t3 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); t3 = ehci_readl(ehci, hostpc_reg); @@ -388,10 +382,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = + &ehci->regs->hostpc[i]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * i); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); @@ -667,7 +660,7 @@ static int ehci_hub_control ( int ports = HCS_N_PORTS (ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; - u32 __iomem *hostpc_reg = NULL; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1]; u32 temp, temp1, status; unsigned long flags; int retval = 0; @@ -680,9 +673,6 @@ static int ehci_hub_control ( * power, "this is the one", etc. EHCI spec supports this. */ - if (ehci->has_hostpc) - hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs - + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -734,7 +724,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (hostpc_reg) { + if (ehci->has_hostpc) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -984,7 +974,7 @@ static int ehci_hub_control ( temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (hostpc_reg) { + if (ehci->has_hostpc) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 7cc95ee3606..de4b9ed5d5d 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -111,7 +111,13 @@ struct ehci_regs { /* ASYNCLISTADDR: offset 0x18 */ u32 async_next; /* address of next async queue head */ - u32 reserved[9]; + u32 reserved1[2]; + + /* TXFILLTUNING: offset 0x24 */ + u32 txfill_tuning; /* TX FIFO Tuning register */ +#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ + + u32 reserved2[6]; /* CONFIGFLAG: offset 0x40 */ u32 configured_flag; @@ -155,26 +161,34 @@ struct ehci_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -}; -#define USBMODE 0x68 /* USB Device mode */ + u32 reserved3[9]; + + /* USBMODE: offset 0x68 */ + u32 usbmode; /* USB Device mode */ #define USBMODE_SDIS (1<<3) /* Stream disable */ #define USBMODE_BE (1<<2) /* BE/LE endianness select */ #define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_IDLE (0<<0) /* idle state */ + u32 reserved4[7]; + /* Moorestown has some non-standard registers, partially due to the fact that * its EHCI controller has both TT and LPM support. HOSTPCx are extensions to * PORTSCx */ -#define HOSTPC0 0x84 /* HOSTPC extension */ + /* HOSTPC: offset 0x84 */ + u32 hostpc[0]; /* HOSTPC extension */ #define HOSTPC_PHCD (1<<22) /* Phy clock disable */ #define HOSTPC_PSPD (3<<25) /* Port speed detection */ -#define USBMODE_EX 0xc8 /* USB Device mode extension */ + + u32 reserved5[17]; + + /* USBMODE_EX: offset 0xc8 */ + u32 usbmode_ex; /* USB Device mode extension */ #define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */ #define USBMODE_EX_HC (3<<0) /* host controller mode */ -#define TXFILLTUNING 0x24 /* TX FIFO Tuning register */ -#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ +}; /* Appendix C, Debug port ... intended for use with special "debug devices" * that can help if there's no serial console. (nonstandard enumeration.) -- cgit v1.2.3-70-g09d2 From 984e97483a143f95d861b7218161ae033df293ab Mon Sep 17 00:00:00 2001 From: Russ Dill Date: Thu, 21 Jun 2012 03:44:31 -0700 Subject: ARM: OMAP: USB: Fixup ehci_hcd_omap_probe error path A recent commit, [PATCH] Fix OMAP EHCI suspend/resume failure (i693) '354ab856' causes ehci probe to fail on omap3xxx. This exposed bugs in the ehci_hcd_omap_probe error path causing an oops. On the error path, call usb_remove_hcd if usb_add_hcd has been called, and call usb_put_hcd if usb_alloc_hcd has been called. Tested on BB-xM. Signed-off-by: Russ.Dill@ti.com Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 17cfb8a1131..6e15fc87cf6 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -347,7 +347,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); - goto err_add_hcd; + goto err_pm_runtime; } /* root ports should always stay powered */ @@ -424,8 +424,12 @@ err_utmi_p1_fck: clk_put(utmi_p1_fck); err_add_hcd: + usb_remove_hcd(hcd); + +err_pm_runtime: disable_put_regulator(pdata); pm_runtime_put_sync(dev); + usb_put_hcd(hcd); err_io: iounmap(regs); -- cgit v1.2.3-70-g09d2 From 08f0d96670c53898b4154e7945e77cdd966003f7 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 25 Jun 2012 22:40:43 +0530 Subject: usb: dwc3: correct set_halt implementation for ep0 set_halt for ep0 is called to stall a deferred control responses by the gadget. We already have a function to stall default control endpoint. This patch points set_halt for ep0 to the already available function. Signed-off-by: Pratyush Anand Signed-off-by: Michel Sanches Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 10 ++++++++++ drivers/usb/dwc3/gadget.c | 7 +++++-- drivers/usb/dwc3/gadget.h | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 8244eb53082..1315e787a31 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -225,6 +225,16 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) dwc3_ep0_out_start(dwc); } +int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + dwc3_ep0_stall_and_restart(dwc); + + return 0; +} + void dwc3_ep0_out_start(struct dwc3 *dwc) { int ret; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d98549a7b8b..13f257db243 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1268,7 +1268,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) dep->flags |= DWC3_EP_WEDGE; spin_unlock_irqrestore(&dwc->lock, flags); - return dwc3_gadget_ep_set_halt(ep, 1); + if (dep->number == 0 || dep->number == 1) + return dwc3_gadget_ep0_set_halt(ep, 1); + else + return dwc3_gadget_ep_set_halt(ep, 1); } /* -------------------------------------------------------------------------- */ @@ -1286,7 +1289,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = { .free_request = dwc3_gadget_ep_free_request, .queue = dwc3_gadget_ep0_queue, .dequeue = dwc3_gadget_ep_dequeue, - .set_halt = dwc3_gadget_ep_set_halt, + .set_halt = dwc3_gadget_ep0_set_halt, .set_wedge = dwc3_gadget_ep_set_wedge, }; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 39ced0b0851..99e6d724882 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -111,6 +111,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); +int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); -- cgit v1.2.3-70-g09d2 From cbc725b371f80710c5c611db78a501a6cd314ec3 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 2 Jul 2012 10:21:52 +0530 Subject: usb: dwc3: keep default hird threshold value as 4b1100 as per data book any HIRD threshold value greater than 4b1100 is invalid. So set the maximum valid value as default values. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 13f257db243..f732f2ccb9b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2448,7 +2448,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); /* TODO: This should be configurable */ - reg |= DWC3_DCTL_HIRD_THRES(31); + reg |= DWC3_DCTL_HIRD_THRES(28); dwc3_writel(dwc->regs, DWC3_DCTL, reg); -- cgit v1.2.3-70-g09d2 From dd864f20b382b131bfe10c87c5c81985f5bafa54 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 2 Jul 2012 10:21:53 +0530 Subject: usb: dwc3: no need to clear INIT{U1,U2}ENA bits as per databook, these bits are cleared by hardware on each USB reset, so no need to clear it explicitly by software in reset ISR. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f732f2ccb9b..e7450931125 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2020,7 +2020,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; - reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA); reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->test_mode = false; -- cgit v1.2.3-70-g09d2 From e274a31e29b6a097c32f526493f4ebce10856dcb Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 2 Jul 2012 10:21:54 +0530 Subject: usb: dwc3: enable ACCEPT{U1,U2}ENA when SetConfiguration received As per databook, ACCEPT{U1,U2}ENA bits should be set after receiving SetConfiguration Command. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 9 +++++++++ drivers/usb/dwc3/gadget.c | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 1315e787a31..9b94886b66e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -474,6 +474,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { u32 cfg; int ret; + u32 reg; dwc->start_config_issued = false; cfg = le16_to_cpu(ctrl->wValue); @@ -488,6 +489,14 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { dwc->dev_state = DWC3_CONFIGURED_STATE; + /* + * Enable transition to U1/U2 state when + * nothing is pending from application. + */ + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc->resize_fifos = true; dev_dbg(dwc->dev, "resize fifos flag SET\n"); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e7450931125..39626e366a8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2020,7 +2020,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; - reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->test_mode = false; -- cgit v1.2.3-70-g09d2 From 6f17f74b3d8345663f5fcfd834145685110075bd Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Mon, 2 Jul 2012 10:21:55 +0530 Subject: usb: dwc3: return error in case of run/stop timeout Although timeout has never been experienced, still to make it meaningful, its better to return error if it ever occurs. Signed-off-by: Pratyush Anand Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 39626e366a8..aedc1c5cfac 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1408,7 +1408,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, return 0; } -static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) { u32 reg; u32 timeout = 500; @@ -1440,7 +1440,7 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) } timeout--; if (!timeout) - break; + return -ETIMEDOUT; udelay(1); } while (1); @@ -1448,20 +1448,23 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) dwc->gadget_driver ? dwc->gadget_driver->function : "no-function", is_on ? "connect" : "disconnect"); + + return 0; } static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) { struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; + int ret; is_on = !!is_on; spin_lock_irqsave(&dwc->lock, flags); - dwc3_gadget_run_stop(dwc, is_on); + ret = dwc3_gadget_run_stop(dwc, is_on); spin_unlock_irqrestore(&dwc->lock, flags); - return 0; + return ret; } static int dwc3_gadget_start(struct usb_gadget *g, -- cgit v1.2.3-70-g09d2 From 48a31af74404e6460eabca410bf0b4a625bfd372 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 25 Jun 2012 16:40:22 +0200 Subject: usb: gadget: mass_storage: make "file" and "ro" read only in some cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The “file” sysfs entry for LUNs was writable even for non-removable LUNs and the fsg_store_file() function did not check whether LUN is removable or not. This made it possible to change or even close LUN's backing file. The same is true for “ro” sysfs entry and LUNs simulating CD-ROM. For those LUNs, the file should not be writable. This commit introduces two new device_attribute structures for those two special cases so that the file/ro sysfs entries are made non-writable when not desired. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_mass_storage.c | 26 +++++++++++++++++++++----- drivers/usb/gadget/storage_common.c | 1 - 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 7ad4f819752..4f1142efa6d 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2609,11 +2609,15 @@ static int fsg_main_thread(void *common_) /*************************** DEVICE ATTRIBUTES ***************************/ -/* Write permission is checked per LUN in store_*() functions. */ static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua); static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); +static struct device_attribute dev_attr_ro_cdrom = + __ATTR(ro, 0444, fsg_show_ro, NULL); +static struct device_attribute dev_attr_file_nonremovable = + __ATTR(file, 0444, fsg_show_file, NULL); + /****************************** FSG COMMON ******************************/ @@ -2724,10 +2728,16 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, goto error_release; } - rc = device_create_file(&curlun->dev, &dev_attr_ro); + rc = device_create_file(&curlun->dev, + curlun->cdrom + ? &dev_attr_ro_cdrom + : &dev_attr_ro); if (rc) goto error_luns; - rc = device_create_file(&curlun->dev, &dev_attr_file); + rc = device_create_file(&curlun->dev, + curlun->removable + ? &dev_attr_file + : &dev_attr_file_nonremovable); if (rc) goto error_luns; rc = device_create_file(&curlun->dev, &dev_attr_nofua); @@ -2862,8 +2872,14 @@ static void fsg_common_release(struct kref *ref) /* In error recovery common->nluns may be zero. */ for (; i; --i, ++lun) { device_remove_file(&lun->dev, &dev_attr_nofua); - device_remove_file(&lun->dev, &dev_attr_ro); - device_remove_file(&lun->dev, &dev_attr_file); + device_remove_file(&lun->dev, + lun->cdrom + ? &dev_attr_ro_cdrom + : &dev_attr_ro); + device_remove_file(&lun->dev, + lun->removable + ? &dev_attr_file + : &dev_attr_file_nonremovable); fsg_lun_close(lun); device_unregister(&lun->dev); } diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index e576678fb62..ae8b18869b8 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -878,7 +878,6 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, if (count > 0 && buf[count-1] == '\n') ((char *) buf)[count-1] = 0; /* Ugh! */ - /* Load new medium */ down_write(filesem); if (count > 0 && buf[0]) { -- cgit v1.2.3-70-g09d2 From eebc0d368f2ecbe34860b74e28c29069707d2057 Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Tue, 26 Jun 2012 11:27:09 -0300 Subject: usb: gadget: lpc32xx_udc: Propagate devicetree to gadget drivers Fill dev.of_node of gadget drivers, so they can use devicetree Signed-off-by: Alexandre Pereira da Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/lpc32xx_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index d227d93ce89..8424947838a 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -3005,6 +3005,7 @@ static int lpc32xx_start(struct usb_gadget_driver *driver, udc->driver = driver; udc->gadget.dev.driver = &driver->driver; + udc->gadget.dev.of_node = udc->dev->of_node; udc->enabled = 1; udc->selfpowered = 1; udc->vbus = 0; -- cgit v1.2.3-70-g09d2 From 7d7b22928b900bcb0f8885a95846e5125a757e76 Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Tue, 26 Jun 2012 11:27:10 -0300 Subject: usb: gadget: s3c-hsotg: Propagate devicetree to gadget drivers Fill dev.of_node of gadget drivers, so they can use devicetree Signed-off-by: Alexandre Pereira da Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 75a28e6d376..b13e0bb5f5b 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2953,6 +2953,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, driver->driver.bus = NULL; hsotg->driver = driver; hsotg->gadget.dev.driver = &driver->driver; + hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask; hsotg->gadget.speed = USB_SPEED_UNKNOWN; -- cgit v1.2.3-70-g09d2 From cb4baf104b11458400941f6d843ed22e122bba2d Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Tue, 26 Jun 2012 11:27:11 -0300 Subject: usb: gadget: fsl_udc: Propagate devicetree to gadget drivers Fill dev.of_node of gadget drivers, so they can use devicetree Signed-off-by: Alexandre Pereira da Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/fsl_udc_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 28316858208..df9fe1cccfe 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2560,6 +2560,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.parent = &pdev->dev; + udc_controller->gadget.dev.of_node = pdev->dev.of_node; ret = device_register(&udc_controller->gadget.dev); if (ret < 0) goto err_free_irq; -- cgit v1.2.3-70-g09d2 From 65c84ea18b1b4b8c03fb67c3bea023ed1446bd2e Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Tue, 26 Jun 2012 11:27:12 -0300 Subject: usb: gadget: at91_udc: Propagate devicetree to gadget drivers Fill dev.of_node of gadget drivers, so they can use devicetree Signed-off-by: Alexandre Pereira da Silva Signed-off-by: Felipe Balbi --- drivers/usb/gadget/at91_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 1a4430f315c..c9e66dfb02e 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1634,6 +1634,7 @@ static int at91_start(struct usb_gadget *gadget, udc = container_of(gadget, struct at91_udc, gadget); udc->driver = driver; udc->gadget.dev.driver = &driver->driver; + udc->gadget.dev.of_node = udc->pdev->dev.of_node; dev_set_drvdata(&udc->gadget.dev, &driver->driver); udc->enabled = 1; udc->selfpowered = 1; -- cgit v1.2.3-70-g09d2 From ded017ee6c7b90f7356bd8488f8af1c10ba90490 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 26 Jun 2012 17:40:32 +0530 Subject: usb: phy: fix return value check of usb_get_phy usb_get_phy will return -ENODEV if it's not able to find the phy. Hence fixed all the callers of usb_get_phy to check for this error condition instead of relying on a non-zero value as success condition. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 2 +- drivers/power/isp1704_charger.c | 2 +- drivers/power/pda_power.c | 16 ++++++++-------- drivers/power/twl4030_charger.c | 7 ++++--- drivers/usb/chipidea/udc.c | 9 +++++---- drivers/usb/gadget/fsl_udc_core.c | 15 ++++++++------- drivers/usb/gadget/mv_udc_core.c | 13 +++++++------ drivers/usb/gadget/omap_udc.c | 25 +++++++++++++------------ drivers/usb/gadget/pxa25x_udc.c | 11 ++++++----- drivers/usb/gadget/pxa27x_udc.c | 11 ++++++----- drivers/usb/gadget/s3c-hsudc.c | 9 +++++---- drivers/usb/host/ehci-fsl.c | 5 +++-- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mv.c | 7 ++++--- drivers/usb/host/ehci-tegra.c | 7 ++++--- drivers/usb/host/ohci-omap.c | 5 +++-- drivers/usb/musb/am35x.c | 3 ++- drivers/usb/musb/blackfin.c | 3 ++- drivers/usb/musb/da8xx.c | 3 ++- drivers/usb/musb/davinci.c | 3 ++- drivers/usb/musb/musb_dsps.c | 3 ++- drivers/usb/musb/omap2430.c | 2 +- drivers/usb/musb/tusb6010.c | 3 ++- drivers/usb/musb/ux500.c | 3 ++- drivers/usb/otg/otg.c | 4 ++-- 25 files changed, 96 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 6bd6f1c4196..d4f0c98428c 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2689,7 +2689,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) } di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!di->usb_phy) { + if (IS_ERR_OR_NULL(di->usb_phy)) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; goto free_usb; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 090e5f9e72c..122911978da 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -416,7 +416,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) return -ENOMEM; isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!isp->phy) + if (IS_ERR_OR_NULL(isp->phy)) goto fail0; isp->dev = &pdev->dev; diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 7602d49e4d8..8dbcd53c5e6 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -322,11 +322,11 @@ static int pda_power_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (transceiver && !pdata->is_usb_online) { - pdata->is_usb_online = otg_is_usb_online; - } - if (transceiver && !pdata->is_ac_online) { - pdata->is_ac_online = otg_is_ac_online; + if (!IS_ERR_OR_NULL(transceiver)) { + if (!pdata->is_usb_online) + pdata->is_usb_online = otg_is_usb_online; + if (!pdata->is_ac_online) + pdata->is_ac_online = otg_is_ac_online; } #endif @@ -373,7 +373,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - if (transceiver && pdata->use_otg_notifier) { + if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) { otg_nb.notifier_call = otg_handle_notification; ret = usb_register_notifier(transceiver, &otg_nb); if (ret) { @@ -408,7 +408,7 @@ usb_supply_failed: if (pdata->is_ac_online && ac_irq) free_irq(ac_irq->start, &pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS - if (transceiver) + if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); #endif ac_irq_failed: @@ -443,7 +443,7 @@ static int pda_power_remove(struct platform_device *pdev) if (pdata->is_ac_online) power_supply_unregister(&pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS - if (transceiver) + if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); #endif if (ac_draw) { diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 13f9db2e853..7cacbaa68ef 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -480,7 +481,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (bci->transceiver != NULL) { + if (!IS_ERR_OR_NULL(bci->transceiver)) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); } @@ -507,7 +508,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return 0; fail_unmask_interrupts: - if (bci->transceiver != NULL) { + if (!IS_ERR_OR_NULL(bci->transceiver)) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); usb_put_phy(bci->transceiver); } @@ -538,7 +539,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - if (bci->transceiver != NULL) { + if (!IS_ERR_OR_NULL(bci->transceiver)) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); usb_put_phy(bci->transceiver); } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index a06d28b119f..4688ab71bd2 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1712,7 +1713,7 @@ static int udc_start(struct ci13xxx *udc) if (retval) goto unreg_device; - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) @@ -1729,7 +1730,7 @@ static int udc_start(struct ci13xxx *udc) return retval; remove_trans: - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { otg_set_peripheral(udc->transceiver->otg, &udc->gadget); usb_put_phy(udc->transceiver); } @@ -1740,7 +1741,7 @@ remove_dbg: unreg_device: device_unregister(&udc->gadget.dev); put_transceiver: - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) usb_put_phy(udc->transceiver); free_pools: dma_pool_destroy(udc->td_pool); @@ -1772,7 +1773,7 @@ static void udc_stop(struct ci13xxx *udc) dma_pool_destroy(udc->td_pool); dma_pool_destroy(udc->qh_pool); - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { otg_set_peripheral(udc->transceiver->otg, NULL); usb_put_phy(udc->transceiver); } diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 0808820ba49..8d8fca635cc 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1229,7 +1230,7 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA) struct fsl_udc *udc; udc = container_of(gadget, struct fsl_udc, gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -ENOTSUPP; } @@ -1983,13 +1984,13 @@ static int fsl_start(struct usb_gadget_driver *driver, goto out; } - if (udc_controller->transceiver) { + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { /* Suspend the controller until OTG enable it */ udc_controller->stopped = 1; printk(KERN_INFO "Suspend udc for OTG auto detect\n"); /* connect to bus through transceiver */ - if (udc_controller->transceiver) { + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { retval = otg_set_peripheral( udc_controller->transceiver->otg, &udc_controller->gadget); @@ -2030,7 +2031,7 @@ static int fsl_stop(struct usb_gadget_driver *driver) if (!driver || driver != udc_controller->driver || !driver->unbind) return -EINVAL; - if (udc_controller->transceiver) + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) otg_set_peripheral(udc_controller->transceiver->otg, NULL); /* stop DR, disable intr */ @@ -2456,7 +2457,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (!udc_controller->transceiver) { + if (IS_ERR_OR_NULL(udc_controller->transceiver)) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; goto err_kfree; @@ -2540,7 +2541,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_free_irq; } - if (!udc_controller->transceiver) { + if (IS_ERR_OR_NULL(udc_controller->transceiver)) { /* initialize usb hw reg except for regs for EP, * leave usbintr reg untouched */ dr_controller_setup(udc_controller); @@ -2564,7 +2565,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) if (ret < 0) goto err_free_irq; - if (udc_controller->transceiver) + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) udc_controller->gadget.is_otg = 1; /* setup QH and epctrl for ep0 */ diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 75ff41a5c95..9305de41af2 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1381,7 +1382,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver, return retval; } - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) { @@ -2107,7 +2108,7 @@ static int __devexit mv_udc_remove(struct platform_device *dev) * then vbus irq will not be requested in udc driver. */ if (udc->pdata && udc->pdata->vbus - && udc->clock_gating && udc->transceiver == NULL) + && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver)) free_irq(udc->pdata->vbus->irq, &dev->dev); /* free memory allocated in probe */ @@ -2325,7 +2326,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) eps_init(udc); /* VBUS detect: we can disable/enable clock on demand.*/ - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) udc->clock_gating = 1; else if (pdata->vbus) { udc->clock_gating = 1; @@ -2369,7 +2370,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) err_unregister: if (udc->pdata && udc->pdata->vbus - && udc->clock_gating && udc->transceiver == NULL) + && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver)) free_irq(pdata->vbus->irq, &dev->dev); device_unregister(&udc->gadget.dev); err_free_irq: @@ -2404,7 +2405,7 @@ static int mv_udc_suspend(struct device *_dev) struct mv_udc *udc = the_controller; /* if OTG is enabled, the following will be done in OTG driver*/ - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return 0; if (udc->pdata->vbus && udc->pdata->vbus->poll) @@ -2437,7 +2438,7 @@ static int mv_udc_resume(struct device *_dev) int retval; /* if OTG is enabled, the following will be done in OTG driver*/ - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return 0; if (!udc->clock_gating) { diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index cf8bf26f12e..7b71295adf6 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1211,7 +1212,7 @@ static int omap_wakeup(struct usb_gadget *gadget) /* NOTE: non-OTG systems may use SRP TOO... */ } else if (!(udc->devstat & UDC_ATT)) { - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) retval = otg_start_srp(udc->transceiver->otg); } spin_unlock_irqrestore(&udc->lock, flags); @@ -1343,7 +1344,7 @@ static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA) struct omap_udc *udc; udc = container_of(gadget, struct omap_udc, gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } @@ -1792,12 +1793,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) if (devstat & UDC_ATT) { udc->gadget.speed = USB_SPEED_FULL; VDBG("connect\n"); - if (!udc->transceiver) + if (IS_ERR_OR_NULL(udc->transceiver)) pullup_enable(udc); // if (driver->connect) call it } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { udc->gadget.speed = USB_SPEED_UNKNOWN; - if (!udc->transceiver) + if (IS_ERR_OR_NULL(udc->transceiver)) pullup_disable(udc); DBG("disconnect, gadget %s\n", udc->driver->driver.name); @@ -1837,12 +1838,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) udc->driver->suspend(&udc->gadget); spin_lock(&udc->lock); } - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) usb_phy_set_suspend( udc->transceiver, 1); } else { VDBG("resume\n"); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) usb_phy_set_suspend( udc->transceiver, 0); if (udc->gadget.speed == USB_SPEED_FULL @@ -2154,7 +2155,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver, omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); /* connect to bus through transceiver */ - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { status = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (status < 0) { @@ -2201,7 +2202,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver) if (machine_without_vbus_sense()) omap_vbus_session(&udc->gadget, 0); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) (void) otg_set_peripheral(udc->transceiver->otg, NULL); else pullup_disable(udc); @@ -2866,7 +2867,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * but not having one probably means no VBUS detection. */ xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (xceiv) + if (!IS_ERR_OR_NULL(xceiv)) type = xceiv->label; else if (config->otg) { DBG("OTG requires external transceiver!\n"); @@ -2898,7 +2899,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) case 16: case 19: case 25: - if (!xceiv) { + if (IS_ERR_OR_NULL(xceiv)) { DBG("external transceiver not registered!\n"); type = "unknown"; } @@ -3010,7 +3011,7 @@ cleanup1: udc = NULL; cleanup0: - if (xceiv) + if (!IS_ERR_OR_NULL(xceiv)) usb_put_phy(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { @@ -3040,7 +3041,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) udc->done = &done; pullup_disable(udc); - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { usb_put_phy(udc->transceiver); udc->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index cc0b1e63dca..fa8e93c2465 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -993,7 +994,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) udc = container_of(_gadget, struct pxa25x_udc, gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } @@ -1299,7 +1300,7 @@ fail: DMSG("registered gadget driver '%s'\n", driver->driver.name); /* connect to bus through transceiver */ - if (dev->transceiver) { + if (!IS_ERR_OR_NULL(dev->transceiver)) { retval = otg_set_peripheral(dev->transceiver->otg, &dev->gadget); if (retval) { @@ -1359,7 +1360,7 @@ static int pxa25x_stop(struct usb_gadget_driver *driver) stop_activity(dev, driver); local_irq_enable(); - if (dev->transceiver) + if (!IS_ERR_OR_NULL(dev->transceiver)) (void) otg_set_peripheral(dev->transceiver->otg, NULL); driver->unbind(&dev->gadget); @@ -2237,7 +2238,7 @@ lubbock_fail0: if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: - if (dev->transceiver) { + if (!IS_ERR_OR_NULL(dev->transceiver)) { usb_put_phy(dev->transceiver); dev->transceiver = NULL; } @@ -2279,7 +2280,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) clk_put(dev->clk); - if (dev->transceiver) { + if (!IS_ERR_OR_NULL(dev->transceiver)) { usb_put_phy(dev->transceiver); dev->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 8f744aab962..644b4305cb9 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1573,7 +1574,7 @@ static int should_enable_udc(struct pxa_udc *udc) int put_on; put_on = ((udc->pullup_on) && (udc->driver)); - put_on &= ((udc->vbus_sensed) || (!udc->transceiver)); + put_on &= ((udc->vbus_sensed) || (IS_ERR_OR_NULL(udc->transceiver))); return put_on; } @@ -1594,7 +1595,7 @@ static int should_disable_udc(struct pxa_udc *udc) int put_off; put_off = ((!udc->pullup_on) || (!udc->driver)); - put_off |= ((!udc->vbus_sensed) && (udc->transceiver)); + put_off |= ((!udc->vbus_sensed) && (!IS_ERR_OR_NULL(udc->transceiver))); return put_off; } @@ -1665,7 +1666,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) struct pxa_udc *udc; udc = to_gadget_udc(_gadget); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } @@ -1834,7 +1835,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver, dev_dbg(udc->dev, "registered gadget driver '%s'\n", driver->driver.name); - if (udc->transceiver) { + if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) { @@ -1908,7 +1909,7 @@ static int pxa27x_udc_stop(struct usb_gadget_driver *driver) dev_info(udc->dev, "unregistered gadget driver '%s'\n", driver->driver.name); - if (udc->transceiver) + if (!IS_ERR_OR_NULL(udc->transceiver)) return otg_set_peripheral(udc->transceiver->otg, NULL); return 0; } diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 22326f27446..7c915625f24 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1165,7 +1166,7 @@ static int s3c_hsudc_start(struct usb_gadget *gadget, } /* connect to bus through transceiver */ - if (hsudc->transceiver) { + if (!IS_ERR_OR_NULL(hsudc->transceiver)) { ret = otg_set_peripheral(hsudc->transceiver->otg, &hsudc->gadget); if (ret) { @@ -1220,7 +1221,7 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget, s3c_hsudc_stop_activity(hsudc); spin_unlock_irqrestore(&hsudc->lock, flags); - if (hsudc->transceiver) + if (!IS_ERR_OR_NULL(hsudc->transceiver)) (void) otg_set_peripheral(hsudc->transceiver->otg, NULL); disable_irq(hsudc->irq); @@ -1249,7 +1250,7 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) if (!hsudc) return -ENODEV; - if (hsudc->transceiver) + if (!IS_ERR_OR_NULL(hsudc->transceiver)) return usb_phy_set_power(hsudc->transceiver, mA); return -EOPNOTSUPP; @@ -1385,7 +1386,7 @@ err_irq: err_remap: release_mem_region(res->start, resource_size(res)); err_res: - if (hsudc->transceiver) + if (!IS_ERR_OR_NULL(hsudc->transceiver)) usb_put_phy(hsudc->transceiver); regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index ba290589d85..32865a7145a 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -146,7 +147,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); - if (ehci->transceiver) { + if (!IS_ERR_OR_NULL(ehci->transceiver)) { retval = otg_set_host(ehci->transceiver->otg, &ehci_to_hcd(ehci)->self); if (retval) { @@ -192,7 +193,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; struct ehci_hcd *ehci = hcd_to_ehci(hcd); - if (ehci->transceiver) { + if (!IS_ERR_OR_NULL(ehci->transceiver)) { otg_set_host(ehci->transceiver->otg, NULL); usb_put_phy(ehci->transceiver); } diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index c7615fb93db..6b4ffb598db 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -146,7 +146,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * management. */ phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!phy) { + if (IS_ERR_OR_NULL(phy)) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; goto unmap; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index ef7aa0df40a..0e8c168ca24 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -254,7 +255,7 @@ static int mv_ehci_probe(struct platform_device *pdev) if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2); - if (!ehci_mv->otg) { + if (IS_ERR_OR_NULL(ehci_mv->otg)) { dev_err(&pdev->dev, "unable to find transceiver\n"); retval = -ENODEV; @@ -302,7 +303,7 @@ err_set_vbus: pdata->set_vbus(0); #ifdef CONFIG_USB_OTG_UTILS err_put_transceiver: - if (ehci_mv->otg) + if (!IS_ERR_OR_NULL(ehci_mv->otg)) usb_put_phy(ehci_mv->otg); #endif err_disable_clk: @@ -331,7 +332,7 @@ static int mv_ehci_remove(struct platform_device *pdev) if (hcd->rh_registered) usb_remove_hcd(hcd); - if (ehci_mv->otg) { + if (!IS_ERR_OR_NULL(ehci_mv->otg)) { otg_set_host(ehci_mv->otg->otg, NULL); usb_put_phy(ehci_mv->otg); } diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 14df2f5cf6a..477ecfa0515 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -750,7 +751,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (tegra->transceiver) + if (!IS_ERR_OR_NULL(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, &hcd->self); } #endif @@ -773,7 +774,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) fail: #ifdef CONFIG_USB_OTG_UTILS - if (tegra->transceiver) { + if (!IS_ERR_OR_NULL(tegra->transceiver)) { otg_set_host(tegra->transceiver->otg, NULL); usb_put_phy(tegra->transceiver); } @@ -808,7 +809,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); #ifdef CONFIG_USB_OTG_UTILS - if (tegra->transceiver) { + if (!IS_ERR_OR_NULL(tegra->transceiver)) { otg_set_host(tegra->transceiver->otg, NULL); usb_put_phy(tegra->transceiver); } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 92a77dfd193..c7b06f504c6 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -212,7 +213,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { ohci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (ohci->transceiver) { + if (!IS_ERR_OR_NULL(ohci->transceiver)) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", @@ -403,7 +404,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) struct ohci_hcd *ohci = hcd_to_ohci (hcd); usb_remove_hcd(hcd); - if (ohci->transceiver) { + if (!IS_ERR_OR_NULL(ohci->transceiver)) { (void) otg_set_host(ohci->transceiver->otg, 0); usb_put_phy(ohci->transceiver); } diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 4a8cbf0e8d5..7a95ab87ac0 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -365,7 +366,7 @@ static int am35x_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) return -ENODEV; if (is_host_enabled(musb)) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 452940986d6..428e6aa3e78 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -416,7 +417,7 @@ static int bfin_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) { + if (IS_ERR_OR_NULL(musb->xceiv)) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index d731c80c4fe..0f9fcec4e1d 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -426,7 +427,7 @@ static int da8xx_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) goto fail; if (is_host_enabled(musb)) diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 582268de3fa..f6492043655 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -385,7 +386,7 @@ static int davinci_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) goto unregister; musb->mregs += DAVINCI_BASE_OFFSET; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 92603e498e6..217808d9fbe 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -377,7 +378,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) return -ENODEV; /* Returns zero if e.g. not clocked */ diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 2813490ba63..5fdb9da8dd5 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -320,7 +320,7 @@ static int omap2430_musb_init(struct musb *musb) * which needs a driver, drivers aren't always needed. */ musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); - if (!musb->xceiv) { + if (IS_ERR_OR_NULL(musb->xceiv)) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 8ddf3d5f7cd..1a1bd9cf40c 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1079,7 +1080,7 @@ static int tusb_musb_init(struct musb *musb) usb_nop_xceiv_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) + if (IS_ERR_OR_NULL(musb->xceiv)) return -ENODEV; pdev = to_platform_device(musb->controller); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 46cf80a8cac..a8c0fadce1b 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -38,7 +39,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) { + if (IS_ERR_OR_NULL(musb->xceiv)) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; } diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 0fa4d8c1b1e..88d62b16f63 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -67,7 +67,7 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) return NULL; phy = usb_get_phy(type); - if (phy) { + if (!IS_ERR(phy)) { *ptr = phy; devres_add(dev, ptr); } else @@ -82,7 +82,7 @@ EXPORT_SYMBOL(devm_usb_get_phy); * @type - the type of the phy the controller requires * * Returns the phy driver, after getting a refcount to it; or - * null if there is no such phy. The caller is responsible for + * -ENODEV if there is no such phy. The caller is responsible for * calling usb_put_phy() to release that count. * * For use by USB host and peripheral drivers. -- cgit v1.2.3-70-g09d2 From f8ecf829481b2cc7301a811da9d2df53ef174977 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 2 Jul 2012 12:20:24 +0530 Subject: usb: phy: fix error handling in usb_get_phy spin_unlock_irqrestore() was not being called in the error path of usb_get_phy. It's fixed here. Reported-by: Marc Kleine-Budde Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/otg/otg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 88d62b16f63..1bf60a22595 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -98,11 +98,12 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type) if (IS_ERR(phy)) { pr_err("unable to find transceiver of type %s\n", usb_phy_type_string(type)); - return phy; + goto err0; } get_device(phy->dev); +err0: spin_unlock_irqrestore(&phy_lock, flags); return phy; -- cgit v1.2.3-70-g09d2 From e864abed546f9f4b76a3580fb3c53cd389e0c015 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Jun 2012 16:49:42 +0900 Subject: USB: ohci-exynos: add clock gating to suspend/resume This patch adds clock gating to suspend and resume functions. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 2909621ea19..fedb0eff35d 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -225,6 +225,9 @@ static int exynos_ohci_suspend(struct device *dev) if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + + clk_disable(exynos_ohci->clk); + fail: spin_unlock_irqrestore(&ohci->lock, flags); @@ -238,6 +241,8 @@ static int exynos_ohci_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; + clk_enable(exynos_ohci->clk); + if (pdata && pdata->phy_init) pdata->phy_init(pdev, S5P_USB_PHY_HOST); -- cgit v1.2.3-70-g09d2 From 390a0a78067c487609ba5bd18c264f7d5b6f4e96 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Jun 2012 16:30:30 +0900 Subject: USB: ohci-exynos: use devm_ functions The devm_ functions allocate memory that is released when a driver detaches. This makes the code smaller and a bit simpler. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index fedb0eff35d..8bcbdb5ade2 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -87,7 +87,8 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) return -EINVAL; } - exynos_ohci = kzalloc(sizeof(struct exynos_ohci_hcd), GFP_KERNEL); + exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd), + GFP_KERNEL); if (!exynos_ohci) return -ENOMEM; @@ -97,8 +98,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); - err = -ENOMEM; - goto fail_hcd; + return -ENOMEM; } exynos_ohci->hcd = hcd; @@ -123,7 +123,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->regs = ioremap(res->start, resource_size(res)); + hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len); if (!hcd->regs) { dev_err(&pdev->dev, "Failed to remap I/O memory\n"); err = -ENOMEM; @@ -134,7 +134,7 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) if (!irq) { dev_err(&pdev->dev, "Failed to get IRQ\n"); err = -ENODEV; - goto fail; + goto fail_io; } if (pdata->phy_init) @@ -146,23 +146,19 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail; + goto fail_io; } platform_set_drvdata(pdev, exynos_ohci); return 0; -fail: - iounmap(hcd->regs); fail_io: clk_disable(exynos_ohci->clk); fail_clken: clk_put(exynos_ohci->clk); fail_clk: usb_put_hcd(hcd); -fail_hcd: - kfree(exynos_ohci); return err; } @@ -177,13 +173,10 @@ static int __devexit exynos_ohci_remove(struct platform_device *pdev) if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); - iounmap(hcd->regs); - clk_disable(exynos_ohci->clk); clk_put(exynos_ohci->clk); usb_put_hcd(hcd); - kfree(exynos_ohci); return 0; } -- cgit v1.2.3-70-g09d2 From 9cb07563721cb05f91b517aefd70b57ba8a1d5aa Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Jun 2012 16:29:46 +0900 Subject: USB: ehci-s5p: use devm_ functions The devm_ functions allocate memory that is released when a driver detaches. This makes the code smaller and a bit simpler. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index c474cec064e..1e483f052ff 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -79,7 +79,8 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) return -EINVAL; } - s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL); + s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd), + GFP_KERNEL); if (!s5p_ehci) return -ENOMEM; @@ -89,8 +90,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); - err = -ENOMEM; - goto fail_hcd; + return -ENOMEM; } s5p_ehci->hcd = hcd; @@ -115,7 +115,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->regs = ioremap(res->start, resource_size(res)); + hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len); if (!hcd->regs) { dev_err(&pdev->dev, "Failed to remap I/O memory\n"); err = -ENOMEM; @@ -126,7 +126,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) if (!irq) { dev_err(&pdev->dev, "Failed to get IRQ\n"); err = -ENODEV; - goto fail; + goto fail_io; } if (pdata->phy_init) @@ -151,23 +151,19 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail; + goto fail_io; } platform_set_drvdata(pdev, s5p_ehci); return 0; -fail: - iounmap(hcd->regs); fail_io: clk_disable(s5p_ehci->clk); fail_clken: clk_put(s5p_ehci->clk); fail_clk: usb_put_hcd(hcd); -fail_hcd: - kfree(s5p_ehci); return err; } @@ -182,13 +178,10 @@ static int __devexit s5p_ehci_remove(struct platform_device *pdev) if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); - iounmap(hcd->regs); - clk_disable(s5p_ehci->clk); clk_put(s5p_ehci->clk); usb_put_hcd(hcd); - kfree(s5p_ehci); return 0; } -- cgit v1.2.3-70-g09d2 From 02f824ac75d1c861aae59be5d6d739043c2066e7 Mon Sep 17 00:00:00 2001 From: Jeffrin Jose Date: Sun, 1 Jul 2012 21:25:16 +0530 Subject: USB: class: cdc-acm: Fixed coding style issue. Fixed coding style issue related to prohibited space in drivers/usb/class/cdc-acm.c Signed-off-by: Jeffrin Jose Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 36a2a0b7b82..56d6bf66848 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -996,7 +996,7 @@ static int acm_probe(struct usb_interface *intf, case USB_CDC_CALL_MANAGEMENT_TYPE: call_management_function = buffer[3]; call_interface_num = buffer[4]; - if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) + if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); break; default: -- cgit v1.2.3-70-g09d2 From 2102e06a5f2e414694921f23591f072a5ba7db9f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:01 +0200 Subject: usbdevfs: Correct amount of data copied to user in processcompl_compat iso data buffers may have holes in them if some packets were short, so for iso urbs we should always copy the entire buffer, just like the regular processcompl does. Signed-off-by: Hans de Goede Acked-by: Alan Stern CC: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e0f107948eb..62679bc031f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1604,10 +1604,14 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) void __user *addr = as->userurb; unsigned int i; - if (as->userbuffer && urb->actual_length) - if (copy_to_user(as->userbuffer, urb->transfer_buffer, - urb->actual_length)) + if (as->userbuffer && urb->actual_length) { + if (urb->number_of_packets > 0) /* Isochronous */ + i = urb->transfer_buffer_length; + else /* Non-Isoc */ + i = urb->actual_length; + if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) return -EFAULT; + } if (put_user(as->status, &userurb->status)) return -EFAULT; if (put_user(urb->actual_length, &userurb->actual_length)) -- cgit v1.2.3-70-g09d2 From 19181bc50e1b8e92a7a3b3d78637c6dc5c0b5a1b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:02 +0200 Subject: usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl There are a few (new) usbdevfs capabilities which an application cannot discover in any other way then checking the kernel version. There are 3 problems with this: 1) It is just not very pretty. 2) Given the tendency of enterprise distros to backport stuff it is not reliable. 3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK (which is its intended use) on devices attached to an XHCI controller. So the availability of these features can be host controller dependent, making depending on them based on the kernel version not a good idea. This patch besides adding the new ioctl also adds flags for the following existing capabilities: USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31 USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3 Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION cap for XHCI controllers, bulk transfers with this flag set will still be accepted when submitted to XHCI controllers. Returning -EINVAL for them would break existing apps, and in most cases the troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers will never get hit, so this would break working use cases. The disadvantage of not returning -EINVAL is that cases were it is causing real trouble may go undetected / the cause of the trouble may be unclear, but this is the best we can do. Signed-off-by: Hans de Goede Acked-by: Alan Stern Acked-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 17 +++++++++++++++++ drivers/usb/host/xhci.c | 2 ++ include/linux/usb.h | 5 +++++ include/linux/usbdevice_fs.h | 7 +++++++ 4 files changed, 31 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 62679bc031f..0b387c1a8b7 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1824,6 +1824,20 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) return usb_hub_release_port(ps->dev, portnum, ps); } +static int proc_get_capabilities(struct dev_state *ps, void __user *arg) +{ + __u32 caps; + + caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; + if (!ps->dev->bus->no_stop_on_short) + caps |= USBDEVFS_CAP_BULK_CONTINUATION; + + if (put_user(caps, (__u32 __user *)arg)) + return -EFAULT; + + return 0; +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1994,6 +2008,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); ret = proc_release_port(ps, p); break; + case USBDEVFS_GET_CAPABILITIES: + ret = proc_get_capabilities(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a979cd0dbe0..7648b2d4b26 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4450,6 +4450,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; + /* XHCI controllers don't stop the ep queue on short packets :| */ + hcd->self.no_stop_on_short = 1; if (usb_hcd_is_primary_hcd(hcd)) { xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); diff --git a/include/linux/usb.h b/include/linux/usb.h index f717fbdaee8..d4f9de1acd4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -331,6 +331,11 @@ struct usb_bus { u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ + unsigned no_stop_on_short:1; /* + * Quirk: some controllers don't stop + * the ep queue on a short transfer + * with the URB_SHORT_NOT_OK flag set. + */ unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 15591d2ea40..07b2ceaaad7 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -125,6 +125,11 @@ struct usbdevfs_hub_portinfo { char port [127]; /* e.g. port 3 connects to device 27 */ }; +/* Device capability flags */ +#define USBDEVFS_CAP_ZERO_PACKET 0x01 +#define USBDEVFS_CAP_BULK_CONTINUATION 0x02 +#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 + #ifdef __KERNEL__ #ifdef CONFIG_COMPAT #include @@ -204,4 +209,6 @@ struct usbdevfs_ioctl32 { #define USBDEVFS_CONNECT _IO('U', 23) #define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int) #define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int) +#define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32) + #endif /* _LINUX_USBDEVICE_FS_H */ -- cgit v1.2.3-70-g09d2 From 3d97ff63f8997761f12c8fbe8082996c6eeaba1a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:03 +0200 Subject: usbdevfs: Use scatter-gather lists for large bulk transfers When using urb->transfer_buffer we need to allocate physical contiguous buffers for the entire transfer, which is pretty much guaranteed to fail with large transfers. Currently userspace works around this by breaking large transfers into multiple urbs. For large bulk transfers this leads to all kind of complications. This patch makes it possible for userspace to reliable submit large bulk transfers to scatter-gather capable host controllers in one go, by using a scatterlist to break the transfer up in managable chunks. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 152 ++++++++++++++++++++++++++++++++++--------- include/linux/usbdevice_fs.h | 1 + 2 files changed, 122 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0b387c1a8b7..ebb8a9de8b5 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #define USB_MAXBUS 64 #define USB_DEVICE_MAX USB_MAXBUS * 128 +#define USB_SG_SIZE 16384 /* split-size for large txs */ /* Mutual exclusion for removal, open, and release */ DEFINE_MUTEX(usbfs_mutex); @@ -285,9 +287,16 @@ static struct async *alloc_async(unsigned int numisoframes) static void free_async(struct async *as) { + int i; + put_pid(as->pid); if (as->cred) put_cred(as->cred); + for (i = 0; i < as->urb->num_sgs; i++) { + if (sg_page(&as->urb->sg[i])) + kfree(sg_virt(&as->urb->sg[i])); + } + kfree(as->urb->sg); kfree(as->urb->transfer_buffer); kfree(as->urb->setup_packet); usb_free_urb(as->urb); @@ -388,6 +397,53 @@ static void snoop_urb(struct usb_device *udev, } } +static void snoop_urb_data(struct urb *urb, unsigned len) +{ + int i, size; + + if (!usbfs_snoop) + return; + + if (urb->num_sgs == 0) { + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, + urb->transfer_buffer, len, 1); + return; + } + + for (i = 0; i < urb->num_sgs && len; i++) { + size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, + sg_virt(&urb->sg[i]), size, 1); + len -= size; + } +} + +static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb) +{ + unsigned i, len, size; + + if (urb->number_of_packets > 0) /* Isochronous */ + len = urb->transfer_buffer_length; + else /* Non-Isoc */ + len = urb->actual_length; + + if (urb->num_sgs == 0) { + if (copy_to_user(userbuffer, urb->transfer_buffer, len)) + return -EFAULT; + return 0; + } + + for (i = 0; i < urb->num_sgs && len; i++) { + size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; + if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size)) + return -EFAULT; + userbuffer += size; + len -= size; + } + + return 0; +} + #define AS_CONTINUATION 1 #define AS_UNLINK 2 @@ -454,9 +510,10 @@ static void async_completed(struct urb *urb) } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, - as->status, COMPLETE, - ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ? - NULL : urb->transfer_buffer, urb->actual_length); + as->status, COMPLETE, NULL, 0); + if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN) + snoop_urb_data(urb, urb->actual_length); + if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && as->status != -ENOENT) cancel_bulk_urbs(ps, as->bulk_addr); @@ -1114,8 +1171,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct async *as = NULL; struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; - int ret, ifnum = -1; - int is_in; + int i, ret, is_in, num_sgs = 0, ifnum = -1; + void *buf; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | USBDEVFS_URB_SHORT_NOT_OK | @@ -1199,6 +1256,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, goto interrupt_urb; } uurb->number_of_packets = 0; + num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); + if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) + num_sgs = 0; break; case USBDEVFS_URB_TYPE_INTERRUPT: @@ -1255,26 +1315,67 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ret = -ENOMEM; goto error; } - u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length; + + u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length + + num_sgs * sizeof(struct scatterlist); ret = usbfs_increase_memory_usage(u); if (ret) goto error; as->mem_usage = u; - if (uurb->buffer_length > 0) { + if (num_sgs) { + as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), + GFP_KERNEL); + if (!as->urb->sg) { + ret = -ENOMEM; + goto error; + } + as->urb->num_sgs = num_sgs; + sg_init_table(as->urb->sg, as->urb->num_sgs); + + totlen = uurb->buffer_length; + for (i = 0; i < as->urb->num_sgs; i++) { + u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen; + buf = kmalloc(u, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto error; + } + sg_set_buf(&as->urb->sg[i], buf, u); + + if (!is_in) { + if (copy_from_user(buf, uurb->buffer, u)) { + ret = -EFAULT; + goto error; + } + } + totlen -= u; + } + } else if (uurb->buffer_length > 0) { as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL); if (!as->urb->transfer_buffer) { ret = -ENOMEM; goto error; } - /* Isochronous input data may end up being discontiguous - * if some of the packets are short. Clear the buffer so - * that the gaps don't leak kernel data to userspace. - */ - if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO) + + if (!is_in) { + if (copy_from_user(as->urb->transfer_buffer, + uurb->buffer, + uurb->buffer_length)) { + ret = -EFAULT; + goto error; + } + } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { + /* + * Isochronous input data may end up being + * discontiguous if some of the packets are short. + * Clear the buffer so that the gaps don't leak + * kernel data to userspace. + */ memset(as->urb->transfer_buffer, 0, uurb->buffer_length); + } } as->urb->dev = ps->dev; as->urb->pipe = (uurb->type << 30) | @@ -1328,17 +1429,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->pid = get_pid(task_pid(current)); as->cred = get_current_cred(); security_task_getsecid(current, &as->secid); - if (!is_in && uurb->buffer_length > 0) { - if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, - uurb->buffer_length)) { - ret = -EFAULT; - goto error; - } - } snoop_urb(ps->dev, as->userurb, as->urb->pipe, as->urb->transfer_buffer_length, 0, SUBMIT, - is_in ? NULL : as->urb->transfer_buffer, - uurb->buffer_length); + NULL, 0); + if (!is_in) + snoop_urb_data(as->urb, as->urb->transfer_buffer_length); + async_newpending(as); if (usb_endpoint_xfer_bulk(&ep->desc)) { @@ -1433,11 +1529,7 @@ static int processcompl(struct async *as, void __user * __user *arg) unsigned int i; if (as->userbuffer && urb->actual_length) { - if (urb->number_of_packets > 0) /* Isochronous */ - i = urb->transfer_buffer_length; - else /* Non-Isoc */ - i = urb->actual_length; - if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) + if (copy_urb_data_to_user(as->userbuffer, urb)) goto err_out; } if (put_user(as->status, &userurb->status)) @@ -1605,11 +1697,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) unsigned int i; if (as->userbuffer && urb->actual_length) { - if (urb->number_of_packets > 0) /* Isochronous */ - i = urb->transfer_buffer_length; - else /* Non-Isoc */ - i = urb->actual_length; - if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) + if (copy_urb_data_to_user(as->userbuffer, urb)) return -EFAULT; } if (put_user(as->status, &userurb->status)) @@ -1831,6 +1919,8 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg) caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; if (!ps->dev->bus->no_stop_on_short) caps |= USBDEVFS_CAP_BULK_CONTINUATION; + if (ps->dev->bus->sg_tablesize) + caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER; if (put_user(caps, (__u32 __user *)arg)) return -EFAULT; diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 07b2ceaaad7..3b74666be02 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -129,6 +129,7 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CAP_ZERO_PACKET 0x01 #define USBDEVFS_CAP_BULK_CONTINUATION 0x02 #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 +#define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 #ifdef __KERNEL__ #ifdef CONFIG_COMPAT -- cgit v1.2.3-70-g09d2 From b7283d5a044c010d5e68afccc0598846f254db7b Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 29 Jun 2012 17:48:49 +0800 Subject: usb: chipidea: remove unneeded NULL check As reported by Dan Carpenter, there is a NULL check in udc_start() that follows a dereference of the pointer that's being checked. However, at that point udc pointer shouldn't ever be NULL and if it is, the dereference should cause an oops. Signed-off-by: Alexander Shishkin Reported-by: Dan Carpenter Acked-by: Felipe Balbi Signed-off-by: Richard Zhao Acked-by: Marek Vasut Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4688ab71bd2..80e71021f18 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1648,9 +1648,6 @@ static int udc_start(struct ci13xxx *udc) struct device *dev = udc->dev; int retval = 0; - if (!udc) - return -EINVAL; - spin_lock_init(&udc->lock); udc->gadget.ops = &usb_gadget_ops; -- cgit v1.2.3-70-g09d2 From b2006d91d8e389a04ffbf603b3896af93b0af427 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 29 Jun 2012 17:48:50 +0800 Subject: usb: chipidea: drop useless arch-check msm glue layer compiles on all arches just fine. Let's drop the unnecessary ARCH check so we have easier compile tests. Signed-off-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Richard Zhao Acked-by: Marek Vasut Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index cc349376972..b69900a62a4 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -5,10 +5,12 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o +# Glue/Bridge layers go here + +obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_msm.o + +# PCI doesn't provide stubs, need to check ifneq ($(CONFIG_PCI),) obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o endif -ifneq ($(CONFIG_ARCH_MSM),) - obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_msm.o -endif -- cgit v1.2.3-70-g09d2 From 17d2fcc393bfe58cc1e2bc34603a48e447ff7afb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 29 Jun 2012 17:48:51 +0800 Subject: usb: chipidea: msm: add missing section annotation No functional changes, it will just free up some code if we don't have hotplug. Signed-off-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Richard Zhao Acked-by: Marek Vasut Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci13xxx_msm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 958069ef95e..8d438b82e86 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -55,7 +55,7 @@ static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { .notify_event = ci13xxx_msm_notify_event, }; -static int ci13xxx_msm_probe(struct platform_device *pdev) +static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) { struct platform_device *plat_ci; int ret; -- cgit v1.2.3-70-g09d2 From 6bf83594e3fa1b1147ed1baff356d4fd30846b84 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 29 Jun 2012 17:48:52 +0800 Subject: usb: chipidea: msm: add remove method allow this driver to be removed too. Signed-off-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Richard Zhao Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci13xxx_msm.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 8d438b82e86..11a7befdc4e 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -84,6 +84,8 @@ static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) if (ret) goto put_platform; + platform_set_drvdata(pdev, plat_ci); + pm_runtime_no_callbacks(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -95,16 +97,23 @@ put_platform: return ret; } +static int __devexit ci13xxx_msm_remove(struct platform_device *pdev) +{ + struct platform_device *plat_ci = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + platform_device_unregister(plat_ci); + + return 0; +} + static struct platform_driver ci13xxx_msm_driver = { .probe = ci13xxx_msm_probe, + .remove = __devexit_p(ci13xxx_msm_remove), .driver = { .name = "msm_hsusb", }, }; -MODULE_ALIAS("platform:msm_hsusb"); -static int __init ci13xxx_msm_init(void) -{ - return platform_driver_register(&ci13xxx_msm_driver); -} -module_init(ci13xxx_msm_init); +module_platform_driver(ci13xxx_msm_driver); +MODULE_ALIAS("platform:msm_hsusb"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 77c4400f2f0fd8384ab5cbe41d81ccc664896b2d Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 29 Jun 2012 17:48:53 +0800 Subject: USB: Chipidea: rename struct ci13xxx_udc_driver to struct ci13xxx_platform_data This patch rename struct ci13xxx_udc_driver and var with the type. ci13xxx_platform_data reflect it's passed from platfrom driver. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 4 ++-- drivers/usb/chipidea/ci13xxx_msm.c | 6 +++--- drivers/usb/chipidea/ci13xxx_pci.c | 20 ++++++++++---------- drivers/usb/chipidea/core.c | 12 ++++++------ drivers/usb/chipidea/host.c | 2 +- drivers/usb/chipidea/udc.c | 24 ++++++++++++------------ include/linux/usb/chipidea.h | 2 +- 7 files changed, 35 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 50911f8490d..0b093308548 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -125,7 +125,7 @@ struct hw_bank { * @remote_wakeup: host-enabled remote wakeup * @suspended: suspended by host * @test_mode: the selected test mode - * @udc_driver: platform specific information supplied by parent device + * @platdata: platform specific information supplied by parent device * @vbus_active: is VBUS active * @transceiver: pointer to USB PHY, if any * @hcd: pointer to usb_hcd for ehci host driver @@ -158,7 +158,7 @@ struct ci13xxx { u8 suspended; u8 test_mode; - struct ci13xxx_udc_driver *udc_driver; + struct ci13xxx_platform_data *platdata; int vbus_active; struct usb_phy *transceiver; struct usb_hcd *hcd; diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 11a7befdc4e..12c0dd6a300 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -45,7 +45,7 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) } } -static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { +static struct ci13xxx_platform_data ci13xxx_msm_platdata = { .name = "ci13xxx_msm", .flags = CI13XXX_REGS_SHARED | CI13XXX_REQUIRE_TRANSCEIVER | @@ -75,8 +75,8 @@ static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) goto put_platform; } - ret = platform_device_add_data(plat_ci, &ci13xxx_msm_udc_driver, - sizeof(ci13xxx_msm_udc_driver)); + ret = platform_device_add_data(plat_ci, &ci13xxx_msm_platdata, + sizeof(ci13xxx_msm_platdata)); if (ret) goto put_platform; diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index e3dab27f5c7..cdcac3a0e94 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -23,17 +23,17 @@ /****************************************************************************** * PCI block *****************************************************************************/ -struct ci13xxx_udc_driver pci_driver = { +struct ci13xxx_platform_data pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = DEF_CAPOFFSET, }; -struct ci13xxx_udc_driver langwell_pci_driver = { +struct ci13xxx_platform_data langwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, }; -struct ci13xxx_udc_driver penwell_pci_driver = { +struct ci13xxx_platform_data penwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, .power_budget = 200, @@ -51,12 +51,12 @@ struct ci13xxx_udc_driver penwell_pci_driver = { static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct ci13xxx_udc_driver *driver = (void *)id->driver_data; + struct ci13xxx_platform_data *platdata = (void *)id->driver_data; struct platform_device *plat_ci; struct resource res[3]; int retval = 0, nres = 2; - if (!driver) { + if (!platdata) { dev_err(&pdev->dev, "device doesn't provide driver data\n"); return -ENODEV; } @@ -95,7 +95,7 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, goto put_platform; } - retval = platform_device_add_data(plat_ci, driver, sizeof(*driver)); + retval = platform_device_add_data(plat_ci, platdata, sizeof(*platdata)); if (retval) goto put_platform; @@ -147,19 +147,19 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { { PCI_DEVICE(0x153F, 0x1004), - .driver_data = (kernel_ulong_t)&pci_driver, + .driver_data = (kernel_ulong_t)&pci_platdata, }, { PCI_DEVICE(0x153F, 0x1006), - .driver_data = (kernel_ulong_t)&pci_driver, + .driver_data = (kernel_ulong_t)&pci_platdata, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), - .driver_data = (kernel_ulong_t)&langwell_pci_driver, + .driver_data = (kernel_ulong_t)&langwell_pci_platdata, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), - .driver_data = (kernel_ulong_t)&penwell_pci_driver, + .driver_data = (kernel_ulong_t)&penwell_pci_platdata, }, { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 15e03b308f8..9a883bd5e11 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -179,7 +179,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) ci->hw_bank.abs = base; ci->hw_bank.cap = ci->hw_bank.abs; - ci->hw_bank.cap += ci->udc_driver->capoffset; + ci->hw_bank.cap += ci->platdata->capoffset; ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap); hw_alloc_regmap(ci, false); @@ -227,11 +227,11 @@ int hw_device_reset(struct ci13xxx *ci, u32 mode) udelay(10); /* not RTOS friendly */ - if (ci->udc_driver->notify_event) - ci->udc_driver->notify_event(ci, + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_RESET_EVENT); - if (ci->udc_driver->flags & CI13XXX_DISABLE_STREAMING) + if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); /* USBMODE should be configured step by step */ @@ -364,7 +364,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) } ci->dev = dev; - ci->udc_driver = dev->platform_data; + ci->platdata = dev->platform_data; ret = hw_device_init(ci, base); if (ret < 0) { @@ -419,7 +419,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, ci); - ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->udc_driver->name, + ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name, ci); if (ret) goto stop; diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 9eacd21c0cd..4a4fdb8c65f 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -116,7 +116,7 @@ static int host_start(struct ci13xxx *ci) hcd->regs = ci->hw_bank.abs; hcd->has_tt = 1; - hcd->power_budget = ci->udc_driver->power_budget; + hcd->power_budget = ci->platdata->power_budget; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 80e71021f18..3094c85dc0b 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1363,7 +1363,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) unsigned long flags; int gadget_ready = 0; - if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS)) + if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) return -EOPNOTSUPP; spin_lock_irqsave(&udc->lock, flags); @@ -1379,8 +1379,8 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) hw_device_state(udc, udc->ep0out->qh.dma); } else { hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, + if (udc->platdata->notify_event) + udc->platdata->notify_event(udc, CI13XXX_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&udc->gadget); pm_runtime_put_sync(&_gadget->dev); @@ -1515,9 +1515,9 @@ static int ci13xxx_start(struct usb_gadget *gadget, udc->driver = driver; pm_runtime_get_sync(&udc->gadget.dev); - if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) { + if (udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { if (udc->vbus_active) { - if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) + if (udc->platdata->flags & CI13XXX_REGS_SHARED) hw_device_reset(udc, USBMODE_CM_DC); } else { pm_runtime_put_sync(&udc->gadget.dev); @@ -1545,11 +1545,11 @@ static int ci13xxx_stop(struct usb_gadget *gadget, spin_lock_irqsave(&udc->lock, flags); - if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) || + if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || udc->vbus_active) { hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, + if (udc->platdata->notify_event) + udc->platdata->notify_event(udc, CI13XXX_CONTROLLER_STOPPED_EVENT); udc->driver = NULL; spin_unlock_irqrestore(&udc->lock, flags); @@ -1582,7 +1582,7 @@ static irqreturn_t udc_irq(struct ci13xxx *udc) spin_lock(&udc->lock); - if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) { + if (udc->platdata->flags & CI13XXX_REGS_SHARED) { if (hw_read(udc, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { spin_unlock(&udc->lock); @@ -1654,7 +1654,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.max_speed = USB_SPEED_HIGH; udc->gadget.is_otg = 0; - udc->gadget.name = udc->udc_driver->name; + udc->gadget.name = udc->platdata->name; INIT_LIST_HEAD(&udc->gadget.ep_list); @@ -1687,14 +1687,14 @@ static int udc_start(struct ci13xxx *udc) udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (udc->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { retval = -ENODEV; goto free_pools; } } - if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { + if (!(udc->platdata->flags & CI13XXX_REGS_SHARED)) { retval = hw_device_reset(udc, USBMODE_CM_DC); if (retval) goto put_transceiver; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index edb90d6cfd1..d4cf970656f 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -6,7 +6,7 @@ #define __LINUX_USB_CHIPIDEA_H struct ci13xxx; -struct ci13xxx_udc_driver { +struct ci13xxx_platform_data { const char *name; /* offset of the capability registers */ uintptr_t capoffset; -- cgit v1.2.3-70-g09d2 From 336c5c310e8f0d5baba7973765339eaf5d989fe1 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Fri, 6 Jul 2012 14:13:52 +0800 Subject: usb: convert port_owners type from void * to struct dev_state * This patch is to convert port_owners type from void * to struct dev_state * in order to make code more readable. Acked-by: Alan Stern Signed-off-by: Lan Tianyu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 21 ++++++++++++--------- drivers/usb/core/usb.h | 9 ++++++--- 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 25a7422ee65..4cc8dc96940 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -81,7 +81,7 @@ struct usb_hub { u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; struct delayed_work init_work; - void **port_owners; + struct dev_state **port_owners; }; static inline int hub_is_superspeed(struct usb_device *hdev) @@ -1271,7 +1271,8 @@ static int hub_configure(struct usb_hub *hub, hdev->children = kzalloc(hdev->maxchild * sizeof(struct usb_device *), GFP_KERNEL); - hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL); + hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *), + GFP_KERNEL); if (!hdev->children || !hub->port_owners) { ret = -ENOMEM; goto fail; @@ -1649,7 +1650,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) * to one of these "claimed" ports, the program will "own" the device. */ static int find_port_owner(struct usb_device *hdev, unsigned port1, - void ***ppowner) + struct dev_state ***ppowner) { if (hdev->state == USB_STATE_NOTATTACHED) return -ENODEV; @@ -1664,10 +1665,11 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1, } /* In the following three functions, the caller must hold hdev's lock */ -int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner) +int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, + struct dev_state *owner) { int rc; - void **powner; + struct dev_state **powner; rc = find_port_owner(hdev, port1, &powner); if (rc) @@ -1678,10 +1680,11 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner) return rc; } -int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner) +int usb_hub_release_port(struct usb_device *hdev, unsigned port1, + struct dev_state *owner) { int rc; - void **powner; + struct dev_state **powner; rc = find_port_owner(hdev, port1, &powner); if (rc) @@ -1692,10 +1695,10 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner) return rc; } -void usb_hub_release_all_ports(struct usb_device *hdev, void *owner) +void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner) { int n; - void **powner; + struct dev_state **powner; n = find_port_owner(hdev, 1, &powner); if (n == 0) { diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 5c5c538ea73..67875a89cfa 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -1,5 +1,7 @@ #include +struct dev_state; + /* Functions local to drivers/usb/core/ */ extern int usb_create_sysfs_dev_files(struct usb_device *dev); @@ -41,10 +43,11 @@ extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_rebind_intf(struct usb_interface *intf); extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port, - void *owner); + struct dev_state *owner); extern int usb_hub_release_port(struct usb_device *hdev, unsigned port, - void *owner); -extern void usb_hub_release_all_ports(struct usb_device *hdev, void *owner); + struct dev_state *owner); +extern void usb_hub_release_all_ports(struct usb_device *hdev, + struct dev_state *owner); extern bool usb_device_is_owned(struct usb_device *udev); extern int usb_hub_init(void); -- cgit v1.2.3-70-g09d2 From c5cf9212a368d88fe1e25797699b167f6daa64a5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 28 Jun 2012 11:19:02 -0400 Subject: EHCI: centralize controller suspend/resume This patch (as1563) removes a lot of duplicated code by moving the EHCI controller suspend/resume routines into the core driver, where the various platform drivers can invoke them as needed. Not only does this simplify these platform drivers, this also makes it easier for other platform drivers to add suspend/resume support in the future. Note: The patch does not touch the ehci-fsl.c file, because its approach to suspend and resume is so different from all the others. It will have to be handled specially by its maintainer. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-au1xxx.c | 73 ++------------------------------ drivers/usb/host/ehci-hcd.c | 89 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-hub.c | 4 +- drivers/usb/host/ehci-msm.c | 19 ++------- drivers/usb/host/ehci-pci.c | 74 ++------------------------------- drivers/usb/host/ehci-platform.c | 7 ++-- drivers/usb/host/ehci-s5p.c | 61 ++------------------------- drivers/usb/host/ehci-sead3.c | 74 ++------------------------------- drivers/usb/host/ehci-spear.c | 61 ++------------------------- 9 files changed, 114 insertions(+), 348 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index bf7441afed1..182d3956590 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -158,28 +158,10 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(10); - - /* Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - spin_lock_irqsave(&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); - - // could save FLADJ in case of Vaux power loss - // ... we'd only use it to handle clock skew + bool do_wakeup = device_may_wakeup(dev); + int rc; + rc = ehci_suspend(hcd, do_wakeup); alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); return rc; @@ -188,56 +170,9 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) static int ehci_hcd_au1xxx_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); alchemy_usb_control(ALCHEMY_USB_EHCI0, 1); - - // maybe restore FLADJ - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* If CF is still set, we maintained PCI Vaux power. - * Just undo the effect of ehci_pci_suspend(). - */ - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - ehci_dbg(ehci, "lost power, restarting\n"); - usb_root_hub_lost_power(hcd->self.root_hub); - - /* Else reset, to cope with power loss or flush-to-storage - * style "resume" having let BIOS kick in during reboot. - */ - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c49fc1e7895..e6823a0cf64 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1242,6 +1242,95 @@ static int ehci_get_frame (struct usb_hcd *hcd) } /*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +/* suspend/resume, section 4.3 */ + +/* These routines handle the generic parts of controller suspend/resume */ + +static int __maybe_unused ehci_suspend(struct usb_hcd *hcd, bool do_wakeup) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + if (time_before(jiffies, ehci->next_statechange)) + msleep(10); + + /* + * Root hub was already suspended. Disable IRQ emission and + * mark HW unaccessible. The PM and USB cores make sure that + * the root hub is either suspended or stopped. + */ + ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); + + spin_lock_irq(&ehci->lock); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + (void) ehci_readl(ehci, &ehci->regs->intr_enable); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + spin_unlock_irq(&ehci->lock); + + return 0; +} + +/* Returns 0 if power was preserved, 1 if power was lost */ +static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + if (time_before(jiffies, ehci->next_statechange)) + msleep(100); + + /* Mark hardware accessible again as we are back to full power by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* + * If CF is still set and we aren't resuming from hibernation + * then we maintained suspend power. + * Just undo the effect of ehci_suspend(). + */ + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF && + !hibernated) { + int mask = INTR_MASK; + + ehci_prepare_ports_for_controller_resume(ehci); + if (!hcd->self.root_hub->do_remote_wakeup) + mask &= ~STS_PCD; + ehci_writel(ehci, mask, &ehci->regs->intr_enable); + ehci_readl(ehci, &ehci->regs->intr_enable); + return 0; + } + + /* + * Else reset, to cope with power loss or resume from hibernation + * having let the firmware kick in during reboot. + */ + usb_root_hub_lost_power(hcd->self.root_hub); + (void) ehci_halt(ehci); + (void) ehci_reset(ehci); + + /* emptying the schedule aborts any urbs */ + spin_lock_irq(&ehci->lock); + if (ehci->reclaim) + end_unlink_async(ehci); + ehci_work(ehci); + spin_unlock_irq(&ehci->lock); + + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + + /* here we "know" root ports should always stay powered */ + ehci_port_power(ehci, 1); + + ehci->rh_state = EHCI_RH_SUSPENDED; + return 1; +} + +#endif + +/*-------------------------------------------------------------------------*/ + /* * The EHCI in ChipIdea HDRC cannot be a separate module or device, * because its registers (and irq) are shared between host/gadget/otg diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index db05e358677..b3e2d66e95b 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) ehci->owned_ports = 0; } -static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci) +static int ehci_port_change(struct ehci_hcd *ehci) { int i = HCS_N_PORTS(ehci->hcs_params); @@ -128,7 +128,7 @@ static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci) return 0; } -static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, +static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup) { int port; diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 6b4ffb598db..17dd9e94001 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -198,24 +198,11 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) static int ehci_msm_pm_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - bool wakeup = device_may_wakeup(dev); + bool do_wakeup = device_may_wakeup(dev); dev_dbg(dev, "ehci-msm PM suspend\n"); - /* - * EHCI helper function has also the same check before manipulating - * port wakeup flags. We do check here the same condition before - * calling the same helper function to avoid bringing hardware - * from Low power mode when there is no need for adjusting port - * wakeup flags. - */ - if (hcd->self.root_hub->do_remote_wakeup && !wakeup) { - pm_runtime_resume(dev); - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), - wakeup); - } - - return 0; + return ehci_suspend(hcd, do_wakeup); } static int ehci_msm_pm_resume(struct device *dev) @@ -223,7 +210,7 @@ static int ehci_msm_pm_resume(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); dev_dbg(dev, "ehci-msm PM resume\n"); - ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 123481793a4..6e767bce060 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -331,29 +331,7 @@ done: static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(10); - - /* Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); - spin_lock_irqsave (&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore (&ehci->lock, flags); - - // could save FLADJ in case of Vaux power loss - // ... we'd only use it to handle clock skew - - return rc; + return ehci_suspend(hcd, do_wakeup); } static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev) @@ -402,54 +380,8 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (usb_is_intel_switchable_ehci(pdev)) ehci_enable_xhci_companion(); - // maybe restore FLADJ - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* If CF is still set and we aren't resuming from hibernation - * then we maintained PCI Vaux power. - * Just undo the effect of ehci_pci_suspend(). - */ - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF && - !hibernated) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - usb_root_hub_lost_power(hcd->self.root_hub); - - /* Else reset, to cope with power loss or flush-to-storage - * style "resume" having let BIOS kick in during reboot. - */ - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - (void) ehci_pci_reinit(ehci, pdev); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; + if (ehci_resume(hcd, hibernated) != 0) + (void) ehci_pci_reinit(ehci, pdev); return 0; } #endif diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index dfe881a34ae..4b1d896d5a2 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -153,17 +153,16 @@ static int __devexit ehci_platform_remove(struct platform_device *dev) static int ehci_platform_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - bool wakeup = device_may_wakeup(dev); + bool do_wakeup = device_may_wakeup(dev); - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup); - return 0; + return ehci_suspend(hcd, do_wakeup); } static int ehci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 1e483f052ff..c7e0936d4a7 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -200,27 +200,12 @@ static int s5p_ehci_suspend(struct device *dev) { struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); + bool do_wakeup = device_may_wakeup(dev); struct platform_device *pdev = to_platform_device(dev); struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; - unsigned long flags; - int rc = 0; + int rc; - if (time_before(jiffies, ehci->next_statechange)) - msleep(20); - - /* - * Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - spin_lock_irqsave(&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); + rc = ehci_suspend(hcd, do_wakeup); if (pdata && pdata->phy_exit) pdata->phy_exit(pdev, S5P_USB_PHY_HOST); @@ -234,7 +219,6 @@ static int s5p_ehci_resume(struct device *dev) { struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct platform_device *pdev = to_platform_device(dev); struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; @@ -246,44 +230,7 @@ static int s5p_ehci_resume(struct device *dev) /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - usb_root_hub_lost_power(hcd->self.root_hub); - - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; - + ehci_resume(hcd, false); return 0; } #else diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c index cc199e87a7a..58c96bd50d2 100644 --- a/drivers/usb/host/ehci-sead3.c +++ b/drivers/usb/host/ehci-sead3.c @@ -160,84 +160,16 @@ static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev) static int ehci_hcd_sead3_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(20); - - /* Root hub was already suspended. Disable irq emission and - * mark HW unaccessible. The PM and USB cores make sure that - * the root hub is either suspended or stopped. - */ - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - spin_lock_irqsave(&ehci->lock, flags); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - (void)ehci_readl(ehci, &ehci->regs->intr_enable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); + bool do_wakeup = device_may_wakeup(dev); - /* could save FLADJ in case of Vaux power loss - * ... we'd only use it to handle clock skew - */ - - return rc; + return ehci_suspend(hcd, do_wakeup); } static int ehci_hcd_sead3_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - - /* maybe restore FLADJ. */ - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - /* Mark hardware accessible again as we are out of D3 state by now */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* If CF is still set, we maintained PCI Vaux power. - * Just undo the effect of ehci_pci_suspend(). - */ - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - ehci_dbg(ehci, "lost power, restarting\n"); - usb_root_hub_lost_power(hcd->self.root_hub); - - /* Else reset, to cope with power loss or flush-to-storage - * style "resume" having let BIOS kick in during reboot. - */ - (void) ehci_halt(ehci); - (void) ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - - ehci->rh_state = EHCI_RH_SUSPENDED; + ehci_resume(hcd, false); return 0; } diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 37ba8c8d2fd..7ed533e6cca 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -97,71 +97,16 @@ static const struct hc_driver ehci_spear_hc_driver = { static int ehci_spear_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - unsigned long flags; - int rc = 0; - - if (time_before(jiffies, ehci->next_statechange)) - msleep(10); - - /* - * Root hub was already suspended. Disable irq emission and mark HW - * unaccessible. The PM and USB cores make sure that the root hub is - * either suspended or stopped. - */ - spin_lock_irqsave(&ehci->lock, flags); - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); - ehci_writel(ehci, 0, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - spin_unlock_irqrestore(&ehci->lock, flags); + bool do_wakeup = device_may_wakeup(dev); - return rc; + return ehci_suspend(hcd, do_wakeup); } static int ehci_spear_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - - if (time_before(jiffies, ehci->next_statechange)) - msleep(100); - - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { - int mask = INTR_MASK; - - ehci_prepare_ports_for_controller_resume(ehci); - - if (!hcd->self.root_hub->do_remote_wakeup) - mask &= ~STS_PCD; - - ehci_writel(ehci, mask, &ehci->regs->intr_enable); - ehci_readl(ehci, &ehci->regs->intr_enable); - return 0; - } - - usb_root_hub_lost_power(hcd->self.root_hub); - - /* - * Else reset, to cope with power loss or flush-to-storage style - * "resume" having let BIOS kick in during reboot. - */ - ehci_halt(ehci); - ehci_reset(ehci); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); + ehci_resume(hcd, false); return 0; } #endif /* CONFIG_PM */ -- cgit v1.2.3-70-g09d2 From fe2072cc1768b0f979195acf19b8ccd381e541c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 9 Jul 2012 15:57:00 +0200 Subject: usb/hcd: Ensure scatter-gather is not used for isoc transfers We don't support sg for isoc transfers, enforce this. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 190b1ec7bdc..bc84106ac05 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1398,7 +1398,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { if (hcd->self.uses_dma) { if (urb->num_sgs) { - int n = dma_map_sg( + int n; + + /* We don't support sg for isoc transfers ! */ + if (usb_endpoint_xfer_isoc(&urb->ep->desc)) { + WARN_ON(1); + return -EINVAL; + } + + n = dma_map_sg( hcd->self.controller, urb->sg, urb->num_sgs, -- cgit v1.2.3-70-g09d2 From 277bd8746bb8d23c00ff8343e58df783544e961e Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 9 Jul 2012 12:04:40 +0530 Subject: drivers: usb: otg: fix build break in twl6030-usb.c Fixed a mistake in the merge conflict resoultion commit(ff9cce) in file twl6030-usb.c Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/twl6030-usb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index a3d0c0405cc..6907d8df7a2 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -302,8 +302,6 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); status = OMAP_MUSB_ID_GROUND; - otg->default_a = true; - twl->phy.state = OTG_STATE_A_IDLE; twl->linkstat = status; omap_musb_mailbox(status); } else { -- cgit v1.2.3-70-g09d2 From 2f0de9d844d6c08a8e9059ed0b8d3d5ab6b7911b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 8 Jul 2012 23:10:28 -0700 Subject: usb: renesas_usbhs: use dmaengine helper functions This patch used dmaengine helper functions instead of using hand setting. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/fifo.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 6ec7f838d7f..d5272d4714c 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -771,22 +771,15 @@ static void xfer_work(struct work_struct *work) struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - struct scatterlist sg; struct dma_async_tx_descriptor *desc; struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt); struct device *dev = usbhs_priv_to_dev(priv); enum dma_transfer_direction dir; - dma_cookie_t cookie; dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; - sg_init_table(&sg, 1); - sg_set_page(&sg, virt_to_page(pkt->dma), - pkt->length, offset_in_page(pkt->dma)); - sg_dma_address(&sg) = pkt->dma + pkt->actual; - sg_dma_len(&sg) = pkt->trans; - - desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir, + desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual, + pkt->trans, dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) return; @@ -794,8 +787,7 @@ static void xfer_work(struct work_struct *work) desc->callback = usbhsf_dma_complete; desc->callback_param = pipe; - cookie = desc->tx_submit(desc); - if (cookie < 0) { + if (dmaengine_submit(desc) < 0) { dev_err(dev, "Failed to submit dma descriptor\n"); return; } -- cgit v1.2.3-70-g09d2 From 26c696c678c4ce180599330999e895cded0f625b Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:40 +0800 Subject: USB: Chipidea: rename struct ci13xxx variables from udc to ci struct ci13xxx represent the controller, which may be device or host, so name its variables as ci. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Reviewed-by: Marek Vasut Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 26 +- drivers/usb/chipidea/ci13xxx_msm.c | 12 +- drivers/usb/chipidea/debug.c | 146 ++++----- drivers/usb/chipidea/udc.c | 628 ++++++++++++++++++------------------- include/linux/usb/chipidea.h | 2 +- 5 files changed, 407 insertions(+), 407 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 0b093308548..9655e3569d4 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -36,7 +36,7 @@ * @name: string description of the endpoint * @qh: queue head for this endpoint * @wedge: is the endpoint wedged - * @udc: pointer to the controller + * @ci: pointer to the controller * @lock: pointer to controller's spinlock * @td_pool: pointer to controller's TD pool */ @@ -54,7 +54,7 @@ struct ci13xxx_ep { int wedge; /* global resources */ - struct ci13xxx *udc; + struct ci13xxx *ci; spinlock_t *lock; struct dma_pool *td_pool; }; @@ -250,9 +250,9 @@ static inline int ffs_nr(u32 x) * * This function returns register contents */ -static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) +static inline u32 hw_read(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask) { - return ioread32(udc->hw_bank.regmap[reg]) & mask; + return ioread32(ci->hw_bank.regmap[reg]) & mask; } /** @@ -261,14 +261,14 @@ static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) * @mask: bitfield mask * @data: new value */ -static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline void hw_write(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask, u32 data) { if (~mask) - data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask) + data = (ioread32(ci->hw_bank.regmap[reg]) & ~mask) | (data & mask); - iowrite32(data, udc->hw_bank.regmap[reg]); + iowrite32(data, ci->hw_bank.regmap[reg]); } /** @@ -278,12 +278,12 @@ static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, * * This function returns register contents */ -static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline u32 hw_test_and_clear(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask) { - u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask; + u32 val = ioread32(ci->hw_bank.regmap[reg]) & mask; - iowrite32(val, udc->hw_bank.regmap[reg]); + iowrite32(val, ci->hw_bank.regmap[reg]); return val; } @@ -295,12 +295,12 @@ static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, * * This function returns register contents */ -static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline u32 hw_test_and_write(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask, u32 data) { - u32 val = hw_read(udc, reg, ~0); + u32 val = hw_read(ci, reg, ~0); - hw_write(udc, reg, mask, data); + hw_write(ci, reg, mask, data); return (val & mask) >> ffs_nr(mask); } diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 12c0dd6a300..5a2fe5f9b6c 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -15,11 +15,11 @@ #include "ci.h" -#define MSM_USB_BASE (udc->hw_bank.abs) +#define MSM_USB_BASE (ci->hw_bank.abs) -static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) +static void ci13xxx_msm_notify_event(struct ci13xxx *ci, unsigned event) { - struct device *dev = udc->gadget.dev.parent; + struct device *dev = ci->gadget.dev.parent; int val; switch (event) { @@ -34,13 +34,13 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. */ - val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL); + val = usb_phy_io_read(ci->transceiver, ULPI_FUNC_CTRL); val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL); + usb_phy_io_write(ci->transceiver, val, ULPI_FUNC_CTRL); break; default: - dev_dbg(dev, "unknown ci13xxx_udc event\n"); + dev_dbg(dev, "unknown ci13xxx event\n"); break; } } diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index c4b3e15532d..c6f50a25756 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -68,15 +68,15 @@ void dbg_interrupt(u32 intmask) * * This function returns number of registers read */ -static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) +static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size) { unsigned i; - if (size > udc->hw_bank.size) - size = udc->hw_bank.size; + if (size > ci->hw_bank.size) + size = ci->hw_bank.size; for (i = 0; i < size; i++) - buf[i] = hw_read(udc, i * sizeof(u32), ~0); + buf[i] = hw_read(ci, i * sizeof(u32), ~0); return size; } @@ -88,18 +88,18 @@ static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) * * This function returns an error code */ -static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) +static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data) { /* align */ addr /= sizeof(u32); - if (addr >= udc->hw_bank.size) + if (addr >= ci->hw_bank.size) return -EINVAL; /* align */ addr *= sizeof(u32); - hw_write(udc, addr, ~0, data); + hw_write(ci, addr, ~0, data); return 0; } @@ -110,13 +110,13 @@ static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) * * This function returns an error code */ -static int hw_intr_clear(struct ci13xxx *udc, int n) +static int hw_intr_clear(struct ci13xxx *ci, int n) { if (n >= REG_BITS) return -EINVAL; - hw_write(udc, OP_USBINTR, BIT(n), 0); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); + hw_write(ci, OP_USBINTR, BIT(n), 0); + hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); return 0; } @@ -127,15 +127,15 @@ static int hw_intr_clear(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_intr_force(struct ci13xxx *udc, int n) +static int hw_intr_force(struct ci13xxx *ci, int n) { if (n >= REG_BITS) return -EINVAL; - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); - hw_write(udc, OP_USBINTR, BIT(n), BIT(n)); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0); + hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); + hw_write(ci, OP_USBINTR, BIT(n), BIT(n)); + hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); + hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0); return 0; } @@ -147,12 +147,12 @@ static int hw_intr_force(struct ci13xxx *udc, int n) static ssize_t show_device(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget *gadget = &udc->gadget; + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget *gadget = &ci->gadget; int n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } @@ -188,8 +188,8 @@ static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); static ssize_t show_driver(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget_driver *driver = udc->driver; + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget_driver *driver = ci->driver; int n = 0; if (attr == NULL || buf == NULL) { @@ -412,22 +412,22 @@ static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); static ssize_t show_inters(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; u32 intr; unsigned i, j, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); /*n += scnprintf(buf + n, PAGE_SIZE - n, - "status = %08x\n", hw_read_intr_status(udc)); + "status = %08x\n", hw_read_intr_status(ci)); n += scnprintf(buf + n, PAGE_SIZE - n, - "enable = %08x\n", hw_read_intr_enable(udc));*/ + "enable = %08x\n", hw_read_intr_enable(ci));*/ n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", isr_statistics.test); @@ -471,7 +471,7 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } @@ -485,31 +485,31 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, static ssize_t store_inters(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned en, bit; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); + dev_err(ci->dev, "EINVAL\n"); goto done; } if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { - dev_err(udc->dev, "<1|0> : enable|disable interrupt\n"); + dev_err(ci->dev, "<1|0> : enable|disable interrupt\n"); goto done; } - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); if (en) { - if (hw_intr_force(udc, bit)) + if (hw_intr_force(ci, bit)) dev_err(dev, "invalid bit number\n"); else isr_statistics.test++; } else { - if (hw_intr_clear(udc, bit)) + if (hw_intr_clear(ci, bit)) dev_err(dev, "invalid bit number\n"); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -524,18 +524,18 @@ static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); static ssize_t show_port_test(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned mode; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); + dev_err(ci->dev, "EINVAL\n"); return 0; } - spin_lock_irqsave(&udc->lock, flags); - mode = hw_port_test_get(udc); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + mode = hw_port_test_get(ci); + spin_unlock_irqrestore(&ci->lock, flags); return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); } @@ -549,24 +549,24 @@ static ssize_t store_port_test(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned mode; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); goto done; } if (sscanf(buf, "%u", &mode) != 1) { - dev_err(udc->dev, ": set port test mode"); + dev_err(ci->dev, ": set port test mode"); goto done; } - spin_lock_irqsave(&udc->lock, flags); - if (hw_port_test_set(udc, mode)) - dev_err(udc->dev, "invalid mode\n"); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (hw_port_test_set(ci, mode)) + dev_err(ci->dev, "invalid mode\n"); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -582,20 +582,20 @@ static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned i, j, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max/2; i++) { - struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i]; + spin_lock_irqsave(&ci->lock, flags); + for (i = 0; i < ci->hw_ep_max/2; i++) { + struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i]; struct ci13xxx_ep *mEpTx = - &udc->ci13xxx_ep[i + udc->hw_ep_max/2]; + &ci->ci13xxx_ep[i + ci->hw_ep_max/2]; n += scnprintf(buf + n, PAGE_SIZE - n, "EP=%02i: RX=%08X TX=%08X\n", i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); @@ -606,7 +606,7 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, *((u32 *)mEpTx->qh.ptr + j)); } } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } @@ -621,25 +621,25 @@ static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); static ssize_t show_registers(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; u32 *dump; unsigned i, k, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL); if (!dump) { - dev_err(udc->dev, "%s: out of memory\n", __func__); + dev_err(ci->dev, "%s: out of memory\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - k = hw_register_read(udc, dump, DUMP_ENTRIES); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + k = hw_register_read(ci, dump, DUMP_ENTRIES); + spin_unlock_irqrestore(&ci->lock, flags); for (i = 0; i < k; i++) { n += scnprintf(buf + n, PAGE_SIZE - n, @@ -660,24 +660,24 @@ static ssize_t store_registers(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long addr, data, flags; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); goto done; } if (sscanf(buf, "%li %li", &addr, &data) != 2) { - dev_err(udc->dev, + dev_err(ci->dev, " : write data to register address\n"); goto done; } - spin_lock_irqsave(&udc->lock, flags); - if (hw_register_write(udc, addr, data)) - dev_err(udc->dev, "invalid address range\n"); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (hw_register_write(ci, addr, data)) + dev_err(ci->dev, "invalid address range\n"); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -693,34 +693,34 @@ static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, static ssize_t show_requests(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; struct list_head *ptr = NULL; struct ci13xxx_req *req = NULL; unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max; i++) - list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) + spin_lock_irqsave(&ci->lock, flags); + for (i = 0; i < ci->hw_ep_max; i++) + list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue) { req = list_entry(ptr, struct ci13xxx_req, queue); n += scnprintf(buf + n, PAGE_SIZE - n, "EP=%02i: TD=%08X %s\n", - i % udc->hw_ep_max/2, (u32)req->dma, - ((i < udc->hw_ep_max/2) ? "RX" : "TX")); + i % ci->hw_ep_max/2, (u32)req->dma, + ((i < ci->hw_ep_max/2) ? "RX" : "TX")); for (j = 0; j < qSize; j++) n += scnprintf(buf + n, PAGE_SIZE - n, " %04X: %08X\n", j, *((u32 *)req->ptr + j)); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 3094c85dc0b..ba8284e2a23 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -67,11 +67,11 @@ static inline int hw_ep_bit(int num, int dir) return num + (dir ? 16 : 0); } -static inline int ep_to_bit(struct ci13xxx *udc, int n) +static inline int ep_to_bit(struct ci13xxx *ci, int n) { - int fill = 16 - udc->hw_ep_max / 2; + int fill = 16 - ci->hw_ep_max / 2; - if (n >= udc->hw_ep_max / 2) + if (n >= ci->hw_ep_max / 2) n += fill; return n; @@ -84,17 +84,17 @@ static inline int ep_to_bit(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_device_state(struct ci13xxx *udc, u32 dma) +static int hw_device_state(struct ci13xxx *ci, u32 dma) { if (dma) { - hw_write(udc, OP_ENDPTLISTADDR, ~0, dma); + hw_write(ci, OP_ENDPTLISTADDR, ~0, dma); /* interrupt, error, port change, reset, sleep/suspend */ - hw_write(udc, OP_USBINTR, ~0, + hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); - hw_write(udc, OP_USBCMD, USBCMD_RS, USBCMD_RS); + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { - hw_write(udc, OP_USBCMD, USBCMD_RS, 0); - hw_write(udc, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + hw_write(ci, OP_USBINTR, ~0, 0); } return 0; } @@ -106,16 +106,16 @@ static int hw_device_state(struct ci13xxx *udc, u32 dma) * * This function returns an error code */ -static int hw_ep_flush(struct ci13xxx *udc, int num, int dir) +static int hw_ep_flush(struct ci13xxx *ci, int num, int dir) { int n = hw_ep_bit(num, dir); do { /* flush any pending transfer */ - hw_write(udc, OP_ENDPTFLUSH, BIT(n), BIT(n)); - while (hw_read(udc, OP_ENDPTFLUSH, BIT(n))) + hw_write(ci, OP_ENDPTFLUSH, BIT(n), BIT(n)); + while (hw_read(ci, OP_ENDPTFLUSH, BIT(n))) cpu_relax(); - } while (hw_read(udc, OP_ENDPTSTAT, BIT(n))); + } while (hw_read(ci, OP_ENDPTSTAT, BIT(n))); return 0; } @@ -127,10 +127,10 @@ static int hw_ep_flush(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) +static int hw_ep_disable(struct ci13xxx *ci, int num, int dir) { - hw_ep_flush(udc, num, dir); - hw_write(udc, OP_ENDPTCTRL + num, + hw_ep_flush(ci, num, dir); + hw_write(ci, OP_ENDPTCTRL + num, dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0); return 0; } @@ -143,7 +143,7 @@ static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) +static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) { u32 mask, data; @@ -166,7 +166,7 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) mask |= ENDPTCTRL_RXE; /* enable */ data |= ENDPTCTRL_RXE; } - hw_write(udc, OP_ENDPTCTRL + num, mask, data); + hw_write(ci, OP_ENDPTCTRL + num, mask, data); return 0; } @@ -177,11 +177,11 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) * * This function returns 1 if endpoint halted */ -static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir) +static int hw_ep_get_halt(struct ci13xxx *ci, int num, int dir) { u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; - return hw_read(udc, OP_ENDPTCTRL + num, mask) ? 1 : 0; + return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0; } /** @@ -191,10 +191,10 @@ static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir) * * This function returns setup status */ -static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n) +static int hw_test_and_clear_setup_status(struct ci13xxx *ci, int n) { - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTSETUPSTAT, BIT(n)); + n = ep_to_bit(ci, n); + return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n)); } /** @@ -205,18 +205,18 @@ static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl) +static int hw_ep_prime(struct ci13xxx *ci, int num, int dir, int is_ctrl) { int n = hw_ep_bit(num, dir); - if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; - hw_write(udc, OP_ENDPTPRIME, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTPRIME, BIT(n), BIT(n)); - while (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + while (hw_read(ci, OP_ENDPTPRIME, BIT(n))) cpu_relax(); - if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; /* status shoult be tested according with manual but it doesn't work */ @@ -232,7 +232,7 @@ static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl) * * This function returns an error code */ -static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) +static int hw_ep_set_halt(struct ci13xxx *ci, int num, int dir, int value) { if (value != 0 && value != 1) return -EINVAL; @@ -243,9 +243,9 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR; /* data toggle - reserved for EP0 but it's in ESS */ - hw_write(udc, reg, mask_xs|mask_xr, + hw_write(ci, reg, mask_xs|mask_xr, value ? mask_xs : mask_xr); - } while (value != hw_ep_get_halt(udc, num, dir)); + } while (value != hw_ep_get_halt(ci, num, dir)); return 0; } @@ -255,10 +255,10 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) * * This function returns true if high speed port */ -static int hw_port_is_high_speed(struct ci13xxx *udc) +static int hw_port_is_high_speed(struct ci13xxx *ci) { - return udc->hw_bank.lpm ? hw_read(udc, OP_DEVLC, DEVLC_PSPD) : - hw_read(udc, OP_PORTSC, PORTSC_HSP); + return ci->hw_bank.lpm ? hw_read(ci, OP_DEVLC, DEVLC_PSPD) : + hw_read(ci, OP_PORTSC, PORTSC_HSP); } /** @@ -266,9 +266,9 @@ static int hw_port_is_high_speed(struct ci13xxx *udc) * * This function returns register data */ -static u32 hw_read_intr_enable(struct ci13xxx *udc) +static u32 hw_read_intr_enable(struct ci13xxx *ci) { - return hw_read(udc, OP_USBINTR, ~0); + return hw_read(ci, OP_USBINTR, ~0); } /** @@ -276,9 +276,9 @@ static u32 hw_read_intr_enable(struct ci13xxx *udc) * * This function returns register data */ -static u32 hw_read_intr_status(struct ci13xxx *udc) +static u32 hw_read_intr_status(struct ci13xxx *ci) { - return hw_read(udc, OP_USBSTS, ~0); + return hw_read(ci, OP_USBSTS, ~0); } /** @@ -288,10 +288,10 @@ static u32 hw_read_intr_status(struct ci13xxx *udc) * * This function returns complete status */ -static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) +static int hw_test_and_clear_complete(struct ci13xxx *ci, int n) { - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTCOMPLETE, BIT(n)); + n = ep_to_bit(ci, n); + return hw_test_and_clear(ci, OP_ENDPTCOMPLETE, BIT(n)); } /** @@ -300,11 +300,11 @@ static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) * * This function returns active interrutps */ -static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) +static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci) { - u32 reg = hw_read_intr_status(udc) & hw_read_intr_enable(udc); + u32 reg = hw_read_intr_status(ci) & hw_read_intr_enable(ci); - hw_write(udc, OP_USBSTS, ~0, reg); + hw_write(ci, OP_USBSTS, ~0, reg); return reg; } @@ -314,9 +314,9 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) +static int hw_test_and_clear_setup_guard(struct ci13xxx *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, 0); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, 0); } /** @@ -325,9 +325,9 @@ static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_set_setup_guard(struct ci13xxx *udc) +static int hw_test_and_set_setup_guard(struct ci13xxx *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); } /** @@ -337,9 +337,9 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *udc) * This function explicitly sets the address, without the "USBADRA" (advance) * feature, which is not supported by older versions of the controller. */ -static void hw_usb_set_address(struct ci13xxx *udc, u8 value) +static void hw_usb_set_address(struct ci13xxx *ci, u8 value) { - hw_write(udc, OP_DEVICEADDR, DEVICEADDR_USBADR, + hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR, value << ffs_nr(DEVICEADDR_USBADR)); } @@ -349,21 +349,21 @@ static void hw_usb_set_address(struct ci13xxx *udc, u8 value) * * This function returns an error code */ -static int hw_usb_reset(struct ci13xxx *udc) +static int hw_usb_reset(struct ci13xxx *ci) { - hw_usb_set_address(udc, 0); + hw_usb_set_address(ci, 0); /* ESS flushes only at end?!? */ - hw_write(udc, OP_ENDPTFLUSH, ~0, ~0); + hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); /* clear setup token semaphores */ - hw_write(udc, OP_ENDPTSETUPSTAT, 0, 0); + hw_write(ci, OP_ENDPTSETUPSTAT, 0, 0); /* clear complete status */ - hw_write(udc, OP_ENDPTCOMPLETE, 0, 0); + hw_write(ci, OP_ENDPTCOMPLETE, 0, 0); /* wait until all bits cleared */ - while (hw_read(udc, OP_ENDPTPRIME, ~0)) + while (hw_read(ci, OP_ENDPTPRIME, ~0)) udelay(10); /* not RTOS friendly */ /* reset all endpoints ? */ @@ -395,7 +395,7 @@ static inline u8 _usb_addr(struct ci13xxx_ep *ep) */ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) { - struct ci13xxx *udc = mEp->udc; + struct ci13xxx *ci = mEp->ci; unsigned i; int ret = 0; unsigned length = mReq->req.length; @@ -418,7 +418,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) if (!mReq->req.no_interrupt) mReq->zptr->token |= TD_IOC; } - ret = usb_gadget_map_request(&udc->gadget, &mReq->req, mEp->dir); + ret = usb_gadget_map_request(&ci->gadget, &mReq->req, mEp->dir); if (ret) return ret; @@ -454,13 +454,13 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) else mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK; wmb(); - if (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + if (hw_read(ci, OP_ENDPTPRIME, BIT(n))) goto done; do { - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); - tmp_stat = hw_read(udc, OP_ENDPTSTAT, BIT(n)); - } while (!hw_read(udc, OP_USBCMD, USBCMD_ATDTW)); - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, 0); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); + tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n)); + } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW)); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; } @@ -472,7 +472,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) wmb(); /* synchronize before ep prime */ - ret = hw_ep_prime(udc, mEp->num, mEp->dir, + ret = hw_ep_prime(ci, mEp->num, mEp->dir, mEp->type == USB_ENDPOINT_XFER_CONTROL); done: return ret; @@ -502,7 +502,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->req.status = 0; - usb_gadget_unmap_request(&mEp->udc->gadget, &mReq->req, mEp->dir); + usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir); mReq->req.status = mReq->ptr->token & TD_STATUS; if ((TD_STATUS_HALTED & mReq->req.status) != 0) @@ -534,7 +534,7 @@ __acquires(mEp->lock) if (mEp == NULL) return -EINVAL; - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); while (!list_empty(&mEp->qh.queue)) { @@ -563,33 +563,33 @@ __acquires(mEp->lock) static int _gadget_stop_activity(struct usb_gadget *gadget) { struct usb_ep *ep; - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->remote_wakeup = 0; - udc->suspended = 0; - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->remote_wakeup = 0; + ci->suspended = 0; + spin_unlock_irqrestore(&ci->lock, flags); /* flush all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_fifo_flush(ep); } - usb_ep_fifo_flush(&udc->ep0out->ep); - usb_ep_fifo_flush(&udc->ep0in->ep); + usb_ep_fifo_flush(&ci->ep0out->ep); + usb_ep_fifo_flush(&ci->ep0in->ep); - if (udc->driver) - udc->driver->disconnect(gadget); + if (ci->driver) + ci->driver->disconnect(gadget); /* make sure to disable all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); } - if (udc->status != NULL) { - usb_ep_free_request(&udc->ep0in->ep, udc->status); - udc->status = NULL; + if (ci->status != NULL) { + usb_ep_free_request(&ci->ep0in->ep, ci->status); + ci->status = NULL; } return 0; @@ -600,36 +600,36 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) *****************************************************************************/ /** * isr_reset_handler: USB reset interrupt handler - * @udc: UDC device + * @ci: UDC device * * This function resets USB engine after a bus reset occurred */ -static void isr_reset_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) +static void isr_reset_handler(struct ci13xxx *ci) +__releases(ci->lock) +__acquires(ci->lock) { int retval; dbg_event(0xFF, "BUS RST", 0); - spin_unlock(&udc->lock); - retval = _gadget_stop_activity(&udc->gadget); + spin_unlock(&ci->lock); + retval = _gadget_stop_activity(&ci->gadget); if (retval) goto done; - retval = hw_usb_reset(udc); + retval = hw_usb_reset(ci); if (retval) goto done; - udc->status = usb_ep_alloc_request(&udc->ep0in->ep, GFP_ATOMIC); - if (udc->status == NULL) + ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC); + if (ci->status == NULL) retval = -ENOMEM; done: - spin_lock(&udc->lock); + spin_lock(&ci->lock); if (retval) - dev_err(udc->dev, "error: %i\n", retval); + dev_err(ci->dev, "error: %i\n", retval); } /** @@ -650,17 +650,17 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) /** * isr_get_status_response: get_status request response - * @udc: udc struct + * @ci: ci struct * @setup: setup request packet * * This function returns an error code */ -static int isr_get_status_response(struct ci13xxx *udc, +static int isr_get_status_response(struct ci13xxx *ci, struct usb_ctrlrequest *setup) __releases(mEp->lock) __acquires(mEp->lock) { - struct ci13xxx_ep *mEp = udc->ep0in; + struct ci13xxx_ep *mEp = ci->ep0in; struct usb_request *req = NULL; gfp_t gfp_flags = GFP_ATOMIC; int dir, num, retval; @@ -684,14 +684,14 @@ __acquires(mEp->lock) if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Assume that device is bus powered for now. */ - *(u16 *)req->buf = udc->remote_wakeup << 1; + *(u16 *)req->buf = ci->remote_wakeup << 1; retval = 0; } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? TX : RX; num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; - *(u16 *)req->buf = hw_ep_get_halt(udc, num, dir); + *(u16 *)req->buf = hw_ep_get_halt(ci, num, dir); } /* else do nothing; reserved for future use */ @@ -723,39 +723,39 @@ __acquires(mEp->lock) static void isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) { - struct ci13xxx *udc = req->context; + struct ci13xxx *ci = req->context; unsigned long flags; - if (udc->setaddr) { - hw_usb_set_address(udc, udc->address); - udc->setaddr = false; + if (ci->setaddr) { + hw_usb_set_address(ci, ci->address); + ci->setaddr = false; } - spin_lock_irqsave(&udc->lock, flags); - if (udc->test_mode) - hw_port_test_set(udc, udc->test_mode); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (ci->test_mode) + hw_port_test_set(ci, ci->test_mode); + spin_unlock_irqrestore(&ci->lock, flags); } /** * isr_setup_status_phase: queues the status phase of a setup transation - * @udc: udc struct + * @ci: ci struct * * This function returns an error code */ -static int isr_setup_status_phase(struct ci13xxx *udc) +static int isr_setup_status_phase(struct ci13xxx *ci) __releases(mEp->lock) __acquires(mEp->lock) { int retval; struct ci13xxx_ep *mEp; - mEp = (udc->ep0_dir == TX) ? udc->ep0out : udc->ep0in; - udc->status->context = udc; - udc->status->complete = isr_setup_status_complete; + mEp = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in; + ci->status->context = ci; + ci->status->complete = isr_setup_status_complete; spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC); + retval = usb_ep_queue(&mEp->ep, ci->status, GFP_ATOMIC); spin_lock(mEp->lock); return retval; @@ -790,7 +790,7 @@ __acquires(mEp->lock) spin_unlock(mEp->lock); if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) && mReq->req.length) - mEpTemp = mEp->udc->ep0in; + mEpTemp = mEp->ci->ep0in; mReq->req.complete(&mEpTemp->ep, &mReq->req); spin_lock(mEp->lock); } @@ -806,48 +806,48 @@ __acquires(mEp->lock) /** * isr_tr_complete_handler: transaction complete interrupt handler - * @udc: UDC descriptor + * @ci: UDC descriptor * * This function handles traffic events */ -static void isr_tr_complete_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) +static void isr_tr_complete_handler(struct ci13xxx *ci) +__releases(ci->lock) +__acquires(ci->lock) { unsigned i; u8 tmode = 0; - for (i = 0; i < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; int type, num, dir, err = -EINVAL; struct usb_ctrlrequest req; if (mEp->ep.desc == NULL) continue; /* not configured */ - if (hw_test_and_clear_complete(udc, i)) { + if (hw_test_and_clear_complete(ci, i)) { err = isr_tr_complete_low(mEp); if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { if (err > 0) /* needs status phase */ - err = isr_setup_status_phase(udc); + err = isr_setup_status_phase(ci); if (err < 0) { dbg_event(_usb_addr(mEp), "ERROR", err); - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, + dev_err(ci->dev, "error: ep_set_halt\n"); - spin_lock(&udc->lock); + spin_lock(&ci->lock); } } } if (mEp->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(udc, i)) + !hw_test_and_clear_setup_status(ci, i)) continue; if (i != 0) { - dev_warn(udc->dev, "ctrl traffic at endpoint %d\n", i); + dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i); continue; } @@ -855,18 +855,18 @@ __acquires(udc->lock) * Flush data and handshake transactions of previous * setup packet. */ - _ep_nuke(udc->ep0out); - _ep_nuke(udc->ep0in); + _ep_nuke(ci->ep0out); + _ep_nuke(ci->ep0in); /* read_setup_packet */ do { - hw_test_and_set_setup_guard(udc); + hw_test_and_set_setup_guard(ci); memcpy(&req, &mEp->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(udc)); + } while (!hw_test_and_clear_setup_guard(ci)); type = req.bRequestType; - udc->ep0_dir = (type & USB_DIR_IN) ? TX : RX; + ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; dbg_setup(_usb_addr(mEp), &req); @@ -881,23 +881,23 @@ __acquires(udc->lock) dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; if (dir) /* TX */ - num += udc->hw_ep_max/2; - if (!udc->ci13xxx_ep[num].wedge) { - spin_unlock(&udc->lock); + num += ci->hw_ep_max/2; + if (!ci->ci13xxx_ep[num].wedge) { + spin_unlock(&ci->lock); err = usb_ep_clear_halt( - &udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); + &ci->ci13xxx_ep[num].ep); + spin_lock(&ci->lock); if (err) break; } - err = isr_setup_status_phase(udc); + err = isr_setup_status_phase(ci); } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && le16_to_cpu(req.wValue) == USB_DEVICE_REMOTE_WAKEUP) { if (req.wLength != 0) break; - udc->remote_wakeup = 0; - err = isr_setup_status_phase(udc); + ci->remote_wakeup = 0; + err = isr_setup_status_phase(ci); } else { goto delegate; } @@ -910,7 +910,7 @@ __acquires(udc->lock) if (le16_to_cpu(req.wLength) != 2 || le16_to_cpu(req.wValue) != 0) break; - err = isr_get_status_response(udc, &req); + err = isr_get_status_response(ci, &req); break; case USB_REQ_SET_ADDRESS: if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) @@ -918,9 +918,9 @@ __acquires(udc->lock) if (le16_to_cpu(req.wLength) != 0 || le16_to_cpu(req.wIndex) != 0) break; - udc->address = (u8)le16_to_cpu(req.wValue); - udc->setaddr = true; - err = isr_setup_status_phase(udc); + ci->address = (u8)le16_to_cpu(req.wValue); + ci->setaddr = true; + err = isr_setup_status_phase(ci); break; case USB_REQ_SET_FEATURE: if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && @@ -932,20 +932,20 @@ __acquires(udc->lock) dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; if (dir) /* TX */ - num += udc->hw_ep_max/2; + num += ci->hw_ep_max/2; - spin_unlock(&udc->lock); - err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); + spin_unlock(&ci->lock); + err = usb_ep_set_halt(&ci->ci13xxx_ep[num].ep); + spin_lock(&ci->lock); if (!err) - isr_setup_status_phase(udc); + isr_setup_status_phase(ci); } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { if (req.wLength != 0) break; switch (le16_to_cpu(req.wValue)) { case USB_DEVICE_REMOTE_WAKEUP: - udc->remote_wakeup = 1; - err = isr_setup_status_phase(udc); + ci->remote_wakeup = 1; + err = isr_setup_status_phase(ci); break; case USB_DEVICE_TEST_MODE: tmode = le16_to_cpu(req.wIndex) >> 8; @@ -955,9 +955,9 @@ __acquires(udc->lock) case TEST_SE0_NAK: case TEST_PACKET: case TEST_FORCE_EN: - udc->test_mode = tmode; + ci->test_mode = tmode; err = isr_setup_status_phase( - udc); + ci); break; default: break; @@ -972,21 +972,21 @@ __acquires(udc->lock) default: delegate: if (req.wLength == 0) /* no data phase */ - udc->ep0_dir = TX; + ci->ep0_dir = TX; - spin_unlock(&udc->lock); - err = udc->driver->setup(&udc->gadget, &req); - spin_lock(&udc->lock); + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); break; } if (err < 0) { dbg_event(_usb_addr(mEp), "ERROR", err); - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, "error: ep_set_halt\n"); - spin_lock(&udc->lock); + dev_err(ci->dev, "error: ep_set_halt\n"); + spin_lock(&ci->lock); } } } @@ -1016,7 +1016,7 @@ static int ep_enable(struct usb_ep *ep, mEp->ep.desc = desc; if (!list_empty(&mEp->qh.queue)) - dev_warn(mEp->udc->dev, "enabling a non-empty endpoint!\n"); + dev_warn(mEp->ci->dev, "enabling a non-empty endpoint!\n"); mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX; mEp->num = usb_endpoint_num(desc); @@ -1044,7 +1044,7 @@ static int ep_enable(struct usb_ep *ep, * is always enabled */ if (mEp->num) - retval |= hw_ep_enable(mEp->udc, mEp->num, mEp->dir, mEp->type); + retval |= hw_ep_enable(mEp->ci, mEp->num, mEp->dir, mEp->type); spin_unlock_irqrestore(mEp->lock, flags); return retval; @@ -1075,7 +1075,7 @@ static int ep_disable(struct usb_ep *ep) dbg_event(_usb_addr(mEp), "DISABLE", 0); retval |= _ep_nuke(mEp); - retval |= hw_ep_disable(mEp->udc, mEp->num, mEp->dir); + retval |= hw_ep_disable(mEp->ci, mEp->num, mEp->dir); if (mEp->type == USB_ENDPOINT_XFER_CONTROL) mEp->dir = (mEp->dir == TX) ? RX : TX; @@ -1132,7 +1132,7 @@ static void ep_free_request(struct usb_ep *ep, struct usb_request *req) if (ep == NULL || req == NULL) { return; } else if (!list_empty(&mReq->queue)) { - dev_err(mEp->udc->dev, "freeing queued request\n"); + dev_err(mEp->ci->dev, "freeing queued request\n"); return; } @@ -1157,7 +1157,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, { struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - struct ci13xxx *udc = mEp->udc; + struct ci13xxx *ci = mEp->ci; int retval = 0; unsigned long flags; @@ -1168,12 +1168,12 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { if (req->length) - mEp = (udc->ep0_dir == RX) ? - udc->ep0out : udc->ep0in; + mEp = (ci->ep0_dir == RX) ? + ci->ep0out : ci->ep0in; if (!list_empty(&mEp->qh.queue)) { _ep_nuke(mEp); retval = -EOVERFLOW; - dev_warn(mEp->udc->dev, "endpoint ctrl %X nuked\n", + dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n", _usb_addr(mEp)); } } @@ -1181,14 +1181,14 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, /* first nuke then test link, e.g. previous status has not sent */ if (!list_empty(&mReq->queue)) { retval = -EBUSY; - dev_err(mEp->udc->dev, "request already in queue\n"); + dev_err(mEp->ci->dev, "request already in queue\n"); goto done; } if (req->length > 4 * CI13XXX_PAGE_SIZE) { req->length = 4 * CI13XXX_PAGE_SIZE; retval = -EMSGSIZE; - dev_warn(mEp->udc->dev, "request length truncated\n"); + dev_warn(mEp->ci->dev, "request length truncated\n"); } dbg_queue(_usb_addr(mEp), req, retval); @@ -1231,12 +1231,12 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) dbg_event(_usb_addr(mEp), "DEQUEUE", 0); - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); /* pop request */ list_del_init(&mReq->queue); - usb_gadget_unmap_request(&mEp->udc->gadget, req, mEp->dir); + usb_gadget_unmap_request(&mEp->ci->gadget, req, mEp->dir); req->status = -ECONNRESET; @@ -1278,7 +1278,7 @@ static int ep_set_halt(struct usb_ep *ep, int value) direction = mEp->dir; do { dbg_event(_usb_addr(mEp), "HALT", value); - retval |= hw_ep_set_halt(mEp->udc, mEp->num, mEp->dir, value); + retval |= hw_ep_set_halt(mEp->ci, mEp->num, mEp->dir, value); if (!value) mEp->wedge = 0; @@ -1326,14 +1326,14 @@ static void ep_fifo_flush(struct usb_ep *ep) unsigned long flags; if (ep == NULL) { - dev_err(mEp->udc->dev, "%02X: -EINVAL\n", _usb_addr(mEp)); + dev_err(mEp->ci->dev, "%02X: -EINVAL\n", _usb_addr(mEp)); return; } spin_lock_irqsave(mEp->lock, flags); dbg_event(_usb_addr(mEp), "FFLUSH", 0); - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); spin_unlock_irqrestore(mEp->lock, flags); } @@ -1359,30 +1359,30 @@ static const struct usb_ep_ops usb_ep_ops = { *****************************************************************************/ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); unsigned long flags; int gadget_ready = 0; - if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) + if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) return -EOPNOTSUPP; - spin_lock_irqsave(&udc->lock, flags); - udc->vbus_active = is_active; - if (udc->driver) + spin_lock_irqsave(&ci->lock, flags); + ci->vbus_active = is_active; + if (ci->driver) gadget_ready = 1; - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); if (gadget_ready) { if (is_active) { pm_runtime_get_sync(&_gadget->dev); - hw_device_reset(udc, USBMODE_CM_DC); - hw_device_state(udc, udc->ep0out->qh.dma); + hw_device_reset(ci, USBMODE_CM_DC); + hw_device_state(ci, ci->ep0out->qh.dma); } else { - hw_device_state(udc, 0); - if (udc->platdata->notify_event) - udc->platdata->notify_event(udc, + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_STOPPED_EVENT); - _gadget_stop_activity(&udc->gadget); + _gadget_stop_activity(&ci->gadget); pm_runtime_put_sync(&_gadget->dev); } } @@ -1392,31 +1392,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) static int ci13xxx_wakeup(struct usb_gadget *_gadget) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); unsigned long flags; int ret = 0; - spin_lock_irqsave(&udc->lock, flags); - if (!udc->remote_wakeup) { + spin_lock_irqsave(&ci->lock, flags); + if (!ci->remote_wakeup) { ret = -EOPNOTSUPP; goto out; } - if (!hw_read(udc, OP_PORTSC, PORTSC_SUSP)) { + if (!hw_read(ci, OP_PORTSC, PORTSC_SUSP)) { ret = -EINVAL; goto out; } - hw_write(udc, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); + hw_write(ci, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); out: - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return ret; } static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); - if (udc->transceiver) - return usb_phy_set_power(udc->transceiver, mA); + if (ci->transceiver) + return usb_phy_set_power(ci->transceiver, mA); return -ENOTSUPP; } @@ -1437,28 +1437,28 @@ static const struct usb_gadget_ops usb_gadget_ops = { .udc_stop = ci13xxx_stop, }; -static int init_eps(struct ci13xxx *udc) +static int init_eps(struct ci13xxx *ci) { int retval = 0, i, j; - for (i = 0; i < udc->hw_ep_max/2; i++) + for (i = 0; i < ci->hw_ep_max/2; i++) for (j = RX; j <= TX; j++) { - int k = i + j * udc->hw_ep_max/2; - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k]; + int k = i + j * ci->hw_ep_max/2; + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[k]; scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i, (j == TX) ? "in" : "out"); - mEp->udc = udc; - mEp->lock = &udc->lock; - mEp->td_pool = udc->td_pool; + mEp->ci = ci; + mEp->lock = &ci->lock; + mEp->td_pool = ci->td_pool; mEp->ep.name = mEp->name; mEp->ep.ops = &usb_ep_ops; mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; INIT_LIST_HEAD(&mEp->qh.queue); - mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL, + mEp->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, &mEp->qh.dma); if (mEp->qh.ptr == NULL) retval = -ENOMEM; @@ -1471,14 +1471,14 @@ static int init_eps(struct ci13xxx *udc) */ if (i == 0) { if (j == RX) - udc->ep0out = mEp; + ci->ep0out = mEp; else - udc->ep0in = mEp; + ci->ep0in = mEp; continue; } - list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); + list_add_tail(&mEp->ep.ep_list, &ci->gadget.ep_list); } return retval; @@ -1494,7 +1494,7 @@ static int init_eps(struct ci13xxx *udc) static int ci13xxx_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; int retval = -ENOMEM; @@ -1502,35 +1502,35 @@ static int ci13xxx_start(struct usb_gadget *gadget, return -EINVAL; - udc->ep0out->ep.desc = &ctrl_endpt_out_desc; - retval = usb_ep_enable(&udc->ep0out->ep); + ci->ep0out->ep.desc = &ctrl_endpt_out_desc; + retval = usb_ep_enable(&ci->ep0out->ep); if (retval) return retval; - udc->ep0in->ep.desc = &ctrl_endpt_in_desc; - retval = usb_ep_enable(&udc->ep0in->ep); + ci->ep0in->ep.desc = &ctrl_endpt_in_desc; + retval = usb_ep_enable(&ci->ep0in->ep); if (retval) return retval; - spin_lock_irqsave(&udc->lock, flags); - - udc->driver = driver; - pm_runtime_get_sync(&udc->gadget.dev); - if (udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { - if (udc->vbus_active) { - if (udc->platdata->flags & CI13XXX_REGS_SHARED) - hw_device_reset(udc, USBMODE_CM_DC); + spin_lock_irqsave(&ci->lock, flags); + + ci->driver = driver; + pm_runtime_get_sync(&ci->gadget.dev); + if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { + if (ci->vbus_active) { + if (ci->platdata->flags & CI13XXX_REGS_SHARED) + hw_device_reset(ci, USBMODE_CM_DC); } else { - pm_runtime_put_sync(&udc->gadget.dev); + pm_runtime_put_sync(&ci->gadget.dev); goto done; } } - retval = hw_device_state(udc, udc->ep0out->qh.dma); + retval = hw_device_state(ci, ci->ep0out->qh.dma); if (retval) - pm_runtime_put_sync(&udc->gadget.dev); + pm_runtime_put_sync(&ci->gadget.dev); done: - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return retval; } @@ -1540,25 +1540,25 @@ static int ci13xxx_start(struct usb_gadget *gadget, static int ci13xxx_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); - if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || - udc->vbus_active) { - hw_device_state(udc, 0); - if (udc->platdata->notify_event) - udc->platdata->notify_event(udc, + if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || + ci->vbus_active) { + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_STOPPED_EVENT); - udc->driver = NULL; - spin_unlock_irqrestore(&udc->lock, flags); - _gadget_stop_activity(&udc->gadget); - spin_lock_irqsave(&udc->lock, flags); - pm_runtime_put(&udc->gadget.dev); + ci->driver = NULL; + spin_unlock_irqrestore(&ci->lock, flags); + _gadget_stop_activity(&ci->gadget); + spin_lock_irqsave(&ci->lock, flags); + pm_runtime_put(&ci->gadget.dev); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return 0; } @@ -1567,64 +1567,64 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * BUS block *****************************************************************************/ /** - * udc_irq: udc interrupt handler + * udc_irq: ci interrupt handler * * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ -static irqreturn_t udc_irq(struct ci13xxx *udc) +static irqreturn_t udc_irq(struct ci13xxx *ci) { irqreturn_t retval; u32 intr; - if (udc == NULL) + if (ci == NULL) return IRQ_HANDLED; - spin_lock(&udc->lock); + spin_lock(&ci->lock); - if (udc->platdata->flags & CI13XXX_REGS_SHARED) { - if (hw_read(udc, OP_USBMODE, USBMODE_CM) != + if (ci->platdata->flags & CI13XXX_REGS_SHARED) { + if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); return IRQ_NONE; } } - intr = hw_test_and_clear_intr_active(udc); + intr = hw_test_and_clear_intr_active(ci); dbg_interrupt(intr); if (intr) { /* order defines priority - do NOT change it */ if (USBi_URI & intr) - isr_reset_handler(udc); + isr_reset_handler(ci); if (USBi_PCI & intr) { - udc->gadget.speed = hw_port_is_high_speed(udc) ? + ci->gadget.speed = hw_port_is_high_speed(ci) ? USB_SPEED_HIGH : USB_SPEED_FULL; - if (udc->suspended && udc->driver->resume) { - spin_unlock(&udc->lock); - udc->driver->resume(&udc->gadget); - spin_lock(&udc->lock); - udc->suspended = 0; + if (ci->suspended && ci->driver->resume) { + spin_unlock(&ci->lock); + ci->driver->resume(&ci->gadget); + spin_lock(&ci->lock); + ci->suspended = 0; } } if (USBi_UI & intr) - isr_tr_complete_handler(udc); + isr_tr_complete_handler(ci); if (USBi_SLI & intr) { - if (udc->gadget.speed != USB_SPEED_UNKNOWN && - udc->driver->suspend) { - udc->suspended = 1; - spin_unlock(&udc->lock); - udc->driver->suspend(&udc->gadget); - spin_lock(&udc->lock); + if (ci->gadget.speed != USB_SPEED_UNKNOWN && + ci->driver->suspend) { + ci->suspended = 1; + spin_unlock(&ci->lock); + ci->driver->suspend(&ci->gadget); + spin_lock(&ci->lock); } } retval = IRQ_HANDLED; } else { retval = IRQ_NONE; } - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); return retval; } @@ -1641,109 +1641,109 @@ static void udc_release(struct device *dev) /** * udc_start: initialize gadget role - * @udc: chipidea controller + * @ci: chipidea controller */ -static int udc_start(struct ci13xxx *udc) +static int udc_start(struct ci13xxx *ci) { - struct device *dev = udc->dev; + struct device *dev = ci->dev; int retval = 0; - spin_lock_init(&udc->lock); + spin_lock_init(&ci->lock); - udc->gadget.ops = &usb_gadget_ops; - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->gadget.max_speed = USB_SPEED_HIGH; - udc->gadget.is_otg = 0; - udc->gadget.name = udc->platdata->name; + ci->gadget.ops = &usb_gadget_ops; + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->gadget.max_speed = USB_SPEED_HIGH; + ci->gadget.is_otg = 0; + ci->gadget.name = ci->platdata->name; - INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&ci->gadget.ep_list); - dev_set_name(&udc->gadget.dev, "gadget"); - udc->gadget.dev.dma_mask = dev->dma_mask; - udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; - udc->gadget.dev.parent = dev; - udc->gadget.dev.release = udc_release; + dev_set_name(&ci->gadget.dev, "gadget"); + ci->gadget.dev.dma_mask = dev->dma_mask; + ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; + ci->gadget.dev.parent = dev; + ci->gadget.dev.release = udc_release; /* alloc resources */ - udc->qh_pool = dma_pool_create("ci13xxx_qh", dev, + ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, sizeof(struct ci13xxx_qh), 64, CI13XXX_PAGE_SIZE); - if (udc->qh_pool == NULL) + if (ci->qh_pool == NULL) return -ENOMEM; - udc->td_pool = dma_pool_create("ci13xxx_td", dev, + ci->td_pool = dma_pool_create("ci13xxx_td", dev, sizeof(struct ci13xxx_td), 64, CI13XXX_PAGE_SIZE); - if (udc->td_pool == NULL) { + if (ci->td_pool == NULL) { retval = -ENOMEM; goto free_qh_pool; } - retval = init_eps(udc); + retval = init_eps(ci); if (retval) goto free_pools; - udc->gadget.ep0 = &udc->ep0in->ep; + ci->gadget.ep0 = &ci->ep0in->ep; - udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (udc->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { - if (udc->transceiver == NULL) { + if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (ci->transceiver == NULL) { retval = -ENODEV; goto free_pools; } } - if (!(udc->platdata->flags & CI13XXX_REGS_SHARED)) { - retval = hw_device_reset(udc, USBMODE_CM_DC); + if (!(ci->platdata->flags & CI13XXX_REGS_SHARED)) { + retval = hw_device_reset(ci, USBMODE_CM_DC); if (retval) goto put_transceiver; } - retval = device_register(&udc->gadget.dev); + retval = device_register(&ci->gadget.dev); if (retval) { - put_device(&udc->gadget.dev); + put_device(&ci->gadget.dev); goto put_transceiver; } - retval = dbg_create_files(&udc->gadget.dev); + retval = dbg_create_files(&ci->gadget.dev); if (retval) goto unreg_device; - if (!IS_ERR_OR_NULL(udc->transceiver)) { - retval = otg_set_peripheral(udc->transceiver->otg, - &udc->gadget); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + retval = otg_set_peripheral(ci->transceiver->otg, + &ci->gadget); if (retval) goto remove_dbg; } - retval = usb_add_gadget_udc(dev, &udc->gadget); + retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) goto remove_trans; - pm_runtime_no_callbacks(&udc->gadget.dev); - pm_runtime_enable(&udc->gadget.dev); + pm_runtime_no_callbacks(&ci->gadget.dev); + pm_runtime_enable(&ci->gadget.dev); return retval; remove_trans: - if (!IS_ERR_OR_NULL(udc->transceiver)) { - otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + otg_set_peripheral(ci->transceiver->otg, &ci->gadget); + usb_put_phy(ci->transceiver); } dev_err(dev, "error = %i\n", retval); remove_dbg: - dbg_remove_files(&udc->gadget.dev); + dbg_remove_files(&ci->gadget.dev); unreg_device: - device_unregister(&udc->gadget.dev); + device_unregister(&ci->gadget.dev); put_transceiver: - if (!IS_ERR_OR_NULL(udc->transceiver)) - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) + usb_put_phy(ci->transceiver); free_pools: - dma_pool_destroy(udc->td_pool); + dma_pool_destroy(ci->td_pool); free_qh_pool: - dma_pool_destroy(udc->qh_pool); + dma_pool_destroy(ci->qh_pool); return retval; } @@ -1752,32 +1752,32 @@ free_qh_pool: * * No interrupts active, the IRQ has been released */ -static void udc_stop(struct ci13xxx *udc) +static void udc_stop(struct ci13xxx *ci) { int i; - if (udc == NULL) + if (ci == NULL) return; - usb_del_gadget_udc(&udc->gadget); + usb_del_gadget_udc(&ci->gadget); - for (i = 0; i < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; - dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma); + dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma); } - dma_pool_destroy(udc->td_pool); - dma_pool_destroy(udc->qh_pool); + dma_pool_destroy(ci->td_pool); + dma_pool_destroy(ci->qh_pool); - if (!IS_ERR_OR_NULL(udc->transceiver)) { - otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + otg_set_peripheral(ci->transceiver->otg, NULL); + usb_put_phy(ci->transceiver); } - dbg_remove_files(&udc->gadget.dev); - device_unregister(&udc->gadget.dev); + dbg_remove_files(&ci->gadget.dev); + device_unregister(&ci->gadget.dev); /* my kobject is dynamic, I swear! */ - memset(&udc->gadget, 0, sizeof(udc->gadget)); + memset(&ci->gadget, 0, sizeof(ci->gadget)); } /** diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index d4cf970656f..d2f7e494f5c 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -19,7 +19,7 @@ struct ci13xxx_platform_data { #define CI13XXX_CONTROLLER_RESET_EVENT 0 #define CI13XXX_CONTROLLER_STOPPED_EVENT 1 - void (*notify_event) (struct ci13xxx *udc, unsigned event); + void (*notify_event) (struct ci13xxx *ci, unsigned event); }; /* Default offset of capability registers */ -- cgit v1.2.3-70-g09d2 From cbc6dc2af39e1395564445fd71cfcc1c70a96277 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:41 +0800 Subject: USB: Chipidea: add unified ci13xxx_{add,remove}_device for platform drivers Platform drivers do the similar things to add/remove ci13xxx device, so create a unified one. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci13xxx_msm.c | 34 +++++++----------------------- drivers/usb/chipidea/ci13xxx_pci.c | 34 ++++++------------------------ drivers/usb/chipidea/core.c | 43 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/chipidea.h | 7 +++++++ 4 files changed, 63 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 5a2fe5f9b6c..b01feb3be92 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -58,43 +58,23 @@ static struct ci13xxx_platform_data ci13xxx_msm_platdata = { static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) { struct platform_device *plat_ci; - int ret; dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n"); - plat_ci = platform_device_alloc("ci_hdrc", -1); - if (!plat_ci) { - dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n"); - return -ENOMEM; + plat_ci = ci13xxx_add_device(&pdev->dev, + pdev->resource, pdev->num_resources, + &ci13xxx_msm_platdata); + if (IS_ERR(plat_ci)) { + dev_err(&pdev->dev, "ci13xxx_add_device failed!\n"); + return PTR_ERR(plat_ci); } - ret = platform_device_add_resources(plat_ci, pdev->resource, - pdev->num_resources); - if (ret) { - dev_err(&pdev->dev, "can't add resources to platform device\n"); - goto put_platform; - } - - ret = platform_device_add_data(plat_ci, &ci13xxx_msm_platdata, - sizeof(ci13xxx_msm_platdata)); - if (ret) - goto put_platform; - - ret = platform_device_add(plat_ci); - if (ret) - goto put_platform; - platform_set_drvdata(pdev, plat_ci); pm_runtime_no_callbacks(&pdev->dev); pm_runtime_enable(&pdev->dev); return 0; - -put_platform: - platform_device_put(plat_ci); - - return ret; } static int __devexit ci13xxx_msm_remove(struct platform_device *pdev) @@ -102,7 +82,7 @@ static int __devexit ci13xxx_msm_remove(struct platform_device *pdev) struct platform_device *plat_ci = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); - platform_device_unregister(plat_ci); + ci13xxx_remove_device(plat_ci); return 0; } diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index cdcac3a0e94..918e14971f2 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -75,13 +75,6 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_try_set_mwi(pdev); - plat_ci = platform_device_alloc("ci_hdrc", -1); - if (!plat_ci) { - dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n"); - retval = -ENOMEM; - goto disable_device; - } - memset(res, 0, sizeof(res)); res[0].start = pci_resource_start(pdev, 0); res[0].end = pci_resource_end(pdev, 0); @@ -89,32 +82,17 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, res[1].start = pdev->irq; res[1].flags = IORESOURCE_IRQ; - retval = platform_device_add_resources(plat_ci, res, nres); - if (retval) { - dev_err(&pdev->dev, "can't add resources to platform device\n"); - goto put_platform; + plat_ci = ci13xxx_add_device(&pdev->dev, res, nres, platdata); + if (IS_ERR(plat_ci)) { + dev_err(&pdev->dev, "ci13xxx_add_device failed!\n"); + retval = PTR_ERR(plat_ci); + goto disable_device; } - retval = platform_device_add_data(plat_ci, platdata, sizeof(*platdata)); - if (retval) - goto put_platform; - - dma_set_coherent_mask(&plat_ci->dev, pdev->dev.coherent_dma_mask); - plat_ci->dev.dma_mask = pdev->dev.dma_mask; - plat_ci->dev.dma_parms = pdev->dev.dma_parms; - plat_ci->dev.parent = &pdev->dev; - pci_set_drvdata(pdev, plat_ci); - retval = platform_device_add(plat_ci); - if (retval) - goto put_platform; - return 0; - put_platform: - pci_set_drvdata(pdev, NULL); - platform_device_put(plat_ci); disable_device: pci_disable_device(pdev); done: @@ -133,7 +111,7 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) { struct platform_device *plat_ci = pci_get_drvdata(pdev); - platform_device_unregister(plat_ci); + ci13xxx_remove_device(plat_ci); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 9a883bd5e11..8b9d06fd032 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -332,6 +332,49 @@ static irqreturn_t ci_irq(int irq, void *data) return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci); } +struct platform_device *ci13xxx_add_device(struct device *dev, + struct resource *res, int nres, + struct ci13xxx_platform_data *platdata) +{ + struct platform_device *pdev; + int ret; + + /* FIXME: find a way to choose id */ + pdev = platform_device_alloc("ci_hdrc", -1); + if (!pdev) + return ERR_PTR(-ENOMEM); + + pdev->dev.parent = dev; + pdev->dev.dma_mask = dev->dma_mask; + pdev->dev.dma_parms = dev->dma_parms; + dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask); + + ret = platform_device_add_resources(pdev, res, nres); + if (ret) + goto err; + + ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + + return pdev; + +err: + platform_device_put(pdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(ci13xxx_add_device); + +void ci13xxx_remove_device(struct platform_device *pdev) +{ + platform_device_unregister(pdev); +} +EXPORT_SYMBOL_GPL(ci13xxx_remove_device); + static int __devinit ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index d2f7e494f5c..be078f0bfde 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -25,4 +25,11 @@ struct ci13xxx_platform_data { /* Default offset of capability registers */ #define DEF_CAPOFFSET 0x100 +/* Add ci13xxx device */ +struct platform_device *ci13xxx_add_device(struct device *dev, + struct resource *res, int nres, + struct ci13xxx_platform_data *platdata); +/* Remove ci13xxx device */ +void ci13xxx_remove_device(struct platform_device *pdev); + #endif -- cgit v1.2.3-70-g09d2 From fe6e125e30fb9d93fdfc5e3e3702b9c7c3076194 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:42 +0800 Subject: USB: Chipidea: add ci13xxx device id management We use ida_simple_get and ida_simple_remove to manage the ids. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 8b9d06fd032..39603d7b791 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -332,17 +333,24 @@ static irqreturn_t ci_irq(int irq, void *data) return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci); } +static DEFINE_IDA(ci_ida); + struct platform_device *ci13xxx_add_device(struct device *dev, struct resource *res, int nres, struct ci13xxx_platform_data *platdata) { struct platform_device *pdev; - int ret; + int id, ret; - /* FIXME: find a way to choose id */ - pdev = platform_device_alloc("ci_hdrc", -1); - if (!pdev) - return ERR_PTR(-ENOMEM); + id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); + if (id < 0) + return ERR_PTR(id); + + pdev = platform_device_alloc("ci_hdrc", id); + if (!pdev) { + ret = -ENOMEM; + goto put_id; + } pdev->dev.parent = dev; pdev->dev.dma_mask = dev->dma_mask; @@ -365,6 +373,8 @@ struct platform_device *ci13xxx_add_device(struct device *dev, err: platform_device_put(pdev); +put_id: + ida_simple_remove(&ci_ida, id); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(ci13xxx_add_device); @@ -372,6 +382,7 @@ EXPORT_SYMBOL_GPL(ci13xxx_add_device); void ci13xxx_remove_device(struct platform_device *pdev) { platform_device_unregister(pdev); + ida_simple_remove(&ci_ida, pdev->id); } EXPORT_SYMBOL_GPL(ci13xxx_remove_device); -- cgit v1.2.3-70-g09d2 From ca72ae9bf6b599519576142b4cb209aa79bbf867 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:43 +0800 Subject: usb: chipidea: select USB_EHCI_ROOT_HUB_TT in USB_CHIPIDEA_HOST of Kconfig ci13xxx host needs Root Hub Transaction Translators. Reported-by: Shawn Guo Signed-off-by: Richard Zhao Acked-by: Felipe Balbi Signed-off-by: Alexander Shishkin Acked-by: Marek Vasut Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index fd36dc8b889..8337fb5d988 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -20,6 +20,7 @@ config USB_CHIPIDEA_UDC config USB_CHIPIDEA_HOST bool "ChipIdea host controller" + select USB_EHCI_ROOT_HUB_TT help Say Y here to enable host controller functionality of the ChipIdea driver. -- cgit v1.2.3-70-g09d2 From a2c3d6902f6f9916b5376c44baa8c1d08bf92a27 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:46 +0800 Subject: usb: chipidea: permit driver bindings pass phy pointer Sometimes, the driver bindings may know what phy they use. For example, when using device tree, the usb controller may have a phandler pointing to usb phy. Signed-off-by: Richard Zhao Reviewed-by: Marek Vasut Acked-by: Felipe Balbi Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 2 ++ drivers/usb/chipidea/core.c | 4 ++++ drivers/usb/chipidea/host.c | 1 + drivers/usb/chipidea/udc.c | 11 +++++++---- include/linux/usb/chipidea.h | 3 +++ 5 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 9655e3569d4..d738603a275 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -160,6 +160,8 @@ struct ci13xxx { struct ci13xxx_platform_data *platdata; int vbus_active; + /* FIXME: some day, we'll not use global phy */ + bool global_phy; struct usb_phy *transceiver; struct usb_hcd *hcd; }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 39603d7b791..1083585fad0 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -419,6 +419,10 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) ci->dev = dev; ci->platdata = dev->platform_data; + if (ci->platdata->phy) + ci->transceiver = ci->platdata->phy; + else + ci->global_phy = true; ret = hw_device_init(ci, base); if (ret < 0) { diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 4a4fdb8c65f..ebff9f4f56e 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -117,6 +117,7 @@ static int host_start(struct ci13xxx *ci) hcd->has_tt = 1; hcd->power_budget = ci->platdata->power_budget; + hcd->phy = ci->transceiver; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ba8284e2a23..c7a032a4f0c 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1685,7 +1685,8 @@ static int udc_start(struct ci13xxx *ci) ci->gadget.ep0 = &ci->ep0in->ep; - ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + if (ci->global_phy) + ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (ci->transceiver == NULL) { @@ -1729,7 +1730,8 @@ static int udc_start(struct ci13xxx *ci) remove_trans: if (!IS_ERR_OR_NULL(ci->transceiver)) { otg_set_peripheral(ci->transceiver->otg, &ci->gadget); - usb_put_phy(ci->transceiver); + if (ci->global_phy) + usb_put_phy(ci->transceiver); } dev_err(dev, "error = %i\n", retval); @@ -1738,7 +1740,7 @@ remove_dbg: unreg_device: device_unregister(&ci->gadget.dev); put_transceiver: - if (!IS_ERR_OR_NULL(ci->transceiver)) + if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy) usb_put_phy(ci->transceiver); free_pools: dma_pool_destroy(ci->td_pool); @@ -1772,7 +1774,8 @@ static void udc_stop(struct ci13xxx *ci) if (!IS_ERR_OR_NULL(ci->transceiver)) { otg_set_peripheral(ci->transceiver->otg, NULL); - usb_put_phy(ci->transceiver); + if (ci->global_phy) + usb_put_phy(ci->transceiver); } dbg_remove_files(&ci->gadget.dev); device_unregister(&ci->gadget.dev); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index be078f0bfde..544825dde82 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -5,12 +5,15 @@ #ifndef __LINUX_USB_CHIPIDEA_H #define __LINUX_USB_CHIPIDEA_H +#include + struct ci13xxx; struct ci13xxx_platform_data { const char *name; /* offset of the capability registers */ uintptr_t capoffset; unsigned power_budget; + struct usb_phy *phy; unsigned long flags; #define CI13XXX_REGS_SHARED BIT(0) #define CI13XXX_REQUIRE_TRANSCEIVER BIT(1) -- cgit v1.2.3-70-g09d2 From b3d99681227a8c5e1f50bccae0f55930aad2ca77 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:47 +0800 Subject: usb: otg: add basic mxs phy driver support mxs phy is used in Freescale i.MX SoCs, for example imx23, imx28, imx6Q. This patch adds the basic host support. Signed-off-by: Richard Zhao Signed-off-by: Marek Vasut Cc: Peter Chen Acked-by: Felipe Balbi Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/mxs-phy.txt | 13 ++ drivers/usb/otg/Kconfig | 10 ++ drivers/usb/otg/Makefile | 1 + drivers/usb/otg/mxs-phy.c | 186 ++++++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/mxs-phy.txt create mode 100644 drivers/usb/otg/mxs-phy.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/usb/mxs-phy.txt b/Documentation/devicetree/bindings/usb/mxs-phy.txt new file mode 100644 index 00000000000..5835b27146e --- /dev/null +++ b/Documentation/devicetree/bindings/usb/mxs-phy.txt @@ -0,0 +1,13 @@ +* Freescale MXS USB Phy Device + +Required properties: +- compatible: Should be "fsl,imx23-usbphy" +- reg: Should contain registers location and length +- interrupts: Should contain phy interrupt + +Example: +usbphy1: usbphy@020c9000 { + compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; + reg = <0x020c9000 0x1000>; + interrupts = <0 44 0x04>; +}; diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 5c87db06b59..13fd1ddf742 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -116,6 +116,16 @@ config FSL_USB2_OTG help Enable this to support Freescale USB OTG transceiver. +config USB_MXS_PHY + tristate "Freescale MXS USB PHY support" + depends on ARCH_MXC || ARCH_MXS + select STMP_DEVICE + select USB_OTG_UTILS + help + Enable this to support the Freescale MXS USB PHY. + + MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x. + config USB_MV_OTG tristate "Marvell USB OTG support" depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 41aa5098b13..a844b8d35d1 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -20,4 +20,5 @@ obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o obj-$(CONFIG_AB8500_USB) += ab8500-usb.o fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o +obj-$(CONFIG_USB_MXS_PHY) += mxs-phy.o obj-$(CONFIG_USB_MV_OTG) += mv_otg.o diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c new file mode 100644 index 00000000000..c1a67cb8e24 --- /dev/null +++ b/drivers/usb/otg/mxs-phy.c @@ -0,0 +1,186 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxs_phy" + +#define HW_USBPHY_PWD 0x00 +#define HW_USBPHY_CTRL 0x30 +#define HW_USBPHY_CTRL_SET 0x34 +#define HW_USBPHY_CTRL_CLR 0x38 + +#define BM_USBPHY_CTRL_SFTRST BIT(31) +#define BM_USBPHY_CTRL_CLKGATE BIT(30) +#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) +#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) +#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) + +struct mxs_phy { + struct usb_phy phy; + struct clk *clk; +}; + +#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) + +static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) +{ + void __iomem *base = mxs_phy->phy.io_priv; + + stmp_reset_block(base + HW_USBPHY_CTRL); + + /* Power up the PHY */ + writel_relaxed(0, base + HW_USBPHY_PWD); + + /* enable FS/LS device */ + writel_relaxed(BM_USBPHY_CTRL_ENUTMILEVEL2 | + BM_USBPHY_CTRL_ENUTMILEVEL3, + base + HW_USBPHY_CTRL_SET); +} + +static int mxs_phy_init(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + + clk_prepare_enable(mxs_phy->clk); + mxs_phy_hw_init(mxs_phy); + + return 0; +} + +static void mxs_phy_shutdown(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + + writel_relaxed(BM_USBPHY_CTRL_CLKGATE, + phy->io_priv + HW_USBPHY_CTRL_SET); + + clk_disable_unprepare(mxs_phy->clk); +} + +static int mxs_phy_on_connect(struct usb_phy *phy, int port) +{ + dev_dbg(phy->dev, "Connect on port %d\n", port); + + mxs_phy_hw_init(to_mxs_phy(phy)); + writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_SET); + + return 0; +} + +static int mxs_phy_on_disconnect(struct usb_phy *phy, int port) +{ + dev_dbg(phy->dev, "Disconnect on port %d\n", port); + + writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_CLR); + + return 0; +} + +static int mxs_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + struct clk *clk; + struct mxs_phy *mxs_phy; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENOENT; + } + + base = devm_request_and_ioremap(&pdev->dev, res); + if (!base) + return -EBUSY; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, + "can't get the clock, err=%ld", PTR_ERR(clk)); + return PTR_ERR(clk); + } + + mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); + if (!mxs_phy) { + dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n"); + return -ENOMEM; + } + + mxs_phy->phy.io_priv = base; + mxs_phy->phy.dev = &pdev->dev; + mxs_phy->phy.label = DRIVER_NAME; + mxs_phy->phy.init = mxs_phy_init; + mxs_phy->phy.shutdown = mxs_phy_shutdown; + mxs_phy->phy.notify_connect = mxs_phy_on_connect; + mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; + + ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); + + mxs_phy->clk = clk; + + platform_set_drvdata(pdev, &mxs_phy->phy); + + return 0; +} + +static int __devexit mxs_phy_remove(struct platform_device *pdev) +{ + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id mxs_phy_dt_ids[] = { + { .compatible = "fsl,imx23-usbphy", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); + +static struct platform_driver mxs_phy_driver = { + .probe = mxs_phy_probe, + .remove = __devexit_p(mxs_phy_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = mxs_phy_dt_ids, + }, +}; + +static int __init mxs_phy_module_init(void) +{ + return platform_driver_register(&mxs_phy_driver); +} +postcore_initcall(mxs_phy_module_init); + +static void __exit mxs_phy_module_exit(void) +{ + platform_driver_unregister(&mxs_phy_driver); +} +module_exit(mxs_phy_module_exit); + +MODULE_ALIAS("platform:mxs-usb-phy"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_AUTHOR("Richard Zhao "); +MODULE_DESCRIPTION("Freescale MXS USB PHY driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 1530280084c3905be6fce802cbfa83fb3bbb8839 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:48 +0800 Subject: usb: chipidea: add imx platform driver This patch supports only the host-mode functionality so far. Signed-off-by: Richard Zhao Signed-off-by: Marek Vasut Cc: Peter Chen Cc: Alexander Shishkin Cc: Felipe Balbi Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/ci13xxx-imx.txt | 18 ++ drivers/usb/chipidea/Makefile | 3 + drivers/usb/chipidea/ci13xxx_imx.c | 198 +++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/ci13xxx-imx.txt create mode 100644 drivers/usb/chipidea/ci13xxx_imx.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt new file mode 100644 index 00000000000..2c290418bb2 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt @@ -0,0 +1,18 @@ +* Freescale i.MX ci13xxx usb controllers + +Required properties: +- compatible: Should be "fsl,imx27-usb" +- reg: Should contain registers location and length +- interrupts: Should contain controller interrupt + +Optional properties: +- fsl,usbphy: phandler of usb phy that connects to the only one port +- vbus-supply: regulator for vbus + +Examples: +usb@02184000 { /* USB OTG */ + compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + reg = <0x02184000 0x200>; + interrupts = <0 43 0x04>; + fsl,usbphy = <&usbphy1>; +}; diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index b69900a62a4..5c66d9c330c 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -14,3 +14,6 @@ ifneq ($(CONFIG_PCI),) obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o endif +ifneq ($(CONFIG_OF_DEVICE),) + obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o +endif diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c new file mode 100644 index 00000000000..ef60d06835d --- /dev/null +++ b/drivers/usb/chipidea/ci13xxx_imx.c @@ -0,0 +1,198 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ci.h" + +#define pdev_to_phy(pdev) \ + ((struct usb_phy *)platform_get_drvdata(pdev)) + +struct ci13xxx_imx_data { + struct device_node *phy_np; + struct usb_phy *phy; + struct platform_device *ci_pdev; + struct clk *clk; + struct regulator *reg_vbus; +}; + +static struct ci13xxx_platform_data ci13xxx_imx_platdata __devinitdata = { + .name = "ci13xxx_imx", + .flags = CI13XXX_REQUIRE_TRANSCEIVER | + CI13XXX_PULLUP_ON_VBUS | + CI13XXX_DISABLE_STREAMING, + .capoffset = DEF_CAPOFFSET, +}; + +static int __devinit ci13xxx_imx_probe(struct platform_device *pdev) +{ + struct ci13xxx_imx_data *data; + struct platform_device *plat_ci, *phy_pdev; + struct device_node *phy_np; + struct resource *res; + struct regulator *reg_vbus; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "Failed to allocate CI13xxx-IMX data!\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Can't get device resources!\n"); + return -ENOENT; + } + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) { + dev_err(&pdev->dev, + "Failed to get clock, err=%ld\n", PTR_ERR(data->clk)); + return PTR_ERR(data->clk); + } + + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, + "Failed to prepare or enable clock, err=%d\n", ret); + return ret; + } + + phy_np = of_parse_phandle(pdev->dev.of_node, "fsl,usbphy", 0); + if (phy_np) { + data->phy_np = phy_np; + phy_pdev = of_find_device_by_node(phy_np); + if (phy_pdev) { + struct usb_phy *phy; + phy = pdev_to_phy(phy_pdev); + if (phy && + try_module_get(phy_pdev->dev.driver->owner)) { + usb_phy_init(phy); + data->phy = phy; + } + } + } + + /* we only support host now, so enable vbus here */ + reg_vbus = devm_regulator_get(&pdev->dev, "vbus"); + if (!IS_ERR(reg_vbus)) { + ret = regulator_enable(reg_vbus); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable vbus regulator, err=%d\n", + ret); + goto put_np; + } + data->reg_vbus = reg_vbus; + } else { + reg_vbus = NULL; + } + + ci13xxx_imx_platdata.phy = data->phy; + + if (!pdev->dev.dma_mask) { + pdev->dev.dma_mask = devm_kzalloc(&pdev->dev, + sizeof(*pdev->dev.dma_mask), GFP_KERNEL); + if (!pdev->dev.dma_mask) { + ret = -ENOMEM; + dev_err(&pdev->dev, "Failed to alloc dma_mask!\n"); + goto err; + } + *pdev->dev.dma_mask = DMA_BIT_MASK(32); + dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask); + } + plat_ci = ci13xxx_add_device(&pdev->dev, + pdev->resource, pdev->num_resources, + &ci13xxx_imx_platdata); + if (IS_ERR(plat_ci)) { + ret = PTR_ERR(plat_ci); + dev_err(&pdev->dev, + "Can't register ci_hdrc platform device, err=%d\n", + ret); + goto err; + } + + data->ci_pdev = plat_ci; + platform_set_drvdata(pdev, data); + + pm_runtime_no_callbacks(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; + +err: + if (reg_vbus) + regulator_disable(reg_vbus); +put_np: + if (phy_np) + of_node_put(phy_np); + clk_disable_unprepare(data->clk); + return ret; +} + +static int __devexit ci13xxx_imx_remove(struct platform_device *pdev) +{ + struct ci13xxx_imx_data *data = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + ci13xxx_remove_device(data->ci_pdev); + + if (data->reg_vbus) + regulator_disable(data->reg_vbus); + + if (data->phy) { + usb_phy_shutdown(data->phy); + module_put(data->phy->dev->driver->owner); + } + + of_node_put(data->phy_np); + + clk_disable_unprepare(data->clk); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id ci13xxx_imx_dt_ids[] = { + { .compatible = "fsl,imx27-usb", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ci13xxx_imx_dt_ids); + +static struct platform_driver ci13xxx_imx_driver = { + .probe = ci13xxx_imx_probe, + .remove = __devexit_p(ci13xxx_imx_remove), + .driver = { + .name = "imx_usb", + .owner = THIS_MODULE, + .of_match_table = ci13xxx_imx_dt_ids, + }, +}; + +module_platform_driver(ci13xxx_imx_driver); + +MODULE_ALIAS("platform:imx-usb"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CI13xxx i.MX USB binding"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_AUTHOR("Richard Zhao "); -- cgit v1.2.3-70-g09d2 From 1a49e2ac9651df7349867a5cf44e2c83de1046af Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 9 Jul 2012 15:55:14 -0400 Subject: EHCI: centralize controller initialization This patch (as1564c) converts the EHCI platform drivers to use the central ehci_setup() routine for generic controller initialization rather than each having its own idiosyncratic approach. The major point of difficulty lies in ehci-pci's many vendor- and device-specific workarounds. Some of them have to be applied before calling ehci_setup() and some after, which necessitates a fair amount of code motion. The other platform drivers require much smaller changes. One point not addressed by the patch is whether ports should be powered on or off following initialization. The different drivers appear to handle this pretty much at random. In fact it shouldn't matter, because the hub driver turns on power to all ports when it binds to the root hub. Straightening that out will be left for another day. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-atmel.c | 19 +---- drivers/usb/host/ehci-au1xxx.c | 14 +--- drivers/usb/host/ehci-cns3xxx.c | 6 +- drivers/usb/host/ehci-fsl.c | 18 +---- drivers/usb/host/ehci-grlib.c | 15 +--- drivers/usb/host/ehci-hcd.c | 5 +- drivers/usb/host/ehci-ixp4xx.c | 6 +- drivers/usb/host/ehci-mv.c | 23 +----- drivers/usb/host/ehci-mxc.c | 17 +--- drivers/usb/host/ehci-octeon.c | 8 +- drivers/usb/host/ehci-omap.c | 104 ++++++++++++------------- drivers/usb/host/ehci-orion.c | 17 +--- drivers/usb/host/ehci-pci.c | 160 +++++++++++++++++++------------------- drivers/usb/host/ehci-pmcmsp.c | 17 +--- drivers/usb/host/ehci-ppc-of.c | 25 +----- drivers/usb/host/ehci-ps3.c | 18 +---- drivers/usb/host/ehci-s5p.c | 12 +-- drivers/usb/host/ehci-sh.c | 16 +--- drivers/usb/host/ehci-spear.c | 10 +-- drivers/usb/host/ehci-tegra.c | 18 +---- drivers/usb/host/ehci-vt8500.c | 14 +--- drivers/usb/host/ehci-w90x900.c | 9 +-- drivers/usb/host/ehci-xilinx-of.c | 31 +------- drivers/usb/host/ehci-xls.c | 21 +---- 24 files changed, 164 insertions(+), 439 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index cf14c95a670..a47e2cffaaf 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -53,30 +53,15 @@ static void atmel_stop_ehci(struct platform_device *pdev) static int ehci_atmel_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval = 0; + int retval; /* registers start at offset 0x0 */ ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - retval = ehci_halt(ehci); - if (retval) - return retval; - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; - ehci->sbrn = 0x20; - - ehci_reset(ehci); ehci_port_power(ehci, 0); return retval; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 182d3956590..cba10d625a5 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -20,10 +20,12 @@ extern int usb_disabled(void); static int au1xxx_ehci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int ret = ehci_init(hcd); + int ret; + + ehci->caps = hcd->regs; + ret = ehci_setup(hcd); ehci->need_io_watchdog = 0; - ehci_reset(ehci); return ret; } @@ -78,7 +80,6 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) { struct usb_hcd *hcd; - struct ehci_hcd *ehci; struct resource *res; int ret; @@ -116,13 +117,6 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) goto err3; } - ehci = hcd_to_ehci(hcd); - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret == 0) { diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c index 6536abdea6e..caaa3e5be33 100644 --- a/drivers/usb/host/ehci-cns3xxx.c +++ b/drivers/usb/host/ehci-cns3xxx.c @@ -33,14 +33,10 @@ static int cns3xxx_ehci_init(struct usb_hcd *hcd) } ehci->caps = hcd->regs; - ehci->regs = hcd->regs - + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 0; - ehci_reset(ehci); - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 74914de8b9b..ab52db684b6 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -348,29 +348,13 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; - ehci->sbrn = 0x20; - - ehci_reset(ehci); - if (of_device_is_compatible(dev->parent->of_node, "fsl,mpc5121-usb2-dr")) { /* diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index fdfd8c5b639..22ca45c079a 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -40,18 +40,13 @@ static int ehci_grlib_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - retval = ehci_halt(ehci); + retval = ehci_setup(hcd); if (retval) return retval; - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; ehci_port_power(ehci, 1); - return ehci_reset(ehci); + return retval; } @@ -164,12 +159,6 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op) ehci->big_endian_capbase = 1; } - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - rv = usb_add_hcd(hcd, irq, 0); if (rv) goto err_ehci; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e6823a0cf64..f9a783bfa1f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -808,7 +808,7 @@ static int ehci_run (struct usb_hcd *hcd) return 0; } -static int __maybe_unused ehci_setup (struct usb_hcd *hcd) +static int ehci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; @@ -832,6 +832,9 @@ static int __maybe_unused ehci_setup (struct usb_hcd *hcd) if (retval) return retval; + if (ehci_is_TDI(ehci)) + tdi_reset(ehci); + ehci_reset(ehci); return 0; diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index c4460f3d009..488d401942e 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -22,14 +22,10 @@ static int ixp4xx_ehci_init(struct usb_hcd *hcd) ehci->big_endian_mmio = 1; ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 - + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - ehci_reset(ehci); - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 0e8c168ca24..f6df1ccc961 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -77,7 +77,6 @@ static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) static int mv_ehci_reset(struct usb_hcd *hcd) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct device *dev = hcd->self.controller; struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev); int retval; @@ -87,25 +86,13 @@ static int mv_ehci_reset(struct usb_hcd *hcd) return -ENODEV; } - /* - * data structure init - */ - retval = ehci_init(hcd); - if (retval) { - dev_err(dev, "ehci_init failed %d\n", retval); - return retval; - } - hcd->has_tt = 1; - ehci->sbrn = 0x20; - retval = ehci_reset(ehci); - if (retval) { - dev_err(dev, "ehci_reset failed %d\n", retval); - return retval; - } + retval = ehci_setup(hcd); + if (retval) + dev_err(dev, "ehci_setup failed %d\n", retval); - return 0; + return retval; } static const struct hc_driver mv_ehci_hc_driver = { @@ -248,8 +235,6 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; - ehci->regs = (struct ehci_regs *) ehci_mv->op_regs; - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index c778ffe4e4e..34201372c85 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -42,27 +42,12 @@ static int ehci_mxc_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - hcd->has_tt = 1; - retval = ehci_halt(ehci); + retval = ehci_setup(hcd); if (retval) return retval; - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - - ehci_reset(ehci); - ehci_port_power(ehci, 0); return 0; } diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index c0104882c72..ba26957abf4 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -56,7 +56,7 @@ static const struct hc_driver ehci_octeon_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -150,12 +150,6 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) #endif ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - ehci_reset(ehci); ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 6e15fc87cf6..6133d93808d 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -145,6 +145,56 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) } } +static int omap_ehci_init(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int rc; + struct ehci_hcd_omap_platform_data *pdata; + + pdata = hcd->self.controller->platform_data; + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_request_one(pdata->reset_gpio_port[0], + GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); + + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_request_one(pdata->reset_gpio_port[1], + GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); + + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(10); + } + + /* Soft reset the PHY using PHY reset command over ULPI */ + if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) + omap_ehci_soft_phy_reset(pdev, 0); + if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) + omap_ehci_soft_phy_reset(pdev, 1); + + /* we know this is the memory we want, no need to ioremap again */ + ehci->caps = hcd->regs; + + rc = ehci_setup(hcd); + + if (pdata->phy_reset) { + /* Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + udelay(10); + + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1); + + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); + } + + /* root ports should always stay powered */ + ehci_port_power(ehci, 1); + + return rc; +} + static int omap_ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, @@ -219,7 +269,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; void __iomem *regs; - struct ehci_hcd *omap_ehci; int ret = -ENODEV; int irq; int i; @@ -281,19 +330,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } } - if (pdata->phy_reset) { - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_request_one(pdata->reset_gpio_port[0], - GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_request_one(pdata->reset_gpio_port[1], - GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); - - /* Hold the PHY in RESET for enough time till DIR is high */ - udelay(10); - } - pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -309,50 +345,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) ehci_write(regs, EHCI_INSNREG04, EHCI_INSNREG04_DISABLE_UNSUSPEND); - /* Soft reset the PHY using PHY reset command over ULPI */ - if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 0); - if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 1); - - omap_ehci = hcd_to_ehci(hcd); - omap_ehci->sbrn = 0x20; - - /* we know this is the memory we want, no need to ioremap again */ - omap_ehci->caps = hcd->regs; - omap_ehci->regs = hcd->regs - + HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase)); - - dbg_hcs_params(omap_ehci, "reset"); - dbg_hcc_params(omap_ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); - - ehci_reset(omap_ehci); - - if (pdata->phy_reset) { - /* Hold the PHY in RESET for enough time till - * PHY is settled and ready - */ - udelay(10); - - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); - } - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_pm_runtime; } - /* root ports should always stay powered */ - ehci_port_power(omap_ehci, 1); - /* get clocks */ utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); if (IS_ERR(utmi_p1_fck)) { @@ -512,7 +510,7 @@ static const struct hc_driver ehci_omap_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = omap_ehci_init, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 82de1073aa5..3e411230953 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -106,21 +106,10 @@ static int ehci_orion_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - hcd->has_tt = 1; - - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* - * data structure init - */ - retval = ehci_init(hcd); + retval = ehci_setup(ehci); if (retval) return retval; - ehci_reset(ehci); - ehci_port_power(ehci, 0); return retval; @@ -261,11 +250,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - ehci->sbrn = 0x20; /* * (Re-)program MBUS remapping windows if we are asked to. diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 6e767bce060..21e5f963f33 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -54,6 +54,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd) u32 temp; int retval; + ehci->caps = hcd->regs; + + /* + * ehci_init() causes memory for DMA transfers to be + * allocated. Thus, any vendor-specific workarounds based on + * limiting the type of memory used for DMA transfers must + * happen before ehci_setup() is called. + * + * Most other workarounds can be done either before or after + * init and reset; they are located here too. + */ switch (pdev->vendor) { case PCI_VENDOR_ID_TOSHIBA_2: /* celleb's companion chip */ @@ -66,20 +77,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) #endif } break; - } - - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* ehci_init() causes memory for DMA transfers to be - * allocated. Thus, any vendor-specific workarounds based on - * limiting the type of memory used for DMA transfers must - * happen before ehci_init() is called. */ - switch (pdev->vendor) { case PCI_VENDOR_ID_NVIDIA: /* NVidia reports that certain chips don't handle * QH, ITD, or SITD addresses above 2GB. (But TD, @@ -95,61 +92,28 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci_warn(ehci, "can't enable NVidia " "workaround for >2GB RAM\n"); break; - } - break; - } - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - retval = ehci_halt(ehci); - if (retval) - return retval; - - if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) || - (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) { - /* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may - * read/write memory space which does not belong to it when - * there is NULL pointer with T-bit set to 1 in the frame list - * table. To avoid the issue, the frame list link pointer - * should always contain a valid pointer to a inactive qh. + /* Some NForce2 chips have problems with selective suspend; + * fixed in newer silicon. */ - ehci->use_dummy_qh = 1; - ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI " - "dummy qh workaround\n"); - } - - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - switch (pdev->vendor) { - case PCI_VENDOR_ID_NEC: - ehci->need_io_watchdog = 0; + case 0x0068: + if (pdev->revision < 0xa4) + ehci->no_selective_suspend = 1; + break; + } break; case PCI_VENDOR_ID_INTEL: - ehci->need_io_watchdog = 0; ehci->fs_i_thresh = 1; if (pdev->device == 0x27cc) { ehci->broken_periodic = 1; ehci_info(ehci, "using broken periodic workaround\n"); } - if (pdev->device == 0x0806 || pdev->device == 0x0811 - || pdev->device == 0x0829) { - ehci_info(ehci, "disable lpm for langwell/penwell\n"); - ehci->has_lpm = 0; - } - if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) { + if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) hcd->has_tt = 1; - tdi_reset(ehci); - } break; case PCI_VENDOR_ID_TDI: - if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { + if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) hcd->has_tt = 1; - tdi_reset(ehci); - } break; case PCI_VENDOR_ID_AMD: /* AMD PLL quirk */ @@ -161,28 +125,17 @@ static int ehci_pci_setup(struct usb_hcd *hcd) retval = -EIO; goto done; } - break; - case PCI_VENDOR_ID_NVIDIA: - switch (pdev->device) { - /* Some NForce2 chips have problems with selective suspend; - * fixed in newer silicon. - */ - case 0x0068: - if (pdev->revision < 0xa4) - ehci->no_selective_suspend = 1; - break; - /* MCP89 chips on the MacBookAir3,1 give EPROTO when - * fetching device descriptors unless LPM is disabled. - * There are also intermittent problems enumerating - * devices with PPCD enabled. + /* + * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may + * read/write memory space which does not belong to it when + * there is NULL pointer with T-bit set to 1 in the frame list + * table. To avoid the issue, the frame list link pointer + * should always contain a valid pointer to a inactive qh. */ - case 0x0d9d: - ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89"); - ehci->has_lpm = 0; - ehci->has_ppcd = 0; - ehci->command &= ~CMD_PPCEE; - break; + if (pdev->device == 0x7808) { + ehci->use_dummy_qh = 1; + ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n"); } break; case PCI_VENDOR_ID_VIA: @@ -203,6 +156,18 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* AMD PLL quirk */ if (usb_amd_find_chipset_info()) ehci->amd_pll_fix = 1; + + /* + * EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may + * read/write memory space which does not belong to it when + * there is NULL pointer with T-bit set to 1 in the frame list + * table. To avoid the issue, the frame list link pointer + * should always contain a valid pointer to a inactive qh. + */ + if (pdev->device == 0x4396) { + ehci->use_dummy_qh = 1; + ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI dummy qh workaround\n"); + } /* SB600 and old version of SB700 have a bug in EHCI controller, * which causes usb devices lose response in some cases. */ @@ -231,6 +196,40 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } + retval = ehci_setup(hcd); + if (retval) + return retval; + + /* These workarounds need to be applied after ehci_setup() */ + switch (pdev->vendor) { + case PCI_VENDOR_ID_NEC: + ehci->need_io_watchdog = 0; + break; + case PCI_VENDOR_ID_INTEL: + ehci->need_io_watchdog = 0; + if (pdev->device == 0x0806 || pdev->device == 0x0811 + || pdev->device == 0x0829) { + ehci_info(ehci, "disable lpm for langwell/penwell\n"); + ehci->has_lpm = 0; + } + break; + case PCI_VENDOR_ID_NVIDIA: + switch (pdev->device) { + /* MCP89 chips on the MacBookAir3,1 give EPROTO when + * fetching device descriptors unless LPM is disabled. + * There are also intermittent problems enumerating + * devices with PPCD enabled. + */ + case 0x0d9d: + ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89"); + ehci->has_lpm = 0; + ehci->has_ppcd = 0; + ehci->command &= ~CMD_PPCEE; + break; + } + break; + } + /* optional debug port, normally in the first BAR */ temp = pci_find_capability(pdev, 0x0a); if (temp) { @@ -238,7 +237,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) temp >>= 16; if ((temp & (3 << 13)) == (1 << 13)) { temp &= 0x1fff; - ehci->debug = ehci_to_hcd(ehci)->regs + temp; + ehci->debug = hcd->regs + temp; temp = ehci_readl(ehci, &ehci->debug->control); ehci_info(ehci, "debug port %d%s\n", HCS_DEBUG_PORT(ehci->hcs_params), @@ -250,8 +249,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) } } - ehci_reset(ehci); - /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; @@ -275,10 +272,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd) } /* Serial Bus Release Number is at PCI 0x60 offset */ - pci_read_config_byte(pdev, 0x60, &ehci->sbrn); if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST) - ehci->sbrn = 0x20; /* ConneXT has no sbrn register */ + ; /* ConneXT has no sbrn register */ + else + pci_read_config_byte(pdev, 0x60, &ehci->sbrn); /* Keep this around for a while just in case some EHCI * implementation uses legacy PCI PM support. This test diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index e8d54de44ac..087aee2a904 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -78,27 +78,14 @@ static int ehci_msp_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; + ehci->big_endian_mmio = 1; ehci->big_endian_desc = 1; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; - retval = ehci_halt(ehci); - if (retval) - return retval; - - ehci_reset(ehci); - - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 41d11fe1425..bbbe89dfd88 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -17,24 +17,6 @@ #include #include -/* called during probe() after chip reset completes */ -static int ehci_ppc_of_setup(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval; - - retval = ehci_halt(ehci); - if (retval) - return retval; - - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - return ehci_reset(ehci); -} - static const struct hc_driver ehci_ppc_of_hc_driver = { .description = hcd_name, @@ -50,7 +32,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_ppc_of_setup, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -178,11 +160,6 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op) ehci->big_endian_desc = 1; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) { rv = ppc44x_enable_bmt(dn); diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index a20e496eb47..45a356e9f13 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -55,28 +55,12 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); ehci->big_endian_mmio = 1; - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, - &ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - result = ehci_halt(ehci); + result = ehci_setup(hcd); if (result) return result; - result = ehci_init(hcd); - - if (result) - return result; - - ehci_reset(ehci); - ps3_ehci_setup_insnreg(ehci); return result; diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index c7e0936d4a7..13c179fb2ee 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -40,7 +40,7 @@ static const struct hc_driver s5p_ehci_hc_driver = { .irq = ehci_irq, .flags = HCD_MEMORY | HCD_USB2, - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -134,20 +134,10 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); - - ehci_reset(ehci); - err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index e7cb3925abf..b3f1e3650da 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -24,25 +24,11 @@ static int ehci_sh_reset(struct usb_hcd *hcd) int ret; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, - &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - ret = ehci_halt(ehci); - if (unlikely(ret)) - return ret; - - ret = ehci_init(hcd); + ret = ehci_setup(hcd); if (unlikely(ret)) return ret; - ehci->sbrn = 0x20; - - ehci_reset(ehci); ehci_port_power(ehci, 0); return ret; diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 7ed533e6cca..c718a065e15 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -41,19 +41,11 @@ static int ehci_spear_setup(struct usb_hcd *hcd) /* registers start at offset 0x0 */ ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, - &ehci->caps->hc_capbase)); - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - retval = ehci_halt(ehci); - if (retval) - return retval; - retval = ehci_init(hcd); + retval = ehci_setup(hcd); if (retval) return retval; - ehci_reset(ehci); ehci_port_power(ehci, 0); return retval; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 477ecfa0515..f7f3ce3275b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -281,30 +281,14 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); /* switch to host mode */ hcd->has_tt = 1; - ehci_reset(ehci); - retval = ehci_halt(ehci); + retval = ehci_setup(ehci); if (retval) return retval; - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - ehci_port_power(ehci, 1); return retval; } diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index c1eda73916c..4d147c4e33f 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -48,7 +48,7 @@ static const struct hc_driver vt8500_ehci_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -121,18 +121,6 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); - - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); - - ehci_port_power(ehci, 1); - - ehci_reset(ehci); ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 3d2e26cbb34..ec598082c14 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -71,21 +71,14 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver, val |= ENPHY; __raw_writel(val, ehci->regs+PHY1_CTR); - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - ehci->sbrn = 0x20; - irq = platform_get_irq(pdev, 0); if (irq < 0) goto err4; - ehci_reset(ehci); - retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) goto err4; - ehci_writel(ehci, 1, &ehci->regs->configured_flag); - return retval; err4: iounmap(hcd->regs); @@ -120,7 +113,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_init, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index e9713d589e3..39f24fa37eb 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -31,30 +31,6 @@ #include #include -/** - * ehci_xilinx_of_setup - Initialize the device for ehci_reset() - * @hcd: Pointer to the usb_hcd device to which the host controller bound - * - * called during probe() after chip reset completes. - */ -static int ehci_xilinx_of_setup(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval; - - retval = ehci_halt(ehci); - if (retval) - return retval; - - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci->sbrn = 0x20; - - return ehci_reset(ehci); -} - /** * ehci_xilinx_port_handed_over - hand the port out if failed to enable it * @hcd: Pointer to the usb_hcd device to which the host controller bound @@ -107,7 +83,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { /* * basic lifecycle operations */ - .reset = ehci_xilinx_of_setup, + .reset = ehci_setup, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, @@ -219,11 +195,6 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op) /* Debug registers are at the first 0x100 region */ ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); rv = usb_add_hcd(hcd, irq, 0); if (rv == 0) diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c index 72f08196f8c..99c353a85ae 100644 --- a/drivers/usb/host/ehci-xls.c +++ b/drivers/usb/host/ehci-xls.c @@ -14,30 +14,11 @@ static int ehci_xls_setup(struct usb_hcd *hcd) { - int retval; struct ehci_hcd *ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - - ehci_reset(ehci); - - return retval; + return ehci_setup(ehci); } int ehci_xls_probe_internal(const struct hc_driver *driver, -- cgit v1.2.3-70-g09d2 From 6d1d051330ee096f575523647fbd8ffe703600b5 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 3 Jul 2012 22:49:04 -0700 Subject: USB: Fix LPM disable/enable during device reset. The USB 3.0 specification says that sending a Set Feature or Clear Feature for U1/U2 Enable is not a valid request when the device is in the Default or Addressed state. It is only valid when the device is in the Configured state. The original LPM patch attempted to disable LPM after the device had been reset by hub_port_init(), before it had the configuration reinstalled. The TI hub I tested with did not fail the Clear Feature U1/U2 Enable request that khubd sent while it was in the addressed state, which is why I didn't catch it. Move the LPM disable before the device reset, so that we can send the Clear Feature U1/U2 Enable successfully, and balance the LPM disable count. Also delete any calls to usb_enable_lpm() on error paths that lead to re-enumeration. The calls will fail because the device isn't configured, and it's not useful to balance the LPM disable count because the usb_device is about to be destroyed before re-enumeration. Fix the early exit path ("done" label) to call usb_enable_lpm() to balance the LPM disable count. Note that calling usb_reset_and_verify_device() with an unconfigured device may fail on the first call to usb_disable_lpm(). That's because the LPM disable count is initialized to 0 (LPM enabled), and usb_disable_lpm() will attempt to send a Clear Feature U1/U2 request to a device in the Addressed state. The next patch will fix that. This commit should be backported to kernels as old as 3.5, that contain the commit 8306095fd2c1100e8244c09bf560f97aca5a311d "USB: Disable USB 3.0 LPM in critical sections." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4cc8dc96940..5c31d2c2f95 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4673,6 +4673,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } parent_hub = hdev_to_hub(parent_hdev); + /* Disable LPM while we reset the device and reinstall the alt settings. + * Device-initiated LPM settings, and system exit latency settings are + * cleared when the device is reset, so we have to set them up again. + */ + ret = usb_unlocked_disable_lpm(udev); + if (ret) { + dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); + goto re_enumerate; + } + set_bit(port1, parent_hub->busy_bits); for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -4700,22 +4710,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev) goto done; mutex_lock(hcd->bandwidth_mutex); - /* Disable LPM while we reset the device and reinstall the alt settings. - * Device-initiated LPM settings, and system exit latency settings are - * cleared when the device is reset, so we have to set them up again. - */ - ret = usb_disable_lpm(udev); - if (ret) { - dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); - mutex_unlock(hcd->bandwidth_mutex); - goto done; - } ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); if (ret < 0) { dev_warn(&udev->dev, "Busted HC? Not enough HCD resources for " "old configuration.\n"); - usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } @@ -4727,7 +4726,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) dev_err(&udev->dev, "can't restore configuration #%d (error=%d)\n", udev->actconfig->desc.bConfigurationValue, ret); - usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); goto re_enumerate; } @@ -4766,17 +4764,17 @@ static int usb_reset_and_verify_device(struct usb_device *udev) desc->bInterfaceNumber, desc->bAlternateSetting, ret); - usb_unlocked_enable_lpm(udev); goto re_enumerate; } } +done: /* Now that the alt settings are re-installed, enable LPM. */ usb_unlocked_enable_lpm(udev); -done: return 0; re_enumerate: + /* LPM state doesn't matter when we're about to destroy the device. */ hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; } -- cgit v1.2.3-70-g09d2 From 9cf65991dd93ac3d5f97f536171c388918b7c1a9 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 3 Jul 2012 23:22:38 -0700 Subject: USB: Disable LPM while the device is unconfigured. The USB 3.0 Set/Clear Feature U1/U2 Enable cannot be sent to a device in the Default or Addressed state. It can only be sent to a configured device. Change the USB core to initialize the LPM disable count to 1 (disabled), which reflects this limitation. Change usb_set_configuration() to ensure that if the device is unconfigured on entry, usb_lpm_disable() is not called. This avoids sending the Clear Feature U1/U2 when the device is in the Addressed state. When usb_set_configuration() exits with a successfully installed configuration, usb_lpm_enable() will be called. Once the new configuration is installed, make sure usb_set_configuration() only calls usb_enable_lpm() if the device moved to the Configured state. If we have unconfigured the device by sending it a Set Configuration for config 0, don't enable LPM. This commit should be backported to kernels as old as 3.5, that contain the commit 8306095fd2c1100e8244c09bf560f97aca5a311d "USB: Disable USB 3.0 LPM in critical sections." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/message.c | 7 ++++--- drivers/usb/core/usb.c | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 8b9d669e378..37239048be1 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1792,14 +1792,15 @@ free_interfaces: * installed, so that the xHCI driver can recalculate the U1/U2 * timeouts. */ - if (usb_disable_lpm(dev)) { + if (dev->actconfig && usb_disable_lpm(dev)) { dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { - usb_enable_lpm(dev); + if (dev->actconfig) + usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; @@ -1819,7 +1820,7 @@ free_interfaces: if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); - usb_enable_lpm(dev); + /* Leave LPM disabled while the device is unconfigured. */ mutex_unlock(hcd->bandwidth_mutex); usb_autosuspend_device(dev); goto free_interfaces; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 25d0c61c3f8..cd8fb44a3e1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -396,6 +396,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->dev.dma_mask = bus->controller->dma_mask; set_dev_node(&dev->dev, dev_to_node(bus->controller)); dev->state = USB_STATE_ATTACHED; + dev->lpm_disable_count = 1; atomic_set(&dev->urbnum, 0); INIT_LIST_HEAD(&dev->ep0.urb_list); -- cgit v1.2.3-70-g09d2 From 249719121bc2b841bdfcab5eb21b10d8b871743b Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 Jul 2012 14:09:30 -0700 Subject: USB: Fix LPM disable count mismatch on driver unbind. When a user runs `echo 0 > bConfigurationValue` for a USB 3.0 device, usb_disable_device() is called. This function disables all drivers, deallocates interfaces, and sets the device configuration value to 0 (unconfigured). With the new scheme to ensure that unconfigured devices have LPM disabled, usb_disable_device() must call usb_unlocked_disable_lpm() once it unconfigures the device. This commit should be backported to kernels as old as 3.5, that contain the commit 8306095fd2c1100e8244c09bf560f97aca5a311d "USB: Disable USB 3.0 LPM in critical sections." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- drivers/usb/core/message.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 37239048be1..c0877b7f505 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1174,6 +1174,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) put_device(&dev->actconfig->interface[i]->dev); dev->actconfig->interface[i] = NULL; } + usb_unlocked_disable_lpm(dev); dev->actconfig = NULL; if (dev->state == USB_STATE_CONFIGURED) usb_set_device_state(dev, USB_STATE_ADDRESS); -- cgit v1.2.3-70-g09d2 From af3a23ef4e5fbaf33f0afdda7d26442f036ba795 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 25 Jun 2012 08:24:30 -0700 Subject: xhci: Export Latency Tolerance Messaging capabilities. Some xHCI host controllers may have optional support for Latency Tolerance Messaging (LTM). This allows USB 3.0 devices that support LTM to pass information about how much latency they can tolerate to the xHC. A PCI xHCI host will use this information to update the PCI Latency Tolerance Request (LTR) info. The goal of this is to gather latency information for the system, to enable hardware-driven C states, and the shutting down of PLLs. Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-hub.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 2732ef660c5..6d21030e2b7 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -508,12 +508,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (hcd->speed != HCD_USB3) goto error; + /* Set the U1 and U2 exit latencies. */ memcpy(buf, &usb_bos_descriptor, USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); buf[12] = HCS_U1_LATENCY(temp); put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + /* Indicate whether the host has LTM support. */ + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_LTC(temp)) + buf[8] |= USB_LTM_SUPPORT; + spin_unlock_irqrestore(&xhci->lock, flags); return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; case GetPortStatus: -- cgit v1.2.3-70-g09d2 From f74631e3426474183389e55f703797bd965cd356 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 25 Jun 2012 12:08:08 -0700 Subject: USB: Enable Latency Tolerance Messaging (LTM). USB 3.0 devices may optionally support a new feature called Latency Tolerance Messaging. If both the xHCI host controller and the device support LTM, it should be turned on in order to give the system hardware a better clue about the latency tolerance values of its PCI devices. Once a Set Feature request to enable LTM is received, the USB 3.0 device will begin to send LTM updates as its buffers fill or empty, and it can tolerate more or less latency. The USB 3.0 spec, section C.4.2 says that LTM should be disabled just before the device is placed into suspend. Then the device will send an updated LTM notification, so that the system doesn't think it should remain in an active state in order to satisfy the latency requirements of the suspended device. The Set and Clear Feature LTM enable command can only be sent to a configured device. The device will respond with an error if that command is sent while it is in the Default or Addressed state. Make sure to check udev->actconfig in usb_enable_ltm() and usb_disable_ltm(), and don't send those commands when the device is unconfigured. LTM should be enabled once a new configuration is installed in usb_set_configuration(). If we end up sending duplicate Set Feature LTM Enable commands on a switch from one installed configuration to another configuration, that should be harmless. Make sure that LTM is disabled before the device is unconfigured in usb_disable_device(). If no drivers are bound to the device, it doesn't make sense to allow the device to control the latency tolerance of the xHCI host controller. Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 87 ++++++++++++++++++++++++++++++++++++++++++---- drivers/usb/core/message.c | 3 ++ include/linux/usb.h | 3 ++ 3 files changed, 87 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5c31d2c2f95..b5bd6bd8fd1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2610,6 +2610,57 @@ static int check_port_resume_type(struct usb_device *udev, return status; } +static bool usb_device_supports_ltm(struct usb_device *udev) +{ + if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + return false; + return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; +} + +int usb_disable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return 0; + + /* Clear Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return 0; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return; + + /* Set Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_enable_ltm); + #ifdef CONFIG_USB_SUSPEND /* @@ -2705,6 +2756,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); + if (usb_disable_ltm(udev)) { + dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", + __func__); + return -ENOMEM; + } if (usb_unlocked_disable_lpm(udev)) { dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", __func__); @@ -2734,7 +2790,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM again */ + /* Try to enable USB3 LTM and LPM again */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); /* System sleep transitions should never fail */ @@ -2935,7 +2992,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM */ + /* Try to enable USB3 LTM and LPM */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); } @@ -3488,6 +3546,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); void usb_unlocked_enable_lpm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); + +int usb_disable_ltm(struct usb_device *udev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) { } +EXPORT_SYMBOL_GPL(usb_enable_ltm); #endif @@ -4673,15 +4740,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } parent_hub = hdev_to_hub(parent_hdev); - /* Disable LPM while we reset the device and reinstall the alt settings. - * Device-initiated LPM settings, and system exit latency settings are - * cleared when the device is reset, so we have to set them up again. + /* Disable LPM and LTM while we reset the device and reinstall the alt + * settings. Device-initiated LPM settings, and system exit latency + * settings are cleared when the device is reset, so we have to set + * them up again. */ ret = usb_unlocked_disable_lpm(udev); if (ret) { dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); goto re_enumerate; } + ret = usb_disable_ltm(udev); + if (ret) { + dev_err(&udev->dev, "%s Failed to disable LTM\n.", + __func__); + goto re_enumerate; + } set_bit(port1, parent_hub->busy_bits); for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -4769,8 +4843,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } done: - /* Now that the alt settings are re-installed, enable LPM. */ + /* Now that the alt settings are re-installed, enable LTM and LPM. */ usb_unlocked_enable_lpm(udev); + usb_enable_ltm(udev); return 0; re_enumerate: diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index c0877b7f505..0ab7da2283e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1175,6 +1175,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev->actconfig->interface[i] = NULL; } usb_unlocked_disable_lpm(dev); + usb_disable_ltm(dev); dev->actconfig = NULL; if (dev->state == USB_STATE_CONFIGURED) usb_set_device_state(dev, USB_STATE_ADDRESS); @@ -1879,6 +1880,8 @@ free_interfaces: /* Now that the interfaces are installed, re-enable LPM. */ usb_unlocked_enable_lpm(dev); + /* Enable LTM if it was turned off by usb_disable_device. */ + usb_enable_ltm(dev); /* Now that all the interfaces are set up, register them * to trigger binding of drivers to interfaces. probe() diff --git a/include/linux/usb.h b/include/linux/usb.h index dea2f0de063..f29831bad23 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -633,6 +633,9 @@ extern void usb_enable_lpm(struct usb_device *udev); extern int usb_unlocked_disable_lpm(struct usb_device *udev); extern void usb_unlocked_enable_lpm(struct usb_device *udev); +extern int usb_disable_ltm(struct usb_device *udev); +extern void usb_enable_ltm(struct usb_device *udev); + /*-------------------------------------------------------------------------*/ /* for drivers using iso endpoints */ -- cgit v1.2.3-70-g09d2 From 024f117c2f3c4bb5df6e6696b709e0f3ed7e5dbb Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 Jul 2012 17:17:24 -0700 Subject: USB: Add a sysfs file to show LTM capabilities. USB 3.0 devices can optionally support Latency Tolerance Messaging (LTM). Add a new sysfs file in the device directory to show whether a device is LTM capable. This file will be present for both USB 2.0 and USB 3.0 devices. Signed-off-by: Sarah Sharp --- Documentation/ABI/testing/sysfs-bus-usb | 12 ++++++++++++ drivers/usb/core/hub.c | 7 ------- drivers/usb/core/sysfs.c | 10 ++++++++++ include/linux/usb.h | 8 ++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 6df4e6f5756..5f75f8f7df3 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -208,3 +208,15 @@ Description: such as ACPI. This file will read either "removable" or "fixed" if the information is available, and "unknown" otherwise. + +What: /sys/bus/usb/devices/.../ltm_capable +Date: July 2012 +Contact: Sarah Sharp +Description: + USB 3.0 devices may optionally support Latency Tolerance + Messaging (LTM). They indicate their support by setting a bit + in the bmAttributes field of their SuperSpeed BOS descriptors. + If that bit is set for the device, ltm_capable will read "yes". + If the device doesn't support LTM, the file will read "no". + The file will be present for all speeds of USB devices, and will + always read "no" for USB 1.1 and USB 2.0 devices. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b5bd6bd8fd1..d739f966b5a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2610,13 +2610,6 @@ static int check_port_resume_type(struct usb_device *udev, return status; } -static bool usb_device_supports_ltm(struct usb_device *udev) -{ - if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) - return false; - return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; -} - int usb_disable_ltm(struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 777f03c3772..682e8256b95 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); +static ssize_t +show_ltm_capable(struct device *dev, struct device_attribute *attr, char *buf) +{ + if (usb_device_supports_ltm(to_usb_device(dev))) + return sprintf(buf, "%s\n", "yes"); + return sprintf(buf, "%s\n", "no"); +} +static DEVICE_ATTR(ltm_capable, S_IRUGO, show_ltm_capable, NULL); + #ifdef CONFIG_PM static ssize_t @@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_authorized.attr, &dev_attr_remove.attr, &dev_attr_removable.attr, + &dev_attr_ltm_capable.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff --git a/include/linux/usb.h b/include/linux/usb.h index f29831bad23..f8506ed0f97 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -636,6 +636,14 @@ extern void usb_unlocked_enable_lpm(struct usb_device *udev); extern int usb_disable_ltm(struct usb_device *udev); extern void usb_enable_ltm(struct usb_device *udev); +static inline bool usb_device_supports_ltm(struct usb_device *udev) +{ + if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + return false; + return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; +} + + /*-------------------------------------------------------------------------*/ /* for drivers using iso endpoints */ -- cgit v1.2.3-70-g09d2 From 7620c687429553d469afb699565054748d74b81f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 7 Jul 2012 23:05:08 -0400 Subject: scsi: set to WCE if usb cache quirk is present. Make use of USB quirk method to identify such HDD while reading the cache status in sd_probe(). If cache quirk is present for the HDD, lets assume that cache is enabled and make WCE bit equal to 1. Signed-off-by: Namjae Jeon Signed-off-by: Pankaj Kumar Signed-off-by: Amit Sahrawat Cc: Alan Stern Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sd.c | 9 +++++++-- include/scsi/scsi_device.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 6f72b80121a..4225064db79 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2261,8 +2261,13 @@ bad_sense: sd_printk(KERN_ERR, sdkp, "Asking for cache data failed\n"); defaults: - sd_printk(KERN_ERR, sdkp, "Assuming drive cache: write through\n"); - sdkp->WCE = 0; + if (sdp->wce_default_on) { + sd_printk(KERN_NOTICE, sdkp, "Assuming drive cache: write back\n"); + sdkp->WCE = 1; + } else { + sd_printk(KERN_ERR, sdkp, "Assuming drive cache: write through\n"); + sdkp->WCE = 0; + } sdkp->RCD = 0; sdkp->DPOFUA = 0; } diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index ba969885232..6d65b55d47a 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -153,6 +153,7 @@ struct scsi_device { unsigned no_read_capacity_16:1; /* Avoid READ_CAPACITY_16 cmds */ unsigned try_rc_10_first:1; /* Try READ_CAPACACITY_10 first */ unsigned is_visible:1; /* is the device visible in sysfs */ + unsigned wce_default_on:1; /* Cache is ON by default */ DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ struct list_head event_list; /* asserted events */ -- cgit v1.2.3-70-g09d2 From e1bc9e50752c291a9ea602866331cd9e02049745 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 7 Jul 2012 23:05:28 -0400 Subject: usb: storage: add support for write cache quirk Add support for write cache quirk on usb hdd. scsi driver will be set to wce by detecting write cache quirk in quirk list when plugging usb hdd. Signed-off-by: Namjae Jeon Signed-off-by: Pankaj Kumar Signed-off-by: Amit Sahrawat Acked-by: Alan Stern Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 2 ++ drivers/usb/storage/scsiglue.c | 5 +++++ drivers/usb/storage/usb.c | 5 ++++- include/linux/usb_usual.h | 4 +++- 4 files changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index a92c5ebf373..c68634edd45 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2932,6 +2932,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. initial READ(10) command); o = CAPACITY_OK (accept the capacity reported by the device); + p = WRITE_CACHE (the device cache is ON + by default); r = IGNORE_RESIDUE (the device reports bogus residue values); s = SINGLE_LUN (the device has only one diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 11418da9bc0..a3d54366afc 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -236,6 +236,11 @@ static int slave_configure(struct scsi_device *sdev) US_FL_SCM_MULT_TARG)) && us->protocol == USB_PR_BULK) us->use_last_sector_hacks = 1; + + /* Check if write cache default on flag is set or not */ + if (us->fflags & US_FL_WRITE_CACHE) + sdev->wce_default_on = 1; + } else { /* Non-disk-type devices don't need to blacklist any pages diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index e23c30ab66d..d012fe4329e 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -473,7 +473,7 @@ static void adjust_quirks(struct us_data *us) US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT | US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 | - US_FL_INITIAL_READ10); + US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE); p = quirks; while (*p) { @@ -529,6 +529,9 @@ static void adjust_quirks(struct us_data *us) case 'o': f |= US_FL_CAPACITY_OK; break; + case 'p': + f |= US_FL_WRITE_CACHE; + break; case 'r': f |= US_FL_IGNORE_RESIDUE; break; diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 17df3600bce..e84e769aadd 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -64,7 +64,9 @@ US_FLAG(NO_READ_CAPACITY_16, 0x00080000) \ /* cannot handle READ_CAPACITY_16 */ \ US_FLAG(INITIAL_READ10, 0x00100000) \ - /* Initial READ(10) (and others) must be retried */ + /* Initial READ(10) (and others) must be retried */ \ + US_FLAG(WRITE_CACHE, 0x00200000) \ + /* Write Cache status is not available */ #define US_FLAG(name, value) US_FL_##name = value , enum { US_DO_ALL_FLAGS }; -- cgit v1.2.3-70-g09d2 From 1f9be64a6de34f8d4a2dce845af15063523a02e3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 7 Jul 2012 23:05:49 -0400 Subject: usb: storage: update usb devices for write cache quirk in quirk list. Update information of Seagate Portable HDD and WD My Passport HDD in quirk list. Signed-off-by: Namjae Jeon Signed-off-by: Pankaj Kumar Signed-off-by: Amit Sahrawat Acked-by: Alan Stern Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 1719886bb9b..62a31bea063 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1267,6 +1267,12 @@ UNUSUAL_DEV( 0x0af0, 0xd357, 0x0000, 0x0000, USB_SC_DEVICE, USB_PR_DEVICE, NULL, 0 ), +/* Reported by Namjae Jeon */ +UNUSUAL_DEV(0x0bc2, 0x2300, 0x0000, 0x9999, + "Seagate", + "Portable HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_WRITE_CACHE), + /* Reported by Ben Efros */ UNUSUAL_DEV( 0x0bc2, 0x3010, 0x0000, 0x0000, "Seagate", @@ -1468,6 +1474,12 @@ UNUSUAL_DEV( 0x1058, 0x0704, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_SANE_SENSE), +/* Reported by Namjae Jeon */ +UNUSUAL_DEV(0x1058, 0x070a, 0x0000, 0x9999, + "Western Digital", + "My Passport HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_WRITE_CACHE), + /* Reported by Fabio Venturi * The device reports a vendor-specific bDeviceClass. */ -- cgit v1.2.3-70-g09d2 From 631fe9d9d20e28fffdf750d12dd2cd275bd654e9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:09 -0400 Subject: USB: EHCI: initialize data before resetting hardware Currently, EHCI initialization turns off the controller (in case it was left running by the firmware) before setting up the ehci_hcd data structure. This patch (as1565) reverses that order. Although it doesn't matter now, it will matter later on when future additions to ehci_halt() will want to acquire a spinlock that gets initialized by ehci_init(). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f9a783bfa1f..8727f4ea343 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -823,12 +823,12 @@ static int ehci_setup(struct usb_hcd *hcd) ehci->sbrn = HCD_USB2; - retval = ehci_halt(ehci); + /* data structure init */ + retval = ehci_init(hcd); if (retval) return retval; - /* data structure init */ - retval = ehci_init(hcd); + retval = ehci_halt(ehci); if (retval) return retval; -- cgit v1.2.3-70-g09d2 From 15be105b4a18c461b95fa683907f6da6deae1b75 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:17 -0400 Subject: USB: EHCI: remove unneeded suspend/resume code This patch (as1566) removes the code in ehci-hcd's resume routines which tries to restart or cancel any transfers left active while the root hub or controller was asleep. This code isn't necessary, because all URBs are terminated before the root hub is suspended. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 7 ------- drivers/usb/host/ehci-hub.c | 13 +------------ 2 files changed, 1 insertion(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8727f4ea343..ab7306de8d1 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1312,13 +1312,6 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) (void) ehci_halt(ehci); (void) ehci_reset(ehci); - /* emptying the schedule aborts any urbs */ - spin_lock_irq(&ehci->lock); - if (ehci->reclaim) - end_unlink_async(ehci); - ehci_work(ehci); - spin_unlock_irq(&ehci->lock); - ehci_writel(ehci, ehci->command, &ehci->regs->command); ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index b3e2d66e95b..1daaa96f527 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -424,23 +424,12 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ehci_vdbg (ehci, "resumed port %d\n", i + 1); } } - (void) ehci_readl(ehci, &ehci->regs->command); - - /* maybe re-activate the schedule(s) */ - temp = 0; - if (ehci->async->qh_next.qh) - temp |= CMD_ASE; - if (ehci->periodic_sched) - temp |= CMD_PSE; - if (temp) { - ehci->command |= temp; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - } ehci->next_statechange = jiffies + msecs_to_jiffies(5); /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); + (void) ehci_readl(ehci, &ehci->regs->intr_enable); spin_unlock_irq (&ehci->lock); ehci_handover_companion_ports(ehci); -- cgit v1.2.3-70-g09d2 From c83e1a9ff68a6535b81c40dc8fda99348ab480fb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:25 -0400 Subject: USB: EHCI: don't refcount QHs This patch (as1567) removes ehci-hcd's reference counting of QH structures. It's not necessary to refcount these things because they always get deallocated at exactly one spot in ehci_endpoint_disable() (except for two special QHs, ehci->async and ehci->dummy) and are never used again. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-mem.c | 24 +++--------------------- drivers/usb/host/ehci-q.c | 12 +++--------- drivers/usb/host/ehci-sched.c | 5 +---- drivers/usb/host/ehci.h | 9 --------- 5 files changed, 8 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ab7306de8d1..1f8f792eec8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1179,7 +1179,7 @@ idle_timeout: if (qh->clearing_tt) goto idle_timeout; if (list_empty (&qh->qtd_list)) { - qh_put (qh); + qh_destroy(ehci, qh); break; } /* else FALL THROUGH */ diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 12f70c302b0..93132d8ad36 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -64,10 +64,8 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd) } -static void qh_destroy(struct ehci_qh *qh) +static void qh_destroy(struct ehci_hcd *ehci, struct ehci_qh *qh) { - struct ehci_hcd *ehci = qh->ehci; - /* clean qtds first, and know this is not linked */ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { ehci_dbg (ehci, "unused qh not empty!\n"); @@ -92,8 +90,6 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) if (!qh->hw) goto fail; memset(qh->hw, 0, sizeof *qh->hw); - qh->refcount = 1; - qh->ehci = ehci; qh->qh_dma = dma; // INIT_LIST_HEAD (&qh->qh_list); INIT_LIST_HEAD (&qh->qtd_list); @@ -113,20 +109,6 @@ fail: return NULL; } -/* to share a qh (cpu threads, or hc) */ -static inline struct ehci_qh *qh_get (struct ehci_qh *qh) -{ - WARN_ON(!qh->refcount); - qh->refcount++; - return qh; -} - -static inline void qh_put (struct ehci_qh *qh) -{ - if (!--qh->refcount) - qh_destroy(qh); -} - /*-------------------------------------------------------------------------*/ /* The queue heads and transfer descriptors are managed from pools tied @@ -138,11 +120,11 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci) { free_cached_lists(ehci); if (ehci->async) - qh_put (ehci->async); + qh_destroy(ehci, ehci->async); ehci->async = NULL; if (ehci->dummy) - qh_put(ehci->dummy); + qh_destroy(ehci, ehci->dummy); ehci->dummy = NULL; /* DMA consistent memory and pools */ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 4378bf72bba..7d117bbffac 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -265,7 +265,6 @@ __acquires(ehci->lock) /* ... update hc-wide periodic stats (for usbfs) */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--; } - qh_put (qh); } if (unlikely(urb->unlinked)) { @@ -946,7 +945,7 @@ qh_make ( ehci_dbg(ehci, "bogus dev %p speed %d\n", urb->dev, urb->dev->speed); done: - qh_put (qh); + qh_destroy(ehci, qh); return NULL; } @@ -1003,7 +1002,6 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) head->qh_next.qh = qh; head->hw->hw_next = dma; - qh_get(qh); qh->xacterrs = 0; qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ @@ -1090,7 +1088,7 @@ static struct ehci_qh *qh_append_tds ( wmb (); dummy->hw_token = token; - urb->hcpriv = qh_get (qh); + urb->hcpriv = qh; } } return qh; @@ -1167,7 +1165,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) // qh->hw_next = cpu_to_hc32(qh->qh_dma); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; - qh_put (qh); // refcount from reclaim /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ next = qh->reclaim; @@ -1186,7 +1183,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) && ehci->async->qh_next.qh == NULL) timer_action (ehci, TIMER_ASYNC_OFF); } - qh_put(qh); /* refcount from async list */ if (next) { ehci->reclaim = NULL; @@ -1230,7 +1226,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh = qh_get (qh); + ehci->reclaim = qh; prev = ehci->async; while (prev->qh_next.qh != qh) @@ -1283,12 +1279,10 @@ static void scan_async (struct ehci_hcd *ehci) * gets unlinked then ehci->qh_scan_next is adjusted * in start_unlink_async(). */ - qh = qh_get(qh); temp = qh_completions(ehci, qh); if (qh->needs_rescan) unlink_async(ehci, qh); qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; - qh_put(qh); if (temp != 0) goto rescan; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 33182c6d1ff..027df3de2dc 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -606,7 +606,6 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_LINKED; qh->xacterrs = 0; - qh_get (qh); /* update per-qh bandwidth for usbfs */ ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period @@ -650,7 +649,6 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; - qh_put (qh); /* maybe turn off periodic schedule */ return disable_periodic(ehci); @@ -2340,7 +2338,7 @@ restart: switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ - temp.qh = qh_get (q.qh); + temp.qh = q.qh; type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); q = q.qh->qh_next; if (temp.qh->stamp != ehci->periodic_stamp) { @@ -2351,7 +2349,6 @@ restart: temp.qh->needs_rescan)) intr_deschedule(ehci, temp.qh); } - qh_put (temp.qh); break; case Q_TYPE_FSTN: /* for "save place" FSTNs, look at QH entries diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 85c3572155d..f7a59f5d70c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -350,16 +350,7 @@ struct ehci_qh { struct ehci_qtd *dummy; struct ehci_qh *reclaim; /* next to reclaim */ - struct ehci_hcd *ehci; unsigned long unlink_time; - - /* - * Do NOT use atomic operations for QH refcounting. On some CPUs - * (PPC7448 for example), atomic operations cannot be performed on - * memory that is cache-inhibited (i.e. being used for DMA). - * Spinlocks are used to protect all QH fields. - */ - u32 refcount; unsigned stamp; u8 needs_rescan; /* Dequeue during giveback */ -- cgit v1.2.3-70-g09d2 From 4c53de72109759c931744734ca75f9cecb3baef1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:32 -0400 Subject: USB: EHCI: add symbolic constants for QHs This patch (as1568) introduces symbolic constants for some of the less-frequently used bitfields in the QH structure. This makes the code a little easier to read and understand. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 6 +++--- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-q.c | 12 ++++++------ drivers/usb/host/ehci.h | 8 +++++++- 4 files changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 7561966fbdc..97338abff29 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -404,9 +404,9 @@ struct debug_buffer { #define speed_char(info1) ({ char tmp; \ switch (info1 & (3 << 12)) { \ - case 0 << 12: tmp = 'f'; break; \ - case 1 << 12: tmp = 'l'; break; \ - case 2 << 12: tmp = 'h'; break; \ + case QH_FULL_SPEED: tmp = 'f'; break; \ + case QH_LOW_SPEED: tmp = 'l'; break; \ + case QH_HIGH_SPEED: tmp = 'h'; break; \ default: tmp = '?'; break; \ }; tmp; }) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1f8f792eec8..cdb15769468 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -667,7 +667,7 @@ static int ehci_init(struct usb_hcd *hcd) hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma); hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD); #if defined(CONFIG_PPC_PS3) - hw->hw_info1 |= cpu_to_hc32(ehci, (1 << 7)); /* I = 1 */ + hw->hw_info1 |= cpu_to_hc32(ehci, QH_INACTIVATE); #endif hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); hw->hw_qtd_next = EHCI_LIST_END(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 7d117bbffac..15650478771 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -100,7 +100,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) * and set the pseudo-toggle in udev. Only usb_clear_halt() will * ever clear it. */ - if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { + if (!(hw->hw_info1 & cpu_to_hc32(ehci, QH_TOGGLE_CTL))) { unsigned is_out, epnum; is_out = qh->is_out; @@ -886,7 +886,7 @@ qh_make ( /* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW: - info1 |= (1 << 12); /* EPS "low" */ + info1 |= QH_LOW_SPEED; /* FALL THROUGH */ case USB_SPEED_FULL: @@ -894,8 +894,8 @@ qh_make ( if (type != PIPE_INTERRUPT) info1 |= (EHCI_TUNE_RL_TT << 28); if (type == PIPE_CONTROL) { - info1 |= (1 << 27); /* for TT */ - info1 |= 1 << 14; /* toggle from qtd */ + info1 |= QH_CONTROL_EP; /* for TT */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ } info1 |= maxp << 16; @@ -920,11 +920,11 @@ qh_make ( break; case USB_SPEED_HIGH: /* no TT involved */ - info1 |= (2 << 12); /* EPS "high" */ + info1 |= QH_HIGH_SPEED; if (type == PIPE_CONTROL) { info1 |= (EHCI_TUNE_RL_HS << 28); info1 |= 64 << 16; /* usb2 fixed maxpacket */ - info1 |= 1 << 14; /* toggle from qtd */ + info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ info2 |= (EHCI_TUNE_MULT_HS << 30); } else if (type == PIPE_BULK) { info1 |= (EHCI_TUNE_RL_HS << 28); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index f7a59f5d70c..195449db1b1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -324,7 +324,13 @@ union ehci_shadow { struct ehci_qh_hw { __hc32 hw_next; /* see EHCI 3.6.1 */ __hc32 hw_info1; /* see EHCI 3.6.2 */ -#define QH_HEAD 0x00008000 +#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ +#define QH_HEAD (1 << 15) /* Head of async reclamation list */ +#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ +#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ +#define QH_LOW_SPEED (1 << 12) +#define QH_FULL_SPEED (0 << 12) +#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ __hc32 hw_info2; /* see EHCI 3.6.2 */ #define QH_SMASK 0x000000ff #define QH_CMASK 0x0000ff00 -- cgit v1.2.3-70-g09d2 From 99ac5b1e9536f142461681fa6143a947d66b4279 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:38 -0400 Subject: USB: EHCI: rename "reclaim" This patch (as1569) renames the ehci->reclaim list in ehci-hcd. The word "reclaim" is used in the EHCI specification to mean something quite different, and "unlink_next" is more descriptive of the list's purpose anyway. Similarly, the "reclaim" field in the ehci_stats structure is renamed "iaa", which is more meaningful (to experts, anyway) and is a better match for the "lost_iaa" field. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 16 +++++++++------- drivers/usb/host/ehci-hcd.c | 25 ++++++++++++------------- drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci-q.c | 22 +++++++++++----------- drivers/usb/host/ehci.h | 10 +++++----- 5 files changed, 38 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 97338abff29..76120957d60 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -538,12 +538,13 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) spin_lock_irqsave (&ehci->lock, flags); for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) qh_lines (ehci, qh, &next, &size); - if (ehci->reclaim && size > 0) { - temp = scnprintf (next, size, "\nreclaim =\n"); + if (ehci->async_unlink && size > 0) { + temp = scnprintf(next, size, "\nunlink =\n"); size -= temp; next += temp; - for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim) + for (qh = ehci->async_unlink; size > 0 && qh; + qh = qh->unlink_next) qh_lines (ehci, qh, &next, &size); } spin_unlock_irqrestore (&ehci->lock, flags); @@ -841,16 +842,17 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) } } - if (ehci->reclaim) { - temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim); + if (ehci->async_unlink) { + temp = scnprintf(next, size, "async unlink qh %p\n", + ehci->async_unlink); size -= temp; next += temp; } #ifdef EHCI_STATS temp = scnprintf (next, size, - "irq normal %ld err %ld reclaim %ld (lost %ld)\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, + "irq normal %ld err %ld iaa %ld (lost %ld)\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.iaa, ehci->stats.lost_iaa); size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index cdb15769468..efee426a246 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -403,7 +403,7 @@ static void ehci_iaa_watchdog(unsigned long param) * (a) SMP races against real IAA firing and retriggering, and * (b) clean HC shutdown, when IAA watchdog was pending. */ - if (ehci->reclaim + if (ehci->async_unlink && !timer_pending(&ehci->iaa_watchdog) && ehci->rh_state == EHCI_RH_RUNNING) { u32 cmd, status; @@ -583,8 +583,8 @@ static void ehci_stop (struct usb_hcd *hcd) usb_amd_dev_put(); #ifdef EHCI_STATS - ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, + ehci_dbg(ehci, "irq normal %ld err %ld iaa %ld (lost %ld)\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.iaa, ehci->stats.lost_iaa); ehci_dbg (ehci, "complete %ld unlink %ld\n", ehci->stats.complete, ehci->stats.unlink); @@ -651,7 +651,6 @@ static int ehci_init(struct usb_hcd *hcd) else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); - ehci->reclaim = NULL; ehci->next_uframe = -1; ehci->clock_frame = -1; @@ -896,11 +895,11 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); - if (ehci->reclaim) { - COUNT(ehci->stats.reclaim); + if (ehci->async_unlink) { + COUNT(ehci->stats.iaa); end_unlink_async(ehci); } else - ehci_dbg(ehci, "IAA with nothing to reclaim?\n"); + ehci_dbg(ehci, "IAA with nothing unlinked?\n"); } /* remote wakeup [4.3.1] */ @@ -1027,7 +1026,7 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { /* failfast */ - if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim) + if (ehci->rh_state != EHCI_RH_RUNNING && ehci->async_unlink) end_unlink_async(ehci); /* If the QH isn't linked then there's nothing we can do @@ -1041,15 +1040,15 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } /* defer till later if busy */ - if (ehci->reclaim) { + if (ehci->async_unlink) { struct ehci_qh *last; - for (last = ehci->reclaim; - last->reclaim; - last = last->reclaim) + for (last = ehci->async_unlink; + last->unlink_next; + last = last->unlink_next) continue; qh->qh_state = QH_STATE_UNLINK_WAIT; - last->reclaim = qh; + last->unlink_next = qh; /* start IAA cycle */ } else diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1daaa96f527..77d3324b4b2 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -301,7 +301,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_halt (ehci); ehci->rh_state = EHCI_RH_SUSPENDED; - if (ehci->reclaim) + if (ehci->async_unlink) end_unlink_async(ehci); /* allow remote wakeup */ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 15650478771..8e80cde8c35 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1153,11 +1153,11 @@ submit_async ( /*-------------------------------------------------------------------------*/ -/* the async qh for the qtds being reclaimed are now unlinked from the HC */ +/* the async qh for the qtds being unlinked are now gone from the HC */ static void end_unlink_async (struct ehci_hcd *ehci) { - struct ehci_qh *qh = ehci->reclaim; + struct ehci_qh *qh = ehci->async_unlink; struct ehci_qh *next; iaa_watchdog_done(ehci); @@ -1167,9 +1167,9 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh->qh_next.qh = NULL; /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ - next = qh->reclaim; - ehci->reclaim = next; - qh->reclaim = NULL; + next = qh->unlink_next; + ehci->async_unlink = next; + qh->unlink_next = NULL; qh_completions (ehci, qh); @@ -1185,7 +1185,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) } if (next) { - ehci->reclaim = NULL; + ehci->async_unlink = NULL; start_unlink_async (ehci, next); } @@ -1203,7 +1203,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) #ifdef DEBUG assert_spin_locked(&ehci->lock); - if (ehci->reclaim + if (ehci->async_unlink || (qh->qh_state != QH_STATE_LINKED && qh->qh_state != QH_STATE_UNLINK_WAIT) ) @@ -1214,7 +1214,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ if (ehci->rh_state != EHCI_RH_HALTED - && !ehci->reclaim) { + && !ehci->async_unlink) { /* ... and CMD_IAAD clear */ ehci->command &= ~CMD_ASE; ehci_writel(ehci, ehci->command, &ehci->regs->command); @@ -1226,7 +1226,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh; + ehci->async_unlink = qh; prev = ehci->async; while (prev->qh_next.qh != qh) @@ -1240,7 +1240,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) { - /* if (unlikely (qh->reclaim != 0)) + /* if (unlikely (qh->unlink_next != 0)) * this will recurse, probably not much */ end_unlink_async (ehci); @@ -1295,7 +1295,7 @@ static void scan_async (struct ehci_hcd *ehci) */ if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->reclaim && (stopped || + if (!ehci->async_unlink && (stopped || time_after_eq(jiffies, qh->unlink_time))) start_unlink_async(ehci, qh); else diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 195449db1b1..3c6c07c0956 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -42,7 +42,7 @@ struct ehci_stats { /* irq usage */ unsigned long normal; unsigned long error; - unsigned long reclaim; + unsigned long iaa; unsigned long lost_iaa; /* termination of urbs from core */ @@ -51,7 +51,7 @@ struct ehci_stats { }; /* ehci_hcd->lock guards shared data against other CPUs: - * ehci_hcd: async, reclaim, periodic (and shadow), ... + * ehci_hcd: async, unlink, periodic (and shadow), ... * usb_host_endpoint: hcpriv * ehci_qh: qh_next, qtd_list * ehci_qtd: qtd_list @@ -81,7 +81,7 @@ struct ehci_hcd { /* one per controller */ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ - struct ehci_qh *reclaim; + struct ehci_qh *async_unlink; struct ehci_qh *qh_scan_next; unsigned scanning : 1; @@ -354,7 +354,7 @@ struct ehci_qh { union ehci_shadow qh_next; /* ptr to qh; or periodic */ struct list_head qtd_list; /* sw qtd list */ struct ehci_qtd *dummy; - struct ehci_qh *reclaim; /* next to reclaim */ + struct ehci_qh *unlink_next; /* next on unlink list */ unsigned long unlink_time; unsigned stamp; @@ -364,7 +364,7 @@ struct ehci_qh { #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */ -#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ #define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ u8 xacterrs; /* XactErr retry counter */ -- cgit v1.2.3-70-g09d2 From 2f5bb665ba7a14c5842fa2e1cde2be039843a2a2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:43 -0400 Subject: USB: EHCI: add pointer to end of async-unlink list This patch (as1570) adds a pointer for the end of ehci-hcd's async-unlink list. The list (which is actually a queue) is singly linked, so having a pointer to its end makes adding new entries easier -- there's no longer any need to scan through the whole list. In principle it could be changed to a standard doubly-linked list. It turns out that doing so actually makes the code less clear, so I'm leaving it as is. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 9 ++------- drivers/usb/host/ehci-q.c | 2 ++ drivers/usb/host/ehci.h | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index efee426a246..8b75e4279a4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1041,14 +1041,9 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* defer till later if busy */ if (ehci->async_unlink) { - struct ehci_qh *last; - - for (last = ehci->async_unlink; - last->unlink_next; - last = last->unlink_next) - continue; qh->qh_state = QH_STATE_UNLINK_WAIT; - last->unlink_next = qh; + ehci->async_unlink_last->unlink_next = qh; + ehci->async_unlink_last = qh; /* start IAA cycle */ } else diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 8e80cde8c35..5193612c96e 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1227,6 +1227,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_state = QH_STATE_UNLINK; ehci->async_unlink = qh; + if (!qh->unlink_next) + ehci->async_unlink_last = qh; prev = ehci->async; while (prev->qh_next.qh != qh) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 3c6c07c0956..475f23e10bb 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -82,6 +82,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *async_unlink; + struct ehci_qh *async_unlink_last; struct ehci_qh *qh_scan_next; unsigned scanning : 1; -- cgit v1.2.3-70-g09d2 From c0c53dbc32ea05a1e1dd9dba4772327da9a11750 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:48 -0400 Subject: USB: EHCI: add new root-hub state: STOPPING This patch (as1571) adds a new state for ehci-hcd's root hubs: EHCI_RH_STOPPING. This value is used at times when the root hub is being stopped and we don't know whether or not the hardware has finished all its DMA yet. Although the purpose may not be apparent, this distinction will come in useful later on. Future patches will avoid actions that depend on the root hub being operational (like turning on the async or periodic schedules) when they see the state is EHCI_RH_STOPPING. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 2 ++ drivers/usb/host/ehci-hcd.c | 13 ++++++------- drivers/usb/host/ehci-hub.c | 3 +-- drivers/usb/host/ehci-q.c | 8 ++++---- drivers/usb/host/ehci-sched.c | 8 ++++---- drivers/usb/host/ehci.h | 7 ++++++- 6 files changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 76120957d60..f0c00de035e 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -706,6 +706,8 @@ static const char *rh_state_string(struct ehci_hcd *ehci) return "suspended"; case EHCI_RH_RUNNING: return "running"; + case EHCI_RH_STOPPING: + return "stopping"; } return "?"; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8b75e4279a4..bc94822f4c5 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -357,10 +357,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci) { u32 temp; -#ifdef DEBUG if (ehci->rh_state != EHCI_RH_RUNNING) - BUG (); -#endif + return; /* wait for any schedule enables/disables to take effect */ temp = (ehci->command << 10) & (STS_ASS | STS_PSS); @@ -494,6 +492,7 @@ static void ehci_shutdown(struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); + ehci->rh_state = EHCI_RH_STOPPING; ehci_silence_controller(ehci); spin_unlock_irq(&ehci->lock); } @@ -562,8 +561,7 @@ static void ehci_stop (struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); - if (ehci->rh_state == EHCI_RH_RUNNING) - ehci_quiesce (ehci); + ehci_quiesce(ehci); ehci_silence_controller(ehci); ehci_reset (ehci); @@ -951,6 +949,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { ehci_err(ehci, "fatal error\n"); + ehci->rh_state = EHCI_RH_STOPPING; dbg_cmd(ehci, "fatal", cmd); dbg_status(ehci, "fatal", status); ehci_halt(ehci); @@ -1026,7 +1025,7 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { /* failfast */ - if (ehci->rh_state != EHCI_RH_RUNNING && ehci->async_unlink) + if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink) end_unlink_async(ehci); /* If the QH isn't linked then there's nothing we can do @@ -1148,7 +1147,7 @@ rescan: goto idle_timeout; } - if (ehci->rh_state != EHCI_RH_RUNNING) + if (ehci->rh_state < EHCI_RH_RUNNING) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 77d3324b4b2..fb1b99e7493 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -227,8 +227,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } /* stop schedules, clean any completed work */ - if (ehci->rh_state == EHCI_RH_RUNNING) - ehci_quiesce (ehci); + ehci_quiesce(ehci); ehci_work(ehci); /* Unlike other USB host controller types, EHCI doesn't have diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5193612c96e..285d5a0f3f7 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -433,7 +433,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop scanning when we reach qtds the hc is using */ } else if (likely (!stopped - && ehci->rh_state == EHCI_RH_RUNNING)) { + && ehci->rh_state >= EHCI_RH_RUNNING)) { break; /* scan the whole queue for unlinks whenever it stops */ @@ -441,7 +441,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) stopped = 1; /* cancel everything if we halt, suspend, etc */ - if (ehci->rh_state != EHCI_RH_RUNNING) + if (ehci->rh_state < EHCI_RH_RUNNING) last_status = -ESHUTDOWN; /* this qtd is active; skip it unless a previous qtd @@ -1241,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) wmb (); /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) { + if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { /* if (unlikely (qh->unlink_next != 0)) * this will recurse, probably not much */ @@ -1263,7 +1263,7 @@ static void scan_async (struct ehci_hcd *ehci) enum ehci_timer_action action = TIMER_IO_WATCHDOG; timer_action_done (ehci, TIMER_ASYNC_SHRINK); - stopped = (ehci->rh_state != EHCI_RH_RUNNING); + stopped = (ehci->rh_state < EHCI_RH_RUNNING); ehci->qh_scan_next = ehci->async->qh_next.qh; while (ehci->qh_scan_next) { diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 027df3de2dc..3429b8a33c5 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -2299,7 +2299,7 @@ scan_periodic (struct ehci_hcd *ehci) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (ehci->rh_state == EHCI_RH_RUNNING) { + if (ehci->rh_state >= EHCI_RH_RUNNING) { clock = ehci_read_frame_index(ehci); clock_frame = (clock >> 3) & (ehci->periodic_size - 1); } else { @@ -2334,7 +2334,7 @@ restart: union ehci_shadow temp; int live; - live = (ehci->rh_state == EHCI_RH_RUNNING); + live = (ehci->rh_state >= EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ @@ -2459,7 +2459,7 @@ restart: * We can't advance our scan without collecting the ISO * transfers that are still pending in this frame. */ - if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) { + if (incomplete && ehci->rh_state >= EHCI_RH_RUNNING) { ehci->next_uframe = now_uframe; break; } @@ -2475,7 +2475,7 @@ restart: if (now_uframe == clock) { unsigned now; - if (ehci->rh_state != EHCI_RH_RUNNING + if (ehci->rh_state < EHCI_RH_RUNNING || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 475f23e10bb..9e8e82ecce5 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -62,10 +62,15 @@ struct ehci_stats { #define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ +/* + * ehci_rh_state values of EHCI_RH_RUNNING or above mean that the + * controller may be doing DMA. Lower values mean there's no DMA. + */ enum ehci_rh_state { EHCI_RH_HALTED, EHCI_RH_SUSPENDED, - EHCI_RH_RUNNING + EHCI_RH_RUNNING, + EHCI_RH_STOPPING }; struct ehci_hcd { /* one per controller */ -- cgit v1.2.3-70-g09d2 From d58b4bcc6df8046cf9c3c59f9ff84d2cd86b93eb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:21:54 -0400 Subject: USB: EHCI: introduce high-res timer This patch (as1572) begins the conversion of ehci-hcd over to using high-resolution timers rather than old-fashioned low-resolution kernel timers. This reduces overhead caused by timer roundoff on systems where HZ is smaller than 1000. Also, the new timer framework introduced here is much more logical and easily extended than the ad-hoc approach ehci-hcd currently uses for timers. An hrtimer structure is added to ehci_hcd, along with a bitflag array and an array of ktime_t values, to keep track of which timing events are pending and what their expiration times are. Only the infrastructure for the timing operations is added in this patch. Later patches will add routines for handling each of the various timing events the driver needs. In some cases the new hrtimer handlers will replace the existing handlers for ehci-hcd's kernel timers; as this happens the old timers will be removed. In other cases the new timing events will replace busy-wait loops. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 15 +++++- drivers/usb/host/ehci-hub.c | 3 ++ drivers/usb/host/ehci-timer.c | 106 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 16 +++++++ 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/host/ehci-timer.c (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bc94822f4c5..f8fed163a23 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -30,8 +30,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -380,6 +379,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); +#include "ehci-timer.c" #include "ehci-hub.c" #include "ehci-lpm.c" #include "ehci-mem.c" @@ -494,7 +494,10 @@ static void ehci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; ehci_silence_controller(ehci); + ehci->enabled_hrtimer_events = 0; spin_unlock_irq(&ehci->lock); + + hrtimer_cancel(&ehci->hrtimer); } static void ehci_port_power (struct ehci_hcd *ehci, int is_on) @@ -561,12 +564,14 @@ static void ehci_stop (struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); + ehci->enabled_hrtimer_events = 0; ehci_quiesce(ehci); ehci_silence_controller(ehci); ehci_reset (ehci); spin_unlock_irq(&ehci->lock); + hrtimer_cancel(&ehci->hrtimer); remove_sysfs_files(ehci); remove_debug_files (ehci); @@ -615,6 +620,10 @@ static int ehci_init(struct usb_hcd *hcd) ehci->iaa_watchdog.function = ehci_iaa_watchdog; ehci->iaa_watchdog.data = (unsigned long) ehci; + hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + ehci->hrtimer.function = ehci_hrtimer_func; + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; + hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); /* @@ -954,6 +963,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) dbg_status(ehci, "fatal", status); ehci_halt(ehci); dead: + ehci->enabled_hrtimer_events = 0; + hrtimer_try_to_cancel(&ehci->hrtimer); ehci_reset(ehci); ehci_writel(ehci, 0, &ehci->regs->configured_flag); usb_hc_died(hcd); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index fb1b99e7493..25329e4b844 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -311,12 +311,15 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_readl(ehci, &ehci->regs->intr_enable); ehci->next_statechange = jiffies + msecs_to_jiffies(10); + ehci->enabled_hrtimer_events = 0; + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; spin_unlock_irq (&ehci->lock); /* ehci_work() may have re-enabled the watchdog timer, which we do not * want, and so we must delete any pending watchdog timer events. */ del_timer_sync(&ehci->watchdog); + hrtimer_cancel(&ehci->hrtimer); return 0; } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c new file mode 100644 index 00000000000..f6279e2883a --- /dev/null +++ b/drivers/usb/host/ehci-timer.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 by Alan Stern + * + * 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 the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* This file is part of ehci-hcd.c */ + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI timer support... Now using hrtimers. + * + * Lots of different events are triggered from ehci->hrtimer. Whenever + * the timer routine runs, it checks each possible event; events that are + * currently enabled and whose expiration time has passed get handled. + * The set of enabled events is stored as a collection of bitflags in + * ehci->enabled_hrtimer_events, and they are numbered in order of + * increasing delay values (ranging between 1 ms and 100 ms). + * + * Rather than implementing a sorted list or tree of all pending events, + * we keep track only of the lowest-numbered pending event, in + * ehci->next_hrtimer_event. Whenever ehci->hrtimer gets restarted, its + * expiration time is set to the timeout value for this event. + * + * As a result, events might not get handled right away; the actual delay + * could be anywhere up to twice the requested delay. This doesn't + * matter, because none of the events are especially time-critical. The + * ones that matter most all have a delay of 1 ms, so they will be + * handled after 2 ms at most, which is okay. In addition to this, we + * allow for an expiration range of 1 ms. + */ + +/* + * Delay lengths for the hrtimer event types. + * Keep this list sorted by delay length, in the same order as + * the event types indexed by enum ehci_hrtimer_event in ehci.h. + */ +static unsigned event_delays_ns[] = { +}; + +/* Enable a pending hrtimer event */ +static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event, + bool resched) +{ + ktime_t *timeout = &ehci->hr_timeouts[event]; + + if (resched) + *timeout = ktime_add(ktime_get(), + ktime_set(0, event_delays_ns[event])); + ehci->enabled_hrtimer_events |= (1 << event); + + /* Track only the lowest-numbered pending event */ + if (event < ehci->next_hrtimer_event) { + ehci->next_hrtimer_event = event; + hrtimer_start_range_ns(&ehci->hrtimer, *timeout, + NSEC_PER_MSEC, HRTIMER_MODE_ABS); + } +} + + +/* + * Handler functions for the hrtimer event types. + * Keep this array in the same order as the event types indexed by + * enum ehci_hrtimer_event in ehci.h. + */ +static void (*event_handlers[])(struct ehci_hcd *) = { +}; + +static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) +{ + struct ehci_hcd *ehci = container_of(t, struct ehci_hcd, hrtimer); + ktime_t now; + unsigned long events; + unsigned long flags; + unsigned e; + + spin_lock_irqsave(&ehci->lock, flags); + + events = ehci->enabled_hrtimer_events; + ehci->enabled_hrtimer_events = 0; + ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; + + /* + * Check each pending event. If its time has expired, handle + * the event; otherwise re-enable it. + */ + now = ktime_get(); + for_each_set_bit(e, &events, EHCI_HRTIMER_NUM_EVENTS) { + if (now.tv64 >= ehci->hr_timeouts[e].tv64) + event_handlers[e](ehci); + else + ehci_enable_event(ehci, e, false); + } + + spin_unlock_irqrestore(&ehci->lock, flags); + return HRTIMER_NORESTART; +} diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 9e8e82ecce5..070be83028d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -73,7 +73,23 @@ enum ehci_rh_state { EHCI_RH_STOPPING }; +/* + * Timer events, ordered by increasing delay length. + * Always update event_delays_ns[] and event_handlers[] (defined in + * ehci-timer.c) in parallel with this list. + */ +enum ehci_hrtimer_event { + EHCI_HRTIMER_NUM_EVENTS /* Must come last */ +}; +#define EHCI_HRTIMER_NO_EVENT 99 + struct ehci_hcd { /* one per controller */ + /* timing support */ + enum ehci_hrtimer_event next_hrtimer_event; + unsigned enabled_hrtimer_events; + ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; + struct hrtimer hrtimer; + /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; struct ehci_regs __iomem *regs; -- cgit v1.2.3-70-g09d2 From 3ca9aebac2ebb8f56d2d097636b8c568320a9f87 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:05 -0400 Subject: USB: EHCI: use hrtimer for the periodic schedule This patch (as1573) adds hrtimer support for managing ehci-hcd's periodic schedule. There are two issues to deal with. First, the schedule's state (on or off) must not be changed until the hardware status has caught up with the current command. This is handled by an hrtimer event that polls at 1-ms intervals to see when the Periodic Schedule Status (PSS) flag matches the Periodic Schedule Enable (PSE) value. Second, the schedule should not be turned off as soon as it becomes empty. Turning the schedule on and off takes time, so we want to wait until the schedule has been empty for a suitable period before turning it off. This is handled by an hrtimer event that gets set to expire 10 ms after the periodic schedule becomes empty. The existing code polls (for up to 1125 us and with interrupts disabled!) to check the status, and doesn't implement a delay before turning off the schedule. Furthermore, if the polling fails then the driver decides that the controller has died. This has caused problems for several people; some controllers can take 10 ms or more to turn off their periodic schedules. This patch fixes these issues. It also makes the "broken_periodic" workaround unnecessary; there is no longer any danger of turning off the periodic schedule after it has been on for less than 1 ms. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/ehci-pci.c | 4 --- drivers/usb/host/ehci-sched.c | 69 ++++++++----------------------------- drivers/usb/host/ehci-timer.c | 80 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 7 ++-- 5 files changed, 101 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f8fed163a23..7e00ca095ce 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -546,7 +546,7 @@ static void ehci_work (struct ehci_hcd *ehci) */ if (ehci->rh_state == EHCI_RH_RUNNING && (ehci->async->qh_next.ptr != NULL || - ehci->periodic_sched != 0)) + ehci->periodic_count != 0)) timer_action (ehci, TIMER_IO_WATCHDOG); } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 21e5f963f33..2cb7d370c4e 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -104,10 +104,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; case PCI_VENDOR_ID_INTEL: ehci->fs_i_thresh = 1; - if (pdev->device == 0x27cc) { - ehci->broken_periodic = 1; - ehci_info(ehci, "using broken periodic workaround\n"); - } if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) hcd->has_tt = 1; break; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 3429b8a33c5..f5c15880c65 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -481,67 +481,26 @@ static int tt_no_collision ( static int enable_periodic (struct ehci_hcd *ehci) { - int status; - - if (ehci->periodic_sched++) + if (ehci->periodic_count++) return 0; - /* did clearing PSE did take effect yet? - * takes effect only at frame boundaries... - */ - status = handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_PSS, 0, 9 * 125); - if (status) { - usb_hc_died(ehci_to_hcd(ehci)); - return status; - } - - ehci->command |= CMD_PSE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - /* posted write ... PSS happens later */ + /* Stop waiting to turn off the periodic schedule */ + ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC); - /* make sure ehci_work scans these */ - ehci->next_uframe = ehci_read_frame_index(ehci) - % (ehci->periodic_size << 3); - if (unlikely(ehci->broken_periodic)) - ehci->last_periodic_enable = ktime_get_real(); + /* Don't start the schedule until PSS is 0 */ + ehci_poll_PSS(ehci); return 0; } static int disable_periodic (struct ehci_hcd *ehci) { - int status; - - if (--ehci->periodic_sched) + if (--ehci->periodic_count) return 0; - if (unlikely(ehci->broken_periodic)) { - /* delay experimentally determined */ - ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000); - ktime_t now = ktime_get_real(); - s64 delay = ktime_us_delta(safe, now); - - if (unlikely(delay > 0)) - udelay(delay); - } - - /* did setting PSE not take effect yet? - * takes effect only at frame boundaries... - */ - status = handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_PSS, STS_PSS, 9 * 125); - if (status) { - usb_hc_died(ehci_to_hcd(ehci)); - return status; - } - - ehci->command &= ~CMD_PSE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - /* posted write ... */ - - free_cached_lists(ehci); + ehci->next_uframe = -1; /* the periodic schedule is empty */ - ehci->next_uframe = -1; + /* Don't turn off the schedule until PSS is 1 */ + ehci_poll_PSS(ehci); return 0; } @@ -650,8 +609,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; - /* maybe turn off periodic schedule */ - return disable_periodic(ehci); + return 0; } static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -706,6 +664,9 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) ehci_err(ehci, "can't reschedule qh %p, err %d\n", qh, rc); } + + /* maybe turn off periodic schedule */ + disable_periodic(ehci); } /*-------------------------------------------------------------------------*/ @@ -2447,7 +2408,7 @@ restart: /* assume completion callbacks modify the queue */ if (unlikely (modified)) { - if (likely(ehci->periodic_sched > 0)) + if (likely(ehci->periodic_count > 0)) goto restart; /* short-circuit this scan */ now_uframe = clock; @@ -2476,7 +2437,7 @@ restart: unsigned now; if (ehci->rh_state < EHCI_RH_RUNNING - || ehci->periodic_sched == 0) + || ehci->periodic_count == 0) break; ehci->next_uframe = now_uframe; now = ehci_read_frame_index(ehci) & (mod - 1); diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index f6279e2883a..ecd3296157c 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -16,6 +16,28 @@ /*-------------------------------------------------------------------------*/ +/* Set a bit in the USBCMD register */ +static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit) +{ + ehci->command |= bit; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + + /* unblock posted write */ + ehci_readl(ehci, &ehci->regs->command); +} + +/* Clear a bit in the USBCMD register */ +static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) +{ + ehci->command &= ~bit; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + + /* unblock posted write */ + ehci_readl(ehci, &ehci->regs->command); +} + +/*-------------------------------------------------------------------------*/ + /* * EHCI timer support... Now using hrtimers. * @@ -45,6 +67,8 @@ * the event types indexed by enum ehci_hrtimer_event in ehci.h. */ static unsigned event_delays_ns[] = { + 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ + 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ }; /* Enable a pending hrtimer event */ @@ -67,12 +91,68 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event, } +/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ +static void ehci_poll_PSS(struct ehci_hcd *ehci) +{ + unsigned actual, want; + + /* Don't do anything if the controller isn't running (e.g., died) */ + if (ehci->rh_state != EHCI_RH_RUNNING) + return; + + want = (ehci->command & CMD_PSE) ? STS_PSS : 0; + actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (ehci->PSS_poll_count++ < 20) { + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); + return; + } + ehci_warn(ehci, "Waited too long for the periodic schedule status, giving up\n"); + } + ehci->PSS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + free_cached_lists(ehci); + if (ehci->periodic_count > 0) { + + /* make sure ehci_work scans these */ + ehci->next_uframe = ehci_read_frame_index(ehci) + & ((ehci->periodic_size << 3) - 1); + ehci_set_command_bit(ehci, CMD_PSE); + } + + } else { /* Running */ + if (ehci->periodic_count == 0) { + + /* Turn off the schedule after a while */ + ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC, + true); + } + } +} + +/* Turn off the periodic schedule after a brief delay */ +static void ehci_disable_PSE(struct ehci_hcd *ehci) +{ + ehci_clear_command_bit(ehci, CMD_PSE); + + /* Poll to see when it actually stops */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by * enum ehci_hrtimer_event in ehci.h. */ static void (*event_handlers[])(struct ehci_hcd *) = { + ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ + ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ }; static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 070be83028d..da2e0ab2385 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -79,6 +79,8 @@ enum ehci_rh_state { * ehci-timer.c) in parallel with this list. */ enum ehci_hrtimer_event { + EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ }; #define EHCI_HRTIMER_NO_EVENT 99 @@ -90,6 +92,8 @@ struct ehci_hcd { /* one per controller */ ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; struct hrtimer hrtimer; + int PSS_poll_count; + /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; struct ehci_regs __iomem *regs; @@ -116,7 +120,7 @@ struct ehci_hcd { /* one per controller */ union ehci_shadow *pshadow; /* mirror hw periodic table */ int next_uframe; /* scan periodic, start here */ - unsigned periodic_sched; /* periodic activity count */ + unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ @@ -165,7 +169,6 @@ struct ehci_hcd { /* one per controller */ unsigned big_endian_capbase:1; unsigned has_amcc_usb23:1; unsigned need_io_watchdog:1; - unsigned broken_periodic:1; unsigned amd_pll_fix:1; unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ -- cgit v1.2.3-70-g09d2 From b015cb79ce84944076a8a849055f898f2a4d6be8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:10 -0400 Subject: USB: EHCI: return void instead of 0 This patch (as1574) changes the return type of multiple functions in ehci-sched.c from int to void. The values they return are now always 0, so there's no reason for them to return any value at all. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index f5c15880c65..69b1861e432 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -479,29 +479,27 @@ static int tt_no_collision ( /*-------------------------------------------------------------------------*/ -static int enable_periodic (struct ehci_hcd *ehci) +static void enable_periodic(struct ehci_hcd *ehci) { if (ehci->periodic_count++) - return 0; + return; /* Stop waiting to turn off the periodic schedule */ ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC); /* Don't start the schedule until PSS is 0 */ ehci_poll_PSS(ehci); - return 0; } -static int disable_periodic (struct ehci_hcd *ehci) +static void disable_periodic(struct ehci_hcd *ehci) { if (--ehci->periodic_count) - return 0; + return; ehci->next_uframe = -1; /* the periodic schedule is empty */ /* Don't turn off the schedule until PSS is 1 */ ehci_poll_PSS(ehci); - return 0; } /*-------------------------------------------------------------------------*/ @@ -512,7 +510,7 @@ static int disable_periodic (struct ehci_hcd *ehci) * this just links in a qh; caller guarantees uframe masks are set right. * no FSTN support (yet; ehci 0.96+) */ -static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) { unsigned i; unsigned period = qh->period; @@ -572,10 +570,10 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) : (qh->usecs * 8); /* maybe enable periodic schedule processing */ - return enable_periodic(ehci); + enable_periodic(ehci); } -static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) +static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) { unsigned i; unsigned period; @@ -608,8 +606,6 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; - - return 0; } static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -843,7 +839,7 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) ehci_dbg (ehci, "reused qh %p schedule\n", qh); /* stuff into the periodic schedule */ - status = qh_link_periodic (ehci, qh); + qh_link_periodic(ehci, qh); done: return status; } @@ -1574,8 +1570,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) } /* fit urb's itds into the selected schedule slot; activate as needed */ -static int -itd_link_urb ( +static void itd_link_urb( struct ehci_hcd *ehci, struct urb *urb, unsigned mod, @@ -1646,7 +1641,7 @@ itd_link_urb ( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); - return enable_periodic(ehci); + enable_periodic(ehci); } #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) @@ -1726,7 +1721,7 @@ itd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; - (void) disable_periodic(ehci); + disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { @@ -1993,8 +1988,7 @@ sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) } /* fit urb's sitds into the selected schedule slot; activate as needed */ -static int -sitd_link_urb ( +static void sitd_link_urb( struct ehci_hcd *ehci, struct urb *urb, unsigned mod, @@ -2056,7 +2050,7 @@ sitd_link_urb ( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); - return enable_periodic(ehci); + enable_periodic(ehci); } /*-------------------------------------------------------------------------*/ @@ -2122,7 +2116,7 @@ sitd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; - (void) disable_periodic(ehci); + disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { -- cgit v1.2.3-70-g09d2 From 9671cd7a91059bcd27665a884ee6568d31ef6857 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:16 -0400 Subject: USB: EHCI: remove PS3 status polling This patch (as1575) removes special code added for status polling of the EHCI controller in PS3 systems. While the controller is running, the polling is now carried out by an hrtimer handler. When the controller is suspending or stopping, we use the same polling routine as the old code -- but in neither case do we need to conclude that the controller has died if the polling goes on for too long. As a result the entire handshake_on_error_set_halt() routine is now unused, so it is removed from the driver. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 69 ++------------------------------------------- 1 file changed, 2 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7e00ca095ce..fd7ae16f77b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -235,68 +235,6 @@ static int ehci_halt (struct ehci_hcd *ehci) STS_HALT, STS_HALT, 16 * 125); } -#if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3) - -/* - * The EHCI controller of the Cell Super Companion Chip used in the - * PS3 will stop the root hub after all root hub ports are suspended. - * When in this condition handshake will return -ETIMEDOUT. The - * STS_HLT bit will not be set, so inspection of the frame index is - * used here to test for the condition. If the condition is found - * return success to allow the USB suspend to complete. - */ - -static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, - void __iomem *ptr, u32 mask, u32 done, - int usec) -{ - unsigned int old_index; - int error; - - if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) - return -ETIMEDOUT; - - old_index = ehci_read_frame_index(ehci); - - error = handshake(ehci, ptr, mask, done, usec); - - if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index) - return 0; - - return error; -} - -#else - -static int handshake_for_broken_root_hub(struct ehci_hcd *ehci, - void __iomem *ptr, u32 mask, u32 done, - int usec) -{ - return -ETIMEDOUT; -} - -#endif - -static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, - u32 mask, u32 done, int usec) -{ - int error; - - error = handshake(ehci, ptr, mask, done, usec); - if (error == -ETIMEDOUT) - error = handshake_for_broken_root_hub(ehci, ptr, mask, done, - usec); - - if (error) { - ehci_halt(ehci); - ehci->rh_state = EHCI_RH_HALTED; - ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n", - ptr, mask, done, error); - } - - return error; -} - /* put TDI/ARC silicon into EHCI mode */ static void tdi_reset (struct ehci_hcd *ehci) { @@ -361,17 +299,14 @@ static void ehci_quiesce (struct ehci_hcd *ehci) /* wait for any schedule enables/disables to take effect */ temp = (ehci->command << 10) & (STS_ASS | STS_PSS); - if (handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_ASS | STS_PSS, temp, 16 * 125)) - return; + handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); /* then disable anything that's still active */ ehci->command &= ~(CMD_ASE | CMD_PSE); ehci_writel(ehci, ehci->command, &ehci->regs->command); /* hardware can take 16 microframes to turn off ... */ - handshake_on_error_set_halt(ehci, &ehci->regs->status, - STS_ASS | STS_PSS, 0, 16 * 125); + handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-70-g09d2 From 314466101c6ae14f6f5db8a86eda1509ba2c02a8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:21 -0400 Subject: USB: EHCI: use hrtimer for async schedule This patch (as1576) adds hrtimer support for managing ehci-hcd's async schedule. Just as with the earlier change to the periodic schedule management, two new hrtimer events take care of everything. One event polls at 1-ms intervals to see when the Asynchronous Schedule Status (ASS) flag matches the Asynchronous Schedule Enable (ASE) value; the schedule's state must not be changed until it does. The other event delays for 15 ms after the async schedule becomes empty before turning it off. The new events replace a busy-wait poll and a kernel timer usage. They also replace the rather illogical method currently used for indicating the async schedule should be turned off: attempting to unlink the dedicated QH at the head of the async list. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 13 ++------- drivers/usb/host/ehci-q.c | 68 +++++++++++++++++++------------------------ drivers/usb/host/ehci-timer.c | 49 +++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 5 +++- 4 files changed, 86 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fd7ae16f77b..21d6fbc0a32 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -95,7 +95,6 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ -#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ #define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) /* 5-ms async qh unlink delay */ @@ -137,7 +136,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) * SHRINK were pending, OFF would never be requested. */ if (timer_pending(&ehci->watchdog) - && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) + && (BIT(TIMER_ASYNC_SHRINK) & ehci->actions)) return; @@ -150,9 +149,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) return; t = EHCI_IO_JIFFIES; break; - case TIMER_ASYNC_OFF: - t = EHCI_ASYNC_JIFFIES; - break; /* case TIMER_ASYNC_SHRINK: */ default: t = EHCI_SHRINK_JIFFIES; @@ -376,10 +372,6 @@ static void ehci_watchdog(unsigned long param) spin_lock_irqsave(&ehci->lock, flags); - /* stop async processing after it's idled a bit */ - if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) - start_unlink_async (ehci, ehci->async); - /* ehci could run by timer, without IRQs ... */ ehci_work (ehci); @@ -470,7 +462,8 @@ static void ehci_work (struct ehci_hcd *ehci) if (ehci->scanning) return; ehci->scanning = 1; - scan_async (ehci); + if (ehci->async_count) + scan_async(ehci); if (ehci->next_uframe != -1) scan_periodic (ehci); ehci->scanning = 0; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 285d5a0f3f7..d68764ef447 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -964,6 +964,30 @@ done: /*-------------------------------------------------------------------------*/ +static void enable_async(struct ehci_hcd *ehci) +{ + if (ehci->async_count++) + return; + + /* Stop waiting to turn off the async schedule */ + ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC); + + /* Don't start the schedule until ASS is 0 */ + ehci_poll_ASS(ehci); +} + +static void disable_async(struct ehci_hcd *ehci) +{ + if (--ehci->async_count) + return; + + /* The async schedule and async_unlink list are supposed to be empty */ + WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink); + + /* Don't turn off the schedule until ASS is 1 */ + ehci_poll_ASS(ehci); +} + /* move qh (and its qtds) onto async queue; maybe enable queue. */ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -977,24 +1001,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) WARN_ON(qh->qh_state != QH_STATE_IDLE); - /* (re)start the async schedule? */ - head = ehci->async; - timer_action_done (ehci, TIMER_ASYNC_OFF); - if (!head->qh_next.qh) { - if (!(ehci->command & CMD_ASE)) { - /* in case a clear of CMD_ASE didn't take yet */ - (void)handshake(ehci, &ehci->regs->status, - STS_ASS, 0, 150); - ehci->command |= CMD_ASE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - /* posted write need not be known to HC yet ... */ - } - } - /* clear halt and/or toggle; and maybe recover from silicon quirk */ qh_refresh(ehci, qh); /* splice right after start */ + head = ehci->async; qh->qh_next = head->qh_next; qh->hw->hw_next = head->hw->hw_next; wmb (); @@ -1005,6 +1016,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->xacterrs = 0; qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ + + enable_async(ehci); } /*-------------------------------------------------------------------------*/ @@ -1173,16 +1186,10 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh_completions (ehci, qh); - if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { + if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) qh_link_async (ehci, qh); - } else { - /* it's not free to turn the async schedule on/off; leave it - * active but idle for a while once it empties. - */ - if (ehci->rh_state == EHCI_RH_RUNNING - && ehci->async->qh_next.qh == NULL) - timer_action (ehci, TIMER_ASYNC_OFF); - } + + disable_async(ehci); if (next) { ehci->async_unlink = NULL; @@ -1210,21 +1217,6 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) BUG (); #endif - /* stop async schedule right now? */ - if (unlikely (qh == ehci->async)) { - /* can't get here without STS_ASS set */ - if (ehci->rh_state != EHCI_RH_HALTED - && !ehci->async_unlink) { - /* ... and CMD_IAAD clear */ - ehci->command &= ~CMD_ASE; - ehci_writel(ehci, ehci->command, &ehci->regs->command); - wmb (); - // handshake later, if we need to - timer_action_done (ehci, TIMER_ASYNC_OFF); - } - return; - } - qh->qh_state = QH_STATE_UNLINK; ehci->async_unlink = qh; if (!qh->unlink_next) diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index ecd3296157c..1e907dd3bb1 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -67,8 +67,10 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) * the event types indexed by enum ehci_hrtimer_event in ehci.h. */ static unsigned event_delays_ns[] = { + 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ + 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; /* Enable a pending hrtimer event */ @@ -91,6 +93,51 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event, } +/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ +static void ehci_poll_ASS(struct ehci_hcd *ehci) +{ + unsigned actual, want; + + /* Don't enable anything if the controller isn't running (e.g., died) */ + if (ehci->rh_state != EHCI_RH_RUNNING) + return; + + want = (ehci->command & CMD_ASE) ? STS_ASS : 0; + actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS; + + if (want != actual) { + + /* Poll again later, but give up after about 20 ms */ + if (ehci->ASS_poll_count++ < 20) { + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); + return; + } + ehci_warn(ehci, "Waited too long for the async schedule status, giving up\n"); + } + ehci->ASS_poll_count = 0; + + /* The status is up-to-date; restart or stop the schedule as needed */ + if (want == 0) { /* Stopped */ + if (ehci->async_count > 0) + ehci_set_command_bit(ehci, CMD_ASE); + + } else { /* Running */ + if (ehci->async_count == 0) { + + /* Turn off the schedule after a while */ + ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC, + true); + } + } +} + +/* Turn off the async schedule after a brief delay */ +static void ehci_disable_ASE(struct ehci_hcd *ehci) +{ + ehci_clear_command_bit(ehci, CMD_ASE); +} + + /* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ static void ehci_poll_PSS(struct ehci_hcd *ehci) { @@ -151,8 +198,10 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) * enum ehci_hrtimer_event in ehci.h. */ static void (*event_handlers[])(struct ehci_hcd *) = { + ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ + ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index da2e0ab2385..bf06bbb77ba 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -79,8 +79,10 @@ enum ehci_rh_state { * ehci-timer.c) in parallel with this list. */ enum ehci_hrtimer_event { + EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ + EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ }; #define EHCI_HRTIMER_NO_EVENT 99 @@ -93,6 +95,7 @@ struct ehci_hcd { /* one per controller */ struct hrtimer hrtimer; int PSS_poll_count; + int ASS_poll_count; /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -110,6 +113,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async_unlink_last; struct ehci_qh *qh_scan_next; unsigned scanning : 1; + unsigned async_count; /* async activity count */ /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ @@ -229,7 +233,6 @@ static inline void iaa_watchdog_done(struct ehci_hcd *ehci) enum ehci_timer_action { TIMER_IO_WATCHDOG, TIMER_ASYNC_SHRINK, - TIMER_ASYNC_OFF, }; static inline void -- cgit v1.2.3-70-g09d2 From df2022553dd8d34d49e16c19d851ea619438f0ef Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:26 -0400 Subject: USB: EHCI: use hrtimer for interrupt QH unlink This patch (as1577) adds hrtimer support for unlinking interrupt QHs in ehci-hcd. The current code relies on a fixed delay of either 2 or 55 us, which is not always adequate and in any case is totally bogus. Thanks to internal caching, the EHCI hardware may continue to access an interrupt QH for more than a millisecond after it has been unlinked. In fact, the EHCI spec doesn't say how long to wait before using an unlinked interrupt QH. The patch sets the delay to 9 microframes minimum, which ought to be adequate. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 6 ++-- drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-sched.c | 71 ++++++++++++++++++++++++++++--------------- drivers/usb/host/ehci-timer.c | 34 +++++++++++++++++++++ drivers/usb/host/ehci.h | 10 +++++- 5 files changed, 94 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 21d6fbc0a32..edcfd2c4295 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -309,6 +309,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci) static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); +static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); +static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); #include "ehci-timer.c" #include "ehci-hub.c" @@ -1034,7 +1036,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) switch (qh->qh_state) { case QH_STATE_LINKED: case QH_STATE_COMPLETING: - intr_deschedule (ehci, qh); + start_unlink_intr(ehci, qh); break; case QH_STATE_IDLE: qh_completions (ehci, qh); @@ -1164,7 +1166,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) if (eptype == USB_ENDPOINT_XFER_BULK) unlink_async(ehci, qh); else - intr_deschedule(ehci, qh); + start_unlink_intr(ehci, qh); } } spin_unlock_irqrestore(&ehci->lock, flags); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 25329e4b844..8aa740dc510 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -302,6 +302,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (ehci->async_unlink) end_unlink_async(ehci); + ehci_handle_intr_unlinks(ehci); /* allow remote wakeup */ mask = INTR_MASK; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 69b1861e432..eec8446f8de 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -578,12 +578,20 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) unsigned i; unsigned period; - // FIXME: - // IF this isn't high speed - // and this qh is active in the current uframe - // (and overlay token SplitXstate is false?) - // THEN - // qh->hw_info1 |= cpu_to_hc32(1 << 7 /* "ignore" */); + /* + * If qh is for a low/full-speed device, simply unlinking it + * could interfere with an ongoing split transaction. To unlink + * it safely would require setting the QH_INACTIVATE bit and + * waiting at least one frame, as described in EHCI 4.12.2.5. + * + * We won't bother with any of this. Instead, we assume that the + * only reason for unlinking an interrupt QH while the current URB + * is still active is to dequeue all the URBs (flush the whole + * endpoint queue). + * + * If rebalancing the periodic schedule is ever implemented, this + * approach will no longer be valid. + */ /* high bandwidth, or otherwise part of every microframe */ if ((period = qh->period) == 0) @@ -608,12 +616,8 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_next.ptr = NULL; } -static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) { - unsigned wait; - struct ehci_qh_hw *hw = qh->hw; - int rc; - /* If the QH isn't linked then there's nothing we can do * unless we were called during a giveback, in which case * qh_completions() has to deal with it. @@ -626,28 +630,45 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) qh_unlink_periodic (ehci, qh); - /* simple/paranoid: always delay, expecting the HC needs to read - * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and - * expect khubd to clean up after any CSPLITs we won't issue. - * active high speed queues may need bigger delays... + /* Make sure the unlinks are visible before starting the timer */ + wmb(); + + /* + * The EHCI spec doesn't say how long it takes the controller to + * stop accessing an unlinked interrupt QH. The timer delay is + * 9 uframes; presumably that will be long enough. */ - if (list_empty (&qh->qtd_list) - || (cpu_to_hc32(ehci, QH_CMASK) - & hw->hw_info2) != 0) - wait = 2; + qh->unlink_cycle = ehci->intr_unlink_cycle; + + /* New entries go at the end of the intr_unlink list */ + if (ehci->intr_unlink) + ehci->intr_unlink_last->unlink_next = qh; else - wait = 55; /* worst case: 3 * 1024 */ + ehci->intr_unlink = qh; + ehci->intr_unlink_last = qh; + + if (ehci->intr_unlinking) + ; /* Avoid recursive calls */ + else if (ehci->rh_state < EHCI_RH_RUNNING) + ehci_handle_intr_unlinks(ehci); + else if (ehci->intr_unlink == qh) { + ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); + ++ehci->intr_unlink_cycle; + } +} + +static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + struct ehci_qh_hw *hw = qh->hw; + int rc; - udelay (wait); qh->qh_state = QH_STATE_IDLE; hw->hw_next = EHCI_LIST_END(ehci); - wmb (); qh_completions(ehci, qh); /* reschedule QH iff another request is queued */ - if (!list_empty(&qh->qtd_list) && - ehci->rh_state == EHCI_RH_RUNNING) { + if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { rc = qh_schedule(ehci, qh); /* An error here likely indicates handshake failure @@ -2302,7 +2323,7 @@ restart: temp.qh->stamp = ehci->periodic_stamp; if (unlikely(list_empty(&temp.qh->qtd_list) || temp.qh->needs_rescan)) - intr_deschedule(ehci, temp.qh); + start_unlink_intr(ehci, temp.qh); } break; case Q_TYPE_FSTN: diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 1e907dd3bb1..bd8b591771b 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ + 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; @@ -192,6 +193,38 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) } +/* Handle unlinked interrupt QHs once they are gone from the hardware */ +static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) +{ + bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); + + /* + * Process all the QHs on the intr_unlink list that were added + * before the current unlink cycle began. The list is in + * temporal order, so stop when we reach the first entry in the + * current cycle. But if the root hub isn't running then + * process all the QHs on the list. + */ + ehci->intr_unlinking = true; + while (ehci->intr_unlink) { + struct ehci_qh *qh = ehci->intr_unlink; + + if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) + break; + ehci->intr_unlink = qh->unlink_next; + qh->unlink_next = NULL; + end_unlink_intr(ehci, qh); + } + + /* Handle remaining entries later */ + if (ehci->intr_unlink) { + ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); + ++ehci->intr_unlink_cycle; + } + ehci->intr_unlinking = false; +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -200,6 +233,7 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) static void (*event_handlers[])(struct ehci_hcd *) = { ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ + ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bf06bbb77ba..f36f1f85d7f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -81,6 +81,7 @@ enum ehci_rh_state { enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ @@ -106,13 +107,16 @@ struct ehci_hcd { /* one per controller */ spinlock_t lock; enum ehci_rh_state rh_state; + /* general schedule support */ + unsigned scanning:1; + bool intr_unlinking:1; + /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *async_unlink; struct ehci_qh *async_unlink_last; struct ehci_qh *qh_scan_next; - unsigned scanning : 1; unsigned async_count; /* async activity count */ /* periodic schedule support */ @@ -123,6 +127,9 @@ struct ehci_hcd { /* one per controller */ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ + struct ehci_qh *intr_unlink; + struct ehci_qh *intr_unlink_last; + unsigned intr_unlink_cycle; int next_uframe; /* scan periodic, start here */ unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ @@ -385,6 +392,7 @@ struct ehci_qh { struct ehci_qh *unlink_next; /* next on unlink list */ unsigned long unlink_time; + unsigned unlink_cycle; unsigned stamp; u8 needs_rescan; /* Dequeue during giveback */ -- cgit v1.2.3-70-g09d2 From bf6387bcd16975ba8952b094f262a359d74e1c8a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:31 -0400 Subject: USB: EHCI: use hrtimer for controller death This patch (as1578) adds an hrtimer event to handle the death of an EHCI controller. When a controller dies, it doesn't necessarily stop running right away. The new event polls at 1-ms intervals to see when all activity has safely stopped. This replaces a busy-wait polling loop in the current code. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 20 ++++++++++---------- drivers/usb/host/ehci-timer.c | 26 ++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 2 ++ 3 files changed, 38 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index edcfd2c4295..1676c66b853 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -888,20 +888,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { ehci_err(ehci, "fatal error\n"); - ehci->rh_state = EHCI_RH_STOPPING; dbg_cmd(ehci, "fatal", cmd); dbg_status(ehci, "fatal", status); - ehci_halt(ehci); dead: - ehci->enabled_hrtimer_events = 0; - hrtimer_try_to_cancel(&ehci->hrtimer); - ehci_reset(ehci); - ehci_writel(ehci, 0, &ehci->regs->configured_flag); usb_hc_died(hcd); - /* generic layer kills/unlinks all urbs, then - * uses ehci_stop to clean up the rest - */ - bh = 1; + + /* Don't let the controller do anything more */ + ehci->rh_state = EHCI_RH_STOPPING; + ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + ehci_handle_controller_death(ehci); + + /* Handle completions when the controller stops */ + bh = 0; } if (bh) diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index bd8b591771b..0c5326a8883 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ + 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ @@ -193,6 +194,30 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci) } +/* Poll the STS_HALT status bit; see when a dead controller stops */ +static void ehci_handle_controller_death(struct ehci_hcd *ehci) +{ + if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) { + + /* Give up after a few milliseconds */ + if (ehci->died_poll_count++ < 5) { + /* Try again later */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true); + return; + } + ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n"); + } + + /* Clean up the mess */ + ehci->rh_state = EHCI_RH_HALTED; + ehci_writel(ehci, 0, &ehci->regs->configured_flag); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + ehci_work(ehci); + + /* Not in process context, so don't try to reset the controller */ +} + + /* Handle unlinked interrupt QHs once they are gone from the hardware */ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) { @@ -233,6 +258,7 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) static void (*event_handlers[])(struct ehci_hcd *) = { ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ + ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index f36f1f85d7f..6874d89b0b6 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -81,6 +81,7 @@ enum ehci_rh_state { enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ + EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ @@ -97,6 +98,7 @@ struct ehci_hcd { /* one per controller */ int PSS_poll_count; int ASS_poll_count; + int died_poll_count; /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; -- cgit v1.2.3-70-g09d2 From 55934eb3b9fa52eb53b9d7342267fc73c38206aa Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:35 -0400 Subject: USB: EHCI: use hrtimer for (s)iTD deallocation This patch (as1579) adds an hrtimer event to handle deallocation of iTDs and siTDs in ehci-hcd. Because of the frame-oriented approach used by the EHCI periodic schedule, the hardware can continue to access the Transfer Descriptor for isochronous (or split-isochronous) transactions for up to a millisecond after the transaction completes. The iTD (or siTD) must not be reused before then. The strategy currently used involves putting completed iTDs on a list of cached entries and every so often returning them to the endpoint's free list. The new strategy reduces overhead by putting completed iTDs back on the free list immediately, although they are not reused until it is safe to do so. When the isochronous endpoint stops (its queue becomes empty), the iTDs on its free list get moved to a global list, from which they will be deallocated after a minimum of 2 ms. This delay is what the new hrtimer event is for. Overall this may not be a tremendous improvement over the current code, but to me it seems a lot more clear and logical. In addition, it removes the need for each iTD to keep a reference to the ehci_iso_stream it belongs to, since the iTD never needs to be moved back to the stream's free list from the global list. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 1 + drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-mem.c | 1 - drivers/usb/host/ehci-sched.c | 137 ++++++++++++------------------------------ drivers/usb/host/ehci-timer.c | 50 +++++++++++++-- drivers/usb/host/ehci.h | 5 +- 6 files changed, 90 insertions(+), 105 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1676c66b853..98b945840c9 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -509,6 +509,7 @@ static void ehci_stop (struct usb_hcd *hcd) spin_lock_irq (&ehci->lock); if (ehci->async) ehci_work (ehci); + end_free_itds(ehci); spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8aa740dc510..6ad806bbe46 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -303,6 +303,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (ehci->async_unlink) end_unlink_async(ehci); ehci_handle_intr_unlinks(ehci); + end_free_itds(ehci); /* allow remote wakeup */ mask = INTR_MASK; diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 93132d8ad36..ef2c3a1eca4 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -118,7 +118,6 @@ fail: static void ehci_mem_cleanup (struct ehci_hcd *ehci) { - free_cached_lists(ehci); if (ehci->async) qh_destroy(ehci, ehci->async); ehci->async = NULL; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index eec8446f8de..1e4f13c11b6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1045,31 +1045,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) if (stream->refcount == 1) { // BUG_ON (!list_empty(&stream->td_list)); - while (!list_empty (&stream->free_list)) { - struct list_head *entry; - - entry = stream->free_list.next; - list_del (entry); - - /* knows about ITD vs SITD */ - if (stream->highspeed) { - struct ehci_itd *itd; - - itd = list_entry (entry, struct ehci_itd, - itd_list); - dma_pool_free (ehci->itd_pool, itd, - itd->itd_dma); - } else { - struct ehci_sitd *sitd; - - sitd = list_entry (entry, struct ehci_sitd, - sitd_list); - dma_pool_free (ehci->sitd_pool, sitd, - sitd->sitd_dma); - } - } - - stream->bEndpointAddress &= 0x0f; if (stream->ep) stream->ep->hcpriv = NULL; @@ -1230,17 +1205,19 @@ itd_urb_transaction ( spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < num_itds; i++) { - /* free_list.next might be cache-hot ... but maybe - * the HC caches it too. avoid that issue for now. + /* + * Use iTDs from the free list, but not iTDs that may + * still be in use by the hardware. */ - - /* prefer previously-allocated itds */ - if (likely (!list_empty(&stream->free_list))) { - itd = list_entry (stream->free_list.prev, + if (likely(!list_empty(&stream->free_list))) { + itd = list_first_entry(&stream->free_list, struct ehci_itd, itd_list); + if (itd->frame == ehci->clock_frame) + goto alloc_itd; list_del (&itd->itd_list); itd_dma = itd->itd_dma; } else { + alloc_itd: spin_unlock_irqrestore (&ehci->lock, flags); itd = dma_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); @@ -1762,24 +1739,18 @@ itd_complete ( done: itd->urb = NULL; - if (ehci->clock_frame != itd->frame || itd->index[7] != -1) { - /* OK to recycle this ITD now. */ - itd->stream = NULL; - list_move(&itd->itd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } else { - /* HW might remember this ITD, so we can't recycle it yet. - * Move it to a safe place until a new frame starts. - */ - list_move(&itd->itd_list, &ehci->cached_itd_list); - if (stream->refcount == 2) { - /* If iso_stream_put() were called here, stream - * would be freed. Instead, just prevent reuse. - */ - stream->ep->hcpriv = NULL; - stream->ep = NULL; - } + + /* Add to the end of the free list for later reuse */ + list_move_tail(&itd->itd_list, &stream->free_list); + + /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ + if (list_empty(&stream->td_list)) { + list_splice_tail_init(&stream->free_list, + &ehci->cached_itd_list); + start_free_itds(ehci); } + + iso_stream_put(ehci, stream); return retval; } @@ -1930,17 +1901,19 @@ sitd_urb_transaction ( * means we never need two sitds for full speed packets. */ - /* free_list.next might be cache-hot ... but maybe - * the HC caches it too. avoid that issue for now. + /* + * Use siTDs from the free list, but not siTDs that may + * still be in use by the hardware. */ - - /* prefer previously-allocated sitds */ - if (!list_empty(&stream->free_list)) { - sitd = list_entry (stream->free_list.prev, + if (likely(!list_empty(&stream->free_list))) { + sitd = list_first_entry(&stream->free_list, struct ehci_sitd, sitd_list); + if (sitd->frame == ehci->clock_frame) + goto alloc_sitd; list_del (&sitd->sitd_list); sitd_dma = sitd->sitd_dma; } else { + alloc_sitd: spin_unlock_irqrestore (&ehci->lock, flags); sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags, &sitd_dma); @@ -2157,24 +2130,18 @@ sitd_complete ( done: sitd->urb = NULL; - if (ehci->clock_frame != sitd->frame) { - /* OK to recycle this SITD now. */ - sitd->stream = NULL; - list_move(&sitd->sitd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } else { - /* HW might remember this SITD, so we can't recycle it yet. - * Move it to a safe place until a new frame starts. - */ - list_move(&sitd->sitd_list, &ehci->cached_sitd_list); - if (stream->refcount == 2) { - /* If iso_stream_put() were called here, stream - * would be freed. Instead, just prevent reuse. - */ - stream->ep->hcpriv = NULL; - stream->ep = NULL; - } + + /* Add to the end of the free list for later reuse */ + list_move_tail(&sitd->sitd_list, &stream->free_list); + + /* Recycle the siTDs when the pipeline is empty (ep no longer in use) */ + if (list_empty(&stream->td_list)) { + list_splice_tail_init(&stream->free_list, + &ehci->cached_sitd_list); + start_free_itds(ehci); } + + iso_stream_put(ehci, stream); return retval; } @@ -2239,28 +2206,6 @@ done: /*-------------------------------------------------------------------------*/ -static void free_cached_lists(struct ehci_hcd *ehci) -{ - struct ehci_itd *itd, *n; - struct ehci_sitd *sitd, *sn; - - list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { - struct ehci_iso_stream *stream = itd->stream; - itd->stream = NULL; - list_move(&itd->itd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } - - list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { - struct ehci_iso_stream *stream = sitd->stream; - sitd->stream = NULL; - list_move(&sitd->sitd_list, &stream->free_list); - iso_stream_put(ehci, stream); - } -} - -/*-------------------------------------------------------------------------*/ - static void scan_periodic (struct ehci_hcd *ehci) { @@ -2282,10 +2227,7 @@ scan_periodic (struct ehci_hcd *ehci) clock = now_uframe + mod - 1; clock_frame = -1; } - if (ehci->clock_frame != clock_frame) { - free_cached_lists(ehci); - ehci->clock_frame = clock_frame; - } + ehci->clock_frame = clock_frame; clock &= mod - 1; clock_frame = clock >> 3; ++ehci->periodic_stamp; @@ -2463,7 +2405,6 @@ restart: clock = now; clock_frame = clock >> 3; if (ehci->clock_frame != clock_frame) { - free_cached_lists(ehci); ehci->clock_frame = clock_frame; ++ehci->periodic_stamp; } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 0c5326a8883..8feb60ff422 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -71,6 +71,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ + 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; @@ -165,7 +166,6 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) /* The status is up-to-date; restart or stop the schedule as needed */ if (want == 0) { /* Stopped */ - free_cached_lists(ehci); if (ehci->periodic_count > 0) { /* make sure ehci_work scans these */ @@ -188,9 +188,6 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) static void ehci_disable_PSE(struct ehci_hcd *ehci) { ehci_clear_command_bit(ehci, CMD_PSE); - - /* Poll to see when it actually stops */ - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); } @@ -250,6 +247,50 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) } +/* Start another free-iTDs/siTDs cycle */ +static void start_free_itds(struct ehci_hcd *ehci) +{ + if (!(ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_FREE_ITDS))) { + ehci->last_itd_to_free = list_entry( + ehci->cached_itd_list.prev, + struct ehci_itd, itd_list); + ehci->last_sitd_to_free = list_entry( + ehci->cached_sitd_list.prev, + struct ehci_sitd, sitd_list); + ehci_enable_event(ehci, EHCI_HRTIMER_FREE_ITDS, true); + } +} + +/* Wait for controller to stop using old iTDs and siTDs */ +static void end_free_itds(struct ehci_hcd *ehci) +{ + struct ehci_itd *itd, *n; + struct ehci_sitd *sitd, *sn; + + if (ehci->rh_state < EHCI_RH_RUNNING) { + ehci->last_itd_to_free = NULL; + ehci->last_sitd_to_free = NULL; + } + + list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { + list_del(&itd->itd_list); + dma_pool_free(ehci->itd_pool, itd, itd->itd_dma); + if (itd == ehci->last_itd_to_free) + break; + } + list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { + list_del(&sitd->sitd_list); + dma_pool_free(ehci->sitd_pool, sitd, sitd->sitd_dma); + if (sitd == ehci->last_sitd_to_free) + break; + } + + if (!list_empty(&ehci->cached_itd_list) || + !list_empty(&ehci->cached_sitd_list)) + start_free_itds(ehci); +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -260,6 +301,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ + end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 6874d89b0b6..bcfbb175e2b 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -83,6 +83,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ + EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ @@ -139,7 +140,9 @@ struct ehci_hcd { /* one per controller */ /* list of itds & sitds completed while clock_frame was still active */ struct list_head cached_itd_list; + struct ehci_itd *last_itd_to_free; struct list_head cached_sitd_list; + struct ehci_sitd *last_sitd_to_free; unsigned clock_frame; /* per root hub port */ @@ -250,8 +253,6 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) clear_bit (action, &ehci->actions); } -static void free_cached_lists(struct ehci_hcd *ehci); - /*-------------------------------------------------------------------------*/ #include -- cgit v1.2.3-70-g09d2 From 8c5bf7be56f1a8aecc1f802f132d53f556a9bc45 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:39 -0400 Subject: USB: EHCI: don't refcount iso_stream structures This patch (as1580) makes ehci_iso_stream structures behave more like QHs, in that they will remain allocated until their isochronous endpoint is disabled. This will come in useful in the future, when periodic bandwidth gets allocated as an altsetting is installed rather than on-the-fly. For now, the change to the ehci_iso_stream lifetimes means that each structure is always deallocated at exactly one spot in ehci_endpoint_disable() and never used again. As a result, it is no longer necessary to use reference counting on these things, and the patch removes it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 12 +++++++--- drivers/usb/host/ehci-sched.c | 53 +++++-------------------------------------- drivers/usb/host/ehci.h | 3 +-- 3 files changed, 16 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 98b945840c9..fb1966cf564 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1085,8 +1085,14 @@ rescan: * accelerate iso completions ... so spin a while. */ if (qh->hw == NULL) { - ehci_vdbg (ehci, "iso delay\n"); - goto idle_timeout; + struct ehci_iso_stream *stream = ep->hcpriv; + + if (!list_empty(&stream->td_list)) + goto idle_timeout; + + /* BUG_ON(!list_empty(&stream->free_list)); */ + kfree(stream); + goto done; } if (ehci->rh_state < EHCI_RH_RUNNING) @@ -1127,8 +1133,8 @@ idle_timeout: list_empty (&qh->qtd_list) ? "" : "(has tds)"); break; } + done: ep->hcpriv = NULL; -done: spin_unlock_irqrestore (&ehci->lock, flags); } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 1e4f13c11b6..11b2f21d7ac 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -934,7 +934,6 @@ iso_stream_alloc (gfp_t mem_flags) INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); stream->next_uframe = -1; - stream->refcount = 1; } return stream; } @@ -1034,32 +1033,6 @@ iso_stream_init ( stream->maxp = maxp; } -static void -iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) -{ - stream->refcount--; - - /* free whenever just a dev->ep reference remains. - * not like a QH -- no persistent state (toggle, halt) - */ - if (stream->refcount == 1) { - // BUG_ON (!list_empty(&stream->td_list)); - - if (stream->ep) - stream->ep->hcpriv = NULL; - - kfree(stream); - } -} - -static inline struct ehci_iso_stream * -iso_stream_get (struct ehci_iso_stream *stream) -{ - if (likely (stream != NULL)) - stream->refcount++; - return stream; -} - static struct ehci_iso_stream * iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) { @@ -1080,7 +1053,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) if (unlikely (stream == NULL)) { stream = iso_stream_alloc(GFP_ATOMIC); if (likely (stream != NULL)) { - /* dev->ep owns the initial refcount */ ep->hcpriv = stream; stream->ep = ep; iso_stream_init(ehci, stream, urb->dev, urb->pipe, @@ -1095,9 +1067,6 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) stream = NULL; } - /* caller guarantees an eventual matching iso_stream_put */ - stream = iso_stream_get (stream); - spin_unlock_irqrestore (&ehci->lock, flags); return stream; } @@ -1611,7 +1580,7 @@ static void itd_link_urb( itd = list_entry (iso_sched->td_list.next, struct ehci_itd, itd_list); list_move_tail (&itd->itd_list, &stream->td_list); - itd->stream = iso_stream_get (stream); + itd->stream = stream; itd->urb = urb; itd_init (ehci, stream, itd); } @@ -1735,7 +1704,6 @@ itd_complete ( dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } - iso_stream_put (ehci, stream); done: itd->urb = NULL; @@ -1750,7 +1718,6 @@ done: start_free_itds(ehci); } - iso_stream_put(ehci, stream); return retval; } @@ -1807,12 +1774,9 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); else usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); -done_not_linked: + done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); - -done: - if (unlikely (status < 0)) - iso_stream_put (ehci, stream); + done: return status; } @@ -2028,7 +1992,7 @@ static void sitd_link_urb( sitd = list_entry (sched->td_list.next, struct ehci_sitd, sitd_list); list_move_tail (&sitd->sitd_list, &stream->td_list); - sitd->stream = iso_stream_get (stream); + sitd->stream = stream; sitd->urb = urb; sitd_patch(ehci, stream, sitd, sched, packet); @@ -2126,7 +2090,6 @@ sitd_complete ( dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } - iso_stream_put (ehci, stream); done: sitd->urb = NULL; @@ -2141,7 +2104,6 @@ done: start_free_itds(ehci); } - iso_stream_put(ehci, stream); return retval; } @@ -2195,12 +2157,9 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); else usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); -done_not_linked: + done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); - -done: - if (status < 0) - iso_stream_put (ehci, stream); + done: return status; } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bcfbb175e2b..755e30b0000 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -386,7 +386,7 @@ struct ehci_qh_hw { } __attribute__ ((aligned(32))); struct ehci_qh { - struct ehci_qh_hw *hw; + struct ehci_qh_hw *hw; /* Must come first */ /* the rest is HCD-private */ dma_addr_t qh_dma; /* address of qh */ union ehci_shadow qh_next; /* ptr to qh; or periodic */ @@ -453,7 +453,6 @@ struct ehci_iso_stream { /* first field matches ehci_hq, but is NULL */ struct ehci_qh_hw *hw; - u32 refcount; u8 bEndpointAddress; u8 highspeed; struct list_head td_list; /* queued itds/sitds */ -- cgit v1.2.3-70-g09d2 From 9d9387475af261949f61a5ec465e1f762d7be08a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:44 -0400 Subject: USB: EHCI: use hrtimer for the IAA watchdog This patch (as1581) replaces the iaa_watchdog kernel timer used by ehci-hcd with an hrtimer event, in keeping with the general conversion to high-res timers. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 66 +++++++++---------------------------------- drivers/usb/host/ehci-hub.c | 1 - drivers/usb/host/ehci-q.c | 4 +-- drivers/usb/host/ehci-timer.c | 45 +++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 16 +---------- 5 files changed, 61 insertions(+), 71 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fb1966cf564..f74ba277c22 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -93,7 +93,6 @@ static const char hcd_name [] = "ehci_hcd"; */ #define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ -#define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) /* 5-ms async qh unlink delay */ @@ -322,51 +321,6 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); /*-------------------------------------------------------------------------*/ -static void ehci_iaa_watchdog(unsigned long param) -{ - struct ehci_hcd *ehci = (struct ehci_hcd *) param; - unsigned long flags; - - spin_lock_irqsave (&ehci->lock, flags); - - /* Lost IAA irqs wedge things badly; seen first with a vt8235. - * So we need this watchdog, but must protect it against both - * (a) SMP races against real IAA firing and retriggering, and - * (b) clean HC shutdown, when IAA watchdog was pending. - */ - if (ehci->async_unlink - && !timer_pending(&ehci->iaa_watchdog) - && ehci->rh_state == EHCI_RH_RUNNING) { - u32 cmd, status; - - /* If we get here, IAA is *REALLY* late. It's barely - * conceivable that the system is so busy that CMD_IAAD - * is still legitimately set, so let's be sure it's - * clear before we read STS_IAA. (The HC should clear - * CMD_IAAD when it sets STS_IAA.) - */ - cmd = ehci_readl(ehci, &ehci->regs->command); - - /* If IAA is set here it either legitimately triggered - * before we cleared IAAD above (but _way_ late, so we'll - * still count it as lost) ... or a silicon erratum: - * - VIA seems to set IAA without triggering the IRQ; - * - IAAD potentially cleared without setting IAA. - */ - status = ehci_readl(ehci, &ehci->regs->status); - if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { - COUNT (ehci->stats.lost_iaa); - ehci_writel(ehci, STS_IAA, &ehci->regs->status); - } - - ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n", - status, cmd); - end_unlink_async(ehci); - } - - spin_unlock_irqrestore(&ehci->lock, flags); -} - static void ehci_watchdog(unsigned long param) { struct ehci_hcd *ehci = (struct ehci_hcd *) param; @@ -418,7 +372,6 @@ static void ehci_shutdown(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); del_timer_sync(&ehci->watchdog); - del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; @@ -491,7 +444,6 @@ static void ehci_stop (struct usb_hcd *hcd) /* no more interrupts ... */ del_timer_sync (&ehci->watchdog); - del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); ehci->enabled_hrtimer_events = 0; @@ -547,10 +499,6 @@ static int ehci_init(struct usb_hcd *hcd) ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; - init_timer(&ehci->iaa_watchdog); - ehci->iaa_watchdog.function = ehci_iaa_watchdog; - ehci->iaa_watchdog.data = (unsigned long) ehci; - hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ehci->hrtimer.function = ehci_hrtimer_func; ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; @@ -830,6 +778,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { + + /* Turn off the IAA watchdog */ + ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_IAA_WATCHDOG); + + /* + * Mild optimization: Allow another IAAD to reset the + * hrtimer, if one occurs before the next expiration. + * In theory we could always cancel the hrtimer, but + * tests show that about half the time it will be reset + * for some other event anyway. + */ + if (ehci->next_hrtimer_event == EHCI_HRTIMER_IAA_WATCHDOG) + ++ehci->next_hrtimer_event; + /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6ad806bbe46..3097872b1e7 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -209,7 +209,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); del_timer_sync(&ehci->watchdog); - del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq (&ehci->lock); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index d68764ef447..7b35933d91a 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1173,8 +1173,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) struct ehci_qh *qh = ehci->async_unlink; struct ehci_qh *next; - iaa_watchdog_done(ehci); - // qh->hw_next = cpu_to_hc32(qh->qh_dma); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; @@ -1243,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command); (void)ehci_readl(ehci, &ehci->regs->command); - iaa_watchdog_start(ehci); + ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 8feb60ff422..aab04206422 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ + 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; @@ -291,6 +292,49 @@ static void end_free_itds(struct ehci_hcd *ehci) } +/* Handle lost (or very late) IAA interrupts */ +static void ehci_iaa_watchdog(struct ehci_hcd *ehci) +{ + if (ehci->rh_state != EHCI_RH_RUNNING) + return; + + /* + * Lost IAA irqs wedge things badly; seen first with a vt8235. + * So we need this watchdog, but must protect it against both + * (a) SMP races against real IAA firing and retriggering, and + * (b) clean HC shutdown, when IAA watchdog was pending. + */ + if (ehci->async_unlink) { + u32 cmd, status; + + /* If we get here, IAA is *REALLY* late. It's barely + * conceivable that the system is so busy that CMD_IAAD + * is still legitimately set, so let's be sure it's + * clear before we read STS_IAA. (The HC should clear + * CMD_IAAD when it sets STS_IAA.) + */ + cmd = ehci_readl(ehci, &ehci->regs->command); + + /* + * If IAA is set here it either legitimately triggered + * after the watchdog timer expired (_way_ late, so we'll + * still count it as lost) ... or a silicon erratum: + * - VIA seems to set IAA without triggering the IRQ; + * - IAAD potentially cleared without setting IAA. + */ + status = ehci_readl(ehci, &ehci->regs->status); + if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { + COUNT(ehci->stats.lost_iaa); + ehci_writel(ehci, STS_IAA, &ehci->regs->status); + } + + ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n", + status, cmd); + end_unlink_async(ehci); + } +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -302,6 +346,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ + ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 755e30b0000..d9414aae610 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -84,6 +84,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ @@ -168,7 +169,6 @@ struct ehci_hcd { /* one per controller */ struct dma_pool *itd_pool; /* itd per iso urb */ struct dma_pool *sitd_pool; /* sitd per split iso urb */ - struct timer_list iaa_watchdog; struct timer_list watchdog; unsigned long actions; unsigned periodic_stamp; @@ -228,20 +228,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) return container_of ((void *) ehci, struct usb_hcd, hcd_priv); } - -static inline void -iaa_watchdog_start(struct ehci_hcd *ehci) -{ - WARN_ON(timer_pending(&ehci->iaa_watchdog)); - mod_timer(&ehci->iaa_watchdog, - jiffies + msecs_to_jiffies(EHCI_IAA_MSECS)); -} - -static inline void iaa_watchdog_done(struct ehci_hcd *ehci) -{ - del_timer(&ehci->iaa_watchdog); -} - enum ehci_timer_action { TIMER_IO_WATCHDOG, TIMER_ASYNC_SHRINK, -- cgit v1.2.3-70-g09d2 From 3c273a056bf46167f0a1309c2ba72282a17d2541 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:49 -0400 Subject: USB: EHCI: unlink multiple async QHs together This patch (as1582) changes ehci-hcd's strategy for unlinking async QHs. Currently the driver never unlinks more than one QH at a time. This can be inefficient and cause unnecessary delays, since a QH cannot be reused while it is waiting to be unlinked. The new strategy unlinks all the waiting QHs at once. In practice the improvement won't be very big, because it's somewhat uncommon to have two or more QHs waiting to be unlinked at any time. But it does happen, and in any case, doing things this way makes more sense IMO. The change requires the async unlinking code to be refactored slightly. Now in addition to the routines for starting and ending an unlink, there are new routines for unlinking a single QH and starting an IAA cycle. This approach is needed because there are two separate paths for unlinking async QHs: When a transfer error occurs or an URB is cancelled, the QH must be unlinked right away; When a QH has been idle sufficiently long, it is unlinked to avoid consuming DMA bandwidth uselessly. In the first case we want the unlink to proceed as quickly as possible, whereas in the second case we can afford to batch several QHs together and unlink them all at once. Hence the division of labor. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 35 ++-------- drivers/usb/host/ehci-hub.c | 3 +- drivers/usb/host/ehci-q.c | 149 +++++++++++++++++++++++++----------------- drivers/usb/host/ehci-timer.c | 3 +- drivers/usb/host/ehci.h | 2 + 5 files changed, 98 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f74ba277c22..86e8ee169c6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -795,7 +795,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); - if (ehci->async_unlink) { + if (ehci->async_iaa) { COUNT(ehci->stats.iaa); end_unlink_async(ehci); } else @@ -926,33 +926,6 @@ static int ehci_urb_enqueue ( } } -static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - /* failfast */ - if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink) - end_unlink_async(ehci); - - /* If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; - return; - } - - /* defer till later if busy */ - if (ehci->async_unlink) { - qh->qh_state = QH_STATE_UNLINK_WAIT; - ehci->async_unlink_last->unlink_next = qh; - ehci->async_unlink_last = qh; - - /* start IAA cycle */ - } else - start_unlink_async (ehci, qh); -} - /* remove from hardware lists * completions normally happen asynchronously */ @@ -979,7 +952,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) switch (qh->qh_state) { case QH_STATE_LINKED: case QH_STATE_COMPLETING: - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); break; case QH_STATE_UNLINK: case QH_STATE_UNLINK_WAIT: @@ -1070,7 +1043,7 @@ rescan: * may already be unlinked. */ if (tmp) - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); /* FALL THROUGH */ case QH_STATE_UNLINK: /* wait for hw to finish? */ case QH_STATE_UNLINK_WAIT: @@ -1133,7 +1106,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) * re-linking will call qh_refresh(). */ if (eptype == USB_ENDPOINT_XFER_BULK) - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); else start_unlink_intr(ehci, qh); } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 3097872b1e7..a3822700e49 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -299,8 +299,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_halt (ehci); ehci->rh_state = EHCI_RH_SUSPENDED; - if (ehci->async_unlink) - end_unlink_async(ehci); + end_unlink_async(ehci); ehci_handle_intr_unlinks(ehci); end_free_itds(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 7b35933d91a..181832921c5 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -293,9 +293,6 @@ __acquires(ehci->lock) spin_lock (&ehci->lock); } -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); -static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); - static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* @@ -1166,82 +1163,114 @@ submit_async ( /*-------------------------------------------------------------------------*/ -/* the async qh for the qtds being unlinked are now gone from the HC */ - -static void end_unlink_async (struct ehci_hcd *ehci) +static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) { - struct ehci_qh *qh = ehci->async_unlink; - struct ehci_qh *next; + struct ehci_qh *prev; - // qh->hw_next = cpu_to_hc32(qh->qh_dma); - qh->qh_state = QH_STATE_IDLE; - qh->qh_next.qh = NULL; + /* Add to the end of the list of QHs waiting for the next IAAD */ + qh->qh_state = QH_STATE_UNLINK; + if (ehci->async_unlink) + ehci->async_unlink_last->unlink_next = qh; + else + ehci->async_unlink = qh; + ehci->async_unlink_last = qh; + + /* Unlink it from the schedule */ + prev = ehci->async; + while (prev->qh_next.qh != qh) + prev = prev->qh_next.qh; + + prev->hw->hw_next = qh->hw->hw_next; + prev->qh_next = qh->qh_next; + if (ehci->qh_scan_next == qh) + ehci->qh_scan_next = qh->qh_next.qh; +} - /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ - next = qh->unlink_next; - ehci->async_unlink = next; - qh->unlink_next = NULL; +static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) +{ + /* + * Do nothing if an IAA cycle is already running or + * if one will be started shortly. + */ + if (ehci->async_iaa || ehci->async_unlinking) + return; - qh_completions (ehci, qh); + /* Do all the waiting QHs at once */ + ehci->async_iaa = ehci->async_unlink; + ehci->async_unlink = NULL; - if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) - qh_link_async (ehci, qh); + /* If the controller isn't running, we don't have to wait for it */ + if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { + if (!nested) /* Avoid recursion */ + end_unlink_async(ehci); - disable_async(ehci); + /* Otherwise start a new IAA cycle */ + } else { + /* Make sure the unlinks are all visible to the hardware */ + wmb(); - if (next) { - ehci->async_unlink = NULL; - start_unlink_async (ehci, next); + ehci_writel(ehci, ehci->command | CMD_IAAD, + &ehci->regs->command); + ehci_readl(ehci, &ehci->regs->command); + ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); } +} + +/* the async qh for the qtds being unlinked are now gone from the HC */ + +static void end_unlink_async(struct ehci_hcd *ehci) +{ + struct ehci_qh *qh; if (ehci->has_synopsys_hc_bug) ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); + + /* Process the idle QHs */ + restart: + ehci->async_unlinking = true; + while (ehci->async_iaa) { + qh = ehci->async_iaa; + ehci->async_iaa = qh->unlink_next; + qh->unlink_next = NULL; + + qh->qh_state = QH_STATE_IDLE; + qh->qh_next.qh = NULL; + + qh_completions(ehci, qh); + if (!list_empty(&qh->qtd_list) && + ehci->rh_state == EHCI_RH_RUNNING) + qh_link_async(ehci, qh); + disable_async(ehci); + } + ehci->async_unlinking = false; + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (ehci->async_unlink) { + start_iaa_cycle(ehci, true); + if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) + goto restart; + } } /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) { - struct ehci_qh *prev; - -#ifdef DEBUG - assert_spin_locked(&ehci->lock); - if (ehci->async_unlink - || (qh->qh_state != QH_STATE_LINKED - && qh->qh_state != QH_STATE_UNLINK_WAIT) - ) - BUG (); -#endif - - qh->qh_state = QH_STATE_UNLINK; - ehci->async_unlink = qh; - if (!qh->unlink_next) - ehci->async_unlink_last = qh; - - prev = ehci->async; - while (prev->qh_next.qh != qh) - prev = prev->qh_next.qh; - - prev->hw->hw_next = qh->hw->hw_next; - prev->qh_next = qh->qh_next; - if (ehci->qh_scan_next == qh) - ehci->qh_scan_next = qh->qh_next.qh; - wmb (); - - /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { - /* if (unlikely (qh->unlink_next != 0)) - * this will recurse, probably not much - */ - end_unlink_async (ehci); + /* + * If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; return; } - ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command); - (void)ehci_readl(ehci, &ehci->regs->command); - ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); + single_unlink_async(ehci, qh); + start_iaa_cycle(ehci, false); } /*-------------------------------------------------------------------------*/ @@ -1269,11 +1298,11 @@ static void scan_async (struct ehci_hcd *ehci) * drops the lock. That's why ehci->qh_scan_next * always holds the next qh to scan; if the next qh * gets unlinked then ehci->qh_scan_next is adjusted - * in start_unlink_async(). + * in single_unlink_async(). */ temp = qh_completions(ehci, qh); if (qh->needs_rescan) - unlink_async(ehci, qh); + start_unlink_async(ehci, qh); qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; if (temp != 0) goto rescan; diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index aab04206422..8ca5f152f5b 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -211,6 +211,7 @@ static void ehci_handle_controller_death(struct ehci_hcd *ehci) ehci_writel(ehci, 0, &ehci->regs->configured_flag); ehci_writel(ehci, 0, &ehci->regs->intr_enable); ehci_work(ehci); + end_unlink_async(ehci); /* Not in process context, so don't try to reset the controller */ } @@ -304,7 +305,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) * (a) SMP races against real IAA firing and retriggering, and * (b) clean HC shutdown, when IAA watchdog was pending. */ - if (ehci->async_unlink) { + if (ehci->async_iaa) { u32 cmd, status; /* If we get here, IAA is *REALLY* late. It's barely diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index d9414aae610..1a782775881 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -114,12 +114,14 @@ struct ehci_hcd { /* one per controller */ /* general schedule support */ unsigned scanning:1; bool intr_unlinking:1; + bool async_unlinking:1; /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *async_unlink; struct ehci_qh *async_unlink_last; + struct ehci_qh *async_iaa; struct ehci_qh *qh_scan_next; unsigned async_count; /* async activity count */ -- cgit v1.2.3-70-g09d2 From 32830f207691176234b4c4dd17f0d7ab6d87d94b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:53 -0400 Subject: USB: EHCI: use hrtimer for unlinking empty async QHs This patch (as1583) changes ehci-hcd to use an hrtimer event for unlinking empty (unused) async QHs instead of using a kernel timer. The check for empty QHs is moved to a new routine, where it doesn't require going through an entire scan of both the async and periodic schedules. And it can unlink multiple QHs at once, unlike the current code. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 16 +-------- drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-q.c | 78 +++++++++++++++++++++++++++++-------------- drivers/usb/host/ehci-timer.c | 2 ++ drivers/usb/host/ehci.h | 4 +-- 5 files changed, 59 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 86e8ee169c6..74ffd20edff 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -94,8 +94,6 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ -#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) - /* 5-ms async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 @@ -130,15 +128,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); static void timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) { - /* Don't override timeouts which shrink or (later) disable - * the async ring; just the I/O watchdog. Note that if a - * SHRINK were pending, OFF would never be requested. - */ - if (timer_pending(&ehci->watchdog) - && (BIT(TIMER_ASYNC_SHRINK) - & ehci->actions)) - return; - if (!test_and_set_bit(action, &ehci->actions)) { unsigned long t; @@ -148,10 +137,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) return; t = EHCI_IO_JIFFIES; break; - /* case TIMER_ASYNC_SHRINK: */ - default: - t = EHCI_SHRINK_JIFFIES; - break; } mod_timer(&ehci->watchdog, t + jiffies); } @@ -307,6 +292,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ static void end_unlink_async(struct ehci_hcd *ehci); +static void unlink_empty_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index a3822700e49..5d84562e271 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -300,6 +300,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci->rh_state = EHCI_RH_SUSPENDED; end_unlink_async(ehci); + unlink_empty_async(ehci); ehci_handle_intr_unlinks(ehci); end_free_itds(ehci); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 181832921c5..bae93176782 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1205,7 +1205,7 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) end_unlink_async(ehci); /* Otherwise start a new IAA cycle */ - } else { + } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1253,6 +1253,39 @@ static void end_unlink_async(struct ehci_hcd *ehci) } } +static void unlink_empty_async(struct ehci_hcd *ehci) +{ + struct ehci_qh *qh, *next; + bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); + bool check_unlinks_later = false; + + /* Unlink all the async QHs that have been empty for a timer cycle */ + next = ehci->async->qh_next.qh; + while (next) { + qh = next; + next = qh->qh_next.qh; + + if (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED) { + if (!stopped && qh->unlink_cycle == + ehci->async_unlink_cycle) + check_unlinks_later = true; + else + single_unlink_async(ehci, qh); + } + } + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (ehci->async_unlink) + start_iaa_cycle(ehci, false); + + /* QHs that haven't been empty for long enough will be handled later */ + if (check_unlinks_later) { + ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); + ++ehci->async_unlink_cycle; + } +} + /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ @@ -1277,12 +1310,8 @@ static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) static void scan_async (struct ehci_hcd *ehci) { - bool stopped; struct ehci_qh *qh; - enum ehci_timer_action action = TIMER_IO_WATCHDOG; - - timer_action_done (ehci, TIMER_ASYNC_SHRINK); - stopped = (ehci->rh_state < EHCI_RH_RUNNING); + bool check_unlinks_later = false; ehci->qh_scan_next = ehci->async->qh_next.qh; while (ehci->qh_scan_next) { @@ -1301,28 +1330,27 @@ static void scan_async (struct ehci_hcd *ehci) * in single_unlink_async(). */ temp = qh_completions(ehci, qh); - if (qh->needs_rescan) + if (qh->needs_rescan) { start_unlink_async(ehci, qh); - qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; - if (temp != 0) + } else if (list_empty(&qh->qtd_list) + && qh->qh_state == QH_STATE_LINKED) { + qh->unlink_cycle = ehci->async_unlink_cycle; + check_unlinks_later = true; + } else if (temp != 0) goto rescan; } + } - /* unlink idle entries, reducing DMA usage as well - * as HCD schedule-scanning costs. delay for any qh - * we just scanned, there's a not-unusual case that it - * doesn't stay idle for long. - * (plus, avoids some kind of re-activation race.) - */ - if (list_empty(&qh->qtd_list) - && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->async_unlink && (stopped || - time_after_eq(jiffies, qh->unlink_time))) - start_unlink_async(ehci, qh); - else - action = TIMER_ASYNC_SHRINK; - } + /* + * Unlink empty entries, reducing DMA usage as well + * as HCD schedule-scanning costs. Delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + */ + if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING && + !(ehci->enabled_hrtimer_events & + BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) { + ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); + ++ehci->async_unlink_cycle; } - if (action == TIMER_ASYNC_SHRINK) - timer_action (ehci, TIMER_ASYNC_SHRINK); } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 8ca5f152f5b..a823290b513 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ + 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ @@ -347,6 +348,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ + unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */ ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 1a782775881..303c36cc99c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -84,6 +84,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ @@ -123,6 +124,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async_unlink_last; struct ehci_qh *async_iaa; struct ehci_qh *qh_scan_next; + unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ /* periodic schedule support */ @@ -232,7 +234,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) enum ehci_timer_action { TIMER_IO_WATCHDOG, - TIMER_ASYNC_SHRINK, }; static inline void @@ -382,7 +383,6 @@ struct ehci_qh { struct ehci_qtd *dummy; struct ehci_qh *unlink_next; /* next on unlink list */ - unsigned long unlink_time; unsigned unlink_cycle; unsigned stamp; -- cgit v1.2.3-70-g09d2 From 361aabf395e4a23cf554cf4ec0c0c6963b8beb01 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:22:57 -0400 Subject: USB: EHCI: don't lose events during a scan This patch (as1584) fixes a minor bug that has been present in ehci-hcd since the beginning. Scanning the schedules for URB completions is single-threaded. If a completion interrupt occurs while an URB is being given back, the interrupt handler realizes that a scan is in progress on another CPU and avoids starting a new one. This means that completion events can be lost. If an URB completes after it has been scanned but while a scan is still in progress, the driver won't notice and won't rescan the completed URB. The patch fixes the problem by adding a new flag to indicate that another scan is needed after the current scan is done. The flag gets set whenever a completion interrupt occurs while a scan is in progress. The rescan will see the completion, thus preventing it from getting lost. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 13 ++++++++++--- drivers/usb/host/ehci.h | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 74ffd20edff..f6cf1d17810 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -400,14 +400,21 @@ static void ehci_work (struct ehci_hcd *ehci) * it reports urb completions. this flag guards against bogus * attempts at re-entrant schedule scanning. */ - if (ehci->scanning) + if (ehci->scanning) { + ehci->need_rescan = true; return; - ehci->scanning = 1; + } + ehci->scanning = true; + + rescan: + ehci->need_rescan = false; if (ehci->async_count) scan_async(ehci); if (ehci->next_uframe != -1) scan_periodic (ehci); - ehci->scanning = 0; + if (ehci->need_rescan) + goto rescan; + ehci->scanning = false; /* the IO watchdog guards against hardware or driver bugs that * misplace IRQs, and should let us run completely without IRQs. diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 303c36cc99c..c462d52ac57 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -113,7 +113,8 @@ struct ehci_hcd { /* one per controller */ enum ehci_rh_state rh_state; /* general schedule support */ - unsigned scanning:1; + bool scanning:1; + bool need_rescan:1; bool intr_unlinking:1; bool async_unlinking:1; -- cgit v1.2.3-70-g09d2 From 569b394f53f0abd177cc665c9b4ace89e3f4c7fb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:00 -0400 Subject: USB: EHCI: always scan each interrupt QH This patch (as1585) fixes a bug in ehci-hcd's scheme for scanning interrupt QHs. Currently a single routine takes care of scanning everything on the periodic schedule. Whenever an interrupt occurs, it scans all isochronous and interrupt URBs scheduled for frames that have elapsed since the last scan. This has two disadvantages. The first is relatively minor: An interrupt QH is likely to end up getting scanned multiple times, particularly if the last scan was not fairly recent. (The current code avoids this by maintaining a periodic_stamp in each interrupt QH.) The second is more serious. The periodic schedule wraps around. If the last scan occurred during frame N, and the next scan occurs when the schedule has gone through an entire cycle and is back at frame N, the scanning code won't look at any frames other than N. Consequently it won't see any QHs that completed during frame N-1 or earlier. The patch replaces the entire frame-based approach for scanning interrupt QHs with a new routine using a list-based approach, the same as for async QHs. This has a slight disadvantage, because it means that all interrupt QHs have to be scanned every time. But it is more robust than the current approach. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 7 +++- drivers/usb/host/ehci-q.c | 3 +- drivers/usb/host/ehci-sched.c | 96 ++++++++++++++++++++++++++----------------- drivers/usb/host/ehci-timer.c | 7 +--- drivers/usb/host/ehci.h | 8 ++-- 5 files changed, 71 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f6cf1d17810..c13dad8a850 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -410,8 +410,10 @@ static void ehci_work (struct ehci_hcd *ehci) ehci->need_rescan = false; if (ehci->async_count) scan_async(ehci); - if (ehci->next_uframe != -1) - scan_periodic (ehci); + if (ehci->intr_count > 0) + scan_intr(ehci); + if (ehci->isoc_count > 0) + scan_isoc(ehci); if (ehci->need_rescan) goto rescan; ehci->scanning = false; @@ -509,6 +511,7 @@ static int ehci_init(struct usb_hcd *hcd) * periodic_size can shrink by USBCMD update if hcc_params allows. */ ehci->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->cached_itd_list); INIT_LIST_HEAD(&ehci->cached_sitd_list); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index bae93176782..c9c7f7b3b7d 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -322,7 +322,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * * It's a bug for qh->qh_state to be anything other than * QH_STATE_IDLE, unless our caller is scan_async() or - * scan_periodic(). + * scan_intr(). */ state = qh->qh_state; qh->qh_state = QH_STATE_COMPLETING; @@ -832,7 +832,6 @@ qh_make ( is_input, 0, hb_mult(maxp) * max_packet(maxp))); qh->start = NO_FRAME; - qh->stamp = ehci->periodic_stamp; if (urb->dev->speed == USB_SPEED_HIGH) { qh->c_usecs = 0; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 11b2f21d7ac..263b542985c 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -569,7 +569,10 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) ? ((qh->usecs + qh->c_usecs) / qh->period) : (qh->usecs * 8); + list_add(&qh->intr_node, &ehci->intr_qh_list); + /* maybe enable periodic schedule processing */ + ++ehci->intr_count; enable_periodic(ehci); } @@ -614,6 +617,11 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = NULL; + + if (ehci->qh_scan_next == qh) + ehci->qh_scan_next = list_entry(qh->intr_node.next, + struct ehci_qh, intr_node); + list_del(&qh->intr_node); } static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -683,6 +691,7 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) } /* maybe turn off periodic schedule */ + --ehci->intr_count; disable_periodic(ehci); } @@ -920,6 +929,35 @@ done_not_linked: return status; } +static void scan_intr(struct ehci_hcd *ehci) +{ + struct ehci_qh *qh; + + list_for_each_entry_safe(qh, ehci->qh_scan_next, &ehci->intr_qh_list, + intr_node) { + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why ehci->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then ehci->qh_scan_next is adjusted + * in qh_unlink_periodic(). + */ + temp = qh_completions(ehci, qh); + if (unlikely(qh->needs_rescan || + (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED))) + start_unlink_intr(ehci, qh); + else if (temp != 0) + goto rescan; + } + } +} + /*-------------------------------------------------------------------------*/ /* ehci_iso_stream ops work with both ITD and SITD */ @@ -1450,6 +1488,10 @@ iso_stream_schedule ( urb->start_frame = stream->next_uframe; if (!stream->highspeed) urb->start_frame >>= 3; + + /* Make sure scan_isoc() sees these */ + if (ehci->isoc_count == 0) + ehci->next_uframe = now; return 0; fail: @@ -1608,6 +1650,7 @@ static void itd_link_urb( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); + ++ehci->isoc_count; enable_periodic(ehci); } @@ -1688,9 +1731,11 @@ itd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; + + --ehci->isoc_count; disable_periodic(ehci); - ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci->amd_pll_fix == 1) usb_amd_quirk_pll_enable(); @@ -2008,6 +2053,7 @@ static void sitd_link_urb( urb->hcpriv = NULL; timer_action (ehci, TIMER_IO_WATCHDOG); + ++ehci->isoc_count; enable_periodic(ehci); } @@ -2074,9 +2120,11 @@ sitd_complete ( ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; + + --ehci->isoc_count; disable_periodic(ehci); - ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci->amd_pll_fix == 1) usb_amd_quirk_pll_enable(); @@ -2165,8 +2213,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, /*-------------------------------------------------------------------------*/ -static void -scan_periodic (struct ehci_hcd *ehci) +static void scan_isoc(struct ehci_hcd *ehci) { unsigned now_uframe, frame, clock, clock_frame, mod; unsigned modified; @@ -2189,7 +2236,6 @@ scan_periodic (struct ehci_hcd *ehci) ehci->clock_frame = clock_frame; clock &= mod - 1; clock_frame = clock >> 3; - ++ehci->periodic_stamp; for (;;) { union ehci_shadow q, *q_p; @@ -2208,36 +2254,10 @@ restart: while (q.ptr != NULL) { unsigned uf; - union ehci_shadow temp; int live; live = (ehci->rh_state >= EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { - case Q_TYPE_QH: - /* handle any completions */ - temp.qh = q.qh; - type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); - q = q.qh->qh_next; - if (temp.qh->stamp != ehci->periodic_stamp) { - modified = qh_completions(ehci, temp.qh); - if (!modified) - temp.qh->stamp = ehci->periodic_stamp; - if (unlikely(list_empty(&temp.qh->qtd_list) || - temp.qh->needs_rescan)) - start_unlink_intr(ehci, temp.qh); - } - break; - case Q_TYPE_FSTN: - /* for "save place" FSTNs, look at QH entries - * in the previous frame for completions. - */ - if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) { - ehci_dbg(ehci, - "ignoring completions from FSTNs\n"); - } - type = Q_NEXT_TYPE(ehci, q.fstn->hw_next); - q = q.fstn->fstn_next; - break; case Q_TYPE_ITD: /* If this ITD is still active, leave it for * later processing ... check the next entry. @@ -2319,12 +2339,17 @@ restart: ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n", type, frame, q.ptr); // BUG (); + /* FALL THROUGH */ + case Q_TYPE_QH: + case Q_TYPE_FSTN: + /* End of the iTDs and siTDs */ q.ptr = NULL; + break; } /* assume completion callbacks modify the queue */ if (unlikely (modified)) { - if (likely(ehci->periodic_count > 0)) + if (likely(ehci->isoc_count > 0)) goto restart; /* short-circuit this scan */ now_uframe = clock; @@ -2353,7 +2378,7 @@ restart: unsigned now; if (ehci->rh_state < EHCI_RH_RUNNING - || ehci->periodic_count == 0) + || ehci->isoc_count == 0) break; ehci->next_uframe = now_uframe; now = ehci_read_frame_index(ehci) & (mod - 1); @@ -2363,10 +2388,7 @@ restart: /* rescan the rest of this frame, then ... */ clock = now; clock_frame = clock >> 3; - if (ehci->clock_frame != clock_frame) { - ehci->clock_frame = clock_frame; - ++ehci->periodic_stamp; - } + ehci->clock_frame = clock_frame; } else { now_uframe++; now_uframe &= mod - 1; diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index a823290b513..0e28bae78d1 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -168,13 +168,8 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) /* The status is up-to-date; restart or stop the schedule as needed */ if (want == 0) { /* Stopped */ - if (ehci->periodic_count > 0) { - - /* make sure ehci_work scans these */ - ehci->next_uframe = ehci_read_frame_index(ehci) - & ((ehci->periodic_size << 3) - 1); + if (ehci->periodic_count > 0) ehci_set_command_bit(ehci, CMD_PSE); - } } else { /* Running */ if (ehci->periodic_count == 0) { diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index c462d52ac57..08637183aad 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -117,6 +117,7 @@ struct ehci_hcd { /* one per controller */ bool need_rescan:1; bool intr_unlinking:1; bool async_unlinking:1; + struct ehci_qh *qh_scan_next; /* async schedule support */ struct ehci_qh *async; @@ -124,7 +125,6 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async_unlink; struct ehci_qh *async_unlink_last; struct ehci_qh *async_iaa; - struct ehci_qh *qh_scan_next; unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ @@ -133,6 +133,7 @@ struct ehci_hcd { /* one per controller */ unsigned periodic_size; __hc32 *periodic; /* hw periodic table */ dma_addr_t periodic_dma; + struct list_head intr_qh_list; unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ @@ -140,6 +141,8 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *intr_unlink_last; unsigned intr_unlink_cycle; int next_uframe; /* scan periodic, start here */ + unsigned intr_count; /* intr activity count */ + unsigned isoc_count; /* isoc activity count */ unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ @@ -176,7 +179,6 @@ struct ehci_hcd { /* one per controller */ struct timer_list watchdog; unsigned long actions; - unsigned periodic_stamp; unsigned random_frame; unsigned long next_statechange; ktime_t last_periodic_enable; @@ -381,11 +383,11 @@ struct ehci_qh { dma_addr_t qh_dma; /* address of qh */ union ehci_shadow qh_next; /* ptr to qh; or periodic */ struct list_head qtd_list; /* sw qtd list */ + struct list_head intr_node; /* list of intr QHs */ struct ehci_qtd *dummy; struct ehci_qh *unlink_next; /* next on unlink list */ unsigned unlink_cycle; - unsigned stamp; u8 needs_rescan; /* Dequeue during giveback */ u8 qh_state; -- cgit v1.2.3-70-g09d2 From 18aafe64d75d0e27dae206cacf4171e4e485d285 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:04 -0400 Subject: USB: EHCI: use hrtimer for the I/O watchdog This patch (as1586) replaces the kernel timer used by ehci-hcd as an I/O watchdog with an hrtimer event. Unlike in the current code, the watchdog event is now always enabled whenever any isochronous URBs are active. This will prevent bugs caused by the periodic schedule wrapping around with no completion interrupts; the watchdog handler is guaranteed to scan the isochronous transfers at least once during each iteration of the schedule. The extra overhead will be negligible: one timer interrupt every 100 ms. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 47 +------------------------------------------ drivers/usb/host/ehci-hub.c | 5 ----- drivers/usb/host/ehci-q.c | 1 + drivers/usb/host/ehci-sched.c | 3 +-- drivers/usb/host/ehci-timer.c | 21 +++++++++++++++++++ drivers/usb/host/ehci.h | 13 +----------- 6 files changed, 25 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c13dad8a850..9f26080889f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -93,8 +93,6 @@ static const char hcd_name [] = "ehci_hcd"; */ #define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ -#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ - /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 module_param (log2_irq_thresh, int, S_IRUGO); @@ -125,25 +123,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); /*-------------------------------------------------------------------------*/ -static void -timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) -{ - if (!test_and_set_bit(action, &ehci->actions)) { - unsigned long t; - - switch (action) { - case TIMER_IO_WATCHDOG: - if (!ehci->need_io_watchdog) - return; - t = EHCI_IO_JIFFIES; - break; - } - mod_timer(&ehci->watchdog, t + jiffies); - } -} - -/*-------------------------------------------------------------------------*/ - /* * handshake - spin reading hc until handshake completes or fails * @ptr: address of hc register to be read @@ -307,19 +286,6 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); /*-------------------------------------------------------------------------*/ -static void ehci_watchdog(unsigned long param) -{ - struct ehci_hcd *ehci = (struct ehci_hcd *) param; - unsigned long flags; - - spin_lock_irqsave(&ehci->lock, flags); - - /* ehci could run by timer, without IRQs ... */ - ehci_work (ehci); - - spin_unlock_irqrestore (&ehci->lock, flags); -} - /* On some systems, leaving remote wakeup enabled prevents system shutdown. * The firmware seems to think that powering off is a wakeup event! * This routine turns off remote wakeup and everything else, on all ports. @@ -357,8 +323,6 @@ static void ehci_shutdown(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - del_timer_sync(&ehci->watchdog); - spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; ehci_silence_controller(ehci); @@ -394,8 +358,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on) */ static void ehci_work (struct ehci_hcd *ehci) { - timer_action_done (ehci, TIMER_IO_WATCHDOG); - /* another CPU may drop ehci->lock during a schedule scan while * it reports urb completions. this flag guards against bogus * attempts at re-entrant schedule scanning. @@ -422,10 +384,7 @@ static void ehci_work (struct ehci_hcd *ehci) * misplace IRQs, and should let us run completely without IRQs. * such lossage has been observed on both VT6202 and VT8235. */ - if (ehci->rh_state == EHCI_RH_RUNNING && - (ehci->async->qh_next.ptr != NULL || - ehci->periodic_count != 0)) - timer_action (ehci, TIMER_IO_WATCHDOG); + turn_on_io_watchdog(ehci); } /* @@ -438,7 +397,6 @@ static void ehci_stop (struct usb_hcd *hcd) ehci_dbg (ehci, "stop\n"); /* no more interrupts ... */ - del_timer_sync (&ehci->watchdog); spin_lock_irq(&ehci->lock); ehci->enabled_hrtimer_events = 0; @@ -490,9 +448,6 @@ static int ehci_init(struct usb_hcd *hcd) * keep io watchdog by default, those good HCDs could turn off it later */ ehci->need_io_watchdog = 1; - init_timer(&ehci->watchdog); - ehci->watchdog.function = ehci_watchdog; - ehci->watchdog.data = (unsigned long) ehci; hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ehci->hrtimer.function = ehci_hrtimer_func; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 5d84562e271..05490d387fd 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -208,7 +208,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); - del_timer_sync(&ehci->watchdog); spin_lock_irq (&ehci->lock); @@ -316,10 +315,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; spin_unlock_irq (&ehci->lock); - /* ehci_work() may have re-enabled the watchdog timer, which we do not - * want, and so we must delete any pending watchdog timer events. - */ - del_timer_sync(&ehci->watchdog); hrtimer_cancel(&ehci->hrtimer); return 0; } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index c9c7f7b3b7d..9bc39ca460c 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -970,6 +970,7 @@ static void enable_async(struct ehci_hcd *ehci) /* Don't start the schedule until ASS is 0 */ ehci_poll_ASS(ehci); + turn_on_io_watchdog(ehci); } static void disable_async(struct ehci_hcd *ehci) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 263b542985c..26ce8fef0e5 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -489,6 +489,7 @@ static void enable_periodic(struct ehci_hcd *ehci) /* Don't start the schedule until PSS is 0 */ ehci_poll_PSS(ehci); + turn_on_io_watchdog(ehci); } static void disable_periodic(struct ehci_hcd *ehci) @@ -1649,7 +1650,6 @@ static void itd_link_urb( iso_sched_free (stream, iso_sched); urb->hcpriv = NULL; - timer_action (ehci, TIMER_IO_WATCHDOG); ++ehci->isoc_count; enable_periodic(ehci); } @@ -2052,7 +2052,6 @@ static void sitd_link_urb( iso_sched_free (stream, sched); urb->hcpriv = NULL; - timer_action (ehci, TIMER_IO_WATCHDOG); ++ehci->isoc_count; enable_periodic(ehci); } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 0e28bae78d1..eb896a2c8f2 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -76,6 +76,7 @@ static unsigned event_delays_ns[] = { 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ + 100 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IO_WATCHDOG */ }; /* Enable a pending hrtimer event */ @@ -332,6 +333,25 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) } +/* Enable the I/O watchdog, if appropriate */ +static void turn_on_io_watchdog(struct ehci_hcd *ehci) +{ + /* Not needed if the controller isn't running or it's already enabled */ + if (ehci->rh_state != EHCI_RH_RUNNING || + (ehci->enabled_hrtimer_events & + BIT(EHCI_HRTIMER_IO_WATCHDOG))) + return; + + /* + * Isochronous transfers always need the watchdog. + * For other sorts we use it only if the flag is set. + */ + if (ehci->isoc_count > 0 || (ehci->need_io_watchdog && + ehci->async_count + ehci->intr_count > 0)) + ehci_enable_event(ehci, EHCI_HRTIMER_IO_WATCHDOG, true); +} + + /* * Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by @@ -347,6 +367,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ + ehci_work, /* EHCI_HRTIMER_IO_WATCHDOG */ }; static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 08637183aad..254f414bd0b 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -88,6 +88,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ + EHCI_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */ }; #define EHCI_HRTIMER_NO_EVENT 99 @@ -177,8 +178,6 @@ struct ehci_hcd { /* one per controller */ struct dma_pool *itd_pool; /* itd per iso urb */ struct dma_pool *sitd_pool; /* sitd per split iso urb */ - struct timer_list watchdog; - unsigned long actions; unsigned random_frame; unsigned long next_statechange; ktime_t last_periodic_enable; @@ -235,16 +234,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) return container_of ((void *) ehci, struct usb_hcd, hcd_priv); } -enum ehci_timer_action { - TIMER_IO_WATCHDOG, -}; - -static inline void -timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) -{ - clear_bit (action, &ehci->actions); -} - /*-------------------------------------------------------------------------*/ #include -- cgit v1.2.3-70-g09d2 From f42890782241a60d107f23d08089a4a12b507a11 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:07 -0400 Subject: USB: EHCI: simplify isochronous scanning This patch (as1587) simplifies ehci-hcd's scan_isoc() routine by eliminating some local variables, declaring boolean-valued values as bool rather than unsigned, changing variable names to make more sense, and so on. The logic at the end of the routine is cut down significantly. The scanning doesn't have to catch up all the way to where the hardware is; it merely has to catch up to where the hardware was when the last interrupt occurred. If the hardware has made more progress since then and issued another interrupt, a rescan will catch up to it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 3 -- drivers/usb/host/ehci-sched.c | 114 +++++++++++------------------------------- drivers/usb/host/ehci.h | 6 +-- 3 files changed, 32 insertions(+), 91 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9f26080889f..340c9c4894b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -488,9 +488,6 @@ static int ehci_init(struct usb_hcd *hcd) else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); - ehci->next_uframe = -1; - ehci->clock_frame = -1; - /* * dedicate a qh for the async ring head, since we couldn't unlink * a 'real' qh without stopping the async schedule [4.8]. use it diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 26ce8fef0e5..7cf3da7babf 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -497,8 +497,6 @@ static void disable_periodic(struct ehci_hcd *ehci) if (--ehci->periodic_count) return; - ehci->next_uframe = -1; /* the periodic schedule is empty */ - /* Don't turn off the schedule until PSS is 1 */ ehci_poll_PSS(ehci); } @@ -1220,7 +1218,7 @@ itd_urb_transaction ( if (likely(!list_empty(&stream->free_list))) { itd = list_first_entry(&stream->free_list, struct ehci_itd, itd_list); - if (itd->frame == ehci->clock_frame) + if (itd->frame == ehci->now_frame) goto alloc_itd; list_del (&itd->itd_list); itd_dma = itd->itd_dma; @@ -1492,7 +1490,7 @@ iso_stream_schedule ( /* Make sure scan_isoc() sees these */ if (ehci->isoc_count == 0) - ehci->next_uframe = now; + ehci->next_frame = now >> 3; return 0; fail: @@ -1666,11 +1664,8 @@ static void itd_link_urb( * (b) only this endpoint's completions submit URBs. It seems some silicon * corrupts things if you reuse completed descriptors very quickly... */ -static unsigned -itd_complete ( - struct ehci_hcd *ehci, - struct ehci_itd *itd -) { +static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) +{ struct urb *urb = itd->urb; struct usb_iso_packet_descriptor *desc; u32 t; @@ -1678,7 +1673,7 @@ itd_complete ( int urb_index = -1; struct ehci_iso_stream *stream = itd->stream; struct usb_device *dev; - unsigned retval = false; + bool retval = false; /* for each uframe with a packet */ for (uframe = 0; uframe < 8; uframe++) { @@ -1917,7 +1912,7 @@ sitd_urb_transaction ( if (likely(!list_empty(&stream->free_list))) { sitd = list_first_entry(&stream->free_list, struct ehci_sitd, sitd_list); - if (sitd->frame == ehci->clock_frame) + if (sitd->frame == ehci->now_frame) goto alloc_sitd; list_del (&sitd->sitd_list); sitd_dma = sitd->sitd_dma; @@ -2071,18 +2066,15 @@ static void sitd_link_urb( * (b) only this endpoint's completions submit URBs. It seems some silicon * corrupts things if you reuse completed descriptors very quickly... */ -static unsigned -sitd_complete ( - struct ehci_hcd *ehci, - struct ehci_sitd *sitd -) { +static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd) +{ struct urb *urb = sitd->urb; struct usb_iso_packet_descriptor *desc; u32 t; int urb_index = -1; struct ehci_iso_stream *stream = sitd->stream; struct usb_device *dev; - unsigned retval = false; + bool retval = false; urb_index = sitd->index; desc = &urb->iso_frame_desc [urb_index]; @@ -2214,34 +2206,29 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, static void scan_isoc(struct ehci_hcd *ehci) { - unsigned now_uframe, frame, clock, clock_frame, mod; - unsigned modified; - - mod = ehci->periodic_size << 3; + unsigned uf, now_frame, frame; + unsigned fmask = ehci->periodic_size - 1; + bool modified, live; /* * When running, scan from last scan point up to "now" * else clean up by scanning everything that's left. * Touches as few pages as possible: cache-friendly. */ - now_uframe = ehci->next_uframe; if (ehci->rh_state >= EHCI_RH_RUNNING) { - clock = ehci_read_frame_index(ehci); - clock_frame = (clock >> 3) & (ehci->periodic_size - 1); + uf = ehci_read_frame_index(ehci); + now_frame = (uf >> 3) & fmask; + live = true; } else { - clock = now_uframe + mod - 1; - clock_frame = -1; + now_frame = (ehci->next_frame - 1) & fmask; + live = false; } - ehci->clock_frame = clock_frame; - clock &= mod - 1; - clock_frame = clock >> 3; + ehci->now_frame = now_frame; + frame = ehci->next_frame; for (;;) { union ehci_shadow q, *q_p; __hc32 type, *hw_p; - unsigned incomplete = false; - - frame = now_uframe >> 3; restart: /* scan each element in frame's queue for completions */ @@ -2249,13 +2236,9 @@ restart: hw_p = &ehci->periodic [frame]; q.ptr = q_p->ptr; type = Q_NEXT_TYPE(ehci, *hw_p); - modified = 0; + modified = false; while (q.ptr != NULL) { - unsigned uf; - int live; - - live = (ehci->rh_state >= EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_ITD: /* If this ITD is still active, leave it for @@ -2263,7 +2246,7 @@ restart: * No need to check for activity unless the * frame is current. */ - if (frame == clock_frame && live) { + if (frame == now_frame && live) { rmb(); for (uf = 0; uf < 8; uf++) { if (q.itd->hw_transaction[uf] & @@ -2271,7 +2254,6 @@ restart: break; } if (uf < 8) { - incomplete = true; q_p = &q.itd->itd_next; hw_p = &q.itd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2303,14 +2285,12 @@ restart: * No need to check for activity unless the * frame is current. */ - if (((frame == clock_frame) || - (((frame + 1) & (ehci->periodic_size - 1)) - == clock_frame)) + if (((frame == now_frame) || + (((frame + 1) & fmask) == now_frame)) && live && (q.sitd->hw_results & SITD_ACTIVE(ehci))) { - incomplete = true; q_p = &q.sitd->sitd_next; hw_p = &q.sitd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2347,50 +2327,14 @@ restart: } /* assume completion callbacks modify the queue */ - if (unlikely (modified)) { - if (likely(ehci->isoc_count > 0)) - goto restart; - /* short-circuit this scan */ - now_uframe = clock; - break; - } + if (unlikely(modified && ehci->isoc_count > 0)) + goto restart; } - /* If we can tell we caught up to the hardware, stop now. - * We can't advance our scan without collecting the ISO - * transfers that are still pending in this frame. - */ - if (incomplete && ehci->rh_state >= EHCI_RH_RUNNING) { - ehci->next_uframe = now_uframe; + /* Stop when we have reached the current frame */ + if (frame == now_frame) break; - } - - // FIXME: this assumes we won't get lapped when - // latencies climb; that should be rare, but... - // detect it, and just go all the way around. - // FLR might help detect this case, so long as latencies - // don't exceed periodic_size msec (default 1.024 sec). - - // FIXME: likewise assumes HC doesn't halt mid-scan - - if (now_uframe == clock) { - unsigned now; - - if (ehci->rh_state < EHCI_RH_RUNNING - || ehci->isoc_count == 0) - break; - ehci->next_uframe = now_uframe; - now = ehci_read_frame_index(ehci) & (mod - 1); - if (now_uframe == now) - break; - - /* rescan the rest of this frame, then ... */ - clock = now; - clock_frame = clock >> 3; - ehci->clock_frame = clock_frame; - } else { - now_uframe++; - now_uframe &= mod - 1; - } + frame = (frame + 1) & fmask; } + ehci->next_frame = now_frame; } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 254f414bd0b..7de58fe52d5 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -141,19 +141,19 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *intr_unlink; struct ehci_qh *intr_unlink_last; unsigned intr_unlink_cycle; - int next_uframe; /* scan periodic, start here */ + unsigned now_frame; /* frame from HC hardware */ + unsigned next_frame; /* scan periodic, start here */ unsigned intr_count; /* intr activity count */ unsigned isoc_count; /* isoc activity count */ unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ - /* list of itds & sitds completed while clock_frame was still active */ + /* list of itds & sitds completed while now_frame was still active */ struct list_head cached_itd_list; struct ehci_itd *last_itd_to_free; struct list_head cached_sitd_list; struct ehci_sitd *last_sitd_to_free; - unsigned clock_frame; /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; -- cgit v1.2.3-70-g09d2 From c4f3476436f7452b97c8accb5dd7d53219a11a3f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:10 -0400 Subject: USB: EHCI: fix up locking This patch (as1588) adjusts the locking in ehci-hcd's various halt, shutdown, and suspend/resume pathways. We want to hold the spinlock while writing device registers and accessing shared variables, but not while polling in a loop. In addition, there's no need to call ehci_work() at times when no URBs can be active, i.e., in ehci_stop() and ehci_bus_suspend(). Finally, ehci_adjust_port_wakeup_flags() is called only in situations where interrupts are enabled; therefore it can use spin_lock_irq rather than spin_lock_irqsave. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 48 +++++++++++++++++++++++++++++-------------- drivers/usb/host/ehci-hub.c | 41 +++++++++++++++++++++++++----------- drivers/usb/host/ehci-tegra.c | 5 ++--- 3 files changed, 64 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 340c9c4894b..ac4c8ddde20 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -167,21 +167,24 @@ static int tdi_in_host_mode (struct ehci_hcd *ehci) return (tmp & 3) == USBMODE_CM_HC; } -/* force HC to halt state from unknown (EHCI spec section 2.3) */ +/* + * Force HC to halt state from unknown (EHCI spec section 2.3). + * Must be called with interrupts enabled and the lock not held. + */ static int ehci_halt (struct ehci_hcd *ehci) { - u32 temp = ehci_readl(ehci, &ehci->regs->status); + u32 temp; + + spin_lock_irq(&ehci->lock); /* disable any irqs left enabled by previous code */ ehci_writel(ehci, 0, &ehci->regs->intr_enable); - if (ehci_is_TDI(ehci) && tdi_in_host_mode(ehci) == 0) { + if (ehci_is_TDI(ehci) && !tdi_in_host_mode(ehci)) { + spin_unlock_irq(&ehci->lock); return 0; } - if ((temp & STS_HALT) != 0) - return 0; - /* * This routine gets called during probe before ehci->command * has been initialized, so we can't rely on its value. @@ -190,7 +193,11 @@ static int ehci_halt (struct ehci_hcd *ehci) temp = ehci_readl(ehci, &ehci->regs->command); temp &= ~(CMD_RUN | CMD_IAAD); ehci_writel(ehci, temp, &ehci->regs->command); - return handshake (ehci, &ehci->regs->status, + + spin_unlock_irq(&ehci->lock); + synchronize_irq(ehci_to_hcd(ehci)->irq); + + return handshake(ehci, &ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); } @@ -210,7 +217,10 @@ static void tdi_reset (struct ehci_hcd *ehci) ehci_writel(ehci, tmp, &ehci->regs->usbmode); } -/* reset a non-running (STS_HALT == 1) controller */ +/* + * Reset a non-running (STS_HALT == 1) controller. + * Must be called with interrupts enabled and the lock not held. + */ static int ehci_reset (struct ehci_hcd *ehci) { int retval; @@ -248,7 +258,10 @@ static int ehci_reset (struct ehci_hcd *ehci) return retval; } -/* idle the controller (from running) */ +/* + * Idle the controller (turn off the schedules). + * Must be called with interrupts enabled and the lock not held. + */ static void ehci_quiesce (struct ehci_hcd *ehci) { u32 temp; @@ -261,8 +274,10 @@ static void ehci_quiesce (struct ehci_hcd *ehci) handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); /* then disable anything that's still active */ + spin_lock_irq(&ehci->lock); ehci->command &= ~(CMD_ASE | CMD_PSE); ehci_writel(ehci, ehci->command, &ehci->regs->command); + spin_unlock_irq(&ehci->lock); /* hardware can take 16 microframes to turn off ... */ handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); @@ -301,11 +316,14 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) /* * Halt HC, turn off all ports, and let the BIOS use the companion controllers. - * Should be called with ehci->lock held. + * Must be called with interrupts enabled and the lock not held. */ static void ehci_silence_controller(struct ehci_hcd *ehci) { ehci_halt(ehci); + + spin_lock_irq(&ehci->lock); + ehci->rh_state = EHCI_RH_HALTED; ehci_turn_off_all_ports(ehci); /* make BIOS/etc use companion controller during reboot */ @@ -313,6 +331,7 @@ static void ehci_silence_controller(struct ehci_hcd *ehci) /* unblock posted writes */ ehci_readl(ehci, &ehci->regs->configured_flag); + spin_unlock_irq(&ehci->lock); } /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). @@ -325,10 +344,11 @@ static void ehci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_STOPPING; - ehci_silence_controller(ehci); ehci->enabled_hrtimer_events = 0; spin_unlock_irq(&ehci->lock); + ehci_silence_controller(ehci); + hrtimer_cancel(&ehci->hrtimer); } @@ -400,11 +420,11 @@ static void ehci_stop (struct usb_hcd *hcd) spin_lock_irq(&ehci->lock); ehci->enabled_hrtimer_events = 0; - ehci_quiesce(ehci); + spin_unlock_irq(&ehci->lock); + ehci_quiesce(ehci); ehci_silence_controller(ehci); ehci_reset (ehci); - spin_unlock_irq(&ehci->lock); hrtimer_cancel(&ehci->hrtimer); remove_sysfs_files(ehci); @@ -412,8 +432,6 @@ static void ehci_stop (struct usb_hcd *hcd) /* root hub is shut down separately (first, when possible) */ spin_lock_irq (&ehci->lock); - if (ehci->async) - ehci_work (ehci); end_free_itds(ehci); spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 05490d387fd..ffc5f27df72 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -59,6 +59,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) /* Give the connections some time to appear */ msleep(20); + spin_lock_irq(&ehci->lock); port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) { @@ -70,23 +71,30 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) clear_bit(port, &ehci->owned_ports); else if (test_bit(port, &ehci->companion_ports)) ehci_writel(ehci, status & ~PORT_PE, reg); - else + else { + spin_unlock_irq(&ehci->lock); ehci_hub_control(hcd, SetPortFeature, USB_PORT_FEAT_RESET, port + 1, NULL, 0); + spin_lock_irq(&ehci->lock); + } } } + spin_unlock_irq(&ehci->lock); if (!ehci->owned_ports) return; msleep(90); /* Wait for resets to complete */ + spin_lock_irq(&ehci->lock); port = HCS_N_PORTS(ehci->hcs_params); while (port--) { if (test_bit(port, &ehci->owned_ports)) { + spin_unlock_irq(&ehci->lock); ehci_hub_control(hcd, GetPortStatus, 0, port + 1, (char *) &buf, sizeof(buf)); + spin_lock_irq(&ehci->lock); /* The companion should now own the port, * but if something went wrong the port must not @@ -105,6 +113,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) } ehci->owned_ports = 0; + spin_unlock_irq(&ehci->lock); } static int ehci_port_change(struct ehci_hcd *ehci) @@ -133,7 +142,6 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, { int port; u32 temp; - unsigned long flags; /* If remote wakeup is enabled for the root hub but disabled * for the controller, we must adjust all the port wakeup flags @@ -143,7 +151,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup) return; - spin_lock_irqsave(&ehci->lock, flags); + spin_lock_irq(&ehci->lock); /* clear phy low-power mode before changing wakeup flags */ if (ehci->has_hostpc) { @@ -154,9 +162,9 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); } - spin_unlock_irqrestore(&ehci->lock, flags); + spin_unlock_irq(&ehci->lock); msleep(5); - spin_lock_irqsave(&ehci->lock, flags); + spin_lock_irq(&ehci->lock); } port = HCS_N_PORTS(ehci->hcs_params); @@ -194,7 +202,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (!suspending && ehci_port_change(ehci)) usb_hcd_resume_root_hub(ehci_to_hcd(ehci)); - spin_unlock_irqrestore(&ehci->lock, flags); + spin_unlock_irq(&ehci->lock); } static int ehci_bus_suspend (struct usb_hcd *hcd) @@ -209,6 +217,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); + /* stop the schedules */ + ehci_quiesce(ehci); + spin_lock_irq (&ehci->lock); /* Once the controller is stopped, port resumes that are already @@ -224,10 +235,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } } - /* stop schedules, clean any completed work */ - ehci_quiesce(ehci); - ehci_work(ehci); - /* Unlike other USB host controller types, EHCI doesn't have * any notion of "global" or bus-wide suspend. The driver has * to manually suspend all the active unsuspended ports, and @@ -289,6 +296,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) "succeeded" : "failed"); } } + spin_unlock_irq(&ehci->lock); /* Apparently some devices need a >= 1-uframe delay here */ if (ehci->bus_suspended) @@ -296,6 +304,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) /* turn off now-idle HC */ ehci_halt (ehci); + + spin_lock_irq(&ehci->lock); ehci->rh_state = EHCI_RH_SUSPENDED; end_unlink_async(ehci); @@ -424,13 +434,14 @@ static int ehci_bus_resume (struct usb_hcd *hcd) } ehci->next_statechange = jiffies + msecs_to_jiffies(5); + spin_unlock_irq(&ehci->lock); + + ehci_handover_companion_ports(ehci); /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); (void) ehci_readl(ehci, &ehci->regs->intr_enable); - spin_unlock_irq (&ehci->lock); - ehci_handover_companion_ports(ehci); return 0; } @@ -1018,7 +1029,9 @@ static int ehci_hub_control ( case USB_PORT_FEAT_TEST: if (!selector || selector > 5) goto error; + spin_unlock_irqrestore(&ehci->lock, flags); ehci_quiesce(ehci); + spin_lock_irqsave(&ehci->lock, flags); /* Put all enabled ports into suspend */ while (ports--) { @@ -1030,7 +1043,11 @@ static int ehci_hub_control ( ehci_writel(ehci, temp | PORT_SUSPEND, sreg); } + + spin_unlock_irqrestore(&ehci->lock, flags); ehci_halt(ehci); + spin_lock_irqsave(&ehci->lock, flags); + temp = ehci_readl(ehci, status_reg); temp |= selector << 16; ehci_writel(ehci, temp, status_reg); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index f7f3ce3275b..65360945df7 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -445,12 +445,11 @@ static int controller_suspend(struct device *dev) if (time_before(jiffies, ehci->next_statechange)) msleep(10); - spin_lock_irqsave(&ehci->lock, flags); + ehci_halt(ehci); + spin_lock_irqsave(&ehci->lock, flags); tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3; - ehci_halt(ehci); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - spin_unlock_irqrestore(&ehci->lock, flags); tegra_ehci_power_down(hcd); -- cgit v1.2.3-70-g09d2 From 43fe3a99d9caf10b25f9c596e9854cdae30db418 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 11 Jul 2012 11:23:16 -0400 Subject: USB: EHCI: resolve some unlikely races This patch (as1589) resolves some unlikely races involving system shutdown or controller death in ehci-hcd: Shutdown races with both root-hub resume and controller resume. Controller death races with root-hub suspend. A new bitflag is added to indicate that the controller has been shut down (whether for system shutdown or because it died). Tests are added in the suspend and resume pathways to avoid reactivating the controller after any sort of shutdown. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 20 +++++++++++++++++++- drivers/usb/host/ehci-hub.c | 27 +++++++++++++++++++++++---- drivers/usb/host/ehci.h | 1 + 3 files changed, 43 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ac4c8ddde20..e44ca5453aa 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -343,6 +343,7 @@ static void ehci_shutdown(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); spin_lock_irq(&ehci->lock); + ehci->shutdown = true; ehci->rh_state = EHCI_RH_STOPPING; ehci->enabled_hrtimer_events = 0; spin_unlock_irq(&ehci->lock); @@ -823,6 +824,7 @@ dead: usb_hc_died(hcd); /* Don't let the controller do anything more */ + ehci->shutdown = true; ehci->rh_state = EHCI_RH_STOPPING; ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); ehci_writel(ehci, ehci->command, &ehci->regs->command); @@ -1129,6 +1131,9 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) /* Mark hardware accessible again as we are back to full power by now */ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + if (ehci->shutdown) + return 0; /* Controller is dead */ + /* * If CF is still set and we aren't resuming from hibernation * then we maintained suspend power. @@ -1139,10 +1144,17 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) int mask = INTR_MASK; ehci_prepare_ports_for_controller_resume(ehci); + + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto skip; + if (!hcd->self.root_hub->do_remote_wakeup) mask &= ~STS_PCD; ehci_writel(ehci, mask, &ehci->regs->intr_enable); ehci_readl(ehci, &ehci->regs->intr_enable); + skip: + spin_unlock_irq(&ehci->lock); return 0; } @@ -1154,14 +1166,20 @@ static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) (void) ehci_halt(ehci); (void) ehci_reset(ehci); + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto skip; + ehci_writel(ehci, ehci->command, &ehci->regs->command); ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + ehci->rh_state = EHCI_RH_SUSPENDED; + spin_unlock_irq(&ehci->lock); + /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - ehci->rh_state = EHCI_RH_SUSPENDED; return 1; } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index ffc5f27df72..c7880223738 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -221,6 +221,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_quiesce(ehci); spin_lock_irq (&ehci->lock); + if (ehci->rh_state < EHCI_RH_RUNNING) + goto done; /* Once the controller is stopped, port resumes that are already * in progress won't complete. Hence if remote wakeup is enabled @@ -306,6 +308,10 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_halt (ehci); spin_lock_irq(&ehci->lock); + if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD)) + ehci_handle_controller_death(ehci); + if (ehci->rh_state != EHCI_RH_RUNNING) + goto done; ehci->rh_state = EHCI_RH_SUSPENDED; end_unlink_async(ehci); @@ -320,6 +326,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_writel(ehci, mask, &ehci->regs->intr_enable); ehci_readl(ehci, &ehci->regs->intr_enable); + done: ehci->next_statechange = jiffies + msecs_to_jiffies(10); ehci->enabled_hrtimer_events = 0; ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; @@ -342,10 +349,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); - if (!HCD_HW_ACCESSIBLE(hcd)) { - spin_unlock_irq(&ehci->lock); - return -ESHUTDOWN; - } + if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown) + goto shutdown; if (unlikely(ehci->debug)) { if (!dbgp_reset_prep()) @@ -384,6 +389,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_unlock_irq(&ehci->lock); msleep(8); spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; /* clear phy low-power mode before resume */ if (ehci->bus_suspended && ehci->has_hostpc) { @@ -401,6 +408,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_unlock_irq(&ehci->lock); msleep(5); spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; } /* manually resume the ports we suspended during bus_suspend() */ @@ -421,6 +430,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) spin_unlock_irq(&ehci->lock); msleep(20); spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; } i = HCS_N_PORTS (ehci->hcs_params); @@ -439,10 +450,18 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ehci_handover_companion_ports(ehci); /* Now we can safely re-enable irqs */ + spin_lock_irq(&ehci->lock); + if (ehci->shutdown) + goto shutdown; ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); (void) ehci_readl(ehci, &ehci->regs->intr_enable); + spin_unlock_irq(&ehci->lock); return 0; + + shutdown: + spin_unlock_irq(&ehci->lock); + return -ESHUTDOWN; } #else diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 7de58fe52d5..da07d98f7d1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -118,6 +118,7 @@ struct ehci_hcd { /* one per controller */ bool need_rescan:1; bool intr_unlinking:1; bool async_unlinking:1; + bool shutdown:1; struct ehci_qh *qh_scan_next; /* async schedule support */ -- cgit v1.2.3-70-g09d2 From 2b982ab177b1698abfde05e3c04d958a65283d63 Mon Sep 17 00:00:00 2001 From: Ben Minerds Date: Thu, 12 Jul 2012 00:10:16 +1000 Subject: USB: serial: keyspan: Fix spacing around conditional. Changes to conform with checkpatch.sh script. - spaces around '?' and ':'. Removed 14 checkpatch.sh errors. Signed-off-by: Ben Minerds Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 9a0ca835590..7f1a0458218 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -176,7 +176,7 @@ static void keyspan_set_termios(struct tty_struct *tty, tty_encode_baud_rate(tty, baud_rate, baud_rate); /* set CTS/RTS handshake etc. */ p_priv->cflag = cflag; - p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none; + p_priv->flow_control = (cflag & CRTSCTS) ? flow_cts : flow_none; /* Mark/Space not supported */ tty->termios->c_cflag &= ~CMSPAR; @@ -1099,7 +1099,7 @@ static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port) } /* set CTS/RTS handshake etc. */ p_priv->cflag = cflag; - p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none; + p_priv->flow_control = (cflag & CRTSCTS) ? flow_cts : flow_none; keyspan_send_setup(port, 1); /* mdelay(100); */ @@ -1704,7 +1704,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, msg.setPrescaler = 0xff; } - msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1; + msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1; switch (p_priv->cflag & CSIZE) { case CS5: msg.lcr |= USA_DATABITS_5; @@ -1721,7 +1721,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, } if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ - msg.lcr |= (p_priv->cflag & PARODD)? + msg.lcr |= (p_priv->cflag & PARODD) ? USA_PARITY_ODD : USA_PARITY_EVEN; } msg.setLcr = 0xff; @@ -1989,7 +1989,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, /* msg.setPrescaler = 0xff; */ } - msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1; + msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1; switch (p_priv->cflag & CSIZE) { case CS5: msg.lcr |= USA_DATABITS_5; @@ -2006,7 +2006,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, } if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ - msg.lcr |= (p_priv->cflag & PARODD)? + msg.lcr |= (p_priv->cflag & PARODD) ? USA_PARITY_ODD : USA_PARITY_EVEN; } msg.setLcr = 0xff; @@ -2173,7 +2173,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, msg.txMode = TXMODE_BYHAND; } - msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1; + msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1; switch (p_priv->cflag & CSIZE) { case CS5: msg.lcr |= USA_DATABITS_5; @@ -2190,7 +2190,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, } if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ - msg.lcr |= (p_priv->cflag & PARODD)? + msg.lcr |= (p_priv->cflag & PARODD) ? USA_PARITY_ODD : USA_PARITY_EVEN; } if (p_priv->old_cflag != p_priv->cflag) { @@ -2317,7 +2317,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial, } if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ - msg.lcr |= (p_priv->cflag & PARODD)? + msg.lcr |= (p_priv->cflag & PARODD) ? USA_PARITY_ODD : USA_PARITY_EVEN; } msg.setLcr = 0xff; -- cgit v1.2.3-70-g09d2 From 40adac81432a1e65cc36b09c0c61b48f3fa292cf Mon Sep 17 00:00:00 2001 From: Ben Minerds Date: Thu, 12 Jul 2012 00:10:17 +1000 Subject: USB: serial: keyspan: Fixed space around equals. Changes to conform with checkpatch.sh script. - space around '='. Removed 1 checkpatch.sh error. Signed-off-by: Ben Minerds Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 7f1a0458218..1524bd5a736 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -474,7 +474,7 @@ static void usa28_indat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); data = urb->transfer_buffer; - tty =tty_port_tty_get(&port->port); + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); -- cgit v1.2.3-70-g09d2 From ddc04ae77f6ed8ba60c2387536eeb685e9f5e856 Mon Sep 17 00:00:00 2001 From: Ben Minerds Date: Thu, 12 Jul 2012 00:10:18 +1000 Subject: USB: serial: keyspan: Fixed space near open parenthesis. Changes to conform with checkpatch.sh script. - space near open parenthesis '('. Removed 2 checkpatch.sh errors. Signed-off-by: Ben Minerds Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 1524bd5a736..9006e574e2a 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -557,7 +557,7 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if( old_dcd_state != p_priv->dcd_state && old_dcd_state) { + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { tty = tty_port_tty_get(&port->port); if (tty && !C_CLOCAL(tty)) tty_hangup(tty); -- cgit v1.2.3-70-g09d2 From 878b5fd690acba94f3e9b053f0281f273237ce3a Mon Sep 17 00:00:00 2001 From: Ben Minerds Date: Thu, 12 Jul 2012 00:10:19 +1000 Subject: USB: serial: keyspan: Removed trailing whitespace. Changes to conform with checkpatch.sh script. - trailing whitespace. Removed 1 checkpatch.sh error. Signed-off-by: Ben Minerds Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 9006e574e2a..5f4b0fb77fb 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -559,7 +559,7 @@ static void usa28_instat_callback(struct urb *urb) if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) + if (tty && !C_CLOCAL(tty)) tty_hangup(tty); tty_kref_put(tty); } -- cgit v1.2.3-70-g09d2 From f9943c2596c76e6d5c032d13edb735a73b0cc582 Mon Sep 17 00:00:00 2001 From: Ben Minerds Date: Thu, 12 Jul 2012 00:10:20 +1000 Subject: USB: serial: keyspan: Removed unrequired parentheses. Changes to conform with checkpatch.sh script. - return is not a function, parentheses not required. Removed 1 checkpatch.sh error. Signed-off-by: Ben Minerds Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 5f4b0fb77fb..af0b70eaf03 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1235,7 +1235,7 @@ static int keyspan_fake_startup(struct usb_serial *serial) if (request_ihex_firmware(&fw, fw_name, &serial->dev->dev)) { dev_err(&serial->dev->dev, "Required keyspan firmware image (%s) unavailable.\n", fw_name); - return(1); + return 1; } dbg("Uploading Keyspan %s firmware.", fw_name); -- cgit v1.2.3-70-g09d2 From 08d7660d45bc3e14b0fafe2e891ca50b4b18591a Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Tue, 10 Jul 2012 06:58:48 -0500 Subject: USB: ehci-fsl: Update ifdef check to work on 64-bit ppc We need to use CONFIG_FSL_SOC_BOOKE instead of CONFIG_PPC_85xx as CONFIG_PPC_85xx isn't defined when we build support for 64-bit embedded FSL PPC SoCs. Signed-off-by: Kumar Gala Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index ab52db684b6..b7451b29c5a 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -313,7 +313,7 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) } if (pdata->have_sysif_regs) { -#ifdef CONFIG_PPC_85xx +#ifdef CONFIG_FSL_SOC_BOOKE out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); #else -- cgit v1.2.3-70-g09d2 From 72119743651054ceddd4fdf0cce161bdb4cc4aac Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 12 Jul 2012 10:34:56 -0400 Subject: usb: host: tegra: pass correct pointer in ehci_setup() The ehci_setup() require the pointer of usb_hcd. Passing the correct pointer in place of ehci_hcd pointer. This is side effect of change: commit 1a49e2ac9651df7349867a5cf44e2c83de1046af Author: Alan Stern EHCI: centralize controller initialization [Although I checked for this specifically, obviously I missed some of the calls. In addition to the mistake in ehci-tegra.c that Laxman fixed here, the same thing needs to be fixed in ehci-orion.c and ehci-xls.c. -- Alan Stern] Signed-off-by: Laxman Dewangan Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-orion.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci-xls.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 3e411230953..9408da83eaf 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -106,7 +106,7 @@ static int ehci_orion_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; - retval = ehci_setup(ehci); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 65360945df7..562c80e2bfe 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -285,7 +285,7 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* switch to host mode */ hcd->has_tt = 1; - retval = ehci_setup(ehci); + retval = ehci_setup(hcd); if (retval) return retval; diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c index 99c353a85ae..8dc6a22d90b 100644 --- a/drivers/usb/host/ehci-xls.c +++ b/drivers/usb/host/ehci-xls.c @@ -18,7 +18,7 @@ static int ehci_xls_setup(struct usb_hcd *hcd) ehci->caps = hcd->regs; - return ehci_setup(ehci); + return ehci_setup(hcd); } int ehci_xls_probe_internal(const struct hc_driver *driver, -- cgit v1.2.3-70-g09d2 From ba5952e0711b14d8d4fe172671f8aa6091ace3ee Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 13 Jul 2012 17:25:24 +0800 Subject: USB: ehci-omap: fix compile failure(v1) The omap_ehci_init() is introduced in the below commit: commit 1a49e2ac9651df7349867a5cf44e2c83de1046af(EHCI: centralize controller initialization) the local variable of 'pdev' inside omap_ehci_init() is used but not defined, so fix the compiling failure. Signed-off-by: Ming Lei Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 6133d93808d..ec21f4a4a05 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -115,9 +115,8 @@ static void omap_ehci_erratum_i693(struct ehci_hcd *ehci) clk_disable(usbhost_p2_fck); } -static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) +static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port) { - struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); unsigned long timeout = jiffies + msecs_to_jiffies(1000); unsigned reg = 0; @@ -139,7 +138,8 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) cpu_relax(); if (time_after(jiffies, timeout)) { - dev_dbg(&pdev->dev, "phy reset operation timed out\n"); + dev_dbg(hcd->self.controller, + "phy reset operation timed out\n"); break; } } @@ -167,9 +167,9 @@ static int omap_ehci_init(struct usb_hcd *hcd) /* Soft reset the PHY using PHY reset command over ULPI */ if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 0); + omap_ehci_soft_phy_reset(hcd, 0); if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(pdev, 1); + omap_ehci_soft_phy_reset(hcd, 1); /* we know this is the memory we want, no need to ioremap again */ ehci->caps = hcd->regs; -- cgit v1.2.3-70-g09d2 From d51389306f598d635c55e72f42609b543efd9469 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Mon, 16 Jul 2012 11:25:36 +0530 Subject: USB: ohci-exynos: Add support for device tree This patch adds support to parse probe data for ohci driver for exynos using device tree. Signed-off-by: Thomas Abraham Signed-off-by: Abhilash Kesavan Signed-off-by: Vivek Gautam Acked-by: Jingoo Han Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 8bcbdb5ade2..fc3091bd237 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -71,6 +72,8 @@ static const struct hc_driver exynos_ohci_hc_driver = { .start_port_reset = ohci_start_port_reset, }; +static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(32); + static int __devinit exynos_ohci_probe(struct platform_device *pdev) { struct exynos4_ohci_platdata *pdata; @@ -87,6 +90,16 @@ static int __devinit exynos_ohci_probe(struct platform_device *pdev) return -EINVAL; } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we move to full device tree support this will vanish off. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ohci_exynos_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd), GFP_KERNEL); if (!exynos_ohci) @@ -256,6 +269,14 @@ static const struct dev_pm_ops exynos_ohci_pm_ops = { .resume = exynos_ohci_resume, }; +#ifdef CONFIG_OF +static const struct of_device_id exynos_ohci_match[] = { + { .compatible = "samsung,exynos-ohci" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_ohci_match); +#endif + static struct platform_driver exynos_ohci_driver = { .probe = exynos_ohci_probe, .remove = __devexit_p(exynos_ohci_remove), @@ -264,6 +285,7 @@ static struct platform_driver exynos_ohci_driver = { .name = "exynos-ohci", .owner = THIS_MODULE, .pm = &exynos_ohci_pm_ops, + .of_match_table = of_match_ptr(exynos_ohci_match), } }; -- cgit v1.2.3-70-g09d2 From 2c026e2b36e8e934de9726398a3e1eeb3fa6055c Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Mon, 16 Jul 2012 11:25:37 +0530 Subject: USB: ehci-s5p: Add support for device tree This patch adds support to parse probe data for ehci driver for exynos using device tree Signed-off-by: Thomas Abraham Signed-off-by: Abhilash Kesavan Signed-off-by: Vivek Gautam Acked-by: Jingoo Han Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 13c179fb2ee..37d84cf9a40 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -63,6 +64,8 @@ static const struct hc_driver s5p_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32); + static int __devinit s5p_ehci_probe(struct platform_device *pdev) { struct s5p_ehci_platdata *pdata; @@ -79,6 +82,16 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) return -EINVAL; } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we move to full device tree support this will vanish off. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ehci_s5p_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd), GFP_KERNEL); if (!s5p_ehci) @@ -233,6 +246,14 @@ static const struct dev_pm_ops s5p_ehci_pm_ops = { .resume = s5p_ehci_resume, }; +#ifdef CONFIG_OF +static const struct of_device_id exynos_ehci_match[] = { + { .compatible = "samsung,exynos-ehci" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_ehci_match); +#endif + static struct platform_driver s5p_ehci_driver = { .probe = s5p_ehci_probe, .remove = __devexit_p(s5p_ehci_remove), @@ -241,6 +262,7 @@ static struct platform_driver s5p_ehci_driver = { .name = "s5p-ehci", .owner = THIS_MODULE, .pm = &s5p_ehci_pm_ops, + .of_match_table = of_match_ptr(exynos_ehci_match), } }; -- cgit v1.2.3-70-g09d2 From 2cf69930f03af63731678de79cb1dbe3547a5938 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 15 Jul 2012 16:47:36 +0200 Subject: USB: qcserial: consolidate usb_set_interface calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to replicate the same code all over the place. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 49 ++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 996015c5f1a..74e0b132af3 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -127,6 +127,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) __u8 nintf; __u8 ifnum; bool is_gobi1k = id->driver_info ? true : false; + int altsetting = -1; dev_dbg(dev, "Is Gobi 1000 = %d\n", is_gobi1k); @@ -160,15 +161,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) retval = 0; /* Success */ break; } - - retval = usb_set_interface(serial->dev, ifnum, 1); - if (retval < 0) { - dev_err(dev, - "Could not set interface, error %d\n", - retval); - retval = -ENODEV; - kfree(data); - } + altsetting = 1; } break; @@ -193,24 +186,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) if (ifnum == 1 && !is_gobi1k) { dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n"); - retval = usb_set_interface(serial->dev, ifnum, 0); - if (retval < 0) { - dev_err(dev, - "Could not set interface, error %d\n", - retval); - retval = -ENODEV; - kfree(data); - } + altsetting = 0; } else if (ifnum == 2) { dev_dbg(dev, "Modem port found\n"); - retval = usb_set_interface(serial->dev, ifnum, 0); - if (retval < 0) { - dev_err(dev, - "Could not set interface, error %d\n", - retval); - retval = -ENODEV; - kfree(data); - } + altsetting = 0; } else if (ifnum==3 && !is_gobi1k) { /* * NMEA (serial line 9600 8N1) @@ -218,14 +197,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) * # echo "\$GPS_STOP" > /dev/ttyUSBx */ dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n"); - retval = usb_set_interface(serial->dev, ifnum, 0); - if (retval < 0) { - dev_err(dev, - "Could not set interface, error %d\n", - retval); - retval = -ENODEV; - kfree(data); - } + altsetting = 0; } break; @@ -235,6 +207,17 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) retval = -ENODEV; } + if (altsetting >= 0) { + retval = usb_set_interface(serial->dev, ifnum, altsetting); + if (retval < 0) { + dev_err(dev, + "Could not set interface, error %d\n", + retval); + retval = -ENODEV; + kfree(data); + } + } + /* Set serial->private if not returning -ENODEV */ if (retval != -ENODEV) usb_set_serial_data(serial, data); -- cgit v1.2.3-70-g09d2 From d25d5f28828708b81658cff6f676b710c7ee171d Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 15 Jul 2012 16:47:37 +0200 Subject: USB: qcserial: centralize probe exit path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creating a common exit path from qcprobe to make it easier to extend it. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 74e0b132af3..7556f343c0f 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -157,11 +157,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { dev_dbg(dev, "QDL port found\n"); - if (serial->interface->num_altsetting == 1) { + if (serial->interface->num_altsetting == 1) retval = 0; /* Success */ - break; - } - altsetting = 1; + else + altsetting = 1; } break; @@ -203,8 +202,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) default: dev_err(dev, "unknown number of interfaces: %d\n", nintf); - kfree(data); - retval = -ENODEV; } if (altsetting >= 0) { @@ -214,13 +211,15 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) "Could not set interface, error %d\n", retval); retval = -ENODEV; - kfree(data); } } - /* Set serial->private if not returning -ENODEV */ - if (retval != -ENODEV) + /* Set serial->private if not returning error */ + if (retval == 0) usb_set_serial_data(serial, data); + else + kfree(data); + return retval; } -- cgit v1.2.3-70-g09d2 From 0dfbf65eb070a6dda535fcfec6028a7412698358 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 15 Jul 2012 16:47:38 +0200 Subject: USB: qcserial: make probe more flexible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preparing qcprobe support for more than just strict Gobi 1k or 2k+ devices. Many newer Qualcomm based devices provide the same serial ports, but using varying USB interface layouts. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 44 +++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 7556f343c0f..a9c4dc4eb05 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -143,14 +143,13 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) spin_lock_init(&data->susp_lock); - switch (nintf) { - case 1: + if (nintf == 1) { /* QDL mode */ /* Gobi 2000 has a single altsetting, older ones have two */ if (serial->interface->num_altsetting == 2) intf = &serial->interface->altsetting[1]; else if (serial->interface->num_altsetting > 2) - break; + goto done; if (intf->desc.bNumEndpoints == 2 && usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) && @@ -162,10 +161,18 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) else altsetting = 1; } - break; + goto done; - case 3: - case 4: + } + + if (nintf < 3 || nintf > 4) { + dev_err(dev, "unknown number of interfaces: %d\n", nintf); + goto done; + } + + /* default to enabling interface */ + altsetting = 0; + switch (ifnum) { /* Composite mode; don't bind to the QMI/net interface as that * gets handled by other drivers. */ @@ -183,27 +190,28 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) * 3: NMEA */ - if (ifnum == 1 && !is_gobi1k) { + case 1: + if (is_gobi1k) + altsetting = -1; + else dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n"); - altsetting = 0; - } else if (ifnum == 2) { - dev_dbg(dev, "Modem port found\n"); - altsetting = 0; - } else if (ifnum==3 && !is_gobi1k) { + break; + case 2: + dev_dbg(dev, "Modem port found\n"); + break; + case 3: + if (is_gobi1k) + altsetting = -1; + else /* * NMEA (serial line 9600 8N1) * # echo "\$GPS_START" > /dev/ttyUSBx * # echo "\$GPS_STOP" > /dev/ttyUSBx */ dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n"); - altsetting = 0; - } - break; - - default: - dev_err(dev, "unknown number of interfaces: %d\n", nintf); } +done: if (altsetting >= 0) { retval = usb_set_interface(serial->dev, ifnum, altsetting); if (retval < 0) { -- cgit v1.2.3-70-g09d2 From e7ad21e00eab6b77fb9b5cd79512b7dee473f476 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 15 Jul 2012 16:47:39 +0200 Subject: USB: qcserial: support generic Qualcomm serial ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to verify the interface layout when doing interface number based matching. We can safely trust the device ID table in this case. This allows the driver to support any USB interface layout for non-Gobi 1k/2k+ devices. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index a9c4dc4eb05..90395c7aa56 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -165,6 +165,13 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) } + /* allow any number of interfaces when doing direct interface match */ + if (id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) { + dev_dbg(dev, "Generic Qualcomm serial interface found\n"); + altsetting = 0; + goto done; + } + if (nintf < 3 || nintf > 4) { dev_err(dev, "unknown number of interfaces: %d\n", nintf); goto done; -- cgit v1.2.3-70-g09d2 From 1937131a01c6ce37bb1d76617293d0d5a0cd66a7 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 15 Jul 2012 16:47:40 +0200 Subject: USB: qcserial: adding Sierra Wireless devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QMI mode of the Sierra Wireless MC7710 is close to a Gobi device, and also identified as one by the Windows drivers provided by Sony. The serial interfaces are the same as for any other Gobi module, but the USB interface layout is different: 0: DM/DIAG (also present in bootloader mode) 2: NMEA 3: AT-capable modem port 8: QMI/net 19: QMI/net (not always present) 20: QMI/net (not always present) Note in particular that the NMEA and AT ports are reversed compared to a Gobi 2k+ device, and that the DM port appears as a QDL port in bootloader mode using the same device ID. The Sony driver also document two new devices with standard Gobi 2k+ layout (1199:68a5, 1199:68a9) having a QDL mode (1199:68a4, 1199:68a8). Adding these as well. Lenovo Windows drivers document the USB interface layout for a few additional Sierra Wireless devices. Adding these while at it: - MC7770 (1199:901b) with standard Gobi 2k+ layout - MC7700 (0f3d:68a2) with the same layout as MC7710 - MC7750 (114f:68a2) with the same layout as MC7710 - EM7700 (1199:901c) with the same layout as MC7710 Cc: Anton Samokhvalov Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 90395c7aa56..8d103019d6a 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -105,6 +105,10 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */ {USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */ {USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ + {USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */ + {USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */ + {USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */ + {USB_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */ {USB_DEVICE(0x1199, 0x9010)}, /* Sierra Wireless Gobi 3000 QDL */ {USB_DEVICE(0x1199, 0x9012)}, /* Sierra Wireless Gobi 3000 QDL */ {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ @@ -112,8 +116,24 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x1199, 0x9015)}, /* Sierra Wireless Gobi 3000 Modem device */ {USB_DEVICE(0x1199, 0x9018)}, /* Sierra Wireless Gobi 3000 QDL */ {USB_DEVICE(0x1199, 0x9019)}, /* Sierra Wireless Gobi 3000 Modem device */ + {USB_DEVICE(0x1199, 0x901b)}, /* Sierra Wireless MC7770 */ {USB_DEVICE(0x12D1, 0x14F0)}, /* Sony Gobi 3000 QDL */ {USB_DEVICE(0x12D1, 0x14F1)}, /* Sony Gobi 3000 Composite */ + + /* non Gobi Qualcomm serial devices */ + {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 0)}, /* Sierra Wireless MC7700 Device Management */ + {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 2)}, /* Sierra Wireless MC7700 NMEA */ + {USB_DEVICE_INTERFACE_NUMBER(0x0f3d, 0x68a2, 3)}, /* Sierra Wireless MC7700 Modem */ + {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 0)}, /* Sierra Wireless MC7750 Device Management */ + {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 2)}, /* Sierra Wireless MC7750 NMEA */ + {USB_DEVICE_INTERFACE_NUMBER(0x114f, 0x68a2, 3)}, /* Sierra Wireless MC7750 Modem */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 0)}, /* Sierra Wireless MC7710 Device Management */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 2)}, /* Sierra Wireless MC7710 NMEA */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x68a2, 3)}, /* Sierra Wireless MC7710 Modem */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 0)}, /* Sierra Wireless EM7700 Device Management */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 2)}, /* Sierra Wireless EM7700 NMEA */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 3)}, /* Sierra Wireless EM7700 Modem */ + { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v1.2.3-70-g09d2 From 726a09a78f2322d320a8044afcdb8e9f366cbc5c Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 15 Jul 2012 16:47:41 +0200 Subject: USB: sierra: QMI mode MC7710 moved to qcserial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MC7710 in QMI mode does not support the vendor specific USB requests used by this driver. The most noticable effect of this is a 5 second delay as each serial port is probed, caused by the set_power_state command timing out: [ 17.434291] usbcore: registered new interface driver sierra [ 17.434383] USB Serial support registered for Sierra USB modem [ 17.434486] sierra 8-4:1.0: Sierra USB modem converter detected [ 22.432413] usb 8-4: Sierra USB modem converter now attached to ttyUSB0 [ 22.432563] sierra 8-4:1.2: Sierra USB modem converter detected [ 27.432410] usb 8-4: Sierra USB modem converter now attached to ttyUSB1 [ 27.432562] sierra 8-4:1.3: Sierra USB modem converter detected [ 32.432463] usb 8-4: Sierra USB modem converter now attached to ttyUSB2 The MC7710 provide the same Qualcomm serial interfaces as Gobi modules, and the qcserial driver has been extended to support the module instead of this driver. Cc: Anton Samokhvalov Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/sierra.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index d423d36acc0..0274710cced 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -288,9 +288,6 @@ static const struct usb_device_id id_table[] = { /* Sierra Wireless HSPA Non-Composite Device */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)}, { USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */ - { USB_DEVICE(0x1199, 0x68A2), /* Sierra Wireless MC77xx in QMI mode */ - .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist - }, { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */ .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, -- cgit v1.2.3-70-g09d2 From 09110529780890804b22e997ae6b4fe3f0b3b158 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 12 Jul 2012 12:37:32 +0200 Subject: USB: option: add ZTE MF821D MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sold by O2 (telefonica germany) under the name "LTE4G" Tested-by: Thomas Schäfer Signed-off-by: Bjørn Mork Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 2b0c88da782..08ff9b86204 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -936,6 +936,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff), -- cgit v1.2.3-70-g09d2 From 925aa46ba963a4da6d8ee6ab1d04a02ffa8db62b Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Wed, 11 Jul 2012 11:09:28 +0800 Subject: USB: notify phy when root hub port connect change Phy may need to change settings when port connect change. Signed-off-by: Richard Zhao Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3febe54883b..540f20bf9e2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -4101,6 +4102,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } } + if (hcd->phy && !hdev->parent) { + if (portstatus & USB_PORT_STAT_CONNECTION) + usb_phy_notify_connect(hcd->phy, port1); + else + usb_phy_notify_disconnect(hcd->phy, port1); + } + /* Return now if debouncing failed or nothing is connected or * the device was "removed". */ -- cgit v1.2.3-70-g09d2 From fd81d59c90d38661e4891ee2f4c9b7c051e93022 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 17 Jul 2012 10:10:50 +0530 Subject: USB: ehci-s5p: Add vbus setup function to the s5p ehci glue layer This patch retrieves and configures the vbus control gpio via the device tree. The suspend/resume callbacks will be later modified for vbus control. Signed-off-by: Abhilash Kesavan Signed-off-by: Vivek Gautam Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 37d84cf9a40..9d8f1dd57cb 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,24 @@ static const struct hc_driver s5p_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static void s5p_setup_vbus_gpio(struct platform_device *pdev) +{ + int err; + int gpio; + + if (!pdev->dev.of_node) + return; + + gpio = of_get_named_gpio(pdev->dev.of_node, + "samsung,vbus-gpio", 0); + if (!gpio_is_valid(gpio)) + return; + + err = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ehci_vbus_gpio"); + if (err) + dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio); +} + static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32); static int __devinit s5p_ehci_probe(struct platform_device *pdev) @@ -92,6 +111,8 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + s5p_setup_vbus_gpio(pdev); + s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd), GFP_KERNEL); if (!s5p_ehci) -- cgit v1.2.3-70-g09d2 From 80da2e0df5af700518611b7d1cc4fc9945bcaf95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 19 Jul 2012 12:39:13 +0200 Subject: usb: Add quirk detection based on interface information When a whole class of devices (possibly from a specific vendor, or across multiple vendors) require a quirk, explictly listing all devices in the class make the quirks table unnecessarily large. Fix this by allowing matching devices based on interface information. Signed-off-by: Laurent Pinchart Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 38 +++++++++++-------- drivers/usb/core/hub.c | 10 +++-- drivers/usb/core/quirks.c | 93 ++++++++++++++++++++++++++++++++++++----------- drivers/usb/core/usb.h | 4 ++ 4 files changed, 106 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 69781016a26..445455a4429 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -607,22 +607,10 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id) } /* returns 0 if no match, 1 if match */ -int usb_match_one_id(struct usb_interface *interface, - const struct usb_device_id *id) +int usb_match_one_id_intf(struct usb_device *dev, + struct usb_host_interface *intf, + const struct usb_device_id *id) { - struct usb_host_interface *intf; - struct usb_device *dev; - - /* proc_connectinfo in devio.c may call us with id == NULL. */ - if (id == NULL) - return 0; - - intf = interface->cur_altsetting; - dev = interface_to_usbdev(interface); - - if (!usb_match_device(dev, id)) - return 0; - /* The interface class, subclass, protocol and number should never be * checked for a match if the device class is Vendor Specific, * unless the match record specifies the Vendor ID. */ @@ -652,6 +640,26 @@ int usb_match_one_id(struct usb_interface *interface, return 1; } + +/* returns 0 if no match, 1 if match */ +int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_host_interface *intf; + struct usb_device *dev; + + /* proc_connectinfo in devio.c may call us with id == NULL. */ + if (id == NULL) + return 0; + + intf = interface->cur_altsetting; + dev = interface_to_usbdev(interface); + + if (!usb_match_device(dev, id)) + return 0; + + return usb_match_one_id_intf(dev, intf, id); +} EXPORT_SYMBOL_GPL(usb_match_one_id); /** diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 540f20bf9e2..821126eb817 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2069,7 +2069,7 @@ static int usb_enumerate_device(struct usb_device *udev) if (err < 0) { dev_err(&udev->dev, "can't read configurations, error %d\n", err); - goto fail; + return err; } } if (udev->wusb == 1 && udev->authorized == 0) { @@ -2085,8 +2085,12 @@ static int usb_enumerate_device(struct usb_device *udev) udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); } err = usb_enumerate_device_otg(udev); -fail: - return err; + if (err < 0) + return err; + + usb_detect_interface_quirks(udev); + + return 0; } static void set_usb_port_removable(struct usb_device *udev) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 32d3adc315f..cbd15d1d25d 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -15,17 +15,22 @@ #include #include "usb.h" -/* List of quirky USB devices. Please keep this list ordered by: +/* Lists of quirky USB devices, split in device quirks and interface quirks. + * Device quirks are applied at the very beginning of the enumeration process, + * right after reading the device descriptor. They can thus only match on device + * information. + * + * Interface quirks are applied after reading all the configuration descriptors. + * They can match on both device and interface information. + * + * Note that the DELAY_INIT and HONOR_BNUMINTERFACES quirks do not make sense as + * interface quirks, as they only influence the enumeration process which is run + * before processing the interface quirks. + * + * Please keep the lists ordered by: * 1) Vendor ID * 2) Product ID * 3) Class ID - * - * as we want specific devices to be overridden first, and only after that, any - * class specific quirks. - * - * Right now the logic aborts if it finds a valid device in the table, we might - * want to change that in the future if it turns out that a whole class of - * devices is broken... */ static const struct usb_device_id usb_quirk_list[] = { /* CBM - Flash disk */ @@ -156,16 +161,53 @@ static const struct usb_device_id usb_quirk_list[] = { { } /* terminating entry must be last */ }; -static const struct usb_device_id *find_id(struct usb_device *udev) +static const struct usb_device_id usb_interface_quirk_list[] = { + { } /* terminating entry must be last */ +}; + +static bool usb_match_any_interface(struct usb_device *udev, + const struct usb_device_id *id) +{ + unsigned int i; + + for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) { + struct usb_host_config *cfg = &udev->config[i]; + unsigned int j; + + for (j = 0; j < cfg->desc.bNumInterfaces; ++j) { + struct usb_interface_cache *cache; + struct usb_host_interface *intf; + + cache = cfg->intf_cache[j]; + if (cache->num_altsetting == 0) + continue; + + intf = &cache->altsetting[0]; + if (usb_match_one_id_intf(udev, intf, id)) + return true; + } + } + + return false; +} + +static u32 __usb_detect_quirks(struct usb_device *udev, + const struct usb_device_id *id) { - const struct usb_device_id *id = usb_quirk_list; + u32 quirks = 0; - for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || - id->driver_info; id++) { - if (usb_match_device(udev, id)) - return id; + for (; id->match_flags; id++) { + if (!usb_match_device(udev, id)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) && + !usb_match_any_interface(udev, id)) + continue; + + quirks |= (u32)(id->driver_info); } - return NULL; + + return quirks; } /* @@ -173,14 +215,10 @@ static const struct usb_device_id *find_id(struct usb_device *udev) */ void usb_detect_quirks(struct usb_device *udev) { - const struct usb_device_id *id = usb_quirk_list; - - id = find_id(udev); - if (id) - udev->quirks = (u32)(id->driver_info); + udev->quirks = __usb_detect_quirks(udev, usb_quirk_list); if (udev->quirks) dev_dbg(&udev->dev, "USB quirks for this device: %x\n", - udev->quirks); + udev->quirks); /* For the present, all devices default to USB-PERSIST enabled */ #if 0 /* was: #ifdef CONFIG_PM */ @@ -197,3 +235,16 @@ void usb_detect_quirks(struct usb_device *udev) udev->persist_enabled = 1; #endif /* CONFIG_PM */ } + +void usb_detect_interface_quirks(struct usb_device *udev) +{ + u32 quirks; + + quirks = __usb_detect_quirks(udev, usb_interface_quirk_list); + if (quirks == 0) + return; + + dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n", + quirks); + udev->quirks |= quirks; +} diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 67875a89cfa..acb103c5c39 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -26,6 +26,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *); extern void usb_detect_quirks(struct usb_device *udev); +extern void usb_detect_interface_quirks(struct usb_device *udev); extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, @@ -37,6 +38,9 @@ extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); extern void usb_kick_khubd(struct usb_device *dev); +extern int usb_match_one_id_intf(struct usb_device *dev, + struct usb_host_interface *intf, + const struct usb_device_id *id); extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); extern void usb_forced_unbind_intf(struct usb_interface *intf); -- cgit v1.2.3-70-g09d2 From e387ef5c47ddeaeaa3cbdc54424cdb7a28dae2c0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 19 Jul 2012 12:39:14 +0200 Subject: usb: Add USB_QUIRK_RESET_RESUME for all Logitech UVC webcams Most Logitech UVC webcams (both early models that don't advertise UVC compatibility and newer UVC-advertised devices) require the RESET_RESUME quirk. Instead of listing each and every model, match the devices based on the UVC interface information. Signed-off-by: Laurent Pinchart Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 58 +++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index cbd15d1d25d..f15501f4c58 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -43,53 +43,23 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech Webcam C200 */ - { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam Fusion */ + { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech Webcam C250 */ - { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam Orbit MP */ + { USB_DEVICE(0x046d, 0x08c2), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech Webcam C300 */ - { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam Pro for Notebook */ + { USB_DEVICE(0x046d, 0x08c3), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech Webcam B/C500 */ - { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam Pro 5000 */ + { USB_DEVICE(0x046d, 0x08c5), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech Webcam C600 */ - { USB_DEVICE(0x046d, 0x0808), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam OEM Dell Notebook */ + { USB_DEVICE(0x046d, 0x08c6), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech Webcam Pro 9000 */ - { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Webcam C905 */ - { USB_DEVICE(0x046d, 0x080a), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Webcam C210 */ - { USB_DEVICE(0x046d, 0x0819), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Webcam C260 */ - { USB_DEVICE(0x046d, 0x081a), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Webcam C310 */ - { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Webcam C910 */ - { USB_DEVICE(0x046d, 0x0821), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Webcam C160 */ - { USB_DEVICE(0x046d, 0x0824), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Webcam C270 */ - { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Quickcam Pro 9000 */ - { USB_DEVICE(0x046d, 0x0990), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Quickcam E3500 */ - { USB_DEVICE(0x046d, 0x09a4), .driver_info = USB_QUIRK_RESET_RESUME }, - - /* Logitech Quickcam Vision Pro */ - { USB_DEVICE(0x046d, 0x09a6), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam OEM Cisco VT Camera II */ + { USB_DEVICE(0x046d, 0x08c7), .driver_info = USB_QUIRK_RESET_RESUME }, /* Logitech Harmony 700-series */ { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -162,6 +132,10 @@ static const struct usb_device_id usb_quirk_list[] = { }; static const struct usb_device_id usb_interface_quirk_list[] = { + /* Logitech UVC Cameras */ + { USB_VENDOR_AND_INTERFACE_INFO(0x046d, USB_CLASS_VIDEO, 1, 0), + .driver_info = USB_QUIRK_RESET_RESUME }, + { } /* terminating entry must be last */ }; -- cgit v1.2.3-70-g09d2