* Kalle Jokiniemi <ext-kalle.jokiniemi@xxxxxxxxx> [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 <ext-kalle.jokiniemi@xxxxxxxxx> > --- > 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 <x0080970@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/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 majordomo@xxxxxxxxxxxxxxx > 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html