2018-12-21 17:02:37 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2017 BayLibre , SAS
* Author : Neil Armstrong < narmstrong @ baylibre . com >
* Author : Jerome Brunet < jbrunet @ baylibre . com >
*/
/*
* The AO Domain embeds a dual / divider to generate a more precise
* 32 , 768 KHz clock for low - power suspend mode and CEC .
* ______ ______
* | | | |
* | Div1 | - | Cnt1 |
* / | ______ | | ______ | \
* - | ______ ______ X - - > Out
* \ | | | | /
* | Div2 | - | Cnt2 |
* | ______ | | ______ |
*
* The dividing can be switched to single or dual , with a counter
* for each divider to set when the switching is done .
*/
# include <linux/clk-provider.h>
clk: meson: rework and clean drivers dependencies
Initially, the meson clock directory only hosted 2 controllers drivers,
for meson8 and gxbb. At the time, both used the same set of clock drivers
so managing the dependencies was not a big concern.
Since this ancient time, entropy did its job, controllers with different
requirement and specific clock drivers have been added. Unfortunately, we
did not do a great job at managing the dependencies between the
controllers and the different clock drivers. Some drivers, such as
clk-phase or vid-pll-div, are compiled even if they are useless on the
target (meson8). As we are adding new controllers, we need to be able to
pick a driver w/o pulling the whole thing.
The patch aims to clean things up by:
* providing a dedicated CONFIG_ for each clock drivers
* allowing clock drivers to be compiled as a modules, if possible
* stating explicitly which drivers are required by each controller.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com
2019-02-01 13:58:41 +01:00
# include <linux/module.h>
# include "clk-regmap.h"
# include "clk-dualdiv.h"
2018-12-21 17:02:37 +01:00
static inline struct meson_clk_dualdiv_data *
meson_clk_dualdiv_data ( struct clk_regmap * clk )
{
return ( struct meson_clk_dualdiv_data * ) clk - > data ;
}
static unsigned long
__dualdiv_param_to_rate ( unsigned long parent_rate ,
const struct meson_clk_dualdiv_param * p )
{
if ( ! p - > dual )
return DIV_ROUND_CLOSEST ( parent_rate , p - > n1 ) ;
return DIV_ROUND_CLOSEST ( parent_rate * ( p - > m1 + p - > m2 ) ,
p - > n1 * p - > m1 + p - > n2 * p - > m2 ) ;
}
static unsigned long meson_clk_dualdiv_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_dualdiv_data * dualdiv = meson_clk_dualdiv_data ( clk ) ;
struct meson_clk_dualdiv_param setting ;
setting . dual = meson_parm_read ( clk - > map , & dualdiv - > dual ) ;
setting . n1 = meson_parm_read ( clk - > map , & dualdiv - > n1 ) + 1 ;
setting . m1 = meson_parm_read ( clk - > map , & dualdiv - > m1 ) + 1 ;
setting . n2 = meson_parm_read ( clk - > map , & dualdiv - > n2 ) + 1 ;
setting . m2 = meson_parm_read ( clk - > map , & dualdiv - > m2 ) + 1 ;
return __dualdiv_param_to_rate ( parent_rate , & setting ) ;
}
static const struct meson_clk_dualdiv_param *
__dualdiv_get_setting ( unsigned long rate , unsigned long parent_rate ,
struct meson_clk_dualdiv_data * dualdiv )
{
const struct meson_clk_dualdiv_param * table = dualdiv - > table ;
unsigned long best = 0 , now = 0 ;
unsigned int i , best_i = 0 ;
if ( ! table )
return NULL ;
for ( i = 0 ; table [ i ] . n1 ; i + + ) {
now = __dualdiv_param_to_rate ( parent_rate , & table [ i ] ) ;
/* If we get an exact match, don't bother any further */
if ( now = = rate ) {
return & table [ i ] ;
} else if ( abs ( now - rate ) < abs ( best - rate ) ) {
best = now ;
best_i = i ;
}
}
return ( struct meson_clk_dualdiv_param * ) & table [ best_i ] ;
}
static long meson_clk_dualdiv_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_dualdiv_data * dualdiv = meson_clk_dualdiv_data ( clk ) ;
const struct meson_clk_dualdiv_param * setting =
__dualdiv_get_setting ( rate , * parent_rate , dualdiv ) ;
if ( ! setting )
return meson_clk_dualdiv_recalc_rate ( hw , * parent_rate ) ;
return __dualdiv_param_to_rate ( * parent_rate , setting ) ;
}
static int meson_clk_dualdiv_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_dualdiv_data * dualdiv = meson_clk_dualdiv_data ( clk ) ;
const struct meson_clk_dualdiv_param * setting =
__dualdiv_get_setting ( rate , parent_rate , dualdiv ) ;
if ( ! setting )
return - EINVAL ;
meson_parm_write ( clk - > map , & dualdiv - > dual , setting - > dual ) ;
meson_parm_write ( clk - > map , & dualdiv - > n1 , setting - > n1 - 1 ) ;
meson_parm_write ( clk - > map , & dualdiv - > m1 , setting - > m1 - 1 ) ;
meson_parm_write ( clk - > map , & dualdiv - > n2 , setting - > n2 - 1 ) ;
meson_parm_write ( clk - > map , & dualdiv - > m2 , setting - > m2 - 1 ) ;
return 0 ;
}
const struct clk_ops meson_clk_dualdiv_ops = {
. recalc_rate = meson_clk_dualdiv_recalc_rate ,
. round_rate = meson_clk_dualdiv_round_rate ,
. set_rate = meson_clk_dualdiv_set_rate ,
} ;
EXPORT_SYMBOL_GPL ( meson_clk_dualdiv_ops ) ;
const struct clk_ops meson_clk_dualdiv_ro_ops = {
. recalc_rate = meson_clk_dualdiv_recalc_rate ,
} ;
EXPORT_SYMBOL_GPL ( meson_clk_dualdiv_ro_ops ) ;
clk: meson: rework and clean drivers dependencies
Initially, the meson clock directory only hosted 2 controllers drivers,
for meson8 and gxbb. At the time, both used the same set of clock drivers
so managing the dependencies was not a big concern.
Since this ancient time, entropy did its job, controllers with different
requirement and specific clock drivers have been added. Unfortunately, we
did not do a great job at managing the dependencies between the
controllers and the different clock drivers. Some drivers, such as
clk-phase or vid-pll-div, are compiled even if they are useless on the
target (meson8). As we are adding new controllers, we need to be able to
pick a driver w/o pulling the whole thing.
The patch aims to clean things up by:
* providing a dedicated CONFIG_ for each clock drivers
* allowing clock drivers to be compiled as a modules, if possible
* stating explicitly which drivers are required by each controller.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com
2019-02-01 13:58:41 +01:00
MODULE_DESCRIPTION ( " Amlogic dual divider driver " ) ;
MODULE_AUTHOR ( " Neil Armstrong <narmstrong@baylibre.com> " ) ;
MODULE_AUTHOR ( " Jerome Brunet <jbrunet@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;