summaryrefslogtreecommitdiff
path: root/drivers/mxc/pm/dptc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/pm/dptc.c')
-rw-r--r--drivers/mxc/pm/dptc.c890
1 files changed, 890 insertions, 0 deletions
diff --git a/drivers/mxc/pm/dptc.c b/drivers/mxc/pm/dptc.c
new file mode 100644
index 000000000000..db28b95fc6f3
--- /dev/null
+++ b/drivers/mxc/pm/dptc.c
@@ -0,0 +1,890 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file dptc.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC DPTC module.
+ *
+ * The DPTC driver
+ * driver is designed as a character driver which interacts with the MXC DPTC
+ * hardware. Upon initialization, the DPTC driver initializes the DPTC hardware
+ * sets up driver nodes attaches to the DPTC interrupt and initializes internal
+ * data structures. When the DPTC interrupt occurs the driver checks the cause
+ * of the interrupt (lower voltage, increase voltage or emergency) and changes
+ * the CPU voltage according to translation table that is loaded into the driver
+ * (the voltage changes are done by calling some routines in the mc13783 driver).
+ * The driver read method is used to read the currently loaded DPTC translation
+ * table and the write method is used in-order to update the translation table.
+ * Driver ioctls are used to change driver parameters and enable/disable the
+ * DPTC operation.
+ *
+ * @ingroup PM_MX31
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <asm/semaphore.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <asm/arch/sdma.h>
+#include <asm/arch/pmic_power.h>
+
+/*
+ * Module header file
+ */
+#include <asm/arch/dptc.h>
+
+/*
+ * CRM registers
+ */
+#ifdef CONFIG_ARCH_MX3
+#include "../../../arch/arm/mach-mx3/crm_regs.h"
+#endif
+
+/*!
+ * The dvfs_dptc_params structure holds all the internal DPTC driver parameters
+ * (current working point, current frequency, translation table and DPTC
+ * log buffer).
+ */
+static dvfs_dptc_params_s *dvfs_dptc_params;
+
+static struct delayed_work dptc_work;
+
+#ifndef CONFIG_MXC_DVFS_SDMA
+static unsigned long ptvai;
+#endif
+
+/*!
+ * Spinlock to protect CRM register accesses
+ */
+static DEFINE_SPINLOCK(mxc_crm_lock);
+
+/*!
+ * This function is called to read the contents of a CCM_MCU register
+ *
+ * @param reg_offset the CCM_MCU register that will read
+ *
+ * @return the register contents
+ */
+static unsigned long mxc_ccm_get_reg(unsigned int reg_offset)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(reg_offset);
+ return reg;
+}
+
+/*!
+ * This function is called to modify the contents of a CCM_MCU register
+ *
+ * @param reg_offset the CCM_MCU register that will read
+ * @param mask the mask to be used to clear the bits that are to be modified
+ * @param data the data that should be written to the register
+ */
+static void mxc_ccm_modify_reg(unsigned int reg_offset, unsigned int mask,
+ unsigned int data)
+{
+ unsigned long flags;
+ unsigned long reg;
+
+ spin_lock_irqsave(&mxc_crm_lock, flags);
+ reg = __raw_readl(reg_offset);
+ reg = (reg & (~mask)) | data;
+ __raw_writel(reg, reg_offset);
+ spin_unlock_irqrestore(&mxc_crm_lock, flags);
+}
+
+/*!
+ * Enable DPTC hardware
+ */
+static void dptc_enable_dptc(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DPTEN,
+ MXC_CCM_PMCR0_DPTEN);
+}
+
+/*!
+ * Disable DPTC hardware
+ */
+static void dptc_disable_dptc(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DPTEN, 0);
+}
+
+/*!
+ * Mask DPTC interrupt
+ */
+static void dptc_mask_dptc_int(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVAIM,
+ MXC_CCM_PMCR0_PTVAIM);
+}
+
+/*!
+ * Unmask DPTC interrupt
+ */
+static void dptc_unmask_dptc_int(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVAIM, 0);
+}
+
+/*!
+ * Read the PTVAI bits from the CCM
+ *
+ * @return PTVAI bits value
+ */
+static unsigned long dptc_get_ptvai(void)
+{
+ return (mxc_ccm_get_reg(MXC_CCM_PMCR0) & MXC_CCM_PMCR0_PTVAI_MASK)
+ >> MXC_CCM_PMCR0_PTVAI_OFFSET;
+}
+
+/*!
+ * Clear DCR bits of the CCM
+ */
+static void dptc_clear_dcr(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DCR, 0);
+}
+
+/*!
+ * If value equal to PTVAI bits indicates working point decrease
+ */
+#define DPTC_DECREASE (unsigned long)0x1
+
+/*!
+ * If value equal to PTVAI bits indicates working point increase
+ */
+#define DPTC_INCREASE (unsigned long)0x2
+
+/*!
+ * If value equal to PTVAI bits indicates working point increase to maximum
+ */
+#define DPTC_EMERG (unsigned long)0x3
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DVFS_SDMA
+/*
+ * MCU will get DPTC interrupt
+ */
+static void dptc_set_ptvis(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVIS,
+ MXC_CCM_PMCR0_PTVIS);
+}
+#endif
+#endif
+
+/*!
+ * This function enables the DPTC reference circuits.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param rc_state each high bit specifies which
+ * reference circuite to enable
+ * @return 0 on success, error code on failure
+ */
+int enable_ref_circuits(dvfs_dptc_params_s * params, unsigned char rc_state)
+{
+ int ret_val;
+
+ if (params->dptc_is_active == FALSE) {
+ params->rc_state = rc_state;
+
+ if (rc_state & 0x1) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE0,
+ MXC_CCM_PMCR0_DRCE0);
+ pr_debug("Ref circuit 0 enabled\n");
+ }
+ if (rc_state & 0x2) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE1,
+ MXC_CCM_PMCR0_DRCE1);
+ pr_debug("Ref circuit 1 enabled\n");
+ }
+ if (rc_state & 0x4) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE2,
+ MXC_CCM_PMCR0_DRCE2);
+ pr_debug("Ref circuit 2 enabled\n");
+ }
+ if (rc_state & 0x8) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE3,
+ MXC_CCM_PMCR0_DRCE3);
+ pr_debug("Ref circuit 3 enabled\n");
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function disables the DPTC reference circuits.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param rc_state each high bit specifies which
+ * reference circuite to disable
+ * @return 0 on success, error code on failure
+ */
+int disable_ref_circuits(dvfs_dptc_params_s * params, unsigned char rc_state)
+{
+ int ret_val;
+
+ if (params->dptc_is_active == FALSE) {
+ params->rc_state &= ~rc_state;
+
+ if (rc_state & 0x1) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE0, 0);
+ pr_debug("Ref circuit 0 disabled\n");
+ }
+ if (rc_state & 0x2) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE1, 0);
+ pr_debug("Ref circuit 1 disabled\n");
+ }
+ if (rc_state & 0x4) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE2, 0);
+ pr_debug("Ref circuit 2 disabled\n");
+ }
+ if (rc_state & 0x8) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE3, 0);
+ pr_debug("Ref circuit 3 disabled\n");
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+static void dptc_workqueue_handler(struct work_struct *work)
+{
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) work_data_bits(work);
+
+ pr_debug("In %s: PTVAI = %lu\n", __FUNCTION__, dptc_get_ptvai());
+ pr_debug("PMCR0 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_PMCR0));
+ pr_debug("DCVR0 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR0));
+ pr_debug("DCVR1 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR1));
+ pr_debug("DCVR2 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR2));
+ pr_debug("DCVR3 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR3));
+ pr_debug("PTVAI = 0x%lx\n", ptvai);
+}
+
+/*!
+ * This function is the DPTC Interrupt handler.
+ * This function wakes-up the dptc_workqueue_handler function that handles the
+ * DPTC interrupt.
+ */
+void dptc_irq(void)
+{
+#ifndef CONFIG_MXC_DVFS_SDMA
+ ptvai = dptc_get_ptvai();
+
+ pr_debug("ptvai = 0x%lx (0x%x)!!!!!!!\n", ptvai,
+ __raw_readl(MXC_CCM_PMCR0));
+ if (ptvai != 0) {
+ dptc_mask_dptc_int();
+ dptc_disable_dptc();
+
+ schedule_delayed_work(&dptc_work, 0);
+ }
+#else
+ schedule_delayed_work(&dptc_work, 0);
+#endif
+}
+
+/*!
+ * This function updates the CPU voltage, produced by mc13783, by calling mc13783
+ * driver functions.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ * frequency.
+ *
+ */
+void set_pmic_voltage(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr, int wp)
+{
+ /* Call mc13783 functions */
+ t_regulator_voltage volt;
+
+ /* Normal mode setting */
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[0];
+ pmic_power_regulator_set_voltage(SW_SW1A, volt);
+
+#ifdef CONFIG_MXC_DVFS
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[1];
+ pmic_power_switcher_set_dvs(SW_SW1A, volt);
+
+ volt.sw1b = dvfs_dptc_tables_ptr->wp[wp].pmic_values[2];
+ pmic_power_switcher_set_dvs(SW_SW1B, volt);
+
+ volt.sw1b = dvfs_dptc_tables_ptr->wp[wp].pmic_values[3];
+ pmic_power_switcher_set_stby(SW_SW1B, volt);
+#endif
+
+ if (cpu_is_mx31()) {
+ pr_debug("DPVV = 0x%lx (0x%lx)\n",
+ mxc_ccm_get_reg(MXC_CCM_PMCR0) & MXC_CCM_PMCR0_DPVV,
+ mxc_ccm_get_reg(MXC_CCM_PMCR0));
+ }
+}
+
+/*!
+ * This function enables the DPTC module. this function updates the DPTC
+ * thresholds, updates the mc13783, unmasks the DPTC interrupt and enables
+ * the DPTC module
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ *
+ * @return 0 if DPTC module was enabled else returns -EINVAL.
+ */
+int start_dptc(dvfs_dptc_params_s * params)
+{
+ int freq_index = 0;
+
+ /* Check if DPTC module isn't already active */
+ if (params->dptc_is_active == FALSE) {
+
+ enable_ref_circuits(params, params->rc_state);
+ disable_ref_circuits(params, ~params->rc_state);
+
+ /*
+ * Set the DPTC thresholds and mc13783 voltage to
+ * correspond to the current working point and frequency.
+ */
+ set_pmic_voltage(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp);
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dvfs_get_dvsup();
+#endif
+
+ update_dptc_thresholds(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ freq_index);
+
+ /* Mark DPCT module as active */
+ params->dptc_is_active = TRUE;
+
+ /* Enable the DPTC module and unmask the DPTC interrupt */
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+
+ return 0;
+ }
+
+ /* DPTC module already active return error */
+ return -EINVAL;
+}
+
+/*!
+ * This function disables the DPTC module.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ *
+ * @return 0 if DPTC module was disabled else returns -EINVAL.
+ */
+int stop_dptc(dvfs_dptc_params_s * params)
+{
+ /* Check if DPTC module isn't already disabled */
+ if (params->dptc_is_active != FALSE) {
+
+ /* Disable the DPTC module and mask the DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+ /* Set working point 0 */
+ set_dptc_wp(params, 0);
+
+ /* Mark DPCT module as inactive */
+ params->dptc_is_active = FALSE;
+
+ return 0;
+ }
+
+ /* DPTC module already disabled, return error */
+ return -EINVAL;
+}
+
+/*!
+ * This function updates the drivers current working point index. This index is
+ * used for access the current DTPC table entry and it corresponds to the
+ * current CPU working point measured by the DPTC hardware.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param new_wp New working point index value to be set.
+ *
+ */
+void set_dptc_wp(dvfs_dptc_params_s * params, int new_wp)
+{
+ int freq_index = 0;
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dvfs_get_dvsup();
+#endif
+
+ /*
+ * Check if new index is smaller than the maximal working point
+ * index in the DPTC translation table and larger that 0.
+ */
+ if ((new_wp < params->dvfs_dptc_tables_ptr->wp_num)
+ && (new_wp >= 0)) {
+ /* Set current working point index to new index */
+ params->dvfs_dptc_tables_ptr->curr_wp = new_wp;
+ }
+
+ /*
+ * Check if new index is larger than the maximal working point index in
+ * the DPTC translation table.
+ */
+ if (new_wp >= params->dvfs_dptc_tables_ptr->wp_num) {
+ /*
+ * Set current working point index to maximal working point
+ * index in the DPTC translation table.
+ */
+ params->dvfs_dptc_tables_ptr->curr_wp =
+ params->dvfs_dptc_tables_ptr->wp_num - 1;
+ }
+
+ /* Check if new index is smaller than 0. */
+ if (new_wp < 0) {
+ /* Set current working point index to 0 (minimal value) */
+ params->dvfs_dptc_tables_ptr->curr_wp = 0;
+ }
+#ifndef CONFIG_MXC_DVFS_SDMA
+ /* Update the DPTC hardware thresholds */
+ update_dptc_thresholds(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ freq_index);
+#else
+ params->prev_wp = params->dvfs_dptc_tables_ptr->curr_wp;
+#endif
+
+ /* Update the mc13783 voltage */
+ set_pmic_voltage(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp);
+
+ /* Write DPTC changes in the DPTC log buffer */
+ add_dptc_log_entry(params, &params->dptc_log_buffer,
+ params->dvfs_dptc_tables_ptr->curr_wp, freq_index);
+
+ pr_debug("Current wp: %d\n", params->dvfs_dptc_tables_ptr->curr_wp);
+}
+
+/*!
+ * This function updates the DPTC threshold registers.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ * @param freq_index translation table index of the current CPU
+ * frequency.
+ *
+ */
+void update_dptc_thresholds(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr,
+ int wp, int freq_index)
+{
+ dcvr_state *dcvr;
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+
+ dcvr_state **dcvr_arr;
+ dcvr_state *curr_freq_dcvr;
+
+ dcvr_arr = dvfs_dptc_tables_ptr->dcvr;
+ dcvr_arr = sdma_phys_to_virt((unsigned long)dcvr_arr);
+ curr_freq_dcvr = dcvr_arr[freq_index];
+ curr_freq_dcvr = sdma_phys_to_virt((unsigned long)curr_freq_dcvr);
+ dcvr = &curr_freq_dcvr[wp];
+#else
+ /* Calculate current table entry offset in the DPTC translation table */
+ dcvr = &dvfs_dptc_tables_ptr->dcvr[freq_index][wp];
+#endif
+ /* Update DPTC threshold registers */
+ mxc_ccm_modify_reg(MXC_CCM_DCVR0, 0xffffffff, dcvr->dcvr_reg[0].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR1, 0xffffffff, dcvr->dcvr_reg[1].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR2, 0xffffffff, dcvr->dcvr_reg[2].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR3, 0xffffffff, dcvr->dcvr_reg[3].AsInt);
+}
+
+/*!
+ * This function increments a log buffer index (head or tail)
+ * by the value of val.
+ *
+ * @param index pointer to the DPTC log buffer index that
+ * we wish to change.
+ * @param val the value in which the index should be incremented.
+ *
+ */
+static void inc_log_index(int *index, int val)
+{
+ *index = (*index + val) % LOG_ENTRIES;
+}
+
+/*!
+ * This function returns the number of entries in the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+static int get_entry_count(dptc_log_s * dptc_log)
+{
+ return ((dptc_log->head - dptc_log->tail + LOG_ENTRIES) % LOG_ENTRIES);
+}
+
+/*!
+ * This function is used by the proc file system to read the DPTC log buffer.
+ * Each time the DPTC proc file system file is read this function is called
+ * and returns the data written in the log buffer.
+ *
+ * @param buf pointer to the buffer the data should be written to.
+ * @param start pointer to the pointer where the new data is
+ * written to.
+ * procedure should update the start pointer to point to
+ * where in the buffer the data was written.
+ * @param offset current offset in the DPTC proc file.
+ * @param count number of bytes to read.
+ * @param eof pointer to eof flag. should be set to 1 when
+ * reaching eof.
+ * @param data driver specific data pointer.
+ *
+ * @return number byte read from the log buffer.
+ *
+ */
+static int read_log(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int entries_to_read;
+ int num_of_entries;
+ int entries_to_end_of_buffer, entries_left;
+ void *entry_ptr;
+ char *buf_ptr;
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) data;
+
+ /* Calculate number of log entries to read */
+ entries_to_read = count / sizeof(dptc_log_entry_s);
+ /* Get the number of current log buffer entries */
+ num_of_entries = get_entry_count(&params->dptc_log_buffer);
+
+ /*
+ * If number of entries to read is larger that the number of entries
+ * in the log buffer set number of entries to read to number of
+ * entries in the log buffer and set eof flag to 1
+ */
+ if (num_of_entries < entries_to_read) {
+ entries_to_read = num_of_entries;
+ *eof = 1;
+ }
+
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&params->dptc_log_buffer.mutex)) {
+ return -EAGAIN;
+ }
+
+ if (num_of_entries == 0 && offset == 0) {
+ inc_log_index(&params->dptc_log_buffer.tail, -1);
+ num_of_entries++;
+ entries_to_read++;
+ }
+
+ /* get the pointer of the last (oldest) entry in the log buffer */
+ entry_ptr = (void *)&params->dptc_log_buffer.
+ entries[params->dptc_log_buffer.tail];
+
+ /* Check if tail index wraps during current read */
+ if ((params->dptc_log_buffer.tail + entries_to_read) < LOG_ENTRIES) {
+ /* No tail wrap around copy data from log buffer to buf */
+ memcpy(buf, entry_ptr,
+ (entries_to_read * sizeof(dptc_log_entry_s)));
+ } else {
+ /*
+ * Tail wrap around.
+ * First copy data from current position until end of buffer,
+ * after that copy the rest from start of the log buffer.
+ */
+ entries_to_end_of_buffer = LOG_ENTRIES -
+ params->dptc_log_buffer.tail;
+ memcpy(buf, entry_ptr,
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s)));
+
+ entry_ptr = (void *)&params->dptc_log_buffer.entries[0];
+ buf_ptr = buf +
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s));
+ entries_left = entries_to_read - entries_to_end_of_buffer;
+ memcpy(buf_ptr, entry_ptr,
+ (entries_left * sizeof(dptc_log_entry_s)));
+ }
+
+ /* Increment the tail index by the number of entries read */
+ inc_log_index(&params->dptc_log_buffer.tail, entries_to_read);
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&params->dptc_log_buffer.mutex);
+
+ /* set start of data to point to buf */
+ *start = buf;
+
+ return (entries_to_read * sizeof(dptc_log_entry_s));
+}
+
+/*!
+ * This function initializes the DPTC log buffer.
+ *
+ * @param params pointer to \b dvfs_dptc_params_s.
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ */
+void init_dptc_log(dvfs_dptc_params_s * params, dptc_log_s * dptc_log)
+{
+ dptc_log->tail = 0;
+ dptc_log->head = 0;
+
+ /* initialize log buffer mutex used for accessing the log buffer */
+ sema_init(&dptc_log->mutex, 1);
+
+ /* add the first log buffer entry */
+ add_dptc_log_entry(params, dptc_log,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ params->current_freq_index);
+}
+
+/*!
+ * This function adds a new entry to the DPTC log buffer.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ * @param wp value of the working point index written
+ * to the log buffer.
+ * @param freq_index value of the frequency index written to
+ * the log buffer.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+void add_dptc_log_entry(dvfs_dptc_params_s * params,
+ dptc_log_s * dptc_log, int wp, int freq_index)
+{
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&dptc_log->mutex)) {
+ return;
+ }
+
+ /* Write values to log buffer */
+ dptc_log->entries[dptc_log->head].jiffies = jiffies;
+ dptc_log->entries[dptc_log->head].wp = wp;
+
+ dptc_log->entries[dptc_log->head].voltage = wp;
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dptc_get_ptvai();
+#endif
+
+ dptc_log->entries[dptc_log->head].freq = freq_index;
+
+ /* Increment the head index by 1 */
+ inc_log_index(&dptc_log->head, 1);
+ /* If head index reaches the tail increment the tail by 1 */
+ if (dptc_log->head == dptc_log->tail) {
+ inc_log_index(&dptc_log->tail, 1);
+ }
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&dptc_log->mutex);
+}
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ */
+void dptc_suspend(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params->dptc_is_active) {
+ dptc_disable_dptc();
+ set_dptc_wp(dvfs_dptc_params,
+ dvfs_dptc_params->
+ dvfs_dptc_tables_ptr->curr_wp - 1);
+ }
+#endif
+}
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ */
+void dptc_resume(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params->dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+#endif
+}
+
+/*!
+ * This function initializes the DPTC hardware
+ *
+ * @param params pointer to the DPTC driver parameters structure.
+ *
+ */
+int __init init_dptc_controller(dvfs_dptc_params_s * params)
+{
+ dvfs_dptc_params = params;
+
+ INIT_DELAYED_WORK(&dptc_work, dptc_workqueue_handler);
+
+ if (create_proc_read_entry(PROC_NODE_NAME, 0,
+ NULL, read_log, params) == NULL) {
+ /*
+ * Error creating proc file system entry.
+ * Exit and return error code
+ */
+ printk(KERN_ERR "DPTC: Unable create proc entry");
+ return -EFAULT;
+ }
+
+ /* Initialize the DPTC log buffer */
+ init_dptc_log(params, &params->dptc_log_buffer);
+
+ set_dptc_curr_freq(params, 0);
+ set_dptc_wp(params, 0);
+
+ /* By default all reference circuits are enabled */
+ params->rc_state = DPTC_REF_CIRCUITS_STATUS;
+
+ dptc_clear_dcr();
+
+ /* Disable DPTC hardware and mask DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DVFS_SDMA
+ dptc_set_ptvis();
+#endif
+#endif
+ printk(KERN_INFO "DPTC controller initialized\n");
+
+ return 0;
+}
+
+/*!
+ * This function updates the drivers current frequency index.This index is
+ * used for access the current DTPC table entry and it corresponds to the
+ * current CPU frequency (each CPU frequency has a separate index number
+ * according to the loaded DPTC table).
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param freq_index New frequency index value to be set.
+ *
+ * @return 0 if the frequency index was updated (the new index is a
+ * valid index and the DPTC module isn't active) else returns
+ * -EINVAL.
+ *
+ */
+int set_dptc_curr_freq(dvfs_dptc_params_s * params, unsigned int freq_index)
+{
+ /*
+ * Check if the new index value is a valid frequency index (smaller
+ * than the maximal index in the DPTC table) and if the DPTC module
+ * is disabled.
+ */
+ if ((freq_index < params->dvfs_dptc_tables_ptr->dvfs_state_num)
+ && (params->dptc_is_active == FALSE)) {
+ /*
+ * Index is valid and DPTC module is
+ * disabled -> change frequency index.
+ */
+ params->current_freq_index = freq_index;
+ add_dptc_log_entry(params, &params->dptc_log_buffer,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ params->current_freq_index);
+
+ return 0;
+ }
+
+ /* Invalid index or DPTC module is active -> return error */
+ return -EINVAL;
+}
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*
+ * DPTC SDMA callback.
+ * Updates the mc13783 voltage
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ */
+void dptc_sdma_callback(dvfs_dptc_params_s * params)
+{
+ printk(KERN_INFO "In %s: params->dvfs_dptc_tables_ptr->curr_wp = %d\n",
+ __FUNCTION__, params->dvfs_dptc_tables_ptr->curr_wp);
+}
+#endif
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dptc_suspend();
+ return 0;
+}
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_resume(struct platform_device *pdev)
+{
+ dptc_resume();
+ return 0;
+}
+
+EXPORT_SYMBOL(dptc_suspend);
+EXPORT_SYMBOL(dptc_resume);