Newer
Older
/*
* Intel IO-APIC support for multi-Pentium hosts.
*
* Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar, Hajnalka Szabo
*
* Many thanks to Stig Venaas for trying out countless experimental
* patches and reporting/debugging problems patiently!
*
* (c) 1999, Multiple IO-APIC support, developed by
* Ken-ichi Yaku <yaku@css1.kbnes.nec.co.jp> and
* Hidemi Kishimoto <kisimoto@css1.kbnes.nec.co.jp>,
* further tested and cleaned up by Zach Brown <zab@redhat.com>
* and Ingo Molnar <mingo@redhat.com>
*
* Fixes
* Maciej W. Rozycki : Bits for genuine 82489DX APICs;
* thanks to Eric Gilmore
* and Rolf G. Tews
* for testing these extensively
* Paul Diefenbaugh : Added full ACPI support
*/
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/mc146818rtc.h>
#include <linux/compiler.h>
#include <linux/acpi.h>
#include <linux/msi.h>
#include <linux/freezer.h>
#include <linux/jiffies.h> /* time_after() */
#ifdef CONFIG_ACPI
#include <acpi/acpi_bus.h>
#endif
#include <linux/bootmem.h>
#include <linux/dmar.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/desc.h>
#include <asm/proto.h>
#include <asm/acpi.h>
#include <asm/dma.h>
#include <asm/msidef.h>
#include <asm/hypertransport.h>
#include <asm/irq_remapping.h>
#include <mach_apicdef.h>
#define __apicdebuginit(type) static type __init
* Is the SiS APIC rmw bug present ?
* -1 = don't know, 0 = no, 1 = yes
static DEFINE_SPINLOCK(ioapic_lock);
static DEFINE_SPINLOCK(vector_lock);
/*
* # of IRQ routing registers
*/
int nr_ioapic_registers[MAX_IO_APICS];
struct mp_config_ioapic mp_ioapics[MAX_IO_APICS];
struct mp_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
/* # of MP IRQ source entries */
int mp_irq_entries;
#if defined (CONFIG_MCA) || defined (CONFIG_EISA)
int mp_bus_id_to_type[MAX_MP_BUSSES];
#endif
DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
static int __init parse_noapic(char *str)
{
/* disable IO-APIC */
disable_ioapic_setup();
return 0;
}
early_param("noapic", parse_noapic);

Chuck Ebbert
committed
struct irq_pin_list *irq_2_pin;
cpumask_t domain;
cpumask_t old_domain;
unsigned move_cleanup_count;
};
/* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */
static struct irq_cfg irq_cfg_legacy[] __initdata = {
[0] = { .irq = 0, .domain = CPU_MASK_ALL, .vector = IRQ0_VECTOR, },
[1] = { .irq = 1, .domain = CPU_MASK_ALL, .vector = IRQ1_VECTOR, },
[2] = { .irq = 2, .domain = CPU_MASK_ALL, .vector = IRQ2_VECTOR, },
[3] = { .irq = 3, .domain = CPU_MASK_ALL, .vector = IRQ3_VECTOR, },
[4] = { .irq = 4, .domain = CPU_MASK_ALL, .vector = IRQ4_VECTOR, },
[5] = { .irq = 5, .domain = CPU_MASK_ALL, .vector = IRQ5_VECTOR, },
[6] = { .irq = 6, .domain = CPU_MASK_ALL, .vector = IRQ6_VECTOR, },
[7] = { .irq = 7, .domain = CPU_MASK_ALL, .vector = IRQ7_VECTOR, },
[8] = { .irq = 8, .domain = CPU_MASK_ALL, .vector = IRQ8_VECTOR, },
[9] = { .irq = 9, .domain = CPU_MASK_ALL, .vector = IRQ9_VECTOR, },
[10] = { .irq = 10, .domain = CPU_MASK_ALL, .vector = IRQ10_VECTOR, },
[11] = { .irq = 11, .domain = CPU_MASK_ALL, .vector = IRQ11_VECTOR, },
[12] = { .irq = 12, .domain = CPU_MASK_ALL, .vector = IRQ12_VECTOR, },
[13] = { .irq = 13, .domain = CPU_MASK_ALL, .vector = IRQ13_VECTOR, },
[14] = { .irq = 14, .domain = CPU_MASK_ALL, .vector = IRQ14_VECTOR, },
[15] = { .irq = 15, .domain = CPU_MASK_ALL, .vector = IRQ15_VECTOR, },
static struct irq_cfg irq_cfg_init = { .irq = -1U, };
static void init_one_irq_cfg(struct irq_cfg *cfg)
{
memcpy(cfg, &irq_cfg_init, sizeof(struct irq_cfg));
}
static struct irq_cfg *irq_cfgx;
#ifdef CONFIG_HAVE_SPARSE_IRQ
/*
* Protect the irq_cfgx_free freelist:
*/
static DEFINE_SPINLOCK(irq_cfg_lock);
static struct irq_cfg *irq_cfgx_free;
struct dyn_array *da = data;
struct irq_cfg *cfg;
int legacy_count;
int i;
cfg = *da->name;
memcpy(cfg, irq_cfg_legacy, sizeof(irq_cfg_legacy));
legacy_count = ARRAY_SIZE(irq_cfg_legacy);
for (i = legacy_count; i < *da->nr; i++)
init_one_irq_cfg(&cfg[i]);
for (i = 1; i < *da->nr; i++)
cfg[i-1].next = &cfg[i];
irq_cfgx_free = &irq_cfgx[legacy_count];
irq_cfgx[legacy_count - 1].next = NULL;
#endif
}
#ifdef CONFIG_HAVE_SPARSE_IRQ
/* need to be biger than size of irq_cfg_legacy */
static int nr_irq_cfg = 32;
static int __init parse_nr_irq_cfg(char *arg)
{
if (arg) {
nr_irq_cfg = simple_strtoul(arg, NULL, 0);
if (nr_irq_cfg < 32)
nr_irq_cfg = 32;
}
return 0;
early_param("nr_irq_cfg", parse_nr_irq_cfg);
#define for_each_irq_cfg(irqX, cfg) \
for (cfg = irq_cfgx, irqX = cfg->irq; cfg; cfg = cfg->next, irqX = cfg ? cfg->irq : -1U)
DEFINE_DYN_ARRAY(irq_cfgx, sizeof(struct irq_cfg), nr_irq_cfg, PAGE_SIZE, init_work);
static struct irq_cfg *irq_cfg(unsigned int irq)
{
struct irq_cfg *cfg;
cfg = irq_cfgx;
while (cfg) {
if (cfg->irq == irq)
return cfg;
cfg = cfg->next;
}
return NULL;
}
static struct irq_cfg *irq_cfg_alloc(unsigned int irq)
{
struct irq_cfg *cfg, *cfg_pri;
cfg_pri = cfg = irq_cfgx;
while (cfg) {
if (cfg->irq == irq)
return cfg;
cfg_pri = cfg;
cfg = cfg->next;
count++;
}
spin_lock_irqsave(&irq_cfg_lock, flags);
if (!irq_cfgx_free) {
unsigned long phys;
unsigned long total_bytes;
/*
* we run out of pre-allocate ones, allocate more
*/
printk(KERN_DEBUG "try to get more irq_cfg %d\n", nr_irq_cfg);
total_bytes = sizeof(struct irq_cfg) * nr_irq_cfg;
if (after_bootmem)
cfg = kzalloc(total_bytes, GFP_ATOMIC);
else
cfg = __alloc_bootmem_nopanic(total_bytes, PAGE_SIZE, 0);
if (!cfg)
panic("please boot with nr_irq_cfg= %d\n", count * 2);
phys = __pa(cfg);
printk(KERN_DEBUG "irq_irq ==> [%#lx - %#lx]\n", phys, phys + total_bytes);
for (i = 0; i < nr_irq_cfg; i++)
init_one_irq_cfg(&cfg[i]);
for (i = 1; i < nr_irq_cfg; i++)
cfg[i-1].next = &cfg[i];
irq_cfgx_free = cfg;
}
cfg = irq_cfgx_free;
irq_cfgx_free = irq_cfgx_free->next;
cfg->next = NULL;
if (cfg_pri)
cfg_pri->next = cfg;
else
irq_cfgx = cfg;
cfg->irq = irq;
spin_unlock_irqrestore(&irq_cfg_lock, flags);
printk(KERN_DEBUG "found new irq_cfg for irq %d\n", cfg->irq);
#ifdef CONFIG_HAVE_SPARSE_IRQ_DEBUG
{
/* dump the results */
struct irq_cfg *cfg;
unsigned long phys;
unsigned long bytes = sizeof(struct irq_cfg);
printk(KERN_DEBUG "=========================== %d\n", irq);
printk(KERN_DEBUG "irq_cfg dump after get that for %d\n", irq);
for_each_irq_cfg(cfg) {
phys = __pa(cfg);
printk(KERN_DEBUG "irq_cfg %d ==> [%#lx - %#lx]\n", cfg->irq, phys, phys + bytes);
}
printk(KERN_DEBUG "===========================\n");
}
#endif
return cfg;
#else
#define for_each_irq_cfg(irq, cfg) \
for (irq = 0, cfg = &irq_cfgx[irq]; irq < nr_irqs; irq++, cfg = &irq_cfgx[irq])
DEFINE_DYN_ARRAY(irq_cfgx, sizeof(struct irq_cfg), nr_irqs, PAGE_SIZE, init_work);
struct irq_cfg *irq_cfg(unsigned int irq)
{
if (irq < nr_irqs)
return &irq_cfgx[irq];
return NULL;
}
struct irq_cfg *irq_cfg_alloc(unsigned int irq)
{
return irq_cfg(irq);
}
#endif
/*
* This is performance-critical, we want to do it O(1)
*
* the indexing order of this array favors 1:1 mappings
* between pins and IRQs.
*/
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
struct irq_pin_list {
int apic, pin;
struct irq_pin_list *next;
};
static struct irq_pin_list *irq_2_pin_head;
/* fill one page ? */
static int nr_irq_2_pin = 0x100;
static struct irq_pin_list *irq_2_pin_ptr;
static void __init irq_2_pin_init_work(void *data)
{
struct dyn_array *da = data;
struct irq_pin_list *pin;
int i;
pin = *da->name;
for (i = 1; i < *da->nr; i++)
pin[i-1].next = &pin[i];
irq_2_pin_ptr = &pin[0];
}
DEFINE_DYN_ARRAY(irq_2_pin_head, sizeof(struct irq_pin_list), nr_irq_2_pin, PAGE_SIZE, irq_2_pin_init_work);
static struct irq_pin_list *get_one_free_irq_2_pin(void)
{
struct irq_pin_list *pin;
int i;
pin = irq_2_pin_ptr;
if (pin) {
irq_2_pin_ptr = pin->next;
pin->next = NULL;
return pin;
}
/*
* we run out of pre-allocate ones, allocate more
*/
printk(KERN_DEBUG "try to get more irq_2_pin %d\n", nr_irq_2_pin);
if (after_bootmem)
pin = kzalloc(sizeof(struct irq_pin_list)*nr_irq_2_pin,
GFP_ATOMIC);
else
pin = __alloc_bootmem_nopanic(sizeof(struct irq_pin_list) *
nr_irq_2_pin, PAGE_SIZE, 0);
if (!pin)
panic("can not get more irq_2_pin\n");
for (i = 1; i < nr_irq_2_pin; i++)
pin[i-1].next = &pin[i];
irq_2_pin_ptr = pin->next;
pin->next = NULL;
return pin;
}
struct io_apic {
unsigned int index;
unsigned int unused[3];
unsigned int data;
};
static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx)
{
return (void __iomem *) __fix_to_virt(FIX_IO_APIC_BASE_0 + idx)
+ (mp_ioapics[idx].mp_apicaddr & ~PAGE_MASK);
}
static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg)
{
struct io_apic __iomem *io_apic = io_apic_base(apic);
writel(reg, &io_apic->index);
return readl(&io_apic->data);
}
static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value)
{
struct io_apic __iomem *io_apic = io_apic_base(apic);
writel(reg, &io_apic->index);
writel(value, &io_apic->data);
}
/*
* Re-write a value: to be used for read-modify-write
* cycles where the read already set up the index register.
*
* Older SiS APIC requires we rewrite the index register
*/
static inline void io_apic_modify(unsigned int apic, unsigned int reg, unsigned int value)
{
struct io_apic __iomem *io_apic = io_apic_base(apic);
if (sis_apic_bug)
writel(reg, &io_apic->index);
writel(value, &io_apic->data);
}
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
static bool io_apic_level_ack_pending(unsigned int irq)
{
struct irq_pin_list *entry;
unsigned long flags;
struct irq_cfg *cfg = irq_cfg(irq);
spin_lock_irqsave(&ioapic_lock, flags);
entry = cfg->irq_2_pin;
for (;;) {
unsigned int reg;
int pin;
if (!entry)
break;
pin = entry->pin;
reg = io_apic_read(entry->apic, 0x10 + pin*2);
/* Is the remote IRR bit set? */
if (reg & IO_APIC_REDIR_REMOTE_IRR) {
spin_unlock_irqrestore(&ioapic_lock, flags);
return true;
}
if (!entry->next)
break;
entry = entry->next;
}
spin_unlock_irqrestore(&ioapic_lock, flags);
return false;
}
union entry_union {
struct { u32 w1, w2; };
struct IO_APIC_route_entry entry;
};
static struct IO_APIC_route_entry ioapic_read_entry(int apic, int pin)
{
union entry_union eu;
unsigned long flags;
spin_lock_irqsave(&ioapic_lock, flags);
eu.w1 = io_apic_read(apic, 0x10 + 2 * pin);
eu.w2 = io_apic_read(apic, 0x11 + 2 * pin);
spin_unlock_irqrestore(&ioapic_lock, flags);
return eu.entry;
}
/*
* When we write a new IO APIC routing entry, we need to write the high
* word first! If the mask bit in the low word is clear, we will enable
* the interrupt, and we need to make sure the entry is fully populated
* before that happens.
*/
static void
__ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e)
{
union entry_union eu;
eu.entry = e;
io_apic_write(apic, 0x11 + 2*pin, eu.w2);
io_apic_write(apic, 0x10 + 2*pin, eu.w1);
}
static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e)
{
unsigned long flags;
spin_lock_irqsave(&ioapic_lock, flags);
__ioapic_write_entry(apic, pin, e);
spin_unlock_irqrestore(&ioapic_lock, flags);
}
/*
* When we mask an IO APIC routing entry, we need to write the low
* word first, in order to set the mask bit before we change the
* high bits!
*/
static void ioapic_mask_entry(int apic, int pin)
{
unsigned long flags;
union entry_union eu = { .entry.mask = 1 };
spin_lock_irqsave(&ioapic_lock, flags);
io_apic_write(apic, 0x10 + 2*pin, eu.w1);
io_apic_write(apic, 0x11 + 2*pin, eu.w2);
spin_unlock_irqrestore(&ioapic_lock, flags);
}
#ifdef CONFIG_SMP
static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector)
{
int apic, pin;
struct irq_cfg *cfg;
struct irq_pin_list *entry;
cfg = irq_cfg(irq);
entry = cfg->irq_2_pin;
for (;;) {
unsigned int reg;
if (!entry)
break;
apic = entry->apic;
pin = entry->pin;
#ifdef CONFIG_INTR_REMAP
/*
* With interrupt-remapping, destination information comes
* from interrupt-remapping table entry.
*/
if (!irq_remapped(irq))
io_apic_write(apic, 0x11 + pin*2, dest);
#else
io_apic_write(apic, 0x11 + pin*2, dest);
reg = io_apic_read(apic, 0x10 + pin*2);
reg &= ~IO_APIC_REDIR_VECTOR_MASK;
reg |= vector;
io_apic_modify(apic, 0x10 + pin*2, reg);
if (!entry->next)
break;
entry = entry->next;
}
}
static int assign_irq_vector(int irq, cpumask_t mask);
static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
{
struct irq_cfg *cfg;
unsigned long flags;
unsigned int dest;
cpumask_t tmp;
cpus_and(tmp, mask, cpu_online_map);
if (cpus_empty(tmp))
return;
if (assign_irq_vector(irq, mask))
return;
cpus_and(tmp, cfg->domain, mask);
dest = cpu_mask_to_apicid(tmp);
/*
* Only the high 8 bits are valid.
*/
dest = SET_APIC_LOGICAL_ID(dest);
spin_lock_irqsave(&ioapic_lock, flags);
__target_IO_APIC_irq(irq, dest, cfg->vector);
spin_unlock_irqrestore(&ioapic_lock, flags);
}
#endif /* CONFIG_SMP */
/*
* The common case is 1:1 IRQ<->pin mappings. Sometimes there are
* shared ISA-space IRQs, so we have to support them. We are super
* fast in the common case, and fast for shared ISA-space IRQs.
*/
static void add_pin_to_irq(unsigned int irq, int apic, int pin)
{
struct irq_cfg *cfg;
struct irq_pin_list *entry;
/* first time to refer irq_cfg, so with new */
cfg = irq_cfg_alloc(irq);
entry = cfg->irq_2_pin;
if (!entry) {
entry = get_one_free_irq_2_pin();
cfg->irq_2_pin = entry;
entry->apic = apic;
entry->pin = pin;
printk(KERN_DEBUG " 0 add_pin_to_irq: irq %d --> apic %d pin %d\n", irq, apic, pin);
return;
}
while (entry->next) {
/* not again, please */
if (entry->apic == apic && entry->pin == pin)
return;
entry->next = get_one_free_irq_2_pin();
entry = entry->next;
printk(KERN_DEBUG " x add_pin_to_irq: irq %d --> apic %d pin %d\n", irq, apic, pin);
}
/*
* Reroute an IRQ to a different pin.
*/
static void __init replace_pin_at_irq(unsigned int irq,
int oldapic, int oldpin,
int newapic, int newpin)
{
struct irq_cfg *cfg = irq_cfg(irq);
struct irq_pin_list *entry = cfg->irq_2_pin;
int replaced = 0;
if (entry->apic == oldapic && entry->pin == oldpin) {
entry->apic = newapic;
entry->pin = newpin;
replaced = 1;
/* every one is different, right? */
/* why? call replace before add? */
if (!replaced)
add_pin_to_irq(irq, newapic, newpin);
#define __DO_ACTION(R, ACTION_ENABLE, ACTION_DISABLE, FINAL) \
\
{ \
int pin; \
struct irq_cfg *cfg; \
struct irq_pin_list *entry; \
\
cfg = irq_cfg(irq); \
entry = cfg->irq_2_pin; \
for (;;) { \
unsigned int reg; \
if (!entry) \
break; \
pin = entry->pin; \
reg = io_apic_read(entry->apic, 0x10 + R + pin*2); \
io_apic_modify(entry->apic, 0x10 + R + pin*2, reg); \
FINAL; \
if (!entry->next) \
break; \
entry = entry->next; \
} \
}
#define DO_ACTION(name,R, ACTION_ENABLE, ACTION_DISABLE, FINAL) \
\
static void name##_IO_APIC_irq (unsigned int irq) \
__DO_ACTION(R, ACTION_ENABLE, ACTION_DISABLE, FINAL)
DO_ACTION(__unmask, 0, |= 0, &= ~IO_APIC_REDIR_MASKED, )
#ifdef CONFIG_X86_64
/*
* Synchronize the IO-APIC and the CPU by doing
* a dummy read from the IO-APIC
*/
static inline void io_apic_sync(unsigned int apic)
struct io_apic __iomem *io_apic = io_apic_base(apic);
readl(&io_apic->data);
DO_ACTION(__mask, 0, |= IO_APIC_REDIR_MASKED, &= ~0, io_apic_sync(entry->apic))
#else
/* mask = 1 */
DO_ACTION(__mask, 0, |= IO_APIC_REDIR_MASKED, &= ~0, )
DO_ACTION(__mask_and_edge, 0, |= IO_APIC_REDIR_MASKED, &= ~IO_APIC_REDIR_LEVEL_TRIGGER, )
DO_ACTION(__unmask_and_level, 0, |= IO_APIC_REDIR_LEVEL_TRIGGER, &= ~IO_APIC_REDIR_MASKED, )
static void mask_IO_APIC_irq (unsigned int irq)
{
unsigned long flags;
spin_lock_irqsave(&ioapic_lock, flags);
__mask_IO_APIC_irq(irq);
spin_unlock_irqrestore(&ioapic_lock, flags);
}
static void unmask_IO_APIC_irq (unsigned int irq)
{
unsigned long flags;
spin_lock_irqsave(&ioapic_lock, flags);
__unmask_IO_APIC_irq(irq);
spin_unlock_irqrestore(&ioapic_lock, flags);
}
static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
{
struct IO_APIC_route_entry entry;
/* Check delivery_mode to be sure we're not clearing an SMI pin */
entry = ioapic_read_entry(apic, pin);
if (entry.delivery_mode == dest_SMI)
return;
/*
* Disable it in the IO-APIC irq-routing table:
*/
ioapic_mask_entry(apic, pin);
static void clear_IO_APIC (void)
{
int apic, pin;
for (apic = 0; apic < nr_ioapics; apic++)
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
clear_IO_APIC_pin(apic, pin);
}
#if !defined(CONFIG_SMP) && defined(CONFIG_X86_32)
void send_IPI_self(int vector)
{
unsigned int cfg;
/*
* Wait for idle.
*/
apic_wait_icr_idle();
cfg = APIC_DM_FIXED | APIC_DEST_SELF | vector | APIC_DEST_LOGICAL;
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
apic_write(APIC_ICR, cfg);
#endif /* !CONFIG_SMP && CONFIG_X86_32*/
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
/*
* support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to
* specific CPU-side IRQs.
*/
#define MAX_PIRQS 8
static int pirq_entries [MAX_PIRQS];
static int pirqs_enabled;
static int __init ioapic_pirq_setup(char *str)
{
int i, max;
int ints[MAX_PIRQS+1];
get_options(str, ARRAY_SIZE(ints), ints);
for (i = 0; i < MAX_PIRQS; i++)
pirq_entries[i] = -1;
pirqs_enabled = 1;
apic_printk(APIC_VERBOSE, KERN_INFO
"PIRQ redirection, working around broken MP-BIOS.\n");
max = MAX_PIRQS;
if (ints[0] < MAX_PIRQS)
max = ints[0];
for (i = 0; i < max; i++) {
apic_printk(APIC_VERBOSE, KERN_DEBUG
"... PIRQ%d -> IRQ %d\n", i, ints[i+1]);
/*
* PIRQs are mapped upside down, usually.
*/
pirq_entries[MAX_PIRQS-i-1] = ints[i+1];
}
return 1;
}
__setup("pirq=", ioapic_pirq_setup);
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
#endif /* CONFIG_X86_32 */
#ifdef CONFIG_INTR_REMAP
/* I/O APIC RTE contents at the OS boot up */
static struct IO_APIC_route_entry *early_ioapic_entries[MAX_IO_APICS];
/*
* Saves and masks all the unmasked IO-APIC RTE's
*/
int save_mask_IO_APIC_setup(void)
{
union IO_APIC_reg_01 reg_01;
unsigned long flags;
int apic, pin;
/*
* The number of IO-APIC IRQ registers (== #pins):
*/
for (apic = 0; apic < nr_ioapics; apic++) {
spin_lock_irqsave(&ioapic_lock, flags);
reg_01.raw = io_apic_read(apic, 1);
spin_unlock_irqrestore(&ioapic_lock, flags);
nr_ioapic_registers[apic] = reg_01.bits.entries+1;
}
for (apic = 0; apic < nr_ioapics; apic++) {
early_ioapic_entries[apic] =
kzalloc(sizeof(struct IO_APIC_route_entry) *
nr_ioapic_registers[apic], GFP_KERNEL);
if (!early_ioapic_entries[apic])
return -ENOMEM;
}
for (apic = 0; apic < nr_ioapics; apic++)
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
struct IO_APIC_route_entry entry;
entry = early_ioapic_entries[apic][pin] =
ioapic_read_entry(apic, pin);
if (!entry.mask) {
entry.mask = 1;
ioapic_write_entry(apic, pin, entry);
}
}
return 0;
}
void restore_IO_APIC_setup(void)
{
int apic, pin;
for (apic = 0; apic < nr_ioapics; apic++)
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
ioapic_write_entry(apic, pin,
early_ioapic_entries[apic][pin]);
}
void reinit_intr_remapped_IO_APIC(int intr_remapping)
{
/*
* for now plain restore of previous settings.
* TBD: In the case of OS enabling interrupt-remapping,
* IO-APIC RTE's need to be setup to point to interrupt-remapping
* table entries. for now, do a plain restore, and wait for
* the setup_IO_APIC_irqs() to do proper initialization.
*/
restore_IO_APIC_setup();
}
#endif
/*
* Find the IRQ entry number of a certain pin.
*/
static int find_irq_entry(int apic, int pin, int type)
{
int i;
for (i = 0; i < mp_irq_entries; i++)
if (mp_irqs[i].mp_irqtype == type &&
(mp_irqs[i].mp_dstapic == mp_ioapics[apic].mp_apicid ||
mp_irqs[i].mp_dstapic == MP_APIC_ALL) &&
mp_irqs[i].mp_dstirq == pin)
return i;
return -1;
}
/*
* Find the pin to which IRQ[irq] (ISA) is connected
*/
static int __init find_isa_irq_pin(int irq, int type)
{
int i;
for (i = 0; i < mp_irq_entries; i++) {
int lbus = mp_irqs[i].mp_srcbus;
(mp_irqs[i].mp_irqtype == type) &&
(mp_irqs[i].mp_srcbusirq == irq))
return mp_irqs[i].mp_dstirq;
static int __init find_isa_irq_apic(int irq, int type)
{
int i;
for (i = 0; i < mp_irq_entries; i++) {
int lbus = mp_irqs[i].mp_srcbus;
(mp_irqs[i].mp_irqtype == type) &&
(mp_irqs[i].mp_srcbusirq == irq))
break;
}
if (i < mp_irq_entries) {
int apic;
for(apic = 0; apic < nr_ioapics; apic++) {
if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic)
return apic;
}
}
return -1;
}
/*
* Find a specific PCI IRQ entry.
* Not an __init, possibly needed by modules
*/
static int pin_2_irq(int idx, int apic, int pin);
int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
{
int apic, i, best_guess = -1;
apic_printk(APIC_DEBUG, "querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n",
bus, slot, pin);
if (test_bit(bus, mp_bus_not_pci)) {
apic_printk(APIC_VERBOSE, "PCI BIOS passed nonexistent PCI bus %d!\n", bus);
return -1;
}
for (i = 0; i < mp_irq_entries; i++) {
int lbus = mp_irqs[i].mp_srcbus;
if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic ||
mp_irqs[i].mp_dstapic == MP_APIC_ALL)
!mp_irqs[i].mp_irqtype &&
(slot == ((mp_irqs[i].mp_srcbusirq >> 2) & 0x1f))) {
int irq = pin_2_irq(i,apic,mp_irqs[i].mp_dstirq);
if (!(apic || IO_APIC_IRQ(irq)))
continue;
if (pin == (mp_irqs[i].mp_srcbusirq & 3))
return irq;
/*
* Use the first all-but-pin matching entry as a
* best-guess fuzzy result for broken mptables.
*/
if (best_guess < 0)
best_guess = irq;
}
}
return best_guess;
}
EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector);
#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
/*
* EISA Edge/Level control register, ELCR
*/
static int EISA_ELCR(unsigned int irq)
{
if (irq < 16) {
unsigned int port = 0x4d0 + (irq >> 3);
return (inb(port) >> (irq & 7)) & 1;
}
apic_printk(APIC_VERBOSE, KERN_INFO
"Broken MPtable reports ISA irq %d\n", irq);
return 0;
}
/* ISA interrupts are always polarity zero edge triggered,
* when listed as conforming in the MP table. */
#define default_ISA_trigger(idx) (0)
#define default_ISA_polarity(idx) (0)