162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cadence USBSS DRD Driver - gadget side. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018 Cadence Design Systems. 662306a36Sopenharmony_ci * Copyright (C) 2017-2018 NXP 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Authors: Pawel Jez <pjez@cadence.com>, 962306a36Sopenharmony_ci * Pawel Laszczak <pawell@cadence.com> 1062306a36Sopenharmony_ci * Peter Chen <peter.chen@nxp.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/usb/composite.h> 1462306a36Sopenharmony_ci#include <linux/iopoll.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "cdns3-gadget.h" 1762306a36Sopenharmony_ci#include "cdns3-trace.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = { 2062306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 2162306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 2262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_CONTROL, 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/** 2662306a36Sopenharmony_ci * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware 2762306a36Sopenharmony_ci * @priv_dev: extended gadget object 2862306a36Sopenharmony_ci * @dma_addr: physical address where data is/will be stored 2962306a36Sopenharmony_ci * @length: data length 3062306a36Sopenharmony_ci * @erdy: set it to 1 when ERDY packet should be sent - 3162306a36Sopenharmony_ci * exit from flow control state 3262306a36Sopenharmony_ci * @zlp: add zero length packet 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev, 3562306a36Sopenharmony_ci dma_addr_t dma_addr, 3662306a36Sopenharmony_ci unsigned int length, int erdy, int zlp) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct cdns3_usb_regs __iomem *regs = priv_dev->regs; 3962306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci priv_ep->trb_pool[0].buffer = cpu_to_le32(TRB_BUFFER(dma_addr)); 4262306a36Sopenharmony_ci priv_ep->trb_pool[0].length = cpu_to_le32(TRB_LEN(length)); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (zlp) { 4562306a36Sopenharmony_ci priv_ep->trb_pool[0].control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_NORMAL)); 4662306a36Sopenharmony_ci priv_ep->trb_pool[1].buffer = cpu_to_le32(TRB_BUFFER(dma_addr)); 4762306a36Sopenharmony_ci priv_ep->trb_pool[1].length = cpu_to_le32(TRB_LEN(0)); 4862306a36Sopenharmony_ci priv_ep->trb_pool[1].control = cpu_to_le32(TRB_CYCLE | TRB_IOC | 4962306a36Sopenharmony_ci TRB_TYPE(TRB_NORMAL)); 5062306a36Sopenharmony_ci } else { 5162306a36Sopenharmony_ci priv_ep->trb_pool[0].control = cpu_to_le32(TRB_CYCLE | TRB_IOC | 5262306a36Sopenharmony_ci TRB_TYPE(TRB_NORMAL)); 5362306a36Sopenharmony_ci priv_ep->trb_pool[1].control = 0; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci writel(EP_STS_TRBERR, ®s->ep_sts); 6162306a36Sopenharmony_ci writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), ®s->ep_traddr); 6262306a36Sopenharmony_ci trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out", 6362306a36Sopenharmony_ci readl(®s->ep_traddr)); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* TRB should be prepared before starting transfer. */ 6662306a36Sopenharmony_ci writel(EP_CMD_DRDY, ®s->ep_cmd); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Resume controller before arming transfer. */ 6962306a36Sopenharmony_ci __cdns3_gadget_wakeup(priv_dev); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (erdy) 7262306a36Sopenharmony_ci writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * cdns3_ep0_delegate_req - Returns status of handling setup packet 7762306a36Sopenharmony_ci * Setup is handled by gadget driver 7862306a36Sopenharmony_ci * @priv_dev: extended gadget object 7962306a36Sopenharmony_ci * @ctrl_req: pointer to received setup packet 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * Returns zero on success or negative value on failure 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_cistatic int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev, 8462306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl_req) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_unlock(&priv_dev->lock); 8962306a36Sopenharmony_ci priv_dev->setup_pending = 1; 9062306a36Sopenharmony_ci ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req); 9162306a36Sopenharmony_ci priv_dev->setup_pending = 0; 9262306a36Sopenharmony_ci spin_lock(&priv_dev->lock); 9362306a36Sopenharmony_ci return ret; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci priv_dev->ep0_data_dir = 0; 9962306a36Sopenharmony_ci priv_dev->ep0_stage = CDNS3_SETUP_STAGE; 10062306a36Sopenharmony_ci cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 10162306a36Sopenharmony_ci sizeof(struct usb_ctrlrequest), 0, 0); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev, 10562306a36Sopenharmony_ci u8 send_stall, u8 send_erdy) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; 10862306a36Sopenharmony_ci struct usb_request *request; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci request = cdns3_next_request(&priv_ep->pending_req_list); 11162306a36Sopenharmony_ci if (request) 11262306a36Sopenharmony_ci list_del_init(&request->list); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (send_stall) { 11562306a36Sopenharmony_ci trace_cdns3_halt(priv_ep, send_stall, 0); 11662306a36Sopenharmony_ci /* set_stall on ep0 */ 11762306a36Sopenharmony_ci cdns3_select_ep(priv_dev, 0x00); 11862306a36Sopenharmony_ci writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd); 11962306a36Sopenharmony_ci } else { 12062306a36Sopenharmony_ci cdns3_prepare_setup_packet(priv_dev); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci priv_dev->ep0_stage = CDNS3_SETUP_STAGE; 12462306a36Sopenharmony_ci writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL, 12562306a36Sopenharmony_ci &priv_dev->regs->ep_cmd); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request 13062306a36Sopenharmony_ci * @priv_dev: extended gadget object 13162306a36Sopenharmony_ci * @ctrl_req: pointer to received setup packet 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage, 13462306a36Sopenharmony_ci * error code on error 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistatic int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, 13762306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl_req) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci enum usb_device_state device_state = priv_dev->gadget.state; 14062306a36Sopenharmony_ci u32 config = le16_to_cpu(ctrl_req->wValue); 14162306a36Sopenharmony_ci int result = 0; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci switch (device_state) { 14462306a36Sopenharmony_ci case USB_STATE_ADDRESS: 14562306a36Sopenharmony_ci result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (result || !config) 14862306a36Sopenharmony_ci goto reset_config; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci case USB_STATE_CONFIGURED: 15262306a36Sopenharmony_ci result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); 15362306a36Sopenharmony_ci if (!config && !result) 15462306a36Sopenharmony_ci goto reset_config; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci default: 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cireset_config: 16462306a36Sopenharmony_ci if (result != USB_GADGET_DELAYED_STATUS) 16562306a36Sopenharmony_ci cdns3_hw_reset_eps_config(priv_dev); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci usb_gadget_set_state(&priv_dev->gadget, 16862306a36Sopenharmony_ci USB_STATE_ADDRESS); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return result; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/** 17462306a36Sopenharmony_ci * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request 17562306a36Sopenharmony_ci * @priv_dev: extended gadget object 17662306a36Sopenharmony_ci * @ctrl_req: pointer to received setup packet 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Returns 0 if success, error code on error 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev, 18162306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl_req) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci enum usb_device_state device_state = priv_dev->gadget.state; 18462306a36Sopenharmony_ci u32 reg; 18562306a36Sopenharmony_ci u32 addr; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci addr = le16_to_cpu(ctrl_req->wValue); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (addr > USB_DEVICE_MAX_ADDRESS) { 19062306a36Sopenharmony_ci dev_err(priv_dev->dev, 19162306a36Sopenharmony_ci "Device address (%d) cannot be greater than %d\n", 19262306a36Sopenharmony_ci addr, USB_DEVICE_MAX_ADDRESS); 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (device_state == USB_STATE_CONFIGURED) { 19762306a36Sopenharmony_ci dev_err(priv_dev->dev, 19862306a36Sopenharmony_ci "can't set_address from configured state\n"); 19962306a36Sopenharmony_ci return -EINVAL; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci reg = readl(&priv_dev->regs->usb_cmd); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR, 20562306a36Sopenharmony_ci &priv_dev->regs->usb_cmd); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci usb_gadget_set_state(&priv_dev->gadget, 20862306a36Sopenharmony_ci (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request 21562306a36Sopenharmony_ci * @priv_dev: extended gadget object 21662306a36Sopenharmony_ci * @ctrl: pointer to received setup packet 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * Returns 0 if success, error code on error 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev, 22162306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep; 22462306a36Sopenharmony_ci __le16 *response_pkt; 22562306a36Sopenharmony_ci u16 usb_status = 0; 22662306a36Sopenharmony_ci u32 recip; 22762306a36Sopenharmony_ci u8 index; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci recip = ctrl->bRequestType & USB_RECIP_MASK; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci switch (recip) { 23262306a36Sopenharmony_ci case USB_RECIP_DEVICE: 23362306a36Sopenharmony_ci /* self powered */ 23462306a36Sopenharmony_ci if (priv_dev->is_selfpowered) 23562306a36Sopenharmony_ci usb_status = BIT(USB_DEVICE_SELF_POWERED); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (priv_dev->wake_up_flag) 23862306a36Sopenharmony_ci usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (priv_dev->gadget.speed != USB_SPEED_SUPER) 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (priv_dev->u1_allowed) 24462306a36Sopenharmony_ci usb_status |= BIT(USB_DEV_STAT_U1_ENABLED); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (priv_dev->u2_allowed) 24762306a36Sopenharmony_ci usb_status |= BIT(USB_DEV_STAT_U2_ENABLED); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case USB_RECIP_INTERFACE: 25162306a36Sopenharmony_ci return cdns3_ep0_delegate_req(priv_dev, ctrl); 25262306a36Sopenharmony_ci case USB_RECIP_ENDPOINT: 25362306a36Sopenharmony_ci index = cdns3_ep_addr_to_index(le16_to_cpu(ctrl->wIndex)); 25462306a36Sopenharmony_ci priv_ep = priv_dev->eps[index]; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* check if endpoint is stalled or stall is pending */ 25762306a36Sopenharmony_ci cdns3_select_ep(priv_dev, le16_to_cpu(ctrl->wIndex)); 25862306a36Sopenharmony_ci if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)) || 25962306a36Sopenharmony_ci (priv_ep->flags & EP_STALL_PENDING)) 26062306a36Sopenharmony_ci usb_status = BIT(USB_ENDPOINT_HALT); 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci default: 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci response_pkt = (__le16 *)priv_dev->setup_buf; 26762306a36Sopenharmony_ci *response_pkt = cpu_to_le16(usb_status); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 27062306a36Sopenharmony_ci sizeof(*response_pkt), 1, 0); 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev, 27562306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl, 27662306a36Sopenharmony_ci int set) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci enum usb_device_state state; 27962306a36Sopenharmony_ci enum usb_device_speed speed; 28062306a36Sopenharmony_ci int ret = 0; 28162306a36Sopenharmony_ci u32 wValue; 28262306a36Sopenharmony_ci u16 tmode; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci wValue = le16_to_cpu(ctrl->wValue); 28562306a36Sopenharmony_ci state = priv_dev->gadget.state; 28662306a36Sopenharmony_ci speed = priv_dev->gadget.speed; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci switch (wValue) { 28962306a36Sopenharmony_ci case USB_DEVICE_REMOTE_WAKEUP: 29062306a36Sopenharmony_ci priv_dev->wake_up_flag = !!set; 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci case USB_DEVICE_U1_ENABLE: 29362306a36Sopenharmony_ci if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci priv_dev->u1_allowed = !!set; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case USB_DEVICE_U2_ENABLE: 29962306a36Sopenharmony_ci if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER) 30062306a36Sopenharmony_ci return -EINVAL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci priv_dev->u2_allowed = !!set; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case USB_DEVICE_LTM_ENABLE: 30562306a36Sopenharmony_ci ret = -EINVAL; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case USB_DEVICE_TEST_MODE: 30862306a36Sopenharmony_ci if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH) 30962306a36Sopenharmony_ci return -EINVAL; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci tmode = le16_to_cpu(ctrl->wIndex); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!set || (tmode & 0xff) != 0) 31462306a36Sopenharmony_ci return -EINVAL; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci tmode >>= 8; 31762306a36Sopenharmony_ci switch (tmode) { 31862306a36Sopenharmony_ci case USB_TEST_J: 31962306a36Sopenharmony_ci case USB_TEST_K: 32062306a36Sopenharmony_ci case USB_TEST_SE0_NAK: 32162306a36Sopenharmony_ci case USB_TEST_PACKET: 32262306a36Sopenharmony_ci cdns3_set_register_bit(&priv_dev->regs->usb_cmd, 32362306a36Sopenharmony_ci USB_CMD_STMODE | 32462306a36Sopenharmony_ci USB_STS_TMODE_SEL(tmode - 1)); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci default: 32762306a36Sopenharmony_ci ret = -EINVAL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci ret = -EINVAL; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev, 33862306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl, 33962306a36Sopenharmony_ci int set) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci u32 wValue; 34262306a36Sopenharmony_ci int ret = 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci wValue = le16_to_cpu(ctrl->wValue); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci switch (wValue) { 34762306a36Sopenharmony_ci case USB_INTRF_FUNC_SUSPEND: 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci default: 35062306a36Sopenharmony_ci ret = -EINVAL; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return ret; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev, 35762306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl, 35862306a36Sopenharmony_ci int set) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep; 36162306a36Sopenharmony_ci int ret = 0; 36262306a36Sopenharmony_ci u8 index; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) 36562306a36Sopenharmony_ci return -EINVAL; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (!(le16_to_cpu(ctrl->wIndex) & ~USB_DIR_IN)) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci index = cdns3_ep_addr_to_index(le16_to_cpu(ctrl->wIndex)); 37162306a36Sopenharmony_ci priv_ep = priv_dev->eps[index]; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci cdns3_select_ep(priv_dev, le16_to_cpu(ctrl->wIndex)); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (set) 37662306a36Sopenharmony_ci __cdns3_gadget_ep_set_halt(priv_ep); 37762306a36Sopenharmony_ci else if (!(priv_ep->flags & EP_WEDGE)) 37862306a36Sopenharmony_ci ret = __cdns3_gadget_ep_clear_halt(priv_ep); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci cdns3_select_ep(priv_dev, 0x00); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * cdns3_req_ep0_handle_feature - 38762306a36Sopenharmony_ci * Handling of GET/SET_FEATURE standard USB request 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * @priv_dev: extended gadget object 39062306a36Sopenharmony_ci * @ctrl: pointer to received setup packet 39162306a36Sopenharmony_ci * @set: must be set to 1 for SET_FEATURE request 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * Returns 0 if success, error code on error 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev, 39662306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl, 39762306a36Sopenharmony_ci int set) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci int ret = 0; 40062306a36Sopenharmony_ci u32 recip; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci recip = ctrl->bRequestType & USB_RECIP_MASK; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci switch (recip) { 40562306a36Sopenharmony_ci case USB_RECIP_DEVICE: 40662306a36Sopenharmony_ci ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set); 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci case USB_RECIP_INTERFACE: 40962306a36Sopenharmony_ci ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set); 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci case USB_RECIP_ENDPOINT: 41262306a36Sopenharmony_ci ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci default: 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/** 42262306a36Sopenharmony_ci * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request 42362306a36Sopenharmony_ci * @priv_dev: extended gadget object 42462306a36Sopenharmony_ci * @ctrl_req: pointer to received setup packet 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Returns 0 if success, error code on error 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cistatic int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev, 42962306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl_req) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci if (priv_dev->gadget.state < USB_STATE_ADDRESS) 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (le16_to_cpu(ctrl_req->wLength) != 6) { 43562306a36Sopenharmony_ci dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n", 43662306a36Sopenharmony_ci ctrl_req->wLength); 43762306a36Sopenharmony_ci return -EINVAL; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0); 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/** 44562306a36Sopenharmony_ci * cdns3_req_ep0_set_isoch_delay - 44662306a36Sopenharmony_ci * Handling of GET_ISOCH_DELAY standard USB request 44762306a36Sopenharmony_ci * @priv_dev: extended gadget object 44862306a36Sopenharmony_ci * @ctrl_req: pointer to received setup packet 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * Returns 0 if success, error code on error 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_cistatic int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev, 45362306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl_req) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci if (ctrl_req->wIndex || ctrl_req->wLength) 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci priv_dev->isoch_delay = le16_to_cpu(ctrl_req->wValue); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * cdns3_ep0_standard_request - Handling standard USB requests 46562306a36Sopenharmony_ci * @priv_dev: extended gadget object 46662306a36Sopenharmony_ci * @ctrl_req: pointer to received setup packet 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * Returns 0 if success, error code on error 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_cistatic int cdns3_ep0_standard_request(struct cdns3_device *priv_dev, 47162306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl_req) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci switch (ctrl_req->bRequest) { 47662306a36Sopenharmony_ci case USB_REQ_SET_ADDRESS: 47762306a36Sopenharmony_ci ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req); 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci case USB_REQ_SET_CONFIGURATION: 48062306a36Sopenharmony_ci ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req); 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci case USB_REQ_GET_STATUS: 48362306a36Sopenharmony_ci ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req); 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci case USB_REQ_CLEAR_FEATURE: 48662306a36Sopenharmony_ci ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0); 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci case USB_REQ_SET_FEATURE: 48962306a36Sopenharmony_ci ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case USB_REQ_SET_SEL: 49262306a36Sopenharmony_ci ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req); 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci case USB_REQ_SET_ISOCH_DELAY: 49562306a36Sopenharmony_ci ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req); 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci default: 49862306a36Sopenharmony_ci ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req); 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return ret; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void __pending_setup_status_handler(struct cdns3_device *priv_dev) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct usb_request *request = priv_dev->pending_status_request; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (priv_dev->status_completion_no_call && request && 51062306a36Sopenharmony_ci request->complete) { 51162306a36Sopenharmony_ci request->complete(&priv_dev->eps[0]->endpoint, request); 51262306a36Sopenharmony_ci priv_dev->status_completion_no_call = 0; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_civoid cdns3_pending_setup_status_handler(struct work_struct *work) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct cdns3_device *priv_dev = container_of(work, struct cdns3_device, 51962306a36Sopenharmony_ci pending_status_wq); 52062306a36Sopenharmony_ci unsigned long flags; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci spin_lock_irqsave(&priv_dev->lock, flags); 52362306a36Sopenharmony_ci __pending_setup_status_handler(priv_dev); 52462306a36Sopenharmony_ci spin_unlock_irqrestore(&priv_dev->lock, flags); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * cdns3_ep0_setup_phase - Handling setup USB requests 52962306a36Sopenharmony_ci * @priv_dev: extended gadget object 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl = priv_dev->setup_buf; 53462306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; 53562306a36Sopenharmony_ci int result; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci trace_cdns3_ctrl_req(ctrl); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (!list_empty(&priv_ep->pending_req_list)) { 54262306a36Sopenharmony_ci struct usb_request *request; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci request = cdns3_next_request(&priv_ep->pending_req_list); 54562306a36Sopenharmony_ci priv_ep->dir = priv_dev->ep0_data_dir; 54662306a36Sopenharmony_ci cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 54762306a36Sopenharmony_ci -ECONNRESET); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (le16_to_cpu(ctrl->wLength)) 55162306a36Sopenharmony_ci priv_dev->ep0_stage = CDNS3_DATA_STAGE; 55262306a36Sopenharmony_ci else 55362306a36Sopenharmony_ci priv_dev->ep0_stage = CDNS3_STATUS_STAGE; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) 55662306a36Sopenharmony_ci result = cdns3_ep0_standard_request(priv_dev, ctrl); 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci result = cdns3_ep0_delegate_req(priv_dev, ctrl); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (result == USB_GADGET_DELAYED_STATUS) 56162306a36Sopenharmony_ci return; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (result < 0) 56462306a36Sopenharmony_ci cdns3_ep0_complete_setup(priv_dev, 1, 1); 56562306a36Sopenharmony_ci else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) 56662306a36Sopenharmony_ci cdns3_ep0_complete_setup(priv_dev, 0, 1); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void cdns3_transfer_completed(struct cdns3_device *priv_dev) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep = priv_dev->eps[0]; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (!list_empty(&priv_ep->pending_req_list)) { 57462306a36Sopenharmony_ci struct usb_request *request; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool); 57762306a36Sopenharmony_ci request = cdns3_next_request(&priv_ep->pending_req_list); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci request->actual = 58062306a36Sopenharmony_ci TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length)); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci priv_ep->dir = priv_dev->ep0_data_dir; 58362306a36Sopenharmony_ci cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci cdns3_ep0_complete_setup(priv_dev, 0, 0); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/** 59062306a36Sopenharmony_ci * cdns3_check_new_setup - Check if controller receive new SETUP packet. 59162306a36Sopenharmony_ci * @priv_dev: extended gadget object 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * The SETUP packet can be kept in on-chip memory or in system memory. 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_cistatic bool cdns3_check_new_setup(struct cdns3_device *priv_dev) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci u32 ep_sts_reg; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci cdns3_select_ep(priv_dev, USB_DIR_OUT); 60062306a36Sopenharmony_ci ep_sts_reg = readl(&priv_dev->regs->ep_sts); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT)); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/** 60662306a36Sopenharmony_ci * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0 60762306a36Sopenharmony_ci * @priv_dev: extended gadget object 60862306a36Sopenharmony_ci * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_civoid cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci u32 ep_sts_reg; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci cdns3_select_ep(priv_dev, dir); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ep_sts_reg = readl(&priv_dev->regs->ep_sts); 61762306a36Sopenharmony_ci writel(ep_sts_reg, &priv_dev->regs->ep_sts); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci trace_cdns3_ep0_irq(priv_dev, ep_sts_reg); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci __pending_setup_status_handler(priv_dev); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (ep_sts_reg & EP_STS_SETUP) 62462306a36Sopenharmony_ci priv_dev->wait_for_setup = 1; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) { 62762306a36Sopenharmony_ci priv_dev->wait_for_setup = 0; 62862306a36Sopenharmony_ci cdns3_ep0_setup_phase(priv_dev); 62962306a36Sopenharmony_ci } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) { 63062306a36Sopenharmony_ci priv_dev->ep0_data_dir = dir; 63162306a36Sopenharmony_ci cdns3_transfer_completed(priv_dev); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (ep_sts_reg & EP_STS_DESCMIS) { 63562306a36Sopenharmony_ci if (dir == 0 && !priv_dev->setup_pending) 63662306a36Sopenharmony_ci cdns3_prepare_setup_packet(priv_dev); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci/** 64162306a36Sopenharmony_ci * cdns3_gadget_ep0_enable 64262306a36Sopenharmony_ci * @ep: pointer to endpoint zero object 64362306a36Sopenharmony_ci * @desc: pointer to usb endpoint descriptor 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * Function shouldn't be called by gadget driver, 64662306a36Sopenharmony_ci * endpoint 0 is allways active 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic int cdns3_gadget_ep0_enable(struct usb_ep *ep, 64962306a36Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci/** 65562306a36Sopenharmony_ci * cdns3_gadget_ep0_disable 65662306a36Sopenharmony_ci * @ep: pointer to endpoint zero object 65762306a36Sopenharmony_ci * 65862306a36Sopenharmony_ci * Function shouldn't be called by gadget driver, 65962306a36Sopenharmony_ci * endpoint 0 is allways active 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_cistatic int cdns3_gadget_ep0_disable(struct usb_ep *ep) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci return -EINVAL; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/** 66762306a36Sopenharmony_ci * cdns3_gadget_ep0_set_halt 66862306a36Sopenharmony_ci * @ep: pointer to endpoint zero object 66962306a36Sopenharmony_ci * @value: 1 for set stall, 0 for clear stall 67062306a36Sopenharmony_ci * 67162306a36Sopenharmony_ci * Returns 0 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_cistatic int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci /* TODO */ 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/** 68062306a36Sopenharmony_ci * cdns3_gadget_ep0_queue - Transfer data on endpoint zero 68162306a36Sopenharmony_ci * @ep: pointer to endpoint zero object 68262306a36Sopenharmony_ci * @request: pointer to request object 68362306a36Sopenharmony_ci * @gfp_flags: gfp flags 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * Returns 0 on success, error code elsewhere 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_cistatic int cdns3_gadget_ep0_queue(struct usb_ep *ep, 68862306a36Sopenharmony_ci struct usb_request *request, 68962306a36Sopenharmony_ci gfp_t gfp_flags) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); 69262306a36Sopenharmony_ci struct cdns3_device *priv_dev = priv_ep->cdns3_dev; 69362306a36Sopenharmony_ci unsigned long flags; 69462306a36Sopenharmony_ci int ret = 0; 69562306a36Sopenharmony_ci u8 zlp = 0; 69662306a36Sopenharmony_ci int i; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci spin_lock_irqsave(&priv_dev->lock, flags); 69962306a36Sopenharmony_ci trace_cdns3_ep0_queue(priv_dev, request); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* cancel the request if controller receive new SETUP packet. */ 70262306a36Sopenharmony_ci if (cdns3_check_new_setup(priv_dev)) { 70362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv_dev->lock, flags); 70462306a36Sopenharmony_ci return -ECONNRESET; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* send STATUS stage. Should be called only for SET_CONFIGURATION */ 70862306a36Sopenharmony_ci if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) { 70962306a36Sopenharmony_ci u32 val; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci cdns3_select_ep(priv_dev, 0x00); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* 71462306a36Sopenharmony_ci * Configure all non-control EPs which are not enabled by class driver 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { 71762306a36Sopenharmony_ci priv_ep = priv_dev->eps[i]; 71862306a36Sopenharmony_ci if (priv_ep && priv_ep->flags & EP_CLAIMED && 71962306a36Sopenharmony_ci !(priv_ep->flags & EP_ENABLED)) 72062306a36Sopenharmony_ci cdns3_ep_config(priv_ep, 0); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci cdns3_set_hw_configuration(priv_dev); 72462306a36Sopenharmony_ci cdns3_ep0_complete_setup(priv_dev, 0, 1); 72562306a36Sopenharmony_ci /* wait until configuration set */ 72662306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val, 72762306a36Sopenharmony_ci val & USB_STS_CFGSTS_MASK, 1, 100); 72862306a36Sopenharmony_ci if (ret == -ETIMEDOUT) 72962306a36Sopenharmony_ci dev_warn(priv_dev->dev, "timeout for waiting configuration set\n"); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci request->actual = 0; 73262306a36Sopenharmony_ci priv_dev->status_completion_no_call = true; 73362306a36Sopenharmony_ci priv_dev->pending_status_request = request; 73462306a36Sopenharmony_ci usb_gadget_set_state(&priv_dev->gadget, USB_STATE_CONFIGURED); 73562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv_dev->lock, flags); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* 73862306a36Sopenharmony_ci * Since there is no completion interrupt for status stage, 73962306a36Sopenharmony_ci * it needs to call ->completion in software after 74062306a36Sopenharmony_ci * ep0_queue is back. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci queue_work(system_freezable_wq, &priv_dev->pending_status_wq); 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (!list_empty(&priv_ep->pending_req_list)) { 74762306a36Sopenharmony_ci dev_err(priv_dev->dev, 74862306a36Sopenharmony_ci "can't handle multiple requests for ep0\n"); 74962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv_dev->lock, flags); 75062306a36Sopenharmony_ci return -EBUSY; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, 75462306a36Sopenharmony_ci priv_dev->ep0_data_dir); 75562306a36Sopenharmony_ci if (ret) { 75662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv_dev->lock, flags); 75762306a36Sopenharmony_ci dev_err(priv_dev->dev, "failed to map request\n"); 75862306a36Sopenharmony_ci return -EINVAL; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci request->status = -EINPROGRESS; 76262306a36Sopenharmony_ci list_add_tail(&request->list, &priv_ep->pending_req_list); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (request->zero && request->length && 76562306a36Sopenharmony_ci (request->length % ep->maxpacket == 0)) 76662306a36Sopenharmony_ci zlp = 1; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv_dev->lock, flags); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/** 77662306a36Sopenharmony_ci * cdns3_gadget_ep_set_wedge - Set wedge on selected endpoint 77762306a36Sopenharmony_ci * @ep: endpoint object 77862306a36Sopenharmony_ci * 77962306a36Sopenharmony_ci * Returns 0 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ciint cdns3_gadget_ep_set_wedge(struct usb_ep *ep) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); 78462306a36Sopenharmony_ci struct cdns3_device *priv_dev = priv_ep->cdns3_dev; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name); 78762306a36Sopenharmony_ci cdns3_gadget_ep_set_halt(ep, 1); 78862306a36Sopenharmony_ci priv_ep->flags |= EP_WEDGE; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic const struct usb_ep_ops cdns3_gadget_ep0_ops = { 79462306a36Sopenharmony_ci .enable = cdns3_gadget_ep0_enable, 79562306a36Sopenharmony_ci .disable = cdns3_gadget_ep0_disable, 79662306a36Sopenharmony_ci .alloc_request = cdns3_gadget_ep_alloc_request, 79762306a36Sopenharmony_ci .free_request = cdns3_gadget_ep_free_request, 79862306a36Sopenharmony_ci .queue = cdns3_gadget_ep0_queue, 79962306a36Sopenharmony_ci .dequeue = cdns3_gadget_ep_dequeue, 80062306a36Sopenharmony_ci .set_halt = cdns3_gadget_ep0_set_halt, 80162306a36Sopenharmony_ci .set_wedge = cdns3_gadget_ep_set_wedge, 80262306a36Sopenharmony_ci}; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci/** 80562306a36Sopenharmony_ci * cdns3_ep0_config - Configures default endpoint 80662306a36Sopenharmony_ci * @priv_dev: extended gadget object 80762306a36Sopenharmony_ci * 80862306a36Sopenharmony_ci * Functions sets parameters: maximal packet size and enables interrupts 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_civoid cdns3_ep0_config(struct cdns3_device *priv_dev) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct cdns3_usb_regs __iomem *regs; 81362306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep; 81462306a36Sopenharmony_ci u32 max_packet_size = 64; 81562306a36Sopenharmony_ci u32 ep_cfg; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci regs = priv_dev->regs; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (priv_dev->gadget.speed == USB_SPEED_SUPER) 82062306a36Sopenharmony_ci max_packet_size = 512; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci priv_ep = priv_dev->eps[0]; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (!list_empty(&priv_ep->pending_req_list)) { 82562306a36Sopenharmony_ci struct usb_request *request; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci request = cdns3_next_request(&priv_ep->pending_req_list); 82862306a36Sopenharmony_ci list_del_init(&request->list); 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci priv_dev->u1_allowed = 0; 83262306a36Sopenharmony_ci priv_dev->u2_allowed = 0; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci priv_dev->gadget.ep0->maxpacket = max_packet_size; 83562306a36Sopenharmony_ci cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* init ep out */ 83862306a36Sopenharmony_ci cdns3_select_ep(priv_dev, USB_DIR_OUT); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (priv_dev->dev_ver >= DEV_VER_V3) { 84162306a36Sopenharmony_ci cdns3_set_register_bit(&priv_dev->regs->dtrans, 84262306a36Sopenharmony_ci BIT(0) | BIT(16)); 84362306a36Sopenharmony_ci cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb, 84462306a36Sopenharmony_ci BIT(0) | BIT(16)); 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci ep_cfg = EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (!(priv_ep->flags & EP_CONFIGURED)) 85062306a36Sopenharmony_ci writel(ep_cfg, ®s->ep_cfg); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN, 85362306a36Sopenharmony_ci ®s->ep_sts_en); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* init ep in */ 85662306a36Sopenharmony_ci cdns3_select_ep(priv_dev, USB_DIR_IN); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (!(priv_ep->flags & EP_CONFIGURED)) 85962306a36Sopenharmony_ci writel(ep_cfg, ®s->ep_cfg); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci priv_ep->flags |= EP_CONFIGURED; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci cdns3_set_register_bit(®s->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci/** 86962306a36Sopenharmony_ci * cdns3_init_ep0 - Initializes software endpoint 0 of gadget 87062306a36Sopenharmony_ci * @priv_dev: extended gadget object 87162306a36Sopenharmony_ci * @priv_ep: extended endpoint object 87262306a36Sopenharmony_ci * 87362306a36Sopenharmony_ci * Returns 0 on success else error code. 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_ciint cdns3_init_ep0(struct cdns3_device *priv_dev, 87662306a36Sopenharmony_ci struct cdns3_endpoint *priv_ep) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci sprintf(priv_ep->name, "ep0"); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* fill linux fields */ 88162306a36Sopenharmony_ci priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops; 88262306a36Sopenharmony_ci priv_ep->endpoint.maxburst = 1; 88362306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&priv_ep->endpoint, 88462306a36Sopenharmony_ci CDNS3_EP0_MAX_PACKET_LIMIT); 88562306a36Sopenharmony_ci priv_ep->endpoint.address = 0; 88662306a36Sopenharmony_ci priv_ep->endpoint.caps.type_control = 1; 88762306a36Sopenharmony_ci priv_ep->endpoint.caps.dir_in = 1; 88862306a36Sopenharmony_ci priv_ep->endpoint.caps.dir_out = 1; 88962306a36Sopenharmony_ci priv_ep->endpoint.name = priv_ep->name; 89062306a36Sopenharmony_ci priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc; 89162306a36Sopenharmony_ci priv_dev->gadget.ep0 = &priv_ep->endpoint; 89262306a36Sopenharmony_ci priv_ep->type = USB_ENDPOINT_XFER_CONTROL; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return cdns3_allocate_trb_pool(priv_ep); 89562306a36Sopenharmony_ci} 896