2006-06-24 13:33:02 +04: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 .
*/
2010-01-11 20:30:41 +03:00
# define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt
2006-06-24 13:33:02 +04:00
# include <linux/kernel.h>
# include <linux/clk.h>
# include <linux/err.h>
2007-02-05 00:45:33 +03:00
# include <linux/module.h>
2006-06-24 13:33:02 +04:00
# include <linux/string.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2009-10-09 02:44:41 +04:00
# include <linux/spinlock.h>
2010-11-17 12:04:33 +03:00
# include <linux/clkdev.h>
2009-10-09 02:44:41 +04:00
# include <mach/hardware.h>
2008-11-30 20:11:49 +03:00
2006-06-24 13:33:02 +04:00
# include <asm/div64.h>
2009-05-07 21:41:47 +04:00
2006-06-24 13:33:02 +04:00
struct clk {
2009-10-09 02:44:41 +04:00
struct clk * parent ;
2006-06-24 13:33:02 +04:00
unsigned long rate ;
int users ;
2009-05-07 21:41:47 +04:00
int sw_locked ;
2009-07-06 20:40:53 +04:00
void __iomem * enable_reg ;
2006-06-24 13:33:02 +04:00
u32 enable_mask ;
2009-05-07 21:41:47 +04:00
unsigned long ( * get_rate ) ( struct clk * clk ) ;
2009-07-01 02:06:43 +04:00
int ( * set_rate ) ( struct clk * clk , unsigned long rate ) ;
2006-06-24 13:33:02 +04:00
} ;
2009-05-07 21:41:47 +04:00
static unsigned long get_uart_rate ( struct clk * clk ) ;
2009-07-01 02:06:43 +04:00
static int set_keytchclk_rate ( struct clk * clk , unsigned long rate ) ;
2009-09-23 03:47:09 +04:00
static int set_div_rate ( struct clk * clk , unsigned long rate ) ;
2010-06-08 14:01:10 +04:00
static int set_i2s_sclk_rate ( struct clk * clk , unsigned long rate ) ;
static int set_i2s_lrclk_rate ( struct clk * clk , unsigned long rate ) ;
2009-10-09 02:44:41 +04:00
static struct clk clk_xtali = {
. rate = EP93XX_EXT_CLK_RATE ,
} ;
2009-05-07 21:41:47 +04:00
static struct clk clk_uart1 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_xtali ,
2009-05-07 21:41:47 +04:00
. sw_locked = 1 ,
2009-07-08 05:00:49 +04:00
. enable_reg = EP93XX_SYSCON_DEVCFG ,
. enable_mask = EP93XX_SYSCON_DEVCFG_U1EN ,
2009-05-07 21:41:47 +04:00
. get_rate = get_uart_rate ,
} ;
static struct clk clk_uart2 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_xtali ,
2009-05-07 21:41:47 +04:00
. sw_locked = 1 ,
2009-07-08 05:00:49 +04:00
. enable_reg = EP93XX_SYSCON_DEVCFG ,
. enable_mask = EP93XX_SYSCON_DEVCFG_U2EN ,
2009-05-07 21:41:47 +04:00
. get_rate = get_uart_rate ,
} ;
static struct clk clk_uart3 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_xtali ,
2009-05-07 21:41:47 +04:00
. sw_locked = 1 ,
2009-07-08 05:00:49 +04:00
. enable_reg = EP93XX_SYSCON_DEVCFG ,
. enable_mask = EP93XX_SYSCON_DEVCFG_U3EN ,
2009-05-07 21:41:47 +04:00
. get_rate = get_uart_rate ,
2007-04-22 15:30:41 +04:00
} ;
2009-10-09 02:44:41 +04:00
static struct clk clk_pll1 = {
. parent = & clk_xtali ,
} ;
static struct clk clk_f = {
. parent = & clk_pll1 ,
} ;
static struct clk clk_h = {
. parent = & clk_pll1 ,
} ;
static struct clk clk_p = {
. parent = & clk_pll1 ,
} ;
static struct clk clk_pll2 = {
. parent = & clk_xtali ,
} ;
2006-06-24 13:33:02 +04:00
static struct clk clk_usb_host = {
2009-10-09 02:44:41 +04:00
. parent = & clk_pll2 ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_USH_EN ,
2006-06-24 13:33:02 +04:00
} ;
2009-07-01 02:06:43 +04:00
static struct clk clk_keypad = {
2009-10-09 02:44:41 +04:00
. parent = & clk_xtali ,
2009-07-01 02:06:43 +04:00
. sw_locked = 1 ,
. enable_reg = EP93XX_SYSCON_KEYTCHCLKDIV ,
. enable_mask = EP93XX_SYSCON_KEYTCHCLKDIV_KEN ,
. set_rate = set_keytchclk_rate ,
} ;
2010-05-11 18:34:54 +04:00
static struct clk clk_spi = {
. parent = & clk_xtali ,
. rate = EP93XX_EXT_CLK_RATE ,
} ;
2009-07-30 01:41:06 +04:00
static struct clk clk_pwm = {
2009-10-09 02:44:41 +04:00
. parent = & clk_xtali ,
2009-07-30 01:41:06 +04:00
. rate = EP93XX_EXT_CLK_RATE ,
} ;
2006-06-24 13:33:02 +04:00
2009-09-23 03:47:09 +04:00
static struct clk clk_video = {
. sw_locked = 1 ,
. enable_reg = EP93XX_SYSCON_VIDCLKDIV ,
. enable_mask = EP93XX_SYSCON_CLKDIV_ENABLE ,
. set_rate = set_div_rate ,
} ;
2010-06-08 14:01:10 +04:00
static struct clk clk_i2s_mclk = {
. sw_locked = 1 ,
. enable_reg = EP93XX_SYSCON_I2SCLKDIV ,
. enable_mask = EP93XX_SYSCON_CLKDIV_ENABLE ,
. set_rate = set_div_rate ,
} ;
static struct clk clk_i2s_sclk = {
. sw_locked = 1 ,
. parent = & clk_i2s_mclk ,
. enable_reg = EP93XX_SYSCON_I2SCLKDIV ,
. enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA ,
. set_rate = set_i2s_sclk_rate ,
} ;
static struct clk clk_i2s_lrclk = {
. sw_locked = 1 ,
. parent = & clk_i2s_sclk ,
. enable_reg = EP93XX_SYSCON_I2SCLKDIV ,
. enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA ,
. set_rate = set_i2s_lrclk_rate ,
} ;
2009-02-26 00:22:38 +03:00
/* DMA Clocks */
static struct clk clk_m2p0 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P0 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p1 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P1 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p2 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P2 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p3 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P3 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p4 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P4 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p5 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P5 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p6 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P6 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p7 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P7 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p8 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P8 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2p9 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P9 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2m0 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M0 ,
2009-02-26 00:22:38 +03:00
} ;
static struct clk clk_m2m1 = {
2009-10-09 02:44:41 +04:00
. parent = & clk_h ,
2009-05-28 23:07:03 +04:00
. enable_reg = EP93XX_SYSCON_PWRCNT ,
. enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M1 ,
2009-02-26 00:22:38 +03:00
} ;
2008-11-30 20:11:49 +03:00
# define INIT_CK(dev,con,ck) \
{ . dev_id = dev , . con_id = con , . clk = ck }
static struct clk_lookup clocks [ ] = {
2009-10-09 02:44:41 +04:00
INIT_CK ( NULL , " xtali " , & clk_xtali ) ,
2009-07-01 02:06:43 +04:00
INIT_CK ( " apb:uart1 " , NULL , & clk_uart1 ) ,
INIT_CK ( " apb:uart2 " , NULL , & clk_uart2 ) ,
INIT_CK ( " apb:uart3 " , NULL , & clk_uart3 ) ,
INIT_CK ( NULL , " pll1 " , & clk_pll1 ) ,
INIT_CK ( NULL , " fclk " , & clk_f ) ,
INIT_CK ( NULL , " hclk " , & clk_h ) ,
2010-07-15 14:01:17 +04:00
INIT_CK ( NULL , " apb_pclk " , & clk_p ) ,
2009-07-01 02:06:43 +04:00
INIT_CK ( NULL , " pll2 " , & clk_pll2 ) ,
INIT_CK ( " ep93xx-ohci " , NULL , & clk_usb_host ) ,
INIT_CK ( " ep93xx-keypad " , NULL , & clk_keypad ) ,
2009-09-23 03:47:09 +04:00
INIT_CK ( " ep93xx-fb " , NULL , & clk_video ) ,
2010-05-11 18:34:54 +04:00
INIT_CK ( " ep93xx-spi.0 " , NULL , & clk_spi ) ,
2010-06-08 14:01:10 +04:00
INIT_CK ( " ep93xx-i2s " , " mclk " , & clk_i2s_mclk ) ,
INIT_CK ( " ep93xx-i2s " , " sclk " , & clk_i2s_sclk ) ,
INIT_CK ( " ep93xx-i2s " , " lrclk " , & clk_i2s_lrclk ) ,
2009-07-30 01:41:06 +04:00
INIT_CK ( NULL , " pwm_clk " , & clk_pwm ) ,
2009-07-01 02:06:43 +04: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 13:33:02 +04:00
} ;
2009-10-09 02:44:41 +04:00
static DEFINE_SPINLOCK ( clk_lock ) ;
static void __clk_enable ( struct clk * clk )
{
if ( ! clk - > users + + ) {
if ( clk - > parent )
__clk_enable ( clk - > parent ) ;
if ( clk - > enable_reg ) {
u32 v ;
v = __raw_readl ( clk - > enable_reg ) ;
v | = clk - > enable_mask ;
if ( clk - > sw_locked )
ep93xx_syscon_swlocked_write ( v , clk - > enable_reg ) ;
else
__raw_writel ( v , clk - > enable_reg ) ;
}
}
}
2006-06-24 13:33:02 +04:00
int clk_enable ( struct clk * clk )
{
2009-10-09 02:44:41 +04:00
unsigned long flags ;
2006-06-24 13:33:02 +04:00
2009-10-09 02:44:41 +04:00
if ( ! clk )
return - EINVAL ;
spin_lock_irqsave ( & clk_lock , flags ) ;
__clk_enable ( clk ) ;
spin_unlock_irqrestore ( & clk_lock , flags ) ;
2006-06-24 13:33:02 +04:00
return 0 ;
}
2008-07-10 17:44:23 +04:00
EXPORT_SYMBOL ( clk_enable ) ;
2006-06-24 13:33:02 +04:00
2009-10-09 02:44:41 +04:00
static void __clk_disable ( struct clk * clk )
2006-06-24 13:33:02 +04:00
{
2009-10-09 02:44:41 +04:00
if ( ! - - clk - > users ) {
if ( clk - > enable_reg ) {
u32 v ;
v = __raw_readl ( clk - > enable_reg ) ;
v & = ~ clk - > enable_mask ;
if ( clk - > sw_locked )
ep93xx_syscon_swlocked_write ( v , clk - > enable_reg ) ;
else
__raw_writel ( v , clk - > enable_reg ) ;
}
2006-06-24 13:33:02 +04:00
2009-10-09 02:44:41 +04:00
if ( clk - > parent )
__clk_disable ( clk - > parent ) ;
2006-06-24 13:33:02 +04:00
}
}
2009-10-09 02:44:41 +04:00
void clk_disable ( struct clk * clk )
{
unsigned long flags ;
if ( ! clk )
return ;
spin_lock_irqsave ( & clk_lock , flags ) ;
__clk_disable ( clk ) ;
spin_unlock_irqrestore ( & clk_lock , flags ) ;
}
2008-07-10 17:44:23 +04:00
EXPORT_SYMBOL ( clk_disable ) ;
2006-06-24 13:33:02 +04:00
2009-05-07 21:41:47 +04:00
static unsigned long get_uart_rate ( struct clk * clk )
{
2009-10-09 02:44:41 +04:00
unsigned long rate = clk_get_rate ( clk - > parent ) ;
2009-05-07 21:41:47 +04:00
u32 value ;
2009-06-11 22:57:34 +04:00
value = __raw_readl ( EP93XX_SYSCON_PWRCNT ) ;
if ( value & EP93XX_SYSCON_PWRCNT_UARTBAUD )
2009-10-09 02:44:41 +04:00
return rate ;
2009-05-07 21:41:47 +04:00
else
2009-10-09 02:44:41 +04:00
return rate / 2 ;
2009-05-07 21:41:47 +04:00
}
2006-06-24 13:33:02 +04:00
unsigned long clk_get_rate ( struct clk * clk )
{
2009-05-07 21:41:47 +04:00
if ( clk - > get_rate )
return clk - > get_rate ( clk ) ;
2006-06-24 13:33:02 +04:00
return clk - > rate ;
}
2008-07-10 17:44:23 +04:00
EXPORT_SYMBOL ( clk_get_rate ) ;
2006-06-24 13:33:02 +04:00
2009-07-01 02:06:43 +04:00
static int set_keytchclk_rate ( struct clk * clk , unsigned long rate )
{
u32 val ;
u32 div_bit ;
val = __raw_readl ( clk - > enable_reg ) ;
/*
* The Key Matrix and ADC clocks are configured using the same
* System Controller register . The clock used will be either
* 1 / 4 or 1 / 16 the external clock rate depending on the
* EP93XX_SYSCON_KEYTCHCLKDIV_KDIV / EP93XX_SYSCON_KEYTCHCLKDIV_ADIV
* bit being set or cleared .
*/
div_bit = clk - > enable_mask > > 15 ;
if ( rate = = EP93XX_KEYTCHCLK_DIV4 )
val | = div_bit ;
else if ( rate = = EP93XX_KEYTCHCLK_DIV16 )
val & = ~ div_bit ;
else
return - EINVAL ;
ep93xx_syscon_swlocked_write ( val , clk - > enable_reg ) ;
clk - > rate = rate ;
return 0 ;
}
2009-10-09 02:44:41 +04:00
static int calc_clk_div ( struct clk * clk , unsigned long rate ,
int * psel , int * esel , int * pdiv , int * div )
2009-09-23 03:47:09 +04:00
{
2009-10-09 02:44:41 +04:00
struct clk * mclk ;
unsigned long max_rate , actual_rate , mclk_rate , rate_err = - 1 ;
2009-09-23 03:47:09 +04:00
int i , found = 0 , __div = 0 , __pdiv = 0 ;
/* Don't exceed the maximum rate */
2010-10-27 01:22:23 +04:00
max_rate = max3 ( clk_pll1 . rate / 4 , clk_pll2 . rate / 4 , clk_xtali . rate / 4 ) ;
2009-09-23 03:47:09 +04:00
rate = min ( rate , max_rate ) ;
/*
* Try the two pll ' s and the external clock
* Because the valid predividers are 2 , 2.5 and 3 , we multiply
* all the clocks by 2 to avoid floating point math .
*
* This is based on the algorithm in the ep93xx raster guide :
* http : //be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf
*
*/
for ( i = 0 ; i < 3 ; i + + ) {
if ( i = = 0 )
2009-10-09 02:44:41 +04:00
mclk = & clk_xtali ;
2009-09-23 03:47:09 +04:00
else if ( i = = 1 )
2009-10-09 02:44:41 +04:00
mclk = & clk_pll1 ;
else
mclk = & clk_pll2 ;
mclk_rate = mclk - > rate * 2 ;
2009-09-23 03:47:09 +04:00
/* Try each predivider value */
for ( __pdiv = 4 ; __pdiv < = 6 ; __pdiv + + ) {
__div = mclk_rate / ( rate * __pdiv ) ;
if ( __div < 2 | | __div > 127 )
continue ;
actual_rate = mclk_rate / ( __pdiv * __div ) ;
if ( ! found | | abs ( actual_rate - rate ) < rate_err ) {
* pdiv = __pdiv - 3 ;
* div = __div ;
* psel = ( i = = 2 ) ;
* esel = ( i ! = 0 ) ;
2009-10-09 02:44:41 +04:00
clk - > parent = mclk ;
clk - > rate = actual_rate ;
2009-09-23 03:47:09 +04:00
rate_err = abs ( actual_rate - rate ) ;
found = 1 ;
}
}
}
if ( ! found )
2009-10-09 02:44:41 +04:00
return - EINVAL ;
2009-09-23 03:47:09 +04:00
2009-10-09 02:44:41 +04:00
return 0 ;
2009-09-23 03:47:09 +04:00
}
static int set_div_rate ( struct clk * clk , unsigned long rate )
{
2009-10-09 02:44:41 +04:00
int err , psel = 0 , esel = 0 , pdiv = 0 , div = 0 ;
2009-09-23 03:47:09 +04:00
u32 val ;
2009-10-09 02:44:41 +04:00
err = calc_clk_div ( clk , rate , & psel , & esel , & pdiv , & div ) ;
if ( err )
return err ;
2009-09-23 03:47:09 +04:00
/* Clear the esel, psel, pdiv and div bits */
val = __raw_readl ( clk - > enable_reg ) ;
val & = ~ 0x7fff ;
/* Set the new esel, psel, pdiv and div bits for the new clock rate */
val | = ( esel ? EP93XX_SYSCON_CLKDIV_ESEL : 0 ) |
( psel ? EP93XX_SYSCON_CLKDIV_PSEL : 0 ) |
( pdiv < < EP93XX_SYSCON_CLKDIV_PDIV_SHIFT ) | div ;
ep93xx_syscon_swlocked_write ( val , clk - > enable_reg ) ;
return 0 ;
}
2010-06-08 14:01:10 +04:00
static int set_i2s_sclk_rate ( struct clk * clk , unsigned long rate )
{
unsigned val = __raw_readl ( clk - > enable_reg ) ;
if ( rate = = clk_i2s_mclk . rate / 2 )
ep93xx_syscon_swlocked_write ( val & ~ EP93XX_I2SCLKDIV_SDIV ,
clk - > enable_reg ) ;
else if ( rate = = clk_i2s_mclk . rate / 4 )
ep93xx_syscon_swlocked_write ( val | EP93XX_I2SCLKDIV_SDIV ,
clk - > enable_reg ) ;
else
return - EINVAL ;
clk_i2s_sclk . rate = rate ;
return 0 ;
}
static int set_i2s_lrclk_rate ( struct clk * clk , unsigned long rate )
{
unsigned val = __raw_readl ( clk - > enable_reg ) &
~ EP93XX_I2SCLKDIV_LRDIV_MASK ;
if ( rate = = clk_i2s_sclk . rate / 32 )
ep93xx_syscon_swlocked_write ( val | EP93XX_I2SCLKDIV_LRDIV32 ,
clk - > enable_reg ) ;
else if ( rate = = clk_i2s_sclk . rate / 64 )
ep93xx_syscon_swlocked_write ( val | EP93XX_I2SCLKDIV_LRDIV64 ,
clk - > enable_reg ) ;
else if ( rate = = clk_i2s_sclk . rate / 128 )
ep93xx_syscon_swlocked_write ( val | EP93XX_I2SCLKDIV_LRDIV128 ,
clk - > enable_reg ) ;
else
return - EINVAL ;
clk_i2s_lrclk . rate = rate ;
return 0 ;
}
2009-07-01 02:06:43 +04:00
int clk_set_rate ( struct clk * clk , unsigned long rate )
{
if ( clk - > set_rate )
return clk - > set_rate ( clk , rate ) ;
return - EINVAL ;
}
EXPORT_SYMBOL ( clk_set_rate ) ;
2006-06-24 13:33:02 +04: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-10-09 02:44:41 +04:00
rate = clk_xtali . rate ;
2006-06-24 13:33:02 +04: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-26 00:22:38 +03: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-05 00:45:33 +03:00
static int __init ep93xx_clock_init ( void )
2006-06-24 13:33:02 +04:00
{
u32 value ;
2010-01-11 23:41:29 +03:00
/* Determine the bootloader configured pll1 rate */
value = __raw_readl ( EP93XX_SYSCON_CLKSET1 ) ;
if ( ! ( value & EP93XX_SYSCON_CLKSET1_NBYP1 ) )
2009-10-09 02:44:41 +04:00
clk_pll1 . rate = clk_xtali . rate ;
2010-01-11 23:41:29 +03:00
else
2006-06-24 13:33:02 +04:00
clk_pll1 . rate = calc_pll_rate ( value ) ;
2010-01-11 23:41:29 +03:00
/* Initialize the pll1 derived clocks */
2006-06-24 13:33:02 +04:00
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-26 00:22:38 +03:00
ep93xx_dma_clock_init ( ) ;
2006-06-24 13:33:02 +04:00
2010-01-11 23:41:29 +03:00
/* Determine the bootloader configured pll2 rate */
2010-02-23 23:20:31 +03:00
value = __raw_readl ( EP93XX_SYSCON_CLKSET2 ) ;
2010-01-11 23:41:29 +03:00
if ( ! ( value & EP93XX_SYSCON_CLKSET2_NBYP2 ) )
2009-10-09 02:44:41 +04:00
clk_pll2 . rate = clk_xtali . rate ;
2010-01-11 23:41:29 +03:00
else if ( value & EP93XX_SYSCON_CLKSET2_PLL2_EN )
2006-06-24 13:33:02 +04:00
clk_pll2 . rate = calc_pll_rate ( value ) ;
2010-01-11 23:41:29 +03:00
else
2006-06-24 13:33:02 +04:00
clk_pll2 . rate = 0 ;
2010-01-11 23:41:29 +03:00
/* Initialize the pll2 derived clocks */
2006-06-24 13:33:02 +04:00
clk_usb_host . rate = clk_pll2 . rate / ( ( ( value > > 28 ) & 0xf ) + 1 ) ;
2010-05-11 18:34:54 +04:00
/*
* EP93xx SSP clock rate was doubled in version E2 . For more information
* see :
* http : //www.cirrus.com/en/pubs/appNote/AN273REV4.pdf
*/
if ( ep93xx_chip_revision ( ) < EP93XX_CHIP_REV_E2 )
clk_spi . rate / = 2 ;
2010-01-11 20:30:41 +03:00
pr_info ( " PLL1 running at %ld MHz, PLL2 at %ld MHz \n " ,
2006-06-24 13:33:02 +04:00
clk_pll1 . rate / 1000000 , clk_pll2 . rate / 1000000 ) ;
2010-01-11 20:30:41 +03:00
pr_info ( " FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz \n " ,
2006-06-24 13:33:02 +04:00
clk_f . rate / 1000000 , clk_h . rate / 1000000 ,
clk_p . rate / 1000000 ) ;
2007-02-05 00:45:33 +03:00
2010-01-12 15:28:00 +03:00
clkdev_add_table ( clocks , ARRAY_SIZE ( clocks ) ) ;
2007-02-05 00:45:33 +03:00
return 0 ;
2006-06-24 13:33:02 +04:00
}
2010-09-03 20:14:54 +04:00
postcore_initcall ( ep93xx_clock_init ) ;