unplugged-kernel/drivers/misc/mediatek/base/power/cpuhotplug/mtk_cpuhp_ppm.c

179 lines
3.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#define pr_fmt(fmt) "cpuhp: " fmt
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <mtk_ppm_api.h>
#include <linux/nmi.h>
#include "mtk_cpuhp_private.h"
static struct cpumask ppm_online_cpus;
static struct task_struct *ppm_kthread;
static DEFINE_MUTEX(ppm_mutex);
#ifdef CONFIG_PM_SLEEP
static struct wakeup_source *hps_ws;
#endif
#ifdef CONFIG_ARM
#define CPU_DOWN cpu_down
#define CPU_UP cpu_up
#else
#define CPU_DOWN(i) \
(get_cpu_device(i) == NULL ? false \
: device_offline(get_cpu_device(i)))
#define CPU_UP(i) \
(get_cpu_device(i) == NULL ? false \
: device_online(get_cpu_device(i)))
#endif
#define HPS_RETRY 10
static int ppm_thread_fn(void *data)
{
int request_cpu_up;
int i;
int rc;
struct cpumask ppm_cpus_req;
while (!kthread_should_stop()) {
if (cpumask_equal(&ppm_online_cpus, cpu_online_mask)) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
continue;
}
set_current_state(TASK_RUNNING);
cpumask_copy(&ppm_cpus_req, &ppm_online_cpus);
#ifdef CONFIG_PM_SLEEP
if (hps_ws)
__pm_stay_awake(hps_ws);
#endif
pr_debug_ratelimited("%s: ppm_cpus_req: %*pbl cpu_online_mask: %*pbl\n"
, __func__
, cpumask_pr_args(&ppm_cpus_req)
, cpumask_pr_args(cpu_online_mask));
/* process the request of up each CPUs from PPM */
for_each_possible_cpu(i) {
request_cpu_up = cpumask_test_cpu(i, &ppm_cpus_req);
if (request_cpu_up && cpu_is_offline(i)) {
int retry = 0;
pr_debug_ratelimited("CPU%d: ppm-request=%d, offline->powerup\n",
i, request_cpu_up);
Retry_ON:
rc = CPU_UP(i);
if (rc) {
if (retry > HPS_RETRY) {
pr_debug_ratelimited(
"fail to bringup cpu(%d) rc: %d\n"
, i, rc);
trigger_all_cpu_backtrace();
continue;
}
retry++;
goto Retry_ON;
}
continue;
}
}
/* process the request of down each CPUs from PPM */
for_each_possible_cpu(i) {
request_cpu_up = cpumask_test_cpu(i, &ppm_cpus_req);
if (!request_cpu_up && cpu_online(i)) {
int retry = 0;
pr_debug_ratelimited("CPU%d: ppm-request=%d, online->powerdown\n",
i, request_cpu_up);
Retry_OFF:
rc = CPU_DOWN(i);
if (rc) {
if (retry > HPS_RETRY) {
pr_debug_ratelimited(
"fail to shutdown cpu(%d) rc: %d\n"
, i, rc);
trigger_all_cpu_backtrace();
continue;
}
retry++;
goto Retry_OFF;
}
continue;
}
}
#ifdef CONFIG_PM_SLEEP
if (hps_ws)
__pm_relax(hps_ws);
#endif
}
return 0;
}
static void ppm_limit_callback(struct ppm_client_req req)
{
mutex_lock(&ppm_mutex);
cpumask_copy(&ppm_online_cpus, &req.online_core[0]);
mutex_unlock(&ppm_mutex);
wake_up_process(ppm_kthread);
}
void ppm_notifier(void)
{
unsigned int cpu;
struct device_node *dn = 0;
const char *smp_method = 0;
cpumask_copy(&ppm_online_cpus, cpu_online_mask);
for_each_present_cpu(cpu) {
dn = of_get_cpu_node(cpu, NULL);
smp_method = of_get_property(dn, "smp-method", NULL);
if (smp_method != NULL) {
if (!strcmp("disabled", smp_method)) {
pr_info("[ENTER Hotplug DEBUG MODE!!!]\n");
return;
}
}
}
/* create a kthread to serve the requests from PPM */
ppm_kthread = kthread_create(ppm_thread_fn, NULL, "cpuhp-ppm");
if (IS_ERR(ppm_kthread)) {
pr_notice("error creating ppm kthread (%ld)\n",
PTR_ERR(ppm_kthread));
return;
}
/* register PPM callback */
mt_ppm_register_client(PPM_CLIENT_HOTPLUG, &ppm_limit_callback);
hps_ws = wakeup_source_register(NULL, "hps");
if (!hps_ws)
pr_debug("hps wakelock register fail!\n");
}