2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2009-09-22 19:44:00 +04:00
/*
* CPU frequency scaling for DaVinci
*
2020-07-13 13:55:02 +03:00
* Copyright ( C ) 2009 Texas Instruments Incorporated - https : //www.ti.com/
2009-09-22 19:44:00 +04:00
*
* Based on linux / arch / arm / plat - omap / cpu - omap . c . Original Copyright follows :
*
* Copyright ( C ) 2005 Nokia Corporation
* Written by Tony Lindgren < tony @ atomide . com >
*
* Based on cpu - sa1110 . c , Copyright ( C ) 2001 Russell King
*
* Copyright ( C ) 2007 - 2008 Texas Instruments , Inc .
* Updated to support OMAP3
* Rajendra Nayak < rnayak @ ti . com >
*/
# include <linux/types.h>
# include <linux/cpufreq.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/clk.h>
2019-02-14 20:05:05 +03:00
# include <linux/platform_data/davinci-cpufreq.h>
2009-09-22 19:44:00 +04:00
# include <linux/platform_device.h>
2011-08-01 00:17:29 +04:00
# include <linux/export.h>
2009-09-22 19:44:00 +04:00
struct davinci_cpufreq {
struct device * dev ;
struct clk * armclk ;
2010-07-20 15:16:50 +04:00
struct clk * asyncclk ;
unsigned long asyncrate ;
2009-09-22 19:44:00 +04:00
} ;
static struct davinci_cpufreq cpufreq ;
2013-10-25 18:15:48 +04:00
static int davinci_target ( struct cpufreq_policy * policy , unsigned int idx )
2009-09-22 19:44:00 +04:00
{
struct davinci_cpufreq_config * pdata = cpufreq . dev - > platform_data ;
struct clk * armclk = cpufreq . armclk ;
2013-08-14 18:08:24 +04:00
unsigned int old_freq , new_freq ;
int ret = 0 ;
2009-09-22 19:44:00 +04:00
2014-01-09 19:08:43 +04:00
old_freq = policy - > cur ;
2013-08-14 18:08:24 +04:00
new_freq = pdata - > freq_table [ idx ] . frequency ;
2009-09-22 19:44:00 +04:00
/* if moving to higher frequency, up the voltage beforehand */
2013-08-14 18:08:24 +04:00
if ( pdata - > set_voltage & & new_freq > old_freq ) {
2010-07-20 15:16:48 +04:00
ret = pdata - > set_voltage ( idx ) ;
if ( ret )
2013-08-14 18:08:24 +04:00
return ret ;
2010-07-20 15:16:48 +04:00
}
2009-09-22 19:44:00 +04:00
2016-12-07 18:22:18 +03:00
ret = clk_set_rate ( armclk , new_freq * 1000 ) ;
2010-07-20 15:16:48 +04:00
if ( ret )
2013-08-14 18:08:24 +04:00
return ret ;
2009-09-22 19:44:00 +04:00
2010-07-20 15:16:50 +04:00
if ( cpufreq . asyncclk ) {
ret = clk_set_rate ( cpufreq . asyncclk , cpufreq . asyncrate ) ;
if ( ret )
2013-08-14 18:08:24 +04:00
return ret ;
2010-07-20 15:16:50 +04:00
}
2009-09-22 19:44:00 +04:00
/* if moving to lower freq, lower the voltage after lowering freq */
2013-08-14 18:08:24 +04:00
if ( pdata - > set_voltage & & new_freq < old_freq )
2009-09-22 19:44:00 +04:00
pdata - > set_voltage ( idx ) ;
2013-08-14 18:08:24 +04:00
return 0 ;
2009-09-22 19:44:00 +04:00
}
2011-02-28 13:21:33 +03:00
static int davinci_cpu_init ( struct cpufreq_policy * policy )
2009-09-22 19:44:00 +04:00
{
int result = 0 ;
struct davinci_cpufreq_config * pdata = cpufreq . dev - > platform_data ;
struct cpufreq_frequency_table * freq_table = pdata - > freq_table ;
if ( policy - > cpu ! = 0 )
return - EINVAL ;
2009-10-22 13:42:16 +04:00
/* Finish platform specific initialization */
if ( pdata - > init ) {
result = pdata - > init ( ) ;
if ( result )
return result ;
}
2014-01-09 19:08:43 +04:00
policy - > clk = cpufreq . armclk ;
2009-09-22 19:44:00 +04:00
/*
* Time measurement across the target ( ) function yields ~ 1500 - 1800u s
* time taken with no drivers on notification list .
2011-03-31 05:57:33 +04:00
* Setting the latency to 2000 us to accommodate addition of drivers
2009-09-22 19:44:00 +04:00
* to pre / post change notification list .
*/
2019-07-16 07:06:08 +03:00
cpufreq_generic_init ( policy , freq_table , 2000 * 1000 ) ;
return 0 ;
2009-09-22 19:44:00 +04:00
}
static struct cpufreq_driver davinci_driver = {
2021-02-02 07:55:11 +03:00
. flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK ,
2016-06-03 08:28:52 +03:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = davinci_target ,
2014-01-09 19:08:43 +04:00
. get = cpufreq_generic_get ,
2009-09-22 19:44:00 +04:00
. init = davinci_cpu_init ,
. name = " davinci " ,
2013-10-03 18:58:02 +04:00
. attr = cpufreq_generic_attr ,
2009-09-22 19:44:00 +04:00
} ;
static int __init davinci_cpufreq_probe ( struct platform_device * pdev )
{
struct davinci_cpufreq_config * pdata = pdev - > dev . platform_data ;
2010-07-20 15:16:50 +04:00
struct clk * asyncclk ;
2009-09-22 19:44:00 +04:00
if ( ! pdata )
return - EINVAL ;
if ( ! pdata - > freq_table )
return - EINVAL ;
cpufreq . dev = & pdev - > dev ;
cpufreq . armclk = clk_get ( NULL , " arm " ) ;
if ( IS_ERR ( cpufreq . armclk ) ) {
dev_err ( cpufreq . dev , " Unable to get ARM clock \n " ) ;
return PTR_ERR ( cpufreq . armclk ) ;
}
2010-07-20 15:16:50 +04:00
asyncclk = clk_get ( cpufreq . dev , " async " ) ;
if ( ! IS_ERR ( asyncclk ) ) {
cpufreq . asyncclk = asyncclk ;
cpufreq . asyncrate = clk_get_rate ( asyncclk ) ;
}
2009-09-22 19:44:00 +04:00
return cpufreq_register_driver ( & davinci_driver ) ;
}
2023-07-12 12:33:14 +03:00
static void __exit davinci_cpufreq_remove ( struct platform_device * pdev )
2009-09-22 19:44:00 +04:00
{
2023-02-08 12:26:54 +03:00
cpufreq_unregister_driver ( & davinci_driver ) ;
2009-09-22 19:44:00 +04:00
clk_put ( cpufreq . armclk ) ;
2010-07-20 15:16:50 +04:00
if ( cpufreq . asyncclk )
clk_put ( cpufreq . asyncclk ) ;
2009-09-22 19:44:00 +04:00
}
static struct platform_driver davinci_cpufreq_driver = {
. driver = {
. name = " cpufreq-davinci " ,
} ,
2023-07-12 12:33:14 +03:00
. remove_new = __exit_p ( davinci_cpufreq_remove ) ,
2009-09-22 19:44:00 +04:00
} ;
2012-04-26 05:45:39 +04:00
int __init davinci_cpufreq_init ( void )
2009-09-22 19:44:00 +04:00
{
return platform_driver_probe ( & davinci_cpufreq_driver ,
davinci_cpufreq_probe ) ;
}