From 0192a1d0731d3e8959b817bbc458c21e1c51408f Mon Sep 17 00:00:00 2001 From: Greg Guyotte Date: Tue, 5 Jun 2012 16:54:26 -0500 Subject: [PATCH] am37x: Adding ABB Support for 1GHz OPP This patch adds ABB support for the AM37x 1GHz OPP. When 1GHz operation occurs, the ABB LDO will be enabled, effectively boosting MPU voltage. At any time that we are not operating at 1GHz, the ABB LDO will be bypassed. This change is required for operation at 1GHz on the AM37x device. This patch only affects AM37x. In the course of booting, it boosts the MPU rate to 1GHz and turns on the ABB LDO. It is expected that the system will be operating at 800MHz prior to this time, as set by Uboot. Also note that after MPU rate is changed, the loops_per_jiffy global is updated, which is required in order to get the proper readout from BogoMIPS. Upstream-Status: Not Appropriate * This patch is a quick fix based on an old kernel. * This will be reworked for the Linux mainline kernel. Signed-off-by: Greg Guyotte --- arch/arm/mach-omap2/pm.c | 30 +++++++- arch/arm/mach-omap2/prm-regbits-34xx.h | 20 +++++ arch/arm/mach-omap2/prm2xxx_3xxx.h | 5 ++ arch/arm/mach-omap2/voltage.c | 123 ++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index f7e4d1b..678877c 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -27,6 +27,8 @@ #include "cm2xxx_3xxx.h" #include "pm.h" +#define RATE_1GHZ 1000000000 + static struct omap_device_pm_latency *pm_lats; static struct device *mpu_dev; @@ -61,7 +63,6 @@ struct device *omap4_get_dsp_device(void) } EXPORT_SYMBOL(omap4_get_dsp_device); -#ifndef CONFIG_CPU_FREQ static unsigned long compute_lpj(unsigned long ref, u_int div, u_int mult) { unsigned long new_jiffy_l, new_jiffy_h; @@ -81,7 +82,6 @@ static unsigned long compute_lpj(unsigned long ref, u_int div, u_int mult) return new_jiffy_h + new_jiffy_l * 100; } -#endif /* static int _init_omap_device(struct omap_hwmod *oh, void *user) */ static int _init_omap_device(char *name, struct device **new_dev) @@ -289,6 +289,7 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, struct clk *clk; struct opp *opp; unsigned long freq, bootup_volt; + int ret; if (!vdd_name || !clk_name || !dev) { printk(KERN_ERR "%s: Invalid parameters!\n", __func__); @@ -311,6 +312,11 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, freq = clk->rate; clk_put(clk); + + /* set up for a voltage to support 1GHz */ + if (cpu_is_omap3630()) + if (!strcmp(vdd_name, "mpu")) + freq = RATE_1GHZ; opp = opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) { @@ -327,6 +333,26 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, } omap_voltage_scale_vdd(voltdm, bootup_volt); + + /* once voltage is changed, we can scale freq to max */ + if (cpu_is_omap3630()) { + if (!strcmp(vdd_name, "mpu")) { + unsigned long cur_rate; + cur_rate = clk->rate; + + ret = clk_set_rate(clk, RATE_1GHZ); + if (ret) { + dev_warn(dev, "%s: Unable to set rate to %d\n", + __func__, RATE_1GHZ); + return ret; + } + /* Update loops_per_jiffy because processor speed is + being changed. Necessary to keep BogoMIPS happy. */ + loops_per_jiffy = compute_lpj(loops_per_jiffy, + cur_rate / 1000000, + RATE_1GHZ / 1000000); + } + } return 0; exit: diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h b/arch/arm/mach-omap2/prm-regbits-34xx.h index 64c087a..2758fa3 100644 --- a/arch/arm/mach-omap2/prm-regbits-34xx.h +++ b/arch/arm/mach-omap2/prm-regbits-34xx.h @@ -89,6 +89,7 @@ #define OMAP3430_LASTPOWERSTATEENTERED_MASK (0x3 << 0) /* PRM_IRQSTATUS_IVA2, PRM_IRQSTATUS_MPU shared bits */ +#define OMAP3630_ABB_LDO_TRANXDONE_ST (1 << 26) #define OMAP3430_WKUP_ST_MASK (1 << 0) /* PRM_IRQENABLE_IVA2, PRM_IRQENABLE_MPU shared bits */ @@ -216,6 +217,9 @@ /* PRM_SYSCONFIG specific bits */ /* PRM_IRQSTATUS_MPU specific bits */ +#define OMAP3630_VC_BYPASS_ACK_EN (1 << 28) +#define OMAP3630_VC_VP1_ACK_EN (1 << 27) +#define OMAP3630_VC_ABB_LDO_TRANXDONE_EN (1 << 26) #define OMAP3430ES2_SND_PERIPH_DPLL_ST_SHIFT 25 #define OMAP3430ES2_SND_PERIPH_DPLL_ST_MASK (1 << 25) #define OMAP3430_VC_TIMEOUTERR_ST_MASK (1 << 24) @@ -587,6 +591,22 @@ /* PRM_VP2_STATUS specific bits */ +/* PRM_LDO_ABB_SETUP */ +#define OMAP3630_SR2_IN_TRANSITION (1 << 6) +#define OMAP3630_SR2_STATUS_SHIFT 3 +#define OMAP3630_SR2_STATUS_MASK (0x3 << 3) +#define OMAP3630_OPP_CHANGE (1 << 2) +#define OMAP3630_OPP_SEL_SHIFT 0 +#define OMAP3630_OPP_SEL_MASK (0x3 << 0) + +/* PRM_LDO_ABB_CTRL */ +#define OMAP3630_SR2_WTCNT_VALUE_SHIFT 8 +#define OMAP3630_SR2_WTCNT_VALUE_MASK (0xff << 8) +#define OMAP3630_SLEEP_RBB_SEL (1 << 3) +#define OMAP3630_ACTIVE_FBB_SEL (1 << 2) +#define OMAP3630_ACTIVE_RBB_SEL (1 << 1) +#define OMAP3630_SR2EN (1 << 0) + /* RM_RSTST_NEON specific bits */ /* PM_WKDEP_NEON specific bits */ diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.h b/arch/arm/mach-omap2/prm2xxx_3xxx.h index 613f83c..79c9ad5 100644 --- a/arch/arm/mach-omap2/prm2xxx_3xxx.h +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.h @@ -171,6 +171,11 @@ #define OMAP3_PRM_VP2_STATUS_OFFSET 0x00e4 #define OMAP3430_PRM_VP2_STATUS OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00e4) +#define OMAP3_PRM_LDO_ABB_SETUP_OFFSET 0x00f0 +#define OMAP3430_PRM_LDO_ABB_SETUP OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00f0) +#define OMAP3_PRM_LDO_ABB_CTRL_OFFSET 0x00f4 +#define OMAP3430_PRM_LDO_ABB_CTRL OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00f4) + #define OMAP3_PRM_CLKSEL_OFFSET 0x0040 #define OMAP3430_PRM_CLKSEL OMAP34XX_PRM_REGADDR(OMAP3430_CCR_MOD, 0x0040) #define OMAP3_PRM_CLKOUT_CTRL_OFFSET 0x0070 diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c index 2e015a7..32944d0 100644 --- a/arch/arm/mach-omap2/voltage.c +++ b/arch/arm/mach-omap2/voltage.c @@ -45,6 +45,10 @@ #define VP_TRANXDONE_TIMEOUT 300 #define VOLTAGE_DIR_SIZE 16 +#define ABB_TRANXDONE_TIMEOUT 100 +#define ABB_FAST_OPP 1 +#define ABB_NOMINAL_OPP 2 + /* Voltage processor register offsets */ struct vp_reg_offs { u8 vpconfig; @@ -764,6 +768,112 @@ static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd, return 0; } +/** + * omap3630_abb_change_active_opp - handle OPP changes with Adaptive Body-Bias + * @target_opp_no: ABB_FAST_OPP or ABB_NOMINAL_OPP + * + * Adaptive Body-Bias is a 3630-specific technique to boost voltage in high + * OPPs for silicon with weak characteristics as well as lower voltage in low + * OPPs for silicon with strong characteristics. + * + * Only Foward Body-Bias for operating at high OPPs is implemented below. + * Reverse Body-Bias for saving power in active cases and sleep cases is not + * yet implemented. + */ +static int omap3630_abb_change_active_opp(u32 target_opp_no) +{ + u32 sr2en_enabled; + int timeout; + + /* has SR2EN been enabled previously? */ + sr2en_enabled = (omap2_prm_read_mod_reg(OMAP3430_GR_MOD, + OMAP3_PRM_LDO_ABB_CTRL_OFFSET) & + OMAP3630_SR2EN); + + /* select OPP */ + /* FIXME: shouldn't be hardcoded OPP here */ + if (target_opp_no == ABB_FAST_OPP) { + /* program for fast opp - enable fbb */ + omap2_prm_rmw_mod_reg_bits(OMAP3630_OPP_SEL_MASK, + (ABB_FAST_OPP << OMAP3630_OPP_SEL_SHIFT), + OMAP3430_GR_MOD, + OMAP3_PRM_LDO_ABB_SETUP_OFFSET); + + /* enable the ABB ldo if not done already */ + if (!sr2en_enabled) + omap2_prm_set_mod_reg_bits(OMAP3630_SR2EN, + OMAP3430_GR_MOD, + OMAP3_PRM_LDO_ABB_CTRL_OFFSET); + pr_debug("ABB: ABB LDO Enabled\n"); + } else if (sr2en_enabled) { + /* program for nominal opp - bypass abb ldo */ + omap2_prm_rmw_mod_reg_bits(OMAP3630_OPP_SEL_MASK, + (ABB_NOMINAL_OPP << OMAP3630_OPP_SEL_SHIFT), + OMAP3430_GR_MOD, + OMAP3_PRM_LDO_ABB_SETUP_OFFSET); + pr_debug("ABB: ABB LDO Bypassed\n"); + } else { + /* nothing to do here */ + return 0; + } + + /* set ACTIVE_FBB_SEL for all 3630 silicon */ + omap2_prm_set_mod_reg_bits(OMAP3630_ACTIVE_FBB_SEL, + OMAP3430_GR_MOD, + OMAP3_PRM_LDO_ABB_CTRL_OFFSET); + + /* program settling time of 30us for ABB ldo transition */ + omap2_prm_rmw_mod_reg_bits(OMAP3630_SR2_WTCNT_VALUE_MASK, + (0x62 << OMAP3630_SR2_WTCNT_VALUE_SHIFT), + OMAP3430_GR_MOD, + OMAP3_PRM_LDO_ABB_CTRL_OFFSET); + + /* clear ABB ldo interrupt status */ + omap2_prm_write_mod_reg(OMAP3630_ABB_LDO_TRANXDONE_ST, + OCP_MOD, + OMAP3_PRM_IRQSTATUS_MPU_OFFSET); + + /* enable ABB LDO OPP change */ + omap2_prm_set_mod_reg_bits(OMAP3630_OPP_CHANGE, + OMAP3430_GR_MOD, + OMAP3_PRM_LDO_ABB_SETUP_OFFSET); + + timeout = 0; + + /* wait until OPP change completes */ + while ((timeout < ABB_TRANXDONE_TIMEOUT ) && + (!(omap2_prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQSTATUS_MPU_OFFSET) & + OMAP3630_ABB_LDO_TRANXDONE_ST))) { + udelay(1); + timeout++; + } + + if (timeout == ABB_TRANXDONE_TIMEOUT) + pr_warning("ABB: TRANXDONE timed out waiting for OPP change\n"); + + timeout = 0; + + /* Clear all pending TRANXDONE interrupts/status */ + while (timeout < ABB_TRANXDONE_TIMEOUT) { + omap2_prm_write_mod_reg(OMAP3630_ABB_LDO_TRANXDONE_ST, + OCP_MOD, + OMAP3_PRM_IRQSTATUS_MPU_OFFSET); + if (!(omap2_prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQSTATUS_MPU_OFFSET) + & OMAP3630_ABB_LDO_TRANXDONE_ST)) + break; + + udelay(1); + timeout++; + } + + if (timeout == ABB_TRANXDONE_TIMEOUT) + pr_warning("ABB: TRANXDONE timed out trying to clear status\n"); + + return 0; +} + /* VP force update method of voltage scaling */ static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd, unsigned long target_volt) @@ -858,6 +968,19 @@ static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd, vpconfig &= ~vdd->vp_reg.vpconfig_forceupdate; vdd->write_reg(vpconfig, mod, vdd->vp_offs.vpconfig); + if (cpu_is_omap3630() && !strcmp(vdd->voltdm.name, "mpu")) { + if (vdd->curr_volt == OMAP3630_VDD_MPU_OPP1G_UV) { + pr_debug("%s: vdd_%s: Attempting ABB enable\n", + __func__, vdd->voltdm.name); + omap3630_abb_change_active_opp(ABB_FAST_OPP); + } + else { + pr_debug("%s: vdd_%s: Attempting ABB disable\n", + __func__, vdd->voltdm.name); + omap3630_abb_change_active_opp(ABB_NOMINAL_OPP); + } + } + return 0; } -- 1.7.0.4