2005-04-17 02:20:36 +04:00
/* us3_cpufreq.c: UltraSPARC-III cpu frequency support
*
* Copyright ( C ) 2003 David S . Miller ( davem @ redhat . com )
*
* Many thanks to Dominik Brodowski for fixing up the cpufreq
* infrastructure in order to make this driver easier to implement .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/smp.h>
# include <linux/cpufreq.h>
# include <linux/threads.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <asm/head.h>
# include <asm/timer.h>
static struct cpufreq_driver * cpufreq_us3_driver ;
struct us3_freq_percpu_info {
struct cpufreq_frequency_table table [ 4 ] ;
} ;
/* Indexed by cpu number. */
static struct us3_freq_percpu_info * us3_freq_table ;
/* UltraSPARC-III has three dividers: 1, 2, and 32. These are controlled
* in the Safari config register .
*/
# define SAFARI_CFG_DIV_1 0x0000000000000000UL
# define SAFARI_CFG_DIV_2 0x0000000040000000UL
# define SAFARI_CFG_DIV_32 0x0000000080000000UL
# define SAFARI_CFG_DIV_MASK 0x00000000C0000000UL
2017-04-12 23:07:37 +03:00
static void read_safari_cfg ( void * arg )
2005-04-17 02:20:36 +04:00
{
2017-04-12 23:07:37 +03:00
unsigned long ret , * val = arg ;
2005-04-17 02:20:36 +04:00
__asm__ __volatile__ ( " ldxa [%%g0] %1, %0 "
: " =&r " ( ret )
: " i " ( ASI_SAFARI_CONFIG ) ) ;
2017-04-12 23:07:37 +03:00
* val = ret ;
2005-04-17 02:20:36 +04:00
}
2017-04-12 23:07:37 +03:00
static void update_safari_cfg ( void * arg )
2005-04-17 02:20:36 +04:00
{
2017-04-12 23:07:37 +03:00
unsigned long reg , * new_bits = arg ;
read_safari_cfg ( & reg ) ;
reg & = ~ SAFARI_CFG_DIV_MASK ;
reg | = * new_bits ;
2005-04-17 02:20:36 +04:00
__asm__ __volatile__ ( " stxa %0, [%%g0] %1 \n \t "
" membar #Sync "
: /* no outputs */
2017-04-12 23:07:37 +03:00
: " r " ( reg ) , " i " ( ASI_SAFARI_CONFIG )
2005-04-17 02:20:36 +04:00
: " memory " ) ;
}
static unsigned long get_current_freq ( unsigned int cpu , unsigned long safari_cfg )
{
2005-08-19 01:35:38 +04:00
unsigned long clock_tick = sparc64_get_clock_tick ( cpu ) / 1000 ;
2005-04-17 02:20:36 +04:00
unsigned long ret ;
switch ( safari_cfg & SAFARI_CFG_DIV_MASK ) {
case SAFARI_CFG_DIV_1 :
ret = clock_tick / 1 ;
break ;
case SAFARI_CFG_DIV_2 :
ret = clock_tick / 2 ;
break ;
case SAFARI_CFG_DIV_32 :
ret = clock_tick / 32 ;
break ;
default :
BUG ( ) ;
2011-06-03 18:45:23 +04:00
}
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-08-19 01:35:38 +04:00
static unsigned int us3_freq_get ( unsigned int cpu )
{
unsigned long reg ;
2017-04-12 23:07:37 +03:00
if ( smp_call_function_single ( cpu , read_safari_cfg , & reg , 1 ) )
return 0 ;
return get_current_freq ( cpu , reg ) ;
2005-08-19 01:35:38 +04:00
}
2013-10-25 18:15:48 +04:00
static int us3_freq_target ( struct cpufreq_policy * policy , unsigned int index )
2005-04-17 02:20:36 +04:00
{
2013-03-24 10:26:43 +04:00
unsigned int cpu = policy - > cpu ;
2017-04-12 23:07:37 +03:00
unsigned long new_bits , new_freq ;
2005-04-17 02:20:36 +04:00
2005-08-19 01:35:38 +04:00
new_freq = sparc64_get_clock_tick ( cpu ) / 1000 ;
2005-04-17 02:20:36 +04:00
switch ( index ) {
case 0 :
new_bits = SAFARI_CFG_DIV_1 ;
new_freq / = 1 ;
break ;
case 1 :
new_bits = SAFARI_CFG_DIV_2 ;
new_freq / = 2 ;
break ;
case 2 :
new_bits = SAFARI_CFG_DIV_32 ;
new_freq / = 32 ;
break ;
default :
BUG ( ) ;
2011-06-03 18:45:23 +04:00
}
2005-04-17 02:20:36 +04:00
2017-04-12 23:07:37 +03:00
return smp_call_function_single ( cpu , update_safari_cfg , & new_bits , 1 ) ;
2005-04-17 02:20:36 +04:00
}
static int __init us3_freq_cpu_init ( struct cpufreq_policy * policy )
{
unsigned int cpu = policy - > cpu ;
2005-08-19 01:35:38 +04:00
unsigned long clock_tick = sparc64_get_clock_tick ( cpu ) / 1000 ;
2005-04-17 02:20:36 +04:00
struct cpufreq_frequency_table * table =
& us3_freq_table [ cpu ] . table [ 0 ] ;
2013-03-30 14:55:15 +04:00
table [ 0 ] . driver_data = 0 ;
2005-04-17 02:20:36 +04:00
table [ 0 ] . frequency = clock_tick / 1 ;
2013-03-30 14:55:15 +04:00
table [ 1 ] . driver_data = 1 ;
2005-04-17 02:20:36 +04:00
table [ 1 ] . frequency = clock_tick / 2 ;
2013-03-30 14:55:15 +04:00
table [ 2 ] . driver_data = 2 ;
2005-04-17 02:20:36 +04:00
table [ 2 ] . frequency = clock_tick / 32 ;
2013-03-30 14:55:15 +04:00
table [ 3 ] . driver_data = 0 ;
2005-04-17 02:20:36 +04:00
table [ 3 ] . frequency = CPUFREQ_TABLE_END ;
policy - > cpuinfo . transition_latency = 0 ;
policy - > cur = clock_tick ;
2018-02-26 08:09:07 +03:00
policy - > freq_table = table ;
2005-04-17 02:20:36 +04:00
2018-02-26 08:09:07 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int us3_freq_cpu_exit ( struct cpufreq_policy * policy )
{
2014-03-10 13:23:33 +04:00
if ( cpufreq_us3_driver )
2013-10-25 18:15:48 +04:00
us3_freq_target ( policy , 0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int __init us3_freq_init ( void )
{
unsigned long manuf , impl , ver ;
int ret ;
2006-02-09 13:52:44 +03:00
if ( tlb_type ! = cheetah & & tlb_type ! = cheetah_plus )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
__asm__ ( " rdpr %%ver, %0 " : " =r " ( ver ) ) ;
manuf = ( ( ver > > 48 ) & 0xffff ) ;
impl = ( ( ver > > 32 ) & 0xffff ) ;
if ( manuf = = CHEETAH_MANUF & &
2005-09-28 09:50:06 +04:00
( impl = = CHEETAH_IMPL | |
impl = = CHEETAH_PLUS_IMPL | |
impl = = JAGUAR_IMPL | |
impl = = PANTHER_IMPL ) ) {
2005-04-17 02:20:36 +04:00
struct cpufreq_driver * driver ;
ret = - ENOMEM ;
2013-08-06 21:23:06 +04:00
driver = kzalloc ( sizeof ( * driver ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! driver )
goto err_out ;
2013-08-06 21:23:06 +04:00
us3_freq_table = kzalloc ( ( NR_CPUS * sizeof ( * us3_freq_table ) ) ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL ) ;
if ( ! us3_freq_table )
goto err_out ;
2005-08-19 01:35:38 +04:00
driver - > init = us3_freq_cpu_init ;
2013-10-03 18:58:26 +04:00
driver - > verify = cpufreq_generic_frequency_table_verify ;
2013-10-25 18:15:48 +04:00
driver - > target_index = us3_freq_target ;
2005-08-19 01:35:38 +04:00
driver - > get = us3_freq_get ;
2005-04-17 02:20:36 +04:00
driver - > exit = us3_freq_cpu_exit ;
strcpy ( driver - > name , " UltraSPARC-III " ) ;
cpufreq_us3_driver = driver ;
ret = cpufreq_register_driver ( driver ) ;
if ( ret )
goto err_out ;
return 0 ;
err_out :
if ( driver ) {
kfree ( driver ) ;
cpufreq_us3_driver = NULL ;
}
2005-11-07 12:01:35 +03:00
kfree ( us3_freq_table ) ;
us3_freq_table = NULL ;
2005-04-17 02:20:36 +04:00
return ret ;
}
return - ENODEV ;
}
static void __exit us3_freq_exit ( void )
{
if ( cpufreq_us3_driver ) {
cpufreq_unregister_driver ( cpufreq_us3_driver ) ;
kfree ( cpufreq_us3_driver ) ;
cpufreq_us3_driver = NULL ;
kfree ( us3_freq_table ) ;
us3_freq_table = NULL ;
}
}
MODULE_AUTHOR ( " David S. Miller <davem@redhat.com> " ) ;
MODULE_DESCRIPTION ( " cpufreq driver for UltraSPARC-III " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( us3_freq_init ) ;
module_exit ( us3_freq_exit ) ;