regulator: cpr2-gfx: add support for fuse bit remapping

On certain targets some fuse parameters are split between
non-contiguous fuse rows.  The various device tree fuse
specification properties cannot handle such a layout.  Therefore,
add support for a pair of device tree properties which can be
used to remap arbitrary combinations of fuse bits into a single
contiguous range of virtual fuse rows.  These remapped fuse rows
can then be referenced by all other device tree fuse properties.

CRs-Fixed: 922440
Change-Id: I6ae32ecadb6cc8d427f5e47bdd665d899e56d181
Signed-off-by: Tirupathi Reddy <tirupath@codeaurora.org>
This commit is contained in:
Tirupathi Reddy 2015-09-10 16:02:15 +05:30
parent 68e98d2ca2
commit 84e0bd4396
2 changed files with 181 additions and 2 deletions

View File

@ -320,6 +320,52 @@ This document describes the bindings that apply for GFX CPR controller.
should operate in closed-loop mode (i.e. CPR sensing loop
enabled) as opposed to open-loop mode (i.e. CPR sensing loop
disabled) by default.
- qcom,fuse-remap-source
Usage: optional
Value type: <prop-encoded-array>
Definition: Array of triplets in which each triplet specifies a fuse location to
remap. The 3 elements in one triplet are:
[0] => fuse row number
[1] => bit offset within the row
[2] => number of bits in the fuse parameter
The fuse bits for all triplets are packed together in the order specified
into 64-bit virtual fuse rows beginning at the row number defined in the
qcom,fuse-remap-base-row property. The remapped rows may be used by any
other properties.
Example:
qcom,fuse-remap-base-row = <1000>;
qcom,fuse-remap-source =
<13 57 2>,
<14 30 3>,
<20 1 7>,
<40 47 120>;
This results in the following bit remapping:
Row Bits Remap Row Remap Bits
13 57..58 --> 1000 0..1
14 30..32 --> 1000 2..4
20 1..7 --> 1000 5..11
40 47..63 --> 1000 12..28
41 0..34 --> 1000 29..63
41 35..63 --> 1001 0..28
42 0..34 --> 1001 29..63
42 35..38 --> 1002 0..3
A tuple like this could then be used to reference some of the
concatenated bits from rows 13, 14, and 20:
qcom,cpr-fuse-init-voltage = <1000 0 6>;
- qcom,fuse-remap-base-row
Usage: required if qcom,fuse-remap-source specified.
Value type: <u32>
Definition: Integer which defines the virtual row number to use as a base when remapping
fuse bits. The remap base row number can be any value as long as it is
greater than all of the real row numbers addressed in other properties of
the cpr2-gfx-regulator device node.
=======
Example
=======
@ -392,4 +438,9 @@ gfx_vreg_corner: regulator@98000 {
<1010 971 1138 1080 579 626 428 479>,
<1086 1039 1210 1146 643 684 489 538>,
<1238 1179 1367 1290 752 789 586 633>;
qcom,fuse-remap-base-row = <1000>;
qcom,fuse-remap-source =
<59 12 6>,
<72 30 2>;
};

View File

@ -163,6 +163,9 @@ struct cpr2_gfx_regulator {
/* eFuse parameters */
phys_addr_t efuse_addr;
void __iomem *efuse_base;
u64 *remapped_row;
u32 remapped_row_base;
int num_remapped_rows;
/* Process voltage parameters */
int *open_loop_volt;
@ -253,10 +256,27 @@ module_param_named(debug_enable, cpr_debug_enable, int, S_IRUGO | S_IWUSR);
#define cpr_err(cpr_vreg, message, ...) \
pr_err("%s: " message, (cpr_vreg)->rdesc.name, ##__VA_ARGS__)
static u64 cpr_read_remapped_efuse_row(struct cpr2_gfx_regulator *cpr_vreg,
u32 row_num)
{
if (row_num - cpr_vreg->remapped_row_base
>= cpr_vreg->num_remapped_rows) {
cpr_err(cpr_vreg, "invalid row=%u, max remapped row=%u\n",
row_num, cpr_vreg->remapped_row_base
+ cpr_vreg->num_remapped_rows - 1);
return 0;
}
return cpr_vreg->remapped_row[row_num - cpr_vreg->remapped_row_base];
}
static u64 cpr_read_efuse_row(struct cpr2_gfx_regulator *cpr_vreg, u32 row_num)
{
u64 efuse_bits;
if (cpr_vreg->remapped_row && row_num >= cpr_vreg->remapped_row_base)
return cpr_read_remapped_efuse_row(cpr_vreg, row_num);
efuse_bits = readq_relaxed(cpr_vreg->efuse_base
+ row_num * BYTES_PER_FUSE_ROW);
return efuse_bits;
@ -1156,6 +1176,108 @@ static int cpr_mem_acc_init(struct cpr2_gfx_regulator *cpr_vreg)
return 0;
}
/*
* Create a set of virtual fuse rows if optional device tree properties are
* present.
*/
static int cpr_remap_efuse_data(struct cpr2_gfx_regulator *cpr_vreg)
{
struct device_node *of_node = cpr_vreg->dev->of_node;
struct property *prop;
u64 fuse_param;
u32 *temp;
int size, rc, i, bits, in_row, in_bit, out_row, out_bit;
prop = of_find_property(of_node, "qcom,fuse-remap-source", NULL);
if (!prop) {
/* No fuse remapping needed. */
return 0;
}
size = prop->length / sizeof(u32);
if (size == 0 || size % 3) {
cpr_err(cpr_vreg, "qcom,fuse-remap-source has invalid size=%d\n",
size);
return -EINVAL;
}
size /= 3;
rc = of_property_read_u32(of_node, "qcom,fuse-remap-base-row",
&cpr_vreg->remapped_row_base);
if (rc) {
cpr_err(cpr_vreg, "could not read qcom,fuse-remap-base-row, rc=%d\n",
rc);
return rc;
}
temp = kcalloc(size * 3, sizeof(*temp), GFP_KERNEL);
if (!temp)
return -ENOMEM;
rc = of_property_read_u32_array(of_node, "qcom,fuse-remap-source", temp,
size * 3);
if (rc) {
cpr_err(cpr_vreg, "could not read qcom,fuse-remap-source, rc=%d\n",
rc);
goto done;
}
/*
* Format of tuples in qcom,fuse-remap-source property:
* <row bit-offset bit-count>
*/
for (i = 0, bits = 0; i < size; i++)
bits += temp[i * 3 + 2];
cpr_vreg->num_remapped_rows = DIV_ROUND_UP(bits, 64);
cpr_vreg->remapped_row = devm_kzalloc(cpr_vreg->dev,
sizeof(*cpr_vreg->remapped_row) * cpr_vreg->num_remapped_rows,
GFP_KERNEL);
if (!cpr_vreg->remapped_row) {
cpr_err(cpr_vreg, "remapped_row memory allocation failed\n");
rc = -ENOMEM;
goto done;
}
for (i = 0, out_row = 0, out_bit = 0; i < size; i++) {
in_row = temp[i * 3];
in_bit = temp[i * 3 + 1];
bits = temp[i * 3 + 2];
while (bits > 64) {
fuse_param = cpr_read_efuse_param(cpr_vreg, in_row,
in_bit, 64);
cpr_vreg->remapped_row[out_row++]
|= fuse_param << out_bit;
if (out_bit > 0)
cpr_vreg->remapped_row[out_row]
|= fuse_param >> (64 - out_bit);
bits -= 64;
in_bit += 64;
}
fuse_param = cpr_read_efuse_param(cpr_vreg, in_row, in_bit,
bits);
cpr_vreg->remapped_row[out_row] |= fuse_param << out_bit;
if (bits < 64 - out_bit) {
out_bit += bits;
} else {
out_row++;
if (out_bit > 0)
cpr_vreg->remapped_row[out_row]
|= fuse_param >> (64 - out_bit);
out_bit = bits - (64 - out_bit);
}
}
done:
kfree(temp);
return rc;
}
static int cpr_efuse_init(struct platform_device *pdev,
struct cpr2_gfx_regulator *cpr_vreg)
{
@ -1568,8 +1690,8 @@ static int cpr_parse_vdd_mx_parameters(struct cpr2_gfx_regulator *cpr_vreg)
return -EINVAL;
}
cpr_vreg->vdd_mx_corner_map = devm_kzalloc(cpr_vreg->dev,
(size + 1) * sizeof(*cpr_vreg->vdd_mx_corner_map),
cpr_vreg->vdd_mx_corner_map = devm_kcalloc(cpr_vreg->dev,
(size + 1), sizeof(*cpr_vreg->vdd_mx_corner_map),
GFP_KERNEL);
if (!cpr_vreg->vdd_mx_corner_map) {
cpr_err(cpr_vreg,
@ -2332,6 +2454,12 @@ static int cpr2_gfx_regulator_probe(struct platform_device *pdev)
return rc;
}
rc = cpr_remap_efuse_data(cpr_vreg);
if (rc) {
cpr_err(cpr_vreg, "Could not remap fuse data: rc=%d\n", rc);
return rc;
}
rc = cpr_parse_fuse_parameters(cpr_vreg);
if (rc) {
cpr_err(cpr_vreg, "Failed to parse fuse parameters: rc=%d\n",