[PATCH v14 0/5] Add the USB5744 hub driver as per new DT binding

Add the usb5744/usb2744 hub driver which does the reset gpio toggling and the i2c initialization sequence.
Tested the USB5744/USB2744 usb hub for usb0, usb1 with the DT nodes on KR260 AMD board.
Changes in v2: - Added the power_on_reset_us variable, for post-reset time. - Removed the DM_REGULATOR ifdef around the regulator API's. - Rename the i2c_init and fixed the return for the API's.
Changes in v3: - Rename i2c_init to init. - Fixed the return values for the dev_read_phandle_with_args API. - Removed the unneccessary cast uint8_t *.
Changes in v4: - Fixed the indentation issues. - Fixed the Reverse xmas tree for indentation. - Replaced dev_dbg to dev_err in all places.
Changes in v5: - Add new API for usb_onboard_hub_reset.
Changes in v6: - Add return for usb_onboard_hub_reset in probe.
Changes in v7: - Sort the structure onboard_hub_data data. - Remove the variable gpio_desc * for dm_gpio_free().
Changes in v8: - Sort the list for usb5744_data.
Changes in v9: - Added the Reviewer tag for patches 1/4, which got missed in v8.
Changes in v10: - Removed the GPIOD_ACTIVE_LOW flag in the devm_gpiod_get_optional.
Changes in v11: - Added the usb2514_hub_data and updated the reset_us to 1.
Changes in v12: - Updated the power on delay for usb2514_hub. - Updated the usb2514_hub_data in the patch 1/7.
Changes in v13: - Updated the reset and power on delay as per the USB5744 specification.
Changes in v14: - Remove the 6/7 patches from this series.
Venkatesh Yadav Abbarapu (5): usb: onboard-hub: Add reset-gpio support usb: onboard-hub: Fix the return values of regulator APIs usb: onboard-hub: add support for Microchip USB5744 usb: onboard-hub: Add i2c initialization for usb5744 hub usb: onboard-hub: Bail out if peer hub is already probed
common/usb_onboard_hub.c | 195 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 5 deletions(-)

As part of the reset, sets the direction of the pin to output before toggling the pin. Delay of millisecond is added in between low and high to meet the setup and hold time requirement of the reset. Update the usb2514 hub_data with the reset delay and power on delay values.
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de --- common/usb_onboard_hub.c | 51 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-)
diff --git a/common/usb_onboard_hub.c b/common/usb_onboard_hub.c index 68a04ac041..e4d1f12cb4 100644 --- a/common/usb_onboard_hub.c +++ b/common/usb_onboard_hub.c @@ -7,14 +7,50 @@ * Mostly inspired by Linux kernel v6.1 onboard_usb_hub driver */
+#include <asm/gpio.h> #include <dm.h> #include <dm/device_compat.h> +#include <linux/delay.h> #include <power/regulator.h>
struct onboard_hub { struct udevice *vdd; + struct gpio_desc *reset_gpio; };
+struct onboard_hub_data { + unsigned long reset_us; + unsigned long power_on_delay_us; +}; + +int usb_onboard_hub_reset(struct udevice *dev) +{ + struct onboard_hub_data *data = + (struct onboard_hub_data *)dev_get_driver_data(dev); + struct onboard_hub *hub = dev_get_priv(dev); + int ret; + + hub->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_IS_OUT); + + /* property is optional, don't return error! */ + if (!hub->reset_gpio) + return 0; + + ret = dm_gpio_set_value(hub->reset_gpio, 1); + if (ret) + return ret; + + udelay(data->reset_us); + + ret = dm_gpio_set_value(hub->reset_gpio, 0); + if (ret) + return ret; + + udelay(data->power_on_delay_us); + + return 0; +} + static int usb_onboard_hub_probe(struct udevice *dev) { struct onboard_hub *hub = dev_get_priv(dev); @@ -30,7 +66,7 @@ static int usb_onboard_hub_probe(struct udevice *dev) if (ret) dev_err(dev, "can't enable vdd-supply: %d\n", ret);
- return ret; + return usb_onboard_hub_reset(dev); }
static int usb_onboard_hub_remove(struct udevice *dev) @@ -38,6 +74,9 @@ static int usb_onboard_hub_remove(struct udevice *dev) struct onboard_hub *hub = dev_get_priv(dev); int ret;
+ if (hub->reset_gpio) + dm_gpio_free(hub->reset_gpio->dev, hub->reset_gpio); + ret = regulator_set_enable_if_allowed(hub->vdd, false); if (ret) dev_err(dev, "can't disable vdd-supply: %d\n", ret); @@ -45,10 +84,16 @@ static int usb_onboard_hub_remove(struct udevice *dev) return ret; }
+static const struct onboard_hub_data usb2514_data = { + .power_on_delay_us = 500, + .reset_us = 1, +}; + static const struct udevice_id usb_onboard_hub_ids[] = { /* Use generic usbVID,PID dt-bindings (usb-device.yaml) */ - { .compatible = "usb424,2514" }, /* USB2514B USB 2.0 */ - { } + { .compatible = "usb424,2514", /* USB2514B USB 2.0 */ + .data = (ulong)&usb2514_data, + } };
U_BOOT_DRIVER(usb_onboard_hub) = {

Don't error out if there is no vdd regulator supply, as these are optional properties.
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de --- common/usb_onboard_hub.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/common/usb_onboard_hub.c b/common/usb_onboard_hub.c index e4d1f12cb4..812e7749de 100644 --- a/common/usb_onboard_hub.c +++ b/common/usb_onboard_hub.c @@ -57,14 +57,18 @@ static int usb_onboard_hub_probe(struct udevice *dev) int ret;
ret = device_get_supply_regulator(dev, "vdd-supply", &hub->vdd); - if (ret) { + if (ret && ret != -ENOENT) { dev_err(dev, "can't get vdd-supply: %d\n", ret); return ret; }
- ret = regulator_set_enable_if_allowed(hub->vdd, true); - if (ret) - dev_err(dev, "can't enable vdd-supply: %d\n", ret); + if (hub->vdd) { + ret = regulator_set_enable_if_allowed(hub->vdd, true); + if (ret && ret != -ENOSYS) { + dev_err(dev, "can't enable vdd-supply: %d\n", ret); + return ret; + } + }
return usb_onboard_hub_reset(dev); }

Add support for the Microchip USB5744 USB3.0 and USB2.0 Hub. The usb5744 driver trigger hub reset signal after soft reset. The usb5744 hub need to reset after the phy initialization, which toggles the gpio.
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de --- common/usb_onboard_hub.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/common/usb_onboard_hub.c b/common/usb_onboard_hub.c index 812e7749de..4860de5323 100644 --- a/common/usb_onboard_hub.c +++ b/common/usb_onboard_hub.c @@ -93,10 +93,21 @@ static const struct onboard_hub_data usb2514_data = { .reset_us = 1, };
+static const struct onboard_hub_data usb5744_data = { + .power_on_delay_us = 1000, + .reset_us = 5, +}; + static const struct udevice_id usb_onboard_hub_ids[] = { /* Use generic usbVID,PID dt-bindings (usb-device.yaml) */ { .compatible = "usb424,2514", /* USB2514B USB 2.0 */ .data = (ulong)&usb2514_data, + }, { + .compatible = "usb424,2744", /* USB2744 USB 2.0 */ + .data = (ulong)&usb5744_data, + }, { + .compatible = "usb424,5744", /* USB5744 USB 3.0 */ + .data = (ulong)&usb5744_data, } };

On 11/25/24 5:12 AM, Venkatesh Yadav Abbarapu wrote:
Add support for the Microchip USB5744 USB3.0 and USB2.0 Hub. The usb5744 driver trigger hub reset signal after soft reset. The usb5744 hub need to reset after the phy initialization, which toggles the gpio.
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de
In
Re: [PATCH v13 3/7] usb: onboard-hub: add support for Microchip USB5744 Mon, 18 Nov 2024 14:18:43 +0100
I wrote
" I retract my RB on this patch until the discussion is concluded too. "
Please be more careful with RBs.
Applied all, thanks.

Add i2c initialization hook and set usb5744 platform data with function having required i2c initialization sequence.
Apart from the USB command attach, prevent the hub from suspend. when the “USB Attach with SMBUS (0xAA56)” command is issued to the hub, the hub is getting enumerated and then it puts in a suspend mode. This causes the hub to NAK any SMBUS access made by the SMBUS Master during this period and not able to see the hub's slave address while running the "i2c probe" command.
Prevent the MCU from the putting the HUB in suspend mode through register write. The BYPASS_UDC_SUSPEND bit (Bit 3) of the RuntimeFlags2 register at address 0x411D controls this aspect of the hub. The BYPASS_UDC_SUSPEND bit in register 0x411Dh must be set to ensure that the MCU is always enabled and ready to respond to SMBus runtime commands. This register needs to be written before the USB attach command is issued. The byte sequence is as follows: Slave addr: 0x2d 00 00 05 00 01 41 1D 08 Slave addr: 0x2d 99 37 00 Slave addr: 0x2d AA 56 00
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de --- common/usb_onboard_hub.c | 106 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-)
diff --git a/common/usb_onboard_hub.c b/common/usb_onboard_hub.c index 4860de5323..bf71266272 100644 --- a/common/usb_onboard_hub.c +++ b/common/usb_onboard_hub.c @@ -10,9 +10,15 @@ #include <asm/gpio.h> #include <dm.h> #include <dm/device_compat.h> +#include <i2c.h> #include <linux/delay.h> #include <power/regulator.h>
+#define USB5744_COMMAND_ATTACH 0x0056 +#define USB5744_COMMAND_ATTACH_LSB 0xAA +#define USB5744_CONFIG_REG_ACCESS 0x0037 +#define USB5744_CONFIG_REG_ACCESS_LSB 0x99 + struct onboard_hub { struct udevice *vdd; struct gpio_desc *reset_gpio; @@ -21,8 +27,89 @@ struct onboard_hub { struct onboard_hub_data { unsigned long reset_us; unsigned long power_on_delay_us; + int (*init)(struct udevice *dev); };
+static int usb5744_i2c_init(struct udevice *dev) +{ + /* + * Prevent the MCU from the putting the HUB in suspend mode through register write. + * The BYPASS_UDC_SUSPEND bit (Bit 3) of the RuntimeFlags2 register at address + * 0x411D controls this aspect of the hub. + * Format to write to hub registers via SMBus- 2D 00 00 05 00 01 41 1D 08 + * Byte 0: Address of slave 2D + * Byte 1: Memory address 00 + * Byte 2: Memory address 00 + * Byte 3: Number of bytes to write to memory + * Byte 4: Write configuration register (00) + * Byte 5: Write the number of data bytes (01- 1 data byte) + * Byte 6: LSB of register address 0x41 + * Byte 7: MSB of register address 0x1D + * Byte 8: value to be written to the register + */ + u8 data_buf[8] = {0x0, 0x5, 0x0, 0x1, 0x41, 0x1D, 0x08}; + u8 config_reg_access_buf = USB5744_CONFIG_REG_ACCESS; + struct udevice *i2c_bus = NULL, *i2c_dev; + struct ofnode_phandle_args phandle; + u8 buf = USB5744_COMMAND_ATTACH; + struct dm_i2c_chip *i2c_chip; + int ret, slave_addr; + + ret = dev_read_phandle_with_args(dev, "i2c-bus", NULL, 0, 0, &phandle); + if (ret) { + dev_err(dev, "i2c-bus not specified\n"); + return ret; + } + + ret = device_get_global_by_ofnode(ofnode_get_parent(phandle.node), &i2c_bus); + if (ret) { + dev_err(dev, "Failed to get i2c node, err: %d\n", ret); + return ret; + } + + ret = ofnode_read_u32(phandle.node, "reg", &slave_addr); + if (ret) + return ret; + + ret = i2c_get_chip(i2c_bus, slave_addr, 1, &i2c_dev); + if (ret) { + dev_err(dev, "%s: can't find i2c chip device for addr 0x%x\n", __func__, + slave_addr); + return ret; + } + + i2c_chip = dev_get_parent_plat(i2c_dev); + if (!i2c_chip) { + dev_err(dev, "parent platform data not found\n"); + return -EINVAL; + } + + i2c_chip->flags &= ~DM_I2C_CHIP_WR_ADDRESS; + /* SMBus write command */ + ret = dm_i2c_write(i2c_dev, 0, (uint8_t *)&data_buf, 8); + if (ret) { + dev_err(dev, "data_buf i2c_write failed, err:%d\n", ret); + return ret; + } + + /* Configuration register access command */ + ret = dm_i2c_write(i2c_dev, USB5744_CONFIG_REG_ACCESS_LSB, + &config_reg_access_buf, 2); + if (ret) { + dev_err(dev, "config_reg_access i2c_write failed, err: %d\n", ret); + return ret; + } + + /* USB Attach with SMBus */ + ret = dm_i2c_write(i2c_dev, USB5744_COMMAND_ATTACH_LSB, &buf, 2); + if (ret) { + dev_err(dev, "usb_attach i2c_write failed, err: %d\n", ret); + return ret; + } + + return 0; +} + int usb_onboard_hub_reset(struct udevice *dev) { struct onboard_hub_data *data = @@ -53,6 +140,8 @@ int usb_onboard_hub_reset(struct udevice *dev)
static int usb_onboard_hub_probe(struct udevice *dev) { + struct onboard_hub_data *data = + (struct onboard_hub_data *)dev_get_driver_data(dev); struct onboard_hub *hub = dev_get_priv(dev); int ret;
@@ -70,7 +159,21 @@ static int usb_onboard_hub_probe(struct udevice *dev) } }
- return usb_onboard_hub_reset(dev); + ret = usb_onboard_hub_reset(dev); + if (ret) + return ret; + + if (data->init) { + ret = data->init(dev); + if (ret) { + dev_err(dev, "onboard i2c init failed: %d\n", ret); + goto err; + } + } + return 0; +err: + dm_gpio_set_value(hub->reset_gpio, 0); + return ret; }
static int usb_onboard_hub_remove(struct udevice *dev) @@ -94,6 +197,7 @@ static const struct onboard_hub_data usb2514_data = { };
static const struct onboard_hub_data usb5744_data = { + .init = usb5744_i2c_init, .power_on_delay_us = 1000, .reset_us = 5, };

The .bind function is implemented to bind the correct "half" of the hub that the driver wants to bind, and returning -ENODEV for the other "half".
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de --- common/usb_onboard_hub.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/common/usb_onboard_hub.c b/common/usb_onboard_hub.c index bf71266272..6f28036e09 100644 --- a/common/usb_onboard_hub.c +++ b/common/usb_onboard_hub.c @@ -176,6 +176,26 @@ err: return ret; }
+static int usb_onboard_hub_bind(struct udevice *dev) +{ + struct ofnode_phandle_args phandle; + const void *fdt = gd->fdt_blob; + int ret, off; + + ret = dev_read_phandle_with_args(dev, "peer-hub", NULL, 0, 0, &phandle); + if (ret) { + dev_err(dev, "peer-hub not specified\n"); + return ret; + } + + off = ofnode_to_offset(phandle.node); + ret = fdt_node_check_compatible(fdt, off, "usb424,5744"); + if (!ret) + return 0; + + return -ENODEV; +} + static int usb_onboard_hub_remove(struct udevice *dev) { struct onboard_hub *hub = dev_get_priv(dev); @@ -218,6 +238,7 @@ static const struct udevice_id usb_onboard_hub_ids[] = { U_BOOT_DRIVER(usb_onboard_hub) = { .name = "usb_onboard_hub", .id = UCLASS_USB_HUB, + .bind = usb_onboard_hub_bind, .probe = usb_onboard_hub_probe, .remove = usb_onboard_hub_remove, .of_match = usb_onboard_hub_ids,

On 11/25/24 05:12, Venkatesh Yadav Abbarapu wrote:
The .bind function is implemented to bind the correct "half" of the hub that the driver wants to bind, and returning -ENODEV for the other "half".
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de
common/usb_onboard_hub.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/common/usb_onboard_hub.c b/common/usb_onboard_hub.c index bf71266272..6f28036e09 100644 --- a/common/usb_onboard_hub.c +++ b/common/usb_onboard_hub.c @@ -176,6 +176,26 @@ err: return ret; }
+static int usb_onboard_hub_bind(struct udevice *dev) +{
- struct ofnode_phandle_args phandle;
- const void *fdt = gd->fdt_blob;
- int ret, off;
- ret = dev_read_phandle_with_args(dev, "peer-hub", NULL, 0, 0, &phandle);
- if (ret) {
dev_err(dev, "peer-hub not specified\n");
return ret;
- }
- off = ofnode_to_offset(phandle.node);
- ret = fdt_node_check_compatible(fdt, off, "usb424,5744");
- if (!ret)
return 0;
- return -ENODEV;
+}
static int usb_onboard_hub_remove(struct udevice *dev) { struct onboard_hub *hub = dev_get_priv(dev); @@ -218,6 +238,7 @@ static const struct udevice_id usb_onboard_hub_ids[] = { U_BOOT_DRIVER(usb_onboard_hub) = { .name = "usb_onboard_hub", .id = UCLASS_USB_HUB,
- .bind = usb_onboard_hub_bind, .probe = usb_onboard_hub_probe, .remove = usb_onboard_hub_remove, .of_match = usb_onboard_hub_ids,
Hi All
This patch has been merged in v2025.01-rc3 (sha1 57e30b09fcfc88da72c310f979b35ed99a9b277c) and it avoids the STM32MP157c-DK2 board to boot:
U-Boot 2025.01-rc3 (Dec 02 2024 - 16:10:28 +0100)
CPU: STM32MP157CAC Rev.B Model: STMicroelectronics STM32MP157C-DK2 Discovery Board Board: stm32mp1 in trusted mode (st,stm32mp157c-dk2) Board: MB1272 Var2.0 Rev.C-01 DRAM: 512 MiB usb_onboard_hub hub@1: peer-hub not specified initcall failed at call c0123da5 (err=-2: No such file or directory) ### ERROR ### Please RESET the board ###
This boards embeds a "usb424,2514" hub, regarding the Documentation/devicetree/bindings/usb/microchip,usb2514.yaml, this kind of hub doesn't need the "peer-hub" property.
Patrice

On 12/2/24 4:16 PM, Patrice CHOTARD wrote:
On 11/25/24 05:12, Venkatesh Yadav Abbarapu wrote:
The .bind function is implemented to bind the correct "half" of the hub that the driver wants to bind, and returning -ENODEV for the other "half".
Signed-off-by: Venkatesh Yadav Abbarapu venkatesh.abbarapu@amd.com Reviewed-by: Marek Vasut marex@denx.de
common/usb_onboard_hub.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/common/usb_onboard_hub.c b/common/usb_onboard_hub.c index bf71266272..6f28036e09 100644 --- a/common/usb_onboard_hub.c +++ b/common/usb_onboard_hub.c @@ -176,6 +176,26 @@ err: return ret; }
+static int usb_onboard_hub_bind(struct udevice *dev) +{
- struct ofnode_phandle_args phandle;
- const void *fdt = gd->fdt_blob;
- int ret, off;
- ret = dev_read_phandle_with_args(dev, "peer-hub", NULL, 0, 0, &phandle);
- if (ret) {
dev_err(dev, "peer-hub not specified\n");
return ret;
- }
- off = ofnode_to_offset(phandle.node);
- ret = fdt_node_check_compatible(fdt, off, "usb424,5744");
- if (!ret)
return 0;
- return -ENODEV;
+}
- static int usb_onboard_hub_remove(struct udevice *dev) { struct onboard_hub *hub = dev_get_priv(dev);
@@ -218,6 +238,7 @@ static const struct udevice_id usb_onboard_hub_ids[] = { U_BOOT_DRIVER(usb_onboard_hub) = { .name = "usb_onboard_hub", .id = UCLASS_USB_HUB,
- .bind = usb_onboard_hub_bind, .probe = usb_onboard_hub_probe, .remove = usb_onboard_hub_remove, .of_match = usb_onboard_hub_ids,
Hi All
This patch has been merged in v2025.01-rc3 (sha1 57e30b09fcfc88da72c310f979b35ed99a9b277c) and it avoids the STM32MP157c-DK2 board to boot:
U-Boot 2025.01-rc3 (Dec 02 2024 - 16:10:28 +0100)
CPU: STM32MP157CAC Rev.B Model: STMicroelectronics STM32MP157C-DK2 Discovery Board Board: stm32mp1 in trusted mode (st,stm32mp157c-dk2) Board: MB1272 Var2.0 Rev.C-01 DRAM: 512 MiB usb_onboard_hub hub@1: peer-hub not specified initcall failed at call c0123da5 (err=-2: No such file or directory) ### ERROR ### Please RESET the board ###
This boards embeds a "usb424,2514" hub, regarding the Documentation/devicetree/bindings/usb/microchip,usb2514.yaml, this kind of hub doesn't need the "peer-hub" property.
Sigh ... thanks for testing.
Clearly this property is only needed for some devices, likely a boolean flag should be added to driver data to indicate a device requires this property ?
Please remind me if there is no fix by rc4 and I'll revert the series so it is not broken in 2025.01. Thank you!
participants (3)
-
Marek Vasut
-
Patrice CHOTARD
-
Venkatesh Yadav Abbarapu