Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

#include "ll/i386/apic.h"

int get_maxlvt(void)
{
        unsigned int v, ver, maxlvt;

        v = apic_read(APIC_LVR);
        ver = GET_APIC_VERSION(v);
        /* 82489DXs do not report # of LVT entries. */
        maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2;
        return maxlvt;
}

void clear_local_APIC(void)
{
        int maxlvt;
        unsigned long v;

        maxlvt = get_maxlvt();

        /*
         * Masking an LVT entry on a P6 can trigger a local APIC error
         * if the vector is zero. Mask LVTERR first to prevent this.
         */

        if (maxlvt >= 3) {
                v = ERROR_APIC_VECTOR; /* any non-zero vector will do */
                apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
        }
        /*
         * Careful: we have to set masks only first to deassert
         * any level-triggered sources.
         */

        v = apic_read(APIC_LVTT);
        apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
        v = apic_read(APIC_LVT0);
        apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
        v = apic_read(APIC_LVT1);
        apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);
        if (maxlvt >= 4) {
                v = apic_read(APIC_LVTPC);
                apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
        }

        /*
         * Clean APIC state for other OSs:
         */

        apic_write_around(APIC_LVTT, APIC_LVT_MASKED);
        apic_write_around(APIC_LVT0, APIC_LVT_MASKED);
        apic_write_around(APIC_LVT1, APIC_LVT_MASKED);
        if (maxlvt >= 3)
                apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
        if (maxlvt >= 4)
                apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
        v = GET_APIC_VERSION(apic_read(APIC_LVR));
        if (APIC_INTEGRATED(v)) {       /* !82489DX */
                if (maxlvt > 3)
                        apic_write(APIC_ESR, 0);
                apic_read(APIC_ESR);
        }
}

void __init connect_bsp_APIC(void)
{
        if (pic_mode) {
                /*
                 * Do not trust the local APIC being empty at bootup.
                 */

                clear_local_APIC();
                /*
                 * PIC mode, enable APIC mode in the IMCR, i.e.
                 * connect BSP's local APIC to INT and NMI lines.
                 */

                printk("leaving PIC mode, enabling APIC mode.\n");
                outb(0x70, 0x22);
                outb(0x01, 0x23);
        }
}

void disconnect_bsp_APIC(void)
{
        if (pic_mode) {
                /*
                 * Put the board back into PIC mode (has an effect
                 * only on certain older boards).  Note that APIC
                 * interrupts, including IPIs, won't work beyond
                 * this point!  The only exception are INIT IPIs.
                 */

                printk("disabling APIC mode, entering PIC mode.\n");
                outb(0x70, 0x22);
                outb(0x00, 0x23);
        }
}

void disable_local_APIC(void)
{
        unsigned long value;

        clear_local_APIC();

        /*
         * Disable APIC (implies clearing of registers
         * for 82489DX!).
         */

        value = apic_read(APIC_SPIV);
        value &= ~APIC_SPIV_APIC_ENABLED;
        apic_write_around(APIC_SPIV, value);
}

/*
 * An initial setup of the virtual wire mode.
 */

void __init init_bsp_APIC(void)
{
        unsigned long value, ver;

        /*
         * Don't do the setup now if we have a SMP BIOS as the
         * through-I/O-APIC virtual wire mode might be active.
         */

        if (smp_found_config || !cpu_has_apic)
                return;

        value = apic_read(APIC_LVR);
        ver = GET_APIC_VERSION(value);

        /*
         * Do not trust the local APIC being empty at bootup.
         */

        clear_local_APIC();

        /*
         * Enable APIC.
         */

        value = apic_read(APIC_SPIV);
        value &= ~APIC_VECTOR_MASK;
        value |= APIC_SPIV_APIC_ENABLED;
        value |= APIC_SPIV_FOCUS_DISABLED;
        value |= SPURIOUS_APIC_VECTOR;
        apic_write_around(APIC_SPIV, value);

        /*
         * Set up the virtual wire mode.
         */

        apic_write_around(APIC_LVT0, APIC_DM_EXTINT);
        value = APIC_DM_NMI;
        if (!APIC_INTEGRATED(ver))              /* 82489DX */
                value |= APIC_LVT_LEVEL_TRIGGER;
        apic_write_around(APIC_LVT1, value);
}

static unsigned long calculate_ldr(unsigned long old)
{
        unsigned long id;
        if(clustered_apic_mode == CLUSTERED_APIC_XAPIC)
                id = physical_to_logical_apicid(hard_smp_processor_id());
        else
                id = 1UL << smp_processor_id();
        return (old & ~APIC_LDR_MASK)|SET_APIC_LOGICAL_ID(id);
}

void __init setup_local_APIC (void)
{
        unsigned long value, ver, maxlvt;

        /* Pound the ESR really hard over the head with a big hammer - mbligh */
        if (esr_disable) {
                apic_write(APIC_ESR, 0);
                apic_write(APIC_ESR, 0);
                apic_write(APIC_ESR, 0);
                apic_write(APIC_ESR, 0);
        }

        value = apic_read(APIC_LVR);
        ver = GET_APIC_VERSION(value);

        if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f)
                __error_in_apic_c();

        /*
         * Double-check wether this APIC is really registered.
         * This is meaningless in clustered apic mode, so we skip it.
         */

        if (!clustered_apic_mode &&
            !test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))
                BUG();

        /*
         * 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...
         */

        if (clustered_apic_mode != CLUSTERED_APIC_NUMAQ) {
                /*
                 * For NUMA-Q (clustered apic logical), the firmware does this
                 * for us. Otherwise put the APIC into clustered or flat
                 * delivery mode. Must be "all ones" explicitly for 82489DX.
                 */

                if(clustered_apic_mode == CLUSTERED_APIC_XAPIC)
                        apic_write_around(APIC_DFR, APIC_DFR_CLUSTER);
                else
                        apic_write_around(APIC_DFR, APIC_DFR_FLAT);

                /*
                 * Set up the logical destination ID.
                 */

                value = apic_read(APIC_LDR);
                apic_write_around(APIC_LDR, calculate_ldr(value));
        }

        /*
         * Set Task Priority to 'accept all'. We never change this
         * later on.
         */

        value = apic_read(APIC_TASKPRI);
        value &= ~APIC_TPRI_MASK;
        apic_write_around(APIC_TASKPRI, value);

        /*
         * Now that we are all set up, enable the APIC
         */

        value = apic_read(APIC_SPIV);
        value &= ~APIC_VECTOR_MASK;
        /*
         * Enable APIC
         */

        value |= APIC_SPIV_APIC_ENABLED;

        /*
         * Some unknown Intel IO/APIC (or APIC) errata is biting us with
         * certain networking cards. If high frequency interrupts are
         * happening on a particular IOAPIC pin, plus the IOAPIC routing
         * entry is masked/unmasked at a high rate as well then sooner or
         * later IOAPIC line gets 'stuck', no more interrupts are received
         * from the device. If focus CPU is disabled then the hang goes
         * away, oh well :-(
         *
         * [ This bug can be reproduced easily with a level-triggered
         *   PCI Ne2000 networking cards and PII/PIII processors, dual
         *   BX chipset. ]
         */

        /*
         * Actually disabling the focus CPU check just makes the hang less
         * frequent as it makes the interrupt distributon model be more
         * like LRU than MRU (the short-term load is more even across CPUs).
         * See also the comment in end_level_ioapic_irq().  --macro
         */

#if 1
        /* Enable focus processor (bit==0) */
        value &= ~APIC_SPIV_FOCUS_DISABLED;
#else
        /* Disable focus processor (bit==1) */
        value |= APIC_SPIV_FOCUS_DISABLED;
#endif
        /*
         * Set spurious IRQ vector
         */

        value |= SPURIOUS_APIC_VECTOR;
        apic_write_around(APIC_SPIV, value);

        /*
         * Set up LVT0, LVT1:
         *
         * set up through-local-APIC on the BP's LINT0. This is not
         * strictly necessery in pure symmetric-IO mode, but sometimes
         * we delegate interrupts to the 8259A.
         */

        /*
         * TODO: set up through-local-APIC from through-I/O-APIC? --macro
         */

        value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
        if (!smp_processor_id() && (pic_mode || !value)) {
                value = APIC_DM_EXTINT;
                printk("enabled ExtINT on CPU#%d\n", smp_processor_id());
        } else {
                value = APIC_DM_EXTINT | APIC_LVT_MASKED;
                printk("masked ExtINT on CPU#%d\n", smp_processor_id());
        }
        apic_write_around(APIC_LVT0, value);

        /*
         * only the BP should see the LINT1 NMI signal, obviously.
         */

        if (!smp_processor_id())
                value = APIC_DM_NMI;
        else
                value = APIC_DM_NMI | APIC_LVT_MASKED;
        if (!APIC_INTEGRATED(ver))              /* 82489DX */
                value |= APIC_LVT_LEVEL_TRIGGER;
        apic_write_around(APIC_LVT1, value);

        if (APIC_INTEGRATED(ver) && !esr_disable) {             /* !82489DX */
                maxlvt = get_maxlvt();
                if (maxlvt > 3)         /* Due to the Pentium erratum 3AP. */
                        apic_write(APIC_ESR, 0);
                value = apic_read(APIC_ESR);
                printk("ESR value before enabling vector: %08lx\n", value);

                value = ERROR_APIC_VECTOR;      // enables sending errors
                apic_write_around(APIC_LVTERR, value);
                /*
                 * spec says clear errors after enabling vector.
                 */

                if (maxlvt > 3)
                        apic_write(APIC_ESR, 0);
                value = apic_read(APIC_ESR);
                printk("ESR value after enabling vector: %08lx\n", value);
        } else {
                if (esr_disable)       
                        /*
                         * Something untraceble is creating bad interrupts on
                         * secondary quads ... for the moment, just leave the
                         * ESR disabled - we can't do anything useful with the
                         * errors anyway - mbligh
                         */

                        printk("Leaving ESR disabled.\n");
                else
                        printk("No ESR for 82489DX.\n");
        }

        if (nmi_watchdog == NMI_LOCAL_APIC)
                setup_apic_nmi_watchdog();
}