Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

bus_freq.c

/*
 * Copyright 2009 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 bus_freq.c
 *
 * @brief A common API for the Freescale Semiconductor i.MXC CPUfreq module
 * and DVFS CORE module.
 *
 * The APIs are for setting bus frequency to low or high.
 *
 * @ingroup PM
 */

#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <mach/clock.h>
#include <mach/hardware.h>
#include <linux/regulator/consumer.h>
#include <mach/mxc_dvfs.h>

#include "iomux.h"
#include "crm_regs.h"

#define GP_LPM_VOLTAGE 850000
#define LP_LPM_VOLTAGE 1050000
#define LP_LOWFREQ_VOLTAGE 1050000
#define LP_NORMAL_VOLTAGE 1200000
#define GP_LPAPM_FREQ   200000000

DEFINE_SPINLOCK(bus_freq_lock);

struct clk *main_bus_clk;
struct clk *pll2;
struct clk *pll1;
struct clk *axi_a_clk;
struct clk *axi_b_clk;
struct clk *axi_c_clk;
struct clk *emi_core_clk;
struct clk *emi_intr_clk;
struct clk *nfc_clk;
struct clk *ahb_clk;
struct clk *vpu_clk;
struct clk *vpu_core_clk;
struct clk *arm_axi_clk;
struct clk *ddr_clk;
struct clk *ipu_clk;
struct clk *periph_apm_clk;
struct clk *lp_apm;
struct clk *cpu_clk;
struct clk *osc;
struct clk *uart_clk;
struct regulator *lp_regulator;
int low_bus_freq_mode;
int high_bus_freq_mode;
char *gp_reg_id = "SW1";
char *lp_reg_id = "SW2";
static struct cpu_wp *cpu_wp_tbl;
static int busfreq_suspended;
/* True if bus_frequency is scaled not using DVFS-PER */
int bus_freq_scaling_is_active;

struct dvfs_wp dvfs_core_setpoint[] = {
                                    {33, 8, 33, 10, 10, 0x08},
                                    {26, 0, 33, 20, 10, 0x08},
                                    {28, 8, 33, 20, 30, 0x08},
                                    {26, 0, 33, 20, 10, 0x08},};

int set_low_bus_freq(void)
{
      int ret = 0;
      unsigned long flags;
      unsigned long lp_lpm_clk;

      if (busfreq_suspended)
            return ret;

      if (low_bus_freq_mode || (clk_get_rate(cpu_clk) != GP_LPAPM_FREQ)) {
            return ret;
      }

      stop_dvfs_per();

      spin_lock_irqsave(&bus_freq_lock, flags);

      lp_lpm_clk = clk_get_rate(periph_apm_clk) / 8;

      /* Set the parent of peripheral_apm_clk to be pll1 */
      clk_set_parent(periph_apm_clk, pll1);
      /* Set the LP clocks */
      clk_set_parent(main_bus_clk, periph_apm_clk);

      clk_set_rate(axi_a_clk, clk_round_rate(axi_a_clk, lp_lpm_clk));
      clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, lp_lpm_clk));
      clk_set_rate(axi_c_clk, clk_round_rate(axi_c_clk, lp_lpm_clk));
      clk_set_rate(emi_core_clk, clk_round_rate(emi_core_clk, lp_lpm_clk));
      clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, lp_lpm_clk));
      /* Set the emi_intr_clk to be at 24MHz.  */
      clk_set_rate(emi_intr_clk, clk_round_rate(emi_intr_clk, lp_lpm_clk));

      low_bus_freq_mode = 1;
      high_bus_freq_mode = 0;

      spin_unlock_irqrestore(&bus_freq_lock, flags);

      /* Set the voltage to 1.05V for the LP domain. */
      ret = regulator_set_voltage(lp_regulator, 1050000, 1050000);
      udelay(100);
      if (ret < 0) {
            printk(KERN_ERR "COULD NOT SET LP VOLTAGE!!!!!!\n");
            return ret;
      }

      return ret;
}

int set_high_bus_freq(int high_bus_freq)
{
      int ret = 0;
      unsigned long flags;
      unsigned long lp_lpm_clk;

      if (!low_bus_freq_mode)
            return ret;

      if (dvfs_per_active())
            return ret;

      /* Set the voltage to 1.25V for the LP domain. */
      ret = regulator_set_voltage(lp_regulator, 1250000, 1250000);
      udelay(100);
      if (ret < 0) {
            printk(KERN_ERR "COULD NOT SET LP VOLTAGE!!!!!!\n");
            return ret;
      }

      spin_lock_irqsave(&bus_freq_lock, flags);

      low_bus_freq_mode = 0;

      /* Set the LP clocks. */
      lp_lpm_clk = clk_get_rate(periph_apm_clk);
      clk_set_rate(axi_a_clk, clk_round_rate(axi_a_clk, lp_lpm_clk/5));
      clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, lp_lpm_clk/5));
      clk_set_rate(axi_c_clk, clk_round_rate(axi_c_clk, lp_lpm_clk/5));
      clk_set_rate(emi_core_clk, clk_round_rate(emi_core_clk, lp_lpm_clk/5));
      clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, lp_lpm_clk/5));
      /* Set emi_intr clock back to divide by 2. */
      clk_set_rate(emi_intr_clk, clk_round_rate(emi_intr_clk, lp_lpm_clk/10));

      /* Set the parent of main_bus_clk to be pll2 */
      clk_set_parent(main_bus_clk, pll2);

      high_bus_freq_mode = 1;

      spin_unlock_irqrestore(&bus_freq_lock, flags);

      start_dvfs_per();
      return ret;
}

int low_freq_bus_used(void)
{
      if ((clk_get_usecount(ipu_clk) == 0)
          && (clk_get_usecount(vpu_clk) == 0))
            return 1;
      else
            return 0;
}

void setup_pll(void)
{
      u32 reg;
      u32 hfsm;
      struct cpu_wp *p;

      /* Setup the DPLL registers */
      hfsm = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL) &
             MXC_PLL_DP_CTL_HFSM;
      reg = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CONFIG);
      reg &= ~MXC_PLL_DP_CONFIG_AREN;
      __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CONFIG);

      if (hfsm) {
            /* Running at lower frequency, need to bump up. */
            p = &cpu_wp_tbl[0];
            /* PDF and MFI */
            reg = p->pdf | p->mfi << MXC_PLL_DP_OP_MFI_OFFSET;
            __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_OP);

            /* MFD */
            __raw_writel(p->mfd, MXC_DPLL1_BASE + MXC_PLL_DP_MFD);

            /* MFI */
            __raw_writel(p->mfn, MXC_DPLL1_BASE + MXC_PLL_DP_MFN);
      } else {
            /* Running at high frequency, need to lower it. */
            p = &cpu_wp_tbl[1];
            /* PDF and MFI */
            reg = p->pdf | p->mfi << MXC_PLL_DP_OP_MFI_OFFSET;
            __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_HFS_OP);

            /* MFD */
            __raw_writel(p->mfd, MXC_DPLL1_BASE + MXC_PLL_DP_HFS_MFD);

            /* MFN */
            __raw_writel(p->mfn, MXC_DPLL1_BASE + MXC_PLL_DP_HFS_MFN);
      }
      if (clk_get_usecount(pll2) != 0) {
            /* Set the temporal frequency to be PLL2 */
            /* Set PLL2_PODF to be 3. */
            reg = __raw_readl(MXC_CCM_CCSR);
            reg |= 2 << MXC_CCM_CCSR_PLL2_PODF_OFFSET;
            __raw_writel(reg, MXC_CCM_CCSR);
            /* Set the parent of STEP_CLK to be PLL2 */
            reg = __raw_readl(MXC_CCM_CCSR);
            reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
                (2 << MXC_CCM_CCSR_STEP_SEL_OFFSET);
            __raw_writel(reg, MXC_CCM_CCSR);
      } else {
            /* Set the temporal frequency to be lp-apm */
            /* Set the parent of STEP_CLK to be lp-apm */
            reg = __raw_readl(MXC_CCM_CCSR);
            reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
                (0 << MXC_CCM_CCSR_STEP_SEL_OFFSET);
            __raw_writel(reg, MXC_CCM_CCSR);
      }
}

static int busfreq_suspend(struct platform_device *pdev, pm_message_t message)
{
      if (low_bus_freq_mode)
            set_high_bus_freq(1);
      busfreq_suspended = 1;
      return 0;
}

static int busfreq_resume(struct platform_device *pdev)
{
      busfreq_suspended = 0;
      return  0;
}

/*!
 * This is the probe routine for the bus frequency driver.
 *
 * @param   pdev   The platform device structure
 *
 * @return         The function returns 0 on success
 *
 */
static int __devinit busfreq_probe(struct platform_device *pdev)
{
      int cpu_wp_nr;

      main_bus_clk = clk_get(NULL, "main_bus_clk");
      if (IS_ERR(main_bus_clk)) {
            printk(KERN_DEBUG "%s: failed to get main_bus_clk\n", __func__);
            return PTR_ERR(main_bus_clk);
      }

      pll2 = clk_get(NULL, "pll2");
      if (IS_ERR(pll2)) {
            printk(KERN_DEBUG "%s: failed to get pll2\n", __func__);
            return PTR_ERR(pll2);
      }

      axi_a_clk = clk_get(NULL, "axi_a_clk");
      if (IS_ERR(axi_a_clk)) {
            printk(KERN_DEBUG "%s: failed to get axi_a_clk\n", __func__);
            return PTR_ERR(axi_a_clk);
      }

      axi_b_clk = clk_get(NULL, "axi_b_clk");
      if (IS_ERR(axi_b_clk)) {
            printk(KERN_DEBUG "%s: failed to get axi_b_clk\n", __func__);
            return PTR_ERR(axi_b_clk);
      }

      axi_c_clk = clk_get(NULL, "axi_c_clk");
      if (IS_ERR(axi_c_clk)) {
            printk(KERN_DEBUG "%s: failed to get axi_c_clk\n", __func__);
            return PTR_ERR(axi_c_clk);
      }

      emi_core_clk = clk_get(NULL, "emi_core_clk");
      if (IS_ERR(emi_core_clk)) {
            printk(KERN_DEBUG "%s: failed to get emi_core_clk\n", __func__);
            return PTR_ERR(emi_core_clk);
      }

      emi_intr_clk = clk_get(NULL, "emi_intr_clk");
      if (IS_ERR(emi_intr_clk)) {
            printk(KERN_DEBUG "%s: failed to get emi_intr_clk\n", __func__);
            return PTR_ERR(emi_intr_clk);
      }

      nfc_clk = clk_get(NULL, "nfc_clk");
      if (IS_ERR(nfc_clk)) {
            printk(KERN_DEBUG "%s: failed to get nfc_clk\n", __func__);
            return PTR_ERR(nfc_clk);
      }

      ahb_clk = clk_get(NULL, "ahb_clk");
      if (IS_ERR(ahb_clk)) {
            printk(KERN_DEBUG "%s: failed to get ahb_clk\n", __func__);
            return PTR_ERR(ahb_clk);
      }

      vpu_core_clk = clk_get(NULL, "vpu_core_clk");
      if (IS_ERR(vpu_core_clk)) {
            printk(KERN_DEBUG "%s: failed to get vpu_core_clk\n", __func__);
            return PTR_ERR(vpu_core_clk);
      }

      arm_axi_clk = clk_get(NULL, "arm_axi_clk");
      if (IS_ERR(arm_axi_clk)) {
            printk(KERN_DEBUG "%s: failed to get arm_axi_clk\n", __func__);
            return PTR_ERR(arm_axi_clk);
      }

      ddr_clk = clk_get(NULL, "ddr_clk");
      if (IS_ERR(ddr_clk)) {
            printk(KERN_DEBUG "%s: failed to get ddr_clk\n", __func__);
            return PTR_ERR(ddr_clk);
      }

      ipu_clk = clk_get(NULL, "ipu_clk");
      if (IS_ERR(ipu_clk)) {
            printk(KERN_DEBUG "%s: failed to get ipu_clk\n", __func__);
            return PTR_ERR(ipu_clk);
      }

      vpu_clk = clk_get(NULL, "vpu_clk");
      if (IS_ERR(vpu_clk)) {
            printk(KERN_DEBUG "%s: failed to get vpu_clk\n", __func__);
            return PTR_ERR(vpu_clk);
      }

      periph_apm_clk = clk_get(NULL, "periph_apm_clk");
      if (IS_ERR(periph_apm_clk)) {
            printk(KERN_DEBUG "%s: failed to get periph_apm_clk\n",
                   __func__);
            return PTR_ERR(periph_apm_clk);
      }

      lp_apm = clk_get(NULL, "lp_apm");
      if (IS_ERR(lp_apm)) {
            printk(KERN_DEBUG "%s: failed to get lp_apm\n", __func__);
            return PTR_ERR(lp_apm);
      }

      cpu_clk = clk_get(NULL, "cpu_clk");
      if (IS_ERR(cpu_clk)) {
            printk(KERN_DEBUG "%s: failed to get cpu_clk\n", __func__);
            return PTR_ERR(cpu_clk);
      }

      osc = clk_get(NULL, "osc");
      if (IS_ERR(osc)) {
            printk(KERN_DEBUG "%s: failed to get osc\n", __func__);
            return PTR_ERR(osc);
      }

      uart_clk = clk_get(NULL, "uart_clk.0");
      if (IS_ERR(uart_clk)) {
            printk(KERN_DEBUG "%s: failed to get uart_clk-0\n", __func__);
            return PTR_ERR(uart_clk);
      }

      pll1 = clk_get(NULL, "pll1_sw_clk");
      if (IS_ERR(pll1)) {
            printk(KERN_DEBUG "%s: failed to get pll1_sw_clk\n", __func__);
            return PTR_ERR(pll1);
      }

      lp_regulator = regulator_get(NULL, lp_reg_id);
      if (IS_ERR(lp_regulator)) {
            clk_put(ahb_clk);
            printk(KERN_DEBUG "%s: failed to get lp regulator\n", __func__);
            return PTR_ERR(lp_regulator);
      }

      low_bus_freq_mode = 0;
      high_bus_freq_mode = 1;

      cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);

      return 0;
}

static struct platform_driver busfreq_driver = {
      .driver = {
               .name = "busfreq",
               },
      .probe = busfreq_probe,
      .suspend = busfreq_suspend,
      .resume = busfreq_resume,
};

/*!
 * Initialise the busfreq_driver.
 *
 * @return  The function always returns 0.
 */
static int __init busfreq_init(void)
{
      if (platform_driver_register(&busfreq_driver) != 0) {
            printk(KERN_ERR "busfreq_driver register failed\n");
            return -ENODEV;
      }

      printk(KERN_INFO "Bus freq driver module loaded\n");
      return 0;
}

static void __exit busfreq_cleanup(void)
{
      /* Unregister the device structure */
      platform_driver_unregister(&busfreq_driver);

      clk_put(main_bus_clk);
      clk_put(pll2);
      clk_put(axi_a_clk);
      clk_put(axi_b_clk);
      clk_put(axi_c_clk);
      clk_put(emi_core_clk);
      clk_put(emi_intr_clk);
      clk_put(nfc_clk);
      clk_put(ahb_clk);
      clk_put(vpu_core_clk);
      clk_put(arm_axi_clk);
      clk_put(ddr_clk);
      clk_put(ipu_clk);
      clk_put(periph_apm_clk);
      clk_put(lp_apm);
      clk_put(osc);
      clk_put(pll1);
      clk_put(pll2);
      regulator_put(lp_regulator);

}

module_init(busfreq_init);
module_exit(busfreq_cleanup);

MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("BusFreq driver");
MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index