>>-----Original Message----- >>From: Kevin Hilman [mailto:khilman@xxxxxxxxxxxxxxxxxxx] >>Sent: Wednesday, August 25, 2010 5:32 AM >>To: Gopinath, Thara >>Cc: linux-omap@xxxxxxxxxxxxxxx; paul@xxxxxxxxx; Cousson, Benoit; Sripathy, Vishwanath; Sawant, Anand; >>Derrick, David >>Subject: Re: [PATCHv2 2/8] OMAP3: PM: Adding voltage driver support for OMAP3 >> >>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. Thanks for the review. >> >>> --- >>> 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. Yep.. Will fix this one >> >>> +/* 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. Ok.. Will implement this >> >>> + /* 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 ? else this should return. I will update it >> >>> + /* 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 Ok will take this in >> >>> + 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 Will fix this. >> >>> + 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. Hmmm... I can try putting in some comments. I had actually taken in This piece of code from the older version of smartreflex.c. I haven't changed anything in this function. Hence all the logic and errors in the old code exist. Btw vc_bypass is not the official voltage scaling method supported. >> >>> + /* 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? else this should return. I will update it >> >>> + /* 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? Yes this will not race as the TRANXDONE is not enabled in the PRM_IRQENABLE register. Intention behind keeping this in polling mode is to avoid the delays/complications in getting an interrupt, calling a handler and updating the prcm interrupt handler to call a callback etc. This is rather short and sweet checking of the status. >> >>> + 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. Ok >> >>> + 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. Ok >> >>> + 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 Will fix the indent issues >> >>> + 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