Try   HackMD

DWC3 TxFIFO adjustment

Background

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.

  1. DWC3 規定每個 IN 方向的 EP 必需有自己的 TxFIFO 進行 Data prefetch
  2. 可按照需求,設定需要使用的 IN EP 的 TxFIFO。以下圖為例,USB 裝置總共有 6 個 IN EP,但只使用其中 4 個,則可以設定 TxFIFO 如圖:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

TxFIFO 計算公式

DWC 規定一個 USB 3.0 裝置的設計對 RAM 的需求為:

  1. RAM0: Cache
  2. RAM1: TxFIFOs(one per IN endpoint)
  3. RAM2: RxFIFO(shared among all OUT endpoints)

其中 RAM 可以為 SoC 提供的 SRAM 空間

對於 TxFIFO 的要求為:

  • EP0: 512 bytes
  • All other IN EP: 1024 byte packet for each IN EP(For burst-capable, each requires a 3 packet buffer)

RAM1(TxFIFO)=((512+2×MDWIDTH)+(Number of IN Endpoints 1)×(3×(1024+MDWIDTH)+MDWIDTH))MDWIDTH

MDWIDTH 定義為(單位為 byte):

  • 4 for 32-bit master-bus
  • 8 for 64-bit master bus
  • 16 for 128-bit master bus mode

根據 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
8336=8×(1024+16)×16+16
8
EP2 8336 8
EP3
2096=2×(1024+16)×16+16
2
EP4 2096 2
EP5 2096 2
EP6
3136=3×(1024+16)×16+16
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
4176=4×(1024+16)×16+16
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_endpointdwc3_gadget_ep_matchdwc3_gadget_ep_enabledwc3_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_endpointdwc3_gadget_resize_fifosdwc3_gadget_ep_matchdwc3_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 中新增