/* ========================================================================== * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless * otherwise expressly agreed to in writing between Synopsys and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with Synopsys or any supplement thereto. You are permitted to use and * redistribute this Software in source and binary forms, with or without * modification, provided that redistributions of source code must retain this * notice. You may not view, use, disclose, copy or distribute this file or * any information contained herein except pursuant to this license grant from * Synopsys. If you do not agree with this notice, including the disclaimer * below, then you are not authorized to use the Software. * * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * ========================================================================== */ /** @file * * This file contains the most of the CFI(Core Feature Interface) * implementation for the OTG. */ #ifdef DWC_UTE_CFI #include "dwc_otg_pcd.h" #include "dwc_otg_cfi.h" /** This definition should actually migrate to the Portability Library */ #define DWC_CONSTANT_CPU_TO_LE16(x) (x) extern dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t *pcd, u16 wIndex); static int cfi_core_features_buf(uint8_t *buf, uint16_t buflen); static int cfi_get_feature_value(uint8_t *buf, uint16_t buflen, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl_req); static int cfi_set_feature_value(struct dwc_otg_pcd *pcd); static int cfi_ep_get_sg_val(uint8_t *buf, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req); static int cfi_ep_get_concat_val(uint8_t *buf, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req); static int cfi_ep_get_align_val(uint8_t *buf, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req); static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req); static void cfi_free_ep_bs_dyn_data(cfi_ep_t *cfiep); static uint16_t get_dfifo_size(dwc_otg_core_if_t *core_if); static int32_t get_rxfifo_size(dwc_otg_core_if_t *core_if, uint16_t wValue); static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue); static uint8_t resize_fifos(dwc_otg_core_if_t *core_if); /** This is the header of the all features descriptor */ static cfi_all_features_header_t all_props_desc_header = { .wVersion = DWC_CONSTANT_CPU_TO_LE16(0x100), .wCoreID = DWC_CONSTANT_CPU_TO_LE16(CFI_CORE_ID_OTG), .wNumFeatures = DWC_CONSTANT_CPU_TO_LE16(9), }; /** This is an array of statically allocated feature descriptors */ static cfi_feature_desc_header_t prop_descs[] = { /* FT_ID_DMA_MODE */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_MODE), .bmAttributes = CFI_FEATURE_ATTR_RW, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(1), }, /* FT_ID_DMA_BUFFER_SETUP */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFFER_SETUP), .bmAttributes = CFI_FEATURE_ATTR_RW, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), }, /* FT_ID_DMA_BUFF_ALIGN */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFF_ALIGN), .bmAttributes = CFI_FEATURE_ATTR_RW, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), }, /* FT_ID_DMA_CONCAT_SETUP */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CONCAT_SETUP), .bmAttributes = CFI_FEATURE_ATTR_RW, /* .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), */ }, /* FT_ID_DMA_CIRCULAR */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CIRCULAR), .bmAttributes = CFI_FEATURE_ATTR_RW, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), }, /* FT_ID_THRESHOLD_SETUP */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_THRESHOLD_SETUP), .bmAttributes = CFI_FEATURE_ATTR_RW, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), }, /* FT_ID_DFIFO_DEPTH */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DFIFO_DEPTH), .bmAttributes = CFI_FEATURE_ATTR_RO, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), }, /* FT_ID_TX_FIFO_DEPTH */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_TX_FIFO_DEPTH), .bmAttributes = CFI_FEATURE_ATTR_RW, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), }, /* FT_ID_RX_FIFO_DEPTH */ { .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_RX_FIFO_DEPTH), .bmAttributes = CFI_FEATURE_ATTR_RW, .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), } }; /** The table of feature names */ cfi_string_t prop_name_table[] = { {FT_ID_DMA_MODE, "dma_mode"}, {FT_ID_DMA_BUFFER_SETUP, "buffer_setup"}, {FT_ID_DMA_BUFF_ALIGN, "buffer_align"}, {FT_ID_DMA_CONCAT_SETUP, "concat_setup"}, {FT_ID_DMA_CIRCULAR, "buffer_circular"}, {FT_ID_THRESHOLD_SETUP, "threshold_setup"}, {FT_ID_DFIFO_DEPTH, "dfifo_depth"}, {FT_ID_TX_FIFO_DEPTH, "txfifo_depth"}, {FT_ID_RX_FIFO_DEPTH, "rxfifo_depth"}, {} }; /************************************************************************/ /** * Returns the name of the feature by its ID * or NULL if no featute ID matches. * */ const uint8_t *get_prop_name(uint16_t prop_id, int *len) { cfi_string_t *pstr; *len = 0; for (pstr = prop_name_table; pstr && pstr->s; pstr++) { if (pstr->id == prop_id) { *len = DWC_STRLEN(pstr->s); return pstr->s; } } return NULL; } /** * This function handles all CFI specific control requests. * * Return a negative value to stall the DCE. */ int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl) { int retval = 0; dwc_otg_pcd_ep_t *ep = NULL; cfiobject_t *cfi = pcd->cfi; struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); uint16_t wLen = DWC_LE16_TO_CPU(&ctrl->wLength); uint16_t wValue = DWC_LE16_TO_CPU(&ctrl->wValue); uint16_t wIndex = DWC_LE16_TO_CPU(&ctrl->wIndex); uint32_t regaddr = 0; uint32_t regval = 0; /* Save this Control Request in the CFI object. * The data field will be assigned in the data * stage completion CB function. */ cfi->ctrl_req = *ctrl; cfi->ctrl_req.data = NULL; cfi->need_gadget_att = 0; cfi->need_status_in_complete = 0; switch (ctrl->bRequest) { case VEN_CORE_GET_FEATURES: retval = cfi_core_features_buf(cfi->buf_in.buf, CFI_IN_BUF_LEN); if (retval >= 0) { /* dump_msg(cfi->buf_in.buf, retval); */ ep = &pcd->ep0; retval = min((uint16_t) retval, wLen); /* Transfer this buffer to the host * through the EP0-IN EP */ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ep->dwc_ep.xfer_len = retval; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; pcd->ep0_pending = 1; dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); } retval = 0; break; case VEN_CORE_GET_FEATURE: CFI_INFO("VEN_CORE_GET_FEATURE\n"); retval = cfi_get_feature_value(cfi->buf_in.buf, CFI_IN_BUF_LEN, pcd, ctrl); if (retval >= 0) { ep = &pcd->ep0; retval = min((uint16_t) retval, wLen); /* Transfer this buffer to the host * through the EP0-IN EP */ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ep->dwc_ep.xfer_len = retval; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; pcd->ep0_pending = 1; dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); } CFI_INFO("VEN_CORE_GET_FEATURE=%d\n", retval); dump_msg(cfi->buf_in.buf, retval); break; case VEN_CORE_SET_FEATURE: CFI_INFO("VEN_CORE_SET_FEATURE\n"); /* Set up an XFER to get the data stage of the control request, * which is the new value of the feature to be modified. */ ep = &pcd->ep0; ep->dwc_ep.is_in = 0; ep->dwc_ep.dma_addr = cfi->buf_out.addr; ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; ep->dwc_ep.xfer_buff = cfi->buf_out.buf; ep->dwc_ep.xfer_len = wLen; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; pcd->ep0_pending = 1; /* Read the control write's data stage */ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); retval = 0; break; case VEN_CORE_RESET_FEATURES: CFI_INFO("VEN_CORE_RESET_FEATURES\n"); cfi->need_gadget_att = 1; cfi->need_status_in_complete = 1; retval = cfi_preproc_reset(pcd, ctrl); CFI_INFO("VEN_CORE_RESET_FEATURES = (%d)\n", retval); break; case VEN_CORE_ACTIVATE_FEATURES: CFI_INFO("VEN_CORE_ACTIVATE_FEATURES\n"); break; case VEN_CORE_READ_REGISTER: CFI_INFO("VEN_CORE_READ_REGISTER\n"); /* wValue optionally contains the HI WORD of * the register offset and wIndex contains * the LOW WORD of the register offset */ if (wValue == 0) { /* @TODO - MAS - fix the access to the base field */ regaddr = 0; /* regaddr = (uint32_t) pcd->otg_dev->os_dep.base; */ /* GET_CORE_IF(pcd)->co */ regaddr |= wIndex; } else { regaddr = (wValue << 16) | wIndex; } /* Read a 32-bit value of the memory at the regaddr */ regval = DWC_READ_REG32((uint32_t *) regaddr); ep = &pcd->ep0; dwc_memcpy(cfi->buf_in.buf, ®val, sizeof(uint32_t)); ep->dwc_ep.is_in = 1; ep->dwc_ep.dma_addr = cfi->buf_in.addr; ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ep->dwc_ep.xfer_len = wLen; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; pcd->ep0_pending = 1; dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); cfi->need_gadget_att = 0; retval = 0; break; case VEN_CORE_WRITE_REGISTER: CFI_INFO("VEN_CORE_WRITE_REGISTER\n"); /* Set up an XFER to get the data stage of the control request, * which is the new value of the register to be modified. */ ep = &pcd->ep0; ep->dwc_ep.is_in = 0; ep->dwc_ep.dma_addr = cfi->buf_out.addr; ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; ep->dwc_ep.xfer_buff = cfi->buf_out.buf; ep->dwc_ep.xfer_len = wLen; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; pcd->ep0_pending = 1; /* Read the control write's data stage */ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); retval = 0; break; default: retval = -DWC_E_NOT_SUPPORTED; break; } return retval; } /** * This function prepares the core features descriptors and copies its * raw representation into the buffer . * * The buffer structure is as follows: * all_features_header (8 bytes) * features_#1 (8 bytes + feature name string length) * features_#2 (8 bytes + feature name string length) * ..... * features_#n - where n=the total count of feature descriptors */ static int cfi_core_features_buf(uint8_t *buf, uint16_t buflen) { cfi_feature_desc_header_t *prop_hdr = prop_descs; cfi_feature_desc_header_t *prop; cfi_all_features_header_t *all_props_hdr = &all_props_desc_header; cfi_all_features_header_t *tmp; uint8_t *tmpbuf = buf; const uint8_t *pname = NULL; int i, j, namelen = 0, totlen; /* Prepare and copy the core features into the buffer */ CFI_INFO("%s:\n", __func__); tmp = (cfi_all_features_header_t *) tmpbuf; *tmp = *all_props_hdr; tmpbuf += CFI_ALL_FEATURES_HDR_LEN; j = sizeof(prop_descs) / sizeof(cfi_all_features_header_t); for (i = 0; i < j; i++, prop_hdr++) { pname = get_prop_name(prop_hdr->wFeatureID, &namelen); prop = (cfi_feature_desc_header_t *) tmpbuf; *prop = *prop_hdr; prop->bNameLen = namelen; prop->wLength = DWC_CONSTANT_CPU_TO_LE16(CFI_FEATURE_DESC_HDR_LEN + namelen); tmpbuf += CFI_FEATURE_DESC_HDR_LEN; dwc_memcpy(tmpbuf, pname, namelen); tmpbuf += namelen; } totlen = tmpbuf - buf; if (totlen > 0) { tmp = (cfi_all_features_header_t *) buf; tmp->wTotalLen = DWC_CONSTANT_CPU_TO_LE16(totlen); } return totlen; } /** * This function releases all the dynamic memory in the CFI object. */ static void cfi_release(cfiobject_t *cfiobj) { cfi_ep_t *cfiep; dwc_list_link_t *tmp; CFI_INFO("%s\n", __func__); if (cfiobj->buf_in.buf) { DWC_DEV_DMA_FREE(CFI_IN_BUF_LEN, cfiobj->buf_in.buf, cfiobj->buf_in.addr); cfiobj->buf_in.buf = NULL; } if (cfiobj->buf_out.buf) { DWC_DEV_DMA_FREE(CFI_OUT_BUF_LEN, cfiobj->buf_out.buf, cfiobj->buf_out.addr); cfiobj->buf_out.buf = NULL; } /* Free the Buffer Setup values for each EP */ /* list_for_each_entry(cfiep, &cfiobj->active_eps, lh) { */ DWC_LIST_FOREACH(tmp, &cfiobj->active_eps) { cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); cfi_free_ep_bs_dyn_data(cfiep); } } /** * This function frees the dynamically allocated EP buffer setup data. */ static void cfi_free_ep_bs_dyn_data(cfi_ep_t *cfiep) { if (cfiep->bm_sg) { DWC_FREE(cfiep->bm_sg); cfiep->bm_sg = NULL; } if (cfiep->bm_align) { DWC_FREE(cfiep->bm_align); cfiep->bm_align = NULL; } if (cfiep->bm_concat) { if (NULL != cfiep->bm_concat->wTxBytes) { DWC_FREE(cfiep->bm_concat->wTxBytes); cfiep->bm_concat->wTxBytes = NULL; } DWC_FREE(cfiep->bm_concat); cfiep->bm_concat = NULL; } } /** * This function initializes the default values of the features * for a specific endpoint and should be called only once when * the EP is enabled first time. */ static int cfi_ep_init_defaults(struct dwc_otg_pcd *pcd, cfi_ep_t *cfiep) { int retval = 0; cfiep->bm_sg = DWC_ALLOC(sizeof(ddma_sg_buffer_setup_t)); if (NULL == cfiep->bm_sg) { CFI_INFO("Failed to allocate memory for SG feature value\n"); return -DWC_E_NO_MEMORY; } dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); /* For the Concatenation feature's default value we do not allocate * memory for the wTxBytes field - it will be done in the * set_feature_value request handler. */ cfiep->bm_concat = DWC_ALLOC(sizeof(ddma_concat_buffer_setup_t)); if (NULL == cfiep->bm_concat) { CFI_INFO ("Failed to allocate memory for CONCATENATION feature value\n"); DWC_FREE(cfiep->bm_sg); return -DWC_E_NO_MEMORY; } dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); cfiep->bm_align = DWC_ALLOC(sizeof(ddma_align_buffer_setup_t)); if (NULL == cfiep->bm_align) { CFI_INFO ("Failed to allocate memory for Alignment feature value\n"); DWC_FREE(cfiep->bm_sg); DWC_FREE(cfiep->bm_concat); return -DWC_E_NO_MEMORY; } dwc_memset(cfiep->bm_align, 0, sizeof(ddma_align_buffer_setup_t)); return retval; } /** * The callback function that notifies the CFI on the activation of * an endpoint in the PCD. The following steps are done in this function: * * Create a dynamically allocated cfi_ep_t object (a CFI wrapper to the PCD's * active endpoint) * Create MAX_DMA_DESCS_PER_EP count DMA Descriptors for the EP * Set the Buffer Mode to standard * Initialize the default values for all EP modes (SG, Circular, Concat, Align) * Add the cfi_ep_t object to the list of active endpoints in the CFI object */ static int cfi_ep_enable(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, struct dwc_otg_pcd_ep *ep) { cfi_ep_t *cfiep; int retval = -DWC_E_NOT_SUPPORTED; CFI_INFO("%s: epname=%s; epnum=0x%02x\n", __func__, "EP_" /*ep->ep.name */ , ep->desc->bEndpointAddress); /* MAS - Check whether this endpoint already is in the list */ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); if (NULL == cfiep) { /* Allocate a cfi_ep_t object */ cfiep = DWC_ALLOC(sizeof(cfi_ep_t)); if (NULL == cfiep) { CFI_INFO ("Unable to allocate memory for in function %s\n", __func__); return -DWC_E_NO_MEMORY; } dwc_memset(cfiep, 0, sizeof(cfi_ep_t)); /* Save the dwc_otg_pcd_ep pointer in the cfiep object */ cfiep->ep = ep; /* Allocate the DMA Descriptors chain of * MAX_DMA_DESCS_PER_EP count */ ep->dwc_ep.descs = DWC_DMA_ALLOC(MAX_DMA_DESCS_PER_EP * sizeof(dwc_otg_dma_desc_t), &ep->dwc_ep.descs_dma_addr); if (NULL == ep->dwc_ep.descs) { DWC_FREE(cfiep); return -DWC_E_NO_MEMORY; } DWC_LIST_INIT(&cfiep->lh); /* Set the buffer mode to BM_STANDARD. It will be modified * when building descriptors for a specific buffer mode */ ep->dwc_ep.buff_mode = BM_STANDARD; /* Create and initialize the default values * for this EP's Buffer modes */ retval = cfi_ep_init_defaults(pcd, cfiep); if (retval < 0) return retval; /* Add the cfi_ep_t object to the CFI object's * list of active endpoints */ DWC_LIST_INSERT_TAIL(&cfi->active_eps, &cfiep->lh); retval = 0; } else { /* The sought EP already is in the list */ CFI_INFO("%s: The sought EP already is in the list\n", __func__); } return retval; } /** * This function is called when the data stage of a 3-stage Control Write request * is complete. * */ static int cfi_ctrl_write_complete(struct cfiobject *cfi, struct dwc_otg_pcd *pcd) { uint32_t addr, reg_value; uint16_t wIndex, wValue; uint8_t bRequest; uint8_t *buf = cfi->buf_out.buf; /* struct usb_ctrlrequest *ctrl_req = &cfi->ctrl_req_saved; */ struct cfi_usb_ctrlrequest *ctrl_req = &cfi->ctrl_req; int retval = -DWC_E_NOT_SUPPORTED; CFI_INFO("%s\n", __func__); bRequest = ctrl_req->bRequest; wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); /* * Save the pointer to the data stage in the ctrl_req's field. * The request should be already saved in the command stage by now. */ ctrl_req->data = cfi->buf_out.buf; cfi->need_status_in_complete = 0; cfi->need_gadget_att = 0; switch (bRequest) { case VEN_CORE_WRITE_REGISTER: /* The buffer contains raw data of * the new value for the register */ reg_value = *((uint32_t *) buf); if (wValue == 0) { addr = 0; /* addr = (uint32_t) pcd->otg_dev->os_dep.base; */ addr += wIndex; } else { addr = (wValue << 16) | wIndex; } /* writel(reg_value, addr); */ retval = 0; cfi->need_status_in_complete = 1; break; case VEN_CORE_SET_FEATURE: /* The buffer contains raw data of * the new value of the feature */ retval = cfi_set_feature_value(pcd); if (retval < 0) return retval; cfi->need_status_in_complete = 1; break; default: break; } return retval; } /** * This function builds the DMA descriptors for the SG buffer mode. */ static void cfi_build_sg_descs(struct cfiobject *cfi, cfi_ep_t *cfiep, dwc_otg_pcd_request_t *req) { struct dwc_otg_pcd_ep *ep = cfiep->ep; ddma_sg_buffer_setup_t *sgval = cfiep->bm_sg; struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; dma_addr_t buff_addr = req->dma; int i; uint32_t txsize, off; txsize = sgval->wSize; off = sgval->bOffset; /* CFI_INFO("%s: %s TXSIZE=0x%08x; OFFSET=0x%08x\n", __func__, cfiep->ep->ep.name, txsize, off);*/ for (i = 0; i < sgval->bCount; i++) { desc->status.b.bs = BS_HOST_BUSY; desc->buf = buff_addr; desc->status.b.l = 0; desc->status.b.ioc = 0; desc->status.b.sp = 0; desc->status.b.bytes = txsize; desc->status.b.bs = BS_HOST_READY; /* Set the next address of the buffer */ buff_addr += txsize + off; desc_last = desc; desc++; } /* Set the last, ioc and sp bits on the Last DMA Descriptor */ desc_last->status.b.l = 1; desc_last->status.b.ioc = 1; desc_last->status.b.sp = ep->dwc_ep.sent_zlp; /* Save the last DMA descriptor pointer */ cfiep->dma_desc_last = desc_last; cfiep->desc_count = sgval->bCount; } /** * This function builds the DMA descriptors for the Concatenation buffer mode. */ static void cfi_build_concat_descs(struct cfiobject *cfi, cfi_ep_t *cfiep, dwc_otg_pcd_request_t *req) { struct dwc_otg_pcd_ep *ep = cfiep->ep; ddma_concat_buffer_setup_t *concatval = cfiep->bm_concat; struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; dma_addr_t buff_addr = req->dma; int i; uint16_t *txsize; txsize = concatval->wTxBytes; for (i = 0; i < concatval->hdr.bDescCount; i++) { desc->buf = buff_addr; desc->status.b.bs = BS_HOST_BUSY; desc->status.b.l = 0; desc->status.b.ioc = 0; desc->status.b.sp = 0; desc->status.b.bytes = *txsize; desc->status.b.bs = BS_HOST_READY; txsize++; /* Set the next address of the buffer */ buff_addr += UGETW(ep->desc->wMaxPacketSize); desc_last = desc; desc++; } /* Set the last, ioc and sp bits on the Last DMA Descriptor */ desc_last->status.b.l = 1; desc_last->status.b.ioc = 1; desc_last->status.b.sp = ep->dwc_ep.sent_zlp; cfiep->dma_desc_last = desc_last; cfiep->desc_count = concatval->hdr.bDescCount; } /** * This function builds the DMA descriptors for the Circular buffer mode */ static void cfi_build_circ_descs(struct cfiobject *cfi, cfi_ep_t *cfiep, dwc_otg_pcd_request_t *req) { /* @todo: MAS - add implementation when * this feature needs to be tested */ } /** * This function builds the DMA descriptors for the Alignment buffer mode */ static void cfi_build_align_descs(struct cfiobject *cfi, cfi_ep_t *cfiep, dwc_otg_pcd_request_t *req) { struct dwc_otg_pcd_ep *ep = cfiep->ep; ddma_align_buffer_setup_t *alignval = cfiep->bm_align; struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; dma_addr_t buff_addr = req->dma; desc->status.b.bs = BS_HOST_BUSY; desc->status.b.l = 1; desc->status.b.ioc = 1; desc->status.b.sp = ep->dwc_ep.sent_zlp; desc->status.b.bytes = req->length; /* Adjust the buffer alignment */ desc->buf = (buff_addr + alignval->bAlign); desc->status.b.bs = BS_HOST_READY; cfiep->dma_desc_last = desc; cfiep->desc_count = 1; } /** * This function builds the DMA descriptors chain for different modes of the * buffer setup of an endpoint. */ static void cfi_build_descriptors(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, struct dwc_otg_pcd_ep *ep, dwc_otg_pcd_request_t *req) { cfi_ep_t *cfiep; /* Get the cfiep by the dwc_otg_pcd_ep */ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); if (NULL == cfiep) { CFI_INFO("%s: Unable to find a matching active endpoint\n", __func__); return; } cfiep->xfer_len = req->length; /* Iterate through all the DMA descriptors */ switch (cfiep->ep->dwc_ep.buff_mode) { case BM_SG: cfi_build_sg_descs(cfi, cfiep, req); break; case BM_CONCAT: cfi_build_concat_descs(cfi, cfiep, req); break; case BM_CIRCULAR: cfi_build_circ_descs(cfi, cfiep, req); break; case BM_ALIGN: cfi_build_align_descs(cfi, cfiep, req); break; default: break; } } /** * Allocate DMA buffer for different Buffer modes. */ static void *cfi_ep_alloc_buf(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, struct dwc_otg_pcd_ep *ep, dma_addr_t *dma, unsigned size, gfp_t flags) { return DWC_DMA_ALLOC(size, dma); } /** * This function initializes the CFI object. */ int init_cfi(cfiobject_t *cfiobj) { CFI_INFO("%s\n", __func__); /* Allocate a buffer for IN XFERs */ cfiobj->buf_in.buf = DWC_DMA_ALLOC(CFI_IN_BUF_LEN, &cfiobj->buf_in.addr); if (NULL == cfiobj->buf_in.buf) { CFI_INFO("Unable to allocate buffer for INs\n"); return -DWC_E_NO_MEMORY; } /* Allocate a buffer for OUT XFERs */ cfiobj->buf_out.buf = DWC_DMA_ALLOC(CFI_OUT_BUF_LEN, &cfiobj->buf_out.addr); if (NULL == cfiobj->buf_out.buf) { CFI_INFO("Unable to allocate buffer for OUT\n"); return -DWC_E_NO_MEMORY; } /* Initialize the callback function pointers */ cfiobj->ops.release = cfi_release; cfiobj->ops.ep_enable = cfi_ep_enable; cfiobj->ops.ctrl_write_complete = cfi_ctrl_write_complete; cfiobj->ops.build_descriptors = cfi_build_descriptors; cfiobj->ops.ep_alloc_buf = cfi_ep_alloc_buf; /* Initialize the list of active endpoints in the CFI object */ DWC_LIST_INIT(&cfiobj->active_eps); return 0; } /** * This function reads the required feature's current value into the buffer * * @retval: Returns negative as error, or the data length of the feature */ static int cfi_get_feature_value(uint8_t *buf, uint16_t buflen, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl_req) { int retval = -DWC_E_NOT_SUPPORTED; struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); uint16_t dfifo, rxfifo, txfifo; switch (ctrl_req->wIndex) { /* Whether the DDMA is enabled or not */ case FT_ID_DMA_MODE: *buf = (coreif->dma_enable && coreif->dma_desc_enable) ? 1 : 0; retval = 1; break; case FT_ID_DMA_BUFFER_SETUP: retval = cfi_ep_get_sg_val(buf, pcd, ctrl_req); break; case FT_ID_DMA_BUFF_ALIGN: retval = cfi_ep_get_align_val(buf, pcd, ctrl_req); break; case FT_ID_DMA_CONCAT_SETUP: retval = cfi_ep_get_concat_val(buf, pcd, ctrl_req); break; case FT_ID_DMA_CIRCULAR: CFI_INFO("GetFeature value (FT_ID_DMA_CIRCULAR)\n"); break; case FT_ID_THRESHOLD_SETUP: CFI_INFO("GetFeature value (FT_ID_THRESHOLD_SETUP)\n"); break; case FT_ID_DFIFO_DEPTH: dfifo = get_dfifo_size(coreif); *((uint16_t *) buf) = dfifo; retval = sizeof(uint16_t); break; case FT_ID_TX_FIFO_DEPTH: retval = get_txfifo_size(pcd, ctrl_req->wValue); if (retval >= 0) { txfifo = retval; *((uint16_t *) buf) = txfifo; retval = sizeof(uint16_t); } break; case FT_ID_RX_FIFO_DEPTH: retval = get_rxfifo_size(coreif, ctrl_req->wValue); if (retval >= 0) { rxfifo = retval; *((uint16_t *) buf) = rxfifo; retval = sizeof(uint16_t); } break; } return retval; } /** * This function resets the SG for the specified EP to its default value */ static int cfi_reset_sg_val(cfi_ep_t *cfiep) { dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); return 0; } /** * This function resets the Alignment for the specified EP to its default value */ static int cfi_reset_align_val(cfi_ep_t *cfiep) { dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); return 0; } /** * This function resets the Concatenation for the specified EP to its default value * This function will also set the value of the wTxBytes field to NULL after * freeing the memory previously allocated for this field. */ static int cfi_reset_concat_val(cfi_ep_t *cfiep) { /* First we need to free the wTxBytes field */ if (cfiep->bm_concat->wTxBytes) { DWC_FREE(cfiep->bm_concat->wTxBytes); cfiep->bm_concat->wTxBytes = NULL; } dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); return 0; } /** * This function resets all the buffer setups of the specified endpoint */ static int cfi_ep_reset_all_setup_vals(cfi_ep_t *cfiep) { cfi_reset_sg_val(cfiep); cfi_reset_align_val(cfiep); cfi_reset_concat_val(cfiep); return 0; } static int cfi_handle_reset_fifo_val(struct dwc_otg_pcd *pcd, uint8_t ep_addr, uint8_t rx_rst, uint8_t tx_rst) { int retval = -DWC_E_INVALID; uint16_t tx_siz[15]; uint16_t rx_siz = 0; dwc_otg_pcd_ep_t *ep = NULL; dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; if (rx_rst) { rx_siz = params->dev_rx_fifo_size; params->dev_rx_fifo_size = GET_CORE_IF(pcd)->init_rxfsiz; } if (tx_rst) { if (ep_addr == 0) { int i; for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { tx_siz[i] = core_if->core_params->dev_tx_fifo_size[i]; core_if->core_params->dev_tx_fifo_size[i] = core_if->init_txfsiz[i]; } } else { ep = get_ep_by_addr(pcd, ep_addr); if (NULL == ep) { CFI_INFO ("%s: Unable to get the endpoint addr=0x%02x\n", __func__, ep_addr); return -DWC_E_INVALID; } tx_siz[0] = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1]; params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = GET_CORE_IF(pcd)->init_txfsiz[ep->dwc_ep. tx_fifo_num - 1]; } } if (resize_fifos(GET_CORE_IF(pcd))) { retval = 0; } else { CFI_INFO ("%s: Error resetting the feature Reset All(FIFO size)\n", __func__); if (rx_rst) params->dev_rx_fifo_size = rx_siz; if (tx_rst) { if (ep_addr == 0) { int i; for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { core_if->core_params-> dev_tx_fifo_size[i] = tx_siz[i]; } } else { params->dev_tx_fifo_size[ep->dwc_ep. tx_fifo_num - 1] = tx_siz[0]; } } retval = -DWC_E_INVALID; } return retval; } static int cfi_handle_reset_all(struct dwc_otg_pcd *pcd, uint8_t addr) { int retval = 0; cfi_ep_t *cfiep; cfiobject_t *cfi = pcd->cfi; dwc_list_link_t *tmp; retval = cfi_handle_reset_fifo_val(pcd, addr, 1, 1); if (retval < 0) return retval; /* If the EP address is known then * reset the features for only that EP */ if (addr) { cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == cfiep) { CFI_INFO("%s: Error getting the EP address 0x%02x\n", __func__, addr); return -DWC_E_INVALID; } retval = cfi_ep_reset_all_setup_vals(cfiep); cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; } /* Otherwise (wValue == 0), reset all features of all EP's */ else { /* Traverse all the active EP's and * reset the feature(s) value(s) */ /* list_for_each_entry(cfiep, &cfi->active_eps, lh) { */ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); retval = cfi_ep_reset_all_setup_vals(cfiep); cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; if (retval < 0) { CFI_INFO ("%s: Error resetting the feature Reset All\n", __func__); return retval; } } } return retval; } static int cfi_handle_reset_dma_buff_setup(struct dwc_otg_pcd *pcd, uint8_t addr) { int retval = 0; cfi_ep_t *cfiep; cfiobject_t *cfi = pcd->cfi; dwc_list_link_t *tmp; /* If the EP address is known then reset * the features for only that EP */ if (addr) { cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == cfiep) { CFI_INFO("%s: Error getting the EP address 0x%02x\n", __func__, addr); return -DWC_E_INVALID; } retval = cfi_reset_sg_val(cfiep); } /* Otherwise (wValue == 0), reset all features of all EP's */ else { /* Traverse all the active EP's and * reset the feature(s) value(s) */ /* list_for_each_entry(cfiep, &cfi->active_eps, lh) { */ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); retval = cfi_reset_sg_val(cfiep); if (retval < 0) { CFI_INFO ("%s: Error resetting the feature Buffer Setup\n", __func__); return retval; } } } return retval; } static int cfi_handle_reset_concat_val(struct dwc_otg_pcd *pcd, uint8_t addr) { int retval = 0; cfi_ep_t *cfiep; cfiobject_t *cfi = pcd->cfi; dwc_list_link_t *tmp; /* If the EP address is known then * reset the features for only that EP */ if (addr) { cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == cfiep) { CFI_INFO("%s: Error getting the EP address 0x%02x\n", __func__, addr); return -DWC_E_INVALID; } retval = cfi_reset_concat_val(cfiep); } /* Otherwise (wValue == 0), reset all features of all EP's */ else { /* Traverse all the active EP's and * reset the feature(s) value(s) */ /* list_for_each_entry(cfiep, &cfi->active_eps, lh) { */ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); retval = cfi_reset_concat_val(cfiep); if (retval < 0) { CFI_INFO ("%s: Error resetting the feature Concatenation Value\n", __func__); return retval; } } } return retval; } static int cfi_handle_reset_align_val(struct dwc_otg_pcd *pcd, uint8_t addr) { int retval = 0; cfi_ep_t *cfiep; cfiobject_t *cfi = pcd->cfi; dwc_list_link_t *tmp; /* If the EP address is known then reset the features for only that EP */ if (addr) { cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == cfiep) { CFI_INFO("%s: Error getting the EP address 0x%02x\n", __func__, addr); return -DWC_E_INVALID; } retval = cfi_reset_align_val(cfiep); } /* Otherwise (wValue == 0), reset all features of all EP's */ else { /* Traverse all the active EP's and reset the feature(s) value(s) */ /* list_for_each_entry(cfiep, &cfi->active_eps, lh) { */ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); retval = cfi_reset_align_val(cfiep); if (retval < 0) { CFI_INFO ("%s: Error resetting the feature Aliignment Value\n", __func__); return retval; } } } return retval; } static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req) { int retval = 0; switch (req->wIndex) { case 0: /* Reset all features */ retval = cfi_handle_reset_all(pcd, req->wValue & 0xff); break; case FT_ID_DMA_BUFFER_SETUP: /* Reset the SG buffer setup */ retval = cfi_handle_reset_dma_buff_setup(pcd, req->wValue & 0xff); break; case FT_ID_DMA_CONCAT_SETUP: /* Reset the Concatenation buffer setup */ retval = cfi_handle_reset_concat_val(pcd, req->wValue & 0xff); break; case FT_ID_DMA_BUFF_ALIGN: /* Reset the Alignment buffer setup */ retval = cfi_handle_reset_align_val(pcd, req->wValue & 0xff); break; case FT_ID_TX_FIFO_DEPTH: retval = cfi_handle_reset_fifo_val(pcd, req->wValue & 0xff, 0, 1); pcd->cfi->need_gadget_att = 0; break; case FT_ID_RX_FIFO_DEPTH: retval = cfi_handle_reset_fifo_val(pcd, 0, 1, 0); pcd->cfi->need_gadget_att = 0; break; default: break; } return retval; } /** * This function sets a new value for the SG buffer setup. */ static int cfi_ep_set_sg_val(uint8_t *buf, struct dwc_otg_pcd *pcd) { uint8_t inaddr, outaddr; cfi_ep_t *epin, *epout; ddma_sg_buffer_setup_t *psgval; uint32_t desccount, size; CFI_INFO("%s\n", __func__); psgval = (ddma_sg_buffer_setup_t *) buf; desccount = (uint32_t) psgval->bCount; size = (uint32_t) psgval->wSize; /* Check the DMA descriptor count */ if ((desccount > MAX_DMA_DESCS_PER_EP) || (desccount == 0)) { CFI_INFO ("%s: The count of DMA Descriptors should be between 1 and %d\n", __func__, MAX_DMA_DESCS_PER_EP); return -DWC_E_INVALID; } /* Check the DMA descriptor count */ if (size == 0) { CFI_INFO("%s: The transfer size should be at least 1 byte\n", __func__); return -DWC_E_INVALID; } inaddr = psgval->bInEndpointAddress; outaddr = psgval->bOutEndpointAddress; epin = get_cfi_ep_by_addr(pcd->cfi, inaddr); epout = get_cfi_ep_by_addr(pcd->cfi, outaddr); if (NULL == epin || NULL == epout) { CFI_INFO ("%s: Unable to get the endpoints inaddr=0x%02x outaddr=0x%02x\n", __func__, inaddr, outaddr); return -DWC_E_INVALID; } epin->ep->dwc_ep.buff_mode = BM_SG; dwc_memcpy(epin->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); epout->ep->dwc_ep.buff_mode = BM_SG; dwc_memcpy(epout->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); return 0; } /** * This function sets a new value for the buffer Alignment setup. */ static int cfi_ep_set_alignment_val(uint8_t *buf, struct dwc_otg_pcd *pcd) { cfi_ep_t *ep; uint8_t addr; ddma_align_buffer_setup_t *palignval; palignval = (ddma_align_buffer_setup_t *) buf; addr = palignval->bEndpointAddress; ep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == ep) { CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", __func__, addr); return -DWC_E_INVALID; } ep->ep->dwc_ep.buff_mode = BM_ALIGN; dwc_memcpy(ep->bm_align, palignval, sizeof(ddma_align_buffer_setup_t)); return 0; } /** * This function sets a new value for the Concatenation buffer setup. */ static int cfi_ep_set_concat_val(uint8_t *buf, struct dwc_otg_pcd *pcd) { uint8_t addr; cfi_ep_t *ep; struct _ddma_concat_buffer_setup_hdr *pConcatValHdr; uint16_t *pVals; uint32_t desccount; int i; uint16_t mps; pConcatValHdr = (struct _ddma_concat_buffer_setup_hdr *)buf; desccount = (uint32_t) pConcatValHdr->bDescCount; pVals = (uint16_t *) (buf + BS_CONCAT_VAL_HDR_LEN); /* Check the DMA descriptor count */ if (desccount > MAX_DMA_DESCS_PER_EP) { CFI_INFO("%s: Maximum DMA Descriptor count should be %d\n", __func__, MAX_DMA_DESCS_PER_EP); return -DWC_E_INVALID; } addr = pConcatValHdr->bEndpointAddress; ep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == ep) { CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", __func__, addr); return -DWC_E_INVALID; } mps = UGETW(ep->ep->desc->wMaxPacketSize); #if 0 for (i = 0; i < desccount; i++) { CFI_INFO("%s: wTxSize[%d]=0x%04x\n", __func__, i, pVals[i]); } CFI_INFO("%s: epname=%s; mps=%d\n", __func__, ep->ep->ep.name, mps); #endif /* Check the wTxSizes to be less than or equal to the mps */ for (i = 0; i < desccount; i++) { if (pVals[i] > mps) { CFI_INFO ("%s: ERROR - the wTxSize[%d] should be <= MPS (wTxSize=%d)\n", __func__, i, pVals[i]); return -DWC_E_INVALID; } } ep->ep->dwc_ep.buff_mode = BM_CONCAT; dwc_memcpy(ep->bm_concat, pConcatValHdr, BS_CONCAT_VAL_HDR_LEN); /* Free the previously allocated storage for the wTxBytes */ if (ep->bm_concat->wTxBytes) { DWC_FREE(ep->bm_concat->wTxBytes); } /* Allocate a new storage for the wTxBytes field */ ep->bm_concat->wTxBytes = DWC_ALLOC(sizeof(uint16_t) * pConcatValHdr->bDescCount); if (NULL == ep->bm_concat->wTxBytes) { CFI_INFO("%s: Unable to allocate memory\n", __func__); return -DWC_E_NO_MEMORY; } /* Copy the new values into the wTxBytes filed */ dwc_memcpy(ep->bm_concat->wTxBytes, buf + BS_CONCAT_VAL_HDR_LEN, sizeof(uint16_t) * pConcatValHdr->bDescCount); return 0; } /** * This function calculates the total of all FIFO sizes * * @param core_if Programming view of DWC_otg controller * * @return The total of data FIFO sizes. * */ static uint16_t get_dfifo_size(dwc_otg_core_if_t *core_if) { dwc_otg_core_params_t *params = core_if->core_params; uint16_t dfifo_total = 0; int i; /* The shared RxFIFO size */ dfifo_total = params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; /* Add up each TxFIFO size to the total */ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) dfifo_total += params->dev_tx_fifo_size[i]; return dfifo_total; } /** * This function returns Rx FIFO size * * @param core_if Programming view of DWC_otg controller * * @return The total of data FIFO sizes. * */ static int32_t get_rxfifo_size(dwc_otg_core_if_t *core_if, uint16_t wValue) { switch (wValue >> 8) { case 0: return (core_if->pwron_rxfsiz < 32768) ? core_if->pwron_rxfsiz : 32768; break; case 1: return core_if->core_params->dev_rx_fifo_size; break; default: return -DWC_E_INVALID; break; } } /** * This function returns Tx FIFO size for IN EP * * @param core_if Programming view of DWC_otg controller * * @return The total of data FIFO sizes. * */ static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue) { dwc_otg_pcd_ep_t *ep; ep = get_ep_by_addr(pcd, wValue & 0xff); if (NULL == ep) { CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", __func__, wValue & 0xff); return -DWC_E_INVALID; } if (!ep->dwc_ep.is_in) { CFI_INFO ("%s: No Tx FIFO assingned to the Out endpoint addr=0x%02x\n", __func__, wValue & 0xff); return -DWC_E_INVALID; } switch (wValue >> 8) { case 0: return (GET_CORE_IF(pcd)->pwron_txfsiz [ep->dwc_ep.tx_fifo_num - 1] < 768) ? GET_CORE_IF(pcd)->pwron_txfsiz[ep->dwc_ep. tx_fifo_num - 1] : 32768; break; case 1: return GET_CORE_IF(pcd)->core_params->dev_tx_fifo_size[ep-> dwc_ep. num - 1]; break; default: return -DWC_E_INVALID; break; } } /** * This function checks if the submitted combination of * device mode FIFO sizes is possible or not. * * @param core_if Programming view of DWC_otg controller * * @return 1 if possible, 0 otherwise. * */ static uint8_t check_fifo_sizes(dwc_otg_core_if_t *core_if) { uint16_t dfifo_actual = 0; dwc_otg_core_params_t *params = core_if->core_params; uint16_t start_addr = 0; int i; dfifo_actual = params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) dfifo_actual += params->dev_tx_fifo_size[i]; if (dfifo_actual > core_if->total_fifo_size) return 0; if (params->dev_rx_fifo_size > 32768 || params->dev_rx_fifo_size < 16) return 0; if (params->dev_nperio_tx_fifo_size > 32768 || params->dev_nperio_tx_fifo_size < 16) return 0; for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { if (params->dev_tx_fifo_size[i] > 768 || params->dev_tx_fifo_size[i] < 4) return 0; } if (params->dev_rx_fifo_size > core_if->pwron_rxfsiz) return 0; start_addr = params->dev_rx_fifo_size; if (params->dev_nperio_tx_fifo_size > core_if->pwron_gnptxfsiz) return 0; start_addr += params->dev_nperio_tx_fifo_size; for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { if (params->dev_tx_fifo_size[i] > core_if->pwron_txfsiz[i]) return 0; start_addr += params->dev_tx_fifo_size[i]; } return 1; } /** * This function resizes Device mode FIFOs * * @param core_if Programming view of DWC_otg controller * * @return 1 if successful, 0 otherwise * */ static uint8_t resize_fifos(dwc_otg_core_if_t *core_if) { int i = 0; dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; dwc_otg_core_params_t *params = core_if->core_params; uint32_t rx_fifo_size; fifosize_data_t nptxfifosize; fifosize_data_t txfifosize[15]; uint32_t rx_fsz_bak; uint32_t nptxfsz_bak; uint32_t txfsz_bak[15]; uint16_t start_address; uint8_t retval = 1; if (!check_fifo_sizes(core_if)) return 0; /* Configure data FIFO sizes */ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { rx_fsz_bak = DWC_READ_REG32(&global_regs->grxfsiz); rx_fifo_size = params->dev_rx_fifo_size; DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size); /* * Tx FIFOs These FIFOs are numbered from 1 to 15. * Indexes of the FIFO size module parameters in the * dev_tx_fifo_size array and the FIFO size registers in * the dtxfsiz array run from 0 to 14. */ /* Non-periodic Tx FIFO */ nptxfsz_bak = DWC_READ_REG32(&global_regs->gnptxfsiz); nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; start_address = params->dev_rx_fifo_size; nptxfifosize.b.startaddr = start_address; DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32); start_address += nptxfifosize.b.depth; for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { txfsz_bak[i] = DWC_READ_REG32(&global_regs->dtxfsiz[i]); txfifosize[i].b.depth = params->dev_tx_fifo_size[i]; txfifosize[i].b.startaddr = start_address; DWC_WRITE_REG32(&global_regs->dtxfsiz[i], txfifosize[i].d32); start_address += txfifosize[i].b.depth; } /** Check if register values are set correctly */ if (rx_fifo_size != DWC_READ_REG32(&global_regs->grxfsiz)) retval = 0; if (nptxfifosize.d32 != DWC_READ_REG32(&global_regs->gnptxfsiz)) retval = 0; for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { if (txfifosize[i].d32 != DWC_READ_REG32(&global_regs->dtxfsiz[i])) { retval = 0; } } /** If register values are not set correctly, reset old values */ if (retval == 0) { DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fsz_bak); /* Non-periodic Tx FIFO */ DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfsz_bak); for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { DWC_WRITE_REG32(&global_regs->dtxfsiz[i], txfsz_bak[i]); } } } else { return 0; } /* Flush the FIFOs */ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ dwc_otg_flush_rx_fifo(core_if); return retval; } /** * This function sets a new value for the buffer Alignment setup. */ static int cfi_ep_set_tx_fifo_val(uint8_t *buf, dwc_otg_pcd_t *pcd) { int retval; uint32_t fsiz; uint16_t size; uint16_t ep_addr; dwc_otg_pcd_ep_t *ep; dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; tx_fifo_size_setup_t *ptxfifoval; ptxfifoval = (tx_fifo_size_setup_t *) buf; ep_addr = ptxfifoval->bEndpointAddress; size = ptxfifoval->wDepth; ep = get_ep_by_addr(pcd, ep_addr); CFI_INFO ("%s: Set Tx FIFO size: endpoint addr=0x%02x, depth=%d, FIFO Num=%d\n", __func__, ep_addr, size, ep->dwc_ep.tx_fifo_num); if (NULL == ep) { CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", __func__, ep_addr); return -DWC_E_INVALID; } fsiz = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1]; params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = size; if (resize_fifos(GET_CORE_IF(pcd))) { retval = 0; } else { CFI_INFO ("%s: Error setting the feature Tx FIFO Size for EP%d\n", __func__, ep_addr); params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = fsiz; retval = -DWC_E_INVALID; } return retval; } /** * This function sets a new value for the buffer Alignment setup. */ static int cfi_set_rx_fifo_val(uint8_t *buf, dwc_otg_pcd_t *pcd) { int retval; uint32_t fsiz; uint16_t size; dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; rx_fifo_size_setup_t *prxfifoval; prxfifoval = (rx_fifo_size_setup_t *) buf; size = prxfifoval->wDepth; fsiz = params->dev_rx_fifo_size; params->dev_rx_fifo_size = size; if (resize_fifos(GET_CORE_IF(pcd))) { retval = 0; } else { CFI_INFO("%s: Error setting the feature Rx FIFO Size\n", __func__); params->dev_rx_fifo_size = fsiz; retval = -DWC_E_INVALID; } return retval; } /** * This function reads the SG of an EP's buffer setup into the buffer buf */ static int cfi_ep_get_sg_val(uint8_t *buf, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req) { int retval = -DWC_E_INVALID; uint8_t addr; cfi_ep_t *ep; /* The Low Byte of the wValue contains * a non-zero address of the endpoint */ addr = req->wValue & 0xFF; if (addr == 0) /* The address should be non-zero */ return retval; ep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == ep) { CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", __func__, addr); return retval; } dwc_memcpy(buf, ep->bm_sg, BS_SG_VAL_DESC_LEN); retval = BS_SG_VAL_DESC_LEN; return retval; } /** * This function reads the Concatenation value of an EP's buffer mode into * the buffer buf */ static int cfi_ep_get_concat_val(uint8_t *buf, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req) { int retval = -DWC_E_INVALID; uint8_t addr; cfi_ep_t *ep; uint8_t desc_count; /* The Low Byte of the wValue contains * a non-zero address of the endpoint */ addr = req->wValue & 0xFF; if (addr == 0) /* The address should be non-zero */ return retval; ep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == ep) { CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", __func__, addr); return retval; } /* Copy the header to the buffer */ dwc_memcpy(buf, ep->bm_concat, BS_CONCAT_VAL_HDR_LEN); /* Advance the buffer pointer by the header size */ buf += BS_CONCAT_VAL_HDR_LEN; desc_count = ep->bm_concat->hdr.bDescCount; /* Copy alll the wTxBytes to the buffer */ dwc_memcpy(buf, ep->bm_concat->wTxBytes, sizeof(uid16_t) * desc_count); retval = BS_CONCAT_VAL_HDR_LEN + sizeof(uid16_t) * desc_count; return retval; } /** * This function reads the buffer Alignment value of an EP's buffer mode into * the buffer buf * * @return The total number of bytes copied to the buffer or negative error code. */ static int cfi_ep_get_align_val(uint8_t *buf, struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *req) { int retval = -DWC_E_INVALID; uint8_t addr; cfi_ep_t *ep; /* The Low Byte of the wValue contains a non-zero address of the endpoint */ addr = req->wValue & 0xFF; if (addr == 0) /* The address should be non-zero */ return retval; ep = get_cfi_ep_by_addr(pcd->cfi, addr); if (NULL == ep) { CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", __func__, addr); return retval; } dwc_memcpy(buf, ep->bm_align, BS_ALIGN_VAL_HDR_LEN); retval = BS_ALIGN_VAL_HDR_LEN; return retval; } /** * This function sets a new value for the specified feature * * @param pcd A pointer to the PCD object * * @return 0 if successful, negative error code otherwise to stall the DCE. */ static int cfi_set_feature_value(struct dwc_otg_pcd *pcd) { int retval = -DWC_E_NOT_SUPPORTED; uint16_t wIndex, wValue; uint8_t bRequest; struct dwc_otg_core_if *coreif; cfiobject_t *cfi = pcd->cfi; struct cfi_usb_ctrlrequest *ctrl_req; uint8_t *buf; ctrl_req = &cfi->ctrl_req; buf = pcd->cfi->ctrl_req.data; coreif = GET_CORE_IF(pcd); bRequest = ctrl_req->bRequest; wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); /* See which feature is to be modified */ switch (wIndex) { case FT_ID_DMA_BUFFER_SETUP: /* Modify the feature */ retval = cfi_ep_set_sg_val(buf, pcd); if (retval < 0) return retval; /* And send this request to the gadget */ cfi->need_gadget_att = 1; break; case FT_ID_DMA_BUFF_ALIGN: retval = cfi_ep_set_alignment_val(buf, pcd); if (retval < 0) return retval; cfi->need_gadget_att = 1; break; case FT_ID_DMA_CONCAT_SETUP: /* Modify the feature */ retval = cfi_ep_set_concat_val(buf, pcd); if (retval < 0) return retval; cfi->need_gadget_att = 1; break; case FT_ID_DMA_CIRCULAR: CFI_INFO("FT_ID_DMA_CIRCULAR\n"); break; case FT_ID_THRESHOLD_SETUP: CFI_INFO("FT_ID_THRESHOLD_SETUP\n"); break; case FT_ID_DFIFO_DEPTH: CFI_INFO("FT_ID_DFIFO_DEPTH\n"); break; case FT_ID_TX_FIFO_DEPTH: CFI_INFO("FT_ID_TX_FIFO_DEPTH\n"); retval = cfi_ep_set_tx_fifo_val(buf, pcd); if (retval < 0) return retval; cfi->need_gadget_att = 0; break; case FT_ID_RX_FIFO_DEPTH: CFI_INFO("FT_ID_RX_FIFO_DEPTH\n"); retval = cfi_set_rx_fifo_val(buf, pcd); if (retval < 0) return retval; cfi->need_gadget_att = 0; break; } return retval; } #endif /* DWC_UTE_CFI */