summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/vp.c
diff options
context:
space:
mode:
authorKevin Hilman <khilman@ti.com>2011-03-21 22:29:13 +0100
committerKevin Hilman <khilman@ti.com>2011-09-15 20:39:11 +0200
commit01f48d308db1cfcfb0151d8d91153fd73e84ae40 (patch)
tree2b36a459bb9ea482cdf60cfaf79c4e4996d599dc /arch/arm/mach-omap2/vp.c
parentOMAP2+: voltage: enable VC bypass scale method when VC is initialized (diff)
downloadlinux-01f48d308db1cfcfb0151d8d91153fd73e84ae40.tar.xz
linux-01f48d308db1cfcfb0151d8d91153fd73e84ae40.zip
OMAP2+: voltage: split out voltage processor (VP) code into new layer
This patch is primarily a move of VP specific code from voltage.c into its own code in vp.c and adds prototypes to vp.h No functional changes, except debugfs... VP debugfs moved to 'vp' subdir of <debugfs>/voltage/ and 'vp_' prefixes removed from all debugfs filenames. Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'arch/arm/mach-omap2/vp.c')
-rw-r--r--arch/arm/mach-omap2/vp.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/vp.c b/arch/arm/mach-omap2/vp.c
new file mode 100644
index 000000000000..f3503de5d8fd
--- /dev/null
+++ b/arch/arm/mach-omap2/vp.c
@@ -0,0 +1,374 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+
+#include <plat/common.h>
+
+#include "voltage.h"
+#include "vp.h"
+#include "prm-regbits-34xx.h"
+#include "prm-regbits-44xx.h"
+#include "prm44xx.h"
+
+static void __init vp_debugfs_init(struct voltagedomain *voltdm);
+
+static void vp_latch_vsel(struct voltagedomain *voltdm)
+{
+ struct omap_vp_instance_data *vp = voltdm->vdd->vp_data;
+ u32 vpconfig;
+ unsigned long uvdc;
+ char vsel;
+ struct omap_vdd_info *vdd = voltdm->vdd;
+
+ uvdc = omap_voltage_get_nom_volt(voltdm);
+ if (!uvdc) {
+ pr_warning("%s: unable to find current voltage for vdd_%s\n",
+ __func__, voltdm->name);
+ return;
+ }
+
+ if (!vdd->pmic_info || !vdd->pmic_info->uv_to_vsel) {
+ pr_warning("%s: PMIC function to convert voltage in uV to"
+ " vsel not registered\n", __func__);
+ return;
+ }
+
+ vsel = vdd->pmic_info->uv_to_vsel(uvdc);
+
+ vpconfig = vdd->read_reg(vp->vp_common->prm_mod, vp->vpconfig);
+ vpconfig &= ~(vp->vp_common->vpconfig_initvoltage_mask |
+ vp->vp_common->vpconfig_initvdd);
+ vpconfig |= vsel << vp->vp_common->vpconfig_initvoltage_shift;
+
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+
+ /* Trigger initVDD value copy to voltage processor */
+ vdd->write_reg((vpconfig | vp->vp_common->vpconfig_initvdd),
+ vp->vp_common->prm_mod, vp->vpconfig);
+
+ /* Clear initVDD copy trigger bit */
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+}
+
+/* Generic voltage init functions */
+void __init omap_vp_init(struct voltagedomain *voltdm)
+{
+ struct omap_vp_instance_data *vp = voltdm->vdd->vp_data;
+ struct omap_vdd_info *vdd = voltdm->vdd;
+ u32 vp_val;
+
+ if (!vdd->read_reg || !vdd->write_reg) {
+ pr_err("%s: No read/write API for accessing vdd_%s regs\n",
+ __func__, voltdm->name);
+ return;
+ }
+
+ vp_val = vdd->vp_rt_data.vpconfig_erroroffset |
+ (vdd->vp_rt_data.vpconfig_errorgain <<
+ vp->vp_common->vpconfig_errorgain_shift) |
+ vp->vp_common->vpconfig_timeouten;
+ vdd->write_reg(vp_val, vp->vp_common->prm_mod, vp->vpconfig);
+
+ vp_val = ((vdd->vp_rt_data.vstepmin_smpswaittimemin <<
+ vp->vp_common->vstepmin_smpswaittimemin_shift) |
+ (vdd->vp_rt_data.vstepmin_stepmin <<
+ vp->vp_common->vstepmin_stepmin_shift));
+ vdd->write_reg(vp_val, vp->vp_common->prm_mod, vp->vstepmin);
+
+ vp_val = ((vdd->vp_rt_data.vstepmax_smpswaittimemax <<
+ vp->vp_common->vstepmax_smpswaittimemax_shift) |
+ (vdd->vp_rt_data.vstepmax_stepmax <<
+ vp->vp_common->vstepmax_stepmax_shift));
+ vdd->write_reg(vp_val, vp->vp_common->prm_mod, vp->vstepmax);
+
+ vp_val = ((vdd->vp_rt_data.vlimitto_vddmax <<
+ vp->vp_common->vlimitto_vddmax_shift) |
+ (vdd->vp_rt_data.vlimitto_vddmin <<
+ vp->vp_common->vlimitto_vddmin_shift) |
+ (vdd->vp_rt_data.vlimitto_timeout <<
+ vp->vp_common->vlimitto_timeout_shift));
+ vdd->write_reg(vp_val, vp->vp_common->prm_mod, vp->vlimitto);
+
+ vp_debugfs_init(voltdm);
+}
+
+/* VP force update method of voltage scaling */
+int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
+ unsigned long target_volt)
+{
+ struct omap_vp_instance_data *vp = voltdm->vdd->vp_data;
+ struct omap_vdd_info *vdd = voltdm->vdd;
+ u32 vpconfig;
+ u8 target_vsel, current_vsel;
+ int ret, timeout = 0;
+
+ ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
+ if (ret)
+ return ret;
+
+ /*
+ * Clear all pending TransactionDone interrupt/status. Typical latency
+ * is <3us
+ */
+ while (timeout++ < VP_TRANXDONE_TIMEOUT) {
+ vdd->write_reg(vp->prm_irqst_data->tranxdone_status,
+ vdd->prm_irqst_mod, vdd->prm_irqst_reg);
+ if (!(vdd->read_reg(vdd->prm_irqst_mod, vdd->prm_irqst_reg) &
+ vp->prm_irqst_data->tranxdone_status))
+ break;
+ udelay(1);
+ }
+ if (timeout >= VP_TRANXDONE_TIMEOUT) {
+ pr_warning("%s: vdd_%s TRANXDONE timeout exceeded."
+ "Voltage change aborted", __func__, voltdm->name);
+ return -ETIMEDOUT;
+ }
+
+ /* Configure for VP-Force Update */
+ vpconfig = vdd->read_reg(vp->vp_common->prm_mod, vp->vpconfig);
+ vpconfig &= ~(vp->vp_common->vpconfig_initvdd |
+ vp->vp_common->vpconfig_forceupdate |
+ vp->vp_common->vpconfig_initvoltage_mask);
+ vpconfig |= ((target_vsel <<
+ vp->vp_common->vpconfig_initvoltage_shift));
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+
+ /* Trigger initVDD value copy to voltage processor */
+ vpconfig |= vp->vp_common->vpconfig_initvdd;
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+
+ /* Force update of voltage */
+ vpconfig |= vp->vp_common->vpconfig_forceupdate;
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+
+ /*
+ * Wait for TransactionDone. Typical latency is <200us.
+ * Depends on SMPSWAITTIMEMIN/MAX and voltage change
+ */
+ timeout = 0;
+ omap_test_timeout((vdd->read_reg(vdd->prm_irqst_mod,
+ vdd->prm_irqst_reg) &
+ vp->prm_irqst_data->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__, voltdm->name);
+
+ omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
+
+ /*
+ * Disable TransactionDone interrupt , clear all status, clear
+ * control registers
+ */
+ timeout = 0;
+ while (timeout++ < VP_TRANXDONE_TIMEOUT) {
+ vdd->write_reg(vp->prm_irqst_data->tranxdone_status,
+ vdd->prm_irqst_mod, vdd->prm_irqst_reg);
+ if (!(vdd->read_reg(vdd->prm_irqst_mod, vdd->prm_irqst_reg) &
+ vp->prm_irqst_data->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__, voltdm->name);
+
+ vpconfig = vdd->read_reg(vp->vp_common->prm_mod, vp->vpconfig);
+ /* Clear initVDD copy trigger bit */
+ vpconfig &= ~vp->vp_common->vpconfig_initvdd;
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+ /* Clear force bit */
+ vpconfig &= ~vp->vp_common->vpconfig_forceupdate;
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+
+ return 0;
+}
+
+/**
+ * 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_vp_instance_data *vp = voltdm->vdd->vp_data;
+ 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 = voltdm->vdd;
+ if (!vdd->read_reg) {
+ pr_err("%s: No read API for reading vdd_%s regs\n",
+ __func__, voltdm->name);
+ return 0;
+ }
+
+ curr_vsel = vdd->read_reg(vp->vp_common->prm_mod, vp->voltage);
+
+ if (!vdd->pmic_info || !vdd->pmic_info->vsel_to_uv) {
+ pr_warning("%s: PMIC function to convert vsel to voltage"
+ "in uV not registerd\n", __func__);
+ return 0;
+ }
+
+ return vdd->pmic_info->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_vp_instance_data *vp;
+ struct omap_vdd_info *vdd;
+ u32 vpconfig;
+
+ if (!voltdm || IS_ERR(voltdm)) {
+ pr_warning("%s: VDD specified does not exist!\n", __func__);
+ return;
+ }
+
+ vdd = voltdm->vdd;
+ vp = voltdm->vdd->vp_data;
+ if (!vdd->read_reg || !vdd->write_reg) {
+ pr_err("%s: No read/write API for accessing vdd_%s regs\n",
+ __func__, voltdm->name);
+ return;
+ }
+
+ /* If VP is already enabled, do nothing. Return */
+ if (vdd->vp_enabled)
+ return;
+
+ vp_latch_vsel(voltdm);
+
+ /* Enable VP */
+ vpconfig = vdd->read_reg(vp->vp_common->prm_mod, vp->vpconfig);
+ vpconfig |= vp->vp_common->vpconfig_vpenable;
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+ vdd->vp_enabled = true;
+}
+
+/**
+ * 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_vp_instance_data *vp;
+ 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 = voltdm->vdd;
+ vp = voltdm->vdd->vp_data;
+ if (!vdd->read_reg || !vdd->write_reg) {
+ pr_err("%s: No read/write API for accessing vdd_%s regs\n",
+ __func__, voltdm->name);
+ return;
+ }
+
+ /* If VP is already disabled, do nothing. Return */
+ if (!vdd->vp_enabled) {
+ pr_warning("%s: Trying to disable VP for vdd_%s when"
+ "it is already disabled\n", __func__, voltdm->name);
+ return;
+ }
+
+ /* Disable VP */
+ vpconfig = vdd->read_reg(vp->vp_common->prm_mod, vp->vpconfig);
+ vpconfig &= ~vp->vp_common->vpconfig_vpenable;
+ vdd->write_reg(vpconfig, vp->vp_common->prm_mod, vp->vpconfig);
+
+ /*
+ * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
+ */
+ omap_test_timeout((vdd->read_reg(vp->vp_common->prm_mod, vp->vstatus)),
+ VP_IDLE_TIMEOUT, timeout);
+
+ if (timeout >= VP_IDLE_TIMEOUT)
+ pr_warning("%s: vdd_%s idle timedout\n",
+ __func__, voltdm->name);
+
+ vdd->vp_enabled = false;
+
+ return;
+}
+
+/* Voltage debugfs support */
+static int vp_volt_debug_get(void *data, u64 *val)
+{
+ struct voltagedomain *voltdm = (struct voltagedomain *)data;
+ struct omap_vp_instance_data *vp = voltdm->vdd->vp_data;
+ struct omap_vdd_info *vdd = voltdm->vdd;
+ u8 vsel;
+
+ if (!vdd) {
+ pr_warning("Wrong paramater passed\n");
+ return -EINVAL;
+ }
+
+ vsel = vdd->read_reg(vp->vp_common->prm_mod, vp->voltage);
+
+ if (!vdd->pmic_info->vsel_to_uv) {
+ pr_warning("PMIC function to convert vsel to voltage"
+ "in uV not registerd\n");
+ return -EINVAL;
+ }
+
+ *val = vdd->pmic_info->vsel_to_uv(vsel);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vp_volt_debug_fops, vp_volt_debug_get, NULL, "%llu\n");
+
+static void __init vp_debugfs_init(struct voltagedomain *voltdm)
+{
+ struct omap_vdd_info *vdd = voltdm->vdd;
+ struct dentry *debug_dir;
+
+ debug_dir = debugfs_create_dir("vp", vdd->debug_dir);
+ if (IS_ERR(debug_dir))
+ pr_err("%s: Unable to create VP debugfs dir dir\n", __func__);
+
+ (void) debugfs_create_x16("errorgain", S_IRUGO, debug_dir,
+ &(vdd->vp_rt_data.vpconfig_errorgain));
+ (void) debugfs_create_x16("smpswaittimemin", S_IRUGO,
+ debug_dir,
+ &(vdd->vp_rt_data.vstepmin_smpswaittimemin));
+ (void) debugfs_create_x8("stepmin", S_IRUGO, debug_dir,
+ &(vdd->vp_rt_data.vstepmin_stepmin));
+ (void) debugfs_create_x16("smpswaittimemax", S_IRUGO,
+ debug_dir,
+ &(vdd->vp_rt_data.vstepmax_smpswaittimemax));
+ (void) debugfs_create_x8("stepmax", S_IRUGO, debug_dir,
+ &(vdd->vp_rt_data.vstepmax_stepmax));
+ (void) debugfs_create_x8("vddmax", S_IRUGO, debug_dir,
+ &(vdd->vp_rt_data.vlimitto_vddmax));
+ (void) debugfs_create_x8("vddmin", S_IRUGO, debug_dir,
+ &(vdd->vp_rt_data.vlimitto_vddmin));
+ (void) debugfs_create_x16("timeout", S_IRUGO, debug_dir,
+ &(vdd->vp_rt_data.vlimitto_timeout));
+ (void) debugfs_create_file("curr_volt", S_IRUGO, debug_dir,
+ (void *) voltdm, &vp_volt_debug_fops);
+}