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 
104a9fc15e0SNeil Armstrong static struct clk_bulk_data meson_gxl_clocks[] = {
105a9fc15e0SNeil Armstrong 	{ .id = "usb_ctrl" },
106a9fc15e0SNeil Armstrong 	{ .id = "ddr" },
107a9fc15e0SNeil Armstrong };
108a9fc15e0SNeil Armstrong 
1091e355f21SHanjie Lin static struct clk_bulk_data meson_g12a_clocks[] = {
1101e355f21SHanjie Lin 	{ .id = NULL },
1111e355f21SHanjie Lin };
1121e355f21SHanjie Lin 
1131e355f21SHanjie Lin static struct clk_bulk_data meson_a1_clocks[] = {
1141e355f21SHanjie Lin 	{ .id = "usb_ctrl" },
1151e355f21SHanjie Lin 	{ .id = "usb_bus" },
1161e355f21SHanjie Lin 	{ .id = "xtal_usb_ctrl" },
1171e355f21SHanjie Lin };
1181e355f21SHanjie Lin 
119a9fc15e0SNeil Armstrong static const char *meson_gxm_phy_names[] = {
120a9fc15e0SNeil Armstrong 	"usb2-phy0", "usb2-phy1", "usb2-phy2",
121a9fc15e0SNeil Armstrong };
122a9fc15e0SNeil Armstrong 
1235174564cSNeil Armstrong static const char *meson_g12a_phy_names[] = {
1245174564cSNeil Armstrong 	"usb2-phy0", "usb2-phy1", "usb3-phy0",
1255174564cSNeil Armstrong };
1265174564cSNeil Armstrong 
1275174564cSNeil Armstrong /*
1285174564cSNeil Armstrong  * Amlogic A1 has a single physical PHY, in slot 1, but still has the
1295174564cSNeil Armstrong  * two U2 PHY controls register blocks like G12A.
1305174564cSNeil Armstrong  * Handling the first PHY on slot 1 would need a large amount of code
1315174564cSNeil Armstrong  * changes, and the current management is generic enough to handle it
1325174564cSNeil Armstrong  * correctly when only the "usb2-phy1" phy is specified on-par with the
1335174564cSNeil Armstrong  * DT bindings.
1345174564cSNeil Armstrong  */
1355174564cSNeil Armstrong static const char *meson_a1_phy_names[] = {
1365174564cSNeil Armstrong 	"usb2-phy0", "usb2-phy1"
1375174564cSNeil Armstrong };
1385174564cSNeil Armstrong 
139013af227SNeil Armstrong struct dwc3_meson_g12a;
140013af227SNeil Armstrong 
1411e355f21SHanjie Lin struct dwc3_meson_g12a_drvdata {
1421e355f21SHanjie Lin 	bool otg_switch_supported;
143df7e3745SNeil Armstrong 	bool otg_phy_host_port_disable;
1441e355f21SHanjie Lin 	struct clk_bulk_data *clks;
1451e355f21SHanjie Lin 	int num_clks;
1465174564cSNeil Armstrong 	const char **phy_names;
1475174564cSNeil Armstrong 	int num_phys;
148013af227SNeil Armstrong 	int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
14931306821SNeil Armstrong 	int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
15031306821SNeil Armstrong 			     enum phy_mode mode);
15131306821SNeil Armstrong 	int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
15231306821SNeil Armstrong 			    enum phy_mode mode);
1535b0ba0caSNeil Armstrong 	int (*usb_init)(struct dwc3_meson_g12a *priv);
1545b0ba0caSNeil Armstrong 	int (*usb_post_init)(struct dwc3_meson_g12a *priv);
1551e355f21SHanjie Lin };
1561e355f21SHanjie Lin 
157a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
158a9fc15e0SNeil Armstrong 					void __iomem *base);
159013af227SNeil Armstrong static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
160013af227SNeil Armstrong 					 void __iomem *base);
161013af227SNeil Armstrong 
16231306821SNeil Armstrong static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
16331306821SNeil Armstrong 					 enum phy_mode mode);
164a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
165a9fc15e0SNeil Armstrong 					enum phy_mode mode);
16631306821SNeil Armstrong 
16731306821SNeil Armstrong static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
16831306821SNeil Armstrong 					int i, enum phy_mode mode);
169a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
170a9fc15e0SNeil Armstrong 				       int i, enum phy_mode mode);
17131306821SNeil Armstrong 
1725b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
173a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv);
174a9fc15e0SNeil Armstrong 
175a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
1765b0ba0caSNeil Armstrong 
177df7e3745SNeil Armstrong /*
178df7e3745SNeil Armstrong  * For GXL and GXM SoCs:
179df7e3745SNeil Armstrong  * USB Phy muxing between the DWC2 Device controller and the DWC3 Host
180df7e3745SNeil Armstrong  * controller is buggy when switching from Device to Host when USB port
181df7e3745SNeil Armstrong  * is unpopulated, it causes the DWC3 to hard crash.
182df7e3745SNeil Armstrong  * When populated (including OTG switching with ID pin), the switch works
183df7e3745SNeil Armstrong  * like a charm like on the G12A platforms.
184df7e3745SNeil Armstrong  * In order to still switch from Host to Device on an USB Type-A port,
185df7e3745SNeil Armstrong  * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
186df7e3745SNeil Armstrong  * controller from the port, but when used the DWC3 controller must be
187df7e3745SNeil Armstrong  * reset to recover usage of the port.
188df7e3745SNeil Armstrong  */
189df7e3745SNeil Armstrong 
190a9fc15e0SNeil Armstrong static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
191a9fc15e0SNeil Armstrong 	.otg_switch_supported = true,
192a9fc15e0SNeil Armstrong 	.otg_phy_host_port_disable = true,
193a9fc15e0SNeil Armstrong 	.clks = meson_gxl_clocks,
194a9fc15e0SNeil Armstrong 	.num_clks = ARRAY_SIZE(meson_g12a_clocks),
195a9fc15e0SNeil Armstrong 	.phy_names = meson_a1_phy_names,
196a9fc15e0SNeil Armstrong 	.num_phys = ARRAY_SIZE(meson_a1_phy_names),
197a9fc15e0SNeil Armstrong 	.setup_regmaps = dwc3_meson_gxl_setup_regmaps,
198a9fc15e0SNeil Armstrong 	.usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
199a9fc15e0SNeil Armstrong 	.set_phy_mode = dwc3_meson_gxl_set_phy_mode,
200a9fc15e0SNeil Armstrong 	.usb_init = dwc3_meson_gxl_usb_init,
201a9fc15e0SNeil Armstrong 	.usb_post_init = dwc3_meson_gxl_usb_post_init,
202a9fc15e0SNeil Armstrong };
203a9fc15e0SNeil Armstrong 
204a9fc15e0SNeil Armstrong static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
205a9fc15e0SNeil Armstrong 	.otg_switch_supported = true,
206a9fc15e0SNeil Armstrong 	.otg_phy_host_port_disable = true,
207a9fc15e0SNeil Armstrong 	.clks = meson_gxl_clocks,
208a9fc15e0SNeil Armstrong 	.num_clks = ARRAY_SIZE(meson_g12a_clocks),
209a9fc15e0SNeil Armstrong 	.phy_names = meson_gxm_phy_names,
210a9fc15e0SNeil Armstrong 	.num_phys = ARRAY_SIZE(meson_gxm_phy_names),
211a9fc15e0SNeil Armstrong 	.setup_regmaps = dwc3_meson_gxl_setup_regmaps,
212a9fc15e0SNeil Armstrong 	.usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
213a9fc15e0SNeil Armstrong 	.set_phy_mode = dwc3_meson_gxl_set_phy_mode,
214a9fc15e0SNeil Armstrong 	.usb_init = dwc3_meson_gxl_usb_init,
215a9fc15e0SNeil Armstrong 	.usb_post_init = dwc3_meson_gxl_usb_post_init,
216a9fc15e0SNeil Armstrong };
217a9fc15e0SNeil Armstrong 
2181e355f21SHanjie Lin static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
2191e355f21SHanjie Lin 	.otg_switch_supported = true,
2201e355f21SHanjie Lin 	.clks = meson_g12a_clocks,
2211e355f21SHanjie Lin 	.num_clks = ARRAY_SIZE(meson_g12a_clocks),
2225174564cSNeil Armstrong 	.phy_names = meson_g12a_phy_names,
2235174564cSNeil Armstrong 	.num_phys = ARRAY_SIZE(meson_g12a_phy_names),
224013af227SNeil Armstrong 	.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
22531306821SNeil Armstrong 	.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
22631306821SNeil Armstrong 	.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
2275b0ba0caSNeil Armstrong 	.usb_init = dwc3_meson_g12a_usb_init,
2281e355f21SHanjie Lin };
2291e355f21SHanjie Lin 
2301e355f21SHanjie Lin static struct dwc3_meson_g12a_drvdata a1_drvdata = {
2311e355f21SHanjie Lin 	.otg_switch_supported = false,
2321e355f21SHanjie Lin 	.clks = meson_a1_clocks,
2331e355f21SHanjie Lin 	.num_clks = ARRAY_SIZE(meson_a1_clocks),
2345174564cSNeil Armstrong 	.phy_names = meson_a1_phy_names,
2355174564cSNeil Armstrong 	.num_phys = ARRAY_SIZE(meson_a1_phy_names),
236013af227SNeil Armstrong 	.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
23731306821SNeil Armstrong 	.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
23831306821SNeil Armstrong 	.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
2395b0ba0caSNeil Armstrong 	.usb_init = dwc3_meson_g12a_usb_init,
2401e355f21SHanjie Lin };
2411e355f21SHanjie Lin 
242c9999337SNeil Armstrong struct dwc3_meson_g12a {
243c9999337SNeil Armstrong 	struct device		*dev;
244013af227SNeil Armstrong 	struct regmap		*u2p_regmap[PHY_COUNT];
245013af227SNeil Armstrong 	struct regmap		*usb_glue_regmap;
246c9999337SNeil Armstrong 	struct reset_control	*reset;
247c9999337SNeil Armstrong 	struct phy		*phys[PHY_COUNT];
248c9999337SNeil Armstrong 	enum usb_dr_mode	otg_mode;
249c9999337SNeil Armstrong 	enum phy_mode		otg_phy_mode;
250c9999337SNeil Armstrong 	unsigned int		usb2_ports;
251c9999337SNeil Armstrong 	unsigned int		usb3_ports;
252c9999337SNeil Armstrong 	struct regulator	*vbus;
253c9999337SNeil Armstrong 	struct usb_role_switch_desc switch_desc;
254c9999337SNeil Armstrong 	struct usb_role_switch	*role_switch;
2551e355f21SHanjie Lin 	const struct dwc3_meson_g12a_drvdata *drvdata;
256c9999337SNeil Armstrong };
257c9999337SNeil Armstrong 
258a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
259a9fc15e0SNeil Armstrong 					 int i, enum phy_mode mode)
260a9fc15e0SNeil Armstrong {
261a9fc15e0SNeil Armstrong 	return phy_set_mode(priv->phys[i], mode);
262a9fc15e0SNeil Armstrong }
263a9fc15e0SNeil Armstrong 
264a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
265a9fc15e0SNeil Armstrong 					enum phy_mode mode)
266a9fc15e0SNeil Armstrong {
267a9fc15e0SNeil Armstrong 	/* On GXL PHY must be started in device mode for DWC2 init */
268a9fc15e0SNeil Armstrong 	return priv->drvdata->set_phy_mode(priv, i,
269a9fc15e0SNeil Armstrong 				(i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE
270a9fc15e0SNeil Armstrong 						    : PHY_MODE_USB_HOST);
271a9fc15e0SNeil Armstrong }
272a9fc15e0SNeil Armstrong 
27331306821SNeil Armstrong static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
274c9999337SNeil Armstrong 					 int i, enum phy_mode mode)
275c9999337SNeil Armstrong {
276c9999337SNeil Armstrong 	if (mode == PHY_MODE_USB_HOST)
277013af227SNeil Armstrong 		regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
278c9999337SNeil Armstrong 				U2P_R0_HOST_DEVICE,
279c9999337SNeil Armstrong 				U2P_R0_HOST_DEVICE);
280c9999337SNeil Armstrong 	else
281013af227SNeil Armstrong 		regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
282c9999337SNeil Armstrong 				U2P_R0_HOST_DEVICE, 0);
28331306821SNeil Armstrong 
28431306821SNeil Armstrong 	return 0;
28531306821SNeil Armstrong }
28631306821SNeil Armstrong 
28731306821SNeil Armstrong static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
28831306821SNeil Armstrong 					 enum phy_mode mode)
28931306821SNeil Armstrong {
29031306821SNeil Armstrong 	int ret;
29131306821SNeil Armstrong 
29231306821SNeil Armstrong 	regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
29331306821SNeil Armstrong 			U2P_R0_POWER_ON_RESET,
29431306821SNeil Armstrong 			U2P_R0_POWER_ON_RESET);
29531306821SNeil Armstrong 
29631306821SNeil Armstrong 	if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
29731306821SNeil Armstrong 		regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
29831306821SNeil Armstrong 				   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
29931306821SNeil Armstrong 				   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
30031306821SNeil Armstrong 
30131306821SNeil Armstrong 		ret = priv->drvdata->set_phy_mode(priv, i, mode);
30231306821SNeil Armstrong 	} else
30331306821SNeil Armstrong 		ret = priv->drvdata->set_phy_mode(priv, i,
30431306821SNeil Armstrong 						  PHY_MODE_USB_HOST);
30531306821SNeil Armstrong 
30631306821SNeil Armstrong 	if (ret)
30731306821SNeil Armstrong 		return ret;
30831306821SNeil Armstrong 
30931306821SNeil Armstrong 	regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
31031306821SNeil Armstrong 			U2P_R0_POWER_ON_RESET, 0);
31131306821SNeil Armstrong 
31231306821SNeil Armstrong 	return 0;
313c9999337SNeil Armstrong }
314c9999337SNeil Armstrong 
3155b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
3165b0ba0caSNeil Armstrong 				     enum phy_mode mode)
317c9999337SNeil Armstrong {
31831306821SNeil Armstrong 	int i, ret;
319c9999337SNeil Armstrong 
3205174564cSNeil Armstrong 	for (i = 0; i < priv->drvdata->num_phys; ++i) {
321c9999337SNeil Armstrong 		if (!priv->phys[i])
322c9999337SNeil Armstrong 			continue;
323c9999337SNeil Armstrong 
3245174564cSNeil Armstrong 		if (!strstr(priv->drvdata->phy_names[i], "usb2"))
3255174564cSNeil Armstrong 			continue;
3265174564cSNeil Armstrong 
3275b0ba0caSNeil Armstrong 		ret = priv->drvdata->usb2_init_phy(priv, i, mode);
32831306821SNeil Armstrong 		if (ret)
32931306821SNeil Armstrong 			return ret;
330c9999337SNeil Armstrong 	}
331c9999337SNeil Armstrong 
332c9999337SNeil Armstrong 	return 0;
333c9999337SNeil Armstrong }
334c9999337SNeil Armstrong 
335c9999337SNeil Armstrong static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
336c9999337SNeil Armstrong {
337013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R3,
338c9999337SNeil Armstrong 			USB_R3_P30_SSC_RANGE_MASK |
339c9999337SNeil Armstrong 			USB_R3_P30_REF_SSP_EN,
340c9999337SNeil Armstrong 			USB_R3_P30_SSC_ENABLE |
341c9999337SNeil Armstrong 			FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
342c9999337SNeil Armstrong 			USB_R3_P30_REF_SSP_EN);
343c9999337SNeil Armstrong 	udelay(2);
344c9999337SNeil Armstrong 
345013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R2,
346c9999337SNeil Armstrong 			USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
347c9999337SNeil Armstrong 			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
348c9999337SNeil Armstrong 
349013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R2,
350c9999337SNeil Armstrong 			USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
351c9999337SNeil Armstrong 			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
352c9999337SNeil Armstrong 
353c9999337SNeil Armstrong 	udelay(2);
354c9999337SNeil Armstrong 
355013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R1,
356c9999337SNeil Armstrong 			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
357c9999337SNeil Armstrong 			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
358c9999337SNeil Armstrong 
359013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R1,
360c9999337SNeil Armstrong 			USB_R1_P30_PCS_TX_SWING_FULL_MASK,
361c9999337SNeil Armstrong 			FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
362c9999337SNeil Armstrong }
363c9999337SNeil Armstrong 
3645b0ba0caSNeil Armstrong static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
3655b0ba0caSNeil Armstrong 					       enum phy_mode mode)
366c9999337SNeil Armstrong {
3675b0ba0caSNeil Armstrong 	if (mode == PHY_MODE_USB_DEVICE) {
368df7e3745SNeil Armstrong 		if (priv->otg_mode != USB_DR_MODE_OTG &&
369df7e3745SNeil Armstrong 		    priv->drvdata->otg_phy_host_port_disable)
370df7e3745SNeil Armstrong 			/* Isolate the OTG PHY port from the Host Controller */
371df7e3745SNeil Armstrong 			regmap_update_bits(priv->usb_glue_regmap, USB_R1,
372df7e3745SNeil Armstrong 				USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
373df7e3745SNeil Armstrong 				FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
374df7e3745SNeil Armstrong 					   BIT(USB2_OTG_PHY)));
375df7e3745SNeil Armstrong 
376013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R0,
377c9999337SNeil Armstrong 				USB_R0_U2D_ACT, USB_R0_U2D_ACT);
378013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R0,
379c9999337SNeil Armstrong 				USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
380013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R4,
381c9999337SNeil Armstrong 				USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
382c9999337SNeil Armstrong 	} else {
383df7e3745SNeil Armstrong 		if (priv->otg_mode != USB_DR_MODE_OTG &&
384df7e3745SNeil Armstrong 		    priv->drvdata->otg_phy_host_port_disable) {
385df7e3745SNeil Armstrong 			regmap_update_bits(priv->usb_glue_regmap, USB_R1,
386df7e3745SNeil Armstrong 				USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
387df7e3745SNeil Armstrong 			msleep(500);
388df7e3745SNeil Armstrong 		}
389013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R0,
390c9999337SNeil Armstrong 				USB_R0_U2D_ACT, 0);
391013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R4,
392c9999337SNeil Armstrong 				USB_R4_P21_SLEEP_M0, 0);
393c9999337SNeil Armstrong 	}
394c9999337SNeil Armstrong }
395c9999337SNeil Armstrong 
3965b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
3975b0ba0caSNeil Armstrong 					 enum phy_mode mode)
398c9999337SNeil Armstrong {
399c9999337SNeil Armstrong 	int ret;
400c9999337SNeil Armstrong 
4015b0ba0caSNeil Armstrong 	ret = dwc3_meson_g12a_usb2_init(priv, mode);
402c9999337SNeil Armstrong 	if (ret)
403c9999337SNeil Armstrong 		return ret;
404c9999337SNeil Armstrong 
405013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R1,
406c9999337SNeil Armstrong 			USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
407c9999337SNeil Armstrong 			FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
408c9999337SNeil Armstrong 
409013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
410c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_0,
411c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_0);
412013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
413c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_1,
414c9999337SNeil Armstrong 			USB_R5_ID_DIG_EN_1);
415013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
416c9999337SNeil Armstrong 			USB_R5_ID_DIG_TH_MASK,
417c9999337SNeil Armstrong 			FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
418c9999337SNeil Armstrong 
419c9999337SNeil Armstrong 	/* If we have an actual SuperSpeed port, initialize it */
420c9999337SNeil Armstrong 	if (priv->usb3_ports)
421c9999337SNeil Armstrong 		dwc3_meson_g12a_usb3_init(priv);
422c9999337SNeil Armstrong 
4235b0ba0caSNeil Armstrong 	dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
424c9999337SNeil Armstrong 
425c9999337SNeil Armstrong 	return 0;
426c9999337SNeil Armstrong }
427c9999337SNeil Armstrong 
428013af227SNeil Armstrong static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
429013af227SNeil Armstrong 	.name = "usb-glue",
430c9999337SNeil Armstrong 	.reg_bits = 8,
431c9999337SNeil Armstrong 	.val_bits = 32,
432c9999337SNeil Armstrong 	.reg_stride = 4,
433c9999337SNeil Armstrong 	.max_register = USB_R5,
434c9999337SNeil Armstrong };
435c9999337SNeil Armstrong 
436c9999337SNeil Armstrong static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
437c9999337SNeil Armstrong {
4385174564cSNeil Armstrong 	const char *phy_name;
439c9999337SNeil Armstrong 	int i;
440c9999337SNeil Armstrong 
4415174564cSNeil Armstrong 	for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
4425174564cSNeil Armstrong 		phy_name = priv->drvdata->phy_names[i];
4435174564cSNeil Armstrong 		priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
444c9999337SNeil Armstrong 		if (!priv->phys[i])
445c9999337SNeil Armstrong 			continue;
446c9999337SNeil Armstrong 
447c9999337SNeil Armstrong 		if (IS_ERR(priv->phys[i]))
448c9999337SNeil Armstrong 			return PTR_ERR(priv->phys[i]);
449c9999337SNeil Armstrong 
4505174564cSNeil Armstrong 		if (strstr(phy_name, "usb3"))
451c9999337SNeil Armstrong 			priv->usb3_ports++;
452c9999337SNeil Armstrong 		else
453c9999337SNeil Armstrong 			priv->usb2_ports++;
454c9999337SNeil Armstrong 	}
455c9999337SNeil Armstrong 
456c9999337SNeil Armstrong 	dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports);
457c9999337SNeil Armstrong 	dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports);
458c9999337SNeil Armstrong 
459c9999337SNeil Armstrong 	return 0;
460c9999337SNeil Armstrong }
461c9999337SNeil Armstrong 
462c9999337SNeil Armstrong static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
463c9999337SNeil Armstrong {
464c9999337SNeil Armstrong 	u32 reg;
465c9999337SNeil Armstrong 
466013af227SNeil Armstrong 	regmap_read(priv->usb_glue_regmap, USB_R5, &reg);
467c9999337SNeil Armstrong 
468c9999337SNeil Armstrong 	if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
469c9999337SNeil Armstrong 		return PHY_MODE_USB_DEVICE;
470c9999337SNeil Armstrong 
471c9999337SNeil Armstrong 	return PHY_MODE_USB_HOST;
472c9999337SNeil Armstrong }
473c9999337SNeil Armstrong 
474c9999337SNeil Armstrong static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
475c9999337SNeil Armstrong 					enum phy_mode mode)
476c9999337SNeil Armstrong {
477c9999337SNeil Armstrong 	int ret;
478c9999337SNeil Armstrong 
4791e355f21SHanjie Lin 	if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY])
480c9999337SNeil Armstrong 		return -EINVAL;
481c9999337SNeil Armstrong 
482c9999337SNeil Armstrong 	if (mode == PHY_MODE_USB_HOST)
483c9999337SNeil Armstrong 		dev_info(priv->dev, "switching to Host Mode\n");
484c9999337SNeil Armstrong 	else
485c9999337SNeil Armstrong 		dev_info(priv->dev, "switching to Device Mode\n");
486c9999337SNeil Armstrong 
487c9999337SNeil Armstrong 	if (priv->vbus) {
488c9999337SNeil Armstrong 		if (mode == PHY_MODE_USB_DEVICE)
489c9999337SNeil Armstrong 			ret = regulator_disable(priv->vbus);
490c9999337SNeil Armstrong 		else
491c9999337SNeil Armstrong 			ret = regulator_enable(priv->vbus);
492c9999337SNeil Armstrong 		if (ret)
493c9999337SNeil Armstrong 			return ret;
494c9999337SNeil Armstrong 	}
495c9999337SNeil Armstrong 
496c9999337SNeil Armstrong 	priv->otg_phy_mode = mode;
497c9999337SNeil Armstrong 
49831306821SNeil Armstrong 	ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
49931306821SNeil Armstrong 	if (ret)
50031306821SNeil Armstrong 		return ret;
501c9999337SNeil Armstrong 
5025b0ba0caSNeil Armstrong 	dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
503c9999337SNeil Armstrong 
504c9999337SNeil Armstrong 	return 0;
505c9999337SNeil Armstrong }
506c9999337SNeil Armstrong 
507bce3052fSHeikki Krogerus static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
508bce3052fSHeikki Krogerus 				    enum usb_role role)
509c9999337SNeil Armstrong {
510bce3052fSHeikki Krogerus 	struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
511c9999337SNeil Armstrong 	enum phy_mode mode;
512c9999337SNeil Armstrong 
513c9999337SNeil Armstrong 	if (role == USB_ROLE_NONE)
514c9999337SNeil Armstrong 		return 0;
515c9999337SNeil Armstrong 
516c9999337SNeil Armstrong 	mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST
517c9999337SNeil Armstrong 				       : PHY_MODE_USB_DEVICE;
518c9999337SNeil Armstrong 
519c9999337SNeil Armstrong 	if (mode == priv->otg_phy_mode)
520c9999337SNeil Armstrong 		return 0;
521c9999337SNeil Armstrong 
522df7e3745SNeil Armstrong 	if (priv->drvdata->otg_phy_host_port_disable)
523df7e3745SNeil Armstrong 		dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
524df7e3745SNeil Armstrong 					 "SoC, when manual switching from "\
525df7e3745SNeil Armstrong 					 "Host to device, DWC3 controller "\
526df7e3745SNeil Armstrong 					 "will need to be resetted in order "\
527df7e3745SNeil Armstrong 					 "to recover usage of the Host port");
528df7e3745SNeil Armstrong 
529c9999337SNeil Armstrong 	return dwc3_meson_g12a_otg_mode_set(priv, mode);
530c9999337SNeil Armstrong }
531c9999337SNeil Armstrong 
532bce3052fSHeikki Krogerus static enum usb_role dwc3_meson_g12a_role_get(struct usb_role_switch *sw)
533c9999337SNeil Armstrong {
534bce3052fSHeikki Krogerus 	struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
535c9999337SNeil Armstrong 
536c9999337SNeil Armstrong 	return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
537c9999337SNeil Armstrong 		USB_ROLE_HOST : USB_ROLE_DEVICE;
538c9999337SNeil Armstrong }
539c9999337SNeil Armstrong 
540f90db107SNeil Armstrong static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data)
541f90db107SNeil Armstrong {
542f90db107SNeil Armstrong 	struct dwc3_meson_g12a *priv = data;
543f90db107SNeil Armstrong 	enum phy_mode otg_id;
544f90db107SNeil Armstrong 
545f90db107SNeil Armstrong 	otg_id = dwc3_meson_g12a_get_id(priv);
546f90db107SNeil Armstrong 	if (otg_id != priv->otg_phy_mode) {
547f90db107SNeil Armstrong 		if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
548f90db107SNeil Armstrong 			dev_warn(priv->dev, "Failed to switch OTG mode\n");
549f90db107SNeil Armstrong 	}
550f90db107SNeil Armstrong 
551013af227SNeil Armstrong 	regmap_update_bits(priv->usb_glue_regmap, USB_R5,
552013af227SNeil Armstrong 			   USB_R5_ID_DIG_IRQ, 0);
553f90db107SNeil Armstrong 
554f90db107SNeil Armstrong 	return IRQ_HANDLED;
555f90db107SNeil Armstrong }
556f90db107SNeil Armstrong 
557c9999337SNeil Armstrong static struct device *dwc3_meson_g12_find_child(struct device *dev,
558c9999337SNeil Armstrong 						const char *compatible)
559c9999337SNeil Armstrong {
560c9999337SNeil Armstrong 	struct platform_device *pdev;
561c9999337SNeil Armstrong 	struct device_node *np;
562c9999337SNeil Armstrong 
563c9999337SNeil Armstrong 	np = of_get_compatible_child(dev->of_node, compatible);
564c9999337SNeil Armstrong 	if (!np)
565c9999337SNeil Armstrong 		return NULL;
566c9999337SNeil Armstrong 
567c9999337SNeil Armstrong 	pdev = of_find_device_by_node(np);
568c9999337SNeil Armstrong 	of_node_put(np);
569c9999337SNeil Armstrong 	if (!pdev)
570c9999337SNeil Armstrong 		return NULL;
571c9999337SNeil Armstrong 
572c9999337SNeil Armstrong 	return &pdev->dev;
573c9999337SNeil Armstrong }
574c9999337SNeil Armstrong 
5751e355f21SHanjie Lin static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
5761e355f21SHanjie Lin 				    struct dwc3_meson_g12a *priv)
577c9999337SNeil Armstrong {
578c9999337SNeil Armstrong 	enum phy_mode otg_id;
5791e355f21SHanjie Lin 	int ret, irq;
5801e355f21SHanjie Lin 	struct device *dev = &pdev->dev;
581c9999337SNeil Armstrong 
5821e355f21SHanjie Lin 	if (!priv->drvdata->otg_switch_supported)
5831e355f21SHanjie Lin 		return 0;
584c9999337SNeil Armstrong 
585f90db107SNeil Armstrong 	if (priv->otg_mode == USB_DR_MODE_OTG) {
586f90db107SNeil Armstrong 		/* Ack irq before registering */
587013af227SNeil Armstrong 		regmap_update_bits(priv->usb_glue_regmap, USB_R5,
588f90db107SNeil Armstrong 				   USB_R5_ID_DIG_IRQ, 0);
589f90db107SNeil Armstrong 
590f90db107SNeil Armstrong 		irq = platform_get_irq(pdev, 0);
591f90db107SNeil Armstrong 		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
592f90db107SNeil Armstrong 						dwc3_meson_g12a_irq_thread,
593f90db107SNeil Armstrong 						IRQF_ONESHOT, pdev->name, priv);
594f90db107SNeil Armstrong 		if (ret)
595f90db107SNeil Armstrong 			return ret;
596f90db107SNeil Armstrong 	}
597f90db107SNeil Armstrong 
598c9999337SNeil Armstrong 	/* Setup OTG mode corresponding to the ID pin */
599c9999337SNeil Armstrong 	if (priv->otg_mode == USB_DR_MODE_OTG) {
600c9999337SNeil Armstrong 		otg_id = dwc3_meson_g12a_get_id(priv);
601c9999337SNeil Armstrong 		if (otg_id != priv->otg_phy_mode) {
602c9999337SNeil Armstrong 			if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
603c9999337SNeil Armstrong 				dev_warn(dev, "Failed to switch OTG mode\n");
604c9999337SNeil Armstrong 		}
605c9999337SNeil Armstrong 	}
606c9999337SNeil Armstrong 
607c9999337SNeil Armstrong 	/* Setup role switcher */
608c9999337SNeil Armstrong 	priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
609c9999337SNeil Armstrong 								"snps,dwc3");
610c9999337SNeil Armstrong 	priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
611c9999337SNeil Armstrong 	priv->switch_desc.allow_userspace_control = true;
612c9999337SNeil Armstrong 	priv->switch_desc.set = dwc3_meson_g12a_role_set;
613c9999337SNeil Armstrong 	priv->switch_desc.get = dwc3_meson_g12a_role_get;
614bce3052fSHeikki Krogerus 	priv->switch_desc.driver_data = priv;
615c9999337SNeil Armstrong 
616c9999337SNeil Armstrong 	priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
617c9999337SNeil Armstrong 	if (IS_ERR(priv->role_switch))
618c9999337SNeil Armstrong 		dev_warn(dev, "Unable to register Role Switch\n");
619c9999337SNeil Armstrong 
620238d7602SNathan Chancellor 	return 0;
6211e355f21SHanjie Lin }
6221e355f21SHanjie Lin 
623a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
624a9fc15e0SNeil Armstrong 					void __iomem *base)
625a9fc15e0SNeil Armstrong {
626a9fc15e0SNeil Armstrong 	/* GXL controls the PHY mode in the PHY registers unlike G12A */
627a9fc15e0SNeil Armstrong 	priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
628a9fc15e0SNeil Armstrong 					&phy_meson_g12a_usb_glue_regmap_conf);
629a9fc15e0SNeil Armstrong 	if (IS_ERR(priv->usb_glue_regmap))
630a9fc15e0SNeil Armstrong 		return PTR_ERR(priv->usb_glue_regmap);
631a9fc15e0SNeil Armstrong 
632a9fc15e0SNeil Armstrong 	return 0;
633a9fc15e0SNeil Armstrong }
634a9fc15e0SNeil Armstrong 
635013af227SNeil Armstrong static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
636013af227SNeil Armstrong 					 void __iomem *base)
637013af227SNeil Armstrong {
638013af227SNeil Armstrong 	int i;
639013af227SNeil Armstrong 
640013af227SNeil Armstrong 	priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
641013af227SNeil Armstrong 					base + G12A_GLUE_OFFSET,
642013af227SNeil Armstrong 					&phy_meson_g12a_usb_glue_regmap_conf);
643013af227SNeil Armstrong 	if (IS_ERR(priv->usb_glue_regmap))
644013af227SNeil Armstrong 		return PTR_ERR(priv->usb_glue_regmap);
645013af227SNeil Armstrong 
646013af227SNeil Armstrong 	/* Create a regmap for each USB2 PHY control register set */
647013af227SNeil Armstrong 	for (i = 0; i < priv->usb2_ports; i++) {
648013af227SNeil Armstrong 		struct regmap_config u2p_regmap_config = {
649013af227SNeil Armstrong 			.reg_bits = 8,
650013af227SNeil Armstrong 			.val_bits = 32,
651013af227SNeil Armstrong 			.reg_stride = 4,
652013af227SNeil Armstrong 			.max_register = U2P_R1,
653013af227SNeil Armstrong 		};
654013af227SNeil Armstrong 
655013af227SNeil Armstrong 		u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
656013af227SNeil Armstrong 							"u2p-%d", i);
657013af227SNeil Armstrong 		if (!u2p_regmap_config.name)
658013af227SNeil Armstrong 			return -ENOMEM;
659013af227SNeil Armstrong 
660013af227SNeil Armstrong 		priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
661013af227SNeil Armstrong 						base + (i * U2P_REG_SIZE),
662013af227SNeil Armstrong 						&u2p_regmap_config);
663013af227SNeil Armstrong 		if (IS_ERR(priv->u2p_regmap[i]))
664013af227SNeil Armstrong 			return PTR_ERR(priv->u2p_regmap[i]);
665013af227SNeil Armstrong 	}
666013af227SNeil Armstrong 
667013af227SNeil Armstrong 	return 0;
668013af227SNeil Armstrong }
669013af227SNeil Armstrong 
6705b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
6715b0ba0caSNeil Armstrong {
6725b0ba0caSNeil Armstrong 	return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
6735b0ba0caSNeil Armstrong }
6745b0ba0caSNeil Armstrong 
675a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv)
676a9fc15e0SNeil Armstrong {
677a9fc15e0SNeil Armstrong 	return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE);
678a9fc15e0SNeil Armstrong }
679a9fc15e0SNeil Armstrong 
680a9fc15e0SNeil Armstrong static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv)
681a9fc15e0SNeil Armstrong {
682a9fc15e0SNeil Armstrong 	int ret;
683a9fc15e0SNeil Armstrong 
684a9fc15e0SNeil Armstrong 	ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY,
685a9fc15e0SNeil Armstrong 					  priv->otg_phy_mode);
686a9fc15e0SNeil Armstrong 	if (ret)
687a9fc15e0SNeil Armstrong 		return ret;
688a9fc15e0SNeil Armstrong 
689a9fc15e0SNeil Armstrong 	dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode);
690a9fc15e0SNeil Armstrong 
691a9fc15e0SNeil Armstrong 	return 0;
692a9fc15e0SNeil Armstrong }
693a9fc15e0SNeil Armstrong 
6941e355f21SHanjie Lin static int dwc3_meson_g12a_probe(struct platform_device *pdev)
6951e355f21SHanjie Lin {
6961e355f21SHanjie Lin 	struct dwc3_meson_g12a	*priv;
6971e355f21SHanjie Lin 	struct device		*dev = &pdev->dev;
6981e355f21SHanjie Lin 	struct device_node	*np = dev->of_node;
6991e355f21SHanjie Lin 	void __iomem *base;
7001e355f21SHanjie Lin 	int ret, i;
7011e355f21SHanjie Lin 
7021e355f21SHanjie Lin 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
7031e355f21SHanjie Lin 	if (!priv)
7041e355f21SHanjie Lin 		return -ENOMEM;
7051e355f21SHanjie Lin 
7061e355f21SHanjie Lin 	base = devm_platform_ioremap_resource(pdev, 0);
7071e355f21SHanjie Lin 	if (IS_ERR(base))
7081e355f21SHanjie Lin 		return PTR_ERR(base);
7091e355f21SHanjie Lin 
710013af227SNeil Armstrong 	priv->drvdata = of_device_get_match_data(&pdev->dev);
711013af227SNeil Armstrong 	priv->dev = dev;
7121e355f21SHanjie Lin 
7131e355f21SHanjie Lin 	priv->vbus = devm_regulator_get_optional(dev, "vbus");
7141e355f21SHanjie Lin 	if (IS_ERR(priv->vbus)) {
7151e355f21SHanjie Lin 		if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
7161e355f21SHanjie Lin 			return PTR_ERR(priv->vbus);
7171e355f21SHanjie Lin 		priv->vbus = NULL;
7181e355f21SHanjie Lin 	}
7191e355f21SHanjie Lin 
7201e355f21SHanjie Lin 	ret = devm_clk_bulk_get(dev,
7211e355f21SHanjie Lin 				priv->drvdata->num_clks,
7221e355f21SHanjie Lin 				priv->drvdata->clks);
7231e355f21SHanjie Lin 	if (ret)
7241e355f21SHanjie Lin 		return ret;
7251e355f21SHanjie Lin 
7261e355f21SHanjie Lin 	ret = clk_bulk_prepare_enable(priv->drvdata->num_clks,
7271e355f21SHanjie Lin 				      priv->drvdata->clks);
7281e355f21SHanjie Lin 	if (ret)
7291e355f21SHanjie Lin 		return ret;
7301e355f21SHanjie Lin 
7311e355f21SHanjie Lin 	platform_set_drvdata(pdev, priv);
7321e355f21SHanjie Lin 
7336d9fa35aSNeil Armstrong 	priv->reset = devm_reset_control_get_shared(dev, NULL);
7341e355f21SHanjie Lin 	if (IS_ERR(priv->reset)) {
7351e355f21SHanjie Lin 		ret = PTR_ERR(priv->reset);
7361e355f21SHanjie Lin 		dev_err(dev, "failed to get device reset, err=%d\n", ret);
737be8c1001SMartin Blumenstingl 		goto err_disable_clks;
7381e355f21SHanjie Lin 	}
7391e355f21SHanjie Lin 
7401e355f21SHanjie Lin 	ret = reset_control_reset(priv->reset);
7411e355f21SHanjie Lin 	if (ret)
7421e355f21SHanjie Lin 		goto err_disable_clks;
7431e355f21SHanjie Lin 
7441e355f21SHanjie Lin 	ret = dwc3_meson_g12a_get_phys(priv);
7451e355f21SHanjie Lin 	if (ret)
7461e355f21SHanjie Lin 		goto err_disable_clks;
7471e355f21SHanjie Lin 
748347052e3SMartin Blumenstingl 	ret = priv->drvdata->setup_regmaps(priv, base);
749347052e3SMartin Blumenstingl 	if (ret)
750347052e3SMartin Blumenstingl 		return ret;
751347052e3SMartin Blumenstingl 
7521e355f21SHanjie Lin 	if (priv->vbus) {
7531e355f21SHanjie Lin 		ret = regulator_enable(priv->vbus);
7541e355f21SHanjie Lin 		if (ret)
7551e355f21SHanjie Lin 			goto err_disable_clks;
7561e355f21SHanjie Lin 	}
7571e355f21SHanjie Lin 
7581e355f21SHanjie Lin 	/* Get dr_mode */
7591e355f21SHanjie Lin 	priv->otg_mode = usb_get_dr_mode(dev);
7601e355f21SHanjie Lin 
7615b0ba0caSNeil Armstrong 	if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
7625b0ba0caSNeil Armstrong 		priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
7635b0ba0caSNeil Armstrong 	else
7645b0ba0caSNeil Armstrong 		priv->otg_phy_mode = PHY_MODE_USB_HOST;
7655b0ba0caSNeil Armstrong 
7665b0ba0caSNeil Armstrong 	ret = priv->drvdata->usb_init(priv);
7678f5bc1ecSNeil Armstrong 	if (ret)
7688f5bc1ecSNeil Armstrong 		goto err_disable_clks;
7691e355f21SHanjie Lin 
7701e355f21SHanjie Lin 	/* Init PHYs */
7711e355f21SHanjie Lin 	for (i = 0 ; i < PHY_COUNT ; ++i) {
7721e355f21SHanjie Lin 		ret = phy_init(priv->phys[i]);
7731e355f21SHanjie Lin 		if (ret)
7741e355f21SHanjie Lin 			goto err_disable_clks;
7751e355f21SHanjie Lin 	}
7761e355f21SHanjie Lin 
7771e355f21SHanjie Lin 	/* Set PHY Power */
7781e355f21SHanjie Lin 	for (i = 0 ; i < PHY_COUNT ; ++i) {
7791e355f21SHanjie Lin 		ret = phy_power_on(priv->phys[i]);
7801e355f21SHanjie Lin 		if (ret)
7811e355f21SHanjie Lin 			goto err_phys_exit;
7821e355f21SHanjie Lin 	}
7831e355f21SHanjie Lin 
7845b0ba0caSNeil Armstrong 	if (priv->drvdata->usb_post_init) {
7855b0ba0caSNeil Armstrong 		ret = priv->drvdata->usb_post_init(priv);
7865b0ba0caSNeil Armstrong 		if (ret)
7875b0ba0caSNeil Armstrong 			goto err_phys_power;
7885b0ba0caSNeil Armstrong 	}
7895b0ba0caSNeil Armstrong 
7901e355f21SHanjie Lin 	ret = of_platform_populate(np, NULL, NULL, dev);
7911e355f21SHanjie Lin 	if (ret)
7921e355f21SHanjie Lin 		goto err_phys_power;
7931e355f21SHanjie Lin 
7941e355f21SHanjie Lin 	ret = dwc3_meson_g12a_otg_init(pdev, priv);
7951e355f21SHanjie Lin 	if (ret)
7961e355f21SHanjie Lin 		goto err_phys_power;
7971e355f21SHanjie Lin 
798c9999337SNeil Armstrong 	pm_runtime_set_active(dev);
799c9999337SNeil Armstrong 	pm_runtime_enable(dev);
800c9999337SNeil Armstrong 	pm_runtime_get_sync(dev);
801c9999337SNeil Armstrong 
802c9999337SNeil Armstrong 	return 0;
803c9999337SNeil Armstrong 
804c9999337SNeil Armstrong err_phys_power:
805c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i)
806c9999337SNeil Armstrong 		phy_power_off(priv->phys[i]);
807c9999337SNeil Armstrong 
808c9999337SNeil Armstrong err_phys_exit:
809c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i)
810c9999337SNeil Armstrong 		phy_exit(priv->phys[i]);
811c9999337SNeil Armstrong 
8121e355f21SHanjie Lin err_disable_clks:
8131e355f21SHanjie Lin 	clk_bulk_disable_unprepare(priv->drvdata->num_clks,
8141e355f21SHanjie Lin 				   priv->drvdata->clks);
8151e355f21SHanjie Lin 
816c9999337SNeil Armstrong 	return ret;
817c9999337SNeil Armstrong }
818c9999337SNeil Armstrong 
819c9999337SNeil Armstrong static int dwc3_meson_g12a_remove(struct platform_device *pdev)
820c9999337SNeil Armstrong {
821c9999337SNeil Armstrong 	struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
822c9999337SNeil Armstrong 	struct device *dev = &pdev->dev;
823c9999337SNeil Armstrong 	int i;
824c9999337SNeil Armstrong 
8251e355f21SHanjie Lin 	if (priv->drvdata->otg_switch_supported)
826c9999337SNeil Armstrong 		usb_role_switch_unregister(priv->role_switch);
827c9999337SNeil Armstrong 
828c9999337SNeil Armstrong 	of_platform_depopulate(dev);
829c9999337SNeil Armstrong 
830c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
831c9999337SNeil Armstrong 		phy_power_off(priv->phys[i]);
832c9999337SNeil Armstrong 		phy_exit(priv->phys[i]);
833c9999337SNeil Armstrong 	}
834c9999337SNeil Armstrong 
835c9999337SNeil Armstrong 	pm_runtime_disable(dev);
836c9999337SNeil Armstrong 	pm_runtime_put_noidle(dev);
837c9999337SNeil Armstrong 	pm_runtime_set_suspended(dev);
838c9999337SNeil Armstrong 
8391e355f21SHanjie Lin 	clk_bulk_disable_unprepare(priv->drvdata->num_clks,
8401e355f21SHanjie Lin 				   priv->drvdata->clks);
8411e355f21SHanjie Lin 
842c9999337SNeil Armstrong 	return 0;
843c9999337SNeil Armstrong }
844c9999337SNeil Armstrong 
845c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
846c9999337SNeil Armstrong {
847c9999337SNeil Armstrong 	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
848c9999337SNeil Armstrong 
8491e355f21SHanjie Lin 	clk_bulk_disable_unprepare(priv->drvdata->num_clks,
8501e355f21SHanjie Lin 				   priv->drvdata->clks);
851c9999337SNeil Armstrong 
852c9999337SNeil Armstrong 	return 0;
853c9999337SNeil Armstrong }
854c9999337SNeil Armstrong 
855c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
856c9999337SNeil Armstrong {
857c9999337SNeil Armstrong 	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
858c9999337SNeil Armstrong 
8591e355f21SHanjie Lin 	return clk_bulk_prepare_enable(priv->drvdata->num_clks,
8601e355f21SHanjie Lin 				       priv->drvdata->clks);
861c9999337SNeil Armstrong }
862c9999337SNeil Armstrong 
863c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
864c9999337SNeil Armstrong {
865c9999337SNeil Armstrong 	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
8661cf084d1SNeil Armstrong 	int i, ret;
8671cf084d1SNeil Armstrong 
8681cf084d1SNeil Armstrong 	if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
8691cf084d1SNeil Armstrong 		ret = regulator_disable(priv->vbus);
8701cf084d1SNeil Armstrong 		if (ret)
8711cf084d1SNeil Armstrong 			return ret;
8721cf084d1SNeil Armstrong 	}
873c9999337SNeil Armstrong 
874c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
875c9999337SNeil Armstrong 		phy_power_off(priv->phys[i]);
876c9999337SNeil Armstrong 		phy_exit(priv->phys[i]);
877c9999337SNeil Armstrong 	}
878c9999337SNeil Armstrong 
879c9999337SNeil Armstrong 	reset_control_assert(priv->reset);
880c9999337SNeil Armstrong 
881c9999337SNeil Armstrong 	return 0;
882c9999337SNeil Armstrong }
883c9999337SNeil Armstrong 
884c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
885c9999337SNeil Armstrong {
886c9999337SNeil Armstrong 	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
887c9999337SNeil Armstrong 	int i, ret;
888c9999337SNeil Armstrong 
889c9999337SNeil Armstrong 	reset_control_deassert(priv->reset);
890c9999337SNeil Armstrong 
8915b0ba0caSNeil Armstrong 	ret = priv->drvdata->usb_init(priv);
8925b0ba0caSNeil Armstrong 	if (ret)
8935b0ba0caSNeil Armstrong 		return ret;
894c9999337SNeil Armstrong 
895c9999337SNeil Armstrong 	/* Init PHYs */
896c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
897c9999337SNeil Armstrong 		ret = phy_init(priv->phys[i]);
898c9999337SNeil Armstrong 		if (ret)
899c9999337SNeil Armstrong 			return ret;
900c9999337SNeil Armstrong 	}
901c9999337SNeil Armstrong 
902c9999337SNeil Armstrong 	/* Set PHY Power */
903c9999337SNeil Armstrong 	for (i = 0 ; i < PHY_COUNT ; ++i) {
904c9999337SNeil Armstrong 		ret = phy_power_on(priv->phys[i]);
905c9999337SNeil Armstrong 		if (ret)
906c9999337SNeil Armstrong 			return ret;
907c9999337SNeil Armstrong 	}
908c9999337SNeil Armstrong 
9091cf084d1SNeil Armstrong        if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
9101cf084d1SNeil Armstrong                ret = regulator_enable(priv->vbus);
9111cf084d1SNeil Armstrong 		if (ret)
9121cf084d1SNeil Armstrong 			return ret;
9131cf084d1SNeil Armstrong 	}
9141cf084d1SNeil Armstrong 
915c9999337SNeil Armstrong 	return 0;
916c9999337SNeil Armstrong }
917c9999337SNeil Armstrong 
918c9999337SNeil Armstrong static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
919c9999337SNeil Armstrong 	SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
920c9999337SNeil Armstrong 	SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
921c9999337SNeil Armstrong 			   dwc3_meson_g12a_runtime_resume, NULL)
922c9999337SNeil Armstrong };
923c9999337SNeil Armstrong 
924c9999337SNeil Armstrong static const struct of_device_id dwc3_meson_g12a_match[] = {
9251e355f21SHanjie Lin 	{
926a9fc15e0SNeil Armstrong 		.compatible = "amlogic,meson-gxl-usb-ctrl",
927a9fc15e0SNeil Armstrong 		.data = &gxl_drvdata,
928a9fc15e0SNeil Armstrong 	},
929a9fc15e0SNeil Armstrong 	{
930a9fc15e0SNeil Armstrong 		.compatible = "amlogic,meson-gxm-usb-ctrl",
931a9fc15e0SNeil Armstrong 		.data = &gxm_drvdata,
932a9fc15e0SNeil Armstrong 	},
933a9fc15e0SNeil Armstrong 	{
9341e355f21SHanjie Lin 		.compatible = "amlogic,meson-g12a-usb-ctrl",
9351e355f21SHanjie Lin 		.data = &g12a_drvdata,
9361e355f21SHanjie Lin 	},
9371e355f21SHanjie Lin 	{
9381e355f21SHanjie Lin 		.compatible = "amlogic,meson-a1-usb-ctrl",
9391e355f21SHanjie Lin 		.data = &a1_drvdata,
9401e355f21SHanjie Lin 	},
941c9999337SNeil Armstrong 	{ /* Sentinel */ }
942c9999337SNeil Armstrong };
943c9999337SNeil Armstrong MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
944c9999337SNeil Armstrong 
945c9999337SNeil Armstrong static struct platform_driver dwc3_meson_g12a_driver = {
946c9999337SNeil Armstrong 	.probe		= dwc3_meson_g12a_probe,
947c9999337SNeil Armstrong 	.remove		= dwc3_meson_g12a_remove,
948c9999337SNeil Armstrong 	.driver		= {
949c9999337SNeil Armstrong 		.name	= "dwc3-meson-g12a",
950c9999337SNeil Armstrong 		.of_match_table = dwc3_meson_g12a_match,
951c9999337SNeil Armstrong 		.pm	= &dwc3_meson_g12a_dev_pm_ops,
952c9999337SNeil Armstrong 	},
953c9999337SNeil Armstrong };
954c9999337SNeil Armstrong 
955c9999337SNeil Armstrong module_platform_driver(dwc3_meson_g12a_driver);
956c9999337SNeil Armstrong MODULE_LICENSE("GPL v2");
957c9999337SNeil Armstrong MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
958c9999337SNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
959