> Some SoCs gate the EMAC's path to the System NOC behind dedicated clocks > that must be enabled before the DMA can reach memory. Add > ethqos_noc_clk_cfg and the corresponding fields in the driver-data and > runtime structs so each compatible can declare its own set with per-clock > rates. The clocks are acquired during probe and enabled/disabled > alongside the existing link clock in ethqos_clks_config(). > > No functional change for existing compatibles. This will help us when > we add support for Shikra. > > Signed-off-by: Mohd Ayaan Anwar > --- > .../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 106 +++++++++++++++++++++ > 1 file changed, 106 insertions(+) > [...] > static u32 rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset) > @@ -689,15 +703,51 @@ static int ethqos_mac_finish_serdes(struct net_device *ndev, void *priv, > static int ethqos_clks_config(void *priv, bool enabled) > { > struct qcom_ethqos *ethqos = priv; > + unsigned int i; > int ret = 0; > > if (enabled) { > + if (ethqos->num_noc_clks) { > + ret = dev_pm_opp_set_rate(ðqos->pdev->dev, > + ethqos->noc_clk_rates[0]); assuming the first clock is always an OPP device seems a bit fragile to me. Can we find a way to enforce it? (e.g. have a dedicated clk_bulk_data struct for it). > + if (ret) { > + dev_err(ðqos->pdev->dev, > + "NOC OPP rate set failed: %d\n", ret); > + return ret; > + } > + > + for (i = 1; i < ethqos->num_noc_clks; i++) { > + ret = clk_set_rate(ethqos->noc_clks[i].clk, > + ethqos->noc_clk_rates[i]); > + if (ret) { > + dev_err(ðqos->pdev->dev, > + "NOC clock rate set failed: %d\n", ret); > + dev_pm_opp_set_rate(ðqos->pdev->dev, 0); > + return ret; > + } > + } > + } > + > ret = clk_prepare_enable(ethqos->link_clk); > if (ret) { > dev_err(ðqos->pdev->dev, "link_clk enable failed\n"); > + if (ethqos->num_noc_clks) > + dev_pm_opp_set_rate(ðqos->pdev->dev, 0); > return ret; > } > > + if (ethqos->num_noc_clks) { > + ret = clk_bulk_prepare_enable(ethqos->num_noc_clks, > + ethqos->noc_clks); > + if (ret) { > + dev_err(ðqos->pdev->dev, > + "NOC clocks enable failed: %d\n", ret); > + clk_disable_unprepare(ethqos->link_clk); > + dev_pm_opp_set_rate(ðqos->pdev->dev, 0); > + return ret; > + } > + } > + > /* Enable functional clock to prevent DMA reset to timeout due > * to lacking PHY clock after the hardware block has been power > * cycled. The actual configuration will be adjusted once > @@ -706,7 +756,12 @@ static int ethqos_clks_config(void *priv, bool enabled) > qcom_ethqos_set_sgmii_loopback(ethqos, true); > ethqos_set_func_clk_en(ethqos); > } else { > + if (ethqos->num_noc_clks) > + clk_bulk_disable_unprepare(ethqos->num_noc_clks, > + ethqos->noc_clks); > clk_disable_unprepare(ethqos->link_clk); > + if (ethqos->num_noc_clks) > + dev_pm_opp_set_rate(ðqos->pdev->dev, 0); > } > > return ret; > @@ -734,6 +789,51 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv) > netdev_dbg(priv->dev, "PTP rate %lu\n", plat_dat->clk_ptp_rate); > } > > +static void qcom_ethqos_noc_opp_cleanup(void *dev) > +{ > + dev_pm_opp_set_rate(dev, 0); > +} > + > +/* Some SoCs gate NOC access behind dedicated clocks. Acquire them here > + * so ethqos_clks_config() can enable/disable them at runtime. The OPP > + * table is used to propagate the required VDD_CX performance state via > + * dev_pm_opp_set_rate(). > + */ > +static int qcom_ethqos_init_noc_clks(struct qcom_ethqos *ethqos, > + const struct ethqos_emac_driver_data *data) > +{ > + struct device *dev = ðqos->pdev->dev; > + unsigned int i; > + int ret; > + > + if (!data->num_noc_clks) > + return 0; > + > + for (i = 0; i < data->num_noc_clks; i++) { > + ethqos->noc_clks[i].id = data->noc_clk_cfg[i].id; > + ethqos->noc_clk_rates[i] = data->noc_clk_cfg[i].rate; > + } > + ethqos->num_noc_clks = data->num_noc_clks; > + > + ret = devm_clk_bulk_get(dev, ethqos->num_noc_clks, ethqos->noc_clks); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to get NOC clocks\n"); > + > + ret = devm_pm_opp_set_clkname(dev, data->noc_clk_cfg[0].id); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to set OPP clock name\n"); > + > + ret = devm_pm_opp_of_add_table(dev); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to add OPP table\n"); > + > + ret = dev_pm_opp_set_rate(dev, data->noc_clk_cfg[0].rate); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to set initial NOC OPP rate\n"); > + > + return devm_add_action_or_reset(dev, qcom_ethqos_noc_opp_cleanup, dev); > +} > + > static int qcom_ethqos_probe(struct platform_device *pdev) > { > struct device_node *np = pdev->dev.of_node; > @@ -795,6 +895,12 @@ static int qcom_ethqos_probe(struct platform_device *pdev) > ethqos->has_emac_ge_3 = data->has_emac_ge_3; > ethqos->needs_sgmii_loopback = data->needs_sgmii_loopback; > > + if (data->num_noc_clks) { I guess you can drop this check since it is already done in qcom_ethqos_init_noc_clks(), right? Regards, Lorenzo > + ret = qcom_ethqos_init_noc_clks(ethqos, data); > + if (ret) > + return ret; > + } > + > ethqos->link_clk = devm_clk_get(dev, data->link_clk_name ?: "rgmii"); > if (IS_ERR(ethqos->link_clk)) > return dev_err_probe(dev, PTR_ERR(ethqos->link_clk), > > -- > 2.34.1 > >