Thara Gopinath <thara@xxxxxx> writes: > This patch adds voltage driver support for OMAP3. The driver > allows configuring the voltage controller and voltage > processors during init and exports APIs to enable/disable > voltage processors, scale voltage and reset voltage. > The driver also maintains the global voltage table on a per > VDD basis which contains the various voltages supported by the > VDD along with per voltage dependent data like smartreflex > n-target value, errminlimit and voltage processor errorgain. > The driver allows scaling of VDD voltages either through > "vc bypass method" or through "vp forceupdate method" the > choice being configurable through the board file. > > This patch contains code originally in linux omap pm branch > smartreflex driver. Major contributors to this driver are > Lesly A M, Rajendra Nayak, Kalle Jokiniemi, Paul Walmsley, > Nishant Menon, Kevin Hilman. > > Signed-off-by: Thara Gopinath <thara@xxxxxx> This is looking pretty good. Mostly minor style issues left to correct. > --- > arch/arm/mach-omap2/Makefile | 3 +- > arch/arm/mach-omap2/voltage.c | 1119 +++++++++++++++++++++++++++++ > arch/arm/plat-omap/include/plat/voltage.h | 132 ++++ > 3 files changed, 1253 insertions(+), 1 deletions(-) > create mode 100644 arch/arm/mach-omap2/voltage.c > create mode 100644 arch/arm/plat-omap/include/plat/voltage.h > > diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile > index 63b2d88..1c095cf 100644 > --- a/arch/arm/mach-omap2/Makefile > +++ b/arch/arm/mach-omap2/Makefile > @@ -49,7 +49,8 @@ obj-$(CONFIG_ARCH_OMAP2) += sdrc2xxx.o > ifeq ($(CONFIG_PM),y) > obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o > obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o > -obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o cpuidle34xx.o > +obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o voltage.o \ > + cpuidle34xx.o > obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o > obj-$(CONFIG_PM_DEBUG) += pm-debug.o > > diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c > new file mode 100644 > index 0000000..cb0fcac > --- /dev/null > +++ b/arch/arm/mach-omap2/voltage.c > @@ -0,0 +1,1119 @@ > +/* > + * OMAP3/OMAP4 Voltage Management Routines > + * > + * Author: Thara Gopinath <thara@xxxxxx> > + * > + * Copyright (C) 2007 Texas Instruments, Inc. > + * Rajendra Nayak <rnayak@xxxxxx> > + * Lesly A M <x0080970@xxxxxx> > + * > + * Copyright (C) 2008 Nokia Corporation > + * Kalle Jokiniemi > + * > + * Copyright (C) 2010 Texas Instruments, Inc. > + * Thara Gopinath <thara@xxxxxx> > + * > + * 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/pm.h> > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <linux/clk.h> > +#include <linux/err.h> > + > +#include <plat/omap-pm.h> > +#include <plat/omap34xx.h> > +#include <plat/opp.h> > +#include <plat/opp_twl_tps.h> > +#include <plat/clock.h> > +#include <plat/common.h> > +#include <plat/voltage.h> > + > +#include "prm-regbits-34xx.h" > + > +#define VP_IDLE_TIMEOUT 200 > +#define VP_TRANXDONE_TIMEOUT 300 > + > +/* PRM voltage module */ > +static u32 volt_mod; > + > +/* Voltage processor register offsets */ > +struct vp_reg_offs { > + u8 vpconfig; > + u8 vstepmin; > + u8 vstepmax; > + u8 vlimitto; > + u8 vstatus; > + u8 voltage; > +}; > + > +/* Voltage Processor bit field values, shifts and masks */ > +struct vp_reg_val { > + /* VPx_VPCONFIG */ > + u32 vpconfig_erroroffset; > + u16 vpconfig_errorgain; > + u32 vpconfig_errorgain_mask; > + u8 vpconfig_errorgain_shift; > + u32 vpconfig_initvoltage_mask; > + u8 vpconfig_initvoltage_shift; > + u32 vpconfig_timeouten; > + u32 vpconfig_initvdd; > + u32 vpconfig_forceupdate; > + u32 vpconfig_vpenable; > + /* VPx_VSTEPMIN */ > + u8 vstepmin_stepmin; > + u16 vstepmin_smpswaittimemin; > + u8 vstepmin_stepmin_shift; > + u8 vstepmin_smpswaittimemin_shift; > + /* VPx_VSTEPMAX */ > + u8 vstepmax_stepmax; > + u16 vstepmax_smpswaittimemax; > + u8 vstepmax_stepmax_shift; > + u8 vstepmax_smpswaittimemax_shift; > + /* VPx_VLIMITTO */ > + u16 vlimitto_vddmin; > + u16 vlimitto_vddmax; > + u16 vlimitto_timeout; > + u16 vlimitto_vddmin_shift; > + u16 vlimitto_vddmax_shift; > + u16 vlimitto_timeout_shift; > + /* PRM_IRQSTATUS*/ > + u32 tranxdone_status; > +}; > + > +/** > + * omap_vdd_info - Per Voltage Domain info > + * > + * @volt_data : voltage table having the distinct voltages supported > + * by the domain and other associated per voltage data. > + * @vp_offs : structure containing the offsets for various > + * vp registers > + * @vp_reg : the register values, shifts, masks for various > + * vp registers > + * @volt_clk : the clock associated with the vdd. > + * @opp_dev : the 'struct device' associated with this vdd. > + * @volt_data_count : Number of distinct voltages supported by this vdd. > + * @nominal_volt : Nominal voltaged for this vdd. > + * cmdval_reg : Voltage controller cmdval register. > + * @vdd_sr_reg : The smartreflex register associated with this VDD. > + */ > +struct omap_vdd_info{ > + struct omap_volt_data *volt_data; > + struct vp_reg_offs vp_offs; > + struct vp_reg_val vp_reg; > + struct clk *volt_clk; > + struct device *opp_dev; > + struct voltagedomain voltdm; > + int volt_data_count; > + unsigned long nominal_volt; > + u8 cmdval_reg; > + u8 vdd_sr_reg; > +}; > +static struct omap_vdd_info *vdd_info; > +/* > + * Number of scalable voltage domains. > + */ > +static int no_scalable_vdd; > + > +/* OMAP3 VDD sturctures */ > +static struct omap_vdd_info omap3_vdd_info[] = { > + { > + .vp_offs = { > + .vpconfig = OMAP3_PRM_VP1_CONFIG_OFFSET, > + .vstepmin = OMAP3_PRM_VP1_VSTEPMIN_OFFSET, > + .vstepmax = OMAP3_PRM_VP1_VSTEPMAX_OFFSET, > + .vlimitto = OMAP3_PRM_VP1_VLIMITTO_OFFSET, > + .vstatus = OMAP3_PRM_VP1_STATUS_OFFSET, > + .voltage = OMAP3_PRM_VP1_VOLTAGE_OFFSET, > + }, > + .voltdm = { > + .name = "mpu", > + }, > + }, > + { > + .vp_offs = { > + .vpconfig = OMAP3_PRM_VP2_CONFIG_OFFSET, > + .vstepmin = OMAP3_PRM_VP2_VSTEPMIN_OFFSET, > + .vstepmax = OMAP3_PRM_VP2_VSTEPMAX_OFFSET, > + .vlimitto = OMAP3_PRM_VP2_VLIMITTO_OFFSET, > + .vstatus = OMAP3_PRM_VP2_STATUS_OFFSET, > + .voltage = OMAP3_PRM_VP2_VOLTAGE_OFFSET, > + }, > + .voltdm = { > + .name = "core", > + }, > + }, > +}; > + > +#define OMAP3_NO_SCALABLE_VDD ARRAY_SIZE(omap3_vdd_info) Use of 'NO' here has already been commented on in earlier versions. Please use the _NUM or _NR suffix which is common. Same issue with the name of the variable that this is assigned to. > +/* TODO: OMAP4 register offsets */ > + > +/* > + * Default voltage controller settings. > + */ > +static struct omap_volt_vc_data vc_config = { > + .clksetup = 0xff, > + .voltsetup_time1 = 0xfff, > + .voltsetup_time2 = 0xfff, > + .voltoffset = 0xff, > + .voltsetup2 = 0xff, > + .vdd0_on = 0x30, /* 1.2v */ > + .vdd0_onlp = 0x20, /* 1.0v */ > + .vdd0_ret = 0x1e, /* 0.975v */ > + .vdd0_off = 0x00, /* 0.6v */ > + .vdd1_on = 0x2c, /* 1.15v */ > + .vdd1_onlp = 0x20, /* 1.0v */ > + .vdd1_ret = 0x1e, /* .975v */ > + .vdd1_off = 0x00, /* 0.6v */ > +}; > + > +/* > + * Default PMIC Data > + */ > +static struct omap_volt_pmic_info volt_pmic_info = { > + .slew_rate = 4000, > + .step_size = 12500, > +}; > + > +/* > + * Structures containing OMAP3430/OMAP3630 voltage supported and various > + * data associated with it per voltage domain basis. Smartreflex Ntarget > + * values are left as 0 as they have to be populated by smartreflex > + * driver after reading the efuse. > + */ > + > +/* VDD1 */ > +static struct omap_volt_data omap34xx_vdd1_volt_data[] = { > + {.volt_nominal = 975000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, > + {.volt_nominal = 1075000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, > + {.volt_nominal = 1200000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, > + {.volt_nominal = 1270000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, > + {.volt_nominal = 1350000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, > +}; > + > +static struct omap_volt_data omap36xx_vdd1_volt_data[] = { > + {.volt_nominal = 930000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, > + {.volt_nominal = 1100000, .sr_errminlimit = 0xF9, .vp_errgain = 0x16}, > + {.volt_nominal = 1260000, .sr_errminlimit = 0xFA, .vp_errgain = 0x23}, > + {.volt_nominal = 1350000, .sr_errminlimit = 0xFA, .vp_errgain = 0x27}, > +}; > + > +/* VDD2 */ > +static struct omap_volt_data omap34xx_vdd2_volt_data[] = { > + {.volt_nominal = 975000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, > + {.volt_nominal = 1050000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, > + {.volt_nominal = 1150000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, > +}; > + > +static struct omap_volt_data omap36xx_vdd2_volt_data[] = { > + {.volt_nominal = 930000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, > + {.volt_nominal = 1137500, .sr_errminlimit = 0xF9, .vp_errgain = 0x16}, > +}; > + > + > +/* By default VPFORCEUPDATE is the chosen method of voltage scaling */ > +static bool voltscale_vpforceupdate = true; > + > +static inline u32 voltage_read_reg(u8 offset) > +{ > + return prm_read_mod_reg(volt_mod, offset); > +} > + > +static inline void voltage_write_reg(u8 offset, u32 value) > +{ > + prm_write_mod_reg(value, volt_mod, offset); > +} > + > +static void vp_latch_vsel(struct omap_vdd_info *vdd) > +{ > + u32 vpconfig; > + unsigned long uvdc; > + char vsel; > + > + uvdc = omap_voltage_get_nom_volt(&vdd->voltdm); > + if (!uvdc) { > + pr_warning("%s: unable to find current voltage for vdd_%s\n", > + __func__, vdd->voltdm.name); > + return; > + } > + vsel = omap_twl_uv_to_vsel(uvdc); > + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); > + vpconfig &= ~(vdd->vp_reg.vpconfig_initvoltage_mask | > + vdd->vp_reg.vpconfig_initvdd); > + vpconfig |= vsel << vdd->vp_reg.vpconfig_initvoltage_shift; > + > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + > + /* Trigger initVDD value copy to voltage processor */ > + voltage_write_reg(vdd->vp_offs.vpconfig, > + (vpconfig | vdd->vp_reg.vpconfig_initvdd)); > + > + /* Clear initVDD copy trigger bit */ > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > +} > + > +/* OMAP3 specific voltage init functions */ > +/* > + * Intializes the voltage controller registers with the PMIC and board > + * specific parameters and voltage setup times for OMAP3. If the board > + * file does not populate the voltage controller parameters through > + * omap3_pm_init_vc, default values specified in vc_config is used. > + */ > +static void __init omap3_init_voltagecontroller(void) > +{ > + voltage_write_reg(OMAP3_PRM_VC_SMPS_SA_OFFSET, > + (OMAP3_SRI2C_SLAVE_ADDR << > + OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT) | > + (OMAP3_SRI2C_SLAVE_ADDR << > + OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT)); > + voltage_write_reg(OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET, > + (OMAP3_VDD2_SR_CONTROL_REG << OMAP3430_VOLRA1_SHIFT) | > + (OMAP3_VDD1_SR_CONTROL_REG << OMAP3430_VOLRA0_SHIFT)); > + voltage_write_reg(OMAP3_PRM_VC_CMD_VAL_0_OFFSET, > + (vc_config.vdd0_on << OMAP3430_VC_CMD_ON_SHIFT) | > + (vc_config.vdd0_onlp << OMAP3430_VC_CMD_ONLP_SHIFT) | > + (vc_config.vdd0_ret << OMAP3430_VC_CMD_RET_SHIFT) | > + (vc_config.vdd0_off << OMAP3430_VC_CMD_OFF_SHIFT)); > + voltage_write_reg(OMAP3_PRM_VC_CMD_VAL_1_OFFSET, > + (vc_config.vdd1_on << OMAP3430_VC_CMD_ON_SHIFT) | > + (vc_config.vdd1_onlp << OMAP3430_VC_CMD_ONLP_SHIFT) | > + (vc_config.vdd1_ret << OMAP3430_VC_CMD_RET_SHIFT) | > + (vc_config.vdd1_off << OMAP3430_VC_CMD_OFF_SHIFT)); > + voltage_write_reg(OMAP3_PRM_VC_CH_CONF_OFFSET, > + OMAP3430_CMD1_MASK | OMAP3430_RAV1_MASK); > + voltage_write_reg(OMAP3_PRM_VC_I2C_CFG_OFFSET, > + OMAP3430_MCODE_SHIFT | OMAP3430_HSEN_MASK); insert blank line when starting a new logical section, especially when its separated by a comment. There a lots of these to fixup. > + /* Write setup times */ > + voltage_write_reg(OMAP3_PRM_CLKSETUP_OFFSET, vc_config.clksetup); > + voltage_write_reg(OMAP3_PRM_VOLTSETUP1_OFFSET, > + (vc_config.voltsetup_time2 << > + OMAP3430_SETUP_TIME2_SHIFT) | > + (vc_config.voltsetup_time1 << > + OMAP3430_SETUP_TIME1_SHIFT)); > + voltage_write_reg(OMAP3_PRM_VOLTOFFSET_OFFSET, vc_config.voltoffset); > + voltage_write_reg(OMAP3_PRM_VOLTSETUP2_OFFSET, vc_config.voltsetup2); > +} > + > +/* Sets up all the VDD related info for OMAP3 */ > +static void __init omap3_vdd_data_configure(struct omap_vdd_info *vdd) > +{ > + unsigned long curr_volt; > + struct omap_volt_data *volt_data; > + struct clk *sys_ck; > + u32 sys_clk_speed, timeout_val, waittime; > + > + if (!strcmp(vdd->voltdm.name, "mpu")) { > + if (cpu_is_omap3630()) { > + vdd->vp_reg.vlimitto_vddmin = > + OMAP3630_VP1_VLIMITTO_VDDMIN; > + vdd->vp_reg.vlimitto_vddmax = > + OMAP3630_VP1_VLIMITTO_VDDMAX; > + vdd->volt_data = omap36xx_vdd1_volt_data; > + vdd->volt_data_count = > + ARRAY_SIZE(omap36xx_vdd1_volt_data); > + } else { > + vdd->vp_reg.vlimitto_vddmin = > + OMAP3430_VP1_VLIMITTO_VDDMIN; > + vdd->vp_reg.vlimitto_vddmax = > + OMAP3430_VP1_VLIMITTO_VDDMAX; > + vdd->volt_data = omap34xx_vdd1_volt_data; > + vdd->volt_data_count = > + ARRAY_SIZE(omap34xx_vdd1_volt_data); > + } > + vdd->volt_clk = clk_get(NULL, "dpll1_ck"); > + vdd->opp_dev = omap2_get_mpuss_device(); > + vdd->vp_reg.tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK; > + vdd->cmdval_reg = OMAP3_PRM_VC_CMD_VAL_0_OFFSET; > + vdd->vdd_sr_reg = OMAP3_VDD1_SR_CONTROL_REG; > + } else if (!strcmp(vdd->voltdm.name, "core")) { > + if (cpu_is_omap3630()) { > + vdd->vp_reg.vlimitto_vddmin = > + OMAP3630_VP2_VLIMITTO_VDDMIN; > + vdd->vp_reg.vlimitto_vddmax = > + OMAP3630_VP2_VLIMITTO_VDDMAX; > + vdd->volt_data = omap36xx_vdd2_volt_data; > + vdd->volt_data_count = > + ARRAY_SIZE(omap36xx_vdd2_volt_data); > + } else { > + vdd->vp_reg.vlimitto_vddmin = > + OMAP3430_VP2_VLIMITTO_VDDMIN; > + vdd->vp_reg.vlimitto_vddmax = > + OMAP3430_VP2_VLIMITTO_VDDMAX; > + vdd->volt_data = omap34xx_vdd2_volt_data; > + vdd->volt_data_count = > + ARRAY_SIZE(omap34xx_vdd2_volt_data); > + } > + vdd->volt_clk = clk_get(NULL, "l3_ick"); > + vdd->opp_dev = omap2_get_l3_device(); > + vdd->vp_reg.tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK; > + vdd->cmdval_reg = OMAP3_PRM_VC_CMD_VAL_1_OFFSET; > + vdd->vdd_sr_reg = OMAP3_VDD2_SR_CONTROL_REG; > + } else { > + pr_warning("%s: vdd_%s does not exisit in OMAP3\n", > + __func__, vdd->voltdm.name); > + return; > + } > + > + if (IS_ERR(vdd->volt_clk)) { > + pr_warning("%s: unable to get clk for vdd_%s\n", > + __func__, vdd->voltdm.name); > + return; > + } > + if (!vdd->opp_dev) { > + pr_warning("%s: unable to get the opp device for vdd_%s\n", > + __func__, vdd->voltdm.name); > + return; > + } > + > + curr_volt = omap_voltage_get_nom_volt(&vdd->voltdm); > + if (!curr_volt) { > + pr_warning("%s: unable to find current voltage for vdd_%s\n", > + __func__, vdd->voltdm.name); > + return; > + } > + > + volt_data = omap_voltage_get_voltdata(&vdd->voltdm, curr_volt); > + if (IS_ERR(volt_data)) { > + pr_warning("%s: Unable to get volt table for vdd_%s at init", > + __func__, vdd->voltdm.name); > + return; > + } > + /* > + * Sys clk rate is require to calculate vp timeout value and > + * smpswaittimemin and smpswaittimemax. > + */ > + sys_ck = clk_get(NULL, "sys_ck"); > + if (IS_ERR(sys_ck)) { > + pr_warning("%s: Could not get the sys clk to calculate" > + "various vdd_%s params\n", __func__, vdd->voltdm.name); > + return; > + } > + sys_clk_speed = clk_get_rate(sys_ck); > + clk_put(sys_ck); > + /* Divide to avoid overflow */ > + sys_clk_speed /= 1000; > + > + /* Nominal/Reset voltage of the VDD */ > + vdd->nominal_volt = 1200000; > + > + /* VPCONFIG bit fields */ > + vdd->vp_reg.vpconfig_erroroffset = (OMAP3_VP_CONFIG_ERROROFFSET << > + OMAP3430_ERROROFFSET_SHIFT); > + vdd->vp_reg.vpconfig_errorgain = volt_data->vp_errgain; > + vdd->vp_reg.vpconfig_errorgain_mask = OMAP3430_ERRORGAIN_MASK; > + vdd->vp_reg.vpconfig_errorgain_shift = OMAP3430_ERRORGAIN_SHIFT; > + vdd->vp_reg.vpconfig_initvoltage_shift = OMAP3430_INITVOLTAGE_SHIFT; > + vdd->vp_reg.vpconfig_initvoltage_mask = OMAP3430_INITVOLTAGE_MASK; > + vdd->vp_reg.vpconfig_timeouten = OMAP3430_TIMEOUTEN_MASK; > + vdd->vp_reg.vpconfig_initvdd = OMAP3430_INITVDD_MASK; > + vdd->vp_reg.vpconfig_forceupdate = OMAP3430_FORCEUPDATE_MASK; > + vdd->vp_reg.vpconfig_vpenable = OMAP3430_VPENABLE_MASK; > + > + /* VSTEPMIN VSTEPMAX bit fields */ > + waittime = ((volt_pmic_info.step_size / volt_pmic_info.slew_rate) * > + sys_clk_speed) / 1000; > + vdd->vp_reg.vstepmin_smpswaittimemin = waittime; > + vdd->vp_reg.vstepmax_smpswaittimemax = waittime; > + vdd->vp_reg.vstepmin_stepmin = OMAP3_VP_VSTEPMIN_VSTEPMIN; > + vdd->vp_reg.vstepmax_stepmax = OMAP3_VP_VSTEPMAX_VSTEPMAX; > + vdd->vp_reg.vstepmin_smpswaittimemin_shift = > + OMAP3430_SMPSWAITTIMEMIN_SHIFT; > + vdd->vp_reg.vstepmax_smpswaittimemax_shift = > + OMAP3430_SMPSWAITTIMEMAX_SHIFT; > + vdd->vp_reg.vstepmin_stepmin_shift = OMAP3430_VSTEPMIN_SHIFT; > + vdd->vp_reg.vstepmax_stepmax_shift = OMAP3430_VSTEPMAX_SHIFT; > + > + /* VLIMITTO bit fields */ > + timeout_val = (sys_clk_speed * OMAP3_VP_VLIMITTO_TIMEOUT_US) / 1000; > + vdd->vp_reg.vlimitto_timeout = timeout_val; > + vdd->vp_reg.vlimitto_vddmin_shift = OMAP3430_VDDMIN_SHIFT; > + vdd->vp_reg.vlimitto_vddmax_shift = OMAP3430_VDDMAX_SHIFT; > + vdd->vp_reg.vlimitto_timeout_shift = OMAP3430_TIMEOUT_SHIFT; > +} > + > +/* Generic voltage init functions */ > +static void __init init_voltageprocessor(struct omap_vdd_info *vdd) > +{ > + u32 vpconfig; > + > + vpconfig = vdd->vp_reg.vpconfig_erroroffset | > + (vdd->vp_reg.vpconfig_errorgain << > + vdd->vp_reg.vpconfig_errorgain_shift) | > + vdd->vp_reg.vpconfig_timeouten; > + > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + > + voltage_write_reg(vdd->vp_offs.vstepmin, > + (vdd->vp_reg.vstepmin_smpswaittimemin << > + vdd->vp_reg.vstepmin_smpswaittimemin_shift) | > + (vdd->vp_reg.vstepmin_stepmin << > + vdd->vp_reg.vstepmin_stepmin_shift)); > + > + voltage_write_reg(vdd->vp_offs.vstepmax, > + (vdd->vp_reg.vstepmax_smpswaittimemax << > + vdd->vp_reg.vstepmax_smpswaittimemax_shift) | > + (vdd->vp_reg.vstepmax_stepmax << > + vdd->vp_reg.vstepmax_stepmax_shift)); > + > + voltage_write_reg(vdd->vp_offs.vlimitto, > + (vdd->vp_reg.vlimitto_vddmax << > + vdd->vp_reg.vlimitto_vddmax_shift) | > + (vdd->vp_reg.vlimitto_vddmin << > + vdd->vp_reg.vlimitto_vddmin_shift) | > + (vdd->vp_reg.vlimitto_timeout << > + vdd->vp_reg.vlimitto_timeout_shift)); > + > + /* Set the init voltage */ > + vp_latch_vsel(vdd); > + > + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); > + /* Force update of voltage */ > + voltage_write_reg(vdd->vp_offs.vpconfig, > + (vpconfig | vdd->vp_reg.vpconfig_forceupdate)); > + /* Clear force bit */ > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > +} > + > +static void __init vdd_data_configure(struct omap_vdd_info *vdd) > +{ > + if (cpu_is_omap34xx()) > + omap3_vdd_data_configure(vdd); > +} > +static void __init init_voltagecontroller(void) > +{ > + if (cpu_is_omap34xx()) > + omap3_init_voltagecontroller(); > +} > + > +/* > + * vc_bypass_scale_voltage - VC bypass method of voltage scaling > + */ > +static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd, > + unsigned long target_volt) > +{ > + struct omap_volt_data *volt_data; > + u32 vc_bypass_value, vc_cmdval, vc_valid, vc_bypass_val_reg_offs; > + u32 vp_errgain_val, vc_cmd_on_mask; > + u32 loop_cnt = 0, retries_cnt = 0; > + u32 smps_steps = 0, smps_delay = 0; > + u8 vc_data_shift, vc_slaveaddr_shift, vc_regaddr_shift; > + u8 vc_cmd_on_shift; > + u8 target_vsel, current_vsel, sr_i2c_slave_addr; > + > + if (cpu_is_omap34xx()) { > + vc_cmd_on_shift = OMAP3430_VC_CMD_ON_SHIFT; > + vc_cmd_on_mask = OMAP3430_VC_CMD_ON_MASK; > + vc_data_shift = OMAP3430_DATA_SHIFT; > + vc_slaveaddr_shift = OMAP3430_SLAVEADDR_SHIFT; > + vc_regaddr_shift = OMAP3430_REGADDR_SHIFT; > + vc_valid = OMAP3430_VALID_MASK; > + vc_bypass_val_reg_offs = OMAP3_PRM_VC_BYPASS_VAL_OFFSET; > + sr_i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR; > + } else ? > + /* Get volt_data corresponding to target_volt */ > + volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt); > + if (IS_ERR(volt_data)) { > + /* > + * If a match is not found but the target voltage is > + * is the nominal vdd voltage allow scaling > + */ > + if (target_volt != vdd->nominal_volt) { > + pr_warning("%s: Unable to get volt table for vdd_%s" > + "during voltage scaling. Some really Wrong!", > + __func__, vdd->voltdm.name); > + return -ENODATA; > + } > + volt_data = NULL; > + } > + > + target_vsel = omap_twl_uv_to_vsel(target_volt); > + current_vsel = voltage_read_reg(vdd->vp_offs.voltage); > + smps_steps = abs(target_vsel - current_vsel); > + > + /* Setting the ON voltage to the new target voltage */ > + vc_cmdval = voltage_read_reg(vdd->cmdval_reg); > + vc_cmdval &= ~vc_cmd_on_mask; > + vc_cmdval |= (target_vsel << vc_cmd_on_shift); > + voltage_write_reg(vdd->cmdval_reg, vc_cmdval); > + > + /* Setting vp errorgain based on the voltage */ > + if (volt_data) { > + vp_errgain_val = voltage_read_reg(vdd->vp_offs.vpconfig); > + vdd->vp_reg.vpconfig_errorgain = volt_data->vp_errgain; > + vp_errgain_val &= ~vdd->vp_reg.vpconfig_errorgain_mask; > + vp_errgain_val |= vdd->vp_reg.vpconfig_errorgain << > + vdd->vp_reg.vpconfig_errorgain_shift; > + voltage_write_reg(vdd->vp_offs.vpconfig, vp_errgain_val); > + } > + > + vc_bypass_value = (target_vsel << vc_data_shift) | > + (vdd->vdd_sr_reg << vc_regaddr_shift) | > + (sr_i2c_slave_addr << vc_slaveaddr_shift); > + > + voltage_write_reg(vc_bypass_val_reg_offs, vc_bypass_value); > + > + voltage_write_reg(vc_bypass_val_reg_offs, vc_bypass_value | vc_valid); > + vc_bypass_value = voltage_read_reg(vc_bypass_val_reg_offs); > + > + while ((vc_bypass_value & vc_valid) != 0x0) { the '!= 0x0' is redundant > + loop_cnt++; > + if (retries_cnt > 10) { > + pr_warning("%s: Loop count exceeded in check SR I2C" > + "write during voltgae scaling\n", __func__); string is confusing > + return -ETIMEDOUT; > + } > + if (loop_cnt > 50) { > + retries_cnt++; > + loop_cnt = 0; > + udelay(10); > + } > + vc_bypass_value = voltage_read_reg(vc_bypass_val_reg_offs); > + } Where do the loop and retry count numbers come from? This section could use some comments describing the retry logic. > + /* SMPS slew rate / step size. 2us added as buffer. */ > + smps_delay = ((smps_steps * volt_pmic_info.step_size) / > + volt_pmic_info.slew_rate) + 2; > + udelay(smps_delay); > + return 0; > +} > + > +/* VP force update method of voltage scaling */ > +static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd, > + unsigned long target_volt) > +{ > + struct omap_volt_data *volt_data; > + u32 vc_cmd_on_mask, vc_cmdval, vpconfig; > + u32 smps_steps = 0, smps_delay = 0; > + int timeout = 0; > + u8 target_vsel, current_vsel; > + u8 vc_cmd_on_shift; > + u8 prm_irqst_reg_offs, ocp_mod; > + > + if (cpu_is_omap34xx()) { > + vc_cmd_on_shift = OMAP3430_VC_CMD_ON_SHIFT; > + vc_cmd_on_mask = OMAP3430_VC_CMD_ON_MASK; > + prm_irqst_reg_offs = OMAP3_PRM_IRQSTATUS_MPU_OFFSET; > + ocp_mod = OCP_MOD; > + } else? > + /* Get volt_data corresponding to the target_volt */ > + volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt); > + if (IS_ERR(volt_data)) { > + /* > + * If a match is not found but the target voltage is > + * is the nominal vdd voltage allow scaling > + */ > + if (target_volt != vdd->nominal_volt) { > + pr_warning("%s: Unable to get voltage table for vdd_%s" > + "during voltage scaling. Some really Wrong!", > + __func__, vdd->voltdm.name); > + return -ENODATA; > + } > + volt_data = NULL; > + } > + > + target_vsel = omap_twl_uv_to_vsel(target_volt); > + current_vsel = voltage_read_reg(vdd->vp_offs.voltage); > + smps_steps = abs(target_vsel - current_vsel); > + > + /* Setting the ON voltage to the new target voltage */ > + vc_cmdval = voltage_read_reg(vdd->cmdval_reg); > + vc_cmdval &= ~vc_cmd_on_mask; > + vc_cmdval |= (target_vsel << vc_cmd_on_shift); > + voltage_write_reg(vdd->cmdval_reg, vc_cmdval); > + > + /* Getting vp errorgain based on the voltage */ > + if (volt_data) > + vdd->vp_reg.vpconfig_errorgain = > + volt_data->vp_errgain; > + /* > + * Clear all pending TransactionDone interrupt/status. Typical latency > + * is <3us > + */ > + while (timeout++ < VP_TRANXDONE_TIMEOUT) { > + prm_write_mod_reg(vdd->vp_reg.tranxdone_status, > + ocp_mod, prm_irqst_reg_offs); > + if (!(prm_read_mod_reg(ocp_mod, prm_irqst_reg_offs) & > + vdd->vp_reg.tranxdone_status)) > + break; > + udelay(1); > + } I'm a little worried about this racing with the PRCM interrupt handler in pm34xx.c. I need to be convinced that this would not interfere with that handler. Even better would be to handle this in an interrupt handler and get rid of the polling. Is there a reason this is done with polling instead of handled in interrupt? > + if (timeout >= VP_TRANXDONE_TIMEOUT) { > + pr_warning("%s: vdd_%s TRANXDONE timeout exceeded." > + "Voltage change aborted", __func__, vdd->voltdm.name); > + return -ETIMEDOUT; > + } > + /* Configure for VP-Force Update */ > + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); > + vpconfig &= ~(vdd->vp_reg.vpconfig_initvdd | > + vdd->vp_reg.vpconfig_forceupdate | > + vdd->vp_reg.vpconfig_initvoltage_mask | > + vdd->vp_reg.vpconfig_errorgain_mask); > + vpconfig |= ((target_vsel << > + vdd->vp_reg.vpconfig_initvoltage_shift) | > + (vdd->vp_reg.vpconfig_errorgain << > + vdd->vp_reg.vpconfig_errorgain_shift)); > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + > + /* Trigger initVDD value copy to voltage processor */ > + vpconfig |= vdd->vp_reg.vpconfig_initvdd; > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + > + /* Force update of voltage */ > + vpconfig |= vdd->vp_reg.vpconfig_forceupdate; > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + > + timeout = 0; > + /* > + * Wait for TransactionDone. Typical latency is <200us. > + * Depends on SMPSWAITTIMEMIN/MAX and voltage change > + */ > + omap_test_timeout((prm_read_mod_reg(ocp_mod, prm_irqst_reg_offs) & > + vdd->vp_reg.tranxdone_status), > + VP_TRANXDONE_TIMEOUT, timeout); > + > + if (timeout >= VP_TRANXDONE_TIMEOUT) > + pr_err("%s: vdd_%s TRANXDONE timeout exceeded." > + "TRANXDONE never got set after the voltage update\n", > + __func__, vdd->voltdm.name); > + /* > + * Wait for voltage to settle with SW wait-loop. > + * SMPS slew rate / step size. 2us added as buffer. > + */ > + smps_delay = ((smps_steps * volt_pmic_info.step_size) / > + volt_pmic_info.slew_rate) + 2; > + udelay(smps_delay); > + > + /* > + * Disable TransactionDone interrupt , clear all status, clear > + * control registers > + */ > + timeout = 0; > + while (timeout++ < VP_TRANXDONE_TIMEOUT) { > + prm_write_mod_reg(vdd->vp_reg.tranxdone_status, > + ocp_mod, prm_irqst_reg_offs); > + if (!(prm_read_mod_reg(ocp_mod, prm_irqst_reg_offs) & > + vdd->vp_reg.tranxdone_status)) > + break; > + udelay(1); > + } > + if (timeout >= VP_TRANXDONE_TIMEOUT) > + pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying" > + "to clear the TRANXDONE status\n", > + __func__, vdd->voltdm.name); > + > + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); > + /* Clear initVDD copy trigger bit */ > + vpconfig &= ~vdd->vp_reg.vpconfig_initvdd;; > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + /* Clear force bit */ > + vpconfig &= ~vdd->vp_reg.vpconfig_forceupdate; > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + > + return 0; > +} > + > +/* Public functions */ > +/** > + * omap_voltage_get_nom_volt : Gets the current non-auto-compensated voltage > + * @voltdm : pointer to the VDD for which current voltage info is needed > + * > + * API to get the current non-auto-compensated voltage for a VDD. > + * Returns 0 in case of error else returns the current voltage for the VDD. > + */ > +unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm) > +{ > + struct omap_opp *opp; > + struct omap_vdd_info *vdd; > + unsigned long freq; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return 0; > + } > + > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); > + > + freq = vdd->volt_clk->rate; > + opp = opp_find_freq_ceil(vdd->opp_dev, &freq); > + if (IS_ERR(opp)) { > + pr_warning("%s: Unable to find OPP for vdd_%s freq%ld\n", > + __func__, voltdm->name, freq); > + return 0; > + } > + > + /* > + * Use higher freq voltage even if an exact match is not available > + * we are probably masking a clock framework bug, so warn > + */ > + if (unlikely(freq != vdd->volt_clk->rate)) > + pr_warning("%s: Available freq %ld != dpll freq %ld.\n", > + __func__, freq, vdd->volt_clk->rate); > + > + return opp_get_voltage(opp); > +} > + > +/** > + * omap_vp_get_curr_volt : API to get the current vp voltage. > + * @voltdm: pointer to the VDD. > + * > + * This API returns the current voltage for the specified voltage processor > + */ > +unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm) > +{ > + struct omap_vdd_info *vdd; > + u8 curr_vsel; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return 0; > + } > + > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); > + > + curr_vsel = voltage_read_reg(vdd->vp_offs.voltage); > + return omap_twl_vsel_to_uv(curr_vsel); > +} > + > +/** > + * omap_vp_enable : API to enable a particular VP > + * @voltdm: pointer to the VDD whose VP is to be enabled. > + * > + * This API enables a particular voltage processor. Needed by the smartreflex > + * class drivers. > + */ > +void omap_vp_enable(struct voltagedomain *voltdm) > +{ > + struct omap_vdd_info *vdd; > + u32 vpconfig; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return; > + } > + > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); > + > + /* If VP is already enabled, do nothing. Return */ > + if (voltage_read_reg(vdd->vp_offs.vpconfig) & > + vdd->vp_reg.vpconfig_vpenable) > + return; > + /* > + * This latching is required only if VC bypass method is used for > + * voltage scaling during dvfs. > + */ > + if (!voltscale_vpforceupdate) > + vp_latch_vsel(vdd); > + > + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); > + /* Enable VP */ > + voltage_write_reg(vdd->vp_offs.vpconfig, > + vpconfig | vdd->vp_reg.vpconfig_vpenable); > +} > + > +/** > + * omap_vp_disable : API to disable a particular VP > + * @voltdm: pointer to the VDD whose VP is to be disabled. > + * > + * This API disables a particular voltage processor. Needed by the smartreflex > + * class drivers. > + */ > +void omap_vp_disable(struct voltagedomain *voltdm) > +{ > + struct omap_vdd_info *vdd; > + u32 vpconfig; > + int timeout; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return; > + } > + > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); > + > + /* If VP is already disabled, do nothing. Return */ > + if (!(voltage_read_reg(vdd->vp_offs.vpconfig) & > + vdd->vp_reg.vpconfig_vpenable)) { > + pr_warning("%s: Trying to disable VP for vdd_%s when" > + "it is already disabled\n", __func__, voltdm->name); > + return; > + } > + > + /* Disable VP */ > + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); > + vpconfig &= ~vdd->vp_reg.vpconfig_vpenable; > + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); > + > + /* > + * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us > + */ > + omap_test_timeout((voltage_read_reg(vdd->vp_offs.vstatus)), > + VP_IDLE_TIMEOUT, timeout); > + > + if (timeout >= VP_IDLE_TIMEOUT) > + pr_warning("%s: vdd_%s idle timedout\n", > + __func__, voltdm->name); > + return; > +} > + > +/** > + * omap_voltage_scale_vdd : API to scale voltage of a particular voltage domain. > + * @voltdm: pointer to the VDD which is to be scaled. > + * @target_volt : The target voltage of the voltage domain > + * > + * This API should be called by the kernel to do the voltage scaling > + * for a particular voltage domain during dvfs or any other situation. > + */ > +int omap_voltage_scale_vdd(struct voltagedomain *voltdm, > + unsigned long target_volt) > +{ > + struct omap_vdd_info *vdd; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return -EINVAL; > + } > + > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); > + > + if (voltscale_vpforceupdate) > + return vp_forceupdate_scale_voltage(vdd, target_volt); > + else > + return vc_bypass_scale_voltage(vdd, target_volt); > +} > + > + > + > +/** > + * omap_voltage_reset : Resets the voltage of a particular voltage domain > + * to that of the current OPP. > + * @voltdm: pointer to the VDD whose voltage is to be reset. > + * > + * This API finds out the correct voltage the voltage domain is supposed > + * to be at and resets the voltage to that level. Should be used expecially > + * while disabling any voltage compensation modules. > + */ > +void omap_voltage_reset(struct voltagedomain *voltdm) > +{ > + unsigned long target_uvdc; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return; > + } > + > + target_uvdc = omap_voltage_get_nom_volt(voltdm); > + if (!target_uvdc) { > + pr_err("%s: unable to find current voltage for vdd_%s\n", > + __func__, voltdm->name); > + return; > + } > + omap_voltage_scale_vdd(voltdm, target_uvdc); > +} > + > +/** > + * omap_change_voltscale_method : API to change the voltage scaling method. > + * @voltscale_method : the method to be used for voltage scaling. > + * > + * This API can be used by the board files to change the method of voltage > + * scaling between vpforceupdate and vcbypass. The parameter values are > + * defined in voltage.h > + */ > +void omap_change_voltscale_method(int voltscale_method) > +{ > + switch (voltscale_method) { > + case VOLTSCALE_VPFORCEUPDATE: > + voltscale_vpforceupdate = true; > + return; > + case VOLTSCALE_VCBYPASS: > + voltscale_vpforceupdate = false; > + return; > + default: > + pr_warning("%s: Trying to change the method of voltage scaling" > + "to an unsupported one!\n", __func__); > + } > +} > + > +/** > + * omap_voltage_init_vc - polpulates vc_config with values specified in > + * board file > + * @setup_vc - the structure with various vc parameters > + * > + * Updates vc_config with the voltage setup times and other parameters as > + * specified in setup_vc. vc_config is later used in init_voltagecontroller > + * to initialize the voltage controller registers. Board files should call > + * this function with the correct volatge settings corresponding > + * the particular PMIC and chip. > + */ > +void __init omap_voltage_init_vc(struct omap_volt_vc_data *setup_vc) > +{ > + if (!setup_vc) > + return; > + > + vc_config.clksetup = setup_vc->clksetup; > + vc_config.voltsetup_time1 = setup_vc->voltsetup_time1; > + vc_config.voltsetup_time2 = setup_vc->voltsetup_time2; > + vc_config.voltoffset = setup_vc->voltoffset; > + vc_config.voltsetup2 = setup_vc->voltsetup2; > + vc_config.vdd0_on = setup_vc->vdd0_on; > + vc_config.vdd0_onlp = setup_vc->vdd0_onlp; > + vc_config.vdd0_ret = setup_vc->vdd0_ret; > + vc_config.vdd0_off = setup_vc->vdd0_off; > + vc_config.vdd1_on = setup_vc->vdd1_on; > + vc_config.vdd1_onlp = setup_vc->vdd1_onlp; > + vc_config.vdd1_ret = setup_vc->vdd1_ret; > + vc_config.vdd1_off = setup_vc->vdd1_off; > +} > + > +/** > + * omap_voltage_get_volttable : API to get the voltage table associated with a > + * particular voltage domain. > + * > + * @voltdm: pointer to the VDD for which the voltage table is required > + * @volt_data : the voltage table for the particular vdd which is to be > + * populated by this API > + * This API populates the voltage table associated with a VDD into the > + * passed parameter pointer. Returns the count of distinct voltages > + * supported by this vdd. > + * > + */ > +int omap_voltage_get_volttable(struct voltagedomain *voltdm, > + struct omap_volt_data **volt_data) > +{ > + struct omap_vdd_info *vdd; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return 0; > + } > + > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); > + > + *volt_data = vdd->volt_data; > + return vdd->volt_data_count; > +} > + > +/** > + * omap_voltage_get_voltdata : API to get the voltage table entry for a > + * particular voltage > + * @voltdm: pointer to the VDD whose voltage table has to be searched > + * @volt : the voltage to be searched in the voltage table > + * > + * This API searches through the voltage table for the required voltage > + * domain and tries to find a matching entry for the passed voltage volt. > + * If a matching entry is found volt_data is populated with that entry. > + * This API searches only through the non-compensated voltages int the > + * voltage table. > + * Returns pointer to the voltage table entry corresponding to volt on > + * sucess. Returns -ENODATA if no voltage table exisits for the passed voltage > + * domain or if there is no matching entry. > + */ > +struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, > + unsigned long volt) > +{ > + struct omap_vdd_info *vdd; > + int i; > + > + if (!voltdm || IS_ERR(voltdm)) { > + pr_warning("%s: VDD specified does not exist!\n", __func__); > + return ERR_PTR(-EINVAL); > + } > + > + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); > + > + if (!vdd->volt_data) { > + pr_warning("%s: voltage table does not exist for vdd_%s\n", > + __func__, voltdm->name); > + return ERR_PTR(-ENODATA); > + } > + > + for (i = 0; i < vdd->volt_data_count; i++) { > + if (vdd->volt_data[i].volt_nominal == volt) > + return &vdd->volt_data[i]; > + } > + > + pr_notice("%s: Unable to match the current voltage with the voltage" > + "table for vdd_%s\n", __func__, voltdm->name); > + > + return ERR_PTR(-ENODATA); > +} > + > +/** > + * omap_voltage_register_pmic : API to register PMIC specific data > + * @pmic_info : the structure containing pmic info > + * > + * This API is to be called by the borad file to specify the pmic specific > + * info as present in omap_volt_pmic_info structure. A default pmic info > + * table is maintained in the driver volt_pmic_info. If the board file do > + * not override the default table using this API, the default values wiil > + * be used in the driver. > + */ > +void omap_voltage_register_pmic(struct omap_volt_pmic_info *pmic_info) > +{ > + volt_pmic_info.slew_rate = pmic_info->slew_rate; > + volt_pmic_info.step_size = pmic_info->step_size; > +} > + > +/** > + * omap_voltage_domain_get : API to get the voltage domain pointer > + * @name : Name of the voltage domain > + * > + * This API looks up in the global vdd_info struct for the > + * existence of voltage domain <name>. If it exists, the API returns > + * a pointer to the voltage domain structure corresponding to the > + * VDD<name>. Else retuns error pointer. > + */ > +struct voltagedomain *omap_voltage_domain_get(char *name) > +{ > + int i; > + > + if (!vdd_info) { > + pr_err("%s: Voltage driver init not yet happened.Faulting!\n", > + __func__); > + return ERR_PTR(-EINVAL); > + } > + > + if (!name) { > + pr_err("%s: No name to get the votage domain!\n", __func__); > + return ERR_PTR(-EINVAL); > + } > + > + for (i = 0; i < no_scalable_vdd; i++) { > + if (!(strcmp(name, vdd_info[i].voltdm.name))) > + return &vdd_info[i].voltdm; > + } > + > + return ERR_PTR(-EINVAL); > +} > + > +/** > + * omap_voltage_init : Volatage init API which does VP and VC init. > + */ > +static int __init omap_voltage_init(void) > +{ > + int i; > + > + if (!cpu_is_omap34xx()) { > + pr_warning("%s: voltage driver support not added\n", __func__); > + return 0; > + } This is redundant, and doesn't need a warning. > + if (cpu_is_omap34xx()) { > + volt_mod = OMAP3430_GR_MOD; > + vdd_info = omap3_vdd_info; > + no_scalable_vdd = OMAP3_NO_SCALABLE_VDD; > + } rather, just 'else return 0' here, and it makes it cleaner for adding OMAP4 support. > + init_voltagecontroller(); > + for (i = 0; i < no_scalable_vdd; i++) { > + vdd_data_configure(&vdd_info[i]); > + init_voltageprocessor(&vdd_info[i]); > + } > + return 0; > +} > +device_initcall(omap_voltage_init); > diff --git a/arch/arm/plat-omap/include/plat/voltage.h b/arch/arm/plat-omap/include/plat/voltage.h > new file mode 100644 > index 0000000..fc9778b > --- /dev/null > +++ b/arch/arm/plat-omap/include/plat/voltage.h > @@ -0,0 +1,132 @@ > +/* > + * OMAP Voltage Management Routines > + * > + * Author: Thara Gopinath <thara@xxxxxx> > + * > + * Copyright (C) 2009 Texas Instruments, Inc. > + * Thara Gopinath <thara@xxxxxx> > + * > + * 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. > + */ > + > +#ifndef __ARCH_ARM_MACH_OMAP2_VOLTAGE_H > +#define __ARCH_ARM_MACH_OMAP2_VOLTAGE_H > + > +#define VOLTSCALE_VPFORCEUPDATE 1 > +#define VOLTSCALE_VCBYPASS 2 > + > +/* Voltage SR Parameters for OMAP3*/ > +#define OMAP3_SRI2C_SLAVE_ADDR 0x12 > +#define OMAP3_VDD1_SR_CONTROL_REG 0x00 > +#define OMAP3_VDD2_SR_CONTROL_REG 0x01 > + > +/* > + * Omap3 VP register specific values. Maybe these need to come from > + * board file or PMIC data structure > + */ > +#define OMAP3_VP_CONFIG_ERROROFFSET 0x00 > +#define OMAP3_VP_VSTEPMIN_SMPSWAITTIMEMIN 0x3C > +#define OMAP3_VP_VSTEPMIN_VSTEPMIN 0x1 > +#define OMAP3_VP_VSTEPMAX_SMPSWAITTIMEMAX 0x3C > +#define OMAP3_VP_VSTEPMAX_VSTEPMAX 0x04 > +#define OMAP3_VP_VLIMITTO_TIMEOUT_US 0x200 > + > +/* > + * Omap3430 specific VP register values. Maybe these need to come from > + * board file or PMIC data structure > + */ > +#define OMAP3430_VP1_VLIMITTO_VDDMIN 0x14 > +#define OMAP3430_VP1_VLIMITTO_VDDMAX 0x42 > +#define OMAP3430_VP2_VLIMITTO_VDDMAX 0x2C > +#define OMAP3430_VP2_VLIMITTO_VDDMIN 0x18 > + > +/* > + * Omap3630 specific VP register values. Maybe these need to come from > + * board file or PMIC data structure > + */ > +#define OMAP3630_VP1_VLIMITTO_VDDMIN 0x18 > +#define OMAP3630_VP1_VLIMITTO_VDDMAX 0x3C > +#define OMAP3630_VP2_VLIMITTO_VDDMIN 0x18 > +#define OMAP3630_VP2_VLIMITTO_VDDMAX 0x30 > + > +/* TODO OMAP4 VP register values if the same file is used for OMAP4*/ > +/** > + * voltagedomain - omap voltage domain global structure > + * @name : Name of the voltage domain which can be used as a unique > + * identifier. > + */ > +struct voltagedomain { > + char *name; > +}; > + > +/** > + * omap_volt_data - Omap voltage specific data. > + * @voltage_nominal : The possible voltage value in uV > + * @sr_nvalue : Smartreflex N target value at voltage <voltage> > + * @sr_errminlimit : Error min limit value for smartreflex. This value > + * differs at differnet opp and thus is linked > + * with voltage. > + * @vp_errorgain : Error gain value for the voltage processor. This > + * field also differs according to the voltage/opp. > + */ > +struct omap_volt_data { > + u32 volt_nominal; > + u32 sr_nvalue; > + u8 sr_errminlimit; > + u8 vp_errgain; > +}; > + > +/** > + * omap_volt_pmic_info - PMIC specific data required by the voltage driver. > + * @slew_rate : PMIC slew rate (in uv/us) > + * @step_size : PMIC voltage step size (in uv) > + */ > +struct omap_volt_pmic_info { > + int slew_rate; > + int step_size; > +}; > + > +/* Various voltage controller related info */ > +struct omap_volt_vc_data { > + u16 clksetup; > + u16 voltsetup_time1; > + u16 voltsetup_time2; > + u16 voltoffset; > + u16 voltsetup2; > +/* PRM_VC_CMD_VAL_0 specific bits */ indent > + u16 vdd0_on; > + u16 vdd0_onlp; > + u16 vdd0_ret; > + u16 vdd0_off; > +/* PRM_VC_CMD_VAL_1 specific bits */ indent > + u16 vdd1_on; > + u16 vdd1_onlp; > + u16 vdd1_ret; > + u16 vdd1_off; > +}; > + > +struct voltagedomain *omap_voltage_domain_get(char *name); > +unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm); > +void omap_vp_enable(struct voltagedomain *voltdm); > +void omap_vp_disable(struct voltagedomain *voltdm); > +int omap_voltage_scale_vdd(struct voltagedomain *voltdm, > + unsigned long target_volt); > +void omap_voltage_reset(struct voltagedomain *voltdm); > +int omap_voltage_get_volttable(struct voltagedomain *voltdm, > + struct omap_volt_data **volt_data); > +struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, > + unsigned long volt); > +void omap_voltage_register_pmic(struct omap_volt_pmic_info *pmic_info); > +unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm); > +#ifdef CONFIG_PM > +void omap_voltage_init_vc(struct omap_volt_vc_data *setup_vc); > +void omap_change_voltscale_method(int voltscale_method); > +#else > +static inline void omap_voltage_init_vc(struct omap_volt_vc_data *setup_vc) {} > +static inline void omap_change_voltscale_method(int voltscale_method) {} > +#endif > + > +#endif Kevin -- 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