2013-09-26 05:18:14 +04:00
/*
* Clock driver for Keystone 2 based devices
*
* Copyright ( C ) 2013 Texas Instruments .
* Murali Karicheri < m - karicheri2 @ ti . com >
* Santosh Shilimkar < santosh . shilimkar @ ti . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/clk-provider.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/of_address.h>
# include <linux/of.h>
# include <linux/module.h>
/* PSC register offsets */
# define PTCMD 0x120
# define PTSTAT 0x128
# define PDSTAT 0x200
# define PDCTL 0x300
# define MDSTAT 0x800
# define MDCTL 0xa00
/* PSC module states */
# define PSC_STATE_SWRSTDISABLE 0
# define PSC_STATE_SYNCRST 1
# define PSC_STATE_DISABLE 2
# define PSC_STATE_ENABLE 3
# define MDSTAT_STATE_MASK 0x3f
# define MDSTAT_MCKOUT BIT(12)
# define PDSTAT_STATE_MASK 0x1f
# define MDCTL_FORCE BIT(31)
# define MDCTL_LRESET BIT(8)
# define PDCTL_NEXT BIT(0)
/* Maximum timeout to bail out state transition for module */
# define STATE_TRANS_MAX_COUNT 0xffff
static void __iomem * domain_transition_base ;
/**
* struct clk_psc_data - PSC data
* @ control_base : Base address for a PSC control
* @ domain_base : Base address for a PSC domain
* @ domain_id : PSC domain id number
*/
struct clk_psc_data {
void __iomem * control_base ;
void __iomem * domain_base ;
u32 domain_id ;
} ;
/**
* struct clk_psc - PSC clock structure
* @ hw : clk_hw for the psc
* @ psc_data : PSC driver specific data
* @ lock : Spinlock used by the driver
*/
struct clk_psc {
struct clk_hw hw ;
struct clk_psc_data * psc_data ;
spinlock_t * lock ;
} ;
static DEFINE_SPINLOCK ( psc_lock ) ;
# define to_clk_psc(_hw) container_of(_hw, struct clk_psc, hw)
static void psc_config ( void __iomem * control_base , void __iomem * domain_base ,
u32 next_state , u32 domain_id )
{
u32 ptcmd , pdstat , pdctl , mdstat , mdctl , ptstat ;
u32 count = STATE_TRANS_MAX_COUNT ;
mdctl = readl ( control_base + MDCTL ) ;
mdctl & = ~ MDSTAT_STATE_MASK ;
mdctl | = next_state ;
/* For disable, we always put the module in local reset */
if ( next_state = = PSC_STATE_DISABLE )
mdctl & = ~ MDCTL_LRESET ;
writel ( mdctl , control_base + MDCTL ) ;
pdstat = readl ( domain_base + PDSTAT ) ;
if ( ! ( pdstat & PDSTAT_STATE_MASK ) ) {
pdctl = readl ( domain_base + PDCTL ) ;
pdctl | = PDCTL_NEXT ;
writel ( pdctl , domain_base + PDCTL ) ;
}
ptcmd = 1 < < domain_id ;
writel ( ptcmd , domain_transition_base + PTCMD ) ;
do {
ptstat = readl ( domain_transition_base + PTSTAT ) ;
} while ( ( ( ptstat > > domain_id ) & 1 ) & & count - - ) ;
count = STATE_TRANS_MAX_COUNT ;
do {
mdstat = readl ( control_base + MDSTAT ) ;
} while ( ! ( ( mdstat & MDSTAT_STATE_MASK ) = = next_state ) & & count - - ) ;
}
static int keystone_clk_is_enabled ( struct clk_hw * hw )
{
struct clk_psc * psc = to_clk_psc ( hw ) ;
struct clk_psc_data * data = psc - > psc_data ;
u32 mdstat = readl ( data - > control_base + MDSTAT ) ;
return ( mdstat & MDSTAT_MCKOUT ) ? 1 : 0 ;
}
static int keystone_clk_enable ( struct clk_hw * hw )
{
struct clk_psc * psc = to_clk_psc ( hw ) ;
struct clk_psc_data * data = psc - > psc_data ;
unsigned long flags = 0 ;
if ( psc - > lock )
spin_lock_irqsave ( psc - > lock , flags ) ;
psc_config ( data - > control_base , data - > domain_base ,
PSC_STATE_ENABLE , data - > domain_id ) ;
if ( psc - > lock )
spin_unlock_irqrestore ( psc - > lock , flags ) ;
return 0 ;
}
static void keystone_clk_disable ( struct clk_hw * hw )
{
struct clk_psc * psc = to_clk_psc ( hw ) ;
struct clk_psc_data * data = psc - > psc_data ;
unsigned long flags = 0 ;
if ( psc - > lock )
spin_lock_irqsave ( psc - > lock , flags ) ;
psc_config ( data - > control_base , data - > domain_base ,
PSC_STATE_DISABLE , data - > domain_id ) ;
if ( psc - > lock )
spin_unlock_irqrestore ( psc - > lock , flags ) ;
}
static const struct clk_ops clk_psc_ops = {
. enable = keystone_clk_enable ,
. disable = keystone_clk_disable ,
. is_enabled = keystone_clk_is_enabled ,
} ;
/**
* clk_register_psc - register psc clock
* @ dev : device that is registering this clock
* @ name : name of this clock
* @ parent_name : name of clock ' s parent
* @ psc_data : platform data to configure this clock
* @ lock : spinlock used by this clock
*/
static struct clk * clk_register_psc ( struct device * dev ,
const char * name ,
const char * parent_name ,
struct clk_psc_data * psc_data ,
spinlock_t * lock )
{
struct clk_init_data init ;
struct clk_psc * psc ;
struct clk * clk ;
psc = kzalloc ( sizeof ( * psc ) , GFP_KERNEL ) ;
if ( ! psc )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & clk_psc_ops ;
2014-01-28 14:49:15 +04:00
init . flags = 0 ;
2013-09-26 05:18:14 +04:00
init . parent_names = ( parent_name ? & parent_name : NULL ) ;
init . num_parents = ( parent_name ? 1 : 0 ) ;
psc - > psc_data = psc_data ;
psc - > lock = lock ;
psc - > hw . init = & init ;
clk = clk_register ( NULL , & psc - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( psc ) ;
return clk ;
}
/**
* of_psc_clk_init - initialize psc clock through DT
* @ node : device tree node for this clock
* @ lock : spinlock used by this clock
*/
static void __init of_psc_clk_init ( struct device_node * node , spinlock_t * lock )
{
const char * clk_name = node - > name ;
const char * parent_name ;
struct clk_psc_data * data ;
struct clk * clk ;
int i ;
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data ) {
pr_err ( " %s: Out of memory \n " , __func__ ) ;
return ;
}
i = of_property_match_string ( node , " reg-names " , " control " ) ;
data - > control_base = of_iomap ( node , i ) ;
if ( ! data - > control_base ) {
pr_err ( " %s: control ioremap failed \n " , __func__ ) ;
goto out ;
}
i = of_property_match_string ( node , " reg-names " , " domain " ) ;
data - > domain_base = of_iomap ( node , i ) ;
if ( ! data - > domain_base ) {
pr_err ( " %s: domain ioremap failed \n " , __func__ ) ;
2013-11-24 01:31:12 +04:00
goto unmap_ctrl ;
2013-09-26 05:18:14 +04:00
}
of_property_read_u32 ( node , " domain-id " , & data - > domain_id ) ;
/* Domain transition registers at fixed address space of domain_id 0 */
if ( ! domain_transition_base & & ! data - > domain_id )
domain_transition_base = data - > domain_base ;
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
parent_name = of_clk_get_parent_name ( node , 0 ) ;
if ( ! parent_name ) {
pr_err ( " %s: Parent clock not found \n " , __func__ ) ;
2013-11-24 01:31:12 +04:00
goto unmap_domain ;
2013-09-26 05:18:14 +04:00
}
clk = clk_register_psc ( NULL , clk_name , parent_name , data , lock ) ;
2013-11-24 01:31:12 +04:00
if ( ! IS_ERR ( clk ) ) {
2013-09-26 05:18:14 +04:00
of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
return ;
}
pr_err ( " %s: error registering clk %s \n " , __func__ , node - > name ) ;
2013-11-24 01:31:12 +04:00
unmap_domain :
iounmap ( data - > domain_base ) ;
unmap_ctrl :
iounmap ( data - > control_base ) ;
2013-09-26 05:18:14 +04:00
out :
kfree ( data ) ;
return ;
}
/**
* of_keystone_psc_clk_init - initialize psc clock through DT
* @ node : device tree node for this clock
*/
static void __init of_keystone_psc_clk_init ( struct device_node * node )
{
of_psc_clk_init ( node , & psc_lock ) ;
}
CLK_OF_DECLARE ( keystone_gate_clk , " ti,keystone,psc-clock " ,
of_keystone_psc_clk_init ) ;