2015-05-23 00:03:33 +03:00
/*
* Copyright ( C ) Maxime Coquelin 2015
* Author : Maxime Coquelin < mcoquelin . stm32 @ gmail . com >
* License terms : GNU General Public License ( GPL ) , version 2
*
* Inspired by time - efm32 . c from Uwe Kleine - Koenig
*/
# include <linux/kernel.h>
# include <linux/clocksource.h>
# include <linux/clockchips.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/clk.h>
# include <linux/reset.h>
# define TIM_CR1 0x00
# define TIM_DIER 0x0c
# define TIM_SR 0x10
# define TIM_EGR 0x14
# define TIM_PSC 0x28
# define TIM_ARR 0x2c
# define TIM_CR1_CEN BIT(0)
# define TIM_CR1_OPM BIT(3)
# define TIM_CR1_ARPE BIT(7)
# define TIM_DIER_UIE BIT(0)
# define TIM_SR_UIF BIT(0)
# define TIM_EGR_UG BIT(0)
struct stm32_clock_event_ddata {
struct clock_event_device evtdev ;
unsigned periodic_top ;
void __iomem * base ;
} ;
2015-06-18 13:54:50 +03:00
static int stm32_clock_event_shutdown ( struct clock_event_device * evtdev )
2015-05-23 00:03:33 +03:00
{
struct stm32_clock_event_ddata * data =
container_of ( evtdev , struct stm32_clock_event_ddata , evtdev ) ;
void * base = data - > base ;
2015-06-18 13:54:50 +03:00
writel_relaxed ( 0 , base + TIM_CR1 ) ;
return 0 ;
}
static int stm32_clock_event_set_periodic ( struct clock_event_device * evtdev )
{
struct stm32_clock_event_ddata * data =
container_of ( evtdev , struct stm32_clock_event_ddata , evtdev ) ;
void * base = data - > base ;
writel_relaxed ( data - > periodic_top , base + TIM_ARR ) ;
writel_relaxed ( TIM_CR1_ARPE | TIM_CR1_CEN , base + TIM_CR1 ) ;
return 0 ;
2015-05-23 00:03:33 +03:00
}
static int stm32_clock_event_set_next_event ( unsigned long evt ,
struct clock_event_device * evtdev )
{
struct stm32_clock_event_ddata * data =
container_of ( evtdev , struct stm32_clock_event_ddata , evtdev ) ;
writel_relaxed ( evt , data - > base + TIM_ARR ) ;
writel_relaxed ( TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN ,
data - > base + TIM_CR1 ) ;
return 0 ;
}
static irqreturn_t stm32_clock_event_handler ( int irq , void * dev_id )
{
struct stm32_clock_event_ddata * data = dev_id ;
writel_relaxed ( 0 , data - > base + TIM_SR ) ;
data - > evtdev . event_handler ( & data - > evtdev ) ;
return IRQ_HANDLED ;
}
static struct stm32_clock_event_ddata clock_event_ddata = {
. evtdev = {
. name = " stm32 clockevent " ,
. features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC ,
2015-06-18 13:54:50 +03:00
. set_state_shutdown = stm32_clock_event_shutdown ,
. set_state_periodic = stm32_clock_event_set_periodic ,
. set_state_oneshot = stm32_clock_event_shutdown ,
. tick_resume = stm32_clock_event_shutdown ,
2015-05-23 00:03:33 +03:00
. set_next_event = stm32_clock_event_set_next_event ,
. rating = 200 ,
} ,
} ;
2016-06-07 00:28:17 +03:00
static int __init stm32_clockevent_init ( struct device_node * np )
2015-05-23 00:03:33 +03:00
{
struct stm32_clock_event_ddata * data = & clock_event_ddata ;
struct clk * clk ;
struct reset_control * rstc ;
unsigned long rate , max_delta ;
int irq , ret , bits , prescaler = 1 ;
clk = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
pr_err ( " failed to get clock for clockevent (%d) \n " , ret ) ;
goto err_clk_get ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret ) {
pr_err ( " failed to enable timer clock for clockevent (%d) \n " ,
ret ) ;
goto err_clk_enable ;
}
rate = clk_get_rate ( clk ) ;
rstc = of_reset_control_get ( np , NULL ) ;
if ( ! IS_ERR ( rstc ) ) {
reset_control_assert ( rstc ) ;
reset_control_deassert ( rstc ) ;
}
data - > base = of_iomap ( np , 0 ) ;
if ( ! data - > base ) {
2016-06-07 00:28:17 +03:00
ret = - ENXIO ;
2015-05-23 00:03:33 +03:00
pr_err ( " failed to map registers for clockevent \n " ) ;
goto err_iomap ;
}
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! irq ) {
2016-06-07 00:28:17 +03:00
ret = - EINVAL ;
2015-05-23 00:03:33 +03:00
pr_err ( " %s: failed to get irq. \n " , np - > full_name ) ;
goto err_get_irq ;
}
/* Detect whether the timer is 16 or 32 bits */
2015-05-28 08:05:53 +03:00
writel_relaxed ( ~ 0U , data - > base + TIM_ARR ) ;
2015-05-23 00:03:33 +03:00
max_delta = readl_relaxed ( data - > base + TIM_ARR ) ;
2015-05-28 08:05:53 +03:00
if ( max_delta = = ~ 0U ) {
2015-05-23 00:03:33 +03:00
prescaler = 1 ;
bits = 32 ;
} else {
prescaler = 1024 ;
bits = 16 ;
}
writel_relaxed ( 0 , data - > base + TIM_ARR ) ;
writel_relaxed ( prescaler - 1 , data - > base + TIM_PSC ) ;
writel_relaxed ( TIM_EGR_UG , data - > base + TIM_EGR ) ;
writel_relaxed ( TIM_DIER_UIE , data - > base + TIM_DIER ) ;
writel_relaxed ( 0 , data - > base + TIM_SR ) ;
data - > periodic_top = DIV_ROUND_CLOSEST ( rate , prescaler * HZ ) ;
clockevents_config_and_register ( & data - > evtdev ,
DIV_ROUND_CLOSEST ( rate , prescaler ) ,
0x1 , max_delta ) ;
ret = request_irq ( irq , stm32_clock_event_handler , IRQF_TIMER ,
" stm32 clockevent " , data ) ;
if ( ret ) {
pr_err ( " %s: failed to request irq. \n " , np - > full_name ) ;
goto err_get_irq ;
}
pr_info ( " %s: STM32 clockevent driver initialized (%d bits) \n " ,
np - > full_name , bits ) ;
2016-06-07 00:28:17 +03:00
return ret ;
2015-05-23 00:03:33 +03:00
err_get_irq :
iounmap ( data - > base ) ;
err_iomap :
clk_disable_unprepare ( clk ) ;
err_clk_enable :
clk_put ( clk ) ;
err_clk_get :
2016-06-07 00:28:17 +03:00
return ret ;
2015-05-23 00:03:33 +03:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( stm32 , " st,stm32-timer " , stm32_clockevent_init ) ;