2006-06-24 10:33:02 +01:00
/*
* arch / arm / mach - ep93xx / clock . c
* Clock control for Cirrus EP93xx chips .
*
* Copyright ( C ) 2006 Lennert Buytenhek < buytenh @ wantstofly . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/clk.h>
# include <linux/err.h>
2007-02-04 22:45:33 +01:00
# include <linux/module.h>
2006-06-24 10:33:02 +01:00
# include <linux/string.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2008-11-30 17:11:49 +00:00
# include <asm/clkdev.h>
2006-06-24 10:33:02 +01:00
# include <asm/div64.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2006-06-24 10:33:02 +01:00
2009-05-07 18:41:47 +01:00
/*
* The EP93xx has two external crystal oscillators . To generate the
* required high - frequency clocks , the processor uses two phase - locked -
* loops ( PLLs ) to multiply the incoming external clock signal to much
* higher frequencies that are then divided down by programmable dividers
* to produce the needed clocks . The PLLs operate independently of one
* another .
*/
# define EP93XX_EXT_CLK_RATE 14745600
# define EP93XX_EXT_RTC_RATE 32768
2006-06-24 10:33:02 +01:00
struct clk {
unsigned long rate ;
int users ;
2009-05-07 18:41:47 +01:00
int sw_locked ;
2006-06-24 10:33:02 +01:00
u32 enable_reg ;
u32 enable_mask ;
2009-05-07 18:41:47 +01:00
unsigned long ( * get_rate ) ( struct clk * clk ) ;
2006-06-24 10:33:02 +01:00
} ;
2009-05-07 18:41:47 +01:00
static unsigned long get_uart_rate ( struct clk * clk ) ;
static struct clk clk_uart1 = {
. sw_locked = 1 ,
2009-07-08 02:00:49 +01:00
. enable_reg = EP93XX_SYSCON_DEVCFG ,
. enable_mask = EP93XX_SYSCON_DEVCFG_U1EN ,
2009-05-07 18:41:47 +01:00
. get_rate = get_uart_rate ,
} ;
static struct clk clk_uart2 = {
. sw_locked = 1 ,
2009-07-08 02:00:49 +01:00
. enable_reg = EP93XX_SYSCON_DEVCFG ,
. enable_mask = EP93XX_SYSCON_DEVCFG_U2EN ,
2009-05-07 18:41:47 +01:00
. get_rate = get_uart_rate ,
} ;
static struct clk clk_uart3 = {
. sw_locked = 1 ,
2009-07-08 02:00:49 +01:00
. enable_reg = EP93XX_SYSCON_DEVCFG ,
. enable_mask = EP93XX_SYSCON_DEVCFG_U3EN ,
2009-05-07 18:41:47 +01:00
. get_rate = get_uart_rate ,
2007-04-22 12:30:41 +01:00
} ;
2008-11-30 17:11:49 +00:00
static struct clk clk_pll1 ;
static struct clk clk_f ;
static struct clk clk_h ;
static struct clk clk_p ;
static struct clk clk_pll2 ;
2006-06-24 10:33:02 +01:00
static struct clk clk_usb_host = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_USH_EN ,
2006-06-24 10:33:02 +01:00
} ;
2009-02-25 22:22:38 +01:00
/* DMA Clocks */
static struct clk clk_m2p0 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P0 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p1 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P1 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p2 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P2 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p3 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P3 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p4 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P4 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p5 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P5 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p6 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P6 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p7 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P7 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p8 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P8 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2p9 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P9 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2m0 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M0 ,
2009-02-25 22:22:38 +01:00
} ;
static struct clk clk_m2m1 = {
2009-05-28 20:07:03 +01:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M1 ,
2009-02-25 22:22:38 +01:00
} ;
2008-11-30 17:11:49 +00:00
# define INIT_CK(dev,con,ck) \
{ . dev_id = dev , . con_id = con , . clk = ck }
static struct clk_lookup clocks [ ] = {
2009-05-07 18:41:47 +01:00
INIT_CK ( " apb:uart1 " , NULL , & clk_uart1 ) ,
INIT_CK ( " apb:uart2 " , NULL , & clk_uart2 ) ,
INIT_CK ( " apb:uart3 " , NULL , & clk_uart3 ) ,
2008-11-30 17:11:49 +00:00
INIT_CK ( NULL , " pll1 " , & clk_pll1 ) ,
INIT_CK ( NULL , " fclk " , & clk_f ) ,
INIT_CK ( NULL , " hclk " , & clk_h ) ,
INIT_CK ( NULL , " pclk " , & clk_p ) ,
INIT_CK ( NULL , " pll2 " , & clk_pll2 ) ,
2009-05-28 19:56:11 +01:00
INIT_CK ( " ep93xx-ohci " , NULL , & clk_usb_host ) ,
2009-02-25 22:22:38 +01:00
INIT_CK ( NULL , " m2p0 " , & clk_m2p0 ) ,
INIT_CK ( NULL , " m2p1 " , & clk_m2p1 ) ,
INIT_CK ( NULL , " m2p2 " , & clk_m2p2 ) ,
INIT_CK ( NULL , " m2p3 " , & clk_m2p3 ) ,
INIT_CK ( NULL , " m2p4 " , & clk_m2p4 ) ,
INIT_CK ( NULL , " m2p5 " , & clk_m2p5 ) ,
INIT_CK ( NULL , " m2p6 " , & clk_m2p6 ) ,
INIT_CK ( NULL , " m2p7 " , & clk_m2p7 ) ,
INIT_CK ( NULL , " m2p8 " , & clk_m2p8 ) ,
INIT_CK ( NULL , " m2p9 " , & clk_m2p9 ) ,
INIT_CK ( NULL , " m2m0 " , & clk_m2m0 ) ,
INIT_CK ( NULL , " m2m1 " , & clk_m2m1 ) ,
2006-06-24 10:33:02 +01:00
} ;
int clk_enable ( struct clk * clk )
{
if ( ! clk - > users + + & & clk - > enable_reg ) {
u32 value ;
value = __raw_readl ( clk - > enable_reg ) ;
2009-07-08 02:00:49 +01:00
value | = clk - > enable_mask ;
2009-05-07 18:41:47 +01:00
if ( clk - > sw_locked )
2009-07-08 02:00:49 +01:00
ep93xx_syscon_swlocked_write ( value , clk - > enable_reg ) ;
else
__raw_writel ( value , clk - > enable_reg ) ;
2006-06-24 10:33:02 +01:00
}
return 0 ;
}
2008-07-10 14:44:23 +01:00
EXPORT_SYMBOL ( clk_enable ) ;
2006-06-24 10:33:02 +01:00
void clk_disable ( struct clk * clk )
{
if ( ! - - clk - > users & & clk - > enable_reg ) {
u32 value ;
value = __raw_readl ( clk - > enable_reg ) ;
2009-07-08 02:00:49 +01:00
value & = ~ clk - > enable_mask ;
2009-05-07 18:41:47 +01:00
if ( clk - > sw_locked )
2009-07-08 02:00:49 +01:00
ep93xx_syscon_swlocked_write ( value , clk - > enable_reg ) ;
else
__raw_writel ( value , clk - > enable_reg ) ;
2006-06-24 10:33:02 +01:00
}
}
2008-07-10 14:44:23 +01:00
EXPORT_SYMBOL ( clk_disable ) ;
2006-06-24 10:33:02 +01:00
2009-05-07 18:41:47 +01:00
static unsigned long get_uart_rate ( struct clk * clk )
{
u32 value ;
2009-06-11 19:57:34 +01:00
value = __raw_readl ( EP93XX_SYSCON_PWRCNT ) ;
if ( value & EP93XX_SYSCON_PWRCNT_UARTBAUD )
2009-05-07 18:41:47 +01:00
return EP93XX_EXT_CLK_RATE ;
else
return EP93XX_EXT_CLK_RATE / 2 ;
}
2006-06-24 10:33:02 +01:00
unsigned long clk_get_rate ( struct clk * clk )
{
2009-05-07 18:41:47 +01:00
if ( clk - > get_rate )
return clk - > get_rate ( clk ) ;
2006-06-24 10:33:02 +01:00
return clk - > rate ;
}
2008-07-10 14:44:23 +01:00
EXPORT_SYMBOL ( clk_get_rate ) ;
2006-06-24 10:33:02 +01:00
static char fclk_divisors [ ] = { 1 , 2 , 4 , 8 , 16 , 1 , 1 , 1 } ;
static char hclk_divisors [ ] = { 1 , 2 , 4 , 5 , 6 , 8 , 16 , 32 } ;
static char pclk_divisors [ ] = { 1 , 2 , 4 , 8 } ;
/*
* PLL rate = 14.7456 MHz * ( X1FBD + 1 ) * ( X2FBD + 1 ) / ( X2IPD + 1 ) / 2 ^ PS
*/
static unsigned long calc_pll_rate ( u32 config_word )
{
unsigned long long rate ;
int i ;
2009-05-07 18:41:47 +01:00
rate = EP93XX_EXT_CLK_RATE ;
2006-06-24 10:33:02 +01:00
rate * = ( ( config_word > > 11 ) & 0x1f ) + 1 ; /* X1FBD */
rate * = ( ( config_word > > 5 ) & 0x3f ) + 1 ; /* X2FBD */
do_div ( rate , ( config_word & 0x1f ) + 1 ) ; /* X2IPD */
for ( i = 0 ; i < ( ( config_word > > 16 ) & 3 ) ; i + + ) /* PS */
rate > > = 1 ;
return ( unsigned long ) rate ;
}
2009-02-25 22:22:38 +01:00
static void __init ep93xx_dma_clock_init ( void )
{
clk_m2p0 . rate = clk_h . rate ;
clk_m2p1 . rate = clk_h . rate ;
clk_m2p2 . rate = clk_h . rate ;
clk_m2p3 . rate = clk_h . rate ;
clk_m2p4 . rate = clk_h . rate ;
clk_m2p5 . rate = clk_h . rate ;
clk_m2p6 . rate = clk_h . rate ;
clk_m2p7 . rate = clk_h . rate ;
clk_m2p8 . rate = clk_h . rate ;
clk_m2p9 . rate = clk_h . rate ;
clk_m2m0 . rate = clk_h . rate ;
clk_m2m1 . rate = clk_h . rate ;
}
2007-02-04 22:45:33 +01:00
static int __init ep93xx_clock_init ( void )
2006-06-24 10:33:02 +01:00
{
u32 value ;
2008-11-30 17:11:49 +00:00
int i ;
2006-06-24 10:33:02 +01:00
value = __raw_readl ( EP93XX_SYSCON_CLOCK_SET1 ) ;
if ( ! ( value & 0x00800000 ) ) { /* PLL1 bypassed? */
2009-05-07 18:41:47 +01:00
clk_pll1 . rate = EP93XX_EXT_CLK_RATE ;
2006-06-24 10:33:02 +01:00
} else {
clk_pll1 . rate = calc_pll_rate ( value ) ;
}
clk_f . rate = clk_pll1 . rate / fclk_divisors [ ( value > > 25 ) & 0x7 ] ;
clk_h . rate = clk_pll1 . rate / hclk_divisors [ ( value > > 20 ) & 0x7 ] ;
clk_p . rate = clk_h . rate / pclk_divisors [ ( value > > 18 ) & 0x3 ] ;
2009-02-25 22:22:38 +01:00
ep93xx_dma_clock_init ( ) ;
2006-06-24 10:33:02 +01:00
value = __raw_readl ( EP93XX_SYSCON_CLOCK_SET2 ) ;
if ( ! ( value & 0x00080000 ) ) { /* PLL2 bypassed? */
2009-05-07 18:41:47 +01:00
clk_pll2 . rate = EP93XX_EXT_CLK_RATE ;
2006-06-24 10:33:02 +01:00
} else if ( value & 0x00040000 ) { /* PLL2 enabled? */
clk_pll2 . rate = calc_pll_rate ( value ) ;
} else {
clk_pll2 . rate = 0 ;
}
clk_usb_host . rate = clk_pll2 . rate / ( ( ( value > > 28 ) & 0xf ) + 1 ) ;
printk ( KERN_INFO " ep93xx: PLL1 running at %ld MHz, PLL2 at %ld MHz \n " ,
clk_pll1 . rate / 1000000 , clk_pll2 . rate / 1000000 ) ;
printk ( KERN_INFO " ep93xx: FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz \n " ,
clk_f . rate / 1000000 , clk_h . rate / 1000000 ,
clk_p . rate / 1000000 ) ;
2007-02-04 22:45:33 +01:00
2008-11-30 17:11:49 +00:00
for ( i = 0 ; i < ARRAY_SIZE ( clocks ) ; i + + )
clkdev_add ( & clocks [ i ] ) ;
2007-02-04 22:45:33 +01:00
return 0 ;
2006-06-24 10:33:02 +01:00
}
2007-02-04 22:45:33 +01:00
arch_initcall ( ep93xx_clock_init ) ;