mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-01-31 15:22:18 +00:00
1877 lines
49 KiB
C
1877 lines
49 KiB
C
|
/* ==========================================================================
|
||
|
* 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 <buf>.
|
||
|
*
|
||
|
* 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_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_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 <cfiep> 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 */
|
||
|
if ((retval = cfi_ep_init_defaults(pcd, cfiep)) < 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 <data> 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 */
|
||
|
if ((retval = cfi_ep_set_sg_val(buf, pcd)) < 0)
|
||
|
return retval;
|
||
|
|
||
|
/* And send this request to the gadget */
|
||
|
cfi->need_gadget_att = 1;
|
||
|
break;
|
||
|
|
||
|
case FT_ID_DMA_BUFF_ALIGN:
|
||
|
if ((retval = cfi_ep_set_alignment_val(buf, pcd)) < 0)
|
||
|
return retval;
|
||
|
cfi->need_gadget_att = 1;
|
||
|
break;
|
||
|
|
||
|
case FT_ID_DMA_CONCAT_SETUP:
|
||
|
/* Modify the feature */
|
||
|
if ((retval = cfi_ep_set_concat_val(buf, pcd)) < 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");
|
||
|
if ((retval = cfi_ep_set_tx_fifo_val(buf, pcd)) < 0)
|
||
|
return retval;
|
||
|
cfi->need_gadget_att = 0;
|
||
|
break;
|
||
|
|
||
|
case FT_ID_RX_FIFO_DEPTH:
|
||
|
CFI_INFO("FT_ID_RX_FIFO_DEPTH\n");
|
||
|
if ((retval = cfi_set_rx_fifo_val(buf, pcd)) < 0)
|
||
|
return retval;
|
||
|
cfi->need_gadget_att = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
#endif //DWC_UTE_CFI
|