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:
parent
a0162b1c15
commit
e4379c08a6
|
@ -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>;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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++) {
|
||||
|
|
Loading…
Reference in New Issue