Re: [PATCH RFC 1/2] PM / devfreq: Generic CPU frequency to device frequency mapping governor
From: Chanwoo Choi
Date: Wed Jun 26 2019 - 04:10:03 EST
Hello Sibi and Hsin-Yi,
On 19. 6. 20. ìí 6:41, Sibi Sankar wrote:
> Hey Hsin-Yi, Chanwoo
>
> On 2019-06-20 15:02, Hsin-Yi Wang wrote:
>> Hi Chanwoo Choi, Saravana Kannan and Sibi Sankar,
>>
>> I've also tested Sibi Sankar's patch[1] locally with mt8183-cci, and
>> it works fine too!
>> It'd be great if Sibi Sankar or anyone who is familiar with the
>> original design can finish this implementation. But if no one has time
>> to do that, I think I can also help on address the comments. Thanks!
>
> Now that we have a user :) I am happy
> to repost the patch with the comments
> addressed.
>
> https://lkml.org/lkml/2019/6/14/4
> Also with ^^ patch and few more in the
> series the dt parsing of required-opps
> should get further simplified.
Even if patch[1] suggested by Saravana is merged,
the patch[2] is necessary. Because, until now,
the child devfreq device cannot catch the timing
of CPU frequency without CPUFREQ notification.
The existing passive governor only supports between
devfreq device and other devfreq device.
[1] https://lkml.org/lkml/2019/6/14/4
[2]
[PATCH RFC 0/9] Add CPU based scaling support to Passive governor
- https://lore.kernel.org/lkml/08c3cff8c39e3d82e044db93e992da72@xxxxxxxxxxxxxx/T/
[PATCH RFC 3/9] PM / devfreq: Add cpu based scaling support to passive_governor
- https://lore.kernel.org/lkml/08c3cff8c39e3d82e044db93e992da72@xxxxxxxxxxxxxx/T/#m1cafb7baf687d2a680d39c85d3ec7d1b590b68fc
>
>>
>>
>> [1]
>> [RFC,2/9] OPP: Export a number of helpers to prevent code duplication
>> - https://patchwork.kernel.org/patch/10875199/
>> [RFC,3/9] PM / devfreq: Add cpu based scaling support to passive_governor
>> - https://patchwork.kernel.org/patch/10875195/
>>
>> Hsin-Yi
>>
>> On Thu, Jun 20, 2019 at 2:56 PM Chanwoo Choi <cw00.choi@xxxxxxxxxxx> wrote:
>>>
>>> + Sibi Sankar
>>>
>>> Hi, Hsin-Yi Wang, Saravana Kannan and Sibi Sankar
>>>
>>> I summarized the history of the related patch about this title.
>>>
>>> Firstly,
>>> As I knew, Saravana sent the patch[1] which contains
>>> 'governor_cpufreq_map.c' last year. According to the Myungoo's comment,
>>>
>>> Secondly,
>>> Sibi Sankar modified the 'governor_passive.c'[2] in order to support
>>> the mapping between cpu frequency and device frequency.
>>> Unfortunately, Sibi Sankar stopped the development about this
>>> because he had found the other method to get his purpose as I knew.
>>>
>>> Thirdly,
>>> Hsin-Yi Wang send the original patch of Saravana without modification.
>>>
>>>
>>> Sincerely, I think that the mapping between cpu frequency and device
>>> frequency is necessary. And I prefer the Sibi's approach which implements
>>> stuff to the existing 'passive' governor.
>>>
>>> We need to discuss about how to implement them by whom.
>>>
>>>
>>> [1] [v3,1/2] PM / devfreq: Generic CPU frequency to device frequency mapping governor
>>> - https://patchwork.kernel.org/patch/10553171/
>>>
>>> [2]
>>> [PATCH RFC 0/9] Add CPU based scaling support to Passive governor
>>> - https://lore.kernel.org/lkml/08c3cff8c39e3d82e044db93e992da72@xxxxxxxxxxxxxx/T/
>>> [PATCH RFC 3/9] PM / devfreq: Add cpu based scaling support to passive_governor
>>> - https://lore.kernel.org/lkml/08c3cff8c39e3d82e044db93e992da72@xxxxxxxxxxxxxx/T/#m1cafb7baf687d2a680d39c85d3ec7d1b590b68fc
>>>
>>>
>>> Best Regards,
>>> Chanwoo Choi
>>>
>>> On 19. 6. 18. ìí 1:14, Hsin-Yi Wang wrote:
>>> > From: Saravana Kannan <skannan@xxxxxxxxxxxxxx>
>>> >
>>> > From: Saravana Kannan <skannan@xxxxxxxxxxxxxx>
>>> >
>>> > Many CPU architectures have caches that can scale independent of the CPUs.
>>> > Frequency scaling of the caches is necessary to make sure the cache is not
>>> > a performance bottleneck that leads to poor performance and power. The same
>>> > idea applies for RAM/DDR.
>>> >
>>> > To achieve this, this patch adds a generic devfreq governor that takes the
>>> > current frequency of each CPU frequency domain and then adjusts the
>>> > frequency of the cache (or any devfreq device) based on the frequency of
>>> > the CPUs. It listens to CPU frequency transition notifiers to keep itself
>>> > up to date on the current CPU frequency.
>>> >
>>> > To decide the frequency of the device, the governor does one of the
>>> > following:
>>> >
>>> > * Uses a CPU frequency to device frequency mapping table
>>> >ÂÂ - Either one mapping table used for all CPU freq policies (typically used
>>> >ÂÂÂÂ for system with homogeneous cores/clusters that have the same OPPs).
>>> >ÂÂ - One mapping table per CPU freq policy (typically used for ASMP systems
>>> >ÂÂÂÂ with heterogeneous CPUs with different OPPs)
>>> >
>>> > OR
>>> >
>>> > * Scales the device frequency in proportion to the CPU frequency. So, if
>>> >ÂÂ the CPUs are running at their max frequency, the device runs at its max
>>> > frequency. If the CPUs are running at their min frequency, the device
>>> >ÂÂ runs at its min frequency. And interpolated for frequencies in between.
>>> >
>>> > Signed-off-by: Saravana Kannan <skannan@xxxxxxxxxxxxxx>
>>> > Signed-off-by: Hsin-Yi Wang <hsinyi@xxxxxxxxxxxx>
>>> > ---
>>> > .../bindings/devfreq/devfreq-cpufreq-map.txt | 53 ++
>>> >Â drivers/devfreq/KconfigÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ |ÂÂ 8 +
>>> >Â drivers/devfreq/MakefileÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ |ÂÂ 1 +
>>> >Â drivers/devfreq/governor_cpufreq_map.cÂÂÂÂÂÂÂ | 583 ++++++++++++++++++
>>> >Â 4 files changed, 645 insertions(+)
>>> >Â create mode 100644 Documentation/devicetree/bindings/devfreq/devfreq-cpufreq-map.txt
>>> >Â create mode 100644 drivers/devfreq/governor_cpufreq_map.c
>>> >
>>> > diff --git a/Documentation/devicetree/bindings/devfreq/devfreq-cpufreq-map.txt b/Documentation/devicetree/bindings/devfreq/devfreq-cpufreq-map.txt
>>> > new file mode 100644
>>> > index 000000000000..982a30bcfc86
>>> > --- /dev/null
>>> > +++ b/Documentation/devicetree/bindings/devfreq/devfreq-cpufreq-map.txt
>>> > @@ -0,0 +1,53 @@
>>> > +Devfreq CPUfreq governor
>>> > +
>>> > +devfreq-cpufreq-map is a parent device that contains one or more child devices.
>>> > +Each child device provides CPU frequency to device frequency mapping for a
>>> > +specific device. Examples of devices that could use this are: DDR, cache and
>>> > +CCI.
>>> > +
>>> > +Parent device name shall be "devfreq-cpufreq-map".
>>> > +
>>> > +Required child device properties:
>>> > +- cpu-to-dev-map, or cpu-to-dev-map-<X>:
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ A list of tuples where each tuple consists of a
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ CPU frequency (KHz) and the corresponding device
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ frequency. CPU frequencies not listed in the table
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ will use the device frequency that corresponds to the
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ next rounded up CPU frequency.
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ Use "cpu-to-dev-map" if all CPUs in the system should
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ share same mapping.
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ Use cpu-to-dev-map-<cpuid> to describe different
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mappings for different CPUs. The property should be
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ listed only for the first CPU if multiple CPUs are
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ synchronous.
>>> > +- target-dev:ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ Phandle to device that this mapping applies to.
>>> > +
>>> > +Example:
>>> > +ÂÂÂÂ devfreq-cpufreq-map {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cpubw-cpufreq {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ target-dev = <&cpubw>;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cpu-to-dev-map =
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 300000Â 1144000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 422400Â 2288000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 652800Â 3051000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 883200Â 5996000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 1190400Â 8056000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 1497600 10101000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 1728000 12145000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 2649600 16250000 >;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ };
>>> > +
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cache-cpufreq {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ target-dev = <&cache>;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cpu-to-dev-map =
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 300000Â 300000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 422400Â 422400 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 652800Â 499200 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 883200Â 576000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ <Â 960000Â 960000 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 1497600 1036800 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 1574400 1574400 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 1728000 1651200 >,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ < 2649600 1728000 >;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ };
>>> > +ÂÂÂÂ };
>>> > diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
>>> > index 0c8204d6b78a..0303f5a400b6 100644
>>> > --- a/drivers/devfreq/Kconfig
>>> > +++ b/drivers/devfreq/Kconfig
>>> > @@ -74,6 +74,14 @@ config DEVFREQ_GOV_PASSIVE
>>> >ÂÂÂÂÂÂÂÂ through sysfs entries. The passive governor recommends that
>>> >ÂÂÂÂÂÂÂÂ devfreq device uses the OPP table to get the frequency/voltage.
>>> >
>>> > +config DEVFREQ_GOV_CPUFREQ_MAP
>>> > +ÂÂÂÂ tristate "CPUfreq Map"
>>> > +ÂÂÂÂ depends on CPU_FREQ
>>> > +ÂÂÂÂ help
>>> > +ÂÂÂÂÂÂ Chooses frequency based on the online CPUs' current frequency and a
>>> > +ÂÂÂÂÂÂ CPU frequency to device frequency mapping table(s). This governor
>>> > +ÂÂÂÂÂÂ can be useful for controlling devices such as DDR, cache, CCI, etc.
>>> > +
>>> >Â comment "DEVFREQ Drivers"
>>> >
>>> >Â config ARM_EXYNOS_BUS_DEVFREQ
>>> > diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
>>> > index 817dde779f16..81141e2c784f 100644
>>> > --- a/drivers/devfreq/Makefile
>>> > +++ b/drivers/devfreq/Makefile
>>> > @@ -6,6 +6,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
>>> >Â obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE)Â += governor_powersave.o
>>> >Â obj-$(CONFIG_DEVFREQ_GOV_USERSPACE)Â += governor_userspace.o
>>> >Â obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)ÂÂÂ += governor_passive.o
>>> > +obj-$(CONFIG_DEVFREQ_GOV_CPUFREQ_MAP)ÂÂÂÂÂÂÂ += governor_cpufreq_map.o
>>> >
>>> >Â # DEVFREQ Drivers
>>> >Â obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
>>> > diff --git a/drivers/devfreq/governor_cpufreq_map.c b/drivers/devfreq/governor_cpufreq_map.c
>>> > new file mode 100644
>>> > index 000000000000..084a3ffb8f54
>>> > --- /dev/null
>>> > +++ b/drivers/devfreq/governor_cpufreq_map.c
>>> > @@ -0,0 +1,583 @@
>>> > +// SPDX-License-Identifier: GPL-2.0
>>> > +/*
>>> > + * Copyright (c) 2014-2015, 2018, The Linux Foundation. All rights reserved.
>>> > + */
>>> > +
>>> > +#define pr_fmt(fmt) "dev-cpufreq-map: " fmt
>>> > +
>>> > +#include <linux/devfreq.h>
>>> > +#include <linux/cpu.h>
>>> > +#include <linux/cpufreq.h>
>>> > +#include <linux/cpumask.h>
>>> > +#include <linux/slab.h>
>>> > +#include <linux/platform_device.h>
>>> > +#include <linux/of.h>
>>> > +#include <linux/module.h>
>>> > +#include "governor.h"
>>> > +
>>> > +struct cpu_state {
>>> > +ÂÂÂÂ unsigned int freq;
>>> > +ÂÂÂÂ unsigned int min_freq;
>>> > +ÂÂÂÂ unsigned int max_freq;
>>> > +ÂÂÂÂ unsigned int first_cpu;
>>> > +};
>>> > +static struct cpu_state *state[NR_CPUS];
>>> > +static int cpufreq_cnt;
>>> > +
>>> > +struct freq_map {
>>> > +ÂÂÂÂ unsigned int cpu_khz;
>>> > +ÂÂÂÂ unsigned int target_freq;
>>> > +};
>>> > +
>>> > +struct devfreq_node {
>>> > +ÂÂÂÂ struct devfreq *df;
>>> > +ÂÂÂÂ void *orig_data;
>>> > +ÂÂÂÂ struct device *dev;
>>> > +ÂÂÂÂ struct device_node *of_node;
>>> > +ÂÂÂÂ struct list_head list;
>>> > +ÂÂÂÂ struct freq_map **map;
>>> > +ÂÂÂÂ struct freq_map *common_map;
>>> > +};
>>> > +static LIST_HEAD(devfreq_list);
>>> > +static DEFINE_MUTEX(state_lock);
>>> > +static DEFINE_MUTEX(cpufreq_reg_lock);
>>> > +
>>> > +static void update_all_devfreqs(void)
>>> > +{
>>> > +ÂÂÂÂ struct devfreq_node *node;
>>> > +
>>> > +ÂÂÂÂ list_for_each_entry(node, &devfreq_list, list) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ struct devfreq *df = node->df;
>>> > +
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (!node->df)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ continue;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ mutex_lock(&df->lock);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ update_devfreq(df);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ mutex_unlock(&df->lock);
>>> > +
>>> > +ÂÂÂÂ }
>>> > +}
>>> > +
>>> > +static struct devfreq_node *find_devfreq_node(struct device *dev)
>>> > +{
>>> > +ÂÂÂÂ struct devfreq_node *node;
>>> > +
>>> > +ÂÂÂÂ list_for_each_entry(node, &devfreq_list, list)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (node->dev == dev || node->of_node == dev->of_node)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ return node;
>>> > +
>>> > +ÂÂÂÂ return NULL;
>>> > +}
>>> > +
>>> > +/* ==================== cpufreq part ==================== */
>>> > +static struct cpu_state *add_policy(struct cpufreq_policy *policy)
>>> > +{
>>> > +ÂÂÂÂ struct cpu_state *new_state;
>>> > +ÂÂÂÂ unsigned int cpu, first_cpu;
>>> > +
>>> > +ÂÂÂÂ new_state = kzalloc(sizeof(struct cpu_state), GFP_KERNEL);
>>> > +ÂÂÂÂ if (!new_state)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return NULL;
>>> > +
>>> > +ÂÂÂÂ first_cpu = cpumask_first(policy->related_cpus);
>>> > +ÂÂÂÂ new_state->first_cpu = first_cpu;
>>> > +ÂÂÂÂ new_state->freq = policy->cur;
>>> > +ÂÂÂÂ new_state->min_freq = policy->cpuinfo.min_freq;
>>> > +ÂÂÂÂ new_state->max_freq = policy->cpuinfo.max_freq;
>>> > +
>>> > +ÂÂÂÂ for_each_cpu(cpu, policy->related_cpus)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ state[cpu] = new_state;
>>> > +
>>> > +ÂÂÂÂ return new_state;
>>> > +}
>>> > +
>>> > +static int cpufreq_trans_notifier(struct notifier_block *nb,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long event, void *data)
>>> > +{
>>> > +ÂÂÂÂ struct cpufreq_freqs *freq = data;
>>> > +ÂÂÂÂ struct cpu_state *s;
>>> > +ÂÂÂÂ struct cpufreq_policy *policy = NULL;
>>> > +
>>> > +ÂÂÂÂ if (event != CPUFREQ_POSTCHANGE)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return 0;
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&state_lock);
>>> > +
>>> > +ÂÂÂÂ s = state[freq->cpu];
>>> > +ÂÂÂÂ if (!s) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ policy = cpufreq_cpu_get(freq->cpu);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (policy) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ s = add_policy(policy);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cpufreq_cpu_put(policy);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ if (!s)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto out;
>>> > +
>>> > +ÂÂÂÂ if (s->freq != freq->new || policy) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ s->freq = freq->new;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ update_all_devfreqs();
>>> > +ÂÂÂÂ }
>>> > +
>>> > +out:
>>> > +ÂÂÂÂ mutex_unlock(&state_lock);
>>> > +ÂÂÂÂ return 0;
>>> > +}
>>> > +
>>> > +static struct notifier_block cpufreq_trans_nb = {
>>> > +ÂÂÂÂ .notifier_call = cpufreq_trans_notifier
>>> > +};
>>> > +
>>> > +static int register_cpufreq(void)
>>> > +{
>>> > +ÂÂÂÂ int ret = 0;
>>> > +ÂÂÂÂ unsigned int cpu;
>>> > +ÂÂÂÂ struct cpufreq_policy *policy;
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&cpufreq_reg_lock);
>>> > +
>>> > +ÂÂÂÂ if (cpufreq_cnt)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto cnt_not_zero;
>>> > +
>>> > +ÂÂÂÂ get_online_cpus();
>>> > +ÂÂÂÂ ret = cpufreq_register_notifier(&cpufreq_trans_nb,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ CPUFREQ_TRANSITION_NOTIFIER);
>>> > +ÂÂÂÂ if (ret)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto out;
>>> > +
>>> > +ÂÂÂÂ for_each_online_cpu(cpu) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ policy = cpufreq_cpu_get(cpu);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (policy) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ add_policy(policy);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cpufreq_cpu_put(policy);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂ }
>>> > +out:
>>> > +ÂÂÂÂ put_online_cpus();
>>> > +cnt_not_zero:
>>> > +ÂÂÂÂ if (!ret)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cpufreq_cnt++;
>>> > +ÂÂÂÂ mutex_unlock(&cpufreq_reg_lock);
>>> > +ÂÂÂÂ return ret;
>>> > +}
>>> > +
>>> > +static int unregister_cpufreq(void)
>>> > +{
>>> > +ÂÂÂÂ int ret = 0;
>>> > +ÂÂÂÂ int cpu;
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&cpufreq_reg_lock);
>>> > +
>>> > +ÂÂÂÂ if (cpufreq_cnt > 1)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto out;
>>> > +
>>> > +ÂÂÂÂ cpufreq_unregister_notifier(&cpufreq_trans_nb,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ CPUFREQ_TRANSITION_NOTIFIER);
>>> > +
>>> > +ÂÂÂÂ for (cpu = ARRAY_SIZE(state) - 1; cpu >= 0; cpu--) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (!state[cpu])
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ continue;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (state[cpu]->first_cpu == cpu)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ kfree(state[cpu]);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ state[cpu] = NULL;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +out:
>>> > +ÂÂÂÂ cpufreq_cnt--;
>>> > +ÂÂÂÂ mutex_unlock(&cpufreq_reg_lock);
>>> > +ÂÂÂÂ return ret;
>>> > +}
>>> > +
>>> > +/* ==================== devfreq part ==================== */
>>> > +
>>> > +static unsigned int interpolate_freq(struct devfreq *df, unsigned int cpu)
>>> > +{
>>> > +ÂÂÂÂ unsigned long *freq_table = df->profile->freq_table;
>>> > +ÂÂÂÂ unsigned int cpu_min = state[cpu]->min_freq;
>>> > +ÂÂÂÂ unsigned int cpu_max = state[cpu]->max_freq;
>>> > +ÂÂÂÂ unsigned int cpu_freq = state[cpu]->freq;
>>> > +ÂÂÂÂ unsigned int dev_min, dev_max, cpu_percent;
>>> > +
>>> > +ÂÂÂÂ if (freq_table) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ dev_min = freq_table[0];
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ dev_max = freq_table[df->profile->max_state - 1];
>>> > +ÂÂÂÂ } else {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (df->max_freq <= df->min_freq)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ return 0;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ dev_min = df->min_freq;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ dev_max = df->max_freq;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ cpu_percent = ((cpu_freq - cpu_min) * 100) / (cpu_max - cpu_min);
>>> > +ÂÂÂÂ return dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
>>> > +}
>>> > +
>>> > +static unsigned int cpu_to_dev_freq(struct devfreq *df, unsigned int cpu)
>>> > +{
>>> > +ÂÂÂÂ struct freq_map *map = NULL;
>>> > +ÂÂÂÂ unsigned int cpu_khz = 0, freq;
>>> > +ÂÂÂÂ struct devfreq_node *n = df->data;
>>> > +
>>> > +ÂÂÂÂ if (!state[cpu] || state[cpu]->first_cpu != cpu) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ freq = 0;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto out;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ if (n->common_map)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ map = n->common_map;
>>> > +ÂÂÂÂ else if (n->map)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ map = n->map[cpu];
>>> > +
>>> > +ÂÂÂÂ cpu_khz = state[cpu]->freq;
>>> > +
>>> > +ÂÂÂÂ if (!map) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ freq = interpolate_freq(df, cpu);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto out;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ while (map->cpu_khz && map->cpu_khz < cpu_khz)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ map++;
>>> > +ÂÂÂÂ if (!map->cpu_khz)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ map--;
>>> > +ÂÂÂÂ freq = map->target_freq;
>>> > +
>>> > +out:
>>> > +ÂÂÂÂ dev_dbg(df->dev.parent, "CPU%u: %d -> dev: %u\n", cpu, cpu_khz, freq);
>>> > +ÂÂÂÂ return freq;
>>> > +}
>>> > +
>>> > +static int devfreq_cpufreq_get_freq(struct devfreq *df,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long *freq)
>>> > +{
>>> > +ÂÂÂÂ unsigned int cpu, tgt_freq = 0;
>>> > +ÂÂÂÂ struct devfreq_node *node;
>>> > +
>>> > +ÂÂÂÂ node = df->data;
>>> > +ÂÂÂÂ if (!node) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ pr_err("Unable to find devfreq node!\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return -ENODEV;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ for_each_possible_cpu(cpu)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ tgt_freq = max(tgt_freq, cpu_to_dev_freq(df, cpu));
>>> > +
>>> > +ÂÂÂÂ *freq = tgt_freq;
>>> > +ÂÂÂÂ return 0;
>>> > +}
>>> > +
>>> > +static unsigned int show_table(char *buf, unsigned int len,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct freq_map *map)
>>> > +{
>>> > +ÂÂÂÂ unsigned int cnt = 0;
>>> > +
>>> > +ÂÂÂÂ cnt += snprintf(buf + cnt, len - cnt, "CPU freq\tDevice freq\n");
>>> > +
>>> > +ÂÂÂÂ while (map->cpu_khz && cnt < len) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cnt += snprintf(buf + cnt, len - cnt, "%8u\t%11u\n",
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ map->cpu_khz, map->target_freq);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ map++;
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ if (cnt < len)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cnt += snprintf(buf + cnt, len - cnt, "\n");
>>> > +
>>> > +ÂÂÂÂ return cnt;
>>> > +}
>>> > +
>>> > +static ssize_t freq_map_show(struct device *dev, struct device_attribute *attr,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ char *buf)
>>> > +{
>>> > +ÂÂÂÂ struct devfreq *df = to_devfreq(dev);
>>> > +ÂÂÂÂ struct devfreq_node *n = df->data;
>>> > +ÂÂÂÂ struct freq_map *map;
>>> > +ÂÂÂÂ unsigned int cnt = 0, cpu;
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&state_lock);
>>> > +ÂÂÂÂ if (n->common_map) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ map = n->common_map;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Common table for all CPUs:\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cnt += show_table(buf + cnt, PAGE_SIZE - cnt, map);
>>> > +ÂÂÂÂ } else if (n->map) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ for_each_possible_cpu(cpu) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ map = n->map[cpu];
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ if (!map)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ continue;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "CPU %u:\n", cpu);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ if (cnt >= PAGE_SIZE)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cnt += show_table(buf + cnt, PAGE_SIZE - cnt, map);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ if (cnt >= PAGE_SIZE)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂ } else {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Device freq interpolated based on CPU freq\n");
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ mutex_unlock(&state_lock);
>>> > +
>>> > +ÂÂÂÂ return cnt;
>>> > +}
>>> > +
>>> > +static DEVICE_ATTR_RO(freq_map);
>>> > +static struct attribute *dev_attr[] = {
>>> > +ÂÂÂÂ &dev_attr_freq_map.attr,
>>> > +ÂÂÂÂ NULL,
>>> > +};
>>> > +
>>> > +static struct attribute_group dev_attr_group = {
>>> > +ÂÂÂÂ .name = "cpufreq-map",
>>> > +ÂÂÂÂ .attrs = dev_attr,
>>> > +};
>>> > +
>>> > +static int devfreq_cpufreq_gov_start(struct devfreq *devfreq)
>>> > +{
>>> > +ÂÂÂÂ int ret = 0;
>>> > +ÂÂÂÂ struct devfreq_node *node;
>>> > +ÂÂÂÂ bool alloc = false;
>>> > +
>>> > +ÂÂÂÂ ret = register_cpufreq();
>>> > +ÂÂÂÂ if (ret)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return ret;
>>> > +
>>> > +ÂÂÂÂ ret = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
>>> > +ÂÂÂÂ if (ret) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ unregister_cpufreq();
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return ret;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&state_lock);
>>> > +
>>> > +ÂÂÂÂ node = find_devfreq_node(devfreq->dev.parent);
>>> > +ÂÂÂÂ if (node == NULL) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ node = kzalloc(sizeof(struct devfreq_node), GFP_KERNEL);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (!node) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret = -ENOMEM;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ goto alloc_fail;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ alloc = true;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ node->dev = devfreq->dev.parent;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ list_add_tail(&node->list, &devfreq_list);
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ node->df = devfreq;
>>> > +ÂÂÂÂ node->orig_data = devfreq->data;
>>> > +ÂÂÂÂ devfreq->data = node;
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&devfreq->lock);
>>> > +ÂÂÂÂ ret = update_devfreq(devfreq);
>>> > +ÂÂÂÂ mutex_unlock(&devfreq->lock);
>>> > +ÂÂÂÂ if (ret) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ pr_err("Freq update failed!\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto update_fail;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ mutex_unlock(&state_lock);
>>> > +ÂÂÂÂ return 0;
>>> > +
>>> > +update_fail:
>>> > +ÂÂÂÂ devfreq->data = node->orig_data;
>>> > +ÂÂÂÂ if (alloc) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ list_del(&node->list);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ kfree(node);
>>> > +ÂÂÂÂ }
>>> > +alloc_fail:
>>> > +ÂÂÂÂ mutex_unlock(&state_lock);
>>> > +ÂÂÂÂ sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
>>> > +ÂÂÂÂ unregister_cpufreq();
>>> > +ÂÂÂÂ return ret;
>>> > +}
>>> > +
>>> > +static void devfreq_cpufreq_gov_stop(struct devfreq *devfreq)
>>> > +{
>>> > +ÂÂÂÂ struct devfreq_node *node = devfreq->data;
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&state_lock);
>>> > +ÂÂÂÂ devfreq->data = node->orig_data;
>>> > +ÂÂÂÂ if (node->map || node->common_map) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ node->df = NULL;
>>> > +ÂÂÂÂ } else {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ list_del(&node->list);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ kfree(node);
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ mutex_unlock(&state_lock);
>>> > +
>>> > +ÂÂÂÂ sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
>>> > +ÂÂÂÂ unregister_cpufreq();
>>> > +}
>>> > +
>>> > +static int devfreq_cpufreq_ev_handler(struct devfreq *devfreq,
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned int event, void *data)
>>> > +{
>>> > +ÂÂÂÂ int ret;
>>> > +
>>> > +ÂÂÂÂ switch (event) {
>>> > +ÂÂÂÂ case DEVFREQ_GOV_START:
>>> > +
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ ret = devfreq_cpufreq_gov_start(devfreq);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (ret) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ pr_err("Governor start failed!\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ return ret;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ pr_debug("Enabled CPUfreq-map governor\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ break;
>>> > +
>>> > +ÂÂÂÂ case DEVFREQ_GOV_STOP:
>>> > +
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ devfreq_cpufreq_gov_stop(devfreq);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ pr_debug("Disabled dev CPUfreq-map governor\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ break;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ return 0;
>>> > +}
>>> > +
>>> > +static struct devfreq_governor devfreq_cpufreq = {
>>> > +ÂÂÂÂ .name = "cpufreq-map",
>>> > +ÂÂÂÂ .get_target_freq = devfreq_cpufreq_get_freq,
>>> > +ÂÂÂÂ .event_handler = devfreq_cpufreq_ev_handler,
>>> > +};
>>> > +
>>> > +#define NUM_COLSÂÂÂÂ 2
>>> > +static struct freq_map *read_tbl(struct device_node *of_node, char *prop_name)
>>> > +{
>>> > +ÂÂÂÂ int len, nf, i, j;
>>> > +ÂÂÂÂ u32 data;
>>> > +ÂÂÂÂ struct freq_map *tbl;
>>> > +
>>> > +ÂÂÂÂ if (!of_find_property(of_node, prop_name, &len))
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return NULL;
>>> > +ÂÂÂÂ len /= sizeof(data);
>>> > +
>>> > +ÂÂÂÂ if (len % NUM_COLS || len == 0)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return NULL;
>>> > +ÂÂÂÂ nf = len / NUM_COLS;
>>> > +
>>> > +ÂÂÂÂ tbl = kzalloc((nf + 1) * sizeof(*tbl), GFP_KERNEL);
>>> > +ÂÂÂÂ if (!tbl)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return NULL;
>>> > +
>>> > +ÂÂÂÂ for (i = 0, j = 0; i < nf; i++, j += 2) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ of_property_read_u32_index(of_node, prop_name, j, &data);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ tbl[i].cpu_khz = data;
>>> > +
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ of_property_read_u32_index(of_node, prop_name, j + 1, &data);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ tbl[i].target_freq = data;
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ tbl[i].cpu_khz = 0;
>>> > +
>>> > +ÂÂÂÂ return tbl;
>>> > +}
>>> > +
>>> > +#define PROP_TARGET "target-dev"
>>> > +#define PROP_TABLE "cpu-to-dev-map"
>>> > +static int add_table_from_of(struct device_node *of_node)
>>> > +{
>>> > +ÂÂÂÂ struct device_node *target_of_node;
>>> > +ÂÂÂÂ struct devfreq_node *node;
>>> > +ÂÂÂÂ struct freq_map *common_tbl;
>>> > +ÂÂÂÂ struct freq_map **tbl_list = NULL;
>>> > +ÂÂÂÂ static char prop_name[] = PROP_TABLE "-999999";
>>> > +ÂÂÂÂ int cpu, ret, cnt = 0, prop_sz = ARRAY_SIZE(prop_name);
>>> > +
>>> > +ÂÂÂÂ target_of_node = of_parse_phandle(of_node, PROP_TARGET, 0);
>>> > +ÂÂÂÂ if (!target_of_node)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return -EINVAL;
>>> > +
>>> > +ÂÂÂÂ node = kzalloc(sizeof(struct devfreq_node), GFP_KERNEL);
>>> > +ÂÂÂÂ if (!node)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return -ENOMEM;
>>> > +
>>> > +ÂÂÂÂ common_tbl = read_tbl(of_node, PROP_TABLE);
>>> > +ÂÂÂÂ if (!common_tbl) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ tbl_list = kzalloc(sizeof(*tbl_list) * NR_CPUS, GFP_KERNEL);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ if (!tbl_list) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret = -ENOMEM;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ goto err_list;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ for_each_possible_cpu(cpu) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret = snprintf(prop_name, prop_sz, "%s-%d",
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ PROP_TABLE, cpu);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ if (ret >= prop_sz) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ pr_warn("More CPUs than I can handle!\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ pr_warn("Skipping rest of the tables!\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ tbl_list[cpu] = read_tbl(of_node, prop_name);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ if (tbl_list[cpu])
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cnt++;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ if (!common_tbl && !cnt) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ ret = -EINVAL;
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ goto err_tbl;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&state_lock);
>>> > +ÂÂÂÂ node->of_node = target_of_node;
>>> > +ÂÂÂÂ node->map = tbl_list;
>>> > +ÂÂÂÂ node->common_map = common_tbl;
>>> > +ÂÂÂÂ list_add_tail(&node->list, &devfreq_list);
>>> > +ÂÂÂÂ mutex_unlock(&state_lock);
>>> > +
>>> > +ÂÂÂÂ return 0;
>>> > +err_tbl:
>>> > +ÂÂÂÂ kfree(tbl_list);
>>> > +err_list:
>>> > +ÂÂÂÂ kfree(node);
>>> > +ÂÂÂÂ return ret;
>>> > +}
>>> > +
>>> > +static int __init devfreq_cpufreq_init(void)
>>> > +{
>>> > +ÂÂÂÂ int ret;
>>> > +ÂÂÂÂ struct device_node *of_par, *of_child;
>>> > +
>>> > +ÂÂÂÂ of_par = of_find_node_by_name(NULL, "devfreq-cpufreq-map");
>>> > +ÂÂÂÂ if (of_par) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ for_each_child_of_node(of_par, of_child) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret = add_table_from_of(of_child);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ if (ret)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ pr_err("Parsing %s failed!\n", of_child->name);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ else
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ pr_debug("Parsed %s.\n", of_child->name);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ }
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ of_node_put(of_par);
>>> > +ÂÂÂÂ } else {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ pr_info("No tables parsed from DT.\n");
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ ret = devfreq_add_governor(&devfreq_cpufreq);
>>> > +ÂÂÂÂ if (ret) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ pr_err("cpufreq-map governor add failed!\n");
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ return ret;
>>> > +ÂÂÂÂ }
>>> > +
>>> > +ÂÂÂÂ return 0;
>>> > +}
>>> > +subsys_initcall(devfreq_cpufreq_init);
>>> > +
>>> > +static void __exit devfreq_cpufreq_exit(void)
>>> > +{
>>> > +ÂÂÂÂ int ret, cpu;
>>> > +ÂÂÂÂ struct devfreq_node *node, *tmp;
>>> > +
>>> > +ÂÂÂÂ ret = devfreq_remove_governor(&devfreq_cpufreq);
>>> > +ÂÂÂÂ if (ret)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ pr_err("cpufreq-map governor remove failed!\n");
>>> > +
>>> > +ÂÂÂÂ mutex_lock(&state_lock);
>>> > +ÂÂÂÂ list_for_each_entry_safe(node, tmp, &devfreq_list, list) {
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ kfree(node->common_map);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ for_each_possible_cpu(cpu)
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ kfree(node->map[cpu]);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ kfree(node->map);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ list_del(&node->list);
>>> > +ÂÂÂÂÂÂÂÂÂÂÂÂ kfree(node);
>>> > +ÂÂÂÂ }
>>> > +ÂÂÂÂ mutex_unlock(&state_lock);
>>> > +}
>>> > +module_exit(devfreq_cpufreq_exit);
>>> > +
>>> > +MODULE_DESCRIPTION("devfreq gov that sets dev freq based on current CPU freq");
>>> > +MODULE_LICENSE("GPL v2");
>>> >
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics