/** * sr_class1p5_voltdm_recal() - Helper routine to reset calibration. * @voltdm: Voltage domain to reset calibration for * @user: unused * * NOTE: Appropriate locks must be held by calling path to ensure mutual * exclusivity */ static int sr_class1p5_voltdm_recal(struct voltagedomain *voltdm, void *user) { struct omap_volt_data *vdata; /* * we need to go no further if sr is not enabled for this domain or * voltage processor is not present for this voltage domain * (example vdd_wakeup). Class 1.5 requires Voltage processor * to function. */ if (!voltdm->vp || !is_sr_enabled(voltdm)) return 0; vdata = omap_voltage_get_curr_vdata(voltdm); if (!vdata) { pr_err("%s: unable to find current voltage for vdd_%s\n", __func__, voltdm->name); return -ENXIO; } omap_sr_disable(voltdm); omap_voltage_calib_reset(voltdm); voltdm_reset(voltdm); omap_sr_enable(voltdm, vdata); pr_info("%s: %s: calibration reset\n", __func__, voltdm->name); return 0; }
static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) { sr_disable_errgen(sr->voltdm); omap_vp_disable(sr->voltdm); sr_disable(sr->voltdm); if (is_volt_reset) voltdm_reset(sr->voltdm); return 0; }
static int sr_class3_disable(struct voltagedomain *voltdm, struct omap_volt_data *vdata, int is_volt_reset) { sr_disable_errgen(voltdm); omap_vp_disable(voltdm); sr_disable(voltdm); if (is_volt_reset) voltdm_reset(voltdm); return 0; }
/** * sr_class1p5_disable() - disable 1.5 mode for a voltage domain * @voltdm: voltage domain for the sr which needs disabling * @volt_data: voltage data for current OPP to disable * @voltdm_cdata: voltage domain specific private class data * @is_volt_reset: reset the voltage? * * This function has the necessity to either disable SR alone OR disable SR * and reset voltage to appropriate level depending on is_volt_reset parameter. * * Disabling SR H/w loop: * If calibration is complete or not yet triggered, we have no need to disable * SR h/w loop. * If calibration is complete, we would have already disabled SR AVS at the end * of calibration and h/w loop is inactive when this is called. * If it was never calibrated before, H/w loop was never enabled in the first * place to disable. * If calibration is underway, we cancel the work queue and disable SR. This is * to provide priority to DVFS transition as such transitions cannot wait * without impacting user experience. * * Resetting voltage: * If we have already completed calibration, then resetting to nominal voltage * is not required as we are functioning at safe voltage levels. * If we have not started calibration, we would like to reset to nominal voltage * If calibration is underway and we are attempting to reset voltage as * well, it implies we are in idle/suspend paths where we give priority * to calibration activity and a retry will be attempted. * * NOTE: Appropriate locks must be held by calling path to ensure mutual * exclusivity */ static int sr_class1p5_disable(struct voltagedomain *voltdm, void *voltdm_cdata, struct omap_volt_data *volt_data, int is_volt_reset) { struct sr_class1p5_work_data *work_data; if (IS_ERR_OR_NULL(voltdm) || IS_ERR_OR_NULL(volt_data)) { pr_err("%s: bad parameters!\n", __func__); return -EINVAL; } work_data = (struct sr_class1p5_work_data *)voltdm_cdata; if (IS_ERR_OR_NULL(work_data)) { pr_err("%s: bad work data??\n", __func__); return -EINVAL; } if (work_data->work_active) { /* if volt reset and work is active, we dont allow this */ if (is_volt_reset) return -EBUSY; /* flag work is dead and remove the old work */ work_data->work_active = false; cancel_delayed_work_sync(&work_data->work); sr_notifier_control(voltdm, false); sr_disable_errgen(voltdm); omap_vp_disable(voltdm); sr_disable(voltdm); /* Cancelled SR, so no more need to keep request */ pm_qos_update_request(&work_data->qos, PM_QOS_DEFAULT_VALUE); } /* If already calibrated, don't need to reset voltage */ if (volt_data->volt_calibrated) return 0; if (is_volt_reset) voltdm_reset(voltdm); return 0; }
/** * sr_classp5_deinit() - class p5 deinitialization * @sr: SR to deinit * @class_priv_data: class private data for deinitialiation (unused) * * currently only resets the calibrated voltage forcing DVFS voltages * to be used in the system * * NOTE: Appropriate locks must be held by calling path to ensure mutual * exclusivity */ static int sr_classp5_deinit(struct omap_sr *sr, void *class_priv_data) { struct voltagedomain *voltdm = NULL; void **voltdm_cdata = NULL; struct sr_classp5_calib_data *work_data = NULL; if (IS_ERR_OR_NULL(sr) || IS_ERR_OR_NULL(sr->voltdm)) { pr_err("%s: bad parameters!\n", __func__); return -EINVAL; } voltdm = sr->voltdm; voltdm_cdata = &sr->voltdm_cdata; if (IS_ERR_OR_NULL(*voltdm_cdata)) { pr_err("%s: ooopps.. class not initialized for %s! bug??\n", __func__, voltdm->name); return -EINVAL; } work_data = (struct sr_classp5_calib_data *)*voltdm_cdata; /* * we dont have SR periodic calib anymore.. so reset calibs * we are already protected by appropriate locks, so no lock needed * here. */ if (work_data->work_active) sr_classp5_disable(sr, 0); /* Ensure worker canceled. */ cancel_delayed_work_sync(&work_data->work); omap_voltage_calib_reset(voltdm); voltdm_reset(voltdm); pm_qos_remove_request(&work_data->qos); kfree(work_data); *voltdm_cdata = NULL; return 0; }
/** * sr_classp5_disable() - disable for a voltage domain * @sr: SmartReflex module, which need to be disabled * @is_volt_reset: reset the voltage? * * This function has the necessity to either disable SR alone OR disable SR * and reset voltage to appropriate level depending on is_volt_reset parameter. * * NOTE: Appropriate locks must be held by calling path to ensure mutual * exclusivity */ static int sr_classp5_disable(struct omap_sr *sr, int is_volt_reset) { struct voltagedomain *voltdm = NULL; struct omap_volt_data *volt_data = NULL; struct sr_classp5_calib_data *work_data = NULL; if (IS_ERR_OR_NULL(sr) || IS_ERR_OR_NULL(sr->voltdm)) { pr_err("%s: bad parameters!\n", __func__); return -EINVAL; } work_data = (struct sr_classp5_calib_data *)sr->voltdm_cdata; if (IS_ERR_OR_NULL(work_data)) { pr_err("%s: bad work data %s\n", __func__, sr->name); return -EINVAL; } if (is_idle_task(current)) { /* * we should not have seen this path if calibration !complete * pm_qos constraint is already released after voltage * calibration work is finished */ WARN_ON(work_data->work_active); return 0; } /* Rest is regular DVFS path */ voltdm = sr->voltdm; volt_data = omap_voltage_get_curr_vdata(voltdm); if (IS_ERR_OR_NULL(volt_data)) { pr_warning("%s: Voltage data is NULL. Cannot disable %s\n", __func__, sr->name); return -ENODATA; } /* need to do rest of code ONLY if required */ if (volt_data->volt_calibrated && !work_data->work_active) { /* * We are going OFF - disable clocks manually to allow OFF-mode. */ if (sr->suspended) sr->ops->put(sr); return 0; } if (work_data->work_active) { /* flag work is dead and remove the old work */ work_data->work_active = false; cancel_delayed_work_sync(&work_data->work); sr_notifier_control(sr, false); } sr_classp5_stop_hw_loop(sr); if (is_volt_reset) voltdm_reset(sr->voltdm); /* Canceled SR, so no more need to keep request */ pm_qos_update_request(&work_data->qos, PM_QOS_DEFAULT_VALUE); /* * We are going OFF - disable clocks manually to allow OFF-mode. */ if (sr->suspended) { /* !!! Should never ever be here - no guarantee to recover !!!*/ WARN(true, "Trying to go OFF with invalid AVS state\n"); sr->ops->put(sr); } return 0; }