Loading Documentation/devicetree/bindings/regulator/cpr-regulator.txt +47 −0 Original line number Diff line number Diff line Loading @@ -294,6 +294,22 @@ Optional properties: applied unconditionally. This property can be used to add or subtract static initial voltage margin from the regulator managed by the CPR controller. - qcom,cpr-quot-offset-adjustment: Array of integer tuples of target quotient offset adjustments to add to the fused quotient offsets of each fuse corner. The elements in a tuple are ordered from lowest voltage corner to highest voltage corner. Each tuple must be of length defined by qcom,cpr-fuse-corners. If the qcom,cpr-fuse-version-map property is specified, then qcom,cpr-quot-offset-adjustment must contain the same number of tuples as qcom,cpr-fuse-version-map. These tuples are then mapped one-to-one in the order specified. E.g. if the second qcom,cpr-fuse-version-map tuple matches for a given device, then the quotient offset adjustments defined in the second qcom,cpr-quot-offset-adjustment tuple will be applied. If the qcom,cpr-fuse-version-map property is not specified, then qcom,cpr-quot-offset-adjustment must contain a single tuple which is then applied unconditionally. If this property is specified, then the quotient offset adjustment values are added to the target quotient offset values read from fuses. This property can be used to add or subtract static quotient offset margin from the regulator managed by the CPR controller. - qcom,cpr-clamp-timer-interval: The number of 64 reference clock cycle blocks to delay for whenever the clamp signal, sensor mask registers or sensor bypass registers change. The CPR controller loop is disabled during this delay. Loading Loading @@ -514,6 +530,25 @@ Optional properties: - qcom,cpr-quot-min-diff: Integer which defines the minimum target-quotient difference between the highest and (highest - 1) fuse corner to keep CPR enabled. If this property is not specified a default value of 50 is used. - qcom,cpr-fuse-quot-offset: Array of quadruples in which each quadruple specifies a fuse location to read in order to get the quotient offset for a fuse corner. The fuse values are encoded as the difference between quotients of that fuse corner and its adjacent lower fuse corner divided by an unpacking multiplier. At present, the multiplier is assumed to be same as that of the value defined under qcom,cpr-fuse-target-quot-scale. The 4 elements in one quadruple are: [0]: => the fuse row number of the bits [1]: => LSB bit position of the bits [2]: => number of the bits [3]: => fuse reading method, 0 for direct reading or 1 for SCM reading The quadruples are ordered from the lowest fuse corner to the highest fuse corner. Quotient offset read from the fuse locations above can be overridden with the property qcom,cpr-quot-adjust-scaling-factor-max. - qcom,cpr-redun-fuse-quot-offset: Array of quadruples in which each quadruple specifies a fuse location to read in order to get the redundant quotient offset for a fuse corner. This property is the same as qcom,cpr-fuse-quot-offset except that it is only utilized if a chip is configured to use the redundant set of fuse values. Example: apc_vreg_corner: regulator@f9018000 { Loading Loading @@ -631,6 +666,16 @@ Example: <0 1>, <0 1>; qcom,cpr-quot-adjust-scaling-factor-max = <0 650 650>; qcom,cpr-fuse-quot-offset = <138 53 5 0>, <138 53 5 0>, <138 48 5 0>, <138 58 5 0>; qcom,cpr-fuse-redun-quot-offset = <200 53 5 0>, <200 53 5 0>, <200 48 5 0>, <200 58 5 0>; qcom,cpr-fuse-init-voltage = <27 36 6 0>, <27 18 6 0>, Loading Loading @@ -659,6 +704,8 @@ Example: <0 0 (-210)>, <0 0 (-60)>, <0 0 (-94)>; qcom,cpr-quot-offset-adjustment = <0 0 (-5)>; qcom,cpr-init-voltage-adjustment = <0 0 (-100000)>, <0 0 (-100000)>, Loading drivers/regulator/cpr-regulator.c +144 −9 Original line number Diff line number Diff line Loading @@ -192,6 +192,11 @@ struct quot_adjust_info { int quot_adjust; }; struct cpr_quot_scale { u32 offset; u32 multiplier; }; static const char * const vdd_apc_name[] = {"vdd-apc-optional-prim", "vdd-apc-optional-sec", "vdd-apc"}; Loading Loading @@ -249,6 +254,7 @@ struct cpr_regulator { int cpr_fuse_map_match; int *cpr_fuse_target_quot; int *cpr_fuse_ro_sel; int *fuse_quot_offset; int gcnt; unsigned int cpr_irq; Loading Loading @@ -1935,6 +1941,127 @@ static int cpr_reduce_ceiling_voltage(struct cpr_regulator *cpr_vreg, return 0; } static int cpr_adjust_target_quot_offsets(struct platform_device *pdev, struct cpr_regulator *cpr_vreg) { struct device_node *of_node = pdev->dev.of_node; int tuple_count, tuple_match, i; u32 index; u32 quot_offset_adjust = 0; int len = 0; int rc = 0; char *quot_offset_str; quot_offset_str = "qcom,cpr-quot-offset-adjustment"; if (!of_find_property(of_node, quot_offset_str, &len)) { /* No static quotient adjustment needed. */ return 0; } if (cpr_vreg->cpr_fuse_map_count) { if (cpr_vreg->cpr_fuse_map_match == FUSE_MAP_NO_MATCH) { /* No matching index to use for quotient adjustment. */ return 0; } tuple_count = cpr_vreg->cpr_fuse_map_count; tuple_match = cpr_vreg->cpr_fuse_map_match; } else { tuple_count = 1; tuple_match = 0; } if (len != cpr_vreg->num_fuse_corners * tuple_count * sizeof(u32)) { cpr_err(cpr_vreg, "%s length=%d is invalid\n", quot_offset_str, len); return -EINVAL; } for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners; i++) { index = tuple_match * cpr_vreg->num_fuse_corners + i - CPR_FUSE_CORNER_MIN; rc = of_property_read_u32_index(of_node, quot_offset_str, index, "_offset_adjust); if (rc) { cpr_err(cpr_vreg, "could not read %s index %u, rc=%d\n", quot_offset_str, index, rc); return rc; } if (quot_offset_adjust) { cpr_vreg->fuse_quot_offset[i] += quot_offset_adjust; cpr_info(cpr_vreg, "Corner[%d]: adjusted target quot = %d\n", i, cpr_vreg->fuse_quot_offset[i]); } } return rc; } static int cpr_get_fuse_quot_offset(struct cpr_regulator *cpr_vreg, struct platform_device *pdev, struct cpr_quot_scale *quot_scale) { struct device *dev = &pdev->dev; struct property *prop; u32 *fuse_sel, *tmp; int rc = 0, i, size; char *quot_offset_str; quot_offset_str = cpr_vreg->cpr_fuse_redundant ? "qcom,cpr-fuse-redun-quot-offset" : "qcom,cpr-fuse-quot-offset"; prop = of_find_property(dev->of_node, quot_offset_str, NULL); if (!prop) { cpr_debug(cpr_vreg, "%s not present\n", quot_offset_str); return 0; } else { size = prop->length / sizeof(u32); if (size != cpr_vreg->num_fuse_corners * 4) { cpr_err(cpr_vreg, "fuse position for quot offset is invalid\n"); return -EINVAL; } } fuse_sel = kzalloc(sizeof(u32) * size, GFP_KERNEL); if (!fuse_sel) { cpr_err(cpr_vreg, "memory alloc failed.\n"); return -ENOMEM; } rc = of_property_read_u32_array(dev->of_node, quot_offset_str, fuse_sel, size); if (rc < 0) { cpr_err(cpr_vreg, "read %s failed, rc = %d\n", quot_offset_str, rc); kfree(fuse_sel); return rc; } cpr_vreg->fuse_quot_offset = devm_kzalloc(dev, sizeof(u32) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->fuse_quot_offset) { cpr_err(cpr_vreg, "Can't allocate memory for cpr_vreg->fuse_quot_offset\n"); kfree(fuse_sel); return -ENOMEM; } tmp = fuse_sel; for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners; i++) { cpr_vreg->fuse_quot_offset[i] = cpr_read_efuse_param(cpr_vreg, fuse_sel[0], fuse_sel[1], fuse_sel[2], fuse_sel[3]); cpr_vreg->fuse_quot_offset[i] *= quot_scale[i].multiplier; fuse_sel += 4; } rc = cpr_adjust_target_quot_offsets(pdev, cpr_vreg); kfree(tmp); return rc; } /* * Adjust the per-virtual-corner open loop voltage with an offset specfied by a * device-tree property. This must be called after open-loop voltage scaling. Loading Loading @@ -2335,6 +2462,10 @@ static int cpr_get_corner_quot_adjustment(struct cpr_regulator *cpr_vreg, freq_max[i] /= 1000000; for (i = CPR_FUSE_CORNER_MIN + 1; i <= highest_fuse_corner; i++) { if (cpr_vreg->fuse_quot_offset) scaling[i] = 1000 * cpr_vreg->fuse_quot_offset[i] / (freq_max[i] - freq_max[i - 1]); else scaling[i] = 1000 * (cpr_vreg->cpr_fuse_target_quot[i] - cpr_vreg->cpr_fuse_target_quot[i - 1]) / (freq_max[i] - freq_max[i - 1]); Loading Loading @@ -2402,11 +2533,6 @@ free_arrays_1: return rc; } struct cpr_quot_scale { u32 offset; u32 multiplier; }; /* * Check if the redundant set of CPR fuses should be used in place of the * primary set and configure the cpr_fuse_redundant element accordingly. Loading Loading @@ -2866,6 +2992,15 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, } } /* * Check whether the fuse-quot-offset is defined per fuse corner. * If it is defined, use it (quot_offset) in the calculation * below for obtaining scaling factor per fuse corner. */ rc = cpr_get_fuse_quot_offset(cpr_vreg, pdev, quot_scale); if (rc < 0) goto error; rc = cpr_get_corner_quot_adjustment(cpr_vreg, &pdev->dev); if (rc) goto error; Loading @@ -2875,7 +3010,7 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, cpr_vreg->cpr_fuse_disable = true; cpr_err(cpr_vreg, "cpr_fuse_bits == 0; permanently disabling CPR\n"); } else { } else if (!cpr_vreg->fuse_quot_offset) { /* * Check if the target quotients for the highest two fuse * corners are too close together. Loading Loading
Documentation/devicetree/bindings/regulator/cpr-regulator.txt +47 −0 Original line number Diff line number Diff line Loading @@ -294,6 +294,22 @@ Optional properties: applied unconditionally. This property can be used to add or subtract static initial voltage margin from the regulator managed by the CPR controller. - qcom,cpr-quot-offset-adjustment: Array of integer tuples of target quotient offset adjustments to add to the fused quotient offsets of each fuse corner. The elements in a tuple are ordered from lowest voltage corner to highest voltage corner. Each tuple must be of length defined by qcom,cpr-fuse-corners. If the qcom,cpr-fuse-version-map property is specified, then qcom,cpr-quot-offset-adjustment must contain the same number of tuples as qcom,cpr-fuse-version-map. These tuples are then mapped one-to-one in the order specified. E.g. if the second qcom,cpr-fuse-version-map tuple matches for a given device, then the quotient offset adjustments defined in the second qcom,cpr-quot-offset-adjustment tuple will be applied. If the qcom,cpr-fuse-version-map property is not specified, then qcom,cpr-quot-offset-adjustment must contain a single tuple which is then applied unconditionally. If this property is specified, then the quotient offset adjustment values are added to the target quotient offset values read from fuses. This property can be used to add or subtract static quotient offset margin from the regulator managed by the CPR controller. - qcom,cpr-clamp-timer-interval: The number of 64 reference clock cycle blocks to delay for whenever the clamp signal, sensor mask registers or sensor bypass registers change. The CPR controller loop is disabled during this delay. Loading Loading @@ -514,6 +530,25 @@ Optional properties: - qcom,cpr-quot-min-diff: Integer which defines the minimum target-quotient difference between the highest and (highest - 1) fuse corner to keep CPR enabled. If this property is not specified a default value of 50 is used. - qcom,cpr-fuse-quot-offset: Array of quadruples in which each quadruple specifies a fuse location to read in order to get the quotient offset for a fuse corner. The fuse values are encoded as the difference between quotients of that fuse corner and its adjacent lower fuse corner divided by an unpacking multiplier. At present, the multiplier is assumed to be same as that of the value defined under qcom,cpr-fuse-target-quot-scale. The 4 elements in one quadruple are: [0]: => the fuse row number of the bits [1]: => LSB bit position of the bits [2]: => number of the bits [3]: => fuse reading method, 0 for direct reading or 1 for SCM reading The quadruples are ordered from the lowest fuse corner to the highest fuse corner. Quotient offset read from the fuse locations above can be overridden with the property qcom,cpr-quot-adjust-scaling-factor-max. - qcom,cpr-redun-fuse-quot-offset: Array of quadruples in which each quadruple specifies a fuse location to read in order to get the redundant quotient offset for a fuse corner. This property is the same as qcom,cpr-fuse-quot-offset except that it is only utilized if a chip is configured to use the redundant set of fuse values. Example: apc_vreg_corner: regulator@f9018000 { Loading Loading @@ -631,6 +666,16 @@ Example: <0 1>, <0 1>; qcom,cpr-quot-adjust-scaling-factor-max = <0 650 650>; qcom,cpr-fuse-quot-offset = <138 53 5 0>, <138 53 5 0>, <138 48 5 0>, <138 58 5 0>; qcom,cpr-fuse-redun-quot-offset = <200 53 5 0>, <200 53 5 0>, <200 48 5 0>, <200 58 5 0>; qcom,cpr-fuse-init-voltage = <27 36 6 0>, <27 18 6 0>, Loading Loading @@ -659,6 +704,8 @@ Example: <0 0 (-210)>, <0 0 (-60)>, <0 0 (-94)>; qcom,cpr-quot-offset-adjustment = <0 0 (-5)>; qcom,cpr-init-voltage-adjustment = <0 0 (-100000)>, <0 0 (-100000)>, Loading
drivers/regulator/cpr-regulator.c +144 −9 Original line number Diff line number Diff line Loading @@ -192,6 +192,11 @@ struct quot_adjust_info { int quot_adjust; }; struct cpr_quot_scale { u32 offset; u32 multiplier; }; static const char * const vdd_apc_name[] = {"vdd-apc-optional-prim", "vdd-apc-optional-sec", "vdd-apc"}; Loading Loading @@ -249,6 +254,7 @@ struct cpr_regulator { int cpr_fuse_map_match; int *cpr_fuse_target_quot; int *cpr_fuse_ro_sel; int *fuse_quot_offset; int gcnt; unsigned int cpr_irq; Loading Loading @@ -1935,6 +1941,127 @@ static int cpr_reduce_ceiling_voltage(struct cpr_regulator *cpr_vreg, return 0; } static int cpr_adjust_target_quot_offsets(struct platform_device *pdev, struct cpr_regulator *cpr_vreg) { struct device_node *of_node = pdev->dev.of_node; int tuple_count, tuple_match, i; u32 index; u32 quot_offset_adjust = 0; int len = 0; int rc = 0; char *quot_offset_str; quot_offset_str = "qcom,cpr-quot-offset-adjustment"; if (!of_find_property(of_node, quot_offset_str, &len)) { /* No static quotient adjustment needed. */ return 0; } if (cpr_vreg->cpr_fuse_map_count) { if (cpr_vreg->cpr_fuse_map_match == FUSE_MAP_NO_MATCH) { /* No matching index to use for quotient adjustment. */ return 0; } tuple_count = cpr_vreg->cpr_fuse_map_count; tuple_match = cpr_vreg->cpr_fuse_map_match; } else { tuple_count = 1; tuple_match = 0; } if (len != cpr_vreg->num_fuse_corners * tuple_count * sizeof(u32)) { cpr_err(cpr_vreg, "%s length=%d is invalid\n", quot_offset_str, len); return -EINVAL; } for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners; i++) { index = tuple_match * cpr_vreg->num_fuse_corners + i - CPR_FUSE_CORNER_MIN; rc = of_property_read_u32_index(of_node, quot_offset_str, index, "_offset_adjust); if (rc) { cpr_err(cpr_vreg, "could not read %s index %u, rc=%d\n", quot_offset_str, index, rc); return rc; } if (quot_offset_adjust) { cpr_vreg->fuse_quot_offset[i] += quot_offset_adjust; cpr_info(cpr_vreg, "Corner[%d]: adjusted target quot = %d\n", i, cpr_vreg->fuse_quot_offset[i]); } } return rc; } static int cpr_get_fuse_quot_offset(struct cpr_regulator *cpr_vreg, struct platform_device *pdev, struct cpr_quot_scale *quot_scale) { struct device *dev = &pdev->dev; struct property *prop; u32 *fuse_sel, *tmp; int rc = 0, i, size; char *quot_offset_str; quot_offset_str = cpr_vreg->cpr_fuse_redundant ? "qcom,cpr-fuse-redun-quot-offset" : "qcom,cpr-fuse-quot-offset"; prop = of_find_property(dev->of_node, quot_offset_str, NULL); if (!prop) { cpr_debug(cpr_vreg, "%s not present\n", quot_offset_str); return 0; } else { size = prop->length / sizeof(u32); if (size != cpr_vreg->num_fuse_corners * 4) { cpr_err(cpr_vreg, "fuse position for quot offset is invalid\n"); return -EINVAL; } } fuse_sel = kzalloc(sizeof(u32) * size, GFP_KERNEL); if (!fuse_sel) { cpr_err(cpr_vreg, "memory alloc failed.\n"); return -ENOMEM; } rc = of_property_read_u32_array(dev->of_node, quot_offset_str, fuse_sel, size); if (rc < 0) { cpr_err(cpr_vreg, "read %s failed, rc = %d\n", quot_offset_str, rc); kfree(fuse_sel); return rc; } cpr_vreg->fuse_quot_offset = devm_kzalloc(dev, sizeof(u32) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->fuse_quot_offset) { cpr_err(cpr_vreg, "Can't allocate memory for cpr_vreg->fuse_quot_offset\n"); kfree(fuse_sel); return -ENOMEM; } tmp = fuse_sel; for (i = CPR_FUSE_CORNER_MIN; i <= cpr_vreg->num_fuse_corners; i++) { cpr_vreg->fuse_quot_offset[i] = cpr_read_efuse_param(cpr_vreg, fuse_sel[0], fuse_sel[1], fuse_sel[2], fuse_sel[3]); cpr_vreg->fuse_quot_offset[i] *= quot_scale[i].multiplier; fuse_sel += 4; } rc = cpr_adjust_target_quot_offsets(pdev, cpr_vreg); kfree(tmp); return rc; } /* * Adjust the per-virtual-corner open loop voltage with an offset specfied by a * device-tree property. This must be called after open-loop voltage scaling. Loading Loading @@ -2335,6 +2462,10 @@ static int cpr_get_corner_quot_adjustment(struct cpr_regulator *cpr_vreg, freq_max[i] /= 1000000; for (i = CPR_FUSE_CORNER_MIN + 1; i <= highest_fuse_corner; i++) { if (cpr_vreg->fuse_quot_offset) scaling[i] = 1000 * cpr_vreg->fuse_quot_offset[i] / (freq_max[i] - freq_max[i - 1]); else scaling[i] = 1000 * (cpr_vreg->cpr_fuse_target_quot[i] - cpr_vreg->cpr_fuse_target_quot[i - 1]) / (freq_max[i] - freq_max[i - 1]); Loading Loading @@ -2402,11 +2533,6 @@ free_arrays_1: return rc; } struct cpr_quot_scale { u32 offset; u32 multiplier; }; /* * Check if the redundant set of CPR fuses should be used in place of the * primary set and configure the cpr_fuse_redundant element accordingly. Loading Loading @@ -2866,6 +2992,15 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, } } /* * Check whether the fuse-quot-offset is defined per fuse corner. * If it is defined, use it (quot_offset) in the calculation * below for obtaining scaling factor per fuse corner. */ rc = cpr_get_fuse_quot_offset(cpr_vreg, pdev, quot_scale); if (rc < 0) goto error; rc = cpr_get_corner_quot_adjustment(cpr_vreg, &pdev->dev); if (rc) goto error; Loading @@ -2875,7 +3010,7 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, cpr_vreg->cpr_fuse_disable = true; cpr_err(cpr_vreg, "cpr_fuse_bits == 0; permanently disabling CPR\n"); } else { } else if (!cpr_vreg->fuse_quot_offset) { /* * Check if the target quotients for the highest two fuse * corners are too close together. Loading