2019-05-28 19:57:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-08-27 17:45:50 +04:00
/*
* PRCMU clock implementation for ux500 platform .
*
* Copyright ( C ) 2012 ST - Ericsson SA
* Author : Ulf Hansson < ulf . hansson @ linaro . org >
*/
# include <linux/clk-provider.h>
# include <linux/mfd/dbx500-prcmu.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/err.h>
# include "clk.h"
# define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
struct clk_prcmu {
struct clk_hw hw ;
u8 cg_sel ;
2013-03-12 23:26:05 +04:00
int is_prepared ;
2012-08-27 17:45:50 +04:00
int is_enabled ;
2013-03-12 23:26:05 +04:00
int opp_requested ;
2012-08-27 17:45:50 +04:00
} ;
/* PRCMU clock operations. */
static int clk_prcmu_prepare ( struct clk_hw * hw )
{
2013-03-12 23:26:05 +04:00
int ret ;
2012-08-27 17:45:50 +04:00
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
2013-03-12 23:26:05 +04:00
ret = prcmu_request_clock ( clk - > cg_sel , true ) ;
if ( ! ret )
clk - > is_prepared = 1 ;
2013-10-08 15:17:47 +04:00
return ret ;
2012-08-27 17:45:50 +04:00
}
static void clk_prcmu_unprepare ( struct clk_hw * hw )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
if ( prcmu_request_clock ( clk - > cg_sel , false ) )
pr_err ( " clk_prcmu: %s failed to disable %s. \n " , __func__ ,
2015-08-12 21:42:23 +03:00
clk_hw_get_name ( hw ) ) ;
2013-03-12 23:26:05 +04:00
else
clk - > is_prepared = 0 ;
}
static int clk_prcmu_is_prepared ( struct clk_hw * hw )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
return clk - > is_prepared ;
2012-08-27 17:45:50 +04:00
}
static int clk_prcmu_enable ( struct clk_hw * hw )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
clk - > is_enabled = 1 ;
return 0 ;
}
static void clk_prcmu_disable ( struct clk_hw * hw )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
clk - > is_enabled = 0 ;
}
static int clk_prcmu_is_enabled ( struct clk_hw * hw )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
return clk - > is_enabled ;
}
static unsigned long clk_prcmu_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
return prcmu_clock_rate ( clk - > cg_sel ) ;
}
static long clk_prcmu_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
return prcmu_round_clock_rate ( clk - > cg_sel , rate ) ;
}
static int clk_prcmu_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
return prcmu_set_clock_rate ( clk - > cg_sel , rate ) ;
}
static int clk_prcmu_opp_prepare ( struct clk_hw * hw )
{
int err ;
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
2013-03-12 23:26:05 +04:00
if ( ! clk - > opp_requested ) {
err = prcmu_qos_add_requirement ( PRCMU_QOS_APE_OPP ,
2015-08-12 21:42:23 +03:00
( char * ) clk_hw_get_name ( hw ) ,
2013-03-12 23:26:05 +04:00
100 ) ;
if ( err ) {
pr_err ( " clk_prcmu: %s fail req APE OPP for %s. \n " ,
2015-08-12 21:42:23 +03:00
__func__ , clk_hw_get_name ( hw ) ) ;
2013-03-12 23:26:05 +04:00
return err ;
}
clk - > opp_requested = 1 ;
2012-08-27 17:45:50 +04:00
}
err = prcmu_request_clock ( clk - > cg_sel , true ) ;
2013-03-12 23:26:05 +04:00
if ( err ) {
prcmu_qos_remove_requirement ( PRCMU_QOS_APE_OPP ,
2015-08-12 21:42:23 +03:00
( char * ) clk_hw_get_name ( hw ) ) ;
2013-03-12 23:26:05 +04:00
clk - > opp_requested = 0 ;
return err ;
}
2012-08-27 17:45:50 +04:00
2013-03-12 23:26:05 +04:00
clk - > is_prepared = 1 ;
return 0 ;
2012-08-27 17:45:50 +04:00
}
static void clk_prcmu_opp_unprepare ( struct clk_hw * hw )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
2013-03-12 23:26:05 +04:00
if ( prcmu_request_clock ( clk - > cg_sel , false ) ) {
pr_err ( " clk_prcmu: %s failed to disable %s. \n " , __func__ ,
2015-08-12 21:42:23 +03:00
clk_hw_get_name ( hw ) ) ;
2013-03-12 23:26:05 +04:00
return ;
}
if ( clk - > opp_requested ) {
prcmu_qos_remove_requirement ( PRCMU_QOS_APE_OPP ,
2015-08-12 21:42:23 +03:00
( char * ) clk_hw_get_name ( hw ) ) ;
2013-03-12 23:26:05 +04:00
clk - > opp_requested = 0 ;
}
clk - > is_prepared = 0 ;
2012-08-27 17:45:50 +04:00
}
2012-09-24 18:43:18 +04:00
static int clk_prcmu_opp_volt_prepare ( struct clk_hw * hw )
{
int err ;
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
2013-03-12 23:26:05 +04:00
if ( ! clk - > opp_requested ) {
err = prcmu_request_ape_opp_100_voltage ( true ) ;
if ( err ) {
pr_err ( " clk_prcmu: %s fail req APE OPP VOLT for %s. \n " ,
2015-08-12 21:42:23 +03:00
__func__ , clk_hw_get_name ( hw ) ) ;
2013-03-12 23:26:05 +04:00
return err ;
}
clk - > opp_requested = 1 ;
2012-09-24 18:43:18 +04:00
}
err = prcmu_request_clock ( clk - > cg_sel , true ) ;
2013-03-12 23:26:05 +04:00
if ( err ) {
2012-09-24 18:43:18 +04:00
prcmu_request_ape_opp_100_voltage ( false ) ;
2013-03-12 23:26:05 +04:00
clk - > opp_requested = 0 ;
return err ;
}
2012-09-24 18:43:18 +04:00
2013-03-12 23:26:05 +04:00
clk - > is_prepared = 1 ;
return 0 ;
2012-09-24 18:43:18 +04:00
}
static void clk_prcmu_opp_volt_unprepare ( struct clk_hw * hw )
{
struct clk_prcmu * clk = to_clk_prcmu ( hw ) ;
2013-03-12 23:26:05 +04:00
if ( prcmu_request_clock ( clk - > cg_sel , false ) ) {
pr_err ( " clk_prcmu: %s failed to disable %s. \n " , __func__ ,
2015-08-12 21:42:23 +03:00
clk_hw_get_name ( hw ) ) ;
2013-03-12 23:26:05 +04:00
return ;
}
if ( clk - > opp_requested ) {
prcmu_request_ape_opp_100_voltage ( false ) ;
clk - > opp_requested = 0 ;
}
clk - > is_prepared = 0 ;
2012-09-24 18:43:18 +04:00
}
2017-08-28 10:02:39 +03:00
static const struct clk_ops clk_prcmu_scalable_ops = {
2012-08-27 17:45:50 +04:00
. prepare = clk_prcmu_prepare ,
. unprepare = clk_prcmu_unprepare ,
2013-03-12 23:26:05 +04:00
. is_prepared = clk_prcmu_is_prepared ,
2012-08-27 17:45:50 +04:00
. enable = clk_prcmu_enable ,
. disable = clk_prcmu_disable ,
. is_enabled = clk_prcmu_is_enabled ,
. recalc_rate = clk_prcmu_recalc_rate ,
. round_rate = clk_prcmu_round_rate ,
. set_rate = clk_prcmu_set_rate ,
} ;
2017-08-28 10:02:39 +03:00
static const struct clk_ops clk_prcmu_gate_ops = {
2012-08-27 17:45:50 +04:00
. prepare = clk_prcmu_prepare ,
. unprepare = clk_prcmu_unprepare ,
2013-03-12 23:26:05 +04:00
. is_prepared = clk_prcmu_is_prepared ,
2012-08-27 17:45:50 +04:00
. enable = clk_prcmu_enable ,
. disable = clk_prcmu_disable ,
. is_enabled = clk_prcmu_is_enabled ,
. recalc_rate = clk_prcmu_recalc_rate ,
} ;
2017-08-28 10:02:39 +03:00
static const struct clk_ops clk_prcmu_scalable_rate_ops = {
2012-10-10 15:42:27 +04:00
. is_enabled = clk_prcmu_is_enabled ,
. recalc_rate = clk_prcmu_recalc_rate ,
. round_rate = clk_prcmu_round_rate ,
. set_rate = clk_prcmu_set_rate ,
} ;
2017-08-28 10:02:39 +03:00
static const struct clk_ops clk_prcmu_rate_ops = {
2012-08-31 16:21:29 +04:00
. is_enabled = clk_prcmu_is_enabled ,
. recalc_rate = clk_prcmu_recalc_rate ,
} ;
2017-08-28 10:02:39 +03:00
static const struct clk_ops clk_prcmu_opp_gate_ops = {
2012-08-27 17:45:50 +04:00
. prepare = clk_prcmu_opp_prepare ,
. unprepare = clk_prcmu_opp_unprepare ,
2013-03-12 23:26:05 +04:00
. is_prepared = clk_prcmu_is_prepared ,
2012-08-27 17:45:50 +04:00
. enable = clk_prcmu_enable ,
. disable = clk_prcmu_disable ,
. is_enabled = clk_prcmu_is_enabled ,
. recalc_rate = clk_prcmu_recalc_rate ,
} ;
2017-08-28 10:02:39 +03:00
static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
2012-09-24 18:43:18 +04:00
. prepare = clk_prcmu_opp_volt_prepare ,
. unprepare = clk_prcmu_opp_volt_unprepare ,
2013-03-12 23:26:05 +04:00
. is_prepared = clk_prcmu_is_prepared ,
2012-09-24 18:43:18 +04:00
. enable = clk_prcmu_enable ,
. disable = clk_prcmu_disable ,
. is_enabled = clk_prcmu_is_enabled ,
. recalc_rate = clk_prcmu_recalc_rate ,
. round_rate = clk_prcmu_round_rate ,
. set_rate = clk_prcmu_set_rate ,
} ;
2012-08-27 17:45:50 +04:00
static struct clk * clk_reg_prcmu ( const char * name ,
const char * parent_name ,
u8 cg_sel ,
unsigned long rate ,
unsigned long flags ,
2017-08-28 10:02:39 +03:00
const struct clk_ops * clk_prcmu_ops )
2012-08-27 17:45:50 +04:00
{
struct clk_prcmu * clk ;
struct clk_init_data clk_prcmu_init ;
struct clk * clk_reg ;
if ( ! name ) {
pr_err ( " clk_prcmu: %s invalid arguments passed \n " , __func__ ) ;
return ERR_PTR ( - EINVAL ) ;
}
2017-09-27 21:30:53 +03:00
clk = kzalloc ( sizeof ( * clk ) , GFP_KERNEL ) ;
2017-09-27 21:23:58 +03:00
if ( ! clk )
2012-08-27 17:45:50 +04:00
return ERR_PTR ( - ENOMEM ) ;
clk - > cg_sel = cg_sel ;
2013-03-12 23:26:05 +04:00
clk - > is_prepared = 1 ;
2012-08-27 17:45:50 +04:00
clk - > is_enabled = 1 ;
2013-03-12 23:26:05 +04:00
clk - > opp_requested = 0 ;
2012-08-27 17:45:50 +04:00
/* "rate" can be used for changing the initial frequency */
if ( rate )
prcmu_set_clock_rate ( cg_sel , rate ) ;
clk_prcmu_init . name = name ;
clk_prcmu_init . ops = clk_prcmu_ops ;
clk_prcmu_init . flags = flags ;
clk_prcmu_init . parent_names = ( parent_name ? & parent_name : NULL ) ;
clk_prcmu_init . num_parents = ( parent_name ? 1 : 0 ) ;
clk - > hw . init = & clk_prcmu_init ;
clk_reg = clk_register ( NULL , & clk - > hw ) ;
if ( IS_ERR_OR_NULL ( clk_reg ) )
goto free_clk ;
return clk_reg ;
free_clk :
kfree ( clk ) ;
pr_err ( " clk_prcmu: %s failed to register clk \n " , __func__ ) ;
return ERR_PTR ( - ENOMEM ) ;
}
struct clk * clk_reg_prcmu_scalable ( const char * name ,
const char * parent_name ,
u8 cg_sel ,
unsigned long rate ,
unsigned long flags )
{
return clk_reg_prcmu ( name , parent_name , cg_sel , rate , flags ,
& clk_prcmu_scalable_ops ) ;
}
struct clk * clk_reg_prcmu_gate ( const char * name ,
const char * parent_name ,
u8 cg_sel ,
unsigned long flags )
{
return clk_reg_prcmu ( name , parent_name , cg_sel , 0 , flags ,
& clk_prcmu_gate_ops ) ;
}
2012-10-10 15:42:27 +04:00
struct clk * clk_reg_prcmu_scalable_rate ( const char * name ,
const char * parent_name ,
u8 cg_sel ,
unsigned long rate ,
unsigned long flags )
{
return clk_reg_prcmu ( name , parent_name , cg_sel , rate , flags ,
& clk_prcmu_scalable_rate_ops ) ;
}
2012-08-31 16:21:29 +04:00
struct clk * clk_reg_prcmu_rate ( const char * name ,
const char * parent_name ,
u8 cg_sel ,
unsigned long flags )
{
return clk_reg_prcmu ( name , parent_name , cg_sel , 0 , flags ,
& clk_prcmu_rate_ops ) ;
}
2012-08-27 17:45:50 +04:00
struct clk * clk_reg_prcmu_opp_gate ( const char * name ,
const char * parent_name ,
u8 cg_sel ,
unsigned long flags )
{
return clk_reg_prcmu ( name , parent_name , cg_sel , 0 , flags ,
& clk_prcmu_opp_gate_ops ) ;
}
2012-09-24 18:43:18 +04:00
struct clk * clk_reg_prcmu_opp_volt_scalable ( const char * name ,
const char * parent_name ,
u8 cg_sel ,
unsigned long rate ,
unsigned long flags )
{
return clk_reg_prcmu ( name , parent_name , cg_sel , rate , flags ,
& clk_prcmu_opp_volt_scalable_ops ) ;
}