2011-03-11 04:39:57 +03:00
/* linux/arch/arm/mach-exynos4/mct.c
*
* Copyright ( c ) 2011 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* EXYNOS4 MCT ( Multi - Core Timer ) support
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/sched.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/clockchips.h>
2013-02-16 04:40:51 +04:00
# include <linux/cpu.h>
2011-03-11 04:39:57 +03:00
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/percpu.h>
2012-11-15 10:48:56 +04:00
# include <linux/of.h>
2013-03-09 11:01:52 +04:00
# include <linux/of_irq.h>
# include <linux/of_address.h>
2013-03-09 11:10:03 +04:00
# include <linux/clocksource.h>
2014-05-02 17:27:01 +04:00
# include <linux/sched_clock.h>
2011-03-11 04:39:57 +03:00
2013-03-09 11:01:47 +04:00
# define EXYNOS4_MCTREG(x) (x)
# define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
# define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104)
# define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110)
# define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200)
# define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204)
# define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208)
# define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240)
# define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244)
# define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248)
# define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C)
# define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300)
# define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x))
# define EXYNOS4_MCT_L_MASK (0xffffff00)
# define MCT_L_TCNTB_OFFSET (0x00)
# define MCT_L_ICNTB_OFFSET (0x08)
# define MCT_L_TCON_OFFSET (0x20)
# define MCT_L_INT_CSTAT_OFFSET (0x30)
# define MCT_L_INT_ENB_OFFSET (0x34)
# define MCT_L_WSTAT_OFFSET (0x40)
# define MCT_G_TCON_START (1 << 8)
# define MCT_G_TCON_COMP0_AUTO_INC (1 << 1)
# define MCT_G_TCON_COMP0_ENABLE (1 << 0)
# define MCT_L_TCON_INTERVAL_MODE (1 << 2)
# define MCT_L_TCON_INT_START (1 << 1)
# define MCT_L_TCON_TIMER_START (1 << 0)
2012-03-10 03:09:21 +04:00
# define TICK_BASE_CNT 1
2011-10-04 12:02:58 +04:00
enum {
MCT_INT_SPI ,
MCT_INT_PPI
} ;
2013-03-09 11:01:50 +04:00
enum {
MCT_G0_IRQ ,
MCT_G1_IRQ ,
MCT_G2_IRQ ,
MCT_G3_IRQ ,
MCT_L0_IRQ ,
MCT_L1_IRQ ,
MCT_L2_IRQ ,
MCT_L3_IRQ ,
2013-12-02 02:48:23 +04:00
MCT_L4_IRQ ,
MCT_L5_IRQ ,
MCT_L6_IRQ ,
MCT_L7_IRQ ,
2013-03-09 11:01:50 +04:00
MCT_NR_IRQS ,
} ;
2013-03-09 11:01:47 +04:00
static void __iomem * reg_base ;
2011-03-11 04:39:57 +03:00
static unsigned long clk_rate ;
2011-10-04 12:02:58 +04:00
static unsigned int mct_int_type ;
2013-03-09 11:01:50 +04:00
static int mct_irqs [ MCT_NR_IRQS ] ;
2011-03-11 04:39:57 +03:00
struct mct_clock_event_device {
2013-02-16 04:40:51 +04:00
struct clock_event_device evt ;
2013-03-09 11:01:47 +04:00
unsigned long base ;
2011-10-04 12:09:26 +04:00
char name [ 10 ] ;
2011-03-11 04:39:57 +03:00
} ;
2013-03-09 11:01:47 +04:00
static void exynos4_mct_write ( unsigned int value , unsigned long offset )
2011-03-11 04:39:57 +03:00
{
2013-03-09 11:01:47 +04:00
unsigned long stat_addr ;
2011-03-11 04:39:57 +03:00
u32 mask ;
u32 i ;
2014-07-05 01:43:20 +04:00
writel_relaxed ( value , reg_base + offset ) ;
2011-03-11 04:39:57 +03:00
2013-03-09 11:01:47 +04:00
if ( likely ( offset > = EXYNOS4_MCT_L_BASE ( 0 ) ) ) {
2014-10-22 05:37:08 +04:00
stat_addr = ( offset & EXYNOS4_MCT_L_MASK ) + MCT_L_WSTAT_OFFSET ;
switch ( offset & ~ EXYNOS4_MCT_L_MASK ) {
2013-03-09 11:01:47 +04:00
case MCT_L_TCON_OFFSET :
2011-10-04 12:09:26 +04:00
mask = 1 < < 3 ; /* L_TCON write status */
break ;
2013-03-09 11:01:47 +04:00
case MCT_L_ICNTB_OFFSET :
2011-10-04 12:09:26 +04:00
mask = 1 < < 1 ; /* L_ICNTB write status */
break ;
2013-03-09 11:01:47 +04:00
case MCT_L_TCNTB_OFFSET :
2011-10-04 12:09:26 +04:00
mask = 1 < < 0 ; /* L_TCNTB write status */
break ;
default :
return ;
}
} else {
2013-03-09 11:01:47 +04:00
switch ( offset ) {
case EXYNOS4_MCT_G_TCON :
2011-10-04 12:09:26 +04:00
stat_addr = EXYNOS4_MCT_G_WSTAT ;
mask = 1 < < 16 ; /* G_TCON write status */
break ;
2013-03-09 11:01:47 +04:00
case EXYNOS4_MCT_G_COMP0_L :
2011-10-04 12:09:26 +04:00
stat_addr = EXYNOS4_MCT_G_WSTAT ;
mask = 1 < < 0 ; /* G_COMP0_L write status */
break ;
2013-03-09 11:01:47 +04:00
case EXYNOS4_MCT_G_COMP0_U :
2011-10-04 12:09:26 +04:00
stat_addr = EXYNOS4_MCT_G_WSTAT ;
mask = 1 < < 1 ; /* G_COMP0_U write status */
break ;
2013-03-09 11:01:47 +04:00
case EXYNOS4_MCT_G_COMP0_ADD_INCR :
2011-10-04 12:09:26 +04:00
stat_addr = EXYNOS4_MCT_G_WSTAT ;
mask = 1 < < 2 ; /* G_COMP0_ADD_INCR w status */
break ;
2013-03-09 11:01:47 +04:00
case EXYNOS4_MCT_G_CNT_L :
2011-10-04 12:09:26 +04:00
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT ;
mask = 1 < < 0 ; /* G_CNT_L write status */
break ;
2013-03-09 11:01:47 +04:00
case EXYNOS4_MCT_G_CNT_U :
2011-10-04 12:09:26 +04:00
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT ;
mask = 1 < < 1 ; /* G_CNT_U write status */
break ;
default :
return ;
}
2011-03-11 04:39:57 +03:00
}
/* Wait maximum 1 ms until written values are applied */
for ( i = 0 ; i < loops_per_jiffy / 1000 * HZ ; i + + )
2014-07-05 01:43:20 +04:00
if ( readl_relaxed ( reg_base + stat_addr ) & mask ) {
writel_relaxed ( mask , reg_base + stat_addr ) ;
2011-03-11 04:39:57 +03:00
return ;
}
2013-03-09 11:01:47 +04:00
panic ( " MCT hangs after writing %d (offset:0x%lx) \n " , value , offset ) ;
2011-03-11 04:39:57 +03:00
}
/* Clocksource handling */
2014-06-11 19:18:48 +04:00
static void exynos4_mct_frc_start ( void )
2011-03-11 04:39:57 +03:00
{
u32 reg ;
2014-07-05 01:43:20 +04:00
reg = readl_relaxed ( reg_base + EXYNOS4_MCT_G_TCON ) ;
2011-03-11 04:39:57 +03:00
reg | = MCT_G_TCON_START ;
exynos4_mct_write ( reg , EXYNOS4_MCT_G_TCON ) ;
}
2014-07-05 01:43:26 +04:00
/**
* exynos4_read_count_64 - Read all 64 - bits of the global counter
*
* This will read all 64 - bits of the global counter taking care to make sure
* that the upper and lower half match . Note that reading the MCT can be quite
* slow ( hundreds of nanoseconds ) so you should use the 32 - bit ( lower half
* only ) version when possible .
*
* Returns the number of cycles in the global counter .
*/
static u64 exynos4_read_count_64 ( void )
2011-03-11 04:39:57 +03:00
{
unsigned int lo , hi ;
2014-07-05 01:43:20 +04:00
u32 hi2 = readl_relaxed ( reg_base + EXYNOS4_MCT_G_CNT_U ) ;
2011-03-11 04:39:57 +03:00
do {
hi = hi2 ;
2014-07-05 01:43:20 +04:00
lo = readl_relaxed ( reg_base + EXYNOS4_MCT_G_CNT_L ) ;
hi2 = readl_relaxed ( reg_base + EXYNOS4_MCT_G_CNT_U ) ;
2011-03-11 04:39:57 +03:00
} while ( hi ! = hi2 ) ;
2016-12-21 22:32:01 +03:00
return ( ( u64 ) hi < < 32 ) | lo ;
2011-03-11 04:39:57 +03:00
}
2014-07-05 01:43:26 +04:00
/**
* exynos4_read_count_32 - Read the lower 32 - bits of the global counter
*
* This will read just the lower 32 - bits of the global counter . This is marked
* as notrace so it can be used by the scheduler clock .
*
* Returns the number of cycles in the global counter ( lower 32 bits ) .
*/
static u32 notrace exynos4_read_count_32 ( void )
{
return readl_relaxed ( reg_base + EXYNOS4_MCT_G_CNT_L ) ;
}
2016-12-21 22:32:01 +03:00
static u64 exynos4_frc_read ( struct clocksource * cs )
2014-07-05 01:38:55 +04:00
{
2014-07-05 01:43:26 +04:00
return exynos4_read_count_32 ( ) ;
2014-07-05 01:38:55 +04:00
}
2011-09-02 09:10:52 +04:00
static void exynos4_frc_resume ( struct clocksource * cs )
{
2014-06-11 19:18:48 +04:00
exynos4_mct_frc_start ( ) ;
2011-09-02 09:10:52 +04:00
}
2015-04-30 07:42:52 +03:00
static struct clocksource mct_frc = {
2011-03-11 04:39:57 +03:00
. name = " mct-frc " ,
. rating = 400 ,
. read = exynos4_frc_read ,
2014-07-05 01:43:26 +04:00
. mask = CLOCKSOURCE_MASK ( 32 ) ,
2011-03-11 04:39:57 +03:00
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
2011-09-02 09:10:52 +04:00
. resume = exynos4_frc_resume ,
2011-03-11 04:39:57 +03:00
} ;
2014-05-02 17:27:01 +04:00
static u64 notrace exynos4_read_sched_clock ( void )
{
2014-07-05 01:43:26 +04:00
return exynos4_read_count_32 ( ) ;
2014-05-02 17:27:01 +04:00
}
2016-08-24 16:49:05 +03:00
# if defined(CONFIG_ARM)
2014-07-05 01:40:23 +04:00
static struct delay_timer exynos4_delay_timer ;
static cycles_t exynos4_read_current_timer ( void )
{
2014-07-05 01:43:26 +04:00
BUILD_BUG_ON_MSG ( sizeof ( cycles_t ) ! = sizeof ( u32 ) ,
" cycles_t needs to move to 32-bit for ARM64 usage " ) ;
return exynos4_read_count_32 ( ) ;
2014-07-05 01:40:23 +04:00
}
2016-08-24 16:49:05 +03:00
# endif
2014-07-05 01:40:23 +04:00
2016-05-31 20:26:55 +03:00
static int __init exynos4_clocksource_init ( void )
2011-03-11 04:39:57 +03:00
{
2014-06-11 19:18:48 +04:00
exynos4_mct_frc_start ( ) ;
2011-03-11 04:39:57 +03:00
2016-08-24 16:49:05 +03:00
# if defined(CONFIG_ARM)
2014-07-05 01:40:23 +04:00
exynos4_delay_timer . read_current_timer = & exynos4_read_current_timer ;
exynos4_delay_timer . freq = clk_rate ;
register_current_timer_delay ( & exynos4_delay_timer ) ;
2016-08-24 16:49:05 +03:00
# endif
2014-07-05 01:40:23 +04:00
2011-03-11 04:39:57 +03:00
if ( clocksource_register_hz ( & mct_frc , clk_rate ) )
panic ( " %s: can't register clocksource \n " , mct_frc . name ) ;
2014-05-02 17:27:01 +04:00
2014-07-05 01:43:26 +04:00
sched_clock_register ( exynos4_read_sched_clock , 32 , clk_rate ) ;
2016-05-31 20:26:55 +03:00
return 0 ;
2011-03-11 04:39:57 +03:00
}
static void exynos4_mct_comp0_stop ( void )
{
unsigned int tcon ;
2014-07-05 01:43:20 +04:00
tcon = readl_relaxed ( reg_base + EXYNOS4_MCT_G_TCON ) ;
2011-03-11 04:39:57 +03:00
tcon & = ~ ( MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC ) ;
exynos4_mct_write ( tcon , EXYNOS4_MCT_G_TCON ) ;
exynos4_mct_write ( 0 , EXYNOS4_MCT_G_INT_ENB ) ;
}
2015-06-18 13:54:20 +03:00
static void exynos4_mct_comp0_start ( bool periodic , unsigned long cycles )
2011-03-11 04:39:57 +03:00
{
unsigned int tcon ;
2016-12-21 22:32:01 +03:00
u64 comp_cycle ;
2011-03-11 04:39:57 +03:00
2014-07-05 01:43:20 +04:00
tcon = readl_relaxed ( reg_base + EXYNOS4_MCT_G_TCON ) ;
2011-03-11 04:39:57 +03:00
2015-06-18 13:54:20 +03:00
if ( periodic ) {
2011-03-11 04:39:57 +03:00
tcon | = MCT_G_TCON_COMP0_AUTO_INC ;
exynos4_mct_write ( cycles , EXYNOS4_MCT_G_COMP0_ADD_INCR ) ;
}
2014-07-05 01:43:26 +04:00
comp_cycle = exynos4_read_count_64 ( ) + cycles ;
2011-03-11 04:39:57 +03:00
exynos4_mct_write ( ( u32 ) comp_cycle , EXYNOS4_MCT_G_COMP0_L ) ;
exynos4_mct_write ( ( u32 ) ( comp_cycle > > 32 ) , EXYNOS4_MCT_G_COMP0_U ) ;
exynos4_mct_write ( 0x1 , EXYNOS4_MCT_G_INT_ENB ) ;
tcon | = MCT_G_TCON_COMP0_ENABLE ;
exynos4_mct_write ( tcon , EXYNOS4_MCT_G_TCON ) ;
}
static int exynos4_comp_set_next_event ( unsigned long cycles ,
struct clock_event_device * evt )
{
2015-06-18 13:54:20 +03:00
exynos4_mct_comp0_start ( false , cycles ) ;
2011-03-11 04:39:57 +03:00
return 0 ;
}
2015-06-18 13:54:20 +03:00
static int mct_set_state_shutdown ( struct clock_event_device * evt )
2011-03-11 04:39:57 +03:00
{
exynos4_mct_comp0_stop ( ) ;
2015-06-18 13:54:20 +03:00
return 0 ;
}
2011-03-11 04:39:57 +03:00
2015-06-18 13:54:20 +03:00
static int mct_set_state_periodic ( struct clock_event_device * evt )
{
unsigned long cycles_per_jiffy ;
cycles_per_jiffy = ( ( ( unsigned long long ) NSEC_PER_SEC / HZ * evt - > mult )
> > evt - > shift ) ;
exynos4_mct_comp0_stop ( ) ;
exynos4_mct_comp0_start ( true , cycles_per_jiffy ) ;
return 0 ;
2011-03-11 04:39:57 +03:00
}
static struct clock_event_device mct_comp_device = {
2015-06-18 13:54:20 +03:00
. name = " mct-comp " ,
. features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT ,
. rating = 250 ,
. set_next_event = exynos4_comp_set_next_event ,
. set_state_periodic = mct_set_state_periodic ,
. set_state_shutdown = mct_set_state_shutdown ,
. set_state_oneshot = mct_set_state_shutdown ,
2015-12-23 14:29:14 +03:00
. set_state_oneshot_stopped = mct_set_state_shutdown ,
2015-06-18 13:54:20 +03:00
. tick_resume = mct_set_state_shutdown ,
2011-03-11 04:39:57 +03:00
} ;
static irqreturn_t exynos4_mct_comp_isr ( int irq , void * dev_id )
{
struct clock_event_device * evt = dev_id ;
exynos4_mct_write ( 0x1 , EXYNOS4_MCT_G_INT_CSTAT ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static struct irqaction mct_comp_event_irq = {
. name = " mct_comp_irq " ,
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
. handler = exynos4_mct_comp_isr ,
. dev_id = & mct_comp_device ,
} ;
2016-05-31 20:26:55 +03:00
static int exynos4_clockevent_init ( void )
2011-03-11 04:39:57 +03:00
{
mct_comp_device . cpumask = cpumask_of ( 0 ) ;
2013-01-12 15:50:05 +04:00
clockevents_config_and_register ( & mct_comp_device , clk_rate ,
0xf , 0xffffffff ) ;
2013-03-09 11:01:50 +04:00
setup_irq ( mct_irqs [ MCT_G0_IRQ ] , & mct_comp_event_irq ) ;
2016-05-31 20:26:55 +03:00
return 0 ;
2011-03-11 04:39:57 +03:00
}
2011-12-08 05:04:49 +04:00
static DEFINE_PER_CPU ( struct mct_clock_event_device , percpu_mct_tick ) ;
2011-03-11 04:39:57 +03:00
/* Clock event handling */
static void exynos4_mct_tick_stop ( struct mct_clock_event_device * mevt )
{
unsigned long tmp ;
unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START ;
2013-03-09 11:01:47 +04:00
unsigned long offset = mevt - > base + MCT_L_TCON_OFFSET ;
2011-03-11 04:39:57 +03:00
2014-07-05 01:43:20 +04:00
tmp = readl_relaxed ( reg_base + offset ) ;
2011-03-11 04:39:57 +03:00
if ( tmp & mask ) {
tmp & = ~ mask ;
2013-03-09 11:01:47 +04:00
exynos4_mct_write ( tmp , offset ) ;
2011-03-11 04:39:57 +03:00
}
}
static void exynos4_mct_tick_start ( unsigned long cycles ,
struct mct_clock_event_device * mevt )
{
unsigned long tmp ;
exynos4_mct_tick_stop ( mevt ) ;
tmp = ( 1 < < 31 ) | cycles ; /* MCT_L_UPDATE_ICNTB */
/* update interrupt count buffer */
exynos4_mct_write ( tmp , mevt - > base + MCT_L_ICNTB_OFFSET ) ;
2011-03-31 05:57:33 +04:00
/* enable MCT tick interrupt */
2011-03-11 04:39:57 +03:00
exynos4_mct_write ( 0x1 , mevt - > base + MCT_L_INT_ENB_OFFSET ) ;
2014-07-05 01:43:20 +04:00
tmp = readl_relaxed ( reg_base + mevt - > base + MCT_L_TCON_OFFSET ) ;
2011-03-11 04:39:57 +03:00
tmp | = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
MCT_L_TCON_INTERVAL_MODE ;
exynos4_mct_write ( tmp , mevt - > base + MCT_L_TCON_OFFSET ) ;
}
static int exynos4_tick_set_next_event ( unsigned long cycles ,
struct clock_event_device * evt )
{
clocksource/drivers/exynos_mct: Use container_of() instead of this_cpu_ptr()
Since evt structure is embedded in per-CPU mevt structure it's
definitely faster to use container_of() to get access to mevt
if we have evt (for example as incoming function argument) instead
of more expensive approach with this_cpu_ptr(&percpu_mct_tick).
this_cpu_ptr() on per-CPU mevt structure leads to access to cp15
to get cpu id and arithmetic operations.
Container_of() is cheaper since it's just one asm instruction.
This should work if used evt pointer is correct and owned by
local mevt structure.
For example, before this patch set_state_shutdown() looks like:
4a4: e92d4010 push {r4, lr}
4a8: e3004000 movw r4, #0
4ac: ebfffffe bl 0 <debug_smp_processor_id>
4b0: e3003000 movw r3, #0
4b4: e3404000 movt r4, #0
4b8: e3403000 movt r3, #0
4bc: e7933100 ldr r3, [r3, r0, lsl #2]
4c0: e0844003 add r4, r4, r3
4c4: e59400c0 ldr r0, [r4, #192] ; 0xc0
4c8: ebffffd4 bl 420 <exynos4_mct_tick_stop.isra.1>
4cc: e3a00000 mov r0, #0
4d0: e8bd8010 pop {r4, pc}
With this patch:
4a4: e92d4010 push {r4, lr}
4a8: e59000c0 ldr r0, [r0, #192] ; 0xc0
4ac: ebffffdb bl 420 <exynos4_mct_tick_stop.isra.1>
4b0: e3a00000 mov r0, #0
4b4: e8bd8010 pop {r4, pc}
Also, for me size of exynos_mct.o decreased from 84588 bytes
to 83956.
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
2015-09-04 02:49:58 +03:00
struct mct_clock_event_device * mevt ;
2011-03-11 04:39:57 +03:00
clocksource/drivers/exynos_mct: Use container_of() instead of this_cpu_ptr()
Since evt structure is embedded in per-CPU mevt structure it's
definitely faster to use container_of() to get access to mevt
if we have evt (for example as incoming function argument) instead
of more expensive approach with this_cpu_ptr(&percpu_mct_tick).
this_cpu_ptr() on per-CPU mevt structure leads to access to cp15
to get cpu id and arithmetic operations.
Container_of() is cheaper since it's just one asm instruction.
This should work if used evt pointer is correct and owned by
local mevt structure.
For example, before this patch set_state_shutdown() looks like:
4a4: e92d4010 push {r4, lr}
4a8: e3004000 movw r4, #0
4ac: ebfffffe bl 0 <debug_smp_processor_id>
4b0: e3003000 movw r3, #0
4b4: e3404000 movt r4, #0
4b8: e3403000 movt r3, #0
4bc: e7933100 ldr r3, [r3, r0, lsl #2]
4c0: e0844003 add r4, r4, r3
4c4: e59400c0 ldr r0, [r4, #192] ; 0xc0
4c8: ebffffd4 bl 420 <exynos4_mct_tick_stop.isra.1>
4cc: e3a00000 mov r0, #0
4d0: e8bd8010 pop {r4, pc}
With this patch:
4a4: e92d4010 push {r4, lr}
4a8: e59000c0 ldr r0, [r0, #192] ; 0xc0
4ac: ebffffdb bl 420 <exynos4_mct_tick_stop.isra.1>
4b0: e3a00000 mov r0, #0
4b4: e8bd8010 pop {r4, pc}
Also, for me size of exynos_mct.o decreased from 84588 bytes
to 83956.
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
2015-09-04 02:49:58 +03:00
mevt = container_of ( evt , struct mct_clock_event_device , evt ) ;
2011-03-11 04:39:57 +03:00
exynos4_mct_tick_start ( cycles , mevt ) ;
return 0 ;
}
2015-06-18 13:54:20 +03:00
static int set_state_shutdown ( struct clock_event_device * evt )
{
clocksource/drivers/exynos_mct: Use container_of() instead of this_cpu_ptr()
Since evt structure is embedded in per-CPU mevt structure it's
definitely faster to use container_of() to get access to mevt
if we have evt (for example as incoming function argument) instead
of more expensive approach with this_cpu_ptr(&percpu_mct_tick).
this_cpu_ptr() on per-CPU mevt structure leads to access to cp15
to get cpu id and arithmetic operations.
Container_of() is cheaper since it's just one asm instruction.
This should work if used evt pointer is correct and owned by
local mevt structure.
For example, before this patch set_state_shutdown() looks like:
4a4: e92d4010 push {r4, lr}
4a8: e3004000 movw r4, #0
4ac: ebfffffe bl 0 <debug_smp_processor_id>
4b0: e3003000 movw r3, #0
4b4: e3404000 movt r4, #0
4b8: e3403000 movt r3, #0
4bc: e7933100 ldr r3, [r3, r0, lsl #2]
4c0: e0844003 add r4, r4, r3
4c4: e59400c0 ldr r0, [r4, #192] ; 0xc0
4c8: ebffffd4 bl 420 <exynos4_mct_tick_stop.isra.1>
4cc: e3a00000 mov r0, #0
4d0: e8bd8010 pop {r4, pc}
With this patch:
4a4: e92d4010 push {r4, lr}
4a8: e59000c0 ldr r0, [r0, #192] ; 0xc0
4ac: ebffffdb bl 420 <exynos4_mct_tick_stop.isra.1>
4b0: e3a00000 mov r0, #0
4b4: e8bd8010 pop {r4, pc}
Also, for me size of exynos_mct.o decreased from 84588 bytes
to 83956.
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
2015-09-04 02:49:58 +03:00
struct mct_clock_event_device * mevt ;
mevt = container_of ( evt , struct mct_clock_event_device , evt ) ;
exynos4_mct_tick_stop ( mevt ) ;
2015-06-18 13:54:20 +03:00
return 0 ;
}
static int set_state_periodic ( struct clock_event_device * evt )
2011-03-11 04:39:57 +03:00
{
clocksource/drivers/exynos_mct: Use container_of() instead of this_cpu_ptr()
Since evt structure is embedded in per-CPU mevt structure it's
definitely faster to use container_of() to get access to mevt
if we have evt (for example as incoming function argument) instead
of more expensive approach with this_cpu_ptr(&percpu_mct_tick).
this_cpu_ptr() on per-CPU mevt structure leads to access to cp15
to get cpu id and arithmetic operations.
Container_of() is cheaper since it's just one asm instruction.
This should work if used evt pointer is correct and owned by
local mevt structure.
For example, before this patch set_state_shutdown() looks like:
4a4: e92d4010 push {r4, lr}
4a8: e3004000 movw r4, #0
4ac: ebfffffe bl 0 <debug_smp_processor_id>
4b0: e3003000 movw r3, #0
4b4: e3404000 movt r4, #0
4b8: e3403000 movt r3, #0
4bc: e7933100 ldr r3, [r3, r0, lsl #2]
4c0: e0844003 add r4, r4, r3
4c4: e59400c0 ldr r0, [r4, #192] ; 0xc0
4c8: ebffffd4 bl 420 <exynos4_mct_tick_stop.isra.1>
4cc: e3a00000 mov r0, #0
4d0: e8bd8010 pop {r4, pc}
With this patch:
4a4: e92d4010 push {r4, lr}
4a8: e59000c0 ldr r0, [r0, #192] ; 0xc0
4ac: ebffffdb bl 420 <exynos4_mct_tick_stop.isra.1>
4b0: e3a00000 mov r0, #0
4b4: e8bd8010 pop {r4, pc}
Also, for me size of exynos_mct.o decreased from 84588 bytes
to 83956.
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
2015-09-04 02:49:58 +03:00
struct mct_clock_event_device * mevt ;
2012-03-10 03:09:21 +04:00
unsigned long cycles_per_jiffy ;
2011-03-11 04:39:57 +03:00
clocksource/drivers/exynos_mct: Use container_of() instead of this_cpu_ptr()
Since evt structure is embedded in per-CPU mevt structure it's
definitely faster to use container_of() to get access to mevt
if we have evt (for example as incoming function argument) instead
of more expensive approach with this_cpu_ptr(&percpu_mct_tick).
this_cpu_ptr() on per-CPU mevt structure leads to access to cp15
to get cpu id and arithmetic operations.
Container_of() is cheaper since it's just one asm instruction.
This should work if used evt pointer is correct and owned by
local mevt structure.
For example, before this patch set_state_shutdown() looks like:
4a4: e92d4010 push {r4, lr}
4a8: e3004000 movw r4, #0
4ac: ebfffffe bl 0 <debug_smp_processor_id>
4b0: e3003000 movw r3, #0
4b4: e3404000 movt r4, #0
4b8: e3403000 movt r3, #0
4bc: e7933100 ldr r3, [r3, r0, lsl #2]
4c0: e0844003 add r4, r4, r3
4c4: e59400c0 ldr r0, [r4, #192] ; 0xc0
4c8: ebffffd4 bl 420 <exynos4_mct_tick_stop.isra.1>
4cc: e3a00000 mov r0, #0
4d0: e8bd8010 pop {r4, pc}
With this patch:
4a4: e92d4010 push {r4, lr}
4a8: e59000c0 ldr r0, [r0, #192] ; 0xc0
4ac: ebffffdb bl 420 <exynos4_mct_tick_stop.isra.1>
4b0: e3a00000 mov r0, #0
4b4: e8bd8010 pop {r4, pc}
Also, for me size of exynos_mct.o decreased from 84588 bytes
to 83956.
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
2015-09-04 02:49:58 +03:00
mevt = container_of ( evt , struct mct_clock_event_device , evt ) ;
2015-06-18 13:54:20 +03:00
cycles_per_jiffy = ( ( ( unsigned long long ) NSEC_PER_SEC / HZ * evt - > mult )
> > evt - > shift ) ;
2011-03-11 04:39:57 +03:00
exynos4_mct_tick_stop ( mevt ) ;
2015-06-18 13:54:20 +03:00
exynos4_mct_tick_start ( cycles_per_jiffy , mevt ) ;
return 0 ;
2011-03-11 04:39:57 +03:00
}
2015-04-30 07:42:51 +03:00
static void exynos4_mct_tick_clear ( struct mct_clock_event_device * mevt )
2011-03-11 04:39:57 +03:00
{
/*
* This is for supporting oneshot mode .
* Mct would generate interrupt periodically
* without explicit stopping .
*/
2015-06-18 13:54:20 +03:00
if ( ! clockevent_state_periodic ( & mevt - > evt ) )
2011-03-11 04:39:57 +03:00
exynos4_mct_tick_stop ( mevt ) ;
/* Clear the MCT tick interrupt */
2015-04-30 07:42:51 +03:00
if ( readl_relaxed ( reg_base + mevt - > base + MCT_L_INT_CSTAT_OFFSET ) & 1 )
2011-10-04 12:02:58 +04:00
exynos4_mct_write ( 0x1 , mevt - > base + MCT_L_INT_CSTAT_OFFSET ) ;
}
static irqreturn_t exynos4_mct_tick_isr ( int irq , void * dev_id )
{
struct mct_clock_event_device * mevt = dev_id ;
2013-02-16 04:40:51 +04:00
struct clock_event_device * evt = & mevt - > evt ;
2011-10-04 12:02:58 +04:00
exynos4_mct_tick_clear ( mevt ) ;
2011-03-11 04:39:57 +03:00
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
2016-07-13 20:17:05 +03:00
static int exynos4_mct_starting_cpu ( unsigned int cpu )
2011-03-11 04:39:57 +03:00
{
2016-07-13 20:17:05 +03:00
struct mct_clock_event_device * mevt =
per_cpu_ptr ( & percpu_mct_tick , cpu ) ;
2015-06-21 23:41:39 +03:00
struct clock_event_device * evt = & mevt - > evt ;
2011-03-11 04:39:57 +03:00
2011-11-03 06:13:12 +04:00
mevt - > base = EXYNOS4_MCT_L_BASE ( cpu ) ;
2014-03-01 17:57:14 +04:00
snprintf ( mevt - > name , sizeof ( mevt - > name ) , " mct_tick%d " , cpu ) ;
2011-03-11 04:39:57 +03:00
2011-11-03 06:13:12 +04:00
evt - > name = mevt - > name ;
2011-03-11 04:39:57 +03:00
evt - > cpumask = cpumask_of ( cpu ) ;
evt - > set_next_event = exynos4_tick_set_next_event ;
2015-06-18 13:54:20 +03:00
evt - > set_state_periodic = set_state_periodic ;
evt - > set_state_shutdown = set_state_shutdown ;
evt - > set_state_oneshot = set_state_shutdown ;
2015-12-23 14:29:14 +03:00
evt - > set_state_oneshot_stopped = set_state_shutdown ;
2015-06-18 13:54:20 +03:00
evt - > tick_resume = set_state_shutdown ;
2011-03-11 04:39:57 +03:00
evt - > features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ;
evt - > rating = 450 ;
2012-03-10 03:09:21 +04:00
exynos4_mct_write ( TICK_BASE_CNT , mevt - > base + MCT_L_TCNTB_OFFSET ) ;
2011-03-11 04:39:57 +03:00
2011-10-04 12:02:58 +04:00
if ( mct_int_type = = MCT_INT_SPI ) {
2015-06-26 16:23:04 +03:00
if ( evt - > irq = = - 1 )
2013-06-18 19:29:35 +04:00
return - EIO ;
2015-06-26 16:23:04 +03:00
irq_force_affinity ( evt - > irq , cpumask_of ( cpu ) ) ;
enable_irq ( evt - > irq ) ;
2011-03-11 04:39:57 +03:00
} else {
2013-03-09 11:01:50 +04:00
enable_percpu_irq ( mct_irqs [ MCT_L0_IRQ ] , 0 ) ;
2011-03-11 04:39:57 +03:00
}
2014-04-16 18:36:45 +04:00
clockevents_config_and_register ( evt , clk_rate / ( TICK_BASE_CNT + 1 ) ,
0xf , 0x7fffffff ) ;
2011-08-24 11:07:39 +04:00
return 0 ;
2011-03-11 04:39:57 +03:00
}
2016-07-13 20:17:05 +03:00
static int exynos4_mct_dying_cpu ( unsigned int cpu )
2011-03-11 04:39:57 +03:00
{
2016-07-13 20:17:05 +03:00
struct mct_clock_event_device * mevt =
per_cpu_ptr ( & percpu_mct_tick , cpu ) ;
2015-06-21 23:41:39 +03:00
struct clock_event_device * evt = & mevt - > evt ;
2015-06-18 13:54:20 +03:00
evt - > set_state_shutdown ( evt ) ;
2015-06-26 16:23:04 +03:00
if ( mct_int_type = = MCT_INT_SPI ) {
if ( evt - > irq ! = - 1 )
disable_irq_nosync ( evt - > irq ) ;
2017-01-17 07:54:36 +03:00
exynos4_mct_write ( 0x1 , mevt - > base + MCT_L_INT_CSTAT_OFFSET ) ;
2015-06-26 16:23:04 +03:00
} else {
2013-03-09 11:01:50 +04:00
disable_percpu_irq ( mct_irqs [ MCT_L0_IRQ ] ) ;
2015-06-26 16:23:04 +03:00
}
2016-07-13 20:17:05 +03:00
return 0 ;
2011-03-11 04:39:57 +03:00
}
2012-01-10 23:44:19 +04:00
2016-05-31 20:26:55 +03:00
static int __init exynos4_timer_resources ( struct device_node * np , void __iomem * base )
2011-03-11 04:39:57 +03:00
{
2015-06-26 16:23:04 +03:00
int err , cpu ;
2013-03-09 12:10:37 +04:00
struct clk * mct_clk , * tick_clk ;
2011-03-11 04:39:57 +03:00
2013-03-09 12:10:31 +04:00
tick_clk = np ? of_clk_get_by_name ( np , " fin_pll " ) :
clk_get ( NULL , " fin_pll " ) ;
if ( IS_ERR ( tick_clk ) )
panic ( " %s: unable to determine tick clock rate \n " , __func__ ) ;
clk_rate = clk_get_rate ( tick_clk ) ;
2011-11-03 06:13:12 +04:00
2013-03-09 12:10:37 +04:00
mct_clk = np ? of_clk_get_by_name ( np , " mct " ) : clk_get ( NULL , " mct " ) ;
if ( IS_ERR ( mct_clk ) )
panic ( " %s: unable to retrieve mct clock instance \n " , __func__ ) ;
clk_prepare_enable ( mct_clk ) ;
2011-11-03 06:13:12 +04:00
2013-04-10 00:07:37 +04:00
reg_base = base ;
2013-03-09 11:01:52 +04:00
if ( ! reg_base )
panic ( " %s: unable to ioremap mct address space \n " , __func__ ) ;
2013-03-09 11:01:47 +04:00
2011-11-03 06:13:12 +04:00
if ( mct_int_type = = MCT_INT_PPI ) {
2013-03-09 11:01:50 +04:00
err = request_percpu_irq ( mct_irqs [ MCT_L0_IRQ ] ,
2011-11-03 06:13:12 +04:00
exynos4_mct_tick_isr , " MCT " ,
& percpu_mct_tick ) ;
WARN ( err , " MCT: can't request IRQ %d (%d) \n " ,
2013-03-09 11:01:50 +04:00
mct_irqs [ MCT_L0_IRQ ] , err ) ;
2013-09-25 14:00:59 +04:00
} else {
2015-06-26 16:23:04 +03:00
for_each_possible_cpu ( cpu ) {
int mct_irq = mct_irqs [ MCT_L0_IRQ + cpu ] ;
struct mct_clock_event_device * pcpu_mevt =
per_cpu_ptr ( & percpu_mct_tick , cpu ) ;
pcpu_mevt - > evt . irq = - 1 ;
irq_set_status_flags ( mct_irq , IRQ_NOAUTOEN ) ;
if ( request_irq ( mct_irq ,
exynos4_mct_tick_isr ,
IRQF_TIMER | IRQF_NOBALANCING ,
pcpu_mevt - > name , pcpu_mevt ) ) {
pr_err ( " exynos-mct: cannot register IRQ (cpu%d) \n " ,
cpu ) ;
continue ;
}
pcpu_mevt - > evt . irq = mct_irq ;
}
2011-11-03 06:13:12 +04:00
}
2012-01-10 23:44:19 +04:00
2016-07-13 20:17:05 +03:00
/* Install hotplug callbacks which configure the timer on this CPU */
err = cpuhp_setup_state ( CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING ,
2016-12-21 22:19:54 +03:00
" clockevents/exynos4/mct_timer:starting " ,
2016-07-13 20:17:05 +03:00
exynos4_mct_starting_cpu ,
exynos4_mct_dying_cpu ) ;
2013-02-16 04:40:51 +04:00
if ( err )
goto out_irq ;
2016-05-31 20:26:55 +03:00
return 0 ;
2013-02-16 04:40:51 +04:00
out_irq :
free_percpu_irq ( mct_irqs [ MCT_L0_IRQ ] , & percpu_mct_tick ) ;
2016-05-31 20:26:55 +03:00
return err ;
2011-03-11 04:39:57 +03:00
}
2016-05-31 20:26:55 +03:00
static int __init mct_init_dt ( struct device_node * np , unsigned int int_type )
2013-04-10 00:07:37 +04:00
{
u32 nr_irqs , i ;
2016-05-31 20:26:55 +03:00
int ret ;
2013-04-10 00:07:37 +04:00
mct_int_type = int_type ;
/* This driver uses only one global timer interrupt */
mct_irqs [ MCT_G0_IRQ ] = irq_of_parse_and_map ( np , MCT_G0_IRQ ) ;
/*
* Find out the number of local irqs specified . The local
* timer irqs are specified after the four global timer
* irqs are specified .
*/
2013-04-20 00:00:04 +04:00
# ifdef CONFIG_OF
2013-04-10 00:07:37 +04:00
nr_irqs = of_irq_count ( np ) ;
2013-04-20 00:00:04 +04:00
# else
nr_irqs = 0 ;
# endif
2013-04-10 00:07:37 +04:00
for ( i = MCT_L0_IRQ ; i < nr_irqs ; i + + )
mct_irqs [ i ] = irq_of_parse_and_map ( np , i ) ;
2016-05-31 20:26:55 +03:00
ret = exynos4_timer_resources ( np , of_iomap ( np , 0 ) ) ;
if ( ret )
return ret ;
ret = exynos4_clocksource_init ( ) ;
if ( ret )
return ret ;
return exynos4_clockevent_init ( ) ;
2011-03-11 04:39:57 +03:00
}
2013-04-10 00:07:37 +04:00
2016-05-31 20:26:55 +03:00
static int __init mct_init_spi ( struct device_node * np )
2013-04-10 00:07:37 +04:00
{
return mct_init_dt ( np , MCT_INT_SPI ) ;
}
2016-05-31 20:26:55 +03:00
static int __init mct_init_ppi ( struct device_node * np )
2013-04-10 00:07:37 +04:00
{
return mct_init_dt ( np , MCT_INT_PPI ) ;
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( exynos4210 , " samsung,exynos4210-mct " , mct_init_spi ) ;
TIMER_OF_DECLARE ( exynos4412 , " samsung,exynos4412-mct " , mct_init_ppi ) ;