static int scpi_cpufreq_init(struct cpufreq_policy *policy) { int ret; unsigned int latency; struct device *cpu_dev; struct scpi_data *priv; struct cpufreq_frequency_table *freq_table; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { pr_err("failed to get cpu%d device\n", policy->cpu); return -ENODEV; } ret = scpi_ops->add_opps_to_device(cpu_dev); if (ret) { dev_warn(cpu_dev, "failed to add opps to the device\n"); return ret; } ret = scpi_get_sharing_cpus(cpu_dev, policy->cpus); if (ret) { dev_warn(cpu_dev, "failed to get sharing cpumask\n"); return ret; } ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); if (ret) { dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); return ret; } ret = dev_pm_opp_get_opp_count(cpu_dev); if (ret <= 0) { dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ret = -EPROBE_DEFER; goto out_free_opp; } priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; goto out_free_opp; } ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto out_free_priv; } priv->cpu_dev = cpu_dev; priv->clk = clk_get(cpu_dev, NULL); if (IS_ERR(priv->clk)) { dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d\n", __func__, cpu_dev->id); ret = PTR_ERR(priv->clk); goto out_free_cpufreq_table; } policy->driver_data = priv; ret = cpufreq_table_validate_and_show(policy, freq_table); if (ret) { dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, ret); goto out_put_clk; } /* scpi allows DVFS request for any domain from any CPU */ policy->dvfs_possible_from_any_cpu = true; latency = scpi_ops->get_transition_latency(cpu_dev); if (!latency) latency = CPUFREQ_ETERNAL; policy->cpuinfo.transition_latency = latency; policy->fast_switch_possible = false; return 0; out_put_clk: clk_put(priv->clk); out_free_cpufreq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_free_priv: kfree(priv); out_free_opp: dev_pm_opp_cpumask_remove_table(policy->cpus); return ret; }
static int cpufreq_init(struct cpufreq_policy *policy) { struct cpufreq_frequency_table *freq_table; struct private_data *priv; struct device *cpu_dev; struct clk *cpu_clk; struct dev_pm_opp *suspend_opp; unsigned int transition_latency; bool opp_v1 = false; const char *name; int ret; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { pr_err("failed to get cpu%d device\n", policy->cpu); return -ENODEV; } cpu_clk = clk_get(cpu_dev, NULL); if (IS_ERR(cpu_clk)) { ret = PTR_ERR(cpu_clk); dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret); return ret; } /* Get OPP-sharing information from "operating-points-v2" bindings */ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus); if (ret) { /* * operating-points-v2 not supported, fallback to old method of * finding shared-OPPs for backward compatibility. */ if (ret == -ENOENT) opp_v1 = true; else goto out_put_clk; } /* * OPP layer will be taking care of regulators now, but it needs to know * the name of the regulator first. */ name = find_supply_name(cpu_dev); if (name) { ret = dev_pm_opp_set_regulator(cpu_dev, name); if (ret) { dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n", policy->cpu, ret); goto out_put_clk; } } /* * Initialize OPP tables for all policy->cpus. They will be shared by * all CPUs which have marked their CPUs shared with OPP bindings. * * For platforms not using operating-points-v2 bindings, we do this * before updating policy->cpus. Otherwise, we will end up creating * duplicate OPPs for policy->cpus. * * OPPs might be populated at runtime, don't check for error here */ dev_pm_opp_of_cpumask_add_table(policy->cpus); /* * But we need OPP table to function so if it is not there let's * give platform code chance to provide it for us. */ ret = dev_pm_opp_get_opp_count(cpu_dev); if (ret <= 0) { dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ret = -EPROBE_DEFER; goto out_free_opp; } if (opp_v1) { struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data(); if (!pd || !pd->independent_clocks) cpumask_setall(policy->cpus); /* * OPP tables are initialized only for policy->cpu, do it for * others as well. */ ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); if (ret) dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); } priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; goto out_free_opp; } priv->reg_name = name; ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto out_free_priv; } priv->cpu_dev = cpu_dev; policy->driver_data = priv; policy->clk = cpu_clk; rcu_read_lock(); suspend_opp = dev_pm_opp_get_suspend_opp(cpu_dev); if (suspend_opp) policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000; rcu_read_unlock(); ret = cpufreq_table_validate_and_show(policy, freq_table); if (ret) { dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, ret); goto out_free_cpufreq_table; } /* Support turbo/boost mode */ if (policy_has_boost_freq(policy)) { /* This gets disabled by core on driver unregister */ ret = cpufreq_enable_boost_support(); if (ret) goto out_free_cpufreq_table; cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs; } transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); if (!transition_latency) transition_latency = CPUFREQ_ETERNAL; policy->cpuinfo.transition_latency = transition_latency; return 0; out_free_cpufreq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_free_priv: kfree(priv); out_free_opp: dev_pm_opp_of_cpumask_remove_table(policy->cpus); if (name) dev_pm_opp_put_regulator(cpu_dev); out_put_clk: clk_put(cpu_clk); return ret; }