2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-05-26 15:39:43 +03:00
/*
* Clocksource using the Low Power Timer found in the Low Power Controller ( LPC )
*
* Copyright ( C ) 2015 STMicroelectronics – All Rights Reserved
*
* Author ( s ) : Francesco Virlinzi < francesco . virlinzi @ st . com >
* Ajit Pal Singh < ajitpal . singh @ st . com >
*/
# include <linux/clk.h>
# include <linux/clocksource.h>
# include <linux/init.h>
# include <linux/of_address.h>
2015-05-12 15:58:11 +03:00
# include <linux/sched_clock.h>
2015-05-26 15:39:43 +03:00
# include <linux/slab.h>
# include <dt-bindings/mfd/st-lpc.h>
/* Low Power Timer */
# define LPC_LPT_LSB_OFF 0x400
# define LPC_LPT_MSB_OFF 0x404
# define LPC_LPT_START_OFF 0x408
static struct st_clksrc_ddata {
struct clk * clk ;
void __iomem * base ;
} ddata ;
static void __init st_clksrc_reset ( void )
{
writel_relaxed ( 0 , ddata . base + LPC_LPT_START_OFF ) ;
writel_relaxed ( 0 , ddata . base + LPC_LPT_MSB_OFF ) ;
writel_relaxed ( 0 , ddata . base + LPC_LPT_LSB_OFF ) ;
writel_relaxed ( 1 , ddata . base + LPC_LPT_START_OFF ) ;
}
2015-05-12 15:58:11 +03:00
static u64 notrace st_clksrc_sched_clock_read ( void )
{
return ( u64 ) readl_relaxed ( ddata . base + LPC_LPT_LSB_OFF ) ;
}
2015-05-26 15:39:43 +03:00
static int __init st_clksrc_init ( void )
{
unsigned long rate ;
int ret ;
st_clksrc_reset ( ) ;
rate = clk_get_rate ( ddata . clk ) ;
2015-05-12 15:58:11 +03:00
sched_clock_register ( st_clksrc_sched_clock_read , 32 , rate ) ;
2015-05-26 15:39:43 +03:00
ret = clocksource_mmio_init ( ddata . base + LPC_LPT_LSB_OFF ,
" clksrc-st-lpc " , rate , 300 , 32 ,
clocksource_mmio_readl_up ) ;
if ( ret ) {
pr_err ( " clksrc-st-lpc: Failed to register clocksource \n " ) ;
return ret ;
}
return 0 ;
}
static int __init st_clksrc_setup_clk ( struct device_node * np )
{
struct clk * clk ;
clk = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " clksrc-st-lpc: Failed to get LPC clock \n " ) ;
return PTR_ERR ( clk ) ;
}
if ( clk_prepare_enable ( clk ) ) {
pr_err ( " clksrc-st-lpc: Failed to enable LPC clock \n " ) ;
return - EINVAL ;
}
if ( ! clk_get_rate ( clk ) ) {
pr_err ( " clksrc-st-lpc: Failed to get LPC clock rate \n " ) ;
clk_disable_unprepare ( clk ) ;
return - EINVAL ;
}
ddata . clk = clk ;
return 0 ;
}
2016-06-01 01:34:25 +03:00
static int __init st_clksrc_of_register ( struct device_node * np )
2015-05-26 15:39:43 +03:00
{
int ret ;
uint32_t mode ;
ret = of_property_read_u32 ( np , " st,lpc-mode " , & mode ) ;
if ( ret ) {
pr_err ( " clksrc-st-lpc: An LPC mode must be provided \n " ) ;
2016-06-01 01:34:25 +03:00
return ret ;
2015-05-26 15:39:43 +03:00
}
/* LPC can either run as a Clocksource or in RTC or WDT mode */
if ( mode ! = ST_LPC_MODE_CLKSRC )
2016-06-01 01:34:25 +03:00
return 0 ;
2015-05-26 15:39:43 +03:00
ddata . base = of_iomap ( np , 0 ) ;
if ( ! ddata . base ) {
pr_err ( " clksrc-st-lpc: Unable to map iomem \n " ) ;
2016-06-01 01:34:25 +03:00
return - ENXIO ;
2015-05-26 15:39:43 +03:00
}
2016-06-01 01:34:25 +03:00
ret = st_clksrc_setup_clk ( np ) ;
if ( ret ) {
2015-05-26 15:39:43 +03:00
iounmap ( ddata . base ) ;
2016-06-01 01:34:25 +03:00
return ret ;
2015-05-26 15:39:43 +03:00
}
2016-06-01 01:34:25 +03:00
ret = st_clksrc_init ( ) ;
if ( ret ) {
2015-05-26 15:39:43 +03:00
clk_disable_unprepare ( ddata . clk ) ;
clk_put ( ddata . clk ) ;
iounmap ( ddata . base ) ;
2016-06-01 01:34:25 +03:00
return ret ;
2015-05-26 15:39:43 +03:00
}
pr_info ( " clksrc-st-lpc: clocksource initialised - running @ %luHz \n " ,
clk_get_rate ( ddata . clk ) ) ;
2016-06-01 01:34:25 +03:00
return ret ;
2015-05-26 15:39:43 +03:00
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( ddata , " st,stih407-lpc " , st_clksrc_of_register ) ;