2018-09-25 10:34:05 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-10-18 01:54:07 +04:00
/*
* R - Car MSTP clocks
*
* Copyright ( C ) 2013 Ideas On Board SPRL
2015-08-04 15:28:02 +03:00
* Copyright ( C ) 2015 Glider bvba
2013-10-18 01:54:07 +04:00
*
* Contact : Laurent Pinchart < laurent . pinchart @ ideasonboard . com >
*/
2015-08-04 15:28:02 +03:00
# include <linux/clk.h>
2013-10-18 01:54:07 +04:00
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
2016-03-08 03:42:07 +03:00
# include <linux/clk/renesas.h>
2015-08-04 15:28:02 +03:00
# include <linux/device.h>
2013-10-18 01:54:07 +04:00
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
2015-08-04 15:28:02 +03:00
# include <linux/pm_clock.h>
# include <linux/pm_domain.h>
2013-10-18 01:54:07 +04:00
# include <linux/spinlock.h>
/*
* MSTP clocks . We can ' t use standard gate clocks as we need to poll on the
* status register when enabling the clock .
*/
# define MSTP_MAX_CLOCKS 32
/**
* struct mstp_clock_group - MSTP gating clocks group
*
2019-06-12 18:25:39 +03:00
* @ data : clock specifier translation for clocks in this group
2013-10-18 01:54:07 +04:00
* @ smstpcr : module stop control register
* @ mstpsr : module stop status register ( optional )
* @ lock : protects writes to SMSTPCR
2016-12-15 20:00:27 +03:00
* @ width_8bit : registers are 8 - bit , not 32 - bit
2019-06-12 18:25:39 +03:00
* @ clks : clocks in this group
2013-10-18 01:54:07 +04:00
*/
struct mstp_clock_group {
struct clk_onecell_data data ;
void __iomem * smstpcr ;
void __iomem * mstpsr ;
spinlock_t lock ;
2016-12-15 20:00:27 +03:00
bool width_8bit ;
2019-06-12 18:25:39 +03:00
struct clk * clks [ ] ;
2013-10-18 01:54:07 +04:00
} ;
/**
* struct mstp_clock - MSTP gating clock
* @ hw : handle between common and hardware - specific interfaces
* @ bit_index : control bit index
* @ group : MSTP clocks group
*/
struct mstp_clock {
struct clk_hw hw ;
u32 bit_index ;
struct mstp_clock_group * group ;
} ;
# define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw)
2016-12-15 20:00:27 +03:00
static inline u32 cpg_mstp_read ( struct mstp_clock_group * group ,
u32 __iomem * reg )
{
2018-03-15 12:43:37 +03:00
return group - > width_8bit ? readb ( reg ) : readl ( reg ) ;
2016-12-15 20:00:27 +03:00
}
static inline void cpg_mstp_write ( struct mstp_clock_group * group , u32 val ,
u32 __iomem * reg )
{
2018-03-15 12:43:37 +03:00
group - > width_8bit ? writeb ( val , reg ) : writel ( val , reg ) ;
2016-12-15 20:00:27 +03:00
}
2013-10-18 01:54:07 +04:00
static int cpg_mstp_clock_endisable ( struct clk_hw * hw , bool enable )
{
struct mstp_clock * clock = to_mstp_clock ( hw ) ;
struct mstp_clock_group * group = clock - > group ;
u32 bitmask = BIT ( clock - > bit_index ) ;
unsigned long flags ;
unsigned int i ;
u32 value ;
spin_lock_irqsave ( & group - > lock , flags ) ;
2016-12-15 20:00:27 +03:00
value = cpg_mstp_read ( group , group - > smstpcr ) ;
2013-10-18 01:54:07 +04:00
if ( enable )
value & = ~ bitmask ;
else
value | = bitmask ;
2016-12-15 20:00:27 +03:00
cpg_mstp_write ( group , value , group - > smstpcr ) ;
2013-10-18 01:54:07 +04:00
2017-02-14 19:08:05 +03:00
if ( ! group - > mstpsr ) {
/* dummy read to ensure write has completed */
cpg_mstp_read ( group , group - > smstpcr ) ;
barrier_data ( group - > smstpcr ) ;
}
2013-10-18 01:54:07 +04:00
spin_unlock_irqrestore ( & group - > lock , flags ) ;
if ( ! enable | | ! group - > mstpsr )
return 0 ;
for ( i = 1000 ; i > 0 ; - - i ) {
2016-12-15 20:00:27 +03:00
if ( ! ( cpg_mstp_read ( group , group - > mstpsr ) & bitmask ) )
2013-10-18 01:54:07 +04:00
break ;
cpu_relax ( ) ;
}
if ( ! i ) {
pr_err ( " %s: failed to enable %p[%d] \n " , __func__ ,
group - > smstpcr , clock - > bit_index ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
static int cpg_mstp_clock_enable ( struct clk_hw * hw )
{
return cpg_mstp_clock_endisable ( hw , true ) ;
}
static void cpg_mstp_clock_disable ( struct clk_hw * hw )
{
cpg_mstp_clock_endisable ( hw , false ) ;
}
static int cpg_mstp_clock_is_enabled ( struct clk_hw * hw )
{
struct mstp_clock * clock = to_mstp_clock ( hw ) ;
struct mstp_clock_group * group = clock - > group ;
u32 value ;
if ( group - > mstpsr )
2016-12-15 20:00:27 +03:00
value = cpg_mstp_read ( group , group - > mstpsr ) ;
2013-10-18 01:54:07 +04:00
else
2016-12-15 20:00:27 +03:00
value = cpg_mstp_read ( group , group - > smstpcr ) ;
2013-10-18 01:54:07 +04:00
2014-05-22 22:02:14 +04:00
return ! ( value & BIT ( clock - > bit_index ) ) ;
2013-10-18 01:54:07 +04:00
}
static const struct clk_ops cpg_mstp_clock_ops = {
. enable = cpg_mstp_clock_enable ,
. disable = cpg_mstp_clock_disable ,
. is_enabled = cpg_mstp_clock_is_enabled ,
} ;
2017-01-23 12:48:21 +03:00
static struct clk * __init cpg_mstp_clock_register ( const char * name ,
const char * parent_name , unsigned int index ,
struct mstp_clock_group * group )
2013-10-18 01:54:07 +04:00
{
struct clk_init_data init ;
struct mstp_clock * clock ;
struct clk * clk ;
clock = kzalloc ( sizeof ( * clock ) , GFP_KERNEL ) ;
2017-09-25 11:10:51 +03:00
if ( ! clock )
2013-10-18 01:54:07 +04:00
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & cpg_mstp_clock_ops ;
2018-11-30 22:05:35 +03:00
init . flags = CLK_SET_RATE_PARENT ;
2015-03-17 19:20:52 +03:00
/* INTC-SYS is the module clock of the GIC, and must not be disabled */
if ( ! strcmp ( name , " intc-sys " ) ) {
pr_debug ( " MSTP %s setting CLK_IS_CRITICAL \n " , name ) ;
init . flags | = CLK_IS_CRITICAL ;
}
2013-10-18 01:54:07 +04:00
init . parent_names = & parent_name ;
init . num_parents = 1 ;
clock - > bit_index = index ;
clock - > group = group ;
clock - > hw . init = & init ;
clk = clk_register ( NULL , & clock - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( clock ) ;
return clk ;
}
static void __init cpg_mstp_clocks_init ( struct device_node * np )
{
struct mstp_clock_group * group ;
2014-04-15 20:06:34 +04:00
const char * idxname ;
2013-10-18 01:54:07 +04:00
struct clk * * clks ;
unsigned int i ;
2019-06-12 18:25:39 +03:00
group = kzalloc ( struct_size ( group , clks , MSTP_MAX_CLOCKS ) , GFP_KERNEL ) ;
if ( group = = NULL ) {
2013-10-18 01:54:07 +04:00
kfree ( group ) ;
return ;
}
2019-06-12 18:25:39 +03:00
clks = group - > clks ;
2013-10-18 01:54:07 +04:00
spin_lock_init ( & group - > lock ) ;
group - > data . clks = clks ;
group - > smstpcr = of_iomap ( np , 0 ) ;
group - > mstpsr = of_iomap ( np , 1 ) ;
if ( group - > smstpcr = = NULL ) {
pr_err ( " %s: failed to remap SMSTPCR \n " , __func__ ) ;
kfree ( group ) ;
return ;
}
2016-12-15 20:00:27 +03:00
if ( of_device_is_compatible ( np , " renesas,r7s72100-mstp-clocks " ) )
group - > width_8bit = true ;
2013-12-28 16:09:09 +04:00
for ( i = 0 ; i < MSTP_MAX_CLOCKS ; + + i )
clks [ i ] = ERR_PTR ( - ENOENT ) ;
2014-04-15 20:06:34 +04:00
if ( of_find_property ( np , " clock-indices " , & i ) )
idxname = " clock-indices " ;
else
idxname = " renesas,clock-indices " ;
2013-10-18 01:54:07 +04:00
for ( i = 0 ; i < MSTP_MAX_CLOCKS ; + + i ) {
const char * parent_name ;
const char * name ;
u32 clkidx ;
int ret ;
/* Skip clocks with no name. */
ret = of_property_read_string_index ( np , " clock-output-names " ,
i , & name ) ;
if ( ret < 0 | | strlen ( name ) = = 0 )
continue ;
parent_name = of_clk_get_parent_name ( np , i ) ;
2014-04-15 20:06:34 +04:00
ret = of_property_read_u32_index ( np , idxname , i , & clkidx ) ;
2013-10-18 01:54:07 +04:00
if ( parent_name = = NULL | | ret < 0 )
break ;
if ( clkidx > = MSTP_MAX_CLOCKS ) {
2018-08-28 18:44:29 +03:00
pr_err ( " %s: invalid clock %pOFn %s index %u \n " ,
__func__ , np , name , clkidx ) ;
2013-10-18 01:54:07 +04:00
continue ;
}
2013-12-28 16:09:08 +04:00
clks [ clkidx ] = cpg_mstp_clock_register ( name , parent_name ,
clkidx , group ) ;
2013-10-18 01:54:07 +04:00
if ( ! IS_ERR ( clks [ clkidx ] ) ) {
2013-12-28 16:09:09 +04:00
group - > data . clk_num = max ( group - > data . clk_num ,
clkidx + 1 ) ;
2013-10-18 01:54:07 +04:00
/*
* Register a clkdev to let board code retrieve the
* clock by name and register aliases for non - DT
* devices .
*
* FIXME : Remove this when all devices that require a
* clock will be instantiated from DT .
*/
clk_register_clkdev ( clks [ clkidx ] , name , NULL ) ;
} else {
2018-08-28 18:44:29 +03:00
pr_err ( " %s: failed to register %pOFn %s clock (%ld) \n " ,
__func__ , np , name , PTR_ERR ( clks [ clkidx ] ) ) ;
2013-10-18 01:54:07 +04:00
}
}
of_clk_add_provider ( np , of_clk_src_onecell_get , & group - > data ) ;
}
CLK_OF_DECLARE ( cpg_mstp_clks , " renesas,cpg-mstp-clocks " , cpg_mstp_clocks_init ) ;
2015-08-04 15:28:02 +03:00
2016-03-04 18:59:26 +03:00
int cpg_mstp_attach_dev ( struct generic_pm_domain * unused , struct device * dev )
2015-08-04 15:28:02 +03:00
{
struct device_node * np = dev - > of_node ;
struct of_phandle_args clkspec ;
struct clk * clk ;
int i = 0 ;
int error ;
while ( ! of_parse_phandle_with_args ( np , " clocks " , " #clock-cells " , i ,
& clkspec ) ) {
if ( of_device_is_compatible ( clkspec . np ,
" renesas,cpg-mstp-clocks " ) )
goto found ;
2015-08-04 16:25:35 +03:00
/* BSC on r8a73a4/sh73a0 uses zb_clk instead of an mstp clock */
2018-12-05 22:50:21 +03:00
if ( of_node_name_eq ( clkspec . np , " zb_clk " ) )
2015-08-04 16:25:35 +03:00
goto found ;
2015-08-04 15:28:02 +03:00
of_node_put ( clkspec . np ) ;
i + + ;
}
return 0 ;
found :
clk = of_clk_get_from_provider ( & clkspec ) ;
of_node_put ( clkspec . np ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
error = pm_clk_create ( dev ) ;
2019-05-27 12:24:16 +03:00
if ( error )
2015-08-04 15:28:02 +03:00
goto fail_put ;
error = pm_clk_add_clk ( dev , clk ) ;
2019-05-27 12:24:16 +03:00
if ( error )
2015-08-04 15:28:02 +03:00
goto fail_destroy ;
return 0 ;
fail_destroy :
pm_clk_destroy ( dev ) ;
fail_put :
clk_put ( clk ) ;
return error ;
}
2016-03-04 18:59:26 +03:00
void cpg_mstp_detach_dev ( struct generic_pm_domain * unused , struct device * dev )
2015-08-04 15:28:02 +03:00
{
2017-02-08 21:08:44 +03:00
if ( ! pm_clk_no_clocks ( dev ) )
2015-08-04 15:28:02 +03:00
pm_clk_destroy ( dev ) ;
}
void __init cpg_mstp_add_clk_domain ( struct device_node * np )
{
struct generic_pm_domain * pd ;
u32 ncells ;
if ( of_property_read_u32 ( np , " #power-domain-cells " , & ncells ) ) {
2017-07-19 00:42:52 +03:00
pr_warn ( " %pOF lacks #power-domain-cells \n " , np ) ;
2015-08-04 15:28:02 +03:00
return ;
}
pd = kzalloc ( sizeof ( * pd ) , GFP_KERNEL ) ;
if ( ! pd )
return ;
pd - > name = np - > name ;
2019-08-09 16:38:34 +03:00
pd - > flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON |
GENPD_FLAG_ACTIVE_WAKEUP ;
2015-08-04 15:28:02 +03:00
pd - > attach_dev = cpg_mstp_attach_dev ;
pd - > detach_dev = cpg_mstp_detach_dev ;
2016-04-22 15:57:29 +03:00
pm_genpd_init ( pd , & pm_domain_always_on_gov , false ) ;
2015-08-04 15:28:02 +03:00
of_genpd_add_provider_simple ( np , pd ) ;
}