2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-05-31 19:03:45 -07:00
/*
* sc520_freq . c : cpufreq driver for the AMD Elan sc520
*
* Copyright ( C ) 2005 Sean Young < sean @ mess . org >
*
* Based on elanfreq . c
*
* 2005 - 03 - 30 : - initial revision
*/
2016-04-05 13:28:25 -07:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-05-31 19:03:45 -07:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/cpufreq.h>
2009-01-18 01:27:35 -05:00
# include <linux/timex.h>
# include <linux/io.h>
2005-05-31 19:03:45 -07:00
2012-01-26 00:09:12 +01:00
# include <asm/cpu_device_id.h>
2005-05-31 19:03:45 -07:00
# include <asm/msr.h>
# define MMCR_BASE 0xfffef000 /* The default base address */
# define OFFS_CPUCTL 0x2 /* CPU Control Register */
static __u8 __iomem * cpuctl ;
static struct cpufreq_frequency_table sc520_freq_table [ ] = {
2014-03-28 19:11:47 +05:30
{ 0 , 0x01 , 100000 } ,
{ 0 , 0x02 , 133000 } ,
{ 0 , 0 , CPUFREQ_TABLE_END } ,
2005-05-31 19:03:45 -07:00
} ;
static unsigned int sc520_freq_get_cpu_frequency ( unsigned int cpu )
{
u8 clockspeed_reg = * cpuctl ;
switch ( clockspeed_reg & 0x03 ) {
default :
2016-04-05 13:28:25 -07:00
pr_err ( " error: cpuctl register has unexpected value %02x \n " ,
2016-04-05 13:28:24 -07:00
clockspeed_reg ) ;
2021-05-03 09:18:40 -07:00
fallthrough ;
2005-05-31 19:03:45 -07:00
case 0x01 :
return 100000 ;
case 0x02 :
return 133000 ;
}
}
2013-10-25 19:45:48 +05:30
static int sc520_freq_target ( struct cpufreq_policy * policy , unsigned int state )
2005-05-31 19:03:45 -07:00
{
u8 clockspeed_reg ;
local_irq_disable ( ) ;
clockspeed_reg = * cpuctl & ~ 0x03 ;
2013-03-30 16:25:15 +05:30
* cpuctl = clockspeed_reg | sc520_freq_table [ state ] . driver_data ;
2005-05-31 19:03:45 -07:00
local_irq_enable ( ) ;
return 0 ;
}
/*
* Module init and exit code
*/
static int sc520_freq_cpu_init ( struct cpufreq_policy * policy )
{
2007-10-19 20:35:04 +02:00
struct cpuinfo_x86 * c = & cpu_data ( 0 ) ;
2005-05-31 19:03:45 -07: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 */
2018-02-26 10:39:03 +05:30
policy - > freq_table = sc520_freq_table ;
2005-05-31 19:03:45 -07:00
2018-02-26 10:39:03 +05:30
return 0 ;
2005-05-31 19:03:45 -07:00
}
2007-02-26 14:55:48 -08:00
static struct cpufreq_driver sc520_freq_driver = {
2005-05-31 19:03:45 -07:00
. get = sc520_freq_get_cpu_frequency ,
2013-10-03 20:28:24 +05:30
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 19:45:48 +05:30
. target_index = sc520_freq_target ,
2005-05-31 19:03:45 -07:00
. init = sc520_freq_cpu_init ,
. name = " sc520_freq " ,
2013-10-03 20:28:24 +05:30
. attr = cpufreq_generic_attr ,
2005-05-31 19:03:45 -07:00
} ;
2012-01-26 00:09:12 +01:00
static const struct x86_cpu_id sc520_ids [ ] = {
2020-03-24 14:51:51 +01:00
X86_MATCH_VENDOR_FAM_MODEL ( AMD , 4 , 9 , NULL ) ,
2012-01-26 00:09:12 +01:00
{ }
} ;
MODULE_DEVICE_TABLE ( x86cpu , sc520_ids ) ;
2005-05-31 19:03:45 -07:00
static int __init sc520_freq_init ( void )
{
2006-10-17 10:02:55 +05:30
int err ;
2005-05-31 19:03:45 -07:00
2012-01-26 00:09:12 +01:00
if ( ! x86_match_cpu ( sc520_ids ) )
2005-05-31 19:03:45 -07:00
return - ENODEV ;
2012-01-26 00:09:12 +01:00
2005-05-31 19:03:45 -07:00
cpuctl = ioremap ( ( unsigned long ) ( MMCR_BASE + OFFS_CPUCTL ) , 1 ) ;
2009-01-18 01:27:35 -05:00
if ( ! cpuctl ) {
2016-04-05 13:28:24 -07:00
pr_err ( " sc520_freq: error: failed to remap memory \n " ) ;
2005-05-31 19:03:45 -07:00
return - ENOMEM ;
}
2006-10-17 10:02:55 +05:30
err = cpufreq_register_driver ( & sc520_freq_driver ) ;
if ( err )
iounmap ( cpuctl ) ;
return err ;
2005-05-31 19:03:45 -07: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 ) ;