Commit c4f5a40b authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'pm-opp'

Merge operating performance points (OPP) updates for 5.20-rc1.

* pm-opp: (43 commits)
  venus: pm_helpers: Fix warning in OPP during probe
  OPP: Don't drop opp->np reference while it is still in use
  OPP: Don't drop opp_table->np reference while it is still in use
  OPP: Remove dev{m}_pm_opp_of_add_table_noclk()
  PM / devfreq: tegra30: Register config_clks helper
  OPP: Allow config_clks helper for single clk case
  OPP: Provide a simple implementation to configure multiple clocks
  OPP: Assert clk_count == 1 for single clk helpers
  OPP: Add key specific assert() method to key finding helpers
  OPP: Compare bandwidths for all paths in _opp_compare_key()
  OPP: Allow multiple clocks for a device
  dt-bindings: opp: accept array of frequencies
  OPP: Make dev_pm_opp_set_opp() independent of frequency
  OPP: Reuse _opp_compare_key() in _opp_add_static_v2()
  OPP: Remove rate_not_available parameter to _opp_add()
  OPP: Use consistent names for OPP table instances
  OPP: Use generic key finding helpers for bandwidth key
  OPP: Use generic key finding helpers for level key
  OPP: Add generic key finding helpers and use them for freq APIs
  OPP: Remove dev_pm_opp_find_freq_ceil_by_volt()
  ...
parents 6ab4b199 f6e0b468
......@@ -50,6 +50,16 @@ patternProperties:
property to uniquely identify the OPP nodes exists. Devices like power
domains must have another (implementation dependent) property.
Entries for multiple clocks shall be provided in the same field, as
array of frequencies. The OPP binding doesn't provide any provisions
to relate the values to their clocks or the order in which the clocks
need to be configured and that is left for the implementation
specific binding.
minItems: 1
maxItems: 16
items:
maxItems: 1
opp-microvolt:
description: |
Voltage for the OPP
......
......@@ -29,9 +29,9 @@ struct private_data {
cpumask_var_t cpus;
struct device *cpu_dev;
struct opp_table *opp_table;
struct cpufreq_frequency_table *freq_table;
bool have_static_opps;
int opp_token;
};
static LIST_HEAD(priv_list);
......@@ -193,7 +193,7 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
struct private_data *priv;
struct device *cpu_dev;
bool fallback = false;
const char *reg_name;
const char *reg_name[] = { NULL, NULL };
int ret;
/* Check if this CPU is already covered by some other policy */
......@@ -218,12 +218,11 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
* OPP layer will be taking care of regulators now, but it needs to know
* the name of the regulator first.
*/
reg_name = find_supply_name(cpu_dev);
if (reg_name) {
priv->opp_table = dev_pm_opp_set_regulators(cpu_dev, &reg_name,
1);
if (IS_ERR(priv->opp_table)) {
ret = PTR_ERR(priv->opp_table);
reg_name[0] = find_supply_name(cpu_dev);
if (reg_name[0]) {
priv->opp_token = dev_pm_opp_set_regulators(cpu_dev, reg_name);
if (priv->opp_token < 0) {
ret = priv->opp_token;
if (ret != -EPROBE_DEFER)
dev_err(cpu_dev, "failed to set regulators: %d\n",
ret);
......@@ -295,7 +294,7 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
out:
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(priv->cpus);
dev_pm_opp_put_regulators(priv->opp_table);
dev_pm_opp_put_regulators(priv->opp_token);
free_cpumask:
free_cpumask_var(priv->cpus);
return ret;
......@@ -309,7 +308,7 @@ static void dt_cpufreq_release(void)
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(priv->cpus);
dev_pm_opp_put_regulators(priv->opp_table);
dev_pm_opp_put_regulators(priv->opp_token);
free_cpumask_var(priv->cpus);
list_del(&priv->node);
}
......
......@@ -31,8 +31,8 @@
/* cpufreq-dt device registered by imx-cpufreq-dt */
static struct platform_device *cpufreq_dt_pdev;
static struct opp_table *cpufreq_opp_table;
static struct device *cpu_dev;
static int cpufreq_opp_token;
enum IMX7ULP_CPUFREQ_CLKS {
ARM,
......@@ -153,9 +153,9 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "cpu speed grade %d mkt segment %d supported-hw %#x %#x\n",
speed_grade, mkt_segment, supported_hw[0], supported_hw[1]);
cpufreq_opp_table = dev_pm_opp_set_supported_hw(cpu_dev, supported_hw, 2);
if (IS_ERR(cpufreq_opp_table)) {
ret = PTR_ERR(cpufreq_opp_table);
cpufreq_opp_token = dev_pm_opp_set_supported_hw(cpu_dev, supported_hw, 2);
if (cpufreq_opp_token < 0) {
ret = cpufreq_opp_token;
dev_err(&pdev->dev, "Failed to set supported opp: %d\n", ret);
return ret;
}
......@@ -163,7 +163,7 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev)
cpufreq_dt_pdev = platform_device_register_data(
&pdev->dev, "cpufreq-dt", -1, NULL, 0);
if (IS_ERR(cpufreq_dt_pdev)) {
dev_pm_opp_put_supported_hw(cpufreq_opp_table);
dev_pm_opp_put_supported_hw(cpufreq_opp_token);
ret = PTR_ERR(cpufreq_dt_pdev);
dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret);
return ret;
......@@ -176,7 +176,7 @@ static int imx_cpufreq_dt_remove(struct platform_device *pdev)
{
platform_device_unregister(cpufreq_dt_pdev);
if (!of_machine_is_compatible("fsl,imx7ulp"))
dev_pm_opp_put_supported_hw(cpufreq_opp_table);
dev_pm_opp_put_supported_hw(cpufreq_opp_token);
else
clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks);
......
......@@ -55,9 +55,7 @@ struct qcom_cpufreq_match_data {
};
struct qcom_cpufreq_drv {
struct opp_table **names_opp_tables;
struct opp_table **hw_opp_tables;
struct opp_table **genpd_opp_tables;
int *opp_tokens;
u32 versions;
const struct qcom_cpufreq_match_data *data;
};
......@@ -315,72 +313,43 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
}
of_node_put(np);
drv->names_opp_tables = kcalloc(num_possible_cpus(),
sizeof(*drv->names_opp_tables),
drv->opp_tokens = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tokens),
GFP_KERNEL);
if (!drv->names_opp_tables) {
if (!drv->opp_tokens) {
ret = -ENOMEM;
goto free_drv;
}
drv->hw_opp_tables = kcalloc(num_possible_cpus(),
sizeof(*drv->hw_opp_tables),
GFP_KERNEL);
if (!drv->hw_opp_tables) {
ret = -ENOMEM;
goto free_opp_names;
}
drv->genpd_opp_tables = kcalloc(num_possible_cpus(),
sizeof(*drv->genpd_opp_tables),
GFP_KERNEL);
if (!drv->genpd_opp_tables) {
ret = -ENOMEM;
goto free_opp;
}
for_each_possible_cpu(cpu) {
struct dev_pm_opp_config config = {
.supported_hw = NULL,
};
cpu_dev = get_cpu_device(cpu);
if (NULL == cpu_dev) {
ret = -ENODEV;
goto free_genpd_opp;
goto free_opp;
}
if (drv->data->get_version) {
config.supported_hw = &drv->versions;
config.supported_hw_count = 1;
if (pvs_name) {
drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name(
cpu_dev,
pvs_name);
if (IS_ERR(drv->names_opp_tables[cpu])) {
ret = PTR_ERR(drv->names_opp_tables[cpu]);
dev_err(cpu_dev, "Failed to add OPP name %s\n",
pvs_name);
goto free_opp;
}
if (pvs_name)
config.prop_name = pvs_name;
}
drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw(
cpu_dev, &drv->versions, 1);
if (IS_ERR(drv->hw_opp_tables[cpu])) {
ret = PTR_ERR(drv->hw_opp_tables[cpu]);
dev_err(cpu_dev,
"Failed to set supported hardware\n");
goto free_genpd_opp;
}
if (drv->data->genpd_names) {
config.genpd_names = drv->data->genpd_names;
config.virt_devs = NULL;
}
if (drv->data->genpd_names) {
drv->genpd_opp_tables[cpu] =
dev_pm_opp_attach_genpd(cpu_dev,
drv->data->genpd_names,
NULL);
if (IS_ERR(drv->genpd_opp_tables[cpu])) {
ret = PTR_ERR(drv->genpd_opp_tables[cpu]);
if (ret != -EPROBE_DEFER)
dev_err(cpu_dev,
"Could not attach to pm_domain: %d\n",
ret);
goto free_genpd_opp;
if (config.supported_hw || config.genpd_names) {
drv->opp_tokens[cpu] = dev_pm_opp_set_config(cpu_dev, &config);
if (drv->opp_tokens[cpu] < 0) {
ret = drv->opp_tokens[cpu];
dev_err(cpu_dev, "Failed to set OPP config\n");
goto free_opp;
}
}
}
......@@ -395,27 +364,10 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
ret = PTR_ERR(cpufreq_dt_pdev);
dev_err(cpu_dev, "Failed to register platform device\n");
free_genpd_opp:
for_each_possible_cpu(cpu) {
if (IS_ERR(drv->genpd_opp_tables[cpu]))
break;
dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
}
kfree(drv->genpd_opp_tables);
free_opp:
for_each_possible_cpu(cpu) {
if (IS_ERR(drv->names_opp_tables[cpu]))
break;
dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]);
}
for_each_possible_cpu(cpu) {
if (IS_ERR(drv->hw_opp_tables[cpu]))
break;
dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
}
kfree(drv->hw_opp_tables);
free_opp_names:
kfree(drv->names_opp_tables);
for_each_possible_cpu(cpu)
dev_pm_opp_clear_config(drv->opp_tokens[cpu]);
kfree(drv->opp_tokens);
free_drv:
kfree(drv);
......@@ -429,15 +381,10 @@ static int qcom_cpufreq_remove(struct platform_device *pdev)
platform_device_unregister(cpufreq_dt_pdev);
for_each_possible_cpu(cpu) {
dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
}
for_each_possible_cpu(cpu)
dev_pm_opp_clear_config(drv->opp_tokens[cpu]);
kfree(drv->names_opp_tables);
kfree(drv->hw_opp_tables);
kfree(drv->genpd_opp_tables);
kfree(drv->opp_tokens);
kfree(drv);
return 0;
......
......@@ -156,9 +156,13 @@ static int sti_cpufreq_set_opp_info(void)
unsigned int hw_info_offset;
unsigned int version[VERSION_ELEMENTS];
int pcode, substrate, major, minor;
int ret;
int opp_token, ret;
char name[MAX_PCODE_NAME_LEN];
struct opp_table *opp_table;
struct dev_pm_opp_config config = {
.supported_hw = version,
.supported_hw_count = ARRAY_SIZE(version),
.prop_name = name,
};
reg_fields = sti_cpufreq_match();
if (!reg_fields) {
......@@ -210,21 +214,14 @@ static int sti_cpufreq_set_opp_info(void)
snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
opp_table = dev_pm_opp_set_prop_name(dev, name);
if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to set prop name\n");
return PTR_ERR(opp_table);
}
version[0] = BIT(major);
version[1] = BIT(minor);
version[2] = BIT(substrate);
opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to set supported hardware\n");
ret = PTR_ERR(opp_table);
goto err_put_prop_name;
opp_token = dev_pm_opp_set_config(dev, &config);
if (opp_token < 0) {
dev_err(dev, "Failed to set OPP config\n");
return opp_token;
}
dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
......@@ -233,10 +230,6 @@ static int sti_cpufreq_set_opp_info(void)
version[0], version[1], version[2]);
return 0;
err_put_prop_name:
dev_pm_opp_put_prop_name(opp_table);
return ret;
}
static int sti_cpufreq_fetch_syscon_registers(void)
......
......@@ -86,20 +86,20 @@ static int sun50i_cpufreq_get_efuse(u32 *versions)
static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
{
struct opp_table **opp_tables;
int *opp_tokens;
char name[MAX_NAME_LEN];
unsigned int cpu;
u32 speed = 0;
int ret;
opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables),
opp_tokens = kcalloc(num_possible_cpus(), sizeof(*opp_tokens),
GFP_KERNEL);
if (!opp_tables)
if (!opp_tokens)
return -ENOMEM;
ret = sun50i_cpufreq_get_efuse(&speed);
if (ret) {
kfree(opp_tables);
kfree(opp_tokens);
return ret;
}
......@@ -113,9 +113,9 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
goto free_opp;
}
opp_tables[cpu] = dev_pm_opp_set_prop_name(cpu_dev, name);
if (IS_ERR(opp_tables[cpu])) {
ret = PTR_ERR(opp_tables[cpu]);
opp_tokens[cpu] = dev_pm_opp_set_prop_name(cpu_dev, name);
if (opp_tokens[cpu] < 0) {
ret = opp_tokens[cpu];
pr_err("Failed to set prop name\n");
goto free_opp;
}
......@@ -124,7 +124,7 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
NULL, 0);
if (!IS_ERR(cpufreq_dt_pdev)) {
platform_set_drvdata(pdev, opp_tables);
platform_set_drvdata(pdev, opp_tokens);
return 0;
}
......@@ -132,27 +132,24 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
pr_err("Failed to register platform device\n");
free_opp:
for_each_possible_cpu(cpu) {
if (IS_ERR_OR_NULL(opp_tables[cpu]))
break;
dev_pm_opp_put_prop_name(opp_tables[cpu]);
}
kfree(opp_tables);
for_each_possible_cpu(cpu)
dev_pm_opp_put_prop_name(opp_tokens[cpu]);
kfree(opp_tokens);
return ret;
}
static int sun50i_cpufreq_nvmem_remove(struct platform_device *pdev)
{
struct opp_table **opp_tables = platform_get_drvdata(pdev);
int *opp_tokens = platform_get_drvdata(pdev);
unsigned int cpu;
platform_device_unregister(cpufreq_dt_pdev);
for_each_possible_cpu(cpu)
dev_pm_opp_put_prop_name(opp_tables[cpu]);
dev_pm_opp_put_prop_name(opp_tokens[cpu]);
kfree(opp_tables);
kfree(opp_tokens);
return 0;
}
......
......@@ -32,9 +32,9 @@ static bool cpu0_node_has_opp_v2_prop(void)
return ret;
}
static void tegra20_cpufreq_put_supported_hw(void *opp_table)
static void tegra20_cpufreq_put_supported_hw(void *opp_token)
{
dev_pm_opp_put_supported_hw(opp_table);
dev_pm_opp_put_supported_hw((unsigned long) opp_token);
}
static void tegra20_cpufreq_dt_unregister(void *cpufreq_dt)
......@@ -45,7 +45,6 @@ static void tegra20_cpufreq_dt_unregister(void *cpufreq_dt)
static int tegra20_cpufreq_probe(struct platform_device *pdev)
{
struct platform_device *cpufreq_dt;
struct opp_table *opp_table;
struct device *cpu_dev;
u32 versions[2];
int err;
......@@ -71,16 +70,15 @@ static int tegra20_cpufreq_probe(struct platform_device *pdev)
if (WARN_ON(!cpu_dev))
return -ENODEV;
opp_table = dev_pm_opp_set_supported_hw(cpu_dev, versions, 2);
err = PTR_ERR_OR_ZERO(opp_table);
if (err) {
err = dev_pm_opp_set_supported_hw(cpu_dev, versions, 2);
if (err < 0) {
dev_err(&pdev->dev, "failed to set supported hw: %d\n", err);
return err;
}
err = devm_add_action_or_reset(&pdev->dev,
tegra20_cpufreq_put_supported_hw,
opp_table);
(void *)((unsigned long) err));
if (err)
return err;
......
......@@ -60,7 +60,6 @@ struct ti_cpufreq_data {
struct device_node *opp_node;
struct regmap *syscon;
const struct ti_cpufreq_soc_data *soc_data;
struct opp_table *opp_table;
};
static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
......@@ -173,7 +172,7 @@ static struct ti_cpufreq_soc_data omap34xx_soc_data = {
* seems to always read as 0).
*/
static const char * const omap3_reg_names[] = {"cpu0", "vbb"};
static const char * const omap3_reg_names[] = {"cpu0", "vbb", NULL};
static struct ti_cpufreq_soc_data omap36xx_soc_data = {
.reg_names = omap3_reg_names,
......@@ -324,10 +323,13 @@ static int ti_cpufreq_probe(struct platform_device *pdev)
{
u32 version[VERSION_COUNT];
const struct of_device_id *match;
struct opp_table *ti_opp_table;
struct ti_cpufreq_data *opp_data;
const char * const default_reg_names[] = {"vdd", "vbb"};
const char * const default_reg_names[] = {"vdd", "vbb", NULL};
int ret;
struct dev_pm_opp_config config = {
.supported_hw = version,
.supported_hw_count = ARRAY_SIZE(version),
};
match = dev_get_platdata(&pdev->dev);
if (!match)
......@@ -370,33 +372,21 @@ static int ti_cpufreq_probe(struct platform_device *pdev)
if (ret)
goto fail_put_node;
ti_opp_table = dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
version, VERSION_COUNT);
if (IS_ERR(ti_opp_table)) {
dev_err(opp_data->cpu_dev,
"Failed to set supported hardware\n");
ret = PTR_ERR(ti_opp_table);
goto fail_put_node;
}
opp_data->opp_table = ti_opp_table;
if (opp_data->soc_data->multi_regulator) {
const char * const *reg_names = default_reg_names;
if (opp_data->soc_data->reg_names)
reg_names = opp_data->soc_data->reg_names;
ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev,
reg_names,
ARRAY_SIZE(default_reg_names));
if (IS_ERR(ti_opp_table)) {
dev_pm_opp_put_supported_hw(opp_data->opp_table);
ret = PTR_ERR(ti_opp_table);
goto fail_put_node;
config.regulator_names = opp_data->soc_data->reg_names;
else
config.regulator_names = default_reg_names;
}
ret = dev_pm_opp_set_config(opp_data->cpu_dev, &config);
if (ret < 0) {
dev_err(opp_data->cpu_dev, "Failed to set OPP config\n");
goto fail_put_node;
}
of_node_put(opp_data->opp_node);
register_cpufreq_dt:
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
......
......@@ -33,7 +33,7 @@ struct exynos_bus {
unsigned long curr_freq;
struct opp_table *opp_table;
int opp_token;
struct clk *clk;
unsigned int ratio;
};
......@@ -161,8 +161,7 @@ static void exynos_bus_exit(struct device *dev)
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
dev_pm_opp_put_regulators(bus->opp_token);
}
static void exynos_bus_passive_exit(struct device *dev)
......@@ -179,18 +178,16 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
struct exynos_bus *bus)
{
struct device *dev = bus->dev;
struct opp_table *opp_table;
const char *vdd = "vdd";
const char *supplies[] = { "vdd", NULL };
int i, ret, count, size;
opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
if (IS_ERR(opp_table)) {
ret = PTR_ERR(opp_table);
ret = dev_pm_opp_set_regulators(dev, supplies);
if (ret < 0) {
dev_err(dev, "failed to set regulators %d\n", ret);
return ret;
}
bus->opp_table = opp_table;
bus->opp_token = ret;
/*
* Get the devfreq-event devices to get the current utilization of
......@@ -236,8 +233,7 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
return 0;
err_regulator:
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
dev_pm_opp_put_regulators(bus->opp_token);
return ret;
}
......@@ -459,8 +455,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
err_reg:
dev_pm_opp_put_regulators(bus->opp_table);
bus->opp_table = NULL;
dev_pm_opp_put_regulators(bus->opp_token);
return ret;
}
......
......@@ -821,6 +821,15 @@ static int devm_tegra_devfreq_init_hw(struct device *dev,
return err;
}
static int tegra_devfreq_config_clks_nop(struct device *dev,
struct opp_table *opp_table,
struct dev_pm_opp *opp, void *data,
bool scaling_down)
{
/* We want to skip clk configuration via dev_pm_opp_set_opp() */
return 0;
}
static int tegra_devfreq_probe(struct platform_device *pdev)
{
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
......@@ -830,6 +839,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
unsigned int i;
long rate;
int err;
const char *clk_names[] = { "actmon", NULL };
struct dev_pm_opp_config config = {
.supported_hw = &hw_version,
.supported_hw_count = 1,
.clk_names = clk_names,
.config_clks = tegra_devfreq_config_clks_nop,
};
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
......@@ -874,13 +890,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err;
}
err = devm_pm_opp_set_supported_hw(&pdev->dev, &hw_version, 1);
err = devm_pm_opp_set_config(&pdev->dev, &config);
if (err) {
dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err);
dev_err(&pdev->dev, "Failed to set OPP config: %d\n", err);
return err;
}
err = devm_pm_opp_of_add_table_noclk(&pdev->dev, 0);
err = devm_pm_opp_of_add_table_indexed(&pdev->dev, 0);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err);
return err;
......
......@@ -111,6 +111,12 @@ int lima_devfreq_init(struct lima_device *ldev)
struct dev_pm_opp *opp;
unsigned long cur_freq;
int ret;
const char *regulator_names[] = { "mali", NULL };
const char *clk_names[] = { "core", NULL };
struct dev_pm_opp_config config = {
.regulator_names = regulator_names,
.clk_names = clk_names,
};
if (!device_property_present(dev, "operating-points-v2"))
/* Optional, continue without devfreq */
......@@ -118,11 +124,7 @@ int lima_devfreq_init(struct lima_device *ldev)
spin_lock_init(&ldevfreq->lock);
ret = devm_pm_opp_set_clkname(dev, "core");
if (ret)
return ret;
ret = devm_pm_opp_set_regulators(dev, (const char *[]){ "mali" }, 1);
ret = devm_pm_opp_set_config(dev, &config);
if (ret) {
/* Continue if the optional regulator is missing */
if (ret != -ENODEV)
......
......@@ -101,8 +101,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
return 0;
}
ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names,
pfdev->comp->num_supplies);
ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names);
if (ret) {
/* Continue if the optional regulator is missing */
if (ret != -ENODEV) {
......
......@@ -626,24 +626,29 @@ static int panfrost_remove(struct platform_device *pdev)
return 0;
}
static const char * const default_supplies[] = { "mali" };
/*
* The OPP core wants the supply names to be NULL terminated, but we need the
* correct num_supplies value for regulator core. Hence, we NULL terminate here
* and then initialize num_supplies with ARRAY_SIZE - 1.
*/
static const char * const default_supplies[] = { "mali", NULL };
static const struct panfrost_compatible default_data = {
.num_supplies = ARRAY_SIZE(default_supplies),
.num_supplies = ARRAY_SIZE(default_supplies) - 1,
.supply_names = default_supplies,
.num_pm_domains = 1, /* optional */
.pm_domain_names = NULL,
};
static const struct panfrost_compatible amlogic_data = {
.num_supplies = ARRAY_SIZE(default_supplies),
.num_supplies = ARRAY_SIZE(default_supplies) - 1,
.supply_names = default_supplies,
.vendor_quirk = panfrost_gpu_amlogic_quirk,
};
static const char * const mediatek_mt8183_supplies[] = { "mali", "sram" };
static const char * const mediatek_mt8183_supplies[] = { "mali", "sram", NULL };
static const char * const mediatek_mt8183_pm_domains[] = { "core0", "core1", "core2" };
static const struct panfrost_compatible mediatek_mt8183_data = {
.num_supplies = ARRAY_SIZE(mediatek_mt8183_supplies),
.num_supplies = ARRAY_SIZE(mediatek_mt8183_supplies) - 1,
.supply_names = mediatek_mt8183_supplies,
.num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains),
.pm_domain_names = mediatek_mt8183_pm_domains,
......
......@@ -875,7 +875,7 @@ static int vcodec_domains_get(struct venus_core *core)
}
skip_pmdomains:
if (!core->has_opp_table)
if (!core->res->opp_pmdomain)
return 0;
/* Attach the power domain for setting performance state */
......@@ -1007,6 +1007,10 @@ static int core_get_v4(struct venus_core *core)
if (ret)
return ret;
ret = vcodec_domains_get(core);
if (ret)
return ret;
if (core->res->opp_pmdomain) {
ret = devm_pm_opp_of_add_table(dev);
if (!ret) {
......@@ -1017,10 +1021,6 @@ static int core_get_v4(struct venus_core *core)
}
}
ret = vcodec_domains_get(core);
if (ret)
return ret;
return 0;
}
......
......@@ -1395,15 +1395,14 @@ static int tegra_emc_interconnect_init(struct tegra_emc *emc)
static int tegra_emc_opp_table_init(struct tegra_emc *emc)
{
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
struct opp_table *hw_opp_table;
int err;
int opp_token, err;
hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
err = PTR_ERR_OR_ZERO(hw_opp_table);
if (err) {
err = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
if (err < 0) {
dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
return err;
}
opp_token = err;
err = dev_pm_opp_of_add_table(emc->dev);
if (err) {
......@@ -1430,7 +1429,7 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)
remove_table:
dev_pm_opp_of_remove_table(emc->dev);
put_hw_table:
dev_pm_opp_put_supported_hw(hw_opp_table);
dev_pm_opp_put_supported_hw(opp_token);
return err;
}
......
This diff is collapsed.
......@@ -41,7 +41,7 @@
* the table if any of the mentioned functions have been invoked in the interim.
*/
int dev_pm_opp_init_cpufreq_table(struct device *dev,
struct cpufreq_frequency_table **table)
struct cpufreq_frequency_table **opp_table)
{
struct dev_pm_opp *opp;
struct cpufreq_frequency_table *freq_table = NULL;
......@@ -76,7 +76,7 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
freq_table[i].driver_data = i;
freq_table[i].frequency = CPUFREQ_TABLE_END;
*table = &freq_table[0];
*opp_table = &freq_table[0];
out:
if (ret)
......@@ -94,13 +94,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table);
* Free up the table allocated by dev_pm_opp_init_cpufreq_table
*/
void dev_pm_opp_free_cpufreq_table(struct device *dev,
struct cpufreq_frequency_table **table)
struct cpufreq_frequency_table **opp_table)
{
if (!table)
if (!opp_table)
return;
kfree(*table);
*table = NULL;
kfree(*opp_table);
*opp_table = NULL;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
#endif /* CONFIG_CPU_FREQ */
......
......@@ -74,6 +74,24 @@ static void opp_debug_create_bw(struct dev_pm_opp *opp,
}
}
static void opp_debug_create_clks(struct dev_pm_opp *opp,
struct opp_table *opp_table,
struct dentry *pdentry)
{
char name[12];
int i;
if (opp_table->clk_count == 1) {
debugfs_create_ulong("rate_hz", S_IRUGO, pdentry, &opp->rates[0]);
return;
}
for (i = 0; i < opp_table->clk_count; i++) {
snprintf(name, sizeof(name), "rate_hz_%d", i);
debugfs_create_ulong(name, S_IRUGO, pdentry, &opp->rates[i]);
}
}
static void opp_debug_create_supplies(struct dev_pm_opp *opp,
struct opp_table *opp_table,
struct dentry *pdentry)
......@@ -117,10 +135,11 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
* Get directory name for OPP.
*
* - Normally rate is unique to each OPP, use it to get unique opp-name.
* - For some devices rate isn't available, use index instead.
* - For some devices rate isn't available or there are multiple, use
* index instead for them.
*/
if (likely(opp->rate))
id = opp->rate;
if (likely(opp_table->clk_count == 1 && opp->rates[0]))
id = opp->rates[0];
else
id = _get_opp_count(opp_table);
......@@ -134,7 +153,6 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo);
debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend);
debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate);
debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate);
debugfs_create_u32("level", S_IRUGO, d, &opp->level);
debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
&opp->clock_latency_ns);
......@@ -142,6 +160,7 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
opp->of_name = of_node_full_name(opp->np);
debugfs_create_str("of_name", S_IRUGO, d, (char **)&opp->of_name);
opp_debug_create_clks(opp, opp_table, d);
opp_debug_create_supplies(opp, opp_table, d);
opp_debug_create_bw(opp, opp_table, d);
......
......@@ -242,19 +242,19 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
opp_table->np = opp_np;
_opp_table_alloc_required_tables(opp_table, dev, opp_np);
of_node_put(opp_np);
}
void _of_clear_opp_table(struct opp_table *opp_table)
{
_opp_table_free_required_tables(opp_table);
of_node_put(opp_table->np);
}
/*
* Release all resources previously acquired with a call to
* _of_opp_alloc_required_opps().
*/
void _of_opp_free_required_opps(struct opp_table *opp_table,
static void _of_opp_free_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp)
{
struct dev_pm_opp **required_opps = opp->required_opps;
......@@ -275,6 +275,12 @@ void _of_opp_free_required_opps(struct opp_table *opp_table,
kfree(required_opps);
}
void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp)
{
_of_opp_free_required_opps(opp_table, opp);
of_node_put(opp->np);
}
/* Populate all required OPPs which are part of "required-opps" list */
static int _of_opp_alloc_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp)
......@@ -767,7 +773,51 @@ void dev_pm_opp_of_remove_table(struct device *dev)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table,
static int _read_rate(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
struct device_node *np)
{
struct property *prop;
int i, count, ret;
u64 *rates;
prop = of_find_property(np, "opp-hz", NULL);
if (!prop)
return -ENODEV;
count = prop->length / sizeof(u64);
if (opp_table->clk_count != count) {
pr_err("%s: Count mismatch between opp-hz and clk_count (%d %d)\n",
__func__, count, opp_table->clk_count);
return -EINVAL;
}
rates = kmalloc_array(count, sizeof(*rates), GFP_KERNEL);
if (!rates)
return -ENOMEM;
ret = of_property_read_u64_array(np, "opp-hz", rates, count);
if (ret) {
pr_err("%s: Error parsing opp-hz: %d\n", __func__, ret);
} else {
/*
* Rate is defined as an unsigned long in clk API, and so
* casting explicitly to its type. Must be fixed once rate is 64
* bit guaranteed in clk API.
*/
for (i = 0; i < count; i++) {
new_opp->rates[i] = (unsigned long)rates[i];
/* This will happen for frequencies > 4.29 GHz */
WARN_ON(new_opp->rates[i] != rates[i]);
}
}
kfree(rates);
return ret;
}
static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
struct device_node *np, bool peak)
{
const char *name = peak ? "opp-peak-kBps" : "opp-avg-kBps";
......@@ -780,9 +830,9 @@ static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table,
return -ENODEV;
count = prop->length / sizeof(u32);
if (table->path_count != count) {
if (opp_table->path_count != count) {
pr_err("%s: Mismatch between %s and paths (%d %d)\n",
__func__, name, count, table->path_count);
__func__, name, count, opp_table->path_count);
return -EINVAL;
}
......@@ -808,34 +858,27 @@ static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table,
return ret;
}
static int _read_opp_key(struct dev_pm_opp *new_opp, struct opp_table *table,
struct device_node *np, bool *rate_not_available)
static int _read_opp_key(struct dev_pm_opp *new_opp,
struct opp_table *opp_table, struct device_node *np)
{
bool found = false;
u64 rate;
int ret;
ret = of_property_read_u64(np, "opp-hz", &rate);
if (!ret) {
/*
* Rate is defined as an unsigned long in clk API, and so
* casting explicitly to its type. Must be fixed once rate is 64
* bit guaranteed in clk API.
*/
new_opp->rate = (unsigned long)rate;
ret = _read_rate(new_opp, opp_table, np);
if (!ret)
found = true;
}
*rate_not_available = !!ret;
else if (ret != -ENODEV)
return ret;
/*
* Bandwidth consists of peak and average (optional) values:
* opp-peak-kBps = <path1_value path2_value>;
* opp-avg-kBps = <path1_value path2_value>;
*/
ret = _read_bw(new_opp, table, np, true);
ret = _read_bw(new_opp, opp_table, np, true);
if (!ret) {
found = true;
ret = _read_bw(new_opp, table, np, false);
ret = _read_bw(new_opp, opp_table, np, false);
}
/* The properties were found but we failed to parse them */
......@@ -881,13 +924,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
struct dev_pm_opp *new_opp;
u32 val;
int ret;
bool rate_not_available = false;
new_opp = _opp_allocate(opp_table);
if (!new_opp)
return ERR_PTR(-ENOMEM);
ret = _read_opp_key(new_opp, opp_table, np, &rate_not_available);
ret = _read_opp_key(new_opp, opp_table, np);
if (ret < 0) {
dev_err(dev, "%s: opp key field not found\n", __func__);
goto free_opp;
......@@ -895,14 +937,14 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
/* Check if the OPP supports hardware's hierarchy of versions or not */
if (!_opp_is_supported(dev, opp_table, np)) {
dev_dbg(dev, "OPP not supported by hardware: %lu\n",
new_opp->rate);
dev_dbg(dev, "OPP not supported by hardware: %s\n",
of_node_full_name(np));
goto free_opp;
}
new_opp->turbo = of_property_read_bool(np, "turbo-mode");
new_opp->np = np;
new_opp->np = of_node_get(np);
new_opp->dynamic = false;
new_opp->available = true;
......@@ -920,7 +962,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
if (opp_table->is_genpd)
new_opp->pstate = pm_genpd_opp_to_performance_state(dev, new_opp);
ret = _opp_add(dev, new_opp, opp_table, rate_not_available);
ret = _opp_add(dev, new_opp, opp_table);
if (ret) {
/* Don't return error for duplicate OPPs */
if (ret == -EBUSY)
......@@ -931,8 +973,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
/* OPP to select on device suspend */
if (of_property_read_bool(np, "opp-suspend")) {
if (opp_table->suspend_opp) {
/* Pick the OPP with higher rate as suspend OPP */
if (new_opp->rate > opp_table->suspend_opp->rate) {
/* Pick the OPP with higher rate/bw/level as suspend OPP */
if (_opp_compare_key(opp_table, new_opp, opp_table->suspend_opp) == 1) {
opp_table->suspend_opp->suspend = false;
new_opp->suspend = true;
opp_table->suspend_opp = new_opp;
......@@ -947,7 +989,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu level:%u\n",
__func__, new_opp->turbo, new_opp->rate,
__func__, new_opp->turbo, new_opp->rates[0],
new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min,
new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns,
new_opp->level);
......@@ -1084,7 +1126,7 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
return ret;
}
static int _of_add_table_indexed(struct device *dev, int index, bool getclk)
static int _of_add_table_indexed(struct device *dev, int index)
{
struct opp_table *opp_table;
int ret, count;
......@@ -1100,7 +1142,7 @@ static int _of_add_table_indexed(struct device *dev, int index, bool getclk)
index = 0;
}
opp_table = _add_opp_table_indexed(dev, index, getclk);
opp_table = _add_opp_table_indexed(dev, index, true);
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
......@@ -1124,11 +1166,11 @@ static void devm_pm_opp_of_table_release(void *data)
dev_pm_opp_of_remove_table(data);
}
static int _devm_of_add_table_indexed(struct device *dev, int index, bool getclk)
static int _devm_of_add_table_indexed(struct device *dev, int index)
{
int ret;
ret = _of_add_table_indexed(dev, index, getclk);
ret = _of_add_table_indexed(dev, index);
if (ret)
return ret;
......@@ -1156,7 +1198,7 @@ static int _devm_of_add_table_indexed(struct device *dev, int index, bool getclk
*/
int devm_pm_opp_of_add_table(struct device *dev)
{
return _devm_of_add_table_indexed(dev, 0, true);
return _devm_of_add_table_indexed(dev, 0);
}
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table);
......@@ -1179,7 +1221,7 @@ EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table);
*/
int dev_pm_opp_of_add_table(struct device *dev)
{
return _of_add_table_indexed(dev, 0, true);
return _of_add_table_indexed(dev, 0);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
......@@ -1195,7 +1237,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
*/
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
return _of_add_table_indexed(dev, index, true);
return _of_add_table_indexed(dev, index);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
......@@ -1208,42 +1250,10 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
*/
int devm_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
return _devm_of_add_table_indexed(dev, index, true);
return _devm_of_add_table_indexed(dev, index);
}
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_indexed);
/**
* dev_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device
* tree without getting clk for device.
* @dev: device pointer used to lookup OPP table.
* @index: Index number.
*
* Register the initial OPP table with the OPP library for given device only
* using the "operating-points-v2" property. Do not try to get the clk for the
* device.
*
* Return: Refer to dev_pm_opp_of_add_table() for return values.
*/
int dev_pm_opp_of_add_table_noclk(struct device *dev, int index)
{
return _of_add_table_indexed(dev, index, false);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_noclk);
/**
* devm_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device
* tree without getting clk for device.
* @dev: device pointer used to lookup OPP table.
* @index: Index number.
*
* This is a resource-managed variant of dev_pm_opp_of_add_table_noclk().
*/
int devm_pm_opp_of_add_table_noclk(struct device *dev, int index)
{
return _devm_of_add_table_indexed(dev, index, false);
}
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_noclk);
/* CPU device specific helpers */
/**
......
......@@ -28,6 +28,27 @@ extern struct mutex opp_table_lock;
extern struct list_head opp_tables, lazy_opp_tables;
/* OPP Config flags */
#define OPP_CONFIG_CLK BIT(0)
#define OPP_CONFIG_REGULATOR BIT(1)
#define OPP_CONFIG_REGULATOR_HELPER BIT(2)
#define OPP_CONFIG_PROP_NAME BIT(3)
#define OPP_CONFIG_SUPPORTED_HW BIT(4)
#define OPP_CONFIG_GENPD BIT(5)
/**
* struct opp_config_data - data for set config operations
* @opp_table: OPP table
* @flags: OPP config flags
*
* This structure stores the OPP config information for each OPP table
* configuration by the callers.
*/
struct opp_config_data {
struct opp_table *opp_table;
unsigned int flags;
};
/*
* Internal data structure organization with the OPP layer library is as
* follows:
......@@ -58,7 +79,7 @@ extern struct list_head opp_tables, lazy_opp_tables;
* @suspend: true if suspend OPP
* @removed: flag indicating that OPP's reference is dropped by OPP core.
* @pstate: Device's power domain's performance state.
* @rate: Frequency in hertz
* @rates: Frequencies in hertz
* @level: Performance level
* @supplies: Power supplies voltage/current values
* @bandwidth: Interconnect bandwidth values
......@@ -81,7 +102,7 @@ struct dev_pm_opp {
bool suspend;
bool removed;
unsigned int pstate;
unsigned long rate;
unsigned long *rates;
unsigned int level;
struct dev_pm_opp_supply *supplies;
......@@ -138,7 +159,7 @@ enum opp_table_access {
* @clock_latency_ns_max: Max clock latency in nanoseconds.
* @parsed_static_opps: Count of devices for which OPPs are initialized from DT.
* @shared_opp: OPP is shared between multiple devices.
* @current_rate: Currently configured frequency.
* @rate_clk_single: Currently configured frequency for single clk.
* @current_opp: Currently configured OPP for the table.
* @suspend_opp: Pointer to OPP to be used during device suspend.
* @genpd_virt_dev_lock: Mutex protecting the genpd virtual device pointers.
......@@ -149,7 +170,11 @@ enum opp_table_access {
* @supported_hw: Array of version number to support.
* @supported_hw_count: Number of elements in supported_hw array.
* @prop_name: A name to postfix to many DT properties, while parsing them.
* @clk: Device's clock handle
* @config_clks: Platform specific config_clks() callback.
* @clks: Device's clock handles, for multiple clocks.
* @clk: Device's clock handle, for single clock.
* @clk_count: Number of clocks.
* @config_regulators: Platform specific config_regulators() callback.
* @regulators: Supply regulators
* @regulator_count: Number of power supply regulators. Its value can be -1
* (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
......@@ -159,9 +184,6 @@ enum opp_table_access {
* @enabled: Set to true if the device's resources are enabled/configured.
* @genpd_performance_state: Device's power domain support performance state.
* @is_genpd: Marks if the OPP table belongs to a genpd.
* @set_opp: Platform specific set_opp callback
* @sod_supplies: Set opp data supplies
* @set_opp_data: Data to be passed to set_opp callback
* @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry.
*
......@@ -188,7 +210,7 @@ struct opp_table {
unsigned int parsed_static_opps;
enum opp_table_access shared_opp;
unsigned long current_rate;
unsigned long rate_clk_single;
struct dev_pm_opp *current_opp;
struct dev_pm_opp *suspend_opp;
......@@ -200,7 +222,11 @@ struct opp_table {
unsigned int *supported_hw;
unsigned int supported_hw_count;
const char *prop_name;
config_clks_t config_clks;
struct clk **clks;
struct clk *clk;
int clk_count;
config_regulators_t config_regulators;
struct regulator **regulators;
int regulator_count;
struct icc_path **paths;
......@@ -209,10 +235,6 @@ struct opp_table {
bool genpd_performance_state;
bool is_genpd;
int (*set_opp)(struct dev_pm_set_opp_data *data);
struct dev_pm_opp_supply *sod_supplies;
struct dev_pm_set_opp_data *set_opp_data;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
char dentry_name[NAME_MAX];
......@@ -228,8 +250,8 @@ struct opp_table *_find_opp_table(struct device *dev);
struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table);
struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
void _opp_free(struct dev_pm_opp *opp);
int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2);
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available);
int _opp_compare_key(struct opp_table *opp_table, struct dev_pm_opp *opp1, struct dev_pm_opp *opp2);
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table);
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu);
struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk);
......@@ -245,14 +267,12 @@ static inline bool lazy_linking_pending(struct opp_table *opp_table)
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index);
void _of_clear_opp_table(struct opp_table *opp_table);
struct opp_table *_managed_opp(struct device *dev, int index);
void _of_opp_free_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp);
void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp);
#else
static inline void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index) {}
static inline void _of_clear_opp_table(struct opp_table *opp_table) {}
static inline struct opp_table *_managed_opp(struct device *dev, int index) { return NULL; }
static inline void _of_opp_free_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp) {}
static inline void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp) {}
#endif
#ifdef CONFIG_DEBUG_FS
......
......@@ -36,11 +36,15 @@ struct ti_opp_supply_optimum_voltage_table {
* @vdd_table: Optimized voltage mapping table
* @num_vdd_table: number of entries in vdd_table
* @vdd_absolute_max_voltage_uv: absolute maximum voltage in UV for the supply
* @old_supplies: Placeholder for supplies information for old OPP.
* @new_supplies: Placeholder for supplies information for new OPP.
*/
struct ti_opp_supply_data {
struct ti_opp_supply_optimum_voltage_table *vdd_table;
u32 num_vdd_table;
u32 vdd_absolute_max_voltage_uv;
struct dev_pm_opp_supply old_supplies[2];
struct dev_pm_opp_supply new_supplies[2];
};
static struct ti_opp_supply_data opp_data;
......@@ -266,27 +270,32 @@ static int _opp_set_voltage(struct device *dev,
return 0;
}
/**
* ti_opp_supply_set_opp() - do the opp supply transition
* @data: information on regulators and new and old opps provided by
* opp core to use in transition
*
* Return: If successful, 0, else appropriate error value.
*/
static int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data)
/* Do the opp supply transition */
static int ti_opp_config_regulators(struct device *dev,
struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp,
struct regulator **regulators, unsigned int count)
{
struct dev_pm_opp_supply *old_supply_vdd = &data->old_opp.supplies[0];
struct dev_pm_opp_supply *old_supply_vbb = &data->old_opp.supplies[1];
struct dev_pm_opp_supply *new_supply_vdd = &data->new_opp.supplies[0];
struct dev_pm_opp_supply *new_supply_vbb = &data->new_opp.supplies[1];
struct device *dev = data->dev;
unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate;
struct clk *clk = data->clk;
struct regulator *vdd_reg = data->regulators[0];
struct regulator *vbb_reg = data->regulators[1];
struct dev_pm_opp_supply *old_supply_vdd = &opp_data.old_supplies[0];
struct dev_pm_opp_supply *old_supply_vbb = &opp_data.old_supplies[1];
struct dev_pm_opp_supply *new_supply_vdd = &opp_data.new_supplies[0];
struct dev_pm_opp_supply *new_supply_vbb = &opp_data.new_supplies[1];
struct regulator *vdd_reg = regulators[0];
struct regulator *vbb_reg = regulators[1];
unsigned long old_freq, freq;
int vdd_uv;
int ret;
/* We must have two regulators here */
WARN_ON(count != 2);
/* Fetch supplies and freq information from OPP core */
ret = dev_pm_opp_get_supplies(new_opp, opp_data.new_supplies);
WARN_ON(ret);
old_freq = dev_pm_opp_get_freq(old_opp);
freq = dev_pm_opp_get_freq(new_opp);
WARN_ON(!old_freq || !freq);
vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data,
new_supply_vdd->u_volt);
......@@ -303,39 +312,24 @@ static int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data)
ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
if (ret)
goto restore_voltage;
}
/* Change frequency */
dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
__func__, old_freq, freq);
ret = clk_set_rate(clk, freq);
if (ret) {
dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
ret);
goto restore_voltage;
}
/* Scaling down? Scale voltage after frequency */
if (freq < old_freq) {
} else {
ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
if (ret)
goto restore_freq;
goto restore_voltage;
ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
"vdd");
if (ret)
goto restore_freq;
goto restore_voltage;
}
return 0;
restore_freq:
ret = clk_set_rate(clk, old_freq);
if (ret)
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
__func__, old_freq);
restore_voltage:
/* Fetch old supplies information only if required */
ret = dev_pm_opp_get_supplies(old_opp, opp_data.old_supplies);
WARN_ON(ret);
/* This shouldn't harm even if the voltages weren't updated earlier */
if (old_supply_vdd->u_volt) {
ret = _opp_set_voltage(dev, old_supply_vbb, 0, vbb_reg, "vbb");
......@@ -405,9 +399,8 @@ static int ti_opp_supply_probe(struct platform_device *pdev)
return ret;
}
ret = PTR_ERR_OR_ZERO(dev_pm_opp_register_set_opp_helper(cpu_dev,
ti_opp_supply_set_opp));
if (ret)
ret = dev_pm_opp_set_config_regulators(cpu_dev, ti_opp_config_regulators);
if (ret < 0)
_free_optimized_voltages(dev, &opp_data);
return ret;
......
......@@ -107,29 +107,46 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev,
{
u32 hw_version;
int err;
/*
* The clk's connection id to set is NULL and this is a NULL terminated
* array, hence two NULL entries.
*/
const char *clk_names[] = { NULL, NULL };
struct dev_pm_opp_config config = {
/*
* For some devices we don't have any OPP table in the DT, and
* in order to use the same code path for all the devices, we
* create a dummy OPP table for them via this. The dummy OPP
* table is only capable of doing clk_set_rate() on invocation
* of dev_pm_opp_set_rate() and doesn't provide any other
* functionality.
*/
.clk_names = clk_names,
};
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"))
if (of_machine_is_compatible("nvidia,tegra20")) {
hw_version = BIT(tegra_sku_info.soc_process_id);
else
config.supported_hw = &hw_version;
config.supported_hw_count = 1;
} else if (of_machine_is_compatible("nvidia,tegra30")) {
hw_version = BIT(tegra_sku_info.soc_speedo_id);
config.supported_hw = &hw_version;
config.supported_hw_count = 1;
}
err = devm_pm_opp_set_supported_hw(dev, &hw_version, 1);
err = devm_pm_opp_set_config(dev, &config);
if (err) {
dev_err(dev, "failed to set OPP supported HW: %d\n", err);
dev_err(dev, "failed to set OPP config: %d\n", err);
return err;
}
/*
* Tegra114+ doesn't support OPP yet, return early for non tegra20/30
* case.
*/
if (!config.supported_hw)
return -ENODEV;
/*
* Older device-trees have an empty OPP table, we will get
* -ENODEV from devm_pm_opp_of_add_table() in this case.
......
......@@ -1384,7 +1384,7 @@ tegra_pmc_core_pd_opp_to_performance_state(struct generic_pm_domain *genpd,
static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
{
struct generic_pm_domain *genpd;
const char *rname = "core";
const char *rname[] = { "core", NULL};
int err;
genpd = devm_kzalloc(pmc->dev, sizeof(*genpd), GFP_KERNEL);
......@@ -1395,7 +1395,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state;
err = devm_pm_opp_set_regulators(pmc->dev, &rname, 1);
err = devm_pm_opp_set_regulators(pmc->dev, rname);
if (err)
return dev_err_probe(pmc->dev, err,
"failed to set core OPP regulator\n");
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment