The barebox driver was ported before Linux learnt about multiple supplies, a clock and the I2C-configured USB5744, and its device table has fallen behind.
Resync with Linux 7.3 drivers/usb/misc/onboard_usb_dev.c and .h. Signed-off-by: Ahmad Fatoum <[email protected]> --- drivers/usb/misc/Kconfig | 11 + drivers/usb/misc/onboard_usb_dev.c | 377 +++++++++++++++++++++++------ drivers/usb/misc/onboard_usb_dev.h | 132 +++++++++- 3 files changed, 446 insertions(+), 74 deletions(-) diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 6fe80a0f176b..eb8d3a208149 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -24,3 +24,14 @@ config USB_ONBOARD_DEV but need some non-trivial form of initialization, such as enabling a power regulator. An example for such device is the Realtek RTS5411 hub. + +config USB_ONBOARD_DEV_USB5744 + bool "Onboard USB Microchip usb5744 hub with SMBus support" + depends on USB_ONBOARD_DEV && I2C + help + Say Y here if you want to support onboard USB Microchip usb5744 + hub that requires SMBus initialization. + + This options enables usb5744 i2c default initialization sequence + during hub start-up configuration stage. Without it, a hub that + has an i2c-bus is not powered. diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 730f443dfefe..bb1b5ffd6e1e 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -1,22 +1,326 @@ // SPDX-License-Identifier: GPL-2.0-only +// SPDX-Comment: Origin-URL: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/misc/onboard_usb_dev.c?id=a174da76d3625a5e32f816ee27924ad5d6c1f19f /* * Driver for onboard USB devices * * Copyright (c) 2022, Google LLC */ -#include <driver.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/err.h> #include <linux/gpio/consumer.h> #include <init.h> +#include <i2c/i2c.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> #include <of.h> -#include <linux/printk.h> -#include <of_device.h> +#include <driver.h> #include <regulator.h> -#include <xfuncs.h> +#include <linux/slab.h> #include <linux/usb/usb.h> #include "onboard_usb_dev.h" +/* USB5744 register offset and mask */ +#define USB5744_CMD_ATTACH 0xAA +#define USB5744_CMD_ATTACH_LSB 0x56 +#define USB5744_CMD_CREG_ACCESS 0x99 +#define USB5744_CMD_CREG_ACCESS_LSB 0x37 +#define USB5744_CREG_MEM_ADDR 0x00 +#define USB5744_CREG_MEM_RD_ADDR 0x04 +#define USB5744_CREG_WRITE 0x00 +#define USB5744_CREG_READ 0x01 +#define USB5744_CREG_RUNTIMEFLAGS2 0x411D +#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3) + +/************************** Platform driver **************************/ + +struct onboard_dev { + struct regulator_bulk_data supplies[MAX_SUPPLIES]; + struct device *dev; + const struct onboard_dev_pdata *pdata; + struct gpio_desc *reset_gpio; + struct clk *clk; +}; + +static int onboard_dev_get_regulators(struct onboard_dev *onboard_dev) +{ + const char * const *supply_names = onboard_dev->pdata->supply_names; + unsigned int num_supplies = onboard_dev->pdata->num_supplies; + struct device *dev = onboard_dev->dev; + unsigned int i; + int err; + + if (num_supplies > MAX_SUPPLIES) + return dev_err_probe(dev, -EINVAL, "max %d supplies supported!\n", + MAX_SUPPLIES); + + for (i = 0; i < num_supplies; i++) + onboard_dev->supplies[i].supply = supply_names[i]; + + err = regulator_bulk_get(dev, num_supplies, onboard_dev->supplies); + if (err) + dev_err(dev, "Failed to get regulator supplies: %pe\n", + ERR_PTR(err)); + + return err; +} + +static int onboard_dev_power_on(struct onboard_dev *onboard_dev) +{ + int err; + + err = clk_prepare_enable(onboard_dev->clk); + if (err) { + dev_err(onboard_dev->dev, "failed to enable clock: %pe\n", + ERR_PTR(err)); + return err; + } + + err = regulator_bulk_enable(onboard_dev->pdata->num_supplies, + onboard_dev->supplies); + if (err) { + dev_err(onboard_dev->dev, "failed to enable supplies: %pe\n", + ERR_PTR(err)); + goto disable_clk; + } + + udelay(onboard_dev->pdata->reset_us); + gpiod_set_value_cansleep(onboard_dev->reset_gpio, 0); + udelay(onboard_dev->pdata->power_on_delay_us); + + return 0; + +disable_clk: + clk_disable_unprepare(onboard_dev->clk); + return err; +} + +static int onboard_dev_power_off(struct onboard_dev *onboard_dev) +{ + int err; + + gpiod_set_value_cansleep(onboard_dev->reset_gpio, 1); + + err = regulator_bulk_disable(onboard_dev->pdata->num_supplies, + onboard_dev->supplies); + if (err) { + dev_err(onboard_dev->dev, "failed to disable supplies: %pe\n", + ERR_PTR(err)); + return err; + } + + clk_disable_unprepare(onboard_dev->clk); + + return 0; +} + +#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744) +static int onboard_dev_5744_i2c_read_byte(struct i2c_client *client, u16 addr, u8 *data) +{ + struct i2c_msg msg[2]; + u8 rd_buf[3]; + int ret; + + u8 wr_buf[7] = {0, USB5744_CREG_MEM_ADDR, 4, + USB5744_CREG_READ, 1, + addr >> 8 & 0xff, + addr & 0xff}; + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = sizeof(wr_buf); + msg[0].buf = wr_buf; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + wr_buf[0] = USB5744_CMD_CREG_ACCESS; + wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB; + wr_buf[2] = 0; + msg[0].len = 3; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + wr_buf[0] = 0; + wr_buf[1] = USB5744_CREG_MEM_RD_ADDR; + msg[0].len = 2; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rd_buf; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + return ret; + *data = rd_buf[1]; + + return 0; +} + +static int onboard_dev_5744_i2c_write_byte(struct i2c_client *client, u16 addr, u8 data) +{ + struct i2c_msg msg[2]; + int ret; + + u8 wr_buf[8] = {0, USB5744_CREG_MEM_ADDR, 5, + USB5744_CREG_WRITE, 1, + addr >> 8 & 0xff, + addr & 0xff, + data}; + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = sizeof(wr_buf); + msg[0].buf = wr_buf; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + msg[0].len = 3; + wr_buf[0] = USB5744_CMD_CREG_ACCESS; + wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB; + wr_buf[2] = 0; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + return 0; +} + +static int onboard_dev_5744_i2c_init(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret; + u8 reg; + + /* + * Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled + * and ready to respond to SMBus runtime commands. + * The command writes 5 bytes to memory and single data byte in + * configuration register. + */ + ret = onboard_dev_5744_i2c_read_byte(client, + USB5744_CREG_RUNTIMEFLAGS2, ®); + if (ret) + return dev_err_probe(dev, ret, "CREG_RUNTIMEFLAGS2 read failed\n"); + + reg |= USB5744_CREG_BYPASS_UDC_SUSPEND; + ret = onboard_dev_5744_i2c_write_byte(client, + USB5744_CREG_RUNTIMEFLAGS2, reg); + if (ret) + return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n"); + + /* Send SMBus command to boot hub. */ + ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH, + USB5744_CMD_ATTACH_LSB); + if (ret < 0) + return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n"); + + return ret; +} +#else +static int onboard_dev_5744_i2c_init(struct i2c_client *client) +{ + return -ENODEV; +} +#endif + +static int onboard_dev_probe(struct device *dev) +{ + struct onboard_dev *onboard_dev; + struct device_node *i2c_node; + struct device_node *peer_node; + struct device *peer_dev; + int err; + + /* the USB 2 and USB 3 halves of one hub share reset and supplies */ + peer_node = of_parse_phandle(dev->of_node, "peer-hub", 0); + if (peer_node) { + peer_dev = of_find_device_by_node(peer_node); + if (peer_dev && dev_get_drvdata(peer_dev)) + return 0; + } + + onboard_dev = devm_kzalloc(dev, sizeof(*onboard_dev), GFP_KERNEL); + if (!onboard_dev) + return -ENOMEM; + + onboard_dev->pdata = device_get_match_data(dev); + if (!onboard_dev->pdata) + return -EINVAL; + + onboard_dev->dev = dev; + + err = onboard_dev_get_regulators(onboard_dev); + if (err) + return err; + + onboard_dev->clk = clk_get_optional(dev, NULL); + if (IS_ERR(onboard_dev->clk)) + return dev_err_probe(dev, PTR_ERR(onboard_dev->clk), + "failed to get clock\n"); + + onboard_dev->reset_gpio = gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(onboard_dev->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(onboard_dev->reset_gpio), + "failed to get reset GPIO\n"); + + dev_set_drvdata(dev, onboard_dev); + + err = onboard_dev_power_on(onboard_dev); + if (err) + return err; + + i2c_node = of_parse_phandle(dev->of_node, "i2c-bus", 0); + if (i2c_node) { + struct i2c_client *client = NULL; + +#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744) + client = of_find_i2c_device_by_node(i2c_node); +#endif + of_node_put(i2c_node); + + if (!client) { + err = -EPROBE_DEFER; + goto err_power_off; + } + + if (of_device_is_compatible(dev->of_node, "usb424,2744") || + of_device_is_compatible(dev->of_node, "usb424,5744")) { + err = onboard_dev_5744_i2c_init(client); + } + + put_device(&client->dev); + if (err < 0) + goto err_power_off; + } + + return 0; + +err_power_off: + onboard_dev_power_off(onboard_dev); + return err; +} + +MODULE_DEVICE_TABLE(of, onboard_dev_match); + +static struct driver onboard_dev_driver = { + .probe = onboard_dev_probe, + .name = "onboard-usb-dev", + .of_match_table = onboard_dev_match, +}; +device_platform_driver(onboard_dev_driver); + void of_usb_host_probe_devs(struct usb_host *host) { struct device_node *np; @@ -28,71 +332,6 @@ void of_usb_host_probe_devs(struct usb_host *host) of_platform_populate(np, onboard_dev_match, host->hw_dev); } -struct onboard_dev { - struct regulator *vdd; - struct device *dev; - const struct onboard_dev_pdata *pdata; - struct gpio_desc *reset_gpio; -}; - -static int onboard_dev_power_on(struct onboard_dev *onboard_dev) -{ - int err; - - err = regulator_enable(onboard_dev->vdd); - if (err) { - dev_err(onboard_dev->dev, "failed to enable regulator: %pe\n", - ERR_PTR(err)); - return err; - } - - udelay(onboard_dev->pdata->reset_us); - gpiod_set_value(onboard_dev->reset_gpio, 0); - - return 0; -} - -static int onboard_dev_probe(struct device *dev) -{ - struct device_node *peer_node; - struct device *peer_dev; - struct onboard_dev *onboard_dev; - - peer_node = of_parse_phandle(dev->of_node, "peer-hub", 0); - if (peer_node) { - peer_dev = of_find_device_by_node(peer_node); - if (peer_dev && peer_dev->priv) - return 0; - } - - onboard_dev = xzalloc(sizeof(*onboard_dev)); - - onboard_dev->pdata = device_get_match_data(dev); - if (!onboard_dev->pdata) - return -EINVAL; - - onboard_dev->vdd = regulator_get(dev, "vdd"); - if (IS_ERR(onboard_dev->vdd)) - return PTR_ERR(onboard_dev->vdd); - - onboard_dev->reset_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(onboard_dev->reset_gpio)) - return dev_errp_probe(dev, onboard_dev->reset_gpio, - "failed to get reset GPIO\n"); - - onboard_dev->dev = dev; - dev->priv = onboard_dev; - - return onboard_dev_power_on(onboard_dev); -} - -static struct driver onboard_dev_driver = { - .name = "onboard-usb-dev", - .probe = onboard_dev_probe, - .of_compatible = onboard_dev_match, -}; -device_platform_driver(onboard_dev_driver); - MODULE_AUTHOR("Matthias Kaehlcke <[email protected]>"); MODULE_DESCRIPTION("Driver for discrete onboard USB devices"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index edb91485bd83..4adb17867f76 100644 --- a/drivers/usb/misc/onboard_usb_dev.h +++ b/drivers/usb/misc/onboard_usb_dev.h @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-Comment: Origin-URL: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/misc/onboard_usb_dev.h?id=9a71ac508974d44d42d358f42e6a766ec67a73d1 */ /* * Copyright (c) 2022, Google LLC */ @@ -6,50 +7,171 @@ #ifndef _USB_MISC_ONBOARD_USB_DEV_H #define _USB_MISC_ONBOARD_USB_DEV_H +#define MAX_SUPPLIES 2 + struct onboard_dev_pdata { unsigned long reset_us; /* reset pulse width in us */ + unsigned long power_on_delay_us; /* power on delay in us */ + unsigned int num_supplies; /* number of supplies */ + const char * const supply_names[MAX_SUPPLIES]; + bool is_hub; }; static const struct onboard_dev_pdata microchip_usb424_data = { .reset_us = 1, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata microchip_usb2514_data = { + .reset_us = 1, + .num_supplies = 2, + .supply_names = { "vdd", "vdda" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata microchip_usb5744_data = { + .reset_us = 0, + .power_on_delay_us = 10000, + .num_supplies = 2, + .supply_names = { "vdd", "vdd2" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata parade_ps5511_data = { + .reset_us = 500, + .num_supplies = 2, + .supply_names = { "vddd11", "vdd33"}, + .is_hub = true, }; static const struct onboard_dev_pdata realtek_rts5411_data = { .reset_us = 0, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = true, }; -static const struct onboard_dev_pdata ti_tusb8041_data = { +static const struct onboard_dev_pdata realtek_rtl8188etv_data = { + .reset_us = 0, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = false, +}; + +static const struct onboard_dev_pdata ti_tusb8020b_data = { .reset_us = 3000, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata bison_intcamera_data = { + .reset_us = 1000, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = false, +}; + +static const struct onboard_dev_pdata corechips_sl6341_data = { + .reset_us = 10000, + .num_supplies = 2, + .supply_names = { "vdd1v1", "vdd3v3" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata cypress_hx3_data = { + .reset_us = 10000, + .num_supplies = 2, + .supply_names = { "vdd", "vdd2" }, + .is_hub = true, }; static const struct onboard_dev_pdata genesys_gl850g_data = { .reset_us = 3, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = true, }; static const struct onboard_dev_pdata genesys_gl852g_data = { .reset_us = 50, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata genesys_gl3590_data = { + .reset_us = 50, + .num_supplies = 2, + .supply_names = { "vdd", "vdd12" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata usb_a_conn_data = { + .num_supplies = 1, + .supply_names = { "vbus" }, }; static const struct onboard_dev_pdata vialab_vl817_data = { .reset_us = 10, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata wch_ch334_data = { + .reset_us = 14000, + .num_supplies = 2, + .supply_names = { "vdd33", "v5" }, + .is_hub = true, +}; + +static const struct onboard_dev_pdata xmos_xvf3500_data = { + .reset_us = 1, + .num_supplies = 2, + .supply_names = { "vdd", "vddio" }, + .is_hub = false, }; static const struct of_device_id onboard_dev_match[] = { - { .compatible = "usb424,2514", .data = µchip_usb424_data, }, + { .compatible = "usb-a-connector", .data = &usb_a_conn_data, }, + { .compatible = "usb424,2412", .data = µchip_usb424_data, }, + { .compatible = "usb424,2514", .data = µchip_usb2514_data, }, { .compatible = "usb424,2517", .data = µchip_usb424_data, }, - { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, - { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, + { .compatible = "usb424,2744", .data = µchip_usb5744_data, }, + { .compatible = "usb424,5744", .data = µchip_usb5744_data, }, + { .compatible = "usb451,8025", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8027", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8140", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8142", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8440", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8442", .data = &ti_tusb8020b_data, }, + { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, }, + { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, }, + { .compatible = "usb4b4,6570", .data = µchip_usb424_data, }, { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, + { .compatible = "usb5e3,625", .data = &genesys_gl3590_data, }, + { .compatible = "usb5e3,626", .data = &genesys_gl852g_data, }, + { .compatible = "usbbda,179", .data = &realtek_rtl8188etv_data, }, { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, + { .compatible = "usb1a40,0101", .data = &vialab_vl817_data, }, + { .compatible = "usb1a86,8091", .data = &wch_ch334_data, }, + { .compatible = "usb1da0,5511", .data = ¶de_ps5511_data, }, + { .compatible = "usb1da0,55a1", .data = ¶de_ps5511_data, }, { .compatible = "usb2109,817", .data = &vialab_vl817_data, }, { .compatible = "usb2109,2817", .data = &vialab_vl817_data, }, + { .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, }, + { .compatible = "usb3431,6241", .data = &corechips_sl6341_data, }, + { .compatible = "usb3431,6341", .data = &corechips_sl6341_data, }, + { .compatible = "usb5986,1198", .data = &bison_intcamera_data, }, {} }; -MODULE_DEVICE_TABLE(of, onboard_dev_match); #endif /* _USB_MISC_ONBOARD_USB_DEV_H */ -- 2.47.3
