diff mbox series

[v8,2/4] firmware: raspberrypi: Introduce vl805 init routine

Message ID 20200505161318.26200-3-nsaenzjulienne@suse.de
State New
Headers show
Series USB: pci-quirks: Add Raspberry Pi 4 quirk | expand

Commit Message

Nicolas Saenz Julienne May 5, 2020, 4:13 p.m. UTC
The Raspberry Pi 4 gets its USB functionality from VL805, a PCIe chip
that implements xHCI. After a PCI reset, VL805's firmware may either be
loaded directly from an EEPROM or, if not present, by the SoC's
co-processor, VideoCore. RPi4's VideoCore OS contains both the non public
firmware load logic and the VL805 firmware blob. The function this patch
introduces triggers the aforementioned process.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>

---

Change since v7:
- Use usleep_delay()
- Add comment about PCI errors
- Don't wait on error
- Typos

Change since v6:
- Add test to avoid loading the firmware when not needed
- Since we have it around, print VL805's firmware version, it'll make
debugging easier in the future
- Correct typos
- Add a clearer view of HW topology in patch description

Changes since v4:
- Inline function definition when RASPBERRYPI_FIRMWARE is not defined

Changes since v1:
- Move include into .c file and add forward declaration to .h

 drivers/firmware/raspberrypi.c             | 61 ++++++++++++++++++++++
 include/soc/bcm2835/raspberrypi-firmware.h |  7 +++
 2 files changed, 68 insertions(+)

Comments

Rob Herring May 7, 2020, 9:48 p.m. UTC | #1
On Tue,  5 May 2020 18:13:15 +0200, Nicolas Saenz Julienne wrote:
> The Raspberry Pi 4 gets its USB functionality from VL805, a PCIe chip
> that implements xHCI. After a PCI reset, VL805's firmware may either be
> loaded directly from an EEPROM or, if not present, by the SoC's
> co-processor, VideoCore. RPi4's VideoCore OS contains both the non public
> firmware load logic and the VL805 firmware blob. The function this patch
> introduces triggers the aforementioned process.
> 
> Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
> 
> ---
> 
> Change since v7:
> - Use usleep_delay()
> - Add comment about PCI errors
> - Don't wait on error
> - Typos
> 
> Change since v6:
> - Add test to avoid loading the firmware when not needed
> - Since we have it around, print VL805's firmware version, it'll make
> debugging easier in the future
> - Correct typos
> - Add a clearer view of HW topology in patch description
> 
> Changes since v4:
> - Inline function definition when RASPBERRYPI_FIRMWARE is not defined
> 
> Changes since v1:
> - Move include into .c file and add forward declaration to .h
> 
>  drivers/firmware/raspberrypi.c             | 61 ++++++++++++++++++++++
>  include/soc/bcm2835/raspberrypi-firmware.h |  7 +++
>  2 files changed, 68 insertions(+)
> 

Reviewed-by: Rob Herring <robh@kernel.org>
Stefan Wahren May 9, 2020, 10:02 a.m. UTC | #2
Hi Nicolas,

Am 07.05.20 um 23:48 schrieb Rob Herring:
> On Tue,  5 May 2020 18:13:15 +0200, Nicolas Saenz Julienne wrote:
>> The Raspberry Pi 4 gets its USB functionality from VL805, a PCIe chip
>> that implements xHCI. After a PCI reset, VL805's firmware may either be
>> loaded directly from an EEPROM or, if not present, by the SoC's
>> co-processor, VideoCore. RPi4's VideoCore OS contains both the non public
>> firmware load logic and the VL805 firmware blob. The function this patch
>> introduces triggers the aforementioned process.
>>
>> Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
>>
>> ---
>>
>> Change since v7:
>> - Use usleep_delay()
>> - Add comment about PCI errors
>> - Don't wait on error
>> - Typos
>>
>> Change since v6:
>> - Add test to avoid loading the firmware when not needed
>> - Since we have it around, print VL805's firmware version, it'll make
>> debugging easier in the future
>> - Correct typos
>> - Add a clearer view of HW topology in patch description
>>
>> Changes since v4:
>> - Inline function definition when RASPBERRYPI_FIRMWARE is not defined
>>
>> Changes since v1:
>> - Move include into .c file and add forward declaration to .h
>>
>>  drivers/firmware/raspberrypi.c             | 61 ++++++++++++++++++++++
>>  include/soc/bcm2835/raspberrypi-firmware.h |  7 +++
>>  2 files changed, 68 insertions(+)
>>
> Reviewed-by: Rob Herring <robh@kernel.org>

i modified the code a little bit for testing, but also successfully
tested it without my modifications:

diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
index 0d1422b..f3f4c2d 100644
--- a/drivers/firmware/raspberrypi.c
+++ b/drivers/firmware/raspberrypi.c
@@ -337,8 +337,10 @@ int rpi_firmware_init_vl805(struct pci_dev *pdev)
         * further down the line.
         */
        pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
&version);
-       if (version)
-               goto exit;
+       if (version) {
+               pci_info(pdev, "VL805 EEPROM firmware version %08x\n",
version);
+               return 0;
+       }
 
        dev_addr = pdev->bus->number << 20 | PCI_SLOT(pdev->devfn) << 15 |
                   PCI_FUNC(pdev->devfn) << 12;
@@ -353,9 +355,8 @@ int rpi_firmware_init_vl805(struct pci_dev *pdev)
 
        pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
                              &version);
-exit:
-       pci_info(pdev, "VL805 firmware version %08x\n", version);
 
+       pci_info(pdev, "VL805 RAM firmware version %08x\n", version);
        return 0;
 }
 EXPORT_SYMBOL_GPL(rpi_firmware_init_vl805);

Here are the my results with 3x Raspberry Pi 4:

VL805 EEPROM firmware version 000137ad
VL805 EEPROM firmware version 00013701
VL805 RAM firmware version 000137ad

So the whole patch series is:

Tested-by: Stefan Wahren <stefan.wahren@i2se.com>
Stefan Wahren May 9, 2020, 10:09 a.m. UTC | #3
Am 09.05.20 um 12:02 schrieb Stefan Wahren:
> Hi Nicolas,
>
> Am 07.05.20 um 23:48 schrieb Rob Herring:
>> On Tue,  5 May 2020 18:13:15 +0200, Nicolas Saenz Julienne wrote:
>>> The Raspberry Pi 4 gets its USB functionality from VL805, a PCIe chip
>>> that implements xHCI. After a PCI reset, VL805's firmware may either be
>>> loaded directly from an EEPROM or, if not present, by the SoC's
>>> co-processor, VideoCore. RPi4's VideoCore OS contains both the non public
>>> firmware load logic and the VL805 firmware blob. The function this patch
>>> introduces triggers the aforementioned process.
>>>
>>> Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
>>>
>>> ---
>>>
>>> Change since v7:
>>> - Use usleep_delay()
>>> - Add comment about PCI errors
>>> - Don't wait on error
>>> - Typos
>>>
>>> Change since v6:
>>> - Add test to avoid loading the firmware when not needed
>>> - Since we have it around, print VL805's firmware version, it'll make
>>> debugging easier in the future
>>> - Correct typos
>>> - Add a clearer view of HW topology in patch description
>>>
>>> Changes since v4:
>>> - Inline function definition when RASPBERRYPI_FIRMWARE is not defined
>>>
>>> Changes since v1:
>>> - Move include into .c file and add forward declaration to .h
>>>
>>>  drivers/firmware/raspberrypi.c             | 61 ++++++++++++++++++++++
>>>  include/soc/bcm2835/raspberrypi-firmware.h |  7 +++
>>>  2 files changed, 68 insertions(+)
>>>
>> Reviewed-by: Rob Herring <robh@kernel.org>
> i modified the code a little bit for testing, but also successfully
> tested it without my modifications:
>
> diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
> index 0d1422b..f3f4c2d 100644
> --- a/drivers/firmware/raspberrypi.c
> +++ b/drivers/firmware/raspberrypi.c
> @@ -337,8 +337,10 @@ int rpi_firmware_init_vl805(struct pci_dev *pdev)
>          * further down the line.
>          */
>         pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
> &version);
> -       if (version)
> -               goto exit;
> +       if (version) {
> +               pci_info(pdev, "VL805 EEPROM firmware version %08x\n",
> version);
> +               return 0;
> +       }
>  
>         dev_addr = pdev->bus->number << 20 | PCI_SLOT(pdev->devfn) << 15 |
>                    PCI_FUNC(pdev->devfn) << 12;
> @@ -353,9 +355,8 @@ int rpi_firmware_init_vl805(struct pci_dev *pdev)
>  
>         pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
>                               &version);
> -exit:
> -       pci_info(pdev, "VL805 firmware version %08x\n", version);
>  
> +       pci_info(pdev, "VL805 RAM firmware version %08x\n", version);
>         return 0;
>  }
>  EXPORT_SYMBOL_GPL(rpi_firmware_init_vl805);
>
> Here are the my results with 3x Raspberry Pi 4:
>
> VL805 EEPROM firmware version 000137ad
> VL805 EEPROM firmware version 00013701
> VL805 RAM firmware version 000137ad
>
> So the whole patch series is:
>
> Tested-by: Stefan Wahren <stefan.wahren@i2se.com>
>
Sorry, for sending from the wrong address:

Tested-by: Stefan Wahren <stefan.wahren@i2se.com>
Nicolas Saenz Julienne May 9, 2020, 7:20 p.m. UTC | #4
On Sat, 2020-05-09 at 12:02 +0200, Stefan Wahren wrote:
> Hi Nicolas,
> 
> Am 07.05.20 um 23:48 schrieb Rob Herring:
> > On Tue,  5 May 2020 18:13:15 +0200, Nicolas Saenz Julienne wrote:
> > > The Raspberry Pi 4 gets its USB functionality from VL805, a PCIe chip
> > > that implements xHCI. After a PCI reset, VL805's firmware may either be
> > > loaded directly from an EEPROM or, if not present, by the SoC's
> > > co-processor, VideoCore. RPi4's VideoCore OS contains both the non public
> > > firmware load logic and the VL805 firmware blob. The function this patch
> > > introduces triggers the aforementioned process.
> > > 
> > > Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
> > > 
> > > ---
> > > 
> > > Change since v7:
> > > - Use usleep_delay()
> > > - Add comment about PCI errors
> > > - Don't wait on error
> > > - Typos
> > > 
> > > Change since v6:
> > > - Add test to avoid loading the firmware when not needed
> > > - Since we have it around, print VL805's firmware version, it'll make
> > > debugging easier in the future
> > > - Correct typos
> > > - Add a clearer view of HW topology in patch description
> > > 
> > > Changes since v4:
> > > - Inline function definition when RASPBERRYPI_FIRMWARE is not defined
> > > 
> > > Changes since v1:
> > > - Move include into .c file and add forward declaration to .h
> > > 
> > >  drivers/firmware/raspberrypi.c             | 61 ++++++++++++++++++++++
> > >  include/soc/bcm2835/raspberrypi-firmware.h |  7 +++
> > >  2 files changed, 68 insertions(+)
> > > 
> > Reviewed-by: Rob Herring <robh@kernel.org>
> 
> i modified the code a little bit for testing, but also successfully
> tested it without my modifications:
> 
> diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
> index 0d1422b..f3f4c2d 100644
> --- a/drivers/firmware/raspberrypi.c
> +++ b/drivers/firmware/raspberrypi.c
> @@ -337,8 +337,10 @@ int rpi_firmware_init_vl805(struct pci_dev *pdev)
>          * further down the line.
>          */
>         pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
> &version);
> -       if (version)
> -               goto exit;
> +       if (version) {
> +               pci_info(pdev, "VL805 EEPROM firmware version %08x\n",
> version);
> +               return 0;
> +       }
>  
>         dev_addr = pdev->bus->number << 20 | PCI_SLOT(pdev->devfn) << 15 |
>                    PCI_FUNC(pdev->devfn) << 12;
> @@ -353,9 +355,8 @@ int rpi_firmware_init_vl805(struct pci_dev *pdev)
>  
>         pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
>                               &version);
> -exit:
> -       pci_info(pdev, "VL805 firmware version %08x\n", version);
>  
> +       pci_info(pdev, "VL805 RAM firmware version %08x\n", version);
>         return 0;
>  }
>  EXPORT_SYMBOL_GPL(rpi_firmware_init_vl805);
> 
> Here are the my results with 3x Raspberry Pi 4:
> 
> VL805 EEPROM firmware version 000137ad
> VL805 EEPROM firmware version 00013701
> VL805 RAM firmware version 000137ad
> 
> So the whole patch series is:
> 
> Tested-by: Stefan Wahren <stefan.wahren@i2se.com>

Thanks for taking the time!

Regards,
Nicolas
diff mbox series

Patch

diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
index da26a584dca0..a166ad0cec2c 100644
--- a/drivers/firmware/raspberrypi.c
+++ b/drivers/firmware/raspberrypi.c
@@ -12,6 +12,8 @@ 
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
 #include <soc/bcm2835/raspberrypi-firmware.h>
 
 #define MBOX_MSG(chan, data28)		(((data28) & ~0xf) | ((chan) & 0xf))
@@ -19,6 +21,8 @@ 
 #define MBOX_DATA28(msg)		((msg) & ~0xf)
 #define MBOX_CHAN_PROPERTY		8
 
+#define VL805_PCI_CONFIG_VERSION_OFFSET		0x50
+
 static struct platform_device *rpi_hwmon;
 static struct platform_device *rpi_clk;
 
@@ -286,6 +290,63 @@  struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node)
 }
 EXPORT_SYMBOL_GPL(rpi_firmware_get);
 
+/*
+ * The Raspberry Pi 4 gets its USB functionality from VL805, a PCIe chip that
+ * implements xHCI. After a PCI reset, VL805's firmware may either be loaded
+ * directly from an EEPROM or, if not present, by the SoC's co-processor,
+ * VideoCore. RPi4's VideoCore OS contains both the non public firmware load
+ * logic and the VL805 firmware blob. This function triggers the aforementioned
+ * process.
+ */
+int rpi_firmware_init_vl805(struct pci_dev *pdev)
+{
+	struct device_node *fw_np;
+	struct rpi_firmware *fw;
+	u32 dev_addr, version;
+	int ret;
+
+	fw_np = of_find_compatible_node(NULL, NULL,
+					"raspberrypi,bcm2835-firmware");
+	if (!fw_np)
+		return 0;
+
+	fw = rpi_firmware_get(fw_np);
+	of_node_put(fw_np);
+	if (!fw)
+		return -ENODEV;
+
+	/*
+	 * Make sure we don't trigger a firmware load unnecessarily.
+	 *
+	 * If something went wrong with PCI, this whole exercise would be
+	 * futile as VideoCore expects from us a configured PCI bus. Just take
+	 * the faulty version (likely ~0) and let xHCI's registration fail
+	 * further down the line.
+	 */
+	pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET, &version);
+	if (version)
+		goto exit;
+
+	dev_addr = pdev->bus->number << 20 | PCI_SLOT(pdev->devfn) << 15 |
+		   PCI_FUNC(pdev->devfn) << 12;
+
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_XHCI_RESET,
+				    &dev_addr, sizeof(dev_addr));
+	if (ret)
+		return ret;
+
+	/* Wait for vl805 to startup */
+	usleep_range(200, 1000);
+
+	pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
+			      &version);
+exit:
+	pci_info(pdev, "VL805 firmware version %08x\n", version);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rpi_firmware_init_vl805);
+
 static const struct of_device_id rpi_firmware_of_match[] = {
 	{ .compatible = "raspberrypi,bcm2835-firmware", },
 	{},
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h
index cc9cdbc66403..3025aca3c358 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -10,6 +10,7 @@ 
 #include <linux/of_device.h>
 
 struct rpi_firmware;
+struct pci_dev;
 
 enum rpi_firmware_property_status {
 	RPI_FIRMWARE_STATUS_REQUEST = 0,
@@ -141,6 +142,7 @@  int rpi_firmware_property(struct rpi_firmware *fw,
 int rpi_firmware_property_list(struct rpi_firmware *fw,
 			       void *data, size_t tag_size);
 struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node);
+int rpi_firmware_init_vl805(struct pci_dev *pdev);
 #else
 static inline int rpi_firmware_property(struct rpi_firmware *fw, u32 tag,
 					void *data, size_t len)
@@ -158,6 +160,11 @@  static inline struct rpi_firmware *rpi_firmware_get(struct device_node *firmware
 {
 	return NULL;
 }
+
+static inline int rpi_firmware_init_vl805(struct pci_dev *pdev)
+{
+	return 0;
+}
 #endif
 
 #endif /* __SOC_RASPBERRY_FIRMWARE_H__ */