2013-10-11 13:41:41 +04:00
/*
* Copyright ( C ) 2013 Boris BREZILLON < b . brezillon @ overkiz . 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/clkdev.h>
# include <linux/clk/at91_pmc.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/io.h>
2014-03-11 13:00:34 +04:00
# include <linux/irq.h>
# include <linux/of_irq.h>
# include <linux/interrupt.h>
# include <linux/wait.h>
# include <linux/sched.h>
2013-10-11 13:41:41 +04:00
# include "pmc.h"
# define SYSTEM_MAX_ID 31
# define SYSTEM_MAX_NAME_SZ 32
# define to_clk_system(hw) container_of(hw, struct clk_system, hw)
struct clk_system {
struct clk_hw hw ;
struct at91_pmc * pmc ;
2014-03-11 13:00:34 +04:00
unsigned int irq ;
wait_queue_head_t wait ;
2013-10-11 13:41:41 +04:00
u8 id ;
} ;
2014-03-11 13:00:34 +04:00
static inline int is_pck ( int id )
{
return ( id > = 8 ) & & ( id < = 15 ) ;
}
static irqreturn_t clk_system_irq_handler ( int irq , void * dev_id )
{
struct clk_system * sys = ( struct clk_system * ) dev_id ;
wake_up ( & sys - > wait ) ;
disable_irq_nosync ( sys - > irq ) ;
return IRQ_HANDLED ;
}
static int clk_system_prepare ( struct clk_hw * hw )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys = to_clk_system ( hw ) ;
struct at91_pmc * pmc = sys - > pmc ;
2014-03-11 13:00:34 +04:00
u32 mask = 1 < < sys - > id ;
2013-10-11 13:41:41 +04:00
2014-03-11 13:00:34 +04:00
pmc_write ( pmc , AT91_PMC_SCER , mask ) ;
if ( ! is_pck ( sys - > id ) )
return 0 ;
while ( ! ( pmc_read ( pmc , AT91_PMC_SR ) & mask ) ) {
if ( sys - > irq ) {
enable_irq ( sys - > irq ) ;
wait_event ( sys - > wait ,
pmc_read ( pmc , AT91_PMC_SR ) & mask ) ;
} else
cpu_relax ( ) ;
}
2013-10-11 13:41:41 +04:00
return 0 ;
}
2014-03-11 13:00:34 +04:00
static void clk_system_unprepare ( struct clk_hw * hw )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys = to_clk_system ( hw ) ;
struct at91_pmc * pmc = sys - > pmc ;
pmc_write ( pmc , AT91_PMC_SCDR , 1 < < sys - > id ) ;
}
2014-03-11 13:00:34 +04:00
static int clk_system_is_prepared ( struct clk_hw * hw )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys = to_clk_system ( hw ) ;
struct at91_pmc * pmc = sys - > pmc ;
2014-03-11 13:00:34 +04:00
if ( ! ( pmc_read ( pmc , AT91_PMC_SCSR ) & ( 1 < < sys - > id ) ) )
return 0 ;
if ( ! is_pck ( sys - > id ) )
return 1 ;
return ! ! ( pmc_read ( pmc , AT91_PMC_SR ) & ( 1 < < sys - > id ) ) ;
2013-10-11 13:41:41 +04:00
}
static const struct clk_ops system_ops = {
2014-03-11 13:00:34 +04:00
. prepare = clk_system_prepare ,
. unprepare = clk_system_unprepare ,
. is_prepared = clk_system_is_prepared ,
2013-10-11 13:41:41 +04:00
} ;
static struct clk * __init
at91_clk_register_system ( struct at91_pmc * pmc , const char * name ,
2014-03-11 13:00:34 +04:00
const char * parent_name , u8 id , int irq )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys ;
struct clk * clk = NULL ;
struct clk_init_data init ;
2014-03-11 13:00:34 +04:00
int ret ;
2013-10-11 13:41:41 +04:00
if ( ! parent_name | | id > SYSTEM_MAX_ID )
return ERR_PTR ( - EINVAL ) ;
sys = kzalloc ( sizeof ( * sys ) , GFP_KERNEL ) ;
if ( ! sys )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & system_ops ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
/*
* CLK_IGNORE_UNUSED is used to avoid ddrck switch off .
* TODO : we should implement a driver supporting at91 ddr controller
* ( see drivers / memory ) which would request and enable the ddrck clock .
* When this is done we will be able to remove CLK_IGNORE_UNUSED flag .
*/
2014-03-11 13:00:33 +04:00
init . flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED ;
2013-10-11 13:41:41 +04:00
sys - > id = id ;
sys - > hw . init = & init ;
sys - > pmc = pmc ;
2014-03-11 13:00:34 +04:00
sys - > irq = irq ;
if ( irq ) {
init_waitqueue_head ( & sys - > wait ) ;
irq_set_status_flags ( sys - > irq , IRQ_NOAUTOEN ) ;
ret = request_irq ( sys - > irq , clk_system_irq_handler ,
IRQF_TRIGGER_HIGH , name , sys ) ;
if ( ret )
return ERR_PTR ( ret ) ;
}
2013-10-11 13:41:41 +04:00
clk = clk_register ( NULL , & sys - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( sys ) ;
return clk ;
}
static void __init
of_at91_clk_sys_setup ( struct device_node * np , struct at91_pmc * pmc )
{
int num ;
2014-03-11 13:00:34 +04:00
int irq = 0 ;
2013-10-11 13:41:41 +04:00
u32 id ;
struct clk * clk ;
const char * name ;
struct device_node * sysclknp ;
const char * parent_name ;
num = of_get_child_count ( np ) ;
if ( num > ( SYSTEM_MAX_ID + 1 ) )
return ;
for_each_child_of_node ( np , sysclknp ) {
if ( of_property_read_u32 ( sysclknp , " reg " , & id ) )
continue ;
if ( of_property_read_string ( np , " clock-output-names " , & name ) )
name = sysclknp - > name ;
2014-03-11 13:00:34 +04:00
if ( is_pck ( id ) )
irq = irq_of_parse_and_map ( sysclknp , 0 ) ;
2013-10-11 13:41:41 +04:00
parent_name = of_clk_get_parent_name ( sysclknp , 0 ) ;
2014-03-11 13:00:34 +04:00
clk = at91_clk_register_system ( pmc , name , parent_name , id , irq ) ;
2013-10-11 13:41:41 +04:00
if ( IS_ERR ( clk ) )
continue ;
of_clk_add_provider ( sysclknp , of_clk_src_simple_get , clk ) ;
}
}
void __init of_at91rm9200_clk_sys_setup ( struct device_node * np ,
struct at91_pmc * pmc )
{
of_at91_clk_sys_setup ( np , pmc ) ;
}