2016-08-30 11:38:07 +03:00
/*
* Copyright ( C ) 2016 Maxime Ripard
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*/
# include <linux/clk-provider.h>
# include "ccu_gate.h"
# include "ccu_mult.h"
2016-09-30 00:50:21 +03:00
struct _ccu_mult {
2016-09-29 23:57:26 +03:00
unsigned long mult , min , max ;
2016-09-30 00:50:21 +03:00
} ;
2016-08-30 11:38:07 +03:00
static void ccu_mult_find_best ( unsigned long parent , unsigned long rate ,
2016-09-30 00:50:21 +03:00
struct _ccu_mult * mult )
2016-08-30 11:38:07 +03:00
{
2016-09-30 00:50:21 +03:00
int _mult ;
_mult = rate / parent ;
2016-09-29 23:57:26 +03:00
if ( _mult < mult - > min )
_mult = mult - > min ;
2016-09-30 00:50:21 +03:00
if ( _mult > mult - > max )
_mult = mult - > max ;
mult - > mult = _mult ;
2016-08-30 11:38:07 +03:00
}
static unsigned long ccu_mult_round_rate ( struct ccu_mux_internal * mux ,
2017-05-17 10:40:31 +03:00
struct clk_hw * parent ,
unsigned long * parent_rate ,
unsigned long rate ,
void * data )
2016-08-30 11:38:07 +03:00
{
struct ccu_mult * cm = data ;
2016-09-30 00:50:21 +03:00
struct _ccu_mult _cm ;
2016-08-30 11:38:07 +03:00
2017-01-22 01:41:56 +03:00
_cm . min = cm - > mult . min ;
2016-10-14 13:08:19 +03:00
if ( cm - > mult . max )
_cm . max = cm - > mult . max ;
else
_cm . max = ( 1 < < cm - > mult . width ) + cm - > mult . offset - 1 ;
2017-05-17 10:40:31 +03:00
ccu_mult_find_best ( * parent_rate , rate , & _cm ) ;
2016-08-30 11:38:07 +03:00
2017-05-17 10:40:31 +03:00
return * parent_rate * _cm . mult ;
2016-08-30 11:38:07 +03:00
}
static void ccu_mult_disable ( struct clk_hw * hw )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
return ccu_gate_helper_disable ( & cm - > common , cm - > enable ) ;
}
static int ccu_mult_enable ( struct clk_hw * hw )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
return ccu_gate_helper_enable ( & cm - > common , cm - > enable ) ;
}
static int ccu_mult_is_enabled ( struct clk_hw * hw )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
return ccu_gate_helper_is_enabled ( & cm - > common , cm - > enable ) ;
}
static unsigned long ccu_mult_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
unsigned long val ;
u32 reg ;
2016-10-13 13:44:55 +03:00
if ( ccu_frac_helper_is_enabled ( & cm - > common , & cm - > frac ) )
return ccu_frac_helper_read_rate ( & cm - > common , & cm - > frac ) ;
2016-08-30 11:38:07 +03:00
reg = readl ( cm - > common . base + cm - > common . reg ) ;
val = reg > > cm - > mult . shift ;
val & = ( 1 < < cm - > mult . width ) - 1 ;
2017-05-17 10:40:35 +03:00
parent_rate = ccu_mux_helper_apply_prediv ( & cm - > common , & cm - > mux , - 1 ,
parent_rate ) ;
2016-08-30 11:38:07 +03:00
2016-11-08 20:12:34 +03:00
return parent_rate * ( val + cm - > mult . offset ) ;
2016-08-30 11:38:07 +03:00
}
static int ccu_mult_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
return ccu_mux_helper_determine_rate ( & cm - > common , & cm - > mux ,
req , ccu_mult_round_rate , cm ) ;
}
static int ccu_mult_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
2016-09-30 00:50:21 +03:00
struct _ccu_mult _cm ;
2016-08-30 11:38:07 +03:00
unsigned long flags ;
u32 reg ;
2017-07-30 19:41:48 +03:00
if ( ccu_frac_helper_has_rate ( & cm - > common , & cm - > frac , rate ) ) {
ccu_frac_helper_enable ( & cm - > common , & cm - > frac ) ;
2017-07-30 19:41:50 +03:00
return ccu_frac_helper_set_rate ( & cm - > common , & cm - > frac ,
rate , cm - > lock ) ;
2017-07-30 19:41:48 +03:00
} else {
2016-10-13 13:44:55 +03:00
ccu_frac_helper_disable ( & cm - > common , & cm - > frac ) ;
2017-07-30 19:41:48 +03:00
}
2016-10-13 13:44:55 +03:00
2017-05-17 10:40:35 +03:00
parent_rate = ccu_mux_helper_apply_prediv ( & cm - > common , & cm - > mux , - 1 ,
parent_rate ) ;
2016-08-30 11:38:07 +03:00
2016-09-30 23:16:51 +03:00
_cm . min = cm - > mult . min ;
2016-10-14 13:08:19 +03:00
if ( cm - > mult . max )
_cm . max = cm - > mult . max ;
else
_cm . max = ( 1 < < cm - > mult . width ) + cm - > mult . offset - 1 ;
2016-09-30 00:50:21 +03:00
ccu_mult_find_best ( parent_rate , rate , & _cm ) ;
2016-08-30 11:38:07 +03:00
spin_lock_irqsave ( cm - > common . lock , flags ) ;
reg = readl ( cm - > common . base + cm - > common . reg ) ;
reg & = ~ GENMASK ( cm - > mult . width + cm - > mult . shift - 1 , cm - > mult . shift ) ;
2016-11-08 20:12:34 +03:00
reg | = ( ( _cm . mult - cm - > mult . offset ) < < cm - > mult . shift ) ;
2016-08-30 11:38:07 +03:00
2016-11-08 20:12:34 +03:00
writel ( reg , cm - > common . base + cm - > common . reg ) ;
2016-08-30 11:38:07 +03:00
spin_unlock_irqrestore ( cm - > common . lock , flags ) ;
2017-04-05 09:37:42 +03:00
ccu_helper_wait_for_lock ( & cm - > common , cm - > lock ) ;
2016-08-30 11:38:07 +03:00
return 0 ;
}
static u8 ccu_mult_get_parent ( struct clk_hw * hw )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
return ccu_mux_helper_get_parent ( & cm - > common , & cm - > mux ) ;
}
static int ccu_mult_set_parent ( struct clk_hw * hw , u8 index )
{
struct ccu_mult * cm = hw_to_ccu_mult ( hw ) ;
return ccu_mux_helper_set_parent ( & cm - > common , & cm - > mux , index ) ;
}
const struct clk_ops ccu_mult_ops = {
. disable = ccu_mult_disable ,
. enable = ccu_mult_enable ,
. is_enabled = ccu_mult_is_enabled ,
. get_parent = ccu_mult_get_parent ,
. set_parent = ccu_mult_set_parent ,
. determine_rate = ccu_mult_determine_rate ,
. recalc_rate = ccu_mult_recalc_rate ,
. set_rate = ccu_mult_set_rate ,
} ;