2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
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 file includes utility functions to register clocks to common
* clock framework for Samsung platforms .
*/
2015-06-19 15:00:46 -07:00
# include <linux/slab.h>
# include <linux/clkdev.h>
# include <linux/clk.h>
# include <linux/clk-provider.h>
2019-04-18 15:20:22 -07:00
# include <linux/io.h>
2014-09-29 13:17:48 +05:30
# include <linux/of_address.h>
2013-03-09 17:02:44 +09:00
# include <linux/syscore_ops.h>
2014-09-29 13:17:48 +05:30
2013-03-09 17:02:44 +09:00
# include "clk.h"
2014-09-22 10:17:02 +05:30
static LIST_HEAD ( clock_reg_cache_list ) ;
2014-02-14 08:16:00 +09:00
void samsung_clk_save ( void __iomem * base ,
struct samsung_clk_reg_dump * rd ,
unsigned int num_regs )
{
for ( ; num_regs > 0 ; - - num_regs , + + rd )
rd - > value = readl ( base + rd - > offset ) ;
}
void samsung_clk_restore ( void __iomem * base ,
const struct samsung_clk_reg_dump * rd ,
unsigned int num_regs )
{
for ( ; num_regs > 0 ; - - num_regs , + + rd )
writel ( rd - > value , base + rd - > offset ) ;
}
2014-02-14 08:16:00 +09:00
struct samsung_clk_reg_dump * samsung_clk_alloc_reg_dump (
const unsigned long * rdump ,
unsigned long nr_rdump )
2014-02-14 08:16:00 +09:00
{
struct samsung_clk_reg_dump * rd ;
unsigned int i ;
rd = kcalloc ( nr_rdump , sizeof ( * rd ) , GFP_KERNEL ) ;
if ( ! rd )
return NULL ;
for ( i = 0 ; i < nr_rdump ; + + i )
rd [ i ] . offset = rdump [ i ] ;
return rd ;
}
2013-03-09 17:02:44 +09:00
/* setup the essentials required to support clock lookup using ccf */
2014-03-12 20:26:44 +05:30
struct samsung_clk_provider * __init samsung_clk_init ( struct device_node * np ,
void __iomem * base , unsigned long nr_clks )
2013-03-09 17:02:44 +09:00
{
2014-03-12 20:26:44 +05:30
struct samsung_clk_provider * ctx ;
2014-02-06 19:33:11 +01:00
int i ;
2017-04-24 08:42:20 +02:00
ctx = kzalloc ( sizeof ( struct samsung_clk_provider ) +
sizeof ( * ctx - > clk_data . hws ) * nr_clks , GFP_KERNEL ) ;
2014-03-12 20:26:44 +05:30
if ( ! ctx )
panic ( " could not allocate clock provider context. \n " ) ;
2013-03-09 17:02:44 +09:00
2014-02-06 19:33:11 +01:00
for ( i = 0 ; i < nr_clks ; + + i )
2017-04-24 08:42:20 +02:00
ctx - > clk_data . hws [ i ] = ERR_PTR ( - ENOENT ) ;
2014-02-06 19:33:11 +01:00
2014-03-12 20:26:44 +05:30
ctx - > reg_base = base ;
2017-04-24 08:42:20 +02:00
ctx - > clk_data . num = nr_clks ;
2014-03-12 20:26:44 +05:30
spin_lock_init ( & ctx - > lock ) ;
return ctx ;
2014-06-18 17:46:52 +02:00
}
void __init samsung_clk_of_add_provider ( struct device_node * np ,
struct samsung_clk_provider * ctx )
{
if ( np ) {
2017-04-24 08:42:20 +02:00
if ( of_clk_add_hw_provider ( np , of_clk_hw_onecell_get ,
2014-06-18 17:46:52 +02:00
& ctx - > clk_data ) )
panic ( " could not register clk provider \n " ) ;
}
2013-03-09 17:02:44 +09:00
}
/* add a clock instance to the clock lookup table used for dt based lookup */
2017-04-24 08:42:20 +02:00
void samsung_clk_add_lookup ( struct samsung_clk_provider * ctx ,
struct clk_hw * clk_hw , unsigned int id )
2013-03-09 17:02:44 +09:00
{
2017-04-24 08:42:20 +02:00
if ( id )
ctx - > clk_data . hws [ id ] = clk_hw ;
2013-03-09 17:02:44 +09:00
}
2013-03-18 13:43:56 +09:00
/* register a list of aliases */
2014-03-12 20:26:44 +05:30
void __init samsung_clk_register_alias ( struct samsung_clk_provider * ctx ,
2015-05-28 10:45:51 +02:00
const struct samsung_clock_alias * list ,
2014-03-12 20:26:44 +05:30
unsigned int nr_clk )
2013-03-18 13:43:56 +09:00
{
2017-04-24 08:42:20 +02:00
struct clk_hw * clk_hw ;
2013-03-18 13:43:56 +09:00
unsigned int idx , ret ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
if ( ! list - > id ) {
pr_err ( " %s: clock id missing for index %d \n " , __func__ ,
idx ) ;
continue ;
}
2017-04-24 08:42:20 +02:00
clk_hw = ctx - > clk_data . hws [ list - > id ] ;
if ( ! clk_hw ) {
2013-03-18 13:43:56 +09:00
pr_err ( " %s: failed to find clock %d \n " , __func__ ,
list - > id ) ;
continue ;
}
2017-04-24 08:42:20 +02:00
ret = clk_hw_register_clkdev ( clk_hw , list - > alias ,
list - > dev_name ) ;
2013-03-18 13:43:56 +09:00
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 */
2014-03-12 20:26:44 +05:30
void __init samsung_clk_register_fixed_rate ( struct samsung_clk_provider * ctx ,
2015-05-28 10:45:51 +02:00
const struct samsung_fixed_rate_clock * list ,
unsigned int nr_clk )
2013-03-09 17:02:44 +09:00
{
2017-04-24 08:42:20 +02:00
struct clk_hw * clk_hw ;
2013-03-09 17:02:44 +09:00
unsigned int idx , ret ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 10:05:00 +02:00
clk_hw = clk_hw_register_fixed_rate ( ctx - > dev , list - > name ,
2013-03-09 17:02:44 +09:00
list - > parent_name , list - > flags , list - > fixed_rate ) ;
2017-04-24 08:42:20 +02:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 17:02:44 +09:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 08:42:20 +02:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 17:02:44 +09:00
/*
* Unconditionally add a clock lookup for the fixed rate clocks .
* There are not many of these on any of Samsung platforms .
*/
2017-04-24 08:42:20 +02:00
ret = clk_hw_register_clkdev ( clk_hw , list - > name , NULL ) ;
2013-03-09 17:02:44 +09:00
if ( ret )
pr_err ( " %s: failed to register clock lookup for %s " ,
__func__ , list - > name ) ;
}
}
/* register a list of fixed factor clocks */
2014-03-12 20:26:44 +05:30
void __init samsung_clk_register_fixed_factor ( struct samsung_clk_provider * ctx ,
2015-05-28 10:45:51 +02:00
const struct samsung_fixed_factor_clock * list , unsigned int nr_clk )
2013-03-09 17:02:44 +09:00
{
2017-04-24 08:42:20 +02:00
struct clk_hw * clk_hw ;
2013-03-09 17:02:44 +09:00
unsigned int idx ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 10:05:00 +02:00
clk_hw = clk_hw_register_fixed_factor ( ctx - > dev , list - > name ,
2013-03-09 17:02:44 +09:00
list - > parent_name , list - > flags , list - > mult , list - > div ) ;
2017-04-24 08:42:20 +02:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 17:02:44 +09:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 08:42:20 +02:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 17:02:44 +09:00
}
}
/* register a list of mux clocks */
2014-03-12 20:26:44 +05:30
void __init samsung_clk_register_mux ( struct samsung_clk_provider * ctx ,
2015-05-28 10:45:51 +02:00
const struct samsung_mux_clock * list ,
2014-03-12 20:26:44 +05:30
unsigned int nr_clk )
2013-03-09 17:02:44 +09:00
{
2017-04-24 08:42:20 +02:00
struct clk_hw * clk_hw ;
2017-10-03 12:00:16 +02:00
unsigned int idx ;
2013-03-09 17:02:44 +09:00
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 10:05:00 +02:00
clk_hw = clk_hw_register_mux ( ctx - > dev , list - > name ,
2017-04-24 08:42:20 +02:00
list - > parent_names , list - > num_parents , list - > flags ,
2014-03-12 20:26:44 +05:30
ctx - > reg_base + list - > offset ,
list - > shift , list - > width , list - > mux_flags , & ctx - > lock ) ;
2017-04-24 08:42:20 +02:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 17:02:44 +09:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 08:42:20 +02:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 17:02:44 +09:00
}
}
/* register a list of div clocks */
2014-03-12 20:26:44 +05:30
void __init samsung_clk_register_div ( struct samsung_clk_provider * ctx ,
2015-05-28 10:45:51 +02:00
const struct samsung_div_clock * list ,
2014-03-12 20:26:44 +05:30
unsigned int nr_clk )
2013-03-09 17:02:44 +09:00
{
2017-04-24 08:42:20 +02:00
struct clk_hw * clk_hw ;
2017-10-03 12:00:16 +02:00
unsigned int idx ;
2013-03-09 17:02:44 +09:00
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2013-03-18 13:43:52 +09:00
if ( list - > table )
2017-08-21 10:05:00 +02:00
clk_hw = clk_hw_register_divider_table ( ctx - > dev ,
2017-04-24 08:42:20 +02:00
list - > name , list - > parent_name , list - > flags ,
2014-03-12 20:26:44 +05:30
ctx - > reg_base + list - > offset ,
list - > shift , list - > width , list - > div_flags ,
list - > table , & ctx - > lock ) ;
2013-03-18 13:43:52 +09:00
else
2017-08-21 10:05:00 +02:00
clk_hw = clk_hw_register_divider ( ctx - > dev , list - > name ,
2014-03-12 20:26:44 +05:30
list - > parent_name , list - > flags ,
ctx - > reg_base + list - > offset , list - > shift ,
list - > width , list - > div_flags , & ctx - > lock ) ;
2017-04-24 08:42:20 +02:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 17:02:44 +09:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 08:42:20 +02:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 17:02:44 +09:00
}
}
/* register a list of gate clocks */
2014-03-12 20:26:44 +05:30
void __init samsung_clk_register_gate ( struct samsung_clk_provider * ctx ,
2015-05-28 10:45:51 +02:00
const struct samsung_gate_clock * list ,
2014-03-12 20:26:44 +05:30
unsigned int nr_clk )
2013-03-09 17:02:44 +09:00
{
2017-04-24 08:42:20 +02:00
struct clk_hw * clk_hw ;
2017-10-03 12:00:16 +02:00
unsigned int idx ;
2013-03-09 17:02:44 +09:00
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 10:05:00 +02:00
clk_hw = clk_hw_register_gate ( ctx - > dev , list - > name , list - > parent_name ,
2014-03-12 20:26:44 +05:30
list - > flags , ctx - > reg_base + list - > offset ,
list - > bit_idx , list - > gate_flags , & ctx - > lock ) ;
2017-04-24 08:42:20 +02:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 17:02:44 +09:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 08:42:20 +02:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 17:02:44 +09:00
}
}
/*
* obtain the clock speed of all external fixed clock sources from device
* tree and register it
*/
2014-03-12 20:26:44 +05:30
void __init samsung_clk_of_register_fixed_ext ( struct samsung_clk_provider * ctx ,
2013-03-09 17:02:44 +09:00
struct samsung_fixed_rate_clock * fixed_rate_clk ,
unsigned int nr_fixed_rate_clk ,
2014-06-26 14:00:06 +02:00
const struct of_device_id * clk_matches )
2013-03-09 17:02:44 +09:00
{
const struct of_device_id * match ;
2014-03-12 20:26:44 +05:30
struct device_node * clk_np ;
2013-03-09 17:02:44 +09:00
u32 freq ;
2014-03-12 20:26:44 +05:30
for_each_matching_node_and_match ( clk_np , clk_matches , & match ) {
if ( of_property_read_u32 ( clk_np , " clock-frequency " , & freq ) )
2013-03-09 17:02:44 +09:00
continue ;
2014-02-26 11:42:41 +09:00
fixed_rate_clk [ ( unsigned long ) match - > data ] . fixed_rate = freq ;
2013-03-09 17:02:44 +09:00
}
2014-03-12 20:26:44 +05:30
samsung_clk_register_fixed_rate ( ctx , fixed_rate_clk , nr_fixed_rate_clk ) ;
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
}
2014-09-22 10:17:02 +05:30
# ifdef CONFIG_PM_SLEEP
static int samsung_clk_suspend ( void )
{
struct samsung_clock_reg_cache * reg_cache ;
2018-09-06 17:55:30 +02:00
list_for_each_entry ( reg_cache , & clock_reg_cache_list , node ) {
2014-09-22 10:17:02 +05:30
samsung_clk_save ( reg_cache - > reg_base , reg_cache - > rdump ,
reg_cache - > rd_num ) ;
2018-09-06 17:55:30 +02:00
samsung_clk_restore ( reg_cache - > reg_base , reg_cache - > rsuspend ,
reg_cache - > rsuspend_num ) ;
}
2014-09-22 10:17:02 +05:30
return 0 ;
}
static void samsung_clk_resume ( void )
{
struct samsung_clock_reg_cache * reg_cache ;
list_for_each_entry ( reg_cache , & clock_reg_cache_list , node )
samsung_clk_restore ( reg_cache - > reg_base , reg_cache - > rdump ,
reg_cache - > rd_num ) ;
}
static struct syscore_ops samsung_clk_syscore_ops = {
. suspend = samsung_clk_suspend ,
. resume = samsung_clk_resume ,
} ;
2018-09-06 17:55:30 +02:00
void samsung_clk_extended_sleep_init ( void __iomem * reg_base ,
2016-05-24 15:19:15 +02:00
const unsigned long * rdump ,
2018-09-06 17:55:30 +02:00
unsigned long nr_rdump ,
const struct samsung_clk_reg_dump * rsuspend ,
unsigned long nr_rsuspend )
2014-09-22 10:17:02 +05:30
{
struct samsung_clock_reg_cache * reg_cache ;
reg_cache = kzalloc ( sizeof ( struct samsung_clock_reg_cache ) ,
GFP_KERNEL ) ;
if ( ! reg_cache )
panic ( " could not allocate register reg_cache. \n " ) ;
reg_cache - > rdump = samsung_clk_alloc_reg_dump ( rdump , nr_rdump ) ;
if ( ! reg_cache - > rdump )
panic ( " could not allocate register dump storage. \n " ) ;
if ( list_empty ( & clock_reg_cache_list ) )
register_syscore_ops ( & samsung_clk_syscore_ops ) ;
reg_cache - > reg_base = reg_base ;
reg_cache - > rd_num = nr_rdump ;
2018-09-06 17:55:30 +02:00
reg_cache - > rsuspend = rsuspend ;
reg_cache - > rsuspend_num = nr_rsuspend ;
2014-09-22 10:17:02 +05:30
list_add_tail ( & reg_cache - > node , & clock_reg_cache_list ) ;
}
# endif
/*
* Common function which registers plls , muxes , dividers and gates
* for each CMU . It also add CMU register list to register cache .
*/
2014-12-23 16:40:21 +09:00
struct samsung_clk_provider * __init samsung_cmu_register_one (
struct device_node * np ,
2016-05-11 14:01:57 +02:00
const struct samsung_cmu_info * cmu )
2014-09-22 10:17:02 +05:30
{
void __iomem * reg_base ;
struct samsung_clk_provider * ctx ;
reg_base = of_iomap ( np , 0 ) ;
2014-12-23 16:40:21 +09:00
if ( ! reg_base ) {
2014-09-22 10:17:02 +05:30
panic ( " %s: failed to map registers \n " , __func__ ) ;
2014-12-23 16:40:21 +09:00
return NULL ;
}
2014-09-22 10:17:02 +05:30
ctx = samsung_clk_init ( np , reg_base , cmu - > nr_clk_ids ) ;
2014-12-23 16:40:21 +09:00
if ( ! ctx ) {
2015-05-21 23:26:03 +05:30
panic ( " %s: unable to allocate ctx \n " , __func__ ) ;
2014-12-23 16:40:21 +09:00
return ctx ;
}
2014-09-22 10:17:02 +05:30
if ( cmu - > pll_clks )
samsung_clk_register_pll ( ctx , cmu - > pll_clks , cmu - > nr_pll_clks ,
reg_base ) ;
if ( cmu - > mux_clks )
samsung_clk_register_mux ( ctx , cmu - > mux_clks ,
cmu - > nr_mux_clks ) ;
if ( cmu - > div_clks )
samsung_clk_register_div ( ctx , cmu - > div_clks , cmu - > nr_div_clks ) ;
if ( cmu - > gate_clks )
samsung_clk_register_gate ( ctx , cmu - > gate_clks ,
cmu - > nr_gate_clks ) ;
if ( cmu - > fixed_clks )
samsung_clk_register_fixed_rate ( ctx , cmu - > fixed_clks ,
cmu - > nr_fixed_clks ) ;
2014-09-22 10:17:03 +05:30
if ( cmu - > fixed_factor_clks )
samsung_clk_register_fixed_factor ( ctx , cmu - > fixed_factor_clks ,
cmu - > nr_fixed_factor_clks ) ;
2014-09-22 10:17:02 +05:30
if ( cmu - > clk_regs )
2018-09-06 17:55:30 +02:00
samsung_clk_extended_sleep_init ( reg_base ,
cmu - > clk_regs , cmu - > nr_clk_regs ,
cmu - > suspend_regs , cmu - > nr_suspend_regs ) ;
2014-09-22 10:17:02 +05:30
samsung_clk_of_add_provider ( np , ctx ) ;
2014-12-23 16:40:21 +09:00
return ctx ;
2014-09-22 10:17:02 +05:30
}