diff --git a/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt index 224d651bcffd..d381cfc21f6c 100644 --- a/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt @@ -78,39 +78,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 @@ -141,9 +152,17 @@ mem_acc_vreg_corner: regulator@fd4aa044 { 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>; diff --git a/arch/arm/boot/dts/qcom/msm8939-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8939-regulator.dtsi index f5dab88a3758..faf39bd211ed 100644 --- a/arch/arm/boot/dts/qcom/msm8939-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8939-regulator.dtsi @@ -69,9 +69,15 @@ qcom,corner-acc-map = <0 0 1>; qcom,l2-acc-custom-data = <0x0 0x0 0x3000>; - qcom,override-acc-fuse-sel = <0 30 1 1 0>; - qcom,override-corner-acc-map = <0 1 1>; - qcom,override-l2-acc-custom-data = <0x0 0x3000 0x3000>; + qcom,override-acc-fuse-sel = <0 30 1 0>; + qcom,override-fuse-version-map = <0>, + <1>; + qcom,override-corner-acc-map = + <0 0 1>, + <0 1 1>; + qcom,override-l2-acc-custom-data = + <0x0 0x0 0x3000>, + <0x0 0x3000 0x3000>; }; apc_vreg_corner: regulator@b018000 { diff --git a/arch/arm/boot/dts/qcom/msm8952-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8952-regulator.dtsi index 9837d31feb8a..49364697d8b2 100644 --- a/arch/arm/boot/dts/qcom/msm8952-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8952-regulator.dtsi @@ -317,8 +317,16 @@ qcom,acc-sel-l1-bit-size = <16>; qcom,corner-acc-map = <0x0 0x5454 0x5555 0xFFFF>; - qcom,override-acc-fuse-sel = <29 43 1 1 0>; - qcom,override-corner-acc-map = <0x0 0x0 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 { diff --git a/drivers/regulator/mem-acc-regulator.c b/drivers/regulator/mem-acc-regulator.c index a273bc93c114..8eebb56160d3 100644 --- a/drivers/regulator/mem-acc-regulator.c +++ b/drivers/regulator/mem-acc-regulator.c @@ -33,7 +33,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, @@ -60,6 +61,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; @@ -532,7 +537,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: @@ -547,37 +554,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]); - - /* 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 (rc) { - pr_err("Unable to find %s rc=%d\n", custom_apc_data_str, rc); - return rc; + 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 (mem_acc_vreg->num_corners != len) { - pr_err("Override custom data is not present for all the corners\n"); + 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 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; + } + 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]; @@ -692,37 +810,36 @@ 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); - if (rc) { - pr_err("Unable to find 'qcom,overrie-corner-acc-map' rc=%d\n", - rc); - return rc; - } + rc = mem_acc_override_corner_map(mem_acc_vreg); + if (rc) { + pr_err("Unable to override corner map rc=%d\n", rc); + return rc; } for (i = 0; i < MEMORY_MAX; i++) {