From 3a64cca266ba7ad1023e69366b6695e41886ab86 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 24 Jan 2020 21:02:04 +0100 Subject: [PATCH] apm821xx: switch to pending xhci-pci-renesas solution This patch replaces most of the out-of-tree xhci firmware loader for the particular xhci chip from Renesas on the WNDR4700's PCB. What remains is a check in xhci-pci main driver which guards against a 10 second delay during boot of the WNDR4700. This is because the xhci-pci will still try to bud-in, but it doesn't do anything useful, if the firmware isn't loaded, but waste time and fail eventually: [ 375.481868] xhci_hcd 0000:45:00.0: xHCI Host Controller [ 375.487149] xhci_hcd 0000:45:00.0: new USB bus registered, assigned bus number 1 [ 385.494590] xhci_hcd 0000:45:00.0: can't setup: -110 [ 385.499558] xhci_hcd 0000:45:00.0: USB bus 1 deregistered [ 385.504963] xhci_hcd 0000:45:00.0: init 0000:45:00.0 fail, -110 [ 385.510889] xhci_hcd: probe of 0000:45:00.0 failed with error -110 Signed-off-by: Christian Lamparter --- package/kernel/linux/modules/usb.mk | 4 + .../100-usb-xhci-export-few-functions.patch | 128 ++ ...sas-xhci-Add-the-renesas-xhci-driver.patch | 641 ++++++++++ ...as-xhci-Add-ROM-loader-for-uPD720201.patch | 418 +++++++ ...hci-allow-multiple-firmware-versions.patch | 96 ++ ...ovide-a-debugfs-hook-for-erasing-rom.patch | 88 ++ ...mware-loader-for-uPD720201-and-uPD72.patch | 1026 ----------------- ...ndle-uPD720201-and-uPD720202-w-o-ROM.patch | 110 ++ 8 files changed, 1485 insertions(+), 1026 deletions(-) create mode 100644 target/linux/apm821xx/patches-5.4/100-usb-xhci-export-few-functions.patch create mode 100644 target/linux/apm821xx/patches-5.4/101-usb-renesas-xhci-Add-the-renesas-xhci-driver.patch create mode 100644 target/linux/apm821xx/patches-5.4/102-usb-renesas-xhci-Add-ROM-loader-for-uPD720201.patch create mode 100644 target/linux/apm821xx/patches-5.4/103-usb-renesas-xhci-allow-multiple-firmware-versions.patch create mode 100644 target/linux/apm821xx/patches-5.4/104-usb-xhci-provide-a-debugfs-hook-for-erasing-rom.patch delete mode 100644 target/linux/apm821xx/patches-5.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch create mode 100644 target/linux/apm821xx/patches-5.4/801-usb-xhci-handle-uPD720201-and-uPD720202-w-o-ROM.patch diff --git a/package/kernel/linux/modules/usb.mk b/package/kernel/linux/modules/usb.mk index e4f6a226fd..ed1d14be5e 100644 --- a/package/kernel/linux/modules/usb.mk +++ b/package/kernel/linux/modules/usb.mk @@ -1658,6 +1658,9 @@ XHCI_MODULES := xhci-hcd xhci-pci xhci-plat-hcd ifdef CONFIG_TARGET_ramips_mt7621 XHCI_MODULES += xhci-mtk endif +ifdef CONFIG_TARGET_apm821xx_nand + XHCI_MODULES += xhci-pci-renesas +endif XHCI_FILES := $(wildcard $(patsubst %,$(LINUX_DIR)/drivers/usb/host/%.ko,$(XHCI_MODULES))) XHCI_AUTOLOAD := $(patsubst $(LINUX_DIR)/drivers/usb/host/%.ko,%,$(XHCI_FILES)) @@ -1672,6 +1675,7 @@ define KernelPackage/usb3 CONFIG_USB_XHCI_PCI \ CONFIG_USB_XHCI_PLATFORM \ CONFIG_USB_XHCI_MTK \ + CONFIG_USB_XHCI_PCI_RENESAS \ CONFIG_USB_XHCI_HCD_DEBUGGING=n FILES:= \ $(XHCI_FILES) diff --git a/target/linux/apm821xx/patches-5.4/100-usb-xhci-export-few-functions.patch b/target/linux/apm821xx/patches-5.4/100-usb-xhci-export-few-functions.patch new file mode 100644 index 0000000000..fdae5b2955 --- /dev/null +++ b/target/linux/apm821xx/patches-5.4/100-usb-xhci-export-few-functions.patch @@ -0,0 +1,128 @@ +From 9448f23ccd2f6ba782768f92c04fa62cac295f50 Mon Sep 17 00:00:00 2001 +From: Vinod Koul +Date: Mon, 13 Jan 2020 14:10:01 +0530 +Subject: [PATCH 100/104] usb: xhci: export few functions + +Export the xhci_pci_setup(), xhci_pci_probe() xhci_pci_remove(), +xhci_pci_suspend() and xhci_pci_resume() functions as they would be used +by renesas-xhci driver. + +Signed-off-by: Vinod Koul +Signed-off-by: Christian Lamparter [5.4 backport] +--- + drivers/usb/host/xhci-pci.c | 18 +++++++++++------- + drivers/usb/host/xhci-pci.h | 18 ++++++++++++++++++ + 2 files changed, 29 insertions(+), 7 deletions(-) + create mode 100644 drivers/usb/host/xhci-pci.h + +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -15,6 +15,7 @@ + + #include "xhci.h" + #include "xhci-trace.h" ++#include "xhci-pci.h" + + #define SSIC_PORT_NUM 2 + #define SSIC_PORT_CFG2 0x880c +@@ -60,8 +61,6 @@ static const char hcd_name[] = "xhci_hcd + + static struct hc_driver __read_mostly xhci_pci_hc_driver; + +-static int xhci_pci_setup(struct usb_hcd *hcd); +- + static const struct xhci_driver_overrides xhci_pci_overrides __initconst = { + .reset = xhci_pci_setup, + }; +@@ -282,7 +281,7 @@ static void xhci_pme_acpi_rtd3_enable(st + #endif /* CONFIG_ACPI */ + + /* called during probe() after chip reset completes */ +-static int xhci_pci_setup(struct usb_hcd *hcd) ++int xhci_pci_setup(struct usb_hcd *hcd) + { + struct xhci_hcd *xhci; + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); +@@ -307,12 +306,13 @@ static int xhci_pci_setup(struct usb_hcd + /* Find any debug ports */ + return xhci_pci_reinit(xhci, pdev); + } ++EXPORT_SYMBOL_GPL(xhci_pci_setup); + + /* + * We need to register our own PCI probe function (instead of the USB core's + * function) in order to create a second roothub under xHCI. + */ +-static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ++int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) + { + int retval; + struct xhci_hcd *xhci; +@@ -378,8 +378,9 @@ put_runtime_pm: + pm_runtime_put_noidle(&dev->dev); + return retval; + } ++EXPORT_SYMBOL_GPL(xhci_pci_probe); + +-static void xhci_pci_remove(struct pci_dev *dev) ++void xhci_pci_remove(struct pci_dev *dev) + { + struct xhci_hcd *xhci; + +@@ -401,6 +402,7 @@ static void xhci_pci_remove(struct pci_d + + usb_hcd_pci_remove(dev); + } ++EXPORT_SYMBOL_GPL(xhci_pci_remove); + + #ifdef CONFIG_PM + /* +@@ -457,7 +459,7 @@ static void xhci_pme_quirk(struct usb_hc + readl(reg); + } + +-static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) ++int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) + { + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); +@@ -482,8 +484,9 @@ static int xhci_pci_suspend(struct usb_h + + return ret; + } ++EXPORT_SYMBOL_GPL(xhci_pci_suspend); + +-static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) ++int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) + { + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); +@@ -531,6 +534,7 @@ static void xhci_pci_shutdown(struct usb + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) + pci_set_power_state(pdev, PCI_D3hot); + } ++EXPORT_SYMBOL_GPL(xhci_pci_resume); + #endif /* CONFIG_PM */ + + /*-------------------------------------------------------------------------*/ +--- /dev/null ++++ b/drivers/usb/host/xhci-pci.h +@@ -0,0 +1,18 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2008 Intel Corp. */ ++ ++#ifndef XHCI_PCI_H ++#define XHCI_PCI_H ++ ++int xhci_pci_setup(struct usb_hcd *hcd); ++ ++int xhci_pci_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id); ++ ++void xhci_pci_remove(struct pci_dev *dev); ++ ++int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup); ++ ++int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated); ++ ++#endif diff --git a/target/linux/apm821xx/patches-5.4/101-usb-renesas-xhci-Add-the-renesas-xhci-driver.patch b/target/linux/apm821xx/patches-5.4/101-usb-renesas-xhci-Add-the-renesas-xhci-driver.patch new file mode 100644 index 0000000000..d895809ea5 --- /dev/null +++ b/target/linux/apm821xx/patches-5.4/101-usb-renesas-xhci-Add-the-renesas-xhci-driver.patch @@ -0,0 +1,641 @@ +From 9dc92708f091bb931aff2e32aa2da1020fabd2a1 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +Date: Mon, 13 Jan 2020 14:10:02 +0530 +Subject: [PATCH 101/104] usb: renesas-xhci: Add the renesas xhci driver + +This add a new driver for renesas xhci which is basically a firmware +loader for uPD720201 and uPD720202 w/o ROM. It uses xhci-pci driver for +most of the work. + +This is added in Makefile before the xhci-pci driver so that it first +get probed for renesas devices before the xhci-pci driver has a chance +to claim any device. + +This patch adds a firmware loader for the uPD720201K8-711-BAC-A +and uPD720202K8-711-BAA-A variant. Both of these chips are listed +in Renesas' R19UH0078EJ0500 Rev.5.00 "User's Manual: Hardware" as +devices which need the firmware loader on page 2 in order to +work as they "do not support the External ROM". + +The "Firmware Download Sequence" is describe in chapter +"7.1 FW Download Interface" R19UH0078EJ0500 Rev.5.00 page 131. + +The firmware "K2013080.mem" is available from a USB3.0 Host to +PCIe Adapter (PP2U-E card) "Firmware download" archive. An +alternative version can be sourced from Netgear's WNDR4700 GPL +archives. + +The release notes of the PP2U-E's "Firmware Download" ver 2.0.1.3 +(2012-06-15) state that the firmware is for the following devices: + - uPD720201 ES 2.0 sample whose revision ID is 2. + - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. + - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. + +Cc: Yoshihiro Shimoda +Signed-off-by: Christian Lamparter +Signed-off-by: Bjorn Andersson +[vkoul: fixed comments: + used macros for timeout count and delay + removed renesas_fw_alive_check + cleaned renesas_fw_callback + removed recurion for renesas_fw_download + added MODULE_FIRMWARE + add register defines and field names + move to a separate driver] +Signed-off-by: Vinod Koul +Signed-off-by: Christian Lamparter [5.4 backport] +--- + drivers/usb/host/Kconfig | 9 + + drivers/usb/host/Makefile | 1 + + drivers/usb/host/xhci-pci-renesas.c | 557 ++++++++++++++++++++++++++++ + 3 files changed, 567 insertions(+) + create mode 100644 drivers/usb/host/xhci-pci-renesas.c + +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -42,6 +42,15 @@ config USB_XHCI_PCI + depends on USB_PCI + default y + ++config USB_XHCI_PCI_RENESAS ++ tristate "Renesas USB XHCI Driver" ++ depends on USB_XHCI_PCI ++ ---help--- ++ Say 'Y' to enable the support for renesas USB XHCI Driver if ++ you have such a device. These devices need additional firmware, ++ make sure that is available. ++ If unsure, say 'N'. ++ + config USB_XHCI_PLATFORM + tristate "Generic xHCI driver for a platform device" + select USB_XHCI_RCAR if ARCH_RENESAS +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -70,6 +70,7 @@ obj-$(CONFIG_USB_OHCI_HCD_DAVINCI) += oh + obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o + obj-$(CONFIG_USB_FHCI_HCD) += fhci.o + obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o ++obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o + obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o + obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o + obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o +--- /dev/null ++++ b/drivers/usb/host/xhci-pci-renesas.c +@@ -0,0 +1,557 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2019-2020 Linaro Limited */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xhci.h" ++#include "xhci-trace.h" ++#include "xhci-pci.h" ++ ++#define RENESAS_FW_VERSION 0x6C ++#define RENESAS_ROM_CONFIG 0xF0 ++#define RENESAS_FW_STATUS 0xF4 ++#define RENESAS_FW_STATUS_MSB 0xF5 ++#define RENESAS_ROM_STATUS 0xF6 ++#define RENESAS_ROM_STATUS_MSB 0xF7 ++#define RENESAS_DATA0 0xF8 ++#define RENESAS_DATA1 0xFC ++ ++#define RENESAS_FW_VERSION_FIELD GENMASK(23, 7) ++#define RENESAS_FW_VERSION_OFFSET 8 ++ ++#define RENESAS_FW_STATUS_DOWNLOAD_ENABLE BIT(0) ++#define RENESAS_FW_STATUS_LOCK BIT(1) ++#define RENESAS_FW_STATUS_RESULT GENMASK(6, 4) ++ #define RENESAS_FW_STATUS_INVALID 0 ++ #define RENESAS_FW_STATUS_SUCCESS BIT(4) ++ #define RENESAS_FW_STATUS_ERROR BIT(5) ++#define RENESAS_FW_STATUS_SET_DATA0 BIT(8) ++#define RENESAS_FW_STATUS_SET_DATA1 BIT(9) ++ ++#define RENESAS_RETRY 10000 ++#define RENESAS_DELAY 10 ++ ++static struct hc_driver __read_mostly xhci_pci_hc_driver; ++ ++static const struct xhci_driver_overrides xhci_pci_overrides __initconst = { ++ .reset = xhci_pci_setup, ++}; ++ ++static const struct renesas_fw_entry { ++ const char *firmware_name; ++ u16 device; ++ u8 revision; ++ u16 expected_version; ++} renesas_fw_table[] = { ++ /* ++ * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A ++ * are listed in R19UH0078EJ0500 Rev.5.00 as devices which ++ * need the software loader. ++ * ++ * PP2U/ReleaseNote_USB3-201-202-FW.txt: ++ * ++ * Note: This firmware is for the following devices. ++ * - uPD720201 ES 2.0 sample whose revision ID is 2. ++ * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. ++ * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. ++ */ ++ { "K2013080.mem", 0x0014, 0x02, 0x2013 }, ++ { "K2013080.mem", 0x0014, 0x03, 0x2013 }, ++ { "K2013080.mem", 0x0015, 0x02, 0x2013 }, ++}; ++ ++MODULE_FIRMWARE("K2013080.mem"); ++ ++static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev) ++{ ++ const struct renesas_fw_entry *entry; ++ size_t i; ++ ++ /* This loader will only work with a RENESAS device. */ ++ if (!(dev->vendor == PCI_VENDOR_ID_RENESAS)) ++ return NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { ++ entry = &renesas_fw_table[i]; ++ if (entry->device == dev->device && ++ entry->revision == dev->revision) ++ return entry; ++ } ++ ++ return NULL; ++} ++ ++static int renesas_fw_download_image(struct pci_dev *dev, ++ const u32 *fw, ++ size_t step) ++{ ++ size_t i; ++ int err; ++ u8 fw_status; ++ bool data0_or_data1; ++ ++ /* ++ * The hardware does alternate between two 32-bit pages. ++ * (This is because each row of the firmware is 8 bytes). ++ * ++ * for even steps we use DATA0, for odd steps DATA1. ++ */ ++ data0_or_data1 = (step & 1) == 1; ++ ++ /* step+1. Read "Set DATAX" and confirm it is cleared. */ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ err = pci_read_config_byte(dev, RENESAS_FW_STATUS_MSB, ++ &fw_status); ++ if (err) ++ return pcibios_err_to_errno(err); ++ if (!(fw_status & BIT(data0_or_data1))) ++ break; ++ ++ udelay(RENESAS_DELAY); ++ } ++ if (i == RENESAS_RETRY) ++ return -ETIMEDOUT; ++ ++ /* ++ * step+2. Write FW data to "DATAX". ++ * "LSB is left" => force little endian ++ */ ++ err = pci_write_config_dword(dev, data0_or_data1 ? ++ RENESAS_DATA1 : RENESAS_DATA0, ++ (__force u32)cpu_to_le32(fw[step])); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ udelay(100); ++ ++ /* step+3. Set "Set DATAX". */ ++ err = pci_write_config_byte(dev, RENESAS_FW_STATUS_MSB, ++ BIT(data0_or_data1)); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ return 0; ++} ++ ++static int renesas_fw_verify(struct pci_dev *dev, ++ const void *fw_data, ++ size_t length) ++{ ++ const struct renesas_fw_entry *entry = renesas_needs_fw_dl(dev); ++ u16 fw_version_pointer; ++ u16 fw_version; ++ ++ if (!entry) ++ return -EINVAL; ++ ++ /* ++ * The Firmware's Data Format is describe in ++ * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124 ++ */ ++ ++ /* ++ * The bootrom chips of the big brother have sizes up to 64k, let's ++ * assume that's the biggest the firmware can get. ++ */ ++ if (length < 0x1000 || length >= 0x10000) { ++ dev_err(&dev->dev, "firmware is size %zd is not (4k - 64k).", ++ length); ++ return -EINVAL; ++ } ++ ++ /* The First 2 bytes are fixed value (55aa). "LSB on Left" */ ++ if (get_unaligned_le16(fw_data) != 0x55aa) { ++ dev_err(&dev->dev, "no valid firmware header found."); ++ return -EINVAL; ++ } ++ ++ /* verify the firmware version position and print it. */ ++ fw_version_pointer = get_unaligned_le16(fw_data + 4); ++ if (fw_version_pointer + 2 >= length) { ++ dev_err(&dev->dev, ++ "firmware version pointer is outside of the firmware image."); ++ return -EINVAL; ++ } ++ ++ fw_version = get_unaligned_le16(fw_data + fw_version_pointer); ++ dev_dbg(&dev->dev, "got firmware version: %02x.", fw_version); ++ ++ if (fw_version != entry->expected_version) { ++ dev_err(&dev->dev, ++ "firmware version mismatch, expected version: %02x.", ++ entry->expected_version); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int renesas_fw_check_running(struct pci_dev *pdev) ++{ ++ int err; ++ u8 fw_state; ++ ++ /* ++ * Test if the device is actually needing the firmware. As most ++ * BIOSes will initialize the device for us. If the device is ++ * initialized. ++ */ ++ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_state); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ /* ++ * Check if "FW Download Lock" is locked. If it is and the FW is ++ * ready we can simply continue. If the FW is not ready, we have ++ * to give up. ++ */ ++ if (fw_state & RENESAS_FW_STATUS_LOCK) { ++ dev_dbg(&pdev->dev, "FW Download Lock is engaged."); ++ ++ if (fw_state & RENESAS_FW_STATUS_SUCCESS) ++ return 0; ++ ++ dev_err(&pdev->dev, ++ "FW Download Lock is set and FW is not ready. Giving Up."); ++ return -EIO; ++ } ++ ++ /* ++ * Check if "FW Download Enable" is set. If someone (us?) tampered ++ * with it and it can't be resetted, we have to give up too... and ++ * ask for a forgiveness and a reboot. ++ */ ++ if (fw_state & RENESAS_FW_STATUS_DOWNLOAD_ENABLE) { ++ dev_err(&pdev->dev, ++ "FW Download Enable is stale. Giving Up (poweroff/reboot needed)."); ++ return -EIO; ++ } ++ ++ /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */ ++ switch (fw_state & RENESAS_FW_STATUS_RESULT) { ++ case 0: /* No result yet */ ++ dev_dbg(&pdev->dev, "FW is not ready/loaded yet."); ++ ++ /* tell the caller, that this device needs the firmware. */ ++ return 1; ++ ++ case RENESAS_FW_STATUS_SUCCESS: /* Success, device should be working. */ ++ dev_dbg(&pdev->dev, "FW is ready."); ++ return 0; ++ ++ case RENESAS_FW_STATUS_ERROR: /* Error State */ ++ dev_err(&pdev->dev, ++ "hardware is in an error state. Giving up (poweroff/reboot needed)."); ++ return -ENODEV; ++ ++ default: /* All other states are marked as "Reserved states" */ ++ dev_err(&pdev->dev, ++ "hardware is in an invalid state %lx. Giving up (poweroff/reboot needed).", ++ (fw_state & RENESAS_FW_STATUS_RESULT) >> 4); ++ return -EINVAL; ++ } ++} ++ ++static int renesas_fw_download(struct pci_dev *pdev, ++ const struct firmware *fw) ++{ ++ const u32 *fw_data = (const u32 *)fw->data; ++ size_t i; ++ int err; ++ u8 fw_status; ++ ++ /* ++ * For more information and the big picture: please look at the ++ * "Firmware Download Sequence" in "7.1 FW Download Interface" ++ * of R19UH0078EJ0500 Rev.5.00 page 131 ++ */ ++ ++ /* ++ * 0. Set "FW Download Enable" bit in the ++ * "FW Download Control & Status Register" at 0xF4 ++ */ ++ err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, ++ RENESAS_FW_STATUS_DOWNLOAD_ENABLE); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ /* 1 - 10 follow one step after the other. */ ++ for (i = 0; i < fw->size / 4; i++) { ++ err = renesas_fw_download_image(pdev, fw_data, i); ++ if (err) { ++ dev_err(&pdev->dev, ++ "Firmware Download Step %zd failed at position %zd bytes with (%d).", ++ i, i * 4, err); ++ return err; ++ } ++ } ++ ++ /* ++ * This sequence continues until the last data is written to ++ * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1" ++ * is cleared by the hardware beforehand. ++ */ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS_MSB, ++ &fw_status); ++ if (err) ++ return pcibios_err_to_errno(err); ++ if (!(fw_status & (BIT(0) | BIT(1)))) ++ break; ++ ++ udelay(RENESAS_DELAY); ++ } ++ if (i == RENESAS_RETRY) ++ dev_warn(&pdev->dev, "Final Firmware Download step timed out."); ++ ++ /* ++ * 11. After finishing writing the last data of FW, the ++ * System Software must clear "FW Download Enable" ++ */ ++ err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, 0); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ /* 12. Read "Result Code" and confirm it is good. */ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_status); ++ if (err) ++ return pcibios_err_to_errno(err); ++ if (fw_status & RENESAS_FW_STATUS_SUCCESS) ++ break; ++ ++ udelay(RENESAS_DELAY); ++ } ++ if (i == RENESAS_RETRY) { ++ /* Timed out / Error - let's see if we can fix this */ ++ err = renesas_fw_check_running(pdev); ++ switch (err) { ++ case 0: /* ++ * we shouldn't end up here. ++ * maybe it took a little bit longer. ++ * But all should be well? ++ */ ++ break; ++ ++ case 1: /* (No result yet! */ ++ return -ETIMEDOUT; ++ ++ default: ++ return err; ++ } ++ } ++ /* ++ * Optional last step: Engage Firmware Lock ++ * ++ * err = pci_write_config_byte(pdev, 0xF4, BIT(2)); ++ * if (err) ++ * return pcibios_err_to_errno(err); ++ */ ++ ++ return 0; ++} ++ ++struct renesas_fw_ctx { ++ struct pci_dev *pdev; ++ const struct pci_device_id *id; ++ bool resume; ++ const struct renesas_fw_entry *entry; ++}; ++ ++static void renesas_fw_callback(const struct firmware *fw, ++ void *context) ++{ ++ struct renesas_fw_ctx *ctx = context; ++ struct pci_dev *pdev = ctx->pdev; ++ struct device *parent = pdev->dev.parent; ++ int err; ++ ++ if (!fw) { ++ dev_err(&pdev->dev, "firmware failed to load\n"); ++ ++ goto cleanup; ++ } ++ ++ err = renesas_fw_verify(pdev, fw->data, fw->size); ++ if (err) ++ goto cleanup; ++ ++ err = renesas_fw_download(pdev, fw); ++ release_firmware(fw); ++ if (err) { ++ dev_err(&pdev->dev, "firmware failed to download (%d).", err); ++ goto cleanup; ++ } ++ ++ if (ctx->resume) ++ return; ++ ++ err = xhci_pci_probe(pdev, ctx->id); ++ if (!err) { ++ /* everything worked */ ++ devm_kfree(&pdev->dev, ctx); ++ return; ++ } ++ ++cleanup: ++ /* in case of an error - fall through */ ++ dev_info(&pdev->dev, "Unloading driver"); ++ ++ if (parent) ++ device_lock(parent); ++ ++ device_release_driver(&pdev->dev); ++ ++ if (parent) ++ device_unlock(parent); ++ ++ pci_dev_put(pdev); ++} ++ ++static int renesas_fw_alive_check(struct pci_dev *pdev) ++{ ++ const struct renesas_fw_entry *entry; ++ ++ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ ++ entry = renesas_needs_fw_dl(pdev); ++ if (!entry) ++ return 0; ++ ++ return renesas_fw_check_running(pdev); ++} ++ ++static int renesas_fw_download_to_hw(struct pci_dev *pdev, ++ const struct pci_device_id *id, ++ bool do_resume) ++{ ++ const struct renesas_fw_entry *entry; ++ struct renesas_fw_ctx *ctx; ++ int err; ++ ++ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ ++ entry = renesas_needs_fw_dl(pdev); ++ if (!entry) ++ return 0; ++ ++ err = renesas_fw_check_running(pdev); ++ /* Continue ahead, if the firmware is already running. */ ++ if (err == 0) ++ return 0; ++ ++ if (err != 1) ++ return err; ++ ++ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ctx->pdev = pdev; ++ ctx->resume = do_resume; ++ ctx->id = id; ++ ctx->entry = entry; ++ ++ pci_dev_get(pdev); ++ err = request_firmware_nowait(THIS_MODULE, 1, entry->firmware_name, ++ &pdev->dev, GFP_KERNEL, ++ ctx, renesas_fw_callback); ++ if (err) { ++ pci_dev_put(pdev); ++ return err; ++ } ++ ++ /* ++ * The renesas_fw_callback() callback will continue the probe ++ * process, once it aquires the firmware. ++ */ ++ return 1; ++} ++ ++static int renesas_xhci_pci_probe(struct pci_dev *dev, ++ const struct pci_device_id *id) ++{ ++ int retval; ++ ++ /* ++ * Check if this device is a RENESAS uPD720201/2 device. ++ * Otherwise, we can continue with xhci_pci_probe as usual. ++ */ ++ retval = renesas_fw_download_to_hw(dev, id, false); ++ switch (retval) { ++ case 0: ++ break; ++ ++ case 1: /* let it load the firmware and recontinue the probe. */ ++ return 0; ++ ++ default: ++ return retval; ++ }; ++ ++ return xhci_pci_probe(dev, id); ++} ++ ++static void renesas_xhci_pci_remove(struct pci_dev *dev) ++{ ++ if (renesas_fw_alive_check(dev)) { ++ /* ++ * bail out early, if this was a renesas device w/o FW. ++ * Else we might hit the NMI watchdog in xhci_handsake ++ * during xhci_reset as part of the driver's unloading. ++ * which we forced in the renesas_fw_callback(). ++ */ ++ return; ++ } ++ ++ xhci_pci_remove(dev); ++} ++ ++static const struct pci_device_id pci_ids[] = { ++ { PCI_DEVICE(0x1912, 0x0014), ++ .driver_data = (unsigned long)&xhci_pci_hc_driver, ++ }, ++ { PCI_DEVICE(0x1912, 0x0015), ++ .driver_data = (unsigned long)&xhci_pci_hc_driver, ++ }, ++ { /* sentinal */ } ++}; ++MODULE_DEVICE_TABLE(pci, pci_ids); ++ ++static struct pci_driver renesas_xhci_pci_driver = { ++ .name = "renesas xhci", ++ .id_table = pci_ids, ++ ++ .probe = renesas_xhci_pci_probe, ++ .remove = renesas_xhci_pci_remove, ++ /* suspend and resume implemented later */ ++ ++ .shutdown = usb_hcd_pci_shutdown, ++#ifdef CONFIG_PM ++ .driver = { ++ .pm = &usb_hcd_pci_pm_ops ++ }, ++#endif ++}; ++ ++static int __init xhci_pci_init(void) ++{ ++ xhci_init_driver(&xhci_pci_hc_driver, &xhci_pci_overrides); ++#ifdef CONFIG_PM ++ xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend; ++ xhci_pci_hc_driver.pci_resume = xhci_pci_resume; ++#endif ++ return pci_register_driver(&renesas_xhci_pci_driver); ++} ++module_init(xhci_pci_init); ++ ++static void __exit xhci_pci_exit(void) ++{ ++ pci_unregister_driver(&renesas_xhci_pci_driver); ++} ++module_exit(xhci_pci_exit); ++ ++MODULE_DESCRIPTION("xHCI PCI Host Controller Driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/apm821xx/patches-5.4/102-usb-renesas-xhci-Add-ROM-loader-for-uPD720201.patch b/target/linux/apm821xx/patches-5.4/102-usb-renesas-xhci-Add-ROM-loader-for-uPD720201.patch new file mode 100644 index 0000000000..e57ca4bd84 --- /dev/null +++ b/target/linux/apm821xx/patches-5.4/102-usb-renesas-xhci-Add-ROM-loader-for-uPD720201.patch @@ -0,0 +1,418 @@ +From f2abef3e6b43da99351372f2e1a4c6073a0bdfcf Mon Sep 17 00:00:00 2001 +From: Vinod Koul +Date: Mon, 13 Jan 2020 14:10:03 +0530 +Subject: [PATCH 102/104] usb: renesas-xhci: Add ROM loader for uPD720201 + +uPD720201 supports ROM and allows software to program the ROM and boot +from it. Add support for detecting if ROM is present, if so load the ROM +if not programmed earlier. + +Signed-off-by: Vinod Koul +Cc: Yoshihiro Shimoda +Cc: Christian Lamparter +Signed-off-by: Christian Lamparter [5.4 backport] +--- + drivers/usb/host/xhci-pci-renesas.c | 352 ++++++++++++++++++++++++++++ + 1 file changed, 352 insertions(+) + +--- a/drivers/usb/host/xhci-pci-renesas.c ++++ b/drivers/usb/host/xhci-pci-renesas.c +@@ -33,6 +33,20 @@ + #define RENESAS_FW_STATUS_SET_DATA0 BIT(8) + #define RENESAS_FW_STATUS_SET_DATA1 BIT(9) + ++#define RENESAS_ROM_STATUS_ACCESS BIT(0) ++#define RENESAS_ROM_STATUS_ERASE BIT(1) ++#define RENESAS_ROM_STATUS_RELOAD BIT(2) ++#define RENESAS_ROM_STATUS_RESULT GENMASK(6, 4) ++ #define RENESAS_ROM_STATUS_INVALID 0 ++ #define RENESAS_ROM_STATUS_SUCCESS BIT(4) ++ #define RENESAS_ROM_STATUS_ERROR BIT(5) ++#define RENESAS_ROM_STATUS_SET_DATA0 BIT(8) ++#define RENESAS_ROM_STATUS_SET_DATA1 BIT(9) ++#define RENESAS_ROM_STATUS_ROM_EXISTS BIT(15) ++ ++#define RENESAS_ROM_ERASE_MAGIC 0x5A65726F ++#define RENESAS_ROM_WRITE_MAGIC 0x53524F4D ++ + #define RENESAS_RETRY 10000 + #define RENESAS_DELAY 10 + +@@ -190,12 +204,81 @@ static int renesas_fw_verify(struct pci_ + + return 0; + } ++static int renesas_check_rom_state(struct pci_dev *pdev) ++{ ++ const struct renesas_fw_entry *entry; ++ u16 rom_state; ++ u32 version; ++ bool valid_version = false; ++ int err, i; ++ ++ /* check FW version */ ++ err = pci_read_config_dword(pdev, RENESAS_FW_VERSION, &version); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ version &= RENESAS_FW_VERSION_FIELD; ++ version = version >> RENESAS_FW_VERSION_OFFSET; ++ dev_dbg(&pdev->dev, "Found FW version loaded is %x\n", version); ++ ++ /* treat version in renesas_fw_table as correct ones */ ++ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { ++ entry = &renesas_fw_table[i]; ++ if (version == entry->expected_version) { ++ dev_dbg(&pdev->dev, "Detected valid ROM version..\n"); ++ valid_version = true; ++ } ++ } ++ if (valid_version == false) ++ dev_dbg(&pdev->dev, "Didn't find valid ROM version\n"); ++ ++ /* ++ * Test if ROM is present and loaded, if so we can skip everything ++ */ ++ err = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_state); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ if (rom_state & BIT(15)) { ++ /* ROM exists */ ++ dev_dbg(&pdev->dev, "ROM exists\n"); ++ ++ /* Check the "Result Code" Bits (6:4) and act accordingly */ ++ switch (rom_state & RENESAS_ROM_STATUS_RESULT) { ++ case RENESAS_ROM_STATUS_SUCCESS: ++ dev_dbg(&pdev->dev, "Success ROM load..."); ++ /* we have valid version and status so success */ ++ if (valid_version) ++ return 0; ++ break; ++ ++ case RENESAS_ROM_STATUS_INVALID: /* No result yet */ ++ dev_dbg(&pdev->dev, "No result as it is ROM..."); ++ /* we have valid version and status so success */ ++ if (valid_version) ++ return 0; ++ break; ++ ++ case RENESAS_ROM_STATUS_ERROR: /* Error State */ ++ default: /* All other states are marked as "Reserved states" */ ++ dev_err(&pdev->dev, "Invalid ROM.."); ++ break; ++ } ++ } ++ ++ return -EIO; ++} + + static int renesas_fw_check_running(struct pci_dev *pdev) + { + int err; + u8 fw_state; + ++ /* Check if device has ROM and loaded, if so skip everything */ ++ err = renesas_check_rom_state(pdev); ++ if (!err) ++ return err; ++ + /* + * Test if the device is actually needing the firmware. As most + * BIOSes will initialize the device for us. If the device is +@@ -363,12 +446,261 @@ struct renesas_fw_ctx { + const struct renesas_fw_entry *entry; + }; + ++static bool renesas_check_rom(struct pci_dev *pdev) ++{ ++ u16 rom_status; ++ int retval; ++ ++ /* 1. Check if external ROM exists */ ++ retval = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_status); ++ if (retval) ++ return false; ++ ++ rom_status &= RENESAS_ROM_STATUS_ROM_EXISTS; ++ if (rom_status) { ++ dev_dbg(&pdev->dev, "External ROM exists\n"); ++ return true; /* External ROM exists */ ++ } ++ ++ return false; ++} ++ ++static void renesas_rom_erase(struct pci_dev *pdev) ++{ ++ int retval, i; ++ u8 status; ++ ++ dev_dbg(&pdev->dev, "Performing ROM Erase...\n"); ++ retval = pci_write_config_dword(pdev, RENESAS_DATA0, ++ RENESAS_ROM_ERASE_MAGIC); ++ if (retval) { ++ dev_err(&pdev->dev, "ROM erase, magic word write failed: %d\n", ++ pcibios_err_to_errno(retval)); ++ return; ++ } ++ ++ retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); ++ if (retval) { ++ dev_err(&pdev->dev, "ROM status read failed: %d\n", ++ pcibios_err_to_errno(retval)); ++ return; ++ } ++ status |= RENESAS_ROM_STATUS_ERASE; ++ retval = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, status); ++ if (retval) { ++ dev_err(&pdev->dev, "ROM erase set word write failed\n"); ++ return; ++ } ++ ++ /* sleep a bit while ROM is erased */ ++ msleep(20); ++ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, ++ &status); ++ status &= RENESAS_ROM_STATUS_ERASE; ++ if (!status) ++ break; ++ ++ mdelay(RENESAS_DELAY); ++ } ++ ++ if (i == RENESAS_RETRY) ++ dev_dbg(&pdev->dev, "Chip erase timedout: %x\n", status); ++ ++ dev_dbg(&pdev->dev, "ROM Erase... Done success\n"); ++} ++ ++static bool renesas_download_rom(struct pci_dev *pdev, ++ const u32 *fw, size_t step) ++{ ++ bool data0_or_data1; ++ u8 fw_status; ++ size_t i; ++ int err; ++ ++ /* ++ * The hardware does alternate between two 32-bit pages. ++ * (This is because each row of the firmware is 8 bytes). ++ * ++ * for even steps we use DATA0, for odd steps DATA1. ++ */ ++ data0_or_data1 = (step & 1) == 1; ++ ++ /* Read "Set DATAX" and confirm it is cleared. */ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB, ++ &fw_status); ++ if (err) { ++ dev_err(&pdev->dev, "Read ROM Status failed: %d\n", ++ pcibios_err_to_errno(err)); ++ return false; ++ } ++ if (!(fw_status & BIT(data0_or_data1))) ++ break; ++ ++ udelay(RENESAS_DELAY); ++ } ++ if (i == RENESAS_RETRY) { ++ dev_err(&pdev->dev, "Timeout for Set DATAX step: %zd\n", step); ++ return false; ++ } ++ ++ /* ++ * Write FW data to "DATAX". ++ * "LSB is left" => force little endian ++ */ ++ err = pci_write_config_dword(pdev, data0_or_data1 ? ++ RENESAS_DATA1 : RENESAS_DATA0, ++ (__force u32)cpu_to_le32(fw[step])); ++ if (err) { ++ dev_err(&pdev->dev, "Write to DATAX failed: %d\n", ++ pcibios_err_to_errno(err)); ++ return false; ++ } ++ ++ udelay(100); ++ ++ /* Set "Set DATAX". */ ++ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS_MSB, ++ BIT(data0_or_data1)); ++ if (err) { ++ dev_err(&pdev->dev, "Write config for DATAX failed: %d\n", ++ pcibios_err_to_errno(err)); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool renesas_setup_rom(struct pci_dev *pdev, const struct firmware *fw) ++{ ++ const u32 *fw_data = (const u32 *)fw->data; ++ int err, i; ++ u8 status; ++ ++ /* 2. Write magic word to Data0 */ ++ err = pci_write_config_dword(pdev, RENESAS_DATA0, ++ RENESAS_ROM_WRITE_MAGIC); ++ if (err) ++ return false; ++ ++ /* 3. Set External ROM access */ ++ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, ++ RENESAS_ROM_STATUS_ACCESS); ++ if (err) ++ goto remove_bypass; ++ ++ /* 4. Check the result */ ++ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); ++ if (err) ++ goto remove_bypass; ++ status &= GENMASK(6, 4); ++ if (status) { ++ dev_err(&pdev->dev, ++ "setting external rom failed: %x\n", status); ++ goto remove_bypass; ++ } ++ ++ /* 5 to 16 Write FW to DATA0/1 while checking SetData0/1 */ ++ for (i = 0; i < fw->size / 4; i++) { ++ err = renesas_download_rom(pdev, fw_data, i); ++ if (!err) { ++ dev_err(&pdev->dev, ++ "ROM Download Step %d failed at position %d bytes\n", ++ i, i * 4); ++ goto remove_bypass; ++ } ++ } ++ ++ /* ++ * wait till DATA0/1 is cleared ++ */ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB, ++ &status); ++ if (err) ++ goto remove_bypass; ++ if (!(status & (BIT(0) | BIT(1)))) ++ break; ++ ++ udelay(RENESAS_DELAY); ++ } ++ if (i == RENESAS_RETRY) { ++ dev_err(&pdev->dev, "Final Firmware ROM Download step timed out\n"); ++ goto remove_bypass; ++ } ++ ++ /* 17. Remove bypass */ ++ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0); ++ if (err) ++ return false; ++ ++ udelay(10); ++ ++ /* 18. check result */ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); ++ if (err) { ++ dev_err(&pdev->dev, "Read ROM status failed:%d\n", ++ pcibios_err_to_errno(err)); ++ return false; ++ } ++ status &= RENESAS_ROM_STATUS_RESULT; ++ if (status == RENESAS_ROM_STATUS_SUCCESS) { ++ dev_dbg(&pdev->dev, "Download ROM success\n"); ++ break; ++ } ++ udelay(RENESAS_DELAY); ++ } ++ if (i == RENESAS_RETRY) { /* Timed out */ ++ dev_err(&pdev->dev, ++ "Download to external ROM TO: %x\n", status); ++ return false; ++ } ++ ++ dev_dbg(&pdev->dev, "Download to external ROM scuceeded\n"); ++ ++ /* Last step set Reload */ ++ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, ++ RENESAS_ROM_STATUS_RELOAD); ++ if (err) { ++ dev_err(&pdev->dev, "Set ROM execute failed: %d\n", ++ pcibios_err_to_errno(err)); ++ return false; ++ } ++ ++ /* ++ * wait till Reload is cleared ++ */ ++ for (i = 0; i < RENESAS_RETRY; i++) { ++ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); ++ if (err) ++ return false; ++ if (!(status & RENESAS_ROM_STATUS_RELOAD)) ++ break; ++ ++ udelay(RENESAS_DELAY); ++ } ++ if (i == RENESAS_RETRY) { ++ dev_err(&pdev->dev, "ROM Exec timed out: %x\n", status); ++ return false; ++ } ++ ++ return true; ++ ++remove_bypass: ++ pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0); ++ return false; ++} ++ + static void renesas_fw_callback(const struct firmware *fw, + void *context) + { + struct renesas_fw_ctx *ctx = context; + struct pci_dev *pdev = ctx->pdev; + struct device *parent = pdev->dev.parent; ++ bool rom; + int err; + + if (!fw) { +@@ -381,6 +713,25 @@ static void renesas_fw_callback(const st + if (err) + goto cleanup; + ++ /* Check if the device has external ROM */ ++ rom = renesas_check_rom(pdev); ++ if (rom) { ++ /* perfrom chip erase first */ ++ renesas_rom_erase(pdev); ++ ++ /* lets try loading fw on ROM first */ ++ rom = renesas_setup_rom(pdev, fw); ++ if (!rom) { ++ dev_err(&pdev->dev, ++ "ROM load failed, falling back on FW load\n"); ++ } else { ++ dev_dbg(&pdev->dev, "ROM load done..\n"); ++ ++ release_firmware(fw); ++ goto do_probe; ++ } ++ } ++ + err = renesas_fw_download(pdev, fw); + release_firmware(fw); + if (err) { +@@ -388,6 +739,7 @@ static void renesas_fw_callback(const st + goto cleanup; + } + ++do_probe: + if (ctx->resume) + return; + diff --git a/target/linux/apm821xx/patches-5.4/103-usb-renesas-xhci-allow-multiple-firmware-versions.patch b/target/linux/apm821xx/patches-5.4/103-usb-renesas-xhci-allow-multiple-firmware-versions.patch new file mode 100644 index 0000000000..9267bd6152 --- /dev/null +++ b/target/linux/apm821xx/patches-5.4/103-usb-renesas-xhci-allow-multiple-firmware-versions.patch @@ -0,0 +1,96 @@ +From 9c57fab28892fdb73c12b4a066318676d2e73ff0 Mon Sep 17 00:00:00 2001 +From: Vinod Koul +Date: Mon, 13 Jan 2020 14:10:04 +0530 +Subject: [PATCH 103/104] usb: renesas-xhci: allow multiple firmware versions + +Allow multiple firmware file versions in table and load them in +increasing order as we find them in the file system. + +Signed-off-by: Vinod Koul +Cc: Yoshihiro Shimoda +Cc: Christian Lamparter +Signed-off-by: Christian Lamparter [5.4 backport] +--- + drivers/usb/host/xhci-pci-renesas.c | 45 +++++++++++++++++++++++++++-- + 1 file changed, 43 insertions(+), 2 deletions(-) + +--- a/drivers/usb/host/xhci-pci-renesas.c ++++ b/drivers/usb/host/xhci-pci-renesas.c +@@ -73,13 +73,20 @@ static const struct renesas_fw_entry { + * - uPD720201 ES 2.0 sample whose revision ID is 2. + * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. + * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. ++ * ++ * Entry expected_version should be kept in decreasing order for a ++ * chip, so that driver will pick latest version and if that fails ++ * then next one will be picked + */ + { "K2013080.mem", 0x0014, 0x02, 0x2013 }, ++ { "K2026090.mem", 0x0014, 0x03, 0x2026 }, + { "K2013080.mem", 0x0014, 0x03, 0x2013 }, ++ { "K2026090.mem", 0x0015, 0x02, 0x2026 }, + { "K2013080.mem", 0x0015, 0x02, 0x2013 }, + }; + + MODULE_FIRMWARE("K2013080.mem"); ++MODULE_FIRMWARE("K2026090.mem"); + + static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev) + { +@@ -100,6 +107,24 @@ static const struct renesas_fw_entry *re + return NULL; + } + ++static const struct ++renesas_fw_entry *renesas_get_next_entry(struct pci_dev *dev, ++ const struct renesas_fw_entry *entry) ++{ ++ const struct renesas_fw_entry *next_entry; ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { ++ next_entry = &renesas_fw_table[i]; ++ if (next_entry->device == dev->device && ++ next_entry->revision == dev->revision && ++ next_entry->expected_version < entry->expected_version) ++ return next_entry; ++ } ++ ++ return NULL; ++} ++ + static int renesas_fw_download_image(struct pci_dev *dev, + const u32 *fw, + size_t step) +@@ -700,13 +725,29 @@ static void renesas_fw_callback(const st + struct renesas_fw_ctx *ctx = context; + struct pci_dev *pdev = ctx->pdev; + struct device *parent = pdev->dev.parent; ++ const struct renesas_fw_entry *next_entry; + bool rom; + int err; + + if (!fw) { + dev_err(&pdev->dev, "firmware failed to load\n"); +- +- goto cleanup; ++ /* ++ * we didn't find firmware, check if we have another ++ * entry for this device ++ */ ++ next_entry = renesas_get_next_entry(ctx->pdev, ctx->entry); ++ if (next_entry) { ++ ctx->entry = next_entry; ++ dev_dbg(&pdev->dev, "Found next entry, requesting: %s\n", ++ next_entry->firmware_name); ++ request_firmware_nowait(THIS_MODULE, 1, ++ next_entry->firmware_name, ++ &pdev->dev, GFP_KERNEL, ++ ctx, renesas_fw_callback); ++ return; ++ } else { ++ goto cleanup; ++ } + } + + err = renesas_fw_verify(pdev, fw->data, fw->size); diff --git a/target/linux/apm821xx/patches-5.4/104-usb-xhci-provide-a-debugfs-hook-for-erasing-rom.patch b/target/linux/apm821xx/patches-5.4/104-usb-xhci-provide-a-debugfs-hook-for-erasing-rom.patch new file mode 100644 index 0000000000..5959daf432 --- /dev/null +++ b/target/linux/apm821xx/patches-5.4/104-usb-xhci-provide-a-debugfs-hook-for-erasing-rom.patch @@ -0,0 +1,88 @@ +From d6e220fb600a6b1f73155b541aa9826ec75d4559 Mon Sep 17 00:00:00 2001 +From: Vinod Koul +Date: Mon, 13 Jan 2020 14:10:05 +0530 +Subject: [PATCH 104/104] usb: xhci: provide a debugfs hook for erasing rom + +run "echo 1 > /sys/kernel/debug/renesas-usb/rom_erase" to erase firmware +when driver is loaded. + +Subsequent init of driver shall reload the firmware + +Signed-off-by: Vinod Koul +Signed-off-by: Christian Lamparter [5.4 backport] +--- + drivers/usb/host/xhci-pci-renesas.c | 35 +++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +--- a/drivers/usb/host/xhci-pci-renesas.c ++++ b/drivers/usb/host/xhci-pci-renesas.c +@@ -2,6 +2,7 @@ + /* Copyright (C) 2019-2020 Linaro Limited */ + + #include ++#include + #include + #include + #include +@@ -229,6 +230,9 @@ static int renesas_fw_verify(struct pci_ + + return 0; + } ++ ++static void debugfs_init(struct pci_dev *pdev); ++ + static int renesas_check_rom_state(struct pci_dev *pdev) + { + const struct renesas_fw_entry *entry; +@@ -252,6 +256,7 @@ static int renesas_check_rom_state(struc + if (version == entry->expected_version) { + dev_dbg(&pdev->dev, "Detected valid ROM version..\n"); + valid_version = true; ++ debugfs_init(pdev); + } + } + if (valid_version == false) +@@ -536,6 +541,34 @@ static void renesas_rom_erase(struct pci + dev_dbg(&pdev->dev, "ROM Erase... Done success\n"); + } + ++static int debugfs_rom_erase(void *data, u64 value) ++{ ++ struct pci_dev *pdev = data; ++ ++ if (value == 1) { ++ dev_dbg(&pdev->dev, "Userspace requested ROM erase\n"); ++ renesas_rom_erase(pdev); ++ return 0; ++ } ++ return -EINVAL; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(rom_erase_ops, NULL, debugfs_rom_erase, "%llu\n"); ++ ++static struct dentry *debugfs_root; ++ ++static void debugfs_init(struct pci_dev *pdev) ++{ ++ debugfs_root = debugfs_create_dir("renesas-usb", NULL); ++ ++ debugfs_create_file("rom_erase", 0200, debugfs_root, ++ pdev, &rom_erase_ops); ++} ++ ++static void debugfs_exit(void) ++{ ++ debugfs_remove_recursive(debugfs_root); ++} ++ + static bool renesas_download_rom(struct pci_dev *pdev, + const u32 *fw, size_t step) + { +@@ -889,6 +922,8 @@ static int renesas_xhci_pci_probe(struct + + static void renesas_xhci_pci_remove(struct pci_dev *dev) + { ++ debugfs_exit(); ++ + if (renesas_fw_alive_check(dev)) { + /* + * bail out early, if this was a renesas device w/o FW. diff --git a/target/linux/apm821xx/patches-5.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch b/target/linux/apm821xx/patches-5.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch deleted file mode 100644 index 36eab9349b..0000000000 --- a/target/linux/apm821xx/patches-5.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch +++ /dev/null @@ -1,1026 +0,0 @@ -Subject: [PATCH v2 1/5] usb: xhci: add firmware loader for uPD720201 and uPD720202 w/o ROM -Date: Fri, 21 Jun 2019 14:29:09 +0530 -Message-Id: <20190621085913.8722-2-vkoul@kernel.org> -From: Christian Lamparter - -This patch adds a firmware loader for the uPD720201K8-711-BAC-A -and uPD720202K8-711-BAA-A variant. Both of these chips are listed -in Renesas' R19UH0078EJ0500 Rev.5.00 "User's Manual: Hardware" as -devices which need the firmware loader on page 2 in order to -work as they "do not support the External ROM". - -The "Firmware Download Sequence" is describe in chapter -"7.1 FW Download Interface" R19UH0078EJ0500 Rev.5.00 page 131. - -The firmware "K2013080.mem" is available from a USB3.0 Host to -PCIe Adapter (PP2U-E card) "Firmware download" archive. An -alternative version can be sourced from Netgear's WNDR4700 GPL -archives. - -The release notes of the PP2U-E's "Firmware Download" ver 2.0.1.3 -(2012-06-15) state that the firmware is for the following devices: - - uPD720201 ES 2.0 sample whose revision ID is 2. - - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. - - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. - -Cc: Yoshihiro Shimoda -Signed-off-by: Christian Lamparter -Signed-off-by: Bjorn Andersson -[vkoul: fixed comments: - used macros for timeout count and delay - removed renesas_fw_alive_check - cleaned renesas_fw_callback - removed recurion for renesas_fw_download - added MODULE_FIRMWARE] -Signed-off-by: Vinod Koul ---- - drivers/usb/host/xhci-pci.c | 458 ++++++++++++++++++++++++++++++++++++ - 1 file changed, 458 insertions(+) - ---- a/drivers/usb/host/xhci-pci.c -+++ b/drivers/usb/host/xhci-pci.c -@@ -12,6 +12,8 @@ - #include - #include - #include -+#include -+#include - - #include "xhci.h" - #include "xhci-trace.h" -@@ -57,6 +59,44 @@ - #define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc - #define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142 - -+#define RENESAS_FW_VERSION 0x6C -+#define RENESAS_ROM_CONFIG 0xF0 -+#define RENESAS_FW_STATUS 0xF4 -+#define RENESAS_FW_STATUS_MSB 0xF5 -+#define RENESAS_ROM_STATUS 0xF6 -+#define RENESAS_ROM_STATUS_MSB 0xF7 -+#define RENESAS_DATA0 0xF8 -+#define RENESAS_DATA1 0xFC -+ -+#define RENESAS_FW_VERSION_FIELD GENMASK(23, 7) -+#define RENESAS_FW_VERSION_OFFSET 8 -+ -+#define RENESAS_FW_STATUS_DOWNLOAD_ENABLE BIT(0) -+#define RENESAS_FW_STATUS_LOCK BIT(1) -+#define RENESAS_FW_STATUS_RESULT GENMASK(6, 4) -+ #define RENESAS_FW_STATUS_INVALID 0 -+ #define RENESAS_FW_STATUS_SUCCESS BIT(4) -+ #define RENESAS_FW_STATUS_ERROR BIT(5) -+#define RENESAS_FW_STATUS_SET_DATA0 BIT(8) -+#define RENESAS_FW_STATUS_SET_DATA1 BIT(9) -+ -+#define RENESAS_ROM_STATUS_ACCESS BIT(0) -+#define RENESAS_ROM_STATUS_ERASE BIT(1) -+#define RENESAS_ROM_STATUS_RELOAD BIT(2) -+#define RENESAS_ROM_STATUS_RESULT GENMASK(6, 4) -+ #define RENESAS_ROM_STATUS_INVALID 0 -+ #define RENESAS_ROM_STATUS_SUCCESS BIT(4) -+ #define RENESAS_ROM_STATUS_ERROR BIT(5) -+#define RENESAS_ROM_STATUS_SET_DATA0 BIT(8) -+#define RENESAS_ROM_STATUS_SET_DATA1 BIT(9) -+#define RENESAS_ROM_STATUS_ROM_EXISTS BIT(15) -+ -+#define RENESAS_ROM_ERASE_MAGIC 0x5A65726F -+#define RENESAS_ROM_WRITE_MAGIC 0x53524F4D -+ -+#define RENESAS_RETRY 1000 -+#define RENESAS_DELAY 10 -+ - static const char hcd_name[] = "xhci_hcd"; - - static struct hc_driver __read_mostly xhci_pci_hc_driver; -@@ -284,6 +324,873 @@ static void xhci_pme_acpi_rtd3_enable(st - static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } - #endif /* CONFIG_ACPI */ - -+static const struct renesas_fw_entry { -+ const char *firmware_name; -+ u16 device; -+ u8 revision; -+ u16 expected_version; -+} renesas_fw_table[] = { -+ /* -+ * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A -+ * are listed in R19UH0078EJ0500 Rev.5.00 as devices which -+ * need the software loader. -+ * -+ * PP2U/ReleaseNote_USB3-201-202-FW.txt: -+ * -+ * Note: This firmware is for the following devices. -+ * - uPD720201 ES 2.0 sample whose revision ID is 2. -+ * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. -+ * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. -+ * -+ * Entry expected_version should be kept in increasing order for a -+ * chip, so that driver will pick first version and if that fails -+ * then next one will be picked -+ */ -+ { "K2013080.mem", 0x0014, 0x02, 0x2013 }, -+ { "K2013080.mem", 0x0014, 0x03, 0x2013 }, -+ { "K2026090.mem", 0x0014, 0x03, 0x2026 }, -+ { "K2013080.mem", 0x0015, 0x02, 0x2013 }, -+ { "K2026090.mem", 0x0015, 0x02, 0x2026 }, -+}; -+ -+MODULE_FIRMWARE("K2013080.mem"); -+MODULE_FIRMWARE("K2026090.mem"); -+ -+static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev) -+{ -+ const struct renesas_fw_entry *entry; -+ size_t i; -+ -+ /* This loader will only work with a RENESAS device. */ -+ if (!(dev->vendor == PCI_VENDOR_ID_RENESAS)) -+ return NULL; -+ -+ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { -+ entry = &renesas_fw_table[i]; -+ if (entry->device == dev->device && -+ entry->revision == dev->revision) -+ return entry; -+ } -+ -+ return NULL; -+} -+ -+static const struct -+renesas_fw_entry *renesas_get_next_entry(struct pci_dev *dev, -+ const struct renesas_fw_entry *entry) -+{ -+ const struct renesas_fw_entry *next_entry; -+ size_t i; -+ -+ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { -+ next_entry = &renesas_fw_table[i]; -+ if (next_entry->device == dev->device && -+ next_entry->revision == dev->revision && -+ next_entry->expected_version > entry->expected_version) -+ return next_entry; -+ } -+ -+ return NULL; -+} -+ -+static int renesas_fw_download_image(struct pci_dev *dev, -+ const u32 *fw, -+ size_t step) -+{ -+ size_t i; -+ int err; -+ u8 fw_status; -+ bool data0_or_data1; -+ -+ /* -+ * The hardware does alternate between two 32-bit pages. -+ * (This is because each row of the firmware is 8 bytes). -+ * -+ * for even steps we use DATA0, for odd steps DATA1. -+ */ -+ data0_or_data1 = (step & 1) == 1; -+ -+ /* step+1. Read "Set DATAX" and confirm it is cleared. */ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ err = pci_read_config_byte(dev, RENESAS_FW_STATUS_MSB, -+ &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (!(fw_status & BIT(data0_or_data1))) -+ break; -+ -+ udelay(RENESAS_DELAY); -+ } -+ if (i == RENESAS_RETRY) -+ return -ETIMEDOUT; -+ -+ /* -+ * step+2. Write FW data to "DATAX". -+ * "LSB is left" => force little endian -+ */ -+ err = pci_write_config_dword(dev, data0_or_data1 ? -+ RENESAS_DATA1 : RENESAS_DATA0, -+ (__force u32)cpu_to_le32(fw[step])); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ udelay(100); -+ -+ /* step+3. Set "Set DATAX". */ -+ err = pci_write_config_byte(dev, RENESAS_FW_STATUS_MSB, -+ BIT(data0_or_data1)); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ return 0; -+} -+ -+static int renesas_fw_verify(struct pci_dev *dev, -+ const void *fw_data, -+ size_t length) -+{ -+ const struct renesas_fw_entry *entry = renesas_needs_fw_dl(dev); -+ u16 fw_version_pointer; -+ u16 fw_version; -+ -+ if (!entry) -+ return -EINVAL; -+ -+ /* -+ * The Firmware's Data Format is describe in -+ * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124 -+ */ -+ -+ /* "Each row is 8 bytes". => firmware size must be a multiple of 8. */ -+ if (length % 8 != 0) -+ dev_warn(&dev->dev, "firmware size is not a multiple of 8."); -+ -+ /* -+ * The bootrom chips of the big brother have sizes up to 64k, let's -+ * assume that's the biggest the firmware can get. -+ */ -+ if (length < 0x1000 || length >= 0x10000) { -+ dev_err(&dev->dev, "firmware is size %zd is not (4k - 64k).", -+ length); -+ return -EINVAL; -+ } -+ -+ /* The First 2 bytes are fixed value (55aa). "LSB on Left" */ -+ if (get_unaligned_le16(fw_data) != 0x55aa) { -+ dev_err(&dev->dev, "no valid firmware header found."); -+ return -EINVAL; -+ } -+ -+ /* verify the firmware version position and print it. */ -+ fw_version_pointer = get_unaligned_le16(fw_data + 4); -+ if (fw_version_pointer + 2 >= length) { -+ dev_err(&dev->dev, -+ "firmware version pointer is outside of the firmware image."); -+ return -EINVAL; -+ } -+ -+ fw_version = get_unaligned_le16(fw_data + fw_version_pointer); -+ dev_dbg(&dev->dev, "got firmware version: %02x.", fw_version); -+ -+ if (fw_version != entry->expected_version) { -+ dev_err(&dev->dev, -+ "firmware version mismatch, expected version: %02x.", -+ entry->expected_version); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int renesas_check_rom_state(struct pci_dev *pdev) -+{ -+ const struct renesas_fw_entry *entry; -+ u16 rom_state; -+ u32 version; -+ bool valid_version = false; -+ int err, i; -+ -+ /* check FW version */ -+ err = pci_read_config_dword(pdev, RENESAS_FW_VERSION, &version); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ version &= RENESAS_FW_VERSION_FIELD; -+ version = version >> RENESAS_FW_VERSION_OFFSET; -+ dev_dbg(&pdev->dev, "Found FW version loaded is %x\n", version); -+ -+ /* treat version in renesas_fw_table as correct ones */ -+ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { -+ entry = &renesas_fw_table[i]; -+ if (version == entry->expected_version) { -+ dev_dbg(&pdev->dev, "Detected valid ROM version..\n"); -+ valid_version = true; -+ } -+ } -+ -+ /* -+ * Test if ROM is present and loaded, if so we can skip everything -+ */ -+ err = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_state); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ if (rom_state & BIT(15)) { -+ /* ROM exists */ -+ dev_dbg(&pdev->dev, "ROM exists\n"); -+ -+ /* Check the "Result Code" Bits (6:4) and act accordingly */ -+ switch (rom_state & RENESAS_ROM_STATUS_RESULT) { -+ case RENESAS_ROM_STATUS_SUCCESS: -+ dev_dbg(&pdev->dev, "Success ROM load..."); -+ /* we have valid version and status so success */ -+ if (valid_version) -+ return 0; -+ break; -+ -+ case RENESAS_ROM_STATUS_INVALID: /* No result yet */ -+ dev_dbg(&pdev->dev, "No result as it is ROM..."); -+ /* we have valid version and status so success */ -+ if (valid_version) -+ return 0; -+ break; -+ -+ case RENESAS_ROM_STATUS_ERROR: /* Error State */ -+ default: /* All other states are marked as "Reserved states" */ -+ dev_err(&pdev->dev, "Invalid ROM.."); -+ break; -+ } -+ } -+ -+ return -EIO; -+} -+ -+static int renesas_fw_check_running(struct pci_dev *pdev) -+{ -+ int err; -+ u8 fw_state; -+ -+ /* Check if device has ROM and loaded, if so skip everything */ -+ err = renesas_check_rom_state(pdev); -+ if (!err) -+ return err; -+ -+ /* -+ * Test if the device is actually needing the firmware. As most -+ * BIOSes will initialize the device for us. If the device is -+ * initialized. -+ */ -+ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_state); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* -+ * Check if "FW Download Lock" is locked. If it is and the FW is -+ * ready we can simply continue. If the FW is not ready, we have -+ * to give up. -+ */ -+ if (fw_state & RENESAS_FW_STATUS_LOCK) { -+ dev_dbg(&pdev->dev, "FW Download Lock is engaged."); -+ -+ if (fw_state & RENESAS_FW_STATUS_SUCCESS) -+ return 0; -+ -+ dev_err(&pdev->dev, -+ "FW Download Lock is set and FW is not ready. Giving Up."); -+ return -EIO; -+ } -+ -+ /* -+ * Check if "FW Download Enable" is set. If someone (us?) tampered -+ * with it and it can't be resetted, we have to give up too... and -+ * ask for a forgiveness and a reboot. -+ */ -+ if (fw_state & RENESAS_FW_STATUS_DOWNLOAD_ENABLE) { -+ dev_err(&pdev->dev, -+ "FW Download Enable is stale. Giving Up (poweroff/reboot needed)."); -+ return -EIO; -+ } -+ -+ /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */ -+ switch (fw_state & RENESAS_FW_STATUS_RESULT) { -+ case 0: /* No result yet */ -+ dev_dbg(&pdev->dev, "FW is not ready/loaded yet."); -+ -+ /* tell the caller, that this device needs the firmware. */ -+ return 1; -+ -+ case RENESAS_FW_STATUS_SUCCESS: /* Success, device should be working. */ -+ dev_dbg(&pdev->dev, "FW is ready."); -+ return 0; -+ -+ case RENESAS_FW_STATUS_ERROR: /* Error State */ -+ dev_err(&pdev->dev, -+ "hardware is in an error state. Giving up (poweroff/reboot needed)."); -+ return -ENODEV; -+ -+ default: /* All other states are marked as "Reserved states" */ -+ dev_err(&pdev->dev, -+ "hardware is in an invalid state %lx. Giving up (poweroff/reboot needed).", -+ (fw_state & RENESAS_FW_STATUS_RESULT) >> 4); -+ return -EINVAL; -+ } -+} -+ -+static int renesas_fw_download(struct pci_dev *pdev, -+ const struct firmware *fw) -+{ -+ const u32 *fw_data = (const u32 *)fw->data; -+ size_t i; -+ int err; -+ u8 fw_status; -+ -+ /* -+ * For more information and the big picture: please look at the -+ * "Firmware Download Sequence" in "7.1 FW Download Interface" -+ * of R19UH0078EJ0500 Rev.5.00 page 131 -+ */ -+ -+ /* -+ * 0. Set "FW Download Enable" bit in the -+ * "FW Download Control & Status Register" at 0xF4 -+ */ -+ err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, -+ RENESAS_FW_STATUS_DOWNLOAD_ENABLE); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* 1 - 10 follow one step after the other. */ -+ for (i = 0; i < fw->size / 4; i++) { -+ err = renesas_fw_download_image(pdev, fw_data, i); -+ if (err) { -+ dev_err(&pdev->dev, -+ "Firmware Download Step %zd failed at position %zd bytes with (%d).", -+ i, i * 4, err); -+ return err; -+ } -+ } -+ -+ /* -+ * This sequence continues until the last data is written to -+ * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1" -+ * is cleared by the hardware beforehand. -+ */ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS_MSB, -+ &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (!(fw_status & (BIT(0) | BIT(1)))) -+ break; -+ -+ udelay(RENESAS_DELAY); -+ } -+ if (i == RENESAS_RETRY) -+ dev_warn(&pdev->dev, "Final Firmware Download step timed out."); -+ -+ /* -+ * 11. After finishing writing the last data of FW, the -+ * System Software must clear "FW Download Enable" -+ */ -+ err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, 0); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* 12. Read "Result Code" and confirm it is good. */ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_status); -+ if (err) -+ return pcibios_err_to_errno(err); -+ if (fw_status & RENESAS_FW_STATUS_SUCCESS) -+ break; -+ -+ udelay(RENESAS_DELAY); -+ } -+ if (i == RENESAS_RETRY) { -+ /* Timed out / Error - let's see if we can fix this */ -+ err = renesas_fw_check_running(pdev); -+ switch (err) { -+ case 0: /* -+ * we shouldn't end up here. -+ * maybe it took a little bit longer. -+ * But all should be well? -+ */ -+ break; -+ -+ case 1: /* (No result yet! */ -+ return -ETIMEDOUT; -+ -+ default: -+ return err; -+ } -+ } -+ /* -+ * Optional last step: Engage Firmware Lock -+ * -+ * err = pci_write_config_byte(pdev, 0xF4, BIT(2)); -+ * if (err) -+ * return pcibios_err_to_errno(err); -+ */ -+ -+ return 0; -+} -+ -+struct renesas_fw_ctx { -+ struct pci_dev *pdev; -+ const struct pci_device_id *id; -+ bool resume; -+ const struct renesas_fw_entry *entry; -+}; -+ -+static int xhci_pci_probe(struct pci_dev *pdev, -+ const struct pci_device_id *id); -+ -+static bool renesas_check_rom(struct pci_dev *pdev) -+{ -+ u16 rom_status; -+ int retval; -+ -+ /* 1. Check if external ROM exists */ -+ retval = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_status); -+ if (retval) -+ return false; -+ -+ rom_status &= RENESAS_ROM_STATUS_ROM_EXISTS; -+ if (rom_status) { -+ dev_dbg(&pdev->dev, "External ROM exists\n"); -+ return true; /* External ROM exists */ -+ } -+ -+ return false; -+} -+ -+static void renesas_rom_erase(struct pci_dev *pdev) -+{ -+ int retval, i; -+ u8 status; -+ -+ dev_dbg(&pdev->dev, "Performing ROM Erase...\n"); -+ retval = pci_write_config_dword(pdev, RENESAS_DATA0, -+ RENESAS_ROM_ERASE_MAGIC); -+ if (retval) { -+ dev_err(&pdev->dev, "ROM erase, magic word write failed: %d\n", -+ pcibios_err_to_errno(retval)); -+ return; -+ } -+ -+ retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); -+ if (retval) { -+ dev_err(&pdev->dev, "ROM status read failed: %d\n", -+ pcibios_err_to_errno(retval)); -+ return; -+ } -+ status |= RENESAS_ROM_STATUS_ERASE; -+ retval = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, status); -+ if (retval) { -+ dev_err(&pdev->dev, "ROM erase set word write failed\n"); -+ return; -+ } -+ -+ /* sleep a bit while ROM is erased */ -+ msleep(20); -+ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, -+ &status); -+ status &= RENESAS_ROM_STATUS_ERASE; -+ if (!status) -+ break; -+ -+ mdelay(RENESAS_DELAY); -+ } -+ -+ if (i == RENESAS_RETRY) -+ dev_dbg(&pdev->dev, "Chip erase timedout: %x\n", status); -+ -+ dev_dbg(&pdev->dev, "ROM Erase... Done success\n"); -+} -+ -+static bool renesas_download_rom(struct pci_dev *pdev, -+ const u32 *fw, size_t step) -+{ -+ bool data0_or_data1; -+ u8 fw_status; -+ size_t i; -+ int err; -+ -+ /* -+ * The hardware does alternate between two 32-bit pages. -+ * (This is because each row of the firmware is 8 bytes). -+ * -+ * for even steps we use DATA0, for odd steps DATA1. -+ */ -+ data0_or_data1 = (step & 1) == 1; -+ -+ /* Read "Set DATAX" and confirm it is cleared. */ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB, -+ &fw_status); -+ if (err) { -+ dev_err(&pdev->dev, "Read ROM Status failed: %d\n", -+ pcibios_err_to_errno(err)); -+ return false; -+ } -+ if (!(fw_status & BIT(data0_or_data1))) -+ break; -+ -+ udelay(RENESAS_DELAY); -+ } -+ if (i == RENESAS_RETRY) { -+ dev_err(&pdev->dev, "Timeout for Set DATAX step: %zd\n", step); -+ return false; -+ } -+ -+ /* -+ * Write FW data to "DATAX". -+ * "LSB is left" => force little endian -+ */ -+ err = pci_write_config_dword(pdev, data0_or_data1 ? -+ RENESAS_DATA1 : RENESAS_DATA0, -+ (__force u32)cpu_to_le32(fw[step])); -+ if (err) { -+ dev_err(&pdev->dev, "Write to DATAX failed: %d\n", -+ pcibios_err_to_errno(err)); -+ return false; -+ } -+ -+ udelay(100); -+ -+ /* Set "Set DATAX". */ -+ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS_MSB, -+ BIT(data0_or_data1)); -+ if (err) { -+ dev_err(&pdev->dev, "Write config for DATAX failed: %d\n", -+ pcibios_err_to_errno(err)); -+ return false; -+ } -+ -+ return true; -+} -+ -+static bool renesas_setup_rom(struct pci_dev *pdev, const struct firmware *fw) -+{ -+ const u32 *fw_data = (const u32 *)fw->data; -+ int err, i; -+ u8 status; -+ -+ /* 2. Write magic word to Data0 */ -+ err = pci_write_config_dword(pdev, RENESAS_DATA0, -+ RENESAS_ROM_WRITE_MAGIC); -+ if (err) -+ return false; -+ -+ /* 3. Set External ROM access */ -+ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, -+ RENESAS_ROM_STATUS_ACCESS); -+ if (err) -+ goto remove_bypass; -+ -+ /* 4. Check the result */ -+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); -+ if (err) -+ goto remove_bypass; -+ status &= GENMASK(6, 4); -+ if (status) { -+ dev_err(&pdev->dev, -+ "setting external rom failed: %x\n", status); -+ goto remove_bypass; -+ } -+ -+ /* 5 to 16 Write FW to DATA0/1 while checking SetData0/1 */ -+ for (i = 0; i < fw->size / 4; i++) { -+ err = renesas_download_rom(pdev, fw_data, i); -+ if (!err) { -+ dev_err(&pdev->dev, -+ "ROM Download Step %d failed at position %d bytes\n", -+ i, i * 4); -+ goto remove_bypass; -+ } -+ } -+ -+ /* -+ * wait till DATA0/1 is cleared -+ */ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB, -+ &status); -+ if (err) -+ goto remove_bypass; -+ if (!(status & (BIT(0) | BIT(1)))) -+ break; -+ -+ udelay(RENESAS_DELAY); -+ } -+ if (i == RENESAS_RETRY) { -+ dev_err(&pdev->dev, "Final Firmware ROM Download step timed out\n"); -+ goto remove_bypass; -+ } -+ -+ /* 17. Remove bypass */ -+ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0); -+ if (err) -+ return false; -+ -+ udelay(10); -+ -+ /* 18. check result */ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); -+ if (err) { -+ dev_err(&pdev->dev, "Read ROM status failed:%d\n", -+ pcibios_err_to_errno(err)); -+ return false; -+ } -+ status &= RENESAS_ROM_STATUS_RESULT; -+ if (status == RENESAS_ROM_STATUS_SUCCESS) { -+ dev_dbg(&pdev->dev, "Download ROM success\n"); -+ break; -+ } -+ udelay(RENESAS_DELAY); -+ } -+ if (i == RENESAS_RETRY) { /* Timed out */ -+ dev_err(&pdev->dev, -+ "Download to external ROM TO: %x\n", status); -+ return false; -+ } -+ -+ dev_dbg(&pdev->dev, "Download to external ROM scuceeded\n"); -+ -+ /* Last step set Reload */ -+ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, -+ RENESAS_ROM_STATUS_RELOAD); -+ if (err) { -+ dev_err(&pdev->dev, "Set ROM execute failed: %d\n", -+ pcibios_err_to_errno(err)); -+ return false; -+ } -+ -+ /* -+ * wait till Reload is cleared -+ */ -+ for (i = 0; i < RENESAS_RETRY; i++) { -+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); -+ if (err) -+ return false; -+ if (!(status & RENESAS_ROM_STATUS_RELOAD)) -+ break; -+ -+ udelay(RENESAS_DELAY); -+ } -+ if (i == RENESAS_RETRY) { -+ dev_err(&pdev->dev, "ROM Exec timed out: %x\n", status); -+ return false; -+ } -+ -+ return true; -+ -+remove_bypass: -+ pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0); -+ return false; -+} -+ -+static void renesas_fw_callback(const struct firmware *fw, -+ void *context) -+{ -+ struct renesas_fw_ctx *ctx = context; -+ struct pci_dev *pdev = ctx->pdev; -+ struct device *parent = pdev->dev.parent; -+ const struct renesas_fw_entry *next_entry; -+ bool rom; -+ int err; -+ -+ if (!fw) { -+ dev_err(&pdev->dev, "firmware failed to load\n"); -+ /* -+ * we didn't find firmware, check if we have another -+ * entry for this device -+ */ -+ next_entry = renesas_get_next_entry(ctx->pdev, ctx->entry); -+ if (next_entry) { -+ ctx->entry = next_entry; -+ dev_dbg(&pdev->dev, "Found next entry, requesting: %s\n", -+ next_entry->firmware_name); -+ request_firmware_nowait(THIS_MODULE, 1, -+ next_entry->firmware_name, -+ &pdev->dev, GFP_KERNEL, -+ ctx, renesas_fw_callback); -+ return; -+ } else { -+ goto cleanup; -+ } -+ } -+ -+ err = renesas_fw_verify(pdev, fw->data, fw->size); -+ if (err) -+ goto cleanup; -+ -+ /* Check if the device has external ROM */ -+ rom = renesas_check_rom(pdev); -+ if (rom) { -+ /* perfrom chip erase first */ -+ renesas_rom_erase(pdev); -+ -+ /* lets try loading fw on ROM first */ -+ rom = renesas_setup_rom(pdev, fw); -+ if (!rom) { -+ dev_err(&pdev->dev, -+ "ROM load failed, falling back on FW load\n"); -+ } else { -+ dev_dbg(&pdev->dev, "ROM load done..\n"); -+ -+ release_firmware(fw); -+ goto do_probe; -+ } -+ } -+ -+ err = renesas_fw_download(pdev, fw); -+ release_firmware(fw); -+ if (err) { -+ dev_err(&pdev->dev, "firmware failed to download (%d).", err); -+ goto cleanup; -+ } -+ -+do_probe: -+ if (ctx->resume) -+ return; -+ -+ err = xhci_pci_probe(pdev, ctx->id); -+ if (!err) { -+ /* everything worked */ -+ devm_kfree(&pdev->dev, ctx); -+ return; -+ } -+ -+cleanup: -+ /* in case of an error - fall through */ -+ dev_info(&pdev->dev, "Unloading driver"); -+ -+ if (parent) -+ device_lock(parent); -+ -+ device_release_driver(&pdev->dev); -+ -+ if (parent) -+ device_unlock(parent); -+ -+ pci_dev_put(pdev); -+} -+ -+static int renesas_fw_alive_check(struct pci_dev *pdev) -+{ -+ const struct renesas_fw_entry *entry; -+ -+ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ -+ entry = renesas_needs_fw_dl(pdev); -+ if (!entry) -+ return 0; -+ -+ return renesas_fw_check_running(pdev); -+} -+ -+static int renesas_fw_download_to_hw(struct pci_dev *pdev, -+ const struct pci_device_id *id, -+ bool do_resume) -+{ -+ const struct renesas_fw_entry *entry; -+ struct renesas_fw_ctx *ctx; -+ int err; -+ -+ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ -+ entry = renesas_needs_fw_dl(pdev); -+ if (!entry) -+ return 0; -+ -+ err = renesas_fw_check_running(pdev); -+ /* Continue ahead, if the firmware is already running. */ -+ if (err == 0) -+ return 0; -+ -+ if (err != 1) -+ return err; -+ -+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; -+ ctx->pdev = pdev; -+ ctx->resume = do_resume; -+ ctx->id = id; -+ ctx->entry = entry; -+ -+ pci_dev_get(pdev); -+ err = request_firmware_nowait(THIS_MODULE, 1, entry->firmware_name, -+ &pdev->dev, GFP_KERNEL, -+ ctx, renesas_fw_callback); -+ if (err) { -+ pci_dev_put(pdev); -+ return err; -+ } -+ -+ /* -+ * The renesas_fw_callback() callback will continue the probe -+ * process, once it aquires the firmware. -+ */ -+ return 1; -+} -+ -+static int renesas_check_if_fw_dl_is_needed(struct pci_dev *pdev) -+{ -+ int err; -+ u8 fw_state; -+ -+ /* -+ * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A -+ * are listed in R19UH0078EJ0500 Rev.5.00 as devices which -+ * need a firmware in order to work. -+ * -+ * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. -+ * - uPD720201 ES 2.0 sample whose revision ID is 2. -+ * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. -+ */ -+ if (!((pdev->vendor == PCI_VENDOR_ID_RENESAS) && -+ ((pdev->device == 0x0015 && pdev->revision == 0x02) || -+ (pdev->device == 0x0014 && -+ (pdev->revision == 0x02 || pdev->revision == 0x03))))) -+ return 0; -+ -+ /* Check if device has ROM and loaded, if so skip everything */ -+ err = renesas_check_rom_state(pdev); -+ if (!err) -+ return err; -+ -+ /* -+ * Test if the firmware was uploaded and is running. -+ * As most BIOSes will initialize the device for us. -+ */ -+ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_state); -+ if (err) -+ return pcibios_err_to_errno(err); -+ -+ /* Check the "Result Code" Bits (6:4) and act accordingly */ -+ switch (fw_state & RENESAS_FW_STATUS_RESULT) { -+ case 0: /* No result yet */ -+ dev_err(&pdev->dev, "FW is not ready/loaded yet."); -+ return -ENODEV; -+ -+ case RENESAS_FW_STATUS_SUCCESS: -+ dev_dbg(&pdev->dev, "FW is ready."); -+ return 0; -+ -+ case RENESAS_FW_STATUS_ERROR: -+ dev_err(&pdev->dev, "HW is in an error state."); -+ return -ENODEV; -+ -+ default: /* All other states are marked as "Reserved states" */ -+ dev_err(&pdev->dev, "HW is in an invalid state (%x).", -+ (fw_state & 0x70) >> 4); -+ return -EINVAL; -+ } -+} -+ - /* called during probe() after chip reset completes */ - static int xhci_pci_setup(struct usb_hcd *hcd) - { -@@ -325,6 +1232,27 @@ static int xhci_pci_probe(struct pci_dev - struct hc_driver *driver; - struct usb_hcd *hcd; - -+ /* -+ * Check if this device is a RENESAS uPD720201/2 device. -+ * Otherwise, we can continue with xhci_pci_probe as usual. -+ */ -+ retval = renesas_fw_download_to_hw(dev, id, false); -+ switch (retval) { -+ case 0: -+ break; -+ -+ case 1: /* let it load the firmware and recontinue the probe. */ -+ return 0; -+ -+ default: -+ return retval; -+ }; -+ -+ /* Check if this device is a RENESAS uPD720201/2 device. */ -+ retval = renesas_check_if_fw_dl_is_needed(dev); -+ if (retval) -+ return retval; -+ - driver = (struct hc_driver *)id->driver_data; - - /* Prevent runtime suspending between USB-2 and USB-3 initialization */ -@@ -386,6 +1314,16 @@ static void xhci_pci_remove(struct pci_d - { - struct xhci_hcd *xhci; - -+ if (renesas_fw_alive_check(dev)) { -+ /* -+ * bail out early, if this was a renesas device w/o FW. -+ * Else we might hit the NMI watchdog in xhci_handsake -+ * during xhci_reset as part of the driver's unloading. -+ * which we forced in the renesas_fw_callback(). -+ */ -+ return; -+ } -+ - xhci = hcd_to_xhci(pci_get_drvdata(dev)); - xhci->xhc_state |= XHCI_STATE_REMOVING; - -@@ -513,6 +1451,11 @@ static int xhci_pci_resume(struct usb_hc - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - usb_enable_intel_xhci_ports(pdev); - -+ /* Check if this device is a RENESAS uPD720201/2 device. */ -+ retval = renesas_check_if_fw_dl_is_needed(pdev); -+ if (retval) -+ return retval; -+ - if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) - xhci_ssic_port_unused_quirk(hcd, false); - diff --git a/target/linux/apm821xx/patches-5.4/801-usb-xhci-handle-uPD720201-and-uPD720202-w-o-ROM.patch b/target/linux/apm821xx/patches-5.4/801-usb-xhci-handle-uPD720201-and-uPD720202-w-o-ROM.patch new file mode 100644 index 0000000000..ed308b087d --- /dev/null +++ b/target/linux/apm821xx/patches-5.4/801-usb-xhci-handle-uPD720201-and-uPD720202-w-o-ROM.patch @@ -0,0 +1,110 @@ +From 4fe1b3064755ff890bfa69547dc1b2a99005af61 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +Date: Wed, 8 Jun 2016 00:14:57 +0200 +Subject: [PATCH] usb: xhci: handle uPD720201 and uPD720202 w/o ROM + +This patch adds a firmware check for the uPD720201K8-711-BAC-A +and uPD720202K8-711-BAA-A variant. Both of these chips are listed +in Renesas' R19UH0078EJ0500 Rev.5.00 "User's Manual: Hardware" as +devices which need a firmware in order to work as they do not have +support to load the firmware from an external ROM. + +Currently, the xhci-pci driver is unable to initialize the hcd in +this case. Instead it will wait for 30 seconds and cause a timeout +in xhci_handshake() and fails. + +[ 5.116990] xhci_hcd 0000:45:00.0: new USB bus registered ... +[ 32.335215] xhci_hcd 0000:45:00.0: can't setup: -110 +[ 32.340179] xhci_hcd 0000:45:00.0: USB bus 2 deregistered +[ 32.345587] xhci_hcd 0000:45:00.0: init 0000:45:00.0 fail, -110 +[ 32.351496] xhci_hcd: probe of 0000:45:00.0 failed with error -110 + +Cc: Yoshihiro Shimoda +Signed-off-by: Christian Lamparter +Signed-off-by: Christian Lamparter +--- + drivers/usb/host/xhci-pci.c | 59 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 59 insertions(+) + +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -280,6 +280,55 @@ static void xhci_pme_acpi_rtd3_enable(st + static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } + #endif /* CONFIG_ACPI */ + ++static int renesas_check_if_fw_dl_is_needed(struct pci_dev *pdev) ++{ ++ int err; ++ u8 fw_state; ++ ++ /* ++ * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A ++ * are listed in R19UH0078EJ0500 Rev.5.00 as devices which ++ * need a firmware in order to work. ++ * ++ * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. ++ * - uPD720201 ES 2.0 sample whose revision ID is 2. ++ * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. ++ */ ++ if (!((pdev->vendor == PCI_VENDOR_ID_RENESAS) && ++ ((pdev->device == 0x0015 && pdev->revision == 0x02) || ++ (pdev->device == 0x0014 && ++ (pdev->revision == 0x02 || pdev->revision == 0x03))))) ++ return 0; ++ ++ /* ++ * Test if the firmware was uploaded and is running. ++ * As most BIOSes will initialize the device for us. ++ */ ++ err = pci_read_config_byte(pdev, 0xf4, &fw_state); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ /* Check the "Result Code" Bits (6:4) and act accordingly */ ++ switch (fw_state & 0x70) { ++ case 0: /* No result yet */ ++ dev_err(&pdev->dev, "FW is not ready/loaded yet."); ++ return -ENODEV; ++ ++ case BIT(4): /* Success, device should be working. */ ++ dev_dbg(&pdev->dev, "FW is ready."); ++ return 0; ++ ++ case BIT(5): /* Error State */ ++ dev_err(&pdev->dev, "HW is in an error state."); ++ return -ENODEV; ++ ++ default: /* All other states are marked as "Reserved states" */ ++ dev_err(&pdev->dev, "HW is in an invalid state (%x).", ++ (fw_state & 0x70) >> 4); ++ return -EINVAL; ++ } ++} ++ + /* called during probe() after chip reset completes */ + int xhci_pci_setup(struct usb_hcd *hcd) + { +@@ -319,6 +368,11 @@ int xhci_pci_probe(struct pci_dev *dev, + struct hc_driver *driver; + struct usb_hcd *hcd; + ++ /* Check if this device is a RENESAS uPD720201/2 device. */ ++ retval = renesas_check_if_fw_dl_is_needed(dev); ++ if (retval) ++ return retval; ++ + driver = (struct hc_driver *)id->driver_data; + + /* Prevent runtime suspending between USB-2 and USB-3 initialization */ +@@ -513,6 +567,11 @@ int xhci_pci_resume(struct usb_hcd *hcd, + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + usb_enable_intel_xhci_ports(pdev); + ++ /* Check if this device is a RENESAS uPD720201/2 device. */ ++ retval = renesas_check_if_fw_dl_is_needed(pdev); ++ if (retval) ++ return retval; ++ + if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) + xhci_ssic_port_unused_quirk(hcd, false); + -- 2.30.2