2012-03-16 10:11:20 +04:00
/*
* Copyright ( C ) 2010 - 2011 Canonical Ltd < jeremy . kerr @ canonical . com >
* Copyright ( C ) 2011 - 2012 Mike Turquette , Linaro Ltd < mturquette @ linaro . org >
*
* 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 .
*
* Gated clock implementation
*/
# include <linux/clk-provider.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/err.h>
# include <linux/string.h>
/**
* DOC : basic gatable clock which can gate and ungate it ' s ouput
*
* Traits of this clock :
* prepare - clk_ ( un ) prepare only ensures parent is ( un ) prepared
* enable - clk_enable and clk_disable are functional & control gating
* rate - inherits rate from parent . No clk_set_rate support
* parent - fixed parent . No clk_set_parent support
*/
2012-04-17 15:15:37 +04:00
/*
* It works on following logic :
*
* For enabling clock , enable = 1
* set2dis = 1 - > clear bit - > set = 0
* set2dis = 0 - > set bit - > set = 1
*
* For disabling clock , enable = 0
* set2dis = 1 - > set bit - > set = 1
* set2dis = 0 - > clear bit - > set = 0
*
* So , result is always : enable xor set2dis .
*/
static void clk_gate_endisable ( struct clk_hw * hw , int enable )
2012-03-16 10:11:20 +04:00
{
2012-04-17 15:15:37 +04:00
struct clk_gate * gate = to_clk_gate ( hw ) ;
int set = gate - > flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0 ;
2014-09-22 09:52:11 +04:00
unsigned long uninitialized_var ( flags ) ;
2012-04-17 15:15:37 +04:00
u32 reg ;
set ^ = enable ;
2012-03-16 10:11:20 +04:00
if ( gate - > lock )
spin_lock_irqsave ( gate - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__acquire ( gate - > lock ) ;
2012-03-16 10:11:20 +04:00
2013-06-08 18:47:19 +04:00
if ( gate - > flags & CLK_GATE_HIWORD_MASK ) {
reg = BIT ( gate - > bit_idx + 16 ) ;
if ( set )
reg | = BIT ( gate - > bit_idx ) ;
} else {
2013-07-22 16:14:40 +04:00
reg = clk_readl ( gate - > reg ) ;
2013-06-08 18:47:19 +04:00
if ( set )
reg | = BIT ( gate - > bit_idx ) ;
else
reg & = ~ BIT ( gate - > bit_idx ) ;
}
2012-03-16 10:11:20 +04:00
2013-07-22 16:14:40 +04:00
clk_writel ( reg , gate - > reg ) ;
2012-03-16 10:11:20 +04:00
if ( gate - > lock )
spin_unlock_irqrestore ( gate - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__release ( gate - > lock ) ;
2012-03-16 10:11:20 +04:00
}
static int clk_gate_enable ( struct clk_hw * hw )
{
2012-04-17 15:15:37 +04:00
clk_gate_endisable ( hw , 1 ) ;
2012-03-16 10:11:20 +04:00
return 0 ;
}
static void clk_gate_disable ( struct clk_hw * hw )
{
2012-04-17 15:15:37 +04:00
clk_gate_endisable ( hw , 0 ) ;
2012-03-16 10:11:20 +04:00
}
2017-08-21 14:59:01 +03:00
int clk_gate_is_enabled ( struct clk_hw * hw )
2012-03-16 10:11:20 +04:00
{
u32 reg ;
struct clk_gate * gate = to_clk_gate ( hw ) ;
2013-07-22 16:14:40 +04:00
reg = clk_readl ( gate - > reg ) ;
2012-03-16 10:11:20 +04:00
/* if a set bit disables this clk, flip it before masking */
if ( gate - > flags & CLK_GATE_SET_TO_DISABLE )
reg ^ = BIT ( gate - > bit_idx ) ;
reg & = BIT ( gate - > bit_idx ) ;
return reg ? 1 : 0 ;
}
2017-08-21 14:59:01 +03:00
EXPORT_SYMBOL_GPL ( clk_gate_is_enabled ) ;
2012-03-16 10:11:20 +04:00
2012-03-27 11:23:22 +04:00
const struct clk_ops clk_gate_ops = {
2012-03-16 10:11:20 +04:00
. enable = clk_gate_enable ,
. disable = clk_gate_disable ,
. is_enabled = clk_gate_is_enabled ,
} ;
EXPORT_SYMBOL_GPL ( clk_gate_ops ) ;
2012-03-27 04:51:03 +04:00
/**
2016-02-07 10:54:45 +03:00
* clk_hw_register_gate - register a gate clock with the clock framework
2012-03-27 04:51:03 +04:00
* @ dev : device that is registering this clock
* @ name : name of this clock
* @ parent_name : name of this clock ' s parent
* @ flags : framework - specific flags for this clock
* @ reg : register address to control gating of this clock
* @ bit_idx : which bit in the register controls gating of this clock
* @ clk_gate_flags : gate - specific flags for this clock
* @ lock : shared register lock for this clock
*/
2016-02-07 10:54:45 +03:00
struct clk_hw * clk_hw_register_gate ( struct device * dev , const char * name ,
2012-03-16 10:11:20 +04:00
const char * parent_name , unsigned long flags ,
void __iomem * reg , u8 bit_idx ,
u8 clk_gate_flags , spinlock_t * lock )
{
struct clk_gate * gate ;
2016-02-07 10:54:45 +03:00
struct clk_hw * hw ;
2012-04-26 09:58:56 +04:00
struct clk_init_data init ;
2016-02-07 10:54:45 +03:00
int ret ;
2012-03-16 10:11:20 +04:00
2013-06-08 18:47:19 +04:00
if ( clk_gate_flags & CLK_GATE_HIWORD_MASK ) {
2014-12-24 17:43:27 +03:00
if ( bit_idx > 15 ) {
2013-06-08 18:47:19 +04:00
pr_err ( " gate bit exceeds LOWORD field \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
}
2012-03-27 04:51:03 +04:00
/* allocate the gate */
2015-05-15 02:47:10 +03:00
gate = kzalloc ( sizeof ( * gate ) , GFP_KERNEL ) ;
if ( ! gate )
2012-03-27 04:51:03 +04:00
return ERR_PTR ( - ENOMEM ) ;
2012-03-16 10:11:20 +04:00
2012-04-26 09:58:56 +04:00
init . name = name ;
init . ops = & clk_gate_ops ;
2012-06-01 12:32:47 +04:00
init . flags = flags | CLK_IS_BASIC ;
2016-11-09 14:00:46 +03:00
init . parent_names = parent_name ? & parent_name : NULL ;
init . num_parents = parent_name ? 1 : 0 ;
2012-04-26 09:58:56 +04:00
2012-03-16 10:11:20 +04:00
/* struct clk_gate assignments */
gate - > reg = reg ;
gate - > bit_idx = bit_idx ;
gate - > flags = clk_gate_flags ;
gate - > lock = lock ;
2012-04-26 09:58:56 +04:00
gate - > hw . init = & init ;
2012-03-16 10:11:20 +04:00
2016-02-07 10:54:45 +03:00
hw = & gate - > hw ;
ret = clk_hw_register ( dev , hw ) ;
if ( ret ) {
2012-03-27 04:51:03 +04:00
kfree ( gate ) ;
2016-02-07 10:54:45 +03:00
hw = ERR_PTR ( ret ) ;
}
2012-03-27 04:51:03 +04:00
2016-02-07 10:54:45 +03:00
return hw ;
}
EXPORT_SYMBOL_GPL ( clk_hw_register_gate ) ;
struct clk * clk_register_gate ( struct device * dev , const char * name ,
const char * parent_name , unsigned long flags ,
void __iomem * reg , u8 bit_idx ,
u8 clk_gate_flags , spinlock_t * lock )
{
struct clk_hw * hw ;
hw = clk_hw_register_gate ( dev , name , parent_name , flags , reg ,
bit_idx , clk_gate_flags , lock ) ;
if ( IS_ERR ( hw ) )
return ERR_CAST ( hw ) ;
return hw - > clk ;
2012-03-16 10:11:20 +04:00
}
2013-08-16 06:06:29 +04:00
EXPORT_SYMBOL_GPL ( clk_register_gate ) ;
2015-01-05 12:52:40 +03:00
void clk_unregister_gate ( struct clk * clk )
{
struct clk_gate * gate ;
struct clk_hw * hw ;
hw = __clk_get_hw ( clk ) ;
if ( ! hw )
return ;
gate = to_clk_gate ( hw ) ;
clk_unregister ( clk ) ;
kfree ( gate ) ;
}
EXPORT_SYMBOL_GPL ( clk_unregister_gate ) ;
2016-02-07 10:54:45 +03:00
void clk_hw_unregister_gate ( struct clk_hw * hw )
{
struct clk_gate * gate ;
gate = to_clk_gate ( hw ) ;
clk_hw_unregister ( hw ) ;
kfree ( gate ) ;
}
EXPORT_SYMBOL_GPL ( clk_hw_unregister_gate ) ;