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

Commit d9499637 authored by Tirupathi Reddy's avatar Tirupathi Reddy
Browse files

regulator: mem-acc: Add range check based override fuse version map



Sometimes, a range of override fuse values require a common mem-acc
settings. Add a range based check against override fuse values to
identify the required override map index.

CRs-Fixed: 2056902
Change-Id: Id41bfe24dd0050aedf7b51e7c6b6a08fd370d922
Signed-off-by: default avatarTirupathi Reddy <tirupath@codeaurora.org>
parent 38afda62
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -159,6 +159,21 @@ Optional properties:
				not specified, then "qcom,override-cornerX-reg-config" must contain a single
				register configuration sequence list which is then applied unconditionally.
				This property can only be specified if qcom,cornerX-reg-config property is already defined.
- qcom,override-acc-range-fuse-list:	Array of tuples define the selection parameters used for selecting the override
				mem-acc configuration. The fused values for these selection parameters are used by the
				qcom,override-fuse-range-map to identify the correct set of override properties.
				Each tuple contains 4 elements as defined below:
				  [0] => the fuse row number of the selector
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
- qcom,override-fuse-range-map:	Array of tuples where each tuple specifies the allowed range for all the selection parameters
				defined in qcom,override-acc-range-fuse-list. The fused values of these selection parameters
				are compared against their allowed range in each tuple starting from 0th tuple and use the
				first matched tuple index to select the right tuples from the other override properties.
				Either qcom,override-fuse-range-map or qcom,override-fuse-version-map is used to select
				the override configuration. The qcom,override-fuse-range-map is used if both the
				properties are specified.

mem_acc_vreg_corner: regulator@fd4aa044 {
	compatible = "qcom,mem-acc-regulator";
@@ -184,6 +199,13 @@ mem_acc_vreg_corner: regulator@fd4aa044 {
	qcom,override-fuse-version-map = <0>,
					 <2>,
					 <(-1)>;
	qcom,override-acc-range-fuse-list =
					<37 40 3 0>,
					<36 30 8 0>;
	qcom,override-fuse-range-map =
				<0 0>, <  0   0>, <49 63>,
				<1 1>, <  0   0>, <50 63>,
				<0 1>, < 95 255>, < 0 63>;
	qcom,override-corner-acc-map =	<0 0 1>,
					<0 1 2>,
					<0 1 1>;
+304 −157
Original line number Diff line number Diff line
@@ -108,6 +108,8 @@ struct mem_acc_regulator {
	u32			*phys_reg_addr_list;
	void __iomem		**remap_reg_addr_list;
	struct corner_acc_reg_config	*corner_acc_reg_config;
	u32			*override_acc_range_fuse_list;
	int			override_acc_range_fuse_num;
};

static DEFINE_MUTEX(mem_acc_memory_mutex);
@@ -551,8 +553,7 @@ static int mem_acc_custom_data_init(struct platform_device *pdev,
	return 0;
}

static int override_mem_acc_custom_data(struct platform_device *pdev,
				 struct mem_acc_regulator *mem_acc_vreg,
static int override_mem_acc_custom_data(struct mem_acc_regulator *mem_acc_vreg,
		 int mem_type)
{
	char *custom_apc_data_str;
@@ -649,27 +650,48 @@ static int mem_acc_override_corner_map(struct mem_acc_regulator *mem_acc_vreg)

}

static int mem_acc_find_override_map_match(struct platform_device *pdev,
static void mem_acc_read_efuse_param(struct mem_acc_regulator *mem_acc_vreg,
		u32 *fuse_sel, int *val)
{
	u64 fuse_bits;

	fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, fuse_sel[0],
					   fuse_sel[3]);
	/*
	 * fuse_sel[1] = LSB position in row (shift)
	 * fuse_sel[2] = num of bits (mask)
	 */
	*val = (fuse_bits >> fuse_sel[1]) & ((1 << fuse_sel[2]) - 1);
}

#define FUSE_TUPLE_SIZE 4
static int mem_acc_parse_override_fuse_version_map(
			 struct mem_acc_regulator *mem_acc_vreg)
{
	struct device_node *of_node = pdev->dev.of_node;
	struct device_node *of_node = mem_acc_vreg->dev->of_node;
	int i, rc, tuple_size;
	int len = 0;
	u32 *tmp;
	char *prop_str = "qcom,override-fuse-version-map";

	/* Specify default no match case. */
	mem_acc_vreg->override_map_match = FUSE_MAP_NO_MATCH;
	mem_acc_vreg->override_map_count = 0;
	u32 fuse_sel[4];
	char *prop_str;

	if (!of_find_property(of_node, prop_str, &len)) {
		/* No mapping present. */
		return 0;
	prop_str = "qcom,override-acc-fuse-sel";
	rc = of_property_read_u32_array(of_node, prop_str, fuse_sel,
					FUSE_TUPLE_SIZE);
	if (rc < 0) {
		pr_err("Read failed - %s rc=%d\n", prop_str, rc);
		return rc;
	}

	mem_acc_read_efuse_param(mem_acc_vreg, fuse_sel,
				 &mem_acc_vreg->override_fuse_value);

	prop_str = "qcom,override-fuse-version-map";
	if (!of_find_property(of_node, prop_str, &len))
		return -EINVAL;

	tuple_size = 1;
	mem_acc_vreg->override_map_count = len / (sizeof(u32) * tuple_size);

	if (len == 0 || len % (sizeof(u32) * tuple_size)) {
		pr_err("%s length=%d is invalid\n", prop_str, len);
		return -EINVAL;
@@ -697,7 +719,8 @@ static int mem_acc_find_override_map_match(struct platform_device *pdev,
	}

	if (mem_acc_vreg->override_map_match != FUSE_MAP_NO_MATCH)
		pr_debug("%s tuple match found: %d\n", prop_str,
		pr_info("override_fuse_val=%d, %s tuple match found: %d\n",
			mem_acc_vreg->override_fuse_value, prop_str,
			mem_acc_vreg->override_map_match);
	else
		pr_err("%s tuple match not found\n", prop_str);
@@ -707,6 +730,121 @@ done:
	return rc;
}

static int mem_acc_parse_override_fuse_version_range(
			 struct mem_acc_regulator *mem_acc_vreg)
{
	struct device_node *of_node = mem_acc_vreg->dev->of_node;
	int i, j, rc, size, row_size;
	int num_fuse_sel, len = 0;
	u32 *tmp = NULL;
	char *prop_str;
	u32 *fuse_val, *fuse_sel;
	char *buf = NULL;
	int pos = 0, buflen;

	prop_str = "qcom,override-acc-range-fuse-list";
	if (!of_find_property(of_node, prop_str, &len)) {
		pr_err("%s property is missing\n", prop_str);
		return -EINVAL;
	}

	size = len / sizeof(u32);
	if (len == 0 || (size % FUSE_TUPLE_SIZE)) {
		pr_err("%s property length (%d) is invalid\n", prop_str, len);
		return -EINVAL;
	}

	num_fuse_sel = size / FUSE_TUPLE_SIZE;
	fuse_val = devm_kcalloc(mem_acc_vreg->dev, num_fuse_sel,
				sizeof(*fuse_val), GFP_KERNEL);
	if (!fuse_val)
		return -ENOMEM;
	mem_acc_vreg->override_acc_range_fuse_list = fuse_val;
	mem_acc_vreg->override_acc_range_fuse_num = num_fuse_sel;

	fuse_sel = kzalloc(len, GFP_KERNEL);
	if (!fuse_sel) {
		rc = -ENOMEM;
		goto done;
	}

	rc = of_property_read_u32_array(of_node, prop_str, fuse_sel,
					size);
	if (rc) {
		pr_err("%s read failed, rc=%d\n", prop_str, rc);
		goto done;
	}

	for (i = 0; i < num_fuse_sel; i++) {
		mem_acc_read_efuse_param(mem_acc_vreg, &fuse_sel[i * 4],
					 &fuse_val[i]);
	}

	prop_str = "qcom,override-fuse-range-map";
	if (!of_find_property(of_node, prop_str, &len))
		goto done;

	row_size = num_fuse_sel * 2;
	mem_acc_vreg->override_map_count = len / (sizeof(u32) * row_size);

	if (len == 0 || len % (sizeof(u32) * row_size)) {
		pr_err("%s length=%d is invalid\n", prop_str, len);
		rc = -EINVAL;
		goto done;
	}

	tmp = kzalloc(len, GFP_KERNEL);
	if (!tmp) {
		rc = -ENOMEM;
		goto done;
	}

	rc = of_property_read_u32_array(of_node, prop_str, tmp,
				mem_acc_vreg->override_map_count * row_size);
	if (rc) {
		pr_err("could not read %s rc=%d\n", prop_str, rc);
		goto done;
	}

	for (i = 0; i < mem_acc_vreg->override_map_count; i++) {
		for (j = 0; j < num_fuse_sel; j++) {
			if (tmp[i * row_size + j * 2] > fuse_val[j]
				|| tmp[i * row_size + j * 2 + 1] < fuse_val[j])
				break;
		}

		if (j == num_fuse_sel) {
			mem_acc_vreg->override_map_match = i;
			break;
		}
	}

	/*
	 * Log register and value mapping since they are useful for
	 * baseline MEM ACC logging.
	 */
	buflen = num_fuse_sel * sizeof("fuse_selxxxx = XXXX ");
	buf = kzalloc(buflen, GFP_KERNEL);
	if (!buf)
		goto done;

	for (j = 0; j < num_fuse_sel; j++)
		pos += scnprintf(buf + pos, buflen - pos, "fuse_sel%d = %d ",
				 j, fuse_val[j]);
	buf[pos] = '\0';
	if (mem_acc_vreg->override_map_match != FUSE_MAP_NO_MATCH)
		pr_info("%s %s tuple match found: %d\n", buf, prop_str,
			mem_acc_vreg->override_map_match);
	else
		pr_err("%s %s tuple match not found\n", buf, prop_str);

done:
	kfree(fuse_sel);
	kfree(tmp);
	kfree(buf);
	return rc;
}

#define MAX_CHARS_PER_INT	20

static int mem_acc_reg_addr_val_dump(struct mem_acc_regulator *mem_acc_vreg,
@@ -791,6 +929,150 @@ static int mem_acc_get_reg_addr_val(struct device_node *of_node,
	return rc;
}

static int mem_acc_override_reg_addr_val_init(
			struct mem_acc_regulator *mem_acc_vreg)
{
	struct device_node *of_node = mem_acc_vreg->dev->of_node;
	struct corner_acc_reg_config *corner_acc_reg_config;
	struct acc_reg_value *override_reg_config_list;
	int i, tuple_count, tuple_match, len = 0, rc = 0;
	u32 list_size, override_max_reg_config_len;
	char prop_str[40];
	struct property *prop;
	int num_corners = mem_acc_vreg->num_corners;

	if (!mem_acc_vreg->corner_acc_reg_config)
		return 0;

	if (mem_acc_vreg->override_map_count) {
		if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH)
			return 0;
		tuple_count = mem_acc_vreg->override_map_count;
		tuple_match = mem_acc_vreg->override_map_match;
	} else {
		tuple_count = 1;
		tuple_match = 0;
	}

	corner_acc_reg_config = mem_acc_vreg->corner_acc_reg_config;
	for (i = 1; i <= num_corners; i++) {
		snprintf(prop_str, sizeof(prop_str),
			 "qcom,override-corner%d-addr-val-map", i);
		prop = of_find_property(of_node, prop_str, &len);
		list_size = len / (tuple_count * sizeof(u32));
		if (!prop) {
			pr_debug("%s property not specified\n", prop_str);
			continue;
		}

		if ((!list_size) || list_size < (num_corners * 2)) {
			pr_err("qcom,override-corner%d-addr-val-map property is missed or invalid length: len=%d\n",
			i, len);
			return -EINVAL;
		}

		override_max_reg_config_len = list_size / (num_corners * 2);
		override_reg_config_list =
				corner_acc_reg_config[i].reg_config_list;

		if (corner_acc_reg_config[i].max_reg_config_len
					!= override_max_reg_config_len) {
			/* Free already allocate memory */
			devm_kfree(mem_acc_vreg->dev, override_reg_config_list);

			/* Allocated memory for new requirement */
			override_reg_config_list =
				devm_kcalloc(mem_acc_vreg->dev,
				override_max_reg_config_len * num_corners,
				sizeof(*override_reg_config_list), GFP_KERNEL);
			if (!override_reg_config_list)
				return -ENOMEM;

			corner_acc_reg_config[i].max_reg_config_len =
						override_max_reg_config_len;
			corner_acc_reg_config[i].reg_config_list =
						override_reg_config_list;
		}

		rc = mem_acc_get_reg_addr_val(of_node, prop_str,
					override_reg_config_list, tuple_match,
					list_size, mem_acc_vreg->num_acc_reg);
		if (rc) {
			pr_err("Failed to read %s property: rc=%d\n",
				prop_str, rc);
			return rc;
		}

		rc = mem_acc_reg_addr_val_dump(mem_acc_vreg,
						&corner_acc_reg_config[i], i);
		if (rc) {
			pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n",
				i, rc);
			return rc;
		}
	}

	return rc;
}

static int mem_acc_parse_override_config(struct mem_acc_regulator *mem_acc_vreg)
{
	struct device_node *of_node = mem_acc_vreg->dev->of_node;
	int i, rc = 0;

	/* Specify default no match case. */
	mem_acc_vreg->override_map_match = FUSE_MAP_NO_MATCH;
	mem_acc_vreg->override_map_count = 0;

	if (of_find_property(of_node, "qcom,override-fuse-range-map",
			     NULL)) {
		rc = mem_acc_parse_override_fuse_version_range(mem_acc_vreg);
		if (rc) {
			pr_err("parsing qcom,override-fuse-range-map property failed, rc=%d\n",
				rc);
			return rc;
		}
	} else if (of_find_property(of_node, "qcom,override-fuse-version-map",
				    NULL)) {
		rc = mem_acc_parse_override_fuse_version_map(mem_acc_vreg);
		if (rc) {
			pr_err("parsing qcom,override-fuse-version-map property failed, rc=%d\n",
				rc);
			return rc;
		}
	} else {
		/* No override fuse configuration defined in device node */
		return 0;
	}

	if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH)
		return 0;

	rc = mem_acc_override_corner_map(mem_acc_vreg);
	if (rc) {
		pr_err("Unable to override corner map rc=%d\n", rc);
		return rc;
	}

	rc = mem_acc_override_reg_addr_val_init(mem_acc_vreg);
	if (rc) {
		pr_err("Unable to override reg_config_list init rc=%d\n",
			rc);
		return rc;
	}

	for (i = 0; i < MEMORY_MAX; i++) {
		rc = override_mem_acc_custom_data(mem_acc_vreg, i);
		if (rc) {
			pr_err("Unable to override custom data for mem_type=%d rc=%d\n",
				i, rc);
			return rc;
		}
	}

	return rc;
}

static int mem_acc_init_reg_config(struct mem_acc_regulator *mem_acc_vreg)
{
	struct device_node *of_node = mem_acc_vreg->dev->of_node;
@@ -967,92 +1249,6 @@ static int mem_acc_reg_config_init(struct mem_acc_regulator *mem_acc_vreg)
	return rc;
}

static int mem_acc_override_reg_addr_val_init(
			struct mem_acc_regulator *mem_acc_vreg)
{
	struct device_node *of_node = mem_acc_vreg->dev->of_node;
	struct corner_acc_reg_config *corner_acc_reg_config;
	struct acc_reg_value *override_reg_config_list;
	int i, tuple_count, tuple_match, len = 0, rc = 0;
	u32 list_size, override_max_reg_config_len;
	char prop_str[40];
	struct property *prop;
	int num_corners = mem_acc_vreg->num_corners;

	if (!mem_acc_vreg->corner_acc_reg_config)
		return 0;

	if (mem_acc_vreg->override_map_count) {
		if (mem_acc_vreg->override_map_match ==	FUSE_MAP_NO_MATCH)
			return 0;
		tuple_count = mem_acc_vreg->override_map_count;
		tuple_match = mem_acc_vreg->override_map_match;
	} else {
		tuple_count = 1;
		tuple_match = 0;
	}

	corner_acc_reg_config = mem_acc_vreg->corner_acc_reg_config;
	for (i = 1; i <= num_corners; i++) {
		snprintf(prop_str, sizeof(prop_str),
				"qcom,override-corner%d-addr-val-map", i);
		prop = of_find_property(of_node, prop_str, &len);
		list_size = len / (tuple_count * sizeof(u32));
		if (!prop) {
			pr_debug("%s property not specified\n", prop_str);
			continue;
		}

		if ((!list_size) || list_size < (num_corners * 2)) {
			pr_err("qcom,override-corner%d-addr-val-map property is missed or invalid length: len=%d\n",
			i, len);
			return -EINVAL;
		}

		override_max_reg_config_len = list_size / (num_corners * 2);
		override_reg_config_list =
				corner_acc_reg_config[i].reg_config_list;

		if (corner_acc_reg_config[i].max_reg_config_len
					!= override_max_reg_config_len) {
			/* Free already allocate memory */
			devm_kfree(mem_acc_vreg->dev, override_reg_config_list);

			/* Allocated memory for new requirement */
			override_reg_config_list =
				devm_kcalloc(mem_acc_vreg->dev,
				override_max_reg_config_len * num_corners,
				sizeof(*override_reg_config_list), GFP_KERNEL);
			if (!override_reg_config_list)
				return -ENOMEM;

			corner_acc_reg_config[i].max_reg_config_len =
						override_max_reg_config_len;
			corner_acc_reg_config[i].reg_config_list =
						override_reg_config_list;
		}

		rc = mem_acc_get_reg_addr_val(of_node, prop_str,
					override_reg_config_list, tuple_match,
					list_size, mem_acc_vreg->num_acc_reg);
		if (rc) {
			pr_err("Failed to read %s property: rc=%d\n",
				prop_str, rc);
			return rc;
		}

		rc = mem_acc_reg_addr_val_dump(mem_acc_vreg,
						&corner_acc_reg_config[i], i);
		if (rc) {
			pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n",
				i, rc);
			return rc;
		}
	}

	return rc;
}

#define MEM_TYPE_STRING_LEN	20
static int mem_acc_init(struct platform_device *pdev,
		struct mem_acc_regulator *mem_acc_vreg)
@@ -1060,8 +1256,6 @@ static int mem_acc_init(struct platform_device *pdev,
	struct device_node *of_node = pdev->dev.of_node;
	struct resource *res;
	int len, rc, i, j;
	u32 fuse_sel[4];
	u64 fuse_bits;
	bool acc_type_present = false;
	char tmps[MEM_TYPE_STRING_LEN];

@@ -1203,59 +1397,12 @@ static int mem_acc_init(struct platform_device *pdev,
		}
	}

	if (of_find_property(mem_acc_vreg->dev->of_node,
				"qcom,override-acc-fuse-sel", NULL)) {
		rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
			"qcom,override-acc-fuse-sel", fuse_sel, 4);
		if (rc < 0) {
			pr_err("Read failed - qcom,override-acc-fuse-sel rc=%d\n",
					rc);
			return rc;
		}

		fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, fuse_sel[0],
								fuse_sel[3]);
		/*
		 * fuse_sel[1] = LSB position in row (shift)
		 * fuse_sel[2] = num of bits (mask)
		 */
		mem_acc_vreg->override_fuse_value = (fuse_bits >> fuse_sel[1]) &
						((1 << fuse_sel[2]) - 1);

		rc = mem_acc_find_override_map_match(pdev, mem_acc_vreg);
	rc = mem_acc_parse_override_config(mem_acc_vreg);
	if (rc) {
			pr_err("Unable to find fuse map match rc=%d\n", rc);
			return rc;
		}

		pr_debug("override_fuse_val=%d override_map_match=%d\n",
					mem_acc_vreg->override_fuse_value,
					mem_acc_vreg->override_map_match);

		rc = mem_acc_override_corner_map(mem_acc_vreg);
		if (rc) {
			pr_err("Unable to override corner map rc=%d\n", rc);
			return rc;
		}

		rc = mem_acc_override_reg_addr_val_init(mem_acc_vreg);
		if (rc) {
			pr_err("Unable to override reg_config_list init rc=%d\n",
		pr_err("Unable to parse mem acc override configuration, rc=%d\n",
			rc);
		return rc;
	}

		for (i = 0; i < MEMORY_MAX; i++) {
			rc = override_mem_acc_custom_data(pdev,
							mem_acc_vreg, i);
			if (rc) {
				pr_err("Unable to override custom data for mem_type=%d rc=%d\n",
					i, rc);
				return rc;
			}
		}
	}

	if (acc_type_present) {
		mem_acc_vreg->mem_acc_type_data = devm_kzalloc(
			mem_acc_vreg->dev, mem_acc_vreg->num_corners *