2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-10-11 13:57:04 +04:00
/*
* Copyright ( C ) 2013 Boris BREZILLON < b . brezillon @ overkiz . com >
*/
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/clk/at91_pmc.h>
# include <linux/of.h>
2014-09-07 10:14:29 +04:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2013-10-11 13:57:04 +04:00
# include "pmc.h"
# define PROG_ID_MAX 7
# define PROG_STATUS_MASK(id) (1 << ((id) + 8))
2019-03-18 13:50:45 +03:00
# define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & layout->pres_mask)
2013-10-11 13:57:04 +04:00
# define PROG_MAX_RM9200_CSS 3
struct clk_programmable {
struct clk_hw hw ;
2014-09-07 10:14:29 +04:00
struct regmap * regmap ;
2013-10-11 13:57:04 +04:00
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 )
{
struct clk_programmable * prog = to_clk_programmable ( hw ) ;
2019-03-18 13:50:45 +03:00
const struct clk_programmable_layout * layout = prog - > layout ;
2014-09-07 10:14:29 +04:00
unsigned int pckr ;
2019-03-18 13:50:45 +03:00
unsigned long rate ;
2014-09-07 10:14:29 +04:00
regmap_read ( prog - > regmap , AT91_PMC_PCKR ( prog - > id ) , & pckr ) ;
2013-10-11 13:57:04 +04:00
2019-03-18 13:50:45 +03:00
if ( layout - > is_pres_direct )
rate = parent_rate / ( PROG_PRES ( layout , pckr ) + 1 ) ;
else
rate = parent_rate > > PROG_PRES ( layout , pckr ) ;
return rate ;
2013-10-11 13:57:04 +04:00
}
2015-07-07 21:48:08 +03:00
static int clk_programmable_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
2013-10-11 13:57:04 +04:00
{
2019-03-18 13:50:45 +03:00
struct clk_programmable * prog = to_clk_programmable ( hw ) ;
const struct clk_programmable_layout * layout = prog - > layout ;
2015-07-31 03:20:57 +03:00
struct clk_hw * parent ;
2014-03-11 13:00:32 +04:00
long best_rate = - EINVAL ;
unsigned long parent_rate ;
2019-03-18 13:50:45 +03:00
unsigned long tmp_rate = 0 ;
2014-03-11 13:00:32 +04:00
int shift ;
int i ;
2015-06-26 02:53:23 +03:00
for ( i = 0 ; i < clk_hw_get_num_parents ( hw ) ; i + + ) {
2015-07-31 03:20:57 +03:00
parent = clk_hw_get_parent_by_index ( hw , i ) ;
2014-03-11 13:00:32 +04:00
if ( ! parent )
continue ;
2015-07-31 03:20:57 +03:00
parent_rate = clk_hw_get_rate ( parent ) ;
2019-03-18 13:50:45 +03:00
if ( layout - > is_pres_direct ) {
for ( shift = 0 ; shift < = layout - > pres_mask ; shift + + ) {
tmp_rate = parent_rate / ( shift + 1 ) ;
if ( tmp_rate < = req - > rate )
break ;
}
} else {
for ( shift = 0 ; shift < layout - > pres_mask ; shift + + ) {
tmp_rate = parent_rate > > shift ;
if ( tmp_rate < = req - > rate )
break ;
}
2014-03-11 13:00:32 +04:00
}
2013-10-11 13:57:04 +04:00
2015-07-07 21:48:08 +03:00
if ( tmp_rate > req - > rate )
2014-03-11 13:00:32 +04:00
continue ;
2013-10-11 13:57:04 +04:00
2015-07-07 21:48:08 +03:00
if ( best_rate < 0 | |
( req - > rate - tmp_rate ) < ( req - > rate - best_rate ) ) {
2014-03-11 13:00:32 +04:00
best_rate = tmp_rate ;
2015-07-07 21:48:08 +03:00
req - > best_parent_rate = parent_rate ;
2015-07-31 03:20:57 +03:00
req - > best_parent_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 ;
}
2015-07-07 21:48:08 +03:00
if ( best_rate < 0 )
return best_rate ;
req - > rate = best_rate ;
return 0 ;
2013-10-11 13:57:04 +04:00
}
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-09-07 10:14:29 +04:00
unsigned int mask = layout - > css_mask ;
2016-07-18 10:49:12 +03:00
unsigned int pckr = index ;
2014-03-11 13:00:34 +04:00
if ( layout - > have_slck_mck )
2014-09-07 10:14:29 +04:00
mask | = AT91_PMC_CSSMCK_MCK ;
2014-03-11 13:00:34 +04:00
2013-10-11 13:57:04 +04:00
if ( index > layout - > css_mask ) {
2014-09-07 10:14:29 +04:00
if ( index > PROG_MAX_RM9200_CSS & & ! layout - > have_slck_mck )
2013-10-11 13:57:04 +04:00
return - EINVAL ;
2014-09-07 10:14:29 +04:00
pckr | = AT91_PMC_CSSMCK_MCK ;
2013-10-11 13:57:04 +04:00
}
2014-09-07 10:14:29 +04:00
regmap_update_bits ( prog - > regmap , AT91_PMC_PCKR ( prog - > id ) , mask , pckr ) ;
2013-10-11 13:57:04 +04:00
return 0 ;
}
static u8 clk_programmable_get_parent ( struct clk_hw * hw )
{
struct clk_programmable * prog = to_clk_programmable ( hw ) ;
const struct clk_programmable_layout * layout = prog - > layout ;
2014-09-07 10:14:29 +04:00
unsigned int pckr ;
u8 ret ;
regmap_read ( prog - > regmap , AT91_PMC_PCKR ( prog - > id ) , & pckr ) ;
ret = pckr & layout - > css_mask ;
2013-10-11 13:57:04 +04:00
2014-09-07 10:14:29 +04:00
if ( layout - > have_slck_mck & & ( pckr & AT91_PMC_CSSMCK_MCK ) & & ! ret )
2014-03-11 13:00:34 +04:00
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
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-09-07 10:14:29 +04:00
2014-03-11 13:00:35 +04:00
if ( ! div )
return - EINVAL ;
2013-10-11 13:57:04 +04:00
2019-03-18 13:50:45 +03:00
if ( layout - > is_pres_direct ) {
shift = div - 1 ;
2013-10-11 13:57:04 +04:00
2019-03-18 13:50:45 +03:00
if ( shift > layout - > pres_mask )
return - EINVAL ;
} else {
shift = fls ( div ) - 1 ;
2013-10-11 13:57:04 +04:00
2019-03-18 13:50:45 +03:00
if ( div ! = ( 1 < < shift ) )
return - EINVAL ;
if ( shift > = layout - > pres_mask )
return - EINVAL ;
}
2013-10-11 13:57:04 +04:00
2014-09-07 10:14:29 +04:00
regmap_update_bits ( prog - > regmap , AT91_PMC_PCKR ( prog - > id ) ,
2019-03-18 13:50:45 +03:00
layout - > pres_mask < < layout - > pres_shift ,
2014-09-07 10:14:29 +04:00
shift < < layout - > pres_shift ) ;
2014-03-11 13:00:34 +04:00
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 ,
} ;
2018-10-16 17:21:44 +03:00
struct clk_hw * __init
2014-09-07 10:14:29 +04:00
at91_clk_register_programmable ( struct regmap * regmap ,
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 ;
2016-06-02 00:31:22 +03:00
struct clk_hw * hw ;
2013-10-11 13:57:04 +04:00
struct clk_init_data init ;
2016-06-02 00:31:22 +03:00
int ret ;
2013-10-11 13:57:04 +04:00
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 ;
2014-09-07 10:14:29 +04:00
prog - > regmap = regmap ;
2013-10-11 13:57:04 +04:00
2016-06-02 00:31:22 +03:00
hw = & prog - > hw ;
ret = clk_hw_register ( NULL , & prog - > hw ) ;
if ( ret ) {
2013-10-11 13:57:04 +04:00
kfree ( prog ) ;
2016-09-25 14:53:58 +03:00
hw = ERR_PTR ( ret ) ;
2017-12-11 19:55:35 +03:00
} else {
pmc_register_pck ( id ) ;
2016-06-02 00:31:22 +03:00
}
2013-10-11 13:57:04 +04:00
2016-06-02 00:31:22 +03:00
return hw ;
2013-10-11 13:57:04 +04:00
}
2018-10-16 17:21:44 +03:00
const struct clk_programmable_layout at91rm9200_programmable_layout = {
2019-03-18 13:50:45 +03:00
. pres_mask = 0x7 ,
2013-10-11 13:57:04 +04:00
. pres_shift = 2 ,
. css_mask = 0x3 ,
. have_slck_mck = 0 ,
2019-03-18 13:50:45 +03:00
. is_pres_direct = 0 ,
2013-10-11 13:57:04 +04:00
} ;
2018-10-16 17:21:44 +03:00
const struct clk_programmable_layout at91sam9g45_programmable_layout = {
2019-03-18 13:50:45 +03:00
. pres_mask = 0x7 ,
2013-10-11 13:57:04 +04:00
. pres_shift = 2 ,
. css_mask = 0x3 ,
. have_slck_mck = 1 ,
2019-03-18 13:50:45 +03:00
. is_pres_direct = 0 ,
2013-10-11 13:57:04 +04:00
} ;
2018-10-16 17:21:44 +03:00
const struct clk_programmable_layout at91sam9x5_programmable_layout = {
2019-03-18 13:50:45 +03:00
. pres_mask = 0x7 ,
2013-10-11 13:57:04 +04:00
. pres_shift = 4 ,
. css_mask = 0x7 ,
. have_slck_mck = 0 ,
2019-03-18 13:50:45 +03:00
. is_pres_direct = 0 ,
2013-10-11 13:57:04 +04:00
} ;