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

Commit f0b7d80a authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drivers: cpuidle: lpm-levels: Support for different CPUs in a cluster"

parents b65dd71c 168922d3
Loading
Loading
Loading
Loading
+9 −8
Original line number Diff line number Diff line
@@ -28,9 +28,6 @@ Required properties:
	- qcom,default-level: The default low power level that a cluster is
	programmed. The SPM of the corresponding device is configured at this
	low power mode by default.
	- qcom,cpu: List of CPU phandles to identify the CPUs associated with
	this cluster. This property is required if and only if the cluster
	node contains a qcom,pm-cpu node.

	qcom,pm-cluster contains qcom,pm-cluster-level nodes which identify
	the various low power modes that the cluster can enter. The
@@ -103,9 +100,13 @@ Required properties:
					power collapse (PC)

[Node bindings for qcom,pm-cpu]
qcom,pm-cpu contains the low power modes that a cpu could enter. Currently it
doesn't have any required properties and is a container for
qcom,pm-cpu-levels.
qcom,pm-cpu contains the low power modes that a cpu could enter and the CPUs
that share the parameters.It contains the following properties.
	- qcom,cpu: List of CPU phandles to identify the CPUs associated with
	this cluster.
	- qcom,pm-cpu-levels: The different low power modes that a CPU could
	enter. The following section explains the required properties of this
	node.

[Node bindings for qcom,pm-cpu-levels]
 Required properties:
@@ -184,7 +185,6 @@ qcom,lpm-levels {
			label = "a53";
			qcom,spm-device-names = "l2";
			qcom,default-level=<0>;
			qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;

			qcom,pm-cluster-level@0{
				reg = <0>;
@@ -210,6 +210,7 @@ qcom,lpm-levels {
			qcom,pm-cpu {
				#address-cells = <1>;
				#size-cells = <0>;
				qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;
				qcom,pm-cpu-level@0 {
					reg = <0>;
					qcom,spm-cpu-mode = "wfi";
@@ -255,7 +256,6 @@ qcom,lpm-levels {
			label = "a57";
			qcom,spm-device-names = "l2";
			qcom,default-level=<0>;
			qcom,cpu = <&CPU4 &CPU5 &CPU6 &CPU7>;

			qcom,pm-cluster-level@0{
				reg = <0>;
@@ -281,6 +281,7 @@ qcom,lpm-levels {
			qcom,pm-cpu {
				#address-cells = <1>;
				#size-cells = <0>;
				qcom,cpu = <&CPU4 &CPU5 &CPU6 &CPU7>;
				qcom,pm-cpu-level@0 {
					reg = <0>;
					qcom,spm-cpu-mode = "wfi";
+53 −3
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@
			#size-cells = <0>;
			label = "L3";
			qcom,spm-device-names = "L3";
			qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5 &CPU6
				&CPU7>;
			qcom,psci-mode-shift = <4>;
			qcom,psci-mode-mask = <0xfff>;

@@ -86,12 +84,64 @@
				qcom,is-reset;
				qcom,notify-rpm;
			};
			qcom,pm-cpu@0 {
				#address-cells = <1>;
				#size-cells = <0>;
				qcom,psci-mode-shift = <0>;
				qcom,psci-mode-mask = <0xf>;
				qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;

				qcom,pm-cpu-level@0 { /* C1 */
					reg = <0>;
					qcom,spm-cpu-mode = "wfi";
					qcom,psci-cpu-mode = <0x1>;
					qcom,latency-us = <43>;
					qcom,ss-power = <454>;
					qcom,energy-overhead = <38639>;
					qcom,time-overhead = <83>;
				};

				qcom,pm-cpu-level@1 { /* C2D */
					reg = <1>;
					qcom,psci-cpu-mode = <0x2>;
					qcom,spm-cpu-mode = "ret";
					qcom,latency-us = <86>;
					qcom,ss-power = <449>;
					qcom,energy-overhead = <78456>;
					qcom,time-overhead = <167>;
				};

				qcom,pm-cpu-level@2 {  /* C3 */
					reg = <2>;
					qcom,spm-cpu-mode = "pc";
					qcom,psci-cpu-mode = <0x3>;
					qcom,latency-us = <612>;
					qcom,ss-power = <436>;
					qcom,energy-overhead = <418225>;
					qcom,time-overhead = <885>;
					qcom,is-reset;
					qcom,use-broadcast-timer;
				};

				qcom,pm-cpu-level@3 {  /* C4 */
					reg = <3>;
					qcom,spm-cpu-mode = "rail-pc";
					qcom,psci-cpu-mode = <0x4>;
					qcom,latency-us = <700>;
					qcom,ss-power = <400>;
					qcom,energy-overhead = <428225>;
					qcom,time-overhead = <1000>;
					qcom,is-reset;
					qcom,use-broadcast-timer;
				};
			};

			qcom,pm-cpu {
			qcom,pm-cpu@1 {
				#address-cells = <1>;
				#size-cells = <0>;
				qcom,psci-mode-shift = <0>;
				qcom,psci-mode-mask = <0xf>;
				qcom,cpu = <&CPU4 &CPU5 &CPU6 &CPU7>;

				qcom,pm-cpu-level@0 { /* C1 */
					reg = <0>;
+154 −165
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@
#include <linux/moduleparam.h>
#include "lpm-levels.h"

bool use_psci;
enum lpm_type {
	IDLE = 0,
	SUSPEND,
@@ -306,6 +305,7 @@ static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
	struct lpm_level_avail *level_list = NULL;
	char cpu_name[20] = {0};
	int ret = 0;
	struct list_head *pos;

	cpu_kobj = devm_kzalloc(&lpm_pdev->dev, sizeof(*cpu_kobj) *
			cpumask_weight(&p->child_cpus), GFP_KERNEL);
@@ -313,16 +313,20 @@ static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
		return -ENOMEM;

	cpu_idx = 0;
	for_each_cpu(cpu, &p->child_cpus) {
	list_for_each(pos, &p->cpu) {
		struct lpm_cpu *lpm_cpu = list_entry(pos, struct lpm_cpu, list);

		for_each_cpu(cpu, &lpm_cpu->related_cpus) {
			snprintf(cpu_name, sizeof(cpu_name), "cpu%d", cpu);
		cpu_kobj[cpu_idx] = kobject_create_and_add(cpu_name, parent);
			cpu_kobj[cpu_idx] = kobject_create_and_add(cpu_name,
					parent);
			if (!cpu_kobj[cpu_idx]) {
				ret = -ENOMEM;
				goto release_kobj;
			}

			level_list = devm_kzalloc(&lpm_pdev->dev,
				p->cpu->nlevels * sizeof(*level_list),
					lpm_cpu->nlevels * sizeof(*level_list),
					GFP_KERNEL);
			if (!level_list) {
				ret = -ENOMEM;
@@ -330,15 +334,17 @@ static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
			}

			/*
		 * Skip enable/disable for WFI. cpuidle expects WFI to be
		 * available at all times.
			 * Skip enable/disable for WFI. cpuidle expects WFI to
			 * be available at all times.
			 */
		for (i = 1; i < p->cpu->nlevels; i++) {

			level_list[i].latency_us = p->levels[i].pwr.latency_us;
			ret = create_lvl_avail_nodes(p->cpu->levels[i].name,
					cpu_kobj[cpu_idx], &level_list[i],
					(void *)p->cpu, cpu, true);
			for (i = 1; i < lpm_cpu->nlevels; i++) {
				level_list[i].latency_us =
					p->levels[i].pwr.latency_us;
				ret = create_lvl_avail_nodes(
						lpm_cpu->levels[i].name,
						cpu_kobj[cpu_idx],
						&level_list[i],
						(void *)lpm_cpu, cpu, true);
				if (ret)
					goto release_kobj;
			}
@@ -346,6 +352,7 @@ static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
			cpu_level_available[cpu] = level_list;
			cpu_idx++;
		}
	}

	return ret;

@@ -385,7 +392,7 @@ int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj)
			return ret;
	}

	if (p->cpu) {
	if (!list_empty(&p->cpu)) {
		ret = create_cpu_lvl_nodes(p, cluster_kobj);
		if (ret)
			return ret;
@@ -431,7 +438,6 @@ static int parse_cluster_params(struct device_node *node,
		return ret;
	}

	if (use_psci) {
	key = "qcom,psci-mode-shift";
	ret = of_property_read_u32(node, key,
			&c->psci_mode_shift);
@@ -453,8 +459,6 @@ static int parse_cluster_params(struct device_node *node,
	/* Set ndevice to 1 as default */
	c->ndevices = 1;

	} else
		pr_warn("Target supports PSCI only\n");
	return 0;
}

@@ -503,21 +507,13 @@ static int parse_cluster_level(struct device_node *node,
	if (ret)
		goto failed;

	if (use_psci) {
		char *k = "qcom,psci-mode";
	key = "qcom,psci-mode";

		ret = of_property_read_u32(node, k, &level->psci_id);
	ret = of_property_read_u32(node, key, &level->psci_id);
	if (ret)
		goto failed;

	level->is_reset = of_property_read_bool(node, "qcom,is-reset");
	} else
		pr_warn("Build supports PSCI targets only");

	key = "label";
	ret = of_property_read_string(node, key, &level->level_name);
	if (ret)
		goto failed;

	if (cluster->nlevels != cluster->default_level) {
		key = "min child idx";
@@ -531,10 +527,6 @@ static int parse_cluster_level(struct device_node *node,
	}

	level->notify_rpm = of_property_read_bool(node, "qcom,notify-rpm");
	level->disable_dynamic_routing = of_property_read_bool(node,
					"qcom,disable-dynamic-int-routing");
	level->last_core_only = of_property_read_bool(node,
					"qcom,last-core-only");

	key = "parse_power_params";
	ret = parse_power_params(node, &level->pwr);
@@ -569,9 +561,7 @@ static int parse_cpu_mode(struct device_node *n, struct lpm_cpu_level *l)
		return ret;
	}

	if (use_psci) {
	key = "qcom,psci-cpu-mode";

	ret = of_property_read_u32(n, key, &l->psci_id);
	if (ret) {
		pr_err("Failed reading %s on device %s\n", key,
@@ -581,8 +571,6 @@ static int parse_cpu_mode(struct device_node *n, struct lpm_cpu_level *l)
	key = "qcom,hyp-psci";

	l->hyp_psci = of_property_read_bool(n, key);
	} else
		pr_warn("Build supports PSCI targets only");
	return 0;

}
@@ -639,51 +627,26 @@ static int calculate_residency(struct power_params *base_pwr,
				next_pwr->time_overhead_us : residency;
}

static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
static int parse_cpu(struct device_node *node, struct lpm_cpu *cpu)
{
	struct device_node *n;
	int ret = -ENOMEM;
	int i, j;
	char *key;

	c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL);
	if (!c->cpu)
		return ret;

	c->cpu->parent = c;
	if (use_psci) {

		key = "qcom,psci-mode-shift";

		ret = of_property_read_u32(node, key, &c->cpu->psci_mode_shift);
		if (ret) {
			pr_err("Failed reading %s on device %s\n", key,
					node->name);
			return ret;
		}
		key = "qcom,psci-mode-mask";

		ret = of_property_read_u32(node, key, &c->cpu->psci_mode_mask);
		if (ret) {
			pr_err("Failed reading %s on device %s\n", key,
					node->name);
			return ret;
		}
	}
	struct device_node *n;
	int ret, i, j;
	const char *key;
	for_each_child_of_node(node, n) {
		struct lpm_cpu_level *l = &c->cpu->levels[c->cpu->nlevels];
		struct lpm_cpu_level *l = &cpu->levels[cpu->nlevels];

		c->cpu->nlevels++;
		cpu->nlevels++;

		ret = parse_cpu_mode(n, l);
		if (ret < 0) {
			pr_info("Failed %s\n", l->name);
			goto failed;
			return ret;
		}

		ret = parse_power_params(n, &l->pwr);
		if (ret)
			goto failed;
			return ret;

		key = "qcom,use-broadcast-timer";
		l->use_bc_timer = of_property_read_bool(n, key);
@@ -698,32 +661,83 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
		if (ret == -EINVAL)
			l->reset_level = LPM_RESET_LVL_NONE;
		else if (ret)
			goto failed;
			return ret;
	}
	for (i = 0; i < c->cpu->nlevels; i++) {
		for (j = 0; j < c->cpu->nlevels; j++) {
	for (i = 0; i < cpu->nlevels; i++) {
		for (j = 0; j < cpu->nlevels; j++) {
			if (i >= j) {
				c->cpu->levels[i].pwr.residencies[j] = 0;
				cpu->levels[i].pwr.residencies[j] = 0;
				continue;
			}

			c->cpu->levels[i].pwr.residencies[j] =
				calculate_residency(&c->cpu->levels[i].pwr,
					&c->cpu->levels[j].pwr);
			cpu->levels[i].pwr.residencies[j] =
				calculate_residency(&cpu->levels[i].pwr,
						&cpu->levels[j].pwr);

			pr_err("%s: idx %d %u\n", __func__, j,
					c->cpu->levels[i].pwr.residencies[j]);
					cpu->levels[i].pwr.residencies[j]);
		}
	}
	for_each_cpu(i, &cpu->related_cpus) {
		per_cpu(max_residency, i) = devm_kzalloc(&lpm_pdev->dev,
				sizeof(uint32_t) * cpu->nlevels,
				GFP_KERNEL);
		if (!per_cpu(max_residency, i))
			return -ENOMEM;
		per_cpu(min_residency, i) = devm_kzalloc(
				&lpm_pdev->dev,
				sizeof(uint32_t) * cpu->nlevels,
				GFP_KERNEL);
		if (!per_cpu(min_residency, i))
			return -ENOMEM;
		set_optimum_cpu_residency(cpu, i, true);
	}

	return 0;
}

static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
{
	int ret = -ENOMEM, i;
	char *key;
	struct lpm_cpu *cpu;

	cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*cpu), GFP_KERNEL);
	if (!cpu)
		return ret;

	if (get_cpumask_for_node(node, &cpu->related_cpus))
		return -EINVAL;

	cpu->parent = c;

	key = "qcom,psci-mode-shift";
	ret = of_property_read_u32(node, key, &cpu->psci_mode_shift);
	if (ret) {
		pr_err("Failed reading %s on device %s\n", key,
				node->name);
		return ret;
	}
	key = "qcom,psci-mode-mask";

	ret = of_property_read_u32(node, key, &cpu->psci_mode_mask);
	if (ret) {
		pr_err("Failed reading %s on device %s\n", key,
				node->name);
		return ret;
	}

	if (parse_cpu(node, cpu))
		goto failed;
	cpumask_or(&c->child_cpus, &c->child_cpus, &cpu->related_cpus);
	list_add(&cpu->list, &c->cpu);
	return 0;
failed:
	for (i = 0; i < c->cpu->nlevels; i++) {
		kfree(c->cpu->levels[i].name);
		c->cpu->levels[i].name = NULL;
	for (i = 0; i < cpu->nlevels; i++) {
		kfree(cpu->levels[i].name);
		cpu->levels[i].name = NULL;
	}
	kfree(c->cpu);
	c->cpu = NULL;
	kfree(cpu);
	pr_err("%s(): Failed with error code:%d\n", __func__, ret);
	return ret;
}
@@ -731,6 +745,7 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
void free_cluster_node(struct lpm_cluster *cluster)
{
	struct list_head *list;
	struct lpm_cpu *cpu, *n;
	int i;

	list_for_each(list, &cluster->child) {
@@ -741,22 +756,21 @@ void free_cluster_node(struct lpm_cluster *cluster)
		free_cluster_node(n);
	};

	if (cluster->cpu) {
		for (i = 0; i < cluster->cpu->nlevels; i++) {
			kfree(cluster->cpu->levels[i].name);
			cluster->cpu->levels[i].name = NULL;
	list_for_each_entry_safe(cpu, n, &cluster->cpu, list) {
		struct lpm_cpu *cpu = list_entry(list, typeof(*cpu), list);

		for (i = 0; i < cpu->nlevels; i++) {
			kfree(cpu->levels[i].name);
			cpu->levels[i].name = NULL;
		}
		list_del(list);
	}
	for (i = 0; i < cluster->nlevels; i++) {
		kfree(cluster->levels[i].mode);
		cluster->levels[i].mode = NULL;
	}
	kfree(cluster->cpu);
	kfree(cluster->name);
	kfree(cluster->lpm_dev);
	cluster->cpu = NULL;
	cluster->name = NULL;
	cluster->lpm_dev = NULL;
	cluster->ndevices = 0;
}

@@ -785,6 +799,7 @@ struct lpm_cluster *parse_cluster(struct device_node *node,
		goto failed_parse_params;

	INIT_LIST_HEAD(&c->child);
	INIT_LIST_HEAD(&c->cpu);
	c->parent = parent;
	spin_lock_init(&c->sync_lock);
	c->min_child_level = NR_LPM_LEVELS;
@@ -795,7 +810,6 @@ struct lpm_cluster *parse_cluster(struct device_node *node,
			continue;
		key = "qcom,pm-cluster-level";
		if (!of_node_cmp(n->name, key)) {
			WARN_ON(!use_psci && c->no_saw_devices);
			if (parse_cluster_level(n, c))
				goto failed_parse_cluster;
			continue;
@@ -805,7 +819,6 @@ struct lpm_cluster *parse_cluster(struct device_node *node,
		if (!of_node_cmp(n->name, key)) {
			struct lpm_cluster *child;

			WARN_ON(!use_psci && c->no_saw_devices);
			child = parse_cluster(n, c);
			if (!child)
				goto failed_parse_cluster;
@@ -819,34 +832,11 @@ struct lpm_cluster *parse_cluster(struct device_node *node,

		key = "qcom,pm-cpu";
		if (!of_node_cmp(n->name, key)) {
			/*
			 * Parse the the cpu node only if a pm-cpu node
			 * is available, though the mask is defined @ the
			 * cluster level
			 */
			if (get_cpumask_for_node(node, &c->child_cpus))
				goto failed_parse_cluster;

			if (parse_cpu_levels(n, c))
				goto failed_parse_cluster;

			c->aff_level = 1;

			for_each_cpu(i, &c->child_cpus) {
				per_cpu(max_residency, i) = devm_kzalloc(
					&lpm_pdev->dev,
					sizeof(uint32_t) * c->cpu->nlevels,
					GFP_KERNEL);
				if (!per_cpu(max_residency, i))
					return ERR_PTR(-ENOMEM);
				per_cpu(min_residency, i) = devm_kzalloc(
					&lpm_pdev->dev,
					sizeof(uint32_t) * c->cpu->nlevels,
					GFP_KERNEL);
				if (!per_cpu(min_residency, i))
					return ERR_PTR(-ENOMEM);
				set_optimum_cpu_residency(c->cpu, i, true);
			}
		}
	}

@@ -883,8 +873,6 @@ struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev)
{
	struct device_node *top = NULL;

	use_psci = of_property_read_bool(pdev->dev.of_node, "qcom,use-psci");

	top = of_find_node_by_name(pdev->dev.of_node, "qcom,pm-cluster");
	if (!top) {
		pr_err("Failed to find root node\n");
@@ -898,6 +886,7 @@ struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev)
void cluster_dt_walkthrough(struct lpm_cluster *cluster)
{
	struct list_head *list;
	struct lpm_cpu *cpu;
	int i, j;
	static int id;
	char str[10] = {0};
@@ -918,12 +907,12 @@ void cluster_dt_walkthrough(struct lpm_cluster *cluster)
					&cluster->name[j], &l->mode[i]);
	}

	if (cluster->cpu) {
	list_for_each_entry(cpu, &cluster->cpu, list) {
		pr_info("%d\n", __LINE__);
		for (j = 0; j < cluster->cpu->nlevels; j++)
		for (j = 0; j < cpu->nlevels; j++)
			pr_info("%s\tCPU mode: %s id:%d\n", str,
					cluster->cpu->levels[j].name,
					cluster->cpu->levels[j].mode);
					cpu->levels[j].name,
					cpu->levels[j].mode);
	}

	id++;
+101 −241

File changed.

Preview size limit exceeded, changes collapsed.

+4 −18
Original line number Diff line number Diff line
@@ -17,8 +17,6 @@
#define MAXSAMPLES 5
#define CLUST_SMPL_INVLD_TIME 40000

extern bool use_psci;

struct lpm_lookup_table {
	uint32_t modes;
	const char *mode_name;
@@ -47,10 +45,13 @@ struct lpm_cpu_level {
};

struct lpm_cpu {
	struct list_head list;
	struct cpumask related_cpus;
	struct lpm_cpu_level levels[NR_LPM_LEVELS];
	int nlevels;
	unsigned int psci_mode_shift;
	unsigned int psci_mode_mask;
	struct cpuidle_driver *drv;
	struct lpm_cluster *parent;
};

@@ -74,21 +75,13 @@ struct lpm_cluster_level {
	struct cpumask num_cpu_votes;
	struct power_params pwr;
	bool notify_rpm;
	bool disable_dynamic_routing;
	bool sync_level;
	bool last_core_only;
	struct lpm_level_avail available;
	unsigned int psci_id;
	bool is_reset;
	int reset_level;
};

struct low_power_ops {
	struct msm_spm_device *spm;
	int (*set_mode)(struct low_power_ops *ops, int mode, bool notify_rpm);
	enum msm_pm_l2_scm_flag tz_flag;
};

struct cluster_history {
	uint32_t resi[MAXSAMPLES];
	int mode[MAXSAMPLES];
@@ -108,16 +101,13 @@ struct lpm_cluster {
	const char *cluster_name;
	const char **name;
	unsigned long aff_level; /* Affinity level of the node */
	struct low_power_ops *lpm_dev;
	int ndevices;
	struct lpm_cluster_level levels[NR_LPM_LEVELS];
	int nlevels;
	enum msm_pm_l2_scm_flag l2_flag;
	int min_child_level;
	int default_level;
	int last_level;
	struct lpm_cpu *cpu;
	struct cpuidle_driver *drv;
	struct list_head cpu;
	spinlock_t sync_lock;
	struct cpumask child_cpus;
	struct cpumask num_children_in_sync;
@@ -125,14 +115,10 @@ struct lpm_cluster {
	struct lpm_stats *stats;
	unsigned int psci_mode_shift;
	unsigned int psci_mode_mask;
	bool no_saw_devices;
	struct cluster_history history;
	struct hrtimer histtimer;
};

int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
int set_l3_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
void lpm_suspend_wake_time(uint64_t wakeup_time);

struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev);
Loading