dm: usb: Add a uclass for USB controllers

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>
Reviewed-by: Marek Vasut <marex@denx.de>
master
Simon Glass 10 years ago
parent 56a71f891b
commit de31213fb8
  1. 14
      drivers/usb/Kconfig
  2. 4
      drivers/usb/host/Makefile
  3. 392
      drivers/usb/host/usb-uclass.c
  4. 4
      drivers/usb/musb-new/musb_uboot.c
  5. 1
      include/dm/uclass-id.h
  6. 285
      include/usb.h

@ -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

@ -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

@ -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),
};

@ -180,7 +180,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;
@ -208,6 +208,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)

@ -40,6 +40,7 @@ enum uclass_id {
UCLASS_PCH, /* x86 platform controller hub */
UCLASS_ETH, /* Ethernet device */
UCLASS_LPC, /* x86 'low pin count' interface */
UCLASS_USB, /* USB bus */
UCLASS_COUNT,
UCLASS_INVALID = -1,

@ -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,

Loading…
Cancel
Save