Commit 50fc6897 authored by Tuukka Tikkanen's avatar Tuukka Tikkanen Committed by Tony Lindgren

ARM: OMAP: Update clock framework

Sync with linux-omap tree. Updates clock framework to reset unused clocks
with a late_initcall.
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent bce39473
......@@ -21,6 +21,19 @@ source "arch/arm/mach-omap/omap1/Kconfig"
comment "OMAP Feature Selections"
config OMAP_RESET_CLOCKS
bool "Reset unused clocks during boot"
depends on ARCH_OMAP
default n
help
Say Y if you want to reset unused clocks during boot.
This option saves power, but assumes all drivers are
using the clock framework. Broken drivers that do not
yet use clock framework may not work with this option.
If you are booting from another operating system, you
probably do not want this option enabled until your
device drivers work properly.
config OMAP_MUX
bool "OMAP multiplexing support"
depends on ARCH_OMAP
......
......@@ -25,6 +25,8 @@ static LIST_HEAD(clocks);
static DECLARE_MUTEX(clocks_sem);
static DEFINE_SPINLOCK(clockfw_lock);
static void propagate_rate(struct clk * clk);
/* UART clock function */
static int set_uart_rate(struct clk * clk, unsigned long rate);
/* External clock (MCLK & BCLK) functions */
static int set_ext_clk_rate(struct clk * clk, unsigned long rate);
static long round_ext_clk_rate(struct clk * clk, unsigned long rate);
......@@ -34,7 +36,7 @@ static int select_table_rate(struct clk * clk, unsigned long rate);
static long round_to_table_rate(struct clk * clk, unsigned long rate);
void clk_setdpll(__u16, __u16);
struct mpu_rate rate_table[] = {
static struct mpu_rate rate_table[] = {
/* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL
* armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv
*/
......@@ -58,7 +60,7 @@ struct mpu_rate rate_table[] = {
{ 168000000, 12000000, 168000000, 0x010f, 0x2710 }, /* 1/1/1/2/8/8 */
#endif
#if defined(CONFIG_OMAP_ARM_150MHZ)
{ 150000000, 12000000, 150000000, 0x150a, 0x2cb0 }, /* 0/0/1/1/2/2 */
{ 150000000, 12000000, 150000000, 0x010a, 0x2cb0 }, /* 1/1/1/2/4/4 */
#endif
#if defined(CONFIG_OMAP_ARM_120MHZ)
{ 120000000, 12000000, 120000000, 0x010a, 0x2510 }, /* 1/1/1/2/4/4 */
......@@ -76,19 +78,11 @@ struct mpu_rate rate_table[] = {
};
static void ckctl_recalc(struct clk * clk)
{
int dsor;
/* Calculate divisor encoded as 2-bit exponent */
dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset));
if (unlikely(clk->rate == clk->parent->rate / dsor))
return; /* No change, quick exit */
clk->rate = clk->parent->rate / dsor;
if (unlikely(clk->flags & RATE_PROPAGATES))
propagate_rate(clk);
}
static void ckctl_recalc(struct clk * clk);
int __clk_enable(struct clk *clk);
void __clk_disable(struct clk *clk);
void __clk_unuse(struct clk *clk);
int __clk_use(struct clk *clk);
static void followparent_recalc(struct clk * clk)
......@@ -102,6 +96,14 @@ static void watchdog_recalc(struct clk * clk)
clk->rate = clk->parent->rate / 14;
}
static void uart_recalc(struct clk * clk)
{
unsigned int val = omap_readl(clk->enable_reg);
if (val & clk->enable_bit)
clk->rate = 48000000;
else
clk->rate = 12000000;
}
static struct clk ck_ref = {
.name = "ck_ref",
......@@ -185,7 +187,7 @@ static struct clk armwdt_ck = {
static struct clk arminth_ck16xx = {
.name = "arminth_ck",
.parent = &arm_ck,
.flags = CLOCK_IN_OMAP16XX,
.flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
.recalc = &followparent_recalc,
/* Note: On 16xx the frequency can be divided by 2 by programming
* ARM_CKCTL:ARM_INTHCK_SEL(14) to 1
......@@ -214,6 +216,38 @@ static struct clk dspmmu_ck = {
.recalc = &ckctl_recalc,
};
static struct clk dspper_ck = {
.name = "dspper_ck",
.parent = &ck_dpll1,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
RATE_CKCTL | DSP_DOMAIN_CLOCK,
.enable_reg = DSP_IDLECT2,
.enable_bit = EN_PERCK,
.rate_offset = CKCTL_PERDIV_OFFSET,
.recalc = &followparent_recalc,
//.recalc = &ckctl_recalc,
};
static struct clk dspxor_ck = {
.name = "dspxor_ck",
.parent = &ck_ref,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
DSP_DOMAIN_CLOCK,
.enable_reg = DSP_IDLECT2,
.enable_bit = EN_XORPCK,
.recalc = &followparent_recalc,
};
static struct clk dsptim_ck = {
.name = "dsptim_ck",
.parent = &ck_ref,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
DSP_DOMAIN_CLOCK,
.enable_reg = DSP_IDLECT2,
.enable_bit = EN_DSPTIMCK,
.recalc = &followparent_recalc,
};
static struct clk tc_ck = {
.name = "tc_ck",
.parent = &ck_dpll1,
......@@ -226,7 +260,7 @@ static struct clk tc_ck = {
static struct clk arminth_ck1510 = {
.name = "arminth_ck",
.parent = &tc_ck,
.flags = CLOCK_IN_OMAP1510,
.flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED,
.recalc = &followparent_recalc,
/* Note: On 1510 the frequency follows TC_CK
*
......@@ -237,7 +271,7 @@ static struct clk arminth_ck1510 = {
static struct clk tipb_ck = {
.name = "tibp_ck",
.parent = &tc_ck,
.flags = CLOCK_IN_OMAP1510,
.flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED,
.recalc = &followparent_recalc,
};
......@@ -271,14 +305,15 @@ static struct clk tc2_ck = {
static struct clk dma_ck = {
.name = "dma_ck",
.parent = &tc_ck,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
ALWAYS_ENABLED,
.recalc = &followparent_recalc,
};
static struct clk dma_lcdfree_ck = {
.name = "dma_lcdfree_ck",
.parent = &tc_ck,
.flags = CLOCK_IN_OMAP16XX,
.flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
.recalc = &followparent_recalc,
};
......@@ -303,14 +338,14 @@ static struct clk lb_ck = {
static struct clk rhea1_ck = {
.name = "rhea1_ck",
.parent = &tc_ck,
.flags = CLOCK_IN_OMAP16XX,
.flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
.recalc = &followparent_recalc,
};
static struct clk rhea2_ck = {
.name = "rhea2_ck",
.parent = &tc_ck,
.flags = CLOCK_IN_OMAP16XX,
.flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
.recalc = &followparent_recalc,
};
......@@ -325,43 +360,56 @@ static struct clk lcd_ck = {
.recalc = &ckctl_recalc,
};
static struct clk uart1_ck = {
static struct clk uart1_1510 = {
.name = "uart1_ck",
/* Direct from ULPD, no parent */
.rate = 12000000,
.flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | ALWAYS_ENABLED,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 29, /* Chooses between 12MHz and 48MHz */
.set_rate = &set_uart_rate,
.recalc = &uart_recalc,
};
static struct clk uart1_16xx = {
.name = "uart1_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
RATE_FIXED | ENABLE_REG_32BIT,
.flags = CLOCK_IN_OMAP16XX | RATE_FIXED | ENABLE_REG_32BIT,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 29,
/* (Only on 1510)
* The "enable bit" actually chooses between 48MHz and 12MHz.
*/
};
static struct clk uart2_ck = {
.name = "uart2_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
RATE_FIXED | ENABLE_REG_32BIT,
.rate = 12000000,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ENABLE_REG_32BIT |
ALWAYS_ENABLED,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 30,
/* (for both 1510 and 16xx)
* The "enable bit" actually chooses between 48MHz and 12MHz/32kHz.
*/
.enable_bit = 30, /* Chooses between 12MHz and 48MHz */
.set_rate = &set_uart_rate,
.recalc = &uart_recalc,
};
static struct clk uart3_1510 = {
.name = "uart3_ck",
/* Direct from ULPD, no parent */
.rate = 12000000,
.flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | ALWAYS_ENABLED,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 31, /* Chooses between 12MHz and 48MHz */
.set_rate = &set_uart_rate,
.recalc = &uart_recalc,
};
static struct clk uart3_ck = {
static struct clk uart3_16xx = {
.name = "uart3_ck",
/* Direct from ULPD, no parent */
.rate = 48000000,
.flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
RATE_FIXED | ENABLE_REG_32BIT,
.flags = CLOCK_IN_OMAP16XX | RATE_FIXED | ENABLE_REG_32BIT,
.enable_reg = MOD_CONF_CTRL_0,
.enable_bit = 31,
/* (Only on 1510)
* The "enable bit" actually chooses between 48MHz and 12MHz.
*/
};
static struct clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */
......@@ -480,6 +528,9 @@ static struct clk * onchip_clks[] = {
/* CK_GEN2 clocks */
&dsp_ck,
&dspmmu_ck,
&dspper_ck,
&dspxor_ck,
&dsptim_ck,
/* CK_GEN3 clocks */
&tc_ck,
&tipb_ck,
......@@ -494,9 +545,11 @@ static struct clk * onchip_clks[] = {
&rhea2_ck,
&lcd_ck,
/* ULPD clocks */
&uart1_ck,
&uart1_1510,
&uart1_16xx,
&uart2_ck,
&uart3_ck,
&uart3_1510,
&uart3_16xx,
&usb_clko,
&usb_hhc_ck1510, &usb_hhc_ck16xx,
&mclk_1510, &mclk_16xx,
......@@ -547,6 +600,10 @@ int __clk_enable(struct clk *clk)
return 0;
}
if (clk->flags & DSP_DOMAIN_CLOCK) {
__clk_use(&api_ck);
}
if (clk->flags & ENABLE_REG_32BIT) {
regval32 = omap_readl(clk->enable_reg);
regval32 |= (1 << clk->enable_bit);
......@@ -557,6 +614,10 @@ int __clk_enable(struct clk *clk)
omap_writew(regval16, clk->enable_reg);
}
if (clk->flags & DSP_DOMAIN_CLOCK) {
__clk_unuse(&api_ck);
}
return 0;
}
......@@ -569,6 +630,10 @@ void __clk_disable(struct clk *clk)
if (clk->enable_reg == 0)
return;
if (clk->flags & DSP_DOMAIN_CLOCK) {
__clk_use(&api_ck);
}
if (clk->flags & ENABLE_REG_32BIT) {
regval32 = omap_readl(clk->enable_reg);
regval32 &= ~(1 << clk->enable_bit);
......@@ -578,6 +643,10 @@ void __clk_disable(struct clk *clk)
regval16 &= ~(1 << clk->enable_bit);
omap_writew(regval16, clk->enable_reg);
}
if (clk->flags & DSP_DOMAIN_CLOCK) {
__clk_unuse(&api_ck);
}
}
......@@ -766,6 +835,31 @@ static int calc_dsor_exp(struct clk *clk, unsigned long rate)
return dsor_exp;
}
static void ckctl_recalc(struct clk * clk)
{
int dsor;
/* Calculate divisor encoded as 2-bit exponent */
if (clk->flags & DSP_DOMAIN_CLOCK) {
/* The clock control bits are in DSP domain,
* so api_ck is needed for access
*/
__clk_use(&api_ck);
dsor = 1 << (3 & (omap_readw(DSP_CKCTL) >> clk->rate_offset));
__clk_unuse(&api_ck);
} else {
dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset));
}
if (unlikely(clk->rate == clk->parent->rate / dsor))
return; /* No change, quick exit */
clk->rate = clk->parent->rate / dsor;
if (unlikely(clk->flags & RATE_PROPAGATES))
propagate_rate(clk);
}
long clk_round_rate(struct clk *clk, unsigned long rate)
{
int dsor_exp;
......@@ -826,7 +920,13 @@ static int select_table_rate(struct clk * clk, unsigned long rate)
if (!ptr->rate)
return -EINVAL;
/*
* If ARM_TIM_CK is coming from CK_GEN1, reset it and set
* safe values for dividers in ARM_CKCTL.
* Otherwise system may hang when reprogramming DPLL_CTL.
*/
if (unlikely(ck_dpll1.rate == 0)) {
omap_writew(0x03ff, ARM_CKCTL);
omap_writew(ptr->dpllctl_val, DPLL_CTL);
ck_dpll1.rate = ptr->pll_rate;
}
......@@ -921,6 +1021,23 @@ static unsigned calc_ext_dsor(unsigned long rate)
return dsor;
}
/* Only needed on 1510 */
static int set_uart_rate(struct clk * clk, unsigned long rate)
{
unsigned int val;
val = omap_readl(clk->enable_reg);
if (rate == 12000000)
val &= ~(1 << clk->enable_bit);
else if (rate == 48000000)
val |= (1 << clk->enable_bit);
else
return -EINVAL;
omap_writel(val, clk->enable_reg);
clk->rate = rate;
return 0;
}
static int set_ext_clk_rate(struct clk * clk, unsigned long rate)
{
......@@ -1023,9 +1140,42 @@ int __init clk_init(void)
ck_ref.rate = 19200000;
#endif
printk("Clocks: ARM_SYSST: 0x%04x DPLL_CTL: 0x%04x ARM_CKCTL: 0x%04x\n",
omap_readw(ARM_SYSST), omap_readw(DPLL_CTL),
omap_readw(ARM_CKCTL));
/* We want to be in syncronous scalable mode */
omap_writew(0x1000, ARM_SYSST);
#ifdef CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER
/* Use values set by bootloader. Determine PLL rate and recalculate
* dependent clocks as if kernel had changed PLL or divisors.
*/
{
unsigned pll_ctl_val = omap_readw(DPLL_CTL);
ck_dpll1.rate = ck_ref.rate; /* Base xtal rate */
if (pll_ctl_val & 0x10) {
/* PLL enabled, apply multiplier and divisor */
if (pll_ctl_val & 0xf80)
ck_dpll1.rate *= (pll_ctl_val & 0xf80) >> 7;
ck_dpll1.rate /= ((pll_ctl_val & 0x60) >> 5) + 1;
} else {
/* PLL disabled, apply bypass divisor */
switch (pll_ctl_val & 0xc) {
case 0:
break;
case 0x4:
ck_dpll1.rate /= 2;
break;
default:
ck_dpll1.rate /= 4;
break;
}
}
}
propagate_rate(&ck_dpll1);
#else
/* Find the highest supported frequency and enable it */
if (select_table_rate(&virtual_ck_mpu, ~0)) {
printk(KERN_ERR "System frequencies not set. Check your config.\n");
......@@ -1034,12 +1184,13 @@ int __init clk_init(void)
omap_writew(0x1005, ARM_CKCTL);
ck_dpll1.rate = 60000000;
propagate_rate(&ck_dpll1);
printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): %ld/%ld/%ld\n",
ck_ref.rate, ck_dpll1.rate, arm_ck.rate);
}
#endif
/* Cache rates for clocks connected to ck_ref (not dpll1) */
propagate_rate(&ck_ref);
printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): %ld.%01ld/%ld/%ld MHz\n",
ck_ref.rate / 1000000, (ck_ref.rate / 100000) % 10,
ck_dpll1.rate, arm_ck.rate);
#ifdef CONFIG_MACH_OMAP_PERSEUS2
/* Select slicer output as OMAP input clock */
......@@ -1074,3 +1225,56 @@ int __init clk_init(void)
return 0;
}
#ifdef CONFIG_OMAP_RESET_CLOCKS
static int __init omap_clk_reset(void)
{
/* Turn off all unused clocks */
struct clk *p;
__u32 regval32;
omap_writew(0, SOFT_REQ_REG);
omap_writew(0, SOFT_REQ_REG2);
list_for_each_entry(p, &clocks, node) {
if (p->usecount > 0 || (p->flags & ALWAYS_ENABLED) ||
p->enable_reg == 0)
continue;
/* Assume no DSP clocks have been activated by bootloader */
if (p->flags & DSP_DOMAIN_CLOCK)
continue;
/* Is the clock already disabled? */
if (p->flags & ENABLE_REG_32BIT)
regval32 = omap_readl(p->enable_reg);
else
regval32 = omap_readw(p->enable_reg);
if ((regval32 & (1 << p->enable_bit)) == 0)
continue;
/* FIXME: This clock seems to be necessary but no-one
* has asked for its activation. */
if (p == &tc2_ck // FIX: pm.c (SRAM), CCP, Camera
|| p == &ck_dpll1out // FIX: SoSSI, SSR
|| p == &arm_gpio_ck // FIX: GPIO code for 1510
) {
printk(KERN_INFO "FIXME: Clock \"%s\" seems unused\n",
p->name);
continue;
}
printk(KERN_INFO "Disabling unused clock \"%s\"... ", p->name);
__clk_disable(p);
printk(" done\n");
}
return 0;
}
late_initcall(omap_clk_reset);
#endif
......@@ -52,6 +52,7 @@ struct mpu_rate {
#define CLOCK_IN_OMAP16XX 64
#define CLOCK_IN_OMAP1510 128
#define CLOCK_IN_OMAP730 256
#define DSP_DOMAIN_CLOCK 512
/* ARM_CKCTL bit shifts */
#define CKCTL_PERDIV_OFFSET 0
......@@ -63,6 +64,8 @@ struct mpu_rate {
/*#define ARM_TIMXO 12*/
#define EN_DSPCK 13
/*#define ARM_INTHCK_SEL 14*/ /* Divide-by-2 for mpu inth_ck */
/* DSP_CKCTL bit shifts */
#define CKCTL_DSPPERDIV_OFFSET 0
/* ARM_IDLECT1 bit shifts */
/*#define IDLWDT_ARM 0*/
......@@ -96,6 +99,9 @@ struct mpu_rate {
#define EN_TC1_CK 2
#define EN_TC2_CK 4
/* DSP_IDLECT2 bit shifts (0,1,2 are same as for ARM_IDLECT2) */
#define EN_DSPTIMCK 5
/* Various register defines for clock controls scattered around OMAP chip */
#define USB_MCLK_EN_BIT 4 /* In ULPD_CLKC_CTRL */
#define USB_HOST_HHC_UHOST_EN 9 /* In MOD_CONF_CTRL_0 */
......@@ -103,7 +109,8 @@ struct mpu_rate {
#define COM_ULPD_PLL_CLK_REQ 1 /* In COM_CLK_DIV_CTRL_SEL */
#define SWD_CLK_DIV_CTRL_SEL 0xfffe0874
#define COM_CLK_DIV_CTRL_SEL 0xfffe0878
#define SOFT_REQ_REG 0xfffe0834
#define SOFT_REQ_REG2 0xfffe0880
int clk_register(struct clk *clk);
void clk_unregister(struct clk *clk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment