/* 
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2000-2002 Silicon Graphics, Inc. All rights reserved.
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/kernel_stat.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/efi.h>
#include <asm/page.h>
#include <linux/threads.h>
#include <asm/sn/simulator.h>
#include <asm/sn/leds.h>

#include "llsc4.h"


#ifdef STANDALONE
#include "lock.h"
#endif

#ifdef INTTEST
static int	inttest=0;
#endif

#ifdef IA64_SEMFIX_INSN
#undef IA64_SEMFIX_INSN
#endif
#ifdef IA64_SEMFIX
#undef IA64_SEMFIX
#endif
# define IA64_SEMFIX_INSN
# define IA64_SEMFIX    ""

#define NOLOCK		0xdead
#define BGUARD(linei)	(0xbbbb0000 | (linei));
#define EGUARD(linei)	(0xeeee0000 | (linei));
#define GUARDLINE(v)	((v)&0xffff)

/*
 * Test parameter table for AUTOTEST
 */
typedef struct {
	int	passes;
	int	linecount;
	int	linepad;
} autotest_table_t;

autotest_table_t autotest_table[] = {
	{50000000,	2,	0x2b4		},
	{50000000,	16,	0,		},
	{50000000,	16,	4,		},
	{50000000,	128,	0x44		},
	{50000000,	128,	0x84		},
	{50000000,	128,	0x200		},
	{50000000,	128,	0x204		},
	{50000000,	128,	0x2b4		},
	{50000000,	2,	8*MB+0x2b4	},
	{50000000,	16,	8*MB+0		},
	{50000000,	16,	8*MB+4		},
	{50000000,	128,	8*MB+0x44	},
	{50000000,	128,	8*MB+0x84	},
	{50000000,	128,	8*MB+0x200	},
	{50000000,	128,	8*MB+0x204	},
	{50000000,	128,	8*MB+0x2b4	},
	{0}};

/*
 * Array of virtual addresses available for test purposes.
 */

typedef struct {
	long	vstart;
	long	vend;
	long	nextaddr;
	long	nextinit;
	int	wrapcount;
} memmap_t;

#define MAPCHUNKS		128
memmap_t 	memmap[MAPCHUNKS];
int		memmapx=0;

typedef struct {
	void	*addr;
	long	data[16];
	long	data_fc[16];
} capture_line_t;

typedef struct {
	int	size;
	void	*blockaddr;
	void	*shadaddr;
	long	blockdata[48];
	long	shaddata[48];
	long	blockdata_fc[48];
	long	shaddata_fc[48];
	long	synerr;
} capture_t;

/*
 * PORTING NOTE: revisit this statement. On hardware we put mbase at 0 and
 * the rest of the tables have to start at 1MB to skip PROM tables.
 */
#define THREADPRIVATESZ()	((sizeof(threadprivate_t)+511)/512*512)
#define THREADPRIVATE(t)	((threadprivate_t*)(((long)mbase)+4096+t*THREADPRIVATESZ()))

#define k_capture		mbase->sk_capture
#define k_go			mbase->sk_go
#define k_linecount		mbase->sk_linecount
#define k_passes		mbase->sk_passes
#define k_napticks		mbase->sk_napticks
#define k_stop_on_error		mbase->sk_stop_on_error
#define k_verbose		mbase->sk_verbose
#define k_threadprivate		mbase->sk_threadprivate
#define k_blocks		mbase->sk_blocks
#define k_iter_msg		mbase->sk_iter_msg
#define k_vv			mbase->sk_vv
#define k_linepad		mbase->sk_linepad
#define k_options		mbase->sk_options
#define k_testnumber		mbase->sk_testnumber
#define k_currentpass		mbase->sk_currentpass

static long		blocks[MAX_LINECOUNT];		/* addresses of data blocks */
static control_t	*mbase;
static vint		initialized=0;

static unsigned int ran_conf_llsc(int);
static int  rerr(capture_t *, char *, void *, void *, int, int, int, int, int, int);
static void dumpline(void *, char *, char *, void *, void *, int);
static int  checkstop(int, int, uint);
static void spin(int);
static void capturedata(capture_t *, uint, void *, void *, int);
static int  randn(uint max, uint *seed);
static uint zrandom (uint *zranseed);
static int  set_lock(uint *, uint);
static int  clr_lock(uint *, uint);
static void Speedo(void);

int autotest_enabled=0;
static int llsctest_number=-1;
static int errstop_enabled=0;
static int fail_enabled=0;
static int l4_opt=0;
static int selective_trigger=0;
static int dump_block_addrs_opt=0;
static lock_t errlock=NOLOCK;
static private_t init_private[LLSC_MAXCPUS];

static int __init autotest_enable(char *str)
{
        autotest_enabled = 1;
	return 1;
}
static int __init set_llscblkadr(char *str)
{
	dump_block_addrs_opt = 1;
	return 1;
}
static int __init set_llscselt(char *str)
{
	selective_trigger = 1;
	return 1;
}
static int __init set_llsctest(char *str)
{
        llsctest_number = simple_strtol(str, &str, 10);
	if (llsctest_number < 0 || llsctest_number > 15)
		llsctest_number = -1;
	return 1;
}
static int __init set_llscerrstop(char *str)
{
        errstop_enabled = 1;
	return 1;
}
static int __init set_llscfail(char *str)
{
        fail_enabled = 8;
	return 1;
}
static int __init set_llscl4(char *str)
{
        l4_opt = 1;
	return 1;
}

static void print_params(void)
{
	printk ("********* Enter AUTOTEST facility on master cpu *************\n");
	printk ("  Test options:\n");
	printk ("     llsctest=<n>\t%d\tTest number to run (all = -1)\n", llsctest_number);
	printk ("     llscerrstop \t%s\tStop on error\n", errstop_enabled ? "on" : "off");
	printk ("     llscfail    \t%s\tForce a failure to test the trigger & error messages\n", fail_enabled ? "on" : "off");
	printk ("     llscselt    \t%s\tSelective triger on failures\n", selective_trigger ? "on" : "off");
	printk ("     llscblkadr  \t%s\tDump data block addresses\n", dump_block_addrs_opt ? "on" : "off");
	printk ("     llscl4      \t%s\tRun only tests that evict from L4\n", l4_opt ? "on" : "off");
	printk ("  SEMFIX: %s\n", IA64_SEMFIX);
	printk ("\n");
}
__setup("autotest", autotest_enable);
__setup("llsctest=", set_llsctest);
__setup("llscerrstop", set_llscerrstop);
__setup("llscfail", set_llscfail);
__setup("llscselt", set_llscselt);
__setup("llscblkadr", set_llscblkadr);
__setup("llscl4", set_llscl4);



static inline int
set_lock(uint *lock, uint id)
{
	uint	old;
	old = cmpxchg_acq(lock, NOLOCK, id);
	return (old == NOLOCK);
}

static inline int
clr_lock(uint *lock, uint id)
{
	uint	old;
	old = cmpxchg_rel(lock, id, NOLOCK);
	return (old == id);
}

static inline void
init_lock(uint *lock)
{
	*lock = NOLOCK;
}

/*------------------------------------------------------------------------+
| Routine  :  ran_conf_llsc - ll/sc shared data test                      |
| Description: This test checks the coherency of shared data              |
+------------------------------------------------------------------------*/
static unsigned int
ran_conf_llsc(int thread)
{
	private_t	pval;
	share_t		sval, sval2;
	uint		vv, linei, slinei, sharei, pass;
	long		t;
	lock_t		lockpat;
	share_t		*sharecopy;
	long		verbose, napticks, passes, linecount, lcount;
	dataline_t	*linep, *slinep;
	int		s, seed;
	threadprivate_t	*tp;
	uint		iter_msg, iter_msg_i=0;
	int		vv_mask;
	int		correct_errors;
	int		errs=0;
	int		stillbad;
	capture_t	capdata;
	private_t	*privp;
	share_t		*sharep;


	linecount = k_linecount;
	napticks = k_napticks;
	verbose = k_verbose;
	passes = k_passes;
	iter_msg = k_iter_msg;
	seed = (thread + 1) * 647;
	tp = THREADPRIVATE(thread);
	vv_mask = (k_vv>>((thread%16)*4)) & 0xf;
	correct_errors = k_options&0xff;

	memset (&capdata, 0, sizeof(capdata));
	for (linei=0; linei<linecount; linei++)
		tp->private[linei] = thread;	

	for (pass = 1; passes == 0 || pass < passes; pass++) {
		lockpat = (pass & 0x0fffffff) + (thread <<28);
		if (lockpat == NOLOCK)
			continue;
		tp->threadpasses = pass;
		if (checkstop(thread, pass, lockpat))
			return 0;
		iter_msg_i++;
		if (iter_msg && iter_msg_i > iter_msg) {
			printk("Thread %d, Pass %d\n", thread, pass);
			iter_msg_i = 0;
		}
		lcount = 0;

		/*
		 * Select line to perform operations on.
		 */
		linei = randn(linecount, &seed);
		sharei = randn(2, &seed);
		slinei = (linei + (linecount/2))%linecount;		/* I dont like this - fix later */

		linep = (dataline_t *)blocks[linei];
		slinep = (dataline_t *)blocks[slinei];
		if (sharei == 0)
			sharecopy = &slinep->share0;
		else
			sharecopy = &slinep->share1;


		vv = randn(4, &seed);
		if ((vv_mask & (1<<vv)) == 0)
			continue;

		if (napticks) {
			t = randn(napticks, &seed);
			udelay(t);
		}
		privp = &linep->private[thread];
		sharep = &linep->share[sharei];
		
		switch(vv) {
		case 0:
			/* Read and verify private count on line. */
			pval = *privp;
			if (verbose)
				printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, tp->private[linei]);
			if (pval != tp->private[linei]) {
				capturedata(&capdata, pass, privp, NULL, sizeof(*privp));
				stillbad = (*privp != tp->private[linei]);
				if (rerr(&capdata, "Private count", linep, slinep, thread, pass, linei, tp->private[linei], pval, stillbad)) {
					return 1;
				}
				if (correct_errors) {
					tp->private[linei] = *privp;
				}
				errs++;
			}
			break;

		case 1:
			/* Read, verify, and increment private count on line. */
			pval = *privp;
			if (verbose)
				printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, tp->private[linei]);
			if (pval != tp->private[linei]) {
				capturedata(&capdata, pass, privp, NULL, sizeof(*privp));
				stillbad = (*privp != tp->private[linei]);
				if (rerr(&capdata, "Private count & inc", linep, slinep, thread, pass, linei, tp->private[linei], pval, stillbad)) {
					return 1;
				}
				errs++;
			}
			pval = (pval==255) ? 0 : pval+1;
			*privp = pval;
			tp->private[linei] = pval;
			break;

		case 2:
			/* Lock line, read and verify shared data. */
			if (verbose)
				printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, *sharecopy);
			lcount = 0;
			while (LOCK(sharei) != 1) {
				if (checkstop(thread, pass, lockpat))
					return 0;
				if (lcount++>1000000) {
					capturedata(&capdata, pass, LOCKADDR(sharei), NULL, sizeof(lock_t));
					stillbad = (GETLOCK(sharei) != 0);
					rerr(&capdata, "Shared data lock", linep, slinep, thread, pass, linei, 0, GETLOCK(sharei), stillbad);
					return 1;
				}
				if ((lcount&0x3fff) == 0)
					udelay(1000);
			}

			sval = *sharep;
			sval2 = *sharecopy;
			if (pass > 12 && thread == 0 && fail_enabled == 1)
				sval++;
			if (sval != sval2) {
				capturedata(&capdata, pass, sharep, sharecopy, sizeof(*sharecopy));
				stillbad = (*sharep != *sharecopy);
				if (!stillbad && *sharep != sval && *sharecopy == sval2)
					stillbad = 2;
				if (rerr(&capdata, "Shared data", linep, slinep, thread, pass, linei, sval2, sval, stillbad)) {
					return 1;
				}
				if (correct_errors)
					*sharep = *sharecopy;
				errs++;
			}


			if ( (s=UNLOCK(sharei)) != 1) {
				capturedata(&capdata, pass, LOCKADDR(sharei), NULL, 4);
				stillbad = (GETLOCK(sharei) != lockpat);
				if (rerr(&capdata, "Shared data unlock", linep, slinep, thread, pass, linei, lockpat, GETLOCK(sharei), stillbad))
					return 1;
				if (correct_errors)
					ZEROLOCK(sharei);	
				errs++;
			}
			break;

		case 3:
			/* Lock line, read and verify shared data, modify shared data. */
			if (verbose)
				printk("Line:%3d, Thread:%d:%d. Val: %x\n", linei, thread, vv, *sharecopy);
			lcount = 0;
			while (LOCK(sharei) != 1) {
				if (checkstop(thread, pass, lockpat))
					return 0;
				if (lcount++>1000000) {
					capturedata(&capdata, pass, LOCKADDR(sharei), NULL, sizeof(lock_t));
					stillbad = (GETLOCK(sharei) != 0);
					rerr(&capdata, "Shared data lock & inc", linep, slinep, thread, pass, linei, 0, GETLOCK(sharei), stillbad);
					return 1;
				}
				if ((lcount&0x3fff) == 0)
					udelay(1000);
			}
			sval = *sharep;
			sval2 = *sharecopy;
			if (sval != sval2) {
				capturedata(&capdata, pass, sharep, sharecopy, sizeof(*sharecopy));
				stillbad = (*sharep != *sharecopy);
				if (!stillbad && *sharep != sval && *sharecopy == sval2)
					stillbad = 2;
				if (rerr(&capdata, "Shared data & inc", linep, slinep, thread, pass, linei, sval2, sval, stillbad)) {
					return 1;
				}
				errs++;
			}

			*sharep = lockpat;
			*sharecopy = lockpat;


			if ( (s=UNLOCK(sharei)) != 1) {
				capturedata(&capdata, pass, LOCKADDR(sharei), NULL, 4);
				stillbad = (GETLOCK(sharei) != lockpat);
				if (rerr(&capdata, "Shared data & inc unlock", linep, slinep, thread, pass, linei, thread, GETLOCK(sharei), stillbad))
					return 1;
				if (correct_errors)
					ZEROLOCK(sharei);	
				errs++;
			}
			break;
		}
	}

	return (errs > 0);
}

static void
trigger_la(long val)
{
	long	*p;

	p = (long*)0xc0000a0001000020L; /* PI_CPU_NUM */
	*p = val;
}

static long
getsynerr(void)
{
	long	err, *errp;

	errp = (long*)0xc0000e0000000340L;	/* SYN_ERR */
	err = *errp;
	if (err)
		*errp = -1L;
	return (err & ~0x60);
}

static int
rerr(capture_t *cap, char *msg, void *lp, void *slp, int thread, int pass, int badlinei, int exp, int found, int stillbad)
{
	int		cpu, i, linei;
	long 		synerr;
	int		selt;


	selt = selective_trigger && stillbad > 1 && 
			memcmp(cap->blockdata, cap->blockdata_fc, 128) != 0 &&
			memcmp(cap->shaddata, cap->shaddata_fc, 128) == 0;
	if (selt) {
		trigger_la(pass);
	} else if (selective_trigger) {
		k_go = ST_STOP;
		return k_stop_on_error;;
	}

	spin(1);
	i = 100;
	while (i && set_lock(&errlock, 1) != 1) {
		spin(1);
		i--;
	}
	printk ("\nDataError!: %-20s, test %ld, thread %d, line:%d, pass %d (0x%x), time %ld expected:%x, found:%x\n",
	    msg, k_testnumber, thread, badlinei, pass, pass, jiffies, exp, found);

	dumpline (lp, "Corrupted data", "D ", cap->blockaddr, cap->blockdata, cap->size);
#ifdef ZZZ
	if (memcmp(cap->blockdata, cap->blockdata_fc, 128))
		dumpline (lp, "Corrupted data", "DF", cap->blockaddr, cap->blockdata_fc, cap->size);
#endif

	if (cap->shadaddr) {
		dumpline (slp, "Shadow    data", "S ", cap->shadaddr, cap->shaddata, cap->size);
#ifdef ZZZ
		if (memcmp(cap->shaddata, cap->shaddata_fc, 128))
			dumpline (slp, "Shadow    data", "SF", cap->shadaddr, cap->shaddata_fc, cap->size);
#endif
	}
	
	printk("Threadpasses: ");
	for (cpu=0,i=0; cpu<LLSC_MAXCPUS; cpu++)
		if (k_threadprivate[cpu]->threadpasses) {
			if (i && (i%8) == 0)
				printk("\n            : ");
			printk("  %d:0x%x", cpu, k_threadprivate[cpu]->threadpasses);
			i++;
		}
	printk("\n");
	
	for (linei=0; linei<k_linecount; linei++) {
		int		slinei, g1linei, g2linei, g1err, g2err, sh0err, sh1err;
		dataline_t	*linep, *slinep;

		slinei = (linei + (k_linecount/2))%k_linecount;
		linep = (dataline_t *)blocks[linei];
		slinep = (dataline_t *)blocks[slinei];

		g1linei = GUARDLINE(linep->guard1);
		g2linei = GUARDLINE(linep->guard2);
		g1err = (g1linei != linei);
		g2err = (g2linei != linei);
		sh0err = (linep->share[0] != slinep->share0);
		sh1err = (linep->share[1] != slinep->share1);

		if (g1err || g2err || sh0err || sh1err) {
			printk("Line 0x%lx (%03d), %sG1 0x%lx (%03d), %sG2 0x%lx (%03d), %sSH0 %08x (%08x), %sSH1 %08x (%08x)\n",
				blocks[linei], linei, 
				g1err ? "*" : " ", blocks[g1linei], g1linei,
				g2err ? "*" : " ", blocks[g2linei], g2linei,
				sh0err ? "*" : " ", linep->share[0], slinep->share0,
				sh1err ? "*" : " ", linep->share[1], slinep->share1);


		}
	}

	printk("\nData was %sfixed by flushcache\n", (stillbad == 1 ? "**** NOT **** " : " "));
	synerr = getsynerr();
	if (synerr)
		printk("SYNERR: Thread %d, Synerr: 0x%lx\n", thread, synerr);
	spin(2);
	printk("\n\n");
	clr_lock(&errlock, 1);

	if (errstop_enabled) {
		local_irq_disable();
		while(1);
	}
	return k_stop_on_error;
}


static void
dumpline(void *lp, char *str1, char *str2, void *addr, void *data, int size)
{
	long *p;
	int i, off;

	printk("%s at 0x%lx, size %d, block starts at 0x%lx\n", str1, (long)addr, size, (long)lp);
	p = (long*) data;
	for (i=0; i<48; i++, p++) {
		if (i%8 == 0) printk("%2s", i==16 ? str2 : "  ");
		printk(" %016lx", *p);
		if ((i&7)==7) printk("\n");
	}
	printk("   ");
	off = (((long)addr) ^ size) & 63L;
	for (i=0; i<off+size; i++) {
		printk("%s", (i>=off) ? "--" : "  ");
		if ((i%8) == 7)
			printk(" ");
	}

	off = ((long)addr) & 127;
	printk(" (line %d)\n", 2+off/64+1);
}


static int
randn(uint max, uint *seedp)
{
	if (max == 1)
		return(0);
	else
		return((int)(zrandom(seedp)>>10) % max);
}


static int
checkstop(int thread, int pass, uint lockpat)
{
	long	synerr;

	if (k_go == ST_RUN)
		return 0;
	if (k_go == ST_STOP)
		return 1;

	if (errstop_enabled) {
		local_irq_disable();
		while(1);
	}
	synerr = getsynerr();
	spin(2);
	if (k_go == ST_STOP)
		return 1;
	if (synerr)
		printk("SYNERR: Thread %d, Synerr: 0x%lx\n", thread, synerr);
	return 1;
}


static void
spin(int j)
{
	udelay(j * 500000);
}

static void
capturedata(capture_t *cap, uint pass, void *blockaddr, void *shadaddr, int size)
{

	if (!selective_trigger)
		trigger_la (pass);

	memcpy (cap->blockdata, CACHEALIGN(blockaddr)-128, 3*128);
	if (shadaddr) 
		memcpy (cap->shaddata, CACHEALIGN(shadaddr)-128, 3*128);

	if (k_stop_on_error) {
		k_go = ST_ERRSTOP;
	}

 	cap->size = size;
	cap->blockaddr = blockaddr;
	cap->shadaddr = shadaddr;

	asm volatile ("fc %0" :: "r"(blockaddr) : "memory");
	ia64_sync_i();
	ia64_srlz_d();
	memcpy (cap->blockdata_fc, CACHEALIGN(blockaddr)-128, 3*128);

	if (shadaddr) {
		asm volatile ("fc %0" :: "r"(shadaddr) : "memory");
		ia64_sync_i();
		ia64_srlz_d();
		memcpy (cap->shaddata_fc, CACHEALIGN(shadaddr)-128, 3*128);
	}
}

int             zranmult = 0x48c27395;

static uint  
zrandom (uint *seedp)
{
        *seedp = (*seedp * zranmult) & 0x7fffffff;
        return (*seedp);
}


void
set_autotest_params(void)
{
	static int	testnumber=-1;

	if (llsctest_number >= 0) {
		testnumber = llsctest_number;
	} else {
		testnumber++;
		if (autotest_table[testnumber].passes == 0) {
			testnumber = 0;
			dump_block_addrs_opt = 0;
		}
	}
	if (testnumber == 0 && l4_opt) testnumber = 9;

	k_passes = autotest_table[testnumber].passes;
	k_linepad = autotest_table[testnumber].linepad;
	k_linecount = autotest_table[testnumber].linecount;
	k_testnumber = testnumber;

	if (IS_RUNNING_ON_SIMULATOR()) {
		printk ("llsc start test %ld\n", k_testnumber);
		k_passes = 1000;
	}
}


static void
set_leds(int errs)
{
	unsigned char	leds=0;

	/*
	 * Leds are:
	 * 	ppppeee-  
	 *   where
	 *      pppp = test number
	 *       eee = error count but top bit is stick
	 */

	leds =  ((errs&7)<<1) | ((k_testnumber&15)<<4) | (errs ? 0x08 : 0);
	set_led_bits(leds, LED_MASK_AUTOTEST);
}

static void
setup_block_addresses(void)
{
	int			i, stride, memmapi;
	dataline_t		*dp;
	long			*ip, *ipe;


	stride = k_linepad + sizeof(dataline_t);
	memmapi = 0;
	for (i=0; i<memmapx; i++) {
		memmap[i].nextaddr = memmap[i].vstart;
		memmap[i].nextinit = memmap[i].vstart;
		memmap[i].wrapcount = 0;
	}

	for (i=0; i<k_linecount; i++) {
		blocks[i] = memmap[memmapi].nextaddr;
		dp = (dataline_t*)blocks[i];
		memmap[memmapi].nextaddr += (stride & 0xffff);
		if (memmap[memmapi].nextaddr + sizeof(dataline_t) >= memmap[memmapi].vend) {
			memmap[memmapi].wrapcount++;
			memmap[memmapi].nextaddr = memmap[memmapi].vstart + 
					memmap[memmapi].wrapcount * sizeof(dataline_t);
		}

		ip = (long*)((memmap[memmapi].nextinit+7)&~7);
		ipe = (long*)(memmap[memmapi].nextaddr+2*sizeof(dataline_t)+8);
		while(ip <= ipe && ip < ((long*)memmap[memmapi].vend-8))
			*ip++ = (long)ip;
		memmap[memmapi].nextinit = (long) ipe;
		dp->guard1 = BGUARD(i);
		dp->guard2 = EGUARD(i);
		dp->lock[0] = dp->lock[1] = NOLOCK;
		dp->share[0] = dp->share0 = 0x1111;
		dp->share[1] = dp->share1 = 0x2222;
		memcpy(dp->private, init_private, LLSC_MAXCPUS*sizeof(private_t));


		if (stride > 16384) {
			memmapi++;
			if (memmapi == memmapx)
				memmapi = 0;
		}
	}

}

static void
dump_block_addrs(void)
{
	int	i;

	printk("LLSC TestNumber %ld\n", k_testnumber);

	for (i=0; i<k_linecount; i++) {
		printk("  %lx", blocks[i]);
		if (i%4 == 3)
			printk("\n");
	}
	printk("\n");
}


static void
set_thread_state(int cpuid, int state)
{
	if (k_threadprivate[cpuid]->threadstate == TS_KILLED) {
		set_led_bits(LED_MASK_AUTOTEST, LED_MASK_AUTOTEST);
		while(1);
	}
	k_threadprivate[cpuid]->threadstate = state;
}

#define MINBLK	(16*1024*1024)
static int
build_mem_map(unsigned long start, unsigned long end, void *arg)
{
	long	lstart, lend;
	long	align = 8*MB;

	printk ("LLSC memmap: start 0x%lx, end 0x%lx, (0x%lx - 0x%lx)\n", 
		start, end, (long) virt_to_page(start), (long) virt_to_page(end-PAGE_SIZE));

	if (memmapx >= MAPCHUNKS || (end-start) < MINBLK)
		return 0;

	/*
	 * Start in the middle of the range & find the first non-free page in both directions
	 * from the midpoint. This is likely to be the bigest free block.
	 */
	lend = lstart = start + (end-start)/2;
	while (lend < end && !PageReserved(virt_to_page(lend)) && virt_to_page(lend)->count.counter == 0)
		lend += PAGE_SIZE;
	lend -= PAGE_SIZE;

	while (lstart >= start && !PageReserved(virt_to_page(lstart)) && virt_to_page(lstart)->count.counter == 0)
		lstart -= PAGE_SIZE;
	lstart += PAGE_SIZE;

	lstart = (lstart + align -1) /align * align;
	end = end / align * align;
	if (lstart >= end)
		return 0;
	printk ("     memmap: start 0x%lx, end 0x%lx\n", lstart, end);

	memmap[memmapx].vstart = lstart;
	memmap[memmapx].vend = end;
	memmapx++;
	return 0;
}

void int_test(void);

int
llsc_main (int cpuid)
{
	int		i, cpu, is_master, repeatcnt=0;
	unsigned int	preverr=0, errs=0, pass=0;
	int		automode=0;

#ifdef INTTEST
	if (inttest)
		int_test();
#endif

	if (!autotest_enabled)
		return 0;

#ifdef CONFIG_SMP
	is_master = !smp_processor_id();
#else
	is_master = 1;
#endif


	if (is_master) {
		mbase = (control_t*) __get_free_pages(GFP_KERNEL, get_order(4096+THREADPRIVATESZ()*LLSC_MAXCPUS));
		printk("LLSC: mbase 0x%lx\n", (long)mbase);
		print_params();
		if(!IS_RUNNING_ON_SIMULATOR())
			spin(10);
		k_currentpass = 0;
		k_go = ST_IDLE;
		k_passes = DEF_PASSES;
		k_napticks = DEF_NAPTICKS;
		k_stop_on_error = DEF_STOP_ON_ERROR;
		k_verbose = DEF_VERBOSE;
		k_linecount = DEF_LINECOUNT;
		k_iter_msg = DEF_ITER_MSG;
		k_vv = DEF_VV;
		k_linepad = DEF_LINEPAD;
		k_blocks = (void*)blocks;
		efi_memmap_walk(build_mem_map, 0);
	
#ifdef CONFIG_IA64_SGI_AUTOTEST
		automode = 1;
#endif

		for (i=0; i<LLSC_MAXCPUS; i++) {
			k_threadprivate[i] = THREADPRIVATE(i);
			memset(k_threadprivate[i], 0, sizeof(*k_threadprivate[i]));
			init_private[i] = i;
		}
		mb();
		initialized = 1;
	} else {
		while (initialized == 0)
			udelay(100);
	}

loop:
	if (is_master) {
		if (automode) {
			if (!preverr || repeatcnt++ > 5) {
				set_autotest_params();
				repeatcnt = 0;
			}
		} else {
			while (k_go == ST_IDLE);
		}

		k_go = ST_INIT;
		if (k_linecount > MAX_LINECOUNT) k_linecount = MAX_LINECOUNT;
		k_linecount = k_linecount & ~1;
		setup_block_addresses();
		if (!preverr && dump_block_addrs_opt)
			dump_block_addrs();

		k_currentpass = pass++;
		k_go = ST_RUN;
		if (fail_enabled)
			fail_enabled--;

	} else {
		while (k_go != ST_RUN || k_currentpass != pass);
		pass++;
	}


	set_leds(errs);
	set_thread_state(cpuid, TS_RUNNING);

	errs += ran_conf_llsc(cpuid);
	preverr = (k_go == ST_ERRSTOP);

	set_leds(errs);
	set_thread_state(cpuid, TS_STOPPED);

	if (is_master) {
		Speedo();
		for (i=0, cpu=0; cpu<LLSC_MAXCPUS; cpu++) {
			while (k_threadprivate[cpu]->threadstate == TS_RUNNING) {
				i++;
				if (i == 10000) { 
					k_go = ST_STOP;
					printk ("  llsc master stopping test number %ld\n", k_testnumber);
				}
				if (i > 100000) {
					k_threadprivate[cpu]->threadstate = TS_KILLED;
					printk ("  llsc: master killing cpuid %d, running test number %ld\n", 
							cpu, k_testnumber);
				}
				udelay(1000);
			}
		}
	}

	goto loop;
}


static void
Speedo(void)
{
	static int i = 0;

	switch (++i%4) {
	case 0:
		printk("|\b");
		break;
	case 1:
		printk("\\\b");
		break;
	case 2:
		printk("-\b");
		break;
	case 3:
		printk("/\b");
		break;
	}
}

#ifdef INTTEST

/* ======================================================================================================== 
 *
 * Some test code to verify that interrupts work
 *
 * Add the following to the arch/ia64/kernel/smp.c after the comment "Reschedule callback"
 * 		if (zzzprint_resched) printk("  cpu %d got interrupt\n", smp_processor_id());
 *
 * Enable the code in arch/ia64/sn/sn1/smp.c to print sending IPIs.
 *
 */

static int __init set_inttest(char *str)
{
        inttest = 1;
	autotest_enabled = 1;

	return 1;
}	

__setup("inttest=", set_inttest);

int	zzzprint_resched=0;

void
int_test() {
	int			mycpu, cpu;
	static volatile int	control_cpu=0;

	mycpu = smp_processor_id();
	zzzprint_resched = 2;

	printk("Testing cross interrupts\n");
	
	while (control_cpu != smp_num_cpus) {
		if (mycpu == cpu_logical_map(control_cpu)) {
			for (cpu=0; cpu<smp_num_cpus; cpu++) {
				printk("Sending interrupt from %d to %d\n", mycpu, cpu_logical_map(cpu));
				udelay(IS_RUNNING_ON_SIMULATOR ? 10000 : 400000);
				smp_send_reschedule(cpu_logical_map(cpu));
				udelay(IS_RUNNING_ON_SIMULATOR ? 10000 : 400000);
				smp_send_reschedule(cpu_logical_map(cpu));
				udelay(IS_RUNNING_ON_SIMULATOR ? 10000 : 400000);
			}
			control_cpu++;
		}
	}

	zzzprint_resched = 1;

	if (mycpu == cpu_logical_map(smp_num_cpus-1)) {
		printk("\nTight loop of cpu %d sending ints to cpu 0 (every 100 us)\n", mycpu);
		udelay(IS_RUNNING_ON_SIMULATOR ? 1000 : 1000000);
		__cli();
		while (1) {
			smp_send_reschedule(0);
			udelay(100);
		}

	}

	while(1);
}
#endif
