Hi, On Thu, Nov 22, 2012 at 05:00:45PM +0200, Roger Quadros wrote: > On 11/22/2012 03:56 PM, Felipe Balbi wrote: > > Hi, > > > > On Thu, Nov 22, 2012 at 09:49:05PM +0800, Andy Green wrote: > >>> Again it sounds like something that should be done at the hub driver > >>> level. I mean using the GPIO (for reset) and Power Regulator. > >>> > >>> not implementing the regulator part itself, but using it. > >> > >> If you mean change the regulator managing code from living in > >> omap-hsusb to ehci-hcd, it sounds like a good idea. > > > > I mean that drivers/usb/core/hub.c should manage it, since that's what > > actually manages the HUB entity. > > Yes. I agree that powering up the on-board hubs should be done > generically and not in ehci-omap.c. I'm not sure how it can be done in > hub.c. > > I'm assuming that such problem is limited to root hub ports where system an external LAN95xx HUB is not the roothub. LAN95xx is connected to the roothub. > designers have the flexibility to provide power to the peripherals > outside the USB Hub specification. > > I can think of two solutions > > 1) Associate the regulators with the root hub _ports_ and enable them as > part of port's power-up routine. doesn't make sense. We need to figure out a way to attach the regulator to the ports which actually have them. In this case it's the external LAN95xx, right ? > 2) Have a global list of regulators that are registered by platform code > and enabled them all at once on hcd init. also wrong as it might cause increased power consumption even when only a single USB port is currently in use. The patch below is *CLEARLY WRONG* it's just to illustrate how this could be started: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1af04bd..662d4cf 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -26,6 +26,7 @@ #include <linux/mutex.h> #include <linux/freezer.h> #include <linux/random.h> +#include <linux/regulator/consumer.h> #include <asm/uaccess.h> #include <asm/byteorder.h> @@ -44,6 +45,7 @@ struct usb_port { struct device dev; struct dev_state *port_owner; enum usb_port_connect_type connect_type; + struct regulator *port_regulator; }; struct usb_hub { @@ -847,8 +849,41 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) else dev_dbg(hub->intfdev, "trying to enable port power on " "non-switchable hub\n"); - for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) + for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) { + regulator_enable(hub->ports[port1]->port_regulator); set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); + } + + /* Wait at least 100 msec for power to become stable */ + delay = max(pgood_delay, (unsigned) 100); + if (do_delay) + msleep(delay); + return delay; +} + +static unsigned hub_power_off(struct usb_hub *hub, bool do_delay) +{ + int port1; + unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; + unsigned delay; + u16 wHubCharacteristics = + le16_to_cpu(hub->descriptor->wHubCharacteristics); + + /* Disable power on each port. Some hubs have reserved values + * of LPSM (> 2) in their descriptors, even though they are + * USB 2.0 hubs. Some hubs do not implement port-power switching + * but only emulate it. In all cases, the ports won't work + * unless we send these messages to the hub. + */ + if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) + dev_dbg(hub->intfdev, "disabling power on all ports\n"); + else + dev_dbg(hub->intfdev, "trying to disable port power on " + "non-switchable hub\n"); + for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) { + regulator_disable(hub->ports[port1]->port_regulator); + clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); + } /* Wait at least 100 msec for power to become stable */ delay = max(pgood_delay, (unsigned) 100); @@ -1336,6 +1371,9 @@ static int hub_configure(struct usb_hub *hub, goto fail; } + if (hub->has_external_port_regulator) /* maybe implement with a quirk flag ??? */ + regulator_get(hub_dev, "hub_port_supply\n"); + wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); /* FIXME for USB 3.0, skip for now */ @@ -4205,8 +4243,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 - && !port_is_power_on(hub, portstatus)) + && !port_is_power_on(hub, portstatus)) { + regulator_enable(hub->ports[port1]->port_regulator); set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); + } if (portstatus & USB_PORT_STAT_ENABLE) goto done; Note that if we have a single regulator, than all ports' regulators should point to the same struct regulator so that regulator_get() and regulator_put() can do proper reference counting. This is just an idea though. -- balbi
Attachment:
signature.asc
Description: Digital signature