On 16/05/2021 16:51, Dmitry Osipenko wrote: > Add common helper which initializes OPP table for Tegra SoC core devices. > > Tested-by: Peter Geis <pgwipeout@xxxxxxxxx> # Ouya T30 > Tested-by: Paul Fertser <fercerpav@xxxxxxxxx> # PAZ00 T20 > Tested-by: Nicolas Chauvet <kwizart@xxxxxxxxx> # PAZ00 T20 and TK1 T124 > Tested-by: Matt Merhar <mattmerhar@xxxxxxxxxxxxxx> # Ouya T30 > Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx> > --- > drivers/soc/tegra/common.c | 112 +++++++++++++++++++++++++++++++++++++ > include/soc/tegra/common.h | 30 ++++++++++ > 2 files changed, 142 insertions(+) > > diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c > index 3dc54f59cafe..c3fd2facfc2d 100644 > --- a/drivers/soc/tegra/common.c > +++ b/drivers/soc/tegra/common.c > @@ -3,9 +3,16 @@ > * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. > */ > > +#define dev_fmt(fmt) "tegra-soc: " fmt > + > +#include <linux/clk.h> > +#include <linux/device.h> > +#include <linux/export.h> > #include <linux/of.h> > +#include <linux/pm_opp.h> > > #include <soc/tegra/common.h> > +#include <soc/tegra/fuse.h> > > static const struct of_device_id tegra_machine_match[] = { > { .compatible = "nvidia,tegra20", }, > @@ -31,3 +38,108 @@ bool soc_is_tegra(void) > > return match != NULL; > } > + > +static int tegra_core_dev_init_opp_state(struct device *dev) > +{ > + struct dev_pm_opp *opp; > + unsigned long rate; > + struct clk *clk; > + int err; > + > + clk = devm_clk_get(dev, NULL); > + if (IS_ERR(clk)) { > + dev_err(dev, "failed to get clk: %pe\n", clk); > + return PTR_ERR(clk); > + } > + > + rate = clk_get_rate(clk); > + if (!rate) { > + dev_err(dev, "failed to get clk rate\n"); > + return -EINVAL; > + } > + > + opp = dev_pm_opp_find_freq_ceil(dev, &rate); > + > + if (opp == ERR_PTR(-ERANGE)) > + opp = dev_pm_opp_find_freq_floor(dev, &rate); > + > + err = PTR_ERR_OR_ZERO(opp); > + if (err) { > + dev_err(dev, "failed to get OPP for %ld Hz: %d\n", > + rate, err); > + return err; > + } > + > + dev_pm_opp_put(opp); > + > + /* first dummy rate-setting initializes voltage vote */ > + err = dev_pm_opp_set_rate(dev, rate); > + if (err) { > + dev_err(dev, "failed to initialize OPP clock: %d\n", err); > + return err; > + } The devm_pm_opp_set_clkname will call clk_get(), so here you should drop the clk reference at the end. Why having it twice? > + > + return 0; > +} > + > +/** > + * devm_tegra_core_dev_init_opp_table() - initialize OPP table > + * @dev: device for which OPP table is initialized > + * @params: pointer to the OPP table configuration > + * > + * This function will initialize OPP table and sync OPP state of a Tegra SoC > + * core device. > + * > + * Return: 0 on success or errorno. > + */ > +int devm_tegra_core_dev_init_opp_table(struct device *dev, > + struct tegra_core_opp_params *params) > +{ > + u32 hw_version; > + int err; > + > + err = devm_pm_opp_set_clkname(dev, NULL); > + if (err) { > + dev_err(dev, "failed to set OPP clk: %d\n", err); > + return err; > + } > + > + /* Tegra114+ doesn't support OPP yet */ > + if (!of_machine_is_compatible("nvidia,tegra20") && > + !of_machine_is_compatible("nvidia,tegra30")) > + return -ENODEV; > + > + if (of_machine_is_compatible("nvidia,tegra20")) > + hw_version = BIT(tegra_sku_info.soc_process_id); > + else > + hw_version = BIT(tegra_sku_info.soc_speedo_id); > + > + err = devm_pm_opp_set_supported_hw(dev, &hw_version, 1); > + if (err) { > + dev_err(dev, "failed to set OPP supported HW: %d\n", err); > + return err; > + } > + > + /* > + * Older device-trees have an empty OPP table, we will get > + * -ENODEV from devm_pm_opp_of_add_table() in this case. > + */ > + err = devm_pm_opp_of_add_table(dev); > + if (err) { > + if (err == -ENODEV) > + dev_err_once(dev, "OPP table not found, please update device-tree\n"); > + else > + dev_err(dev, "failed to add OPP table: %d\n", err); > + > + return err; > + } > + > + if (params->init_state) { > + err = tegra_core_dev_init_opp_state(dev); > + if (err) > + return err; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(devm_tegra_core_dev_init_opp_table); > diff --git a/include/soc/tegra/common.h b/include/soc/tegra/common.h > index 98027a76ce3d..e8eab13aa199 100644 > --- a/include/soc/tegra/common.h > +++ b/include/soc/tegra/common.h > @@ -6,6 +6,36 @@ > #ifndef __SOC_TEGRA_COMMON_H__ > #define __SOC_TEGRA_COMMON_H__ > > +#include <linux/errno.h> > +#include <linux/types.h> > + > +struct device; > + > +/** > + * Tegra SoC core device OPP table configuration > + * > + * @init_state: pre-initialize OPP state of a device > + */ > +struct tegra_core_opp_params { > + bool init_state; > +}; > + > +#ifdef CONFIG_ARCH_TEGRA > bool soc_is_tegra(void); > +int devm_tegra_core_dev_init_opp_table(struct device *dev, > + struct tegra_core_opp_params *params); > +#else > +static inline bool soc_is_tegra(void) This looks unrelated. Please make it a separate patch. > +{ > + return false; > +} > + > +static inline int > +devm_tegra_core_dev_init_opp_table(struct device *dev, > + struct tegra_core_opp_params *params) > +{ > + return -ENODEV; > +} > +#endif > > #endif /* __SOC_TEGRA_COMMON_H__ */ > Best regards, Krzysztof