diff options
Diffstat (limited to 'drivers/mxc/pm/dptc.c')
-rw-r--r-- | drivers/mxc/pm/dptc.c | 890 |
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, ¶ms->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(¶ms->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(¶ms->dptc_log_buffer.mutex)) { + return -EAGAIN; + } + + if (num_of_entries == 0 && offset == 0) { + inc_log_index(¶ms->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 *)¶ms->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 *)¶ms->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(¶ms->dptc_log_buffer.tail, entries_to_read); + + /* Up the log buffer mutex to allow access to the log buffer */ + up(¶ms->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, ¶ms->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, ¶ms->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); |