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; 1341e355f21SHanjie Lin struct clk_bulk_data *clks; 1351e355f21SHanjie Lin int num_clks; 1365174564cSNeil Armstrong const char **phy_names; 1375174564cSNeil Armstrong int num_phys; 138013af227SNeil Armstrong int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base); 13931306821SNeil Armstrong int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i, 14031306821SNeil Armstrong enum phy_mode mode); 14131306821SNeil Armstrong int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i, 14231306821SNeil Armstrong enum phy_mode mode); 1435b0ba0caSNeil Armstrong int (*usb_init)(struct dwc3_meson_g12a *priv); 1445b0ba0caSNeil Armstrong int (*usb_post_init)(struct dwc3_meson_g12a *priv); 1451e355f21SHanjie Lin }; 1461e355f21SHanjie Lin 147013af227SNeil Armstrong static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv, 148013af227SNeil Armstrong void __iomem *base); 149013af227SNeil Armstrong 15031306821SNeil Armstrong static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i, 15131306821SNeil Armstrong enum phy_mode mode); 15231306821SNeil Armstrong 15331306821SNeil Armstrong static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv, 15431306821SNeil Armstrong int i, enum phy_mode mode); 15531306821SNeil Armstrong 1565b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv); 1575b0ba0caSNeil Armstrong 1581e355f21SHanjie Lin static struct dwc3_meson_g12a_drvdata g12a_drvdata = { 1591e355f21SHanjie Lin .otg_switch_supported = true, 1601e355f21SHanjie Lin .clks = meson_g12a_clocks, 1611e355f21SHanjie Lin .num_clks = ARRAY_SIZE(meson_g12a_clocks), 1625174564cSNeil Armstrong .phy_names = meson_g12a_phy_names, 1635174564cSNeil Armstrong .num_phys = ARRAY_SIZE(meson_g12a_phy_names), 164013af227SNeil Armstrong .setup_regmaps = dwc3_meson_g12a_setup_regmaps, 16531306821SNeil Armstrong .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy, 16631306821SNeil Armstrong .set_phy_mode = dwc3_meson_g12a_set_phy_mode, 1675b0ba0caSNeil Armstrong .usb_init = dwc3_meson_g12a_usb_init, 1681e355f21SHanjie Lin }; 1691e355f21SHanjie Lin 1701e355f21SHanjie Lin static struct dwc3_meson_g12a_drvdata a1_drvdata = { 1711e355f21SHanjie Lin .otg_switch_supported = false, 1721e355f21SHanjie Lin .clks = meson_a1_clocks, 1731e355f21SHanjie Lin .num_clks = ARRAY_SIZE(meson_a1_clocks), 1745174564cSNeil Armstrong .phy_names = meson_a1_phy_names, 1755174564cSNeil Armstrong .num_phys = ARRAY_SIZE(meson_a1_phy_names), 176013af227SNeil Armstrong .setup_regmaps = dwc3_meson_g12a_setup_regmaps, 17731306821SNeil Armstrong .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy, 17831306821SNeil Armstrong .set_phy_mode = dwc3_meson_g12a_set_phy_mode, 1795b0ba0caSNeil Armstrong .usb_init = dwc3_meson_g12a_usb_init, 1801e355f21SHanjie Lin }; 1811e355f21SHanjie Lin 182c9999337SNeil Armstrong struct dwc3_meson_g12a { 183c9999337SNeil Armstrong struct device *dev; 184013af227SNeil Armstrong struct regmap *u2p_regmap[PHY_COUNT]; 185013af227SNeil Armstrong struct regmap *usb_glue_regmap; 186c9999337SNeil Armstrong struct reset_control *reset; 187c9999337SNeil Armstrong struct phy *phys[PHY_COUNT]; 188c9999337SNeil Armstrong enum usb_dr_mode otg_mode; 189c9999337SNeil Armstrong enum phy_mode otg_phy_mode; 190c9999337SNeil Armstrong unsigned int usb2_ports; 191c9999337SNeil Armstrong unsigned int usb3_ports; 192c9999337SNeil Armstrong struct regulator *vbus; 193c9999337SNeil Armstrong struct usb_role_switch_desc switch_desc; 194c9999337SNeil Armstrong struct usb_role_switch *role_switch; 1951e355f21SHanjie Lin const struct dwc3_meson_g12a_drvdata *drvdata; 196c9999337SNeil Armstrong }; 197c9999337SNeil Armstrong 19831306821SNeil Armstrong static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv, 199c9999337SNeil Armstrong int i, enum phy_mode mode) 200c9999337SNeil Armstrong { 201c9999337SNeil Armstrong if (mode == PHY_MODE_USB_HOST) 202013af227SNeil Armstrong regmap_update_bits(priv->u2p_regmap[i], U2P_R0, 203c9999337SNeil Armstrong U2P_R0_HOST_DEVICE, 204c9999337SNeil Armstrong U2P_R0_HOST_DEVICE); 205c9999337SNeil Armstrong else 206013af227SNeil Armstrong regmap_update_bits(priv->u2p_regmap[i], U2P_R0, 207c9999337SNeil Armstrong U2P_R0_HOST_DEVICE, 0); 20831306821SNeil Armstrong 20931306821SNeil Armstrong return 0; 21031306821SNeil Armstrong } 21131306821SNeil Armstrong 21231306821SNeil Armstrong static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i, 21331306821SNeil Armstrong enum phy_mode mode) 21431306821SNeil Armstrong { 21531306821SNeil Armstrong int ret; 21631306821SNeil Armstrong 21731306821SNeil Armstrong regmap_update_bits(priv->u2p_regmap[i], U2P_R0, 21831306821SNeil Armstrong U2P_R0_POWER_ON_RESET, 21931306821SNeil Armstrong U2P_R0_POWER_ON_RESET); 22031306821SNeil Armstrong 22131306821SNeil Armstrong if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) { 22231306821SNeil Armstrong regmap_update_bits(priv->u2p_regmap[i], U2P_R0, 22331306821SNeil Armstrong U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, 22431306821SNeil Armstrong U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); 22531306821SNeil Armstrong 22631306821SNeil Armstrong ret = priv->drvdata->set_phy_mode(priv, i, mode); 22731306821SNeil Armstrong } else 22831306821SNeil Armstrong ret = priv->drvdata->set_phy_mode(priv, i, 22931306821SNeil Armstrong PHY_MODE_USB_HOST); 23031306821SNeil Armstrong 23131306821SNeil Armstrong if (ret) 23231306821SNeil Armstrong return ret; 23331306821SNeil Armstrong 23431306821SNeil Armstrong regmap_update_bits(priv->u2p_regmap[i], U2P_R0, 23531306821SNeil Armstrong U2P_R0_POWER_ON_RESET, 0); 23631306821SNeil Armstrong 23731306821SNeil Armstrong return 0; 238c9999337SNeil Armstrong } 239c9999337SNeil Armstrong 2405b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv, 2415b0ba0caSNeil Armstrong enum phy_mode mode) 242c9999337SNeil Armstrong { 24331306821SNeil Armstrong int i, ret; 244c9999337SNeil Armstrong 2455174564cSNeil Armstrong for (i = 0; i < priv->drvdata->num_phys; ++i) { 246c9999337SNeil Armstrong if (!priv->phys[i]) 247c9999337SNeil Armstrong continue; 248c9999337SNeil Armstrong 2495174564cSNeil Armstrong if (!strstr(priv->drvdata->phy_names[i], "usb2")) 2505174564cSNeil Armstrong continue; 2515174564cSNeil Armstrong 2525b0ba0caSNeil Armstrong ret = priv->drvdata->usb2_init_phy(priv, i, mode); 25331306821SNeil Armstrong if (ret) 25431306821SNeil Armstrong return ret; 255c9999337SNeil Armstrong } 256c9999337SNeil Armstrong 257c9999337SNeil Armstrong return 0; 258c9999337SNeil Armstrong } 259c9999337SNeil Armstrong 260c9999337SNeil Armstrong static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) 261c9999337SNeil Armstrong { 262013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R3, 263c9999337SNeil Armstrong USB_R3_P30_SSC_RANGE_MASK | 264c9999337SNeil Armstrong USB_R3_P30_REF_SSP_EN, 265c9999337SNeil Armstrong USB_R3_P30_SSC_ENABLE | 266c9999337SNeil Armstrong FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | 267c9999337SNeil Armstrong USB_R3_P30_REF_SSP_EN); 268c9999337SNeil Armstrong udelay(2); 269c9999337SNeil Armstrong 270013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R2, 271c9999337SNeil Armstrong USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 272c9999337SNeil Armstrong FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); 273c9999337SNeil Armstrong 274013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R2, 275c9999337SNeil Armstrong USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 276c9999337SNeil Armstrong FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); 277c9999337SNeil Armstrong 278c9999337SNeil Armstrong udelay(2); 279c9999337SNeil Armstrong 280013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R1, 281c9999337SNeil Armstrong USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, 282c9999337SNeil Armstrong USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); 283c9999337SNeil Armstrong 284013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R1, 285c9999337SNeil Armstrong USB_R1_P30_PCS_TX_SWING_FULL_MASK, 286c9999337SNeil Armstrong FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); 287c9999337SNeil Armstrong } 288c9999337SNeil Armstrong 2895b0ba0caSNeil Armstrong static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv, 2905b0ba0caSNeil Armstrong enum phy_mode mode) 291c9999337SNeil Armstrong { 2925b0ba0caSNeil Armstrong if (mode == PHY_MODE_USB_DEVICE) { 293013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R0, 294c9999337SNeil Armstrong USB_R0_U2D_ACT, USB_R0_U2D_ACT); 295013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R0, 296c9999337SNeil Armstrong USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); 297013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R4, 298c9999337SNeil Armstrong USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); 299c9999337SNeil Armstrong } else { 300013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R0, 301c9999337SNeil Armstrong USB_R0_U2D_ACT, 0); 302013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R4, 303c9999337SNeil Armstrong USB_R4_P21_SLEEP_M0, 0); 304c9999337SNeil Armstrong } 305c9999337SNeil Armstrong } 306c9999337SNeil Armstrong 3075b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv, 3085b0ba0caSNeil Armstrong enum phy_mode mode) 309c9999337SNeil Armstrong { 310c9999337SNeil Armstrong int ret; 311c9999337SNeil Armstrong 3125b0ba0caSNeil Armstrong ret = dwc3_meson_g12a_usb2_init(priv, mode); 313c9999337SNeil Armstrong if (ret) 314c9999337SNeil Armstrong return ret; 315c9999337SNeil Armstrong 316013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R1, 317c9999337SNeil Armstrong USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 318c9999337SNeil Armstrong FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); 319c9999337SNeil Armstrong 320013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R5, 321c9999337SNeil Armstrong USB_R5_ID_DIG_EN_0, 322c9999337SNeil Armstrong USB_R5_ID_DIG_EN_0); 323013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R5, 324c9999337SNeil Armstrong USB_R5_ID_DIG_EN_1, 325c9999337SNeil Armstrong USB_R5_ID_DIG_EN_1); 326013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R5, 327c9999337SNeil Armstrong USB_R5_ID_DIG_TH_MASK, 328c9999337SNeil Armstrong FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); 329c9999337SNeil Armstrong 330c9999337SNeil Armstrong /* If we have an actual SuperSpeed port, initialize it */ 331c9999337SNeil Armstrong if (priv->usb3_ports) 332c9999337SNeil Armstrong dwc3_meson_g12a_usb3_init(priv); 333c9999337SNeil Armstrong 3345b0ba0caSNeil Armstrong dwc3_meson_g12a_usb_otg_apply_mode(priv, mode); 335c9999337SNeil Armstrong 336c9999337SNeil Armstrong return 0; 337c9999337SNeil Armstrong } 338c9999337SNeil Armstrong 339013af227SNeil Armstrong static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = { 340013af227SNeil Armstrong .name = "usb-glue", 341c9999337SNeil Armstrong .reg_bits = 8, 342c9999337SNeil Armstrong .val_bits = 32, 343c9999337SNeil Armstrong .reg_stride = 4, 344c9999337SNeil Armstrong .max_register = USB_R5, 345c9999337SNeil Armstrong }; 346c9999337SNeil Armstrong 347c9999337SNeil Armstrong static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) 348c9999337SNeil Armstrong { 3495174564cSNeil Armstrong const char *phy_name; 350c9999337SNeil Armstrong int i; 351c9999337SNeil Armstrong 3525174564cSNeil Armstrong for (i = 0 ; i < priv->drvdata->num_phys ; ++i) { 3535174564cSNeil Armstrong phy_name = priv->drvdata->phy_names[i]; 3545174564cSNeil Armstrong priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name); 355c9999337SNeil Armstrong if (!priv->phys[i]) 356c9999337SNeil Armstrong continue; 357c9999337SNeil Armstrong 358c9999337SNeil Armstrong if (IS_ERR(priv->phys[i])) 359c9999337SNeil Armstrong return PTR_ERR(priv->phys[i]); 360c9999337SNeil Armstrong 3615174564cSNeil Armstrong if (strstr(phy_name, "usb3")) 362c9999337SNeil Armstrong priv->usb3_ports++; 363c9999337SNeil Armstrong else 364c9999337SNeil Armstrong priv->usb2_ports++; 365c9999337SNeil Armstrong } 366c9999337SNeil Armstrong 367c9999337SNeil Armstrong dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports); 368c9999337SNeil Armstrong dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports); 369c9999337SNeil Armstrong 370c9999337SNeil Armstrong return 0; 371c9999337SNeil Armstrong } 372c9999337SNeil Armstrong 373c9999337SNeil Armstrong static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv) 374c9999337SNeil Armstrong { 375c9999337SNeil Armstrong u32 reg; 376c9999337SNeil Armstrong 377013af227SNeil Armstrong regmap_read(priv->usb_glue_regmap, USB_R5, ®); 378c9999337SNeil Armstrong 379c9999337SNeil Armstrong if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG)) 380c9999337SNeil Armstrong return PHY_MODE_USB_DEVICE; 381c9999337SNeil Armstrong 382c9999337SNeil Armstrong return PHY_MODE_USB_HOST; 383c9999337SNeil Armstrong } 384c9999337SNeil Armstrong 385c9999337SNeil Armstrong static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, 386c9999337SNeil Armstrong enum phy_mode mode) 387c9999337SNeil Armstrong { 388c9999337SNeil Armstrong int ret; 389c9999337SNeil Armstrong 3901e355f21SHanjie Lin if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY]) 391c9999337SNeil Armstrong return -EINVAL; 392c9999337SNeil Armstrong 393c9999337SNeil Armstrong if (mode == PHY_MODE_USB_HOST) 394c9999337SNeil Armstrong dev_info(priv->dev, "switching to Host Mode\n"); 395c9999337SNeil Armstrong else 396c9999337SNeil Armstrong dev_info(priv->dev, "switching to Device Mode\n"); 397c9999337SNeil Armstrong 398c9999337SNeil Armstrong if (priv->vbus) { 399c9999337SNeil Armstrong if (mode == PHY_MODE_USB_DEVICE) 400c9999337SNeil Armstrong ret = regulator_disable(priv->vbus); 401c9999337SNeil Armstrong else 402c9999337SNeil Armstrong ret = regulator_enable(priv->vbus); 403c9999337SNeil Armstrong if (ret) 404c9999337SNeil Armstrong return ret; 405c9999337SNeil Armstrong } 406c9999337SNeil Armstrong 407c9999337SNeil Armstrong priv->otg_phy_mode = mode; 408c9999337SNeil Armstrong 40931306821SNeil Armstrong ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode); 41031306821SNeil Armstrong if (ret) 41131306821SNeil Armstrong return ret; 412c9999337SNeil Armstrong 4135b0ba0caSNeil Armstrong dwc3_meson_g12a_usb_otg_apply_mode(priv, mode); 414c9999337SNeil Armstrong 415c9999337SNeil Armstrong return 0; 416c9999337SNeil Armstrong } 417c9999337SNeil Armstrong 418bce3052fSHeikki Krogerus static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw, 419bce3052fSHeikki Krogerus enum usb_role role) 420c9999337SNeil Armstrong { 421bce3052fSHeikki Krogerus struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw); 422c9999337SNeil Armstrong enum phy_mode mode; 423c9999337SNeil Armstrong 424c9999337SNeil Armstrong if (role == USB_ROLE_NONE) 425c9999337SNeil Armstrong return 0; 426c9999337SNeil Armstrong 427c9999337SNeil Armstrong mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST 428c9999337SNeil Armstrong : PHY_MODE_USB_DEVICE; 429c9999337SNeil Armstrong 430c9999337SNeil Armstrong if (mode == priv->otg_phy_mode) 431c9999337SNeil Armstrong return 0; 432c9999337SNeil Armstrong 433c9999337SNeil Armstrong return dwc3_meson_g12a_otg_mode_set(priv, mode); 434c9999337SNeil Armstrong } 435c9999337SNeil Armstrong 436bce3052fSHeikki Krogerus static enum usb_role dwc3_meson_g12a_role_get(struct usb_role_switch *sw) 437c9999337SNeil Armstrong { 438bce3052fSHeikki Krogerus struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw); 439c9999337SNeil Armstrong 440c9999337SNeil Armstrong return priv->otg_phy_mode == PHY_MODE_USB_HOST ? 441c9999337SNeil Armstrong USB_ROLE_HOST : USB_ROLE_DEVICE; 442c9999337SNeil Armstrong } 443c9999337SNeil Armstrong 444f90db107SNeil Armstrong static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data) 445f90db107SNeil Armstrong { 446f90db107SNeil Armstrong struct dwc3_meson_g12a *priv = data; 447f90db107SNeil Armstrong enum phy_mode otg_id; 448f90db107SNeil Armstrong 449f90db107SNeil Armstrong otg_id = dwc3_meson_g12a_get_id(priv); 450f90db107SNeil Armstrong if (otg_id != priv->otg_phy_mode) { 451f90db107SNeil Armstrong if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) 452f90db107SNeil Armstrong dev_warn(priv->dev, "Failed to switch OTG mode\n"); 453f90db107SNeil Armstrong } 454f90db107SNeil Armstrong 455013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R5, 456013af227SNeil Armstrong USB_R5_ID_DIG_IRQ, 0); 457f90db107SNeil Armstrong 458f90db107SNeil Armstrong return IRQ_HANDLED; 459f90db107SNeil Armstrong } 460f90db107SNeil Armstrong 461c9999337SNeil Armstrong static struct device *dwc3_meson_g12_find_child(struct device *dev, 462c9999337SNeil Armstrong const char *compatible) 463c9999337SNeil Armstrong { 464c9999337SNeil Armstrong struct platform_device *pdev; 465c9999337SNeil Armstrong struct device_node *np; 466c9999337SNeil Armstrong 467c9999337SNeil Armstrong np = of_get_compatible_child(dev->of_node, compatible); 468c9999337SNeil Armstrong if (!np) 469c9999337SNeil Armstrong return NULL; 470c9999337SNeil Armstrong 471c9999337SNeil Armstrong pdev = of_find_device_by_node(np); 472c9999337SNeil Armstrong of_node_put(np); 473c9999337SNeil Armstrong if (!pdev) 474c9999337SNeil Armstrong return NULL; 475c9999337SNeil Armstrong 476c9999337SNeil Armstrong return &pdev->dev; 477c9999337SNeil Armstrong } 478c9999337SNeil Armstrong 4791e355f21SHanjie Lin static int dwc3_meson_g12a_otg_init(struct platform_device *pdev, 4801e355f21SHanjie Lin struct dwc3_meson_g12a *priv) 481c9999337SNeil Armstrong { 482c9999337SNeil Armstrong enum phy_mode otg_id; 4831e355f21SHanjie Lin int ret, irq; 4841e355f21SHanjie Lin struct device *dev = &pdev->dev; 485c9999337SNeil Armstrong 4861e355f21SHanjie Lin if (!priv->drvdata->otg_switch_supported) 4871e355f21SHanjie Lin return 0; 488c9999337SNeil Armstrong 489f90db107SNeil Armstrong if (priv->otg_mode == USB_DR_MODE_OTG) { 490f90db107SNeil Armstrong /* Ack irq before registering */ 491013af227SNeil Armstrong regmap_update_bits(priv->usb_glue_regmap, USB_R5, 492f90db107SNeil Armstrong USB_R5_ID_DIG_IRQ, 0); 493f90db107SNeil Armstrong 494f90db107SNeil Armstrong irq = platform_get_irq(pdev, 0); 495f90db107SNeil Armstrong ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 496f90db107SNeil Armstrong dwc3_meson_g12a_irq_thread, 497f90db107SNeil Armstrong IRQF_ONESHOT, pdev->name, priv); 498f90db107SNeil Armstrong if (ret) 499f90db107SNeil Armstrong return ret; 500f90db107SNeil Armstrong } 501f90db107SNeil Armstrong 502c9999337SNeil Armstrong /* Setup OTG mode corresponding to the ID pin */ 503c9999337SNeil Armstrong if (priv->otg_mode == USB_DR_MODE_OTG) { 504c9999337SNeil Armstrong otg_id = dwc3_meson_g12a_get_id(priv); 505c9999337SNeil Armstrong if (otg_id != priv->otg_phy_mode) { 506c9999337SNeil Armstrong if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) 507c9999337SNeil Armstrong dev_warn(dev, "Failed to switch OTG mode\n"); 508c9999337SNeil Armstrong } 509c9999337SNeil Armstrong } 510c9999337SNeil Armstrong 511c9999337SNeil Armstrong /* Setup role switcher */ 512c9999337SNeil Armstrong priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, 513c9999337SNeil Armstrong "snps,dwc3"); 514c9999337SNeil Armstrong priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); 515c9999337SNeil Armstrong priv->switch_desc.allow_userspace_control = true; 516c9999337SNeil Armstrong priv->switch_desc.set = dwc3_meson_g12a_role_set; 517c9999337SNeil Armstrong priv->switch_desc.get = dwc3_meson_g12a_role_get; 518bce3052fSHeikki Krogerus priv->switch_desc.driver_data = priv; 519c9999337SNeil Armstrong 520c9999337SNeil Armstrong priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); 521c9999337SNeil Armstrong if (IS_ERR(priv->role_switch)) 522c9999337SNeil Armstrong dev_warn(dev, "Unable to register Role Switch\n"); 523c9999337SNeil Armstrong 524238d7602SNathan Chancellor return 0; 5251e355f21SHanjie Lin } 5261e355f21SHanjie Lin 527013af227SNeil Armstrong static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv, 528013af227SNeil Armstrong void __iomem *base) 529013af227SNeil Armstrong { 530013af227SNeil Armstrong int i; 531013af227SNeil Armstrong 532013af227SNeil Armstrong priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, 533013af227SNeil Armstrong base + G12A_GLUE_OFFSET, 534013af227SNeil Armstrong &phy_meson_g12a_usb_glue_regmap_conf); 535013af227SNeil Armstrong if (IS_ERR(priv->usb_glue_regmap)) 536013af227SNeil Armstrong return PTR_ERR(priv->usb_glue_regmap); 537013af227SNeil Armstrong 538013af227SNeil Armstrong /* Create a regmap for each USB2 PHY control register set */ 539013af227SNeil Armstrong for (i = 0; i < priv->usb2_ports; i++) { 540013af227SNeil Armstrong struct regmap_config u2p_regmap_config = { 541013af227SNeil Armstrong .reg_bits = 8, 542013af227SNeil Armstrong .val_bits = 32, 543013af227SNeil Armstrong .reg_stride = 4, 544013af227SNeil Armstrong .max_register = U2P_R1, 545013af227SNeil Armstrong }; 546013af227SNeil Armstrong 547013af227SNeil Armstrong u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL, 548013af227SNeil Armstrong "u2p-%d", i); 549013af227SNeil Armstrong if (!u2p_regmap_config.name) 550013af227SNeil Armstrong return -ENOMEM; 551013af227SNeil Armstrong 552013af227SNeil Armstrong priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev, 553013af227SNeil Armstrong base + (i * U2P_REG_SIZE), 554013af227SNeil Armstrong &u2p_regmap_config); 555013af227SNeil Armstrong if (IS_ERR(priv->u2p_regmap[i])) 556013af227SNeil Armstrong return PTR_ERR(priv->u2p_regmap[i]); 557013af227SNeil Armstrong } 558013af227SNeil Armstrong 559013af227SNeil Armstrong return 0; 560013af227SNeil Armstrong } 561013af227SNeil Armstrong 5625b0ba0caSNeil Armstrong static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) 5635b0ba0caSNeil Armstrong { 5645b0ba0caSNeil Armstrong return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode); 5655b0ba0caSNeil Armstrong } 5665b0ba0caSNeil Armstrong 5671e355f21SHanjie Lin static int dwc3_meson_g12a_probe(struct platform_device *pdev) 5681e355f21SHanjie Lin { 5691e355f21SHanjie Lin struct dwc3_meson_g12a *priv; 5701e355f21SHanjie Lin struct device *dev = &pdev->dev; 5711e355f21SHanjie Lin struct device_node *np = dev->of_node; 5721e355f21SHanjie Lin void __iomem *base; 5731e355f21SHanjie Lin int ret, i; 5741e355f21SHanjie Lin 5751e355f21SHanjie Lin priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 5761e355f21SHanjie Lin if (!priv) 5771e355f21SHanjie Lin return -ENOMEM; 5781e355f21SHanjie Lin 5791e355f21SHanjie Lin base = devm_platform_ioremap_resource(pdev, 0); 5801e355f21SHanjie Lin if (IS_ERR(base)) 5811e355f21SHanjie Lin return PTR_ERR(base); 5821e355f21SHanjie Lin 583013af227SNeil Armstrong priv->drvdata = of_device_get_match_data(&pdev->dev); 584013af227SNeil Armstrong 585013af227SNeil Armstrong priv->dev = dev; 586013af227SNeil Armstrong ret = priv->drvdata->setup_regmaps(priv, base); 587013af227SNeil Armstrong if (ret) 588013af227SNeil Armstrong return ret; 5891e355f21SHanjie Lin 5901e355f21SHanjie Lin priv->vbus = devm_regulator_get_optional(dev, "vbus"); 5911e355f21SHanjie Lin if (IS_ERR(priv->vbus)) { 5921e355f21SHanjie Lin if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) 5931e355f21SHanjie Lin return PTR_ERR(priv->vbus); 5941e355f21SHanjie Lin priv->vbus = NULL; 5951e355f21SHanjie Lin } 5961e355f21SHanjie Lin 5971e355f21SHanjie Lin ret = devm_clk_bulk_get(dev, 5981e355f21SHanjie Lin priv->drvdata->num_clks, 5991e355f21SHanjie Lin priv->drvdata->clks); 6001e355f21SHanjie Lin if (ret) 6011e355f21SHanjie Lin return ret; 6021e355f21SHanjie Lin 6031e355f21SHanjie Lin ret = clk_bulk_prepare_enable(priv->drvdata->num_clks, 6041e355f21SHanjie Lin priv->drvdata->clks); 6051e355f21SHanjie Lin if (ret) 6061e355f21SHanjie Lin return ret; 6071e355f21SHanjie Lin 6081e355f21SHanjie Lin platform_set_drvdata(pdev, priv); 6091e355f21SHanjie Lin 6106d9fa35aSNeil Armstrong priv->reset = devm_reset_control_get_shared(dev, NULL); 6111e355f21SHanjie Lin if (IS_ERR(priv->reset)) { 6121e355f21SHanjie Lin ret = PTR_ERR(priv->reset); 6131e355f21SHanjie Lin dev_err(dev, "failed to get device reset, err=%d\n", ret); 6141e355f21SHanjie Lin return ret; 6151e355f21SHanjie Lin } 6161e355f21SHanjie Lin 6171e355f21SHanjie Lin ret = reset_control_reset(priv->reset); 6181e355f21SHanjie Lin if (ret) 6191e355f21SHanjie Lin goto err_disable_clks; 6201e355f21SHanjie Lin 6211e355f21SHanjie Lin ret = dwc3_meson_g12a_get_phys(priv); 6221e355f21SHanjie Lin if (ret) 6231e355f21SHanjie Lin goto err_disable_clks; 6241e355f21SHanjie Lin 6251e355f21SHanjie Lin if (priv->vbus) { 6261e355f21SHanjie Lin ret = regulator_enable(priv->vbus); 6271e355f21SHanjie Lin if (ret) 6281e355f21SHanjie Lin goto err_disable_clks; 6291e355f21SHanjie Lin } 6301e355f21SHanjie Lin 6311e355f21SHanjie Lin /* Get dr_mode */ 6321e355f21SHanjie Lin priv->otg_mode = usb_get_dr_mode(dev); 6331e355f21SHanjie Lin 6345b0ba0caSNeil Armstrong if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) 6355b0ba0caSNeil Armstrong priv->otg_phy_mode = PHY_MODE_USB_DEVICE; 6365b0ba0caSNeil Armstrong else 6375b0ba0caSNeil Armstrong priv->otg_phy_mode = PHY_MODE_USB_HOST; 6385b0ba0caSNeil Armstrong 6395b0ba0caSNeil Armstrong ret = priv->drvdata->usb_init(priv); 6408f5bc1ecSNeil Armstrong if (ret) 6418f5bc1ecSNeil Armstrong goto err_disable_clks; 6421e355f21SHanjie Lin 6431e355f21SHanjie Lin /* Init PHYs */ 6441e355f21SHanjie Lin for (i = 0 ; i < PHY_COUNT ; ++i) { 6451e355f21SHanjie Lin ret = phy_init(priv->phys[i]); 6461e355f21SHanjie Lin if (ret) 6471e355f21SHanjie Lin goto err_disable_clks; 6481e355f21SHanjie Lin } 6491e355f21SHanjie Lin 6501e355f21SHanjie Lin /* Set PHY Power */ 6511e355f21SHanjie Lin for (i = 0 ; i < PHY_COUNT ; ++i) { 6521e355f21SHanjie Lin ret = phy_power_on(priv->phys[i]); 6531e355f21SHanjie Lin if (ret) 6541e355f21SHanjie Lin goto err_phys_exit; 6551e355f21SHanjie Lin } 6561e355f21SHanjie Lin 6575b0ba0caSNeil Armstrong if (priv->drvdata->usb_post_init) { 6585b0ba0caSNeil Armstrong ret = priv->drvdata->usb_post_init(priv); 6595b0ba0caSNeil Armstrong if (ret) 6605b0ba0caSNeil Armstrong goto err_phys_power; 6615b0ba0caSNeil Armstrong } 6625b0ba0caSNeil Armstrong 6631e355f21SHanjie Lin ret = of_platform_populate(np, NULL, NULL, dev); 6641e355f21SHanjie Lin if (ret) 6651e355f21SHanjie Lin goto err_phys_power; 6661e355f21SHanjie Lin 6671e355f21SHanjie Lin ret = dwc3_meson_g12a_otg_init(pdev, priv); 6681e355f21SHanjie Lin if (ret) 6691e355f21SHanjie Lin goto err_phys_power; 6701e355f21SHanjie Lin 671c9999337SNeil Armstrong pm_runtime_set_active(dev); 672c9999337SNeil Armstrong pm_runtime_enable(dev); 673c9999337SNeil Armstrong pm_runtime_get_sync(dev); 674c9999337SNeil Armstrong 675c9999337SNeil Armstrong return 0; 676c9999337SNeil Armstrong 677c9999337SNeil Armstrong err_phys_power: 678c9999337SNeil Armstrong for (i = 0 ; i < PHY_COUNT ; ++i) 679c9999337SNeil Armstrong phy_power_off(priv->phys[i]); 680c9999337SNeil Armstrong 681c9999337SNeil Armstrong err_phys_exit: 682c9999337SNeil Armstrong for (i = 0 ; i < PHY_COUNT ; ++i) 683c9999337SNeil Armstrong phy_exit(priv->phys[i]); 684c9999337SNeil Armstrong 6851e355f21SHanjie Lin err_disable_clks: 6861e355f21SHanjie Lin clk_bulk_disable_unprepare(priv->drvdata->num_clks, 6871e355f21SHanjie Lin priv->drvdata->clks); 6881e355f21SHanjie Lin 689c9999337SNeil Armstrong return ret; 690c9999337SNeil Armstrong } 691c9999337SNeil Armstrong 692c9999337SNeil Armstrong static int dwc3_meson_g12a_remove(struct platform_device *pdev) 693c9999337SNeil Armstrong { 694c9999337SNeil Armstrong struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); 695c9999337SNeil Armstrong struct device *dev = &pdev->dev; 696c9999337SNeil Armstrong int i; 697c9999337SNeil Armstrong 6981e355f21SHanjie Lin if (priv->drvdata->otg_switch_supported) 699c9999337SNeil Armstrong usb_role_switch_unregister(priv->role_switch); 700c9999337SNeil Armstrong 701c9999337SNeil Armstrong of_platform_depopulate(dev); 702c9999337SNeil Armstrong 703c9999337SNeil Armstrong for (i = 0 ; i < PHY_COUNT ; ++i) { 704c9999337SNeil Armstrong phy_power_off(priv->phys[i]); 705c9999337SNeil Armstrong phy_exit(priv->phys[i]); 706c9999337SNeil Armstrong } 707c9999337SNeil Armstrong 708c9999337SNeil Armstrong pm_runtime_disable(dev); 709c9999337SNeil Armstrong pm_runtime_put_noidle(dev); 710c9999337SNeil Armstrong pm_runtime_set_suspended(dev); 711c9999337SNeil Armstrong 7121e355f21SHanjie Lin clk_bulk_disable_unprepare(priv->drvdata->num_clks, 7131e355f21SHanjie Lin priv->drvdata->clks); 7141e355f21SHanjie Lin 715c9999337SNeil Armstrong return 0; 716c9999337SNeil Armstrong } 717c9999337SNeil Armstrong 718c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) 719c9999337SNeil Armstrong { 720c9999337SNeil Armstrong struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); 721c9999337SNeil Armstrong 7221e355f21SHanjie Lin clk_bulk_disable_unprepare(priv->drvdata->num_clks, 7231e355f21SHanjie Lin priv->drvdata->clks); 724c9999337SNeil Armstrong 725c9999337SNeil Armstrong return 0; 726c9999337SNeil Armstrong } 727c9999337SNeil Armstrong 728c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) 729c9999337SNeil Armstrong { 730c9999337SNeil Armstrong struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); 731c9999337SNeil Armstrong 7321e355f21SHanjie Lin return clk_bulk_prepare_enable(priv->drvdata->num_clks, 7331e355f21SHanjie Lin priv->drvdata->clks); 734c9999337SNeil Armstrong } 735c9999337SNeil Armstrong 736c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) 737c9999337SNeil Armstrong { 738c9999337SNeil Armstrong struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); 7391cf084d1SNeil Armstrong int i, ret; 7401cf084d1SNeil Armstrong 7411cf084d1SNeil Armstrong if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { 7421cf084d1SNeil Armstrong ret = regulator_disable(priv->vbus); 7431cf084d1SNeil Armstrong if (ret) 7441cf084d1SNeil Armstrong return ret; 7451cf084d1SNeil Armstrong } 746c9999337SNeil Armstrong 747c9999337SNeil Armstrong for (i = 0 ; i < PHY_COUNT ; ++i) { 748c9999337SNeil Armstrong phy_power_off(priv->phys[i]); 749c9999337SNeil Armstrong phy_exit(priv->phys[i]); 750c9999337SNeil Armstrong } 751c9999337SNeil Armstrong 752c9999337SNeil Armstrong reset_control_assert(priv->reset); 753c9999337SNeil Armstrong 754c9999337SNeil Armstrong return 0; 755c9999337SNeil Armstrong } 756c9999337SNeil Armstrong 757c9999337SNeil Armstrong static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) 758c9999337SNeil Armstrong { 759c9999337SNeil Armstrong struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); 760c9999337SNeil Armstrong int i, ret; 761c9999337SNeil Armstrong 762c9999337SNeil Armstrong reset_control_deassert(priv->reset); 763c9999337SNeil Armstrong 7645b0ba0caSNeil Armstrong ret = priv->drvdata->usb_init(priv); 7655b0ba0caSNeil Armstrong if (ret) 7665b0ba0caSNeil Armstrong return ret; 767c9999337SNeil Armstrong 768c9999337SNeil Armstrong /* Init PHYs */ 769c9999337SNeil Armstrong for (i = 0 ; i < PHY_COUNT ; ++i) { 770c9999337SNeil Armstrong ret = phy_init(priv->phys[i]); 771c9999337SNeil Armstrong if (ret) 772c9999337SNeil Armstrong return ret; 773c9999337SNeil Armstrong } 774c9999337SNeil Armstrong 775c9999337SNeil Armstrong /* Set PHY Power */ 776c9999337SNeil Armstrong for (i = 0 ; i < PHY_COUNT ; ++i) { 777c9999337SNeil Armstrong ret = phy_power_on(priv->phys[i]); 778c9999337SNeil Armstrong if (ret) 779c9999337SNeil Armstrong return ret; 780c9999337SNeil Armstrong } 781c9999337SNeil Armstrong 7821cf084d1SNeil Armstrong if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { 7831cf084d1SNeil Armstrong ret = regulator_enable(priv->vbus); 7841cf084d1SNeil Armstrong if (ret) 7851cf084d1SNeil Armstrong return ret; 7861cf084d1SNeil Armstrong } 7871cf084d1SNeil Armstrong 788c9999337SNeil Armstrong return 0; 789c9999337SNeil Armstrong } 790c9999337SNeil Armstrong 791c9999337SNeil Armstrong static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { 792c9999337SNeil Armstrong SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) 793c9999337SNeil Armstrong SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, 794c9999337SNeil Armstrong dwc3_meson_g12a_runtime_resume, NULL) 795c9999337SNeil Armstrong }; 796c9999337SNeil Armstrong 797c9999337SNeil Armstrong static const struct of_device_id dwc3_meson_g12a_match[] = { 7981e355f21SHanjie Lin { 7991e355f21SHanjie Lin .compatible = "amlogic,meson-g12a-usb-ctrl", 8001e355f21SHanjie Lin .data = &g12a_drvdata, 8011e355f21SHanjie Lin }, 8021e355f21SHanjie Lin { 8031e355f21SHanjie Lin .compatible = "amlogic,meson-a1-usb-ctrl", 8041e355f21SHanjie Lin .data = &a1_drvdata, 8051e355f21SHanjie Lin }, 806c9999337SNeil Armstrong { /* Sentinel */ } 807c9999337SNeil Armstrong }; 808c9999337SNeil Armstrong MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); 809c9999337SNeil Armstrong 810c9999337SNeil Armstrong static struct platform_driver dwc3_meson_g12a_driver = { 811c9999337SNeil Armstrong .probe = dwc3_meson_g12a_probe, 812c9999337SNeil Armstrong .remove = dwc3_meson_g12a_remove, 813c9999337SNeil Armstrong .driver = { 814c9999337SNeil Armstrong .name = "dwc3-meson-g12a", 815c9999337SNeil Armstrong .of_match_table = dwc3_meson_g12a_match, 816c9999337SNeil Armstrong .pm = &dwc3_meson_g12a_dev_pm_ops, 817c9999337SNeil Armstrong }, 818c9999337SNeil Armstrong }; 819c9999337SNeil Armstrong 820c9999337SNeil Armstrong module_platform_driver(dwc3_meson_g12a_driver); 821c9999337SNeil Armstrong MODULE_LICENSE("GPL v2"); 822c9999337SNeil Armstrong MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); 823c9999337SNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 824