Commit f6235eb1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pm-5.9-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull more power management updates from Rafael Wysocki:
 "These are mostly ARM cpufreq driver updates plus a cpufreq core
  cleanup, an ARM-wide change to make schedutil the default scaling
  governor, an intel_pstate driver fix and some runtime PM changes
  regarding kerneldoc comments.

  Specifics:

   - Add adaptive voltage scaling (AVS) support to the brcmstb cpufreq
     driver and clean it up (Florian Fainelli, Markus Mayer).

   - Add a new Tegra cpufreq driver and clean up the existing one (Jon
     Hunter, Sumit Gupta).

   - Add bandwidth level support to the Qcom cpufreq driver along with
     OPP changes (Sibi Sankar).

   - Clean up the sti, cpufreq-dt, ap806, CPPC cpufreq drivers (Viresh
     Kumar, Lee Jones, Ivan Kokshaysky, Sven Auhagen, Xin Hao).

   - Make schedutil the default governor for ARM (Valentin Schneider).

   - Fix dependency issues for the imx cpufreq driver (Walter Lozano).

   - Clean up cached_resolved_idx handlihng in the cpufreq core (Viresh
     Kumar).

   - Fix the intel_pstate driver to use the correct maximum frequency
     value when MSR_TURBO_RATIO_LIMIT is 0 (Srinivas Pandruvada).

   - Provide kenrneldoc comments for multiple runtime PM helpers and
     improve the pm_runtime_get_if_active() kerneldoc (Rafael Wysocki)"

* tag 'pm-5.9-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (22 commits)
  cpufreq: intel_pstate: Fix cpuinfo_max_freq when MSR_TURBO_RATIO_LIMIT is 0
  PM: runtime: Improve kerneldoc of pm_runtime_get_if_active()
  PM: runtime: Add kerneldoc comments to multiple helpers
  cpufreq: make schedutil the default for arm and arm64
  cpufreq: cached_resolved_idx can not be negative
  cpufreq: Add Tegra194 cpufreq driver
  dt-bindings: arm: Add NVIDIA Tegra194 CPU Complex binding
  cpufreq: imx: Select NVMEM_IMX_OCOTP
  cpufreq: sti-cpufreq: Fix some formatting and misspelling issues
  cpufreq: tegra186: Simplify probe return path
  cpufreq: CPPC: Reuse caps variable in few routines
  cpufreq: ap806: fix cpufreq driver needs ap cpu clk
  cpufreq: cppc: Reorder code and remove apply_hisi_workaround variable
  cpufreq: dt: fix oops on armada37xx
  cpufreq: brcmstb-avs-cpufreq: send S2_ENTER / S2_EXIT commands to AVS
  cpufreq: brcmstb-avs-cpufreq: Support polling AVS firmware
  cpufreq: brcmstb-avs-cpufreq: more flexible interface for __issue_avs_command()
  cpufreq: qcom: Disable fast switch when scaling DDR/L3
  cpufreq: qcom: Update the bandwidth levels on frequency change
  OPP: Add and export helper to set bandwidth
  ...
parents 2f12d440 0873ad92
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/arm/nvidia,tegra194-ccplex.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: NVIDIA Tegra194 CPU Complex device tree bindings
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jonathan Hunter <jonathanh@nvidia.com>
- Sumit Gupta <sumitg@nvidia.com>
description: |+
Tegra194 SOC has homogeneous architecture where each cluster has two
symmetric cores. Compatible string in "cpus" node represents the CPU
Complex having all clusters.
properties:
$nodename:
const: cpus
compatible:
enum:
- nvidia,tegra194-ccplex
nvidia,bpmp:
$ref: '/schemas/types.yaml#/definitions/phandle'
description: |
Specifies the bpmp node that needs to be queried to get
operating point data for all CPUs.
examples:
- |
cpus {
compatible = "nvidia,tegra194-ccplex";
nvidia,bpmp = <&bpmp>;
#address-cells = <1>;
#size-cells = <0>;
cpu0_0: cpu@0 {
compatible = "nvidia,tegra194-carmel";
device_type = "cpu";
reg = <0x0>;
enable-method = "psci";
};
cpu0_1: cpu@1 {
compatible = "nvidia,tegra194-carmel";
device_type = "cpu";
reg = <0x001>;
enable-method = "psci";
};
cpu1_0: cpu@100 {
compatible = "nvidia,tegra194-carmel";
device_type = "cpu";
reg = <0x100>;
enable-method = "psci";
};
cpu1_1: cpu@101 {
compatible = "nvidia,tegra194-carmel";
device_type = "cpu";
reg = <0x101>;
enable-method = "psci";
};
};
...
......@@ -1085,24 +1085,26 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_resume);
/**
* pm_runtime_get_if_active - Conditionally bump up the device's usage counter.
* pm_runtime_get_if_active - Conditionally bump up device usage counter.
* @dev: Device to handle.
* @ign_usage_count: Whether or not to look at the current usage counter value.
*
* Return -EINVAL if runtime PM is disabled for the device.
* Return -EINVAL if runtime PM is disabled for @dev.
*
* Otherwise, if the device's runtime PM status is RPM_ACTIVE and either
* ign_usage_count is true or the device's usage_count is non-zero, increment
* the counter and return 1. Otherwise return 0 without changing the counter.
* Otherwise, if the runtime PM status of @dev is %RPM_ACTIVE and either
* @ign_usage_count is %true or the runtime PM usage counter of @dev is not
* zero, increment the usage counter of @dev and return 1. Otherwise, return 0
* without changing the usage counter.
*
* If ign_usage_count is true, the function can be used to prevent suspending
* the device when its runtime PM status is RPM_ACTIVE.
* If @ign_usage_count is %true, this function can be used to prevent suspending
* the device when its runtime PM status is %RPM_ACTIVE.
*
* If ign_usage_count is false, the function can be used to prevent suspending
* the device when both its runtime PM status is RPM_ACTIVE and its usage_count
* is non-zero.
* If @ign_usage_count is %false, this function can be used to prevent
* suspending the device when both its runtime PM status is %RPM_ACTIVE and its
* runtime PM usage counter is not zero.
*
* The caller is resposible for putting the device's usage count when ther
* return value is greater than zero.
* The caller is resposible for decrementing the runtime PM usage counter of
* @dev after this function has returned a positive value for it.
*/
int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count)
{
......
......@@ -37,7 +37,7 @@ config CPU_FREQ_STAT
choice
prompt "Default CPUFreq governor"
default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ
default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if BIG_LITTLE
default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if ARM64 || ARM
default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if X86_INTEL_PSTATE && SMP
default CPU_FREQ_DEFAULT_GOV_PERFORMANCE
help
......
......@@ -41,6 +41,7 @@ config ARM_ARMADA_37XX_CPUFREQ
config ARM_ARMADA_8K_CPUFREQ
tristate "Armada 8K CPUFreq driver"
depends on ARCH_MVEBU && CPUFREQ_DT
select ARMADA_AP_CPU_CLK
help
This enables the CPUFreq driver support for Marvell
Armada8k SOCs.
......@@ -93,6 +94,7 @@ config ARM_IMX6Q_CPUFREQ
tristate "Freescale i.MX6 cpufreq support"
depends on ARCH_MXC
depends on REGULATOR_ANATOP
select NVMEM_IMX_OCOTP
select PM_OPP
help
This adds cpufreq driver support for Freescale i.MX6 series SoCs.
......@@ -314,6 +316,13 @@ config ARM_TEGRA186_CPUFREQ
help
This adds the CPUFreq driver support for Tegra186 SOCs.
config ARM_TEGRA194_CPUFREQ
tristate "Tegra194 CPUFreq support"
depends on ARCH_TEGRA_194_SOC && TEGRA_BPMP
default y
help
This adds CPU frequency driver support for Tegra194 SOCs.
config ARM_TI_CPUFREQ
bool "Texas Instruments CPUFreq support"
depends on ARCH_OMAP2PLUS
......
......@@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o
obj-$(CONFIG_ARM_TEGRA194_CPUFREQ) += tegra194-cpufreq.o
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
......
......@@ -456,6 +456,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
/* Now that everything is setup, enable the DVFS at hardware level */
armada37xx_cpufreq_enable_dvfs(nb_pm_base);
memset(&pdata, 0, sizeof(pdata));
pdata.suspend = armada37xx_cpufreq_suspend;
pdata.resume = armada37xx_cpufreq_resume;
......
......@@ -42,6 +42,7 @@
*/
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
......@@ -178,6 +179,7 @@ struct private_data {
struct completion done;
struct semaphore sem;
struct pmap pmap;
int host_irq;
};
static void __iomem *__map_region(const char *name)
......@@ -195,11 +197,36 @@ static void __iomem *__map_region(const char *name)
return ptr;
}
static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send,
static unsigned long wait_for_avs_command(struct private_data *priv,
unsigned long timeout)
{
unsigned long time_left = 0;
u32 val;
/* Event driven, wait for the command interrupt */
if (priv->host_irq >= 0)
return wait_for_completion_timeout(&priv->done,
msecs_to_jiffies(timeout));
/* Polling for command completion */
do {
time_left = timeout;
val = readl(priv->base + AVS_MBOX_STATUS);
if (val)
break;
usleep_range(1000, 2000);
} while (--timeout);
return time_left;
}
static int __issue_avs_command(struct private_data *priv, unsigned int cmd,
unsigned int num_in, unsigned int num_out,
u32 args[])
{
unsigned long time_left = msecs_to_jiffies(AVS_TIMEOUT);
void __iomem *base = priv->base;
unsigned long time_left;
unsigned int i;
int ret;
u32 val;
......@@ -225,11 +252,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send,
/* Clear status before we begin. */
writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);
/* We need to send arguments for this command. */
if (args && is_send) {
for (i = 0; i < AVS_MAX_CMD_ARGS; i++)
writel(args[i], base + AVS_MBOX_PARAM(i));
}
/* Provide input parameters */
for (i = 0; i < num_in; i++)
writel(args[i], base + AVS_MBOX_PARAM(i));
/* Protect from spurious interrupts. */
reinit_completion(&priv->done);
......@@ -239,7 +264,7 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send,
writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0);
/* Wait for AVS co-processor to finish processing the command. */
time_left = wait_for_completion_timeout(&priv->done, time_left);
time_left = wait_for_avs_command(priv, AVS_TIMEOUT);
/*
* If the AVS status is not in the expected range, it means AVS didn't
......@@ -256,11 +281,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send,
goto out;
}
/* This command returned arguments, so we read them back. */
if (args && !is_send) {
for (i = 0; i < AVS_MAX_CMD_ARGS; i++)
args[i] = readl(base + AVS_MBOX_PARAM(i));
}
/* Process returned values */
for (i = 0; i < num_out; i++)
args[i] = readl(base + AVS_MBOX_PARAM(i));
/* Clear status to tell AVS co-processor we are done. */
writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);
......@@ -338,7 +361,7 @@ static int brcm_avs_get_pmap(struct private_data *priv, struct pmap *pmap)
u32 args[AVS_MAX_CMD_ARGS];
int ret;
ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, false, args);
ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, 0, 4, args);
if (ret || !pmap)
return ret;
......@@ -359,7 +382,7 @@ static int brcm_avs_set_pmap(struct private_data *priv, struct pmap *pmap)
args[2] = pmap->p2;
args[3] = pmap->state;
return __issue_avs_command(priv, AVS_CMD_SET_PMAP, true, args);
return __issue_avs_command(priv, AVS_CMD_SET_PMAP, 4, 0, args);
}
static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate)
......@@ -367,7 +390,7 @@ static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate)
u32 args[AVS_MAX_CMD_ARGS];
int ret;
ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, false, args);
ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, 0, 1, args);
if (ret)
return ret;
*pstate = args[0];
......@@ -381,7 +404,8 @@ static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate)
args[0] = pstate;
return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, true, args);
return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, 1, 0, args);
}
static u32 brcm_avs_get_voltage(void __iomem *base)
......@@ -482,7 +506,14 @@ static int brcm_avs_suspend(struct cpufreq_policy *policy)
* AVS co-processor, not necessarily the P-state we are running at now.
* So, we get the current P-state explicitly.
*/
return brcm_avs_get_pstate(priv, &priv->pmap.state);
ret = brcm_avs_get_pstate(priv, &priv->pmap.state);
if (ret)
return ret;
/* This is best effort. Nothing to do if it fails. */
(void)__issue_avs_command(priv, AVS_CMD_S2_ENTER, 0, 0, NULL);
return 0;
}
static int brcm_avs_resume(struct cpufreq_policy *policy)
......@@ -490,6 +521,9 @@ static int brcm_avs_resume(struct cpufreq_policy *policy)
struct private_data *priv = policy->driver_data;
int ret;
/* This is best effort. Nothing to do if it fails. */
(void)__issue_avs_command(priv, AVS_CMD_S2_EXIT, 0, 0, NULL);
ret = brcm_avs_set_pmap(priv, &priv->pmap);
if (ret == -EEXIST) {
struct platform_device *pdev = cpufreq_get_driver_data();
......@@ -511,7 +545,7 @@ static int brcm_avs_prepare_init(struct platform_device *pdev)
{
struct private_data *priv;
struct device *dev;
int host_irq, ret;
int ret;
dev = &pdev->dev;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
......@@ -538,19 +572,14 @@ static int brcm_avs_prepare_init(struct platform_device *pdev)
goto unmap_base;
}
host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR);
if (host_irq < 0) {
dev_err(dev, "Couldn't find interrupt %s -- %d\n",
BRCM_AVS_HOST_INTR, host_irq);
ret = host_irq;
goto unmap_intr_base;
}
priv->host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR);
ret = devm_request_irq(dev, host_irq, irq_handler, IRQF_TRIGGER_RISING,
ret = devm_request_irq(dev, priv->host_irq, irq_handler,
IRQF_TRIGGER_RISING,
BRCM_AVS_HOST_INTR, priv);
if (ret) {
if (ret && priv->host_irq >= 0) {
dev_err(dev, "IRQ request failed: %s (%d) -- %d\n",
BRCM_AVS_HOST_INTR, host_irq, ret);
BRCM_AVS_HOST_INTR, priv->host_irq, ret);
goto unmap_intr_base;
}
......@@ -593,7 +622,7 @@ static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy)
/* All cores share the same clock and thus the same policy. */
cpumask_setall(policy->cpus);
ret = __issue_avs_command(priv, AVS_CMD_ENABLE, false, NULL);
ret = __issue_avs_command(priv, AVS_CMD_ENABLE, 0, 0, NULL);
if (!ret) {
unsigned int pstate;
......
......@@ -45,8 +45,6 @@ struct cppc_workaround_oem_info {
u32 oem_revision;
};
static bool apply_hisi_workaround;
static struct cppc_workaround_oem_info wa_info[] = {
{
.oem_id = "HISI ",
......@@ -59,50 +57,6 @@ static struct cppc_workaround_oem_info wa_info[] = {
}
};
static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
unsigned int perf);
/*
* HISI platform does not support delivered performance counter and
* reference performance counter. It can calculate the performance using the
* platform specific mechanism. We reuse the desired performance register to
* store the real performance calculated by the platform.
*/
static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum)
{
struct cppc_cpudata *cpudata = all_cpu_data[cpunum];
u64 desired_perf;
int ret;
ret = cppc_get_desired_perf(cpunum, &desired_perf);
if (ret < 0)
return -EIO;
return cppc_cpufreq_perf_to_khz(cpudata, desired_perf);
}
static void cppc_check_hisi_workaround(void)
{
struct acpi_table_header *tbl;
acpi_status status = AE_OK;
int i;
status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl);
if (ACPI_FAILURE(status) || !tbl)
return;
for (i = 0; i < ARRAY_SIZE(wa_info); i++) {
if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) &&
!memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) &&
wa_info[i].oem_revision == tbl->oem_revision) {
apply_hisi_workaround = true;
break;
}
}
acpi_put_table(tbl);
}
/* Callback function used to retrieve the max frequency from DMI */
static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
{
......@@ -161,7 +115,7 @@ static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
if (!max_khz)
max_khz = cppc_get_dmi_max_khz();
mul = max_khz;
div = cpu->perf_caps.highest_perf;
div = caps->highest_perf;
}
return (u64)perf * mul / div;
}
......@@ -184,7 +138,7 @@ static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu,
} else {
if (!max_khz)
max_khz = cppc_get_dmi_max_khz();
mul = cpu->perf_caps.highest_perf;
mul = caps->highest_perf;
div = max_khz;
}
......@@ -402,9 +356,6 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum)
struct cppc_cpudata *cpu = all_cpu_data[cpunum];
int ret;
if (apply_hisi_workaround)
return hisi_cppc_cpufreq_get_rate(cpunum);
ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0);
if (ret)
return ret;
......@@ -455,6 +406,48 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
.name = "cppc_cpufreq",
};
/*
* HISI platform does not support delivered performance counter and
* reference performance counter. It can calculate the performance using the
* platform specific mechanism. We reuse the desired performance register to
* store the real performance calculated by the platform.
*/
static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum)
{
struct cppc_cpudata *cpudata = all_cpu_data[cpunum];
u64 desired_perf;
int ret;
ret = cppc_get_desired_perf(cpunum, &desired_perf);
if (ret < 0)
return -EIO;
return cppc_cpufreq_perf_to_khz(cpudata, desired_perf);
}
static void cppc_check_hisi_workaround(void)
{
struct acpi_table_header *tbl;
acpi_status status = AE_OK;
int i;
status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl);
if (ACPI_FAILURE(status) || !tbl)
return;
for (i = 0; i < ARRAY_SIZE(wa_info); i++) {
if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) &&
!memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) &&
wa_info[i].oem_revision == tbl->oem_revision) {
/* Overwrite the get() callback */
cppc_cpufreq_driver.get = hisi_cppc_cpufreq_get_rate;
break;
}
}
acpi_put_table(tbl);
}
static int __init cppc_cpufreq_init(void)
{
int i, ret = 0;
......
......@@ -132,6 +132,8 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "qcom,apq8096", },
{ .compatible = "qcom,msm8996", },
{ .compatible = "qcom,qcs404", },
{ .compatible = "qcom,sc7180", },
{ .compatible = "qcom,sdm845", },
{ .compatible = "st,stih407", },
{ .compatible = "st,stih410", },
......
......@@ -541,7 +541,7 @@ unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy,
policy->cached_target_freq = target_freq;
if (cpufreq_driver->target_index) {
int idx;
unsigned int idx;
idx = cpufreq_frequency_table_target(policy, target_freq,
CPUFREQ_RELATION_L);
......
......@@ -1647,6 +1647,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
intel_pstate_get_hwp_max(cpu->cpu, &phy_max, &current_max);
cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling;
cpu->pstate.turbo_pstate = phy_max;
} else {
cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling;
}
......
......@@ -6,6 +6,7 @@
#include <linux/bitfield.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/interconnect.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
......@@ -30,6 +31,48 @@
static unsigned long cpu_hw_rate, xo_rate;
static struct platform_device *global_pdev;
static bool icc_scaling_enabled;
static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy,
unsigned long freq_khz)
{
unsigned long freq_hz = freq_khz * 1000;
struct dev_pm_opp *opp;
struct device *dev;
int ret;
dev = get_cpu_device(policy->cpu);
if (!dev)
return -ENODEV;
opp = dev_pm_opp_find_freq_exact(dev, freq_hz, true);
if (IS_ERR(opp))
return PTR_ERR(opp);
ret = dev_pm_opp_set_bw(dev, opp);
dev_pm_opp_put(opp);
return ret;
}
static int qcom_cpufreq_update_opp(struct device *cpu_dev,
unsigned long freq_khz,
unsigned long volt)
{
unsigned long freq_hz = freq_khz * 1000;
int ret;
/* Skip voltage update if the opp table is not available */
if (!icc_scaling_enabled)
return dev_pm_opp_add(cpu_dev, freq_hz, volt);
ret = dev_pm_opp_adjust_voltage(cpu_dev, freq_hz, volt, volt, volt);
if (ret) {
dev_err(cpu_dev, "Voltage update failed freq=%ld\n", freq_khz);
return ret;
}
return dev_pm_opp_enable(cpu_dev, freq_hz);
}
static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
unsigned int index)
......@@ -39,6 +82,9 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
writel_relaxed(index, perf_state_reg);
if (icc_scaling_enabled)
qcom_cpufreq_set_bw(policy, freq);
arch_set_freq_scale(policy->related_cpus, freq,
policy->cpuinfo.max_freq);
return 0;
......@@ -66,13 +112,10 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
unsigned int target_freq)
{
void __iomem *perf_state_reg = policy->driver_data;
int index;
unsigned int index;
unsigned long freq;
index = policy->cached_resolved_idx;
if (index < 0)
return 0;
writel_relaxed(index, perf_state_reg);
freq = policy->freq_table[index].frequency;
......@@ -89,11 +132,34 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
u32 data, src, lval, i, core_count, prev_freq = 0, freq;
u32 volt;
struct cpufreq_frequency_table *table;
struct dev_pm_opp *opp;
unsigned long rate;
int ret;
table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
ret = dev_pm_opp_of_add_table(cpu_dev);
if (!ret) {
/* Disable all opps and cross-validate against LUT later */
icc_scaling_enabled = true;
for (rate = 0; ; rate++) {
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
if (IS_ERR(opp))
break;
dev_pm_opp_put(opp);
dev_pm_opp_disable(cpu_dev, rate);
}
} else if (ret != -ENODEV) {
dev_err(cpu_dev, "Invalid opp table in device tree\n");
return ret;
} else {
policy->fast_switch_possible = true;
icc_scaling_enabled = false;
}
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
data = readl_relaxed(base + REG_FREQ_LUT +
i * LUT_ROW_SIZE);
......@@ -112,7 +178,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
if (freq != prev_freq && core_count != LUT_TURBO_IND) {
table[i].frequency = freq;
dev_pm_opp_add(cpu_dev, freq * 1000, volt);
qcom_cpufreq_update_opp(cpu_dev, freq, volt);
dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i,
freq, core_count);
} else if (core_count == LUT_TURBO_IND) {
......@@ -133,7 +199,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
if (prev->frequency == CPUFREQ_ENTRY_INVALID) {
prev->frequency = prev_freq;
prev->flags = CPUFREQ_BOOST_FREQ;
dev_pm_opp_add(cpu_dev, prev_freq * 1000, volt);
qcom_cpufreq_update_opp(cpu_dev, prev_freq, volt);
}
break;
......@@ -240,8 +306,6 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
policy->fast_switch_possible = true;
return 0;
error:
devm_iounmap(dev, base);
......@@ -254,6 +318,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
void __iomem *base = policy->driver_data - REG_PERF_STATE;
dev_pm_opp_remove_all_dynamic(cpu_dev);
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
kfree(policy->freq_table);
devm_iounmap(&global_pdev->dev, base);
......@@ -282,6 +347,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = {
static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
{
struct device *cpu_dev;
struct clk *clk;
int ret;
......@@ -301,6 +367,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
global_pdev = pdev;
/* Check for optional interconnect paths on CPU0 */
cpu_dev = get_cpu_device(0);
if (!cpu_dev)
return -EPROBE_DEFER;
ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
if (ret)
return ret;
ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver);
if (ret)
dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n");
......
......@@ -40,11 +40,11 @@ enum {
};
/**
* ST CPUFreq Driver Data
* struct sti_cpufreq_ddata - ST CPUFreq Driver Data
*
* @cpu_node CPU's OF node
* @syscfg_eng Engineering Syscon register map
* @regmap Syscon register map
* @cpu: CPU's OF node
* @syscfg_eng: Engineering Syscon register map
* @syscfg: Syscon register map
*/
static struct sti_cpufreq_ddata {
struct device *cpu;
......
......@@ -223,15 +223,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
}
}
tegra_bpmp_put(bpmp);
tegra186_cpufreq_driver.driver_data = data;
err = cpufreq_register_driver(&tegra186_cpufreq_driver);
if (err)
return err;
return 0;
put_bpmp:
tegra_bpmp_put(bpmp);
......
This diff is collapsed.
......@@ -831,6 +831,37 @@ static int _set_required_opps(struct device *dev,
return ret;
}
/**
* dev_pm_opp_set_bw() - sets bandwidth levels corresponding to an opp
* @dev: device for which we do this operation
* @opp: opp based on which the bandwidth levels are to be configured
*
* This configures the bandwidth to the levels specified by the OPP. However
* if the OPP specified is NULL the bandwidth levels are cleared out.
*
* Return: 0 on success or a negative error value.
*/
int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp)
{
struct opp_table *opp_table;
int ret;
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_err(dev, "%s: device opp table doesn't exist\n", __func__);
return PTR_ERR(opp_table);
}
if (opp)
ret = _set_opp_bw(opp_table, opp, dev, false);
else
ret = _set_opp_bw(opp_table, NULL, dev, true);
dev_pm_opp_put_opp_table(opp_table);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw);
/**
* dev_pm_opp_set_rate() - Configure new OPP based on frequency
* @dev: device for which we do this operation
......
......@@ -127,7 +127,7 @@ struct cpufreq_policy {
/* Cached frequency lookup from cpufreq_driver_resolve_freq. */
unsigned int cached_target_freq;
int cached_resolved_idx;
unsigned int cached_resolved_idx;
/* Synchronization for frequency transitions */
bool transition_ongoing; /* Tracks transition status */
......
......@@ -152,6 +152,7 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names
void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
void dev_pm_opp_remove_table(struct device *dev);
......@@ -343,6 +344,11 @@ static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_f
return -ENOTSUPP;
}
static inline int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp)
{
return -EOPNOTSUPP;
}
static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask)
{
return -ENOTSUPP;
......
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