2008-03-26 11:11:55 +03:00
/* time.c: UltraSparc timer and TOD clock support.
2005-04-17 02:20:36 +04:00
*
2008-03-26 11:11:55 +03:00
* Copyright ( C ) 1997 , 2008 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1998 Eddie C . Dost ( ecd @ skynet . be )
*
* Based largely on code which is :
*
* Copyright ( C ) 1996 Thomas K . Dyas ( tdyas @ eden . rutgers . edu )
*/
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/sched.h>
2008-05-20 21:16:50 +04:00
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/param.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/time.h>
# include <linux/timex.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/mc146818rtc.h>
# include <linux/delay.h>
# include <linux/profile.h>
# include <linux/bcd.h>
# include <linux/jiffies.h>
# include <linux/cpufreq.h>
# include <linux/percpu.h>
2006-03-02 04:32:46 +03:00
# include <linux/miscdevice.h>
# include <linux/rtc.h>
2008-08-29 08:06:27 +04:00
# include <linux/rtc/m48t59.h>
2007-02-22 17:24:10 +03:00
# include <linux/kernel_stat.h>
2007-03-06 02:28:37 +03:00
# include <linux/clockchips.h>
# include <linux/clocksource.h>
2008-08-08 02:33:36 +04:00
# include <linux/of_device.h>
2008-08-29 08:06:27 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/oplib.h>
# include <asm/timer.h>
# include <asm/irq.h>
# include <asm/io.h>
2006-06-30 02:28:05 +04:00
# include <asm/prom.h>
2005-04-17 02:20:36 +04:00
# include <asm/starfire.h>
# include <asm/smp.h>
# include <asm/sections.h>
# include <asm/cpudata.h>
2006-03-02 04:32:46 +03:00
# include <asm/uaccess.h>
2006-10-09 14:51:14 +04:00
# include <asm/irq_regs.h>
2005-04-17 02:20:36 +04:00
2008-03-26 11:11:55 +03:00
# include "entry.h"
2005-04-17 02:20:36 +04:00
DEFINE_SPINLOCK ( rtc_lock ) ;
# define TICK_PRIV_BIT (1UL << 63)
2007-03-06 02:28:37 +03:00
# define TICKCMP_IRQ_BIT (1UL << 63)
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SMP
unsigned long profile_pc ( struct pt_regs * regs )
{
unsigned long pc = instruction_pointer ( regs ) ;
if ( in_lock_functions ( pc ) )
return regs - > u_regs [ UREG_RETPC ] ;
return pc ;
}
EXPORT_SYMBOL ( profile_pc ) ;
# endif
static void tick_disable_protection ( void )
{
/* Set things up so user can access tick register for profiling
* purposes . Also workaround BB_ERRATA_1 by doing a dummy
* read back of % tick after writing it .
*/
__asm__ __volatile__ (
" ba,pt %%xcc, 1f \n "
" nop \n "
" .align 64 \n "
" 1: rd %%tick, %%g2 \n "
" add %%g2, 6, %%g2 \n "
" andn %%g2, %0, %%g2 \n "
" wrpr %%g2, 0, %%tick \n "
" rdpr %%tick, %%g0 "
: /* no outputs */
: " r " ( TICK_PRIV_BIT )
: " g2 " ) ;
}
2007-03-06 02:28:37 +03:00
static void tick_disable_irq ( void )
2005-04-17 02:20:36 +04:00
{
__asm__ __volatile__ (
" ba,pt %%xcc, 1f \n "
2007-03-06 02:28:37 +03:00
" nop \n "
2005-04-17 02:20:36 +04:00
" .align 64 \n "
2007-03-06 02:28:37 +03:00
" 1: wr %0, 0x0, %%tick_cmpr \n "
2005-04-17 02:20:36 +04:00
" rd %%tick_cmpr, %%g0 "
: /* no outputs */
2007-03-06 02:28:37 +03:00
: " r " ( TICKCMP_IRQ_BIT ) ) ;
}
static void tick_init_tick ( void )
{
tick_disable_protection ( ) ;
tick_disable_irq ( ) ;
2005-04-17 02:20:36 +04:00
}
static unsigned long tick_get_tick ( void )
{
unsigned long ret ;
__asm__ __volatile__ ( " rd %%tick, %0 \n \t "
" mov %0, %0 "
: " =r " ( ret ) ) ;
return ret & ~ TICK_PRIV_BIT ;
}
2007-03-06 02:28:37 +03:00
static int tick_add_compare ( unsigned long adj )
2005-04-17 02:20:36 +04:00
{
2007-03-06 02:28:37 +03:00
unsigned long orig_tick , new_tick , new_compare ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
__asm__ __volatile__ ( " rd %%tick, %0 "
: " =r " ( orig_tick ) ) ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
orig_tick & = ~ TICKCMP_IRQ_BIT ;
2005-04-17 02:20:36 +04:00
/* Workaround for Spitfire Errata (#54 I think??), I discovered
* this via Sun BugID 4008234 , mentioned in Solaris - 2.5 .1 patch
* number 103640.
*
* On Blackbird writes to % tick_cmpr can fail , the
* workaround seems to be to execute the wr instruction
* at the start of an I - cache line , and perform a dummy
* read back from % tick_cmpr right after writing to it . - DaveM
*/
2007-03-06 02:28:37 +03:00
__asm__ __volatile__ ( " ba,pt %%xcc, 1f \n \t "
" add %1, %2, %0 \n \t "
2005-04-17 02:20:36 +04:00
" .align 64 \n "
" 1: \n \t "
" wr %0, 0, %%tick_cmpr \n \t "
2007-03-06 02:28:37 +03:00
" rd %%tick_cmpr, %%g0 \n \t "
: " =r " ( new_compare )
: " r " ( orig_tick ) , " r " ( adj ) ) ;
__asm__ __volatile__ ( " rd %%tick, %0 "
: " =r " ( new_tick ) ) ;
new_tick & = ~ TICKCMP_IRQ_BIT ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
return ( ( long ) ( new_tick - ( orig_tick + adj ) ) ) > 0L ;
2005-04-17 02:20:36 +04:00
}
2007-03-06 02:28:37 +03:00
static unsigned long tick_add_tick ( unsigned long adj )
2005-04-17 02:20:36 +04:00
{
2007-03-06 02:28:37 +03:00
unsigned long new_tick ;
2005-04-17 02:20:36 +04:00
/* Also need to handle Blackbird bug here too. */
__asm__ __volatile__ ( " rd %%tick, %0 \n \t "
2007-03-06 02:28:37 +03:00
" add %0, %1, %0 \n \t "
2005-04-17 02:20:36 +04:00
" wrpr %0, 0, %%tick \n \t "
2007-03-06 02:28:37 +03:00
: " =&r " ( new_tick )
: " r " ( adj ) ) ;
2005-04-17 02:20:36 +04:00
return new_tick ;
}
2005-07-11 02:45:11 +04:00
static struct sparc64_tick_ops tick_operations __read_mostly = {
2007-03-06 02:28:37 +03:00
. name = " tick " ,
2005-04-17 02:20:36 +04:00
. init_tick = tick_init_tick ,
2007-03-06 02:28:37 +03:00
. disable_irq = tick_disable_irq ,
2005-04-17 02:20:36 +04:00
. get_tick = tick_get_tick ,
. add_tick = tick_add_tick ,
. add_compare = tick_add_compare ,
. softint_mask = 1UL < < 0 ,
} ;
2005-11-08 01:10:10 +03:00
struct sparc64_tick_ops * tick_ops __read_mostly = & tick_operations ;
2007-03-06 02:28:37 +03:00
static void stick_disable_irq ( void )
{
__asm__ __volatile__ (
" wr %0, 0x0, %%asr25 "
: /* no outputs */
: " r " ( TICKCMP_IRQ_BIT ) ) ;
}
static void stick_init_tick ( void )
2005-04-17 02:20:36 +04:00
{
2006-02-12 10:14:59 +03:00
/* Writes to the %tick and %stick register are not
* allowed on sun4v . The Hypervisor controls that
* bit , per - strand .
*/
if ( tlb_type ! = hypervisor ) {
tick_disable_protection ( ) ;
2007-03-06 02:28:37 +03:00
tick_disable_irq ( ) ;
2006-02-12 10:14:59 +03:00
/* Let the user get at STICK too. */
__asm__ __volatile__ (
" rd %%asr24, %%g2 \n "
" andn %%g2, %0, %%g2 \n "
" wr %%g2, 0, %%asr24 "
: /* no outputs */
: " r " ( TICK_PRIV_BIT )
: " g1 " , " g2 " ) ;
}
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
stick_disable_irq ( ) ;
2005-04-17 02:20:36 +04:00
}
static unsigned long stick_get_tick ( void )
{
unsigned long ret ;
__asm__ __volatile__ ( " rd %%asr24, %0 "
: " =r " ( ret ) ) ;
return ret & ~ TICK_PRIV_BIT ;
}
2007-03-06 02:28:37 +03:00
static unsigned long stick_add_tick ( unsigned long adj )
2005-04-17 02:20:36 +04:00
{
2007-03-06 02:28:37 +03:00
unsigned long new_tick ;
2005-04-17 02:20:36 +04:00
__asm__ __volatile__ ( " rd %%asr24, %0 \n \t "
2007-03-06 02:28:37 +03:00
" add %0, %1, %0 \n \t "
2005-04-17 02:20:36 +04:00
" wr %0, 0, %%asr24 \n \t "
2007-03-06 02:28:37 +03:00
: " =&r " ( new_tick )
: " r " ( adj ) ) ;
2005-04-17 02:20:36 +04:00
return new_tick ;
}
2007-03-06 02:28:37 +03:00
static int stick_add_compare ( unsigned long adj )
2005-04-17 02:20:36 +04:00
{
2007-03-06 02:28:37 +03:00
unsigned long orig_tick , new_tick ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
__asm__ __volatile__ ( " rd %%asr24, %0 "
: " =r " ( orig_tick ) ) ;
orig_tick & = ~ TICKCMP_IRQ_BIT ;
__asm__ __volatile__ ( " wr %0, 0, %%asr25 "
: /* no outputs */
: " r " ( orig_tick + adj ) ) ;
__asm__ __volatile__ ( " rd %%asr24, %0 "
: " =r " ( new_tick ) ) ;
new_tick & = ~ TICKCMP_IRQ_BIT ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
return ( ( long ) ( new_tick - ( orig_tick + adj ) ) ) > 0L ;
2005-04-17 02:20:36 +04:00
}
2005-07-11 02:45:11 +04:00
static struct sparc64_tick_ops stick_operations __read_mostly = {
2007-03-06 02:28:37 +03:00
. name = " stick " ,
2005-04-17 02:20:36 +04:00
. init_tick = stick_init_tick ,
2007-03-06 02:28:37 +03:00
. disable_irq = stick_disable_irq ,
2005-04-17 02:20:36 +04:00
. get_tick = stick_get_tick ,
. add_tick = stick_add_tick ,
. add_compare = stick_add_compare ,
. softint_mask = 1UL < < 16 ,
} ;
/* On Hummingbird the STICK/STICK_CMPR register is implemented
* in I / O space . There are two 64 - bit registers each , the
* first holds the low 32 - bits of the value and the second holds
* the high 32 - bits .
*
* Since STICK is constantly updating , we have to access it carefully .
*
* The sequence we use to read is :
2006-01-18 02:21:01 +03:00
* 1 ) read high
* 2 ) read low
* 3 ) read high again , if it rolled re - read both low and high again .
2005-04-17 02:20:36 +04:00
*
* Writing STICK safely is also tricky :
* 1 ) write low to zero
* 2 ) write high
* 3 ) write low
*/
# define HBIRD_STICKCMP_ADDR 0x1fe0000f060UL
# define HBIRD_STICK_ADDR 0x1fe0000f070UL
static unsigned long __hbird_read_stick ( void )
{
unsigned long ret , tmp1 , tmp2 , tmp3 ;
2006-01-18 02:21:01 +03:00
unsigned long addr = HBIRD_STICK_ADDR + 8 ;
2005-04-17 02:20:36 +04:00
2006-01-18 02:21:01 +03:00
__asm__ __volatile__ ( " ldxa [%1] %5, %2 \n "
" 1: \n \t "
2005-04-17 02:20:36 +04:00
" sub %1, 0x8, %1 \n \t "
2006-01-18 02:21:01 +03:00
" ldxa [%1] %5, %3 \n \t "
" add %1, 0x8, %1 \n \t "
2005-04-17 02:20:36 +04:00
" ldxa [%1] %5, %4 \n \t "
" cmp %4, %2 \n \t "
2006-01-18 02:21:01 +03:00
" bne,a,pn %%xcc, 1b \n \t "
" mov %4, %2 \n \t "
" sllx %4, 32, %4 \n \t "
2005-04-17 02:20:36 +04:00
" or %3, %4, %0 \n \t "
: " =&r " ( ret ) , " =&r " ( addr ) ,
" =&r " ( tmp1 ) , " =&r " ( tmp2 ) , " =&r " ( tmp3 )
: " i " ( ASI_PHYS_BYPASS_EC_E ) , " 1 " ( addr ) ) ;
return ret ;
}
static void __hbird_write_stick ( unsigned long val )
{
unsigned long low = ( val & 0xffffffffUL ) ;
unsigned long high = ( val > > 32UL ) ;
unsigned long addr = HBIRD_STICK_ADDR ;
__asm__ __volatile__ ( " stxa %%g0, [%0] %4 \n \t "
" add %0, 0x8, %0 \n \t "
" stxa %3, [%0] %4 \n \t "
" sub %0, 0x8, %0 \n \t "
" stxa %2, [%0] %4 "
: " =&r " ( addr )
: " 0 " ( addr ) , " r " ( low ) , " r " ( high ) ,
" i " ( ASI_PHYS_BYPASS_EC_E ) ) ;
}
static void __hbird_write_compare ( unsigned long val )
{
unsigned long low = ( val & 0xffffffffUL ) ;
unsigned long high = ( val > > 32UL ) ;
unsigned long addr = HBIRD_STICKCMP_ADDR + 0x8UL ;
__asm__ __volatile__ ( " stxa %3, [%0] %4 \n \t "
" sub %0, 0x8, %0 \n \t "
" stxa %2, [%0] %4 "
: " =&r " ( addr )
: " 0 " ( addr ) , " r " ( low ) , " r " ( high ) ,
" i " ( ASI_PHYS_BYPASS_EC_E ) ) ;
}
2007-03-06 02:28:37 +03:00
static void hbtick_disable_irq ( void )
2005-04-17 02:20:36 +04:00
{
2007-03-06 02:28:37 +03:00
__hbird_write_compare ( TICKCMP_IRQ_BIT ) ;
}
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
static void hbtick_init_tick ( void )
{
2005-04-17 02:20:36 +04:00
tick_disable_protection ( ) ;
/* XXX This seems to be necessary to 'jumpstart' Hummingbird
* XXX into actually sending STICK interrupts . I think because
* XXX of how we store % tick_cmpr in head . S this somehow resets the
* XXX { TICK + STICK } interrupt mux . - DaveM
*/
__hbird_write_stick ( __hbird_read_stick ( ) ) ;
2007-03-06 02:28:37 +03:00
hbtick_disable_irq ( ) ;
2005-04-17 02:20:36 +04:00
}
static unsigned long hbtick_get_tick ( void )
{
return __hbird_read_stick ( ) & ~ TICK_PRIV_BIT ;
}
2007-03-06 02:28:37 +03:00
static unsigned long hbtick_add_tick ( unsigned long adj )
2005-04-17 02:20:36 +04:00
{
unsigned long val ;
val = __hbird_read_stick ( ) + adj ;
__hbird_write_stick ( val ) ;
return val ;
}
2007-03-06 02:28:37 +03:00
static int hbtick_add_compare ( unsigned long adj )
2005-04-17 02:20:36 +04:00
{
2007-03-06 02:28:37 +03:00
unsigned long val = __hbird_read_stick ( ) ;
unsigned long val2 ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
val & = ~ TICKCMP_IRQ_BIT ;
val + = adj ;
2005-04-17 02:20:36 +04:00
__hbird_write_compare ( val ) ;
2007-03-06 02:28:37 +03:00
val2 = __hbird_read_stick ( ) & ~ TICKCMP_IRQ_BIT ;
return ( ( long ) ( val2 - val ) ) > 0L ;
2005-04-17 02:20:36 +04:00
}
2005-07-11 02:45:11 +04:00
static struct sparc64_tick_ops hbtick_operations __read_mostly = {
2007-03-06 02:28:37 +03:00
. name = " hbtick " ,
2005-04-17 02:20:36 +04:00
. init_tick = hbtick_init_tick ,
2007-03-06 02:28:37 +03:00
. disable_irq = hbtick_disable_irq ,
2005-04-17 02:20:36 +04:00
. get_tick = hbtick_get_tick ,
. add_tick = hbtick_add_tick ,
. add_compare = hbtick_add_compare ,
. softint_mask = 1UL < < 0 ,
} ;
2005-07-11 02:45:11 +04:00
static unsigned long timer_ticks_per_nsec_quotient __read_mostly ;
2005-04-17 02:20:36 +04:00
2007-07-21 15:37:37 +04:00
int update_persistent_clock ( struct timespec now )
2007-02-22 15:16:21 +03:00
{
2008-08-29 04:34:31 +04:00
struct rtc_device * rtc = rtc_class_open ( " rtc0 " ) ;
if ( rtc )
return rtc_set_mmss ( rtc , now . tv_sec ) ;
2008-08-29 08:54:34 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2008-08-29 09:16:15 +04:00
unsigned long cmos_regs ;
EXPORT_SYMBOL ( cmos_regs ) ;
2006-06-23 06:12:03 +04:00
2008-08-29 09:16:15 +04:00
struct resource rtc_cmos_resource ;
static struct platform_device rtc_cmos_device = {
. name = " rtc_cmos " ,
. id = - 1 ,
. resource = & rtc_cmos_resource ,
. num_resources = 1 ,
} ;
2006-06-23 06:12:03 +04:00
2008-08-29 08:06:27 +04:00
static int __devinit rtc_probe ( struct of_device * op , const struct of_device_id * match )
2006-06-23 06:12:03 +04:00
{
2008-08-29 09:16:15 +04:00
struct resource * r ;
2006-06-23 06:12:03 +04:00
2008-08-29 09:16:15 +04:00
printk ( KERN_INFO " %s: RTC regs at 0x%lx \n " ,
op - > node - > full_name , op - > resource [ 0 ] . start ) ;
2007-05-12 08:18:50 +04:00
2008-08-29 09:16:15 +04:00
/* The CMOS RTC driver only accepts IORESOURCE_IO, so cons
* up a fake resource so that the probe works for all cases .
* When the RTC is behind an ISA bus it will have IORESOURCE_IO
* already , whereas when it ' s behind EBUS is will be IORESOURCE_MEM .
*/
r = & rtc_cmos_resource ;
r - > flags = IORESOURCE_IO ;
r - > name = op - > resource [ 0 ] . name ;
r - > start = op - > resource [ 0 ] . start ;
r - > end = op - > resource [ 0 ] . end ;
cmos_regs = op - > resource [ 0 ] . start ;
return platform_device_register ( & rtc_cmos_device ) ;
}
2008-08-31 12:23:17 +04:00
static struct of_device_id __initdata rtc_match [ ] = {
2008-08-29 09:16:15 +04:00
{
. name = " rtc " ,
. compatible = " m5819 " ,
} ,
{
. name = " rtc " ,
. compatible = " isa-m5819p " ,
} ,
{
. name = " rtc " ,
. compatible = " isa-m5823p " ,
} ,
{
. name = " rtc " ,
. compatible = " ds1287 " ,
} ,
{ } ,
} ;
static struct of_platform_driver rtc_driver = {
. match_table = rtc_match ,
. probe = rtc_probe ,
. driver = {
. name = " rtc " ,
} ,
} ;
2006-06-30 01:39:40 +04:00
2008-08-29 08:54:34 +04:00
static struct platform_device rtc_bq4802_device = {
. name = " rtc-bq4802 " ,
. id = - 1 ,
. num_resources = 1 ,
} ;
2008-08-29 09:16:15 +04:00
static int __devinit bq4802_probe ( struct of_device * op , const struct of_device_id * match )
{
2006-06-23 06:12:03 +04:00
2008-08-29 08:54:34 +04:00
printk ( KERN_INFO " %s: BQ4802 regs at 0x%lx \n " ,
op - > node - > full_name , op - > resource [ 0 ] . start ) ;
2006-06-23 06:12:03 +04:00
2008-08-29 08:54:34 +04:00
rtc_bq4802_device . resource = & op - > resource [ 0 ] ;
return platform_device_register ( & rtc_bq4802_device ) ;
2006-06-23 06:12:03 +04:00
}
2008-08-31 12:23:17 +04:00
static struct of_device_id __initdata bq4802_match [ ] = {
2006-06-30 01:36:52 +04:00
{
2008-08-29 08:06:27 +04:00
. name = " rtc " ,
2008-08-29 09:16:15 +04:00
. compatible = " bq4802 " ,
2008-08-29 08:06:27 +04:00
} ,
} ;
2008-08-29 09:16:15 +04:00
static struct of_platform_driver bq4802_driver = {
. match_table = bq4802_match ,
. probe = bq4802_probe ,
2008-08-29 08:06:27 +04:00
. driver = {
2008-08-29 09:16:15 +04:00
. name = " bq4802 " ,
2006-06-30 01:36:52 +04:00
} ,
2008-08-29 08:06:27 +04:00
} ;
static unsigned char mostek_read_byte ( struct device * dev , u32 ofs )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
void __iomem * regs ;
unsigned char val ;
regs = ( void __iomem * ) pdev - > resource [ 0 ] . start ;
val = readb ( regs + ofs ) ;
/* the year 0 is 1968 */
if ( ofs = = M48T59_YEAR ) {
val + = 0x68 ;
if ( ( val & 0xf ) > 9 )
val + = 6 ;
}
return val ;
}
static void mostek_write_byte ( struct device * dev , u32 ofs , u8 val )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
void __iomem * regs ;
regs = ( void __iomem * ) pdev - > resource [ 0 ] . start ;
if ( ofs = = M48T59_YEAR ) {
if ( val < 0x68 )
val + = 0x32 ;
else
val - = 0x68 ;
if ( ( val & 0xf ) > 9 )
val + = 6 ;
if ( ( val & 0xf0 ) > 0x9A )
val + = 0x60 ;
}
writeb ( val , regs + ofs ) ;
}
static struct m48t59_plat_data m48t59_data = {
. read_byte = mostek_read_byte ,
. write_byte = mostek_write_byte ,
} ;
static struct platform_device m48t59_rtc = {
. name = " rtc-m48t59 " ,
. id = 0 ,
. num_resources = 1 ,
. dev = {
. platform_data = & m48t59_data ,
} ,
} ;
static int __devinit mostek_probe ( struct of_device * op , const struct of_device_id * match )
{
struct device_node * dp = op - > node ;
/* On an Enterprise system there can be multiple mostek clocks.
* We should only match the one that is on the central FHC bus .
*/
if ( ! strcmp ( dp - > parent - > name , " fhc " ) & &
strcmp ( dp - > parent - > parent - > name , " central " ) ! = 0 )
return - ENODEV ;
printk ( KERN_INFO " %s: Mostek regs at 0x%lx \n " ,
dp - > full_name , op - > resource [ 0 ] . start ) ;
m48t59_rtc . resource = & op - > resource [ 0 ] ;
return platform_device_register ( & m48t59_rtc ) ;
}
2008-08-31 12:23:17 +04:00
static struct of_device_id __initdata mostek_match [ ] = {
2006-06-30 01:36:52 +04:00
{
2008-08-29 08:06:27 +04:00
. name = " eeprom " ,
2006-06-30 01:36:52 +04:00
} ,
{ } ,
} ;
2006-06-23 06:12:03 +04:00
2008-08-29 08:06:27 +04:00
static struct of_platform_driver mostek_driver = {
. match_table = mostek_match ,
. probe = mostek_probe ,
2007-10-11 10:27:34 +04:00
. driver = {
2008-08-29 08:06:27 +04:00
. name = " mostek " ,
2007-10-11 10:27:34 +04:00
} ,
2006-06-30 01:36:52 +04:00
} ;
2006-06-23 06:12:03 +04:00
2008-08-29 12:34:27 +04:00
static struct platform_device rtc_sun4v_device = {
. name = " rtc-sun4v " ,
. id = - 1 ,
} ;
2008-08-29 12:35:19 +04:00
static struct platform_device rtc_starfire_device = {
. name = " rtc-starfire " ,
. id = - 1 ,
} ;
2006-06-30 01:36:52 +04:00
static int __init clock_init ( void )
2006-06-23 06:12:03 +04:00
{
2008-08-29 12:35:19 +04:00
if ( this_is_starfire )
return platform_device_register ( & rtc_starfire_device ) ;
2008-08-29 12:34:27 +04:00
if ( tlb_type = = hypervisor )
return platform_device_register ( & rtc_sun4v_device ) ;
2005-04-17 02:20:36 +04:00
2008-08-29 08:06:27 +04:00
( void ) of_register_driver ( & rtc_driver , & of_platform_bus_type ) ;
( void ) of_register_driver ( & mostek_driver , & of_platform_bus_type ) ;
2008-08-29 09:16:15 +04:00
( void ) of_register_driver ( & bq4802_driver , & of_platform_bus_type ) ;
2008-08-29 08:06:27 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-30 01:36:52 +04:00
/* Must be after subsys_initcall() so that busses are probed. Must
* be before device_initcall ( ) because things like the RTC driver
* need to see the clock registers .
*/
fs_initcall ( clock_init ) ;
2005-04-17 02:20:36 +04:00
/* This is gets the master TICK_INT timer going. */
static unsigned long sparc64_init_timers ( void )
{
2006-06-22 10:34:02 +04:00
struct device_node * dp ;
2005-04-17 02:20:36 +04:00
unsigned long clock ;
2006-06-22 10:34:02 +04:00
dp = of_find_node_by_path ( " / " ) ;
2005-04-17 02:20:36 +04:00
if ( tlb_type = = spitfire ) {
unsigned long ver , manuf , impl ;
__asm__ __volatile__ ( " rdpr %%ver, %0 "
: " =&r " ( ver ) ) ;
manuf = ( ( ver > > 48 ) & 0xffff ) ;
impl = ( ( ver > > 32 ) & 0xffff ) ;
if ( manuf = = 0x17 & & impl = = 0x13 ) {
/* Hummingbird, aka Ultra-IIe */
tick_ops = & hbtick_operations ;
2007-05-26 02:49:59 +04:00
clock = of_getintprop_default ( dp , " stick-frequency " , 0 ) ;
2005-04-17 02:20:36 +04:00
} else {
tick_ops = & tick_operations ;
2007-05-26 02:49:59 +04:00
clock = local_cpu_data ( ) . clock_tick ;
2005-04-17 02:20:36 +04:00
}
} else {
tick_ops = & stick_operations ;
2007-05-26 02:49:59 +04:00
clock = of_getintprop_default ( dp , " stick-frequency " , 0 ) ;
2005-04-17 02:20:36 +04:00
}
return clock ;
}
struct freq_table {
unsigned long clock_tick_ref ;
unsigned int ref_freq ;
} ;
2006-02-17 23:33:13 +03:00
static DEFINE_PER_CPU ( struct freq_table , sparc64_freq_table ) = { 0 , 0 } ;
2005-04-17 02:20:36 +04:00
unsigned long sparc64_get_clock_tick ( unsigned int cpu )
{
struct freq_table * ft = & per_cpu ( sparc64_freq_table , cpu ) ;
if ( ft - > clock_tick_ref )
return ft - > clock_tick_ref ;
return cpu_data ( cpu ) . clock_tick ;
}
# ifdef CONFIG_CPU_FREQ
static int sparc64_cpufreq_notifier ( struct notifier_block * nb , unsigned long val ,
void * data )
{
struct cpufreq_freqs * freq = data ;
unsigned int cpu = freq - > cpu ;
struct freq_table * ft = & per_cpu ( sparc64_freq_table , cpu ) ;
if ( ! ft - > ref_freq ) {
ft - > ref_freq = freq - > old ;
ft - > clock_tick_ref = cpu_data ( cpu ) . clock_tick ;
}
if ( ( val = = CPUFREQ_PRECHANGE & & freq - > old < freq - > new ) | |
( val = = CPUFREQ_POSTCHANGE & & freq - > old > freq - > new ) | |
( val = = CPUFREQ_RESUMECHANGE ) ) {
cpu_data ( cpu ) . clock_tick =
cpufreq_scale ( ft - > clock_tick_ref ,
ft - > ref_freq ,
freq - > new ) ;
}
return 0 ;
}
static struct notifier_block sparc64_cpufreq_notifier_block = {
. notifier_call = sparc64_cpufreq_notifier
} ;
2008-07-24 03:21:07 +04:00
static int __init register_sparc64_cpufreq_notifier ( void )
{
cpufreq_register_notifier ( & sparc64_cpufreq_notifier_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
return 0 ;
}
core_initcall ( register_sparc64_cpufreq_notifier ) ;
2005-04-17 02:20:36 +04:00
# endif /* CONFIG_CPU_FREQ */
2007-03-06 02:28:37 +03:00
static int sparc64_next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
2007-03-27 12:20:14 +04:00
return tick_ops - > add_compare ( delta ) ? - ETIME : 0 ;
2007-03-06 02:28:37 +03:00
}
static void sparc64_timer_setup ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
switch ( mode ) {
case CLOCK_EVT_MODE_ONESHOT :
2007-07-21 15:37:34 +04:00
case CLOCK_EVT_MODE_RESUME :
2007-03-06 02:28:37 +03:00
break ;
case CLOCK_EVT_MODE_SHUTDOWN :
tick_ops - > disable_irq ( ) ;
break ;
case CLOCK_EVT_MODE_PERIODIC :
case CLOCK_EVT_MODE_UNUSED :
WARN_ON ( 1 ) ;
break ;
} ;
}
static struct clock_event_device sparc64_clockevent = {
. features = CLOCK_EVT_FEAT_ONESHOT ,
. set_mode = sparc64_timer_setup ,
. set_next_event = sparc64_next_event ,
. rating = 100 ,
. shift = 30 ,
. irq = - 1 ,
2005-04-17 02:20:36 +04:00
} ;
2007-03-06 02:28:37 +03:00
static DEFINE_PER_CPU ( struct clock_event_device , sparc64_events ) ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
void timer_interrupt ( int irq , struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2007-03-06 02:28:37 +03:00
struct pt_regs * old_regs = set_irq_regs ( regs ) ;
unsigned long tick_mask = tick_ops - > softint_mask ;
int cpu = smp_processor_id ( ) ;
struct clock_event_device * evt = & per_cpu ( sparc64_events , cpu ) ;
clear_softint ( tick_mask ) ;
irq_enter ( ) ;
kstat_this_cpu . irqs [ 0 ] + + ;
if ( unlikely ( ! evt - > event_handler ) ) {
printk ( KERN_WARNING
" Spurious SPARC64 timer interrupt on cpu %d \n " , cpu ) ;
} else
evt - > event_handler ( evt ) ;
irq_exit ( ) ;
set_irq_regs ( old_regs ) ;
}
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
void __devinit setup_sparc64_timer ( void )
{
struct clock_event_device * sevt ;
unsigned long pstate ;
2005-04-17 02:20:36 +04:00
2007-03-06 02:28:37 +03:00
/* Guarantee that the following sequences execute
* uninterrupted .
2005-04-17 02:20:36 +04:00
*/
2007-03-06 02:28:37 +03:00
__asm__ __volatile__ ( " rdpr %%pstate, %0 \n \t "
" wrpr %0, %1, %%pstate "
: " =r " ( pstate )
: " i " ( PSTATE_IE ) ) ;
tick_ops - > init_tick ( ) ;
/* Restore PSTATE_IE. */
__asm__ __volatile__ ( " wrpr %0, 0x0, %%pstate "
: /* no outputs */
: " r " ( pstate ) ) ;
sevt = & __get_cpu_var ( sparc64_events ) ;
memcpy ( sevt , & sparc64_clockevent , sizeof ( * sevt ) ) ;
sevt - > cpumask = cpumask_of_cpu ( smp_processor_id ( ) ) ;
clockevents_register_device ( sevt ) ;
}
2007-05-18 09:55:26 +04:00
# define SPARC64_NSEC_PER_CYC_SHIFT 10UL
2007-03-06 02:28:37 +03:00
static struct clocksource clocksource_tick = {
. rating = 100 ,
. mask = CLOCKSOURCE_MASK ( 64 ) ,
. shift = 16 ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
static void __init setup_clockevent_multiplier ( unsigned long hz )
{
unsigned long mult , shift = 32 ;
while ( 1 ) {
mult = div_sc ( hz , NSEC_PER_SEC , shift ) ;
if ( mult & & ( mult > > 32UL ) = = 0UL )
break ;
shift - - ;
}
sparc64_clockevent . shift = shift ;
sparc64_clockevent . mult = mult ;
}
2007-07-14 13:23:37 +04:00
static unsigned long tb_ticks_per_usec __read_mostly ;
void __delay ( unsigned long loops )
{
unsigned long bclock , now ;
bclock = tick_ops - > get_tick ( ) ;
do {
now = tick_ops - > get_tick ( ) ;
} while ( ( now - bclock ) < loops ) ;
}
EXPORT_SYMBOL ( __delay ) ;
void udelay ( unsigned long usecs )
{
__delay ( tb_ticks_per_usec * usecs ) ;
}
EXPORT_SYMBOL ( udelay ) ;
2007-03-06 02:28:37 +03:00
void __init time_init ( void )
{
unsigned long clock = sparc64_init_timers ( ) ;
2005-04-17 02:20:36 +04:00
2007-07-14 13:23:37 +04:00
tb_ticks_per_usec = clock / USEC_PER_SEC ;
2005-04-17 02:20:36 +04:00
timer_ticks_per_nsec_quotient =
2007-03-06 02:28:37 +03:00
clocksource_hz2mult ( clock , SPARC64_NSEC_PER_CYC_SHIFT ) ;
clocksource_tick . name = tick_ops - > name ;
clocksource_tick . mult =
clocksource_hz2mult ( clock ,
clocksource_tick . shift ) ;
clocksource_tick . read = tick_ops - > get_tick ;
printk ( " clocksource: mult[%x] shift[%d] \n " ,
clocksource_tick . mult , clocksource_tick . shift ) ;
clocksource_register ( & clocksource_tick ) ;
sparc64_clockevent . name = tick_ops - > name ;
setup_clockevent_multiplier ( clock ) ;
sparc64_clockevent . max_delta_ns =
2008-03-26 11:11:55 +03:00
clockevent_delta2ns ( 0x7fffffffffffffffUL , & sparc64_clockevent ) ;
2007-03-06 02:28:37 +03:00
sparc64_clockevent . min_delta_ns =
clockevent_delta2ns ( 0xF , & sparc64_clockevent ) ;
printk ( " clockevent: mult[%lx] shift[%d] \n " ,
sparc64_clockevent . mult , sparc64_clockevent . shift ) ;
setup_sparc64_timer ( ) ;
2005-04-17 02:20:36 +04:00
}
unsigned long long sched_clock ( void )
{
unsigned long ticks = tick_ops - > get_tick ( ) ;
return ( ticks * timer_ticks_per_nsec_quotient )
> > SPARC64_NSEC_PER_CYC_SHIFT ;
}
2008-02-06 12:36:42 +03:00
int __devinit read_current_timer ( unsigned long * timer_val )
{
* timer_val = tick_ops - > get_tick ( ) ;
return 0 ;
}