2010-10-27 17:28:57 +01:00
/* MN10300 clockevents
*
* Copyright ( C ) 2010 Red Hat , Inc . All Rights Reserved .
* Written by Mark Salter ( msalter @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , or ( at your option ) any later version .
*/
# include <linux/clockchips.h>
# include <linux/interrupt.h>
# include <linux/percpu.h>
# include <linux/smp.h>
# include <asm/timex.h>
# include "internal.h"
# ifdef CONFIG_SMP
# if (CONFIG_NR_CPUS > 2) && !defined(CONFIG_GEENERIC_CLOCKEVENTS_BROADCAST)
# error "This doesn't scale well! Need per-core local timers."
# endif
# else /* CONFIG_SMP */
# define stop_jiffies_counter1()
# define reload_jiffies_counter1(x)
# define TMJC1IRQ TMJCIRQ
# endif
static int next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
unsigned int cpu = smp_processor_id ( ) ;
if ( cpu = = 0 ) {
stop_jiffies_counter ( ) ;
reload_jiffies_counter ( delta - 1 ) ;
} else {
stop_jiffies_counter1 ( ) ;
reload_jiffies_counter1 ( delta - 1 ) ;
}
return 0 ;
}
static void set_clock_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
/* Nothing to do ... */
}
static DEFINE_PER_CPU ( struct clock_event_device , mn10300_clockevent_device ) ;
static DEFINE_PER_CPU ( struct irqaction , timer_irq ) ;
static irqreturn_t timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * cd ;
unsigned int cpu = smp_processor_id ( ) ;
if ( cpu = = 0 )
stop_jiffies_counter ( ) ;
else
stop_jiffies_counter1 ( ) ;
cd = & per_cpu ( mn10300_clockevent_device , cpu ) ;
cd - > event_handler ( cd ) ;
return IRQ_HANDLED ;
}
static void event_handler ( struct clock_event_device * dev )
{
}
int __init init_clockevents ( void )
{
struct clock_event_device * cd ;
struct irqaction * iact ;
unsigned int cpu = smp_processor_id ( ) ;
cd = & per_cpu ( mn10300_clockevent_device , cpu ) ;
if ( cpu = = 0 ) {
stop_jiffies_counter ( ) ;
cd - > irq = TMJCIRQ ;
} else {
stop_jiffies_counter1 ( ) ;
cd - > irq = TMJC1IRQ ;
}
cd - > name = " Timestamp " ;
cd - > features = CLOCK_EVT_FEAT_ONESHOT ;
2011-03-18 16:52:50 +00:00
/* Calculate shift/mult. We want to spawn at least 1 second */
clockevents_calc_mult_shift ( cd , MN10300_JCCLK , 1 ) ;
2010-10-27 17:28:57 +01:00
2011-03-18 16:52:50 +00:00
/* Calculate the min / max delta */
2010-10-27 17:28:57 +01:00
cd - > max_delta_ns = clockevent_delta2ns ( TMJCBR_MAX , cd ) ;
cd - > min_delta_ns = clockevent_delta2ns ( 100 , cd ) ;
cd - > rating = 200 ;
cd - > cpumask = cpumask_of ( smp_processor_id ( ) ) ;
cd - > set_mode = set_clock_mode ;
cd - > event_handler = event_handler ;
cd - > set_next_event = next_event ;
iact = & per_cpu ( timer_irq , cpu ) ;
iact - > flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER ;
iact - > handler = timer_interrupt ;
clockevents_register_device ( cd ) ;
# if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
/* setup timer irq affinity so it only runs on this cpu */
{
2011-03-18 16:52:53 +00:00
struct irq_data * data ;
data = irq_get_irq_data ( cd - > irq ) ;
cpumask_copy ( data - > affinity , cpumask_of ( cpu ) ) ;
2010-10-27 17:28:57 +01:00
iact - > flags | = IRQF_NOBALANCING ;
}
# endif
if ( cpu = = 0 ) {
reload_jiffies_counter ( MN10300_JC_PER_HZ - 1 ) ;
iact - > name = " CPU0 Timer " ;
} else {
reload_jiffies_counter1 ( MN10300_JC_PER_HZ - 1 ) ;
iact - > name = " CPU1 Timer " ;
}
setup_jiffies_interrupt ( cd - > irq , iact ) ;
return 0 ;
}