2015-03-30 12:59:52 +03:00
/*
* System Control and Power Interface ( SCPI ) based CPUFreq Interface driver
*
* It provides necessary ops to arm_big_little cpufreq driver .
*
* Copyright ( C ) 2015 ARM Ltd .
* Sudeep Holla < sudeep . holla @ arm . com >
*
* 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 .
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2018-01-10 19:44:15 +03:00
# include <linux/clk.h>
cpufreq: arm_big_little: use generic OPP functions for {init, free}_opp_table
Currently when performing random CPU hot-plugs and suspend-to-ram(S2R)
on systems using arm_big_little cpufreq driver, we get warnings similar
to something like below:
cpu cpu1: _opp_add: duplicate OPPs detected. Existing: freq: 600000000,
volt: 800000, enabled: 1. New: freq: 600000000, volt: 800000, enabled: 1
This is mainly because the OPPs for the shared cpus are not set. We can
just use dev_pm_opp_of_cpumask_add_table in case the OPPs are obtained
from DT(arm_big_little_dt.c) or use dev_pm_opp_set_sharing_cpus if the
OPPs are obtained by other means like firmware(e.g. scpi-cpufreq.c)
Also now that the generic dev_pm_opp{,_of}_cpumask_remove_table can
handle removal of opp table and entries for all associated CPUs, we can
re-use dev_pm_opp{,_of}_cpumask_remove_table as free_opp_table in
cpufreq_arm_bL_ops.
This patch makes necessary changes to reuse the generic OPP functions for
{init,free}_opp_table and thereby eliminating the warnings.
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-05-03 17:05:05 +03:00
# include <linux/cpu.h>
2015-03-30 12:59:52 +03:00
# include <linux/cpufreq.h>
2018-01-10 19:44:15 +03:00
# include <linux/cpumask.h>
# include <linux/cpu_cooling.h>
# include <linux/export.h>
2015-03-30 12:59:52 +03:00
# include <linux/module.h>
2018-01-10 19:44:15 +03:00
# include <linux/of_platform.h>
2015-03-30 12:59:52 +03:00
# include <linux/pm_opp.h>
# include <linux/scpi_protocol.h>
2018-01-10 19:44:15 +03:00
# include <linux/slab.h>
2015-03-30 12:59:52 +03:00
# include <linux/types.h>
2018-01-10 19:44:15 +03:00
struct scpi_data {
struct clk * clk ;
struct device * cpu_dev ;
struct thermal_cooling_device * cdev ;
} ;
2015-03-30 12:59:52 +03:00
static struct scpi_ops * scpi_ops ;
2018-01-10 19:44:15 +03:00
static unsigned int scpi_cpufreq_get_rate ( unsigned int cpu )
{
struct cpufreq_policy * policy = cpufreq_cpu_get_raw ( cpu ) ;
struct scpi_data * priv = policy - > driver_data ;
unsigned long rate = clk_get_rate ( priv - > clk ) ;
return rate / 1000 ;
}
static int
scpi_cpufreq_set_target ( struct cpufreq_policy * policy , unsigned int index )
{
2018-02-26 16:11:03 +03:00
unsigned long freq = policy - > freq_table [ index ] . frequency ;
2018-01-10 19:44:15 +03:00
struct scpi_data * priv = policy - > driver_data ;
2018-02-26 16:11:03 +03:00
u64 rate = freq * 1000 ;
2018-01-10 19:44:15 +03:00
int ret ;
ret = clk_set_rate ( priv - > clk , rate ) ;
2018-02-26 16:11:03 +03:00
if ( ret )
return ret ;
if ( clk_get_rate ( priv - > clk ) ! = rate )
return - EIO ;
arch_set_freq_scale ( policy - > related_cpus , freq ,
policy - > cpuinfo . max_freq ) ;
return 0 ;
2018-01-10 19:44:15 +03:00
}
static int
scpi_get_sharing_cpus ( struct device * cpu_dev , struct cpumask * cpumask )
2015-03-30 12:59:52 +03:00
{
2018-01-10 19:44:15 +03:00
int cpu , domain , tdomain ;
struct device * tcpu_dev ;
domain = scpi_ops - > device_domain_id ( cpu_dev ) ;
if ( domain < 0 )
return domain ;
for_each_possible_cpu ( cpu ) {
if ( cpu = = cpu_dev - > id )
continue ;
tcpu_dev = get_cpu_device ( cpu ) ;
if ( ! tcpu_dev )
continue ;
tdomain = scpi_ops - > device_domain_id ( tcpu_dev ) ;
if ( tdomain = = domain )
cpumask_set_cpu ( cpu , cpumask ) ;
}
return 0 ;
cpufreq: arm_big_little: use generic OPP functions for {init, free}_opp_table
Currently when performing random CPU hot-plugs and suspend-to-ram(S2R)
on systems using arm_big_little cpufreq driver, we get warnings similar
to something like below:
cpu cpu1: _opp_add: duplicate OPPs detected. Existing: freq: 600000000,
volt: 800000, enabled: 1. New: freq: 600000000, volt: 800000, enabled: 1
This is mainly because the OPPs for the shared cpus are not set. We can
just use dev_pm_opp_of_cpumask_add_table in case the OPPs are obtained
from DT(arm_big_little_dt.c) or use dev_pm_opp_set_sharing_cpus if the
OPPs are obtained by other means like firmware(e.g. scpi-cpufreq.c)
Also now that the generic dev_pm_opp{,_of}_cpumask_remove_table can
handle removal of opp table and entries for all associated CPUs, we can
re-use dev_pm_opp{,_of}_cpumask_remove_table as free_opp_table in
cpufreq_arm_bL_ops.
This patch makes necessary changes to reuse the generic OPP functions for
{init,free}_opp_table and thereby eliminating the warnings.
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-05-03 17:05:05 +03:00
}
2018-01-10 19:44:15 +03:00
static int scpi_cpufreq_init ( struct cpufreq_policy * policy )
cpufreq: arm_big_little: use generic OPP functions for {init, free}_opp_table
Currently when performing random CPU hot-plugs and suspend-to-ram(S2R)
on systems using arm_big_little cpufreq driver, we get warnings similar
to something like below:
cpu cpu1: _opp_add: duplicate OPPs detected. Existing: freq: 600000000,
volt: 800000, enabled: 1. New: freq: 600000000, volt: 800000, enabled: 1
This is mainly because the OPPs for the shared cpus are not set. We can
just use dev_pm_opp_of_cpumask_add_table in case the OPPs are obtained
from DT(arm_big_little_dt.c) or use dev_pm_opp_set_sharing_cpus if the
OPPs are obtained by other means like firmware(e.g. scpi-cpufreq.c)
Also now that the generic dev_pm_opp{,_of}_cpumask_remove_table can
handle removal of opp table and entries for all associated CPUs, we can
re-use dev_pm_opp{,_of}_cpumask_remove_table as free_opp_table in
cpufreq_arm_bL_ops.
This patch makes necessary changes to reuse the generic OPP functions for
{init,free}_opp_table and thereby eliminating the warnings.
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-05-03 17:05:05 +03:00
{
2017-04-27 17:20:37 +03:00
int ret ;
2018-01-10 19:44:15 +03:00
unsigned int latency ;
struct device * cpu_dev ;
struct scpi_data * priv ;
struct cpufreq_frequency_table * freq_table ;
cpu_dev = get_cpu_device ( policy - > cpu ) ;
if ( ! cpu_dev ) {
pr_err ( " failed to get cpu%d device \n " , policy - > cpu ) ;
return - ENODEV ;
}
2015-03-30 12:59:52 +03:00
2017-04-27 17:20:37 +03:00
ret = scpi_ops - > add_opps_to_device ( cpu_dev ) ;
if ( ret ) {
dev_warn ( cpu_dev , " failed to add opps to the device \n " ) ;
return ret ;
2015-03-30 12:59:52 +03:00
}
2018-01-10 19:44:15 +03:00
ret = scpi_get_sharing_cpus ( cpu_dev , policy - > cpus ) ;
if ( ret ) {
dev_warn ( cpu_dev , " failed to get sharing cpumask \n " ) ;
return ret ;
}
ret = dev_pm_opp_set_sharing_cpus ( cpu_dev , policy - > cpus ) ;
if ( ret ) {
cpufreq: arm_big_little: use generic OPP functions for {init, free}_opp_table
Currently when performing random CPU hot-plugs and suspend-to-ram(S2R)
on systems using arm_big_little cpufreq driver, we get warnings similar
to something like below:
cpu cpu1: _opp_add: duplicate OPPs detected. Existing: freq: 600000000,
volt: 800000, enabled: 1. New: freq: 600000000, volt: 800000, enabled: 1
This is mainly because the OPPs for the shared cpus are not set. We can
just use dev_pm_opp_of_cpumask_add_table in case the OPPs are obtained
from DT(arm_big_little_dt.c) or use dev_pm_opp_set_sharing_cpus if the
OPPs are obtained by other means like firmware(e.g. scpi-cpufreq.c)
Also now that the generic dev_pm_opp{,_of}_cpumask_remove_table can
handle removal of opp table and entries for all associated CPUs, we can
re-use dev_pm_opp{,_of}_cpumask_remove_table as free_opp_table in
cpufreq_arm_bL_ops.
This patch makes necessary changes to reuse the generic OPP functions for
{init,free}_opp_table and thereby eliminating the warnings.
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-05-03 17:05:05 +03:00
dev_err ( cpu_dev , " %s: failed to mark OPPs as shared: %d \n " ,
__func__ , ret ) ;
2018-01-10 19:44:15 +03:00
return ret ;
}
ret = dev_pm_opp_get_opp_count ( cpu_dev ) ;
if ( ret < = 0 ) {
dev_dbg ( cpu_dev , " OPP table is not ready, deferring probe \n " ) ;
ret = - EPROBE_DEFER ;
goto out_free_opp ;
}
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
ret = - ENOMEM ;
goto out_free_opp ;
}
ret = dev_pm_opp_init_cpufreq_table ( cpu_dev , & freq_table ) ;
if ( ret ) {
dev_err ( cpu_dev , " failed to init cpufreq table: %d \n " , ret ) ;
goto out_free_priv ;
}
priv - > cpu_dev = cpu_dev ;
priv - > clk = clk_get ( cpu_dev , NULL ) ;
if ( IS_ERR ( priv - > clk ) ) {
dev_err ( cpu_dev , " %s: Failed to get clk for cpu: %d \n " ,
__func__ , cpu_dev - > id ) ;
2018-01-23 05:09:02 +03:00
ret = PTR_ERR ( priv - > clk ) ;
2018-01-10 19:44:15 +03:00
goto out_free_cpufreq_table ;
}
policy - > driver_data = priv ;
ret = cpufreq_table_validate_and_show ( policy , freq_table ) ;
if ( ret ) {
dev_err ( cpu_dev , " %s: invalid frequency table: %d \n " , __func__ ,
ret ) ;
goto out_put_clk ;
}
/* scpi allows DVFS request for any domain from any CPU */
policy - > dvfs_possible_from_any_cpu = true ;
latency = scpi_ops - > get_transition_latency ( cpu_dev ) ;
if ( ! latency )
latency = CPUFREQ_ETERNAL ;
policy - > cpuinfo . transition_latency = latency ;
policy - > fast_switch_possible = false ;
return 0 ;
out_put_clk :
clk_put ( priv - > clk ) ;
out_free_cpufreq_table :
dev_pm_opp_free_cpufreq_table ( cpu_dev , & freq_table ) ;
out_free_priv :
kfree ( priv ) ;
out_free_opp :
dev_pm_opp_cpumask_remove_table ( policy - > cpus ) ;
cpufreq: arm_big_little: use generic OPP functions for {init, free}_opp_table
Currently when performing random CPU hot-plugs and suspend-to-ram(S2R)
on systems using arm_big_little cpufreq driver, we get warnings similar
to something like below:
cpu cpu1: _opp_add: duplicate OPPs detected. Existing: freq: 600000000,
volt: 800000, enabled: 1. New: freq: 600000000, volt: 800000, enabled: 1
This is mainly because the OPPs for the shared cpus are not set. We can
just use dev_pm_opp_of_cpumask_add_table in case the OPPs are obtained
from DT(arm_big_little_dt.c) or use dev_pm_opp_set_sharing_cpus if the
OPPs are obtained by other means like firmware(e.g. scpi-cpufreq.c)
Also now that the generic dev_pm_opp{,_of}_cpumask_remove_table can
handle removal of opp table and entries for all associated CPUs, we can
re-use dev_pm_opp{,_of}_cpumask_remove_table as free_opp_table in
cpufreq_arm_bL_ops.
This patch makes necessary changes to reuse the generic OPP functions for
{init,free}_opp_table and thereby eliminating the warnings.
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-05-03 17:05:05 +03:00
return ret ;
2015-03-30 12:59:52 +03:00
}
2018-01-10 19:44:15 +03:00
static int scpi_cpufreq_exit ( struct cpufreq_policy * policy )
{
struct scpi_data * priv = policy - > driver_data ;
cpufreq_cooling_unregister ( priv - > cdev ) ;
clk_put ( priv - > clk ) ;
dev_pm_opp_free_cpufreq_table ( priv - > cpu_dev , & policy - > freq_table ) ;
kfree ( priv ) ;
dev_pm_opp_cpumask_remove_table ( policy - > related_cpus ) ;
return 0 ;
}
static void scpi_cpufreq_ready ( struct cpufreq_policy * policy )
{
struct scpi_data * priv = policy - > driver_data ;
2018-01-22 17:41:07 +03:00
priv - > cdev = of_cpufreq_cooling_register ( policy ) ;
2018-01-10 19:44:15 +03:00
}
static struct cpufreq_driver scpi_cpufreq_driver = {
. name = " scpi-cpufreq " ,
. flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
CPUFREQ_NEED_INITIAL_FREQ_CHECK ,
. verify = cpufreq_generic_frequency_table_verify ,
. attr = cpufreq_generic_attr ,
. get = scpi_cpufreq_get_rate ,
. init = scpi_cpufreq_init ,
. exit = scpi_cpufreq_exit ,
. ready = scpi_cpufreq_ready ,
. target_index = scpi_cpufreq_set_target ,
2015-03-30 12:59:52 +03:00
} ;
static int scpi_cpufreq_probe ( struct platform_device * pdev )
{
2018-01-10 19:44:15 +03:00
int ret ;
2015-03-30 12:59:52 +03:00
scpi_ops = get_scpi_ops ( ) ;
if ( ! scpi_ops )
return - EIO ;
2018-01-10 19:44:15 +03:00
ret = cpufreq_register_driver ( & scpi_cpufreq_driver ) ;
if ( ret )
dev_err ( & pdev - > dev , " %s: registering cpufreq failed, err: %d \n " ,
__func__ , ret ) ;
return ret ;
2015-03-30 12:59:52 +03:00
}
static int scpi_cpufreq_remove ( struct platform_device * pdev )
{
2018-01-10 19:44:15 +03:00
cpufreq_unregister_driver ( & scpi_cpufreq_driver ) ;
2015-03-30 12:59:52 +03:00
scpi_ops = NULL ;
return 0 ;
}
static struct platform_driver scpi_cpufreq_platdrv = {
. driver = {
. name = " scpi-cpufreq " ,
} ,
. probe = scpi_cpufreq_probe ,
. remove = scpi_cpufreq_remove ,
} ;
module_platform_driver ( scpi_cpufreq_platdrv ) ;
MODULE_AUTHOR ( " Sudeep Holla <sudeep.holla@arm.com> " ) ;
MODULE_DESCRIPTION ( " ARM SCPI CPUFreq interface driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;