2007-01-24 22:41:42 +03:00
/*
* Copyright ( C ) 1995 Linus Torvalds
* Adapted from ' alpha ' version by Gary Thomas
* Modified by Cort Dougan ( cort @ cs . nmt . edu )
* Modified for MBX using prep / chrp / pmac functions by Dan ( dmalek @ jlc . net )
* Further modified for generic 8 xx by Dan .
*/
/*
* bootup setup stuff . .
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/time.h>
# include <linux/rtc.h>
2008-01-24 16:19:01 +01:00
# include <linux/fsl_devices.h>
2007-01-24 22:41:42 +03:00
# include <asm/io.h>
# include <asm/mpc8xx.h>
# include <asm/8xx_immap.h>
# include <asm/prom.h>
# include <asm/fs_pd.h>
# include <mm/mmu_decl.h>
2007-09-14 14:22:36 -05:00
# include <sysdev/mpc8xx_pic.h>
2008-01-24 16:18:32 +01:00
# include "mpc8xx.h"
2007-07-09 11:37:35 -07:00
struct mpc8xx_pcmcia_ops m8xx_pcmcia_ops ;
2007-01-24 22:41:42 +03:00
extern int cpm_pic_init ( void ) ;
extern int cpm_get_irq ( void ) ;
/* A place holder for time base interrupts, if they are ever enabled. */
2007-09-14 14:22:36 -05:00
static irqreturn_t timebase_interrupt ( int irq , void * dev )
2007-01-24 22:41:42 +03:00
{
printk ( " timebase_interrupt() \n " ) ;
return IRQ_HANDLED ;
}
static struct irqaction tbint_irqaction = {
. handler = timebase_interrupt ,
. name = " tbint " ,
} ;
/* per-board overridable init_internal_rtc() function. */
void __init __attribute__ ( ( weak ) )
init_internal_rtc ( void )
{
2007-09-14 14:22:36 -05:00
sit8xx_t __iomem * sys_tmr = immr_map ( im_sit ) ;
2007-01-24 22:41:42 +03:00
/* Disable the RTC one second and alarm interrupts. */
clrbits16 ( & sys_tmr - > sit_rtcsc , ( RTCSC_SIE | RTCSC_ALE ) ) ;
/* Enable the RTC */
setbits16 ( & sys_tmr - > sit_rtcsc , ( RTCSC_RTF | RTCSC_RTE ) ) ;
immr_unmap ( sys_tmr ) ;
}
static int __init get_freq ( char * name , unsigned long * val )
{
2007-08-21 02:36:19 +10:00
struct device_node * cpu ;
const unsigned int * fp ;
int found = 0 ;
2007-01-24 22:41:42 +03:00
2007-08-21 02:36:19 +10:00
/* The cpu node should have timebase and clock frequency properties */
cpu = of_find_node_by_type ( NULL , " cpu " ) ;
2007-01-24 22:41:42 +03:00
2007-08-21 02:36:19 +10:00
if ( cpu ) {
fp = of_get_property ( cpu , name , NULL ) ;
if ( fp ) {
found = 1 ;
* val = * fp ;
}
2007-01-24 22:41:42 +03:00
2007-08-21 02:36:19 +10:00
of_node_put ( cpu ) ;
}
2007-01-24 22:41:42 +03:00
2007-08-21 02:36:19 +10:00
return found ;
2007-01-24 22:41:42 +03:00
}
/* The decrementer counts at the system (internal) clock frequency divided by
* sixteen , or external oscillator divided by four . We force the processor
* to use system clock divided by sixteen .
*/
void __init mpc8xx_calibrate_decr ( void )
{
struct device_node * cpu ;
2007-09-14 14:22:36 -05:00
cark8xx_t __iomem * clk_r1 ;
car8xx_t __iomem * clk_r2 ;
sitk8xx_t __iomem * sys_tmr1 ;
sit8xx_t __iomem * sys_tmr2 ;
2007-01-24 22:41:42 +03:00
int irq , virq ;
2007-09-14 14:22:36 -05:00
clk_r1 = immr_map ( im_clkrstk ) ;
2007-01-24 22:41:42 +03:00
/* Unlock the SCCR. */
out_be32 ( & clk_r1 - > cark_sccrk , ~ KAPWR_KEY ) ;
out_be32 ( & clk_r1 - > cark_sccrk , KAPWR_KEY ) ;
immr_unmap ( clk_r1 ) ;
/* Force all 8xx processors to use divide by 16 processor clock. */
2007-09-14 14:22:36 -05:00
clk_r2 = immr_map ( im_clkrst ) ;
2007-01-24 22:41:42 +03:00
setbits32 ( & clk_r2 - > car_sccr , 0x02000000 ) ;
immr_unmap ( clk_r2 ) ;
/* Processor frequency is MHz.
*/
2007-08-21 02:36:19 +10:00
ppc_proc_freq = 50000000 ;
if ( ! get_freq ( " clock-frequency " , & ppc_proc_freq ) )
2007-11-20 12:47:55 +11:00
printk ( KERN_ERR " WARNING: Estimating processor frequency "
2007-08-21 02:36:19 +10:00
" (not found) \n " ) ;
2008-02-22 06:45:08 +11:00
ppc_tb_freq = ppc_proc_freq / 16 ;
2007-08-21 02:36:19 +10:00
printk ( " Decrementer Frequency = 0x%lx \n " , ppc_tb_freq ) ;
2007-01-24 22:41:42 +03:00
/* Perform some more timer/timebase initialization. This used
* to be done elsewhere , but other changes caused it to get
* called more than once . . . . that is a bad thing .
*
* First , unlock all of the registers we are going to modify .
* To protect them from corruption during power down , registers
* that are maintained by keep alive power are " locked " . To
* modify these registers we have to write the key value to
* the key location associated with the register .
* Some boards power up with these unlocked , while others
* are locked . Writing anything ( including the unlock code ? )
* to the unlocked registers will lock them again . So , here
* we guarantee the registers are locked , then we unlock them
* for our use .
*/
2007-09-14 14:22:36 -05:00
sys_tmr1 = immr_map ( im_sitk ) ;
2007-01-24 22:41:42 +03:00
out_be32 ( & sys_tmr1 - > sitk_tbscrk , ~ KAPWR_KEY ) ;
out_be32 ( & sys_tmr1 - > sitk_rtcsck , ~ KAPWR_KEY ) ;
out_be32 ( & sys_tmr1 - > sitk_tbk , ~ KAPWR_KEY ) ;
out_be32 ( & sys_tmr1 - > sitk_tbscrk , KAPWR_KEY ) ;
out_be32 ( & sys_tmr1 - > sitk_rtcsck , KAPWR_KEY ) ;
out_be32 ( & sys_tmr1 - > sitk_tbk , KAPWR_KEY ) ;
immr_unmap ( sys_tmr1 ) ;
init_internal_rtc ( ) ;
/* Enabling the decrementer also enables the timebase interrupts
* ( or from the other point of view , to get decrementer interrupts
* we have to enable the timebase ) . The decrementer interrupt
* is wired into the vector table , nothing to do here for that .
*/
2007-08-21 02:36:19 +10:00
cpu = of_find_node_by_type ( NULL , " cpu " ) ;
virq = irq_of_parse_and_map ( cpu , 0 ) ;
2007-01-24 22:41:42 +03:00
irq = irq_map [ virq ] . hwirq ;
2007-09-14 14:22:36 -05:00
sys_tmr2 = immr_map ( im_sit ) ;
2007-01-24 22:41:42 +03:00
out_be16 ( & sys_tmr2 - > sit_tbscr , ( ( 1 < < ( 7 - ( irq / 2 ) ) ) < < 8 ) |
( TBSCR_TBF | TBSCR_TBE ) ) ;
immr_unmap ( sys_tmr2 ) ;
if ( setup_irq ( virq , & tbint_irqaction ) )
panic ( " Could not allocate timer IRQ! " ) ;
}
/* The RTC on the MPC8xx is an internal register.
* We want to protect this during power down , so we need to unlock ,
* modify , and re - lock .
*/
int mpc8xx_set_rtc_time ( struct rtc_time * tm )
{
2007-09-14 14:22:36 -05:00
sitk8xx_t __iomem * sys_tmr1 ;
sit8xx_t __iomem * sys_tmr2 ;
2007-01-24 22:41:42 +03:00
int time ;
2007-09-14 14:22:36 -05:00
sys_tmr1 = immr_map ( im_sitk ) ;
sys_tmr2 = immr_map ( im_sit ) ;
2007-01-24 22:41:42 +03:00
time = mktime ( tm - > tm_year + 1900 , tm - > tm_mon + 1 , tm - > tm_mday ,
2007-08-21 02:36:19 +10:00
tm - > tm_hour , tm - > tm_min , tm - > tm_sec ) ;
2007-01-24 22:41:42 +03:00
out_be32 ( & sys_tmr1 - > sitk_rtck , KAPWR_KEY ) ;
out_be32 ( & sys_tmr2 - > sit_rtc , time ) ;
out_be32 ( & sys_tmr1 - > sitk_rtck , ~ KAPWR_KEY ) ;
immr_unmap ( sys_tmr2 ) ;
immr_unmap ( sys_tmr1 ) ;
return 0 ;
}
void mpc8xx_get_rtc_time ( struct rtc_time * tm )
{
unsigned long data ;
2007-09-14 14:22:36 -05:00
sit8xx_t __iomem * sys_tmr = immr_map ( im_sit ) ;
2007-01-24 22:41:42 +03:00
/* Get time from the RTC. */
data = in_be32 ( & sys_tmr - > sit_rtc ) ;
to_tm ( data , tm ) ;
2007-08-21 02:36:19 +10:00
tm - > tm_year - = 1900 ;
tm - > tm_mon - = 1 ;
2007-01-24 22:41:42 +03:00
immr_unmap ( sys_tmr ) ;
return ;
}
void mpc8xx_restart ( char * cmd )
{
2007-09-14 14:22:36 -05:00
car8xx_t __iomem * clk_r = immr_map ( im_clkrst ) ;
2007-01-24 22:41:42 +03:00
local_irq_disable ( ) ;
setbits32 ( & clk_r - > car_plprcr , 0x00000080 ) ;
/* Clear the ME bit in MSR to cause checkstop on machine check
*/
mtmsr ( mfmsr ( ) & ~ 0x1000 ) ;
2007-09-14 14:22:36 -05:00
in_8 ( & clk_r - > res [ 0 ] ) ;
panic ( " Restart failed \n " ) ;
2007-01-24 22:41:42 +03:00
}
static void cpm_cascade ( unsigned int irq , struct irq_desc * desc )
{
int cascade_irq ;
if ( ( cascade_irq = cpm_get_irq ( ) ) > = 0 ) {
struct irq_desc * cdesc = irq_desc + cascade_irq ;
generic_handle_irq ( cascade_irq ) ;
cdesc - > chip - > eoi ( cascade_irq ) ;
}
desc - > chip - > eoi ( irq ) ;
}
2008-01-24 16:17:32 +01:00
/* Initialize the internal interrupt controllers. The number of
2007-01-24 22:41:42 +03:00
* interrupts supported can vary with the processor type , and the
* 82 xx family can have up to 64.
* External interrupts can be either edge or level triggered , and
* need to be initialized by the appropriate driver .
*/
2008-01-24 16:17:32 +01:00
void __init mpc8xx_pics_init ( void )
2007-01-24 22:41:42 +03:00
{
int irq ;
if ( mpc8xx_pic_init ( ) ) {
2007-08-21 02:36:19 +10:00
printk ( KERN_ERR " Failed interrupt 8xx controller initialization \n " ) ;
2007-01-24 22:41:42 +03:00
return ;
}
irq = cpm_pic_init ( ) ;
if ( irq ! = NO_IRQ )
set_irq_chained_handler ( irq , cpm_cascade ) ;
}