2013-10-11 13:57:04 +04:00
/*
* Copyright ( C ) 2013 Boris BREZILLON < b . brezillon @ overkiz . 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 <linux/clkdev.h>
# include <linux/clk/at91_pmc.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/io.h>
# include <linux/wait.h>
# include <linux/sched.h>
# include "pmc.h"
# define PROG_SOURCE_MAX 5
# define PROG_ID_MAX 7
# define PROG_STATUS_MASK(id) (1 << ((id) + 8))
# define PROG_PRES_MASK 0x7
# define PROG_MAX_RM9200_CSS 3
struct clk_programmable_layout {
u8 pres_shift ;
u8 css_mask ;
u8 have_slck_mck ;
} ;
struct clk_programmable {
struct clk_hw hw ;
struct at91_pmc * pmc ;
u8 id ;
const struct clk_programmable_layout * layout ;
} ;
# define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
static unsigned long clk_programmable_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2014-03-11 13:00:34 +04:00
u32 pres ;
2013-10-11 13:57:04 +04:00
struct clk_programmable * prog = to_clk_programmable ( hw ) ;
struct at91_pmc * pmc = prog - > pmc ;
const struct clk_programmable_layout * layout = prog - > layout ;
2014-03-11 13:00:34 +04:00
pres = ( pmc_read ( pmc , AT91_PMC_PCKR ( prog - > id ) ) > > layout - > pres_shift ) &
PROG_PRES_MASK ;
return parent_rate > > pres ;
2013-10-11 13:57:04 +04:00
}
2014-03-11 13:00:32 +04:00
static long clk_programmable_determine_rate ( struct clk_hw * hw ,
unsigned long rate ,
2015-01-23 14:03:31 +03:00
unsigned long min_rate ,
unsigned long max_rate ,
2014-03-11 13:00:32 +04:00
unsigned long * best_parent_rate ,
2014-12-02 10:54:22 +03:00
struct clk_hw * * best_parent_hw )
2013-10-11 13:57:04 +04:00
{
2014-03-11 13:00:32 +04:00
struct clk * parent = NULL ;
long best_rate = - EINVAL ;
unsigned long parent_rate ;
unsigned long tmp_rate ;
int shift ;
int i ;
for ( i = 0 ; i < __clk_get_num_parents ( hw - > clk ) ; i + + ) {
parent = clk_get_parent_by_index ( hw - > clk , i ) ;
if ( ! parent )
continue ;
parent_rate = __clk_get_rate ( parent ) ;
for ( shift = 0 ; shift < PROG_PRES_MASK ; shift + + ) {
tmp_rate = parent_rate > > shift ;
if ( tmp_rate < = rate )
break ;
}
2013-10-11 13:57:04 +04:00
2014-03-11 13:00:32 +04:00
if ( tmp_rate > rate )
continue ;
2013-10-11 13:57:04 +04:00
2014-03-11 13:00:32 +04:00
if ( best_rate < 0 | | ( rate - tmp_rate ) < ( rate - best_rate ) ) {
best_rate = tmp_rate ;
* best_parent_rate = parent_rate ;
2014-12-02 10:54:22 +03:00
* best_parent_hw = __clk_get_hw ( parent ) ;
2013-10-11 13:57:04 +04:00
}
2014-03-11 13:00:32 +04:00
if ( ! best_rate )
2013-10-11 13:57:04 +04:00
break ;
}
return best_rate ;
}
static int clk_programmable_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_programmable * prog = to_clk_programmable ( hw ) ;
const struct clk_programmable_layout * layout = prog - > layout ;
2014-03-11 13:00:34 +04:00
struct at91_pmc * pmc = prog - > pmc ;
u32 tmp = pmc_read ( pmc , AT91_PMC_PCKR ( prog - > id ) ) & ~ layout - > css_mask ;
if ( layout - > have_slck_mck )
tmp & = AT91_PMC_CSSMCK_MCK ;
2013-10-11 13:57:04 +04:00
if ( index > layout - > css_mask ) {
if ( index > PROG_MAX_RM9200_CSS & & layout - > have_slck_mck ) {
2014-03-11 13:00:34 +04:00
tmp | = AT91_PMC_CSSMCK_MCK ;
2013-10-11 13:57:04 +04:00
return 0 ;
} else {
return - EINVAL ;
}
}
2014-03-11 13:00:34 +04:00
pmc_write ( pmc , AT91_PMC_PCKR ( prog - > id ) , tmp | index ) ;
2013-10-11 13:57:04 +04:00
return 0 ;
}
static u8 clk_programmable_get_parent ( struct clk_hw * hw )
{
u32 tmp ;
u8 ret ;
struct clk_programmable * prog = to_clk_programmable ( hw ) ;
struct at91_pmc * pmc = prog - > pmc ;
const struct clk_programmable_layout * layout = prog - > layout ;
tmp = pmc_read ( pmc , AT91_PMC_PCKR ( prog - > id ) ) ;
2014-03-11 13:00:34 +04:00
ret = tmp & layout - > css_mask ;
if ( layout - > have_slck_mck & & ( tmp & AT91_PMC_CSSMCK_MCK ) & & ! ret )
ret = PROG_MAX_RM9200_CSS + 1 ;
2013-10-11 13:57:04 +04:00
return ret ;
}
static int clk_programmable_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_programmable * prog = to_clk_programmable ( hw ) ;
2014-03-11 13:00:34 +04:00
struct at91_pmc * pmc = prog - > pmc ;
const struct clk_programmable_layout * layout = prog - > layout ;
2014-03-11 13:00:35 +04:00
unsigned long div = parent_rate / rate ;
2013-10-11 13:57:04 +04:00
int shift = 0 ;
2014-03-11 13:00:34 +04:00
u32 tmp = pmc_read ( pmc , AT91_PMC_PCKR ( prog - > id ) ) &
~ ( PROG_PRES_MASK < < layout - > pres_shift ) ;
2013-10-11 13:57:04 +04:00
2014-03-11 13:00:35 +04:00
if ( ! div )
return - EINVAL ;
2013-10-11 13:57:04 +04:00
2014-03-11 13:00:35 +04:00
shift = fls ( div ) - 1 ;
2013-10-11 13:57:04 +04:00
2014-03-11 13:00:35 +04:00
if ( div ! = ( 1 < < shift ) )
return - EINVAL ;
2013-10-11 13:57:04 +04:00
2014-03-11 13:00:35 +04:00
if ( shift > = PROG_PRES_MASK )
return - EINVAL ;
2013-10-11 13:57:04 +04:00
2014-03-11 13:00:34 +04:00
pmc_write ( pmc , AT91_PMC_PCKR ( prog - > id ) ,
tmp | ( shift < < layout - > pres_shift ) ) ;
2013-10-11 13:57:04 +04:00
return 0 ;
}
static const struct clk_ops programmable_ops = {
. recalc_rate = clk_programmable_recalc_rate ,
2014-03-11 13:00:32 +04:00
. determine_rate = clk_programmable_determine_rate ,
2013-10-11 13:57:04 +04:00
. get_parent = clk_programmable_get_parent ,
. set_parent = clk_programmable_set_parent ,
. set_rate = clk_programmable_set_rate ,
} ;
static struct clk * __init
2014-03-11 13:00:34 +04:00
at91_clk_register_programmable ( struct at91_pmc * pmc ,
2013-10-11 13:57:04 +04:00
const char * name , const char * * parent_names ,
u8 num_parents , u8 id ,
const struct clk_programmable_layout * layout )
{
struct clk_programmable * prog ;
struct clk * clk = NULL ;
struct clk_init_data init ;
if ( id > PROG_ID_MAX )
return ERR_PTR ( - EINVAL ) ;
prog = kzalloc ( sizeof ( * prog ) , GFP_KERNEL ) ;
if ( ! prog )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & programmable_ops ;
init . parent_names = parent_names ;
init . num_parents = num_parents ;
init . flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE ;
prog - > id = id ;
prog - > layout = layout ;
prog - > hw . init = & init ;
prog - > pmc = pmc ;
clk = clk_register ( NULL , & prog - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( prog ) ;
return clk ;
}
static const struct clk_programmable_layout at91rm9200_programmable_layout = {
. pres_shift = 2 ,
. css_mask = 0x3 ,
. have_slck_mck = 0 ,
} ;
static const struct clk_programmable_layout at91sam9g45_programmable_layout = {
. pres_shift = 2 ,
. css_mask = 0x3 ,
. have_slck_mck = 1 ,
} ;
static const struct clk_programmable_layout at91sam9x5_programmable_layout = {
. pres_shift = 4 ,
. css_mask = 0x7 ,
. have_slck_mck = 0 ,
} ;
static void __init
of_at91_clk_prog_setup ( struct device_node * np , struct at91_pmc * pmc ,
const struct clk_programmable_layout * layout )
{
int num ;
u32 id ;
int i ;
struct clk * clk ;
int num_parents ;
const char * parent_names [ PROG_SOURCE_MAX ] ;
const char * name ;
struct device_node * progclknp ;
2015-05-29 12:25:45 +03:00
num_parents = of_clk_get_parent_count ( np ) ;
2013-10-11 13:57:04 +04:00
if ( num_parents < = 0 | | num_parents > PROG_SOURCE_MAX )
return ;
for ( i = 0 ; i < num_parents ; + + i ) {
parent_names [ i ] = of_clk_get_parent_name ( np , i ) ;
if ( ! parent_names [ i ] )
return ;
}
num = of_get_child_count ( np ) ;
if ( ! num | | num > ( PROG_ID_MAX + 1 ) )
return ;
for_each_child_of_node ( np , progclknp ) {
if ( of_property_read_u32 ( progclknp , " reg " , & id ) )
continue ;
if ( of_property_read_string ( np , " clock-output-names " , & name ) )
name = progclknp - > name ;
2014-03-11 13:00:34 +04:00
clk = at91_clk_register_programmable ( pmc , name ,
2013-10-11 13:57:04 +04:00
parent_names , num_parents ,
id , layout ) ;
if ( IS_ERR ( clk ) )
continue ;
of_clk_add_provider ( progclknp , of_clk_src_simple_get , clk ) ;
}
}
void __init of_at91rm9200_clk_prog_setup ( struct device_node * np ,
struct at91_pmc * pmc )
{
of_at91_clk_prog_setup ( np , pmc , & at91rm9200_programmable_layout ) ;
}
void __init of_at91sam9g45_clk_prog_setup ( struct device_node * np ,
struct at91_pmc * pmc )
{
of_at91_clk_prog_setup ( np , pmc , & at91sam9g45_programmable_layout ) ;
}
void __init of_at91sam9x5_clk_prog_setup ( struct device_node * np ,
struct at91_pmc * pmc )
{
of_at91_clk_prog_setup ( np , pmc , & at91sam9x5_programmable_layout ) ;
}