2019-05-28 19:57:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-06-05 01:17:21 +04:00
/*
* Nomadik clock implementation
* Copyright ( C ) 2013 ST - Ericsson AB
* Author : Linus Walleij < linus . walleij @ linaro . org >
*/
# define pr_fmt(fmt) "Nomadik SRC clocks: " fmt
# include <linux/bitops.h>
2015-06-20 01:00:46 +03:00
# include <linux/slab.h>
2012-01-11 16:52:34 +04:00
# include <linux/err.h>
# include <linux/io.h>
# include <linux/clk-provider.h>
2013-04-16 23:38:29 +04:00
# include <linux/of.h>
2013-06-05 01:17:21 +04:00
# include <linux/of_address.h>
# include <linux/debugfs.h>
# include <linux/seq_file.h>
# include <linux/spinlock.h>
# include <linux/reboot.h>
2012-01-11 16:52:34 +04:00
/*
* The Nomadik clock tree is described in the STN8815A12 DB V4 .2
* reference manual for the chip , page 94 ff .
2013-06-05 01:17:21 +04:00
* Clock IDs are in the STn8815 Reference Manual table 3 , page 27.
*/
# define SRC_CR 0x00U
2013-09-13 23:45:51 +04:00
# define SRC_CR_T0_ENSEL BIT(15)
# define SRC_CR_T1_ENSEL BIT(17)
# define SRC_CR_T2_ENSEL BIT(19)
# define SRC_CR_T3_ENSEL BIT(21)
# define SRC_CR_T4_ENSEL BIT(23)
# define SRC_CR_T5_ENSEL BIT(25)
# define SRC_CR_T6_ENSEL BIT(27)
# define SRC_CR_T7_ENSEL BIT(29)
2013-06-05 01:17:21 +04:00
# define SRC_XTALCR 0x0CU
# define SRC_XTALCR_XTALTIMEN BIT(20)
# define SRC_XTALCR_SXTALDIS BIT(19)
# define SRC_XTALCR_MXTALSTAT BIT(2)
# define SRC_XTALCR_MXTALEN BIT(1)
# define SRC_XTALCR_MXTALOVER BIT(0)
# define SRC_PLLCR 0x10U
# define SRC_PLLCR_PLLTIMEN BIT(29)
# define SRC_PLLCR_PLL2EN BIT(28)
# define SRC_PLLCR_PLL1STAT BIT(2)
# define SRC_PLLCR_PLL1EN BIT(1)
# define SRC_PLLCR_PLL1OVER BIT(0)
# define SRC_PLLFR 0x14U
# define SRC_PCKEN0 0x24U
# define SRC_PCKDIS0 0x28U
# define SRC_PCKENSR0 0x2CU
# define SRC_PCKSR0 0x30U
# define SRC_PCKEN1 0x34U
# define SRC_PCKDIS1 0x38U
# define SRC_PCKENSR1 0x3CU
# define SRC_PCKSR1 0x40U
/* Lock protecting the SRC_CR register */
static DEFINE_SPINLOCK ( src_lock ) ;
/* Base address of the SRC */
static void __iomem * src_base ;
2013-09-22 20:17:28 +04:00
static int nomadik_clk_reboot_handler ( struct notifier_block * this ,
unsigned long code ,
void * unused )
{
u32 val ;
/* The main chrystal need to be enabled for reboot to work */
val = readl ( src_base + SRC_XTALCR ) ;
val & = ~ SRC_XTALCR_MXTALOVER ;
val | = SRC_XTALCR_MXTALEN ;
pr_crit ( " force-enabling MXTALO \n " ) ;
writel ( val , src_base + SRC_XTALCR ) ;
return NOTIFY_OK ;
}
static struct notifier_block nomadik_clk_reboot_notifier = {
. notifier_call = nomadik_clk_reboot_handler ,
} ;
static const struct of_device_id nomadik_src_match [ ] __initconst = {
{ . compatible = " stericsson,nomadik-src " } ,
{ /* sentinel */ }
} ;
2013-10-07 21:53:13 +04:00
static void __init nomadik_src_init ( void )
2013-09-22 20:17:28 +04:00
{
struct device_node * np ;
u32 val ;
np = of_find_matching_node ( NULL , nomadik_src_match ) ;
if ( ! np ) {
pr_crit ( " no matching node for SRC, aborting clock init \n " ) ;
return ;
}
src_base = of_iomap ( np , 0 ) ;
if ( ! src_base ) {
2018-08-28 18:44:29 +03:00
pr_err ( " %s: must have src parent node with REGS (%pOFn) \n " ,
__func__ , np ) ;
2013-09-22 20:17:28 +04:00
return ;
}
/* Set all timers to use the 2.4 MHz TIMCLK */
val = readl ( src_base + SRC_CR ) ;
val | = SRC_CR_T0_ENSEL ;
val | = SRC_CR_T1_ENSEL ;
val | = SRC_CR_T2_ENSEL ;
val | = SRC_CR_T3_ENSEL ;
val | = SRC_CR_T4_ENSEL ;
val | = SRC_CR_T5_ENSEL ;
val | = SRC_CR_T6_ENSEL ;
val | = SRC_CR_T7_ENSEL ;
writel ( val , src_base + SRC_CR ) ;
val = readl ( src_base + SRC_XTALCR ) ;
pr_info ( " SXTALO is %s \n " ,
( val & SRC_XTALCR_SXTALDIS ) ? " disabled " : " enabled " ) ;
pr_info ( " MXTAL is %s \n " ,
( val & SRC_XTALCR_MXTALSTAT ) ? " enabled " : " disabled " ) ;
if ( of_property_read_bool ( np , " disable-sxtalo " ) ) {
/* The machine uses an external oscillator circuit */
val | = SRC_XTALCR_SXTALDIS ;
pr_info ( " disabling SXTALO \n " ) ;
}
if ( of_property_read_bool ( np , " disable-mxtalo " ) ) {
/* Disable this too: also run by external oscillator */
val | = SRC_XTALCR_MXTALOVER ;
val & = ~ SRC_XTALCR_MXTALEN ;
pr_info ( " disabling MXTALO \n " ) ;
}
writel ( val , src_base + SRC_XTALCR ) ;
register_reboot_notifier ( & nomadik_clk_reboot_notifier ) ;
}
2013-06-05 01:17:21 +04:00
/**
* struct clk_pll1 - Nomadik PLL1 clock
* @ hw : corresponding clock hardware entry
* @ id : PLL instance : 1 or 2
*/
struct clk_pll {
struct clk_hw hw ;
int id ;
} ;
/**
* struct clk_src - Nomadik src clock
* @ hw : corresponding clock hardware entry
* @ id : the clock ID
* @ group1 : true if the clock is in group1 , else it is in group0
* @ clkbit : bit 0. . .31 corresponding to the clock in each clock register
*/
struct clk_src {
struct clk_hw hw ;
int id ;
bool group1 ;
u32 clkbit ;
} ;
# define to_pll(_hw) container_of(_hw, struct clk_pll, hw)
# define to_src(_hw) container_of(_hw, struct clk_src, hw)
static int pll_clk_enable ( struct clk_hw * hw )
{
struct clk_pll * pll = to_pll ( hw ) ;
u32 val ;
spin_lock ( & src_lock ) ;
val = readl ( src_base + SRC_PLLCR ) ;
if ( pll - > id = = 1 ) {
if ( val & SRC_PLLCR_PLL1OVER ) {
val | = SRC_PLLCR_PLL1EN ;
writel ( val , src_base + SRC_PLLCR ) ;
}
} else if ( pll - > id = = 2 ) {
val | = SRC_PLLCR_PLL2EN ;
writel ( val , src_base + SRC_PLLCR ) ;
}
spin_unlock ( & src_lock ) ;
return 0 ;
}
static void pll_clk_disable ( struct clk_hw * hw )
{
struct clk_pll * pll = to_pll ( hw ) ;
u32 val ;
spin_lock ( & src_lock ) ;
val = readl ( src_base + SRC_PLLCR ) ;
if ( pll - > id = = 1 ) {
if ( val & SRC_PLLCR_PLL1OVER ) {
val & = ~ SRC_PLLCR_PLL1EN ;
writel ( val , src_base + SRC_PLLCR ) ;
}
} else if ( pll - > id = = 2 ) {
val & = ~ SRC_PLLCR_PLL2EN ;
writel ( val , src_base + SRC_PLLCR ) ;
}
spin_unlock ( & src_lock ) ;
}
static int pll_clk_is_enabled ( struct clk_hw * hw )
{
struct clk_pll * pll = to_pll ( hw ) ;
u32 val ;
val = readl ( src_base + SRC_PLLCR ) ;
if ( pll - > id = = 1 ) {
if ( val & SRC_PLLCR_PLL1OVER )
return ! ! ( val & SRC_PLLCR_PLL1EN ) ;
} else if ( pll - > id = = 2 ) {
return ! ! ( val & SRC_PLLCR_PLL2EN ) ;
}
return 1 ;
}
static unsigned long pll_clk_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_pll * pll = to_pll ( hw ) ;
u32 val ;
val = readl ( src_base + SRC_PLLFR ) ;
if ( pll - > id = = 1 ) {
u8 mul ;
u8 div ;
mul = ( val > > 8 ) & 0x3FU ;
mul + = 2 ;
div = val & 0x07U ;
return ( parent_rate * mul ) > > div ;
}
if ( pll - > id = = 2 ) {
u8 mul ;
mul = ( val > > 24 ) & 0x3FU ;
mul + = 2 ;
return ( parent_rate * mul ) ;
}
/* Unknown PLL */
return 0 ;
}
static const struct clk_ops pll_clk_ops = {
. enable = pll_clk_enable ,
. disable = pll_clk_disable ,
. is_enabled = pll_clk_is_enabled ,
. recalc_rate = pll_clk_recalc_rate ,
} ;
2016-06-02 02:15:19 +03:00
static struct clk_hw * __init
2013-06-05 01:17:21 +04:00
pll_clk_register ( struct device * dev , const char * name ,
const char * parent_name , u32 id )
{
2016-06-02 02:15:19 +03:00
int ret ;
2013-06-05 01:17:21 +04:00
struct clk_pll * pll ;
struct clk_init_data init ;
if ( id ! = 1 & & id ! = 2 ) {
pr_err ( " %s: the Nomadik has only PLL 1 & 2 \n " , __func__ ) ;
return ERR_PTR ( - EINVAL ) ;
}
pll = kzalloc ( sizeof ( * pll ) , GFP_KERNEL ) ;
2017-04-20 11:04:00 +03:00
if ( ! pll )
2013-06-05 01:17:21 +04:00
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & pll_clk_ops ;
init . parent_names = ( parent_name ? & parent_name : NULL ) ;
init . num_parents = ( parent_name ? 1 : 0 ) ;
pll - > hw . init = & init ;
pll - > id = id ;
pr_debug ( " register PLL1 clock \" %s \" \n " , name ) ;
2016-06-02 02:15:19 +03:00
ret = clk_hw_register ( dev , & pll - > hw ) ;
if ( ret ) {
2013-06-05 01:17:21 +04:00
kfree ( pll ) ;
2016-06-02 02:15:19 +03:00
return ERR_PTR ( ret ) ;
}
2013-06-05 01:17:21 +04:00
2016-06-02 02:15:19 +03:00
return & pll - > hw ;
2013-06-05 01:17:21 +04:00
}
/*
* The Nomadik SRC clocks are gated , but not in the sense that
* you read - modify - write a register . Instead there are separate
* clock enable and clock disable registers . Writing a ' 1 ' bit in
* the enable register for a certain clock ungates that clock without
* affecting the other clocks . The disable register works the opposite
* way .
2012-01-11 16:52:34 +04:00
*/
2013-06-05 01:17:21 +04:00
static int src_clk_enable ( struct clk_hw * hw )
{
struct clk_src * sclk = to_src ( hw ) ;
u32 enreg = sclk - > group1 ? SRC_PCKEN1 : SRC_PCKEN0 ;
u32 sreg = sclk - > group1 ? SRC_PCKSR1 : SRC_PCKSR0 ;
writel ( sclk - > clkbit , src_base + enreg ) ;
/* spin until enabled */
while ( ! ( readl ( src_base + sreg ) & sclk - > clkbit ) )
cpu_relax ( ) ;
return 0 ;
}
static void src_clk_disable ( struct clk_hw * hw )
{
struct clk_src * sclk = to_src ( hw ) ;
u32 disreg = sclk - > group1 ? SRC_PCKDIS1 : SRC_PCKDIS0 ;
u32 sreg = sclk - > group1 ? SRC_PCKSR1 : SRC_PCKSR0 ;
writel ( sclk - > clkbit , src_base + disreg ) ;
/* spin until disabled */
while ( readl ( src_base + sreg ) & sclk - > clkbit )
cpu_relax ( ) ;
}
static int src_clk_is_enabled ( struct clk_hw * hw )
{
struct clk_src * sclk = to_src ( hw ) ;
u32 sreg = sclk - > group1 ? SRC_PCKSR1 : SRC_PCKSR0 ;
u32 val = readl ( src_base + sreg ) ;
return ! ! ( val & sclk - > clkbit ) ;
}
static unsigned long
src_clk_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
return parent_rate ;
}
static const struct clk_ops src_clk_ops = {
. enable = src_clk_enable ,
. disable = src_clk_disable ,
. is_enabled = src_clk_is_enabled ,
. recalc_rate = src_clk_recalc_rate ,
} ;
2016-06-02 02:15:19 +03:00
static struct clk_hw * __init
2013-06-05 01:17:21 +04:00
src_clk_register ( struct device * dev , const char * name ,
const char * parent_name , u8 id )
{
2016-06-02 02:15:19 +03:00
int ret ;
2013-06-05 01:17:21 +04:00
struct clk_src * sclk ;
struct clk_init_data init ;
sclk = kzalloc ( sizeof ( * sclk ) , GFP_KERNEL ) ;
2017-04-20 11:04:00 +03:00
if ( ! sclk )
2013-06-05 01:17:21 +04:00
return ERR_PTR ( - ENOMEM ) ;
2017-04-20 11:04:00 +03:00
2013-06-05 01:17:21 +04:00
init . name = name ;
init . ops = & src_clk_ops ;
/* Do not force-disable the static SDRAM controller */
if ( id = = 2 )
init . flags = CLK_IGNORE_UNUSED ;
else
init . flags = 0 ;
init . parent_names = ( parent_name ? & parent_name : NULL ) ;
init . num_parents = ( parent_name ? 1 : 0 ) ;
sclk - > hw . init = & init ;
sclk - > id = id ;
sclk - > group1 = ( id > 31 ) ;
sclk - > clkbit = BIT ( id & 0x1f ) ;
pr_debug ( " register clock \" %s \" ID: %d group: %d bits: %08x \n " ,
name , id , sclk - > group1 , sclk - > clkbit ) ;
2016-06-02 02:15:19 +03:00
ret = clk_hw_register ( dev , & sclk - > hw ) ;
if ( ret ) {
2013-06-05 01:17:21 +04:00
kfree ( sclk ) ;
2016-06-02 02:15:19 +03:00
return ERR_PTR ( ret ) ;
}
2013-06-05 01:17:21 +04:00
2016-06-02 02:15:19 +03:00
return & sclk - > hw ;
2013-06-05 01:17:21 +04:00
}
# ifdef CONFIG_DEBUG_FS
static u32 src_pcksr0_boot ;
static u32 src_pcksr1_boot ;
static const char * const src_clk_names [ ] = {
" HCLKDMA0 " ,
" HCLKSMC " ,
" HCLKSDRAM " ,
" HCLKDMA1 " ,
" HCLKCLCD " ,
" PCLKIRDA " ,
" PCLKSSP " ,
" PCLKUART0 " ,
" PCLKSDI " ,
" PCLKI2C0 " ,
" PCLKI2C1 " ,
" PCLKUART1 " ,
" PCLMSP0 " ,
" HCLKUSB " ,
" HCLKDIF " ,
" HCLKSAA " ,
" HCLKSVA " ,
" PCLKHSI " ,
" PCLKXTI " ,
" PCLKUART2 " ,
" PCLKMSP1 " ,
" PCLKMSP2 " ,
" PCLKOWM " ,
" HCLKHPI " ,
" PCLKSKE " ,
" PCLKHSEM " ,
" HCLK3D " ,
" HCLKHASH " ,
" HCLKCRYP " ,
" PCLKMSHC " ,
" HCLKUSBM " ,
" HCLKRNG " ,
" RESERVED " ,
" RESERVED " ,
" RESERVED " ,
" RESERVED " ,
" CLDCLK " ,
" IRDACLK " ,
" SSPICLK " ,
" UART0CLK " ,
" SDICLK " ,
" I2C0CLK " ,
" I2C1CLK " ,
" UART1CLK " ,
" MSPCLK0 " ,
" USBCLK " ,
" DIFCLK " ,
" IPI2CCLK " ,
" IPBMCCLK " ,
" HSICLKRX " ,
" HSICLKTX " ,
" UART2CLK " ,
" MSPCLK1 " ,
" MSPCLK2 " ,
" OWMCLK " ,
" RESERVED " ,
" SKECLK " ,
" RESERVED " ,
" 3DCLK " ,
" PCLKMSP3 " ,
" MSPCLK3 " ,
" MSHCCLK " ,
" USBMCLK " ,
" RNGCCLK " ,
} ;
2018-11-23 18:06:00 +03:00
static int nomadik_src_clk_debugfs_show ( struct seq_file * s , void * what )
2013-06-05 01:17:21 +04:00
{
int i ;
u32 src_pcksr0 = readl ( src_base + SRC_PCKSR0 ) ;
u32 src_pcksr1 = readl ( src_base + SRC_PCKSR1 ) ;
u32 src_pckensr0 = readl ( src_base + SRC_PCKENSR0 ) ;
u32 src_pckensr1 = readl ( src_base + SRC_PCKENSR1 ) ;
2017-04-20 10:45:04 +03:00
seq_puts ( s , " Clock: Boot: Now: Request: ASKED: \n " ) ;
2013-06-05 01:17:21 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( src_clk_names ) ; i + + ) {
u32 pcksrb = ( i < 0x20 ) ? src_pcksr0_boot : src_pcksr1_boot ;
u32 pcksr = ( i < 0x20 ) ? src_pcksr0 : src_pcksr1 ;
u32 pckreq = ( i < 0x20 ) ? src_pckensr0 : src_pckensr1 ;
u32 mask = BIT ( i & 0x1f ) ;
seq_printf ( s , " %s %s %s %s \n " ,
src_clk_names [ i ] ,
( pcksrb & mask ) ? " on " : " off " ,
( pcksr & mask ) ? " on " : " off " ,
( pckreq & mask ) ? " on " : " off " ) ;
}
return 0 ;
}
2018-11-23 18:06:00 +03:00
DEFINE_SHOW_ATTRIBUTE ( nomadik_src_clk_debugfs ) ;
2013-06-05 01:17:21 +04:00
static int __init nomadik_src_clk_init_debugfs ( void )
{
2014-01-21 12:06:21 +04:00
/* Vital for multiplatform */
if ( ! src_base )
return - ENODEV ;
2013-06-05 01:17:21 +04:00
src_pcksr0_boot = readl ( src_base + SRC_PCKSR0 ) ;
src_pcksr1_boot = readl ( src_base + SRC_PCKSR1 ) ;
debugfs_create_file ( " nomadik-src-clk " , S_IFREG | S_IRUGO ,
2018-11-23 18:06:00 +03:00
NULL , NULL , & nomadik_src_clk_debugfs_fops ) ;
2013-06-05 01:17:21 +04:00
return 0 ;
}
2015-05-02 03:05:51 +03:00
device_initcall ( nomadik_src_clk_init_debugfs ) ;
2013-06-05 01:17:21 +04:00
# endif
static void __init of_nomadik_pll_setup ( struct device_node * np )
{
2016-06-02 02:15:19 +03:00
struct clk_hw * hw ;
2013-06-05 01:17:21 +04:00
const char * clk_name = np - > name ;
const char * parent_name ;
u32 pll_id ;
2013-09-17 02:32:34 +04:00
if ( ! src_base )
nomadik_src_init ( ) ;
2013-06-05 01:17:21 +04:00
if ( of_property_read_u32 ( np , " pll-id " , & pll_id ) ) {
pr_err ( " %s: PLL \" %s \" missing pll-id property \n " ,
__func__ , clk_name ) ;
return ;
}
parent_name = of_clk_get_parent_name ( np , 0 ) ;
2016-06-02 02:15:19 +03:00
hw = pll_clk_register ( NULL , clk_name , parent_name , pll_id ) ;
if ( ! IS_ERR ( hw ) )
of_clk_add_hw_provider ( np , of_clk_hw_simple_get , hw ) ;
2013-06-05 01:17:21 +04:00
}
2013-09-17 02:32:34 +04:00
CLK_OF_DECLARE ( nomadik_pll_clk ,
" st,nomadik-pll-clock " , of_nomadik_pll_setup ) ;
2013-06-05 01:17:21 +04:00
static void __init of_nomadik_hclk_setup ( struct device_node * np )
{
2016-06-02 02:15:19 +03:00
struct clk_hw * hw ;
2013-06-05 01:17:21 +04:00
const char * clk_name = np - > name ;
const char * parent_name ;
2013-09-17 02:32:34 +04:00
if ( ! src_base )
nomadik_src_init ( ) ;
2013-06-05 01:17:21 +04:00
parent_name = of_clk_get_parent_name ( np , 0 ) ;
/*
* The HCLK divides PLL1 with 1 ( passthru ) , 2 , 3 or 4.
*/
2016-06-02 02:15:19 +03:00
hw = clk_hw_register_divider ( NULL , clk_name , parent_name ,
2013-06-05 01:17:21 +04:00
0 , src_base + SRC_CR ,
13 , 2 ,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO ,
& src_lock ) ;
2016-06-02 02:15:19 +03:00
if ( ! IS_ERR ( hw ) )
of_clk_add_hw_provider ( np , of_clk_hw_simple_get , hw ) ;
2013-06-05 01:17:21 +04:00
}
2013-09-17 02:32:34 +04:00
CLK_OF_DECLARE ( nomadik_hclk_clk ,
" st,nomadik-hclk-clock " , of_nomadik_hclk_setup ) ;
2013-06-05 01:17:21 +04:00
static void __init of_nomadik_src_clk_setup ( struct device_node * np )
{
2016-06-02 02:15:19 +03:00
struct clk_hw * hw ;
2013-06-05 01:17:21 +04:00
const char * clk_name = np - > name ;
const char * parent_name ;
u32 clk_id ;
2013-09-17 02:32:34 +04:00
if ( ! src_base )
nomadik_src_init ( ) ;
2013-06-05 01:17:21 +04:00
if ( of_property_read_u32 ( np , " clock-id " , & clk_id ) ) {
pr_err ( " %s: SRC clock \" %s \" missing clock-id property \n " ,
__func__ , clk_name ) ;
return ;
}
parent_name = of_clk_get_parent_name ( np , 0 ) ;
2016-06-02 02:15:19 +03:00
hw = src_clk_register ( NULL , clk_name , parent_name , clk_id ) ;
if ( ! IS_ERR ( hw ) )
of_clk_add_hw_provider ( np , of_clk_hw_simple_get , hw ) ;
2013-06-05 01:17:21 +04:00
}
2013-09-17 02:32:34 +04:00
CLK_OF_DECLARE ( nomadik_src_clk ,
" st,nomadik-src-clock " , of_nomadik_src_clk_setup ) ;