2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-08-22 06:36:17 +03:00
/*
* Copyright ( c ) 2016 Rockchip Electronics Co . Ltd .
* Author : Lin Huang < hl @ rock - chips . com >
*/
# include <linux/arm-smccc.h>
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <soc/rockchip/rockchip_sip.h>
# include "clk.h"
struct rockchip_ddrclk {
struct clk_hw hw ;
void __iomem * reg_base ;
int mux_offset ;
int mux_shift ;
int mux_width ;
int div_shift ;
int div_width ;
int ddr_flag ;
spinlock_t * lock ;
} ;
# define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
static int rockchip_ddrclk_sip_set_rate ( struct clk_hw * hw , unsigned long drate ,
unsigned long prate )
{
struct rockchip_ddrclk * ddrclk = to_rockchip_ddrclk_hw ( hw ) ;
unsigned long flags ;
struct arm_smccc_res res ;
spin_lock_irqsave ( ddrclk - > lock , flags ) ;
arm_smccc_smc ( ROCKCHIP_SIP_DRAM_FREQ , drate , 0 ,
ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE ,
0 , 0 , 0 , 0 , & res ) ;
spin_unlock_irqrestore ( ddrclk - > lock , flags ) ;
return res . a0 ;
}
static unsigned long
rockchip_ddrclk_sip_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct arm_smccc_res res ;
arm_smccc_smc ( ROCKCHIP_SIP_DRAM_FREQ , 0 , 0 ,
ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE ,
0 , 0 , 0 , 0 , & res ) ;
return res . a0 ;
}
static long rockchip_ddrclk_sip_round_rate ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long * prate )
{
struct arm_smccc_res res ;
arm_smccc_smc ( ROCKCHIP_SIP_DRAM_FREQ , rate , 0 ,
ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE ,
0 , 0 , 0 , 0 , & res ) ;
return res . a0 ;
}
static u8 rockchip_ddrclk_get_parent ( struct clk_hw * hw )
{
struct rockchip_ddrclk * ddrclk = to_rockchip_ddrclk_hw ( hw ) ;
u32 val ;
2019-04-18 14:12:11 +03:00
val = readl ( ddrclk - > reg_base +
2016-08-22 06:36:17 +03:00
ddrclk - > mux_offset ) > > ddrclk - > mux_shift ;
val & = GENMASK ( ddrclk - > mux_width - 1 , 0 ) ;
return val ;
}
static const struct clk_ops rockchip_ddrclk_sip_ops = {
. recalc_rate = rockchip_ddrclk_sip_recalc_rate ,
. set_rate = rockchip_ddrclk_sip_set_rate ,
. round_rate = rockchip_ddrclk_sip_round_rate ,
. get_parent = rockchip_ddrclk_get_parent ,
} ;
struct clk * rockchip_clk_register_ddrclk ( const char * name , int flags ,
const char * const * parent_names ,
u8 num_parents , int mux_offset ,
int mux_shift , int mux_width ,
int div_shift , int div_width ,
int ddr_flag , void __iomem * reg_base ,
spinlock_t * lock )
{
struct rockchip_ddrclk * ddrclk ;
struct clk_init_data init ;
struct clk * clk ;
ddrclk = kzalloc ( sizeof ( * ddrclk ) , GFP_KERNEL ) ;
if ( ! ddrclk )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . parent_names = parent_names ;
init . num_parents = num_parents ;
init . flags = flags ;
init . flags | = CLK_SET_RATE_NO_REPARENT ;
switch ( ddr_flag ) {
case ROCKCHIP_DDRCLK_SIP :
init . ops = & rockchip_ddrclk_sip_ops ;
break ;
default :
pr_err ( " %s: unsupported ddrclk type %d \n " , __func__ , ddr_flag ) ;
kfree ( ddrclk ) ;
return ERR_PTR ( - EINVAL ) ;
}
ddrclk - > reg_base = reg_base ;
ddrclk - > lock = lock ;
ddrclk - > hw . init = & init ;
ddrclk - > mux_offset = mux_offset ;
ddrclk - > mux_shift = mux_shift ;
ddrclk - > mux_width = mux_width ;
ddrclk - > div_shift = div_shift ;
ddrclk - > div_width = div_width ;
ddrclk - > ddr_flag = ddr_flag ;
clk = clk_register ( NULL , & ddrclk - > hw ) ;
2016-10-12 15:31:41 +03:00
if ( IS_ERR ( clk ) )
2016-08-22 06:36:17 +03:00
kfree ( ddrclk ) ;
return clk ;
}
2020-09-14 05:22:21 +03:00
EXPORT_SYMBOL_GPL ( rockchip_clk_register_ddrclk ) ;