1c9999337SNeil Armstrong // SPDX-License-Identifier: GPL-2.0
2c9999337SNeil Armstrong /*
3c9999337SNeil Armstrong  * USB Glue for Amlogic G12A SoCs
4c9999337SNeil Armstrong  *
5c9999337SNeil Armstrong  * Copyright (c) 2019 BayLibre, SAS
6c9999337SNeil Armstrong  * Author: Neil Armstrong <narmstrong@baylibre.com>
7c9999337SNeil Armstrong  */
8c9999337SNeil Armstrong 
9c9999337SNeil Armstrong /*
10c9999337SNeil Armstrong  * The USB is organized with a glue around the DWC3 Controller IP as :
11c9999337SNeil Armstrong  * - Control registers for each USB2 Ports
12c9999337SNeil Armstrong  * - Control registers for the USB PHY layer
13c9999337SNeil Armstrong  * - SuperSpeed PHY can be enabled only if port is used
14f90db107SNeil Armstrong  * - Dynamic OTG switching with ID change interrupt
15c9999337SNeil Armstrong  */
16c9999337SNeil Armstrong 
17c9999337SNeil Armstrong #include <linux/module.h>
18c9999337SNeil Armstrong #include <linux/kernel.h>
19c9999337SNeil Armstrong #include <linux/platform_device.h>
20c9999337SNeil Armstrong #include <linux/clk.h>
21c9999337SNeil Armstrong #include <linux/of.h>
22c9999337SNeil Armstrong #include <linux/of_platform.h>
23c9999337SNeil Armstrong #include <linux/pm_runtime.h>
24c9999337SNeil Armstrong #include <linux/regmap.h>
25c9999337SNeil Armstrong #include <linux/bitfield.h>
26c9999337SNeil Armstrong #include <linux/bitops.h>
27c9999337SNeil Armstrong #include <linux/reset.h>
28c9999337SNeil Armstrong #include <linux/phy/phy.h>
29c9999337SNeil Armstrong #include <linux/usb/otg.h>
30c9999337SNeil Armstrong #include <linux/usb/role.h>
31c9999337SNeil Armstrong #include <linux/regulator/consumer.h>
32c9999337SNeil Armstrong 
33013af227SNeil Armstrong /* USB2 Ports Control Registers, offsets are per-port */
34c9999337SNeil Armstrong 
35c9999337SNeil Armstrong #define U2P_REG_SIZE						0x20
36c9999337SNeil Armstrong 
37c9999337SNeil Armstrong #define U2P_R0							0x0
38c9999337SNeil Armstrong 	#define U2P_R0_HOST_DEVICE				BIT(0)
39c9999337SNeil Armstrong 	#define U2P_R0_POWER_OK					BIT(1)
40c9999337SNeil Armstrong 	#define U2P_R0_HAST_MODE				BIT(2)
41c9999337SNeil Armstrong 	#define U2P_R0_POWER_ON_RESET				BIT(3)
42c9999337SNeil Armstrong 	#define U2P_R0_ID_PULLUP				BIT(4)
43c9999337SNeil Armstrong 	#define U2P_R0_DRV_VBUS					BIT(5)
44c9999337SNeil Armstrong 
45c9999337SNeil Armstrong #define U2P_R1							0x4
46c9999337SNeil Armstrong 	#define U2P_R1_PHY_READY				BIT(0)
47c9999337SNeil Armstrong 	#define U2P_R1_ID_DIG					BIT(1)
48c9999337SNeil Armstrong 	#define U2P_R1_OTG_SESSION_VALID			BIT(2)
49c9999337SNeil Armstrong 	#define U2P_R1_VBUS_VALID				BIT(3)
50c9999337SNeil Armstrong 
51c9999337SNeil Armstrong /* USB Glue Control Registers */
52c9999337SNeil Armstrong 
53013af227SNeil Armstrong #define G12A_GLUE_OFFSET					0x80
54013af227SNeil Armstrong 
55013af227SNeil Armstrong #define USB_R0							0x00
56c9999337SNeil Armstrong 	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
57c9999337SNeil Armstrong 	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
58c9999337SNeil Armstrong 	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
59c9999337SNeil Armstrong 	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
60c9999337SNeil Armstrong 	#define USB_R0_U2D_ACT					BIT(31)
61c9999337SNeil Armstrong 
62013af227SNeil Armstrong #define USB_R1							0x04
63c9999337SNeil Armstrong 	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
64c9999337SNeil Armstrong 	#define USB_R1_U3H_PME_ENABLE				BIT(1)
65c9999337SNeil Armstrong 	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(4, 2)
66c9999337SNeil Armstrong 	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(9, 7)
67c9999337SNeil Armstrong 	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(13, 12)
68c9999337SNeil Armstrong 	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
69c9999337SNeil Armstrong 	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
70c9999337SNeil Armstrong 	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
71c9999337SNeil Armstrong 	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
72c9999337SNeil Armstrong 	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
73c9999337SNeil Armstrong 
74013af227SNeil Armstrong #define USB_R2							0x08
75c9999337SNeil Armstrong 	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
76c9999337SNeil Armstrong 	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
77c9999337SNeil Armstrong 
78013af227SNeil Armstrong #define USB_R3							0x0c
79c9999337SNeil Armstrong 	#define USB_R3_P30_SSC_ENABLE				BIT(0)
80c9999337SNeil Armstrong 	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
81c9999337SNeil Armstrong 	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
82c9999337SNeil Armstrong 	#define USB_R3_P30_REF_SSP_EN				BIT(13)
83c9999337SNeil Armstrong 
84013af227SNeil Armstrong #define USB_R4							0x10
85c9999337SNeil Armstrong 	#define USB_R4_P21_PORT_RESET_0				BIT(0)
86c9999337SNeil Armstrong 	#define USB_R4_P21_SLEEP_M0				BIT(1)
87c9999337SNeil Armstrong 	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
88c9999337SNeil Armstrong 	#define USB_R4_P21_ONLY					BIT(4)
89c9999337SNeil Armstrong 
90013af227SNeil Armstrong #define USB_R5							0x14
91c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_SYNC				BIT(0)
92c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_REG				BIT(1)
93c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
94c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_EN_0				BIT(4)
95c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_EN_1				BIT(5)
96c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_CURR				BIT(6)
97c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_IRQ				BIT(7)
98c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
99c9999337SNeil Armstrong 	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
100c9999337SNeil Armstrong 
1015174564cSNeil Armstrong #define PHY_COUNT						3
1025174564cSNeil Armstrong #define USB2_OTG_PHY						1
103c9999337SNeil Armstrong 
1041e355f21SHanjie Lin static struct clk_bulk_data meson_g12a_clocks[] = {
1051e355f21SHanjie Lin 	{ .id = NULL },
1061e355f21SHanjie Lin };
1071e355f21SHanjie Lin 
1081e355f21SHanjie Lin static struct clk_bulk_data meson_a1_clocks[] = {
1091e355f21SHanjie Lin 	{ .id = "usb_ctrl" },
1101e355f21SHanjie Lin 	{ .id = "usb_bus" },
1111e355f21SHanjie Lin 	{ .id = "xtal_usb_ctrl" },
1121e355f21SHanjie Lin };
1131e355f21SHanjie Lin 
1145174564cSNeil Armstrong static const char *meson_g12a_phy_names[] = {
1155174564cSNeil Armstrong 	"usb2-phy0", "usb2-phy1", "usb3-phy0",
1165174564cSNeil Armstrong };
1175174564cSNeil Armstrong 
1185174564cSNeil Armstrong /*
1195174564cSNeil Armstrong  * Amlogic A1 has a single physical PHY, in slot 1, but still has the
1205174564cSNeil Armstrong  * two U2 PHY controls register blocks like G12A.
1215174564cSNeil Armstrong  * Handling the first PHY on slot 1 would need a large amount of code
1225174564cSNeil Armstrong  * changes, and the current management is generic enough to handle it
1235174564cSNeil Armstrong  * correctly when only the "usb2-phy1" phy is specified on-par with the
1245174564cSNeil Armstrong  * DT bindings.
1255174564cSNeil Armstrong  */
1265174564cSNeil Armstrong static const char *meson_a1_phy_names[] = {
1275174564cSNeil Armstrong 	"usb2-phy0", "usb2-phy1"
1285174564cSNeil Armstrong };
1295174564cSNeil Armstrong 
130013af227SNeil Armstrong struct dwc3_meson_g12a;
131013af227SNeil Armstrong 
1321e355f21SHanjie Lin struct dwc3_meson_g12a_drvdata {
1331e355f21SHanjie Lin 	bool otg_switch_supported;
134df7e3745SNeil Armstrong 	bool otg_phy_host_port_disable;
1351e355f21SHanjie Lin 	struct clk_bulk_data *clks;
1361e355f21SHanjie Lin 	int num_clks;
1375174564cSNeil Armstrong 	const char **phy_names;
1385174564cSNeil Armstrong 	int num_phys;
139013af227SNeil Armstrong 	int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
14031306821SNeil Armstrong 	int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
14131306821SNeil Armstrong 			     enum phy_mode mode);
14231306821SNeil Armstrong 	int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
14331306821SNeil Armstrong 			    enum phy_mode mode);
1445b0ba0caSNeil Armstrong 	int (*usb_init)(struct dwc3_meson_g12a *priv);
1455b0ba0caSNeil Armstrong 	int (*usb_post_init)(struct dwc3_meson_g12a *priv);
1461e355f21SHanjie Lin };
1471e355f21SHanjie Lin 
148013af227SNeil Armstrong static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
149013af227SNeil Armstrong 					 void __iomem *base);
150013af227SNeil Armstrong 
15131306821SNeil Armstrong static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
15231306821SNeil Armstrong 					  enum phy_mode mode);
15331306821SNeil Armstrong 
15431306821SNeil Armstrong static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
15531306821SNeil Armstrong 					int i, enum phy_mode mode);
15631306821SNeil Armstrong 
1575b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
1585b0ba0caSNeil Armstrong 
159df7e3745SNeil Armstrong /*
160df7e3745SNeil Armstrong  * For GXL and GXM SoCs:
161df7e3745SNeil Armstrong  * USB Phy muxing between the DWC2 Device controller and the DWC3 Host
162df7e3745SNeil Armstrong  * controller is buggy when switching from Device to Host when USB port
163df7e3745SNeil Armstrong  * is unpopulated, it causes the DWC3 to hard crash.
164df7e3745SNeil Armstrong  * When populated (including OTG switching with ID pin), the switch works
165df7e3745SNeil Armstrong  * like a charm like on the G12A platforms.
166df7e3745SNeil Armstrong  * In order to still switch from Host to Device on an USB Type-A port,
167df7e3745SNeil Armstrong  * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
168df7e3745SNeil Armstrong  * controller from the port, but when used the DWC3 controller must be
169df7e3745SNeil Armstrong  * reset to recover usage of the port.
170df7e3745SNeil Armstrong  */
171df7e3745SNeil Armstrong 
1721e355f21SHanjie Lin static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
1731e355f21SHanjie Lin 	.otg_switch_supported = true,
1741e355f21SHanjie Lin 	.clks = meson_g12a_clocks,
1751e355f21SHanjie Lin 	.num_clks = ARRAY_SIZE(meson_g12a_clocks),
1765174564cSNeil Armstrong 	.phy_names = meson_g12a_phy_names,
1775174564cSNeil Armstrong 	.num_phys = ARRAY_SIZE(meson_g12a_phy_names),
178013af227SNeil Armstrong 	.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
17931306821SNeil Armstrong 	.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
18031306821SNeil Armstrong 	.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
1815b0ba0caSNeil Armstrong 	.usb_init = dwc3_meson_g12a_usb_init,
1821e355f21SHanjie Lin };
1831e355f21SHanjie Lin 
1841e355f21SHanjie Lin static struct dwc3_meson_g12a_drvdata a1_drvdata = {
1851e355f21SHanjie Lin 	.otg_switch_supported = false,
1861e355f21SHanjie Lin 	.clks = meson_a1_clocks,
1871e355f21SHanjie Lin 	.num_clks = ARRAY_SIZE(meson_a1_clocks),
1885174564cSNeil Armstrong 	.phy_names = meson_a1_phy_names,
1895174564cSNeil Armstrong 	.num_phys = ARRAY_SIZE(meson_a1_phy_names),
190013af227SNeil Armstrong 	.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
19131306821SNeil Armstrong 	.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
19231306821SNeil Armstrong 	.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
1935b0ba0caSNeil Armstrong 	.usb_init = dwc3_meson_g12a_usb_init,
1941e355f21SHanjie Lin };
1951e355f21SHanjie Lin 
196c9999337SNeil Armstrong struct dwc3_meson_g12a {
197c9999337SNeil Armstrong 	struct device		*dev;
198013af227SNeil Armstrong 	struct regmap		*u2p_regmap[PHY_COUNT];
199013af227SNeil Armstrong 	struct regmap		*usb_glue_regmap;
200c9999337SNeil Armstrong 	struct reset_control	*reset;
201c9999337SNeil Armstrong 	struct phy		*phys[PHY_COUNT];
202c9999337SNeil Armstrong 	enum usb_dr_mode	otg_mode;
203c9999337SNeil Armstrong 	enum phy_mode		otg_phy_mode;
204c9999337SNeil Armstrong 	unsigned int		usb2_ports;
205c9999337SNeil Armstrong 	unsigned int		usb3_ports;
206c9999337SNeil Armstrong 	struct regulator	*vbus;
207c9999337SNeil Armstrong 	struct usb_role_switch_desc switch_desc;
208c9999337SNeil Armstrong 	struct usb_role_switch	*role_switch;
2091e355f21SHanjie Lin 	const struct dwc3_meson_g12a_drvdata *drvdata;
210c9999337SNeil Armstrong };
211c9999337SNeil Armstrong 
21231306821SNeil Armstrong static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
213c9999337SNeil Armstrong 					 int i, enum phy_mode mode)
214c9999337SNeil Armstrong {
215c9999337SNeil Armstrong 	if (mode == PHY_MODE_USB_HOST)
216013af227SNeil Armstrong 		regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
217c9999337SNeil Armstrong 				U2P_R0_HOST_DEVICE,
218c9999337SNeil Armstrong 				U2P_R0_HOST_DEVICE);
219c9999337SNeil Armstrong 	else
220013af227SNeil Armstrong 		regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
221c9999337SNeil Armstrong 				U2P_R0_HOST_DEVICE, 0);
22231306821SNeil Armstrong 
22331306821SNeil Armstrong 	return 0;
22431306821SNeil Armstrong }
22531306821SNeil Armstrong 
22631306821SNeil Armstrong static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
22731306821SNeil Armstrong 					 enum phy_mode mode)
22831306821SNeil Armstrong {
22931306821SNeil Armstrong 	int ret;
23031306821SNeil Armstrong 
23131306821SNeil Armstrong 	regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
23231306821SNeil Armstrong 			U2P_R0_POWER_ON_RESET,
23331306821SNeil Armstrong 			U2P_R0_POWER_ON_RESET);
23431306821SNeil Armstrong 
23531306821SNeil Armstrong 	if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
23631306821SNeil Armstrong 		regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
23731306821SNeil Armstrong 				   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
23831306821SNeil Armstrong 				   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
23931306821SNeil Armstrong 
24031306821SNeil Armstrong 		ret = priv->drvdata->set_phy_mode(priv, i, mode);
24131306821SNeil Armstrong 	} else
24231306821SNeil Armstrong 		ret = priv->drvdata->set_phy_mode(priv, i,
24331306821SNeil Armstrong 						  PHY_MODE_USB_HOST);
24431306821SNeil Armstrong 
24531306821SNeil Armstrong 	if (ret)
24631306821SNeil Armstrong 		return ret;
24731306821SNeil Armstrong 
24831306821SNeil Armstrong 	regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
24931306821SNeil Armstrong 			U2P_R0_POWER_ON_RESET, 0);
25031306821SNeil Armstrong 
25131306821SNeil Armstrong 	return 0;
252c9999337SNeil Armstrong }
253c9999337SNeil Armstrong 
2545b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
2555b0ba0caSNeil Armstrong 				     enum phy_mode mode)
256c9999337SNeil Armstrong {
25731306821SNeil Armstrong 	int i, ret;
258c9999337SNeil Armstrong 
2595174564cSNeil Armstrong 	for (i = 0; i < priv->drvdata->num_phys; ++i) {
260c9999337SNeil Armstrong 		if (!priv->phys[i])
261c9999337SNeil Armstrong 			continue;
262c9999337SNeil Armstrong 
2635174564cSNeil Armstrong 		if (!strstr(priv->drvdata->phy_names[i], "usb2"))
2645174564cSNeil Armstrong 			continue;
2655174564cSNeil Armstrong 
2665b0ba0caSNeil Armstrong 		ret = priv->drvdata->usb2_init_phy(priv, i, mode);
26731306821SNeil Armstrong 		if (ret)
26831306821SNeil Armstrong 			return ret;
269c9999337SNeil Armstrong 	}
270c9999337SNeil Armstrong 
271c9999337SNeil Armstrong 	return 0;
272c9999337SNeil Armstrong }
273c9999337SNeil Armstrong 
274c9999337SNeil Armstrong static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
275c9999337SNeil Armstrong {
276013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R3,
277c9999337SNeil Armstrong 			USB_R3_P30_SSC_RANGE_MASK |
278c9999337SNeil Armstrong 			USB_R3_P30_REF_SSP_EN,
279c9999337SNeil Armstrong 			USB_R3_P30_SSC_ENABLE |
280c9999337SNeil Armstrong 			FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
281c9999337SNeil Armstrong 			USB_R3_P30_REF_SSP_EN);
282c9999337SNeil Armstrong 	udelay(2);
283c9999337SNeil Armstrong 
284013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R2,
285c9999337SNeil Armstrong 			USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
286c9999337SNeil Armstrong 			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
287c9999337SNeil Armstrong 
288013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R2,
289c9999337SNeil Armstrong 			USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
290c9999337SNeil Armstrong 			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
291c9999337SNeil Armstrong 
292c9999337SNeil Armstrong 	udelay(2);
293c9999337SNeil Armstrong 
294013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R1,
295c9999337SNeil Armstrong 			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
296c9999337SNeil Armstrong 			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
297c9999337SNeil Armstrong 
298013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R1,
299c9999337SNeil Armstrong 			USB_R1_P30_PCS_TX_SWING_FULL_MASK,
300c9999337SNeil Armstrong 			FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
301c9999337SNeil Armstrong }
302c9999337SNeil Armstrong 
3035b0ba0caSNeil Armstrong static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
3045b0ba0caSNeil Armstrong 					       enum phy_mode mode)
305c9999337SNeil Armstrong {
3065b0ba0caSNeil Armstrong 	if (mode == PHY_MODE_USB_DEVICE) {
307df7e3745SNeil Armstrong 		if (priv->otg_mode != USB_DR_MODE_OTG &&
308df7e3745SNeil Armstrong 		    priv->drvdata->otg_phy_host_port_disable)
309df7e3745SNeil Armstrong 			/* Isolate the OTG PHY port from the Host Controller */
310df7e3745SNeil Armstrong 			regmap_update_bits(priv->usb_glue_regmap, USB_R1,
311df7e3745SNeil Armstrong 				USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
312df7e3745SNeil Armstrong 				FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
313df7e3745SNeil Armstrong 					   BIT(USB2_OTG_PHY)));
314df7e3745SNeil Armstrong 
315013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R0,
316c9999337SNeil Armstrong 				USB_R0_U2D_ACT, USB_R0_U2D_ACT);
317013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R0,
318c9999337SNeil Armstrong 				USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
319013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R4,
320c9999337SNeil Armstrong 				USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
321c9999337SNeil Armstrong 	} else {
322df7e3745SNeil Armstrong 		if (priv->otg_mode != USB_DR_MODE_OTG &&
323df7e3745SNeil Armstrong 		    priv->drvdata->otg_phy_host_port_disable) {
324df7e3745SNeil Armstrong 			regmap_update_bits(priv->usb_glue_regmap, USB_R1,
325df7e3745SNeil Armstrong 				USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
326df7e3745SNeil Armstrong 			msleep(500);
327df7e3745SNeil Armstrong 		}
328013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R0,
329c9999337SNeil Armstrong 				USB_R0_U2D_ACT, 0);
330013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R4,
331c9999337SNeil Armstrong 				USB_R4_P21_SLEEP_M0, 0);
332c9999337SNeil Armstrong 	}
333c9999337SNeil Armstrong }
334c9999337SNeil Armstrong 
3355b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
3365b0ba0caSNeil Armstrong 					 enum phy_mode mode)
337c9999337SNeil Armstrong {
338c9999337SNeil Armstrong 	int ret;
339c9999337SNeil Armstrong 
3405b0ba0caSNeil Armstrong 	ret = dwc3_meson_g12a_usb2_init(priv, mode);
341c9999337SNeil Armstrong 	if (ret)
342c9999337SNeil Armstrong 		return ret;
343c9999337SNeil Armstrong 
344013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R1,
345c9999337SNeil Armstrong 			USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
346c9999337SNeil Armstrong 			FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
347c9999337SNeil Armstrong 
348013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
349c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_0,
350c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_0);
351013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
352c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_1,
353c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_1);
354013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
355c9999337SNeil Armstrong 			USB_R5_ID_DIG_TH_MASK,
356c9999337SNeil Armstrong 			FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
357c9999337SNeil Armstrong 
358c9999337SNeil Armstrong 	/* If we have an actual SuperSpeed port, initialize it */
359c9999337SNeil Armstrong 	if (priv->usb3_ports)
360c9999337SNeil Armstrong 		dwc3_meson_g12a_usb3_init(priv);
361c9999337SNeil Armstrong 
3625b0ba0caSNeil Armstrong 	dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
363c9999337SNeil Armstrong 
364c9999337SNeil Armstrong 	return 0;
365c9999337SNeil Armstrong }
366c9999337SNeil Armstrong 
367013af227SNeil Armstrong static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
368013af227SNeil Armstrong 	.name = "usb-glue",
369c9999337SNeil Armstrong 	.reg_bits = 8,
370c9999337SNeil Armstrong 	.val_bits = 32,
371c9999337SNeil Armstrong 	.reg_stride = 4,
372c9999337SNeil Armstrong 	.max_register = USB_R5,
373c9999337SNeil Armstrong };
374c9999337SNeil Armstrong 
375c9999337SNeil Armstrong static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
376c9999337SNeil Armstrong {
3775174564cSNeil Armstrong 	const char *phy_name;
378c9999337SNeil Armstrong 	int i;
379c9999337SNeil Armstrong 
3805174564cSNeil Armstrong 	for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
3815174564cSNeil Armstrong 		phy_name = priv->drvdata->phy_names[i];
3825174564cSNeil Armstrong 		priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
383c9999337SNeil Armstrong 		if (!priv->phys[i])
384c9999337SNeil Armstrong 			continue;
385c9999337SNeil Armstrong 
386c9999337SNeil Armstrong 		if (IS_ERR(priv->phys[i]))
387c9999337SNeil Armstrong 			return PTR_ERR(priv->phys[i]);
388c9999337SNeil Armstrong 
3895174564cSNeil Armstrong 		if (strstr(phy_name, "usb3"))
390c9999337SNeil Armstrong 			priv->usb3_ports++;
391c9999337SNeil Armstrong 		else
392c9999337SNeil Armstrong 			priv->usb2_ports++;
393c9999337SNeil Armstrong 	}
394c9999337SNeil Armstrong 
395c9999337SNeil Armstrong 	dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports);
396c9999337SNeil Armstrong 	dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports);
397c9999337SNeil Armstrong 
398c9999337SNeil Armstrong 	return 0;
399c9999337SNeil Armstrong }
400c9999337SNeil Armstrong 
401c9999337SNeil Armstrong static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
402c9999337SNeil Armstrong {
403c9999337SNeil Armstrong 	u32 reg;
404c9999337SNeil Armstrong 
405013af227SNeil Armstrong 	regmap_read(priv->usb_glue_regmap, USB_R5, &reg);
406c9999337SNeil Armstrong 
407c9999337SNeil Armstrong 	if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
408c9999337SNeil Armstrong 		return PHY_MODE_USB_DEVICE;
409c9999337SNeil Armstrong 
410c9999337SNeil Armstrong 	return PHY_MODE_USB_HOST;
411c9999337SNeil Armstrong }
412c9999337SNeil Armstrong 
413c9999337SNeil Armstrong static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
414c9999337SNeil Armstrong 					enum phy_mode mode)
415c9999337SNeil Armstrong {
416c9999337SNeil Armstrong 	int ret;
417c9999337SNeil Armstrong 
4181e355f21SHanjie Lin 	if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY])
419c9999337SNeil Armstrong 		return -EINVAL;
420c9999337SNeil Armstrong 
421c9999337SNeil Armstrong 	if (mode == PHY_MODE_USB_HOST)
422c9999337SNeil Armstrong 		dev_info(priv->dev, "switching to Host Mode\n");
423c9999337SNeil Armstrong 	else
424c9999337SNeil Armstrong 		dev_info(priv->dev, "switching to Device Mode\n");
425c9999337SNeil Armstrong 
426c9999337SNeil Armstrong 	if (priv->vbus) {
427c9999337SNeil Armstrong 		if (mode == PHY_MODE_USB_DEVICE)
428c9999337SNeil Armstrong 			ret = regulator_disable(priv->vbus);
429c9999337SNeil Armstrong 		else
430c9999337SNeil Armstrong 			ret = regulator_enable(priv->vbus);
431c9999337SNeil Armstrong 		if (ret)
432c9999337SNeil Armstrong 			return ret;
433c9999337SNeil Armstrong 	}
434c9999337SNeil Armstrong 
435c9999337SNeil Armstrong 	priv->otg_phy_mode = mode;
436c9999337SNeil Armstrong 
43731306821SNeil Armstrong 	ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
43831306821SNeil Armstrong 	if (ret)
43931306821SNeil Armstrong 		return ret;
440c9999337SNeil Armstrong 
4415b0ba0caSNeil Armstrong 	dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
442c9999337SNeil Armstrong 
443c9999337SNeil Armstrong 	return 0;
444c9999337SNeil Armstrong }
445c9999337SNeil Armstrong 
446bce3052fSHeikki Krogerus static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
447bce3052fSHeikki Krogerus 				    enum usb_role role)
448c9999337SNeil Armstrong {
449bce3052fSHeikki Krogerus 	struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
450c9999337SNeil Armstrong 	enum phy_mode mode;
451c9999337SNeil Armstrong 
452c9999337SNeil Armstrong 	if (role == USB_ROLE_NONE)
453c9999337SNeil Armstrong 		return 0;
454c9999337SNeil Armstrong 
455c9999337SNeil Armstrong 	mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST
456c9999337SNeil Armstrong 				       : PHY_MODE_USB_DEVICE;
457c9999337SNeil Armstrong 
458c9999337SNeil Armstrong 	if (mode == priv->otg_phy_mode)
459c9999337SNeil Armstrong 		return 0;
460c9999337SNeil Armstrong 
461df7e3745SNeil Armstrong 	if (priv->drvdata->otg_phy_host_port_disable)
462df7e3745SNeil Armstrong 		dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
463df7e3745SNeil Armstrong 					 "SoC, when manual switching from "\
464df7e3745SNeil Armstrong 					 "Host to device, DWC3 controller "\
465df7e3745SNeil Armstrong 					 "will need to be resetted in order "\
466df7e3745SNeil Armstrong 					 "to recover usage of the Host port");
467df7e3745SNeil Armstrong 
468c9999337SNeil Armstrong 	return dwc3_meson_g12a_otg_mode_set(priv, mode);
469c9999337SNeil Armstrong }
470c9999337SNeil Armstrong 
471bce3052fSHeikki Krogerus static enum usb_role dwc3_meson_g12a_role_get(struct usb_role_switch *sw)
472c9999337SNeil Armstrong {
473bce3052fSHeikki Krogerus 	struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
474c9999337SNeil Armstrong 
475c9999337SNeil Armstrong 	return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
476c9999337SNeil Armstrong 		USB_ROLE_HOST : USB_ROLE_DEVICE;
477c9999337SNeil Armstrong }
478c9999337SNeil Armstrong 
479f90db107SNeil Armstrong static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data)
480f90db107SNeil Armstrong {
481f90db107SNeil Armstrong 	struct dwc3_meson_g12a *priv = data;
482f90db107SNeil Armstrong 	enum phy_mode otg_id;
483f90db107SNeil Armstrong 
484f90db107SNeil Armstrong 	otg_id = dwc3_meson_g12a_get_id(priv);
485f90db107SNeil Armstrong 	if (otg_id != priv->otg_phy_mode) {
486f90db107SNeil Armstrong 		if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
487f90db107SNeil Armstrong 			dev_warn(priv->dev, "Failed to switch OTG mode\n");
488f90db107SNeil Armstrong 	}
489f90db107SNeil Armstrong 
490013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
491013af227SNeil Armstrong 			   USB_R5_ID_DIG_IRQ, 0);
492f90db107SNeil Armstrong 
493f90db107SNeil Armstrong 	return IRQ_HANDLED;
494f90db107SNeil Armstrong }
495f90db107SNeil Armstrong 
496c9999337SNeil Armstrong static struct device *dwc3_meson_g12_find_child(struct device *dev,
497c9999337SNeil Armstrong 						const char *compatible)
498c9999337SNeil Armstrong {
499c9999337SNeil Armstrong 	struct platform_device *pdev;
500c9999337SNeil Armstrong 	struct device_node *np;
501c9999337SNeil Armstrong 
502c9999337SNeil Armstrong 	np = of_get_compatible_child(dev->of_node, compatible);
503c9999337SNeil Armstrong 	if (!np)
504c9999337SNeil Armstrong 		return NULL;
505c9999337SNeil Armstrong 
506c9999337SNeil Armstrong 	pdev = of_find_device_by_node(np);
507c9999337SNeil Armstrong 	of_node_put(np);
508c9999337SNeil Armstrong 	if (!pdev)
509c9999337SNeil Armstrong 		return NULL;
510c9999337SNeil Armstrong 
511c9999337SNeil Armstrong 	return &pdev->dev;
512c9999337SNeil Armstrong }
513c9999337SNeil Armstrong 
5141e355f21SHanjie Lin static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
5151e355f21SHanjie Lin 				    struct dwc3_meson_g12a *priv)
516c9999337SNeil Armstrong {
517c9999337SNeil Armstrong 	enum phy_mode otg_id;
5181e355f21SHanjie Lin 	int ret, irq;
5191e355f21SHanjie Lin 	struct device *dev = &pdev->dev;
520c9999337SNeil Armstrong 
5211e355f21SHanjie Lin 	if (!priv->drvdata->otg_switch_supported)
5221e355f21SHanjie Lin 		return 0;
523c9999337SNeil Armstrong 
524f90db107SNeil Armstrong 	if (priv->otg_mode == USB_DR_MODE_OTG) {
525f90db107SNeil Armstrong 		/* Ack irq before registering */
526013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R5,
527f90db107SNeil Armstrong 				   USB_R5_ID_DIG_IRQ, 0);
528f90db107SNeil Armstrong 
529f90db107SNeil Armstrong 		irq = platform_get_irq(pdev, 0);
530f90db107SNeil Armstrong 		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
531f90db107SNeil Armstrong 						dwc3_meson_g12a_irq_thread,
532f90db107SNeil Armstrong 						IRQF_ONESHOT, pdev->name, priv);
533f90db107SNeil Armstrong 		if (ret)
534f90db107SNeil Armstrong 			return ret;
535f90db107SNeil Armstrong 	}
536f90db107SNeil Armstrong 
537c9999337SNeil Armstrong 	/* Setup OTG mode corresponding to the ID pin */
538c9999337SNeil Armstrong 	if (priv->otg_mode == USB_DR_MODE_OTG) {
539c9999337SNeil Armstrong 		otg_id = dwc3_meson_g12a_get_id(priv);
540c9999337SNeil Armstrong 		if (otg_id != priv->otg_phy_mode) {
541c9999337SNeil Armstrong 			if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
542c9999337SNeil Armstrong 				dev_warn(dev, "Failed to switch OTG mode\n");
543c9999337SNeil Armstrong 		}
544c9999337SNeil Armstrong 	}
545c9999337SNeil Armstrong 
546c9999337SNeil Armstrong 	/* Setup role switcher */
547c9999337SNeil Armstrong 	priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
548c9999337SNeil Armstrong 								"snps,dwc3");
549c9999337SNeil Armstrong 	priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
550c9999337SNeil Armstrong 	priv->switch_desc.allow_userspace_control = true;
551c9999337SNeil Armstrong 	priv->switch_desc.set = dwc3_meson_g12a_role_set;
552c9999337SNeil Armstrong 	priv->switch_desc.get = dwc3_meson_g12a_role_get;
553bce3052fSHeikki Krogerus 	priv->switch_desc.driver_data = priv;
554c9999337SNeil Armstrong 
555c9999337SNeil Armstrong 	priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
556c9999337SNeil Armstrong 	if (IS_ERR(priv->role_switch))
557c9999337SNeil Armstrong 		dev_warn(dev, "Unable to register Role Switch\n");
558c9999337SNeil Armstrong 
559238d7602SNathan Chancellor 	return 0;
5601e355f21SHanjie Lin }
5611e355f21SHanjie Lin 
562013af227SNeil Armstrong static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
563013af227SNeil Armstrong 					 void __iomem *base)
564013af227SNeil Armstrong {
565013af227SNeil Armstrong 	int i;
566013af227SNeil Armstrong 
567013af227SNeil Armstrong 	priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
568013af227SNeil Armstrong 					base + G12A_GLUE_OFFSET,
569013af227SNeil Armstrong 					&phy_meson_g12a_usb_glue_regmap_conf);
570013af227SNeil Armstrong 	if (IS_ERR(priv->usb_glue_regmap))
571013af227SNeil Armstrong 		return PTR_ERR(priv->usb_glue_regmap);
572013af227SNeil Armstrong 
573013af227SNeil Armstrong 	/* Create a regmap for each USB2 PHY control register set */
574013af227SNeil Armstrong 	for (i = 0; i < priv->usb2_ports; i++) {
575013af227SNeil Armstrong 		struct regmap_config u2p_regmap_config = {
576013af227SNeil Armstrong 			.reg_bits = 8,
577013af227SNeil Armstrong 			.val_bits = 32,
578013af227SNeil Armstrong 			.reg_stride = 4,
579013af227SNeil Armstrong 			.max_register = U2P_R1,
580013af227SNeil Armstrong 		};
581013af227SNeil Armstrong 
582013af227SNeil Armstrong 		u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
583013af227SNeil Armstrong 							"u2p-%d", i);
584013af227SNeil Armstrong 		if (!u2p_regmap_config.name)
585013af227SNeil Armstrong 			return -ENOMEM;
586013af227SNeil Armstrong 
587013af227SNeil Armstrong 		priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
588013af227SNeil Armstrong 						base + (i * U2P_REG_SIZE),
589013af227SNeil Armstrong 						&u2p_regmap_config);
590013af227SNeil Armstrong 		if (IS_ERR(priv->u2p_regmap[i]))
591013af227SNeil Armstrong 			return PTR_ERR(priv->u2p_regmap[i]);
592013af227SNeil Armstrong 	}
593013af227SNeil Armstrong 
594013af227SNeil Armstrong 	return 0;
595013af227SNeil Armstrong }
596013af227SNeil Armstrong 
5975b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
5985b0ba0caSNeil Armstrong {
5995b0ba0caSNeil Armstrong 	return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
6005b0ba0caSNeil Armstrong }
6015b0ba0caSNeil Armstrong 
6021e355f21SHanjie Lin static int dwc3_meson_g12a_probe(struct platform_device *pdev)
6031e355f21SHanjie Lin {
6041e355f21SHanjie Lin 	struct dwc3_meson_g12a	*priv;
6051e355f21SHanjie Lin 	struct device		*dev = &pdev->dev;
6061e355f21SHanjie Lin 	struct device_node	*np = dev->of_node;
6071e355f21SHanjie Lin 	void __iomem *base;
6081e355f21SHanjie Lin 	int ret, i;
6091e355f21SHanjie Lin 
6101e355f21SHanjie Lin 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
6111e355f21SHanjie Lin 	if (!priv)
6121e355f21SHanjie Lin 		return -ENOMEM;
6131e355f21SHanjie Lin 
6141e355f21SHanjie Lin 	base = devm_platform_ioremap_resource(pdev, 0);
6151e355f21SHanjie Lin 	if (IS_ERR(base))
6161e355f21SHanjie Lin 		return PTR_ERR(base);
6171e355f21SHanjie Lin 
618013af227SNeil Armstrong 	priv->drvdata = of_device_get_match_data(&pdev->dev);
619013af227SNeil Armstrong 
620013af227SNeil Armstrong 	priv->dev = dev;
621013af227SNeil Armstrong 	ret = priv->drvdata->setup_regmaps(priv, base);
622013af227SNeil Armstrong 	if (ret)
623013af227SNeil Armstrong 		return ret;
6241e355f21SHanjie Lin 
6251e355f21SHanjie Lin 	priv->vbus = devm_regulator_get_optional(dev, "vbus");
6261e355f21SHanjie Lin 	if (IS_ERR(priv->vbus)) {
6271e355f21SHanjie Lin 		if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
6281e355f21SHanjie Lin 			return PTR_ERR(priv->vbus);
6291e355f21SHanjie Lin 		priv->vbus = NULL;
6301e355f21SHanjie Lin 	}
6311e355f21SHanjie Lin 
6321e355f21SHanjie Lin 	ret = devm_clk_bulk_get(dev,
6331e355f21SHanjie Lin 				priv->drvdata->num_clks,
6341e355f21SHanjie Lin 				priv->drvdata->clks);
6351e355f21SHanjie Lin 	if (ret)
6361e355f21SHanjie Lin 		return ret;
6371e355f21SHanjie Lin 
6381e355f21SHanjie Lin 	ret = clk_bulk_prepare_enable(priv->drvdata->num_clks,
6391e355f21SHanjie Lin 				      priv->drvdata->clks);
6401e355f21SHanjie Lin 	if (ret)
6411e355f21SHanjie Lin 		return ret;
6421e355f21SHanjie Lin 
6431e355f21SHanjie Lin 	platform_set_drvdata(pdev, priv);
6441e355f21SHanjie Lin 
6456d9fa35aSNeil Armstrong 	priv->reset = devm_reset_control_get_shared(dev, NULL);
6461e355f21SHanjie Lin 	if (IS_ERR(priv->reset)) {
6471e355f21SHanjie Lin 		ret = PTR_ERR(priv->reset);
6481e355f21SHanjie Lin 		dev_err(dev, "failed to get device reset, err=%d\n", ret);
6491e355f21SHanjie Lin 		return ret;
6501e355f21SHanjie Lin 	}
6511e355f21SHanjie Lin 
6521e355f21SHanjie Lin 	ret = reset_control_reset(priv->reset);
6531e355f21SHanjie Lin 	if (ret)
6541e355f21SHanjie Lin 		goto err_disable_clks;
6551e355f21SHanjie Lin 
6561e355f21SHanjie Lin 	ret = dwc3_meson_g12a_get_phys(priv);
6571e355f21SHanjie Lin 	if (ret)
6581e355f21SHanjie Lin 		goto err_disable_clks;
6591e355f21SHanjie Lin 
6601e355f21SHanjie Lin 	if (priv->vbus) {
6611e355f21SHanjie Lin 		ret = regulator_enable(priv->vbus);
6621e355f21SHanjie Lin 		if (ret)
6631e355f21SHanjie Lin 			goto err_disable_clks;
6641e355f21SHanjie Lin 	}
6651e355f21SHanjie Lin 
6661e355f21SHanjie Lin 	/* Get dr_mode */
6671e355f21SHanjie Lin 	priv->otg_mode = usb_get_dr_mode(dev);
6681e355f21SHanjie Lin 
6695b0ba0caSNeil Armstrong 	if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
6705b0ba0caSNeil Armstrong 		priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
6715b0ba0caSNeil Armstrong 	else
6725b0ba0caSNeil Armstrong 		priv->otg_phy_mode = PHY_MODE_USB_HOST;
6735b0ba0caSNeil Armstrong 
6745b0ba0caSNeil Armstrong 	ret = priv->drvdata->usb_init(priv);
6758f5bc1ecSNeil Armstrong 	if (ret)
6768f5bc1ecSNeil Armstrong 		goto err_disable_clks;
6771e355f21SHanjie Lin 
6781e355f21SHanjie Lin 	/* Init PHYs */
6791e355f21SHanjie Lin 	for (i = 0 ; i < PHY_COUNT ; ++i) {
6801e355f21SHanjie Lin 		ret = phy_init(priv->phys[i]);
6811e355f21SHanjie Lin 		if (ret)
6821e355f21SHanjie Lin 			goto err_disable_clks;
6831e355f21SHanjie Lin 	}
6841e355f21SHanjie Lin 
6851e355f21SHanjie Lin 	/* Set PHY Power */
6861e355f21SHanjie Lin 	for (i = 0 ; i < PHY_COUNT ; ++i) {
6871e355f21SHanjie Lin 		ret = phy_power_on(priv->phys[i]);
6881e355f21SHanjie Lin 		if (ret)
6891e355f21SHanjie Lin 			goto err_phys_exit;
6901e355f21SHanjie Lin 	}
6911e355f21SHanjie Lin 
6925b0ba0caSNeil Armstrong 	if (priv->drvdata->usb_post_init) {
6935b0ba0caSNeil Armstrong 		ret = priv->drvdata->usb_post_init(priv);
6945b0ba0caSNeil Armstrong 		if (ret)
6955b0ba0caSNeil Armstrong 			goto err_phys_power;
6965b0ba0caSNeil Armstrong 	}
6975b0ba0caSNeil Armstrong 
6981e355f21SHanjie Lin 	ret = of_platform_populate(np, NULL, NULL, dev);
6991e355f21SHanjie Lin 	if (ret)
7001e355f21SHanjie Lin 		goto err_phys_power;
7011e355f21SHanjie Lin 
7021e355f21SHanjie Lin 	ret = dwc3_meson_g12a_otg_init(pdev, priv);
7031e355f21SHanjie Lin 	if (ret)
7041e355f21SHanjie Lin 		goto err_phys_power;
7051e355f21SHanjie Lin 
706c9999337SNeil Armstrong 	pm_runtime_set_active(dev);
707c9999337SNeil Armstrong 	pm_runtime_enable(dev);
708c9999337SNeil Armstrong 	pm_runtime_get_sync(dev);
709c9999337SNeil Armstrong 
710c9999337SNeil Armstrong 	return 0;
711c9999337SNeil Armstrong 
712c9999337SNeil Armstrong err_phys_power:
713c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i)
714c9999337SNeil Armstrong 		phy_power_off(priv->phys[i]);
715c9999337SNeil Armstrong 
716c9999337SNeil Armstrong err_phys_exit:
717c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i)
718c9999337SNeil Armstrong 		phy_exit(priv->phys[i]);
719c9999337SNeil Armstrong 
7201e355f21SHanjie Lin err_disable_clks:
7211e355f21SHanjie Lin 	clk_bulk_disable_unprepare(priv->drvdata->num_clks,
7221e355f21SHanjie Lin 				   priv->drvdata->clks);
7231e355f21SHanjie Lin 
724c9999337SNeil Armstrong 	return ret;
725c9999337SNeil Armstrong }
726c9999337SNeil Armstrong 
727c9999337SNeil Armstrong static int dwc3_meson_g12a_remove(struct platform_device *pdev)
728c9999337SNeil Armstrong {
729c9999337SNeil Armstrong 	struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
730c9999337SNeil Armstrong 	struct device *dev = &pdev->dev;
731c9999337SNeil Armstrong 	int i;
732c9999337SNeil Armstrong 
7331e355f21SHanjie Lin 	if (priv->drvdata->otg_switch_supported)
734c9999337SNeil Armstrong 		usb_role_switch_unregister(priv->role_switch);
735c9999337SNeil Armstrong 
736c9999337SNeil Armstrong 	of_platform_depopulate(dev);
737c9999337SNeil Armstrong 
738c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
739c9999337SNeil Armstrong 		phy_power_off(priv->phys[i]);
740c9999337SNeil Armstrong 		phy_exit(priv->phys[i]);
741c9999337SNeil Armstrong 	}
742c9999337SNeil Armstrong 
743c9999337SNeil Armstrong 	pm_runtime_disable(dev);
744c9999337SNeil Armstrong 	pm_runtime_put_noidle(dev);
745c9999337SNeil Armstrong 	pm_runtime_set_suspended(dev);
746c9999337SNeil Armstrong 
7471e355f21SHanjie Lin 	clk_bulk_disable_unprepare(priv->drvdata->num_clks,
7481e355f21SHanjie Lin 				   priv->drvdata->clks);
7491e355f21SHanjie Lin 
750c9999337SNeil Armstrong 	return 0;
751c9999337SNeil Armstrong }
752c9999337SNeil Armstrong 
753c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
754c9999337SNeil Armstrong {
755c9999337SNeil Armstrong 	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
756c9999337SNeil Armstrong 
7571e355f21SHanjie Lin 	clk_bulk_disable_unprepare(priv->drvdata->num_clks,
7581e355f21SHanjie Lin 				   priv->drvdata->clks);
759c9999337SNeil Armstrong 
760c9999337SNeil Armstrong 	return 0;
761c9999337SNeil Armstrong }
762c9999337SNeil Armstrong 
763c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
764c9999337SNeil Armstrong {
765c9999337SNeil Armstrong 	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
766c9999337SNeil Armstrong 
7671e355f21SHanjie Lin 	return clk_bulk_prepare_enable(priv->drvdata->num_clks,
7681e355f21SHanjie Lin 				       priv->drvdata->clks);
769c9999337SNeil Armstrong }
770c9999337SNeil Armstrong 
771c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
772c9999337SNeil Armstrong {
773c9999337SNeil Armstrong 	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
7741cf084d1SNeil Armstrong 	int i, ret;
7751cf084d1SNeil Armstrong 
7761cf084d1SNeil Armstrong 	if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
7771cf084d1SNeil Armstrong 		ret = regulator_disable(priv->vbus);
7781cf084d1SNeil Armstrong 		if (ret)
7791cf084d1SNeil Armstrong 			return ret;
7801cf084d1SNeil Armstrong 	}
781c9999337SNeil Armstrong 
782c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
783c9999337SNeil Armstrong 		phy_power_off(priv->phys[i]);
784c9999337SNeil Armstrong 		phy_exit(priv->phys[i]);
785c9999337SNeil Armstrong 	}
786c9999337SNeil Armstrong 
787c9999337SNeil Armstrong 	reset_control_assert(priv->reset);
788c9999337SNeil Armstrong 
789c9999337SNeil Armstrong 	return 0;
790c9999337SNeil Armstrong }
791c9999337SNeil Armstrong 
792c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
793c9999337SNeil Armstrong {
794c9999337SNeil Armstrong 	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
795c9999337SNeil Armstrong 	int i, ret;
796c9999337SNeil Armstrong 
797c9999337SNeil Armstrong 	reset_control_deassert(priv->reset);
798c9999337SNeil Armstrong 
7995b0ba0caSNeil Armstrong 	ret = priv->drvdata->usb_init(priv);
8005b0ba0caSNeil Armstrong 	if (ret)
8015b0ba0caSNeil Armstrong 		return ret;
802c9999337SNeil Armstrong 
803c9999337SNeil Armstrong 	/* Init PHYs */
804c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
805c9999337SNeil Armstrong 		ret = phy_init(priv->phys[i]);
806c9999337SNeil Armstrong 		if (ret)
807c9999337SNeil Armstrong 			return ret;
808c9999337SNeil Armstrong 	}
809c9999337SNeil Armstrong 
810c9999337SNeil Armstrong 	/* Set PHY Power */
811c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
812c9999337SNeil Armstrong 		ret = phy_power_on(priv->phys[i]);
813c9999337SNeil Armstrong 		if (ret)
814c9999337SNeil Armstrong 			return ret;
815c9999337SNeil Armstrong 	}
816c9999337SNeil Armstrong 
8171cf084d1SNeil Armstrong        if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
8181cf084d1SNeil Armstrong                ret = regulator_enable(priv->vbus);
8191cf084d1SNeil Armstrong 		if (ret)
8201cf084d1SNeil Armstrong 			return ret;
8211cf084d1SNeil Armstrong 	}
8221cf084d1SNeil Armstrong 
823c9999337SNeil Armstrong 	return 0;
824c9999337SNeil Armstrong }
825c9999337SNeil Armstrong 
826c9999337SNeil Armstrong static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
827c9999337SNeil Armstrong 	SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
828c9999337SNeil Armstrong 	SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
829c9999337SNeil Armstrong 			   dwc3_meson_g12a_runtime_resume, NULL)
830c9999337SNeil Armstrong };
831c9999337SNeil Armstrong 
832c9999337SNeil Armstrong static const struct of_device_id dwc3_meson_g12a_match[] = {
8331e355f21SHanjie Lin 	{
8341e355f21SHanjie Lin 		.compatible = "amlogic,meson-g12a-usb-ctrl",
8351e355f21SHanjie Lin 		.data = &g12a_drvdata,
8361e355f21SHanjie Lin 	},
8371e355f21SHanjie Lin 	{
8381e355f21SHanjie Lin 		.compatible = "amlogic,meson-a1-usb-ctrl",
8391e355f21SHanjie Lin 		.data = &a1_drvdata,
8401e355f21SHanjie Lin 	},
841c9999337SNeil Armstrong 	{ /* Sentinel */ }
842c9999337SNeil Armstrong };
843c9999337SNeil Armstrong MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
844c9999337SNeil Armstrong 
845c9999337SNeil Armstrong static struct platform_driver dwc3_meson_g12a_driver = {
846c9999337SNeil Armstrong 	.probe		= dwc3_meson_g12a_probe,
847c9999337SNeil Armstrong 	.remove		= dwc3_meson_g12a_remove,
848c9999337SNeil Armstrong 	.driver		= {
849c9999337SNeil Armstrong 		.name	= "dwc3-meson-g12a",
850c9999337SNeil Armstrong 		.of_match_table = dwc3_meson_g12a_match,
851c9999337SNeil Armstrong 		.pm	= &dwc3_meson_g12a_dev_pm_ops,
852c9999337SNeil Armstrong 	},
853c9999337SNeil Armstrong };
854c9999337SNeil Armstrong 
855c9999337SNeil Armstrong module_platform_driver(dwc3_meson_g12a_driver);
856c9999337SNeil Armstrong MODULE_LICENSE("GPL v2");
857c9999337SNeil Armstrong MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
858c9999337SNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
859