2013-10-11 12:22:06 +02: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 08:14:29 +02:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2013-10-11 12:22:06 +02:00
# include "pmc.h"
# define UTMI_FIXED_MUL 40
struct clk_utmi {
struct clk_hw hw ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
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 )
{
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 ;
2013-10-11 12:22:06 +02:00
2014-09-07 08:14:29 +02:00
regmap_update_bits ( utmi - > regmap , AT91_CKGR_UCKR , uckr , uckr ) ;
2013-10-11 12:22:06 +02:00
2015-09-16 23:47:39 +02:00
while ( ! clk_utmi_ready ( utmi - > regmap ) )
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 ) ;
2014-09-07 08:14:29 +02:00
return clk_utmi_ready ( utmi - > regmap ) ;
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 ) ;
2014-09-07 08:14:29 +02:00
regmap_update_bits ( utmi - > regmap , 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 )
{
/* UTMI clk is a fixed clk multiplier */
return parent_rate * UTMI_FIXED_MUL ;
}
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 ,
} ;
static struct clk * __init
2015-09-16 23:47:39 +02:00
at91_clk_register_utmi ( struct regmap * regmap ,
2013-10-11 12:22:06 +02:00
const char * name , const char * parent_name )
{
struct clk_utmi * utmi ;
struct clk * clk = NULL ;
struct clk_init_data init ;
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 ;
2014-09-07 08:14:29 +02:00
utmi - > regmap = regmap ;
2013-10-11 12:22:06 +02:00
clk = clk_register ( NULL , & utmi - > hw ) ;
2015-09-16 23:47:39 +02:00
if ( IS_ERR ( clk ) )
2013-10-11 12:22:06 +02:00
kfree ( utmi ) ;
return clk ;
}
2014-09-07 08:14:29 +02:00
static void __init of_at91sam9x5_clk_utmi_setup ( struct device_node * np )
2013-10-11 12:22:06 +02:00
{
struct clk * clk ;
const char * parent_name ;
const char * name = np - > name ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
2013-10-11 12:22:06 +02:00
parent_name = of_clk_get_parent_name ( np , 0 ) ;
of_property_read_string ( np , " clock-output-names " , & name ) ;
2014-09-07 08:14:29 +02:00
regmap = syscon_node_to_regmap ( of_get_parent ( np ) ) ;
if ( IS_ERR ( regmap ) )
return ;
2015-09-16 23:47:39 +02:00
clk = at91_clk_register_utmi ( regmap , name , parent_name ) ;
2013-10-11 12:22:06 +02:00
if ( IS_ERR ( clk ) )
return ;
of_clk_add_provider ( np , of_clk_src_simple_get , clk ) ;
return ;
}
2014-09-07 08:14:29 +02:00
CLK_OF_DECLARE ( at91sam9x5_clk_utmi , " atmel,at91sam9x5-clk-utmi " ,
of_at91sam9x5_clk_utmi_setup ) ;