summaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/Kconfig.arm14
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/armada-37xx-cpufreq.c163
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c52
-rw-r--r--drivers/cpufreq/cpufreq.c8
-rw-r--r--drivers/cpufreq/cpufreq_governor.c12
-rw-r--r--drivers/cpufreq/exynos5440-cpufreq.c452
-rw-r--r--drivers/cpufreq/imx6q-cpufreq.c21
-rw-r--r--drivers/cpufreq/intel_pstate.c27
-rw-r--r--drivers/cpufreq/pcc-cpufreq.c9
-rw-r--r--drivers/cpufreq/powernv-cpufreq.c20
-rw-r--r--drivers/cpufreq/qcom-cpufreq-kryo.c5
12 files changed, 295 insertions, 489 deletions
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 52f5f1a2040c..0cd8eb76ad59 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -71,20 +71,6 @@ config ARM_BRCMSTB_AVS_CPUFREQ
Say Y, if you have a Broadcom SoC with AVS support for DFS or DVFS.
-config ARM_EXYNOS5440_CPUFREQ
- tristate "SAMSUNG EXYNOS5440"
- depends on SOC_EXYNOS5440
- depends on HAVE_CLK && OF
- select PM_OPP
- default y
- help
- This adds the CPUFreq driver for Samsung EXYNOS5440
- SoC. The nature of exynos5440 clock controller is
- different than previous exynos controllers so not using
- the common exynos framework.
-
- If in doubt, say N.
-
config ARM_HIGHBANK_CPUFREQ
tristate "Calxeda Highbank-based"
depends on ARCH_HIGHBANK && CPUFREQ_DT && REGULATOR
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index fb4a2ecac43b..c1ffeabe4ecf 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -56,7 +56,6 @@ obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
-obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o
obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
index 739da90ff3f6..75491fc841a6 100644
--- a/drivers/cpufreq/armada-37xx-cpufreq.c
+++ b/drivers/cpufreq/armada-37xx-cpufreq.c
@@ -51,6 +51,16 @@
#define ARMADA_37XX_DVFS_LOAD_2 2
#define ARMADA_37XX_DVFS_LOAD_3 3
+/* AVS register set */
+#define ARMADA_37XX_AVS_CTL0 0x0
+#define ARMADA_37XX_AVS_ENABLE BIT(30)
+#define ARMADA_37XX_AVS_HIGH_VDD_LIMIT 16
+#define ARMADA_37XX_AVS_LOW_VDD_LIMIT 22
+#define ARMADA_37XX_AVS_VDD_MASK 0x3F
+#define ARMADA_37XX_AVS_CTL2 0x8
+#define ARMADA_37XX_AVS_LOW_VDD_EN BIT(6)
+#define ARMADA_37XX_AVS_VSET(x) (0x1C + 4 * (x))
+
/*
* On Armada 37xx the Power management manages 4 level of CPU load,
* each level can be associated with a CPU clock source, a CPU
@@ -58,6 +68,17 @@
*/
#define LOAD_LEVEL_NR 4
+#define MIN_VOLT_MV 1000
+
+/* AVS value for the corresponding voltage (in mV) */
+static int avs_map[] = {
+ 747, 758, 770, 782, 793, 805, 817, 828, 840, 852, 863, 875, 887, 898,
+ 910, 922, 933, 945, 957, 968, 980, 992, 1003, 1015, 1027, 1038, 1050,
+ 1062, 1073, 1085, 1097, 1108, 1120, 1132, 1143, 1155, 1167, 1178, 1190,
+ 1202, 1213, 1225, 1237, 1248, 1260, 1272, 1283, 1295, 1307, 1318, 1330,
+ 1342
+};
+
struct armada37xx_cpufreq_state {
struct regmap *regmap;
u32 nb_l0l1;
@@ -71,6 +92,7 @@ static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state;
struct armada_37xx_dvfs {
u32 cpu_freq_max;
u8 divider[LOAD_LEVEL_NR];
+ u32 avs[LOAD_LEVEL_NR];
};
static struct armada_37xx_dvfs armada_37xx_dvfs[] = {
@@ -148,6 +170,128 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
clk_set_parent(clk, parent);
}
+/*
+ * Find out the armada 37x supported AVS value whose voltage value is
+ * the round-up closest to the target voltage value.
+ */
+static u32 armada_37xx_avs_val_match(int target_vm)
+{
+ u32 avs;
+
+ /* Find out the round-up closest supported voltage value */
+ for (avs = 0; avs < ARRAY_SIZE(avs_map); avs++)
+ if (avs_map[avs] >= target_vm)
+ break;
+
+ /*
+ * If all supported voltages are smaller than target one,
+ * choose the largest supported voltage
+ */
+ if (avs == ARRAY_SIZE(avs_map))
+ avs = ARRAY_SIZE(avs_map) - 1;
+
+ return avs;
+}
+
+/*
+ * For Armada 37xx soc, L0(VSET0) VDD AVS value is set to SVC revision
+ * value or a default value when SVC is not supported.
+ * - L0 can be read out from the register of AVS_CTRL_0 and L0 voltage
+ * can be got from the mapping table of avs_map.
+ * - L1 voltage should be about 100mv smaller than L0 voltage
+ * - L2 & L3 voltage should be about 150mv smaller than L0 voltage.
+ * This function calculates L1 & L2 & L3 AVS values dynamically based
+ * on L0 voltage and fill all AVS values to the AVS value table.
+ */
+static void __init armada37xx_cpufreq_avs_configure(struct regmap *base,
+ struct armada_37xx_dvfs *dvfs)
+{
+ unsigned int target_vm;
+ int load_level = 0;
+ u32 l0_vdd_min;
+
+ if (base == NULL)
+ return;
+
+ /* Get L0 VDD min value */
+ regmap_read(base, ARMADA_37XX_AVS_CTL0, &l0_vdd_min);
+ l0_vdd_min = (l0_vdd_min >> ARMADA_37XX_AVS_LOW_VDD_LIMIT) &
+ ARMADA_37XX_AVS_VDD_MASK;
+ if (l0_vdd_min >= ARRAY_SIZE(avs_map)) {
+ pr_err("L0 VDD MIN %d is not correct.\n", l0_vdd_min);
+ return;
+ }
+ dvfs->avs[0] = l0_vdd_min;
+
+ if (avs_map[l0_vdd_min] <= MIN_VOLT_MV) {
+ /*
+ * If L0 voltage is smaller than 1000mv, then all VDD sets
+ * use L0 voltage;
+ */
+ u32 avs_min = armada_37xx_avs_val_match(MIN_VOLT_MV);
+
+ for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++)
+ dvfs->avs[load_level] = avs_min;
+
+ return;
+ }
+
+ /*
+ * L1 voltage is equal to L0 voltage - 100mv and it must be
+ * larger than 1000mv
+ */
+
+ target_vm = avs_map[l0_vdd_min] - 100;
+ target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV;
+ dvfs->avs[1] = armada_37xx_avs_val_match(target_vm);
+
+ /*
+ * L2 & L3 voltage is equal to L0 voltage - 150mv and it must
+ * be larger than 1000mv
+ */
+ target_vm = avs_map[l0_vdd_min] - 150;
+ target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV;
+ dvfs->avs[2] = dvfs->avs[3] = armada_37xx_avs_val_match(target_vm);
+}
+
+static void __init armada37xx_cpufreq_avs_setup(struct regmap *base,
+ struct armada_37xx_dvfs *dvfs)
+{
+ unsigned int avs_val = 0, freq;
+ int load_level = 0;
+
+ if (base == NULL)
+ return;
+
+ /* Disable AVS before the configuration */
+ regmap_update_bits(base, ARMADA_37XX_AVS_CTL0,
+ ARMADA_37XX_AVS_ENABLE, 0);
+
+
+ /* Enable low voltage mode */
+ regmap_update_bits(base, ARMADA_37XX_AVS_CTL2,
+ ARMADA_37XX_AVS_LOW_VDD_EN,
+ ARMADA_37XX_AVS_LOW_VDD_EN);
+
+
+ for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++) {
+ freq = dvfs->cpu_freq_max / dvfs->divider[load_level];
+
+ avs_val = dvfs->avs[load_level];
+ regmap_update_bits(base, ARMADA_37XX_AVS_VSET(load_level-1),
+ ARMADA_37XX_AVS_VDD_MASK << ARMADA_37XX_AVS_HIGH_VDD_LIMIT |
+ ARMADA_37XX_AVS_VDD_MASK << ARMADA_37XX_AVS_LOW_VDD_LIMIT,
+ avs_val << ARMADA_37XX_AVS_HIGH_VDD_LIMIT |
+ avs_val << ARMADA_37XX_AVS_LOW_VDD_LIMIT);
+ }
+
+ /* Enable AVS after the configuration */
+ regmap_update_bits(base, ARMADA_37XX_AVS_CTL0,
+ ARMADA_37XX_AVS_ENABLE,
+ ARMADA_37XX_AVS_ENABLE);
+
+}
+
static void armada37xx_cpufreq_disable_dvfs(struct regmap *base)
{
unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
@@ -216,7 +360,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
struct platform_device *pdev;
unsigned long freq;
unsigned int cur_frequency;
- struct regmap *nb_pm_base;
+ struct regmap *nb_pm_base, *avs_base;
struct device *cpu_dev;
int load_lvl, ret;
struct clk *clk;
@@ -227,6 +371,14 @@ static int __init armada37xx_cpufreq_driver_init(void)
if (IS_ERR(nb_pm_base))
return -ENODEV;
+ avs_base =
+ syscon_regmap_lookup_by_compatible("marvell,armada-3700-avs");
+
+ /* if AVS is not present don't use it but still try to setup dvfs */
+ if (IS_ERR(avs_base)) {
+ pr_info("Syscon failed for Adapting Voltage Scaling: skip it\n");
+ avs_base = NULL;
+ }
/* Before doing any configuration on the DVFS first, disable it */
armada37xx_cpufreq_disable_dvfs(nb_pm_base);
@@ -270,16 +422,21 @@ static int __init armada37xx_cpufreq_driver_init(void)
armada37xx_cpufreq_state->regmap = nb_pm_base;
+ armada37xx_cpufreq_avs_configure(avs_base, dvfs);
+ armada37xx_cpufreq_avs_setup(avs_base, dvfs);
+
armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
clk_put(clk);
for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR;
load_lvl++) {
+ unsigned long u_volt = avs_map[dvfs->avs[load_lvl]] * 1000;
freq = cur_frequency / dvfs->divider[load_lvl];
-
- ret = dev_pm_opp_add(cpu_dev, freq, 0);
+ ret = dev_pm_opp_add(cpu_dev, freq, u_volt);
if (ret)
goto remove_opp;
+
+
}
/* Now that everything is setup, enable the DVFS at hardware level */
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index a9d3eec32795..30f302149730 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -296,10 +296,62 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
return ret;
}
+static inline u64 get_delta(u64 t1, u64 t0)
+{
+ if (t1 > t0 || t0 > ~(u32)0)
+ return t1 - t0;
+
+ return (u32)t1 - (u32)t0;
+}
+
+static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu,
+ struct cppc_perf_fb_ctrs fb_ctrs_t0,
+ struct cppc_perf_fb_ctrs fb_ctrs_t1)
+{
+ u64 delta_reference, delta_delivered;
+ u64 reference_perf, delivered_perf;
+
+ reference_perf = fb_ctrs_t0.reference_perf;
+
+ delta_reference = get_delta(fb_ctrs_t1.reference,
+ fb_ctrs_t0.reference);
+ delta_delivered = get_delta(fb_ctrs_t1.delivered,
+ fb_ctrs_t0.delivered);
+
+ /* Check to avoid divide-by zero */
+ if (delta_reference || delta_delivered)
+ delivered_perf = (reference_perf * delta_delivered) /
+ delta_reference;
+ else
+ delivered_perf = cpu->perf_ctrls.desired_perf;
+
+ return cppc_cpufreq_perf_to_khz(cpu, delivered_perf);
+}
+
+static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum)
+{
+ struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0};
+ struct cppc_cpudata *cpu = all_cpu_data[cpunum];
+ int ret;
+
+ ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0);
+ if (ret)
+ return ret;
+
+ udelay(2); /* 2usec delay between sampling */
+
+ ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t1);
+ if (ret)
+ return ret;
+
+ return cppc_get_rate_from_fbctrs(cpu, fb_ctrs_t0, fb_ctrs_t1);
+}
+
static struct cpufreq_driver cppc_cpufreq_driver = {
.flags = CPUFREQ_CONST_LOOPS,
.verify = cppc_verify_policy,
.target = cppc_cpufreq_set_target,
+ .get = cppc_cpufreq_get_rate,
.init = cppc_cpufreq_cpu_init,
.stop_cpu = cppc_cpufreq_stop_cpu,
.name = "cppc_cpufreq",
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index b0dfd3222013..f53fb41efb7b 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -923,7 +923,12 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
struct freq_attr *fattr = to_attr(attr);
ssize_t ret = -EINVAL;
- cpus_read_lock();
+ /*
+ * cpus_read_trylock() is used here to work around a circular lock
+ * dependency problem with respect to the cpufreq_register_driver().
+ */
+ if (!cpus_read_trylock())
+ return -EBUSY;
if (cpu_online(policy->cpu)) {
down_write(&policy->rwsem);
@@ -2236,6 +2241,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
policy->min = new_policy->min;
policy->max = new_policy->max;
+ trace_cpu_frequency_limits(policy);
policy->cached_target_freq = UINT_MAX;
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 1d50e97d49f1..6d53f7d9fc7a 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -555,12 +555,20 @@ EXPORT_SYMBOL_GPL(cpufreq_dbs_governor_stop);
void cpufreq_dbs_governor_limits(struct cpufreq_policy *policy)
{
- struct policy_dbs_info *policy_dbs = policy->governor_data;
+ struct policy_dbs_info *policy_dbs;
+
+ /* Protect gov->gdbs_data against cpufreq_dbs_governor_exit() */
+ mutex_lock(&gov_dbs_data_mutex);
+ policy_dbs = policy->governor_data;
+ if (!policy_dbs)
+ goto out;
mutex_lock(&policy_dbs->update_mutex);
cpufreq_policy_apply_limits(policy);
gov_update_sample_delay(policy_dbs, 0);
-
mutex_unlock(&policy_dbs->update_mutex);
+
+out:
+ mutex_unlock(&gov_dbs_data_mutex);
}
EXPORT_SYMBOL_GPL(cpufreq_dbs_governor_limits);
diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c
deleted file mode 100644
index 932caa386ece..000000000000
--- a/drivers/cpufreq/exynos5440-cpufreq.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Amit Daniel Kachhap <amit.daniel@samsung.com>
- *
- * EXYNOS5440 - CPU frequency scaling support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/clk.h>
-#include <linux/cpu.h>
-#include <linux/cpufreq.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/pm_opp.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-/* Register definitions */
-#define XMU_DVFS_CTRL 0x0060
-#define XMU_PMU_P0_7 0x0064
-#define XMU_C0_3_PSTATE 0x0090
-#define XMU_P_LIMIT 0x00a0
-#define XMU_P_STATUS 0x00a4
-#define XMU_PMUEVTEN 0x00d0
-#define XMU_PMUIRQEN 0x00d4
-#define XMU_PMUIRQ 0x00d8
-
-/* PMU mask and shift definations */
-#define P_VALUE_MASK 0x7
-
-#define XMU_DVFS_CTRL_EN_SHIFT 0
-
-#define P0_7_CPUCLKDEV_SHIFT 21
-#define P0_7_CPUCLKDEV_MASK 0x7
-#define P0_7_ATBCLKDEV_SHIFT 18
-#define P0_7_ATBCLKDEV_MASK 0x7
-#define P0_7_CSCLKDEV_SHIFT 15
-#define P0_7_CSCLKDEV_MASK 0x7
-#define P0_7_CPUEMA_SHIFT 28
-#define P0_7_CPUEMA_MASK 0xf
-#define P0_7_L2EMA_SHIFT 24
-#define P0_7_L2EMA_MASK 0xf
-#define P0_7_VDD_SHIFT 8
-#define P0_7_VDD_MASK 0x7f
-#define P0_7_FREQ_SHIFT 0
-#define P0_7_FREQ_MASK 0xff
-
-#define C0_3_PSTATE_VALID_SHIFT 8
-#define C0_3_PSTATE_CURR_SHIFT 4
-#define C0_3_PSTATE_NEW_SHIFT 0
-
-#define PSTATE_CHANGED_EVTEN_SHIFT 0
-
-#define PSTATE_CHANGED_IRQEN_SHIFT 0
-
-#define PSTATE_CHANGED_SHIFT 0
-
-/* some constant values for clock divider calculation */
-#define CPU_DIV_FREQ_MAX 500
-#define CPU_DBG_FREQ_MAX 375
-#define CPU_ATB_FREQ_MAX 500
-
-#define PMIC_LOW_VOLT 0x30
-#define PMIC_HIGH_VOLT 0x28
-
-#define CPUEMA_HIGH 0x2
-#define CPUEMA_MID 0x4
-#define CPUEMA_LOW 0x7
-
-#define L2EMA_HIGH 0x1
-#define L2EMA_MID 0x3
-#define L2EMA_LOW 0x4
-
-#define DIV_TAB_MAX 2
-/* frequency unit is 20MHZ */
-#define FREQ_UNIT 20
-#define MAX_VOLTAGE 1550000 /* In microvolt */
-#define VOLTAGE_STEP 12500 /* In microvolt */
-
-#define CPUFREQ_NAME "exynos5440_dvfs"
-#define DEF_TRANS_LATENCY 100000
-
-enum cpufreq_level_index {
- L0, L1, L2, L3, L4,
- L5, L6, L7, L8, L9,
-};
-#define CPUFREQ_LEVEL_END (L7 + 1)
-
-struct exynos_dvfs_data {
- void __iomem *base;
- struct resource *mem;
- int irq;
- struct clk *cpu_clk;
- unsigned int latency;
- struct cpufreq_frequency_table *freq_table;
- unsigned int freq_count;
- struct device *dev;
- bool dvfs_enabled;
- struct work_struct irq_work;
-};
-
-static struct exynos_dvfs_data *dvfs_info;
-static DEFINE_MUTEX(cpufreq_lock);
-static struct cpufreq_freqs freqs;
-
-static int init_div_table(void)
-{
- struct cpufreq_frequency_table *pos, *freq_tbl = dvfs_info->freq_table;
- unsigned int tmp, clk_div, ema_div, freq, volt_id, idx;
- struct dev_pm_opp *opp;
-
- cpufreq_for_each_entry_idx(pos, freq_tbl, idx) {
- opp = dev_pm_opp_find_freq_exact(dvfs_info->dev,
- pos->frequency * 1000, true);
- if (IS_ERR(opp)) {
- dev_err(dvfs_info->dev,
- "failed to find valid OPP for %u KHZ\n",
- pos->frequency);
- return PTR_ERR(opp);
- }
-
- freq = pos->frequency / 1000; /* In MHZ */
- clk_div = ((freq / CPU_DIV_FREQ_MAX) & P0_7_CPUCLKDEV_MASK)
- << P0_7_CPUCLKDEV_SHIFT;
- clk_div |= ((freq / CPU_ATB_FREQ_MAX) & P0_7_ATBCLKDEV_MASK)
- << P0_7_ATBCLKDEV_SHIFT;
- clk_div |= ((freq / CPU_DBG_FREQ_MAX) & P0_7_CSCLKDEV_MASK)
- << P0_7_CSCLKDEV_SHIFT;
-
- /* Calculate EMA */
- volt_id = dev_pm_opp_get_voltage(opp);
-
- volt_id = (MAX_VOLTAGE - volt_id) / VOLTAGE_STEP;
- if (volt_id < PMIC_HIGH_VOLT) {
- ema_div = (CPUEMA_HIGH << P0_7_CPUEMA_SHIFT) |
- (L2EMA_HIGH << P0_7_L2EMA_SHIFT);
- } else if (volt_id > PMIC_LOW_VOLT) {
- ema_div = (CPUEMA_LOW << P0_7_CPUEMA_SHIFT) |
- (L2EMA_LOW << P0_7_L2EMA_SHIFT);
- } else {
- ema_div = (CPUEMA_MID << P0_7_CPUEMA_SHIFT) |
- (L2EMA_MID << P0_7_L2EMA_SHIFT);
- }
-
- tmp = (clk_div | ema_div | (volt_id << P0_7_VDD_SHIFT)
- | ((freq / FREQ_UNIT) << P0_7_FREQ_SHIFT));
-
- __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 * idx);
- dev_pm_opp_put(opp);
- }
-
- return 0;
-}
-
-static void exynos_enable_dvfs(unsigned int cur_frequency)
-{
- unsigned int tmp, cpu;
- struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
- struct cpufreq_frequency_table *pos;
- /* Disable DVFS */
- __raw_writel(0, dvfs_info->base + XMU_DVFS_CTRL);
-
- /* Enable PSTATE Change Event */
- tmp = __raw_readl(dvfs_info->base + XMU_PMUEVTEN);
- tmp |= (1 << PSTATE_CHANGED_EVTEN_SHIFT);
- __raw_writel(tmp, dvfs_info->base + XMU_PMUEVTEN);
-
- /* Enable PSTATE Change IRQ */
- tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQEN);
- tmp |= (1 << PSTATE_CHANGED_IRQEN_SHIFT);
- __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQEN);
-
- /* Set initial performance index */
- cpufreq_for_each_entry(pos, freq_table)
- if (pos->frequency == cur_frequency)
- break;
-
- if (pos->frequency == CPUFREQ_TABLE_END) {
- dev_crit(dvfs_info->dev, "Boot up frequency not supported\n");
- /* Assign the highest frequency */
- pos = freq_table;
- cur_frequency = pos->frequency;
- }
-
- dev_info(dvfs_info->dev, "Setting dvfs initial frequency = %uKHZ",
- cur_frequency);
-
- for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) {
- tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
- tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
- tmp |= ((pos - freq_table) << C0_3_PSTATE_NEW_SHIFT);
- __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
- }
-
- /* Enable DVFS */
- __raw_writel(1 << XMU_DVFS_CTRL_EN_SHIFT,
- dvfs_info->base + XMU_DVFS_CTRL);
-}
-
-static int exynos_target(struct cpufreq_policy *policy, unsigned int index)
-{
- unsigned int tmp;
- int i;
- struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
-
- mutex_lock(&cpufreq_lock);
-
- freqs.old = policy->cur;
- freqs.new = freq_table[index].frequency;
-
- cpufreq_freq_transition_begin(policy, &freqs);
-
- /* Set the target frequency in all C0_3_PSTATE register */
- for_each_cpu(i, policy->cpus) {
- tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
- tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
- tmp |= (index << C0_3_PSTATE_NEW_SHIFT);
-
- __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
- }
- mutex_unlock(&cpufreq_lock);
- return 0;
-}
-
-static void exynos_cpufreq_work(struct work_struct *work)
-{
- unsigned int cur_pstate, index;
- struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
- struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
-
- /* Ensure we can access cpufreq structures */
- if (unlikely(dvfs_info->dvfs_enabled == false))
- goto skip_work;
-
- mutex_lock(&cpufreq_lock);
- freqs.old = policy->cur;
-
- cur_pstate = __raw_readl(dvfs_info->base + XMU_P_STATUS);
- if (cur_pstate >> C0_3_PSTATE_VALID_SHIFT & 0x1)
- index = (cur_pstate >> C0_3_PSTATE_CURR_SHIFT) & P_VALUE_MASK;
- else
- index = (cur_pstate >> C0_3_PSTATE_NEW_SHIFT) & P_VALUE_MASK;
-
- if (likely(index < dvfs_info->freq_count)) {
- freqs.new = freq_table[index].frequency;
- } else {
- dev_crit(dvfs_info->dev, "New frequency out of range\n");
- freqs.new = freqs.old;
- }
- cpufreq_freq_transition_end(policy, &freqs, 0);
-
- cpufreq_cpu_put(policy);
- mutex_unlock(&cpufreq_lock);
-skip_work:
- enable_irq(dvfs_info->irq);
-}
-
-static irqreturn_t exynos_cpufreq_irq(int irq, void *id)
-{
- unsigned int tmp;
-
- tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQ);
- if (tmp >> PSTATE_CHANGED_SHIFT & 0x1) {
- __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQ);
- disable_irq_nosync(irq);
- schedule_work(&dvfs_info->irq_work);
- }
- return IRQ_HANDLED;
-}
-
-static void exynos_sort_descend_freq_table(void)
-{
- struct cpufreq_frequency_table *freq_tbl = dvfs_info->freq_table;
- int i = 0, index;
- unsigned int tmp_freq;
- /*
- * Exynos5440 clock controller state logic expects the cpufreq table to
- * be in descending order. But the OPP library constructs the table in
- * ascending order. So to make the table descending we just need to
- * swap the i element with the N - i element.
- */
- for (i = 0; i < dvfs_info->freq_count / 2; i++) {
- index = dvfs_info->freq_count - i - 1;
- tmp_freq = freq_tbl[i].frequency;
- freq_tbl[i].frequency = freq_tbl[index].frequency;
- freq_tbl[index].frequency = tmp_freq;
- }
-}
-
-static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
-{
- policy->clk = dvfs_info->cpu_clk;
- return cpufreq_generic_init(policy, dvfs_info->freq_table,
- dvfs_info->latency);
-}
-
-static struct cpufreq_driver exynos_driver = {
- .flags = CPUFREQ_STICKY | CPUFREQ_ASYNC_NOTIFICATION |
- CPUFREQ_NEED_INITIAL_FREQ_CHECK,
- .verify = cpufreq_generic_frequency_table_verify,
- .target_index = exynos_target,
- .get = cpufreq_generic_get,
- .init = exynos_cpufreq_cpu_init,
- .name = CPUFREQ_NAME,
- .attr = cpufreq_generic_attr,
-};
-
-static const struct of_device_id exynos_cpufreq_match[] = {
- {
- .compatible = "samsung,exynos5440-cpufreq",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, exynos_cpufreq_match);
-
-static int exynos_cpufreq_probe(struct platform_device *pdev)
-{
- int ret = -EINVAL;
- struct device_node *np;
- struct resource res;
- unsigned int cur_frequency;
-
- np = pdev->dev.of_node;
- if (!np)
- return -ENODEV;
-
- dvfs_info = devm_kzalloc(&pdev->dev, sizeof(*dvfs_info), GFP_KERNEL);
- if (!dvfs_info) {
- ret = -ENOMEM;
- goto err_put_node;
- }
-
- dvfs_info->dev = &pdev->dev;
-
- ret = of_address_to_resource(np, 0, &res);
- if (ret)
- goto err_put_node;
-
- dvfs_info->base = devm_ioremap_resource(dvfs_info->dev, &res);
- if (IS_ERR(dvfs_info->base)) {
- ret = PTR_ERR(dvfs_info->base);
- goto err_put_node;
- }
-
- dvfs_info->irq = irq_of_parse_and_map(np, 0);
- if (!dvfs_info->irq) {
- dev_err(dvfs_info->dev, "No cpufreq irq found\n");
- ret = -ENODEV;
- goto err_put_node;
- }
-
- ret = dev_pm_opp_of_add_table(dvfs_info->dev);
- if (ret) {
- dev_err(dvfs_info->dev, "failed to init OPP table: %d\n", ret);
- goto err_put_node;
- }
-
- ret = dev_pm_opp_init_cpufreq_table(dvfs_info->dev,
- &dvfs_info->freq_table);
- if (ret) {
- dev_err(dvfs_info->dev,
- "failed to init cpufreq table: %d\n", ret);
- goto err_free_opp;
- }
- dvfs_info->freq_count = dev_pm_opp_get_opp_count(dvfs_info->dev);
- exynos_sort_descend_freq_table();
-
- if (of_property_read_u32(np, "clock-latency", &dvfs_info->latency))
- dvfs_info->latency = DEF_TRANS_LATENCY;
-
- dvfs_info->cpu_clk = devm_clk_get(dvfs_info->dev, "armclk");
- if (IS_ERR(dvfs_info->cpu_clk)) {
- dev_err(dvfs_info->dev, "Failed to get cpu clock\n");
- ret = PTR_ERR(dvfs_info->cpu_clk);
- goto err_free_table;
- }
-
- cur_frequency = clk_get_rate(dvfs_info->cpu_clk);
- if (!cur_frequency) {
- dev_err(dvfs_info->dev, "Failed to get clock rate\n");
- ret = -EINVAL;
- goto err_free_table;
- }
- cur_frequency /= 1000;
-
- INIT_WORK(&dvfs_info->irq_work, exynos_cpufreq_work);
- ret = devm_request_irq(dvfs_info->dev, dvfs_info->irq,
- exynos_cpufreq_irq, IRQF_TRIGGER_NONE,
- CPUFREQ_NAME, dvfs_info);
- if (ret) {
- dev_err(dvfs_info->dev, "Failed to register IRQ\n");
- goto err_free_table;
- }
-
- ret = init_div_table();
- if (ret) {
- dev_err(dvfs_info->dev, "Failed to initialise div table\n");
- goto err_free_table;
- }
-
- exynos_enable_dvfs(cur_frequency);
- ret = cpufreq_register_driver(&exynos_driver);
- if (ret) {
- dev_err(dvfs_info->dev,
- "%s: failed to register cpufreq driver\n", __func__);
- goto err_free_table;
- }
-
- of_node_put(np);
- dvfs_info->dvfs_enabled = true;
- return 0;
-
-err_free_table:
- dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
-err_free_opp:
- dev_pm_opp_of_remove_table(dvfs_info->dev);
-err_put_node:
- of_node_put(np);
- dev_err(&pdev->dev, "%s: failed initialization\n", __func__);
- return ret;
-}
-
-static int exynos_cpufreq_remove(struct platform_device *pdev)
-{
- cpufreq_unregister_driver(&exynos_driver);
- dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
- dev_pm_opp_of_remove_table(dvfs_info->dev);
- return 0;
-}
-
-static struct platform_driver exynos_cpufreq_platdrv = {
- .driver = {
- .name = "exynos5440-cpufreq",
- .of_match_table = exynos_cpufreq_match,
- },
- .probe = exynos_cpufreq_probe,
- .remove = exynos_cpufreq_remove,
-};
-module_platform_driver(exynos_cpufreq_platdrv);
-
-MODULE_AUTHOR("Amit Daniel Kachhap <amit.daniel@samsung.com>");
-MODULE_DESCRIPTION("Exynos5440 cpufreq driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
index 8b3c2a79ad6c..b2ff423ad7f8 100644
--- a/drivers/cpufreq/imx6q-cpufreq.c
+++ b/drivers/cpufreq/imx6q-cpufreq.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -50,6 +51,7 @@ static struct clk_bulk_data clks[] = {
};
static struct device *cpu_dev;
+static struct thermal_cooling_device *cdev;
static bool free_opp;
static struct cpufreq_frequency_table *freq_table;
static unsigned int max_freq;
@@ -191,6 +193,16 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
return 0;
}
+static void imx6q_cpufreq_ready(struct cpufreq_policy *policy)
+{
+ cdev = of_cpufreq_cooling_register(policy);
+
+ if (!cdev)
+ dev_err(cpu_dev,
+ "running cpufreq without cooling device: %ld\n",
+ PTR_ERR(cdev));
+}
+
static int imx6q_cpufreq_init(struct cpufreq_policy *policy)
{
int ret;
@@ -202,13 +214,22 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy)
return ret;
}
+static int imx6q_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_cooling_unregister(cdev);
+
+ return 0;
+}
+
static struct cpufreq_driver imx6q_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = imx6q_set_target,
.get = cpufreq_generic_get,
.init = imx6q_cpufreq_init,
+ .exit = imx6q_cpufreq_exit,
.name = "imx6q-cpufreq",
+ .ready = imx6q_cpufreq_ready,
.attr = cpufreq_generic_attr,
.suspend = cpufreq_generic_suspend,
};
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index d4ed0022b0dd..b6a1aadaff9f 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -670,21 +670,18 @@ static ssize_t store_energy_performance_preference(
{
struct cpudata *cpu_data = all_cpu_data[policy->cpu];
char str_preference[21];
- int ret, i = 0;
+ int ret;
ret = sscanf(buf, "%20s", str_preference);
if (ret != 1)
return -EINVAL;
- while (energy_perf_strings[i] != NULL) {
- if (!strcmp(str_preference, energy_perf_strings[i])) {
- intel_pstate_set_energy_pref_index(cpu_data, i);
- return count;
- }
- ++i;
- }
+ ret = match_string(energy_perf_strings, -1, str_preference);
+ if (ret < 0)
+ return ret;
- return -EINVAL;
+ intel_pstate_set_energy_pref_index(cpu_data, ret);
+ return count;
}
static ssize_t show_energy_performance_preference(
@@ -2011,7 +2008,8 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
static void intel_pstate_adjust_policy_max(struct cpufreq_policy *policy,
struct cpudata *cpu)
{
- if (cpu->pstate.max_pstate_physical > cpu->pstate.max_pstate &&
+ if (!hwp_active &&
+ cpu->pstate.max_pstate_physical > cpu->pstate.max_pstate &&
policy->max < policy->cpuinfo.max_freq &&
policy->max > cpu->pstate.max_freq) {
pr_debug("policy->max > max non turbo frequency\n");
@@ -2085,6 +2083,15 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy)
cpu->pstate.max_pstate : cpu->pstate.turbo_pstate;
policy->cpuinfo.max_freq *= cpu->pstate.scaling;
+ if (hwp_active) {
+ unsigned int max_freq;
+
+ max_freq = global.turbo_disabled ?
+ cpu->pstate.max_freq : cpu->pstate.turbo_freq;
+ if (max_freq < policy->cpuinfo.max_freq)
+ policy->cpuinfo.max_freq = max_freq;
+ }
+
intel_pstate_init_acpi_perf_limits(policy);
policy->fast_switch_possible = true;
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
index 0c56c9759672..099a849396f6 100644
--- a/drivers/cpufreq/pcc-cpufreq.c
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -593,6 +593,15 @@ static int __init pcc_cpufreq_init(void)
return ret;
}
+ if (num_present_cpus() > 4) {
+ pcc_cpufreq_driver.flags |= CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING;
+ pr_err("%s: Too many CPUs, dynamic performance scaling disabled\n",
+ __func__);
+ pr_err("%s: Try to enable another scaling driver through BIOS settings\n",
+ __func__);
+ pr_err("%s: and complain to the system vendor\n", __func__);
+ }
+
ret = cpufreq_register_driver(&pcc_cpufreq_driver);
return ret;
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index 54edaec1e608..bf6519cf64bc 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -758,8 +758,13 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
cur_msec = jiffies_to_msecs(get_jiffies_64());
- spin_lock(&gpstates->gpstate_lock);
freq_data.pstate_id = idx_to_pstate(new_index);
+ if (!gpstates) {
+ freq_data.gpstate_id = freq_data.pstate_id;
+ goto no_gpstate;
+ }
+
+ spin_lock(&gpstates->gpstate_lock);
if (!gpstates->last_sampled_time) {
gpstate_idx = new_index;
@@ -809,6 +814,7 @@ gpstates_done:
spin_unlock(&gpstates->gpstate_lock);
+no_gpstate:
/*
* Use smp_call_function to send IPI and execute the
* mtspr on target CPU. We could do that without IPI
@@ -843,6 +849,13 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy)
kernfs_put(kn);
}
+ policy->freq_table = powernv_freqs;
+ policy->fast_switch_possible = true;
+
+ if (pvr_version_is(PVR_POWER9))
+ return 0;
+
+ /* Initialise Gpstate ramp-down timer only on POWER8 */
gpstates = kzalloc(sizeof(*gpstates), GFP_KERNEL);
if (!gpstates)
return -ENOMEM;
@@ -857,8 +870,6 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy)
msecs_to_jiffies(GPSTATE_TIMER_INTERVAL);
spin_lock_init(&gpstates->gpstate_lock);
- policy->freq_table = powernv_freqs;
- policy->fast_switch_possible = true;
return 0;
}
@@ -998,7 +1009,8 @@ static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy)
freq_data.pstate_id = idx_to_pstate(powernv_pstate_info.min);
freq_data.gpstate_id = idx_to_pstate(powernv_pstate_info.min);
smp_call_function_single(policy->cpu, set_pstate, &freq_data, 1);
- del_timer_sync(&gpstates->timer);
+ if (gpstates)
+ del_timer_sync(&gpstates->timer);
}
static unsigned int powernv_fast_switch(struct cpufreq_policy *policy,
diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c b/drivers/cpufreq/qcom-cpufreq-kryo.c
index efc9a7ae4857..a1830fa25fc5 100644
--- a/drivers/cpufreq/qcom-cpufreq-kryo.c
+++ b/drivers/cpufreq/qcom-cpufreq-kryo.c
@@ -109,8 +109,9 @@ static int qcom_cpufreq_kryo_probe(struct platform_device *pdev)
speedbin_nvmem = of_nvmem_cell_get(np, NULL);
of_node_put(np);
if (IS_ERR(speedbin_nvmem)) {
- dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
- PTR_ERR(speedbin_nvmem));
+ if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
+ dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
+ PTR_ERR(speedbin_nvmem));
return PTR_ERR(speedbin_nvmem);
}
OpenPOWER on IntegriCloud