2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-05-07 20:02:15 +04:00
/*
* drivers / clk / at91 / sckc . c
*
* Copyright ( C ) 2013 Boris BREZILLON < b . brezillon @ overkiz . com >
*/
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
2016-09-20 23:58:29 +03:00
# include <linux/delay.h>
2014-05-07 20:02:15 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/io.h>
2016-09-20 23:58:29 +03:00
# define SLOW_CLOCK_FREQ 32768
# define SLOWCK_SW_CYCLES 5
# define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
SLOW_CLOCK_FREQ )
# define AT91_SCKC_CR 0x00
2019-05-21 13:11:26 +03:00
struct clk_slow_bits {
u32 cr_rcen ;
u32 cr_osc32en ;
u32 cr_osc32byp ;
u32 cr_oscsel ;
} ;
2016-09-20 23:58:29 +03:00
struct clk_slow_osc {
struct clk_hw hw ;
void __iomem * sckcr ;
2019-05-21 13:11:26 +03:00
const struct clk_slow_bits * bits ;
2016-09-20 23:58:29 +03:00
unsigned long startup_usec ;
} ;
# define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
2016-09-20 23:58:30 +03:00
struct clk_sama5d4_slow_osc {
struct clk_hw hw ;
void __iomem * sckcr ;
2019-05-21 13:11:26 +03:00
const struct clk_slow_bits * bits ;
2016-09-20 23:58:30 +03:00
unsigned long startup_usec ;
bool prepared ;
} ;
# define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
2016-09-20 23:58:29 +03:00
struct clk_slow_rc_osc {
struct clk_hw hw ;
void __iomem * sckcr ;
2019-05-21 13:11:26 +03:00
const struct clk_slow_bits * bits ;
2016-09-20 23:58:29 +03:00
unsigned long frequency ;
unsigned long accuracy ;
unsigned long startup_usec ;
} ;
# define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
struct clk_sam9x5_slow {
struct clk_hw hw ;
void __iomem * sckcr ;
2019-05-21 13:11:26 +03:00
const struct clk_slow_bits * bits ;
2016-09-20 23:58:29 +03:00
u8 parent ;
} ;
# define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
static int clk_slow_osc_prepare ( struct clk_hw * hw )
{
struct clk_slow_osc * osc = to_clk_slow_osc ( hw ) ;
void __iomem * sckcr = osc - > sckcr ;
u32 tmp = readl ( sckcr ) ;
2019-05-21 13:11:26 +03:00
if ( tmp & ( osc - > bits - > cr_osc32byp | osc - > bits - > cr_osc32en ) )
2016-09-20 23:58:29 +03:00
return 0 ;
2019-05-21 13:11:26 +03:00
writel ( tmp | osc - > bits - > cr_osc32en , sckcr ) ;
2016-09-20 23:58:29 +03:00
2019-09-20 18:39:06 +03:00
if ( system_state < SYSTEM_RUNNING )
udelay ( osc - > startup_usec ) ;
else
usleep_range ( osc - > startup_usec , osc - > startup_usec + 1 ) ;
2016-09-20 23:58:29 +03:00
return 0 ;
}
static void clk_slow_osc_unprepare ( struct clk_hw * hw )
{
struct clk_slow_osc * osc = to_clk_slow_osc ( hw ) ;
void __iomem * sckcr = osc - > sckcr ;
u32 tmp = readl ( sckcr ) ;
2019-05-21 13:11:26 +03:00
if ( tmp & osc - > bits - > cr_osc32byp )
2016-09-20 23:58:29 +03:00
return ;
2019-05-21 13:11:26 +03:00
writel ( tmp & ~ osc - > bits - > cr_osc32en , sckcr ) ;
2016-09-20 23:58:29 +03:00
}
static int clk_slow_osc_is_prepared ( struct clk_hw * hw )
{
struct clk_slow_osc * osc = to_clk_slow_osc ( hw ) ;
void __iomem * sckcr = osc - > sckcr ;
u32 tmp = readl ( sckcr ) ;
2019-05-21 13:11:26 +03:00
if ( tmp & osc - > bits - > cr_osc32byp )
2016-09-20 23:58:29 +03:00
return 1 ;
2019-05-21 13:11:26 +03:00
return ! ! ( tmp & osc - > bits - > cr_osc32en ) ;
2016-09-20 23:58:29 +03:00
}
static const struct clk_ops slow_osc_ops = {
. prepare = clk_slow_osc_prepare ,
. unprepare = clk_slow_osc_unprepare ,
. is_prepared = clk_slow_osc_is_prepared ,
} ;
static struct clk_hw * __init
at91_clk_register_slow_osc ( void __iomem * sckcr ,
const char * name ,
const char * parent_name ,
unsigned long startup ,
2019-05-21 13:11:26 +03:00
bool bypass ,
const struct clk_slow_bits * bits )
2016-09-20 23:58:29 +03:00
{
struct clk_slow_osc * osc ;
struct clk_hw * hw ;
struct clk_init_data init ;
int ret ;
if ( ! sckcr | | ! name | | ! parent_name )
return ERR_PTR ( - EINVAL ) ;
osc = kzalloc ( sizeof ( * osc ) , GFP_KERNEL ) ;
if ( ! osc )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & slow_osc_ops ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
init . flags = CLK_IGNORE_UNUSED ;
osc - > hw . init = & init ;
osc - > sckcr = sckcr ;
osc - > startup_usec = startup ;
2019-05-21 13:11:26 +03:00
osc - > bits = bits ;
2016-09-20 23:58:29 +03:00
if ( bypass )
2019-05-21 13:11:26 +03:00
writel ( ( readl ( sckcr ) & ~ osc - > bits - > cr_osc32en ) |
osc - > bits - > cr_osc32byp , sckcr ) ;
2016-09-20 23:58:29 +03:00
hw = & osc - > hw ;
ret = clk_hw_register ( NULL , & osc - > hw ) ;
if ( ret ) {
kfree ( osc ) ;
hw = ERR_PTR ( ret ) ;
}
return hw ;
}
2019-06-27 18:53:39 +03:00
static void at91_clk_unregister_slow_osc ( struct clk_hw * hw )
{
struct clk_slow_osc * osc = to_clk_slow_osc ( hw ) ;
clk_hw_unregister ( hw ) ;
kfree ( osc ) ;
}
2016-09-20 23:58:29 +03:00
static unsigned long clk_slow_rc_osc_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_slow_rc_osc * osc = to_clk_slow_rc_osc ( hw ) ;
return osc - > frequency ;
}
static unsigned long clk_slow_rc_osc_recalc_accuracy ( struct clk_hw * hw ,
unsigned long parent_acc )
{
struct clk_slow_rc_osc * osc = to_clk_slow_rc_osc ( hw ) ;
return osc - > accuracy ;
}
static int clk_slow_rc_osc_prepare ( struct clk_hw * hw )
{
struct clk_slow_rc_osc * osc = to_clk_slow_rc_osc ( hw ) ;
void __iomem * sckcr = osc - > sckcr ;
2019-05-21 13:11:26 +03:00
writel ( readl ( sckcr ) | osc - > bits - > cr_rcen , sckcr ) ;
2016-09-20 23:58:29 +03:00
2019-09-20 18:39:06 +03:00
if ( system_state < SYSTEM_RUNNING )
udelay ( osc - > startup_usec ) ;
else
usleep_range ( osc - > startup_usec , osc - > startup_usec + 1 ) ;
2016-09-20 23:58:29 +03:00
return 0 ;
}
static void clk_slow_rc_osc_unprepare ( struct clk_hw * hw )
{
struct clk_slow_rc_osc * osc = to_clk_slow_rc_osc ( hw ) ;
void __iomem * sckcr = osc - > sckcr ;
2019-05-21 13:11:26 +03:00
writel ( readl ( sckcr ) & ~ osc - > bits - > cr_rcen , sckcr ) ;
2016-09-20 23:58:29 +03:00
}
static int clk_slow_rc_osc_is_prepared ( struct clk_hw * hw )
{
struct clk_slow_rc_osc * osc = to_clk_slow_rc_osc ( hw ) ;
2019-05-21 13:11:26 +03:00
return ! ! ( readl ( osc - > sckcr ) & osc - > bits - > cr_rcen ) ;
2016-09-20 23:58:29 +03:00
}
static const struct clk_ops slow_rc_osc_ops = {
. prepare = clk_slow_rc_osc_prepare ,
. unprepare = clk_slow_rc_osc_unprepare ,
. is_prepared = clk_slow_rc_osc_is_prepared ,
. recalc_rate = clk_slow_rc_osc_recalc_rate ,
. recalc_accuracy = clk_slow_rc_osc_recalc_accuracy ,
} ;
static struct clk_hw * __init
at91_clk_register_slow_rc_osc ( void __iomem * sckcr ,
const char * name ,
unsigned long frequency ,
unsigned long accuracy ,
2019-05-21 13:11:26 +03:00
unsigned long startup ,
const struct clk_slow_bits * bits )
2016-09-20 23:58:29 +03:00
{
struct clk_slow_rc_osc * osc ;
struct clk_hw * hw ;
struct clk_init_data init ;
int ret ;
if ( ! sckcr | | ! name )
return ERR_PTR ( - EINVAL ) ;
osc = kzalloc ( sizeof ( * osc ) , GFP_KERNEL ) ;
if ( ! osc )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & slow_rc_osc_ops ;
init . parent_names = NULL ;
init . num_parents = 0 ;
init . flags = CLK_IGNORE_UNUSED ;
osc - > hw . init = & init ;
osc - > sckcr = sckcr ;
2019-05-21 13:11:26 +03:00
osc - > bits = bits ;
2016-09-20 23:58:29 +03:00
osc - > frequency = frequency ;
osc - > accuracy = accuracy ;
osc - > startup_usec = startup ;
hw = & osc - > hw ;
ret = clk_hw_register ( NULL , & osc - > hw ) ;
if ( ret ) {
kfree ( osc ) ;
hw = ERR_PTR ( ret ) ;
}
return hw ;
}
2019-06-27 18:53:40 +03:00
static void at91_clk_unregister_slow_rc_osc ( struct clk_hw * hw )
{
struct clk_slow_rc_osc * osc = to_clk_slow_rc_osc ( hw ) ;
clk_hw_unregister ( hw ) ;
kfree ( osc ) ;
}
2016-09-20 23:58:29 +03:00
static int clk_sam9x5_slow_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_sam9x5_slow * slowck = to_clk_sam9x5_slow ( hw ) ;
void __iomem * sckcr = slowck - > sckcr ;
u32 tmp ;
if ( index > 1 )
return - EINVAL ;
tmp = readl ( sckcr ) ;
2019-05-21 13:11:26 +03:00
if ( ( ! index & & ! ( tmp & slowck - > bits - > cr_oscsel ) ) | |
( index & & ( tmp & slowck - > bits - > cr_oscsel ) ) )
2016-09-20 23:58:29 +03:00
return 0 ;
if ( index )
2019-05-21 13:11:26 +03:00
tmp | = slowck - > bits - > cr_oscsel ;
2016-09-20 23:58:29 +03:00
else
2019-05-21 13:11:26 +03:00
tmp & = ~ slowck - > bits - > cr_oscsel ;
2016-09-20 23:58:29 +03:00
writel ( tmp , sckcr ) ;
2019-09-20 18:39:06 +03:00
if ( system_state < SYSTEM_RUNNING )
udelay ( SLOWCK_SW_TIME_USEC ) ;
else
usleep_range ( SLOWCK_SW_TIME_USEC , SLOWCK_SW_TIME_USEC + 1 ) ;
2016-09-20 23:58:29 +03:00
return 0 ;
}
static u8 clk_sam9x5_slow_get_parent ( struct clk_hw * hw )
{
struct clk_sam9x5_slow * slowck = to_clk_sam9x5_slow ( hw ) ;
2019-05-21 13:11:26 +03:00
return ! ! ( readl ( slowck - > sckcr ) & slowck - > bits - > cr_oscsel ) ;
2016-09-20 23:58:29 +03:00
}
static const struct clk_ops sam9x5_slow_ops = {
. set_parent = clk_sam9x5_slow_set_parent ,
. get_parent = clk_sam9x5_slow_get_parent ,
} ;
static struct clk_hw * __init
at91_clk_register_sam9x5_slow ( void __iomem * sckcr ,
const char * name ,
const char * * parent_names ,
2019-05-21 13:11:26 +03:00
int num_parents ,
const struct clk_slow_bits * bits )
2016-09-20 23:58:29 +03:00
{
struct clk_sam9x5_slow * slowck ;
struct clk_hw * hw ;
struct clk_init_data init ;
int ret ;
if ( ! sckcr | | ! name | | ! parent_names | | ! num_parents )
return ERR_PTR ( - EINVAL ) ;
slowck = kzalloc ( sizeof ( * slowck ) , GFP_KERNEL ) ;
if ( ! slowck )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & sam9x5_slow_ops ;
init . parent_names = parent_names ;
init . num_parents = num_parents ;
init . flags = 0 ;
slowck - > hw . init = & init ;
slowck - > sckcr = sckcr ;
2019-05-21 13:11:26 +03:00
slowck - > bits = bits ;
slowck - > parent = ! ! ( readl ( sckcr ) & slowck - > bits - > cr_oscsel ) ;
2016-09-20 23:58:29 +03:00
hw = & slowck - > hw ;
ret = clk_hw_register ( NULL , & slowck - > hw ) ;
if ( ret ) {
kfree ( slowck ) ;
hw = ERR_PTR ( ret ) ;
}
return hw ;
}
2019-06-27 18:53:41 +03:00
static void at91_clk_unregister_sam9x5_slow ( struct clk_hw * hw )
{
struct clk_sam9x5_slow * slowck = to_clk_sam9x5_slow ( hw ) ;
clk_hw_unregister ( hw ) ;
kfree ( slowck ) ;
}
2019-02-19 20:01:56 +03:00
static void __init at91sam9x5_sckc_register ( struct device_node * np ,
2019-05-21 13:11:26 +03:00
unsigned int rc_osc_startup_us ,
const struct clk_slow_bits * bits )
2016-09-20 23:58:29 +03:00
{
2019-02-19 20:01:55 +03:00
const char * parent_names [ 2 ] = { " slow_rc_osc " , " slow_osc " } ;
void __iomem * regbase = of_iomap ( np , 0 ) ;
struct device_node * child = NULL ;
const char * xtal_name ;
2019-06-27 18:53:42 +03:00
struct clk_hw * slow_rc , * slow_osc , * slowck ;
2019-02-19 20:01:55 +03:00
bool bypass ;
2019-06-27 18:53:42 +03:00
int ret ;
2016-09-20 23:58:29 +03:00
2019-02-19 20:01:55 +03:00
if ( ! regbase )
2016-09-20 23:58:29 +03:00
return ;
2019-06-27 18:53:42 +03:00
slow_rc = at91_clk_register_slow_rc_osc ( regbase , parent_names [ 0 ] ,
32768 , 50000000 ,
rc_osc_startup_us , bits ) ;
if ( IS_ERR ( slow_rc ) )
2016-09-20 23:58:29 +03:00
return ;
2019-02-19 20:01:55 +03:00
xtal_name = of_clk_get_parent_name ( np , 0 ) ;
if ( ! xtal_name ) {
/* DT backward compatibility */
child = of_get_compatible_child ( np , " atmel,at91sam9x5-clk-slow-osc " ) ;
if ( ! child )
2019-06-27 18:53:42 +03:00
goto unregister_slow_rc ;
2014-05-07 20:02:15 +04:00
2019-02-19 20:01:55 +03:00
xtal_name = of_clk_get_parent_name ( child , 0 ) ;
bypass = of_property_read_bool ( child , " atmel,osc-bypass " ) ;
2014-05-07 20:02:15 +04:00
2019-02-19 20:01:55 +03:00
child = of_get_compatible_child ( np , " atmel,at91sam9x5-clk-slow " ) ;
} else {
bypass = of_property_read_bool ( np , " atmel,osc-bypass " ) ;
}
2014-05-07 20:02:15 +04:00
2019-02-19 20:01:55 +03:00
if ( ! xtal_name )
2019-06-27 18:53:42 +03:00
goto unregister_slow_rc ;
2019-02-19 20:01:55 +03:00
2019-06-27 18:53:42 +03:00
slow_osc = at91_clk_register_slow_osc ( regbase , parent_names [ 1 ] ,
xtal_name , 1200000 , bypass , bits ) ;
if ( IS_ERR ( slow_osc ) )
goto unregister_slow_rc ;
2019-02-19 20:01:55 +03:00
2019-06-27 18:53:42 +03:00
slowck = at91_clk_register_sam9x5_slow ( regbase , " slowck " , parent_names ,
2 , bits ) ;
if ( IS_ERR ( slowck ) )
goto unregister_slow_osc ;
2019-02-19 20:01:55 +03:00
/* DT backward compatibility */
if ( child )
2019-06-27 18:53:42 +03:00
ret = of_clk_add_hw_provider ( child , of_clk_hw_simple_get ,
slowck ) ;
else
ret = of_clk_add_hw_provider ( np , of_clk_hw_simple_get , slowck ) ;
if ( WARN_ON ( ret ) )
goto unregister_slowck ;
return ;
unregister_slowck :
at91_clk_unregister_sam9x5_slow ( slowck ) ;
unregister_slow_osc :
at91_clk_unregister_slow_osc ( slow_osc ) ;
unregister_slow_rc :
at91_clk_unregister_slow_rc_osc ( slow_rc ) ;
2014-05-07 20:02:15 +04:00
}
2019-02-19 20:01:56 +03:00
2019-05-21 13:11:26 +03:00
static const struct clk_slow_bits at91sam9x5_bits = {
. cr_rcen = BIT ( 0 ) ,
. cr_osc32en = BIT ( 1 ) ,
. cr_osc32byp = BIT ( 2 ) ,
. cr_oscsel = BIT ( 3 ) ,
} ;
2019-02-19 20:01:56 +03:00
static void __init of_at91sam9x5_sckc_setup ( struct device_node * np )
{
2019-05-21 13:11:26 +03:00
at91sam9x5_sckc_register ( np , 75 , & at91sam9x5_bits ) ;
2019-02-19 20:01:56 +03:00
}
2014-05-07 20:02:15 +04:00
CLK_OF_DECLARE ( at91sam9x5_clk_sckc , " atmel,at91sam9x5-sckc " ,
of_at91sam9x5_sckc_setup ) ;
2016-09-20 23:58:30 +03:00
2019-02-19 20:01:56 +03:00
static void __init of_sama5d3_sckc_setup ( struct device_node * np )
{
2019-05-21 13:11:26 +03:00
at91sam9x5_sckc_register ( np , 500 , & at91sam9x5_bits ) ;
2019-02-19 20:01:56 +03:00
}
CLK_OF_DECLARE ( sama5d3_clk_sckc , " atmel,sama5d3-sckc " ,
of_sama5d3_sckc_setup ) ;
2019-05-21 13:11:33 +03:00
static const struct clk_slow_bits at91sam9x60_bits = {
. cr_osc32en = BIT ( 1 ) ,
. cr_osc32byp = BIT ( 2 ) ,
. cr_oscsel = BIT ( 24 ) ,
} ;
static void __init of_sam9x60_sckc_setup ( struct device_node * np )
{
void __iomem * regbase = of_iomap ( np , 0 ) ;
struct clk_hw_onecell_data * clk_data ;
struct clk_hw * slow_rc , * slow_osc ;
const char * xtal_name ;
const char * parent_names [ 2 ] = { " slow_rc_osc " , " slow_osc " } ;
bool bypass ;
int ret ;
if ( ! regbase )
return ;
2020-07-22 10:38:16 +03:00
slow_rc = clk_hw_register_fixed_rate_with_accuracy ( NULL , parent_names [ 0 ] ,
NULL , 0 , 32768 ,
93750000 ) ;
2019-05-21 13:11:33 +03:00
if ( IS_ERR ( slow_rc ) )
return ;
xtal_name = of_clk_get_parent_name ( np , 0 ) ;
if ( ! xtal_name )
goto unregister_slow_rc ;
bypass = of_property_read_bool ( np , " atmel,osc-bypass " ) ;
slow_osc = at91_clk_register_slow_osc ( regbase , parent_names [ 1 ] ,
xtal_name , 5000000 , bypass ,
& at91sam9x60_bits ) ;
if ( IS_ERR ( slow_osc ) )
goto unregister_slow_rc ;
2019-09-27 21:51:10 +03:00
clk_data = kzalloc ( struct_size ( clk_data , hws , 2 ) , GFP_KERNEL ) ;
2019-05-21 13:11:33 +03:00
if ( ! clk_data )
goto unregister_slow_osc ;
/* MD_SLCK and TD_SLCK. */
clk_data - > num = 2 ;
clk_data - > hws [ 0 ] = clk_hw_register_fixed_rate ( NULL , " md_slck " ,
parent_names [ 0 ] ,
0 , 32768 ) ;
if ( IS_ERR ( clk_data - > hws [ 0 ] ) )
goto clk_data_free ;
clk_data - > hws [ 1 ] = at91_clk_register_sam9x5_slow ( regbase , " td_slck " ,
parent_names , 2 ,
& at91sam9x60_bits ) ;
if ( IS_ERR ( clk_data - > hws [ 1 ] ) )
goto unregister_md_slck ;
ret = of_clk_add_hw_provider ( np , of_clk_hw_onecell_get , clk_data ) ;
if ( WARN_ON ( ret ) )
goto unregister_td_slck ;
return ;
unregister_td_slck :
2019-06-27 18:53:45 +03:00
at91_clk_unregister_sam9x5_slow ( clk_data - > hws [ 1 ] ) ;
2019-05-21 13:11:33 +03:00
unregister_md_slck :
clk_hw_unregister ( clk_data - > hws [ 0 ] ) ;
clk_data_free :
kfree ( clk_data ) ;
unregister_slow_osc :
2019-06-27 18:53:45 +03:00
at91_clk_unregister_slow_osc ( slow_osc ) ;
2019-05-21 13:11:33 +03:00
unregister_slow_rc :
clk_hw_unregister ( slow_rc ) ;
}
CLK_OF_DECLARE ( sam9x60_clk_sckc , " microchip,sam9x60-sckc " ,
of_sam9x60_sckc_setup ) ;
2016-09-20 23:58:30 +03:00
static int clk_sama5d4_slow_osc_prepare ( struct clk_hw * hw )
{
struct clk_sama5d4_slow_osc * osc = to_clk_sama5d4_slow_osc ( hw ) ;
if ( osc - > prepared )
return 0 ;
/*
* Assume that if it has already been selected ( for example by the
* bootloader ) , enough time has aready passed .
*/
2019-05-21 13:11:26 +03:00
if ( ( readl ( osc - > sckcr ) & osc - > bits - > cr_oscsel ) ) {
2016-09-20 23:58:30 +03:00
osc - > prepared = true ;
return 0 ;
}
2019-09-20 18:39:06 +03:00
if ( system_state < SYSTEM_RUNNING )
udelay ( osc - > startup_usec ) ;
else
usleep_range ( osc - > startup_usec , osc - > startup_usec + 1 ) ;
2016-09-20 23:58:30 +03:00
osc - > prepared = true ;
return 0 ;
}
static int clk_sama5d4_slow_osc_is_prepared ( struct clk_hw * hw )
{
struct clk_sama5d4_slow_osc * osc = to_clk_sama5d4_slow_osc ( hw ) ;
return osc - > prepared ;
}
static const struct clk_ops sama5d4_slow_osc_ops = {
. prepare = clk_sama5d4_slow_osc_prepare ,
. is_prepared = clk_sama5d4_slow_osc_is_prepared ,
} ;
2019-05-21 13:11:26 +03:00
static const struct clk_slow_bits at91sama5d4_bits = {
. cr_oscsel = BIT ( 3 ) ,
} ;
2016-09-20 23:58:30 +03:00
static void __init of_sama5d4_sckc_setup ( struct device_node * np )
{
void __iomem * regbase = of_iomap ( np , 0 ) ;
2019-06-27 18:53:44 +03:00
struct clk_hw * slow_rc , * slowck ;
2016-09-20 23:58:30 +03:00
struct clk_sama5d4_slow_osc * osc ;
struct clk_init_data init ;
const char * xtal_name ;
const char * parent_names [ 2 ] = { " slow_rc_osc " , " slow_osc " } ;
int ret ;
if ( ! regbase )
return ;
2019-06-27 18:53:44 +03:00
slow_rc = clk_hw_register_fixed_rate_with_accuracy ( NULL ,
parent_names [ 0 ] ,
NULL , 0 , 32768 ,
250000000 ) ;
if ( IS_ERR ( slow_rc ) )
2016-09-20 23:58:30 +03:00
return ;
xtal_name = of_clk_get_parent_name ( np , 0 ) ;
osc = kzalloc ( sizeof ( * osc ) , GFP_KERNEL ) ;
if ( ! osc )
2019-06-27 18:53:44 +03:00
goto unregister_slow_rc ;
2016-09-20 23:58:30 +03:00
init . name = parent_names [ 1 ] ;
init . ops = & sama5d4_slow_osc_ops ;
init . parent_names = & xtal_name ;
init . num_parents = 1 ;
init . flags = CLK_IGNORE_UNUSED ;
osc - > hw . init = & init ;
osc - > sckcr = regbase ;
osc - > startup_usec = 1200000 ;
2019-05-21 13:11:26 +03:00
osc - > bits = & at91sama5d4_bits ;
2016-09-20 23:58:30 +03:00
ret = clk_hw_register ( NULL , & osc - > hw ) ;
2019-06-27 18:53:44 +03:00
if ( ret )
goto free_slow_osc_data ;
2016-09-20 23:58:30 +03:00
2019-06-27 18:53:44 +03:00
slowck = at91_clk_register_sam9x5_slow ( regbase , " slowck " ,
parent_names , 2 ,
& at91sama5d4_bits ) ;
if ( IS_ERR ( slowck ) )
goto unregister_slow_osc ;
2016-09-20 23:58:30 +03:00
2019-06-27 18:53:44 +03:00
ret = of_clk_add_hw_provider ( np , of_clk_hw_simple_get , slowck ) ;
if ( WARN_ON ( ret ) )
goto unregister_slowck ;
return ;
unregister_slowck :
at91_clk_unregister_sam9x5_slow ( slowck ) ;
unregister_slow_osc :
clk_hw_unregister ( & osc - > hw ) ;
free_slow_osc_data :
kfree ( osc ) ;
unregister_slow_rc :
clk_hw_unregister ( slow_rc ) ;
2016-09-20 23:58:30 +03:00
}
CLK_OF_DECLARE ( sama5d4_clk_sckc , " atmel,sama5d4-sckc " ,
of_sama5d4_sckc_setup ) ;