Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: cdnsp: Fix issue with resuming from L1

In very rare cases after resuming controller from L1 to L0 it reads
registers before the clock UTMI have been enabled and as the result
driver reads incorrect value.
Most of registers are in APB domain clock but some of them (e.g. PORTSC)
are in UTMI domain clock.
After entering to L1 state the UTMI clock can be disabled.
When controller transition from L1 to L0 the port status change event is
reported and in interrupt runtime function driver reads PORTSC.
During this read operation controller synchronize UTMI and APB domain
but UTMI clock is still disabled and in result it reads 0xFFFFFFFF value.
To fix this issue driver increases APB timeout value.

The issue is platform specific and if the default value of APB timeout
is not sufficient then this time should be set Individually for each
platform.

Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver")
Cc: stable <stable@kernel.org>
Signed-off-by: Pawel Laszczak <pawell@cadence.com>
Acked-by: Peter Chen <peter.chen@kernel.org>
Link: https://lore.kernel.org/r/PH7PR07MB953846C57973E4DB134CAA71DDBF2@PH7PR07MB9538.namprd07.prod.outlook.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Pawel Laszczak and committed by
Greg Kroah-Hartman
241e2ce8 59820fde

+45 -2
+29
drivers/usb/cdns3/cdnsp-gadget.c
··· 139 139 (portsc & PORT_CHANGE_BITS), port_regs); 140 140 } 141 141 142 + static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev) 143 + { 144 + struct cdns *cdns = dev_get_drvdata(pdev->dev); 145 + __le32 __iomem *reg; 146 + void __iomem *base; 147 + u32 offset = 0; 148 + u32 val; 149 + 150 + if (!cdns->override_apb_timeout) 151 + return; 152 + 153 + base = &pdev->cap_regs->hc_capbase; 154 + offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP); 155 + reg = base + offset + REG_CHICKEN_BITS_3_OFFSET; 156 + 157 + val = le32_to_cpu(readl(reg)); 158 + val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout); 159 + writel(cpu_to_le32(val), reg); 160 + } 161 + 142 162 static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit) 143 163 { 144 164 __le32 __iomem *reg; ··· 1817 1797 pdev->hcc_params = readl(&pdev->cap_regs->hc_capbase); 1818 1798 pdev->hci_version = HC_VERSION(pdev->hcc_params); 1819 1799 pdev->hcc_params = readl(&pdev->cap_regs->hcc_params); 1800 + 1801 + /* 1802 + * Override the APB timeout value to give the controller more time for 1803 + * enabling UTMI clock and synchronizing APB and UTMI clock domains. 1804 + * This fix is platform specific and is required to fixes issue with 1805 + * reading incorrect value from PORTSC register after resuming 1806 + * from L1 state. 1807 + */ 1808 + cdnsp_set_apb_timeout_value(pdev); 1820 1809 1821 1810 cdnsp_get_rev_cap(pdev); 1822 1811
+3
drivers/usb/cdns3/cdnsp-gadget.h
··· 520 520 #define REG_CHICKEN_BITS_2_OFFSET 0x48 521 521 #define CHICKEN_XDMA_2_TP_CACHE_DIS BIT(28) 522 522 523 + #define REG_CHICKEN_BITS_3_OFFSET 0x4C 524 + #define CHICKEN_APB_TIMEOUT_SET(p, val) (((p) & ~GENMASK(21, 0)) | (val)) 525 + 523 526 /* XBUF Extended Capability ID. */ 524 527 #define XBUF_CAP_ID 0xCB 525 528 #define XBUF_RX_TAG_MASK_0_OFFSET 0x1C
+10 -2
drivers/usb/cdns3/cdnsp-pci.c
··· 28 28 #define PCI_DRIVER_NAME "cdns-pci-usbssp" 29 29 #define PLAT_DRIVER_NAME "cdns-usbssp" 30 30 31 + #define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 32 + 31 33 static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) 32 34 { 33 35 /* ··· 141 139 cdnsp->otg_irq = pdev->irq; 142 140 } 143 141 142 + /* 143 + * Cadence PCI based platform require some longer timeout for APB 144 + * to fixes domain clock synchronization issue after resuming 145 + * controller from L1 state. 146 + */ 147 + cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; 148 + pci_set_drvdata(pdev, cdnsp); 149 + 144 150 if (pci_is_enabled(func)) { 145 151 cdnsp->dev = dev; 146 152 cdnsp->gadget_init = cdnsp_gadget_init; ··· 157 147 if (ret) 158 148 goto free_cdnsp; 159 149 } 160 - 161 - pci_set_drvdata(pdev, cdnsp); 162 150 163 151 device_wakeup_enable(&pdev->dev); 164 152 if (pci_dev_run_wake(pdev))
+3
drivers/usb/cdns3/core.h
··· 79 79 * @pdata: platform data from glue layer 80 80 * @lock: spinlock structure 81 81 * @xhci_plat_data: xhci private data structure pointer 82 + * @override_apb_timeout: hold value of APB timeout. For value 0 the default 83 + * value in CHICKEN_BITS_3 will be preserved. 82 84 * @gadget_init: pointer to gadget initialization function 83 85 */ 84 86 struct cdns { ··· 119 117 struct cdns3_platform_data *pdata; 120 118 spinlock_t lock; 121 119 struct xhci_plat_priv *xhci_plat_data; 120 + u32 override_apb_timeout; 122 121 123 122 int (*gadget_init)(struct cdns *cdns); 124 123 };