mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
PM / devfreq: Add devfreq driver for simple device
This driver registers a devfreq device that allows devfreq governors to scale the frequency of a device clock. This single driver can be used to support multiple devices as long as those devices don't have a need or mechanism to monitor their load and use clock APIs to control their device/core clock. If devices need to support device specific status monitoring, they could extend this driver to allow registering device specific status monitoring functions or write their own specific devfreq device driver. Change-Id: Ie1797acf7b35cac6dc49428e270c23082634eb1e Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
This commit is contained in:
parent
d4dfec3ab0
commit
2161830f8b
@ -0,0 +1,47 @@
|
|||||||
|
Devfreq simple device
|
||||||
|
|
||||||
|
devfreq-simple-dev is a device that represents a simple device that cannot do
|
||||||
|
any status reporting and uses a clock that can be scaled by one of more
|
||||||
|
devfreq governors. It provides a list of usable frequencies for the device
|
||||||
|
and some additional optional parameters.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Must be "devfreq-simple-dev"
|
||||||
|
- clock-names: Must be "devfreq_clk"
|
||||||
|
- clocks: Must refer to the clock that's fed to the device.
|
||||||
|
- freq-tbl-khz: A list of usable frequencies (in KHz) for the device
|
||||||
|
clock.
|
||||||
|
Optional properties:
|
||||||
|
- polling-ms: Polling interval for the device in milliseconds. Default: 50
|
||||||
|
- governor: Initial governor to user for the device. Default: "performance"
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
qcom,cache {
|
||||||
|
compatible = "devfreq-simple-dev";
|
||||||
|
clock-names = "devfreq_clk";
|
||||||
|
clocks = <&clock_krait clk_l2_clk>;
|
||||||
|
polling-ms = 50;
|
||||||
|
governor = "cpufreq";
|
||||||
|
freq-tbl-khz =
|
||||||
|
< 300000 >,
|
||||||
|
< 345600 >,
|
||||||
|
< 422400 >,
|
||||||
|
< 499200 >,
|
||||||
|
< 576000 >,
|
||||||
|
< 652800 >,
|
||||||
|
< 729600 >,
|
||||||
|
< 806400 >,
|
||||||
|
< 883200 >,
|
||||||
|
< 960000 >,
|
||||||
|
< 1036800 >,
|
||||||
|
< 1113600 >,
|
||||||
|
< 1190400 >,
|
||||||
|
< 1267200 >,
|
||||||
|
< 1344000 >,
|
||||||
|
< 1420800 >,
|
||||||
|
< 1497600 >,
|
||||||
|
< 1574400 >,
|
||||||
|
< 1651200 >,
|
||||||
|
< 1728000 >;
|
||||||
|
};
|
@ -113,6 +113,16 @@ config ARM_RK3399_DMC_DEVFREQ
|
|||||||
It sets the frequency for the memory controller and reads the usage counts
|
It sets the frequency for the memory controller and reads the usage counts
|
||||||
from hardware.
|
from hardware.
|
||||||
|
|
||||||
|
config DEVFREQ_SIMPLE_DEV
|
||||||
|
tristate "Device driver for simple clock device with no status info"
|
||||||
|
select DEVFREQ_GOV_PERFORMANCE
|
||||||
|
select DEVFREQ_GOV_POWERSAVE
|
||||||
|
select DEVFREQ_GOV_USERSPACE
|
||||||
|
select DEVFREQ_GOV_CPUFREQ
|
||||||
|
help
|
||||||
|
Device driver for simple devices that control their frequency using
|
||||||
|
clock APIs and don't have any form of status reporting.
|
||||||
|
|
||||||
source "drivers/devfreq/event/Kconfig"
|
source "drivers/devfreq/event/Kconfig"
|
||||||
|
|
||||||
endif # PM_DEVFREQ
|
endif # PM_DEVFREQ
|
||||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
|
|||||||
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
|
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
|
||||||
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
|
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
|
||||||
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
|
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
|
||||||
|
obj-$(CONFIG_DEVFREQ_SIMPLE_DEV) += devfreq_simple_dev.o
|
||||||
|
|
||||||
# DEVFREQ Event Drivers
|
# DEVFREQ Event Drivers
|
||||||
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
|
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
|
||||||
|
190
drivers/devfreq/devfreq_simple_dev.c
Normal file
190
drivers/devfreq/devfreq_simple_dev.c
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 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) "devfreq-simple-dev: " 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/platform_device.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/devfreq.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <trace/events/power.h>
|
||||||
|
|
||||||
|
struct dev_data {
|
||||||
|
struct clk *clk;
|
||||||
|
struct devfreq *df;
|
||||||
|
struct devfreq_dev_profile profile;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev_target(struct device *dev, unsigned long *freq, u32 flags)
|
||||||
|
{
|
||||||
|
struct dev_data *d = dev_get_drvdata(dev);
|
||||||
|
unsigned long rfreq;
|
||||||
|
|
||||||
|
find_freq(&d->profile, freq, flags);
|
||||||
|
|
||||||
|
rfreq = clk_round_rate(d->clk, *freq * 1000);
|
||||||
|
if (IS_ERR_VALUE(rfreq)) {
|
||||||
|
dev_err(dev, "devfreq: Cannot find matching frequency for %lu\n",
|
||||||
|
*freq);
|
||||||
|
return rfreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clk_set_rate(d->clk, rfreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev_get_cur_freq(struct device *dev, unsigned long *freq)
|
||||||
|
{
|
||||||
|
struct dev_data *d = dev_get_drvdata(dev);
|
||||||
|
unsigned long f;
|
||||||
|
|
||||||
|
f = clk_get_rate(d->clk);
|
||||||
|
if (IS_ERR_VALUE(f))
|
||||||
|
return f;
|
||||||
|
*freq = f / 1000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PROP_TBL "freq-tbl-khz"
|
||||||
|
static int devfreq_clock_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct dev_data *d;
|
||||||
|
struct devfreq_dev_profile *p;
|
||||||
|
u32 *data, poll;
|
||||||
|
const char *gov_name;
|
||||||
|
int ret, len, i, j;
|
||||||
|
unsigned long f;
|
||||||
|
|
||||||
|
d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
|
||||||
|
if (!d)
|
||||||
|
return -ENOMEM;
|
||||||
|
platform_set_drvdata(pdev, d);
|
||||||
|
|
||||||
|
d->clk = devm_clk_get(dev, "devfreq_clk");
|
||||||
|
if (IS_ERR(d->clk))
|
||||||
|
return PTR_ERR(d->clk);
|
||||||
|
|
||||||
|
if (!of_find_property(dev->of_node, PROP_TBL, &len))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
len /= sizeof(*data);
|
||||||
|
data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
p = &d->profile;
|
||||||
|
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;
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
f = clk_round_rate(d->clk, data[i] * 1000);
|
||||||
|
if (IS_ERR_VALUE(f))
|
||||||
|
dev_warn(dev, "Unable to find dev rate for %d KHz",
|
||||||
|
data[i]);
|
||||||
|
else
|
||||||
|
p->freq_table[j++] = f / 1000;
|
||||||
|
}
|
||||||
|
p->max_state = j;
|
||||||
|
devm_kfree(dev, data);
|
||||||
|
|
||||||
|
if (p->max_state == 0) {
|
||||||
|
dev_err(dev, "Error parsing property %s!\n", PROP_TBL);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->target = dev_target;
|
||||||
|
p->get_cur_freq = dev_get_cur_freq;
|
||||||
|
ret = dev_get_cur_freq(dev, &p->initial_freq);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
p->polling_ms = 50;
|
||||||
|
if (!of_property_read_u32(dev->of_node, "polling-ms", &poll))
|
||||||
|
p->polling_ms = poll;
|
||||||
|
|
||||||
|
if (of_property_read_string(dev->of_node, "governor", &gov_name))
|
||||||
|
gov_name = "performance";
|
||||||
|
|
||||||
|
|
||||||
|
d->df = devfreq_add_device(dev, p, gov_name, NULL);
|
||||||
|
if (IS_ERR(d->df))
|
||||||
|
return PTR_ERR(d->df);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int devfreq_clock_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct dev_data *d = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
devfreq_remove_device(d->df);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id devfreq_simple_match_table[] = {
|
||||||
|
{ .compatible = "devfreq-simple-dev" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver devfreq_clock_driver = {
|
||||||
|
.probe = devfreq_clock_probe,
|
||||||
|
.remove = devfreq_clock_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "devfreq-simple-dev",
|
||||||
|
.of_match_table = devfreq_simple_match_table,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(devfreq_clock_driver);
|
||||||
|
MODULE_DESCRIPTION("Devfreq driver for setting generic device clock frequency");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
Loading…
x
Reference in New Issue
Block a user