usb: xhci: handle uPD720201 and uPD720202 w/o ROM
diff mbox series

Message ID 1465337697-14385-1-git-send-email-chunkeey@gmail.com
State New, archived
Headers show
Series
  • usb: xhci: handle uPD720201 and uPD720202 w/o ROM
Related show

Commit Message

Christian Lamparter June 7, 2016, 10:14 p.m. UTC
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 <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
---
 drivers/usb/host/xhci-pci.c | 59 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

Comments

Christian Lamparter June 20, 2016, 3:32 p.m. UTC | #1
On Wednesday, June 08, 2016 12:14:57 AM Christian Lamparter wrote:
> 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 <yoshihiro.shimoda.uh@renesas.com>
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
Hello? 

Are there any news on this? Or is there anything else that I'm missing
which blocks this patch? If it's because the device won't be working
either with this patch, then please let me know. If Renesas is willing
to add the uPD720201/2 firmware (Like they did for their R-Car controllers)
to linux-firmware, I can write the  necessary firmware loader from
the PDF for this device and put it into xhci-quirks.

Regards,
Christian

> ---
>  drivers/usb/host/xhci-pci.c | 59 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 59 insertions(+)
> 
> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
> index 48672fa..3271a81 100644
> --- a/drivers/usb/host/xhci-pci.c
> +++ b/drivers/usb/host/xhci-pci.c
> @@ -207,6 +207,55 @@ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev)
>  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 */
>  static int xhci_pci_setup(struct usb_hcd *hcd)
>  {
> @@ -246,6 +295,11 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
>  	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 */
> @@ -424,6 +478,11 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
>  	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);
>  
>
Yoshihiro Shimoda June 21, 2016, 5:56 a.m. UTC | #2
Hello,

> From: Christian Lamparter
> Sent: Tuesday, June 21, 2016 12:32 AM
> 
> On Wednesday, June 08, 2016 12:14:57 AM Christian Lamparter wrote:
> > 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 <yoshihiro.shimoda.uh@renesas.com>
> > Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> Hello?
> 
> Are there any news on this? Or is there anything else that I'm missing
> which blocks this patch? If it's because the device won't be working
> either with this patch, then please let me know.

Thank you for the patch with CC me.
However, I'm afraid but I don't know the detail of the Renesas xHCI PCI controller.
(My job is for R-Car environment for now.)

By the way, the issue seems similar with R-Car environment though :)
http://thread.gmane.org/gmane.linux.kernel.stable/175457/focus=140699

and fix patch for it:
http://thread.gmane.org/gmane.linux.kernel.stable/177524

Best regards,
Yoshihiro Shimoda

> either with this patch, then please let me know. If Renesas is willing
> to add the uPD720201/2 firmware (Like they did for their R-Car controllers)
> to linux-firmware, I can write the  necessary firmware loader from
> the PDF for this device and put it into xhci-quirks.
> 
> Regards,
> Christian
Christian Lamparter June 23, 2016, 6:45 p.m. UTC | #3
Hello,

On Tuesday, June 21, 2016 05:56:58 AM Yoshihiro Shimoda wrote:
> > From: Christian Lamparter
> > Sent: Tuesday, June 21, 2016 12:32 AM
> > 
> > On Wednesday, June 08, 2016 12:14:57 AM Christian Lamparter wrote:
> > > 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 <yoshihiro.shimoda.uh@renesas.com>
> > > Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> > Hello?
> > 
> > Are there any news on this? Or is there anything else that I'm missing
> > which blocks this patch? If it's because the device won't be working
> > either with this patch, then please let me know.
> 
> Thank you for the patch with CC me.
> However, I'm afraid but I don't know the detail of the Renesas xHCI PCI controller.
> (My job is for R-Car environment for now.)
Oh. I see. Still, I would like to thank you for your input. I looked at
the rcar loader and firmware: The firmware header (aa55 + firmware version
pointer at 0x14) are carbon-copies of the pci parts (or vice versa?). I guess
I shouldn't be surprised, I bet the rcar and uPD both have a similar SuperH.
The firmware upload mechanism itself seems to be a bit different though, the
spec for the pci devices lists much more stuff to check and verify. Also, the
pci variant uploades 2 x 32bit packages at a time (via data0 and data1 in the
pci config space). However, the procedure is still somewhat identical.

Anyway, if you want to take a look: I went ahead and wrote a firmware loader
for the uPD720201 and uPD720202: I have attached in the patch below. The device
now works for this APM82181 (PowerPC 464 - Big Endian).

xhci_hcd 0000:45:00.0: xHCI Host Controller
xhci_hcd 0000:45:00.0: new USB bus registered, assigned bus number 2
xhci_hcd 0000:45:00.0: hcc params 0x014051cf hci version 0x100 quirks 0x01000090
xhci_hcd 0000:45:00.0: xHCI Host Controller
xhci_hcd 0000:45:00.0: new USB bus registered, assigned bus number 3
usb usb3: We don't know the algorithms for LPM for this host, disabling LPM.
hub 3-0:1.0: USB hub found
hub 3-0:1.0: 2 ports detected
usb 3-1: new SuperSpeed USB device number 2 using xhci_hcd
usb-storage 3-1:1.0: USB Mass Storage device detected
scsi host1: usb-storage 3-1:1.0
scsi 1:0:0:0: Direct-Access     Intenso  Slim Line        PMAP PQ: 0 ANSI: 6
sd 1:0:0:0: [sdb] 15466496 512-byte logical blocks: (7.92 GB/7.38 GiB)
...

> By the way, the issue seems similar with R-Car environment though :)
> http://thread.gmane.org/gmane.linux.kernel.stable/175457/focus=140699
> 
> and fix patch for it:
> http://thread.gmane.org/gmane.linux.kernel.stable/177524
Yes :). Same thing here. Load the firmware and the device is happy.
I know this is a big ask: Do you know or can you give me a lead
to whom would be willing to help/support the uPD720201/2? It would
be great if the firmware could be added to linux-firmware.git.

Note: This is one way to do it. But there are many more... I think adding
a pci_enable quirk to drivers/usb/host/pci-quirk.c is the least invasive
way... unless of course, someone knows a even better method... So please:
let me know!

Regards,
Christian
---
>From a0dc613140bab907a3d5787a7ae7b0638bf674d0 Mon Sep 17 00:00:00 2001
From: Christian Lamparter <chunkeey@gmail.com>
Date: Thu, 23 Jun 2016 20:28:20 +0200
Subject: [PATCH] usb: xhci: add firmware loader quirk for  uPD720201 and
 uPD720202

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 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 fail.

[    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

With the firmware loader quirk and the correct firmware in place,
the device will now initialize successfully:

[    5.384776] xhci_hcd 0000:45:00.0: xHCI Host Controller
[    5.390028] xhci_hcd 0000:45:00.0: new USB bus registered ...
[    5.403017] xhci_hcd 0000:45:00.0: hcc params 0x014051cf ...
[    5.412217] hub 2-0:1.0: USB hub found
[    5.416123] hub 2-0:1.0: 2 ports detected
[    5.420494] xhci_hcd 0000:45:00.0: xHCI Host Controller
[    5.425730] xhci_hcd 0000:45:00.0: new USB bus registered...
[    5.441771] hub 3-0:1.0: USB hub found
[    5.445692] hub 3-0:1.0: 2 ports detected
...

The firmware image can be extracted from the Windows driver.
A viable source is the "PP2U-E" (USB3.0 Host to PCIe Adapter)'s
"Firmware download (ver 2.0.1.3) Jun 15, 2012" file. It contains
the K2013080.mem file which needs to be placed in /lib/firmware.

Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
---
 drivers/usb/host/pci-quirks.c | 362 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 362 insertions(+)

diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 35af362..4d96142 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -16,6 +16,10 @@
 #include <linux/export.h>
 #include <linux/acpi.h>
 #include <linux/dmi.h>
+#include <linux/firmware.h>
+
+#include <asm/unaligned.h>
+
 #include "pci-quirks.h"
 #include "xhci-ext-caps.h"
 
@@ -1089,3 +1093,361 @@ static void quirk_usb_early_handoff(struct pci_dev *pdev)
 }
 DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
 			PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
+
+
+/* Renesas uPD720201/uPD720202 firmware loader */
+
+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 },
+};
+
+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 < 10000; i++) {
+		err = pci_read_config_byte(dev, 0xF5, &fw_status);
+		if (err)
+			return pcibios_err_to_errno(err);
+		if (!(fw_status & BIT(data0_or_data1)))
+			break;
+
+		udelay(1);
+	}
+	if (i == 10000)
+		return -ETIMEDOUT;
+
+	/*
+	 * step+2. Write FW data to "DATAX".
+	 * "LSB is left" => force little endian
+	 */
+	err = pci_write_config_dword(dev, data0_or_data1 ? 0xFC : 0xF8,
+				     (__force u32) cpu_to_le32(fw[step]));
+	if (err)
+		return pcibios_err_to_errno(err);
+
+	/* step+3. Set "Set DATAX". */
+	err = pci_write_config_byte(dev, 0xF5, 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_err(&dev->dev, "firmware size is not a multipe of 8.");
+		return -EINVAL;
+	}
+
+	/*
+	 * The bootrom chips of the big brother have sizes up to 64k.
+	 * This is as big as the firmware can get.
+	 */
+	if (length < 0x1000 || length >= 0x10000) {
+		dev_err(&dev->dev, "firmware is size %zd is not between 4k and 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, 0xF4, &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 & BIT(1)) {
+		dev_dbg(&pdev->dev, "FW Download Lock is engaged.");
+
+		if (fw_state & BIT(4))
+			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 & BIT(0)) {
+		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 & 0x70)) {
+	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 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, "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 %x. Giving up (poweroff/reboot needed).",
+			(fw_state & 0x70) >> 4);
+		return -EINVAL;
+	}
+}
+
+static int renesas_fw_download(struct pci_dev *pdev,
+	const struct firmware *fw, unsigned int retry_counter)
+{
+	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, 0xF4, BIT(0));
+	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 < 10000; i++) {
+		err = pci_read_config_byte(pdev, 0xF5, &fw_status);
+		if (err)
+			return pcibios_err_to_errno(err);
+		if (!(fw_status & (BIT(0) | BIT(1))))
+			break;
+
+		udelay(1);
+	}
+	if (i == 10000)
+		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, 0xF4, 0);
+	if (err)
+		return pcibios_err_to_errno(err);
+
+	/* 12. Read "Result Code" and confirm it is good. */
+	for (i = 0; i < 10000; i++) {
+		err = pci_read_config_byte(pdev, 0xF4, &fw_status);
+		if (err)
+			return pcibios_err_to_errno(err);
+		if (fw_status & BIT(4))
+			break;
+
+		udelay(1);
+	}
+	if (i == 10000) {
+		/* 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? - we can try to retry) */
+			if (retry_counter < 10) {
+				retry_counter++;
+				dev_warn(&pdev->dev, "Retry Firmware download: %d try.",
+					  retry_counter);
+				return renesas_fw_download(pdev, fw,
+							   retry_counter);
+			}
+			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;
+}
+
+static void renesas_fw_download_to_hw(struct pci_dev *pdev)
+{
+	const struct firmware *fw = NULL;
+	const struct renesas_fw_entry *entry;
+	int err;
+
+	/* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */
+	entry = renesas_needs_fw_dl(pdev);
+	if (!entry)
+		return;
+
+	err = renesas_fw_check_running(pdev);
+	/* Continue ahead, if the firmware is already running. */
+	if (err == 0)
+		return;
+
+	if (err != 1) {
+		dev_err(&pdev->dev, "firmware running check failed (%d).",
+			err);
+		return;
+	}
+
+	err = request_firmware(&fw, entry->firmware_name, &pdev->dev);
+	if (err) {
+		dev_err(&pdev->dev, "firmware request failed (%d).", err);
+		return;
+	}
+
+	err = renesas_fw_verify(pdev, fw->data, fw->size);
+	if (err)
+		goto free_fw;
+
+	err = renesas_fw_download(pdev, fw, 0);
+	if (err) {
+		dev_err(&pdev->dev, "firmware failed to download (%d).", err);
+		goto free_fw;
+	}
+
+ free_fw:
+	release_firmware(fw);
+	return;
+}
+
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_RENESAS, 0x0014, renesas_fw_download_to_hw);
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_RENESAS, 0x0015, renesas_fw_download_to_hw);

Patch
diff mbox series

diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 48672fa..3271a81 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -207,6 +207,55 @@  static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev)
 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 */
 static int xhci_pci_setup(struct usb_hcd *hcd)
 {
@@ -246,6 +295,11 @@  static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	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 */
@@ -424,6 +478,11 @@  static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
 	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);