regulator: mem-acc: Select multiple override ACC configurations

Add logic to choose between multiple ACC override configurations
based on the override fuse. The scheme maps each possible override
fuse value to a override configuration.

Update 8952 and 8939 ACC DT configuration accordingly.

CRs-Fixed: 896763
Change-Id: Ia5cc395edab3d9fe34c94af5457462a677b59399
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
This commit is contained in:
Anirudh Ghayal 2015-08-20 18:48:36 +05:30 committed by Gerrit - the friendly Code Review server
parent a0162b1c15
commit e4379c08a6
4 changed files with 224 additions and 74 deletions

View File

@ -78,39 +78,50 @@ Optional properties:
while switching between APC corners. The custom register address while switching between APC corners. The custom register address
is specified by "acc-l2-custom" reg-property. The length of the array is specified by "acc-l2-custom" reg-property. The length of the array
should be equal to number of APC corners. 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 - qcom,override-acc-fuse-sel: Array of 4 elements which specify the way to read the override fuse.
compare with in order to decide whether to The override fuse value is used by the qcom,override-fuse-version-map
apply override acc-map and custom data or not to identify the correct set of override properties.
and method to read fuse row, using SCM to read or read register directly. The 4 elements with index [0..4] are:
The 5 elements with index [0..4] are:
[0] => the fuse row number of the selector [0] => the fuse row number of the selector
[1] => LSB bit position of the bits [1] => LSB bit position of the bits
[2] => number of bits [2] => number of bits
[3] => the value to select override ACC configuration [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
[4] => 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.
When the value of the fuse bits specified by first 3 elements equals to the value Any element in a tuple may use the value 0xffffffff as a wildcard.
in 4th element, overridden accelerator configuration logic is applied. The index of the first value (in the array) which matches the override fuse
Otherwise, the original configuration should be applied. If the 5th element is 0, is used to select the right tuples from the other override properties.
read the fuse row from register directly. Otherwise, read it through SCM. - qcom,override-corner-acc-map: Array of tuples which overrides the existing acc-corner map
- qcom,override-corner-acc-map: Array which overrides the existing acc-corner map (specified by qcom,corner-acc-map) (specified by qcom,corner-acc-map) with corner values selected
with corner values specified in this property. from this property. "qcom,override-corner-acc-map" must contain the
[0] maps APC SVS corner (1) to accelerator SVS corner same number of tuples as "qcom,override-fuse-version-map". These tuples
[1] maps APC NOMINAL corner (2) to accelerator NOMINAL corner are then mapped one-to-one in the order specified. If the
[2] maps APC TURBO corner (3) to accelerator TURBO corner "qcom,override-fuse-version-map" property is not specified, then
- qcom,override-l1-acc-custom-data: Array which overrides the existing l1-acc-custom data "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. (specified by qcom,l1-acc-custom-data), with values specified in this property.
The corresponding custom data is written into the custom register The corresponding custom data is written into the custom register while
while switching between APC corners. The custom register address switching between APC corners. The custom register address is specified by
is specified by "acc-11-custom" reg-property. The length of the array "acc-11-custom" reg-property. This property can only be specified if the
should be equal to number of APC corners. "qcom,l1-acc-custom-data" is already defined. If the
This property can only be specified if the "qcom,l1-acc-custom-data" is already defined. "qcom,override-fuse-version-map" property is specified, then
- qcom,override-l2-acc-custom-data: Array which overrides the existing l2-acc-custom data qcom,override-l1-acc-custom-data must contain the same number of tuples
(specified by qcom,l2-acc-custom-data) with values specified in this property. as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one
The corresponding custom data is written into the custom register in the order specified. If the qcom,override-fuse-version-map property is
while switching between APC corners. The custom register address not specified, then "qcom,override-l1-acc-custom-data" must contain a single
is specified by "acc-l2-custom" reg-property. The length of the array tuple which is then applied unconditionally.
should be equal to number of APC corners. - qcom,override-l2-acc-custom-data: Array of tuples of which overrides the existing l1-acc-custom data
This property can only be specified if the " qcom,l2-acc-custom-data" is already defined. (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 - 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 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 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,corner-acc-map = <0 1 3>;
qcom,l1-config-skip-fuse-sel = <0 52 1 1 0>; qcom,l1-config-skip-fuse-sel = <0 52 1 1 0>;
qcom,l2-acc-custom-data = <0x0 0x3000 0x3000>; 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,override-acc-fuse-sel = <0 52 2 0>;
qcom,overide-l2-acc-custom-data = <0x0 0x0 0x3000>; 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-type1 = <0x02 0x02 0x00>;
qcom,mem-acc-type2 = <0x02 0x02 0x00>; qcom,mem-acc-type2 = <0x02 0x02 0x00>;
qcom,mem-acc-type3 = <0x02 0x02 0x00>; qcom,mem-acc-type3 = <0x02 0x02 0x00>;

View File

@ -69,9 +69,15 @@
qcom,corner-acc-map = <0 0 1>; qcom,corner-acc-map = <0 0 1>;
qcom,l2-acc-custom-data = <0x0 0x0 0x3000>; qcom,l2-acc-custom-data = <0x0 0x0 0x3000>;
qcom,override-acc-fuse-sel = <0 30 1 1 0>; qcom,override-acc-fuse-sel = <0 30 1 0>;
qcom,override-corner-acc-map = <0 1 1>; qcom,override-fuse-version-map = <0>,
qcom,override-l2-acc-custom-data = <0x0 0x3000 0x3000>; <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 { apc_vreg_corner: regulator@b018000 {

View File

@ -317,8 +317,16 @@
qcom,acc-sel-l1-bit-size = <16>; qcom,acc-sel-l1-bit-size = <16>;
qcom,corner-acc-map = <0x0 0x5454 0x5555 0xFFFF>; qcom,corner-acc-map = <0x0 0x5454 0x5555 0xFFFF>;
qcom,override-acc-fuse-sel = <29 43 1 1 0>; qcom,override-acc-fuse-sel = <29 43 2 0>;
qcom,override-corner-acc-map = <0x0 0x0 0x5555 0xFFFF>; 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 { apc_vreg_corner: regulator@b018000 {

View File

@ -33,7 +33,8 @@
/* mem-acc config flags */ /* mem-acc config flags */
#define MEM_ACC_SKIP_L1_CONFIG BIT(0) #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 { enum {
MEMORY_L1, MEMORY_L1,
@ -60,6 +61,10 @@ struct mem_acc_regulator {
u32 num_acc_en; u32 num_acc_en;
u32 *corner_acc_map; u32 *corner_acc_map;
u32 num_corners; 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_sel_base[MEMORY_MAX];
void __iomem *acc_en_base; void __iomem *acc_en_base;
@ -532,7 +537,9 @@ static int override_mem_acc_custom_data(struct platform_device *pdev,
int mem_type) int mem_type)
{ {
char *custom_apc_data_str; 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) { switch (mem_type) {
case MEMORY_L1: 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, 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); pr_debug("%s not specified\n", custom_apc_data_str);
return 0; return 0;
} }
/* Free old custom data */ if (mem_acc_vreg->override_map_count) {
devm_kfree(&pdev->dev, mem_acc_vreg->acc_custom_data[mem_type]); if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH)
return 0;
/* Populate override custom data */ tuple_count = mem_acc_vreg->override_map_count;
rc = populate_acc_data(mem_acc_vreg, custom_apc_data_str, tuple_match = mem_acc_vreg->override_map_match;
&mem_acc_vreg->acc_custom_data[mem_type], &len); } else {
if (rc) { tuple_count = 1;
pr_err("Unable to find %s rc=%d\n", custom_apc_data_str, rc); tuple_match = 0;
return rc;
} }
if (mem_acc_vreg->num_corners != len) { if (len != mem_acc_vreg->num_corners * tuple_count * sizeof(u32)) {
pr_err("Override custom data is not present for all the corners\n"); pr_err("%s length=%d is invalid\n", custom_apc_data_str, len);
return -EINVAL; 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; 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 #define MEM_TYPE_STRING_LEN 20
static int mem_acc_init(struct platform_device *pdev, static int mem_acc_init(struct platform_device *pdev,
struct mem_acc_regulator *mem_acc_vreg) struct mem_acc_regulator *mem_acc_vreg)
{ {
struct resource *res; struct resource *res;
int len, rc, i, j; int len, rc, i, j;
u32 override_acc_fuse_sel[5]; u32 fuse_sel[4];
u64 fuse_bits;
bool acc_type_present = false; bool acc_type_present = false;
char tmps[MEM_TYPE_STRING_LEN]; 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, if (of_find_property(mem_acc_vreg->dev->of_node,
"qcom,override-acc-fuse-sel", NULL)) { "qcom,override-acc-fuse-sel", NULL)) {
rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node, rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
"qcom,override-acc-fuse-sel", "qcom,override-acc-fuse-sel", fuse_sel, 4);
override_acc_fuse_sel, 5);
if (rc < 0) { if (rc < 0) {
pr_err("Read failed - qcom,override-acc-fuse-sel rc=%d\n", pr_err("Read failed - qcom,override-acc-fuse-sel rc=%d\n",
rc); rc);
return rc; return rc;
} }
if (mem_acc_fuse_is_setting_expected(mem_acc_vreg, fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, fuse_sel[0],
override_acc_fuse_sel)) { fuse_sel[3]);
mem_acc_vreg->flags |= MEM_ACC_OVERRIDE_CONFIG; /*
pr_debug("Apply ACC override configuration\n"); * 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) { pr_debug("override_fuse_val=%d override_map_match=%d\n",
if (of_find_property(mem_acc_vreg->dev->of_node, mem_acc_vreg->override_fuse_value,
"qcom,override-corner-acc-map", NULL)) { mem_acc_vreg->override_map_match);
/* Free old corner-acc-map */
devm_kfree(&pdev->dev, mem_acc_vreg->corner_acc_map);
/* Populate override corner acc map */ rc = mem_acc_override_corner_map(mem_acc_vreg);
rc = populate_acc_data(mem_acc_vreg, if (rc) {
"qcom,override-corner-acc-map", pr_err("Unable to override corner map rc=%d\n", rc);
&mem_acc_vreg->corner_acc_map, return rc;
&mem_acc_vreg->num_corners);
if (rc) {
pr_err("Unable to find 'qcom,overrie-corner-acc-map' rc=%d\n",
rc);
return rc;
}
} }
for (i = 0; i < MEMORY_MAX; i++) { for (i = 0; i < MEMORY_MAX; i++) {