2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-06-29 22:05:32 +03:00
/*
* Copyright ( C ) 2016 Maxime Ripard
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*/
# include <linux/clk-provider.h>
2019-04-19 01:20:22 +03:00
# include <linux/io.h>
2016-06-29 22:05:32 +03:00
# include "ccu_gate.h"
# include "ccu_nkm.h"
struct _ccu_nkm {
2016-09-29 23:57:26 +03:00
unsigned long n , min_n , max_n ;
unsigned long k , min_k , max_k ;
unsigned long m , min_m , max_m ;
2016-06-29 22:05:32 +03:00
} ;
2024-03-10 16:21:13 +03:00
static bool ccu_nkm_is_valid_rate ( struct ccu_common * common , unsigned long parent ,
unsigned long n , unsigned long m )
{
struct ccu_nkm * nkm = container_of ( common , struct ccu_nkm , common ) ;
if ( nkm - > max_m_n_ratio & & ( m > nkm - > max_m_n_ratio * n ) )
return false ;
if ( nkm - > min_parent_m_ratio & & ( parent < nkm - > min_parent_m_ratio * m ) )
return false ;
return true ;
}
2023-08-07 15:43:40 +03:00
static unsigned long ccu_nkm_find_best_with_parent_adj ( struct ccu_common * common ,
struct clk_hw * parent_hw ,
2023-08-07 15:43:35 +03:00
unsigned long * parent , unsigned long rate ,
struct _ccu_nkm * nkm )
{
2023-10-23 16:35:02 +03:00
unsigned long best_rate = 0 , best_parent_rate = * parent ;
2023-08-07 15:43:35 +03:00
unsigned long best_n = 0 , best_k = 0 , best_m = 0 ;
unsigned long _n , _k , _m ;
for ( _k = nkm - > min_k ; _k < = nkm - > max_k ; _k + + ) {
for ( _n = nkm - > min_n ; _n < = nkm - > max_n ; _n + + ) {
for ( _m = nkm - > min_m ; _m < = nkm - > max_m ; _m + + ) {
2023-10-23 16:35:02 +03:00
unsigned long tmp_rate , tmp_parent ;
2023-08-07 15:43:35 +03:00
tmp_parent = clk_hw_round_rate ( parent_hw , rate * _m / ( _n * _k ) ) ;
2024-03-10 16:21:13 +03:00
if ( ! ccu_nkm_is_valid_rate ( common , tmp_parent , _n , _m ) )
continue ;
2023-08-07 15:43:35 +03:00
tmp_rate = tmp_parent * _n * _k / _m ;
2023-08-07 15:43:44 +03:00
if ( ccu_is_better_rate ( common , rate , tmp_rate , best_rate ) | |
( tmp_parent = = * parent & & tmp_rate = = best_rate ) ) {
2023-08-07 15:43:35 +03:00
best_rate = tmp_rate ;
best_parent_rate = tmp_parent ;
best_n = _n ;
best_k = _k ;
best_m = _m ;
}
}
}
}
nkm - > n = best_n ;
nkm - > k = best_k ;
nkm - > m = best_m ;
* parent = best_parent_rate ;
return best_rate ;
}
2022-12-31 20:30:55 +03:00
static unsigned long ccu_nkm_find_best ( unsigned long parent , unsigned long rate ,
2023-08-07 15:43:40 +03:00
struct _ccu_nkm * nkm , struct ccu_common * common )
2016-06-29 22:05:32 +03:00
{
unsigned long best_rate = 0 ;
unsigned long best_n = 0 , best_k = 0 , best_m = 0 ;
unsigned long _n , _k , _m ;
2016-09-29 23:57:26 +03:00
for ( _k = nkm - > min_k ; _k < = nkm - > max_k ; _k + + ) {
for ( _n = nkm - > min_n ; _n < = nkm - > max_n ; _n + + ) {
for ( _m = nkm - > min_m ; _m < = nkm - > max_m ; _m + + ) {
2024-03-10 16:21:13 +03:00
if ( ! ccu_nkm_is_valid_rate ( common , parent , _n , _m ) )
continue ;
2016-09-29 23:53:12 +03:00
unsigned long tmp_rate ;
tmp_rate = parent * _n * _k / _m ;
2023-08-07 15:43:40 +03:00
if ( ccu_is_better_rate ( common , rate , tmp_rate , best_rate ) ) {
2016-09-29 23:53:12 +03:00
best_rate = tmp_rate ;
best_n = _n ;
best_k = _k ;
best_m = _m ;
}
}
2016-06-29 22:05:32 +03:00
}
}
nkm - > n = best_n ;
nkm - > k = best_k ;
nkm - > m = best_m ;
2022-12-31 20:30:55 +03:00
return best_rate ;
2016-06-29 22:05:32 +03:00
}
static void ccu_nkm_disable ( struct clk_hw * hw )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
return ccu_gate_helper_disable ( & nkm - > common , nkm - > enable ) ;
}
static int ccu_nkm_enable ( struct clk_hw * hw )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
return ccu_gate_helper_enable ( & nkm - > common , nkm - > enable ) ;
}
static int ccu_nkm_is_enabled ( struct clk_hw * hw )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
return ccu_gate_helper_is_enabled ( & nkm - > common , nkm - > enable ) ;
}
static unsigned long ccu_nkm_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
2017-08-12 15:43:51 +03:00
unsigned long n , m , k , rate ;
2016-06-29 22:05:32 +03:00
u32 reg ;
reg = readl ( nkm - > common . base + nkm - > common . reg ) ;
n = reg > > nkm - > n . shift ;
n & = ( 1 < < nkm - > n . width ) - 1 ;
2016-11-08 20:12:34 +03:00
n + = nkm - > n . offset ;
if ( ! n )
n + + ;
2016-06-29 22:05:32 +03:00
k = reg > > nkm - > k . shift ;
k & = ( 1 < < nkm - > k . width ) - 1 ;
2016-11-08 20:12:34 +03:00
k + = nkm - > k . offset ;
if ( ! k )
k + + ;
2016-06-29 22:05:32 +03:00
m = reg > > nkm - > m . shift ;
m & = ( 1 < < nkm - > m . width ) - 1 ;
2016-11-08 20:12:34 +03:00
m + = nkm - > m . offset ;
if ( ! m )
m + + ;
2016-06-29 22:05:32 +03:00
2017-08-12 15:43:51 +03:00
rate = parent_rate * n * k / m ;
if ( nkm - > common . features & CCU_FEATURE_FIXED_POSTDIV )
rate / = nkm - > fixed_post_div ;
return rate ;
2016-06-29 22:05:32 +03:00
}
2016-07-26 10:04:28 +03:00
static unsigned long ccu_nkm_round_rate ( struct ccu_mux_internal * mux ,
2023-08-07 15:43:34 +03:00
struct clk_hw * parent_hw ,
2017-05-17 10:40:31 +03:00
unsigned long * parent_rate ,
2016-07-26 10:04:28 +03:00
unsigned long rate ,
void * data )
2016-06-29 22:05:32 +03:00
{
2016-07-26 10:04:28 +03:00
struct ccu_nkm * nkm = data ;
2016-06-29 22:05:32 +03:00
struct _ccu_nkm _nkm ;
2017-03-24 11:33:05 +03:00
_nkm . min_n = nkm - > n . min ? : 1 ;
2016-10-14 13:08:19 +03:00
_nkm . max_n = nkm - > n . max ? : 1 < < nkm - > n . width ;
2017-03-24 11:33:05 +03:00
_nkm . min_k = nkm - > k . min ? : 1 ;
2016-10-14 13:08:19 +03:00
_nkm . max_k = nkm - > k . max ? : 1 < < nkm - > k . width ;
2016-09-29 23:57:26 +03:00
_nkm . min_m = 1 ;
2016-09-06 13:29:04 +03:00
_nkm . max_m = nkm - > m . max ? : 1 < < nkm - > m . width ;
2016-06-29 22:05:32 +03:00
2017-08-12 15:43:51 +03:00
if ( nkm - > common . features & CCU_FEATURE_FIXED_POSTDIV )
rate * = nkm - > fixed_post_div ;
2023-08-07 15:43:35 +03:00
if ( ! clk_hw_can_set_rate_parent ( & nkm - > common . hw ) )
2023-08-07 15:43:40 +03:00
rate = ccu_nkm_find_best ( * parent_rate , rate , & _nkm , & nkm - > common ) ;
2023-08-07 15:43:35 +03:00
else
2023-08-07 15:43:40 +03:00
rate = ccu_nkm_find_best_with_parent_adj ( & nkm - > common , parent_hw , parent_rate , rate ,
& _nkm ) ;
2017-08-12 15:43:51 +03:00
if ( nkm - > common . features & CCU_FEATURE_FIXED_POSTDIV )
rate / = nkm - > fixed_post_div ;
return rate ;
2016-07-26 10:04:28 +03:00
}
static int ccu_nkm_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
return ccu_mux_helper_determine_rate ( & nkm - > common , & nkm - > mux ,
req , ccu_nkm_round_rate , nkm ) ;
2016-06-29 22:05:32 +03:00
}
static int ccu_nkm_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
struct _ccu_nkm _nkm ;
unsigned long flags ;
u32 reg ;
2017-08-12 15:43:51 +03:00
if ( nkm - > common . features & CCU_FEATURE_FIXED_POSTDIV )
rate * = nkm - > fixed_post_div ;
2017-03-24 11:33:05 +03:00
_nkm . min_n = nkm - > n . min ? : 1 ;
2016-10-14 13:08:19 +03:00
_nkm . max_n = nkm - > n . max ? : 1 < < nkm - > n . width ;
2017-03-24 11:33:05 +03:00
_nkm . min_k = nkm - > k . min ? : 1 ;
2016-10-14 13:08:19 +03:00
_nkm . max_k = nkm - > k . max ? : 1 < < nkm - > k . width ;
2016-09-29 23:57:26 +03:00
_nkm . min_m = 1 ;
2016-09-06 13:29:04 +03:00
_nkm . max_m = nkm - > m . max ? : 1 < < nkm - > m . width ;
2016-06-29 22:05:32 +03:00
2023-08-07 15:43:40 +03:00
ccu_nkm_find_best ( parent_rate , rate , & _nkm , & nkm - > common ) ;
2016-06-29 22:05:32 +03:00
spin_lock_irqsave ( nkm - > common . lock , flags ) ;
reg = readl ( nkm - > common . base + nkm - > common . reg ) ;
reg & = ~ GENMASK ( nkm - > n . width + nkm - > n . shift - 1 , nkm - > n . shift ) ;
reg & = ~ GENMASK ( nkm - > k . width + nkm - > k . shift - 1 , nkm - > k . shift ) ;
reg & = ~ GENMASK ( nkm - > m . width + nkm - > m . shift - 1 , nkm - > m . shift ) ;
2016-11-08 20:12:34 +03:00
reg | = ( _nkm . n - nkm - > n . offset ) < < nkm - > n . shift ;
reg | = ( _nkm . k - nkm - > k . offset ) < < nkm - > k . shift ;
reg | = ( _nkm . m - nkm - > m . offset ) < < nkm - > m . shift ;
2016-06-29 22:05:32 +03:00
writel ( reg , nkm - > common . base + nkm - > common . reg ) ;
spin_unlock_irqrestore ( nkm - > common . lock , flags ) ;
ccu_helper_wait_for_lock ( & nkm - > common , nkm - > lock ) ;
return 0 ;
}
2016-07-26 10:04:28 +03:00
static u8 ccu_nkm_get_parent ( struct clk_hw * hw )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
return ccu_mux_helper_get_parent ( & nkm - > common , & nkm - > mux ) ;
}
static int ccu_nkm_set_parent ( struct clk_hw * hw , u8 index )
{
struct ccu_nkm * nkm = hw_to_ccu_nkm ( hw ) ;
return ccu_mux_helper_set_parent ( & nkm - > common , & nkm - > mux , index ) ;
}
2016-06-29 22:05:32 +03:00
const struct clk_ops ccu_nkm_ops = {
. disable = ccu_nkm_disable ,
. enable = ccu_nkm_enable ,
. is_enabled = ccu_nkm_is_enabled ,
2016-07-26 10:04:28 +03:00
. get_parent = ccu_nkm_get_parent ,
. set_parent = ccu_nkm_set_parent ,
. determine_rate = ccu_nkm_determine_rate ,
2016-06-29 22:05:32 +03:00
. recalc_rate = ccu_nkm_recalc_rate ,
. set_rate = ccu_nkm_set_rate ,
} ;
2021-11-19 06:33:34 +03:00
EXPORT_SYMBOL_NS_GPL ( ccu_nkm_ops , SUNXI_CCU ) ;