blob: d52edfef77091fc1d4c9ad4b364175ff0ea9360c [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From f1c0db40a3ade1f1a39e5794d728f2953d817322 Mon Sep 17 00:00:00 2001
2From: Al Cooper <alcooperx@gmail.com>
3Date: Fri, 3 Jan 2020 13:18:02 -0500
4Subject: [PATCH] phy: usb: Add "wake on" functionality
5
6Add the ability to handle USB wake events from USB devices when
7in S2 mode. Typically there is some additional configuration
8needed to tell the USB device to generate the wake event when
9suspended but this varies with the different USB device classes.
10For example, on USB Ethernet dongles, ethtool should be used to
11enable the magic packet wake functionality in the dongle.
12NOTE: This requires that the "power/wakeup" sysfs entry for
13the USB device generating the wakeup be set to "enabled".
14
15This functionality requires a special hardware sideband path that
16will trigger the AON_PM_L2 interrupt needed to wake the system from
17S2 even though the USB host controllers are in IDDQ (low power state)
18and most USB related clocks are shut off. For the sideband signaling
19to work we need to leave the usbx_freerun clock running, but this
20clock consumes very little power by design. There's a bug in the
21XHCI wake hardware so only EHCI/OHCI wake is currently supported.
22
23Signed-off-by: Al Cooper <alcooperx@gmail.com>
24Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
25Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
26---
27 drivers/phy/broadcom/phy-brcm-usb-init.c | 17 +++++++++
28 drivers/phy/broadcom/phy-brcm-usb-init.h | 1 +
29 drivers/phy/broadcom/phy-brcm-usb.c | 48 ++++++++++++++++++++++--
30 3 files changed, 63 insertions(+), 3 deletions(-)
31
32--- a/drivers/phy/broadcom/phy-brcm-usb-init.c
33+++ b/drivers/phy/broadcom/phy-brcm-usb-init.c
34@@ -58,6 +58,8 @@
35 #define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */
36 #define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */
37 #define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */
38+#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK 0x00000001
39+#define USB_CTRL_USB_PM_STATUS 0x38
40 #define USB_CTRL_USB30_CTL1 0x60
41 #define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010
42 #define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000
43@@ -855,6 +857,10 @@ void brcm_usb_init_common(struct brcm_us
44 u32 reg;
45 void __iomem *ctrl = params->ctrl_regs;
46
47+ /* Clear any pending wake conditions */
48+ reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS));
49+ brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS));
50+
51 /* Take USB out of power down */
52 if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) {
53 USB_CTRL_UNSET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN);
54@@ -1010,6 +1016,17 @@ void brcm_usb_uninit_xhci(struct brcm_us
55 USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
56 }
57
58+void brcm_usb_wake_enable(struct brcm_usb_init_params *params,
59+ int enable)
60+{
61+ void __iomem *ctrl = params->ctrl_regs;
62+
63+ if (enable)
64+ USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN);
65+ else
66+ USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN);
67+}
68+
69 void brcm_usb_set_family_map(struct brcm_usb_init_params *params)
70 {
71 int fam;
72--- a/drivers/phy/broadcom/phy-brcm-usb-init.h
73+++ b/drivers/phy/broadcom/phy-brcm-usb-init.h
74@@ -38,5 +38,6 @@ void brcm_usb_init_xhci(struct brcm_usb_
75 void brcm_usb_uninit_common(struct brcm_usb_init_params *ini);
76 void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini);
77 void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini);
78+void brcm_usb_wake_enable(struct brcm_usb_init_params *params, int enable);
79
80 #endif /* _USB_BRCM_COMMON_INIT_H */
81--- a/drivers/phy/broadcom/phy-brcm-usb.c
82+++ b/drivers/phy/broadcom/phy-brcm-usb.c
83@@ -57,11 +57,22 @@ struct brcm_usb_phy_data {
84 bool has_xhci;
85 struct clk *usb_20_clk;
86 struct clk *usb_30_clk;
87+ struct clk *suspend_clk;
88 struct mutex mutex; /* serialize phy init */
89 int init_count;
90+ int wake_irq;
91 struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
92 };
93
94+static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id)
95+{
96+ struct phy *gphy = dev_id;
97+
98+ pm_wakeup_event(&gphy->dev, 0);
99+
100+ return IRQ_HANDLED;
101+}
102+
103 static int brcm_usb_phy_init(struct phy *gphy)
104 {
105 struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
106@@ -76,6 +87,7 @@ static int brcm_usb_phy_init(struct phy
107 if (priv->init_count++ == 0) {
108 clk_prepare_enable(priv->usb_20_clk);
109 clk_prepare_enable(priv->usb_30_clk);
110+ clk_prepare_enable(priv->suspend_clk);
111 brcm_usb_init_common(&priv->ini);
112 }
113 mutex_unlock(&priv->mutex);
114@@ -108,6 +120,7 @@ static int brcm_usb_phy_exit(struct phy
115 brcm_usb_uninit_common(&priv->ini);
116 clk_disable_unprepare(priv->usb_20_clk);
117 clk_disable_unprepare(priv->usb_30_clk);
118+ clk_disable_unprepare(priv->suspend_clk);
119 }
120 mutex_unlock(&priv->mutex);
121 phy->inited = false;
122@@ -228,11 +241,12 @@ static const struct attribute_group brcm
123 .attrs = brcm_usb_phy_attrs,
124 };
125
126-static int brcm_usb_phy_dvr_init(struct device *dev,
127+static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
128 struct brcm_usb_phy_data *priv,
129 struct device_node *dn)
130 {
131- struct phy *gphy;
132+ struct device *dev = &pdev->dev;
133+ struct phy *gphy = NULL;
134 int err;
135
136 priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb");
137@@ -275,6 +289,28 @@ static int brcm_usb_phy_dvr_init(struct
138 if (err)
139 return err;
140 }
141+
142+ priv->suspend_clk = clk_get(dev, "usb0_freerun");
143+ if (IS_ERR(priv->suspend_clk)) {
144+ dev_err(dev, "Suspend Clock not found in Device Tree\n");
145+ priv->suspend_clk = NULL;
146+ }
147+
148+ priv->wake_irq = platform_get_irq_byname(pdev, "wake");
149+ if (priv->wake_irq < 0)
150+ priv->wake_irq = platform_get_irq_byname(pdev, "wakeup");
151+ if (priv->wake_irq >= 0) {
152+ err = devm_request_irq(dev, priv->wake_irq,
153+ brcm_usb_phy_wake_isr, 0,
154+ dev_name(dev), gphy);
155+ if (err < 0)
156+ return err;
157+ device_set_wakeup_capable(dev, 1);
158+ } else {
159+ dev_info(dev,
160+ "Wake interrupt missing, system wake not supported\n");
161+ }
162+
163 return 0;
164 }
165
166@@ -335,7 +371,7 @@ static int brcm_usb_phy_probe(struct pla
167 if (of_property_read_bool(dn, "brcm,has-eohci"))
168 priv->has_eohci = true;
169
170- err = brcm_usb_phy_dvr_init(dev, priv, dn);
171+ err = brcm_usb_phy_dvr_init(pdev, priv, dn);
172 if (err)
173 return err;
174
175@@ -386,10 +422,13 @@ static int brcm_usb_phy_suspend(struct d
176 if (priv->phys[BRCM_USB_PHY_2_0].inited)
177 brcm_usb_uninit_eohci(&priv->ini);
178 brcm_usb_uninit_common(&priv->ini);
179+ brcm_usb_wake_enable(&priv->ini, true);
180 if (priv->phys[BRCM_USB_PHY_3_0].inited)
181 clk_disable_unprepare(priv->usb_30_clk);
182 if (priv->phys[BRCM_USB_PHY_2_0].inited)
183 clk_disable_unprepare(priv->usb_20_clk);
184+ if (priv->wake_irq >= 0)
185+ enable_irq_wake(priv->wake_irq);
186 }
187 return 0;
188 }
189@@ -400,6 +439,7 @@ static int brcm_usb_phy_resume(struct de
190
191 clk_prepare_enable(priv->usb_20_clk);
192 clk_prepare_enable(priv->usb_30_clk);
193+ brcm_usb_wake_enable(&priv->ini, false);
194 brcm_usb_init_ipp(&priv->ini);
195
196 /*
197@@ -407,6 +447,8 @@ static int brcm_usb_phy_resume(struct de
198 * Uninitialize anything that wasn't previously initialized.
199 */
200 if (priv->init_count) {
201+ if (priv->wake_irq >= 0)
202+ disable_irq_wake(priv->wake_irq);
203 brcm_usb_init_common(&priv->ini);
204 if (priv->phys[BRCM_USB_PHY_2_0].inited) {
205 brcm_usb_init_eohci(&priv->ini);