-// SPDX-License-Identifier: GPL-2.0-only
+       // SPDX-License-Identifier: GPL-2.0-only
 /*
  * linux/drivers/devfreq/governor_passive.c
  *
 
 #define HZ_PER_KHZ     1000
 
+static struct devfreq_cpu_data *
+get_parent_cpu_data(struct devfreq_passive_data *p_data,
+                   struct cpufreq_policy *policy)
+{
+       struct devfreq_cpu_data *parent_cpu_data;
+
+       if (!p_data || !policy)
+               return NULL;
+
+       list_for_each_entry(parent_cpu_data, &p_data->cpu_data_list, node)
+               if (parent_cpu_data->first_cpu == cpumask_first(policy->related_cpus))
+                       return parent_cpu_data;
+
+       return NULL;
+}
+
 static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
                                                struct opp_table *p_opp_table,
                                                struct opp_table *opp_table,
        struct devfreq_passive_data *p_data =
                                (struct devfreq_passive_data *)devfreq->data;
        struct devfreq_cpu_data *parent_cpu_data;
+       struct cpufreq_policy *policy;
        unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent;
        unsigned long dev_min, dev_max;
        unsigned long freq = 0;
+       int ret = 0;
 
        for_each_online_cpu(cpu) {
-               parent_cpu_data = p_data->parent_cpu_data[cpu];
-               if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu)
+               policy = cpufreq_cpu_get(cpu);
+               if (!policy) {
+                       ret = -EINVAL;
+                       continue;
+               }
+
+               parent_cpu_data = get_parent_cpu_data(p_data, policy);
+               if (!parent_cpu_data) {
+                       cpufreq_cpu_put(policy);
                        continue;
+               }
 
                /* Get target freq via required opps */
                cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
                                        devfreq->opp_table, &cpu_cur);
                if (freq) {
                        *target_freq = max(freq, *target_freq);
+                       cpufreq_cpu_put(policy);
                        continue;
                }
 
                freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
 
                *target_freq = max(freq, *target_freq);
+               cpufreq_cpu_put(policy);
        }
 
-       return 0;
+       return ret;
 }
 
 static int get_target_freq_with_devfreq(struct devfreq *devfreq,
        unsigned int cur_freq;
        int ret;
 
-       if (event != CPUFREQ_POSTCHANGE || !freqs ||
-               !p_data->parent_cpu_data[freqs->policy->cpu])
+       if (event != CPUFREQ_POSTCHANGE || !freqs)
                return 0;
 
-       parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu];
-       if (parent_cpu_data->cur_freq == freqs->new)
+       parent_cpu_data = get_parent_cpu_data(p_data, freqs->policy);
+       if (!parent_cpu_data || parent_cpu_data->cur_freq == freqs->new)
                return 0;
 
        cur_freq = parent_cpu_data->cur_freq;
        struct devfreq_passive_data *p_data
                        = (struct devfreq_passive_data *)devfreq->data;
        struct devfreq_cpu_data *parent_cpu_data;
-       int cpu, ret;
+       int cpu, ret = 0;
 
        if (p_data->nb.notifier_call) {
                ret = cpufreq_unregister_notifier(&p_data->nb,
        }
 
        for_each_possible_cpu(cpu) {
-               parent_cpu_data = p_data->parent_cpu_data[cpu];
-               if (!parent_cpu_data)
+               struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+               if (!policy) {
+                       ret = -EINVAL;
                        continue;
+               }
 
+               parent_cpu_data = get_parent_cpu_data(p_data, policy);
+               if (!parent_cpu_data) {
+                       cpufreq_cpu_put(policy);
+                       continue;
+               }
+
+               list_del(&parent_cpu_data->node);
                if (parent_cpu_data->opp_table)
                        dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
                kfree(parent_cpu_data);
+               cpufreq_cpu_put(policy);
        }
 
-       return 0;
+       return ret;
 }
 
 static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
        unsigned int cpu;
        int ret;
 
+       p_data->cpu_data_list
+               = (struct list_head)LIST_HEAD_INIT(p_data->cpu_data_list);
+
        p_data->nb.notifier_call = cpufreq_passive_notifier_call;
        ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
        if (ret) {
        }
 
        for_each_possible_cpu(cpu) {
-               if (p_data->parent_cpu_data[cpu])
-                       continue;
-
                policy = cpufreq_cpu_get(cpu);
                if (!policy) {
                        ret = -EPROBE_DEFER;
                        goto err;
                }
 
+               parent_cpu_data = get_parent_cpu_data(p_data, policy);
+               if (parent_cpu_data) {
+                       cpufreq_cpu_put(policy);
+                       continue;
+               }
+
                parent_cpu_data = kzalloc(sizeof(*parent_cpu_data),
                                                GFP_KERNEL);
                if (!parent_cpu_data) {
                parent_cpu_data->min_freq = policy->cpuinfo.min_freq;
                parent_cpu_data->max_freq = policy->cpuinfo.max_freq;
 
-               p_data->parent_cpu_data[cpu] = parent_cpu_data;
+               list_add_tail(&parent_cpu_data->node, &p_data->cpu_data_list);
                cpufreq_cpu_put(policy);
        }