Exemplo n.º 1
0
/**
 * sr_class1p5_notify() - isr notifier for status events
 * @voltdm:	voltage domain for which we were triggered
 * @voltdm_cdata: voltage domain specific private class data
 * @status:	notifier event to use
 *
 * This basically collects data for the work to use.
 */
static int sr_class1p5_notify(struct voltagedomain *voltdm,
			      void *voltdm_cdata,
			      u32 status)
{
	struct sr_class1p5_work_data *work_data;
	int idx = 0;

	if (IS_ERR_OR_NULL(voltdm)) {
		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:%s no work data!!\n", __func__, voltdm->name);
		return -EINVAL;
	}

	/* Wait for transdone so that we know the voltage to read */
	do {
		if (omap_vp_is_transdone(voltdm))
			break;
		idx++;
		/* get some constant delay */
		udelay(1);
	} while (idx < MAX_CHECK_VPTRANS_US);

	/*
	 * NOTE:
	 * If we timeout, we still read the data,
	 * if we are oscillating+irq latencies are too high, we could
	 * have scenarios where we miss transdone event. since
	 * we waited long enough, it is still safe to read the voltage
	 * as we would have waited long enough - Dont warn for this.
	 */
	idx = (work_data->num_osc_samples) % SR1P5_STABLE_SAMPLES;
	work_data->u_volt_samples[idx] = omap_vp_get_curr_volt(voltdm);
	work_data->num_osc_samples++;

	omap_vp_clear_transdone(voltdm);


	return 0;
}
Exemplo n.º 2
0
/**
 * sr_classp5_calib_work() - work which actually does the calibration
 * @work: pointer to the work
 *
 * calibration routine uses the following logic:
 * on the first trigger, we start the isr to collect sr voltages
 * wait for stabilization delay (reschdule self instead of sleeping)
 * after the delay, see if we collected any isr events
 * if none, we have calibrated voltage.
 * if there are any, we retry untill we giveup.
 * on retry timeout, select a voltage to use as safe voltage.
 */
static void sr_classp5_calib_work(struct work_struct *work)
{
	struct sr_classp5_calib_data *work_data =
	    container_of(work, struct sr_classp5_calib_data, work.work);
	unsigned long u_volt_safe = 0, u_volt_current = 0, u_volt_margin = 0;
	struct omap_volt_data *volt_data;
	struct voltagedomain *voltdm;
	struct omap_sr *sr;
	struct omap_voltdm_pmic *pmic;
	int idx = 0;

	if (!work) {
		pr_err("%s: ooops.. null work_data?\n", __func__);
		return;
	}

	/*
	 * Handle the case where we might have just been scheduled AND
	 * disable was called.
	 */
	if (!mutex_trylock(&omap_dvfs_lock)) {
		schedule_delayed_work(&work_data->work,
				      msecs_to_jiffies(SRP5_SAMPLING_DELAY_MS *
						       SRP5_STABLE_SAMPLES));
		return;
	}

	sr = work_data->sr;
	voltdm = sr->voltdm;
	/*
	 * In the unlikely case that we did get through when unplanned,
	 * flag and return.
	 */
	if (unlikely(!work_data->work_active)) {
		pr_err("%s:%s unplanned work invocation!\n", __func__,
		       sr->name);
		/* No expectation of calibration, remove qos req */
		pm_qos_update_request(&work_data->qos, PM_QOS_DEFAULT_VALUE);
		mutex_unlock(&omap_dvfs_lock);
		return;
	}

	volt_data = work_data->vdata;

	work_data->num_calib_triggers++;
	/* if we are triggered first time, we need to start isr to sample */
	if (work_data->num_calib_triggers == 1) {
		/* We could be interrupted many times, so, only for debug */
		pr_debug("%s: %s: Calibration start: Voltage Nominal=%d\n",
			 __func__, sr->name, volt_data->volt_nominal);
		goto start_sampling;
	}

	/* Stop isr from interrupting our measurements :) */
	sr_notifier_control(sr, false);

	/*
	 * Quit sampling
	 * a) if we have oscillations
	 * b) if we have nominal voltage as the voltage
	 */
	if (work_data->num_calib_triggers == SRP5_MAX_TRIGGERS)
		goto stop_sampling;

	/* if there are no samples captured.. SR is silent, aka stability! */
	if (!work_data->num_osc_samples) {
		/* Did we interrupt too early? */
		u_volt_current = omap_vp_get_curr_volt(voltdm);
		/*
		 * If we waited enough amount of iterations, then we might be
		 * on a corner case where voltage adjusted = Vnom!
		 */
		if (work_data->num_calib_triggers < 2 &&
		    u_volt_current >= volt_data->volt_nominal)
			goto start_sampling;
		u_volt_safe = u_volt_current;
		goto done_calib;
	}

	/* we have potential oscillations/first sample */
start_sampling:
	work_data->num_osc_samples = 0;

	/* Clear transdone events so that we can go on. */
	do {
		if (!omap_vp_is_transdone(voltdm))
			break;
		idx++;
		/* get some constant delay */
		udelay(1);
		omap_vp_clear_transdone(voltdm);
	} while (idx < SRP5_MAX_CHECK_VPTRANS_US);
	if (idx >= SRP5_MAX_CHECK_VPTRANS_US)
		pr_warning("%s: timed out waiting for transdone clear!!\n",
			   __func__);

	/* Clear pending events */
	sr_notifier_control(sr, false);
	/* trigger sampling */
	sr_notifier_control(sr, true);
	schedule_delayed_work(&work_data->work,
			      msecs_to_jiffies(SRP5_SAMPLING_DELAY_MS *
					       SRP5_STABLE_SAMPLES));
	mutex_unlock(&omap_dvfs_lock);
	return;

stop_sampling:
	/*
	 * We are here for Oscillations due to two scenarios:
	 * a) SR is attempting to adjust voltage lower than VLIMITO
	 *    which VP will ignore, but SR will re-attempt
	 * b) actual oscillations
	 * NOTE: For debugging, enable debug to see the samples.
	 */
	pr_warning("%s: %s Stop sampling: Voltage Nominal=%d samples=%d\n",
		   __func__, sr->name,
		   volt_data->volt_nominal, work_data->num_osc_samples);

	/* pick up current voltage */
	u_volt_current = omap_vp_get_curr_volt(voltdm);

	/* Just in case we got more interrupts than our tiny buffer */
	if (work_data->num_osc_samples > SRP5_STABLE_SAMPLES)
		idx = SRP5_STABLE_SAMPLES;
	else
		idx = work_data->num_osc_samples;
	/* Index at 0 */
	idx -= 1;
	u_volt_safe = u_volt_current;
	/* Grab the max of the samples as the stable voltage */
	for (; idx >= 0; idx--) {
		pr_debug("%s: osc_v[%d]=%ld, safe_v=%ld\n", __func__, idx,
			 work_data->u_volt_samples[idx], u_volt_safe);
		if (work_data->u_volt_samples[idx] > u_volt_safe)
			u_volt_safe = work_data->u_volt_samples[idx];
	}

	/* Fall through to close up common stuff */
done_calib:
	sr_classp5_stop_hw_loop(sr);

	pmic = voltdm->pmic;

	/* Add Per OPP margin if needed */
	if (volt_data->volt_margin)
		u_volt_margin += sr_classp5_adjust_margin(pmic,
							 volt_data->volt_margin,
							 u_volt_current);
	u_volt_safe += u_volt_margin;

	/* just warn, dont clamp down on voltage */
	if (u_volt_margin && u_volt_safe > volt_data->volt_nominal) {
		pr_warning("%s: %s Vsafe %ld > Vnom %d. %ld[%d] margin on"
			   "vnom %d curr_v=%ld\n", __func__, sr->name,
			   u_volt_safe, volt_data->volt_nominal, u_volt_margin,
			   volt_data->volt_margin, volt_data->volt_nominal,
			   u_volt_current);
	}

	volt_data->volt_calibrated = u_volt_safe;
	/* Setup my dynamic voltage for the next calibration for this opp */
	volt_data->volt_dynamic_nominal = omap_get_dyn_nominal(volt_data);

	/*
	 * if the voltage we decided as safe is not the current voltage,
	 * switch
	 */
	if (volt_data->volt_calibrated != u_volt_current) {
		pr_debug("%s: %s reconfiguring to voltage %d\n",
			 __func__, sr->name, volt_data->volt_calibrated);
		voltdm_scale(voltdm, volt_data);
	}

	pr_info("%s: %s: Calibration complete: Voltage Nominal=%d "
		"Calib=%d Dyn=%d OPP_margin=%d total_margin=%ld\n",
		__func__, sr->name, volt_data->volt_nominal,
		volt_data->volt_calibrated, volt_data->volt_dynamic_nominal,
		volt_data->volt_margin, u_volt_margin);

	work_data->work_active = false;

	/* Calibration done, Remove qos req */
	pm_qos_update_request(&work_data->qos, PM_QOS_DEFAULT_VALUE);
	mutex_unlock(&omap_dvfs_lock);
}
Exemplo n.º 3
0
/**
 * sr_class1p5_calib_work() - work which actually does the calibration
 * @work: pointer to the work
 *
 * calibration routine uses the following logic:
 * on the first trigger, we start the isr to collect sr voltages
 * wait for stabilization delay (reschdule self instead of sleeping)
 * after the delay, see if we collected any isr events
 * if none, we have calibrated voltage.
 * if there are any, we retry untill we giveup.
 * on retry timeout, select a voltage to use as safe voltage.
 */
static void sr_class1p5_calib_work(struct work_struct *work)
{
	struct sr_class1p5_work_data *work_data =
	    container_of(work, struct sr_class1p5_work_data, work.work);
	unsigned long u_volt_safe = 0, u_volt_current = 0, u_volt_margin = 0;
	struct omap_volt_data *volt_data;
	struct voltagedomain *voltdm;
	int idx = 0;

	if (!work) {
		pr_err("%s: ooops.. null work_data?\n", __func__);
		return;
	}

	/*
	 * Handle the case where we might have just been scheduled AND
	 * 1.5 disable was called.
	 */
	if (!mutex_trylock(&omap_dvfs_lock)) {
		schedule_delayed_work(&work_data->work,
				      msecs_to_jiffies(SR1P5_SAMPLING_DELAY_MS *
						       SR1P5_STABLE_SAMPLES));
		return;
	}

	voltdm = work_data->voltdm;
	/*
	 * In the unlikely case that we did get through when unplanned,
	 * flag and return.
	 */
	if (unlikely(!work_data->work_active)) {
		pr_err("%s:%s unplanned work invocation!\n", __func__,
		       voltdm->name);
		mutex_unlock(&omap_dvfs_lock);
		return;
	}

	volt_data = work_data->vdata;

	work_data->num_calib_triggers++;
	/* if we are triggered first time, we need to start isr to sample */
	if (work_data->num_calib_triggers == 1) {
		/* We could be interrupted many times, so, only for debug */
		pr_debug("%s: %s: Calibration start: Voltage Nominal=%d\n",
			 __func__, voltdm->name, volt_data->volt_nominal);
		goto start_sampling;
	}

	/* Stop isr from interrupting our measurements :) */
	sr_notifier_control(voltdm, false);

	/*
	 * Quit sampling
	 * a) if we have oscillations
	 * b) if we have nominal voltage as the voltage
	 */
	if (work_data->num_calib_triggers == SR1P5_MAX_TRIGGERS)
		goto stop_sampling;

	/* if there are no samples captured.. SR is silent, aka stability! */
	if (!work_data->num_osc_samples) {
		/* Did we interrupt too early? */
		u_volt_current = omap_vp_get_curr_volt(voltdm);
		if (u_volt_current >= volt_data->volt_nominal)
			goto start_sampling;
		u_volt_safe = u_volt_current;
		goto done_calib;
	}

	/* we have potential oscillations/first sample */
start_sampling:
	work_data->num_osc_samples = 0;

	/* Clear transdone events so that we can go on. */
	do {
		if (!omap_vp_is_transdone(voltdm))
			break;
		idx++;
		/* get some constant delay */
		udelay(1);
		omap_vp_clear_transdone(voltdm);
	} while (idx < MAX_CHECK_VPTRANS_US);
	if (idx >= MAX_CHECK_VPTRANS_US)
		pr_warning("%s: timed out waiting for transdone clear!!\n",
			   __func__);

	/* Clear pending events */
	sr_notifier_control(voltdm, false);
	/* trigger sampling */
	sr_notifier_control(voltdm, true);
	schedule_delayed_work(&work_data->work,
			      msecs_to_jiffies(SR1P5_SAMPLING_DELAY_MS *
					       SR1P5_STABLE_SAMPLES));
	mutex_unlock(&omap_dvfs_lock);
	return;

stop_sampling:
	/*
	 * We are here for Oscillations due to two scenarios:
	 * a) SR is attempting to adjust voltage lower than VLIMITO
	 *    which VP will ignore, but SR will re-attempt
	 * b) actual oscillations
	 * NOTE: For debugging, enable debug to see the samples.
	 */
	pr_warning("%s: %s Stop sampling: Voltage Nominal=%d samples=%d\n",
		   __func__, work_data->voltdm->name,
		   volt_data->volt_nominal, work_data->num_osc_samples);

	/* pick up current voltage */
	u_volt_current = omap_vp_get_curr_volt(voltdm);

	/* Just in case we got more interrupts than our tiny buffer */
	if (work_data->num_osc_samples > SR1P5_STABLE_SAMPLES)
		idx = SR1P5_STABLE_SAMPLES;
	else
		idx = work_data->num_osc_samples;
	/* Index at 0 */
	idx -= 1;
	u_volt_safe = u_volt_current;
	/* Grab the max of the samples as the stable voltage */
	for (; idx >= 0; idx--) {
		pr_debug("%s: osc_v[%d]=%ld, safe_v=%ld\n", __func__, idx,
			work_data->u_volt_samples[idx], u_volt_safe);
		if (work_data->u_volt_samples[idx] > u_volt_safe)
			u_volt_safe = work_data->u_volt_samples[idx];
	}

	/* Fall through to close up common stuff */
done_calib:
	sr_disable_errgen(voltdm);
	omap_vp_disable(voltdm);
	sr_disable(voltdm);

	/* Add margin if needed */
	if (volt_data->volt_margin) {
		struct omap_voltdm_pmic *pmic = voltdm->pmic;
		/* Convert to rounded to PMIC step level if available */
		if (pmic && pmic->vsel_to_uv && pmic->uv_to_vsel) {
			/*
			 * To ensure conversion works:
			 * use a proper base voltage - we use the current volt
			 * then convert it with pmic routine to vsel and back
			 * to voltage, and finally remove the base voltage
			 */
			u_volt_margin = u_volt_current + volt_data->volt_margin;
			u_volt_margin = pmic->uv_to_vsel(u_volt_margin);
			u_volt_margin = pmic->vsel_to_uv(u_volt_margin);
			u_volt_margin -= u_volt_current;
		} else {
			u_volt_margin = volt_data->volt_margin;
		}

		u_volt_safe += u_volt_margin;
	}

	if (u_volt_safe > volt_data->volt_nominal) {
		pr_warning("%s: %s Vsafe %ld > Vnom %d. %ld[%d] margin on"
			"vnom %d curr_v=%ld\n", __func__, voltdm->name,
			u_volt_safe, volt_data->volt_nominal, u_volt_margin,
			volt_data->volt_margin, volt_data->volt_nominal,
			u_volt_current);
	}

	volt_data->volt_calibrated = u_volt_safe;
	/* Setup my dynamic voltage for the next calibration for this opp */
	volt_data->volt_dynamic_nominal = omap_get_dyn_nominal(volt_data);

	/*
	 * if the voltage we decided as safe is not the current voltage,
	 * switch
	 */
	if (volt_data->volt_calibrated != u_volt_current) {
		pr_debug("%s: %s reconfiguring to voltage %d\n",
			 __func__, voltdm->name, volt_data->volt_calibrated);
		voltdm_scale(voltdm, volt_data);
	}

	pr_info("%s: %s: Calibration complete: Voltage:Nominal=%d,"
		"Calib=%d,margin=%d\n",
		 __func__, voltdm->name, volt_data->volt_nominal,
		 volt_data->volt_calibrated, volt_data->volt_margin);
	/*
	 * TODO: Setup my wakeup voltage to allow immediate going to OFF and
	 * on - Pending twl and voltage layer cleanups.
	 * This is necessary, as this is not done as part of regular
	 * Dvfs flow.
	 * vc_setup_on_voltage(voltdm, volt_data->volt_calibrated);
	 */
	work_data->work_active = false;
	mutex_unlock(&omap_dvfs_lock);
}
/**
 * do_calibrate() - work which actually does the calibration
 * @work: pointer to the work
 *
 * calibration routine uses the following logic:
 * on the first trigger, we start the isr to collect sr voltages
 * wait for stabilization delay (reschdule self instead of sleeping)
 * after the delay, see if we collected any isr events
 * if none, we have calibrated voltage.
 * if there are any, we retry untill we giveup.
 * on retry timeout, select a voltage to use as safe voltage.
 */
static void do_calibrate(struct work_struct *work)
{
	struct sr_class1p5_work_data *work_data =
	    container_of(work, struct sr_class1p5_work_data, work.work);
	unsigned long u_volt_safe = 0, u_volt_current = 0;
	struct omap_volt_data *volt_data;
	struct voltagedomain *voltdm;

	if (unlikely(!work_data)) {
		pr_err("%s: ooops.. null work_data?\n", __func__);
		return;
	}

	/*
	 * Handle the case where we might have just been scheduled AND
	 * 1.5 disable was called.
	 */
	if (omap_vscale_pause(work_data->voltdm, true)) {
		schedule_delayed_work(&work_data->work,
				      msecs_to_jiffies(SR1P5_SAMPLING_DELAY_MS *
						       SR1P5_STABLE_SAMPLES));
		return;
	}

	voltdm = work_data->voltdm;
	/*
	 * In the unlikely case that we did get through when unplanned,
	 * flag and return.
	 */
	if (unlikely(!work_data->work_active)) {
		pr_err("%s:%s unplanned work invocation!\n", __func__,
		       voltdm->name);
		omap_vscale_unpause(work_data->voltdm);
		return;
	}

	work_data->num_calib_triggers++;
	/* if we are triggered first time, we need to start isr to sample */
	if (work_data->num_calib_triggers == 1)
		goto start_sampling;

	/* Stop isr from interrupting our measurements :) */
	sr_notifier_control(voltdm, false);

	volt_data = work_data->vdata;

	/* if there are no samples captured.. SR is silent, aka stability! */
	if (!work_data->num_osc_samples) {
		u_volt_safe = omap_vp_get_curr_volt(voltdm);
		u_volt_current = u_volt_safe;
		goto done_calib;
	}
	if (work_data->num_calib_triggers == SR1P5_MAX_TRIGGERS) {
		pr_warning("%s: %s recalib timeout!\n", __func__,
			   work_data->voltdm->name);
		goto oscillating_calib;
	}

	/* we have potential oscillations/first sample */
start_sampling:
	work_data->num_osc_samples = 0;
	/* Clear pending events */
	sr_notifier_control(voltdm, false);
	/* Clear all transdones */
	while (omap_vp_is_transdone(voltdm))
		omap_vp_clear_transdone(voltdm);
	/* trigger sampling */
	sr_notifier_control(voltdm, true);
	schedule_delayed_work(&work_data->work,
			      msecs_to_jiffies(SR1P5_SAMPLING_DELAY_MS *
					       SR1P5_STABLE_SAMPLES));
	omap_vscale_unpause(work_data->voltdm);

	return;

oscillating_calib:
	/* Use the nominal voltage as the safe voltage */
	u_volt_safe = volt_data->volt_nominal;
	/* pick up current voltage to switch if needed */
	u_volt_current = omap_vp_get_curr_volt(voltdm);

	/* Fall through to close up common stuff */
done_calib:
	omap_vp_disable(voltdm);
	sr_disable(voltdm);

	volt_data->volt_calibrated = u_volt_safe;
	/* Setup my dynamic voltage for the next calibration for this opp */
	volt_data->volt_dynamic_nominal = omap_get_dyn_nominal(volt_data);

	/*
	 * if the voltage we decided as safe is not the current voltage,
	 * switch
	 */

	if (cpu_is_omap3630()) {
		pr_debug("%s:%s - sr opp margin %d\n", __func__, voltdm->name, volt_data->sr_oppmargin);

		volt_data->volt_calibrated += volt_data->sr_oppmargin;
		if (volt_data->volt_calibrated > volt_data->volt_nominal) {
			volt_data->volt_calibrated = volt_data->volt_nominal;
		}
	}

	if (volt_data->volt_calibrated != u_volt_current) {
		pr_debug("%s:%s reconfiguring to voltage %d\n",
			 __func__, voltdm->name, volt_data->volt_calibrated);
		omap_voltage_scale_vdd(voltdm, volt_data);
	}

	/*
	 * TODO: Setup my wakeup voltage to allow immediate going to OFF and
	 * on - Pending twl and voltage layer cleanups.
	 * This is necessary, as this is not done as part of regular
	 * Dvfs flow.
	 * vc_setup_on_voltage(voltdm, volt_data->volt_calibrated);
	 */
	work_data->work_active = false;
	omap_vscale_unpause(work_data->voltdm);

	/* Release c-state constraint */
	omap_pm_set_max_mpu_wakeup_lat(&work_data->qos_request, -1);
	pr_debug("%s - %s: release c-state\n", __func__, voltdm->name);
}