aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/smartreflex-class1p5.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2/smartreflex-class1p5.c')
-rw-r--r--arch/arm/mach-omap2/smartreflex-class1p5.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/smartreflex-class1p5.c b/arch/arm/mach-omap2/smartreflex-class1p5.c
new file mode 100644
index 0000000..2090884
--- /dev/null
+++ b/arch/arm/mach-omap2/smartreflex-class1p5.c
@@ -0,0 +1,678 @@
+/*
+ * 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 = 0;
+ struct omap_volt_data *volt_data;
+ struct voltagedomain *voltdm;
+ int idx = 0;
+
+ if (!work) {
+ pr_err("%s: ooops.. null work_data?\n", __func__);
+ return;
+ }
+
+ /*
+ * Handle the case where we might have just been scheduled AND
+ * 1.5 disable was called.
+ */
+ if (!mutex_trylock(&omap_dvfs_lock)) {
+ schedule_delayed_work(&work_data->work,
+ msecs_to_jiffies(SR1P5_SAMPLING_DELAY_MS *
+ SR1P5_STABLE_SAMPLES));
+ return;
+ }
+
+ voltdm = work_data->voltdm;
+ /*
+ * In the unlikely case that we did get through when unplanned,
+ * flag and return.
+ */
+ if (unlikely(!work_data->work_active)) {
+ pr_err("%s:%s unplanned work invocation!\n", __func__,
+ voltdm->name);
+ mutex_unlock(&omap_dvfs_lock);
+ return;
+ }
+
+ volt_data = work_data->vdata;
+
+ work_data->num_calib_triggers++;
+ /* if we are triggered first time, we need to start isr to sample */
+ if (work_data->num_calib_triggers == 1) {
+ /* We could be interrupted many times, so, only for debug */
+ pr_debug("%s: %s: Calibration start: Voltage Nominal=%d\n",
+ __func__, voltdm->name, volt_data->volt_nominal);
+ goto start_sampling;
+ }
+
+ /* Stop isr from interrupting our measurements :) */
+ sr_notifier_control(voltdm, false);
+
+ /*
+ * Quit sampling
+ * a) if we have oscillations
+ * b) if we have nominal voltage as the voltage
+ */
+ if (work_data->num_calib_triggers == SR1P5_MAX_TRIGGERS)
+ goto stop_sampling;
+
+ /* if there are no samples captured.. SR is silent, aka stability! */
+ if (!work_data->num_osc_samples) {
+ /* Did we interrupt too early? */
+ u_volt_current = omap_vp_get_curr_volt(voltdm);
+ if (u_volt_current >= volt_data->volt_nominal)
+ goto start_sampling;
+ u_volt_safe = u_volt_current;
+ goto done_calib;
+ }
+
+ /* we have potential oscillations/first sample */
+start_sampling:
+ work_data->num_osc_samples = 0;
+
+ /* Clear transdone events so that we can go on. */
+ do {
+ if (!omap_vp_is_transdone(voltdm))
+ break;
+ idx++;
+ /* get some constant delay */
+ udelay(1);
+ omap_vp_clear_transdone(voltdm);
+ } while (idx < MAX_CHECK_VPTRANS_US);
+ if (idx >= MAX_CHECK_VPTRANS_US)
+ pr_warning("%s: timed out waiting for transdone clear!!\n",
+ __func__);
+
+ /* Clear pending events */
+ sr_notifier_control(voltdm, false);
+ /* trigger sampling */
+ sr_notifier_control(voltdm, true);
+ schedule_delayed_work(&work_data->work,
+ msecs_to_jiffies(SR1P5_SAMPLING_DELAY_MS *
+ SR1P5_STABLE_SAMPLES));
+ mutex_unlock(&omap_dvfs_lock);
+ return;
+
+stop_sampling:
+ /*
+ * We are here for Oscillations due to two scenarios:
+ * a) SR is attempting to adjust voltage lower than VLIMITO
+ * which VP will ignore, but SR will re-attempt
+ * b) actual oscillations
+ * NOTE: For debugging, enable debug to see the samples.
+ */
+ pr_warning("%s: %s Stop sampling: Voltage Nominal=%d samples=%d\n",
+ __func__, work_data->voltdm->name,
+ volt_data->volt_nominal, work_data->num_osc_samples);
+
+ /* pick up current voltage */
+ u_volt_current = omap_vp_get_curr_volt(voltdm);
+
+ /* Just in case we got more interrupts than our tiny buffer */
+ if (work_data->num_osc_samples > SR1P5_STABLE_SAMPLES)
+ idx = SR1P5_STABLE_SAMPLES;
+ else
+ idx = work_data->num_osc_samples;
+ /* Index at 0 */
+ idx -= 1;
+ u_volt_safe = u_volt_current;
+ /* Grab the max of the samples as the stable voltage */
+ for (; idx >= 0; idx--) {
+ pr_debug("%s: osc_v[%d]=%ld, safe_v=%ld\n", __func__, idx,
+ work_data->u_volt_samples[idx], u_volt_safe);
+ if (work_data->u_volt_samples[idx] > u_volt_safe)
+ u_volt_safe = work_data->u_volt_samples[idx];
+ }
+
+ /* Fall through to close up common stuff */
+done_calib:
+ sr_disable_errgen(voltdm);
+ omap_vp_disable(voltdm);
+ sr_disable(voltdm);
+
+ /* Add margin if needed */
+ if (volt_data->volt_margin) {
+ struct omap_voltdm_pmic *pmic = voltdm->pmic;
+ /* Convert to rounded to PMIC step level if available */
+ if (pmic && pmic->vsel_to_uv && pmic->uv_to_vsel) {
+ /*
+ * To ensure conversion works:
+ * use a proper base voltage - we use the current volt
+ * then convert it with pmic routine to vsel and back
+ * to voltage, and finally remove the base voltage
+ */
+ u_volt_margin = u_volt_current + volt_data->volt_margin;
+ u_volt_margin = pmic->uv_to_vsel(u_volt_margin);
+ u_volt_margin = pmic->vsel_to_uv(u_volt_margin);
+ u_volt_margin -= u_volt_current;
+ } else {
+ u_volt_margin = volt_data->volt_margin;
+ }
+
+ u_volt_safe += u_volt_margin;
+ }
+
+ if (u_volt_safe > volt_data->volt_nominal) {
+ pr_warning("%s: %s Vsafe %ld > Vnom %d. %ld[%d] margin on"
+ "vnom %d curr_v=%ld\n", __func__, voltdm->name,
+ u_volt_safe, volt_data->volt_nominal, u_volt_margin,
+ volt_data->volt_margin, volt_data->volt_nominal,
+ u_volt_current);
+ }
+
+ volt_data->volt_calibrated = u_volt_safe;
+ /* Setup my dynamic voltage for the next calibration for this opp */
+ volt_data->volt_dynamic_nominal = omap_get_dyn_nominal(volt_data);
+
+ /*
+ * if the voltage we decided as safe is not the current voltage,
+ * switch
+ */
+ if (volt_data->volt_calibrated != u_volt_current) {
+ pr_debug("%s: %s reconfiguring to voltage %d\n",
+ __func__, voltdm->name, volt_data->volt_calibrated);
+ voltdm_scale(voltdm, volt_data);
+ }
+
+ pr_info("%s: %s: Calibration complete: Voltage:Nominal=%d,"
+ "Calib=%d,margin=%d\n",
+ __func__, voltdm->name, volt_data->volt_nominal,
+ volt_data->volt_calibrated, volt_data->volt_margin);
+ /*
+ * TODO: Setup my wakeup voltage to allow immediate going to OFF and
+ * on - Pending twl and voltage layer cleanups.
+ * This is necessary, as this is not done as part of regular
+ * Dvfs flow.
+ * vc_setup_on_voltage(voltdm, volt_data->volt_calibrated);
+ */
+ work_data->work_active = false;
+ mutex_unlock(&omap_dvfs_lock);
+}
+
+#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);