Loading Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt +20 −0 Original line number Original line Diff line number Diff line Loading @@ -198,6 +198,21 @@ Optional properties: - qti,cpr-uplift-speed-bin: The speed bin value corresponding to one type of processor which needs to apply the - qti,cpr-uplift-speed-bin: The speed bin value corresponding to one type of processor which needs to apply the pvs voltage uplift workaround. pvs voltage uplift workaround. This is required if cpr-fuse-uplift-disable-sel is present. This is required if cpr-fuse-uplift-disable-sel is present. - qti,cpr-quot-adjust-table: Array of triples in which each triple indicates the speed bin of the CPU, the virtual corner to use and the quotient adjustment. The 3 elements in one triple are: [0]: => the speed bin of the CPU. [1]: => the virtual voltage corner to use. [2]: => the quotient adjustment for the corresponding virtual corner. If the speed bin in a triple is equal to the speed bin of the CPU, the adjustment would be subtracted from the quotient value of the voltage corner when the CPU is running at that virtual corner. Each virtual corner value must be in the range 1 to the number of elements in qti,cpr-corner-map. - qti,cpr-corner-map: Array of elements of fuse corner value for each virtual corner. The location or 1-based index of an element in the list corresponds to the virtual corner value. For example, the first element in the list is the fuse corner value that virtual corner 1 maps to. This is required if qti,cpr-quot-adjust-table is present. Example: Example: apc_vreg_corner: regulator@f9018000 { apc_vreg_corner: regulator@f9018000 { Loading Loading @@ -260,5 +275,10 @@ Example: qti,cpr-uplift-max-volt = <1350000>; qti,cpr-uplift-max-volt = <1350000>; qti,cpr-uplift-speed-bin = <1>; qti,cpr-uplift-speed-bin = <1>; qti,speed-bin-fuse-sel = <22 0 3 0>; qti,speed-bin-fuse-sel = <22 0 3 0>; qti,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3>; qti,cpr-quot-adjust-table = <1 1 0>, <1 2 0>, <1 3 0>, <1 4 0>, <1 5 450>, <1 6 375>, <1 7 300>, <1 8 225>, <1 9 187>, <1 10 150>, <1 11 75>, <1 12 0>; }; }; arch/arm/mach-msm/cpr-regulator.c +208 −47 Original line number Original line Diff line number Diff line Loading @@ -146,6 +146,12 @@ #define FLAGS_SET_MIN_VOLTAGE BIT(1) #define FLAGS_SET_MIN_VOLTAGE BIT(1) #define FLAGS_UPLIFT_QUOT_VOLT BIT(2) #define FLAGS_UPLIFT_QUOT_VOLT BIT(2) struct quot_adjust_info { int speed_bin; int virtual_corner; int quot_adjust; }; enum voltage_change_dir { enum voltage_change_dir { NO_CHANGE, NO_CHANGE, DOWN, DOWN, Loading Loading @@ -194,11 +200,11 @@ struct cpr_regulator { int ceiling_volt[CPR_CORNER_MAX]; int ceiling_volt[CPR_CORNER_MAX]; int floor_volt[CPR_CORNER_MAX]; int floor_volt[CPR_CORNER_MAX]; int last_volt[CPR_CORNER_MAX]; int *last_volt; int step_volt; int step_volt; int save_ctl[CPR_CORNER_MAX]; int *save_ctl; int save_irq[CPR_CORNER_MAX]; int *save_irq; u32 save_regs[CPR_NUM_SAVE_REGS]; u32 save_regs[CPR_NUM_SAVE_REGS]; u32 save_reg_val[CPR_NUM_SAVE_REGS]; u32 save_reg_val[CPR_NUM_SAVE_REGS]; Loading @@ -218,6 +224,9 @@ struct cpr_regulator { u32 vdd_apc_step_up_limit; u32 vdd_apc_step_up_limit; u32 vdd_apc_step_down_limit; u32 vdd_apc_step_down_limit; u32 flags; u32 flags; int *corner_map; u32 num_corners; int *quot_adjust; }; }; #define CPR_DEBUG_MASK_IRQ BIT(0) #define CPR_DEBUG_MASK_IRQ BIT(0) Loading Loading @@ -341,9 +350,11 @@ static void cpr_ctl_modify(struct cpr_regulator *cpr_vreg, u32 mask, u32 value) static void cpr_ctl_enable(struct cpr_regulator *cpr_vreg, int corner) static void cpr_ctl_enable(struct cpr_regulator *cpr_vreg, int corner) { { u32 val; u32 val; int fuse_corner = cpr_vreg->corner_map[corner]; if (cpr_is_allowed(cpr_vreg) && if (cpr_is_allowed(cpr_vreg) && (cpr_vreg->ceiling_volt[corner] > cpr_vreg->floor_volt[corner])) (cpr_vreg->ceiling_volt[fuse_corner] > cpr_vreg->floor_volt[fuse_corner])) val = RBCPR_CTL_LOOP_EN; val = RBCPR_CTL_LOOP_EN; else else val = 0; val = 0; Loading Loading @@ -387,9 +398,12 @@ static void cpr_corner_save(struct cpr_regulator *cpr_vreg, int corner) static void cpr_corner_restore(struct cpr_regulator *cpr_vreg, int corner) static void cpr_corner_restore(struct cpr_regulator *cpr_vreg, int corner) { { u32 gcnt, ctl, irq, ro_sel; u32 gcnt, ctl, irq, ro_sel; int fuse_corner = cpr_vreg->corner_map[corner]; ro_sel = cpr_vreg->cpr_fuse_ro_sel[fuse_corner]; gcnt = cpr_vreg->gcnt | (cpr_vreg->cpr_fuse_target_quot[fuse_corner] - cpr_vreg->quot_adjust[corner]); ro_sel = cpr_vreg->cpr_fuse_ro_sel[corner]; gcnt = cpr_vreg->gcnt | cpr_vreg->cpr_fuse_target_quot[corner]; cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt); cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt); ctl = cpr_vreg->save_ctl[corner]; ctl = cpr_vreg->save_ctl[corner]; cpr_write(cpr_vreg, REG_RBCPR_CTL, ctl); cpr_write(cpr_vreg, REG_RBCPR_CTL, ctl); Loading Loading @@ -427,8 +441,9 @@ static int cpr_enable_param_set(const char *val, const struct kernel_param *kp) goto _exit; goto _exit; } } cpr_debug("%d -> %d [corner=%d]\n", cpr_debug("%d -> %d [corner=%d, fuse_corner=%d]\n", old_cpr_enable, cpr_enable, the_cpr->corner); old_cpr_enable, cpr_enable, the_cpr->corner, the_cpr->corner_map[the_cpr->corner]); if (the_cpr->cpr_fuse_disable) { if (the_cpr->cpr_fuse_disable) { /* Already disabled */ /* Already disabled */ Loading Loading @@ -474,13 +489,14 @@ static int cpr_apc_set(struct cpr_regulator *cpr_vreg, u32 new_volt) static int cpr_mx_get(struct cpr_regulator *cpr_vreg, int corner, int apc_volt) static int cpr_mx_get(struct cpr_regulator *cpr_vreg, int corner, int apc_volt) { { int vdd_mx; int vdd_mx; int fuse_corner = cpr_vreg->corner_map[corner]; switch (cpr_vreg->vdd_mx_vmin_method) { switch (cpr_vreg->vdd_mx_vmin_method) { case VDD_MX_VMIN_APC: case VDD_MX_VMIN_APC: vdd_mx = apc_volt; vdd_mx = apc_volt; break; break; case VDD_MX_VMIN_APC_CORNER_CEILING: case VDD_MX_VMIN_APC_CORNER_CEILING: vdd_mx = cpr_vreg->ceiling_volt[corner]; vdd_mx = cpr_vreg->ceiling_volt[fuse_corner]; break; break; case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING: case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING: vdd_mx = cpr_vreg->pvs_corner_v[APC_PVS_SLOW] vdd_mx = cpr_vreg->pvs_corner_v[APC_PVS_SLOW] Loading @@ -501,15 +517,19 @@ static int cpr_mx_set(struct cpr_regulator *cpr_vreg, int corner, int vdd_mx_vmin) int vdd_mx_vmin) { { int rc; int rc; int fuse_corner = cpr_vreg->corner_map[corner]; rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin, rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin, cpr_vreg->vdd_mx_vmax); cpr_vreg->vdd_mx_vmax); cpr_debug("[corner:%d] %d uV\n", corner, vdd_mx_vmin); cpr_debug("[corner:%d, fuse_corner:%d] %d uV\n", corner, if (!rc) fuse_corner, vdd_mx_vmin); if (!rc) { cpr_vreg->vdd_mx_vmin = vdd_mx_vmin; cpr_vreg->vdd_mx_vmin = vdd_mx_vmin; else } else { pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n", pr_err("set: vdd_mx [corner:%d, fuse_corner:%d] = %d uV failed: rc=%d\n", corner, vdd_mx_vmin, rc); corner, fuse_corner, vdd_mx_vmin, rc); } return rc; return rc; } } Loading Loading @@ -547,10 +567,11 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, enum voltage_change_dir dir) enum voltage_change_dir dir) { { u32 reg_val, error_steps, reg_mask; u32 reg_val, error_steps, reg_mask; int last_volt, new_volt, corner; int last_volt, new_volt, corner, fuse_corner; u32 gcnt, quot; u32 gcnt, quot; corner = cpr_vreg->corner; corner = cpr_vreg->corner; fuse_corner = cpr_vreg->corner_map[corner]; reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0); reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0); Loading @@ -558,20 +579,22 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, & RBCPR_RESULT0_ERROR_STEPS_MASK; & RBCPR_RESULT0_ERROR_STEPS_MASK; last_volt = cpr_vreg->last_volt[corner]; last_volt = cpr_vreg->last_volt[corner]; cpr_debug_irq("last_volt[corner:%d] = %d uV\n", corner, last_volt); cpr_debug_irq("last_volt[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, last_volt); gcnt = cpr_read(cpr_vreg, REG_RBCPR_GCNT_TARGET gcnt = cpr_read(cpr_vreg, REG_RBCPR_GCNT_TARGET (cpr_vreg->cpr_fuse_ro_sel[corner])); (cpr_vreg->cpr_fuse_ro_sel[fuse_corner])); quot = gcnt & ((1 << RBCPR_GCNT_TARGET_GCNT_SHIFT) - 1); quot = gcnt & ((1 << RBCPR_GCNT_TARGET_GCNT_SHIFT) - 1); if (dir == UP) { if (dir == UP) { cpr_debug_irq("Up: cpr status = 0x%08x (error_steps=%d)\n", cpr_debug_irq("Up: cpr status = 0x%08x (error_steps=%d)\n", reg_val, error_steps); reg_val, error_steps); if (last_volt >= cpr_vreg->ceiling_volt[corner]) { if (last_volt >= cpr_vreg->ceiling_volt[fuse_corner]) { cpr_debug_irq("[corn:%d] @ ceiling: %d >= %d: NACK\n", cpr_debug_irq( corner, last_volt, "[corn:%d, fuse_corn:%d] @ ceiling: %d >= %d: NACK\n", cpr_vreg->ceiling_volt[corner]); corner, fuse_corner, last_volt, cpr_vreg->ceiling_volt[fuse_corner]); cpr_irq_clr_nack(cpr_vreg); cpr_irq_clr_nack(cpr_vreg); cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, Loading @@ -594,11 +617,12 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Calculate new voltage */ /* Calculate new voltage */ new_volt = last_volt + (error_steps * cpr_vreg->step_volt); new_volt = last_volt + (error_steps * cpr_vreg->step_volt); if (new_volt > cpr_vreg->ceiling_volt[corner]) { if (new_volt > cpr_vreg->ceiling_volt[fuse_corner]) { cpr_debug_irq("new_volt(%d) >= ceiling(%d): Clamp\n", cpr_debug_irq("new_volt(%d) >= ceiling(%d): Clamp\n", new_volt, new_volt, cpr_vreg->ceiling_volt[corner]); cpr_vreg->ceiling_volt[fuse_corner]); new_volt = cpr_vreg->ceiling_volt[corner]; new_volt = cpr_vreg->ceiling_volt[fuse_corner]; } } if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { Loading @@ -619,16 +643,18 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Ack */ /* Ack */ cpr_irq_clr_ack(cpr_vreg); cpr_irq_clr_ack(cpr_vreg); cpr_debug_irq("UP: -> new_volt[corner:%d] = %d uV\n", cpr_debug_irq( corner, new_volt); "UP: -> new_volt[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, new_volt); } else if (dir == DOWN) { } else if (dir == DOWN) { cpr_debug_irq("Down: cpr status = 0x%08x (error_steps=%d)\n", cpr_debug_irq("Down: cpr status = 0x%08x (error_steps=%d)\n", reg_val, error_steps); reg_val, error_steps); if (last_volt <= cpr_vreg->floor_volt[corner]) { if (last_volt <= cpr_vreg->floor_volt[fuse_corner]) { cpr_debug_irq("[corn:%d] @ floor: %d <= %d: NACK\n", cpr_debug_irq( corner, last_volt, "[corn:%d, fuse_corner:%d] @ floor: %d <= %d: NACK\n", cpr_vreg->floor_volt[corner]); corner, fuse_corner, last_volt, cpr_vreg->floor_volt[fuse_corner]); cpr_irq_clr_nack(cpr_vreg); cpr_irq_clr_nack(cpr_vreg); cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, Loading @@ -655,11 +681,11 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Calculte new voltage */ /* Calculte new voltage */ new_volt = last_volt - (error_steps * cpr_vreg->step_volt); new_volt = last_volt - (error_steps * cpr_vreg->step_volt); if (new_volt < cpr_vreg->floor_volt[corner]) { if (new_volt < cpr_vreg->floor_volt[fuse_corner]) { cpr_debug_irq("new_volt(%d) < floor(%d): Clamp\n", cpr_debug_irq("new_volt(%d) < floor(%d): Clamp\n", new_volt, new_volt, cpr_vreg->floor_volt[corner]); cpr_vreg->floor_volt[fuse_corner]); new_volt = cpr_vreg->floor_volt[corner]; new_volt = cpr_vreg->floor_volt[fuse_corner]; } } if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { Loading @@ -678,8 +704,9 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Ack */ /* Ack */ cpr_irq_clr_ack(cpr_vreg); cpr_irq_clr_ack(cpr_vreg); cpr_debug_irq("DOWN: -> new_volt[corner:%d] = %d uV\n", cpr_debug_irq( corner, new_volt); "DOWN: -> new_volt[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, new_volt); } } } } Loading Loading @@ -801,6 +828,7 @@ static int cpr_regulator_set_voltage(struct regulator_dev *rdev, int rc; int rc; int new_volt; int new_volt; enum voltage_change_dir change_dir = NO_CHANGE; enum voltage_change_dir change_dir = NO_CHANGE; int fuse_corner = cpr_vreg->corner_map[corner]; mutex_lock(&cpr_vreg->cpr_mutex); mutex_lock(&cpr_vreg->cpr_mutex); Loading @@ -808,10 +836,12 @@ static int cpr_regulator_set_voltage(struct regulator_dev *rdev, cpr_ctl_disable(cpr_vreg); cpr_ctl_disable(cpr_vreg); new_volt = cpr_vreg->last_volt[corner]; new_volt = cpr_vreg->last_volt[corner]; } else { } else { new_volt = cpr_vreg->pvs_corner_v[cpr_vreg->process][corner]; new_volt = cpr_vreg->pvs_corner_v [cpr_vreg->process][fuse_corner]; } } cpr_debug("[corner:%d] = %d uV\n", corner, new_volt); cpr_debug("[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, new_volt); if (corner > cpr_vreg->corner) if (corner > cpr_vreg->corner) change_dir = UP; change_dir = UP; Loading Loading @@ -904,11 +934,12 @@ static int cpr_regulator_resume(struct platform_device *pdev) #define cpr_regulator_resume NULL #define cpr_regulator_resume NULL #endif #endif static int cpr_config(struct cpr_regulator *cpr_vreg) static int cpr_config(struct cpr_regulator *cpr_vreg, struct device *dev) { { int i; int i; u32 val, gcnt, reg; u32 val, gcnt, reg; void __iomem *rbcpr_clk; void __iomem *rbcpr_clk; int size; /* Use 19.2 MHz clock for CPR. */ /* Use 19.2 MHz clock for CPR. */ rbcpr_clk = ioremap(cpr_vreg->rbcpr_clk_addr, 4); rbcpr_clk = ioremap(cpr_vreg->rbcpr_clk_addr, 4); Loading Loading @@ -991,9 +1022,14 @@ static int cpr_config(struct cpr_regulator *cpr_vreg) if (val <= RBCPR_VER_2) if (val <= RBCPR_VER_2) cpr_vreg->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS; cpr_vreg->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS; cpr_corner_save(cpr_vreg, CPR_CORNER_SVS); size = cpr_vreg->num_corners + 1; cpr_corner_save(cpr_vreg, CPR_CORNER_NORMAL); cpr_vreg->save_ctl = devm_kzalloc(dev, sizeof(int) * size, GFP_KERNEL); cpr_corner_save(cpr_vreg, CPR_CORNER_TURBO); cpr_vreg->save_irq = devm_kzalloc(dev, sizeof(int) * size, GFP_KERNEL); if (!cpr_vreg->save_ctl || !cpr_vreg->save_irq) return -ENOMEM; for (i = 1; i < size; i++) cpr_corner_save(cpr_vreg, i); return 0; return 0; } } Loading Loading @@ -1224,6 +1260,106 @@ static int cpr_voltage_uplift_wa_inc_quot(struct cpr_regulator *cpr_vreg, return rc; return rc; } } static int cpr_get_of_corner_mappings(struct cpr_regulator *cpr_vreg, struct device *dev) { int rc = 0; int i, size, stripe_size; struct property *prop; u32 *tmp; bool corners_mapped; prop = of_find_property(dev->of_node, "qti,cpr-corner-map", NULL); if (prop) { size = prop->length / sizeof(u32); corners_mapped = true; } else { size = CPR_CORNER_MAX - 1; corners_mapped = false; } cpr_vreg->corner_map = devm_kzalloc(dev, sizeof(int) * (size + 1), GFP_KERNEL); if (!cpr_vreg->corner_map) { pr_err("Can't allocate cpr_vreg->corner_map memory\n"); return -ENOMEM; } cpr_vreg->num_corners = size; if (!corners_mapped) { for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) cpr_vreg->corner_map[i] = i; } else { rc = of_property_read_u32_array(dev->of_node, "qti,cpr-corner-map", &cpr_vreg->corner_map[1], size); if (rc) { pr_err("qti,cpr-corner-map missing, rc = %d", rc); return rc; } } cpr_vreg->quot_adjust = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->quot_adjust) { pr_err("Can't allocate cpr_vreg->quot_adjust memory\n"); return -ENOMEM; } prop = of_find_property(dev->of_node, "qti,cpr-quot-adjust-table", NULL); if (prop) { if (!corners_mapped) { pr_err("qti,cpr-corner-map missing\n"); return -EINVAL; } size = prop->length / sizeof(u32); tmp = kzalloc(sizeof(u32) * size, GFP_KERNEL); if (!tmp) return -ENOMEM; rc = of_property_read_u32_array(dev->of_node, "qti,cpr-quot-adjust-table", tmp, size); if (rc) { pr_err("qti,cpr-quot-adjust-table missing, rc = %d", rc); kfree(tmp); return rc; } stripe_size = sizeof(struct quot_adjust_info) / sizeof(int); if ((size % stripe_size) != 0) { pr_err("qti,cpr-quot-adjust-table data is not correct"); kfree(tmp); return -EINVAL; } for (i = 0; i < size; i += stripe_size) { if (tmp[i] == cpr_vreg->speed_bin) { if (tmp[i + 1] >= 1 && tmp[i + 1] <= cpr_vreg->num_corners) { cpr_vreg->quot_adjust[tmp[i + 1]] = tmp[i + 2]; } else { pr_err("qti,cpr-quot-adjust-table data is not correct"); kfree(tmp); return -EINVAL; } } } kfree(tmp); } return 0; } static int cpr_init_cpr_efuse(struct platform_device *pdev, static int cpr_init_cpr_efuse(struct platform_device *pdev, struct cpr_regulator *cpr_vreg) struct cpr_regulator *cpr_vreg) { { Loading Loading @@ -1353,6 +1489,10 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, } } } } rc = cpr_get_of_corner_mappings(cpr_vreg, &pdev->dev); if (rc) return rc; cpr_vreg->cpr_fuse_bits = fuse_bits; cpr_vreg->cpr_fuse_bits = fuse_bits; if (!cpr_vreg->cpr_fuse_bits) { if (!cpr_vreg->cpr_fuse_bits) { cpr_vreg->cpr_fuse_disable = 1; cpr_vreg->cpr_fuse_disable = 1; Loading Loading @@ -1381,9 +1521,15 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, return 0; return 0; } } static int cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg) static int cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg, struct device *dev) { { int i; int i; int size = cpr_vreg->num_corners + 1; cpr_vreg->last_volt = devm_kzalloc(dev, sizeof(int) * size, GFP_KERNEL); if (!cpr_vreg->last_volt) return -EINVAL; /* Construct CPR voltage limits */ /* Construct CPR voltage limits */ for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) { for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) { Loading @@ -1391,8 +1537,11 @@ static int cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg) cpr_vreg->pvs_corner_v[APC_PVS_FAST][i]; cpr_vreg->pvs_corner_v[APC_PVS_FAST][i]; cpr_vreg->ceiling_volt[i] = cpr_vreg->ceiling_volt[i] = cpr_vreg->pvs_corner_v[APC_PVS_SLOW][i]; cpr_vreg->pvs_corner_v[APC_PVS_SLOW][i]; cpr_vreg->last_volt[i] = } cpr_vreg->pvs_corner_v[cpr_vreg->process][i]; for (i = 1; i < size; i++) { cpr_vreg->last_volt[i] = cpr_vreg->pvs_corner_v [cpr_vreg->process][cpr_vreg->corner_map[i]]; } } return 0; return 0; Loading Loading @@ -1492,7 +1641,9 @@ static int cpr_init_cpr(struct platform_device *pdev, resource_size(res)); resource_size(res)); /* Init all voltage set points of APC regulator for CPR */ /* Init all voltage set points of APC regulator for CPR */ cpr_init_cpr_voltages(cpr_vreg); rc = cpr_init_cpr_voltages(cpr_vreg, &pdev->dev); if (rc) return rc; /* Init CPR configuration parameters */ /* Init CPR configuration parameters */ rc = cpr_init_cpr_parameters(pdev, cpr_vreg); rc = cpr_init_cpr_parameters(pdev, cpr_vreg); Loading @@ -1507,7 +1658,7 @@ static int cpr_init_cpr(struct platform_device *pdev, } } /* Configure CPR HW but keep it disabled */ /* Configure CPR HW but keep it disabled */ rc = cpr_config(cpr_vreg); rc = cpr_config(cpr_vreg, &pdev->dev); if (rc) if (rc) return rc; return rc; Loading Loading @@ -1593,6 +1744,8 @@ static void cpr_parse_speed_bin_fuse(struct cpr_regulator *cpr_vreg, pr_info("[row: %d]: 0x%llx, speed_bits = %d\n", pr_info("[row: %d]: 0x%llx, speed_bits = %d\n", fuse_sel[0], fuse_bits, speed_bits); fuse_sel[0], fuse_bits, speed_bits); cpr_vreg->speed_bin = speed_bits; cpr_vreg->speed_bin = speed_bits; } else { cpr_vreg->speed_bin = UINT_MAX; } } } } Loading Loading @@ -1695,18 +1848,26 @@ static ssize_t cpr_debugfs_read(struct file *file, char __user *buff, ssize_t len, ret = 0; ssize_t len, ret = 0; u32 gcnt, ro_sel, ctl, irq_status, reg, error_steps; u32 gcnt, ro_sel, ctl, irq_status, reg, error_steps; u32 step_dn, step_up, error, error_lt0, busy; u32 step_dn, step_up, error, error_lt0, busy; int fuse_corner; if (!debugfs_buf) if (!debugfs_buf) return -ENOMEM; return -ENOMEM; mutex_lock(&the_cpr->cpr_mutex); mutex_lock(&the_cpr->cpr_mutex); fuse_corner = the_cpr->corner_map[the_cpr->corner]; len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, "corner = %d, current_volt = %d uV\n", "corner = %d, current_volt = %d uV\n", the_cpr->corner, the_cpr->last_volt[the_cpr->corner]); the_cpr->corner, the_cpr->last_volt[the_cpr->corner]); ret += len; ret += len; ro_sel = the_cpr->cpr_fuse_ro_sel[the_cpr->corner]; len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, "fuse_corner = %d, current_volt = %d uV\n", fuse_corner, the_cpr->last_volt[the_cpr->corner]); ret += len; ro_sel = the_cpr->cpr_fuse_ro_sel[fuse_corner]; gcnt = cpr_read(the_cpr, REG_RBCPR_GCNT_TARGET(ro_sel)); gcnt = cpr_read(the_cpr, REG_RBCPR_GCNT_TARGET(ro_sel)); len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, "rbcpr_gcnt_target (%u) = 0x%02X\n", ro_sel, gcnt); "rbcpr_gcnt_target (%u) = 0x%02X\n", ro_sel, gcnt); Loading Loading
Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt +20 −0 Original line number Original line Diff line number Diff line Loading @@ -198,6 +198,21 @@ Optional properties: - qti,cpr-uplift-speed-bin: The speed bin value corresponding to one type of processor which needs to apply the - qti,cpr-uplift-speed-bin: The speed bin value corresponding to one type of processor which needs to apply the pvs voltage uplift workaround. pvs voltage uplift workaround. This is required if cpr-fuse-uplift-disable-sel is present. This is required if cpr-fuse-uplift-disable-sel is present. - qti,cpr-quot-adjust-table: Array of triples in which each triple indicates the speed bin of the CPU, the virtual corner to use and the quotient adjustment. The 3 elements in one triple are: [0]: => the speed bin of the CPU. [1]: => the virtual voltage corner to use. [2]: => the quotient adjustment for the corresponding virtual corner. If the speed bin in a triple is equal to the speed bin of the CPU, the adjustment would be subtracted from the quotient value of the voltage corner when the CPU is running at that virtual corner. Each virtual corner value must be in the range 1 to the number of elements in qti,cpr-corner-map. - qti,cpr-corner-map: Array of elements of fuse corner value for each virtual corner. The location or 1-based index of an element in the list corresponds to the virtual corner value. For example, the first element in the list is the fuse corner value that virtual corner 1 maps to. This is required if qti,cpr-quot-adjust-table is present. Example: Example: apc_vreg_corner: regulator@f9018000 { apc_vreg_corner: regulator@f9018000 { Loading Loading @@ -260,5 +275,10 @@ Example: qti,cpr-uplift-max-volt = <1350000>; qti,cpr-uplift-max-volt = <1350000>; qti,cpr-uplift-speed-bin = <1>; qti,cpr-uplift-speed-bin = <1>; qti,speed-bin-fuse-sel = <22 0 3 0>; qti,speed-bin-fuse-sel = <22 0 3 0>; qti,cpr-corner-map = <1 1 2 2 3 3 3 3 3 3 3 3>; qti,cpr-quot-adjust-table = <1 1 0>, <1 2 0>, <1 3 0>, <1 4 0>, <1 5 450>, <1 6 375>, <1 7 300>, <1 8 225>, <1 9 187>, <1 10 150>, <1 11 75>, <1 12 0>; }; };
arch/arm/mach-msm/cpr-regulator.c +208 −47 Original line number Original line Diff line number Diff line Loading @@ -146,6 +146,12 @@ #define FLAGS_SET_MIN_VOLTAGE BIT(1) #define FLAGS_SET_MIN_VOLTAGE BIT(1) #define FLAGS_UPLIFT_QUOT_VOLT BIT(2) #define FLAGS_UPLIFT_QUOT_VOLT BIT(2) struct quot_adjust_info { int speed_bin; int virtual_corner; int quot_adjust; }; enum voltage_change_dir { enum voltage_change_dir { NO_CHANGE, NO_CHANGE, DOWN, DOWN, Loading Loading @@ -194,11 +200,11 @@ struct cpr_regulator { int ceiling_volt[CPR_CORNER_MAX]; int ceiling_volt[CPR_CORNER_MAX]; int floor_volt[CPR_CORNER_MAX]; int floor_volt[CPR_CORNER_MAX]; int last_volt[CPR_CORNER_MAX]; int *last_volt; int step_volt; int step_volt; int save_ctl[CPR_CORNER_MAX]; int *save_ctl; int save_irq[CPR_CORNER_MAX]; int *save_irq; u32 save_regs[CPR_NUM_SAVE_REGS]; u32 save_regs[CPR_NUM_SAVE_REGS]; u32 save_reg_val[CPR_NUM_SAVE_REGS]; u32 save_reg_val[CPR_NUM_SAVE_REGS]; Loading @@ -218,6 +224,9 @@ struct cpr_regulator { u32 vdd_apc_step_up_limit; u32 vdd_apc_step_up_limit; u32 vdd_apc_step_down_limit; u32 vdd_apc_step_down_limit; u32 flags; u32 flags; int *corner_map; u32 num_corners; int *quot_adjust; }; }; #define CPR_DEBUG_MASK_IRQ BIT(0) #define CPR_DEBUG_MASK_IRQ BIT(0) Loading Loading @@ -341,9 +350,11 @@ static void cpr_ctl_modify(struct cpr_regulator *cpr_vreg, u32 mask, u32 value) static void cpr_ctl_enable(struct cpr_regulator *cpr_vreg, int corner) static void cpr_ctl_enable(struct cpr_regulator *cpr_vreg, int corner) { { u32 val; u32 val; int fuse_corner = cpr_vreg->corner_map[corner]; if (cpr_is_allowed(cpr_vreg) && if (cpr_is_allowed(cpr_vreg) && (cpr_vreg->ceiling_volt[corner] > cpr_vreg->floor_volt[corner])) (cpr_vreg->ceiling_volt[fuse_corner] > cpr_vreg->floor_volt[fuse_corner])) val = RBCPR_CTL_LOOP_EN; val = RBCPR_CTL_LOOP_EN; else else val = 0; val = 0; Loading Loading @@ -387,9 +398,12 @@ static void cpr_corner_save(struct cpr_regulator *cpr_vreg, int corner) static void cpr_corner_restore(struct cpr_regulator *cpr_vreg, int corner) static void cpr_corner_restore(struct cpr_regulator *cpr_vreg, int corner) { { u32 gcnt, ctl, irq, ro_sel; u32 gcnt, ctl, irq, ro_sel; int fuse_corner = cpr_vreg->corner_map[corner]; ro_sel = cpr_vreg->cpr_fuse_ro_sel[fuse_corner]; gcnt = cpr_vreg->gcnt | (cpr_vreg->cpr_fuse_target_quot[fuse_corner] - cpr_vreg->quot_adjust[corner]); ro_sel = cpr_vreg->cpr_fuse_ro_sel[corner]; gcnt = cpr_vreg->gcnt | cpr_vreg->cpr_fuse_target_quot[corner]; cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt); cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt); ctl = cpr_vreg->save_ctl[corner]; ctl = cpr_vreg->save_ctl[corner]; cpr_write(cpr_vreg, REG_RBCPR_CTL, ctl); cpr_write(cpr_vreg, REG_RBCPR_CTL, ctl); Loading Loading @@ -427,8 +441,9 @@ static int cpr_enable_param_set(const char *val, const struct kernel_param *kp) goto _exit; goto _exit; } } cpr_debug("%d -> %d [corner=%d]\n", cpr_debug("%d -> %d [corner=%d, fuse_corner=%d]\n", old_cpr_enable, cpr_enable, the_cpr->corner); old_cpr_enable, cpr_enable, the_cpr->corner, the_cpr->corner_map[the_cpr->corner]); if (the_cpr->cpr_fuse_disable) { if (the_cpr->cpr_fuse_disable) { /* Already disabled */ /* Already disabled */ Loading Loading @@ -474,13 +489,14 @@ static int cpr_apc_set(struct cpr_regulator *cpr_vreg, u32 new_volt) static int cpr_mx_get(struct cpr_regulator *cpr_vreg, int corner, int apc_volt) static int cpr_mx_get(struct cpr_regulator *cpr_vreg, int corner, int apc_volt) { { int vdd_mx; int vdd_mx; int fuse_corner = cpr_vreg->corner_map[corner]; switch (cpr_vreg->vdd_mx_vmin_method) { switch (cpr_vreg->vdd_mx_vmin_method) { case VDD_MX_VMIN_APC: case VDD_MX_VMIN_APC: vdd_mx = apc_volt; vdd_mx = apc_volt; break; break; case VDD_MX_VMIN_APC_CORNER_CEILING: case VDD_MX_VMIN_APC_CORNER_CEILING: vdd_mx = cpr_vreg->ceiling_volt[corner]; vdd_mx = cpr_vreg->ceiling_volt[fuse_corner]; break; break; case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING: case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING: vdd_mx = cpr_vreg->pvs_corner_v[APC_PVS_SLOW] vdd_mx = cpr_vreg->pvs_corner_v[APC_PVS_SLOW] Loading @@ -501,15 +517,19 @@ static int cpr_mx_set(struct cpr_regulator *cpr_vreg, int corner, int vdd_mx_vmin) int vdd_mx_vmin) { { int rc; int rc; int fuse_corner = cpr_vreg->corner_map[corner]; rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin, rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin, cpr_vreg->vdd_mx_vmax); cpr_vreg->vdd_mx_vmax); cpr_debug("[corner:%d] %d uV\n", corner, vdd_mx_vmin); cpr_debug("[corner:%d, fuse_corner:%d] %d uV\n", corner, if (!rc) fuse_corner, vdd_mx_vmin); if (!rc) { cpr_vreg->vdd_mx_vmin = vdd_mx_vmin; cpr_vreg->vdd_mx_vmin = vdd_mx_vmin; else } else { pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n", pr_err("set: vdd_mx [corner:%d, fuse_corner:%d] = %d uV failed: rc=%d\n", corner, vdd_mx_vmin, rc); corner, fuse_corner, vdd_mx_vmin, rc); } return rc; return rc; } } Loading Loading @@ -547,10 +567,11 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, enum voltage_change_dir dir) enum voltage_change_dir dir) { { u32 reg_val, error_steps, reg_mask; u32 reg_val, error_steps, reg_mask; int last_volt, new_volt, corner; int last_volt, new_volt, corner, fuse_corner; u32 gcnt, quot; u32 gcnt, quot; corner = cpr_vreg->corner; corner = cpr_vreg->corner; fuse_corner = cpr_vreg->corner_map[corner]; reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0); reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0); Loading @@ -558,20 +579,22 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, & RBCPR_RESULT0_ERROR_STEPS_MASK; & RBCPR_RESULT0_ERROR_STEPS_MASK; last_volt = cpr_vreg->last_volt[corner]; last_volt = cpr_vreg->last_volt[corner]; cpr_debug_irq("last_volt[corner:%d] = %d uV\n", corner, last_volt); cpr_debug_irq("last_volt[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, last_volt); gcnt = cpr_read(cpr_vreg, REG_RBCPR_GCNT_TARGET gcnt = cpr_read(cpr_vreg, REG_RBCPR_GCNT_TARGET (cpr_vreg->cpr_fuse_ro_sel[corner])); (cpr_vreg->cpr_fuse_ro_sel[fuse_corner])); quot = gcnt & ((1 << RBCPR_GCNT_TARGET_GCNT_SHIFT) - 1); quot = gcnt & ((1 << RBCPR_GCNT_TARGET_GCNT_SHIFT) - 1); if (dir == UP) { if (dir == UP) { cpr_debug_irq("Up: cpr status = 0x%08x (error_steps=%d)\n", cpr_debug_irq("Up: cpr status = 0x%08x (error_steps=%d)\n", reg_val, error_steps); reg_val, error_steps); if (last_volt >= cpr_vreg->ceiling_volt[corner]) { if (last_volt >= cpr_vreg->ceiling_volt[fuse_corner]) { cpr_debug_irq("[corn:%d] @ ceiling: %d >= %d: NACK\n", cpr_debug_irq( corner, last_volt, "[corn:%d, fuse_corn:%d] @ ceiling: %d >= %d: NACK\n", cpr_vreg->ceiling_volt[corner]); corner, fuse_corner, last_volt, cpr_vreg->ceiling_volt[fuse_corner]); cpr_irq_clr_nack(cpr_vreg); cpr_irq_clr_nack(cpr_vreg); cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, Loading @@ -594,11 +617,12 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Calculate new voltage */ /* Calculate new voltage */ new_volt = last_volt + (error_steps * cpr_vreg->step_volt); new_volt = last_volt + (error_steps * cpr_vreg->step_volt); if (new_volt > cpr_vreg->ceiling_volt[corner]) { if (new_volt > cpr_vreg->ceiling_volt[fuse_corner]) { cpr_debug_irq("new_volt(%d) >= ceiling(%d): Clamp\n", cpr_debug_irq("new_volt(%d) >= ceiling(%d): Clamp\n", new_volt, new_volt, cpr_vreg->ceiling_volt[corner]); cpr_vreg->ceiling_volt[fuse_corner]); new_volt = cpr_vreg->ceiling_volt[corner]; new_volt = cpr_vreg->ceiling_volt[fuse_corner]; } } if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { Loading @@ -619,16 +643,18 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Ack */ /* Ack */ cpr_irq_clr_ack(cpr_vreg); cpr_irq_clr_ack(cpr_vreg); cpr_debug_irq("UP: -> new_volt[corner:%d] = %d uV\n", cpr_debug_irq( corner, new_volt); "UP: -> new_volt[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, new_volt); } else if (dir == DOWN) { } else if (dir == DOWN) { cpr_debug_irq("Down: cpr status = 0x%08x (error_steps=%d)\n", cpr_debug_irq("Down: cpr status = 0x%08x (error_steps=%d)\n", reg_val, error_steps); reg_val, error_steps); if (last_volt <= cpr_vreg->floor_volt[corner]) { if (last_volt <= cpr_vreg->floor_volt[fuse_corner]) { cpr_debug_irq("[corn:%d] @ floor: %d <= %d: NACK\n", cpr_debug_irq( corner, last_volt, "[corn:%d, fuse_corner:%d] @ floor: %d <= %d: NACK\n", cpr_vreg->floor_volt[corner]); corner, fuse_corner, last_volt, cpr_vreg->floor_volt[fuse_corner]); cpr_irq_clr_nack(cpr_vreg); cpr_irq_clr_nack(cpr_vreg); cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, cpr_debug_irq("gcnt = 0x%08x (quot = %d)\n", gcnt, Loading @@ -655,11 +681,11 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Calculte new voltage */ /* Calculte new voltage */ new_volt = last_volt - (error_steps * cpr_vreg->step_volt); new_volt = last_volt - (error_steps * cpr_vreg->step_volt); if (new_volt < cpr_vreg->floor_volt[corner]) { if (new_volt < cpr_vreg->floor_volt[fuse_corner]) { cpr_debug_irq("new_volt(%d) < floor(%d): Clamp\n", cpr_debug_irq("new_volt(%d) < floor(%d): Clamp\n", new_volt, new_volt, cpr_vreg->floor_volt[corner]); cpr_vreg->floor_volt[fuse_corner]); new_volt = cpr_vreg->floor_volt[corner]; new_volt = cpr_vreg->floor_volt[fuse_corner]; } } if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) { Loading @@ -678,8 +704,9 @@ static void cpr_scale(struct cpr_regulator *cpr_vreg, /* Ack */ /* Ack */ cpr_irq_clr_ack(cpr_vreg); cpr_irq_clr_ack(cpr_vreg); cpr_debug_irq("DOWN: -> new_volt[corner:%d] = %d uV\n", cpr_debug_irq( corner, new_volt); "DOWN: -> new_volt[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, new_volt); } } } } Loading Loading @@ -801,6 +828,7 @@ static int cpr_regulator_set_voltage(struct regulator_dev *rdev, int rc; int rc; int new_volt; int new_volt; enum voltage_change_dir change_dir = NO_CHANGE; enum voltage_change_dir change_dir = NO_CHANGE; int fuse_corner = cpr_vreg->corner_map[corner]; mutex_lock(&cpr_vreg->cpr_mutex); mutex_lock(&cpr_vreg->cpr_mutex); Loading @@ -808,10 +836,12 @@ static int cpr_regulator_set_voltage(struct regulator_dev *rdev, cpr_ctl_disable(cpr_vreg); cpr_ctl_disable(cpr_vreg); new_volt = cpr_vreg->last_volt[corner]; new_volt = cpr_vreg->last_volt[corner]; } else { } else { new_volt = cpr_vreg->pvs_corner_v[cpr_vreg->process][corner]; new_volt = cpr_vreg->pvs_corner_v [cpr_vreg->process][fuse_corner]; } } cpr_debug("[corner:%d] = %d uV\n", corner, new_volt); cpr_debug("[corner:%d, fuse_corner:%d] = %d uV\n", corner, fuse_corner, new_volt); if (corner > cpr_vreg->corner) if (corner > cpr_vreg->corner) change_dir = UP; change_dir = UP; Loading Loading @@ -904,11 +934,12 @@ static int cpr_regulator_resume(struct platform_device *pdev) #define cpr_regulator_resume NULL #define cpr_regulator_resume NULL #endif #endif static int cpr_config(struct cpr_regulator *cpr_vreg) static int cpr_config(struct cpr_regulator *cpr_vreg, struct device *dev) { { int i; int i; u32 val, gcnt, reg; u32 val, gcnt, reg; void __iomem *rbcpr_clk; void __iomem *rbcpr_clk; int size; /* Use 19.2 MHz clock for CPR. */ /* Use 19.2 MHz clock for CPR. */ rbcpr_clk = ioremap(cpr_vreg->rbcpr_clk_addr, 4); rbcpr_clk = ioremap(cpr_vreg->rbcpr_clk_addr, 4); Loading Loading @@ -991,9 +1022,14 @@ static int cpr_config(struct cpr_regulator *cpr_vreg) if (val <= RBCPR_VER_2) if (val <= RBCPR_VER_2) cpr_vreg->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS; cpr_vreg->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS; cpr_corner_save(cpr_vreg, CPR_CORNER_SVS); size = cpr_vreg->num_corners + 1; cpr_corner_save(cpr_vreg, CPR_CORNER_NORMAL); cpr_vreg->save_ctl = devm_kzalloc(dev, sizeof(int) * size, GFP_KERNEL); cpr_corner_save(cpr_vreg, CPR_CORNER_TURBO); cpr_vreg->save_irq = devm_kzalloc(dev, sizeof(int) * size, GFP_KERNEL); if (!cpr_vreg->save_ctl || !cpr_vreg->save_irq) return -ENOMEM; for (i = 1; i < size; i++) cpr_corner_save(cpr_vreg, i); return 0; return 0; } } Loading Loading @@ -1224,6 +1260,106 @@ static int cpr_voltage_uplift_wa_inc_quot(struct cpr_regulator *cpr_vreg, return rc; return rc; } } static int cpr_get_of_corner_mappings(struct cpr_regulator *cpr_vreg, struct device *dev) { int rc = 0; int i, size, stripe_size; struct property *prop; u32 *tmp; bool corners_mapped; prop = of_find_property(dev->of_node, "qti,cpr-corner-map", NULL); if (prop) { size = prop->length / sizeof(u32); corners_mapped = true; } else { size = CPR_CORNER_MAX - 1; corners_mapped = false; } cpr_vreg->corner_map = devm_kzalloc(dev, sizeof(int) * (size + 1), GFP_KERNEL); if (!cpr_vreg->corner_map) { pr_err("Can't allocate cpr_vreg->corner_map memory\n"); return -ENOMEM; } cpr_vreg->num_corners = size; if (!corners_mapped) { for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) cpr_vreg->corner_map[i] = i; } else { rc = of_property_read_u32_array(dev->of_node, "qti,cpr-corner-map", &cpr_vreg->corner_map[1], size); if (rc) { pr_err("qti,cpr-corner-map missing, rc = %d", rc); return rc; } } cpr_vreg->quot_adjust = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->quot_adjust) { pr_err("Can't allocate cpr_vreg->quot_adjust memory\n"); return -ENOMEM; } prop = of_find_property(dev->of_node, "qti,cpr-quot-adjust-table", NULL); if (prop) { if (!corners_mapped) { pr_err("qti,cpr-corner-map missing\n"); return -EINVAL; } size = prop->length / sizeof(u32); tmp = kzalloc(sizeof(u32) * size, GFP_KERNEL); if (!tmp) return -ENOMEM; rc = of_property_read_u32_array(dev->of_node, "qti,cpr-quot-adjust-table", tmp, size); if (rc) { pr_err("qti,cpr-quot-adjust-table missing, rc = %d", rc); kfree(tmp); return rc; } stripe_size = sizeof(struct quot_adjust_info) / sizeof(int); if ((size % stripe_size) != 0) { pr_err("qti,cpr-quot-adjust-table data is not correct"); kfree(tmp); return -EINVAL; } for (i = 0; i < size; i += stripe_size) { if (tmp[i] == cpr_vreg->speed_bin) { if (tmp[i + 1] >= 1 && tmp[i + 1] <= cpr_vreg->num_corners) { cpr_vreg->quot_adjust[tmp[i + 1]] = tmp[i + 2]; } else { pr_err("qti,cpr-quot-adjust-table data is not correct"); kfree(tmp); return -EINVAL; } } } kfree(tmp); } return 0; } static int cpr_init_cpr_efuse(struct platform_device *pdev, static int cpr_init_cpr_efuse(struct platform_device *pdev, struct cpr_regulator *cpr_vreg) struct cpr_regulator *cpr_vreg) { { Loading Loading @@ -1353,6 +1489,10 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, } } } } rc = cpr_get_of_corner_mappings(cpr_vreg, &pdev->dev); if (rc) return rc; cpr_vreg->cpr_fuse_bits = fuse_bits; cpr_vreg->cpr_fuse_bits = fuse_bits; if (!cpr_vreg->cpr_fuse_bits) { if (!cpr_vreg->cpr_fuse_bits) { cpr_vreg->cpr_fuse_disable = 1; cpr_vreg->cpr_fuse_disable = 1; Loading Loading @@ -1381,9 +1521,15 @@ static int cpr_init_cpr_efuse(struct platform_device *pdev, return 0; return 0; } } static int cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg) static int cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg, struct device *dev) { { int i; int i; int size = cpr_vreg->num_corners + 1; cpr_vreg->last_volt = devm_kzalloc(dev, sizeof(int) * size, GFP_KERNEL); if (!cpr_vreg->last_volt) return -EINVAL; /* Construct CPR voltage limits */ /* Construct CPR voltage limits */ for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) { for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) { Loading @@ -1391,8 +1537,11 @@ static int cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg) cpr_vreg->pvs_corner_v[APC_PVS_FAST][i]; cpr_vreg->pvs_corner_v[APC_PVS_FAST][i]; cpr_vreg->ceiling_volt[i] = cpr_vreg->ceiling_volt[i] = cpr_vreg->pvs_corner_v[APC_PVS_SLOW][i]; cpr_vreg->pvs_corner_v[APC_PVS_SLOW][i]; cpr_vreg->last_volt[i] = } cpr_vreg->pvs_corner_v[cpr_vreg->process][i]; for (i = 1; i < size; i++) { cpr_vreg->last_volt[i] = cpr_vreg->pvs_corner_v [cpr_vreg->process][cpr_vreg->corner_map[i]]; } } return 0; return 0; Loading Loading @@ -1492,7 +1641,9 @@ static int cpr_init_cpr(struct platform_device *pdev, resource_size(res)); resource_size(res)); /* Init all voltage set points of APC regulator for CPR */ /* Init all voltage set points of APC regulator for CPR */ cpr_init_cpr_voltages(cpr_vreg); rc = cpr_init_cpr_voltages(cpr_vreg, &pdev->dev); if (rc) return rc; /* Init CPR configuration parameters */ /* Init CPR configuration parameters */ rc = cpr_init_cpr_parameters(pdev, cpr_vreg); rc = cpr_init_cpr_parameters(pdev, cpr_vreg); Loading @@ -1507,7 +1658,7 @@ static int cpr_init_cpr(struct platform_device *pdev, } } /* Configure CPR HW but keep it disabled */ /* Configure CPR HW but keep it disabled */ rc = cpr_config(cpr_vreg); rc = cpr_config(cpr_vreg, &pdev->dev); if (rc) if (rc) return rc; return rc; Loading Loading @@ -1593,6 +1744,8 @@ static void cpr_parse_speed_bin_fuse(struct cpr_regulator *cpr_vreg, pr_info("[row: %d]: 0x%llx, speed_bits = %d\n", pr_info("[row: %d]: 0x%llx, speed_bits = %d\n", fuse_sel[0], fuse_bits, speed_bits); fuse_sel[0], fuse_bits, speed_bits); cpr_vreg->speed_bin = speed_bits; cpr_vreg->speed_bin = speed_bits; } else { cpr_vreg->speed_bin = UINT_MAX; } } } } Loading Loading @@ -1695,18 +1848,26 @@ static ssize_t cpr_debugfs_read(struct file *file, char __user *buff, ssize_t len, ret = 0; ssize_t len, ret = 0; u32 gcnt, ro_sel, ctl, irq_status, reg, error_steps; u32 gcnt, ro_sel, ctl, irq_status, reg, error_steps; u32 step_dn, step_up, error, error_lt0, busy; u32 step_dn, step_up, error, error_lt0, busy; int fuse_corner; if (!debugfs_buf) if (!debugfs_buf) return -ENOMEM; return -ENOMEM; mutex_lock(&the_cpr->cpr_mutex); mutex_lock(&the_cpr->cpr_mutex); fuse_corner = the_cpr->corner_map[the_cpr->corner]; len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, "corner = %d, current_volt = %d uV\n", "corner = %d, current_volt = %d uV\n", the_cpr->corner, the_cpr->last_volt[the_cpr->corner]); the_cpr->corner, the_cpr->last_volt[the_cpr->corner]); ret += len; ret += len; ro_sel = the_cpr->cpr_fuse_ro_sel[the_cpr->corner]; len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, "fuse_corner = %d, current_volt = %d uV\n", fuse_corner, the_cpr->last_volt[the_cpr->corner]); ret += len; ro_sel = the_cpr->cpr_fuse_ro_sel[fuse_corner]; gcnt = cpr_read(the_cpr, REG_RBCPR_GCNT_TARGET(ro_sel)); gcnt = cpr_read(the_cpr, REG_RBCPR_GCNT_TARGET(ro_sel)); len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret, "rbcpr_gcnt_target (%u) = 0x%02X\n", ro_sel, gcnt); "rbcpr_gcnt_target (%u) = 0x%02X\n", ro_sel, gcnt); Loading