]>
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); | |
601 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | |
602 | dwc3_meson_g12a_irq_thread, | |
603 | IRQF_ONESHOT, pdev->name, priv); | |
604 | if (ret) | |
605 | return ret; | |
606 | } | |
607 | ||
608 | /* Setup OTG mode corresponding to the ID pin */ | |
609 | if (priv->otg_mode == USB_DR_MODE_OTG) { | |
610 | otg_id = dwc3_meson_g12a_get_id(priv); | |
611 | if (otg_id != priv->otg_phy_mode) { | |
612 | if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) | |
613 | dev_warn(dev, "Failed to switch OTG mode\n"); | |
614 | } | |
615 | } | |
616 | ||
617 | /* Setup role switcher */ | |
618 | priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, | |
619 | "snps,dwc3"); | |
620 | priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); | |
621 | priv->switch_desc.allow_userspace_control = true; | |
622 | priv->switch_desc.set = dwc3_meson_g12a_role_set; | |
623 | priv->switch_desc.get = dwc3_meson_g12a_role_get; | |
a8ab3e76 | 624 | priv->switch_desc.driver_data = priv; |
1e355f21 HL |
625 | |
626 | priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); | |
627 | if (IS_ERR(priv->role_switch)) | |
628 | dev_warn(dev, "Unable to register Role Switch\n"); | |
629 | ||
238d7602 | 630 | return 0; |
1e355f21 HL |
631 | } |
632 | ||
a9fc15e0 NA |
633 | static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv, |
634 | void __iomem *base) | |
635 | { | |
636 | /* GXL controls the PHY mode in the PHY registers unlike G12A */ | |
637 | priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base, | |
638 | &phy_meson_g12a_usb_glue_regmap_conf); | |
a793cf81 | 639 | return PTR_ERR_OR_ZERO(priv->usb_glue_regmap); |
a9fc15e0 NA |
640 | } |
641 | ||
013af227 NA |
642 | static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv, |
643 | void __iomem *base) | |
644 | { | |
645 | int i; | |
646 | ||
647 | priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, | |
648 | base + G12A_GLUE_OFFSET, | |
649 | &phy_meson_g12a_usb_glue_regmap_conf); | |
650 | if (IS_ERR(priv->usb_glue_regmap)) | |
651 | return PTR_ERR(priv->usb_glue_regmap); | |
652 | ||
653 | /* Create a regmap for each USB2 PHY control register set */ | |
654 | for (i = 0; i < priv->usb2_ports; i++) { | |
655 | struct regmap_config u2p_regmap_config = { | |
656 | .reg_bits = 8, | |
657 | .val_bits = 32, | |
658 | .reg_stride = 4, | |
659 | .max_register = U2P_R1, | |
660 | }; | |
661 | ||
662 | u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL, | |
663 | "u2p-%d", i); | |
664 | if (!u2p_regmap_config.name) | |
665 | return -ENOMEM; | |
666 | ||
667 | priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev, | |
668 | base + (i * U2P_REG_SIZE), | |
669 | &u2p_regmap_config); | |
670 | if (IS_ERR(priv->u2p_regmap[i])) | |
671 | return PTR_ERR(priv->u2p_regmap[i]); | |
672 | } | |
673 | ||
674 | return 0; | |
675 | } | |
676 | ||
5b0ba0ca NA |
677 | static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) |
678 | { | |
679 | return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode); | |
680 | } | |
681 | ||
a9fc15e0 NA |
682 | static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv) |
683 | { | |
684 | return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE); | |
685 | } | |
686 | ||
687 | static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv) | |
688 | { | |
689 | int ret; | |
690 | ||
691 | ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, | |
692 | priv->otg_phy_mode); | |
693 | if (ret) | |
694 | return ret; | |
695 | ||
696 | dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode); | |
697 | ||
698 | return 0; | |
699 | } | |
700 | ||
c9999337 NA |
701 | static int dwc3_meson_g12a_probe(struct platform_device *pdev) |
702 | { | |
703 | struct dwc3_meson_g12a *priv; | |
704 | struct device *dev = &pdev->dev; | |
705 | struct device_node *np = dev->of_node; | |
706 | void __iomem *base; | |
1e355f21 | 707 | int ret, i; |
c9999337 NA |
708 | |
709 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
710 | if (!priv) | |
711 | return -ENOMEM; | |
712 | ||
c6e4999c | 713 | base = devm_platform_ioremap_resource(pdev, 0); |
c9999337 NA |
714 | if (IS_ERR(base)) |
715 | return PTR_ERR(base); | |
716 | ||
013af227 | 717 | priv->drvdata = of_device_get_match_data(&pdev->dev); |
013af227 | 718 | priv->dev = dev; |
c9999337 NA |
719 | |
720 | priv->vbus = devm_regulator_get_optional(dev, "vbus"); | |
721 | if (IS_ERR(priv->vbus)) { | |
722 | if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) | |
723 | return PTR_ERR(priv->vbus); | |
724 | priv->vbus = NULL; | |
725 | } | |
726 | ||
1e355f21 HL |
727 | ret = devm_clk_bulk_get(dev, |
728 | priv->drvdata->num_clks, | |
729 | priv->drvdata->clks); | |
c9999337 NA |
730 | if (ret) |
731 | return ret; | |
732 | ||
1e355f21 HL |
733 | ret = clk_bulk_prepare_enable(priv->drvdata->num_clks, |
734 | priv->drvdata->clks); | |
735 | if (ret) | |
736 | return ret; | |
c9999337 NA |
737 | |
738 | platform_set_drvdata(pdev, priv); | |
c9999337 | 739 | |
6d9fa35a | 740 | priv->reset = devm_reset_control_get_shared(dev, NULL); |
c9999337 NA |
741 | if (IS_ERR(priv->reset)) { |
742 | ret = PTR_ERR(priv->reset); | |
743 | dev_err(dev, "failed to get device reset, err=%d\n", ret); | |
be8c1001 | 744 | goto err_disable_clks; |
c9999337 NA |
745 | } |
746 | ||
a6498d51 | 747 | ret = reset_control_reset(priv->reset); |
c9999337 | 748 | if (ret) |
a6498d51 | 749 | goto err_disable_clks; |
c9999337 NA |
750 | |
751 | ret = dwc3_meson_g12a_get_phys(priv); | |
752 | if (ret) | |
a6498d51 | 753 | goto err_disable_clks; |
c9999337 | 754 | |
347052e3 MB |
755 | ret = priv->drvdata->setup_regmaps(priv, base); |
756 | if (ret) | |
a5ada3df | 757 | goto err_disable_clks; |
347052e3 | 758 | |
c9999337 NA |
759 | if (priv->vbus) { |
760 | ret = regulator_enable(priv->vbus); | |
761 | if (ret) | |
a6498d51 | 762 | goto err_disable_clks; |
c9999337 NA |
763 | } |
764 | ||
765 | /* Get dr_mode */ | |
766 | priv->otg_mode = usb_get_dr_mode(dev); | |
767 | ||
5b0ba0ca NA |
768 | if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) |
769 | priv->otg_phy_mode = PHY_MODE_USB_DEVICE; | |
770 | else | |
771 | priv->otg_phy_mode = PHY_MODE_USB_HOST; | |
772 | ||
773 | ret = priv->drvdata->usb_init(priv); | |
8f5bc1ec | 774 | if (ret) |
a6498d51 | 775 | goto err_disable_clks; |
c9999337 NA |
776 | |
777 | /* Init PHYs */ | |
778 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
779 | ret = phy_init(priv->phys[i]); | |
780 | if (ret) | |
a6498d51 | 781 | goto err_disable_clks; |
c9999337 NA |
782 | } |
783 | ||
784 | /* Set PHY Power */ | |
785 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
786 | ret = phy_power_on(priv->phys[i]); | |
787 | if (ret) | |
788 | goto err_phys_exit; | |
789 | } | |
790 | ||
5b0ba0ca NA |
791 | if (priv->drvdata->usb_post_init) { |
792 | ret = priv->drvdata->usb_post_init(priv); | |
793 | if (ret) | |
794 | goto err_phys_power; | |
795 | } | |
796 | ||
c9999337 | 797 | ret = of_platform_populate(np, NULL, NULL, dev); |
1e355f21 | 798 | if (ret) |
c9999337 | 799 | goto err_phys_power; |
c9999337 | 800 | |
1e355f21 HL |
801 | ret = dwc3_meson_g12a_otg_init(pdev, priv); |
802 | if (ret) | |
803 | goto err_phys_power; | |
c9999337 NA |
804 | |
805 | pm_runtime_set_active(dev); | |
806 | pm_runtime_enable(dev); | |
807 | pm_runtime_get_sync(dev); | |
808 | ||
809 | return 0; | |
810 | ||
811 | err_phys_power: | |
812 | for (i = 0 ; i < PHY_COUNT ; ++i) | |
813 | phy_power_off(priv->phys[i]); | |
814 | ||
815 | err_phys_exit: | |
816 | for (i = 0 ; i < PHY_COUNT ; ++i) | |
817 | phy_exit(priv->phys[i]); | |
818 | ||
1e355f21 HL |
819 | err_disable_clks: |
820 | clk_bulk_disable_unprepare(priv->drvdata->num_clks, | |
821 | priv->drvdata->clks); | |
822 | ||
c9999337 NA |
823 | return ret; |
824 | } | |
825 | ||
826 | static int dwc3_meson_g12a_remove(struct platform_device *pdev) | |
827 | { | |
828 | struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); | |
829 | struct device *dev = &pdev->dev; | |
830 | int i; | |
831 | ||
1e355f21 HL |
832 | if (priv->drvdata->otg_switch_supported) |
833 | usb_role_switch_unregister(priv->role_switch); | |
c9999337 NA |
834 | |
835 | of_platform_depopulate(dev); | |
836 | ||
837 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
838 | phy_power_off(priv->phys[i]); | |
839 | phy_exit(priv->phys[i]); | |
840 | } | |
841 | ||
842 | pm_runtime_disable(dev); | |
843 | pm_runtime_put_noidle(dev); | |
844 | pm_runtime_set_suspended(dev); | |
845 | ||
1e355f21 HL |
846 | clk_bulk_disable_unprepare(priv->drvdata->num_clks, |
847 | priv->drvdata->clks); | |
848 | ||
c9999337 NA |
849 | return 0; |
850 | } | |
851 | ||
852 | static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) | |
853 | { | |
854 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
855 | ||
1e355f21 HL |
856 | clk_bulk_disable_unprepare(priv->drvdata->num_clks, |
857 | priv->drvdata->clks); | |
c9999337 NA |
858 | |
859 | return 0; | |
860 | } | |
861 | ||
862 | static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) | |
863 | { | |
864 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
865 | ||
1e355f21 HL |
866 | return clk_bulk_prepare_enable(priv->drvdata->num_clks, |
867 | priv->drvdata->clks); | |
c9999337 NA |
868 | } |
869 | ||
870 | static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) | |
871 | { | |
872 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
1cf084d1 NA |
873 | int i, ret; |
874 | ||
875 | if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { | |
876 | ret = regulator_disable(priv->vbus); | |
877 | if (ret) | |
878 | return ret; | |
879 | } | |
c9999337 NA |
880 | |
881 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
882 | phy_power_off(priv->phys[i]); | |
883 | phy_exit(priv->phys[i]); | |
884 | } | |
885 | ||
886 | reset_control_assert(priv->reset); | |
887 | ||
888 | return 0; | |
889 | } | |
890 | ||
891 | static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) | |
892 | { | |
893 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); | |
894 | int i, ret; | |
895 | ||
896 | reset_control_deassert(priv->reset); | |
897 | ||
5b0ba0ca NA |
898 | ret = priv->drvdata->usb_init(priv); |
899 | if (ret) | |
900 | return ret; | |
c9999337 NA |
901 | |
902 | /* Init PHYs */ | |
903 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
904 | ret = phy_init(priv->phys[i]); | |
905 | if (ret) | |
906 | return ret; | |
907 | } | |
908 | ||
909 | /* Set PHY Power */ | |
910 | for (i = 0 ; i < PHY_COUNT ; ++i) { | |
911 | ret = phy_power_on(priv->phys[i]); | |
912 | if (ret) | |
913 | return ret; | |
914 | } | |
915 | ||
e5ee93d4 FB |
916 | if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { |
917 | ret = regulator_enable(priv->vbus); | |
1cf084d1 NA |
918 | if (ret) |
919 | return ret; | |
920 | } | |
921 | ||
c9999337 NA |
922 | return 0; |
923 | } | |
924 | ||
925 | static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { | |
926 | SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) | |
927 | SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, | |
928 | dwc3_meson_g12a_runtime_resume, NULL) | |
929 | }; | |
930 | ||
931 | static const struct of_device_id dwc3_meson_g12a_match[] = { | |
a9fc15e0 NA |
932 | { |
933 | .compatible = "amlogic,meson-gxl-usb-ctrl", | |
934 | .data = &gxl_drvdata, | |
935 | }, | |
936 | { | |
937 | .compatible = "amlogic,meson-gxm-usb-ctrl", | |
938 | .data = &gxm_drvdata, | |
939 | }, | |
65f3d449 NA |
940 | { |
941 | .compatible = "amlogic,meson-axg-usb-ctrl", | |
942 | .data = &axg_drvdata, | |
943 | }, | |
1e355f21 HL |
944 | { |
945 | .compatible = "amlogic,meson-g12a-usb-ctrl", | |
946 | .data = &g12a_drvdata, | |
947 | }, | |
948 | { | |
949 | .compatible = "amlogic,meson-a1-usb-ctrl", | |
950 | .data = &a1_drvdata, | |
951 | }, | |
c9999337 NA |
952 | { /* Sentinel */ } |
953 | }; | |
954 | MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); | |
955 | ||
956 | static struct platform_driver dwc3_meson_g12a_driver = { | |
957 | .probe = dwc3_meson_g12a_probe, | |
958 | .remove = dwc3_meson_g12a_remove, | |
959 | .driver = { | |
960 | .name = "dwc3-meson-g12a", | |
961 | .of_match_table = dwc3_meson_g12a_match, | |
962 | .pm = &dwc3_meson_g12a_dev_pm_ops, | |
963 | }, | |
964 | }; | |
965 | ||
966 | module_platform_driver(dwc3_meson_g12a_driver); | |
967 | MODULE_LICENSE("GPL v2"); | |
968 | MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); | |
969 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); |