]>
Commit | Line | Data |
---|---|---|
c9999337 NA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * USB Glue for Amlogic G12A SoCs | |
4 | * | |
5 | * Copyright (c) 2019 BayLibre, SAS | |
6 | * Author: Neil Armstrong <narmstrong@baylibre.com> | |
7 | */ | |
8 | ||
9 | /* | |
10 | * The USB is organized with a glue around the DWC3 Controller IP as : | |
11 | * - Control registers for each USB2 Ports | |
12 | * - Control registers for the USB PHY layer | |
13 | * - SuperSpeed PHY can be enabled only if port is used | |
f90db107 | 14 | * - Dynamic OTG switching with ID change interrupt |
c9999337 NA |
15 | */ |
16 | ||
17 | #include <linux/module.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/clk.h> | |
21 | #include <linux/of.h> | |
22 | #include <linux/of_platform.h> | |
23 | #include <linux/pm_runtime.h> | |
24 | #include <linux/regmap.h> | |
25 | #include <linux/bitfield.h> | |
26 | #include <linux/bitops.h> | |
27 | #include <linux/reset.h> | |
28 | #include <linux/phy/phy.h> | |
29 | #include <linux/usb/otg.h> | |
30 | #include <linux/usb/role.h> | |
31 | #include <linux/regulator/consumer.h> | |
32 | ||
013af227 | 33 | /* USB2 Ports Control Registers, offsets are per-port */ |
c9999337 NA |
34 | |
35 | #define U2P_REG_SIZE 0x20 | |
36 | ||
37 | #define U2P_R0 0x0 | |
38 | #define U2P_R0_HOST_DEVICE BIT(0) | |
39 | #define U2P_R0_POWER_OK BIT(1) | |
40 | #define U2P_R0_HAST_MODE BIT(2) | |
41 | #define U2P_R0_POWER_ON_RESET BIT(3) | |
42 | #define U2P_R0_ID_PULLUP BIT(4) | |
43 | #define U2P_R0_DRV_VBUS BIT(5) | |
44 | ||
45 | #define U2P_R1 0x4 | |
46 | #define U2P_R1_PHY_READY BIT(0) | |
47 | #define U2P_R1_ID_DIG BIT(1) | |
48 | #define U2P_R1_OTG_SESSION_VALID BIT(2) | |
49 | #define U2P_R1_VBUS_VALID BIT(3) | |
50 | ||
51 | /* USB Glue Control Registers */ | |
52 | ||
013af227 NA |
53 | #define G12A_GLUE_OFFSET 0x80 |
54 | ||
55 | #define USB_R0 0x00 | |
c9999337 NA |
56 | #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) |
57 | #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) | |
58 | #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) | |
59 | #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) | |
60 | #define USB_R0_U2D_ACT BIT(31) | |
61 | ||
013af227 | 62 | #define USB_R1 0x04 |
c9999337 NA |
63 | #define USB_R1_U3H_BIGENDIAN_GS BIT(0) |
64 | #define USB_R1_U3H_PME_ENABLE BIT(1) | |
65 | #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2) | |
66 | #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7) | |
67 | #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12) | |
68 | #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) | |
69 | #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) | |
70 | #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) | |
71 | #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) | |
72 | #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) | |
73 | ||
013af227 | 74 | #define USB_R2 0x08 |
c9999337 NA |
75 | #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) |
76 | #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) | |
77 | ||
013af227 | 78 | #define USB_R3 0x0c |
c9999337 NA |
79 | #define USB_R3_P30_SSC_ENABLE BIT(0) |
80 | #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) | |
81 | #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) | |
82 | #define USB_R3_P30_REF_SSP_EN BIT(13) | |
83 | ||
013af227 | 84 | #define USB_R4 0x10 |
c9999337 NA |
85 | #define USB_R4_P21_PORT_RESET_0 BIT(0) |
86 | #define USB_R4_P21_SLEEP_M0 BIT(1) | |
87 | #define USB_R4_MEM_PD_MASK GENMASK(3, 2) | |
88 | #define USB_R4_P21_ONLY BIT(4) | |
89 | ||
013af227 | 90 | #define USB_R5 0x14 |
c9999337 NA |
91 | #define USB_R5_ID_DIG_SYNC BIT(0) |
92 | #define USB_R5_ID_DIG_REG BIT(1) | |
93 | #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) | |
94 | #define USB_R5_ID_DIG_EN_0 BIT(4) | |
95 | #define USB_R5_ID_DIG_EN_1 BIT(5) | |
96 | #define USB_R5_ID_DIG_CURR BIT(6) | |
97 | #define USB_R5_ID_DIG_IRQ BIT(7) | |
98 | #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) | |
99 | #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) | |
100 | ||
5174564c NA |
101 | #define PHY_COUNT 3 |
102 | #define USB2_OTG_PHY 1 | |
c9999337 | 103 | |
a9fc15e0 NA |
104 | static struct clk_bulk_data meson_gxl_clocks[] = { |
105 | { .id = "usb_ctrl" }, | |
106 | { .id = "ddr" }, | |
107 | }; | |
108 | ||
1e355f21 HL |
109 | static struct clk_bulk_data meson_g12a_clocks[] = { |
110 | { .id = NULL }, | |
111 | }; | |
112 | ||
113 | static struct clk_bulk_data meson_a1_clocks[] = { | |
114 | { .id = "usb_ctrl" }, | |
115 | { .id = "usb_bus" }, | |
116 | { .id = "xtal_usb_ctrl" }, | |
117 | }; | |
118 | ||
e5ee93d4 | 119 | static const char * const meson_gxm_phy_names[] = { |
a9fc15e0 NA |
120 | "usb2-phy0", "usb2-phy1", "usb2-phy2", |
121 | }; | |
122 | ||
e5ee93d4 | 123 | static const char * const meson_g12a_phy_names[] = { |
5174564c NA |
124 | "usb2-phy0", "usb2-phy1", "usb3-phy0", |
125 | }; | |
126 | ||
127 | /* | |
128 | * Amlogic A1 has a single physical PHY, in slot 1, but still has the | |
129 | * two U2 PHY controls register blocks like G12A. | |
65f3d449 | 130 | * AXG has the similar scheme, thus needs the same tweak. |
5174564c NA |
131 | * Handling the first PHY on slot 1 would need a large amount of code |
132 | * changes, and the current management is generic enough to handle it | |
133 | * correctly when only the "usb2-phy1" phy is specified on-par with the | |
134 | * DT bindings. | |
135 | */ | |
e5ee93d4 | 136 | static const char * const meson_a1_phy_names[] = { |
5174564c NA |
137 | "usb2-phy0", "usb2-phy1" |
138 | }; | |
139 | ||
013af227 NA |
140 | struct dwc3_meson_g12a; |
141 | ||
1e355f21 HL |
142 | struct dwc3_meson_g12a_drvdata { |
143 | bool otg_switch_supported; | |
df7e3745 | 144 | bool otg_phy_host_port_disable; |
1e355f21 HL |
145 | struct clk_bulk_data *clks; |
146 | int num_clks; | |
e5ee93d4 | 147 | const char * const *phy_names; |
5174564c | 148 | int num_phys; |
013af227 | 149 | int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base); |
31306821 NA |
150 | int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i, |
151 | enum phy_mode mode); | |
152 | int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i, | |
153 | enum phy_mode mode); | |
5b0ba0ca NA |
154 | int (*usb_init)(struct dwc3_meson_g12a *priv); |
155 | int (*usb_post_init)(struct dwc3_meson_g12a *priv); | |
1e355f21 HL |
156 | }; |
157 | ||
a9fc15e0 NA |
158 | static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv, |
159 | void __iomem *base); | |
013af227 NA |
160 | static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv, |
161 | void __iomem *base); | |
162 | ||
31306821 | 163 | static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i, |
a9fc15e0 NA |
164 | enum phy_mode mode); |
165 | static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i, | |
166 | enum phy_mode mode); | |
31306821 NA |
167 | |
168 | static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv, | |
169 | int i, enum phy_mode mode); | |
a9fc15e0 NA |
170 | static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv, |
171 | int i, enum phy_mode mode); | |
31306821 | 172 | |
5b0ba0ca | 173 | static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv); |
a9fc15e0 NA |
174 | static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv); |
175 | ||
176 | static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv); | |
5b0ba0ca | 177 | |
df7e3745 NA |
178 | /* |
179 | * For GXL and GXM SoCs: | |
180 | * USB Phy muxing between the DWC2 Device controller and the DWC3 Host | |
181 | * controller is buggy when switching from Device to Host when USB port | |
182 | * is unpopulated, it causes the DWC3 to hard crash. | |
183 | * When populated (including OTG switching with ID pin), the switch works | |
184 | * like a charm like on the G12A platforms. | |
185 | * In order to still switch from Host to Device on an USB Type-A port, | |
186 | * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host | |
187 | * controller from the port, but when used the DWC3 controller must be | |
188 | * reset to recover usage of the port. | |
189 | */ | |
190 | ||
a9fc15e0 NA |
191 | static struct dwc3_meson_g12a_drvdata gxl_drvdata = { |
192 | .otg_switch_supported = true, | |
193 | .otg_phy_host_port_disable = true, | |
194 | .clks = meson_gxl_clocks, | |
195 | .num_clks = ARRAY_SIZE(meson_g12a_clocks), | |
196 | .phy_names = meson_a1_phy_names, | |
197 | .num_phys = ARRAY_SIZE(meson_a1_phy_names), | |
198 | .setup_regmaps = dwc3_meson_gxl_setup_regmaps, | |
199 | .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy, | |
200 | .set_phy_mode = dwc3_meson_gxl_set_phy_mode, | |
201 | .usb_init = dwc3_meson_gxl_usb_init, | |
202 | .usb_post_init = dwc3_meson_gxl_usb_post_init, | |
203 | }; | |
204 | ||
205 | static struct dwc3_meson_g12a_drvdata gxm_drvdata = { | |
206 | .otg_switch_supported = true, | |
207 | .otg_phy_host_port_disable = true, | |
208 | .clks = meson_gxl_clocks, | |
209 | .num_clks = ARRAY_SIZE(meson_g12a_clocks), | |
210 | .phy_names = meson_gxm_phy_names, | |
211 | .num_phys = ARRAY_SIZE(meson_gxm_phy_names), | |
212 | .setup_regmaps = dwc3_meson_gxl_setup_regmaps, | |
213 | .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy, | |
214 | .set_phy_mode = dwc3_meson_gxl_set_phy_mode, | |
215 | .usb_init = dwc3_meson_gxl_usb_init, | |
216 | .usb_post_init = dwc3_meson_gxl_usb_post_init, | |
217 | }; | |
218 | ||
65f3d449 NA |
219 | static struct dwc3_meson_g12a_drvdata axg_drvdata = { |
220 | .otg_switch_supported = true, | |
221 | .clks = meson_gxl_clocks, | |
222 | .num_clks = ARRAY_SIZE(meson_gxl_clocks), | |
223 | .phy_names = meson_a1_phy_names, | |
224 | .num_phys = ARRAY_SIZE(meson_a1_phy_names), | |
225 | .setup_regmaps = dwc3_meson_gxl_setup_regmaps, | |
226 | .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy, | |
227 | .set_phy_mode = dwc3_meson_gxl_set_phy_mode, | |
228 | .usb_init = dwc3_meson_g12a_usb_init, | |
229 | .usb_post_init = dwc3_meson_gxl_usb_post_init, | |
230 | }; | |
231 | ||
1e355f21 HL |
232 | static struct dwc3_meson_g12a_drvdata g12a_drvdata = { |
233 | .otg_switch_supported = true, | |
234 | .clks = meson_g12a_clocks, | |
235 | .num_clks = ARRAY_SIZE(meson_g12a_clocks), | |
5174564c NA |
236 | .phy_names = meson_g12a_phy_names, |
237 | .num_phys = ARRAY_SIZE(meson_g12a_phy_names), | |
013af227 | 238 | .setup_regmaps = dwc3_meson_g12a_setup_regmaps, |
31306821 NA |
239 | .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy, |
240 | .set_phy_mode = dwc3_meson_g12a_set_phy_mode, | |
5b0ba0ca | 241 | .usb_init = dwc3_meson_g12a_usb_init, |
1e355f21 HL |
242 | }; |
243 | ||
244 | static struct dwc3_meson_g12a_drvdata a1_drvdata = { | |
245 | .otg_switch_supported = false, | |
246 | .clks = meson_a1_clocks, | |
247 | .num_clks = ARRAY_SIZE(meson_a1_clocks), | |
5174564c NA |
248 | .phy_names = meson_a1_phy_names, |
249 | .num_phys = ARRAY_SIZE(meson_a1_phy_names), | |
013af227 | 250 | .setup_regmaps = dwc3_meson_g12a_setup_regmaps, |
31306821 NA |
251 | .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy, |
252 | .set_phy_mode = dwc3_meson_g12a_set_phy_mode, | |
5b0ba0ca | 253 | .usb_init = dwc3_meson_g12a_usb_init, |
1e355f21 HL |
254 | }; |
255 | ||
c9999337 NA |
256 | struct dwc3_meson_g12a { |
257 | struct device *dev; | |
013af227 NA |
258 | struct regmap *u2p_regmap[PHY_COUNT]; |
259 | struct regmap *usb_glue_regmap; | |
c9999337 NA |
260 | struct reset_control *reset; |
261 | struct phy *phys[PHY_COUNT]; | |
262 | enum usb_dr_mode otg_mode; | |
263 | enum phy_mode otg_phy_mode; | |
264 | unsigned int usb2_ports; | |
265 | unsigned int usb3_ports; | |
266 | struct regulator *vbus; | |
267 | struct usb_role_switch_desc switch_desc; | |
268 | struct usb_role_switch *role_switch; | |
1e355f21 | 269 | const struct dwc3_meson_g12a_drvdata *drvdata; |
c9999337 NA |
270 | }; |
271 | ||
a9fc15e0 NA |
272 | static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv, |
273 | int i, enum phy_mode mode) | |
274 | { | |
275 | return phy_set_mode(priv->phys[i], mode); | |
276 | } | |
277 | ||
278 | static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i, | |
279 | enum phy_mode mode) | |
280 | { | |
281 | /* On GXL PHY must be started in device mode for DWC2 init */ | |
282 | return priv->drvdata->set_phy_mode(priv, i, | |
283 | (i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE | |
284 | : PHY_MODE_USB_HOST); | |
285 | } | |
286 | ||
31306821 NA |
287 | static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv, |
288 | int i, enum phy_mode mode) | |
c9999337 NA |
289 | { |
290 | if (mode == PHY_MODE_USB_HOST) | |
013af227 | 291 | regmap_update_bits(priv->u2p_regmap[i], U2P_R0, |
c9999337 NA |
292 | U2P_R0_HOST_DEVICE, |
293 | U2P_R0_HOST_DEVICE); | |
294 | else | |
013af227 | 295 | regmap_update_bits(priv->u2p_regmap[i], U2P_R0, |
c9999337 | 296 | U2P_R0_HOST_DEVICE, 0); |
31306821 NA |
297 | |
298 | return 0; | |
299 | } | |
300 | ||
301 | static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i, | |
302 | enum phy_mode mode) | |
303 | { | |
304 | int ret; | |
305 | ||
306 | regmap_update_bits(priv->u2p_regmap[i], U2P_R0, | |
307 | U2P_R0_POWER_ON_RESET, | |
308 | U2P_R0_POWER_ON_RESET); | |
309 | ||
310 | if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) { | |
311 | regmap_update_bits(priv->u2p_regmap[i], U2P_R0, | |
312 | U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, | |
313 | U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); | |
314 | ||
315 | ret = priv->drvdata->set_phy_mode(priv, i, mode); | |
316 | } else | |
317 | ret = priv->drvdata->set_phy_mode(priv, i, | |
318 | PHY_MODE_USB_HOST); | |
319 | ||
320 | if (ret) | |
321 | return ret; | |
322 | ||
323 | regmap_update_bits(priv->u2p_regmap[i], U2P_R0, | |
324 | U2P_R0_POWER_ON_RESET, 0); | |
325 | ||
326 | return 0; | |
c9999337 NA |
327 | } |
328 | ||
5b0ba0ca NA |
329 | static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv, |
330 | enum phy_mode mode) | |
c9999337 | 331 | { |
31306821 | 332 | int i, ret; |
c9999337 | 333 | |
5174564c | 334 | for (i = 0; i < priv->drvdata->num_phys; ++i) { |
c9999337 NA |
335 | if (!priv->phys[i]) |
336 | continue; | |
337 | ||
5174564c NA |
338 | if (!strstr(priv->drvdata->phy_names[i], "usb2")) |
339 | continue; | |
340 | ||
5b0ba0ca | 341 | ret = priv->drvdata->usb2_init_phy(priv, i, mode); |
31306821 NA |
342 | if (ret) |
343 | return ret; | |
c9999337 NA |
344 | } |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) | |
350 | { | |
013af227 | 351 | regmap_update_bits(priv->usb_glue_regmap, USB_R3, |
c9999337 NA |
352 | USB_R3_P30_SSC_RANGE_MASK | |
353 | USB_R3_P30_REF_SSP_EN, | |
354 | USB_R3_P30_SSC_ENABLE | | |
355 | FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | | |
356 | USB_R3_P30_REF_SSP_EN); | |
357 | udelay(2); | |
358 | ||
013af227 | 359 | regmap_update_bits(priv->usb_glue_regmap, USB_R2, |
c9999337 NA |
360 | USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, |
361 | FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); | |
362 | ||
013af227 | 363 | regmap_update_bits(priv->usb_glue_regmap, USB_R2, |
c9999337 NA |
364 | USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, |
365 | FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); | |
366 | ||
367 | udelay(2); | |
368 | ||
013af227 | 369 | regmap_update_bits(priv->usb_glue_regmap, USB_R1, |
c9999337 NA |
370 | USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, |
371 | USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); | |
372 | ||
013af227 | 373 | regmap_update_bits(priv->usb_glue_regmap, USB_R1, |
c9999337 NA |
374 | USB_R1_P30_PCS_TX_SWING_FULL_MASK, |
375 | FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); | |
376 | } | |
377 | ||
5b0ba0ca NA |
378 | static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv, |
379 | enum phy_mode mode) | |
c9999337 | 380 | { |
5b0ba0ca | 381 | if (mode == PHY_MODE_USB_DEVICE) { |
df7e3745 NA |
382 | if (priv->otg_mode != USB_DR_MODE_OTG && |
383 | priv->drvdata->otg_phy_host_port_disable) | |
384 | /* Isolate the OTG PHY port from the Host Controller */ | |
385 | regmap_update_bits(priv->usb_glue_regmap, USB_R1, | |
386 | USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, | |
387 | FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, | |
388 | BIT(USB2_OTG_PHY))); | |
389 | ||
013af227 | 390 | regmap_update_bits(priv->usb_glue_regmap, USB_R0, |
c9999337 | 391 | USB_R0_U2D_ACT, USB_R0_U2D_ACT); |
013af227 | 392 | regmap_update_bits(priv->usb_glue_regmap, USB_R0, |
c9999337 | 393 | USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); |
013af227 | 394 | regmap_update_bits(priv->usb_glue_regmap, USB_R4, |
c9999337 NA |
395 | USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); |
396 | } else { | |
df7e3745 NA |
397 | if (priv->otg_mode != USB_DR_MODE_OTG && |
398 | priv->drvdata->otg_phy_host_port_disable) { | |
399 | regmap_update_bits(priv->usb_glue_regmap, USB_R1, | |
400 | USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0); | |
401 | msleep(500); | |
402 | } | |
013af227 | 403 | regmap_update_bits(priv->usb_glue_regmap, USB_R0, |
c9999337 | 404 | USB_R0_U2D_ACT, 0); |
013af227 | 405 | regmap_update_bits(priv->usb_glue_regmap, USB_R4, |
c9999337 NA |
406 | USB_R4_P21_SLEEP_M0, 0); |
407 | } | |
408 | } | |
409 | ||
5b0ba0ca NA |
410 | static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv, |
411 | enum phy_mode mode) | |
c9999337 NA |
412 | { |
413 | int ret; | |
414 | ||
5b0ba0ca | 415 | ret = dwc3_meson_g12a_usb2_init(priv, mode); |
c9999337 NA |
416 | if (ret) |
417 | return ret; | |
418 | ||
013af227 | 419 | regmap_update_bits(priv->usb_glue_regmap, USB_R1, |
c9999337 NA |
420 | USB_R1_U3H_FLADJ_30MHZ_REG_MASK, |
421 | FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); | |
422 | ||
013af227 | 423 | regmap_update_bits(priv->usb_glue_regmap, USB_R5, |
c9999337 NA |
424 | USB_R5_ID_DIG_EN_0, |
425 | USB_R5_ID_DIG_EN_0); | |
013af227 | 426 | regmap_update_bits(priv->usb_glue_regmap, USB_R5, |
c9999337 NA |
427 | USB_R5_ID_DIG_EN_1, |
428 | USB_R5_ID_DIG_EN_1); | |
013af227 | 429 | regmap_update_bits(priv->usb_glue_regmap, USB_R5, |
c9999337 NA |
430 | USB_R5_ID_DIG_TH_MASK, |
431 | FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); | |
432 | ||
433 | /* If we have an actual SuperSpeed port, initialize it */ | |
434 | if (priv->usb3_ports) | |
435 | dwc3_meson_g12a_usb3_init(priv); | |
436 | ||
5b0ba0ca | 437 | dwc3_meson_g12a_usb_otg_apply_mode(priv, mode); |
c9999337 NA |
438 | |
439 | return 0; | |
440 | } | |
441 | ||
013af227 NA |
442 | static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = { |
443 | .name = "usb-glue", | |
c9999337 NA |
444 | .reg_bits = 8, |
445 | .val_bits = 32, | |
446 | .reg_stride = 4, | |
447 | .max_register = USB_R5, | |
448 | }; | |
449 | ||
450 | static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) | |
451 | { | |
5174564c | 452 | const char *phy_name; |
c9999337 NA |
453 | int i; |
454 | ||
5174564c NA |
455 | for (i = 0 ; i < priv->drvdata->num_phys ; ++i) { |
456 | phy_name = priv->drvdata->phy_names[i]; | |
457 | priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name); | |
c9999337 NA |
458 | if (!priv->phys[i]) |
459 | continue; | |
460 | ||
461 | if (IS_ERR(priv->phys[i])) | |
462 | return PTR_ERR(priv->phys[i]); | |
463 | ||
5174564c | 464 | if (strstr(phy_name, "usb3")) |
c9999337 NA |
465 | priv->usb3_ports++; |
466 | else | |
467 | priv->usb2_ports++; | |
468 | } | |
469 | ||
470 | dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports); | |
471 | dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports); | |
472 | ||
473 | return 0; | |
474 | } | |
475 | ||
476 | static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv) | |
477 | { | |
478 | u32 reg; | |
479 | ||
013af227 | 480 | regmap_read(priv->usb_glue_regmap, USB_R5, ®); |
c9999337 NA |
481 | |
482 | if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG)) | |
483 | return PHY_MODE_USB_DEVICE; | |
484 | ||
485 | return PHY_MODE_USB_HOST; | |
486 | } | |
487 | ||
488 | static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, | |
489 | enum phy_mode mode) | |
490 | { | |
491 | int ret; | |
492 | ||
1e355f21 | 493 | if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY]) |
c9999337 NA |
494 | return -EINVAL; |
495 | ||
496 | if (mode == PHY_MODE_USB_HOST) | |
497 | dev_info(priv->dev, "switching to Host Mode\n"); | |
498 | else | |
499 | dev_info(priv->dev, "switching to Device Mode\n"); | |
500 | ||
501 | if (priv->vbus) { | |
502 | if (mode == PHY_MODE_USB_DEVICE) | |
503 | ret = regulator_disable(priv->vbus); | |
504 | else | |
505 | ret = regulator_enable(priv->vbus); | |
506 | if (ret) | |
507 | return ret; | |
508 | } | |
509 | ||
510 | priv->otg_phy_mode = mode; | |
511 | ||
31306821 NA |
512 | ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode); |
513 | if (ret) | |
514 | return ret; | |
c9999337 | 515 | |
5b0ba0ca | 516 | dwc3_meson_g12a_usb_otg_apply_mode(priv, mode); |
c9999337 NA |
517 | |
518 | return 0; | |
519 | } | |
520 | ||
bce3052f HK |
521 | static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw, |
522 | enum usb_role role) | |
c9999337 | 523 | { |
bce3052f | 524 | struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw); |
c9999337 NA |
525 | enum phy_mode mode; |
526 | ||
527 | if (role == USB_ROLE_NONE) | |
528 | return 0; | |
529 | ||
530 | mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST | |
531 | : PHY_MODE_USB_DEVICE; | |
532 | ||
533 | if (mode == priv->otg_phy_mode) | |
534 | return 0; | |
535 | ||
df7e3745 | 536 | if (priv->drvdata->otg_phy_host_port_disable) |
e5ee93d4 | 537 | dev_warn_once(priv->dev, "Broken manual OTG switch\n"); |
df7e3745 | 538 | |
c9999337 NA |
539 | return dwc3_meson_g12a_otg_mode_set(priv, mode); |
540 | } | |
541 | ||
bce3052f | 542 | static enum usb_role dwc3_meson_g12a_role_get(struct usb_role_switch *sw) |
c9999337 | 543 | { |
bce3052f | 544 | struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw); |
c9999337 NA |
545 | |
546 | return priv->otg_phy_mode == PHY_MODE_USB_HOST ? | |
547 | USB_ROLE_HOST : USB_ROLE_DEVICE; | |
548 | } | |
549 | ||
f90db107 NA |
550 | static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data) |
551 | { | |
552 | struct dwc3_meson_g12a *priv = data; | |
553 | enum phy_mode otg_id; | |
554 | ||
555 | otg_id = dwc3_meson_g12a_get_id(priv); | |
556 | if (otg_id != priv->otg_phy_mode) { | |
557 | if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) | |
558 | dev_warn(priv->dev, "Failed to switch OTG mode\n"); | |
559 | } | |
560 | ||
013af227 NA |
561 | regmap_update_bits(priv->usb_glue_regmap, USB_R5, |
562 | USB_R5_ID_DIG_IRQ, 0); | |
f90db107 NA |
563 | |
564 | return IRQ_HANDLED; | |
565 | } | |
566 | ||
c9999337 NA |
567 | static struct device *dwc3_meson_g12_find_child(struct device *dev, |
568 | const char *compatible) | |
569 | { | |
570 | struct platform_device *pdev; | |
571 | struct device_node *np; | |
572 | ||
573 | np = of_get_compatible_child(dev->of_node, compatible); | |
574 | if (!np) | |
575 | return NULL; | |
576 | ||
577 | pdev = of_find_device_by_node(np); | |
578 | of_node_put(np); | |
579 | if (!pdev) | |
580 | return NULL; | |
581 | ||
582 | return &pdev->dev; | |
583 | } | |
584 | ||
1e355f21 HL |
585 | static int dwc3_meson_g12a_otg_init(struct platform_device *pdev, |
586 | struct dwc3_meson_g12a *priv) | |
587 | { | |
588 | enum phy_mode otg_id; | |
589 | int ret, irq; | |
590 | struct device *dev = &pdev->dev; | |
591 | ||
592 | if (!priv->drvdata->otg_switch_supported) | |
593 | return 0; | |
594 | ||
595 | if (priv->otg_mode == USB_DR_MODE_OTG) { | |
596 | /* Ack irq before registering */ | |
013af227 | 597 | regmap_update_bits(priv->usb_glue_regmap, USB_R5, |
1e355f21 HL |
598 | USB_R5_ID_DIG_IRQ, 0); |
599 | ||
600 | irq = platform_get_irq(pdev, 0); | |
baa2986b SS |
601 | if (irq < 0) |
602 | return irq; | |
1e355f21 HL |
603 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, |
604 | dwc3_meson_g12a_irq_thread, | |
605 | IRQF_ONESHOT, pdev->name, priv); | |
606 | if (ret) | |
607 | return ret; | |
608 | } | |
609 | ||
610 | /* Setup OTG mode corresponding to the ID pin */ | |
611 | if (priv->otg_mode == USB_DR_MODE_OTG) { | |
612 | otg_id = dwc3_meson_g12a_get_id(priv); | |
613 | if (otg_id != priv->otg_phy_mode) { | |
614 | if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) | |
615 | dev_warn(dev, "Failed to switch OTG mode\n"); | |
616 | } | |
617 | } | |
618 | ||
619 | /* Setup role switcher */ | |
620 | priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, | |
621 | "snps,dwc3"); | |
622 | priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); | |
623 | priv->switch_desc.allow_userspace_control = true; | |
624 | priv->switch_desc.set = dwc3_meson_g12a_role_set; | |
625 | priv->switch_desc.get = dwc3_meson_g12a_role_get; | |
a8ab3e76 | 626 | priv->switch_desc.driver_data = priv; |
1e355f21 HL |
627 | |
628 | priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); | |
629 | if (IS_ERR(priv->role_switch)) | |
630 | dev_warn(dev, "Unable to register Role Switch\n"); | |
631 | ||
238d7602 | 632 | return 0; |
1e355f21 HL |
633 | } |
634 | ||
a9fc15e0 NA |
635 | static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv, |
636 | void __iomem *base) | |
637 | { | |
638 | /* GXL controls the PHY mode in the PHY registers unlike G12A */ | |
639 | priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base, | |
640 | &phy_meson_g12a_usb_glue_regmap_conf); | |
a793cf81 | 641 | return PTR_ERR_OR_ZERO(priv->usb_glue_regmap); |
a9fc15e0 NA |
642 | } |
643 | ||
013af227 NA |
644 | static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv, |
645 | void __iomem *base) | |
646 | { | |
647 | int i; | |
648 | ||
649 | priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, | |
650 | base + G12A_GLUE_OFFSET, | |
651 | &phy_meson_g12a_usb_glue_regmap_conf); | |
652 | if (IS_ERR(priv->usb_glue_regmap)) | |
653 | return PTR_ERR(priv->usb_glue_regmap); | |
654 | ||
655 | /* Create a regmap for each USB2 PHY control register set */ | |
4d2aa178 | 656 | for (i = 0; i < priv->drvdata->num_phys; i++) { |
013af227 NA |
657 | struct regmap_config u2p_regmap_config = { |
658 | .reg_bits = 8, | |
659 | .val_bits = 32, | |
660 | .reg_stride = 4, | |
661 | .max_register = U2P_R1, | |
662 | }; | |
663 | ||
4d2aa178 NA |
664 | if (!strstr(priv->drvdata->phy_names[i], "usb2")) |
665 | continue; | |
666 | ||
013af227 NA |
667 | u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL, |
668 | "u2p-%d", i); | |
669 | if (!u2p_regmap_config.name) | |
670 | return -ENOMEM; | |
671 | ||
672 | priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev, | |
673 | base + (i * U2P_REG_SIZE), | |
674 | &u2p_regmap_config); | |
675 | if (IS_ERR(priv->u2p_regmap[i])) | |
676 | return PTR_ERR(priv->u2p_regmap[i]); | |
677 | } | |
678 | ||
679 | return 0; | |
680 | } | |
681 | ||
5b0ba0ca NA |
682 | static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) |
683 | { | |
684 | return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode); | |
685 | } | |
686 | ||
a9fc15e0 NA |
687 | static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv) |
688 | { | |
689 | return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE); | |
690 | } | |
691 | ||
692 | static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv) | |
693 | { | |
694 | int ret; | |
695 | ||
696 | ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, | |
697 | priv->otg_phy_mode); | |
698 | if (ret) | |
699 | return ret; | |
700 | ||
701 | dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode); | |
702 | ||
703 | return 0; | |
704 | } | |
705 | ||
c9999337 NA |
706 | static int dwc3_meson_g12a_probe(struct platform_device *pdev) |
707 | { | |
708 | struct dwc3_meson_g12a *priv; | |
709 | struct device *dev = &pdev->dev; | |
710 | struct device_node *np = dev->of_node; | |
711 | void __iomem *base; | |
1e355f21 | 712 | int ret, i; |
c9999337 NA |
713 | |
714 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
715 | if (!priv) | |
716 | return -ENOMEM; | |
717 | ||
c6e4999c | 718 | base = devm_platform_ioremap_resource(pdev, 0); |
c9999337 NA |
719 | if (IS_ERR(base)) |
720 | return PTR_ERR(base); | |
721 | ||
013af227 | 722 | priv->drvdata = of_device_get_match_data(&pdev->dev); |
013af227 | 723 | priv->dev = dev; |
c9999337 NA |
724 | |
725 | priv->vbus = devm_regulator_get_optional(dev, "vbus"); | |
726 | if (IS_ERR(priv->vbus)) { | |
727 | if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) | |
728 | return PTR_ERR(priv->vbus); | |
729 | priv->vbus = NULL; | |
730 | } | |
731 | ||
1e355f21 HL |
732 | ret = devm_clk_bulk_get(dev, |
733 | priv->drvdata->num_clks, | |
734 | priv->drvdata->clks); | |
c9999337 NA |
735 | if (ret) |
736 | return ret; | |
737 | ||
1e355f21 HL |
738 | ret = clk_bulk_prepare_enable(priv->drvdata->num_clks, |
739 | priv->drvdata->clks); | |
740 | if (ret) | |
741 | return ret; | |
c9999337 NA |
742 | |
743 | platform_set_drvdata(pdev, priv); | |
c9999337 | 744 | |
6d9fa35a | 745 | priv->reset = devm_reset_control_get_shared(dev, NULL); |
c9999337 NA |
746 | if (IS_ERR(priv->reset)) { |
747 | ret = PTR_ERR(priv->reset); | |
748 | dev_err(dev, "failed to get device reset, err=%d\n", ret); | |
be8c1001 | 749 | goto err_disable_clks; |
c9999337 NA |
750 | } |
751 | ||
a6498d51 | 752 | ret = reset_control_reset(priv->reset); |
c9999337 | 753 | if (ret) |
a6498d51 | 754 | goto err_disable_clks; |
c9999337 NA |
755 | |
756 | ret = dwc3_meson_g12a_get_phys(priv); | |
757 | if (ret) | |
45c5a729 | 758 | goto err_rearm; |
c9999337 | 759 | |
347052e3 MB |
760 | ret = priv->drvdata->setup_regmaps(priv, base); |
761 | if (ret) | |
45c5a729 | 762 | goto err_rearm; |
347052e3 | 763 | |
c9999337 NA |
764 | if (priv->vbus) { |
765 | ret = regulator_enable(priv->vbus); | |
766 | if (ret) | |
45c5a729 | 767 | goto err_rearm; |
c9999337 NA |
768 | } |
769 | ||
770 | /* Get dr_mode */ | |
771 | priv->otg_mode = usb_get_dr_mode(dev); | |
772 | ||
5b0ba0ca NA |
773 | if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) |
774 | priv->otg_phy_mode = PHY_MODE_USB_DEVICE; | |
775 | else | |
776 | priv->otg_phy_mode = PHY_MODE_USB_HOST; | |
777 | ||
778 | ret = priv->drvdata->usb_init(priv); | |
8f5bc1ec | 779 | if (ret) |
1d0d3d81 | 780 | goto err_disable_regulator; |
c9999337 NA |
781 | |
782 | /* Init PHYs */ | |
783 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
784 | ret = phy_init(priv->phys[i]); | |
785 | if (ret) | |
1d0d3d81 | 786 | goto err_disable_regulator; |
c9999337 NA |
787 | } |
788 | ||
789 | /* Set PHY Power */ | |
790 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
791 | ret = phy_power_on(priv->phys[i]); | |
792 | if (ret) | |
793 | goto err_phys_exit; | |
794 | } | |
795 | ||
5b0ba0ca NA |
796 | if (priv->drvdata->usb_post_init) { |
797 | ret = priv->drvdata->usb_post_init(priv); | |
798 | if (ret) | |
799 | goto err_phys_power; | |
800 | } | |
801 | ||
c9999337 | 802 | ret = of_platform_populate(np, NULL, NULL, dev); |
1e355f21 | 803 | if (ret) |
c9999337 | 804 | goto err_phys_power; |
c9999337 | 805 | |
1e355f21 HL |
806 | ret = dwc3_meson_g12a_otg_init(pdev, priv); |
807 | if (ret) | |
808 | goto err_phys_power; | |
c9999337 NA |
809 | |
810 | pm_runtime_set_active(dev); | |
811 | pm_runtime_enable(dev); | |
812 | pm_runtime_get_sync(dev); | |
813 | ||
814 | return 0; | |
815 | ||
816 | err_phys_power: | |
817 | for (i = 0 ; i < PHY_COUNT ; ++i) | |
818 | phy_power_off(priv->phys[i]); | |
819 | ||
820 | err_phys_exit: | |
821 | for (i = 0 ; i < PHY_COUNT ; ++i) | |
822 | phy_exit(priv->phys[i]); | |
823 | ||
1d0d3d81 CJ |
824 | err_disable_regulator: |
825 | if (priv->vbus) | |
826 | regulator_disable(priv->vbus); | |
827 | ||
45c5a729 AOA |
828 | err_rearm: |
829 | reset_control_rearm(priv->reset); | |
830 | ||
1e355f21 HL |
831 | err_disable_clks: |
832 | clk_bulk_disable_unprepare(priv->drvdata->num_clks, | |
833 | priv->drvdata->clks); | |
834 | ||
c9999337 NA |
835 | return ret; |
836 | } | |
837 | ||
838 | static int dwc3_meson_g12a_remove(struct platform_device *pdev) | |
839 | { | |
840 | struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); | |
841 | struct device *dev = &pdev->dev; | |
842 | int i; | |
843 | ||
1e355f21 HL |
844 | if (priv->drvdata->otg_switch_supported) |
845 | usb_role_switch_unregister(priv->role_switch); | |
c9999337 NA |
846 | |
847 | of_platform_depopulate(dev); | |
848 | ||
849 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
850 | phy_power_off(priv->phys[i]); | |
851 | phy_exit(priv->phys[i]); | |
852 | } | |
853 | ||
854 | pm_runtime_disable(dev); | |
855 | pm_runtime_put_noidle(dev); | |
856 | pm_runtime_set_suspended(dev); | |
857 | ||
45c5a729 AOA |
858 | reset_control_rearm(priv->reset); |
859 | ||
1e355f21 HL |
860 | clk_bulk_disable_unprepare(priv->drvdata->num_clks, |
861 | priv->drvdata->clks); | |
862 | ||
c9999337 NA |
863 | return 0; |
864 | } | |
865 | ||
866 | static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) | |
867 | { | |
868 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
869 | ||
1e355f21 HL |
870 | clk_bulk_disable_unprepare(priv->drvdata->num_clks, |
871 | priv->drvdata->clks); | |
c9999337 NA |
872 | |
873 | return 0; | |
874 | } | |
875 | ||
876 | static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) | |
877 | { | |
878 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
879 | ||
1e355f21 HL |
880 | return clk_bulk_prepare_enable(priv->drvdata->num_clks, |
881 | priv->drvdata->clks); | |
c9999337 NA |
882 | } |
883 | ||
884 | static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) | |
885 | { | |
886 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
1cf084d1 NA |
887 | int i, ret; |
888 | ||
889 | if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { | |
890 | ret = regulator_disable(priv->vbus); | |
891 | if (ret) | |
892 | return ret; | |
893 | } | |
c9999337 NA |
894 | |
895 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
896 | phy_power_off(priv->phys[i]); | |
897 | phy_exit(priv->phys[i]); | |
898 | } | |
899 | ||
45c5a729 | 900 | reset_control_rearm(priv->reset); |
c9999337 NA |
901 | |
902 | return 0; | |
903 | } | |
904 | ||
905 | static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) | |
906 | { | |
907 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
908 | int i, ret; | |
909 | ||
45c5a729 AOA |
910 | ret = reset_control_reset(priv->reset); |
911 | if (ret) | |
912 | return ret; | |
c9999337 | 913 | |
5b0ba0ca NA |
914 | ret = priv->drvdata->usb_init(priv); |
915 | if (ret) | |
916 | return ret; | |
c9999337 NA |
917 | |
918 | /* Init PHYs */ | |
919 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
920 | ret = phy_init(priv->phys[i]); | |
921 | if (ret) | |
922 | return ret; | |
923 | } | |
924 | ||
925 | /* Set PHY Power */ | |
926 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
927 | ret = phy_power_on(priv->phys[i]); | |
928 | if (ret) | |
929 | return ret; | |
930 | } | |
931 | ||
e5ee93d4 FB |
932 | if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { |
933 | ret = regulator_enable(priv->vbus); | |
1cf084d1 NA |
934 | if (ret) |
935 | return ret; | |
936 | } | |
937 | ||
c9999337 NA |
938 | return 0; |
939 | } | |
940 | ||
941 | static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { | |
942 | SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) | |
943 | SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, | |
944 | dwc3_meson_g12a_runtime_resume, NULL) | |
945 | }; | |
946 | ||
947 | static const struct of_device_id dwc3_meson_g12a_match[] = { | |
a9fc15e0 NA |
948 | { |
949 | .compatible = "amlogic,meson-gxl-usb-ctrl", | |
950 | .data = &gxl_drvdata, | |
951 | }, | |
952 | { | |
953 | .compatible = "amlogic,meson-gxm-usb-ctrl", | |
954 | .data = &gxm_drvdata, | |
955 | }, | |
65f3d449 NA |
956 | { |
957 | .compatible = "amlogic,meson-axg-usb-ctrl", | |
958 | .data = &axg_drvdata, | |
959 | }, | |
1e355f21 HL |
960 | { |
961 | .compatible = "amlogic,meson-g12a-usb-ctrl", | |
962 | .data = &g12a_drvdata, | |
963 | }, | |
964 | { | |
965 | .compatible = "amlogic,meson-a1-usb-ctrl", | |
966 | .data = &a1_drvdata, | |
967 | }, | |
c9999337 NA |
968 | { /* Sentinel */ } |
969 | }; | |
970 | MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); | |
971 | ||
972 | static struct platform_driver dwc3_meson_g12a_driver = { | |
973 | .probe = dwc3_meson_g12a_probe, | |
974 | .remove = dwc3_meson_g12a_remove, | |
975 | .driver = { | |
976 | .name = "dwc3-meson-g12a", | |
977 | .of_match_table = dwc3_meson_g12a_match, | |
978 | .pm = &dwc3_meson_g12a_dev_pm_ops, | |
979 | }, | |
980 | }; | |
981 | ||
982 | module_platform_driver(dwc3_meson_g12a_driver); | |
983 | MODULE_LICENSE("GPL v2"); | |
984 | MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); | |
985 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); |