mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 12:57:06 +09:00
usb: dwc3: gadget: fix ep init for unequal num of in/out eps
Some SoCs have different number of endpoints between the EP-IN
and EP-OUT (e.g. RK3399/RV1126 have 7 in endpoints and 6 out
endpoints), it will fail to init all of the endpoints.
In my test case, I use RV1126 dwc3 to support 3 UVC functions
at the same time, and each UVC function need one in endpoint
for control interface and one in endpoint for streaming interface,
so it needs to init 7 in endpoints (include ep0-in) in this case.
Without this patch, it will fail to init the ep7-in because
it set the wrong DWC3_DEP_BASE for ep7-in.
According to dwc3 databook, the register DALEPENA and the "USB
Endpoint Number" field of Parameter1 are doing 1:1 mapping for
endpoints, meaning physical endpoints 2 maps to logical endpoint
2:
Bit[0]: USB EP0-OUT
Bit[1]: USB EP0-IN
Bit[2]: USB EP1-OUT
Bit[3]: USB EP1-IN
...
Bit[13]: USB EP7-IN
The dwc3 driver use dep->number to index endpoint number and init
the DALEPENA and the "USB Endpoint Number" field of Parameter1.
For RV1126, it should set dep->number to 13 for EP7-IN.
But the registers DEPCMDPAR2(#n),DEPCMDPAR1(#n), DEPCMDPAR0(#n),
and DEPCMD(#n) don't 1:1 mapping for endpoints. For RV1126, it
should set #n to 12 for EP7-IN. And the event->endpoint_number
in the dwc3_endpoint_interrupt() is equal to 12 for EP7-IN.
Fixes: c2185009e2 ("usb: dwc3: gadget: fix init endpoints and resize tx fifos")
Change-Id: I0898306196f4dacf09b0de3cf4d76d9026b6315c
Signed-off-by: William Wu <william.wu@rock-chips.com>
This commit is contained in:
@@ -283,21 +283,33 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
|
|||||||
|
|
||||||
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
|
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
|
||||||
{
|
{
|
||||||
struct dwc3_ep *dep;
|
struct dwc3_ep *dep = NULL;
|
||||||
u32 windex = le16_to_cpu(wIndex_le);
|
u32 windex = le16_to_cpu(wIndex_le);
|
||||||
u32 epnum;
|
u32 epnum, ep_index;
|
||||||
|
u8 num, direction;
|
||||||
|
|
||||||
epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1;
|
epnum = windex & USB_ENDPOINT_NUMBER_MASK;
|
||||||
if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
|
direction = windex & USB_ENDPOINT_DIR_MASK;
|
||||||
epnum |= 1;
|
ep_index = 0;
|
||||||
|
|
||||||
dep = dwc->eps[epnum];
|
for (num = 0; num < dwc->num_eps; num++) {
|
||||||
if (!dep) {
|
dep = dwc->eps[num];
|
||||||
dev_warn(dwc->dev, "epnum %d, windex 0x%08x\n", epnum, windex);
|
if (!dep) {
|
||||||
return NULL;
|
dev_warn(dwc->dev, "dep is NULL, num %d, windex 0x%08x\n",
|
||||||
|
num, windex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((direction == USB_DIR_IN && dep->direction) ||
|
||||||
|
(direction == USB_DIR_OUT && !dep->direction))
|
||||||
|
ep_index++;
|
||||||
|
|
||||||
|
if (ep_index == epnum + 1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dep->flags & DWC3_EP_ENABLED)
|
|
||||||
|
if (dep && (dep->flags & DWC3_EP_ENABLED))
|
||||||
return dep;
|
return dep;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -162,27 +162,26 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
|
|||||||
int last_fifo_depth;
|
int last_fifo_depth;
|
||||||
int fifo_size;
|
int fifo_size;
|
||||||
int mdwidth;
|
int mdwidth;
|
||||||
u8 num, num_in_eps;
|
u8 num, fifo_number;
|
||||||
|
|
||||||
if (!dwc->needs_fifo_resize)
|
if (!dwc->needs_fifo_resize)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
num_in_eps = DWC3_NUM_IN_EPS(&dwc->hwparams);
|
|
||||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||||
/* MDWIDTH is represented in bits, we need it in bytes */
|
/* MDWIDTH is represented in bits, we need it in bytes */
|
||||||
mdwidth >>= 3;
|
mdwidth >>= 3;
|
||||||
|
fifo_number = 0;
|
||||||
fifo_size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
|
fifo_size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
|
||||||
last_fifo_depth = DWC3_GTXFIFOSIZ_TXFSTADDR(fifo_size) >> 16;
|
last_fifo_depth = DWC3_GTXFIFOSIZ_TXFSTADDR(fifo_size) >> 16;
|
||||||
|
|
||||||
for (num = 0; num < num_in_eps; num++) {
|
for (num = 0; num < dwc->num_eps; num++) {
|
||||||
u8 epnum = (num << 1) | 1;
|
struct dwc3_ep *dep = dwc->eps[num];
|
||||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
|
||||||
int fifo_number = dep->number >> 1;
|
|
||||||
int mult = 1;
|
int mult = 1;
|
||||||
int tmp;
|
int tmp;
|
||||||
|
|
||||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
/* Skip out endpoints */
|
||||||
|
if (!dep || !dep->direction ||
|
||||||
|
!(dep->flags & DWC3_EP_ENABLED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
|
if (usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
|
||||||
@@ -217,6 +216,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
|
|||||||
fifo_size);
|
fifo_size);
|
||||||
|
|
||||||
last_fifo_depth += (fifo_size & 0xffff);
|
last_fifo_depth += (fifo_size & 0xffff);
|
||||||
|
fifo_number++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2360,13 +2360,24 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
|||||||
bool direction = epnum & 1;
|
bool direction = epnum & 1;
|
||||||
int ret;
|
int ret;
|
||||||
u8 num = epnum >> 1;
|
u8 num = epnum >> 1;
|
||||||
|
u8 num_in_eps, num_out_eps;
|
||||||
|
|
||||||
|
num_in_eps = DWC3_NUM_IN_EPS(&dwc->hwparams);
|
||||||
|
num_out_eps = dwc->num_eps - num_in_eps;
|
||||||
|
|
||||||
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
||||||
if (!dep)
|
if (!dep)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* reconfig direction and num if num_out_eps != num_in_eps */
|
||||||
|
if ((!direction && ((epnum >> 1) + 1) > num_out_eps) ||
|
||||||
|
(direction && ((epnum >> 1) + 1) > num_in_eps)) {
|
||||||
|
direction = !direction;
|
||||||
|
num = num + (epnum & 1);
|
||||||
|
}
|
||||||
|
|
||||||
dep->dwc = dwc;
|
dep->dwc = dwc;
|
||||||
dep->number = epnum;
|
dep->number = num << 1 | direction;
|
||||||
dep->direction = direction;
|
dep->direction = direction;
|
||||||
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
|
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
|
||||||
dwc->eps[epnum] = dep;
|
dwc->eps[epnum] = dep;
|
||||||
@@ -2407,25 +2418,12 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
|||||||
|
|
||||||
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
|
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
|
||||||
{
|
{
|
||||||
u8 epnum, num;
|
u8 epnum;
|
||||||
u8 num_in_eps, num_out_eps;
|
|
||||||
bool direction;
|
|
||||||
|
|
||||||
num_in_eps = DWC3_NUM_IN_EPS(&dwc->hwparams);
|
|
||||||
num_out_eps = total - num_in_eps;
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
||||||
|
|
||||||
for (epnum = 0; epnum < total; epnum++) {
|
for (epnum = 0; epnum < total; epnum++) {
|
||||||
int ret;
|
int ret;
|
||||||
direction = epnum & 1;
|
|
||||||
num = (epnum >> 1) + 1;
|
|
||||||
|
|
||||||
if ((!direction && num > num_out_eps) ||
|
|
||||||
(direction && num > num_in_eps)) {
|
|
||||||
total++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = dwc3_gadget_init_endpoint(dwc, epnum);
|
ret = dwc3_gadget_init_endpoint(dwc, epnum);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|||||||
Reference in New Issue
Block a user