2008-07-28 18:44:17 +02:00
/*
* Intel CPU Microcode Update Driver for Linux
*
* Copyright ( C ) 2000 - 2006 Tigran Aivazian < tigran @ aivazian . fsnet . co . uk >
* 2006 Shaohua Li < shaohua . li @ intel . com >
*
* This driver allows to upgrade microcode on Intel processors
* belonging to IA - 32 family - PentiumPro , Pentium II ,
* Pentium III , Xeon , Pentium 4 , etc .
*
* Reference : Section 8.11 of Volume 3 a , IA - 32 Intel ? Architecture
* Software Developer ' s Manual
* Order Number 253668 or free download from :
*
* http : //developer.intel.com/design/pentium4/manuals/253668.htm
*
* For more information , go to http : //www.urbanmyth.org/microcode
*
* 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 .
*
* 1.0 16 Feb 2000 , Tigran Aivazian < tigran @ sco . com >
* Initial release .
* 1.01 18 Feb 2000 , Tigran Aivazian < tigran @ sco . com >
* Added read ( ) support + cleanups .
* 1.02 21 Feb 2000 , Tigran Aivazian < tigran @ sco . com >
* Added ' device trimming ' support . open ( O_WRONLY ) zeroes
* and frees the saved copy of applied microcode .
* 1.03 29 Feb 2000 , Tigran Aivazian < tigran @ sco . com >
* Made to use devfs ( / dev / cpu / microcode ) + cleanups .
* 1.04 06 Jun 2000 , Simon Trimmer < simon @ veritas . com >
* Added misc device support ( now uses both devfs and misc ) .
* Added MICROCODE_IOCFREE ioctl to clear memory .
* 1.05 09 Jun 2000 , Simon Trimmer < simon @ veritas . com >
* Messages for error cases ( non Intel & no suitable microcode ) .
* 1.06 03 Aug 2000 , Tigran Aivazian < tigran @ veritas . com >
* Removed - > release ( ) . Removed exclusive open and status bitmap .
* Added microcode_rwsem to serialize read ( ) / write ( ) / ioctl ( ) .
* Removed global kernel lock usage .
* 1.07 07 Sep 2000 , Tigran Aivazian < tigran @ veritas . com >
* Write 0 to 0x8B msr and then cpuid before reading revision ,
* so that it works even if there were no update done by the
* BIOS . Otherwise , reading from 0x8B gives junk ( which happened
* to be 0 on my machine which is why it worked even when I
* disabled update by the BIOS )
* Thanks to Eric W . Biederman < ebiederman @ lnxi . com > for the fix .
* 1.08 11 Dec 2000 , Richard Schaal < richard . schaal @ intel . com > and
* Tigran Aivazian < tigran @ veritas . com >
* Intel Pentium 4 processor support and bugfixes .
* 1.09 30 Oct 2001 , Tigran Aivazian < tigran @ veritas . com >
* Bugfix for HT ( Hyper - Threading ) enabled processors
* whereby processor resources are shared by all logical processors
* in a single CPU package .
* 1.10 28 Feb 2002 Asit K Mallick < asit . k . mallick @ intel . com > and
* Tigran Aivazian < tigran @ veritas . com > ,
2008-08-01 12:46:45 +02:00
* Serialize updates as required on HT processors due to
* speculative nature of implementation .
2008-07-28 18:44:17 +02:00
* 1.11 22 Mar 2002 Tigran Aivazian < tigran @ veritas . com >
* Fix the panic when writing zero - length microcode chunk .
* 1.12 29 Sep 2003 Nitin Kamble < nitin . a . kamble @ intel . com > ,
* Jun Nakajima < jun . nakajima @ intel . com >
* Support for the microcode updates in the new format .
* 1.13 10 Oct 2003 Tigran Aivazian < tigran @ veritas . com >
* Removed - > read ( ) method and obsoleted MICROCODE_IOCFREE ioctl
* because we no longer hold a copy of applied microcode
* in kernel memory .
* 1.14 25 Jun 2004 Tigran Aivazian < tigran @ veritas . com >
* Fix sigmatch ( ) macro to handle old CPUs with pf = = 0.
* Thanks to Stuart Swales for pointing out this bug .
*/
2009-12-08 22:30:50 -08:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2009-03-11 11:19:46 +01:00
# include <linux/platform_device.h>
# include <linux/miscdevice.h>
2009-05-11 23:48:27 +02:00
# include <linux/capability.h>
2009-03-11 11:19:46 +01:00
# include <linux/kernel.h>
# include <linux/module.h>
2008-07-28 18:44:17 +02:00
# include <linux/mutex.h>
# include <linux/cpu.h>
2009-03-11 11:19:46 +01:00
# include <linux/fs.h>
# include <linux/mm.h>
2008-07-28 18:44:17 +02:00
# include <asm/microcode.h>
2009-03-11 11:19:46 +01:00
# include <asm/processor.h>
2008-07-28 18:44:17 +02:00
MODULE_DESCRIPTION ( " Microcode Update Driver " ) ;
MODULE_AUTHOR ( " Tigran Aivazian <tigran@aivazian.fsnet.co.uk> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-03-11 11:19:46 +01:00
# define MICROCODE_VERSION "2.00"
2008-07-28 18:44:17 +02:00
2009-03-11 11:19:46 +01:00
static struct microcode_ops * microcode_ops ;
2008-07-28 18:44:17 +02:00
2009-05-11 23:48:27 +02:00
/*
* Synchronization .
*
* All non cpu - hotplug - callback call sites use :
*
* - microcode_mutex to synchronize with each other ;
* - get / put_online_cpus ( ) to synchronize with
* the cpu - hotplug - callback call sites .
*
* We guarantee that only a single cpu is being
* updated at any particular moment of time .
*/
2008-08-20 00:22:26 +02:00
static DEFINE_MUTEX ( microcode_mutex ) ;
2008-07-28 18:44:17 +02:00
2009-03-11 11:19:46 +01:00
struct ucode_cpu_info ucode_cpu_info [ NR_CPUS ] ;
2008-07-28 18:44:21 +02:00
EXPORT_SYMBOL_GPL ( ucode_cpu_info ) ;
2008-07-28 18:44:17 +02:00
2009-05-11 23:48:27 +02:00
/*
* Operations that are run on a target cpu :
*/
struct cpu_info_ctx {
struct cpu_signature * cpu_sig ;
int err ;
} ;
static void collect_cpu_info_local ( void * arg )
{
struct cpu_info_ctx * ctx = arg ;
ctx - > err = microcode_ops - > collect_cpu_info ( smp_processor_id ( ) ,
ctx - > cpu_sig ) ;
}
static int collect_cpu_info_on_target ( int cpu , struct cpu_signature * cpu_sig )
{
struct cpu_info_ctx ctx = { . cpu_sig = cpu_sig , . err = 0 } ;
int ret ;
ret = smp_call_function_single ( cpu , collect_cpu_info_local , & ctx , 1 ) ;
if ( ! ret )
ret = ctx . err ;
return ret ;
}
static int collect_cpu_info ( int cpu )
{
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
int ret ;
memset ( uci , 0 , sizeof ( * uci ) ) ;
ret = collect_cpu_info_on_target ( cpu , & uci - > cpu_sig ) ;
if ( ! ret )
uci - > valid = 1 ;
return ret ;
}
struct apply_microcode_ctx {
int err ;
} ;
static void apply_microcode_local ( void * arg )
{
struct apply_microcode_ctx * ctx = arg ;
ctx - > err = microcode_ops - > apply_microcode ( smp_processor_id ( ) ) ;
}
static int apply_microcode_on_target ( int cpu )
{
struct apply_microcode_ctx ctx = { . err = 0 } ;
int ret ;
ret = smp_call_function_single ( cpu , apply_microcode_local , & ctx , 1 ) ;
if ( ! ret )
ret = ctx . err ;
return ret ;
}
2008-07-28 18:44:17 +02:00
# ifdef CONFIG_MICROCODE_OLD_INTERFACE
2008-09-11 23:27:52 +02:00
static int do_microcode_update ( const void __user * buf , size_t size )
2008-07-28 18:44:17 +02:00
{
int error = 0 ;
int cpu ;
2009-04-14 19:25:42 +01:00
2008-09-11 23:27:52 +02:00
for_each_online_cpu ( cpu ) {
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
2009-05-11 23:48:27 +02:00
enum ucode_state ustate ;
2008-09-11 23:27:52 +02:00
if ( ! uci - > valid )
continue ;
2009-04-14 19:25:42 +01:00
2009-05-11 23:48:27 +02:00
ustate = microcode_ops - > request_microcode_user ( cpu , buf , size ) ;
if ( ustate = = UCODE_ERROR ) {
error = - 1 ;
break ;
} else if ( ustate = = UCODE_OK )
apply_microcode_on_target ( cpu ) ;
2008-07-28 18:44:17 +02:00
}
2009-05-11 23:48:27 +02:00
2008-07-28 18:44:17 +02:00
return error ;
}
2008-08-01 12:46:45 +02:00
static int microcode_open ( struct inode * unused1 , struct file * unused2 )
2008-07-28 18:44:17 +02:00
{
return capable ( CAP_SYS_RAWIO ) ? 0 : - EPERM ;
}
2008-08-01 12:46:45 +02:00
static ssize_t microcode_write ( struct file * file , const char __user * buf ,
size_t len , loff_t * ppos )
2008-07-28 18:44:17 +02:00
{
2009-05-11 23:48:27 +02:00
ssize_t ret = - EINVAL ;
2008-07-28 18:44:17 +02:00
2009-09-21 17:03:05 -07:00
if ( ( len > > PAGE_SHIFT ) > totalram_pages ) {
2009-12-08 22:30:50 -08:00
pr_err ( " too much data (max %ld pages) \n " , totalram_pages ) ;
2009-05-11 23:48:27 +02:00
return ret ;
2008-07-28 18:44:17 +02:00
}
get_online_cpus ( ) ;
mutex_lock ( & microcode_mutex ) ;
2009-05-11 23:48:27 +02:00
if ( do_microcode_update ( buf , len ) = = 0 )
2008-07-28 18:44:17 +02:00
ret = ( ssize_t ) len ;
mutex_unlock ( & microcode_mutex ) ;
put_online_cpus ( ) ;
return ret ;
}
static const struct file_operations microcode_fops = {
2009-05-11 23:48:27 +02:00
. owner = THIS_MODULE ,
. write = microcode_write ,
. open = microcode_open ,
2008-07-28 18:44:17 +02:00
} ;
static struct miscdevice microcode_dev = {
2009-05-11 23:48:27 +02:00
. minor = MICROCODE_MINOR ,
. name = " microcode " ,
2009-09-18 23:01:12 +02:00
. nodename = " cpu/microcode " ,
2009-05-11 23:48:27 +02:00
. fops = & microcode_fops ,
2008-07-28 18:44:17 +02:00
} ;
2008-08-01 12:46:45 +02:00
static int __init microcode_dev_init ( void )
2008-07-28 18:44:17 +02:00
{
int error ;
error = misc_register ( & microcode_dev ) ;
if ( error ) {
2009-12-08 22:30:50 -08:00
pr_err ( " can't misc_register on minor=%d \n " , MICROCODE_MINOR ) ;
2008-07-28 18:44:17 +02:00
return error ;
}
return 0 ;
}
2008-08-01 12:46:45 +02:00
static void microcode_dev_exit ( void )
2008-07-28 18:44:17 +02:00
{
misc_deregister ( & microcode_dev ) ;
}
MODULE_ALIAS_MISCDEV ( MICROCODE_MINOR ) ;
# else
2009-03-11 11:19:46 +01:00
# define microcode_dev_init() 0
# define microcode_dev_exit() do { } while (0)
2008-07-28 18:44:17 +02:00
# endif
/* fake device for request_firmware */
2009-03-11 11:19:46 +01:00
static struct platform_device * microcode_pdev ;
2008-07-28 18:44:17 +02:00
2009-05-11 23:48:27 +02:00
static int reload_for_cpu ( int cpu )
2009-03-11 16:32:36 +10:30
{
2009-05-11 23:48:27 +02:00
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
2009-03-11 16:32:36 +10:30
int err = 0 ;
mutex_lock ( & microcode_mutex ) ;
if ( uci - > valid ) {
2009-05-11 23:48:27 +02:00
enum ucode_state ustate ;
ustate = microcode_ops - > request_microcode_fw ( cpu , & microcode_pdev - > dev ) ;
if ( ustate = = UCODE_OK )
apply_microcode_on_target ( cpu ) ;
else
if ( ustate = = UCODE_ERROR )
err = - EINVAL ;
2009-03-11 16:32:36 +10:30
}
mutex_unlock ( & microcode_mutex ) ;
2009-05-11 23:48:27 +02:00
2009-03-11 16:32:36 +10:30
return err ;
}
2008-07-28 18:44:17 +02:00
static ssize_t reload_store ( struct sys_device * dev ,
struct sysdev_attribute * attr ,
2009-05-11 23:48:27 +02:00
const char * buf , size_t size )
2008-07-28 18:44:17 +02:00
{
2009-05-11 23:48:27 +02:00
unsigned long val ;
2008-07-28 18:44:17 +02:00
int cpu = dev - > id ;
2009-05-11 23:48:27 +02:00
int ret = 0 ;
char * end ;
2008-07-28 18:44:17 +02:00
2009-05-11 23:48:27 +02:00
val = simple_strtoul ( buf , & end , 0 ) ;
2008-07-28 18:44:17 +02:00
if ( end = = buf )
return - EINVAL ;
2009-05-11 23:48:27 +02:00
2008-07-28 18:44:17 +02:00
if ( val = = 1 ) {
get_online_cpus ( ) ;
2009-03-11 16:32:36 +10:30
if ( cpu_online ( cpu ) )
2009-05-11 23:48:27 +02:00
ret = reload_for_cpu ( cpu ) ;
2008-07-28 18:44:17 +02:00
put_online_cpus ( ) ;
}
2009-05-11 23:48:27 +02:00
if ( ! ret )
ret = size ;
return ret ;
2008-07-28 18:44:17 +02:00
}
static ssize_t version_show ( struct sys_device * dev ,
struct sysdev_attribute * attr , char * buf )
{
struct ucode_cpu_info * uci = ucode_cpu_info + dev - > id ;
2008-08-20 00:22:26 +02:00
return sprintf ( buf , " 0x%x \n " , uci - > cpu_sig . rev ) ;
2008-07-28 18:44:17 +02:00
}
static ssize_t pf_show ( struct sys_device * dev ,
struct sysdev_attribute * attr , char * buf )
{
struct ucode_cpu_info * uci = ucode_cpu_info + dev - > id ;
2008-08-20 00:22:26 +02:00
return sprintf ( buf , " 0x%x \n " , uci - > cpu_sig . pf ) ;
2008-07-28 18:44:17 +02:00
}
static SYSDEV_ATTR ( reload , 0200 , NULL , reload_store ) ;
static SYSDEV_ATTR ( version , 0400 , version_show , NULL ) ;
static SYSDEV_ATTR ( processor_flags , 0400 , pf_show , NULL ) ;
static struct attribute * mc_default_attrs [ ] = {
& attr_reload . attr ,
& attr_version . attr ,
& attr_processor_flags . attr ,
NULL
} ;
static struct attribute_group mc_attr_group = {
2009-05-11 23:48:27 +02:00
. attrs = mc_default_attrs ,
. name = " microcode " ,
2008-07-28 18:44:17 +02:00
} ;
2009-05-11 23:48:27 +02:00
static void microcode_fini_cpu ( int cpu )
2008-08-20 00:22:26 +02:00
{
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
microcode_ops - > microcode_fini_cpu ( cpu ) ;
uci - > valid = 0 ;
2008-12-20 00:15:24 +01:00
}
2009-05-11 23:48:27 +02:00
static enum ucode_state microcode_resume_cpu ( int cpu )
2008-08-20 00:22:26 +02:00
{
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
2009-05-11 23:48:27 +02:00
if ( ! uci - > mc )
return UCODE_NFOUND ;
2009-12-08 22:30:50 -08:00
pr_debug ( " CPU%d updated upon resume \n " , cpu ) ;
2009-05-11 23:48:27 +02:00
apply_microcode_on_target ( cpu ) ;
return UCODE_OK ;
2008-08-20 00:22:26 +02:00
}
2009-05-11 23:48:27 +02:00
static enum ucode_state microcode_init_cpu ( int cpu )
2008-08-20 00:22:26 +02:00
{
2009-05-11 23:48:27 +02:00
enum ucode_state ustate ;
2008-08-20 00:22:26 +02:00
2009-05-11 23:48:27 +02:00
if ( collect_cpu_info ( cpu ) )
return UCODE_ERROR ;
2008-08-20 00:22:26 +02:00
2009-05-11 23:48:27 +02:00
/* --dimm. Trigger a delayed update? */
if ( system_state ! = SYSTEM_RUNNING )
return UCODE_NFOUND ;
2008-08-20 00:22:26 +02:00
2009-05-11 23:48:27 +02:00
ustate = microcode_ops - > request_microcode_fw ( cpu , & microcode_pdev - > dev ) ;
2008-08-20 00:22:26 +02:00
2009-05-11 23:48:27 +02:00
if ( ustate = = UCODE_OK ) {
2009-12-08 22:30:50 -08:00
pr_debug ( " CPU%d updated upon init \n " , cpu ) ;
2009-05-11 23:48:27 +02:00
apply_microcode_on_target ( cpu ) ;
2008-08-20 00:22:26 +02:00
}
2009-05-11 23:48:27 +02:00
return ustate ;
2008-08-20 00:22:26 +02:00
}
2009-05-11 23:48:27 +02:00
static enum ucode_state microcode_update_cpu ( int cpu )
2008-08-20 00:22:26 +02:00
{
2009-05-11 23:48:27 +02:00
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
enum ucode_state ustate ;
2008-08-20 00:22:26 +02:00
2009-11-11 20:03:29 +01:00
if ( uci - > valid & & uci - > mc )
2009-05-11 23:48:27 +02:00
ustate = microcode_resume_cpu ( cpu ) ;
else
ustate = microcode_init_cpu ( cpu ) ;
2008-08-20 00:22:26 +02:00
2009-05-11 23:48:27 +02:00
return ustate ;
2008-08-20 00:22:26 +02:00
}
static int mc_sysdev_add ( struct sys_device * sys_dev )
2008-07-28 18:44:17 +02:00
{
int err , cpu = sys_dev - > id ;
if ( ! cpu_online ( cpu ) )
return 0 ;
2009-12-08 22:30:50 -08:00
pr_debug ( " CPU%d added \n " , cpu ) ;
2008-07-28 18:44:17 +02:00
err = sysfs_create_group ( & sys_dev - > kobj , & mc_attr_group ) ;
if ( err )
return err ;
2009-05-11 23:48:27 +02:00
if ( microcode_init_cpu ( cpu ) = = UCODE_ERROR )
err = - EINVAL ;
2009-03-11 16:32:36 +10:30
return err ;
2008-07-28 18:44:17 +02:00
}
static int mc_sysdev_remove ( struct sys_device * sys_dev )
{
int cpu = sys_dev - > id ;
if ( ! cpu_online ( cpu ) )
return 0 ;
2009-12-08 22:30:50 -08:00
pr_debug ( " CPU%d removed \n " , cpu ) ;
2008-08-20 00:22:26 +02:00
microcode_fini_cpu ( cpu ) ;
2008-07-28 18:44:17 +02:00
sysfs_remove_group ( & sys_dev - > kobj , & mc_attr_group ) ;
return 0 ;
}
static int mc_sysdev_resume ( struct sys_device * dev )
{
int cpu = dev - > id ;
2009-05-11 23:48:27 +02:00
struct ucode_cpu_info * uci = ucode_cpu_info + cpu ;
2008-07-28 18:44:17 +02:00
if ( ! cpu_online ( cpu ) )
return 0 ;
2008-09-14 14:50:26 +02:00
2009-05-11 23:48:27 +02:00
/*
* All non - bootup cpus are still disabled ,
* so only CPU 0 will apply ucode here .
*
* Moreover , there can be no concurrent
* updates from any other places at this point .
*/
WARN_ON ( cpu ! = 0 ) ;
if ( uci - > valid & & uci - > mc )
microcode_ops - > apply_microcode ( cpu ) ;
2008-07-28 18:44:17 +02:00
return 0 ;
}
static struct sysdev_driver mc_sysdev_driver = {
2009-05-11 23:48:27 +02:00
. add = mc_sysdev_add ,
. remove = mc_sysdev_remove ,
. resume = mc_sysdev_resume ,
2008-07-28 18:44:17 +02:00
} ;
static __cpuinit int
mc_cpu_callback ( struct notifier_block * nb , unsigned long action , void * hcpu )
{
unsigned int cpu = ( unsigned long ) hcpu ;
struct sys_device * sys_dev ;
sys_dev = get_cpu_sysdev ( cpu ) ;
switch ( action ) {
case CPU_ONLINE :
case CPU_ONLINE_FROZEN :
2009-05-11 23:48:27 +02:00
microcode_update_cpu ( cpu ) ;
2008-08-20 00:22:26 +02:00
case CPU_DOWN_FAILED :
2008-07-28 18:44:17 +02:00
case CPU_DOWN_FAILED_FROZEN :
2009-12-08 22:30:50 -08:00
pr_debug ( " CPU%d added \n " , cpu ) ;
2008-07-28 18:44:17 +02:00
if ( sysfs_create_group ( & sys_dev - > kobj , & mc_attr_group ) )
2009-12-08 22:30:50 -08:00
pr_err ( " Failed to create group for CPU%d \n " , cpu ) ;
2008-07-28 18:44:17 +02:00
break ;
case CPU_DOWN_PREPARE :
case CPU_DOWN_PREPARE_FROZEN :
/* Suspend is in progress, only remove the interface */
sysfs_remove_group ( & sys_dev - > kobj , & mc_attr_group ) ;
2009-12-08 22:30:50 -08:00
pr_debug ( " CPU%d removed \n " , cpu ) ;
2008-08-20 00:22:26 +02:00
break ;
case CPU_DEAD :
case CPU_UP_CANCELED_FROZEN :
/* The CPU refused to come up during a system resume */
microcode_fini_cpu ( cpu ) ;
2008-07-28 18:44:17 +02:00
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block __refdata mc_cpu_notifier = {
2009-03-11 11:19:46 +01:00
. notifier_call = mc_cpu_callback ,
2008-07-28 18:44:17 +02:00
} ;
2008-09-23 12:08:44 +02:00
static int __init microcode_init ( void )
2008-07-28 18:44:17 +02:00
{
2008-09-23 12:08:44 +02:00
struct cpuinfo_x86 * c = & cpu_data ( 0 ) ;
2008-07-28 18:44:17 +02:00
int error ;
2008-09-23 12:08:44 +02:00
if ( c - > x86_vendor = = X86_VENDOR_INTEL )
microcode_ops = init_intel_microcode ( ) ;
2008-09-24 11:50:35 +02:00
else if ( c - > x86_vendor = = X86_VENDOR_AMD )
2008-09-23 12:08:44 +02:00
microcode_ops = init_amd_microcode ( ) ;
2008-07-28 18:44:21 +02:00
2008-09-23 12:08:44 +02:00
if ( ! microcode_ops ) {
2009-12-08 22:30:50 -08:00
pr_err ( " no support for this CPU vendor \n " ) ;
2008-09-23 12:08:44 +02:00
return - ENODEV ;
}
2008-07-28 18:44:17 +02:00
microcode_pdev = platform_device_register_simple ( " microcode " , - 1 ,
NULL , 0 ) ;
if ( IS_ERR ( microcode_pdev ) ) {
microcode_dev_exit ( ) ;
return PTR_ERR ( microcode_pdev ) ;
}
2009-11-10 12:07:23 +01:00
if ( microcode_ops - > init )
microcode_ops - > init ( & microcode_pdev - > dev ) ;
2008-07-28 18:44:17 +02:00
get_online_cpus ( ) ;
2009-05-11 23:48:27 +02:00
mutex_lock ( & microcode_mutex ) ;
2008-07-28 18:44:17 +02:00
error = sysdev_driver_register ( & cpu_sysdev_class , & mc_sysdev_driver ) ;
2009-05-11 23:48:27 +02:00
mutex_unlock ( & microcode_mutex ) ;
2008-07-28 18:44:17 +02:00
put_online_cpus ( ) ;
2009-05-11 23:48:27 +02:00
2008-07-28 18:44:17 +02:00
if ( error ) {
platform_device_unregister ( microcode_pdev ) ;
return error ;
}
2009-05-11 23:48:27 +02:00
error = microcode_dev_init ( ) ;
if ( error )
return error ;
2008-07-28 18:44:17 +02:00
register_hotcpu_notifier ( & mc_cpu_notifier ) ;
2008-07-28 18:44:21 +02:00
2009-05-11 23:48:27 +02:00
pr_info ( " Microcode Update Driver: v " MICROCODE_VERSION
2009-12-08 22:30:50 -08:00
" <tigran@aivazian.fsnet.co.uk>, Peter Oruba \n " ) ;
2008-07-28 18:44:21 +02:00
2008-07-28 18:44:17 +02:00
return 0 ;
}
2009-05-11 23:48:27 +02:00
module_init ( microcode_init ) ;
2008-07-28 18:44:17 +02:00
2008-09-23 12:08:44 +02:00
static void __exit microcode_exit ( void )
2008-07-28 18:44:17 +02:00
{
microcode_dev_exit ( ) ;
unregister_hotcpu_notifier ( & mc_cpu_notifier ) ;
get_online_cpus ( ) ;
2009-05-11 23:48:27 +02:00
mutex_lock ( & microcode_mutex ) ;
2008-07-28 18:44:17 +02:00
sysdev_driver_unregister ( & cpu_sysdev_class , & mc_sysdev_driver ) ;
2009-05-11 23:48:27 +02:00
mutex_unlock ( & microcode_mutex ) ;
2008-07-28 18:44:17 +02:00
put_online_cpus ( ) ;
platform_device_unregister ( microcode_pdev ) ;
2009-11-10 12:07:23 +01:00
if ( microcode_ops - > fini )
microcode_ops - > fini ( ) ;
2008-07-28 18:44:21 +02:00
microcode_ops = NULL ;
2009-05-11 23:48:27 +02:00
pr_info ( " Microcode Update Driver: v " MICROCODE_VERSION " removed. \n " ) ;
2008-07-28 18:44:21 +02:00
}
2008-09-23 12:08:44 +02:00
module_exit ( microcode_exit ) ;