Example #1
0
/**
 * 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;
}