2009-07-31 15:29:22 +04:00
# include <linux/clk.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/io.h>
2010-11-17 12:04:33 +03:00
# include <linux/clkdev.h>
2009-07-31 15:29:22 +04:00
# include <mach/clock.h>
# include <mach/hardware.h>
# include <mach/common.h>
# include <asm/bug.h>
# include <asm/div64.h>
# include "crm_regs.h"
# define CRM_SMALL_DIVIDER(base, name) \
crm_small_divider ( base , \
base # # _ # # name # # _OFFSET , \
base # # _ # # name # # _MASK )
# define CRM_1DIVIDER(base, name) \
crm_divider ( base , \
base # # _ # # name # # _OFFSET , \
base # # _ # # name # # _MASK , 1 )
# define CRM_16DIVIDER(base, name) \
crm_divider ( base , \
base # # _ # # name # # _OFFSET , \
base # # _ # # name # # _MASK , 16 )
static u32 crm_small_divider ( void __iomem * reg , u8 offset , u32 mask )
{
static const u32 crm_small_dividers [ ] = {
2 , 3 , 4 , 5 , 6 , 8 , 10 , 12
} ;
u8 idx ;
idx = ( __raw_readl ( reg ) & mask ) > > offset ;
if ( idx > 7 )
return 1 ;
return crm_small_dividers [ idx ] ;
}
static u32 crm_divider ( void __iomem * reg , u8 offset , u32 mask , u32 z )
{
u32 div ;
div = ( __raw_readl ( reg ) & mask ) > > offset ;
return div ? div : z ;
}
static int _clk_1bit_enable ( struct clk * clk )
{
u32 reg ;
reg = __raw_readl ( clk - > enable_reg ) ;
reg | = 1 < < clk - > enable_shift ;
__raw_writel ( reg , clk - > enable_reg ) ;
return 0 ;
}
static void _clk_1bit_disable ( struct clk * clk )
{
u32 reg ;
reg = __raw_readl ( clk - > enable_reg ) ;
reg & = ~ ( 1 < < clk - > enable_shift ) ;
__raw_writel ( reg , clk - > enable_reg ) ;
}
static int _clk_3bit_enable ( struct clk * clk )
{
u32 reg ;
reg = __raw_readl ( clk - > enable_reg ) ;
reg | = 0x7 < < clk - > enable_shift ;
__raw_writel ( reg , clk - > enable_reg ) ;
return 0 ;
}
static void _clk_3bit_disable ( struct clk * clk )
{
u32 reg ;
reg = __raw_readl ( clk - > enable_reg ) ;
reg & = ~ ( 0x7 < < clk - > enable_shift ) ;
__raw_writel ( reg , clk - > enable_reg ) ;
}
static unsigned long ckih_rate ;
static unsigned long clk_ckih_get_rate ( struct clk * clk )
{
return ckih_rate ;
}
static struct clk ckih_clk = {
. get_rate = clk_ckih_get_rate ,
} ;
static unsigned long clk_ckih_x2_get_rate ( struct clk * clk )
{
return 2 * clk_get_rate ( clk - > parent ) ;
}
static struct clk ckih_x2_clk = {
. parent = & ckih_clk ,
. get_rate = clk_ckih_x2_get_rate ,
} ;
static unsigned long clk_ckil_get_rate ( struct clk * clk )
{
return CKIL_CLK_FREQ ;
}
static struct clk ckil_clk = {
. get_rate = clk_ckil_get_rate ,
} ;
/* plls stuff */
static struct clk mcu_pll_clk ;
static struct clk dsp_pll_clk ;
static struct clk usb_pll_clk ;
static struct clk * pll_clk ( u8 sel )
{
switch ( sel ) {
case 0 :
return & mcu_pll_clk ;
case 1 :
return & dsp_pll_clk ;
case 2 :
return & usb_pll_clk ;
}
BUG ( ) ;
}
static void __iomem * pll_base ( struct clk * clk )
{
if ( clk = = & mcu_pll_clk )
return MXC_PLL0_BASE ;
else if ( clk = = & dsp_pll_clk )
return MXC_PLL1_BASE ;
else if ( clk = = & usb_pll_clk )
return MXC_PLL2_BASE ;
BUG ( ) ;
}
static unsigned long clk_pll_get_rate ( struct clk * clk )
{
const void __iomem * pllbase ;
unsigned long dp_op , dp_mfd , dp_mfn , pll_hfsm , ref_clk , mfi ;
long mfn , mfn_abs , mfd , pdf ;
s64 temp ;
pllbase = pll_base ( clk ) ;
pll_hfsm = __raw_readl ( pllbase + MXC_PLL_DP_CTL ) & MXC_PLL_DP_CTL_HFSM ;
if ( pll_hfsm = = 0 ) {
dp_op = __raw_readl ( pllbase + MXC_PLL_DP_OP ) ;
dp_mfd = __raw_readl ( pllbase + MXC_PLL_DP_MFD ) ;
dp_mfn = __raw_readl ( pllbase + MXC_PLL_DP_MFN ) ;
} else {
dp_op = __raw_readl ( pllbase + MXC_PLL_DP_HFS_OP ) ;
dp_mfd = __raw_readl ( pllbase + MXC_PLL_DP_HFS_MFD ) ;
dp_mfn = __raw_readl ( pllbase + MXC_PLL_DP_HFS_MFN ) ;
}
pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK ;
mfi = ( dp_op > > MXC_PLL_DP_OP_MFI_OFFSET ) & MXC_PLL_DP_OP_PDF_MASK ;
mfi = ( mfi < = 5 ) ? 5 : mfi ;
mfd = dp_mfd & MXC_PLL_DP_MFD_MASK ;
mfn = dp_mfn & MXC_PLL_DP_MFN_MASK ;
mfn = ( mfn < = 0x4000000 ) ? mfn : ( mfn - 0x10000000 ) ;
if ( mfn < 0 )
mfn_abs = - mfn ;
else
mfn_abs = mfn ;
/* XXX: actually this asumes that ckih is fed to pll, but spec says
* that ckih_x2 is also possible . need to check this out .
*/
ref_clk = clk_get_rate ( & ckih_clk ) ;
ref_clk * = 2 ;
ref_clk / = pdf + 1 ;
temp = ( u64 ) ref_clk * mfn_abs ;
do_div ( temp , mfd ) ;
if ( mfn < 0 )
temp = - temp ;
temp + = ref_clk * mfi ;
return temp ;
}
static int clk_pll_enable ( struct clk * clk )
{
void __iomem * ctl ;
u32 reg ;
ctl = pll_base ( clk ) ;
reg = __raw_readl ( ctl ) ;
reg | = ( MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN ) ;
__raw_writel ( reg , ctl ) ;
do {
reg = __raw_readl ( ctl ) ;
} while ( ( reg & MXC_PLL_DP_CTL_LRF ) ! = MXC_PLL_DP_CTL_LRF ) ;
return 0 ;
}
static void clk_pll_disable ( struct clk * clk )
{
void __iomem * ctl ;
u32 reg ;
ctl = pll_base ( clk ) ;
reg = __raw_readl ( ctl ) ;
reg & = ~ ( MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN ) ;
__raw_writel ( reg , ctl ) ;
}
static struct clk mcu_pll_clk = {
. parent = & ckih_clk ,
. get_rate = clk_pll_get_rate ,
. enable = clk_pll_enable ,
. disable = clk_pll_disable ,
} ;
static struct clk dsp_pll_clk = {
. parent = & ckih_clk ,
. get_rate = clk_pll_get_rate ,
. enable = clk_pll_enable ,
. disable = clk_pll_disable ,
} ;
static struct clk usb_pll_clk = {
. parent = & ckih_clk ,
. get_rate = clk_pll_get_rate ,
. enable = clk_pll_enable ,
. disable = clk_pll_disable ,
} ;
/* plls stuff end */
/* ap_ref_clk stuff */
static struct clk ap_ref_clk ;
static unsigned long clk_ap_ref_get_rate ( struct clk * clk )
{
u32 ascsr , acsr ;
u8 ap_pat_ref_div_2 , ap_isel , acs , ads ;
ascsr = __raw_readl ( MXC_CRMAP_ASCSR ) ;
acsr = __raw_readl ( MXC_CRMAP_ACSR ) ;
/* 0 for ckih, 1 for ckih*2 */
ap_isel = ascsr & MXC_CRMAP_ASCSR_APISEL ;
/* reg divider */
ap_pat_ref_div_2 = ( ascsr > > MXC_CRMAP_ASCSR_AP_PATDIV2_OFFSET ) & 0x1 ;
/* undocumented, 1 for disabling divider */
ads = ( acsr > > MXC_CRMAP_ACSR_ADS_OFFSET ) & 0x1 ;
/* 0 for pat_ref, 1 for divider out */
acs = acsr & MXC_CRMAP_ACSR_ACS ;
if ( acs & ! ads )
/* use divided clock */
return clk_get_rate ( clk - > parent ) / ( ap_pat_ref_div_2 ? 2 : 1 ) ;
return clk_get_rate ( clk - > parent ) * ( ap_isel ? 2 : 1 ) ;
}
static struct clk ap_ref_clk = {
. parent = & ckih_clk ,
. get_rate = clk_ap_ref_get_rate ,
} ;
/* ap_ref_clk stuff end */
/* ap_pre_dfs_clk stuff */
static struct clk ap_pre_dfs_clk ;
static unsigned long clk_ap_pre_dfs_get_rate ( struct clk * clk )
{
u32 acsr , ascsr ;
acsr = __raw_readl ( MXC_CRMAP_ACSR ) ;
ascsr = __raw_readl ( MXC_CRMAP_ASCSR ) ;
if ( acsr & MXC_CRMAP_ACSR_ACS ) {
u8 sel ;
sel = ( ascsr & MXC_CRMAP_ASCSR_APSEL_MASK ) > >
MXC_CRMAP_ASCSR_APSEL_OFFSET ;
return clk_get_rate ( pll_clk ( sel ) ) /
CRM_SMALL_DIVIDER ( MXC_CRMAP_ACDR , ARMDIV ) ;
}
return clk_get_rate ( & ap_ref_clk ) ;
}
static struct clk ap_pre_dfs_clk = {
. get_rate = clk_ap_pre_dfs_get_rate ,
} ;
/* ap_pre_dfs_clk stuff end */
/* usb_clk stuff */
static struct clk usb_clk ;
static struct clk * clk_usb_parent ( struct clk * clk )
{
u32 acsr , ascsr ;
acsr = __raw_readl ( MXC_CRMAP_ACSR ) ;
ascsr = __raw_readl ( MXC_CRMAP_ASCSR ) ;
if ( acsr & MXC_CRMAP_ACSR_ACS ) {
u8 sel ;
sel = ( ascsr & MXC_CRMAP_ASCSR_USBSEL_MASK ) > >
MXC_CRMAP_ASCSR_USBSEL_OFFSET ;
return pll_clk ( sel ) ;
}
return & ap_ref_clk ;
}
static unsigned long clk_usb_get_rate ( struct clk * clk )
{
return clk_get_rate ( clk - > parent ) /
CRM_SMALL_DIVIDER ( MXC_CRMAP_ACDER2 , USBDIV ) ;
}
static struct clk usb_clk = {
. enable_reg = MXC_CRMAP_ACDER2 ,
. enable_shift = MXC_CRMAP_ACDER2_USBEN_OFFSET ,
. get_rate = clk_usb_get_rate ,
. enable = _clk_1bit_enable ,
. disable = _clk_1bit_disable ,
} ;
/* usb_clk stuff end */
static unsigned long clk_ipg_get_rate ( struct clk * clk )
{
return clk_get_rate ( clk - > parent ) / CRM_16DIVIDER ( MXC_CRMAP_ACDR , IPDIV ) ;
}
static unsigned long clk_ahb_get_rate ( struct clk * clk )
{
return clk_get_rate ( clk - > parent ) /
CRM_16DIVIDER ( MXC_CRMAP_ACDR , AHBDIV ) ;
}
static struct clk ipg_clk = {
. parent = & ap_pre_dfs_clk ,
. get_rate = clk_ipg_get_rate ,
} ;
static struct clk ahb_clk = {
. parent = & ap_pre_dfs_clk ,
. get_rate = clk_ahb_get_rate ,
} ;
/* perclk_clk stuff */
static struct clk perclk_clk ;
static unsigned long clk_perclk_get_rate ( struct clk * clk )
{
u32 acder2 ;
acder2 = __raw_readl ( MXC_CRMAP_ACDER2 ) ;
if ( acder2 & MXC_CRMAP_ACDER2_BAUD_ISEL_MASK )
return 2 * clk_get_rate ( clk - > parent ) ;
return clk_get_rate ( clk - > parent ) ;
}
static struct clk perclk_clk = {
. parent = & ckih_clk ,
. get_rate = clk_perclk_get_rate ,
} ;
/* perclk_clk stuff end */
/* uart_clk stuff */
static struct clk uart_clk [ ] ;
static unsigned long clk_uart_get_rate ( struct clk * clk )
{
u32 div ;
switch ( clk - > id ) {
case 0 :
case 1 :
div = CRM_SMALL_DIVIDER ( MXC_CRMAP_ACDER2 , BAUDDIV ) ;
break ;
case 2 :
div = CRM_SMALL_DIVIDER ( MXC_CRMAP_APRA , UART3DIV ) ;
break ;
default :
BUG ( ) ;
}
return clk_get_rate ( clk - > parent ) / div ;
}
static struct clk uart_clk [ ] = {
{
. id = 0 ,
. parent = & perclk_clk ,
. enable_reg = MXC_CRMAP_APRA ,
. enable_shift = MXC_CRMAP_APRA_UART1EN_OFFSET ,
. get_rate = clk_uart_get_rate ,
. enable = _clk_1bit_enable ,
. disable = _clk_1bit_disable ,
} , {
. id = 1 ,
. parent = & perclk_clk ,
. enable_reg = MXC_CRMAP_APRA ,
. enable_shift = MXC_CRMAP_APRA_UART2EN_OFFSET ,
. get_rate = clk_uart_get_rate ,
. enable = _clk_1bit_enable ,
. disable = _clk_1bit_disable ,
} , {
. id = 2 ,
. parent = & perclk_clk ,
. enable_reg = MXC_CRMAP_APRA ,
. enable_shift = MXC_CRMAP_APRA_UART3EN_OFFSET ,
. get_rate = clk_uart_get_rate ,
. enable = _clk_1bit_enable ,
. disable = _clk_1bit_disable ,
} ,
} ;
/* uart_clk stuff end */
/* sdhc_clk stuff */
static struct clk nfc_clk ;
static unsigned long clk_nfc_get_rate ( struct clk * clk )
{
return clk_get_rate ( clk - > parent ) /
CRM_1DIVIDER ( MXC_CRMAP_ACDER2 , NFCDIV ) ;
}
static struct clk nfc_clk = {
. parent = & ahb_clk ,
. enable_reg = MXC_CRMAP_ACDER2 ,
. enable_shift = MXC_CRMAP_ACDER2_NFCEN_OFFSET ,
. get_rate = clk_nfc_get_rate ,
. enable = _clk_1bit_enable ,
. disable = _clk_1bit_disable ,
} ;
/* sdhc_clk stuff end */
/* sdhc_clk stuff */
static struct clk sdhc_clk [ ] ;
static struct clk * clk_sdhc_parent ( struct clk * clk )
{
u32 aprb ;
u8 sel ;
u32 mask ;
int offset ;
aprb = __raw_readl ( MXC_CRMAP_APRB ) ;
switch ( clk - > id ) {
case 0 :
mask = MXC_CRMAP_APRB_SDHC1_ISEL_MASK ;
offset = MXC_CRMAP_APRB_SDHC1_ISEL_OFFSET ;
break ;
case 1 :
mask = MXC_CRMAP_APRB_SDHC2_ISEL_MASK ;
offset = MXC_CRMAP_APRB_SDHC2_ISEL_OFFSET ;
break ;
default :
BUG ( ) ;
}
sel = ( aprb & mask ) > > offset ;
switch ( sel ) {
case 0 :
return & ckih_clk ;
case 1 :
return & ckih_x2_clk ;
}
return & usb_clk ;
}
static unsigned long clk_sdhc_get_rate ( struct clk * clk )
{
u32 div ;
switch ( clk - > id ) {
case 0 :
div = CRM_SMALL_DIVIDER ( MXC_CRMAP_APRB , SDHC1_DIV ) ;
break ;
case 1 :
div = CRM_SMALL_DIVIDER ( MXC_CRMAP_APRB , SDHC2_DIV ) ;
break ;
default :
BUG ( ) ;
}
return clk_get_rate ( clk - > parent ) / div ;
}
static int clk_sdhc_enable ( struct clk * clk )
{
u32 amlpmre1 , aprb ;
amlpmre1 = __raw_readl ( MXC_CRMAP_AMLPMRE1 ) ;
aprb = __raw_readl ( MXC_CRMAP_APRB ) ;
switch ( clk - > id ) {
case 0 :
amlpmre1 | = ( 0x7 < < MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET ) ;
aprb | = ( 0x1 < < MXC_CRMAP_APRB_SDHC1EN_OFFSET ) ;
break ;
case 1 :
amlpmre1 | = ( 0x7 < < MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET ) ;
aprb | = ( 0x1 < < MXC_CRMAP_APRB_SDHC2EN_OFFSET ) ;
break ;
}
__raw_writel ( amlpmre1 , MXC_CRMAP_AMLPMRE1 ) ;
__raw_writel ( aprb , MXC_CRMAP_APRB ) ;
return 0 ;
}
static void clk_sdhc_disable ( struct clk * clk )
{
u32 amlpmre1 , aprb ;
amlpmre1 = __raw_readl ( MXC_CRMAP_AMLPMRE1 ) ;
aprb = __raw_readl ( MXC_CRMAP_APRB ) ;
switch ( clk - > id ) {
case 0 :
amlpmre1 & = ~ ( 0x7 < < MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET ) ;
aprb & = ~ ( 0x1 < < MXC_CRMAP_APRB_SDHC1EN_OFFSET ) ;
break ;
case 1 :
amlpmre1 & = ~ ( 0x7 < < MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET ) ;
aprb & = ~ ( 0x1 < < MXC_CRMAP_APRB_SDHC2EN_OFFSET ) ;
break ;
}
__raw_writel ( amlpmre1 , MXC_CRMAP_AMLPMRE1 ) ;
__raw_writel ( aprb , MXC_CRMAP_APRB ) ;
}
static struct clk sdhc_clk [ ] = {
{
. id = 0 ,
. get_rate = clk_sdhc_get_rate ,
. enable = clk_sdhc_enable ,
. disable = clk_sdhc_disable ,
} , {
. id = 1 ,
. get_rate = clk_sdhc_get_rate ,
. enable = clk_sdhc_enable ,
. disable = clk_sdhc_disable ,
} ,
} ;
/* sdhc_clk stuff end */
/* wdog_clk stuff */
static struct clk wdog_clk [ ] = {
{
. id = 0 ,
. parent = & ipg_clk ,
. enable_reg = MXC_CRMAP_AMLPMRD ,
. enable_shift = MXC_CRMAP_AMLPMRD_MLPMD7_OFFSET ,
. enable = _clk_3bit_enable ,
. disable = _clk_3bit_disable ,
} , {
. id = 1 ,
. parent = & ipg_clk ,
. enable_reg = MXC_CRMAP_AMLPMRD ,
. enable_shift = MXC_CRMAP_AMLPMRD_MLPMD3_OFFSET ,
. enable = _clk_3bit_enable ,
. disable = _clk_3bit_disable ,
} ,
} ;
/* wdog_clk stuff end */
/* gpt_clk stuff */
static struct clk gpt_clk = {
. parent = & ipg_clk ,
. enable_reg = MXC_CRMAP_AMLPMRC ,
. enable_shift = MXC_CRMAP_AMLPMRC_MLPMC4_OFFSET ,
. enable = _clk_3bit_enable ,
. disable = _clk_3bit_disable ,
} ;
/* gpt_clk stuff end */
/* cspi_clk stuff */
static struct clk cspi_clk [ ] = {
{
. id = 0 ,
. parent = & ipg_clk ,
. enable_reg = MXC_CRMAP_AMLPMRE2 ,
. enable_shift = MXC_CRMAP_AMLPMRE2_MLPME0_OFFSET ,
. enable = _clk_3bit_enable ,
. disable = _clk_3bit_disable ,
} , {
. id = 1 ,
. parent = & ipg_clk ,
. enable_reg = MXC_CRMAP_AMLPMRE1 ,
. enable_shift = MXC_CRMAP_AMLPMRE1_MLPME6_OFFSET ,
. enable = _clk_3bit_enable ,
. disable = _clk_3bit_disable ,
} ,
} ;
/* cspi_clk stuff end */
# define _REGISTER_CLOCK(d, n, c) \
{ \
. dev_id = d , \
. con_id = n , \
. clk = & c , \
} ,
static struct clk_lookup lookups [ ] = {
_REGISTER_CLOCK ( " imx-uart.0 " , NULL , uart_clk [ 0 ] )
_REGISTER_CLOCK ( " imx-uart.1 " , NULL , uart_clk [ 1 ] )
_REGISTER_CLOCK ( " imx-uart.2 " , NULL , uart_clk [ 2 ] )
_REGISTER_CLOCK ( " mxc-mmc.0 " , NULL , sdhc_clk [ 0 ] )
_REGISTER_CLOCK ( " mxc-mmc.1 " , NULL , sdhc_clk [ 1 ] )
_REGISTER_CLOCK ( " mxc-wdt.0 " , NULL , wdog_clk [ 0 ] )
_REGISTER_CLOCK ( " spi_imx.0 " , NULL , cspi_clk [ 0 ] )
_REGISTER_CLOCK ( " spi_imx.1 " , NULL , cspi_clk [ 1 ] )
} ;
int __init mxc91231_clocks_init ( unsigned long fref )
{
void __iomem * gpt_base ;
ckih_rate = fref ;
usb_clk . parent = clk_usb_parent ( & usb_clk ) ;
sdhc_clk [ 0 ] . parent = clk_sdhc_parent ( & sdhc_clk [ 0 ] ) ;
sdhc_clk [ 1 ] . parent = clk_sdhc_parent ( & sdhc_clk [ 1 ] ) ;
2010-01-12 15:28:00 +03:00
clkdev_add_table ( lookups , ARRAY_SIZE ( lookups ) ) ;
2009-07-31 15:29:22 +04:00
gpt_base = MXC91231_IO_ADDRESS ( MXC91231_GPT1_BASE_ADDR ) ;
mxc_timer_init ( & gpt_clk , gpt_base , MXC91231_INT_GPT ) ;
return 0 ;
}