devfreq: Add MSM CPUfreq governor
The MSM CPUfreq devfreq governor determines the CPU to DDR bandwidth vote
based on the current CPU frequency of all the active CPUs.
This functionality used to be a part of the MSM CPUfreq driver that
directly voted for the CPU to DDR bandwidth using MSM bus scaling APIs.
This refactor decouples CPU to DDR BW scaling from CPU frequency and allows
switch between various CPU BW governors.
The bandwidth values in the msm-cpufreq table have to be updated since the
new CPU BW driver does the MBps to Bps conversion correctly. The MSM
CPUfreq driver used to do * 1000 * 1000 to convert from MBps to Bps instead
of doing * 1024 * 1024.
Change-Id: I184f09d628a1b27d52506d2f760d65831f1a1110
Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/arm/msm/msm-cpufreq.txt b/Documentation/devicetree/bindings/arm/msm/msm-cpufreq.txt
index 02514d4..fed49c9 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm-cpufreq.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm-cpufreq.txt
@@ -9,23 +9,15 @@
- compatible: Must be "qcom,msm-cpufreq"
- qcom,cpufreq-table: A list of tuples where each tuple consists of a
usable CPU frequency (KHz), an optional cache
- frequency (KHz) and an optional memory bandwidth
+ frequency (KHz) and a mandatory memory bandwidth
value (MBPS) listed in that order. The cache
frequencies shall not be listed if the device cannot
- run the cache asynchronous to one or more CPUs. The
- memory bandwidth values shall not be listed if the
- optional cpu-mem-ports property is not supplied.
-
-Optional properties:
-- qcom,cpu-mem-ports: A list of tuples where each tuple consists of a bus
- master (CPU) port number and a bus slave (memory)
- port number.
+ run the cache asynchronous to one or more CPUs.
Example:
qcom,msm-cpufreq@0 {
regs = <0 4>
compatible = "qcom,msm-cpufreq";
- qcom,cpu-mem-ports = <1 512>, <2 513>;
qcom,cpufreq-table =
< 300000 300000 600 >,
< 422400 422400 1200 >,
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 804cc44..e0304f5 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -1018,23 +1018,32 @@
cpu-vdd-supply = <&apc_vreg_corner>;
};
+ qcom,cpubw {
+ compatible = "qcom,cpubw";
+ qcom,cpu-mem-ports = <1 512>;
+ qcom,bw-tbl =
+ < 1525 /* 200 MHz */ >,
+ < 2441 /* 320 MHz */ >,
+ < 3051 /* 400 MHz */ >,
+ < 4066 /* 533 MHz */ >;
+ };
+
qcom,msm-cpufreq@0 {
reg = <0 4>;
compatible = "qcom,msm-cpufreq";
- qcom,cpu-mem-ports = <1 512>;
qcom,cpufreq-table =
- < 300000 1600 /* 200 MHz */ >,
- < 384000 1600 /* 200 MHz */ >,
- < 600000 1600 /* 200 MHz */ >,
- < 787200 3200 /* 400 MHz */ >,
- < 998400 4264 /* 533 MHz */ >,
- < 1094400 4264 /* 533 MHz */ >,
- < 1190400 4264 /* 533 MHz */ >,
- < 1305600 4264 /* 533 MHz */ >,
- < 1344000 4264 /* 533 MHz */ >,
- < 1401600 4264 /* 533 MHz */ >,
- < 1497600 4264 /* 533 MHz */ >,
- < 1593600 4264 /* 533 MHz */ >;
+ < 300000 1525 >,
+ < 384000 1525 >,
+ < 600000 1525 >,
+ < 787200 3051 >,
+ < 998400 4066 >,
+ < 1094400 4066 >,
+ < 1190400 4066 >,
+ < 1305600 4066 >,
+ < 1344000 4066 >,
+ < 1401600 4066 >,
+ < 1497600 4066 >,
+ < 1593600 4066 >;
};
qcom,ocmem@fdd00000 {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index a912da8..e4dace1 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -1515,25 +1515,39 @@
< 2265600000 925000 691 >;
};
+ qcom,cpubw {
+ compatible = "qcom,cpubw";
+ qcom,cpu-mem-ports = <1 512>, <2 512>;
+ qcom,bw-tbl =
+ < 572 /* 75 MHz */ >,
+ < 1144 /* 150 MHz */ >,
+ < 1525 /* 200 MHz */ >,
+ < 2342 /* 307 MHz */ >,
+ < 3509 /* 460 MHz */ >,
+ < 4684 /* 614 MHz */ >,
+ < 6103 /* 800 MHz */ >,
+ < 7102 /* 931 MHz */ >;
+ };
+
qcom,msm-cpufreq@0 {
reg = <0 4>;
compatible = "qcom,msm-cpufreq";
- qcom,cpu-mem-ports = <1 512>, <2 512>;
qcom,cpufreq-table =
- < 300000 300000 600 /* 75 MHz */ >,
- < 422400 422400 1200 /* 150 MHz */ >,
- < 652800 499200 1600 /* 200 MHz */ >,
- < 729600 576000 2456 /* 307 MHz */ >,
- < 883200 576000 2456 /* 307 MHz */ >,
- < 960000 960000 3680 /* 460 MHz */ >,
- < 1036800 1036800 3680 /* 460 MHz */ >,
- < 1190400 1036800 3680 /* 460 MHz */ >,
- < 1267200 1267200 4912 /* 614 MHz */ >,
- < 1497600 1497600 4912 /* 614 MHz */ >,
- < 1574400 1574400 6400 /* 800 MHz */ >,
- < 1728000 1651200 6400 /* 800 MHz */ >,
- < 1958400 1728000 7448 /* 931 MHz */ >,
- < 2265600 1728000 7448 /* 931 MHz */ >;
+ < 300000 300000 572 >,
+ < 422400 422400 1144 >,
+ < 652800 499200 1525 >,
+ < 729600 576000 2342 >,
+ < 883200 576000 2342 >,
+ < 960000 960000 3509 >,
+ < 1036800 1036800 3509 >,
+ < 1190400 1036800 3509 >,
+ < 1267200 1267200 4684 >,
+ < 1497600 1497600 4684 >,
+ < 1574400 1574400 6103 >,
+ < 1728000 1651200 6103 >,
+ < 1958400 1728000 7102 >,
+ < 2265600 1728000 7102 >,
+ < 2457600 1728000 7102 >;
};
usb3: qcom,ssusb@f9200000 {
diff --git a/arch/arm/boot/dts/msm8974pro.dtsi b/arch/arm/boot/dts/msm8974pro.dtsi
index 41f02fe..b670cfd 100755
--- a/arch/arm/boot/dts/msm8974pro.dtsi
+++ b/arch/arm/boot/dts/msm8974pro.dtsi
@@ -1548,25 +1548,6 @@
< 2457600000 970000 802 >;
};
- qcom,msm-cpufreq@0 {
- qcom,cpufreq-table =
- < 300000 300000 300 /* 37.5 MHz */ >,
- < 422400 422400 300 /* 37.5 MHz */ >,
- < 652800 499200 300 /* 37.5 MHz */ >,
- < 729600 576000 300 /* 37.5 MHz */ >,
- < 883200 576000 300 /* 37.5 MHz */ >,
- < 960000 960000 300 /* 37.5 MHz */ >,
- < 1036800 1036800 300 /* 37.5 MHz */ >,
- < 1190400 1036800 300 /* 37.5 MHz */ >,
- < 1267200 1267200 300 /* 37.5 MHz */ >,
- < 1497600 1497600 300 /* 37.5 MHz */ >,
- < 1574400 1574400 300 /* 37.5 MHz */ >,
- < 1728000 1651200 300 /* 37.5 MHz */ >,
- < 1958400 1728000 300 /* 37.5 MHz */ >,
- < 2265600 1728000 300 /* 37.5 MHz */ >,
- < 2496000 1728000 300 /* 37.5 MHz */ >;
- };
-
i2c@f9928000 { /* BLSP-1 QUP-6 */
cell-index = <3>;
compatible = "qcom,i2c-qup";
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 32f240a..17a6f4c 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1955,6 +1955,7 @@
select DEVFREQ_GOV_PERFORMANCE
select DEVFREQ_GOV_POWERSAVE
select DEVFREQ_GOV_USERSPACE
+ select DEVFREQ_GOV_MSM_CPUFREQ
default n
help
Different devfreq governors use this devfreq device to make CPU to
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index 60856c2..0978a2d 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -32,7 +32,7 @@
#include <linux/platform_device.h>
#include <trace/events/power.h>
#include <mach/socinfo.h>
-#include <mach/msm_bus.h>
+#include <mach/cpufreq.h>
#include "acpuclock.h"
@@ -47,16 +47,12 @@
static struct clk *cpu_clk[NR_CPUS];
static struct clk *l2_clk;
static unsigned int freq_index[NR_CPUS];
+static unsigned int max_freq_index;
static struct cpufreq_frequency_table *freq_table;
static unsigned int *l2_khz;
static bool is_clk;
static bool is_sync;
-static struct msm_bus_vectors *bus_vec_lst;
-static struct msm_bus_scale_pdata bus_bw = {
- .name = "msm-cpufreq",
- .active_only = 1,
-};
-static u32 bus_client;
+static unsigned long *mem_bw;
struct cpufreq_work_struct {
struct work_struct work;
@@ -77,6 +73,11 @@
static DEFINE_PER_CPU(struct cpufreq_suspend_t, cpufreq_suspend);
+unsigned long msm_cpufreq_get_bw(void)
+{
+ return mem_bw[max_freq_index];
+}
+
static void update_l2_bw(int *also_cpu)
{
int rc = 0, cpu;
@@ -98,10 +99,10 @@
goto out;
}
- if (bus_client)
- rc = msm_bus_scale_client_update_request(bus_client, index);
+ max_freq_index = index;
+ rc = devfreq_msm_cpufreq_update_bw();
if (rc)
- pr_err("Bandwidth req failed (%d)\n", rc);
+ pr_err("Unable to update BW (%d)\n", rc);
out:
mutex_unlock(&l2bw_lock);
@@ -397,33 +398,14 @@
};
#define PROP_TBL "qcom,cpufreq-table"
-#define PROP_PORTS "qcom,cpu-mem-ports"
static int cpufreq_parse_dt(struct device *dev)
{
- int ret, len, nf, num_cols = 1, num_paths = 0, i, j, k;
- u32 *data, *ports = NULL;
- struct msm_bus_vectors *v = NULL;
+ int ret, len, nf, num_cols = 2, i, j;
+ u32 *data;
if (l2_clk)
num_cols++;
- /* Parse optional bus ports parameter */
- if (of_find_property(dev->of_node, PROP_PORTS, &len)) {
- len /= sizeof(*ports);
- if (len % 2)
- return -EINVAL;
-
- ports = devm_kzalloc(dev, len * sizeof(*ports), GFP_KERNEL);
- if (!ports)
- return -ENOMEM;
- ret = of_property_read_u32_array(dev->of_node, PROP_PORTS,
- ports, len);
- if (ret)
- return ret;
- num_paths = len / 2;
- num_cols++;
- }
-
/* Parse CPU freq -> L2/Mem BW map table. */
if (!of_find_property(dev->of_node, PROP_TBL, &len))
return -EINVAL;
@@ -444,7 +426,9 @@
/* Allocate all data structures. */
freq_table = devm_kzalloc(dev, (nf + 1) * sizeof(*freq_table),
GFP_KERNEL);
- if (!freq_table)
+ mem_bw = devm_kzalloc(dev, nf * sizeof(*mem_bw), GFP_KERNEL);
+
+ if (!freq_table || !mem_bw)
return -ENOMEM;
if (l2_clk) {
@@ -453,15 +437,6 @@
return -ENOMEM;
}
- if (num_paths) {
- int sz_u = nf * sizeof(*bus_bw.usecase);
- int sz_v = nf * num_paths * sizeof(*bus_vec_lst);
- bus_bw.usecase = devm_kzalloc(dev, sz_u, GFP_KERNEL);
- v = bus_vec_lst = devm_kzalloc(dev, sz_v, GFP_KERNEL);
- if (!bus_bw.usecase || !bus_vec_lst)
- return -ENOMEM;
- }
-
j = 0;
for (i = 0; i < nf; i++) {
unsigned long f;
@@ -504,25 +479,12 @@
}
}
- if (num_paths) {
- unsigned int bw_mbps = data[j++];
- bus_bw.usecase[i].num_paths = num_paths;
- bus_bw.usecase[i].vectors = v;
- for (k = 0; k < num_paths; k++) {
- v->src = ports[k * 2];
- v->dst = ports[k * 2 + 1];
- v->ib = bw_mbps * 1000000ULL;
- v++;
- }
- }
+ mem_bw[i] = data[j++];
}
- bus_bw.num_usecases = i;
freq_table[i].index = i;
freq_table[i].frequency = CPUFREQ_TABLE_END;
- if (ports)
- devm_kfree(dev, ports);
devm_kfree(dev, data);
return 0;
@@ -532,15 +494,12 @@
static int msm_cpufreq_show(struct seq_file *m, void *unused)
{
unsigned int i, cpu_freq;
- uint64_t ib;
if (!freq_table)
return 0;
seq_printf(m, "%10s%10s", "CPU (KHz)", "L2 (KHz)");
- if (bus_bw.usecase)
- seq_printf(m, "%12s", "Mem (MBps)");
- seq_printf(m, "\n");
+ seq_printf(m, "%12s\n", "Mem (MBps)");
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
cpu_freq = freq_table[i].frequency;
@@ -548,11 +507,7 @@
continue;
seq_printf(m, "%10d", cpu_freq);
seq_printf(m, "%10d", l2_khz ? l2_khz[i] : cpu_freq);
- if (bus_bw.usecase) {
- ib = bus_bw.usecase[i].vectors[0].ib;
- do_div(ib, 1000000);
- seq_printf(m, "%12llu", ib);
- }
+ seq_printf(m, "%12lu", mem_bw[i]);
seq_printf(m, "\n");
}
return 0;
@@ -602,10 +557,10 @@
cpufreq_frequency_table_get_attr(freq_table, cpu);
}
- if (bus_bw.usecase) {
- bus_client = msm_bus_scale_register_client(&bus_bw);
- if (!bus_client)
- dev_warn(dev, "Unable to register bus client\n");
+ ret = register_devfreq_msm_cpufreq();
+ if (ret) {
+ pr_err("devfreq governor registration failed\n");
+ return ret;
}
is_clk = true;
diff --git a/arch/arm/mach-msm/devfreq_cpubw.c b/arch/arm/mach-msm/devfreq_cpubw.c
index 4c7cb08..4b2d7ea 100644
--- a/arch/arm/mach-msm/devfreq_cpubw.c
+++ b/arch/arm/mach-msm/devfreq_cpubw.c
@@ -112,6 +112,7 @@
{ .name = "performance" },
{ .name = "powersave" },
{ .name = "userspace" },
+ { .name = "msm_cpufreq" },
};
struct devfreq_dev_profile cpubw_profile = {
.polling_ms = 50,
@@ -185,7 +186,7 @@
return -ENODEV;
}
- df = devfreq_add_device(dev, &cpubw_profile, "powersave", NULL);
+ df = devfreq_add_device(dev, &cpubw_profile, "msm_cpufreq", NULL);
if (IS_ERR(df)) {
msm_bus_scale_unregister_client(bus_client);
return PTR_ERR(df);
diff --git a/arch/arm/mach-msm/include/mach/cpufreq.h b/arch/arm/mach-msm/include/mach/cpufreq.h
new file mode 100644
index 0000000..46872d7
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/cpufreq.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MACH_CPUFREQ_H
+#define __MACH_CPUFREQ_H
+
+#if defined(CONFIG_DEVFREQ_GOV_MSM_CPUFREQ)
+extern int devfreq_msm_cpufreq_update_bw(void);
+extern int register_devfreq_msm_cpufreq(void);
+#else
+static int devfreq_msm_cpufreq_update_bw(void)
+{
+ return 0;
+}
+static int register_devfreq_msm_cpufreq(void)
+{
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_CPU_FREQ_MSM)
+extern unsigned long msm_cpufreq_get_bw(void);
+#else
+extern unsigned long msm_cpufreq_get_bw(void)
+{
+ return ULONG_MAX;
+}
+#endif
+
+#endif
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 17bbe19..ba38e23 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -71,6 +71,14 @@
Sets the frequency using a "on-demand" algorithm.
This governor is unlikely to be useful for other devices.
+config DEVFREQ_GOV_MSM_CPUFREQ
+ bool "MSM CPUfreq"
+ depends on CPU_FREQ_MSM
+ help
+ MSM CPUfreq based governor for CPU bandwidth voting. Sets the CPU
+ to DDR BW vote based on the current CPU frequency. This governor
+ is unlikely to be useful for non-MSM devices.
+
comment "DEVFREQ Drivers"
config ARM_EXYNOS4_BUS_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 29b48ff..3a960a4 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
obj-$(CONFIG_DEVFREQ_GOV_MSM_ADRENO_TZ) += governor_msm_adreno_tz.o
+obj-$(CONFIG_DEVFREQ_GOV_MSM_CPUFREQ) += governor_msm_cpufreq.o
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos4_bus.o
diff --git a/drivers/devfreq/governor_msm_cpufreq.c b/drivers/devfreq/governor_msm_cpufreq.c
new file mode 100644
index 0000000..9b13e26
--- /dev/null
+++ b/drivers/devfreq/governor_msm_cpufreq.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/devfreq.h>
+#include <mach/cpufreq.h>
+#include "governor.h"
+
+DEFINE_MUTEX(df_lock);
+static struct devfreq *df;
+
+static int devfreq_msm_cpufreq_get_freq(struct devfreq *df,
+ unsigned long *freq,
+ u32 *flag)
+{
+ *freq = msm_cpufreq_get_bw();
+ return 0;
+}
+
+int devfreq_msm_cpufreq_update_bw(void)
+{
+ int ret = 0;
+
+ mutex_lock(&df_lock);
+ if (df) {
+ mutex_lock(&df->lock);
+ ret = update_devfreq(df);
+ mutex_unlock(&df->lock);
+ }
+ mutex_unlock(&df_lock);
+ return ret;
+}
+
+static int devfreq_msm_cpufreq_ev_handler(struct devfreq *devfreq,
+ unsigned int event, void *data)
+{
+ int ret;
+
+ switch (event) {
+ case DEVFREQ_GOV_START:
+ mutex_lock(&df_lock);
+ df = devfreq;
+ mutex_unlock(&df_lock);
+
+ ret = devfreq_msm_cpufreq_update_bw();
+ if (ret) {
+ pr_err("Unable to update BW! Gov start failed!\n");
+ return ret;
+ }
+
+ devfreq_monitor_stop(df);
+ pr_debug("Enabled MSM CPUfreq governor\n");
+ break;
+
+ case DEVFREQ_GOV_STOP:
+ mutex_lock(&df_lock);
+ df = NULL;
+ mutex_unlock(&df_lock);
+
+ pr_debug("Disabled MSM CPUfreq governor\n");
+ break;
+ }
+
+ return 0;
+}
+
+static struct devfreq_governor devfreq_msm_cpufreq = {
+ .name = "msm_cpufreq",
+ .get_target_freq = devfreq_msm_cpufreq_get_freq,
+ .event_handler = devfreq_msm_cpufreq_ev_handler,
+};
+
+int register_devfreq_msm_cpufreq(void)
+{
+ return devfreq_add_governor(&devfreq_msm_cpufreq);
+}