2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-03-09 12:02:44 +04: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-20 01:00:46 +03:00
# include <linux/slab.h>
# include <linux/clkdev.h>
# include <linux/clk.h>
# include <linux/clk-provider.h>
2019-04-19 01:20:22 +03:00
# include <linux/io.h>
2014-09-29 11:47:48 +04:00
# include <linux/of_address.h>
2013-03-09 12:02:44 +04:00
# include <linux/syscore_ops.h>
2014-09-29 11:47:48 +04:00
2013-03-09 12:02:44 +04:00
# include "clk.h"
2014-09-22 08:47:02 +04:00
static LIST_HEAD ( clock_reg_cache_list ) ;
2014-02-14 03:16:00 +04: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 03:16:00 +04:00
struct samsung_clk_reg_dump * samsung_clk_alloc_reg_dump (
const unsigned long * rdump ,
unsigned long nr_rdump )
2014-02-14 03:16:00 +04: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 12:02:44 +04:00
/* setup the essentials required to support clock lookup using ccf */
2014-03-12 18:56:44 +04:00
struct samsung_clk_provider * __init samsung_clk_init ( struct device_node * np ,
void __iomem * base , unsigned long nr_clks )
2013-03-09 12:02:44 +04:00
{
2014-03-12 18:56:44 +04:00
struct samsung_clk_provider * ctx ;
2014-02-06 22:33:11 +04:00
int i ;
2019-09-27 21:51:10 +03:00
ctx = kzalloc ( struct_size ( ctx , clk_data . hws , nr_clks ) , GFP_KERNEL ) ;
2014-03-12 18:56:44 +04:00
if ( ! ctx )
panic ( " could not allocate clock provider context. \n " ) ;
2013-03-09 12:02:44 +04:00
2014-02-06 22:33:11 +04:00
for ( i = 0 ; i < nr_clks ; + + i )
2017-04-24 09:42:20 +03:00
ctx - > clk_data . hws [ i ] = ERR_PTR ( - ENOENT ) ;
2014-02-06 22:33:11 +04:00
2014-03-12 18:56:44 +04:00
ctx - > reg_base = base ;
2017-04-24 09:42:20 +03:00
ctx - > clk_data . num = nr_clks ;
2014-03-12 18:56:44 +04:00
spin_lock_init ( & ctx - > lock ) ;
return ctx ;
2014-06-18 19:46:52 +04:00
}
void __init samsung_clk_of_add_provider ( struct device_node * np ,
struct samsung_clk_provider * ctx )
{
if ( np ) {
2017-04-24 09:42:20 +03:00
if ( of_clk_add_hw_provider ( np , of_clk_hw_onecell_get ,
2014-06-18 19:46:52 +04:00
& ctx - > clk_data ) )
panic ( " could not register clk provider \n " ) ;
}
2013-03-09 12:02:44 +04:00
}
/* add a clock instance to the clock lookup table used for dt based lookup */
2017-04-24 09:42:20 +03:00
void samsung_clk_add_lookup ( struct samsung_clk_provider * ctx ,
struct clk_hw * clk_hw , unsigned int id )
2013-03-09 12:02:44 +04:00
{
2017-04-24 09:42:20 +03:00
if ( id )
ctx - > clk_data . hws [ id ] = clk_hw ;
2013-03-09 12:02:44 +04:00
}
2013-03-18 08:43:56 +04:00
/* register a list of aliases */
2014-03-12 18:56:44 +04:00
void __init samsung_clk_register_alias ( struct samsung_clk_provider * ctx ,
2015-05-28 11:45:51 +03:00
const struct samsung_clock_alias * list ,
2014-03-12 18:56:44 +04:00
unsigned int nr_clk )
2013-03-18 08:43:56 +04:00
{
2017-04-24 09:42:20 +03:00
struct clk_hw * clk_hw ;
2013-03-18 08:43:56 +04: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 09:42:20 +03:00
clk_hw = ctx - > clk_data . hws [ list - > id ] ;
if ( ! clk_hw ) {
2013-03-18 08:43:56 +04:00
pr_err ( " %s: failed to find clock %d \n " , __func__ ,
list - > id ) ;
continue ;
}
2017-04-24 09:42:20 +03:00
ret = clk_hw_register_clkdev ( clk_hw , list - > alias ,
list - > dev_name ) ;
2013-03-18 08:43:56 +04:00
if ( ret )
pr_err ( " %s: failed to register lookup %s \n " ,
__func__ , list - > alias ) ;
}
}
2013-03-09 12:02:44 +04:00
/* register a list of fixed clocks */
2014-03-12 18:56:44 +04:00
void __init samsung_clk_register_fixed_rate ( struct samsung_clk_provider * ctx ,
2015-05-28 11:45:51 +03:00
const struct samsung_fixed_rate_clock * list ,
unsigned int nr_clk )
2013-03-09 12:02:44 +04:00
{
2017-04-24 09:42:20 +03:00
struct clk_hw * clk_hw ;
2013-03-09 12:02:44 +04:00
unsigned int idx , ret ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 11:05:00 +03:00
clk_hw = clk_hw_register_fixed_rate ( ctx - > dev , list - > name ,
2013-03-09 12:02:44 +04:00
list - > parent_name , list - > flags , list - > fixed_rate ) ;
2017-04-24 09:42:20 +03:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 12:02:44 +04:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 09:42:20 +03:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 12:02:44 +04: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 09:42:20 +03:00
ret = clk_hw_register_clkdev ( clk_hw , list - > name , NULL ) ;
2013-03-09 12:02:44 +04: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 18:56:44 +04:00
void __init samsung_clk_register_fixed_factor ( struct samsung_clk_provider * ctx ,
2015-05-28 11:45:51 +03:00
const struct samsung_fixed_factor_clock * list , unsigned int nr_clk )
2013-03-09 12:02:44 +04:00
{
2017-04-24 09:42:20 +03:00
struct clk_hw * clk_hw ;
2013-03-09 12:02:44 +04:00
unsigned int idx ;
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 11:05:00 +03:00
clk_hw = clk_hw_register_fixed_factor ( ctx - > dev , list - > name ,
2013-03-09 12:02:44 +04:00
list - > parent_name , list - > flags , list - > mult , list - > div ) ;
2017-04-24 09:42:20 +03:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 12:02:44 +04:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 09:42:20 +03:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 12:02:44 +04:00
}
}
/* register a list of mux clocks */
2014-03-12 18:56:44 +04:00
void __init samsung_clk_register_mux ( struct samsung_clk_provider * ctx ,
2015-05-28 11:45:51 +03:00
const struct samsung_mux_clock * list ,
2014-03-12 18:56:44 +04:00
unsigned int nr_clk )
2013-03-09 12:02:44 +04:00
{
2017-04-24 09:42:20 +03:00
struct clk_hw * clk_hw ;
2017-10-03 13:00:16 +03:00
unsigned int idx ;
2013-03-09 12:02:44 +04:00
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 11:05:00 +03:00
clk_hw = clk_hw_register_mux ( ctx - > dev , list - > name ,
2017-04-24 09:42:20 +03:00
list - > parent_names , list - > num_parents , list - > flags ,
2014-03-12 18:56:44 +04:00
ctx - > reg_base + list - > offset ,
list - > shift , list - > width , list - > mux_flags , & ctx - > lock ) ;
2017-04-24 09:42:20 +03:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 12:02:44 +04:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 09:42:20 +03:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 12:02:44 +04:00
}
}
/* register a list of div clocks */
2014-03-12 18:56:44 +04:00
void __init samsung_clk_register_div ( struct samsung_clk_provider * ctx ,
2015-05-28 11:45:51 +03:00
const struct samsung_div_clock * list ,
2014-03-12 18:56:44 +04:00
unsigned int nr_clk )
2013-03-09 12:02:44 +04:00
{
2017-04-24 09:42:20 +03:00
struct clk_hw * clk_hw ;
2017-10-03 13:00:16 +03:00
unsigned int idx ;
2013-03-09 12:02:44 +04:00
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2013-03-18 08:43:52 +04:00
if ( list - > table )
2017-08-21 11:05:00 +03:00
clk_hw = clk_hw_register_divider_table ( ctx - > dev ,
2017-04-24 09:42:20 +03:00
list - > name , list - > parent_name , list - > flags ,
2014-03-12 18:56:44 +04:00
ctx - > reg_base + list - > offset ,
list - > shift , list - > width , list - > div_flags ,
list - > table , & ctx - > lock ) ;
2013-03-18 08:43:52 +04:00
else
2017-08-21 11:05:00 +03:00
clk_hw = clk_hw_register_divider ( ctx - > dev , list - > name ,
2014-03-12 18:56:44 +04:00
list - > parent_name , list - > flags ,
ctx - > reg_base + list - > offset , list - > shift ,
list - > width , list - > div_flags , & ctx - > lock ) ;
2017-04-24 09:42:20 +03:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 12:02:44 +04:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 09:42:20 +03:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 12:02:44 +04:00
}
}
/* register a list of gate clocks */
2014-03-12 18:56:44 +04:00
void __init samsung_clk_register_gate ( struct samsung_clk_provider * ctx ,
2015-05-28 11:45:51 +03:00
const struct samsung_gate_clock * list ,
2014-03-12 18:56:44 +04:00
unsigned int nr_clk )
2013-03-09 12:02:44 +04:00
{
2017-04-24 09:42:20 +03:00
struct clk_hw * clk_hw ;
2017-10-03 13:00:16 +03:00
unsigned int idx ;
2013-03-09 12:02:44 +04:00
for ( idx = 0 ; idx < nr_clk ; idx + + , list + + ) {
2017-08-21 11:05:00 +03:00
clk_hw = clk_hw_register_gate ( ctx - > dev , list - > name , list - > parent_name ,
2014-03-12 18:56:44 +04:00
list - > flags , ctx - > reg_base + list - > offset ,
list - > bit_idx , list - > gate_flags , & ctx - > lock ) ;
2017-04-24 09:42:20 +03:00
if ( IS_ERR ( clk_hw ) ) {
2013-03-09 12:02:44 +04:00
pr_err ( " %s: failed to register clock %s \n " , __func__ ,
list - > name ) ;
continue ;
}
2017-04-24 09:42:20 +03:00
samsung_clk_add_lookup ( ctx , clk_hw , list - > id ) ;
2013-03-09 12:02:44 +04:00
}
}
/*
* obtain the clock speed of all external fixed clock sources from device
* tree and register it
*/
2014-03-12 18:56:44 +04:00
void __init samsung_clk_of_register_fixed_ext ( struct samsung_clk_provider * ctx ,
2013-03-09 12:02:44 +04:00
struct samsung_fixed_rate_clock * fixed_rate_clk ,
unsigned int nr_fixed_rate_clk ,
2014-06-26 16:00:06 +04:00
const struct of_device_id * clk_matches )
2013-03-09 12:02:44 +04:00
{
const struct of_device_id * match ;
2014-03-12 18:56:44 +04:00
struct device_node * clk_np ;
2013-03-09 12:02:44 +04:00
u32 freq ;
2014-03-12 18:56:44 +04:00
for_each_matching_node_and_match ( clk_np , clk_matches , & match ) {
if ( of_property_read_u32 ( clk_np , " clock-frequency " , & freq ) )
2013-03-09 12:02:44 +04:00
continue ;
2014-02-26 06:42:41 +04:00
fixed_rate_clk [ ( unsigned long ) match - > data ] . fixed_rate = freq ;
2013-03-09 12:02:44 +04:00
}
2014-03-12 18:56:44 +04:00
samsung_clk_register_fixed_rate ( ctx , fixed_rate_clk , nr_fixed_rate_clk ) ;
2013-03-09 12:02:44 +04:00
}
2014-09-22 08:47:02 +04:00
# ifdef CONFIG_PM_SLEEP
static int samsung_clk_suspend ( void )
{
struct samsung_clock_reg_cache * reg_cache ;
2018-09-06 18:55:30 +03:00
list_for_each_entry ( reg_cache , & clock_reg_cache_list , node ) {
2014-09-22 08:47:02 +04:00
samsung_clk_save ( reg_cache - > reg_base , reg_cache - > rdump ,
reg_cache - > rd_num ) ;
2018-09-06 18:55:30 +03:00
samsung_clk_restore ( reg_cache - > reg_base , reg_cache - > rsuspend ,
reg_cache - > rsuspend_num ) ;
}
2014-09-22 08:47:02 +04:00
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 18:55:30 +03:00
void samsung_clk_extended_sleep_init ( void __iomem * reg_base ,
2016-05-24 16:19:15 +03:00
const unsigned long * rdump ,
2018-09-06 18:55:30 +03:00
unsigned long nr_rdump ,
const struct samsung_clk_reg_dump * rsuspend ,
unsigned long nr_rsuspend )
2014-09-22 08:47:02 +04:00
{
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 18:55:30 +03:00
reg_cache - > rsuspend = rsuspend ;
reg_cache - > rsuspend_num = nr_rsuspend ;
2014-09-22 08:47:02 +04:00
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 10:40:21 +03:00
struct samsung_clk_provider * __init samsung_cmu_register_one (
struct device_node * np ,
2016-05-11 15:01:57 +03:00
const struct samsung_cmu_info * cmu )
2014-09-22 08:47:02 +04:00
{
void __iomem * reg_base ;
struct samsung_clk_provider * ctx ;
reg_base = of_iomap ( np , 0 ) ;
2014-12-23 10:40:21 +03:00
if ( ! reg_base ) {
2014-09-22 08:47:02 +04:00
panic ( " %s: failed to map registers \n " , __func__ ) ;
2014-12-23 10:40:21 +03:00
return NULL ;
}
2014-09-22 08:47:02 +04:00
ctx = samsung_clk_init ( np , reg_base , cmu - > nr_clk_ids ) ;
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 08:47:03 +04:00
if ( cmu - > fixed_factor_clks )
samsung_clk_register_fixed_factor ( ctx , cmu - > fixed_factor_clks ,
cmu - > nr_fixed_factor_clks ) ;
2014-09-22 08:47:02 +04:00
if ( cmu - > clk_regs )
2018-09-06 18:55:30 +03:00
samsung_clk_extended_sleep_init ( reg_base ,
cmu - > clk_regs , cmu - > nr_clk_regs ,
cmu - > suspend_regs , cmu - > nr_suspend_regs ) ;
2021-10-14 22:53:45 +03:00
if ( cmu - > cpu_clks )
samsung_clk_register_cpu ( ctx , cmu - > cpu_clks , cmu - > nr_cpu_clks ) ;
2014-09-22 08:47:02 +04:00
samsung_clk_of_add_provider ( np , ctx ) ;
2014-12-23 10:40:21 +03:00
return ctx ;
2014-09-22 08:47:02 +04:00
}