2019-04-02 14:50:54 +02:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright ( C ) 2019 Microchip Technology Inc .
*
*/
# include <linux/bitfield.h>
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/clk/at91_pmc.h>
# include <linux/of.h>
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
# include "pmc.h"
2020-01-20 14:10:06 +02:00
# define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
2020-07-22 10:38:11 +03:00
# define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
2020-07-22 10:38:14 +03:00
# define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
2019-04-02 14:50:54 +02:00
# define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
# define UPLL_DIV 2
# define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
2020-07-22 10:38:13 +03:00
# define FCORE_MIN (600000000)
# define FCORE_MAX (1200000000)
2020-07-22 10:38:24 +03:00
# define PLL_MAX_ID 7
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
struct sam9x60_pll_core {
2019-04-02 14:50:54 +02:00
struct regmap * regmap ;
spinlock_t * lock ;
const struct clk_pll_characteristics * characteristics ;
2020-07-22 10:38:24 +03:00
const struct clk_pll_layout * layout ;
struct clk_hw hw ;
2019-04-02 14:50:54 +02:00
u8 id ;
2020-07-22 10:38:24 +03:00
} ;
struct sam9x60_frac {
struct sam9x60_pll_core core ;
u32 frac ;
2019-04-02 14:50:54 +02:00
u16 mul ;
} ;
2020-07-22 10:38:24 +03:00
struct sam9x60_div {
struct sam9x60_pll_core core ;
u8 div ;
} ;
# define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
# define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
# define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
2019-04-02 14:50:54 +02:00
static inline bool sam9x60_pll_ready ( struct regmap * regmap , int id )
{
unsigned int status ;
2020-01-20 14:10:06 +02:00
regmap_read ( regmap , AT91_PMC_PLL_ISR0 , & status ) ;
2019-04-02 14:50:54 +02:00
return ! ! ( status & BIT ( id ) ) ;
}
2020-07-22 10:38:24 +03:00
static bool sam9x60_frac_pll_ready ( struct regmap * regmap , u8 id )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
return sam9x60_pll_ready ( regmap , id ) ;
}
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
static unsigned long sam9x60_frac_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct sam9x60_frac * frac = to_sam9x60_frac ( core ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return ( parent_rate * ( frac - > mul + 1 ) +
( ( u64 ) parent_rate * frac - > frac > > 22 ) ) ;
}
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
static int sam9x60_frac_pll_prepare ( struct clk_hw * hw )
{
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct sam9x60_frac * frac = to_sam9x60_frac ( core ) ;
struct regmap * regmap = core - > regmap ;
unsigned int val , cfrac , cmul ;
unsigned long flags ;
spin_lock_irqsave ( core - > lock , flags ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_ID_MSK , core - > id ) ;
2020-01-20 14:10:06 +02:00
regmap_read ( regmap , AT91_PMC_PLL_CTRL1 , & val ) ;
2020-07-22 10:38:24 +03:00
cmul = ( val & core - > layout - > mul_mask ) > > core - > layout - > mul_shift ;
cfrac = ( val & core - > layout - > frac_mask ) > > core - > layout - > frac_shift ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
if ( sam9x60_frac_pll_ready ( regmap , core - > id ) & &
( cmul = = frac - > mul & & cfrac = = frac - > frac ) )
goto unlock ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
/* Recommended value for PMC_PLL_ACR */
if ( core - > characteristics - > upll )
2020-01-20 14:10:06 +02:00
val = AT91_PMC_PLL_ACR_DEFAULT_UPLL ;
2019-11-11 13:28:57 +00:00
else
2020-01-20 14:10:06 +02:00
val = AT91_PMC_PLL_ACR_DEFAULT_PLLA ;
regmap_write ( regmap , AT91_PMC_PLL_ACR , val ) ;
2019-04-02 14:50:54 +02:00
2020-01-20 14:10:06 +02:00
regmap_write ( regmap , AT91_PMC_PLL_CTRL1 ,
2020-07-22 10:38:24 +03:00
( frac - > mul < < core - > layout - > mul_shift ) |
( frac - > frac < < core - > layout - > frac_shift ) ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
if ( core - > characteristics - > upll ) {
2019-04-02 14:50:54 +02:00
/* Enable the UTMI internal bandgap */
2020-01-20 14:10:06 +02:00
val | = AT91_PMC_PLL_ACR_UTMIBG ;
regmap_write ( regmap , AT91_PMC_PLL_ACR , val ) ;
2019-04-02 14:50:54 +02:00
udelay ( 10 ) ;
/* Enable the UTMI internal regulator */
2020-01-20 14:10:06 +02:00
val | = AT91_PMC_PLL_ACR_UTMIVR ;
regmap_write ( regmap , AT91_PMC_PLL_ACR , val ) ;
2019-04-02 14:50:54 +02:00
udelay ( 10 ) ;
}
2020-01-20 14:10:06 +02:00
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
2020-07-22 10:38:24 +03:00
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK ,
AT91_PMC_PLL_UPDT_UPDATE | core - > id ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
regmap_update_bits ( regmap , AT91_PMC_PLL_CTRL0 ,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL ,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL ) ;
2019-04-02 14:50:54 +02:00
2020-01-20 14:10:06 +02:00
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
2020-07-22 10:38:24 +03:00
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK ,
AT91_PMC_PLL_UPDT_UPDATE | core - > id ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
while ( ! sam9x60_pll_ready ( regmap , core - > id ) )
2019-04-02 14:50:54 +02:00
cpu_relax ( ) ;
2020-07-22 10:38:24 +03:00
unlock :
spin_unlock_irqrestore ( core - > lock , flags ) ;
2019-04-02 14:50:54 +02:00
return 0 ;
}
2020-07-22 10:38:24 +03:00
static void sam9x60_frac_pll_unprepare ( struct clk_hw * hw )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct regmap * regmap = core - > regmap ;
unsigned long flags ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
spin_lock_irqsave ( core - > lock , flags ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_ID_MSK , core - > id ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_CTRL0 , AT91_PMC_PLL_CTRL0_ENPLL , 0 ) ;
if ( core - > characteristics - > upll )
regmap_update_bits ( regmap , AT91_PMC_PLL_ACR ,
AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR , 0 ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK ,
AT91_PMC_PLL_UPDT_UPDATE | core - > id ) ;
spin_unlock_irqrestore ( core - > lock , flags ) ;
2019-04-02 14:50:54 +02:00
}
2020-07-22 10:38:24 +03:00
static int sam9x60_frac_pll_is_prepared ( struct clk_hw * hw )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
return sam9x60_pll_ready ( core - > regmap , core - > id ) ;
}
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
static long sam9x60_frac_pll_compute_mul_frac ( struct sam9x60_pll_core * core ,
unsigned long rate ,
unsigned long parent_rate ,
bool update )
{
struct sam9x60_frac * frac = to_sam9x60_frac ( core ) ;
unsigned long tmprate , remainder ;
unsigned long nmul = 0 ;
unsigned long nfrac = 0 ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
if ( rate < FCORE_MIN | | rate > FCORE_MAX )
return - ERANGE ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
/*
* Calculate the multiplier associated with the current
* divider that provide the closest rate to the requested one .
*/
nmul = mult_frac ( rate , 1 , parent_rate ) ;
tmprate = mult_frac ( parent_rate , nmul , 1 ) ;
remainder = rate - tmprate ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
if ( remainder ) {
nfrac = DIV_ROUND_CLOSEST_ULL ( ( u64 ) remainder * ( 1 < < 22 ) ,
parent_rate ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
tmprate + = DIV_ROUND_CLOSEST_ULL ( ( u64 ) nfrac * parent_rate ,
( 1 < < 22 ) ) ;
}
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
/* Check if resulted rate is a valid. */
if ( tmprate < FCORE_MIN | | tmprate > FCORE_MAX )
return - ERANGE ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
if ( update ) {
frac - > mul = nmul - 1 ;
frac - > frac = nfrac ;
}
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return tmprate ;
2019-04-02 14:50:54 +02:00
}
2020-07-22 10:38:24 +03:00
static long sam9x60_frac_pll_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return sam9x60_frac_pll_compute_mul_frac ( core , rate , * parent_rate , false ) ;
2019-04-02 14:50:54 +02:00
}
2020-07-22 10:38:24 +03:00
static int sam9x60_frac_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return sam9x60_frac_pll_compute_mul_frac ( core , rate , parent_rate , true ) ;
}
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
static const struct clk_ops sam9x60_frac_pll_ops = {
. prepare = sam9x60_frac_pll_prepare ,
. unprepare = sam9x60_frac_pll_unprepare ,
. is_prepared = sam9x60_frac_pll_is_prepared ,
. recalc_rate = sam9x60_frac_pll_recalc_rate ,
. round_rate = sam9x60_frac_pll_round_rate ,
. set_rate = sam9x60_frac_pll_set_rate ,
} ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
static int sam9x60_div_pll_prepare ( struct clk_hw * hw )
{
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct sam9x60_div * div = to_sam9x60_div ( core ) ;
struct regmap * regmap = core - > regmap ;
unsigned long flags ;
unsigned int val , cdiv ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
spin_lock_irqsave ( core - > lock , flags ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_ID_MSK , core - > id ) ;
regmap_read ( regmap , AT91_PMC_PLL_CTRL0 , & val ) ;
cdiv = ( val & core - > layout - > div_mask ) > > core - > layout - > div_shift ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
/* Stop if enabled an nothing changed. */
if ( ! ! ( val & core - > layout - > endiv_mask ) & & cdiv = = div - > div )
goto unlock ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
regmap_update_bits ( regmap , AT91_PMC_PLL_CTRL0 ,
core - > layout - > div_mask | core - > layout - > endiv_mask ,
( div - > div < < core - > layout - > div_shift ) |
( 1 < < core - > layout - > endiv_shift ) ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK ,
AT91_PMC_PLL_UPDT_UPDATE | core - > id ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
while ( ! sam9x60_pll_ready ( regmap , core - > id ) )
cpu_relax ( ) ;
unlock :
spin_unlock_irqrestore ( core - > lock , flags ) ;
return 0 ;
}
static void sam9x60_div_pll_unprepare ( struct clk_hw * hw )
{
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct regmap * regmap = core - > regmap ;
unsigned long flags ;
spin_lock_irqsave ( core - > lock , flags ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_ID_MSK , core - > id ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_CTRL0 ,
core - > layout - > endiv_mask , 0 ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK ,
AT91_PMC_PLL_UPDT_UPDATE | core - > id ) ;
spin_unlock_irqrestore ( core - > lock , flags ) ;
}
static int sam9x60_div_pll_is_prepared ( struct clk_hw * hw )
{
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct regmap * regmap = core - > regmap ;
unsigned long flags ;
unsigned int val ;
spin_lock_irqsave ( core - > lock , flags ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_ID_MSK , core - > id ) ;
regmap_read ( regmap , AT91_PMC_PLL_CTRL0 , & val ) ;
spin_unlock_irqrestore ( core - > lock , flags ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return ! ! ( val & core - > layout - > endiv_mask ) ;
}
static unsigned long sam9x60_div_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct sam9x60_div * div = to_sam9x60_div ( core ) ;
return DIV_ROUND_CLOSEST_ULL ( parent_rate , ( div - > div + 1 ) ) ;
}
static long sam9x60_div_pll_compute_div ( struct sam9x60_pll_core * core ,
unsigned long * parent_rate ,
unsigned long rate )
{
const struct clk_pll_characteristics * characteristics =
core - > characteristics ;
struct clk_hw * parent = clk_hw_get_parent ( & core - > hw ) ;
unsigned long tmp_rate , tmp_parent_rate , tmp_diff ;
long best_diff = - 1 , best_rate = - EINVAL ;
2020-08-25 09:59:11 +03:00
u32 divid ;
2020-07-22 10:38:24 +03:00
if ( ! rate )
return 0 ;
if ( rate < characteristics - > output [ 0 ] . min | |
rate > characteristics - > output [ 0 ] . max )
return - ERANGE ;
for ( divid = 1 ; divid < core - > layout - > div_mask ; divid + + ) {
tmp_parent_rate = clk_hw_round_rate ( parent , rate * divid ) ;
if ( ! tmp_parent_rate )
2020-07-22 10:38:13 +03:00
continue ;
2020-07-22 10:38:24 +03:00
tmp_rate = DIV_ROUND_CLOSEST_ULL ( tmp_parent_rate , divid ) ;
tmp_diff = abs ( rate - tmp_rate ) ;
if ( best_diff < 0 | | best_diff > tmp_diff ) {
* parent_rate = tmp_parent_rate ;
best_rate = tmp_rate ;
best_diff = tmp_diff ;
2019-04-02 14:50:54 +02:00
}
2020-07-22 10:38:24 +03:00
if ( ! best_diff )
2019-04-02 14:50:54 +02:00
break ;
}
2020-07-22 10:38:24 +03:00
if ( best_rate < characteristics - > output [ 0 ] . min | |
best_rate > characteristics - > output [ 0 ] . max )
2019-04-02 14:50:54 +02:00
return - ERANGE ;
2020-07-22 10:38:24 +03:00
return best_rate ;
2019-04-02 14:50:54 +02:00
}
2020-07-22 10:38:24 +03:00
static long sam9x60_div_pll_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return sam9x60_div_pll_compute_div ( core , parent_rate , rate ) ;
2019-04-02 14:50:54 +02:00
}
2020-07-22 10:38:24 +03:00
static int sam9x60_div_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
struct sam9x60_pll_core * core = to_sam9x60_pll_core ( hw ) ;
struct sam9x60_div * div = to_sam9x60_div ( core ) ;
div - > div = DIV_ROUND_CLOSEST ( parent_rate , rate ) - 1 ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return 0 ;
2019-04-02 14:50:54 +02:00
}
2020-07-22 10:38:24 +03:00
static const struct clk_ops sam9x60_div_pll_ops = {
. prepare = sam9x60_div_pll_prepare ,
. unprepare = sam9x60_div_pll_unprepare ,
. is_prepared = sam9x60_div_pll_is_prepared ,
. recalc_rate = sam9x60_div_pll_recalc_rate ,
. round_rate = sam9x60_div_pll_round_rate ,
. set_rate = sam9x60_div_pll_set_rate ,
2019-04-02 14:50:54 +02:00
} ;
struct clk_hw * __init
2020-07-22 10:38:24 +03:00
sam9x60_clk_register_frac_pll ( struct regmap * regmap , spinlock_t * lock ,
const char * name , const char * parent_name ,
struct clk_hw * parent_hw , u8 id ,
const struct clk_pll_characteristics * characteristics ,
const struct clk_pll_layout * layout , bool critical )
2019-04-02 14:50:54 +02:00
{
2020-07-22 10:38:24 +03:00
struct sam9x60_frac * frac ;
2019-04-02 14:50:54 +02:00
struct clk_hw * hw ;
struct clk_init_data init ;
2020-07-22 10:38:24 +03:00
unsigned long parent_rate , flags ;
unsigned int val ;
2019-04-02 14:50:54 +02:00
int ret ;
2020-07-22 10:38:24 +03:00
if ( id > PLL_MAX_ID | | ! lock | | ! parent_hw )
2019-04-02 14:50:54 +02:00
return ERR_PTR ( - EINVAL ) ;
2020-07-22 10:38:24 +03:00
frac = kzalloc ( sizeof ( * frac ) , GFP_KERNEL ) ;
if ( ! frac )
2019-04-02 14:50:54 +02:00
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
2020-07-22 10:38:24 +03:00
init . ops = & sam9x60_frac_pll_ops ;
2019-04-02 14:50:54 +02:00
init . flags = CLK_SET_RATE_GATE ;
2020-07-22 10:38:24 +03:00
if ( critical )
init . flags | = CLK_IS_CRITICAL ;
frac - > core . id = id ;
frac - > core . hw . init = & init ;
frac - > core . characteristics = characteristics ;
frac - > core . layout = layout ;
frac - > core . regmap = regmap ;
frac - > core . lock = lock ;
spin_lock_irqsave ( frac - > core . lock , flags ) ;
if ( sam9x60_pll_ready ( regmap , id ) ) {
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_ID_MSK , id ) ;
regmap_read ( regmap , AT91_PMC_PLL_CTRL1 , & val ) ;
frac - > mul = FIELD_GET ( PMC_PLL_CTRL1_MUL_MSK , val ) ;
frac - > frac = FIELD_GET ( PMC_PLL_CTRL1_FRACR_MSK , val ) ;
} else {
/*
* This means the PLL is not setup by bootloaders . In this
* case we need to set the minimum rate for it . Otherwise
* a clock child of this PLL may be enabled before setting
* its rate leading to enabling this PLL with unsupported
* rate . This will lead to PLL not being locked at all .
*/
parent_rate = clk_hw_get_rate ( parent_hw ) ;
if ( ! parent_rate ) {
hw = ERR_PTR ( - EINVAL ) ;
goto free ;
}
ret = sam9x60_frac_pll_compute_mul_frac ( & frac - > core , FCORE_MIN ,
parent_rate , true ) ;
if ( ret < = 0 ) {
hw = ERR_PTR ( ret ) ;
goto free ;
}
}
spin_unlock_irqrestore ( frac - > core . lock , flags ) ;
hw = & frac - > core . hw ;
ret = clk_hw_register ( NULL , hw ) ;
if ( ret ) {
kfree ( frac ) ;
hw = ERR_PTR ( ret ) ;
}
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
return hw ;
free :
spin_unlock_irqrestore ( frac - > core . lock , flags ) ;
kfree ( frac ) ;
return hw ;
}
struct clk_hw * __init
sam9x60_clk_register_div_pll ( struct regmap * regmap , spinlock_t * lock ,
const char * name , const char * parent_name , u8 id ,
const struct clk_pll_characteristics * characteristics ,
const struct clk_pll_layout * layout , bool critical )
{
struct sam9x60_div * div ;
struct clk_hw * hw ;
struct clk_init_data init ;
unsigned long flags ;
unsigned int val ;
int ret ;
if ( id > PLL_MAX_ID | | ! lock )
return ERR_PTR ( - EINVAL ) ;
div = kzalloc ( sizeof ( * div ) , GFP_KERNEL ) ;
if ( ! div )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
init . ops = & sam9x60_div_pll_ops ;
init . flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
CLK_SET_RATE_PARENT ;
if ( critical )
init . flags | = CLK_IS_CRITICAL ;
div - > core . id = id ;
div - > core . hw . init = & init ;
div - > core . characteristics = characteristics ;
div - > core . layout = layout ;
div - > core . regmap = regmap ;
div - > core . lock = lock ;
spin_lock_irqsave ( div - > core . lock , flags ) ;
regmap_update_bits ( regmap , AT91_PMC_PLL_UPDT ,
AT91_PMC_PLL_UPDT_ID_MSK , id ) ;
regmap_read ( regmap , AT91_PMC_PLL_CTRL0 , & val ) ;
div - > div = FIELD_GET ( PMC_PLL_CTRL0_DIV_MSK , val ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
spin_unlock_irqrestore ( div - > core . lock , flags ) ;
2019-04-02 14:50:54 +02:00
2020-07-22 10:38:24 +03:00
hw = & div - > core . hw ;
2019-04-02 14:50:54 +02:00
ret = clk_hw_register ( NULL , hw ) ;
if ( ret ) {
2020-07-22 10:38:24 +03:00
kfree ( div ) ;
2019-04-02 14:50:54 +02:00
hw = ERR_PTR ( ret ) ;
}
return hw ;
}