OMAP2: clock: add DPLL autoidle support

Add the necessary code and data to allow the clock framework to enable
and disable the OMAP2 DPLL autoidle state.  This is so the direct
register access can be moved out of the mach-omap2/pm24xx.c code, and other
code that needs to control this (e.g., CPUIdle) can do so via an API.
As part of this patch, remove the pm24xx.c code that formerly wrote
directly to the autoidle bits.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Kevin Hilman <khilman@ti.com>
Tested-by: Rajendra Nayak <rnayak@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
This commit is contained in:
Paul Walmsley 2011-02-25 15:49:53 -07:00
parent c6461f5c59
commit 0fd0c21be7
8 changed files with 105 additions and 8 deletions

View File

@ -115,7 +115,8 @@ obj-$(CONFIG_ARCH_OMAP2) += $(clock-common) clock2xxx.o \
clkt2xxx_sys.o \
clkt2xxx_dpllcore.o \
clkt2xxx_virt_prcm_set.o \
clkt2xxx_apll.o clkt2xxx_osc.o
clkt2xxx_apll.o clkt2xxx_osc.o \
clkt2xxx_dpll.o
obj-$(CONFIG_SOC_OMAP2420) += clock2420_data.o
obj-$(CONFIG_SOC_OMAP2430) += clock2430.o clock2430_data.o
obj-$(CONFIG_ARCH_OMAP3) += $(clock-common) clock3xxx.o \

View File

@ -0,0 +1,63 @@
/*
* OMAP2-specific DPLL control functions
*
* Copyright (C) 2011 Nokia Corporation
* Paul Walmsley
*
* 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 <linux/kernel.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <plat/clock.h>
#include "clock.h"
#include "cm2xxx_3xxx.h"
#include "cm-regbits-24xx.h"
/* Private functions */
/**
* _allow_idle - enable DPLL autoidle bits
* @clk: struct clk * of the DPLL to operate on
*
* Enable DPLL automatic idle control. The DPLL will enter low-power
* stop when its downstream clocks are gated. No return value.
* REVISIT: DPLL can optionally enter low-power bypass by writing 0x1
* instead. Add some mechanism to optionally enter this mode.
*/
static void _allow_idle(struct clk *clk)
{
if (!clk || !clk->dpll_data)
return;
omap2xxx_cm_set_dpll_auto_low_power_stop();
}
/**
* _deny_idle - prevent DPLL from automatically idling
* @clk: struct clk * of the DPLL to operate on
*
* Disable DPLL automatic idle control. No return value.
*/
static void _deny_idle(struct clk *clk)
{
if (!clk || !clk->dpll_data)
return;
omap2xxx_cm_set_dpll_disable_autoidle();
}
/* Public data */
const struct clkops clkops_omap2xxx_dpll_ops = {
.allow_idle = _allow_idle,
.deny_idle = _deny_idle,
};

View File

@ -148,6 +148,7 @@ extern void omap2_clk_exit_cpufreq_table(struct cpufreq_frequency_table **table)
#define omap2_clk_exit_cpufreq_table 0
#endif
extern const struct clkops clkops_omap2xxx_dpll_ops;
extern const struct clkops clkops_omap3_noncore_dpll_ops;
extern const struct clkops clkops_omap3_core_dpll_ops;
extern const struct clkops clkops_omap4_dpllmx_ops;

View File

@ -125,7 +125,7 @@ static struct dpll_data dpll_dd = {
*/
static struct clk dpll_ck = {
.name = "dpll_ck",
.ops = &clkops_null,
.ops = &clkops_omap2xxx_dpll_ops,
.parent = &sys_ck, /* Can be func_32k also */
.dpll_data = &dpll_dd,
.clkdm_name = "wkup_clkdm",

View File

@ -125,7 +125,7 @@ static struct dpll_data dpll_dd = {
*/
static struct clk dpll_ck = {
.name = "dpll_ck",
.ops = &clkops_null,
.ops = &clkops_omap2xxx_dpll_ops,
.parent = &sys_ck, /* Can be func_32k also */
.dpll_data = &dpll_dd,
.clkdm_name = "wkup_clkdm",

View File

@ -25,6 +25,10 @@
#include "cm-regbits-24xx.h"
#include "cm-regbits-34xx.h"
/* CM_AUTOIDLE_PLL.AUTO_* bit values */
#define DPLL_AUTOIDLE_DISABLE 0x0
#define OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP 0x3
static const u8 cm_idlest_offs[] = {
CM_IDLEST1, CM_IDLEST2, OMAP2430_CM_IDLEST3
};
@ -125,6 +129,29 @@ void omap3xxx_cm_clkdm_force_wakeup(s16 module, u32 mask)
_write_clktrctrl(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, module, mask);
}
/*
* DPLL autoidle control
*/
static void _omap2xxx_set_dpll_autoidle(u8 m)
{
u32 v;
v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE);
v &= ~OMAP24XX_AUTO_DPLL_MASK;
v |= m << OMAP24XX_AUTO_DPLL_SHIFT;
omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE);
}
void omap2xxx_cm_set_dpll_disable_autoidle(void)
{
_omap2xxx_set_dpll_autoidle(OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP);
}
void omap2xxx_cm_set_dpll_auto_low_power_stop(void)
{
_omap2xxx_set_dpll_autoidle(DPLL_AUTOIDLE_DISABLE);
}
/*
*

View File

@ -122,6 +122,9 @@ extern void omap3xxx_cm_clkdm_disable_hwsup(s16 module, u32 mask);
extern void omap3xxx_cm_clkdm_force_sleep(s16 module, u32 mask);
extern void omap3xxx_cm_clkdm_force_wakeup(s16 module, u32 mask);
extern void omap2xxx_cm_set_dpll_disable_autoidle(void);
extern void omap2xxx_cm_set_dpll_auto_low_power_stop(void);
#endif
/* CM register bits shared between 24XX and 3430 */

View File

@ -378,6 +378,7 @@ static void __init prcm_setup_regs(void)
{
int i, num_mem_banks;
struct powerdomain *pwrdm;
u32 v;
/* Enable autoidle */
omap2_prm_write_mod_reg(OMAP24XX_AUTOIDLE_MASK, OCP_MOD,
@ -468,11 +469,12 @@ static void __init prcm_setup_regs(void)
omap2_cm_write_mod_reg(OMAP2420_AUTO_DSP_IPI_MASK, OMAP24XX_DSP_MOD,
CM_AUTOIDLE);
/* Put DPLL and both APLLs into autoidle mode */
omap2_cm_write_mod_reg((0x03 << OMAP24XX_AUTO_DPLL_SHIFT) |
(0x03 << OMAP24XX_AUTO_96M_SHIFT) |
(0x03 << OMAP24XX_AUTO_54M_SHIFT),
PLL_MOD, CM_AUTOIDLE);
/* Put both APLLs into autoidle mode */
v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE);
v &= ~(OMAP24XX_AUTO_96M_MASK | OMAP24XX_AUTO_54M_SHIFT);
v |= (0x03 << OMAP24XX_AUTO_96M_SHIFT) |
(0x03 << OMAP24XX_AUTO_54M_SHIFT);
omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE);
omap2_cm_write_mod_reg(OMAP24XX_AUTO_OMAPCTRL_MASK |
OMAP24XX_AUTO_WDT1_MASK |