2014-05-15 10:55:11 +02:00
/*
* Copyright ( C ) 2014 Free Electrons
*
* License Terms : GNU General Public License v2
* Author : Boris BREZILLON < boris . brezillon @ free - electrons . com >
*
* Allwinner A31 AR100 clock driver
*
*/
2016-01-25 21:15:46 +08:00
# include <linux/bitops.h>
2014-05-15 10:55:11 +02:00
# include <linux/clk-provider.h>
2016-07-04 17:12:18 -04:00
# include <linux/init.h>
2014-05-15 10:55:11 +02:00
# include <linux/of.h>
# include <linux/platform_device.h>
2016-01-25 21:15:46 +08:00
# include <linux/spinlock.h>
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
# include "clk-factors.h"
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
/**
* sun6i_get_ar100_factors - Calculates factors p , m for AR100
*
* AR100 rate is calculated as follows
* rate = ( parent_rate > > p ) / ( m + 1 ) ;
*/
static void sun6i_get_ar100_factors ( struct factors_request * req )
2014-05-15 10:55:11 +02:00
{
2016-01-25 21:15:46 +08:00
unsigned long div ;
2014-05-15 10:55:11 +02:00
int shift ;
2016-01-25 21:15:46 +08:00
/* clock only divides */
if ( req - > rate > req - > parent_rate )
req - > rate = req - > parent_rate ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
div = DIV_ROUND_UP ( req - > parent_rate , req - > rate ) ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
if ( div < 32 )
shift = 0 ;
else if ( div > > 1 < 32 )
shift = 1 ;
else if ( div > > 2 < 32 )
shift = 2 ;
else
shift = 3 ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
div > > = shift ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
if ( div > 32 )
div = 32 ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
req - > rate = ( req - > parent_rate > > shift ) / div ;
req - > m = div - 1 ;
req - > p = shift ;
2014-05-15 10:55:11 +02:00
}
2016-01-25 21:15:46 +08:00
static const struct clk_factors_config sun6i_ar100_config = {
. mwidth = 5 ,
. mshift = 8 ,
. pwidth = 2 ,
. pshift = 4 ,
2014-05-15 10:55:11 +02:00
} ;
2016-02-02 16:55:30 +01:00
static const struct factors_data sun6i_ar100_data = {
2016-01-25 21:15:46 +08:00
. mux = 16 ,
. muxmask = GENMASK ( 1 , 0 ) ,
. table = & sun6i_ar100_config ,
. getter = sun6i_get_ar100_factors ,
} ;
static DEFINE_SPINLOCK ( sun6i_ar100_lock ) ;
2014-05-15 10:55:11 +02:00
static int sun6i_a31_ar100_clk_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct resource * r ;
2016-01-25 21:15:46 +08:00
void __iomem * reg ;
2014-05-15 10:55:11 +02:00
struct clk * clk ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2016-01-25 21:15:46 +08:00
reg = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( reg ) )
return PTR_ERR ( reg ) ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
clk = sunxi_factors_register ( np , & sun6i_ar100_data , & sun6i_ar100_lock ,
reg ) ;
if ( ! clk )
return - ENOMEM ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
platform_set_drvdata ( pdev , clk ) ;
2014-05-15 10:55:11 +02:00
2016-01-25 21:15:46 +08:00
return 0 ;
}
2014-05-15 10:55:11 +02:00
2014-07-28 00:49:43 -03:00
static const struct of_device_id sun6i_a31_ar100_clk_dt_ids [ ] = {
2014-05-15 10:55:11 +02:00
{ . compatible = " allwinner,sun6i-a31-ar100-clk " } ,
{ /* sentinel */ }
} ;
static struct platform_driver sun6i_a31_ar100_clk_driver = {
. driver = {
. name = " sun6i-a31-ar100-clk " ,
. of_match_table = sun6i_a31_ar100_clk_dt_ids ,
2016-07-04 17:12:18 -04:00
. suppress_bind_attrs = true ,
2014-05-15 10:55:11 +02:00
} ,
. probe = sun6i_a31_ar100_clk_probe ,
} ;
2016-07-04 17:12:18 -04:00
builtin_platform_driver ( sun6i_a31_ar100_clk_driver ) ;