Path: blob/master/arch/x86/kernel/apic/es7000_32.c
17621 views
/*1* Written by: Garry Forsgren, Unisys Corporation2* Natalie Protasevich, Unisys Corporation3*4* This file contains the code to configure and interface5* with Unisys ES7000 series hardware system manager.6*7* Copyright (c) 2003 Unisys Corporation.8* Copyright (C) 2009, Red Hat, Inc., Ingo Molnar9*10* All Rights Reserved.11*12* This program is free software; you can redistribute it and/or modify it13* under the terms of version 2 of the GNU General Public License as14* published by the Free Software Foundation.15*16* This program is distributed in the hope that it would be useful, but17* WITHOUT ANY WARRANTY; without even the implied warranty of18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.19*20* You should have received a copy of the GNU General Public License along21* with this program; if not, write the Free Software Foundation, Inc., 5922* Temple Place - Suite 330, Boston MA 02111-1307, USA.23*24* Contact information: Unisys Corporation, Township Line & Union Meeting25* Roads-A, Unisys Way, Blue Bell, Pennsylvania, 19424, or:26*27* http://www.unisys.com28*/2930#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt3132#include <linux/notifier.h>33#include <linux/spinlock.h>34#include <linux/cpumask.h>35#include <linux/threads.h>36#include <linux/kernel.h>37#include <linux/module.h>38#include <linux/reboot.h>39#include <linux/string.h>40#include <linux/types.h>41#include <linux/errno.h>42#include <linux/acpi.h>43#include <linux/init.h>44#include <linux/gfp.h>45#include <linux/nmi.h>46#include <linux/smp.h>47#include <linux/io.h>4849#include <asm/apicdef.h>50#include <asm/atomic.h>51#include <asm/fixmap.h>52#include <asm/mpspec.h>53#include <asm/setup.h>54#include <asm/apic.h>55#include <asm/ipi.h>5657/*58* ES7000 chipsets59*/6061#define NON_UNISYS 062#define ES7000_CLASSIC 163#define ES7000_ZORRO 26465#define MIP_REG 166#define MIP_PSAI_REG 46768#define MIP_BUSY 169#define MIP_SPIN 0xf000070#define MIP_VALID 0x0100000000000000ULL71#define MIP_SW_APIC 0x1020b7273#define MIP_PORT(val) ((val >> 32) & 0xffff)7475#define MIP_RD_LO(val) (val & 0xffffffff)7677struct mip_reg {78unsigned long long off_0x00;79unsigned long long off_0x08;80unsigned long long off_0x10;81unsigned long long off_0x18;82unsigned long long off_0x20;83unsigned long long off_0x28;84unsigned long long off_0x30;85unsigned long long off_0x38;86};8788struct mip_reg_info {89unsigned long long mip_info;90unsigned long long delivery_info;91unsigned long long host_reg;92unsigned long long mip_reg;93};9495struct psai {96unsigned long long entry_type;97unsigned long long addr;98unsigned long long bep_addr;99};100101#ifdef CONFIG_ACPI102103struct es7000_oem_table {104struct acpi_table_header Header;105u32 OEMTableAddr;106u32 OEMTableSize;107};108109static unsigned long oem_addrX;110static unsigned long oem_size;111112#endif113114/*115* ES7000 Globals116*/117118static volatile unsigned long *psai;119static struct mip_reg *mip_reg;120static struct mip_reg *host_reg;121static int mip_port;122static unsigned long mip_addr;123static unsigned long host_addr;124125int es7000_plat;126127/*128* GSI override for ES7000 platforms.129*/130131132static int __cpuinit wakeup_secondary_cpu_via_mip(int cpu, unsigned long eip)133{134unsigned long vect = 0, psaival = 0;135136if (psai == NULL)137return -1;138139vect = ((unsigned long)__pa(eip)/0x1000) << 16;140psaival = (0x1000000 | vect | cpu);141142while (*psai & 0x1000000)143;144145*psai = psaival;146147return 0;148}149150static int es7000_apic_is_cluster(void)151{152/* MPENTIUMIII */153if (boot_cpu_data.x86 == 6 &&154(boot_cpu_data.x86_model >= 7 && boot_cpu_data.x86_model <= 11))155return 1;156157return 0;158}159160static void setup_unisys(void)161{162/*163* Determine the generation of the ES7000 currently running.164*165* es7000_plat = 1 if the machine is a 5xx ES7000 box166* es7000_plat = 2 if the machine is a x86_64 ES7000 box167*168*/169if (!(boot_cpu_data.x86 <= 15 && boot_cpu_data.x86_model <= 2))170es7000_plat = ES7000_ZORRO;171else172es7000_plat = ES7000_CLASSIC;173}174175/*176* Parse the OEM Table:177*/178static int parse_unisys_oem(char *oemptr)179{180int i;181int success = 0;182unsigned char type, size;183unsigned long val;184char *tp = NULL;185struct psai *psaip = NULL;186struct mip_reg_info *mi;187struct mip_reg *host, *mip;188189tp = oemptr;190191tp += 8;192193for (i = 0; i <= 6; i++) {194type = *tp++;195size = *tp++;196tp -= 2;197switch (type) {198case MIP_REG:199mi = (struct mip_reg_info *)tp;200val = MIP_RD_LO(mi->host_reg);201host_addr = val;202host = (struct mip_reg *)val;203host_reg = __va(host);204val = MIP_RD_LO(mi->mip_reg);205mip_port = MIP_PORT(mi->mip_info);206mip_addr = val;207mip = (struct mip_reg *)val;208mip_reg = __va(mip);209pr_debug("host_reg = 0x%lx\n",210(unsigned long)host_reg);211pr_debug("mip_reg = 0x%lx\n",212(unsigned long)mip_reg);213success++;214break;215case MIP_PSAI_REG:216psaip = (struct psai *)tp;217if (tp != NULL) {218if (psaip->addr)219psai = __va(psaip->addr);220else221psai = NULL;222success++;223}224break;225default:226break;227}228tp += size;229}230231if (success < 2)232es7000_plat = NON_UNISYS;233else234setup_unisys();235236return es7000_plat;237}238239#ifdef CONFIG_ACPI240static int __init find_unisys_acpi_oem_table(unsigned long *oem_addr)241{242struct acpi_table_header *header = NULL;243struct es7000_oem_table *table;244acpi_size tbl_size;245acpi_status ret;246int i = 0;247248for (;;) {249ret = acpi_get_table_with_size("OEM1", i++, &header, &tbl_size);250if (!ACPI_SUCCESS(ret))251return -1;252253if (!memcmp((char *) &header->oem_id, "UNISYS", 6))254break;255256early_acpi_os_unmap_memory(header, tbl_size);257}258259table = (void *)header;260261oem_addrX = table->OEMTableAddr;262oem_size = table->OEMTableSize;263264early_acpi_os_unmap_memory(header, tbl_size);265266*oem_addr = (unsigned long)__acpi_map_table(oem_addrX, oem_size);267268return 0;269}270271static void __init unmap_unisys_acpi_oem_table(unsigned long oem_addr)272{273if (!oem_addr)274return;275276__acpi_unmap_table((char *)oem_addr, oem_size);277}278279static int es7000_check_dsdt(void)280{281struct acpi_table_header header;282283if (ACPI_SUCCESS(acpi_get_table_header(ACPI_SIG_DSDT, 0, &header)) &&284!strncmp(header.oem_id, "UNISYS", 6))285return 1;286return 0;287}288289static int es7000_acpi_ret;290291/* Hook from generic ACPI tables.c */292static int __init es7000_acpi_madt_oem_check(char *oem_id, char *oem_table_id)293{294unsigned long oem_addr = 0;295int check_dsdt;296int ret = 0;297298/* check dsdt at first to avoid clear fix_map for oem_addr */299check_dsdt = es7000_check_dsdt();300301if (!find_unisys_acpi_oem_table(&oem_addr)) {302if (check_dsdt) {303ret = parse_unisys_oem((char *)oem_addr);304} else {305setup_unisys();306ret = 1;307}308/*309* we need to unmap it310*/311unmap_unisys_acpi_oem_table(oem_addr);312}313314es7000_acpi_ret = ret;315316return ret && !es7000_apic_is_cluster();317}318319static int es7000_acpi_madt_oem_check_cluster(char *oem_id, char *oem_table_id)320{321int ret = es7000_acpi_ret;322323return ret && es7000_apic_is_cluster();324}325326#else /* !CONFIG_ACPI: */327static int es7000_acpi_madt_oem_check(char *oem_id, char *oem_table_id)328{329return 0;330}331332static int es7000_acpi_madt_oem_check_cluster(char *oem_id, char *oem_table_id)333{334return 0;335}336#endif /* !CONFIG_ACPI */337338static void es7000_spin(int n)339{340int i = 0;341342while (i++ < n)343rep_nop();344}345346static int es7000_mip_write(struct mip_reg *mip_reg)347{348int status = 0;349int spin;350351spin = MIP_SPIN;352while ((host_reg->off_0x38 & MIP_VALID) != 0) {353if (--spin <= 0) {354WARN(1, "Timeout waiting for Host Valid Flag\n");355return -1;356}357es7000_spin(MIP_SPIN);358}359360memcpy(host_reg, mip_reg, sizeof(struct mip_reg));361outb(1, mip_port);362363spin = MIP_SPIN;364365while ((mip_reg->off_0x38 & MIP_VALID) == 0) {366if (--spin <= 0) {367WARN(1, "Timeout waiting for MIP Valid Flag\n");368return -1;369}370es7000_spin(MIP_SPIN);371}372373status = (mip_reg->off_0x00 & 0xffff0000000000ULL) >> 48;374mip_reg->off_0x38 &= ~MIP_VALID;375376return status;377}378379static void es7000_enable_apic_mode(void)380{381struct mip_reg es7000_mip_reg;382int mip_status;383384if (!es7000_plat)385return;386387pr_info("Enabling APIC mode.\n");388memset(&es7000_mip_reg, 0, sizeof(struct mip_reg));389es7000_mip_reg.off_0x00 = MIP_SW_APIC;390es7000_mip_reg.off_0x38 = MIP_VALID;391392while ((mip_status = es7000_mip_write(&es7000_mip_reg)) != 0)393WARN(1, "Command failed, status = %x\n", mip_status);394}395396static void es7000_vector_allocation_domain(int cpu, struct cpumask *retmask)397{398/* Careful. Some cpus do not strictly honor the set of cpus399* specified in the interrupt destination when using lowest400* priority interrupt delivery mode.401*402* In particular there was a hyperthreading cpu observed to403* deliver interrupts to the wrong hyperthread when only one404* hyperthread was specified in the interrupt desitination.405*/406cpumask_clear(retmask);407cpumask_bits(retmask)[0] = APIC_ALL_CPUS;408}409410411static void es7000_wait_for_init_deassert(atomic_t *deassert)412{413while (!atomic_read(deassert))414cpu_relax();415}416417static unsigned int es7000_get_apic_id(unsigned long x)418{419return (x >> 24) & 0xFF;420}421422static void es7000_send_IPI_mask(const struct cpumask *mask, int vector)423{424default_send_IPI_mask_sequence_phys(mask, vector);425}426427static void es7000_send_IPI_allbutself(int vector)428{429default_send_IPI_mask_allbutself_phys(cpu_online_mask, vector);430}431432static void es7000_send_IPI_all(int vector)433{434es7000_send_IPI_mask(cpu_online_mask, vector);435}436437static int es7000_apic_id_registered(void)438{439return 1;440}441442static const struct cpumask *target_cpus_cluster(void)443{444return cpu_all_mask;445}446447static const struct cpumask *es7000_target_cpus(void)448{449return cpumask_of(smp_processor_id());450}451452static unsigned long es7000_check_apicid_used(physid_mask_t *map, int apicid)453{454return 0;455}456457static unsigned long es7000_check_apicid_present(int bit)458{459return physid_isset(bit, phys_cpu_present_map);460}461462static int es7000_early_logical_apicid(int cpu)463{464/* on es7000, logical apicid is the same as physical */465return early_per_cpu(x86_bios_cpu_apicid, cpu);466}467468static unsigned long calculate_ldr(int cpu)469{470unsigned long id = per_cpu(x86_bios_cpu_apicid, cpu);471472return SET_APIC_LOGICAL_ID(id);473}474475/*476* Set up the logical destination ID.477*478* Intel recommends to set DFR, LdR and TPR before enabling479* an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel480* document number 292116). So here it goes...481*/482static void es7000_init_apic_ldr_cluster(void)483{484unsigned long val;485int cpu = smp_processor_id();486487apic_write(APIC_DFR, APIC_DFR_CLUSTER);488val = calculate_ldr(cpu);489apic_write(APIC_LDR, val);490}491492static void es7000_init_apic_ldr(void)493{494unsigned long val;495int cpu = smp_processor_id();496497apic_write(APIC_DFR, APIC_DFR_FLAT);498val = calculate_ldr(cpu);499apic_write(APIC_LDR, val);500}501502static void es7000_setup_apic_routing(void)503{504int apic = per_cpu(x86_bios_cpu_apicid, smp_processor_id());505506pr_info("Enabling APIC mode: %s. Using %d I/O APICs, target cpus %lx\n",507(apic_version[apic] == 0x14) ?508"Physical Cluster" : "Logical Cluster",509nr_ioapics, cpumask_bits(es7000_target_cpus())[0]);510}511512static int es7000_cpu_present_to_apicid(int mps_cpu)513{514if (!mps_cpu)515return boot_cpu_physical_apicid;516else if (mps_cpu < nr_cpu_ids)517return per_cpu(x86_bios_cpu_apicid, mps_cpu);518else519return BAD_APICID;520}521522static int cpu_id;523524static void es7000_apicid_to_cpu_present(int phys_apicid, physid_mask_t *retmap)525{526physid_set_mask_of_physid(cpu_id, retmap);527++cpu_id;528}529530static void es7000_ioapic_phys_id_map(physid_mask_t *phys_map, physid_mask_t *retmap)531{532/* For clustered we don't have a good way to do this yet - hack */533physids_promote(0xFFL, retmap);534}535536static int es7000_check_phys_apicid_present(int cpu_physical_apicid)537{538boot_cpu_physical_apicid = read_apic_id();539return 1;540}541542static unsigned int es7000_cpu_mask_to_apicid(const struct cpumask *cpumask)543{544unsigned int round = 0;545int cpu, uninitialized_var(apicid);546547/*548* The cpus in the mask must all be on the apic cluster.549*/550for_each_cpu(cpu, cpumask) {551int new_apicid = early_per_cpu(x86_cpu_to_logical_apicid, cpu);552553if (round && APIC_CLUSTER(apicid) != APIC_CLUSTER(new_apicid)) {554WARN(1, "Not a valid mask!");555556return BAD_APICID;557}558apicid = new_apicid;559round++;560}561return apicid;562}563564static unsigned int565es7000_cpu_mask_to_apicid_and(const struct cpumask *inmask,566const struct cpumask *andmask)567{568int apicid = early_per_cpu(x86_cpu_to_logical_apicid, 0);569cpumask_var_t cpumask;570571if (!alloc_cpumask_var(&cpumask, GFP_ATOMIC))572return apicid;573574cpumask_and(cpumask, inmask, andmask);575cpumask_and(cpumask, cpumask, cpu_online_mask);576apicid = es7000_cpu_mask_to_apicid(cpumask);577578free_cpumask_var(cpumask);579580return apicid;581}582583static int es7000_phys_pkg_id(int cpuid_apic, int index_msb)584{585return cpuid_apic >> index_msb;586}587588static int probe_es7000(void)589{590/* probed later in mptable/ACPI hooks */591return 0;592}593594static int es7000_mps_ret;595static int es7000_mps_oem_check(struct mpc_table *mpc, char *oem,596char *productid)597{598int ret = 0;599600if (mpc->oemptr) {601struct mpc_oemtable *oem_table =602(struct mpc_oemtable *)mpc->oemptr;603604if (!strncmp(oem, "UNISYS", 6))605ret = parse_unisys_oem((char *)oem_table);606}607608es7000_mps_ret = ret;609610return ret && !es7000_apic_is_cluster();611}612613static int es7000_mps_oem_check_cluster(struct mpc_table *mpc, char *oem,614char *productid)615{616int ret = es7000_mps_ret;617618return ret && es7000_apic_is_cluster();619}620621/* We've been warned by a false positive warning.Use __refdata to keep calm. */622static struct apic __refdata apic_es7000_cluster = {623624.name = "es7000",625.probe = probe_es7000,626.acpi_madt_oem_check = es7000_acpi_madt_oem_check_cluster,627.apic_id_registered = es7000_apic_id_registered,628629.irq_delivery_mode = dest_LowestPrio,630/* logical delivery broadcast to all procs: */631.irq_dest_mode = 1,632633.target_cpus = target_cpus_cluster,634.disable_esr = 1,635.dest_logical = 0,636.check_apicid_used = es7000_check_apicid_used,637.check_apicid_present = es7000_check_apicid_present,638639.vector_allocation_domain = es7000_vector_allocation_domain,640.init_apic_ldr = es7000_init_apic_ldr_cluster,641642.ioapic_phys_id_map = es7000_ioapic_phys_id_map,643.setup_apic_routing = es7000_setup_apic_routing,644.multi_timer_check = NULL,645.cpu_present_to_apicid = es7000_cpu_present_to_apicid,646.apicid_to_cpu_present = es7000_apicid_to_cpu_present,647.setup_portio_remap = NULL,648.check_phys_apicid_present = es7000_check_phys_apicid_present,649.enable_apic_mode = es7000_enable_apic_mode,650.phys_pkg_id = es7000_phys_pkg_id,651.mps_oem_check = es7000_mps_oem_check_cluster,652653.get_apic_id = es7000_get_apic_id,654.set_apic_id = NULL,655.apic_id_mask = 0xFF << 24,656657.cpu_mask_to_apicid = es7000_cpu_mask_to_apicid,658.cpu_mask_to_apicid_and = es7000_cpu_mask_to_apicid_and,659660.send_IPI_mask = es7000_send_IPI_mask,661.send_IPI_mask_allbutself = NULL,662.send_IPI_allbutself = es7000_send_IPI_allbutself,663.send_IPI_all = es7000_send_IPI_all,664.send_IPI_self = default_send_IPI_self,665666.wakeup_secondary_cpu = wakeup_secondary_cpu_via_mip,667668.trampoline_phys_low = 0x467,669.trampoline_phys_high = 0x469,670671.wait_for_init_deassert = NULL,672673/* Nothing to do for most platforms, since cleared by the INIT cycle: */674.smp_callin_clear_local_apic = NULL,675.inquire_remote_apic = default_inquire_remote_apic,676677.read = native_apic_mem_read,678.write = native_apic_mem_write,679.icr_read = native_apic_icr_read,680.icr_write = native_apic_icr_write,681.wait_icr_idle = native_apic_wait_icr_idle,682.safe_wait_icr_idle = native_safe_apic_wait_icr_idle,683684.x86_32_early_logical_apicid = es7000_early_logical_apicid,685};686687static struct apic __refdata apic_es7000 = {688689.name = "es7000",690.probe = probe_es7000,691.acpi_madt_oem_check = es7000_acpi_madt_oem_check,692.apic_id_registered = es7000_apic_id_registered,693694.irq_delivery_mode = dest_Fixed,695/* phys delivery to target CPUs: */696.irq_dest_mode = 0,697698.target_cpus = es7000_target_cpus,699.disable_esr = 1,700.dest_logical = 0,701.check_apicid_used = es7000_check_apicid_used,702.check_apicid_present = es7000_check_apicid_present,703704.vector_allocation_domain = es7000_vector_allocation_domain,705.init_apic_ldr = es7000_init_apic_ldr,706707.ioapic_phys_id_map = es7000_ioapic_phys_id_map,708.setup_apic_routing = es7000_setup_apic_routing,709.multi_timer_check = NULL,710.cpu_present_to_apicid = es7000_cpu_present_to_apicid,711.apicid_to_cpu_present = es7000_apicid_to_cpu_present,712.setup_portio_remap = NULL,713.check_phys_apicid_present = es7000_check_phys_apicid_present,714.enable_apic_mode = es7000_enable_apic_mode,715.phys_pkg_id = es7000_phys_pkg_id,716.mps_oem_check = es7000_mps_oem_check,717718.get_apic_id = es7000_get_apic_id,719.set_apic_id = NULL,720.apic_id_mask = 0xFF << 24,721722.cpu_mask_to_apicid = es7000_cpu_mask_to_apicid,723.cpu_mask_to_apicid_and = es7000_cpu_mask_to_apicid_and,724725.send_IPI_mask = es7000_send_IPI_mask,726.send_IPI_mask_allbutself = NULL,727.send_IPI_allbutself = es7000_send_IPI_allbutself,728.send_IPI_all = es7000_send_IPI_all,729.send_IPI_self = default_send_IPI_self,730731.trampoline_phys_low = 0x467,732.trampoline_phys_high = 0x469,733734.wait_for_init_deassert = es7000_wait_for_init_deassert,735736/* Nothing to do for most platforms, since cleared by the INIT cycle: */737.smp_callin_clear_local_apic = NULL,738.inquire_remote_apic = default_inquire_remote_apic,739740.read = native_apic_mem_read,741.write = native_apic_mem_write,742.icr_read = native_apic_icr_read,743.icr_write = native_apic_icr_write,744.wait_icr_idle = native_apic_wait_icr_idle,745.safe_wait_icr_idle = native_safe_apic_wait_icr_idle,746747.x86_32_early_logical_apicid = es7000_early_logical_apicid,748};749750/*751* Need to check for es7000 followed by es7000_cluster, so this order752* in apic_drivers is important.753*/754apic_drivers(apic_es7000, apic_es7000_cluster);755756757