2005-07-10 22:58:20 +04:00
/*
2011-05-27 06:39:18 +04:00
* CPU frequency scaling for OMAP using OPP information
2005-07-10 22:58:20 +04:00
*
* Copyright ( C ) 2005 Nokia Corporation
* Written by Tony Lindgren < tony @ atomide . com >
*
* Based on cpu - sa1110 . c , Copyright ( C ) 2001 Russell King
*
2010-08-12 04:02:43 +04:00
* Copyright ( C ) 2007 - 2011 Texas Instruments , Inc .
* - OMAP3 / 4 support by Rajendra Nayak , Santosh Shilimkar
*
2005-07-10 22:58:20 +04:00
* 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 .
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/cpufreq.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/err.h>
2006-01-07 19:15:52 +03:00
# include <linux/clk.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2013-09-20 01:03:52 +04:00
# include <linux/pm_opp.h>
2011-09-22 03:53:00 +04:00
# include <linux/cpu.h>
2011-09-30 21:41:26 +04:00
# include <linux/module.h>
cpufreq: OMAP: instantiate omap-cpufreq as a platform_driver
As multi-platform build is being adopted by more and more ARM platforms,
initcall function should be used very carefully. For example, when
CONFIG_ARM_OMAP2PLUS_CPUFREQ is built in the kernel, omap_cpufreq_init()
will be called on all the platforms to initialize omap-cpufreq driver.
Further, on OMAP, we now use Soc generic cpufreq-cpu0 driver using device
tree entries. To allow cpufreq-cpu0 and omap-cpufreq drivers to co-exist
for OMAP in a single image, we need to ensure the following:
1. With device tree boot, we use cpufreq-cpu0
2. With non device tree boot, we use omap-cpufreq
In the case of (1), we will have cpu OPPs and regulator registered
as part of the device tree nodes, to ensure that omap-cpufreq
and cpufreq-cpu0 don't conflict in managing the frequency of the
same CPU, we should not permit omap-cpufreq to be probed.
In the case of (2), we will not have the cpufreq-cpu0 device, hence
only omap-cpufreq will be active.
To eliminate this undesired these effects, we change omap-cpufreq
driver to have it instantiated as a platform_driver and register
"omap-cpufreq" device only when booted without device tree nodes on
OMAP platforms.
This allows the following:
a) Will only run on platforms that create the platform_device
"omap-cpufreq".
b) Since the platform_device is registered only when device tree nodes
are *not* populated, omap-cpufreq driver does not conflict with
the usage of cpufreq-cpu0 driver which is used on OMAP platforms when
device tree nodes are present.
Inspired by commit 5553f9e26f6f49a93ba732fd222eac6973a4cf35
(cpufreq: instantiate cpufreq-cpu0 as a platform_driver)
[robherring2@gmail.com: reported conflict of omap-cpufreq vs other
driver in an non-device tree supported boot]
Reported-by: Rob Herring <robherring2@gmail.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Kevin Hilman <khilman@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-04-10 03:22:01 +04:00
# include <linux/platform_device.h>
2011-07-16 02:05:04 +04:00
# include <linux/regulator/consumer.h>
2005-07-10 22:58:20 +04:00
2010-08-12 04:02:43 +04:00
# include <asm/smp_plat.h>
2011-09-22 03:53:00 +04:00
# include <asm/cpu.h>
2005-07-10 22:58:20 +04:00
2012-02-23 17:49:24 +04:00
/* OPP tolerance in percentage */
# define OPP_TOLERANCE 4
2010-08-12 04:02:43 +04:00
static struct cpufreq_frequency_table * freq_table ;
2011-05-27 06:39:20 +04:00
static atomic_t freq_table_users = ATOMIC_INIT ( 0 ) ;
2011-05-26 03:38:47 +04:00
static struct device * mpu_dev ;
2011-07-16 02:05:04 +04:00
static struct regulator * mpu_reg ;
2007-08-30 13:46:39 +04:00
2013-10-25 18:15:48 +04:00
static int omap_target ( struct cpufreq_policy * policy , unsigned int index )
2005-07-10 22:58:20 +04:00
{
2013-11-13 14:09:23 +04:00
int r , ret ;
2013-09-20 01:03:51 +04:00
struct dev_pm_opp * opp ;
2012-02-23 17:49:24 +04:00
unsigned long freq , volt = 0 , volt_old = 0 , tol = 0 ;
2013-08-14 18:08:24 +04:00
unsigned int old_freq , new_freq ;
2005-07-10 22:58:20 +04:00
2014-01-09 19:08:43 +04:00
old_freq = policy - > cur ;
2013-08-14 18:08:24 +04:00
new_freq = freq_table [ index ] . frequency ;
2009-01-28 05:13:38 +03:00
2013-08-14 18:08:24 +04:00
freq = new_freq * 1000 ;
2014-01-09 19:08:43 +04:00
ret = clk_round_rate ( policy - > clk , freq ) ;
2012-10-03 02:39:03 +04:00
if ( IS_ERR_VALUE ( ret ) ) {
dev_warn ( mpu_dev ,
" CPUfreq: Cannot find matching frequency for %lu \n " ,
freq ) ;
return ret ;
}
freq = ret ;
2011-07-16 02:05:04 +04:00
if ( mpu_reg ) {
2013-01-18 23:52:32 +04:00
rcu_read_lock ( ) ;
2013-09-20 01:03:50 +04:00
opp = dev_pm_opp_find_freq_ceil ( mpu_dev , & freq ) ;
2011-07-16 02:05:04 +04:00
if ( IS_ERR ( opp ) ) {
2013-01-18 23:52:32 +04:00
rcu_read_unlock ( ) ;
2011-07-16 02:05:04 +04:00
dev_err ( mpu_dev , " %s: unable to find MPU OPP for %d \n " ,
2013-08-14 18:08:24 +04:00
__func__ , new_freq ) ;
2011-07-16 02:05:04 +04:00
return - EINVAL ;
}
2013-09-20 01:03:50 +04:00
volt = dev_pm_opp_get_voltage ( opp ) ;
2013-01-18 23:52:32 +04:00
rcu_read_unlock ( ) ;
2012-02-23 17:49:24 +04:00
tol = volt * OPP_TOLERANCE / 100 ;
2011-07-16 02:05:04 +04:00
volt_old = regulator_get_voltage ( mpu_reg ) ;
}
dev_dbg ( mpu_dev , " cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV \n " ,
2013-08-14 18:08:24 +04:00
old_freq / 1000 , volt_old ? volt_old / 1000 : - 1 ,
new_freq / 1000 , volt ? volt / 1000 : - 1 ) ;
2013-06-19 09:48:20 +04:00
2011-07-16 02:05:04 +04:00
/* scaling up? scale voltage before frequency */
2013-08-14 18:08:24 +04:00
if ( mpu_reg & & ( new_freq > old_freq ) ) {
2012-02-23 17:49:24 +04:00
r = regulator_set_voltage ( mpu_reg , volt - tol , volt + tol ) ;
2011-07-16 02:05:04 +04:00
if ( r < 0 ) {
dev_warn ( mpu_dev , " %s: unable to scale voltage up. \n " ,
__func__ ) ;
2013-08-14 18:08:24 +04:00
return r ;
2011-07-16 02:05:04 +04:00
}
}
2010-08-12 04:02:43 +04:00
2014-01-09 19:08:43 +04:00
ret = clk_set_rate ( policy - > clk , new_freq * 1000 ) ;
2011-09-22 03:53:00 +04:00
2011-07-16 02:05:04 +04:00
/* scaling down? scale voltage after frequency */
2013-08-14 18:08:24 +04:00
if ( mpu_reg & & ( new_freq < old_freq ) ) {
2012-02-23 17:49:24 +04:00
r = regulator_set_voltage ( mpu_reg , volt - tol , volt + tol ) ;
2011-07-16 02:05:04 +04:00
if ( r < 0 ) {
dev_warn ( mpu_dev , " %s: unable to scale voltage down. \n " ,
__func__ ) ;
2014-01-09 19:08:43 +04:00
clk_set_rate ( policy - > clk , old_freq * 1000 ) ;
2013-08-14 18:08:24 +04:00
return r ;
2011-07-16 02:05:04 +04:00
}
}
2005-07-10 22:58:20 +04:00
return ret ;
}
2011-05-27 06:39:20 +04:00
static inline void freq_table_free ( void )
{
if ( atomic_dec_and_test ( & freq_table_users ) )
2013-09-20 01:03:50 +04:00
dev_pm_opp_free_cpufreq_table ( mpu_dev , & freq_table ) ;
2011-05-27 06:39:20 +04:00
}
2013-06-19 21:54:04 +04:00
static int omap_cpu_init ( struct cpufreq_policy * policy )
2005-07-10 22:58:20 +04:00
{
2013-10-03 18:59:18 +04:00
int result ;
2010-08-12 04:02:43 +04:00
2014-01-09 19:08:43 +04:00
policy - > clk = clk_get ( NULL , " cpufreq_ck " ) ;
if ( IS_ERR ( policy - > clk ) )
return PTR_ERR ( policy - > clk ) ;
2005-07-10 22:58:20 +04:00
2013-10-03 18:59:18 +04:00
if ( ! freq_table ) {
2013-09-20 01:03:50 +04:00
result = dev_pm_opp_init_cpufreq_table ( mpu_dev , & freq_table ) ;
2013-10-03 18:59:18 +04:00
if ( result ) {
dev_err ( mpu_dev ,
" %s: cpu%d: failed creating freq table[%d] \n " ,
2011-05-27 06:39:17 +04:00
__func__ , policy - > cpu , result ) ;
2013-10-03 18:59:18 +04:00
goto fail ;
}
2009-01-28 05:13:38 +03:00
}
2012-08-09 11:08:21 +04:00
atomic_inc_return ( & freq_table_users ) ;
2009-01-28 05:13:38 +03:00
/* FIXME: what's the actual transition time? */
2013-10-03 18:59:18 +04:00
result = cpufreq_generic_init ( policy , freq_table , 300 * 1000 ) ;
if ( ! result )
return 0 ;
2011-05-27 06:39:19 +04:00
2011-05-27 06:39:20 +04:00
freq_table_free ( ) ;
2013-10-03 18:59:18 +04:00
fail :
2014-01-09 19:08:43 +04:00
clk_put ( policy - > clk ) ;
2011-05-27 06:39:19 +04:00
return result ;
2005-07-10 22:58:20 +04:00
}
2007-08-30 13:46:39 +04:00
static int omap_cpu_exit ( struct cpufreq_policy * policy )
{
2011-05-27 06:39:20 +04:00
freq_table_free ( ) ;
2014-01-09 19:08:43 +04:00
clk_put ( policy - > clk ) ;
2007-08-30 13:46:39 +04:00
return 0 ;
}
2005-07-10 22:58:20 +04:00
static struct cpufreq_driver omap_driver = {
2013-12-03 09:50:45 +04:00
. flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK ,
2013-10-03 18:58:13 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = omap_target ,
2014-01-09 19:08:43 +04:00
. get = cpufreq_generic_get ,
2005-07-10 22:58:20 +04:00
. init = omap_cpu_init ,
2007-08-30 13:46:39 +04:00
. exit = omap_cpu_exit ,
2005-07-10 22:58:20 +04:00
. name = " omap " ,
2013-10-03 18:58:13 +04:00
. attr = cpufreq_generic_attr ,
2005-07-10 22:58:20 +04:00
} ;
cpufreq: OMAP: instantiate omap-cpufreq as a platform_driver
As multi-platform build is being adopted by more and more ARM platforms,
initcall function should be used very carefully. For example, when
CONFIG_ARM_OMAP2PLUS_CPUFREQ is built in the kernel, omap_cpufreq_init()
will be called on all the platforms to initialize omap-cpufreq driver.
Further, on OMAP, we now use Soc generic cpufreq-cpu0 driver using device
tree entries. To allow cpufreq-cpu0 and omap-cpufreq drivers to co-exist
for OMAP in a single image, we need to ensure the following:
1. With device tree boot, we use cpufreq-cpu0
2. With non device tree boot, we use omap-cpufreq
In the case of (1), we will have cpu OPPs and regulator registered
as part of the device tree nodes, to ensure that omap-cpufreq
and cpufreq-cpu0 don't conflict in managing the frequency of the
same CPU, we should not permit omap-cpufreq to be probed.
In the case of (2), we will not have the cpufreq-cpu0 device, hence
only omap-cpufreq will be active.
To eliminate this undesired these effects, we change omap-cpufreq
driver to have it instantiated as a platform_driver and register
"omap-cpufreq" device only when booted without device tree nodes on
OMAP platforms.
This allows the following:
a) Will only run on platforms that create the platform_device
"omap-cpufreq".
b) Since the platform_device is registered only when device tree nodes
are *not* populated, omap-cpufreq driver does not conflict with
the usage of cpufreq-cpu0 driver which is used on OMAP platforms when
device tree nodes are present.
Inspired by commit 5553f9e26f6f49a93ba732fd222eac6973a4cf35
(cpufreq: instantiate cpufreq-cpu0 as a platform_driver)
[robherring2@gmail.com: reported conflict of omap-cpufreq vs other
driver in an non-device tree supported boot]
Reported-by: Rob Herring <robherring2@gmail.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Kevin Hilman <khilman@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-04-10 03:22:01 +04:00
static int omap_cpufreq_probe ( struct platform_device * pdev )
2005-07-10 22:58:20 +04:00
{
2012-09-07 01:22:44 +04:00
mpu_dev = get_cpu_device ( 0 ) ;
if ( ! mpu_dev ) {
2011-05-26 03:38:47 +04:00
pr_warning ( " %s: unable to get the mpu device \n " , __func__ ) ;
2012-09-07 01:22:44 +04:00
return - EINVAL ;
2011-05-26 03:38:47 +04:00
}
2011-07-16 02:05:04 +04:00
mpu_reg = regulator_get ( mpu_dev , " vcc " ) ;
if ( IS_ERR ( mpu_reg ) ) {
pr_warning ( " %s: unable to get MPU regulator \n " , __func__ ) ;
mpu_reg = NULL ;
} else {
/*
* Ensure physical regulator is present .
* ( e . g . could be dummy regulator . )
*/
if ( regulator_get_voltage ( mpu_reg ) < 0 ) {
pr_warn ( " %s: physical regulator not present for MPU \n " ,
__func__ ) ;
regulator_put ( mpu_reg ) ;
mpu_reg = NULL ;
}
}
2005-07-10 22:58:20 +04:00
return cpufreq_register_driver ( & omap_driver ) ;
}
cpufreq: OMAP: instantiate omap-cpufreq as a platform_driver
As multi-platform build is being adopted by more and more ARM platforms,
initcall function should be used very carefully. For example, when
CONFIG_ARM_OMAP2PLUS_CPUFREQ is built in the kernel, omap_cpufreq_init()
will be called on all the platforms to initialize omap-cpufreq driver.
Further, on OMAP, we now use Soc generic cpufreq-cpu0 driver using device
tree entries. To allow cpufreq-cpu0 and omap-cpufreq drivers to co-exist
for OMAP in a single image, we need to ensure the following:
1. With device tree boot, we use cpufreq-cpu0
2. With non device tree boot, we use omap-cpufreq
In the case of (1), we will have cpu OPPs and regulator registered
as part of the device tree nodes, to ensure that omap-cpufreq
and cpufreq-cpu0 don't conflict in managing the frequency of the
same CPU, we should not permit omap-cpufreq to be probed.
In the case of (2), we will not have the cpufreq-cpu0 device, hence
only omap-cpufreq will be active.
To eliminate this undesired these effects, we change omap-cpufreq
driver to have it instantiated as a platform_driver and register
"omap-cpufreq" device only when booted without device tree nodes on
OMAP platforms.
This allows the following:
a) Will only run on platforms that create the platform_device
"omap-cpufreq".
b) Since the platform_device is registered only when device tree nodes
are *not* populated, omap-cpufreq driver does not conflict with
the usage of cpufreq-cpu0 driver which is used on OMAP platforms when
device tree nodes are present.
Inspired by commit 5553f9e26f6f49a93ba732fd222eac6973a4cf35
(cpufreq: instantiate cpufreq-cpu0 as a platform_driver)
[robherring2@gmail.com: reported conflict of omap-cpufreq vs other
driver in an non-device tree supported boot]
Reported-by: Rob Herring <robherring2@gmail.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Kevin Hilman <khilman@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-04-10 03:22:01 +04:00
static int omap_cpufreq_remove ( struct platform_device * pdev )
2010-08-12 04:02:43 +04:00
{
cpufreq: OMAP: instantiate omap-cpufreq as a platform_driver
As multi-platform build is being adopted by more and more ARM platforms,
initcall function should be used very carefully. For example, when
CONFIG_ARM_OMAP2PLUS_CPUFREQ is built in the kernel, omap_cpufreq_init()
will be called on all the platforms to initialize omap-cpufreq driver.
Further, on OMAP, we now use Soc generic cpufreq-cpu0 driver using device
tree entries. To allow cpufreq-cpu0 and omap-cpufreq drivers to co-exist
for OMAP in a single image, we need to ensure the following:
1. With device tree boot, we use cpufreq-cpu0
2. With non device tree boot, we use omap-cpufreq
In the case of (1), we will have cpu OPPs and regulator registered
as part of the device tree nodes, to ensure that omap-cpufreq
and cpufreq-cpu0 don't conflict in managing the frequency of the
same CPU, we should not permit omap-cpufreq to be probed.
In the case of (2), we will not have the cpufreq-cpu0 device, hence
only omap-cpufreq will be active.
To eliminate this undesired these effects, we change omap-cpufreq
driver to have it instantiated as a platform_driver and register
"omap-cpufreq" device only when booted without device tree nodes on
OMAP platforms.
This allows the following:
a) Will only run on platforms that create the platform_device
"omap-cpufreq".
b) Since the platform_device is registered only when device tree nodes
are *not* populated, omap-cpufreq driver does not conflict with
the usage of cpufreq-cpu0 driver which is used on OMAP platforms when
device tree nodes are present.
Inspired by commit 5553f9e26f6f49a93ba732fd222eac6973a4cf35
(cpufreq: instantiate cpufreq-cpu0 as a platform_driver)
[robherring2@gmail.com: reported conflict of omap-cpufreq vs other
driver in an non-device tree supported boot]
Reported-by: Rob Herring <robherring2@gmail.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Kevin Hilman <khilman@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-04-10 03:22:01 +04:00
return cpufreq_unregister_driver ( & omap_driver ) ;
2010-08-12 04:02:43 +04:00
}
2009-01-28 05:13:38 +03:00
cpufreq: OMAP: instantiate omap-cpufreq as a platform_driver
As multi-platform build is being adopted by more and more ARM platforms,
initcall function should be used very carefully. For example, when
CONFIG_ARM_OMAP2PLUS_CPUFREQ is built in the kernel, omap_cpufreq_init()
will be called on all the platforms to initialize omap-cpufreq driver.
Further, on OMAP, we now use Soc generic cpufreq-cpu0 driver using device
tree entries. To allow cpufreq-cpu0 and omap-cpufreq drivers to co-exist
for OMAP in a single image, we need to ensure the following:
1. With device tree boot, we use cpufreq-cpu0
2. With non device tree boot, we use omap-cpufreq
In the case of (1), we will have cpu OPPs and regulator registered
as part of the device tree nodes, to ensure that omap-cpufreq
and cpufreq-cpu0 don't conflict in managing the frequency of the
same CPU, we should not permit omap-cpufreq to be probed.
In the case of (2), we will not have the cpufreq-cpu0 device, hence
only omap-cpufreq will be active.
To eliminate this undesired these effects, we change omap-cpufreq
driver to have it instantiated as a platform_driver and register
"omap-cpufreq" device only when booted without device tree nodes on
OMAP platforms.
This allows the following:
a) Will only run on platforms that create the platform_device
"omap-cpufreq".
b) Since the platform_device is registered only when device tree nodes
are *not* populated, omap-cpufreq driver does not conflict with
the usage of cpufreq-cpu0 driver which is used on OMAP platforms when
device tree nodes are present.
Inspired by commit 5553f9e26f6f49a93ba732fd222eac6973a4cf35
(cpufreq: instantiate cpufreq-cpu0 as a platform_driver)
[robherring2@gmail.com: reported conflict of omap-cpufreq vs other
driver in an non-device tree supported boot]
Reported-by: Rob Herring <robherring2@gmail.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Kevin Hilman <khilman@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-04-10 03:22:01 +04:00
static struct platform_driver omap_cpufreq_platdrv = {
. driver = {
. name = " omap-cpufreq " ,
} ,
. probe = omap_cpufreq_probe ,
. remove = omap_cpufreq_remove ,
} ;
module_platform_driver ( omap_cpufreq_platdrv ) ;
2010-08-12 04:02:43 +04:00
MODULE_DESCRIPTION ( " cpufreq driver for OMAP SoCs " ) ;
MODULE_LICENSE ( " GPL " ) ;