2006-11-27 14:16:27 -07:00
/*
*
* Utility functions for the Freescale MPC52xx .
*
* Copyright ( C ) 2006 Sylvain Munaut < tnt @ 246 tNt . com >
*
* 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 .
*
*/
# undef DEBUG
# include <linux/kernel.h>
2008-01-24 22:25:31 -07:00
# include <linux/spinlock.h>
2007-10-10 09:52:00 -06:00
# include <linux/of_platform.h>
2006-11-27 14:16:27 -07:00
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/mpc52xx.h>
2008-01-24 22:25:31 -07:00
/* MPC5200 device tree match tables */
static struct of_device_id mpc52xx_xlb_ids [ ] __initdata = {
{ . compatible = " fsl,mpc5200-xlb " , } ,
{ . compatible = " mpc5200-xlb " , } ,
{ }
} ;
static struct of_device_id mpc52xx_bus_ids [ ] __initdata = {
{ . compatible = " fsl,mpc5200-immr " , } ,
{ . compatible = " fsl,mpc5200b-immr " , } ,
{ . compatible = " fsl,lpb " , } ,
/* depreciated matches; shouldn't be used in new device trees */
{ . type = " builtin " , . compatible = " mpc5200 " , } , /* efika */
{ . type = " soc " , . compatible = " mpc5200 " , } , /* lite5200 */
{ }
} ;
2007-10-19 04:44:33 +10:00
/*
* This variable is mapped in mpc52xx_map_wdt ( ) and used in mpc52xx_restart ( ) .
* Permanent mapping is required because mpc52xx_restart ( ) can be called
* from interrupt context while node mapping ( which calls ioremap ( ) )
* cannot be used at such point .
*/
2008-01-24 22:25:31 -07:00
static spinlock_t mpc52xx_lock = SPIN_LOCK_UNLOCKED ;
static struct mpc52xx_gpt __iomem * mpc52xx_wdt ;
static struct mpc52xx_cdm __iomem * mpc52xx_cdm ;
2006-11-27 14:16:27 -07:00
/**
* mpc52xx_find_ipb_freq - Find the IPB bus frequency for a device
* @ node : device node
*
* Returns IPB bus frequency , or 0 if the bus frequency cannot be found .
*/
unsigned int
mpc52xx_find_ipb_freq ( struct device_node * node )
{
struct device_node * np ;
const unsigned int * p_ipb_freq = NULL ;
of_node_get ( node ) ;
while ( node ) {
2007-04-03 22:26:41 +10:00
p_ipb_freq = of_get_property ( node , " bus-frequency " , NULL ) ;
2006-11-27 14:16:27 -07:00
if ( p_ipb_freq )
break ;
np = of_get_parent ( node ) ;
of_node_put ( node ) ;
node = np ;
}
if ( node )
of_node_put ( node ) ;
return p_ipb_freq ? * p_ipb_freq : 0 ;
}
2006-12-04 17:29:12 -07:00
EXPORT_SYMBOL ( mpc52xx_find_ipb_freq ) ;
2006-11-27 14:16:27 -07:00
2007-10-09 14:45:28 -06:00
/*
* Configure the XLB arbiter settings to match what Linux expects .
*/
2006-11-27 14:16:27 -07:00
void __init
2007-10-09 14:45:28 -06:00
mpc5200_setup_xlb_arbiter ( void )
2006-11-27 14:16:27 -07:00
{
2008-01-18 09:30:37 -07:00
struct device_node * np ;
2006-11-27 14:16:27 -07:00
struct mpc52xx_xlb __iomem * xlb ;
2008-01-24 22:25:31 -07:00
np = of_find_matching_node ( NULL , mpc52xx_xlb_ids ) ;
2008-01-18 09:30:37 -07:00
xlb = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
2007-10-09 14:45:28 -06:00
if ( ! xlb ) {
2006-11-27 14:16:27 -07:00
printk ( KERN_ERR __FILE__ " : "
2008-10-08 11:36:21 -06:00
" Error mapping XLB in mpc52xx_setup_cpu(). "
2006-11-27 14:16:27 -07:00
" Expect some abnormal behavior \n " ) ;
2007-10-09 14:45:28 -06:00
return ;
2006-11-27 14:16:27 -07:00
}
/* Configure the XLB Arbiter priorities */
out_be32 ( & xlb - > master_pri_enable , 0xff ) ;
out_be32 ( & xlb - > master_priority , 0x11111111 ) ;
2008-10-15 11:09:59 -06:00
/*
* Disable XLB pipelining
2007-10-09 14:45:28 -06:00
* ( cfr errate 292. We could do this only just before ATA PIO
* transaction and re - enable it afterwards . . . )
2008-10-15 11:09:59 -06:00
* Not needed on MPC5200B .
2007-10-09 14:45:28 -06:00
*/
2008-10-15 11:09:59 -06:00
if ( ( mfspr ( SPRN_SVR ) & MPC5200_SVR_MASK ) = = MPC5200_SVR )
out_be32 ( & xlb - > config , in_be32 ( & xlb - > config ) | MPC52xx_XLB_CFG_PLDIS ) ;
2006-11-27 14:16:27 -07:00
2007-10-09 14:45:28 -06:00
iounmap ( xlb ) ;
2006-11-27 14:16:27 -07:00
}
2008-01-24 22:25:31 -07:00
/**
* mpc52xx_declare_of_platform_devices : register internal devices and children
* of the localplus bus to the of_platform
* bus .
*/
2007-01-02 23:29:53 +01:00
void __init
2006-11-27 14:16:27 -07:00
mpc52xx_declare_of_platform_devices ( void )
{
/* Find every child of the SOC node and add it to of_platform */
2007-11-10 04:11:49 +11:00
if ( of_platform_bus_probe ( NULL , mpc52xx_bus_ids , NULL ) )
2007-01-02 23:29:53 +01:00
printk ( KERN_ERR __FILE__ " : "
" Error while probing of_platform bus \n " ) ;
2006-11-27 14:16:27 -07:00
}
2008-01-24 22:25:31 -07:00
/*
2008-01-24 22:25:31 -07:00
* match tables used by mpc52xx_map_common_devices ( )
2008-01-24 22:25:31 -07:00
*/
static struct of_device_id mpc52xx_gpt_ids [ ] __initdata = {
{ . compatible = " fsl,mpc5200-gpt " , } ,
{ . compatible = " mpc5200-gpt " , } , /* old */
{ }
} ;
2008-01-24 22:25:31 -07:00
static struct of_device_id mpc52xx_cdm_ids [ ] __initdata = {
{ . compatible = " fsl,mpc5200-cdm " , } ,
{ . compatible = " mpc5200-cdm " , } , /* old */
{ }
} ;
2008-01-24 22:25:31 -07:00
2008-01-24 22:25:31 -07:00
/**
* mpc52xx_map_common_devices : iomap devices required by common code
*/
2007-10-19 04:44:33 +10:00
void __init
2008-01-24 22:25:31 -07:00
mpc52xx_map_common_devices ( void )
2007-10-19 04:44:33 +10:00
{
struct device_node * np ;
2008-01-24 22:25:31 -07:00
2007-10-19 04:44:33 +10:00
/* mpc52xx_wdt is mapped here and used in mpc52xx_restart,
* possibly from a interrupt context . wdt is only implement
* on a gpt0 , so check has - wdt property before mapping .
*/
2008-01-24 22:25:31 -07:00
for_each_matching_node ( np , mpc52xx_gpt_ids ) {
if ( of_get_property ( np , " fsl,has-wdt " , NULL ) | |
of_get_property ( np , " has-wdt " , NULL ) ) {
2008-01-18 09:30:37 -07:00
mpc52xx_wdt = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
2008-01-24 22:25:31 -07:00
break ;
2007-10-19 04:44:33 +10:00
}
}
2008-01-24 22:25:31 -07:00
/* Clock Distribution Module, used by PSC clock setting function */
np = of_find_matching_node ( NULL , mpc52xx_cdm_ids ) ;
mpc52xx_cdm = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
2007-10-19 04:44:33 +10:00
}
2008-01-24 22:25:31 -07:00
/**
* mpc52xx_set_psc_clkdiv : Set clock divider in the CDM for PSC ports
*
* @ psc_id : id of psc port ; must be 1 , 2 , 3 or 6
* @ clkdiv : clock divider value to put into CDM PSC register .
*/
int mpc52xx_set_psc_clkdiv ( int psc_id , int clkdiv )
{
unsigned long flags ;
u16 __iomem * reg ;
u32 val ;
u32 mask ;
u32 mclken_div ;
if ( ! mpc52xx_cdm )
return - ENODEV ;
mclken_div = 0x8000 | ( clkdiv & 0x1FF ) ;
switch ( psc_id ) {
case 1 : reg = & mpc52xx_cdm - > mclken_div_psc1 ; mask = 0x20 ; break ;
case 2 : reg = & mpc52xx_cdm - > mclken_div_psc2 ; mask = 0x40 ; break ;
case 3 : reg = & mpc52xx_cdm - > mclken_div_psc3 ; mask = 0x80 ; break ;
case 6 : reg = & mpc52xx_cdm - > mclken_div_psc6 ; mask = 0x10 ; break ;
default :
return - ENODEV ;
}
/* Set the rate and enable the clock */
spin_lock_irqsave ( & mpc52xx_lock , flags ) ;
out_be16 ( reg , mclken_div ) ;
val = in_be32 ( & mpc52xx_cdm - > clk_enables ) ;
out_be32 ( & mpc52xx_cdm - > clk_enables , val | mask ) ;
spin_unlock_irqrestore ( & mpc52xx_lock , flags ) ;
return 0 ;
}
2008-02-23 22:51:28 -07:00
EXPORT_SYMBOL ( mpc52xx_set_psc_clkdiv ) ;
2008-01-24 22:25:31 -07:00
/**
* mpc52xx_restart : ppc_md - > restart hook for mpc5200 using the watchdog timer
*/
2007-10-19 04:44:33 +10:00
void
mpc52xx_restart ( char * cmd )
{
local_irq_disable ( ) ;
/* Turn on the watchdog and wait for it to expire.
* It effectively does a reset . */
if ( mpc52xx_wdt ) {
out_be32 ( & mpc52xx_wdt - > mode , 0x00000000 ) ;
out_be32 ( & mpc52xx_wdt - > count , 0x000000ff ) ;
out_be32 ( & mpc52xx_wdt - > mode , 0x00009004 ) ;
} else
2008-10-08 11:36:21 -06:00
printk ( KERN_ERR __FILE__ " : "
" mpc52xx_restart: Can't access wdt. "
2007-10-19 04:44:33 +10:00
" Restart impossible, system halted. \n " ) ;
while ( 1 ) ;
}