diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 7c9df630dbe4f848be07dda04a3f2a295a78d7c7..d119a1fbf9466243b2c3afecfe0f7e306523229b 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -404,6 +405,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) static int __devinit dwc3_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; struct resource *res; struct dwc3 *dwc; @@ -469,6 +471,9 @@ static int __devinit dwc3_probe(struct platform_device *pdev) else dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; + if (of_get_property(node, "tx-fifo-resize", NULL)) + dwc->needs_fifo_resize = true; + pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); pm_runtime_forbid(&pdev->dev); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f4878c4dec0fca2f9473f9a4d277dbe375605db9..123c3aa471283431874ba1a36c57326ce9eaf9d1 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -172,6 +172,10 @@ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) #define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) +/* Global TX Fifo Size Register */ +#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) + /* Global HWPARAMS1 Register */ #define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 @@ -546,8 +550,13 @@ struct dwc3_hwparams { #define DWC3_MODE_DRD 2 #define DWC3_MODE_HUB 3 +#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) + /* HWPARAMS1 */ -#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) +#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) + +/* HWPARAMS7 */ +#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) struct dwc3_request { struct usb_request request; @@ -594,6 +603,8 @@ struct dwc3_request { * @ep0_expect_in: true when we expect a DATA IN transfer * @start_config_issued: true when StartConfig command has been issued * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround + * @needs_fifo_resize: not all users might want fifo resizing, flag it + * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state @@ -651,6 +662,8 @@ struct dwc3 { unsigned start_config_issued:1; unsigned setup_packet_pending:1; unsigned delayed_status:1; + unsigned needs_fifo_resize:1; + unsigned resize_fifos:1; enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; @@ -812,6 +825,7 @@ union dwc3_event { /* prototypes */ void dwc3_set_mode(struct dwc3 *dwc, u32 mode); +int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e90ebb9dd3e87f1905dd5b8f482b4f589826aa99..5104dbf46680de50e5ac48e9f4cc20303eae2781 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -457,8 +457,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) case DWC3_ADDRESS_STATE: ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ - if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) + if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { dwc->dev_state = DWC3_CONFIGURED_STATE; + dwc->resize_fifos = true; + dev_dbg(dwc->dev, "resize fifos flag SET\n"); + } break; case DWC3_CONFIGURED_STATE: @@ -707,6 +710,12 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum) { struct dwc3_ep *dep = dwc->eps[epnum]; + if (dwc->resize_fifos) { + dev_dbg(dwc->dev, "starting to resize fifos\n"); + dwc3_gadget_resize_tx_fifos(dwc); + dwc->resize_fifos = 0; + } + WARN_ON(dwc3_ep0_start_control_status(dep)); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d406a75456a0d661973e4028c39a9466df0b0ab2..7913d1b50e3853223786dacd4194adb0445e3bba 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -125,6 +125,80 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) return -ETIMEDOUT; } +/** + * 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 the ideal world, we would always be able to satisfy the + * following equation: + * + * ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \ + * (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes + * + * Unfortunately, due to many variables that's not always the case. + */ +int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) +{ + int last_fifo_depth = 0; + int ram1_depth; + int fifo_size; + int mdwidth; + int num; + + if (!dwc->needs_fifo_resize) + return 0; + + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); + + /* MDWIDTH is represented in bits, we need it in bytes */ + mdwidth >>= 3; + + /* + * FIXME For now we will only allocate 1 wMaxPacketSize space + * for each enabled endpoint, later patches will come to + * improve this algorithm so that we better use the internal + * FIFO space + */ + for (num = 0; num < DWC3_ENDPOINTS_NUM; num++) { + struct dwc3_ep *dep = dwc->eps[num]; + int fifo_number = dep->number >> 1; + int tmp; + + if (!(dep->number & 1)) + continue; + + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + tmp = dep->endpoint.maxpacket; + tmp += mdwidth; + tmp += mdwidth; + + fifo_size = DIV_ROUND_UP(tmp, mdwidth); + fifo_size |= (last_fifo_depth << 16); + + dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n", + dep->name, last_fifo_depth, fifo_size & 0xffff); + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(fifo_number), + fifo_size); + + last_fifo_depth += (fifo_size & 0xffff); + } + + return 0; +} + void dwc3_map_buffer_to_dma(struct dwc3_request *req) { struct dwc3 *dwc = req->dep->dwc;