@@ -35,6 +35,7 @@
#include "cm.h"
#include "pm.h"
#include "prm-regbits-34xx.h"
+#include "smartreflex.h"
int omap2_pm_debug;
@@ -617,6 +618,8 @@ static int __init pm_dbg_init(void)
&voltage_off_while_idle,
&pm_dbg_option_fops);
+ (void)sr_debugfs_create_entries(d);
+
pm_dbg_init_done = 1;
return 0;
@@ -292,17 +292,23 @@ static int program_opp(int res, struct omap_opp *opp, int target_level,
else
raise = 0;
+#ifdef CONFIG_OMAP_SMARTREFLEX
+ sr_vp_disable_both(t_opp, c_opp);
+#endif
for (i = 0; i < 2; i++) {
if (i == raise)
ret = program_opp_freq(res, target_level,
current_level);
#ifdef CONFIG_OMAP_SMARTREFLEX
else
- sr_voltagescale_vcbypass(t_opp, c_opp,
+ sr_voltage_set(t_opp, c_opp,
opp[target_level].vsel,
opp[current_level].vsel);
#endif
}
+#ifdef CONFIG_OMAP_SMARTREFLEX
+ sr_vp_enable_both(t_opp, c_opp);
+#endif
return ret;
}
new file mode 100644
@@ -0,0 +1,1434 @@
+/*
+ * linux/arch/arm/mach-omap3/smartreflex.c
+ *
+ * OMAP34XX SmartReflex Voltage Control
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ * Nishanth Menon
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Kalle Jokiniemi
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Lesly A M <x0080970@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+
+#include <mach/omap34xx.h>
+#include <mach/control.h>
+#include <mach/clock.h>
+#include <mach/omap-pm.h>
+#include <mach/resource.h>
+#include <mach/powerdomain.h>
+
+#include "prm.h"
+#include "smartreflex.h"
+#include "prm-regbits-34xx.h"
+
+/* MCUDISACK is expected to happen within 1uSec. */
+#define COUNT_TIMEOUT_MCUDISACK 200
+
+/* VPINIDLE is expected to happen within 100uSec. Typical is 2uSec */
+#define COUNT_TIMEOUT_VPINIDLE 200
+
+/* Time taken for setting the device - worst case as FS I2C
+ * Depends on SMPSWAITIME MIN/MAX Typical is 200uSec
+ */
+#define COUNT_TIMEOUT_TRANSDONE_SET 400
+
+/* Time to clear out multiple transdone events typical is 3uSec */
+#define COUNT_TIMEOUT_TRANSDONE_CLR 50
+
+/* Time For VCBypass mode for TWL4030 derivative chip. */
+#define COUNT_TIMEOUT_TWL4030_VCBYPASS 500
+
+/* How many retries to do for I2C errors seen on bus for Forceupdate? */
+#define COUNT_RETRY_SMPSNOACK 4
+
+#define SR_REGADDR(offset) (sr->srbase_addr + (offset))
+
+/* Which function to use for setting voltage */
+#ifdef CONFIG_OMAP_VC_BYPASS_UPDATE
+#define SR_CHOSEN_VOLTAGE_UPDATE_MECH sr_vc_bypass
+#else
+#define SR_CHOSEN_VOLTAGE_UPDATE_MECH sr_vp_forceupdate
+#endif
+
+/* Structure for Voltage processor */
+struct omap_sr_vp {
+ /* Store the commonly used register offsets.
+ * this saves a if condition decision
+ */
+ u16 prm_vpx_status_offset;
+ u16 prm_vpx_config_offset;
+ u16 prm_vpx_stepmin_offset;
+ u16 prm_vpx_stepmax_offset;
+ u16 prm_vpx_limito_offset;
+ /* Store the defaults
+ * allowing us to save OCP read
+ * operation
+ */
+ u32 vpconfig_value;
+ u32 vpstepmin_value;
+ u32 vpstepmax_value;
+ u32 vplimito_value;
+ u32 vpenable_mask;
+ u32 irqmask_trans_done;
+ u32 irqmask_smps_noack;
+};
+
+/* Structure for Smart Reflex */
+struct omap_sr {
+ u8 srid;
+ /* SR activity marker */
+ u8 is_sr_reset;
+ u8 is_autocomp_active;
+ u32 req_opp_no;
+ u32 opp_nvalue[5];
+ u32 sr_config_value;
+ u32 sr_errconfig_value;
+ struct clk *fclk;
+ struct clk *iclk;
+ void __iomem *srbase_addr;
+ /* Lock for maintaining SR+VP programming sequence atomicity */
+ /* Voltage processor for the specific SR module */
+ struct omap_sr_vp vp;
+};
+
+/* Smart Reflex 1 structure */
+static struct omap_sr sr1 = {
+ /* *INDENT-OFF* */
+ .srid = SR1,
+ .is_sr_reset = 1,
+ .is_autocomp_active = 0,
+ .srbase_addr = (void *)OMAP34XX_SR1_BASE,
+ .vp = {
+ .prm_vpx_status_offset = OMAP3_PRM_VP1_STATUS_OFFSET,
+ .prm_vpx_config_offset = OMAP3_PRM_VP1_CONFIG_OFFSET,
+ .prm_vpx_stepmin_offset = OMAP3_PRM_VP1_VSTEPMIN_OFFSET,
+ .prm_vpx_stepmax_offset = OMAP3_PRM_VP1_VSTEPMAX_OFFSET,
+ .prm_vpx_limito_offset = OMAP3_PRM_VP1_VLIMITTO_OFFSET,
+ .vpconfig_value = PRM_VP1_CONFIG_ERROROFFSET |
+ PRM_VP1_CONFIG_TIMEOUTEN,
+ .vpstepmin_value = PRM_VP1_VSTEPMIN_SMPSWAITTIMEMIN |
+ PRM_VP1_VSTEPMIN_VSTEPMIN,
+ .vpstepmax_value = PRM_VP1_VSTEPMAX_SMPSWAITTIMEMAX |
+ PRM_VP1_VSTEPMAX_VSTEPMAX,
+ .vplimito_value = PRM_VP1_VLIMITTO_VDDMAX |
+ PRM_VP1_VLIMITTO_VDDMIN,
+ .vpenable_mask = PRM_VP1_CONFIG_VPENABLE,
+ .irqmask_trans_done = VP1_IRQMASK_TRANSDONE,
+ .irqmask_smps_noack = OMAP3430_VP1_NOSMPSACK_ST,
+ },
+ /* *INDENT-ON* */
+};
+
+/* Smart Reflex 2 structure */
+static struct omap_sr sr2 = {
+ /* *INDENT-OFF* */
+ .srid = SR2,
+ .is_sr_reset = 1,
+ .is_autocomp_active = 0,
+ .srbase_addr = (void *)OMAP34XX_SR2_BASE,
+ .vp = {
+ .prm_vpx_status_offset = OMAP3_PRM_VP2_STATUS_OFFSET,
+ .prm_vpx_config_offset = OMAP3_PRM_VP2_CONFIG_OFFSET,
+ .prm_vpx_stepmin_offset = OMAP3_PRM_VP2_VSTEPMIN_OFFSET,
+ .prm_vpx_stepmax_offset = OMAP3_PRM_VP2_VSTEPMAX_OFFSET,
+ .prm_vpx_limito_offset = OMAP3_PRM_VP2_VLIMITTO_OFFSET,
+ .vpconfig_value = PRM_VP2_CONFIG_ERROROFFSET |
+ PRM_VP2_CONFIG_TIMEOUTEN,
+ .vpstepmin_value = PRM_VP2_VSTEPMIN_SMPSWAITTIMEMIN |
+ PRM_VP2_VSTEPMIN_VSTEPMIN,
+ .vpstepmax_value = PRM_VP2_VSTEPMAX_SMPSWAITTIMEMAX |
+ PRM_VP2_VSTEPMAX_VSTEPMAX,
+ .vplimito_value = PRM_VP2_VLIMITTO_VDDMAX |
+ PRM_VP2_VLIMITTO_VDDMIN,
+ .vpenable_mask = PRM_VP2_CONFIG_VPENABLE,
+ .irqmask_trans_done = VP2_IRQMASK_TRANSDONE,
+ .irqmask_smps_noack = OMAP3430_VP1_NOSMPSACK_ST,
+ },
+ /* *INDENT-ON* */
+};
+
+/****************** SMART REFLEX DEBUGFS ENTRIES *************************/
+
+#ifdef __SR_DEBUG
+
+/**
+ * @brief sr_debugfs_set - set variable with value
+ *
+ * @param data - variable pointer
+ * @param val - value to set
+ *
+ * @return - 0
+ */
+static int sr_debugfs_set(void *data, u64 val)
+{
+ u32 *option = data;
+
+ *option = val;
+
+ return 0;
+}
+
+/**
+ * @brief sr_debugfs_get - setup the params
+ *
+ * @param data - variable to set
+ * @param val - value to return
+ *
+ * @return - 0
+ */
+static int sr_debugfs_get(void *data, u64 * val)
+{
+ u32 *option = data;
+
+ *val = *option;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sr_debugfs_option_fops, sr_debugfs_get, sr_debugfs_set,
+ "%llu\n");
+
+/**
+ * @brief sr_debugfs_create_entries - create the Smart Reflex entries - called
+ * from pm-debug
+ *
+ * @param d - parent directory tree
+ *
+ * @return 0 if all ok, else returns with error
+ */
+int sr_debugfs_create_entries(struct dentry *d)
+{
+ struct dentry *sr_dir;
+ struct dentry *sr_sub_dir;
+ struct omap_sr *sr_list[] = { &sr1, &sr2 };
+ int count;
+
+ sr_dir = debugfs_create_dir("smartreflex", d);
+ if (IS_ERR(sr_dir))
+ return PTR_ERR(sr_dir);
+ for (count = 0; count < (sizeof(sr_list) / sizeof(struct omap_sr *));
+ count++) {
+ struct omap_sr *sr = sr_list[count];
+ char name[2] = "0";
+ name[0] += sr->srid;
+ sr_sub_dir = debugfs_create_dir(name, sr_dir);
+ if (IS_ERR(sr_sub_dir))
+ continue;
+
+ (void)debugfs_create_file("nvalue_opp1", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->opp_nvalue[0]),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("nvalue_opp2", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->opp_nvalue[1]),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("nvalue_opp3", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->opp_nvalue[2]),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("nvalue_opp4", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->opp_nvalue[3]),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("nvalue_opp5", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->opp_nvalue[4]),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("sr_errconfig_value",
+ S_IRUGO | S_IWUGO, sr_sub_dir,
+ &(sr->sr_errconfig_value),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("sr_config_value", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->sr_config_value),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("vpconfig_value", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->vp.vpconfig_value),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("vpstepmin_value", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->vp.vpstepmin_value),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("vpstepmax_value", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->vp.vpstepmax_value),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("vplimito_value", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->vp.vplimito_value),
+ &sr_debugfs_option_fops);
+ (void)debugfs_create_file("vplimito_value", S_IRUGO | S_IWUGO,
+ sr_sub_dir, &(sr->vp.vplimito_value),
+ &sr_debugfs_option_fops);
+ }
+
+ return 0;
+}
+
+#endif /* __SR_DEBUG */
+
+/****************** PMIC WEAK FUNCTIONS FOR TWL4030 derivatives **********/
+
+/**
+ * @brief pmic_srinit - Power management IC initialization
+ * for smart reflex. The current code is written for TWL4030
+ * derivatives, replace in board file if PMIC requires
+ * a different sequence
+ *
+ * @return result of operation
+ */
+int __weak __init omap_pmic_srinit(void)
+{
+ int ret = -ENODEV;
+#ifdef CONFIG_TWL4030_CORE
+ u8 reg;
+ /* Enable SR on T2 */
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, ®,
+ R_DCDC_GLOBAL_CFG);
+
+ reg |= DCDC_GLOBAL_CFG_ENABLE_SRFLX;
+ ret |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, reg,
+ R_DCDC_GLOBAL_CFG);
+#endif /* End of CONFIG_TWL4030_CORE */
+ return ret;
+}
+
+/**
+ * @brief omap_pmic_voltage_ramp_delay - how much should this pmic ramp delay
+ * Various PMICs have different ramp up and down delays. choose to implement
+ * in required pmic file to override this function.
+ * On TWL4030 derivatives:
+ * T2 SMPS slew rate (min) 4mV/uS, step size 12.5mV,
+ * 2us added as buffer.
+ *
+ * @param srid - which SR is this for?
+ * @param target_vsel - targetted voltage selction
+ * @param current_vsel - current voltage selection
+ *
+ * @return delay in uSeconds
+ */
+u32 __weak omap_pmic_voltage_ramp_delay(u8 srid, u8 target_vsel,
+ u8 current_vsel)
+{
+ u32 t2_smps_steps = abs(target_vsel - current_vsel);
+ u32 t2_smps_delay = ((t2_smps_steps * 125) / 40) + 2;
+ return t2_smps_delay;
+}
+
+#ifdef CONFIG_OMAP_VC_BYPASS_UPDATE
+/**
+ * @brief omap_pmic_voltage_cmds - hook for pmic command sequence
+ * to be send out which are specific to pmic to set a specific voltage.
+ * this should inturn call vc_send_command with the required sequence
+ * The current implementation is for TWL4030 derivatives
+ *
+ * @param srid - which SR is this for?
+ * @param target_vsel - what voltage is desired to be set?
+ *
+ * @return specific value to set.
+ */
+int __weak omap_pmic_voltage_cmds(u8 srid, u8 target_vsel)
+{
+ u8 reg_addr = (srid == SR1) ? R_VDD1_SR_CONTROL : R_VDD2_SR_CONTROL;
+ u16 timeout = COUNT_TIMEOUT_TWL4030_VCBYPASS;
+ return vc_send_command(R_SRI2C_SLAVE_ADDR, reg_addr, target_vsel,
+ &timeout);
+}
+#endif /* ifdef CONFIG_OMAP_VC_BYPASS_UPDATE */
+
+/*********************** Voltage Controller functions *************************/
+
+/**
+ * @brief vc_send_command - The actual command transmission using
+ * Voltage controller on I2C4
+ *
+ * @param slave_addr - what is the PMIC slave address
+ * @param reg_addr - what is the register address I should be using?
+ * @param data - what value do you want to write here?
+ * @param timeout_us timeout in uSeconds - returns actual time left
+ *
+ * @return 0 if all ok, else error value
+ */
+int vc_send_command(u8 slave_addr, u8 reg_addr, u8 data, u16 *timeout_us)
+{
+ u32 value;
+ u32 count;
+
+ if (unlikely(!timeout_us))
+ return -EINVAL;
+
+ /* timeout = timeout_us/10 -> each udelay event
+ * 1 udelay event every 50 iteration, assuming
+ * each iteration is instaneous,
+ * count = (timeout_us/10) * 50 or timeout_us * 5
+ */
+ count = *timeout_us * 5;
+
+ value = (data << OMAP3430_DATA_SHIFT) |
+ (reg_addr << OMAP3430_REGADDR_SHIFT) |
+ (slave_addr << OMAP3430_SLAVEADDR_SHIFT);
+
+ prm_write_mod_reg(value, OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
+
+ value = prm_set_mod_reg_bits(OMAP3430_VALID, OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
+ /*
+ * Do continous 50 checks then follow with a 10usec delay,
+ * then check again
+ */
+ do {
+ value = prm_read_mod_reg(OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_BYPASS_VAL_OFFSET)
+ & OMAP3430_VALID;
+ /* should i wait? */
+ if (value && (count % 50))
+ udelay(10);
+ count--;
+ *timeout_us -= 5;
+ } while (value && count);
+ if (!count) {
+ pr_err("VC:Command Timedout! slave_addr=0x%02X,"
+ "reg=0x%02X, value=0x%02X", slave_addr, reg_addr, data);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(vc_send_command);
+
+/**
+ * @brief sr_vc_bypass - setup voltage using VC Bypass technique
+ *
+ * @param target_opp - target opp to go to
+ * @param current_opp - current opp
+ * @param target_vsel - which voltage to go to?
+ * @param current_vsel - current voltage
+ *
+ * @return -success or failure
+ */
+#ifdef CONFIG_OMAP_VC_BYPASS_UPDATE
+static int sr_vc_bypass(struct omap_sr *sr,
+ u32 target_opp_no, u8 target_vsel, u8 current_vsel)
+{
+ int ret = 0;
+ struct omap_sr_vp *vp = &sr->vp;
+ u32 vc_cmd_val_offs;
+ u32 vpconfig_value;
+
+ vc_cmd_val_offs =
+ (sr->srid ==
+ SR1) ? OMAP3_PRM_VC_CMD_VAL_0_OFFSET :
+ OMAP3_PRM_VC_CMD_VAL_1_OFFSET;
+
+ vpconfig_value = vp->vpconfig_value;
+ vpconfig_value |= ((target_opp_no < 3) ? SR_ERRGAIN_LOWOP :
+ SR_ERRGAIN_HIGHOPP) << OMAP3430_ERRORGAIN_SHIFT;
+ vpconfig_value |= target_vsel << OMAP3430_INITVOLTAGE_SHIFT;
+ prm_write_mod_reg(vpconfig_value, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ prm_rmw_mod_reg_bits(OMAP3430_VC_CMD_ON_MASK,
+ (target_vsel << OMAP3430_VC_CMD_ON_SHIFT),
+ OMAP3430_GR_MOD, vc_cmd_val_offs);
+
+ /*
+ * Various PMIC might need a set of commands
+ * provide hooks for specific PMICs to implement
+ */
+ ret = omap_pmic_voltage_cmds(sr->srid, target_vsel);
+
+ /* delay based on pmic */
+ if (!ret)
+ udelay(omap_pmic_voltage_ramp_delay(sr->srid,
+ target_vsel, current_vsel));
+
+ WARN_ON(ret);
+
+ return ret;
+}
+#endif /* ifdef CONFIG_OMAP_VC_BYPASS_UPDATE */
+
+/********************* SR Private functions ***************************/
+
+/**
+ * @brief sr_write_reg - write to Smart Reflex Register
+ *
+ * @param sr - pointer to SR structure
+ * @param offset - SR reg offset to write to
+ * @param value - value to write with
+ */
+static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value)
+{
+ __raw_writel(value, SR_REGADDR(offset));
+}
+
+/**
+ * @brief sr_modify_reg - Modify a register and write a new value to a field
+ *
+ * @param sr -pointer to SR structure
+ * @param offset -register offset
+ * @param mask -mask to clear out
+ * @param value -value to write there
+ */
+static inline void sr_modify_reg(struct omap_sr *sr, unsigned offset, u32 mask,
+ u32 value)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(SR_REGADDR(offset));
+ reg_val &= ~mask;
+ reg_val |= value;
+
+ __raw_writel(reg_val, SR_REGADDR(offset));
+}
+
+/**
+ * @brief sr_read_reg - read a SR register
+ *
+ * @param sr - pointer to SR structure
+ * @param offset - register offset
+ *
+ * @return value in that register
+ */
+static inline u32 sr_read_reg(struct omap_sr *sr, unsigned offset)
+{
+ return __raw_readl(SR_REGADDR(offset));
+}
+
+/**
+ * @brief sr_vplimito_value -return the timeout value based on sysclk
+ * and desired timeout uSec
+ *
+ * @param sys_clk_speed - internal sysclk freq in hz
+ * @param timeout_us - timeout in uSec
+ *
+ * @return timeout value to use in VPLIMITTO:TIMEOUT reg
+ */
+static inline u32 sr_vplimito_value(u32 sys_clk_speed, u16 timeout_us)
+{
+ u32 timeout_val;
+ /* prevent round off errors, we will divide by 10 later */
+ timeout_val = (sys_clk_speed / 100000);
+ timeout_val *= timeout_us;
+ timeout_val /= 10;
+ return timeout_val;
+}
+
+/**
+ * @brief sr_clk_enable - SR clock enable
+ *
+ * @param sr - Structure to SR structure
+ *
+ * @return - result
+ */
+static int sr_clk_enable(struct omap_sr *sr)
+{
+ if (clk_enable(sr->iclk) != 0) {
+ pr_crit("SR:Could not enable %s for [%d]\n",
+ sr->iclk->name, sr->srid);
+ return -EINVAL;
+ }
+ if (clk_enable(sr->fclk) != 0) {
+ pr_crit("SR:Could not enable %s for [%d]\n",
+ sr->fclk->name, sr->srid);
+ clk_disable(sr->iclk);
+ return -EINVAL;
+ }
+ sr_modify_reg(sr, ERRCONFIG,
+ SR_CLKACTIVITY_MASK | ERRCONFIG_INTERRUPT_STATUS_MASK,
+ SR_CLKACTIVITY_IOFF_FON);
+ sr->is_sr_reset = 0;
+
+ return 0;
+}
+
+/**
+ * @brief sr_clk_disable - SR func clock disable
+ *
+ * @param sr - pointer to SR structure
+ */
+static void sr_clk_disable(struct omap_sr *sr)
+{
+ /* set fclk, iclk- idle */
+ sr_modify_reg(sr, ERRCONFIG,
+ SR_CLKACTIVITY_MASK | ERRCONFIG_INTERRUPT_STATUS_MASK,
+ SR_CLKACTIVITY_IOFF_FOFF);
+
+ clk_disable(sr->fclk);
+ clk_disable(sr->iclk);
+ sr->is_sr_reset = 1;
+}
+
+/****************** Voltage processor functions ***************************/
+
+/**
+ * @brief sr_vp_clear_vptransdone - clear vptrans_done event
+ *
+ * @param sr - sr pointer
+ *
+ * @return 0 if cleared ok, else 1 if timedout!
+ */
+static int sr_vp_clear_vptransdone(struct omap_sr *sr)
+{
+ struct omap_sr_vp *vp = &sr->vp;
+ u32 irqstat;
+ u32 count = COUNT_TIMEOUT_TRANSDONE_CLR;
+ do {
+ prm_write_mod_reg(vp->irqmask_trans_done, OCP_MOD,
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+ irqstat = prm_read_mod_reg(OCP_MOD,
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET) &
+ vp->irqmask_trans_done;
+ if (irqstat)
+ udelay(1);
+ count--;
+ } while (count && irqstat);
+ if (!count) {
+ pr_crit("SR:VPTransdone[%d]:Timedout\n", sr->srid);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/**
+ * @brief sr_vp_forceupdate - do a forceupdate method
+ * to update the voltage level
+ *
+ * @param sr - pointer to sr structure
+ * @param target_opp_no - targetted opp number
+ * @param target_vsel - targetted voltage level
+ * @param current_vsel - current voltage level
+ *
+ * @return 0 if all worked out, else 1
+ */
+#ifndef CONFIG_OMAP_VC_BYPASS_UPDATE
+static int sr_vp_forceupdate(struct omap_sr *sr, u32 target_opp_no,
+ u8 target_vsel, u8 current_vsel)
+{
+ u32 count;
+ u32 irqstat;
+ u32 vpconfig_value;
+ u32 retry_counter = COUNT_RETRY_SMPSNOACK;
+
+ struct omap_sr_vp *vp = &sr->vp;
+
+retry_forceupdate:
+ /* First clear any pending events in the system */
+ if (sr_vp_clear_vptransdone(sr)) {
+ pr_crit("SR:forceupdate-transdone1[%d]:timedout\n", sr->srid);
+ return -ETIMEDOUT;
+ }
+
+ vpconfig_value = vp->vpconfig_value;
+ vpconfig_value |= ((target_opp_no < 3) ? SR_ERRGAIN_LOWOP :
+ SR_ERRGAIN_HIGHOPP) << OMAP3430_ERRORGAIN_SHIFT;
+ vpconfig_value |= target_vsel << OMAP3430_INITVOLTAGE_SHIFT;
+
+ prm_write_mod_reg(vpconfig_value, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ /* Trigger initVDD value copy to voltage processor */
+ prm_set_mod_reg_bits(OMAP3430_INITVDD, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ /* Force update of voltage */
+ prm_set_mod_reg_bits(OMAP3430_FORCEUPDATE, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ /* Clear initVDD copy trigger bit */
+ prm_clear_mod_reg_bits(OMAP3430_INITVDD, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+ /* Clear force bit */
+ prm_clear_mod_reg_bits(OMAP3430_FORCEUPDATE, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ /* Now wait for the i2c transactions to complete */
+ count = COUNT_TIMEOUT_TRANSDONE_SET;
+ irqstat = 0;
+ do {
+ irqstat = prm_read_mod_reg(OCP_MOD,
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET) &
+ vp->irqmask_trans_done;
+ if (!irqstat)
+ udelay(1);
+ count--;
+ } while (count && !irqstat);
+ if (!count) {
+ irqstat = prm_read_mod_reg(OCP_MOD,
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+ if (irqstat & vp->irqmask_smps_noack) {
+ WARN(irqstat, "SMPS NO ACK DETECTED[0x%08X]!!"
+ "ATTEMPTING RECOVERY [%d left]\n",
+ irqstat, retry_counter);
+ prm_write_mod_reg(irqstat, OCP_MOD,
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+ retry_counter--;
+ if (retry_counter)
+ goto retry_forceupdate;
+ }
+ pr_crit("SR:forceupdate-transdone[%d]:timedout-"
+ "irqstat=0x%08X\n", sr->srid, irqstat);
+ BUG();
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * Now we wait for voltage to rise on PMIC
+ */
+ udelay(omap_pmic_voltage_ramp_delay(sr->srid, target_vsel,
+ current_vsel));
+
+ /* clear that event */
+ if (sr_vp_clear_vptransdone(sr)) {
+ pr_crit("SR:forceupdate-transdone2[%d]:timedout\n", sr->srid);
+ BUG();
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+#endif /* ifndef CONFIG_OMAP_VC_BYPASS_UPDATE */
+
+/**
+ * @brief sr_vp_enable - enable VP enable code
+ *
+ * @param sr
+ */
+static int sr_vp_enable(struct omap_sr *sr, u32 target_opp_no)
+{
+ u32 vpconfig_value;
+ struct omap_sr_vp *vp = &sr->vp;
+
+ /* Disable VP */
+ prm_clear_mod_reg_bits(vp->vpenable_mask, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+ /* Clear INITVDD */
+ prm_clear_mod_reg_bits(OMAP3430_INITVDD, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ vpconfig_value = vp->vpconfig_value;
+ vpconfig_value |= ((sr->srid == SR1) ? mpu_opps[target_opp_no].vsel :
+ l3_opps[target_opp_no].vsel) <<
+ OMAP3430_INITVOLTAGE_SHIFT;
+ vpconfig_value |=
+ ((target_opp_no < 3) ? SR_ERRGAIN_LOWOP : SR_ERRGAIN_HIGHOPP) <<
+ OMAP3430_ERRORGAIN_SHIFT;
+
+ prm_write_mod_reg(vpconfig_value, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ prm_write_mod_reg(vp->vpstepmin_value, OMAP3430_GR_MOD,
+ vp->prm_vpx_stepmin_offset);
+ prm_write_mod_reg(vp->vpstepmax_value, OMAP3430_GR_MOD,
+ vp->prm_vpx_stepmax_offset);
+ prm_write_mod_reg(vp->vplimito_value, OMAP3430_GR_MOD,
+ vp->prm_vpx_limito_offset);
+
+ /* write1 to latch */
+ prm_set_mod_reg_bits(OMAP3430_INITVDD, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+ /* write2 clear */
+ prm_clear_mod_reg_bits(OMAP3430_INITVDD, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+ /* Enable VP */
+ prm_set_mod_reg_bits(vp->vpenable_mask, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+ return 0;
+}
+
+/**
+ * @brief sr_vp_disable - disbale Voltage processor
+ *
+ * @param sr - sr structure
+ *
+ * @return 0 if all ok, else return -ETIMEDOUT
+ */
+static int sr_vp_disable(struct omap_sr *sr)
+{
+ int count;
+ u32 v;
+ struct omap_sr_vp *vp = &sr->vp;
+
+ v = prm_read_mod_reg(OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset) & vp->vpenable_mask;
+ /* Am i already disabled? */
+ if (!v) {
+ pr_info("SR[%d] attempt to disable VP when already disabled!\n",
+ sr->srid);
+ return 0;
+ }
+
+ /* Disable VP */
+ prm_clear_mod_reg_bits(vp->vpenable_mask, OMAP3430_GR_MOD,
+ vp->prm_vpx_config_offset);
+
+ /* Wait for vp to get idle - clear any events pending */
+ count = COUNT_TIMEOUT_MCUDISACK;
+ do {
+ v = prm_read_mod_reg(OMAP3430_GR_MOD,
+ vp->prm_vpx_status_offset) &
+ PRM_VP_STATUS_VPINIDLE;
+ if (!v)
+ udelay(1);
+ count--;
+ } while (count && !v);
+ if (unlikely(!count)) {
+ v = prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+ pr_warning("SR:vpdisable-vpinidle[%d][opp=%d]:timedout-"
+ "irqstat=0x%08X\n", sr->srid, sr->req_opp_no, v);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/**
+ * @brief sr_vp_configure - configure the basic SR structure
+ *
+ * @param sr - pointer to SR structure
+ */
+static void sr_vp_configure(struct omap_sr *sr)
+{
+ u32 target_opp_no;
+ u32 target_vsel;
+ u32 prm_vpx_voltage;
+
+ if (sr->srid == SR1) {
+ target_opp_no = mpu_opps[resource_get_level("vdd1_opp")].opp_id;
+ target_vsel = mpu_opps[resource_get_level("vdd1_opp")].vsel;
+ prm_vpx_voltage = prm_read_mod_reg(OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_VOLTAGE_OFFSET);
+ } else {
+ target_opp_no = l3_opps[resource_get_level("vdd2_opp")].opp_id;
+ target_vsel = l3_opps[resource_get_level("vdd2_opp")].vsel;
+ prm_vpx_voltage = prm_read_mod_reg(OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_VOLTAGE_OFFSET);
+ }
+
+ sr_vp_enable(sr, target_opp_no);
+ if (SR_CHOSEN_VOLTAGE_UPDATE_MECH
+ (sr, target_opp_no, target_vsel, prm_vpx_voltage))
+ pr_crit("SR[%d] CONFIGURE VP failed!!\n", sr->srid);
+}
+
+/**
+ * @brief sr_vp_reset_voltage - reset the voltages back to DVFS values
+ *
+ * @param srid -SRID
+ *
+ * @return 0 if ok, else result
+ */
+static int sr_vp_reset_voltage(u8 srid)
+{
+ u32 target_opp_no;
+ u32 target_vsel;
+ u32 prm_vpx_voltage;
+ struct omap_sr *sr;
+ if (srid == SR1) {
+ sr = &sr1;
+ target_opp_no = sr1.req_opp_no;
+ target_vsel = mpu_opps[target_opp_no].vsel;
+ prm_vpx_voltage = prm_read_mod_reg(OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_VOLTAGE_OFFSET);
+ } else {
+ sr = &sr2;
+ target_opp_no = sr2.req_opp_no;
+ target_vsel = l3_opps[target_opp_no].vsel;
+ prm_vpx_voltage = prm_read_mod_reg(OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_VOLTAGE_OFFSET);
+ }
+ return SR_CHOSEN_VOLTAGE_UPDATE_MECH(sr, target_opp_no,
+ target_vsel, prm_vpx_voltage);
+
+}
+
+/*********************** SR functions *************************/
+
+/**
+ * @brief sr_enable - enable smart reflex
+ *
+ * @param sr - smartreflex structure we are interest
+ * @param target_opp_no - target opp we want to switch to
+ *
+ * @return 0 if all went right, else return err val
+ */
+static int sr_enable(struct omap_sr *sr, u32 target_opp_no)
+{
+ u32 value;
+
+ if (!sr->is_sr_reset) {
+ pr_info("SR[%d]already enabled\n", sr->srid);
+ return -EINVAL;
+ }
+
+ sr->req_opp_no = target_opp_no;
+
+ value = sr->opp_nvalue[target_opp_no - 1];
+ if (value == 0) {
+ pr_info("SR[%d]:OPP%d doesn't support SmartReflex\n",
+ sr->srid, target_opp_no);
+ return -EINVAL;
+ }
+
+ sr_clk_enable(sr);
+ /* Start with setting SREnable as 0 */
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0);
+
+ sr_write_reg(sr, SRCONFIG, sr->sr_config_value);
+
+ sr_write_reg(sr, NVALUERECIPROCAL, value);
+
+ value = sr->sr_errconfig_value |
+ ((target_opp_no < 3) ? SR_ERRMINLIMIT_LOWOPP :
+ SR_ERRMINLIMIT_HIGHOPP);
+
+ sr_write_reg(sr, ERRCONFIG, value);
+
+ /* SRCONFIG - enable SR */
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE);
+
+ return 0;
+}
+
+/**
+ * @brief sr_disable - disable Smart Reflex
+ *
+ * @param sr - pointer to sr structure of interest
+ *
+ * @return 0 if all went right, else return INVAL/TIMEDOUT
+ */
+int sr_disable(struct omap_sr *sr)
+{
+ int count;
+ u32 stat;
+ u32 value;
+
+ if (sr->is_sr_reset) {
+ pr_info("SR[%d]-disable:already Disabled\n", sr->srid);
+ return -EINVAL;
+ }
+ value = sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE;
+ if (!value) {
+ pr_info("SR[%d] attempt to disable SR when already disabled!\n",
+ sr->srid);
+ return 0;
+ }
+ value = sr->opp_nvalue[sr->req_opp_no - 1];
+ if (value == 0) {
+ pr_info("SR[%d]-disable:"
+ "OPP%d doesn't support SmartReflex\n",
+ sr->srid, sr->req_opp_no);
+ return -EINVAL;
+ }
+ /* Enable the MCUDISACKINST */
+ sr_modify_reg(sr, ERRCONFIG, ERRCONFIG_MCUDISACKINTEN |
+ ERRCONFIG_INTERRUPT_STATUS_MASK,
+ ERRCONFIG_MCUDISACKINTEN);
+
+ /* SRCONFIG - disable SR */
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0);
+
+ /* Disable VPBOUND interrupt and clear any status
+ * before actually waiting for disack
+ * should be done after sr is disabled
+ */
+ sr_modify_reg(sr, ERRCONFIG, ERRCONFIG_VPBOUNDINTEN |
+ ERRCONFIG_INTERRUPT_STATUS_MASK, ERRCONFIG_VPBOUNDINTST);
+
+ /* Wait for MCUDISACKINTST to be set */
+ count = COUNT_TIMEOUT_MCUDISACK;
+ do {
+ stat = sr_read_reg(sr, ERRCONFIG) & ERRCONFIG_MCUDISACKINTST;
+ if (!stat)
+ udelay(1);
+ count--;
+ } while (count && !stat);
+ /* Clear the event and disable MCUDISACKINST */
+ sr_modify_reg(sr, ERRCONFIG, ERRCONFIG_MCUDISACKINTEN |
+ ERRCONFIG_INTERRUPT_STATUS_MASK,
+ ERRCONFIG_MCUDISACKINTST);
+
+ if (!count) {
+ pr_crit("SR[%d]-disable:MCUDIS timedout\n", sr->srid);
+ return -ETIMEDOUT;
+ }
+ sr_clk_disable(sr);
+ return 0;
+}
+
+/**************** Common enable/disable functionality *********************/
+
+/**
+ * @brief srvp_disable - disable SR and VP
+ * These functions are based on h/w timeouts and should ideally not fail.
+ *
+ * @param sr -sr struct
+ *
+ * @return result
+ */
+static int srvp_disable(struct omap_sr *sr)
+{
+ int ret;
+ /* If we dont have an nvalue, dont bother.. */
+ if (!sr->opp_nvalue[sr->req_opp_no - 1]) {
+ pr_warning("SR[%d]: OPP%d does not support SR to disable\n",
+ sr->srid, sr->req_opp_no);
+ return -EINVAL;
+ }
+ ret = sr_vp_disable(sr);
+ if (unlikely(ret)) {
+ pr_err("SR[%d]: failed to disable vp:%d\n", sr->srid, ret);
+ } else {
+ ret = sr_disable(sr);
+ if (unlikely(ret))
+ pr_err("SR[%d]: failed to disable sr:%d\n",
+ sr->srid, ret);
+ }
+ /*
+ * Does not make much sense renabling SR as
+ * system is going to be in an invalid state
+ */
+ WARN_ON(ret);
+ return ret;
+}
+
+/**
+ * @brief srvp_enable - enable SR and VP
+ * These functions are based on h/w timeouts and should ideally not fail.
+ *
+ * @param sr -sr struct
+ * @param target_opp -opp to go to
+ *
+ * @return result
+ */
+static int srvp_enable(struct omap_sr *sr, u32 target_opp)
+{
+ int ret;
+ /* If we dont have an nvalue, dont bother.. */
+ if (!sr->opp_nvalue[target_opp - 1]) {
+ pr_warning("SR[%d]: OPP%d does not support SR to enable\n",
+ sr->srid, target_opp);
+ return -EINVAL;
+ }
+ ret = sr_vp_enable(sr, target_opp);
+ if (unlikely(ret)) {
+ pr_err("SR[%d]: failed to enable vp:%d\n", sr->srid, ret);
+ } else {
+ ret = sr_enable(sr, target_opp);
+ /* Attempt to recover */
+ if (unlikely(ret)) {
+ pr_err("SR[%d]: failed to enable sr:%d\n", sr->srid,
+ ret);
+ /* nothing we can do if vp_disable fails */
+ (void)sr_vp_disable(sr);
+ }
+ }
+ /*
+ * potentially system in an invalid state - warn..
+ */
+ WARN_ON(ret);
+ return ret;
+}
+
+/*********************** DVFS Entry POINTS **********************************/
+
+/**
+ * @brief sr_vp_enable_both - enable both vp and sr
+ *
+ * @param target_opp - targetted op
+ * @param current_opp - current opp
+ *
+ * @return 0 if ok, 1 if not ok
+ */
+int sr_vp_enable_both(u32 target_opp, u32 current_opp)
+{
+ struct omap_sr *sr;
+ u32 vdd, target_opp_no;
+ int ret = 0;
+
+ vdd = get_vdd(target_opp);
+ target_opp_no = get_opp_no(target_opp);
+ sr = (vdd == PRCM_VDD1) ? &sr1 : &sr2;
+
+ if (sr->is_autocomp_active && sr->is_sr_reset) {
+ ret = srvp_enable(sr, target_opp_no);
+ if (ret) {
+ pr_err("SR[%d]:enableboth:"
+ "failed enable SR\n", sr->srid);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(sr_vp_enable_both);
+
+/**
+ * @brief sr_vp_disable_both - disable both vp and sr
+ *
+ * @param target_opp - targetted opp
+ * @param current_opp - current opp
+ *
+ * @return 0 if ok, 1 if not ok
+ */
+int sr_vp_disable_both(u32 target_opp, u32 current_opp)
+{
+ struct omap_sr *sr;
+ u32 vdd;
+ int ret = 0;
+
+ vdd = get_vdd(target_opp);
+ sr = (vdd == PRCM_VDD1) ? &sr1 : &sr2;
+
+ if (sr->is_autocomp_active && !sr->is_sr_reset) {
+ ret = srvp_disable(sr);
+ if (ret) {
+ pr_err("SR[%d]:disableboth:"
+ "failed disable SR\n", sr->srid);
+ }
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL(sr_vp_disable_both);
+
+/**
+ * @brief sr_voltage_set - setup a voltage requested
+ *
+ * @param target_opp - targetted opp
+ * @param current_opp - current opp
+ * @param target_vsel - targeted voltage
+ * @param current_vsel - current voltage
+ *
+ * @return - result of op -0 if ok, else value
+ */
+int sr_voltage_set(u32 target_opp, u32 current_opp,
+ u8 target_vsel, u8 current_vsel)
+{
+ struct omap_sr *sr;
+ u32 vdd, target_opp_no;
+ int ret;
+
+ vdd = get_vdd(target_opp);
+ target_opp_no = get_opp_no(target_opp);
+ sr = (vdd == PRCM_VDD1) ? &sr1 : &sr2;
+
+ ret =
+ SR_CHOSEN_VOLTAGE_UPDATE_MECH(sr, target_opp_no, target_vsel,
+ current_vsel);
+
+ return ret;
+}
+EXPORT_SYMBOL(sr_voltage_set);
+
+/*********************** CPUIDLE ENTRY POINTS *********************************/
+
+/**
+ * @brief disable_smartreflex - disable SmartReflex before WFI
+ *
+ * @param srid SRID
+ */
+void disable_smartreflex(u8 srid)
+{
+ struct omap_sr *sr = NULL;
+ int ret;
+ u32 current_opp_no;
+
+ /* I want to be in irq_disabled context..
+ * else I will die.. find the rootcause and fix it instead
+ */
+ BUG_ON(!irqs_disabled());
+
+ sr = (srid == SR1) ? &sr1 : &sr2;
+
+ current_opp_no = (sr->srid == SR1) ?
+ mpu_opps[resource_get_level("vdd1_opp")].opp_id :
+ l3_opps[resource_get_level("vdd2_opp")].opp_id;
+
+ if (sr->is_autocomp_active && !sr->is_sr_reset) {
+ sr->req_opp_no = current_opp_no;
+ ret = srvp_disable(sr);
+ if (ret)
+ pr_err("SR[%d]:disable_smartreflex:"
+ "failed disable SR\n", sr->srid);
+ }
+
+ /* Reset the voltage for current OPP to nominal if SR was enabled */
+ if (sr->is_autocomp_active)
+ sr_vp_reset_voltage(srid);
+}
+EXPORT_SYMBOL(disable_smartreflex);
+
+/**
+ * @brief enable_smartreflex - enable smart reflex after WFI is hit
+ *
+ * @param srid -SR ID to hit
+ */
+void enable_smartreflex(u8 srid)
+{
+ struct omap_sr *sr;
+ int ret;
+ u32 current_opp_no;
+
+ /* I want to be in irq_disabled context..
+ * else I will die.. find the rootcause and fix it instead
+ */
+ BUG_ON(!irqs_disabled());
+
+ sr = (srid == SR1) ? &sr1 : &sr2;
+
+ current_opp_no = (sr->srid == SR1) ?
+ mpu_opps[resource_get_level("vdd1_opp")].opp_id :
+ l3_opps[resource_get_level("vdd2_opp")].opp_id;
+ if (sr->is_autocomp_active && sr->is_sr_reset) {
+ ret = srvp_enable(sr, current_opp_no);
+ if (ret)
+ pr_err("SR[%d]:enable_smartreflex:"
+ "failed enable SR\n", sr->srid);
+ }
+}
+EXPORT_SYMBOL(enable_smartreflex);
+
+/*********************** SYSFS ENTRY POINTS *********************************/
+/**
+ * @brief omap_sr_vdd_autocomp_show - Sysfs entry for showing SR status
+ *
+ */
+static ssize_t omap_sr_vdd_autocomp_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ u8 srid;
+ struct omap_sr *sr;
+ srid = (strcmp(attr->attr.name, "sr_vdd1_autocomp") == 0) ? SR1 : SR2;
+ sr = (srid == SR1) ? &sr1 : &sr2;
+ return sprintf(buf, "%d\n", sr->is_autocomp_active);
+}
+
+/**
+ * @brief omap_sr_vdd_autocomp_store - enable/disable SR
+ *
+ */
+static ssize_t omap_sr_vdd_autocomp_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned short value;
+ u8 srid;
+ struct omap_sr *sr;
+ int ret = 0;
+ u32 current_vddopp_no;
+ const char *name;
+
+ name = attr->attr.name;
+
+ srid = (strcmp(name, "sr_vdd1_autocomp") == 0) ? SR1 : SR2;
+ sr = (srid == SR1) ? &sr1 : &sr2;
+ current_vddopp_no = (sr->srid == SR1) ?
+ mpu_opps[resource_get_level("vdd1_opp")].opp_id :
+ l3_opps[resource_get_level("vdd2_opp")].opp_id;
+
+ if (sscanf(buf, "%hu", &value) != 1 || (value > 1)) {
+ pr_err("%s: Invalid value[%d]. Use 0 or 1\n", name, value);
+ return -EINVAL;
+ }
+ if (sr->is_autocomp_active == value) {
+ pr_info("%s: Already set to %d \n", name, value);
+ return n;
+ }
+ /*
+ * Sanity check-might happen if SR and dvfs/idle paths collide
+ * but unlikely though..
+ */
+ if (unlikely(value ^ sr->is_sr_reset)) {
+ pr_warning("%s: Is already set %d Vs %d.\n", name, value,
+ sr->is_sr_reset);
+ return n;
+ }
+
+ if (value)
+ ret = srvp_enable(sr, current_vddopp_no);
+ else {
+ ret = srvp_disable(sr);
+ /* reset the voltage back to nominal */
+ sr_vp_configure(sr);
+ }
+ if (!ret) {
+ sr->is_autocomp_active = value;
+ ret = n;
+ }
+
+ return ret;
+}
+
+static struct kobj_attribute sr_vdd1_autocomp = {
+ .attr = {
+ .name = __stringify(sr_vdd1_autocomp),
+ .mode = 0644,
+ },
+ .show = omap_sr_vdd_autocomp_show,
+ .store = omap_sr_vdd_autocomp_store,
+};
+
+static struct kobj_attribute sr_vdd2_autocomp = {
+ .attr = {
+ .name = __stringify(sr_vdd2_autocomp),
+ .mode = 0644,
+ },
+ .show = omap_sr_vdd_autocomp_show,
+ .store = omap_sr_vdd_autocomp_store,
+};
+
+/*********************** INIT FUNCTIONS *************************************/
+
+/**
+ * @brief srvp_init - configure smart reflex and VP params
+ *
+ * @param sr - structure of sr of interest
+ */
+static void __init srvp_init(struct omap_sr *sr)
+{
+ struct clk *sys_ck;
+ u32 sys_clk_speed;
+ u32 clk_len;
+ u32 senn_mod, senp_mod;
+
+ /* Grab the clock speed */
+ sys_ck = clk_get(NULL, "sys_ck");
+ sys_clk_speed = clk_get_rate(sys_ck);
+ clk_put(sys_ck);
+
+ switch (sys_clk_speed) {
+ case 12000000:
+ clk_len = SRCLKLENGTH_12MHZ_SYSCLK;
+ break;
+ case 13000000:
+ clk_len = SRCLKLENGTH_13MHZ_SYSCLK;
+ break;
+ case 19200000:
+ clk_len = SRCLKLENGTH_19MHZ_SYSCLK;
+ break;
+ case 26000000:
+ clk_len = SRCLKLENGTH_26MHZ_SYSCLK;
+ break;
+ case 38400000:
+ clk_len = SRCLKLENGTH_38MHZ_SYSCLK;
+ break;
+ default:
+ pr_err("SR[%d]:Invalid sysclk value: %d\n", sr->srid,
+ sys_clk_speed);
+ BUG();
+ return;
+ }
+ /* Configure from the nvalues */
+ if (sr->srid == SR1) {
+ senn_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR1_SENNENABLE_MASK) >>
+ OMAP343X_SR1_SENNENABLE_SHIFT;
+ senp_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR1_SENPENABLE_MASK) >>
+ OMAP343X_SR1_SENPENABLE_SHIFT;
+
+ sr->opp_nvalue[4] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP5_VDD1);
+ sr->opp_nvalue[3] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP4_VDD1);
+ sr->opp_nvalue[2] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP3_VDD1);
+ sr->opp_nvalue[1] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP2_VDD1);
+ sr->opp_nvalue[0] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP1_VDD1);
+ sr->sr_errconfig_value = SR1_ERRWEIGHT | SR1_ERRMAXLIMIT;
+ sr->vp.vplimito_value |= sr_vplimito_value(sys_clk_speed,
+ PRM_VP1_VLIMITTO_TIMEOUT_US)
+ << PRM_VP1_VLIMITTO_TIMEOUT_SHIFT;
+
+ } else {
+ senn_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR2_SENNENABLE_MASK) >>
+ OMAP343X_SR2_SENNENABLE_SHIFT;
+
+ senp_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR2_SENPENABLE_MASK) >>
+ OMAP343X_SR2_SENPENABLE_SHIFT;
+
+ sr->opp_nvalue[2] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP3_VDD2);
+ sr->opp_nvalue[1] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP2_VDD2);
+ sr->opp_nvalue[0] =
+ omap_ctrl_readl(OMAP343X_CONTROL_FUSE_OPP1_VDD2);
+ sr->sr_errconfig_value = SR2_ERRWEIGHT | SR2_ERRMAXLIMIT;
+ sr->vp.vplimito_value |= sr_vplimito_value(sys_clk_speed,
+ PRM_VP2_VLIMITTO_TIMEOUT_US)
+ << PRM_VP2_VLIMITTO_TIMEOUT_SHIFT;
+ }
+
+ sr->sr_errconfig_value |=
+ ERRCONFIG_VPBOUNDINTEN | ERRCONFIG_VPBOUNDINTST |
+ SR_CLKACTIVITY_IOFF_FON;
+
+ sr->sr_config_value =
+ (clk_len << SRCONFIG_SRCLKLENGTH_SHIFT) | SRCONFIG_ERRGEN_EN
+ | SRCONFIG_SENENABLE | (senn_mod <<
+ SRCONFIG_SENNENABLE_SHIFT) |
+ (senp_mod << SRCONFIG_SENPENABLE_SHIFT) | SRCONFIG_DELAYCTRL;
+
+ /* Set up nominal voltage */
+ sr_vp_configure(sr);
+}
+
+/**
+ * @brief omap_sr_init - SR initialization
+ *
+ * @return 0
+ */
+static int __init omap_sr_init(void)
+{
+ int ret = 0;
+ ret = omap_pmic_srinit();
+ if (ret) {
+ pr_crit("PMIC init failed during SmartReflex initilization."
+ "connectivity issues?: %d\n", ret);
+ return ret;
+ }
+ /* TODO: Extend this support for various PMICs */
+ if (cpu_is_omap34xx()) {
+ sr1.fclk = clk_get(NULL, "sr1_fck");
+ sr1.iclk = clk_get(NULL, "sr_l4_ick");
+ sr1.srbase_addr = ioremap((u32) sr1.srbase_addr, SZ_4K);
+ sr2.fclk = clk_get(NULL, "sr2_fck");
+ sr2.iclk = clk_get(NULL, "sr_l4_ick");
+ sr2.srbase_addr = ioremap((u32) sr2.srbase_addr, SZ_4K);
+
+ srvp_init(&sr1);
+ srvp_init(&sr2);
+
+ } else {
+ pr_warning("SmartReflex is not supported\n");
+ return -ENODEV;
+ }
+
+ pr_info("SmartReflex driver initialized\n");
+
+ ret = sysfs_create_file(power_kobj, &sr_vdd1_autocomp.attr);
+ if (ret)
+ pr_warning("sysfs_create_file failed: %d\n", ret);
+
+ ret = sysfs_create_file(power_kobj, &sr_vdd2_autocomp.attr);
+ if (ret)
+ pr_warning("sysfs_create_file failed: %d\n", ret);
+
+ return 0;
+}
+
+late_initcall(omap_sr_init);
new file mode 100644
@@ -0,0 +1,274 @@
+#ifndef __ARCH_ARM_MACH_OMAP3_SMARTREFLEX_H
+#define __ARCH_ARM_MACH_OMAP3_SMARTREFLEX_H
+/*
+ * linux/arch/arm/mach-omap2/smartreflex.h
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ * Nishanth Menon
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Kalle Jokiniemi
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Lesly A M <x0080970@ti.com>
+ *
+ * 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.
+ */
+
+/* SMART REFLEX REG ADDRESS OFFSET */
+#define SRCONFIG 0x00
+#define SRSTATUS 0x04
+#define SENVAL 0x08
+#define SENMIN 0x0C
+#define SENMAX 0x10
+#define SENAVG 0x14
+#define AVGWEIGHT 0x18
+#define NVALUERECIPROCAL 0x1C
+#define SENERROR 0x20
+#define ERRCONFIG 0x24
+
+/* SR Modules */
+#define SR1 1
+#define SR2 2
+
+#define VP1_IRQMASK_TRANSDONE (0x1 << 15)
+#define VP2_IRQMASK_TRANSDONE (0x1 << 21)
+
+/* PRM_VP1_CONFIG */
+#define PRM_VP1_CONFIG_ERROROFFSET (0x00 << 24)
+#define PRM_VP1_CONFIG_ERRORGAIN (0x18 << 16)
+#define SR_ERRGAIN_LOWOP (0x0C)
+#define SR_ERRGAIN_HIGHOPP (0x18)
+#define SR_ERRMINLIMIT_LOWOPP (0xF4 << 0)
+#define SR_ERRMINLIMIT_HIGHOPP (0xF9 << 0)
+#define PRM_VP_STATUS_VPINIDLE (0x1 << 0)
+
+#define PRM_VP1_CONFIG_INITVOLTAGE (0x30 << 8) /* 1.2 volt */
+#define PRM_VP1_CONFIG_TIMEOUTEN (0x1 << 3)
+#define PRM_VP1_CONFIG_INITVDD (0x1 << 2)
+#define PRM_VP1_CONFIG_FORCEUPDATE (0x1 << 1)
+#define PRM_VP1_CONFIG_VPENABLE (0x1 << 0)
+
+/* PRM_VP1_VSTEPMIN */
+#define PRM_VP1_VSTEPMIN_SMPSWAITTIMEMIN (0x03C << 8)
+#define PRM_VP1_VSTEPMIN_VSTEPMIN (0x01 << 0)
+
+/* PRM_VP1_VSTEPMAX */
+#define PRM_VP1_VSTEPMAX_SMPSWAITTIMEMAX (0x003C << 8)
+#define PRM_VP1_VSTEPMAX_VSTEPMAX (0x08 << 0)
+
+/* PRM_VP1_VLIMITTO */
+#define PRM_VP1_VLIMITTO_VDDMAX (0x44 << 24)
+#define PRM_VP1_VLIMITTO_VDDMIN (0x14 << 16)
+#define PRM_VP1_VLIMITTO_TIMEOUT_US (200)
+#define PRM_VP1_VLIMITTO_TIMEOUT_SHIFT (0)
+
+/* PRM_VP2_CONFIG */
+#define PRM_VP2_CONFIG_ERROROFFSET (0x00 << 24)
+#define PRM_VP2_CONFIG_ERRORGAIN (0x18 << 16)
+
+#define PRM_VP2_CONFIG_INITVOLTAGE (0x30 << 8) /* 1.2 volt */
+#define PRM_VP2_CONFIG_TIMEOUTEN (0x1 << 3)
+#define PRM_VP2_CONFIG_INITVDD (0x1 << 2)
+#define PRM_VP2_CONFIG_FORCEUPDATE (0x1 << 1)
+#define PRM_VP2_CONFIG_VPENABLE (0x1 << 0)
+
+/* PRM_VP2_VSTEPMIN */
+#define PRM_VP2_VSTEPMIN_SMPSWAITTIMEMIN (0x03C << 8)
+#define PRM_VP2_VSTEPMIN_VSTEPMIN (0x01 << 0)
+
+/* PRM_VP2_VSTEPMAX */
+#define PRM_VP2_VSTEPMAX_SMPSWAITTIMEMAX (0x003C << 8)
+#define PRM_VP2_VSTEPMAX_VSTEPMAX (0x08 << 0)
+
+/* PRM_VP2_VLIMITTO */
+#define PRM_VP2_VLIMITTO_VDDMAX (0x42 << 24)
+#define PRM_VP2_VLIMITTO_VDDMIN (0x18 << 16)
+#define PRM_VP2_VLIMITTO_TIMEOUT_US (200)
+#define PRM_VP2_VLIMITTO_TIMEOUT_SHIFT (0)
+
+/* SRCONFIG */
+#define SRCLKLENGTH_12MHZ_SYSCLK 0x3C
+#define SRCLKLENGTH_13MHZ_SYSCLK 0x41
+#define SRCLKLENGTH_19MHZ_SYSCLK 0x60
+#define SRCLKLENGTH_26MHZ_SYSCLK 0x82
+#define SRCLKLENGTH_38MHZ_SYSCLK 0xC0
+
+#define SRCONFIG_SRCLKLENGTH_SHIFT 12
+#define SRCONFIG_SENNENABLE_SHIFT 5
+#define SRCONFIG_SENPENABLE_SHIFT 3
+
+#define SRCONFIG_SRENABLE (0x01 << 11)
+#define SRCONFIG_SENENABLE (0x01 << 10)
+#define SRCONFIG_ERRGEN_EN (0x01 << 9)
+#define SRCONFIG_MINMAXAVG_EN (0x01 << 8)
+
+#define SRCONFIG_DELAYCTRL (0x01 << 2)
+#define SRCONFIG_CLKCTRL (0x00 << 0)
+
+/* NVALUERECIPROCAL */
+#define NVALUERECIPROCAL_SENPGAIN_SHIFT 20
+#define NVALUERECIPROCAL_SENNGAIN_SHIFT 16
+#define NVALUERECIPROCAL_RNSENP_SHIFT 8
+#define NVALUERECIPROCAL_RNSENN_SHIFT 0
+
+/* ERRCONFIG */
+#define SR_CLKACTIVITY_MASK (0x03 << 20)
+#define SR_ERRWEIGHT_MASK (0x07 << 16)
+#define SR_ERRMAXLIMIT_MASK (0xFF << 8)
+#define SR_ERRMINLIMIT_MASK (0xFF << 0)
+
+#define SR_CLKACTIVITY_IOFF_FOFF (0x00 << 20)
+#define SR_CLKACTIVITY_IOFF_FON (0x02 << 20)
+
+#define ERRCONFIG_VPBOUNDINTEN (0x1 << 31)
+#define ERRCONFIG_MCUDISACKINTEN (0x1 << 23)
+
+/* Status Bits */
+#define ERRCONFIG_VPBOUNDINTST (0x1 << 30)
+#define ERRCONFIG_MCUACCUMINTST (0x1 << 28)
+#define ERRCONFIG_MCUVALIDINTST (0x1 << 26)
+#define ERRCONFIG_MCUBOUNDINTST (0x1 << 24)
+#define ERRCONFIG_MCUDISACKINTST (0x1 << 22)
+
+/* WARNING: Ensure all access to errconfig register skips
+ * clearing intst bits to ensure that we dont clear status
+ * bits unwantedly.. esp vpbound
+ */
+#define ERRCONFIG_INTERRUPT_STATUS_MASK (ERRCONFIG_VPBOUNDINTST |\
+ ERRCONFIG_MCUACCUMINTST |\
+ ERRCONFIG_MCUVALIDINTST |\
+ ERRCONFIG_MCUBOUNDINTST |\
+ ERRCONFIG_MCUDISACKINTST | (0X1<<19))
+
+#define SR1_ERRWEIGHT (0x04 << 16)
+#define SR1_ERRMAXLIMIT (0x02 << 8)
+#define SR1_ERRMINLIMIT (0xFA << 0)
+
+#define SR2_ERRWEIGHT (0x04 << 16)
+#define SR2_ERRMAXLIMIT (0x02 << 8)
+#define SR2_ERRMINLIMIT (0xFA << 0)
+
+/* T2 SMART REFLEX */
+#define R_SRI2C_SLAVE_ADDR 0x12
+#define R_VDD1_SR_CONTROL 0x00
+#define R_VDD2_SR_CONTROL 0x01
+
+/* VDDs*/
+#define PRCM_VDD1 1
+#define PRCM_VDD2 2
+
+/*
+ * XXX: These should be removed/moved from here once we have a working DVFS
+ * implementation in place
+ */
+#define PHY_TO_OFF_PM_MASTER(p) (p - 0x36)
+#define PHY_TO_OFF_PM_RECIEVER(p) (p - 0x5b)
+#define PHY_TO_OFF_PM_INT(p) (p - 0x2e)
+
+/* Vmode control */
+#define R_DCDC_GLOBAL_CFG PHY_TO_OFF_PM_RECIEVER(0x61)
+/* R_DCDC_GLOBAL_CFG register, SMARTREFLEX_ENABLE values */
+#define DCDC_GLOBAL_CFG_ENABLE_SRFLX 0x08
+
+/* DEVICE ID/DPLL ID/CLOCK ID: bits 28-31 for OMAP type */
+#define OMAP_TYPE_SHIFT 28
+#define OMAP_TYPE_MASK 0xF
+/* OPP ID: bits: 0-4 for OPP number */
+#define OPP_NO_POS 0
+#define OPP_NO_MASK 0x1F
+/* OPP ID: bits: 5-6 for VDD */
+#define VDD_NO_POS 5
+#define VDD_NO_MASK 0x3
+/* Other IDs: bits 20-27 for ID type */
+/* These IDs have bits 25,26,27 as 1 */
+#define OTHER_ID_TYPE_SHIFT 20
+#define OTHER_ID_TYPE_MASK 0xFF
+
+#define OTHER_ID_TYPE(X) ((X & OTHER_ID_TYPE_MASK) << OTHER_ID_TYPE_SHIFT)
+#define ID_OPP_NO(X) ((X & OPP_NO_MASK) << OPP_NO_POS)
+#define ID_VDD(X) ((X & VDD_NO_MASK) << VDD_NO_POS)
+#define OMAP(X) ((X >> OMAP_TYPE_SHIFT) & OMAP_TYPE_MASK)
+#define get_opp_no(X) ((X >> OPP_NO_POS) & OPP_NO_MASK)
+#define get_vdd(X) ((X >> VDD_NO_POS) & VDD_NO_MASK)
+
+/* XXX: end remove/move */
+
+/* XXX: find more appropriate place for these once DVFS is in place */
+extern u32 current_vdd1_opp;
+extern u32 current_vdd2_opp;
+
+/*
+ * Smartreflex module enable/disable interface.
+ * NOTE: if smartreflex is not enabled from sysfs, these functions will not
+ * do anything.
+ */
+#ifdef CONFIG_OMAP_SMARTREFLEX
+void enable_smartreflex(u8 srid);
+void disable_smartreflex(u8 srid);
+int sr_voltage_set(u32 target_opp, u32 current_opp,
+ u8 target_vsel, u8 current_vsel);
+int sr_vp_disable_both(u32 target_opp, u32 current_opp);
+int sr_vp_enable_both(u32 target_opp, u32 current_opp);
+int vc_send_command(u8 slave_addr, u8 reg_addr, u8 data, u16 *timeout_us);
+int omap_pmic_srinit(void);
+u32 omap_pmic_voltage_ramp_delay(u8 srid, u8 target_vsel, u8 current_vsel);
+#ifdef CONFIG_OMAP_VC_BYPASS_UPDATE
+int omap_pmic_voltage_cmds(u8 srid, u8 target_vsel);
+#endif
+
+#ifdef CONFIG_PM_DEBUG
+#define __SR_DEBUG
+#endif
+
+#else
+static inline void enable_smartreflex(u8 srid)
+{
+}
+
+static inline void disable_smartreflex(u8 srid)
+{
+}
+
+static inline int sr_voltage_set(u32 target_opp, u32 current_opp,
+ u8 target_vsel, u8 current_vsel)
+{
+ return -EINVAL;
+}
+
+static inline int sr_vp_disable_both(u32 target_opp, u32 current_opp)
+{
+ return -EINVAL;
+}
+
+static inline int sr_vp_enable_both(u32 target_opp, u32 current_opp)
+{
+ return -EINVAL;
+}
+
+static inline int vc_send_command(u8 slave_addr, u8 reg_addr, u8 data,
+ u16 *timeout_us)
+{
+ return -EINVAL;
+}
+
+#ifdef CONFIG_OMAP_VC_BYPASS_UPDATE
+static inline int omap_pmic_voltage_cmds(u8 srid, u8 target_vsel)
+{
+ return -EINVAL;
+}
+#endif
+#endif
+
+#ifdef __SR_DEBUG
+int sr_debugfs_create_entries(struct dentry *d);
+#else
+static inline int sr_debugfs_create_entries(struct dentry *d)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif /* __ARCH_ARM_MACH_OMAP3_SMARTREFLEX_H */
@@ -81,19 +81,14 @@ config OMAP_SMARTREFLEX
compensation for VDD1 and VDD2, user must write 1 to
/sys/power/sr_vddX_autocomp, where X is 1 or 2.
-config OMAP_SMARTREFLEX_TESTING
- bool "Smartreflex testing support"
+config OMAP_VC_BYPASS_UPDATE
+ bool "Use VC Bypass method of updating voltage"
depends on OMAP_SMARTREFLEX
default n
help
- Say Y if you want to enable SmartReflex testing with SW hardcoded
- NVALUES intead of E-fuse NVALUES set in factory silicon testing.
-
- In some devices the E-fuse values have not been set, even though
- SmartReflex modules are included. Using these hardcoded values set
- in software, one can test the SmartReflex features without E-fuse.
-
- WARNING: Enabling this option may cause your device to hang!
+ Say Y if you would like to use VC Bypass method of
+ update. If this is disabled, the default
+ VP forceupdate method is used.
config OMAP_RESET_CLOCKS
bool "Reset unused clocks during boot"