2006-05-16 14:54:37 +04:00
/*
* arch / arm / mach - pnx4008 / clock . c
*
* Clock control driver for PNX4008
*
* Authors : Vitaly Wool , Dmitry Chigirev < source @ mvista . com >
* Generic clock management functions are partially based on :
* linux / arch / arm / mach - omap / clock . c
*
* 2005 - 2006 ( c ) MontaVista Software , Inc . This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed " as is " without any warranty of any kind , whether express
* or implied .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/delay.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2006-05-16 14:54:37 +04:00
2009-11-20 13:32:41 +03:00
# include <asm/clkdev.h>
2006-05-16 14:54:37 +04:00
2009-11-20 13:32:41 +03:00
# include <mach/hardware.h>
2008-08-05 19:14:15 +04:00
# include <mach/clock.h>
2006-05-16 14:54:37 +04:00
# include "clock.h"
/*forward declaration*/
static struct clk per_ck ;
static struct clk hclk_ck ;
static struct clk ck_1MHz ;
static struct clk ck_13MHz ;
static struct clk ck_pll1 ;
static int local_set_rate ( struct clk * clk , u32 rate ) ;
static inline void clock_lock ( void )
{
local_irq_disable ( ) ;
}
static inline void clock_unlock ( void )
{
local_irq_enable ( ) ;
}
static void propagate_rate ( struct clk * clk )
{
struct clk * tmp_clk ;
tmp_clk = clk ;
while ( tmp_clk - > propagate_next ) {
tmp_clk = tmp_clk - > propagate_next ;
local_set_rate ( tmp_clk , tmp_clk - > user_rate ) ;
}
}
2009-11-20 14:28:59 +03:00
static void clk_reg_disable ( struct clk * clk )
2006-05-16 14:54:37 +04:00
{
if ( clk - > enable_reg )
__raw_writel ( __raw_readl ( clk - > enable_reg ) &
~ ( 1 < < clk - > enable_shift ) , clk - > enable_reg ) ;
}
2009-11-20 14:28:59 +03:00
static int clk_reg_enable ( struct clk * clk )
2006-05-16 14:54:37 +04:00
{
if ( clk - > enable_reg )
__raw_writel ( __raw_readl ( clk - > enable_reg ) |
( 1 < < clk - > enable_shift ) , clk - > enable_reg ) ;
2009-11-20 14:28:59 +03:00
return 0 ;
2006-05-16 14:54:37 +04:00
}
static inline void clk_reg_disable1 ( struct clk * clk )
{
if ( clk - > enable_reg1 )
__raw_writel ( __raw_readl ( clk - > enable_reg1 ) &
~ ( 1 < < clk - > enable_shift1 ) , clk - > enable_reg1 ) ;
}
static inline void clk_reg_enable1 ( struct clk * clk )
{
if ( clk - > enable_reg1 )
__raw_writel ( __raw_readl ( clk - > enable_reg1 ) |
( 1 < < clk - > enable_shift1 ) , clk - > enable_reg1 ) ;
}
static int clk_wait_for_pll_lock ( struct clk * clk )
{
int i ;
i = 0 ;
while ( i + + < 0xFFF & & ! ( __raw_readl ( clk - > scale_reg ) & 1 ) ) ; /*wait for PLL to lock */
if ( ! ( __raw_readl ( clk - > scale_reg ) & 1 ) ) {
printk ( KERN_ERR
" %s ERROR: failed to lock, scale reg data: %x \n " ,
clk - > name , __raw_readl ( clk - > scale_reg ) ) ;
return - 1 ;
}
return 0 ;
}
static int switch_to_dirty_13mhz ( struct clk * clk )
{
int i ;
int ret ;
u32 tmp_reg ;
ret = 0 ;
if ( ! clk - > rate )
clk_reg_enable1 ( clk ) ;
tmp_reg = __raw_readl ( clk - > parent_switch_reg ) ;
/*if 13Mhz clock selected, select 13'MHz (dirty) source from OSC */
if ( ! ( tmp_reg & 1 ) ) {
tmp_reg | = ( 1 < < 1 ) ; /* Trigger switch to 13'MHz (dirty) clock */
__raw_writel ( tmp_reg , clk - > parent_switch_reg ) ;
i = 0 ;
while ( i + + < 0xFFF & & ! ( __raw_readl ( clk - > parent_switch_reg ) & 1 ) ) ; /*wait for 13'MHz selection status */
if ( ! ( __raw_readl ( clk - > parent_switch_reg ) & 1 ) ) {
printk ( KERN_ERR
" %s ERROR: failed to select 13'MHz, parent sw reg data: %x \n " ,
clk - > name , __raw_readl ( clk - > parent_switch_reg ) ) ;
ret = - 1 ;
}
}
if ( ! clk - > rate )
clk_reg_disable1 ( clk ) ;
return ret ;
}
static int switch_to_clean_13mhz ( struct clk * clk )
{
int i ;
int ret ;
u32 tmp_reg ;
ret = 0 ;
if ( ! clk - > rate )
clk_reg_enable1 ( clk ) ;
tmp_reg = __raw_readl ( clk - > parent_switch_reg ) ;
/*if 13'Mhz clock selected, select 13MHz (clean) source from OSC */
if ( tmp_reg & 1 ) {
tmp_reg & = ~ ( 1 < < 1 ) ; /* Trigger switch to 13MHz (clean) clock */
__raw_writel ( tmp_reg , clk - > parent_switch_reg ) ;
i = 0 ;
while ( i + + < 0xFFF & & ( __raw_readl ( clk - > parent_switch_reg ) & 1 ) ) ; /*wait for 13MHz selection status */
if ( __raw_readl ( clk - > parent_switch_reg ) & 1 ) {
printk ( KERN_ERR
" %s ERROR: failed to select 13MHz, parent sw reg data: %x \n " ,
clk - > name , __raw_readl ( clk - > parent_switch_reg ) ) ;
ret = - 1 ;
}
}
if ( ! clk - > rate )
clk_reg_disable1 ( clk ) ;
return ret ;
}
static int set_13MHz_parent ( struct clk * clk , struct clk * parent )
{
int ret = - EINVAL ;
if ( parent = = & ck_13MHz )
ret = switch_to_clean_13mhz ( clk ) ;
else if ( parent = = & ck_pll1 )
ret = switch_to_dirty_13mhz ( clk ) ;
return ret ;
}
# define PLL160_MIN_FCCO 156000
# define PLL160_MAX_FCCO 320000
/*
* Calculate pll160 settings .
* Possible input : up to 320 MHz with step of clk - > parent - > rate .
* In PNX4008 parent rate for pll160s may be either 1 or 13 MHz .
* Ignored paths : " feedback " ( bit 13 set ) , " div-by-N " .
* Setting ARM PLL4 rate to 0 will put CPU into direct run mode .
* Setting PLL5 and PLL3 rate to 0 will disable USB and DSP clock input .
* Please refer to PNX4008 IC manual for details .
*/
static int pll160_set_rate ( struct clk * clk , u32 rate )
{
u32 tmp_reg , tmp_m , tmp_2p , i ;
u32 parent_rate ;
int ret = - EINVAL ;
parent_rate = clk - > parent - > rate ;
if ( ! parent_rate )
goto out ;
/* set direct run for ARM or disable output for others */
clk_reg_disable ( clk ) ;
/* disable source input as well (ignored for ARM) */
clk_reg_disable1 ( clk ) ;
tmp_reg = __raw_readl ( clk - > scale_reg ) ;
tmp_reg & = ~ 0x1ffff ; /*clear all settings, power down */
__raw_writel ( tmp_reg , clk - > scale_reg ) ;
rate - = rate % parent_rate ; /*round down the input */
if ( rate > PLL160_MAX_FCCO )
rate = PLL160_MAX_FCCO ;
if ( ! rate ) {
clk - > rate = 0 ;
ret = 0 ;
goto out ;
}
clk_reg_enable1 ( clk ) ;
tmp_reg = __raw_readl ( clk - > scale_reg ) ;
if ( rate = = parent_rate ) {
/*enter direct bypass mode */
tmp_reg | = ( ( 1 < < 14 ) | ( 1 < < 15 ) ) ;
__raw_writel ( tmp_reg , clk - > scale_reg ) ;
clk - > rate = parent_rate ;
clk_reg_enable ( clk ) ;
ret = 0 ;
goto out ;
}
i = 0 ;
for ( tmp_2p = 1 ; tmp_2p < 16 ; tmp_2p < < = 1 ) {
if ( rate * tmp_2p > = PLL160_MIN_FCCO )
break ;
i + + ;
}
if ( tmp_2p > 1 )
tmp_reg | = ( ( i - 1 ) < < 11 ) ;
else
tmp_reg | = ( 1 < < 14 ) ; /*direct mode, no divide */
tmp_m = rate * tmp_2p ;
tmp_m / = parent_rate ;
tmp_reg | = ( tmp_m - 1 ) < < 1 ; /*calculate M */
tmp_reg | = ( 1 < < 16 ) ; /*power up PLL */
__raw_writel ( tmp_reg , clk - > scale_reg ) ;
if ( clk_wait_for_pll_lock ( clk ) < 0 ) {
clk_reg_disable ( clk ) ;
clk_reg_disable1 ( clk ) ;
tmp_reg = __raw_readl ( clk - > scale_reg ) ;
tmp_reg & = ~ 0x1ffff ; /*clear all settings, power down */
__raw_writel ( tmp_reg , clk - > scale_reg ) ;
clk - > rate = 0 ;
ret = - EFAULT ;
goto out ;
}
clk - > rate = ( tmp_m * parent_rate ) / tmp_2p ;
if ( clk - > flags & RATE_PROPAGATES )
propagate_rate ( clk ) ;
clk_reg_enable ( clk ) ;
ret = 0 ;
out :
return ret ;
}
/*configure PER_CLK*/
static int per_clk_set_rate ( struct clk * clk , u32 rate )
{
u32 tmp ;
tmp = __raw_readl ( clk - > scale_reg ) ;
tmp & = ~ ( 0x1f < < 2 ) ;
tmp | = ( ( clk - > parent - > rate / clk - > rate ) - 1 ) < < 2 ;
__raw_writel ( tmp , clk - > scale_reg ) ;
clk - > rate = rate ;
return 0 ;
}
/*configure HCLK*/
static int hclk_set_rate ( struct clk * clk , u32 rate )
{
u32 tmp ;
tmp = __raw_readl ( clk - > scale_reg ) ;
tmp = tmp & ~ 0x3 ;
switch ( rate ) {
case 1 :
break ;
case 2 :
tmp | = 1 ;
break ;
case 4 :
tmp | = 2 ;
break ;
}
__raw_writel ( tmp , clk - > scale_reg ) ;
clk - > rate = rate ;
return 0 ;
}
static u32 hclk_round_rate ( struct clk * clk , u32 rate )
{
switch ( rate ) {
case 1 :
case 4 :
return rate ;
}
return 2 ;
}
static u32 per_clk_round_rate ( struct clk * clk , u32 rate )
{
return CLK_RATE_13MHZ ;
}
static int on_off_set_rate ( struct clk * clk , u32 rate )
{
if ( rate ) {
clk_reg_enable ( clk ) ;
clk - > rate = 1 ;
} else {
clk_reg_disable ( clk ) ;
clk - > rate = 0 ;
}
return 0 ;
}
static int on_off_inv_set_rate ( struct clk * clk , u32 rate )
{
if ( rate ) {
clk_reg_disable ( clk ) ; /*enable bit is inverted */
clk - > rate = 1 ;
} else {
clk_reg_enable ( clk ) ;
clk - > rate = 0 ;
}
return 0 ;
}
static u32 on_off_round_rate ( struct clk * clk , u32 rate )
{
return ( rate ? 1 : 0 ) ;
}
static u32 pll4_round_rate ( struct clk * clk , u32 rate )
{
if ( rate > CLK_RATE_208MHZ )
rate = CLK_RATE_208MHZ ;
if ( rate = = CLK_RATE_208MHZ & & hclk_ck . user_rate = = 1 )
rate = CLK_RATE_208MHZ - CLK_RATE_13MHZ ;
return ( rate - ( rate % ( hclk_ck . user_rate * CLK_RATE_13MHZ ) ) ) ;
}
static u32 pll3_round_rate ( struct clk * clk , u32 rate )
{
if ( rate > CLK_RATE_208MHZ )
rate = CLK_RATE_208MHZ ;
return ( rate - rate % CLK_RATE_13MHZ ) ;
}
static u32 pll5_round_rate ( struct clk * clk , u32 rate )
{
return ( rate ? CLK_RATE_48MHZ : 0 ) ;
}
static u32 ck_13MHz_round_rate ( struct clk * clk , u32 rate )
{
return ( rate ? CLK_RATE_13MHZ : 0 ) ;
}
static int ck_13MHz_set_rate ( struct clk * clk , u32 rate )
{
if ( rate ) {
clk_reg_disable ( clk ) ; /*enable bit is inverted */
udelay ( 500 ) ;
clk - > rate = CLK_RATE_13MHZ ;
ck_1MHz . rate = CLK_RATE_1MHZ ;
} else {
clk_reg_enable ( clk ) ;
clk - > rate = 0 ;
ck_1MHz . rate = 0 ;
}
return 0 ;
}
static int pll1_set_rate ( struct clk * clk , u32 rate )
{
#if 0 /* doesn't work on some boards, probably a HW BUG */
if ( rate ) {
clk_reg_disable ( clk ) ; /*enable bit is inverted */
if ( ! clk_wait_for_pll_lock ( clk ) ) {
clk - > rate = CLK_RATE_13MHZ ;
} else {
clk_reg_enable ( clk ) ;
clk - > rate = 0 ;
}
} else {
clk_reg_enable ( clk ) ;
clk - > rate = 0 ;
}
# endif
return 0 ;
}
/* Clock sources */
static struct clk osc_13MHz = {
. name = " osc_13MHz " ,
. flags = FIXED_RATE ,
. rate = CLK_RATE_13MHZ ,
} ;
static struct clk ck_13MHz = {
. name = " ck_13MHz " ,
. parent = & osc_13MHz ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & ck_13MHz_round_rate ,
. set_rate = & ck_13MHz_set_rate ,
. enable_reg = OSC13CTRL_REG ,
. enable_shift = 0 ,
. rate = CLK_RATE_13MHZ ,
} ;
static struct clk osc_32KHz = {
. name = " osc_32KHz " ,
. flags = FIXED_RATE ,
. rate = CLK_RATE_32KHZ ,
} ;
/*attached to PLL5*/
static struct clk ck_1MHz = {
. name = " ck_1MHz " ,
. flags = FIXED_RATE | PARENT_SET_RATE ,
. parent = & ck_13MHz ,
} ;
/* PLL1 (397) - provides 13' MHz clock */
static struct clk ck_pll1 = {
. name = " ck_pll1 " ,
. parent = & osc_32KHz ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & ck_13MHz_round_rate ,
. set_rate = & pll1_set_rate ,
. enable_reg = PLLCTRL_REG ,
. enable_shift = 1 ,
. scale_reg = PLLCTRL_REG ,
. rate = CLK_RATE_13MHZ ,
} ;
/* CPU/Bus PLL */
static struct clk ck_pll4 = {
. name = " ck_pll4 " ,
. parent = & ck_pll1 ,
. flags = RATE_PROPAGATES | NEEDS_INITIALIZATION ,
. propagate_next = & per_ck ,
. round_rate = & pll4_round_rate ,
. set_rate = & pll160_set_rate ,
. rate = CLK_RATE_208MHZ ,
. scale_reg = HCLKPLLCTRL_REG ,
. enable_reg = PWRCTRL_REG ,
. enable_shift = 2 ,
. parent_switch_reg = SYSCLKCTRL_REG ,
. set_parent = & set_13MHz_parent ,
} ;
/* USB PLL */
static struct clk ck_pll5 = {
. name = " ck_pll5 " ,
. parent = & ck_1MHz ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & pll5_round_rate ,
. set_rate = & pll160_set_rate ,
. scale_reg = USBCTRL_REG ,
. enable_reg = USBCTRL_REG ,
. enable_shift = 18 ,
. enable_reg1 = USBCTRL_REG ,
. enable_shift1 = 17 ,
} ;
/* XPERTTeak DSP PLL */
static struct clk ck_pll3 = {
. name = " ck_pll3 " ,
. parent = & ck_pll1 ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & pll3_round_rate ,
. set_rate = & pll160_set_rate ,
. scale_reg = DSPPLLCTRL_REG ,
. enable_reg = DSPCLKCTRL_REG ,
. enable_shift = 3 ,
. enable_reg1 = DSPCLKCTRL_REG ,
. enable_shift1 = 2 ,
. parent_switch_reg = DSPCLKCTRL_REG ,
. set_parent = & set_13MHz_parent ,
} ;
static struct clk hclk_ck = {
. name = " hclk_ck " ,
. parent = & ck_pll4 ,
. flags = PARENT_SET_RATE ,
. set_rate = & hclk_set_rate ,
. round_rate = & hclk_round_rate ,
. scale_reg = HCLKDIVCTRL_REG ,
. rate = 2 ,
. user_rate = 2 ,
} ;
static struct clk per_ck = {
. name = " per_ck " ,
. parent = & ck_pll4 ,
. flags = FIXED_RATE ,
. propagate_next = & hclk_ck ,
. set_rate = & per_clk_set_rate ,
. round_rate = & per_clk_round_rate ,
. scale_reg = HCLKDIVCTRL_REG ,
. rate = CLK_RATE_13MHZ ,
. user_rate = CLK_RATE_13MHZ ,
} ;
static struct clk m2hclk_ck = {
. name = " m2hclk_ck " ,
. parent = & hclk_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_inv_set_rate ,
. rate = 1 ,
. enable_shift = 6 ,
. enable_reg = PWRCTRL_REG ,
} ;
static struct clk vfp9_ck = {
. name = " vfp9_ck " ,
. parent = & ck_pll4 ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. rate = 1 ,
. enable_shift = 4 ,
. enable_reg = VFP9CLKCTRL_REG ,
} ;
static struct clk keyscan_ck = {
. name = " keyscan_ck " ,
. parent = & osc_32KHz ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 0 ,
. enable_reg = KEYCLKCTRL_REG ,
} ;
static struct clk touch_ck = {
. name = " touch_ck " ,
. parent = & osc_32KHz ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 0 ,
. enable_reg = TSCLKCTRL_REG ,
} ;
static struct clk pwm1_ck = {
. name = " pwm1_ck " ,
. parent = & osc_32KHz ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 0 ,
. enable_reg = PWMCLKCTRL_REG ,
} ;
static struct clk pwm2_ck = {
. name = " pwm2_ck " ,
. parent = & osc_32KHz ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 2 ,
. enable_reg = PWMCLKCTRL_REG ,
} ;
static struct clk jpeg_ck = {
. name = " jpeg_ck " ,
. parent = & hclk_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 0 ,
. enable_reg = JPEGCLKCTRL_REG ,
} ;
static struct clk ms_ck = {
. name = " ms_ck " ,
. parent = & ck_pll4 ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 5 ,
. enable_reg = MSCTRL_REG ,
} ;
static struct clk dum_ck = {
. name = " dum_ck " ,
. parent = & hclk_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 0 ,
. enable_reg = DUMCLKCTRL_REG ,
} ;
static struct clk flash_ck = {
. name = " flash_ck " ,
. parent = & hclk_ck ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 1 , /* Only MLC clock supported */
. enable_reg = FLASHCLKCTRL_REG ,
} ;
static struct clk i2c0_ck = {
. name = " i2c0_ck " ,
. parent = & per_ck ,
2009-11-20 15:46:07 +03:00
. flags = NEEDS_INITIALIZATION | FIXED_RATE ,
2006-05-16 14:54:37 +04:00
. enable_shift = 0 ,
. enable_reg = I2CCLKCTRL_REG ,
2009-11-20 15:46:07 +03:00
. rate = 13000000 ,
2009-11-20 14:44:46 +03:00
. enable = clk_reg_enable ,
. disable = clk_reg_disable ,
2006-05-16 14:54:37 +04:00
} ;
static struct clk i2c1_ck = {
. name = " i2c1_ck " ,
. parent = & per_ck ,
2009-11-20 15:46:07 +03:00
. flags = NEEDS_INITIALIZATION | FIXED_RATE ,
2006-05-16 14:54:37 +04:00
. enable_shift = 1 ,
. enable_reg = I2CCLKCTRL_REG ,
2009-11-20 15:46:07 +03:00
. rate = 13000000 ,
2009-11-20 14:44:46 +03:00
. enable = clk_reg_enable ,
. disable = clk_reg_disable ,
2006-05-16 14:54:37 +04:00
} ;
static struct clk i2c2_ck = {
. name = " i2c2_ck " ,
. parent = & per_ck ,
2009-11-20 15:46:07 +03:00
. flags = NEEDS_INITIALIZATION | FIXED_RATE ,
2006-05-16 14:54:37 +04:00
. enable_shift = 2 ,
. enable_reg = USB_OTG_CLKCTRL_REG ,
2009-11-20 15:46:07 +03:00
. rate = 13000000 ,
2009-11-20 14:44:46 +03:00
. enable = clk_reg_enable ,
. disable = clk_reg_disable ,
2006-05-16 14:54:37 +04:00
} ;
static struct clk spi0_ck = {
. name = " spi0_ck " ,
. parent = & hclk_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 0 ,
. enable_reg = SPICTRL_REG ,
} ;
static struct clk spi1_ck = {
. name = " spi1_ck " ,
. parent = & hclk_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 4 ,
. enable_reg = SPICTRL_REG ,
} ;
static struct clk dma_ck = {
. name = " dma_ck " ,
. parent = & hclk_ck ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 0 ,
. enable_reg = DMACLKCTRL_REG ,
} ;
static struct clk uart3_ck = {
. name = " uart3_ck " ,
. parent = & per_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. rate = 1 ,
. enable_shift = 0 ,
. enable_reg = UARTCLKCTRL_REG ,
} ;
static struct clk uart4_ck = {
. name = " uart4_ck " ,
. parent = & per_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 1 ,
. enable_reg = UARTCLKCTRL_REG ,
} ;
static struct clk uart5_ck = {
. name = " uart5_ck " ,
. parent = & per_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. rate = 1 ,
. enable_shift = 2 ,
. enable_reg = UARTCLKCTRL_REG ,
} ;
static struct clk uart6_ck = {
. name = " uart6_ck " ,
. parent = & per_ck ,
. flags = NEEDS_INITIALIZATION ,
. round_rate = & on_off_round_rate ,
. set_rate = & on_off_set_rate ,
. enable_shift = 3 ,
. enable_reg = UARTCLKCTRL_REG ,
} ;
2006-06-26 19:31:49 +04:00
static struct clk wdt_ck = {
. name = " wdt_ck " ,
. parent = & per_ck ,
. flags = NEEDS_INITIALIZATION ,
. enable_shift = 0 ,
. enable_reg = TIMCLKCTRL_REG ,
2009-11-20 16:04:14 +03:00
. enable = clk_reg_enable ,
. disable = clk_reg_disable ,
2006-06-26 19:31:49 +04:00
} ;
2006-05-16 14:54:37 +04:00
/* These clocks are visible outside this module
* and can be initialized
*/
2009-11-20 13:32:41 +03:00
static struct clk * onchip_clks [ ] __initdata = {
2006-05-16 14:54:37 +04:00
& ck_13MHz ,
& ck_pll1 ,
& ck_pll4 ,
& ck_pll5 ,
& ck_pll3 ,
& vfp9_ck ,
& m2hclk_ck ,
& hclk_ck ,
& dma_ck ,
& flash_ck ,
& dum_ck ,
& keyscan_ck ,
& pwm1_ck ,
& pwm2_ck ,
& jpeg_ck ,
& ms_ck ,
& touch_ck ,
& i2c0_ck ,
& i2c1_ck ,
& i2c2_ck ,
& spi0_ck ,
& spi1_ck ,
& uart3_ck ,
& uart4_ck ,
& uart5_ck ,
& uart6_ck ,
2006-06-26 19:31:49 +04:00
& wdt_ck ,
2006-05-16 14:54:37 +04:00
} ;
2009-11-20 13:32:41 +03:00
static struct clk_lookup onchip_clkreg [ ] = {
{ . clk = & ck_13MHz , . con_id = " ck_13MHz " } ,
{ . clk = & ck_pll1 , . con_id = " ck_pll1 " } ,
{ . clk = & ck_pll4 , . con_id = " ck_pll4 " } ,
{ . clk = & ck_pll5 , . con_id = " ck_pll5 " } ,
{ . clk = & ck_pll3 , . con_id = " ck_pll3 " } ,
{ . clk = & vfp9_ck , . con_id = " vfp9_ck " } ,
{ . clk = & m2hclk_ck , . con_id = " m2hclk_ck " } ,
{ . clk = & hclk_ck , . con_id = " hclk_ck " } ,
{ . clk = & dma_ck , . con_id = " dma_ck " } ,
{ . clk = & flash_ck , . con_id = " flash_ck " } ,
{ . clk = & dum_ck , . con_id = " dum_ck " } ,
{ . clk = & keyscan_ck , . con_id = " keyscan_ck " } ,
{ . clk = & pwm1_ck , . con_id = " pwm1_ck " } ,
{ . clk = & pwm2_ck , . con_id = " pwm2_ck " } ,
{ . clk = & jpeg_ck , . con_id = " jpeg_ck " } ,
{ . clk = & ms_ck , . con_id = " ms_ck " } ,
{ . clk = & touch_ck , . con_id = " touch_ck " } ,
2009-11-20 13:46:24 +03:00
{ . clk = & i2c0_ck , . dev_id = " pnx-i2c.0 " } ,
{ . clk = & i2c1_ck , . dev_id = " pnx-i2c.1 " } ,
{ . clk = & i2c2_ck , . dev_id = " pnx-i2c.2 " } ,
2009-11-20 13:32:41 +03:00
{ . clk = & spi0_ck , . con_id = " spi0_ck " } ,
{ . clk = & spi1_ck , . con_id = " spi1_ck " } ,
{ . clk = & uart3_ck , . con_id = " uart3_ck " } ,
{ . clk = & uart4_ck , . con_id = " uart4_ck " } ,
{ . clk = & uart5_ck , . con_id = " uart5_ck " } ,
{ . clk = & uart6_ck , . con_id = " uart6_ck " } ,
2009-11-20 16:07:28 +03:00
{ . clk = & wdt_ck , . dev_id = " pnx4008-watchdog " } ,
2009-11-20 13:32:41 +03:00
} ;
2006-06-22 13:26:20 +04:00
static void local_clk_disable ( struct clk * clk )
{
2009-11-20 14:25:44 +03:00
if ( WARN_ON ( clk - > usecount = = 0 ) )
return ;
2006-06-22 13:26:20 +04:00
2009-11-20 14:25:44 +03:00
if ( ! ( - - clk - > usecount ) ) {
2009-11-20 14:28:59 +03:00
if ( clk - > disable )
clk - > disable ( clk ) ;
else if ( ! ( clk - > flags & FIXED_RATE ) & & clk - > rate & & clk - > set_rate )
2009-11-20 14:25:44 +03:00
clk - > set_rate ( clk , 0 ) ;
2006-06-22 13:26:20 +04:00
if ( clk - > parent )
2009-11-20 14:25:44 +03:00
local_clk_disable ( clk - > parent ) ;
2006-06-22 13:26:20 +04:00
}
}
2009-11-20 14:25:44 +03:00
static int local_clk_enable ( struct clk * clk )
2006-06-22 13:26:20 +04:00
{
int ret = 0 ;
2009-11-20 14:25:44 +03:00
if ( clk - > usecount = = 0 ) {
if ( clk - > parent ) {
ret = local_clk_enable ( clk - > parent ) ;
if ( ret ! = 0 )
goto out ;
2006-06-22 13:26:20 +04:00
}
2009-11-20 14:28:59 +03:00
if ( clk - > enable )
ret = clk - > enable ( clk ) ;
else if ( ! ( clk - > flags & FIXED_RATE ) & & ! clk - > rate & & clk - > set_rate
& & clk - > user_rate )
2009-11-20 14:25:44 +03:00
ret = clk - > set_rate ( clk , clk - > user_rate ) ;
2006-06-22 13:26:20 +04:00
if ( ret ! = 0 & & clk - > parent ) {
2009-11-20 14:25:44 +03:00
local_clk_disable ( clk - > parent ) ;
goto out ;
2006-06-22 13:26:20 +04:00
}
2009-11-20 14:25:44 +03:00
clk - > usecount + + ;
2006-06-22 13:26:20 +04:00
}
out :
return ret ;
}
2006-05-16 14:54:37 +04:00
static int local_set_rate ( struct clk * clk , u32 rate )
{
int ret = - EINVAL ;
if ( clk - > set_rate ) {
if ( clk - > user_rate = = clk - > rate & & clk - > parent - > rate ) {
/* if clock enabled or rate not set */
clk - > user_rate = clk - > round_rate ( clk , rate ) ;
ret = clk - > set_rate ( clk , clk - > user_rate ) ;
} else
clk - > user_rate = clk - > round_rate ( clk , rate ) ;
ret = 0 ;
}
return ret ;
}
int clk_set_rate ( struct clk * clk , unsigned long rate )
{
int ret = - EINVAL ;
if ( clk - > flags & FIXED_RATE )
goto out ;
clock_lock ( ) ;
if ( ( clk - > flags & PARENT_SET_RATE ) & & clk - > parent ) {
clk - > user_rate = clk - > round_rate ( clk , rate ) ;
/* parent clock needs to be refreshed
for the setting to take effect */
} else {
ret = local_set_rate ( clk , rate ) ;
}
ret = 0 ;
clock_unlock ( ) ;
out :
return ret ;
}
EXPORT_SYMBOL ( clk_set_rate ) ;
unsigned long clk_get_rate ( struct clk * clk )
{
unsigned long ret ;
clock_lock ( ) ;
ret = clk - > rate ;
clock_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL ( clk_get_rate ) ;
int clk_enable ( struct clk * clk )
{
2009-11-20 14:25:44 +03:00
int ret ;
2006-05-16 14:54:37 +04:00
clock_lock ( ) ;
2009-11-20 14:25:44 +03:00
ret = local_clk_enable ( clk ) ;
2006-05-16 14:54:37 +04:00
clock_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL ( clk_enable ) ;
void clk_disable ( struct clk * clk )
{
clock_lock ( ) ;
2009-11-20 14:25:44 +03:00
local_clk_disable ( clk ) ;
2006-05-16 14:54:37 +04:00
clock_unlock ( ) ;
}
2006-06-22 13:26:20 +04:00
EXPORT_SYMBOL ( clk_disable ) ;
2006-05-16 14:54:37 +04:00
long clk_round_rate ( struct clk * clk , unsigned long rate )
{
long ret ;
clock_lock ( ) ;
if ( clk - > round_rate )
ret = clk - > round_rate ( clk , rate ) ;
else
ret = clk - > rate ;
clock_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL ( clk_round_rate ) ;
int clk_set_parent ( struct clk * clk , struct clk * parent )
{
int ret = - ENODEV ;
if ( ! clk - > set_parent )
goto out ;
clock_lock ( ) ;
ret = clk - > set_parent ( clk , parent ) ;
if ( ! ret )
clk - > parent = parent ;
clock_unlock ( ) ;
out :
return ret ;
}
EXPORT_SYMBOL ( clk_set_parent ) ;
static int __init clk_init ( void )
{
struct clk * * clkp ;
/* Disable autoclocking, as it doesn't seem to work */
__raw_writel ( 0xff , AUTOCLK_CTRL ) ;
for ( clkp = onchip_clks ; clkp < onchip_clks + ARRAY_SIZE ( onchip_clks ) ;
clkp + + ) {
2009-11-20 14:28:59 +03:00
struct clk * clk = * clkp ;
if ( clk - > flags & NEEDS_INITIALIZATION ) {
if ( clk - > set_rate ) {
clk - > user_rate = clk - > rate ;
local_set_rate ( clk , clk - > user_rate ) ;
if ( clk - > set_parent )
clk - > set_parent ( clk , clk - > parent ) ;
}
if ( clk - > enable & & clk - > usecount )
clk - > enable ( clk ) ;
if ( clk - > disable & & ! clk - > usecount )
clk - > disable ( clk ) ;
2006-05-16 14:54:37 +04:00
}
pr_debug ( " %s: clock %s, rate %ld \n " ,
2009-11-20 14:28:59 +03:00
__func__ , clk - > name , clk - > rate ) ;
2006-05-16 14:54:37 +04:00
}
2009-11-20 14:25:44 +03:00
local_clk_enable ( & ck_pll4 ) ;
2006-05-16 14:54:37 +04:00
/* if ck_13MHz is not used, disable it. */
if ( ck_13MHz . usecount = = 0 )
local_clk_disable ( & ck_13MHz ) ;
/* Disable autoclocking */
__raw_writeb ( 0xff , AUTOCLK_CTRL ) ;
2009-11-20 13:32:41 +03:00
clkdev_add_table ( onchip_clkreg , ARRAY_SIZE ( onchip_clkreg ) ) ;
2006-05-16 14:54:37 +04:00
return 0 ;
}
arch_initcall ( clk_init ) ;