[PATCH v2] usb: host: ehci-platform: add support for optional external vbus supply

Alan Stern stern at rowland.harvard.edu
Tue Feb 20 10:10:30 PST 2018


On Tue, 20 Feb 2018, Roger Quadros wrote:

> On 20/02/18 16:46, Amelie DELAUNAY wrote:
> > Hi,
> > 
> > On 02/20/2018 03:00 PM, Roger Quadros wrote:
> >> Hi,
> >>
> >> On 20/02/18 14:58, Amelie Delaunay wrote:
> >>> On some boards, especially when vbus supply requires large current,
> >>> and the charge pump on the PHY isn't enough, an external vbus power switch
> >>> may be used.
> >>> Add support for this optional external vbus supply in ehci-platform.
> >>>
> >>> Signed-off-by: Amelie Delaunay <amelie.delaunay at st.com>
> >>>
> >>> ---
> >>> Changes in v2:
> >>>   * Address Roger Quadros comments: move regulator_enable/disable from
> >>> ehci_platform_power_on/off to ehci_platform_port_power.
> >>> ---
> >>>   Documentation/devicetree/bindings/usb/usb-ehci.txt |  1 +
> >>>   drivers/usb/host/ehci-platform.c                   | 31 ++++++++++++++++++++++
> >>>   2 files changed, 32 insertions(+)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt
> >>> index 3efde12..fc480cd 100644
> >>> --- a/Documentation/devicetree/bindings/usb/usb-ehci.txt
> >>> +++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt
> >>> @@ -19,6 +19,7 @@ Optional properties:
> >>>    - phys : phandle + phy specifier pair
> >>>    - phy-names : "usb"
> >>>    - resets : phandle + reset specifier pair
> >>> + - vbus-supply : phandle of regulator supplying vbus
> >>>   
> >>
> >> Can platforms have more than one regulator e.g. one regulator per port?
> >>
> > 
> > I imagine that yes, platforms could have one regulator per port.
> > Regulator consumers bindings impose a <name>-supply property per 
> > regulator, so, what do you think about :
> > vbus0-supply for port#0
> > vbus1-supply for port#1
> > ...
> > vbusN-supply for port#N
> > 
> > And then in probe, allocate 'struct regulator *vbus_supplies' with a 
> > size corresponding to 'HCS_N_PORTS(ehci->hcs_params) * sizeof(struct 
> > regulator *)'.
> > And loop to get optional regulator vbus0, vbus1,..., vbusN.
> > And then enable/disable the corresponding regulator in 
> > ehci_platform_port_power thanks to portnum.
> 
> Looks fine to me but we need to get Alan's opinion if this is worth the effort.
> If there isn't a single platform needing it we could probably do without it
> but the DT binding must be scalable to add this feature in the future.

I agree that for now there don't seem to be any platforms requiring 
more than one regulator, but this should be implemented in a way that 
could be expanded if necessary.

Anyway, the basic idea is reasonable.  I don't know to what extent 
people want to power-off their EHCI ports, but if they do then we ought 
to turn off external regulators at the same time.

Is there a real-life use case for this?

Alan Stern

> And what if it is ganged power? i.e. one regulator for more than one port.
> Probably they all can point to the same regulator instance and it should work.
> 
> > 
> >>>   Example (Sequoia 440EPx):
> >>>       ehci at e0000300 {
> >>> diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
> >>> index b065a96..05be100 100644
> >>> --- a/drivers/usb/host/ehci-platform.c
> >>> +++ b/drivers/usb/host/ehci-platform.c
> >>> @@ -29,6 +29,7 @@
> >>>   #include <linux/of.h>
> >>>   #include <linux/phy/phy.h>
> >>>   #include <linux/platform_device.h>
> >>> +#include <linux/regulator/consumer.h>
> >>>   #include <linux/reset.h>
> >>>   #include <linux/usb.h>
> >>>   #include <linux/usb/hcd.h>
> >>> @@ -46,6 +47,7 @@ struct ehci_platform_priv {
> >>>   	struct reset_control *rsts;
> >>>   	struct phy **phys;
> >>>   	int num_phys;
> >>> +	struct regulator *vbus_supply;
> >>>   	bool reset_on_resume;
> >>>   };
> >>>   
> >>> @@ -76,6 +78,25 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
> >>>   	return 0;
> >>>   }
> >>>   
> >>> +static int ehci_platform_port_power(struct usb_hcd *hcd, int portnum,
> >>> +				    bool enable)
> >>> +{
> >>> +	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
> >>> +	int ret = 0;
> >>> +
> >>> +	if (priv->vbus_supply) {
> >>> +		if (enable)
> >>> +			ret = regulator_enable(priv->vbus_supply);
> >>> +		else
> >>> +			ret = regulator_disable(priv->vbus_supply);
> >>> +		if (ret)
> >>> +			dev_err(hcd->self.controller,
> >>> +				"failed to %s vbus supply: %d\n",
> >>> +				enable ? "enable" : "disable", ret);
> >>> +	}
> >>> +	return ret;
> >>> +}
> >>> +
> >>>   static int ehci_platform_power_on(struct platform_device *dev)
> >>>   {
> >>>   	struct usb_hcd *hcd = platform_get_drvdata(dev);
> >>> @@ -134,6 +155,7 @@ static struct hc_driver __read_mostly ehci_platform_hc_driver;
> >>>   static const struct ehci_driver_overrides platform_overrides __initconst = {
> >>>   	.reset =		ehci_platform_reset,
> >>>   	.extra_priv_size =	sizeof(struct ehci_platform_priv),
> >>> +	.port_power =		ehci_platform_port_power,
> >>>   };
> >>>   
> >>>   static struct usb_ehci_pdata ehci_platform_defaults = {
> >>> @@ -247,6 +269,15 @@ static int ehci_platform_probe(struct platform_device *dev)
> >>>   	if (err)
> >>>   		goto err_put_clks;
> >>>   
> >>> +	priv->vbus_supply = devm_regulator_get_optional(&dev->dev, "vbus");
> >>> +	if (IS_ERR(priv->vbus_supply)) {
> >>> +		err = PTR_ERR(priv->vbus_supply);
> >>> +		if (err == -ENODEV)
> >>> +			priv->vbus_supply = NULL;
> >>> +		else
> >>> +			goto err_reset;
> >>> +	}
> >>> +
> >>>   	if (pdata->big_endian_desc)
> >>>   		ehci->big_endian_desc = 1;
> >>>   	if (pdata->big_endian_mmio)
> >>>
> 
> 






More information about the linux-arm-kernel mailing list