Message ID | 1578537045-23260-5-git-send-email-hanjie.lin@amlogic.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | arm64: meson: Add support for USB on Amlogic A1 | expand |
Hi, On 09/01/2020 03:30, 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@amlogic.com> > Signed-off-by: Yue Wang <yue.wang@amlogic.com> > --- > drivers/usb/dwc3/dwc3-meson-g12a.c | 99 +++++++++++++++++++++++++++----------- > 1 file changed, 71 insertions(+), 28 deletions(-) > > diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c > index 8a3ec1a..957eda2 100644 > --- a/drivers/usb/dwc3/dwc3-meson-g12a.c > +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c > @@ -96,6 +96,11 @@ > #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) > #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) > > +enum meson_soc_id { > + MESON_SOC_G12A = 0, > + MESON_SOC_A1, > +}; > + > enum { > USB2_HOST_PHY = 0, > USB2_OTG_PHY, > @@ -107,10 +112,21 @@ 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_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; > @@ -120,6 +136,7 @@ struct dwc3_meson_g12a { > struct regulator *vbus; > struct usb_role_switch_desc switch_desc; > struct usb_role_switch *role_switch; > + int soc_id; > }; > > static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, > @@ -151,7 +168,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 (priv->otg_mode == USB_DR_MODE_OTG && i == USB2_OTG_PHY) { I as said on v2, this is wrong, we can/need/must allow switching even if the dr_mode is not USB_DR_MODE_OTG. Please add a struct used in match data with a simple bool like "otg_switch_support" and use it here and below instead of using USB_DR_MODE_OTG. > regmap_update_bits(priv->regmap, > U2P_R0 + (U2P_REG_SIZE * i), > U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, > @@ -295,7 +312,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, > { > int ret; > > - if (!priv->phys[USB2_OTG_PHY]) > + if (priv->otg_mode != USB_DR_MODE_OTG || !priv->phys[USB2_OTG_PHY]) Ditto > return -EINVAL; > > if (mode == PHY_MODE_USB_HOST) > @@ -409,17 +426,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->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); > + > + if (priv->soc_id == MESON_SOC_G12A) { > + priv->clks = devm_kmemdup(dev, meson_g12a_clocks, > + sizeof(meson_g12a_clocks), > + GFP_KERNEL); > + priv->num_clks = ARRAY_SIZE(meson_g12a_clocks); > + } else if (priv->soc_id == MESON_SOC_A1) { > + 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; > + } > + > + 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 +465,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 +490,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 +499,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,18 +510,17 @@ 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; > - } > + > + if (priv->otg_mode != USB_DR_MODE_OTG) > + goto setup_pm_runtime; > > /* Setup OTG mode corresponding to the ID pin */ > - if (priv->otg_mode == USB_DR_MODE_OTG) { > - otg_id = dwc3_meson_g12a_get_id(priv); > - if (otg_id != priv->otg_phy_mode) { > - if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) > - dev_warn(dev, "Failed to switch OTG mode\n"); > - } > + otg_id = dwc3_meson_g12a_get_id(priv); > + if (otg_id != priv->otg_phy_mode) { > + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) > + dev_warn(dev, "Failed to switch OTG mode\n"); > } > > /* Setup role switcher */ > @@ -504,6 +535,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) > if (IS_ERR(priv->role_switch)) > dev_warn(dev, "Unable to register Role Switch\n"); > > +setup_pm_runtime: Ditto > pm_runtime_set_active(dev); > pm_runtime_enable(dev); > pm_runtime_get_sync(dev); > @@ -518,6 +550,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; > } > > @@ -527,7 +562,8 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev) > struct device *dev = &pdev->dev; > int i; > > - usb_role_switch_unregister(priv->role_switch); > + if (priv->otg_mode == USB_DR_MODE_OTG) > + usb_role_switch_unregister(priv->role_switch); > > of_platform_depopulate(dev); > > @@ -547,7 +583,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 +592,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) > @@ -619,7 +655,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-g12a-usb-ctrl" }, > + { > + .compatible = "amlogic,meson-g12a-usb-ctrl", > + .data = (void *)MESON_SOC_G12A, > + }, > + { > + .compatible = "amlogic,meson-a1-usb-ctrl", > + .data = (void *)MESON_SOC_A1, > + }, > { /* Sentinel */ } > }; > MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); > Neil
On 2020/1/9 17:13, Neil Armstrong wrote: > Hi, > > On 09/01/2020 03:30, 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@amlogic.com> >> Signed-off-by: Yue Wang <yue.wang@amlogic.com> >> --- >> drivers/usb/dwc3/dwc3-meson-g12a.c | 99 +++++++++++++++++++++++++++----------- >> 1 file changed, 71 insertions(+), 28 deletions(-) >> >> diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c >> index 8a3ec1a..957eda2 100644 >> --- a/drivers/usb/dwc3/dwc3-meson-g12a.c >> +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c >> @@ -96,6 +96,11 @@ >> #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) >> #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) >> >> +enum meson_soc_id { >> + MESON_SOC_G12A = 0, >> + MESON_SOC_A1, >> +}; >> + >> enum { >> USB2_HOST_PHY = 0, >> USB2_OTG_PHY, >> @@ -107,10 +112,21 @@ 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_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; >> @@ -120,6 +136,7 @@ struct dwc3_meson_g12a { >> struct regulator *vbus; >> struct usb_role_switch_desc switch_desc; >> struct usb_role_switch *role_switch; >> + int soc_id; >> }; >> >> static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, >> @@ -151,7 +168,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 (priv->otg_mode == USB_DR_MODE_OTG && i == USB2_OTG_PHY) { > > I as said on v2, this is wrong, we can/need/must allow switching even if the dr_mode is not USB_DR_MODE_OTG. > > Please add a struct used in match data with a simple bool like "otg_switch_support" and use it here and below > instead of using USB_DR_MODE_OTG. > Hi neil, Right, I thought only otg dr_mode(from dts) can allow switching by mistake. I will add a drvdata struct includes both otg_switch_supported and clock info for each SoC comaptible in next version. Thanks, Hanjie >> regmap_update_bits(priv->regmap, >> U2P_R0 + (U2P_REG_SIZE * i), >> U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, >> @@ -295,7 +312,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, >> { >> int ret; >> >> - if (!priv->phys[USB2_OTG_PHY]) >> + if (priv->otg_mode != USB_DR_MODE_OTG || !priv->phys[USB2_OTG_PHY]) > > Ditto > >> return -EINVAL; >> >> if (mode == PHY_MODE_USB_HOST) >> @@ -409,17 +426,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->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); >> + >> + if (priv->soc_id == MESON_SOC_G12A) { >> + priv->clks = devm_kmemdup(dev, meson_g12a_clocks, >> + sizeof(meson_g12a_clocks), >> + GFP_KERNEL); >> + priv->num_clks = ARRAY_SIZE(meson_g12a_clocks); >> + } else if (priv->soc_id == MESON_SOC_A1) { >> + 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; >> + } >> + >> + 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 +465,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 +490,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 +499,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,18 +510,17 @@ 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; >> - } >> + >> + if (priv->otg_mode != USB_DR_MODE_OTG) >> + goto setup_pm_runtime; >> >> /* Setup OTG mode corresponding to the ID pin */ >> - if (priv->otg_mode == USB_DR_MODE_OTG) { >> - otg_id = dwc3_meson_g12a_get_id(priv); >> - if (otg_id != priv->otg_phy_mode) { >> - if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) >> - dev_warn(dev, "Failed to switch OTG mode\n"); >> - } >> + otg_id = dwc3_meson_g12a_get_id(priv); >> + if (otg_id != priv->otg_phy_mode) { >> + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) >> + dev_warn(dev, "Failed to switch OTG mode\n"); >> } >> >> /* Setup role switcher */ >> @@ -504,6 +535,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) >> if (IS_ERR(priv->role_switch)) >> dev_warn(dev, "Unable to register Role Switch\n"); >> >> +setup_pm_runtime: > > Ditto > >> pm_runtime_set_active(dev); >> pm_runtime_enable(dev); >> pm_runtime_get_sync(dev); >> @@ -518,6 +550,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; >> } >> >> @@ -527,7 +562,8 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev) >> struct device *dev = &pdev->dev; >> int i; >> >> - usb_role_switch_unregister(priv->role_switch); >> + if (priv->otg_mode == USB_DR_MODE_OTG) >> + usb_role_switch_unregister(priv->role_switch); >> >> of_platform_depopulate(dev); >> >> @@ -547,7 +583,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 +592,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) >> @@ -619,7 +655,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-g12a-usb-ctrl" }, >> + { >> + .compatible = "amlogic,meson-g12a-usb-ctrl", >> + .data = (void *)MESON_SOC_G12A, >> + }, >> + { >> + .compatible = "amlogic,meson-a1-usb-ctrl", >> + .data = (void *)MESON_SOC_A1, >> + }, >> { /* Sentinel */ } >> }; >> MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); >> > > Neil > > _______________________________________________ > linux-amlogic mailing list > linux-amlogic@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-amlogic > > . >
Hi, Hanjie Lin <hanjie.lin@amlogic.com> writes: > 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@amlogic.com> > Signed-off-by: Yue Wang <yue.wang@amlogic.com> you're not really adding a new glue. Rather, you're adding support for a new platform in an existing glue. Make sure subject is clearer > @@ -409,17 +426,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->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); > + > + if (priv->soc_id == MESON_SOC_G12A) { you can use of_device_is_compatible() and get rid of the enumeration you added.
On Wed, Jan 15, 2020 at 9:43 AM Felipe Balbi <balbi@kernel.org> wrote: [...] > > - priv->clk = devm_clk_get(dev, NULL); > > - if (IS_ERR(priv->clk)) > > - return PTR_ERR(priv->clk); > > + priv->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); > > + > > + if (priv->soc_id == MESON_SOC_G12A) { > > you can use of_device_is_compatible() and get rid of the enumeration you added. Hanjie switched to a struct (instead of an enum) that is passed as "match data" in v5 [0] of this series personally I prefer what Hanjie has in v5 over of_device_is_compatible() (because that match data pattern is what we also use on other Amlogic drivers) Martin [0] https://patchwork.kernel.org/patch/11326669/
Hi, Martin Blumenstingl <martin.blumenstingl@googlemail.com> writes: > On Wed, Jan 15, 2020 at 9:43 AM Felipe Balbi <balbi@kernel.org> wrote: > [...] >> > - priv->clk = devm_clk_get(dev, NULL); >> > - if (IS_ERR(priv->clk)) >> > - return PTR_ERR(priv->clk); >> > + priv->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); >> > + >> > + if (priv->soc_id == MESON_SOC_G12A) { >> >> you can use of_device_is_compatible() and get rid of the enumeration you added. > Hanjie switched to a struct (instead of an enum) that is passed as > "match data" in v5 [0] of this series > personally I prefer what Hanjie has in v5 over > of_device_is_compatible() (because that match data pattern is what we > also use on other Amlogic drivers) but you end up duplicating functionality that already exists by means of of_device_is_compatible(). Frankly, I don't have a hard opinion about this anyway, since it's not touching dwc3 core. In this case, I'll defer to whatever you guys prefer.
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 8a3ec1a..957eda2 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -96,6 +96,11 @@ #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) +enum meson_soc_id { + MESON_SOC_G12A = 0, + MESON_SOC_A1, +}; + enum { USB2_HOST_PHY = 0, USB2_OTG_PHY, @@ -107,10 +112,21 @@ 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_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; @@ -120,6 +136,7 @@ struct dwc3_meson_g12a { struct regulator *vbus; struct usb_role_switch_desc switch_desc; struct usb_role_switch *role_switch; + int soc_id; }; static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, @@ -151,7 +168,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 (priv->otg_mode == USB_DR_MODE_OTG && i == USB2_OTG_PHY) { regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, @@ -295,7 +312,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, { int ret; - if (!priv->phys[USB2_OTG_PHY]) + if (priv->otg_mode != USB_DR_MODE_OTG || !priv->phys[USB2_OTG_PHY]) return -EINVAL; if (mode == PHY_MODE_USB_HOST) @@ -409,17 +426,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->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); + + if (priv->soc_id == MESON_SOC_G12A) { + priv->clks = devm_kmemdup(dev, meson_g12a_clocks, + sizeof(meson_g12a_clocks), + GFP_KERNEL); + priv->num_clks = ARRAY_SIZE(meson_g12a_clocks); + } else if (priv->soc_id == MESON_SOC_A1) { + 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; + } + + 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 +465,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 +490,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 +499,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,18 +510,17 @@ 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; - } + + if (priv->otg_mode != USB_DR_MODE_OTG) + goto setup_pm_runtime; /* Setup OTG mode corresponding to the ID pin */ - if (priv->otg_mode == USB_DR_MODE_OTG) { - otg_id = dwc3_meson_g12a_get_id(priv); - if (otg_id != priv->otg_phy_mode) { - if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) - dev_warn(dev, "Failed to switch OTG mode\n"); - } + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(dev, "Failed to switch OTG mode\n"); } /* Setup role switcher */ @@ -504,6 +535,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) if (IS_ERR(priv->role_switch)) dev_warn(dev, "Unable to register Role Switch\n"); +setup_pm_runtime: pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -518,6 +550,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; } @@ -527,7 +562,8 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; - usb_role_switch_unregister(priv->role_switch); + if (priv->otg_mode == USB_DR_MODE_OTG) + usb_role_switch_unregister(priv->role_switch); of_platform_depopulate(dev); @@ -547,7 +583,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 +592,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) @@ -619,7 +655,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-g12a-usb-ctrl" }, + { + .compatible = "amlogic,meson-g12a-usb-ctrl", + .data = (void *)MESON_SOC_G12A, + }, + { + .compatible = "amlogic,meson-a1-usb-ctrl", + .data = (void *)MESON_SOC_A1, + }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);