[LG Optimus Black] Huexxx:
http://permalink.gmane.org/gmane.linux.ports.arm.omap/53604

Part 1/2:

On Tue, Mar 1, 2011 at 15:23, Gulati, Shweta <shweta.gulati <at> ti.com> wrote:
>
> Hi,
>
> On Sat, Feb 19, 2011 at 5:31 PM, Nishanth Menon <nm <at> ti.com> 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 <at> ti.com>
> > Cc: Eduardo Valentin <eduardo.valentin <at> nokia.com>
> > Cc: Igor Dmitriev <ext-dmitriev.igor <at> nokia.com>
> > Cc: Paul <paul <at> pwsan.com>
> > Cc: Peter 'p2' De Schrijver <Peter.De-Schrijver <at> nokia.com>
> > Cc: Tony Lindgren <tony <at> atomide.com>
> >
> > Signed-off-by: Nishanth Menon <nm <at> ti.com>
> > ---
> > 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 <at> ti.com>
> > + *
> > + * 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 */
> > +}
> > +