2008-07-09 14:54:02 -06:00
/*
* Copyright ( C ) 2007 , 2008 Freescale Semiconductor , Inc . All rights reserved .
*
* Author : John Rigby < jrigby @ freescale . com >
*
* Implements the clk api defined in include / linux / clk . h
*
* Original based on linux / arch / arm / mach - integrator / clock . c
*
* Copyright ( C ) 2004 ARM Limited .
* Written by Deep Blue Solutions Limited .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/string.h>
# include <linux/clk.h>
# include <linux/mutex.h>
# include <linux/io.h>
# include <linux/of_platform.h>
2009-06-17 00:30:22 -06:00
# include <asm/mpc5xxx.h>
2008-07-09 14:54:02 -06:00
# include <asm/clk_interface.h>
# undef CLK_DEBUG
static int clocks_initialized ;
# define CLK_HAS_RATE 0x1 /* has rate in MHz */
# define CLK_HAS_CTRL 0x2 /* has control reg and bit */
struct clk {
struct list_head node ;
char name [ 32 ] ;
int flags ;
struct device * dev ;
unsigned long rate ;
struct module * owner ;
void ( * calc ) ( struct clk * ) ;
struct clk * parent ;
int reg , bit ; /* CLK_HAS_CTRL */
int div_shift ; /* only used by generic_div_clk_calc */
} ;
static LIST_HEAD ( clocks ) ;
static DEFINE_MUTEX ( clocks_mutex ) ;
static struct clk * mpc5121_clk_get ( struct device * dev , const char * id )
{
struct clk * p , * clk = ERR_PTR ( - ENOENT ) ;
int dev_match = 0 ;
int id_match = 0 ;
2009-01-09 15:49:04 -07:00
if ( dev = = NULL | | id = = NULL )
2008-07-09 14:54:02 -06:00
return NULL ;
mutex_lock ( & clocks_mutex ) ;
list_for_each_entry ( p , & clocks , node ) {
2009-01-09 15:49:04 -07:00
if ( dev = = p - > dev )
2008-07-09 14:54:02 -06:00
dev_match + + ;
if ( strcmp ( id , p - > name ) = = 0 )
id_match + + ;
if ( ( dev_match | | id_match ) & & try_module_get ( p - > owner ) ) {
clk = p ;
break ;
}
}
mutex_unlock ( & clocks_mutex ) ;
return clk ;
}
# ifdef CLK_DEBUG
static void dump_clocks ( void )
{
struct clk * p ;
mutex_lock ( & clocks_mutex ) ;
printk ( KERN_INFO " CLOCKS: \n " ) ;
list_for_each_entry ( p , & clocks , node ) {
2009-06-17 06:30:31 -06:00
pr_info ( " %s=%ld " , p - > name , p - > rate ) ;
2008-07-09 14:54:02 -06:00
if ( p - > parent )
2009-06-17 06:30:31 -06:00
pr_cont ( " %s=%ld " , p - > parent - > name ,
2008-07-09 14:54:02 -06:00
p - > parent - > rate ) ;
if ( p - > flags & CLK_HAS_CTRL )
2009-06-17 06:30:31 -06:00
pr_cont ( " reg/bit=%d/%d " , p - > reg , p - > bit ) ;
pr_cont ( " \n " ) ;
2008-07-09 14:54:02 -06:00
}
mutex_unlock ( & clocks_mutex ) ;
}
# define DEBUG_CLK_DUMP() dump_clocks()
# else
# define DEBUG_CLK_DUMP()
# endif
static void mpc5121_clk_put ( struct clk * clk )
{
module_put ( clk - > owner ) ;
}
# define NRPSC 12
struct mpc512x_clockctl {
u32 spmr ; /* System PLL Mode Reg */
u32 sccr [ 2 ] ; /* System Clk Ctrl Reg 1 & 2 */
u32 scfr1 ; /* System Clk Freq Reg 1 */
u32 scfr2 ; /* System Clk Freq Reg 2 */
u32 reserved ;
u32 bcr ; /* Bread Crumb Reg */
u32 pccr [ NRPSC ] ; /* PSC Clk Ctrl Reg 0-11 */
u32 spccr ; /* SPDIF Clk Ctrl Reg */
u32 cccr ; /* CFM Clk Ctrl Reg */
u32 dccr ; /* DIU Clk Cnfg Reg */
} ;
struct mpc512x_clockctl __iomem * clockctl ;
static int mpc5121_clk_enable ( struct clk * clk )
{
unsigned int mask ;
if ( clk - > flags & CLK_HAS_CTRL ) {
mask = in_be32 ( & clockctl - > sccr [ clk - > reg ] ) ;
mask | = 1 < < clk - > bit ;
out_be32 ( & clockctl - > sccr [ clk - > reg ] , mask ) ;
}
return 0 ;
}
static void mpc5121_clk_disable ( struct clk * clk )
{
unsigned int mask ;
if ( clk - > flags & CLK_HAS_CTRL ) {
mask = in_be32 ( & clockctl - > sccr [ clk - > reg ] ) ;
mask & = ~ ( 1 < < clk - > bit ) ;
out_be32 ( & clockctl - > sccr [ clk - > reg ] , mask ) ;
}
}
static unsigned long mpc5121_clk_get_rate ( struct clk * clk )
{
if ( clk - > flags & CLK_HAS_RATE )
return clk - > rate ;
else
return 0 ;
}
static long mpc5121_clk_round_rate ( struct clk * clk , unsigned long rate )
{
return rate ;
}
static int mpc5121_clk_set_rate ( struct clk * clk , unsigned long rate )
{
return 0 ;
}
static int clk_register ( struct clk * clk )
{
mutex_lock ( & clocks_mutex ) ;
list_add ( & clk - > node , & clocks ) ;
mutex_unlock ( & clocks_mutex ) ;
return 0 ;
}
static unsigned long spmf_mult ( void )
{
/*
* Convert spmf to multiplier
*/
static int spmf_to_mult [ ] = {
68 , 1 , 12 , 16 ,
20 , 24 , 28 , 32 ,
36 , 40 , 44 , 48 ,
52 , 56 , 60 , 64
} ;
int spmf = ( clockctl - > spmr > > 24 ) & 0xf ;
return spmf_to_mult [ spmf ] ;
}
static unsigned long sysdiv_div_x_2 ( void )
{
/*
* Convert sysdiv to divisor x 2
* Some divisors have fractional parts so
* multiply by 2 then divide by this value
*/
static int sysdiv_to_div_x_2 [ ] = {
4 , 5 , 6 , 7 ,
8 , 9 , 10 , 14 ,
12 , 16 , 18 , 22 ,
20 , 24 , 26 , 30 ,
28 , 32 , 34 , 38 ,
36 , 40 , 42 , 46 ,
44 , 48 , 50 , 54 ,
52 , 56 , 58 , 62 ,
60 , 64 , 66 ,
} ;
int sysdiv = ( clockctl - > scfr2 > > 26 ) & 0x3f ;
return sysdiv_to_div_x_2 [ sysdiv ] ;
}
static unsigned long ref_to_sys ( unsigned long rate )
{
rate * = spmf_mult ( ) ;
rate * = 2 ;
rate / = sysdiv_div_x_2 ( ) ;
return rate ;
}
static unsigned long sys_to_ref ( unsigned long rate )
{
rate * = sysdiv_div_x_2 ( ) ;
rate / = 2 ;
rate / = spmf_mult ( ) ;
return rate ;
}
static long ips_to_ref ( unsigned long rate )
{
int ips_div = ( clockctl - > scfr1 > > 23 ) & 0x7 ;
rate * = ips_div ; /* csb_clk = ips_clk * ips_div */
rate * = 2 ; /* sys_clk = csb_clk * 2 */
return sys_to_ref ( rate ) ;
}
static unsigned long devtree_getfreq ( char * clockname )
{
struct device_node * np ;
const unsigned int * prop ;
unsigned int val = 0 ;
np = of_find_compatible_node ( NULL , NULL , " fsl,mpc5121-immr " ) ;
if ( np ) {
prop = of_get_property ( np , clockname , NULL ) ;
if ( prop )
val = * prop ;
of_node_put ( np ) ;
}
return val ;
}
static void ref_clk_calc ( struct clk * clk )
{
unsigned long rate ;
rate = devtree_getfreq ( " bus-frequency " ) ;
if ( rate = = 0 ) {
printk ( KERN_ERR " No bus-frequency in dev tree \n " ) ;
clk - > rate = 0 ;
return ;
}
clk - > rate = ips_to_ref ( rate ) ;
}
static struct clk ref_clk = {
. name = " ref_clk " ,
. calc = ref_clk_calc ,
} ;
static void sys_clk_calc ( struct clk * clk )
{
clk - > rate = ref_to_sys ( ref_clk . rate ) ;
}
static struct clk sys_clk = {
. name = " sys_clk " ,
. calc = sys_clk_calc ,
} ;
static void diu_clk_calc ( struct clk * clk )
{
int diudiv_x_2 = clockctl - > scfr1 & 0xff ;
unsigned long rate ;
rate = sys_clk . rate ;
rate * = 2 ;
rate / = diudiv_x_2 ;
clk - > rate = rate ;
}
static void half_clk_calc ( struct clk * clk )
{
clk - > rate = clk - > parent - > rate / 2 ;
}
static void generic_div_clk_calc ( struct clk * clk )
{
int div = ( clockctl - > scfr1 > > clk - > div_shift ) & 0x7 ;
clk - > rate = clk - > parent - > rate / div ;
}
static void unity_clk_calc ( struct clk * clk )
{
clk - > rate = clk - > parent - > rate ;
}
static struct clk csb_clk = {
. name = " csb_clk " ,
. calc = half_clk_calc ,
. parent = & sys_clk ,
} ;
static void e300_clk_calc ( struct clk * clk )
{
int spmf = ( clockctl - > spmr > > 16 ) & 0xf ;
int ratex2 = clk - > parent - > rate * spmf ;
clk - > rate = ratex2 / 2 ;
}
static struct clk e300_clk = {
. name = " e300_clk " ,
. calc = e300_clk_calc ,
. parent = & csb_clk ,
} ;
static struct clk ips_clk = {
. name = " ips_clk " ,
. calc = generic_div_clk_calc ,
. parent = & csb_clk ,
. div_shift = 23 ,
} ;
/*
* Clocks controlled by SCCR1 ( . reg = 0 )
*/
static struct clk lpc_clk = {
. name = " lpc_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 0 ,
. bit = 30 ,
. calc = generic_div_clk_calc ,
. parent = & ips_clk ,
. div_shift = 11 ,
} ;
static struct clk nfc_clk = {
. name = " nfc_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 0 ,
. bit = 29 ,
. calc = generic_div_clk_calc ,
. parent = & ips_clk ,
. div_shift = 8 ,
} ;
static struct clk pata_clk = {
. name = " pata_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 0 ,
. bit = 28 ,
. calc = unity_clk_calc ,
. parent = & ips_clk ,
} ;
/*
* PSC clocks ( bits 27 - 16 )
* are setup elsewhere
*/
static struct clk sata_clk = {
. name = " sata_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 0 ,
. bit = 14 ,
. calc = unity_clk_calc ,
. parent = & ips_clk ,
} ;
static struct clk fec_clk = {
. name = " fec_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 0 ,
. bit = 13 ,
. calc = unity_clk_calc ,
. parent = & ips_clk ,
} ;
static struct clk pci_clk = {
. name = " pci_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 0 ,
. bit = 11 ,
. calc = generic_div_clk_calc ,
. parent = & csb_clk ,
. div_shift = 20 ,
} ;
/*
* Clocks controlled by SCCR2 ( . reg = 1 )
*/
static struct clk diu_clk = {
. name = " diu_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 31 ,
. calc = diu_clk_calc ,
} ;
static struct clk axe_clk = {
. name = " axe_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 30 ,
. calc = unity_clk_calc ,
. parent = & csb_clk ,
} ;
static struct clk usb1_clk = {
. name = " usb1_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 28 ,
. calc = unity_clk_calc ,
. parent = & csb_clk ,
} ;
static struct clk usb2_clk = {
. name = " usb2_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 27 ,
. calc = unity_clk_calc ,
. parent = & csb_clk ,
} ;
static struct clk i2c_clk = {
. name = " i2c_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 26 ,
. calc = unity_clk_calc ,
. parent = & ips_clk ,
} ;
static struct clk mscan_clk = {
. name = " mscan_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 25 ,
. calc = unity_clk_calc ,
. parent = & ips_clk ,
} ;
static struct clk sdhc_clk = {
. name = " sdhc_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 24 ,
. calc = unity_clk_calc ,
. parent = & ips_clk ,
} ;
static struct clk mbx_bus_clk = {
. name = " mbx_bus_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 22 ,
. calc = half_clk_calc ,
. parent = & csb_clk ,
} ;
static struct clk mbx_clk = {
. name = " mbx_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 21 ,
. calc = unity_clk_calc ,
. parent = & csb_clk ,
} ;
static struct clk mbx_3d_clk = {
. name = " mbx_3d_clk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 20 ,
. calc = generic_div_clk_calc ,
. parent = & mbx_bus_clk ,
. div_shift = 14 ,
} ;
static void psc_mclk_in_calc ( struct clk * clk )
{
clk - > rate = devtree_getfreq ( " psc_mclk_in " ) ;
if ( ! clk - > rate )
clk - > rate = 25000000 ;
}
static struct clk psc_mclk_in = {
. name = " psc_mclk_in " ,
. calc = psc_mclk_in_calc ,
} ;
static struct clk spdif_txclk = {
. name = " spdif_txclk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 23 ,
} ;
static struct clk spdif_rxclk = {
. name = " spdif_rxclk " ,
. flags = CLK_HAS_CTRL ,
. reg = 1 ,
. bit = 23 ,
} ;
static void ac97_clk_calc ( struct clk * clk )
{
/* ac97 bit clock is always 24.567 MHz */
clk - > rate = 24567000 ;
}
static struct clk ac97_clk = {
. name = " ac97_clk_in " ,
. calc = ac97_clk_calc ,
} ;
struct clk * rate_clks [ ] = {
& ref_clk ,
& sys_clk ,
& diu_clk ,
& csb_clk ,
& e300_clk ,
& ips_clk ,
& fec_clk ,
& sata_clk ,
& pata_clk ,
& nfc_clk ,
& lpc_clk ,
& mbx_bus_clk ,
& mbx_clk ,
& mbx_3d_clk ,
& axe_clk ,
& usb1_clk ,
& usb2_clk ,
& i2c_clk ,
& mscan_clk ,
& sdhc_clk ,
& pci_clk ,
& psc_mclk_in ,
& spdif_txclk ,
& spdif_rxclk ,
& ac97_clk ,
NULL
} ;
static void rate_clk_init ( struct clk * clk )
{
if ( clk - > calc ) {
clk - > calc ( clk ) ;
clk - > flags | = CLK_HAS_RATE ;
clk_register ( clk ) ;
} else {
printk ( KERN_WARNING
" Could not initialize clk %s without a calc routine \n " ,
clk - > name ) ;
}
}
static void rate_clks_init ( void )
{
struct clk * * cpp , * clk ;
cpp = rate_clks ;
while ( ( clk = * cpp + + ) )
rate_clk_init ( clk ) ;
}
/*
* There are two clk enable registers with 32 enable bits each
* psc clocks and device clocks are all stored in dev_clks
*/
struct clk dev_clks [ 2 ] [ 32 ] ;
/*
* Given a psc number return the dev_clk
* associated with it
*/
static struct clk * psc_dev_clk ( int pscnum )
{
int reg , bit ;
struct clk * clk ;
reg = 0 ;
bit = 27 - pscnum ;
clk = & dev_clks [ reg ] [ bit ] ;
clk - > reg = 0 ;
clk - > bit = bit ;
return clk ;
}
/*
* PSC clock rate calculation
*/
static void psc_calc_rate ( struct clk * clk , int pscnum , struct device_node * np )
{
unsigned long mclk_src = sys_clk . rate ;
unsigned long mclk_div ;
/*
* Can only change value of mclk divider
* when the divider is disabled .
*
* Zero is not a valid divider so minimum
* divider is 1
*
* disable / set divider / enable
*/
out_be32 ( & clockctl - > pccr [ pscnum ] , 0 ) ;
out_be32 ( & clockctl - > pccr [ pscnum ] , 0x00020000 ) ;
out_be32 ( & clockctl - > pccr [ pscnum ] , 0x00030000 ) ;
if ( clockctl - > pccr [ pscnum ] & 0x80 ) {
clk - > rate = spdif_rxclk . rate ;
return ;
}
switch ( ( clockctl - > pccr [ pscnum ] > > 14 ) & 0x3 ) {
case 0 :
mclk_src = sys_clk . rate ;
break ;
case 1 :
mclk_src = ref_clk . rate ;
break ;
case 2 :
mclk_src = psc_mclk_in . rate ;
break ;
case 3 :
mclk_src = spdif_txclk . rate ;
break ;
}
mclk_div = ( ( clockctl - > pccr [ pscnum ] > > 17 ) & 0x7fff ) + 1 ;
clk - > rate = mclk_src / mclk_div ;
}
/*
* Find all psc nodes in device tree and assign a clock
* with name " psc%d_mclk " and dev pointing at the device
* returned from of_find_device_by_node
*/
static void psc_clks_init ( void )
{
struct device_node * np ;
const u32 * cell_index ;
struct of_device * ofdev ;
for_each_compatible_node ( np , NULL , " fsl,mpc5121-psc " ) {
cell_index = of_get_property ( np , " cell-index " , NULL ) ;
if ( cell_index ) {
int pscnum = * cell_index ;
struct clk * clk = psc_dev_clk ( pscnum ) ;
clk - > flags = CLK_HAS_RATE | CLK_HAS_CTRL ;
ofdev = of_find_device_by_node ( np ) ;
clk - > dev = & ofdev - > dev ;
/*
* AC97 is special rate clock does
* not go through normal path
*/
if ( strcmp ( " ac97 " , np - > name ) = = 0 )
clk - > rate = ac97_clk . rate ;
else
psc_calc_rate ( clk , pscnum , np ) ;
sprintf ( clk - > name , " psc%d_mclk " , pscnum ) ;
clk_register ( clk ) ;
clk_enable ( clk ) ;
}
}
}
static struct clk_interface mpc5121_clk_functions = {
. clk_get = mpc5121_clk_get ,
. clk_enable = mpc5121_clk_enable ,
. clk_disable = mpc5121_clk_disable ,
. clk_get_rate = mpc5121_clk_get_rate ,
. clk_put = mpc5121_clk_put ,
. clk_round_rate = mpc5121_clk_round_rate ,
. clk_set_rate = mpc5121_clk_set_rate ,
. clk_set_parent = NULL ,
. clk_get_parent = NULL ,
} ;
static int
mpc5121_clk_init ( void )
{
struct device_node * np ;
np = of_find_compatible_node ( NULL , NULL , " fsl,mpc5121-clock " ) ;
if ( np ) {
clockctl = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
}
if ( ! clockctl ) {
printk ( KERN_ERR " Could not map clock control registers \n " ) ;
return 0 ;
}
rate_clks_init ( ) ;
psc_clks_init ( ) ;
/* leave clockctl mapped forever */
/*iounmap(clockctl); */
DEBUG_CLK_DUMP ( ) ;
clocks_initialized + + ;
clk_functions = mpc5121_clk_functions ;
return 0 ;
}
arch_initcall ( mpc5121_clk_init ) ;