Loading Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt +51 −48 Original line number Diff line number Diff line Loading @@ -53,21 +53,6 @@ Optional properties: defined by the qcom,acc-sel-l2-bit-size property. This N-bit value specifies the corner value used by the accelerator for the L2 cache. - qcom,l1-config-skip-fuse-sel: Array of 5 elements to indicate where to read the bits, what value to compare with in order to decide whether to skip configuring the L1 accelerator or not while changing the APC corner and method to read fuse row, using SCM to read or read register directly. The 5 elements with index [0..4] are: [0] => the fuse row number of the selector [1] => LSB bit position of the bits [2] => number of bits [3] => the value to select skip L1 config logic [4] => fuse reading method, 0 for direct reading or 1 for SCM reading When the value of the fuse bits specified by first 3 elements equals to the value in 4th element, L1 accelerator configuration logic is skipped. Otherwise, the original configuration sent from corner map should be applied. If the 5th element is 0, read the fuse row from register directly. Otherwise, read it through SCM. - qcom,l1-acc-custom-data: Array which maps APC corner values to L1 ACC custom data values. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address Loading @@ -78,39 +63,50 @@ Optional properties: while switching between APC corners. The custom register address is specified by "acc-l2-custom" reg-property. The length of the array should be equal to number of APC corners. - qcom,override-acc-fuse-sel: Array of 5 elements to indicate where to read the bits, what value to compare with in order to decide whether to apply override acc-map and custom data or not and method to read fuse row, using SCM to read or read register directly. The 5 elements with index [0..4] are: - qcom,override-acc-fuse-sel: Array of 4 elements which specify the way to read the override fuse. The override fuse value is used by the qcom,override-fuse-version-map to identify the correct set of override properties. The 4 elements with index [0..4] are: [0] => the fuse row number of the selector [1] => LSB bit position of the bits [2] => number of bits [3] => the value to select override ACC configuration [4] => fuse reading method, 0 for direct reading or 1 for SCM reading When the value of the fuse bits specified by first 3 elements equals to the value in 4th element, overridden accelerator configuration logic is applied. Otherwise, the original configuration should be applied. If the 5th element is 0, read the fuse row from register directly. Otherwise, read it through SCM. - qcom,override-corner-acc-map: Array which overrides the existing acc-corner map (specified by qcom,corner-acc-map) with corner values specified in this property. [0] maps APC SVS corner (1) to accelerator SVS corner [1] maps APC NOMINAL corner (2) to accelerator NOMINAL corner [2] maps APC TURBO corner (3) to accelerator TURBO corner - qcom,override-l1-acc-custom-data: Array which overrides the existing l1-acc-custom data [3] => fuse reading method, 0 for direct reading or 1 for SCM reading - qcom,override-fuse-version-map: Array of integers which each match to a override fuse value. Any element in a tuple may use the value 0xffffffff as a wildcard. The index of the first value (in the array) which matches the override fuse is used to select the right tuples from the other override properties. - qcom,override-corner-acc-map: Array of tuples which overrides the existing acc-corner map (specified by qcom,corner-acc-map) with corner values selected from this property. "qcom,override-corner-acc-map" must contain the same number of tuples as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one in the order specified. If the "qcom,override-fuse-version-map" property is not specified, then "qcom,override-corner-acc-map" must contain a single tuple which is then applied unconditionally. - qcom,override-l1-acc-custom-data: Array of tuples of which overrides the existing l1-acc-custom data (specified by qcom,l1-acc-custom-data), with values specified in this property. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-11-custom" reg-property. The length of the array should be equal to number of APC corners. This property can only be specified if the "qcom,l1-acc-custom-data" is already defined. - qcom,override-l2-acc-custom-data: Array which overrides the existing l2-acc-custom data (specified by qcom,l2-acc-custom-data) with values specified in this property. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-l2-custom" reg-property. The length of the array should be equal to number of APC corners. This property can only be specified if the " qcom,l2-acc-custom-data" is already defined. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-11-custom" reg-property. This property can only be specified if the "qcom,l1-acc-custom-data" is already defined. If the "qcom,override-fuse-version-map" property is specified, then qcom,override-l1-acc-custom-data must contain the same number of tuples as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one in the order specified. If the qcom,override-fuse-version-map property is not specified, then "qcom,override-l1-acc-custom-data" must contain a single tuple which is then applied unconditionally. - qcom,override-l2-acc-custom-data: Array of tuples of which overrides the existing l1-acc-custom data (specified by qcom,l2-acc-custom-data), with values specified in this property. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-12-custom" reg-property. This property can only be specified if the "qcom,l2-acc-custom-data" is already defined. If the "qcom,override-fuse-version-map" property is specified, then "qcom,override-l2-acc-custom-data" must contain the same number of tuples as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one in the order specified. If the qcom,override-fuse-version-map property is not specified, then "qcom,override-l2-acc-custom-data" must contain a single tuple which is then applied unconditionally. - qcom,mem-acc-type1: Array which specifies the value to be written to the mem acc type1 register for each fuse corner, from the lowest fuse corner to the highest fuse corner. The length of the array must be equal to the number of APC fuse corners. This property must be present if reg names Loading Loading @@ -139,11 +135,18 @@ mem_acc_vreg_corner: regulator@fd4aa044 { qcom,acc-sel-l1-bit-size = <2>; qcom,acc-sel-l2-bit-size = <2>; qcom,corner-acc-map = <0 1 3>; qcom,l1-config-skip-fuse-sel = <0 52 1 1 0>; qcom,l2-acc-custom-data = <0x0 0x3000 0x3000>; qcom,override-acc-fuse-sel = <0 52 1 1 0>; qcom,override-corner-acc-map = <0 0 1>; qcom,overide-l2-acc-custom-data = <0x0 0x0 0x3000>; qcom,override-acc-fuse-sel = <0 52 2 0>; qcom,override-fuse-version-map = <0>, <2>, <(-1)>; qcom,override-corner-acc-map = <0 0 1>, <0 1 2>, <0 1 1>; qcom,overide-l2-acc-custom-data = <0x0 0x0 0x3000>, <0x0 0x3000 0x3000>, <0x0 0x0 0x0>; qcom,mem-acc-type1 = <0x02 0x02 0x00>; qcom,mem-acc-type2 = <0x02 0x02 0x00>; qcom,mem-acc-type3 = <0x02 0x02 0x00>; Loading arch/arm/boot/dts/qcom/msm8952-regulator.dtsi +16 −4 Original line number Diff line number Diff line Loading @@ -308,15 +308,26 @@ &soc { mem_acc_vreg_corner: regulator@01942130 { compatible = "qcom,mem-acc-regulator"; reg = <0x1942130 0x4>; reg-names = "acc-sel-l1"; reg = <0x1942130 0x4>, <0x5c000 0x1000>; reg-names = "acc-sel-l1", "efuse_addr"; regulator-name = "mem_acc_corner"; regulator-min-microvolt = <1>; regulator-max-microvolt = <3>; regulator-max-microvolt = <4>; qcom,acc-sel-l1-bit-pos = <0>; qcom,acc-sel-l1-bit-size = <16>; qcom,corner-acc-map = <0x0 0x5555 0x5555>; qcom,corner-acc-map = <0x0 0x5454 0x5555 0xFFFF>; qcom,override-acc-fuse-sel = <29 43 2 0>; qcom,override-fuse-version-map = <0>, <1>, <2>, <3>; qcom,override-corner-acc-map = <0x0 0x5454 0x5555 0xFFFF>, <0x0 0x5400 0x5555 0xFFFF>, <0x0 0x0054 0x5555 0xFFFF>, <0x0 0x0 0x5555 0xFFFF>; }; apc_vreg_corner: regulator@b018000 { Loading Loading @@ -356,6 +367,7 @@ <RPM_SMD_REGULATOR_LEVEL_TURBO>; mem-acc-supply = <&mem_acc_vreg_corner>; qcom,mem-acc-corner-map = <1 2 2 2 3 4 4 4>; qcom,cpr-ref-clk = <19200>; qcom,cpr-timer-delay = <5000>; Loading drivers/regulator/mem-acc-regulator.c +154 −87 Original line number Diff line number Diff line Loading @@ -32,8 +32,8 @@ #define BYTES_PER_FUSE_ROW 8 /* mem-acc config flags */ #define MEM_ACC_SKIP_L1_CONFIG BIT(0) #define MEM_ACC_OVERRIDE_CONFIG BIT(1) #define FUSE_MAP_NO_MATCH (-1) #define FUSE_PARAM_MATCH_ANY (-1) enum { MEMORY_L1, Loading @@ -60,6 +60,10 @@ struct mem_acc_regulator { u32 num_acc_en; u32 *corner_acc_map; u32 num_corners; u32 override_fuse_value; int override_map_match; int override_map_count; void __iomem *acc_sel_base[MEMORY_MAX]; void __iomem *acc_en_base; Loading Loading @@ -129,28 +133,6 @@ static u64 mem_acc_read_efuse_row(struct mem_acc_regulator *mem_acc_vreg, return efuse_bits; } static int mem_acc_fuse_is_setting_expected( struct mem_acc_regulator *mem_acc_vreg, u32 sel_array[5]) { u64 fuse_bits; u32 ret; fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, sel_array[0], sel_array[4]); ret = (fuse_bits >> sel_array[1]) & ((1 << sel_array[2]) - 1); if (ret == sel_array[3]) ret = 1; else ret = 0; pr_info("[row:%d] = 0x%llx @%d:%d == %d ?: %s\n", sel_array[0], fuse_bits, sel_array[1], sel_array[2], sel_array[3], (ret == 1) ? "yes" : "no"); return ret; } static inline u32 apc_to_acc_corner(struct mem_acc_regulator *mem_acc_vreg, int corner) { Loading @@ -166,14 +148,6 @@ static void __update_acc_sel(struct mem_acc_regulator *mem_acc_vreg, { u32 acc_data, acc_data_old, i, bit, acc_corner; /* * Do not configure the L1 ACC corner if the the corresponding flag is * set. */ if ((mem_type == MEMORY_L1) && (mem_acc_vreg->flags & MEM_ACC_SKIP_L1_CONFIG)) return; acc_data = readl_relaxed(mem_acc_vreg->acc_sel_base[mem_type]); acc_data_old = acc_data; for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) { Loading Loading @@ -422,8 +396,7 @@ static int mem_acc_efuse_init(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct resource *res; int len, rc = 0; u32 l1_config_skip_fuse_sel[5]; int len; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse_addr"); if (!res || !res->start) { Loading @@ -445,24 +418,6 @@ static int mem_acc_efuse_init(struct platform_device *pdev, return -EINVAL; } if (of_find_property(mem_acc_vreg->dev->of_node, "qcom,l1-config-skip-fuse-sel", NULL)) { rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node, "qcom,l1-config-skip-fuse-sel", l1_config_skip_fuse_sel, 5); if (rc < 0) { pr_err("Read failed - qcom,l1-config-skip-fuse-sel rc=%d\n", rc); return rc; } if (mem_acc_fuse_is_setting_expected(mem_acc_vreg, l1_config_skip_fuse_sel)) { mem_acc_vreg->flags |= MEM_ACC_SKIP_L1_CONFIG; pr_debug("Skip L1 configuration enabled\n"); } } return 0; } Loading Loading @@ -532,7 +487,9 @@ static int override_mem_acc_custom_data(struct platform_device *pdev, int mem_type) { char *custom_apc_data_str; int len, rc = 0; int len, rc = 0, i; int tuple_count, tuple_match; u32 index = 0, value = 0; switch (mem_type) { case MEMORY_L1: Loading @@ -547,37 +504,148 @@ static int override_mem_acc_custom_data(struct platform_device *pdev, } if (!of_find_property(mem_acc_vreg->dev->of_node, custom_apc_data_str, NULL)) { custom_apc_data_str, &len)) { pr_debug("%s not specified\n", custom_apc_data_str); return 0; } /* Free old custom data */ devm_kfree(&pdev->dev, mem_acc_vreg->acc_custom_data[mem_type]); 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; } /* Populate override custom data */ rc = populate_acc_data(mem_acc_vreg, custom_apc_data_str, &mem_acc_vreg->acc_custom_data[mem_type], &len); if (len != mem_acc_vreg->num_corners * tuple_count * sizeof(u32)) { pr_err("%s length=%d is invalid\n", custom_apc_data_str, len); return -EINVAL; } for (i = 0; i < mem_acc_vreg->num_corners; i++) { index = (tuple_match * mem_acc_vreg->num_corners) + i; rc = of_property_read_u32_index(mem_acc_vreg->dev->of_node, custom_apc_data_str, index, &value); if (rc) { pr_err("Unable to find %s rc=%d\n", custom_apc_data_str, rc); pr_err("Unable read %s index %u, rc=%d\n", custom_apc_data_str, index, rc); return rc; } mem_acc_vreg->acc_custom_data[mem_type][i] = value; } if (mem_acc_vreg->num_corners != len) { pr_err("Override custom data is not present for all the corners\n"); return 0; } static int mem_acc_override_corner_map(struct mem_acc_regulator *mem_acc_vreg) { int len = 0, i, rc; int tuple_count, tuple_match; u32 index = 0, value = 0; char *prop_str = "qcom,override-corner-acc-map"; if (!of_find_property(mem_acc_vreg->dev->of_node, prop_str, &len)) 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; } if (len != mem_acc_vreg->num_corners * tuple_count * sizeof(u32)) { pr_err("%s length=%d is invalid\n", prop_str, len); return -EINVAL; } for (i = 0; i < mem_acc_vreg->num_corners; i++) { index = (tuple_match * mem_acc_vreg->num_corners) + i; rc = of_property_read_u32_index(mem_acc_vreg->dev->of_node, prop_str, index, &value); if (rc) { pr_err("Unable read %s index %u, rc=%d\n", prop_str, index, rc); return rc; } mem_acc_vreg->corner_acc_map[i] = value; } return 0; } static int mem_acc_find_override_map_match(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = pdev->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; if (!of_find_property(of_node, prop_str, &len)) { /* No mapping present. */ return 0; } 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; } tmp = kzalloc(len, GFP_KERNEL); if (!tmp) return -ENOMEM; rc = of_property_read_u32_array(of_node, prop_str, tmp, mem_acc_vreg->override_map_count * tuple_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++) { if (tmp[i * tuple_size] != mem_acc_vreg->override_fuse_value && tmp[i * tuple_size] != FUSE_PARAM_MATCH_ANY) { continue; } else { mem_acc_vreg->override_map_match = i; break; } } if (mem_acc_vreg->override_map_match != FUSE_MAP_NO_MATCH) pr_debug("%s tuple match found: %d\n", prop_str, mem_acc_vreg->override_map_match); else pr_err("%s tuple match not found\n", prop_str); done: kfree(tmp); return rc; } #define MEM_TYPE_STRING_LEN 20 static int mem_acc_init(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct resource *res; int len, rc, i, j; u32 override_acc_fuse_sel[5]; u32 fuse_sel[4]; u64 fuse_bits; bool acc_type_present = false; char tmps[MEM_TYPE_STRING_LEN]; Loading Loading @@ -692,38 +760,37 @@ 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", override_acc_fuse_sel, 5); "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; } if (mem_acc_fuse_is_setting_expected(mem_acc_vreg, override_acc_fuse_sel)) { mem_acc_vreg->flags |= MEM_ACC_OVERRIDE_CONFIG; pr_debug("Apply ACC override configuration\n"); } 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); if (rc) { pr_err("Unable to find fuse map match rc=%d\n", rc); return rc; } if (mem_acc_vreg->flags & MEM_ACC_OVERRIDE_CONFIG) { if (of_find_property(mem_acc_vreg->dev->of_node, "qcom,override-corner-acc-map", NULL)) { /* Free old corner-acc-map */ devm_kfree(&pdev->dev, mem_acc_vreg->corner_acc_map); pr_debug("override_fuse_val=%d override_map_match=%d\n", mem_acc_vreg->override_fuse_value, mem_acc_vreg->override_map_match); /* Populate override corner acc map */ rc = populate_acc_data(mem_acc_vreg, "qcom,override-corner-acc-map", &mem_acc_vreg->corner_acc_map, &mem_acc_vreg->num_corners); rc = mem_acc_override_corner_map(mem_acc_vreg); if (rc) { pr_err("Unable to find 'qcom,overrie-corner-acc-map' rc=%d\n", rc); pr_err("Unable to override corner map rc=%d\n", rc); return rc; } } for (i = 0; i < MEMORY_MAX; i++) { rc = override_mem_acc_custom_data(pdev, Loading Loading
Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt +51 −48 Original line number Diff line number Diff line Loading @@ -53,21 +53,6 @@ Optional properties: defined by the qcom,acc-sel-l2-bit-size property. This N-bit value specifies the corner value used by the accelerator for the L2 cache. - qcom,l1-config-skip-fuse-sel: Array of 5 elements to indicate where to read the bits, what value to compare with in order to decide whether to skip configuring the L1 accelerator or not while changing the APC corner and method to read fuse row, using SCM to read or read register directly. The 5 elements with index [0..4] are: [0] => the fuse row number of the selector [1] => LSB bit position of the bits [2] => number of bits [3] => the value to select skip L1 config logic [4] => fuse reading method, 0 for direct reading or 1 for SCM reading When the value of the fuse bits specified by first 3 elements equals to the value in 4th element, L1 accelerator configuration logic is skipped. Otherwise, the original configuration sent from corner map should be applied. If the 5th element is 0, read the fuse row from register directly. Otherwise, read it through SCM. - qcom,l1-acc-custom-data: Array which maps APC corner values to L1 ACC custom data values. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address Loading @@ -78,39 +63,50 @@ Optional properties: while switching between APC corners. The custom register address is specified by "acc-l2-custom" reg-property. The length of the array should be equal to number of APC corners. - qcom,override-acc-fuse-sel: Array of 5 elements to indicate where to read the bits, what value to compare with in order to decide whether to apply override acc-map and custom data or not and method to read fuse row, using SCM to read or read register directly. The 5 elements with index [0..4] are: - qcom,override-acc-fuse-sel: Array of 4 elements which specify the way to read the override fuse. The override fuse value is used by the qcom,override-fuse-version-map to identify the correct set of override properties. The 4 elements with index [0..4] are: [0] => the fuse row number of the selector [1] => LSB bit position of the bits [2] => number of bits [3] => the value to select override ACC configuration [4] => fuse reading method, 0 for direct reading or 1 for SCM reading When the value of the fuse bits specified by first 3 elements equals to the value in 4th element, overridden accelerator configuration logic is applied. Otherwise, the original configuration should be applied. If the 5th element is 0, read the fuse row from register directly. Otherwise, read it through SCM. - qcom,override-corner-acc-map: Array which overrides the existing acc-corner map (specified by qcom,corner-acc-map) with corner values specified in this property. [0] maps APC SVS corner (1) to accelerator SVS corner [1] maps APC NOMINAL corner (2) to accelerator NOMINAL corner [2] maps APC TURBO corner (3) to accelerator TURBO corner - qcom,override-l1-acc-custom-data: Array which overrides the existing l1-acc-custom data [3] => fuse reading method, 0 for direct reading or 1 for SCM reading - qcom,override-fuse-version-map: Array of integers which each match to a override fuse value. Any element in a tuple may use the value 0xffffffff as a wildcard. The index of the first value (in the array) which matches the override fuse is used to select the right tuples from the other override properties. - qcom,override-corner-acc-map: Array of tuples which overrides the existing acc-corner map (specified by qcom,corner-acc-map) with corner values selected from this property. "qcom,override-corner-acc-map" must contain the same number of tuples as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one in the order specified. If the "qcom,override-fuse-version-map" property is not specified, then "qcom,override-corner-acc-map" must contain a single tuple which is then applied unconditionally. - qcom,override-l1-acc-custom-data: Array of tuples of which overrides the existing l1-acc-custom data (specified by qcom,l1-acc-custom-data), with values specified in this property. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-11-custom" reg-property. The length of the array should be equal to number of APC corners. This property can only be specified if the "qcom,l1-acc-custom-data" is already defined. - qcom,override-l2-acc-custom-data: Array which overrides the existing l2-acc-custom data (specified by qcom,l2-acc-custom-data) with values specified in this property. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-l2-custom" reg-property. The length of the array should be equal to number of APC corners. This property can only be specified if the " qcom,l2-acc-custom-data" is already defined. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-11-custom" reg-property. This property can only be specified if the "qcom,l1-acc-custom-data" is already defined. If the "qcom,override-fuse-version-map" property is specified, then qcom,override-l1-acc-custom-data must contain the same number of tuples as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one in the order specified. If the qcom,override-fuse-version-map property is not specified, then "qcom,override-l1-acc-custom-data" must contain a single tuple which is then applied unconditionally. - qcom,override-l2-acc-custom-data: Array of tuples of which overrides the existing l1-acc-custom data (specified by qcom,l2-acc-custom-data), with values specified in this property. The corresponding custom data is written into the custom register while switching between APC corners. The custom register address is specified by "acc-12-custom" reg-property. This property can only be specified if the "qcom,l2-acc-custom-data" is already defined. If the "qcom,override-fuse-version-map" property is specified, then "qcom,override-l2-acc-custom-data" must contain the same number of tuples as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one in the order specified. If the qcom,override-fuse-version-map property is not specified, then "qcom,override-l2-acc-custom-data" must contain a single tuple which is then applied unconditionally. - qcom,mem-acc-type1: Array which specifies the value to be written to the mem acc type1 register for each fuse corner, from the lowest fuse corner to the highest fuse corner. The length of the array must be equal to the number of APC fuse corners. This property must be present if reg names Loading Loading @@ -139,11 +135,18 @@ mem_acc_vreg_corner: regulator@fd4aa044 { qcom,acc-sel-l1-bit-size = <2>; qcom,acc-sel-l2-bit-size = <2>; qcom,corner-acc-map = <0 1 3>; qcom,l1-config-skip-fuse-sel = <0 52 1 1 0>; qcom,l2-acc-custom-data = <0x0 0x3000 0x3000>; qcom,override-acc-fuse-sel = <0 52 1 1 0>; qcom,override-corner-acc-map = <0 0 1>; qcom,overide-l2-acc-custom-data = <0x0 0x0 0x3000>; qcom,override-acc-fuse-sel = <0 52 2 0>; qcom,override-fuse-version-map = <0>, <2>, <(-1)>; qcom,override-corner-acc-map = <0 0 1>, <0 1 2>, <0 1 1>; qcom,overide-l2-acc-custom-data = <0x0 0x0 0x3000>, <0x0 0x3000 0x3000>, <0x0 0x0 0x0>; qcom,mem-acc-type1 = <0x02 0x02 0x00>; qcom,mem-acc-type2 = <0x02 0x02 0x00>; qcom,mem-acc-type3 = <0x02 0x02 0x00>; Loading
arch/arm/boot/dts/qcom/msm8952-regulator.dtsi +16 −4 Original line number Diff line number Diff line Loading @@ -308,15 +308,26 @@ &soc { mem_acc_vreg_corner: regulator@01942130 { compatible = "qcom,mem-acc-regulator"; reg = <0x1942130 0x4>; reg-names = "acc-sel-l1"; reg = <0x1942130 0x4>, <0x5c000 0x1000>; reg-names = "acc-sel-l1", "efuse_addr"; regulator-name = "mem_acc_corner"; regulator-min-microvolt = <1>; regulator-max-microvolt = <3>; regulator-max-microvolt = <4>; qcom,acc-sel-l1-bit-pos = <0>; qcom,acc-sel-l1-bit-size = <16>; qcom,corner-acc-map = <0x0 0x5555 0x5555>; qcom,corner-acc-map = <0x0 0x5454 0x5555 0xFFFF>; qcom,override-acc-fuse-sel = <29 43 2 0>; qcom,override-fuse-version-map = <0>, <1>, <2>, <3>; qcom,override-corner-acc-map = <0x0 0x5454 0x5555 0xFFFF>, <0x0 0x5400 0x5555 0xFFFF>, <0x0 0x0054 0x5555 0xFFFF>, <0x0 0x0 0x5555 0xFFFF>; }; apc_vreg_corner: regulator@b018000 { Loading Loading @@ -356,6 +367,7 @@ <RPM_SMD_REGULATOR_LEVEL_TURBO>; mem-acc-supply = <&mem_acc_vreg_corner>; qcom,mem-acc-corner-map = <1 2 2 2 3 4 4 4>; qcom,cpr-ref-clk = <19200>; qcom,cpr-timer-delay = <5000>; Loading
drivers/regulator/mem-acc-regulator.c +154 −87 Original line number Diff line number Diff line Loading @@ -32,8 +32,8 @@ #define BYTES_PER_FUSE_ROW 8 /* mem-acc config flags */ #define MEM_ACC_SKIP_L1_CONFIG BIT(0) #define MEM_ACC_OVERRIDE_CONFIG BIT(1) #define FUSE_MAP_NO_MATCH (-1) #define FUSE_PARAM_MATCH_ANY (-1) enum { MEMORY_L1, Loading @@ -60,6 +60,10 @@ struct mem_acc_regulator { u32 num_acc_en; u32 *corner_acc_map; u32 num_corners; u32 override_fuse_value; int override_map_match; int override_map_count; void __iomem *acc_sel_base[MEMORY_MAX]; void __iomem *acc_en_base; Loading Loading @@ -129,28 +133,6 @@ static u64 mem_acc_read_efuse_row(struct mem_acc_regulator *mem_acc_vreg, return efuse_bits; } static int mem_acc_fuse_is_setting_expected( struct mem_acc_regulator *mem_acc_vreg, u32 sel_array[5]) { u64 fuse_bits; u32 ret; fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, sel_array[0], sel_array[4]); ret = (fuse_bits >> sel_array[1]) & ((1 << sel_array[2]) - 1); if (ret == sel_array[3]) ret = 1; else ret = 0; pr_info("[row:%d] = 0x%llx @%d:%d == %d ?: %s\n", sel_array[0], fuse_bits, sel_array[1], sel_array[2], sel_array[3], (ret == 1) ? "yes" : "no"); return ret; } static inline u32 apc_to_acc_corner(struct mem_acc_regulator *mem_acc_vreg, int corner) { Loading @@ -166,14 +148,6 @@ static void __update_acc_sel(struct mem_acc_regulator *mem_acc_vreg, { u32 acc_data, acc_data_old, i, bit, acc_corner; /* * Do not configure the L1 ACC corner if the the corresponding flag is * set. */ if ((mem_type == MEMORY_L1) && (mem_acc_vreg->flags & MEM_ACC_SKIP_L1_CONFIG)) return; acc_data = readl_relaxed(mem_acc_vreg->acc_sel_base[mem_type]); acc_data_old = acc_data; for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) { Loading Loading @@ -422,8 +396,7 @@ static int mem_acc_efuse_init(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct resource *res; int len, rc = 0; u32 l1_config_skip_fuse_sel[5]; int len; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse_addr"); if (!res || !res->start) { Loading @@ -445,24 +418,6 @@ static int mem_acc_efuse_init(struct platform_device *pdev, return -EINVAL; } if (of_find_property(mem_acc_vreg->dev->of_node, "qcom,l1-config-skip-fuse-sel", NULL)) { rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node, "qcom,l1-config-skip-fuse-sel", l1_config_skip_fuse_sel, 5); if (rc < 0) { pr_err("Read failed - qcom,l1-config-skip-fuse-sel rc=%d\n", rc); return rc; } if (mem_acc_fuse_is_setting_expected(mem_acc_vreg, l1_config_skip_fuse_sel)) { mem_acc_vreg->flags |= MEM_ACC_SKIP_L1_CONFIG; pr_debug("Skip L1 configuration enabled\n"); } } return 0; } Loading Loading @@ -532,7 +487,9 @@ static int override_mem_acc_custom_data(struct platform_device *pdev, int mem_type) { char *custom_apc_data_str; int len, rc = 0; int len, rc = 0, i; int tuple_count, tuple_match; u32 index = 0, value = 0; switch (mem_type) { case MEMORY_L1: Loading @@ -547,37 +504,148 @@ static int override_mem_acc_custom_data(struct platform_device *pdev, } if (!of_find_property(mem_acc_vreg->dev->of_node, custom_apc_data_str, NULL)) { custom_apc_data_str, &len)) { pr_debug("%s not specified\n", custom_apc_data_str); return 0; } /* Free old custom data */ devm_kfree(&pdev->dev, mem_acc_vreg->acc_custom_data[mem_type]); 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; } /* Populate override custom data */ rc = populate_acc_data(mem_acc_vreg, custom_apc_data_str, &mem_acc_vreg->acc_custom_data[mem_type], &len); if (len != mem_acc_vreg->num_corners * tuple_count * sizeof(u32)) { pr_err("%s length=%d is invalid\n", custom_apc_data_str, len); return -EINVAL; } for (i = 0; i < mem_acc_vreg->num_corners; i++) { index = (tuple_match * mem_acc_vreg->num_corners) + i; rc = of_property_read_u32_index(mem_acc_vreg->dev->of_node, custom_apc_data_str, index, &value); if (rc) { pr_err("Unable to find %s rc=%d\n", custom_apc_data_str, rc); pr_err("Unable read %s index %u, rc=%d\n", custom_apc_data_str, index, rc); return rc; } mem_acc_vreg->acc_custom_data[mem_type][i] = value; } if (mem_acc_vreg->num_corners != len) { pr_err("Override custom data is not present for all the corners\n"); return 0; } static int mem_acc_override_corner_map(struct mem_acc_regulator *mem_acc_vreg) { int len = 0, i, rc; int tuple_count, tuple_match; u32 index = 0, value = 0; char *prop_str = "qcom,override-corner-acc-map"; if (!of_find_property(mem_acc_vreg->dev->of_node, prop_str, &len)) 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; } if (len != mem_acc_vreg->num_corners * tuple_count * sizeof(u32)) { pr_err("%s length=%d is invalid\n", prop_str, len); return -EINVAL; } for (i = 0; i < mem_acc_vreg->num_corners; i++) { index = (tuple_match * mem_acc_vreg->num_corners) + i; rc = of_property_read_u32_index(mem_acc_vreg->dev->of_node, prop_str, index, &value); if (rc) { pr_err("Unable read %s index %u, rc=%d\n", prop_str, index, rc); return rc; } mem_acc_vreg->corner_acc_map[i] = value; } return 0; } static int mem_acc_find_override_map_match(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = pdev->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; if (!of_find_property(of_node, prop_str, &len)) { /* No mapping present. */ return 0; } 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; } tmp = kzalloc(len, GFP_KERNEL); if (!tmp) return -ENOMEM; rc = of_property_read_u32_array(of_node, prop_str, tmp, mem_acc_vreg->override_map_count * tuple_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++) { if (tmp[i * tuple_size] != mem_acc_vreg->override_fuse_value && tmp[i * tuple_size] != FUSE_PARAM_MATCH_ANY) { continue; } else { mem_acc_vreg->override_map_match = i; break; } } if (mem_acc_vreg->override_map_match != FUSE_MAP_NO_MATCH) pr_debug("%s tuple match found: %d\n", prop_str, mem_acc_vreg->override_map_match); else pr_err("%s tuple match not found\n", prop_str); done: kfree(tmp); return rc; } #define MEM_TYPE_STRING_LEN 20 static int mem_acc_init(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct resource *res; int len, rc, i, j; u32 override_acc_fuse_sel[5]; u32 fuse_sel[4]; u64 fuse_bits; bool acc_type_present = false; char tmps[MEM_TYPE_STRING_LEN]; Loading Loading @@ -692,38 +760,37 @@ 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", override_acc_fuse_sel, 5); "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; } if (mem_acc_fuse_is_setting_expected(mem_acc_vreg, override_acc_fuse_sel)) { mem_acc_vreg->flags |= MEM_ACC_OVERRIDE_CONFIG; pr_debug("Apply ACC override configuration\n"); } 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); if (rc) { pr_err("Unable to find fuse map match rc=%d\n", rc); return rc; } if (mem_acc_vreg->flags & MEM_ACC_OVERRIDE_CONFIG) { if (of_find_property(mem_acc_vreg->dev->of_node, "qcom,override-corner-acc-map", NULL)) { /* Free old corner-acc-map */ devm_kfree(&pdev->dev, mem_acc_vreg->corner_acc_map); pr_debug("override_fuse_val=%d override_map_match=%d\n", mem_acc_vreg->override_fuse_value, mem_acc_vreg->override_map_match); /* Populate override corner acc map */ rc = populate_acc_data(mem_acc_vreg, "qcom,override-corner-acc-map", &mem_acc_vreg->corner_acc_map, &mem_acc_vreg->num_corners); rc = mem_acc_override_corner_map(mem_acc_vreg); if (rc) { pr_err("Unable to find 'qcom,overrie-corner-acc-map' rc=%d\n", rc); pr_err("Unable to override corner map rc=%d\n", rc); return rc; } } for (i = 0; i < MEMORY_MAX; i++) { rc = override_mem_acc_custom_data(pdev, Loading