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
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>;

View File

@ -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 {

View File

@ -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 {

View File

@ -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++) {