/* * This API is to be called during init to put the various voltage * domains to the voltage as per the opp table. Typically we boot up * at the nominal voltage. So this function finds out the rate of * the clock associated with the voltage domain, finds out the correct * opp entry and puts the voltage domain to the voltage specifies * in the opp entry */ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, struct device *dev) { struct voltagedomain *voltdm; struct clk *clk; struct opp *opp; unsigned long freq, bootup_volt; if (!vdd_name || !clk_name || !dev) { printk(KERN_ERR "%s: Invalid parameters!\n", __func__); goto exit; } voltdm = omap_voltage_domain_lookup(vdd_name); if (IS_ERR(voltdm)) { printk(KERN_ERR "%s: Unable to get vdd pointer for vdd_%s\n", __func__, vdd_name); goto exit; } clk = clk_get(NULL, clk_name); if (IS_ERR(clk)) { printk(KERN_ERR "%s: unable to get clk %s\n", __func__, clk_name); goto exit; } freq = clk->rate; clk_put(clk); opp = opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) { printk(KERN_ERR "%s: unable to find boot up OPP for vdd_%s\n", __func__, vdd_name); goto exit; } bootup_volt = opp_get_voltage(opp); if (!bootup_volt) { printk(KERN_ERR "%s: unable to find voltage corresponding" "to the bootup OPP for vdd_%s\n", __func__, vdd_name); goto exit; } omap_voltage_scale_vdd(voltdm, bootup_volt); return 0; exit: printk(KERN_ERR "%s: Unable to put vdd_%s to its init voltage\n\n", __func__, vdd_name); return -EINVAL; }
/** * omap_voltage_reset() - Resets the voltage of a particular voltage domain * to that of the current OPP. * @voltdm: pointer to the VDD whose voltage is to be reset. * * This API finds out the correct voltage the voltage domain is supposed * to be at and resets the voltage to that level. Should be used expecially * while disabling any voltage compensation modules. */ void omap_voltage_reset(struct voltagedomain *voltdm) { unsigned long target_uvdc; if (!voltdm || IS_ERR(voltdm)) { pr_warning("%s: VDD specified does not exist!\n", __func__); return; } target_uvdc = omap_voltage_get_nom_volt(voltdm); if (!target_uvdc) { pr_err("%s: unable to find current voltage for vdd_%s\n", __func__, voltdm->name); return; } omap_voltage_scale_vdd(voltdm, target_uvdc); }
/** * 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); }