2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-09-15 20:15:53 +04:00
/*
* clk - h32mx . c
*
* Copyright ( C ) 2014 Atmel
*
* Alexandre Belloni < alexandre . belloni @ free - electrons . 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/regmap.h>
# include <linux/mfd/syscon.h>
2014-09-15 20:15:53 +04:00
# include "pmc.h"
# define H32MX_MAX_FREQ 90000000
struct clk_sama5d4_h32mx {
struct clk_hw hw ;
2014-09-07 10:14:29 +04:00
struct regmap * regmap ;
2014-09-15 20:15:53 +04:00
} ;
# define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)
static unsigned long clk_sama5d4_h32mx_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_sama5d4_h32mx * h32mxclk = to_clk_sama5d4_h32mx ( hw ) ;
2014-09-07 10:14:29 +04:00
unsigned int mckr ;
2014-09-15 20:15:53 +04:00
2014-09-07 10:14:29 +04:00
regmap_read ( h32mxclk - > regmap , AT91_PMC_MCKR , & mckr ) ;
if ( mckr & AT91_PMC_H32MXDIV )
2014-09-15 20:15:53 +04:00
return parent_rate / 2 ;
if ( parent_rate > H32MX_MAX_FREQ )
pr_warn ( " H32MX clock is too fast \n " ) ;
return parent_rate ;
}
static long clk_sama5d4_h32mx_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
unsigned long div ;
if ( rate > * parent_rate )
return * parent_rate ;
div = * parent_rate / 2 ;
if ( rate < div )
return div ;
if ( rate - div < * parent_rate - rate )
return div ;
return * parent_rate ;
}
static int clk_sama5d4_h32mx_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_sama5d4_h32mx * h32mxclk = to_clk_sama5d4_h32mx ( hw ) ;
2014-09-07 10:14:29 +04:00
u32 mckr = 0 ;
2014-09-15 20:15:53 +04:00
if ( parent_rate ! = rate & & ( parent_rate / 2 ) ! = rate )
return - EINVAL ;
if ( ( parent_rate / 2 ) = = rate )
2014-09-07 10:14:29 +04:00
mckr = AT91_PMC_H32MXDIV ;
regmap_update_bits ( h32mxclk - > regmap , AT91_PMC_MCKR ,
AT91_PMC_H32MXDIV , mckr ) ;
2014-09-15 20:15:53 +04:00
return 0 ;
}
static const struct clk_ops h32mx_ops = {
. recalc_rate = clk_sama5d4_h32mx_recalc_rate ,
. round_rate = clk_sama5d4_h32mx_round_rate ,
. set_rate = clk_sama5d4_h32mx_set_rate ,
} ;
2018-10-16 17:21:41 +03:00
struct clk_hw * __init
at91_clk_register_h32mx ( struct regmap * regmap , const char * name ,
const char * parent_name )
2014-09-15 20:15:53 +04:00
{
struct clk_sama5d4_h32mx * h32mxclk ;
struct clk_init_data init ;
2016-06-02 00:31:22 +03:00
int ret ;
2014-09-15 20:15:53 +04:00
h32mxclk = kzalloc ( sizeof ( * h32mxclk ) , GFP_KERNEL ) ;
if ( ! h32mxclk )
2018-10-16 17:21:41 +03:00
return ERR_PTR ( - ENOMEM ) ;
2014-09-15 20:15:53 +04:00
2018-10-16 17:21:41 +03:00
init . name = name ;
2014-09-15 20:15:53 +04:00
init . ops = & h32mx_ops ;
init . parent_names = parent_name ? & parent_name : NULL ;
init . num_parents = parent_name ? 1 : 0 ;
init . flags = CLK_SET_RATE_GATE ;
h32mxclk - > hw . init = & init ;
2014-09-07 10:14:29 +04:00
h32mxclk - > regmap = regmap ;
2014-09-15 20:15:53 +04:00
2016-06-02 00:31:22 +03:00
ret = clk_hw_register ( NULL , & h32mxclk - > hw ) ;
if ( ret ) {
2015-06-26 16:30:22 +03:00
kfree ( h32mxclk ) ;
2018-10-16 17:21:41 +03:00
return ERR_PTR ( ret ) ;
2015-06-26 16:30:22 +03:00
}
2014-09-15 20:15:53 +04:00
2018-10-16 17:21:41 +03:00
return & h32mxclk - > hw ;
}