2007-10-27 00:17:01 -07:00
/* linux/arch/sparc/kernel/time.c
2005-04-16 15:20:36 -07:00
*
2007-10-27 00:17:01 -07:00
* Copyright ( C ) 1995 David S . Miller ( davem @ davemloft . net )
2005-04-16 15:20:36 -07:00
* Copyright ( C ) 1996 Thomas K . Dyas ( tdyas @ eden . rutgers . edu )
*
* Chris Davis ( cdavis @ cois . on . ca ) 03 / 27 / 1998
* Added support for the intersil on the sun4 / 4200
*
* Gleb Raiko ( rajko @ mech . math . msu . su ) 08 / 18 / 1998
* Support for MicroSPARC - IIep , PCI CPU .
*
* This file handles the Sparc specific time handling details .
*
* 1997 - 09 - 10 Updated NTP code according to technical memorandum Jan ' 96
* " A Kernel Model for Precision Timekeeping " by Dave Mills
*/
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/param.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/time.h>
2008-09-03 15:52:38 -07:00
# include <linux/rtc.h>
# include <linux/rtc/m48t59.h>
2005-04-16 15:20:36 -07:00
# include <linux/timex.h>
2012-04-04 21:49:26 +02:00
# include <linux/clocksource.h>
# include <linux/clockchips.h>
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/ioport.h>
# include <linux/profile.h>
2008-08-27 04:05:35 -07:00
# include <linux/of.h>
2008-08-07 15:33:36 -07:00
# include <linux/of_device.h>
2008-09-03 15:52:38 -07:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
2014-05-16 23:25:44 +02:00
# include <asm/mc146818rtc.h>
2005-04-16 15:20:36 -07:00
# include <asm/oplib.h>
2010-01-15 01:34:28 -08:00
# include <asm/timex.h>
2005-04-16 15:20:36 -07:00
# include <asm/timer.h>
# include <asm/irq.h>
# include <asm/io.h>
# include <asm/idprom.h>
# include <asm/page.h>
# include <asm/pcic.h>
2006-10-08 14:30:44 +01:00
# include <asm/irq_regs.h>
2012-04-04 21:49:26 +02:00
# include <asm/setup.h>
2005-04-16 15:20:36 -07:00
2014-05-16 23:25:44 +02:00
# include "kernel.h"
2007-07-21 19:18:57 -07:00
# include "irq.h"
2012-04-04 21:49:26 +02:00
static __cacheline_aligned_in_smp DEFINE_SEQLOCK ( timer_cs_lock ) ;
static __volatile__ u64 timer_cs_internal_counter = 0 ;
static char timer_cs_enabled = 0 ;
static struct clock_event_device timer_ce ;
static char timer_ce_enabled = 0 ;
# ifdef CONFIG_SMP
DEFINE_PER_CPU ( struct clock_event_device , sparc32_clockevent ) ;
# endif
2005-04-16 15:20:36 -07:00
DEFINE_SPINLOCK ( rtc_lock ) ;
2009-01-08 16:58:05 -08:00
EXPORT_SYMBOL ( rtc_lock ) ;
2005-04-16 15:20:36 -07:00
static int set_rtc_mmss ( unsigned long ) ;
unsigned long profile_pc ( struct pt_regs * regs )
{
extern char __copy_user_begin [ ] , __copy_user_end [ ] ;
extern char __bzero_begin [ ] , __bzero_end [ ] ;
unsigned long pc = regs - > pc ;
if ( in_lock_functions ( pc ) | |
( pc > = ( unsigned long ) __copy_user_begin & &
pc < ( unsigned long ) __copy_user_end ) | |
( pc > = ( unsigned long ) __bzero_begin & &
2006-12-17 16:18:47 -08:00
pc < ( unsigned long ) __bzero_end ) )
2005-04-16 15:20:36 -07:00
pc = regs - > u_regs [ UREG_RETPC ] ;
return pc ;
}
2006-10-17 19:21:48 -07:00
EXPORT_SYMBOL ( profile_pc ) ;
2014-05-16 23:25:44 +02:00
volatile u32 __iomem * master_l10_counter ;
2005-04-16 15:20:36 -07:00
2010-03-03 19:57:27 -08:00
int update_persistent_clock ( struct timespec now )
{
return set_rtc_mmss ( now . tv_sec ) ;
}
2012-04-04 21:49:26 +02:00
irqreturn_t notrace timer_interrupt ( int dummy , void * dev_id )
{
if ( timer_cs_enabled ) {
write_seqlock ( & timer_cs_lock ) ;
timer_cs_internal_counter + + ;
2012-05-14 17:30:35 +02:00
sparc_config . clear_clock_irq ( ) ;
2012-04-04 21:49:26 +02:00
write_sequnlock ( & timer_cs_lock ) ;
} else {
2012-05-14 17:30:35 +02:00
sparc_config . clear_clock_irq ( ) ;
2012-04-04 21:49:26 +02:00
}
2005-04-16 15:20:36 -07:00
2012-04-04 21:49:26 +02:00
if ( timer_ce_enabled )
timer_ce . event_handler ( & timer_ce ) ;
2005-04-16 15:20:36 -07:00
2012-04-04 21:49:26 +02:00
return IRQ_HANDLED ;
}
static void timer_ce_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
2005-04-16 15:20:36 -07:00
{
2012-04-04 21:49:26 +02:00
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
case CLOCK_EVT_MODE_RESUME :
timer_ce_enabled = 1 ;
break ;
case CLOCK_EVT_MODE_SHUTDOWN :
timer_ce_enabled = 0 ;
break ;
default :
break ;
}
smp_mb ( ) ;
}
static __init void setup_timer_ce ( void )
{
struct clock_event_device * ce = & timer_ce ;
BUG_ON ( smp_processor_id ( ) ! = boot_cpu_id ) ;
ce - > name = " timer_ce " ;
ce - > rating = 100 ;
ce - > features = CLOCK_EVT_FEAT_PERIODIC ;
ce - > set_mode = timer_ce_set_mode ;
ce - > cpumask = cpu_possible_mask ;
ce - > shift = 32 ;
ce - > mult = div_sc ( sparc_config . clock_rate , NSEC_PER_SEC ,
ce - > shift ) ;
clockevents_register_device ( ce ) ;
}
2005-04-16 15:20:36 -07:00
2012-04-04 21:49:26 +02:00
static unsigned int sbus_cycles_offset ( void )
{
2014-05-16 23:25:44 +02:00
u32 val , offset ;
2005-04-16 15:20:36 -07:00
2014-05-16 23:25:44 +02:00
val = sbus_readl ( master_l10_counter ) ;
2012-04-04 21:49:26 +02:00
offset = ( val > > TIMER_VALUE_SHIFT ) & TIMER_VALUE_MASK ;
2005-04-16 15:20:36 -07:00
2012-04-04 21:49:26 +02:00
/* Limit hit? */
if ( val & TIMER_LIMIT_BIT )
offset + = sparc_config . cs_period ;
return offset ;
2005-04-16 15:20:36 -07:00
}
2012-04-04 21:49:26 +02:00
static cycle_t timer_cs_read ( struct clocksource * cs )
{
unsigned int seq , offset ;
u64 cycles ;
do {
seq = read_seqbegin ( & timer_cs_lock ) ;
cycles = timer_cs_internal_counter ;
offset = sparc_config . get_cycles_offset ( ) ;
} while ( read_seqretry ( & timer_cs_lock , seq ) ) ;
/* Count absolute cycles */
cycles * = sparc_config . cs_period ;
cycles + = offset ;
return cycles ;
}
static struct clocksource timer_cs = {
. name = " timer_cs " ,
. rating = 100 ,
. read = timer_cs_read ,
. mask = CLOCKSOURCE_MASK ( 64 ) ,
. shift = 2 ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
static __init int setup_timer_cs ( void )
{
timer_cs_enabled = 1 ;
timer_cs . mult = clocksource_hz2mult ( sparc_config . clock_rate ,
timer_cs . shift ) ;
return clocksource_register ( & timer_cs ) ;
}
# ifdef CONFIG_SMP
static void percpu_ce_setup ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
int cpu = __first_cpu ( evt - > cpumask ) ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2012-05-14 17:30:35 +02:00
sparc_config . load_profile_irq ( cpu ,
SBUS_CLOCK_RATE / HZ ) ;
2012-04-04 21:49:26 +02:00
break ;
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_UNUSED :
2012-05-14 17:30:35 +02:00
sparc_config . load_profile_irq ( cpu , 0 ) ;
2012-04-04 21:49:26 +02:00
break ;
default :
break ;
}
}
static int percpu_ce_set_next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
int cpu = __first_cpu ( evt - > cpumask ) ;
unsigned int next = ( unsigned int ) delta ;
2012-05-14 17:30:35 +02:00
sparc_config . load_profile_irq ( cpu , next ) ;
2012-04-04 21:49:26 +02:00
return 0 ;
}
void register_percpu_ce ( int cpu )
{
struct clock_event_device * ce = & per_cpu ( sparc32_clockevent , cpu ) ;
unsigned int features = CLOCK_EVT_FEAT_PERIODIC ;
if ( sparc_config . features & FEAT_L14_ONESHOT )
features | = CLOCK_EVT_FEAT_ONESHOT ;
ce - > name = " percpu_ce " ;
ce - > rating = 200 ;
ce - > features = features ;
ce - > set_mode = percpu_ce_setup ;
ce - > set_next_event = percpu_ce_set_next_event ;
ce - > cpumask = cpumask_of ( cpu ) ;
ce - > shift = 32 ;
ce - > mult = div_sc ( sparc_config . clock_rate , NSEC_PER_SEC ,
ce - > shift ) ;
ce - > max_delta_ns = clockevent_delta2ns ( sparc_config . clock_rate , ce ) ;
ce - > min_delta_ns = clockevent_delta2ns ( 100 , ce ) ;
clockevents_register_device ( ce ) ;
}
# endif
2008-09-03 15:52:38 -07:00
static unsigned char mostek_read_byte ( struct device * dev , u32 ofs )
2005-04-16 15:20:36 -07:00
{
2008-09-03 15:52:38 -07:00
struct platform_device * pdev = to_platform_device ( dev ) ;
struct m48t59_plat_data * pdata = pdev - > dev . platform_data ;
2008-10-29 15:35:24 -07:00
return readb ( pdata - > ioaddr + ofs ) ;
2005-04-16 15:20:36 -07:00
}
2008-09-03 15:52:38 -07:00
static void mostek_write_byte ( struct device * dev , u32 ofs , u8 val )
2005-04-16 15:20:36 -07:00
{
2008-09-03 15:52:38 -07:00
struct platform_device * pdev = to_platform_device ( dev ) ;
struct m48t59_plat_data * pdata = pdev - > dev . platform_data ;
2008-10-29 15:35:24 -07:00
writeb ( val , pdata - > ioaddr + ofs ) ;
2005-04-16 15:20:36 -07:00
}
2008-09-03 15:52:38 -07:00
static struct m48t59_plat_data m48t59_data = {
. read_byte = mostek_read_byte ,
. write_byte = mostek_write_byte ,
} ;
/* resource is set at runtime */
static struct platform_device m48t59_rtc = {
. name = " rtc-m48t59 " ,
. id = 0 ,
. num_resources = 1 ,
. dev = {
. platform_data = & m48t59_data ,
} ,
} ;
2006-07-27 22:08:01 -07:00
2012-12-21 14:03:26 -08:00
static int clock_probe ( struct platform_device * op )
2005-04-16 15:20:36 -07:00
{
2010-04-13 16:12:29 -07:00
struct device_node * dp = op - > dev . of_node ;
2007-03-29 00:47:23 -07:00
const char * model = of_get_property ( dp , " model " , NULL ) ;
2005-04-16 15:20:36 -07:00
2006-06-29 14:36:52 -07:00
if ( ! model )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2011-03-24 16:34:52 -07:00
/* Only the primary RTC has an address property */
if ( ! of_find_property ( dp , " address " , NULL ) )
return - ENODEV ;
2008-09-03 15:52:38 -07:00
m48t59_rtc . resource = & op - > resource [ 0 ] ;
2006-06-29 14:36:52 -07:00
if ( ! strcmp ( model , " mk48t02 " ) ) {
2005-04-16 15:20:36 -07:00
/* Map the clock register io area read-only */
2008-09-03 15:52:38 -07:00
m48t59_data . ioaddr = of_ioremap ( & op - > resource [ 0 ] , 0 ,
2048 , " rtc-m48t59 " ) ;
m48t59_data . type = M48T59RTC_TYPE_M48T02 ;
2006-06-29 14:36:52 -07:00
} else if ( ! strcmp ( model , " mk48t08 " ) ) {
2008-09-03 15:52:38 -07:00
m48t59_data . ioaddr = of_ioremap ( & op - > resource [ 0 ] , 0 ,
8192 , " rtc-m48t59 " ) ;
m48t59_data . type = M48T59RTC_TYPE_M48T08 ;
2006-06-29 14:36:52 -07:00
} else
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2008-09-03 15:52:38 -07:00
if ( platform_device_register ( & m48t59_rtc ) < 0 )
printk ( KERN_ERR " Registering RTC device failed \n " ) ;
2006-07-27 22:08:01 -07:00
2006-06-29 14:36:52 -07:00
return 0 ;
}
sparc32: fix section mismatch warnings in apc, pmc and time_32
In all cases there were a struct of_device_id variable defined __initdata.
But it was referenced from struct platform_driver.of_match_table
which is not guaranteed to be used during init only.
So drop the __initdata annotation.
This fixes following warnings:
WARNING: arch/sparc/kernel/built-in.o(.data+0x810): Section mismatch in reference from the variable clock_driver to the variable .init.data:clock_match
The variable clock_driver references
the variable __initdata clock_match
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/sparc/kernel/built-in.o(.data+0xcec): Section mismatch in reference from the variable apc_driver to the variable .init.data:apc_match
The variable apc_driver references
the variable __initdata apc_match
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/sparc/kernel/built-in.o(.data+0xd60): Section mismatch in reference from the variable pmc_driver to the variable .init.data:pmc_match
The variable pmc_driver references
the variable __initdata pmc_match
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-04-21 15:37:20 -07:00
static struct of_device_id clock_match [ ] = {
2006-06-29 14:36:52 -07:00
{
. name = " eeprom " ,
} ,
{ } ,
} ;
2011-02-22 20:01:33 -07:00
static struct platform_driver clock_driver = {
2006-06-29 14:36:52 -07:00
. probe = clock_probe ,
2010-04-13 16:13:02 -07:00
. driver = {
. name = " rtc " ,
. owner = THIS_MODULE ,
. of_match_table = clock_match ,
2007-10-10 23:27:34 -07:00
} ,
2006-06-29 14:36:52 -07:00
} ;
/* Probe for the mostek real time clock chip. */
2006-07-27 22:08:01 -07:00
static int __init clock_init ( void )
2006-06-29 14:36:52 -07:00
{
2011-02-22 20:01:33 -07:00
return platform_driver_register ( & clock_driver ) ;
2005-04-16 15:20:36 -07:00
}
2006-07-27 22:08:01 -07: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 ) ;
2012-04-04 21:49:26 +02:00
static void __init sparc32_late_time_init ( void )
2005-04-16 15:20:36 -07:00
{
2012-04-04 21:49:26 +02:00
if ( sparc_config . features & FEAT_L10_CLOCKEVENT )
setup_timer_ce ( ) ;
if ( sparc_config . features & FEAT_L10_CLOCKSOURCE )
setup_timer_cs ( ) ;
# ifdef CONFIG_SMP
register_percpu_ce ( smp_processor_id ( ) ) ;
# endif
2005-04-16 15:20:36 -07:00
}
2012-04-04 21:49:26 +02:00
static void __init sbus_time_init ( void )
2005-04-16 15:20:36 -07:00
{
2012-04-04 21:49:26 +02:00
sparc_config . get_cycles_offset = sbus_cycles_offset ;
sparc_config . init_timers ( ) ;
2005-04-16 15:20:36 -07:00
}
2012-04-04 21:49:26 +02:00
void __init time_init ( void )
2005-04-16 15:20:36 -07:00
{
2012-04-04 21:49:26 +02:00
sparc_config . features = 0 ;
late_time_init = sparc32_late_time_init ;
2005-04-16 15:20:36 -07:00
2011-04-17 13:49:55 +02:00
if ( pcic_present ( ) )
2010-01-15 01:34:28 -08:00
pci_time_init ( ) ;
2011-04-17 13:49:55 +02:00
else
sbus_time_init ( ) ;
2005-04-16 15:20:36 -07:00
}
2010-01-15 01:34:28 -08:00
2008-09-03 15:52:38 -07:00
static int set_rtc_mmss ( unsigned long secs )
2005-04-16 15:20:36 -07:00
{
2008-09-03 15:52:38 -07:00
struct rtc_device * rtc = rtc_class_open ( " rtc0 " ) ;
2008-09-10 13:36:13 -07:00
int err = - 1 ;
2005-04-16 15:20:36 -07:00
2008-09-10 13:36:13 -07:00
if ( rtc ) {
err = rtc_set_mmss ( rtc , secs ) ;
rtc_class_close ( rtc ) ;
}
2005-04-16 15:20:36 -07:00
2008-09-10 13:36:13 -07:00
return err ;
2005-04-16 15:20:36 -07:00
}