diff options
Diffstat (limited to 'drivers/usb/cdns3')
-rw-r--r-- | drivers/usb/cdns3/cdns3-gadget.c | 6 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-imx.c | 2 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-pci-wrap.c | 5 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-plat.c | 4 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-starfive.c | 2 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-ti.c | 124 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-trace.h | 26 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-debug.h | 5 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-ep0.c | 18 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-gadget.c | 67 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-gadget.h | 19 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-pci.c | 37 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-ring.c | 46 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-trace.h | 10 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.c | 9 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.h | 6 | ||||
-rw-r--r-- | drivers/usb/cdns3/drd.c | 10 | ||||
-rw-r--r-- | drivers/usb/cdns3/drd.h | 3 | ||||
-rw-r--r-- | drivers/usb/cdns3/host.c | 15 |
19 files changed, 300 insertions, 114 deletions
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index fd1beb10bba7..d9d8dc05b235 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -1963,6 +1963,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data) unsigned int bit; unsigned long reg; + local_bh_disable(); spin_lock_irqsave(&priv_dev->lock, flags); reg = readl(&priv_dev->regs->usb_ists); @@ -2004,6 +2005,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data) irqend: writel(~0, &priv_dev->regs->ep_ien); spin_unlock_irqrestore(&priv_dev->lock, flags); + local_bh_enable(); return ret; } @@ -3468,7 +3470,7 @@ __must_hold(&cdns->lock) return 0; } -static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated) +static int cdns3_gadget_resume(struct cdns *cdns, bool lost_power) { struct cdns3_device *priv_dev = cdns->gadget_dev; @@ -3476,7 +3478,7 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated) return 0; cdns3_gadget_config(priv_dev); - if (hibernated) + if (lost_power) writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf); return 0; diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 281de47e2a3b..a2f041e1707c 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -422,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match); static struct platform_driver cdns_imx_driver = { .probe = cdns_imx_probe, - .remove_new = cdns_imx_remove, + .remove = 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 1f6320d98a76..3b3b3dc75f35 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -37,9 +37,6 @@ struct cdns3_wrap { #define PCI_DRIVER_NAME "cdns3-pci-usbss" #define PLAT_DRIVER_NAME "cdns-usb3" -#define CDNS_VENDOR_ID 0x17cd -#define CDNS_DEVICE_ID 0x0100 - static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev) { struct pci_dev *func; @@ -190,7 +187,7 @@ static void cdns3_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id cdns3_pci_ids[] = { - { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, + { PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USBSS) }, { 0, } }; diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 3ef8e3c872a3..735df88774e4 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -179,8 +179,6 @@ err_phy3_init: /** * cdns3_plat_remove() - unbind drd driver and clean up * @pdev: Pointer to Linux platform device - * - * Returns 0 on success otherwise negative errno */ static void cdns3_plat_remove(struct platform_device *pdev) { @@ -327,7 +325,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match); static struct platform_driver cdns3_driver = { .probe = cdns3_plat_probe, - .remove_new = cdns3_plat_remove, + .remove = 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 index c04d196abd87..2ff7f2b48cc2 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -230,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); static struct platform_driver cdns_starfive_driver = { .probe = cdns_starfive_probe, - .remove_new = cdns_starfive_remove, + .remove = cdns_starfive_remove, .driver = { .name = "cdns3-starfive", .of_match_table = cdns_starfive_of_match, diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 5945c4b1e11f..302ebf6d8e53 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -16,6 +16,7 @@ #include <linux/of_platform.h> #include <linux/pm_runtime.h> #include <linux/property.h> +#include "core.h" /* USB Wrapper register offsets */ #define USBSS_PID 0x0 @@ -57,6 +58,7 @@ struct cdns_ti { unsigned vbus_divider:1; struct clk *usb2_refclk; struct clk *lpm_clk; + int usb2_refclk_rate_code; }; static const int cdns_ti_rate_table[] = { /* in KHZ */ @@ -85,15 +87,62 @@ static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value) writel(value, data->usbss + offset); } +static struct cdns3_platform_data cdns_ti_pdata = { + .quirks = CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE, /* Errata i2409 */ +}; + +static const struct of_dev_auxdata cdns_ti_auxdata[] = { + { + .compatible = "cdns,usb3", + .platform_data = &cdns_ti_pdata, + }, + {}, +}; + +static void cdns_ti_reset_and_init_hw(struct cdns_ti *data) +{ + u32 reg; + + /* assert RESET */ + reg = cdns_ti_readl(data, USBSS_W1); + reg &= ~USBSS_W1_PWRUP_RST; + cdns_ti_writel(data, USBSS_W1, reg); + + /* set static config */ + reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); + reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK; + reg |= data->usb2_refclk_rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT; + + reg &= ~USBSS1_STATIC_VBUS_SEL_MASK; + if (data->vbus_divider) + reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT; + + cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg); + reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); + + /* set USB2_ONLY mode if requested */ + reg = cdns_ti_readl(data, USBSS_W1); + if (data->usb2_only) + reg |= USBSS_W1_USB2_ONLY; + + /* set default modestrap */ + reg |= USBSS_W1_MODESTRAP_SEL; + reg &= ~USBSS_W1_MODESTRAP_MASK; + reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT; + cdns_ti_writel(data, USBSS_W1, reg); + + /* de-assert RESET */ + reg |= USBSS_W1_PWRUP_RST; + cdns_ti_writel(data, USBSS_W1, reg); +} + static int cdns_ti_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; struct cdns_ti *data; - int error; - u32 reg; - int rate_code, i; unsigned long rate; + int error, i; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -133,7 +182,17 @@ static int cdns_ti_probe(struct platform_device *pdev) return -EINVAL; } - rate_code = i; + data->usb2_refclk_rate_code = i; + data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); + data->usb2_only = device_property_read_bool(dev, "ti,usb2-only"); + + /* + * The call below to pm_runtime_get_sync() MIGHT reset hardware, if it + * detects it as uninitialised. We want to enforce a reset at probe, + * and so do it manually here. This means the first runtime_resume() + * will be a no-op. + */ + cdns_ti_reset_and_init_hw(data); pm_runtime_enable(dev); error = pm_runtime_get_sync(dev); @@ -142,41 +201,7 @@ static int cdns_ti_probe(struct platform_device *pdev) goto err; } - /* assert RESET */ - reg = cdns_ti_readl(data, USBSS_W1); - reg &= ~USBSS_W1_PWRUP_RST; - cdns_ti_writel(data, USBSS_W1, reg); - - /* set static config */ - reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); - reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK; - reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT; - - reg &= ~USBSS1_STATIC_VBUS_SEL_MASK; - data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); - if (data->vbus_divider) - reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT; - - cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg); - reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); - - /* set USB2_ONLY mode if requested */ - reg = cdns_ti_readl(data, USBSS_W1); - data->usb2_only = device_property_read_bool(dev, "ti,usb2-only"); - if (data->usb2_only) - reg |= USBSS_W1_USB2_ONLY; - - /* set default modestrap */ - reg |= USBSS_W1_MODESTRAP_SEL; - reg &= ~USBSS_W1_MODESTRAP_MASK; - reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT; - cdns_ti_writel(data, USBSS_W1, reg); - - /* de-assert RESET */ - reg |= USBSS_W1_PWRUP_RST; - cdns_ti_writel(data, USBSS_W1, reg); - - error = of_platform_populate(node, NULL, NULL, dev); + error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev); if (error) { dev_err(dev, "failed to create children: %d\n", error); goto err; @@ -211,6 +236,24 @@ static void cdns_ti_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); } +static int cdns_ti_runtime_resume(struct device *dev) +{ + const u32 mask = USBSS_W1_PWRUP_RST | USBSS_W1_MODESTRAP_SEL; + struct cdns_ti *data = dev_get_drvdata(dev); + u32 w1; + + w1 = cdns_ti_readl(data, USBSS_W1); + if ((w1 & mask) != mask) + cdns_ti_reset_and_init_hw(data); + + return 0; +} + +static const struct dev_pm_ops cdns_ti_pm_ops = { + RUNTIME_PM_OPS(NULL, cdns_ti_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + static const struct of_device_id cdns_ti_of_match[] = { { .compatible = "ti,j721e-usb", }, { .compatible = "ti,am64-usb", }, @@ -220,10 +263,11 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match); static struct platform_driver cdns_ti_driver = { .probe = cdns_ti_probe, - .remove_new = cdns_ti_remove, + .remove = cdns_ti_remove, .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, + .pm = pm_ptr(&cdns_ti_pm_ops), }, }; diff --git a/drivers/usb/cdns3/cdns3-trace.h b/drivers/usb/cdns3/cdns3-trace.h index 40db89e3333c..c4e542f1b9b7 100644 --- a/drivers/usb/cdns3/cdns3-trace.h +++ b/drivers/usb/cdns3/cdns3-trace.h @@ -33,7 +33,7 @@ TRACE_EVENT(cdns3_halt, __field(u8, flush) ), TP_fast_assign( - __assign_str(name, ep_priv->name); + __assign_str(name); __entry->halt = halt; __entry->flush = flush; ), @@ -49,8 +49,8 @@ TRACE_EVENT(cdns3_wa1, __string(msg, msg) ), TP_fast_assign( - __assign_str(ep_name, ep_priv->name); - __assign_str(msg, msg); + __assign_str(ep_name); + __assign_str(msg); ), TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg)) ); @@ -63,8 +63,8 @@ TRACE_EVENT(cdns3_wa2, __string(msg, msg) ), TP_fast_assign( - __assign_str(ep_name, ep_priv->name); - __assign_str(msg, msg); + __assign_str(ep_name); + __assign_str(msg); ), TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg)) ); @@ -77,7 +77,7 @@ DECLARE_EVENT_CLASS(cdns3_log_doorbell, __field(u32, ep_trbaddr) ), TP_fast_assign( - __assign_str(name, ep_name); + __assign_str(name); __entry->ep_trbaddr = ep_trbaddr; ), TP_printk("%s, ep_trbaddr %08x", __get_str(name), @@ -125,7 +125,7 @@ DECLARE_EVENT_CLASS(cdns3_log_epx_irq, __field(u32, use_streams) ), TP_fast_assign( - __assign_str(ep_name, priv_ep->name); + __assign_str(ep_name); __entry->ep_sts = readl(&priv_dev->regs->ep_sts); __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr); __entry->ep_last_sid = priv_ep->last_stream_id; @@ -214,7 +214,7 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __field(unsigned int, stream_id) ), TP_fast_assign( - __assign_str(name, req->priv_ep->name); + __assign_str(name); __entry->req = req; __entry->buf = req->request.buf; __entry->actual = req->request.actual; @@ -294,7 +294,7 @@ DECLARE_EVENT_CLASS(cdns3_stream_split_transfer_len, __field(unsigned int, stream_id) ), TP_fast_assign( - __assign_str(name, req->priv_ep->name); + __assign_str(name); __entry->req = req; __entry->actual = req->request.length; __entry->length = req->request.actual; @@ -329,7 +329,7 @@ DECLARE_EVENT_CLASS(cdns3_log_aligned_request, __field(u32, aligned_buf_size) ), TP_fast_assign( - __assign_str(name, priv_req->priv_ep->name); + __assign_str(name); __entry->req = &priv_req->request; __entry->buf = priv_req->request.buf; __entry->dma = priv_req->request.dma; @@ -364,7 +364,7 @@ DECLARE_EVENT_CLASS(cdns3_log_map_request, __field(dma_addr_t, dma) ), TP_fast_assign( - __assign_str(name, priv_req->priv_ep->name); + __assign_str(name); __entry->req = &priv_req->request; __entry->buf = priv_req->request.buf; __entry->dma = priv_req->request.dma; @@ -395,7 +395,7 @@ DECLARE_EVENT_CLASS(cdns3_log_trb, __field(unsigned int, last_stream_id) ), TP_fast_assign( - __assign_str(name, priv_ep->name); + __assign_str(name); __entry->trb = trb; __entry->buffer = le32_to_cpu(trb->buffer); __entry->length = le32_to_cpu(trb->length); @@ -467,7 +467,7 @@ DECLARE_EVENT_CLASS(cdns3_log_ep, __field(u8, dequeue) ), TP_fast_assign( - __assign_str(name, priv_ep->name); + __assign_str(name); __entry->maxpacket = priv_ep->endpoint.maxpacket; __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit; __entry->max_streams = priv_ep->endpoint.max_streams; diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h index cd138acdcce1..86860686d836 100644 --- a/drivers/usb/cdns3/cdnsp-debug.h +++ b/drivers/usb/cdns3/cdnsp-debug.h @@ -327,12 +327,13 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0, case TRB_RESET_EP: case TRB_HALT_ENDPOINT: ret = scnprintf(str, size, - "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", + "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c %c", cdnsp_trb_type_string(type), ep_num, ep_id % 2 ? "out" : "in", TRB_TO_EP_INDEX(field3), field1, field0, TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + field3 & TRB_CYCLE ? 'C' : 'c', + field3 & TRB_ESP ? 'P' : 'p'); break; case TRB_STOP_RING: ret = scnprintf(str, size, diff --git a/drivers/usb/cdns3/cdnsp-ep0.c b/drivers/usb/cdns3/cdnsp-ep0.c index f317d3c84781..5cd9b898ce97 100644 --- a/drivers/usb/cdns3/cdnsp-ep0.c +++ b/drivers/usb/cdns3/cdnsp-ep0.c @@ -414,6 +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; + struct cdnsp_ep *pep; int ret = -EINVAL; u16 len; @@ -427,10 +428,21 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev) goto out; } + pep = &pdev->eps[0]; + /* Restore the ep0 to Stopped/Running state. */ - if (pdev->eps[0].ep_state & EP_HALTED) { - trace_cdnsp_ep0_halted("Restore to normal state"); - cdnsp_halt_endpoint(pdev, &pdev->eps[0], 0); + if (pep->ep_state & EP_HALTED) { + if (GET_EP_CTX_STATE(pep->out_ctx) == EP_STATE_HALTED) + cdnsp_halt_endpoint(pdev, pep, 0); + + /* + * Halt Endpoint Command for SSP2 for ep0 preserve current + * endpoint state and driver has to synchronize the + * software endpoint state with endpoint output context + * state. + */ + pep->ep_state &= ~EP_HALTED; + pep->ep_state |= EP_STOPPED; } /* diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 4a3f0f958256..55f95f41b3b4 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/pci.h> #include <linux/irq.h> #include <linux/dmi.h> @@ -28,7 +29,8 @@ unsigned int cdnsp_port_speed(unsigned int port_status) { /*Detect gadget speed based on PORTSC register*/ - if (DEV_SUPERSPEEDPLUS(port_status)) + if (DEV_SUPERSPEEDPLUS(port_status) || + DEV_SSP_GEN1x2(port_status) || DEV_SSP_GEN2x2(port_status)) return USB_SPEED_SUPER_PLUS; else if (DEV_SUPERSPEED(port_status)) return USB_SPEED_SUPER; @@ -138,6 +140,26 @@ static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev, (portsc & PORT_CHANGE_BITS), port_regs); } +static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev) +{ + struct cdns *cdns = dev_get_drvdata(pdev->dev); + __le32 __iomem *reg; + void __iomem *base; + u32 offset = 0; + u32 val; + + if (!cdns->override_apb_timeout) + return; + + base = &pdev->cap_regs->hc_capbase; + offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP); + reg = base + offset + REG_CHICKEN_BITS_3_OFFSET; + + val = le32_to_cpu(readl(reg)); + val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout); + writel(cpu_to_le32(val), reg); +} + static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit) { __le32 __iomem *reg; @@ -526,6 +548,7 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev) dma_addr_t cmd_deq_dma; union cdnsp_trb *event; u32 cycle_state; + u32 retry = 10; int ret, val; u64 cmd_dma; u32 flags; @@ -557,8 +580,23 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev) flags = le32_to_cpu(event->event_cmd.flags); /* Check the owner of the TRB. */ - if ((flags & TRB_CYCLE) != cycle_state) + if ((flags & TRB_CYCLE) != cycle_state) { + /* + * Give some extra time to get chance controller + * to finish command before returning error code. + * Checking CMD_RING_BUSY is not sufficient because + * this bit is cleared to '0' when the Command + * Descriptor has been executed by controller + * and not when command completion event has + * be added to event ring. + */ + if (retry--) { + udelay(20); + continue; + } + return -EINVAL; + } cmd_dma = le64_to_cpu(event->event_cmd.cmd_trb); @@ -1671,12 +1709,12 @@ static int cdnsp_gadget_init_endpoints(struct cdnsp_device *pdev) "CTRL: %s, INT: %s, BULK: %s, ISOC %s, " "SupDir IN: %s, OUT: %s\n", pep->name, 1024, - (pep->endpoint.caps.type_control) ? "yes" : "no", - (pep->endpoint.caps.type_int) ? "yes" : "no", - (pep->endpoint.caps.type_bulk) ? "yes" : "no", - (pep->endpoint.caps.type_iso) ? "yes" : "no", - (pep->endpoint.caps.dir_in) ? "yes" : "no", - (pep->endpoint.caps.dir_out) ? "yes" : "no"); + str_yes_no(pep->endpoint.caps.type_control), + str_yes_no(pep->endpoint.caps.type_int), + str_yes_no(pep->endpoint.caps.type_bulk), + str_yes_no(pep->endpoint.caps.type_iso), + str_yes_no(pep->endpoint.caps.dir_in), + str_yes_no(pep->endpoint.caps.dir_out)); INIT_LIST_HEAD(&pep->pending_list); } @@ -1772,6 +1810,8 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev) reg += cdnsp_find_next_ext_cap(reg, 0, RTL_REV_CAP); pdev->rev_cap = reg; + pdev->rtl_revision = readl(&pdev->rev_cap->rtl_revision); + dev_info(pdev->dev, "Rev: %08x/%08x, eps: %08x, buff: %08x/%08x\n", readl(&pdev->rev_cap->ctrl_revision), readl(&pdev->rev_cap->rtl_revision), @@ -1797,6 +1837,15 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev) pdev->hci_version = HC_VERSION(pdev->hcc_params); pdev->hcc_params = readl(&pdev->cap_regs->hcc_params); + /* + * Override the APB timeout value to give the controller more time for + * enabling UTMI clock and synchronizing APB and UTMI clock domains. + * This fix is platform specific and is required to fixes issue with + * reading incorrect value from PORTSC register after resuming + * from L1 state. + */ + cdnsp_set_apb_timeout_value(pdev); + cdnsp_get_rev_cap(pdev); /* Make sure the Device Controller is halted. */ @@ -1973,7 +2022,7 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup) return 0; } -static int cdnsp_gadget_resume(struct cdns *cdns, bool hibernated) +static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power) { struct cdnsp_device *pdev = cdns->gadget_dev; enum usb_device_speed max_speed; diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index dbee6f085277..a91cca509db0 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -285,11 +285,15 @@ struct cdnsp_port_regs { #define XDEV_HS (0x3 << 10) #define XDEV_SS (0x4 << 10) #define XDEV_SSP (0x5 << 10) +#define XDEV_SSP1x2 (0x6 << 10) +#define XDEV_SSP2x2 (0x7 << 10) #define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0 << 10)) #define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS) #define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS) #define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS) #define DEV_SUPERSPEEDPLUS(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP) +#define DEV_SSP_GEN1x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP1x2) +#define DEV_SSP_GEN2x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP2x2) #define DEV_SUPERSPEED_ANY(p) (((p) & DEV_SPEED_MASK) >= XDEV_SS) #define DEV_PORT_SPEED(p) (((p) >> 10) & 0x0f) /* Port Link State Write Strobe - set this when changing link state */ @@ -520,6 +524,9 @@ struct cdnsp_rev_cap { #define REG_CHICKEN_BITS_2_OFFSET 0x48 #define CHICKEN_XDMA_2_TP_CACHE_DIS BIT(28) +#define REG_CHICKEN_BITS_3_OFFSET 0x4C +#define CHICKEN_APB_TIMEOUT_SET(p, val) (((p) & ~GENMASK(21, 0)) | (val)) + /* XBUF Extended Capability ID. */ #define XBUF_CAP_ID 0xCB #define XBUF_RX_TAG_MASK_0_OFFSET 0x1C @@ -811,6 +818,7 @@ struct cdnsp_stream_info { * generate Missed Service Error Event. * Set skip flag when receive a Missed Service Error Event and * process the missed tds on the endpoint ring. + * @wa1_nop_trb: hold pointer to NOP trb. */ struct cdnsp_ep { struct usb_ep endpoint; @@ -838,6 +846,8 @@ struct cdnsp_ep { #define EP_UNCONFIGURED BIT(7) bool skip; + union cdnsp_trb *wa1_nop_trb; + }; /** @@ -977,6 +987,12 @@ enum cdnsp_setup_dev { #define STREAM_ID_FOR_TRB(p) ((((p)) << 16) & GENMASK(31, 16)) #define SCT_FOR_TRB(p) (((p) << 1) & 0x7) +/* + * Halt Endpoint Command TRB field. + * The ESP bit only exists in the SSP2 controller. + */ +#define TRB_ESP BIT(9) + /* Link TRB specific fields. */ #define TRB_TC BIT(1) @@ -1354,6 +1370,7 @@ struct cdnsp_port { * @rev_cap: Controller Capabilities Registers. * @hcs_params1: Cached register copies of read-only HCSPARAMS1 * @hcc_params: Cached register copies of read-only HCCPARAMS1 + * @rtl_revision: Cached controller rtl revision. * @setup: Temporary buffer for setup packet. * @ep0_preq: Internal allocated request used during enumeration. * @ep0_stage: ep0 stage during enumeration process. @@ -1408,6 +1425,8 @@ struct cdnsp_device { __u32 hcs_params1; __u32 hcs_params3; __u32 hcc_params; + #define RTL_REVISION_NEW_LPM 0x2700 + __u32 rtl_revision; /* Lock used in interrupt thread context. */ spinlock_t lock; struct usb_ctrlrequest setup; diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 0725668ffea4..8c361b8394e9 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -28,10 +28,7 @@ #define PCI_DRIVER_NAME "cdns-pci-usbssp" #define PLAT_DRIVER_NAME "cdns-usbssp" -#define CDNS_VENDOR_ID 0x17cd -#define CDNS_DEVICE_ID 0x0200 -#define CDNS_DRD_ID 0x0100 -#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80) +#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { @@ -40,10 +37,10 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) * Platform has two function. The fist keeps resources for * Host/Device while the secon keeps resources for DRD/OTG. */ - 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 (pdev->device == PCI_DEVICE_ID_CDNS_USBSSP) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSS, NULL); + if (pdev->device == PCI_DEVICE_ID_CDNS_USBSS) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSSP, NULL); return NULL; } @@ -144,6 +141,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, cdnsp->otg_irq = pdev->irq; } + /* + * Cadence PCI based platform require some longer timeout for APB + * to fixes domain clock synchronization issue after resuming + * controller from L1 state. + */ + cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; + pci_set_drvdata(pdev, cdnsp); + if (pci_is_enabled(func)) { cdnsp->dev = dev; cdnsp->gadget_init = cdnsp_gadget_init; @@ -153,8 +158,6 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, goto free_cdnsp; } - pci_set_drvdata(pdev, cdnsp); - device_wakeup_enable(&pdev->dev); if (pci_dev_run_wake(pdev)) pm_runtime_put_noidle(&pdev->dev); @@ -220,18 +223,18 @@ static const struct dev_pm_ops cdnsp_pci_pm_ops = { }; static const struct pci_device_id cdnsp_pci_ids[] = { - { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, - 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 }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), + .class = PCI_CLASS_SERIAL_USB_DEVICE }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), + .class = PCI_CLASS_SERIAL_USB_CDNS }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSS), + .class = PCI_CLASS_SERIAL_USB_CDNS }, { 0, } }; static struct pci_driver cdnsp_pci_driver = { .name = "cdnsp-pci", - .id_table = &cdnsp_pci_ids[0], + .id_table = cdnsp_pci_ids, .probe = cdnsp_pci_probe, .remove = cdnsp_pci_remove, .driver = { diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 02f297f5637d..0758f171f73e 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -308,7 +308,8 @@ static bool cdnsp_ring_ep_doorbell(struct cdnsp_device *pdev, writel(db_value, reg_addr); - cdnsp_force_l0_go(pdev); + if (pdev->rtl_revision < RTL_REVISION_NEW_LPM) + cdnsp_force_l0_go(pdev); /* Doorbell was set. */ return true; @@ -402,7 +403,7 @@ static u64 cdnsp_get_hw_deq(struct cdnsp_device *pdev, struct cdnsp_stream_ctx *st_ctx; struct cdnsp_ep *pep; - pep = &pdev->eps[stream_id]; + pep = &pdev->eps[ep_index]; if (pep->ep_state & EP_HAS_STREAMS) { st_ctx = &pep->stream_info.stream_ctx_array[stream_id]; @@ -718,7 +719,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev, seg = cdnsp_trb_in_td(pdev, cur_td->start_seg, cur_td->first_trb, cur_td->last_trb, hw_deq); - if (seg && (pep->ep_state & EP_ENABLED)) + if (seg && (pep->ep_state & EP_ENABLED) && + !(pep->ep_state & EP_DIS_IN_RROGRESS)) cdnsp_find_new_dequeue_state(pdev, pep, preq->request.stream_id, cur_td, &deq_state); else @@ -736,7 +738,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev, * During disconnecting all endpoint will be disabled so we don't * have to worry about updating dequeue pointer. */ - if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING) { + if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING || + pep->ep_state & EP_DIS_IN_RROGRESS) { status = -ESHUTDOWN; ret = cdnsp_cmd_set_deq(pdev, pep, &deq_state); } @@ -769,7 +772,9 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id) } if (port_id != old_port) { - cdnsp_disable_slot(pdev); + if (pdev->slot_id) + cdnsp_disable_slot(pdev); + pdev->active_port = port; cdnsp_enable_slot(pdev); } @@ -1905,6 +1910,23 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) return ret; /* + * workaround 1: STOP EP command on LINK TRB with TC bit set to 1 + * causes that internal cycle bit can have incorrect state after + * command complete. In consequence empty transfer ring can be + * incorrectly detected when EP is resumed. + * NOP TRB before LINK TRB avoid such scenario. STOP EP command is + * then on NOP TRB and internal cycle bit is not changed and have + * correct value. + */ + if (pep->wa1_nop_trb) { + field = le32_to_cpu(pep->wa1_nop_trb->trans_event.flags); + field ^= TRB_CYCLE; + + pep->wa1_nop_trb->trans_event.flags = cpu_to_le32(field); + pep->wa1_nop_trb = NULL; + } + + /* * Don't give the first TRB to the hardware (by toggling the cycle bit) * until we've finished creating all the other TRBs. The ring's cycle * state may change as we enqueue the other TRBs, so save it too. @@ -1999,6 +2021,17 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) send_addr = addr; } + if (cdnsp_trb_is_link(ring->enqueue + 1)) { + field = TRB_TYPE(TRB_TR_NOOP) | TRB_IOC; + if (!ring->cycle_state) + field |= TRB_CYCLE; + + pep->wa1_nop_trb = ring->enqueue; + + cdnsp_queue_trb(pdev, ring, 0, 0x0, 0x0, + TRB_INTR_TARGET(0), field); + } + cdnsp_check_trb_math(preq, enqd_len); ret = cdnsp_giveback_first_trb(pdev, pep, preq->request.stream_id, start_cycle, start_trb); @@ -2452,7 +2485,8 @@ void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, unsigned int ep_index) { cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_HALT_ENDPOINT) | SLOT_ID_FOR_TRB(pdev->slot_id) | - EP_ID_FOR_TRB(ep_index)); + EP_ID_FOR_TRB(ep_index) | + (!ep_index ? TRB_ESP : 0)); } void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num) diff --git a/drivers/usb/cdns3/cdnsp-trace.h b/drivers/usb/cdns3/cdnsp-trace.h index 4b51011eb00b..f2bcf77a5d0a 100644 --- a/drivers/usb/cdns3/cdnsp-trace.h +++ b/drivers/usb/cdns3/cdnsp-trace.h @@ -48,7 +48,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_ep, __field(u8, drbls_count) ), TP_fast_assign( - __assign_str(name, pep->name); + __assign_str(name); __entry->state = pep->ep_state; __entry->stream_id = stream_id; __entry->enabled = pep->ep_state & EP_HAS_STREAMS; @@ -138,7 +138,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_simple, __string(text, msg) ), TP_fast_assign( - __assign_str(text, msg); + __assign_str(text); ), TP_printk("%s", __get_str(text)) ); @@ -303,7 +303,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_bounce, __field(unsigned int, unalign) ), TP_fast_assign( - __assign_str(name, preq->pep->name); + __assign_str(name); __entry->new_buf_len = new_buf_len; __entry->offset = offset; __entry->dma = dma; @@ -470,7 +470,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_request, ), TP_fast_assign( - __assign_str(name, req->pep->name); + __assign_str(name); __entry->request = &req->request; __entry->preq = req; __entry->buf = req->request.buf; @@ -674,7 +674,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_td_info, __field(dma_addr_t, trb_dma) ), TP_fast_assign( - __assign_str(name, preq->pep->name); + __assign_str(name); __entry->request = &preq->request; __entry->preq = preq; __entry->first_trb = preq->td.first_trb; diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 465e9267b49c..1243a5cea91b 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -524,14 +524,13 @@ EXPORT_SYMBOL_GPL(cdns_suspend); int cdns_resume(struct cdns *cdns) { + bool power_lost = cdns_power_is_lost(cdns); enum usb_role real_role; bool role_changed = false; int ret = 0; - if (cdns_power_is_lost(cdns)) { - if (cdns->role_sw) { - cdns->role = cdns_role_get(cdns->role_sw); - } else { + if (power_lost) { + if (!cdns->role_sw) { real_role = cdns_hw_role_state_machine(cdns); if (real_role != cdns->role) { ret = cdns_hw_role_switch(cdns); @@ -553,7 +552,7 @@ int cdns_resume(struct cdns *cdns) } if (cdns->roles[cdns->role]->resume) - cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns)); + cdns->roles[cdns->role]->resume(cdns, power_lost); return 0; } diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 81a9c9d6be08..801be9e61340 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -30,7 +30,7 @@ struct cdns_role_driver { int (*start)(struct cdns *cdns); void (*stop)(struct cdns *cdns); int (*suspend)(struct cdns *cdns, bool do_wakeup); - int (*resume)(struct cdns *cdns, bool hibernated); + int (*resume)(struct cdns *cdns, bool lost_power); const char *name; #define CDNS_ROLE_STATE_INACTIVE 0 #define CDNS_ROLE_STATE_ACTIVE 1 @@ -44,6 +44,7 @@ struct cdns3_platform_data { bool suspend, bool wakeup); unsigned long quirks; #define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0) +#define CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE BIT(1) }; /** @@ -78,6 +79,8 @@ struct cdns3_platform_data { * @pdata: platform data from glue layer * @lock: spinlock structure * @xhci_plat_data: xhci private data structure pointer + * @override_apb_timeout: hold value of APB timeout. For value 0 the default + * value in CHICKEN_BITS_3 will be preserved. * @gadget_init: pointer to gadget initialization function */ struct cdns { @@ -116,6 +119,7 @@ struct cdns { struct cdns3_platform_data *pdata; spinlock_t lock; struct xhci_plat_priv *xhci_plat_data; + u32 override_apb_timeout; int (*gadget_init)(struct cdns *cdns); }; diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 8b936a2e93a0..84fb38a5723a 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -389,7 +389,7 @@ static irqreturn_t cdns_drd_irq(int irq, void *data) int cdns_drd_init(struct cdns *cdns) { void __iomem *regs; - u32 state; + u32 state, reg; int ret; regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res); @@ -433,6 +433,14 @@ int cdns_drd_init(struct cdns *cdns) cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *) &cdns->otg_v1_regs->ien; writel(1, &cdns->otg_v1_regs->simulate); + + if (cdns->pdata && + (cdns->pdata->quirks & CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE)) { + reg = readl(&cdns->otg_v1_regs->susp_ctrl); + reg |= SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE; + writel(reg, &cdns->otg_v1_regs->susp_ctrl); + } + cdns->version = CDNS3_CONTROLLER_V1; } else { dev_err(cdns->dev, "not supported DID=0x%08x\n", state); diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index d72370c321d3..1e2aee14d629 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -193,6 +193,9 @@ struct cdns_otg_irq_regs { /* OTGREFCLK - bitmasks */ #define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31) +/* SUPS_CTRL - bitmasks */ +#define SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE BIT(17) + /* OVERRIDE - bitmasks */ #define OVERRIDE_IDPULLUP BIT(0) /* Only for CDNS3_CONTROLLER_V0 version */ diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index ceca4d839dfd..f0df114c2b53 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -62,7 +62,9 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { .resume_quirk = xhci_cdns3_resume_quirk, }; -static const struct xhci_plat_priv xhci_plat_cdnsp_xhci; +static const struct xhci_plat_priv xhci_plat_cdnsp_xhci = { + .quirks = XHCI_CDNS_SCTX_QUIRK, +}; static int __cdns_host_init(struct cdns *cdns) { @@ -136,6 +138,16 @@ static void cdns_host_exit(struct cdns *cdns) cdns_drd_host_off(cdns); } +static int cdns_host_resume(struct cdns *cdns, bool power_lost) +{ + struct usb_hcd *hcd = platform_get_drvdata(cdns->host_dev); + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + priv->power_lost = power_lost; + + return 0; +} + int cdns_host_init(struct cdns *cdns) { struct cdns_role_driver *rdrv; @@ -146,6 +158,7 @@ int cdns_host_init(struct cdns *cdns) rdrv->start = __cdns_host_init; rdrv->stop = cdns_host_exit; + rdrv->resume = cdns_host_resume; rdrv->state = CDNS_ROLE_STATE_INACTIVE; rdrv->name = "host"; |