Path: blob/master/arch/x86/platform/visws/visws_quirks.c
10820 views
/*1* SGI Visual Workstation support and quirks, unmaintained.2*3* Split out from setup.c by [email protected]4*5* Copyright (C) 1999 Bent Hagemark, Ingo Molnar6*7* SGI Visual Workstation interrupt controller8*9* The Cobalt system ASIC in the Visual Workstation contains a "Cobalt" APIC10* which serves as the main interrupt controller in the system. Non-legacy11* hardware in the system uses this controller directly. Legacy devices12* are connected to the PIIX4 which in turn has its 8259(s) connected to13* a of the Cobalt APIC entry.14*15* 09/02/2000 - Updated for 2.4 by [email protected]16*17* 25/11/2002 - Updated for 2.5 by Andrey Panin <[email protected]>18*/19#include <linux/interrupt.h>20#include <linux/module.h>21#include <linux/init.h>22#include <linux/smp.h>2324#include <asm/visws/cobalt.h>25#include <asm/visws/piix4.h>26#include <asm/io_apic.h>27#include <asm/fixmap.h>28#include <asm/reboot.h>29#include <asm/setup.h>30#include <asm/apic.h>31#include <asm/e820.h>32#include <asm/time.h>33#include <asm/io.h>3435#include <linux/kernel_stat.h>3637#include <asm/i8259.h>38#include <asm/irq_vectors.h>39#include <asm/visws/lithium.h>4041#include <linux/sched.h>42#include <linux/kernel.h>43#include <linux/pci.h>44#include <linux/pci_ids.h>4546extern int no_broadcast;4748char visws_board_type = -1;49char visws_board_rev = -1;5051static void __init visws_time_init(void)52{53printk(KERN_INFO "Starting Cobalt Timer system clock\n");5455/* Set the countdown value */56co_cpu_write(CO_CPU_TIMEVAL, CO_TIME_HZ/HZ);5758/* Start the timer */59co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) | CO_CTRL_TIMERUN);6061/* Enable (unmask) the timer interrupt */62co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) & ~CO_CTRL_TIMEMASK);6364setup_default_timer_irq();65}6667/* Replaces the default init_ISA_irqs in the generic setup */68static void __init visws_pre_intr_init(void);6970/* Quirk for machine specific memory setup. */7172#define MB (1024 * 1024)7374unsigned long sgivwfb_mem_phys;75unsigned long sgivwfb_mem_size;76EXPORT_SYMBOL(sgivwfb_mem_phys);77EXPORT_SYMBOL(sgivwfb_mem_size);7879long long mem_size __initdata = 0;8081static char * __init visws_memory_setup(void)82{83long long gfx_mem_size = 8 * MB;8485mem_size = boot_params.alt_mem_k;8687if (!mem_size) {88printk(KERN_WARNING "Bootloader didn't set memory size, upgrade it !\n");89mem_size = 128 * MB;90}9192/*93* this hardcodes the graphics memory to 8 MB94* it really should be sized dynamically (or at least95* set as a boot param)96*/97if (!sgivwfb_mem_size) {98printk(KERN_WARNING "Defaulting to 8 MB framebuffer size\n");99sgivwfb_mem_size = 8 * MB;100}101102/*103* Trim to nearest MB104*/105sgivwfb_mem_size &= ~((1 << 20) - 1);106sgivwfb_mem_phys = mem_size - gfx_mem_size;107108e820_add_region(0, LOWMEMSIZE(), E820_RAM);109e820_add_region(HIGH_MEMORY, mem_size - sgivwfb_mem_size - HIGH_MEMORY, E820_RAM);110e820_add_region(sgivwfb_mem_phys, sgivwfb_mem_size, E820_RESERVED);111112return "PROM";113}114115static void visws_machine_emergency_restart(void)116{117/*118* Visual Workstations restart after this119* register is poked on the PIIX4120*/121outb(PIIX4_RESET_VAL, PIIX4_RESET_PORT);122}123124static void visws_machine_power_off(void)125{126unsigned short pm_status;127/* extern unsigned int pci_bus0; */128129while ((pm_status = inw(PMSTS_PORT)) & 0x100)130outw(pm_status, PMSTS_PORT);131132outw(PM_SUSPEND_ENABLE, PMCNTRL_PORT);133134mdelay(10);135136#define PCI_CONF1_ADDRESS(bus, devfn, reg) \137(0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3))138139/* outl(PCI_CONF1_ADDRESS(pci_bus0, SPECIAL_DEV, SPECIAL_REG), 0xCF8); */140outl(PIIX_SPECIAL_STOP, 0xCFC);141}142143static void __init visws_get_smp_config(unsigned int early)144{145}146147/*148* The Visual Workstation is Intel MP compliant in the hardware149* sense, but it doesn't have a BIOS(-configuration table).150* No problem for Linux.151*/152153static void __init MP_processor_info(struct mpc_cpu *m)154{155int ver, logical_apicid;156physid_mask_t apic_cpus;157158if (!(m->cpuflag & CPU_ENABLED))159return;160161logical_apicid = m->apicid;162printk(KERN_INFO "%sCPU #%d %u:%u APIC version %d\n",163m->cpuflag & CPU_BOOTPROCESSOR ? "Bootup " : "",164m->apicid, (m->cpufeature & CPU_FAMILY_MASK) >> 8,165(m->cpufeature & CPU_MODEL_MASK) >> 4, m->apicver);166167if (m->cpuflag & CPU_BOOTPROCESSOR)168boot_cpu_physical_apicid = m->apicid;169170ver = m->apicver;171if ((ver >= 0x14 && m->apicid >= 0xff) || m->apicid >= 0xf) {172printk(KERN_ERR "Processor #%d INVALID. (Max ID: %d).\n",173m->apicid, MAX_LOCAL_APIC);174return;175}176177apic->apicid_to_cpu_present(m->apicid, &apic_cpus);178physids_or(phys_cpu_present_map, phys_cpu_present_map, apic_cpus);179/*180* Validate version181*/182if (ver == 0x0) {183printk(KERN_ERR "BIOS bug, APIC version is 0 for CPU#%d! "184"fixing up to 0x10. (tell your hw vendor)\n",185m->apicid);186ver = 0x10;187}188apic_version[m->apicid] = ver;189}190191static void __init visws_find_smp_config(void)192{193struct mpc_cpu *mp = phys_to_virt(CO_CPU_TAB_PHYS);194unsigned short ncpus = readw(phys_to_virt(CO_CPU_NUM_PHYS));195196if (ncpus > CO_CPU_MAX) {197printk(KERN_WARNING "find_visws_smp: got cpu count of %d at %p\n",198ncpus, mp);199200ncpus = CO_CPU_MAX;201}202203if (ncpus > setup_max_cpus)204ncpus = setup_max_cpus;205206#ifdef CONFIG_X86_LOCAL_APIC207smp_found_config = 1;208#endif209while (ncpus--)210MP_processor_info(mp++);211212mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;213}214215static void visws_trap_init(void);216217void __init visws_early_detect(void)218{219int raw;220221visws_board_type = (char)(inb_p(PIIX_GPI_BD_REG) & PIIX_GPI_BD_REG)222>> PIIX_GPI_BD_SHIFT;223224if (visws_board_type < 0)225return;226227/*228* Override the default platform setup functions229*/230x86_init.resources.memory_setup = visws_memory_setup;231x86_init.mpparse.get_smp_config = visws_get_smp_config;232x86_init.mpparse.find_smp_config = visws_find_smp_config;233x86_init.irqs.pre_vector_init = visws_pre_intr_init;234x86_init.irqs.trap_init = visws_trap_init;235x86_init.timers.timer_init = visws_time_init;236x86_init.pci.init = pci_visws_init;237x86_init.pci.init_irq = x86_init_noop;238239/*240* Install reboot quirks:241*/242pm_power_off = visws_machine_power_off;243machine_ops.emergency_restart = visws_machine_emergency_restart;244245/*246* Do not use broadcast IPIs:247*/248no_broadcast = 0;249250#ifdef CONFIG_X86_IO_APIC251/*252* Turn off IO-APIC detection and initialization:253*/254skip_ioapic_setup = 1;255#endif256257/*258* Get Board rev.259* First, we have to initialize the 307 part to allow us access260* to the GPIO registers. Let's map them at 0x0fc0 which is right261* after the PIIX4 PM section.262*/263outb_p(SIO_DEV_SEL, SIO_INDEX);264outb_p(SIO_GP_DEV, SIO_DATA); /* Talk to GPIO regs. */265266outb_p(SIO_DEV_MSB, SIO_INDEX);267outb_p(SIO_GP_MSB, SIO_DATA); /* MSB of GPIO base address */268269outb_p(SIO_DEV_LSB, SIO_INDEX);270outb_p(SIO_GP_LSB, SIO_DATA); /* LSB of GPIO base address */271272outb_p(SIO_DEV_ENB, SIO_INDEX);273outb_p(1, SIO_DATA); /* Enable GPIO registers. */274275/*276* Now, we have to map the power management section to write277* a bit which enables access to the GPIO registers.278* What lunatic came up with this shit?279*/280outb_p(SIO_DEV_SEL, SIO_INDEX);281outb_p(SIO_PM_DEV, SIO_DATA); /* Talk to GPIO regs. */282283outb_p(SIO_DEV_MSB, SIO_INDEX);284outb_p(SIO_PM_MSB, SIO_DATA); /* MSB of PM base address */285286outb_p(SIO_DEV_LSB, SIO_INDEX);287outb_p(SIO_PM_LSB, SIO_DATA); /* LSB of PM base address */288289outb_p(SIO_DEV_ENB, SIO_INDEX);290outb_p(1, SIO_DATA); /* Enable PM registers. */291292/*293* Now, write the PM register which enables the GPIO registers.294*/295outb_p(SIO_PM_FER2, SIO_PM_INDEX);296outb_p(SIO_PM_GP_EN, SIO_PM_DATA);297298/*299* Now, initialize the GPIO registers.300* We want them all to be inputs which is the301* power on default, so let's leave them alone.302* So, let's just read the board rev!303*/304raw = inb_p(SIO_GP_DATA1);305raw &= 0x7f; /* 7 bits of valid board revision ID. */306307if (visws_board_type == VISWS_320) {308if (raw < 0x6) {309visws_board_rev = 4;310} else if (raw < 0xc) {311visws_board_rev = 5;312} else {313visws_board_rev = 6;314}315} else if (visws_board_type == VISWS_540) {316visws_board_rev = 2;317} else {318visws_board_rev = raw;319}320321printk(KERN_INFO "Silicon Graphics Visual Workstation %s (rev %d) detected\n",322(visws_board_type == VISWS_320 ? "320" :323(visws_board_type == VISWS_540 ? "540" :324"unknown")), visws_board_rev);325}326327#define A01234 (LI_INTA_0 | LI_INTA_1 | LI_INTA_2 | LI_INTA_3 | LI_INTA_4)328#define BCD (LI_INTB | LI_INTC | LI_INTD)329#define ALLDEVS (A01234 | BCD)330331static __init void lithium_init(void)332{333set_fixmap(FIX_LI_PCIA, LI_PCI_A_PHYS);334set_fixmap(FIX_LI_PCIB, LI_PCI_B_PHYS);335336if ((li_pcia_read16(PCI_VENDOR_ID) != PCI_VENDOR_ID_SGI) ||337(li_pcia_read16(PCI_DEVICE_ID) != PCI_DEVICE_ID_SGI_LITHIUM)) {338printk(KERN_EMERG "Lithium hostbridge %c not found\n", 'A');339/* panic("This machine is not SGI Visual Workstation 320/540"); */340}341342if ((li_pcib_read16(PCI_VENDOR_ID) != PCI_VENDOR_ID_SGI) ||343(li_pcib_read16(PCI_DEVICE_ID) != PCI_DEVICE_ID_SGI_LITHIUM)) {344printk(KERN_EMERG "Lithium hostbridge %c not found\n", 'B');345/* panic("This machine is not SGI Visual Workstation 320/540"); */346}347348li_pcia_write16(LI_PCI_INTEN, ALLDEVS);349li_pcib_write16(LI_PCI_INTEN, ALLDEVS);350}351352static __init void cobalt_init(void)353{354/*355* On normal SMP PC this is used only with SMP, but we have to356* use it and set it up here to start the Cobalt clock357*/358set_fixmap(FIX_APIC_BASE, APIC_DEFAULT_PHYS_BASE);359setup_local_APIC();360printk(KERN_INFO "Local APIC Version %#x, ID %#x\n",361(unsigned int)apic_read(APIC_LVR),362(unsigned int)apic_read(APIC_ID));363364set_fixmap(FIX_CO_CPU, CO_CPU_PHYS);365set_fixmap(FIX_CO_APIC, CO_APIC_PHYS);366printk(KERN_INFO "Cobalt Revision %#lx, APIC ID %#lx\n",367co_cpu_read(CO_CPU_REV), co_apic_read(CO_APIC_ID));368369/* Enable Cobalt APIC being careful to NOT change the ID! */370co_apic_write(CO_APIC_ID, co_apic_read(CO_APIC_ID) | CO_APIC_ENABLE);371372printk(KERN_INFO "Cobalt APIC enabled: ID reg %#lx\n",373co_apic_read(CO_APIC_ID));374}375376static void __init visws_trap_init(void)377{378lithium_init();379cobalt_init();380}381382/*383* IRQ controller / APIC support:384*/385386static DEFINE_SPINLOCK(cobalt_lock);387388/*389* Set the given Cobalt APIC Redirection Table entry to point390* to the given IDT vector/index.391*/392static inline void co_apic_set(int entry, int irq)393{394co_apic_write(CO_APIC_LO(entry), CO_APIC_LEVEL | (irq + FIRST_EXTERNAL_VECTOR));395co_apic_write(CO_APIC_HI(entry), 0);396}397398/*399* Cobalt (IO)-APIC functions to handle PCI devices.400*/401static inline int co_apic_ide0_hack(void)402{403extern char visws_board_type;404extern char visws_board_rev;405406if (visws_board_type == VISWS_320 && visws_board_rev == 5)407return 5;408return CO_APIC_IDE0;409}410411static int is_co_apic(unsigned int irq)412{413if (IS_CO_APIC(irq))414return CO_APIC(irq);415416switch (irq) {417case 0: return CO_APIC_CPU;418case CO_IRQ_IDE0: return co_apic_ide0_hack();419case CO_IRQ_IDE1: return CO_APIC_IDE1;420default: return -1;421}422}423424425/*426* This is the SGI Cobalt (IO-)APIC:427*/428static void enable_cobalt_irq(struct irq_data *data)429{430co_apic_set(is_co_apic(data->irq), data->irq);431}432433static void disable_cobalt_irq(struct irq_data *data)434{435int entry = is_co_apic(data->irq);436437co_apic_write(CO_APIC_LO(entry), CO_APIC_MASK);438co_apic_read(CO_APIC_LO(entry));439}440441static void ack_cobalt_irq(struct irq_data *data)442{443unsigned long flags;444445spin_lock_irqsave(&cobalt_lock, flags);446disable_cobalt_irq(data);447apic_write(APIC_EOI, APIC_EIO_ACK);448spin_unlock_irqrestore(&cobalt_lock, flags);449}450451static struct irq_chip cobalt_irq_type = {452.name = "Cobalt-APIC",453.irq_enable = enable_cobalt_irq,454.irq_disable = disable_cobalt_irq,455.irq_ack = ack_cobalt_irq,456};457458459/*460* This is the PIIX4-based 8259 that is wired up indirectly to Cobalt461* -- not the manner expected by the code in i8259.c.462*463* there is a 'master' physical interrupt source that gets sent to464* the CPU. But in the chipset there are various 'virtual' interrupts465* waiting to be handled. We represent this to Linux through a 'master'466* interrupt controller type, and through a special virtual interrupt-467* controller. Device drivers only see the virtual interrupt sources.468*/469static unsigned int startup_piix4_master_irq(struct irq_data *data)470{471legacy_pic->init(0);472enable_cobalt_irq(data);473return 0;474}475476static struct irq_chip piix4_master_irq_type = {477.name = "PIIX4-master",478.irq_startup = startup_piix4_master_irq,479.irq_ack = ack_cobalt_irq,480};481482static void pii4_mask(struct irq_data *data) { }483484static struct irq_chip piix4_virtual_irq_type = {485.name = "PIIX4-virtual",486.irq_mask = pii4_mask,487};488489/*490* PIIX4-8259 master/virtual functions to handle interrupt requests491* from legacy devices: floppy, parallel, serial, rtc.492*493* None of these get Cobalt APIC entries, neither do they have IDT494* entries. These interrupts are purely virtual and distributed from495* the 'master' interrupt source: CO_IRQ_8259.496*497* When the 8259 interrupts its handler figures out which of these498* devices is interrupting and dispatches to its handler.499*500* CAREFUL: devices see the 'virtual' interrupt only. Thus disable/501* enable_irq gets the right irq. This 'master' irq is never directly502* manipulated by any driver.503*/504static irqreturn_t piix4_master_intr(int irq, void *dev_id)505{506unsigned long flags;507int realirq;508509raw_spin_lock_irqsave(&i8259A_lock, flags);510511/* Find out what's interrupting in the PIIX4 master 8259 */512outb(0x0c, 0x20); /* OCW3 Poll command */513realirq = inb(0x20);514515/*516* Bit 7 == 0 means invalid/spurious517*/518if (unlikely(!(realirq & 0x80)))519goto out_unlock;520521realirq &= 7;522523if (unlikely(realirq == 2)) {524outb(0x0c, 0xa0);525realirq = inb(0xa0);526527if (unlikely(!(realirq & 0x80)))528goto out_unlock;529530realirq = (realirq & 7) + 8;531}532533/* mask and ack interrupt */534cached_irq_mask |= 1 << realirq;535if (unlikely(realirq > 7)) {536inb(0xa1);537outb(cached_slave_mask, 0xa1);538outb(0x60 + (realirq & 7), 0xa0);539outb(0x60 + 2, 0x20);540} else {541inb(0x21);542outb(cached_master_mask, 0x21);543outb(0x60 + realirq, 0x20);544}545546raw_spin_unlock_irqrestore(&i8259A_lock, flags);547548/*549* handle this 'virtual interrupt' as a Cobalt one now.550*/551generic_handle_irq(realirq);552553return IRQ_HANDLED;554555out_unlock:556raw_spin_unlock_irqrestore(&i8259A_lock, flags);557return IRQ_NONE;558}559560static struct irqaction master_action = {561.handler = piix4_master_intr,562.name = "PIIX4-8259",563.flags = IRQF_NO_THREAD,564};565566static struct irqaction cascade_action = {567.handler = no_action,568.name = "cascade",569.flags = IRQF_NO_THREAD,570};571572static inline void set_piix4_virtual_irq_type(void)573{574piix4_virtual_irq_type.irq_enable = i8259A_chip.irq_unmask;575piix4_virtual_irq_type.irq_disable = i8259A_chip.irq_mask;576piix4_virtual_irq_type.irq_unmask = i8259A_chip.irq_unmask;577}578579static void __init visws_pre_intr_init(void)580{581int i;582583set_piix4_virtual_irq_type();584585for (i = 0; i < CO_IRQ_APIC0 + CO_APIC_LAST + 1; i++) {586struct irq_chip *chip = NULL;587588if (i == 0)589chip = &cobalt_irq_type;590else if (i == CO_IRQ_IDE0)591chip = &cobalt_irq_type;592else if (i == CO_IRQ_IDE1)593chip = &cobalt_irq_type;594else if (i == CO_IRQ_8259)595chip = &piix4_master_irq_type;596else if (i < CO_IRQ_APIC0)597chip = &piix4_virtual_irq_type;598else if (IS_CO_APIC(i))599chip = &cobalt_irq_type;600601if (chip)602irq_set_chip(i, chip);603}604605setup_irq(CO_IRQ_8259, &master_action);606setup_irq(2, &cascade_action);607}608609610