5,596,128 Members 34,295 Now Online
XDA Developers Android and Mobile Development Forum

Smartreflex - how to improve the algorithm

Tip us?
 
franciscofranco
Old
#1  
franciscofranco's Avatar
Recognized Developer - OP
Thanks Meter 80825
Posts: 14,690
Join Date: Dec 2010
Location: Mountain View, CA

 
DONATE TO ME
Default Smartreflex - how to improve the algorithm

Hey everyone, lets get this thing started.

One of the big problems in this device is the Smartreflex calibration on higher than stock frequencies (because thats what we hackers do here at XDA). I haven't tried to improve the algorithm just yet but I would be very happy to start discussion on this matter and if anyone has ideas, code snippets. Just bring it so we can discuss it properly. I will be contributing with code soon when I have time to take a deep look on the driver.

I won't tolerate off-topic, or questions like "My dog ate my kitten, is that kernel's fault?", so this will be VERY heavily moderated.

FacesIn app
Per-App Modes app
Nexus Display Control
franco.Kernel updater Free app
franco.Kernel - Galaxy Nexus
franco.Kernel - Nexus 4
franco.Kernel - Nexus 5
franco.Kernel - Nexus 7
franco.Kernel - Nexus 7 2013
franco.Kernel - Nexus 10


Google+ | Twitter | Portfolio

CruzerLite franco.Kernel case for the Nexus 4
CruzerLite franco.Kernel case for the Galaxy Nexus

You don't like my reply? Read this

However, if you put any trust in Quadrant scores you could use them to prove that dancing naked for 5 minutes in your garden affects device performance. By Chainfire

My applications translations are managed by:

The Following 15 Users Say Thank You to franciscofranco For This Useful Post: [ Click to Expand ]
 
simms22
Old
#2  
simms22's Avatar
Recognized Contributor
Thanks Meter 17961
Posts: 22,584
Join Date: Jun 2009
Location: BROOKLYN!
Quote:
Originally Posted by franciscofranco View Post
Hey everyone, lets get this thing started.

One of the big problems in this device is the Smartreflex calibration on higher than stock frequencies (because thats what we hackers do here at XDA). I haven't tried to improve the algorithm just yet but I would be very happy to start discussion on this matter and if anyone has ideas, code snippets. Just bring it so we can discuss it properly. I will be contributing with code soon when I have time to take a deep look on the driver.

I won't tolerate off-topic, or questions like "My dog ate my kitten, is that kernel's fault?", so this will be VERY heavily moderated.
honestly, id rather just have smart reflex gone. i feel more comfortable being in control then letting an algorithm decide.
Google+ Profile
Twitter Simms22
DerKernel For Trinity kernels and more!
The Following 3 Users Say Thank You to simms22 For This Useful Post: [ Click to Expand ]
 
franciscofranco
Old
#3  
franciscofranco's Avatar
Recognized Developer - OP
Thanks Meter 80825
Posts: 14,690
Join Date: Dec 2010
Location: Mountain View, CA

 
DONATE TO ME
Quote:
Originally Posted by simms22 View Post
honestly, id rather just have smart reflex gone. i feel more comfortable being in control then letting an algorithm decide.
Why? That way we would have to hardcode all the voltages and we would have to set the values a little bit higher than what SR usually calibrates to prevent **** going haywire with some poor hardware devices. SR does a good job and mostly never misses a good sweet spot between stability and battery savings.

Do you have any set of voltage values you would recommend that would work on 99% of the devices?

FacesIn app
Per-App Modes app
Nexus Display Control
franco.Kernel updater Free app
franco.Kernel - Galaxy Nexus
franco.Kernel - Nexus 4
franco.Kernel - Nexus 5
franco.Kernel - Nexus 7
franco.Kernel - Nexus 7 2013
franco.Kernel - Nexus 10


Google+ | Twitter | Portfolio

CruzerLite franco.Kernel case for the Nexus 4
CruzerLite franco.Kernel case for the Galaxy Nexus

You don't like my reply? Read this

However, if you put any trust in Quadrant scores you could use them to prove that dancing naked for 5 minutes in your garden affects device performance. By Chainfire

My applications translations are managed by:

The Following User Says Thank You to franciscofranco For This Useful Post: [ Click to Expand ]
 
simms22
Old
#4  
simms22's Avatar
Recognized Contributor
Thanks Meter 17961
Posts: 22,584
Join Date: Jun 2009
Location: BROOKLYN!
Quote:
Originally Posted by franciscofranco View Post
Why? That way we would have to hardcode all the voltages and we would have to set values a little bit higher than what SR usually calibrates to prevent **** going haywire with some poor hardware devices. SR does a good job and mostly never misses a good sweet spot between stability and battery savings.

Do you have any set of voltage values you would recommend that would work on 99% of the devices?
true.
since all of our devices seem to like different values, i guess there isnt a value that would work on 99% of our devices, unfortunately. so it would have to be a range of values. i guess i was looking at it from a users point of view, and not from the developers side.
Google+ Profile
Twitter Simms22
DerKernel For Trinity kernels and more!
 
franciscofranco
Old
#5  
franciscofranco's Avatar
Recognized Developer - OP
Thanks Meter 80825
Posts: 14,690
Join Date: Dec 2010
Location: Mountain View, CA

 
DONATE TO ME
Quote:
Originally Posted by simms22 View Post
true.
since all of our devices seem to like different values, i guess there isnt a value that would work on 99% of our devices, unfortunately. so it would have to be a range of values. i guess i was looking at it from a users point of view, and not from the developers side.
Maybe we can try both, trying to hardcore them and let users find their own sweet spot and at the same time perfect the algorithm, I believe theres margin for improvement. If you find some good values feel free to share here.

I also hope some other good developers come here and throw out their ideas.

Small off-topic but related to this new forum, can you create a new topic about your SoD experience (the email you sent us how you could reproduce the issue) would be very nice we could discuss that as well in a specific topic.

FacesIn app
Per-App Modes app
Nexus Display Control
franco.Kernel updater Free app
franco.Kernel - Galaxy Nexus
franco.Kernel - Nexus 4
franco.Kernel - Nexus 5
franco.Kernel - Nexus 7
franco.Kernel - Nexus 7 2013
franco.Kernel - Nexus 10


Google+ | Twitter | Portfolio

CruzerLite franco.Kernel case for the Nexus 4
CruzerLite franco.Kernel case for the Galaxy Nexus

You don't like my reply? Read this

However, if you put any trust in Quadrant scores you could use them to prove that dancing naked for 5 minutes in your garden affects device performance. By Chainfire

My applications translations are managed by:

The Following User Says Thank You to franciscofranco For This Useful Post: [ Click to Expand ]
 
ak
Old
#6  
ak's Avatar
Recognized Contributor
Thanks Meter 36016
Posts: 8,429
Join Date: Feb 2011
Location: Ak Land Valley

 
DONATE TO ME
Thumbs up smartreflex

I do not know if the code is optimized or not I have no basis to evaluate.

From my experience I have seen that in order to avoid problems related to the various soc, I decided to let users tinker with voltages and smartreflex.

This is why in some cases I raised the voltages.

From that day problems with my kernel is greatly decreased.



AK
Website: link

Github: link
Twitter: link
Xda N4: link
Xda N7: link
Google+: link
Dirty_v GN: link
Nephilim: link

I do NOT answer technical questions via PM.
Post your question in the correct thread if you want a response.
by morfic:
Just flash over.
Only thing we wipe here is our butts!


The Following User Says Thank You to ak For This Useful Post: [ Click to Expand ]
 
franciscofranco
Old
#7  
franciscofranco's Avatar
Recognized Developer - OP
Thanks Meter 80825
Posts: 14,690
Join Date: Dec 2010
Location: Mountain View, CA

 
DONATE TO ME
Quote:
Originally Posted by anarkia1976 View Post
I do not know if the code is optimized or not I have no basis to evaluate.

From my experience I have seen that in order to avoid problems related to the various soc, I decided to let users tinker with voltages and smartreflex.

This is why in some cases I raised the voltages.

From that day problems with my kernel is greatly decreased.
What do you mean by tinkering with voltages and smartreflex? Thats what I have in my kernel at the moment: custom voltage interface (from Ezekeel) and SR is also active, so I dunno what you mean.

FacesIn app
Per-App Modes app
Nexus Display Control
franco.Kernel updater Free app
franco.Kernel - Galaxy Nexus
franco.Kernel - Nexus 4
franco.Kernel - Nexus 5
franco.Kernel - Nexus 7
franco.Kernel - Nexus 7 2013
franco.Kernel - Nexus 10


Google+ | Twitter | Portfolio

CruzerLite franco.Kernel case for the Nexus 4
CruzerLite franco.Kernel case for the Galaxy Nexus

You don't like my reply? Read this

However, if you put any trust in Quadrant scores you could use them to prove that dancing naked for 5 minutes in your garden affects device performance. By Chainfire

My applications translations are managed by:

 
zillinder
Old
#8  
Junior Member
Thanks Meter 7
Posts: 27
Join Date: May 2010
Franco, would you mind sharing the code of SR in here and let us have a look at it?
I haven't seen it so far plus I am in no way a kernel dev, but I'd like to see it and throw something at the thread whichever comes to my mind, always ontopic of course
 
franciscofranco
Old
#9  
franciscofranco's Avatar
Recognized Developer - OP
Thanks Meter 80825
Posts: 14,690
Join Date: Dec 2010
Location: Mountain View, CA

 
DONATE TO ME
Quote:
Originally Posted by zillinder View Post
Franco, would you mind sharing the code of SR in here and let us have a look at it?
I haven't seen it so far plus I am in no way a kernel dev, but I'd like to see it and throw something at the thread whichever comes to my mind, always ontopic of course
Code:
/*
 * Smart reflex Class 1.5 specific implementations
 *
 * Copyright (C) 2010-2011 Texas Instruments, Inc.
 * Nishanth Menon <nm@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/uaccess.h>
#include <linux/kobject.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/opp.h>

#include "smartreflex.h"
#include "voltage.h"
#include "dvfs.h"

#define MAX_VDDS		3
#define SR1P5_SAMPLING_DELAY_MS	1
#define SR1P5_STABLE_SAMPLES	10
#define SR1P5_MAX_TRIGGERS	5

/*
 * We expect events in 10uS, if we don't receive it in twice as long,
 * we stop waiting for the event and use the current value
 */
#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
 * @u_volt_samples:	private data for collecting voltage samples in
 *			case oscillations. filled by the notifier and
 *			consumed by the work item.
 * @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;
	unsigned long u_volt_samples[SR1P5_STABLE_SAMPLES];
	bool work_active;
};

#if CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY
/* recal_work:	recalibration calibration work */
static struct delayed_work recal_work;
#endif

/**
 * 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_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;
	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];
	}
	/* Use the nominal voltage as the safe voltage to recover bad osc */
	if (u_volt_safe > volt_data->volt_nominal)
		u_volt_safe = volt_data->volt_nominal;


	/* 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;
		}
		/* Add margin IF we are lower than nominal */
		if ((u_volt_safe + u_volt_margin) < volt_data->volt_nominal) {
			u_volt_safe += u_volt_margin;
		} else {
			pr_err("%s: %s could not add %ld[%d] margin"
				"to vnom %d curr_v=%ld\n",
				__func__, voltdm->name, 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);
}

#if CONFIG_OMAP_SR_CLASS1P5_RECALIBRATION_DELAY

/**
 * sr_class1p5_voltdm_recal() - Helper routine to reset calibration.
 * @voltdm:	Voltage domain to reset calibration for
 * @user:	unused
 *
 * NOTE: Appropriate locks must be held by calling path to ensure mutual
 * exclusivity
 */
static int sr_class1p5_voltdm_recal(struct voltagedomain *voltdm,
		void *user)
{
	struct omap_volt_data *vdata;

	/*
	 * we need to go no further if sr is not enabled for this domain or
	 * voltage processor is not present for this voltage domain
	 * (example vdd_wakeup). Class 1.5 requires Voltage processor
	 * to function.
	 */
	if (!voltdm->vp || !is_sr_enabled(voltdm))
		return 0;

	vdata = omap_voltage_get_curr_vdata(voltdm);
	if (!vdata) {
		pr_err("%s: unable to find current voltage for vdd_%s\n",
			__func__, voltdm->name);
		return -ENXIO;
	}

	omap_sr_disable(voltdm);
	omap_voltage_calib_reset(voltdm);
	voltdm_reset(voltdm);
	omap_sr_enable(voltdm, vdata);
	pr_info("%s: %s: calibration reset\n", __func__, voltdm->name);

	return 0;
}

/**
 * sr_class1p5_recal_work() - 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 sr_class1p5_recal_work(struct work_struct *work)
{
	mutex_lock(&omap_dvfs_lock);
	if (voltdm_for_each(sr_class1p5_voltdm_recal, NULL))
		pr_err("%s: Recalibration failed\n", __func__);
	mutex_unlock(&omap_dvfs_lock);
	/* 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 for a voltage domain
 * @voltdm:		voltage domain to enable SR for
 * @voltdm_cdata:	voltage domain specific private class data
 * @volt_data:		voltdata for the current OPP being transitioned to
 *
 * 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.
 *
 * NOTE: Appropriate locks must be held by calling path to ensure mutual
 * exclusivity
 */
static int sr_class1p5_enable(struct voltagedomain *voltdm,
			      void *voltdm_cdata,
			      struct omap_volt_data *volt_data)
{
	int r;
	struct sr_class1p5_work_data *work_data;

	if (IS_ERR_OR_NULL(voltdm) || IS_ERR_OR_NULL(volt_data)) {
		pr_err("%s: bad parameters!\n", __func__);
		return -EINVAL;
	}

	/* If already calibrated, nothing to do here.. */
	if (volt_data->volt_calibrated)
		return 0;

	work_data = (struct sr_class1p5_work_data *)voltdm_cdata;
	if (IS_ERR_OR_NULL(work_data)) {
		pr_err("%s: 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);
		sr_disable_errgen(voltdm);
		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 1.5 mode for a voltage domain
 * @voltdm: voltage domain for the sr which needs disabling
 * @volt_data:	voltage data for current OPP to disable
 * @voltdm_cdata: voltage domain specific private class data
 * @is_volt_reset: reset the voltage?
 *
 * This function has the necessity to either disable SR alone OR disable SR
 * and reset voltage to appropriate level depending on is_volt_reset parameter.
 *
 * Disabling SR H/w loop:
 * If calibration is complete or not yet triggered, we have no need to disable
 * SR h/w loop.
 * If calibration is complete, we would have already disabled SR AVS at the end
 * of calibration and h/w loop is inactive when this is called.
 * If it was never calibrated before, H/w loop was never enabled in the first
 * place to disable.
 * If calibration is underway, we cancel the work queue and disable SR. This is
 * to provide priority to DVFS transition as such transitions cannot wait
 * without impacting user experience.
 *
 * Resetting voltage:
 * If we have already completed calibration, then resetting to nominal voltage
 * is not required as we are functioning at safe voltage levels.
 * If we have not started calibration, we would like to reset to nominal voltage
 * If calibration is underway and we are attempting to reset voltage as
 * well, it implies we are in idle/suspend paths where we give priority
 * to calibration activity and a retry will be attempted.
 *
 * NOTE: Appropriate locks must be held by calling path to ensure mutual
 * exclusivity
 */
static int sr_class1p5_disable(struct voltagedomain *voltdm,
			       void *voltdm_cdata,
			       struct omap_volt_data *volt_data,
			       int is_volt_reset)
{
	struct sr_class1p5_work_data *work_data;

	if (IS_ERR_OR_NULL(voltdm) || IS_ERR_OR_NULL(volt_data)) {
		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: bad work data??\n", __func__);
		return -EINVAL;
	}
	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);
		sr_disable_errgen(voltdm);
		omap_vp_disable(voltdm);
		sr_disable(voltdm);
	}

	/* If already calibrated, don't need to reset voltage */
	if (volt_data->volt_calibrated)
		return 0;

	if (is_volt_reset)
		voltdm_reset(voltdm);
	return 0;
}

/**
 * sr_class1p5_configure() - configuration function
 * @voltdm:	configure for which voltage domain
 * @voltdm_cdata: voltage domain specific private class data
 *
 * we dont do much here other than setup some registers for
 * the sr module involved.
 */
static int sr_class1p5_configure(struct voltagedomain *voltdm,
				 void *voltdm_cdata)
{
	if (IS_ERR_OR_NULL(voltdm)) {
		pr_err("%s: bad parameters!\n", __func__);
		return -EINVAL;
	}

	return sr_configure_errgen(voltdm);
}

/**
 * sr_class1p5_init() - class 1p5 init
 * @voltdm:		sr voltage domain
 * @voltdm_cdata:	voltage domain specific private class data
 *			allocated by class init with work item data
 *			freed by deinit.
 * @class_priv_data:	private data for the class (unused)
 *
 * we do class specific initialization like creating sysfs/debugfs entries
 * needed, spawning of a kthread if needed etc.
 */
static int sr_class1p5_init(struct voltagedomain *voltdm,
			    void **voltdm_cdata, void *class_priv_data)
{
	struct sr_class1p5_work_data *work_data;

	if (IS_ERR_OR_NULL(voltdm) || IS_ERR_OR_NULL(voltdm_cdata)) {
		pr_err("%s: bad parameters!\n", __func__);
		return -EINVAL;
	}

	if (!IS_ERR_OR_NULL(*voltdm_cdata)) {
		pr_err("%s: ooopps.. class already initialized for %s! bug??\n",
		       __func__, voltdm->name);
		return -EINVAL;
	}
	/* setup our work params */
	work_data = kzalloc(sizeof(struct sr_class1p5_work_data), GFP_KERNEL);
	if (!work_data) {
		pr_err("%s: no memory to allocate work data on domain %s\n",
			__func__, voltdm->name);
		return -ENOMEM;
	}

	work_data->voltdm = voltdm;
	INIT_DELAYED_WORK_DEFERRABLE(&work_data->work, sr_class1p5_calib_work);
	*voltdm_cdata = (void *)work_data;

	return 0;
}

/**
 * sr_class1p5_deinit() - class 1p5 deinitialization
 * @voltdm:	voltage domain for which to do this.
 * @voltdm_cdata: voltage domain specific private class data
 *		allocated by class init with work item data
 *		freed by deinit.
 * @class_priv_data: class private data for deinitialiation (unused)
 *
 * currently only resets the calibrated voltage forcing DVFS voltages
 * to be used in the system
 *
 * NOTE: Appropriate locks must be held by calling path to ensure mutual
 * exclusivity
 */
static int sr_class1p5_deinit(struct voltagedomain *voltdm,
			      void **voltdm_cdata, void *class_priv_data)
{
	struct sr_class1p5_work_data *work_data;

	if (IS_ERR_OR_NULL(voltdm) || IS_ERR_OR_NULL(voltdm_cdata)) {
		pr_err("%s: bad parameters!\n", __func__);
		return -EINVAL;
	}

	if (IS_ERR_OR_NULL(*voltdm_cdata)) {
		pr_err("%s: ooopps.. class not initialized for %s! bug??\n",
		       __func__, voltdm->name);
		return -EINVAL;
	}

	work_data = (struct sr_class1p5_work_data *) *voltdm_cdata;

	/*
	 * we dont have SR periodic calib anymore.. so reset calibs
	 * we are already protected by appropriate locks, so no lock needed
	 * here.
	 */
	if (work_data->work_active)
		sr_class1p5_disable(voltdm, work_data, work_data->vdata, 0);

	/* Ensure worker canceled. */
	cancel_delayed_work_sync(&work_data->work);
	omap_voltage_calib_reset(voltdm);
	voltdm_reset(voltdm);

	*voltdm_cdata = NULL;
	kfree(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,
	.init = sr_class1p5_init,
	.deinit = sr_class1p5_deinit,
	.notify = sr_class1p5_notify,
	/*
	 * trigger for bound - this tells VP that SR has a voltage
	 * change. we should try and ensure transdone is set before reading
	 * vp voltage.
	 */
	.notify_flags = SR_NOTIFY_MCUBOUND,
};

/**
 * sr_class1p5_driver_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_driver_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,
					     sr_class1p5_recal_work);
		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_driver_init);

FacesIn app
Per-App Modes app
Nexus Display Control
franco.Kernel updater Free app
franco.Kernel - Galaxy Nexus
franco.Kernel - Nexus 4
franco.Kernel - Nexus 5
franco.Kernel - Nexus 7
franco.Kernel - Nexus 7 2013
franco.Kernel - Nexus 10


Google+ | Twitter | Portfolio

CruzerLite franco.Kernel case for the Nexus 4
CruzerLite franco.Kernel case for the Galaxy Nexus

You don't like my reply? Read this

However, if you put any trust in Quadrant scores you could use them to prove that dancing naked for 5 minutes in your garden affects device performance. By Chainfire

My applications translations are managed by:

The Following 13 Users Say Thank You to franciscofranco For This Useful Post: [ Click to Expand ]
 
supernova_00
Old
(Last edited by supernova_00; 25th January 2013 at 09:10 PM.)
#10  
Senior Member
Thanks Meter 44
Posts: 266
Join Date: Feb 2012
Location: Aberdeen, MD
I might be pointing out the obvious here but sometimes the obvious isn't so obvious...has anyone contacted Nishanth Menon that is named in the code header to see if TI has a latest and greatest SR? I would assume they would since phones are coming out now with higher CPU speeds then what the current SR could handle around two years ago. I see the guy is pretty active online with posting stuff to open source sites and still deals with smart reflex related things as this site shows: http://marc.info/?a=124043331500007&r=1&w=2
I dunno what that site is or know what any of that stuff means but I just stumbled upon it and just wanted to show he is active with TI and smart reflex work, among other things

The Following 2 Users Say Thank You to supernova_00 For This Useful Post: [ Click to Expand ]
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes