2009-11-16 20:32:59 +03:00
/*
* Cpufreq driver for the loongson - 2 processors
*
* The 2 E revision of loongson processor not support this feature .
*
2015-07-07 21:56:04 +03:00
* Copyright ( C ) 2006 - 2008 Lemote Inc . & Institute of Computing Technology
2009-11-16 20:32:59 +03:00
* Author : Yanhua , yanh @ lemote . com
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
2016-04-05 23:28:25 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2009-11-16 20:32:59 +03:00
# include <linux/cpufreq.h>
# include <linux/module.h>
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
2013-05-21 18:59:19 +04:00
# include <asm/idle.h>
2009-11-16 20:32:59 +03:00
2019-10-20 17:43:14 +03:00
# include <asm/mach-loongson2ef/loongson.h>
2009-11-16 20:32:59 +03:00
static uint nowait ;
static void ( * saved_cpu_wait ) ( void ) ;
static int loongson2_cpu_freq_notifier ( struct notifier_block * nb ,
unsigned long val , void * data ) ;
static struct notifier_block loongson2_cpufreq_notifier_block = {
. notifier_call = loongson2_cpu_freq_notifier
} ;
static int loongson2_cpu_freq_notifier ( struct notifier_block * nb ,
unsigned long val , void * data )
{
if ( val = = CPUFREQ_POSTCHANGE )
current_cpu_data . udelay_val = loops_per_jiffy ;
return 0 ;
}
/*
* Here we notify other drivers of the proposed change and the final change .
*/
static int loongson2_cpufreq_target ( struct cpufreq_policy * policy ,
2013-10-25 18:15:48 +04:00
unsigned int index )
2009-11-16 20:32:59 +03:00
{
unsigned int freq ;
freq =
( ( cpu_clock_freq / 1000 ) *
2013-10-25 18:15:48 +04:00
loongson2_clockmod_table [ index ] . driver_data ) / 8 ;
2009-11-16 20:32:59 +03:00
/* setting the cpu frequency */
2020-04-09 12:02:28 +03:00
loongson2_cpu_set_rate ( freq ) ;
2009-11-16 20:32:59 +03:00
return 0 ;
}
static int loongson2_cpufreq_cpu_init ( struct cpufreq_policy * policy )
{
int i ;
2013-01-03 13:34:20 +04:00
unsigned long rate ;
int ret ;
2009-11-16 20:32:59 +03:00
2013-01-03 13:34:20 +04:00
rate = cpu_clock_freq / 1000 ;
2020-04-09 12:02:28 +03:00
if ( ! rate )
2009-11-16 20:32:59 +03:00
return - EINVAL ;
/* clock table init */
for ( i = 2 ;
( loongson2_clockmod_table [ i ] . frequency ! = CPUFREQ_TABLE_END ) ;
i + + )
2013-01-03 13:34:20 +04:00
loongson2_clockmod_table [ i ] . frequency = ( rate * i ) / 8 ;
2009-11-16 20:32:59 +03:00
2020-04-09 12:02:28 +03:00
ret = loongson2_cpu_set_rate ( rate ) ;
if ( ret )
2013-08-05 22:27:12 +04:00
return ret ;
2019-07-16 07:06:08 +03:00
cpufreq_generic_init ( policy , & loongson2_clockmod_table [ 0 ] , 0 ) ;
return 0 ;
2009-11-16 20:32:59 +03:00
}
static int loongson2_cpufreq_exit ( struct cpufreq_policy * policy )
{
return 0 ;
}
static struct cpufreq_driver loongson2_cpufreq_driver = {
. name = " loongson2 " ,
. init = loongson2_cpufreq_cpu_init ,
2013-10-03 18:58:11 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = loongson2_cpufreq_target ,
2014-01-09 19:08:43 +04:00
. get = cpufreq_generic_get ,
2009-11-16 20:32:59 +03:00
. exit = loongson2_cpufreq_exit ,
2013-10-03 18:58:11 +04:00
. attr = cpufreq_generic_attr ,
2009-11-16 20:32:59 +03:00
} ;
2017-08-13 12:40:06 +03:00
static const struct platform_device_id platform_device_ids [ ] = {
2009-11-16 20:32:59 +03:00
{
. name = " loongson2_cpufreq " ,
} ,
{ }
} ;
MODULE_DEVICE_TABLE ( platform , platform_device_ids ) ;
static struct platform_driver platform_driver = {
. driver = {
. name = " loongson2_cpufreq " ,
} ,
. id_table = platform_device_ids ,
} ;
2012-08-01 19:15:32 +04:00
/*
* This is the simple version of Loongson - 2 wait , Maybe we need do this in
* interrupt disabled context .
*/
static DEFINE_SPINLOCK ( loongson2_wait_lock ) ;
static void loongson2_cpu_wait ( void )
{
unsigned long flags ;
u32 cpu_freq ;
spin_lock_irqsave ( & loongson2_wait_lock , flags ) ;
2020-01-21 15:30:45 +03:00
cpu_freq = readl ( LOONGSON_CHIPCFG ) ;
/* Put CPU into wait mode */
writel ( readl ( LOONGSON_CHIPCFG ) & ~ 0x7 , LOONGSON_CHIPCFG ) ;
/* Restore CPU state */
writel ( cpu_freq , LOONGSON_CHIPCFG ) ;
2012-08-01 19:15:32 +04:00
spin_unlock_irqrestore ( & loongson2_wait_lock , flags ) ;
2013-05-21 16:05:27 +04:00
local_irq_enable ( ) ;
2012-08-01 19:15:32 +04:00
}
2009-11-16 20:32:59 +03:00
static int __init cpufreq_init ( void )
{
int ret ;
/* Register platform stuff */
ret = platform_driver_register ( & platform_driver ) ;
if ( ret )
return ret ;
2016-04-05 23:28:25 +03:00
pr_info ( " Loongson-2F CPU frequency driver \n " ) ;
2009-11-16 20:32:59 +03:00
cpufreq_register_notifier ( & loongson2_cpufreq_notifier_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
ret = cpufreq_register_driver ( & loongson2_cpufreq_driver ) ;
if ( ! ret & & ! nowait ) {
saved_cpu_wait = cpu_wait ;
cpu_wait = loongson2_cpu_wait ;
}
return ret ;
}
static void __exit cpufreq_exit ( void )
{
if ( ! nowait & & saved_cpu_wait )
cpu_wait = saved_cpu_wait ;
cpufreq_unregister_driver ( & loongson2_cpufreq_driver ) ;
cpufreq_unregister_notifier ( & loongson2_cpufreq_notifier_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
platform_driver_unregister ( & platform_driver ) ;
}
module_init ( cpufreq_init ) ;
module_exit ( cpufreq_exit ) ;
module_param ( nowait , uint , 0644 ) ;
MODULE_PARM_DESC ( nowait , " Disable Loongson-2F specific wait " ) ;
MODULE_AUTHOR ( " Yanhua <yanh@lemote.com> " ) ;
MODULE_DESCRIPTION ( " cpufreq driver for Loongson2F " ) ;
MODULE_LICENSE ( " GPL " ) ;