* Kalle Jokiniemi <[EMAIL PROTECTED]> [080617 10:22]:
> This patch integrates TI's SmartReflex driver into linux-omap. SmartReflex is
> a module that adjusts OMAP3 VDD1 and VDD2 operating voltages around the
> nominal values of current operating point depending on silicon
> characteristics and operating conditions.
> 
> The patch adds Kconfig options "SmartReflex support" and a sub-option
> "SmartReflex testing support" under "System type"->"TI OMAP implementations"
> menu. The testing support can be used to test SmartReflex functionality, if
> the E-fuse values have not been set for the device. It however uses software
> hard coded sensor parameters, which may not work on all devices. Beware.
> 
> The driver creates two sysfs entries into /sys/power/ named
> "sr_vdd1_autocomp" and "sr_vdd2_autocomp" which can be used to activate
> voltage autocompensation feature of SmartReflex modules 1 and 2.
> 
> Use the following commands to enable autocompensation:
> 
> echo -n 1 > /sys/power/sr_vdd1_autocomp
> echo -n 1 > /sys/power/sr_vdd2_autocomp
> 
> To disable:
> 
> echo -n 0 > /sys/power/sr_vdd1_autocomp
> echo -n 0 > /sys/power/sr_vdd2_autocomp

Pushing today.

Tony

> 
> Signed-off-by: Kalle Jokiniemi <[EMAIL PROTECTED]>
> ---
>  arch/arm/mach-omap2/Makefile      |    3 +
>  arch/arm/mach-omap2/pm34xx.c      |    9 +
>  arch/arm/mach-omap2/smartreflex.c |  871 
> +++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-omap2/smartreflex.h |   21 +
>  arch/arm/plat-omap/Kconfig        |   31 ++
>  5 files changed, 935 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/mach-omap2/smartreflex.c
> 
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index 5a572b0..38d9b29 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -24,6 +24,9 @@ obj-$(CONFIG_ARCH_OMAP3)            += pm34xx.o sleep34xx.o
>  obj-$(CONFIG_PM_DEBUG)                       += pm-debug.o
>  endif
>  
> +# SmartReflex driver
> +obj-$(CONFIG_OMAP_SMARTREFLEX)               += smartreflex.o
> +
>  # Clock framework
>  obj-$(CONFIG_ARCH_OMAP2)             += clock24xx.o
>  obj-$(CONFIG_ARCH_OMAP3)             += clock34xx.o
> diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
> index 40a5828..c7493f5 100644
> --- a/arch/arm/mach-omap2/pm34xx.c
> +++ b/arch/arm/mach-omap2/pm34xx.c
> @@ -36,6 +36,7 @@
>  
>  #include "prm.h"
>  #include "pm.h"
> +#include "smartreflex.h"
>  
>  struct power_state {
>       struct powerdomain *pwrdm;
> @@ -259,6 +260,10 @@ static int omap3_pm_suspend(void)
>       struct power_state *pwrst;
>       int state, ret = 0;
>  
> +     /* XXX Disable smartreflex before entering suspend */
> +     disable_smartreflex(SR1);
> +     disable_smartreflex(SR2);
> +
>       /* Read current next_pwrsts */
>       list_for_each_entry(pwrst, &pwrst_list, node)
>               pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
> @@ -290,6 +295,10 @@ restore:
>               printk(KERN_INFO "Successfully put all powerdomains "
>                      "to target state\n");
>  
> +     /* XXX Enable smartreflex after suspend */
> +     enable_smartreflex(SR1);
> +     enable_smartreflex(SR2);
> +
>       return ret;
>  }
>  
> diff --git a/arch/arm/mach-omap2/smartreflex.c 
> b/arch/arm/mach-omap2/smartreflex.c
> new file mode 100644
> index 0000000..0f3a659
> --- /dev/null
> +++ b/arch/arm/mach-omap2/smartreflex.c
> @@ -0,0 +1,871 @@
> +/*
> + * linux/arch/arm/mach-omap3/smartreflex.c
> + *
> + * OMAP34XX SmartReflex Voltage Control
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Kalle Jokiniemi
> + *
> + * Copyright (C) 2007 Texas Instruments, Inc.
> + * Lesly A M <[EMAIL PROTECTED]>
> + *
> + * 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 <asm/arch/omap34xx.h>
> +#include <asm/arch/control.h>
> +#include <asm/arch/clock.h>
> +
> +#include "prm.h"
> +#include "smartreflex.h"
> +#include "prm-regbits-34xx.h"
> +
> +/* XXX: These should be relocated where-ever the OPP implementation will be 
> */
> +u32 current_vdd1_opp;
> +u32 current_vdd2_opp;
> +
> +struct omap_sr {
> +     int             srid;
> +     int             is_sr_reset;
> +     int             is_autocomp_active;
> +     struct clk      *clk;
> +     u32             clk_length;
> +     u32             req_opp_no;
> +     u32             opp1_nvalue, opp2_nvalue, opp3_nvalue, opp4_nvalue;
> +     u32             opp5_nvalue;
> +     u32             senp_mod, senn_mod;
> +     u32             srbase_addr;
> +     u32             vpbase_addr;
> +};
> +
> +/* Custom clocks to enable SR specific enable/disable functions. */
> +struct sr_custom_clk {
> +     struct clk      clk;  /* meta-clock with custom enable/disable calls */
> +     struct clk      *fck; /* actual functional clock */
> +     struct omap_sr  *sr;
> +};
> +
> +static inline void sr_write_reg(struct omap_sr *sr, int offset, u32 value)
> +{
> +     __raw_writel(value, sr->srbase_addr + offset);
> +}
> +
> +static inline void sr_modify_reg(struct omap_sr *sr, int offset, u32 mask,
> +                                                             u32 value)
> +{
> +     u32 reg_val;
> +
> +     reg_val = __raw_readl(sr->srbase_addr + offset);
> +     reg_val &= ~mask;
> +     reg_val |= value;
> +
> +     __raw_writel(reg_val, sr->srbase_addr + offset);
> +}
> +
> +static inline u32 sr_read_reg(struct omap_sr *sr, int offset)
> +{
> +     return __raw_readl(sr->srbase_addr + offset);
> +}
> +
> +/* Custom clock handling functions */
> +static int sr_clk_enable(struct clk *clk)
> +{
> +     struct sr_custom_clk *sr_clk = container_of(clk, struct sr_custom_clk,
> +                                                                     clk);
> +
> +     if (clk_enable(sr_clk->fck) != 0) {
> +             printk(KERN_ERR "Could not enable %s\n", sr_clk->fck->name);
> +             goto clk_enable_err;
> +     }
> +
> +     /* set fclk- active , iclk- idle */
> +     sr_modify_reg(sr_clk->sr, ERRCONFIG, SR_CLKACTIVITY_MASK,
> +                                             SR_CLKACTIVITY_IOFF_FON);
> +
> +     return 0;
> +
> +clk_enable_err:
> +     return -1;
> +}
> +
> +static void sr_clk_disable(struct clk *clk)
> +{
> +     struct sr_custom_clk *sr_clk = container_of(clk, struct sr_custom_clk,
> +                                                                     clk);
> +
> +     /* set fclk, iclk- idle */
> +     sr_modify_reg(sr_clk->sr, ERRCONFIG, SR_CLKACTIVITY_MASK,
> +                                             SR_CLKACTIVITY_IOFF_FOFF);
> +
> +     clk_disable(sr_clk->fck);
> +     sr_clk->sr->is_sr_reset = 1;
> +}
> +
> +static struct omap_sr sr1 = {
> +     .srid                   = SR1,
> +     .is_sr_reset            = 1,
> +     .is_autocomp_active     = 0,
> +     .clk_length             = 0,
> +     .srbase_addr            = OMAP2_IO_ADDRESS(OMAP34XX_SR1_BASE),
> +};
> +
> +static struct omap_sr sr2 = {
> +     .srid                   = SR2,
> +     .is_sr_reset            = 1,
> +     .is_autocomp_active     = 0,
> +     .clk_length             = 0,
> +     .srbase_addr            = OMAP2_IO_ADDRESS(OMAP34XX_SR2_BASE),
> +};
> +
> +static struct sr_custom_clk sr1_custom_clk = {
> +     .clk = {
> +                     .name           = "sr1_custom_clk",
> +                     .enable         = sr_clk_enable,
> +                     .disable        = sr_clk_disable,
> +     },
> +     .sr     = &sr1,
> +};
> +
> +static struct sr_custom_clk sr2_custom_clk = {
> +     .clk = {
> +                     .name           = "sr2_custom_clk",
> +                     .enable         = sr_clk_enable,
> +                     .disable        = sr_clk_disable,
> +     },
> +     .sr     = &sr2,
> +};
> +
> +static void cal_reciprocal(u32 sensor, u32 *sengain, u32 *rnsen)
> +{
> +     u32 gn, rn, mul;
> +
> +     for (gn = 0; gn < GAIN_MAXLIMIT; gn++) {
> +             mul = 1 << (gn + 8);
> +             rn = mul / sensor;
> +             if (rn < R_MAXLIMIT) {
> +                     *sengain = gn;
> +                     *rnsen = rn;
> +             }
> +     }
> +}
> +
> +static u32 cal_test_nvalue(u32 sennval, u32 senpval)
> +{
> +     u32 senpgain, senngain;
> +     u32 rnsenp, rnsenn;
> +
> +     /* Calculating the gain and reciprocal of the SenN and SenP values */
> +     cal_reciprocal(senpval, &senpgain, &rnsenp);
> +     cal_reciprocal(sennval, &senngain, &rnsenn);
> +
> +     return ((senpgain << NVALUERECIPROCAL_SENPGAIN_SHIFT) |
> +             (senngain << NVALUERECIPROCAL_SENNGAIN_SHIFT) |
> +             (rnsenp << NVALUERECIPROCAL_RNSENP_SHIFT) |
> +             (rnsenn << NVALUERECIPROCAL_RNSENN_SHIFT));
> +}
> +
> +static void sr_clk_init(struct sr_custom_clk *sr_clk)
> +{
> +     if (sr_clk->sr->srid == SR1) {
> +             sr_clk->fck = clk_get(NULL, "sr1_fck");
> +             if (IS_ERR(sr_clk->fck))
> +                     printk(KERN_ERR "Could not get sr1_fck\n");
> +     } else if (sr_clk->sr->srid == SR2) {
> +             sr_clk->fck = clk_get(NULL, "sr2_fck");
> +             if (IS_ERR(sr_clk->fck))
> +                     printk(KERN_ERR "Could not get sr2_fck\n");
> +     }
> +     clk_register(&sr_clk->clk);
> +}
> +
> +static void sr_set_clk_length(struct omap_sr *sr)
> +{
> +     struct clk *osc_sys_ck;
> +     u32 sys_clk = 0;
> +
> +     osc_sys_ck = clk_get(NULL, "osc_sys_ck");
> +     sys_clk = clk_get_rate(osc_sys_ck);
> +     clk_put(osc_sys_ck);
> +
> +     switch (sys_clk) {
> +     case 12000000:
> +             sr->clk_length = SRCLKLENGTH_12MHZ_SYSCLK;
> +             break;
> +     case 13000000:
> +             sr->clk_length = SRCLKLENGTH_13MHZ_SYSCLK;
> +             break;
> +     case 19200000:
> +             sr->clk_length = SRCLKLENGTH_19MHZ_SYSCLK;
> +             break;
> +     case 26000000:
> +             sr->clk_length = SRCLKLENGTH_26MHZ_SYSCLK;
> +             break;
> +     case 38400000:
> +             sr->clk_length = SRCLKLENGTH_38MHZ_SYSCLK;
> +             break;
> +     default :
> +             printk(KERN_ERR "Invalid sysclk value: %d\n", sys_clk);
> +             break;
> +     }
> +}
> +
> +static void sr_set_efuse_nvalues(struct omap_sr *sr)
> +{
> +     if (sr->srid == SR1) {
> +             sr->senn_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
> +                                     OMAP343X_SR1_SENNENABLE_MASK) >>
> +                                     OMAP343X_SR1_SENNENABLE_SHIFT;
> +
> +             sr->senp_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
> +                                     OMAP343X_SR1_SENPENABLE_MASK) >>
> +                                     OMAP343X_SR1_SENPENABLE_SHIFT;
> +
> +             sr->opp5_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP5_VDD1);
> +             sr->opp4_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP4_VDD1);
> +             sr->opp3_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP3_VDD1);
> +             sr->opp2_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP2_VDD1);
> +             sr->opp1_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP1_VDD1);
> +     } else if (sr->srid == SR2) {
> +             sr->senn_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
> +                                     OMAP343X_SR2_SENNENABLE_MASK) >>
> +                                     OMAP343X_SR2_SENNENABLE_SHIFT;
> +
> +             sr->senp_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
> +                                     OMAP343X_SR2_SENPENABLE_MASK) >>
> +                                     OMAP343X_SR2_SENPENABLE_SHIFT;
> +
> +             sr->opp3_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP3_VDD2);
> +             sr->opp2_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP2_VDD2);
> +             sr->opp1_nvalue = omap_ctrl_readl(
> +                                     OMAP343X_CONTROL_FUSE_OPP1_VDD2);
> +     }
> +}
> +
> +/* Hard coded nvalues for testing purposes, may cause device to hang! */
> +static void sr_set_testing_nvalues(struct omap_sr *sr)
> +{
> +     if (sr->srid == SR1) {
> +             sr->senp_mod = 0x03;    /* SenN-M5 enabled */
> +             sr->senn_mod = 0x03;
> +
> +             /* calculate nvalues for each opp */
> +             sr->opp5_nvalue = cal_test_nvalue(0xacd + 0x330, 0x848 + 0x330);
> +             sr->opp4_nvalue = cal_test_nvalue(0x964 + 0x2a0, 0x727 + 0x2a0);
> +             sr->opp3_nvalue = cal_test_nvalue(0x85b + 0x200, 0x655 + 0x200);
> +             sr->opp2_nvalue = cal_test_nvalue(0x506 + 0x1a0, 0x3be + 0x1a0);
> +             sr->opp1_nvalue = cal_test_nvalue(0x373 + 0x100, 0x28c + 0x100);
> +     } else if (sr->srid == SR2) {
> +             sr->senp_mod = 0x03;
> +             sr->senn_mod = 0x03;
> +
> +             sr->opp3_nvalue = cal_test_nvalue(0x76f + 0x200, 0x579 + 0x200);
> +             sr->opp2_nvalue = cal_test_nvalue(0x4f5 + 0x1c0, 0x390 + 0x1c0);
> +             sr->opp1_nvalue = cal_test_nvalue(0x359, 0x25d);
> +     }
> +
> +}
> +
> +static void sr_set_nvalues(struct omap_sr *sr)
> +{
> +     if (SR_TESTING_NVALUES)
> +             sr_set_testing_nvalues(sr);
> +     else
> +             sr_set_efuse_nvalues(sr);
> +}
> +
> +static void sr_configure_vp(int srid)
> +{
> +     u32 vpconfig;
> +
> +     if (srid == SR1) {
> +             vpconfig = PRM_VP1_CONFIG_ERROROFFSET | PRM_VP1_CONFIG_ERRORGAIN
> +                                     | PRM_VP1_CONFIG_INITVOLTAGE
> +                                     | PRM_VP1_CONFIG_TIMEOUTEN;
> +
> +             prm_write_mod_reg(vpconfig, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP1_CONFIG_OFFSET);
> +             prm_write_mod_reg(PRM_VP1_VSTEPMIN_SMPSWAITTIMEMIN |
> +                                     PRM_VP1_VSTEPMIN_VSTEPMIN,
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP1_VSTEPMIN_OFFSET);
> +
> +             prm_write_mod_reg(PRM_VP1_VSTEPMAX_SMPSWAITTIMEMAX |
> +                                     PRM_VP1_VSTEPMAX_VSTEPMAX,
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP1_VSTEPMAX_OFFSET);
> +
> +             prm_write_mod_reg(PRM_VP1_VLIMITTO_VDDMAX |
> +                                     PRM_VP1_VLIMITTO_VDDMIN |
> +                                     PRM_VP1_VLIMITTO_TIMEOUT,
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP1_VLIMITTO_OFFSET);
> +
> +             /* Trigger initVDD value copy to voltage processor */
> +             prm_set_mod_reg_bits(PRM_VP1_CONFIG_INITVDD, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP1_CONFIG_OFFSET);
> +             /* Clear initVDD copy trigger bit */
> +             prm_clear_mod_reg_bits(PRM_VP1_CONFIG_INITVDD, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP1_CONFIG_OFFSET);
> +
> +     } else if (srid == SR2) {
> +             vpconfig = PRM_VP2_CONFIG_ERROROFFSET | PRM_VP2_CONFIG_ERRORGAIN
> +                                     | PRM_VP2_CONFIG_INITVOLTAGE
> +                                     | PRM_VP2_CONFIG_TIMEOUTEN;
> +
> +             prm_write_mod_reg(vpconfig, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP2_CONFIG_OFFSET);
> +             prm_write_mod_reg(PRM_VP2_VSTEPMIN_SMPSWAITTIMEMIN |
> +                                     PRM_VP2_VSTEPMIN_VSTEPMIN,
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP2_VSTEPMIN_OFFSET);
> +
> +             prm_write_mod_reg(PRM_VP2_VSTEPMAX_SMPSWAITTIMEMAX |
> +                                     PRM_VP2_VSTEPMAX_VSTEPMAX,
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP2_VSTEPMAX_OFFSET);
> +
> +             prm_write_mod_reg(PRM_VP2_VLIMITTO_VDDMAX |
> +                                     PRM_VP2_VLIMITTO_VDDMIN |
> +                                     PRM_VP2_VLIMITTO_TIMEOUT,
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP2_VLIMITTO_OFFSET);
> +
> +             /* Trigger initVDD value copy to voltage processor */
> +             prm_set_mod_reg_bits(PRM_VP2_CONFIG_INITVDD, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP2_CONFIG_OFFSET);
> +             /* Reset initVDD copy trigger bit */
> +             prm_clear_mod_reg_bits(PRM_VP2_CONFIG_INITVDD, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP2_CONFIG_OFFSET);
> +
> +     }
> +}
> +
> +static void sr_configure_vc(void)
> +{
> +     prm_write_mod_reg((R_SRI2C_SLAVE_ADDR << OMAP3430_SMPS_SA1_SHIFT) |
> +                     (R_SRI2C_SLAVE_ADDR << OMAP3430_SMPS_SA0_SHIFT),
> +                     OMAP3430_GR_MOD, OMAP3_PRM_VC_SMPS_SA_OFFSET);
> +
> +     prm_write_mod_reg((R_VDD2_SR_CONTROL << OMAP3430_VOLRA1_SHIFT) |
> +                     (R_VDD1_SR_CONTROL << OMAP3430_VOLRA0_SHIFT),
> +                     OMAP3430_GR_MOD, OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET);
> +
> +     prm_write_mod_reg((OMAP3430_VC_CMD_VAL0_ON <<
> +             OMAP3430_VC_CMD_ON_SHIFT) |
> +             (OMAP3430_VC_CMD_VAL0_ONLP << OMAP3430_VC_CMD_ONLP_SHIFT) |
> +             (OMAP3430_VC_CMD_VAL0_RET << OMAP3430_VC_CMD_RET_SHIFT) |
> +             (OMAP3430_VC_CMD_VAL0_OFF << OMAP3430_VC_CMD_OFF_SHIFT),
> +             OMAP3430_GR_MOD, OMAP3_PRM_VC_CMD_VAL_0_OFFSET);
> +
> +     prm_write_mod_reg((OMAP3430_VC_CMD_VAL1_ON <<
> +             OMAP3430_VC_CMD_ON_SHIFT) |
> +             (OMAP3430_VC_CMD_VAL1_ONLP << OMAP3430_VC_CMD_ONLP_SHIFT) |
> +             (OMAP3430_VC_CMD_VAL1_RET << OMAP3430_VC_CMD_RET_SHIFT) |
> +             (OMAP3430_VC_CMD_VAL1_OFF << OMAP3430_VC_CMD_OFF_SHIFT),
> +             OMAP3430_GR_MOD, OMAP3_PRM_VC_CMD_VAL_1_OFFSET);
> +
> +     prm_write_mod_reg(OMAP3430_CMD1 | OMAP3430_RAV1,
> +                             OMAP3430_GR_MOD,
> +                             OMAP3_PRM_VC_CH_CONF_OFFSET);
> +
> +     prm_write_mod_reg(OMAP3430_MCODE_SHIFT | OMAP3430_HSEN | OMAP3430_SREN,
> +                             OMAP3430_GR_MOD,
> +                             OMAP3_PRM_VC_I2C_CFG_OFFSET);
> +
> +     /* Setup voltctrl and other setup times */
> +     /* XXX CONFIG_SYSOFFMODE has not been implemented yet */
> +#ifdef CONFIG_SYSOFFMODE
> +     prm_write_mod_reg(OMAP3430_AUTO_OFF | OMAP3430_AUTO_RET,
> +                     OMAP3430_GR_MOD,
> +                     OMAP3_PRM_VOLTCTRL_OFFSET);
> +
> +     prm_write_mod_reg(OMAP3430_CLKSETUP_DURATION, OMAP3430_GR_MOD,
> +                     OMAP3_PRM_CLKSETUP_OFFSET);
> +     prm_write_mod_reg((OMAP3430_VOLTSETUP_TIME2 <<
> +                     OMAP3430_VOLTSETUP_TIME2_OFFSET) |
> +                     (OMAP3430_VOLTSETUP_TIME1 <<
> +                     OMAP3430_VOLTSETUP_TIME1_OFFSET),
> +                     OMAP3430_GR_MOD, OMAP3_PRM_VOLTSETUP1_OFFSET);
> +
> +     prm_write_mod_reg(OMAP3430_VOLTOFFSET_DURATION, OMAP3430_GR_MOD,
> +                     OMAP3_PRM_VOLTOFFSET_OFFSET);
> +     prm_write_mod_reg(OMAP3430_VOLTSETUP2_DURATION, OMAP3430_GR_MOD,
> +                     OMAP3_PRM_VOLTSETUP2_OFFSET);
> +#else
> +     prm_set_mod_reg_bits(OMAP3430_AUTO_RET, OMAP3430_GR_MOD,
> +                     OMAP3_PRM_VOLTCTRL_OFFSET);
> +#endif
> +
> +}
> +
> +static void sr_configure(struct omap_sr *sr)
> +{
> +     u32 sr_config;
> +     u32 senp_en , senn_en;
> +
> +     if (sr->clk_length == 0)
> +             sr_set_clk_length(sr);
> +
> +     senp_en = sr->senp_mod;
> +     senn_en = sr->senn_mod;
> +     if (sr->srid == SR1) {
> +             sr_config = SR1_SRCONFIG_ACCUMDATA |
> +                     (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) |
> +                     SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN |
> +                     SRCONFIG_MINMAXAVG_EN |
> +                     (senn_en << SRCONFIG_SENNENABLE_SHIFT) |
> +                     (senp_en << SRCONFIG_SENPENABLE_SHIFT) |
> +                     SRCONFIG_DELAYCTRL;
> +
> +             sr_write_reg(sr, SRCONFIG, sr_config);
> +             sr_write_reg(sr, AVGWEIGHT, SR1_AVGWEIGHT_SENPAVGWEIGHT |
> +                                     SR1_AVGWEIGHT_SENNAVGWEIGHT);
> +
> +             sr_modify_reg(sr, ERRCONFIG, (SR_ERRWEIGHT_MASK |
> +                     SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK),
> +                     (SR1_ERRWEIGHT | SR1_ERRMAXLIMIT | SR1_ERRMINLIMIT));
> +
> +     } else if (sr->srid == SR2) {
> +             sr_config = SR2_SRCONFIG_ACCUMDATA |
> +                     (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) |
> +                     SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN |
> +                     SRCONFIG_MINMAXAVG_EN |
> +                     (senn_en << SRCONFIG_SENNENABLE_SHIFT) |
> +                     (senp_en << SRCONFIG_SENPENABLE_SHIFT) |
> +                     SRCONFIG_DELAYCTRL;
> +
> +             sr_write_reg(sr, SRCONFIG, sr_config);
> +             sr_write_reg(sr, AVGWEIGHT, SR2_AVGWEIGHT_SENPAVGWEIGHT |
> +                                     SR2_AVGWEIGHT_SENNAVGWEIGHT);
> +             sr_modify_reg(sr, ERRCONFIG, (SR_ERRWEIGHT_MASK |
> +                     SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK),
> +                     (SR2_ERRWEIGHT | SR2_ERRMAXLIMIT | SR2_ERRMINLIMIT));
> +
> +     }
> +     sr->is_sr_reset = 0;
> +}
> +
> +static int sr_enable(struct omap_sr *sr, u32 target_opp_no)
> +{
> +     u32 nvalue_reciprocal;
> +
> +     sr->req_opp_no = target_opp_no;
> +
> +     if (sr->srid == SR1) {
> +             switch (target_opp_no) {
> +             case 5:
> +                     nvalue_reciprocal = sr->opp5_nvalue;
> +                     break;
> +             case 4:
> +                     nvalue_reciprocal = sr->opp4_nvalue;
> +                     break;
> +             case 3:
> +                     nvalue_reciprocal = sr->opp3_nvalue;
> +                     break;
> +             case 2:
> +                     nvalue_reciprocal = sr->opp2_nvalue;
> +                     break;
> +             case 1:
> +                     nvalue_reciprocal = sr->opp1_nvalue;
> +                     break;
> +             default:
> +                     nvalue_reciprocal = sr->opp3_nvalue;
> +                     break;
> +             }
> +     } else {
> +             switch (target_opp_no) {
> +             case 3:
> +                     nvalue_reciprocal = sr->opp3_nvalue;
> +                     break;
> +             case 2:
> +                     nvalue_reciprocal = sr->opp2_nvalue;
> +                     break;
> +             case 1:
> +                     nvalue_reciprocal = sr->opp1_nvalue;
> +                     break;
> +             default:
> +                     nvalue_reciprocal = sr->opp3_nvalue;
> +                     break;
> +             }
> +     }
> +
> +     if (nvalue_reciprocal == 0) {
> +             printk(KERN_NOTICE "OPP%d doesn't support SmartReflex\n",
> +                                                             target_opp_no);
> +             return SR_FALSE;
> +     }
> +
> +     sr_write_reg(sr, NVALUERECIPROCAL, nvalue_reciprocal);
> +
> +     /* Enable the interrupt */
> +     sr_modify_reg(sr, ERRCONFIG,
> +                     (ERRCONFIG_VPBOUNDINTEN | ERRCONFIG_VPBOUNDINTST),
> +                     (ERRCONFIG_VPBOUNDINTEN | ERRCONFIG_VPBOUNDINTST));
> +     if (sr->srid == SR1) {
> +             /* Enable VP1 */
> +             prm_set_mod_reg_bits(PRM_VP1_CONFIG_VPENABLE, OMAP3430_GR_MOD,
> +                             OMAP3_PRM_VP1_CONFIG_OFFSET);
> +     } else if (sr->srid == SR2) {
> +             /* Enable VP2 */
> +             prm_set_mod_reg_bits(PRM_VP2_CONFIG_VPENABLE, OMAP3430_GR_MOD,
> +                             OMAP3_PRM_VP2_CONFIG_OFFSET);
> +     }
> +
> +     /* SRCONFIG - enable SR */
> +     sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE);
> +     return SR_TRUE;
> +}
> +
> +static void sr_disable(struct omap_sr *sr)
> +{
> +     sr->is_sr_reset = 1;
> +
> +     /* SRCONFIG - disable SR */
> +     sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, ~SRCONFIG_SRENABLE);
> +
> +     if (sr->srid == SR1) {
> +             /* Disable VP1 */
> +             prm_clear_mod_reg_bits(PRM_VP1_CONFIG_VPENABLE, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP1_CONFIG_OFFSET);
> +     } else if (sr->srid == SR2) {
> +             /* Disable VP2 */
> +             prm_clear_mod_reg_bits(PRM_VP2_CONFIG_VPENABLE, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VP2_CONFIG_OFFSET);
> +     }
> +}
> +
> +
> +void sr_start_vddautocomap(int srid, u32 target_opp_no)
> +{
> +     struct omap_sr *sr = NULL;
> +
> +     if (srid == SR1)
> +             sr = &sr1;
> +     else if (srid == SR2)
> +             sr = &sr2;
> +
> +     if (sr->is_sr_reset == 1) {
> +             clk_enable(sr->clk);
> +             sr_configure(sr);
> +     }
> +
> +     if (sr->is_autocomp_active == 1)
> +             printk(KERN_WARNING "SR%d: VDD autocomp is already active\n",
> +                                                                     srid);
> +
> +     sr->is_autocomp_active = 1;
> +     if (!sr_enable(sr, target_opp_no)) {
> +             printk(KERN_WARNING "SR%d: VDD autocomp not activated\n", srid);
> +             sr->is_autocomp_active = 0;
> +             if (sr->is_sr_reset == 1)
> +                     clk_disable(sr->clk);
> +     }
> +}
> +EXPORT_SYMBOL(sr_start_vddautocomap);
> +
> +int sr_stop_vddautocomap(int srid)
> +{
> +     struct omap_sr *sr = NULL;
> +
> +     if (srid == SR1)
> +             sr = &sr1;
> +     else if (srid == SR2)
> +             sr = &sr2;
> +
> +     if (sr->is_autocomp_active == 1) {
> +             sr_disable(sr);
> +             clk_disable(sr->clk);
> +             sr->is_autocomp_active = 0;
> +             return SR_TRUE;
> +     } else {
> +             printk(KERN_WARNING "SR%d: VDD autocomp is not active\n",
> +                                                             srid);
> +             return SR_FALSE;
> +     }
> +
> +}
> +EXPORT_SYMBOL(sr_stop_vddautocomap);
> +
> +void enable_smartreflex(int srid)
> +{
> +     u32 target_opp_no = 0;
> +     struct omap_sr *sr = NULL;
> +
> +     if (srid == SR1)
> +             sr = &sr1;
> +     else if (srid == SR2)
> +             sr = &sr2;
> +
> +     if (sr->is_autocomp_active == 1) {
> +             if (sr->is_sr_reset == 1) {
> +                     /* Enable SR clks */
> +                     clk_enable(sr->clk);
> +
> +                     if (srid == SR1)
> +                             target_opp_no = get_opp_no(current_vdd1_opp);
> +                     else if (srid == SR2)
> +                             target_opp_no = get_opp_no(current_vdd2_opp);
> +
> +                     sr_configure(sr);
> +
> +                     if (!sr_enable(sr, target_opp_no))
> +                             clk_disable(sr->clk);
> +             }
> +     }
> +}
> +
> +void disable_smartreflex(int srid)
> +{
> +     struct omap_sr *sr = NULL;
> +
> +     if (srid == SR1)
> +             sr = &sr1;
> +     else if (srid == SR2)
> +             sr = &sr2;
> +
> +     if (sr->is_autocomp_active == 1) {
> +             if (sr->is_sr_reset == 0) {
> +
> +                     sr->is_sr_reset = 1;
> +                     /* SRCONFIG - disable SR */
> +                     sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE,
> +                                                     ~SRCONFIG_SRENABLE);
> +
> +                     /* Disable SR clk */
> +                     clk_disable(sr->clk);
> +                     if (sr->srid == SR1) {
> +                             /* Disable VP1 */
> +                             prm_clear_mod_reg_bits(PRM_VP1_CONFIG_VPENABLE,
> +                                             OMAP3430_GR_MOD,
> +                                             OMAP3_PRM_VP1_CONFIG_OFFSET);
> +                     } else if (sr->srid == SR2) {
> +                             /* Disable VP2 */
> +                             prm_clear_mod_reg_bits(PRM_VP2_CONFIG_VPENABLE,
> +                                             OMAP3430_GR_MOD,
> +                                             OMAP3_PRM_VP2_CONFIG_OFFSET);
> +                     }
> +             }
> +     }
> +}
> +
> +/* Voltage Scaling using SR VCBYPASS */
> +int sr_voltagescale_vcbypass(u32 target_opp, u8 vsel)
> +{
> +     int sr_status = 0;
> +     u32 vdd, target_opp_no;
> +     u32 vc_bypass_value;
> +     u32 reg_addr = 0;
> +     u32 loop_cnt = 0, retries_cnt = 0;
> +
> +     vdd = get_vdd(target_opp);
> +     target_opp_no = get_opp_no(target_opp);
> +
> +     if (vdd == PRCM_VDD1) {
> +             sr_status = sr_stop_vddautocomap(SR1);
> +
> +             prm_rmw_mod_reg_bits(OMAP3430_VC_CMD_ON_MASK,
> +                                     (vsel << OMAP3430_VC_CMD_ON_SHIFT),
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VC_CMD_VAL_0_OFFSET);
> +             reg_addr = R_VDD1_SR_CONTROL;
> +
> +     } else if (vdd == PRCM_VDD2) {
> +             sr_status = sr_stop_vddautocomap(SR2);
> +
> +             prm_rmw_mod_reg_bits(OMAP3430_VC_CMD_ON_MASK,
> +                                     (vsel << OMAP3430_VC_CMD_ON_SHIFT),
> +                                     OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VC_CMD_VAL_1_OFFSET);
> +             reg_addr = R_VDD2_SR_CONTROL;
> +     }
> +
> +     vc_bypass_value = (vsel << OMAP3430_DATA_SHIFT) |
> +                     (reg_addr << OMAP3430_REGADDR_SHIFT) |
> +                     (R_SRI2C_SLAVE_ADDR << OMAP3430_SLAVEADDR_SHIFT);
> +
> +     prm_write_mod_reg(vc_bypass_value, OMAP3430_GR_MOD,
> +                     OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
> +
> +     vc_bypass_value = prm_set_mod_reg_bits(OMAP3430_VALID, OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
> +
> +     while ((vc_bypass_value & OMAP3430_VALID) != 0x0) {
> +             loop_cnt++;
> +             if (retries_cnt > 10) {
> +                     printk(KERN_INFO "Loop count exceeded in check SR I2C"
> +                                                             "write\n");
> +                     return SR_FAIL;
> +             }
> +             if (loop_cnt > 50) {
> +                     retries_cnt++;
> +                     loop_cnt = 0;
> +                     udelay(10);
> +             }
> +             vc_bypass_value = prm_read_mod_reg(OMAP3430_GR_MOD,
> +                                     OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
> +     }
> +
> +     udelay(T2_SMPS_UPDATE_DELAY);
> +
> +     if (sr_status) {
> +             if (vdd == PRCM_VDD1)
> +                     sr_start_vddautocomap(SR1, target_opp_no);
> +             else if (vdd == PRCM_VDD2)
> +                     sr_start_vddautocomap(SR2, target_opp_no);
> +     }
> +
> +     return SR_PASS;
> +}
> +
> +/* Sysfs interface to select SR VDD1 auto compensation */
> +static ssize_t omap_sr_vdd1_autocomp_show(struct kobject *kobj,
> +                                     struct kobj_attribute *attr, char *buf)
> +{
> +     return sprintf(buf, "%d\n", sr1.is_autocomp_active);
> +}
> +
> +static ssize_t omap_sr_vdd1_autocomp_store(struct kobject *kobj,
> +                                     struct kobj_attribute *attr,
> +                                     const char *buf, size_t n)
> +{
> +     u32 current_vdd1opp_no;
> +     unsigned short value;
> +
> +     if (sscanf(buf, "%hu", &value) != 1 || (value > 1)) {
> +             printk(KERN_ERR "sr_vdd1_autocomp: Invalid value\n");
> +             return -EINVAL;
> +     }
> +
> +     current_vdd1opp_no = get_opp_no(current_vdd1_opp);
> +
> +     if (value == 0)
> +             sr_stop_vddautocomap(SR1);
> +     else
> +             sr_start_vddautocomap(SR1, current_vdd1opp_no);
> +
> +     return n;
> +}
> +
> +static struct kobj_attribute sr_vdd1_autocomp = {
> +     .attr = {
> +     .name = __stringify(sr_vdd1_autocomp),
> +     .mode = 0644,
> +     },
> +     .show = omap_sr_vdd1_autocomp_show,
> +     .store = omap_sr_vdd1_autocomp_store,
> +};
> +
> +/* Sysfs interface to select SR VDD2 auto compensation */
> +static ssize_t omap_sr_vdd2_autocomp_show(struct kobject *kobj,
> +                                     struct kobj_attribute *attr, char *buf)
> +{
> +     return sprintf(buf, "%d\n", sr2.is_autocomp_active);
> +}
> +
> +static ssize_t omap_sr_vdd2_autocomp_store(struct kobject *kobj,
> +                                     struct kobj_attribute *attr,
> +                                     const char *buf, size_t n)
> +{
> +     u32 current_vdd2opp_no;
> +     unsigned short value;
> +
> +     if (sscanf(buf, "%hu", &value) != 1 || (value > 1)) {
> +             printk(KERN_ERR "sr_vdd2_autocomp: Invalid value\n");
> +             return -EINVAL;
> +     }
> +
> +     current_vdd2opp_no = get_opp_no(current_vdd2_opp);
> +
> +     if (value == 0)
> +             sr_stop_vddautocomap(SR2);
> +     else
> +             sr_start_vddautocomap(SR2, current_vdd2opp_no);
> +
> +     return n;
> +}
> +
> +static struct kobj_attribute sr_vdd2_autocomp = {
> +     .attr = {
> +     .name = __stringify(sr_vdd2_autocomp),
> +     .mode = 0644,
> +     },
> +     .show = omap_sr_vdd2_autocomp_show,
> +     .store = omap_sr_vdd2_autocomp_store,
> +};
> +
> +
> +
> +static int __init omap3_sr_init(void)
> +{
> +     int ret = 0;
> +     u8 RdReg;
> +
> +     if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
> +             current_vdd1_opp = PRCM_VDD1_OPP3;
> +             current_vdd2_opp = PRCM_VDD2_OPP3;
> +     } else {
> +             current_vdd1_opp = PRCM_VDD1_OPP1;
> +             current_vdd2_opp = PRCM_VDD1_OPP1;
> +     }
> +     if (cpu_is_omap34xx()) {
> +             sr_clk_init(&sr1_custom_clk);
> +             sr_clk_init(&sr2_custom_clk);
> +             sr1.clk = clk_get(NULL, "sr1_custom_clk");
> +             sr2.clk = clk_get(NULL, "sr2_custom_clk");
> +     }
> +     sr_set_clk_length(&sr1);
> +     sr_set_clk_length(&sr2);
> +
> +     /* Call the VPConfig, VCConfig, set N Values. */
> +     sr_set_nvalues(&sr1);
> +     sr_configure_vp(SR1);
> +
> +     sr_set_nvalues(&sr2);
> +     sr_configure_vp(SR2);
> +
> +     sr_configure_vc();
> +
> +     /* Enable SR on T2 */
> +     ret = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &RdReg,
> +                                     R_DCDC_GLOBAL_CFG);
> +
> +     RdReg |= DCDC_GLOBAL_CFG_ENABLE_SRFLX;
> +     ret |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, RdReg,
> +                                     R_DCDC_GLOBAL_CFG);
> +
> +     printk(KERN_INFO "SmartReflex driver initialized\n");
> +
> +     ret = sysfs_create_file(power_kobj, &sr_vdd1_autocomp.attr);
> +     if (ret)
> +             printk(KERN_ERR "sysfs_create_file failed: %d\n", ret);
> +
> +     ret = sysfs_create_file(power_kobj, &sr_vdd2_autocomp.attr);
> +     if (ret)
> +             printk(KERN_ERR "sysfs_create_file failed: %d\n", ret);
> +
> +     return 0;
> +}
> +
> +late_initcall(omap3_sr_init);
> diff --git a/arch/arm/mach-omap2/smartreflex.h 
> b/arch/arm/mach-omap2/smartreflex.h
> index 574329f..9b9904d 100644
> --- a/arch/arm/mach-omap2/smartreflex.h
> +++ b/arch/arm/mach-omap2/smartreflex.h
> @@ -230,6 +230,27 @@
>  #define PRCM_NO_VDD2_OPPS    3
>  /* 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;
> +
> +#ifdef CONFIG_OMAP_SMARTREFLEX_TESTING
> +#define SR_TESTING_NVALUES   1
> +#else
> +#define SR_TESTING_NVALUES   0
>  #endif
>  
> +/*
> + * 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(int srid);
> +void disable_smartreflex(int srid);
> +#else
> +static inline void enable_smartreflex(int srid) {}
> +static inline void disable_smartreflex(int srid) {}
> +#endif
>  
> +#endif
> diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
> index b085b07..960c13f 100644
> --- a/arch/arm/plat-omap/Kconfig
> +++ b/arch/arm/plat-omap/Kconfig
> @@ -56,6 +56,37 @@ config OMAP_DEBUG_CLOCKDOMAIN
>         for every clockdomain register write.  However, the
>         extra detail costs some memory.
>  
> +config OMAP_SMARTREFLEX
> +     bool "SmartReflex support"
> +     depends on ARCH_OMAP34XX && TWL4030_CORE
> +     help
> +       Say Y if you want to enable SmartReflex.
> +
> +       SmartReflex can perform continuous dynamic voltage
> +       scaling around the nominal operating point voltage
> +       according to silicon characteristics and operating
> +       conditions. Enabling SmartReflex reduces power
> +       consumption.
> +
> +       Please note, that by default SmartReflex is only
> +       initialized. To enable the automatic voltage
> +       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"
> +     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!
> +
>  config OMAP_RESET_CLOCKS
>       bool "Reset unused clocks during boot"
>       depends on ARCH_OMAP
> -- 
> 1.5.4.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to [EMAIL PROTECTED]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to