2022-05-10 20:24:51 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-11-07 15:01:48 +04:00
/*
* Allwinner SoCs hstimer driver .
*
* Copyright ( C ) 2013 Maxime Ripard
*
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*/
# include <linux/clk.h>
# include <linux/clockchips.h>
2017-06-11 08:22:10 +03:00
# include <linux/clocksource.h>
2013-11-07 15:01:48 +04:00
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/irqreturn.h>
2014-04-17 13:06:45 +04:00
# include <linux/reset.h>
2015-03-31 13:12:25 +03:00
# include <linux/slab.h>
2023-06-30 23:01:28 +03:00
# include <linux/platform_device.h>
2013-11-07 15:01:48 +04:00
# define TIMER_IRQ_EN_REG 0x00
# define TIMER_IRQ_EN(val) BIT(val)
# define TIMER_IRQ_ST_REG 0x04
# define TIMER_CTL_REG(val) (0x20 * (val) + 0x10)
# define TIMER_CTL_ENABLE BIT(0)
# define TIMER_CTL_RELOAD BIT(1)
# define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
# define TIMER_CTL_ONESHOT BIT(7)
# define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14)
# define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18)
# define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c)
# define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20)
# define TIMER_SYNC_TICKS 3
2023-06-30 23:01:27 +03:00
struct sun5i_timer {
2015-03-31 13:12:25 +03:00
void __iomem * base ;
struct clk * clk ;
2015-03-31 13:12:26 +03:00
struct notifier_block clk_rate_cb ;
2015-03-31 13:12:25 +03:00
u32 ticks_per_jiffy ;
struct clocksource clksrc ;
struct clock_event_device clkevt ;
} ;
2023-06-30 23:01:26 +03:00
# define nb_to_sun5i_timer(x) \
2023-06-30 23:01:27 +03:00
container_of ( x , struct sun5i_timer , clk_rate_cb )
2023-06-30 23:01:26 +03:00
# define clksrc_to_sun5i_timer(x) \
container_of ( x , struct sun5i_timer , clksrc )
# define clkevt_to_sun5i_timer(x) \
container_of ( x , struct sun5i_timer , clkevt )
2013-11-07 15:01:48 +04:00
/*
* When we disable a timer , we need to wait at least for 2 cycles of
* the timer source clock . We will use for that the clocksource timer
* that is already setup and runs at the same frequency than the other
* timers , and we never will be disabled .
*/
2023-06-30 23:01:26 +03:00
static void sun5i_clkevt_sync ( struct sun5i_timer * ce )
2013-11-07 15:01:48 +04:00
{
2023-06-30 23:01:27 +03:00
u32 old = readl ( ce - > base + TIMER_CNTVAL_LO_REG ( 1 ) ) ;
2013-11-07 15:01:48 +04:00
2023-06-30 23:01:27 +03:00
while ( ( old - readl ( ce - > base + TIMER_CNTVAL_LO_REG ( 1 ) ) ) < TIMER_SYNC_TICKS )
2013-11-07 15:01:48 +04:00
cpu_relax ( ) ;
}
2023-06-30 23:01:26 +03:00
static void sun5i_clkevt_time_stop ( struct sun5i_timer * ce , u8 timer )
2013-11-07 15:01:48 +04:00
{
2023-06-30 23:01:27 +03:00
u32 val = readl ( ce - > base + TIMER_CTL_REG ( timer ) ) ;
writel ( val & ~ TIMER_CTL_ENABLE , ce - > base + TIMER_CTL_REG ( timer ) ) ;
2013-11-07 15:01:48 +04:00
2015-03-31 13:12:25 +03:00
sun5i_clkevt_sync ( ce ) ;
2013-11-07 15:01:48 +04:00
}
2023-06-30 23:01:26 +03:00
static void sun5i_clkevt_time_setup ( struct sun5i_timer * ce , u8 timer , u32 delay )
2013-11-07 15:01:48 +04:00
{
2023-06-30 23:01:27 +03:00
writel ( delay , ce - > base + TIMER_INTVAL_LO_REG ( timer ) ) ;
2013-11-07 15:01:48 +04:00
}
2023-06-30 23:01:26 +03:00
static void sun5i_clkevt_time_start ( struct sun5i_timer * ce , u8 timer , bool periodic )
2013-11-07 15:01:48 +04:00
{
2023-06-30 23:01:27 +03:00
u32 val = readl ( ce - > base + TIMER_CTL_REG ( timer ) ) ;
2013-11-07 15:01:48 +04:00
if ( periodic )
val & = ~ TIMER_CTL_ONESHOT ;
else
val | = TIMER_CTL_ONESHOT ;
writel ( val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD ,
2023-06-30 23:01:27 +03:00
ce - > base + TIMER_CTL_REG ( timer ) ) ;
2013-11-07 15:01:48 +04:00
}
2015-06-18 13:54:51 +03:00
static int sun5i_clkevt_shutdown ( struct clock_event_device * clkevt )
2013-11-07 15:01:48 +04:00
{
2023-06-30 23:01:26 +03:00
struct sun5i_timer * ce = clkevt_to_sun5i_timer ( clkevt ) ;
2015-03-31 13:12:25 +03:00
2015-06-18 13:54:51 +03:00
sun5i_clkevt_time_stop ( ce , 0 ) ;
return 0 ;
}
static int sun5i_clkevt_set_oneshot ( struct clock_event_device * clkevt )
{
2023-06-30 23:01:26 +03:00
struct sun5i_timer * ce = clkevt_to_sun5i_timer ( clkevt ) ;
2015-06-18 13:54:51 +03:00
sun5i_clkevt_time_stop ( ce , 0 ) ;
sun5i_clkevt_time_start ( ce , 0 , false ) ;
return 0 ;
}
static int sun5i_clkevt_set_periodic ( struct clock_event_device * clkevt )
{
2023-06-30 23:01:26 +03:00
struct sun5i_timer * ce = clkevt_to_sun5i_timer ( clkevt ) ;
2015-06-18 13:54:51 +03:00
sun5i_clkevt_time_stop ( ce , 0 ) ;
2023-06-30 23:01:27 +03:00
sun5i_clkevt_time_setup ( ce , 0 , ce - > ticks_per_jiffy ) ;
2015-06-18 13:54:51 +03:00
sun5i_clkevt_time_start ( ce , 0 , true ) ;
return 0 ;
2013-11-07 15:01:48 +04:00
}
static int sun5i_clkevt_next_event ( unsigned long evt ,
2015-03-31 13:12:25 +03:00
struct clock_event_device * clkevt )
2013-11-07 15:01:48 +04:00
{
2023-06-30 23:01:26 +03:00
struct sun5i_timer * ce = clkevt_to_sun5i_timer ( clkevt ) ;
2015-03-31 13:12:25 +03:00
sun5i_clkevt_time_stop ( ce , 0 ) ;
sun5i_clkevt_time_setup ( ce , 0 , evt - TIMER_SYNC_TICKS ) ;
sun5i_clkevt_time_start ( ce , 0 , false ) ;
2013-11-07 15:01:48 +04:00
return 0 ;
}
static irqreturn_t sun5i_timer_interrupt ( int irq , void * dev_id )
{
2023-06-30 23:01:26 +03:00
struct sun5i_timer * ce = dev_id ;
2013-11-07 15:01:48 +04:00
2023-06-30 23:01:27 +03:00
writel ( 0x1 , ce - > base + TIMER_IRQ_ST_REG ) ;
2015-03-31 13:12:25 +03:00
ce - > clkevt . event_handler ( & ce - > clkevt ) ;
2013-11-07 15:01:48 +04:00
return IRQ_HANDLED ;
}
2016-12-21 22:32:01 +03:00
static u64 sun5i_clksrc_read ( struct clocksource * clksrc )
2016-10-18 08:49:18 +03:00
{
2023-06-30 23:01:26 +03:00
struct sun5i_timer * cs = clksrc_to_sun5i_timer ( clksrc ) ;
2016-10-18 08:49:18 +03:00
2023-06-30 23:01:27 +03:00
return ~ readl ( cs - > base + TIMER_CNTVAL_LO_REG ( 1 ) ) ;
2016-10-18 08:49:18 +03:00
}
2023-06-30 23:01:26 +03:00
static int sun5i_rate_cb ( struct notifier_block * nb ,
unsigned long event , void * data )
2015-03-31 13:12:26 +03:00
{
struct clk_notifier_data * ndata = data ;
2023-06-30 23:01:26 +03:00
struct sun5i_timer * cs = nb_to_sun5i_timer ( nb ) ;
2015-03-31 13:12:26 +03:00
switch ( event ) {
case PRE_RATE_CHANGE :
clocksource_unregister ( & cs - > clksrc ) ;
break ;
case POST_RATE_CHANGE :
clocksource_register_hz ( & cs - > clksrc , ndata - > new_rate ) ;
2023-06-30 23:01:26 +03:00
clockevents_update_freq ( & cs - > clkevt , ndata - > new_rate ) ;
2023-06-30 23:01:27 +03:00
cs - > ticks_per_jiffy = DIV_ROUND_UP ( ndata - > new_rate , HZ ) ;
2015-03-31 13:12:26 +03:00
break ;
default :
break ;
}
return NOTIFY_DONE ;
}
2023-06-30 23:01:28 +03:00
static int sun5i_setup_clocksource ( struct platform_device * pdev ,
unsigned long rate )
2015-03-31 13:12:25 +03:00
{
2023-06-30 23:01:28 +03:00
struct sun5i_timer * cs = platform_get_drvdata ( pdev ) ;
2023-06-30 23:01:27 +03:00
void __iomem * base = cs - > base ;
2015-03-31 13:12:25 +03:00
int ret ;
writel ( ~ 0 , base + TIMER_INTVAL_LO_REG ( 1 ) ) ;
writel ( TIMER_CTL_ENABLE | TIMER_CTL_RELOAD ,
base + TIMER_CTL_REG ( 1 ) ) ;
2023-06-30 23:01:28 +03:00
cs - > clksrc . name = pdev - > dev . of_node - > name ;
2016-10-18 08:49:18 +03:00
cs - > clksrc . rating = 340 ;
cs - > clksrc . read = sun5i_clksrc_read ;
cs - > clksrc . mask = CLOCKSOURCE_MASK ( 32 ) ;
cs - > clksrc . flags = CLOCK_SOURCE_IS_CONTINUOUS ;
ret = clocksource_register_hz ( & cs - > clksrc , rate ) ;
2015-03-31 13:12:25 +03:00
if ( ret ) {
2023-06-30 23:01:28 +03:00
dev_err ( & pdev - > dev , " Couldn't register clock source. \n " ) ;
2023-06-30 23:01:26 +03:00
return ret ;
2015-03-31 13:12:25 +03:00
}
return 0 ;
2015-03-31 13:12:26 +03:00
}
2023-06-30 23:01:28 +03:00
static int sun5i_setup_clockevent ( struct platform_device * pdev ,
unsigned long rate , int irq )
2015-03-31 13:12:25 +03:00
{
2023-06-30 23:01:28 +03:00
struct device * dev = & pdev - > dev ;
struct sun5i_timer * ce = platform_get_drvdata ( pdev ) ;
2023-06-30 23:01:27 +03:00
void __iomem * base = ce - > base ;
2015-03-31 13:12:25 +03:00
int ret ;
u32 val ;
2023-06-30 23:01:28 +03:00
ce - > clkevt . name = dev - > of_node - > name ;
2015-03-31 13:12:25 +03:00
ce - > clkevt . features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ;
ce - > clkevt . set_next_event = sun5i_clkevt_next_event ;
2015-06-18 13:54:51 +03:00
ce - > clkevt . set_state_shutdown = sun5i_clkevt_shutdown ;
ce - > clkevt . set_state_periodic = sun5i_clkevt_set_periodic ;
ce - > clkevt . set_state_oneshot = sun5i_clkevt_set_oneshot ;
ce - > clkevt . tick_resume = sun5i_clkevt_shutdown ;
2015-03-31 13:12:25 +03:00
ce - > clkevt . rating = 340 ;
ce - > clkevt . irq = irq ;
ce - > clkevt . cpumask = cpu_possible_mask ;
/* Enable timer0 interrupt */
val = readl ( base + TIMER_IRQ_EN_REG ) ;
writel ( val | TIMER_IRQ_EN ( 0 ) , base + TIMER_IRQ_EN_REG ) ;
clockevents_config_and_register ( & ce - > clkevt , rate ,
TIMER_SYNC_TICKS , 0xffffffff ) ;
2023-06-30 23:01:28 +03:00
ret = devm_request_irq ( dev , irq , sun5i_timer_interrupt ,
IRQF_TIMER | IRQF_IRQPOLL ,
" sun5i_timer0 " , ce ) ;
2015-03-31 13:12:25 +03:00
if ( ret ) {
2023-06-30 23:01:28 +03:00
dev_err ( dev , " Unable to register interrupt \n " ) ;
2023-06-30 23:01:26 +03:00
return ret ;
2015-03-31 13:12:25 +03:00
}
return 0 ;
}
2023-06-30 23:01:28 +03:00
static int sun5i_timer_probe ( struct platform_device * pdev )
2013-11-07 15:01:48 +04:00
{
2023-06-30 23:01:28 +03:00
struct device * dev = & pdev - > dev ;
2023-06-30 23:01:26 +03:00
struct sun5i_timer * st ;
2014-04-17 13:06:45 +04:00
struct reset_control * rstc ;
2015-03-31 13:12:25 +03:00
void __iomem * timer_base ;
2013-11-07 15:01:48 +04:00
struct clk * clk ;
2023-06-30 23:01:26 +03:00
unsigned long rate ;
2016-06-07 00:28:36 +03:00
int irq , ret ;
2013-11-07 15:01:48 +04:00
2023-06-30 23:01:28 +03:00
st = devm_kzalloc ( dev , sizeof ( * st ) , GFP_KERNEL ) ;
2023-06-30 23:01:26 +03:00
if ( ! st )
return - ENOMEM ;
2023-06-30 23:01:28 +03:00
platform_set_drvdata ( pdev , st ) ;
timer_base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2016-06-07 00:28:36 +03:00
if ( IS_ERR ( timer_base ) ) {
2023-06-30 23:01:28 +03:00
dev_err ( dev , " Can't map registers \n " ) ;
2018-02-22 12:54:55 +03:00
return PTR_ERR ( timer_base ) ;
2016-06-07 00:28:36 +03:00
}
2013-11-07 15:01:48 +04:00
2023-06-30 23:01:28 +03:00
irq = platform_get_irq ( pdev , 0 ) ;
2023-08-31 07:14:14 +03:00
if ( irq < 0 )
2023-06-30 23:01:28 +03:00
return irq ;
2013-11-07 15:01:48 +04:00
2023-06-30 23:01:28 +03:00
clk = devm_clk_get_enabled ( dev , NULL ) ;
2016-06-07 00:28:36 +03:00
if ( IS_ERR ( clk ) ) {
2023-06-30 23:01:28 +03:00
dev_err ( dev , " Can't get timer clock \n " ) ;
2016-06-07 00:28:36 +03:00
return PTR_ERR ( clk ) ;
}
2013-11-07 15:01:48 +04:00
2023-06-30 23:01:26 +03:00
rate = clk_get_rate ( clk ) ;
if ( ! rate ) {
2023-06-30 23:01:28 +03:00
dev_err ( dev , " Couldn't get parent clock rate \n " ) ;
return - EINVAL ;
2023-06-30 23:01:26 +03:00
}
2023-06-30 23:01:27 +03:00
st - > base = timer_base ;
st - > ticks_per_jiffy = DIV_ROUND_UP ( rate , HZ ) ;
st - > clk = clk ;
st - > clk_rate_cb . notifier_call = sun5i_rate_cb ;
st - > clk_rate_cb . next = NULL ;
2023-06-30 23:01:26 +03:00
2023-06-30 23:01:28 +03:00
ret = devm_clk_notifier_register ( dev , clk , & st - > clk_rate_cb ) ;
2023-06-30 23:01:26 +03:00
if ( ret ) {
2023-06-30 23:01:28 +03:00
dev_err ( dev , " Unable to register clock notifier. \n " ) ;
return ret ;
2023-06-30 23:01:26 +03:00
}
2023-06-30 23:01:28 +03:00
rstc = devm_reset_control_get_optional_exclusive ( dev , NULL ) ;
if ( rstc )
2014-04-17 13:06:45 +04:00
reset_control_deassert ( rstc ) ;
2023-06-30 23:01:28 +03:00
ret = sun5i_setup_clocksource ( pdev , rate ) ;
if ( ret )
return ret ;
ret = sun5i_setup_clockevent ( pdev , rate , irq ) ;
2016-06-07 00:28:36 +03:00
if ( ret )
2023-06-30 23:01:28 +03:00
goto err_unreg_clocksource ;
2016-06-07 00:28:36 +03:00
2023-06-30 23:01:28 +03:00
return 0 ;
2023-06-30 23:01:26 +03:00
2023-06-30 23:01:28 +03:00
err_unreg_clocksource :
clocksource_unregister ( & st - > clksrc ) ;
2023-06-30 23:01:26 +03:00
return ret ;
2013-11-07 15:01:48 +04:00
}
2023-06-30 23:01:28 +03:00
static void sun5i_timer_remove ( struct platform_device * pdev )
{
struct sun5i_timer * st = platform_get_drvdata ( pdev ) ;
clocksource_unregister ( & st - > clksrc ) ;
}
static const struct of_device_id sun5i_timer_of_match [ ] = {
{ . compatible = " allwinner,sun5i-a13-hstimer " } ,
{ . compatible = " allwinner,sun7i-a20-hstimer " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , sun5i_timer_of_match ) ;
static struct platform_driver sun5i_timer_driver = {
. probe = sun5i_timer_probe ,
. remove_new = sun5i_timer_remove ,
. driver = {
. name = " sun5i-timer " ,
. of_match_table = sun5i_timer_of_match ,
. suppress_bind_attrs = true ,
} ,
} ;
module_platform_driver ( sun5i_timer_driver ) ;