diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2020-07-30 18:52:15 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2020-07-30 18:52:15 +0200 |
commit | a7ee88c3d3d19d90d41a2f4e49bb0637cc927572 (patch) | |
tree | ab6734597165a15c521fa283ff71d2baec3eca01 | |
parent | 92ed301919932f777713b9172e525674157e983d (diff) | |
parent | 27a69714450f5c9288cec2b20f1ae4f7ad34dacf (diff) |
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
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-devfreq | 12 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt | 2 | ||||
-rw-r--r-- | MAINTAINERS | 9 | ||||
-rw-r--r-- | drivers/devfreq/devfreq-event.c | 4 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 195 | ||||
-rw-r--r-- | drivers/devfreq/rk3399_dmc.c | 42 | ||||
-rw-r--r-- | drivers/memory/samsung/exynos5422-dmc.c | 14 | ||||
-rw-r--r-- | include/linux/devfreq.h | 9 |
8 files changed, 218 insertions, 69 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 9758eb85ade3..deefffb3bbe4 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -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 diff --git a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt index 0ec68141f85a..a10d1f6d85c6 100644 --- a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt +++ b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt @@ -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: diff --git a/MAINTAINERS b/MAINTAINERS index f0569cf304ca..85d7fb9f04c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index 8c31b0f2e28f..56efbeb7851e 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c @@ -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 diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 52b9c3e141f3..561d91b2d3bf 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -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, diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index 24f04f78285b..027769e39f9b 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -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; - - /* - * 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 (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); + } /* * 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); diff --git a/drivers/memory/samsung/exynos5422-dmc.c b/drivers/memory/samsung/exynos5422-dmc.c index 25196d6268e2..53bfe6b7b703 100644 --- a/drivers/memory/samsung/exynos5422-dmc.c +++ b/drivers/memory/samsung/exynos5422-dmc.c @@ -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; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 57e871a559a9..12782fbb4c25 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -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, |