2019-06-04 10:10:53 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-09-26 15:41:01 +02:00
/*
* Copyright ( c ) 2014 Lucas Stach < l . stach @ pengutronix . de > , Pengutronix
*/
# include <linux/clk.h>
# include <linux/clk-provider.h>
2020-07-30 09:22:51 +08:00
# include <linux/export.h>
2014-09-26 15:41:01 +02:00
# include <linux/slab.h>
2015-04-29 18:34:42 -03:00
# include "clk.h"
2014-09-26 15:41:01 +02:00
struct clk_cpu {
struct clk_hw hw ;
struct clk * div ;
struct clk * mux ;
struct clk * pll ;
struct clk * step ;
} ;
static inline struct clk_cpu * to_clk_cpu ( struct clk_hw * hw )
{
return container_of ( hw , struct clk_cpu , hw ) ;
}
static unsigned long clk_cpu_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_cpu * cpu = to_clk_cpu ( hw ) ;
return clk_get_rate ( cpu - > div ) ;
}
static long clk_cpu_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
struct clk_cpu * cpu = to_clk_cpu ( hw ) ;
return clk_round_rate ( cpu - > pll , rate ) ;
}
static int clk_cpu_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_cpu * cpu = to_clk_cpu ( hw ) ;
int ret ;
/* switch to PLL bypass clock */
ret = clk_set_parent ( cpu - > mux , cpu - > step ) ;
if ( ret )
return ret ;
/* reprogram PLL */
ret = clk_set_rate ( cpu - > pll , rate ) ;
if ( ret ) {
clk_set_parent ( cpu - > mux , cpu - > pll ) ;
return ret ;
}
/* switch back to PLL clock */
clk_set_parent ( cpu - > mux , cpu - > pll ) ;
/* Ensure the divider is what we expect */
clk_set_rate ( cpu - > div , rate ) ;
return 0 ;
}
static const struct clk_ops clk_cpu_ops = {
. recalc_rate = clk_cpu_recalc_rate ,
. round_rate = clk_cpu_round_rate ,
. set_rate = clk_cpu_set_rate ,
} ;
2019-05-29 12:26:42 +00:00
struct clk_hw * imx_clk_hw_cpu ( const char * name , const char * parent_name ,
2014-09-26 15:41:01 +02:00
struct clk * div , struct clk * mux , struct clk * pll ,
struct clk * step )
{
struct clk_cpu * cpu ;
2019-05-29 12:26:42 +00:00
struct clk_hw * hw ;
2014-09-26 15:41:01 +02:00
struct clk_init_data init ;
2019-05-29 12:26:42 +00:00
int ret ;
2014-09-26 15:41:01 +02:00
cpu = kzalloc ( sizeof ( * cpu ) , GFP_KERNEL ) ;
if ( ! cpu )
return ERR_PTR ( - ENOMEM ) ;
cpu - > div = div ;
cpu - > mux = mux ;
cpu - > pll = pll ;
cpu - > step = step ;
init . name = name ;
init . ops = & clk_cpu_ops ;
2018-10-17 06:11:59 +00:00
init . flags = CLK_IS_CRITICAL ;
2014-09-26 15:41:01 +02:00
init . parent_names = & parent_name ;
init . num_parents = 1 ;
cpu - > hw . init = & init ;
2019-05-29 12:26:42 +00:00
hw = & cpu - > hw ;
2014-09-26 15:41:01 +02:00
2019-05-29 12:26:42 +00:00
ret = clk_hw_register ( NULL , hw ) ;
if ( ret ) {
2014-09-26 15:41:01 +02:00
kfree ( cpu ) ;
2019-05-29 12:26:42 +00:00
return ERR_PTR ( ret ) ;
}
2014-09-26 15:41:01 +02:00
2019-05-29 12:26:42 +00:00
return hw ;
2014-09-26 15:41:01 +02:00
}
2020-07-30 09:22:51 +08:00
EXPORT_SYMBOL_GPL ( imx_clk_hw_cpu ) ;