Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca26747c authored by Ram Chandrasekar's avatar Ram Chandrasekar Committed by Gerrit - the friendly Code Review server
Browse files

msm: thermal: Add new IOCTL commands to mitigate cluster based devices



Add new IOCTL command to mitigate the maximum and
minimum frequency of a synchronous cluster.

Add new command to get the clock frequency plan
for a cluster.

change-Id: I82cdcb3529ae9cce03c46cfddedc68ae56aaf9ea
Signed-off-by: default avatarRam Chandrasekar <rkumbako@codeaurora.org>
parent e1b6fe17
Loading
Loading
Loading
Loading
+100 −2
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ struct msm_thermal_ioctl_dev {
static int msm_thermal_major;
static struct class *thermal_class;
static struct msm_thermal_ioctl_dev *msm_thermal_dev;
static unsigned int freq_table_len[NR_CPUS], freq_table_set[NR_CPUS];
static unsigned int *freq_table_ptr[NR_CPUS];

static int msm_thermal_ioctl_open(struct inode *node, struct file *filep)
{
@@ -96,8 +98,6 @@ static long validate_and_copy(unsigned int *cmd, unsigned long *arg,
		}
		break;
	default:
		ret = -ENOTTY;
		goto validate_exit;
		break;
	}

@@ -105,6 +105,90 @@ validate_exit:
	return ret;
}

static long msm_thermal_process_freq_table_req(struct msm_thermal_ioctl *query,
		unsigned long *arg)
{
	long ret = 0;
	uint32_t table_idx, idx = 0, cluster_id = query->clock_freq.cluster_num;
	struct clock_plan_arg *clock_freq = &(query->clock_freq);

	if (!freq_table_len[cluster_id]) {
		ret = msm_thermal_get_freq_plan_size(cluster_id,
			&freq_table_len[cluster_id]);
		if (ret) {
			pr_err("%s: Cluster%d freq table length get err:%ld\n",
				KBUILD_MODNAME, cluster_id, ret);
			goto process_freq_exit;
		}
		if (!freq_table_len[cluster_id]) {
			pr_err("%s: Cluster%d freq table empty\n",
				KBUILD_MODNAME, cluster_id);
			ret = -EAGAIN;
			goto process_freq_exit;
		}

		freq_table_set[cluster_id] = freq_table_len[cluster_id]
						/ MSM_IOCTL_FREQ_SIZE;
		if (freq_table_len[cluster_id] % MSM_IOCTL_FREQ_SIZE)
			freq_table_set[cluster_id]++;

		if (!freq_table_ptr[cluster_id]) {
			freq_table_ptr[cluster_id] = kzalloc(
				sizeof(unsigned int) *
				freq_table_len[cluster_id], GFP_KERNEL);
			if (!freq_table_ptr[cluster_id]) {
				pr_err("%s: memory alloc failed\n",
						KBUILD_MODNAME);
				freq_table_len[cluster_id] = 0;
				ret = -ENOMEM;
				goto process_freq_exit;
			}
		}
		ret = msm_thermal_get_cluster_freq_plan(cluster_id,
			freq_table_ptr[cluster_id]);
		if (ret) {
			pr_err("%s: Error getting frequency table. err:%ld\n",
					KBUILD_MODNAME, ret);
			freq_table_len[cluster_id] = 0;
			freq_table_set[cluster_id] = 0;
			kfree(freq_table_ptr[cluster_id]);
			freq_table_ptr[cluster_id] = NULL;
			goto process_freq_exit;
		}
	}

	if (!clock_freq->freq_table_len) {
		clock_freq->freq_table_len = freq_table_len[cluster_id];
		goto copy_and_return;
	}
	if (clock_freq->set_idx >= freq_table_set[cluster_id]) {
		pr_err("%s: Invalid freq table set%d for cluster%d\n",
			KBUILD_MODNAME, clock_freq->set_idx,
			cluster_id);
		ret = -EINVAL;
		goto process_freq_exit;
	}

	table_idx = MSM_IOCTL_FREQ_SIZE * clock_freq->set_idx;
	for (; table_idx < freq_table_len[cluster_id]
		&& idx < MSM_IOCTL_FREQ_SIZE; idx++, table_idx++) {
		clock_freq->freq_table[idx] =
			freq_table_ptr[cluster_id][table_idx];
	}
	clock_freq->freq_table_len = idx;

copy_and_return:
	ret = copy_to_user((void __user *)(*arg), query,
		sizeof(struct msm_thermal_ioctl));
	if (ret) {
		pr_err("%s: copy_to_user error:%ld.\n", KBUILD_MODNAME, ret);
		goto process_freq_exit;
	}

process_freq_exit:
	return ret;
}

static long msm_thermal_ioctl_process(struct file *filep, unsigned int cmd,
	unsigned long arg)
{
@@ -126,6 +210,17 @@ static long msm_thermal_ioctl_process(struct file *filep, unsigned int cmd,
		ret = msm_thermal_set_frequency(query.cpu_freq.cpu_num,
			query.cpu_freq.freq_req, false);
		break;
	case MSM_THERMAL_SET_CLUSTER_MAX_FREQUENCY:
		ret = msm_thermal_set_cluster_freq(query.cpu_freq.cpu_num,
			query.cpu_freq.freq_req, true);
		break;
	case MSM_THERMAL_SET_CLUSTER_MIN_FREQUENCY:
		ret = msm_thermal_set_cluster_freq(query.cpu_freq.cpu_num,
			query.cpu_freq.freq_req, false);
		break;
	case MSM_THERMAL_GET_CLUSTER_FREQUENCY_PLAN:
		ret = msm_thermal_process_freq_table_req(&query, &arg);
		break;
	default:
		ret = -ENOTTY;
		goto process_exit;
@@ -218,6 +313,7 @@ ioctl_init_exit:

void msm_thermal_ioctl_cleanup()
{
	uint32_t idx = 0;
	dev_t thermal_dev = MKDEV(msm_thermal_major, 0);

	if (!msm_thermal_dev) {
@@ -226,6 +322,8 @@ void msm_thermal_ioctl_cleanup()
		return;
	}

	for (; idx < num_possible_cpus(); idx++)
		kfree(freq_table_ptr[idx]);
	device_destroy(thermal_class, thermal_dev);
	class_destroy(thermal_class);
	cdev_del(&msm_thermal_dev->char_dev);
+119 −0
Original line number Diff line number Diff line
@@ -2565,6 +2565,125 @@ init_freq_thread:
	}
}

int msm_thermal_get_freq_plan_size(uint32_t cluster, unsigned int *table_len)
{
	uint32_t i = 0;
	struct cluster_info *cluster_ptr = NULL;

	if (!core_ptr) {
		pr_err("Topology ptr not initialized\n");
		return -ENODEV;
	}
	if (!table_len) {
		pr_err("Invalid input\n");
		return -EINVAL;
	}
	if (!freq_table_get)
		check_freq_table();

	for (; i < core_ptr->entity_count; i++) {
		cluster_ptr = &core_ptr->child_entity_ptr[i];
		if (cluster_ptr->cluster_id == cluster) {
			if (!cluster_ptr->freq_table) {
				pr_err("Cluster%d clock plan not initialized\n",
						cluster);
				return -EINVAL;
			}
			*table_len = cluster_ptr->freq_idx_high + 1;
			return 0;
		}
	}

	pr_err("Invalid cluster ID:%d\n", cluster);
	return -EINVAL;
}

int msm_thermal_get_cluster_freq_plan(uint32_t cluster, unsigned int *table_ptr)
{
	uint32_t i = 0;
	struct cluster_info *cluster_ptr = NULL;

	if (!core_ptr) {
		pr_err("Topology ptr not initialized\n");
		return -ENODEV;
	}
	if (!table_ptr) {
		pr_err("Invalid input\n");
		return -EINVAL;
	}
	if (!freq_table_get)
		check_freq_table();

	for (; i < core_ptr->entity_count; i++) {
		cluster_ptr = &core_ptr->child_entity_ptr[i];
		if (cluster_ptr->cluster_id == cluster)
			break;
	}
	if (i == core_ptr->entity_count) {
		pr_err("Invalid cluster ID:%d\n", cluster);
		return -EINVAL;
	}
	if (!cluster_ptr->freq_table) {
		pr_err("Cluster%d clock plan not initialized\n", cluster);
		return -EINVAL;
	}

	for (i = 0; i <= cluster_ptr->freq_idx_high; i++)
		table_ptr[i] = cluster_ptr->freq_table[i].frequency;

	return 0;
}

int msm_thermal_set_cluster_freq(uint32_t cluster, uint32_t freq, bool is_max)
{
	int ret = 0;
	uint32_t i = 0;
	struct cluster_info *cluster_ptr = NULL;
	bool notify = false;

	if (!core_ptr) {
		pr_err("Topology ptr not initialized\n");
		return -ENODEV;
	}

	for (; i < core_ptr->entity_count; i++) {
		cluster_ptr = &core_ptr->child_entity_ptr[i];
		if (cluster_ptr->cluster_id != cluster)
			continue;
		if (!cluster_ptr->sync_cluster) {
			pr_err("Cluster%d is not synchronous\n", cluster);
			return -EINVAL;
		} else {
			pr_debug("Update Cluster%d %s frequency to %d\n",
				cluster, (is_max) ? "max" : "min", freq);
			break;
		}
	}
	if (i == core_ptr->entity_count) {
		pr_err("Invalid cluster ID:%d\n", cluster);
		return -EINVAL;
	}

	for_each_cpu_mask(i, cluster_ptr->cluster_cores) {
		uint32_t *freq_ptr = (is_max) ? &cpus[i].user_max_freq
					: &cpus[i].user_min_freq;
		if (*freq_ptr == freq)
			continue;
		notify = true;
		*freq_ptr = freq;
	}

	if (freq_mitigation_task) {
		if (notify)
			complete(&freq_mitigation_complete);
	} else {
		pr_err("Frequency mitigation task is not initialized\n");
		return -ESRCH;
	}

	return ret;
}

int msm_thermal_set_frequency(uint32_t cpu, uint32_t freq, bool is_max)
{
	int ret = 0;
+21 −0
Original line number Diff line number Diff line
@@ -60,6 +60,12 @@ extern int msm_thermal_init(struct msm_thermal_data *pdata);
extern int msm_thermal_device_init(void);
extern int msm_thermal_set_frequency(uint32_t cpu, uint32_t freq,
	bool is_max);
extern int msm_thermal_set_cluster_freq(uint32_t cluster, uint32_t freq,
	bool is_max);
extern int msm_thermal_get_freq_plan_size(uint32_t cluster,
	unsigned int *table_len);
extern int msm_thermal_get_cluster_freq_plan(uint32_t cluster,
	unsigned int *table_ptr);
#else
static inline int msm_thermal_init(struct msm_thermal_data *pdata)
{
@@ -74,6 +80,21 @@ static inline int msm_thermal_set_frequency(uint32_t cpu, uint32_t freq,
{
	return -ENOSYS;
}
static inline int msm_thermal_set_cluster_freq(uint32_t cluster, uint32_t freq,
	bool is_max);
{
	return -ENOSYS;
}
static inline int msm_thermal_get_freq_plan_size(uint32_t cluster,
	unsigned int *table_len);
{
	return -ENOSYS;
}
static inline int msm_thermal_get_cluster_freq_plan(uint32_t cluster,
	unsigned int *table_ptr);
{
	return -ENOSYS;
}
#endif

#endif /*__MSM_THERMAL_H*/
+45 −0
Original line number Diff line number Diff line
@@ -4,16 +4,47 @@
#include <linux/ioctl.h>

#define MSM_THERMAL_IOCTL_NAME "msm_thermal_query"
#define MSM_IOCTL_FREQ_SIZE 16

struct __attribute__((__packed__)) cpu_freq_arg {
	uint32_t cpu_num;
	uint32_t freq_req;
};

struct __attribute__((__packed__)) clock_plan_arg {
	uint32_t cluster_num;
	/*
	** A value of zero for freq_table_len, will fetch the length of the
	** cluster frequency table. A non-zero value will fetch the frequency
	** table contents.
	*/
	uint32_t freq_table_len;
	/*
	** For clusters with frequency table length greater than
	** MSM_IOCTL_FREQ_SIZE, the frequency table is fetched from kernel
	** in multiple sets or iterations. The set_idx variable,
	** indicates, which set/part of frequency table the user is requesting.
	** The set index value starts from zero. A set index value of 'Z',
	** will fetch MSM_IOCTL_FREQ_SIZE or maximum available number of
	** frequency values (if it is less than MSM_IOCTL_FREQ_SIZE)
	** from the frequency table, starting from the index
	** (Z * MSM_IOCTL_FREQ_SIZE).
	** For example, in a device supporting 19 different frequencies, a set
	** index value of 0 will fetch the first 16 (MSM_IOCTL_FREQ_SIZE)
	** frequencies starting from the index 0 and a set value of 1 will fetch
	** the remaining 3 frequencies starting from the index 16.
	** A successful get, will populate the freq_table_len with the
	** number of frequency table entries fetched.
	*/
	uint32_t set_idx;
	unsigned int freq_table[MSM_IOCTL_FREQ_SIZE];
};

struct __attribute__((__packed__)) msm_thermal_ioctl {
	uint32_t size;
	union {
		struct cpu_freq_arg cpu_freq;
		struct clock_plan_arg clock_freq;
	};
};

@@ -21,6 +52,11 @@ enum {
	/*Set CPU Frequency*/
	MSM_SET_CPU_MAX_FREQ = 0x00,
	MSM_SET_CPU_MIN_FREQ = 0x01,
	/*Set cluster frequency*/
	MSM_SET_CLUSTER_MAX_FREQ = 0x02,
	MSM_SET_CLUSTER_MIN_FREQ = 0x03,
	/*Get cluster frequency plan*/
	MSM_GET_CLUSTER_FREQ_PLAN = 0x04,

	MSM_CMD_MAX_NR,
};
@@ -33,6 +69,15 @@ enum {
#define MSM_THERMAL_SET_CPU_MIN_FREQUENCY _IOW(MSM_THERMAL_MAGIC_NUM,\
		MSM_SET_CPU_MIN_FREQ, struct msm_thermal_ioctl)

#define MSM_THERMAL_SET_CLUSTER_MAX_FREQUENCY _IOW(MSM_THERMAL_MAGIC_NUM,\
		MSM_SET_CLUSTER_MAX_FREQ, struct msm_thermal_ioctl)

#define MSM_THERMAL_SET_CLUSTER_MIN_FREQUENCY _IOW(MSM_THERMAL_MAGIC_NUM,\
		MSM_SET_CLUSTER_MIN_FREQ, struct msm_thermal_ioctl)

#define MSM_THERMAL_GET_CLUSTER_FREQUENCY_PLAN _IOR(MSM_THERMAL_MAGIC_NUM,\
		MSM_GET_CLUSTER_FREQ_PLAN, struct msm_thermal_ioctl)

#ifdef __KERNEL__
extern int msm_thermal_ioctl_init(void);
extern void msm_thermal_ioctl_cleanup(void);