Default setting of InfinityX TxFIFOs for each Endpoints
IN EP |
TxFIFO size(bytes) |
---|---|
EP1 | 8336 |
EP2 | 8336 |
EP3 | 2096 |
EP4 | 2096 |
EP5 | 2096 |
EP6 | 3136 |
EP7 | 3136 |
According to DWC USB3 databook 2.4.2 Flexible IN Endpoint TxFIFO Allocation :
The USB device design requires each IN endpoint to have its own TxFIFO for data prefetch to meet USB turnaround time.
In your application, if you have six IN endpoints, and at a given time only four are active,
you can choose four TxFIFOs during coreConsultant configuration and software can map the four TxFIFOs to four currently active endpoints.
DWC 規定一個 USB 3.0 裝置的設計對 RAM 的需求為:
其中 RAM 可以為 SoC 提供的 SRAM 空間
對於 TxFIFO 的要求為:
MDWIDTH 定義為(單位為 byte):
根據 TxFIFO 公式,以下程式碼計算一個 IN EP 所需要的大小:
/**
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
* @dwc: pointer to the DWC3 context
* @nfifos: number of fifos to calculate for
*
* Calculates the size value based on the equation below:
*
* DWC3 revision 280A and prior:
* fifo_size = mult * (max_packet / mdwidth) + 1;
*
* DWC3 revision 290A and onwards:
* fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
*
* The max packet size is set to 1024, as the txfifo requirements mainly apply
* to super speed USB use cases. However, it is safe to overestimate the fifo
* allocations for other scenarios, i.e. high speed USB.
*/
static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
{
int max_packet = 1024;
int fifo_size;
int mdwidth;
mdwidth = dwc3_mdwidth(dwc);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth >>= 3;
if (DWC3_VER_IS_PRIOR(DWC3, 290A))
fifo_size = mult * (max_packet / mdwidth) + 1;
else
fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1;
return fifo_size;
}
根據上述程式碼,InfinityX TxFIFOs 配置情況如下:
IN EP |
TxFIFO size(bytes) | Packets |
---|---|---|
EP1 | 8 | |
EP2 | 8336 | 8 |
EP3 | 2 | |
EP4 | 2096 | 2 |
EP5 | 2096 | 2 |
EP6 | 3 | |
EP7 | 3136 | 3 |
若不修改 TxFIFO 的預設值,只有 EP1, EP2, EP6 與 EP7 可以支援 Burst (Up to 3 pakcets)
透過適當分配 TxFIFO,可達到每個 IN EP 都可以支援 Burst 如下:
IN EP |
TxFIFO size(bytes) | Packets |
---|---|---|
EP1 | 4 | |
EP2 | 4176 | 4 |
EP3 | 4176 | 4 |
EP4 | 4176 | 4 |
EP5 | 4176 | 4 |
EP6 | 4176 | 4 |
EP7 | 4176 | 4 |
透過以下程式碼,第 47 行的位置,若 IN EP 作為 Isochronous Transfer,則 TxFIFO 一律配置 3 個 packets 以上:
/*
* dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
* @dwc: pointer to our context structure
*
* This function will a best effort FIFO allocation in order
* to improve FIFO usage and throughput, while still allowing
* us to enable as many endpoints as possible.
*
* Keep in mind that this operation will be highly dependent
* on the configured size for RAM1 - which contains TxFifo -,
* the amount of endpoints enabled on coreConsultant tool, and
* the width of the Master Bus.
*
* In general, FIFO depths are represented with the following equation:
*
* fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
*
* In conjunction with dwc3_gadget_check_config(), this resizing logic will
* ensure that all endpoints will have enough internal memory for one max
* packet per endpoint.
*/
static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
int fifo_0_start;
int ram1_depth;
int fifo_size;
int min_depth;
int num_in_ep;
int remaining;
int num_fifos = 1;
int fifo;
int tmp;
if (!dwc->do_fifo_resize)
return 0;
/* resize IN endpoints except ep0 */
if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
return 0;
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
if ((dep->endpoint.maxburst > 1 &&
usb_endpoint_xfer_bulk(dep->endpoint.desc)) ||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
num_fifos = 3;
if (dep->endpoint.maxburst > 6 &&
usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31))
num_fifos = dwc->tx_fifo_resize_max_num;
/* FIFO size for a single buffer */
fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);
/* Calculate the number of remaining EPs w/o any FIFO */
num_in_ep = dwc->max_cfg_eps;
num_in_ep -= dwc->num_ep_resized;
/* Reserve at least one FIFO for the number of IN EPs */
min_depth = num_in_ep * (fifo + 1);
remaining = ram1_depth - min_depth - dwc->last_fifo_depth;
remaining = max_t(int, 0, remaining);
/*
* We've already reserved 1 FIFO per EP, so check what we can fit in
* addition to it. If there is not enough remaining space, allocate
* all the remaining space to the EP.
*/
fifo_size = (num_fifos - 1) * fifo;
if (remaining < fifo_size)
fifo_size = remaining;
fifo_size += fifo;
/* Last increment according to the TX FIFO size equation */
fifo_size++;
/* Check if TXFIFOs start at non-zero addr */
tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
if (DWC3_IP_IS(DWC3))
dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
else
dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
/* Check fifo size allocation doesn't exceed available RAM size. */
if (dwc->last_fifo_depth >= ram1_depth) {
dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
dwc->last_fifo_depth, ram1_depth,
dep->endpoint.name, fifo_size);
if (DWC3_IP_IS(DWC3))
fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
else
fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
dwc->last_fifo_depth -= fifo_size;
return -ENOMEM;
}
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
dwc->num_ep_resized++;
return 0;
}
修改預設 TxFIFO 的目的是為了讓 Host 選擇適當的 IN EP 做傳輸,透過實做 match_ep,device 將提供 Host 每個 IN EP 的 TxFIFO
觀察 DWC3 於 Linux device driver 調用,dwc3_gadget_init_in_endpoint
計算每個 IN EP 預設的 TxFIFO 之 maxpacket
,亦即 mult * max_packet
其中 max_packet
為 1024 bytes
static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
u32 mdwidth;
int size;
int maxpacket;
mdwidth = dwc3_mdwidth(dwc);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
if (DWC3_IP_IS(DWC3))
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
/*
* maxpacket size is determined as part of the following, after assuming
* a mult value of one maxpacket:
* DWC3 revision 280A and prior:
* fifo_size = mult * (max_packet / mdwidth) + 1;
* maxpacket = mdwidth * (fifo_size - 1);
*
* DWC3 revision 290A and onwards:
* fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
* maxpacket = mdwidth * ((fifo_size - 1) - 1) - mdwidth;
*/
if (DWC3_VER_IS_PRIOR(DWC3, 290A))
maxpacket = mdwidth * (size - 1);
else
maxpacket = mdwidth * ((size - 1) - 1) - mdwidth;
/* Functionally, space for one max packet is sufficient */
size = min_t(int, maxpacket, 1024);
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
&dwc->gadget->ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
return dwc3_alloc_trb_pool(dep);
}
原始流程:
dwc3_gadget_init_in_endpoint
→ dwc3_gadget_ep_match
→ dwc3_gadget_ep_enable
→ dwc3_gadget_resize_fifos
其中 dwc3_gadget_ep_match
由 V4L2 層的 uvc_function_bind
調用
然而 dwc3_gadget_resize_tx_fifos
的調用會在 match_ep 之後,也就是 Host 只會取得每個 IN EP 預設的 TxFIFO
流程需修改如下:
dwc3_gadget_init_in_endpoint
→ dwc3_gadget_resize_fifos
→ dwc3_gadget_ep_match
→ dwc3_gadget_ep_enable
static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
u32 mdwidth;
int size;
int maxpacket;
mdwidth = dwc3_mdwidth(dwc);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
+ dwc3_gadget_resize_tx_fifos(dep);
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
if (DWC3_IP_IS(DWC3))
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
/*
* maxpacket size is determined as part of the following, after assuming
* a mult value of one maxpacket:
* DWC3 revision 280A and prior:
* fifo_size = mult * (max_packet / mdwidth) + 1;
* maxpacket = mdwidth * (fifo_size - 1);
*
* DWC3 revision 290A and onwards:
* fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
* maxpacket = mdwidth * ((fifo_size - 1) - 1) - mdwidth;
*/
if (DWC3_VER_IS_PRIOR(DWC3, 290A))
maxpacket = mdwidth * (size - 1);
else
maxpacket = mdwidth * ((size - 1) - 1) - mdwidth;
/* Functionally, space for one max packet is sufficient */
size = min_t(int, maxpacket, 1024);
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
&dwc->gadget->ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
return dwc3_alloc_trb_pool(dep);
}
上述修改只能將每個 IN EP 的 TxFIFO 重新配置為 3 個 max_packets
,亦即 3x1024 bytes,若需自由調整,可在 dts 中新增