From patchwork Fri Aug 13 13:47:05 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thara Gopinath X-Patchwork-Id: 119429 X-Patchwork-Delegate: khilman@deeprootsystems.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o7DDlb4V028670 for ; Fri, 13 Aug 2010 13:47:37 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1761658Ab0HMNrc (ORCPT ); Fri, 13 Aug 2010 09:47:32 -0400 Received: from bear.ext.ti.com ([192.94.94.41]:33287 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1761651Ab0HMNr1 (ORCPT ); Fri, 13 Aug 2010 09:47:27 -0400 Received: from dbdp31.itg.ti.com ([172.24.170.98]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id o7DDlL7h031720 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 13 Aug 2010 08:47:24 -0500 Received: from localhost.localdomain (localhost [127.0.0.1]) by dbdp31.itg.ti.com (8.13.8/8.13.8) with ESMTP id o7DDlEPp010023; Fri, 13 Aug 2010 19:17:16 +0530 (IST) From: Thara Gopinath To: linux-omap@vger.kernel.org Cc: khilman@deeprootsystems.com, paul@pwsan.com, b-cousson@ti.com, vishwanath.bs@ti.com, sawant@ti.com, dderrick@ti.com, Thara Gopinath Subject: [PATCHv2 2/8] OMAP3: PM: Adding voltage driver support for OMAP3 Date: Fri, 13 Aug 2010 19:17:05 +0530 Message-Id: <1281707231-3026-3-git-send-email-thara@ti.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1281707231-3026-1-git-send-email-thara@ti.com> References: <1281707231-3026-1-git-send-email-thara@ti.com> Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 13 Aug 2010 13:47:38 +0000 (UTC) diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 63b2d88..1c095cf 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -49,7 +49,8 @@ obj-$(CONFIG_ARCH_OMAP2) += sdrc2xxx.o ifeq ($(CONFIG_PM),y) obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o -obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o cpuidle34xx.o +obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o voltage.o \ + cpuidle34xx.o obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o obj-$(CONFIG_PM_DEBUG) += pm-debug.o diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c new file mode 100644 index 0000000..cb0fcac --- /dev/null +++ b/arch/arm/mach-omap2/voltage.c @@ -0,0 +1,1119 @@ +/* + * OMAP3/OMAP4 Voltage Management Routines + * + * Author: Thara Gopinath + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Rajendra Nayak + * Lesly A M + * + * Copyright (C) 2008 Nokia Corporation + * Kalle Jokiniemi + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Thara Gopinath + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "prm-regbits-34xx.h" + +#define VP_IDLE_TIMEOUT 200 +#define VP_TRANXDONE_TIMEOUT 300 + +/* PRM voltage module */ +static u32 volt_mod; + +/* Voltage processor register offsets */ +struct vp_reg_offs { + u8 vpconfig; + u8 vstepmin; + u8 vstepmax; + u8 vlimitto; + u8 vstatus; + u8 voltage; +}; + +/* Voltage Processor bit field values, shifts and masks */ +struct vp_reg_val { + /* VPx_VPCONFIG */ + u32 vpconfig_erroroffset; + u16 vpconfig_errorgain; + u32 vpconfig_errorgain_mask; + u8 vpconfig_errorgain_shift; + u32 vpconfig_initvoltage_mask; + u8 vpconfig_initvoltage_shift; + u32 vpconfig_timeouten; + u32 vpconfig_initvdd; + u32 vpconfig_forceupdate; + u32 vpconfig_vpenable; + /* VPx_VSTEPMIN */ + u8 vstepmin_stepmin; + u16 vstepmin_smpswaittimemin; + u8 vstepmin_stepmin_shift; + u8 vstepmin_smpswaittimemin_shift; + /* VPx_VSTEPMAX */ + u8 vstepmax_stepmax; + u16 vstepmax_smpswaittimemax; + u8 vstepmax_stepmax_shift; + u8 vstepmax_smpswaittimemax_shift; + /* VPx_VLIMITTO */ + u16 vlimitto_vddmin; + u16 vlimitto_vddmax; + u16 vlimitto_timeout; + u16 vlimitto_vddmin_shift; + u16 vlimitto_vddmax_shift; + u16 vlimitto_timeout_shift; + /* PRM_IRQSTATUS*/ + u32 tranxdone_status; +}; + +/** + * omap_vdd_info - Per Voltage Domain info + * + * @volt_data : voltage table having the distinct voltages supported + * by the domain and other associated per voltage data. + * @vp_offs : structure containing the offsets for various + * vp registers + * @vp_reg : the register values, shifts, masks for various + * vp registers + * @volt_clk : the clock associated with the vdd. + * @opp_dev : the 'struct device' associated with this vdd. + * @volt_data_count : Number of distinct voltages supported by this vdd. + * @nominal_volt : Nominal voltaged for this vdd. + * cmdval_reg : Voltage controller cmdval register. + * @vdd_sr_reg : The smartreflex register associated with this VDD. + */ +struct omap_vdd_info{ + struct omap_volt_data *volt_data; + struct vp_reg_offs vp_offs; + struct vp_reg_val vp_reg; + struct clk *volt_clk; + struct device *opp_dev; + struct voltagedomain voltdm; + int volt_data_count; + unsigned long nominal_volt; + u8 cmdval_reg; + u8 vdd_sr_reg; +}; +static struct omap_vdd_info *vdd_info; +/* + * Number of scalable voltage domains. + */ +static int no_scalable_vdd; + +/* OMAP3 VDD sturctures */ +static struct omap_vdd_info omap3_vdd_info[] = { + { + .vp_offs = { + .vpconfig = OMAP3_PRM_VP1_CONFIG_OFFSET, + .vstepmin = OMAP3_PRM_VP1_VSTEPMIN_OFFSET, + .vstepmax = OMAP3_PRM_VP1_VSTEPMAX_OFFSET, + .vlimitto = OMAP3_PRM_VP1_VLIMITTO_OFFSET, + .vstatus = OMAP3_PRM_VP1_STATUS_OFFSET, + .voltage = OMAP3_PRM_VP1_VOLTAGE_OFFSET, + }, + .voltdm = { + .name = "mpu", + }, + }, + { + .vp_offs = { + .vpconfig = OMAP3_PRM_VP2_CONFIG_OFFSET, + .vstepmin = OMAP3_PRM_VP2_VSTEPMIN_OFFSET, + .vstepmax = OMAP3_PRM_VP2_VSTEPMAX_OFFSET, + .vlimitto = OMAP3_PRM_VP2_VLIMITTO_OFFSET, + .vstatus = OMAP3_PRM_VP2_STATUS_OFFSET, + .voltage = OMAP3_PRM_VP2_VOLTAGE_OFFSET, + }, + .voltdm = { + .name = "core", + }, + }, +}; + +#define OMAP3_NO_SCALABLE_VDD ARRAY_SIZE(omap3_vdd_info) + +/* TODO: OMAP4 register offsets */ + +/* + * Default voltage controller settings. + */ +static struct omap_volt_vc_data vc_config = { + .clksetup = 0xff, + .voltsetup_time1 = 0xfff, + .voltsetup_time2 = 0xfff, + .voltoffset = 0xff, + .voltsetup2 = 0xff, + .vdd0_on = 0x30, /* 1.2v */ + .vdd0_onlp = 0x20, /* 1.0v */ + .vdd0_ret = 0x1e, /* 0.975v */ + .vdd0_off = 0x00, /* 0.6v */ + .vdd1_on = 0x2c, /* 1.15v */ + .vdd1_onlp = 0x20, /* 1.0v */ + .vdd1_ret = 0x1e, /* .975v */ + .vdd1_off = 0x00, /* 0.6v */ +}; + +/* + * Default PMIC Data + */ +static struct omap_volt_pmic_info volt_pmic_info = { + .slew_rate = 4000, + .step_size = 12500, +}; + +/* + * Structures containing OMAP3430/OMAP3630 voltage supported and various + * data associated with it per voltage domain basis. Smartreflex Ntarget + * values are left as 0 as they have to be populated by smartreflex + * driver after reading the efuse. + */ + +/* VDD1 */ +static struct omap_volt_data omap34xx_vdd1_volt_data[] = { + {.volt_nominal = 975000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, + {.volt_nominal = 1075000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, + {.volt_nominal = 1200000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, + {.volt_nominal = 1270000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, + {.volt_nominal = 1350000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, +}; + +static struct omap_volt_data omap36xx_vdd1_volt_data[] = { + {.volt_nominal = 930000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, + {.volt_nominal = 1100000, .sr_errminlimit = 0xF9, .vp_errgain = 0x16}, + {.volt_nominal = 1260000, .sr_errminlimit = 0xFA, .vp_errgain = 0x23}, + {.volt_nominal = 1350000, .sr_errminlimit = 0xFA, .vp_errgain = 0x27}, +}; + +/* VDD2 */ +static struct omap_volt_data omap34xx_vdd2_volt_data[] = { + {.volt_nominal = 975000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, + {.volt_nominal = 1050000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, + {.volt_nominal = 1150000, .sr_errminlimit = 0xF9, .vp_errgain = 0x18}, +}; + +static struct omap_volt_data omap36xx_vdd2_volt_data[] = { + {.volt_nominal = 930000, .sr_errminlimit = 0xF4, .vp_errgain = 0x0C}, + {.volt_nominal = 1137500, .sr_errminlimit = 0xF9, .vp_errgain = 0x16}, +}; + + +/* By default VPFORCEUPDATE is the chosen method of voltage scaling */ +static bool voltscale_vpforceupdate = true; + +static inline u32 voltage_read_reg(u8 offset) +{ + return prm_read_mod_reg(volt_mod, offset); +} + +static inline void voltage_write_reg(u8 offset, u32 value) +{ + prm_write_mod_reg(value, volt_mod, offset); +} + +static void vp_latch_vsel(struct omap_vdd_info *vdd) +{ + u32 vpconfig; + unsigned long uvdc; + char vsel; + + uvdc = omap_voltage_get_nom_volt(&vdd->voltdm); + if (!uvdc) { + pr_warning("%s: unable to find current voltage for vdd_%s\n", + __func__, vdd->voltdm.name); + return; + } + vsel = omap_twl_uv_to_vsel(uvdc); + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); + vpconfig &= ~(vdd->vp_reg.vpconfig_initvoltage_mask | + vdd->vp_reg.vpconfig_initvdd); + vpconfig |= vsel << vdd->vp_reg.vpconfig_initvoltage_shift; + + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + + /* Trigger initVDD value copy to voltage processor */ + voltage_write_reg(vdd->vp_offs.vpconfig, + (vpconfig | vdd->vp_reg.vpconfig_initvdd)); + + /* Clear initVDD copy trigger bit */ + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); +} + +/* OMAP3 specific voltage init functions */ +/* + * Intializes the voltage controller registers with the PMIC and board + * specific parameters and voltage setup times for OMAP3. If the board + * file does not populate the voltage controller parameters through + * omap3_pm_init_vc, default values specified in vc_config is used. + */ +static void __init omap3_init_voltagecontroller(void) +{ + voltage_write_reg(OMAP3_PRM_VC_SMPS_SA_OFFSET, + (OMAP3_SRI2C_SLAVE_ADDR << + OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT) | + (OMAP3_SRI2C_SLAVE_ADDR << + OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT)); + voltage_write_reg(OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET, + (OMAP3_VDD2_SR_CONTROL_REG << OMAP3430_VOLRA1_SHIFT) | + (OMAP3_VDD1_SR_CONTROL_REG << OMAP3430_VOLRA0_SHIFT)); + voltage_write_reg(OMAP3_PRM_VC_CMD_VAL_0_OFFSET, + (vc_config.vdd0_on << OMAP3430_VC_CMD_ON_SHIFT) | + (vc_config.vdd0_onlp << OMAP3430_VC_CMD_ONLP_SHIFT) | + (vc_config.vdd0_ret << OMAP3430_VC_CMD_RET_SHIFT) | + (vc_config.vdd0_off << OMAP3430_VC_CMD_OFF_SHIFT)); + voltage_write_reg(OMAP3_PRM_VC_CMD_VAL_1_OFFSET, + (vc_config.vdd1_on << OMAP3430_VC_CMD_ON_SHIFT) | + (vc_config.vdd1_onlp << OMAP3430_VC_CMD_ONLP_SHIFT) | + (vc_config.vdd1_ret << OMAP3430_VC_CMD_RET_SHIFT) | + (vc_config.vdd1_off << OMAP3430_VC_CMD_OFF_SHIFT)); + voltage_write_reg(OMAP3_PRM_VC_CH_CONF_OFFSET, + OMAP3430_CMD1_MASK | OMAP3430_RAV1_MASK); + voltage_write_reg(OMAP3_PRM_VC_I2C_CFG_OFFSET, + OMAP3430_MCODE_SHIFT | OMAP3430_HSEN_MASK); + /* Write setup times */ + voltage_write_reg(OMAP3_PRM_CLKSETUP_OFFSET, vc_config.clksetup); + voltage_write_reg(OMAP3_PRM_VOLTSETUP1_OFFSET, + (vc_config.voltsetup_time2 << + OMAP3430_SETUP_TIME2_SHIFT) | + (vc_config.voltsetup_time1 << + OMAP3430_SETUP_TIME1_SHIFT)); + voltage_write_reg(OMAP3_PRM_VOLTOFFSET_OFFSET, vc_config.voltoffset); + voltage_write_reg(OMAP3_PRM_VOLTSETUP2_OFFSET, vc_config.voltsetup2); +} + +/* Sets up all the VDD related info for OMAP3 */ +static void __init omap3_vdd_data_configure(struct omap_vdd_info *vdd) +{ + unsigned long curr_volt; + struct omap_volt_data *volt_data; + struct clk *sys_ck; + u32 sys_clk_speed, timeout_val, waittime; + + if (!strcmp(vdd->voltdm.name, "mpu")) { + if (cpu_is_omap3630()) { + vdd->vp_reg.vlimitto_vddmin = + OMAP3630_VP1_VLIMITTO_VDDMIN; + vdd->vp_reg.vlimitto_vddmax = + OMAP3630_VP1_VLIMITTO_VDDMAX; + vdd->volt_data = omap36xx_vdd1_volt_data; + vdd->volt_data_count = + ARRAY_SIZE(omap36xx_vdd1_volt_data); + } else { + vdd->vp_reg.vlimitto_vddmin = + OMAP3430_VP1_VLIMITTO_VDDMIN; + vdd->vp_reg.vlimitto_vddmax = + OMAP3430_VP1_VLIMITTO_VDDMAX; + vdd->volt_data = omap34xx_vdd1_volt_data; + vdd->volt_data_count = + ARRAY_SIZE(omap34xx_vdd1_volt_data); + } + vdd->volt_clk = clk_get(NULL, "dpll1_ck"); + vdd->opp_dev = omap2_get_mpuss_device(); + vdd->vp_reg.tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK; + vdd->cmdval_reg = OMAP3_PRM_VC_CMD_VAL_0_OFFSET; + vdd->vdd_sr_reg = OMAP3_VDD1_SR_CONTROL_REG; + } else if (!strcmp(vdd->voltdm.name, "core")) { + if (cpu_is_omap3630()) { + vdd->vp_reg.vlimitto_vddmin = + OMAP3630_VP2_VLIMITTO_VDDMIN; + vdd->vp_reg.vlimitto_vddmax = + OMAP3630_VP2_VLIMITTO_VDDMAX; + vdd->volt_data = omap36xx_vdd2_volt_data; + vdd->volt_data_count = + ARRAY_SIZE(omap36xx_vdd2_volt_data); + } else { + vdd->vp_reg.vlimitto_vddmin = + OMAP3430_VP2_VLIMITTO_VDDMIN; + vdd->vp_reg.vlimitto_vddmax = + OMAP3430_VP2_VLIMITTO_VDDMAX; + vdd->volt_data = omap34xx_vdd2_volt_data; + vdd->volt_data_count = + ARRAY_SIZE(omap34xx_vdd2_volt_data); + } + vdd->volt_clk = clk_get(NULL, "l3_ick"); + vdd->opp_dev = omap2_get_l3_device(); + vdd->vp_reg.tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK; + vdd->cmdval_reg = OMAP3_PRM_VC_CMD_VAL_1_OFFSET; + vdd->vdd_sr_reg = OMAP3_VDD2_SR_CONTROL_REG; + } else { + pr_warning("%s: vdd_%s does not exisit in OMAP3\n", + __func__, vdd->voltdm.name); + return; + } + + if (IS_ERR(vdd->volt_clk)) { + pr_warning("%s: unable to get clk for vdd_%s\n", + __func__, vdd->voltdm.name); + return; + } + if (!vdd->opp_dev) { + pr_warning("%s: unable to get the opp device for vdd_%s\n", + __func__, vdd->voltdm.name); + return; + } + + curr_volt = omap_voltage_get_nom_volt(&vdd->voltdm); + if (!curr_volt) { + pr_warning("%s: unable to find current voltage for vdd_%s\n", + __func__, vdd->voltdm.name); + return; + } + + volt_data = omap_voltage_get_voltdata(&vdd->voltdm, curr_volt); + if (IS_ERR(volt_data)) { + pr_warning("%s: Unable to get volt table for vdd_%s at init", + __func__, vdd->voltdm.name); + return; + } + /* + * Sys clk rate is require to calculate vp timeout value and + * smpswaittimemin and smpswaittimemax. + */ + sys_ck = clk_get(NULL, "sys_ck"); + if (IS_ERR(sys_ck)) { + pr_warning("%s: Could not get the sys clk to calculate" + "various vdd_%s params\n", __func__, vdd->voltdm.name); + return; + } + sys_clk_speed = clk_get_rate(sys_ck); + clk_put(sys_ck); + /* Divide to avoid overflow */ + sys_clk_speed /= 1000; + + /* Nominal/Reset voltage of the VDD */ + vdd->nominal_volt = 1200000; + + /* VPCONFIG bit fields */ + vdd->vp_reg.vpconfig_erroroffset = (OMAP3_VP_CONFIG_ERROROFFSET << + OMAP3430_ERROROFFSET_SHIFT); + vdd->vp_reg.vpconfig_errorgain = volt_data->vp_errgain; + vdd->vp_reg.vpconfig_errorgain_mask = OMAP3430_ERRORGAIN_MASK; + vdd->vp_reg.vpconfig_errorgain_shift = OMAP3430_ERRORGAIN_SHIFT; + vdd->vp_reg.vpconfig_initvoltage_shift = OMAP3430_INITVOLTAGE_SHIFT; + vdd->vp_reg.vpconfig_initvoltage_mask = OMAP3430_INITVOLTAGE_MASK; + vdd->vp_reg.vpconfig_timeouten = OMAP3430_TIMEOUTEN_MASK; + vdd->vp_reg.vpconfig_initvdd = OMAP3430_INITVDD_MASK; + vdd->vp_reg.vpconfig_forceupdate = OMAP3430_FORCEUPDATE_MASK; + vdd->vp_reg.vpconfig_vpenable = OMAP3430_VPENABLE_MASK; + + /* VSTEPMIN VSTEPMAX bit fields */ + waittime = ((volt_pmic_info.step_size / volt_pmic_info.slew_rate) * + sys_clk_speed) / 1000; + vdd->vp_reg.vstepmin_smpswaittimemin = waittime; + vdd->vp_reg.vstepmax_smpswaittimemax = waittime; + vdd->vp_reg.vstepmin_stepmin = OMAP3_VP_VSTEPMIN_VSTEPMIN; + vdd->vp_reg.vstepmax_stepmax = OMAP3_VP_VSTEPMAX_VSTEPMAX; + vdd->vp_reg.vstepmin_smpswaittimemin_shift = + OMAP3430_SMPSWAITTIMEMIN_SHIFT; + vdd->vp_reg.vstepmax_smpswaittimemax_shift = + OMAP3430_SMPSWAITTIMEMAX_SHIFT; + vdd->vp_reg.vstepmin_stepmin_shift = OMAP3430_VSTEPMIN_SHIFT; + vdd->vp_reg.vstepmax_stepmax_shift = OMAP3430_VSTEPMAX_SHIFT; + + /* VLIMITTO bit fields */ + timeout_val = (sys_clk_speed * OMAP3_VP_VLIMITTO_TIMEOUT_US) / 1000; + vdd->vp_reg.vlimitto_timeout = timeout_val; + vdd->vp_reg.vlimitto_vddmin_shift = OMAP3430_VDDMIN_SHIFT; + vdd->vp_reg.vlimitto_vddmax_shift = OMAP3430_VDDMAX_SHIFT; + vdd->vp_reg.vlimitto_timeout_shift = OMAP3430_TIMEOUT_SHIFT; +} + +/* Generic voltage init functions */ +static void __init init_voltageprocessor(struct omap_vdd_info *vdd) +{ + u32 vpconfig; + + vpconfig = vdd->vp_reg.vpconfig_erroroffset | + (vdd->vp_reg.vpconfig_errorgain << + vdd->vp_reg.vpconfig_errorgain_shift) | + vdd->vp_reg.vpconfig_timeouten; + + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + + voltage_write_reg(vdd->vp_offs.vstepmin, + (vdd->vp_reg.vstepmin_smpswaittimemin << + vdd->vp_reg.vstepmin_smpswaittimemin_shift) | + (vdd->vp_reg.vstepmin_stepmin << + vdd->vp_reg.vstepmin_stepmin_shift)); + + voltage_write_reg(vdd->vp_offs.vstepmax, + (vdd->vp_reg.vstepmax_smpswaittimemax << + vdd->vp_reg.vstepmax_smpswaittimemax_shift) | + (vdd->vp_reg.vstepmax_stepmax << + vdd->vp_reg.vstepmax_stepmax_shift)); + + voltage_write_reg(vdd->vp_offs.vlimitto, + (vdd->vp_reg.vlimitto_vddmax << + vdd->vp_reg.vlimitto_vddmax_shift) | + (vdd->vp_reg.vlimitto_vddmin << + vdd->vp_reg.vlimitto_vddmin_shift) | + (vdd->vp_reg.vlimitto_timeout << + vdd->vp_reg.vlimitto_timeout_shift)); + + /* Set the init voltage */ + vp_latch_vsel(vdd); + + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); + /* Force update of voltage */ + voltage_write_reg(vdd->vp_offs.vpconfig, + (vpconfig | vdd->vp_reg.vpconfig_forceupdate)); + /* Clear force bit */ + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); +} + +static void __init vdd_data_configure(struct omap_vdd_info *vdd) +{ + if (cpu_is_omap34xx()) + omap3_vdd_data_configure(vdd); +} +static void __init init_voltagecontroller(void) +{ + if (cpu_is_omap34xx()) + omap3_init_voltagecontroller(); +} + +/* + * vc_bypass_scale_voltage - VC bypass method of voltage scaling + */ +static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd, + unsigned long target_volt) +{ + struct omap_volt_data *volt_data; + u32 vc_bypass_value, vc_cmdval, vc_valid, vc_bypass_val_reg_offs; + u32 vp_errgain_val, vc_cmd_on_mask; + u32 loop_cnt = 0, retries_cnt = 0; + u32 smps_steps = 0, smps_delay = 0; + u8 vc_data_shift, vc_slaveaddr_shift, vc_regaddr_shift; + u8 vc_cmd_on_shift; + u8 target_vsel, current_vsel, sr_i2c_slave_addr; + + if (cpu_is_omap34xx()) { + vc_cmd_on_shift = OMAP3430_VC_CMD_ON_SHIFT; + vc_cmd_on_mask = OMAP3430_VC_CMD_ON_MASK; + vc_data_shift = OMAP3430_DATA_SHIFT; + vc_slaveaddr_shift = OMAP3430_SLAVEADDR_SHIFT; + vc_regaddr_shift = OMAP3430_REGADDR_SHIFT; + vc_valid = OMAP3430_VALID_MASK; + vc_bypass_val_reg_offs = OMAP3_PRM_VC_BYPASS_VAL_OFFSET; + sr_i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR; + } + + /* Get volt_data corresponding to target_volt */ + volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt); + if (IS_ERR(volt_data)) { + /* + * If a match is not found but the target voltage is + * is the nominal vdd voltage allow scaling + */ + if (target_volt != vdd->nominal_volt) { + pr_warning("%s: Unable to get volt table for vdd_%s" + "during voltage scaling. Some really Wrong!", + __func__, vdd->voltdm.name); + return -ENODATA; + } + volt_data = NULL; + } + + target_vsel = omap_twl_uv_to_vsel(target_volt); + current_vsel = voltage_read_reg(vdd->vp_offs.voltage); + smps_steps = abs(target_vsel - current_vsel); + + /* Setting the ON voltage to the new target voltage */ + vc_cmdval = voltage_read_reg(vdd->cmdval_reg); + vc_cmdval &= ~vc_cmd_on_mask; + vc_cmdval |= (target_vsel << vc_cmd_on_shift); + voltage_write_reg(vdd->cmdval_reg, vc_cmdval); + + /* Setting vp errorgain based on the voltage */ + if (volt_data) { + vp_errgain_val = voltage_read_reg(vdd->vp_offs.vpconfig); + vdd->vp_reg.vpconfig_errorgain = volt_data->vp_errgain; + vp_errgain_val &= ~vdd->vp_reg.vpconfig_errorgain_mask; + vp_errgain_val |= vdd->vp_reg.vpconfig_errorgain << + vdd->vp_reg.vpconfig_errorgain_shift; + voltage_write_reg(vdd->vp_offs.vpconfig, vp_errgain_val); + } + + vc_bypass_value = (target_vsel << vc_data_shift) | + (vdd->vdd_sr_reg << vc_regaddr_shift) | + (sr_i2c_slave_addr << vc_slaveaddr_shift); + + voltage_write_reg(vc_bypass_val_reg_offs, vc_bypass_value); + + voltage_write_reg(vc_bypass_val_reg_offs, vc_bypass_value | vc_valid); + vc_bypass_value = voltage_read_reg(vc_bypass_val_reg_offs); + + while ((vc_bypass_value & vc_valid) != 0x0) { + loop_cnt++; + if (retries_cnt > 10) { + pr_warning("%s: Loop count exceeded in check SR I2C" + "write during voltgae scaling\n", __func__); + return -ETIMEDOUT; + } + if (loop_cnt > 50) { + retries_cnt++; + loop_cnt = 0; + udelay(10); + } + vc_bypass_value = voltage_read_reg(vc_bypass_val_reg_offs); + } + + /* SMPS slew rate / step size. 2us added as buffer. */ + smps_delay = ((smps_steps * volt_pmic_info.step_size) / + volt_pmic_info.slew_rate) + 2; + udelay(smps_delay); + return 0; +} + +/* VP force update method of voltage scaling */ +static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd, + unsigned long target_volt) +{ + struct omap_volt_data *volt_data; + u32 vc_cmd_on_mask, vc_cmdval, vpconfig; + u32 smps_steps = 0, smps_delay = 0; + int timeout = 0; + u8 target_vsel, current_vsel; + u8 vc_cmd_on_shift; + u8 prm_irqst_reg_offs, ocp_mod; + + if (cpu_is_omap34xx()) { + vc_cmd_on_shift = OMAP3430_VC_CMD_ON_SHIFT; + vc_cmd_on_mask = OMAP3430_VC_CMD_ON_MASK; + prm_irqst_reg_offs = OMAP3_PRM_IRQSTATUS_MPU_OFFSET; + ocp_mod = OCP_MOD; + } + + /* Get volt_data corresponding to the target_volt */ + volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt); + if (IS_ERR(volt_data)) { + /* + * If a match is not found but the target voltage is + * is the nominal vdd voltage allow scaling + */ + if (target_volt != vdd->nominal_volt) { + pr_warning("%s: Unable to get voltage table for vdd_%s" + "during voltage scaling. Some really Wrong!", + __func__, vdd->voltdm.name); + return -ENODATA; + } + volt_data = NULL; + } + + target_vsel = omap_twl_uv_to_vsel(target_volt); + current_vsel = voltage_read_reg(vdd->vp_offs.voltage); + smps_steps = abs(target_vsel - current_vsel); + + /* Setting the ON voltage to the new target voltage */ + vc_cmdval = voltage_read_reg(vdd->cmdval_reg); + vc_cmdval &= ~vc_cmd_on_mask; + vc_cmdval |= (target_vsel << vc_cmd_on_shift); + voltage_write_reg(vdd->cmdval_reg, vc_cmdval); + + /* Getting vp errorgain based on the voltage */ + if (volt_data) + vdd->vp_reg.vpconfig_errorgain = + volt_data->vp_errgain; + /* + * Clear all pending TransactionDone interrupt/status. Typical latency + * is <3us + */ + while (timeout++ < VP_TRANXDONE_TIMEOUT) { + prm_write_mod_reg(vdd->vp_reg.tranxdone_status, + ocp_mod, prm_irqst_reg_offs); + if (!(prm_read_mod_reg(ocp_mod, prm_irqst_reg_offs) & + vdd->vp_reg.tranxdone_status)) + break; + udelay(1); + } + + if (timeout >= VP_TRANXDONE_TIMEOUT) { + pr_warning("%s: vdd_%s TRANXDONE timeout exceeded." + "Voltage change aborted", __func__, vdd->voltdm.name); + return -ETIMEDOUT; + } + /* Configure for VP-Force Update */ + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); + vpconfig &= ~(vdd->vp_reg.vpconfig_initvdd | + vdd->vp_reg.vpconfig_forceupdate | + vdd->vp_reg.vpconfig_initvoltage_mask | + vdd->vp_reg.vpconfig_errorgain_mask); + vpconfig |= ((target_vsel << + vdd->vp_reg.vpconfig_initvoltage_shift) | + (vdd->vp_reg.vpconfig_errorgain << + vdd->vp_reg.vpconfig_errorgain_shift)); + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + + /* Trigger initVDD value copy to voltage processor */ + vpconfig |= vdd->vp_reg.vpconfig_initvdd; + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + + /* Force update of voltage */ + vpconfig |= vdd->vp_reg.vpconfig_forceupdate; + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + + timeout = 0; + /* + * Wait for TransactionDone. Typical latency is <200us. + * Depends on SMPSWAITTIMEMIN/MAX and voltage change + */ + omap_test_timeout((prm_read_mod_reg(ocp_mod, prm_irqst_reg_offs) & + vdd->vp_reg.tranxdone_status), + VP_TRANXDONE_TIMEOUT, timeout); + + if (timeout >= VP_TRANXDONE_TIMEOUT) + pr_err("%s: vdd_%s TRANXDONE timeout exceeded." + "TRANXDONE never got set after the voltage update\n", + __func__, vdd->voltdm.name); + /* + * Wait for voltage to settle with SW wait-loop. + * SMPS slew rate / step size. 2us added as buffer. + */ + smps_delay = ((smps_steps * volt_pmic_info.step_size) / + volt_pmic_info.slew_rate) + 2; + udelay(smps_delay); + + /* + * Disable TransactionDone interrupt , clear all status, clear + * control registers + */ + timeout = 0; + while (timeout++ < VP_TRANXDONE_TIMEOUT) { + prm_write_mod_reg(vdd->vp_reg.tranxdone_status, + ocp_mod, prm_irqst_reg_offs); + if (!(prm_read_mod_reg(ocp_mod, prm_irqst_reg_offs) & + vdd->vp_reg.tranxdone_status)) + break; + udelay(1); + } + if (timeout >= VP_TRANXDONE_TIMEOUT) + pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying" + "to clear the TRANXDONE status\n", + __func__, vdd->voltdm.name); + + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); + /* Clear initVDD copy trigger bit */ + vpconfig &= ~vdd->vp_reg.vpconfig_initvdd;; + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + /* Clear force bit */ + vpconfig &= ~vdd->vp_reg.vpconfig_forceupdate; + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + + return 0; +} + +/* Public functions */ +/** + * omap_voltage_get_nom_volt : Gets the current non-auto-compensated voltage + * @voltdm : pointer to the VDD for which current voltage info is needed + * + * API to get the current non-auto-compensated voltage for a VDD. + * Returns 0 in case of error else returns the current voltage for the VDD. + */ +unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm) +{ + struct omap_opp *opp; + struct omap_vdd_info *vdd; + unsigned long freq; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return 0; + } + + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); + + freq = vdd->volt_clk->rate; + opp = opp_find_freq_ceil(vdd->opp_dev, &freq); + if (IS_ERR(opp)) { + pr_warning("%s: Unable to find OPP for vdd_%s freq%ld\n", + __func__, voltdm->name, freq); + return 0; + } + + /* + * Use higher freq voltage even if an exact match is not available + * we are probably masking a clock framework bug, so warn + */ + if (unlikely(freq != vdd->volt_clk->rate)) + pr_warning("%s: Available freq %ld != dpll freq %ld.\n", + __func__, freq, vdd->volt_clk->rate); + + return opp_get_voltage(opp); +} + +/** + * omap_vp_get_curr_volt : API to get the current vp voltage. + * @voltdm: pointer to the VDD. + * + * This API returns the current voltage for the specified voltage processor + */ +unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm) +{ + struct omap_vdd_info *vdd; + u8 curr_vsel; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return 0; + } + + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); + + curr_vsel = voltage_read_reg(vdd->vp_offs.voltage); + return omap_twl_vsel_to_uv(curr_vsel); +} + +/** + * omap_vp_enable : API to enable a particular VP + * @voltdm: pointer to the VDD whose VP is to be enabled. + * + * This API enables a particular voltage processor. Needed by the smartreflex + * class drivers. + */ +void omap_vp_enable(struct voltagedomain *voltdm) +{ + struct omap_vdd_info *vdd; + u32 vpconfig; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return; + } + + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); + + /* If VP is already enabled, do nothing. Return */ + if (voltage_read_reg(vdd->vp_offs.vpconfig) & + vdd->vp_reg.vpconfig_vpenable) + return; + /* + * This latching is required only if VC bypass method is used for + * voltage scaling during dvfs. + */ + if (!voltscale_vpforceupdate) + vp_latch_vsel(vdd); + + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); + /* Enable VP */ + voltage_write_reg(vdd->vp_offs.vpconfig, + vpconfig | vdd->vp_reg.vpconfig_vpenable); +} + +/** + * omap_vp_disable : API to disable a particular VP + * @voltdm: pointer to the VDD whose VP is to be disabled. + * + * This API disables a particular voltage processor. Needed by the smartreflex + * class drivers. + */ +void omap_vp_disable(struct voltagedomain *voltdm) +{ + struct omap_vdd_info *vdd; + u32 vpconfig; + int timeout; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return; + } + + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); + + /* If VP is already disabled, do nothing. Return */ + if (!(voltage_read_reg(vdd->vp_offs.vpconfig) & + vdd->vp_reg.vpconfig_vpenable)) { + pr_warning("%s: Trying to disable VP for vdd_%s when" + "it is already disabled\n", __func__, voltdm->name); + return; + } + + /* Disable VP */ + vpconfig = voltage_read_reg(vdd->vp_offs.vpconfig); + vpconfig &= ~vdd->vp_reg.vpconfig_vpenable; + voltage_write_reg(vdd->vp_offs.vpconfig, vpconfig); + + /* + * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us + */ + omap_test_timeout((voltage_read_reg(vdd->vp_offs.vstatus)), + VP_IDLE_TIMEOUT, timeout); + + if (timeout >= VP_IDLE_TIMEOUT) + pr_warning("%s: vdd_%s idle timedout\n", + __func__, voltdm->name); + return; +} + +/** + * omap_voltage_scale_vdd : API to scale voltage of a particular voltage domain. + * @voltdm: pointer to the VDD which is to be scaled. + * @target_volt : The target voltage of the voltage domain + * + * This API should be called by the kernel to do the voltage scaling + * for a particular voltage domain during dvfs or any other situation. + */ +int omap_voltage_scale_vdd(struct voltagedomain *voltdm, + unsigned long target_volt) +{ + struct omap_vdd_info *vdd; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return -EINVAL; + } + + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); + + if (voltscale_vpforceupdate) + return vp_forceupdate_scale_voltage(vdd, target_volt); + else + return vc_bypass_scale_voltage(vdd, target_volt); +} + + + +/** + * omap_voltage_reset : Resets the voltage of a particular voltage domain + * to that of the current OPP. + * @voltdm: pointer to the VDD whose voltage is to be reset. + * + * This API finds out the correct voltage the voltage domain is supposed + * to be at and resets the voltage to that level. Should be used expecially + * while disabling any voltage compensation modules. + */ +void omap_voltage_reset(struct voltagedomain *voltdm) +{ + unsigned long target_uvdc; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return; + } + + target_uvdc = omap_voltage_get_nom_volt(voltdm); + if (!target_uvdc) { + pr_err("%s: unable to find current voltage for vdd_%s\n", + __func__, voltdm->name); + return; + } + omap_voltage_scale_vdd(voltdm, target_uvdc); +} + +/** + * omap_change_voltscale_method : API to change the voltage scaling method. + * @voltscale_method : the method to be used for voltage scaling. + * + * This API can be used by the board files to change the method of voltage + * scaling between vpforceupdate and vcbypass. The parameter values are + * defined in voltage.h + */ +void omap_change_voltscale_method(int voltscale_method) +{ + switch (voltscale_method) { + case VOLTSCALE_VPFORCEUPDATE: + voltscale_vpforceupdate = true; + return; + case VOLTSCALE_VCBYPASS: + voltscale_vpforceupdate = false; + return; + default: + pr_warning("%s: Trying to change the method of voltage scaling" + "to an unsupported one!\n", __func__); + } +} + +/** + * omap_voltage_init_vc - polpulates vc_config with values specified in + * board file + * @setup_vc - the structure with various vc parameters + * + * Updates vc_config with the voltage setup times and other parameters as + * specified in setup_vc. vc_config is later used in init_voltagecontroller + * to initialize the voltage controller registers. Board files should call + * this function with the correct volatge settings corresponding + * the particular PMIC and chip. + */ +void __init omap_voltage_init_vc(struct omap_volt_vc_data *setup_vc) +{ + if (!setup_vc) + return; + + vc_config.clksetup = setup_vc->clksetup; + vc_config.voltsetup_time1 = setup_vc->voltsetup_time1; + vc_config.voltsetup_time2 = setup_vc->voltsetup_time2; + vc_config.voltoffset = setup_vc->voltoffset; + vc_config.voltsetup2 = setup_vc->voltsetup2; + vc_config.vdd0_on = setup_vc->vdd0_on; + vc_config.vdd0_onlp = setup_vc->vdd0_onlp; + vc_config.vdd0_ret = setup_vc->vdd0_ret; + vc_config.vdd0_off = setup_vc->vdd0_off; + vc_config.vdd1_on = setup_vc->vdd1_on; + vc_config.vdd1_onlp = setup_vc->vdd1_onlp; + vc_config.vdd1_ret = setup_vc->vdd1_ret; + vc_config.vdd1_off = setup_vc->vdd1_off; +} + +/** + * omap_voltage_get_volttable : API to get the voltage table associated with a + * particular voltage domain. + * + * @voltdm: pointer to the VDD for which the voltage table is required + * @volt_data : the voltage table for the particular vdd which is to be + * populated by this API + * This API populates the voltage table associated with a VDD into the + * passed parameter pointer. Returns the count of distinct voltages + * supported by this vdd. + * + */ +int omap_voltage_get_volttable(struct voltagedomain *voltdm, + struct omap_volt_data **volt_data) +{ + struct omap_vdd_info *vdd; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return 0; + } + + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); + + *volt_data = vdd->volt_data; + return vdd->volt_data_count; +} + +/** + * omap_voltage_get_voltdata : API to get the voltage table entry for a + * particular voltage + * @voltdm: pointer to the VDD whose voltage table has to be searched + * @volt : the voltage to be searched in the voltage table + * + * This API searches through the voltage table for the required voltage + * domain and tries to find a matching entry for the passed voltage volt. + * If a matching entry is found volt_data is populated with that entry. + * This API searches only through the non-compensated voltages int the + * voltage table. + * Returns pointer to the voltage table entry corresponding to volt on + * sucess. Returns -ENODATA if no voltage table exisits for the passed voltage + * domain or if there is no matching entry. + */ +struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, + unsigned long volt) +{ + struct omap_vdd_info *vdd; + int i; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warning("%s: VDD specified does not exist!\n", __func__); + return ERR_PTR(-EINVAL); + } + + vdd = container_of(voltdm, struct omap_vdd_info, voltdm); + + if (!vdd->volt_data) { + pr_warning("%s: voltage table does not exist for vdd_%s\n", + __func__, voltdm->name); + return ERR_PTR(-ENODATA); + } + + for (i = 0; i < vdd->volt_data_count; i++) { + if (vdd->volt_data[i].volt_nominal == volt) + return &vdd->volt_data[i]; + } + + pr_notice("%s: Unable to match the current voltage with the voltage" + "table for vdd_%s\n", __func__, voltdm->name); + + return ERR_PTR(-ENODATA); +} + +/** + * omap_voltage_register_pmic : API to register PMIC specific data + * @pmic_info : the structure containing pmic info + * + * This API is to be called by the borad file to specify the pmic specific + * info as present in omap_volt_pmic_info structure. A default pmic info + * table is maintained in the driver volt_pmic_info. If the board file do + * not override the default table using this API, the default values wiil + * be used in the driver. + */ +void omap_voltage_register_pmic(struct omap_volt_pmic_info *pmic_info) +{ + volt_pmic_info.slew_rate = pmic_info->slew_rate; + volt_pmic_info.step_size = pmic_info->step_size; +} + +/** + * omap_voltage_domain_get : API to get the voltage domain pointer + * @name : Name of the voltage domain + * + * This API looks up in the global vdd_info struct for the + * existence of voltage domain . If it exists, the API returns + * a pointer to the voltage domain structure corresponding to the + * VDD. Else retuns error pointer. + */ +struct voltagedomain *omap_voltage_domain_get(char *name) +{ + int i; + + if (!vdd_info) { + pr_err("%s: Voltage driver init not yet happened.Faulting!\n", + __func__); + return ERR_PTR(-EINVAL); + } + + if (!name) { + pr_err("%s: No name to get the votage domain!\n", __func__); + return ERR_PTR(-EINVAL); + } + + for (i = 0; i < no_scalable_vdd; i++) { + if (!(strcmp(name, vdd_info[i].voltdm.name))) + return &vdd_info[i].voltdm; + } + + return ERR_PTR(-EINVAL); +} + +/** + * omap_voltage_init : Volatage init API which does VP and VC init. + */ +static int __init omap_voltage_init(void) +{ + int i; + + if (!cpu_is_omap34xx()) { + pr_warning("%s: voltage driver support not added\n", __func__); + return 0; + } + + if (cpu_is_omap34xx()) { + volt_mod = OMAP3430_GR_MOD; + vdd_info = omap3_vdd_info; + no_scalable_vdd = OMAP3_NO_SCALABLE_VDD; + } + init_voltagecontroller(); + for (i = 0; i < no_scalable_vdd; i++) { + vdd_data_configure(&vdd_info[i]); + init_voltageprocessor(&vdd_info[i]); + } + return 0; +} +device_initcall(omap_voltage_init); diff --git a/arch/arm/plat-omap/include/plat/voltage.h b/arch/arm/plat-omap/include/plat/voltage.h new file mode 100644 index 0000000..fc9778b --- /dev/null +++ b/arch/arm/plat-omap/include/plat/voltage.h @@ -0,0 +1,132 @@ +/* + * OMAP Voltage Management Routines + * + * Author: Thara Gopinath + * + * Copyright (C) 2009 Texas Instruments, Inc. + * Thara Gopinath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_VOLTAGE_H +#define __ARCH_ARM_MACH_OMAP2_VOLTAGE_H + +#define VOLTSCALE_VPFORCEUPDATE 1 +#define VOLTSCALE_VCBYPASS 2 + +/* Voltage SR Parameters for OMAP3*/ +#define OMAP3_SRI2C_SLAVE_ADDR 0x12 +#define OMAP3_VDD1_SR_CONTROL_REG 0x00 +#define OMAP3_VDD2_SR_CONTROL_REG 0x01 + +/* + * Omap3 VP register specific values. Maybe these need to come from + * board file or PMIC data structure + */ +#define OMAP3_VP_CONFIG_ERROROFFSET 0x00 +#define OMAP3_VP_VSTEPMIN_SMPSWAITTIMEMIN 0x3C +#define OMAP3_VP_VSTEPMIN_VSTEPMIN 0x1 +#define OMAP3_VP_VSTEPMAX_SMPSWAITTIMEMAX 0x3C +#define OMAP3_VP_VSTEPMAX_VSTEPMAX 0x04 +#define OMAP3_VP_VLIMITTO_TIMEOUT_US 0x200 + +/* + * Omap3430 specific VP register values. Maybe these need to come from + * board file or PMIC data structure + */ +#define OMAP3430_VP1_VLIMITTO_VDDMIN 0x14 +#define OMAP3430_VP1_VLIMITTO_VDDMAX 0x42 +#define OMAP3430_VP2_VLIMITTO_VDDMAX 0x2C +#define OMAP3430_VP2_VLIMITTO_VDDMIN 0x18 + +/* + * Omap3630 specific VP register values. Maybe these need to come from + * board file or PMIC data structure + */ +#define OMAP3630_VP1_VLIMITTO_VDDMIN 0x18 +#define OMAP3630_VP1_VLIMITTO_VDDMAX 0x3C +#define OMAP3630_VP2_VLIMITTO_VDDMIN 0x18 +#define OMAP3630_VP2_VLIMITTO_VDDMAX 0x30 + +/* TODO OMAP4 VP register values if the same file is used for OMAP4*/ + +/** + * voltagedomain - omap voltage domain global structure + * @name : Name of the voltage domain which can be used as a unique + * identifier. + */ +struct voltagedomain { + char *name; +}; + +/** + * omap_volt_data - Omap voltage specific data. + * @voltage_nominal : The possible voltage value in uV + * @sr_nvalue : Smartreflex N target value at voltage + * @sr_errminlimit : Error min limit value for smartreflex. This value + * differs at differnet opp and thus is linked + * with voltage. + * @vp_errorgain : Error gain value for the voltage processor. This + * field also differs according to the voltage/opp. + */ +struct omap_volt_data { + u32 volt_nominal; + u32 sr_nvalue; + u8 sr_errminlimit; + u8 vp_errgain; +}; + +/** + * omap_volt_pmic_info - PMIC specific data required by the voltage driver. + * @slew_rate : PMIC slew rate (in uv/us) + * @step_size : PMIC voltage step size (in uv) + */ +struct omap_volt_pmic_info { + int slew_rate; + int step_size; +}; + +/* Various voltage controller related info */ +struct omap_volt_vc_data { + u16 clksetup; + u16 voltsetup_time1; + u16 voltsetup_time2; + u16 voltoffset; + u16 voltsetup2; +/* PRM_VC_CMD_VAL_0 specific bits */ + u16 vdd0_on; + u16 vdd0_onlp; + u16 vdd0_ret; + u16 vdd0_off; +/* PRM_VC_CMD_VAL_1 specific bits */ + u16 vdd1_on; + u16 vdd1_onlp; + u16 vdd1_ret; + u16 vdd1_off; +}; + +struct voltagedomain *omap_voltage_domain_get(char *name); +unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm); +void omap_vp_enable(struct voltagedomain *voltdm); +void omap_vp_disable(struct voltagedomain *voltdm); +int omap_voltage_scale_vdd(struct voltagedomain *voltdm, + unsigned long target_volt); +void omap_voltage_reset(struct voltagedomain *voltdm); +int omap_voltage_get_volttable(struct voltagedomain *voltdm, + struct omap_volt_data **volt_data); +struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, + unsigned long volt); +void omap_voltage_register_pmic(struct omap_volt_pmic_info *pmic_info); +unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm); +#ifdef CONFIG_PM +void omap_voltage_init_vc(struct omap_volt_vc_data *setup_vc); +void omap_change_voltscale_method(int voltscale_method); +#else +static inline void omap_voltage_init_vc(struct omap_volt_vc_data *setup_vc) {} +static inline void omap_change_voltscale_method(int voltscale_method) {} +#endif + +#endif