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