Hi, On 18/12/2019 03:42, Hanjie Lin wrote: > Adds support for Amlogic A1 USB Control Glue HW. > > The Amlogic A1 SoC Family embeds 1 USB Controllers: > - a DWC3 IP configured as Host for USB2 and USB3 > > A glue connects the controllers to the USB2 PHY of A1 SoC. > > Signed-off-by: Hanjie Lin <hanjie.lin@xxxxxxxxxxx> > Signed-off-by: Yue Wang <yue.wang@xxxxxxxxxxx> > --- > drivers/usb/dwc3/dwc3-meson-g12a.c | 69 +++++++++++++++++++++++++++----------- > 1 file changed, 49 insertions(+), 20 deletions(-) > > diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c > index 8a3ec1a..3817daf 100644 > --- a/drivers/usb/dwc3/dwc3-meson-g12a.c > +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c > @@ -107,10 +107,22 @@ static const char *phy_names[PHY_COUNT] = { > "usb2-phy0", "usb2-phy1", "usb3-phy0", > }; > > +static const struct clk_bulk_data meson_g12a_clocks[] = { > + { .id = NULL}, > +}; > + > +static const struct clk_bulk_data meson_a1_clocks[] = { > + { .id = "usb_ctrl"}, > + { .id = "usb_bus"}, > + { .id = "xtal_usb_phy"}, > + { .id = "xtal_usb_ctrl"}, > +}; > + > struct dwc3_meson_g12a { > struct device *dev; > struct regmap *regmap; > - struct clk *clk; > + struct clk_bulk_data *clks; > + int num_clks; > struct reset_control *reset; > struct phy *phys[PHY_COUNT]; > enum usb_dr_mode otg_mode; > @@ -151,7 +163,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) > U2P_R0_POWER_ON_RESET, > U2P_R0_POWER_ON_RESET); > > - if (i == USB2_OTG_PHY) { > + if (i == USB2_OTG_PHY && priv->otg_mode != USB_DR_MODE_HOST) { This is wrong for G12A, please use something else to exclude PULLUPP/VBUS to be updated on A1. > regmap_update_bits(priv->regmap, > U2P_R0 + (U2P_REG_SIZE * i), > U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, > @@ -295,7 +307,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, > { > int ret; > > - if (!priv->phys[USB2_OTG_PHY]) > + if (!priv->phys[USB2_OTG_PHY] || priv->otg_mode == USB_DR_MODE_HOST) Same, this is wrong on G12A since a Host port can be switched to device, use something else to disable this for A1, like setting a "otg_supported" flag into the match data, and using it in dwc3_meson_g12a_usb2_init(), avoiding registering usb_role_switch, the IRQ and updating the priv->otg_mode in probe(). > return -EINVAL; > > if (mode == PHY_MODE_USB_HOST) > @@ -409,17 +421,32 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) > priv->vbus = NULL; > } > > - priv->clk = devm_clk_get(dev, NULL); > - if (IS_ERR(priv->clk)) > - return PTR_ERR(priv->clk); > + priv->dev = dev; > + > + if (of_device_is_compatible(np, "amlogic,meson-g12a-usb-ctrl")) { > + priv->clks = devm_kmemdup(dev, meson_g12a_clocks, > + sizeof(meson_g12a_clocks), > + GFP_KERNEL); > + priv->num_clks = ARRAY_SIZE(meson_g12a_clocks); > + } else if (of_device_is_compatible(np, "amlogic,meson-a1-usb-ctrl")) { > + priv->clks = devm_kmemdup(dev, meson_a1_clocks, > + sizeof(meson_a1_clocks), > + GFP_KERNEL); > + priv->num_clks = ARRAY_SIZE(meson_a1_clocks); > + } else { > + return -EINVAL; > + } Like the PHY driver, please use of_device_get_match_data() and a match data for each compatible instead. > + > + if (!priv->clks) > + return -ENOMEM; > > - ret = clk_prepare_enable(priv->clk); > + ret = devm_clk_bulk_get(dev, priv->num_clks, priv->clks); > if (ret) > return ret; > > - devm_add_action_or_reset(dev, > - (void(*)(void *))clk_disable_unprepare, > - priv->clk); > + ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); > + if (ret) > + return ret; > > platform_set_drvdata(pdev, priv); > priv->dev = dev; > @@ -433,16 +460,16 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) > > ret = reset_control_reset(priv->reset); > if (ret) > - return ret; > + goto err_disable_clks; > > ret = dwc3_meson_g12a_get_phys(priv); > if (ret) > - return ret; > + goto err_disable_clks; > > if (priv->vbus) { > ret = regulator_enable(priv->vbus); > if (ret) > - return ret; > + goto err_disable_clks; > } > > /* Get dr_mode */ > @@ -458,7 +485,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) > dwc3_meson_g12a_irq_thread, > IRQF_ONESHOT, pdev->name, priv); > if (ret) > - return ret; > + goto err_disable_clks; > } > > dwc3_meson_g12a_usb_init(priv); > @@ -467,7 +494,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) > for (i = 0 ; i < PHY_COUNT ; ++i) { > ret = phy_init(priv->phys[i]); > if (ret) > - return ret; > + goto err_disable_clks; > } > > /* Set PHY Power */ > @@ -478,10 +505,8 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) > } > > ret = of_platform_populate(np, NULL, NULL, dev); > - if (ret) { > - clk_disable_unprepare(priv->clk); > + if (ret) > goto err_phys_power; > - } > > /* Setup OTG mode corresponding to the ID pin */ > if (priv->otg_mode == USB_DR_MODE_OTG) { > @@ -518,6 +543,9 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) > for (i = 0 ; i < PHY_COUNT ; ++i) > phy_exit(priv->phys[i]); > > +err_disable_clks: > + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); > + > return ret; > } > > @@ -547,7 +575,7 @@ static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) > { > struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); > > - clk_disable(priv->clk); > + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); > > return 0; > } > @@ -556,7 +584,7 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) > { > struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); > > - return clk_enable(priv->clk); > + return clk_bulk_prepare_enable(priv->num_clks, priv->clks); > } > > static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) > @@ -620,6 +648,7 @@ 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-g12a-usb-ctrl" }, > + { .compatible = "amlogic,meson-a1-usb-ctrl" }, > { /* Sentinel */ } > }; > MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); > Thanks, Neil