2015-05-08 17:31:57 +03:00
/*
* H8S2678 clock driver
*
* Copyright 2015 Yoshinori Sato < ysato @ users . sourceforge . jp >
*/
# include <linux/clk-provider.h>
# include <linux/err.h>
# include <linux/device.h>
# include <linux/of_address.h>
2015-09-01 06:27:32 +03:00
# include <linux/slab.h>
2015-05-08 17:31:57 +03:00
static DEFINE_SPINLOCK ( clklock ) ;
# define MAX_FREQ 33333333
# define MIN_FREQ 8000000
struct pll_clock {
struct clk_hw hw ;
void __iomem * sckcr ;
void __iomem * pllcr ;
} ;
# define to_pll_clock(_hw) container_of(_hw, struct pll_clock, hw)
static unsigned long pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct pll_clock * pll_clock = to_pll_clock ( hw ) ;
2015-07-14 03:06:53 +03:00
int mul = 1 < < ( readb ( pll_clock - > pllcr ) & 3 ) ;
2015-05-08 17:31:57 +03:00
return parent_rate * mul ;
}
static long pll_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
int i , m = - 1 ;
long offset [ 3 ] ;
if ( rate > MAX_FREQ )
rate = MAX_FREQ ;
if ( rate < MIN_FREQ )
rate = MIN_FREQ ;
for ( i = 0 ; i < 3 ; i + + )
offset [ i ] = abs ( rate - ( * prate * ( 1 < < i ) ) ) ;
for ( i = 0 ; i < 3 ; i + + )
if ( m < 0 )
m = i ;
else
m = ( offset [ i ] < offset [ m ] ) ? i : m ;
return * prate * ( 1 < < m ) ;
}
static int pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
int pll ;
unsigned char val ;
unsigned long flags ;
struct pll_clock * pll_clock = to_pll_clock ( hw ) ;
pll = ( ( rate / parent_rate ) / 2 ) & 0x03 ;
spin_lock_irqsave ( & clklock , flags ) ;
2015-07-14 03:06:53 +03:00
val = readb ( pll_clock - > sckcr ) ;
2015-05-08 17:31:57 +03:00
val | = 0x08 ;
2015-07-14 03:06:53 +03:00
writeb ( val , pll_clock - > sckcr ) ;
val = readb ( pll_clock - > pllcr ) ;
2015-05-08 17:31:57 +03:00
val & = ~ 0x03 ;
val | = pll ;
2015-07-14 03:06:53 +03:00
writeb ( val , pll_clock - > pllcr ) ;
2015-05-08 17:31:57 +03:00
spin_unlock_irqrestore ( & clklock , flags ) ;
return 0 ;
}
static const struct clk_ops pll_ops = {
. recalc_rate = pll_recalc_rate ,
. round_rate = pll_round_rate ,
. set_rate = pll_set_rate ,
} ;
static void __init h8s2678_pll_clk_setup ( struct device_node * node )
{
2016-02-20 04:36:51 +03:00
unsigned int num_parents ;
2015-05-08 17:31:57 +03:00
struct clk * clk ;
const char * clk_name = node - > name ;
const char * parent_name ;
struct pll_clock * pll_clock ;
struct clk_init_data init ;
num_parents = of_clk_get_parent_count ( node ) ;
2016-02-20 04:36:51 +03:00
if ( ! num_parents ) {
2015-05-08 17:31:57 +03:00
pr_err ( " %s: no parent found " , clk_name ) ;
return ;
}
2015-07-14 02:54:04 +03:00
pll_clock = kzalloc ( sizeof ( * pll_clock ) , GFP_KERNEL ) ;
if ( ! pll_clock )
2015-05-08 17:31:57 +03:00
return ;
pll_clock - > sckcr = of_iomap ( node , 0 ) ;
if ( pll_clock - > sckcr = = NULL ) {
pr_err ( " %s: failed to map divide register " , clk_name ) ;
2015-05-14 13:05:00 +03:00
goto free_clock ;
2015-05-08 17:31:57 +03:00
}
pll_clock - > pllcr = of_iomap ( node , 1 ) ;
if ( pll_clock - > pllcr = = NULL ) {
pr_err ( " %s: failed to map multiply register " , clk_name ) ;
2015-05-14 13:05:00 +03:00
goto unmap_sckcr ;
2015-05-08 17:31:57 +03:00
}
parent_name = of_clk_get_parent_name ( node , 0 ) ;
init . name = clk_name ;
init . ops = & pll_ops ;
init . flags = CLK_IS_BASIC ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
pll_clock - > hw . init = & init ;
clk = clk_register ( NULL , & pll_clock - > hw ) ;
2015-05-14 13:05:00 +03:00
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: failed to register %s div clock (%ld) \n " ,
__func__ , clk_name , PTR_ERR ( clk ) ) ;
goto unmap_pllcr ;
2015-05-08 17:31:57 +03:00
}
2015-05-14 13:05:00 +03:00
of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
return ;
unmap_pllcr :
iounmap ( pll_clock - > pllcr ) ;
unmap_sckcr :
iounmap ( pll_clock - > sckcr ) ;
free_clock :
kfree ( pll_clock ) ;
2015-05-08 17:31:57 +03:00
}
CLK_OF_DECLARE ( h8s2678_div_clk , " renesas,h8s2678-pll-clock " ,
h8s2678_pll_clk_setup ) ;