Loading Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt +22 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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>; Loading drivers/regulator/mem-acc-regulator.c +304 −157 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -549,8 +551,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; Loading Loading @@ -647,27 +648,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; Loading Loading @@ -695,7 +717,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); Loading @@ -705,6 +728,121 @@ static int mem_acc_find_override_map_match(struct platform_device *pdev, 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, Loading Loading @@ -789,6 +927,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; Loading Loading @@ -965,92 +1247,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) Loading @@ -1058,8 +1254,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]; Loading Loading @@ -1201,59 +1395,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 * Loading Loading
Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt +22 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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>; Loading
drivers/regulator/mem-acc-regulator.c +304 −157 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -549,8 +551,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; Loading Loading @@ -647,27 +648,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; Loading Loading @@ -695,7 +717,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); Loading @@ -705,6 +728,121 @@ static int mem_acc_find_override_map_match(struct platform_device *pdev, 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, Loading Loading @@ -789,6 +927,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; Loading Loading @@ -965,92 +1247,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) Loading @@ -1058,8 +1254,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]; Loading Loading @@ -1201,59 +1395,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 * Loading