Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2dd9de8 authored by Saravana Kannan's avatar Saravana Kannan
Browse files

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: default avatarSaravana Kannan <skannan@codeaurora.org>
parent f0f3dd3b
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
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 */ >;
	};
+29 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+218 −0
Original line number Diff line number Diff line
/*
 * 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");