Loading Documentation/devicetree/bindings/regulator/cpr-regulator.txt +77 −0 Original line number Diff line number Diff line Loading @@ -437,6 +437,41 @@ Optional properties: qcom,cpr-fuse-version-map property is not specified, then qcom,cpr-virtual-corner-quotient-adjustment must contain a single tuple which is then applied unconditionally. - qcom,cpr-cpus: Array of CPU phandles which correspond to the cores that this cpr-regulator device must monitor when adjusting the voltage and/or target quotient based upon the number of online cores. This property must be specified in order to utilize the qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment or qcom,cpr-online-cpu-virtual-corner-quotient-adjustment properties. - qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment: Array of tuples where each tuple specifies the voltage adjustment for each corner. These adjustments apply to the initial voltage of each corner. The size of each tuple must be equal to qcom,cpr-fuse-corners if consumers request fuse corners or the length of qcom,cpr-corner-map if consumers request virtual corners. In each tuple, the value corresponds to the voltage adjustment when running at that corner at init, from lowest to highest. The tuples must be organized into 1 group if qcom,cpr-fuse-version-map is not specified or the same number of groups as the number of tuples in qcom,cpr-fuse-version-map. The i-th group of tuples corresponds to the voltage adjustments for i-th fuse version map tuple. In each group, there are 1 plus length of qcom,cpr-cpus tuples, each tuple corresponds to the number of cores online, from 0 to the number of elements in qcom,cpr-cpus. - qcom,cpr-online-cpu-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given virtual corner may be reduced to the per number of cores online, per-virtual corner ceiling voltage value. This property takes precedence over qcom,cpr-scaled-init-voltage-as-ceiling if both are specified. - qcom,cpr-online-cpu-virtual-corner-quotient-adjustment: Array of tuples where each tuple specifies the quotient adjustment for each corner. These adjustments will be applied to each corner at run time. The size of each tuple must be equal to qcom,cpr-fuse-corners if consumers request fuse corners or the length of qcom,cpr-corner-map if consumers request virtual corners. In each tuple, the value corresponds to the quotient adjustment when running at that corner, from lowest to highest. The tuples must be organized into 1 group if qcom,cpr-fuse-version-map is not specified or the same number of groups as the number of tuples in qcom,cpr-fuse-version-map. The i-th group of tuples corresponds to the quotient adjustments for i-th fuse version map tuple. In each group, there are 1 plus length of qcom,cpr-cpus tuples, each tuple corresponds to the number of cores online, from 0 to the number of elements in qcom,cpr-cpus. - qcom,cpr-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given virtual corner may be reduced to the per-fuse-corner initial voltage fuse value. - qcom,cpr-scaled-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given Loading Loading @@ -578,6 +613,9 @@ Optional properties: qcom,cpr-min-quot-diff-adjustment must contain a single tuple which is then applied unconditionally. The qcom,cpr-min-quot-diff-adjustment property must be specified if the qcom,cpr-fuse-min-quot-diff property is specified. - qcom,cpr-skip-voltage-change-during-suspend: Boolean property which indicates that the CPR voltage should not be adjusted based upon the number of online cores while entering or exiting system suspend. Example: apc_vreg_corner: regulator@f9018000 { Loading Loading @@ -752,6 +790,45 @@ Example: <0 0 0 100 0 0 0 0 0 0 0 0>, <0 0 0 0 0 0 0 0 0 0 0 (-300)>, <0 0 0 (-60) 0 0 0 0 0 0 0 0>; qcom,cpr-cpus = <&CPU0 &CPU1 &CPU2 &CPU3>; qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment = /* 1st fuse version tuple matched */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */ <0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 2nd fuse version tuple matched */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */ <0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 3rd fuse version tuple matched */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */ <0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>; /* 4 CPUs online */ qcom,cpr-online-cpu-virtual-corner-quotient-adjustment = /* 1st fuse version tuple matched */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 0 CPUs online */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 1 CPUs online */ <0 0 0 (-3) (-3) (-3) (-3) (-3) (-6) 0 (-6) (-6)>, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 2nd fuse version tuple matched */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 0 CPUs online */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 1 CPUs online */ <0 0 0 (-3) (-3) (-3) (-3) (-3) (-6) 0 (-6) (-6)>, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 3rd fuse version tuple matched */ <0 0 0 (-21) (-21) (-21) (-32) (-32) (-42) 0 (-42) (-63)>, /* 0 CPUs online */ <0 0 0 (-21) (-21) (-21) (-32) (-32) (-42) 0 (-42) (-63)>, /* 1 CPUs online */ <0 0 0 (-11) (-11) (-11) (-11) (-11) (-21) 0 (-21) (-21)>, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>; /* 4 CPUs online */ qcom,cpr-allowed = <0>, <1>, Loading drivers/regulator/cpr-regulator.c +479 −18 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/module.h> #include <linux/cpu.h> #include <linux/err.h> #include <linux/string.h> #include <linux/kernel.h> Loading Loading @@ -294,7 +295,19 @@ struct cpr_regulator { u32 num_corners; int *quot_adjust; int num_adj_cpus; int online_cpus; int *adj_cpus; int **adj_cpus_save_ctl; int **adj_cpus_save_irq; int **adj_cpus_last_volt; int **adj_cpus_quot_adjust; int **adj_cpus_open_loop_volt; bool adj_cpus_open_loop_volt_as_ceiling; struct notifier_block cpu_notifier; bool is_cpr_suspended; bool skip_voltage_change_during_suspend; }; #define CPR_DEBUG_MASK_IRQ BIT(0) Loading Loading @@ -1013,7 +1026,7 @@ static int cpr_regulator_disable(struct regulator_dev *rdev) } static int cpr_regulator_set_voltage(struct regulator_dev *rdev, int corner, int corner_max, unsigned *selector) int corner, bool reset_quot) { struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev); int rc; Loading Loading @@ -1044,6 +1057,9 @@ static int cpr_regulator_set_voltage(struct regulator_dev *rdev, if (cpr_is_allowed(cpr_vreg) && cpr_vreg->vreg_enabled) { cpr_irq_clr(cpr_vreg); if (reset_quot) cpr_corner_restore(cpr_vreg, corner); else cpr_corner_switch(cpr_vreg, corner); cpr_ctl_enable(cpr_vreg, corner); } Loading @@ -1056,6 +1072,12 @@ _exit: return rc; } static int cpr_regulator_set_voltage_op(struct regulator_dev *rdev, int corner, int corner_max, unsigned *selector) { return cpr_regulator_set_voltage(rdev, corner, false); } static int cpr_regulator_get_voltage(struct regulator_dev *rdev) { struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev); Loading @@ -1067,7 +1089,7 @@ static struct regulator_ops cpr_corner_ops = { .enable = cpr_regulator_enable, .disable = cpr_regulator_disable, .is_enabled = cpr_regulator_is_enabled, .set_voltage = cpr_regulator_set_voltage, .set_voltage = cpr_regulator_set_voltage_op, .get_voltage = cpr_regulator_get_voltage, }; Loading @@ -1076,15 +1098,10 @@ static int cpr_suspend(struct cpr_regulator *cpr_vreg) { cpr_debug(cpr_vreg, "suspend\n"); mutex_lock(&cpr_vreg->cpr_mutex); cpr_ctl_disable(cpr_vreg); cpr_irq_clr(cpr_vreg); cpr_vreg->is_cpr_suspended = true; mutex_unlock(&cpr_vreg->cpr_mutex); return 0; } Loading @@ -1093,14 +1110,10 @@ static int cpr_resume(struct cpr_regulator *cpr_vreg) { cpr_debug(cpr_vreg, "resume\n"); mutex_lock(&cpr_vreg->cpr_mutex); cpr_vreg->is_cpr_suspended = false; cpr_irq_clr(cpr_vreg); cpr_ctl_enable(cpr_vreg, cpr_vreg->corner); mutex_unlock(&cpr_vreg->cpr_mutex); return 0; } Loading @@ -1108,21 +1121,35 @@ static int cpr_regulator_suspend(struct platform_device *pdev, pm_message_t state) { struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev); int rc = 0; mutex_lock(&cpr_vreg->cpr_mutex); if (cpr_is_allowed(cpr_vreg)) return cpr_suspend(cpr_vreg); else return 0; rc = cpr_suspend(cpr_vreg); cpr_vreg->is_cpr_suspended = true; mutex_unlock(&cpr_vreg->cpr_mutex); return rc; } static int cpr_regulator_resume(struct platform_device *pdev) { struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev); int rc = 0; mutex_lock(&cpr_vreg->cpr_mutex); cpr_vreg->is_cpr_suspended = false; if (cpr_is_allowed(cpr_vreg)) return cpr_resume(cpr_vreg); else return 0; rc = cpr_resume(cpr_vreg); mutex_unlock(&cpr_vreg->cpr_mutex); return rc; } #else #define cpr_regulator_suspend NULL Loading Loading @@ -3483,6 +3510,429 @@ static int cpr_init_cpr_parameters(struct platform_device *pdev, return 0; } static void cpr_regulator_switch_adj_cpus(struct cpr_regulator *cpr_vreg) { cpr_vreg->last_volt = cpr_vreg->adj_cpus_last_volt [cpr_vreg->online_cpus]; cpr_vreg->save_ctl = cpr_vreg->adj_cpus_save_ctl[cpr_vreg->online_cpus]; cpr_vreg->save_irq = cpr_vreg->adj_cpus_save_irq[cpr_vreg->online_cpus]; if (cpr_vreg->adj_cpus_quot_adjust) cpr_vreg->quot_adjust = cpr_vreg->adj_cpus_quot_adjust [cpr_vreg->online_cpus]; if (cpr_vreg->adj_cpus_open_loop_volt) cpr_vreg->open_loop_volt = cpr_vreg->adj_cpus_open_loop_volt [cpr_vreg->online_cpus]; if (cpr_vreg->adj_cpus_open_loop_volt_as_ceiling) cpr_vreg->ceiling_volt = cpr_vreg->open_loop_volt; } static void cpr_regulator_set_online_cpus(struct cpr_regulator *cpr_vreg) { int i, j; cpr_vreg->online_cpus = 0; get_online_cpus(); for_each_online_cpu(i) for (j = 0; j < cpr_vreg->num_adj_cpus; j++) if (i == cpr_vreg->adj_cpus[j]) cpr_vreg->online_cpus++; put_online_cpus(); } static int cpr_regulator_cpu_callback(struct notifier_block *nb, unsigned long action, void *data) { struct cpr_regulator *cpr_vreg = container_of(nb, struct cpr_regulator, cpu_notifier); int prev_online_cpus = cpr_vreg->online_cpus; int cpu = (long)data; int rc, i; action &= ~CPU_TASKS_FROZEN; if (action != CPU_UP_PREPARE && action != CPU_UP_CANCELED && action != CPU_DEAD) return NOTIFY_OK; if (cpr_vreg->skip_voltage_change_during_suspend) { mutex_lock(&cpr_vreg->cpr_mutex); if (cpr_vreg->is_cpr_suspended) { /* Do nothing during system suspend/resume */ mutex_unlock(&cpr_vreg->cpr_mutex); return NOTIFY_OK; } mutex_unlock(&cpr_vreg->cpr_mutex); } cpr_regulator_set_online_cpus(cpr_vreg); if (action == CPU_UP_PREPARE) for (i = 0; i < cpr_vreg->num_adj_cpus; i++) if (cpu == cpr_vreg->adj_cpus[i]) { cpr_vreg->online_cpus++; break; } if (cpr_vreg->online_cpus == prev_online_cpus) return NOTIFY_OK; cpr_debug(cpr_vreg, "adjusting quotient for %d cpus\n", cpr_vreg->online_cpus); cpr_regulator_switch_adj_cpus(cpr_vreg); if (cpr_vreg->corner) { rc = cpr_regulator_set_voltage(cpr_vreg->rdev, cpr_vreg->corner, true); if (rc) cpr_err(cpr_vreg, "could not update quotient, rc=%d\n", rc); } return NOTIFY_OK; } static int cpr_parse_adj_cpus_init_voltage(struct cpr_regulator *cpr_vreg, struct device *dev) { int rc, i, j, k, tuple_count, tuple_match, len, offset; int *temp; if (!of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment", NULL)) 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 voltage 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; } len = (cpr_vreg->num_adj_cpus + 1) * tuple_count * cpr_vreg->num_corners; temp = kzalloc(sizeof(int) * len, GFP_KERNEL); if (!temp) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } cpr_vreg->adj_cpus_open_loop_volt = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_open_loop_volt) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } cpr_vreg->adj_cpus_open_loop_volt[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_open_loop_volt[0]) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } for (i = 1; i <= cpr_vreg->num_adj_cpus; i++) cpr_vreg->adj_cpus_open_loop_volt[i] = cpr_vreg->adj_cpus_open_loop_volt[0] + i * (cpr_vreg->num_corners + 1); rc = of_property_read_u32_array(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment", temp, len); if (rc) { cpr_err(cpr_vreg, "failed to read qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment, rc=%d\n", rc); goto done; } cpr_debug(cpr_vreg, "Open loop voltage based on number of online CPUs:\n"); offset = tuple_match * cpr_vreg->num_corners * (cpr_vreg->num_adj_cpus + 1); for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) { for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) { k = j - 1 + offset; cpr_vreg->adj_cpus_open_loop_volt[i][j] = cpr_vreg->open_loop_volt[j] + temp[k]; cpr_vreg->adj_cpus_open_loop_volt[i][j] = DIV_ROUND_UP(cpr_vreg-> adj_cpus_open_loop_volt[i][j], cpr_vreg->step_volt) * cpr_vreg->step_volt; if (cpr_vreg->adj_cpus_open_loop_volt[i][j] > cpr_vreg->ceiling_volt[j]) cpr_vreg->adj_cpus_open_loop_volt[i][j] = cpr_vreg->ceiling_volt[j]; if (cpr_vreg->adj_cpus_open_loop_volt[i][j] < cpr_vreg->floor_volt[j]) cpr_vreg->adj_cpus_open_loop_volt[i][j] = cpr_vreg->floor_volt[j]; cpr_debug(cpr_vreg, "cpus=%d, corner=%d, volt=%d\n", i, j, cpr_vreg->adj_cpus_open_loop_volt[i][j]); } offset += cpr_vreg->num_corners; } cpr_vreg->adj_cpus_open_loop_volt_as_ceiling = of_property_read_bool(dev->of_node, "qcom,cpr-online-cpu-init-voltage-as-ceiling"); done: kfree(temp); return rc; } static int cpr_parse_adj_cpus_target_quot(struct cpr_regulator *cpr_vreg, struct device *dev) { int rc, i, j, k, tuple_count, tuple_match, len, offset; int *temp; if (!of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment", NULL)) 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; } len = (cpr_vreg->num_adj_cpus + 1) * tuple_count * cpr_vreg->num_corners; temp = kzalloc(sizeof(int) * len, GFP_KERNEL); if (!temp) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } cpr_vreg->adj_cpus_quot_adjust = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_quot_adjust) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } cpr_vreg->adj_cpus_quot_adjust[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_quot_adjust[0]) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } for (i = 1; i <= cpr_vreg->num_adj_cpus; i++) cpr_vreg->adj_cpus_quot_adjust[i] = cpr_vreg->adj_cpus_quot_adjust[0] + i * (cpr_vreg->num_corners + 1); rc = of_property_read_u32_array(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment", temp, len); if (rc) { cpr_err(cpr_vreg, "failed to read qcom,cpr-online-cpu-virtual-corner-quotient-adjustment, rc=%d\n", rc); goto done; } cpr_debug(cpr_vreg, "Target quotients based on number of online CPUs:\n"); offset = tuple_match * cpr_vreg->num_corners * (cpr_vreg->num_adj_cpus + 1); for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) { for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) { k = j - 1 + offset; cpr_vreg->adj_cpus_quot_adjust[i][j] = cpr_vreg->quot_adjust[j] - temp[k]; cpr_debug(cpr_vreg, "cpus=%d, corner=%d, quot=%d\n", i, j, cpr_vreg->cpr_fuse_target_quot[ cpr_vreg->corner_map[j]] - cpr_vreg->adj_cpus_quot_adjust[i][j]); } offset += cpr_vreg->num_corners; } done: kfree(temp); return rc; } static int cpr_init_per_cpu_adjustments(struct cpr_regulator *cpr_vreg, struct device *dev) { struct device_node *cpu_node; int rc, i, j, cpu; if (!of_find_property(dev->of_node, "qcom,cpr-cpus", &cpr_vreg->num_adj_cpus)) { /* No per-online CPU adjustment needed */ return 0; } cpr_vreg->num_adj_cpus /= sizeof(u32); if (!of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment", NULL) && !of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment", NULL)) { cpr_err(cpr_vreg, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment and/or qcom,cpr-online-cpu-virtual-corner-quotient-adjustment must be specified\n"); return -EINVAL; } cpr_vreg->adj_cpus = devm_kzalloc(dev, sizeof(int) * cpr_vreg->num_adj_cpus, GFP_KERNEL); if (!cpr_vreg->adj_cpus) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } for (i = 0; i < cpr_vreg->num_adj_cpus; i++) { cpu_node = of_parse_phandle(dev->of_node, "qcom,cpr-cpus", i); if (!cpu_node) { cpr_err(cpr_vreg, "could not find CPU node %d\n", i); return -EINVAL; } cpr_vreg->adj_cpus[i] = -1; for_each_possible_cpu(cpu) { if (of_get_cpu_node(cpu, NULL) == cpu_node) { cpr_vreg->adj_cpus[i] = cpu; break; } } of_node_put(cpu_node); } rc = cpr_parse_adj_cpus_init_voltage(cpr_vreg, dev); if (rc) { cpr_err(cpr_vreg, "cpr_parse_adj_cpus_init_voltage failed: rc =%d\n", rc); return rc; } rc = cpr_parse_adj_cpus_target_quot(cpr_vreg, dev); if (rc) { cpr_err(cpr_vreg, "cpr_parse_adj_cpus_target_quot failed: rc =%d\n", rc); return rc; } cpr_vreg->adj_cpus_last_volt = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_ctl = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_irq = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_last_volt || !cpr_vreg->adj_cpus_save_ctl || !cpr_vreg->adj_cpus_save_irq) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } cpr_vreg->adj_cpus_last_volt[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_ctl[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_irq[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_last_volt[0] || !cpr_vreg->adj_cpus_save_ctl[0] || !cpr_vreg->adj_cpus_save_irq[0]) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } for (i = 1; i <= cpr_vreg->num_adj_cpus; i++) { j = i * (cpr_vreg->num_corners + 1); cpr_vreg->adj_cpus_last_volt[i] = cpr_vreg->adj_cpus_last_volt[0] + j; cpr_vreg->adj_cpus_save_ctl[i] = cpr_vreg->adj_cpus_save_ctl[0] + j; cpr_vreg->adj_cpus_save_irq[i] = cpr_vreg->adj_cpus_save_irq[0] + j; } for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) { for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) { cpr_vreg->adj_cpus_save_ctl[i][j] = cpr_vreg->save_ctl[j]; cpr_vreg->adj_cpus_save_irq[i][j] = cpr_vreg->save_irq[j]; cpr_vreg->adj_cpus_last_volt[i][j] = cpr_vreg->adj_cpus_open_loop_volt ? cpr_vreg->adj_cpus_open_loop_volt[i][j] : cpr_vreg->open_loop_volt[j]; } } cpr_regulator_set_online_cpus(cpr_vreg); cpr_debug(cpr_vreg, "%d cpus online\n", cpr_vreg->online_cpus); devm_kfree(dev, cpr_vreg->last_volt); devm_kfree(dev, cpr_vreg->save_ctl); devm_kfree(dev, cpr_vreg->save_irq); if (cpr_vreg->adj_cpus_quot_adjust) devm_kfree(dev, cpr_vreg->quot_adjust); if (cpr_vreg->adj_cpus_open_loop_volt) devm_kfree(dev, cpr_vreg->open_loop_volt); if (cpr_vreg->adj_cpus_open_loop_volt_as_ceiling) devm_kfree(dev, cpr_vreg->ceiling_volt); cpr_regulator_switch_adj_cpus(cpr_vreg); cpr_vreg->skip_voltage_change_during_suspend = of_property_read_bool(dev->of_node, "qcom,cpr-skip-voltage-change-during-suspend"); cpr_vreg->cpu_notifier.notifier_call = cpr_regulator_cpu_callback; register_hotcpu_notifier(&cpr_vreg->cpu_notifier); return rc; } static int cpr_init_cpr(struct platform_device *pdev, struct cpr_regulator *cpr_vreg) { Loading Loading @@ -4285,6 +4735,14 @@ static int cpr_regulator_probe(struct platform_device *pdev) goto err_out; } /* Load per-online CPU adjustment data */ rc = cpr_init_per_cpu_adjustments(cpr_vreg, &pdev->dev); if (rc) { cpr_err(cpr_vreg, "cpr_init_per_cpu_adjustments failed: rc=%d\n", rc); goto err_out; } cpr_efuse_free(cpr_vreg); /* Loading Loading @@ -4343,6 +4801,9 @@ static int cpr_regulator_remove(struct platform_device *pdev) list_del(&cpr_vreg->list); mutex_unlock(&cpr_regulator_list_mutex); if (cpr_vreg->adj_cpus) unregister_hotcpu_notifier(&cpr_vreg->cpu_notifier); cpr_apc_exit(cpr_vreg); cpr_debugfs_remove(cpr_vreg); regulator_unregister(cpr_vreg->rdev); Loading Loading
Documentation/devicetree/bindings/regulator/cpr-regulator.txt +77 −0 Original line number Diff line number Diff line Loading @@ -437,6 +437,41 @@ Optional properties: qcom,cpr-fuse-version-map property is not specified, then qcom,cpr-virtual-corner-quotient-adjustment must contain a single tuple which is then applied unconditionally. - qcom,cpr-cpus: Array of CPU phandles which correspond to the cores that this cpr-regulator device must monitor when adjusting the voltage and/or target quotient based upon the number of online cores. This property must be specified in order to utilize the qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment or qcom,cpr-online-cpu-virtual-corner-quotient-adjustment properties. - qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment: Array of tuples where each tuple specifies the voltage adjustment for each corner. These adjustments apply to the initial voltage of each corner. The size of each tuple must be equal to qcom,cpr-fuse-corners if consumers request fuse corners or the length of qcom,cpr-corner-map if consumers request virtual corners. In each tuple, the value corresponds to the voltage adjustment when running at that corner at init, from lowest to highest. The tuples must be organized into 1 group if qcom,cpr-fuse-version-map is not specified or the same number of groups as the number of tuples in qcom,cpr-fuse-version-map. The i-th group of tuples corresponds to the voltage adjustments for i-th fuse version map tuple. In each group, there are 1 plus length of qcom,cpr-cpus tuples, each tuple corresponds to the number of cores online, from 0 to the number of elements in qcom,cpr-cpus. - qcom,cpr-online-cpu-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given virtual corner may be reduced to the per number of cores online, per-virtual corner ceiling voltage value. This property takes precedence over qcom,cpr-scaled-init-voltage-as-ceiling if both are specified. - qcom,cpr-online-cpu-virtual-corner-quotient-adjustment: Array of tuples where each tuple specifies the quotient adjustment for each corner. These adjustments will be applied to each corner at run time. The size of each tuple must be equal to qcom,cpr-fuse-corners if consumers request fuse corners or the length of qcom,cpr-corner-map if consumers request virtual corners. In each tuple, the value corresponds to the quotient adjustment when running at that corner, from lowest to highest. The tuples must be organized into 1 group if qcom,cpr-fuse-version-map is not specified or the same number of groups as the number of tuples in qcom,cpr-fuse-version-map. The i-th group of tuples corresponds to the quotient adjustments for i-th fuse version map tuple. In each group, there are 1 plus length of qcom,cpr-cpus tuples, each tuple corresponds to the number of cores online, from 0 to the number of elements in qcom,cpr-cpus. - qcom,cpr-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given virtual corner may be reduced to the per-fuse-corner initial voltage fuse value. - qcom,cpr-scaled-init-voltage-as-ceiling: Boolean which indicates that the ceiling voltage used for a given Loading Loading @@ -578,6 +613,9 @@ Optional properties: qcom,cpr-min-quot-diff-adjustment must contain a single tuple which is then applied unconditionally. The qcom,cpr-min-quot-diff-adjustment property must be specified if the qcom,cpr-fuse-min-quot-diff property is specified. - qcom,cpr-skip-voltage-change-during-suspend: Boolean property which indicates that the CPR voltage should not be adjusted based upon the number of online cores while entering or exiting system suspend. Example: apc_vreg_corner: regulator@f9018000 { Loading Loading @@ -752,6 +790,45 @@ Example: <0 0 0 100 0 0 0 0 0 0 0 0>, <0 0 0 0 0 0 0 0 0 0 0 (-300)>, <0 0 0 (-60) 0 0 0 0 0 0 0 0>; qcom,cpr-cpus = <&CPU0 &CPU1 &CPU2 &CPU3>; qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment = /* 1st fuse version tuple matched */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */ <0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 2nd fuse version tuple matched */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */ <0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 3rd fuse version tuple matched */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 0 CPUs online */ <0 0 0 (-10000) (-10000) (-10000) (-15000) (-15000) (-20000) 0 (-20000) (-30000) >, /* 1 CPUs online */ <0 0 0 (-5000) (-5000) (-5000) (-5000) (-5000) (-10000) 0 (-10000) (-10000) >, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>; /* 4 CPUs online */ qcom,cpr-online-cpu-virtual-corner-quotient-adjustment = /* 1st fuse version tuple matched */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 0 CPUs online */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 1 CPUs online */ <0 0 0 (-3) (-3) (-3) (-3) (-3) (-6) 0 (-6) (-6)>, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 2nd fuse version tuple matched */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 0 CPUs online */ <0 0 0 (-6) (-6) (-6) (-9) (-9) (-12) 0 (-12) (-18)>, /* 1 CPUs online */ <0 0 0 (-3) (-3) (-3) (-3) (-3) (-6) 0 (-6) (-6)>, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 4 CPUs online */ /* 3rd fuse version tuple matched */ <0 0 0 (-21) (-21) (-21) (-32) (-32) (-42) 0 (-42) (-63)>, /* 0 CPUs online */ <0 0 0 (-21) (-21) (-21) (-32) (-32) (-42) 0 (-42) (-63)>, /* 1 CPUs online */ <0 0 0 (-11) (-11) (-11) (-11) (-11) (-21) 0 (-21) (-21)>, /* 2 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>, /* 3 CPUs online */ <0 0 0 0 0 0 0 0 0 0 0 0>; /* 4 CPUs online */ qcom,cpr-allowed = <0>, <1>, Loading
drivers/regulator/cpr-regulator.c +479 −18 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ #define pr_fmt(fmt) "%s: " fmt, __func__ #include <linux/module.h> #include <linux/cpu.h> #include <linux/err.h> #include <linux/string.h> #include <linux/kernel.h> Loading Loading @@ -294,7 +295,19 @@ struct cpr_regulator { u32 num_corners; int *quot_adjust; int num_adj_cpus; int online_cpus; int *adj_cpus; int **adj_cpus_save_ctl; int **adj_cpus_save_irq; int **adj_cpus_last_volt; int **adj_cpus_quot_adjust; int **adj_cpus_open_loop_volt; bool adj_cpus_open_loop_volt_as_ceiling; struct notifier_block cpu_notifier; bool is_cpr_suspended; bool skip_voltage_change_during_suspend; }; #define CPR_DEBUG_MASK_IRQ BIT(0) Loading Loading @@ -1013,7 +1026,7 @@ static int cpr_regulator_disable(struct regulator_dev *rdev) } static int cpr_regulator_set_voltage(struct regulator_dev *rdev, int corner, int corner_max, unsigned *selector) int corner, bool reset_quot) { struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev); int rc; Loading Loading @@ -1044,6 +1057,9 @@ static int cpr_regulator_set_voltage(struct regulator_dev *rdev, if (cpr_is_allowed(cpr_vreg) && cpr_vreg->vreg_enabled) { cpr_irq_clr(cpr_vreg); if (reset_quot) cpr_corner_restore(cpr_vreg, corner); else cpr_corner_switch(cpr_vreg, corner); cpr_ctl_enable(cpr_vreg, corner); } Loading @@ -1056,6 +1072,12 @@ _exit: return rc; } static int cpr_regulator_set_voltage_op(struct regulator_dev *rdev, int corner, int corner_max, unsigned *selector) { return cpr_regulator_set_voltage(rdev, corner, false); } static int cpr_regulator_get_voltage(struct regulator_dev *rdev) { struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev); Loading @@ -1067,7 +1089,7 @@ static struct regulator_ops cpr_corner_ops = { .enable = cpr_regulator_enable, .disable = cpr_regulator_disable, .is_enabled = cpr_regulator_is_enabled, .set_voltage = cpr_regulator_set_voltage, .set_voltage = cpr_regulator_set_voltage_op, .get_voltage = cpr_regulator_get_voltage, }; Loading @@ -1076,15 +1098,10 @@ static int cpr_suspend(struct cpr_regulator *cpr_vreg) { cpr_debug(cpr_vreg, "suspend\n"); mutex_lock(&cpr_vreg->cpr_mutex); cpr_ctl_disable(cpr_vreg); cpr_irq_clr(cpr_vreg); cpr_vreg->is_cpr_suspended = true; mutex_unlock(&cpr_vreg->cpr_mutex); return 0; } Loading @@ -1093,14 +1110,10 @@ static int cpr_resume(struct cpr_regulator *cpr_vreg) { cpr_debug(cpr_vreg, "resume\n"); mutex_lock(&cpr_vreg->cpr_mutex); cpr_vreg->is_cpr_suspended = false; cpr_irq_clr(cpr_vreg); cpr_ctl_enable(cpr_vreg, cpr_vreg->corner); mutex_unlock(&cpr_vreg->cpr_mutex); return 0; } Loading @@ -1108,21 +1121,35 @@ static int cpr_regulator_suspend(struct platform_device *pdev, pm_message_t state) { struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev); int rc = 0; mutex_lock(&cpr_vreg->cpr_mutex); if (cpr_is_allowed(cpr_vreg)) return cpr_suspend(cpr_vreg); else return 0; rc = cpr_suspend(cpr_vreg); cpr_vreg->is_cpr_suspended = true; mutex_unlock(&cpr_vreg->cpr_mutex); return rc; } static int cpr_regulator_resume(struct platform_device *pdev) { struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev); int rc = 0; mutex_lock(&cpr_vreg->cpr_mutex); cpr_vreg->is_cpr_suspended = false; if (cpr_is_allowed(cpr_vreg)) return cpr_resume(cpr_vreg); else return 0; rc = cpr_resume(cpr_vreg); mutex_unlock(&cpr_vreg->cpr_mutex); return rc; } #else #define cpr_regulator_suspend NULL Loading Loading @@ -3483,6 +3510,429 @@ static int cpr_init_cpr_parameters(struct platform_device *pdev, return 0; } static void cpr_regulator_switch_adj_cpus(struct cpr_regulator *cpr_vreg) { cpr_vreg->last_volt = cpr_vreg->adj_cpus_last_volt [cpr_vreg->online_cpus]; cpr_vreg->save_ctl = cpr_vreg->adj_cpus_save_ctl[cpr_vreg->online_cpus]; cpr_vreg->save_irq = cpr_vreg->adj_cpus_save_irq[cpr_vreg->online_cpus]; if (cpr_vreg->adj_cpus_quot_adjust) cpr_vreg->quot_adjust = cpr_vreg->adj_cpus_quot_adjust [cpr_vreg->online_cpus]; if (cpr_vreg->adj_cpus_open_loop_volt) cpr_vreg->open_loop_volt = cpr_vreg->adj_cpus_open_loop_volt [cpr_vreg->online_cpus]; if (cpr_vreg->adj_cpus_open_loop_volt_as_ceiling) cpr_vreg->ceiling_volt = cpr_vreg->open_loop_volt; } static void cpr_regulator_set_online_cpus(struct cpr_regulator *cpr_vreg) { int i, j; cpr_vreg->online_cpus = 0; get_online_cpus(); for_each_online_cpu(i) for (j = 0; j < cpr_vreg->num_adj_cpus; j++) if (i == cpr_vreg->adj_cpus[j]) cpr_vreg->online_cpus++; put_online_cpus(); } static int cpr_regulator_cpu_callback(struct notifier_block *nb, unsigned long action, void *data) { struct cpr_regulator *cpr_vreg = container_of(nb, struct cpr_regulator, cpu_notifier); int prev_online_cpus = cpr_vreg->online_cpus; int cpu = (long)data; int rc, i; action &= ~CPU_TASKS_FROZEN; if (action != CPU_UP_PREPARE && action != CPU_UP_CANCELED && action != CPU_DEAD) return NOTIFY_OK; if (cpr_vreg->skip_voltage_change_during_suspend) { mutex_lock(&cpr_vreg->cpr_mutex); if (cpr_vreg->is_cpr_suspended) { /* Do nothing during system suspend/resume */ mutex_unlock(&cpr_vreg->cpr_mutex); return NOTIFY_OK; } mutex_unlock(&cpr_vreg->cpr_mutex); } cpr_regulator_set_online_cpus(cpr_vreg); if (action == CPU_UP_PREPARE) for (i = 0; i < cpr_vreg->num_adj_cpus; i++) if (cpu == cpr_vreg->adj_cpus[i]) { cpr_vreg->online_cpus++; break; } if (cpr_vreg->online_cpus == prev_online_cpus) return NOTIFY_OK; cpr_debug(cpr_vreg, "adjusting quotient for %d cpus\n", cpr_vreg->online_cpus); cpr_regulator_switch_adj_cpus(cpr_vreg); if (cpr_vreg->corner) { rc = cpr_regulator_set_voltage(cpr_vreg->rdev, cpr_vreg->corner, true); if (rc) cpr_err(cpr_vreg, "could not update quotient, rc=%d\n", rc); } return NOTIFY_OK; } static int cpr_parse_adj_cpus_init_voltage(struct cpr_regulator *cpr_vreg, struct device *dev) { int rc, i, j, k, tuple_count, tuple_match, len, offset; int *temp; if (!of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment", NULL)) 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 voltage 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; } len = (cpr_vreg->num_adj_cpus + 1) * tuple_count * cpr_vreg->num_corners; temp = kzalloc(sizeof(int) * len, GFP_KERNEL); if (!temp) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } cpr_vreg->adj_cpus_open_loop_volt = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_open_loop_volt) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } cpr_vreg->adj_cpus_open_loop_volt[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_open_loop_volt[0]) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } for (i = 1; i <= cpr_vreg->num_adj_cpus; i++) cpr_vreg->adj_cpus_open_loop_volt[i] = cpr_vreg->adj_cpus_open_loop_volt[0] + i * (cpr_vreg->num_corners + 1); rc = of_property_read_u32_array(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment", temp, len); if (rc) { cpr_err(cpr_vreg, "failed to read qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment, rc=%d\n", rc); goto done; } cpr_debug(cpr_vreg, "Open loop voltage based on number of online CPUs:\n"); offset = tuple_match * cpr_vreg->num_corners * (cpr_vreg->num_adj_cpus + 1); for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) { for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) { k = j - 1 + offset; cpr_vreg->adj_cpus_open_loop_volt[i][j] = cpr_vreg->open_loop_volt[j] + temp[k]; cpr_vreg->adj_cpus_open_loop_volt[i][j] = DIV_ROUND_UP(cpr_vreg-> adj_cpus_open_loop_volt[i][j], cpr_vreg->step_volt) * cpr_vreg->step_volt; if (cpr_vreg->adj_cpus_open_loop_volt[i][j] > cpr_vreg->ceiling_volt[j]) cpr_vreg->adj_cpus_open_loop_volt[i][j] = cpr_vreg->ceiling_volt[j]; if (cpr_vreg->adj_cpus_open_loop_volt[i][j] < cpr_vreg->floor_volt[j]) cpr_vreg->adj_cpus_open_loop_volt[i][j] = cpr_vreg->floor_volt[j]; cpr_debug(cpr_vreg, "cpus=%d, corner=%d, volt=%d\n", i, j, cpr_vreg->adj_cpus_open_loop_volt[i][j]); } offset += cpr_vreg->num_corners; } cpr_vreg->adj_cpus_open_loop_volt_as_ceiling = of_property_read_bool(dev->of_node, "qcom,cpr-online-cpu-init-voltage-as-ceiling"); done: kfree(temp); return rc; } static int cpr_parse_adj_cpus_target_quot(struct cpr_regulator *cpr_vreg, struct device *dev) { int rc, i, j, k, tuple_count, tuple_match, len, offset; int *temp; if (!of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment", NULL)) 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; } len = (cpr_vreg->num_adj_cpus + 1) * tuple_count * cpr_vreg->num_corners; temp = kzalloc(sizeof(int) * len, GFP_KERNEL); if (!temp) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } cpr_vreg->adj_cpus_quot_adjust = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_quot_adjust) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } cpr_vreg->adj_cpus_quot_adjust[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_quot_adjust[0]) { cpr_err(cpr_vreg, "Could not allocate memory\n"); rc = -ENOMEM; goto done; } for (i = 1; i <= cpr_vreg->num_adj_cpus; i++) cpr_vreg->adj_cpus_quot_adjust[i] = cpr_vreg->adj_cpus_quot_adjust[0] + i * (cpr_vreg->num_corners + 1); rc = of_property_read_u32_array(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment", temp, len); if (rc) { cpr_err(cpr_vreg, "failed to read qcom,cpr-online-cpu-virtual-corner-quotient-adjustment, rc=%d\n", rc); goto done; } cpr_debug(cpr_vreg, "Target quotients based on number of online CPUs:\n"); offset = tuple_match * cpr_vreg->num_corners * (cpr_vreg->num_adj_cpus + 1); for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) { for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) { k = j - 1 + offset; cpr_vreg->adj_cpus_quot_adjust[i][j] = cpr_vreg->quot_adjust[j] - temp[k]; cpr_debug(cpr_vreg, "cpus=%d, corner=%d, quot=%d\n", i, j, cpr_vreg->cpr_fuse_target_quot[ cpr_vreg->corner_map[j]] - cpr_vreg->adj_cpus_quot_adjust[i][j]); } offset += cpr_vreg->num_corners; } done: kfree(temp); return rc; } static int cpr_init_per_cpu_adjustments(struct cpr_regulator *cpr_vreg, struct device *dev) { struct device_node *cpu_node; int rc, i, j, cpu; if (!of_find_property(dev->of_node, "qcom,cpr-cpus", &cpr_vreg->num_adj_cpus)) { /* No per-online CPU adjustment needed */ return 0; } cpr_vreg->num_adj_cpus /= sizeof(u32); if (!of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment", NULL) && !of_find_property(dev->of_node, "qcom,cpr-online-cpu-virtual-corner-quotient-adjustment", NULL)) { cpr_err(cpr_vreg, "qcom,cpr-online-cpu-virtual-corner-init-voltage-adjustment and/or qcom,cpr-online-cpu-virtual-corner-quotient-adjustment must be specified\n"); return -EINVAL; } cpr_vreg->adj_cpus = devm_kzalloc(dev, sizeof(int) * cpr_vreg->num_adj_cpus, GFP_KERNEL); if (!cpr_vreg->adj_cpus) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } for (i = 0; i < cpr_vreg->num_adj_cpus; i++) { cpu_node = of_parse_phandle(dev->of_node, "qcom,cpr-cpus", i); if (!cpu_node) { cpr_err(cpr_vreg, "could not find CPU node %d\n", i); return -EINVAL; } cpr_vreg->adj_cpus[i] = -1; for_each_possible_cpu(cpu) { if (of_get_cpu_node(cpu, NULL) == cpu_node) { cpr_vreg->adj_cpus[i] = cpu; break; } } of_node_put(cpu_node); } rc = cpr_parse_adj_cpus_init_voltage(cpr_vreg, dev); if (rc) { cpr_err(cpr_vreg, "cpr_parse_adj_cpus_init_voltage failed: rc =%d\n", rc); return rc; } rc = cpr_parse_adj_cpus_target_quot(cpr_vreg, dev); if (rc) { cpr_err(cpr_vreg, "cpr_parse_adj_cpus_target_quot failed: rc =%d\n", rc); return rc; } cpr_vreg->adj_cpus_last_volt = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_ctl = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_irq = devm_kzalloc(dev, sizeof(int *) * (cpr_vreg->num_adj_cpus + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_last_volt || !cpr_vreg->adj_cpus_save_ctl || !cpr_vreg->adj_cpus_save_irq) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } cpr_vreg->adj_cpus_last_volt[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_ctl[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); cpr_vreg->adj_cpus_save_irq[0] = devm_kzalloc(dev, sizeof(int) * (cpr_vreg->num_adj_cpus + 1) * (cpr_vreg->num_corners + 1), GFP_KERNEL); if (!cpr_vreg->adj_cpus_last_volt[0] || !cpr_vreg->adj_cpus_save_ctl[0] || !cpr_vreg->adj_cpus_save_irq[0]) { cpr_err(cpr_vreg, "Could not allocate memory\n"); return -ENOMEM; } for (i = 1; i <= cpr_vreg->num_adj_cpus; i++) { j = i * (cpr_vreg->num_corners + 1); cpr_vreg->adj_cpus_last_volt[i] = cpr_vreg->adj_cpus_last_volt[0] + j; cpr_vreg->adj_cpus_save_ctl[i] = cpr_vreg->adj_cpus_save_ctl[0] + j; cpr_vreg->adj_cpus_save_irq[i] = cpr_vreg->adj_cpus_save_irq[0] + j; } for (i = 0; i <= cpr_vreg->num_adj_cpus; i++) { for (j = CPR_CORNER_MIN; j <= cpr_vreg->num_corners; j++) { cpr_vreg->adj_cpus_save_ctl[i][j] = cpr_vreg->save_ctl[j]; cpr_vreg->adj_cpus_save_irq[i][j] = cpr_vreg->save_irq[j]; cpr_vreg->adj_cpus_last_volt[i][j] = cpr_vreg->adj_cpus_open_loop_volt ? cpr_vreg->adj_cpus_open_loop_volt[i][j] : cpr_vreg->open_loop_volt[j]; } } cpr_regulator_set_online_cpus(cpr_vreg); cpr_debug(cpr_vreg, "%d cpus online\n", cpr_vreg->online_cpus); devm_kfree(dev, cpr_vreg->last_volt); devm_kfree(dev, cpr_vreg->save_ctl); devm_kfree(dev, cpr_vreg->save_irq); if (cpr_vreg->adj_cpus_quot_adjust) devm_kfree(dev, cpr_vreg->quot_adjust); if (cpr_vreg->adj_cpus_open_loop_volt) devm_kfree(dev, cpr_vreg->open_loop_volt); if (cpr_vreg->adj_cpus_open_loop_volt_as_ceiling) devm_kfree(dev, cpr_vreg->ceiling_volt); cpr_regulator_switch_adj_cpus(cpr_vreg); cpr_vreg->skip_voltage_change_during_suspend = of_property_read_bool(dev->of_node, "qcom,cpr-skip-voltage-change-during-suspend"); cpr_vreg->cpu_notifier.notifier_call = cpr_regulator_cpu_callback; register_hotcpu_notifier(&cpr_vreg->cpu_notifier); return rc; } static int cpr_init_cpr(struct platform_device *pdev, struct cpr_regulator *cpr_vreg) { Loading Loading @@ -4285,6 +4735,14 @@ static int cpr_regulator_probe(struct platform_device *pdev) goto err_out; } /* Load per-online CPU adjustment data */ rc = cpr_init_per_cpu_adjustments(cpr_vreg, &pdev->dev); if (rc) { cpr_err(cpr_vreg, "cpr_init_per_cpu_adjustments failed: rc=%d\n", rc); goto err_out; } cpr_efuse_free(cpr_vreg); /* Loading Loading @@ -4343,6 +4801,9 @@ static int cpr_regulator_remove(struct platform_device *pdev) list_del(&cpr_vreg->list); mutex_unlock(&cpr_regulator_list_mutex); if (cpr_vreg->adj_cpus) unregister_hotcpu_notifier(&cpr_vreg->cpu_notifier); cpr_apc_exit(cpr_vreg); cpr_debugfs_remove(cpr_vreg); regulator_unregister(cpr_vreg->rdev); Loading