summaryrefslogtreecommitdiff
path: root/drivers/usb/cdns3/ep0.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/cdns3/ep0.c')
-rw-r--r--drivers/usb/cdns3/ep0.c193
1 files changed, 109 insertions, 84 deletions
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
index 390779e2294d..3785b75a8149 100644
--- a/drivers/usb/cdns3/ep0.c
+++ b/drivers/usb/cdns3/ep0.c
@@ -7,14 +7,16 @@
*
* Authors: Pawel Jez <pjez@cadence.com>,
* Pawel Laszczak <pawell@cadence.com>
- * Peter Chen <peter.chen@nxp.com>
+ * Peter Chen <peter.chen@nxp.com>
*/
+#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/usb/composite.h>
-#include <linux/iopoll.h>
#include "gadget.h"
#include "trace.h"
+#include "core.h"
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
@@ -32,25 +34,14 @@ static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
*/
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
dma_addr_t dma_addr,
- unsigned int length, int erdy, int zlp)
+ unsigned int length, int erdy)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
- priv_ep->trb_pool[0].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
- priv_ep->trb_pool[0].length = cpu_to_le32(TRB_LEN(length));
-
- if (zlp) {
- priv_ep->trb_pool[0].control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_NORMAL));
- priv_ep->trb_pool[1].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
- priv_ep->trb_pool[1].length = cpu_to_le32(TRB_LEN(0));
- priv_ep->trb_pool[1].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
- TRB_TYPE(TRB_NORMAL));
- } else {
- priv_ep->trb_pool[0].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
- TRB_TYPE(TRB_NORMAL));
- priv_ep->trb_pool[1].control = 0;
- }
+ priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
+ priv_ep->trb_pool->length = TRB_LEN(length);
+ priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
@@ -60,13 +51,12 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
readl(&regs->ep_traddr));
-
- /* TRB should be prepared before starting transfer. */
- writel(EP_CMD_DRDY, &regs->ep_cmd);
-
/* Resume controller before arming transfer. */
__cdns3_gadget_wakeup(priv_dev);
+ /* TRB should be prepared before starting transfer */
+ writel(EP_CMD_DRDY, &regs->ep_cmd);
+
if (erdy)
writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
}
@@ -82,11 +72,14 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
- int ret;
+ int ret = 0;
spin_unlock(&priv_dev->lock);
priv_dev->setup_pending = 1;
- ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
+ if (priv_dev->gadget_driver && priv_dev->gadget_driver->setup
+ && get_gadget_data(&priv_dev->gadget))
+ ret = priv_dev->gadget_driver->setup(&priv_dev->gadget,
+ ctrl_req);
priv_dev->setup_pending = 0;
spin_lock(&priv_dev->lock);
return ret;
@@ -97,7 +90,7 @@ static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
priv_dev->ep0_data_dir = 0;
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
- sizeof(struct usb_ctrlrequest), 0, 0);
+ sizeof(struct usb_ctrlrequest), 0);
}
static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
@@ -111,7 +104,7 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
list_del_init(&request->list);
if (send_stall) {
- trace_cdns3_halt(priv_ep, send_stall, 0);
+ cdns3_dbg(priv_ep->cdns3_dev, "STALL for ep0\n");
/* set_stall on ep0 */
cdns3_select_ep(priv_dev, 0x00);
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
@@ -122,8 +115,6 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
&priv_dev->regs->ep_cmd);
-
- cdns3_allow_enable_l1(priv_dev, 1);
}
/**
@@ -160,13 +151,12 @@ static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
if (result)
return result;
- if (config) {
- cdns3_set_hw_configuration(priv_dev);
- } else {
+ if (!config) {
cdns3_hw_reset_eps_config(priv_dev);
usb_gadget_set_state(&priv_dev->gadget,
USB_STATE_ADDRESS);
}
+
break;
case USB_STATE_CONFIGURED:
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
@@ -234,11 +224,10 @@ static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl)
{
- struct cdns3_endpoint *priv_ep;
__le16 *response_pkt;
u16 usb_status = 0;
u32 recip;
- u8 index;
+ u32 reg;
recip = ctrl->bRequestType & USB_RECIP_MASK;
@@ -254,6 +243,8 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
if (priv_dev->gadget.speed != USB_SPEED_SUPER)
break;
+ reg = readl(&priv_dev->regs->usb_sts);
+
if (priv_dev->u1_allowed)
usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
@@ -264,13 +255,9 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
case USB_RECIP_INTERFACE:
return cdns3_ep0_delegate_req(priv_dev, ctrl);
case USB_RECIP_ENDPOINT:
- index = cdns3_ep_addr_to_index(le16_to_cpu(ctrl->wIndex));
- priv_ep = priv_dev->eps[index];
-
- /* check if endpoint is stalled or stall is pending */
- cdns3_select_ep(priv_dev, le16_to_cpu(ctrl->wIndex));
- if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)) ||
- (priv_ep->flags & EP_STALL_PENDING))
+ /* check if endpoint is stalled */
+ cdns3_select_ep(priv_dev, ctrl->wIndex);
+ if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
usb_status = BIT(USB_ENDPOINT_HALT);
break;
default:
@@ -281,7 +268,7 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
*response_pkt = cpu_to_le16(usb_status);
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
- sizeof(*response_pkt), 1, 0);
+ sizeof(*response_pkt), 1);
return 0;
}
@@ -293,13 +280,15 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
enum usb_device_speed speed;
int ret = 0;
u32 wValue;
+ u32 wIndex;
u16 tmode;
wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
state = priv_dev->gadget.state;
speed = priv_dev->gadget.speed;
- switch (wValue) {
+ switch (ctrl->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
priv_dev->wake_up_flag = !!set;
break;
@@ -339,7 +328,7 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
* for sending status stage.
* This time should be less then 3ms.
*/
- mdelay(1);
+ usleep_range(1000, 2000);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
@@ -393,12 +382,48 @@ static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
cdns3_select_ep(priv_dev, le16_to_cpu(ctrl->wIndex));
- if (set)
- __cdns3_gadget_ep_set_halt(priv_ep);
- else if (!(priv_ep->flags & EP_WEDGE))
- ret = __cdns3_gadget_ep_clear_halt(priv_ep);
+ if (set) {
+ cdns3_dbg(priv_ep->cdns3_dev, "Stall endpoint %s\n",
+ priv_ep->name);
+ writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+ priv_ep->flags |= EP_STALL;
+ } else {
+ struct usb_request *request;
+ struct cdns3_request *priv_req;
+ struct cdns3_trb *trb;
+
+ if (priv_dev->eps[index]->flags & EP_WEDGE) {
+ cdns3_select_ep(priv_dev, 0x00);
+ return 0;
+ }
+
+ cdns3_dbg(priv_ep->cdns3_dev, "Clear Stalled endpoint %s\n",
+ priv_ep->name);
+
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+ if (request) {
+ priv_req = to_cdns3_request(request);
+ trb = priv_req->trb;
+ trb->control = trb->control ^ TRB_CYCLE;
+ }
+
+ writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+ /* wait for EPRST cleared */
+ ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+ EP_CMD_EPRST, 0, 100);
+ if (ret)
+ return -EINVAL;
+
+ priv_ep->flags &= ~EP_STALL;
- cdns3_select_ep(priv_dev, 0x00);
+ if (request) {
+ cdns3_dbg(priv_ep->cdns3_dev, "Resume transfer for %s\n",
+ priv_ep->name);
+ trb->control = trb->control ^ TRB_CYCLE;
+ cdns3_rearm_transfer(priv_ep, 1);
+ }
+ }
return ret;
}
@@ -458,7 +483,7 @@ static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
return -EINVAL;
}
- cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
+ cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
return 0;
}
@@ -546,6 +571,30 @@ void cdns3_pending_setup_status_handler(struct work_struct *work)
}
/**
+ * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+
+void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
+ int status)
+{
+ struct cdns3_endpoint *priv_ep;
+ struct usb_request *request;
+
+ priv_ep = priv_dev->eps[0];
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+
+ priv_ep->dir = priv_dev->ep0_data_dir;
+ cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
+}
+
+/**
* cdns3_ep0_setup_phase - Handling setup USB requests
* @priv_dev: extended gadget object
*/
@@ -601,6 +650,10 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
priv_ep->dir = priv_dev->ep0_data_dir;
+ if (request->zero && request->length && priv_ep->dir
+ && (request->length % priv_dev->gadget.ep0->maxpacket == 0))
+ cdns3_ep0_run_transfer(priv_dev, request->dma, 0, 1);
+
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
}
@@ -641,12 +694,7 @@ void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
__pending_setup_status_handler(priv_dev);
- if (ep_sts_reg & EP_STS_SETUP)
- priv_dev->wait_for_setup = 1;
-
- if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
- priv_dev->wait_for_setup = 0;
- cdns3_allow_enable_l1(priv_dev, 0);
+ if ((ep_sts_reg & EP_STS_SETUP)) {
cdns3_ep0_setup_phase(priv_dev);
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
priv_dev->ep0_data_dir = dir;
@@ -708,12 +756,13 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
unsigned long flags;
- int erdy_sent = 0;
int ret = 0;
- u8 zlp = 0;
+
+ cdns3_dbg(priv_ep->cdns3_dev, "Queue to Ep0%s L: %d\n",
+ priv_dev->ep0_data_dir ? "IN" : "OUT",
+ request->length);
spin_lock_irqsave(&priv_dev->lock, flags);
- trace_cdns3_ep0_queue(priv_dev, request);
/* cancel the request if controller receive new SETUP packet. */
if (cdns3_check_new_setup(priv_dev)) {
@@ -725,13 +774,8 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
cdns3_select_ep(priv_dev, 0x00);
- erdy_sent = !priv_dev->hw_configured_flag;
cdns3_set_hw_configuration(priv_dev);
-
- if (!erdy_sent)
- cdns3_ep0_complete_setup(priv_dev, 0, 1);
-
- cdns3_allow_enable_l1(priv_dev, 1);
+ cdns3_ep0_complete_setup(priv_dev, 0, 1);
request->actual = 0;
priv_dev->status_completion_no_call = true;
@@ -765,13 +809,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
request->status = -EINPROGRESS;
list_add_tail(&request->list, &priv_ep->pending_req_list);
-
- if (request->zero && request->length &&
- (request->length % ep->maxpacket == 0))
- zlp = 1;
-
- cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
-
+ cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
@@ -824,13 +862,7 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev)
max_packet_size = 512;
priv_ep = priv_dev->eps[0];
-
- if (!list_empty(&priv_ep->pending_req_list)) {
- struct usb_request *request;
-
- request = cdns3_next_request(&priv_ep->pending_req_list);
- list_del_init(&request->list);
- }
+ INIT_LIST_HEAD(&priv_ep->pending_req_list);
priv_dev->u1_allowed = 0;
priv_dev->u2_allowed = 0;
@@ -841,13 +873,6 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev)
/* init ep out */
cdns3_select_ep(priv_dev, USB_DIR_OUT);
- if (priv_dev->dev_ver >= DEV_VER_V3) {
- cdns3_set_register_bit(&priv_dev->regs->dtrans,
- BIT(0) | BIT(16));
- cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
- BIT(0) | BIT(16));
- }
-
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
&regs->ep_cfg);