xref: /kernel/linux/linux-5.10/arch/x86/kernel/cpu/cyrix.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/bitops.h>
38c2ecf20Sopenharmony_ci#include <linux/delay.h>
48c2ecf20Sopenharmony_ci#include <linux/pci.h>
58c2ecf20Sopenharmony_ci#include <asm/dma.h>
68c2ecf20Sopenharmony_ci#include <linux/io.h>
78c2ecf20Sopenharmony_ci#include <asm/processor-cyrix.h>
88c2ecf20Sopenharmony_ci#include <asm/processor-flags.h>
98c2ecf20Sopenharmony_ci#include <linux/timer.h>
108c2ecf20Sopenharmony_ci#include <asm/pci-direct.h>
118c2ecf20Sopenharmony_ci#include <asm/tsc.h>
128c2ecf20Sopenharmony_ci#include <asm/cpufeature.h>
138c2ecf20Sopenharmony_ci#include <linux/sched.h>
148c2ecf20Sopenharmony_ci#include <linux/sched/clock.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "cpu.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * Read NSC/Cyrix DEVID registers (DIR) to get more detailed info. about the CPU
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_cistatic void __do_cyrix_devid(unsigned char *dir0, unsigned char *dir1)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	unsigned char ccr2, ccr3;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	/* we test for DEVID by checking whether CCR3 is writable */
268c2ecf20Sopenharmony_ci	ccr3 = getCx86(CX86_CCR3);
278c2ecf20Sopenharmony_ci	setCx86(CX86_CCR3, ccr3 ^ 0x80);
288c2ecf20Sopenharmony_ci	getCx86(0xc0);   /* dummy to change bus */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (getCx86(CX86_CCR3) == ccr3) {       /* no DEVID regs. */
318c2ecf20Sopenharmony_ci		ccr2 = getCx86(CX86_CCR2);
328c2ecf20Sopenharmony_ci		setCx86(CX86_CCR2, ccr2 ^ 0x04);
338c2ecf20Sopenharmony_ci		getCx86(0xc0);  /* dummy */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci		if (getCx86(CX86_CCR2) == ccr2) /* old Cx486SLC/DLC */
368c2ecf20Sopenharmony_ci			*dir0 = 0xfd;
378c2ecf20Sopenharmony_ci		else {                          /* Cx486S A step */
388c2ecf20Sopenharmony_ci			setCx86(CX86_CCR2, ccr2);
398c2ecf20Sopenharmony_ci			*dir0 = 0xfe;
408c2ecf20Sopenharmony_ci		}
418c2ecf20Sopenharmony_ci	} else {
428c2ecf20Sopenharmony_ci		setCx86(CX86_CCR3, ccr3);  /* restore CCR3 */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci		/* read DIR0 and DIR1 CPU registers */
458c2ecf20Sopenharmony_ci		*dir0 = getCx86(CX86_DIR0);
468c2ecf20Sopenharmony_ci		*dir1 = getCx86(CX86_DIR1);
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void do_cyrix_devid(unsigned char *dir0, unsigned char *dir1)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	unsigned long flags;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	local_irq_save(flags);
558c2ecf20Sopenharmony_ci	__do_cyrix_devid(dir0, dir1);
568c2ecf20Sopenharmony_ci	local_irq_restore(flags);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci/*
598c2ecf20Sopenharmony_ci * Cx86_dir0_msb is a HACK needed by check_cx686_cpuid/slop in bugs.h in
608c2ecf20Sopenharmony_ci * order to identify the Cyrix CPU model after we're out of setup.c
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * Actually since bugs.h doesn't even reference this perhaps someone should
638c2ecf20Sopenharmony_ci * fix the documentation ???
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistatic unsigned char Cx86_dir0_msb = 0;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic const char Cx86_model[][9] = {
688c2ecf20Sopenharmony_ci	"Cx486", "Cx486", "5x86 ", "6x86", "MediaGX ", "6x86MX ",
698c2ecf20Sopenharmony_ci	"M II ", "Unknown"
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_cistatic const char Cx486_name[][5] = {
728c2ecf20Sopenharmony_ci	"SLC", "DLC", "SLC2", "DLC2", "SRx", "DRx",
738c2ecf20Sopenharmony_ci	"SRx2", "DRx2"
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_cistatic const char Cx486S_name[][4] = {
768c2ecf20Sopenharmony_ci	"S", "S2", "Se", "S2e"
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_cistatic const char Cx486D_name[][4] = {
798c2ecf20Sopenharmony_ci	"DX", "DX2", "?", "?", "?", "DX4"
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_cistatic char Cx86_cb[] = "?.5x Core/Bus Clock";
828c2ecf20Sopenharmony_cistatic const char cyrix_model_mult1[] = "12??43";
838c2ecf20Sopenharmony_cistatic const char cyrix_model_mult2[] = "12233445";
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * Reset the slow-loop (SLOP) bit on the 686(L) which is set by some old
878c2ecf20Sopenharmony_ci * BIOSes for compatibility with DOS games.  This makes the udelay loop
888c2ecf20Sopenharmony_ci * work correctly, and improves performance.
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * FIXME: our newer udelay uses the tsc. We don't need to frob with SLOP
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void check_cx686_slop(struct cpuinfo_x86 *c)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned long flags;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (Cx86_dir0_msb == 3) {
988c2ecf20Sopenharmony_ci		unsigned char ccr3, ccr5;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		local_irq_save(flags);
1018c2ecf20Sopenharmony_ci		ccr3 = getCx86(CX86_CCR3);
1028c2ecf20Sopenharmony_ci		setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
1038c2ecf20Sopenharmony_ci		ccr5 = getCx86(CX86_CCR5);
1048c2ecf20Sopenharmony_ci		if (ccr5 & 2)
1058c2ecf20Sopenharmony_ci			setCx86(CX86_CCR5, ccr5 & 0xfd);  /* reset SLOP */
1068c2ecf20Sopenharmony_ci		setCx86(CX86_CCR3, ccr3);                 /* disable MAPEN */
1078c2ecf20Sopenharmony_ci		local_irq_restore(flags);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		if (ccr5 & 2) { /* possible wrong calibration done */
1108c2ecf20Sopenharmony_ci			pr_info("Recalibrating delay loop with SLOP bit reset\n");
1118c2ecf20Sopenharmony_ci			calibrate_delay();
1128c2ecf20Sopenharmony_ci			c->loops_per_jiffy = loops_per_jiffy;
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void set_cx86_reorder(void)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	u8 ccr3;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	pr_info("Enable Memory access reorder on Cyrix/NSC processor.\n");
1238c2ecf20Sopenharmony_ci	ccr3 = getCx86(CX86_CCR3);
1248c2ecf20Sopenharmony_ci	setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Load/Store Serialize to mem access disable (=reorder it) */
1278c2ecf20Sopenharmony_ci	setCx86(CX86_PCR0, getCx86(CX86_PCR0) & ~0x80);
1288c2ecf20Sopenharmony_ci	/* set load/store serialize from 1GB to 4GB */
1298c2ecf20Sopenharmony_ci	ccr3 |= 0xe0;
1308c2ecf20Sopenharmony_ci	setCx86(CX86_CCR3, ccr3);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void set_cx86_memwb(void)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	pr_info("Enable Memory-Write-back mode on Cyrix/NSC processor.\n");
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* CCR2 bit 2: unlock NW bit */
1388c2ecf20Sopenharmony_ci	setCx86(CX86_CCR2, getCx86(CX86_CCR2) & ~0x04);
1398c2ecf20Sopenharmony_ci	/* set 'Not Write-through' */
1408c2ecf20Sopenharmony_ci	write_cr0(read_cr0() | X86_CR0_NW);
1418c2ecf20Sopenharmony_ci	/* CCR2 bit 2: lock NW bit and set WT1 */
1428c2ecf20Sopenharmony_ci	setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x14);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci *	Configure later MediaGX and/or Geode processor.
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void geode_configure(void)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	unsigned long flags;
1528c2ecf20Sopenharmony_ci	u8 ccr3;
1538c2ecf20Sopenharmony_ci	local_irq_save(flags);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Suspend on halt power saving and enable #SUSP pin */
1568c2ecf20Sopenharmony_ci	setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x88);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ccr3 = getCx86(CX86_CCR3);
1598c2ecf20Sopenharmony_ci	setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);	/* enable MAPEN */
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* FPU fast, DTE cache, Mem bypass */
1638c2ecf20Sopenharmony_ci	setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x38);
1648c2ecf20Sopenharmony_ci	setCx86(CX86_CCR3, ccr3);			/* disable MAPEN */
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	set_cx86_memwb();
1678c2ecf20Sopenharmony_ci	set_cx86_reorder();
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void early_init_cyrix(struct cpuinfo_x86 *c)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	unsigned char dir0, dir0_msn, dir1 = 0;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	__do_cyrix_devid(&dir0, &dir1);
1778c2ecf20Sopenharmony_ci	dir0_msn = dir0 >> 4; /* identifies CPU "family"   */
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	switch (dir0_msn) {
1808c2ecf20Sopenharmony_ci	case 3: /* 6x86/6x86L */
1818c2ecf20Sopenharmony_ci		/* Emulate MTRRs using Cyrix's ARRs. */
1828c2ecf20Sopenharmony_ci		set_cpu_cap(c, X86_FEATURE_CYRIX_ARR);
1838c2ecf20Sopenharmony_ci		break;
1848c2ecf20Sopenharmony_ci	case 5: /* 6x86MX/M II */
1858c2ecf20Sopenharmony_ci		/* Emulate MTRRs using Cyrix's ARRs. */
1868c2ecf20Sopenharmony_ci		set_cpu_cap(c, X86_FEATURE_CYRIX_ARR);
1878c2ecf20Sopenharmony_ci		break;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void init_cyrix(struct cpuinfo_x86 *c)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	unsigned char dir0, dir0_msn, dir0_lsn, dir1 = 0;
1948c2ecf20Sopenharmony_ci	char *buf = c->x86_model_id;
1958c2ecf20Sopenharmony_ci	const char *p = NULL;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/*
1988c2ecf20Sopenharmony_ci	 * Bit 31 in normal CPUID used for nonstandard 3DNow ID;
1998c2ecf20Sopenharmony_ci	 * 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway
2008c2ecf20Sopenharmony_ci	 */
2018c2ecf20Sopenharmony_ci	clear_cpu_cap(c, 0*32+31);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* Cyrix used bit 24 in extended (AMD) CPUID for Cyrix MMX extensions */
2048c2ecf20Sopenharmony_ci	if (test_cpu_cap(c, 1*32+24)) {
2058c2ecf20Sopenharmony_ci		clear_cpu_cap(c, 1*32+24);
2068c2ecf20Sopenharmony_ci		set_cpu_cap(c, X86_FEATURE_CXMMX);
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	do_cyrix_devid(&dir0, &dir1);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	check_cx686_slop(c);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	Cx86_dir0_msb = dir0_msn = dir0 >> 4; /* identifies CPU "family"   */
2148c2ecf20Sopenharmony_ci	dir0_lsn = dir0 & 0xf;                /* model or clock multiplier */
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* common case step number/rev -- exceptions handled below */
2178c2ecf20Sopenharmony_ci	c->x86_model = (dir1 >> 4) + 1;
2188c2ecf20Sopenharmony_ci	c->x86_stepping = dir1 & 0xf;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Now cook; the original recipe is by Channing Corn, from Cyrix.
2218c2ecf20Sopenharmony_ci	 * We do the same thing for each generation: we work out
2228c2ecf20Sopenharmony_ci	 * the model, multiplier and stepping.  Black magic included,
2238c2ecf20Sopenharmony_ci	 * to make the silicon step/rev numbers match the printed ones.
2248c2ecf20Sopenharmony_ci	 */
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	switch (dir0_msn) {
2278c2ecf20Sopenharmony_ci		unsigned char tmp;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	case 0: /* Cx486SLC/DLC/SRx/DRx */
2308c2ecf20Sopenharmony_ci		p = Cx486_name[dir0_lsn & 7];
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	case 1: /* Cx486S/DX/DX2/DX4 */
2348c2ecf20Sopenharmony_ci		p = (dir0_lsn & 8) ? Cx486D_name[dir0_lsn & 5]
2358c2ecf20Sopenharmony_ci			: Cx486S_name[dir0_lsn & 3];
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	case 2: /* 5x86 */
2398c2ecf20Sopenharmony_ci		Cx86_cb[2] = cyrix_model_mult1[dir0_lsn & 5];
2408c2ecf20Sopenharmony_ci		p = Cx86_cb+2;
2418c2ecf20Sopenharmony_ci		break;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	case 3: /* 6x86/6x86L */
2448c2ecf20Sopenharmony_ci		Cx86_cb[1] = ' ';
2458c2ecf20Sopenharmony_ci		Cx86_cb[2] = cyrix_model_mult1[dir0_lsn & 5];
2468c2ecf20Sopenharmony_ci		if (dir1 > 0x21) { /* 686L */
2478c2ecf20Sopenharmony_ci			Cx86_cb[0] = 'L';
2488c2ecf20Sopenharmony_ci			p = Cx86_cb;
2498c2ecf20Sopenharmony_ci			(c->x86_model)++;
2508c2ecf20Sopenharmony_ci		} else             /* 686 */
2518c2ecf20Sopenharmony_ci			p = Cx86_cb+1;
2528c2ecf20Sopenharmony_ci		/* Emulate MTRRs using Cyrix's ARRs. */
2538c2ecf20Sopenharmony_ci		set_cpu_cap(c, X86_FEATURE_CYRIX_ARR);
2548c2ecf20Sopenharmony_ci		/* 6x86's contain this bug */
2558c2ecf20Sopenharmony_ci		set_cpu_bug(c, X86_BUG_COMA);
2568c2ecf20Sopenharmony_ci		break;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	case 4: /* MediaGX/GXm or Geode GXM/GXLV/GX1 */
2598c2ecf20Sopenharmony_ci	case 11: /* GX1 with inverted Device ID */
2608c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
2618c2ecf20Sopenharmony_ci	{
2628c2ecf20Sopenharmony_ci		u32 vendor, device;
2638c2ecf20Sopenharmony_ci		/*
2648c2ecf20Sopenharmony_ci		 * It isn't really a PCI quirk directly, but the cure is the
2658c2ecf20Sopenharmony_ci		 * same. The MediaGX has deep magic SMM stuff that handles the
2668c2ecf20Sopenharmony_ci		 * SB emulation. It throws away the fifo on disable_dma() which
2678c2ecf20Sopenharmony_ci		 * is wrong and ruins the audio.
2688c2ecf20Sopenharmony_ci		 *
2698c2ecf20Sopenharmony_ci		 *  Bug2: VSA1 has a wrap bug so that using maximum sized DMA
2708c2ecf20Sopenharmony_ci		 *  causes bad things. According to NatSemi VSA2 has another
2718c2ecf20Sopenharmony_ci		 *  bug to do with 'hlt'. I've not seen any boards using VSA2
2728c2ecf20Sopenharmony_ci		 *  and X doesn't seem to support it either so who cares 8).
2738c2ecf20Sopenharmony_ci		 *  VSA1 we work around however.
2748c2ecf20Sopenharmony_ci		 */
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		pr_info("Working around Cyrix MediaGX virtual DMA bugs.\n");
2778c2ecf20Sopenharmony_ci		isa_dma_bridge_buggy = 2;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		/* We do this before the PCI layer is running. However we
2808c2ecf20Sopenharmony_ci		   are safe here as we know the bridge must be a Cyrix
2818c2ecf20Sopenharmony_ci		   companion and must be present */
2828c2ecf20Sopenharmony_ci		vendor = read_pci_config_16(0, 0, 0x12, PCI_VENDOR_ID);
2838c2ecf20Sopenharmony_ci		device = read_pci_config_16(0, 0, 0x12, PCI_DEVICE_ID);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		/*
2868c2ecf20Sopenharmony_ci		 *  The 5510/5520 companion chips have a funky PIT.
2878c2ecf20Sopenharmony_ci		 */
2888c2ecf20Sopenharmony_ci		if (vendor == PCI_VENDOR_ID_CYRIX &&
2898c2ecf20Sopenharmony_ci			(device == PCI_DEVICE_ID_CYRIX_5510 ||
2908c2ecf20Sopenharmony_ci					device == PCI_DEVICE_ID_CYRIX_5520))
2918c2ecf20Sopenharmony_ci			mark_tsc_unstable("cyrix 5510/5520 detected");
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci#endif
2948c2ecf20Sopenharmony_ci		c->x86_cache_size = 16;	/* Yep 16K integrated cache thats it */
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		/* GXm supports extended cpuid levels 'ala' AMD */
2978c2ecf20Sopenharmony_ci		if (c->cpuid_level == 2) {
2988c2ecf20Sopenharmony_ci			/* Enable cxMMX extensions (GX1 Datasheet 54) */
2998c2ecf20Sopenharmony_ci			setCx86(CX86_CCR7, getCx86(CX86_CCR7) | 1);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci			/*
3028c2ecf20Sopenharmony_ci			 * GXm : 0x30 ... 0x5f GXm  datasheet 51
3038c2ecf20Sopenharmony_ci			 * GXlv: 0x6x          GXlv datasheet 54
3048c2ecf20Sopenharmony_ci			 *  ?  : 0x7x
3058c2ecf20Sopenharmony_ci			 * GX1 : 0x8x          GX1  datasheet 56
3068c2ecf20Sopenharmony_ci			 */
3078c2ecf20Sopenharmony_ci			if ((0x30 <= dir1 && dir1 <= 0x6f) ||
3088c2ecf20Sopenharmony_ci					(0x80 <= dir1 && dir1 <= 0x8f))
3098c2ecf20Sopenharmony_ci				geode_configure();
3108c2ecf20Sopenharmony_ci			return;
3118c2ecf20Sopenharmony_ci		} else { /* MediaGX */
3128c2ecf20Sopenharmony_ci			Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4';
3138c2ecf20Sopenharmony_ci			p = Cx86_cb+2;
3148c2ecf20Sopenharmony_ci			c->x86_model = (dir1 & 0x20) ? 1 : 2;
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	case 5: /* 6x86MX/M II */
3198c2ecf20Sopenharmony_ci		if (dir1 > 7) {
3208c2ecf20Sopenharmony_ci			dir0_msn++;  /* M II */
3218c2ecf20Sopenharmony_ci			/* Enable MMX extensions (App note 108) */
3228c2ecf20Sopenharmony_ci			setCx86(CX86_CCR7, getCx86(CX86_CCR7)|1);
3238c2ecf20Sopenharmony_ci		} else {
3248c2ecf20Sopenharmony_ci			/* A 6x86MX - it has the bug. */
3258c2ecf20Sopenharmony_ci			set_cpu_bug(c, X86_BUG_COMA);
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci		tmp = (!(dir0_lsn & 7) || dir0_lsn & 1) ? 2 : 0;
3288c2ecf20Sopenharmony_ci		Cx86_cb[tmp] = cyrix_model_mult2[dir0_lsn & 7];
3298c2ecf20Sopenharmony_ci		p = Cx86_cb+tmp;
3308c2ecf20Sopenharmony_ci		if (((dir1 & 0x0f) > 4) || ((dir1 & 0xf0) == 0x20))
3318c2ecf20Sopenharmony_ci			(c->x86_model)++;
3328c2ecf20Sopenharmony_ci		/* Emulate MTRRs using Cyrix's ARRs. */
3338c2ecf20Sopenharmony_ci		set_cpu_cap(c, X86_FEATURE_CYRIX_ARR);
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	case 0xf:  /* Cyrix 486 without DEVID registers */
3378c2ecf20Sopenharmony_ci		switch (dir0_lsn) {
3388c2ecf20Sopenharmony_ci		case 0xd:  /* either a 486SLC or DLC w/o DEVID */
3398c2ecf20Sopenharmony_ci			dir0_msn = 0;
3408c2ecf20Sopenharmony_ci			p = Cx486_name[!!boot_cpu_has(X86_FEATURE_FPU)];
3418c2ecf20Sopenharmony_ci			break;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		case 0xe:  /* a 486S A step */
3448c2ecf20Sopenharmony_ci			dir0_msn = 0;
3458c2ecf20Sopenharmony_ci			p = Cx486S_name[0];
3468c2ecf20Sopenharmony_ci			break;
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci		break;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	default:  /* unknown (shouldn't happen, we know everyone ;-) */
3518c2ecf20Sopenharmony_ci		dir0_msn = 7;
3528c2ecf20Sopenharmony_ci		break;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci	strcpy(buf, Cx86_model[dir0_msn & 7]);
3558c2ecf20Sopenharmony_ci	if (p)
3568c2ecf20Sopenharmony_ci		strcat(buf, p);
3578c2ecf20Sopenharmony_ci	return;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci/*
3618c2ecf20Sopenharmony_ci * Handle National Semiconductor branded processors
3628c2ecf20Sopenharmony_ci */
3638c2ecf20Sopenharmony_cistatic void init_nsc(struct cpuinfo_x86 *c)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	/*
3668c2ecf20Sopenharmony_ci	 * There may be GX1 processors in the wild that are branded
3678c2ecf20Sopenharmony_ci	 * NSC and not Cyrix.
3688c2ecf20Sopenharmony_ci	 *
3698c2ecf20Sopenharmony_ci	 * This function only handles the GX processor, and kicks every
3708c2ecf20Sopenharmony_ci	 * thing else to the Cyrix init function above - that should
3718c2ecf20Sopenharmony_ci	 * cover any processors that might have been branded differently
3728c2ecf20Sopenharmony_ci	 * after NSC acquired Cyrix.
3738c2ecf20Sopenharmony_ci	 *
3748c2ecf20Sopenharmony_ci	 * If this breaks your GX1 horribly, please e-mail
3758c2ecf20Sopenharmony_ci	 * info-linux@ldcmail.amd.com to tell us.
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* Handle the GX (Formally known as the GX2) */
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (c->x86 == 5 && c->x86_model == 5)
3818c2ecf20Sopenharmony_ci		cpu_detect_cache_sizes(c);
3828c2ecf20Sopenharmony_ci	else
3838c2ecf20Sopenharmony_ci		init_cyrix(c);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/*
3878c2ecf20Sopenharmony_ci * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
3888c2ecf20Sopenharmony_ci * by the fact that they preserve the flags across the division of 5/2.
3898c2ecf20Sopenharmony_ci * PII and PPro exhibit this behavior too, but they have cpuid available.
3908c2ecf20Sopenharmony_ci */
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci/*
3938c2ecf20Sopenharmony_ci * Perform the Cyrix 5/2 test. A Cyrix won't change
3948c2ecf20Sopenharmony_ci * the flags, while other 486 chips will.
3958c2ecf20Sopenharmony_ci */
3968c2ecf20Sopenharmony_cistatic inline int test_cyrix_52div(void)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	unsigned int test;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	__asm__ __volatile__(
4018c2ecf20Sopenharmony_ci	     "sahf\n\t"		/* clear flags (%eax = 0x0005) */
4028c2ecf20Sopenharmony_ci	     "div %b2\n\t"	/* divide 5 by 2 */
4038c2ecf20Sopenharmony_ci	     "lahf"		/* store flags into %ah */
4048c2ecf20Sopenharmony_ci	     : "=a" (test)
4058c2ecf20Sopenharmony_ci	     : "0" (5), "q" (2)
4068c2ecf20Sopenharmony_ci	     : "cc");
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* AH is 0x02 on Cyrix after the divide.. */
4098c2ecf20Sopenharmony_ci	return (unsigned char) (test >> 8) == 0x02;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic void cyrix_identify(struct cpuinfo_x86 *c)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	/* Detect Cyrix with disabled CPUID */
4158c2ecf20Sopenharmony_ci	if (c->x86 == 4 && test_cyrix_52div()) {
4168c2ecf20Sopenharmony_ci		unsigned char dir0, dir1;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		strcpy(c->x86_vendor_id, "CyrixInstead");
4198c2ecf20Sopenharmony_ci		c->x86_vendor = X86_VENDOR_CYRIX;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		/* Actually enable cpuid on the older cyrix */
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		/* Retrieve CPU revisions */
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		do_cyrix_devid(&dir0, &dir1);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		dir0 >>= 4;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		/* Check it is an affected model */
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		if (dir0 == 5 || dir0 == 3) {
4328c2ecf20Sopenharmony_ci			unsigned char ccr3;
4338c2ecf20Sopenharmony_ci			unsigned long flags;
4348c2ecf20Sopenharmony_ci			pr_info("Enabling CPUID on Cyrix processor.\n");
4358c2ecf20Sopenharmony_ci			local_irq_save(flags);
4368c2ecf20Sopenharmony_ci			ccr3 = getCx86(CX86_CCR3);
4378c2ecf20Sopenharmony_ci			/* enable MAPEN  */
4388c2ecf20Sopenharmony_ci			setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
4398c2ecf20Sopenharmony_ci			/* enable cpuid  */
4408c2ecf20Sopenharmony_ci			setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80);
4418c2ecf20Sopenharmony_ci			/* disable MAPEN */
4428c2ecf20Sopenharmony_ci			setCx86(CX86_CCR3, ccr3);
4438c2ecf20Sopenharmony_ci			local_irq_restore(flags);
4448c2ecf20Sopenharmony_ci		}
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic const struct cpu_dev cyrix_cpu_dev = {
4498c2ecf20Sopenharmony_ci	.c_vendor	= "Cyrix",
4508c2ecf20Sopenharmony_ci	.c_ident	= { "CyrixInstead" },
4518c2ecf20Sopenharmony_ci	.c_early_init	= early_init_cyrix,
4528c2ecf20Sopenharmony_ci	.c_init		= init_cyrix,
4538c2ecf20Sopenharmony_ci	.c_identify	= cyrix_identify,
4548c2ecf20Sopenharmony_ci	.c_x86_vendor	= X86_VENDOR_CYRIX,
4558c2ecf20Sopenharmony_ci};
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cicpu_dev_register(cyrix_cpu_dev);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic const struct cpu_dev nsc_cpu_dev = {
4608c2ecf20Sopenharmony_ci	.c_vendor	= "NSC",
4618c2ecf20Sopenharmony_ci	.c_ident	= { "Geode by NSC" },
4628c2ecf20Sopenharmony_ci	.c_init		= init_nsc,
4638c2ecf20Sopenharmony_ci	.c_x86_vendor	= X86_VENDOR_NSC,
4648c2ecf20Sopenharmony_ci};
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cicpu_dev_register(nsc_cpu_dev);
467