2013-10-11 13:44:36 +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>
2014-09-07 10:14:29 +04:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2013-10-11 13:44:36 +04:00
# include "pmc.h"
2014-09-07 10:14:29 +04:00
DEFINE_SPINLOCK ( pmc_pcr_lock ) ;
2013-10-11 13:44:36 +04:00
# define PERIPHERAL_MAX 64
# define PERIPHERAL_AT91RM9200 0
# define PERIPHERAL_AT91SAM9X5 1
# define PERIPHERAL_ID_MIN 2
# define PERIPHERAL_ID_MAX 31
# define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
# define PERIPHERAL_RSHIFT_MASK 0x3
# define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
2015-05-28 15:01:08 +03:00
# define PERIPHERAL_MAX_SHIFT 3
2013-10-11 13:44:36 +04:00
struct clk_peripheral {
struct clk_hw hw ;
2014-09-07 10:14:29 +04:00
struct regmap * regmap ;
2013-10-11 13:44:36 +04:00
u32 id ;
} ;
# define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
struct clk_sam9x5_peripheral {
struct clk_hw hw ;
2014-09-07 10:14:29 +04:00
struct regmap * regmap ;
2013-10-11 13:44:36 +04:00
struct clk_range range ;
2014-09-07 10:14:29 +04:00
spinlock_t * lock ;
2013-10-11 13:44:36 +04:00
u32 id ;
u32 div ;
bool auto_div ;
} ;
# define to_clk_sam9x5_peripheral(hw) \
container_of ( hw , struct clk_sam9x5_peripheral , hw )
static int clk_peripheral_enable ( struct clk_hw * hw )
{
struct clk_peripheral * periph = to_clk_peripheral ( hw ) ;
int offset = AT91_PMC_PCER ;
u32 id = periph - > id ;
if ( id < PERIPHERAL_ID_MIN )
return 0 ;
if ( id > PERIPHERAL_ID_MAX )
offset = AT91_PMC_PCER1 ;
2014-09-07 10:14:29 +04:00
regmap_write ( periph - > regmap , offset , PERIPHERAL_MASK ( id ) ) ;
2013-10-11 13:44:36 +04:00
return 0 ;
}
static void clk_peripheral_disable ( struct clk_hw * hw )
{
struct clk_peripheral * periph = to_clk_peripheral ( hw ) ;
int offset = AT91_PMC_PCDR ;
u32 id = periph - > id ;
if ( id < PERIPHERAL_ID_MIN )
return ;
if ( id > PERIPHERAL_ID_MAX )
offset = AT91_PMC_PCDR1 ;
2014-09-07 10:14:29 +04:00
regmap_write ( periph - > regmap , offset , PERIPHERAL_MASK ( id ) ) ;
2013-10-11 13:44:36 +04:00
}
static int clk_peripheral_is_enabled ( struct clk_hw * hw )
{
struct clk_peripheral * periph = to_clk_peripheral ( hw ) ;
int offset = AT91_PMC_PCSR ;
2014-09-07 10:14:29 +04:00
unsigned int status ;
2013-10-11 13:44:36 +04:00
u32 id = periph - > id ;
if ( id < PERIPHERAL_ID_MIN )
return 1 ;
if ( id > PERIPHERAL_ID_MAX )
offset = AT91_PMC_PCSR1 ;
2014-09-07 10:14:29 +04:00
regmap_read ( periph - > regmap , offset , & status ) ;
return status & PERIPHERAL_MASK ( id ) ? 1 : 0 ;
2013-10-11 13:44:36 +04:00
}
static const struct clk_ops peripheral_ops = {
. enable = clk_peripheral_enable ,
. disable = clk_peripheral_disable ,
. is_enabled = clk_peripheral_is_enabled ,
} ;
2016-06-02 00:31:22 +03:00
static struct clk_hw * __init
2014-09-07 10:14:29 +04:00
at91_clk_register_peripheral ( struct regmap * regmap , const char * name ,
2013-10-11 13:44:36 +04:00
const char * parent_name , u32 id )
{
struct clk_peripheral * periph ;
struct clk_init_data init ;
2016-06-02 00:31:22 +03:00
struct clk_hw * hw ;
int ret ;
2013-10-11 13:44:36 +04:00
2014-09-07 10:14:29 +04:00
if ( ! name | | ! parent_name | | id > PERIPHERAL_ID_MAX )
2013-10-11 13:44:36 +04:00
return ERR_PTR ( - EINVAL ) ;
periph = kzalloc ( sizeof ( * periph ) , GFP_KERNEL ) ;
if ( ! periph )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & peripheral_ops ;
init . parent_names = ( parent_name ? & parent_name : NULL ) ;
init . num_parents = ( parent_name ? 1 : 0 ) ;
init . flags = 0 ;
periph - > id = id ;
periph - > hw . init = & init ;
2014-09-07 10:14:29 +04:00
periph - > regmap = regmap ;
2013-10-11 13:44:36 +04:00
2016-06-02 00:31:22 +03:00
hw = & periph - > hw ;
ret = clk_hw_register ( NULL , & periph - > hw ) ;
if ( ret ) {
2013-10-11 13:44:36 +04:00
kfree ( periph ) ;
2016-06-02 00:31:22 +03:00
hw = ERR_PTR ( ret ) ;
}
2013-10-11 13:44:36 +04:00
2016-06-02 00:31:22 +03:00
return hw ;
2013-10-11 13:44:36 +04:00
}
static void clk_sam9x5_peripheral_autodiv ( struct clk_sam9x5_peripheral * periph )
{
2015-07-31 03:20:57 +03:00
struct clk_hw * parent ;
2013-10-11 13:44:36 +04:00
unsigned long parent_rate ;
int shift = 0 ;
if ( ! periph - > auto_div )
return ;
if ( periph - > range . max ) {
2015-07-31 03:20:57 +03:00
parent = clk_hw_get_parent_by_index ( & periph - > hw , 0 ) ;
parent_rate = clk_hw_get_rate ( parent ) ;
2013-10-11 13:44:36 +04:00
if ( ! parent_rate )
return ;
for ( ; shift < PERIPHERAL_MAX_SHIFT ; shift + + ) {
if ( parent_rate > > shift < = periph - > range . max )
break ;
}
}
periph - > auto_div = false ;
periph - > div = shift ;
}
static int clk_sam9x5_peripheral_enable ( struct clk_hw * hw )
{
struct clk_sam9x5_peripheral * periph = to_clk_sam9x5_peripheral ( hw ) ;
2014-09-07 10:14:29 +04:00
unsigned long flags ;
2013-10-11 13:44:36 +04:00
if ( periph - > id < PERIPHERAL_ID_MIN )
return 0 ;
2014-09-07 10:14:29 +04:00
spin_lock_irqsave ( periph - > lock , flags ) ;
regmap_write ( periph - > regmap , AT91_PMC_PCR ,
( periph - > id & AT91_PMC_PCR_PID_MASK ) ) ;
regmap_update_bits ( periph - > regmap , AT91_PMC_PCR ,
AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
AT91_PMC_PCR_EN ,
AT91_PMC_PCR_DIV ( periph - > div ) |
AT91_PMC_PCR_CMD |
AT91_PMC_PCR_EN ) ;
spin_unlock_irqrestore ( periph - > lock , flags ) ;
2013-10-11 13:44:36 +04:00
return 0 ;
}
static void clk_sam9x5_peripheral_disable ( struct clk_hw * hw )
{
struct clk_sam9x5_peripheral * periph = to_clk_sam9x5_peripheral ( hw ) ;
2014-09-07 10:14:29 +04:00
unsigned long flags ;
2013-10-11 13:44:36 +04:00
if ( periph - > id < PERIPHERAL_ID_MIN )
return ;
2014-09-07 10:14:29 +04:00
spin_lock_irqsave ( periph - > lock , flags ) ;
regmap_write ( periph - > regmap , AT91_PMC_PCR ,
( periph - > id & AT91_PMC_PCR_PID_MASK ) ) ;
regmap_update_bits ( periph - > regmap , AT91_PMC_PCR ,
AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD ,
AT91_PMC_PCR_CMD ) ;
spin_unlock_irqrestore ( periph - > lock , flags ) ;
2013-10-11 13:44:36 +04:00
}
static int clk_sam9x5_peripheral_is_enabled ( struct clk_hw * hw )
{
struct clk_sam9x5_peripheral * periph = to_clk_sam9x5_peripheral ( hw ) ;
2014-09-07 10:14:29 +04:00
unsigned long flags ;
unsigned int status ;
2013-10-11 13:44:36 +04:00
if ( periph - > id < PERIPHERAL_ID_MIN )
return 1 ;
2014-09-07 10:14:29 +04:00
spin_lock_irqsave ( periph - > lock , flags ) ;
regmap_write ( periph - > regmap , AT91_PMC_PCR ,
( periph - > id & AT91_PMC_PCR_PID_MASK ) ) ;
regmap_read ( periph - > regmap , AT91_PMC_PCR , & status ) ;
spin_unlock_irqrestore ( periph - > lock , flags ) ;
2013-10-11 13:44:36 +04:00
2014-09-07 10:14:29 +04:00
return status & AT91_PMC_PCR_EN ? 1 : 0 ;
2013-10-11 13:44:36 +04:00
}
static unsigned long
clk_sam9x5_peripheral_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_sam9x5_peripheral * periph = to_clk_sam9x5_peripheral ( hw ) ;
2014-09-07 10:14:29 +04:00
unsigned long flags ;
unsigned int status ;
2013-10-11 13:44:36 +04:00
if ( periph - > id < PERIPHERAL_ID_MIN )
return parent_rate ;
2014-09-07 10:14:29 +04:00
spin_lock_irqsave ( periph - > lock , flags ) ;
regmap_write ( periph - > regmap , AT91_PMC_PCR ,
( periph - > id & AT91_PMC_PCR_PID_MASK ) ) ;
regmap_read ( periph - > regmap , AT91_PMC_PCR , & status ) ;
spin_unlock_irqrestore ( periph - > lock , flags ) ;
2013-10-11 13:44:36 +04:00
2014-09-07 10:14:29 +04:00
if ( status & AT91_PMC_PCR_EN ) {
periph - > div = PERIPHERAL_RSHIFT ( status ) ;
2013-10-11 13:44:36 +04:00
periph - > auto_div = false ;
} else {
clk_sam9x5_peripheral_autodiv ( periph ) ;
}
return parent_rate > > periph - > div ;
}
static long clk_sam9x5_peripheral_round_rate ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long * parent_rate )
{
int shift = 0 ;
unsigned long best_rate ;
unsigned long best_diff ;
unsigned long cur_rate = * parent_rate ;
unsigned long cur_diff ;
struct clk_sam9x5_peripheral * periph = to_clk_sam9x5_peripheral ( hw ) ;
if ( periph - > id < PERIPHERAL_ID_MIN | | ! periph - > range . max )
return * parent_rate ;
if ( periph - > range . max ) {
2015-05-28 15:01:08 +03:00
for ( ; shift < = PERIPHERAL_MAX_SHIFT ; shift + + ) {
2013-10-11 13:44:36 +04:00
cur_rate = * parent_rate > > shift ;
if ( cur_rate < = periph - > range . max )
break ;
}
}
if ( rate > = cur_rate )
return cur_rate ;
best_diff = cur_rate - rate ;
best_rate = cur_rate ;
2015-05-28 15:01:08 +03:00
for ( ; shift < = PERIPHERAL_MAX_SHIFT ; shift + + ) {
2013-10-11 13:44:36 +04:00
cur_rate = * parent_rate > > shift ;
if ( cur_rate < rate )
cur_diff = rate - cur_rate ;
else
cur_diff = cur_rate - rate ;
if ( cur_diff < best_diff ) {
best_diff = cur_diff ;
best_rate = cur_rate ;
}
if ( ! best_diff | | cur_rate < rate )
break ;
}
return best_rate ;
}
static int clk_sam9x5_peripheral_set_rate ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long parent_rate )
{
int shift ;
struct clk_sam9x5_peripheral * periph = to_clk_sam9x5_peripheral ( hw ) ;
if ( periph - > id < PERIPHERAL_ID_MIN | | ! periph - > range . max ) {
if ( parent_rate = = rate )
return 0 ;
else
return - EINVAL ;
}
if ( periph - > range . max & & rate > periph - > range . max )
return - EINVAL ;
2015-05-28 15:01:08 +03:00
for ( shift = 0 ; shift < = PERIPHERAL_MAX_SHIFT ; shift + + ) {
2013-10-11 13:44:36 +04:00
if ( parent_rate > > shift = = rate ) {
periph - > auto_div = false ;
periph - > div = shift ;
return 0 ;
}
}
return - EINVAL ;
}
static const struct clk_ops sam9x5_peripheral_ops = {
. enable = clk_sam9x5_peripheral_enable ,
. disable = clk_sam9x5_peripheral_disable ,
. is_enabled = clk_sam9x5_peripheral_is_enabled ,
. recalc_rate = clk_sam9x5_peripheral_recalc_rate ,
. round_rate = clk_sam9x5_peripheral_round_rate ,
. set_rate = clk_sam9x5_peripheral_set_rate ,
} ;
2016-06-02 00:31:22 +03:00
static struct clk_hw * __init
2014-09-07 10:14:29 +04:00
at91_clk_register_sam9x5_peripheral ( struct regmap * regmap , spinlock_t * lock ,
const char * name , const char * parent_name ,
u32 id , const struct clk_range * range )
2013-10-11 13:44:36 +04:00
{
struct clk_sam9x5_peripheral * periph ;
struct clk_init_data init ;
2016-06-02 00:31:22 +03:00
struct clk_hw * hw ;
int ret ;
2013-10-11 13:44:36 +04:00
2014-09-07 10:14:29 +04:00
if ( ! name | | ! parent_name )
2013-10-11 13:44:36 +04:00
return ERR_PTR ( - EINVAL ) ;
periph = kzalloc ( sizeof ( * periph ) , GFP_KERNEL ) ;
if ( ! periph )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & sam9x5_peripheral_ops ;
init . parent_names = ( parent_name ? & parent_name : NULL ) ;
init . num_parents = ( parent_name ? 1 : 0 ) ;
init . flags = 0 ;
periph - > id = id ;
periph - > hw . init = & init ;
periph - > div = 0 ;
2014-09-07 10:14:29 +04:00
periph - > regmap = regmap ;
periph - > lock = lock ;
2013-10-11 13:44:36 +04:00
periph - > auto_div = true ;
periph - > range = * range ;
2016-06-02 00:31:22 +03:00
hw = & periph - > hw ;
ret = clk_hw_register ( NULL , & periph - > hw ) ;
if ( ret ) {
2013-10-11 13:44:36 +04:00
kfree ( periph ) ;
2016-06-02 00:31:22 +03:00
hw = ERR_PTR ( ret ) ;
2017-06-08 03:36:47 +03:00
} else {
2013-10-11 13:44:36 +04:00
clk_sam9x5_peripheral_autodiv ( periph ) ;
2017-06-08 03:36:47 +03:00
pmc_register_id ( id ) ;
}
2013-10-11 13:44:36 +04:00
2016-06-02 00:31:22 +03:00
return hw ;
2013-10-11 13:44:36 +04:00
}
static void __init
2014-09-07 10:14:29 +04:00
of_at91_clk_periph_setup ( struct device_node * np , u8 type )
2013-10-11 13:44:36 +04:00
{
int num ;
u32 id ;
2016-06-02 00:31:22 +03:00
struct clk_hw * hw ;
2013-10-11 13:44:36 +04:00
const char * parent_name ;
const char * name ;
struct device_node * periphclknp ;
2014-09-07 10:14:29 +04:00
struct regmap * regmap ;
2013-10-11 13:44:36 +04:00
parent_name = of_clk_get_parent_name ( np , 0 ) ;
if ( ! parent_name )
return ;
num = of_get_child_count ( np ) ;
if ( ! num | | num > PERIPHERAL_MAX )
return ;
2014-09-07 10:14:29 +04:00
regmap = syscon_node_to_regmap ( of_get_parent ( np ) ) ;
if ( IS_ERR ( regmap ) )
return ;
2013-10-11 13:44:36 +04:00
for_each_child_of_node ( np , periphclknp ) {
if ( of_property_read_u32 ( periphclknp , " reg " , & id ) )
continue ;
if ( id > = PERIPHERAL_MAX )
continue ;
if ( of_property_read_string ( np , " clock-output-names " , & name ) )
name = periphclknp - > name ;
if ( type = = PERIPHERAL_AT91RM9200 ) {
2016-06-02 00:31:22 +03:00
hw = at91_clk_register_peripheral ( regmap , name ,
2013-10-11 13:44:36 +04:00
parent_name , id ) ;
} else {
struct clk_range range = CLK_RANGE ( 0 , 0 ) ;
of_at91_get_clk_range ( periphclknp ,
" atmel,clk-output-range " ,
& range ) ;
2016-06-02 00:31:22 +03:00
hw = at91_clk_register_sam9x5_peripheral ( regmap ,
2014-09-07 10:14:29 +04:00
& pmc_pcr_lock ,
name ,
2013-10-11 13:44:36 +04:00
parent_name ,
id , & range ) ;
}
2016-06-02 00:31:22 +03:00
if ( IS_ERR ( hw ) )
2013-10-11 13:44:36 +04:00
continue ;
2016-06-02 00:31:22 +03:00
of_clk_add_hw_provider ( periphclknp , of_clk_hw_simple_get , hw ) ;
2013-10-11 13:44:36 +04:00
}
}
2014-09-07 10:14:29 +04:00
static void __init of_at91rm9200_clk_periph_setup ( struct device_node * np )
2013-10-11 13:44:36 +04:00
{
2014-09-07 10:14:29 +04:00
of_at91_clk_periph_setup ( np , PERIPHERAL_AT91RM9200 ) ;
2013-10-11 13:44:36 +04:00
}
2014-09-07 10:14:29 +04:00
CLK_OF_DECLARE ( at91rm9200_clk_periph , " atmel,at91rm9200-clk-peripheral " ,
of_at91rm9200_clk_periph_setup ) ;
2013-10-11 13:44:36 +04:00
2014-09-07 10:14:29 +04:00
static void __init of_at91sam9x5_clk_periph_setup ( struct device_node * np )
2013-10-11 13:44:36 +04:00
{
2014-09-07 10:14:29 +04:00
of_at91_clk_periph_setup ( np , PERIPHERAL_AT91SAM9X5 ) ;
2013-10-11 13:44:36 +04:00
}
2014-09-07 10:14:29 +04:00
CLK_OF_DECLARE ( at91sam9x5_clk_periph , " atmel,at91sam9x5-clk-peripheral " ,
of_at91sam9x5_clk_periph_setup ) ;