Skip to contentSkip to conversation

Various USB improvements

PATCH Superseded Simon Glass 4 participants 29 messages Dec 13, 2012 – Dec 15, 2012

Subsystems (2)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover Various USB improvements

This series adds a few USB improvements for Marek:

- Support for interupt transfers
- Faster start-up and proper shutdown of ASIX USB adapter
- Basic 64-bit controller support (used by x86)
- Fix detection of empty USB media readers
- Clean up after device configuration failure

Michael Spang (1):
usb: usbeth: smsc95xx: remove EEPROM loaded check

Milind Choudhary (1):
usb: Clean up newly allocated device nodes in case of configuration
failure

Patrick Georgi (1):
usb: ehci: Support interrupt transfers via periodic list

Simon Glass (2):
usb: net: asix: Do a fast init if link already established
x86: Enable USB features for coreboot

Vincent Palatin (6):
usb: ehci: Add 64-bit controller support
usb: ehci: generic PCI support
usb: ehci: Fix aliasing issue in EHCI interrupt code
usb: ehci: Add timeout on interrupt endpoint operations
usb: properly detect empty mass storage media reader
usb: Add multiple controllers support for EHCI PCI

 common/usb.c                |   12 ++
 common/usb_hub.c            |    2 +
 common/usb_storage.c        |   10 ++
 drivers/usb/eth/asix.c      |   40 +++++-
 drivers/usb/eth/smsc95xx.c  |    4 -
 drivers/usb/host/ehci-hcd.c |  312 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/host/ehci-pci.c |   55 +++++++-
 drivers/usb/host/ehci.h     |    6 +-
 include/configs/coreboot.h  |   17 +++
 include/usb.h               |    1 +
 include/usb_ether.h         |    1 +
 11 files changed, 437 insertions(+), 23 deletions(-)
Signature · 1 line
1.7.7.3
#1 lore raw

PATCH 1/11 usb: net: asix: Do a fast init if link already established

The Asix driver takes the link down during init() and then brings it back up. This commit changes this so that if a link has already been established successfully we simply check that the link is still good.

Also fix up asix_halt() to actually halt RX on the interface. Previously this was not done, so the device would continue to operate evern when halted, violating a U-Boot requirement.

This reduces the delay between successive network commands.

---
 drivers/usb/eth/asix.c |   40 ++++++++++++++++++++++++++++++++--------
 include/usb_ether.h    |    1 +
 2 files changed, 33 insertions(+), 8 deletions(-)
drivers/usb/eth/asix.c +32 -8
index 75ec8f7..a991fea 100644
@@ -84,6 +84,7 @@
 
 #define AX_DEFAULT_RX_CTL	\
 	(AX_RX_CTL_SO | AX_RX_CTL_AB)
+#define AX_DISABLE_RX_CTL	AX_RX_CTL_AB
 
 /* GPIO 2 toggles */
 #define AX_GPIO_GPO2EN		0x10	/* GPIO2 Output enable */
@@ -410,17 +411,16 @@ static int asix_basic_reset(struct ueth_data *dev)
 	return 0;
 }
 
-/*
- * Asix callbacks
- */
-static int asix_init(struct eth_device *eth, bd_t *bd)
+static int full_init(struct eth_device *eth)
 {
-	struct ueth_data	*dev = (struct ueth_data *)eth->priv;
-	int timeout = 0;
-#define TIMEOUT_RESOLUTION 50	/* ms */
-	int link_detected;
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
 
 	debug("** %s()\n", __func__);
+	if (asix_basic_reset(dev))
+		goto out_err;
+
+	if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0)
+		goto out_err;
 
 	dev->phy_id = asix_get_phy_addr(dev);
 	if (dev->phy_id < 0)
@@ -446,6 +446,25 @@ static int asix_init(struct eth_device *eth, bd_t *bd)
 		debug("Write IPG,IPG1,IPG2 failed\n");
 		goto out_err;
 	}
+	return 0;
+out_err:
+	return -1;
+}
+
+/*
+ * Asix callbacks
+ */
+static int asix_init(struct eth_device *eth, bd_t *bd)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+	int timeout = 0;
+#define TIMEOUT_RESOLUTION 50	/* ms */
+	int link_detected;
+
+	debug("** %s()\n", __func__);
+
+	if (!dev->has_been_running && full_init(eth))
+		return -1;
 
 	if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0)
 		goto out_err;
@@ -467,6 +486,7 @@ static int asix_init(struct eth_device *eth, bd_t *bd)
 		printf("unable to connect.\n");
 		goto out_err;
 	}
+	dev->has_been_running = 1;
 
 	return 0;
 out_err:
@@ -571,7 +591,11 @@ static int asix_recv(struct eth_device *eth)
 
 static void asix_halt(struct eth_device *eth)
 {
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+	/* Disable packet reception */
 	debug("** %s()\n", __func__);
+	(void)asix_write_rx_ctl(dev, AX_DISABLE_RX_CTL);
 }
 
 /*
include/usb_ether.h +1 -0
index 7c7aecb..6145210 100644
@@ -49,6 +49,7 @@ struct ueth_data {
 	unsigned char	subclass;		/* as in overview */
 	unsigned char	protocol;		/* .............. */
 	unsigned char	irqinterval;	/* Intervall for IRQ Pipe */
+	char has_been_running;		/* PHY already inited */
 
 	/* driver private */
 	void *dev_priv;
Signature · 1 line
1.7.7.3
#2 lore raw

PATCH 1/11 usb: net: asix: Do a fast init if link already established

Dear Simon Glass,

Quoted 10 lines

The Asix driver takes the link down during init() and then brings it back up. This commit changes this so that if a link has already been established successfully we simply check that the link is still good.

Also fix up asix_halt() to actually halt RX on the interface. Previously this was not done, so the device would continue to operate evern when halted, violating a U-Boot requirement.

This reduces the delay between successive network commands.

Joe, this is yours. Gimme a go/no-go and an Ack ;-)

Best regards,
Marek Vasut

#3 lore raw

PATCH 1/11 usb: net: asix: Do a fast init if link already established

In reply to Simon Glass

Hi Simon,

Quoted 103 lines · Simon Glass

The Asix driver takes the link down during init() and then brings it back up. This commit changes this so that if a link has already been established successfully we simply check that the link is still good.

Also fix up asix_halt() to actually halt RX on the interface. Previously this was not done, so the device would continue to operate evern when halted, violating a U-Boot requirement.

This reduces the delay between successive network commands.

---
 drivers/usb/eth/asix.c |   40 ++++++++++++++++++++++++++++++++--------
 include/usb_ether.h    |    1 +
 2 files changed, 33 insertions(+), 8 deletions(-)
drivers/usb/eth/asix.c +32 -8
index 75ec8f7..a991fea 100644
@@ -84,6 +84,7 @@
 
 #define AX_DEFAULT_RX_CTL      \
        (AX_RX_CTL_SO | AX_RX_CTL_AB)
+#define AX_DISABLE_RX_CTL      AX_RX_CTL_AB
 
 /* GPIO 2 toggles */
 #define AX_GPIO_GPO2EN         0x10    /* GPIO2 Output enable */
@@ -410,17 +411,16 @@ static int asix_basic_reset(struct ueth_data *dev)
        return 0;
 }
 
-/*
- * Asix callbacks
- */
-static int asix_init(struct eth_device *eth, bd_t *bd)
+static int full_init(struct eth_device *eth)
 {
-       struct ueth_data        *dev = (struct ueth_data *)eth->priv;
-       int timeout = 0;
-#define TIMEOUT_RESOLUTION 50  /* ms */
-       int link_detected;
+       struct ueth_data *dev = (struct ueth_data *)eth->priv;
 
        debug("** %s()\n", __func__);
+       if (asix_basic_reset(dev))
+               goto out_err;
+
+       if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0)
+               goto out_err;
 
        dev->phy_id = asix_get_phy_addr(dev);
        if (dev->phy_id < 0)
@@ -446,6 +446,25 @@ static int asix_init(struct eth_device *eth, bd_t *bd)
                debug("Write IPG,IPG1,IPG2 failed\n");
                goto out_err;
        }
+       return 0;
+out_err:
+       return -1;
+}
+
+/*
+ * Asix callbacks
+ */
+static int asix_init(struct eth_device *eth, bd_t *bd)
+{
+       struct ueth_data *dev = (struct ueth_data *)eth->priv;
+       int timeout = 0;
+#define TIMEOUT_RESOLUTION 50  /* ms */
+       int link_detected;
+
+       debug("** %s()\n", __func__);
+
+       if (!dev->has_been_running && full_init(eth))
+               return -1;
 
        if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0)
                goto out_err;
@@ -467,6 +486,7 @@ static int asix_init(struct eth_device *eth, bd_t *bd)
                printf("unable to connect.\n");
                goto out_err;
        }
+       dev->has_been_running = 1;
 
        return 0;
 out_err:
@@ -571,7 +591,11 @@ static int asix_recv(struct eth_device *eth)
 
 static void asix_halt(struct eth_device *eth)
 {
+       struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+       /* Disable packet reception */
        debug("** %s()\n", __func__);
+       (void)asix_write_rx_ctl(dev, AX_DISABLE_RX_CTL);
 }
 
 /*
include/usb_ether.h +0 -0
index 7c7aecb..6145210 100644

include/usb_ether.h struct ueth_data { · line 55

Quoted 7 lines
include/usb_ether.h +1 -0
@@ -49,6 +49,7 @@ struct ueth_data {
        unsigned char   subclass;               /* as in overview */
        unsigned char   protocol;               /* .............. */
        unsigned char   irqinterval;    /* Intervall for IRQ Pipe */
+       char has_been_running;          /* PHY already inited */
 
        /* driver private */
        void *dev_priv;
#4 lore raw

PATCH 2/11 usb: ehci: Add 64-bit controller support

In reply to Simon Glass

From: Vincent Palatin <[email protected]>

On EHCI controller with 64-bit address space support, we must initialize properly the high word for the PCI bus master accesses.

---
 drivers/usb/host/ehci-hcd.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
drivers/usb/host/ehci-hcd.c +3 -0
index 7f98a63..20309ad 100644
@@ -870,6 +870,9 @@ int usb_lowlevel_init(int index, void **controller)
 	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
 		return -1;
 #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);
 
 	qh_list = &ehcic[index].qh_list;
 
Signature · 1 line
1.7.7.3
Applied
#5 lore raw

PATCH 2/11 usb: ehci: Add 64-bit controller support

Dear Simon Glass,

Quoted 16 lines

From: Vincent Palatin <[email protected]>

On EHCI controller with 64-bit address space support, we must initialize properly the high word for the PCI bus master accesses.

---
 drivers/usb/host/ehci-hcd.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
drivers/usb/host/ehci-hcd.c +0 -0
index 7f98a63..20309ad 100644

drivers/usb/host/ehci-hcd.c int usb_lowlevel_init(int index, void **controller) · line 877

Quoted 8 lines
drivers/usb/host/ehci-hcd.c +3 -0
@@ -870,6 +870,9 @@ int usb_lowlevel_init(int index, void **controller)
 	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
 		return -1;
 #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);
 
 	qh_list = &ehcic[index].qh_list;

Applied, thanks

Best regards,
Marek Vasut

#6 lore raw

PATCH 3/11 usb: ehci: generic PCI support

In reply to Simon Glass

From: Vincent Palatin <[email protected]>

Instead of hardcoding the PCI IDs on the USB controller, use the PCI class to detect them.

Ensure the busmaster bit is properly set in the PCI configuration.

---
 drivers/usb/host/ehci-pci.c |   37 +++++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)
drivers/usb/host/ehci-pci.c +37 -0
index 29af02d..0dc0e6e 100644
@@ -32,6 +32,34 @@ static struct pci_device_id ehci_pci_ids[] = {
 	{0x12D8, 0x400F},	/* Pericom */
 	{0, 0}
 };
+#else
+static pci_dev_t ehci_find_class(void)
+{
+	int bus;
+	int devnum;
+	pci_dev_t bdf;
+	uint32_t class;
+
+	for (bus = 0; bus < pci_last_busno(); bus++) {
+		for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) {
+			pci_read_config_dword(PCI_BDF(bus, devnum, 0),
+					      PCI_CLASS_REVISION, &class);
+			if (class >> 16 == 0xffff)
+				continue;
+
+			for (bdf = PCI_BDF(bus, devnum, 0);
+					bdf <= PCI_BDF(bus, devnum,
+						PCI_MAX_PCI_FUNCTIONS - 1);
+					bdf += PCI_BDF(0, 0, 1)) {
+				pci_read_config_dword(bdf, PCI_CLASS_REVISION,
+						      &class);
+				if (class >> 8 == 0x0c0320)
+					return bdf;
+			}
+		}
+	}
+	return -1;
+}
 #endif
 
 /*
@@ -41,8 +69,13 @@ static struct pci_device_id ehci_pci_ids[] = {
 int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
 {
 	pci_dev_t pdev;
+	uint32_t cmd;
 
+#ifdef CONFIG_PCI_EHCI_DEVICE
 	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
+#else
+	pdev = ehci_find_class();
+#endif
 	if (pdev == -1) {
 		printf("EHCI host controller not found\n");
 		return -1;
@@ -57,6 +90,10 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
 			(uint32_t)*hccr, (uint32_t)*hcor,
 			(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
 
+	/* enable busmaster */
+	pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_MASTER;
+	pci_write_config_dword(pdev, PCI_COMMAND, cmd);
 	return 0;
 }
 
Signature · 1 line
1.7.7.3
#7 lore raw

PATCH 3/11 usb: ehci: generic PCI support

Dear Simon Glass,

Quoted 32 lines

From: Vincent Palatin <[email protected]>

Instead of hardcoding the PCI IDs on the USB controller, use the PCI class to detect them.

Ensure the busmaster bit is properly set in the PCI configuration.

---
 drivers/usb/host/ehci-pci.c |   37 +++++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)
drivers/usb/host/ehci-pci.c +12 -0
index 29af02d..0dc0e6e 100644
@@ -32,6 +32,34 @@ static struct pci_device_id ehci_pci_ids[] = {
 	{0x12D8, 0x400F},	/* Pericom */
 	{0, 0}
 };
+#else
+static pci_dev_t ehci_find_class(void)
+{
+	int bus;
+	int devnum;
+	pci_dev_t bdf;
+	uint32_t class;
+
+	for (bus = 0; bus < pci_last_busno(); bus++) {
+		for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) {
+			pci_read_config_dword(PCI_BDF(bus, devnum, 0),
+					      PCI_CLASS_REVISION, &class);

drivers/usb/host/ehci-pci.c static struct pci_device_id ehci_pci_ids[] = { · line 56

Quoted 10 lines
drivers/usb/host/ehci-pci.c +10 -0
+			if (class >> 16 == 0xffff)
+				continue;
+
+			for (bdf = PCI_BDF(bus, devnum, 0);
+					bdf <= PCI_BDF(bus, devnum,
+						PCI_MAX_PCI_FUNCTIONS - 1);
+					bdf += PCI_BDF(0, 0, 1)) {
+				pci_read_config_dword(bdf, PCI_CLASS_REVISION,
+						      &class);
+				if (class >> 8 == 0x0c0320)

Can we at least describe this magic please?

drivers/usb/host/ehci-pci.c static struct pci_device_id ehci_pci_ids[] = { · line 61

Quoted 5 lines
drivers/usb/host/ehci-pci.c +5 -0
+					return bdf;
+			}
+		}
+	}
+	return -1;

Let's try to use errno.h please. It won't be consistent for a while, but we'll get there.

Quoted 19 lines
drivers/usb/host/ehci-pci.c +6 -0
+}
 #endif
 
 /*
@@ -41,8 +69,13 @@ static struct pci_device_id ehci_pci_ids[] = {
 int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor
 **hcor) {
 	pci_dev_t pdev;
+	uint32_t cmd;
 
+#ifdef CONFIG_PCI_EHCI_DEVICE
 	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
+#else
+	pdev = ehci_find_class();
+#endif
 	if (pdev == -1) {
 		printf("EHCI host controller not found\n");
 		return -1;

drivers/usb/host/ehci-pci.c int ehci_hcd_init(int index, struct ehci_hccr **hccr, · line 98

Quoted 9 lines
drivers/usb/host/ehci-pci.c +4 -0
@@ -57,6 +90,10 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr,
 struct ehci_hcor **hcor) (uint32_t)*hccr, (uint32_t)*hcor,
 			(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
 
+	/* enable busmaster */
+	pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_MASTER;
+	pci_write_config_dword(pdev, PCI_COMMAND, cmd);
 	return 0;
 }

Best regards,
Marek Vasut

#8 lore raw

PATCH 3/11 usb: ehci: generic PCI support

Quoted 38 lines · Marek Vasut

Dear Simon Glass,

Quoted 36 lines

From: Vincent Palatin <[email protected]>

Instead of hardcoding the PCI IDs on the USB controller, use the PCI class to detect them.

Ensure the busmaster bit is properly set in the PCI configuration.

---
 drivers/usb/host/ehci-pci.c |   37 +++++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)
drivers/usb/host/ehci-pci.c +16 -0
index 29af02d..0dc0e6e 100644
@@ -32,6 +32,34 @@ static struct pci_device_id ehci_pci_ids[] = {
      {0x12D8, 0x400F},       /* Pericom */
      {0, 0}
 };
+#else
+static pci_dev_t ehci_find_class(void)
+{
+     int bus;
+     int devnum;
+     pci_dev_t bdf;
+     uint32_t class;
+
+     for (bus = 0; bus < pci_last_busno(); bus++) {
+             for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) {
+                     pci_read_config_dword(PCI_BDF(bus, devnum, 0),
+                                           PCI_CLASS_REVISION, &class);
+                     if (class >> 16 == 0xffff)
+                             continue;
+
+                     for (bdf = PCI_BDF(bus, devnum, 0);
Quoted 8 lines

drivers/usb/host/ehci-pci.c static struct pci_device_id ehci_pci_ids[] = { · line 56

Quoted 6 lines
drivers/usb/host/ehci-pci.c +6 -0
+                                     bdf <= PCI_BDF(bus, devnum,
+                                             PCI_MAX_PCI_FUNCTIONS - 1);
+                                     bdf += PCI_BDF(0, 0, 1)) {
+                             pci_read_config_dword(bdf, PCI_CLASS_REVISION,
+                                                   &class);
+                             if (class >> 8 == 0x0c0320)

Can we at least describe this magic please?

The PCI class word is something like that :
class = [31:24] class code [23:16] subclass [15:8] Prog if [7:0] Revision ID For this use case :
class code = 0x0C (Serial bus controller)
subclass = 0x03 (USB controller)
Program interface = 0x20 (EHCI)

This is available in include/pci_ids.h as "#define
PCI_CLASS_SERIAL_USB_EHCI 0x0c0320"
so just replacing the numeric constant by this define should be ok ?

Quoted 40 lines

drivers/usb/host/ehci-pci.c static struct pci_device_id ehci_pci_ids[] = { · line 61

Quoted 5 lines
drivers/usb/host/ehci-pci.c +5 -0
+                                     return bdf;
+                     }
+             }
+     }
+     return -1;

Let's try to use errno.h please. It won't be consistent for a while, but we'll get there.

Quoted 19 lines
drivers/usb/host/ehci-pci.c +6 -0
+}
 #endif
 
 /*
@@ -41,8 +69,13 @@ static struct pci_device_id ehci_pci_ids[] = {
 int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor
 **hcor) {
      pci_dev_t pdev;
+     uint32_t cmd;
 
+#ifdef CONFIG_PCI_EHCI_DEVICE
      pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
+#else
+     pdev = ehci_find_class();
+#endif
      if (pdev == -1) {
              printf("EHCI host controller not found\n");
              return -1;

drivers/usb/host/ehci-pci.c int ehci_hcd_init(int index, struct ehci_hccr **hccr, · line 98

Quoted 9 lines
drivers/usb/host/ehci-pci.c +4 -0
@@ -57,6 +90,10 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr,
 struct ehci_hcor **hcor) (uint32_t)*hccr, (uint32_t)*hcor,
                      (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
 
+     /* enable busmaster */
+     pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
+     cmd |= PCI_COMMAND_MASTER;
+     pci_write_config_dword(pdev, PCI_COMMAND, cmd);
      return 0;
 }

Best regards,
Marek Vasut

#9 lore raw

PATCH 3/11 usb: ehci: generic PCI support

Dear Vincent Palatin,

Quoted 61 lines · Marek Vasut
Quoted 44 lines

Dear Simon Glass,

Quoted 42 lines

From: Vincent Palatin <[email protected]>

Instead of hardcoding the PCI IDs on the USB controller, use the PCI class to detect them.

Ensure the busmaster bit is properly set in the PCI configuration.

---

 drivers/usb/host/ehci-pci.c |   37

+++++++++++++++++++++++++++++++++++++ 1 files changed, 37 insertions(+), 0 deletions(-)

drivers/usb/host/ehci-pci.c +10 -0
index 29af02d..0dc0e6e 100644
@@ -32,6 +32,34 @@ static struct pci_device_id ehci_pci_ids[] = {
 
      {0x12D8, 0x400F},       /* Pericom */
      {0, 0}
 
 };
 
+#else
+static pci_dev_t ehci_find_class(void)
+{
+     int bus;
+     int devnum;
+     pci_dev_t bdf;
+     uint32_t class;
+
+     for (bus = 0; bus < pci_last_busno(); bus++) {
+             for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++)

{ + pci_read_config_dword(PCI_BDF(bus, devnum, 0),
+ PCI_CLASS_REVISION,
&class); + if (class >> 16 == 0xffff)

drivers/usb/host/ehci-pci.c +4 -0
+                             continue;
+
+                     for (bdf = PCI_BDF(bus, devnum, 0);
+                                     bdf <= PCI_BDF(bus, devnum,
Quoted 8 lines
Quoted 6 lines

+ PCI_MAX_PCI_FUNCTIONS -
1); + bdf += PCI_BDF(0, 0, 1)) { +
pci_read_config_dword(bdf,
PCI_CLASS_REVISION, +
&class);
+ if (class >> 8 == 0x0c0320)

Can we at least describe this magic please?

The PCI class word is something like that :
class = [31:24] class code [23:16] subclass [15:8] Prog if [7:0] Revision ID For this use case :
class code = 0x0C (Serial bus controller)
subclass = 0x03 (USB controller)
Program interface = 0x20 (EHCI)

Quoted 3 lines

This is available in include/pci_ids.h as "#define
PCI_CLASS_SERIAL_USB_EHCI 0x0c0320"
so just replacing the numeric constant by this define should be ok ?

Of course :)

#10 lore raw

PATCH 3/11 usb: ehci: generic PCI support

In reply to Marek Vasut

Hi Marek,

Quoted 38 lines · Marek Vasut

Dear Simon Glass,

Quoted 36 lines

From: Vincent Palatin <[email protected]>

Instead of hardcoding the PCI IDs on the USB controller, use the PCI class to detect them.

Ensure the busmaster bit is properly set in the PCI configuration.

---
 drivers/usb/host/ehci-pci.c |   37 +++++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)
drivers/usb/host/ehci-pci.c +16 -0
index 29af02d..0dc0e6e 100644
@@ -32,6 +32,34 @@ static struct pci_device_id ehci_pci_ids[] = {
      {0x12D8, 0x400F},       /* Pericom */
      {0, 0}
 };
+#else
+static pci_dev_t ehci_find_class(void)
+{
+     int bus;
+     int devnum;
+     pci_dev_t bdf;
+     uint32_t class;
+
+     for (bus = 0; bus < pci_last_busno(); bus++) {
+             for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) {
+                     pci_read_config_dword(PCI_BDF(bus, devnum, 0),
+                                           PCI_CLASS_REVISION, &class);
+                     if (class >> 16 == 0xffff)
+                             continue;
+
+                     for (bdf = PCI_BDF(bus, devnum, 0);
Quoted 8 lines

drivers/usb/host/ehci-pci.c static struct pci_device_id ehci_pci_ids[] = { · line 56

Quoted 6 lines
drivers/usb/host/ehci-pci.c +6 -0
+                                     bdf <= PCI_BDF(bus, devnum,
+                                             PCI_MAX_PCI_FUNCTIONS - 1);
+                                     bdf += PCI_BDF(0, 0, 1)) {
+                             pci_read_config_dword(bdf, PCI_CLASS_REVISION,
+                                                   &class);
+                             if (class >> 8 == 0x0c0320)

Can we at least describe this magic please?

Changed to PCI_CLASS_SERIAL_USB_EHCI.

Quoted 9 lines

drivers/usb/host/ehci-pci.c static struct pci_device_id ehci_pci_ids[] = { · line 61

Quoted 6 lines
drivers/usb/host/ehci-pci.c +5 -0
+                                     return bdf;
+                     }
+             }
+     }
+     return -1;

Let's try to use errno.h please. It won't be consistent for a while, but we'll get there.

Yes, although pci_find_devices() doesn't, so I will just check for < 0 below.

Quoted 23 lines
Quoted 23 lines
drivers/usb/host/ehci-pci.c +6 -0
+}
 #endif
 
 /*
@@ -41,8 +69,13 @@ static struct pci_device_id ehci_pci_ids[] = {
 int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor
 **hcor) {
      pci_dev_t pdev;
+     uint32_t cmd;
 
+#ifdef CONFIG_PCI_EHCI_DEVICE
      pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
+#else
+     pdev = ehci_find_class();
+#endif
      if (pdev == -1) {
              printf("EHCI host controller not found\n");
              return -1;
@@ -57,6 +90,10 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr,
 struct ehci_hcor **hcor) (uint32_t)*hccr, (uint32_t)*hcor,
                      (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
 
Quoted 9 lines

drivers/usb/host/ehci-pci.c int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) · line 98

Quoted 6 lines
drivers/usb/host/ehci-pci.c +4 -0
+     /* enable busmaster */
+     pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
+     cmd |= PCI_COMMAND_MASTER;
+     pci_write_config_dword(pdev, PCI_COMMAND, cmd);
      return 0;
 }

Best regards,
Marek Vasut

Regards,
Simon

#11 lore raw

PATCH v3 2/7 usb: ehci: generic PCI support

In reply to Simon Glass

From: Vincent Palatin <[email protected]>

Instead of hardcoding the PCI IDs on the USB controller, use the PCI class to detect them.

Ensure the busmaster bit is properly set in the PCI configuration.

---
Changes in v3:
- Check the last PCI bus (this was unfortunately omitted in v2)

Changes in v2:
- Use errno where possible
- Use PCI_CLASS_SERIAL_USB_EHCI instead of the number

 drivers/usb/host/ehci-pci.c |   41 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 40 insertions(+), 1 deletions(-)
drivers/usb/host/ehci-pci.c +40 -1
index 29af02d..001d141 100644
@@ -19,6 +19,7 @@
  */
 
 #include <common.h>
+#include <errno.h>
 #include <pci.h>
 #include <usb.h>
 
@@ -32,6 +33,35 @@ static struct pci_device_id ehci_pci_ids[] = {
 	{0x12D8, 0x400F},	/* Pericom */
 	{0, 0}
 };
+#else
+static pci_dev_t ehci_find_class(void)
+{
+	int bus;
+	int devnum;
+	pci_dev_t bdf;
+	uint32_t class;
+
+	for (bus = 0; bus <= pci_last_busno(); bus++) {
+		for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) {
+			pci_read_config_dword(PCI_BDF(bus, devnum, 0),
+					      PCI_CLASS_REVISION, &class);
+			if (class >> 16 == 0xffff)
+				continue;
+
+			for (bdf = PCI_BDF(bus, devnum, 0);
+					bdf <= PCI_BDF(bus, devnum,
+						PCI_MAX_PCI_FUNCTIONS - 1);
+					bdf += PCI_BDF(0, 0, 1)) {
+				pci_read_config_dword(bdf, PCI_CLASS_REVISION,
+						      &class);
+				if (class >> 8 == PCI_CLASS_SERIAL_USB_EHCI)
+					return bdf;
+			}
+		}
+	}
+
+	return -ENODEV;
+}
 #endif
 
 /*
@@ -41,9 +71,14 @@ static struct pci_device_id ehci_pci_ids[] = {
 int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
 {
 	pci_dev_t pdev;
+	uint32_t cmd;
 
+#ifdef CONFIG_PCI_EHCI_DEVICE
 	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
-	if (pdev == -1) {
+#else
+	pdev = ehci_find_class();
+#endif
+	if (pdev < 0) {
 		printf("EHCI host controller not found\n");
 		return -1;
 	}
@@ -57,6 +92,10 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
 			(uint32_t)*hccr, (uint32_t)*hcor,
 			(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
 
+	/* enable busmaster */
+	pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_MASTER;
+	pci_write_config_dword(pdev, PCI_COMMAND, cmd);
 	return 0;
 }
 
Signature · 1 line
1.7.7.3
#12 lore raw

PATCH 4/11 usb: ehci: Support interrupt transfers via periodic list

In reply to Simon Glass

From: Patrick Georgi <[email protected]>

Interrupt transfers aren't meant to be used from the async list (the EHCI spec indicates trouble with low/full-speed intr on async).

Build a periodic list instead, and provide an API to make use of it. Then, use that API from the existing interrupt transfer API.

This provides support for USB keyboards using EHCI.

---
 drivers/usb/host/ehci-hcd.c |  295 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/host/ehci.h     |    6 +-
 2 files changed, 296 insertions(+), 5 deletions(-)
drivers/usb/host/ehci-hcd.c +291 -4
index 20309ad..0f4bc49 100644
@@ -27,6 +27,7 @@
 #include <asm/io.h>
 #include <malloc.h>
 #include <watchdog.h>
+#include <linux/compiler.h>
 
 #include "ehci.h"
 
@@ -39,7 +40,10 @@ static struct ehci_ctrl {
 	struct ehci_hcor *hcor;
 	int rootdev;
 	uint16_t portreset;
-	struct QH qh_list __attribute__((aligned(USB_DMA_MINALIGN)));
+	struct QH qh_list __aligned(USB_DMA_MINALIGN);
+	struct QH periodic_queue __aligned(USB_DMA_MINALIGN);
+	uint32_t *periodic_list;
+	int ntds;
 } ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
 
 #define ALIGN_END_ADDR(type, ptr, size)			\
@@ -858,6 +862,7 @@ int usb_lowlevel_init(int index, void **controller)
 	uint32_t reg;
 	uint32_t cmd;
 	struct QH *qh_list;
+	struct QH *periodic;
 
 	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
 		return -1;
@@ -887,6 +892,38 @@ int usb_lowlevel_init(int index, void **controller)
 	qh_list->qh_overlay.qt_token =
 			cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED));
 
+	/* Set async. queue head pointer. */
+	ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list);
+
+	/*
+	 * Set up periodic list
+	 * Step 1: Parent QH for all periodic transfers.
+	 */
+	periodic = &ehcic[index].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);
+	periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+
+	/*
+	 * Step 2: Setup frame-list: Every microframe, USB tries the same list.
+	 *         In particular, device specifications on polling frequency
+	 *         are disregarded. Keyboards seem to send NAK/NYet reliably
+	 *         when polled with an empty buffer.
+	 *
+	 *         Split Transactions will be spread across microframes using
+	 *         S-mask and C-mask.
+	 */
+	ehcic[index].periodic_list = memalign(4096, 1024*4);
+	int i;
+	for (i = 0; i < 1024; i++)
+		ehcic[index].periodic_list[i] = (uint32_t)periodic
+						| QH_LINK_TYPE_QH;
+
+	/* Set periodic list base address */
+	ehci_writel(&ehcic[index].hcor->or_periodiclistbase,
+		(uint32_t)ehcic[index].periodic_list);
+
 	reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams);
 	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
 	debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
@@ -956,10 +993,243 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 	return ehci_submit_async(dev, pipe, buffer, length, setup);
 }
 
+struct int_queue {
+	struct QH *first;
+	struct QH *current;
+	struct QH *last;
+	struct qTD *tds;
+};
+
+#define NEXT_QH(qh) (struct QH *)((qh)->qh_link & ~0x1f)
+
+static int
+enable_periodic(struct ehci_ctrl *ctrl)
+{
+	uint32_t cmd;
+	struct ehci_hcor *hcor = ctrl->hcor;
+
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	cmd |= CMD_PSE;
+	ehci_writel(&hcor->or_usbcmd, cmd);
+
+	int ret = handshake((uint32_t *)&hcor->or_usbsts,
+			STD_PSS, STD_PSS, 100 * 1000);
+	if (ret < 0) {
+		printf("EHCI failed: timeout when enabling periodic list\n");
+		return -1;
+	}
+	udelay(1000);
+	return 0;
+}
+
+static int
+disable_periodic(struct ehci_ctrl *ctrl)
+{
+	uint32_t cmd;
+	struct ehci_hcor *hcor = ctrl->hcor;
+
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	cmd &= ~CMD_PSE;
+	ehci_writel(&hcor->or_usbcmd, cmd);
+
+	int ret = handshake((uint32_t *)&hcor->or_usbsts,
+			STD_PSS, 0, 100 * 1000);
+	if (ret < 0) {
+		printf("EHCI failed: timeout when enabling periodic list\n");
+		return -1;
+	}
+	return 0;
+}
+
+int periodic_schedules;
+
+struct int_queue *
+create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
+		 int elementsize, void *buffer)
+{
+	int i;
+	struct ehci_ctrl *ctrl = dev->controller;
+	struct int_queue *result = NULL;
+	debug("Enter create_int_queue\n");
+	if (usb_pipetype(pipe) != PIPE_INTERRUPT) {
+		debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe));
+		return NULL;
+	}
+
+	/* limit to 4 full pages worth of data -
+	 * we can safely fit them in a single TD,
+	 * no matter the alignment
+	 */
+	if (elementsize >= 16384) {
+		debug("too large elements for interrupt transfers\n");
+		return NULL;
+	}
+
+	result = malloc(sizeof(*result));
+	if (!result) {
+		debug("ehci intr queue: out of memory\n");
+		goto fail1;
+	}
+	result->first = memalign(32, sizeof(struct QH) * queuesize);
+	if (!result->first) {
+		debug("ehci intr queue: out of memory\n");
+		goto fail2;
+	}
+	result->current = result->first;
+	result->last = result->first + elementsize - 1;
+	result->tds = memalign(32, sizeof(struct qTD) * queuesize);
+	if (!result->tds) {
+		debug("ehci intr queue: out of memory\n");
+		goto fail3;
+	}
+	memset(result->first, 0, sizeof(struct QH) * queuesize);
+	memset(result->tds, 0, sizeof(struct qTD) * queuesize);
+
+	for (i = 0; i < queuesize; i++) {
+		struct QH *qh = result->first + i;
+		struct qTD *td = result->tds + i;
+		void **buf = &qh->buffer;
+
+		qh->qh_link = (uint32_t)(qh+1) | QH_LINK_TYPE_QH;
+		if (i == queuesize - 1)
+			qh->qh_link = QH_LINK_TERMINATE;
+
+		qh->qh_overlay.qt_next = (uint32_t)td;
+		qh->qh_endpt1 = (0 << 28) | /* No NAK reload (ehci 4.9) */
+			(usb_maxpacket(dev, pipe) << 16) | /* MPS */
+			(1 << 14) | /* TODO: Data Toggle Control */
+			QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) |
+			(usb_pipeendpoint(pipe) << 8) | /* Endpoint Number */
+			(usb_pipedevice(pipe) << 0);
+		qh->qh_endpt2 = (1 << 30); /* 1 Tx per mframe */
+		if (dev->speed == USB_SPEED_LOW ||
+				dev->speed == USB_SPEED_FULL) {
+			debug("TT: port: %d, hub address: %d\n",
+				dev->portnr, dev->parent->devnum);
+			qh->qh_endpt2 |= (dev->portnr << 23) |
+				(dev->parent->devnum << 16) |
+				(0x1c << 8) | /* C-mask: microframes 2-4 */
+				(1 << 0); /* S-mask: microframe 0 */
+		}
+
+		td->qt_next = QT_NEXT_TERMINATE;
+		td->qt_altnext = QT_NEXT_TERMINATE;
+		debug("communication direction is '%s'\n",
+		      usb_pipein(pipe) ? "in" : "out");
+		td->qt_token = (elementsize << 16) |
+			((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */
+			0x80; /* active */
+		td->qt_buffer[0] = (uint32_t)buffer + i * elementsize;
+		td->qt_buffer[1] = (td->qt_buffer[0] + 0x1000) & ~0xfff;
+		td->qt_buffer[2] = (td->qt_buffer[0] + 0x2000) & ~0xfff;
+		td->qt_buffer[3] = (td->qt_buffer[0] + 0x3000) & ~0xfff;
+		td->qt_buffer[4] = (td->qt_buffer[0] + 0x4000) & ~0xfff;
+
+		*buf = buffer + i * elementsize;
+	}
+
+	if (disable_periodic(ctrl) < 0) {
+		debug("FATAL: periodic should never fail, but did");
+		goto fail3;
+	}
+
+	/* hook up to periodic list */
+	struct QH *list = &ctrl->periodic_queue;
+	result->last->qh_link = list->qh_link;
+	list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH;
+
+	if (enable_periodic(ctrl) < 0) {
+		debug("FATAL: periodic should never fail, but did");
+		goto fail3;
+	}
+	periodic_schedules++;
+
+	debug("Exit create_int_queue\n");
+	return result;
+fail3:
+	if (result->tds)
+		free(result->tds);
+fail2:
+	if (result->first)
+		free(result->first);
+	if (result)
+		free(result);
+fail1:
+	return NULL;
+}
+
+/* TODO: requeue depleted buffers */
+void *
+poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+	struct QH *cur = queue->current;
+	/* depleted queue */
+	if (cur == NULL) {
+		debug("Exit poll_int_queue with completed queue\n");
+		return NULL;
+	}
+	/* still active */
+	if (cur->qh_overlay.qt_token & 0x80) {
+		debug("Exit poll_int_queue with no completed intr transfer. "
+		      "token is %x\n", cur->qh_overlay.qt_token);
+		return NULL;
+	}
+	/* TODO: Handle failures */
+	if (!(cur->qh_link & QH_LINK_TERMINATE))
+		queue->current++;
+	else
+		queue->current = NULL;
+	debug("Exit poll_int_queue with completed intr transfer. "
+	      "token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token,
+	      &cur->qh_overlay.qt_token, queue->first);
+	return cur->buffer;
+}
+
+/* Do not free buffers associated with QHs, they're owned by someone else */
+int
+destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+	struct ehci_ctrl *ctrl = dev->controller;
+	int result = -1;
+
+	if (disable_periodic(ctrl) < 0) {
+		debug("FATAL: periodic should never fail, but did");
+		goto out;
+	}
+	periodic_schedules--;
+
+	struct QH *cur = &ctrl->periodic_queue;
+	while (!(cur->qh_link & QH_LINK_TERMINATE)) {
+		debug("considering %p, with qh_link %x\n", cur, cur->qh_link);
+		if (NEXT_QH(cur) == queue->first) {
+			debug("found candidate. removing from chain\n");
+			cur->qh_link = queue->last->qh_link;
+			result = 0;
+			goto out;
+		}
+		cur = NEXT_QH(cur);
+	}
+
+	if (periodic_schedules > 0)
+		if (enable_periodic(ctrl) < 0) {
+			debug("FATAL: periodic should never fail, but did");
+			result = -1;
+		}
+
+out:
+	free(queue->tds);
+	free(queue->first);
+	free(queue);
+	return result;
+}
+
 int
 submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 	       int length, int interval)
 {
+	void *backbuffer;
+	struct int_queue *queue;
+
 	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
 	      dev, pipe, buffer, length, interval);
 
@@ -975,9 +1245,26 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 	 * not require more than a single qTD.
 	 */
 	if (length > usb_maxpacket(dev, pipe)) {
-		printf("%s: Interrupt transfers requiring several transactions "
-			"are not supported.\n", __func__);
+		printf("%s: Interrupt transfers requiring several "
+			"transactions are not supported.\n", __func__);
 		return -1;
 	}
-	return ehci_submit_async(dev, pipe, buffer, length, NULL);
+
+	queue = create_int_queue(dev, pipe, 1, length, buffer);
+
+	/* TODO: pick some useful timeout rule */
+	while ((backbuffer = poll_int_queue(dev, queue)) == NULL)
+		;
+
+	if (backbuffer != buffer) {
+		debug("got wrong buffer back (%x instead of %x)\n",
+		      (uint32_t)backbuffer, (uint32_t)buffer);
+		return -1;
+	}
+
+	if (destroy_int_queue(dev, queue) == -1)
+		return -1;
+
+	/* everything worked out fine */
+	return 0;
 }
drivers/usb/host/ehci.h +5 -1
index 1e3cd79..8bc2ba1 100644
@@ -69,6 +69,7 @@ struct ehci_hcor {
 #define CMD_RUN		(1 << 0)		/* start/stop HC */
 	uint32_t or_usbsts;
 #define STS_ASS		(1 << 15)
+#define	STD_PSS		(1 << 14)
 #define STS_HALT	(1 << 12)
 	uint32_t or_usbintr;
 #define INTR_UE         (1 << 0)                /* USB interrupt enable */
@@ -245,7 +246,10 @@ struct QH {
 	 * Add dummy fill value to make the size of this struct
 	 * aligned to 32 bytes
 	 */
-	uint8_t fill[16];
+	union {
+		uint8_t fill[16];
+		void *buffer;
+	};
 };
 
 /* Low level init functions */
Signature · 1 line
1.7.7.3
#13 lore raw

PATCH 4/11 usb: ehci: Support interrupt transfers via periodic list

Dear Simon Glass,

Quoted 5 lines

From: Patrick Georgi <[email protected]>

Interrupt transfers aren't meant to be used from the async list (the EHCI spec indicates trouble with low/full-speed intr on async).

Quoted 7 lines

Build a periodic list instead, and provide an API to make use of it. Then, use that API from the existing interrupt transfer API.

This provides support for USB keyboards using EHCI.

[...]

Looks good in general. Clear up those ToDos please. Also, some while() cycles are endless, make use of some timeout please.

Best regards,
Marek Vasut

#14 lore raw

PATCH 4/11 usb: ehci: Support interrupt transfers via periodic list

Hi,

Quoted 10 lines · Marek Vasut

Dear Simon Glass,

Quoted 8 lines

From: Patrick Georgi <[email protected]>

Interrupt transfers aren't meant to be used from the async list (the EHCI spec indicates trouble with low/full-speed intr on async).

Build a periodic list instead, and provide an API to make use of it. Then, use that API from the existing interrupt transfer API.

Quoted 8 lines
Quoted 4 lines

This provides support for USB keyboards using EHCI.

[...]

Looks good in general. Clear up those ToDos please. Also, some while() cycles are endless, make use of some timeout please.

Not much ability to do anything other than remove them...

The timeouts come in the next patch - should I squash them?

Regards,
Simon

Quoted 3 lines

Best regards,
Marek Vasut

#15 lore raw

PATCH 4/11 usb: ehci: Support interrupt transfers via periodic list

Dear Simon Glass,

Quoted 18 lines

Hi,

Quoted 15 lines · Marek Vasut

Dear Simon Glass,

Quoted 12 lines

From: Patrick Georgi <[email protected]>

Interrupt transfers aren't meant to be used from the async list (the EHCI spec indicates trouble with low/full-speed intr on async).

Build a periodic list instead, and provide an API to make use of it. Then, use that API from the existing interrupt transfer API.

This provides support for USB keyboards using EHCI.

Quoted 8 lines
Quoted 4 lines

[...]

Looks good in general. Clear up those ToDos please. Also, some while() cycles are endless, make use of some timeout please.

Not much ability to do anything other than remove them...

The timeouts come in the next patch - should I squash them?

Yeah, squash them. Was there any reason to split them ?

Best regards,
Marek Vasut

#16 lore raw

PATCH 4/11 usb: ehci: Support interrupt transfers via periodic list

Hi Marek,

Quoted 22 lines · Marek Vasut

Dear Simon Glass,

Quoted 20 lines

Hi,

Quoted 17 lines · Marek Vasut

Dear Simon Glass,

Quoted 12 lines

From: Patrick Georgi <[email protected]>

Interrupt transfers aren't meant to be used from the async list (the EHCI spec indicates trouble with low/full-speed intr on async).

Build a periodic list instead, and provide an API to make use of it. Then, use that API from the existing interrupt transfer API.

This provides support for USB keyboards using EHCI.

[...]

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Looks good in general. Clear up those ToDos please. Also, some while() cycles are endless, make use of some timeout please.

Not much ability to do anything other than remove them...

The timeouts come in the next patch - should I squash them?

Yeah, squash them. Was there any reason to split them ?

Not really - just a bug fix that we did. Will squash.

Regards,
Simon

Quoted 3 lines

Best regards,
Marek Vasut

#17 lore raw

PATCH 5/11 usb: ehci: Fix aliasing issue in EHCI interrupt code

In reply to Simon Glass

From: Vincent Palatin <[email protected]>

The interrupt endpoint handling code stores the buffer pointer in the QH padding field. We need to make it the size of a pointer to avoid strict aliasing issue with the compiler.

---
 drivers/usb/host/ehci.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/usb/host/ehci.h +1 -1
index 8bc2ba1..5370eb0 100644
@@ -247,7 +247,7 @@ struct QH {
 	 * aligned to 32 bytes
 	 */
 	union {
-		uint8_t fill[16];
+		uint32_t fill[4];
 		void *buffer;
 	};
 };
Signature · 1 line
1.7.7.3
#18 lore raw

PATCH 6/11 usb: ehci: Add timeout on interrupt endpoint operations

In reply to Simon Glass

From: Vincent Palatin <[email protected]>

Ensure we cannot get stuck in the keyboard scanning if something wrong happens (USB device unplugged or fatal I/O error)

---
 drivers/usb/host/ehci-hcd.c |   18 ++++++++++++++++--
 1 files changed, 16 insertions(+), 2 deletions(-)
drivers/usb/host/ehci-hcd.c +16 -2
index 0f4bc49..51aa4b3 100644
@@ -1191,6 +1191,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
 {
 	struct ehci_ctrl *ctrl = dev->controller;
 	int result = -1;
+	unsigned long timeout;
 
 	if (disable_periodic(ctrl) < 0) {
 		debug("FATAL: periodic should never fail, but did");
@@ -1199,6 +1200,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
 	periodic_schedules--;
 
 	struct QH *cur = &ctrl->periodic_queue;
+	timeout = get_timer(0) + 500; /* abort after 500ms */
 	while (!(cur->qh_link & QH_LINK_TERMINATE)) {
 		debug("considering %p, with qh_link %x\n", cur, cur->qh_link);
 		if (NEXT_QH(cur) == queue->first) {
@@ -1208,6 +1210,11 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
 			goto out;
 		}
 		cur = NEXT_QH(cur);
+		if (get_timer(0) > timeout) {
+			printf("Timeout destroying interrupt endpoint queue\n");
+			result = -1;
+			break;
+		}
 	}
 
 	if (periodic_schedules > 0)
@@ -1229,6 +1236,8 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 {
 	void *backbuffer;
 	struct int_queue *queue;
+	unsigned long timeout;
+	int result = 0;
 
 	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
 	      dev, pipe, buffer, length, interval);
@@ -1253,8 +1262,13 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 	queue = create_int_queue(dev, pipe, 1, length, buffer);
 
 	/* TODO: pick some useful timeout rule */
+	timeout = get_timer(0) + USB_TIMEOUT_MS(pipe);
 	while ((backbuffer = poll_int_queue(dev, queue)) == NULL)
-		;
+		if (get_timer(0) > timeout) {
+			printf("Timeout poll on interrupt endpoint\n");
+			result = -1;
+			break;
+		}
 
 	if (backbuffer != buffer) {
 		debug("got wrong buffer back (%x instead of %x)\n",
@@ -1266,5 +1280,5 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 		return -1;
 
 	/* everything worked out fine */
-	return 0;
+	return result;
 }
Signature · 1 line
1.7.7.3
#19 lore raw

PATCH 6/11 usb: ehci: Add timeout on interrupt endpoint operations

Dear Simon Glass,

Quoted 9 lines

From: Vincent Palatin <[email protected]>

Ensure we cannot get stuck in the keyboard scanning if something wrong happens (USB device unplugged or fatal I/O error)

---

I like it. Let's use errno.h and rebase on top of u-boot-usb.git ... I just pushed it.

Best regards,
Marek Vasut

#20 lore raw

PATCH 6/11 usb: ehci: Add timeout on interrupt endpoint operations

Hi Marek,

Quoted 7 lines · Marek Vasut

Dear Simon Glass,

Quoted 5 lines

From: Vincent Palatin <[email protected]>

Ensure we cannot get stuck in the keyboard scanning if something wrong happens (USB device unplugged or fatal I/O error)

Quoted 7 lines
Quoted 4 lines

---

I like it. Let's use errno.h and rebase on top of u-boot-usb.git ... I just pushed it.

OK I have had a crack at that and will resend.

Regards,
Simon

Quoted 3 lines

Best regards,
Marek Vasut

#21 lore raw

PATCH 7/11 usb: usbeth: smsc95xx: remove EEPROM loaded check

In reply to Simon Glass

From: Michael Spang <[email protected]>

[port of Linux kernel commit bcd218be5aeb by Steve Glendinning]

The eeprom read & write commands currently check the E2P_CMD_LOADED_ bit is set before allowing any operations. This prevents any reading or writing unless a correctly programmed EEPROM is installed.

---
 drivers/usb/eth/smsc95xx.c |    4 ----
 1 files changed, 0 insertions(+), 4 deletions(-)
drivers/usb/eth/smsc95xx.c +0 -4
index dc5ca65..fd8f8a7 100644
@@ -265,10 +265,6 @@ static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)
 
 	do {
 		smsc95xx_read_reg(dev, E2P_CMD, &val);
-		if (!(val & E2P_CMD_LOADED_)) {
-			debug("No EEPROM present\n");
-			return -1;
-		}
 		if (!(val & E2P_CMD_BUSY_))
 			return 0;
 		udelay(40);
Signature · 1 line
1.7.7.3
#22 lore raw

PATCH 7/11 usb: usbeth: smsc95xx: remove EEPROM loaded check

Dear Simon Glass,

Quoted 10 lines

From: Michael Spang <[email protected]>

[port of Linux kernel commit bcd218be5aeb by Steve Glendinning]

The eeprom read & write commands currently check the E2P_CMD_LOADED_ bit is set before allowing any operations. This prevents any reading or writing unless a correctly programmed EEPROM is installed.

Quoted 19 lines
---
 drivers/usb/eth/smsc95xx.c |    4 ----
 1 files changed, 0 insertions(+), 4 deletions(-)
drivers/usb/eth/smsc95xx.c +0 -4
index dc5ca65..fd8f8a7 100644
@@ -265,10 +265,6 @@ static int smsc95xx_eeprom_confirm_not_busy(struct
 ueth_data *dev)
 
 	do {
 		smsc95xx_read_reg(dev, E2P_CMD, &val);
-		if (!(val & E2P_CMD_LOADED_)) {
-			debug("No EEPROM present\n");
-			return -1;
-		}
 		if (!(val & E2P_CMD_BUSY_))
 			return 0;
Quoted 1 line

udelay(40);

Best regards,
Marek Vasut

#23 lore raw

PATCH 8/11 usb: Clean up newly allocated device nodes in case of configuration failure

In reply to Simon Glass

From: Milind Choudhary <[email protected]>

If probe of a newly connected device fails for some reason, clean up the allocated entry in usb_dev array.

---
 common/usb.c     |   12 ++++++++++++
 common/usb_hub.c |    2 ++
 include/usb.h    |    1 +
 3 files changed, 15 insertions(+), 0 deletions(-)
common/usb.c +12 -0
index ac9b4ca..6fc0fc1 100644
@@ -805,6 +805,18 @@ struct usb_device *usb_alloc_new_device(void *controller)
 	return &usb_dev[dev_index - 1];
 }
 
+/*
+ * Free the newly created device node.
+ * Called in error cases where configuring a newly attached
+ * device fails for some reason.
+ */
+void usb_free_device(void)
+{
+	dev_index--;
+	USB_PRINTF("Freeing device node: %d\n", dev_index);
+	memset(&usb_dev[dev_index], 0, sizeof(struct usb_device));
+	usb_dev[dev_index].devnum = -1;
+}
 
 /*
  * By the time we get here, the device has gotten a new device ID
common/usb_hub.c +2 -0
index e4a1201..a3c5f22 100644
@@ -259,6 +259,8 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
 	/* Run it through the hoops (find a driver, etc) */
 	if (usb_new_device(usb)) {
 		/* Woops, disable the port */
+		usb_free_device();
+		dev->children[port] = NULL;
 		USB_HUB_PRINTF("hub: disabling port %d\n", port + 1);
 		usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE);
 	}
include/usb.h +1 -0
index 8d8a2c9..d79c865 100644
@@ -392,5 +392,6 @@ int hub_port_reset(struct usb_device *dev, int port,
 struct usb_device *usb_alloc_new_device(void *controller);
 
 int usb_new_device(struct usb_device *dev);
+void usb_free_device(void);
 
 #endif /*_USB_H_ */
Signature · 1 line
1.7.7.3
Applied
#24 lore raw

PATCH 8/11 usb: Clean up newly allocated device nodes in case of configuration failure

Dear Simon Glass,

Quoted 4 lines

From: Milind Choudhary <[email protected]>

If probe of a newly connected device fails for some reason, clean up the allocated entry in usb_dev array.

Applied, thanks

Quoted 49 lines
---
 common/usb.c     |   12 ++++++++++++
 common/usb_hub.c |    2 ++
 include/usb.h    |    1 +
 3 files changed, 15 insertions(+), 0 deletions(-)
common/usb.c +12 -0
index ac9b4ca..6fc0fc1 100644
@@ -805,6 +805,18 @@ struct usb_device *usb_alloc_new_device(void
 *controller) return &usb_dev[dev_index - 1];
 }
 
+/*
+ * Free the newly created device node.
+ * Called in error cases where configuring a newly attached
+ * device fails for some reason.
+ */
+void usb_free_device(void)
+{
+	dev_index--;
+	USB_PRINTF("Freeing device node: %d\n", dev_index);
+	memset(&usb_dev[dev_index], 0, sizeof(struct usb_device));
+	usb_dev[dev_index].devnum = -1;
+}
 
 /*
  * By the time we get here, the device has gotten a new device ID
common/usb_hub.c +2 -0
index e4a1201..a3c5f22 100644
@@ -259,6 +259,8 @@ void usb_hub_port_connect_change(struct usb_device
 *dev, int port) /* Run it through the hoops (find a driver, etc) */
 	if (usb_new_device(usb)) {
 		/* Woops, disable the port */
+		usb_free_device();
+		dev->children[port] = NULL;
 		USB_HUB_PRINTF("hub: disabling port %d\n", port + 1);
 		usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE);
 	}
include/usb.h +0 -0
index 8d8a2c9..d79c865 100644

include/usb.h int hub_port_reset(struct usb_device *dev, int port, · line 397

Quoted 6 lines
include/usb.h +1 -0
@@ -392,5 +392,6 @@ int hub_port_reset(struct usb_device *dev, int port,
 struct usb_device *usb_alloc_new_device(void *controller);
 
 int usb_new_device(struct usb_device *dev);
+void usb_free_device(void);
 
 #endif /*_USB_H_ */

Best regards,
Marek Vasut

#25 lore raw

PATCH 9/11 usb: properly detect empty mass storage media reader

In reply to Simon Glass

From: Vincent Palatin <[email protected]>

When a USB card reader is empty, it will return "Not Ready - medium not present" as Key Code Qualifier. In that situation, it's useless waiting for the full timeout since the result won't change until the user inserts a card.

---
 common/usb_storage.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)
common/usb_storage.c +10 -0
index 2d92ee1..fb322b4 100644
@@ -970,6 +970,16 @@ static int usb_test_unit_ready(ccb *srb, struct us_data *ss)
 			return 0;
 		}
 		usb_request_sense(srb, ss);
+		/*
+		 * Check the Key Code Qualifier, if it matches
+		 * "Not Ready - medium not present"
+		 * (the sense Key equals 0x2 and the ASC is 0x3a)
+		 * return immediately as the medium being absent won't change
+		 * unless there is a user action.
+		 */
+		if ((srb->sense_buf[2] == 0x02) &&
+		    (srb->sense_buf[12] == 0x3a))
+			return -1;
 		mdelay(100);
 	} while (retries--);
 
Signature · 1 line
1.7.7.3
Applied
#26 lore raw

PATCH 9/11 usb: properly detect empty mass storage media reader

Dear Simon Glass,

Quoted 6 lines

From: Vincent Palatin <[email protected]>

When a USB card reader is empty, it will return "Not Ready - medium not present" as Key Code Qualifier. In that situation, it's useless waiting for the full timeout since the result won't change until the user inserts a card.

Applied, thanks!

Quoted 17 lines
---
 common/usb_storage.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)
common/usb_storage.c +2 -0
index 2d92ee1..fb322b4 100644
@@ -970,6 +970,16 @@ static int usb_test_unit_ready(ccb *srb, struct
 us_data *ss) return 0;
 		}
 		usb_request_sense(srb, ss);
+		/*
+		 * Check the Key Code Qualifier, if it matches

common/usb_storage.c static int usb_test_unit_ready(ccb *srb, struct · line 984

Quoted 10 lines
common/usb_storage.c +8 -0
+		 * "Not Ready - medium not present"
+		 * (the sense Key equals 0x2 and the ASC is 0x3a)
+		 * return immediately as the medium being absent won't change
+		 * unless there is a user action.
+		 */
+		if ((srb->sense_buf[2] == 0x02) &&
+		    (srb->sense_buf[12] == 0x3a))
+			return -1;
 		mdelay(100);
 	} while (retries--);

Best regards,
Marek Vasut

#27 lore raw

PATCH 10/11 usb: Add multiple controllers support for EHCI PCI

In reply to Simon Glass

From: Vincent Palatin <[email protected]>

Use the ability to have several active EHCI controller on a system in the PCI EHCI controller implementation.

---
 drivers/usb/host/ehci-pci.c |   24 +++++++++++++++---------
 1 files changed, 15 insertions(+), 9 deletions(-)
drivers/usb/host/ehci-pci.c +15 -9
index 0dc0e6e..2a3cb31 100644
@@ -33,7 +33,7 @@ static struct pci_device_id ehci_pci_ids[] = {
 	{0, 0}
 };
 #else
-static pci_dev_t ehci_find_class(void)
+static pci_dev_t ehci_find_class(int index)
 {
 	int bus;
 	int devnum;
@@ -53,7 +53,7 @@ static pci_dev_t ehci_find_class(void)
 					bdf += PCI_BDF(0, 0, 1)) {
 				pci_read_config_dword(bdf, PCI_CLASS_REVISION,
 						      &class);
-				if (class >> 8 == 0x0c0320)
+				if ((class >> 8 == 0x0c0320) && !index--)
 					return bdf;
 			}
 		}
@@ -66,34 +66,40 @@ static pci_dev_t ehci_find_class(void)
  * Create the appropriate control structures to manage
  * a new EHCI host controller.
  */
-int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
+int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr,
+		struct ehci_hcor **ret_hcor)
 {
 	pci_dev_t pdev;
 	uint32_t cmd;
+	struct ehci_hccr *hccr;
+	struct ehci_hcor *hcor;
 
 #ifdef CONFIG_PCI_EHCI_DEVICE
 	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
 #else
-	pdev = ehci_find_class();
+	pdev = ehci_find_class(index);
 #endif
 	if (pdev == -1) {
 		printf("EHCI host controller not found\n");
 		return -1;
 	}
 
-	*hccr = (struct ehci_hccr *)pci_map_bar(pdev,
+	hccr = (struct ehci_hccr *)pci_map_bar(pdev,
 			PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
-	*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
-			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
+	hcor = (struct ehci_hcor *)((uint32_t) hccr +
+			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
 
 	debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n",
-			(uint32_t)*hccr, (uint32_t)*hcor,
-			(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
+			(uint32_t)hccr, (uint32_t)hcor,
+			(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
 
 	/* enable busmaster */
 	pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
 	cmd |= PCI_COMMAND_MASTER;
 	pci_write_config_dword(pdev, PCI_COMMAND, cmd);
+
+	*ret_hccr = hccr;
+	*ret_hcor = hcor;
 	return 0;
 }
 
Signature · 1 line
1.7.7.3
#28 lore raw

PATCH 11/11 x86: Enable USB features for coreboot

In reply to Simon Glass

Enable PCI EHCI, storage, keyboard and Ethernet for USB.

---
 include/configs/coreboot.h |   17 +++++++++++++++++
 1 files changed, 17 insertions(+), 0 deletions(-)
include/configs/coreboot.h +17 -0
index adeace0..79adbc0 100644
@@ -267,6 +267,23 @@
  */
 #define CONFIG_PCI
 
+/*-----------------------------------------------------------------------
+ * USB configuration
+ */
+#define CONFIG_USB_EHCI
+#define CONFIG_USB_EHCI_PCI
+#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS     12
+#define CONFIG_USB_MAX_CONTROLLER_COUNT        2
+#define CONFIG_USB_STORAGE
+#define CONFIG_USB_KEYBOARD
+#define CONFIG_SYS_USB_EVENT_POLL
+
+#define CONFIG_USB_HOST_ETHER
+#define CONFIG_USB_ETHER_ASIX
+#define CONFIG_USB_ETHER_SMSC95XX
+
+#define CONFIG_CMD_USB
+
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	CONFIG_STD_DEVICES_SETTINGS
 
Signature · 1 line
1.7.7.3