Tom Rini 2022-07-14 11:10:49 -04:00
commit 54f683dbfb
29 changed files with 3874 additions and 92 deletions

View File

@ -847,6 +847,7 @@ libs-y += drivers/usb/host/
libs-y += drivers/usb/mtu3/
libs-y += drivers/usb/musb/
libs-y += drivers/usb/musb-new/
libs-y += drivers/usb/isp1760/
libs-y += drivers/usb/phy/
libs-y += drivers/usb/ulpi/
ifdef CONFIG_POST

View File

@ -810,7 +810,6 @@ config ARCH_KEYSTONE
select CMD_POWEROFF
select CPU_V7A
select DDR_SPD
select GPIO_EXTRA_HEADER
select SUPPORT_SPL
select SYS_ARCH_TIMER
select SYS_THUMB_BUILD

View File

@ -19,7 +19,7 @@ static int spl_sdp_load_image(struct spl_image_info *spl_image,
usb_gadget_initialize(controller_index);
board_usb_init(0, USB_INIT_DEVICE);
board_usb_init(controller_index, USB_INIT_DEVICE);
g_dnl_clear_detach();
ret = g_dnl_register("usb_dnl_sdp");

View File

@ -47,6 +47,8 @@
#define HUB_SHORT_RESET_TIME 20
#define HUB_LONG_RESET_TIME 200
#define HUB_DEBOUNCE_TIMEOUT 1000
#define PORT_OVERCURRENT_MAX_SCAN_COUNT 3
struct usb_device_scan {
@ -208,10 +210,10 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
* will be done based on this value in the USB port loop in
* usb_hub_configure() later.
*/
hub->connect_timeout = hub->query_delay + 1000;
hub->connect_timeout = hub->query_delay + HUB_DEBOUNCE_TIMEOUT;
debug("devnum=%d poweron: query_delay=%d connect_timeout=%d\n",
dev->devnum, max(100, (int)pgood_delay),
max(100, (int)pgood_delay) + 1000);
max(100, (int)pgood_delay) + HUB_DEBOUNCE_TIMEOUT);
}
#if !CONFIG_IS_ENABLED(DM_USB)

View File

@ -581,21 +581,22 @@ static int probe_usb_keyboard(struct usb_device *dev)
stdinname = env_get("stdin");
#if CONFIG_IS_ENABLED(CONSOLE_MUX)
error = iomux_doenv(stdin, stdinname);
if (error)
return error;
if (strstr(stdinname, DEVNAME) != NULL) {
error = iomux_doenv(stdin, stdinname);
if (error)
return error;
}
#else
/* Check if this is the standard input device. */
if (strcmp(stdinname, DEVNAME))
return 1;
if (!strcmp(stdinname, DEVNAME)) {
/* Reassign the console */
if (overwrite_console())
return 1;
/* Reassign the console */
if (overwrite_console())
return 1;
error = console_assign(stdin, DEVNAME);
if (error)
return error;
error = console_assign(stdin, DEVNAME);
if (error)
return error;
}
#endif
return 0;

View File

@ -28,6 +28,8 @@ CONFIG_SYS_BOOTM_LEN=0x800000
# CONFIG_CMD_XIMG is not set
CONFIG_CMD_LOADM=y
# CONFIG_CMD_LOADS is not set
CONFIG_CMD_MMC=y
CONFIG_CMD_USB=y
# CONFIG_CMD_SETEXPR is not set
# CONFIG_CMD_NFS is not set
CONFIG_CMD_CACHE=y
@ -50,4 +52,5 @@ CONFIG_DM_RTC=y
CONFIG_RTC_EMULATION=y
CONFIG_DM_SERIAL=y
CONFIG_USB=y
CONFIG_USB_ISP1760=y
CONFIG_ERRNO_STR=y

View File

@ -68,6 +68,8 @@ config SPL_DM_USB_GADGET
source "drivers/usb/host/Kconfig"
source "drivers/usb/isp1760/Kconfig"
source "drivers/usb/cdns3/Kconfig"
source "drivers/usb/dwc3/Kconfig"

View File

@ -4,5 +4,9 @@
#
obj-$(CONFIG_$(SPL_)DM_USB) += common.o
obj-$(CONFIG_USB_ISP1760) += usb_urb.o
obj-$(CONFIG_USB_MUSB_HOST) += usb_urb.o
obj-$(CONFIG_USB_MUSB_GADGET) += usb_urb.o
obj-$(CONFIG_USB_R8A66597_HCD) += usb_urb.o
obj-$(CONFIG_USB_EHCI_FSL) += fsl-dt-fixup.o fsl-errata.o
obj-$(CONFIG_USB_XHCI_FSL) += fsl-dt-fixup.o fsl-errata.o

View File

@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common code for usb urb handling, based on the musb-new code
*
* Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
*
*/
#include <dm/device.h>
#include <dm/device_compat.h>
#include <linux/usb/usb_urb_compat.h>
#include <time.h>
#include <usb.h>
#if CONFIG_IS_ENABLED(DM_USB)
struct usb_device *usb_dev_get_parent(struct usb_device *udev)
{
struct udevice *parent = udev->dev->parent;
/*
* When called from usb-uclass.c: usb_scan_device() udev->dev points
* to the parent udevice, not the actual udevice belonging to the
* udev as the device is not instantiated yet.
*
* If dev is an usb-bus, then we are called from usb_scan_device() for
* an usb-device plugged directly into the root port, return NULL.
*/
if (device_get_uclass_id(udev->dev) == UCLASS_USB)
return NULL;
/*
* If these 2 are not the same we are being called from
* usb_scan_device() and udev itself is the parent.
*/
if (dev_get_parent_priv(udev->dev) != udev)
return udev;
/* We are being called normally, use the parent pointer */
if (device_get_uclass_id(parent) == UCLASS_USB_HUB)
return dev_get_parent_priv(parent);
return NULL;
}
#else
struct usb_device *usb_dev_get_parent(struct usb_device *udev)
{
return udev->parent;
}
#endif
static void usb_urb_complete(struct urb *urb)
{
urb->dev->status &= ~USB_ST_NOT_PROC;
urb->dev->act_len = urb->actual_length;
if (urb->status == -EINPROGRESS)
urb->status = 0;
}
void usb_urb_fill(struct urb *urb, struct usb_host_endpoint *hep,
struct usb_device *dev, int endpoint_type,
unsigned long pipe, void *buffer, int len,
struct devrequest *setup, int interval)
{
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
u16 maxpacketsize = is_in ? dev->epmaxpacketin[epnum] :
dev->epmaxpacketout[epnum];
memset(urb, 0, sizeof(struct urb));
memset(hep, 0, sizeof(struct usb_host_endpoint));
INIT_LIST_HEAD(&hep->urb_list);
INIT_LIST_HEAD(&urb->urb_list);
urb->ep = hep;
urb->complete = usb_urb_complete;
urb->status = -EINPROGRESS;
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = buffer;
urb->transfer_dma = (unsigned long)buffer;
urb->transfer_buffer_length = len;
urb->setup_packet = (unsigned char *)setup;
urb->ep->desc.wMaxPacketSize = __cpu_to_le16(maxpacketsize);
urb->ep->desc.bmAttributes = endpoint_type;
urb->ep->desc.bEndpointAddress = ((is_in ? USB_DIR_IN : USB_DIR_OUT) |
epnum);
urb->ep->desc.bInterval = interval;
}
int usb_urb_submit(struct usb_hcd *hcd, struct urb *urb)
{
const struct usb_urb_ops *ops = hcd->urb_ops;
unsigned long timeout;
int ret;
if (!ops)
return -EINVAL;
ret = ops->urb_enqueue(hcd, urb, 0);
if (ret < 0) {
printf("Failed to enqueue URB to controller\n");
return ret;
}
timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe);
do {
if (ctrlc())
return -EIO;
ops->isr(0, hcd);
} while (urb->status == -EINPROGRESS && get_timer(0) < timeout);
if (urb->status == -EINPROGRESS)
ops->urb_dequeue(hcd, urb, -ETIME);
return urb->status;
}
int usb_urb_submit_control(struct usb_hcd *hcd, struct urb *urb,
struct usb_host_endpoint *hep,
struct usb_device *dev, unsigned long pipe,
void *buffer, int len, struct devrequest *setup,
int interval, enum usb_device_speed speed)
{
const struct usb_urb_ops *ops = hcd->urb_ops;
usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, buffer,
len, setup, 0);
/* Fix speed for non hub-attached devices */
if (!usb_dev_get_parent(dev)) {
dev->speed = speed;
if (ops->hub_control)
return ops->hub_control(hcd, dev, pipe, buffer, len,
setup);
}
return usb_urb_submit(hcd, urb);
}
int usb_urb_submit_bulk(struct usb_hcd *hcd, struct urb *urb,
struct usb_host_endpoint *hep, struct usb_device *dev,
unsigned long pipe, void *buffer, int len)
{
usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_BULK, pipe, buffer, len,
NULL, 0);
return usb_urb_submit(hcd, urb);
}
int usb_urb_submit_irq(struct usb_hcd *hcd, struct urb *urb,
struct usb_host_endpoint *hep, struct usb_device *dev,
unsigned long pipe, void *buffer, int len, int interval)
{
usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_INT, pipe, buffer, len,
NULL, interval);
return usb_urb_submit(hcd, urb);
}

View File

@ -26,6 +26,7 @@
#include <reset.h>
#include <clk.h>
#include <usb/xhci.h>
#include <asm/gpio.h>
struct dwc3_glue_data {
struct clk_bulk clks;
@ -43,6 +44,7 @@ struct dwc3_generic_priv {
void *base;
struct dwc3 dwc3;
struct phy_bulk phys;
struct gpio_desc ulpi_reset;
};
struct dwc3_generic_host_priv {
@ -78,6 +80,27 @@ static int dwc3_generic_probe(struct udevice *dev,
if (rc && rc != -ENOTSUPP)
return rc;
if (CONFIG_IS_ENABLED(DM_GPIO) &&
device_is_compatible(dev->parent, "xlnx,zynqmp-dwc3")) {
rc = gpio_request_by_name(dev->parent, "reset-gpios", 0,
&priv->ulpi_reset, GPIOD_ACTIVE_LOW);
if (rc)
return rc;
/* Toggle ulpi to reset the phy. */
rc = dm_gpio_set_value(&priv->ulpi_reset, 1);
if (rc)
return rc;
mdelay(5);
rc = dm_gpio_set_value(&priv->ulpi_reset, 0);
if (rc)
return rc;
mdelay(5);
}
if (device_is_compatible(dev->parent, "rockchip,rk3399-dwc3"))
reset_deassert_bulk(&glue->resets);
@ -99,6 +122,13 @@ static int dwc3_generic_remove(struct udevice *dev,
{
struct dwc3 *dwc3 = &priv->dwc3;
if (CONFIG_IS_ENABLED(DM_GPIO) &&
device_is_compatible(dev->parent, "xlnx,zynqmp-dwc3")) {
struct gpio_desc *ulpi_reset = &priv->ulpi_reset;
dm_gpio_free(ulpi_reset->dev, ulpi_reset);
}
dwc3_remove(dwc3);
dwc3_shutdown_phy(dev, &priv->phys);
unmap_physmem(dwc3->regs, MAP_NOCACHE);

View File

@ -14,6 +14,7 @@
#include <dm/device_compat.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/usb/usb_urb_compat.h>
#include <power/regulator.h>
#include "r8a66597.h"
@ -24,35 +25,6 @@
#define R8A66597_DPRINT(...)
#endif
static inline struct usb_device *usb_dev_get_parent(struct usb_device *udev)
{
struct udevice *parent = udev->dev->parent;
/*
* When called from usb-uclass.c: usb_scan_device() udev->dev points
* to the parent udevice, not the actual udevice belonging to the
* udev as the device is not instantiated yet.
*
* If dev is an usb-bus, then we are called from usb_scan_device() for
* an usb-device plugged directly into the root port, return NULL.
*/
if (device_get_uclass_id(udev->dev) == UCLASS_USB)
return NULL;
/*
* If these 2 are not the same we are being called from
* usb_scan_device() and udev itself is the parent.
*/
if (dev_get_parent_priv(udev->dev) != udev)
return udev;
/* We are being called normally, use the parent pointer */
if (device_get_uclass_id(parent) == UCLASS_USB_HUB)
return dev_get_parent_priv(parent);
return NULL;
}
static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport)
{
struct usb_device *parent = usb_dev_get_parent(dev);

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
config USB_ISP1760
tristate "NXP ISP 1760/1761/1763 support"
select DM_USB
select USB_HOST
help
Say Y or M here if your system as an ISP1760/1761/1763 USB host
controller.
This USB controller is usually attached to a non-DMA-Master
capable bus.

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
isp1760-y := isp1760-core.o isp1760-if.o isp1760-uboot.o isp1760-hcd.o
#isp1760-hcd.o
obj-$(CONFIG_USB_ISP1760) += isp1760.o

View File

@ -0,0 +1,380 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the NXP ISP1760 chip
*
* Copyright 2022 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
*
* This is based on linux kernel driver, original developed:
* Copyright 2014 Laurent Pinchart
* Copyright 2007 Sebastian Siewior
*
*/
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/device_compat.h>
#include <dm/devres.h>
#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <regmap.h>
#include <usb.h>
#include "isp1760-core.h"
#include "isp1760-hcd.h"
#include "isp1760-regs.h"
#define msleep(a) udelay((a) * 1000)
static int isp1760_init_core(struct isp1760_device *isp)
{
struct isp1760_hcd *hcd = &isp->hcd;
/*
* Reset the host controller, including the CPU interface
* configuration.
*/
isp1760_field_set(hcd->fields, SW_RESET_RESET_ALL);
msleep(100);
/* Setup HW Mode Control: This assumes a level active-low interrupt */
if ((isp->devflags & ISP1760_FLAG_ANALOG_OC) && hcd->is_isp1763)
return -EINVAL;
if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16)
isp1760_field_clear(hcd->fields, HW_DATA_BUS_WIDTH);
if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_8)
isp1760_field_set(hcd->fields, HW_DATA_BUS_WIDTH);
if (isp->devflags & ISP1760_FLAG_ANALOG_OC)
isp1760_field_set(hcd->fields, HW_ANA_DIGI_OC);
if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH)
isp1760_field_set(hcd->fields, HW_DACK_POL_HIGH);
if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
isp1760_field_set(hcd->fields, HW_DREQ_POL_HIGH);
if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH)
isp1760_field_set(hcd->fields, HW_INTR_HIGH_ACT);
if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
isp1760_field_set(hcd->fields, HW_INTR_EDGE_TRIG);
/*
* The ISP1761 has a dedicated DC IRQ line but supports sharing the HC
* IRQ line for both the host and device controllers. Hardcode IRQ
* sharing for now and disable the DC interrupts globally to avoid
* spurious interrupts during HCD registration.
*/
if (isp->devflags & ISP1760_FLAG_ISP1761) {
isp1760_reg_write(hcd->regs, ISP176x_DC_MODE, 0);
isp1760_field_set(hcd->fields, HW_COMN_IRQ);
}
/*
* PORT 1 Control register of the ISP1760 is the OTG control register
* on ISP1761.
*
* TODO: Really support OTG. For now we configure port 1 in device mode
*/
if (((isp->devflags & ISP1760_FLAG_ISP1761) ||
(isp->devflags & ISP1760_FLAG_ISP1763)) &&
(isp->devflags & ISP1760_FLAG_PERIPHERAL_EN)) {
isp1760_field_set(hcd->fields, HW_DM_PULLDOWN);
isp1760_field_set(hcd->fields, HW_DP_PULLDOWN);
isp1760_field_set(hcd->fields, HW_OTG_DISABLE);
} else {
isp1760_field_set(hcd->fields, HW_SW_SEL_HC_DC);
isp1760_field_set(hcd->fields, HW_VBUS_DRV);
isp1760_field_set(hcd->fields, HW_SEL_CP_EXT);
}
printf("%s bus width: %u, oc: %s\n",
hcd->is_isp1763 ? "isp1763" : "isp1760",
isp->devflags & ISP1760_FLAG_BUS_WIDTH_8 ? 8 :
isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
hcd->is_isp1763 ? "not available" :
isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital");
return 0;
}
void isp1760_set_pullup(struct isp1760_device *isp, bool enable)
{
struct isp1760_hcd *hcd = &isp->hcd;
if (enable)
isp1760_field_set(hcd->fields, HW_DP_PULLUP);
else
isp1760_field_set(hcd->fields, HW_DP_PULLUP_CLEAR);
}
/*
* ISP1760/61:
*
* 60kb divided in:
* - 32 blocks @ 256 bytes
* - 20 blocks @ 1024 bytes
* - 4 blocks @ 8192 bytes
*/
static const struct isp1760_memory_layout isp176x_memory_conf = {
.blocks[0] = 32,
.blocks_size[0] = 256,
.blocks[1] = 20,
.blocks_size[1] = 1024,
.blocks[2] = 4,
.blocks_size[2] = 8192,
.slot_num = 32,
.payload_blocks = 32 + 20 + 4,
.payload_area_size = 0xf000,
};
/*
* ISP1763:
*
* 20kb divided in:
* - 8 blocks @ 256 bytes
* - 2 blocks @ 1024 bytes
* - 4 blocks @ 4096 bytes
*/
static const struct isp1760_memory_layout isp1763_memory_conf = {
.blocks[0] = 8,
.blocks_size[0] = 256,
.blocks[1] = 2,
.blocks_size[1] = 1024,
.blocks[2] = 4,
.blocks_size[2] = 4096,
.slot_num = 16,
.payload_blocks = 8 + 2 + 4,
.payload_area_size = 0x5000,
};
static const struct regmap_config isp1760_hc_regmap_conf = {
.width = REGMAP_SIZE_16,
};
static const struct reg_field isp1760_hc_reg_fields[] = {
[HCS_PPC] = REG_FIELD(ISP176x_HC_HCSPARAMS, 4, 4),
[HCS_N_PORTS] = REG_FIELD(ISP176x_HC_HCSPARAMS, 0, 3),
[HCC_ISOC_CACHE] = REG_FIELD(ISP176x_HC_HCCPARAMS, 7, 7),
[HCC_ISOC_THRES] = REG_FIELD(ISP176x_HC_HCCPARAMS, 4, 6),
[CMD_LRESET] = REG_FIELD(ISP176x_HC_USBCMD, 7, 7),
[CMD_RESET] = REG_FIELD(ISP176x_HC_USBCMD, 1, 1),
[CMD_RUN] = REG_FIELD(ISP176x_HC_USBCMD, 0, 0),
[STS_PCD] = REG_FIELD(ISP176x_HC_USBSTS, 2, 2),
[HC_FRINDEX] = REG_FIELD(ISP176x_HC_FRINDEX, 0, 13),
[FLAG_CF] = REG_FIELD(ISP176x_HC_CONFIGFLAG, 0, 0),
[HC_ISO_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_ISO_PTD_DONEMAP, 0, 31),
[HC_ISO_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_ISO_PTD_SKIPMAP, 0, 31),
[HC_ISO_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_ISO_PTD_LASTPTD, 0, 31),
[HC_INT_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_INT_PTD_DONEMAP, 0, 31),
[HC_INT_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_INT_PTD_SKIPMAP, 0, 31),
[HC_INT_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_INT_PTD_LASTPTD, 0, 31),
[HC_ATL_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_ATL_PTD_DONEMAP, 0, 31),
[HC_ATL_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_ATL_PTD_SKIPMAP, 0, 31),
[HC_ATL_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_ATL_PTD_LASTPTD, 0, 31),
[PORT_OWNER] = REG_FIELD(ISP176x_HC_PORTSC1, 13, 13),
[PORT_POWER] = REG_FIELD(ISP176x_HC_PORTSC1, 12, 12),
[PORT_LSTATUS] = REG_FIELD(ISP176x_HC_PORTSC1, 10, 11),
[PORT_RESET] = REG_FIELD(ISP176x_HC_PORTSC1, 8, 8),
[PORT_SUSPEND] = REG_FIELD(ISP176x_HC_PORTSC1, 7, 7),
[PORT_RESUME] = REG_FIELD(ISP176x_HC_PORTSC1, 6, 6),
[PORT_PE] = REG_FIELD(ISP176x_HC_PORTSC1, 2, 2),
[PORT_CSC] = REG_FIELD(ISP176x_HC_PORTSC1, 1, 1),
[PORT_CONNECT] = REG_FIELD(ISP176x_HC_PORTSC1, 0, 0),
[ALL_ATX_RESET] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 31, 31),
[HW_ANA_DIGI_OC] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 15, 15),
[HW_COMN_IRQ] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 10, 10),
[HW_DATA_BUS_WIDTH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 8, 8),
[HW_DACK_POL_HIGH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 6, 6),
[HW_DREQ_POL_HIGH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 5, 5),
[HW_INTR_HIGH_ACT] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 2, 2),
[HW_INTR_EDGE_TRIG] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 1, 1),
[HW_GLOBAL_INTR_EN] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 0, 0),
[HC_CHIP_REV] = REG_FIELD(ISP176x_HC_CHIP_ID, 16, 31),
[HC_CHIP_ID_HIGH] = REG_FIELD(ISP176x_HC_CHIP_ID, 8, 15),
[HC_CHIP_ID_LOW] = REG_FIELD(ISP176x_HC_CHIP_ID, 0, 7),
[HC_SCRATCH] = REG_FIELD(ISP176x_HC_SCRATCH, 0, 31),
[SW_RESET_RESET_ALL] = REG_FIELD(ISP176x_HC_RESET, 0, 0),
[ISO_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 2, 2),
[INT_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 1, 1),
[ATL_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 0, 0),
[MEM_BANK_SEL] = REG_FIELD(ISP176x_HC_MEMORY, 16, 17),
[MEM_START_ADDR] = REG_FIELD(ISP176x_HC_MEMORY, 0, 15),
[HC_INTERRUPT] = REG_FIELD(ISP176x_HC_INTERRUPT, 0, 9),
[HC_ATL_IRQ_ENABLE] = REG_FIELD(ISP176x_HC_INTERRUPT_ENABLE, 8, 8),
[HC_INT_IRQ_ENABLE] = REG_FIELD(ISP176x_HC_INTERRUPT_ENABLE, 7, 7),
[HC_ISO_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_OR, 0, 31),
[HC_INT_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_OR, 0, 31),
[HC_ATL_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_OR, 0, 31),
[HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_AND, 0, 31),
[HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_AND, 0, 31),
[HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_AND, 0, 31),
[HW_OTG_DISABLE] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 10, 10),
[HW_SW_SEL_HC_DC] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 7, 7),
[HW_VBUS_DRV] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 4, 4),
[HW_SEL_CP_EXT] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 3, 3),
[HW_DM_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 2, 2),
[HW_DP_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 1, 1),
[HW_DP_PULLUP] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 0, 0),
[HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 10, 10),
[HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 7, 7),
[HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 4, 4),
[HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 3, 3),
[HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 2, 2),
[HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 1, 1),
[HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 0, 0),
/* Make sure the array is sized properly during compilation */
[HC_FIELD_MAX] = {},
};
static const struct regmap_config isp1763_hc_regmap_conf = {
.width = REGMAP_SIZE_16,
};
static const struct reg_field isp1763_hc_reg_fields[] = {
[CMD_LRESET] = REG_FIELD(ISP1763_HC_USBCMD, 7, 7),
[CMD_RESET] = REG_FIELD(ISP1763_HC_USBCMD, 1, 1),
[CMD_RUN] = REG_FIELD(ISP1763_HC_USBCMD, 0, 0),
[STS_PCD] = REG_FIELD(ISP1763_HC_USBSTS, 2, 2),
[HC_FRINDEX] = REG_FIELD(ISP1763_HC_FRINDEX, 0, 13),
[FLAG_CF] = REG_FIELD(ISP1763_HC_CONFIGFLAG, 0, 0),
[HC_ISO_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_ISO_PTD_DONEMAP, 0, 15),
[HC_ISO_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_ISO_PTD_SKIPMAP, 0, 15),
[HC_ISO_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_ISO_PTD_LASTPTD, 0, 15),
[HC_INT_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_INT_PTD_DONEMAP, 0, 15),
[HC_INT_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_INT_PTD_SKIPMAP, 0, 15),
[HC_INT_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_INT_PTD_LASTPTD, 0, 15),
[HC_ATL_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_ATL_PTD_DONEMAP, 0, 15),
[HC_ATL_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_ATL_PTD_SKIPMAP, 0, 15),
[HC_ATL_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_ATL_PTD_LASTPTD, 0, 15),
[PORT_OWNER] = REG_FIELD(ISP1763_HC_PORTSC1, 13, 13),
[PORT_POWER] = REG_FIELD(ISP1763_HC_PORTSC1, 12, 12),
[PORT_LSTATUS] = REG_FIELD(ISP1763_HC_PORTSC1, 10, 11),
[PORT_RESET] = REG_FIELD(ISP1763_HC_PORTSC1, 8, 8),
[PORT_SUSPEND] = REG_FIELD(ISP1763_HC_PORTSC1, 7, 7),
[PORT_RESUME] = REG_FIELD(ISP1763_HC_PORTSC1, 6, 6),
[PORT_PE] = REG_FIELD(ISP1763_HC_PORTSC1, 2, 2),
[PORT_CSC] = REG_FIELD(ISP1763_HC_PORTSC1, 1, 1),
[PORT_CONNECT] = REG_FIELD(ISP1763_HC_PORTSC1, 0, 0),
[HW_DATA_BUS_WIDTH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 4, 4),
[HW_DACK_POL_HIGH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 6, 6),
[HW_DREQ_POL_HIGH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 5, 5),
[HW_INTF_LOCK] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 3, 3),
[HW_INTR_HIGH_ACT] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 2, 2),
[HW_INTR_EDGE_TRIG] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 1, 1),
[HW_GLOBAL_INTR_EN] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 0, 0),
[SW_RESET_RESET_ATX] = REG_FIELD(ISP1763_HC_RESET, 3, 3),
[SW_RESET_RESET_ALL] = REG_FIELD(ISP1763_HC_RESET, 0, 0),
[HC_CHIP_ID_HIGH] = REG_FIELD(ISP1763_HC_CHIP_ID, 0, 15),
[HC_CHIP_ID_LOW] = REG_FIELD(ISP1763_HC_CHIP_REV, 8, 15),
[HC_CHIP_REV] = REG_FIELD(ISP1763_HC_CHIP_REV, 0, 7),
[HC_SCRATCH] = REG_FIELD(ISP1763_HC_SCRATCH, 0, 15),
[ISO_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 2, 2),
[INT_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 1, 1),
[ATL_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 0, 0),
[MEM_START_ADDR] = REG_FIELD(ISP1763_HC_MEMORY, 0, 15),
[HC_DATA] = REG_FIELD(ISP1763_HC_DATA, 0, 15),
[HC_INTERRUPT] = REG_FIELD(ISP1763_HC_INTERRUPT, 0, 10),
[HC_ATL_IRQ_ENABLE] = REG_FIELD(ISP1763_HC_INTERRUPT_ENABLE, 8, 8),
[HC_INT_IRQ_ENABLE] = REG_FIELD(ISP1763_HC_INTERRUPT_ENABLE, 7, 7),
[HC_ISO_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_ISO_IRQ_MASK_OR, 0, 15),
[HC_INT_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_INT_IRQ_MASK_OR, 0, 15),
[HC_ATL_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_ATL_IRQ_MASK_OR, 0, 15),
[HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_ISO_IRQ_MASK_AND, 0, 15),
[HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_INT_IRQ_MASK_AND, 0, 15),
[HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_ATL_IRQ_MASK_AND, 0, 15),
[HW_HC_2_DIS] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 15, 15),
[HW_OTG_DISABLE] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 10, 10),
[HW_SW_SEL_HC_DC] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 7, 7),
[HW_VBUS_DRV] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 4, 4),
[HW_SEL_CP_EXT] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 3, 3),
[HW_DM_PULLDOWN] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 2, 2),
[HW_DP_PULLDOWN] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 1, 1),
[HW_DP_PULLUP] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 0, 0),
[HW_HC_2_DIS_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 15, 15),
[HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 10, 10),
[HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 7, 7),
[HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 4, 4),
[HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 3, 3),
[HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 2, 2),
[HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 1, 1),
[HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 0, 0),
/* Make sure the array is sized properly during compilation */
[HC_FIELD_MAX] = {},
};
int isp1760_register(struct isp1760_device *isp, struct resource *mem, int irq,
unsigned long irqflags)
{
const struct regmap_config *hc_regmap;
const struct reg_field *hc_reg_fields;
struct isp1760_hcd *hcd;
struct regmap_field *f;
unsigned int devflags;
struct udevice *dev;
int ret;
int i;
hcd = &isp->hcd;
devflags = isp->devflags;
dev = isp->dev;
hcd->is_isp1763 = !!(devflags & ISP1760_FLAG_ISP1763);
if (!hcd->is_isp1763 && (devflags & ISP1760_FLAG_BUS_WIDTH_8)) {
dev_err(dev, "isp1760/61 do not support data width 8\n");
return -EINVAL;
}
if (hcd->is_isp1763) {
hc_regmap = &isp1763_hc_regmap_conf;
hc_reg_fields = &isp1763_hc_reg_fields[0];
} else {
hc_regmap = &isp1760_hc_regmap_conf;
hc_reg_fields = &isp1760_hc_reg_fields[0];
}
hcd->base = devm_ioremap(dev, mem->start, resource_size(mem));
if (IS_ERR(hcd->base))
return PTR_ERR(hcd->base);
hcd->regs = devm_regmap_init(dev, NULL, NULL, hc_regmap);
if (IS_ERR(hcd->regs))
return PTR_ERR(hcd->regs);
for (i = 0; i < HC_FIELD_MAX; i++) {
f = devm_regmap_field_alloc(dev, hcd->regs, hc_reg_fields[i]);
if (IS_ERR(f))
return PTR_ERR(f);
hcd->fields[i] = f;
}
if (hcd->is_isp1763)
hcd->memory_layout = &isp1763_memory_conf;
else
hcd->memory_layout = &isp176x_memory_conf;
ret = isp1760_init_core(isp);
if (ret < 0)
return ret;
hcd->dev = dev;
ret = isp1760_hcd_register(hcd, mem, irq, irqflags, dev);
if (ret < 0)
return ret;
ret = isp1760_hcd_lowlevel_init(hcd);
if (ret < 0)
return ret;
dev_set_drvdata(dev, isp);
return 0;
}
void isp1760_unregister(struct isp1760_device *isp)
{
isp1760_hcd_unregister(&isp->hcd);
}

View File

@ -0,0 +1,96 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the NXP ISP1760 chip
*
* Copyright 2021 Linaro, Rui Miguel Silva
* Copyright 2014 Laurent Pinchart
* Copyright 2007 Sebastian Siewior
*
* Contacts:
* Sebastian Siewior <bigeasy@linutronix.de>
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Rui Miguel Silva <rui.silva@linaro.org>
*/
#ifndef _ISP1760_CORE_H_
#define _ISP1760_CORE_H_
#include <linux/compat.h>
#include <linux/ioport.h>
#include <regmap.h>
#include "isp1760-hcd.h"
struct device;
struct gpio_desc;
/*
* Device flags that can vary from board to board. All of these
* indicate the most "atypical" case, so that a devflags of 0 is
* a sane default configuration.
*/
#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */
#define ISP1760_FLAG_PERIPHERAL_EN 0x00000004 /* Port 1 supports Peripheral mode*/
#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */
#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */
#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */
#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */
#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */
#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */
#define ISP1760_FLAG_ISP1763 0x00000200 /* Chip is ISP1763 */
#define ISP1760_FLAG_BUS_WIDTH_8 0x00000400 /* 8-bit data bus width */
struct isp1760_device {
struct udevice *dev;
unsigned int devflags;
struct gpio_desc *rst_gpio;
struct isp1760_hcd hcd;
};
int isp1760_register(struct isp1760_device *isp, struct resource *mem, int irq,
unsigned long irqflags);
void isp1760_unregister(struct isp1760_device *isp);
void isp1760_set_pullup(struct isp1760_device *isp, bool enable);
static inline u32 isp1760_field_read(struct regmap_field **fields, u32 field)
{
unsigned int val;
regmap_field_read(fields[field], &val);
return val;
}
static inline void isp1760_field_write(struct regmap_field **fields, u32 field,
u32 val)
{
regmap_field_write(fields[field], val);
}
static inline void isp1760_field_set(struct regmap_field **fields, u32 field)
{
isp1760_field_write(fields, field, 0xFFFFFFFF);
}
static inline void isp1760_field_clear(struct regmap_field **fields, u32 field)
{
isp1760_field_write(fields, field, 0);
}
static inline u32 isp1760_reg_read(struct regmap *regs, u32 reg)
{
unsigned int val;
regmap_read(regs, reg, &val);
return val;
}
static inline void isp1760_reg_write(struct regmap *regs, u32 reg, u32 val)
{
regmap_write(regs, reg, val);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ISP1760_HCD_H_
#define _ISP1760_HCD_H_
#include <regmap.h>
#include "isp1760-regs.h"
struct isp1760_qh;
struct isp1760_qtd;
struct resource;
struct usb_hcd;
struct isp1760_slotinfo {
struct isp1760_qh *qh;
struct isp1760_qtd *qtd;
unsigned long timestamp;
};
/* chip memory management */
#define ISP176x_BLOCK_MAX (32 + 20 + 4)
#define ISP176x_BLOCK_NUM 3
struct isp1760_memory_layout {
unsigned int blocks[ISP176x_BLOCK_NUM];
unsigned int blocks_size[ISP176x_BLOCK_NUM];
unsigned int slot_num;
unsigned int payload_blocks;
unsigned int payload_area_size;
};
struct isp1760_memory_chunk {
unsigned int start;
unsigned int size;
unsigned int free;
};
enum isp1760_queue_head_types {
QH_CONTROL,
QH_BULK,
QH_INTERRUPT,
QH_END
};
struct isp1760_hcd {
struct usb_hcd *hcd;
struct udevice *dev;
void __iomem *base;
struct regmap *regs;
struct regmap_field *fields[HC_FIELD_MAX];
bool is_isp1763;
const struct isp1760_memory_layout *memory_layout;
struct isp1760_slotinfo *atl_slots;
int atl_done_map;
struct isp1760_slotinfo *int_slots;
int int_done_map;
struct isp1760_memory_chunk memory_pool[ISP176x_BLOCK_MAX];
struct list_head qh_list[QH_END];
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024
unsigned int periodic_size;
unsigned int i_thresh;
unsigned long reset_done;
unsigned long next_statechange;
};
int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem,
int irq, unsigned long irqflags, struct udevice *dev);
void isp1760_hcd_unregister(struct isp1760_hcd *priv);
int isp1760_hcd_lowlevel_init(struct isp1760_hcd *priv);
int isp1760_init_kmem_once(void);
void isp1760_deinit_kmem_cache(void);
#endif /* _ISP1760_HCD_H_ */

View File

@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
*
* based on original code from:
* (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
*/
#include <common.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/device_compat.h>
#include <dm/devres.h>
#include <dm/lists.h>
#include <linux/bug.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/usb/otg.h>
#include <log.h>
#include <usb.h>
#include "isp1760-core.h"
#include "isp1760-regs.h"
#include "isp1760-uboot.h"
static int isp1760_of_to_plat(struct udevice *dev)
{
struct isp1760_device *isp = dev_get_plat(dev);
unsigned int devflags = 0;
u32 bus_width = 0;
ofnode dp;
if (!dev_has_ofnode(dev)) {
/* select isp1763 as the default device */
devflags = ISP1760_FLAG_ISP1763 | ISP1760_FLAG_BUS_WIDTH_16;
pr_err("isp1760: no platform data\n");
goto isp_setup;
}
dp = dev_ofnode(dev);
if (ofnode_device_is_compatible(dp, "nxp,usb-isp1761"))
devflags |= ISP1760_FLAG_ISP1761;
if (ofnode_device_is_compatible(dp, "nxp,usb-isp1763"))
devflags |= ISP1760_FLAG_ISP1763;
/*
* Some systems wire up only 8 of 16 data lines or
* 16 of the 32 data lines
*/
bus_width = ofnode_read_u32_default(dp, "bus-width", 16);
if (bus_width == 16)
devflags |= ISP1760_FLAG_BUS_WIDTH_16;
else if (bus_width == 8)
devflags |= ISP1760_FLAG_BUS_WIDTH_8;
if (usb_get_dr_mode(dev_ofnode(dev)) == USB_DR_MODE_PERIPHERAL)
devflags |= ISP1760_FLAG_PERIPHERAL_EN;
if (ofnode_read_bool(dp, "analog-oc"))
devflags |= ISP1760_FLAG_ANALOG_OC;
if (ofnode_read_bool(dp, "dack-polarity"))
devflags |= ISP1760_FLAG_DACK_POL_HIGH;
if (ofnode_read_bool(dp, "dreq-polarity"))
devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
isp_setup:
isp->devflags = devflags;
isp->dev = dev;
return 0;
}
static int isp1760_plat_probe(struct udevice *dev)
{
struct isp1760_device *isp = dev_get_plat(dev);
struct resource mem_res;
struct resource irq_res;
int ret;
dev_read_resource(dev, 0, &mem_res);
dev_read_resource(dev, 1, &irq_res);
isp1760_init_kmem_once();
ret = isp1760_register(isp, &mem_res, irq_res.start, irq_res.flags);
if (ret < 0) {
isp1760_deinit_kmem_cache();
return ret;
}
return 0;
}
static int isp1760_plat_remove(struct udevice *dev)
{
struct isp1760_device *isp = dev_get_plat(dev);
isp1760_deinit_kmem_cache();
isp1760_unregister(isp);
return 0;
}
static const struct udevice_id isp1760_ids[] = {
{ .compatible = "nxp,usb-isp1760", },
{ .compatible = "nxp,usb-isp1761", },
{ .compatible = "nxp,usb-isp1763", },
{ },
};
U_BOOT_DRIVER(isp1760) = {
.name = "isp1760",
.id = UCLASS_USB,
.of_match = isp1760_ids,
.of_to_plat = isp1760_of_to_plat,
.ops = &isp1760_usb_ops,
.probe = isp1760_plat_probe,
.remove = isp1760_plat_remove,
.plat_auto = sizeof(struct isp1760_device),
.priv_auto = sizeof(struct isp1760_host_data),
};

View File

@ -0,0 +1,292 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the NXP ISP1760 chip
*
* Copyright 2021 Linaro, Rui Miguel Silva
* Copyright 2014 Laurent Pinchart
* Copyright 2007 Sebastian Siewior
*
* Contacts:
* Sebastian Siewior <bigeasy@linutronix.de>
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Rui Miguel Silva <rui.silva@linaro.org>
*/
#ifndef _ISP176x_REGS_H_
#define _ISP176x_REGS_H_
/* -----------------------------------------------------------------------------
* Host Controller
*/
/* ISP1760/31 */
/* EHCI capability registers */
#define ISP176x_HC_VERSION 0x002
#define ISP176x_HC_HCSPARAMS 0x004
#define ISP176x_HC_HCCPARAMS 0x008
/* EHCI operational registers */
#define ISP176x_HC_USBCMD 0x020
#define ISP176x_HC_USBSTS 0x024
#define ISP176x_HC_FRINDEX 0x02c
#define ISP176x_HC_CONFIGFLAG 0x060
#define ISP176x_HC_PORTSC1 0x064
#define ISP176x_HC_ISO_PTD_DONEMAP 0x130
#define ISP176x_HC_ISO_PTD_SKIPMAP 0x134
#define ISP176x_HC_ISO_PTD_LASTPTD 0x138
#define ISP176x_HC_INT_PTD_DONEMAP 0x140
#define ISP176x_HC_INT_PTD_SKIPMAP 0x144
#define ISP176x_HC_INT_PTD_LASTPTD 0x148
#define ISP176x_HC_ATL_PTD_DONEMAP 0x150
#define ISP176x_HC_ATL_PTD_SKIPMAP 0x154
#define ISP176x_HC_ATL_PTD_LASTPTD 0x158
/* Configuration Register */
#define ISP176x_HC_HW_MODE_CTRL 0x300
#define ISP176x_HC_CHIP_ID 0x304
#define ISP176x_HC_SCRATCH 0x308
#define ISP176x_HC_RESET 0x30c
#define ISP176x_HC_BUFFER_STATUS 0x334
#define ISP176x_HC_MEMORY 0x33c
/* Interrupt Register */
#define ISP176x_HC_INTERRUPT 0x310
#define ISP176x_HC_INTERRUPT_ENABLE 0x314
#define ISP176x_HC_ISO_IRQ_MASK_OR 0x318
#define ISP176x_HC_INT_IRQ_MASK_OR 0x31c
#define ISP176x_HC_ATL_IRQ_MASK_OR 0x320
#define ISP176x_HC_ISO_IRQ_MASK_AND 0x324
#define ISP176x_HC_INT_IRQ_MASK_AND 0x328
#define ISP176x_HC_ATL_IRQ_MASK_AND 0x32c
#define ISP176x_HC_OTG_CTRL_SET 0x374
#define ISP176x_HC_OTG_CTRL_CLEAR 0x376
enum isp176x_host_controller_fields {
/* HC_PORTSC1 */
PORT_OWNER, PORT_POWER, PORT_LSTATUS, PORT_RESET, PORT_SUSPEND,
PORT_RESUME, PORT_PE, PORT_CSC, PORT_CONNECT,
/* HC_HCSPARAMS */
HCS_PPC, HCS_N_PORTS,
/* HC_HCCPARAMS */
HCC_ISOC_CACHE, HCC_ISOC_THRES,
/* HC_USBCMD */
CMD_LRESET, CMD_RESET, CMD_RUN,
/* HC_USBSTS */
STS_PCD,
/* HC_FRINDEX */
HC_FRINDEX,
/* HC_CONFIGFLAG */
FLAG_CF,
/* ISO/INT/ATL PTD */
HC_ISO_PTD_DONEMAP, HC_ISO_PTD_SKIPMAP, HC_ISO_PTD_LASTPTD,
HC_INT_PTD_DONEMAP, HC_INT_PTD_SKIPMAP, HC_INT_PTD_LASTPTD,
HC_ATL_PTD_DONEMAP, HC_ATL_PTD_SKIPMAP, HC_ATL_PTD_LASTPTD,
/* HC_HW_MODE_CTRL */
ALL_ATX_RESET, HW_ANA_DIGI_OC, HW_DEV_DMA, HW_COMN_IRQ, HW_COMN_DMA,
HW_DATA_BUS_WIDTH, HW_DACK_POL_HIGH, HW_DREQ_POL_HIGH, HW_INTR_HIGH_ACT,
HW_INTF_LOCK, HW_INTR_EDGE_TRIG, HW_GLOBAL_INTR_EN,
/* HC_CHIP_ID */
HC_CHIP_ID_HIGH, HC_CHIP_ID_LOW, HC_CHIP_REV,
/* HC_SCRATCH */
HC_SCRATCH,
/* HC_RESET */
SW_RESET_RESET_ATX, SW_RESET_RESET_HC, SW_RESET_RESET_ALL,
/* HC_BUFFER_STATUS */
ISO_BUF_FILL, INT_BUF_FILL, ATL_BUF_FILL,
/* HC_MEMORY */
MEM_BANK_SEL, MEM_START_ADDR,
/* HC_DATA */
HC_DATA,
/* HC_INTERRUPT */
HC_INTERRUPT,
/* HC_INTERRUPT_ENABLE */
HC_INT_IRQ_ENABLE, HC_ATL_IRQ_ENABLE,
/* INTERRUPT MASKS */
HC_ISO_IRQ_MASK_OR, HC_INT_IRQ_MASK_OR, HC_ATL_IRQ_MASK_OR,
HC_ISO_IRQ_MASK_AND, HC_INT_IRQ_MASK_AND, HC_ATL_IRQ_MASK_AND,
/* HW_OTG_CTRL_SET */
HW_OTG_DISABLE, HW_SW_SEL_HC_DC, HW_VBUS_DRV, HW_SEL_CP_EXT,
HW_DM_PULLDOWN, HW_DP_PULLDOWN, HW_DP_PULLUP, HW_HC_2_DIS,
/* HW_OTG_CTRL_CLR */
HW_OTG_DISABLE_CLEAR, HW_SW_SEL_HC_DC_CLEAR, HW_VBUS_DRV_CLEAR,
HW_SEL_CP_EXT_CLEAR, HW_DM_PULLDOWN_CLEAR, HW_DP_PULLDOWN_CLEAR,
HW_DP_PULLUP_CLEAR, HW_HC_2_DIS_CLEAR,
/* Last element */
HC_FIELD_MAX,
};
/* ISP1763 */
/* EHCI operational registers */
#define ISP1763_HC_USBCMD 0x8c
#define ISP1763_HC_USBSTS 0x90
#define ISP1763_HC_FRINDEX 0x98
#define ISP1763_HC_CONFIGFLAG 0x9c
#define ISP1763_HC_PORTSC1 0xa0
#define ISP1763_HC_ISO_PTD_DONEMAP 0xa4
#define ISP1763_HC_ISO_PTD_SKIPMAP 0xa6
#define ISP1763_HC_ISO_PTD_LASTPTD 0xa8
#define ISP1763_HC_INT_PTD_DONEMAP 0xaa
#define ISP1763_HC_INT_PTD_SKIPMAP 0xac
#define ISP1763_HC_INT_PTD_LASTPTD 0xae
#define ISP1763_HC_ATL_PTD_DONEMAP 0xb0
#define ISP1763_HC_ATL_PTD_SKIPMAP 0xb2
#define ISP1763_HC_ATL_PTD_LASTPTD 0xb4
/* Configuration Register */
#define ISP1763_HC_HW_MODE_CTRL 0xb6
#define ISP1763_HC_CHIP_REV 0x70
#define ISP1763_HC_CHIP_ID 0x72
#define ISP1763_HC_SCRATCH 0x78
#define ISP1763_HC_RESET 0xb8
#define ISP1763_HC_BUFFER_STATUS 0xba
#define ISP1763_HC_MEMORY 0xc4
#define ISP1763_HC_DATA 0xc6
/* Interrupt Register */
#define ISP1763_HC_INTERRUPT 0xd4
#define ISP1763_HC_INTERRUPT_ENABLE 0xd6
#define ISP1763_HC_ISO_IRQ_MASK_OR 0xd8
#define ISP1763_HC_INT_IRQ_MASK_OR 0xda
#define ISP1763_HC_ATL_IRQ_MASK_OR 0xdc
#define ISP1763_HC_ISO_IRQ_MASK_AND 0xde
#define ISP1763_HC_INT_IRQ_MASK_AND 0xe0
#define ISP1763_HC_ATL_IRQ_MASK_AND 0xe2
#define ISP1763_HC_OTG_CTRL_SET 0xe4
#define ISP1763_HC_OTG_CTRL_CLEAR 0xe6
/* -----------------------------------------------------------------------------
* Peripheral Controller
*/
#define DC_IEPTX(n) (1 << (11 + 2 * (n)))
#define DC_IEPRX(n) (1 << (10 + 2 * (n)))
#define DC_IEPRXTX(n) (3 << (10 + 2 * (n)))
#define ISP176x_DC_CDBGMOD_ACK BIT(6)
#define ISP176x_DC_DDBGMODIN_ACK BIT(4)
#define ISP176x_DC_DDBGMODOUT_ACK BIT(2)
#define ISP176x_DC_IEP0SETUP BIT(8)
#define ISP176x_DC_IEVBUS BIT(7)
#define ISP176x_DC_IEHS_STA BIT(5)
#define ISP176x_DC_IERESM BIT(4)
#define ISP176x_DC_IESUSP BIT(3)
#define ISP176x_DC_IEBRST BIT(0)
#define ISP176x_DC_ENDPTYP_ISOC 0x01
#define ISP176x_DC_ENDPTYP_BULK 0x02
#define ISP176x_DC_ENDPTYP_INTERRUPT 0x03
/* Initialization Registers */
#define ISP176x_DC_ADDRESS 0x0200
#define ISP176x_DC_MODE 0x020c
#define ISP176x_DC_INTCONF 0x0210
#define ISP176x_DC_DEBUG 0x0212
#define ISP176x_DC_INTENABLE 0x0214
/* Data Flow Registers */
#define ISP176x_DC_EPMAXPKTSZ 0x0204
#define ISP176x_DC_EPTYPE 0x0208
#define ISP176x_DC_BUFLEN 0x021c
#define ISP176x_DC_BUFSTAT 0x021e
#define ISP176x_DC_DATAPORT 0x0220
#define ISP176x_DC_CTRLFUNC 0x0228
#define ISP176x_DC_EPINDEX 0x022c
/* DMA Registers */
#define ISP176x_DC_DMACMD 0x0230
#define ISP176x_DC_DMATXCOUNT 0x0234
#define ISP176x_DC_DMACONF 0x0238
#define ISP176x_DC_DMAHW 0x023c
#define ISP176x_DC_DMAINTREASON 0x0250
#define ISP176x_DC_DMAINTEN 0x0254
#define ISP176x_DC_DMAEP 0x0258
#define ISP176x_DC_DMABURSTCOUNT 0x0264
/* General Registers */
#define ISP176x_DC_INTERRUPT 0x0218
#define ISP176x_DC_CHIPID 0x0270
#define ISP176x_DC_FRAMENUM 0x0274
#define ISP176x_DC_SCRATCH 0x0278
#define ISP176x_DC_UNLOCKDEV 0x027c
#define ISP176x_DC_INTPULSEWIDTH 0x0280
#define ISP176x_DC_TESTMODE 0x0284
enum isp176x_device_controller_fields {
/* DC_ADDRESS */
DC_DEVEN, DC_DEVADDR,
/* DC_MODE */
DC_VBUSSTAT, DC_SFRESET, DC_GLINTENA,
/* DC_INTCONF */
DC_CDBGMOD_ACK, DC_DDBGMODIN_ACK, DC_DDBGMODOUT_ACK, DC_INTPOL,
/* DC_INTENABLE */
DC_IEPRXTX_7, DC_IEPRXTX_6, DC_IEPRXTX_5, DC_IEPRXTX_4, DC_IEPRXTX_3,
DC_IEPRXTX_2, DC_IEPRXTX_1, DC_IEPRXTX_0,
DC_IEP0SETUP, DC_IEVBUS, DC_IEHS_STA, DC_IERESM, DC_IESUSP, DC_IEBRST,
/* DC_EPINDEX */
DC_EP0SETUP, DC_ENDPIDX, DC_EPDIR,
/* DC_CTRLFUNC */
DC_CLBUF, DC_VENDP, DC_DSEN, DC_STATUS, DC_STALL,
/* DC_BUFLEN */
DC_BUFLEN,
/* DC_EPMAXPKTSZ */
DC_FFOSZ,
/* DC_EPTYPE */
DC_EPENABLE, DC_ENDPTYP,
/* DC_FRAMENUM */
DC_FRAMENUM, DC_UFRAMENUM,
/* DC_CHIP_ID */
DC_CHIP_ID_HIGH, DC_CHIP_ID_LOW,
/* DC_SCRATCH */
DC_SCRATCH,
/* Last element */
DC_FIELD_MAX,
};
/* ISP1763 */
/* Initialization Registers */
#define ISP1763_DC_ADDRESS 0x00
#define ISP1763_DC_MODE 0x0c
#define ISP1763_DC_INTCONF 0x10
#define ISP1763_DC_INTENABLE 0x14
/* Data Flow Registers */
#define ISP1763_DC_EPMAXPKTSZ 0x04
#define ISP1763_DC_EPTYPE 0x08
#define ISP1763_DC_BUFLEN 0x1c
#define ISP1763_DC_BUFSTAT 0x1e
#define ISP1763_DC_DATAPORT 0x20
#define ISP1763_DC_CTRLFUNC 0x28
#define ISP1763_DC_EPINDEX 0x2c
/* DMA Registers */
#define ISP1763_DC_DMACMD 0x30
#define ISP1763_DC_DMATXCOUNT 0x34
#define ISP1763_DC_DMACONF 0x38
#define ISP1763_DC_DMAHW 0x3c
#define ISP1763_DC_DMAINTREASON 0x50
#define ISP1763_DC_DMAINTEN 0x54
#define ISP1763_DC_DMAEP 0x58
#define ISP1763_DC_DMABURSTCOUNT 0x64
/* General Registers */
#define ISP1763_DC_INTERRUPT 0x18
#define ISP1763_DC_CHIPID_LOW 0x70
#define ISP1763_DC_CHIPID_HIGH 0x72
#define ISP1763_DC_FRAMENUM 0x74
#define ISP1763_DC_SCRATCH 0x78
#define ISP1763_DC_UNLOCKDEV 0x7c
#define ISP1763_DC_INTPULSEWIDTH 0x80
#define ISP1763_DC_TESTMODE 0x84
#endif

View File

@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the NXP ISP1760 chip
*
* Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
*
*/
#include <common.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/device_compat.h>
#include <dm/devres.h>
#include <dm/lists.h>
#include <linux/bug.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/usb/otg.h>
#include <linux/usb/usb_urb_compat.h>
#include <log.h>
#include <usb.h>
#include "isp1760-core.h"
#include "isp1760-hcd.h"
#include "isp1760-regs.h"
#include "isp1760-uboot.h"
static int isp1760_msg_submit_control(struct udevice *dev,
struct usb_device *udev,
unsigned long pipe, void *buffer,
int length, struct devrequest *setup)
{
struct isp1760_host_data *host = dev_get_priv(dev);
return usb_urb_submit_control(&host->hcd, &host->urb, &host->hep, udev,
pipe, buffer, length, setup, 0,
host->host_speed);
}
static int isp1760_msg_submit_bulk(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length)
{
struct isp1760_host_data *host = dev_get_priv(dev);
return usb_urb_submit_bulk(&host->hcd, &host->urb, &host->hep, udev,
pipe, buffer, length);
}
static int isp1760_msg_submit_irq(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
int interval, bool nonblock)
{
struct isp1760_host_data *host = dev_get_priv(dev);
return usb_urb_submit_irq(&host->hcd, &host->urb, &host->hep, udev,
pipe, buffer, length, interval);
}
static int isp1760_get_max_xfer_size(struct udevice *dev, size_t *size)
{
struct isp1760_host_data *host = dev_get_priv(dev);
struct isp1760_hcd *priv = host->hcd.hcd_priv;
const struct isp1760_memory_layout *mem = priv->memory_layout;
*size = mem->blocks_size[ISP176x_BLOCK_NUM - 1];
return 0;
}
struct dm_usb_ops isp1760_usb_ops = {
.control = isp1760_msg_submit_control,
.bulk = isp1760_msg_submit_bulk,
.interrupt = isp1760_msg_submit_irq,
.get_max_xfer_size = isp1760_get_max_xfer_size,
};

View File

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the NXP ISP1760 chip
*
* Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
*
*/
#ifndef __ISP1760_UBOOT_H__
#define __ISP1760_UBOOT_H__
#include <linux/usb/usb_urb_compat.h>
#include <usb.h>
#include "isp1760-core.h"
struct isp1760_host_data {
struct isp1760_hcd *priv;
struct usb_hcd hcd;
enum usb_device_speed host_speed;
struct usb_host_endpoint hep;
struct urb urb;
};
extern struct dm_usb_ops isp1760_usb_ops;
#endif

View File

@ -89,9 +89,9 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/musb.h>
#include <linux/usb/usb_urb_compat.h>
#include <asm/io.h>
#include "linux-compat.h"
#include "usb-compat.h"
#endif
#include "musb_core.h"

View File

@ -26,8 +26,8 @@
#include <dm/device_compat.h>
#include <usb.h>
#include <linux/bug.h>
#include <linux/usb/usb_urb_compat.h>
#include "linux-compat.h"
#include "usb-compat.h"
#endif
#include "musb_core.h"

View File

@ -10,7 +10,7 @@
#ifndef _MUSB_HOST_H
#define _MUSB_HOST_H
#ifdef __UBOOT__
#include "usb-compat.h"
#include <linux/usb/usb_urb_compat.h>
#endif
static inline struct usb_hcd *musb_to_hcd(struct musb *musb)

View File

@ -8,10 +8,10 @@
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/usb_urb_compat.h>
#include <usb.h>
#include "linux-compat.h"
#include "usb-compat.h"
#include "musb_core.h"
#include "musb_host.h"
#include "musb_gadget.h"
@ -453,39 +453,3 @@ struct musb *musb_register(struct musb_hdrc_platform_data *plat, void *bdata,
return *musbp;
}
#if CONFIG_IS_ENABLED(DM_USB)
struct usb_device *usb_dev_get_parent(struct usb_device *udev)
{
struct udevice *parent = udev->dev->parent;
/*
* When called from usb-uclass.c: usb_scan_device() udev->dev points
* to the parent udevice, not the actual udevice belonging to the
* udev as the device is not instantiated yet.
*
* If dev is an usb-bus, then we are called from usb_scan_device() for
* an usb-device plugged directly into the root port, return NULL.
*/
if (device_get_uclass_id(udev->dev) == UCLASS_USB)
return NULL;
/*
* If these 2 are not the same we are being called from
* usb_scan_device() and udev itself is the parent.
*/
if (dev_get_parent_priv(udev->dev) != udev)
return udev;
/* We are being called normally, use the parent pointer */
if (device_get_uclass_id(parent) == UCLASS_USB_HUB)
return dev_get_parent_priv(parent);
return NULL;
}
#else
struct usb_device *usb_dev_get_parent(struct usb_device *udev)
{
return udev->parent;
}
#endif

View File

@ -8,8 +8,8 @@
#define __MUSB_UBOOT_H__
#include <usb.h>
#include <linux/usb/usb_urb_compat.h>
#include "linux-compat.h"
#include "usb-compat.h"
#include "musb_core.h"
struct musb_host_data {

View File

@ -24,4 +24,10 @@
#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1
#define BOOT_TARGET_DEVICES(func) \
func(USB, usb, 0)
#include <config_distro_bootcmd.h>
#endif

View File

@ -1,16 +1,32 @@
#ifndef __USB_COMPAT_H__
#define __USB_COMPAT_H__
/* SPDX-License-Identifier: GPL-2.0+ */
#include "usb.h"
#ifndef __USB_URB_COMPAT_H__
#define __USB_URB_COMPAT_H__
#include <linux/compat.h>
#include <usb.h>
struct udevice;
struct urb;
struct usb_hcd;
struct usb_urb_ops {
int (*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags);
int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status);
int (*hub_control)(struct usb_hcd *hcd, struct usb_device *dev,
unsigned long pipe, void *buffer, int len,
struct devrequest *setup);
irqreturn_t (*isr)(int irq, void *priv);
};
struct usb_hcd {
void *hcd_priv;
const struct usb_urb_ops *urb_ops;
};
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct usb_endpoint_descriptor desc;
struct list_head urb_list;
void *hcpriv;
};
@ -23,8 +39,6 @@ struct usb_host_endpoint {
#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */
#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */
struct urb;
typedef void (*usb_complete_t)(struct urb *);
struct urb {
@ -76,4 +90,25 @@ static inline int usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd,
*/
struct usb_device *usb_dev_get_parent(struct usb_device *udev);
int usb_urb_submit_control(struct usb_hcd *hcd, struct urb *urb,
struct usb_host_endpoint *hep,
struct usb_device *dev, unsigned long pipe,
void *buffer, int len, struct devrequest *setup,
int interval, enum usb_device_speed speed);
int usb_urb_submit_bulk(struct usb_hcd *hcd, struct urb *urb,
struct usb_host_endpoint *hep, struct usb_device *dev,
unsigned long pipe, void *buffer, int len);
int usb_urb_submit_irq(struct usb_hcd *hcd, struct urb *urb,
struct usb_host_endpoint *hep, struct usb_device *dev,
unsigned long pipe, void *buffer, int len, int interval);
void usb_urb_fill(struct urb *urb, struct usb_host_endpoint *hep,
struct usb_device *dev, int endpoint_type,
unsigned long pipe, void *buffer, int len,
struct devrequest *setup, int interval);
int usb_urb_submit(struct usb_hcd *hcd, struct urb *urb);
#endif /* __USB_COMPAT_H__ */

View File

@ -81,6 +81,32 @@
#define EndpointOutRequest \
((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
/* class requests from the USB 2.0 hub spec, table 11-15 */
#define HUB_CLASS_REQ(dir, type, request) ((((dir) | (type)) << 8) | (request))
/* GetBusState and SetHubDescriptor are optional, omitted */
#define ClearHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, \
USB_REQ_CLEAR_FEATURE)
#define ClearPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \
USB_REQ_CLEAR_FEATURE)
#define GetHubDescriptor HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, \
USB_REQ_GET_DESCRIPTOR)
#define GetHubStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, \
USB_REQ_GET_STATUS)
#define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, \
USB_REQ_GET_STATUS)
#define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, \
USB_REQ_SET_FEATURE)
#define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \
USB_REQ_SET_FEATURE)
#define ClearTTBuffer HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \
HUB_CLEAR_TT_BUFFER)
#define ResetTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \
HUB_RESET_TT)
#define GetTTState HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, \
HUB_GET_TT_STATE)
#define StopTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \
HUB_STOP_TT)
/* Descriptor types */
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
@ -289,10 +315,16 @@
#define USB_SS_PORT_STAT_C_CONFIG_ERROR 0x0080
/* wHubCharacteristics (masks) */
#define HUB_CHAR_COMMON_OCPM 0x0000 /* All ports Over-Current reporting */
#define HUB_CHAR_INDV_PORT_LPSM 0x0001 /* per-port power control */
#define HUB_CHAR_NO_LPSM 0x0002 /* no power switching */
#define HUB_CHAR_LPSM 0x0003
#define HUB_CHAR_COMPOUND 0x0004
#define HUB_CHAR_INDV_PORT_OCPM 0x0008 /* per-port Over-current reporting */
#define HUB_CHAR_NO_OCPM 0x0010 /* No Over-current Protection support */
#define HUB_CHAR_OCPM 0x0018
#define HUB_CHAR_TTTT 0x0060 /* TT Think Time mask */
#define HUB_CHAR_PORTIND 0x0080 /* per-port indicators (LEDs) */
/*
* Hub Status & Hub Change bit masks