mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
caa8b668c5
PD#SWPL-99365 Problem: gki optimize Solution: 1, code optimize 2, watchdog_hld to ko Verify: sc2_ah212 Change-Id: Ibb1b16bd61d794defb39d53f17ad86179ba4a661 Signed-off-by: Hanjie Lin <hanjie.lin@amlogic.com>
280 lines
5.6 KiB
C
280 lines
5.6 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/amlogic/cpu_hotplug.h>
|
|
#include <linux/string.h>
|
|
#include <../kernel/sched/sched.h>
|
|
|
|
#define CPU_HOTPLUG_NONE 0
|
|
#define CPU_HOTPLUG_PLUG 1
|
|
#define CPU_HOTPLUG_UNPLUG 2
|
|
#define MAX_CLUSTERS 2
|
|
|
|
struct cpu_hotplug_s {
|
|
int clusters;
|
|
unsigned int flags[MAX_CLUSTERS];
|
|
unsigned int max_num[MAX_CLUSTERS];
|
|
cpumask_t cpumask[MAX_CLUSTERS];
|
|
unsigned int cpunum[MAX_CLUSTERS];
|
|
unsigned int min_num[MAX_CLUSTERS];
|
|
const char *compatible[MAX_CLUSTERS];
|
|
struct work_struct work;
|
|
struct mutex mutex; /* define a mutex */
|
|
};
|
|
|
|
static struct cpu_hotplug_s hpg;
|
|
struct rq;
|
|
|
|
static int meson_get_cluster_cores(int cpu)
|
|
{
|
|
struct device *cpu_dev;
|
|
struct device_node *np;
|
|
const char *tmp_compatible;
|
|
|
|
cpu_dev = get_cpu_device(cpu);
|
|
if (!cpu_dev)
|
|
return -1;
|
|
|
|
np = of_node_get(cpu_dev->of_node);
|
|
if (!np)
|
|
return -1;
|
|
|
|
if (!of_property_read_string(np, "compatible", &tmp_compatible)) {
|
|
if (!hpg.compatible[0]) {
|
|
hpg.compatible[0] = tmp_compatible;
|
|
return 0;
|
|
}
|
|
if (!strcmp(hpg.compatible[0], tmp_compatible))
|
|
return 0;
|
|
if (!hpg.compatible[1]) {
|
|
hpg.compatible[1] = tmp_compatible;
|
|
return 1;
|
|
}
|
|
if (!strcmp(hpg.compatible[1], tmp_compatible))
|
|
return 1;
|
|
pr_err("fail to determine %s belongs to which cluster\n",
|
|
tmp_compatible);
|
|
return -1;
|
|
} else {
|
|
pr_err("fail to get %s compatible value\n",
|
|
np->full_name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int cpu_hotplug_cpumask_init(void)
|
|
{
|
|
int cpu, cluster;
|
|
|
|
hpg.clusters = 0;
|
|
for (cluster = 0; cluster < MAX_CLUSTERS; cluster++) {
|
|
hpg.cpunum[cluster] = 0;
|
|
cpumask_clear(&hpg.cpumask[cluster]);
|
|
}
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
cluster = meson_get_cluster_cores(cpu);
|
|
if (cluster < 0)
|
|
continue;
|
|
cpumask_set_cpu(cpu, &hpg.cpumask[cluster]);
|
|
if (hpg.clusters < cluster + 1)
|
|
hpg.clusters = cluster + 1;
|
|
hpg.cpunum[cluster]++;
|
|
}
|
|
|
|
for (cluster = 0; cluster < MAX_CLUSTERS; cluster++) {
|
|
hpg.max_num[cluster] = hpg.cpunum[cluster];
|
|
hpg.flags[cluster] = CPU_HOTPLUG_NONE;
|
|
hpg.min_num[cluster] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cpu_num_online(int cluster)
|
|
{
|
|
cpumask_t mask;
|
|
|
|
if (cluster >= hpg.clusters)
|
|
return 0;
|
|
cpumask_and(&mask, &hpg.cpumask[cluster], cpu_online_mask);
|
|
return cpumask_weight(&mask);
|
|
}
|
|
|
|
void cpufreq_set_max_cpu_num(unsigned int num, unsigned int cluster)
|
|
{
|
|
unsigned int cpu_online;
|
|
|
|
if ((!num && !cluster) || cluster > hpg.clusters)
|
|
return;
|
|
|
|
mutex_lock(&hpg.mutex);
|
|
if (num > hpg.cpunum[cluster])
|
|
num = hpg.cpunum[cluster];
|
|
|
|
if (hpg.max_num[cluster] == num) {
|
|
mutex_unlock(&hpg.mutex);
|
|
return;
|
|
}
|
|
|
|
cpu_online = cpu_num_online(cluster);
|
|
hpg.max_num[cluster] = num;
|
|
if (num < cpu_online) {
|
|
hpg.flags[cluster] = CPU_HOTPLUG_UNPLUG;
|
|
schedule_work(&hpg.work);
|
|
} else if (num > cpu_online) {
|
|
hpg.flags[cluster] = CPU_HOTPLUG_PLUG;
|
|
schedule_work(&hpg.work);
|
|
}
|
|
|
|
mutex_unlock(&hpg.mutex);
|
|
}
|
|
EXPORT_SYMBOL(cpufreq_set_max_cpu_num);
|
|
|
|
static int find_online_cpu(unsigned int cluster)
|
|
{
|
|
int cpu;
|
|
|
|
for_each_cpu_and(cpu, &hpg.cpumask[cluster], cpu_online_mask) {
|
|
if (!cluster && cpu == cpumask_first(&hpg.cpumask[cluster]))
|
|
continue;
|
|
|
|
return cpu;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void set_hotplug_online(unsigned int cluster)
|
|
{
|
|
unsigned int cpu, online;
|
|
|
|
for_each_cpu(cpu, &hpg.cpumask[cluster]) {
|
|
if (cpu_online(cpu))
|
|
continue;
|
|
|
|
online = cpu_num_online(cluster);
|
|
if (online >= hpg.max_num[cluster])
|
|
break;
|
|
|
|
add_cpu(cpu);
|
|
}
|
|
}
|
|
|
|
static void set_hotplug_offline(unsigned int cluster)
|
|
{
|
|
unsigned int target, online, min, max;
|
|
|
|
online = cpu_num_online(cluster);
|
|
min = hpg.min_num[cluster];
|
|
max = hpg.max_num[cluster];
|
|
|
|
while (online > min && (online > max)) {
|
|
target = find_online_cpu(cluster);
|
|
if (target == -1)
|
|
break;
|
|
|
|
remove_cpu(target);
|
|
|
|
online = cpu_num_online(cluster);
|
|
min = hpg.min_num[cluster];
|
|
max = hpg.max_num[cluster];
|
|
}
|
|
}
|
|
|
|
static void hotplug_work(struct work_struct *work)
|
|
{
|
|
unsigned int cluster, flg;
|
|
|
|
mutex_lock(&hpg.mutex);
|
|
for (cluster = 0; cluster < hpg.clusters; cluster++) {
|
|
if (!hpg.flags[cluster])
|
|
continue;
|
|
|
|
flg = hpg.flags[cluster];
|
|
hpg.flags[cluster] = 0;
|
|
|
|
switch (flg) {
|
|
case CPU_HOTPLUG_PLUG:
|
|
set_hotplug_online(cluster);
|
|
break;
|
|
case CPU_HOTPLUG_UNPLUG:
|
|
set_hotplug_offline(cluster);
|
|
break;
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&hpg.mutex);
|
|
}
|
|
|
|
static ssize_t hotplug_max_cpus_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
unsigned int max = 0;
|
|
unsigned int c = 0;
|
|
|
|
for (c = 0; c < hpg.clusters; c++)
|
|
max |= hpg.max_num[c] << (c * 8);
|
|
return sprintf(buf, "0x%04x\n", max);
|
|
}
|
|
|
|
static ssize_t hotplug_max_cpus_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned int input;
|
|
unsigned int max;
|
|
unsigned int c = 0;
|
|
int ret;
|
|
|
|
ret = kstrtouint(buf, 0, &input);
|
|
if (ret) {
|
|
dev_err(NULL, " %s <%d>\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
for (c = 0; c < hpg.clusters; c++) {
|
|
max = input & 0xff;
|
|
if (max)
|
|
cpufreq_set_max_cpu_num(max, c);
|
|
input = input >> 8;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(hotplug_max_cpus);
|
|
|
|
int cpu_hotplug_init(void)
|
|
{
|
|
int err;
|
|
|
|
mutex_init(&hpg.mutex);
|
|
cpu_hotplug_cpumask_init();
|
|
INIT_WORK(&hpg.work, hotplug_work);
|
|
|
|
err = sysfs_create_file(&cpu_subsys.dev_root->kobj,
|
|
&dev_attr_hotplug_max_cpus.attr);
|
|
if (err) {
|
|
dev_err(NULL, " %s <%d>\n", __func__, err);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int get_cpunum_by_cluster(int cluster)
|
|
{
|
|
if (!cluster || cluster == 1)
|
|
return hpg.cpunum[cluster];
|
|
|
|
pr_err("Invalid cluster argument\n");
|
|
return -1;
|
|
}
|