2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-07-11 01:55:18 +04:00
/*
* Copyright 2013 Emilio López
*
* Emilio López < emilio @ elopez . com . ar >
*/
2015-06-20 01:00:46 +03:00
# include <linux/clk.h>
2014-07-11 01:55:18 +04:00
# include <linux/clk-provider.h>
2019-04-19 01:20:22 +03:00
# include <linux/io.h>
2014-07-11 20:43:18 +04:00
# include <linux/of_address.h>
2014-12-20 13:36:49 +03:00
# include <linux/platform_device.h>
2015-06-20 01:00:46 +03:00
# include <linux/slab.h>
2014-07-11 01:55:18 +04:00
# include "clk-factors.h"
/**
2016-10-01 22:46:24 +03:00
* sun4i_a10_get_mod0_factors ( ) - calculates m , n factors for MOD0 - style clocks
2014-07-11 01:55:18 +04:00
* MOD0 rate is calculated as follows
* rate = ( parent_rate > > p ) / ( m + 1 ) ;
*/
2016-01-25 16:15:42 +03:00
static void sun4i_a10_get_mod0_factors ( struct factors_request * req )
2014-07-11 01:55:18 +04:00
{
u8 div , calcm , calcp ;
/* These clocks can only divide, so we will never be able to achieve
* frequencies higher than the parent frequency */
2016-01-25 16:15:42 +03:00
if ( req - > rate > req - > parent_rate )
req - > rate = req - > parent_rate ;
2014-07-11 01:55:18 +04:00
2016-01-25 16:15:42 +03:00
div = DIV_ROUND_UP ( req - > parent_rate , req - > rate ) ;
2014-07-11 01:55:18 +04:00
if ( div < 16 )
calcp = 0 ;
else if ( div / 2 < 16 )
calcp = 1 ;
else if ( div / 4 < 16 )
calcp = 2 ;
else
calcp = 3 ;
calcm = DIV_ROUND_UP ( div , 1 < < calcp ) ;
2016-01-25 16:15:42 +03:00
req - > rate = ( req - > parent_rate > > calcp ) / calcm ;
req - > m = calcm - 1 ;
req - > p = calcp ;
2014-07-11 01:55:18 +04:00
}
/* user manual says "n" but it's really "p" */
2016-01-25 16:15:38 +03:00
static const struct clk_factors_config sun4i_a10_mod0_config = {
2014-07-11 01:55:18 +04:00
. mshift = 0 ,
. mwidth = 4 ,
. pshift = 16 ,
. pwidth = 2 ,
} ;
2014-12-20 13:36:49 +03:00
static const struct factors_data sun4i_a10_mod0_data = {
2014-07-11 01:55:18 +04:00
. enable = 31 ,
. mux = 24 ,
2014-10-20 18:10:26 +04:00
. muxmask = BIT ( 1 ) | BIT ( 0 ) ,
2014-07-11 01:55:18 +04:00
. table = & sun4i_a10_mod0_config ,
. getter = sun4i_a10_get_mod0_factors ,
} ;
static DEFINE_SPINLOCK ( sun4i_a10_mod0_lock ) ;
static void __init sun4i_a10_mod0_setup ( struct device_node * node )
{
2014-11-23 16:38:07 +03:00
void __iomem * reg ;
reg = of_iomap ( node , 0 ) ;
if ( ! reg ) {
2014-12-20 13:36:49 +03:00
/*
* This happens with mod0 clk nodes instantiated through
* mfd , as those do not have their resources assigned at
* CLK_OF_DECLARE time yet , so do not print an error .
*/
2014-11-23 16:38:07 +03:00
return ;
}
sunxi_factors_register ( node , & sun4i_a10_mod0_data ,
& sun4i_a10_mod0_lock , reg ) ;
2014-07-11 01:55:18 +04:00
}
2016-07-05 19:23:28 +03:00
CLK_OF_DECLARE_DRIVER ( sun4i_a10_mod0 , " allwinner,sun4i-a10-mod0-clk " ,
sun4i_a10_mod0_setup ) ;
2014-07-11 01:56:11 +04:00
2014-12-20 13:36:49 +03:00
static int sun4i_a10_mod0_clk_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct resource * r ;
void __iomem * reg ;
if ( ! np )
return - ENODEV ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
reg = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( reg ) )
return PTR_ERR ( reg ) ;
sunxi_factors_register ( np , & sun4i_a10_mod0_data ,
& sun4i_a10_mod0_lock , reg ) ;
return 0 ;
}
static const struct of_device_id sun4i_a10_mod0_clk_dt_ids [ ] = {
{ . compatible = " allwinner,sun4i-a10-mod0-clk " } ,
{ /* sentinel */ }
} ;
static struct platform_driver sun4i_a10_mod0_clk_driver = {
. driver = {
. name = " sun4i-a10-mod0-clk " ,
. of_match_table = sun4i_a10_mod0_clk_dt_ids ,
} ,
. probe = sun4i_a10_mod0_clk_probe ,
} ;
2015-06-03 18:20:05 +03:00
builtin_platform_driver ( sun4i_a10_mod0_clk_driver ) ;
2014-12-20 13:36:49 +03:00
2015-01-17 08:19:26 +03:00
static const struct factors_data sun9i_a80_mod0_data __initconst = {
. enable = 31 ,
. mux = 24 ,
. muxmask = BIT ( 3 ) | BIT ( 2 ) | BIT ( 1 ) | BIT ( 0 ) ,
. table = & sun4i_a10_mod0_config ,
. getter = sun4i_a10_get_mod0_factors ,
} ;
static void __init sun9i_a80_mod0_setup ( struct device_node * node )
{
void __iomem * reg ;
reg = of_io_request_and_map ( node , 0 , of_node_full_name ( node ) ) ;
if ( IS_ERR ( reg ) ) {
2018-08-28 18:44:29 +03:00
pr_err ( " Could not get registers for mod0-clk: %pOFn \n " ,
node ) ;
2015-01-17 08:19:26 +03:00
return ;
}
sunxi_factors_register ( node , & sun9i_a80_mod0_data ,
& sun4i_a10_mod0_lock , reg ) ;
}
CLK_OF_DECLARE ( sun9i_a80_mod0 , " allwinner,sun9i-a80-mod0-clk " , sun9i_a80_mod0_setup ) ;
2014-07-11 01:56:11 +04:00
static DEFINE_SPINLOCK ( sun5i_a13_mbus_lock ) ;
static void __init sun5i_a13_mbus_setup ( struct device_node * node )
{
2014-11-23 16:38:07 +03:00
void __iomem * reg ;
reg = of_iomap ( node , 0 ) ;
if ( ! reg ) {
pr_err ( " Could not get registers for a13-mbus-clk \n " ) ;
return ;
}
2014-07-11 01:56:11 +04:00
/* The MBUS clocks needs to be always enabled */
2018-01-03 03:50:27 +03:00
sunxi_factors_register_critical ( node , & sun4i_a10_mod0_data ,
& sun5i_a13_mbus_lock , reg ) ;
2014-07-11 01:56:11 +04:00
}
CLK_OF_DECLARE ( sun5i_a13_mbus , " allwinner,sun5i-a13-mbus-clk " , sun5i_a13_mbus_setup ) ;
2014-07-11 20:43:18 +04:00
struct mmc_phase {
struct clk_hw hw ;
2014-12-07 19:43:04 +03:00
u8 offset ;
2014-07-11 20:43:18 +04:00
void __iomem * reg ;
spinlock_t * lock ;
} ;
# define to_mmc_phase(_hw) container_of(_hw, struct mmc_phase, hw)
static int mmc_get_phase ( struct clk_hw * hw )
{
struct clk * mmc , * mmc_parent , * clk = hw - > clk ;
struct mmc_phase * phase = to_mmc_phase ( hw ) ;
unsigned int mmc_rate , mmc_parent_rate ;
u16 step , mmc_div ;
u32 value ;
u8 delay ;
value = readl ( phase - > reg ) ;
2014-12-07 19:43:04 +03:00
delay = ( value > > phase - > offset ) & 0x3 ;
2014-07-11 20:43:18 +04:00
if ( ! delay )
return 180 ;
/* Get the main MMC clock */
mmc = clk_get_parent ( clk ) ;
if ( ! mmc )
return - EINVAL ;
/* And its rate */
mmc_rate = clk_get_rate ( mmc ) ;
if ( ! mmc_rate )
return - EINVAL ;
/* Now, get the MMC parent (most likely some PLL) */
mmc_parent = clk_get_parent ( mmc ) ;
if ( ! mmc_parent )
return - EINVAL ;
/* And its rate */
mmc_parent_rate = clk_get_rate ( mmc_parent ) ;
if ( ! mmc_parent_rate )
return - EINVAL ;
/* Get MMC clock divider */
mmc_div = mmc_parent_rate / mmc_rate ;
step = DIV_ROUND_CLOSEST ( 360 , mmc_div ) ;
return delay * step ;
}
static int mmc_set_phase ( struct clk_hw * hw , int degrees )
{
struct clk * mmc , * mmc_parent , * clk = hw - > clk ;
struct mmc_phase * phase = to_mmc_phase ( hw ) ;
unsigned int mmc_rate , mmc_parent_rate ;
unsigned long flags ;
u32 value ;
u8 delay ;
/* Get the main MMC clock */
mmc = clk_get_parent ( clk ) ;
if ( ! mmc )
return - EINVAL ;
/* And its rate */
mmc_rate = clk_get_rate ( mmc ) ;
if ( ! mmc_rate )
return - EINVAL ;
/* Now, get the MMC parent (most likely some PLL) */
mmc_parent = clk_get_parent ( mmc ) ;
if ( ! mmc_parent )
return - EINVAL ;
/* And its rate */
mmc_parent_rate = clk_get_rate ( mmc_parent ) ;
if ( ! mmc_parent_rate )
return - EINVAL ;
if ( degrees ! = 180 ) {
u16 step , mmc_div ;
/* Get MMC clock divider */
mmc_div = mmc_parent_rate / mmc_rate ;
/*
* We can only outphase the clocks by multiple of the
* PLL ' s period .
*
* Since the MMC clock in only a divider , and the
* formula to get the outphasing in degrees is deg =
* 360 * delta / period
*
* If we simplify this formula , we can see that the
* only thing that we ' re concerned about is the number
* of period we want to outphase our clock from , and
* the divider set by the MMC clock .
*/
step = DIV_ROUND_CLOSEST ( 360 , mmc_div ) ;
delay = DIV_ROUND_CLOSEST ( degrees , step ) ;
} else {
delay = 0 ;
}
spin_lock_irqsave ( phase - > lock , flags ) ;
value = readl ( phase - > reg ) ;
2014-12-07 19:43:04 +03:00
value & = ~ GENMASK ( phase - > offset + 3 , phase - > offset ) ;
value | = delay < < phase - > offset ;
2014-07-11 20:43:18 +04:00
writel ( value , phase - > reg ) ;
spin_unlock_irqrestore ( phase - > lock , flags ) ;
return 0 ;
}
static const struct clk_ops mmc_clk_ops = {
. get_phase = mmc_get_phase ,
. set_phase = mmc_set_phase ,
} ;
2015-01-13 04:37:23 +03:00
/*
* sunxi_mmc_setup - Common setup function for mmc module clocks
*
* The only difference between module clocks on different platforms is the
* width of the mux register bits and the valid values , which are passed in
* through struct factors_data . The phase clocks parts are identical .
*/
static void __init sunxi_mmc_setup ( struct device_node * node ,
const struct factors_data * data ,
spinlock_t * lock )
2014-12-07 19:43:04 +03:00
{
struct clk_onecell_data * clk_data ;
const char * parent ;
void __iomem * reg ;
int i ;
2014-07-11 20:43:18 +04:00
2014-12-07 19:43:04 +03:00
reg = of_io_request_and_map ( node , 0 , of_node_full_name ( node ) ) ;
if ( IS_ERR ( reg ) ) {
2018-08-28 18:44:29 +03:00
pr_err ( " Couldn't map the %pOFn clock registers \n " , node ) ;
2014-12-07 19:43:04 +03:00
return ;
}
2014-07-11 20:43:18 +04:00
2014-12-07 19:43:04 +03:00
clk_data = kmalloc ( sizeof ( * clk_data ) , GFP_KERNEL ) ;
if ( ! clk_data )
return ;
2014-07-11 20:43:18 +04:00
2014-12-07 19:43:04 +03:00
clk_data - > clks = kcalloc ( 3 , sizeof ( * clk_data - > clks ) , GFP_KERNEL ) ;
if ( ! clk_data - > clks )
goto err_free_data ;
clk_data - > clk_num = 3 ;
2015-01-13 04:37:23 +03:00
clk_data - > clks [ 0 ] = sunxi_factors_register ( node , data , lock , reg ) ;
2014-12-07 19:43:04 +03:00
if ( ! clk_data - > clks [ 0 ] )
goto err_free_clks ;
parent = __clk_get_name ( clk_data - > clks [ 0 ] ) ;
for ( i = 1 ; i < 3 ; i + + ) {
struct clk_init_data init = {
. num_parents = 1 ,
. parent_names = & parent ,
. ops = & mmc_clk_ops ,
} ;
struct mmc_phase * phase ;
phase = kmalloc ( sizeof ( * phase ) , GFP_KERNEL ) ;
if ( ! phase )
continue ;
phase - > hw . init = & init ;
phase - > reg = reg ;
2015-01-13 04:37:23 +03:00
phase - > lock = lock ;
2014-12-07 19:43:04 +03:00
if ( i = = 1 )
phase - > offset = 8 ;
else
phase - > offset = 20 ;
if ( of_property_read_string_index ( node , " clock-output-names " ,
i , & init . name ) )
init . name = node - > name ;
clk_data - > clks [ i ] = clk_register ( NULL , & phase - > hw ) ;
if ( IS_ERR ( clk_data - > clks [ i ] ) ) {
kfree ( phase ) ;
continue ;
}
}
2014-07-11 20:43:18 +04:00
2014-12-07 19:43:04 +03:00
of_clk_add_provider ( node , of_clk_src_onecell_get , clk_data ) ;
2014-07-11 20:43:18 +04:00
return ;
2014-12-07 19:43:04 +03:00
err_free_clks :
kfree ( clk_data - > clks ) ;
err_free_data :
kfree ( clk_data ) ;
2014-07-11 20:43:18 +04:00
}
2015-01-13 04:37:23 +03:00
static DEFINE_SPINLOCK ( sun4i_a10_mmc_lock ) ;
static void __init sun4i_a10_mmc_setup ( struct device_node * node )
{
sunxi_mmc_setup ( node , & sun4i_a10_mod0_data , & sun4i_a10_mmc_lock ) ;
}
2014-12-07 19:43:04 +03:00
CLK_OF_DECLARE ( sun4i_a10_mmc , " allwinner,sun4i-a10-mmc-clk " , sun4i_a10_mmc_setup ) ;
2015-01-17 08:19:26 +03:00
static DEFINE_SPINLOCK ( sun9i_a80_mmc_lock ) ;
static void __init sun9i_a80_mmc_setup ( struct device_node * node )
{
sunxi_mmc_setup ( node , & sun9i_a80_mod0_data , & sun9i_a80_mmc_lock ) ;
}
CLK_OF_DECLARE ( sun9i_a80_mmc , " allwinner,sun9i-a80-mmc-clk " , sun9i_a80_mmc_setup ) ;