2005-04-17 02:20:36 +04:00
/*
* Copyright 2004 James Cleverdon , IBM .
* Subject to the GNU Public License , v .2
*
2005-07-29 08:15:42 +04:00
* Flat APIC subarch code .
2005-04-17 02:20:36 +04:00
*
* Hacked for x86 - 64 by James Cleverdon from i386 architecture code by
* Martin Bligh , Andi Kleen , James Bottomley , John Stultz , and
* James Cleverdon .
*/
# include <linux/threads.h>
# include <linux/cpumask.h>
# include <linux/string.h>
# include <linux/kernel.h>
# include <linux/ctype.h>
# include <linux/init.h>
# include <asm/smp.h>
# include <asm/ipi.h>
2007-05-02 21:27:04 +04:00
# include <asm/genapic.h>
2005-04-17 02:20:36 +04:00
static cpumask_t flat_target_cpus ( void )
{
return cpu_online_map ;
}
2006-10-08 17:47:55 +04:00
static cpumask_t flat_vector_allocation_domain ( int cpu )
{
/* Careful. Some cpus do not strictly honor the set of cpus
* specified in the interrupt destination when using lowest
* priority interrupt delivery mode .
*
* In particular there was a hyperthreading cpu observed to
* deliver interrupts to the wrong hyperthread when only one
* hyperthread was specified in the interrupt desitination .
*/
cpumask_t domain = { { [ 0 ] = APIC_ALL_CPUS , } } ;
return domain ;
}
2005-04-17 02:20:36 +04:00
/*
* Set up the logical destination ID .
*
* Intel recommends to set DFR , LDR and TPR before enabling
* an APIC . See e . g . " AP-388 82489DX User's Manual " ( Intel
* document number 292116 ) . So here it goes . . .
*/
static void flat_init_apic_ldr ( void )
{
unsigned long val ;
unsigned long num , id ;
num = smp_processor_id ( ) ;
id = 1UL < < num ;
x86_cpu_to_log_apicid [ num ] = id ;
2005-09-12 20:49:23 +04:00
apic_write ( APIC_DFR , APIC_DFR_FLAT ) ;
2005-04-17 02:20:36 +04:00
val = apic_read ( APIC_LDR ) & ~ APIC_LDR_MASK ;
val | = SET_APIC_LOGICAL_ID ( id ) ;
2005-09-12 20:49:23 +04:00
apic_write ( APIC_LDR , val ) ;
2005-04-17 02:20:36 +04:00
}
static void flat_send_IPI_mask ( cpumask_t cpumask , int vector )
{
unsigned long mask = cpus_addr ( cpumask ) [ 0 ] ;
unsigned long cfg ;
unsigned long flags ;
2006-09-26 12:52:33 +04:00
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
/*
* Wait for idle .
*/
apic_wait_icr_idle ( ) ;
/*
* prepare target chip field
*/
cfg = __prepare_ICR2 ( mask ) ;
2005-09-12 20:49:23 +04:00
apic_write ( APIC_ICR2 , cfg ) ;
2005-04-17 02:20:36 +04:00
/*
* program the ICR
*/
cfg = __prepare_ICR ( 0 , vector , APIC_DEST_LOGICAL ) ;
/*
* Send the IPI . The write to APIC_ICR fires this off .
*/
2005-09-12 20:49:23 +04:00
apic_write ( APIC_ICR , cfg ) ;
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
}
2005-06-26 01:55:03 +04:00
static void flat_send_IPI_allbutself ( int vector )
{
2006-06-26 15:59:56 +04:00
# ifdef CONFIG_HOTPLUG_CPU
int hotplug = 1 ;
2005-09-10 00:01:52 +04:00
# else
2006-06-26 15:59:56 +04:00
int hotplug = 0 ;
# endif
if ( hotplug | | vector = = NMI_VECTOR ) {
cpumask_t allbutme = cpu_online_map ;
2006-01-12 00:43:09 +03:00
2006-06-26 15:59:56 +04:00
cpu_clear ( smp_processor_id ( ) , allbutme ) ;
2005-09-10 00:01:52 +04:00
2006-06-26 15:59:56 +04:00
if ( ! cpus_empty ( allbutme ) )
flat_send_IPI_mask ( allbutme , vector ) ;
} else if ( num_online_cpus ( ) > 1 ) {
__send_IPI_shortcut ( APIC_DEST_ALLBUT , vector , APIC_DEST_LOGICAL ) ;
}
2005-06-26 01:55:02 +04:00
}
static void flat_send_IPI_all ( int vector )
{
2006-06-26 15:59:56 +04:00
if ( vector = = NMI_VECTOR )
flat_send_IPI_mask ( cpu_online_map , vector ) ;
else
__send_IPI_shortcut ( APIC_DEST_ALLINC , vector , APIC_DEST_LOGICAL ) ;
2005-06-26 01:55:02 +04:00
}
2005-04-17 02:20:36 +04:00
static int flat_apic_id_registered ( void )
{
return physid_isset ( GET_APIC_ID ( apic_read ( APIC_ID ) ) , phys_cpu_present_map ) ;
}
static unsigned int flat_cpu_mask_to_apicid ( cpumask_t cpumask )
{
return cpus_addr ( cpumask ) [ 0 ] & APIC_ALL_CPUS ;
}
static unsigned int phys_pkg_id ( int index_msb )
{
2006-06-26 15:56:04 +04:00
return hard_smp_processor_id ( ) > > index_msb ;
2005-04-17 02:20:36 +04:00
}
struct genapic apic_flat = {
. name = " flat " ,
. int_delivery_mode = dest_LowestPrio ,
. int_dest_mode = ( APIC_DEST_LOGICAL ! = 0 ) ,
. target_cpus = flat_target_cpus ,
2006-10-08 17:47:55 +04:00
. vector_allocation_domain = flat_vector_allocation_domain ,
2005-04-17 02:20:36 +04:00
. apic_id_registered = flat_apic_id_registered ,
. init_apic_ldr = flat_init_apic_ldr ,
. send_IPI_all = flat_send_IPI_all ,
. send_IPI_allbutself = flat_send_IPI_allbutself ,
. send_IPI_mask = flat_send_IPI_mask ,
. cpu_mask_to_apicid = flat_cpu_mask_to_apicid ,
. phys_pkg_id = phys_pkg_id ,
} ;
2005-07-29 08:15:42 +04:00
/*
* Physflat mode is used when there are more than 8 CPUs on a AMD system .
* We cannot use logical delivery in this case because the mask
* overflows , so use physical mode .
*/
static cpumask_t physflat_target_cpus ( void )
{
2006-10-21 20:37:02 +04:00
return cpu_online_map ;
2005-07-29 08:15:42 +04:00
}
2006-10-08 17:47:55 +04:00
static cpumask_t physflat_vector_allocation_domain ( int cpu )
{
cpumask_t domain = CPU_MASK_NONE ;
cpu_set ( cpu , domain ) ;
return domain ;
}
2005-07-29 08:15:42 +04:00
static void physflat_send_IPI_mask ( cpumask_t cpumask , int vector )
{
send_IPI_mask_sequence ( cpumask , vector ) ;
}
static void physflat_send_IPI_allbutself ( int vector )
{
cpumask_t allbutme = cpu_online_map ;
2006-01-12 00:43:09 +03:00
cpu_clear ( smp_processor_id ( ) , allbutme ) ;
2005-07-29 08:15:42 +04:00
physflat_send_IPI_mask ( allbutme , vector ) ;
}
static void physflat_send_IPI_all ( int vector )
{
physflat_send_IPI_mask ( cpu_online_map , vector ) ;
}
static unsigned int physflat_cpu_mask_to_apicid ( cpumask_t cpumask )
{
int cpu ;
/*
* We ' re using fixed IRQ delivery , can only return one phys APIC ID .
* May as well be the first .
*/
cpu = first_cpu ( cpumask ) ;
if ( ( unsigned ) cpu < NR_CPUS )
return x86_cpu_to_apicid [ cpu ] ;
else
return BAD_APICID ;
}
struct genapic apic_physflat = {
. name = " physical flat " ,
2005-09-12 20:49:24 +04:00
. int_delivery_mode = dest_Fixed ,
2005-07-29 08:15:42 +04:00
. int_dest_mode = ( APIC_DEST_PHYSICAL ! = 0 ) ,
. target_cpus = physflat_target_cpus ,
2006-10-08 17:47:55 +04:00
. vector_allocation_domain = physflat_vector_allocation_domain ,
2005-07-29 08:15:42 +04:00
. apic_id_registered = flat_apic_id_registered ,
. init_apic_ldr = flat_init_apic_ldr , /*not needed, but shouldn't hurt*/
. send_IPI_all = physflat_send_IPI_all ,
. send_IPI_allbutself = physflat_send_IPI_allbutself ,
. send_IPI_mask = physflat_send_IPI_mask ,
. cpu_mask_to_apicid = physflat_cpu_mask_to_apicid ,
. phys_pkg_id = phys_pkg_id ,
} ;