mirror of https://gitee.com/openkylin/linux.git
Update devfreq for 5.9
Detailed description for this pull request: 1. Update devfreq core - Add support delayed timer for polling mode. Until now, the devfreq supports only deferrable timer for reducing the unneeded CPU wakeup. But, it has a problem for Non-CPU device like DMC device with DMA operation. These Non-CPU device need to monitor continuously regardless of CPU state. Add support the delayed timer for polling mode to support the continuous monitoring. - Fix indentation of result of devfreq_summary debugfs node. - Fix the wrong end of code with semicolon instead of comma - Clean-up code to use the unified local variable name in sysfs-related internal funcitons. - Fix trivial spelling for devfreq-event.c. 2. Update devfreq driver - Add the exception handling code to control when rockchip,pmu property is absent for rk3399_dmc.c. - Add missing 'rockchip,pmu' property to dt-binding document for rk3399_dmc.c. - Change the kind of timer of exynos5422-dmc.c from deferrable to delayed timer in order to monitor the DMC (Dynamic Memory Controller) status regardless of CPU idle state. And adjust the polling interval and upthreshold value in order to react faster and make better decisions when benchmarking testing for the memory behavior. - Add module parameter to either enable or disable the IRQ mode for DMC behavior monitoring. The exynos5422-dmc.c can operate in both polling and IRQ mode. The user can choose the monitoring mode by using module param. The default monitoring mode is polling mode with delayed timer. 3. Add maintainer entry - Add Dmitry Osipenko <digetx@gmail.com> as maintainer for memory frequency scaling drivers for Nvidia Tegra. He have been developed and reviewed the tegra*-devfreq.c. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEsSpuqBtbWtRe4rLGnM3fLN7rz1MFAl8iiJ0WHGN3MDAuY2hv aUBzYW1zdW5nLmNvbQAKCRCczd8s3uvPU+WbD/0TkndmsnqXgzkLsyAUFgWsRy3N LL8xwtHXmM76ujT5m5UH5A+BHp3Ex9SsGA4xJ9cr7C3Reg2OmSKe8BZjkA52fwDE 2qu0CHB4IP41EjS3skBqiEhSGdFPv7xd9z39dk6xgUNaQM3yEmcrtPI96jx2CYE9 WYroUl8Lc1uU9fnV+1dyah7nK9p+pi27QqFQBdyOLikOpi2qq5loY6EsBjDq8bym Lv5VjgpI5cpBflolf1y5Zi6p+qFHNUroPz5iOnYJIUNqgKUHEhY8CdGVlLynQTo/ IOLXvhuGQc7q2grFKUjHGTAps+YV2lbY8j8WZl+ujhLTkCxme/XILHXe7b2GHHZy TleViwsdhL0lYkGCOrla66qFn2kNIXMjEnRJ3GfL7wRUFliS6IlFrg50/TLws7Qe RogI+rM/LuBPM9H4IDy5WTglChnctOxc2sSmbWKy2u1LoDMxfR/SIEwjvdFq/enx U0fE/vpXrJkADPSk/4+W/AdnnV2JmIFKlHoy83cZYzp5KHq9voQOv575sMkvSYRl hRc9Y8zxYtPOS9cJGV/nxgyEfp/gkOpcwrvy/uPuOqVMLC//ZEK/gR78nfT1YvJ3 c6ODnY8wpK+HZdqhWqc7SXWA9kK3BZrrDRkDBRPXthVOvyvKcifKn9AjVETqRGDu OPpZ19FZqIy3KMVMEg== =Iw2C -----END PGP SIGNATURE----- Merge tag 'devfreq-next-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux Pull devfreq updates for v5.9 from Chanwoo Choi: 1. Update devfreq core - Add delayed timer support for polling mode. Until now, devfreq supported only deferrable timer to avoid unneeded CPU wakeups. However, it has a problem for non-CPU devices, like DMC, doing DMA. Such devices need to monitor continuously regardless of the CPU state, so delayed timer support for the polling mode to facilitate the continuous monitoring. - Fix indentation of result of devfreq_summary debugfs node. - Fix the wrong end of code with a semicolon instead of a comma. - Clean-up code to use a unified local variable name in sysfs-related internal funcitons. - Fix trivial spelling mistake in devfreq-event.c. 2. Update devfreq drivers - Add the exception handling code to control when rockchip,pmu property is absent for rk3399_dmc.c. - Add missing 'rockchip,pmu' property to dt-binding document for rk3399_dmc.c. - Change the type of timer in exynos5422-dmc.c from deferrable to delayed in order to monitor the DMC (Dynamic Memory Controller) status regardless of the CPU idle state. Also adjust the polling interval and upthreshold value in order to react faster and make better decisions when benchmarking testing for the memory behavior. - Add module parameter to either enable or disable the IRQ mode for DMC behavior monitoring. exynos5422-dmc.c can operate in both the polling and the IRQ mode. The user can choose the monitoring mode via a module param. The default monitoring mode is the polling mode with a delayed timer. 3. Add maintainer entry - Add Dmitry Osipenko <digetx@gmail.com> as maintainer for memory frequency scaling drivers for Nvidia Tegra. He has developed and reviewed tegra*-devfreq.c. * tag 'devfreq-next-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux: PM / devfreq: Fix the wrong end with semicolon PM / devfreq: Fix indentaion of devfreq_summary debugfs node PM / devfreq: Clean up the devfreq instance name in sysfs attr memory: samsung: exynos5422-dmc: Add module param to control IRQ mode memory: samsung: exynos5422-dmc: Adjust polling interval and uptreshold memory: samsung: exynos5422-dmc: Use delayed timer as default PM / devfreq: Add support delayed timer for polling mode dt-bindings: devfreq: rk3399_dmc: Add rockchip,pmu phandle PM / devfreq: tegra: Add Dmitry as a maintainer PM / devfreq: event: Fix trivial spelling PM / devfreq: rk3399_dmc: Fix kernel oops when rockchip,pmu is absent
This commit is contained in:
commit
a7ee88c3d3
|
@ -108,3 +108,15 @@ Description:
|
|||
frequency requested by governors and min_freq.
|
||||
The max_freq overrides min_freq because max_freq may be
|
||||
used to throttle devices to avoid overheating.
|
||||
|
||||
What: /sys/class/devfreq/.../timer
|
||||
Date: July 2020
|
||||
Contact: Chanwoo Choi <cw00.choi@samsung.com>
|
||||
Description:
|
||||
This ABI shows and stores the kind of work timer by users.
|
||||
This work timer is used by devfreq workqueue in order to
|
||||
monitor the device status such as utilization. The user
|
||||
can change the work timer on runtime according to their demand
|
||||
as following:
|
||||
echo deferrable > /sys/class/devfreq/.../timer
|
||||
echo delayed > /sys/class/devfreq/.../timer
|
||||
|
|
|
@ -18,6 +18,8 @@ Optional properties:
|
|||
format depends on the interrupt controller.
|
||||
It should be a DCF interrupt. When DDR DVFS finishes
|
||||
a DCF interrupt is triggered.
|
||||
- rockchip,pmu: Phandle to the syscon managing the "PMU general register
|
||||
files".
|
||||
|
||||
Following properties relate to DDR timing:
|
||||
|
||||
|
|
|
@ -11097,6 +11097,15 @@ F: Documentation/core-api/boot-time-mm.rst
|
|||
F: include/linux/memblock.h
|
||||
F: mm/memblock.c
|
||||
|
||||
MEMORY FREQUENCY SCALING DRIVERS FOR NVIDIA TEGRA
|
||||
M: Dmitry Osipenko <digetx@gmail.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
L: linux-tegra@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
|
||||
S: Maintained
|
||||
F: drivers/devfreq/tegra20-devfreq.c
|
||||
F: drivers/devfreq/tegra30-devfreq.c
|
||||
|
||||
MEMORY MANAGEMENT
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
L: linux-mm@kvack.org
|
||||
|
|
|
@ -293,7 +293,7 @@ static void devfreq_event_release_edev(struct device *dev)
|
|||
/**
|
||||
* devfreq_event_add_edev() - Add new devfreq-event device.
|
||||
* @dev : the device owning the devfreq-event device being created
|
||||
* @desc : the devfreq-event device's decriptor which include essential
|
||||
* @desc : the devfreq-event device's descriptor which include essential
|
||||
* data for devfreq-event device.
|
||||
*
|
||||
* Note that this function add new devfreq-event device to devfreq-event class
|
||||
|
@ -385,7 +385,7 @@ static void devm_devfreq_event_release(struct device *dev, void *res)
|
|||
/**
|
||||
* devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev()
|
||||
* @dev : the device owning the devfreq-event device being created
|
||||
* @desc : the devfreq-event device's decriptor which include essential
|
||||
* @desc : the devfreq-event device's descriptor which include essential
|
||||
* data for devfreq-event device.
|
||||
*
|
||||
* Note that this function manages automatically the memory of devfreq-event
|
||||
|
|
|
@ -49,6 +49,11 @@ static LIST_HEAD(devfreq_governor_list);
|
|||
static LIST_HEAD(devfreq_list);
|
||||
static DEFINE_MUTEX(devfreq_list_lock);
|
||||
|
||||
static const char timer_name[][DEVFREQ_NAME_LEN] = {
|
||||
[DEVFREQ_TIMER_DEFERRABLE] = { "deferrable" },
|
||||
[DEVFREQ_TIMER_DELAYED] = { "delayed" },
|
||||
};
|
||||
|
||||
/**
|
||||
* find_device_devfreq() - find devfreq struct using device pointer
|
||||
* @dev: device pointer used to lookup device devfreq.
|
||||
|
@ -454,7 +459,17 @@ void devfreq_monitor_start(struct devfreq *devfreq)
|
|||
if (devfreq->governor->interrupt_driven)
|
||||
return;
|
||||
|
||||
INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
|
||||
switch (devfreq->profile->timer) {
|
||||
case DEVFREQ_TIMER_DEFERRABLE:
|
||||
INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
|
||||
break;
|
||||
case DEVFREQ_TIMER_DELAYED:
|
||||
INIT_DELAYED_WORK(&devfreq->work, devfreq_monitor);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (devfreq->profile->polling_ms)
|
||||
queue_delayed_work(devfreq_wq, &devfreq->work,
|
||||
msecs_to_jiffies(devfreq->profile->polling_ms));
|
||||
|
@ -771,6 +786,11 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
|||
devfreq->data = data;
|
||||
devfreq->nb.notifier_call = devfreq_notifier_call;
|
||||
|
||||
if (devfreq->profile->timer < 0
|
||||
|| devfreq->profile->timer >= DEVFREQ_TIMER_NUM) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!devfreq->profile->max_state && !devfreq->profile->freq_table) {
|
||||
mutex_unlock(&devfreq->lock);
|
||||
err = set_freq_table(devfreq);
|
||||
|
@ -1260,18 +1280,20 @@ EXPORT_SYMBOL(devfreq_remove_governor);
|
|||
static ssize_t name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct devfreq *devfreq = to_devfreq(dev);
|
||||
return sprintf(buf, "%s\n", dev_name(devfreq->dev.parent));
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
return sprintf(buf, "%s\n", dev_name(df->dev.parent));
|
||||
}
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
static ssize_t governor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (!to_devfreq(dev)->governor)
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
|
||||
if (!df->governor)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name);
|
||||
return sprintf(buf, "%s\n", df->governor->name);
|
||||
}
|
||||
|
||||
static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
|
||||
|
@ -1282,6 +1304,9 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
|
|||
char str_governor[DEVFREQ_NAME_LEN + 1];
|
||||
const struct devfreq_governor *governor, *prev_governor;
|
||||
|
||||
if (!df->governor)
|
||||
return -EINVAL;
|
||||
|
||||
ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
@ -1295,20 +1320,18 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
|
|||
if (df->governor == governor) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
} else if ((df->governor && df->governor->immutable) ||
|
||||
governor->immutable) {
|
||||
} else if (df->governor->immutable || governor->immutable) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (df->governor) {
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
goto out;
|
||||
}
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
prev_governor = df->governor;
|
||||
df->governor = governor;
|
||||
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
|
||||
|
@ -1343,13 +1366,16 @@ static ssize_t available_governors_show(struct device *d,
|
|||
struct devfreq *df = to_devfreq(d);
|
||||
ssize_t count = 0;
|
||||
|
||||
if (!df->governor)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
|
||||
/*
|
||||
* The devfreq with immutable governor (e.g., passive) shows
|
||||
* only own governor.
|
||||
*/
|
||||
if (df->governor && df->governor->immutable) {
|
||||
if (df->governor->immutable) {
|
||||
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
|
||||
"%s ", df->governor_name);
|
||||
/*
|
||||
|
@ -1383,27 +1409,37 @@ static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr,
|
|||
char *buf)
|
||||
{
|
||||
unsigned long freq;
|
||||
struct devfreq *devfreq = to_devfreq(dev);
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
|
||||
if (devfreq->profile->get_cur_freq &&
|
||||
!devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq))
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
if (df->profile->get_cur_freq &&
|
||||
!df->profile->get_cur_freq(df->dev.parent, &freq))
|
||||
return sprintf(buf, "%lu\n", freq);
|
||||
|
||||
return sprintf(buf, "%lu\n", devfreq->previous_freq);
|
||||
return sprintf(buf, "%lu\n", df->previous_freq);
|
||||
}
|
||||
static DEVICE_ATTR_RO(cur_freq);
|
||||
|
||||
static ssize_t target_freq_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%lu\n", to_devfreq(dev)->previous_freq);
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
|
||||
return sprintf(buf, "%lu\n", df->previous_freq);
|
||||
}
|
||||
static DEVICE_ATTR_RO(target_freq);
|
||||
|
||||
static ssize_t polling_interval_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", to_devfreq(dev)->profile->polling_ms);
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", df->profile->polling_ms);
|
||||
}
|
||||
|
||||
static ssize_t polling_interval_store(struct device *dev,
|
||||
|
@ -1531,6 +1567,9 @@ static ssize_t available_frequencies_show(struct device *d,
|
|||
ssize_t count = 0;
|
||||
int i;
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&df->lock);
|
||||
|
||||
for (i = 0; i < df->profile->max_state; i++)
|
||||
|
@ -1551,49 +1590,53 @@ static DEVICE_ATTR_RO(available_frequencies);
|
|||
static ssize_t trans_stat_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct devfreq *devfreq = to_devfreq(dev);
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
ssize_t len;
|
||||
int i, j;
|
||||
unsigned int max_state = devfreq->profile->max_state;
|
||||
unsigned int max_state;
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
max_state = df->profile->max_state;
|
||||
|
||||
if (max_state == 0)
|
||||
return sprintf(buf, "Not Supported.\n");
|
||||
|
||||
mutex_lock(&devfreq->lock);
|
||||
if (!devfreq->stop_polling &&
|
||||
devfreq_update_status(devfreq, devfreq->previous_freq)) {
|
||||
mutex_unlock(&devfreq->lock);
|
||||
mutex_lock(&df->lock);
|
||||
if (!df->stop_polling &&
|
||||
devfreq_update_status(df, df->previous_freq)) {
|
||||
mutex_unlock(&df->lock);
|
||||
return 0;
|
||||
}
|
||||
mutex_unlock(&devfreq->lock);
|
||||
mutex_unlock(&df->lock);
|
||||
|
||||
len = sprintf(buf, " From : To\n");
|
||||
len += sprintf(buf + len, " :");
|
||||
for (i = 0; i < max_state; i++)
|
||||
len += sprintf(buf + len, "%10lu",
|
||||
devfreq->profile->freq_table[i]);
|
||||
df->profile->freq_table[i]);
|
||||
|
||||
len += sprintf(buf + len, " time(ms)\n");
|
||||
|
||||
for (i = 0; i < max_state; i++) {
|
||||
if (devfreq->profile->freq_table[i]
|
||||
== devfreq->previous_freq) {
|
||||
if (df->profile->freq_table[i]
|
||||
== df->previous_freq) {
|
||||
len += sprintf(buf + len, "*");
|
||||
} else {
|
||||
len += sprintf(buf + len, " ");
|
||||
}
|
||||
len += sprintf(buf + len, "%10lu:",
|
||||
devfreq->profile->freq_table[i]);
|
||||
df->profile->freq_table[i]);
|
||||
for (j = 0; j < max_state; j++)
|
||||
len += sprintf(buf + len, "%10u",
|
||||
devfreq->stats.trans_table[(i * max_state) + j]);
|
||||
df->stats.trans_table[(i * max_state) + j]);
|
||||
|
||||
len += sprintf(buf + len, "%10llu\n", (u64)
|
||||
jiffies64_to_msecs(devfreq->stats.time_in_state[i]));
|
||||
jiffies64_to_msecs(df->stats.time_in_state[i]));
|
||||
}
|
||||
|
||||
len += sprintf(buf + len, "Total transition : %u\n",
|
||||
devfreq->stats.total_trans);
|
||||
df->stats.total_trans);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -1604,6 +1647,9 @@ static ssize_t trans_stat_store(struct device *dev,
|
|||
struct devfreq *df = to_devfreq(dev);
|
||||
int err, value;
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
if (df->profile->max_state == 0)
|
||||
return count;
|
||||
|
||||
|
@ -1625,6 +1671,69 @@ static ssize_t trans_stat_store(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR_RW(trans_stat);
|
||||
|
||||
static ssize_t timer_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
|
||||
if (!df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%s\n", timer_name[df->profile->timer]);
|
||||
}
|
||||
|
||||
static ssize_t timer_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
char str_timer[DEVFREQ_NAME_LEN + 1];
|
||||
int timer = -1;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!df->governor || !df->profile)
|
||||
return -EINVAL;
|
||||
|
||||
ret = sscanf(buf, "%16s", str_timer);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < DEVFREQ_TIMER_NUM; i++) {
|
||||
if (!strncmp(timer_name[i], str_timer, DEVFREQ_NAME_LEN)) {
|
||||
timer = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timer < 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (df->profile->timer == timer) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&df->lock);
|
||||
df->profile->timer = timer;
|
||||
mutex_unlock(&df->lock);
|
||||
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
|
||||
if (ret)
|
||||
dev_warn(dev, "%s: Governor %s not started(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
out:
|
||||
return ret ? ret : count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(timer);
|
||||
|
||||
static struct attribute *devfreq_attrs[] = {
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_governor.attr,
|
||||
|
@ -1636,6 +1745,7 @@ static struct attribute *devfreq_attrs[] = {
|
|||
&dev_attr_min_freq.attr,
|
||||
&dev_attr_max_freq.attr,
|
||||
&dev_attr_trans_stat.attr,
|
||||
&dev_attr_timer.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(devfreq);
|
||||
|
@ -1657,8 +1767,7 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
|
|||
unsigned long cur_freq, min_freq, max_freq;
|
||||
unsigned int polling_ms;
|
||||
|
||||
seq_printf(s, "%-30s %-10s %-10s %-15s %10s %12s %12s %12s\n",
|
||||
"dev_name",
|
||||
seq_printf(s, "%-30s %-30s %-15s %10s %12s %12s %12s\n",
|
||||
"dev",
|
||||
"parent_dev",
|
||||
"governor",
|
||||
|
@ -1666,10 +1775,9 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
|
|||
"cur_freq_Hz",
|
||||
"min_freq_Hz",
|
||||
"max_freq_Hz");
|
||||
seq_printf(s, "%30s %10s %10s %15s %10s %12s %12s %12s\n",
|
||||
seq_printf(s, "%30s %30s %15s %10s %12s %12s %12s\n",
|
||||
"------------------------------",
|
||||
"------------------------------",
|
||||
"----------",
|
||||
"----------",
|
||||
"---------------",
|
||||
"----------",
|
||||
"------------",
|
||||
|
@ -1692,14 +1800,13 @@ static int devfreq_summary_show(struct seq_file *s, void *data)
|
|||
#endif
|
||||
|
||||
mutex_lock(&devfreq->lock);
|
||||
cur_freq = devfreq->previous_freq,
|
||||
cur_freq = devfreq->previous_freq;
|
||||
get_freq_range(devfreq, &min_freq, &max_freq);
|
||||
polling_ms = devfreq->profile->polling_ms,
|
||||
polling_ms = devfreq->profile->polling_ms;
|
||||
mutex_unlock(&devfreq->lock);
|
||||
|
||||
seq_printf(s,
|
||||
"%-30s %-10s %-10s %-15s %10d %12ld %12ld %12ld\n",
|
||||
dev_name(devfreq->dev.parent),
|
||||
"%-30s %-30s %-15s %10d %12ld %12ld %12ld\n",
|
||||
dev_name(&devfreq->dev),
|
||||
p_devfreq ? dev_name(&p_devfreq->dev) : "null",
|
||||
devfreq->governor_name,
|
||||
|
|
|
@ -95,18 +95,20 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
|
|||
|
||||
mutex_lock(&dmcfreq->lock);
|
||||
|
||||
if (target_rate >= dmcfreq->odt_dis_freq)
|
||||
odt_enable = true;
|
||||
if (dmcfreq->regmap_pmu) {
|
||||
if (target_rate >= dmcfreq->odt_dis_freq)
|
||||
odt_enable = true;
|
||||
|
||||
/*
|
||||
* This makes a SMC call to the TF-A to set the DDR PD (power-down)
|
||||
* timings and to enable or disable the ODT (on-die termination)
|
||||
* resistors.
|
||||
*/
|
||||
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, dmcfreq->odt_pd_arg0,
|
||||
dmcfreq->odt_pd_arg1,
|
||||
ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD,
|
||||
odt_enable, 0, 0, 0, &res);
|
||||
/*
|
||||
* This makes a SMC call to the TF-A to set the DDR PD
|
||||
* (power-down) timings and to enable or disable the
|
||||
* ODT (on-die termination) resistors.
|
||||
*/
|
||||
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, dmcfreq->odt_pd_arg0,
|
||||
dmcfreq->odt_pd_arg1,
|
||||
ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD,
|
||||
odt_enable, 0, 0, 0, &res);
|
||||
}
|
||||
|
||||
/*
|
||||
* If frequency scaling from low to high, adjust voltage first.
|
||||
|
@ -371,13 +373,14 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
node = of_parse_phandle(np, "rockchip,pmu", 0);
|
||||
if (node) {
|
||||
data->regmap_pmu = syscon_node_to_regmap(node);
|
||||
of_node_put(node);
|
||||
if (IS_ERR(data->regmap_pmu)) {
|
||||
ret = PTR_ERR(data->regmap_pmu);
|
||||
goto err_edev;
|
||||
}
|
||||
if (!node)
|
||||
goto no_pmu;
|
||||
|
||||
data->regmap_pmu = syscon_node_to_regmap(node);
|
||||
of_node_put(node);
|
||||
if (IS_ERR(data->regmap_pmu)) {
|
||||
ret = PTR_ERR(data->regmap_pmu);
|
||||
goto err_edev;
|
||||
}
|
||||
|
||||
regmap_read(data->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
|
||||
|
@ -399,6 +402,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
|
|||
goto err_edev;
|
||||
};
|
||||
|
||||
no_pmu:
|
||||
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
|
||||
ROCKCHIP_SIP_CONFIG_DRAM_INIT,
|
||||
0, 0, 0, 0, &res);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -21,6 +22,10 @@
|
|||
#include "../jedec_ddr.h"
|
||||
#include "../of_memory.h"
|
||||
|
||||
static int irqmode;
|
||||
module_param(irqmode, int, 0644);
|
||||
MODULE_PARM_DESC(irqmode, "Enable IRQ mode (0=off [default], 1=on)");
|
||||
|
||||
#define EXYNOS5_DREXI_TIMINGAREF (0x0030)
|
||||
#define EXYNOS5_DREXI_TIMINGROW0 (0x0034)
|
||||
#define EXYNOS5_DREXI_TIMINGDATA0 (0x0038)
|
||||
|
@ -945,6 +950,7 @@ static int exynos5_dmc_get_cur_freq(struct device *dev, unsigned long *freq)
|
|||
* It provides to the devfreq framework needed functions and polling period.
|
||||
*/
|
||||
static struct devfreq_dev_profile exynos5_dmc_df_profile = {
|
||||
.timer = DEVFREQ_TIMER_DELAYED,
|
||||
.target = exynos5_dmc_target,
|
||||
.get_dev_status = exynos5_dmc_get_status,
|
||||
.get_cur_freq = exynos5_dmc_get_cur_freq,
|
||||
|
@ -1427,7 +1433,7 @@ static int exynos5_dmc_probe(struct platform_device *pdev)
|
|||
/* There is two modes in which the driver works: polling or IRQ */
|
||||
irq[0] = platform_get_irq_byname(pdev, "drex_0");
|
||||
irq[1] = platform_get_irq_byname(pdev, "drex_1");
|
||||
if (irq[0] > 0 && irq[1] > 0) {
|
||||
if (irq[0] > 0 && irq[1] > 0 && irqmode) {
|
||||
ret = devm_request_threaded_irq(dev, irq[0], NULL,
|
||||
dmc_irq_thread, IRQF_ONESHOT,
|
||||
dev_name(dev), dmc);
|
||||
|
@ -1465,10 +1471,10 @@ static int exynos5_dmc_probe(struct platform_device *pdev)
|
|||
* Setup default thresholds for the devfreq governor.
|
||||
* The values are chosen based on experiments.
|
||||
*/
|
||||
dmc->gov_data.upthreshold = 30;
|
||||
dmc->gov_data.upthreshold = 10;
|
||||
dmc->gov_data.downdifferential = 5;
|
||||
|
||||
exynos5_dmc_df_profile.polling_ms = 500;
|
||||
exynos5_dmc_df_profile.polling_ms = 100;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1484,7 +1490,7 @@ static int exynos5_dmc_probe(struct platform_device *pdev)
|
|||
if (dmc->in_irq_mode)
|
||||
exynos5_dmc_start_perf_events(dmc, PERF_COUNTER_START_VALUE);
|
||||
|
||||
dev_info(dev, "DMC initialized\n");
|
||||
dev_info(dev, "DMC initialized, in irq mode: %d\n", dmc->in_irq_mode);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -31,6 +31,13 @@
|
|||
#define DEVFREQ_PRECHANGE (0)
|
||||
#define DEVFREQ_POSTCHANGE (1)
|
||||
|
||||
/* DEVFREQ work timers */
|
||||
enum devfreq_timer {
|
||||
DEVFREQ_TIMER_DEFERRABLE = 0,
|
||||
DEVFREQ_TIMER_DELAYED,
|
||||
DEVFREQ_TIMER_NUM,
|
||||
};
|
||||
|
||||
struct devfreq;
|
||||
struct devfreq_governor;
|
||||
|
||||
|
@ -70,6 +77,7 @@ struct devfreq_dev_status {
|
|||
* @initial_freq: The operating frequency when devfreq_add_device() is
|
||||
* called.
|
||||
* @polling_ms: The polling interval in ms. 0 disables polling.
|
||||
* @timer: Timer type is either deferrable or delayed timer.
|
||||
* @target: The device should set its operating frequency at
|
||||
* freq or lowest-upper-than-freq value. If freq is
|
||||
* higher than any operable frequency, set maximum.
|
||||
|
@ -96,6 +104,7 @@ struct devfreq_dev_status {
|
|||
struct devfreq_dev_profile {
|
||||
unsigned long initial_freq;
|
||||
unsigned int polling_ms;
|
||||
enum devfreq_timer timer;
|
||||
|
||||
int (*target)(struct device *dev, unsigned long *freq, u32 flags);
|
||||
int (*get_dev_status)(struct device *dev,
|
||||
|
|
Loading…
Reference in New Issue