/** * 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; }
/** * 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); }
/** * 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); }