Message ID | 1542535751-16079-13-git-send-email-pawell@cadence.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Introduced new Cadence USBSS DRD Driver | expand |
On 18/11/18 12:09, Pawel Laszczak wrote: > Patch implements a set of function related to enumeration process. > Some standard requests are handled on controller driver level and > other are delegated to gadget core driver. > All class requests are delegated to gadget core driver. > > Signed-off-by: Pawel Laszczak <pawell@cadence.com> > --- > drivers/usb/cdns3/ep0.c | 491 ++++++++++++++++++++++++++++++++++++- > drivers/usb/cdns3/gadget.c | 119 +++++++++ > drivers/usb/cdns3/gadget.h | 4 + > 3 files changed, 610 insertions(+), 4 deletions(-) > > diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c > index eb92fd234bd7..6f33d98f7684 100644 > --- a/drivers/usb/cdns3/ep0.c > +++ b/drivers/usb/cdns3/ep0.c > @@ -10,6 +10,7 @@ > * Peter Chen <peter.chen@nxp.com> > */ > > +#include <linux/usb/composite.h> > #include "gadget.h" > > static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = { > @@ -52,9 +53,31 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev, > writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd); > } > > +/** > + * cdns3_ep0_delegate_req - Returns status of handling setup packet > + * Setup is handled by gadget driver > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * > + * Returns zero on success or negative value on failure > + */ > +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl_req) > +{ > + int ret; > + > + spin_unlock(&priv_dev->lock); > + priv_dev->setup_pending = 1; > + ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req); > + priv_dev->setup_pending = 0; Why is setup_pending flag being set and cleared? > + spin_lock(&priv_dev->lock); > + return ret; > +} > + > static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev) > { > - //TODO: Implements this function > + priv_dev->ep0_data_dir = 0; > + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 8, 0); why hardcode to 8? Don't vendor specific requests have different lengths? > } > > static void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) > @@ -90,9 +113,431 @@ static void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) > } > } > > +/** > + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * > + * Returns 0 if success, 0x7FFF on deferred status stage, error code on error what is this magic number 0x7fff? > + */ > +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl_req) > +{ > + enum usb_device_state device_state = priv_dev->gadget.state; > + struct cdns3_endpoint *priv_ep, *temp_ep; > + u32 config = le16_to_cpu(ctrl_req->wValue); > + int result = 0; > + > + switch (device_state) { > + case USB_STATE_ADDRESS: > + /* Configure non-control EPs */ > + list_for_each_entry_safe(priv_ep, temp_ep, > + &priv_dev->ep_match_list, > + ep_match_pending_list) > + cdns3_ep_config(priv_ep); Why configure here? They should be configured at ep_enable. no? And you don't need to maintain a ep_match/pending_list. > + > + result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); > + > + if (result) > + return result; > + > + if (config) { What if result is USB_GADGET_DELAYED_STATUS? > + cdns3_set_hw_configuration(priv_dev); usb_gadget_set_state(USB_STATE_CONFIGURED) ? > + } else { > + cdns3_gadget_unconfig(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); > + > + if (!config && !result) { > + cdns3_gadget_unconfig(priv_dev); > + usb_gadget_set_state(&priv_dev->gadget, > + USB_STATE_ADDRESS); > + } > + break; > + default: > + result = -EINVAL; > + } > + > + return result; > +} > + > +/** > + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * > + * Returns 0 if success, error code on error > + */ > +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl_req) > +{ > + enum usb_device_state device_state = priv_dev->gadget.state; > + u32 reg; > + u32 addr; > + > + addr = le16_to_cpu(ctrl_req->wValue); > + > + if (addr > DEVICE_ADDRESS_MAX) { If DEVICE_ADDRESS_MAX comes from USB spec it must be in ch9.h. Maybe add something like #define USB_DEVICE_MAX_ADDRESS 127 > + dev_err(&priv_dev->dev, > + "Device address (%d) cannot be greater than %d\n", > + addr, DEVICE_ADDRESS_MAX); > + return -EINVAL; > + } > + > + if (device_state == USB_STATE_CONFIGURED) { > + dev_err(&priv_dev->dev, "USB device already configured\n"); Message is misleading. How about "can't set_address from configured state" > + return -EINVAL; > + } > + > + reg = readl(&priv_dev->regs->usb_cmd); > + > + writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR, > + &priv_dev->regs->usb_cmd); > + > + usb_gadget_set_state(&priv_dev->gadget, > + (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); > + > + cdns3_prepare_setup_packet(priv_dev); why call this here? This should be done after the current ep0 request is complete. > + > + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); > + > + return 0; > +} > + > +/** > + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * > + * Returns 0 if success, error code on error > + */ > +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl) > +{ > + __le16 *response_pkt; > + u16 usb_status = 0; > + u32 recip; > + u32 reg; > + > + recip = ctrl->bRequestType & USB_RECIP_MASK; > + > + switch (recip) { > + case USB_RECIP_DEVICE: > + /* self powered */ > + usb_status |= priv_dev->gadget.is_selfpowered; if (prv_devgadget.is_selfpowered) usb_stats |= BIT(USB_DEVICE_SELF_POWERED); > + > + if (priv_dev->gadget.speed != USB_SPEED_SUPER) You should check controller speed directly instead. > + break; > + > + reg = readl(&priv_dev->regs->usb_sts); > + > + if (priv_dev->u1_allowed) > + usb_status |= BIT(USB_DEV_STAT_U1_ENABLED); > + > + if (priv_dev->u2_allowed) > + usb_status |= BIT(USB_DEV_STAT_U2_ENABLED); > + > + if (priv_dev->wake_up_flag) > + usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP); Remote wakeup is not SS specific. So needs to go before the SS check. > + break; > + case USB_RECIP_INTERFACE: > + return cdns3_ep0_delegate_req(priv_dev, ctrl); > + case USB_RECIP_ENDPOINT: > + /* 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: > + return -EINVAL; > + } > + > + response_pkt = (__le16 *)priv_dev->setup; > + *response_pkt = cpu_to_le16(usb_status); > + > + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, > + sizeof(*response_pkt), 1); > + return 0; > +} > + > +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl, > + int set) > +{ > + enum usb_device_state state; > + 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 (ctrl->wValue) { > + case USB_DEVICE_REMOTE_WAKEUP: > + priv_dev->wake_up_flag = !!set; > + break; > + case USB_DEVICE_U1_ENABLE: > + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) > + return -EINVAL; > + > + priv_dev->u1_allowed = !!set; > + break; > + case USB_DEVICE_U2_ENABLE: > + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) > + return -EINVAL; > + > + priv_dev->u2_allowed = !!set; > + break; > + case USB_DEVICE_LTM_ENABLE: > + ret = -EINVAL; > + break; > + case USB_DEVICE_TEST_MODE: > + if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH) > + return -EINVAL; > + > + tmode = le16_to_cpu(ctrl->wIndex); > + > + if (!set || (tmode & 0xff) != 0) > + return -EINVAL; > + > + switch (tmode >> 8) { > + case TEST_J: > + case TEST_K: > + case TEST_SE0_NAK: > + case TEST_PACKET: > + cdns3_set_register_bit(&priv_dev->regs->usb_cmd, > + USB_CMD_STMODE | > + USB_STS_TMODE_SEL(tmode - 1)); > + break; > + default: > + ret = -EINVAL; > + } > + break; > + default: > + ret = -EINVAL; > + } > + > + return ret; > +} > + > +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl, > + int set) > +{ > + u32 wValue; > + int ret = 0; > + > + wValue = le16_to_cpu(ctrl->wValue); > + > + switch (wValue) { > + case USB_INTRF_FUNC_SUSPEND: > + break; > + default: > + ret = -EINVAL; > + } > + > + return ret; > +} > + > +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl, > + int set) > +{ > + struct cdns3_endpoint *priv_ep; > + int ret = 0; > + u8 index; > + > + if (!(ctrl->wIndex & ~USB_DIR_IN)) > + return 0; Why is this check? > + > + index = cdns3_ep_addr_to_index(ctrl->wIndex); > + priv_ep = priv_dev->eps[index]; > + > + cdns3_select_ep(priv_dev, ctrl->wIndex); > + > + if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) > + return -EINVAL; This check should be done first before you try to decode wIndex. > + > + if (set) { > + writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); > + priv_ep->flags |= EP_STALL; > + } else { > + struct usb_request *request; > + > + if (priv_dev->eps[index]->flags & EP_WEDGE) { > + cdns3_select_ep(priv_dev, 0x00); > + return 0; > + } > + > + 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; > + > + request = cdns3_next_request(&priv_ep->request_list); > + if (request) > + cdns3_ep_run_transfer(priv_ep, request); > + } > + return ret; > +} > + > +/** > + * cdns3_req_ep0_handle_feature - > + * Handling of GET/SET_FEATURE standard USB request > + * > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * @set: must be set to 1 for SET_FEATURE request > + * > + * Returns 0 if success, error code on error > + */ > +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl, > + int set) > +{ > + int ret = 0; > + u32 recip; > + > + recip = ctrl->bRequestType & USB_RECIP_MASK; > + > + switch (recip) { > + case USB_RECIP_DEVICE: > + ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set); > + break; > + case USB_RECIP_INTERFACE: > + ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set); > + break; > + case USB_RECIP_ENDPOINT: > + ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set); > + break; > + default: > + return -EINVAL; > + } > + > + if (!ret) > + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); > + > + return ret; > +} > + > +/** > + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * > + * Returns 0 if success, error code on error > + */ > +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl_req) > +{ > + if (priv_dev->gadget.state < USB_STATE_ADDRESS) > + return -EINVAL; > + > + if (ctrl_req->wLength != 6) { > + dev_err(&priv_dev->dev, "Set SEL should be 6 bytes, got %d\n", > + ctrl_req->wLength); > + return -EINVAL; > + } > + > + priv_dev->ep0_data_dir = 0; > + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1); > + return 0; > +} > + > +/** > + * cdns3_req_ep0_set_isoch_delay - > + * Handling of GET_ISOCH_DELAY standard USB request > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * > + * Returns 0 if success, error code on error > + */ > +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl_req) > +{ > + if (ctrl_req->wIndex || ctrl_req->wLength) > + return -EINVAL; > + > + priv_dev->isoch_delay = ctrl_req->wValue; > + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); > + return 0; > +} > + > +/** > + * cdns3_ep0_standard_request - Handling standard USB requests > + * @priv_dev: extended gadget object > + * @ctrl_req: pointer to received setup packet > + * > + * Returns 0 if success, error code on error > + */ > +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev, > + struct usb_ctrlrequest *ctrl_req) > +{ > + int ret; > + > + switch (ctrl_req->bRequest) { > + case USB_REQ_SET_ADDRESS: > + ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req); > + break; > + case USB_REQ_SET_CONFIGURATION: > + ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req); > + break; > + case USB_REQ_GET_STATUS: > + ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req); > + break; > + case USB_REQ_CLEAR_FEATURE: > + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0); > + break; > + case USB_REQ_SET_FEATURE: > + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1); > + break; > + case USB_REQ_SET_SEL: > + ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req); > + break; > + case USB_REQ_SET_ISOCH_DELAY: > + ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req); > + break; > + default: > + ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req); > + break; > + } > + > + return ret; > +} > + > static void __pending_setup_status_handler(struct cdns3_device *priv_dev) > { > - //TODO: Implements this function > + struct usb_request *request = priv_dev->pending_status_request; > + > + if (priv_dev->status_completion_no_call && request && > + request->complete) { > + request->complete(priv_dev->gadget.ep0, request); > + priv_dev->status_completion_no_call = 0; > + } > +} > + > +void cdns3_pending_setup_status_handler(struct work_struct *work) > +{ > + struct cdns3_device *priv_dev = container_of(work, struct cdns3_device, > + pending_status_wq); > + unsigned long flags; > + > + spin_lock_irqsave(&priv_dev->lock, flags); > + __pending_setup_status_handler(priv_dev); > + spin_unlock_irqrestore(&priv_dev->lock, flags); > } > > /** > @@ -101,12 +546,50 @@ static void __pending_setup_status_handler(struct cdns3_device *priv_dev) > */ > static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev) > { > - //TODO: Implements this function. > + struct usb_ctrlrequest *ctrl = priv_dev->setup; > + int result; > + > + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) > + result = cdns3_ep0_standard_request(priv_dev, ctrl); > + else > + result = cdns3_ep0_delegate_req(priv_dev, ctrl); > + > + if (result != 0 && result != USB_GADGET_DELAYED_STATUS) { > + dev_dbg(&priv_dev->dev, "STALL for ep0\n"); > + /* set_stall on ep0 */ > + cdns3_select_ep(priv_dev, 0x00); > + writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); > + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); > + } > } > > static void cdns3_transfer_completed(struct cdns3_device *priv_dev) > { > - //TODO: Implements this function > + if (priv_dev->ep0_request) { > + usb_gadget_unmap_request_by_dev(priv_dev->sysdev, > + priv_dev->ep0_request, > + priv_dev->ep0_data_dir); > + > + priv_dev->ep0_request->actual = > + TRB_LEN(le32_to_cpu(priv_dev->trb_ep0->length)); > + > + dev_dbg(&priv_dev->dev, "Ep0 completion length %d\n", > + priv_dev->ep0_request->actual); > + list_del_init(&priv_dev->ep0_request->list); > + } > + > + if (priv_dev->ep0_request && > + priv_dev->ep0_request->complete) { > + spin_unlock(&priv_dev->lock); > + priv_dev->ep0_request->complete(priv_dev->gadget.ep0, > + priv_dev->ep0_request); > + > + priv_dev->ep0_request = NULL; > + spin_lock(&priv_dev->lock); > + } > + > + cdns3_prepare_setup_packet(priv_dev); > + writel(EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); > } > > /** > diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c > index 309202474e57..0202ff5f6c90 100644 > --- a/drivers/usb/cdns3/gadget.c > +++ b/drivers/usb/cdns3/gadget.c > @@ -70,6 +70,30 @@ static u8 cdns3_ep_reg_pos_to_index(int i) > return ((i / 16) + (((i % 16) - 2) * 2)); > } > > +/** > + * cdns3_ep_addr_to_index - Macro converts endpoint address to > + * index of endpoint object in cdns3_device.eps[] container > + * @ep_addr: endpoint address for which endpoint object is required > + * > + * Remember that endpoint container doesn't contain default endpoint > + */ > +u8 cdns3_ep_addr_to_index(u8 ep_addr) > +{ > + return (((ep_addr & 0x7F) - 1) + ((ep_addr & USB_DIR_IN) ? 1 : 0)); > +} > + > +/** > + * cdns3_ep_addr_to_bit_pos - Macro converts endpoint address to > + * bit position in ep_ists register > + * @ep_addr: endpoint address for which bit position is required > + * > + * Remember that endpoint container doesn't contain default endpoint > + */ > +static u32 cdns3_ep_addr_to_bit_pos(u8 ep_addr) > +{ > + return (1 << (ep_addr & 0x7F)) << ((ep_addr & 0x80) ? 16 : 0); > +} > + > /** > * cdns3_next_request - returns next request from list > * @list: list containing requests > @@ -464,6 +488,99 @@ static irqreturn_t cdns3_irq_handler_thread(struct cdns3 *cdns) > return ret; > } > > +/** > + * cdns3_ep_onchip_buffer_alloc - Try to allocate onchip buf for EP > + * > + * The real allocation will occur during write to EP_CFG register, > + * this function is used to check if the 'size' allocation is allowed. > + * > + * @priv_dev: extended gadget object > + * @size: the size (KB) for EP would like to allocate > + * @is_in: the direction for EP > + * > + * Return 0 if the later allocation is allowed or negative value on failure > + */ > +static int cdns3_ep_onchip_buffer_alloc(struct cdns3_device *priv_dev, > + int size, int is_in) > +{ > + if (is_in) { > + priv_dev->onchip_mem_allocated_size += size; > + } else if (!priv_dev->out_mem_is_allocated) { > + /* ALL OUT EPs are shared the same chunk onchip memory */ > + priv_dev->onchip_mem_allocated_size += size; > + priv_dev->out_mem_is_allocated = 1; > + } > + > + if (priv_dev->onchip_mem_allocated_size > CDNS3_ONCHIP_BUF_SIZE) { > + priv_dev->onchip_mem_allocated_size -= size; > + return -EPERM; > + } else { > + return 0; > + } > +} > + > +/** > + * cdns3_ep_config Configure hardware endpoint > + * @priv_ep: extended endpoint object > + */ > +void cdns3_ep_config(struct cdns3_endpoint *priv_ep) > +{ > + bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); > + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; > + u32 bEndpointAddress = priv_ep->num | priv_ep->dir; > + u32 interrupt_mask = EP_STS_EN_TRBERREN; > + u32 max_packet_size = 0; > + u32 ep_cfg = 0; > + int ret; > + > + if (priv_ep->type == USB_ENDPOINT_XFER_INT) { > + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT); > + } else if (priv_ep->type == USB_ENDPOINT_XFER_BULK) { > + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK); > + } else { > + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC); > + interrupt_mask = 0xFFFFFFFF; > + } > + > + switch (priv_dev->gadget.speed) { > + case USB_SPEED_FULL: > + max_packet_size = is_iso_ep ? 1023 : 64; > + break; > + case USB_SPEED_HIGH: > + max_packet_size = is_iso_ep ? 1024 : 512; > + break; > + case USB_SPEED_SUPER: > + max_packet_size = 1024; > + break; > + default: > + //all other speed are not supported > + return; > + } > + > + ret = cdns3_ep_onchip_buffer_alloc(priv_dev, CDNS3_EP_BUF_SIZE, > + priv_ep->dir); where do you free the buffer_alloc? > + if (ret) { > + dev_err(&priv_dev->dev, "onchip mem is full, ep is invalid\n"); > + return; > + } > + > + ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | > + EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) | > + EP_CFG_MAXBURST(priv_ep->endpoint.maxburst); > + > + cdns3_select_ep(priv_dev, bEndpointAddress); > + > + writel(ep_cfg, &priv_dev->regs->ep_cfg); > + writel(interrupt_mask, &priv_dev->regs->ep_sts_en); > + > + dev_dbg(&priv_dev->dev, "Configure %s: with val %08x\n", > + priv_ep->name, ep_cfg); > + > + /* enable interrupt for selected endpoint */ > + cdns3_set_register_bit(&priv_dev->regs->ep_ien, > + cdns3_ep_addr_to_bit_pos(bEndpointAddress)); > +} > + > /* Find correct direction for HW endpoint according to description */ > static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc, > struct cdns3_endpoint *priv_ep) > @@ -1104,6 +1221,8 @@ static int __cdns3_gadget_init(struct cdns3 *cdns) > priv_dev->is_connected = 0; > > spin_lock_init(&priv_dev->lock); > + INIT_WORK(&priv_dev->pending_status_wq, > + cdns3_pending_setup_status_handler); > > priv_dev->in_standby_mode = 1; > > diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h > index 8c2f363f9340..db8c6cb9f2a5 100644 > --- a/drivers/usb/cdns3/gadget.h > +++ b/drivers/usb/cdns3/gadget.h > @@ -1070,14 +1070,18 @@ struct cdns3_device { > > int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec); > void cdns3_set_register_bit(void __iomem *ptr, u32 mask); > +void cdns3_pending_setup_status_handler(struct work_struct *work); > int cdns3_init_ep0(struct cdns3_device *priv_dev); > void cdns3_ep0_config(struct cdns3_device *priv_dev); > +void cdns3_ep_config(struct cdns3_endpoint *priv_ep); > void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir); > void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep); > void cdns3_enable_l1(struct cdns3_device *priv_dev, int enable); > struct usb_request *cdns3_next_request(struct list_head *list); > +void cdns3_gadget_unconfig(struct cdns3_device *priv_dev); > int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, > struct usb_request *request); > +u8 cdns3_ep_addr_to_index(u8 ep_addr); > int cdns3_gadget_ep_set_wedge(struct usb_ep *ep); > int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value); > struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep, > cheers, -roger
>On 18/11/18 12:09, Pawel Laszczak wrote: >> Patch implements a set of function related to enumeration process. >> Some standard requests are handled on controller driver level and >> other are delegated to gadget core driver. >> All class requests are delegated to gadget core driver. >> >> Signed-off-by: Pawel Laszczak <pawell@cadence.com> >> --- >> drivers/usb/cdns3/ep0.c | 491 ++++++++++++++++++++++++++++++++++++- >> drivers/usb/cdns3/gadget.c | 119 +++++++++ >> drivers/usb/cdns3/gadget.h | 4 + >> 3 files changed, 610 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c >> index eb92fd234bd7..6f33d98f7684 100644 >> --- a/drivers/usb/cdns3/ep0.c >> +++ b/drivers/usb/cdns3/ep0.c >> @@ -10,6 +10,7 @@ >> * Peter Chen <peter.chen@nxp.com> >> */ >> >> +#include <linux/usb/composite.h> >> #include "gadget.h" >> >> static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = { >> @@ -52,9 +53,31 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev, >> writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd); >> } >> >> +/** >> + * cdns3_ep0_delegate_req - Returns status of handling setup packet >> + * Setup is handled by gadget driver >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * >> + * Returns zero on success or negative value on failure >> + */ >> +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl_req) >> +{ >> + int ret; >> + >> + spin_unlock(&priv_dev->lock); >> + priv_dev->setup_pending = 1; >> + ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req); >> + priv_dev->setup_pending = 0; > >Why is setup_pending flag being set and cleared? This flag is checking during handling DESCMISS interrupt. If this flag is set then driver not start DMA for SETUP packet waiting in on-chip buffer. > >> + spin_lock(&priv_dev->lock); >> + return ret; >> +} >> + >> static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev) >> { >> - //TODO: Implements this function >> + priv_dev->ep0_data_dir = 0; >> + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 8, 0); > >why hardcode to 8? >Don't vendor specific requests have different lengths? SETUP packet always has 8 bytes. I will change this to sizeof(struct struct usb_ctrlrequest). It says more. > >> } >> >> static void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) >> @@ -90,9 +113,431 @@ static void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) >> } >> } >> >> +/** >> + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * >> + * Returns 0 if success, 0x7FFF on deferred status stage, error code on error > >what is this magic number 0x7fff? We will have USB_GADGET_DELAYED_STATUS instead 0x7FFF; > >> + */ >> +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl_req) >> +{ >> + enum usb_device_state device_state = priv_dev->gadget.state; >> + struct cdns3_endpoint *priv_ep, *temp_ep; >> + u32 config = le16_to_cpu(ctrl_req->wValue); >> + int result = 0; >> + >> + switch (device_state) { >> + case USB_STATE_ADDRESS: >> + /* Configure non-control EPs */ >> + list_for_each_entry_safe(priv_ep, temp_ep, >> + &priv_dev->ep_match_list, >> + ep_match_pending_list) >> + cdns3_ep_config(priv_ep); > >Why configure here? They should be configured at ep_enable. no? >And you don't need to maintain a ep_match/pending_list. It's a little tricky. We need to configure all endpoint on SET_CONFIGURATION request. After reset we can set only once CFGSET bit in usb_conf register. We don't need to enable endpoint, but we need set all other parameters. Not all endpoints are enabled before SET_CONFIGURATION, but most of them, or even all are claimed during loading function by means of usb_ep_autoconfig. After setting CFGSET bit we can't change configuration of any endpoint. We can only enable or disable them. >> + >> + result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); >> + >> + if (result) >> + return result; >> + >> + if (config) { > >What if result is USB_GADGET_DELAYED_STATUS? It's handled be cdns3_ep0_setup_phase when this function return USB_GADGET_DELAYED_STATUS > >> + cdns3_set_hw_configuration(priv_dev); > >usb_gadget_set_state(USB_STATE_CONFIGURED) ? It is set in set_config in composite driver. I think that it is sufficient. > >> + } else { >> + cdns3_gadget_unconfig(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); >> + >> + if (!config && !result) { >> + cdns3_gadget_unconfig(priv_dev); >> + usb_gadget_set_state(&priv_dev->gadget, >> + USB_STATE_ADDRESS); >> + } >> + break; >> + default: >> + result = -EINVAL; >> + } >> + >> + return result; >> +} >> + >> +/** >> + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * >> + * Returns 0 if success, error code on error >> + */ >> +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl_req) >> +{ >> + enum usb_device_state device_state = priv_dev->gadget.state; >> + u32 reg; >> + u32 addr; >> + >> + addr = le16_to_cpu(ctrl_req->wValue); >> + >> + if (addr > DEVICE_ADDRESS_MAX) { > >If DEVICE_ADDRESS_MAX comes from USB spec it must be in ch9.h. >Maybe add something like > >#define USB_DEVICE_MAX_ADDRESS 127 > Yes, it should, but I didn't see such macro definition in ch9.h. I will add change this to USB_DEVICE_MAX_ADDRESS. >> + dev_err(&priv_dev->dev, >> + "Device address (%d) cannot be greater than %d\n", >> + addr, DEVICE_ADDRESS_MAX); >> + return -EINVAL; >> + } >> + >> + if (device_state == USB_STATE_CONFIGURED) { >> + dev_err(&priv_dev->dev, "USB device already configured\n"); > >Message is misleading. How about "can't set_address from configured state" > Sounds better. >> + return -EINVAL; >> + } >> + >> + reg = readl(&priv_dev->regs->usb_cmd); >> + >> + writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR, >> + &priv_dev->regs->usb_cmd); >> + >> + usb_gadget_set_state(&priv_dev->gadget, >> + (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); >> + >> + cdns3_prepare_setup_packet(priv_dev); > >why call this here? This should be done after the current ep0 request is complete. It only arm DMA for next SETUP packet. It's allow to eliminate one extra DESCMISS interrupt. This interrupt is generated when packet is in on-chip buffer and DMA is not started. Additionally, all OUT endpoints have common shared FIFO buffer. It's the reason why data from This on-chip buffers should be copied to system memory ASAP. Each delay before getting packet from this FIFO has impact on performance. >> + >> + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); >> + >> + return 0; >> +} >> + >> +/** >> + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * >> + * Returns 0 if success, error code on error >> + */ >> +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl) >> +{ >> + __le16 *response_pkt; >> + u16 usb_status = 0; >> + u32 recip; >> + u32 reg; >> + >> + recip = ctrl->bRequestType & USB_RECIP_MASK; >> + >> + switch (recip) { >> + case USB_RECIP_DEVICE: >> + /* self powered */ >> + usb_status |= priv_dev->gadget.is_selfpowered; > >if (prv_devgadget.is_selfpowered) > usb_stats |= BIT(USB_DEVICE_SELF_POWERED); > Ok, I don't understand why, but I see such solution in MTU3 driver In musb it is used in such way: result[0] = musb->g.is_selfpowered << USB_DEVICE_SELF_POWERED; >> + >> + if (priv_dev->gadget.speed != USB_SPEED_SUPER) > >You should check controller speed directly instead. > >> + break; >> + >> + reg = readl(&priv_dev->regs->usb_sts); >> + >> + if (priv_dev->u1_allowed) >> + usb_status |= BIT(USB_DEV_STAT_U1_ENABLED); >> + >> + if (priv_dev->u2_allowed) >> + usb_status |= BIT(USB_DEV_STAT_U2_ENABLED); >> + >> + if (priv_dev->wake_up_flag) >> + usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP); > >Remote wakeup is not SS specific. So needs to go before the SS check. Yes, sure. > >> + break; >> + case USB_RECIP_INTERFACE: >> + return cdns3_ep0_delegate_req(priv_dev, ctrl); >> + case USB_RECIP_ENDPOINT: >> + /* 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: >> + return -EINVAL; >> + } >> + >> + response_pkt = (__le16 *)priv_dev->setup; >> + *response_pkt = cpu_to_le16(usb_status); >> + >> + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, >> + sizeof(*response_pkt), 1); >> + return 0; >> +} >> + >> +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl, >> + int set) >> +{ >> + enum usb_device_state state; >> + 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 (ctrl->wValue) { >> + case USB_DEVICE_REMOTE_WAKEUP: >> + priv_dev->wake_up_flag = !!set; >> + break; >> + case USB_DEVICE_U1_ENABLE: >> + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) >> + return -EINVAL; >> + >> + priv_dev->u1_allowed = !!set; >> + break; >> + case USB_DEVICE_U2_ENABLE: >> + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) >> + return -EINVAL; >> + >> + priv_dev->u2_allowed = !!set; >> + break; >> + case USB_DEVICE_LTM_ENABLE: >> + ret = -EINVAL; >> + break; >> + case USB_DEVICE_TEST_MODE: >> + if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH) >> + return -EINVAL; >> + >> + tmode = le16_to_cpu(ctrl->wIndex); >> + >> + if (!set || (tmode & 0xff) != 0) >> + return -EINVAL; >> + >> + switch (tmode >> 8) { >> + case TEST_J: >> + case TEST_K: >> + case TEST_SE0_NAK: >> + case TEST_PACKET: >> + cdns3_set_register_bit(&priv_dev->regs->usb_cmd, >> + USB_CMD_STMODE | >> + USB_STS_TMODE_SEL(tmode - 1)); >> + break; >> + default: >> + ret = -EINVAL; >> + } >> + break; >> + default: >> + ret = -EINVAL; >> + } >> + >> + return ret; >> +} >> + >> +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl, >> + int set) >> +{ >> + u32 wValue; >> + int ret = 0; >> + >> + wValue = le16_to_cpu(ctrl->wValue); >> + >> + switch (wValue) { >> + case USB_INTRF_FUNC_SUSPEND: >> + break; >> + default: >> + ret = -EINVAL; >> + } >> + >> + return ret; >> +} >> + >> +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl, >> + int set) >> +{ >> + struct cdns3_endpoint *priv_ep; >> + int ret = 0; >> + u8 index; >> + >> + if (!(ctrl->wIndex & ~USB_DIR_IN)) >> + return 0; > >Why is this check? Whether endpoint in wIndex is different then 0. I use this value in next line and it must be greater than 0. > >> + >> + index = cdns3_ep_addr_to_index(ctrl->wIndex); >> + priv_ep = priv_dev->eps[index]; >> + >> + cdns3_select_ep(priv_dev, ctrl->wIndex); >> + >> + if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) >> + return -EINVAL; > >This check should be done first before you try to decode wIndex. I understand that you mean that it doesn't make sense doing anything when endpoint is halted. > >> + >> + if (set) { >> + writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); >> + priv_ep->flags |= EP_STALL; >> + } else { >> + struct usb_request *request; >> + >> + if (priv_dev->eps[index]->flags & EP_WEDGE) { >> + cdns3_select_ep(priv_dev, 0x00); >> + return 0; >> + } >> + >> + 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; >> + >> + request = cdns3_next_request(&priv_ep->request_list); >> + if (request) >> + cdns3_ep_run_transfer(priv_ep, request); >> + } >> + return ret; >> +} >> + >> +/** >> + * cdns3_req_ep0_handle_feature - >> + * Handling of GET/SET_FEATURE standard USB request >> + * >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * @set: must be set to 1 for SET_FEATURE request >> + * >> + * Returns 0 if success, error code on error >> + */ >> +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl, >> + int set) >> +{ >> + int ret = 0; >> + u32 recip; >> + >> + recip = ctrl->bRequestType & USB_RECIP_MASK; >> + >> + switch (recip) { >> + case USB_RECIP_DEVICE: >> + ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set); >> + break; >> + case USB_RECIP_INTERFACE: >> + ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set); >> + break; >> + case USB_RECIP_ENDPOINT: >> + ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set); >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + if (!ret) >> + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); >> + >> + return ret; >> +} >> + >> +/** >> + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * >> + * Returns 0 if success, error code on error >> + */ >> +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl_req) >> +{ >> + if (priv_dev->gadget.state < USB_STATE_ADDRESS) >> + return -EINVAL; >> + >> + if (ctrl_req->wLength != 6) { >> + dev_err(&priv_dev->dev, "Set SEL should be 6 bytes, got %d\n", >> + ctrl_req->wLength); >> + return -EINVAL; >> + } >> + >> + priv_dev->ep0_data_dir = 0; >> + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1); >> + return 0; >> +} >> + >> +/** >> + * cdns3_req_ep0_set_isoch_delay - >> + * Handling of GET_ISOCH_DELAY standard USB request >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * >> + * Returns 0 if success, error code on error >> + */ >> +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl_req) >> +{ >> + if (ctrl_req->wIndex || ctrl_req->wLength) >> + return -EINVAL; >> + >> + priv_dev->isoch_delay = ctrl_req->wValue; >> + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); >> + return 0; >> +} >> + >> +/** >> + * cdns3_ep0_standard_request - Handling standard USB requests >> + * @priv_dev: extended gadget object >> + * @ctrl_req: pointer to received setup packet >> + * >> + * Returns 0 if success, error code on error >> + */ >> +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev, >> + struct usb_ctrlrequest *ctrl_req) >> +{ >> + int ret; >> + >> + switch (ctrl_req->bRequest) { >> + case USB_REQ_SET_ADDRESS: >> + ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req); >> + break; >> + case USB_REQ_SET_CONFIGURATION: >> + ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req); >> + break; >> + case USB_REQ_GET_STATUS: >> + ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req); >> + break; >> + case USB_REQ_CLEAR_FEATURE: >> + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0); >> + break; >> + case USB_REQ_SET_FEATURE: >> + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1); >> + break; >> + case USB_REQ_SET_SEL: >> + ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req); >> + break; >> + case USB_REQ_SET_ISOCH_DELAY: >> + ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req); >> + break; >> + default: >> + ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req); >> + break; >> + } >> + >> + return ret; >> +} >> + >> static void __pending_setup_status_handler(struct cdns3_device *priv_dev) >> { >> - //TODO: Implements this function >> + struct usb_request *request = priv_dev->pending_status_request; >> + >> + if (priv_dev->status_completion_no_call && request && >> + request->complete) { >> + request->complete(priv_dev->gadget.ep0, request); >> + priv_dev->status_completion_no_call = 0; >> + } >> +} >> + >> +void cdns3_pending_setup_status_handler(struct work_struct *work) >> +{ >> + struct cdns3_device *priv_dev = container_of(work, struct cdns3_device, >> + pending_status_wq); >> + unsigned long flags; >> + >> + spin_lock_irqsave(&priv_dev->lock, flags); >> + __pending_setup_status_handler(priv_dev); >> + spin_unlock_irqrestore(&priv_dev->lock, flags); >> } >> >> /** >> @@ -101,12 +546,50 @@ static void __pending_setup_status_handler(struct cdns3_device *priv_dev) >> */ >> static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev) >> { >> - //TODO: Implements this function. >> + struct usb_ctrlrequest *ctrl = priv_dev->setup; >> + int result; >> + >> + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) >> + result = cdns3_ep0_standard_request(priv_dev, ctrl); >> + else >> + result = cdns3_ep0_delegate_req(priv_dev, ctrl); >> + >> + if (result != 0 && result != USB_GADGET_DELAYED_STATUS) { >> + dev_dbg(&priv_dev->dev, "STALL for ep0\n"); >> + /* set_stall on ep0 */ >> + cdns3_select_ep(priv_dev, 0x00); >> + writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); >> + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); >> + } >> } >> >> static void cdns3_transfer_completed(struct cdns3_device *priv_dev) >> { >> - //TODO: Implements this function >> + if (priv_dev->ep0_request) { >> + usb_gadget_unmap_request_by_dev(priv_dev->sysdev, >> + priv_dev->ep0_request, >> + priv_dev->ep0_data_dir); >> + >> + priv_dev->ep0_request->actual = >> + TRB_LEN(le32_to_cpu(priv_dev->trb_ep0->length)); >> + >> + dev_dbg(&priv_dev->dev, "Ep0 completion length %d\n", >> + priv_dev->ep0_request->actual); >> + list_del_init(&priv_dev->ep0_request->list); >> + } >> + >> + if (priv_dev->ep0_request && >> + priv_dev->ep0_request->complete) { >> + spin_unlock(&priv_dev->lock); >> + priv_dev->ep0_request->complete(priv_dev->gadget.ep0, >> + priv_dev->ep0_request); >> + >> + priv_dev->ep0_request = NULL; >> + spin_lock(&priv_dev->lock); >> + } >> + >> + cdns3_prepare_setup_packet(priv_dev); >> + writel(EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); >> } >> >> /** >> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c >> index 309202474e57..0202ff5f6c90 100644 >> --- a/drivers/usb/cdns3/gadget.c >> +++ b/drivers/usb/cdns3/gadget.c >> @@ -70,6 +70,30 @@ static u8 cdns3_ep_reg_pos_to_index(int i) >> return ((i / 16) + (((i % 16) - 2) * 2)); >> } >> >> +/** >> + * cdns3_ep_addr_to_index - Macro converts endpoint address to >> + * index of endpoint object in cdns3_device.eps[] container >> + * @ep_addr: endpoint address for which endpoint object is required >> + * >> + * Remember that endpoint container doesn't contain default endpoint >> + */ >> +u8 cdns3_ep_addr_to_index(u8 ep_addr) >> +{ >> + return (((ep_addr & 0x7F) - 1) + ((ep_addr & USB_DIR_IN) ? 1 : 0)); >> +} >> + >> +/** >> + * cdns3_ep_addr_to_bit_pos - Macro converts endpoint address to >> + * bit position in ep_ists register >> + * @ep_addr: endpoint address for which bit position is required >> + * >> + * Remember that endpoint container doesn't contain default endpoint >> + */ >> +static u32 cdns3_ep_addr_to_bit_pos(u8 ep_addr) >> +{ >> + return (1 << (ep_addr & 0x7F)) << ((ep_addr & 0x80) ? 16 : 0); >> +} >> + >> /** >> * cdns3_next_request - returns next request from list >> * @list: list containing requests >> @@ -464,6 +488,99 @@ static irqreturn_t cdns3_irq_handler_thread(struct cdns3 *cdns) >> return ret; >> } >> >> +/** >> + * cdns3_ep_onchip_buffer_alloc - Try to allocate onchip buf for EP >> + * >> + * The real allocation will occur during write to EP_CFG register, >> + * this function is used to check if the 'size' allocation is allowed. >> + * >> + * @priv_dev: extended gadget object >> + * @size: the size (KB) for EP would like to allocate >> + * @is_in: the direction for EP >> + * >> + * Return 0 if the later allocation is allowed or negative value on failure >> + */ >> +static int cdns3_ep_onchip_buffer_alloc(struct cdns3_device *priv_dev, >> + int size, int is_in) >> +{ >> + if (is_in) { >> + priv_dev->onchip_mem_allocated_size += size; >> + } else if (!priv_dev->out_mem_is_allocated) { >> + /* ALL OUT EPs are shared the same chunk onchip memory */ >> + priv_dev->onchip_mem_allocated_size += size; >> + priv_dev->out_mem_is_allocated = 1; >> + } >> + >> + if (priv_dev->onchip_mem_allocated_size > CDNS3_ONCHIP_BUF_SIZE) { >> + priv_dev->onchip_mem_allocated_size -= size; >> + return -EPERM; >> + } else { >> + return 0; >> + } >> +} >> + >> +/** >> + * cdns3_ep_config Configure hardware endpoint >> + * @priv_ep: extended endpoint object >> + */ >> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep) >> +{ >> + bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); >> + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; >> + u32 bEndpointAddress = priv_ep->num | priv_ep->dir; >> + u32 interrupt_mask = EP_STS_EN_TRBERREN; >> + u32 max_packet_size = 0; >> + u32 ep_cfg = 0; >> + int ret; >> + >> + if (priv_ep->type == USB_ENDPOINT_XFER_INT) { >> + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT); >> + } else if (priv_ep->type == USB_ENDPOINT_XFER_BULK) { >> + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK); >> + } else { >> + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC); >> + interrupt_mask = 0xFFFFFFFF; >> + } >> + >> + switch (priv_dev->gadget.speed) { >> + case USB_SPEED_FULL: >> + max_packet_size = is_iso_ep ? 1023 : 64; >> + break; >> + case USB_SPEED_HIGH: >> + max_packet_size = is_iso_ep ? 1024 : 512; >> + break; >> + case USB_SPEED_SUPER: >> + max_packet_size = 1024; >> + break; >> + default: >> + //all other speed are not supported >> + return; >> + } >> + >> + ret = cdns3_ep_onchip_buffer_alloc(priv_dev, CDNS3_EP_BUF_SIZE, >> + priv_ep->dir); > >where do you free the buffer_alloc? I don't free this buffer. This function was added by Peter Chan, and it refer to on-chip buffer. Peter in his platform has limited number of on-chip memory. If I remember correctly It has only 16KB. It's kind of software protection against exceeding this buffer. It's hard coded now in driver. I think that this value can be read from register, I have to consult this with hardware team. I think the cdns3_ep_onchip_buffer_reserve is a better name. It is not associated with allocation of memory. > >> + if (ret) { >> + dev_err(&priv_dev->dev, "onchip mem is full, ep is invalid\n"); >> + return; >> + } >> + >> + ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | >> + EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) | >> + EP_CFG_MAXBURST(priv_ep->endpoint.maxburst); >> + >> + cdns3_select_ep(priv_dev, bEndpointAddress); >> + >> + writel(ep_cfg, &priv_dev->regs->ep_cfg); >> + writel(interrupt_mask, &priv_dev->regs->ep_sts_en); >> + >> + dev_dbg(&priv_dev->dev, "Configure %s: with val %08x\n", >> + priv_ep->name, ep_cfg); >> + >> + /* enable interrupt for selected endpoint */ >> + cdns3_set_register_bit(&priv_dev->regs->ep_ien, >> + cdns3_ep_addr_to_bit_pos(bEndpointAddress)); >> +} >> + >> /* Find correct direction for HW endpoint according to description */ >> static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc, >> struct cdns3_endpoint *priv_ep) >> @@ -1104,6 +1221,8 @@ static int __cdns3_gadget_init(struct cdns3 *cdns) >> priv_dev->is_connected = 0; >> >> spin_lock_init(&priv_dev->lock); >> + INIT_WORK(&priv_dev->pending_status_wq, >> + cdns3_pending_setup_status_handler); >> >> priv_dev->in_standby_mode = 1; >> >> diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h >> index 8c2f363f9340..db8c6cb9f2a5 100644 >> --- a/drivers/usb/cdns3/gadget.h >> +++ b/drivers/usb/cdns3/gadget.h >> @@ -1070,14 +1070,18 @@ struct cdns3_device { >> >> int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec); >> void cdns3_set_register_bit(void __iomem *ptr, u32 mask); >> +void cdns3_pending_setup_status_handler(struct work_struct *work); >> int cdns3_init_ep0(struct cdns3_device *priv_dev); >> void cdns3_ep0_config(struct cdns3_device *priv_dev); >> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep); >> void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir); >> void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep); >> void cdns3_enable_l1(struct cdns3_device *priv_dev, int enable); >> struct usb_request *cdns3_next_request(struct list_head *list); >> +void cdns3_gadget_unconfig(struct cdns3_device *priv_dev); >> int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, >> struct usb_request *request); >> +u8 cdns3_ep_addr_to_index(u8 ep_addr); >> int cdns3_gadget_ep_set_wedge(struct usb_ep *ep); >> int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value); >> struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep, >> Cheers Pawel
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index eb92fd234bd7..6f33d98f7684 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -10,6 +10,7 @@ * Peter Chen <peter.chen@nxp.com> */ +#include <linux/usb/composite.h> #include "gadget.h" static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = { @@ -52,9 +53,31 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev, writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd); } +/** + * cdns3_ep0_delegate_req - Returns status of handling setup packet + * Setup is handled by gadget driver + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns zero on success or negative value on failure + */ +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + int ret; + + spin_unlock(&priv_dev->lock); + priv_dev->setup_pending = 1; + ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req); + priv_dev->setup_pending = 0; + spin_lock(&priv_dev->lock); + return ret; +} + static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev) { - //TODO: Implements this function + priv_dev->ep0_data_dir = 0; + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 8, 0); } static void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) @@ -90,9 +113,431 @@ static void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) } } +/** + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, 0x7FFF on deferred status stage, error code on error + */ +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + enum usb_device_state device_state = priv_dev->gadget.state; + struct cdns3_endpoint *priv_ep, *temp_ep; + u32 config = le16_to_cpu(ctrl_req->wValue); + int result = 0; + + switch (device_state) { + case USB_STATE_ADDRESS: + /* Configure non-control EPs */ + list_for_each_entry_safe(priv_ep, temp_ep, + &priv_dev->ep_match_list, + ep_match_pending_list) + cdns3_ep_config(priv_ep); + + result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + + if (result) + return result; + + if (config) { + cdns3_set_hw_configuration(priv_dev); + } else { + cdns3_gadget_unconfig(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); + + if (!config && !result) { + cdns3_gadget_unconfig(priv_dev); + usb_gadget_set_state(&priv_dev->gadget, + USB_STATE_ADDRESS); + } + break; + default: + result = -EINVAL; + } + + return result; +} + +/** + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + enum usb_device_state device_state = priv_dev->gadget.state; + u32 reg; + u32 addr; + + addr = le16_to_cpu(ctrl_req->wValue); + + if (addr > DEVICE_ADDRESS_MAX) { + dev_err(&priv_dev->dev, + "Device address (%d) cannot be greater than %d\n", + addr, DEVICE_ADDRESS_MAX); + return -EINVAL; + } + + if (device_state == USB_STATE_CONFIGURED) { + dev_err(&priv_dev->dev, "USB device already configured\n"); + return -EINVAL; + } + + reg = readl(&priv_dev->regs->usb_cmd); + + writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR, + &priv_dev->regs->usb_cmd); + + usb_gadget_set_state(&priv_dev->gadget, + (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); + + cdns3_prepare_setup_packet(priv_dev); + + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); + + return 0; +} + +/** + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl) +{ + __le16 *response_pkt; + u16 usb_status = 0; + u32 recip; + u32 reg; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + /* self powered */ + usb_status |= priv_dev->gadget.is_selfpowered; + + 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); + + if (priv_dev->u2_allowed) + usb_status |= BIT(USB_DEV_STAT_U2_ENABLED); + + if (priv_dev->wake_up_flag) + usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP); + break; + case USB_RECIP_INTERFACE: + return cdns3_ep0_delegate_req(priv_dev, ctrl); + case USB_RECIP_ENDPOINT: + /* 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: + return -EINVAL; + } + + response_pkt = (__le16 *)priv_dev->setup; + *response_pkt = cpu_to_le16(usb_status); + + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, + sizeof(*response_pkt), 1); + return 0; +} + +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + enum usb_device_state state; + 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 (ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + priv_dev->wake_up_flag = !!set; + break; + case USB_DEVICE_U1_ENABLE: + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) + return -EINVAL; + + priv_dev->u1_allowed = !!set; + break; + case USB_DEVICE_U2_ENABLE: + if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) + return -EINVAL; + + priv_dev->u2_allowed = !!set; + break; + case USB_DEVICE_LTM_ENABLE: + ret = -EINVAL; + break; + case USB_DEVICE_TEST_MODE: + if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH) + return -EINVAL; + + tmode = le16_to_cpu(ctrl->wIndex); + + if (!set || (tmode & 0xff) != 0) + return -EINVAL; + + switch (tmode >> 8) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + cdns3_set_register_bit(&priv_dev->regs->usb_cmd, + USB_CMD_STMODE | + USB_STS_TMODE_SEL(tmode - 1)); + break; + default: + ret = -EINVAL; + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + u32 wValue; + int ret = 0; + + wValue = le16_to_cpu(ctrl->wValue); + + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + struct cdns3_endpoint *priv_ep; + int ret = 0; + u8 index; + + if (!(ctrl->wIndex & ~USB_DIR_IN)) + return 0; + + index = cdns3_ep_addr_to_index(ctrl->wIndex); + priv_ep = priv_dev->eps[index]; + + cdns3_select_ep(priv_dev, ctrl->wIndex); + + if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) + return -EINVAL; + + if (set) { + writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); + priv_ep->flags |= EP_STALL; + } else { + struct usb_request *request; + + if (priv_dev->eps[index]->flags & EP_WEDGE) { + cdns3_select_ep(priv_dev, 0x00); + return 0; + } + + 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; + + request = cdns3_next_request(&priv_ep->request_list); + if (request) + cdns3_ep_run_transfer(priv_ep, request); + } + return ret; +} + +/** + * cdns3_req_ep0_handle_feature - + * Handling of GET/SET_FEATURE standard USB request + * + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * @set: must be set to 1 for SET_FEATURE request + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl, + int set) +{ + int ret = 0; + u32 recip; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set); + break; + case USB_RECIP_INTERFACE: + ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set); + break; + case USB_RECIP_ENDPOINT: + ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set); + break; + default: + return -EINVAL; + } + + if (!ret) + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); + + return ret; +} + +/** + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + if (priv_dev->gadget.state < USB_STATE_ADDRESS) + return -EINVAL; + + if (ctrl_req->wLength != 6) { + dev_err(&priv_dev->dev, "Set SEL should be 6 bytes, got %d\n", + ctrl_req->wLength); + return -EINVAL; + } + + priv_dev->ep0_data_dir = 0; + cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1); + return 0; +} + +/** + * cdns3_req_ep0_set_isoch_delay - + * Handling of GET_ISOCH_DELAY standard USB request + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + if (ctrl_req->wIndex || ctrl_req->wLength) + return -EINVAL; + + priv_dev->isoch_delay = ctrl_req->wValue; + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); + return 0; +} + +/** + * cdns3_ep0_standard_request - Handling standard USB requests + * @priv_dev: extended gadget object + * @ctrl_req: pointer to received setup packet + * + * Returns 0 if success, error code on error + */ +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev, + struct usb_ctrlrequest *ctrl_req) +{ + int ret; + + switch (ctrl_req->bRequest) { + case USB_REQ_SET_ADDRESS: + ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req); + break; + case USB_REQ_SET_CONFIGURATION: + ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req); + break; + case USB_REQ_GET_STATUS: + ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req); + break; + case USB_REQ_CLEAR_FEATURE: + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0); + break; + case USB_REQ_SET_FEATURE: + ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1); + break; + case USB_REQ_SET_SEL: + ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req); + break; + case USB_REQ_SET_ISOCH_DELAY: + ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req); + break; + default: + ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + break; + } + + return ret; +} + static void __pending_setup_status_handler(struct cdns3_device *priv_dev) { - //TODO: Implements this function + struct usb_request *request = priv_dev->pending_status_request; + + if (priv_dev->status_completion_no_call && request && + request->complete) { + request->complete(priv_dev->gadget.ep0, request); + priv_dev->status_completion_no_call = 0; + } +} + +void cdns3_pending_setup_status_handler(struct work_struct *work) +{ + struct cdns3_device *priv_dev = container_of(work, struct cdns3_device, + pending_status_wq); + unsigned long flags; + + spin_lock_irqsave(&priv_dev->lock, flags); + __pending_setup_status_handler(priv_dev); + spin_unlock_irqrestore(&priv_dev->lock, flags); } /** @@ -101,12 +546,50 @@ static void __pending_setup_status_handler(struct cdns3_device *priv_dev) */ static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev) { - //TODO: Implements this function. + struct usb_ctrlrequest *ctrl = priv_dev->setup; + int result; + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + result = cdns3_ep0_standard_request(priv_dev, ctrl); + else + result = cdns3_ep0_delegate_req(priv_dev, ctrl); + + if (result != 0 && result != USB_GADGET_DELAYED_STATUS) { + dev_dbg(&priv_dev->dev, "STALL for ep0\n"); + /* set_stall on ep0 */ + cdns3_select_ep(priv_dev, 0x00); + writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); + writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); + } } static void cdns3_transfer_completed(struct cdns3_device *priv_dev) { - //TODO: Implements this function + if (priv_dev->ep0_request) { + usb_gadget_unmap_request_by_dev(priv_dev->sysdev, + priv_dev->ep0_request, + priv_dev->ep0_data_dir); + + priv_dev->ep0_request->actual = + TRB_LEN(le32_to_cpu(priv_dev->trb_ep0->length)); + + dev_dbg(&priv_dev->dev, "Ep0 completion length %d\n", + priv_dev->ep0_request->actual); + list_del_init(&priv_dev->ep0_request->list); + } + + if (priv_dev->ep0_request && + priv_dev->ep0_request->complete) { + spin_unlock(&priv_dev->lock); + priv_dev->ep0_request->complete(priv_dev->gadget.ep0, + priv_dev->ep0_request); + + priv_dev->ep0_request = NULL; + spin_lock(&priv_dev->lock); + } + + cdns3_prepare_setup_packet(priv_dev); + writel(EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); } /** diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 309202474e57..0202ff5f6c90 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -70,6 +70,30 @@ static u8 cdns3_ep_reg_pos_to_index(int i) return ((i / 16) + (((i % 16) - 2) * 2)); } +/** + * cdns3_ep_addr_to_index - Macro converts endpoint address to + * index of endpoint object in cdns3_device.eps[] container + * @ep_addr: endpoint address for which endpoint object is required + * + * Remember that endpoint container doesn't contain default endpoint + */ +u8 cdns3_ep_addr_to_index(u8 ep_addr) +{ + return (((ep_addr & 0x7F) - 1) + ((ep_addr & USB_DIR_IN) ? 1 : 0)); +} + +/** + * cdns3_ep_addr_to_bit_pos - Macro converts endpoint address to + * bit position in ep_ists register + * @ep_addr: endpoint address for which bit position is required + * + * Remember that endpoint container doesn't contain default endpoint + */ +static u32 cdns3_ep_addr_to_bit_pos(u8 ep_addr) +{ + return (1 << (ep_addr & 0x7F)) << ((ep_addr & 0x80) ? 16 : 0); +} + /** * cdns3_next_request - returns next request from list * @list: list containing requests @@ -464,6 +488,99 @@ static irqreturn_t cdns3_irq_handler_thread(struct cdns3 *cdns) return ret; } +/** + * cdns3_ep_onchip_buffer_alloc - Try to allocate onchip buf for EP + * + * The real allocation will occur during write to EP_CFG register, + * this function is used to check if the 'size' allocation is allowed. + * + * @priv_dev: extended gadget object + * @size: the size (KB) for EP would like to allocate + * @is_in: the direction for EP + * + * Return 0 if the later allocation is allowed or negative value on failure + */ +static int cdns3_ep_onchip_buffer_alloc(struct cdns3_device *priv_dev, + int size, int is_in) +{ + if (is_in) { + priv_dev->onchip_mem_allocated_size += size; + } else if (!priv_dev->out_mem_is_allocated) { + /* ALL OUT EPs are shared the same chunk onchip memory */ + priv_dev->onchip_mem_allocated_size += size; + priv_dev->out_mem_is_allocated = 1; + } + + if (priv_dev->onchip_mem_allocated_size > CDNS3_ONCHIP_BUF_SIZE) { + priv_dev->onchip_mem_allocated_size -= size; + return -EPERM; + } else { + return 0; + } +} + +/** + * cdns3_ep_config Configure hardware endpoint + * @priv_ep: extended endpoint object + */ +void cdns3_ep_config(struct cdns3_endpoint *priv_ep) +{ + bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + u32 bEndpointAddress = priv_ep->num | priv_ep->dir; + u32 interrupt_mask = EP_STS_EN_TRBERREN; + u32 max_packet_size = 0; + u32 ep_cfg = 0; + int ret; + + if (priv_ep->type == USB_ENDPOINT_XFER_INT) { + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT); + } else if (priv_ep->type == USB_ENDPOINT_XFER_BULK) { + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK); + } else { + ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC); + interrupt_mask = 0xFFFFFFFF; + } + + switch (priv_dev->gadget.speed) { + case USB_SPEED_FULL: + max_packet_size = is_iso_ep ? 1023 : 64; + break; + case USB_SPEED_HIGH: + max_packet_size = is_iso_ep ? 1024 : 512; + break; + case USB_SPEED_SUPER: + max_packet_size = 1024; + break; + default: + //all other speed are not supported + return; + } + + ret = cdns3_ep_onchip_buffer_alloc(priv_dev, CDNS3_EP_BUF_SIZE, + priv_ep->dir); + if (ret) { + dev_err(&priv_dev->dev, "onchip mem is full, ep is invalid\n"); + return; + } + + ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | + EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) | + EP_CFG_MAXBURST(priv_ep->endpoint.maxburst); + + cdns3_select_ep(priv_dev, bEndpointAddress); + + writel(ep_cfg, &priv_dev->regs->ep_cfg); + writel(interrupt_mask, &priv_dev->regs->ep_sts_en); + + dev_dbg(&priv_dev->dev, "Configure %s: with val %08x\n", + priv_ep->name, ep_cfg); + + /* enable interrupt for selected endpoint */ + cdns3_set_register_bit(&priv_dev->regs->ep_ien, + cdns3_ep_addr_to_bit_pos(bEndpointAddress)); +} + /* Find correct direction for HW endpoint according to description */ static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc, struct cdns3_endpoint *priv_ep) @@ -1104,6 +1221,8 @@ static int __cdns3_gadget_init(struct cdns3 *cdns) priv_dev->is_connected = 0; spin_lock_init(&priv_dev->lock); + INIT_WORK(&priv_dev->pending_status_wq, + cdns3_pending_setup_status_handler); priv_dev->in_standby_mode = 1; diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 8c2f363f9340..db8c6cb9f2a5 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -1070,14 +1070,18 @@ struct cdns3_device { int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec); void cdns3_set_register_bit(void __iomem *ptr, u32 mask); +void cdns3_pending_setup_status_handler(struct work_struct *work); int cdns3_init_ep0(struct cdns3_device *priv_dev); void cdns3_ep0_config(struct cdns3_device *priv_dev); +void cdns3_ep_config(struct cdns3_endpoint *priv_ep); void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir); void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep); void cdns3_enable_l1(struct cdns3_device *priv_dev, int enable); struct usb_request *cdns3_next_request(struct list_head *list); +void cdns3_gadget_unconfig(struct cdns3_device *priv_dev); int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, struct usb_request *request); +u8 cdns3_ep_addr_to_index(u8 ep_addr); int cdns3_gadget_ep_set_wedge(struct usb_ep *ep); int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value); struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
Patch implements a set of function related to enumeration process. Some standard requests are handled on controller driver level and other are delegated to gadget core driver. All class requests are delegated to gadget core driver. Signed-off-by: Pawel Laszczak <pawell@cadence.com> --- drivers/usb/cdns3/ep0.c | 491 ++++++++++++++++++++++++++++++++++++- drivers/usb/cdns3/gadget.c | 119 +++++++++ drivers/usb/cdns3/gadget.h | 4 + 3 files changed, 610 insertions(+), 4 deletions(-)