1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
From e6fe50ef22071fe87ce48f79ab4fe21cbec2081b Mon Sep 17 00:00:00 2001
From: Chunfeng Yun <chunfeng.yun@mediatek.com>
Date: Wed, 3 Jan 2018 16:53:20 +0800
Subject: [PATCH 190/224] usb: xhci-mtk: supports remote wakeup for mt2712 with
two xHCI IPs
The old way of usb wakeup only supports platform with single xHCI IP,
such as mt8173, but mt2712 has two xHCI IPs, so rebuild its flow and
supports the new glue layer of usb wakeup on mt2712 which is different
from mt8173.
Due to there is a hardware bug with the LINE STATE wakeup mode on
mt8173 which causes wakeup failure by low speed devices, and also
because IP SLEEP mode can cover all functions of LINE STATE mode,
it is unused in fact, and will not support it later, so remove it at
the same time.
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/host/xhci-mtk.c | 177 +++++++++++++++-----------------------------
drivers/usb/host/xhci-mtk.h | 6 +-
2 files changed, 65 insertions(+), 118 deletions(-)
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -66,26 +66,21 @@
/* u2_phy_pll register */
#define CTRL_U2_FORCE_PLL_STB BIT(28)
-#define PERI_WK_CTRL0 0x400
-#define UWK_CTR0_0P_LS_PE BIT(8) /* posedge */
-#define UWK_CTR0_0P_LS_NE BIT(7) /* negedge for 0p linestate*/
-#define UWK_CTL1_1P_LS_C(x) (((x) & 0xf) << 1)
-#define UWK_CTL1_1P_LS_E BIT(0)
-
-#define PERI_WK_CTRL1 0x404
-#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26)
-#define UWK_CTL1_IS_E BIT(25)
-#define UWK_CTL1_0P_LS_C(x) (((x) & 0xf) << 21)
-#define UWK_CTL1_0P_LS_E BIT(20)
-#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */
-#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */
-#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */
-#define UWK_CTL1_0P_LS_P BIT(7)
-#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */
-
-enum ssusb_wakeup_src {
- SSUSB_WK_IP_SLEEP = 1,
- SSUSB_WK_LINE_STATE = 2,
+/* usb remote wakeup registers in syscon */
+/* mt8173 etc */
+#define PERI_WK_CTRL1 0x4
+#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */
+#define WC1_IS_EN BIT(25)
+#define WC1_IS_P BIT(6) /* polarity for ip sleep */
+
+/* mt2712 etc */
+#define PERI_SSUSB_SPM_CTRL 0x0
+#define SSC_IP_SLEEP_EN BIT(4)
+#define SSC_SPM_INT_EN BIT(1)
+
+enum ssusb_uwk_vers {
+ SSUSB_UWK_V1 = 1,
+ SSUSB_UWK_V2,
};
static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
@@ -308,112 +303,58 @@ static void xhci_mtk_clks_disable(struct
}
/* only clocks can be turn off for ip-sleep wakeup mode */
-static void usb_wakeup_ip_sleep_en(struct xhci_hcd_mtk *mtk)
+static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable)
{
- u32 tmp;
- struct regmap *pericfg = mtk->pericfg;
+ u32 reg, msk, val;
- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
- tmp &= ~UWK_CTL1_IS_P;
- tmp &= ~(UWK_CTL1_IS_C(0xf));
- tmp |= UWK_CTL1_IS_C(0x8);
- regmap_write(pericfg, PERI_WK_CTRL1, tmp);
- regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
-
- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
- dev_dbg(mtk->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
- __func__, tmp);
-}
-
-static void usb_wakeup_ip_sleep_dis(struct xhci_hcd_mtk *mtk)
-{
- u32 tmp;
-
- regmap_read(mtk->pericfg, PERI_WK_CTRL1, &tmp);
- tmp &= ~UWK_CTL1_IS_E;
- regmap_write(mtk->pericfg, PERI_WK_CTRL1, tmp);
+ switch (mtk->uwk_vers) {
+ case SSUSB_UWK_V1:
+ reg = mtk->uwk_reg_base + PERI_WK_CTRL1;
+ msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P;
+ val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0;
+ break;
+ case SSUSB_UWK_V2:
+ reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
+ msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
+ val = enable ? msk : 0;
+ break;
+ default:
+ return;
+ };
+ regmap_update_bits(mtk->uwk, reg, msk, val);
}
-/*
-* for line-state wakeup mode, phy's power should not power-down
-* and only support cable plug in/out
-*/
-static void usb_wakeup_line_state_en(struct xhci_hcd_mtk *mtk)
+static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
+ struct device_node *dn)
{
- u32 tmp;
- struct regmap *pericfg = mtk->pericfg;
-
- /* line-state of u2-port0 */
- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
- tmp &= ~UWK_CTL1_0P_LS_P;
- tmp &= ~(UWK_CTL1_0P_LS_C(0xf));
- tmp |= UWK_CTL1_0P_LS_C(0x8);
- regmap_write(pericfg, PERI_WK_CTRL1, tmp);
- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
- regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_0P_LS_E);
+ struct of_phandle_args args;
+ int ret;
- /* line-state of u2-port1 */
- regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
- tmp &= ~(UWK_CTL1_1P_LS_C(0xf));
- tmp |= UWK_CTL1_1P_LS_C(0x8);
- regmap_write(pericfg, PERI_WK_CTRL0, tmp);
- regmap_write(pericfg, PERI_WK_CTRL0, tmp | UWK_CTL1_1P_LS_E);
-}
+ /* Wakeup function is optional */
+ mtk->uwk_en = of_property_read_bool(dn, "wakeup-source");
+ if (!mtk->uwk_en)
+ return 0;
-static void usb_wakeup_line_state_dis(struct xhci_hcd_mtk *mtk)
-{
- u32 tmp;
- struct regmap *pericfg = mtk->pericfg;
+ ret = of_parse_phandle_with_fixed_args(dn,
+ "mediatek,syscon-wakeup", 2, 0, &args);
+ if (ret)
+ return ret;
- /* line-state of u2-port0 */
- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
- tmp &= ~UWK_CTL1_0P_LS_E;
- regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+ mtk->uwk_reg_base = args.args[0];
+ mtk->uwk_vers = args.args[1];
+ mtk->uwk = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ dev_info(mtk->dev, "uwk - reg:0x%x, version:%d\n",
+ mtk->uwk_reg_base, mtk->uwk_vers);
- /* line-state of u2-port1 */
- regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
- tmp &= ~UWK_CTL1_1P_LS_E;
- regmap_write(pericfg, PERI_WK_CTRL0, tmp);
-}
+ return PTR_ERR_OR_ZERO(mtk->uwk);
-static void usb_wakeup_enable(struct xhci_hcd_mtk *mtk)
-{
- if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
- usb_wakeup_ip_sleep_en(mtk);
- else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
- usb_wakeup_line_state_en(mtk);
}
-static void usb_wakeup_disable(struct xhci_hcd_mtk *mtk)
+static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable)
{
- if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
- usb_wakeup_ip_sleep_dis(mtk);
- else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
- usb_wakeup_line_state_dis(mtk);
-}
-
-static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
- struct device_node *dn)
-{
- struct device *dev = mtk->dev;
-
- /*
- * wakeup function is optional, so it is not an error if this property
- * does not exist, and in such case, no need to get relative
- * properties anymore.
- */
- of_property_read_u32(dn, "mediatek,wakeup-src", &mtk->wakeup_src);
- if (!mtk->wakeup_src)
- return 0;
-
- mtk->pericfg = syscon_regmap_lookup_by_phandle(dn,
- "mediatek,syscon-wakeup");
- if (IS_ERR(mtk->pericfg)) {
- dev_err(dev, "fail to get pericfg regs\n");
- return PTR_ERR(mtk->pericfg);
- }
-
- return 0;
+ if (mtk->uwk_en)
+ usb_wakeup_ip_sleep_set(mtk, enable);
}
static int xhci_mtk_setup(struct usb_hcd *hcd);
@@ -595,8 +536,10 @@ static int xhci_mtk_probe(struct platfor
&mtk->u3p_dis_msk);
ret = usb_wakeup_of_property_parse(mtk, node);
- if (ret)
+ if (ret) {
+ dev_err(dev, "failed to parse uwk property\n");
return ret;
+ }
mtk->num_phys = of_count_phandle_with_args(node,
"phys", "#phy-cells");
@@ -780,7 +723,7 @@ static int __maybe_unused xhci_mtk_suspe
xhci_mtk_host_disable(mtk);
xhci_mtk_phy_power_off(mtk);
xhci_mtk_clks_disable(mtk);
- usb_wakeup_enable(mtk);
+ usb_wakeup_set(mtk, true);
return 0;
}
@@ -790,7 +733,7 @@ static int __maybe_unused xhci_mtk_resum
struct usb_hcd *hcd = mtk->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- usb_wakeup_disable(mtk);
+ usb_wakeup_set(mtk, false);
xhci_mtk_clks_enable(mtk);
xhci_mtk_phy_power_on(mtk);
xhci_mtk_host_enable(mtk);
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -131,8 +131,12 @@ struct xhci_hcd_mtk {
struct regmap *pericfg;
struct phy **phys;
int num_phys;
- int wakeup_src;
bool lpm_support;
+ /* usb remote wakeup */
+ bool uwk_en;
+ struct regmap *uwk;
+ u32 uwk_reg_base;
+ u32 uwk_vers;
};
static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd)
|