2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-06-06 15:43:07 +04:00
/*
* ( C ) Copyright 2009 Intel Corporation
* Author : Jacob Pan ( jacob . jun . pan @ intel . com )
*
* Shared with ARM platforms , Jamie Iles , Picochip 2011
*
* Support for the Synopsys DesignWare APB Timers .
*/
# include <linux/dw_apb_timer.h>
# include <linux/delay.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/io.h>
# include <linux/slab.h>
# define APBT_MIN_PERIOD 4
# define APBT_MIN_DELTA_USEC 200
2013-06-18 06:34:57 +04:00
# define APBTMR_N_LOAD_COUNT 0x00
# define APBTMR_N_CURRENT_VALUE 0x04
# define APBTMR_N_CONTROL 0x08
# define APBTMR_N_EOI 0x0c
# define APBTMR_N_INT_STATUS 0x10
2011-06-06 15:43:07 +04:00
# define APBTMRS_INT_STATUS 0xa0
# define APBTMRS_EOI 0xa4
# define APBTMRS_RAW_INT_STATUS 0xa8
# define APBTMRS_COMP_VERSION 0xac
# define APBTMR_CONTROL_ENABLE (1 << 0)
/* 1: periodic, 0:free running. */
# define APBTMR_CONTROL_MODE_PERIODIC (1 << 1)
# define APBTMR_CONTROL_INT (1 << 2)
static inline struct dw_apb_clock_event_device *
ced_to_dw_apb_ced ( struct clock_event_device * evt )
{
return container_of ( evt , struct dw_apb_clock_event_device , ced ) ;
}
static inline struct dw_apb_clocksource *
clocksource_to_dw_apb_clocksource ( struct clocksource * cs )
{
return container_of ( cs , struct dw_apb_clocksource , cs ) ;
}
2015-11-25 19:01:52 +03:00
static inline u32 apbt_readl ( struct dw_apb_timer * timer , unsigned long offs )
2011-06-06 15:43:07 +04:00
{
return readl ( timer - > base + offs ) ;
}
2015-11-25 19:01:52 +03:00
static inline void apbt_writel ( struct dw_apb_timer * timer , u32 val ,
unsigned long offs )
2011-06-06 15:43:07 +04:00
{
writel ( val , timer - > base + offs ) ;
}
2015-11-25 19:01:53 +03:00
static inline u32 apbt_readl_relaxed ( struct dw_apb_timer * timer , unsigned long offs )
{
return readl_relaxed ( timer - > base + offs ) ;
}
static inline void apbt_writel_relaxed ( struct dw_apb_timer * timer , u32 val ,
unsigned long offs )
{
writel_relaxed ( val , timer - > base + offs ) ;
}
2011-06-06 15:43:07 +04:00
static void apbt_disable_int ( struct dw_apb_timer * timer )
{
2015-11-25 18:41:23 +03:00
u32 ctrl = apbt_readl ( timer , APBTMR_N_CONTROL ) ;
2011-06-06 15:43:07 +04:00
ctrl | = APBTMR_CONTROL_INT ;
apbt_writel ( timer , ctrl , APBTMR_N_CONTROL ) ;
}
/**
* dw_apb_clockevent_pause ( ) - stop the clock_event_device from running
*
* @ dw_ced : The APB clock to stop generating events .
*/
void dw_apb_clockevent_pause ( struct dw_apb_clock_event_device * dw_ced )
{
disable_irq ( dw_ced - > timer . irq ) ;
apbt_disable_int ( & dw_ced - > timer ) ;
}
static void apbt_eoi ( struct dw_apb_timer * timer )
{
2015-11-25 19:01:53 +03:00
apbt_readl_relaxed ( timer , APBTMR_N_EOI ) ;
2011-06-06 15:43:07 +04:00
}
static irqreturn_t dw_apb_clockevent_irq ( int irq , void * data )
{
struct clock_event_device * evt = data ;
struct dw_apb_clock_event_device * dw_ced = ced_to_dw_apb_ced ( evt ) ;
if ( ! evt - > event_handler ) {
2017-03-09 12:47:10 +03:00
pr_info ( " Spurious APBT timer interrupt %d \n " , irq ) ;
2011-06-06 15:43:07 +04:00
return IRQ_NONE ;
}
if ( dw_ced - > eoi )
dw_ced - > eoi ( & dw_ced - > timer ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static void apbt_enable_int ( struct dw_apb_timer * timer )
{
2015-11-25 18:41:23 +03:00
u32 ctrl = apbt_readl ( timer , APBTMR_N_CONTROL ) ;
2011-06-06 15:43:07 +04:00
/* clear pending intr */
apbt_readl ( timer , APBTMR_N_EOI ) ;
ctrl & = ~ APBTMR_CONTROL_INT ;
apbt_writel ( timer , ctrl , APBTMR_N_CONTROL ) ;
}
2015-06-18 13:54:19 +03:00
static int apbt_shutdown ( struct clock_event_device * evt )
2011-06-06 15:43:07 +04:00
{
2015-06-18 13:54:19 +03:00
struct dw_apb_clock_event_device * dw_ced = ced_to_dw_apb_ced ( evt ) ;
2015-11-25 18:41:23 +03:00
u32 ctrl ;
2015-06-18 13:54:19 +03:00
pr_debug ( " %s CPU %d state=shutdown \n " , __func__ ,
cpumask_first ( evt - > cpumask ) ) ;
ctrl = apbt_readl ( & dw_ced - > timer , APBTMR_N_CONTROL ) ;
ctrl & = ~ APBTMR_CONTROL_ENABLE ;
apbt_writel ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
return 0 ;
}
static int apbt_set_oneshot ( struct clock_event_device * evt )
{
2011-06-06 15:43:07 +04:00
struct dw_apb_clock_event_device * dw_ced = ced_to_dw_apb_ced ( evt ) ;
2015-11-25 18:41:23 +03:00
u32 ctrl ;
2011-06-06 15:43:07 +04:00
2015-06-18 13:54:19 +03:00
pr_debug ( " %s CPU %d state=oneshot \n " , __func__ ,
cpumask_first ( evt - > cpumask ) ) ;
ctrl = apbt_readl ( & dw_ced - > timer , APBTMR_N_CONTROL ) ;
/*
* set free running mode , this mode will let timer reload max
* timeout which will give time ( 3 min on 25 MHz clock ) to rearm
* the next event , therefore emulate the one - shot mode .
*/
ctrl & = ~ APBTMR_CONTROL_ENABLE ;
ctrl & = ~ APBTMR_CONTROL_MODE_PERIODIC ;
apbt_writel ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
/* write again to set free running mode */
apbt_writel ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
/*
* DW APB p . 46 , load counter with all 1 s before starting free
* running mode .
*/
apbt_writel ( & dw_ced - > timer , ~ 0 , APBTMR_N_LOAD_COUNT ) ;
ctrl & = ~ APBTMR_CONTROL_INT ;
ctrl | = APBTMR_CONTROL_ENABLE ;
apbt_writel ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
return 0 ;
}
static int apbt_set_periodic ( struct clock_event_device * evt )
{
struct dw_apb_clock_event_device * dw_ced = ced_to_dw_apb_ced ( evt ) ;
unsigned long period = DIV_ROUND_UP ( dw_ced - > timer . freq , HZ ) ;
2015-11-25 18:41:23 +03:00
u32 ctrl ;
2015-06-18 13:54:19 +03:00
pr_debug ( " %s CPU %d state=periodic \n " , __func__ ,
cpumask_first ( evt - > cpumask ) ) ;
ctrl = apbt_readl ( & dw_ced - > timer , APBTMR_N_CONTROL ) ;
ctrl | = APBTMR_CONTROL_MODE_PERIODIC ;
apbt_writel ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
/*
* DW APB p . 46 , have to disable timer before load counter ,
* may cause sync problem .
*/
ctrl & = ~ APBTMR_CONTROL_ENABLE ;
apbt_writel ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
udelay ( 1 ) ;
pr_debug ( " Setting clock period %lu for HZ %d \n " , period , HZ ) ;
apbt_writel ( & dw_ced - > timer , period , APBTMR_N_LOAD_COUNT ) ;
ctrl | = APBTMR_CONTROL_ENABLE ;
apbt_writel ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
return 0 ;
}
static int apbt_resume ( struct clock_event_device * evt )
{
struct dw_apb_clock_event_device * dw_ced = ced_to_dw_apb_ced ( evt ) ;
pr_debug ( " %s CPU %d state=resume \n " , __func__ ,
cpumask_first ( evt - > cpumask ) ) ;
apbt_enable_int ( & dw_ced - > timer ) ;
return 0 ;
2011-06-06 15:43:07 +04:00
}
static int apbt_next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
2015-11-25 18:41:23 +03:00
u32 ctrl ;
2011-06-06 15:43:07 +04:00
struct dw_apb_clock_event_device * dw_ced = ced_to_dw_apb_ced ( evt ) ;
/* Disable timer */
2015-11-25 19:01:53 +03:00
ctrl = apbt_readl_relaxed ( & dw_ced - > timer , APBTMR_N_CONTROL ) ;
2011-06-06 15:43:07 +04:00
ctrl & = ~ APBTMR_CONTROL_ENABLE ;
2015-11-25 19:01:53 +03:00
apbt_writel_relaxed ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
2011-06-06 15:43:07 +04:00
/* write new count */
2015-11-25 19:01:53 +03:00
apbt_writel_relaxed ( & dw_ced - > timer , delta , APBTMR_N_LOAD_COUNT ) ;
2011-06-06 15:43:07 +04:00
ctrl | = APBTMR_CONTROL_ENABLE ;
2015-11-25 19:01:53 +03:00
apbt_writel_relaxed ( & dw_ced - > timer , ctrl , APBTMR_N_CONTROL ) ;
2011-06-06 15:43:07 +04:00
return 0 ;
}
/**
* dw_apb_clockevent_init ( ) - use an APB timer as a clock_event_device
*
2020-05-21 23:48:13 +03:00
* @ cpu : The CPU the events will be targeted at or - 1 if CPU affiliation
* isn ' t required .
2011-06-06 15:43:07 +04:00
* @ name : The name used for the timer and the IRQ for it .
* @ rating : The rating to give the timer .
* @ base : I / O base for the timer registers .
* @ irq : The interrupt number to use for the timer .
* @ freq : The frequency that the timer counts at .
*
* This creates a clock_event_device for using with the generic clock layer
* but does not start and register it . This should be done with
* dw_apb_clockevent_register ( ) as the next step . If this is the first time
* it has been called for a timer then the IRQ will be requested , if not it
* just be enabled to allow CPU hotplug to avoid repeatedly requesting and
* releasing the IRQ .
*/
struct dw_apb_clock_event_device *
dw_apb_clockevent_init ( int cpu , const char * name , unsigned rating ,
void __iomem * base , int irq , unsigned long freq )
{
struct dw_apb_clock_event_device * dw_ced =
kzalloc ( sizeof ( * dw_ced ) , GFP_KERNEL ) ;
int err ;
if ( ! dw_ced )
return NULL ;
dw_ced - > timer . base = base ;
dw_ced - > timer . irq = irq ;
dw_ced - > timer . freq = freq ;
clockevents_calc_mult_shift ( & dw_ced - > ced , freq , APBT_MIN_PERIOD ) ;
dw_ced - > ced . max_delta_ns = clockevent_delta2ns ( 0x7fffffff ,
& dw_ced - > ced ) ;
2017-03-30 23:07:49 +03:00
dw_ced - > ced . max_delta_ticks = 0x7fffffff ;
2011-06-06 15:43:07 +04:00
dw_ced - > ced . min_delta_ns = clockevent_delta2ns ( 5000 , & dw_ced - > ced ) ;
2017-03-30 23:07:49 +03:00
dw_ced - > ced . min_delta_ticks = 5000 ;
2020-05-21 23:48:13 +03:00
dw_ced - > ced . cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of ( cpu ) ;
2015-07-04 08:06:43 +03:00
dw_ced - > ced . features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ ;
2015-06-18 13:54:19 +03:00
dw_ced - > ced . set_state_shutdown = apbt_shutdown ;
dw_ced - > ced . set_state_periodic = apbt_set_periodic ;
dw_ced - > ced . set_state_oneshot = apbt_set_oneshot ;
2016-02-26 12:45:57 +03:00
dw_ced - > ced . set_state_oneshot_stopped = apbt_shutdown ;
2015-06-18 13:54:19 +03:00
dw_ced - > ced . tick_resume = apbt_resume ;
2011-06-06 15:43:07 +04:00
dw_ced - > ced . set_next_event = apbt_next_event ;
dw_ced - > ced . irq = dw_ced - > timer . irq ;
dw_ced - > ced . rating = rating ;
dw_ced - > ced . name = name ;
dw_ced - > eoi = apbt_eoi ;
2020-02-27 13:59:02 +03:00
err = request_irq ( irq , dw_apb_clockevent_irq ,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING ,
dw_ced - > ced . name , & dw_ced - > ced ) ;
2011-06-06 15:43:07 +04:00
if ( err ) {
pr_err ( " failed to request timer irq \n " ) ;
kfree ( dw_ced ) ;
dw_ced = NULL ;
}
return dw_ced ;
}
/**
* dw_apb_clockevent_resume ( ) - resume a clock that has been paused .
*
* @ dw_ced : The APB clock to resume .
*/
void dw_apb_clockevent_resume ( struct dw_apb_clock_event_device * dw_ced )
{
enable_irq ( dw_ced - > timer . irq ) ;
}
/**
* dw_apb_clockevent_stop ( ) - stop the clock_event_device and release the IRQ .
*
* @ dw_ced : The APB clock to stop generating the events .
*/
void dw_apb_clockevent_stop ( struct dw_apb_clock_event_device * dw_ced )
{
free_irq ( dw_ced - > timer . irq , & dw_ced - > ced ) ;
}
/**
* dw_apb_clockevent_register ( ) - register the clock with the generic layer
*
* @ dw_ced : The APB clock to register as a clock_event_device .
*/
void dw_apb_clockevent_register ( struct dw_apb_clock_event_device * dw_ced )
{
apbt_writel ( & dw_ced - > timer , 0 , APBTMR_N_CONTROL ) ;
clockevents_register_device ( & dw_ced - > ced ) ;
apbt_enable_int ( & dw_ced - > timer ) ;
}
/**
* dw_apb_clocksource_start ( ) - start the clocksource counting .
*
* @ dw_cs : The clocksource to start .
*
* This is used to start the clocksource before registration and can be used
* to enable calibration of timers .
*/
void dw_apb_clocksource_start ( struct dw_apb_clocksource * dw_cs )
{
/*
* start count down from 0xffff _ffff . this is done by toggling the
* enable bit then load initial load count to ~ 0.
*/
2015-11-25 18:41:23 +03:00
u32 ctrl = apbt_readl ( & dw_cs - > timer , APBTMR_N_CONTROL ) ;
2011-06-06 15:43:07 +04:00
ctrl & = ~ APBTMR_CONTROL_ENABLE ;
apbt_writel ( & dw_cs - > timer , ctrl , APBTMR_N_CONTROL ) ;
apbt_writel ( & dw_cs - > timer , ~ 0 , APBTMR_N_LOAD_COUNT ) ;
/* enable, mask interrupt */
ctrl & = ~ APBTMR_CONTROL_MODE_PERIODIC ;
ctrl | = ( APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT ) ;
apbt_writel ( & dw_cs - > timer , ctrl , APBTMR_N_CONTROL ) ;
/* read it once to get cached counter value initialized */
dw_apb_clocksource_read ( dw_cs ) ;
}
2016-12-21 22:32:01 +03:00
static u64 __apbt_read_clocksource ( struct clocksource * cs )
2011-06-06 15:43:07 +04:00
{
2015-11-25 18:41:23 +03:00
u32 current_count ;
2011-06-06 15:43:07 +04:00
struct dw_apb_clocksource * dw_cs =
clocksource_to_dw_apb_clocksource ( cs ) ;
2015-11-25 19:01:53 +03:00
current_count = apbt_readl_relaxed ( & dw_cs - > timer ,
APBTMR_N_CURRENT_VALUE ) ;
2011-06-06 15:43:07 +04:00
2016-12-21 22:32:01 +03:00
return ( u64 ) ~ current_count ;
2011-06-06 15:43:07 +04:00
}
static void apbt_restart_clocksource ( struct clocksource * cs )
{
struct dw_apb_clocksource * dw_cs =
clocksource_to_dw_apb_clocksource ( cs ) ;
dw_apb_clocksource_start ( dw_cs ) ;
}
/**
* dw_apb_clocksource_init ( ) - use an APB timer as a clocksource .
*
* @ rating : The rating to give the clocksource .
* @ name : The name for the clocksource .
* @ base : The I / O base for the timer registers .
* @ freq : The frequency that the timer counts at .
*
* This creates a clocksource using an APB timer but does not yet register it
* with the clocksource system . This should be done with
* dw_apb_clocksource_register ( ) as the next step .
*/
struct dw_apb_clocksource *
2011-07-25 19:34:37 +04:00
dw_apb_clocksource_init ( unsigned rating , const char * name , void __iomem * base ,
2011-06-06 15:43:07 +04:00
unsigned long freq )
{
struct dw_apb_clocksource * dw_cs = kzalloc ( sizeof ( * dw_cs ) , GFP_KERNEL ) ;
if ( ! dw_cs )
return NULL ;
dw_cs - > timer . base = base ;
dw_cs - > timer . freq = freq ;
dw_cs - > cs . name = name ;
dw_cs - > cs . rating = rating ;
dw_cs - > cs . read = __apbt_read_clocksource ;
dw_cs - > cs . mask = CLOCKSOURCE_MASK ( 32 ) ;
dw_cs - > cs . flags = CLOCK_SOURCE_IS_CONTINUOUS ;
dw_cs - > cs . resume = apbt_restart_clocksource ;
return dw_cs ;
}
/**
* dw_apb_clocksource_register ( ) - register the APB clocksource .
*
* @ dw_cs : The clocksource to register .
*/
void dw_apb_clocksource_register ( struct dw_apb_clocksource * dw_cs )
{
clocksource_register_hz ( & dw_cs - > cs , dw_cs - > timer . freq ) ;
}
/**
* dw_apb_clocksource_read ( ) - read the current value of a clocksource .
*
* @ dw_cs : The clocksource to read .
*/
2016-12-21 22:32:01 +03:00
u64 dw_apb_clocksource_read ( struct dw_apb_clocksource * dw_cs )
2011-06-06 15:43:07 +04:00
{
2016-12-21 22:32:01 +03:00
return ( u64 ) ~ apbt_readl ( & dw_cs - > timer , APBTMR_N_CURRENT_VALUE ) ;
2011-06-06 15:43:07 +04:00
}