Commit 9ac1fb15 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'cpufreq/arm/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm

Pull ARM cpufreq driver changes for v5.9-rc1 from Viresh Kumar:

"Here are the details:

- Adaptive voltage scaling (AVS) support and minor cleanups for
  brcmstb driver (Florian Fainelli and Markus Mayer).

- A new tegra driver and cleanup for the existing one (Sumit Gupta and
  Jon Hunter).

- Bandwidth level support for Qcom driver along with OPP changes (Sibi
  Sankar).

- Cleanups to sti, cpufreq-dt, ap806, CPPC drivers (Viresh Kumar, Lee
  Jones, Ivan Kokshaysky, Sven Auhagen, and Xin Hao).

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

- Fix dependency issues for imx (Walter Lozano).

- Cleanup around cached_resolved_idx in cpufreq core (Viresh Kumar)."

* 'cpufreq/arm/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  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
  cpufreq: blacklist SC7180 in cpufreq-dt-platdev
  cpufreq: blacklist SDM845 in cpufreq-dt-platdev
parents 4daca379 f259eab3
# 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";
};
};
...
...@@ -37,7 +37,7 @@ config CPU_FREQ_STAT ...@@ -37,7 +37,7 @@ config CPU_FREQ_STAT
choice choice
prompt "Default CPUFreq governor" prompt "Default CPUFreq governor"
default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ 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_SCHEDUTIL if X86_INTEL_PSTATE && SMP
default CPU_FREQ_DEFAULT_GOV_PERFORMANCE default CPU_FREQ_DEFAULT_GOV_PERFORMANCE
help help
......
...@@ -41,6 +41,7 @@ config ARM_ARMADA_37XX_CPUFREQ ...@@ -41,6 +41,7 @@ config ARM_ARMADA_37XX_CPUFREQ
config ARM_ARMADA_8K_CPUFREQ config ARM_ARMADA_8K_CPUFREQ
tristate "Armada 8K CPUFreq driver" tristate "Armada 8K CPUFreq driver"
depends on ARCH_MVEBU && CPUFREQ_DT depends on ARCH_MVEBU && CPUFREQ_DT
select ARMADA_AP_CPU_CLK
help help
This enables the CPUFreq driver support for Marvell This enables the CPUFreq driver support for Marvell
Armada8k SOCs. Armada8k SOCs.
...@@ -93,6 +94,7 @@ config ARM_IMX6Q_CPUFREQ ...@@ -93,6 +94,7 @@ config ARM_IMX6Q_CPUFREQ
tristate "Freescale i.MX6 cpufreq support" tristate "Freescale i.MX6 cpufreq support"
depends on ARCH_MXC depends on ARCH_MXC
depends on REGULATOR_ANATOP depends on REGULATOR_ANATOP
select NVMEM_IMX_OCOTP
select PM_OPP select PM_OPP
help help
This adds cpufreq driver support for Freescale i.MX6 series SoCs. This adds cpufreq driver support for Freescale i.MX6 series SoCs.
...@@ -314,6 +316,13 @@ config ARM_TEGRA186_CPUFREQ ...@@ -314,6 +316,13 @@ config ARM_TEGRA186_CPUFREQ
help help
This adds the CPUFreq driver support for Tegra186 SOCs. 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 config ARM_TI_CPUFREQ
bool "Texas Instruments CPUFreq support" bool "Texas Instruments CPUFreq support"
depends on ARCH_OMAP2PLUS depends on ARCH_OMAP2PLUS
......
...@@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o ...@@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-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_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
......
...@@ -456,6 +456,7 @@ static int __init armada37xx_cpufreq_driver_init(void) ...@@ -456,6 +456,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
/* Now that everything is setup, enable the DVFS at hardware level */ /* Now that everything is setup, enable the DVFS at hardware level */
armada37xx_cpufreq_enable_dvfs(nb_pm_base); armada37xx_cpufreq_enable_dvfs(nb_pm_base);
memset(&pdata, 0, sizeof(pdata));
pdata.suspend = armada37xx_cpufreq_suspend; pdata.suspend = armada37xx_cpufreq_suspend;
pdata.resume = armada37xx_cpufreq_resume; pdata.resume = armada37xx_cpufreq_resume;
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
*/ */
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -178,6 +179,7 @@ struct private_data { ...@@ -178,6 +179,7 @@ struct private_data {
struct completion done; struct completion done;
struct semaphore sem; struct semaphore sem;
struct pmap pmap; struct pmap pmap;
int host_irq;
}; };
static void __iomem *__map_region(const char *name) static void __iomem *__map_region(const char *name)
...@@ -195,11 +197,36 @@ static void __iomem *__map_region(const char *name) ...@@ -195,11 +197,36 @@ static void __iomem *__map_region(const char *name)
return ptr; 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[]) u32 args[])
{ {
unsigned long time_left = msecs_to_jiffies(AVS_TIMEOUT);
void __iomem *base = priv->base; void __iomem *base = priv->base;
unsigned long time_left;
unsigned int i; unsigned int i;
int ret; int ret;
u32 val; u32 val;
...@@ -225,11 +252,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, ...@@ -225,11 +252,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send,
/* Clear status before we begin. */ /* Clear status before we begin. */
writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);
/* We need to send arguments for this command. */ /* Provide input parameters */
if (args && is_send) { for (i = 0; i < num_in; i++)
for (i = 0; i < AVS_MAX_CMD_ARGS; i++) writel(args[i], base + AVS_MBOX_PARAM(i));
writel(args[i], base + AVS_MBOX_PARAM(i));
}
/* Protect from spurious interrupts. */ /* Protect from spurious interrupts. */
reinit_completion(&priv->done); reinit_completion(&priv->done);
...@@ -239,7 +264,7 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, ...@@ -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); writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0);
/* Wait for AVS co-processor to finish processing the command. */ /* 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 * 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, ...@@ -256,11 +281,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send,
goto out; goto out;
} }
/* This command returned arguments, so we read them back. */ /* Process returned values */
if (args && !is_send) { for (i = 0; i < num_out; i++)
for (i = 0; i < AVS_MAX_CMD_ARGS; i++) args[i] = readl(base + AVS_MBOX_PARAM(i));
args[i] = readl(base + AVS_MBOX_PARAM(i));
}
/* Clear status to tell AVS co-processor we are done. */ /* Clear status to tell AVS co-processor we are done. */
writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); 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) ...@@ -338,7 +361,7 @@ static int brcm_avs_get_pmap(struct private_data *priv, struct pmap *pmap)
u32 args[AVS_MAX_CMD_ARGS]; u32 args[AVS_MAX_CMD_ARGS];
int ret; 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) if (ret || !pmap)
return ret; return ret;
...@@ -359,7 +382,7 @@ static int brcm_avs_set_pmap(struct private_data *priv, struct pmap *pmap) ...@@ -359,7 +382,7 @@ static int brcm_avs_set_pmap(struct private_data *priv, struct pmap *pmap)
args[2] = pmap->p2; args[2] = pmap->p2;
args[3] = pmap->state; 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) 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) ...@@ -367,7 +390,7 @@ static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate)
u32 args[AVS_MAX_CMD_ARGS]; u32 args[AVS_MAX_CMD_ARGS];
int ret; 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) if (ret)
return ret; return ret;
*pstate = args[0]; *pstate = args[0];
...@@ -381,7 +404,8 @@ static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate) ...@@ -381,7 +404,8 @@ static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate)
args[0] = 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) static u32 brcm_avs_get_voltage(void __iomem *base)
...@@ -482,7 +506,14 @@ static int brcm_avs_suspend(struct cpufreq_policy *policy) ...@@ -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. * AVS co-processor, not necessarily the P-state we are running at now.
* So, we get the current P-state explicitly. * 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) static int brcm_avs_resume(struct cpufreq_policy *policy)
...@@ -490,6 +521,9 @@ 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; struct private_data *priv = policy->driver_data;
int ret; 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); ret = brcm_avs_set_pmap(priv, &priv->pmap);
if (ret == -EEXIST) { if (ret == -EEXIST) {
struct platform_device *pdev = cpufreq_get_driver_data(); struct platform_device *pdev = cpufreq_get_driver_data();
...@@ -511,7 +545,7 @@ static int brcm_avs_prepare_init(struct platform_device *pdev) ...@@ -511,7 +545,7 @@ static int brcm_avs_prepare_init(struct platform_device *pdev)
{ {
struct private_data *priv; struct private_data *priv;
struct device *dev; struct device *dev;
int host_irq, ret; int ret;
dev = &pdev->dev; dev = &pdev->dev;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
...@@ -538,19 +572,14 @@ static int brcm_avs_prepare_init(struct platform_device *pdev) ...@@ -538,19 +572,14 @@ static int brcm_avs_prepare_init(struct platform_device *pdev)
goto unmap_base; goto unmap_base;
} }
host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR); priv->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;
}
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); BRCM_AVS_HOST_INTR, priv);
if (ret) { if (ret && priv->host_irq >= 0) {
dev_err(dev, "IRQ request failed: %s (%d) -- %d\n", 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; goto unmap_intr_base;
} }
...@@ -593,7 +622,7 @@ static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy) ...@@ -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. */ /* All cores share the same clock and thus the same policy. */
cpumask_setall(policy->cpus); 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) { if (!ret) {
unsigned int pstate; unsigned int pstate;
......
...@@ -45,8 +45,6 @@ struct cppc_workaround_oem_info { ...@@ -45,8 +45,6 @@ struct cppc_workaround_oem_info {
u32 oem_revision; u32 oem_revision;
}; };
static bool apply_hisi_workaround;
static struct cppc_workaround_oem_info wa_info[] = { static struct cppc_workaround_oem_info wa_info[] = {
{ {
.oem_id = "HISI ", .oem_id = "HISI ",
...@@ -59,50 +57,6 @@ static struct cppc_workaround_oem_info wa_info[] = { ...@@ -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 */ /* Callback function used to retrieve the max frequency from DMI */
static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) 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, ...@@ -161,7 +115,7 @@ static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
if (!max_khz) if (!max_khz)
max_khz = cppc_get_dmi_max_khz(); max_khz = cppc_get_dmi_max_khz();
mul = max_khz; mul = max_khz;
div = cpu->perf_caps.highest_perf; div = caps->highest_perf;
} }
return (u64)perf * mul / div; return (u64)perf * mul / div;
} }
...@@ -184,7 +138,7 @@ static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu, ...@@ -184,7 +138,7 @@ static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu,
} else { } else {
if (!max_khz) if (!max_khz)
max_khz = cppc_get_dmi_max_khz(); max_khz = cppc_get_dmi_max_khz();
mul = cpu->perf_caps.highest_perf; mul = caps->highest_perf;
div = max_khz; div = max_khz;
} }
...@@ -402,9 +356,6 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum) ...@@ -402,9 +356,6 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum)
struct cppc_cpudata *cpu = all_cpu_data[cpunum]; struct cppc_cpudata *cpu = all_cpu_data[cpunum];
int ret; int ret;
if (apply_hisi_workaround)
return hisi_cppc_cpufreq_get_rate(cpunum);
ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0); ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0);
if (ret) if (ret)
return ret; return ret;
...@@ -455,6 +406,48 @@ static struct cpufreq_driver cppc_cpufreq_driver = { ...@@ -455,6 +406,48 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
.name = "cppc_cpufreq", .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) static int __init cppc_cpufreq_init(void)
{ {
int i, ret = 0; int i, ret = 0;
......
...@@ -132,6 +132,8 @@ static const struct of_device_id blacklist[] __initconst = { ...@@ -132,6 +132,8 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "qcom,apq8096", }, { .compatible = "qcom,apq8096", },
{ .compatible = "qcom,msm8996", }, { .compatible = "qcom,msm8996", },
{ .compatible = "qcom,qcs404", }, { .compatible = "qcom,qcs404", },
{ .compatible = "qcom,sc7180", },
{ .compatible = "qcom,sdm845", },
{ .compatible = "st,stih407", }, { .compatible = "st,stih407", },
{ .compatible = "st,stih410", }, { .compatible = "st,stih410", },
......
...@@ -541,7 +541,7 @@ unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy, ...@@ -541,7 +541,7 @@ unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy,
policy->cached_target_freq = target_freq; policy->cached_target_freq = target_freq;
if (cpufreq_driver->target_index) { if (cpufreq_driver->target_index) {
int idx; unsigned int idx;
idx = cpufreq_frequency_table_target(policy, target_freq, idx = cpufreq_frequency_table_target(policy, target_freq,
CPUFREQ_RELATION_L); CPUFREQ_RELATION_L);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interconnect.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -30,6 +31,48 @@ ...@@ -30,6 +31,48 @@
static unsigned long cpu_hw_rate, xo_rate; static unsigned long cpu_hw_rate, xo_rate;
static struct platform_device *global_pdev; 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, static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
unsigned int index) unsigned int index)
...@@ -39,6 +82,9 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, ...@@ -39,6 +82,9 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy,
writel_relaxed(index, perf_state_reg); writel_relaxed(index, perf_state_reg);
if (icc_scaling_enabled)
qcom_cpufreq_set_bw(policy, freq);
arch_set_freq_scale(policy->related_cpus, freq, arch_set_freq_scale(policy->related_cpus, freq,
policy->cpuinfo.max_freq); policy->cpuinfo.max_freq);
return 0; return 0;
...@@ -66,13 +112,10 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, ...@@ -66,13 +112,10 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
unsigned int target_freq) unsigned int target_freq)
{ {
void __iomem *perf_state_reg = policy->driver_data; void __iomem *perf_state_reg = policy->driver_data;
int index; unsigned int index;
unsigned long freq; unsigned long freq;
index = policy->cached_resolved_idx; index = policy->cached_resolved_idx;
if (index < 0)
return 0;
writel_relaxed(index, perf_state_reg); writel_relaxed(index, perf_state_reg);
freq = policy->freq_table[index].frequency; freq = policy->freq_table[index].frequency;
...@@ -89,11 +132,34 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, ...@@ -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 data, src, lval, i, core_count, prev_freq = 0, freq;
u32 volt; u32 volt;
struct cpufreq_frequency_table *table; 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); table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL);
if (!table) if (!table)
return -ENOMEM; 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++) { for (i = 0; i < LUT_MAX_ENTRIES; i++) {
data = readl_relaxed(base + REG_FREQ_LUT + data = readl_relaxed(base + REG_FREQ_LUT +
i * LUT_ROW_SIZE); i * LUT_ROW_SIZE);
...@@ -112,7 +178,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, ...@@ -112,7 +178,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
if (freq != prev_freq && core_count != LUT_TURBO_IND) { if (freq != prev_freq && core_count != LUT_TURBO_IND) {
table[i].frequency = freq; 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, dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i,
freq, core_count); freq, core_count);
} else if (core_count == LUT_TURBO_IND) { } else if (core_count == LUT_TURBO_IND) {
...@@ -133,7 +199,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, ...@@ -133,7 +199,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
if (prev->frequency == CPUFREQ_ENTRY_INVALID) { if (prev->frequency == CPUFREQ_ENTRY_INVALID) {
prev->frequency = prev_freq; prev->frequency = prev_freq;
prev->flags = CPUFREQ_BOOST_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; break;
...@@ -240,8 +306,6 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) ...@@ -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); dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
policy->fast_switch_possible = true;
return 0; return 0;
error: error:
devm_iounmap(dev, base); devm_iounmap(dev, base);
...@@ -254,6 +318,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) ...@@ -254,6 +318,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
void __iomem *base = policy->driver_data - REG_PERF_STATE; void __iomem *base = policy->driver_data - REG_PERF_STATE;
dev_pm_opp_remove_all_dynamic(cpu_dev); dev_pm_opp_remove_all_dynamic(cpu_dev);
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
kfree(policy->freq_table); kfree(policy->freq_table);
devm_iounmap(&global_pdev->dev, base); devm_iounmap(&global_pdev->dev, base);
...@@ -282,6 +347,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = { ...@@ -282,6 +347,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = {
static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
{ {
struct device *cpu_dev;
struct clk *clk; struct clk *clk;
int ret; int ret;
...@@ -301,6 +367,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) ...@@ -301,6 +367,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
global_pdev = 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); ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver);
if (ret) if (ret)
dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n"); dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n");
......
...@@ -40,11 +40,11 @@ enum { ...@@ -40,11 +40,11 @@ enum {
}; };
/** /**
* ST CPUFreq Driver Data * struct sti_cpufreq_ddata - ST CPUFreq Driver Data
* *
* @cpu_node CPU's OF node * @cpu: CPU's OF node
* @syscfg_eng Engineering Syscon register map * @syscfg_eng: Engineering Syscon register map
* @regmap Syscon register map * @syscfg: Syscon register map
*/ */
static struct sti_cpufreq_ddata { static struct sti_cpufreq_ddata {
struct device *cpu; struct device *cpu;
......
...@@ -223,15 +223,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev) ...@@ -223,15 +223,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
} }
} }
tegra_bpmp_put(bpmp);
tegra186_cpufreq_driver.driver_data = data; tegra186_cpufreq_driver.driver_data = data;
err = cpufreq_register_driver(&tegra186_cpufreq_driver); err = cpufreq_register_driver(&tegra186_cpufreq_driver);
if (err)
return err;
return 0;
put_bpmp: put_bpmp:
tegra_bpmp_put(bpmp); tegra_bpmp_put(bpmp);
......
This diff is collapsed.
...@@ -831,6 +831,37 @@ static int _set_required_opps(struct device *dev, ...@@ -831,6 +831,37 @@ static int _set_required_opps(struct device *dev,
return ret; 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_pm_opp_set_rate() - Configure new OPP based on frequency
* @dev: device for which we do this operation * @dev: device for which we do this operation
......
...@@ -127,7 +127,7 @@ struct cpufreq_policy { ...@@ -127,7 +127,7 @@ struct cpufreq_policy {
/* Cached frequency lookup from cpufreq_driver_resolve_freq. */ /* Cached frequency lookup from cpufreq_driver_resolve_freq. */
unsigned int cached_target_freq; unsigned int cached_target_freq;
int cached_resolved_idx; unsigned int cached_resolved_idx;
/* Synchronization for frequency transitions */ /* Synchronization for frequency transitions */
bool transition_ongoing; /* Tracks transition status */ bool transition_ongoing; /* Tracks transition status */
......
...@@ -152,6 +152,7 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names ...@@ -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); 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_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_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_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); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
void dev_pm_opp_remove_table(struct device *dev); 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 ...@@ -343,6 +344,11 @@ static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_f
return -ENOTSUPP; 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) static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask)
{ {
return -ENOTSUPP; return -ENOTSUPP;
......
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