summaryrefslogtreecommitdiff
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.c113
1 files changed, 70 insertions, 43 deletions
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index 1f7f4d88ed9d..55e144ba8cfc 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -116,34 +116,34 @@ static struct clk_bulk_data meson_a1_clocks[] = {
{ .id = "xtal_usb_ctrl" },
};
-static const char *meson_gxm_phy_names[] = {
+static const char * const meson_gxm_phy_names[] = {
"usb2-phy0", "usb2-phy1", "usb2-phy2",
};
-static const char *meson_g12a_phy_names[] = {
+static const char * const 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.
+ * AXG has the similar scheme, thus needs the same tweak.
* 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[] = {
+static const char * const 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;
+ const char * const *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,
@@ -187,8 +187,7 @@ static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
* reset to recover usage of the port.
*/
-static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
- .otg_switch_supported = true,
+static const struct dwc3_meson_g12a_drvdata gxl_drvdata = {
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
@@ -201,8 +200,7 @@ static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
.usb_post_init = dwc3_meson_gxl_usb_post_init,
};
-static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
- .otg_switch_supported = true,
+static const struct dwc3_meson_g12a_drvdata gxm_drvdata = {
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
@@ -215,8 +213,19 @@ static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
.usb_post_init = dwc3_meson_gxl_usb_post_init,
};
-static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
- .otg_switch_supported = true,
+static const struct dwc3_meson_g12a_drvdata axg_drvdata = {
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_gxl_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_g12a_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
+};
+
+static const struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.clks = meson_g12a_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
.phy_names = meson_g12a_phy_names,
@@ -227,8 +236,7 @@ static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.usb_init = dwc3_meson_g12a_usb_init,
};
-static struct dwc3_meson_g12a_drvdata a1_drvdata = {
- .otg_switch_supported = false,
+static const struct dwc3_meson_g12a_drvdata a1_drvdata = {
.clks = meson_a1_clocks,
.num_clks = ARRAY_SIZE(meson_a1_clocks),
.phy_names = meson_a1_phy_names,
@@ -293,7 +301,7 @@ static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
U2P_R0_POWER_ON_RESET,
U2P_R0_POWER_ON_RESET);
- if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
+ if (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);
@@ -476,7 +484,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
{
int ret;
- if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY])
+ if (!priv->phys[USB2_OTG_PHY])
return -EINVAL;
if (mode == PHY_MODE_USB_HOST)
@@ -520,11 +528,7 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
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");
+ dev_warn_once(priv->dev, "Broken manual OTG switch\n");
return dwc3_meson_g12a_otg_mode_set(priv, mode);
}
@@ -579,15 +583,14 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
int ret, irq;
struct device *dev = &pdev->dev;
- if (!priv->drvdata->otg_switch_supported)
- return 0;
-
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
dwc3_meson_g12a_irq_thread,
IRQF_ONESHOT, pdev->name, priv);
@@ -626,10 +629,7 @@ static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
/* 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;
+ return PTR_ERR_OR_ZERO(priv->usb_glue_regmap);
}
static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
@@ -644,7 +644,7 @@ static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
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++) {
+ for (i = 0; i < priv->drvdata->num_phys; i++) {
struct regmap_config u2p_regmap_config = {
.reg_bits = 8,
.val_bits = 32,
@@ -652,6 +652,9 @@ static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
.max_register = U2P_R1,
};
+ if (!strstr(priv->drvdata->phy_names[i], "usb2"))
+ continue;
+
u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
"u2p-%d", i);
if (!u2p_regmap_config.name)
@@ -743,16 +746,16 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
ret = dwc3_meson_g12a_get_phys(priv);
if (ret)
- goto err_disable_clks;
+ goto err_rearm;
ret = priv->drvdata->setup_regmaps(priv, base);
if (ret)
- return ret;
+ goto err_rearm;
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
- goto err_disable_clks;
+ goto err_rearm;
}
/* Get dr_mode */
@@ -765,13 +768,13 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
ret = priv->drvdata->usb_init(priv);
if (ret)
- goto err_disable_clks;
+ goto err_disable_regulator;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
ret = phy_init(priv->phys[i]);
if (ret)
- goto err_disable_clks;
+ goto err_disable_regulator;
}
/* Set PHY Power */
@@ -793,7 +796,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
ret = dwc3_meson_g12a_otg_init(pdev, priv);
if (ret)
- goto err_phys_power;
+ goto err_plat_depopulate;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -801,6 +804,9 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
return 0;
+err_plat_depopulate:
+ of_platform_depopulate(dev);
+
err_phys_power:
for (i = 0 ; i < PHY_COUNT ; ++i)
phy_power_off(priv->phys[i]);
@@ -809,6 +815,13 @@ err_phys_exit:
for (i = 0 ; i < PHY_COUNT ; ++i)
phy_exit(priv->phys[i]);
+err_disable_regulator:
+ if (priv->vbus)
+ regulator_disable(priv->vbus);
+
+err_rearm:
+ reset_control_rearm(priv->reset);
+
err_disable_clks:
clk_bulk_disable_unprepare(priv->drvdata->num_clks,
priv->drvdata->clks);
@@ -816,14 +829,16 @@ err_disable_clks:
return ret;
}
-static int dwc3_meson_g12a_remove(struct platform_device *pdev)
+static void dwc3_meson_g12a_remove(struct platform_device *pdev)
{
struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
int i;
- if (priv->drvdata->otg_switch_supported)
- usb_role_switch_unregister(priv->role_switch);
+ usb_role_switch_unregister(priv->role_switch);
+
+ put_device(priv->switch_desc.udc);
+ put_device(priv->switch_desc.usb2_port);
of_platform_depopulate(dev);
@@ -836,10 +851,10 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev)
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
+ reset_control_rearm(priv->reset);
+
clk_bulk_disable_unprepare(priv->drvdata->num_clks,
priv->drvdata->clks);
-
- return 0;
}
static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
@@ -876,7 +891,7 @@ static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
phy_exit(priv->phys[i]);
}
- reset_control_assert(priv->reset);
+ reset_control_rearm(priv->reset);
return 0;
}
@@ -886,7 +901,9 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
int i, ret;
- reset_control_deassert(priv->reset);
+ ret = reset_control_reset(priv->reset);
+ if (ret)
+ return ret;
ret = priv->drvdata->usb_init(priv);
if (ret)
@@ -906,8 +923,14 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
return ret;
}
- if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
- ret = regulator_enable(priv->vbus);
+ if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
+ ret = regulator_enable(priv->vbus);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->drvdata->usb_post_init) {
+ ret = priv->drvdata->usb_post_init(priv);
if (ret)
return ret;
}
@@ -931,6 +954,10 @@ static const struct of_device_id dwc3_meson_g12a_match[] = {
.data = &gxm_drvdata,
},
{
+ .compatible = "amlogic,meson-axg-usb-ctrl",
+ .data = &axg_drvdata,
+ },
+ {
.compatible = "amlogic,meson-g12a-usb-ctrl",
.data = &g12a_drvdata,
},