[U-Boot] [PATCH v2 0/80] dm: Add USB support

This series adds driver model support to USB. The intent is to permit the various subsystems (OHCI, EHCI, XHCI) to co-exist and allow any number of USB ports of different types.
With the RFC series, only USB controllers had a real driver model device. USB devices (including the hub in the controller) were not modelled as driver model devices.
While this was expedient, and produced much fewer patches, it is not a long-term solution. Also, since then, driver model Ethernet support (which USB can use) has been merged to u-boot-dm/next. It seems better to bite the bullet and do a full conversion.
Unfortunately this results in a very large series. It includes:
- USB uclass - USB hub uclass - Adjustments to make USB keyboard, Ethernet and storage continue to work - Sandbox USB emulation support - Sandbox USB emulations for flash and hub devices - A reasonable set of tests - EHCI and XHCI support (not OHCI) - Conversion of Tegra and Exynos drivers to driver model - Adjustments to make the 'usb' command operate as expected - README describing how USB works with driver model
Both EHCI and XHCI needed a little bit of refactoring before they are converted to driver model - e.g. removal of weak fuctions and splitting up the init code.
This series includes patches to remove the non-driver-model code for Exynos and Tegra, to be applied once everything is stable. For now it is convenient to be able to compare the two options.
This series does not change how USB is started up. The 'usb start' command scans the buses as before, and takes just as long. Once it has started you can use 'dm tree' to see the tree structure of the USB bus, although 'usb tree' provides a better view. It is unclear how we could implement lazy init for USB given the long time it takes to probe the bus.
A README is provided to describe how USB works under driver model.
This series is available at u-boot-dm/usb-working.
Changes in v2: - Rewrite and expand series to support driver model fully
Simon Glass (80): linker_lists: Add a function to access a linker list entry sandbox: Fix comment for os_open() dm: test: bus: Use a local variable to simplify code dm: exynos: snow: Move the keyboard to I2C dm: core: Support allocating driver-private data for DMA dm: core: Convert driver_bind() to use const dm: core: Rename driver data function to dev_get_driver_data() dm: core: Mark device as active before calling uclass probe() methods dm: core: Add device children and sibling functions dm: gpio: Add an implementation for gpio_get_number() dm: usb: Add a uclass for USB controllers dm: usb: Adjust usb command to prepare for driver model dm: usb: Adjust usb_alloc_new_device() to return an error dm: usb: Convert 'usb' command to support driver model dm: usb: Drop the legacy USB init sequence dm: usb: Refactor port resets dm: usb: Move descriptor setup code into its own function dm: usb: Split out more code from usb_new_device() dm: usb: Complete the splitting up of usb_new_device() dm: usb: Convert core usb.c file to support driver model dm: usb: Split hub detection into its own function dm: usb: Add driver model support for hubs dm: usb: Move USB storage definitions to usb_defs.h dm: usb: Fix type problems in usb_stor_get_info() dm: usb: Simply device finding code in usb_storage dm: usb: Adjust usb_storage to work with sandbox dm: usb: Move storage device scanning into its own function dm: usb: Convert usb_storage to driver model dm: usb: Move all the EHCI weak functions together and declare them dm: usb: Pass EHCI controller pointer to ehci_get_port_speed() dm: usb: Allow ECHI to hold private data for the controller dm: usb: tegra: Store the controller type explicitly dm: usb: Pass EHCI controller pointer to ehci_powerup_fixup() dm: usb: tegra: Drop use of global controller variable dm: usb: Pass EHCI controller pointer to ehci_set_usbmode() dm: usb: Pass EHCI controller pointer to ehci_get_portsc_register() dm: usb: ehci: Use a function to find the controller from struct udevice dm: usb: Refactor EHCI init dm: usb: Drop the EHCI weak functions dm: usb: Change ehci_reset() to use a pointer dm: usb: Add driver model support to EHCI dm: usb: Allow USB drivers to be declared and auto-probed dm: usb: Bind generic USB devices when there is no driver dm: usb: Allow setting up a USB controller as a device/gadget dm: usb: Split out the keyboard probe into its own function dm: usb: Support driver model with USB keyboards dm: usb: tegra: Add vbus GPIOs for nyan dm: usb: Move struct usb_string to a common place dm: usb: sandbox: Add a uclass for USB device emulation dm: usb: sandbox: Reset emulation devices in usb stop() dm: usb: sandbox: Add an emulator for USB flash devices dm: usb: sandbox: Add an emulator for USB hub emulation dm: usb: sandbox: Add a driver for sandbox dm: usb: dts: sandbox: Add some sample USB devices to sandbox dm: usb: Add support for USB ethernet devices with driver model dm: usb: exynos: Add driver model support to exynos EHCI dm: usb: tegra: Remove the port_addr_clear_csc variable dm: usb: tegra: Tidy up error handling and a static function dm: usb: tegra: Move most of init/uninit into a function dm: usb: tegra: Add driver model support to tegra EHCI dm: usb: xhci: Use a function to get xhci_ctrl dm: usb: xhci: Use explicit parameters for xhci_alloc_virt_device() dm: usb: xhci: Use explicit parameters for xhci_setup_addressable_virt_dev() dm: usb: xhci: Factor out common init/uninit dm: usb: Support driver model in XHCI dm: usb: Rename the XHCI HCD to U-Boot dm: usb: exynos: Adjust XHCI driver to support driver model dm: usb: exynos: Use driver model for USB dm: usb: exynos: Enable both USB ports on snow dm: usb: exynos: Enable both EHCI and XHCI on snow dm: usb: tegra: Move to driver model for USB dm: usb: Add a generic descriptor struct dm: usb: Tidy up pipe value decoding dm: usb: sandbox: Enable USB dm: test: Correct printf() output nit in 'dm uclass' dm: test: Allow 'dm test' to select a particular test to run dm: usb: Add tests for the USB uclass dm: usb: tegra: Drop legacy USB code dm: usb: exynos: Drop legacy USB code dm: usb: Add a README for driver model
Makefile | 1 + arch/arm/cpu/armv7/exynos/Kconfig | 3 + arch/arm/dts/exynos5250-snow.dts | 5 +- arch/arm/dts/tegra124-nyan-big.dts | 2 + arch/arm/mach-tegra/Kconfig | 3 + arch/sandbox/dts/sandbox.dts | 40 ++ board/genesi/mx51_efikamx/efikamx-usb.c | 4 +- board/nvidia/common/board.c | 1 - common/cmd_usb.c | 198 ++++++++-- common/usb.c | 277 ++++++++------ common/usb_hub.c | 194 ++++++++-- common/usb_kbd.c | 119 ++++-- common/usb_storage.c | 249 ++++++------ configs/sandbox_defconfig | 4 + doc/driver-model/usb-info.txt | 415 ++++++++++++++++++++ drivers/core/device-remove.c | 4 +- drivers/core/device.c | 66 +++- drivers/core/lists.c | 2 +- drivers/gpio/gpio-uclass.c | 12 + drivers/i2c/s3c24x0_i2c.c | 2 +- drivers/i2c/tegra_i2c.c | 6 +- drivers/usb/Kconfig | 16 + drivers/usb/emul/Kconfig | 8 + drivers/usb/emul/Makefile | 10 + drivers/usb/emul/sandbox_flash.c | 423 +++++++++++++++++++++ drivers/usb/emul/sandbox_hub.c | 303 +++++++++++++++ drivers/usb/emul/usb-emul-uclass.c | 263 +++++++++++++ drivers/usb/eth/usb_ether.c | 52 ++- drivers/usb/gadget/ci_udc.c | 4 + drivers/usb/host/Makefile | 5 + drivers/usb/host/ehci-exynos.c | 127 +++---- drivers/usb/host/ehci-faraday.c | 112 +++--- drivers/usb/host/ehci-hcd.c | 379 ++++++++++++++----- drivers/usb/host/ehci-mx5.c | 12 + drivers/usb/host/ehci-tegra.c | 288 +++++++------- drivers/usb/host/ehci.h | 47 +++ drivers/usb/host/usb-sandbox.c | 117 ++++++ drivers/usb/host/usb-uclass.c | 645 ++++++++++++++++++++++++++++++++ drivers/usb/host/xhci-exynos5.c | 106 +++--- drivers/usb/host/xhci-mem.c | 24 +- drivers/usb/host/xhci-ring.c | 8 +- drivers/usb/host/xhci.c | 312 ++++++++++++--- drivers/usb/host/xhci.h | 31 +- drivers/usb/musb-new/musb_uboot.c | 4 +- include/configs/sandbox.h | 2 + include/configs/snow.h | 2 + include/dm/device-internal.h | 2 +- include/dm/device.h | 51 ++- include/dm/test.h | 7 +- include/dm/uclass-id.h | 5 + include/fdtdec.h | 5 - include/linker_lists.h | 10 + include/linux/usb/ch9.h | 18 + include/linux/usb/gadget.h | 13 - include/os.h | 2 +- include/usb.h | 494 +++++++++++++++++++++++- include/usb_defs.h | 68 +++- lib/fdtdec.c | 5 - test/dm/Makefile | 1 + test/dm/bus.c | 16 +- test/dm/cmd_dm.c | 13 +- test/dm/test-dm.sh | 3 + test/dm/test-main.c | 7 +- test/dm/test-uclass.c | 3 +- test/dm/test.dts | 41 ++ test/dm/usb.c | 50 +++ 66 files changed, 4807 insertions(+), 914 deletions(-) create mode 100644 doc/driver-model/usb-info.txt create mode 100644 drivers/usb/emul/Kconfig create mode 100644 drivers/usb/emul/Makefile create mode 100644 drivers/usb/emul/sandbox_flash.c create mode 100644 drivers/usb/emul/sandbox_hub.c create mode 100644 drivers/usb/emul/usb-emul-uclass.c create mode 100644 drivers/usb/host/usb-sandbox.c create mode 100644 drivers/usb/host/usb-uclass.c create mode 100644 test/dm/usb.c

Once declared, you cannot access a linker_list entry since you do not have a symbol name for it. Add llsym() macro to provide this. This avoids searching for the symbol at run-time based on name.
An example usage is to declare a driver with U_BOOT_DRIVER(), then obtain a pointer to that driver later.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
include/linker_lists.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/include/linker_lists.h b/include/linker_lists.h index 940c871..b22d169 100644 --- a/include/linker_lists.h +++ b/include/linker_lists.h @@ -103,6 +103,16 @@ */
/** + * ll_sym() - Access a linker-generated array entry + * @_type: Data type of the entry + * @_name: Name of the entry + * @_list: name of the list. Should contain only characters allowed + * in a C variable name! + */ +#define llsym(_type, _name, _list) \ + ((_type *)&_u_boot_list_2_##_list##_2_##_name) + +/** * ll_entry_declare() - Declare linker-generated array entry * @_type: Data type of the entry * @_name: Name of the entry

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
Once declared, you cannot access a linker_list entry since you do not have a symbol name for it. Add llsym() macro to provide this. This avoids searching for the symbol at run-time based on name.
An example usage is to declare a driver with U_BOOT_DRIVER(), then obtain a pointer to that driver later.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
include/linker_lists.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
Applied to u-boot-dm/next.

This has the wrong #define in the function comment. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
include/os.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/os.h b/include/os.h index e3645e0..a758f09 100644 --- a/include/os.h +++ b/include/os.h @@ -64,7 +64,7 @@ off_t os_lseek(int fd, off_t offset, int whence); * Access to the OS open() system call * * \param pathname Pathname of file to open - * \param flags Flags, like O_RDONLY, O_RDWR + * \param flags Flags, like OS_O_RDONLY, OS_O_RDWR * \return file descriptor, or -1 on error */ int os_open(const char *pathname, int flags);

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
This has the wrong #define in the function comment. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
include/os.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm/next.

Adjust this test to avoid repeating the same code too often.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
test/dm/bus.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/test/dm/bus.c b/test/dm/bus.c index faffe6a..116a52d 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -273,20 +273,22 @@ DM_TEST(dm_test_bus_parent_data, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); /* As above but the size is controlled by the uclass */ static int dm_test_bus_parent_data_uclass(struct dm_test_state *dms) { + struct driver *drv; struct udevice *bus; int size; int ret;
/* Set the driver size to 0 so that the uclass size is used */ ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); - size = bus->driver->per_child_auto_alloc_size; + drv = (struct driver *)bus->driver; + size = drv->per_child_auto_alloc_size; bus->uclass->uc_drv->per_child_auto_alloc_size = size; - bus->driver->per_child_auto_alloc_size = 0; + drv->per_child_auto_alloc_size = 0; ret = test_bus_parent_data(dms); if (ret) return ret; bus->uclass->uc_drv->per_child_auto_alloc_size = 0; - bus->driver->per_child_auto_alloc_size = size; + drv->per_child_auto_alloc_size = size;
return 0; } @@ -414,19 +416,21 @@ DM_TEST(dm_test_bus_parent_platdata, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); static int dm_test_bus_parent_platdata_uclass(struct dm_test_state *dms) { struct udevice *bus; + struct driver *drv; int size; int ret;
/* Set the driver size to 0 so that the uclass size is used */ ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); - size = bus->driver->per_child_platdata_auto_alloc_size; + drv = (struct driver *)bus->driver; + size = drv->per_child_platdata_auto_alloc_size; bus->uclass->uc_drv->per_child_platdata_auto_alloc_size = size; - bus->driver->per_child_platdata_auto_alloc_size = 0; + drv->per_child_platdata_auto_alloc_size = 0; ret = test_bus_parent_platdata(dms); if (ret) return ret; bus->uclass->uc_drv->per_child_platdata_auto_alloc_size = 0; - bus->driver->per_child_platdata_auto_alloc_size = size; + drv->per_child_platdata_auto_alloc_size = size;
return 0; }

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
Adjust this test to avoid repeating the same code too often.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
test/dm/bus.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
Applied to u-boot-dm/next.

The snow keyboard on shipping devices is actually on I2C not SPI. Move the label to indicate this.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
arch/arm/dts/exynos5250-snow.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 7d8be69..d6777ee 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -40,7 +40,7 @@ };
i2c4: i2c@12ca0000 { - cros-ec@1e { + cros_ec: cros-ec@1e { reg = <0x1e>; compatible = "google,cros-ec"; i2c-max-frequency = <100000>; @@ -65,7 +65,7 @@ spi@131b0000 { spi-max-frequency = <1000000>; spi-deactivate-delay = <100>; - cros_ec: cros-ec@0 { + cros-ec@0 { reg = <0>; compatible = "google,cros-ec"; spi-max-frequency = <5000000>;

Hi,
On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
The snow keyboard on shipping devices is actually on I2C not SPI. Move the label to indicate this.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
arch/arm/dts/exynos5250-snow.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
This patch is not needed now, so I'm dropping it. Commit c9b53f8d solved this problem.
Regards, Simon

Some driver want to put DMA buffers in their private data. Add a flag to tell driver model to align driver-private data to a cache boundary so that DMA will work correctly in this case.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/core/device.c | 19 +++++++++++++++++-- include/dm/device.h | 3 +++ 2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/core/device.c b/drivers/core/device.c index 7483405..1ca5d1c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -164,6 +164,21 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, -1, devp); }
+static void *alloc_priv(int size, uint flags) +{ + void *priv; + + if (flags & DM_FLAG_ALLOC_PRIV_DMA) { + priv = memalign(ARCH_DMA_MINALIGN, size); + if (priv) + memset(priv, '\0', size); + } else { + priv = calloc(1, size); + } + + return priv; +} + int device_probe_child(struct udevice *dev, void *parent_priv) { struct driver *drv; @@ -182,7 +197,7 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
/* Allocate private data if requested */ if (drv->priv_auto_alloc_size) { - dev->priv = calloc(1, drv->priv_auto_alloc_size); + dev->priv = alloc_priv(drv->priv_auto_alloc_size, drv->flags); if (!dev->priv) { ret = -ENOMEM; goto fail; @@ -206,7 +221,7 @@ int device_probe_child(struct udevice *dev, void *parent_priv) per_child_auto_alloc_size; } if (size) { - dev->parent_priv = calloc(1, size); + dev->parent_priv = alloc_priv(size, drv->flags); if (!dev->parent_priv) { ret = -ENOMEM; goto fail; diff --git a/include/dm/device.h b/include/dm/device.h index 6980954..f27b34b 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -30,6 +30,9 @@ struct driver_info; /* DM is responsible for allocating and freeing parent_platdata */ #define DM_FLAG_ALLOC_PARENT_PDATA (1 << 3)
+/* Allocate driver private data on a DMA boundary */ +#define DM_FLAG_ALLOC_PRIV_DMA (1 << 4) + /** * struct udevice - An instance of a driver *

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
Some driver want to put DMA buffers in their private data. Add a flag to tell driver model to align driver-private data to a cache boundary so that DMA will work correctly in this case.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/core/device.c | 19 +++++++++++++++++-- include/dm/device.h | 3 +++ 2 files changed, 20 insertions(+), 2 deletions(-)
Applied to u-boot-dm/next.

The driver is not modified by driver model, so update driver_bind() to recognise that.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/core/device-remove.c | 4 ++-- drivers/core/device.c | 7 ++++--- include/dm/device-internal.h | 2 +- include/dm/device.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 3a5f48d..7fee1c0 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -66,7 +66,7 @@ static int device_chld_remove(struct udevice *dev)
int device_unbind(struct udevice *dev) { - struct driver *drv; + const struct driver *drv; int ret;
if (!dev) @@ -139,7 +139,7 @@ void device_free(struct udevice *dev)
int device_remove(struct udevice *dev) { - struct driver *drv; + const struct driver *drv; int ret;
if (!dev) diff --git a/drivers/core/device.c b/drivers/core/device.c index 1ca5d1c..f1a03d9 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -24,8 +24,9 @@
DECLARE_GLOBAL_DATA_PTR;
-int device_bind(struct udevice *parent, struct driver *drv, const char *name, - void *platdata, int of_offset, struct udevice **devp) +int device_bind(struct udevice *parent, const struct driver *drv, + const char *name, void *platdata, int of_offset, + struct udevice **devp) { struct udevice *dev; struct uclass *uc; @@ -181,7 +182,7 @@ static void *alloc_priv(int size, uint flags)
int device_probe_child(struct udevice *dev, void *parent_priv) { - struct driver *drv; + const struct driver *drv; int size = 0; int ret; int seq; diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index e2418fe..687462b 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -34,7 +34,7 @@ struct udevice; * @devp: Returns a pointer to the bound device * @return 0 if OK, -ve on error */ -int device_bind(struct udevice *parent, struct driver *drv, +int device_bind(struct udevice *parent, const struct driver *drv, const char *name, void *platdata, int of_offset, struct udevice **devp);
diff --git a/include/dm/device.h b/include/dm/device.h index f27b34b..fafecce 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -70,7 +70,7 @@ struct driver_info; * when the device is probed and will be unique within the device's uclass. */ struct udevice { - struct driver *driver; + const struct driver *driver; const char *name; void *platdata; void *parent_platdata;

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
The driver is not modified by driver model, so update driver_bind() to recognise that.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/core/device-remove.c | 4 ++-- drivers/core/device.c | 7 ++++--- include/dm/device-internal.h | 2 +- include/dm/device.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-)
Applied to u-boot-dm/next.

The existing get_get_of_data() function provides access to both the driver's compatible string and its driver data. However only the latter is actually useful. Update the interface to reflect this and fix up existing users.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/core/device.c | 4 ++-- drivers/core/lists.c | 2 +- drivers/i2c/s3c24x0_i2c.c | 2 +- drivers/i2c/tegra_i2c.c | 6 +++--- include/dm/device.h | 16 +++++++++++----- 5 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/drivers/core/device.c b/drivers/core/device.c index f1a03d9..4fba118 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -467,9 +467,9 @@ struct udevice *dev_get_parent(struct udevice *child) return child->parent; }
-ulong dev_get_of_data(struct udevice *dev) +ulong dev_get_driver_data(struct udevice *dev) { - return dev->of_id->data; + return dev->driver_data; }
enum uclass_id device_get_uclass_id(struct udevice *dev) diff --git a/drivers/core/lists.c b/drivers/core/lists.c index ff115c4..647e390 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -168,7 +168,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, dm_warn("Error binding driver '%s'\n", entry->name); return ret; } else { - dev->of_id = id; + dev->driver_data = id->data; found = true; if (devp) *devp = dev; diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index b4ee33f..27ff587 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -1348,7 +1348,7 @@ static int s3c_i2c_ofdata_to_platdata(struct udevice *dev) struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); int node, flags;
- i2c_bus->is_highspeed = dev->of_id->data; + i2c_bus->is_highspeed = dev_get_driver_data(dev); node = dev->of_offset;
if (i2c_bus->is_highspeed) { diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index f414287..fc95646 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -338,7 +338,7 @@ static int tegra_i2c_probe(struct udevice *dev) bool is_dvc;
i2c_bus->id = dev->seq; - i2c_bus->type = dev_get_of_data(dev); + i2c_bus->type = dev_get_driver_data(dev); i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg");
/* @@ -360,7 +360,7 @@ static int tegra_i2c_probe(struct udevice *dev) if (i2c_bus->periph_id == -1) return -EINVAL;
- is_dvc = dev_get_of_data(dev) == TYPE_DVC; + is_dvc = dev_get_driver_data(dev) == TYPE_DVC; if (is_dvc) { i2c_bus->control = &((struct dvc_ctlr *)i2c_bus->regs)->control; @@ -469,7 +469,7 @@ int tegra_i2c_get_dvc_bus(struct udevice **busp) for (uclass_first_device(UCLASS_I2C, &bus); bus; uclass_next_device(&bus)) { - if (dev_get_of_data(bus) == TYPE_DVC) { + if (dev_get_driver_data(bus) == TYPE_DVC) { *busp = bus; return 0; } diff --git a/include/dm/device.h b/include/dm/device.h index fafecce..ec22885 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -55,7 +55,8 @@ struct driver_info; * @platdata: Configuration data for this device * @parent_platdata: The parent bus's configuration data for this device * @of_offset: Device tree node offset for this device (- for none) - * @of_id: Pointer to the udevice_id structure which created the device + * @driver_data: Driver data word for the entry that matched this device with + * its driver * @parent: Parent of this device, or NULL for the top level device * @priv: Private data for this device * @uclass: Pointer to uclass for this device @@ -75,7 +76,7 @@ struct udevice { void *platdata; void *parent_platdata; int of_offset; - const struct udevice_id *of_id; + ulong driver_data; struct udevice *parent; void *priv; struct uclass *uclass; @@ -251,13 +252,18 @@ struct udevice *dev_get_parent(struct udevice *child); void *dev_get_uclass_priv(struct udevice *dev);
/** - * dev_get_of_data() - get the device tree data used to bind a device + * dev_get_driver_data() - get the driver data used to bind a device * * When a device is bound using a device tree node, it matches a * particular compatible string as in struct udevice_id. This function - * returns the associated data value for that compatible string + * returns the associated data value for that compatible string. This is + * the 'data' field in struct udevice_id. + * + * For USB devices, this is the driver_info field in struct usb_device_id. + * + * @dev: Device to check */ -ulong dev_get_of_data(struct udevice *dev); +ulong dev_get_driver_data(struct udevice *dev);
/* * device_get_uclass_id() - return the uclass ID of a device

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
The existing get_get_of_data() function provides access to both the driver's compatible string and its driver data. However only the latter is actually useful. Update the interface to reflect this and fix up existing users.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/core/device.c | 4 ++-- drivers/core/lists.c | 2 +- drivers/i2c/s3c24x0_i2c.c | 2 +- drivers/i2c/tegra_i2c.c | 6 +++--- include/dm/device.h | 16 +++++++++++----- 5 files changed, 18 insertions(+), 12 deletions(-)
Applied to u-boot-dm/next.

The uclass pre-probe functions may end up calling back into the device in some circumstances. This can fail if recursion takes place. Adjust the ordering so that we mark the device as active early, then retract this later if needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/core/device.c | 8 +++++--- test/dm/test-uclass.c | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/core/device.c b/drivers/core/device.c index 4fba118..b7ed21c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -243,6 +243,8 @@ int device_probe_child(struct udevice *dev, void *parent_priv) } dev->seq = seq;
+ dev->flags |= DM_FLAG_ACTIVATED; + ret = uclass_pre_probe_device(dev); if (ret) goto fail; @@ -269,10 +271,8 @@ int device_probe_child(struct udevice *dev, void *parent_priv) }
ret = uclass_post_probe_device(dev); - if (ret) { - dev->flags &= ~DM_FLAG_ACTIVATED; + if (ret) goto fail_uclass; - }
return 0; fail_uclass: @@ -281,6 +281,8 @@ fail_uclass: __func__, dev->name); } fail: + dev->flags &= ~DM_FLAG_ACTIVATED; + dev->seq = -1; device_free(dev);
diff --git a/test/dm/test-uclass.c b/test/dm/test-uclass.c index be91657..7cb37f7 100644 --- a/test/dm/test-uclass.c +++ b/test/dm/test-uclass.c @@ -31,6 +31,7 @@ int test_ping(struct udevice *dev, int pingval, int *pingret) static int test_post_bind(struct udevice *dev) { dm_testdrv_op_count[DM_TEST_OP_POST_BIND]++; + ut_assert(!device_active(dev));
return 0; } @@ -48,7 +49,7 @@ static int test_pre_probe(struct udevice *dev)
dm_testdrv_op_count[DM_TEST_OP_PRE_PROBE]++; ut_assert(priv); - ut_assert(!device_active(dev)); + ut_assert(device_active(dev));
return 0; }

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
The uclass pre-probe functions may end up calling back into the device in some circumstances. This can fail if recursion takes place. Adjust the ordering so that we mark the device as active early, then retract this later if needed.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/core/device.c | 8 +++++--- test/dm/test-uclass.c | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-)
Applied to u-boot-dm/next.

Add some utility functions to check for children and for the last sibling in a device's parent.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/core/device.c | 28 ++++++++++++++++++++++++++++ include/dm/device.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+)
diff --git a/drivers/core/device.c b/drivers/core/device.c index b7ed21c..ccaa99c 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -490,3 +490,31 @@ fdt_addr_t dev_get_addr(struct udevice *dev) return FDT_ADDR_T_NONE; } #endif + +bool device_has_children(struct udevice *dev) +{ + return !list_empty(&dev->child_head); +} + +bool device_has_active_children(struct udevice *dev) +{ + struct udevice *child; + + for (device_find_first_child(dev, &child); + child; + device_find_next_child(&child)) { + if (device_active(child)) + return true; + } + + return false; +} + +bool device_is_last_sibling(struct udevice *dev) +{ + struct udevice *parent = dev->parent; + + if (!parent) + return false; + return list_is_last(&dev->sibling_node, &parent->child_head); +} diff --git a/include/dm/device.h b/include/dm/device.h index ec22885..c11342c 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -380,4 +380,34 @@ int device_find_next_child(struct udevice **devp); */ fdt_addr_t dev_get_addr(struct udevice *dev);
+/** + * device_has_children() - check if a device has any children + * + * @dev: Device to check + * @return true if the device has one or more children + */ +bool device_has_children(struct udevice *dev); + +/** + * device_has_active_children() - check if a device has any active children + * + * @dev: Device to check + * @return true if the device has one or more children and at least one of + * them is active (probed). + */ +bool device_has_active_children(struct udevice *dev); + +/** + * device_is_last_sibling() - check if a device is the last sibling + * + * This function can be useful for display purposes, when special action needs + * to be taken when displaying the last sibling. This can happen when a tree + * view of devices is being displayed. + * + * @dev: Device to check + * @return true if there are no more siblings after this one - i.e. is it + * last in the list. + */ +bool device_is_last_sibling(struct udevice *dev); + #endif

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
Add some utility functions to check for children and for the last sibling in a device's parent.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/core/device.c | 28 ++++++++++++++++++++++++++++ include/dm/device.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+)
Applied to u-boot-dm/next.

This has a prototype but no implementation. It returns the global GPIO number given a gpio_desc. It is useful for debugging in some cases.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/gpio/gpio-uclass.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index b6e1058..380366f 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -689,6 +689,18 @@ static int gpio_renumber(struct udevice *removed_dev) return 0; }
+int gpio_get_number(struct gpio_desc *desc) +{ + struct udevice *dev = desc->dev; + struct gpio_dev_priv *uc_priv; + + if (!dev) + return -1; + uc_priv = dev->uclass_priv; + + return uc_priv->gpio_base + desc->offset; +} + static int gpio_post_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
This has a prototype but no implementation. It returns the global GPIO number given a gpio_desc. It is useful for debugging in some cases.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/gpio/gpio-uclass.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
Applied to u-boot-dm/next.

Add a uclass that can represent a USB controller. For now we do not create devices for things attached to the controller. This will be added later.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/Kconfig | 14 ++ drivers/usb/host/Makefile | 4 + drivers/usb/host/usb-uclass.c | 392 ++++++++++++++++++++++++++++++++++++++ drivers/usb/musb-new/musb_uboot.c | 4 +- include/dm/uclass-id.h | 1 + include/usb.h | 285 ++++++++++++++++++++++++++- 6 files changed, 694 insertions(+), 6 deletions(-) create mode 100644 drivers/usb/host/usb-uclass.c
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index b4a9442..a4414ef 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -35,6 +35,20 @@ config USB
if USB
+config DM_USB + bool "Enable driver model for USB" + depends on USB && DM + help + Enable driver model for USB. The USB interface is then implemented + by the USB uclass. Multiple USB controllers of different types + (XHCI, EHCI) can be attached and used. The 'usb' command works as + normal. OCHI is not supported at present. + + Much of the code is shared but with this option enabled the USB + uclass takes care of device enumeration. USB devices can be + declared with the USB_DEVICE() macro and will be automatically + probed when found on the bus. + source "drivers/usb/host/Kconfig"
config USB_STORAGE diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index eb6f34b..9419295 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -5,6 +5,10 @@ # SPDX-License-Identifier: GPL-2.0+ #
+ifdef CONFIG_DM_USB +obj-$(CONFIG_CMD_USB) += usb-uclass.o +endif + # ohci obj-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o obj-$(CONFIG_USB_ATMEL) += ohci-at91.o diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c new file mode 100644 index 0000000..22dcd14 --- /dev/null +++ b/drivers/usb/host/usb-uclass.c @@ -0,0 +1,392 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <usb.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +extern bool usb_started; /* flag for the started/stopped USB status */ +static bool asynch_allowed; + +int usb_disable_asynch(int disable) +{ + int old_value = asynch_allowed; + + asynch_allowed = !disable; + return old_value; +} + +int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length, int interval) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->interrupt) + return -ENOSYS; + + return ops->interrupt(bus, udev, pipe, buffer, length, interval); +} + +int submit_control_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, struct devrequest *setup) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->control) + return -ENOSYS; + + return ops->control(bus, udev, pipe, buffer, length, setup); +} + +int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->bulk) + return -ENOSYS; + + return ops->bulk(bus, udev, pipe, buffer, length); +} + +int usb_alloc_device(struct usb_device *udev) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + /* This is only requird by some controllers - current XHCI */ + if (!ops->alloc_device) + return 0; + + return ops->alloc_device(bus, udev); +} + +int usb_stop(void) +{ + struct udevice *bus; + struct uclass *uc; + int err = 0, ret; + + /* De-activate any devices that have been activated */ + ret = uclass_get(UCLASS_USB, &uc); + if (ret) + return ret; + uclass_foreach_dev(bus, uc) { + ret = device_remove(bus); + if (ret && !err) + err = ret; + } + + usb_stor_reset(); + usb_hub_reset(); + usb_started = 0; + + return err; +} + +static int usb_scan_bus(struct udevice *bus, bool recurse) +{ + struct usb_bus_priv *priv; + struct udevice *dev; + int ret; + + priv = dev_get_uclass_priv(bus); + + assert(recurse); /* TODO: Support non-recusive */ + + ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev); + if (ret) + return ret; + + return priv->next_addr; +} + +int usb_init(void) +{ + int controllers_initialized = 0; + struct udevice *bus; + struct uclass *uc; + int count = 0; + int ret; + + asynch_allowed = 1; + usb_hub_reset(); + + ret = uclass_get(UCLASS_USB, &uc); + if (ret) + return ret; + + uclass_foreach_dev(bus, uc) { + /* init low_level USB */ + count++; + printf("USB"); + printf("%d: ", bus->seq); + ret = device_probe(bus); + if (ret == -ENODEV) { /* No such device. */ + puts("Port not available.\n"); + controllers_initialized++; + continue; + } + + if (ret) { /* Other error. */ + printf("probe failed, error %d\n", ret); + continue; + } + /* + * lowlevel init is OK, now scan the bus for devices + * i.e. search HUBs and configure them + */ + controllers_initialized++; + printf("scanning bus %d for devices... ", bus->seq); + debug("\n"); + ret = usb_scan_bus(bus, true); + if (ret < 0) + printf("failed, error %d\n", ret); + else if (!ret) + printf("No USB Device found\n"); + else + printf("%d USB Device(s) found\n", ret); + usb_started = true; + } + + debug("scan end\n"); + /* if we were not able to find at least one working bus, bail out */ + if (!count) + printf("No controllers found\n"); + else if (controllers_initialized == 0) + printf("USB error: all controllers failed lowlevel init\n"); + + return usb_started ? 0 : -1; +} + +int usb_reset_root_port(void) +{ + return -ENOSYS; +} + +static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) +{ + struct usb_device *udev; + struct udevice *dev; + + if (!device_active(parent)) + return NULL; + udev = dev_get_parentdata(parent); + if (udev->devnum == devnum) + return udev; + + for (device_find_first_child(parent, &dev); + dev; + device_find_next_child(&dev)) { + udev = find_child_devnum(dev, devnum); + if (udev) + return udev; + } + + return NULL; +} + +struct usb_device *usb_get_dev_index(struct udevice *bus, int index) +{ + struct udevice *hub; + int devnum = index + 1; /* Addresses are allocated from 1 on USB */ + + device_find_first_child(bus, &hub); + if (device_get_uclass_id(hub) == UCLASS_USB_HUB) + return find_child_devnum(hub, devnum); + + return NULL; +} + +int usb_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +int usb_port_reset(struct usb_device *parent, int portnr) +{ + unsigned short portstatus; + int ret; + + debug("%s: start\n", __func__); + + if (parent) { + /* reset the port for the second time */ + assert(portnr > 0); + debug("%s: reset %d\n", __func__, portnr - 1); + ret = legacy_hub_port_reset(parent, portnr - 1, &portstatus); + if (ret < 0) { + printf("\n Couldn't reset port %i\n", portnr); + return ret; + } + } else { + debug("%s: reset root\n", __func__); + usb_reset_root_port(); + } + + return 0; +} + +int usb_legacy_port_reset(struct usb_device *parent, int portnr) +{ + return usb_port_reset(parent, portnr); +} + +int usb_scan_device(struct udevice *parent, int port, + enum usb_device_speed speed, struct udevice **devp) +{ + struct udevice *dev; + bool created = false; + struct usb_dev_platdata *plat; + struct usb_bus_priv *priv; + struct usb_device *parent_udev; + int ret; + ALLOC_CACHE_ALIGN_BUFFER(struct usb_device, udev, 1); + struct usb_interface_descriptor *iface = &udev->config.if_desc[0].desc; + + *devp = NULL; + memset(udev, '\0', sizeof(*udev)); + ret = usb_get_bus(parent, &udev->controller_dev); + if (ret) + return ret; + priv = dev_get_uclass_priv(udev->controller_dev); + + /* + * Somewhat nasty, this. We create a local device and use the normal + * USB stack to read its descriptor. Then we know what type of device + * to create for real. + * + * udev->dev is set to the parent, since we don't have a real device + * yet. The USB stack should not access udev.dev anyway, except perhaps + * to find the controller, and the controller will either be @parent, + * or some parent of @parent. + * + * Another option might be to create the device as a generic USB + * device, then morph it into the correct one when we know what it + * should be. This means that a generic USB device would morph into + * a network controller, or a USB flash stick, for example. However, + * we don't support such morphing and it isn't clear that it would + * be easy to do. + * + * Yet another option is to split out the USB stack parts of udev + * into something like a 'struct urb' (as Linux does) which can exist + * independently of any device. This feels cleaner, but calls for quite + * a big change to the USB stack. + * + * For now, the approach is to set up an empty udev, read its + * descriptor and assign it an address, then bind a real device and + * stash the resulting information into the device's parent + * platform data. Then when we probe it, usb_child_pre_probe() is called + * and it will pull the information out of the stash. + */ + udev->dev = parent; + udev->speed = speed; + udev->devnum = priv->next_addr + 1; + udev->portnr = port; + debug("Calling usb_setup_device(), portnr=%d\n", udev->portnr); + parent_udev = device_get_uclass_id(parent) == UCLASS_USB_HUB ? + dev_get_parentdata(parent) : NULL; + ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev, port); + debug("read_descriptor for '%s': ret=%d\n", parent->name, ret); + if (ret) + return ret; + ret = usb_find_child(parent, &udev->descriptor, iface, &dev); + debug("** usb_find_child returns %d\n", ret); + + /* TODO: Find a suitable driver and create the device */ + return -ENOENT; +} + +int usb_child_post_bind(struct udevice *dev) +{ + struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); + const void *blob = gd->fdt_blob; + int val; + + if (dev->of_offset == -1) + return 0; + + /* We only support matching a few things */ + val = fdtdec_get_int(blob, dev->of_offset, "usb,device-class", -1); + if (val != -1) { + plat->id.match_flags |= USB_DEVICE_ID_MATCH_DEV_CLASS; + plat->id.bDeviceClass = val; + } + val = fdtdec_get_int(blob, dev->of_offset, "usb,interface-class", -1); + if (val != -1) { + plat->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS; + plat->id.bInterfaceClass = val; + } + + return 0; +} + +int usb_get_bus(struct udevice *dev, struct udevice **busp) +{ + struct udevice *bus; + + *busp = NULL; + for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; ) + bus = bus->parent; + if (!bus) { + /* By design this cannot happen */ + assert(bus); + debug("USB HUB '%s' does not have a controller\n", dev->name); + return -EXDEV; + } + *busp = bus; + + return 0; +} + +int usb_child_pre_probe(struct udevice *dev) +{ + struct udevice *bus; + struct usb_device *udev = dev_get_parentdata(dev); + struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); + int ret; + + ret = usb_get_bus(dev, &bus); + if (ret) + return ret; + udev->controller_dev = bus; + udev->dev = dev; + udev->devnum = plat->devnum; + udev->slot_id = plat->slot_id; + udev->portnr = plat->portnr; + udev->speed = plat->speed; + debug("** device '%s': getting slot_id=%d\n", dev->name, plat->slot_id); + + ret = usb_select_config(udev); + if (ret) + return ret; + + return 0; +} + +UCLASS_DRIVER(usb) = { + .id = UCLASS_USB, + .name = "usb", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_bind = usb_post_bind, + .per_child_auto_alloc_size = sizeof(struct usb_device), + .per_device_auto_alloc_size = sizeof(struct usb_bus_priv), + .child_post_bind = usb_child_post_bind, + .child_pre_probe = usb_child_pre_probe, + .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), +}; diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 6e58ddf..f42db59 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -177,7 +177,7 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) return NULL; /* URB still pending */ }
-void usb_reset_root_port(void) +int usb_reset_root_port(void) { void *mbase = host->mregs; u8 power; @@ -194,6 +194,8 @@ void usb_reset_root_port(void) (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? USB_SPEED_FULL : USB_SPEED_LOW; mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50); + + return 0; }
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 84a6955..cb6d2e5 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -39,6 +39,7 @@ enum uclass_id { UCLASS_PCI_GENERIC, /* Generic PCI bus device */ UCLASS_PCH, /* x86 platform controller hub */ UCLASS_ETH, /* Ethernet device */ + UCLASS_USB, /* USB bus */
UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/usb.h b/include/usb.h index a8fee0b..0e1c16a 100644 --- a/include/usb.h +++ b/include/usb.h @@ -2,6 +2,9 @@ * (C) Copyright 2001 * Denis Peter, MPL AG Switzerland * + * Adapted for U-Boot driver model + * (C) Copyright 2015 Google, Inc + * * SPDX-License-Identifier: GPL-2.0+ * Note: Part of this code has been derived from linux * @@ -9,6 +12,7 @@ #ifndef _USB_H_ #define _USB_H_
+#include <fdtdec.h> #include <usb_defs.h> #include <linux/usb/ch9.h> #include <asm/cache.h> @@ -85,6 +89,19 @@ enum { PACKET_SIZE_64 = 3, };
+/** + * struct usb_device - information about a USB device + * + * With driver model both UCLASS_USB (the USB controllers) and UCLASS_USB_HUB + * (the hubs) have this as parent data. Hubs are children of controllers or + * other hubs and there is always a single root hub for each controller. + * Therefore struct usb_device can always be accessed with + * dev_get_parentdata(dev), where dev is a USB device. + * + * Pointers exist for obtaining both the device (could be any uclass) and + * controller (UCLASS_USB) from this structure. The controller does not have + * a struct usb_device since it is not a device. + */ struct usb_device { int devnum; /* Device number on USB bus */ int speed; /* full/low/high */ @@ -123,13 +140,19 @@ struct usb_device { unsigned long int_pending; /* 1 bit per ep, used by int_queue */ int act_len; /* transfered bytes */ int maxchild; /* Number of ports if hub */ - int portnr; + int portnr; /* Port number, 1=first */ +#ifndef CONFIG_DM_USB + /* parent hub, or NULL if this is the root hub */ struct usb_device *parent; struct usb_device *children[USB_MAXCHILDREN]; - void *controller; /* hardware controller private data */ +#endif /* slot_id - for xHCI enabled devices */ unsigned int slot_id; +#ifdef CONFIG_DM_USB + struct udevice *dev; /* Pointer to associated device */ + struct udevice *controller_dev; /* Pointer to associated controller */ +#endif };
struct int_queue; @@ -160,8 +183,9 @@ enum usb_init_type {
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller); int usb_lowlevel_stop(int index); -#ifdef CONFIG_MUSB_HOST -void usb_reset_root_port(void); + +#if defined(CONFIG_MUSB_HOST) || defined(CONFIG_DM_USB) +int usb_reset_root_port(void); #else #define usb_reset_root_port() #endif @@ -245,7 +269,6 @@ int usb_stop(void); /* stop the USB Controller */ int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol); int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id); -struct usb_device *usb_get_dev_index(int index); int usb_control_msg(struct usb_device *dev, unsigned int pipe, unsigned char request, unsigned char requesttype, unsigned short value, unsigned short index, @@ -423,6 +446,258 @@ struct usb_hub_device { struct usb_hub_descriptor desc; };
+#ifdef CONFIG_DM_USB +/** + * struct usb_platdata - Platform data about a USB controller + * + * Given a USB controller (UCLASS_USB) dev this is dev_get_platdata(dev) + */ +struct usb_platdata { + enum usb_init_type init_type; +}; + +/** + * struct usb_dev_platdata - Platform data about a USB device + * + * Given a USB device dev this structure is dev_get_parent_platdata(dev). + * This is used by sandbox to provide emulation data also. + * + * @id: ID used to match this device + * @speed: Stores the speed associated with a USB device + * @devnum: Device address on the USB bus + * @slot_id: USB3 slot ID, which is separate from the device address + * @portnr: Port number of this device on its parent hub, numbered from 1 + * (0 mean this device is the root hub) + * @strings: List of descriptor strings (for sandbox emulation purposes) + * @desc_list: List of descriptors (for sandbox emulation purposes) + */ +struct usb_dev_platdata { + struct usb_device_id id; + enum usb_device_speed speed; + int devnum; + int slot_id; + int portnr; /* Hub port number, 1..n */ +#ifdef CONFIG_SANDBOX + struct usb_string *strings; + /* NULL-terminated list of descriptor pointers */ + struct usb_generic_descriptor **desc_list; +#endif + int configno; +}; + +/** + * struct usb_bus_priv - information about the USB controller + * + * Given a USB controller (UCLASS_USB) 'dev', this is + * dev_get_uclass_priv(dev). + * + * @next_addr: Next device address to allocate minus 1. Incremented by 1 + * each time a new device address is set, so this holds the + * number of devices on the bus + * @desc_before_addr: true if we can read a device descriptor before it + * has been assigned an address. For XHCI this is not possible + * so this will be false. + */ +struct usb_bus_priv { + int next_addr; + bool desc_before_addr; +}; + +/** + * struct dm_usb_ops - USB controller operations + * + * This defines the operations supoorted on a USB controller. Common + * arguments are: + * + * @bus: USB bus (i.e. controller), which is in UCLASS_USB. + * @udev: USB device parent data. Controllers are not expected to need + * this, since the device address on the bus is encoded in @pipe. + * It is used for sandbox, and can be handy for debugging and + * logging. + * @pipe: An assortment of bitfields which provide address and packet + * type information. See create_pipe() above for encoding + * details + * @buffer: A buffer to use for sending/receiving. This should be + * DMA-aligned. + * @length: Buffer length in bytes + */ +struct dm_usb_ops { + /** + * control() - Send a control message + * + * Most parameters are as above. + * + * @setup: Additional setup information required by the message + */ + int (*control)(struct udevice *bus, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup); + /** + * bulk() - Send a bulk message + * + * Parameters are as above. + */ + int (*bulk)(struct udevice *bus, struct usb_device *udev, + unsigned long pipe, void *buffer, int length); + /** + * interrupt() - Send an interrupt message + * + * Most parameters are as above. + * + * @interval: Interrupt interval + */ + int (*interrupt)(struct udevice *bus, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval); + /** + * alloc_device() - Allocate a new device context (XHCI) + * + * Before sending packets to a new device on an XHCI bus, a device + * context must be created. If this method is not NULL it will be + * called before the device is enumerated (even before its descriptor + * is read). This should be NULL for EHCI, which does not need this. + */ + int (*alloc_device)(struct udevice *bus, struct usb_device *udev); +}; + +#define usb_get_ops(dev) ((struct dm_usb_ops *)(dev)->driver->ops) +#define usb_get_emul_ops(dev) ((struct dm_usb_ops *)(dev)->driver->ops) + +#ifdef CONFIG_MUSB_HOST +int usb_reset_root_port(void); +#endif + +/** + * usb_get_dev_index() - look up a device index number + * + * Look up devices using their index number (starting at 0). This works since + * in U-Boot device addresses are allocated starting at 1 with no gaps. + * + * TODO(sjg@chromium.org): Remove this function when usb_ether.c is modified + * to work better with driver model. + * + * @bus: USB bus to check + * @index: Index number of device to find (0=first). This is just the + * device address less 1. + */ +struct usb_device *usb_get_dev_index(struct udevice *bus, int index); + +/** + * usb_legacy_port_reset() - Legacy function to reset a hub port + * + * @hub: Hub device + * @portnr: Port number (1=first) + */ +int usb_legacy_port_reset(struct usb_device *hub, int portnr); + +/** + * usb_setup_device() - set up a device ready for use + * + * @dev: USB device pointer. This need not be a real device - it is + * common for it to just be a local variable with its ->dev + * member (i.e. @dev->dev) set to the parent device + * @do_read: true to read the device descriptor before an address is set + * (should be false for XHCI buses, true otherwise) + * @parent: Parent device (either UCLASS_USB or UCLASS_USB_HUB) + * @portnr: Port number on hub (1=first) or 0 for none + * @return 0 if OK, -ve on error */ +int usb_setup_device(struct usb_device *dev, bool do_read, + struct usb_device *parent, int portnr); + +/** + * usb_hub_scan() - Scan a hub and find its devices + * + * @hub: Hub device to scan + */ +int usb_hub_scan(struct udevice *hub); + +/** + * usb_scan_device() - Scan a device on a bus + * + * Scan a device on a bus. It has already been detected and is ready to + * be enumerated. This may be either the root hub (@parent is a bus) or a + * normal device (@parent is a hub) + * + * @parent: Parent device + * @port: Hub port number (numbered from 1) + * @speed: USB speed to use for this device + * @devp: Returns pointer to device if all is well + * @return 0 if OK, -ve on error + */ +int usb_scan_device(struct udevice *parent, int port, + enum usb_device_speed speed, struct udevice **devp); + +/** + * usb_get_bus() - Find the bus for a device + * + * Search up through parents to find the bus this device is connected to. This + * will be a device with uclass UCLASS_USB. + * + * @dev: Device to check + * @busp: Returns bus, or NULL if not found + * @return 0 if OK, -EXDEV is somehow this bus does not have a controller (this + * indicates a critical error in the USB stack + */ +int usb_get_bus(struct udevice *dev, struct udevice **busp); + +/** + * usb_select_config() - Set up a device ready for use + * + * This function assumes that the device already has an address and a driver + * bound, and is ready to be set up. + * + * This re-reads the device and configuration descriptors and sets the + * configuration + * + * @dev: Device to set up + */ +int usb_select_config(struct usb_device *dev); + +/** + * usb_child_pre_probe() - Pre-probe function for USB devices + * + * This is called on all children of hubs and USB controllers (i.e. UCLASS_USB + * and UCLASS_USB_HUB) when a new device is about to be probed. It sets up the + * device from the saved platform data and calls usb_select_config() to + * finish set up. + * + * Once this is done, the device's normal driver can take over, knowing the + * device is accessible on the USB bus. + * + * This function is for use only by the internal USB stack. + * + * @dev: Device to set up + */ +int usb_child_pre_probe(struct udevice *dev); + +struct ehci_ctrl; + +/** + * usb_setup_ehci_gadget() - Set up a USB device as a gadget + * + * TODO(sjg@chromium.org): Tidy this up when USB gadgets can use driver model + * + * This provides a way to tell a controller to start up as a USB device + * instead of as a host. It is untested. + */ +int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp); + +/** + * usb_stor_reset() - Prepare to scan USB storage devices + * + * Empty the list of USB storage devices in preparation for scanning them. + * This must be called before a USB scan. + */ +void usb_stor_reset(void); + +#else /* !CONFIG_DM_USB */ + +struct usb_device *usb_get_dev_index(int index); + +#endif + +bool usb_device_has_child_on_port(struct usb_device *parent, int port); + int usb_hub_probe(struct usb_device *dev, int ifnum); void usb_hub_reset(void); int hub_port_reset(struct usb_device *dev, int port,

On 25 March 2015 at 12:21, Simon Glass sjg@chromium.org wrote:
Add a uclass that can represent a USB controller. For now we do not create devices for things attached to the controller. This will be added later.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/Kconfig | 14 ++ drivers/usb/host/Makefile | 4 + drivers/usb/host/usb-uclass.c | 392 ++++++++++++++++++++++++++++++++++++++ drivers/usb/musb-new/musb_uboot.c | 4 +- include/dm/uclass-id.h | 1 + include/usb.h | 285 ++++++++++++++++++++++++++- 6 files changed, 694 insertions(+), 6 deletions(-) create mode 100644 drivers/usb/host/usb-uclass.c
Applied to u-boot-dm/next.

Use 'udev' instead of 'dev' in a few places, reserving 'dev' for driver model's struct udevice. Also adjust the code in a few minor ways to make it easier to plumb in driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/cmd_usb.c | 51 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 26 deletions(-)
diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 27813f0..085802b 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -254,15 +254,15 @@ static void usb_display_config(struct usb_device *dev)
static struct usb_device *usb_find_device(int devnum) { - struct usb_device *dev; + struct usb_device *udev; int d;
for (d = 0; d < USB_MAX_DEVICE; d++) { - dev = usb_get_dev_index(d); - if (dev == NULL) + udev = usb_get_dev_index(d); + if (udev == NULL) return NULL; - if (dev->devnum == devnum) - return dev; + if (udev->devnum == devnum) + return udev; }
return NULL; @@ -305,8 +305,8 @@ static void usb_show_tree_graph(struct usb_device *dev, char *pre) has_child = 1; } /* check if we are the last one */ - last_child = 1; - if (dev->parent != NULL) { + last_child = (dev->parent != NULL); + if (last_child) { for (i = 0; i < dev->parent->maxchild; i++) { /* search for children */ if (dev->parent->children[i] == dev) { @@ -324,7 +324,7 @@ static void usb_show_tree_graph(struct usb_device *dev, char *pre) } /* for all children of the parent */ printf("\b+-"); /* correct last child */ - if (last_child) + if (last_child && index) pre[index-1] = ' '; } /* if not root hub */ else @@ -355,7 +355,7 @@ static void usb_show_tree(struct usb_device *dev) { char preamble[32];
- memset(preamble, 0, 32); + memset(preamble, '\0', sizeof(preamble)); usb_show_tree_graph(dev, &preamble[0]); }
@@ -466,9 +466,8 @@ static void do_usb_start(void) */ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - + struct usb_device *udev = NULL; int i; - struct usb_device *dev = NULL; extern char usb_started; #ifdef CONFIG_USB_STORAGE block_dev_desc_t *stor_dev; @@ -509,11 +508,11 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (strncmp(argv[1], "tree", 4) == 0) { puts("USB device tree:\n"); for (i = 0; i < USB_MAX_DEVICE; i++) { - dev = usb_get_dev_index(i); - if (dev == NULL) + udev = usb_get_dev_index(i); + if (udev == NULL) break; - if (dev->parent == NULL) - usb_show_tree(dev); + if (udev->parent == NULL) + usb_show_tree(udev); } return 0; } @@ -521,23 +520,23 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int d; if (argc == 2) { for (d = 0; d < USB_MAX_DEVICE; d++) { - dev = usb_get_dev_index(d); - if (dev == NULL) + udev = usb_get_dev_index(d); + if (udev == NULL) break; - usb_display_desc(dev); - usb_display_config(dev); + usb_display_desc(udev); + usb_display_config(udev); } return 0; } else { i = simple_strtoul(argv[2], NULL, 10); printf("config for device %d\n", i); - dev = usb_find_device(i); - if (dev == NULL) { + udev = usb_find_device(i); + if (udev == NULL) { printf("*** No device available ***\n"); return 0; } else { - usb_display_desc(dev); - usb_display_config(dev); + usb_display_desc(udev); + usb_display_config(udev); } } return 0; @@ -546,13 +545,13 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc < 5) return CMD_RET_USAGE; i = simple_strtoul(argv[2], NULL, 10); - dev = usb_find_device(i); - if (dev == NULL) { + udev = usb_find_device(i); + if (udev == NULL) { printf("Device %d does not exist.\n", i); return 1; } i = simple_strtoul(argv[3], NULL, 10); - return usb_test(dev, i, argv[4]); + return usb_test(udev, i, argv[4]); } #ifdef CONFIG_USB_STORAGE if (strncmp(argv[1], "stor", 4) == 0)

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Use 'udev' instead of 'dev' in a few places, reserving 'dev' for driver model's struct udevice. Also adjust the code in a few minor ways to make it easier to plumb in driver model.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/cmd_usb.c | 51 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 26 deletions(-)
Applied to u-boot-dm/next.

This function returns NULL on error at present. Adjust it so that we can return a real error, as is needed with driver model. Also improve the error handling in its caller, usb_hub_port_connect_change(), and adjust the code order to prepare for driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb.c | 17 ++++++++--------- common/usb_hub.c | 41 ++++++++++++++++++++++++++--------------- include/usb.h | 20 ++++++++++++++++++-- 3 files changed, 52 insertions(+), 26 deletions(-)
diff --git a/common/usb.c b/common/usb.c index 32e15cd..f87c6a5 100644 --- a/common/usb.c +++ b/common/usb.c @@ -94,12 +94,12 @@ int usb_init(void) controllers_initialized++; start_index = dev_index; printf("scanning bus %d for devices... ", i); - dev = usb_alloc_new_device(ctrl); + ret = usb_alloc_new_device(ctrl, &dev); /* * device 0 is always present * (root hub, so let it analyze) */ - if (dev) + if (!ret) usb_new_device(dev);
if (start_index == dev_index) @@ -827,16 +827,13 @@ struct usb_device *usb_get_dev_index(int index) return &usb_dev[index]; }
-/* returns a pointer of a new device structure or NULL, if - * no device struct is available - */ -struct usb_device *usb_alloc_new_device(void *controller) +int usb_alloc_new_device(struct udevice *controller, struct usb_device **devp) { int i; debug("New Device %d\n", dev_index); if (dev_index == USB_MAX_DEVICE) { printf("ERROR, too many USB Devices, max=%d\n", USB_MAX_DEVICE); - return NULL; + return -ENOSPC; } /* default Address is 0, real addresses start with 1 */ usb_dev[dev_index].devnum = dev_index + 1; @@ -846,7 +843,9 @@ struct usb_device *usb_alloc_new_device(void *controller) usb_dev[dev_index].parent = NULL; usb_dev[dev_index].controller = controller; dev_index++; - return &usb_dev[dev_index - 1]; + *devp = &usb_dev[dev_index - 1]; + + return 0; }
/* @@ -854,7 +853,7 @@ struct usb_device *usb_alloc_new_device(void *controller) * Called in error cases where configuring a newly attached * device fails for some reason. */ -void usb_free_device(void) +void usb_free_device(struct udevice *controller) { dev_index--; debug("Freeing device node: %d\n", dev_index); diff --git a/common/usb_hub.c b/common/usb_hub.c index 66b4a72..7199e25 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -24,6 +24,7 @@
#include <common.h> #include <command.h> +#include <errno.h> #include <asm/processor.h> #include <asm/unaligned.h> #include <linux/ctype.h> @@ -206,16 +207,18 @@ int hub_port_reset(struct usb_device *dev, int port, }
-void usb_hub_port_connect_change(struct usb_device *dev, int port) +int usb_hub_port_connect_change(struct usb_device *dev, int port) { struct usb_device *usb; ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus; + int ret, speed;
/* Check status */ - if (usb_get_port_status(dev, port + 1, portsts) < 0) { + ret = usb_get_port_status(dev, port + 1, portsts); + if (ret < 0) { debug("get_port_status failed\n"); - return; + return ret; }
portstatus = le16_to_cpu(portsts->wPortStatus); @@ -233,47 +236,55 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port) debug("usb_disconnect(&hub->children[port]);\n"); /* Return now if nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return; + return -ENOTCONN; } mdelay(200);
/* Reset the port */ - if (hub_port_reset(dev, port, &portstatus) < 0) { + ret = hub_port_reset(dev, port, &portstatus); + if (ret < 0) { printf("cannot reset port %i!?\n", port + 1); - return; + return ret; }
mdelay(200);
- /* Allocate a new device struct for it */ - usb = usb_alloc_new_device(dev->controller); - switch (portstatus & USB_PORT_STAT_SPEED_MASK) { case USB_PORT_STAT_SUPER_SPEED: - usb->speed = USB_SPEED_SUPER; + speed = USB_SPEED_SUPER; break; case USB_PORT_STAT_HIGH_SPEED: - usb->speed = USB_SPEED_HIGH; + speed = USB_SPEED_HIGH; break; case USB_PORT_STAT_LOW_SPEED: - usb->speed = USB_SPEED_LOW; + speed = USB_SPEED_LOW; break; default: - usb->speed = USB_SPEED_FULL; + speed = USB_SPEED_FULL; break; }
+ ret = usb_alloc_new_device(dev->controller, &usb); + if (ret) { + printf("cannot create new device: ret=%d", ret); + return ret; + } + dev->children[port] = usb; + usb->speed = speed; usb->parent = dev; usb->portnr = port + 1; /* Run it through the hoops (find a driver, etc) */ - if (usb_new_device(usb)) { + ret = usb_new_device(usb); + if (ret < 0) { /* Woops, disable the port */ - usb_free_device(); + usb_free_device(dev->controller); dev->children[port] = NULL; debug("hub: disabling port %d\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); } + + return ret; }
diff --git a/include/usb.h b/include/usb.h index 0e1c16a..11ff5ab 100644 --- a/include/usb.h +++ b/include/usb.h @@ -703,10 +703,26 @@ void usb_hub_reset(void); int hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat);
-struct usb_device *usb_alloc_new_device(void *controller); +/** + * usb_alloc_new_device() - Allocate a new device + * + * @devp: returns a pointer of a new device structure. With driver model this + * is a device pointer, but with legacy USB this pointer is + * driver-specific. + * @return 0 if OK, -ENOSPC if we have found out of room for new devices + */ +int usb_alloc_new_device(struct udevice *controller, struct usb_device **devp); + +/** + * usb_free_device() - Free a partially-inited device + * + * This is an internal function. It is used to reverse the action of + * usb_alloc_new_device() when we hit a problem during init. + */ +void usb_free_device(struct udevice *controller);
int usb_new_device(struct usb_device *dev); -void usb_free_device(void); + int usb_alloc_device(struct usb_device *dev);
#endif /*_USB_H_ */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This function returns NULL on error at present. Adjust it so that we can return a real error, as is needed with driver model. Also improve the error handling in its caller, usb_hub_port_connect_change(), and adjust the code order to prepare for driver model.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb.c | 17 ++++++++--------- common/usb_hub.c | 41 ++++++++++++++++++++++++++--------------- include/usb.h | 20 ++++++++++++++++++-- 3 files changed, 52 insertions(+), 26 deletions(-)
Applied to u-boot-dm/next.

Adjust this command to work with the new driver model uclass. It needs to iterate through multiple independent controllers to find hubs, and work through their children recursively in a different way. Otherwise the functionality is much the same.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/cmd_usb.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-)
diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 085802b..eab55cd 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -2,6 +2,9 @@ * (C) Copyright 2001 * Denis Peter, MPL AG Switzerland * + * Adapted for U-Boot driver model + * (C) Copyright 2015 Google, Inc + * * Most of this source has been derived from the Linux USB * project. * @@ -10,6 +13,7 @@
#include <common.h> #include <command.h> +#include <dm.h> #include <asm/byteorder.h> #include <asm/unaligned.h> #include <part.h> @@ -252,8 +256,46 @@ static void usb_display_config(struct usb_device *dev) printf("\n"); }
+/* + * With driver model this isn't right since we can have multiple controllers + * and the device numbering starts at 1 on each bus. + * TODO(sjg@chromium.org): Add a way to specify the controller/bus. + */ static struct usb_device *usb_find_device(int devnum) { +#ifdef CONFIG_DM_USB + struct usb_device *udev; + struct udevice *hub; + struct uclass *uc; + int ret; + + /* Device addresses start at 1 */ + devnum++; + ret = uclass_get(UCLASS_USB_HUB, &uc); + if (ret) + return NULL; + + uclass_foreach_dev(hub, uc) { + struct udevice *dev; + + if (!device_active(hub)) + continue; + udev = dev_get_parentdata(hub); + if (udev->devnum == devnum) + return udev; + + for (device_find_first_child(hub, &dev); + dev; + device_find_next_child(&dev)) { + if (!device_active(hub)) + continue; + + udev = dev_get_parentdata(dev); + if (udev->devnum == devnum) + return udev; + } + } +#else struct usb_device *udev; int d;
@@ -264,6 +306,7 @@ static struct usb_device *usb_find_device(int devnum) if (udev->devnum == devnum) return udev; } +#endif
return NULL; } @@ -293,20 +336,31 @@ static inline char *portspeed(int speed) /* shows the device tree recursively */ static void usb_show_tree_graph(struct usb_device *dev, char *pre) { - int i, index; + int index; int has_child, last_child;
index = strlen(pre); printf(" %s", pre); +#ifdef CONFIG_DM_USB + has_child = device_has_active_children(dev->dev); +#else /* check if the device has connected children */ + int i; + has_child = 0; for (i = 0; i < dev->maxchild; i++) { if (dev->children[i] != NULL) has_child = 1; } +#endif /* check if we are the last one */ +#ifdef CONFIG_DM_USB + last_child = device_is_last_sibling(dev->dev); +#else last_child = (dev->parent != NULL); +#endif if (last_child) { +#ifndef CONFIG_DM_USB for (i = 0; i < dev->parent->maxchild; i++) { /* search for children */ if (dev->parent->children[i] == dev) { @@ -322,6 +376,7 @@ static void usb_show_tree_graph(struct usb_device *dev, char *pre) } /* while */ } /* device found */ } /* for all children of the parent */ +#endif printf("\b+-"); /* correct last child */ if (last_child && index) @@ -340,6 +395,26 @@ static void usb_show_tree_graph(struct usb_device *dev, char *pre) if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial); printf(" %s\n", pre); +#ifdef CONFIG_DM_USB + struct udevice *child; + + for (device_find_first_child(dev->dev, &child); + child; + device_find_next_child(&child)) { + struct usb_device *udev; + + if (!device_active(child)) + continue; + + udev = dev_get_parentdata(child); + + /* Ignore emulators, we only want real devices */ + if (device_get_uclass_id(child) != UCLASS_USB_EMUL) { + usb_show_tree_graph(udev, pre); + pre[index] = 0; + } + } +#else if (dev->maxchild > 0) { for (i = 0; i < dev->maxchild; i++) { if (dev->children[i] != NULL) { @@ -348,6 +423,7 @@ static void usb_show_tree_graph(struct usb_device *dev, char *pre) } } } +#endif }
/* main routine for the tree command */ @@ -448,10 +524,13 @@ static void do_usb_start(void) if (usb_init() < 0) return;
+ /* Driver model will probe the devices as they are found */ +#ifndef CONFIG_DM_USB #ifdef CONFIG_USB_STORAGE /* try to recognize storage devices immediately */ usb_stor_curr_dev = usb_stor_scan(1); #endif +#endif #ifdef CONFIG_USB_HOST_ETHER /* try to recognize ethernet devices immediately */ usb_ether_curr_dev = usb_host_eth_scan(1); @@ -461,6 +540,43 @@ static void do_usb_start(void) #endif }
+#ifdef CONFIG_DM_USB +static void show_info(struct udevice *dev) +{ + struct udevice *child; + struct usb_device *udev; + + udev = dev_get_parentdata(dev); + usb_display_desc(udev); + usb_display_config(udev); + for (device_find_first_child(dev, &child); + child; + device_find_next_child(&child)) { + if (device_active(child)) + show_info(child); + } +} + +static int usb_device_info(void) +{ + struct udevice *bus; + + for (uclass_first_device(UCLASS_USB, &bus); + bus; + uclass_next_device(&bus)) { + struct udevice *hub; + + device_find_first_child(bus, &hub); + if (device_get_uclass_id(hub) == UCLASS_USB_HUB && + device_active(hub)) { + show_info(hub); + } + } + + return 0; +} +#endif + /****************************************************************************** * usb command intepreter */ @@ -507,6 +623,23 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } if (strncmp(argv[1], "tree", 4) == 0) { puts("USB device tree:\n"); +#ifdef CONFIG_DM_USB + struct udevice *bus; + + for (uclass_first_device(UCLASS_USB, &bus); + bus; + uclass_next_device(&bus)) { + struct usb_device *udev; + struct udevice *hub; + + device_find_first_child(bus, &hub); + if (device_get_uclass_id(hub) == UCLASS_USB_HUB && + device_active(hub)) { + udev = dev_get_parentdata(hub); + usb_show_tree(udev); + } + } +#else for (i = 0; i < USB_MAX_DEVICE; i++) { udev = usb_get_dev_index(i); if (udev == NULL) @@ -514,11 +647,15 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (udev->parent == NULL) usb_show_tree(udev); } +#endif return 0; } if (strncmp(argv[1], "inf", 3) == 0) { - int d; if (argc == 2) { +#ifdef CONFIG_DM_USB + usb_device_info(); +#else + int d; for (d = 0; d < USB_MAX_DEVICE; d++) { udev = usb_get_dev_index(d); if (udev == NULL) @@ -526,8 +663,14 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) usb_display_desc(udev); usb_display_config(udev); } +#endif return 0; } else { + /* + * With driver model this isn't right since we can + * have multiple controllers and the device numbering + * starts at 1 on each bus. + */ i = simple_strtoul(argv[2], NULL, 10); printf("config for device %d\n", i); udev = usb_find_device(i);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Adjust this command to work with the new driver model uclass. It needs to iterate through multiple independent controllers to find hubs, and work through their children recursively in a different way. Otherwise the functionality is much the same.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/cmd_usb.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-)
Applied to u-boot-dm/next.

This CONFIG is not used anywhere in U-Boot, so drop it.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-)
diff --git a/common/usb.c b/common/usb.c index f87c6a5..330e6a2 100644 --- a/common/usb.c +++ b/common/usb.c @@ -899,26 +899,8 @@ int usb_new_device(struct usb_device *dev) addr = dev->devnum; dev->devnum = 0;
-#ifdef CONFIG_LEGACY_USB_INIT_SEQ - /* this is the old and known way of initializing devices, it is - * different than what Windows and Linux are doing. Windows and Linux - * both retrieve 64 bytes while reading the device descriptor - * Several USB stick devices report ERR: CTL_TIMEOUT, caused by an - * invalid header while reading 8 bytes as device descriptor. */ - dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ - dev->maxpacketsize = PACKET_SIZE_8; - dev->epmaxpacketin[0] = 8; - dev->epmaxpacketout[0] = 8; - - err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, tmpbuf, 8); - if (err < 8) { - printf("\n USB device not responding, " \ - "giving up (status=%lX)\n", dev->status); - return 1; - } - memcpy(&dev->descriptor, tmpbuf, 8); -#else - /* This is a Windows scheme of initialization sequence, with double + /* + * This is a Windows scheme of initialization sequence, with double * reset of the device (Linux uses the same sequence) * Some equipment is said to work only with such init sequence; this * patch is based on the work by Alan Stern: @@ -929,7 +911,8 @@ int usb_new_device(struct usb_device *dev) struct usb_device *parent = dev->parent; unsigned short portstatus;
- /* send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is + /* + * send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is * only 18 bytes long, this will terminate with a short packet. But if * the maxpacket size is 8 or 16 the device may be waiting to transmit * some more, or keeps on retransmitting the 8 byte header. */ @@ -972,7 +955,6 @@ int usb_new_device(struct usb_device *dev) } else { usb_reset_root_port(); } -#endif
dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;

On Wednesday, March 25, 2015 at 07:22:03 PM, Simon Glass wrote:
This CONFIG is not used anywhere in U-Boot, so drop it.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

On 26 March 2015 at 12:22, Marek Vasut marex@denx.de wrote:
On Wednesday, March 25, 2015 at 07:22:03 PM, Simon Glass wrote:
This CONFIG is not used anywhere in U-Boot, so drop it.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Marek Vasut marex@denx.de
Applied to u-boot-dm/next.

Move the port reset code into its own function. Rename usb_hub_reset() to indicate that is is now a legacy function.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb.c | 40 ++++++++++++++++++++++++++-------------- common/usb_hub.c | 4 ++-- include/usb.h | 18 +++++++++++++++++- 3 files changed, 45 insertions(+), 17 deletions(-)
diff --git a/common/usb.c b/common/usb.c index 330e6a2..4ab2213 100644 --- a/common/usb.c +++ b/common/usb.c @@ -871,6 +871,26 @@ __weak int usb_alloc_device(struct usb_device *udev) { return 0; } + +int usb_legacy_port_reset(struct usb_device *hub, int portnr) +{ + if (hub) { + unsigned short portstatus; + int err; + + /* reset the port for the second time */ + err = legacy_hub_port_reset(hub, portnr - 1, &portstatus); + if (err < 0) { + printf("\n Couldn't reset port %i\n", portnr); + return err; + } + } else { + usb_reset_root_port(); + } + + return 0; +} + /* * By the time we get here, the device has gotten a new device ID * and is in the default state. We need to identify the thing and @@ -907,9 +927,6 @@ int usb_new_device(struct usb_device *dev) * http://sourceforge.net/mailarchive/forum.php? * thread_id=5729457&forum_id=5398 */ - __maybe_unused struct usb_device_descriptor *desc; - struct usb_device *parent = dev->parent; - unsigned short portstatus;
/* * send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is @@ -917,7 +934,6 @@ int usb_new_device(struct usb_device *dev) * the maxpacket size is 8 or 16 the device may be waiting to transmit * some more, or keeps on retransmitting the 8 byte header. */
- desc = (struct usb_device_descriptor *)tmpbuf; dev->descriptor.bMaxPacketSize0 = 64; /* Start off at 64 bytes */ /* Default to 64 byte max packet size */ dev->maxpacketsize = PACKET_SIZE_64; @@ -931,6 +947,9 @@ int usb_new_device(struct usb_device *dev) * of that is done for XHCI unlike EHCI. */ #ifndef CONFIG_USB_XHCI + struct usb_device_descriptor *desc; + + desc = (struct usb_device_descriptor *)tmpbuf; err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); if (err < 0) { debug("usb_new_device: usb_get_descriptor() failed\n"); @@ -945,16 +964,9 @@ int usb_new_device(struct usb_device *dev) dev->descriptor.bDeviceClass = desc->bDeviceClass; #endif
- if (parent) { - /* reset the port for the second time */ - err = hub_port_reset(dev->parent, dev->portnr - 1, &portstatus); - if (err < 0) { - printf("\n Couldn't reset port %i\n", dev->portnr); - return 1; - } - } else { - usb_reset_root_port(); - } + err = usb_legacy_port_reset(dev->parent, dev->portnr); + if (err) + return err;
dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; diff --git a/common/usb_hub.c b/common/usb_hub.c index 7199e25..49fa5a6 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -141,7 +141,7 @@ static inline char *portspeed(int portstatus) return speed_str; }
-int hub_port_reset(struct usb_device *dev, int port, +int legacy_hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat) { int tries; @@ -241,7 +241,7 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port) mdelay(200);
/* Reset the port */ - ret = hub_port_reset(dev, port, &portstatus); + ret = legacy_hub_port_reset(dev, port, &portstatus); if (ret < 0) { printf("cannot reset port %i!?\n", port + 1); return ret; diff --git a/include/usb.h b/include/usb.h index 11ff5ab..badb287 100644 --- a/include/usb.h +++ b/include/usb.h @@ -700,9 +700,25 @@ bool usb_device_has_child_on_port(struct usb_device *parent, int port);
int usb_hub_probe(struct usb_device *dev, int ifnum); void usb_hub_reset(void); -int hub_port_reset(struct usb_device *dev, int port, + +/** + * legacy_hub_port_reset() - reset a port given its usb_device pointer + * + * Reset a hub port and see if a device is present on that port, providing + * sufficient time for it to show itself. The port status is returned. + * + * With driver model this moves to hub_port_reset() and is passed a struct + * udevice. + * + * @dev: USB device to reset + * @port: Port number to reset (note ports are numbered from 0 here) + * @portstat: Returns port status + */ +int legacy_hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat);
+int hub_port_reset(struct udevice *dev, int port, unsigned short *portstat); + /** * usb_alloc_new_device() - Allocate a new device *

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Move the port reset code into its own function. Rename usb_hub_reset() to indicate that is is now a legacy function.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb.c | 40 ++++++++++++++++++++++++++-------------- common/usb_hub.c | 4 ++-- include/usb.h | 18 +++++++++++++++++- 3 files changed, 45 insertions(+), 17 deletions(-)
Applied to u-boot-dm/next.

usb_new_device() is far too long and does far too much. As a first step, move the code that does initial setup and reads a descriptor into its own function called usb_setup_descriptor().
For XHCI the init order is different - we set up the device but don't actually read the descriptor until after we set an address. Support this option as a parameter to usb_setup_descriptor().
Avoid changing this torturous code more than necessary to make it easy to review.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb.c | 117 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 50 deletions(-)
diff --git a/common/usb.c b/common/usb.c index 4ab2213..d0debbc 100644 --- a/common/usb.c +++ b/common/usb.c @@ -891,35 +891,12 @@ int usb_legacy_port_reset(struct usb_device *hub, int portnr) return 0; }
-/* - * By the time we get here, the device has gotten a new device ID - * and is in the default state. We need to identify the thing and - * get the ball rolling.. - * - * Returns 0 for success, != 0 for error. - */ -int usb_new_device(struct usb_device *dev) +static int usb_setup_descriptor(struct usb_device *dev, bool do_read) { - int addr, err; - int tmp; + __maybe_unused struct usb_device_descriptor *desc; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ);
/* - * Allocate usb 3.0 device context. - * USB 3.0 (xHCI) protocol tries to allocate device slot - * and related data structures first. This call does that. - * Refer to sec 4.3.2 in xHCI spec rev1.0 - */ - if (usb_alloc_device(dev)) { - printf("Cannot allocate device context to get SLOT_ID\n"); - return -1; - } - - /* We still haven't set the Address yet */ - addr = dev->devnum; - dev->devnum = 0; - - /* * This is a Windows scheme of initialization sequence, with double * reset of the device (Linux uses the same sequence) * Some equipment is said to work only with such init sequence; this @@ -934,40 +911,31 @@ int usb_new_device(struct usb_device *dev) * the maxpacket size is 8 or 16 the device may be waiting to transmit * some more, or keeps on retransmitting the 8 byte header. */
+ desc = (struct usb_device_descriptor *)tmpbuf; dev->descriptor.bMaxPacketSize0 = 64; /* Start off at 64 bytes */ /* Default to 64 byte max packet size */ dev->maxpacketsize = PACKET_SIZE_64; dev->epmaxpacketin[0] = 64; dev->epmaxpacketout[0] = 64;
- /* - * XHCI needs to issue a Address device command to setup - * proper device context structures, before it can interact - * with the device. So a get_descriptor will fail before any - * of that is done for XHCI unlike EHCI. - */ -#ifndef CONFIG_USB_XHCI - struct usb_device_descriptor *desc; + if (do_read) { + int err;
- desc = (struct usb_device_descriptor *)tmpbuf; - err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); - if (err < 0) { - debug("usb_new_device: usb_get_descriptor() failed\n"); - return 1; + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); + if (err < sizeof(dev->descriptor)) { + if (err < 0) { + printf("unable to get device descriptor (error=%d)\n", + err); + return err; + } else { + printf("USB device descriptor short read (expected %i, got %i)\n", + (int)sizeof(dev->descriptor), err); + return -EIO; + } + } + memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor)); }
- dev->descriptor.bMaxPacketSize0 = desc->bMaxPacketSize0; - /* - * Fetch the device class, driver can use this info - * to differentiate between HUB and DEVICE. - */ - dev->descriptor.bDeviceClass = desc->bDeviceClass; -#endif - - err = usb_legacy_port_reset(dev->parent, dev->portnr); - if (err) - return err; - dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; switch (dev->descriptor.bMaxPacketSize0) { @@ -984,6 +952,55 @@ int usb_new_device(struct usb_device *dev) dev->maxpacketsize = PACKET_SIZE_64; break; } + + return 0; +} + +/* + * By the time we get here, the device has gotten a new device ID + * and is in the default state. We need to identify the thing and + * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + bool do_read = true; + int addr, err; + int tmp; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); + + /* + * Allocate usb 3.0 device context. + * USB 3.0 (xHCI) protocol tries to allocate device slot + * and related data structures first. This call does that. + * Refer to sec 4.3.2 in xHCI spec rev1.0 + */ + if (usb_alloc_device(dev)) { + printf("Cannot allocate device context to get SLOT_ID\n"); + return -1; + } + + /* We still haven't set the Address yet */ + addr = dev->devnum; + dev->devnum = 0; + + /* + * XHCI needs to issue a Address device command to setup + * proper device context structures, before it can interact + * with the device. So a get_descriptor will fail before any + * of that is done for XHCI unlike EHCI. + */ +#ifndef CONFIG_USB_XHCI + do_read = false; +#endif + err = usb_setup_descriptor(dev, do_read); + if (err) + return err; + err = usb_legacy_port_reset(dev->parent, dev->portnr); + if (err) + return err; + dev->devnum = addr;
err = usb_set_address(dev); /* set address */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
usb_new_device() is far too long and does far too much. As a first step, move the code that does initial setup and reads a descriptor into its own function called usb_setup_descriptor().
For XHCI the init order is different - we set up the device but don't actually read the descriptor until after we set an address. Support this option as a parameter to usb_setup_descriptor().
Avoid changing this torturous code more than necessary to make it easy to review.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb.c | 117 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 50 deletions(-)
Applied to u-boot-dm/next.

Move the code that sets up the device with a new address into its own function, usb_prepare_device().
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb.c | 72 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 30 deletions(-)
diff --git a/common/usb.c b/common/usb.c index d0debbc..c3f805d 100644 --- a/common/usb.c +++ b/common/usb.c @@ -956,19 +956,10 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) return 0; }
-/* - * By the time we get here, the device has gotten a new device ID - * and is in the default state. We need to identify the thing and - * get the ball rolling.. - * - * Returns 0 for success, != 0 for error. - */ -int usb_new_device(struct usb_device *dev) +static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, + struct usb_device *parent, int portnr) { - bool do_read = true; - int addr, err; - int tmp; - ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); + int err;
/* * Allocate usb 3.0 device context. @@ -976,28 +967,15 @@ int usb_new_device(struct usb_device *dev) * and related data structures first. This call does that. * Refer to sec 4.3.2 in xHCI spec rev1.0 */ - if (usb_alloc_device(dev)) { + err = usb_alloc_device(dev); + if (err) { printf("Cannot allocate device context to get SLOT_ID\n"); - return -1; + return err; } - - /* We still haven't set the Address yet */ - addr = dev->devnum; - dev->devnum = 0; - - /* - * XHCI needs to issue a Address device command to setup - * proper device context structures, before it can interact - * with the device. So a get_descriptor will fail before any - * of that is done for XHCI unlike EHCI. - */ -#ifndef CONFIG_USB_XHCI - do_read = false; -#endif err = usb_setup_descriptor(dev, do_read); if (err) return err; - err = usb_legacy_port_reset(dev->parent, dev->portnr); + err = usb_legacy_port_reset(parent, portnr); if (err) return err;
@@ -1008,11 +986,45 @@ int usb_new_device(struct usb_device *dev) if (err < 0) { printf("\n USB device not accepting new address " \ "(error=%lX)\n", dev->status); - return 1; + return err; }
mdelay(10); /* Let the SET_ADDRESS settle */
+ return 0; +} + +/* + * By the time we get here, the device has gotten a new device ID + * and is in the default state. We need to identify the thing and + * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + bool do_read = true; + int addr, err; + int tmp, ret; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); + + /* We still haven't set the Address yet */ + addr = dev->devnum; + dev->devnum = 0; + + /* + * XHCI needs to issue a Address device command to setup + * proper device context structures, before it can interact + * with the device. So a get_descriptor will fail before any + * of that is done for XHCI unlike EHCI. + */ +#ifndef CONFIG_USB_XHCI + do_read = false; +#endif + ret = usb_prepare_device(dev, addr, do_read, dev->parent, dev->portnr); + if (ret) + return ret; + tmp = sizeof(dev->descriptor);
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Move the code that sets up the device with a new address into its own function, usb_prepare_device().
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb.c | 72 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 30 deletions(-)
Applied to u-boot-dm/next.

This function now calls usb_setup_device() to set up the device and usb_hub_probe() to check if it is a hub. The XHCI special case is now a parameter to usb_setup_device(). The latter will be used by the USB uclass when it is added, since it does not rely on any CONFIGs or legacy data structures.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb.c | 114 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 46 deletions(-)
diff --git a/common/usb.c b/common/usb.c index c3f805d..5e14d7c 100644 --- a/common/usb.c +++ b/common/usb.c @@ -994,71 +994,40 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, return 0; }
-/* - * By the time we get here, the device has gotten a new device ID - * and is in the default state. We need to identify the thing and - * get the ball rolling.. - * - * Returns 0 for success, != 0 for error. - */ -int usb_new_device(struct usb_device *dev) +static int usb_select_config(struct usb_device *dev) { - bool do_read = true; - int addr, err; - int tmp, ret; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); + int err;
- /* We still haven't set the Address yet */ - addr = dev->devnum; - dev->devnum = 0; - - /* - * XHCI needs to issue a Address device command to setup - * proper device context structures, before it can interact - * with the device. So a get_descriptor will fail before any - * of that is done for XHCI unlike EHCI. - */ -#ifndef CONFIG_USB_XHCI - do_read = false; -#endif - ret = usb_prepare_device(dev, addr, do_read, dev->parent, dev->portnr); - if (ret) - return ret; - - tmp = sizeof(dev->descriptor); + err = usb_setup_descriptor(dev, true); + if (err) + return err;
- err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, - tmpbuf, sizeof(dev->descriptor)); - if (err < tmp) { - if (err < 0) - printf("unable to get device descriptor (error=%d)\n", - err); - else - printf("USB device descriptor short read " \ - "(expected %i, got %i)\n", tmp, err); - return 1; - } - memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor)); /* correct le values */ le16_to_cpus(&dev->descriptor.bcdUSB); le16_to_cpus(&dev->descriptor.idVendor); le16_to_cpus(&dev->descriptor.idProduct); le16_to_cpus(&dev->descriptor.bcdDevice); + /* only support for one config for now */ err = usb_get_configuration_no(dev, tmpbuf, 0); if (err < 0) { printf("usb_new_device: Cannot read configuration, " \ "skipping device %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct); - return -1; + return -err; } usb_parse_config(dev, tmpbuf, 0); usb_set_maxpacket(dev); - /* we set the default configuration here */ + /* + * we set the default configuration here + * This seems premature. If the driver wants a different configuration + * it will need to select itself. + */ if (usb_set_configuration(dev, dev->config.desc.bConfigurationValue)) { printf("failed to set default configuration " \ "len %d, status %lX\n", dev->act_len, dev->status); - return -1; + return -err; } debug("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", dev->descriptor.iManufacturer, dev->descriptor.iProduct, @@ -1078,11 +1047,64 @@ int usb_new_device(struct usb_device *dev) debug("Manufacturer %s\n", dev->mf); debug("Product %s\n", dev->prod); debug("SerialNumber %s\n", dev->serial); - /* now prode if the device is a hub */ - usb_hub_probe(dev, 0); + return 0; }
+static int usb_setup_device(struct usb_device *dev, bool do_read, + struct usb_device *parent, int portnr) +{ + int addr; + int ret; + + /* We still haven't set the Address yet */ + addr = dev->devnum; + dev->devnum = 0; + + ret = usb_prepare_device(dev, addr, do_read, parent, portnr); + if (ret) + return ret; + ret = usb_select_config(dev); + + return ret; +} + +/* + * By the time we get here, the device has gotten a new device ID + * and is in the default state. We need to identify the thing and + * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + bool do_read = true; + int count; + int err; + + /* + * XHCI needs to issue a Address device command to setup + * proper device context structures, before it can interact + * with the device. So a get_descriptor will fail before any + * of that is done for XHCI unlike EHCI. + */ +#ifndef CONFIG_USB_XHCI + do_read = false; +#endif + err = usb_setup_device(dev, do_read, dev->parent, dev->portnr); + if (err) + return err; + + count = 1; + /* now prode if the device is a hub */ + err = usb_hub_probe(dev, 0); + if (err < 0) + return err; + count += err; + + return count; +} + __weak int board_usb_init(int index, enum usb_init_type init) {

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This function now calls usb_setup_device() to set up the device and usb_hub_probe() to check if it is a hub. The XHCI special case is now a parameter to usb_setup_device(). The latter will be used by the USB uclass when it is added, since it does not rely on any CONFIGs or legacy data structures.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb.c | 114 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 46 deletions(-)
Applied to u-boot-dm/next.

Add the required #ifdefs and remove unwanted data structures so that the USB uclass will be able to use this file.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/common/usb.c b/common/usb.c index 5e14d7c..06c1c43 100644 --- a/common/usb.c +++ b/common/usb.c @@ -28,6 +28,7 @@ */ #include <common.h> #include <command.h> +#include <dm.h> #include <asm/processor.h> #include <linux/compiler.h> #include <linux/ctype.h> @@ -41,12 +42,13 @@
#define USB_BUFSIZ 512
-static struct usb_device usb_dev[USB_MAX_DEVICE]; -static int dev_index; static int asynch_allowed; - char usb_started; /* flag for the started/stopped USB status */
+#ifndef CONFIG_DM_USB +static struct usb_device usb_dev[USB_MAX_DEVICE]; +static int dev_index; + #ifndef CONFIG_USB_MAX_CONTROLLER_COUNT #define CONFIG_USB_MAX_CONTROLLER_COUNT 1 #endif @@ -152,6 +154,7 @@ int usb_disable_asynch(int disable) asynch_allowed = !disable; return old_value; } +#endif /* !CONFIG_DM_USB */
/*------------------------------------------------------------------- @@ -815,6 +818,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) * the USB device are static allocated [USB_MAX_DEVICE]. */
+#ifndef CONFIG_DM_USB
/* returns a pointer to the device with the index [index]. * if the device is not assigned (dev->devnum==-1) returns NULL @@ -871,7 +875,9 @@ __weak int usb_alloc_device(struct usb_device *udev) { return 0; } +#endif /* !CONFIG_DM_USB */
+#ifndef CONFIG_DM_USB int usb_legacy_port_reset(struct usb_device *hub, int portnr) { if (hub) { @@ -890,6 +896,7 @@ int usb_legacy_port_reset(struct usb_device *hub, int portnr)
return 0; } +#endif
static int usb_setup_descriptor(struct usb_device *dev, bool do_read) { @@ -994,7 +1001,7 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, return 0; }
-static int usb_select_config(struct usb_device *dev) +int usb_select_config(struct usb_device *dev) { ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); int err; @@ -1051,8 +1058,8 @@ static int usb_select_config(struct usb_device *dev) return 0; }
-static int usb_setup_device(struct usb_device *dev, bool do_read, - struct usb_device *parent, int portnr) +int usb_setup_device(struct usb_device *dev, bool do_read, + struct usb_device *parent, int portnr) { int addr; int ret; @@ -1069,6 +1076,7 @@ static int usb_setup_device(struct usb_device *dev, bool do_read, return ret; }
+#ifndef CONFIG_DM_USB /* * By the time we get here, the device has gotten a new device ID * and is in the default state. We need to identify the thing and @@ -1104,10 +1112,21 @@ int usb_new_device(struct usb_device *dev)
return count; } +#endif
__weak int board_usb_init(int index, enum usb_init_type init) { return 0; } + +bool usb_device_has_child_on_port(struct usb_device *parent, int port) +{ +#ifdef CONFIG_DM_USB + return false; +#else + return parent->children[port] != NULL; +#endif +} + /* EOF */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Add the required #ifdefs and remove unwanted data structures so that the USB uclass will be able to use this file.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-)
Applied to u-boot-dm/next.

Split out the hub detection logic so it can be used by driver model. Also adjust the code to return errors correctly.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_hub.c | 57 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 16 deletions(-)
diff --git a/common/usb_hub.c b/common/usb_hub.c index 49fa5a6..cc22f4b 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -297,27 +297,30 @@ static int usb_hub_configure(struct usb_device *dev) struct usb_hub_descriptor *descriptor; struct usb_hub_device *hub; __maybe_unused struct usb_hub_status *hubsts; + int ret;
/* "allocate" Hub device */ hub = usb_hub_allocate(); if (hub == NULL) - return -1; + return -ENOMEM; hub->pusb_dev = dev; /* Get the the hub descriptor */ - if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { + ret = usb_get_hub_descriptor(dev, buffer, 4); + if (ret < 0) { debug("usb_hub_configure: failed to get hub " \ "descriptor, giving up %lX\n", dev->status); - return -1; + return ret; } descriptor = (struct usb_hub_descriptor *)buffer;
length = min_t(int, descriptor->bLength, sizeof(struct usb_hub_descriptor));
- if (usb_get_hub_descriptor(dev, buffer, length) < 0) { + ret = usb_get_hub_descriptor(dev, buffer, length); + if (ret < 0) { debug("usb_hub_configure: failed to get hub " \ "descriptor 2nd giving up %lX\n", dev->status); - return -1; + return ret; } memcpy((unsigned char *)&hub->desc, buffer, length); /* adjust 16bit values */ @@ -385,13 +388,14 @@ static int usb_hub_configure(struct usb_device *dev) if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { debug("usb_hub_configure: failed to get Status - " \ "too long: %d\n", descriptor->bLength); - return -1; + return -EFBIG; }
- if (usb_get_hub_status(dev, buffer) < 0) { + ret = usb_get_hub_status(dev, buffer); + if (ret < 0) { debug("usb_hub_configure: failed to get Status %lX\n", dev->status); - return -1; + return ret; }
#ifdef DEBUG @@ -423,6 +427,7 @@ static int usb_hub_configure(struct usb_device *dev) int ret; ulong start = get_timer(0);
+ debug("\n\nScanning port %d\n", i + 1); /* * Wait for (whichever finishes first) * - A maximum of 10 seconds @@ -503,33 +508,53 @@ static int usb_hub_configure(struct usb_device *dev) return 0; }
-int usb_hub_probe(struct usb_device *dev, int ifnum) +static int usb_hub_check(struct usb_device *dev, int ifnum) { struct usb_interface *iface; - struct usb_endpoint_descriptor *ep; - int ret; + struct usb_endpoint_descriptor *ep = NULL;
iface = &dev->config.if_desc[ifnum]; /* Is it a hub? */ if (iface->desc.bInterfaceClass != USB_CLASS_HUB) - return 0; + goto err; /* Some hubs have a subclass of 1, which AFAICT according to the */ /* specs is not defined, but it works */ if ((iface->desc.bInterfaceSubClass != 0) && (iface->desc.bInterfaceSubClass != 1)) - return 0; + goto err; /* Multiple endpoints? What kind of mutant ninja-hub is this? */ if (iface->desc.bNumEndpoints != 1) - return 0; + goto err; ep = &iface->ep_desc[0]; /* Output endpoint? Curiousier and curiousier.. */ if (!(ep->bEndpointAddress & USB_DIR_IN)) - return 0; + goto err; /* If it's not an interrupt endpoint, we'd better punt! */ if ((ep->bmAttributes & 3) != 3) - return 0; + goto err; /* We found a hub */ debug("USB hub found\n"); + return 0; + +err: + debug("USB hub not found: bInterfaceClass=%d, bInterfaceSubClass=%d, bNumEndpoints=%d\n", + iface->desc.bInterfaceClass, iface->desc.bInterfaceSubClass, + iface->desc.bNumEndpoints); + if (ep) { + debug(" bEndpointAddress=%#x, bmAttributes=%d", + ep->bEndpointAddress, ep->bmAttributes); + } + + return -ENOENT; +} + +int usb_hub_probe(struct usb_device *dev, int ifnum) +{ + int ret; + + ret = usb_hub_check(dev, ifnum); + if (ret) + return 0; ret = usb_hub_configure(dev); return ret; }

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Split out the hub detection logic so it can be used by driver model. Also adjust the code to return errors correctly.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_hub.c | 57 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 16 deletions(-)
Applied to u-boot-dm/next.

Adjust the existing hub code to support driver model, and add a USB driver for hubs.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_hub.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++--- include/dm/uclass-id.h | 1 + 2 files changed, 91 insertions(+), 4 deletions(-)
diff --git a/common/usb_hub.c b/common/usb_hub.c index cc22f4b..6933015 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -24,12 +24,16 @@
#include <common.h> #include <command.h> +#include <dm.h> #include <errno.h> #include <asm/processor.h> #include <asm/unaligned.h> #include <linux/ctype.h> #include <asm/byteorder.h> #include <asm/unaligned.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR;
#include <usb.h> #ifdef CONFIG_4xx @@ -38,6 +42,7 @@
#define USB_BUFSIZ 512
+/* TODO(sjg@chromium.org): Remove this when CONFIG_DM_USB is defined */ static struct usb_hub_device hub_dev[USB_MAX_HUB]; static int usb_hub_index;
@@ -148,7 +153,12 @@ int legacy_hub_port_reset(struct usb_device *dev, int port, ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus, portchange;
- debug("hub_port_reset: resetting port %d...\n", port); +#ifdef CONFIG_DM_USB + debug("%s: resetting '%s' port %d...\n", __func__, dev->dev->name, + port + 1); +#else + debug("%s: resetting port %d...\n", __func__, port + 1); +#endif for (tries = 0; tries < MAX_TRIES; tries++) {
usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); @@ -206,10 +216,17 @@ int legacy_hub_port_reset(struct usb_device *dev, int port, return 0; }
+#ifdef CONFIG_DM_USB +int hub_port_reset(struct udevice *dev, int port, unsigned short *portstat) +{ + struct usb_device *udev = dev_get_parentdata(dev); + + return legacy_hub_port_reset(udev, port, portstat); +} +#endif
int usb_hub_port_connect_change(struct usb_device *dev, int port) { - struct usb_device *usb; ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus; int ret, speed; @@ -232,7 +249,8 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port)
/* Disconnect any existing devices under this port */ if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && - (!(portstatus & USB_PORT_STAT_ENABLE))) || (dev->children[port])) { + (!(portstatus & USB_PORT_STAT_ENABLE))) || + usb_device_has_child_on_port(dev, port)) { debug("usb_disconnect(&hub->children[port]);\n"); /* Return now if nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) @@ -264,6 +282,13 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port) break; }
+#ifdef CONFIG_DM_USB + struct udevice *child; + + ret = usb_scan_device(dev->dev, port + 1, speed, &child); +#else + struct usb_device *usb; + ret = usb_alloc_new_device(dev->controller, &usb); if (ret) { printf("cannot create new device: ret=%d", ret); @@ -280,6 +305,9 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port) /* Woops, disable the port */ usb_free_device(dev->controller); dev->children[port] = NULL; + } +#endif + if (ret < 0) { debug("hub: disabling port %d\n", port + 1); usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); } @@ -427,7 +455,11 @@ static int usb_hub_configure(struct usb_device *dev) int ret; ulong start = get_timer(0);
+#ifdef CONFIG_DM_USB + debug("\n\nScanning '%s' port %d\n", dev->dev->name, i + 1); +#else debug("\n\nScanning port %d\n", i + 1); +#endif /* * Wait for (whichever finishes first) * - A maximum of 10 seconds @@ -477,7 +509,7 @@ static int usb_hub_configure(struct usb_device *dev) * them again. Works at least with mouse driver */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && - ((dev->children[i]))) { + usb_device_has_child_on_port(dev, i)) { debug("already running port %i " \ "disabled by hub (EMI?), " \ "re-enabling...\n", i + 1); @@ -558,3 +590,57 @@ int usb_hub_probe(struct usb_device *dev, int ifnum) ret = usb_hub_configure(dev); return ret; } + +#ifdef CONFIG_DM_USB +int usb_hub_scan(struct udevice *hub) +{ + struct usb_device *udev = dev_get_parentdata(hub); + + return usb_hub_configure(udev); +} + +static int usb_hub_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +static int usb_hub_post_probe(struct udevice *dev) +{ + debug("%s\n", __func__); + return usb_hub_scan(dev); +} + +static const struct udevice_id usb_hub_ids[] = { + { .compatible = "usb-hub" }, + { } +}; + +U_BOOT_DRIVER(usb_generic_hub) = { + .name = "usb_hub", + .id = UCLASS_USB_HUB, + .of_match = usb_hub_ids, + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; + +UCLASS_DRIVER(usb_hub) = { + .id = UCLASS_USB_HUB, + .name = "usb_hub", + .post_bind = usb_hub_post_bind, + .post_probe = usb_hub_post_probe, + .child_pre_probe = usb_child_pre_probe, + .per_child_auto_alloc_size = sizeof(struct usb_device), + .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), +}; + +static const struct usb_device_id hub_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, + .bDeviceClass = USB_CLASS_HUB + }, + { } /* Terminating entry */ +}; + +USB_DEVICE(usb_generic_hub, hub_id_table); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index cb6d2e5..acec938 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -40,6 +40,7 @@ enum uclass_id { UCLASS_PCH, /* x86 platform controller hub */ UCLASS_ETH, /* Ethernet device */ UCLASS_USB, /* USB bus */ + UCLASS_USB_HUB, /* USB hub */
UCLASS_COUNT, UCLASS_INVALID = -1,

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Adjust the existing hub code to support driver model, and add a USB driver for hubs.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_hub.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++--- include/dm/uclass-id.h | 1 + 2 files changed, 91 insertions(+), 4 deletions(-)
Applied to u-boot-dm/next.

These are better off in a header file so they can be used by other code (e.g. the sandbox USB storage emulator).
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_storage.c | 45 ++------------------------------------------- include/usb_defs.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 43 deletions(-)
diff --git a/common/usb_storage.c b/common/usb_storage.c index 1411737..590408a 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -56,49 +56,8 @@ static const unsigned char us_direction[256/8] = { #define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
static ccb usb_ccb __attribute__((aligned(ARCH_DMA_MINALIGN))); - -/* - * CBI style - */ - -#define US_CBI_ADSC 0 - -/* - * BULK only - */ -#define US_BBB_RESET 0xff -#define US_BBB_GET_MAX_LUN 0xfe - -/* Command Block Wrapper */ -typedef struct { - __u32 dCBWSignature; -# define CBWSIGNATURE 0x43425355 - __u32 dCBWTag; - __u32 dCBWDataTransferLength; - __u8 bCBWFlags; -# define CBWFLAGS_OUT 0x00 -# define CBWFLAGS_IN 0x80 - __u8 bCBWLUN; - __u8 bCDBLength; -# define CBWCDBLENGTH 16 - __u8 CBWCDB[CBWCDBLENGTH]; -} umass_bbb_cbw_t; -#define UMASS_BBB_CBW_SIZE 31 static __u32 CBWTag;
-/* Command Status Wrapper */ -typedef struct { - __u32 dCSWSignature; -# define CSWSIGNATURE 0x53425355 - __u32 dCSWTag; - __u32 dCSWDataResidue; - __u8 bCSWStatus; -# define CSWSTATUS_GOOD 0x0 -# define CSWSTATUS_FAILED 0x1 -# define CSWSTATUS_PHASE 0x2 -} umass_bbb_csw_t; -#define UMASS_BBB_CSW_SIZE 13 - #define USB_MAX_STOR_DEV 5 static int usb_max_devs; /* number of highest available usb device */
@@ -483,7 +442,7 @@ static int usb_stor_BBB_comdat(ccb *srb, struct us_data *us) int actlen; int dir_in; unsigned int pipe; - ALLOC_CACHE_ALIGN_BUFFER(umass_bbb_cbw_t, cbw, 1); + ALLOC_CACHE_ALIGN_BUFFER(struct umass_bbb_cbw, cbw, 1);
dir_in = US_DIRECTION(srb->cmd[0]);
@@ -658,7 +617,7 @@ static int usb_stor_BBB_transport(ccb *srb, struct us_data *us) int dir_in; int actlen, data_actlen; unsigned int pipe, pipein, pipeout; - ALLOC_CACHE_ALIGN_BUFFER(umass_bbb_csw_t, csw, 1); + ALLOC_CACHE_ALIGN_BUFFER(struct umass_bbb_csw, csw, 1); #ifdef BBB_XPORT_TRACE unsigned char *ptr; int index; diff --git a/include/usb_defs.h b/include/usb_defs.h index 236a5ec..d7f7465 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -286,4 +286,46 @@ #define HUB_CHANGE_LOCAL_POWER 0x0001 #define HUB_CHANGE_OVERCURRENT 0x0002
+/* + * CBI style + */ + +#define US_CBI_ADSC 0 + +/* Command Block Wrapper */ +struct umass_bbb_cbw { + __u32 dCBWSignature; +# define CBWSIGNATURE 0x43425355 + __u32 dCBWTag; + __u32 dCBWDataTransferLength; + __u8 bCBWFlags; +# define CBWFLAGS_OUT 0x00 +# define CBWFLAGS_IN 0x80 +# define CBWFLAGS_SBZ 0x7f + __u8 bCBWLUN; + __u8 bCDBLength; +# define CBWCDBLENGTH 16 + __u8 CBWCDB[CBWCDBLENGTH]; +}; +#define UMASS_BBB_CBW_SIZE 31 + +/* Command Status Wrapper */ +struct umass_bbb_csw { + __u32 dCSWSignature; +# define CSWSIGNATURE 0x53425355 + __u32 dCSWTag; + __u32 dCSWDataResidue; + __u8 bCSWStatus; +# define CSWSTATUS_GOOD 0x0 +# define CSWSTATUS_FAILED 0x1 +# define CSWSTATUS_PHASE 0x2 +}; +#define UMASS_BBB_CSW_SIZE 13 + +/* + * BULK only + */ +#define US_BBB_RESET 0xff +#define US_BBB_GET_MAX_LUN 0xfe + #endif /*_USB_DEFS_H_ */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
These are better off in a header file so they can be used by other code (e.g. the sandbox USB storage emulator).
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_storage.c | 45 ++------------------------------------------- include/usb_defs.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 43 deletions(-)
Applied to u-boot-dm/next.

This function assumes that unsigned long is 32-bits wide, but it is not on 64-bit machines. Use the correct type, and add a few debug() lines also.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_storage.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/common/usb_storage.c b/common/usb_storage.c index 590408a..80ed14a 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -1178,6 +1178,7 @@ int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, iface->desc.bInterfaceClass != USB_CLASS_MASS_STORAGE || iface->desc.bInterfaceSubClass < US_SC_MIN || iface->desc.bInterfaceSubClass > US_SC_MAX) { + debug("Not mass storage\n"); /* if it's not a mass storage, we go no further */ return 0; } @@ -1293,9 +1294,9 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, block_dev_desc_t *dev_desc) { unsigned char perq, modi; - ALLOC_CACHE_ALIGN_BUFFER(unsigned long, cap, 2); + ALLOC_CACHE_ALIGN_BUFFER(uint32_t, cap, 2); ALLOC_CACHE_ALIGN_BUFFER(unsigned char, usb_stor_buf, 36); - unsigned long *capacity, *blksz; + uint32_t *capacity, *blksz; ccb *pccb = &usb_ccb;
pccb->pdata = usb_stor_buf; @@ -1304,8 +1305,10 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, pccb->lun = dev_desc->lun; debug(" address %d\n", dev_desc->target);
- if (usb_inquiry(pccb, ss)) + if (usb_inquiry(pccb, ss)) { + debug("%s: usb_inquiry() failed\n", __func__); return -1; + }
perq = usb_stor_buf[0]; modi = usb_stor_buf[1]; @@ -1315,6 +1318,7 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, * they would not respond to test_unit_ready . */ if (((perq & 0x1f) == 0x1f) || ((perq & 0x1f) == 0x0d)) { + debug("%s: unknown/unsupported device\n", __func__); return 0; } if ((modi&0x80) == 0x80) { @@ -1352,19 +1356,19 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, cap[1] = 0x200; } ss->flags &= ~USB_READY; - debug("Read Capacity returns: 0x%lx, 0x%lx\n", cap[0], cap[1]); + debug("Read Capacity returns: 0x%x, 0x%x\n", cap[0], cap[1]); #if 0 if (cap[0] > (0x200000 * 10)) /* greater than 10 GByte */ cap[0] >>= 16; #endif - cap[0] = cpu_to_be32(cap[0]); - cap[1] = cpu_to_be32(cap[1]); + cap[0] = be32_to_cpu(cap[0]); + cap[1] = be32_to_cpu(cap[1]);
/* this assumes bigendian! */ cap[0] += 1; capacity = &cap[0]; blksz = &cap[1]; - debug("Capacity = 0x%lx, blocksz = 0x%lx\n", *capacity, *blksz); + debug("Capacity = 0x%x, blocksz = 0x%x\n", *capacity, *blksz); dev_desc->lba = *capacity; dev_desc->blksz = *blksz; dev_desc->log2blksz = LOG2(dev_desc->blksz);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This function assumes that unsigned long is 32-bits wide, but it is not on 64-bit machines. Use the correct type, and add a few debug() lines also.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_storage.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
Applied to u-boot-dm/next.

The for() loop is not needed since the value is immediately accessible. Use this instead to simplify the code.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_storage.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-)
diff --git a/common/usb_storage.c b/common/usb_storage.c index 80ed14a..64d01ea 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -1005,7 +1005,7 @@ unsigned long usb_stor_read(int device, lbaint_t blknr, unsigned short smallblks; struct usb_device *dev; struct us_data *ss; - int retry, i; + int retry; ccb *srb = &usb_ccb;
if (blkcnt == 0) @@ -1013,14 +1013,11 @@ unsigned long usb_stor_read(int device, lbaint_t blknr,
device &= 0xff; /* Setup device */ - debug("\nusb_read: dev %d \n", device); - dev = NULL; - for (i = 0; i < USB_MAX_DEVICE; i++) { - dev = usb_get_dev_index(i); - if (dev == NULL) - return 0; - if (dev->devnum == usb_dev_desc[device].target) - break; + debug("\nusb_read: dev %d\n", device); + dev = usb_dev_desc[device].priv; + if (!dev) { + debug("%s: No device\n", __func__); + return 0; } ss = (struct us_data *)dev->privptr;
@@ -1078,7 +1075,7 @@ unsigned long usb_stor_write(int device, lbaint_t blknr, unsigned short smallblks; struct usb_device *dev; struct us_data *ss; - int retry, i; + int retry; ccb *srb = &usb_ccb;
if (blkcnt == 0) @@ -1086,15 +1083,10 @@ unsigned long usb_stor_write(int device, lbaint_t blknr,
device &= 0xff; /* Setup device */ - debug("\nusb_write: dev %d \n", device); - dev = NULL; - for (i = 0; i < USB_MAX_DEVICE; i++) { - dev = usb_get_dev_index(i); - if (dev == NULL) - return 0; - if (dev->devnum == usb_dev_desc[device].target) - break; - } + debug("\nusb_write: dev %d\n", device); + dev = usb_dev_desc[device].priv; + if (!dev) + return 0; ss = (struct us_data *)dev->privptr;
usb_disable_asynch(1); /* asynch transfer not allowed */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
The for() loop is not needed since the value is immediately accessible. Use this instead to simplify the code.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_storage.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-)
Applied to u-boot-dm/next.

With a few tweaks we can compile this code with sandbox and enable testing of the USB storage layer.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_storage.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/common/usb_storage.c b/common/usb_storage.c index 64d01ea..acea5ae 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -34,6 +34,7 @@ #include <common.h> #include <command.h> #include <inttypes.h> +#include <mapmem.h> #include <asm/byteorder.h> #include <asm/processor.h>
@@ -295,8 +296,9 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) /* set up the transfer loop */ do { /* transfer the data */ - debug("Bulk xfer 0x%x(%d) try #%d\n", - (unsigned int)buf, this_xfer, 11 - maxtry); + debug("Bulk xfer 0x%lx(%d) try #%d\n", + (ulong)map_to_sysmem(buf), this_xfer, + 11 - maxtry); result = usb_bulk_msg(us->pusb_dev, pipe, buf, this_xfer, &partial, USB_CNTL_TIMEOUT * 5); @@ -562,7 +564,7 @@ static int usb_stor_CBI_get_status(ccb *srb, struct us_data *us) (void *) &us->ip_data, us->irqmaxp, us->irqinterval); timeout = 1000; while (timeout--) { - if ((volatile int *) us->ip_wanted == NULL) + if (*(volatile int *)&us->ip_wanted == 0) break; mdelay(10); }

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
With a few tweaks we can compile this code with sandbox and enable testing of the USB storage layer.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_storage.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
Applied to u-boot-dm/next.

The usb_stor_scan() function is quite long, so split out the code that scans each device into its own function. Also, rather than setting up the block device list once at the start, set it up as each device is scanned. This makes it possible to use this code from driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_storage.c | 97 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 38 deletions(-)
diff --git a/common/usb_storage.c b/common/usb_storage.c index acea5ae..67494b7 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -33,6 +33,7 @@
#include <common.h> #include <command.h> +#include <errno.h> #include <inttypes.h> #include <mapmem.h> #include <asm/byteorder.h> @@ -168,6 +169,60 @@ static unsigned int usb_get_max_lun(struct us_data *us) return (len > 0) ? *result : 0; }
+static int usb_stor_probe_device(struct usb_device *dev) +{ + if (dev == NULL) + return -ENOENT; /* no more devices available */ + + debug("\n\nProbing for storage\n"); + if (usb_storage_probe(dev, 0, &usb_stor[usb_max_devs])) { + /* OK, it's a storage device. Iterate over its LUNs + * and populate `usb_dev_desc'. + */ + int lun, max_lun, start = usb_max_devs; + + max_lun = usb_get_max_lun(&usb_stor[usb_max_devs]); + for (lun = 0; + lun <= max_lun && usb_max_devs < USB_MAX_STOR_DEV; + lun++) { + struct block_dev_desc *blkdev; + + blkdev = &usb_dev_desc[usb_max_devs]; + memset(blkdev, '\0', sizeof(block_dev_desc_t)); + blkdev->if_type = IF_TYPE_USB; + blkdev->dev = usb_max_devs; + blkdev->part_type = PART_TYPE_UNKNOWN; + blkdev->target = 0xff; + blkdev->type = DEV_TYPE_UNKNOWN; + blkdev->block_read = usb_stor_read; + blkdev->block_write = usb_stor_write; + blkdev->lun = lun; + blkdev->priv = dev; + + if (usb_stor_get_info(dev, &usb_stor[start], + &usb_dev_desc[usb_max_devs]) == + 1) { + usb_max_devs++; + debug("%s: Found device %p\n", __func__, dev); + } + } + } + + /* if storage device */ + if (usb_max_devs == USB_MAX_STOR_DEV) { + printf("max USB Storage Device reached: %d stopping\n", + usb_max_devs); + return -ENOSPC; + } + + return 0; +} + +void usb_stor_reset(void) +{ + usb_max_devs = 0; +} + /******************************************************************************* * scan the usb and reports device info * to the user if mode = 1 @@ -176,54 +231,20 @@ static unsigned int usb_get_max_lun(struct us_data *us) int usb_stor_scan(int mode) { unsigned char i; - struct usb_device *dev;
if (mode == 1) printf(" scanning usb for storage devices... ");
usb_disable_asynch(1); /* asynch transfer not allowed */
- for (i = 0; i < USB_MAX_STOR_DEV; i++) { - memset(&usb_dev_desc[i], 0, sizeof(block_dev_desc_t)); - usb_dev_desc[i].if_type = IF_TYPE_USB; - usb_dev_desc[i].dev = i; - usb_dev_desc[i].part_type = PART_TYPE_UNKNOWN; - usb_dev_desc[i].target = 0xff; - usb_dev_desc[i].type = DEV_TYPE_UNKNOWN; - usb_dev_desc[i].block_read = usb_stor_read; - usb_dev_desc[i].block_write = usb_stor_write; - } - - usb_max_devs = 0; + usb_stor_reset(); for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + dev = usb_get_dev_index(i); /* get device */ debug("i=%d\n", i); - if (dev == NULL) - break; /* no more devices available */ - - if (usb_storage_probe(dev, 0, &usb_stor[usb_max_devs])) { - /* OK, it's a storage device. Iterate over its LUNs - * and populate `usb_dev_desc'. - */ - int lun, max_lun, start = usb_max_devs; - - max_lun = usb_get_max_lun(&usb_stor[usb_max_devs]); - for (lun = 0; - lun <= max_lun && usb_max_devs < USB_MAX_STOR_DEV; - lun++) { - usb_dev_desc[usb_max_devs].lun = lun; - if (usb_stor_get_info(dev, &usb_stor[start], - &usb_dev_desc[usb_max_devs]) == 1) { - usb_max_devs++; - } - } - } - /* if storage device */ - if (usb_max_devs == USB_MAX_STOR_DEV) { - printf("max USB Storage Device reached: %d stopping\n", - usb_max_devs); + if (usb_stor_probe_device(dev)) break; - } } /* for */
usb_disable_asynch(0); /* asynch transfer allowed */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
The usb_stor_scan() function is quite long, so split out the code that scans each device into its own function. Also, rather than setting up the block device list once at the start, set it up as each device is scanned. This makes it possible to use this code from driver model.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_storage.c | 97 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 38 deletions(-)
Applied to u-boot-dm/next.

Add support for scanning USB storage devices with driver model. This mostly involves adding a USB device ID for storage devices.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_storage.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/common/usb_storage.c b/common/usb_storage.c index 67494b7..8b8962e 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -9,6 +9,8 @@ * * Adapted for U-Boot: * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * Driver model conversion: + * (C) Copyright 2015 Google, Inc * * For BBB support (C) Copyright 2003 * Gary Jennejohn, DENX Software Engineering garyj@denx.de @@ -33,11 +35,13 @@
#include <common.h> #include <command.h> +#include <dm.h> #include <errno.h> #include <inttypes.h> #include <mapmem.h> #include <asm/byteorder.h> #include <asm/processor.h> +#include <dm/device-internal.h>
#include <part.h> #include <usb.h> @@ -106,7 +110,6 @@ struct us_data {
static struct us_data usb_stor[USB_MAX_STOR_DEV];
- #define USB_STOR_TRANSPORT_GOOD 0 #define USB_STOR_TRANSPORT_FAILED -1 #define USB_STOR_TRANSPORT_ERROR -2 @@ -119,7 +122,6 @@ unsigned long usb_stor_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer); unsigned long usb_stor_write(int device, lbaint_t blknr, lbaint_t blkcnt, const void *buffer); -struct usb_device * usb_get_dev_index(int index); void uhci_show_temp_int_td(void);
#ifdef CONFIG_PARTITIONS @@ -223,6 +225,7 @@ void usb_stor_reset(void) usb_max_devs = 0; }
+#ifndef CONFIG_DM_USB /******************************************************************************* * scan the usb and reports device info * to the user if mode = 1 @@ -253,6 +256,7 @@ int usb_stor_scan(int mode) return 0; return -1; } +#endif
static int usb_stor_irq(struct usb_device *dev) { @@ -1396,3 +1400,46 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, debug("partype: %d\n", dev_desc->part_type); return 1; } + +#ifdef CONFIG_DM_USB + +static int usb_mass_storage_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parentdata(dev); + int ret; + + usb_disable_asynch(1); /* asynch transfer not allowed */ + ret = usb_stor_probe_device(udev); + usb_disable_asynch(0); /* asynch transfer allowed */ + + return ret; +} + +static const struct udevice_id usb_mass_storage_ids[] = { + { .compatible = "usb-mass-storage" }, + { } +}; + +U_BOOT_DRIVER(usb_mass_storage) = { + .name = "usb_mass_storage", + .id = UCLASS_MASS_STORAGE, + .of_match = usb_mass_storage_ids, + .probe = usb_mass_storage_probe, +}; + +UCLASS_DRIVER(usb_mass_storage) = { + .id = UCLASS_MASS_STORAGE, + .name = "usb_mass_storage", +}; + +static const struct usb_device_id mass_storage_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, + .bInterfaceClass = USB_CLASS_MASS_STORAGE + }, + { } /* Terminating entry */ +}; + +USB_DEVICE(usb_mass_storage, mass_storage_id_table); + +#endif

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Add support for scanning USB storage devices with driver model. This mostly involves adding a USB device ID for storage devices.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_storage.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-)
Applied to u-boot-dm/next.

Put these at the top of the file so they are in one place. Also add function prototypes to the header file to avoid call site mismatches.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 22 +++++++++++----------- drivers/usb/host/ehci.h | 6 ++++++ 2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f1fb190..10a0671 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -143,6 +143,17 @@ __weak void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) mdelay(50); }
+__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ + if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + /* Printing the message would cause a scan failure! */ + debug("The request port(%u) is not configured\n", port); + return NULL; + } + + return (uint32_t *)&hcor->or_portsc[port]; +} + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; @@ -649,17 +660,6 @@ fail: return -1; }
-__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) -{ - if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { - /* Printing the message would cause a scan failure! */ - debug("The request port(%u) is not configured\n", port); - return NULL; - } - - return (uint32_t *)&hcor->or_portsc[port]; -} - int ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 79aecd4..3e5427a 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -250,6 +250,12 @@ struct ehci_ctrl { int ntds; };
+/* Weak functions that drivers can override */ +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg); +void ehci_set_usbmode(int index); +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg); +uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port); + /* Low level init functions */ int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct ehci_hcor **hcor);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Put these at the top of the file so they are in one place. Also add function prototypes to the header file to avoid call site mismatches.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 22 +++++++++++----------- drivers/usb/host/ehci.h | 6 ++++++ 2 files changed, 17 insertions(+), 11 deletions(-)
Applied to u-boot-dm/next.

Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-faraday.c | 5 +++-- drivers/usb/host/ehci-hcd.c | 4 ++-- drivers/usb/host/ehci-tegra.c | 5 +++-- drivers/usb/host/ehci.h | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c index 3b761bc..e386813 100644 --- a/drivers/usb/host/ehci-faraday.c +++ b/drivers/usb/host/ehci-faraday.c @@ -101,11 +101,12 @@ void ehci_set_usbmode(int index) * This ehci_get_port_speed() overrides the weak function * in "ehci-hcd.c". */ -int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) { int spd, ret = PORTSC_PSPD_HS; - union ehci_faraday_regs *regs = (void __iomem *)((ulong)hcor - 0x10); + union ehci_faraday_regs *regs;
+ ret = (void __iomem *)((ulong)ctrl->hcor - 0x10); if (ehci_is_fotg2xx(regs)) spd = OTGCSR_SPD(readl(®s->otg.otgcsr)); else diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 10a0671..fed3942 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -119,7 +119,7 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif
-__weak int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +__weak int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) { return PORTSC_PSPD(reg); } @@ -781,7 +781,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
if (ehci_is_TDI()) { - switch (ehci_get_port_speed(ctrl->hcor, reg)) { + switch (ehci_get_port_speed(ctrl, reg)) { case PORTSC_PSPD_FS: break; case PORTSC_PSPD_LS: diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index b5ad1e3..0831fe9 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -225,13 +225,14 @@ void ehci_set_usbmode(int index) * This ehci_get_port_speed overrides the weak function ehci_get_port_speed * in "ehci-hcd.c". */ -int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) { uint32_t tmp; uint32_t *reg_ptr;
if (controller->has_hostpc) { - reg_ptr = (uint32_t *)((u8 *)&hcor->or_usbcmd + HOSTPC1_DEVLC); + reg_ptr = (uint32_t *)((u8 *)&ctrl->hcor->or_usbcmd + + HOSTPC1_DEVLC); tmp = ehci_readl(reg_ptr); return HOSTPC1_PSPD(tmp); } else diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 3e5427a..ec4d6b0 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -251,7 +251,7 @@ struct ehci_ctrl { };
/* Weak functions that drivers can override */ -int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg); +int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg); void ehci_set_usbmode(int index); void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg); uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-faraday.c | 5 +++-- drivers/usb/host/ehci-hcd.c | 4 ++-- drivers/usb/host/ehci-tegra.c | 5 +++-- drivers/usb/host/ehci.h | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-)
Applied to u-boot-dm/next.

Add a private data pointer that clients of EHCI can use to access their private information. This establishes a link between struct ehci_ctrl and its associated controller data structure.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 10 ++++++++++ drivers/usb/host/ehci.h | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fed3942..d85308f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -930,6 +930,16 @@ unknown: return -1; }
+void ehci_set_controller_priv(int index, void *priv) +{ + ehcic[index].priv = priv; +} + +void *ehci_get_controller_priv(int index) +{ + return ehcic[index].priv; +} + int usb_lowlevel_stop(int index) { ehci_shutdown(&ehcic[index]); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index ec4d6b0..d538bb6 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -248,6 +248,7 @@ struct ehci_ctrl { uint32_t *periodic_list; int periodic_schedules; int ntds; + void *priv; /* client's private data */ };
/* Weak functions that drivers can override */ @@ -256,6 +257,26 @@ void ehci_set_usbmode(int index); void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg); uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port);
+/** + * ehci_set_controller_priv() - Set up private data for the controller + * + * This function can be called in ehci_hcd_init() to tell the EHCI layer + * about the controller's private data pointer. Then in the above functions + * this can be accessed given the struct ehci_ctrl pointer. + * + * @index: Controller number to set + * @priv: Controller pointer + */ +void ehci_set_controller_priv(int index, void *priv); + +/** + * ehci_get_controller_priv() - Get controller private data + * + * @index Controller number to get + * @return controller pointer for this index + */ +void *ehci_get_controller_priv(int index); + /* Low level init functions */ int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct ehci_hcor **hcor);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Add a private data pointer that clients of EHCI can use to access their private information. This establishes a link between struct ehci_ctrl and its associated controller data structure.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 10 ++++++++++ drivers/usb/host/ehci.h | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+)
Applied to u-boot-dm/next.

At present the tegra driver uses a separate pointer to know which controller type is in use. This works because only one controller type is used at a time.
With driver model we want to make the controller state hermetic in the sense that it is not necessary to look elsewhere to know the controller type. This will permit a controller to implement the EHCI weak functions without reference to global data structures.
To achieve this, define an enum for the controller type and store it with the information on each EHCI controller.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 0831fe9..9a73e60 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -61,6 +61,14 @@ enum dr_mode { DR_MODE_OTG, /* supports both */ };
+enum usb_ctlr_type { + USB_CTLR_T20, + USB_CTLR_T30, + USB_CTLR_T114, + + USB_CTRL_COUNT, +}; + /* Information about a USB port */ struct fdt_usb { struct usb_ctlr *reg; /* address of registers in physical memory */ @@ -69,6 +77,7 @@ struct fdt_usb { unsigned enabled:1; /* 1 to enable, 0 to disable */ unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */ unsigned initialized:1; /* has this port already been initialized? */ + enum usb_ctlr_type type; enum usb_init_type init_type; enum dr_mode dr_mode; /* dual role mode */ enum periph_id periph_id;/* peripheral id */ @@ -162,7 +171,7 @@ struct fdt_usb_controller { const unsigned *pll_parameter; };
-static struct fdt_usb_controller fdt_usb_controllers[] = { +static struct fdt_usb_controller fdt_usb_controllers[USB_CTRL_COUNT] = { { .compat = COMPAT_NVIDIA_TEGRA20_USB, .has_hostpc = 0, @@ -284,7 +293,7 @@ void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr) setbits_le32(&usbctlr->susp_ctrl, UTMIP_PHY_ENB); }
-static const unsigned *get_pll_timing(void) +static const unsigned *get_pll_timing(struct fdt_usb_controller *controller) { const unsigned *timing;
@@ -331,6 +340,7 @@ static void init_phy_mux(struct fdt_usb *config, uint pts, static int init_utmi_usb_controller(struct fdt_usb *config, enum usb_init_type init) { + struct fdt_usb_controller *controller; u32 b_sess_valid_mask, val; int loop_count; const unsigned *timing; @@ -363,11 +373,14 @@ static int init_utmi_usb_controller(struct fdt_usb *config, VBUS_SENSE_CTL_MASK, VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT);
+ controller = &fdt_usb_controllers[config->type]; + debug("controller=%p, type=%d\n", controller, config->type); + /* * PLL Delay CONFIGURATION settings. The following parameters control * the bring up of the plls. */ - timing = get_pll_timing(); + timing = get_pll_timing(controller);
if (!controller->has_hostpc) { val = readl(&usbctlr->utmip_misc_cfg1); @@ -702,10 +715,12 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) * @blob: fdt blob * @node_list: list of nodes to process (any <=0 are ignored) * @count: number of nodes to process + * @id: controller type (enum usb_ctlr_type) * * Return: 0 - ok, -1 - error */ -static int process_usb_nodes(const void *blob, int node_list[], int count) +static int process_usb_nodes(const void *blob, int node_list[], int count, + enum usb_ctlr_type id) { struct fdt_usb config; int node, i; @@ -729,9 +744,11 @@ static int process_usb_nodes(const void *blob, int node_list[], int count) return -1; } if (!clk_done) { - config_clock(get_pll_timing()); + config_clock(get_pll_timing( + &fdt_usb_controllers[id])); clk_done = 1; } + config.type = id; config.initialized = 0;
/* add new USB port to the list of available ports */ @@ -753,7 +770,7 @@ int usb_process_devicetree(const void *blob) count = fdtdec_find_aliases_for_id(blob, "usb", controller->compat, node_list, USB_PORTS_MAX); if (count) { - err = process_usb_nodes(blob, node_list, count); + err = process_usb_nodes(blob, node_list, count, i); if (err) printf("%s: Error processing USB node!\n", __func__); @@ -786,6 +803,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, return -1;
config = &port[index]; + ehci_set_controller_priv(index, config);
switch (init) { case USB_INIT_HOST:

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
At present the tegra driver uses a separate pointer to know which controller type is in use. This works because only one controller type is used at a time.
With driver model we want to make the controller state hermetic in the sense that it is not necessary to look elsewhere to know the controller type. This will permit a controller to implement the EHCI weak functions without reference to global data structures.
To achieve this, define an enum for the controller type and store it with the information on each EHCI controller.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-)
Applied to u-boot-dm/next.

Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
board/genesi/mx51_efikamx/efikamx-usb.c | 3 ++- drivers/usb/host/ehci-hcd.c | 5 +++-- drivers/usb/host/ehci-tegra.c | 3 ++- drivers/usb/host/ehci.h | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/board/genesi/mx51_efikamx/efikamx-usb.c b/board/genesi/mx51_efikamx/efikamx-usb.c index 0b43101..0c0b8d3 100644 --- a/board/genesi/mx51_efikamx/efikamx-usb.c +++ b/board/genesi/mx51_efikamx/efikamx-usb.c @@ -173,7 +173,8 @@ int board_ehci_hcd_init(int port) return 0; }
-void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg) { uint32_t port = OTG_BASE_ADDR + (0x200 * CONFIG_MXC_USB_PORT); struct usb_ehci *ehci = (struct usb_ehci *)port; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index d85308f..e9f58e3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -138,7 +138,8 @@ __weak void ehci_set_usbmode(int index) ehci_writel(reg_ptr, tmp); }
-__weak void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +__weak void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg) { mdelay(50); } @@ -843,7 +844,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, * usb 2.0 specification say 50 ms resets on * root */ - ehci_powerup_fixup(status_reg, ®); + ehci_powerup_fixup(ctrl, status_reg, ®);
ehci_writel(status_reg, reg & ~EHCI_PS_PR); /* diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 9a73e60..5d746f2 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -198,7 +198,8 @@ static struct fdt_usb_controller *controller; * This ehci_powerup_fixup overrides the weak function ehci_powerup_fixup * in "ehci-hcd.c". */ -void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg) { mdelay(50); /* This is to avoid PORT_ENABLE bit to be cleared in "ehci-hcd.c". */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index d538bb6..a00c7e7 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -254,7 +254,8 @@ struct ehci_ctrl { /* Weak functions that drivers can override */ int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg); void ehci_set_usbmode(int index); -void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg); +void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg); uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port);
/**

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
board/genesi/mx51_efikamx/efikamx-usb.c | 3 ++- drivers/usb/host/ehci-hcd.c | 5 +++-- drivers/usb/host/ehci-tegra.c | 3 ++- drivers/usb/host/ehci.h | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-)
Applied to u-boot-dm/next.

We don't need this anymore, so adjust the code to avoid using it.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 5d746f2..5060dd1 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -189,8 +189,6 @@ static struct fdt_usb_controller fdt_usb_controllers[USB_CTRL_COUNT] = { }, };
-static struct fdt_usb_controller *controller; - /* * A known hardware issue where Connect Status Change bit of PORTSC register * of USB1 controller will be set after Port Reset. @@ -201,6 +199,10 @@ static struct fdt_usb_controller *controller; void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, uint32_t *reg) { + struct fdt_usb *config = ctrl->priv; + struct fdt_usb_controller *controller; + + controller = &fdt_usb_controllers[config->type]; mdelay(50); /* This is to avoid PORT_ENABLE bit to be cleared in "ehci-hcd.c". */ if (controller->has_hostpc) @@ -237,9 +239,12 @@ void ehci_set_usbmode(int index) */ int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) { + struct fdt_usb *config = ctrl->priv; + struct fdt_usb_controller *controller; uint32_t tmp; uint32_t *reg_ptr;
+ controller = &fdt_usb_controllers[config->type]; if (controller->has_hostpc) { reg_ptr = (uint32_t *)((u8 *)&ctrl->hcor->or_usbcmd + HOSTPC1_DEVLC); @@ -766,10 +771,9 @@ int usb_process_devicetree(const void *blob) int i;
for (i = 0; i < ARRAY_SIZE(fdt_usb_controllers); i++) { - controller = &fdt_usb_controllers[i]; - count = fdtdec_find_aliases_for_id(blob, "usb", - controller->compat, node_list, USB_PORTS_MAX); + fdt_usb_controllers[i].compat, node_list, + USB_PORTS_MAX); if (count) { err = process_usb_nodes(blob, node_list, count, i); if (err) @@ -778,8 +782,6 @@ int usb_process_devicetree(const void *blob) return err; } } - if (i == ARRAY_SIZE(fdt_usb_controllers)) - controller = NULL;
return err; }

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
We don't need this anymore, so adjust the code to avoid using it.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
Applied to u-boot-dm/next.

Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller. This makes the weak functions use a consistent API.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-faraday.c | 2 +- drivers/usb/host/ehci-hcd.c | 6 +++--- drivers/usb/host/ehci-tegra.c | 5 ++--- drivers/usb/host/ehci.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c index e386813..c64672b 100644 --- a/drivers/usb/host/ehci-faraday.c +++ b/drivers/usb/host/ehci-faraday.c @@ -92,7 +92,7 @@ int ehci_hcd_stop(int index) * This ehci_set_usbmode() overrides the weak function * in "ehci-hcd.c". */ -void ehci_set_usbmode(int index) +void ehci_set_usbmode(struct ehci_ctrl *ctrl) { /* nothing needs to be done */ } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e9f58e3..196e23a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -124,12 +124,12 @@ __weak int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) return PORTSC_PSPD(reg); }
-__weak void ehci_set_usbmode(int index) +__weak void ehci_set_usbmode(struct ehci_ctrl *ctrl) { uint32_t tmp; uint32_t *reg_ptr;
- reg_ptr = (uint32_t *)((u8 *)&ehcic[index].hcor->or_usbcmd + USBMODE); + reg_ptr = (uint32_t *)((u8 *)&ctrl->hcor->or_usbcmd + USBMODE); tmp = ehci_readl(reg_ptr); tmp |= USBMODE_CM_HC; #if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) @@ -187,7 +187,7 @@ static int ehci_reset(int index) }
if (ehci_is_TDI()) - ehci_set_usbmode(index); + ehci_set_usbmode(&ehcic[index]);
#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 5060dd1..6054b2b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -219,13 +219,12 @@ void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, * This ehci_set_usbmode overrides the weak function ehci_set_usbmode * in "ehci-hcd.c". */ -void ehci_set_usbmode(int index) +void ehci_set_usbmode(struct ehci_ctrl *ctrl) { - struct fdt_usb *config; + struct fdt_usb *config = ctrl->priv; struct usb_ctlr *usbctlr; uint32_t tmp;
- config = &port[index]; usbctlr = config->reg;
tmp = ehci_readl(&usbctlr->usb_mode); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index a00c7e7..164b3cb 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -253,7 +253,7 @@ struct ehci_ctrl {
/* Weak functions that drivers can override */ int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg); -void ehci_set_usbmode(int index); +void ehci_set_usbmode(struct ehci_ctrl *ctrl); void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, uint32_t *reg); uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller. This makes the weak functions use a consistent API.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-faraday.c | 2 +- drivers/usb/host/ehci-hcd.c | 6 +++--- drivers/usb/host/ehci-tegra.c | 5 ++--- drivers/usb/host/ehci.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-)
Applied to u-boot-dm/next.

Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller. This makes the weak functions use a consistent API.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-faraday.c | 4 ++-- drivers/usb/host/ehci-hcd.c | 6 +++--- drivers/usb/host/ehci.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c index c64672b..b865fea 100644 --- a/drivers/usb/host/ehci-faraday.c +++ b/drivers/usb/host/ehci-faraday.c @@ -134,7 +134,7 @@ int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) * This ehci_get_portsc_register() overrides the weak function * in "ehci-hcd.c". */ -uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port) { /* Faraday EHCI has one and only one portsc register */ if (port) { @@ -144,5 +144,5 @@ uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) }
/* Faraday EHCI PORTSC register offset is 0x20 from hcor */ - return (uint32_t *)((uint8_t *)hcor + 0x20); + return (uint32_t *)((uint8_t *)ctrl->hcor + 0x20); } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 196e23a..4b0074e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -144,7 +144,7 @@ __weak void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, mdelay(50); }
-__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +__weak uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port) { if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { /* Printing the message would cause a scan failure! */ @@ -152,7 +152,7 @@ __weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) return NULL; }
- return (uint32_t *)&hcor->or_portsc[port]; + return (uint32_t *)&ctrl->hcor->or_portsc[port]; }
static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) @@ -687,7 +687,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1); + status_reg = ehci_get_portsc_register(ctrl, port - 1); if (!status_reg) return -1; break; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 164b3cb..0fd59bc 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -256,7 +256,7 @@ int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg); void ehci_set_usbmode(struct ehci_ctrl *ctrl); void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, uint32_t *reg); -uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port); +uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port);
/** * ehci_set_controller_priv() - Set up private data for the controller

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Adjust this function so that it is passed an EHCI controller pointer so that implementations can look up their controller. This makes the weak functions use a consistent API.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-faraday.c | 4 ++-- drivers/usb/host/ehci-hcd.c | 6 +++--- drivers/usb/host/ehci.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-)
Applied to u-boot-dm/next.

With driver model we want to remove the controller pointer in struct udevice and use driver model data structures instead. To prepare for this, move access to this field to a function which can provide a different implementation for driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 54 +++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 17 deletions(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4b0074e..d01e17e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -119,6 +119,11 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif
+static struct ehci_ctrl *ehci_get_ctrl(struct usb_device *udev) +{ + return udev->controller; +} + __weak int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) { return PORTSC_PSPD(reg); @@ -315,7 +320,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t cmd; int timeout; int ret = 0; - struct ehci_ctrl *ctrl = dev->controller; + struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -661,9 +666,8 @@ fail: return -1; }
-int -ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, - int length, struct devrequest *req) +static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, struct devrequest *req) { uint8_t tmpbuf[4]; u16 typeReq; @@ -672,7 +676,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, uint32_t reg; uint32_t *status_reg; int port = le16_to_cpu(req->index) & 0xff; - struct ehci_ctrl *ctrl = dev->controller; + struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
srclen = 0;
@@ -1075,9 +1079,8 @@ done: return 0; }
-int -submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int length) +static int _ehci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length) {
if (usb_pipetype(pipe) != PIPE_BULK) { @@ -1087,11 +1090,11 @@ submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return ehci_submit_async(dev, pipe, buffer, length, NULL); }
-int -submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int length, struct devrequest *setup) +static int _ehci_submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, + struct devrequest *setup) { - struct ehci_ctrl *ctrl = dev->controller; + struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
if (usb_pipetype(pipe) != PIPE_CONTROL) { debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); @@ -1161,7 +1164,7 @@ struct int_queue * create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, int elementsize, void *buffer, int interval) { - struct ehci_ctrl *ctrl = dev->controller; + struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); struct int_queue *result = NULL; int i;
@@ -1353,7 +1356,7 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) { - struct ehci_ctrl *ctrl = dev->controller; + struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); int result = -1; unsigned long timeout;
@@ -1397,9 +1400,8 @@ out: return result; }
-int -submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int length, int interval) +static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, int interval) { void *backbuffer; struct int_queue *queue; @@ -1434,3 +1436,21 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, /* everything worked out fine */ return result; } + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length) +{ + return _ehci_submit_bulk_msg(dev, pipe, buffer, length); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int length, struct devrequest *setup) +{ + return _ehci_submit_control_msg(dev, pipe, buffer, length, setup); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, int interval) +{ + return _ehci_submit_int_msg(dev, pipe, buffer, length, interval); +}

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
With driver model we want to remove the controller pointer in struct udevice and use driver model data structures instead. To prepare for this, move access to this field to a function which can provide a different implementation for driver model.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 54 +++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 17 deletions(-)
Applied to u-boot-dm/next.

Move the bulk of the code in usb_lowlevel_init() into a separate function which will also be used by driver model. Keep the CONFIG options out of this function by providing a tweak flag for Faraday. We need to avoid using CONFIG options in driver model code where possible, since it makes it impossible to use multiple controllers in that code where they have different options.
The CONFIG_EHCI_HCD_INIT_AFTER_RESET option is also kept out of the common init function. With driver model the controller will be able to perform this extra init itself after registering with the EHCI layer.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 117 +++++++++++++++++++++++++------------------- drivers/usb/host/ehci.h | 6 +++ 2 files changed, 72 insertions(+), 51 deletions(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index d01e17e..3fa6fe2 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -945,41 +945,19 @@ void *ehci_get_controller_priv(int index) return ehcic[index].priv; }
-int usb_lowlevel_stop(int index) -{ - ehci_shutdown(&ehcic[index]); - return ehci_hcd_stop(index); -} - -int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +static int ehci_common_init(struct ehci_ctrl *ctrl, uint tweaks) { - uint32_t reg; - uint32_t cmd; struct QH *qh_list; struct QH *periodic; + uint32_t reg; + uint32_t cmd; int i; - int rc; - - rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); - if (rc) - return rc; - if (init == USB_INIT_DEVICE) - goto done; - - /* EHCI spec section 4.1 */ - if (ehci_reset(index)) - return -1;
-#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) - rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); - if (rc) - return rc; -#endif /* Set the high address word (aka segment) for 64-bit controller */ - if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1) - ehci_writel(&ehcic[index].hcor->or_ctrldssegment, 0); + if (ehci_readl(&ctrl->hccr->cr_hccparams) & 1) + ehci_writel(&ctrl->hcor->or_ctrldssegment, 0);
- qh_list = &ehcic[index].qh_list; + qh_list = &ctrl->qh_list;
/* Set head of reclaim list */ memset(qh_list, 0, sizeof(*qh_list)); @@ -995,14 +973,14 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) ALIGN_END_ADDR(struct QH, qh_list, 1));
/* Set async. queue head pointer. */ - ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); + ehci_writel(&ctrl->hcor->or_asynclistaddr, (uint32_t)qh_list);
/* * Set up periodic list * Step 1: Parent QH for all periodic transfers. */ - ehcic[index].periodic_schedules = 0; - periodic = &ehcic[index].periodic_queue; + ctrl->periodic_schedules = 0; + periodic = &ctrl->periodic_queue; memset(periodic, 0, sizeof(*periodic)); periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); @@ -1020,25 +998,25 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) * Split Transactions will be spread across microframes using * S-mask and C-mask. */ - if (ehcic[index].periodic_list == NULL) - ehcic[index].periodic_list = memalign(4096, 1024 * 4); + if (ctrl->periodic_list == NULL) + ctrl->periodic_list = memalign(4096, 1024 * 4);
- if (!ehcic[index].periodic_list) + if (!ctrl->periodic_list) return -ENOMEM; for (i = 0; i < 1024; i++) { - ehcic[index].periodic_list[i] = cpu_to_hc32((uint32_t)periodic + ctrl->periodic_list[i] = cpu_to_hc32((uint32_t)periodic | QH_LINK_TYPE_QH); }
- flush_dcache_range((uint32_t)ehcic[index].periodic_list, - ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list, + flush_dcache_range((uint32_t)ctrl->periodic_list, + ALIGN_END_ADDR(uint32_t, ctrl->periodic_list, 1024));
/* Set periodic list base address */ - ehci_writel(&ehcic[index].hcor->or_periodiclistbase, - (uint32_t)ehcic[index].periodic_list); + ehci_writel(&ctrl->hcor->or_periodiclistbase, + (uint32_t)ctrl->periodic_list);
- reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams); + reg = ehci_readl(&ctrl->hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); /* Port Indicators */ @@ -1051,29 +1029,66 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) | 0x01, &descriptor.hub.wHubCharacteristics);
/* Start the host controller. */ - cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); /* * Philips, Intel, and maybe others need CMD_RUN before the * root hub will detect new devices (why?); NEC doesn't */ cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); cmd |= CMD_RUN; - ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); + ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
-#ifndef CONFIG_USB_EHCI_FARADAY - /* take control over the ports */ - cmd = ehci_readl(&ehcic[index].hcor->or_configflag); - cmd |= FLAG_CF; - ehci_writel(&ehcic[index].hcor->or_configflag, cmd); -#endif + if (!(tweaks & EHCI_TWEAK_NO_INIT_CF)) { + /* take control over the ports */ + cmd = ehci_readl(&ctrl->hcor->or_configflag); + cmd |= FLAG_CF; + ehci_writel(&ctrl->hcor->or_configflag, cmd); + }
/* unblock posted write */ - cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); mdelay(5); - reg = HC_VERSION(ehci_readl(&ehcic[index].hccr->cr_capbase)); + reg = HC_VERSION(ehci_readl(&ctrl->hccr->cr_capbase)); printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
- ehcic[index].rootdev = 0; + return 0; +} + +int usb_lowlevel_stop(int index) +{ + ehci_shutdown(&ehcic[index]); + return ehci_hcd_stop(index); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + struct ehci_ctrl *ctrl = &ehcic[index]; + uint tweaks = 0; + int rc; + + rc = ehci_hcd_init(index, init, &ctrl->hccr, &ctrl->hcor); + if (rc) + return rc; + if (init == USB_INIT_DEVICE) + goto done; + + /* EHCI spec section 4.1 */ + if (ehci_reset(index)) + return -1; + +#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) + rc = ehci_hcd_init(index, init, &ctrl->hccr, &ctrl->hcor); + if (rc) + return rc; +#endif +#ifdef CONFIG_USB_EHCI_FARADAY + tweaks |= EHCI_TWEAK_NO_INIT_CF; +#endif + rc = ehci_common_init(ctrl, tweaks); + if (rc) + return rc; + + ctrl->rootdev = 0; done: *controller = &ehcic[index]; return 0; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 0fd59bc..2ca111a 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -238,6 +238,12 @@ struct QH { }; };
+/* Tweak flags for EHCI, used to control operation */ +enum { + /* don't use or_configflag in init */ + EHCI_TWEAK_NO_INIT_CF = 1 << 0, +}; + struct ehci_ctrl { struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ struct ehci_hcor *hcor;

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Move the bulk of the code in usb_lowlevel_init() into a separate function which will also be used by driver model. Keep the CONFIG options out of this function by providing a tweak flag for Faraday. We need to avoid using CONFIG options in driver model code where possible, since it makes it impossible to use multiple controllers in that code where they have different options.
The CONFIG_EHCI_HCD_INIT_AFTER_RESET option is also kept out of the common init function. With driver model the controller will be able to perform this extra init itself after registering with the EHCI layer.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 117 +++++++++++++++++++++++++------------------- drivers/usb/host/ehci.h | 6 +++ 2 files changed, 72 insertions(+), 51 deletions(-)
Applied to u-boot-dm/next.

These are a pain with driver model because we might have different EHCI drivers which want to implement them differently. Now that they use consistent function signatures, we can in good conscience move them to a struct.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
board/genesi/mx51_efikamx/efikamx-usb.c | 5 +- drivers/usb/host/ehci-faraday.c | 113 +++++++++++++++----------------- drivers/usb/host/ehci-hcd.c | 49 +++++++++++--- drivers/usb/host/ehci-mx5.c | 12 ++++ drivers/usb/host/ehci-tegra.c | 26 ++++---- drivers/usb/host/ehci.h | 27 +++++--- 6 files changed, 136 insertions(+), 96 deletions(-)
diff --git a/board/genesi/mx51_efikamx/efikamx-usb.c b/board/genesi/mx51_efikamx/efikamx-usb.c index 0c0b8d3..9dfd249 100644 --- a/board/genesi/mx51_efikamx/efikamx-usb.c +++ b/board/genesi/mx51_efikamx/efikamx-usb.c @@ -173,8 +173,9 @@ int board_ehci_hcd_init(int port) return 0; }
-void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, - uint32_t *reg) +/* This overrides a weak function */ +void mx5_ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg) { uint32_t port = OTG_BASE_ADDR + (0x200 * CONFIG_MXC_USB_PORT); struct usb_ehci *ehci = (struct usb_ehci *)port; diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c index b865fea..821222c 100644 --- a/drivers/usb/host/ehci-faraday.c +++ b/drivers/usb/host/ehci-faraday.c @@ -29,6 +29,59 @@ static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs) return !readl(®s->usb.easstr); }
+void faraday_ehci_set_usbmode(struct ehci_ctrl *ctrl) +{ + /* nothing needs to be done */ +} + +int faraday_ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) +{ + int spd, ret = PORTSC_PSPD_HS; + union ehci_faraday_regs *regs; + + ret = (void __iomem *)((ulong)ctrl->hcor - 0x10); + if (ehci_is_fotg2xx(regs)) + spd = OTGCSR_SPD(readl(®s->otg.otgcsr)); + else + spd = BMCSR_SPD(readl(®s->usb.bmcsr)); + + switch (spd) { + case 0: /* full speed */ + ret = PORTSC_PSPD_FS; + break; + case 1: /* low speed */ + ret = PORTSC_PSPD_LS; + break; + case 2: /* high speed */ + ret = PORTSC_PSPD_HS; + break; + default: + printf("ehci-faraday: invalid device speed\n"); + break; + } + + return ret; +} + +uint32_t *faraday_ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port) +{ + /* Faraday EHCI has one and only one portsc register */ + if (port) { + /* Printing the message would cause a scan failure! */ + debug("The request port(%d) is not configured\n", port); + return NULL; + } + + /* Faraday EHCI PORTSC register offset is 0x20 from hcor */ + return (uint32_t *)((uint8_t *)ctrl->hcor + 0x20); +} + +static const struct ehci_ops faraday_ehci_ops = { + .set_usb_mode = faraday_ehci_set_usbmode, + .get_port_speed = faraday_ehci_get_port_speed, + .get_portsc_register = faraday_ehci_get_portsc_register, +}; + /* * Create the appropriate control structures to manage * a new EHCI host controller. @@ -43,6 +96,7 @@ int ehci_hcd_init(int index, enum usb_init_type init,
if (index < 0 || index >= ARRAY_SIZE(base_list)) return -1; + ehci_set_controller_priv(index, NULL, &faraday_ehci_ops); regs = (void __iomem *)base_list[index]; hccr = (struct ehci_hccr *)®s->usb.hccr; hcor = (struct ehci_hcor *)®s->usb.hcor; @@ -87,62 +141,3 @@ int ehci_hcd_stop(int index) { return 0; } - -/* - * This ehci_set_usbmode() overrides the weak function - * in "ehci-hcd.c". - */ -void ehci_set_usbmode(struct ehci_ctrl *ctrl) -{ - /* nothing needs to be done */ -} - -/* - * This ehci_get_port_speed() overrides the weak function - * in "ehci-hcd.c". - */ -int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) -{ - int spd, ret = PORTSC_PSPD_HS; - union ehci_faraday_regs *regs; - - ret = (void __iomem *)((ulong)ctrl->hcor - 0x10); - if (ehci_is_fotg2xx(regs)) - spd = OTGCSR_SPD(readl(®s->otg.otgcsr)); - else - spd = BMCSR_SPD(readl(®s->usb.bmcsr)); - - switch (spd) { - case 0: /* full speed */ - ret = PORTSC_PSPD_FS; - break; - case 1: /* low speed */ - ret = PORTSC_PSPD_LS; - break; - case 2: /* high speed */ - ret = PORTSC_PSPD_HS; - break; - default: - printf("ehci-faraday: invalid device speed\n"); - break; - } - - return ret; -} - -/* - * This ehci_get_portsc_register() overrides the weak function - * in "ehci-hcd.c". - */ -uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port) -{ - /* Faraday EHCI has one and only one portsc register */ - if (port) { - /* Printing the message would cause a scan failure! */ - debug("The request port(%d) is not configured\n", port); - return NULL; - } - - /* Faraday EHCI PORTSC register offset is 0x20 from hcor */ - return (uint32_t *)((uint8_t *)ctrl->hcor + 0x20); -} diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 3fa6fe2..82f7318 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -124,12 +124,12 @@ static struct ehci_ctrl *ehci_get_ctrl(struct usb_device *udev) return udev->controller; }
-__weak int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) +static int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) { return PORTSC_PSPD(reg); }
-__weak void ehci_set_usbmode(struct ehci_ctrl *ctrl) +static void ehci_set_usbmode(struct ehci_ctrl *ctrl) { uint32_t tmp; uint32_t *reg_ptr; @@ -143,13 +143,13 @@ __weak void ehci_set_usbmode(struct ehci_ctrl *ctrl) ehci_writel(reg_ptr, tmp); }
-__weak void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, +static void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, uint32_t *reg) { mdelay(50); }
-__weak uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port) +static uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port) { if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { /* Printing the message would cause a scan failure! */ @@ -178,6 +178,7 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
static int ehci_reset(int index) { + struct ehci_ctrl *ctrl = &ehcic[index]; uint32_t cmd; int ret = 0;
@@ -192,7 +193,7 @@ static int ehci_reset(int index) }
if (ehci_is_TDI()) - ehci_set_usbmode(&ehcic[index]); + ctrl->ops.set_usb_mode(&ehcic[index]);
#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning); @@ -691,7 +692,7 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): - status_reg = ehci_get_portsc_register(ctrl, port - 1); + status_reg = ctrl->ops.get_portsc_register(ctrl, port - 1); if (!status_reg) return -1; break; @@ -786,7 +787,7 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
if (ehci_is_TDI()) { - switch (ehci_get_port_speed(ctrl, reg)) { + switch (ctrl->ops.get_port_speed(ctrl, reg)) { case PORTSC_PSPD_FS: break; case PORTSC_PSPD_LS: @@ -848,7 +849,7 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, * usb 2.0 specification say 50 ms resets on * root */ - ehci_powerup_fixup(ctrl, status_reg, ®); + ctrl->ops.powerup_fixup(ctrl, status_reg, ®);
ehci_writel(status_reg, reg & ~EHCI_PS_PR); /* @@ -935,9 +936,37 @@ unknown: return -1; }
-void ehci_set_controller_priv(int index, void *priv) +const struct ehci_ops default_ehci_ops = { + .set_usb_mode = ehci_set_usbmode, + .get_port_speed = ehci_get_port_speed, + .powerup_fixup = ehci_powerup_fixup, + .get_portsc_register = ehci_get_portsc_register, +}; + +static void ehci_setup_ops(struct ehci_ctrl *ctrl, const struct ehci_ops *ops) +{ + if (!ops) { + ctrl->ops = default_ehci_ops; + } else { + ctrl->ops = *ops; + if (!ctrl->ops.set_usb_mode) + ctrl->ops.set_usb_mode = ehci_set_usbmode; + if (!ctrl->ops.get_port_speed) + ctrl->ops.get_port_speed = ehci_get_port_speed; + if (!ctrl->ops.powerup_fixup) + ctrl->ops.powerup_fixup = ehci_powerup_fixup; + if (!ctrl->ops.get_portsc_register) + ctrl->ops.get_portsc_register = + ehci_get_portsc_register; + } +} + +void ehci_set_controller_priv(int index, void *priv, const struct ehci_ops *ops) { - ehcic[index].priv = priv; + struct ehci_ctrl *ctrl = &ehcic[index]; + + ctrl->priv = priv; + ehci_setup_ops(ctrl, ops); }
void *ehci_get_controller_priv(int index) diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 7566c61..d319962 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -218,11 +218,23 @@ void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) { }
+__weak void mx5_ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg) +{ + mdelay(50); +} + +static const struct ehci_ops mx5_ehci_ops = { + .powerup_fixup = mx5_ehci_powerup_fixup, +}; + int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct ehci_hcor **hcor) { struct usb_ehci *ehci;
+ /* The only user for this is efikamx-usb */ + ehci_set_controller_priv(index, NULL, &mx5_ehci_ops); set_usboh3_clk(); enable_usboh3_clk(true); set_usb_phy_clk(); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6054b2b..38333c7 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -193,11 +193,9 @@ static struct fdt_usb_controller fdt_usb_controllers[USB_CTRL_COUNT] = { * A known hardware issue where Connect Status Change bit of PORTSC register * of USB1 controller will be set after Port Reset. * We have to clear it in order for later device enumeration to proceed. - * This ehci_powerup_fixup overrides the weak function ehci_powerup_fixup - * in "ehci-hcd.c". */ -void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, - uint32_t *reg) +static void tegra_ehci_powerup_fixup(struct ehci_ctrl *ctrl, + uint32_t *status_reg, uint32_t *reg) { struct fdt_usb *config = ctrl->priv; struct fdt_usb_controller *controller; @@ -215,11 +213,7 @@ void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, *reg |= EHCI_PS_CSC; }
-/* - * This ehci_set_usbmode overrides the weak function ehci_set_usbmode - * in "ehci-hcd.c". - */ -void ehci_set_usbmode(struct ehci_ctrl *ctrl) +static void tegra_ehci_set_usbmode(struct ehci_ctrl *ctrl) { struct fdt_usb *config = ctrl->priv; struct usb_ctlr *usbctlr; @@ -232,11 +226,7 @@ void ehci_set_usbmode(struct ehci_ctrl *ctrl) ehci_writel(&usbctlr->usb_mode, tmp); }
-/* - * This ehci_get_port_speed overrides the weak function ehci_get_port_speed - * in "ehci-hcd.c". - */ -int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) +static int tegra_ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) { struct fdt_usb *config = ctrl->priv; struct fdt_usb_controller *controller; @@ -714,6 +704,12 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) return 0; }
+static const struct ehci_ops tegra_ehci_ops = { + .set_usb_mode = tegra_ehci_set_usbmode, + .get_port_speed = tegra_ehci_get_port_speed, + .powerup_fixup = tegra_ehci_powerup_fixup, +}; + /* * process_usb_nodes() - Process a list of USB nodes, adding them to our list * of USB ports. @@ -805,7 +801,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, return -1;
config = &port[index]; - ehci_set_controller_priv(index, config); + ehci_set_controller_priv(index, config, &tegra_ehci_ops);
switch (init) { case USB_INIT_HOST: diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2ca111a..cc23e1f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -244,6 +244,16 @@ enum { EHCI_TWEAK_NO_INIT_CF = 1 << 0, };
+struct ehci_ctrl; + +struct ehci_ops { + void (*set_usb_mode)(struct ehci_ctrl *ctrl); + int (*get_port_speed)(struct ehci_ctrl *ctrl, uint32_t reg); + void (*powerup_fixup)(struct ehci_ctrl *ctrl, uint32_t *status_reg, + uint32_t *reg); + uint32_t *(*get_portsc_register)(struct ehci_ctrl *ctrl, int port); +}; + struct ehci_ctrl { struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ struct ehci_hcor *hcor; @@ -254,27 +264,24 @@ struct ehci_ctrl { uint32_t *periodic_list; int periodic_schedules; int ntds; + struct ehci_ops ops; void *priv; /* client's private data */ };
-/* Weak functions that drivers can override */ -int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg); -void ehci_set_usbmode(struct ehci_ctrl *ctrl); -void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg, - uint32_t *reg); -uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port); - /** - * ehci_set_controller_priv() - Set up private data for the controller + * ehci_set_controller_info() - Set up private data for the controller * * This function can be called in ehci_hcd_init() to tell the EHCI layer * about the controller's private data pointer. Then in the above functions - * this can be accessed given the struct ehci_ctrl pointer. + * this can be accessed given the struct ehci_ctrl pointer. Also special + * EHCI operation methods can be provided if required * * @index: Controller number to set * @priv: Controller pointer + * @ops: Controller operations, or NULL to use default */ -void ehci_set_controller_priv(int index, void *priv); +void ehci_set_controller_priv(int index, void *priv, + const struct ehci_ops *ops);
/** * ehci_get_controller_priv() - Get controller private data

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
These are a pain with driver model because we might have different EHCI drivers which want to implement them differently. Now that they use consistent function signatures, we can in good conscience move them to a struct.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
board/genesi/mx51_efikamx/efikamx-usb.c | 5 +- drivers/usb/host/ehci-faraday.c | 113 +++++++++++++++----------------- drivers/usb/host/ehci-hcd.c | 49 +++++++++++--- drivers/usb/host/ehci-mx5.c | 12 ++++ drivers/usb/host/ehci-tegra.c | 26 ++++---- drivers/usb/host/ehci.h | 27 +++++--- 6 files changed, 136 insertions(+), 96 deletions(-)
Applied to u-boot-dm/next.

The index cannot be used with driver model, and isn't needed anyway. Change the parameter to a pointer.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 82f7318..86082a9 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -176,16 +176,15 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) return -1; }
-static int ehci_reset(int index) +static int ehci_reset(struct ehci_ctrl *ctrl) { - struct ehci_ctrl *ctrl = &ehcic[index]; uint32_t cmd; int ret = 0;
- cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); cmd = (cmd & ~CMD_RUN) | CMD_RESET; - ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); - ret = handshake((uint32_t *)&ehcic[index].hcor->or_usbcmd, + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&ctrl->hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000); if (ret < 0) { printf("EHCI fail to reset\n"); @@ -193,13 +192,13 @@ static int ehci_reset(int index) }
if (ehci_is_TDI()) - ctrl->ops.set_usb_mode(&ehcic[index]); + ctrl->ops.set_usb_mode(ctrl);
#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH - cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning); + cmd = ehci_readl(&ctrl->hcor->or_txfilltuning); cmd &= ~TXFIFO_THRESH_MASK; cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH); - ehci_writel(&ehcic[index].hcor->or_txfilltuning, cmd); + ehci_writel(&ctrl->hcor->or_txfilltuning, cmd); #endif out: return ret; @@ -1102,7 +1101,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) goto done;
/* EHCI spec section 4.1 */ - if (ehci_reset(index)) + if (ehci_reset(ctrl)) return -1;
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
The index cannot be used with driver model, and isn't needed anyway. Change the parameter to a pointer.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
Applied to u-boot-dm/next.

Add a way for EHCI controller drivers to support driver model. Drivers can call ehci_register() to register themselves in their probe() methods.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 125 ++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/ehci.h | 6 +++ 2 files changed, 127 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 86082a9..227aa83 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -21,6 +21,7 @@ * MA 02111-1307 USA */ #include <common.h> +#include <dm.h> #include <errno.h> #include <asm/byteorder.h> #include <asm/unaligned.h> @@ -42,7 +43,9 @@ */ #define HCHALT_TIMEOUT (8 * 1000)
+#ifndef CONFIG_DM_USB static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; +#endif
#define ALIGN_END_ADDR(type, ptr, size) \ ((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN)) @@ -121,7 +124,18 @@ static struct descriptor {
static struct ehci_ctrl *ehci_get_ctrl(struct usb_device *udev) { +#ifdef CONFIG_DM_USB + struct udevice *dev; + + /* Find the USB controller */ + for (dev = udev->dev; + device_get_uclass_id(dev) != UCLASS_USB; + dev = dev->parent) + ; + return dev_get_priv(dev); +#else return udev->controller; +#endif }
static int ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg) @@ -281,12 +295,13 @@ static inline u8 ehci_encode_speed(enum usb_device_speed speed) return QH_FULL_SPEED; }
-static void ehci_update_endpt2_dev_n_port(struct usb_device *dev, +static void ehci_update_endpt2_dev_n_port(struct usb_device *udev, struct QH *qh) { struct usb_device *ttdev; + int parent_devnum;
- if (dev->speed != USB_SPEED_LOW && dev->speed != USB_SPEED_FULL) + if (udev->speed != USB_SPEED_LOW && udev->speed != USB_SPEED_FULL) return;
/* @@ -294,14 +309,35 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *dev, * the tt, so of the first upstream usb-2 hub, there may be usb-1 hubs * in the tree before that one! */ - ttdev = dev; +#ifdef CONFIG_DM_USB + struct udevice *parent; + + for (ttdev = udev; ; ) { + struct udevice *dev = ttdev->dev; + + if (dev->parent && + device_get_uclass_id(dev->parent) == UCLASS_USB_HUB) + parent = dev->parent; + else + parent = NULL; + if (!parent) + return; + ttdev = dev_get_parentdata(parent); + if (!ttdev->speed != USB_SPEED_HIGH) + break; + } + parent_devnum = ttdev->devnum; +#else + ttdev = udev; while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH) ttdev = ttdev->parent; if (!ttdev->parent) return; + parent_devnum = ttdev->parent->devnum; +#endif
qh->qh_endpt2 |= cpu_to_hc32(QH_ENDPT2_PORTNUM(ttdev->portnr) | - QH_ENDPT2_HUBADDR(ttdev->parent->devnum)); + QH_ENDPT2_HUBADDR(parent_devnum)); }
static int @@ -960,6 +996,7 @@ static void ehci_setup_ops(struct ehci_ctrl *ctrl, const struct ehci_ops *ops) } }
+#ifndef CONFIG_DM_USB void ehci_set_controller_priv(int index, void *priv, const struct ehci_ops *ops) { struct ehci_ctrl *ctrl = &ehcic[index]; @@ -972,6 +1009,7 @@ void *ehci_get_controller_priv(int index) { return ehcic[index].priv; } +#endif
static int ehci_common_init(struct ehci_ctrl *ctrl, uint tweaks) { @@ -1082,6 +1120,7 @@ static int ehci_common_init(struct ehci_ctrl *ctrl, uint tweaks) return 0; }
+#ifndef CONFIG_DM_USB int usb_lowlevel_stop(int index) { ehci_shutdown(&ehcic[index]); @@ -1121,6 +1160,7 @@ done: *controller = &ehcic[index]; return 0; } +#endif
static int _ehci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length) @@ -1480,6 +1520,7 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe, return result; }
+#ifndef CONFIG_DM_USB int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length) { @@ -1497,3 +1538,79 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, { return _ehci_submit_int_msg(dev, pipe, buffer, length, interval); } +#endif + +#ifdef CONFIG_DM_USB +static int ehci_submit_control_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup) +{ + debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__, + dev->name, udev, udev->dev->name, udev->portnr); + + return _ehci_submit_control_msg(udev, pipe, buffer, length, setup); +} + +static int ehci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _ehci_submit_bulk_msg(udev, pipe, buffer, length); +} + +static int ehci_submit_int_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _ehci_submit_int_msg(udev, pipe, buffer, length, interval); +} + +int ehci_register(struct udevice *dev, struct ehci_hccr *hccr, + struct ehci_hcor *hcor, const struct ehci_ops *ops, + uint tweaks, enum usb_init_type init) +{ + struct ehci_ctrl *ctrl = dev_get_priv(dev); + int ret; + + debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p, init=%d\n", __func__, + dev->name, ctrl, hccr, hcor, init); + + ehci_setup_ops(ctrl, ops); + ctrl->hccr = hccr; + ctrl->hcor = hcor; + ctrl->priv = ctrl; + + if (init == USB_INIT_DEVICE) + goto done; + ret = ehci_reset(ctrl); + if (ret) + goto err; + + ret = ehci_common_init(ctrl, tweaks); + if (ret) + goto err; +done: + return 0; +err: + free(ctrl); + debug("%s: failed, ret=%d\n", __func__, ret); + return ret; +} + +int ehci_deregister(struct udevice *dev) +{ + struct ehci_ctrl *ctrl = dev_get_priv(dev); + + ehci_shutdown(ctrl); + + return 0; +} + +struct dm_usb_ops ehci_usb_ops = { + .control = ehci_submit_control_msg, + .bulk = ehci_submit_bulk_msg, + .interrupt = ehci_submit_int_msg, +}; + +#endif diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index cc23e1f..774282d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -296,4 +296,10 @@ int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, struct ehci_hcor **hcor); int ehci_hcd_stop(int index);
+int ehci_register(struct udevice *dev, struct ehci_hccr *hccr, + struct ehci_hcor *hcor, const struct ehci_ops *ops, + uint tweaks, enum usb_init_type init); +int ehci_deregister(struct udevice *dev); +extern struct dm_usb_ops ehci_usb_ops; + #endif /* USB_EHCI_H */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Add a way for EHCI controller drivers to support driver model. Drivers can call ehci_register() to register themselves in their probe() methods.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-hcd.c | 125 ++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/ehci.h | 6 +++ 2 files changed, 127 insertions(+), 4 deletions(-)
Applied to u-boot-dm/next.

USB devices in U-Boot are currently probed only after all devices have been enumerated. Each type of device is probed by custom code, e.g.:
- USB storage - Keyboard - Ethernet
With driver model this approach doesn't work very well. We could build a picture of the bus and then go back and add the devices later, but this means that the data structures are incomplete for quite a while. It also does not follow the model of being able to bind a device when we discover it.
We would prefer to have devices automatically be bound as the device is enumerated. This allows us to attach drivers to particular USB classes or product/vendor IDs. This is the method used by Linux.
Add the required #defines from Linux, a way of declaring a USB driver and the logic to locate the correct driver given the USB device's descriptors.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/usb-uclass.c | 206 +++++++++++++++++++++++++++++++++++++++++- include/usb.h | 107 ++++++++++++++++++++++ 2 files changed, 311 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 22dcd14..a86e905 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -2,6 +2,8 @@ * (C) Copyright 2015 Google, Inc * Written by Simon Glass sjg@chromium.org * + * usb_match_device() modified from Linux kernel v4.0. + * * SPDX-License-Identifier: GPL-2.0+ */
@@ -247,6 +249,179 @@ int usb_legacy_port_reset(struct usb_device *parent, int portnr) return usb_port_reset(parent, portnr); }
+/* returns 0 if no match, 1 if match */ +int usb_match_device(const struct usb_device_descriptor *desc, + const struct usb_device_id *id) +{ + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + id->idVendor != le16_to_cpu(desc->idVendor)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->idProduct != le16_to_cpu(desc->idProduct)) + return 0; + + /* No need to test id->bcdDevice_lo != 0, since 0 is never + greater than any unsigned number. */ + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > le16_to_cpu(desc->bcdDevice))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < le16_to_cpu(desc->bcdDevice))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != desc->bDeviceClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass != desc->bDeviceSubClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != desc->bDeviceProtocol)) + return 0; + + return 1; +} + +/* returns 0 if no match, 1 if match */ +int usb_match_one_id_intf(const struct usb_device_descriptor *desc, + const struct usb_interface_descriptor *int_desc, + const struct usb_device_id *id) +{ + /* The interface class, subclass, protocol and number should never be + * checked for a match if the device class is Vendor Specific, + * unless the match record specifies the Vendor ID. */ + if (desc->bDeviceClass == USB_CLASS_VENDOR_SPEC && + !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL | + USB_DEVICE_ID_MATCH_INT_NUMBER))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && + (id->bInterfaceClass != int_desc->bInterfaceClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && + (id->bInterfaceSubClass != int_desc->bInterfaceSubClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && + (id->bInterfaceProtocol != int_desc->bInterfaceProtocol)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) && + (id->bInterfaceNumber != int_desc->bInterfaceNumber)) + return 0; + + return 1; +} + +/* returns 0 if no match, 1 if match */ +int usb_match_one_id(struct usb_device_descriptor *desc, + struct usb_interface_descriptor *int_desc, + const struct usb_device_id *id) +{ + if (!usb_match_device(desc, id)) + return 0; + + return usb_match_one_id_intf(desc, int_desc, id); +} + +/** + * usb_find_and_bind_driver() - Find and bind the right USB driver + * + * This only looks at certain fields in the descriptor. + */ +static int usb_find_and_bind_driver(struct udevice *parent, + struct usb_device_descriptor *desc, + struct usb_interface_descriptor *iface, + int bus_seq, int devnum, + struct udevice **devp) +{ + struct usb_driver_entry *start, *entry; + int n_ents; + int ret; + char name[30], *str; + + *devp = NULL; + debug("%s: Searching for driver\n", __func__); + start = ll_entry_start(struct usb_driver_entry, usb_driver_entry); + n_ents = ll_entry_count(struct usb_driver_entry, usb_driver_entry); + for (entry = start; entry != start + n_ents; entry++) { + const struct usb_device_id *id; + struct udevice *dev; + const struct driver *drv; + struct usb_dev_platdata *plat; + + for (id = entry->match; id->match_flags; id++) { + if (!usb_match_one_id(desc, iface, id)) + continue; + + drv = entry->driver; + /* + * We could pass the descriptor to the driver as + * platdata (instead of NULL) and allow its bind() + * method to return -ENOENT if it doesn't support this + * device. That way we could continue the search to + * find another driver. For now this doesn't seem + * necesssary, so just bind the first match. + */ + ret = device_bind(parent, drv, drv->name, NULL, -1, + &dev); + if (ret) + goto error; + debug("%s: Match found: %s\n", __func__, drv->name); + dev->driver_data = id->driver_info; + plat = dev_get_parent_platdata(dev); + plat->id = *id; + *devp = dev; + return 0; + } + } + + ret = -ENOENT; +error: + debug("%s: No match found: %d\n", __func__, ret); + return ret; +} + +/** + * usb_find_child() - Find an existing device which matches our needs + * + * + */ +static int usb_find_child(struct udevice *parent, + struct usb_device_descriptor *desc, + struct usb_interface_descriptor *iface, + struct udevice **devp) +{ + struct udevice *dev; + + *devp = NULL; + for (device_find_first_child(parent, &dev); + dev; + device_find_next_child(&dev)) { + struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); + + /* If this device is already in use, skip it */ + if (device_active(dev)) + continue; + debug(" %s: name='%s', plat=%d, desc=%d\n", __func__, + dev->name, plat->id.bDeviceClass, desc->bDeviceClass); + if (usb_match_one_id(desc, iface, &plat->id)) { + *devp = dev; + return 0; + } + } + + return -ENOENT; +} + int usb_scan_device(struct udevice *parent, int port, enum usb_device_speed speed, struct udevice **devp) { @@ -307,9 +482,36 @@ int usb_scan_device(struct udevice *parent, int port, return ret; ret = usb_find_child(parent, &udev->descriptor, iface, &dev); debug("** usb_find_child returns %d\n", ret); + if (ret) { + if (ret != -ENOENT) + return ret; + ret = usb_find_and_bind_driver(parent, &udev->descriptor, iface, + udev->controller_dev->seq, + udev->devnum, &dev); + if (ret) + return ret; + created = true; + } + plat = dev_get_parent_platdata(dev); + debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat); + plat->devnum = udev->devnum; + plat->speed = udev->speed; + plat->slot_id = udev->slot_id; + plat->portnr = port; + debug("** device '%s': stashing slot_id=%d\n", dev->name, + plat->slot_id); + priv->next_addr++; + ret = device_probe(dev); + if (ret) { + debug("%s: Device '%s' probe failed\n", __func__, dev->name); + priv->next_addr--; + if (created) + device_unbind(dev); + return ret; + } + *devp = dev;
- /* TODO: Find a suitable driver and create the device */ - return -ENOENT; + return 0; }
int usb_child_post_bind(struct udevice *dev) diff --git a/include/usb.h b/include/usb.h index badb287..799b0c3 100644 --- a/include/usb.h +++ b/include/usb.h @@ -412,6 +412,113 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); ((usb_pipeendpoint(pipe) * 2) - \ (usb_pipein(pipe) ? 0 : 1))
+/** + * struct usb_device_id - identifies USB devices for probing and hotplugging + * @match_flags: Bit mask controlling which of the other fields are used to + * match against new devices. Any field except for driver_info may be + * used, although some only make sense in conjunction with other fields. + * This is usually set by a USB_DEVICE_*() macro, which sets all + * other fields in this structure except for driver_info. + * @idVendor: USB vendor ID for a device; numbers are assigned + * by the USB forum to its members. + * @idProduct: Vendor-assigned product ID. + * @bcdDevice_lo: Low end of range of vendor-assigned product version numbers. + * This is also used to identify individual product versions, for + * a range consisting of a single device. + * @bcdDevice_hi: High end of version number range. The range of product + * versions is inclusive. + * @bDeviceClass: Class of device; numbers are assigned + * by the USB forum. Products may choose to implement classes, + * or be vendor-specific. Device classes specify behavior of all + * the interfaces on a device. + * @bDeviceSubClass: Subclass of device; associated with bDeviceClass. + * @bDeviceProtocol: Protocol of device; associated with bDeviceClass. + * @bInterfaceClass: Class of interface; numbers are assigned + * by the USB forum. Products may choose to implement classes, + * or be vendor-specific. Interface classes specify behavior only + * of a given interface; other interfaces may support other classes. + * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass. + * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass. + * @bInterfaceNumber: Number of interface; composite devices may use + * fixed interface numbers to differentiate between vendor-specific + * interfaces. + * @driver_info: Holds information used by the driver. Usually it holds + * a pointer to a descriptor understood by the driver, or perhaps + * device flags. + * + * In most cases, drivers will create a table of device IDs by using + * USB_DEVICE(), or similar macros designed for that purpose. + * They will then export it to userspace using MODULE_DEVICE_TABLE(), + * and provide it to the USB core through their usb_driver structure. + * + * See the usb_match_id() function for information about how matches are + * performed. Briefly, you will normally use one of several macros to help + * construct these entries. Each entry you provide will either identify + * one or more specific products, or will identify a class of products + * which have agreed to behave the same. You should put the more specific + * matches towards the beginning of your table, so that driver_info can + * record quirks of specific products. + */ +struct usb_device_id { + /* which fields to match against? */ + u16 match_flags; + + /* Used for product specific matches; range is inclusive */ + u16 idVendor; + u16 idProduct; + u16 bcdDevice_lo; + u16 bcdDevice_hi; + + /* Used for device class matches */ + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + + /* Used for interface class matches */ + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + + /* Used for vendor-specific interface matches */ + u8 bInterfaceNumber; + + /* not matched against */ + ulong driver_info; +}; + +/* Some useful macros to use to create struct usb_device_id */ +#define USB_DEVICE_ID_MATCH_VENDOR 0x0001 +#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 +#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 +#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 +#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 +#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 +#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 +#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 +#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 +#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 +#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400 + +/* Match anything, indicates this is a valid entry even if everything is 0 */ +#define USB_DEVICE_ID_MATCH_NONE 0x0800 +#define USB_DEVICE_ID_MATCH_ALL 0x07ff + +/** + * struct usb_driver_entry - Matches a driver to its usb_device_ids + * @compatible: Compatible string + * @data: Data for this compatible string + */ +struct usb_driver_entry { + struct driver *driver; + const struct usb_device_id *match; +}; + +#define USB_DEVICE(__name, __match) \ + ll_entry_declare(struct usb_driver_entry, __name, usb_driver_entry) = {\ + .driver = llsym(struct driver, __name, driver), \ + .match = __match, \ + } + /************************************************************************* * Hub Stuff */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
USB devices in U-Boot are currently probed only after all devices have been enumerated. Each type of device is probed by custom code, e.g.:
- USB storage
- Keyboard
- Ethernet
With driver model this approach doesn't work very well. We could build a picture of the bus and then go back and add the devices later, but this means that the data structures are incomplete for quite a while. It also does not follow the model of being able to bind a device when we discover it.
We would prefer to have devices automatically be bound as the device is enumerated. This allows us to attach drivers to particular USB classes or product/vendor IDs. This is the method used by Linux.
Add the required #defines from Linux, a way of declaring a USB driver and the logic to locate the correct driver given the USB device's descriptors.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/usb-uclass.c | 206 +++++++++++++++++++++++++++++++++++++++++- include/usb.h | 107 ++++++++++++++++++++++ 2 files changed, 311 insertions(+), 2 deletions(-)
Applied to u-boot-dm/next.

At present USB devices with no driver model driver cannot be seen in the device list, and we fail to set them up correctly. This means they cannot be used.
While having real drivers that support driver model for all USB devices is the eventual goal, we are not there yet.
As a stop-gap, add a generic USB driver which is bound when we do not have a real driver. This allows the device to be set up and shown on the bus. It also allows ad-hoc code (such as usb_ether) to find these devices and set them up.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/usb-uclass.c | 18 +++++++++++++++++- include/dm/uclass-id.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index a86e905..fa5f14e 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -384,7 +384,13 @@ static int usb_find_and_bind_driver(struct udevice *parent, } }
- ret = -ENOENT; + /* Bind a generic driver so that the device can be used */ + snprintf(name, sizeof(name), "generic_bus_%x_dev_%x", bus_seq, devnum); + str = strdup(name); + if (!str) + return -ENOMEM; + ret = device_bind_driver(parent, "usb_dev_generic_drv", str, devp); + error: debug("%s: No match found: %d\n", __func__, ret); return ret; @@ -592,3 +598,13 @@ UCLASS_DRIVER(usb) = { .child_pre_probe = usb_child_pre_probe, .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), }; + +UCLASS_DRIVER(usb_dev_generic) = { + .id = UCLASS_USB_DEV_GENERIC, + .name = "usb_dev_generic", +}; + +U_BOOT_DRIVER(usb_dev_generic_drv) = { + .id = UCLASS_USB_DEV_GENERIC, + .name = "usb_dev_generic_drv", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index acec938..42a6f1f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -41,6 +41,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_USB, /* USB bus */ UCLASS_USB_HUB, /* USB hub */ + UCLASS_USB_DEV_GENERIC, /* USB generic device */
UCLASS_COUNT, UCLASS_INVALID = -1,

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
At present USB devices with no driver model driver cannot be seen in the device list, and we fail to set them up correctly. This means they cannot be used.
While having real drivers that support driver model for all USB devices is the eventual goal, we are not there yet.
As a stop-gap, add a generic USB driver which is bound when we do not have a real driver. This allows the device to be set up and shown on the bus. It also allows ad-hoc code (such as usb_ether) to find these devices and set them up.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/usb-uclass.c | 18 +++++++++++++++++- include/dm/uclass-id.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-)
Applied to u-boot-dm/next.

Some controllers support OTG (on-the-go) where they can operate as either host or device. The gadget layer in U-Boot supports this.
While this layer does not interact with driver model, we can provide a function which sets up the controller in the correct way. This way the code at least builds (although it likely will not work).
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/gadget/ci_udc.c | 4 ++++ drivers/usb/host/usb-uclass.c | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index b0ef35e..0afc987 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -883,7 +883,11 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) return -EINVAL;
+#ifdef CONFIG_DM_USB + ret = usb_setup_ehci_gadget(&controller.ctrl); +#else ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl); +#endif if (ret) return ret;
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index fa5f14e..29ef5d9 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -249,6 +249,30 @@ int usb_legacy_port_reset(struct usb_device *parent, int portnr) return usb_port_reset(parent, portnr); }
+int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) +{ + struct usb_platdata *plat; + struct udevice *dev; + int ret; + + /* Find the old device and remove it */ + ret = uclass_find_device_by_seq(UCLASS_USB, 0, true, &dev); + if (ret) + return ret; + ret = device_remove(dev); + if (ret) + return ret; + + plat = dev_get_platdata(dev); + plat->init_type = USB_INIT_DEVICE; + ret = device_probe(dev); + if (ret) + return ret; + *ctlrp = dev_get_priv(dev); + + return 0; +} + /* returns 0 if no match, 1 if match */ int usb_match_device(const struct usb_device_descriptor *desc, const struct usb_device_id *id)

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Some controllers support OTG (on-the-go) where they can operate as either host or device. The gadget layer in U-Boot supports this.
While this layer does not interact with driver model, we can provide a function which sets up the controller in the correct way. This way the code at least builds (although it likely will not work).
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/gadget/ci_udc.c | 4 ++++ drivers/usb/host/usb-uclass.c | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+)
Applied to u-boot-dm/next.

Before adding driver model support, split out code from this over-long function.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_kbd.c | 89 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 38 deletions(-)
diff --git a/common/usb_kbd.c b/common/usb_kbd.c index ecc3085..e02529f 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -471,59 +471,72 @@ static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) return 1; }
+static int probe_usb_keyboard(struct usb_device *dev) +{ + char *stdinname; + struct stdio_dev usb_kbd_dev; + int error; + + /* Try probing the keyboard */ + if (usb_kbd_probe(dev, 0) != 1) + return -ENOENT; + + /* Register the keyboard */ + debug("USB KBD: register.\n"); + memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); + strcpy(usb_kbd_dev.name, DEVNAME); + usb_kbd_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + usb_kbd_dev.getc = usb_kbd_getc; + usb_kbd_dev.tstc = usb_kbd_testc; + usb_kbd_dev.priv = (void *)dev; + error = stdio_register(&usb_kbd_dev); + if (error) + return error; + + stdinname = getenv("stdin"); +#ifdef CONFIG_CONSOLE_MUX + error = iomux_doenv(stdin, stdinname); + if (error) + return error; +#else + /* Check if this is the standard input device. */ + if (strcmp(stdinname, DEVNAME)) + return 1; + + /* Reassign the console */ + if (overwrite_console()) + return 1; + + error = console_assign(stdin, DEVNAME); + if (error) + return error; +#endif + + return 0; +} + /* Search for keyboard and register it if found. */ int drv_usb_kbd_init(void) { - struct stdio_dev usb_kbd_dev; - struct usb_device *dev; - char *stdinname = getenv("stdin"); int error, i;
- /* Scan all USB Devices */ + debug("%s: Probing for keyboard\n", __func__);/* Scan all USB Devices */ for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + /* Get USB device. */ dev = usb_get_dev_index(i); if (!dev) - return -1; + break;
if (dev->devnum == -1) continue;
- /* Try probing the keyboard */ - if (usb_kbd_probe(dev, 0) != 1) - continue; - - /* Register the keyboard */ - debug("USB KBD: register.\n"); - memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); - strcpy(usb_kbd_dev.name, DEVNAME); - usb_kbd_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; - usb_kbd_dev.getc = usb_kbd_getc; - usb_kbd_dev.tstc = usb_kbd_testc; - usb_kbd_dev.priv = (void *)dev; - error = stdio_register(&usb_kbd_dev); - if (error) - return error; - -#ifdef CONFIG_CONSOLE_MUX - error = iomux_doenv(stdin, stdinname); - if (error) - return error; -#else - /* Check if this is the standard input device. */ - if (strcmp(stdinname, DEVNAME)) - return 1; - - /* Reassign the console */ - if (overwrite_console()) + error = probe_usb_keyboard(dev); + if (!error) return 1; - - error = console_assign(stdin, DEVNAME); - if (error) + if (error && error != -ENOENT) return error; -#endif - - return 1; }
/* No USB Keyboard found */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Before adding driver model support, split out code from this over-long function.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_kbd.c | 89 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 38 deletions(-)
Applied to u-boot-dm/next.

Allow USB keyboards to work with driver model. The main difference is that we can have multiple buses (each with its own device numbering) and each bus must be scanned.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
common/usb_kbd.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/common/usb_kbd.c b/common/usb_kbd.c index e02529f..24a1a56 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -8,6 +8,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <dm.h> #include <errno.h> #include <malloc.h> #include <stdio_dev.h> @@ -520,7 +521,37 @@ int drv_usb_kbd_init(void) { int error, i;
- debug("%s: Probing for keyboard\n", __func__);/* Scan all USB Devices */ + debug("%s: Probing for keyboard\n", __func__); +#ifdef CONFIG_DM_USB + /* + * TODO: We should add USB_DEVICE() declarations to each USB ethernet + * driver and then most of this file can be removed. + */ + struct udevice *bus; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_USB, &uc); + if (ret) + return ret; + uclass_foreach_dev(bus, uc) { + for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + + dev = usb_get_dev_index(bus, i); /* get device */ + debug("i=%d, %p\n", i, dev); + if (!dev) + break; /* no more devices available */ + + error = probe_usb_keyboard(dev); + if (!error) + return 1; + if (error && error != -ENOENT) + return error; + } /* for */ + } +#else + /* Scan all USB Devices */ for (i = 0; i < USB_MAX_DEVICE; i++) { struct usb_device *dev;
@@ -538,6 +569,7 @@ int drv_usb_kbd_init(void) if (error && error != -ENOENT) return error; } +#endif
/* No USB Keyboard found */ return -1;

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Allow USB keyboards to work with driver model. The main difference is that we can have multiple buses (each with its own device numbering) and each bus must be scanned.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
common/usb_kbd.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
Applied to u-boot-dm/next.

These are needed to enable the USB bus (although not sufficient since it still does not work).
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
arch/arm/dts/tegra124-nyan-big.dts | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm/dts/tegra124-nyan-big.dts b/arch/arm/dts/tegra124-nyan-big.dts index c1f35a0..9367193 100644 --- a/arch/arm/dts/tegra124-nyan-big.dts +++ b/arch/arm/dts/tegra124-nyan-big.dts @@ -230,6 +230,7 @@
usb@7d000000 { /* Rear external USB port. */ status = "okay"; + nvidia,vbus-gpio = <&gpio TEGRA_GPIO(N, 4) GPIO_ACTIVE_HIGH>; };
usb-phy@7d000000 { @@ -246,6 +247,7 @@
usb@7d008000 { /* Left external USB port. */ status = "okay"; + nvidia,vbus-gpio = <&gpio TEGRA_GPIO(N, 5) GPIO_ACTIVE_HIGH>; };
usb-phy@7d008000 {

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
These are needed to enable the USB bus (although not sufficient since it still does not work).
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
arch/arm/dts/tegra124-nyan-big.dts | 2 ++ 1 file changed, 2 insertions(+)
Applied to u-boot-dm/next.

This is needed for sandbox USB device emulation, so move it to a place where it can be found by things other than gadgets.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
include/linux/usb/ch9.h | 13 +++++++++++++ include/linux/usb/gadget.h | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index bd48704..10675b4 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -1002,4 +1002,17 @@ struct usb_set_sel_req { */ #define USB_SELF_POWER_VBUS_MAX_DRAW 100
+/** + * struct usb_string - wraps a C string and its USB id + * @id:the (nonzero) ID for this string + * @s:the string, in UTF-8 encoding + * + * If you're using usb_gadget_get_string(), use this to wrap a string + * together with its ID. + */ +struct usb_string { + u8 id; + const char *s; +}; + #endif /* __LINUX_USB_CH9_H */ diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 9bccd45..bd409bc 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -806,19 +806,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); /* utility to simplify dealing with string descriptors */
/** - * struct usb_string - wraps a C string and its USB id - * @id:the (nonzero) ID for this string - * @s:the string, in UTF-8 encoding - * - * If you're using usb_gadget_get_string(), use this to wrap a string - * together with its ID. - */ -struct usb_string { - u8 id; - const char *s; -}; - -/** * struct usb_gadget_strings - a set of USB strings in a given language * @language:identifies the strings' language (0x0409 for en-us) * @strings:array of strings with their ids

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This is needed for sandbox USB device emulation, so move it to a place where it can be found by things other than gadgets.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
include/linux/usb/ch9.h | 13 +++++++++++++ include/linux/usb/gadget.h | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-)
Applied to u-boot-dm/next.

With sandbox we want to be able to emulate USB devices so that we can test the USB stack. Add a uclass to support this. It implements the same operations as a normal USB device driver, but in this case passes them on to an emulation driver.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
Makefile | 1 + drivers/usb/Kconfig | 2 + drivers/usb/emul/Kconfig | 8 ++ drivers/usb/emul/Makefile | 8 ++ drivers/usb/emul/usb-emul-uclass.c | 263 +++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/usb.h | 64 ++++++++- 7 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/emul/Kconfig create mode 100644 drivers/usb/emul/Makefile create mode 100644 drivers/usb/emul/usb-emul-uclass.c
diff --git a/Makefile b/Makefile index 1b3ebe7..c389869 100644 --- a/Makefile +++ b/Makefile @@ -637,6 +637,7 @@ libs-y += drivers/spi/ libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/ libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/ libs-y += drivers/serial/ +libs-y += drivers/usb/emul/ libs-y += drivers/usb/eth/ libs-y += drivers/usb/gadget/ libs-y += drivers/usb/host/ diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index a4414ef..637ef3d 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -51,6 +51,8 @@ config DM_USB
source "drivers/usb/host/Kconfig"
+source "drivers/usb/emul/Kconfig" + config USB_STORAGE bool "USB Mass Storage support" ---help--- diff --git a/drivers/usb/emul/Kconfig b/drivers/usb/emul/Kconfig new file mode 100644 index 0000000..ae1ab23 --- /dev/null +++ b/drivers/usb/emul/Kconfig @@ -0,0 +1,8 @@ +config USB_EMUL + bool "Support for USB device emulation" + depends on DM_USB && SANDBOX + help + Since sandbox does not have access to a real USB bus, it is possible + to use device emulators instead. This allows testing of the USB + stack on sandbox without needing a real device, or any host machine + USB resources. diff --git a/drivers/usb/emul/Makefile b/drivers/usb/emul/Makefile new file mode 100644 index 0000000..f75bbd8 --- /dev/null +++ b/drivers/usb/emul/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2015 Google, Inc +# Written by Simon Glass sjg@chromium.org +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_USB_EMUL) += usb-emul-uclass.o diff --git a/drivers/usb/emul/usb-emul-uclass.c b/drivers/usb/emul/usb-emul-uclass.c new file mode 100644 index 0000000..205f2c5 --- /dev/null +++ b/drivers/usb/emul/usb-emul-uclass.c @@ -0,0 +1,263 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <usb.h> +#include <dm/root.h> +#include <dm/device-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int copy_to_unicode(char *buff, int length, const char *str) +{ + int ptr; + int i; + + if (length < 2) + return 0; + buff[1] = USB_DT_STRING; + for (ptr = 2, i = 0; ptr + 1 < length && *str; i++, ptr += 2) { + buff[ptr] = str[i]; + buff[ptr + 1] = 0; + } + buff[0] = ptr; + + return ptr; +} + +static int usb_emul_get_string(struct usb_string *strings, int index, + char *buff, int length) +{ + if (index == 0) { + char *desc = buff; + + desc[0] = 4; + desc[1] = USB_DT_STRING; + desc[2] = 0x09; + desc[3] = 0x14; + return 4; + } else if (strings) { + struct usb_string *ptr; + + for (ptr = strings; ptr->s; ptr++) { + if (ptr->id == index) + return copy_to_unicode(buff, length, ptr->s); + } + } + + return -EINVAL; +} + +static struct usb_generic_descriptor **find_descriptor( + struct usb_generic_descriptor **ptr, int type, int index) +{ + debug("%s: type=%x, index=%d\n", __func__, type, index); + for (; *ptr; ptr++) { + if ((*ptr)->bDescriptorType != type) + continue; + switch (type) { + case USB_DT_CONFIG: { + struct usb_config_descriptor *cdesc; + + cdesc = (struct usb_config_descriptor *)*ptr; + if (cdesc && cdesc->bConfigurationValue == index) + return ptr; + break; + } + default: + return ptr; + } + } + debug("%s: config ptr=%p\n", __func__, *ptr); + + return ptr; +} + +static int usb_emul_get_descriptor(struct usb_dev_platdata *plat, int value, + void *buffer, int length) +{ + struct usb_generic_descriptor **ptr; + int type = value >> 8; + int index = value & 0xff; + int upto, todo; + + debug("%s: type=%d, index=%d, plat=%p\n", __func__, type, index, plat); + if (type == USB_DT_STRING) { + return usb_emul_get_string(plat->strings, index, buffer, + length); + } + + ptr = find_descriptor((struct usb_generic_descriptor **)plat->desc_list, + type, index); + if (!ptr) { + debug("%s: Could not find descriptor type %d, index %d\n", + __func__, type, index); + return -ENOENT; + } + for (upto = 0; *ptr && upto < length; ptr++, upto += todo) { + todo = min(length - upto, (int)(*ptr)->bLength); + + memcpy(buffer + upto, *ptr, todo); + } + + return upto ? upto : length ? -EIO : 0; +} + +int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp) +{ + int devnum = usb_pipedevice(pipe); + struct udevice *dev; + struct uclass *uc; + int ret; + + *emulp = NULL; + ret = uclass_get(UCLASS_USB_EMUL, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct usb_dev_platdata *udev = dev_get_parent_platdata(dev); + + if (udev->devnum == devnum) { + debug("%s: Found emulator '%s', addr %d\n", __func__, + dev->name, udev->devnum); + *emulp = dev; + return 0; + } + } + + debug("%s: No emulator found, addr %d\n", __func__, devnum); + return -ENOENT; +} + +int usb_emul_control(struct udevice *emul, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup) +{ + struct dm_usb_ops *ops = usb_get_emul_ops(emul); + struct usb_dev_platdata *plat; + int ret; + + /* We permit getting the descriptor before we are probed */ + plat = dev_get_parent_platdata(emul); + if (!ops->control) + return -ENOSYS; + debug("%s: dev=%s\n", __func__, emul->name); + if (pipe == usb_rcvctrlpipe(udev, 0)) { + switch (setup->request) { + case USB_REQ_GET_DESCRIPTOR: { + return usb_emul_get_descriptor(plat, setup->value, + buffer, length); + } + default: + ret = device_probe(emul); + if (ret) + return ret; + return ops->control(emul, udev, pipe, buffer, length, + setup); + } + } else if (pipe == usb_snddefctrl(udev)) { + switch (setup->request) { + case USB_REQ_SET_ADDRESS: + debug(" ** set address %s %d\n", emul->name, + setup->value); + plat->devnum = setup->value; + return 0; + default: + debug("requestsend =%x\n", setup->request); + break; + } + } else if (pipe == usb_sndctrlpipe(udev, 0)) { + switch (setup->request) { + case USB_REQ_SET_CONFIGURATION: + plat->configno = setup->value; + return 0; + default: + ret = device_probe(emul); + if (ret) + return ret; + return ops->control(emul, udev, pipe, buffer, length, + setup); + } + } + debug("pipe=%lx\n", pipe); + + return -EIO; +} + +int usb_emul_bulk(struct udevice *emul, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + struct dm_usb_ops *ops = usb_get_emul_ops(emul); + int ret; + + /* We permit getting the descriptor before we are probed */ + if (!ops->bulk) + return -ENOSYS; + debug("%s: dev=%s\n", __func__, emul->name); + ret = device_probe(emul); + if (ret) + return ret; + return ops->bulk(emul, udev, pipe, buffer, length); +} + +int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, + struct usb_string *strings, void **desc_list) +{ + struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); + struct usb_generic_descriptor **ptr; + struct usb_config_descriptor *cdesc; + int upto; + + plat->strings = strings; + plat->desc_list = (struct usb_generic_descriptor **)desc_list; + + /* Fill in wTotalLength for each configuration descriptor */ + ptr = plat->desc_list; + for (cdesc = NULL, upto = 0; *ptr; upto += (*ptr)->bLength, ptr++) { + debug(" - upto=%d, type=%d\n", upto, (*ptr)->bDescriptorType); + if ((*ptr)->bDescriptorType == USB_DT_CONFIG) { + if (cdesc) { + cdesc->wTotalLength = upto; + debug("%s: config %d length %d\n", __func__, + cdesc->bConfigurationValue, + cdesc->bLength); + } + cdesc = (struct usb_config_descriptor *)*ptr; + upto = 0; + } + } + if (cdesc) { + cdesc->wTotalLength = upto; + debug("%s: config %d length %d\n", __func__, + cdesc->bConfigurationValue, cdesc->wTotalLength); + } + + return 0; +} + +int usb_emul_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +void usb_emul_reset(struct udevice *dev) +{ + struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); + + plat->devnum = 0; + plat->configno = 0; +} + +UCLASS_DRIVER(usb_emul) = { + .id = UCLASS_USB_EMUL, + .name = "usb_emul", + .post_bind = usb_emul_post_bind, + .per_child_auto_alloc_size = sizeof(struct usb_device), + .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 42a6f1f..2d0a145 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -21,6 +21,7 @@ enum uclass_id { UCLASS_SPI_EMUL, /* sandbox SPI device emulator */ UCLASS_I2C_EMUL, /* sandbox I2C device emulator */ UCLASS_PCI_EMUL, /* sandbox PCI device emulator */ + UCLASS_USB_EMUL, /* sandbox USB bus device emulator */ UCLASS_SIMPLE_BUS,
/* U-Boot uclasses start here */ diff --git a/include/usb.h b/include/usb.h index 799b0c3..b449074 100644 --- a/include/usb.h +++ b/include/usb.h @@ -179,7 +179,8 @@ enum usb_init_type { defined(CONFIG_USB_BLACKFIN) || defined(CONFIG_USB_AM35X) || \ defined(CONFIG_USB_MUSB_DSPS) || defined(CONFIG_USB_MUSB_AM35X) || \ defined(CONFIG_USB_MUSB_OMAP2PLUS) || defined(CONFIG_USB_MUSB_SUNXI) || \ - defined(CONFIG_USB_XHCI) || defined(CONFIG_USB_DWC2) + defined(CONFIG_USB_XHCI) || defined(CONFIG_USB_DWC2) || \ + defined(CONFIG_USB_EMUL)
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller); int usb_lowlevel_stop(int index); @@ -848,4 +849,65 @@ int usb_new_device(struct usb_device *dev);
int usb_alloc_device(struct usb_device *dev);
+/** + * usb_emul_setup_device() - Set up a new USB device emulation + * + * This is normally called when a new emulation device is bound. It tells + * the USB emulation uclass about the features of the emulator. + * + * @dev: Emulation device + * @maxpacketsize: Maximum packet size (e.g. PACKET_SIZE_64) + * @strings: List of USB string descriptors, terminated by a NULL + * entry + * @desc_list: List of points or USB descriptors, terminated by NULL. + * The first entry must be struct usb_device_descriptor, + * and others follow on after that. + * @return 0 if OK, -ve on error + */ +int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, + struct usb_string *strings, void **desc_list); + +/** + * usb_emul_control() - Send a control packet to an emulator + * + * @emul: Emulator device + * @udev: USB device (which the emulator is causing to appear) + * See struct dm_usb_ops for details on other parameters + * @return 0 if OK, -ve on error + */ +int usb_emul_control(struct udevice *emul, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup); + +/** + * usb_emul_bulk() - Send a bulk packet to an emulator + * + * @emul: Emulator device + * @udev: USB device (which the emulator is causing to appear) + * See struct dm_usb_ops for details on other parameters + * @return 0 if OK, -ve on error + */ +int usb_emul_bulk(struct udevice *emul, struct usb_device *udev, + unsigned long pipe, void *buffer, int length); + +/** + * usb_emul_find() - Find an emulator for a particular device + * + * Check @pipe to find a device number on bus @bus and return it. + * + * @bus: USB bus (controller) + * @pipe: Describes pipe being used, and includes the device number + * @emulp: Returns pointer to emulator, or NULL if not found + * @return 0 if found, -ve on error + */ +int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp); + +/** + * usb_emul_reset() - Reset all emulators ready for use + * + * Clear out any address information in the emulators and make then ready for + * a new USB scan + */ +void usb_emul_reset(struct udevice *dev); + #endif /*_USB_H_ */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
With sandbox we want to be able to emulate USB devices so that we can test the USB stack. Add a uclass to support this. It implements the same operations as a normal USB device driver, but in this case passes them on to an emulation driver.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
Makefile | 1 + drivers/usb/Kconfig | 2 + drivers/usb/emul/Kconfig | 8 ++ drivers/usb/emul/Makefile | 8 ++ drivers/usb/emul/usb-emul-uclass.c | 263 +++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/usb.h | 64 ++++++++- 7 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/emul/Kconfig create mode 100644 drivers/usb/emul/Makefile create mode 100644 drivers/usb/emul/usb-emul-uclass.c
Applied to u-boot-dm/next.

These devices must have their addresses removed ready for the next USB bus enumeration. Add this logic to usb_stop().
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/usb-uclass.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 29ef5d9..714bc0e 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -93,6 +93,17 @@ int usb_stop(void) err = ret; }
+#ifdef CONFIG_SANDBOX + struct udevice *dev; + + /* Reset all enulation devices */ + ret = uclass_get(UCLASS_USB_EMUL, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) + usb_emul_reset(dev); +#endif usb_stor_reset(); usb_hub_reset(); usb_started = 0;

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
These devices must have their addresses removed ready for the next USB bus enumeration. Add this logic to usb_stop().
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/usb-uclass.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
Applied to u-boot-dm/next.

This emulator supports USB enumeration and allows a local file to be provided as the contents of the emulated flash stick. U-Boot can then use the file as it would a normal device, with all access passing through the usb_stor layer and the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/emul/Makefile | 1 + drivers/usb/emul/sandbox_flash.c | 423 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 3 files changed, 425 insertions(+) create mode 100644 drivers/usb/emul/sandbox_flash.c
diff --git a/drivers/usb/emul/Makefile b/drivers/usb/emul/Makefile index f75bbd8..1d5acce 100644 --- a/drivers/usb/emul/Makefile +++ b/drivers/usb/emul/Makefile @@ -5,4 +5,5 @@ # SPDX-License-Identifier: GPL-2.0+ #
+obj-$(CONFIG_USB_EMUL) += sandbox_flash.o obj-$(CONFIG_USB_EMUL) += usb-emul-uclass.o diff --git a/drivers/usb/emul/sandbox_flash.c b/drivers/usb/emul/sandbox_flash.c new file mode 100644 index 0000000..6e0808d --- /dev/null +++ b/drivers/usb/emul/sandbox_flash.c @@ -0,0 +1,423 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <os.h> +#include <scsi.h> +#include <usb.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This driver emulates a flash stick using the UFI command specification and + * the BBB (bulk/bulk/bulk) protocol. It supports only a single logical unit + * number (LUN 0). + */ + +enum { + SANDBOX_FLASH_EP_OUT = 1, /* endpoints */ + SANDBOX_FLASH_EP_IN = 2, + SANDBOX_FLASH_BLOCK_LEN = 512, +}; + +enum cmd_phase { + PHASE_START, + PHASE_DATA, + PHASE_STATUS, +}; + +/** + * struct sandbox_flash_priv - private state for this driver + * + * @error: true if there is an error condition + * @alloc_len: Allocation length from the last incoming command + * @transfer_len: Transfer length from CBW header + * @read_len: Number of blocks of data left in the current read command + * @tag: Tag value from last command + * @fd: File descriptor of backing file + * @file_size: Size of file in bytes + * @status_buff: Data buffer for outgoing status + * @buff_used: Number of bytes ready to transfer back to host + * @buff: Data buffer for outgoing data + */ +struct sandbox_flash_priv { + bool error; + int alloc_len; + int transfer_len; + int read_len; + enum cmd_phase phase; + u32 tag; + int fd; + loff_t file_size; + struct umass_bbb_csw status; + int buff_used; + u8 buff[512]; +}; + +struct sandbox_flash_plat { + const char *pathname; +}; + +struct scsi_inquiry_resp { + u8 type; + u8 flags; + u8 version; + u8 data_format; + u8 additional_len; + u8 spare[3]; + char vendor[8]; + char product[16]; + char revision[4]; +}; + +struct scsi_read_capacity_resp { + u32 last_block_addr; + u32 block_len; +}; + +struct __packed scsi_read10_req { + u8 cmd; + u8 lun_flags; + u32 lba; + u8 spare; + u16 transfer_len; + u8 spare2[3]; +}; + +enum { + STRINGID_MANUFACTURER = 1, + STRINGID_PRODUCT, + STRINGID_SERIAL, + + STRINGID_COUNT, +}; + +static struct usb_string flash_strings[] = { + {STRINGID_MANUFACTURER, "sandbox"}, + {STRINGID_PRODUCT, "flash"}, + {STRINGID_SERIAL, "2345"}, + {}, +}; + +static struct usb_device_descriptor flash_device_desc = { + .bLength = sizeof(flash_device_desc), + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .idVendor = __constant_cpu_to_le16(0x1234), + .idProduct = __constant_cpu_to_le16(0x5678), + .iManufacturer = STRINGID_MANUFACTURER, + .iProduct = STRINGID_PRODUCT, + .iSerialNumber = STRINGID_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor flash_config0 = { + .bLength = sizeof(flash_config0), + .bDescriptorType = USB_DT_CONFIG, + + /* wTotalLength is set up by usb-emul-uclass */ + .bNumInterfaces = 1, + .bConfigurationValue = 0, + .iConfiguration = 0, + .bmAttributes = 1 << 7, + .bMaxPower = 50, +}; + +static struct usb_interface_descriptor flash_interface0 = { + .bLength = sizeof(flash_interface0), + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = US_SC_UFI, + .bInterfaceProtocol = US_PR_BULK, + .iInterface = 0, +}; + +static struct usb_endpoint_descriptor flash_endpoint0_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = SANDBOX_FLASH_EP_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_endpoint_descriptor flash_endpoint1_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = SANDBOX_FLASH_EP_IN | USB_ENDPOINT_DIR_MASK, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static void *flash_desc_list[] = { + &flash_device_desc, + &flash_config0, + &flash_interface0, + &flash_endpoint0_out, + &flash_endpoint1_in, + NULL, +}; + +static int sandbox_flash_control(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buff, int len, + struct devrequest *setup) +{ + struct sandbox_flash_priv *priv = dev_get_priv(dev); + + if (pipe == usb_rcvctrlpipe(udev, 0)) { + switch (setup->request) { + case US_BBB_RESET: + priv->error = false; + return 0; + case US_BBB_GET_MAX_LUN: + *(char *)buff = '\0'; + return 1; + default: + debug("request=%x\n", setup->request); + break; + } + } + debug("pipe=%lx\n", pipe); + + return -EIO; +} + +static void setup_fail_response(struct sandbox_flash_priv *priv) +{ + struct umass_bbb_csw *csw = &priv->status; + + csw->dCSWSignature = CSWSIGNATURE; + csw->dCSWTag = priv->tag; + csw->dCSWDataResidue = 0; + csw->bCSWStatus = CSWSTATUS_FAILED; + priv->buff_used = 0; +} + +/** + * setup_response() - set up a response to send back to the host + * + * @priv: Sandbox flash private data + * @resp: Response to send, or NULL if none + * @size: Size of response + */ +static void setup_response(struct sandbox_flash_priv *priv, void *resp, + int size) +{ + struct umass_bbb_csw *csw = &priv->status; + + csw->dCSWSignature = CSWSIGNATURE; + csw->dCSWTag = priv->tag; + csw->dCSWDataResidue = 0; + csw->bCSWStatus = CSWSTATUS_GOOD; + + assert(!resp || resp == priv->buff); + priv->buff_used = size; +} + +static void handle_read(struct sandbox_flash_priv *priv, ulong lba, + ulong transfer_len) +{ + debug("%s: lba=%lx, transfer_len=%lx\n", __func__, lba, transfer_len); + if (priv->fd != -1) { + os_lseek(priv->fd, lba * SANDBOX_FLASH_BLOCK_LEN, OS_SEEK_SET); + priv->read_len = transfer_len; + setup_response(priv, priv->buff, + transfer_len * SANDBOX_FLASH_BLOCK_LEN); + } else { + setup_fail_response(priv); + } +} + +static int handle_ufi_command(struct sandbox_flash_priv *priv, const void *buff, + int len) +{ + const struct SCSI_cmd_block *req = buff; + + switch (*req->cmd) { + case SCSI_INQUIRY: { + struct scsi_inquiry_resp *resp = (void *)priv->buff; + + priv->alloc_len = req->cmd[4]; + memset(resp, '\0', sizeof(*resp)); + resp->data_format = 1; + resp->additional_len = 0x1f; + strncpy(resp->vendor, + flash_strings[STRINGID_MANUFACTURER - 1].s, + sizeof(resp->vendor)); + strncpy(resp->product, flash_strings[STRINGID_PRODUCT - 1].s, + sizeof(resp->product)); + strncpy(resp->revision, "1.0", sizeof(resp->revision)); + setup_response(priv, resp, sizeof(*resp)); + break; + } + case SCSI_TST_U_RDY: + setup_response(priv, NULL, 0); + break; + case SCSI_RD_CAPAC: { + struct scsi_read_capacity_resp *resp = (void *)priv->buff; + uint blocks; + + if (priv->file_size) + blocks = priv->file_size / SANDBOX_FLASH_BLOCK_LEN - 1; + else + blocks = 0; + resp->last_block_addr = cpu_to_be32(blocks); + resp->block_len = cpu_to_be32(SANDBOX_FLASH_BLOCK_LEN); + setup_response(priv, resp, sizeof(*resp)); + break; + } + case SCSI_READ10: { + struct scsi_read10_req *req = (void *)buff; + + handle_read(priv, be32_to_cpu(req->lba), + be16_to_cpu(req->transfer_len)); + break; + } + default: + debug("Command not supported: %x\n", req->cmd[0]); + return -EPROTONOSUPPORT; + } + + priv->phase = priv->transfer_len ? PHASE_DATA : PHASE_STATUS; + return 0; +} + +static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buff, int len) +{ + struct sandbox_flash_priv *priv = dev_get_priv(dev); + int ep = usb_pipeendpoint(pipe); + struct umass_bbb_cbw *cbw = buff; + + debug("%s: dev=%s, pipe=%lx, ep=%x, len=%x, phase=%d\n", __func__, + dev->name, pipe, ep, len, priv->phase); + switch (ep) { + case SANDBOX_FLASH_EP_OUT: + switch (priv->phase) { + case PHASE_START: + priv->alloc_len = 0; + priv->read_len = 0; + if (priv->error || len != UMASS_BBB_CBW_SIZE || + cbw->dCBWSignature != CBWSIGNATURE) + goto err; + if ((cbw->bCBWFlags & CBWFLAGS_SBZ) || + cbw->bCBWLUN != 0) + goto err; + if (cbw->bCDBLength < 1 || cbw->bCDBLength >= 0x10) + goto err; + priv->transfer_len = cbw->dCBWDataTransferLength; + priv->tag = cbw->dCBWTag; + return handle_ufi_command(priv, cbw->CBWCDB, + cbw->bCDBLength); + case PHASE_DATA: + debug("data out\n"); + break; + default: + break; + } + case SANDBOX_FLASH_EP_IN: + switch (priv->phase) { + case PHASE_DATA: + debug("data in, len=%x, alloc_len=%x, priv->read_len=%x\n", + len, priv->alloc_len, priv->read_len); + if (priv->read_len) { + ulong bytes_read; + + bytes_read = os_read(priv->fd, buff, len); + if (bytes_read != len) + return -EIO; + priv->read_len -= len / SANDBOX_FLASH_BLOCK_LEN; + if (!priv->read_len) + priv->phase = PHASE_STATUS; + } else { + if (priv->alloc_len && len > priv->alloc_len) + len = priv->alloc_len; + memcpy(buff, priv->buff, len); + priv->phase = PHASE_STATUS; + } + return len; + case PHASE_STATUS: + debug("status in, len=%x\n", len); + if (len > sizeof(priv->status)) + len = sizeof(priv->status); + memcpy(buff, &priv->status, len); + priv->phase = PHASE_START; + return len; + default: + break; + } + } +err: + priv->error = true; + debug("%s: Detected transfer error\n", __func__); + return 0; +} + +static int sandbox_flash_ofdata_to_platdata(struct udevice *dev) +{ + struct sandbox_flash_plat *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + + plat->pathname = fdt_getprop(blob, dev->of_offset, "sandbox,filepath", + NULL); + + return 0; +} + +static int sandbox_flash_bind(struct udevice *dev) +{ + return usb_emul_setup_device(dev, PACKET_SIZE_64, flash_strings, + flash_desc_list); +} + +static int sandbox_flash_probe(struct udevice *dev) +{ + struct sandbox_flash_plat *plat = dev_get_platdata(dev); + struct sandbox_flash_priv *priv = dev_get_priv(dev); + + priv->fd = os_open(plat->pathname, OS_O_RDONLY); + if (priv->fd != -1) + return os_get_filesize(plat->pathname, &priv->file_size); + + return 0; +} + +static const struct dm_usb_ops sandbox_usb_flash_ops = { + .control = sandbox_flash_control, + .bulk = sandbox_flash_bulk, +}; + +static const struct udevice_id sandbox_usb_flash_ids[] = { + { .compatible = "sandbox,usb-flash" }, + { } +}; + +U_BOOT_DRIVER(usb_sandbox_flash) = { + .name = "usb_sandbox_flash", + .id = UCLASS_USB_EMUL, + .of_match = sandbox_usb_flash_ids, + .bind = sandbox_flash_bind, + .probe = sandbox_flash_probe, + .ofdata_to_platdata = sandbox_flash_ofdata_to_platdata, + .ops = &sandbox_usb_flash_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_flash_priv), + .platdata_auto_alloc_size = sizeof(struct sandbox_flash_plat), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 2d0a145..8ebe9fa 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -43,6 +43,7 @@ enum uclass_id { UCLASS_USB, /* USB bus */ UCLASS_USB_HUB, /* USB hub */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ + UCLASS_MASS_STORAGE, /* Mass storage device */
UCLASS_COUNT, UCLASS_INVALID = -1,

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This emulator supports USB enumeration and allows a local file to be provided as the contents of the emulated flash stick. U-Boot can then use the file as it would a normal device, with all access passing through the usb_stor layer and the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/emul/Makefile | 1 + drivers/usb/emul/sandbox_flash.c | 423 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 3 files changed, 425 insertions(+) create mode 100644 drivers/usb/emul/sandbox_flash.c
Applied to u-boot-dm/next.

All USB controllers need a root hub. Add a sandbox emulation for this so that we can add USB devices to sandbox.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/emul/Makefile | 1 + drivers/usb/emul/sandbox_hub.c | 303 +++++++++++++++++++++++++++++++++++++++++ include/usb_defs.h | 3 + 3 files changed, 307 insertions(+) create mode 100644 drivers/usb/emul/sandbox_hub.c
diff --git a/drivers/usb/emul/Makefile b/drivers/usb/emul/Makefile index 1d5acce..8fd83d5 100644 --- a/drivers/usb/emul/Makefile +++ b/drivers/usb/emul/Makefile @@ -6,4 +6,5 @@ #
obj-$(CONFIG_USB_EMUL) += sandbox_flash.o +obj-$(CONFIG_USB_EMUL) += sandbox_hub.o obj-$(CONFIG_USB_EMUL) += usb-emul-uclass.o diff --git a/drivers/usb/emul/sandbox_hub.c b/drivers/usb/emul/sandbox_hub.c new file mode 100644 index 0000000..280c708 --- /dev/null +++ b/drivers/usb/emul/sandbox_hub.c @@ -0,0 +1,303 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <usb.h> +#include <dm/device-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* We only support up to 8 */ +#define SANDBOX_NUM_PORTS 2 + +struct sandbox_hub_platdata { + struct usb_dev_platdata plat; + int port; /* Port number (numbered from 0) */ +}; + +enum { + STRING_MANUFACTURER = 1, + STRING_PRODUCT, + STRING_SERIAL, + + STRING_count, +}; + +static struct usb_string hub_strings[] = { + {STRING_MANUFACTURER, "sandbox"}, + {STRING_PRODUCT, "hub"}, + {STRING_SERIAL, "2345"}, +}; + +static struct usb_device_descriptor hub_device_desc = { + .bLength = sizeof(hub_device_desc), + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + + .idVendor = __constant_cpu_to_le16(0x1234), + .idProduct = __constant_cpu_to_le16(0x5678), + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 1, +}; + +static struct usb_config_descriptor hub_config1 = { + .bLength = sizeof(hub_config1), + .bDescriptorType = USB_DT_CONFIG, + + /* wTotalLength is set up by usb-emul-uclass */ + .bNumInterfaces = 1, + .bConfigurationValue = 0, + .iConfiguration = 0, + .bmAttributes = 1 << 7, + .bMaxPower = 50, +}; + +static struct usb_interface_descriptor hub_interface0 = { + .bLength = sizeof(hub_interface0), + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = US_PR_CB, + .iInterface = 0, +}; + +static struct usb_endpoint_descriptor hub_endpoint0_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_hub_descriptor hub_desc = { + .bLength = sizeof(hub_desc), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = SANDBOX_NUM_PORTS, + .wHubCharacteristics = __constant_cpu_to_le16(1 << 0 | 1 << 3 | + 1 << 7), + .bPwrOn2PwrGood = 2, + .bHubContrCurrent = 5, + .DeviceRemovable = {0, 0xff}, /* all ports removeable */ +#if SANDBOX_NUM_PORTS > 8 +#error "This code sets up an incorrect mask" +#endif +}; + +static void *hub_desc_list[] = { + &hub_device_desc, + &hub_config1, + &hub_interface0, + &hub_endpoint0_in, + &hub_desc, + NULL, +}; + +struct sandbox_hub_priv { + int status[SANDBOX_NUM_PORTS]; + int change[SANDBOX_NUM_PORTS]; +}; + +static struct udevice *hub_find_device(struct udevice *hub, int port) +{ + struct udevice *dev; + + for (device_find_first_child(hub, &dev); + dev; + device_find_next_child(&dev)) { + struct sandbox_hub_platdata *plat; + + plat = dev_get_parent_platdata(dev); + if (plat->port == port) + return dev; + } + + return NULL; +} + +static int clrset_post_state(struct udevice *hub, int port, int clear, int set) +{ + struct sandbox_hub_priv *priv = dev_get_priv(hub); + int *status = &priv->status[port]; + int *change = &priv->change[port]; + int ret = 0; + + if ((clear | set) & USB_PORT_STAT_POWER) { + struct udevice *dev = hub_find_device(hub, port); + + if (dev) { + if (set & USB_PORT_STAT_POWER) { + ret = device_probe(dev); + debug("%s: %s: power on, probed, ret=%d\n", + __func__, dev->name, ret); + if (!ret) { + set |= USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE; + } + + } else if (clear & USB_PORT_STAT_POWER) { + debug("%s: %s: power off, removed, ret=%d\n", + __func__, dev->name, ret); + ret = device_remove(dev); + clear |= USB_PORT_STAT_CONNECTION; + } + } + } + *change |= *status & clear; + *change |= ~*status & set; + *change &= 0x1f; + *status = (*status & ~clear) | set; + + return ret; +} + +static int sandbox_hub_submit_control_msg(struct udevice *bus, + struct usb_device *udev, + unsigned long pipe, + void *buffer, int length, + struct devrequest *setup) +{ + struct sandbox_hub_priv *priv = dev_get_priv(bus); + int ret = 0; + + if (pipe == usb_rcvctrlpipe(udev, 0)) { + switch (setup->requesttype) { + case USB_RT_HUB | USB_DIR_IN: + switch (setup->request) { + case USB_REQ_GET_STATUS: { + struct usb_hub_status *hubsts = buffer; + + hubsts->wHubStatus = 0; + hubsts->wHubChange = 0; + udev->status = 0; + udev->act_len = sizeof(*hubsts); + return 0; + } + default: + debug("%s: rx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, + setup->request); + break; + } + case USB_RT_PORT | USB_DIR_IN: + switch (setup->request) { + case USB_REQ_GET_STATUS: { + struct usb_port_status *portsts = buffer; + int port; + + port = (setup->index & USB_HUB_PORT_MASK) - 1; + portsts->wPortStatus = priv->status[port]; + portsts->wPortChange = priv->change[port]; + udev->status = 0; + udev->act_len = sizeof(*portsts); + return 0; + } + } + default: + debug("%s: rx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, setup->request); + break; + } + } else if (pipe == usb_sndctrlpipe(udev, 0)) { + switch (setup->requesttype) { + case USB_RT_PORT: + switch (setup->request) { + case USB_REQ_SET_FEATURE: { + int port; + + port = (setup->index & USB_HUB_PORT_MASK) - 1; + debug("set feature port=%x, feature=%x\n", + port, setup->value); + if (setup->value < USB_PORT_FEAT_C_CONNECTION) { + ret = clrset_post_state(bus, port, 0, + 1 << setup->value); + } else { + debug(" ** Invalid feature\n"); + } + return ret; + } + case USB_REQ_CLEAR_FEATURE: { + int port; + + port = (setup->index & USB_HUB_PORT_MASK) - 1; + debug("clear feature port=%x, feature=%x\n", + port, setup->value); + if (setup->value < USB_PORT_FEAT_C_CONNECTION) { + ret = clrset_post_state(bus, port, + 1 << setup->value, 0); + } else { + priv->change[port] &= 1 << + (setup->value - 16); + } + udev->status = 0; + return 0; + } + default: + debug("%s: tx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, + setup->request); + break; + } + default: + debug("%s: tx ctl requesttype=%x, request=%x\n", + __func__, setup->requesttype, setup->request); + break; + } + } + debug("pipe=%lx\n", pipe); + + return -EIO; +} + +static int sandbox_hub_bind(struct udevice *dev) +{ + return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings, + hub_desc_list); +} + +static int sandbox_child_post_bind(struct udevice *dev) +{ + struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev); + + plat->port = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); + + return 0; +} + +static const struct dm_usb_ops sandbox_usb_hub_ops = { + .control = sandbox_hub_submit_control_msg, +}; + +static const struct udevice_id sandbox_usb_hub_ids[] = { + { .compatible = "sandbox,usb-hub" }, + { } +}; + +U_BOOT_DRIVER(usb_sandbox_hub) = { + .name = "usb_sandbox_hub", + .id = UCLASS_USB_EMUL, + .of_match = sandbox_usb_hub_ids, + .bind = sandbox_hub_bind, + .ops = &sandbox_usb_hub_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_hub_priv), + .per_child_platdata_auto_alloc_size = + sizeof(struct sandbox_hub_platdata), + .child_post_bind = sandbox_child_post_bind, +}; diff --git a/include/usb_defs.h b/include/usb_defs.h index d7f7465..27ddc47 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -286,6 +286,9 @@ #define HUB_CHANGE_LOCAL_POWER 0x0001 #define HUB_CHANGE_OVERCURRENT 0x0002
+/* Mask for wIndex in get/set port feature */ +#define USB_HUB_PORT_MASK 0xf + /* * CBI style */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
All USB controllers need a root hub. Add a sandbox emulation for this so that we can add USB devices to sandbox.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/emul/Makefile | 1 + drivers/usb/emul/sandbox_hub.c | 303 +++++++++++++++++++++++++++++++++++++++++ include/usb_defs.h | 3 + 3 files changed, 307 insertions(+) create mode 100644 drivers/usb/emul/sandbox_hub.c
Applied to u-boot-dm/next.

This driver supports using emulation devices to provide a USB bus within sandbox.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/Makefile | 1 + drivers/usb/host/usb-sandbox.c | 117 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 drivers/usb/host/usb-sandbox.c
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 9419295..7658f87 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -7,6 +7,7 @@
ifdef CONFIG_DM_USB obj-$(CONFIG_CMD_USB) += usb-uclass.o +obj-$(CONFIG_SANDBOX) += usb-sandbox.o endif
# ohci diff --git a/drivers/usb/host/usb-sandbox.c b/drivers/usb/host/usb-sandbox.c new file mode 100644 index 0000000..c5f9822 --- /dev/null +++ b/drivers/usb/host/usb-sandbox.c @@ -0,0 +1,117 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <usb.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +static void usbmon_trace(struct udevice *bus, ulong pipe, + struct devrequest *setup, struct udevice *emul) +{ + static const char types[] = "ZICB"; + int type; + + type = (pipe & USB_PIPE_TYPE_MASK) >> USB_PIPE_TYPE_SHIFT; + debug("0 0 S %c%c:%d:%03ld:%ld", types[type], + pipe & USB_DIR_IN ? 'i' : 'o', + bus->seq, + (pipe & USB_PIPE_DEV_MASK) >> USB_PIPE_DEV_SHIFT, + (pipe & USB_PIPE_EP_MASK) >> USB_PIPE_EP_SHIFT); + if (setup) { + debug(" s %02x %02x %04x %04x %04x", setup->requesttype, + setup->request, setup->value, setup->index, + setup->length); + } + debug(" %s", emul ? emul->name : "(no emul found)"); + + debug("\n"); +} + +static int sandbox_submit_control(struct udevice *bus, + struct usb_device *udev, + unsigned long pipe, + void *buffer, int length, + struct devrequest *setup) +{ + struct udevice *emul; + int ret; + + /* Just use child of dev as emulator? */ + debug("%s: bus=%s\n", __func__, bus->name); + ret = usb_emul_find(bus, pipe, &emul); + usbmon_trace(bus, pipe, setup, emul); + if (ret) + return ret; + ret = usb_emul_control(emul, udev, pipe, buffer, length, setup); + if (ret < 0) { + debug("ret=%d\n", ret); + udev->status = ret; + udev->act_len = 0; + } else { + udev->status = 0; + udev->act_len = ret; + } + + return ret; +} + +static int sandbox_submit_bulk(struct udevice *bus, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + struct udevice *emul; + int ret; + + /* Just use child of dev as emulator? */ + debug("%s: bus=%s\n", __func__, bus->name); + ret = usb_emul_find(bus, pipe, &emul); + usbmon_trace(bus, pipe, NULL, emul); + if (ret) + return ret; + ret = usb_emul_bulk(emul, udev, pipe, buffer, length); + if (ret < 0) { + debug("ret=%d\n", ret); + udev->status = ret; + udev->act_len = 0; + } else { + udev->status = 0; + udev->act_len = ret; + } + + return ret; +} + +static int sandbox_alloc_device(struct udevice *dev, struct usb_device *udev) +{ + return 0; +} + +static int sandbox_usb_probe(struct udevice *dev) +{ + return 0; +} + +static const struct dm_usb_ops sandbox_usb_ops = { + .control = sandbox_submit_control, + .bulk = sandbox_submit_bulk, + .alloc_device = sandbox_alloc_device, +}; + +static const struct udevice_id sandbox_usb_ids[] = { + { .compatible = "sandbox,usb" }, + { } +}; + +U_BOOT_DRIVER(usb_sandbox) = { + .name = "usb_sandbox", + .id = UCLASS_USB, + .of_match = sandbox_usb_ids, + .probe = sandbox_usb_probe, + .ops = &sandbox_usb_ops, +};

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This driver supports using emulation devices to provide a USB bus within sandbox.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/Makefile | 1 + drivers/usb/host/usb-sandbox.c | 117 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 drivers/usb/host/usb-sandbox.c
Applied to u-boot-dm/next.

These allow basic testing of the USB functionality within sandbox.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
arch/sandbox/dts/sandbox.dts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 7d050d9..03b540c 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -1,5 +1,7 @@ /dts-v1/;
+#define USB_CLASS_HUB 9 + / { #address-cells = <1>; #size-cells = <1>; @@ -219,4 +221,42 @@ reg = <0x90000000 0x1000>; host-raw-interface = "lo"; }; + + usb@0 { + compatible = "sandbox,usb"; + status = "disabled"; + hub { + compatible = "sandbox,usb-hub"; + #address-cells = <1>; + #size-cells = <0>; + flash-stick { + reg = <0>; + compatible = "sandbox,usb-flash"; + }; + }; + }; + + usb@1 { + compatible = "sandbox,usb"; + hub { + compatible = "usb-hub"; + usb,device-class = <USB_CLASS_HUB>; + hub-emul { + compatible = "sandbox,usb-hub"; + #address-cells = <1>; + #size-cells = <0>; + flash-stick { + reg = <0>; + compatible = "sandbox,usb-flash"; + sandbox,filepath = "flash.bin"; + }; + }; + }; + }; + + usb@2 { + compatible = "sandbox,usb"; + status = "disabled"; + }; + };

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
These allow basic testing of the USB functionality within sandbox.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
arch/sandbox/dts/sandbox.dts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
Applied to u-boot-dm/next.

Add support for scanning USB etghernet devices with driver model. This mostly involves scanning all buses since device numbering is not unique across buses.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/eth/usb_ether.c | 52 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index 7cb96e3..c72b7e4 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -5,7 +5,9 @@ */
#include <common.h> +#include <dm.h> #include <usb.h> +#include <dm/device-internal.h>
#include "usb_ether.h"
@@ -118,8 +120,6 @@ static void probe_valid_drivers(struct usb_device *dev) int usb_host_eth_scan(int mode) { int i, old_async; - struct usb_device *dev; -
if (mode == 1) printf(" scanning usb for ethernet devices... "); @@ -138,23 +138,59 @@ int usb_host_eth_scan(int mode) }
usb_max_eth_dev = 0; +#ifdef CONFIG_DM_USB + /* + * TODO: We should add USB_DEVICE() declarations to each USB ethernet + * driver and then most of this file can be removed. + */ + struct udevice *bus; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_USB, &uc); + if (ret) + return ret; + uclass_foreach_dev(bus, uc) { + for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + + dev = usb_get_dev_index(bus, i); /* get device */ + debug("i=%d, %s\n", i, dev ? dev->dev->name : "(done)"); + if (!dev) + break; /* no more devices available */ + + /* + * find valid usb_ether driver for this device, + * if any + */ + probe_valid_drivers(dev); + + /* check limit */ + if (usb_max_eth_dev == USB_MAX_ETH_DEV) + break; + } /* for */ + } +#else for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + dev = usb_get_dev_index(i); /* get device */ debug("i=%d\n", i); - if (dev == NULL) + if (!dev) break; /* no more devices available */
/* find valid usb_ether driver for this device, if any */ probe_valid_drivers(dev);
/* check limit */ - if (usb_max_eth_dev == USB_MAX_ETH_DEV) { - printf("max USB Ethernet Device reached: %d stopping\n", - usb_max_eth_dev); + if (usb_max_eth_dev == USB_MAX_ETH_DEV) break; - } } /* for */ - +#endif + if (usb_max_eth_dev == USB_MAX_ETH_DEV) { + printf("max USB Ethernet Device reached: %d stopping\n", + usb_max_eth_dev); + } usb_disable_asynch(old_async); /* restore asynch value */ printf("%d Ethernet Device(s) found\n", usb_max_eth_dev); if (usb_max_eth_dev > 0)

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Add support for scanning USB etghernet devices with driver model. This mostly involves scanning all buses since device numbering is not unique across buses.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/eth/usb_ether.c | 52 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-)
Applied to u-boot-dm/next.

Update this driver with driver model support for USB.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-exynos.c | 112 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index f3c077d..86cf631 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -8,6 +8,7 @@ */
#include <common.h> +#include <dm.h> #include <fdtdec.h> #include <libfdt.h> #include <malloc.h> @@ -24,19 +25,73 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR;
+#ifdef CONFIG_DM_USB +struct exynos_ehci_platdata { + struct usb_platdata usb_plat; + fdt_addr_t hcd_base; + fdt_addr_t phy_base; + struct gpio_desc vbus_gpio; +}; +#endif + /** * Contains pointers to register base addresses * for the usb controller. */ struct exynos_ehci { + struct ehci_ctrl ctrl; struct exynos_usb_phy *usb; struct ehci_hccr *hcd; +#ifndef CONFIG_DM_USB struct gpio_desc vbus_gpio; +#endif };
+#ifndef CONFIG_DM_USB static struct exynos_ehci exynos; +#endif
-#ifdef CONFIG_OF_CONTROL +#ifdef CONFIG_DM_USB +static int ehci_usb_ofdata_to_platdata(struct udevice *dev) +{ + struct exynos_ehci_platdata *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + unsigned int node; + int depth; + + /* + * Get the base address for XHCI controller from the device node + */ + plat->hcd_base = dev_get_addr(dev); + if (plat->hcd_base == FDT_ADDR_T_NONE) { + debug("Can't get the XHCI register base address\n"); + return -ENXIO; + } + + depth = 0; + node = fdtdec_next_compatible_subnode(blob, dev->of_offset, + COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); + if (node <= 0) { + debug("XHCI: Can't get device node for usb3-phy controller\n"); + return -ENODEV; + } + + /* + * Get the base address for usbphy from the device node + */ + plat->phy_base = fdtdec_get_addr(blob, node, "reg"); + if (plat->phy_base == FDT_ADDR_T_NONE) { + debug("Can't get the usbphy register address\n"); + return -ENXIO; + } + + /* Vbus gpio */ + gpio_request_by_name(dev, "samsung,vbus-gpio", 0, + &plat->vbus_gpio, GPIOD_IS_OUT); + + return 0; +} +#else static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) { fdt_addr_t addr; @@ -215,6 +270,7 @@ static void reset_usb_phy(struct exynos_usb_phy *usb) set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); }
+#ifndef CONFIG_DM_USB /* * EHCI-initialization * Create the appropriate control structures to manage @@ -268,3 +324,57 @@ int ehci_hcd_stop(int index)
return 0; } +#endif + +#ifdef CONFIG_DM_USB +static int ehci_usb_probe(struct udevice *dev) +{ + struct exynos_ehci_platdata *plat = dev_get_platdata(dev); + struct exynos_ehci *ctx = dev_get_priv(dev); + struct ehci_hcor *hcor; + + ctx->hcd = (struct ehci_hccr *)plat->hcd_base; + ctx->usb = (struct exynos_usb_phy *)plat->phy_base; + hcor = (struct ehci_hcor *)((uint32_t)ctx->hcd + + HC_LENGTH(ehci_readl(&ctx->hcd->cr_capbase))); + + /* setup the Vbus gpio here */ + if (dm_gpio_is_valid(&plat->vbus_gpio)) + dm_gpio_set_value(&plat->vbus_gpio, 1); + + setup_usb_phy(ctx->usb); + + return ehci_register(dev, ctx->hcd, hcor, NULL, 0, USB_INIT_HOST); +} + +static int ehci_usb_remove(struct udevice *dev) +{ + struct exynos_ehci *ctx = dev_get_priv(dev); + int ret; + + ret = ehci_deregister(dev); + if (ret) + return ret; + reset_usb_phy(ctx->usb); + + return 0; +} + +static const struct udevice_id ehci_usb_ids[] = { + { .compatible = "samsung,exynos-ehci" }, + { } +}; + +U_BOOT_DRIVER(usb_ehci) = { + .name = "ehci_exynos", + .id = UCLASS_USB, + .of_match = ehci_usb_ids, + .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, + .probe = ehci_usb_probe, + .remove = ehci_usb_remove, + .ops = &ehci_usb_ops, + .priv_auto_alloc_size = sizeof(struct exynos_ehci), + .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Update this driver with driver model support for USB.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-exynos.c | 112 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-)
Applied to u-boot-dm/next.

This variable is a bit of a hack. We can obtain the same information from the normal device config. This will fit better with driver model, where global variables are best avoided.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 38333c7..464f55d 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -87,8 +87,6 @@ struct fdt_usb {
static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ static unsigned port_count; /* Number of available ports */ -/* Port that needs to clear CSC after Port Reset */ -static u32 port_addr_clear_csc;
/* * This table has USB timing parameters for each Oscillator frequency we @@ -206,7 +204,7 @@ static void tegra_ehci_powerup_fixup(struct ehci_ctrl *ctrl, if (controller->has_hostpc) *reg |= EHCI_PS_PE;
- if (((u32)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc) + if (!config->has_legacy_mode) return; /* For EHCI_PS_CSC to be cleared in ehci_hcd.c */ if (ehci_readl(status_reg) & EHCI_PS_CSC) @@ -683,8 +681,6 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) config->enabled = fdtdec_get_is_enabled(blob, node); config->has_legacy_mode = fdtdec_get_bool(blob, node, "nvidia,has-legacy-mode"); - if (config->has_legacy_mode) - port_addr_clear_csc = (u32) config->reg; config->periph_id = clock_decode_periph_id(blob, node); if (config->periph_id == PERIPH_ID_NONE) { debug("%s: Missing/invalid peripheral ID\n", __func__);

-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Simon Glass Sent: Thursday, March 26, 2015 2:23 AM To: U-Boot Mailing List Cc: Marek Vasut; Stephen Warren; Vivek Gautam; Tom Warren Subject: [U-Boot] [PATCH v2 57/80] dm: usb: tegra: Remove the port_addr_clear_csc variable
This variable is a bit of a hack. We can obtain the same information from the normal device config. This will fit better with driver model, where global variables > are best avoided.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 38333c7..464f55d 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -87,8 +87,6 @@ struct fdt_usb {
static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ static unsigned port_count; /* Number of available ports */ -/* Port that needs to clear CSC after Port Reset */ -static u32 port_addr_clear_csc;
/*
- This table has USB timing parameters for each Oscillator frequency we @@ -206,7 +204,7 @@ static void tegra_ehci_powerup_fixup(struct ehci_ctrl *ctrl,
if (controller->has_hostpc)
*reg |= EHCI_PS_PE;
- if (((u32)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc)
- if (!config->has_legacy_mode) return;
How do we get config (config->has_legacy_mode) for in this function? If you read ehci_set_usbmode, "config" comes from "config = &port[index];". Or "config" is an input argument like set_up_vbus(struct fdt_usb *config, ...) But for ehci_powerup_fixup, I don't see a way for us to get "config".
/* For EHCI_PS_CSC to be cleared in ehci_hcd.c */
--nvpublic

Please ignore my question because it is answered by [PATCH v2 34/80].
On 03/26/2015 11:38 AM, Jim Lin wrote:
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Simon Glass Sent: Thursday, March 26, 2015 2:23 AM To: U-Boot Mailing List Cc: Marek Vasut; Stephen Warren; Vivek Gautam; Tom Warren Subject: [U-Boot] [PATCH v2 57/80] dm: usb: tegra: Remove the port_addr_clear_csc variable
This variable is a bit of a hack. We can obtain the same information from the normal device config. This will fit better with driver model, where global variables > are best avoided.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 38333c7..464f55d 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -87,8 +87,6 @@ struct fdt_usb {
static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ static unsigned port_count; /* Number of available ports */ -/* Port that needs to clear CSC after Port Reset */ -static u32 port_addr_clear_csc;
/*
- This table has USB timing parameters for each Oscillator frequency we @@ -206,7 +204,7 @@ static void tegra_ehci_powerup_fixup(struct ehci_ctrl *ctrl,
if (controller->has_hostpc)
*reg |= EHCI_PS_PE;
- if (((u32)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc)
- if (!config->has_legacy_mode) return;
How do we get config (config->has_legacy_mode) for in this function? If you read ehci_set_usbmode, "config" comes from "config = &port[index];". Or "config" is an input argument like set_up_vbus(struct fdt_usb *config, ...) But for ehci_powerup_fixup, I don't see a way for us to get "config".
/* For EHCI_PS_CSC to be cleared in ehci_hcd.c */
--nvpublic _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On 26 March 2015 at 03:01, Jim Lin jilin@nvidia.com wrote:
Please ignore my question because it is answered by [PATCH v2 34/80].
On 03/26/2015 11:38 AM, Jim Lin wrote:
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Simon Glass Sent: Thursday, March 26, 2015 2:23 AM To: U-Boot Mailing List Cc: Marek Vasut; Stephen Warren; Vivek Gautam; Tom Warren Subject: [U-Boot] [PATCH v2 57/80] dm: usb: tegra: Remove the port_addr_clear_csc variable
This variable is a bit of a hack. We can obtain the same information from the normal device config. This will fit better with driver model, where global variables > are best avoided.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 38333c7..464f55d 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -87,8 +87,6 @@ struct fdt_usb { static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ static unsigned port_count; /* Number of available ports */ -/* Port that needs to clear CSC after Port Reset */ -static u32 port_addr_clear_csc; /*
- This table has USB timing parameters for each Oscillator frequency
we @@ -206,7 +204,7 @@ static void tegra_ehci_powerup_fixup(struct ehci_ctrl *ctrl,
if (controller->has_hostpc)
*reg |= EHCI_PS_PE;
if (((u32)status_reg & TEGRA_USB_ADDR_MASK) !=
port_addr_clear_csc)
if (!config->has_legacy_mode) return;
How do we get config (config->has_legacy_mode) for in this function? If you read ehci_set_usbmode, "config" comes from "config = &port[index];". Or "config" is an input argument like set_up_vbus(struct fdt_usb *config, ...) But for ehci_powerup_fixup, I don't see a way for us to get "config".
/* For EHCI_PS_CSC to be cleared in ehci_hcd.c */
--nvpublic _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
Applied to u-boot-dm/next.

Try to return useful error numbers where possible. Also avoid swallowing an error number when it is returned by a called function.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 464f55d..04653e4 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -266,7 +266,8 @@ static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init) } }
-void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr) +static void usbf_reset_controller(struct fdt_usb *config, + struct usb_ctlr *usbctlr) { /* Reset the USB controller with 2us delay */ reset_periph(config->periph_id, 2); @@ -524,7 +525,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config, udelay(1); } if (!loop_count) - return -1; + return -ETIMEDOUT;
/* Disable ICUSB FS/LS transceiver */ clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1); @@ -567,6 +568,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config, int loop_count; struct ulpi_viewport ulpi_vp; struct usb_ctlr *usbctlr = config->reg; + int ret;
/* set up ULPI reference clock on pllp_out4 */ clock_enable(PERIPH_ID_DEV2_OUT); @@ -612,9 +614,10 @@ static int init_ulpi_usb_controller(struct fdt_usb *config, ulpi_vp.port_num = 0; ulpi_vp.viewport_addr = (u32)&usbctlr->ulpi_viewport;
- if (ulpi_init(&ulpi_vp)) { + ret = ulpi_init(&ulpi_vp); + if (ret) { printf("Tegra ULPI viewport init failed\n"); - return -1; + return ret; }
ulpi_set_vbus(&ulpi_vp, 1, 1); @@ -631,7 +634,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config, udelay(1); } if (!loop_count) - return -1; + return -ETIMEDOUT; clrbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
return 0; @@ -642,7 +645,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config, { printf("No code to set up ULPI controller, please enable" "CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT"); - return -1; + return -ENOSYS; } #endif
@@ -669,7 +672,7 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) else { debug("%s: Cannot decode dr_mode '%s'\n", __func__, mode); - return -FDT_ERR_NOTFOUND; + return -EINVAL; } } else { config->dr_mode = DR_MODE_HOST; @@ -684,7 +687,7 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) config->periph_id = clock_decode_periph_id(blob, node); if (config->periph_id == PERIPH_ID_NONE) { debug("%s: Missing/invalid peripheral ID\n", __func__); - return -FDT_ERR_NOTFOUND; + return -EINVAL; } gpio_request_by_name_nodev(blob, node, "nvidia,vbus-gpio", 0, &config->vbus_gpio, GPIOD_IS_OUT);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Try to return useful error numbers where possible. Also avoid swallowing an error number when it is returned by a called function.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
Applied to u-boot-dm/next.

We want to use mostly the same init and uninit code for driver model, so move the common part into two functions.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 143 ++++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 62 deletions(-)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 04653e4..940a583 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -703,6 +703,82 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) return 0; }
+int usb_common_init(struct fdt_usb *config, enum usb_init_type init) +{ + int ret = 0; + + switch (init) { + case USB_INIT_HOST: + switch (config->dr_mode) { + case DR_MODE_HOST: + case DR_MODE_OTG: + break; + default: + printf("tegrausb: Invalid dr_mode %d for host mode\n", + config->dr_mode); + return -1; + } + break; + case USB_INIT_DEVICE: + if (config->periph_id != PERIPH_ID_USBD) { + printf("tegrausb: Device mode only supported on first USB controller\n"); + return -1; + } + if (!config->utmi) { + printf("tegrausb: Device mode only supported with UTMI PHY\n"); + return -1; + } + switch (config->dr_mode) { + case DR_MODE_DEVICE: + case DR_MODE_OTG: + break; + default: + printf("tegrausb: Invalid dr_mode %d for device mode\n", + config->dr_mode); + return -1; + } + break; + default: + printf("tegrausb: Unknown USB_INIT_* %d\n", init); + return -1; + } + +#ifndef CONFIG_DM_USB + /* skip init, if the port is already initialized */ + if (config->initialized && config->init_type == init) + return 0; +#endif + + debug("%d, %d\n", config->utmi, config->ulpi); + if (config->utmi) + ret = init_utmi_usb_controller(config, init); + else if (config->ulpi) + ret = init_ulpi_usb_controller(config, init); + if (ret) + return ret; + + set_up_vbus(config, init); + + config->init_type = init; + + return 0; +} + +void usb_common_uninit(struct fdt_usb *priv) +{ + struct usb_ctlr *usbctlr; + + usbctlr = priv->reg; + + /* Stop controller */ + writel(0, &usbctlr->usb_cmd); + udelay(1000); + + /* Initiate controller reset */ + writel(2, &usbctlr->usb_cmd); + udelay(1000); +} + static const struct ehci_ops tegra_ehci_ops = { .set_usb_mode = tegra_ehci_set_usbmode, .get_port_speed = tegra_ehci_get_port_speed, @@ -795,6 +871,7 @@ int ehci_hcd_init(int index, enum usb_init_type init, { struct fdt_usb *config; struct usb_ctlr *usbctlr; + int ret;
if (index >= port_count) return -1; @@ -802,62 +879,14 @@ int ehci_hcd_init(int index, enum usb_init_type init, config = &port[index]; ehci_set_controller_priv(index, config, &tegra_ehci_ops);
- switch (init) { - case USB_INIT_HOST: - switch (config->dr_mode) { - case DR_MODE_HOST: - case DR_MODE_OTG: - break; - default: - printf("tegrausb: Invalid dr_mode %d for host mode\n", - config->dr_mode); - return -1; - } - break; - case USB_INIT_DEVICE: - if (config->periph_id != PERIPH_ID_USBD) { - printf("tegrausb: Device mode only supported on first USB controller\n"); - return -1; - } - if (!config->utmi) { - printf("tegrausb: Device mode only supported with UTMI PHY\n"); - return -1; - } - switch (config->dr_mode) { - case DR_MODE_DEVICE: - case DR_MODE_OTG: - break; - default: - printf("tegrausb: Invalid dr_mode %d for device mode\n", - config->dr_mode); - return -1; - } - break; - default: - printf("tegrausb: Unknown USB_INIT_* %d\n", init); - return -1; - } - - /* skip init, if the port is already initialized */ - if (config->initialized && config->init_type == init) - goto success; - - if (config->utmi && init_utmi_usb_controller(config, init)) { - printf("tegrausb: Cannot init port %d\n", index); - return -1; - } - - if (config->ulpi && init_ulpi_usb_controller(config, init)) { + ret = usb_common_init(config, init); + if (ret) { printf("tegrausb: Cannot init port %d\n", index); - return -1; + return ret; }
- set_up_vbus(config, init); - config->initialized = 1; - config->init_type = init;
-success: usbctlr = config->reg; *hccr = (struct ehci_hccr *)&usbctlr->cap_length; *hcor = (struct ehci_hcor *)&usbctlr->usb_cmd; @@ -870,17 +899,7 @@ success: */ int ehci_hcd_stop(int index) { - struct usb_ctlr *usbctlr; - - usbctlr = port[index].reg; - - /* Stop controller */ - writel(0, &usbctlr->usb_cmd); - udelay(1000); - - /* Initiate controller reset */ - writel(2, &usbctlr->usb_cmd); - udelay(1000); + usb_common_uninit(&port[index]);
port[index].initialized = 0;

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
We want to use mostly the same init and uninit code for driver model, so move the common part into two functions.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 143 ++++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 62 deletions(-)
Applied to u-boot-dm/next.

Update this driver with driver model support for USB.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 83 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 940a583..66e5649 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -7,6 +7,7 @@ */
#include <common.h> +#include <dm.h> #include <asm/errno.h> #include <asm/io.h> #include <asm-generic/gpio.h> @@ -20,6 +21,8 @@
#include "ehci.h"
+DECLARE_GLOBAL_DATA_PTR; + #define USB1_ADDR_MASK 0xFFFF0000
#define HOSTPC1_DEVLC 0x84 @@ -32,9 +35,11 @@ #endif #endif
+#ifndef CONFIG_DM_USB enum { USB_PORTS_MAX = 3, /* Maximum ports we allow */ }; +#endif
/* Parameters we need for USB */ enum { @@ -71,12 +76,15 @@ enum usb_ctlr_type {
/* Information about a USB port */ struct fdt_usb { + struct ehci_ctrl ehci; struct usb_ctlr *reg; /* address of registers in physical memory */ unsigned utmi:1; /* 1 if port has external tranceiver, else 0 */ unsigned ulpi:1; /* 1 if port has external ULPI transceiver */ unsigned enabled:1; /* 1 to enable, 0 to disable */ unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */ +#ifndef CONFIG_DM_USB unsigned initialized:1; /* has this port already been initialized? */ +#endif enum usb_ctlr_type type; enum usb_init_type init_type; enum dr_mode dr_mode; /* dual role mode */ @@ -85,8 +93,10 @@ struct fdt_usb { struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */ };
+#ifndef CONFIG_DM_USB static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ static unsigned port_count; /* Number of available ports */ +#endif
/* * This table has USB timing parameters for each Oscillator frequency we @@ -163,6 +173,7 @@ static const u8 utmip_elastic_limit = 16; static const u8 utmip_hs_sync_start_delay = 9;
struct fdt_usb_controller { + /* TODO(sjg@chromium.org): Remove when we only use driver model */ int compat; /* flag to determine whether controller supports hostpc register */ u32 has_hostpc:1; @@ -785,6 +796,7 @@ static const struct ehci_ops tegra_ehci_ops = { .powerup_fixup = tegra_ehci_powerup_fixup, };
+#ifndef CONFIG_DM_USB /* * process_usb_nodes() - Process a list of USB nodes, adding them to our list * of USB ports. @@ -905,3 +917,74 @@ int ehci_hcd_stop(int index)
return 0; } +#endif /* !CONFIG_DM_USB */ + +#ifdef CONFIG_DM_USB +static int ehci_usb_ofdata_to_platdata(struct udevice *dev) +{ + struct fdt_usb *priv = dev_get_priv(dev); + int ret; + + ret = fdt_decode_usb(gd->fdt_blob, dev->of_offset, priv); + if (ret) + return ret; + + priv->type = dev_get_driver_data(dev); + + return 0; +} + +static int ehci_usb_probe(struct udevice *dev) +{ + struct usb_platdata *plat = dev_get_platdata(dev); + struct fdt_usb *priv = dev_get_priv(dev); + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + static bool clk_done; + int ret; + + ret = usb_common_init(priv, plat->init_type); + if (ret) + return ret; + hccr = (struct ehci_hccr *)&priv->reg->cap_length; + hcor = (struct ehci_hcor *)&priv->reg->usb_cmd; + if (!clk_done) { + config_clock(get_pll_timing(&fdt_usb_controllers[priv->type])); + clk_done = true; + } + + return ehci_register(dev, hccr, hcor, &tegra_ehci_ops, 0, + plat->init_type); +} + +static int ehci_usb_remove(struct udevice *dev) +{ + int ret; + + ret = ehci_deregister(dev); + if (ret) + return ret; + + return 0; +} + +static const struct udevice_id ehci_usb_ids[] = { + { .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 }, + { .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 }, + { .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 }, + { } +}; + +U_BOOT_DRIVER(usb_ehci) = { + .name = "ehci_tegra", + .id = UCLASS_USB, + .of_match = ehci_usb_ids, + .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, + .probe = ehci_usb_probe, + .remove = ehci_usb_remove, + .ops = &ehci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct fdt_usb), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Update this driver with driver model support for USB.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-tegra.c | 83 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)
Applied to u-boot-dm/next.

Rather than getting this directly from struct usb_device, call a function to obtain it. This will make it possible for driver model to provide it another way.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/xhci-ring.c | 8 ++++---- drivers/usb/host/xhci.c | 19 ++++++++++++------- drivers/usb/host/xhci.h | 2 ++ 3 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b5aade9..246b697 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -353,7 +353,7 @@ static void giveback_first_trb(struct usb_device *udev, int ep_index, int start_cycle, struct xhci_generic_trb *start_trb) { - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
/* * Pass all the TRBs to the hardware at once and make sure this write @@ -477,7 +477,7 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) */ static void abort_td(struct usb_device *udev, int ep_index) { - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; union xhci_trb *event; u32 field; @@ -554,7 +554,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, int start_cycle; u32 field = 0; u32 length_field = 0; - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); int slot_id = udev->slot_id; int ep_index; struct xhci_virt_device *virt_dev; @@ -748,7 +748,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, u32 length_field; u64 buf_64 = 0; struct xhci_generic_trb *start_trb; - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); int slot_id = udev->slot_id; int ep_index; u32 trb_fields[4]; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 87f2972..ab39878 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -110,6 +110,11 @@ static struct descriptor {
static struct xhci_ctrl xhcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
+struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev) +{ + return udev->controller; +} + /** * Waits for as per specified amount of time * for the "result" to match with "done" @@ -250,7 +255,7 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) { struct xhci_container_ctx *in_ctx; struct xhci_virt_device *virt_dev; - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); union xhci_trb *event;
virt_dev = ctrl->devs[udev->slot_id]; @@ -298,7 +303,7 @@ static int xhci_set_configuration(struct usb_device *udev) int ep_index; unsigned int dir; unsigned int ep_type; - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); int num_of_ep; int ep_flag = 0; u64 trb_64 = 0; @@ -382,7 +387,7 @@ static int xhci_set_configuration(struct usb_device *udev) static int xhci_address_device(struct usb_device *udev) { int ret = 0; - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); struct xhci_slot_ctx *slot_ctx; struct xhci_input_control_ctx *ctrl_ctx; struct xhci_virt_device *virt_dev; @@ -463,8 +468,8 @@ static int xhci_address_device(struct usb_device *udev) */ int usb_alloc_device(struct usb_device *udev) { + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); union xhci_trb *event; - struct xhci_ctrl *ctrl = udev->controller; int ret;
/* @@ -510,7 +515,7 @@ int usb_alloc_device(struct usb_device *udev) */ int xhci_check_maxpacket(struct usb_device *udev) { - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); unsigned int slot_id = udev->slot_id; int ep_index = 0; /* control endpoint */ struct xhci_container_ctx *in_ctx; @@ -640,7 +645,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, int len, srclen; uint32_t reg; volatile uint32_t *status_reg; - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); struct xhci_hcor *hcor = ctrl->hcor;
if ((req->requesttype & USB_RT_PORT) && @@ -904,7 +909,7 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer, int length, struct devrequest *setup) { - struct xhci_ctrl *ctrl = udev->controller; + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); int ret = 0;
if (usb_pipetype(pipe) != PIPE_CONTROL) { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6685ed2..6e76872 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1250,4 +1250,6 @@ int xhci_alloc_virt_device(struct usb_device *udev); int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, struct xhci_hcor *hcor);
+struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev); + #endif /* HOST_XHCI_H_ */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Rather than getting this directly from struct usb_device, call a function to obtain it. This will make it possible for driver model to provide it another way.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/xhci-ring.c | 8 ++++---- drivers/usb/host/xhci.c | 19 ++++++++++++------- drivers/usb/host/xhci.h | 2 ++ 3 files changed, 18 insertions(+), 11 deletions(-)
Applied to u-boot-dm/next.

This function should not be delving into struct usb_device. Pass in the parameters it needs directly.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/xhci-mem.c | 4 +--- drivers/usb/host/xhci.c | 2 +- drivers/usb/host/xhci.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 89908e8..4644ffa 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -352,12 +352,10 @@ static struct xhci_container_ctx * @param udev pointer to USB deivce structure * @return 0 on success else -1 on failure */ -int xhci_alloc_virt_device(struct usb_device *udev) +int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id) { u64 byte_64 = 0; - unsigned int slot_id = udev->slot_id; struct xhci_virt_device *virt_dev; - struct xhci_ctrl *ctrl = udev->controller;
/* Slot ID 0 is reserved */ if (ctrl->devs[slot_id]) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ab39878..de006f7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -491,7 +491,7 @@ int usb_alloc_device(struct usb_device *udev)
xhci_acknowledge_event(ctrl);
- ret = xhci_alloc_virt_device(udev); + ret = xhci_alloc_virt_device(ctrl, udev->slot_id); if (ret < 0) { /* * TODO: Unsuccessful Address Device command shall leave diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6e76872..00faa81 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1246,7 +1246,7 @@ void xhci_flush_cache(uint32_t addr, u32 type_len); void xhci_inval_cache(uint32_t addr, u32 type_len); void xhci_cleanup(struct xhci_ctrl *ctrl); struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs); -int xhci_alloc_virt_device(struct usb_device *udev); +int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id); int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, struct xhci_hcor *hcor);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This function should not be delving into struct usb_device. Pass in the parameters it needs directly.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/xhci-mem.c | 4 +--- drivers/usb/host/xhci.c | 2 +- drivers/usb/host/xhci.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
Applied to u-boot-dm/next.

This function should not be delving into struct usb_device. Pass in the parameters it needs directly.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/xhci-mem.c | 19 +++++++------------ drivers/usb/host/xhci.c | 29 ++++++++++++++++++++++------- drivers/usb/host/xhci.h | 3 ++- 3 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 4644ffa..1b55534 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -625,17 +625,16 @@ void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx, * @param udev pointer to the Device Data Structure * @return returns negative value on failure else 0 on success */ -void xhci_setup_addressable_virt_dev(struct usb_device *udev) +void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, int slot_id, + int speed, int hop_portnr) { - struct usb_device *hop = udev; struct xhci_virt_device *virt_dev; struct xhci_ep_ctx *ep0_ctx; struct xhci_slot_ctx *slot_ctx; u32 port_num = 0; u64 trb_64 = 0; - struct xhci_ctrl *ctrl = udev->controller;
- virt_dev = ctrl->devs[udev->slot_id]; + virt_dev = ctrl->devs[slot_id];
BUG_ON(!virt_dev);
@@ -646,7 +645,7 @@ void xhci_setup_addressable_virt_dev(struct usb_device *udev) /* Only the control endpoint is valid - one endpoint context */ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | 0);
- switch (udev->speed) { + switch (speed) { case USB_SPEED_SUPER: slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS); break; @@ -664,11 +663,7 @@ void xhci_setup_addressable_virt_dev(struct usb_device *udev) BUG(); }
- /* Extract the root hub port number */ - if (hop->parent) - while (hop->parent->parent) - hop = hop->parent; - port_num = hop->portnr; + port_num = hop_portnr; debug("port_num = %d\n", port_num);
slot_ctx->dev_info2 |= @@ -678,9 +673,9 @@ void xhci_setup_addressable_virt_dev(struct usb_device *udev) /* Step 4 - ring already allocated */ /* Step 5 */ ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT); - debug("SPEED = %d\n", udev->speed); + debug("SPEED = %d\n", speed);
- switch (udev->speed) { + switch (speed) { case USB_SPEED_SUPER: ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) << MAX_PACKET_SHIFT)); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index de006f7..774c3e6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -384,7 +384,7 @@ static int xhci_set_configuration(struct usb_device *udev) * @param udev pointer to the Device Data Structure * @return 0 if successful else error code on failure */ -static int xhci_address_device(struct usb_device *udev) +static int xhci_address_device(struct usb_device *udev, int root_portnr) { int ret = 0; struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); @@ -400,8 +400,9 @@ static int xhci_address_device(struct usb_device *udev) * This is the first Set Address since device plug-in * so setting up the slot context. */ - debug("Setting up addressable devices\n"); - xhci_setup_addressable_virt_dev(udev); + debug("Setting up addressable devices %p\n", ctrl->dcbaa); + xhci_setup_addressable_virt_dev(ctrl, udev->slot_id, udev->speed, + root_portnr);
ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); @@ -903,11 +904,12 @@ submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, * @param buffer buffer to be read/written based on the request * @param length length of the buffer * @param setup Request type + * @param root_portnr Root port number that this device is on * @return returns 0 if successful else -1 on failure */ -int -submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer, - int length, struct devrequest *setup) +static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, + struct devrequest *setup, int root_portnr) { struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); int ret = 0; @@ -921,7 +923,7 @@ submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer, return xhci_submit_root(udev, pipe, buffer, setup);
if (setup->request == USB_REQ_SET_ADDRESS) - return xhci_address_device(udev); + return xhci_address_device(udev, root_portnr);
if (setup->request == USB_REQ_SET_CONFIGURATION) { ret = xhci_set_configuration(udev); @@ -1007,6 +1009,19 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) return 0; }
+int submit_control_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, struct devrequest *setup) +{ + struct usb_device *hop = udev; + + if (hop->parent) + while (hop->parent->parent) + hop = hop->parent; + + return _xhci_submit_control_msg(udev, pipe, buffer, length, setup, + hop->portnr); +} + /** * Stops the XHCI host controller * and cleans up all the related data structures diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 00faa81..916a5a4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1232,7 +1232,8 @@ void xhci_endpoint_copy(struct xhci_ctrl *ctrl, void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx, struct xhci_container_ctx *out_ctx); -void xhci_setup_addressable_virt_dev(struct usb_device *udev); +void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, int slot_id, + int speed, int hop_portnr); void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, u32 slot_id, u32 ep_index, trb_type cmd); void xhci_acknowledge_event(struct xhci_ctrl *ctrl);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This function should not be delving into struct usb_device. Pass in the parameters it needs directly.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/xhci-mem.c | 19 +++++++------------ drivers/usb/host/xhci.c | 29 ++++++++++++++++++++++------- drivers/usb/host/xhci.h | 3 ++- 3 files changed, 31 insertions(+), 20 deletions(-)
Applied to u-boot-dm/next.

Since driver model will want to use most of the same code for XHCI init and uninit, put it in a separate function.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/xhci.c | 85 ++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 33 deletions(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 774c3e6..4d94618 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -936,33 +936,16 @@ static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe, return xhci_ctrl_tx(udev, pipe, setup, length, buffer); }
-/** - * Intialises the XHCI host controller - * and allocates the necessary data structures - * - * @param index index to the host controller data structure - * @return pointer to the intialised controller - */ -int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +static int xhci_lowlevel_init(struct xhci_ctrl *ctrl) { + struct xhci_hccr *hccr; + struct xhci_hcor *hcor; uint32_t val; uint32_t val2; uint32_t reg; - struct xhci_hccr *hccr; - struct xhci_hcor *hcor; - struct xhci_ctrl *ctrl; - - if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0) - return -ENODEV; - - if (xhci_reset(hcor) != 0) - return -ENODEV; - - ctrl = &xhcic[index]; - - ctrl->hccr = hccr; - ctrl->hcor = hcor;
+ hccr = ctrl->hccr; + hcor = ctrl->hcor; /* * Program the Number of Device Slots Enabled field in the CONFIG * register with the max value of slots the HC can handle. @@ -1004,7 +987,20 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) reg = HC_VERSION(xhci_readl(&hccr->cr_capbase)); printf("USB XHCI %x.%02x\n", reg >> 8, reg & 0xff);
- *controller = &xhcic[index]; + return 0; +} + +static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl) +{ + u32 temp; + + xhci_reset(ctrl->hcor); + + debug("// Disabling event ring interrupts\n"); + temp = xhci_readl(&ctrl->hcor->or_usbsts); + xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT); + temp = xhci_readl(&ctrl->ir_set->irq_pending); + xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp));
return 0; } @@ -1023,6 +1019,38 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe, }
/** + * Intialises the XHCI host controller + * and allocates the necessary data structures + * + * @param index index to the host controller data structure + * @return pointer to the intialised controller + */ +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + struct xhci_hccr *hccr; + struct xhci_hcor *hcor; + struct xhci_ctrl *ctrl; + int ret; + + if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0) + return -ENODEV; + + if (xhci_reset(hcor) != 0) + return -ENODEV; + + ctrl = &xhcic[index]; + + ctrl->hccr = hccr; + ctrl->hcor = hcor; + + ret = xhci_lowlevel_init(ctrl); + + *controller = &xhcic[index]; + + return ret; +} + +/** * Stops the XHCI host controller * and cleans up all the related data structures * @@ -1032,18 +1060,9 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe, int usb_lowlevel_stop(int index) { struct xhci_ctrl *ctrl = (xhcic + index); - u32 temp; - - xhci_reset(ctrl->hcor); - - debug("// Disabling event ring interrupts\n"); - temp = xhci_readl(&ctrl->hcor->or_usbsts); - xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT); - temp = xhci_readl(&ctrl->ir_set->irq_pending); - xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp));
+ xhci_lowlevel_stop(ctrl); xhci_hcd_stop(index); - xhci_cleanup(ctrl);
return 0;

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Since driver model will want to use most of the same code for XHCI init and uninit, put it in a separate function.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/xhci.c | 85 ++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 33 deletions(-)
Applied to u-boot-dm/next.

Add driver model support in the XHCI support code so that it can be used by XHCI USB drivers.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/xhci-mem.c | 1 + drivers/usb/host/xhci.c | 179 ++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci.h | 24 ++++++ 3 files changed, 197 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 1b55534..79cb17e 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -15,6 +15,7 @@ */
#include <common.h> +#include <dm.h> #include <asm/byteorder.h> #include <usb.h> #include <malloc.h> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4d94618..6d9d4a6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -21,6 +21,7 @@ */
#include <common.h> +#include <dm.h> #include <asm/byteorder.h> #include <usb.h> #include <malloc.h> @@ -108,11 +109,24 @@ static struct descriptor { }, };
+#ifndef CONFIG_DM_USB static struct xhci_ctrl xhcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; +#endif
struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev) { +#ifdef CONFIG_DM_USB + struct udevice *dev; + + /* Find the USB controller */ + for (dev = udev->dev; + device_get_uclass_id(dev) != UCLASS_USB; + dev = dev->parent) + ; + return dev_get_priv(dev); +#else return udev->controller; +#endif }
/** @@ -467,7 +481,7 @@ static int xhci_address_device(struct usb_device *udev, int root_portnr) * @param udev pointer to the Device Data Structure * @return Returns 0 on succes else return error code on failure */ -int usb_alloc_device(struct usb_device *udev) +int _xhci_alloc_device(struct usb_device *udev) { struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); union xhci_trb *event; @@ -505,6 +519,13 @@ int usb_alloc_device(struct usb_device *udev) return 0; }
+#ifndef CONFIG_DM_USB +int usb_alloc_device(struct usb_device *udev) +{ + return _xhci_alloc_device(udev); +} +#endif + /* * Full speed devices may have a max packet size greater than 8 bytes, but the * USB core doesn't know that until it reads the first 8 bytes of the @@ -864,9 +885,8 @@ unknown: * @param interval interval of the interrupt * @return 0 */ -int -submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, - int length, int interval) +static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, int interval) { /* * TODO: Not addressing any interrupt type transfer requests @@ -884,9 +904,8 @@ submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, * @param length length of the buffer * @return returns 0 if successful else -1 on failure */ -int -submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, - int length) +static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int length) { if (usb_pipetype(pipe) != PIPE_BULK) { printf("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); @@ -1005,6 +1024,7 @@ static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl) return 0; }
+#ifndef CONFIG_DM_USB int submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer, int length, struct devrequest *setup) { @@ -1018,6 +1038,18 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe, hop->portnr); }
+int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length) +{ + return _xhci_submit_bulk_msg(udev, pipe, buffer, length); +} + +int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, + int length, int interval) +{ + return _xhci_submit_int_msg(udev, pipe, buffer, length, interval); +} + /** * Intialises the XHCI host controller * and allocates the necessary data structures @@ -1067,3 +1099,136 @@ int usb_lowlevel_stop(int index)
return 0; } +#endif /* CONFIG_DM_USB */ + +#ifdef CONFIG_DM_USB +/* +static struct usb_device *get_usb_device(struct udevice *dev) +{ + struct usb_device *udev; + + if (device_get_uclass_id(dev) == UCLASS_USB) + udev = dev_get_uclass_priv(dev); + else + udev = dev_get_parentdata(dev); + + return udev; +} +*/ +static bool is_root_hub(struct udevice *dev) +{ + if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) + return true; + + return false; +} + +static int xhci_submit_control_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup) +{ + struct usb_device *uhop; + struct udevice *hub; + int root_portnr = 0; + + debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__, + dev->name, udev, udev->dev->name, udev->portnr); + hub = udev->dev; + if (device_get_uclass_id(hub) == UCLASS_USB_HUB) { + /* Figure out our port number on the root hub */ + if (is_root_hub(hub)) { + root_portnr = udev->portnr; + } else { + while (!is_root_hub(hub->parent)) + hub = hub->parent; + uhop = dev_get_parentdata(hub); + root_portnr = uhop->portnr; + } + } +/* + struct usb_device *hop = udev; + + if (hop->parent) + while (hop->parent->parent) + hop = hop->parent; +*/ + return _xhci_submit_control_msg(udev, pipe, buffer, length, setup, + root_portnr); +} + +static int xhci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _xhci_submit_bulk_msg(udev, pipe, buffer, length); +} + +static int xhci_submit_int_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _xhci_submit_int_msg(udev, pipe, buffer, length, interval); +} + +static int xhci_alloc_device(struct udevice *dev, struct usb_device *udev) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _xhci_alloc_device(udev); +} + +int xhci_register(struct udevice *dev, struct xhci_hccr *hccr, + struct xhci_hcor *hcor) +{ + struct xhci_ctrl *ctrl = dev_get_priv(dev); + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); + int ret; + + debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p\n", __func__, dev->name, + ctrl, hccr, hcor); + + ctrl->dev = dev; + + /* + * XHCI needs to issue a Address device command to setup + * proper device context structures, before it can interact + * with the device. So a get_descriptor will fail before any + * of that is done for XHCI unlike EHCI. + */ + priv->desc_before_addr = false; + + ret = xhci_reset(hcor); + if (ret) + goto err; + + ctrl->hccr = hccr; + ctrl->hcor = hcor; + ret = xhci_lowlevel_init(ctrl); + if (ret) + goto err; + + return 0; +err: + free(ctrl); + debug("%s: failed, ret=%d\n", __func__, ret); + return ret; +} + +int xhci_deregister(struct udevice *dev) +{ + struct xhci_ctrl *ctrl = dev_get_priv(dev); + + xhci_lowlevel_stop(ctrl); + xhci_cleanup(ctrl); + + return 0; +} + +struct dm_usb_ops xhci_usb_ops = { + .control = xhci_submit_control_msg, + .bulk = xhci_submit_bulk_msg, + .interrupt = xhci_submit_int_msg, + .alloc_device = xhci_alloc_device, +}; + +#endif diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 916a5a4..7f7cb75 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1200,6 +1200,9 @@ void xhci_hcd_stop(int index); #define XHCI_STS_CNR (1 << 11)
struct xhci_ctrl { +#ifdef CONFIG_DM_USB + struct udevice *dev; +#endif struct xhci_hccr *hccr; /* R/O registers, not need for volatile */ struct xhci_hcor *hcor; struct xhci_doorbell_array *dba; @@ -1251,6 +1254,27 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id); int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, struct xhci_hcor *hcor);
+/** + * xhci_deregister() - Unregister an XHCI controller + * + * @dev: Controller device + * @return 0 if registered, -ve on error + */ +int xhci_deregister(struct udevice *dev); + +/** + * xhci_register() - Register a new XHCI controller + * + * @dev: Controller device + * @hccr: Host controller control registers + * @hcor: Not sure what this means + * @return 0 if registered, -ve on error + */ +int xhci_register(struct udevice *dev, struct xhci_hccr *hccr, + struct xhci_hcor *hcor); + +extern struct dm_usb_ops xhci_usb_ops; + struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev);
#endif /* HOST_XHCI_H_ */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Add driver model support in the XHCI support code so that it can be used by XHCI USB drivers.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/xhci-mem.c | 1 + drivers/usb/host/xhci.c | 179 ++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci.h | 24 ++++++ 3 files changed, 197 insertions(+), 7 deletions(-)
Applied to u-boot-dm/next.

This should be "U-Boot", not "u-boot".
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6d9d4a6..7396c2d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -704,7 +704,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, srclen = 4; break; case 1: /* Vendor String */ - srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; + srcptr = "\16\3U\0-\0B\0o\0o\0t\0"; srclen = 14; break; case 2: /* Product Name */

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
This should be "U-Boot", not "u-boot".
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm/next.

Support driver model in the exynos XHCI driver.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/xhci-exynos5.c | 120 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c index 3f86fdc..23c7ecc 100644 --- a/drivers/usb/host/xhci-exynos5.c +++ b/drivers/usb/host/xhci-exynos5.c @@ -14,6 +14,7 @@ */
#include <common.h> +#include <dm.h> #include <fdtdec.h> #include <libfdt.h> #include <malloc.h> @@ -32,20 +33,76 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR;
+#ifdef CONFIG_DM_USB +struct exynos_xhci_platdata { + fdt_addr_t hcd_base; + fdt_addr_t phy_base; + struct gpio_desc vbus_gpio; +}; +#endif + /** * Contains pointers to register base addresses * for the usb controller. */ struct exynos_xhci { +#ifdef CONFIG_DM_USB + struct usb_platdata usb_plat; +#endif + struct xhci_ctrl ctrl; struct exynos_usb3_phy *usb3_phy; struct xhci_hccr *hcd; struct dwc3 *dwc3_reg; +#ifndef CONFIG_DM_USB struct gpio_desc vbus_gpio; +#endif };
+#ifndef CONFIG_DM_USB static struct exynos_xhci exynos; +#endif
-#ifdef CONFIG_OF_CONTROL +#ifdef CONFIG_DM_USB +static int xhci_usb_ofdata_to_platdata(struct udevice *dev) +{ + struct exynos_xhci_platdata *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + unsigned int node; + int depth; + + /* + * Get the base address for XHCI controller from the device node + */ + plat->hcd_base = fdtdec_get_addr(blob, dev->of_offset, "reg"); + if (plat->hcd_base == FDT_ADDR_T_NONE) { + debug("Can't get the XHCI register base address\n"); + return -ENXIO; + } + + depth = 0; + node = fdtdec_next_compatible_subnode(blob, dev->of_offset, + COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth); + if (node <= 0) { + debug("XHCI: Can't get device node for usb3-phy controller\n"); + return -ENODEV; + } + + /* + * Get the base address for usbphy from the device node + */ + plat->phy_base = fdtdec_get_addr(blob, node, "reg"); + if (plat->phy_base == FDT_ADDR_T_NONE) { + debug("Can't get the usbphy register address\n"); + return -ENXIO; + } + + /* Vbus gpio */ + gpio_request_by_name(dev, "samsung,vbus-gpio", 0, + &plat->vbus_gpio, GPIOD_IS_OUT); + + return 0; +} +#else static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) { fdt_addr_t addr; @@ -283,6 +340,7 @@ static void exynos_xhci_core_exit(struct exynos_xhci *exynos) exynos5_usb3_phy_exit(exynos->usb3_phy); }
+#ifndef CONFIG_DM_USB int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) { struct exynos_xhci *ctx = &exynos; @@ -326,3 +384,63 @@ void xhci_hcd_stop(int index)
exynos_xhci_core_exit(ctx); } +#endif + +#ifdef CONFIG_DM_USB +static int xhci_usb_probe(struct udevice *dev) +{ + struct exynos_xhci_platdata *plat = dev_get_platdata(dev); + struct exynos_xhci *ctx = dev_get_priv(dev); + struct xhci_hcor *hcor; + int ret; + + ctx->hcd = (struct xhci_hccr *)plat->hcd_base; + ctx->usb3_phy = (struct exynos_usb3_phy *)plat->phy_base; + ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); + hcor = (struct xhci_hcor *)((uint32_t)ctx->hcd + + HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase))); + + /* setup the Vbus gpio here */ + if (dm_gpio_is_valid(&plat->vbus_gpio)) + dm_gpio_set_value(&plat->vbus_gpio, 1); + + ret = exynos_xhci_core_init(ctx); + if (ret) { + puts("XHCI: failed to initialize controller\n"); + return -EINVAL; + } + + return xhci_register(dev, ctx->hcd, hcor); +} + +static int xhci_usb_remove(struct udevice *dev) +{ + struct exynos_xhci *ctx = dev_get_priv(dev); + int ret; + + ret = xhci_deregister(dev); + if (ret) + return ret; + exynos_xhci_core_exit(ctx); + + return 0; +} + +static const struct udevice_id xhci_usb_ids[] = { + { .compatible = "samsung,exynos5250-xhci" }, + { } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "xhci_exynos", + .id = UCLASS_USB, + .of_match = xhci_usb_ids, + .ofdata_to_platdata = xhci_usb_ofdata_to_platdata, + .probe = xhci_usb_probe, + .remove = xhci_usb_remove, + .ops = &xhci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct exynos_xhci_platdata), + .priv_auto_alloc_size = sizeof(struct exynos_xhci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Support driver model in the exynos XHCI driver.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/xhci-exynos5.c | 120 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-)
Applied to u-boot-dm/next.

Convert Exynos boards over to use driver model for USB. This does not remove any unnecessary code so far.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
arch/arm/cpu/armv7/exynos/Kconfig | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/arm/cpu/armv7/exynos/Kconfig b/arch/arm/cpu/armv7/exynos/Kconfig index eb86a7f..b042fba 100644 --- a/arch/arm/cpu/armv7/exynos/Kconfig +++ b/arch/arm/cpu/armv7/exynos/Kconfig @@ -86,6 +86,9 @@ config SYS_MALLOC_F config SYS_MALLOC_F_LEN default 0x400
+config DM_USB + default y + source "board/samsung/smdkv310/Kconfig" source "board/samsung/trats/Kconfig" source "board/samsung/universal_c210/Kconfig"

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Convert Exynos boards over to use driver model for USB. This does not remove any unnecessary code so far.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
arch/arm/cpu/armv7/exynos/Kconfig | 3 +++ 1 file changed, 3 insertions(+)
Applied to u-boot-dm/next.

Switch snow over to use both EHCI and XHCI at the same time.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
include/configs/snow.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/configs/snow.h b/include/configs/snow.h index fe802f2..e024955 100644 --- a/include/configs/snow.h +++ b/include/configs/snow.h @@ -26,7 +26,9 @@ #define CONFIG_ARCH_EARLY_INIT_R
#define CONFIG_USB_XHCI +#define CONFIG_USB_EHCI #define CONFIG_USB_XHCI_EXYNOS +#define CONFIG_USB_EHCI_EXYNOS
#define CONFIG_SYS_PROMPT "snow # " #define CONFIG_IDENT_STRING " for snow"

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Switch snow over to use both EHCI and XHCI at the same time.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
include/configs/snow.h | 2 ++ 1 file changed, 2 insertions(+)
Applied to u-boot-dm/next.

Since we can support both controllers now, enable this in the device tree.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
arch/arm/dts/exynos5250-snow.dts | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index d6777ee..668961a 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -133,6 +133,7 @@
ehci@12110000 { samsung,vbus-gpio = <&gpx1 1 GPIO_ACTIVE_HIGH>; + status = "okay"; };
xhci@12000000 {

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Since we can support both controllers now, enable this in the device tree.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
arch/arm/dts/exynos5250-snow.dts | 1 + 1 file changed, 1 insertion(+)
Applied to u-boot-dm/next.

Move Tegra boards over to driver model for USB EHCI. There are a few things that are no-longer needed (e.g. in fdtdec.h) but these will be cleaned up in a later patch to be applied one we are confident this change is fully tested.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
arch/arm/mach-tegra/Kconfig | 3 +++ board/nvidia/common/board.c | 2 ++ 2 files changed, 5 insertions(+)
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index fccfd79..3625035 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -47,6 +47,9 @@ config DM_I2C config DM_GPIO default y
+config DM_USB + default y + source "arch/arm/mach-tegra/tegra20/Kconfig" source "arch/arm/mach-tegra/tegra30/Kconfig" source "arch/arm/mach-tegra/tegra114/Kconfig" diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index 018dddb..a97035c 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -128,8 +128,10 @@ int board_init(void)
#ifdef CONFIG_USB_EHCI_TEGRA pin_mux_usb(); +#ifndef CONFIG_DM_USB usb_process_devicetree(gd->fdt_blob); #endif +#endif
#ifdef CONFIG_LCD tegra_lcd_check_next_stage(gd->fdt_blob, 0);

On 25 March 2015 at 12:22, Simon Glass sjg@chromium.org wrote:
Move Tegra boards over to driver model for USB EHCI. There are a few things that are no-longer needed (e.g. in fdtdec.h) but these will be cleaned up in a later patch to be applied one we are confident this change is fully tested.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
arch/arm/mach-tegra/Kconfig | 3 +++ board/nvidia/common/board.c | 2 ++ 2 files changed, 5 insertions(+)
Applied to u-boot-dm/next.

This is useful for creating lists of descriptors, and is better than using void * for this purpose.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
include/linux/usb/ch9.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 10675b4..822fca0 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -379,6 +379,11 @@ struct usb_endpoint_descriptor { #define USB_DT_ENDPOINT_SIZE 7 #define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
+/* Used to access common fields */ +struct usb_generic_descriptor { + __u8 bLength; + __u8 bDescriptorType; +};
/* * Endpoints

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
This is useful for creating lists of descriptors, and is better than using void * for this purpose.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
include/linux/usb/ch9.h | 5 +++++ 1 file changed, 5 insertions(+)
Applied to u-boot-dm/next.

Add a few more shifts/masks to make it easier to decode a pipe value (rather than just building it). We need this for USB device emulation.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
include/usb_defs.h | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/include/usb_defs.h b/include/usb_defs.h index 27ddc47..8214ba9 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -165,12 +165,14 @@ #define USB_TEST_MODE_FORCE_ENABLE 0x05
-/* "pipe" definitions */ - -#define PIPE_ISOCHRONOUS 0 -#define PIPE_INTERRUPT 1 -#define PIPE_CONTROL 2 -#define PIPE_BULK 3 +/* + * "pipe" definitions, use unsigned so we can compare reliably, since this + * value is shifted up to bits 30/31. + */ +#define PIPE_ISOCHRONOUS 0U +#define PIPE_INTERRUPT 1U +#define PIPE_CONTROL 2U +#define PIPE_BULK 3U #define PIPE_DEVEP_MASK 0x0007ff00
#define USB_ISOCHRONOUS 0 @@ -178,6 +180,15 @@ #define USB_CONTROL 2 #define USB_BULK 3
+#define USB_PIPE_TYPE_SHIFT 30 +#define USB_PIPE_TYPE_MASK (3 << USB_PIPE_TYPE_SHIFT) + +#define USB_PIPE_DEV_SHIFT 8 +#define USB_PIPE_DEV_MASK (0x7f << USB_PIPE_DEV_SHIFT) + +#define USB_PIPE_EP_SHIFT 15 +#define USB_PIPE_EP_MASK (0xf << USB_PIPE_EP_SHIFT) + /* USB-status codes: */ #define USB_ST_ACTIVE 0x1 /* TD is active */ #define USB_ST_STALLED 0x2 /* TD is stalled */

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
Add a few more shifts/masks to make it easier to decode a pipe value (rather than just building it). We need this for USB device emulation.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
include/usb_defs.h | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
Applied to u-boot-dm/next.

Enable USB emulation and associated features so that USB can be used in sandbox.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
configs/sandbox_defconfig | 4 ++++ include/configs/sandbox.h | 2 ++ 2 files changed, 6 insertions(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index e23b959..3f4812c 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -15,3 +15,7 @@ CONFIG_CMD_CROS_EC=y CONFIG_PCI=y CONFIG_DM_PCI=y CONFIG_PCI_SANDBOX=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_EMUL=y +CONFIG_USB_STORAGE=y diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 558ea2c..95cfce6 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -60,6 +60,7 @@ #define CONFIG_CMD_GPT #define CONFIG_PARTITION_UUIDS #define CONFIG_EFI_PARTITION +#define CONFIG_DOS_PARTITION
/* * Size of malloc() pool, before and after relocation @@ -200,5 +201,6 @@ #define CONFIG_TPM_TIS_SANDBOX
#define CONFIG_CMD_LZMADEC +#define CONFIG_CMD_USB
#endif

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
Enable USB emulation and associated features so that USB can be used in sandbox.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
configs/sandbox_defconfig | 4 ++++ include/configs/sandbox.h | 2 ++ 2 files changed, 6 insertions(+)
Applied to u-boot-dm/next.

Neither the hyphen nor the equals sign is needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
test/dm/cmd_dm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/dm/cmd_dm.c b/test/dm/cmd_dm.c index 8d531fd..62e065c 100644 --- a/test/dm/cmd_dm.c +++ b/test/dm/cmd_dm.c @@ -79,7 +79,7 @@ static void dm_display_line(struct udevice *dev) dev->flags & DM_FLAG_ACTIVATED ? '*' : ' ', dev->name, (ulong)map_to_sysmem(dev)); if (dev->seq != -1 || dev->req_seq != -1) - printf(", seq-%d, (req=%d)", dev->seq, dev->req_seq); + printf(", seq %d, (req %d)", dev->seq, dev->req_seq); puts("\n"); }

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
Neither the hyphen nor the equals sign is needed.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
test/dm/cmd_dm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm/next.

As well as running all tests, it is useful to be able to run a selected test.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
include/dm/test.h | 7 ++++--- test/dm/cmd_dm.c | 11 ++++++++--- test/dm/test-main.c | 7 +++++-- 3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/include/dm/test.h b/include/dm/test.h index b310e5f..9c4b8d3 100644 --- a/include/dm/test.h +++ b/include/dm/test.h @@ -205,12 +205,13 @@ void dm_leak_check_start(struct dm_test_state *dms);
/** - * dm_test_main() - Run all the tests + * dm_test_main() - Run all or one of the tests * - * This runs all available driver model tests + * This runs all available driver model tests, or a selected one * + * @test_name: Name of test to run, or NULL for all * @return 0 if OK, -ve on error */ -int dm_test_main(void); +int dm_test_main(const char *test_name);
#endif diff --git a/test/dm/cmd_dm.c b/test/dm/cmd_dm.c index 62e065c..2f527e9 100644 --- a/test/dm/cmd_dm.c +++ b/test/dm/cmd_dm.c @@ -113,7 +113,12 @@ static int do_dm_dump_uclass(cmd_tbl_t *cmdtp, int flag, int argc, static int do_dm_test(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - return dm_test_main(); + const char *test_name = NULL; + + if (argc > 0) + test_name = argv[0]; + + return dm_test_main(test_name); } #define TEST_HELP "\ndm test Run tests" #else @@ -133,7 +138,7 @@ static int do_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) cmd_tbl_t *test_cmd; int ret;
- if (argc != 2) + if (argc < 2) return CMD_RET_USAGE; test_cmd = find_cmd_tbl(argv[1], test_commands, ARRAY_SIZE(test_commands)); @@ -148,7 +153,7 @@ static int do_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
U_BOOT_CMD( - dm, 2, 1, do_dm, + dm, 3, 1, do_dm, "Driver model low level access", "tree Dump driver model tree ('*' = activated)\n" "dm uclass Dump list of instances for each uclass" diff --git a/test/dm/test-main.c b/test/dm/test-main.c index 90ca810..a47bb37 100644 --- a/test/dm/test-main.c +++ b/test/dm/test-main.c @@ -65,7 +65,7 @@ static int dm_test_destroy(struct dm_test_state *dms) return 0; }
-int dm_test_main(void) +int dm_test_main(const char *test_name) { struct dm_test *tests = ll_entry_start(struct dm_test, dm_test); const int n_ents = ll_entry_count(struct dm_test, dm_test); @@ -83,9 +83,12 @@ int dm_test_main(void) ut_assert(gd->fdt_blob); }
- printf("Running %d driver model tests\n", n_ents); + if (!test_name) + printf("Running %d driver model tests\n", n_ents);
for (test = tests; test < tests + n_ents; test++) { + if (test_name && strcmp(test_name, test->name)) + continue; printf("Test: %s\n", test->name); ut_assertok(dm_test_init(dms));

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
As well as running all tests, it is useful to be able to run a selected test.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
include/dm/test.h | 7 ++++--- test/dm/cmd_dm.c | 11 ++++++++--- test/dm/test-main.c | 7 +++++-- 3 files changed, 17 insertions(+), 8 deletions(-)
Applied to u-boot-dm/next.

This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
test/dm/Makefile | 1 + test/dm/test-dm.sh | 3 +++ test/dm/test.dts | 41 +++++++++++++++++++++++++++++++++++++++++ test/dm/usb.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 test/dm/usb.c
diff --git a/test/dm/Makefile b/test/dm/Makefile index a2e2d23..fd9e29f 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -23,4 +23,5 @@ obj-$(CONFIG_DM_I2C) += i2c.o obj-$(CONFIG_DM_PCI) += pci.o obj-$(CONFIG_DM_SPI_FLASH) += sf.o obj-$(CONFIG_DM_SPI) += spi.o +obj-$(CONFIG_DM_USB) += usb.o endif diff --git a/test/dm/test-dm.sh b/test/dm/test-dm.sh index 8ebc392..6158f68 100755 --- a/test/dm/test-dm.sh +++ b/test/dm/test-dm.sh @@ -10,5 +10,8 @@ dtc -I dts -O dtb test/dm/test.dts -o test/dm/test.dtb make O=sandbox sandbox_config || die "Cannot configure U-Boot" make O=sandbox -s -j${NUM_CPUS} || die "Cannot build U-Boot" dd if=/dev/zero of=spi.bin bs=1M count=2 +echo -n "this is a test" > testflash.bin +dd if=/dev/zero bs=1M count=4 >>testflash.bin ./sandbox/u-boot -d test/dm/test.dtb -c "dm test" rm spi.bin +rm testflash.bin diff --git a/test/dm/test.dts b/test/dm/test.dts index 0ab0916..d0c40be 100644 --- a/test/dm/test.dts +++ b/test/dm/test.dts @@ -20,6 +20,9 @@ testfdt8 = "/a-test"; eth0 = "/eth@10002000"; eth5 = ð_5; + usb0 = &usb_0; + usb1 = &usb_1; + usb2 = &usb_2; };
uart0: serial { @@ -186,4 +189,42 @@ fake-host-hwaddr = <0x00 0x00 0x66 0x44 0x22 0x22>; };
+ usb_0: usb@0 { + compatible = "sandbox,usb"; + status = "disabled"; + hub { + compatible = "sandbox,usb-hub"; + #address-cells = <1>; + #size-cells = <0>; + flash-stick { + reg = <0>; + compatible = "sandbox,usb-flash"; + }; + }; + }; + + usb_1: usb@1 { + compatible = "sandbox,usb"; + hub { + compatible = "usb-hub"; + usb,device-class = <9>; + hub-emul { + compatible = "sandbox,usb-hub"; + #address-cells = <1>; + #size-cells = <0>; + flash-stick { + reg = <0>; + compatible = "sandbox,usb-flash"; + sandbox,filepath = "testflash.bin"; + }; + + }; + }; + }; + + usb_2: usb@2 { + compatible = "sandbox,usb"; + status = "disabled"; + }; + }; diff --git a/test/dm/usb.c b/test/dm/usb.c new file mode 100644 index 0000000..6ea86d7 --- /dev/null +++ b/test/dm/usb.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <usb.h> +#include <asm/io.h> +#include <dm/test.h> +#include <dm/ut.h> + +/* Test that sandbox USB works correctly */ +static int dm_test_usb_base(struct dm_test_state *dms) +{ + struct udevice *bus; + + ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 0, &bus)); + ut_assertok(uclass_get_device(UCLASS_USB, 0, &bus)); + ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 2, &bus)); + + return 0; +} +DM_TEST(dm_test_usb_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* + * Test that we can use the flash stick. This is more of a functional test. It + * covers scanning the bug, setting up a hub and a flash stick and reading + * data from the flash stick. + */ +static int dm_test_usb_flash(struct dm_test_state *dms) +{ + struct udevice *dev; + block_dev_desc_t *dev_desc; + char cmp[1024]; + + ut_assertok(usb_init()); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev)); + ut_assertok(get_device("usb", "0", &dev_desc)); + + /* Read a few blocks and look for the string we expect */ + ut_asserteq(512, dev_desc->blksz); + memset(cmp, '\0', sizeof(cmp)); + ut_asserteq(2, dev_desc->block_read(dev_desc->dev, 0, 2, cmp)); + ut_assertok(strcmp(cmp, "this is a test")); + + return 0; +} +DM_TEST(dm_test_usb_flash, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
test/dm/Makefile | 1 + test/dm/test-dm.sh | 3 +++ test/dm/test.dts | 41 +++++++++++++++++++++++++++++++++++++++++ test/dm/usb.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 test/dm/usb.c
Applied to u-boot-dm/next.

Hi Simon,
On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote:
This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
I'm seeing a seg fault when running the dm tests and bisected it to this patch.
I'm not sure why it's related, but it appears to seg fault on a GPIO test...
U-Boot 2015.04-00280-ge00cb22-dirty (Apr 21 2015 - 00:02:01)
DRAM: 128 MiB Using default environment
In: serial Out: lcd Err: lcd Net: eth0: eth@10002000, eth5: eth@10003000, eth1: eth@10004000 Running 53 driver model tests Test: dm_test_autobind Test: dm_test_autoprobe Test: dm_test_bus_child_post_bind Test: dm_test_bus_child_post_bind_uclass Test: dm_test_bus_child_pre_probe_uclass Test: dm_test_bus_children Device 'c-test@0': seq 0 is in use by 'd-test' Device 'c-test@1': seq 1 is in use by 'f-test' Test: dm_test_bus_children_funcs Test: dm_test_bus_children_iterators Test: dm_test_bus_parent_data Test: dm_test_bus_parent_data_uclass Test: dm_test_bus_parent_ops Test: dm_test_bus_parent_platdata Test: dm_test_bus_parent_platdata_uclass Test: dm_test_children Test: dm_test_device_get_uclass_id Test: dm_test_eth Using eth@10002000 device Using eth@10003000 device Using eth@10004000 device Test: dm_test_eth_alias Using eth@10002000 device Using eth@10004000 device Using eth@10002000 device Using eth@10003000 device Test: dm_test_eth_prime Using eth@10003000 device Using eth@10002000 device Test: dm_test_eth_rotate
Error: eth@10004000 address not set.
Error: eth@10004000 address not set. Using eth@10002000 device
Error: eth@10004000 address not set.
Error: eth@10004000 address not set. Using eth@10004000 device Test: dm_test_fdt Test: dm_test_fdt_offset Test: dm_test_fdt_pre_reloc Test: dm_test_fdt_uclass_seq Test: dm_test_gpio extra-gpios: get_value: error: gpio b5 not reserved Test: dm_test_gpio_anon Test: dm_test_gpio_copy Test: dm_test_gpio_leak extra-gpios: get_value: error: gpio b5 not reserved
Program received signal SIGSEGV, Segmentation fault. 0x000009ec in ?? () (gdb) bt #0 0x000009ec in ?? () #1 0x0806a0aa in uclass_destroy (uc=0xb5abd228) at /home/joe/u-boot/drivers/core/uclass.c:109 #2 0x080a29e1 in dm_leak_check_end (dms=0x8106870) at /home/joe/u-boot/test/dm/core.c:89 #3 0x080a46a6 in dm_test_gpio_leak (dms=0x8106870) at /home/joe/u-boot/test/dm/gpio.c:173 #4 0x080a0ed2 in dm_test_main (test_name=0x0) at /home/joe/u-boot/test/dm/test-main.c:103 #5 0x0809e9fb in do_dm (cmdtp=0x80c0250, flag=0, argc=135022648, argv=0xb5abbd40) at /home/joe/u-boot/test/dm/cmd_dm.c:150 #6 0x08065d6b in cmd_process (flag=0, argc=2, argv=0xb5abbd40, repeatable=0x80c5fc4, ticks=0x0) at /home/joe/u-boot/common/command.c:493 #7 0x0804d6fb in run_list_real (pi=0xb5abbc88) at /home/joe/u-boot/common/cli_hush.c:1656 #8 0x0804dce4 in parse_stream_outer (inp=0xbffff0e8, flag=2) at /home/joe/u-boot/common/cli_hush.c:2003 #9 0x0804df1d in parse_string_outer (s=0xbffff513 "dm test", flag=2) at /home/joe/u-boot/common/cli_hush.c:3248 #10 0x0804a855 in sandbox_main_loop_init () at /home/joe/u-boot/arch/sandbox/cpu/start.c:85 #11 0x0804e65b in run_main_loop () at /home/joe/u-boot/common/board_r.c:682 #12 0x0808f082 in initcall_run_list (init_sequence=0x80c1f68) at /home/joe/u-boot/lib/initcall.c:27 #13 0x0804e798 in board_init_r (new_gd=0xb5ab9f14, dest_addr=0) at /home/joe/u-boot/common/board_r.c:916 #14 0x0804a810 in main (argc=Cannot access memory at address 0x0 ) at /home/joe/u-boot/arch/sandbox/cpu/start.c:276 (gdb) f 1 #1 0x0806a0aa in uclass_destroy (uc=0xb5abd228) at /home/joe/u-boot/drivers/core/uclass.c:109 109 ret = device_unbind(dev); (gdb) l - 99 int uclass_destroy(struct uclass *uc) 100 { 101 struct uclass_driver *uc_drv; 102 struct udevice *dev, *tmp; 103 int ret; 104 105 list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) { 106 ret = device_remove(dev); 107 if (ret) 108 return ret; (gdb) l 109 ret = device_unbind(dev); 110 if (ret) 111 return ret; 112 } 113 114 uc_drv = uc->uc_drv; 115 if (uc_drv->destroy) 116 uc_drv->destroy(uc); 117 list_del(&uc->sibling_node); 118 if (uc_drv->priv_auto_alloc_size) (gdb)
Thoughts? -Joe

Hi Joe,
On 20 April 2015 at 23:24, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote:
This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
I'm seeing a seg fault when running the dm tests and bisected it to this patch.
I'm not sure why it's related, but it appears to seg fault on a GPIO test...
U-Boot 2015.04-00280-ge00cb22-dirty (Apr 21 2015 - 00:02:01)
DRAM: 128 MiB Using default environment
In: serial Out: lcd Err: lcd Net: eth0: eth@10002000, eth5: eth@10003000, eth1: eth@10004000 Running 53 driver model tests Test: dm_test_autobind Test: dm_test_autoprobe Test: dm_test_bus_child_post_bind Test: dm_test_bus_child_post_bind_uclass Test: dm_test_bus_child_pre_probe_uclass Test: dm_test_bus_children Device 'c-test@0': seq 0 is in use by 'd-test' Device 'c-test@1': seq 1 is in use by 'f-test' Test: dm_test_bus_children_funcs Test: dm_test_bus_children_iterators Test: dm_test_bus_parent_data Test: dm_test_bus_parent_data_uclass Test: dm_test_bus_parent_ops Test: dm_test_bus_parent_platdata Test: dm_test_bus_parent_platdata_uclass Test: dm_test_children Test: dm_test_device_get_uclass_id Test: dm_test_eth Using eth@10002000 device Using eth@10003000 device Using eth@10004000 device Test: dm_test_eth_alias Using eth@10002000 device Using eth@10004000 device Using eth@10002000 device Using eth@10003000 device Test: dm_test_eth_prime Using eth@10003000 device Using eth@10002000 device Test: dm_test_eth_rotate
Error: eth@10004000 address not set.
Error: eth@10004000 address not set. Using eth@10002000 device
Error: eth@10004000 address not set.
Error: eth@10004000 address not set. Using eth@10004000 device Test: dm_test_fdt Test: dm_test_fdt_offset Test: dm_test_fdt_pre_reloc Test: dm_test_fdt_uclass_seq Test: dm_test_gpio extra-gpios: get_value: error: gpio b5 not reserved Test: dm_test_gpio_anon Test: dm_test_gpio_copy Test: dm_test_gpio_leak extra-gpios: get_value: error: gpio b5 not reserved
Program received signal SIGSEGV, Segmentation fault. 0x000009ec in ?? () (gdb) bt #0 0x000009ec in ?? () #1 0x0806a0aa in uclass_destroy (uc=0xb5abd228) at /home/joe/u-boot/drivers/core/uclass.c:109 #2 0x080a29e1 in dm_leak_check_end (dms=0x8106870) at /home/joe/u-boot/test/dm/core.c:89 #3 0x080a46a6 in dm_test_gpio_leak (dms=0x8106870) at /home/joe/u-boot/test/dm/gpio.c:173 #4 0x080a0ed2 in dm_test_main (test_name=0x0) at /home/joe/u-boot/test/dm/test-main.c:103 #5 0x0809e9fb in do_dm (cmdtp=0x80c0250, flag=0, argc=135022648, argv=0xb5abbd40) at /home/joe/u-boot/test/dm/cmd_dm.c:150 #6 0x08065d6b in cmd_process (flag=0, argc=2, argv=0xb5abbd40, repeatable=0x80c5fc4, ticks=0x0) at /home/joe/u-boot/common/command.c:493 #7 0x0804d6fb in run_list_real (pi=0xb5abbc88) at /home/joe/u-boot/common/cli_hush.c:1656 #8 0x0804dce4 in parse_stream_outer (inp=0xbffff0e8, flag=2) at /home/joe/u-boot/common/cli_hush.c:2003 #9 0x0804df1d in parse_string_outer (s=0xbffff513 "dm test", flag=2) at /home/joe/u-boot/common/cli_hush.c:3248 #10 0x0804a855 in sandbox_main_loop_init () at /home/joe/u-boot/arch/sandbox/cpu/start.c:85 #11 0x0804e65b in run_main_loop () at /home/joe/u-boot/common/board_r.c:682 #12 0x0808f082 in initcall_run_list (init_sequence=0x80c1f68) at /home/joe/u-boot/lib/initcall.c:27 #13 0x0804e798 in board_init_r (new_gd=0xb5ab9f14, dest_addr=0) at /home/joe/u-boot/common/board_r.c:916 #14 0x0804a810 in main (argc=Cannot access memory at address 0x0 ) at /home/joe/u-boot/arch/sandbox/cpu/start.c:276 (gdb) f 1 #1 0x0806a0aa in uclass_destroy (uc=0xb5abd228) at /home/joe/u-boot/drivers/core/uclass.c:109 109 ret = device_unbind(dev); (gdb) l - 99 int uclass_destroy(struct uclass *uc) 100 { 101 struct uclass_driver *uc_drv; 102 struct udevice *dev, *tmp; 103 int ret; 104 105 list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) { 106 ret = device_remove(dev); 107 if (ret) 108 return ret; (gdb) l 109 ret = device_unbind(dev); 110 if (ret) 111 return ret; 112 } 113 114 uc_drv = uc->uc_drv; 115 if (uc_drv->destroy) 116 uc_drv->destroy(uc); 117 list_del(&uc->sibling_node); 118 if (uc_drv->priv_auto_alloc_size) (gdb)
Thoughts?
Yes it is broken. I sent a series to fix this recent ('dm: core: Fix up test failures') starting with this patch:
http://patchwork.ozlabs.org/patch/462556/
If you are able to test it that would be good.
Regards, Simon

Hi Simon,
On Tue, Apr 21, 2015 at 8:14 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 20 April 2015 at 23:24, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote:
This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
I'm seeing a seg fault when running the dm tests and bisected it to this patch.
I'm not sure why it's related, but it appears to seg fault on a GPIO test...
<--snip-->
Thoughts?
Yes it is broken. I sent a series to fix this recent ('dm: core: Fix up test failures') starting with this patch:
http://patchwork.ozlabs.org/patch/462556/
If you are able to test it that would be good.
I tested the series, but I still have a USB test failure...
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found /home/joe/u-boot/test/dm/usb.c:45, dm_test_usb_flash(): 2 == dev_desc->block_read(dev_desc->dev, 0, 2, cmp): Expected 2, got 0 """
Have you seen that one?
Thanks, -Joe

Hi Joe,
On 21 April 2015 at 10:05, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 8:14 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 20 April 2015 at 23:24, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote:
This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
I'm seeing a seg fault when running the dm tests and bisected it to this patch.
I'm not sure why it's related, but it appears to seg fault on a GPIO test...
<--snip-->
Thoughts?
Yes it is broken. I sent a series to fix this recent ('dm: core: Fix up test failures') starting with this patch:
http://patchwork.ozlabs.org/patch/462556/
If you are able to test it that would be good.
I tested the series, but I still have a USB test failure...
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found /home/joe/u-boot/test/dm/usb.c:45, dm_test_usb_flash(): 2 == dev_desc->block_read(dev_desc->dev, 0, 2, cmp): Expected 2, got 0 """
Have you seen that one?
No I don't see that. It is saying that it was not able to read 2 512-byte blocks from the testflash.bin file. It should be created by the test script. I just tried it again.
BTW I'd like to get a sandbox network device that works in a purely emulated way (i.e. without any reference to real hardware). Then we could use it for ping tests, etc. and they would run instantly. At present the network tests are quite slow. What do you think?
Regards, Simon

Hi Simon,
On Tue, Apr 21, 2015 at 11:19 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 21 April 2015 at 10:05, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 8:14 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 20 April 2015 at 23:24, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote:
This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
I'm seeing a seg fault when running the dm tests and bisected it to this patch.
I'm not sure why it's related, but it appears to seg fault on a GPIO test...
<--snip-->
Thoughts?
Yes it is broken. I sent a series to fix this recent ('dm: core: Fix up test failures') starting with this patch:
http://patchwork.ozlabs.org/patch/462556/
If you are able to test it that would be good.
I tested the series, but I still have a USB test failure...
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found /home/joe/u-boot/test/dm/usb.c:45, dm_test_usb_flash(): 2 == dev_desc->block_read(dev_desc->dev, 0, 2, cmp): Expected 2, got 0 """
Have you seen that one?
No I don't see that. It is saying that it was not able to read 2 512-byte blocks from the testflash.bin file. It should be created by the test script. I just tried it again.
That makes sense... I wasn't creating that file. D'oh! Working for me now too.
BTW I'd like to get a sandbox network device that works in a purely emulated way (i.e. without any reference to real hardware). Then we could use it for ping tests, etc. and they would run instantly. At present the network tests are quite slow. What do you think?
The tests are all using fully emulated Ethernet... the issue is that the ping test ensures that on timeout an error is returned. Even though it is an emulated MAC, the timeout in the network stack is still there.
I'll work on a patch that adds a way to change the ping timeout to make this faster.
Cheers, -Joe

Hi Joe,
On 21 April 2015 at 10:57, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 11:19 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 21 April 2015 at 10:05, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 8:14 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 20 April 2015 at 23:24, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote:
This adds a simple test for probing and a functional test using the flash stick emulator, which tests a large chunk of the USB stack.
Signed-off-by: Simon Glass sjg@chromium.org
I'm seeing a seg fault when running the dm tests and bisected it to this patch.
I'm not sure why it's related, but it appears to seg fault on a GPIO test...
<--snip-->
Thoughts?
Yes it is broken. I sent a series to fix this recent ('dm: core: Fix up test failures') starting with this patch:
http://patchwork.ozlabs.org/patch/462556/
If you are able to test it that would be good.
I tested the series, but I still have a USB test failure...
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found /home/joe/u-boot/test/dm/usb.c:45, dm_test_usb_flash(): 2 == dev_desc->block_read(dev_desc->dev, 0, 2, cmp): Expected 2, got 0 """
Have you seen that one?
No I don't see that. It is saying that it was not able to read 2 512-byte blocks from the testflash.bin file. It should be created by the test script. I just tried it again.
That makes sense... I wasn't creating that file. D'oh! Working for me now too.
BTW I'd like to get a sandbox network device that works in a purely emulated way (i.e. without any reference to real hardware). Then we could use it for ping tests, etc. and they would run instantly. At present the network tests are quite slow. What do you think?
The tests are all using fully emulated Ethernet... the issue is that the ping test ensures that on timeout an error is returned. Even though it is an emulated MAC, the timeout in the network stack is still there.
I'll work on a patch that adds a way to change the ping timeout to make this faster.
OK thanks for explaining this. Rather than changing the ping timeout, can you look at changing the time? With sandbox it should be possible to adjust the time so that timeouts appear to happen instantly. The arch/sandbox/include/test.h file has some test functions used by various parts of the stack.
Regards, Simon

Hi Simon,
On Tue, Apr 21, 2015 at 12:00 PM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 21 April 2015 at 10:57, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 11:19 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 21 April 2015 at 10:05, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 8:14 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 20 April 2015 at 23:24, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote: > This adds a simple test for probing and a functional test using the flash > stick emulator, which tests a large chunk of the USB stack. > > Signed-off-by: Simon Glass sjg@chromium.org
I'm seeing a seg fault when running the dm tests and bisected it to this patch.
I'm not sure why it's related, but it appears to seg fault on a GPIO test...
<--snip-->
Thoughts?
Yes it is broken. I sent a series to fix this recent ('dm: core: Fix up test failures') starting with this patch:
http://patchwork.ozlabs.org/patch/462556/
If you are able to test it that would be good.
I tested the series, but I still have a USB test failure...
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found /home/joe/u-boot/test/dm/usb.c:45, dm_test_usb_flash(): 2 == dev_desc->block_read(dev_desc->dev, 0, 2, cmp): Expected 2, got 0 """
Have you seen that one?
No I don't see that. It is saying that it was not able to read 2 512-byte blocks from the testflash.bin file. It should be created by the test script. I just tried it again.
That makes sense... I wasn't creating that file. D'oh! Working for me now too.
BTW I'd like to get a sandbox network device that works in a purely emulated way (i.e. without any reference to real hardware). Then we could use it for ping tests, etc. and they would run instantly. At present the network tests are quite slow. What do you think?
The tests are all using fully emulated Ethernet... the issue is that the ping test ensures that on timeout an error is returned. Even though it is an emulated MAC, the timeout in the network stack is still there.
I'll work on a patch that adds a way to change the ping timeout to make this faster.
OK thanks for explaining this. Rather than changing the ping timeout, can you look at changing the time? With sandbox it should be possible to adjust the time so that timeouts appear to happen instantly. The arch/sandbox/include/test.h file has some test functions used by various parts of the stack.
I posted a series that handles the issue as you recommended and called it "test: Speed up test timeouts by advancing time".
I now notice that the only test that takes any time is the USB Flash test.
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found """
It takes about 3 seconds. Is that a timeout too?
Thanks, -Joe

Hi Joe,
On 21 April 2015 at 14:10, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 12:00 PM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 21 April 2015 at 10:57, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 11:19 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 21 April 2015 at 10:05, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Apr 21, 2015 at 8:14 AM, Simon Glass sjg@chromium.org wrote:
Hi Joe,
On 20 April 2015 at 23:24, Joe Hershberger joe.hershberger@gmail.com wrote: > Hi Simon, > > On Wed, Mar 25, 2015 at 1:23 PM, Simon Glass sjg@chromium.org wrote: >> This adds a simple test for probing and a functional test using the flash >> stick emulator, which tests a large chunk of the USB stack. >> >> Signed-off-by: Simon Glass sjg@chromium.org > > I'm seeing a seg fault when running the dm tests and bisected it to this patch. > > I'm not sure why it's related, but it appears to seg fault on a GPIO test...
<--snip-->
> Thoughts?
Yes it is broken. I sent a series to fix this recent ('dm: core: Fix up test failures') starting with this patch:
http://patchwork.ozlabs.org/patch/462556/
If you are able to test it that would be good.
I tested the series, but I still have a USB test failure...
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found /home/joe/u-boot/test/dm/usb.c:45, dm_test_usb_flash(): 2 == dev_desc->block_read(dev_desc->dev, 0, 2, cmp): Expected 2, got 0 """
Have you seen that one?
No I don't see that. It is saying that it was not able to read 2 512-byte blocks from the testflash.bin file. It should be created by the test script. I just tried it again.
That makes sense... I wasn't creating that file. D'oh! Working for me now too.
BTW I'd like to get a sandbox network device that works in a purely emulated way (i.e. without any reference to real hardware). Then we could use it for ping tests, etc. and they would run instantly. At present the network tests are quite slow. What do you think?
The tests are all using fully emulated Ethernet... the issue is that the ping test ensures that on timeout an error is returned. Even though it is an emulated MAC, the timeout in the network stack is still there.
I'll work on a patch that adds a way to change the ping timeout to make this faster.
OK thanks for explaining this. Rather than changing the ping timeout, can you look at changing the time? With sandbox it should be possible to adjust the time so that timeouts appear to happen instantly. The arch/sandbox/include/test.h file has some test functions used by various parts of the stack.
I posted a series that handles the issue as you recommended and called it "test: Speed up test timeouts by advancing time".
I see it. This is great, thank you!
I now notice that the only test that takes any time is the USB Flash test.
""" Test: dm_test_usb_flash USB-1: scanning bus 1 for devices... 2 USB Device(s) found """
It takes about 3 seconds. Is that a timeout too?
Yes. I'll take a look at how you have advanced time - we should do this for USB too.
Regards, Simon

Drop the code that doesn't use driver model for USB.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
board/nvidia/common/board.c | 3 - drivers/usb/host/ehci-tegra.c | 150 ------------------------------------------ include/fdtdec.h | 3 - lib/fdtdec.c | 3 - 4 files changed, 159 deletions(-)
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index a97035c..91bc03b 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -128,9 +128,6 @@ int board_init(void)
#ifdef CONFIG_USB_EHCI_TEGRA pin_mux_usb(); -#ifndef CONFIG_DM_USB - usb_process_devicetree(gd->fdt_blob); -#endif #endif
#ifdef CONFIG_LCD diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 66e5649..bfa53ed 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -35,12 +35,6 @@ DECLARE_GLOBAL_DATA_PTR; #endif #endif
-#ifndef CONFIG_DM_USB -enum { - USB_PORTS_MAX = 3, /* Maximum ports we allow */ -}; -#endif - /* Parameters we need for USB */ enum { PARAM_DIVN, /* PLL FEEDBACK DIVIDer */ @@ -82,9 +76,6 @@ struct fdt_usb { unsigned ulpi:1; /* 1 if port has external ULPI transceiver */ unsigned enabled:1; /* 1 to enable, 0 to disable */ unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */ -#ifndef CONFIG_DM_USB - unsigned initialized:1; /* has this port already been initialized? */ -#endif enum usb_ctlr_type type; enum usb_init_type init_type; enum dr_mode dr_mode; /* dual role mode */ @@ -93,11 +84,6 @@ struct fdt_usb { struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */ };
-#ifndef CONFIG_DM_USB -static struct fdt_usb port[USB_PORTS_MAX]; /* List of valid USB ports */ -static unsigned port_count; /* Number of available ports */ -#endif - /* * This table has USB timing parameters for each Oscillator frequency we * support. There are four sets of values: @@ -173,8 +159,6 @@ static const u8 utmip_elastic_limit = 16; static const u8 utmip_hs_sync_start_delay = 9;
struct fdt_usb_controller { - /* TODO(sjg@chromium.org): Remove when we only use driver model */ - int compat; /* flag to determine whether controller supports hostpc register */ u32 has_hostpc:1; const unsigned *pll_parameter; @@ -182,17 +166,14 @@ struct fdt_usb_controller {
static struct fdt_usb_controller fdt_usb_controllers[USB_CTRL_COUNT] = { { - .compat = COMPAT_NVIDIA_TEGRA20_USB, .has_hostpc = 0, .pll_parameter = (const unsigned *)T20_usb_pll, }, { - .compat = COMPAT_NVIDIA_TEGRA30_USB, .has_hostpc = 1, .pll_parameter = (const unsigned *)T30_usb_pll, }, { - .compat = COMPAT_NVIDIA_TEGRA114_USB, .has_hostpc = 1, .pll_parameter = (const unsigned *)T114_usb_pll, }, @@ -754,12 +735,6 @@ int usb_common_init(struct fdt_usb *config, enum usb_init_type init) return -1; }
-#ifndef CONFIG_DM_USB - /* skip init, if the port is already initialized */ - if (config->initialized && config->init_type == init) - return 0; -#endif - debug("%d, %d\n", config->utmi, config->ulpi); if (config->utmi) ret = init_utmi_usb_controller(config, init); @@ -796,130 +771,6 @@ static const struct ehci_ops tegra_ehci_ops = { .powerup_fixup = tegra_ehci_powerup_fixup, };
-#ifndef CONFIG_DM_USB -/* - * process_usb_nodes() - Process a list of USB nodes, adding them to our list - * of USB ports. - * @blob: fdt blob - * @node_list: list of nodes to process (any <=0 are ignored) - * @count: number of nodes to process - * @id: controller type (enum usb_ctlr_type) - * - * Return: 0 - ok, -1 - error - */ -static int process_usb_nodes(const void *blob, int node_list[], int count, - enum usb_ctlr_type id) -{ - struct fdt_usb config; - int node, i; - int clk_done = 0; - - port_count = 0; - for (i = 0; i < count; i++) { - if (port_count == USB_PORTS_MAX) { - printf("tegrausb: Cannot register more than %d ports\n", - USB_PORTS_MAX); - return -1; - } - - debug("USB %d: ", i); - node = node_list[i]; - if (!node) - continue; - if (fdt_decode_usb(blob, node, &config)) { - debug("Cannot decode USB node %s\n", - fdt_get_name(blob, node, NULL)); - return -1; - } - if (!clk_done) { - config_clock(get_pll_timing( - &fdt_usb_controllers[id])); - clk_done = 1; - } - config.type = id; - config.initialized = 0; - - /* add new USB port to the list of available ports */ - port[port_count++] = config; - } - - return 0; -} - -int usb_process_devicetree(const void *blob) -{ - int node_list[USB_PORTS_MAX]; - int count, err = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(fdt_usb_controllers); i++) { - count = fdtdec_find_aliases_for_id(blob, "usb", - fdt_usb_controllers[i].compat, node_list, - USB_PORTS_MAX); - if (count) { - err = process_usb_nodes(blob, node_list, count, i); - if (err) - printf("%s: Error processing USB node!\n", - __func__); - return err; - } - } - - return err; -} - -/** - * Start up the given port number (ports are numbered from 0 on each board). - * This returns values for the appropriate hccr and hcor addresses to use for - * USB EHCI operations. - * - * @param index port number to start - * @param hccr returns start address of EHCI HCCR registers - * @param hcor returns start address of EHCI HCOR registers - * @return 0 if ok, -1 on error (generally invalid port number) - */ -int ehci_hcd_init(int index, enum usb_init_type init, - struct ehci_hccr **hccr, struct ehci_hcor **hcor) -{ - struct fdt_usb *config; - struct usb_ctlr *usbctlr; - int ret; - - if (index >= port_count) - return -1; - - config = &port[index]; - ehci_set_controller_priv(index, config, &tegra_ehci_ops); - - ret = usb_common_init(config, init); - if (ret) { - printf("tegrausb: Cannot init port %d\n", index); - return ret; - } - - config->initialized = 1; - - usbctlr = config->reg; - *hccr = (struct ehci_hccr *)&usbctlr->cap_length; - *hcor = (struct ehci_hcor *)&usbctlr->usb_cmd; - - return 0; -} - -/* - * Bring down the specified USB controller - */ -int ehci_hcd_stop(int index) -{ - usb_common_uninit(&port[index]); - - port[index].initialized = 0; - - return 0; -} -#endif /* !CONFIG_DM_USB */ - -#ifdef CONFIG_DM_USB static int ehci_usb_ofdata_to_platdata(struct udevice *dev) { struct fdt_usb *priv = dev_get_priv(dev); @@ -987,4 +838,3 @@ U_BOOT_DRIVER(usb_ehci) = { .priv_auto_alloc_size = sizeof(struct fdt_usb), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; -#endif diff --git a/include/fdtdec.h b/include/fdtdec.h index 063a980..c24fed2 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -112,9 +112,6 @@ static inline fdt_size_t fdt_resource_size(const struct fdt_resource *res) */ enum fdt_compat_id { COMPAT_UNKNOWN, - COMPAT_NVIDIA_TEGRA20_USB, /* Tegra20 USB port */ - COMPAT_NVIDIA_TEGRA30_USB, /* Tegra30 USB port */ - COMPAT_NVIDIA_TEGRA114_USB, /* Tegra114 USB port */ COMPAT_NVIDIA_TEGRA20_EMC, /* Tegra20 memory controller */ COMPAT_NVIDIA_TEGRA20_EMC_TABLE, /* Tegra20 memory timing table */ COMPAT_NVIDIA_TEGRA20_KBC, /* Tegra20 Keyboard */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 9fcc1bb..d3e725b 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -21,9 +21,6 @@ DECLARE_GLOBAL_DATA_PTR; #define COMPAT(id, name) name static const char * const compat_names[COMPAT_COUNT] = { COMPAT(UNKNOWN, "<none>"), - COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"), - COMPAT(NVIDIA_TEGRA30_USB, "nvidia,tegra30-ehci"), - COMPAT(NVIDIA_TEGRA114_USB, "nvidia,tegra114-ehci"), COMPAT(NVIDIA_TEGRA20_EMC, "nvidia,tegra20-emc"), COMPAT(NVIDIA_TEGRA20_EMC_TABLE, "nvidia,tegra20-emc-table"), COMPAT(NVIDIA_TEGRA20_KBC, "nvidia,tegra20-kbc"),

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
Drop the code that doesn't use driver model for USB.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
board/nvidia/common/board.c | 3 - drivers/usb/host/ehci-tegra.c | 150 ------------------------------------------ include/fdtdec.h | 3 - lib/fdtdec.c | 3 - 4 files changed, 159 deletions(-)
Applied to u-boot-dm.

Drop the code that doesn't use driver model for USB.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: None
drivers/usb/host/ehci-exynos.c | 117 ---------------------------------------- drivers/usb/host/xhci-exynos5.c | 108 ------------------------------------- include/fdtdec.h | 2 - lib/fdtdec.c | 2 - 4 files changed, 229 deletions(-)
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 86cf631..18e9251 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -25,14 +25,12 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR;
-#ifdef CONFIG_DM_USB struct exynos_ehci_platdata { struct usb_platdata usb_plat; fdt_addr_t hcd_base; fdt_addr_t phy_base; struct gpio_desc vbus_gpio; }; -#endif
/** * Contains pointers to register base addresses @@ -42,16 +40,8 @@ struct exynos_ehci { struct ehci_ctrl ctrl; struct exynos_usb_phy *usb; struct ehci_hccr *hcd; -#ifndef CONFIG_DM_USB - struct gpio_desc vbus_gpio; -#endif };
-#ifndef CONFIG_DM_USB -static struct exynos_ehci exynos; -#endif - -#ifdef CONFIG_DM_USB static int ehci_usb_ofdata_to_platdata(struct udevice *dev) { struct exynos_ehci_platdata *plat = dev_get_platdata(dev); @@ -91,55 +81,6 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
return 0; } -#else -static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) -{ - fdt_addr_t addr; - unsigned int node; - int depth; - - node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_EHCI); - if (node <= 0) { - debug("EHCI: Can't get device node for ehci\n"); - return -ENODEV; - } - - /* - * Get the base address for EHCI controller from the device node - */ - addr = fdtdec_get_addr(blob, node, "reg"); - if (addr == FDT_ADDR_T_NONE) { - debug("Can't get the EHCI register address\n"); - return -ENXIO; - } - - exynos->hcd = (struct ehci_hccr *)addr; - - /* Vbus gpio */ - gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, - &exynos->vbus_gpio, GPIOD_IS_OUT); - - depth = 0; - node = fdtdec_next_compatible_subnode(blob, node, - COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); - if (node <= 0) { - debug("EHCI: Can't get device node for usb-phy controller\n"); - return -ENODEV; - } - - /* - * Get the base address for usbphy from the device node - */ - exynos->usb = (struct exynos_usb_phy *)fdtdec_get_addr(blob, node, - "reg"); - if (exynos->usb == NULL) { - debug("Can't get the usbphy register address\n"); - return -ENXIO; - } - - return 0; -} -#endif
static void exynos5_setup_usb_phy(struct exynos_usb_phy *usb) { @@ -270,63 +211,6 @@ static void reset_usb_phy(struct exynos_usb_phy *usb) set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); }
-#ifndef CONFIG_DM_USB -/* - * EHCI-initialization - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(int index, enum usb_init_type init, - struct ehci_hccr **hccr, struct ehci_hcor **hcor) -{ - struct exynos_ehci *ctx = &exynos; - -#ifdef CONFIG_OF_CONTROL - if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) { - debug("Unable to parse device tree for ehci-exynos\n"); - return -ENODEV; - } -#else - ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy(); - ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci(); -#endif - -#ifdef CONFIG_OF_CONTROL - /* setup the Vbus gpio here */ - if (dm_gpio_is_valid(&ctx->vbus_gpio)) - dm_gpio_set_value(&ctx->vbus_gpio, 1); -#endif - - setup_usb_phy(ctx->usb); - - board_usb_init(index, init); - - *hccr = ctx->hcd; - *hcor = (struct ehci_hcor *)((uint32_t) *hccr - + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - - debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n", - (uint32_t)*hccr, (uint32_t)*hcor, - (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the EHCI host controller. - */ -int ehci_hcd_stop(int index) -{ - struct exynos_ehci *ctx = &exynos; - - reset_usb_phy(ctx->usb); - - return 0; -} -#endif - -#ifdef CONFIG_DM_USB static int ehci_usb_probe(struct udevice *dev) { struct exynos_ehci_platdata *plat = dev_get_platdata(dev); @@ -377,4 +261,3 @@ U_BOOT_DRIVER(usb_ehci) = { .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; -#endif diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c index 23c7ecc..a27a796 100644 --- a/drivers/usb/host/xhci-exynos5.c +++ b/drivers/usb/host/xhci-exynos5.c @@ -33,36 +33,24 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR;
-#ifdef CONFIG_DM_USB struct exynos_xhci_platdata { fdt_addr_t hcd_base; fdt_addr_t phy_base; struct gpio_desc vbus_gpio; }; -#endif
/** * Contains pointers to register base addresses * for the usb controller. */ struct exynos_xhci { -#ifdef CONFIG_DM_USB struct usb_platdata usb_plat; -#endif struct xhci_ctrl ctrl; struct exynos_usb3_phy *usb3_phy; struct xhci_hccr *hcd; struct dwc3 *dwc3_reg; -#ifndef CONFIG_DM_USB - struct gpio_desc vbus_gpio; -#endif };
-#ifndef CONFIG_DM_USB -static struct exynos_xhci exynos; -#endif - -#ifdef CONFIG_DM_USB static int xhci_usb_ofdata_to_platdata(struct udevice *dev) { struct exynos_xhci_platdata *plat = dev_get_platdata(dev); @@ -102,54 +90,6 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
return 0; } -#else -static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) -{ - fdt_addr_t addr; - unsigned int node; - int depth; - - node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI); - if (node <= 0) { - debug("XHCI: Can't get device node for xhci\n"); - return -ENODEV; - } - - /* - * Get the base address for XHCI controller from the device node - */ - addr = fdtdec_get_addr(blob, node, "reg"); - if (addr == FDT_ADDR_T_NONE) { - debug("Can't get the XHCI register base address\n"); - return -ENXIO; - } - exynos->hcd = (struct xhci_hccr *)addr; - - /* Vbus gpio */ - gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, - &exynos->vbus_gpio, GPIOD_IS_OUT); - - depth = 0; - node = fdtdec_next_compatible_subnode(blob, node, - COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth); - if (node <= 0) { - debug("XHCI: Can't get device node for usb3-phy controller\n"); - return -ENODEV; - } - - /* - * Get the base address for usbphy from the device node - */ - exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node, - "reg"); - if (exynos->usb3_phy == NULL) { - debug("Can't get the usbphy register address\n"); - return -ENXIO; - } - - return 0; -} -#endif
static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) { @@ -340,53 +280,6 @@ static void exynos_xhci_core_exit(struct exynos_xhci *exynos) exynos5_usb3_phy_exit(exynos->usb3_phy); }
-#ifndef CONFIG_DM_USB -int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) -{ - struct exynos_xhci *ctx = &exynos; - int ret; - -#ifdef CONFIG_OF_CONTROL - exynos_usb3_parse_dt(gd->fdt_blob, ctx); -#else - ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy(); - ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci(); -#endif - - ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); - -#ifdef CONFIG_OF_CONTROL - /* setup the Vbus gpio here */ - if (dm_gpio_is_valid(&ctx->vbus_gpio)) - dm_gpio_set_value(&ctx->vbus_gpio, 1); -#endif - - ret = exynos_xhci_core_init(ctx); - if (ret) { - puts("XHCI: failed to initialize controller\n"); - return -EINVAL; - } - - *hccr = (ctx->hcd); - *hcor = (struct xhci_hcor *)((uint32_t) *hccr - + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); - - debug("Exynos5-xhci: init hccr %x and hcor %x hc_length %d\n", - (uint32_t)*hccr, (uint32_t)*hcor, - (uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); - - return 0; -} - -void xhci_hcd_stop(int index) -{ - struct exynos_xhci *ctx = &exynos; - - exynos_xhci_core_exit(ctx); -} -#endif - -#ifdef CONFIG_DM_USB static int xhci_usb_probe(struct udevice *dev) { struct exynos_xhci_platdata *plat = dev_get_platdata(dev); @@ -443,4 +336,3 @@ U_BOOT_DRIVER(usb_xhci) = { .priv_auto_alloc_size = sizeof(struct exynos_xhci), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; -#endif diff --git a/include/fdtdec.h b/include/fdtdec.h index c24fed2..b9d63ad 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -133,8 +133,6 @@ enum fdt_compat_id { COMPAT_WOLFSON_WM8994_CODEC, /* Wolfson WM8994 Sound Codec */ COMPAT_GOOGLE_CROS_EC, /* Google CROS_EC Protocol */ COMPAT_GOOGLE_CROS_EC_KEYB, /* Google CROS_EC Keyboard */ - COMPAT_SAMSUNG_EXYNOS_EHCI, /* Exynos EHCI controller */ - COMPAT_SAMSUNG_EXYNOS5_XHCI, /* Exynos5 XHCI controller */ COMPAT_SAMSUNG_EXYNOS_USB_PHY, /* Exynos phy controller for usb2.0 */ COMPAT_SAMSUNG_EXYNOS5_USB3_PHY,/* Exynos phy controller for usb3.0 */ COMPAT_SAMSUNG_EXYNOS_TMU, /* Exynos TMU */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index d3e725b..94460d0 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -41,8 +41,6 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(WOLFSON_WM8994_CODEC, "wolfson,wm8994-codec"), COMPAT(GOOGLE_CROS_EC, "google,cros-ec"), COMPAT(GOOGLE_CROS_EC_KEYB, "google,cros-ec-keyb"), - COMPAT(SAMSUNG_EXYNOS_EHCI, "samsung,exynos-ehci"), - COMPAT(SAMSUNG_EXYNOS5_XHCI, "samsung,exynos5250-xhci"), COMPAT(SAMSUNG_EXYNOS_USB_PHY, "samsung,exynos-usb-phy"), COMPAT(SAMSUNG_EXYNOS5_USB3_PHY, "samsung,exynos5250-usb3-phy"), COMPAT(SAMSUNG_EXYNOS_TMU, "samsung,exynos-tmu"),

On 25 March 2015 at 12:23, Simon Glass sjg@chromium.org wrote:
Drop the code that doesn't use driver model for USB.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2: None
drivers/usb/host/ehci-exynos.c | 117 ---------------------------------------- drivers/usb/host/xhci-exynos5.c | 108 ------------------------------------- include/fdtdec.h | 2 - lib/fdtdec.c | 2 - 4 files changed, 229 deletions(-)
Applied to u-boot-dm.

Add some documentation describing how USB is implemented with USB. This might make things easier for people to understand.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - Rewrite and expand series to support driver model fully
doc/driver-model/usb-info.txt | 415 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 doc/driver-model/usb-info.txt
diff --git a/doc/driver-model/usb-info.txt b/doc/driver-model/usb-info.txt new file mode 100644 index 0000000..3762b6c --- /dev/null +++ b/doc/driver-model/usb-info.txt @@ -0,0 +1,415 @@ +How USB works with driver model +=============================== + +Introduction +------------ + +Driver model USB support makes use of existing features but changes how +drivers are found. This document provides some information intended to help +understand how things work with USB in U-Boot when driver model is enabled. + + +Enabling driver model for USB +----------------------------- + +A new CONFIG_DM_USB option is provided to enable driver model for USB. This +causes the USB uclass to be included, and drops the equivalent code in +usb.c. In particular the usb_init() function is then implemented by the +uclass. + + +Support for ECHI and XCHI +------------------------- + +So far OHCI is not supported. Both EHCI and XHCI drivers should be declared +as drivers in the USB uclass. For example: + +static const struct udevice_id ehci_usb_ids[] = { + { .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 }, + { .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 }, + { .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 }, + { } +}; + +U_BOOT_DRIVER(usb_ehci) = { + .name = "ehci_tegra", + .id = UCLASS_USB, + .of_match = ehci_usb_ids, + .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, + .probe = tegra_ehci_usb_probe, + .remove = tegra_ehci_usb_remove, + .ops = &ehci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct fdt_usb), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; + +Here ehci_usb_ids is used to list the controllers that the driver supports. +Each has its own data value. Controllers must be in the UCLASS_USB uclass. + +The ofdata_to_platdata() method allows the controller driver to grab any +necessary settings from the device tree. + +The ops here are ehci_usb_ops. All EHCI drivers will use these same ops in +most cases, since they are all ECHI-compatible. For ECHI there are also some +special operations that can be overriden when calling ehci_register(). + +The driver can use priv_auto_alloc_size to set the size of its private data. +This can hold run-time information needed by the driver for operation. It +exists when the device is probed (not when it is bound) and is removed when +the driver is removed. + +Note that usb_platdata is currently only used to deal with setting up a bus +in USB device mode (OTG operation). It can be omitted if that is not +supported. + +The driver's probe() method should do the basic controller init and then +call ehci_register() to register itself as an EHCI device. It should call +ehci_deregister() in the remove() method. Registering a new EHCI device +does not by itself cause the bus to be scanned. + +The old ehci_hcd_init() function is no-longer used. Nor is it necessary to +set up the USB controllers from board init code. When 'usb start' is used, +each controller will be probed and its bus scanned. + +XHCI works in a similar way. + + +Data structures +--------------- + +The following primary data structures are in use: + +- struct usb_device + This holds information about a device on the bus. All devices have + this structure, even the root hub. The controller itself does not + have this structure. You can access it for a device 'dev' with + dev_get_parentdata(dev). It matches the old structure except that the + parent and child information is not present (since driver model + handles that). Once the device is set up, you can find the device + descriptor and current configuration descriptor in this structure. + +- struct usb_platdata + This holds platform data for a controller. So far this is only used + as a work-around for controllers which can act as USB devices in OTG + mode, since the gadget framework does not use driver model. + +- struct usb_dev_platdata + This holds platform data for a device. You can access it for a + device 'dev' with dev_get_parent_platdata(dev). It holds the device + address and speed - anything that can be determined before the device + driver is actually set up. When probing the bus this structure is + used to provide essential information to the device driver. + +- struct usb_bus_priv + This is private information for each controller, maintained by the + controller uclass. It is mostly used to keep track of the next + device address to use. + +Of these, only struct usb_device was used prior to driver model. + + +USB buses +--------- + +Given a controller, you know the bus - it is the one attached to the +controller. Each controller handles exactly one bus. Every controller has a +root hub attached to it. This hub, which is itself a USB device, can provide +one or more 'ports' to which additional devices can be attached. It is +possible to power up a hub and find out which of its ports have devices +attached. + +Devices are given addresses starting at 1. The root hub is always address 1, +and from there the devices are numbered in sequence. The USB uclass takes +care of this numbering automatically during enumeration. + +USB devices are enumerated by finding a device on a particular hub, and +setting its address to the next available address. The USB bus stretches out +in a tree structure, potentially with multiple hubs each with several ports +and perhaps other hubs. Some hubs will have their own power since otherwise +the 5V 500mA power supplied by the controller will not be sufficient to run +very many devices. + +Enumeration in U-Boot takes a long time since devices are probed one at a +time, and each is given sufficient time to wake up and announce itself. The +timeouts are set for the slowest device. + +Up to 127 devices can be on each bus. USB has four bus speeds: low +(1.5Mbps), full (12Mbps), high (480Mbps) which is only available with USB2 +and newer (EHCI), and super (5Gbps) which is only available with USB3 and +newer (XHCI). If you connect a super-speed device to a high-speed hub, you +will only get high-speed. + + +USB operations +-------------- + +As before driver model, messages can be sent using submit_bulk_msg() and the +like. These are now implemented by the USB uclass and route through the +controller drivers. Note that messages are not sent to the driver of the +device itself - i.e. they don't pass down the stack to the controller. +U-Boot simply finds the controller to which the device is attached, and sends +the message there with an appropriate 'pipe' value so it can be addressed +properly. Having said that, the USB device which should receive the message +is passed in to the driver methods, for use by sandbox. This design decision +is open for review and the code impact of changing it is small since the +methods are typically implemented by the EHCI and XHCI stacks. + +Controller drivers (in UCLASS_USB) themselves provide methods for sending +each message type. For XHCI an additional alloc_device() method is provided +since XHCI needs to allocate a device context before it can even read the +device's descriptor. + +These methods use a 'pipe' which is a collection of bit fields used to +describe the type of message, direction of transfer and the intended +recipient (device number). + + +USB Devices +----------- + +USB devices are found using a simple algorithm which works through the +available hubs in a depth-first search. Devices can be in any uclass, but +are attached to a parent hub (or controller in the case of the root hub) and +so have parent data attached to them (this is struct usb_device). + +By the time the device's probe() method is called, it is enumerated and is +ready to talk to the host. + +The enumeration process needs to work out which driver to attach to each USB +device. It does this by examining the device class, interface class, vendor +ID, product ID, etc. See struct usb_driver_entry for how drivers are matched +with USB devices - you can use the USB_DEVICE() macro to declare a USB +driver. For example, usb_storage.c defines a USB_DEVICE() to handle storage +devices, and it will be used for all USB devices which match. + + + +Technical details on enumeration flow +------------------------------------- + +It is useful to understand precisely how a USB bus is enumerating to avoid +confusion when dealing with USB devices. + +Device initialisation happens roughly like this: + +- At some point the 'usb start' command is run +- This calls usb_init() which works through each controller in turn +- The controller is probed(). This does no enumeration. +- Then usb_scan_bus() is called. This calls usb_scan_device() to scan the +(only) device that is attached to the controller - a root hub +- usb_scan_device() sets up a fake struct usb_device and calls +usb_setup_device(), passing the port number to be scanned, in this case port +0 +- usb_setup_device() first calls usb_prepare_device() to set the device +address, then usb_select_config() to select the first configuration +- at this point the device is enumerated but we do not have a real struct +udevice for it. But we do have the descriptor in struct usb_device so we can +use this to figure out what driver to use +- back in usb_scan_device(), we call usb_find_child() to try to find an +existing device which matches the one we just found on the bus. This can +happen if the device is mentioned in the device tree, or if we previously +scanned the bus and so the device was created before +- if usb_find_child() does not find an existing device, we call +usb_find_and_bind_driver() which tries to bind one +- usb_find_and_bind_driver() searches all available USB drivers (declared +with USB_DEVICE()). If it finds a match it binds that driver to create a new +device. +- If it does not, it binds a generic driver. A generic driver is good enough +to allow access to the device (sending it packets, etc.) but all +functionality will need to be implemented outside the driver model. +- in any case, when usb_find_child() and/or usb_find_and_bind_driver() are +done, we have a device with the correct uclass. At this point we want to +probe the device +- first we store basic information about the new device (address, port, +speed) in its parent platform data. We cannot store it its private data +since that will not exist until the device is probed. +- then we call device_probe() which probes the device +- the first probe step is actually the USB controller's (or USB hubs's) +child_pre_probe() method. This gets called before anything else and is +intended to set up a child device ready to be used with its parent bus. For +USB this calls usb_child_pre_probe() which grabs the information that was +stored in the parent platform data and stores it in the parent private data +(which is struct usb_device, a real one this time). It then calls +usb_select_config() again to make sure that everything about the device is +set up +- note that we have called usb_select_config() twice. This is inefficient +but the alternative is to store additional information in the platform data. +The time taken is minimal and this way is simpler +- at this point the device is set up and ready for use so far as the USB +subsystem is concerned +- the device's probe() method is then called. It can send messages and do +whatever else it wants to make the device work. + +Note that the first device is always a root hub, and this must be scanned to +find any devices. The above steps will have created a hub (UCLASS_USB_HUB), +given it address 1 and set the configuration. + +For hubs, the hub uclass has a post_probe() method. This means that after +any hub is probed, the uclass gets to do some processing. In this case +usb_hub_post_probe() is called, and the following steps take place: + +- usb_hub_post_probe() calls usb_hub_scan() to scan the hub, which in turn +calls usb_hub_configure() +- hub power is enabled +- we loop through each port on the hub, performing the same steps for each +- first, check if there is a device present. This happens in +usb_hub_port_connect_change(). If so, then usb_scan_device() is called to +scan the device, passing the appropriate port number. +- you will recognise usb_scan_device() from the steps above. It sets up the +device ready for use. If it is a hub, it will scan that hub before it +continues here (recursively, depth-first) +- once all hub ports are scanned in this way, the hub is ready for use and +all of its downstream devices also +- additional controllers are scanned in the same way + +The above method has some nice properties: + +- the bus enumeration happens by virtue of driver model's natural device flow +- most logic is in the USB controller and hub uclasses; the actual device +drivers do not need to know they are on a USB bus, at least so far as +enumeration goes +- hub scanning happens automatically after a hub is probed + + +Hubs +---- + +USB hubs are scanned as in the section above. While hubs have their own +uclass, they share some common elements with controllers: + +- they both attach private data to their children (struct usb_device, +accessible for a child with dev_get_parentdata(child)) +- they both use usb_child_pre_probe() to set up their children as proper USB +devices + + +Example - Mass Storage +---------------------- + +As an example of a USB device driver, see usb_storage.c. It uses its own +uclass and declares itself as follows: + +U_BOOT_DRIVER(usb_mass_storage) = { + .name = "usb_mass_storage", + .id = UCLASS_MASS_STORAGE, + .of_match = usb_mass_storage_ids, + .probe = usb_mass_storage_probe, +}; + +static const struct usb_device_id mass_storage_id_table[] = { + { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, + .bInterfaceClass = USB_CLASS_MASS_STORAGE}, + { } /* Terminating entry */ +}; + +USB_DEVICE(usb_mass_storage, mass_storage_id_table); + +The USB_DEVICE() macro attaches the given table of matching information to +the given driver. Note that the driver is declared in U_BOOT_DRIVER() as +'usb_mass_storage' and this must match the first parameter of USB_DEVICE. + +When usb_find_and_bind_driver() is called on a USB device with the +bInterfaceClass value of USB_CLASS_MASS_STORAGE, it will automatically find +this driver and use it. + + +Counter-example: USB Ethernet +----------------------------- + +As an example of the old way of doing things, see usb_ether.c. When the bus +is scanned, all Ethernet devices will be created as generic USB devices (in +uclass UCLASS_USB_DEV_GENERIC). Then, when the scan is completed, +usb_host_eth_scan() will be called. This looks through all the devices on +each bus and manually figures out which are Ethernet devices in the ways of +yore. + +In fact, usb_ether should be moved to driver model. Each USB Ethernet driver +(e.g drivers/usb/eth/asix.c) should include a USB_DEVICE() declaration, so +that it will be found as part of normal USB enumeration. Then, instead of a +generic USB driver, a real (driver-model-aware) driver will be used. Since +Ethernet now supports driver model, this should be fairly easy to achieve, +and then usb_ether.c and the usb_host_eth_scan() will melt away. + + +Sandbox +------- + +All driver model uclasses must have tests and USB is no exception. To +achieve this, a sandbox USB controller is provided. This can make use of +emulation drivers which pretend to be USB devices. Emulations are provided +for a hub and a flash stick. These are enough to create a pretend USB bus +(defined by the sandbox device tree sandbox.dts) which can be scanned and +used. + +Tests in test/dm/usb.c make use of this feature. It allows much of the USB +stack to be tested without real hardware being needed. + +Here is an example device tree fragment: + + usb@1 { + compatible = "sandbox,usb"; + hub { + compatible = "usb-hub"; + usb,device-class = <USB_CLASS_HUB>; + hub-emul { + compatible = "sandbox,usb-hub"; + #address-cells = <1>; + #size-cells = <0>; + flash-stick { + reg = <0>; + compatible = "sandbox,usb-flash"; + sandbox,filepath = "flash.bin"; + }; + }; + }; + }; + +This defines a single controller, containing a root hub (which is required). +The hub is emulated by a hub emulator, and the emulated hub has a single +flash stick to emulate on one of its ports. + +When 'usb start' is used, the following 'dm tree' output will be available: + + usb [ + ] `-- usb@1 + usb_hub [ + ] `-- hub + usb_emul [ + ] |-- hub-emul + usb_emul [ + ] | `-- flash-stick + usb_mass_st [ + ] `-- usb_mass_storage + + +This may look a confusing. Most of it mirrors the device tree, but the +'usb_mass_storage' device is not in the device tree. This is created by +usb_find_and_bind_driver() based on the USB_DRIVER in usb_storage.c. While +'flash-stick' is the emulation device, 'usb_mass_storage' is the real U-Boot +USB device driver that talks to it. + + +Future work +----------- + +It is pretty uncommon to have a large USB bus with lots of hubs on an +embedded system. In fact anything other than a root hub is uncommon. Still +it would be possible to speed up enumeration in two ways: + +- breadth-first search would allow devices to be reset and probed in +parallel to some extent +- enumeration could be lazy, in the sense that we could enumerate just the +root hub at first, then only progress to the next 'level' when a device is +used that we cannot find. This could be made easier if the devices were +statically declared in the device tree (which is acceptable for production +boards where the same, known, things are on each bus). + +But in common cases the current algorithm is sufficient. + +Other things that need doing: +- Convert usb_ether to use driver model as described above +- Test that keyboards work (and convert to driver model) +- Move the USB gadget framework to driver model +- Implement OHCI in driver model +- Implement USB PHYs in driver model +- Work out a clever way to provide lazy init for USB devices + +-- +Simon Glass sjg@chromium.org +23-Mar-15

There are some typos. Please correct them, thanks.
On 03/26/2015 02:23 AM, Simon Glass wrote:
Add some documentation describing how USB is implemented with USB. This might make things easier for people to understand.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2:
Rewrite and expand series to support driver model fully
doc/driver-model/usb-info.txt | 415 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 doc/driver-model/usb-info.txt
diff --git a/doc/driver-model/usb-info.txt b/doc/driver-model/usb-info.txt new file mode 100644 index 0000000..3762b6c --- /dev/null +++ b/doc/driver-model/usb-info.txt @@ -0,0 +1,415 @@ +How USB works with driver model +===============================
+Introduction +------------
+Driver model USB support makes use of existing features but changes how +drivers are found. This document provides some information intended to help +understand how things work with USB in U-Boot when driver model is enabled.
+Enabling driver model for USB +-----------------------------
+A new CONFIG_DM_USB option is provided to enable driver model for USB. This +causes the USB uclass to be included, and drops the equivalent code in +usb.c. In particular the usb_init() function is then implemented by the +uclass.
+Support for ECHI and XCHI
EHCI XHCI
+-------------------------
+So far OHCI is not supported. Both EHCI and XHCI drivers should be declared +as drivers in the USB uclass. For example:
+static const struct udevice_id ehci_usb_ids[] = {
- { .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 },
- { .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 },
- { .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 },
- { }
+};
+U_BOOT_DRIVER(usb_ehci) = {
- .name = "ehci_tegra",
- .id = UCLASS_USB,
- .of_match = ehci_usb_ids,
- .ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
- .probe = tegra_ehci_usb_probe,
- .remove = tegra_ehci_usb_remove,
- .ops = &ehci_usb_ops,
- .platdata_auto_alloc_size = sizeof(struct usb_platdata),
- .priv_auto_alloc_size = sizeof(struct fdt_usb),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+Here ehci_usb_ids is used to list the controllers that the driver supports. +Each has its own data value. Controllers must be in the UCLASS_USB uclass.
+The ofdata_to_platdata() method allows the controller driver to grab any +necessary settings from the device tree.
+The ops here are ehci_usb_ops. All EHCI drivers will use these same ops in +most cases, since they are all ECHI-compatible. For ECHI there are also some
EHCI-compatible For EHCI
+special operations that can be overriden when calling ehci_register().
overridden
+This defines a single controller, containing a root hub (which is required). +The hub is emulated by a hub emulator, and the emulated hub has a single +flash stick to emulate on one of its ports.
+When 'usb start' is used, the following 'dm tree' output will be available:
- usb [ + ] `-- usb@1
- usb_hub [ + ] `-- hub
- usb_emul [ + ] |-- hub-emul
- usb_emul [ + ] | `-- flash-stick
- usb_mass_st [ + ] `-- usb_mass_storage
+This may look a confusing. Most of it mirrors the device tree, but the
may look confusing --nvpublic

Hi Jim,
On 6 April 2015 at 21:24, Jim Lin jilin@nvidia.com wrote:
There are some typos. Please correct them, thanks.
On 03/26/2015 02:23 AM, Simon Glass wrote:
Add some documentation describing how USB is implemented with USB. This might make things easier for people to understand.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v2:
Rewrite and expand series to support driver model fully
doc/driver-model/usb-info.txt | 415
++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 doc/driver-model/usb-info.txt
diff --git a/doc/driver-model/usb-info.txt b/doc/driver-model/usb-info.txt new file mode 100644 index 0000000..3762b6c --- /dev/null +++ b/doc/driver-model/usb-info.txt @@ -0,0 +1,415 @@ +How USB works with driver model +===============================
+Introduction +------------
+Driver model USB support makes use of existing features but changes how +drivers are found. This document provides some information intended to help +understand how things work with USB in U-Boot when driver model is enabled.
+Enabling driver model for USB +-----------------------------
+A new CONFIG_DM_USB option is provided to enable driver model for USB. This +causes the USB uclass to be included, and drops the equivalent code in +usb.c. In particular the usb_init() function is then implemented by the +uclass.
+Support for ECHI and XCHI
EHCI XHCI
+-------------------------
+So far OHCI is not supported. Both EHCI and XHCI drivers should be declared +as drivers in the USB uclass. For example:
+static const struct udevice_id ehci_usb_ids[] = {
{ .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 },
{ .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 },
{ .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 },
{ }
+};
+U_BOOT_DRIVER(usb_ehci) = {
.name = "ehci_tegra",
.id = UCLASS_USB,
.of_match = ehci_usb_ids,
.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
.probe = tegra_ehci_usb_probe,
.remove = tegra_ehci_usb_remove,
.ops = &ehci_usb_ops,
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
.priv_auto_alloc_size = sizeof(struct fdt_usb),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
+Here ehci_usb_ids is used to list the controllers that the driver supports. +Each has its own data value. Controllers must be in the UCLASS_USB uclass.
+The ofdata_to_platdata() method allows the controller driver to grab any +necessary settings from the device tree.
+The ops here are ehci_usb_ops. All EHCI drivers will use these same ops in +most cases, since they are all ECHI-compatible. For ECHI there are also some
EHCI-compatible For EHCI
+special operations that can be overriden when calling ehci_register().
overridden
+This defines a single controller, containing a root hub (which is required). +The hub is emulated by a hub emulator, and the emulated hub has a single +flash stick to emulate on one of its ports.
+When 'usb start' is used, the following 'dm tree' output will be available:
- usb [ + ] `-- usb@1
- usb_hub [ + ] `-- hub
- usb_emul [ + ] |-- hub-emul
- usb_emul [ + ] | `-- flash-stick
- usb_mass_st [ + ] `-- usb_mass_storage
+This may look a confusing. Most of it mirrors the device tree, but the
may look confusing --nvpublic
Thanks for the corrections, I fixed these.
Applied to u-boot-dm/next.

On Wednesday, March 25, 2015 at 07:21:48 PM, Simon Glass wrote:
This series adds driver model support to USB. The intent is to permit the various subsystems (OHCI, EHCI, XHCI) to co-exist and allow any number of USB ports of different types.
With the RFC series, only USB controllers had a real driver model device. USB devices (including the hub in the controller) were not modelled as driver model devices.
While this was expedient, and produced much fewer patches, it is not a long-term solution. Also, since then, driver model Ethernet support (which USB can use) has been merged to u-boot-dm/next. It seems better to bite the bullet and do a full conversion.
Whew.
Reviewed-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

Hi Marek,
On 26 March 2015 at 13:40, Marek Vasut marex@denx.de wrote:
On Wednesday, March 25, 2015 at 07:21:48 PM, Simon Glass wrote:
This series adds driver model support to USB. The intent is to permit the various subsystems (OHCI, EHCI, XHCI) to co-exist and allow any number of USB ports of different types.
With the RFC series, only USB controllers had a real driver model device. USB devices (including the hub in the controller) were not modelled as driver model devices.
While this was expedient, and produced much fewer patches, it is not a long-term solution. Also, since then, driver model Ethernet support (which USB can use) has been merged to u-boot-dm/next. It seems better to bite the bullet and do a full conversion.
Whew.
Reviewed-by: Marek Vasut marex@denx.de
Thanks very much for working through these!
Unless there are other comments I plan to apply this to u-boot-dm/next in the next few days. I expect it will need some tweaking as boards are moved over too.
Regards, Simon

On Monday, April 06, 2015 at 01:38:13 AM, Simon Glass wrote:
Hi Marek,
Hi Simon,
On 26 March 2015 at 13:40, Marek Vasut marex@denx.de wrote:
On Wednesday, March 25, 2015 at 07:21:48 PM, Simon Glass wrote:
This series adds driver model support to USB. The intent is to permit the various subsystems (OHCI, EHCI, XHCI) to co-exist and allow any number of USB ports of different types.
With the RFC series, only USB controllers had a real driver model device. USB devices (including the hub in the controller) were not modelled as driver model devices.
While this was expedient, and produced much fewer patches, it is not a long-term solution. Also, since then, driver model Ethernet support (which USB can use) has been merged to u-boot-dm/next. It seems better to bite the bullet and do a full conversion.
Whew.
Reviewed-by: Marek Vasut marex@denx.de
Thanks very much for working through these!
Unless there are other comments I plan to apply this to u-boot-dm/next in the next few days. I expect it will need some tweaking as boards are moved over too.
Please apply, I'm really looking forward to this.
Thank you very much for putting all the effort into this !
Best regards, Marek Vasut

Hi Marek,
On 6 April 2015 at 07:13, Marek Vasut marex@denx.de wrote:
On Monday, April 06, 2015 at 01:38:13 AM, Simon Glass wrote:
Hi Marek,
Hi Simon,
On 26 March 2015 at 13:40, Marek Vasut marex@denx.de wrote:
On Wednesday, March 25, 2015 at 07:21:48 PM, Simon Glass wrote:
This series adds driver model support to USB. The intent is to permit the various subsystems (OHCI, EHCI, XHCI) to co-exist and allow any number of USB ports of different types.
With the RFC series, only USB controllers had a real driver model device. USB devices (including the hub in the controller) were not modelled as driver model devices.
While this was expedient, and produced much fewer patches, it is not a long-term solution. Also, since then, driver model Ethernet support (which USB can use) has been merged to u-boot-dm/next. It seems better to bite the bullet and do a full conversion.
Whew.
Reviewed-by: Marek Vasut marex@denx.de
Thanks very much for working through these!
Unless there are other comments I plan to apply this to u-boot-dm/next in the next few days. I expect it will need some tweaking as boards are moved over too.
Please apply, I'm really looking forward to this.
Thank you very much for putting all the effort into this !
OK, I'll apply to u-boot-dm/next, do a bit more testing, and apply. It will be great to get this moved over.
Regards, Simon

On Tuesday, April 07, 2015 at 12:38:58 AM, Simon Glass wrote:
Hi Marek,
Hi Simon,
On 6 April 2015 at 07:13, Marek Vasut marex@denx.de wrote:
On Monday, April 06, 2015 at 01:38:13 AM, Simon Glass wrote:
Hi Marek,
Hi Simon,
On 26 March 2015 at 13:40, Marek Vasut marex@denx.de wrote:
On Wednesday, March 25, 2015 at 07:21:48 PM, Simon Glass wrote:
This series adds driver model support to USB. The intent is to permit the various subsystems (OHCI, EHCI, XHCI) to co-exist and allow any number of USB ports of different types.
With the RFC series, only USB controllers had a real driver model device. USB devices (including the hub in the controller) were not modelled as driver model devices.
While this was expedient, and produced much fewer patches, it is not a long-term solution. Also, since then, driver model Ethernet support (which USB can use) has been merged to u-boot-dm/next. It seems better to bite the bullet and do a full conversion.
Whew.
Reviewed-by: Marek Vasut marex@denx.de
Thanks very much for working through these!
Unless there are other comments I plan to apply this to u-boot-dm/next in the next few days. I expect it will need some tweaking as boards are moved over too.
Please apply, I'm really looking forward to this.
Thank you very much for putting all the effort into this !
OK, I'll apply to u-boot-dm/next, do a bit more testing, and apply. It will be great to get this moved over.
Thank you!
Best regards, Marek Vasut
participants (4)
-
Jim Lin
-
Joe Hershberger
-
Marek Vasut
-
Simon Glass