2013-03-09 17:02:44 +09:00
/*
* Copyright ( c ) 2013 Samsung Electronics Co . , Ltd .
* Copyright ( c ) 2013 Linaro Ltd .
* Author : Thomas Abraham < thomas . ab @ samsung . 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 file includes utility functions to register clocks to common
* clock framework for Samsung platforms .
*/
# include <linux/syscore_ops.h>
# include "clk.h"
static DEFINE_SPINLOCK ( lock ) ;
static struct clk * * clk_table ;
static void __iomem * reg_base ;
# ifdef CONFIG_OF
static struct clk_onecell_data clk_data ;
# endif
# ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump * reg_dump ;
static unsigned long nr_reg_dump ;
static int samsung_clk_suspend ( void )
{
struct samsung_clk_reg_dump * rd = reg_dump ;
unsigned long i ;
for ( i = 0 ; i < nr_reg_dump ; i + + , rd + + )
rd - > value = __raw_readl ( reg_base + rd - > offset ) ;
return 0 ;
}
static void samsung_clk_resume ( void )
{
struct samsung_clk_reg_dump * rd = reg_dump ;
unsigned long i ;
for ( i = 0 ; i < nr_reg_dump ; i + + , rd + + )
__raw_writel ( rd - > value , reg_base + rd - > offset ) ;
}
static struct syscore_ops samsung_clk_syscore_ops = {
. suspend = samsung_clk_suspend ,
. resume = samsung_clk_resume ,
} ;
# endif /* CONFIG_PM_SLEEP */
/* setup the essentials required to support clock lookup using ccf */
void __init samsung_clk_init ( struct device_node * np , void __iomem * base ,
unsigned long nr_clks , unsigned long * rdump ,
2013-04-04 13:35:35 +09:00
unsigned long nr_rdump , unsigned long * soc_rdump ,
unsigned long nr_soc_rdump )
2013-03-09 17:02:44 +09:00
{
reg_base = base ;
# ifdef CONFIG_PM_SLEEP
if ( rdump & & nr_rdump ) {
unsigned int idx ;
reg_dump = kzalloc ( sizeof ( struct samsung_clk_reg_dump )
2013-04-04 13:35:35 +09:00
* ( nr_rdump + nr_soc_rdump ) , GFP_KERNEL ) ;
2013-03-09 17:02:44 +09:00
if ( ! reg_dump ) {
pr_err ( " %s: memory alloc for register dump failed \n " ,
__func__ ) ;
return ;
}
for ( idx = 0 ; idx < nr_rdump ; idx + + )
reg_dump [ idx ] . offset = rdump [ idx ] ;
2013-04-04 13:35:35 +09:00
for ( idx = 0 ; idx < nr_soc_rdump ; idx + + )
reg_dump [ nr_rdump + idx ] . offset = soc_rdump [ idx ] ;
nr_reg_dump = nr_rdump + nr_soc_rdump ;
2013-03-09 17:02:44 +09:00
register_syscore_ops ( & samsung_clk_syscore_ops ) ;
}
# endif
2013-03-18 13:43:52 +09:00
clk_table = kzalloc ( sizeof ( struct clk * ) * nr_clks , GFP_KERNEL ) ;
if ( ! clk_table )
panic ( " could not allocate clock lookup table \n " ) ;
2013-03-18 13:43:52 +09:00
if ( ! np )
return ;
# ifdef CONFIG_OF
2013-03-18 13:43:52 +09:00
clk_data . clks = clk_table ;
clk_data . clk_num = nr_clks ;
of_clk_add_provider ( np , of_clk_src_onecell_get , & clk_data ) ;
# endif
2013-03-09 17:02:44 +09:00
}
/* add a clock instance to the clock lookup table used for dt based lookup */
void samsung_clk_add_lookup ( struct clk * clk , unsigned int id )
{
if ( clk_table & & id )
clk_table [ id ] = clk ;
}
2013-03-18 13:43:56 +09:00
/* register a list of aliases */
void __init samsung_clk_register_alias ( struct samsung_clock_alias * list ,
unsigned int nr_clk )
{
struct clk * clk ;
unsigned int idx , ret ;
if ( ! clk_table ) {
pr_err ( " %s: clock table missing \n " , __func__ ) ;
return ;
}
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
if ( ! list - > id ) {
pr_err ( " %s: clock id missing for index %d \n " , __func__ ,
idx ) ;
continue ;
}
clk = clk_table [ list - > id ] ;
if ( ! clk ) {
pr_err ( " %s: failed to find clock %d \n " , __func__ ,
list - > id ) ;
continue ;
}
ret = clk_register_clkdev ( clk , list - > alias , list - > dev_name ) ;
if ( ret )
pr_err ( " %s: failed to register lookup %s \n " ,
__func__ , list - > alias ) ;
}
}
2013-03-09 17:02:44 +09:00
/* register a list of fixed clocks */
void __init samsung_clk_register_fixed_rate (
struct samsung_fixed_rate_clock * list , unsigned int nr_clk )
{
struct clk * clk ;
unsigned int idx , ret ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
clk = clk_register_fixed_rate ( NULL , list - > name ,
list - > parent_name , list - > flags , list - > fixed_rate ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
samsung_clk_add_lookup ( clk , list - > id ) ;
/*
* Unconditionally add a clock lookup for the fixed rate clocks .
* There are not many of these on any of Samsung platforms .
*/
ret = clk_register_clkdev ( clk , list - > name , NULL ) ;
if ( ret )
pr_err ( " %s: failed to register clock lookup for %s " ,
__func__ , list - > name ) ;
}
}
/* register a list of fixed factor clocks */
void __init samsung_clk_register_fixed_factor (
struct samsung_fixed_factor_clock * list , unsigned int nr_clk )
{
struct clk * clk ;
unsigned int idx ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
clk = clk_register_fixed_factor ( NULL , list - > name ,
list - > parent_name , list - > flags , list - > mult , list - > div ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
samsung_clk_add_lookup ( clk , list - > id ) ;
}
}
/* register a list of mux clocks */
void __init samsung_clk_register_mux ( struct samsung_mux_clock * list ,
unsigned int nr_clk )
{
struct clk * clk ;
unsigned int idx , ret ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
clk = clk_register_mux ( NULL , list - > name , list - > parent_names ,
list - > num_parents , list - > flags , reg_base + list - > offset ,
list - > shift , list - > width , list - > mux_flags , & lock ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
samsung_clk_add_lookup ( clk , list - > id ) ;
/* register a clock lookup only if a clock alias is specified */
if ( list - > alias ) {
ret = clk_register_clkdev ( clk , list - > alias ,
list - > dev_name ) ;
if ( ret )
pr_err ( " %s: failed to register lookup %s \n " ,
__func__ , list - > alias ) ;
}
}
}
/* register a list of div clocks */
void __init samsung_clk_register_div ( struct samsung_div_clock * list ,
unsigned int nr_clk )
{
struct clk * clk ;
unsigned int idx , ret ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2013-03-18 13:43:52 +09:00
if ( list - > table )
clk = clk_register_divider_table ( NULL , list - > name ,
list - > parent_name , list - > flags ,
reg_base + list - > offset , list - > shift ,
list - > width , list - > div_flags ,
list - > table , & lock ) ;
else
clk = clk_register_divider ( NULL , list - > name ,
list - > parent_name , list - > flags ,
reg_base + list - > offset , list - > shift ,
list - > width , list - > div_flags , & lock ) ;
2013-03-09 17:02:44 +09:00
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
samsung_clk_add_lookup ( clk , list - > id ) ;
/* register a clock lookup only if a clock alias is specified */
if ( list - > alias ) {
ret = clk_register_clkdev ( clk , list - > alias ,
list - > dev_name ) ;
if ( ret )
pr_err ( " %s: failed to register lookup %s \n " ,
__func__ , list - > alias ) ;
}
}
}
/* register a list of gate clocks */
void __init samsung_clk_register_gate ( struct samsung_gate_clock * list ,
unsigned int nr_clk )
{
struct clk * clk ;
unsigned int idx , ret ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
clk = clk_register_gate ( NULL , list - > name , list - > parent_name ,
list - > flags , reg_base + list - > offset ,
list - > bit_idx , list - > gate_flags , & lock ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
/* register a clock lookup only if a clock alias is specified */
if ( list - > alias ) {
ret = clk_register_clkdev ( clk , list - > alias ,
list - > dev_name ) ;
if ( ret )
pr_err ( " %s: failed to register lookup %s \n " ,
__func__ , list - > alias ) ;
}
samsung_clk_add_lookup ( clk , list - > id ) ;
}
}
/*
* obtain the clock speed of all external fixed clock sources from device
* tree and register it
*/
2013-04-08 21:35:25 +09:00
# ifdef CONFIG_OF
2013-03-09 17:02:44 +09:00
void __init samsung_clk_of_register_fixed_ext (
struct samsung_fixed_rate_clock * fixed_rate_clk ,
unsigned int nr_fixed_rate_clk ,
struct of_device_id * clk_matches )
{
const struct of_device_id * match ;
struct device_node * np ;
u32 freq ;
for_each_matching_node_and_match ( np , clk_matches , & match ) {
if ( of_property_read_u32 ( np , " clock-frequency " , & freq ) )
continue ;
fixed_rate_clk [ ( u32 ) match - > data ] . fixed_rate = freq ;
}
samsung_clk_register_fixed_rate ( fixed_rate_clk , nr_fixed_rate_clk ) ;
}
2013-04-08 21:35:25 +09:00
# endif
2013-03-09 17:02:44 +09:00
/* utility function to get the rate of a specified clock */
unsigned long _get_rate ( const char * clk_name )
{
struct clk * clk ;
2013-08-26 19:09:00 +02:00
clk = __clk_lookup ( clk_name ) ;
if ( ! clk ) {
2013-03-09 17:02:44 +09:00
pr_err ( " %s: could not find clock %s \n " , __func__ , clk_name ) ;
return 0 ;
}
2013-08-26 19:09:00 +02:00
return clk_get_rate ( clk ) ;
2013-03-09 17:02:44 +09:00
}