2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-10-11 12:22:06 +02: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 08:14:29 +02:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2017-09-06 11:51:59 +02:00
# include <soc/at91/atmel-sfr.h>
2013-10-11 12:22:06 +02:00
# include "pmc.h"
2017-09-06 11:51:59 +02:00
/*
* The purpose of this clock is to generate a 480 MHz signal . A different
* rate can ' t be configured .
*/
# define UTMI_RATE 480000000
2013-10-11 12:22:06 +02:00
struct clk_utmi {
struct clk_hw hw ;
2017-09-06 11:51:59 +02:00
struct regmap * regmap_pmc ;
struct regmap * regmap_sfr ;
2013-10-11 12:22:06 +02:00
} ;
# define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
2014-09-07 08:14:29 +02:00
static inline bool clk_utmi_ready ( struct regmap * regmap )
{
unsigned int status ;
regmap_read ( regmap , AT91_PMC_SR , & status ) ;
return status & AT91_PMC_LOCKU ;
}
2013-10-11 12:22:06 +02:00
static int clk_utmi_prepare ( struct clk_hw * hw )
{
2017-09-06 11:51:59 +02:00
struct clk_hw * hw_parent ;
2013-10-11 12:22:06 +02:00
struct clk_utmi * utmi = to_clk_utmi ( hw ) ;
2014-09-07 08:14:29 +02:00
unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
AT91_PMC_BIASEN ;
2017-09-06 11:51:59 +02:00
unsigned int utmi_ref_clk_freq ;
unsigned long parent_rate ;
/*
* If mainck rate is different from 12 MHz , we have to configure the
* FREQ field of the SFR_UTMICKTRIM register to generate properly
* the utmi clock .
*/
hw_parent = clk_hw_get_parent ( hw ) ;
parent_rate = clk_hw_get_rate ( hw_parent ) ;
switch ( parent_rate ) {
case 12000000 :
utmi_ref_clk_freq = 0 ;
break ;
case 16000000 :
utmi_ref_clk_freq = 1 ;
break ;
case 24000000 :
utmi_ref_clk_freq = 2 ;
break ;
/*
* Not supported on SAMA5D2 but it ' s not an issue since MAINCK
* maximum value is 24 MHz .
*/
case 48000000 :
utmi_ref_clk_freq = 3 ;
break ;
default :
pr_err ( " UTMICK: unsupported mainck rate \n " ) ;
return - EINVAL ;
}
2013-10-11 12:22:06 +02:00
2017-09-06 11:51:59 +02:00
if ( utmi - > regmap_sfr ) {
regmap_update_bits ( utmi - > regmap_sfr , AT91_SFR_UTMICKTRIM ,
AT91_UTMICKTRIM_FREQ , utmi_ref_clk_freq ) ;
} else if ( utmi_ref_clk_freq ) {
pr_err ( " UTMICK: sfr node required \n " ) ;
return - EINVAL ;
}
2013-10-11 12:22:06 +02:00
2017-09-06 11:51:59 +02:00
regmap_update_bits ( utmi - > regmap_pmc , AT91_CKGR_UCKR , uckr , uckr ) ;
while ( ! clk_utmi_ready ( utmi - > regmap_pmc ) )
2015-09-16 23:47:39 +02:00
cpu_relax ( ) ;
2013-10-11 12:22:06 +02:00
return 0 ;
}
static int clk_utmi_is_prepared ( struct clk_hw * hw )
{
struct clk_utmi * utmi = to_clk_utmi ( hw ) ;
2017-09-06 11:51:59 +02:00
return clk_utmi_ready ( utmi - > regmap_pmc ) ;
2013-10-11 12:22:06 +02:00
}
static void clk_utmi_unprepare ( struct clk_hw * hw )
{
struct clk_utmi * utmi = to_clk_utmi ( hw ) ;
2017-09-06 11:51:59 +02:00
regmap_update_bits ( utmi - > regmap_pmc , AT91_CKGR_UCKR ,
AT91_PMC_UPLLEN , 0 ) ;
2013-10-11 12:22:06 +02:00
}
static unsigned long clk_utmi_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2017-09-06 11:51:59 +02:00
/* UTMI clk rate is fixed. */
return UTMI_RATE ;
2013-10-11 12:22:06 +02:00
}
static const struct clk_ops utmi_ops = {
. prepare = clk_utmi_prepare ,
. unprepare = clk_utmi_unprepare ,
. is_prepared = clk_utmi_is_prepared ,
. recalc_rate = clk_utmi_recalc_rate ,
} ;
2018-10-16 16:21:44 +02:00
struct clk_hw * __init
2017-09-06 11:51:59 +02:00
at91_clk_register_utmi ( struct regmap * regmap_pmc , struct regmap * regmap_sfr ,
2013-10-11 12:22:06 +02:00
const char * name , const char * parent_name )
{
struct clk_utmi * utmi ;
2016-06-01 14:31:22 -07:00
struct clk_hw * hw ;
2013-10-11 12:22:06 +02:00
struct clk_init_data init ;
2016-06-01 14:31:22 -07:00
int ret ;
2013-10-11 12:22:06 +02:00
utmi = kzalloc ( sizeof ( * utmi ) , GFP_KERNEL ) ;
if ( ! utmi )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & utmi_ops ;
init . parent_names = parent_name ? & parent_name : NULL ;
init . num_parents = parent_name ? 1 : 0 ;
init . flags = CLK_SET_RATE_GATE ;
utmi - > hw . init = & init ;
2017-09-06 11:51:59 +02:00
utmi - > regmap_pmc = regmap_pmc ;
utmi - > regmap_sfr = regmap_sfr ;
2013-10-11 12:22:06 +02:00
2016-06-01 14:31:22 -07:00
hw = & utmi - > hw ;
ret = clk_hw_register ( NULL , & utmi - > hw ) ;
if ( ret ) {
2013-10-11 12:22:06 +02:00
kfree ( utmi ) ;
2016-06-01 14:31:22 -07:00
hw = ERR_PTR ( ret ) ;
}
2013-10-11 12:22:06 +02:00
2016-06-01 14:31:22 -07:00
return hw ;
2013-10-11 12:22:06 +02:00
}