2018-05-16 11:50:40 +03:00
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2016-06-07 09:16:17 +03:00
/*
* Copyright ( c ) 2016 AmLogic , Inc .
* Author : Michael Turquette < mturquette @ baylibre . com >
*/
/*
* MultiPhase Locked Loops are outputs from a PLL with additional frequency
* scaling capabilities . MPLL rates are calculated as :
*
* f ( N2_integer , SDM_IN ) = 2.0 G / ( N2_integer + SDM_IN / 16384 )
*/
# include <linux/clk-provider.h>
# include "clkc.h"
2017-03-09 13:41:50 +03:00
# define SDM_DEN 16384
# define N2_MIN 4
2017-03-09 13:41:53 +03:00
# define N2_MAX 511
2016-06-07 09:16:17 +03:00
2018-02-12 17:58:40 +03:00
static inline struct meson_clk_mpll_data *
meson_clk_mpll_data ( struct clk_regmap * clk )
{
return ( struct meson_clk_mpll_data * ) clk - > data ;
}
2016-06-07 09:16:17 +03:00
2017-04-01 16:02:24 +03:00
static long rate_from_params ( unsigned long parent_rate ,
2018-02-12 17:58:40 +03:00
unsigned int sdm ,
unsigned int n2 )
2017-03-09 13:41:50 +03:00
{
2017-04-01 16:02:24 +03:00
unsigned long divisor = ( SDM_DEN * n2 ) + sdm ;
if ( n2 < N2_MIN )
return - EINVAL ;
2017-04-01 16:02:25 +03:00
return DIV_ROUND_UP_ULL ( ( u64 ) parent_rate * SDM_DEN , divisor ) ;
2017-03-09 13:41:50 +03:00
}
static void params_from_rate ( unsigned long requested_rate ,
unsigned long parent_rate ,
2018-02-12 17:58:40 +03:00
unsigned int * sdm ,
2018-05-15 19:36:51 +03:00
unsigned int * n2 ,
u8 flags )
2017-03-09 13:41:50 +03:00
{
uint64_t div = parent_rate ;
2018-05-15 19:36:51 +03:00
uint64_t frac = do_div ( div , requested_rate ) ;
frac * = SDM_DEN ;
if ( flags & CLK_MESON_MPLL_ROUND_CLOSEST )
* sdm = DIV_ROUND_CLOSEST_ULL ( frac , requested_rate ) ;
else
* sdm = DIV_ROUND_UP_ULL ( frac , requested_rate ) ;
if ( * sdm = = SDM_DEN ) {
* sdm = 0 ;
div + = 1 ;
}
2017-03-09 13:41:50 +03:00
if ( div < N2_MIN ) {
* n2 = N2_MIN ;
2017-04-01 16:02:24 +03:00
* sdm = 0 ;
2017-03-09 13:41:50 +03:00
} else if ( div > N2_MAX ) {
* n2 = N2_MAX ;
2017-04-01 16:02:24 +03:00
* sdm = SDM_DEN - 1 ;
2017-03-09 13:41:50 +03:00
} else {
* n2 = div ;
}
}
2016-06-07 09:16:17 +03:00
static unsigned long mpll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2018-02-12 17:58:40 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_mpll_data * mpll = meson_clk_mpll_data ( clk ) ;
unsigned int sdm , n2 ;
2017-04-01 16:02:24 +03:00
long rate ;
2016-06-07 09:16:17 +03:00
2018-02-12 17:58:40 +03:00
sdm = meson_parm_read ( clk - > map , & mpll - > sdm ) ;
n2 = meson_parm_read ( clk - > map , & mpll - > n2 ) ;
2016-06-07 09:16:17 +03:00
2017-04-01 16:02:24 +03:00
rate = rate_from_params ( parent_rate , sdm , n2 ) ;
2018-02-12 17:58:40 +03:00
return rate < 0 ? 0 : rate ;
2017-03-09 13:41:50 +03:00
}
static long mpll_round_rate ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long * parent_rate )
{
2018-05-15 19:36:51 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_mpll_data * mpll = meson_clk_mpll_data ( clk ) ;
2018-02-12 17:58:40 +03:00
unsigned int sdm , n2 ;
2017-03-09 13:41:50 +03:00
2018-05-15 19:36:51 +03:00
params_from_rate ( rate , * parent_rate , & sdm , & n2 , mpll - > flags ) ;
2017-03-09 13:41:50 +03:00
return rate_from_params ( * parent_rate , sdm , n2 ) ;
}
static int mpll_set_rate ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long parent_rate )
{
2018-02-12 17:58:40 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_mpll_data * mpll = meson_clk_mpll_data ( clk ) ;
unsigned int sdm , n2 ;
2017-03-09 13:41:50 +03:00
unsigned long flags = 0 ;
2018-05-15 19:36:51 +03:00
params_from_rate ( rate , parent_rate , & sdm , & n2 , mpll - > flags ) ;
2017-03-09 13:41:50 +03:00
if ( mpll - > lock )
spin_lock_irqsave ( mpll - > lock , flags ) ;
else
__acquire ( mpll - > lock ) ;
2018-02-12 17:58:40 +03:00
/* Enable and set the fractional part */
meson_parm_write ( clk - > map , & mpll - > sdm , sdm ) ;
meson_parm_write ( clk - > map , & mpll - > sdm_en , 1 ) ;
2017-07-28 19:32:28 +03:00
2018-02-12 17:58:40 +03:00
/* Set additional fractional part enable if required */
if ( MESON_PARM_APPLICABLE ( & mpll - > ssen ) )
meson_parm_write ( clk - > map , & mpll - > ssen , 1 ) ;
2017-03-09 13:41:50 +03:00
2018-02-12 17:58:40 +03:00
/* Set the integer divider part */
meson_parm_write ( clk - > map , & mpll - > n2 , n2 ) ;
/* Set the magic misc bit if required */
if ( MESON_PARM_APPLICABLE ( & mpll - > misc ) )
meson_parm_write ( clk - > map , & mpll - > misc , 1 ) ;
2018-01-19 18:42:36 +03:00
2017-03-09 13:41:50 +03:00
if ( mpll - > lock )
spin_unlock_irqrestore ( mpll - > lock , flags ) ;
else
__release ( mpll - > lock ) ;
2016-06-07 09:16:17 +03:00
2017-03-09 13:41:50 +03:00
return 0 ;
}
2016-06-07 09:16:17 +03:00
const struct clk_ops meson_clk_mpll_ro_ops = {
2017-03-09 13:41:50 +03:00
. recalc_rate = mpll_recalc_rate ,
. round_rate = mpll_round_rate ,
} ;
const struct clk_ops meson_clk_mpll_ops = {
. recalc_rate = mpll_recalc_rate ,
. round_rate = mpll_round_rate ,
. set_rate = mpll_set_rate ,
2016-06-07 09:16:17 +03:00
} ;