aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/usb/dwc3/dwc3-meson-g12a.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/dwc3-meson-g12a.c')
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c422
1 files changed, 339 insertions, 83 deletions
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index b81d085bc534..1f7f4d88ed9d 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -30,7 +30,7 @@
#include <linux/usb/role.h>
#include <linux/regulator/consumer.h>
-/* USB2 Ports Control Registers */
+/* USB2 Ports Control Registers, offsets are per-port */
#define U2P_REG_SIZE 0x20
@@ -50,14 +50,16 @@
/* USB Glue Control Registers */
-#define USB_R0 0x80
+#define G12A_GLUE_OFFSET 0x80
+
+#define USB_R0 0x00
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
#define USB_R0_U2D_ACT BIT(31)
-#define USB_R1 0x84
+#define USB_R1 0x04
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
#define USB_R1_U3H_PME_ENABLE BIT(1)
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2)
@@ -69,23 +71,23 @@
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
-#define USB_R2 0x88
+#define USB_R2 0x08
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
-#define USB_R3 0x8c
+#define USB_R3 0x0c
#define USB_R3_P30_SSC_ENABLE BIT(0)
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
#define USB_R3_P30_REF_SSP_EN BIT(13)
-#define USB_R4 0x90
+#define USB_R4 0x10
#define USB_R4_P21_PORT_RESET_0 BIT(0)
#define USB_R4_P21_SLEEP_M0 BIT(1)
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
#define USB_R4_P21_ONLY BIT(4)
-#define USB_R5 0x94
+#define USB_R5 0x14
#define USB_R5_ID_DIG_SYNC BIT(0)
#define USB_R5_ID_DIG_REG BIT(1)
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
@@ -96,15 +98,12 @@
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
-enum {
- USB2_HOST_PHY = 0,
- USB2_OTG_PHY,
- USB3_HOST_PHY,
- PHY_COUNT,
-};
+#define PHY_COUNT 3
+#define USB2_OTG_PHY 1
-static const char *phy_names[PHY_COUNT] = {
- "usb2-phy0", "usb2-phy1", "usb3-phy0",
+static struct clk_bulk_data meson_gxl_clocks[] = {
+ { .id = "usb_ctrl" },
+ { .id = "ddr" },
};
static struct clk_bulk_data meson_g12a_clocks[] = {
@@ -117,27 +116,133 @@ static struct clk_bulk_data meson_a1_clocks[] = {
{ .id = "xtal_usb_ctrl" },
};
+static const char *meson_gxm_phy_names[] = {
+ "usb2-phy0", "usb2-phy1", "usb2-phy2",
+};
+
+static const char *meson_g12a_phy_names[] = {
+ "usb2-phy0", "usb2-phy1", "usb3-phy0",
+};
+
+/*
+ * Amlogic A1 has a single physical PHY, in slot 1, but still has the
+ * two U2 PHY controls register blocks like G12A.
+ * Handling the first PHY on slot 1 would need a large amount of code
+ * changes, and the current management is generic enough to handle it
+ * correctly when only the "usb2-phy1" phy is specified on-par with the
+ * DT bindings.
+ */
+static const char *meson_a1_phy_names[] = {
+ "usb2-phy0", "usb2-phy1"
+};
+
+struct dwc3_meson_g12a;
+
struct dwc3_meson_g12a_drvdata {
bool otg_switch_supported;
+ bool otg_phy_host_port_disable;
struct clk_bulk_data *clks;
int num_clks;
+ const char **phy_names;
+ int num_phys;
+ int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
+ int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+ int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+ int (*usb_init)(struct dwc3_meson_g12a *priv);
+ int (*usb_post_init)(struct dwc3_meson_g12a *priv);
+};
+
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base);
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base);
+
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode);
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode);
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv);
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
+
+/*
+ * For GXL and GXM SoCs:
+ * USB Phy muxing between the DWC2 Device controller and the DWC3 Host
+ * controller is buggy when switching from Device to Host when USB port
+ * is unpopulated, it causes the DWC3 to hard crash.
+ * When populated (including OTG switching with ID pin), the switch works
+ * like a charm like on the G12A platforms.
+ * In order to still switch from Host to Device on an USB Type-A port,
+ * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
+ * controller from the port, but when used the DWC3 controller must be
+ * reset to recover usage of the port.
+ */
+
+static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
+ .otg_switch_supported = true,
+ .otg_phy_host_port_disable = true,
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_a1_phy_names,
+ .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+ .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+ .usb_init = dwc3_meson_gxl_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
+};
+
+static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
+ .otg_switch_supported = true,
+ .otg_phy_host_port_disable = true,
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_gxm_phy_names,
+ .num_phys = ARRAY_SIZE(meson_gxm_phy_names),
+ .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+ .usb_init = dwc3_meson_gxl_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
};
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.otg_switch_supported = true,
.clks = meson_g12a_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_g12a_phy_names,
+ .num_phys = ARRAY_SIZE(meson_g12a_phy_names),
+ .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+ .usb_init = dwc3_meson_g12a_usb_init,
};
static struct dwc3_meson_g12a_drvdata a1_drvdata = {
.otg_switch_supported = false,
.clks = meson_a1_clocks,
.num_clks = ARRAY_SIZE(meson_a1_clocks),
+ .phy_names = meson_a1_phy_names,
+ .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+ .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+ .usb_init = dwc3_meson_g12a_usb_init,
};
struct dwc3_meson_g12a {
struct device *dev;
- struct regmap *regmap;
+ struct regmap *u2p_regmap[PHY_COUNT];
+ struct regmap *usb_glue_regmap;
struct reset_control *reset;
struct phy *phys[PHY_COUNT];
enum usb_dr_mode otg_mode;
@@ -150,49 +255,78 @@ struct dwc3_meson_g12a {
const struct dwc3_meson_g12a_drvdata *drvdata;
};
-static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
- int i, enum phy_mode mode)
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode)
+{
+ return phy_set_mode(priv->phys[i], mode);
+}
+
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode)
+{
+ /* On GXL PHY must be started in device mode for DWC2 init */
+ return priv->drvdata->set_phy_mode(priv, i,
+ (i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE
+ : PHY_MODE_USB_HOST);
+}
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode)
{
if (mode == PHY_MODE_USB_HOST)
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE,
U2P_R0_HOST_DEVICE);
else
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE, 0);
+
+ return 0;
}
-static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode)
{
- int i;
+ int ret;
- if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
- priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
- else
- priv->otg_phy_mode = PHY_MODE_USB_HOST;
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_POWER_ON_RESET,
+ U2P_R0_POWER_ON_RESET);
- for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
- if (!priv->phys[i])
- continue;
+ if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+ U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_POWER_ON_RESET,
- U2P_R0_POWER_ON_RESET);
+ ret = priv->drvdata->set_phy_mode(priv, i, mode);
+ } else
+ ret = priv->drvdata->set_phy_mode(priv, i,
+ PHY_MODE_USB_HOST);
- if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
- regmap_update_bits(priv->regmap,
- U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
- U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_POWER_ON_RESET, 0);
+
+ return 0;
+}
- dwc3_meson_g12a_usb2_set_mode(priv, i,
- priv->otg_phy_mode);
- } else
- dwc3_meson_g12a_usb2_set_mode(priv, i,
- PHY_MODE_USB_HOST);
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
+{
+ int i, ret;
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_POWER_ON_RESET, 0);
+ for (i = 0; i < priv->drvdata->num_phys; ++i) {
+ if (!priv->phys[i])
+ continue;
+
+ if (!strstr(priv->drvdata->phy_names[i], "usb2"))
+ continue;
+
+ ret = priv->drvdata->usb2_init_phy(priv, i, mode);
+ if (ret)
+ return ret;
}
return 0;
@@ -200,7 +334,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
{
- regmap_update_bits(priv->regmap, USB_R3,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R3,
USB_R3_P30_SSC_RANGE_MASK |
USB_R3_P30_REF_SSP_EN,
USB_R3_P30_SSC_ENABLE |
@@ -208,61 +342,77 @@ static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
USB_R3_P30_REF_SSP_EN);
udelay(2);
- regmap_update_bits(priv->regmap, USB_R2,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
- regmap_update_bits(priv->regmap, USB_R2,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
udelay(2);
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
}
-static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
+static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
{
- if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
- regmap_update_bits(priv->regmap, USB_R0,
+ if (mode == PHY_MODE_USB_DEVICE) {
+ if (priv->otg_mode != USB_DR_MODE_OTG &&
+ priv->drvdata->otg_phy_host_port_disable)
+ /* Isolate the OTG PHY port from the Host Controller */
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+ USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+ FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+ BIT(USB2_OTG_PHY)));
+
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, USB_R0_U2D_ACT);
- regmap_update_bits(priv->regmap, USB_R0,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
- regmap_update_bits(priv->regmap, USB_R4,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
} else {
- regmap_update_bits(priv->regmap, USB_R0,
+ if (priv->otg_mode != USB_DR_MODE_OTG &&
+ priv->drvdata->otg_phy_host_port_disable) {
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+ USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
+ msleep(500);
+ }
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, 0);
- regmap_update_bits(priv->regmap, USB_R4,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, 0);
}
}
-static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
{
int ret;
- ret = dwc3_meson_g12a_usb2_init(priv);
+ ret = dwc3_meson_g12a_usb2_init(priv, mode);
if (ret)
return ret;
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_0,
USB_R5_ID_DIG_EN_0);
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_1,
USB_R5_ID_DIG_EN_1);
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_TH_MASK,
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
@@ -270,12 +420,13 @@ static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
if (priv->usb3_ports)
dwc3_meson_g12a_usb3_init(priv);
- dwc3_meson_g12a_usb_otg_apply_mode(priv);
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
-static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
+static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
+ .name = "usb-glue",
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
@@ -284,17 +435,19 @@ static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
{
+ const char *phy_name;
int i;
- for (i = 0 ; i < PHY_COUNT ; ++i) {
- priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
+ for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
+ phy_name = priv->drvdata->phy_names[i];
+ priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
if (!priv->phys[i])
continue;
if (IS_ERR(priv->phys[i]))
return PTR_ERR(priv->phys[i]);
- if (i == USB3_HOST_PHY)
+ if (strstr(phy_name, "usb3"))
priv->usb3_ports++;
else
priv->usb2_ports++;
@@ -310,7 +463,7 @@ static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
{
u32 reg;
- regmap_read(priv->regmap, USB_R5, &reg);
+ regmap_read(priv->usb_glue_regmap, USB_R5, &reg);
if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
return PHY_MODE_USB_DEVICE;
@@ -342,9 +495,11 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
priv->otg_phy_mode = mode;
- dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+ ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
+ if (ret)
+ return ret;
- dwc3_meson_g12a_usb_otg_apply_mode(priv);
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
@@ -364,6 +519,13 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
if (mode == priv->otg_phy_mode)
return 0;
+ if (priv->drvdata->otg_phy_host_port_disable)
+ dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
+ "SoC, when manual switching from "\
+ "Host to device, DWC3 controller "\
+ "will need to be resetted in order "\
+ "to recover usage of the Host port");
+
return dwc3_meson_g12a_otg_mode_set(priv, mode);
}
@@ -386,7 +548,8 @@ static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data)
dev_warn(priv->dev, "Failed to switch OTG mode\n");
}
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
+ USB_R5_ID_DIG_IRQ, 0);
return IRQ_HANDLED;
}
@@ -421,7 +584,7 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
irq = platform_get_irq(pdev, 0);
@@ -457,6 +620,77 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
return 0;
}
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base)
+{
+ /* GXL controls the PHY mode in the PHY registers unlike G12A */
+ priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
+ &phy_meson_g12a_usb_glue_regmap_conf);
+ if (IS_ERR(priv->usb_glue_regmap))
+ return PTR_ERR(priv->usb_glue_regmap);
+
+ return 0;
+}
+
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base)
+{
+ int i;
+
+ priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
+ base + G12A_GLUE_OFFSET,
+ &phy_meson_g12a_usb_glue_regmap_conf);
+ if (IS_ERR(priv->usb_glue_regmap))
+ return PTR_ERR(priv->usb_glue_regmap);
+
+ /* Create a regmap for each USB2 PHY control register set */
+ for (i = 0; i < priv->usb2_ports; i++) {
+ struct regmap_config u2p_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = U2P_R1,
+ };
+
+ u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
+ "u2p-%d", i);
+ if (!u2p_regmap_config.name)
+ return -ENOMEM;
+
+ priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
+ base + (i * U2P_REG_SIZE),
+ &u2p_regmap_config);
+ if (IS_ERR(priv->u2p_regmap[i]))
+ return PTR_ERR(priv->u2p_regmap[i]);
+ }
+
+ return 0;
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+ return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
+}
+
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv)
+{
+ return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE);
+}
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv)
+{
+ int ret;
+
+ ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY,
+ priv->otg_phy_mode);
+ if (ret)
+ return ret;
+
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode);
+
+ return 0;
+}
+
static int dwc3_meson_g12a_probe(struct platform_device *pdev)
{
struct dwc3_meson_g12a *priv;
@@ -473,10 +707,8 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- priv->regmap = devm_regmap_init_mmio(dev, base,
- &phy_meson_g12a_usb3_regmap_conf);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
+ priv->drvdata = of_device_get_match_data(&pdev->dev);
+ priv->dev = dev;
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
@@ -485,8 +717,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
priv->vbus = NULL;
}
- priv->drvdata = of_device_get_match_data(&pdev->dev);
-
ret = devm_clk_bulk_get(dev,
priv->drvdata->num_clks,
priv->drvdata->clks);
@@ -499,13 +729,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
return ret;
platform_set_drvdata(pdev, priv);
- priv->dev = dev;
- priv->reset = devm_reset_control_get(dev, NULL);
+ priv->reset = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(priv->reset)) {
ret = PTR_ERR(priv->reset);
dev_err(dev, "failed to get device reset, err=%d\n", ret);
- return ret;
+ goto err_disable_clks;
}
ret = reset_control_reset(priv->reset);
@@ -516,6 +745,10 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (ret)
goto err_disable_clks;
+ ret = priv->drvdata->setup_regmaps(priv, base);
+ if (ret)
+ return ret;
+
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
@@ -525,7 +758,14 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
/* Get dr_mode */
priv->otg_mode = usb_get_dr_mode(dev);
- dwc3_meson_g12a_usb_init(priv);
+ if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+ priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
+ else
+ priv->otg_phy_mode = PHY_MODE_USB_HOST;
+
+ ret = priv->drvdata->usb_init(priv);
+ if (ret)
+ goto err_disable_clks;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -541,6 +781,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
goto err_phys_exit;
}
+ if (priv->drvdata->usb_post_init) {
+ ret = priv->drvdata->usb_post_init(priv);
+ if (ret)
+ goto err_phys_power;
+ }
+
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
goto err_phys_power;
@@ -642,7 +888,9 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
reset_control_deassert(priv->reset);
- dwc3_meson_g12a_usb_init(priv);
+ ret = priv->drvdata->usb_init(priv);
+ if (ret)
+ return ret;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -675,6 +923,14 @@ static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
static const struct of_device_id dwc3_meson_g12a_match[] = {
{
+ .compatible = "amlogic,meson-gxl-usb-ctrl",
+ .data = &gxl_drvdata,
+ },
+ {
+ .compatible = "amlogic,meson-gxm-usb-ctrl",
+ .data = &gxm_drvdata,
+ },
+ {
.compatible = "amlogic,meson-g12a-usb-ctrl",
.data = &g12a_drvdata,
},