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, ®); 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