2013-10-03 23:56:29 +04:00
/*
* Copyright ( C ) 2013 Pengutronix
* Uwe Kleine - Koenig < u . kleine - koenig @ pengutronix . de >
*
* 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 .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# 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>
# define TIMERn_CTRL 0x00
# define TIMERn_CTRL_PRESC(val) (((val) & 0xf) << 24)
# define TIMERn_CTRL_PRESC_1024 TIMERn_CTRL_PRESC(10)
# define TIMERn_CTRL_CLKSEL(val) (((val) & 0x3) << 16)
# define TIMERn_CTRL_CLKSEL_PRESCHFPERCLK TIMERn_CTRL_CLKSEL(0)
# define TIMERn_CTRL_OSMEN 0x00000010
# define TIMERn_CTRL_MODE(val) (((val) & 0x3) << 0)
# define TIMERn_CTRL_MODE_UP TIMERn_CTRL_MODE(0)
# define TIMERn_CTRL_MODE_DOWN TIMERn_CTRL_MODE(1)
# define TIMERn_CMD 0x04
# define TIMERn_CMD_START 0x00000001
# define TIMERn_CMD_STOP 0x00000002
# define TIMERn_IEN 0x0c
# define TIMERn_IF 0x10
# define TIMERn_IFS 0x14
# define TIMERn_IFC 0x18
# define TIMERn_IRQ_UF 0x00000002
# define TIMERn_TOP 0x1c
# define TIMERn_CNT 0x24
struct efm32_clock_event_ddata {
struct clock_event_device evtdev ;
void __iomem * base ;
unsigned periodic_top ;
} ;
2015-06-18 13:54:41 +03:00
static int efm32_clock_event_shutdown ( struct clock_event_device * evtdev )
2013-10-03 23:56:29 +04:00
{
struct efm32_clock_event_ddata * ddata =
container_of ( evtdev , struct efm32_clock_event_ddata , evtdev ) ;
2015-06-18 13:54:41 +03:00
writel_relaxed ( TIMERn_CMD_STOP , ddata - > base + TIMERn_CMD ) ;
return 0 ;
}
static int efm32_clock_event_set_oneshot ( struct clock_event_device * evtdev )
{
struct efm32_clock_event_ddata * ddata =
container_of ( evtdev , struct efm32_clock_event_ddata , evtdev ) ;
writel_relaxed ( TIMERn_CMD_STOP , ddata - > base + TIMERn_CMD ) ;
writel_relaxed ( TIMERn_CTRL_PRESC_1024 |
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
TIMERn_CTRL_OSMEN |
TIMERn_CTRL_MODE_DOWN ,
ddata - > base + TIMERn_CTRL ) ;
return 0 ;
}
static int efm32_clock_event_set_periodic ( struct clock_event_device * evtdev )
{
struct efm32_clock_event_ddata * ddata =
container_of ( evtdev , struct efm32_clock_event_ddata , evtdev ) ;
writel_relaxed ( TIMERn_CMD_STOP , ddata - > base + TIMERn_CMD ) ;
writel_relaxed ( ddata - > periodic_top , ddata - > base + TIMERn_TOP ) ;
writel_relaxed ( TIMERn_CTRL_PRESC_1024 |
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
TIMERn_CTRL_MODE_DOWN ,
ddata - > base + TIMERn_CTRL ) ;
writel_relaxed ( TIMERn_CMD_START , ddata - > base + TIMERn_CMD ) ;
return 0 ;
2013-10-03 23:56:29 +04:00
}
static int efm32_clock_event_set_next_event ( unsigned long evt ,
struct clock_event_device * evtdev )
{
struct efm32_clock_event_ddata * ddata =
container_of ( evtdev , struct efm32_clock_event_ddata , evtdev ) ;
writel_relaxed ( TIMERn_CMD_STOP , ddata - > base + TIMERn_CMD ) ;
writel_relaxed ( evt , ddata - > base + TIMERn_CNT ) ;
writel_relaxed ( TIMERn_CMD_START , ddata - > base + TIMERn_CMD ) ;
return 0 ;
}
static irqreturn_t efm32_clock_event_handler ( int irq , void * dev_id )
{
struct efm32_clock_event_ddata * ddata = dev_id ;
writel_relaxed ( TIMERn_IRQ_UF , ddata - > base + TIMERn_IFC ) ;
ddata - > evtdev . event_handler ( & ddata - > evtdev ) ;
return IRQ_HANDLED ;
}
static struct efm32_clock_event_ddata clock_event_ddata = {
. evtdev = {
. name = " efm32 clockevent " ,
2015-03-30 23:17:13 +03:00
. features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC ,
2015-06-18 13:54:41 +03:00
. set_state_shutdown = efm32_clock_event_shutdown ,
. set_state_periodic = efm32_clock_event_set_periodic ,
. set_state_oneshot = efm32_clock_event_set_oneshot ,
2013-10-03 23:56:29 +04:00
. set_next_event = efm32_clock_event_set_next_event ,
. rating = 200 ,
} ,
} ;
static struct irqaction efm32_clock_event_irq = {
. name = " efm32 clockevent " ,
. flags = IRQF_TIMER ,
. handler = efm32_clock_event_handler ,
. dev_id = & clock_event_ddata ,
} ;
static int __init efm32_clocksource_init ( struct device_node * np )
{
struct clk * clk ;
void __iomem * base ;
unsigned long rate ;
int ret ;
clk = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
pr_err ( " failed to get clock for clocksource (%d) \n " , ret ) ;
goto err_clk_get ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret ) {
pr_err ( " failed to enable timer clock for clocksource (%d) \n " ,
ret ) ;
goto err_clk_enable ;
}
rate = clk_get_rate ( clk ) ;
base = of_iomap ( np , 0 ) ;
if ( ! base ) {
ret = - EADDRNOTAVAIL ;
pr_err ( " failed to map registers for clocksource \n " ) ;
goto err_iomap ;
}
writel_relaxed ( TIMERn_CTRL_PRESC_1024 |
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
TIMERn_CTRL_MODE_UP , base + TIMERn_CTRL ) ;
writel_relaxed ( TIMERn_CMD_START , base + TIMERn_CMD ) ;
ret = clocksource_mmio_init ( base + TIMERn_CNT , " efm32 timer " ,
DIV_ROUND_CLOSEST ( rate , 1024 ) , 200 , 16 ,
clocksource_mmio_readl_up ) ;
if ( ret ) {
pr_err ( " failed to init clocksource (%d) \n " , ret ) ;
goto err_clocksource_init ;
}
return 0 ;
err_clocksource_init :
iounmap ( base ) ;
err_iomap :
clk_disable_unprepare ( clk ) ;
err_clk_enable :
clk_put ( clk ) ;
err_clk_get :
return ret ;
}
static int __init efm32_clockevent_init ( struct device_node * np )
{
struct clk * clk ;
void __iomem * base ;
unsigned long rate ;
int irq ;
int ret ;
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 ) ;
base = of_iomap ( np , 0 ) ;
if ( ! base ) {
ret = - EADDRNOTAVAIL ;
pr_err ( " failed to map registers for clockevent \n " ) ;
goto err_iomap ;
}
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! irq ) {
ret = - ENOENT ;
pr_err ( " failed to get irq for clockevent \n " ) ;
goto err_get_irq ;
}
writel_relaxed ( TIMERn_IRQ_UF , base + TIMERn_IEN ) ;
clock_event_ddata . base = base ;
clock_event_ddata . periodic_top = DIV_ROUND_CLOSEST ( rate , 1024 * HZ ) ;
clockevents_config_and_register ( & clock_event_ddata . evtdev ,
DIV_ROUND_CLOSEST ( rate , 1024 ) ,
0xf , 0xffff ) ;
2016-06-06 19:00:14 +03:00
ret = setup_irq ( irq , & efm32_clock_event_irq ) ;
if ( ret ) {
2017-03-09 12:47:10 +03:00
pr_err ( " Failed setup irq \n " ) ;
2016-06-06 19:00:14 +03:00
goto err_setup_irq ;
}
2015-03-03 13:46:49 +03:00
2013-10-03 23:56:29 +04:00
return 0 ;
2016-06-06 19:00:14 +03:00
err_setup_irq :
2013-10-03 23:56:29 +04:00
err_get_irq :
iounmap ( base ) ;
err_iomap :
clk_disable_unprepare ( clk ) ;
err_clk_enable :
clk_put ( clk ) ;
err_clk_get :
return ret ;
}
/*
* This function asserts that we have exactly one clocksource and one
* clock_event_device in the end .
*/
2016-06-06 19:00:14 +03:00
static int __init efm32_timer_init ( struct device_node * np )
2013-10-03 23:56:29 +04:00
{
static int has_clocksource , has_clockevent ;
2016-06-06 19:00:14 +03:00
int ret = 0 ;
2013-10-03 23:56:29 +04:00
if ( ! has_clocksource ) {
ret = efm32_clocksource_init ( np ) ;
if ( ! ret ) {
has_clocksource = 1 ;
2016-06-06 19:00:14 +03:00
return 0 ;
2013-10-03 23:56:29 +04:00
}
}
if ( ! has_clockevent ) {
ret = efm32_clockevent_init ( np ) ;
if ( ! ret ) {
has_clockevent = 1 ;
2016-06-06 19:00:14 +03:00
return 0 ;
2013-10-03 23:56:29 +04:00
}
}
2016-06-06 19:00:14 +03:00
return ret ;
2013-10-03 23:56:29 +04:00
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( efm32compat , " efm32,timer " , efm32_timer_init ) ;
TIMER_OF_DECLARE ( efm32 , " energymicro,efm32-timer " , efm32_timer_init ) ;