2023-04-01 19:19:16 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
2023-04-01 19:19:18 +08:00
* StarFive JH71X0 Clock Generator Driver
2023-04-01 19:19:16 +08:00
*
* Copyright ( C ) 2021 - 2022 Emil Renner Berthing < kernel @ esmil . dk >
*/
# include <linux/clk-provider.h>
# include <linux/debugfs.h>
# include <linux/device.h>
# include <linux/io.h>
2023-04-01 19:19:17 +08:00
# include "clk-starfive-jh71x0.h"
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
static struct jh71x0_clk * jh71x0_clk_from ( struct clk_hw * hw )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
return container_of ( hw , struct jh71x0_clk , hw ) ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
static struct jh71x0_clk_priv * jh71x0_priv_from ( struct jh71x0_clk * clk )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
return container_of ( clk , struct jh71x0_clk_priv , reg [ clk - > idx ] ) ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
static u32 jh71x0_clk_reg_get ( struct jh71x0_clk * clk )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk_priv * priv = jh71x0_priv_from ( clk ) ;
2023-04-01 19:19:16 +08:00
void __iomem * reg = priv - > base + 4 * clk - > idx ;
return readl_relaxed ( reg ) ;
}
2023-04-01 19:19:18 +08:00
static void jh71x0_clk_reg_rmw ( struct jh71x0_clk * clk , u32 mask , u32 value )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk_priv * priv = jh71x0_priv_from ( clk ) ;
2023-04-01 19:19:16 +08:00
void __iomem * reg = priv - > base + 4 * clk - > idx ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > rmw_lock , flags ) ;
value | = readl_relaxed ( reg ) & ~ mask ;
writel_relaxed ( value , reg ) ;
spin_unlock_irqrestore ( & priv - > rmw_lock , flags ) ;
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_enable ( struct clk_hw * hw )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
jh71x0_clk_reg_rmw ( clk , JH71X0_CLK_ENABLE , JH71X0_CLK_ENABLE ) ;
2023-04-01 19:19:16 +08:00
return 0 ;
}
2023-04-01 19:19:18 +08:00
static void jh71x0_clk_disable ( struct clk_hw * hw )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
jh71x0_clk_reg_rmw ( clk , JH71X0_CLK_ENABLE , 0 ) ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_is_enabled ( struct clk_hw * hw )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
return ! ! ( jh71x0_clk_reg_get ( clk ) & JH71X0_CLK_ENABLE ) ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
static unsigned long jh71x0_clk_recalc_rate ( struct clk_hw * hw ,
2023-04-01 19:19:16 +08:00
unsigned long parent_rate )
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
u32 div = jh71x0_clk_reg_get ( clk ) & JH71X0_CLK_DIV_MASK ;
2023-04-01 19:19:16 +08:00
return div ? parent_rate / div : 0 ;
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_determine_rate ( struct clk_hw * hw ,
2023-04-01 19:19:16 +08:00
struct clk_rate_request * req )
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
2023-04-01 19:19:16 +08:00
unsigned long parent = req - > best_parent_rate ;
unsigned long rate = clamp ( req - > rate , req - > min_rate , req - > max_rate ) ;
unsigned long div = min_t ( unsigned long , DIV_ROUND_UP ( parent , rate ) , clk - > max_div ) ;
unsigned long result = parent / div ;
/*
* we want the result clamped by min_rate and max_rate if possible :
* case 1 : div hits the max divider value , which means it ' s less than
* parent / rate , so the result is greater than rate and min_rate in
* particular . we can ' t do anything about result > max_rate because the
* divider doesn ' t go any further .
* case 2 : div = DIV_ROUND_UP ( parent , rate ) which means the result is
* always lower or equal to rate and max_rate . however the result may
* turn out lower than min_rate , but then the next higher rate is fine :
* div - 1 = ceil ( parent / rate ) - 1 < parent / rate
* and thus
* min_rate < = rate < parent / ( div - 1 )
*/
if ( result < req - > min_rate & & div > 1 )
result = parent / ( div - 1 ) ;
req - > rate = result ;
return 0 ;
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_set_rate ( struct clk_hw * hw ,
2023-04-01 19:19:16 +08:00
unsigned long rate ,
unsigned long parent_rate )
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
2023-04-01 19:19:16 +08:00
unsigned long div = clamp ( DIV_ROUND_CLOSEST ( parent_rate , rate ) ,
1UL , ( unsigned long ) clk - > max_div ) ;
2023-04-01 19:19:18 +08:00
jh71x0_clk_reg_rmw ( clk , JH71X0_CLK_DIV_MASK , div ) ;
2023-04-01 19:19:16 +08:00
return 0 ;
}
2023-04-01 19:19:18 +08:00
static unsigned long jh71x0_clk_frac_recalc_rate ( struct clk_hw * hw ,
2023-04-01 19:19:16 +08:00
unsigned long parent_rate )
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
u32 reg = jh71x0_clk_reg_get ( clk ) ;
unsigned long div100 = 100 * ( reg & JH71X0_CLK_INT_MASK ) +
( ( reg & JH71X0_CLK_FRAC_MASK ) > > JH71X0_CLK_FRAC_SHIFT ) ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
return ( div100 > = JH71X0_CLK_FRAC_MIN ) ? 100 * parent_rate / div100 : 0 ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_frac_determine_rate ( struct clk_hw * hw ,
2023-04-01 19:19:16 +08:00
struct clk_rate_request * req )
{
unsigned long parent100 = 100 * req - > best_parent_rate ;
unsigned long rate = clamp ( req - > rate , req - > min_rate , req - > max_rate ) ;
unsigned long div100 = clamp ( DIV_ROUND_CLOSEST ( parent100 , rate ) ,
2023-04-01 19:19:18 +08:00
JH71X0_CLK_FRAC_MIN , JH71X0_CLK_FRAC_MAX ) ;
2023-04-01 19:19:16 +08:00
unsigned long result = parent100 / div100 ;
2023-04-01 19:19:18 +08:00
/* clamp the result as in jh71x0_clk_determine_rate() above */
if ( result > req - > max_rate & & div100 < JH71X0_CLK_FRAC_MAX )
2023-04-01 19:19:16 +08:00
result = parent100 / ( div100 + 1 ) ;
2023-04-01 19:19:18 +08:00
if ( result < req - > min_rate & & div100 > JH71X0_CLK_FRAC_MIN )
2023-04-01 19:19:16 +08:00
result = parent100 / ( div100 - 1 ) ;
req - > rate = result ;
return 0 ;
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_frac_set_rate ( struct clk_hw * hw ,
2023-04-01 19:19:16 +08:00
unsigned long rate ,
unsigned long parent_rate )
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
2023-04-01 19:19:16 +08:00
unsigned long div100 = clamp ( DIV_ROUND_CLOSEST ( 100 * parent_rate , rate ) ,
2023-04-01 19:19:18 +08:00
JH71X0_CLK_FRAC_MIN , JH71X0_CLK_FRAC_MAX ) ;
u32 value = ( ( div100 % 100 ) < < JH71X0_CLK_FRAC_SHIFT ) | ( div100 / 100 ) ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
jh71x0_clk_reg_rmw ( clk , JH71X0_CLK_DIV_MASK , value ) ;
2023-04-01 19:19:16 +08:00
return 0 ;
}
2023-04-01 19:19:18 +08:00
static u8 jh71x0_clk_get_parent ( struct clk_hw * hw )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
u32 value = jh71x0_clk_reg_get ( clk ) ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
return ( value & JH71X0_CLK_MUX_MASK ) > > JH71X0_CLK_MUX_SHIFT ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_set_parent ( struct clk_hw * hw , u8 index )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
u32 value = ( u32 ) index < < JH71X0_CLK_MUX_SHIFT ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
jh71x0_clk_reg_rmw ( clk , JH71X0_CLK_MUX_MASK , value ) ;
2023-04-01 19:19:16 +08:00
return 0 ;
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_get_phase ( struct clk_hw * hw )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
u32 value = jh71x0_clk_reg_get ( clk ) ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
return ( value & JH71X0_CLK_INVERT ) ? 180 : 0 ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
static int jh71x0_clk_set_phase ( struct clk_hw * hw , int degrees )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
2023-04-01 19:19:16 +08:00
u32 value ;
if ( degrees = = 0 )
value = 0 ;
else if ( degrees = = 180 )
2023-04-01 19:19:18 +08:00
value = JH71X0_CLK_INVERT ;
2023-04-01 19:19:16 +08:00
else
return - EINVAL ;
2023-04-01 19:19:18 +08:00
jh71x0_clk_reg_rmw ( clk , JH71X0_CLK_INVERT , value ) ;
2023-04-01 19:19:16 +08:00
return 0 ;
}
# ifdef CONFIG_DEBUG_FS
2023-04-01 19:19:18 +08:00
static void jh71x0_clk_debug_init ( struct clk_hw * hw , struct dentry * dentry )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
static const struct debugfs_reg32 jh71x0_clk_reg = {
2023-04-01 19:19:16 +08:00
. name = " CTRL " ,
. offset = 0 ,
} ;
2023-04-01 19:19:18 +08:00
struct jh71x0_clk * clk = jh71x0_clk_from ( hw ) ;
struct jh71x0_clk_priv * priv = jh71x0_priv_from ( clk ) ;
2023-04-01 19:19:16 +08:00
struct debugfs_regset32 * regset ;
regset = devm_kzalloc ( priv - > dev , sizeof ( * regset ) , GFP_KERNEL ) ;
if ( ! regset )
return ;
2023-04-01 19:19:18 +08:00
regset - > regs = & jh71x0_clk_reg ;
2023-04-01 19:19:16 +08:00
regset - > nregs = 1 ;
regset - > base = priv - > base + 4 * clk - > idx ;
debugfs_create_regset32 ( " registers " , 0400 , dentry , regset ) ;
}
# else
2023-04-01 19:19:18 +08:00
# define jh71x0_clk_debug_init NULL
2023-04-01 19:19:16 +08:00
# endif
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_gate_ops = {
. enable = jh71x0_clk_enable ,
. disable = jh71x0_clk_disable ,
. is_enabled = jh71x0_clk_is_enabled ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_div_ops = {
. recalc_rate = jh71x0_clk_recalc_rate ,
. determine_rate = jh71x0_clk_determine_rate ,
. set_rate = jh71x0_clk_set_rate ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_fdiv_ops = {
. recalc_rate = jh71x0_clk_frac_recalc_rate ,
. determine_rate = jh71x0_clk_frac_determine_rate ,
. set_rate = jh71x0_clk_frac_set_rate ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_gdiv_ops = {
. enable = jh71x0_clk_enable ,
. disable = jh71x0_clk_disable ,
. is_enabled = jh71x0_clk_is_enabled ,
. recalc_rate = jh71x0_clk_recalc_rate ,
. determine_rate = jh71x0_clk_determine_rate ,
. set_rate = jh71x0_clk_set_rate ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_mux_ops = {
2023-07-07 22:02:18 +02:00
. determine_rate = __clk_mux_determine_rate ,
2023-04-01 19:19:18 +08:00
. set_parent = jh71x0_clk_set_parent ,
. get_parent = jh71x0_clk_get_parent ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_gmux_ops = {
. enable = jh71x0_clk_enable ,
. disable = jh71x0_clk_disable ,
. is_enabled = jh71x0_clk_is_enabled ,
2023-07-07 22:02:18 +02:00
. determine_rate = __clk_mux_determine_rate ,
2023-04-01 19:19:18 +08:00
. set_parent = jh71x0_clk_set_parent ,
. get_parent = jh71x0_clk_get_parent ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_mdiv_ops = {
. recalc_rate = jh71x0_clk_recalc_rate ,
. determine_rate = jh71x0_clk_determine_rate ,
. get_parent = jh71x0_clk_get_parent ,
. set_parent = jh71x0_clk_set_parent ,
. set_rate = jh71x0_clk_set_rate ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_gmd_ops = {
. enable = jh71x0_clk_enable ,
. disable = jh71x0_clk_disable ,
. is_enabled = jh71x0_clk_is_enabled ,
. recalc_rate = jh71x0_clk_recalc_rate ,
. determine_rate = jh71x0_clk_determine_rate ,
. get_parent = jh71x0_clk_get_parent ,
. set_parent = jh71x0_clk_set_parent ,
. set_rate = jh71x0_clk_set_rate ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
static const struct clk_ops jh71x0_clk_inv_ops = {
. get_phase = jh71x0_clk_get_phase ,
. set_phase = jh71x0_clk_set_phase ,
. debug_init = jh71x0_clk_debug_init ,
2023-04-01 19:19:16 +08:00
} ;
2023-04-01 19:19:18 +08:00
const struct clk_ops * starfive_jh71x0_clk_ops ( u32 max )
2023-04-01 19:19:16 +08:00
{
2023-04-01 19:19:18 +08:00
if ( max & JH71X0_CLK_DIV_MASK ) {
if ( max & JH71X0_CLK_MUX_MASK ) {
if ( max & JH71X0_CLK_ENABLE )
return & jh71x0_clk_gmd_ops ;
return & jh71x0_clk_mdiv_ops ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
if ( max & JH71X0_CLK_ENABLE )
return & jh71x0_clk_gdiv_ops ;
if ( max = = JH71X0_CLK_FRAC_MAX )
return & jh71x0_clk_fdiv_ops ;
return & jh71x0_clk_div_ops ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
if ( max & JH71X0_CLK_MUX_MASK ) {
if ( max & JH71X0_CLK_ENABLE )
return & jh71x0_clk_gmux_ops ;
return & jh71x0_clk_mux_ops ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
if ( max & JH71X0_CLK_ENABLE )
return & jh71x0_clk_gate_ops ;
2023-04-01 19:19:16 +08:00
2023-04-01 19:19:18 +08:00
return & jh71x0_clk_inv_ops ;
2023-04-01 19:19:16 +08:00
}
2023-04-01 19:19:18 +08:00
EXPORT_SYMBOL_GPL ( starfive_jh71x0_clk_ops ) ;