msm: devfreq_cpubw: Add devfreq driver for CPU to DDR bandwidth voting
This driver registers itself as a devfreq device that allows devfreq governors to make CPU to DDR IB/AB bandwidth votes. This driver allows the governors to be agnostic of the bandwidth voting APIs, the number of CPU master ports or DDR slave ports, the actual port numbers, the system topology, the available DDR frequencies, etc. Change-Id: If055ddd580afd41f9668b111e6c09a047488b2e0 Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
This commit is contained in:
parent
f0f3dd3bb3
commit
a2dd9de8c7
|
@ -0,0 +1,30 @@
|
|||
MSM CPU bandwidth device
|
||||
|
||||
cpubw is a device that represents the CPU subsystem master ports in a MSM SoC
|
||||
and the related info that is needed to make CPU to DDR bandwidth votes.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "qcom,cpubw"
|
||||
- qcom,cpu-mem-ports: A list of tuples where each tuple consists of a bus
|
||||
master (CPU subsystem) port number and a bus slave
|
||||
(memory) port number.
|
||||
- qcom,bw-tbl: A list of meaningful instantaneous bandwidth values
|
||||
(in MB/s) that can be requested from the CPU
|
||||
subsystem to DDR. The list of values depend on the
|
||||
supported DDR frequencies and the bus widths.
|
||||
|
||||
Example:
|
||||
|
||||
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 */ >;
|
||||
};
|
|
@ -11,6 +11,8 @@ config ARCH_MSM8974
|
|||
select MSM_SCM
|
||||
select MSM_GPIOMUX
|
||||
select MSM_MULTIMEDIA_USE_ION
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
select MSM_PIL
|
||||
select MSM_SPM_V2
|
||||
select MSM_L2_SPM
|
||||
|
@ -66,6 +68,8 @@ config ARCH_APQ8084
|
|||
select MSM_RPM_LOG
|
||||
select USB_ARCH_HAS_XHCI
|
||||
select KRAIT_REGULATOR
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
select MSM_PIL
|
||||
select ENABLE_VMALLOC_SAVINGS
|
||||
select MSM_ULTRASOUND_B
|
||||
|
@ -75,6 +79,8 @@ config ARCH_MPQ8092
|
|||
bool "MPQ8092"
|
||||
select REGULATOR
|
||||
select ARCH_MSM_KRAITMP
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
select GPIO_MSM_V3
|
||||
select ARM_GIC
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
|
@ -103,6 +109,8 @@ config ARCH_FSM9900
|
|||
select MSM_SCM
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select MSM_GPIOMUX
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
select MSM_PIL
|
||||
select MSM_NATIVE_RESTART
|
||||
select REGULATOR
|
||||
|
@ -133,6 +141,8 @@ config ARCH_MSMKRYPTON
|
|||
select QMI_ENCDEC
|
||||
select MSM_JTAG_MM if CORESIGHT_ETM
|
||||
select MSM_CORTEX_A7
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
|
||||
config ARCH_MSM8610
|
||||
bool "MSM8610"
|
||||
|
@ -159,6 +169,8 @@ config ARCH_MSM8610
|
|||
select MSM_CORTEX_A7
|
||||
select CPU_FREQ_MSM
|
||||
select CPU_FREQ
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
select MSM_PIL
|
||||
select MSM_RUN_QUEUE_STATS
|
||||
select ARM_HAS_SG_CHAIN
|
||||
|
@ -200,6 +212,8 @@ config ARCH_MSM8226
|
|||
select MSM_CORTEX_A7
|
||||
select CPU_FREQ_MSM
|
||||
select CPU_FREQ
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
select MSM_PIL
|
||||
select MSM_RUN_QUEUE_STATS
|
||||
select ARM_HAS_SG_CHAIN
|
||||
|
@ -224,6 +238,8 @@ config ARCH_MSMSAMARIUM
|
|||
select CPU_V7
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select MSM_SCM
|
||||
select PM_DEVFREQ
|
||||
select MSM_DEVFREQ_CPUBW
|
||||
select MSM_PIL
|
||||
select MSM_GPIOMUX
|
||||
select MSM_SPM_V2
|
||||
|
@ -807,6 +823,19 @@ config MSM_CPU_FREQ_MIN
|
|||
|
||||
endif # CPU_FREQ_MSM
|
||||
|
||||
config MSM_DEVFREQ_CPUBW
|
||||
bool "Devfreq device for CPU<->DDR IB/AB BW voting"
|
||||
depends on PM_DEVFREQ
|
||||
select DEVFREQ_GOV_PERFORMANCE
|
||||
select DEVFREQ_GOV_POWERSAVE
|
||||
select DEVFREQ_GOV_USERSPACE
|
||||
default n
|
||||
help
|
||||
Different devfreq governors use this devfreq device to make CPU to
|
||||
DDR IB/AB bandwidth votes. This driver provides a SoC topology
|
||||
agnostic interface to so that some of the devfreq governors can be
|
||||
shared across SoCs.
|
||||
|
||||
config MSM_AVS_HW
|
||||
bool "Enable Adaptive Voltage Scaling (AVS)"
|
||||
default n
|
||||
|
|
|
@ -173,6 +173,7 @@ obj-$(CONFIG_MSM_SMCMOD) += smcmod.o
|
|||
obj-$(CONFIG_ARCH_MSM8974) += msm_mpmctr.o
|
||||
obj-$(CONFIG_MSM_CPR_REGULATOR) += cpr-regulator.o
|
||||
obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o
|
||||
obj-$(CONFIG_MSM_DEVFREQ_CPUBW) += devfreq_cpubw.o
|
||||
|
||||
|
||||
obj-$(CONFIG_ARCH_RANDOM) += early_random.o
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "cpubw: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/of.h>
|
||||
#include <trace/events/power.h>
|
||||
#include <mach/msm_bus.h>
|
||||
#include <mach/msm_bus_board.h>
|
||||
|
||||
/* Has to be ULL to prevent overflow where this macro is used. */
|
||||
#define MBYTE (1ULL << 20)
|
||||
#define MAX_PATHS 2
|
||||
|
||||
static struct msm_bus_vectors vectors[MAX_PATHS * 2];
|
||||
static struct msm_bus_paths bw_levels[] = {
|
||||
{ .vectors = &vectors[0] },
|
||||
{ .vectors = &vectors[MAX_PATHS] },
|
||||
};
|
||||
static struct msm_bus_scale_pdata bw_data = {
|
||||
.usecase = bw_levels,
|
||||
.num_usecases = ARRAY_SIZE(bw_levels),
|
||||
.name = "devfreq_cpubw",
|
||||
.active_only = 1,
|
||||
};
|
||||
static int num_paths;
|
||||
static u32 bus_client;
|
||||
|
||||
static int set_bw(int new_ib, int new_ab)
|
||||
{
|
||||
static int cur_idx, cur_ab, cur_ib;
|
||||
int i, ret;
|
||||
|
||||
if (cur_ib == new_ib && cur_ab == new_ab)
|
||||
return 0;
|
||||
|
||||
i = (cur_idx + 1) % ARRAY_SIZE(bw_levels);
|
||||
|
||||
bw_levels[i].vectors[0].ib = new_ib * MBYTE;
|
||||
bw_levels[i].vectors[0].ab = new_ab / num_paths * MBYTE;
|
||||
bw_levels[i].vectors[1].ib = new_ib * MBYTE;
|
||||
bw_levels[i].vectors[1].ab = new_ab / num_paths * MBYTE;
|
||||
|
||||
pr_debug("BW MBps: AB: %d IB: %d\n", new_ab, new_ib);
|
||||
|
||||
ret = msm_bus_scale_client_update_request(bus_client, i);
|
||||
if (ret) {
|
||||
pr_err("bandwidth request failed (%d)\n", ret);
|
||||
} else {
|
||||
cur_idx = i;
|
||||
cur_ib = new_ib;
|
||||
cur_ab = new_ab;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq,
|
||||
u32 flags)
|
||||
{
|
||||
int i;
|
||||
unsigned long atmost, atleast, f;
|
||||
|
||||
atmost = p->freq_table[0];
|
||||
atleast = p->freq_table[p->max_state-1];
|
||||
for (i = 0; i < p->max_state; i++) {
|
||||
f = p->freq_table[i];
|
||||
if (f <= *freq)
|
||||
atmost = max(f, atmost);
|
||||
if (f >= *freq)
|
||||
atleast = min(f, atleast);
|
||||
}
|
||||
|
||||
if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND)
|
||||
*freq = atmost;
|
||||
else
|
||||
*freq = atleast;
|
||||
}
|
||||
|
||||
struct devfreq_dev_profile cpubw_profile;
|
||||
static long gov_ab;
|
||||
|
||||
int cpubw_target(struct device *dev, unsigned long *freq, u32 flags)
|
||||
{
|
||||
find_freq(&cpubw_profile, freq, flags);
|
||||
return set_bw(*freq, gov_ab);
|
||||
}
|
||||
|
||||
static struct devfreq_governor_data gov_data[] = {
|
||||
{ .name = "performance" },
|
||||
{ .name = "powersave" },
|
||||
{ .name = "userspace" },
|
||||
};
|
||||
struct devfreq_dev_profile cpubw_profile = {
|
||||
.polling_ms = 50,
|
||||
.target = cpubw_target,
|
||||
.governor_data = gov_data,
|
||||
.num_governor_data = ARRAY_SIZE(gov_data),
|
||||
};
|
||||
|
||||
#define PROP_PORTS "qcom,cpu-mem-ports"
|
||||
#define PROP_TBL "qcom,bw-tbl"
|
||||
|
||||
static int __init cpubw_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct devfreq_dev_profile *p = &cpubw_profile;
|
||||
struct devfreq *df;
|
||||
u32 *data, ports[MAX_PATHS * 2];
|
||||
int ret, len, i;
|
||||
|
||||
if (of_find_property(dev->of_node, PROP_PORTS, &len)) {
|
||||
len /= sizeof(ports[0]);
|
||||
if (len % 2 || len > ARRAY_SIZE(ports)) {
|
||||
dev_err(dev, "Unexpected number of ports\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(dev->of_node, PROP_PORTS,
|
||||
ports, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_paths = len / 2;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_paths; i++) {
|
||||
bw_levels[0].vectors[i].src = ports[2 * i];
|
||||
bw_levels[0].vectors[i].dst = ports[2 * i + 1];
|
||||
bw_levels[1].vectors[i].src = ports[2 * i];
|
||||
bw_levels[1].vectors[i].dst = ports[2 * i + 1];
|
||||
}
|
||||
bw_levels[0].num_paths = num_paths;
|
||||
bw_levels[1].num_paths = num_paths;
|
||||
|
||||
if (of_find_property(dev->of_node, PROP_TBL, &len)) {
|
||||
len /= sizeof(*data);
|
||||
data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
p->freq_table = devm_kzalloc(dev,
|
||||
len * sizeof(*p->freq_table),
|
||||
GFP_KERNEL);
|
||||
if (!p->freq_table)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_property_read_u32_array(dev->of_node, PROP_TBL,
|
||||
data, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
p->freq_table[i] = data[i];
|
||||
p->max_state = len;
|
||||
}
|
||||
|
||||
bus_client = msm_bus_scale_register_client(&bw_data);
|
||||
if (!bus_client) {
|
||||
dev_err(dev, "Unable to register bus client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
df = devfreq_add_device(dev, &cpubw_profile, "powersave", NULL);
|
||||
if (IS_ERR(df)) {
|
||||
msm_bus_scale_unregister_client(bus_client);
|
||||
return PTR_ERR(df);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id match_table[] = {
|
||||
{ .compatible = "qcom,cpubw" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver cpubw_driver = {
|
||||
.driver = {
|
||||
.name = "cpubw",
|
||||
.of_match_table = match_table,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init cpubw_init(void)
|
||||
{
|
||||
platform_driver_probe(&cpubw_driver, cpubw_probe);
|
||||
return 0;
|
||||
}
|
||||
device_initcall(cpubw_init);
|
||||
|
||||
MODULE_DESCRIPTION("CPU DDR bandwidth voting driver MSM CPUs");
|
||||
MODULE_LICENSE("GPL v2");
|
Loading…
Reference in New Issue