2005-04-16 15:20:36 -07:00
/*
* Carsten Langgaard , carstenl @ mips . com
* Copyright ( C ) 1999 , 2000 MIPS Technologies , Inc . All rights reserved .
*
* This program is free software ; you can distribute it and / or modify it
* under the terms of the GNU General Public License ( Version 2 ) as
* published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston MA 02111 - 1307 , USA .
*
* Setting up the clock on the MIPS boards .
*/
# include <linux/types.h>
2011-06-01 19:04:57 +01:00
# include <linux/i8253.h>
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
# include <linux/kernel_stat.h>
# include <linux/sched.h>
# include <linux/spinlock.h>
# include <linux/interrupt.h>
# include <linux/timex.h>
# include <linux/mc146818rtc.h>
# include <asm/mipsregs.h>
2006-04-05 09:45:45 +01:00
# include <asm/mipsmtregs.h>
2005-07-14 15:57:16 +00:00
# include <asm/hardirq.h>
# include <asm/irq.h>
2005-04-16 15:20:36 -07:00
# include <asm/div64.h>
2012-03-28 18:30:02 +01:00
# include <asm/setup.h>
2005-04-16 15:20:36 -07:00
# include <asm/time.h>
# include <asm/mc146818-time.h>
2005-07-14 15:57:16 +00:00
# include <asm/msc01_ic.h>
2012-12-07 03:51:04 +00:00
# include <asm/gic.h>
2005-04-16 15:20:36 -07:00
# include <asm/mips-boards/generic.h>
2005-07-14 15:57:16 +00:00
# include <asm/mips-boards/maltaint.h>
2005-04-16 15:20:36 -07:00
unsigned long cpu_khz ;
2005-07-14 15:57:16 +00:00
static int mips_cpu_timer_irq ;
2008-04-28 17:14:26 +01:00
static int mips_cpu_perf_irq ;
2007-06-20 22:27:10 +01:00
extern int cp0_perfcount_irq ;
2005-04-16 15:20:36 -07:00
2006-10-07 19:44:33 +01:00
static void mips_timer_dispatch ( void )
2005-04-16 15:20:36 -07:00
{
2006-10-07 19:44:33 +01:00
do_IRQ ( mips_cpu_timer_irq ) ;
2005-07-14 15:57:16 +00:00
}
2007-05-24 22:24:20 +01:00
static void mips_perf_dispatch ( void )
{
2008-04-28 17:14:26 +01:00
do_IRQ ( mips_cpu_perf_irq ) ;
2007-05-24 22:24:20 +01:00
}
2012-12-07 03:51:04 +00:00
static unsigned int freqround ( unsigned int freq , unsigned int amount )
{
freq + = amount ;
freq - = freq % ( amount * 2 ) ;
return freq ;
}
2005-04-16 15:20:36 -07:00
/*
2012-12-07 03:51:04 +00:00
* Estimate CPU and GIC frequencies .
2005-04-16 15:20:36 -07:00
*/
2012-12-07 03:51:04 +00:00
static void __init estimate_frequencies ( void )
2005-04-16 15:20:36 -07:00
{
2006-10-31 19:53:15 +00:00
unsigned long flags ;
2012-12-07 03:51:04 +00:00
unsigned int count , start ;
2013-04-10 16:28:36 -05:00
# ifdef CONFIG_IRQ_GIC
2012-12-07 03:51:04 +00:00
unsigned int giccount = 0 , gicstart = 0 ;
2013-04-10 16:28:36 -05:00
# endif
2005-04-16 15:20:36 -07:00
2012-11-21 18:34:03 -08:00
# if defined (CONFIG_KVM_GUEST) && defined (CONFIG_KVM_HOST_FREQ)
unsigned int prid = read_c0_prid ( ) & 0xffff00 ;
/*
* XXXKYMA : hardwire the CPU frequency to Host Freq / 4
*/
count = ( CONFIG_KVM_HOST_FREQ * 1000000 ) > > 3 ;
if ( ( prid ! = ( PRID_COMP_MIPS | PRID_IMP_20KC ) ) & &
( prid ! = ( PRID_COMP_MIPS | PRID_IMP_25KF ) ) )
count * = 2 ;
mips_hpt_frequency = count ;
return ;
# endif
2005-04-16 15:20:36 -07:00
local_irq_save ( flags ) ;
2012-12-07 03:51:04 +00:00
/* Start counter exactly on falling edge of update flag. */
2005-04-16 15:20:36 -07:00
while ( CMOS_READ ( RTC_REG_A ) & RTC_UIP ) ;
while ( ! ( CMOS_READ ( RTC_REG_A ) & RTC_UIP ) ) ;
2012-12-07 03:51:04 +00:00
/* Initialize counters. */
2006-10-31 18:33:09 +00:00
start = read_c0_count ( ) ;
2013-04-10 16:28:36 -05:00
# ifdef CONFIG_IRQ_GIC
2012-12-07 03:51:04 +00:00
if ( gic_present )
GICREAD ( GIC_REG ( SHARED , GIC_SH_COUNTER_31_00 ) , gicstart ) ;
2013-04-10 16:28:36 -05:00
# endif
2005-04-16 15:20:36 -07:00
2012-12-07 03:51:04 +00:00
/* Read counter exactly on falling edge of update flag. */
2005-04-16 15:20:36 -07:00
while ( CMOS_READ ( RTC_REG_A ) & RTC_UIP ) ;
while ( ! ( CMOS_READ ( RTC_REG_A ) & RTC_UIP ) ) ;
2012-12-07 03:51:04 +00:00
count = read_c0_count ( ) ;
2013-04-10 16:28:36 -05:00
# ifdef CONFIG_IRQ_GIC
2012-12-07 03:51:04 +00:00
if ( gic_present )
GICREAD ( GIC_REG ( SHARED , GIC_SH_COUNTER_31_00 ) , giccount ) ;
2013-04-10 16:28:36 -05:00
# endif
2005-04-16 15:20:36 -07:00
local_irq_restore ( flags ) ;
2012-12-07 03:51:04 +00:00
count - = start ;
mips_hpt_frequency = count ;
2013-04-10 16:28:36 -05:00
# ifdef CONFIG_IRQ_GIC
if ( gic_present ) {
giccount - = gicstart ;
2012-12-07 03:51:04 +00:00
gic_frequency = giccount ;
2013-04-10 16:28:36 -05:00
}
# endif
2005-04-16 15:20:36 -07:00
}
2009-08-14 15:47:31 +02:00
void read_persistent_clock ( struct timespec * ts )
2005-04-16 15:20:36 -07:00
{
2009-08-14 15:47:31 +02:00
ts - > tv_sec = mc146818_get_cmos_time ( ) ;
ts - > tv_nsec = 0 ;
2005-04-16 15:20:36 -07:00
}
2008-04-01 02:03:23 +04:00
static void __init plat_perf_setup ( void )
2007-05-24 22:24:20 +01:00
{
2007-01-08 01:27:40 +09:00
# ifdef MSC01E_INT_BASE
2005-07-14 15:57:16 +00:00
if ( cpu_has_veic ) {
2007-10-11 23:46:15 +01:00
set_vi_handler ( MSC01E_INT_PERFCTR , mips_perf_dispatch ) ;
2008-04-28 17:14:26 +01:00
mips_cpu_perf_irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR ;
2007-01-08 01:27:40 +09:00
} else
# endif
2007-06-20 22:27:10 +01:00
if ( cp0_perfcount_irq > = 0 ) {
if ( cpu_has_vint )
set_vi_handler ( cp0_perfcount_irq , mips_perf_dispatch ) ;
2008-04-28 17:14:26 +01:00
mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq ;
2007-05-24 22:24:20 +01:00
# ifdef CONFIG_SMP
2011-03-27 15:19:28 +02:00
irq_set_handler ( mips_cpu_perf_irq , handle_percpu_irq ) ;
2007-05-24 22:24:20 +01:00
# endif
2005-07-14 15:57:16 +00:00
}
2007-05-24 22:24:20 +01:00
}
2005-07-14 15:57:16 +00:00
2008-03-08 09:56:28 +00:00
unsigned int __cpuinit get_c0_compare_int ( void )
2007-05-24 22:24:20 +01:00
{
2007-05-24 22:46:25 +01:00
# ifdef MSC01E_INT_BASE
2007-05-24 22:24:20 +01:00
if ( cpu_has_veic ) {
2007-10-11 23:46:15 +01:00
set_vi_handler ( MSC01E_INT_CPUCTR , mips_timer_dispatch ) ;
2007-05-24 22:24:20 +01:00
mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR ;
2007-10-29 14:23:43 +00:00
} else
2007-05-24 22:46:25 +01:00
# endif
{
2007-05-24 22:24:20 +01:00
if ( cpu_has_vint )
2007-06-20 22:27:10 +01:00
set_vi_handler ( cp0_compare_irq , mips_timer_dispatch ) ;
mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq ;
2007-05-24 22:24:20 +01:00
}
2005-07-14 15:57:16 +00:00
2007-10-29 14:23:43 +00:00
return mips_cpu_timer_irq ;
}
void __init plat_time_init ( void )
{
2012-12-07 03:51:04 +00:00
unsigned int prid = read_c0_prid ( ) & 0xffff00 ;
unsigned int freq ;
2007-10-29 14:23:43 +00:00
2012-12-07 03:51:04 +00:00
estimate_frequencies ( ) ;
2007-10-29 14:23:43 +00:00
2012-12-07 03:51:04 +00:00
freq = mips_hpt_frequency ;
if ( ( prid ! = ( PRID_COMP_MIPS | PRID_IMP_20KC ) ) & &
( prid ! = ( PRID_COMP_MIPS | PRID_IMP_25KF ) ) )
freq * = 2 ;
freq = freqround ( freq , 5000 ) ;
2013-04-10 16:28:36 -05:00
printk ( " CPU frequency %d.%02d MHz \n " , freq / 1000000 ,
2012-12-07 03:51:04 +00:00
( freq % 1000000 ) * 100 / 1000000 ) ;
cpu_khz = freq / 1000 ;
2013-04-10 16:28:36 -05:00
mips_scroll_message ( ) ;
2007-10-29 14:23:43 +00:00
2012-12-07 03:51:04 +00:00
# ifdef CONFIG_I8253
/* Only Malta has a PIT. */
2007-10-29 14:23:43 +00:00
setup_pit_timer ( ) ;
2005-08-17 17:44:08 +00:00
# endif
2007-05-24 22:24:20 +01:00
2013-04-10 16:28:36 -05:00
# ifdef CONFIG_IRQ_GIC
if ( gic_present ) {
freq = freqround ( gic_frequency , 5000 ) ;
printk ( " GIC frequency %d.%02d MHz \n " , freq / 1000000 ,
( freq % 1000000 ) * 100 / 1000000 ) ;
# ifdef CONFIG_CSRC_GIC
gic_clocksource_init ( gic_frequency ) ;
# endif
}
# endif
2012-12-07 03:51:04 +00:00
2007-10-11 23:46:09 +01:00
plat_perf_setup ( ) ;
2005-04-16 15:20:36 -07:00
}