diff options
Diffstat (limited to 'drivers/usb/cdns3')
| -rw-r--r-- | drivers/usb/cdns3/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/usb/cdns3/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-debug.h | 8 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-gadget.c | 40 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-imx.c | 30 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-pci-wrap.c | 5 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-plat.c | 5 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-starfive.c | 246 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-ti.c | 6 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-trace.h | 28 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdnsp-ep0.c | 22 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdnsp-gadget.c | 2 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdnsp-gadget.h | 4 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdnsp-pci.c | 27 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdnsp-ring.c | 110 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdnsp-trace.h | 12 |
16 files changed, 410 insertions, 147 deletions
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index b98ca0a1352a..0a514b591527 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -78,6 +78,17 @@ config USB_CDNS3_IMX For example, imx8qm and imx8qxp. +config USB_CDNS3_STARFIVE + tristate "Cadence USB3 support on StarFive SoC platforms" + depends on ARCH_STARFIVE || COMPILE_TEST + help + Say 'Y' or 'M' here if you are building for StarFive SoCs + platforms that contain Cadence USB3 controller core. + + e.g. JH7110. + + If you choose to build this driver as module it will + be dynamically linked and module will be called cdns3-starfive.ko endif if USB_CDNS_SUPPORT diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 61edb2f89276..48dfae75b5aa 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -24,6 +24,7 @@ endif obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o +obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o cdnsp-udc-pci-y := cdnsp-pci.o diff --git a/drivers/usb/cdns3/cdns3-debug.h b/drivers/usb/cdns3/cdns3-debug.h index a5c6a29e1340..4618cfe85a4f 100644 --- a/drivers/usb/cdns3/cdns3-debug.h +++ b/drivers/usb/cdns3/cdns3-debug.h @@ -107,8 +107,7 @@ static inline char *cdns3_decode_ep0_irq(char *str, * Prints out all TRBs in the endpoint ring, even those after the Link TRB. *. */ -static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep, - struct cdns3_trb *ring, char *str) +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep, char *str) { dma_addr_t addr = priv_ep->trb_pool_dma; struct cdns3_trb *trb; @@ -136,9 +135,6 @@ static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep, "\t\tfree trbs: %d, CCS=%d, PCS=%d\n", priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs); - if (trb_per_sector > TRBS_PER_SEGMENT) - trb_per_sector = TRBS_PER_SEGMENT; - if (trb_per_sector > TRBS_PER_SEGMENT) { sprintf(str + ret, "\t\tTransfer ring %d too big\n", trb_per_sector); @@ -146,7 +142,7 @@ static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep, } for (i = 0; i < trb_per_sector; ++i) { - trb = &ring[i]; + trb = &priv_ep->trb_pool[i]; ret += sprintf(str + ret, "\t\t@%pad %08x %08x %08x\n", &addr, le32_to_cpu(trb->buffer), diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 5adcb349718c..ea19253fd2d0 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -800,7 +800,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, if (request->status == -EINPROGRESS) request->status = status; - usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request, + if (likely(!(priv_req->flags & REQUEST_UNALIGNED))) + usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request, priv_ep->dir); if ((priv_req->flags & REQUEST_UNALIGNED) && @@ -808,10 +809,10 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, /* Make DMA buffer CPU accessible */ dma_sync_single_for_cpu(priv_dev->sysdev, priv_req->aligned_buf->dma, - priv_req->aligned_buf->size, + request->actual, priv_req->aligned_buf->dir); memcpy(request->buf, priv_req->aligned_buf->buf, - request->length); + request->actual); } priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); @@ -2097,6 +2098,19 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) else priv_ep->trb_burst_size = 16; + /* + * In versions preceding DEV_VER_V2, for example, iMX8QM, there exit the bugs + * in the DMA. These bugs occur when the trb_burst_size exceeds 16 and the + * address is not aligned to 128 Bytes (which is a product of the 64-bit AXI + * and AXI maximum burst length of 16 or 0xF+1, dma_axi_ctrl0[3:0]). This + * results in data corruption when it crosses the 4K border. The corruption + * specifically occurs from the position (4K - (address & 0x7F)) to 4K. + * + * So force trb_burst_size to 16 at such platform. + */ + if (priv_dev->dev_ver < DEV_VER_V2) + priv_ep->trb_burst_size = 16; + mult = min_t(u8, mult, EP_CFG_MULT_MAX); buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX); maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX); @@ -2530,10 +2544,12 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, if (ret < 0) return ret; - ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, + if (likely(!(priv_req->flags & REQUEST_UNALIGNED))) { + ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, usb_endpoint_dir_in(ep->desc)); - if (ret) - return ret; + if (ret) + return ret; + } list_add_tail(&request->list, &priv_ep->deferred_req_list); @@ -2614,6 +2630,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep, u8 req_on_hw_ring = 0; unsigned long flags; int ret = 0; + int val; if (!ep || !request || !ep->desc) return -EINVAL; @@ -2649,6 +2666,13 @@ found: /* Update ring only if removed request is on pending_req_list list */ if (req_on_hw_ring && link_trb) { + /* Stop DMA */ + writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd); + + /* wait for DFLUSH cleared */ + readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val, + !(val & EP_CMD_DFLUSH), 1, 1000); + link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma + ((priv_req->end_trb + 1) * TRB_SIZE))); link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) | @@ -2660,6 +2684,10 @@ found: cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET); + req = cdns3_next_request(&priv_ep->pending_req_list); + if (req) + cdns3_rearm_transfer(priv_ep, 1); + not_found: spin_unlock_irqrestore(&priv_dev->lock, flags); return ret; diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 59860d1753fd..281de47e2a3b 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -105,11 +105,11 @@ static inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value) } static const struct clk_bulk_data imx_cdns3_core_clks[] = { - { .id = "usb3_lpm_clk" }, - { .id = "usb3_bus_clk" }, - { .id = "usb3_aclk" }, - { .id = "usb3_ipg_clk" }, - { .id = "usb3_core_pclk" }, + { .id = "lpm" }, + { .id = "bus" }, + { .id = "aclk" }, + { .id = "ipg" }, + { .id = "core" }, }; static int cdns_imx_noncore_init(struct cdns_imx *data) @@ -218,7 +218,7 @@ err: return ret; } -static int cdns_imx_remove(struct platform_device *pdev) +static void cdns_imx_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cdns_imx *data = dev_get_drvdata(dev); @@ -229,8 +229,6 @@ static int cdns_imx_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); platform_set_drvdata(pdev, NULL); - - return 0; } #ifdef CONFIG_PM @@ -375,14 +373,22 @@ static inline bool cdns_imx_is_power_lost(struct cdns_imx *data) return false; } +static int __maybe_unused cdns_imx_system_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + return 0; +} + static int __maybe_unused cdns_imx_system_resume(struct device *dev) { struct cdns_imx *data = dev_get_drvdata(dev); int ret; - ret = cdns_imx_resume(dev); - if (ret) + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "Could not get runtime PM.\n"); return ret; + } if (cdns_imx_is_power_lost(data)) { dev_dbg(dev, "resume from power lost\n"); @@ -405,7 +411,7 @@ static int cdns_imx_platform_suspend(struct device *dev, static const struct dev_pm_ops cdns_imx_pm_ops = { SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_suspend, cdns_imx_system_resume) + SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_system_suspend, cdns_imx_system_resume) }; static const struct of_device_id cdns_imx_of_match[] = { @@ -416,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match); static struct platform_driver cdns_imx_driver = { .probe = cdns_imx_probe, - .remove = cdns_imx_remove, + .remove_new = cdns_imx_remove, .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c index deeea618ba33..1f6320d98a76 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -60,6 +60,11 @@ static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev) return NULL; } + if (func->devfn != PCI_DEV_FN_HOST_DEVICE && + func->devfn != PCI_DEV_FN_OTG) { + return NULL; + } + return func; } diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 2bc5d094548b..884e2301237f 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -175,7 +175,7 @@ err_phy3_init: * * Returns 0 on success otherwise negative errno */ -static int cdns3_plat_remove(struct platform_device *pdev) +static void cdns3_plat_remove(struct platform_device *pdev) { struct cdns *cdns = platform_get_drvdata(pdev); struct device *dev = cdns->dev; @@ -187,7 +187,6 @@ static int cdns3_plat_remove(struct platform_device *pdev) set_phy_power_off(cdns); phy_exit(cdns->usb2_phy); phy_exit(cdns->usb3_phy); - return 0; } #ifdef CONFIG_PM @@ -320,7 +319,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match); static struct platform_driver cdns3_driver = { .probe = cdns3_plat_probe, - .remove = cdns3_plat_remove, + .remove_new = cdns3_plat_remove, .driver = { .name = "cdns-usb3", .of_match_table = of_match_ptr(of_cdns3_match), diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 000000000000..fc1f003b145d --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Minda Chen <minda.chen@starfivetech.com> + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/reset.h> +#include <linux/regmap.h> +#include <linux/usb/otg.h> +#include "core.h" + +#define USB_STRAP_HOST BIT(17) +#define USB_STRAP_DEVICE BIT(18) +#define USB_STRAP_MASK GENMASK(18, 16) + +#define USB_SUSPENDM_HOST BIT(19) +#define USB_SUSPENDM_MASK BIT(19) + +#define USB_MISC_CFG_MASK GENMASK(23, 20) +#define USB_SUSPENDM_BYPS BIT(20) +#define USB_PLL_EN BIT(22) +#define USB_REFCLK_MODE BIT(23) + +struct cdns_starfive { + struct device *dev; + struct regmap *stg_syscon; + struct reset_control *resets; + struct clk_bulk_data *clks; + int num_clks; + u32 stg_usb_mode; +}; + +static void cdns_mode_init(struct platform_device *pdev, + struct cdns_starfive *data) +{ + enum usb_dr_mode mode; + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_MISC_CFG_MASK, + USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); + + /* dr mode setting */ + mode = usb_get_dr_mode(&pdev->dev); + + switch (mode) { + case USB_DR_MODE_HOST: + regmap_update_bits(data->stg_syscon, + data->stg_usb_mode, + USB_STRAP_MASK, + USB_STRAP_HOST); + regmap_update_bits(data->stg_syscon, + data->stg_usb_mode, + USB_SUSPENDM_MASK, + USB_SUSPENDM_HOST); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_STRAP_MASK, USB_STRAP_DEVICE); + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_SUSPENDM_MASK, 0); + break; + default: + break; + } +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ + int ret; + + ret = clk_bulk_prepare_enable(data->num_clks, data->clks); + if (ret) + return dev_err_probe(data->dev, ret, + "failed to enable clocks\n"); + + ret = reset_control_deassert(data->resets); + if (ret) { + dev_err(data->dev, "failed to reset clocks\n"); + goto err_clk_init; + } + + return ret; + +err_clk_init: + clk_bulk_disable_unprepare(data->num_clks, data->clks); + return ret; +} + +static void cdns_clk_rst_deinit(struct cdns_starfive *data) +{ + reset_control_assert(data->resets); + clk_bulk_disable_unprepare(data->num_clks, data->clks); +} + +static int cdns_starfive_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_starfive *data; + unsigned int args; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + + data->stg_syscon = + syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,stg-syscon", 1, &args); + + if (IS_ERR(data->stg_syscon)) + return dev_err_probe(dev, PTR_ERR(data->stg_syscon), + "Failed to parse starfive,stg-syscon\n"); + + data->stg_usb_mode = args; + + data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks); + if (data->num_clks < 0) + return dev_err_probe(data->dev, -ENODEV, + "Failed to get clocks\n"); + + data->resets = devm_reset_control_array_get_exclusive(data->dev); + if (IS_ERR(data->resets)) + return dev_err_probe(data->dev, PTR_ERR(data->resets), + "Failed to get resets"); + + cdns_mode_init(pdev, data); + ret = cdns_clk_rst_init(data); + if (ret) + return ret; + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "Failed to create children\n"); + cdns_clk_rst_deinit(data); + return ret; + } + + device_set_wakeup_capable(dev, true); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + platform_set_drvdata(pdev, data); + + return 0; +} + +static int cdns_starfive_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int cdns_starfive_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_starfive *data = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + device_for_each_child(dev, NULL, cdns_starfive_remove_core); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + cdns_clk_rst_deinit(data); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int cdns_starfive_runtime_resume(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(data->num_clks, data->clks); +} + +static int cdns_starfive_runtime_suspend(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(data->num_clks, data->clks); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cdns_starfive_resume(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + return cdns_clk_rst_init(data); +} + +static int cdns_starfive_suspend(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + cdns_clk_rst_deinit(data); + + return 0; +} +#endif +#endif + +static const struct dev_pm_ops cdns_starfive_pm_ops = { + SET_RUNTIME_PM_OPS(cdns_starfive_runtime_suspend, + cdns_starfive_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume) +}; + +static const struct of_device_id cdns_starfive_of_match[] = { + { .compatible = "starfive,jh7110-usb", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); + +static struct platform_driver cdns_starfive_driver = { + .probe = cdns_starfive_probe, + .remove = cdns_starfive_remove, + .driver = { + .name = "cdns3-starfive", + .of_match_table = cdns_starfive_of_match, + .pm = &cdns_starfive_pm_ops, + }, +}; +module_platform_driver(cdns_starfive_driver); + +MODULE_ALIAS("platform:cdns3-starfive"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer"); diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 07c318770362..81b9132e3aaa 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -199,7 +199,7 @@ static int cdns_ti_remove_core(struct device *dev, void *c) return 0; } -static int cdns_ti_remove(struct platform_device *pdev) +static void cdns_ti_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -208,8 +208,6 @@ static int cdns_ti_remove(struct platform_device *pdev) pm_runtime_disable(dev); platform_set_drvdata(pdev, NULL); - - return 0; } static const struct of_device_id cdns_ti_of_match[] = { @@ -221,7 +219,7 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match); static struct platform_driver cdns_ti_driver = { .probe = cdns_ti_probe, - .remove = cdns_ti_remove, + .remove_new = cdns_ti_remove, .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, diff --git a/drivers/usb/cdns3/cdns3-trace.h b/drivers/usb/cdns3/cdns3-trace.h index 7574b4a62813..40db89e3333c 100644 --- a/drivers/usb/cdns3/cdns3-trace.h +++ b/drivers/usb/cdns3/cdns3-trace.h @@ -100,13 +100,12 @@ DECLARE_EVENT_CLASS(cdns3_log_usb_irq, TP_STRUCT__entry( __field(enum usb_device_speed, speed) __field(u32, usb_ists) - __dynamic_array(char, str, CDNS3_MSG_MAX) ), TP_fast_assign( __entry->speed = cdns3_get_speed(priv_dev); __entry->usb_ists = usb_ists; ), - TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed, + TP_printk("%s", cdns3_decode_usb_irq(__get_buf(CDNS3_MSG_MAX), __entry->speed, __entry->usb_ists)) ); @@ -124,7 +123,6 @@ DECLARE_EVENT_CLASS(cdns3_log_epx_irq, __field(u32, ep_traddr) __field(u32, ep_last_sid) __field(u32, use_streams) - __dynamic_array(char, str, CDNS3_MSG_MAX) ), TP_fast_assign( __assign_str(ep_name, priv_ep->name); @@ -134,7 +132,7 @@ DECLARE_EVENT_CLASS(cdns3_log_epx_irq, __entry->use_streams = priv_ep->use_streams; ), TP_printk("%s, ep_traddr: %08x ep_last_sid: %08x use_streams: %d", - cdns3_decode_epx_irq(__get_str(str), + cdns3_decode_epx_irq(__get_buf(CDNS3_MSG_MAX), __get_str(ep_name), __entry->ep_sts), __entry->ep_traddr, @@ -153,13 +151,12 @@ DECLARE_EVENT_CLASS(cdns3_log_ep0_irq, TP_STRUCT__entry( __field(int, ep_dir) __field(u32, ep_sts) - __dynamic_array(char, str, CDNS3_MSG_MAX) ), TP_fast_assign( __entry->ep_dir = priv_dev->selected_ep; __entry->ep_sts = ep_sts; ), - TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str), + TP_printk("%s", cdns3_decode_ep0_irq(__get_buf(CDNS3_MSG_MAX), __entry->ep_dir, __entry->ep_sts)) ); @@ -178,7 +175,6 @@ DECLARE_EVENT_CLASS(cdns3_log_ctrl, __field(u16, wValue) __field(u16, wIndex) __field(u16, wLength) - __dynamic_array(char, str, CDNS3_MSG_MAX) ), TP_fast_assign( __entry->bRequestType = ctrl->bRequestType; @@ -187,7 +183,7 @@ DECLARE_EVENT_CLASS(cdns3_log_ctrl, __entry->wIndex = le16_to_cpu(ctrl->wIndex); __entry->wLength = le16_to_cpu(ctrl->wLength); ), - TP_printk("%s", usb_decode_ctrl(__get_str(str), CDNS3_MSG_MAX, + TP_printk("%s", usb_decode_ctrl(__get_buf(CDNS3_MSG_MAX), CDNS3_MSG_MAX, __entry->bRequestType, __entry->bRequest, __entry->wValue, __entry->wIndex, __entry->wLength) @@ -438,22 +434,16 @@ DECLARE_EVENT_CLASS(cdns3_log_ring, TP_PROTO(struct cdns3_endpoint *priv_ep), TP_ARGS(priv_ep), TP_STRUCT__entry( - __dynamic_array(u8, ring, TRB_RING_SIZE) - __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint)) __dynamic_array(char, buffer, - (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX) + GET_TRBS_PER_SEGMENT(priv_ep->type) > TRBS_PER_SEGMENT ? + CDNS3_MSG_MAX : + (GET_TRBS_PER_SEGMENT(priv_ep->type) * 65) + CDNS3_MSG_MAX) ), TP_fast_assign( - memcpy(__get_dynamic_array(priv_ep), priv_ep, - sizeof(struct cdns3_endpoint)); - memcpy(__get_dynamic_array(ring), priv_ep->trb_pool, - TRB_RING_SIZE); + cdns3_dbg_ring(priv_ep, __get_str(buffer)); ), - TP_printk("%s", - cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep), - (struct cdns3_trb *)__get_str(ring), - __get_str(buffer))) + TP_printk("%s", __get_str(buffer)) ); DEFINE_EVENT(cdns3_log_ring, cdns3_ring, diff --git a/drivers/usb/cdns3/cdnsp-ep0.c b/drivers/usb/cdns3/cdnsp-ep0.c index 9b8325f82499..f317d3c84781 100644 --- a/drivers/usb/cdns3/cdnsp-ep0.c +++ b/drivers/usb/cdns3/cdnsp-ep0.c @@ -403,20 +403,6 @@ static int cdnsp_ep0_std_request(struct cdnsp_device *pdev, case USB_REQ_SET_ISOCH_DELAY: ret = cdnsp_ep0_set_isoch_delay(pdev, ctrl); break; - case USB_REQ_SET_INTERFACE: - /* - * Add request into pending list to block sending status stage - * by libcomposite. - */ - list_add_tail(&pdev->ep0_preq.list, - &pdev->ep0_preq.pep->pending_list); - - ret = cdnsp_ep0_delegate_req(pdev, ctrl); - if (ret == -EBUSY) - ret = 0; - - list_del(&pdev->ep0_preq.list); - break; default: ret = cdnsp_ep0_delegate_req(pdev, ctrl); break; @@ -428,7 +414,7 @@ static int cdnsp_ep0_std_request(struct cdnsp_device *pdev, void cdnsp_setup_analyze(struct cdnsp_device *pdev) { struct usb_ctrlrequest *ctrl = &pdev->setup; - int ret = 0; + int ret = -EINVAL; u16 len; trace_cdnsp_ctrl_req(ctrl); @@ -438,7 +424,6 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev) if (pdev->gadget.state == USB_STATE_NOTATTACHED) { dev_err(pdev->dev, "ERR: Setup detected in unattached state\n"); - ret = -EINVAL; goto out; } @@ -474,9 +459,6 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev) else ret = cdnsp_ep0_delegate_req(pdev, ctrl); - if (!len) - pdev->ep0_stage = CDNSP_STATUS_STAGE; - if (ret == USB_GADGET_DELAYED_STATUS) { trace_cdnsp_ep0_status_stage("delayed"); return; @@ -484,6 +466,6 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev) out: if (ret < 0) cdnsp_ep0_stall(pdev); - else if (pdev->ep0_stage == CDNSP_STATUS_STAGE) + else if (!len && pdev->ep0_stage != CDNSP_STATUS_STAGE) cdnsp_status_stage(pdev); } diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index f9aa50ff14d4..fff9ec9c391f 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -378,7 +378,7 @@ int cdnsp_ep_enqueue(struct cdnsp_ep *pep, struct cdnsp_request *preq) ret = cdnsp_queue_bulk_tx(pdev, preq); break; case USB_ENDPOINT_XFER_ISOC: - ret = cdnsp_queue_isoc_tx_prepare(pdev, preq); + ret = cdnsp_queue_isoc_tx(pdev, preq); } if (ret) diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index f740fa6089d8..e1b5801fdddf 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -1532,8 +1532,8 @@ void cdnsp_queue_stop_endpoint(struct cdnsp_device *pdev, unsigned int ep_index); int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq); int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq); -int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev, - struct cdnsp_request *preq); +int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, + struct cdnsp_request *preq); void cdnsp_queue_configure_endpoint(struct cdnsp_device *pdev, dma_addr_t in_ctx_ptr); void cdnsp_queue_reset_ep(struct cdnsp_device *pdev, unsigned int ep_index); diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index efd54ed918b9..7b151f5af3cc 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -29,30 +29,23 @@ #define PLAT_DRIVER_NAME "cdns-usbssp" #define CDNS_VENDOR_ID 0x17cd -#define CDNS_DEVICE_ID 0x0100 +#define CDNS_DEVICE_ID 0x0200 +#define CDNS_DRD_ID 0x0100 #define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80) static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { - struct pci_dev *func; - /* * Gets the second function. - * It's little tricky, but this platform has two function. - * The fist keeps resources for Host/Device while the second - * keeps resources for DRD/OTG. + * Platform has two function. The fist keeps resources for + * Host/Device while the secon keeps resources for DRD/OTG. */ - func = pci_get_device(pdev->vendor, pdev->device, NULL); - if (!func) - return NULL; + if (pdev->device == CDNS_DEVICE_ID) + return pci_get_device(pdev->vendor, CDNS_DRD_ID, NULL); + else if (pdev->device == CDNS_DRD_ID) + return pci_get_device(pdev->vendor, CDNS_DEVICE_ID, NULL); - if (func->devfn == pdev->devfn) { - func = pci_get_device(pdev->vendor, pdev->device, func); - if (!func) - return NULL; - } - - return func; + return NULL; } static int cdnsp_pci_probe(struct pci_dev *pdev, @@ -230,6 +223,8 @@ static const struct pci_device_id cdnsp_pci_ids[] = { PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID }, { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, CDNS_DRD_IF, PCI_ANY_ID }, + { PCI_VENDOR_ID_CDNS, CDNS_DRD_ID, PCI_ANY_ID, PCI_ANY_ID, + CDNS_DRD_IF, PCI_ANY_ID }, { 0, } }; diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index b23e543b3a3d..07f6068342d4 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -1333,6 +1333,20 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev, ep_ring->dequeue, td->last_trb, ep_trb_dma); + desc = td->preq->pep->endpoint.desc; + + if (ep_seg) { + ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) + / sizeof(*ep_trb)]; + + trace_cdnsp_handle_transfer(ep_ring, + (struct cdnsp_generic_trb *)ep_trb); + + if (pep->skip && usb_endpoint_xfer_isoc(desc) && + td->last_trb != ep_trb) + return -EAGAIN; + } + /* * Skip the Force Stopped Event. The event_trb(ep_trb_dma) * of FSE is not in the current TD pointed by ep_ring->dequeue @@ -1347,7 +1361,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev, goto cleanup; } - desc = td->preq->pep->endpoint.desc; if (!ep_seg) { if (!pep->skip || !usb_endpoint_xfer_isoc(desc)) { /* Something is busted, give up! */ @@ -1374,12 +1387,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev, goto cleanup; } - ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) - / sizeof(*ep_trb)]; - - trace_cdnsp_handle_transfer(ep_ring, - (struct cdnsp_generic_trb *)ep_trb); - if (cdnsp_trb_is_noop(ep_trb)) goto cleanup; @@ -1726,11 +1733,6 @@ static unsigned int count_sg_trbs_needed(struct cdnsp_request *preq) return num_trbs; } -static unsigned int count_isoc_trbs_needed(struct cdnsp_request *preq) -{ - return cdnsp_count_trbs(preq->request.dma, preq->request.length); -} - static void cdnsp_check_trb_math(struct cdnsp_request *preq, int running_total) { if (running_total != preq->request.length) @@ -2192,28 +2194,48 @@ static unsigned int } /* Queue function isoc transfer */ -static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, - struct cdnsp_request *preq) +int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, + struct cdnsp_request *preq) { - int trb_buff_len, td_len, td_remain_len, ret; + unsigned int trb_buff_len, td_len, td_remain_len, block_len; unsigned int burst_count, last_burst_pkt; unsigned int total_pkt_count, max_pkt; struct cdnsp_generic_trb *start_trb; + struct scatterlist *sg = NULL; bool more_trbs_coming = true; struct cdnsp_ring *ep_ring; + unsigned int num_sgs = 0; int running_total = 0; u32 field, length_field; + u64 addr, send_addr; int start_cycle; int trbs_per_td; - u64 addr; - int i; + int i, sent_len, ret; ep_ring = preq->pep->ring; + + td_len = preq->request.length; + + if (preq->request.num_sgs) { + num_sgs = preq->request.num_sgs; + sg = preq->request.sg; + addr = (u64)sg_dma_address(sg); + block_len = sg_dma_len(sg); + trbs_per_td = count_sg_trbs_needed(preq); + } else { + addr = (u64)preq->request.dma; + block_len = td_len; + trbs_per_td = count_trbs_needed(preq); + } + + ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td); + if (ret) + return ret; + start_trb = &ep_ring->enqueue->generic; start_cycle = ep_ring->cycle_state; - td_len = preq->request.length; - addr = (u64)preq->request.dma; td_remain_len = td_len; + send_addr = addr; max_pkt = usb_endpoint_maxp(preq->pep->endpoint.desc); total_pkt_count = DIV_ROUND_UP(td_len, max_pkt); @@ -2225,11 +2247,6 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, burst_count = cdnsp_get_burst_count(pdev, preq, total_pkt_count); last_burst_pkt = cdnsp_get_last_burst_packet_count(pdev, preq, total_pkt_count); - trbs_per_td = count_isoc_trbs_needed(preq); - - ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td); - if (ret) - goto cleanup; /* * Set isoc specific data for the first TRB in a TD. @@ -2248,6 +2265,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, /* Calculate TRB length. */ trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr); + trb_buff_len = min(trb_buff_len, block_len); if (trb_buff_len > td_remain_len) trb_buff_len = td_remain_len; @@ -2256,7 +2274,8 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, trb_buff_len, td_len, preq, more_trbs_coming, 0); - length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0); + length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) | + TRB_INTR_TARGET(0); /* Only first TRB is isoc, overwrite otherwise. */ if (i) { @@ -2281,12 +2300,27 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, } cdnsp_queue_trb(pdev, ep_ring, more_trbs_coming, - lower_32_bits(addr), upper_32_bits(addr), + lower_32_bits(send_addr), upper_32_bits(send_addr), length_field, field); running_total += trb_buff_len; addr += trb_buff_len; td_remain_len -= trb_buff_len; + + sent_len = trb_buff_len; + while (sg && sent_len >= block_len) { + /* New sg entry */ + --num_sgs; + sent_len -= block_len; + if (num_sgs != 0) { + sg = sg_next(sg); + block_len = sg_dma_len(sg); + addr = (u64)sg_dma_address(sg); + addr += sent_len; + } + } + block_len -= sent_len; + send_addr = addr; } /* Check TD length */ @@ -2324,30 +2358,6 @@ cleanup: return ret; } -int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev, - struct cdnsp_request *preq) -{ - struct cdnsp_ring *ep_ring; - u32 ep_state; - int num_trbs; - int ret; - - ep_ring = preq->pep->ring; - ep_state = GET_EP_CTX_STATE(preq->pep->out_ctx); - num_trbs = count_isoc_trbs_needed(preq); - - /* - * Check the ring to guarantee there is enough room for the whole - * request. Do not insert any td of the USB Request to the ring if the - * check failed. - */ - ret = cdnsp_prepare_ring(pdev, ep_ring, ep_state, num_trbs, GFP_ATOMIC); - if (ret) - return ret; - - return cdnsp_queue_isoc_tx(pdev, preq); -} - /**** Command Ring Operations ****/ /* * Generic function for queuing a command TRB on the command ring. diff --git a/drivers/usb/cdns3/cdnsp-trace.h b/drivers/usb/cdns3/cdnsp-trace.h index 5983dfb99653..4b51011eb00b 100644 --- a/drivers/usb/cdns3/cdnsp-trace.h +++ b/drivers/usb/cdns3/cdnsp-trace.h @@ -271,7 +271,6 @@ DECLARE_EVENT_CLASS(cdnsp_log_ctrl, __field(u16, wValue) __field(u16, wIndex) __field(u16, wLength) - __dynamic_array(char, str, CDNSP_MSG_MAX) ), TP_fast_assign( __entry->bRequestType = ctrl->bRequestType; @@ -280,7 +279,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_ctrl, __entry->wIndex = le16_to_cpu(ctrl->wIndex); __entry->wLength = le16_to_cpu(ctrl->wLength); ), - TP_printk("%s", usb_decode_ctrl(__get_str(str), CDNSP_MSG_MAX, + TP_printk("%s", usb_decode_ctrl(__get_buf(CDNSP_MSG_MAX), CDNSP_MSG_MAX, __entry->bRequestType, __entry->bRequest, __entry->wValue, __entry->wIndex, __entry->wLength) @@ -345,7 +344,6 @@ DECLARE_EVENT_CLASS(cdnsp_log_trb, __field(u32, field3) __field(union cdnsp_trb *, trb) __field(dma_addr_t, trb_dma) - __dynamic_array(char, str, CDNSP_MSG_MAX) ), TP_fast_assign( __entry->type = ring->type; @@ -359,7 +357,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_trb, ), TP_printk("%s: %s trb: %p(%pad)", cdnsp_ring_type_string(__entry->type), - cdnsp_decode_trb(__get_str(str), CDNSP_MSG_MAX, + cdnsp_decode_trb(__get_buf(CDNSP_MSG_MAX), CDNSP_MSG_MAX, __entry->field0, __entry->field1, __entry->field2, __entry->field3), __entry->trb, &__entry->trb_dma @@ -544,7 +542,6 @@ DECLARE_EVENT_CLASS(cdnsp_log_ep_ctx, __field(u32, info2) __field(u64, deq) __field(u32, tx_info) - __dynamic_array(char, str, CDNSP_MSG_MAX) ), TP_fast_assign( __entry->info = le32_to_cpu(ctx->ep_info); @@ -552,7 +549,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_ep_ctx, __entry->deq = le64_to_cpu(ctx->deq); __entry->tx_info = le32_to_cpu(ctx->tx_info); ), - TP_printk("%s", cdnsp_decode_ep_context(__get_str(str), CDNSP_MSG_MAX, + TP_printk("%s", cdnsp_decode_ep_context(__get_buf(CDNSP_MSG_MAX), CDNSP_MSG_MAX, __entry->info, __entry->info2, __entry->deq, __entry->tx_info) ) @@ -777,7 +774,6 @@ DECLARE_EVENT_CLASS(cdnsp_log_portsc, TP_STRUCT__entry( __field(u32, portnum) __field(u32, portsc) - __dynamic_array(char, str, CDNSP_MSG_MAX) ), TP_fast_assign( __entry->portnum = portnum; @@ -785,7 +781,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_portsc, ), TP_printk("port-%d: %s", __entry->portnum, - cdnsp_decode_portsc(__get_str(str), CDNSP_MSG_MAX, + cdnsp_decode_portsc(__get_buf(CDNSP_MSG_MAX), CDNSP_MSG_MAX, __entry->portsc) ) ); |
