/*
 * Copyright 1991-1998 by Open Software Foundation, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */
/*
 * MkLinux
 */

/*
 * Values from http://einstein.et.tudelft.nl/~offerman/chiplist.html
 * (dated 18 Oct 1995)
 */

#include <kern/misc_protos.h>
#include <i386/cpuid.h>

/*
 * Generic product array (before CPUID)
 */
unsigned int cpuid_i386_freq[] = { 12, 16, 20, 25, 33, 0 };
unsigned int cpuid_i486_freq[] = { 20, 25, 33, 50, 0 };

struct cpuid_product cpuid_generic[] = {
    {
	0,		CPUID_FAMILY_386,	0,
	80,	cpuid_i386_freq,		"i386"
    },
    {
	0,		CPUID_FAMILY_486,	0,
	240,	cpuid_i486_freq,		"i486"
    },
};

/*
 * INTEL product array
 */
unsigned int cpuid_i486_dx_freq[] = { 20, 25, 33, 0 };
unsigned int cpuid_i486_dx_s_freq[] = { 50, 0 };
unsigned int cpuid_i486_sx_freq[] = { 16, 20, 25, 33, 0 };
unsigned int cpuid_i486_dx2_freq[] = { 32, 40, 50, 66, 0 };
unsigned int cpuid_i486_sl_freq[] = { 25, 33, 0 };
unsigned int cpuid_i486_sx2_freq[] = { 50, 0 };
unsigned int cpuid_i486_dx2wb_freq[] = { 50, 66, 0 };
unsigned int cpuid_i486_dx4_freq[] = { 90, 100, 0 };

unsigned int cpuid_i486_dx2wb_od_freq[] = { 32, 40, 50, 66, 0 };
unsigned int cpuid_i486_dx4_od_freq[] = { 75, 99, 0 };

unsigned int cpuid_p5_freq[] = { 60, 66, 0 };
unsigned int cpuid_p54_freq[] = { 60, 66, 75, 90, 100, 120, 133, 166, 200, 0 };

unsigned int cpuid_p24t_freq[] = { 25, 33, 0 };
unsigned int cpuid_p24ct_freq[] = { 63, 83, 0 };

struct cpuid_product cpuid_intel[] = {
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_DX,
	240,	cpuid_i486_dx_freq,		"Intel 486DX"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_DX_S,
	240,	cpuid_i486_dx_s_freq,		"Intel 486DX-S"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_SX,
	240,	cpuid_i486_sx_freq,		"Intel 486SX"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_DX2,
	240,	cpuid_i486_dx2_freq,		"Intel 486DX2"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_SL,
	240,	cpuid_i486_sl_freq,		"Intel 486SL"
    },
    {  
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_SX2,
	240,	cpuid_i486_sx2_freq,		"Intel 486SX2"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_DX2WB,
	240,	cpuid_i486_dx2wb_freq,		"Intel 486DX2WB"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_486,	CPUID_MODEL_I486_DX4,
	240,	cpuid_i486_dx4_freq,		"Intel 486DX4"
    },
    {
	CPUID_TYPE_OVERDRIVE,	CPUID_FAMILY_486,	CPUID_MODEL_I486_DX2,
	240,	cpuid_i486_dx2_freq,		"Intel 486DX2 OverDrive"
    },
    {
	CPUID_TYPE_OVERDRIVE,	CPUID_FAMILY_486,	CPUID_MODEL_I486_DX2WB,
	240,	cpuid_i486_dx2wb_od_freq,	"Intel 486DX2WB OverDrive"
    },
    {
	CPUID_TYPE_OVERDRIVE,	CPUID_FAMILY_486,	CPUID_MODEL_I486_DX4,
	240,	cpuid_i486_dx4_od_freq,		"Intel 486DX4 OverDrive"
    },
    {
	CPUID_TYPE_OVERDRIVE,	CPUID_FAMILY_P5,	CPUID_MODEL_P24T,
	208,	cpuid_p24t_freq,		"Intel Pentium P24T OverDrive"
    },
    {
	CPUID_TYPE_OVERDRIVE,	CPUID_FAMILY_P5,	CPUID_MODEL_P54,
	207,	cpuid_p24ct_freq,		"Intel Pentium P24CT OverDrive"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_P5,	CPUID_MODEL_P5A,
	207,	cpuid_p5_freq,			"Intel Pentium P5 rev A"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_P5,	CPUID_MODEL_P5,
	207,	cpuid_p5_freq,			"Intel Pentium P5"
    },
    {
	CPUID_TYPE_OEM,		CPUID_FAMILY_P5,	CPUID_MODEL_P54,
	207,	cpuid_p54_freq,			"Intel Pentium P54"
    }
};
unsigned int cpuid_intel_size = sizeof (cpuid_intel) / sizeof (cpuid_intel[0]);

/*
 * AMD product arrays
 */
unsigned int cpuid_am486_dx_freq[] = { 33, 40, 0 };
unsigned int cpuid_am486_dx2_freq[] = { 50, 66, 80, 99, 0 };
unsigned int cpuid_am486_dx4_freq[] = { 99, 120, 133, 0 };
unsigned int cpuid_am486_dx4wb_freq[] = { 99, 120, 133, 0 };

/*
 * UMC product array 
 */
unsigned int cpuid_u5sd_freq[] = { 25, 33, 40, 0 };
unsigned int cpuid_u5s_freq[] = { 25, 33, 40, 0 };

/*
 * Vendor ID array
 */
struct cpuid_name cpuid_name[] = {
    {	CPUID_VID_INTEL,
	cpuid_intel,	sizeof (cpuid_intel) / sizeof (cpuid_intel[0])
    },
    {	CPUID_VID_UMC,
	(struct cpuid_product *)0,
    },
    {	CPUID_VID_AMD,
	(struct cpuid_product *)0,
    },
    {	CPUID_VID_CYRIX,
	(struct cpuid_product *)0,
    },
    {	CPUID_VID_NEXTGEN,
	(struct cpuid_product *)0
    },
    {	"",
	cpuid_generic,	sizeof (cpuid_generic) / sizeof (cpuid_generic[0])
    },
    {	(char *)0,
    }
};

/*
 * Feature Flag values
 */
char *cpuid_flag[] = {
    "FPU",	/* Floating point unit on-chip */
    "VME",	/* Virtual Mode Extension */
    "DE",	/* Debugging Extension */
    "PSE",	/* Page Size Extension */
    "TSC",	/* Time Stamp Counter */
    "MSR",	/* Model Specific Registers */
    "PAE",	/* Physical Address Extension */
    "MCE",	/* Machine Check Exception */
    "CX8",	/* CMPXCHG8 Instruction sSupported */
    "APIC",	/* Local APIC Supported */
    "(bit 10)",
    "(bit 11)",
    "MTRR",	/* Machine Type Range Register */
    "PGE",	/* Page Global Enable */
    "MCA",	/* Machine Check Architecture */
    "CMOV",	/* Conditional Move Instruction Supported */
    "(bit 16)",
    "(bit 17)",
    "(bit 18)",
    "(bit 19)",
    "(bit 20)",
    "(bit 21)",
    "(bit 22)",
    "(bit 23)",
    "(bit 24)",
    "(bit 25)",
    "(bit 26)",
    "(bit 27)",
    "(bit 28)",
    "(bit 29)",
    "(bit 30)",
    "(bit 31)",
};

/*
 * Cache description array
 */
struct cpuid_cache_desc cpuid_cache_desc[] = {
    {	CPUID_CACHE_ITLB_4K,
	"Instruction TBL, 4K, pages 4-way set associative, 64 entries"
    },
    {	CPUID_CACHE_ITLB_4M,
	"Instruction TBL, 4M, pages 4-way set associative, 4 entries"
    },
    {	CPUID_CACHE_DTLB_4K,
	"Data TBL, 4K pages, 4-way set associative, 64 entries"
    },
    {	CPUID_CACHE_DTLB_4M,
	"Data TBL, 4M pages, 4-way set associative, 4 entries"
    },
    {	CPUID_CACHE_ICACHE_8K,
	"Instruction L1 cache, 8K, 4-way set associative, 32byte line size"
    },
    {	CPUID_CACHE_DCACHE_8K,
	"Data L1 cache, 8K, 2-way set associative, 32byte line size"
    },
    {	CPUID_CACHE_UCACHE_128K,
	"Unified L2 cache, 128K, 4-way set associative, 32byte line size"
    },
    {	CPUID_CACHE_UCACHE_256K,
	"Unified L2 cache, 256K, 4-way set associative, 32byte line size"
    },
    {	CPUID_CACHE_UCACHE_512K,
	"Unified L2 cache, 512K, 4-way set associative, 32byte line size"
    },
    {	CPUID_CACHE_NULL,
	(char *)0
    }
};
    
/*
 * CPU identification
 */
unsigned int	cpuid_value;
unsigned char	cpuid_type;
unsigned char	cpuid_family;
unsigned char	cpuid_model;
unsigned char	cpuid_stepping;
unsigned int	cpuid_feature;
char		cpuid_vid[CPUID_VID_SIZE + 1];
unsigned char	cpuid_cache[CPUID_CACHE_SIZE];

/*
 * Return correct CPU_TYPE
 */
/*ARGSUSED*/
cpu_type_t
cpuid_cputype(
    int my_cpu)
{
    switch (cpuid_family) {
    case CPUID_FAMILY_PPRO:
	return (CPU_TYPE_PENTIUMPRO);
    case CPUID_FAMILY_P5:
	return (CPU_TYPE_PENTIUM);
    case CPUID_FAMILY_486:
	return (CPU_TYPE_I486);
    default:
	break;
    }
    return (CPU_TYPE_I386);
}

/*
 * Display processor signature
 */
/*ARGSUSED*/
void
cpuid_cpu_display(
    char *header,
    int my_cpu)
{
    struct cpuid_name *name;
    unsigned int i;
    unsigned int *freq;
    unsigned int mhz;
    unsigned int feature;
    char **flag;
    extern unsigned int delaycount;

    /*
     * Identify vendor ID
     */
    for (name = cpuid_name; name->name != (char *)0; name++) {
	char *p = name->name;
	char *q = cpuid_vid;
	while (*p == *q && *p != 0) {
	    p++;
	    q++;
	}
	if (*p == '\0' && *q == '\0')
	    break;
    }
    if (name->name == (char *)0) {
	printf("Unrecognized processor vendor id = '%s'\n", cpuid_vid);
	return;
    }

    /*
     * Identify Product ID
     */
    for (i = 0; i < name->size; i++)
	if (name->product[i].type == cpuid_type &&
	    name->product[i].family == cpuid_family &&
	    name->product[i].model == cpuid_model)
	    break;
    if (i == name->size) {
	printf("%s processor (type = 0x%x, family = 0x%x, model = 0x%x)\n",
	       "Unrecognized", cpuid_type, cpuid_family, cpuid_model);
	return;
    }

    /*
     * Look for frequency and adjust it to known values
     */
    mhz = (1000 * delaycount) / name->product[i].delay;
    for (freq = name->product[i].frequency; *freq != 0; freq++)
	if (*freq >= mhz)
	    break;
    if (*freq == 0)
	mhz = *(freq - 1);
    else if (freq == name->product[i].frequency)
	mhz = *freq;
    else if (*freq - mhz > mhz - *(freq - 1))
	mhz = *(freq - 1);
    else if (*freq != mhz)
	mhz = *freq;

    /*
     * Display product and frequency
     */
    printf("%s: %s at %d MHz (signature = %d/%d/%d/%d)\n",
	   header, name->product[i].name, mhz, cpuid_type,
	   cpuid_family, cpuid_model, cpuid_stepping);

    /*
     * Display feature (if any)
     */
    if (cpuid_feature) {
	i = 0;
	flag = cpuid_flag;
	for (feature = cpuid_feature; feature != 0; feature >>= 1) {
	    if (feature & 1)
		if (i == 0) {
		    printf("%s: %s", header, *flag);
		    i = 1;
		} else 
		    printf(", %s", *flag);
	    flag++;
	}
	printf("\n");
    }
}

/*
 * Display processor configuration information
 */
/*ARGSUSED*/
void
cpuid_cache_display(
    char *header,
    int my_cpu)
{
    struct cpuid_cache_desc *desc;
    unsigned int i;

    if (cpuid_cache[CPUID_CACHE_VALID] == 1)
	for (i = 0; i < CPUID_CACHE_SIZE; i++) {
	    if (i != CPUID_CACHE_VALID || cpuid_cache[i] == CPUID_CACHE_NULL)
		continue;
	    for (desc = cpuid_cache_desc;
		 desc->description != (char *)0; desc++)
		if (desc->value == cpuid_cache[i])
		    break;
	    if (desc->description != (char *)0)
		printf("%s: %s\n", header, desc->description);
	}
}
