Hi, On Tue, Mar 1, 2011 at 3:47 PM, Menon, Nishanth <nm@xxxxxx> wrote: > On Tue, Mar 1, 2011 at 15:23, Gulati, Shweta <shweta.gulati@xxxxxx> wrote: >> >> Hi, >> >> On Sat, Feb 19, 2011 at 5:31 PM, Nishanth Menon <nm@xxxxxx> wrote: >> > Traditional SmartReflex AVS(Automatic Voltage Scaling) classes are: >> > * Class 0 - Product test calibration >> > Silicon is calibration at production floor and fused with voltages >> > for each OPP >> > * Class 1 - Boot time calibration >> > Silicon is calibrated once at boot time and voltages are stored for >> > the lifetime of operation. >> > * Class 2 - continuous s/w calibration >> > SR module notifies s/w for any change in the system which is desired >> > and the s/w makes runtime decisions in terms of setting the voltage, >> > this mechanism could be used in the system which does not have PMIC >> > capable of SR without using the voltage controller and voltage >> > processor blocks. >> > * Class 3 - continuous h/w calibration >> > SR module is switch on after reaching a voltage level and SR >> > continuously monitors the system and makes runtime adjustments without >> > s/w involvement. >> > >> > OMAP3430 has used SmartReflex AVS and with a a PMIC which understands the SR >> > protocol, Class 3 has been used. With OMAP3630 onwards, a new SmartReflex AVS >> > class of operation Class 1.5 was introduced. >> > * Class 1.5 - periodic s/w calibration >> > This uses the h/w calibration loop and at the end of calibration >> > stores the voltages to be used run time, periodic recalibration is >> > performed as well. >> > >> > The operational mode is describes as the following: >> > * SmartReflex AVS h/w calibration loop is essential to identify the optimal >> > voltage for a given OPP. >> > * Once this optimal voltage is detected, SmartReflex AVS loop is disabled in >> > class 1.5 mode of operation. >> > * Until there is a need for a recalibration, any further transition to an OPP >> > voltage which is calibrated can use the calibrated voltage and does not >> > require enabling the SR AVS h/w loop. >> > * On a periodic basis (recommendation being once approximately every 24 hours), >> > software is expected to perform a recalibration to find a new optimal >> > voltage which is compensated for device aging. >> > - For performing this recalibration, the start voltage does not need to >> > be the nominal voltage anymore. instead, the system can start with a >> > voltage which is 50mV higher than the previously calibrated voltage to >> > identify the new optimal voltage as the aging factor within a period of >> > 1 day is not going to be anywhere close to 50mV. >> > - This "new starting point" for recalibration is called a dynamic >> > nominal voltage for that voltage point. >> > In short, with the introduction of SmartReflex class 1.5, there are three new >> > voltages possible in a system's dvfs transition: >> > * Nominal Voltage - The maximum voltage needed for a worst possible device >> > in the worst possible conditions. This is the voltage we choose as >> > the starting point for the h/w loop to optimize for the first time >> > calibration on system bootup. >> > * Dynamic Nominal Voltage - Worst case voltage for a specific device in >> > considering the system aging on the worst process device. >> > * Calibrated Voltage - Best voltage for the current device at a given point >> > of time. >> > >> > In terms of the implementation, doing calibration involves waiting for the >> > smartreflex h/w loop to settle down, and doing this as part of the dvfs flow >> > itself is to increase the latency of dvfs transition when there is a need to >> > calibrate that opp. instead, the calibration is performed "out of path" using >> > a workqueue statemachine. The workqueue waits for the system stabilization, >> > then enables VP interrupts to monitor for system instability interms of voltage >> > oscillations that are reported back to the system as interrupts, in case of >> > prolonged system oscillations, nominal voltage is chosen as a safe voltage and >> > this event is logged in the system log for developer debug and fixing. >> > >> > For the recalibration, a common workqueue for all domains is started at the >> > start of the class initialization and it resets the calibrated voltages >> > on a periodic basis. For distros that may choose not to do the recommended >> > periodic recalibration, instead choose to perform boot time calibration, >> > kconfig configuration option is provided to do so. >> > >> > TODO: >> > a) Cpuidle and suspend paths are not integrated with SmartReflex driver at >> > this point. >> > b) Since the SR registers are accessed and controlled in parallel to DVFS >> > some sort of mechanism is necessary to be introduced along with OMAP >> > dvfs layer to ensure mutual exclusivity >> > c) Additional debug interfaces for vmin analysis for platform characterization >> > and addition of system margin needs to be introduced from smartreflex >> > perspective. >> > >> > This implementation also includes the following contributors: >> > Tony Lindgren for suggestion on using interrupt based mechanism instead of >> > polling to detect voltage oscillations. >> > Peter 'p2' De Schrijver for debating alternatives on recalibration mechanisms >> > Paul Walmsey, Eduardo Valentin, Ambresh K, Igor Dmitriev and quiet a few others >> > for patient review, testing and reporting of issues of a previous incarnation >> > of this implemenation. Last, but not the least, the TI H/w team in introducing >> > this new SR AVS class and patiently debating it's various facets. >> > >> > Cc: Ambresh K <ambresh@xxxxxx> >> > Cc: Eduardo Valentin <eduardo.valentin@xxxxxxxxx> >> > Cc: Igor Dmitriev <ext-dmitriev.igor@xxxxxxxxx> >> > Cc: Paul <paul@xxxxxxxxx> >> > Cc: Peter 'p2' De Schrijver <Peter.De-Schrijver@xxxxxxxxx> >> > Cc: Tony Lindgren <tony@xxxxxxxxxxx> >> > >> > Signed-off-by: Nishanth Menon <nm@xxxxxx> >> > --- >> > arch/arm/mach-omap2/Makefile | 1 + >> > arch/arm/mach-omap2/smartreflex-class1p5.c | 556 +++++++++++++++++++++++++ >> > arch/arm/mach-omap2/smartreflex-class3.c | 4 +- >> > arch/arm/mach-omap2/smartreflex.c | 34 ++- >> > arch/arm/mach-omap2/voltage.c | 69 +++ >> > arch/arm/plat-omap/Kconfig | 17 + >> > arch/arm/plat-omap/include/plat/smartreflex.h | 13 +- >> > arch/arm/plat-omap/include/plat/voltage.h | 23 +- >> > 8 files changed, 709 insertions(+), 8 deletions(-) >> > create mode 100644 arch/arm/mach-omap2/smartreflex-class1p5.c >> > >> > diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile >> > index 1c0c2b0..1a82e6d 100644 >> > --- a/arch/arm/mach-omap2/Makefile >> > +++ b/arch/arm/mach-omap2/Makefile >> > @@ -66,6 +66,7 @@ obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o voltage.o pm_bus.o >> > obj-$(CONFIG_PM_DEBUG) += pm-debug.o >> > obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o >> > obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o >> > +obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS1P5) += smartreflex-class1p5.o >> > >> > AFLAGS_sleep24xx.o :=-Wa,-march=armv6 >> > AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a >> > diff --git a/arch/arm/mach-omap2/smartreflex-class1p5.c b/arch/arm/mach-omap2/smartreflex-class1p5.c >> > new file mode 100644 >> > index 0000000..832f10b >> > --- /dev/null >> > +++ b/arch/arm/mach-omap2/smartreflex-class1p5.c >> > @@ -0,0 +1,556 @@ >> > +/* >> > + * Smart reflex Class 1.5 specific implementations >> > + * >> > + * Copyright (C) 2010-2011 Texas Instruments, Inc. >> > + * Nishanth Menon <nm@xxxxxx> >> > + * >> > + * Smart reflex class 1.5 is also called periodic SW Calibration >> > + * Some of the highlights are as follows: >> > + * – Host CPU triggers OPP calibration when transitioning to non calibrated >> > + * OPP >> > + * – SR-AVS + VP modules are used to perform calibration >> > + * – Once completed, the SmartReflex-AVS module can be disabled >> > + * – Enables savings based on process, supply DC accuracy and aging >> > + * >> > + * This program is free software; you can redistribute it and/or modify >> > + * it under the terms of the GNU General Public License version 2 as >> > + * published by the Free Software Foundation. >> > + */ >> > +#include <linux/kernel.h> >> > +#include <linux/delay.h> >> > +#include <linux/err.h> >> > +#include <linux/io.h> >> > +#include <linux/fs.h> >> > +#include <linux/string.h> >> > +#include <linux/uaccess.h> >> > +#include <linux/kobject.h> >> > +#include <linux/workqueue.h> >> > +#include <linux/opp.h> >> > + >> > +#include <plat/smartreflex.h> >> > +#include <plat/voltage.h> >> > + >> > +#define MAX_VDDS 3 >> > +#define SR1P5_SAMPLING_DELAY_MS 1 >> > +#define SR1P5_STABLE_SAMPLES 5 >> > +#define SR1P5_MAX_TRIGGERS 5 >> > + >> > +/* >> > + * we expect events in 10uS, if we dont get 2wice times as much, >> > + * we could kind of ignore this as a missed event. >> > + */ >> > +#define MAX_CHECK_VPTRANS_US 20 >> > + >> > +/** >> > + * struct sr_class1p5_work_data - data meant to be used by calibration work >> > + * @work: calibration work >> > + * @voltdm: voltage domain for which we are triggering >> > + * @vdata: voltage data we are calibrating >> > + * @num_calib_triggers: number of triggers from calibration loop >> > + * @num_osc_samples: number of samples collected by isr >> > + * @work_active: have we scheduled a work item? >> > + */ >> > +struct sr_class1p5_work_data { >> > + struct delayed_work work; >> > + struct voltagedomain *voltdm; >> > + struct omap_volt_data *vdata; >> > + u8 num_calib_triggers; >> > + u8 num_osc_samples; >> > + bool work_active; >> > +}; >> > + >> > +#if CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY >> > +/* recal_work: recalibration calibration work */ >> > +static struct delayed_work recal_work; >> > +#endif >> > + >> > +/** >> > + * struct sr_class1p5_data - private data for class 1p5 >> > + * @work_data: work item data per voltage domain >> > + */ >> > +struct sr_class1p5_data { >> > + struct sr_class1p5_work_data work_data[MAX_VDDS]; >> > +}; >> > + >> > +static void sr_class1p5_reset_calib(struct voltagedomain *voltdm, bool reset, >> > + bool recal); >> > + >> > +/* our instance of class 1p5 private data */ >> > +static struct sr_class1p5_data class_1p5_data; >> > + >> > +static struct sr_class1p5_work_data *get_sr1p5_work(struct voltagedomain >> > + *voltdm) >> > +{ >> > + int idx; >> > + for (idx = 0; idx < MAX_VDDS; idx++) { >> > + if (class_1p5_data.work_data[idx].voltdm && !strcmp >> > + (class_1p5_data.work_data[idx].voltdm->name, voltdm->name)) >> > + return &class_1p5_data.work_data[idx]; >> > + } >> > + return ERR_PTR(-ENODATA); >> > +} >> > + >> > +/** >> > + * sr_class1p5_notify() - isr notifier for status events >> > + * @voltdm: voltage domain for which we were triggered >> > + * @status: notifier event to use >> > + * >> > + * This basically collects data for the work to use. >> > + */ >> > +static int sr_class1p5_notify(struct voltagedomain *voltdm, u32 status) >> > +{ >> > + struct sr_class1p5_work_data *work_data; >> > + int idx = 0; >> > + work_data = get_sr1p5_work(voltdm); >> > + >> > + if (unlikely(!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); >> > + >> > + /* >> > + * 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 - still flag it.. >> > + */ >> > + if (idx >= MAX_CHECK_VPTRANS_US) >> > + pr_warning("%s: timed out waiting for transdone!!\n", __func__); >> > + >> > + omap_vp_clear_transdone(voltdm); >> > + >> > + idx = (work_data->num_osc_samples) % SR1P5_STABLE_SAMPLES; >> > + work_data->num_osc_samples++; >> > + >> > + return 0; >> > +} >> > + >> > +/** >> > + * 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; >> > + } >> > + >> > + /* >> > + * TODO:Handle the case where we might have just been scheduled AND >> > + * 1.5 disable was called. check and HOLD dvfs >> > + */ >> > + >> > + 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); >> > + /* TODO release the dvfs */ >> > + 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)); >> > + /* TODO: release dvfs */ >> > + 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 (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; >> > + /* TODO: release dvfs */ >> > +} >> > + >> > +#if CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY >> > +/** >> > + * do_recalibrate() - work which actually does the calibration >> > + * @work: pointer to the work >> > + * >> > + * on a periodic basis, we come and reset our calibration setup >> > + * so that a recalibration of the OPPs take place. This takes >> > + * care of aging factor in the system. >> > + */ >> > +static void do_recalibrate(struct work_struct *work) >> > +{ >> > + struct voltagedomain *voltdm; >> > + int idx; >> > + static struct sr_class1p5_work_data *work_data; >> > + >> > + for (idx = 0; idx < MAX_VDDS; idx++) { >> > + work_data = &class_1p5_data.work_data[idx]; >> > + voltdm = work_data->voltdm; >> > + if (voltdm) { >> > + /* if sr is not enabled, we check later */ >> > + if (!is_sr_enabled(voltdm)) >> > + continue; >> > + /* TODO: Pause the dvfs transitions */ >> > + /* if sr is not enabled, we check later */ >> > + >> > + /* Reset and force a recalibration for current opp */ >> > + sr_class1p5_reset_calib(voltdm, true, true); >> > + >> > + /* TODO: unpause DVFS transitions */ >> > + } >> > + } >> > + /* We come back again after time the usual delay */ >> > + schedule_delayed_work(&recal_work, >> > + msecs_to_jiffies(CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY)); >> > +} >> > +#endif /* CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY */ >> > + >> > +/** >> > + * sr_class1p5_enable() - class 1.5 mode of enable >> > + * @voltdm: voltage domain to enable SR for >> > + * @volt_data: voltdata to the voltage transition taking place >> > + * >> > + * when this gets called, we use the h/w loop to setup our voltages >> > + * to an calibrated voltage, detect any oscillations, recover from the same >> > + * and finally store the optimized voltage as the calibrated voltage in the >> > + * system >> > + */ >> > +static int sr_class1p5_enable(struct voltagedomain *voltdm, >> > + struct omap_volt_data *volt_data) >> > +{ >> > + int r; >> > + struct sr_class1p5_work_data *work_data; >> > + /* if already calibrated, nothing to do here.. */ >> > + if (volt_data->volt_calibrated) >> > + return 0; >> > + >> > + work_data = get_sr1p5_work(voltdm); >> > + if (unlikely(!work_data)) { >> > + pr_err("%s: aieeee.. bad work data??\n", __func__); >> > + return -EINVAL; >> > + } >> > + >> > + if (work_data->work_active) >> > + return 0; >> > + >> > + omap_vp_enable(voltdm); >> > + r = sr_enable(voltdm, volt_data); >> > + if (r) { >> > + pr_err("%s: sr[%s] failed\n", __func__, voltdm->name); >> > + omap_vp_disable(voltdm); >> > + return r; >> > + } >> > + work_data->vdata = volt_data; >> > + work_data->work_active = true; >> > + work_data->num_calib_triggers = 0; >> > + /* program the workqueue and leave it to calibrate offline.. */ >> > + schedule_delayed_work(&work_data->work, >> > + msecs_to_jiffies(SR1P5_SAMPLING_DELAY_MS * >> > + SR1P5_STABLE_SAMPLES)); >> > + >> > + return 0; >> > +} >> > + >> > +/** >> > + * sr_class1p5_disable() - disable for class 1p5 >> > + * @voltdm: voltage domain for the sr which needs disabling >> > + * @volt_data: voltagedata to disable >> > + * @is_volt_reset: reset the voltage? >> > + * >> > + * we dont do anything if the class 1p5 is being used. this is because we >> > + * already disable sr at the end of calibration and no h/w loop is actually >> > + * active when this is called. >> > + */ >> > +static int sr_class1p5_disable(struct voltagedomain *voltdm, >> > + struct omap_volt_data *volt_data, >> > + int is_volt_reset) >> > +{ >> > + struct sr_class1p5_work_data *work_data; >> > + >> > + work_data = get_sr1p5_work(voltdm); >> > + 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); >> > + omap_vp_disable(voltdm); >> > + sr_disable(voltdm); >> > + } >> > + >> > + /* if already calibrated, nothin special to do here.. */ >> > + if (volt_data->volt_calibrated) >> > + return 0; >> > + >> > + if (is_volt_reset) >> > + omap_voltage_reset(voltdm); >> > + return 0; >> > +} >> > + >> > +/** >> > + * sr_class1p5_configure() - configuration function >> > + * @voldm: configure for which voltage domain >> > + * >> > + * we dont do much here other than setup some registers for >> > + * the sr module involved. >> > + */ >> > +static int sr_class1p5_configure(struct voltagedomain *voltdm) >> > +{ >> > + return sr_configure_errgen(voltdm); >> > +} >> > + >> > +/** >> > + * sr_class1p5_reset_calib() - reset all calibrated voltages >> > + * @srid: srid to reset the calibration for >> > + * @reset: reset voltage before we recal? >> > + * @recal: should I recalibrate my current opp? >> > + * >> > + * if we call this, it means either periodic calibration trigger was >> > + * fired(either from sysfs or other mechanisms) or we have disabled class 1p5, >> > + * meaning we cant trust the calib voltages anymore, it is better to use >> > + * nominal in the system >> > + */ >> > +static void sr_class1p5_reset_calib(struct voltagedomain *voltdm, bool reset, >> > + bool recal) >> > +{ >> > + struct sr_class1p5_work_data *work_data; >> > + >> > + /* I dont need to go further if sr is not present */ >> > + if (!is_sr_enabled(voltdm)) >> > + return; >> > + >> > + work_data = get_sr1p5_work(voltdm); >> > + >> > + if (work_data->work_active) >> > + sr_class1p5_disable(voltdm, work_data->vdata, 0); >> > + >> > + omap_voltage_calib_reset(voltdm); >> > + >> > + /* >> > + * I should now reset the voltages to my nominal to be safe >> > + */ >> > + if (reset) >> > + omap_voltage_reset(voltdm); >> > + >> > + /* >> > + * I should fire a recalibration for current opp if needed >> > + * Note: i have just reset my calibrated voltages, and if >> > + * i call sr_enable equivalent, I will cause a recalibration >> > + * loop, even though the function is called sr_enable.. we >> > + * are in class 1.5 ;) >> > + */ >> > + if (reset && recal) >> > + sr_class1p5_enable(voltdm, work_data->vdata); >> > +} >> > + >> > +/** >> > + * sr_class1p5_cinit() - class 1p5 init >> > + * @voltdm: sr voltage domain >> > + * @class_priv_data: private data for the class >> > + * >> > + * we do class specific initialization like creating sysfs/debugfs entries >> > + * needed, spawning of a kthread if needed etc. >> > + */ >> > +static int sr_class1p5_cinit(struct voltagedomain *voltdm, >> > + void *class_priv_data) >> > +{ >> > + struct sr_class1p5_work_data *work_data; >> > + int idx; >> > + >> > + if (!class_priv_data) { >> > + pr_err("%s: bad param? no priv data!\n", __func__); >> > + return -EINVAL; >> > + } >> > + >> > + /* setup our work params */ >> > + work_data = get_sr1p5_work(voltdm); >> > + if (!IS_ERR_OR_NULL(work_data)) { >> > + pr_err("%s: ooopps.. class already initialized for %s! bug??\n", >> > + __func__, voltdm->name); >> > + return -EINVAL; >> > + } >> > + work_data = NULL; >> > + /* get the next spare work_data */ >> > + for (idx = 0; idx < MAX_VDDS; idx++) { >> > + if (!class_1p5_data.work_data[idx].voltdm) { >> > + work_data = &class_1p5_data.work_data[idx]; >> > + break; >> > + } >> > + } >> > + if (!work_data) { >> > + pr_err("%s: no more space for work data for domains!\n", >> > + __func__); >> > + return -ENOMEM; >> > + } >> > + work_data->voltdm = voltdm; >> > + INIT_DELAYED_WORK_DEFERRABLE(&work_data->work, do_calibrate); >> > + return 0; >> > +} >> > + >> > +/** >> > + * sr_class1p5_cdeinit() - class 1p5 deinitialization >> > + * @voltdm: voltage domain for which to do this. >> > + * @class_priv_data: class private data for deinitialiation >> > + * >> > + * currently only resets the calibrated voltage forcing dvfs voltages >> > + * to be used in the system >> > + */ >> > +static int sr_class1p5_cdeinit(struct voltagedomain *voltdm, >> > + void *class_priv_data) >> > +{ >> > + struct sr_class1p5_work_data *work_data; >> > + >> > + /* setup our work params */ >> > + work_data = get_sr1p5_work(voltdm); >> > + if (IS_ERR_OR_NULL(work_data)) { >> > + pr_err("%s: ooopps.. class not initialized for %s! bug??\n", >> > + __func__, voltdm->name); >> > + return -EINVAL; >> > + } >> > + >> > + /* >> > + * we dont have SR periodic calib anymore.. so reset calibs >> > + * we are already protected by sr debugfs lock, so no lock needed >> > + * here. >> > + */ >> > + sr_class1p5_reset_calib(voltdm, true, false); >> > + >> > + /* reset all data for this work data */ >> > + memset(work_data, 0, sizeof(*work_data)); >> > + >> > + return 0; >> > +} >> > + >> > +/* SR class1p5 structure */ >> > +static struct omap_sr_class_data class1p5_data = { >> > + .enable = sr_class1p5_enable, >> > + .disable = sr_class1p5_disable, >> > + .configure = sr_class1p5_configure, >> > + .class_type = SR_CLASS1P5, >> > + .class_init = sr_class1p5_cinit, >> > + .class_deinit = sr_class1p5_cdeinit, >> > + .notify = sr_class1p5_notify, >> > + /* >> > + * trigger for bound - this tells VP that SR has a voltage >> > + * change. we should ensure transdone is set before reading >> > + * vp voltage. >> > + */ >> > + .notify_flags = SR_NOTIFY_MCUBOUND, >> > + .class_priv_data = (void *)&class_1p5_data, >> > +}; >> > + >> > +/** >> > + * sr_class1p5_init() - register class 1p5 as default >> > + * >> > + * board files call this function to use class 1p5, we register with the >> > + * smartreflex subsystem >> > + */ >> > +static int __init sr_class1p5_init(void) >> > +{ >> > + int r; >> > + >> > + /* Enable this class only for OMAP3630 and OMAP4 */ >> > + if (!(cpu_is_omap3630() || cpu_is_omap44xx())) >> > + return -EINVAL; >> > + >> > + r = sr_register_class(&class1p5_data); >> > + if (r) { >> > + pr_err("SmartReflex class 1.5 driver: " >> > + "failed to register with %d\n", r); >> > + } else { >> > +#if CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY >> > + INIT_DELAYED_WORK_DEFERRABLE(&recal_work, do_recalibrate); >> > + schedule_delayed_work(&recal_work, msecs_to_jiffies( >> > + CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY)); >> > +#endif >> > + pr_info("SmartReflex class 1.5 driver: initialized (%dms)\n", >> > + CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY); >> > + } >> > + return r; >> > +} >> > +late_initcall(sr_class1p5_init); >> > diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c >> > index 7ac88da..5f7a33e 100644 >> > --- a/arch/arm/mach-omap2/smartreflex-class3.c >> > +++ b/arch/arm/mach-omap2/smartreflex-class3.c >> > @@ -21,7 +21,9 @@ static int sr_class3_enable(struct voltagedomain *voltdm, >> > return sr_enable(voltdm, volt_data); >> > } >> > >> > -static int sr_class3_disable(struct voltagedomain *voltdm, int is_volt_reset) >> > +static int sr_class3_disable(struct voltagedomain *voltdm, >> > + struct omap_volt_data *vdata, >> > + int is_volt_reset) >> > { >> > omap_vp_disable(voltdm); >> > sr_disable(voltdm); >> > diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c >> > index 3a5f2f6..bb55b65 100644 >> > --- a/arch/arm/mach-omap2/smartreflex.c >> > +++ b/arch/arm/mach-omap2/smartreflex.c >> > @@ -317,7 +317,9 @@ static void sr_stop_vddautocomp(struct omap_sr *sr) >> > } >> > >> > if (sr->autocomp_active) { >> > - sr_class->disable(sr->voltdm, 1); >> > + sr_class->disable(sr->voltdm, >> > + omap_voltage_get_nom_volt(sr->voltdm), >> > + 1); >> > if (sr_class->class_deinit && >> > sr_class->class_deinit(sr->voltdm, >> > sr_class->class_priv_data)) { >> > @@ -471,6 +473,28 @@ static u32 sr_retrieve_nvalue(struct omap_sr *sr, u32 efuse_offs) >> > /* Public Functions */ >> > >> > /** >> > + * is_sr_enabled() - is Smart reflex enabled for this domain? >> > + * @voltdm: voltage domain to check >> > + * >> > + * Returns 0 if SR is enabled for this domain, else returns err >> > + */ >> > +bool is_sr_enabled(struct voltagedomain *voltdm) >> > +{ >> > + struct omap_sr *sr; >> > + if (IS_ERR_OR_NULL(voltdm)) { >> > + pr_warning("%s: invalid param voltdm\n", __func__); >> > + return false; >> > + } >> > + sr = _sr_lookup(voltdm); >> > + if (IS_ERR(sr)) { >> > + pr_warning("%s: omap_sr struct for sr_%s not found\n", >> > + __func__, voltdm->name); >> > + return false; >> > + } >> > + return sr->autocomp_active; >> > +} >> > + >> > +/** >> > * sr_configure_errgen() - Configures the smrtreflex to perform AVS using the >> > * error generator module. >> > * @voltdm: VDD pointer to which the SR module to be configured belongs to. >> > @@ -839,6 +863,7 @@ void omap_sr_enable(struct voltagedomain *voltdm, >> > * omap_sr_disable() - API to disable SR without resetting the voltage >> > * processor voltage >> > * @voltdm: VDD pointer to which the SR module to be configured belongs to. >> > + * @volt_data: Voltage data to go to >> > * >> > * This API is to be called from the kernel in order to disable >> > * a particular smartreflex module. This API will in turn call >> > @@ -846,7 +871,8 @@ void omap_sr_enable(struct voltagedomain *voltdm, >> > * the smartreflex class disable not to reset the VP voltage after >> > * disabling smartreflex. >> > */ >> > -void omap_sr_disable(struct voltagedomain *voltdm) >> > +void omap_sr_disable(struct voltagedomain *voltdm, >> > + struct omap_volt_data *vdata) >> > { >> > struct omap_sr *sr = _sr_lookup(voltdm); >> > >> > @@ -865,7 +891,7 @@ void omap_sr_disable(struct voltagedomain *voltdm) >> > return; >> > } >> > >> > - sr_class->disable(voltdm, 0); >> > + sr_class->disable(voltdm, vdata, 0); >> > } >> > >> > /** >> > @@ -898,7 +924,7 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) >> > return; >> > } >> > >> > - sr_class->disable(voltdm, 1); >> > + sr_class->disable(voltdm, omap_voltage_get_nom_volt(voltdm), 1); >> > } >> > >> > /** >> > diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c >> > index 77cb0cd..c451835 100644 >> > --- a/arch/arm/mach-omap2/voltage.c >> > +++ b/arch/arm/mach-omap2/voltage.c >> > @@ -374,9 +374,45 @@ static int nom_volt_debug_get(void *data, u64 *val) >> > return 0; >> > } >> > >> > +static int dyn_volt_debug_get(void *data, u64 *val) >> > +{ >> > + struct omap_vdd_info *vdd = (struct omap_vdd_info *) data; >> > + struct omap_volt_data *volt_data; >> > + >> > + if (!vdd) { >> > + pr_warning("Wrong paramater passed\n"); >> > + return -EINVAL; >> > + } >> > + volt_data = omap_voltage_get_nom_volt(&vdd->voltdm); >> > + >> > + *val = volt_data->volt_dynamic_nominal; >> > + >> > + return 0; >> > +} >> > + >> > +static int calib_volt_debug_get(void *data, u64 *val) >> > +{ >> > + struct omap_vdd_info *vdd = (struct omap_vdd_info *) data; >> > + struct omap_volt_data *volt_data; >> > + >> > + if (!vdd) { >> > + pr_warning("Wrong paramater passed\n"); >> > + return -EINVAL; >> > + } >> > + volt_data = omap_voltage_get_nom_volt(&vdd->voltdm); >> > + >> > + *val = volt_data->volt_calibrated; >> > + >> > + return 0; >> > +} >> > + >> > DEFINE_SIMPLE_ATTRIBUTE(vp_volt_debug_fops, vp_volt_debug_get, NULL, "%llu\n"); >> > DEFINE_SIMPLE_ATTRIBUTE(nom_volt_debug_fops, nom_volt_debug_get, NULL, >> > "%llu\n"); >> > +DEFINE_SIMPLE_ATTRIBUTE(dyn_volt_debug_fops, dyn_volt_debug_get, NULL, >> > + "%llu\n"); >> > +DEFINE_SIMPLE_ATTRIBUTE(calib_volt_debug_fops, calib_volt_debug_get, NULL, >> > + "%llu\n"); >> > static void vp_latch_vsel(struct omap_vdd_info *vdd) >> > { >> > u32 vpconfig; >> > @@ -504,6 +540,12 @@ static void __init vdd_debugfs_init(struct omap_vdd_info *vdd) >> > (void) debugfs_create_file("curr_nominal_volt", S_IRUGO, >> > vdd->debug_dir, (void *) vdd, >> > &nom_volt_debug_fops); >> > + (void) debugfs_create_file("curr_dyn_nominal_volt", S_IRUGO, >> > + vdd->debug_dir, (void *) vdd, >> > + &dyn_volt_debug_fops); >> > + (void) debugfs_create_file("curr_calibrated_volt", S_IRUGO, >> > + vdd->debug_dir, (void *) vdd, >> > + &calib_volt_debug_fops); >> > } >> > >> > /* Voltage scale and accessory APIs */ >> > @@ -1132,6 +1174,33 @@ struct omap_volt_data *omap_voltage_get_nom_volt(struct voltagedomain *voltdm) >> > } >> > >> > /** >> > + * omap_voltage_calib_reset() - reset the calibrated voltage entries >> > + * @voltdm: voltage domain to reset the entries for >> > + * >> > + * when the calibrated entries are no longer valid, this api allows >> > + * the calibrated voltages to be reset. >> > + */ >> > +int omap_voltage_calib_reset(struct voltagedomain *voltdm) >> > +{ >> > + struct omap_vdd_info *vdd; >> > + struct omap_volt_data *volt_data; >> > + >> > + if (IS_ERR_OR_NULL(voltdm)) { >> > + pr_warning("%s: VDD specified does not exist!\n", __func__); >> > + return -EINVAL; >> > + } >> > + >> > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); >> > + volt_data = vdd->volt_data; >> > + /* reset the calibrated voltages as 0 */ >> > + while (volt_data->volt_nominal) { >> > + volt_data->volt_calibrated = 0; >> > + volt_data++; >> > + } >> > + return 0; >> > +} >> > + >> > +/** >> > * omap_vp_get_curr_volt() - API to get the current vp voltage. >> > * @voltdm: pointer to the VDD. >> > * >> > diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig >> > index b6333ae..dba7939 100644 >> > --- a/arch/arm/plat-omap/Kconfig >> > +++ b/arch/arm/plat-omap/Kconfig >> > @@ -67,6 +67,23 @@ config OMAP_SMARTREFLEX_CLASS3 >> > Class 3 implementation of Smartreflex employs continuous hardware >> > voltage calibration. >> > >> > +config OMAP_SMARTREFLEX_CLASS1P5 >> > + bool "Class 1.5 mode of Smartreflex Implementation" >> > + depends on OMAP_SMARTREFLEX && TWL4030_CORE >> > + help >> > + Say Y to enable Class 1.5 implementation of Smartreflex >> > + Class 1.5 implementation of Smartreflex employs software controlled >> > + hardware voltage calibration. >> > + >> > +config OMAP_SR_CLASS1P5_RECALIBRATION_DELAY >> > + int "Class 1.5 mode recalibration recalibration delay(ms)" >> > + depends on OMAP_SMARTREFLEX_CLASS1P5 >> > + default 86400000 >> > + help >> > + Setup the recalibration delay in milliseconds. Use 0 for never doing >> > + a recalibration. Defaults to recommended recalibration every 24hrs. >> > + If you do not understand this, use the default. >> > + >> > config OMAP_RESET_CLOCKS >> > bool "Reset unused clocks during boot" >> > depends on ARCH_OMAP >> > diff --git a/arch/arm/plat-omap/include/plat/smartreflex.h b/arch/arm/plat-omap/include/plat/smartreflex.h >> > index 07f35b2..ee5c58f 100644 >> > --- a/arch/arm/plat-omap/include/plat/smartreflex.h >> > +++ b/arch/arm/plat-omap/include/plat/smartreflex.h >> > @@ -167,6 +167,7 @@ struct omap_sr_pmic_data { >> > #define SR_CLASS1 0x1 >> > #define SR_CLASS2 0x2 >> > #define SR_CLASS3 0x3 >> > +#define SR_CLASS1P5 0x4 >> > >> > /** >> > * struct omap_sr_class_data - Smartreflex class driver info >> > @@ -187,7 +188,9 @@ struct omap_sr_pmic_data { >> > struct omap_sr_class_data { >> > int (*enable)(struct voltagedomain *voltdm, >> > struct omap_volt_data *volt_data); >> > - int (*disable)(struct voltagedomain *voltdm, int is_volt_reset); >> > + int (*disable)(struct voltagedomain *voltdm, >> > + struct omap_volt_data *volt_data, >> > + int is_volt_reset); >> > int (*class_init)(struct voltagedomain *voltdm, void *class_priv_data); >> > int (*class_deinit)(struct voltagedomain *voltdm, >> > void *class_priv_data); >> > @@ -235,7 +238,8 @@ struct omap_sr_data { >> > /* Smartreflex module enable/disable interface */ >> > void omap_sr_enable(struct voltagedomain *voltdm, >> > struct omap_volt_data *volt_data); >> > -void omap_sr_disable(struct voltagedomain *voltdm); >> > +void omap_sr_disable(struct voltagedomain *voltdm, >> > + struct omap_volt_data *volt_data); >> > void omap_sr_disable_reset_volt(struct voltagedomain *voltdm); >> > >> > /* API to register the pmic specific data with the smartreflex driver. */ >> > @@ -250,6 +254,7 @@ int sr_configure_minmax(struct voltagedomain *voltdm); >> > >> > /* API to register the smartreflex class driver with the smartreflex driver */ >> > int sr_register_class(struct omap_sr_class_data *class_data); >> > +bool is_sr_enabled(struct voltagedomain *voltdm); >> > #else >> > static inline void omap_sr_enable(struct voltagedomain *voltdm) {} >> > static inline void omap_sr_disable(struct voltagedomain *voltdm) {} >> > @@ -264,5 +269,9 @@ static inline void omap_sr_disable_reset_volt( >> > struct voltagedomain *voltdm) {} >> > static inline void omap_sr_register_pmic( >> > struct omap_sr_pmic_data *pmic_data) {} >> > +static inline bool is_sr_enabled(struct voltagedomain *voltdm) >> > +{ >> > + return false; >> > +} >> > #endif >> > #endif >> > diff --git a/arch/arm/plat-omap/include/plat/voltage.h b/arch/arm/plat-omap/include/plat/voltage.h >> > index 332c581..54445f0 100644 >> > --- a/arch/arm/plat-omap/include/plat/voltage.h >> > +++ b/arch/arm/plat-omap/include/plat/voltage.h >> > @@ -58,6 +58,8 @@ >> > #define OMAP4430_VDD_CORE_OPP50_UV 930000 >> > #define OMAP4430_VDD_CORE_OPP100_UV 1100000 >> > >> > +#define OMAP3PLUS_DYNAMIC_NOMINAL_MARGIN_UV 50000 >> > + >> > /** >> > * struct voltagedomain - omap voltage domain global structure. >> > * @name: Name of the voltage domain which can be used as a unique >> > @@ -81,6 +83,8 @@ struct voltagedomain { >> > */ >> > struct omap_volt_data { >> > u32 volt_nominal; >> > + u32 volt_calibrated; >> > + u32 volt_dynamic_nominal; >> > u32 sr_efuse_offs; >> > u8 sr_errminlimit; >> > u8 vp_errgain; >> > @@ -127,6 +131,7 @@ struct omap_volt_data *omap_voltage_get_nom_volt(struct voltagedomain *voltdm); >> > bool omap_vp_is_transdone(struct voltagedomain *voltdm); >> > bool omap_vp_clear_transdone(struct voltagedomain *voltdm); >> > struct dentry *omap_voltage_get_dbgdir(struct voltagedomain *voltdm); >> > +int omap_voltage_calib_reset(struct voltagedomain *voltdm); >> > #ifdef CONFIG_PM >> > int omap_voltage_register_pmic(struct voltagedomain *voltdm, >> > struct omap_volt_pmic_info *pmic_info); >> > @@ -160,7 +165,23 @@ static inline unsigned long omap_get_operation_voltage( >> > { >> > if (IS_ERR_OR_NULL(vdata)) >> > return 0; >> > - return vdata->volt_nominal; >> > + return (vdata->volt_calibrated) ? vdata->volt_calibrated : >> > + (vdata->volt_dynamic_nominal) ? vdata->volt_dynamic_nominal : >> > + vdata->volt_nominal; >> > } >> > >> > +/* what is my dynamic nominal? */ >> > +static inline unsigned long omap_get_dyn_nominal(struct omap_volt_data *vdata) >> > +{ >> > + if (IS_ERR_OR_NULL(vdata)) >> > + return 0; >> > + if (vdata->volt_calibrated) { >> > + unsigned long v = vdata->volt_calibrated + >> > + OMAP3PLUS_DYNAMIC_NOMINAL_MARGIN_UV; >> > + if (v > vdata->volt_nominal) >> > + return vdata->volt_nominal; >> > + return v; >> > + } >> > + return vdata->volt_nominal; >> > +} >> > #endif >> > -- >> > 1.7.1 >> > >> > -- >> > To unsubscribe from this list: send the line "unsubscribe linux-omap" in >> > the body of a message to majordomo@xxxxxxxxxxxxxxx >> > More majordomo info at http://vger.kernel.org/majordomo-info.html >> > >> I have tested this Patch Series on OMAP 3630 SDP, for MPU and IVA Powerdomains > Could you clarify on null pointer issue: on SDP3630? or OMAP4SDP? I > see there is a risk with OMAP4 where there is a bug in the following > scenario: > bootloader setups the OPP frequency for an OPP(e.g. 1GHz) which is > disabled by default on OPP table in kernel. this needs to be cleanly > fixed in pm.c, at the same time, the exposed functions in class1p5 > needs parameter validation as well. Sorry, it is reproduced on OMAP 4430 SDP. >> it throws error log "Unable to refer to NULL pointer" if >> curr_nominal_volt, curr_calibrated_volt >> or curr_dynamical_volt is accessed through debugfs. >> Could you please provide fix for this. > > > Regards, > Nishanth Menon > -- Thanks, Regards, Shweta -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html