2005-06-01 06:03:45 +04:00
/*
* sc520_freq . c : cpufreq driver for the AMD Elan sc520
*
* Copyright ( C ) 2005 Sean Young < sean @ mess . org >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Based on elanfreq . c
*
* 2005 - 03 - 30 : - initial revision
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/cpufreq.h>
2009-01-18 09:27:35 +03:00
# include <linux/timex.h>
# include <linux/io.h>
2005-06-01 06:03:45 +04:00
2012-01-26 03:09:12 +04:00
# include <asm/cpu_device_id.h>
2005-06-01 06:03:45 +04:00
# include <asm/msr.h>
# define MMCR_BASE 0xfffef000 /* The default base address */
# define OFFS_CPUCTL 0x2 /* CPU Control Register */
static __u8 __iomem * cpuctl ;
2009-01-18 09:27:35 +03:00
# define PFX "sc520_freq: "
2005-06-01 06:03:45 +04:00
static struct cpufreq_frequency_table sc520_freq_table [ ] = {
2014-03-28 17:41:47 +04:00
{ 0 , 0x01 , 100000 } ,
{ 0 , 0x02 , 133000 } ,
{ 0 , 0 , CPUFREQ_TABLE_END } ,
2005-06-01 06:03:45 +04:00
} ;
static unsigned int sc520_freq_get_cpu_frequency ( unsigned int cpu )
{
u8 clockspeed_reg = * cpuctl ;
switch ( clockspeed_reg & 0x03 ) {
default :
2009-01-18 09:27:35 +03:00
printk ( KERN_ERR PFX " error: cpuctl register has unexpected "
" value %02x \n " , clockspeed_reg ) ;
2005-06-01 06:03:45 +04:00
case 0x01 :
return 100000 ;
case 0x02 :
return 133000 ;
}
}
2013-10-25 18:15:48 +04:00
static int sc520_freq_target ( struct cpufreq_policy * policy , unsigned int state )
2005-06-01 06:03:45 +04:00
{
u8 clockspeed_reg ;
local_irq_disable ( ) ;
clockspeed_reg = * cpuctl & ~ 0x03 ;
2013-03-30 14:55:15 +04:00
* cpuctl = clockspeed_reg | sc520_freq_table [ state ] . driver_data ;
2005-06-01 06:03:45 +04:00
local_irq_enable ( ) ;
return 0 ;
}
/*
* Module init and exit code
*/
static int sc520_freq_cpu_init ( struct cpufreq_policy * policy )
{
2007-10-19 22:35:04 +04:00
struct cpuinfo_x86 * c = & cpu_data ( 0 ) ;
2005-06-01 06:03:45 +04:00
/* capability check */
if ( c - > x86_vendor ! = X86_VENDOR_AMD | |
c - > x86 ! = 4 | | c - > x86_model ! = 9 )
return - ENODEV ;
/* cpuinfo and default policy values */
policy - > cpuinfo . transition_latency = 1000000 ; /* 1ms */
2013-09-16 17:26:34 +04:00
return cpufreq_table_validate_and_show ( policy , sc520_freq_table ) ;
2005-06-01 06:03:45 +04:00
}
2007-02-27 01:55:48 +03:00
static struct cpufreq_driver sc520_freq_driver = {
2005-06-01 06:03:45 +04:00
. get = sc520_freq_get_cpu_frequency ,
2013-10-03 18:58:24 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = sc520_freq_target ,
2005-06-01 06:03:45 +04:00
. init = sc520_freq_cpu_init ,
. name = " sc520_freq " ,
2013-10-03 18:58:24 +04:00
. attr = cpufreq_generic_attr ,
2005-06-01 06:03:45 +04:00
} ;
2012-01-26 03:09:12 +04:00
static const struct x86_cpu_id sc520_ids [ ] = {
{ X86_VENDOR_AMD , 4 , 9 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( x86cpu , sc520_ids ) ;
2005-06-01 06:03:45 +04:00
static int __init sc520_freq_init ( void )
{
2006-10-17 08:32:55 +04:00
int err ;
2005-06-01 06:03:45 +04:00
2012-01-26 03:09:12 +04:00
if ( ! x86_match_cpu ( sc520_ids ) )
2005-06-01 06:03:45 +04:00
return - ENODEV ;
2012-01-26 03:09:12 +04:00
2005-06-01 06:03:45 +04:00
cpuctl = ioremap ( ( unsigned long ) ( MMCR_BASE + OFFS_CPUCTL ) , 1 ) ;
2009-01-18 09:27:35 +03:00
if ( ! cpuctl ) {
2005-06-01 06:03:45 +04:00
printk ( KERN_ERR " sc520_freq: error: failed to remap memory \n " ) ;
return - ENOMEM ;
}
2006-10-17 08:32:55 +04:00
err = cpufreq_register_driver ( & sc520_freq_driver ) ;
if ( err )
iounmap ( cpuctl ) ;
return err ;
2005-06-01 06:03:45 +04:00
}
static void __exit sc520_freq_exit ( void )
{
cpufreq_unregister_driver ( & sc520_freq_driver ) ;
iounmap ( cpuctl ) ;
}
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Sean Young <sean@mess.org> " ) ;
MODULE_DESCRIPTION ( " cpufreq driver for AMD's Elan sc520 CPU " ) ;
module_init ( sc520_freq_init ) ;
module_exit ( sc520_freq_exit ) ;