aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Armstrong <narmstrong@baylibre.com>2019-04-23 04:51:27 -0400
committerFelipe Balbi <felipe.balbi@linux.intel.com>2019-05-03 02:13:47 -0400
commitc99993376f72ca3dcc989813512607c6435cbed8 (patch)
tree49ecef680ba90f0ae48a935bdefb6b6641471f4c
parentfc4e326ee72cc36c942333c65d851247b31c567b (diff)
usb: dwc3: Add Amlogic G12A DWC3 glue
Adds support for Amlogic G12A USB Control Glue HW. The Amlogic G12A SoC Family embeds 2 USB Controllers : - a DWC3 IP configured as Host for USB2 and USB3 - a DWC2 IP configured as Peripheral USB2 Only A glue connects these both controllers to 2 USB2 PHYs, and optionnally to an USB3+PCIE Combo PHY shared with the PCIE controller. The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including routing of the OTG PHY between the DWC3 and DWC2 controllers, and setups the on-chip OTG mode selection for this PHY. This drivers supports the on-probe setup of the OTG mode, and manually via a debugfs interface. The IRQ mode change detect is yet to be added in a future patchset, mainly due to lack of hardware to validate on. Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
-rw-r--r--drivers/usb/dwc3/Kconfig10
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c604
3 files changed, 615 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 784309435916..4a62045cc812 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -96,6 +96,16 @@ config USB_DWC3_KEYSTONE
96 Support of USB2/3 functionality in TI Keystone2 and AM654 platforms. 96 Support of USB2/3 functionality in TI Keystone2 and AM654 platforms.
97 Say 'Y' or 'M' here if you have one such device 97 Say 'Y' or 'M' here if you have one such device
98 98
99config USB_DWC3_MESON_G12A
100 tristate "Amlogic Meson G12A Platforms"
101 depends on OF && COMMON_CLK
102 depends on ARCH_MESON || COMPILE_TEST
103 default USB_DWC3
104 select USB_ROLE_SWITCH
105 help
106 Support USB2/3 functionality in Amlogic G12A platforms.
107 Say 'Y' or 'M' if you have one such device.
108
99config USB_DWC3_OF_SIMPLE 109config USB_DWC3_OF_SIMPLE
100 tristate "Generic OF Simple Glue Layer" 110 tristate "Generic OF Simple Glue Layer"
101 depends on OF && COMMON_CLK 111 depends on OF && COMMON_CLK
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 6e3ef6144e5d..ae86da0dc5bd 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
47obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o 47obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
48obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o 48obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o
49obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o 49obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
50obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
50obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o 51obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
51obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o 52obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
52obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o 53obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
new file mode 100644
index 000000000000..2aec31a2eacb
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -0,0 +1,604 @@
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
14 *
15 * TOFIX:
16 * - Add dynamic OTG switching with ID change interrupt
17 */
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/platform_device.h>
22#include <linux/clk.h>
23#include <linux/of.h>
24#include <linux/of_platform.h>
25#include <linux/pm_runtime.h>
26#include <linux/regmap.h>
27#include <linux/bitfield.h>
28#include <linux/bitops.h>
29#include <linux/reset.h>
30#include <linux/phy/phy.h>
31#include <linux/usb/otg.h>
32#include <linux/usb/role.h>
33#include <linux/regulator/consumer.h>
34
35/* USB2 Ports Control Registers */
36
37#define U2P_REG_SIZE 0x20
38
39#define U2P_R0 0x0
40 #define U2P_R0_HOST_DEVICE BIT(0)
41 #define U2P_R0_POWER_OK BIT(1)
42 #define U2P_R0_HAST_MODE BIT(2)
43 #define U2P_R0_POWER_ON_RESET BIT(3)
44 #define U2P_R0_ID_PULLUP BIT(4)
45 #define U2P_R0_DRV_VBUS BIT(5)
46
47#define U2P_R1 0x4
48 #define U2P_R1_PHY_READY BIT(0)
49 #define U2P_R1_ID_DIG BIT(1)
50 #define U2P_R1_OTG_SESSION_VALID BIT(2)
51 #define U2P_R1_VBUS_VALID BIT(3)
52
53/* USB Glue Control Registers */
54
55#define USB_R0 0x80
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
62#define USB_R1 0x84
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
74#define USB_R2 0x88
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
78#define USB_R3 0x8c
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
84#define USB_R4 0x90
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
90#define USB_R5 0x94
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
101enum {
102 USB2_HOST_PHY = 0,
103 USB2_OTG_PHY,
104 USB3_HOST_PHY,
105 PHY_COUNT,
106};
107
108static const char *phy_names[PHY_COUNT] = {
109 "usb2-phy0", "usb2-phy1", "usb3-phy0",
110};
111
112struct dwc3_meson_g12a {
113 struct device *dev;
114 struct regmap *regmap;
115 struct clk *clk;
116 struct reset_control *reset;
117 struct phy *phys[PHY_COUNT];
118 enum usb_dr_mode otg_mode;
119 enum phy_mode otg_phy_mode;
120 unsigned int usb2_ports;
121 unsigned int usb3_ports;
122 struct regulator *vbus;
123 struct usb_role_switch_desc switch_desc;
124 struct usb_role_switch *role_switch;
125};
126
127static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
128 int i, enum phy_mode mode)
129{
130 if (mode == PHY_MODE_USB_HOST)
131 regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
132 U2P_R0_HOST_DEVICE,
133 U2P_R0_HOST_DEVICE);
134 else
135 regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
136 U2P_R0_HOST_DEVICE, 0);
137}
138
139static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
140{
141 int i;
142
143 if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
144 priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
145 else
146 priv->otg_phy_mode = PHY_MODE_USB_HOST;
147
148 for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
149 if (!priv->phys[i])
150 continue;
151
152 regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
153 U2P_R0_POWER_ON_RESET,
154 U2P_R0_POWER_ON_RESET);
155
156 if (i == USB2_OTG_PHY) {
157 regmap_update_bits(priv->regmap,
158 U2P_R0 + (U2P_REG_SIZE * i),
159 U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
160 U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
161
162 dwc3_meson_g12a_usb2_set_mode(priv, i,
163 priv->otg_phy_mode);
164 } else
165 dwc3_meson_g12a_usb2_set_mode(priv, i,
166 PHY_MODE_USB_HOST);
167
168 regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
169 U2P_R0_POWER_ON_RESET, 0);
170 }
171
172 return 0;
173}
174
175static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
176{
177 regmap_update_bits(priv->regmap, USB_R3,
178 USB_R3_P30_SSC_RANGE_MASK |
179 USB_R3_P30_REF_SSP_EN,
180 USB_R3_P30_SSC_ENABLE |
181 FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
182 USB_R3_P30_REF_SSP_EN);
183 udelay(2);
184
185 regmap_update_bits(priv->regmap, USB_R2,
186 USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
187 FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
188
189 regmap_update_bits(priv->regmap, USB_R2,
190 USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
191 FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
192
193 udelay(2);
194
195 regmap_update_bits(priv->regmap, USB_R1,
196 USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
197 USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
198
199 regmap_update_bits(priv->regmap, USB_R1,
200 USB_R1_P30_PCS_TX_SWING_FULL_MASK,
201 FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
202}
203
204static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
205{
206 if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
207 regmap_update_bits(priv->regmap, USB_R0,
208 USB_R0_U2D_ACT, USB_R0_U2D_ACT);
209 regmap_update_bits(priv->regmap, USB_R0,
210 USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
211 regmap_update_bits(priv->regmap, USB_R4,
212 USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
213 } else {
214 regmap_update_bits(priv->regmap, USB_R0,
215 USB_R0_U2D_ACT, 0);
216 regmap_update_bits(priv->regmap, USB_R4,
217 USB_R4_P21_SLEEP_M0, 0);
218 }
219}
220
221static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
222{
223 int ret;
224
225 ret = dwc3_meson_g12a_usb2_init(priv);
226 if (ret)
227 return ret;
228
229 regmap_update_bits(priv->regmap, USB_R1,
230 USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
231 FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
232
233 regmap_update_bits(priv->regmap, USB_R5,
234 USB_R5_ID_DIG_EN_0,
235 USB_R5_ID_DIG_EN_0);
236 regmap_update_bits(priv->regmap, USB_R5,
237 USB_R5_ID_DIG_EN_1,
238 USB_R5_ID_DIG_EN_1);
239 regmap_update_bits(priv->regmap, USB_R5,
240 USB_R5_ID_DIG_TH_MASK,
241 FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
242
243 /* If we have an actual SuperSpeed port, initialize it */
244 if (priv->usb3_ports)
245 dwc3_meson_g12a_usb3_init(priv);
246
247 dwc3_meson_g12a_usb_otg_apply_mode(priv);
248
249 return 0;
250}
251
252static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
253 .reg_bits = 8,
254 .val_bits = 32,
255 .reg_stride = 4,
256 .max_register = USB_R5,
257};
258
259static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
260{
261 int i;
262
263 for (i = 0 ; i < PHY_COUNT ; ++i) {
264 priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
265 if (!priv->phys[i])
266 continue;
267
268 if (IS_ERR(priv->phys[i]))
269 return PTR_ERR(priv->phys[i]);
270
271 if (i == USB3_HOST_PHY)
272 priv->usb3_ports++;
273 else
274 priv->usb2_ports++;
275 }
276
277 dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports);
278 dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports);
279
280 return 0;
281}
282
283static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
284{
285 u32 reg;
286
287 regmap_read(priv->regmap, USB_R5, &reg);
288
289 if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
290 return PHY_MODE_USB_DEVICE;
291
292 return PHY_MODE_USB_HOST;
293}
294
295static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
296 enum phy_mode mode)
297{
298 int ret;
299
300 if (!priv->phys[USB2_OTG_PHY])
301 return -EINVAL;
302
303 if (mode == PHY_MODE_USB_HOST)
304 dev_info(priv->dev, "switching to Host Mode\n");
305 else
306 dev_info(priv->dev, "switching to Device Mode\n");
307
308 if (priv->vbus) {
309 if (mode == PHY_MODE_USB_DEVICE)
310 ret = regulator_disable(priv->vbus);
311 else
312 ret = regulator_enable(priv->vbus);
313 if (ret)
314 return ret;
315 }
316
317 priv->otg_phy_mode = mode;
318
319 dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
320
321 dwc3_meson_g12a_usb_otg_apply_mode(priv);
322
323 return 0;
324}
325
326static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role)
327{
328 struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
329 enum phy_mode mode;
330
331 if (role == USB_ROLE_NONE)
332 return 0;
333
334 mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST
335 : PHY_MODE_USB_DEVICE;
336
337 if (mode == priv->otg_phy_mode)
338 return 0;
339
340 return dwc3_meson_g12a_otg_mode_set(priv, mode);
341}
342
343static enum usb_role dwc3_meson_g12a_role_get(struct device *dev)
344{
345 struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
346
347 return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
348 USB_ROLE_HOST : USB_ROLE_DEVICE;
349}
350
351static struct device *dwc3_meson_g12_find_child(struct device *dev,
352 const char *compatible)
353{
354 struct platform_device *pdev;
355 struct device_node *np;
356
357 np = of_get_compatible_child(dev->of_node, compatible);
358 if (!np)
359 return NULL;
360
361 pdev = of_find_device_by_node(np);
362 of_node_put(np);
363 if (!pdev)
364 return NULL;
365
366 return &pdev->dev;
367}
368
369static int dwc3_meson_g12a_probe(struct platform_device *pdev)
370{
371 struct dwc3_meson_g12a *priv;
372 struct device *dev = &pdev->dev;
373 struct device_node *np = dev->of_node;
374 void __iomem *base;
375 struct resource *res;
376 enum phy_mode otg_id;
377 int ret, i;
378
379 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
380 if (!priv)
381 return -ENOMEM;
382
383 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
384 base = devm_ioremap_resource(dev, res);
385 if (IS_ERR(base))
386 return PTR_ERR(base);
387
388 priv->regmap = devm_regmap_init_mmio(dev, base,
389 &phy_meson_g12a_usb3_regmap_conf);
390 if (IS_ERR(priv->regmap))
391 return PTR_ERR(priv->regmap);
392
393 priv->vbus = devm_regulator_get_optional(dev, "vbus");
394 if (IS_ERR(priv->vbus)) {
395 if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
396 return PTR_ERR(priv->vbus);
397 priv->vbus = NULL;
398 }
399
400 priv->clk = devm_clk_get(dev, NULL);
401 if (IS_ERR(priv->clk))
402 return PTR_ERR(priv->clk);
403
404 ret = clk_prepare_enable(priv->clk);
405 if (ret)
406 return ret;
407
408 devm_add_action_or_reset(dev,
409 (void(*)(void *))clk_disable_unprepare,
410 priv->clk);
411
412 platform_set_drvdata(pdev, priv);
413 priv->dev = dev;
414
415 priv->reset = devm_reset_control_get(dev, NULL);
416 if (IS_ERR(priv->reset)) {
417 ret = PTR_ERR(priv->reset);
418 dev_err(dev, "failed to get device reset, err=%d\n", ret);
419 return ret;
420 }
421
422 ret = reset_control_reset(priv->reset);
423 if (ret)
424 return ret;
425
426 ret = dwc3_meson_g12a_get_phys(priv);
427 if (ret)
428 return ret;
429
430 if (priv->vbus) {
431 ret = regulator_enable(priv->vbus);
432 if (ret)
433 return ret;
434 }
435
436 /* Get dr_mode */
437 priv->otg_mode = usb_get_dr_mode(dev);
438
439 dwc3_meson_g12a_usb_init(priv);
440
441 /* Init PHYs */
442 for (i = 0 ; i < PHY_COUNT ; ++i) {
443 ret = phy_init(priv->phys[i]);
444 if (ret)
445 return ret;
446 }
447
448 /* Set PHY Power */
449 for (i = 0 ; i < PHY_COUNT ; ++i) {
450 ret = phy_power_on(priv->phys[i]);
451 if (ret)
452 goto err_phys_exit;
453 }
454
455 ret = of_platform_populate(np, NULL, NULL, dev);
456 if (ret) {
457 clk_disable_unprepare(priv->clk);
458 goto err_phys_power;
459 }
460
461 /* Setup OTG mode corresponding to the ID pin */
462 if (priv->otg_mode == USB_DR_MODE_OTG) {
463 /* TOFIX Handle ID mode toggling via IRQ */
464 otg_id = dwc3_meson_g12a_get_id(priv);
465 if (otg_id != priv->otg_phy_mode) {
466 if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
467 dev_warn(dev, "Failed to switch OTG mode\n");
468 }
469 }
470
471 /* Setup role switcher */
472 priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
473 "snps,dwc3");
474 priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
475 priv->switch_desc.allow_userspace_control = true;
476 priv->switch_desc.set = dwc3_meson_g12a_role_set;
477 priv->switch_desc.get = dwc3_meson_g12a_role_get;
478
479 priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
480 if (IS_ERR(priv->role_switch))
481 dev_warn(dev, "Unable to register Role Switch\n");
482
483 pm_runtime_set_active(dev);
484 pm_runtime_enable(dev);
485 pm_runtime_get_sync(dev);
486
487 return 0;
488
489err_phys_power:
490 for (i = 0 ; i < PHY_COUNT ; ++i)
491 phy_power_off(priv->phys[i]);
492
493err_phys_exit:
494 for (i = 0 ; i < PHY_COUNT ; ++i)
495 phy_exit(priv->phys[i]);
496
497 return ret;
498}
499
500static int dwc3_meson_g12a_remove(struct platform_device *pdev)
501{
502 struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
503 struct device *dev = &pdev->dev;
504 int i;
505
506 usb_role_switch_unregister(priv->role_switch);
507
508 of_platform_depopulate(dev);
509
510 for (i = 0 ; i < PHY_COUNT ; ++i) {
511 phy_power_off(priv->phys[i]);
512 phy_exit(priv->phys[i]);
513 }
514
515 pm_runtime_disable(dev);
516 pm_runtime_put_noidle(dev);
517 pm_runtime_set_suspended(dev);
518
519 return 0;
520}
521
522static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
523{
524 struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
525
526 clk_disable(priv->clk);
527
528 return 0;
529}
530
531static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
532{
533 struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
534
535 return clk_enable(priv->clk);
536}
537
538static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
539{
540 struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
541 int i;
542
543 for (i = 0 ; i < PHY_COUNT ; ++i) {
544 phy_power_off(priv->phys[i]);
545 phy_exit(priv->phys[i]);
546 }
547
548 reset_control_assert(priv->reset);
549
550 return 0;
551}
552
553static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
554{
555 struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
556 int i, ret;
557
558 reset_control_deassert(priv->reset);
559
560 dwc3_meson_g12a_usb_init(priv);
561
562 /* Init PHYs */
563 for (i = 0 ; i < PHY_COUNT ; ++i) {
564 ret = phy_init(priv->phys[i]);
565 if (ret)
566 return ret;
567 }
568
569 /* Set PHY Power */
570 for (i = 0 ; i < PHY_COUNT ; ++i) {
571 ret = phy_power_on(priv->phys[i]);
572 if (ret)
573 return ret;
574 }
575
576 return 0;
577}
578
579static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
580 SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
581 SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
582 dwc3_meson_g12a_runtime_resume, NULL)
583};
584
585static const struct of_device_id dwc3_meson_g12a_match[] = {
586 { .compatible = "amlogic,meson-g12a-usb-ctrl" },
587 { /* Sentinel */ }
588};
589MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
590
591static struct platform_driver dwc3_meson_g12a_driver = {
592 .probe = dwc3_meson_g12a_probe,
593 .remove = dwc3_meson_g12a_remove,
594 .driver = {
595 .name = "dwc3-meson-g12a",
596 .of_match_table = dwc3_meson_g12a_match,
597 .pm = &dwc3_meson_g12a_dev_pm_ops,
598 },
599};
600
601module_platform_driver(dwc3_meson_g12a_driver);
602MODULE_LICENSE("GPL v2");
603MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
604MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");