From patchwork Mon Apr 20 06:48:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: gao yunxiao X-Patchwork-Id: 11498143 X-Patchwork-Delegate: daniel.lezcano@linaro.org Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0296D14B4 for ; Mon, 20 Apr 2020 06:48:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DA5B82072B for ; Mon, 20 Apr 2020 06:48:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Ke27Vcc/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725896AbgDTGst (ORCPT ); Mon, 20 Apr 2020 02:48:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34512 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1725773AbgDTGst (ORCPT ); Mon, 20 Apr 2020 02:48:49 -0400 Received: from mail-pj1-x1043.google.com (mail-pj1-x1043.google.com [IPv6:2607:f8b0:4864:20::1043]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F281AC061A0C for ; Sun, 19 Apr 2020 23:48:48 -0700 (PDT) Received: by mail-pj1-x1043.google.com with SMTP id hi11so3453888pjb.3 for ; Sun, 19 Apr 2020 23:48:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=DMUHPHgNK4YCFCzeh+evq9wW6FTpDRZskhPvZiOmbB8=; b=Ke27Vcc/CWWaiv800arg0Eg6ABZAksH3nQqwAcZgCiebZbNffWnnQJETywVrlrOJEH 5NaJWLUocwEi6gMNckb1zCHFIXFU2ZM3PwMUGpxmE7xjTANu2XSYC8++CkpZnkW3cGd4 Zur0dxDejfCf4CTc32DX+WC9ai488uhMLhY9sPoy2glhkdyaZaW3o47XIETbKbIlFiEF R9axhHOX7vW5EkSGQ/wOR9+XiuvGr828cykKsEC+v30IQG/Fqh2aZhwmQSMSZ4hL7Cpa ZonXdmdwaNe2Qwlo4LZtEKrogNeeL3+ZV1JJITRtiA/1FWqYvVqohPlG4QViFo3UUktT qNVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=DMUHPHgNK4YCFCzeh+evq9wW6FTpDRZskhPvZiOmbB8=; b=XgShWObam9lPZKczNtG/7/A2tcYNpj18K5tFLoXH5z5t7LQ14hN7hFuVSSUy9ysz4t PANGtqx5cPc3Rg/GoO6bVWRwNJq00Hppz6bUHQLoNVTGwsVl7KgBwEO+m+JGrtMMwJ2K br/NYsN5sIqw16nX0Nc+LNxFFckFL7VWCaWUqoBn4YsGNc7eWdScefNCR+pwyt7RWU7c SgZhfnAa87tUvgwxwczSlH7URp0nTZRpmPYT2mPY5leyoGBLztV74UTejOgpoiJLx5oa Fe7dz5k/TmxcP04P6iQBNFpNuC3iERs3+Ngd+3KKw6QHzJRrzkKFtc2ysHJgRBP6Yvup 9Nrg== X-Gm-Message-State: AGi0Puayt1rMXMGzlBbVLpf9pxJXJSEWbxrVGKnJtk1HqrDx/F6GnshX PAONx3qGfaRVgiFPS1Kejao= X-Google-Smtp-Source: APiQypINPRLAgn23EsNm7Hfe5uCgruT2zqVdwGUPTVuEAWyAUUFdsVnSx11u2s1Cl3N8dtLFy5Ezwg== X-Received: by 2002:a17:902:6bc8:: with SMTP id m8mr15424930plt.104.1587365328426; Sun, 19 Apr 2020 23:48:48 -0700 (PDT) Received: from bj04432pcu2.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id w3sm128254pfn.115.2020.04.19.23.48.45 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 19 Apr 2020 23:48:48 -0700 (PDT) From: gao.yunxiao6@gmail.com To: daniel.lezcano@linaro.org, viresh.kumar@linaro.org, amit.kachhap@gmail.com, javi.merino@kernel.org Cc: linux-pm@vger.kernel.org, kernel-team@android.com, orsonzhai@gmail.com, zhang.lyra@gmail.com, Jeson Gao Subject: [PATCH 1/2] thermal/drivers/cpufreq_cooling: Add platform callback functions Date: Mon, 20 Apr 2020 14:48:39 +0800 Message-Id: <1587365320-25222-1-git-send-email-gao.yunxiao6@gmail.com> X-Mailer: git-send-email 1.9.1 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Jeson Gao On some platforms, due to the high power consumption, thermal frequency reduction policy cannot control the desired temperature, platform have to use the hotplug mechanism to mitigate temperature rising,so adding the platform callback to support this function. platform will hotplug out CPU when the normalised power is lower than the power corresponding to the minimum frequency limit that is set by platform. Signed-off-by: Jeson Gao --- drivers/thermal/cpufreq_cooling.c | 52 +++++++++++++++++++++++++++++++++++++++ include/linux/cpu_cooling.h | 30 ++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c index e297e13..16cbf58 100644 --- a/drivers/thermal/cpufreq_cooling.c +++ b/drivers/thermal/cpufreq_cooling.c @@ -64,6 +64,7 @@ struct time_in_idle { * @node: list_head to link all cpufreq_cooling_device together. * @idle_time: idle time stats * @qos_req: PM QoS contraint to apply + * @plat_ops: point to platform callback function. * * This structure is required for keeping information of each registered * cpufreq_cooling_device. @@ -78,6 +79,7 @@ struct cpufreq_cooling_device { struct list_head node; struct time_in_idle *idle_time; struct freq_qos_request qos_req; + struct cpufreq_cooling_plat_ops *plat_ops; }; static DEFINE_IDA(cpufreq_ida); @@ -313,12 +315,24 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev, u32 last_load, normalised_power; struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; struct cpufreq_policy *policy = cpufreq_cdev->policy; + struct cpufreq_cooling_plat_ops *plat_ops = cpufreq_cdev->plat_ops; last_load = cpufreq_cdev->last_load ?: 1; normalised_power = (power * 100) / last_load; target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power); *state = get_level(cpufreq_cdev, target_freq); + if (*state == cpufreq_cdev->max_level && + plat_ops && plat_ops->cpufreq_plat_min_freq_limit) { + plat_ops->cpufreq_plat_min_freq_limit(policy, &target_freq); + *state = get_level(cpufreq_cdev, target_freq); + } + + if (plat_ops && plat_ops->cpufreq_plat_cpu_ctrl) + plat_ops->cpufreq_plat_cpu_ctrl(policy, + last_load, normalised_power, + cpu_freq_to_power(cpufreq_cdev, target_freq)); + trace_thermal_power_cpu_limit(policy->related_cpus, target_freq, *state, power); return 0; @@ -684,3 +698,41 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) kfree(cpufreq_cdev); } EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); + +/** + * cpufreq_cooling_plat_ops_register - register platform callback function. + * @cdev: thermal cooling device pointer. + * @plat_ops: platform callback function pointer. + */ +int cpufreq_cooling_plat_ops_register(struct thermal_cooling_device *cdev, + struct cpufreq_cooling_plat_ops *plat_ops) +{ + struct cpufreq_cooling_device *cpufreq_cdev; + + if (!cdev && !cdev->devdata && !plat_ops) + return -EINVAL; + + cpufreq_cdev = cdev->devdata; + cpufreq_cdev->plat_ops = plat_ops; + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_cooling_plat_ops_register); + +/** + * cpufreq_cooling_plat_ops_unregister - unregister platform callback function. + * @cdev: thermal cooling device pointer. + */ +int cpufreq_cooling_plat_ops_unregister(struct thermal_cooling_device *cdev) +{ + struct cpufreq_cooling_device *cpufreq_cdev; + + if (!cdev && !cdev->devdata) + return -EINVAL; + + cpufreq_cdev = cdev->devdata; + cpufreq_cdev->plat_ops = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_cooling_plat_ops_unregister); diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index 65501d8..3934918 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -19,6 +19,23 @@ struct cpufreq_policy; +/** + * struct cpufreq_cooling_plat_ops - platfom cpu cooling policy ops + * + * @cpufreq_plat_cpu_ctrl: this function provides a further core control + * policy when the current policies cannot cool down to an expected + * temperature value. + * + * @cpufreq_plat_min_freq_limit: set cpu frequency limit, cooling devices + * are not allowed to adjust cpu frequency to out of that limit. + */ +struct cpufreq_cooling_plat_ops { + int (*cpufreq_plat_cpu_ctrl)(struct cpufreq_policy *policy, + u32 load, u32 normalised_power, u32 freq_power); + void (*cpufreq_plat_min_freq_limit)(struct cpufreq_policy *policy, + u32 *target_freq); +}; + #ifdef CONFIG_CPU_FREQ_THERMAL /** * cpufreq_cooling_register - function to create cpufreq cooling device. @@ -40,6 +57,19 @@ struct thermal_cooling_device * struct thermal_cooling_device * of_cpufreq_cooling_register(struct cpufreq_policy *policy); +/** + * cpufreq_cooling_plat_ops_register - register platform callback function. + * @cdev: thermal cooling device pointer. + * @plat_ops: platform callback function pointer. + */ +int cpufreq_cooling_plat_ops_register(struct thermal_cooling_device *cdev, + struct cpufreq_cooling_plat_ops *plat_ops); +/** + * cpufreq_cooling_plat_ops_unregister - unregister platform callback function. + * @cdev: thermal cooling device pointer. + */ +int cpufreq_cooling_plat_ops_unregister(struct thermal_cooling_device *cdev); + #else /* !CONFIG_CPU_FREQ_THERMAL */ static inline struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy) From patchwork Mon Apr 20 06:48:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: gao yunxiao X-Patchwork-Id: 11498147 X-Patchwork-Delegate: daniel.lezcano@linaro.org Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8F98614DD for ; Mon, 20 Apr 2020 06:49:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6E1A920857 for ; Mon, 20 Apr 2020 06:49:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mAYD8qoT" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725991AbgDTGtH (ORCPT ); Mon, 20 Apr 2020 02:49:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34558 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1725773AbgDTGtG (ORCPT ); Mon, 20 Apr 2020 02:49:06 -0400 Received: from mail-pj1-x1041.google.com (mail-pj1-x1041.google.com [IPv6:2607:f8b0:4864:20::1041]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 87714C061A0C for ; Sun, 19 Apr 2020 23:49:06 -0700 (PDT) Received: by mail-pj1-x1041.google.com with SMTP id 7so4237246pjo.0 for ; Sun, 19 Apr 2020 23:49:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=HQGG6DqR+aQd7lfrY0B6TjSFS6DgE7uSY3kCKd6Vxw0=; b=mAYD8qoTqJx7bIt5lj2G8pyg65R0duAgNL+faC+g1DOEJ4Nv7aiTsf9XxbGif4+BKB 5sE759wfqZ3clrMdsd8xQQ6LJRJcFX8J4dnSuP/N//gQ4FwJ4Uc9NgfiexMphTat8mcy A/o6WN8BKKHeDw2wUDD+m+vFcKwr/KgNUu0B1oteWb+RGnX+tpzA7+moNUA9EA8i1GUC PkX9Ni2Yn7qwuah7N2DMnbg10j1TLIwBKVGN3E3vaRPoVAREHFCHVH4TwOScBQNmEwHt Ww3Nl3kEBojgc5uCBsWMHBgPhrXx2jLfM4ItuwGm0hZPKuB6S3S2ZsxGdS4x+iotsMSX iVZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=HQGG6DqR+aQd7lfrY0B6TjSFS6DgE7uSY3kCKd6Vxw0=; b=mEb4vaY4wrC5pfAFr5CnruagnciFnhRYqPV7L5z4cdRVOtKpu1wz/tRPoehMSYYp79 UkyLKOH2whWZJHDufe3ZOfKTJ23DgVQ6/sLk8wznlozElGh9AgiLo9Kv1LpZIyUGm22E egQR4FH+4AJakJ3qAoCxylBc7ZNnVMCG+VtPSDhzMBYlK8bLoEQgQw4v6pm8Z4p8Fnr5 eQoHDRAS24SueOjy73oeO4AHz7RztJzkAuEG/RVBO6UbLA2ndEauModsFfqGCS+wSG1l r5oVa/VPI+NazG+Se5NBU4p6IKgee4wiKSv3viyYY5T8hBsTkcN0KtDcctbSrXF35R1k uHZg== X-Gm-Message-State: AGi0PuakcRmL1sYaUKy3FpsrQgpAOnFy86kptKac39qMKOz+UR/LeckX pNMqOuBh/4nbOESS0vjSndYbBCbF X-Google-Smtp-Source: APiQypIM1wiylcfIARw1gNogdZkv4xMX06t02gkeNP79wQp3z/dYWNVE0jmX74u/gH3j/cn6zBg38Q== X-Received: by 2002:a17:90a:6548:: with SMTP id f8mr19410313pjs.114.1587365346073; Sun, 19 Apr 2020 23:49:06 -0700 (PDT) Received: from bj04432pcu2.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id w3sm128254pfn.115.2020.04.19.23.49.03 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 19 Apr 2020 23:49:05 -0700 (PDT) From: gao.yunxiao6@gmail.com To: daniel.lezcano@linaro.org, viresh.kumar@linaro.org, amit.kachhap@gmail.com, javi.merino@kernel.org Cc: linux-pm@vger.kernel.org, kernel-team@android.com, orsonzhai@gmail.com, zhang.lyra@gmail.com, Jeson Gao Subject: [PATCH 2/2] thermal/drivers/sprd_cpu_cooling: Add platform mitigation thermal driver Date: Mon, 20 Apr 2020 14:48:40 +0800 Message-Id: <1587365320-25222-2-git-send-email-gao.yunxiao6@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1587365320-25222-1-git-send-email-gao.yunxiao6@gmail.com> References: <1587365320-25222-1-git-send-email-gao.yunxiao6@gmail.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Jeson Gao This driver provides a further core ctrl policy to reduce temperature for unisoc platform. CPU unplug or isolation usually would be used for core ctrl policy. This dirver is showing an example of using core ctrl policy. Signed-off-by: Jeson Gao --- drivers/thermal/Kconfig | 8 + drivers/thermal/Makefile | 1 + drivers/thermal/sprd_cpu_cooling.c | 340 +++++++++++++++++++++++++++++++++++++ 3 files changed, 349 insertions(+) create mode 100644 drivers/thermal/sprd_cpu_cooling.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 91af271..41a57b0 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -490,4 +490,12 @@ config SPRD_THERMAL help Support for the Spreadtrum thermal sensor driver in the Linux thermal framework. + +config SPRD_CPU_COOLING + tristate "sprd cpu cooling support" + depends on CPU_FREQ_THERMAL + help + This implements the sprd cpu cooling mechanism to mitigate temperature + rising. + endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 8c8ed7b..4031842 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o +obj-$(CONFIG_SPRD_CPU_COOLING) += sprd_cpu_cooling.o diff --git a/drivers/thermal/sprd_cpu_cooling.c b/drivers/thermal/sprd_cpu_cooling.c new file mode 100644 index 0000000..2af2714 --- /dev/null +++ b/drivers/thermal/sprd_cpu_cooling.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2020 Spreadtrum Communications Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct sprd_cooling_cluster { + int id; + int cpu; + int cpus; + u32 min_freq; + struct cpufreq_policy policy; +}; + +struct sprd_cooling_data { + int nr_clusters; + struct sprd_cooling_cluster *cluster; +}; + +static void sprd_update_target_cpus(struct cpufreq_policy *policy, + u32 target_cpus) +{ + int ret, cpu, first; + u32 curr_online_cpus, ncpus; + + ncpus = cpumask_weight(policy->related_cpus); + curr_online_cpus = cpumask_weight(policy->cpus); + first = cpumask_first(policy->related_cpus); + + if (target_cpus > curr_online_cpus) { + cpu = first; + for_each_cpu(cpu, policy->related_cpus) { + if (curr_online_cpus == target_cpus) + break; + if ((target_cpus > curr_online_cpus) && + !cpu_online(cpu)) { + ret = add_cpu(cpu); + if (!ret && cpu_online(cpu)) + curr_online_cpus++; + } + } + } else if (target_cpus < curr_online_cpus) { + for (cpu = (first + ncpus - 1); cpu >= first; cpu--) { + if (curr_online_cpus == target_cpus) + break; + if ((target_cpus < curr_online_cpus) && + cpu_online(cpu)) { + ret = remove_cpu(cpu); + if (!ret && !cpu_online(cpu)) + curr_online_cpus--; + } + } + } +} + +static void sprd_estimate_down_cpus(struct cpufreq_policy *policy, + u32 load, u32 normalised_power, + u32 freq_power) +{ + int i = 0; + u32 target_cpus, online_cpus; + u32 avg_load, estimate_power = 0; + + if (normalised_power) { + online_cpus = cpumask_weight(policy->cpus); + if (!online_cpus) + return; + + avg_load = (load / online_cpus) ?: 1; + do { + estimate_power += (freq_power * avg_load) / 100; + if (estimate_power > normalised_power) + break; + i++; + } while (i < online_cpus); + + target_cpus = max(i, 0); + } else + target_cpus = 0; + + sprd_update_target_cpus(policy, target_cpus); +} + +static void sprd_estimate_up_cpus(struct cpufreq_policy *policy, + u32 load, u32 normalised_power, + u32 freq_power) +{ + int i = 0, ncpus; + u32 target_cpus; + u32 avg_load, estimate_power = 0; + + ncpus = cpumask_weight(policy->related_cpus); + avg_load = (load / ncpus) ?: 1; + + do { + estimate_power += (freq_power * avg_load) / 100; + if (estimate_power > normalised_power) + break; + i++; + } while (i < ncpus); + + target_cpus = min(i, ncpus); + sprd_update_target_cpus(policy, target_cpus); +} + +static void sprd_keep_cpus(struct cpufreq_policy *policy) +{ + u32 online_cpus; + + online_cpus = cpumask_weight(policy->cpus); + sprd_update_target_cpus(policy, online_cpus); +} + +static int sprd_cpufreq_cpu_ctrl(struct cpufreq_policy *policy, + u32 load, u32 normalised_power, + u32 freq_power) +{ + unsigned int ncpus, online_cpus; + + ncpus = cpumask_weight(policy->related_cpus); + online_cpus = cpumask_weight(policy->cpus); + + if (normalised_power < freq_power) + sprd_estimate_down_cpus(policy, load, + normalised_power, freq_power); + else if (online_cpus < ncpus) + sprd_estimate_up_cpus(policy, load, + normalised_power, freq_power); + else + sprd_keep_cpus(policy); + + return 0; +} + +static void sprd_cpufreq_min_freq_limit(struct cpufreq_policy *policy, + u32 *target_freq) +{ + struct sprd_cooling_cluster *cluster = + container_of(policy, struct sprd_cooling_cluster, policy); + + if (*target_freq < cluster->min_freq) + *target_freq = cluster->min_freq; +} + +struct cpufreq_cooling_plat_ops plat_ops = { + .cpufreq_plat_cpu_ctrl = sprd_cpufreq_cpu_ctrl, + .cpufreq_plat_min_freq_limit = sprd_cpufreq_min_freq_limit, +}; + +static int sprd_get_cluster_counts(void) +{ + int cpu = 0, core_num = -1; + int cluster_num = 0; + + do { + core_num = cpumask_weight(topology_core_cpumask(cpu)); + if (core_num > 0) { + cpu = cpu + core_num; + cluster_num++; + } else + break; + } while (cpu > 0); + + return cluster_num; +} + +static int sprd_cpu_cooling_ops_register(struct platform_device *pdev) +{ + int id, ret = 0; + struct thermal_cooling_device *cdev; + struct sprd_cooling_cluster *cluster; + struct sprd_cooling_data *data = platform_get_drvdata(pdev); + + for (id = 0; id < data->nr_clusters; id++) { + cluster = &data->cluster[id]; + cdev = cluster->policy.cdev; + if (cdev) { + ret = cpufreq_cooling_plat_ops_register(cdev, + &plat_ops); + if (ret < 0) { + dev_err(&pdev->dev, + "CPU%d: failed to register platform function\n", + cluster->cpu); + break; + } + } else { + ret = -ENODEV; + dev_err(&pdev->dev, + "CPU%d: failed to get thermal device\n", + cluster->cpu); + break; + } + } + + return ret; +} + +static int sprd_cpu_cooling_ops_unregister(struct platform_device *pdev) +{ + int id, ret = 0; + struct thermal_cooling_device *cdev; + struct sprd_cooling_cluster *cluster; + struct sprd_cooling_data *data = platform_get_drvdata(pdev); + + for (id = 0; id < data->nr_clusters; id++) { + cluster = &data->cluster[id]; + cdev = cluster->policy.cdev; + if (cdev) { + ret = cpufreq_cooling_plat_ops_unregister(cdev); + if (ret < 0) { + dev_err(&pdev->dev, + "cpu%d: failed to unregister platform function\n", + cluster->cpu); + break; + } + } else { + dev_err(&pdev->dev, + "cpu%d: failed to get thermal device\n", + cluster->cpu); + ret = -ENODEV; + break; + } + } + + return ret; +} + +static int sprd_init_cooling_cluster(struct platform_device *pdev) +{ + int cpu = 0, id, core_num, ret = 0; + struct sprd_cooling_cluster *cluster; + struct sprd_cooling_data *data = platform_get_drvdata(pdev); + + do { + core_num = cpumask_weight(topology_core_cpumask(cpu)); + id = topology_physical_package_id((cpu)); + if (core_num > 0 && id >= 0) { + cluster = &data->cluster[id]; + cluster->id = id; + cluster->cpu = cpu; + cluster->cpus = core_num; + ret = cpufreq_get_policy(&(cluster->policy), cpu); + if (ret < 0) { + dev_err(&pdev->dev, + "CPU%d failed to get policy\n", + cpu); + break; + } + + cpu = cpu + core_num; + } else + break; + + } while (cpu > 0); + + return ret; +} + +static int sprd_cpu_cooling_probe(struct platform_device *pdev) +{ + int ret = -1; + int counts = 0; + struct sprd_cooling_data *data; + struct device *dev = &pdev->dev; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + counts = sprd_get_cluster_counts(); + data->nr_clusters = counts; + data->cluster = devm_kzalloc(dev, + sizeof(*data->cluster) * data->nr_clusters, + GFP_KERNEL); + if (!data->cluster) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + ret = sprd_init_cooling_cluster(pdev); + if (ret < 0) { + dev_err(dev, "Failed to init cooling cluster\n"); + return ret; + } + + ret = sprd_cpu_cooling_ops_register(pdev); + if (ret < 0) { + dev_err(dev, "Failed to register cooling callback function\n"); + return ret; + } + + return 0; +} + +static int sprd_cpu_cooling_remove(struct platform_device *pdev) +{ + int ret; + + ret = sprd_cpu_cooling_ops_unregister(pdev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to unregister cooling callback function\n"); + return ret; + } + + return 0; +} + +static struct platform_driver sprd_cpu_cooling_driver = { + .probe = sprd_cpu_cooling_probe, + .remove = sprd_cpu_cooling_remove, + .driver = { + .owner = THIS_MODULE, + .name = "sprd_cpu_cooling", + }, +}; +static int __init sprd_cpu_cooling_init(void) +{ + return platform_driver_register(&sprd_cpu_cooling_driver); +} + +static void __exit sprd_cpu_cooling_exit(void) +{ + platform_driver_unregister(&sprd_cpu_cooling_driver); +} + +module_init(sprd_cpu_cooling_init) +module_exit(sprd_cpu_cooling_exit); + +MODULE_DESCRIPTION("sprd cpu cooling"); +MODULE_LICENSE("GPL");