2013-07-18 12:52:33 +04:00
/*
* TI clock support
*
* Copyright ( C ) 2013 Texas Instruments , Inc .
*
* Tero Kristo < t - kristo @ ti . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2015-06-20 01:00:46 +03:00
# include <linux/clk.h>
2013-07-18 12:52:33 +04:00
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/clk/ti.h>
# include <linux/of.h>
2013-10-22 12:39:36 +04:00
# include <linux/of_address.h>
# include <linux/list.h>
2015-04-27 22:23:06 +03:00
# include <linux/regmap.h>
# include <linux/bootmem.h>
2016-09-29 12:00:57 +03:00
# include <linux/device.h>
2013-07-18 12:52:33 +04:00
2014-12-16 19:20:46 +03:00
# include "clock.h"
2013-07-18 12:52:33 +04:00
# undef pr_fmt
# define pr_fmt(fmt) "%s: " fmt, __func__
2013-10-22 12:39:36 +04:00
struct ti_clk_ll_ops * ti_clk_ll_ops ;
2014-09-12 16:01:57 +04:00
static struct device_node * clocks_node_ptr [ CLK_MAX_MEMMAPS ] ;
2013-10-22 12:39:36 +04:00
2015-07-15 22:03:52 +03:00
static struct ti_clk_features ti_clk_features ;
2015-04-27 21:55:42 +03:00
2015-04-27 22:23:06 +03:00
struct clk_iomap {
struct regmap * regmap ;
void __iomem * mem ;
} ;
static struct clk_iomap * clk_memmaps [ CLK_MAX_MEMMAPS ] ;
2017-02-09 12:24:37 +03:00
static void clk_memmap_writel ( u32 val , const struct clk_omap_reg * reg )
2015-04-27 22:23:06 +03:00
{
2017-02-09 12:24:37 +03:00
struct clk_iomap * io = clk_memmaps [ reg - > index ] ;
2015-04-27 22:23:06 +03:00
2017-02-09 12:24:37 +03:00
if ( reg - > ptr )
writel_relaxed ( val , reg - > ptr ) ;
else if ( io - > regmap )
regmap_write ( io - > regmap , reg - > offset , val ) ;
2015-04-27 22:23:06 +03:00
else
2017-02-09 12:24:37 +03:00
writel_relaxed ( val , io - > mem + reg - > offset ) ;
2015-04-27 22:23:06 +03:00
}
2017-02-09 12:24:37 +03:00
static u32 clk_memmap_readl ( const struct clk_omap_reg * reg )
2015-04-27 22:23:06 +03:00
{
u32 val ;
2017-02-09 12:24:37 +03:00
struct clk_iomap * io = clk_memmaps [ reg - > index ] ;
2015-04-27 22:23:06 +03:00
2017-02-09 12:24:37 +03:00
if ( reg - > ptr )
val = readl_relaxed ( reg - > ptr ) ;
else if ( io - > regmap )
regmap_read ( io - > regmap , reg - > offset , & val ) ;
2015-04-27 22:23:06 +03:00
else
2017-02-09 12:24:37 +03:00
val = readl_relaxed ( io - > mem + reg - > offset ) ;
2015-04-27 22:23:06 +03:00
return val ;
}
2015-04-27 21:55:42 +03:00
/**
* ti_clk_setup_ll_ops - setup low level clock operations
* @ ops : low level clock ops descriptor
*
* Sets up low level clock operations for TI clock driver . This is used
* to provide various callbacks for the clock driver towards platform
* specific code . Returns 0 on success , - EBUSY if ll_ops have been
* registered already .
*/
int ti_clk_setup_ll_ops ( struct ti_clk_ll_ops * ops )
{
if ( ti_clk_ll_ops ) {
pr_err ( " Attempt to register ll_ops multiple times. \n " ) ;
return - EBUSY ;
}
ti_clk_ll_ops = ops ;
2015-04-27 22:23:06 +03:00
ops - > clk_readl = clk_memmap_readl ;
ops - > clk_writel = clk_memmap_writel ;
2015-04-27 21:55:42 +03:00
return 0 ;
}
2015-02-27 18:54:14 +03:00
2013-07-18 12:52:33 +04:00
/**
* ti_dt_clocks_register - register DT alias clocks during boot
* @ oclks : list of clocks to register
*
* Register alias or non - standard DT clock entries during boot . By
* default , DT clocks are found based on their node name . If any
* additional con - id / dev - id - > clock mapping is required , use this
* function to list these .
*/
void __init ti_dt_clocks_register ( struct ti_dt_clk oclks [ ] )
{
struct ti_dt_clk * c ;
struct device_node * node ;
struct clk * clk ;
struct of_phandle_args clkspec ;
for ( c = oclks ; c - > node_name ! = NULL ; c + + ) {
node = of_find_node_by_name ( NULL , c - > node_name ) ;
clkspec . np = node ;
clk = of_clk_get_from_provider ( & clkspec ) ;
if ( ! IS_ERR ( clk ) ) {
c - > lk . clk = clk ;
clkdev_add ( & c - > lk ) ;
} else {
pr_warn ( " failed to lookup clock node %s \n " ,
c - > node_name ) ;
}
}
}
2013-10-22 12:39:36 +04:00
struct clk_init_item {
struct device_node * node ;
struct clk_hw * hw ;
ti_of_clk_init_cb_t func ;
struct list_head link ;
} ;
static LIST_HEAD ( retry_list ) ;
/**
* ti_clk_retry_init - retries a failed clock init at later phase
* @ node : device not for the clock
* @ hw : partially initialized clk_hw struct for the clock
* @ func : init function to be called for the clock
*
* Adds a failed clock init to the retry list . The retry list is parsed
* once all the other clocks have been initialized .
*/
int __init ti_clk_retry_init ( struct device_node * node , struct clk_hw * hw ,
ti_of_clk_init_cb_t func )
{
struct clk_init_item * retry ;
pr_debug ( " %s: adding to retry list... \n " , node - > name ) ;
retry = kzalloc ( sizeof ( * retry ) , GFP_KERNEL ) ;
if ( ! retry )
return - ENOMEM ;
retry - > node = node ;
retry - > func = func ;
retry - > hw = hw ;
list_add ( & retry - > link , & retry_list ) ;
return 0 ;
}
/**
* ti_clk_get_reg_addr - get register address for a clock register
* @ node : device node for the clock
* @ index : register index from the clock node
2017-02-09 12:24:37 +03:00
* @ reg : pointer to target register struct
2013-10-22 12:39:36 +04:00
*
2017-02-09 12:24:37 +03:00
* Builds clock register address from device tree information , and returns
* the data via the provided output pointer @ reg . Returns 0 on success ,
* negative error value on failure .
2013-10-22 12:39:36 +04:00
*/
2017-02-09 12:24:37 +03:00
int ti_clk_get_reg_addr ( struct device_node * node , int index ,
struct clk_omap_reg * reg )
2013-10-22 12:39:36 +04:00
{
u32 val ;
2014-09-12 16:01:57 +04:00
int i ;
2013-10-22 12:39:36 +04:00
2014-09-12 16:01:57 +04:00
for ( i = 0 ; i < CLK_MAX_MEMMAPS ; i + + ) {
if ( clocks_node_ptr [ i ] = = node - > parent )
break ;
}
if ( i = = CLK_MAX_MEMMAPS ) {
pr_err ( " clk-provider not found for %s! \n " , node - > name ) ;
2017-02-09 12:24:37 +03:00
return - ENOENT ;
2014-09-12 16:01:57 +04:00
}
reg - > index = i ;
2013-10-22 12:39:36 +04:00
if ( of_property_read_u32_index ( node , " reg " , index , & val ) ) {
pr_err ( " %s must have reg[%d]! \n " , node - > name , index ) ;
2017-02-09 12:24:37 +03:00
return - EINVAL ;
2013-10-22 12:39:36 +04:00
}
reg - > offset = val ;
2017-02-09 12:24:37 +03:00
reg - > ptr = NULL ;
2013-10-22 12:39:36 +04:00
2017-02-09 12:24:37 +03:00
return 0 ;
2013-10-22 12:39:36 +04:00
}
/**
2015-04-27 22:23:06 +03:00
* omap2_clk_provider_init - init master clock provider
2013-10-22 12:39:36 +04:00
* @ parent : master node
* @ index : internal index for clk_reg_ops
2015-04-27 22:23:06 +03:00
* @ syscon : syscon regmap pointer for accessing clock registers
* @ mem : iomem pointer for the clock provider memory area , only used if
* syscon is not provided
2013-10-22 12:39:36 +04:00
*
2014-09-12 16:01:57 +04:00
* Initializes a master clock IP block . This basically sets up the
* mapping from clocks node to the memory map index . All the clocks
* are then initialized through the common of_clk_init call , and the
* clocks will access their memory maps based on the node layout .
2015-04-27 22:23:06 +03:00
* Returns 0 in success .
2013-10-22 12:39:36 +04:00
*/
2015-04-27 22:23:06 +03:00
int __init omap2_clk_provider_init ( struct device_node * parent , int index ,
struct regmap * syscon , void __iomem * mem )
2013-10-22 12:39:36 +04:00
{
struct device_node * clocks ;
2015-04-27 22:23:06 +03:00
struct clk_iomap * io ;
2013-10-22 12:39:36 +04:00
/* get clocks for this parent */
clocks = of_get_child_by_name ( parent , " clocks " ) ;
if ( ! clocks ) {
pr_err ( " %s missing 'clocks' child node. \n " , parent - > name ) ;
2015-04-27 22:23:06 +03:00
return - EINVAL ;
2013-10-22 12:39:36 +04:00
}
2014-09-12 16:01:57 +04:00
/* add clocks node info */
clocks_node_ptr [ index ] = clocks ;
2015-04-27 22:23:06 +03:00
io = kzalloc ( sizeof ( * io ) , GFP_KERNEL ) ;
2015-07-15 21:55:42 +03:00
if ( ! io )
return - ENOMEM ;
2015-04-27 22:23:06 +03:00
io - > regmap = syscon ;
io - > mem = mem ;
clk_memmaps [ index ] = io ;
return 0 ;
}
/**
* omap2_clk_legacy_provider_init - initialize a legacy clock provider
* @ index : index for the clock provider
* @ mem : iomem pointer for the clock provider memory area
*
* Initializes a legacy clock provider memory mapping .
*/
void __init omap2_clk_legacy_provider_init ( int index , void __iomem * mem )
{
struct clk_iomap * io ;
io = memblock_virt_alloc ( sizeof ( * io ) , 0 ) ;
io - > mem = mem ;
clk_memmaps [ index ] = io ;
2014-09-12 16:01:57 +04:00
}
2013-10-22 12:39:36 +04:00
2014-09-12 16:01:57 +04:00
/**
* ti_dt_clk_init_retry_clks - init clocks from the retry list
*
* Initializes any clocks that have failed to initialize before ,
* reasons being missing parent node ( s ) during earlier init . This
* typically happens only for DPLLs which need to have both of their
* parent clocks ready during init .
*/
void ti_dt_clk_init_retry_clks ( void )
{
struct clk_init_item * retry ;
struct clk_init_item * tmp ;
int retries = 5 ;
while ( ! list_empty ( & retry_list ) & & retries ) {
list_for_each_entry_safe ( retry , tmp , & retry_list , link ) {
pr_debug ( " retry-init: %s \n " , retry - > node - > name ) ;
retry - > func ( retry - > hw , retry - > node ) ;
list_del ( & retry - > link ) ;
kfree ( retry ) ;
}
retries - - ;
2013-10-22 12:39:36 +04:00
}
}
2014-12-16 19:20:46 +03:00
2015-02-03 19:59:32 +03:00
# if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_ATAGS)
2014-12-16 19:20:46 +03:00
void __init ti_clk_patch_legacy_clks ( struct ti_clk * * patch )
{
while ( * patch ) {
memcpy ( ( * patch ) - > patch , * patch , sizeof ( * * patch ) ) ;
patch + + ;
}
}
struct clk __init * ti_clk_register_clk ( struct ti_clk * setup )
{
struct clk * clk ;
struct ti_clk_fixed * fixed ;
struct ti_clk_fixed_factor * fixed_factor ;
struct clk_hw * clk_hw ;
2016-09-29 12:06:40 +03:00
int ret ;
2014-12-16 19:20:46 +03:00
if ( setup - > clk )
return setup - > clk ;
switch ( setup - > type ) {
case TI_CLK_FIXED :
fixed = setup - > data ;
2016-03-01 22:00:03 +03:00
clk = clk_register_fixed_rate ( NULL , setup - > name , NULL , 0 ,
fixed - > frequency ) ;
2016-09-29 12:06:40 +03:00
if ( ! IS_ERR ( clk ) ) {
ret = ti_clk_add_alias ( NULL , clk , setup - > name ) ;
if ( ret ) {
clk_unregister ( clk ) ;
clk = ERR_PTR ( ret ) ;
}
}
2014-12-16 19:20:46 +03:00
break ;
2014-12-16 19:20:47 +03:00
case TI_CLK_MUX :
clk = ti_clk_register_mux ( setup ) ;
break ;
2014-12-16 19:20:50 +03:00
case TI_CLK_DIVIDER :
clk = ti_clk_register_divider ( setup ) ;
break ;
2014-12-16 19:20:52 +03:00
case TI_CLK_COMPOSITE :
clk = ti_clk_register_composite ( setup ) ;
break ;
2014-12-16 19:20:46 +03:00
case TI_CLK_FIXED_FACTOR :
fixed_factor = setup - > data ;
clk = clk_register_fixed_factor ( NULL , setup - > name ,
fixed_factor - > parent ,
0 , fixed_factor - > mult ,
fixed_factor - > div ) ;
2016-09-29 12:06:40 +03:00
if ( ! IS_ERR ( clk ) ) {
ret = ti_clk_add_alias ( NULL , clk , setup - > name ) ;
if ( ret ) {
clk_unregister ( clk ) ;
clk = ERR_PTR ( ret ) ;
}
}
2014-12-16 19:20:46 +03:00
break ;
2014-12-16 19:20:48 +03:00
case TI_CLK_GATE :
clk = ti_clk_register_gate ( setup ) ;
break ;
2015-01-29 23:24:28 +03:00
case TI_CLK_DPLL :
clk = ti_clk_register_dpll ( setup ) ;
break ;
2014-12-16 19:20:46 +03:00
default :
pr_err ( " bad type for %s! \n " , setup - > name ) ;
clk = ERR_PTR ( - EINVAL ) ;
}
if ( ! IS_ERR ( clk ) ) {
setup - > clk = clk ;
if ( setup - > clkdm_name ) {
2015-06-30 02:56:30 +03:00
clk_hw = __clk_get_hw ( clk ) ;
if ( clk_hw_get_flags ( clk_hw ) & CLK_IS_BASIC ) {
2014-12-16 19:20:46 +03:00
pr_warn ( " can't setup clkdm for basic clk %s \n " ,
setup - > name ) ;
} else {
to_clk_hw_omap ( clk_hw ) - > clkdm_name =
setup - > clkdm_name ;
omap2_init_clk_clkdm ( clk_hw ) ;
}
}
}
return clk ;
}
int __init ti_clk_register_legacy_clks ( struct ti_clk_alias * clks )
{
struct clk * clk ;
bool retry ;
struct ti_clk_alias * retry_clk ;
struct ti_clk_alias * tmp ;
while ( clks - > clk ) {
clk = ti_clk_register_clk ( clks - > clk ) ;
if ( IS_ERR ( clk ) ) {
if ( PTR_ERR ( clk ) = = - EAGAIN ) {
list_add ( & clks - > link , & retry_list ) ;
} else {
pr_err ( " register for %s failed: %ld \n " ,
clks - > clk - > name , PTR_ERR ( clk ) ) ;
return PTR_ERR ( clk ) ;
}
}
clks + + ;
}
retry = true ;
while ( ! list_empty ( & retry_list ) & & retry ) {
retry = false ;
list_for_each_entry_safe ( retry_clk , tmp , & retry_list , link ) {
pr_debug ( " retry-init: %s \n " , retry_clk - > clk - > name ) ;
clk = ti_clk_register_clk ( retry_clk - > clk ) ;
if ( IS_ERR ( clk ) ) {
if ( PTR_ERR ( clk ) = = - EAGAIN ) {
continue ;
} else {
pr_err ( " register for %s failed: %ld \n " ,
retry_clk - > clk - > name ,
PTR_ERR ( clk ) ) ;
return PTR_ERR ( clk ) ;
}
} else {
retry = true ;
list_del ( & retry_clk - > link ) ;
}
}
}
return 0 ;
}
2015-02-03 19:59:32 +03:00
# endif
2015-02-27 18:54:14 +03:00
2017-04-19 20:44:56 +03:00
static const struct of_device_id simple_clk_match_table [ ] __initconst = {
{ . compatible = " fixed-clock " } ,
{ . compatible = " fixed-factor-clock " } ,
{ }
} ;
2016-09-29 12:05:00 +03:00
/**
* ti_clk_add_aliases - setup clock aliases
*
* Sets up any missing clock aliases . No return value .
*/
void __init ti_clk_add_aliases ( void )
{
struct device_node * np ;
struct clk * clk ;
for_each_matching_node ( np , simple_clk_match_table ) {
struct of_phandle_args clkspec ;
clkspec . np = np ;
clk = of_clk_get_from_provider ( & clkspec ) ;
ti_clk_add_alias ( NULL , clk , np - > name ) ;
}
}
2015-02-27 18:54:14 +03:00
/**
* ti_clk_setup_features - setup clock features flags
* @ features : features definition to use
*
* Initializes the clock driver features flags based on platform
* provided data . No return value .
*/
void __init ti_clk_setup_features ( struct ti_clk_features * features )
{
memcpy ( & ti_clk_features , features , sizeof ( * features ) ) ;
}
/**
* ti_clk_get_features - get clock driver features flags
*
* Get TI clock driver features description . Returns a pointer
* to the current feature setup .
*/
const struct ti_clk_features * ti_clk_get_features ( void )
{
return & ti_clk_features ;
}
2015-03-03 11:51:01 +03:00
/**
* omap2_clk_enable_init_clocks - prepare & enable a list of clocks
* @ clk_names : ptr to an array of strings of clock names to enable
* @ num_clocks : number of clock names in @ clk_names
*
* Prepare and enable a list of clocks , named by @ clk_names . No
* return value . XXX Deprecated ; only needed until these clocks are
* properly claimed and enabled by the drivers or core code that uses
* them . XXX What code disables & calls clk_put on these clocks ?
*/
void omap2_clk_enable_init_clocks ( const char * * clk_names , u8 num_clocks )
{
struct clk * init_clk ;
int i ;
for ( i = 0 ; i < num_clocks ; i + + ) {
init_clk = clk_get ( NULL , clk_names [ i ] ) ;
if ( WARN ( IS_ERR ( init_clk ) , " could not find init clock %s \n " ,
clk_names [ i ] ) )
continue ;
clk_prepare_enable ( init_clk ) ;
}
}
2016-09-29 12:00:57 +03:00
/**
* ti_clk_add_alias - add a clock alias for a TI clock
* @ dev : device alias for this clock
* @ clk : clock handle to create alias for
* @ con : connection ID for this clock
*
* Creates a clock alias for a TI clock . Allocates the clock lookup entry
* and assigns the data to it . Returns 0 if successful , negative error
* value otherwise .
*/
int ti_clk_add_alias ( struct device * dev , struct clk * clk , const char * con )
{
struct clk_lookup * cl ;
if ( ! clk )
return 0 ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
cl = kzalloc ( sizeof ( * cl ) , GFP_KERNEL ) ;
if ( ! cl )
return - ENOMEM ;
if ( dev )
cl - > dev_id = dev_name ( dev ) ;
cl - > con_id = con ;
cl - > clk = clk ;
clkdev_add ( cl ) ;
return 0 ;
}
/**
* ti_clk_register - register a TI clock to the common clock framework
* @ dev : device for this clock
* @ hw : hardware clock handle
* @ con : connection ID for this clock
*
* Registers a TI clock to the common clock framework , and adds a clock
* alias for it . Returns a handle to the registered clock if successful ,
* ERR_PTR value in failure .
*/
struct clk * ti_clk_register ( struct device * dev , struct clk_hw * hw ,
const char * con )
{
struct clk * clk ;
int ret ;
clk = clk_register ( dev , hw ) ;
if ( IS_ERR ( clk ) )
return clk ;
ret = ti_clk_add_alias ( dev , clk , con ) ;
if ( ret ) {
clk_unregister ( clk ) ;
return ERR_PTR ( ret ) ;
}
return clk ;
}