mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 21:44:27 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			206 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From f1c0db40a3ade1f1a39e5794d728f2953d817322 Mon Sep 17 00:00:00 2001
 | |
| From: Al Cooper <alcooperx@gmail.com>
 | |
| Date: Fri, 3 Jan 2020 13:18:02 -0500
 | |
| Subject: [PATCH] phy: usb: Add "wake on" functionality
 | |
| 
 | |
| Add the ability to handle USB wake events from USB devices when
 | |
| in S2 mode. Typically there is some additional configuration
 | |
| needed to tell the USB device to generate the wake event when
 | |
| suspended but this varies with the different USB device classes.
 | |
| For example, on USB Ethernet dongles, ethtool should be used to
 | |
| enable the magic packet wake functionality in the dongle.
 | |
| NOTE:  This requires that the "power/wakeup" sysfs entry for
 | |
| the USB device generating the wakeup be set to "enabled".
 | |
| 
 | |
| This functionality requires a special hardware sideband path that
 | |
| will trigger the AON_PM_L2 interrupt needed to wake the system from
 | |
| S2 even though the USB host controllers are in IDDQ (low power state)
 | |
| and most USB related clocks are shut off. For the sideband signaling
 | |
| to work we need to leave the usbx_freerun clock running, but this
 | |
| clock consumes very little power by design. There's a bug in the
 | |
| XHCI wake hardware so only EHCI/OHCI wake is currently supported.
 | |
| 
 | |
| Signed-off-by: Al Cooper <alcooperx@gmail.com>
 | |
| Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
 | |
| Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
 | |
| ---
 | |
|  drivers/phy/broadcom/phy-brcm-usb-init.c | 17 +++++++++
 | |
|  drivers/phy/broadcom/phy-brcm-usb-init.h |  1 +
 | |
|  drivers/phy/broadcom/phy-brcm-usb.c      | 48 ++++++++++++++++++++++--
 | |
|  3 files changed, 63 insertions(+), 3 deletions(-)
 | |
| 
 | |
| --- a/drivers/phy/broadcom/phy-brcm-usb-init.c
 | |
| +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c
 | |
| @@ -58,6 +58,8 @@
 | |
|  #define   USB_CTRL_USB_PM_SOFT_RESET_MASK		0x40000000 /* option */
 | |
|  #define   USB_CTRL_USB_PM_USB20_HC_RESETB_MASK		0x30000000 /* option */
 | |
|  #define   USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK	0x00300000 /* option */
 | |
| +#define   USB_CTRL_USB_PM_RMTWKUP_EN_MASK		0x00000001
 | |
| +#define USB_CTRL_USB_PM_STATUS		0x38
 | |
|  #define USB_CTRL_USB30_CTL1		0x60
 | |
|  #define   USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK	0x00000010
 | |
|  #define   USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK		0x00010000
 | |
| @@ -855,6 +857,10 @@ void brcm_usb_init_common(struct brcm_us
 | |
|  	u32 reg;
 | |
|  	void __iomem *ctrl = params->ctrl_regs;
 | |
|  
 | |
| +	/* Clear any pending wake conditions */
 | |
| +	reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS));
 | |
| +	brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS));
 | |
| +
 | |
|  	/* Take USB out of power down */
 | |
|  	if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) {
 | |
|  		USB_CTRL_UNSET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN);
 | |
| @@ -1010,6 +1016,17 @@ void brcm_usb_uninit_xhci(struct brcm_us
 | |
|  	USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
 | |
|  }
 | |
|  
 | |
| +void brcm_usb_wake_enable(struct brcm_usb_init_params *params,
 | |
| +			  int enable)
 | |
| +{
 | |
| +	void __iomem *ctrl = params->ctrl_regs;
 | |
| +
 | |
| +	if (enable)
 | |
| +		USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN);
 | |
| +	else
 | |
| +		USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN);
 | |
| +}
 | |
| +
 | |
|  void brcm_usb_set_family_map(struct brcm_usb_init_params *params)
 | |
|  {
 | |
|  	int fam;
 | |
| --- a/drivers/phy/broadcom/phy-brcm-usb-init.h
 | |
| +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h
 | |
| @@ -38,5 +38,6 @@ void brcm_usb_init_xhci(struct brcm_usb_
 | |
|  void brcm_usb_uninit_common(struct brcm_usb_init_params *ini);
 | |
|  void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini);
 | |
|  void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini);
 | |
| +void brcm_usb_wake_enable(struct brcm_usb_init_params *params, int enable);
 | |
|  
 | |
|  #endif /* _USB_BRCM_COMMON_INIT_H */
 | |
| --- a/drivers/phy/broadcom/phy-brcm-usb.c
 | |
| +++ b/drivers/phy/broadcom/phy-brcm-usb.c
 | |
| @@ -57,11 +57,22 @@ struct brcm_usb_phy_data {
 | |
|  	bool			has_xhci;
 | |
|  	struct clk		*usb_20_clk;
 | |
|  	struct clk		*usb_30_clk;
 | |
| +	struct clk		*suspend_clk;
 | |
|  	struct mutex		mutex;	/* serialize phy init */
 | |
|  	int			init_count;
 | |
| +	int			wake_irq;
 | |
|  	struct brcm_usb_phy	phys[BRCM_USB_PHY_ID_MAX];
 | |
|  };
 | |
|  
 | |
| +static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id)
 | |
| +{
 | |
| +	struct phy *gphy = dev_id;
 | |
| +
 | |
| +	pm_wakeup_event(&gphy->dev, 0);
 | |
| +
 | |
| +	return IRQ_HANDLED;
 | |
| +}
 | |
| +
 | |
|  static int brcm_usb_phy_init(struct phy *gphy)
 | |
|  {
 | |
|  	struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
 | |
| @@ -76,6 +87,7 @@ static int brcm_usb_phy_init(struct phy
 | |
|  	if (priv->init_count++ == 0) {
 | |
|  		clk_prepare_enable(priv->usb_20_clk);
 | |
|  		clk_prepare_enable(priv->usb_30_clk);
 | |
| +		clk_prepare_enable(priv->suspend_clk);
 | |
|  		brcm_usb_init_common(&priv->ini);
 | |
|  	}
 | |
|  	mutex_unlock(&priv->mutex);
 | |
| @@ -108,6 +120,7 @@ static int brcm_usb_phy_exit(struct phy
 | |
|  		brcm_usb_uninit_common(&priv->ini);
 | |
|  		clk_disable_unprepare(priv->usb_20_clk);
 | |
|  		clk_disable_unprepare(priv->usb_30_clk);
 | |
| +		clk_disable_unprepare(priv->suspend_clk);
 | |
|  	}
 | |
|  	mutex_unlock(&priv->mutex);
 | |
|  	phy->inited = false;
 | |
| @@ -228,11 +241,12 @@ static const struct attribute_group brcm
 | |
|  	.attrs = brcm_usb_phy_attrs,
 | |
|  };
 | |
|  
 | |
| -static int brcm_usb_phy_dvr_init(struct device *dev,
 | |
| +static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
 | |
|  				 struct brcm_usb_phy_data *priv,
 | |
|  				 struct device_node *dn)
 | |
|  {
 | |
| -	struct phy *gphy;
 | |
| +	struct device *dev = &pdev->dev;
 | |
| +	struct phy *gphy = NULL;
 | |
|  	int err;
 | |
|  
 | |
|  	priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb");
 | |
| @@ -275,6 +289,28 @@ static int brcm_usb_phy_dvr_init(struct
 | |
|  		if (err)
 | |
|  			return err;
 | |
|  	}
 | |
| +
 | |
| +	priv->suspend_clk = clk_get(dev, "usb0_freerun");
 | |
| +	if (IS_ERR(priv->suspend_clk)) {
 | |
| +		dev_err(dev, "Suspend Clock not found in Device Tree\n");
 | |
| +		priv->suspend_clk = NULL;
 | |
| +	}
 | |
| +
 | |
| +	priv->wake_irq = platform_get_irq_byname(pdev, "wake");
 | |
| +	if (priv->wake_irq < 0)
 | |
| +		priv->wake_irq = platform_get_irq_byname(pdev, "wakeup");
 | |
| +	if (priv->wake_irq >= 0) {
 | |
| +		err = devm_request_irq(dev, priv->wake_irq,
 | |
| +				       brcm_usb_phy_wake_isr, 0,
 | |
| +				       dev_name(dev), gphy);
 | |
| +		if (err < 0)
 | |
| +			return err;
 | |
| +		device_set_wakeup_capable(dev, 1);
 | |
| +	} else {
 | |
| +		dev_info(dev,
 | |
| +			 "Wake interrupt missing, system wake not supported\n");
 | |
| +	}
 | |
| +
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| @@ -335,7 +371,7 @@ static int brcm_usb_phy_probe(struct pla
 | |
|  	if (of_property_read_bool(dn, "brcm,has-eohci"))
 | |
|  		priv->has_eohci = true;
 | |
|  
 | |
| -	err = brcm_usb_phy_dvr_init(dev, priv, dn);
 | |
| +	err = brcm_usb_phy_dvr_init(pdev, priv, dn);
 | |
|  	if (err)
 | |
|  		return err;
 | |
|  
 | |
| @@ -386,10 +422,13 @@ static int brcm_usb_phy_suspend(struct d
 | |
|  		if (priv->phys[BRCM_USB_PHY_2_0].inited)
 | |
|  			brcm_usb_uninit_eohci(&priv->ini);
 | |
|  		brcm_usb_uninit_common(&priv->ini);
 | |
| +		brcm_usb_wake_enable(&priv->ini, true);
 | |
|  		if (priv->phys[BRCM_USB_PHY_3_0].inited)
 | |
|  			clk_disable_unprepare(priv->usb_30_clk);
 | |
|  		if (priv->phys[BRCM_USB_PHY_2_0].inited)
 | |
|  			clk_disable_unprepare(priv->usb_20_clk);
 | |
| +		if (priv->wake_irq >= 0)
 | |
| +			enable_irq_wake(priv->wake_irq);
 | |
|  	}
 | |
|  	return 0;
 | |
|  }
 | |
| @@ -400,6 +439,7 @@ static int brcm_usb_phy_resume(struct de
 | |
|  
 | |
|  	clk_prepare_enable(priv->usb_20_clk);
 | |
|  	clk_prepare_enable(priv->usb_30_clk);
 | |
| +	brcm_usb_wake_enable(&priv->ini, false);
 | |
|  	brcm_usb_init_ipp(&priv->ini);
 | |
|  
 | |
|  	/*
 | |
| @@ -407,6 +447,8 @@ static int brcm_usb_phy_resume(struct de
 | |
|  	 * Uninitialize anything that wasn't previously initialized.
 | |
|  	 */
 | |
|  	if (priv->init_count) {
 | |
| +		if (priv->wake_irq >= 0)
 | |
| +			disable_irq_wake(priv->wake_irq);
 | |
|  		brcm_usb_init_common(&priv->ini);
 | |
|  		if (priv->phys[BRCM_USB_PHY_2_0].inited) {
 | |
|  			brcm_usb_init_eohci(&priv->ini);
 |