2018-05-16 11:50:40 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-06-01 14:13:53 +03:00
/*
* Copyright ( c ) 2015 Endless Mobile , Inc .
* Author : Carlo Caione < carlo @ endlessm . com >
*
2018-02-19 14:21:39 +03:00
* Copyright ( c ) 2018 Baylibre , SAS .
* Author : Jerome Brunet < jbrunet @ baylibre . com >
2015-06-01 14:13:53 +03:00
*/
/*
* In the most basic form , a Meson PLL is composed as follows :
*
* PLL
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | |
* in - - - - - [ / N ] - - - [ * M ] - - - [ > > OD ] - - - - - > > out
* | ^ ^ |
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | |
* FREF VCO
*
2018-02-19 14:21:39 +03:00
* out = in * ( m + frac / frac_max ) / ( n < < sum ( ods ) )
2015-06-01 14:13:53 +03:00
*/
# include <linux/clk-provider.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
2018-01-19 18:55:23 +03:00
# include <linux/math64.h>
2015-06-01 14:13:53 +03:00
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/slab.h>
# include <linux/string.h>
# include "clkc.h"
2018-02-12 17:58:42 +03:00
static inline struct meson_clk_pll_data *
meson_clk_pll_data ( struct clk_regmap * clk )
{
return ( struct meson_clk_pll_data * ) clk - > data ;
}
2015-06-01 14:13:53 +03:00
2018-02-19 14:21:39 +03:00
static unsigned long __pll_params_to_rate ( unsigned long parent_rate ,
const struct pll_rate_table * pllt ,
u16 frac ,
struct meson_clk_pll_data * pll )
{
u64 rate = ( u64 ) parent_rate * pllt - > m ;
unsigned int od = pllt - > od + pllt - > od2 + pllt - > od3 ;
if ( frac & & MESON_PARM_APPLICABLE ( & pll - > frac ) ) {
u64 frac_rate = ( u64 ) parent_rate * frac ;
rate + = DIV_ROUND_UP_ULL ( frac_rate ,
( 1 < < pll - > frac . width ) ) ;
}
return DIV_ROUND_UP_ULL ( rate , pllt - > n < < od ) ;
}
2015-06-01 14:13:53 +03:00
static unsigned long meson_clk_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
2018-02-19 14:21:39 +03:00
struct pll_rate_table pllt ;
u16 frac ;
2015-06-01 14:13:53 +03:00
2018-02-19 14:21:39 +03:00
pllt . n = meson_parm_read ( clk - > map , & pll - > n ) ;
pllt . m = meson_parm_read ( clk - > map , & pll - > m ) ;
pllt . od = meson_parm_read ( clk - > map , & pll - > od ) ;
2018-01-19 18:55:25 +03:00
2018-02-19 14:21:39 +03:00
pllt . od2 = MESON_PARM_APPLICABLE ( & pll - > od2 ) ?
meson_parm_read ( clk - > map , & pll - > od2 ) :
0 ;
2018-01-19 18:55:23 +03:00
2018-02-19 14:21:39 +03:00
pllt . od3 = MESON_PARM_APPLICABLE ( & pll - > od3 ) ?
meson_parm_read ( clk - > map , & pll - > od3 ) :
0 ;
2015-06-01 14:13:53 +03:00
2018-02-19 14:21:39 +03:00
frac = MESON_PARM_APPLICABLE ( & pll - > frac ) ?
meson_parm_read ( clk - > map , & pll - > frac ) :
0 ;
2018-01-19 18:55:23 +03:00
2018-02-19 14:21:39 +03:00
return __pll_params_to_rate ( parent_rate , & pllt , frac , pll ) ;
2015-06-01 14:13:53 +03:00
}
2018-02-19 14:21:39 +03:00
static u16 __pll_params_with_frac ( unsigned long rate ,
unsigned long parent_rate ,
const struct pll_rate_table * pllt ,
struct meson_clk_pll_data * pll )
2015-06-01 14:13:53 +03:00
{
2018-02-19 14:21:39 +03:00
u16 frac_max = ( 1 < < pll - > frac . width ) ;
u64 val = ( u64 ) rate * pllt - > n ;
2018-01-19 18:55:21 +03:00
2018-02-19 14:21:39 +03:00
val < < = pllt - > od + pllt - > od2 + pllt - > od3 ;
2018-02-19 14:21:41 +03:00
if ( pll - > flags & CLK_MESON_PLL_ROUND_CLOSEST )
val = DIV_ROUND_CLOSEST_ULL ( val * frac_max , parent_rate ) ;
else
val = div_u64 ( val * frac_max , parent_rate ) ;
2018-02-19 14:21:39 +03:00
val - = pllt - > m * frac_max ;
2015-06-01 14:13:53 +03:00
2018-02-19 14:21:39 +03:00
return min ( ( u16 ) val , ( u16 ) ( frac_max - 1 ) ) ;
2015-06-01 14:13:53 +03:00
}
2018-02-12 17:58:42 +03:00
static const struct pll_rate_table *
2018-02-19 14:21:39 +03:00
meson_clk_get_pll_settings ( unsigned long rate ,
struct meson_clk_pll_data * pll )
2015-06-01 14:13:53 +03:00
{
2018-02-19 14:21:39 +03:00
const struct pll_rate_table * table = pll - > table ;
unsigned int i = 0 ;
2015-06-01 14:13:53 +03:00
2018-02-12 17:58:42 +03:00
if ( ! table )
2018-01-19 18:55:21 +03:00
return NULL ;
2018-02-19 14:21:39 +03:00
/* Find the first table element exceeding rate */
while ( table [ i ] . rate & & table [ i ] . rate < = rate )
i + + ;
2018-02-19 14:21:41 +03:00
if ( i ! = 0 ) {
if ( MESON_PARM_APPLICABLE ( & pll - > frac ) | |
! ( pll - > flags & CLK_MESON_PLL_ROUND_CLOSEST ) | |
( abs ( rate - table [ i - 1 ] . rate ) <
abs ( rate - table [ i ] . rate ) ) )
i - - ;
}
2018-02-19 14:21:39 +03:00
return ( struct pll_rate_table * ) & table [ i ] ;
}
static long meson_clk_pll_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
const struct pll_rate_table * pllt =
meson_clk_get_pll_settings ( rate , pll ) ;
u16 frac ;
if ( ! pllt )
return meson_clk_pll_recalc_rate ( hw , * parent_rate ) ;
if ( ! MESON_PARM_APPLICABLE ( & pll - > frac )
| | rate = = pllt - > rate )
return pllt - > rate ;
2018-02-12 17:58:42 +03:00
2018-02-19 14:21:39 +03:00
/*
* The rate provided by the setting is not an exact match , let ' s
* try to improve the result using the fractional parameter
*/
frac = __pll_params_with_frac ( rate , * parent_rate , pllt , pll ) ;
return __pll_params_to_rate ( * parent_rate , pllt , frac , pll ) ;
2015-06-01 14:13:53 +03:00
}
2018-02-12 17:58:42 +03:00
static int meson_clk_pll_wait_lock ( struct clk_hw * hw )
2017-03-22 13:32:23 +03:00
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
2018-02-19 14:21:38 +03:00
int delay = 24000000 ;
2018-02-12 17:58:42 +03:00
do {
/* Is the clock locked now ? */
if ( meson_parm_read ( clk - > map , & pll - > l ) )
2017-03-22 13:32:23 +03:00
return 0 ;
2015-06-01 14:13:53 +03:00
delay - - ;
2018-02-12 17:58:42 +03:00
} while ( delay > 0 ) ;
2015-06-01 14:13:53 +03:00
return - ETIMEDOUT ;
}
2018-02-12 17:58:42 +03:00
static void meson_clk_pll_init ( struct clk_hw * hw )
2017-03-22 13:32:23 +03:00
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
if ( pll - > init_count ) {
meson_parm_write ( clk - > map , & pll - > rst , 1 ) ;
regmap_multi_reg_write ( clk - > map , pll - > init_regs ,
pll - > init_count ) ;
meson_parm_write ( clk - > map , & pll - > rst , 0 ) ;
}
2017-03-22 13:32:23 +03:00
}
2015-06-01 14:13:53 +03:00
static int meson_clk_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
const struct pll_rate_table * pllt ;
2015-06-01 14:13:53 +03:00
unsigned long old_rate ;
2018-02-19 14:21:39 +03:00
u16 frac = 0 ;
2015-06-01 14:13:53 +03:00
if ( parent_rate = = 0 | | rate = = 0 )
return - EINVAL ;
old_rate = rate ;
2018-02-19 14:21:39 +03:00
pllt = meson_clk_get_pll_settings ( rate , pll ) ;
2018-02-12 17:58:42 +03:00
if ( ! pllt )
2015-06-01 14:13:53 +03:00
return - EINVAL ;
2018-02-12 17:58:42 +03:00
/* Put the pll in reset to write the params */
meson_parm_write ( clk - > map , & pll - > rst , 1 ) ;
2016-06-07 04:08:15 +03:00
2018-02-12 17:58:42 +03:00
meson_parm_write ( clk - > map , & pll - > n , pllt - > n ) ;
meson_parm_write ( clk - > map , & pll - > m , pllt - > m ) ;
meson_parm_write ( clk - > map , & pll - > od , pllt - > od ) ;
2018-01-19 18:55:25 +03:00
2018-02-12 17:58:42 +03:00
if ( MESON_PARM_APPLICABLE ( & pll - > od2 ) )
meson_parm_write ( clk - > map , & pll - > od2 , pllt - > od2 ) ;
2016-06-07 04:08:15 +03:00
2018-02-12 17:58:42 +03:00
if ( MESON_PARM_APPLICABLE ( & pll - > od3 ) )
meson_parm_write ( clk - > map , & pll - > od3 , pllt - > od3 ) ;
2018-02-19 14:21:39 +03:00
if ( MESON_PARM_APPLICABLE ( & pll - > frac ) ) {
frac = __pll_params_with_frac ( rate , parent_rate , pllt , pll ) ;
meson_parm_write ( clk - > map , & pll - > frac , frac ) ;
}
2018-02-12 17:58:42 +03:00
/* make sure the reset is cleared at this point */
meson_parm_write ( clk - > map , & pll - > rst , 0 ) ;
2017-03-22 13:32:23 +03:00
2018-02-12 17:58:42 +03:00
if ( meson_clk_pll_wait_lock ( hw ) ) {
2015-06-01 14:13:53 +03:00
pr_warn ( " %s: pll did not lock, trying to restore old rate %lu \n " ,
__func__ , old_rate ) ;
2018-02-12 17:58:42 +03:00
/*
* FIXME : Do we really need / want this HACK ?
* It looks unsafe . what happens if the clock gets into a
* broken state and we can ' t lock back on the old_rate ? Looks
* like an infinite recursion is possible
*/
2015-06-01 14:13:53 +03:00
meson_clk_pll_set_rate ( hw , old_rate , parent_rate ) ;
}
2018-02-12 17:58:42 +03:00
return 0 ;
2015-06-01 14:13:53 +03:00
}
2016-04-28 22:01:42 +03:00
const struct clk_ops meson_clk_pll_ops = {
2018-02-12 17:58:42 +03:00
. init = meson_clk_pll_init ,
2015-06-01 14:13:53 +03:00
. recalc_rate = meson_clk_pll_recalc_rate ,
. round_rate = meson_clk_pll_round_rate ,
. set_rate = meson_clk_pll_set_rate ,
} ;
2016-04-28 22:01:42 +03:00
const struct clk_ops meson_clk_pll_ro_ops = {
2015-06-01 14:13:53 +03:00
. recalc_rate = meson_clk_pll_recalc_rate ,
} ;