/*	$NetBSD: disptest.c,v 1.6 2009/03/18 10:22:29 cegger Exp $	*/

/*-
 * Copyright (c) 1999 Shin Takemura.
 * All rights reserved.
 *
 * This software is part of the PocketBSD.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the PocketBSD project
 *	and its contributors.
 * 4. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
#include <pbsdboot.h>

extern BOOL SetKMode(BOOL);
#define ARRAYSIZEOF(a)	(sizeof(a)/sizeof(*(a)))

static struct area {
  long start, end;
} targets[] = {
	{ 0x0a000000, 0x0b000000 },
	{ 0x10000000, 0x14000000 },
};
int ntargets = ARRAYSIZEOF(targets);

void
flush_XX(void)
{
  static volatile unsigned char tmp[1024*64];
  int i, s;

  for (i = 0; i < ARRAYSIZEOF(tmp); i++) {
	  s += tmp[i];
  }
}

static void
gpio_test(void)
{
#define GIUBASE 0xab000000
#define GIUOFFSET 0x0100
	volatile unsigned short *giusell;
	volatile unsigned short *giuselh;
	volatile unsigned short *giupiodl;
	volatile unsigned short *giupiodh;
	unsigned short sell = 0;
	unsigned short selh = 0;
	unsigned short piodl = 0;
	unsigned short piodh = 0;
	int res, i;
	unsigned short regs[16];
	unsigned short prev_regs[16];

	unsigned char* p = (char*)VirtualAlloc(0, 1024, MEM_RESERVE,
				      PAGE_NOACCESS);
	res = VirtualCopy((LPVOID)p, (LPVOID)(GIUBASE >> 8), 1024,
			  PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);
	if (!res) {
		win_printf(TEXT("VirtualCopy() failed."));
	}

	for (i = 0; i < 16; i++) {
		prev_regs[i] = ~0;
	}

	giusell = (unsigned short*)(p + GIUOFFSET + 0);
	giuselh = (unsigned short*)(p + GIUOFFSET + 2);
	giupiodl = (unsigned short*)(p + GIUOFFSET + 4);
	giupiodh = (unsigned short*)(p + GIUOFFSET + 6);

	while (1) {
		sell = *giusell;
		selh = *giuselh;
		*giusell = sell;
		*giuselh = selh;

		piodl = *giupiodl;
		piodh = *giupiodh;
		*giupiodl = piodl;
		*giupiodh = piodh;
		for (i = 0; i < 16; i++) {
			regs[i] = *(unsigned short*)(p + GIUOFFSET + i * 2);
		}
		for (i = 0; i < 16; i++) {
			if (i != 3 && i != 5 && regs[i] != prev_regs[i]) {
				win_printf(TEXT("IOSEL=%04x%04x "),
					   regs[1], regs[0]);
				win_printf(TEXT("PIOD=%04x%04x "),
					   regs[3], regs[2]);
				win_printf(TEXT("STAT=%04x%04x "),
					   regs[5], regs[4]);
				win_printf(TEXT("(%04x%04x) "),
					   regs[5]&regs[7], regs[4]&regs[6]);
				win_printf(TEXT("EN=%04x%04x "),
					   regs[7], regs[6]);
				win_printf(TEXT("TYP=%04x%04x "),
					   regs[9], regs[8]);
				win_printf(TEXT("ALSEL=%04x%04x "),
					   regs[11], regs[10]);
				win_printf(TEXT("HTSEL=%04x%04x "),
					   regs[13], regs[12]);
				win_printf(TEXT("PODAT=%04x%04x "),
					   regs[15], regs[14]);
				win_printf(TEXT("\n"));
				for (i = 0; i < 16; i++) {
					prev_regs[i] = regs[i];
				}
				break;
			}
		}
	}
}

static struct regdesc {
	TCHAR *name;
	int physaddr;
	int size;
	int mask;
	//void *addr;
	unsigned long val;
	unsigned long preval;
} test_regs[] = {
#if 0
	/*
	 * Vrc4172 GPIO and PWM
	 */
	{ TEXT("EXGPDATA0"),	0x15001080,	2,	0xfffd		},
	{ TEXT("EXGPDATA1"),	0x150010c0,	2,	0xffff		},
	{ TEXT("LCDDUTYEN"),	0x15003880,	2,	0xffff		},
	{ TEXT("LCDFREQ"),	0x15003882,	2,	0xffff		},
	{ TEXT("LCDDUTY"),	0x15003884,	2,	0xffff		},
#endif

#if 0
	/*
	 * Vr41xx GPIO
	 */
	{ TEXT("GIUPIODL"),	0x0b000104,	2,	0xffff		},
	{ TEXT("GIUPIODH"),	0x0b000106,	2,	0xffff		},
	{ TEXT("GIUPODATL"),	0x0b00011c,	2,	0xffff		},
	{ TEXT("GIUPODATH"),	0x0b00011e,	2,	0xffff		},
	{ TEXT("GIUUSEUPDN"),	0x0b0002e0,	2,	0xffff		},
	{ TEXT("GIUTERMUPDN"),	0x0b0002e2,	2,	0xffff		},
#endif

	/*
	 * MQ200
	 */
	{ TEXT("PM00R"),	0x0a600000,	4,	0xffffffff	},
	{ TEXT("PM01R"),	0x0a600004,	4,	0xffffffff	},
	{ TEXT("PM02R"),	0x0a600008,	4,	0xffffffff	},
	{ TEXT("PM06R"),	0x0a600018,	4,	0xffffffff	},
	{ TEXT("PM07R"),	0x0a60001c,	4,	0xffffffff	},

	{ TEXT("CC00R"),	0x0a602000,	4,	0x0000003f	},
	{ TEXT("CC01R"),	0x0a602004,	4,	0x00000000	},

	{ TEXT("MM00R"),	0x0a604000,	4,	0x00000007	},
	{ TEXT("MM01R"),	0x0a604004,	4,	0xffffffff	},
	{ TEXT("MM02R"),	0x0a604008,	4,	0xffffffff	},
	{ TEXT("MM03R"),	0x0a60400c,	4,	0x00000001	},
	{ TEXT("MM04R"),	0x0a604010,	4,	0x00000001	},

	{ TEXT("IN00R"),	0x0a608000,	4,	0x0000001f	},
	{ TEXT("IN01R"),	0x0a608004,	4,	0x0000ffff	},
	{ TEXT("IN02R"),	0x0a608008,	4,	0x00000000	},
	{ TEXT("IN03R"),	0x0a60800c,	4,	0x00000000	},

	{ TEXT("GC00R"),	0x0a60a000,	4,	0xfffff9ff	},
	{ TEXT("GC01R"),	0x0a60a004,	4,	0x10ffffff	},
	{ TEXT("GC20R"),	0x0a60a080,	4,	0xffffffff	},
	{ TEXT("GC21R"),	0x0a60a084,	4,	0x0000007f	},

	{ TEXT("FP00R"),	0x0a60e000,	4,	0xffffffff	},
	{ TEXT("FP01R"),	0x0a60e004,	4,	0xffffffff	},
	{ TEXT("FP02R"),	0x0a60e008,	4,	0x007fffff	},
	{ TEXT("FP03R"),	0x0a60e00c,	4,	0x0707003f	},
	{ TEXT("FP04R"),	0x0a60e010,	4,	0xffff3fff	},
	{ TEXT("FP05R"),	0x0a60e014,	4,	0xffffffff	},
	{ TEXT("FP0FR"),	0x0a60e03c,	4,	0xffffffff	},

	{ TEXT("DC00R"),	0x0a614000,	4,	0xffffffff	},
	{ TEXT("DC01R"),	0x0a614004,	4,	0x0000003f	},
	{ TEXT("DC02R"),	0x0a614008,	4,	0xffffffff	},
	{ TEXT("DC03R"),	0x0a61400c,	4,	0xffffffff	},

	{ TEXT("PC00R"),	0x0a616000,	4,	0xffffffff	},
	{ TEXT("PC04R"),	0x0a616004,	4,	0xffffffff	},
	{ TEXT("PC08R"),	0x0a616008,	4,	0xffffffff	},
	{ TEXT("PC0CR"),	0x0a61600c,	4,	0xffffffff	},
	{ TEXT("PC10R"),	0x0a616010,	4,	0xffffffff	},
	{ TEXT("PC14R"),	0x0a616014,	4,	0xffffffff	},
	{ TEXT("PC2CR"),	0x0a61602c,	4,	0xffffffff	},
	{ TEXT("PC3CR"),	0x0a61603c,	4,	0xffffffff	},
	{ TEXT("PC40R"),	0x0a616040,	4,	0xffffffff	},
	{ TEXT("PC44R"),	0x0a616044,	4,	0x00000003	},
};

extern int SetKMode(int);
static void
regfetch(struct regdesc* desc)
{
	SetKMode(1);
	switch (desc->size) {
	case 1:
		desc->val = *(unsigned char*)(desc->physaddr | 0xa0000000);
		break;
	case 2:
		desc->val = *(unsigned short*)(desc->physaddr | 0xa0000000);
		break;
	case 4:
		desc->val = *(unsigned long*)(desc->physaddr | 0xa0000000);
		break;
	default:
		win_printf(TEXT("Invalid size"));
		break;
	}
	SetKMode(0);
	desc->val &= desc->mask;
}

static void
register_test(void)
{
    int i;
    int nregs = sizeof(test_regs)/sizeof(*test_regs);

    for (i = 0; i < nregs; i++) {
	regfetch(&test_regs[i]);
	test_regs[i].preval = test_regs[i].val;
    }

    while (1) {
	for (i = 0; i < nregs; i++) {
	    regfetch(&test_regs[i]);
	    if (test_regs[i].val != test_regs[i].preval) {
		win_printf(TEXT("%20s(%08x) %08x -> %08x\n"),
		    test_regs[i].name,
		    test_regs[i].physaddr,
		    test_regs[i].preval,
		    test_regs[i].val);
		test_regs[i].preval = test_regs[i].val;
	    }
	}
	Sleep(10); /* 10 msec */
    }
}

static void
dump_memory(void)
{
	HANDLE fh = INVALID_HANDLE_VALUE;
#define UNICODE_MEMORY_CARD \
	TEXT('\\'), 0xff92, 0xff93, 0xff98, TEXT(' '), 0xff76, 0xff70, \
	0xff84, 0xff9e
	TCHAR filename[] = { UNICODE_MEMORY_CARD,  TEXT('2'), TEXT('\\'),
			     TEXT('d'), TEXT('u'),  TEXT('m'), TEXT('p'), 0 };
	unsigned long *addr;
	int found;

	win_printf(TEXT("dump to %s\n"), filename);
	fh = CreateFile(
		filename,      	/* file name */
		GENERIC_WRITE,	/* access (read-write) mode */
		FILE_SHARE_WRITE,/* share mode */
		NULL,		/* pointer to security attributes */
		CREATE_ALWAYS,	/* how to create */
		FILE_ATTRIBUTE_NORMAL,	/* file attributes*/
		NULL		/* handle to file with attributes to */
	    );
	if (fh == INVALID_HANDLE_VALUE) {
		return;
	}

	for (addr = (unsigned long*)0xbe000000;
	    addr < (unsigned long*)0xbfffffff;
	    addr += 2048) {
		char buf[2048];
		DWORD n;

		SetKMode(1);
		memcpy(buf, addr, 2048);
		SetKMode(0);
		if (WriteFile(fh, buf, 2048, &n, NULL) == 0 ||
		    n != 2048) {
			win_printf(TEXT("dump failed\n"));
			break;
		}
	}

	CloseHandle(fh);
}

static void
serial_test(void)
{
#if 1
#  define SIUADDR 0xac000000
#  define REGOFFSET 0x0
#else
#  define SIUADDR 0xab000000
#  define REGOFFSET 0x1a0
#endif
#define REGSIZE 32
	int i, changed, res;
	unsigned char regs[REGSIZE], prev_regs[REGSIZE];
	unsigned char* p = (char*)VirtualAlloc(0, 1024, MEM_RESERVE,
				      PAGE_NOACCESS);
	
	for (i = 0; i < ARRAYSIZEOF(prev_regs); i++) {
		prev_regs[i] = ~0;
	}

	res = VirtualCopy((LPVOID)p, (LPVOID)(SIUADDR >> 8), 1024,
			  PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);
	if (!res) {
		win_printf(TEXT("VirtualCopy() failed."));
	}

	while (1) {
		flush_XX();

		for (i = 0; i < ARRAYSIZEOF(regs); i++) {
			regs[i] = p[REGOFFSET + i];
		}

		changed = 0;
		for (i = 0; i < ARRAYSIZEOF(regs); i++) {
			if (regs[i] != prev_regs[i]) {
				changed++;
			}
			prev_regs[i] = regs[i];
		}
		if (changed) {
			win_printf(TEXT("SIU regs: "));
			for (i = 0; i < ARRAYSIZEOF(regs); i++) {
				win_printf(TEXT("%02x "), regs[i]);
			}
			win_printf(TEXT("\n"));
		}
	}

	VirtualFree(p, 0, MEM_RELEASE);
}

static long
checksum(char* addr, int size)
{
	long sum = 0;
	int i;

	for (i = 0; i < size; i++) {
		sum += *addr++ * i;
	}
	return (sum);
}

static int
examine(char* addr, int size)
{
	long random_data[256];
	long dijest;
	int i;

	for (i = 0; i < ARRAYSIZEOF(random_data); i++) {
		random_data[i] = Random();
	}
	if (sizeof(random_data) < size) {
		size = sizeof(random_data);
	}
	memcpy(addr, (char*)random_data, size);
	dijest= checksum((char*)random_data, size);

	return (dijest == checksum(addr, size));
}

void
display_search(void)
{
	int step = 0x10000;
	int i;
	long addr;

	for (i = 0; i < ntargets; i++) {
		int prevres = -1;
		for (addr = targets[i].start;
		     addr < targets[i].end;
		     addr += step) {
			int res;
#if 0
			char* p = (char*)VirtualAlloc(0, step, MEM_RESERVE,
						      PAGE_NOACCESS);
			res = VirtualCopy((LPVOID)p, (LPVOID)(addr >> 8), step,
				   PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);
			if (!res) {
				win_printf(TEXT("VirtualCopy() failed."));
			}
			res = examine(p, step);
			VirtualFree(p, 0, MEM_RELEASE);
#else
			SetKMode(1);
			res = examine((char*)((int)addr | 0xa0000000), step);
			SetKMode(0);
#endif
			if (res != prevres && prevres != -1) {
				if (res) {
					win_printf(TEXT("0x%x "), addr);
				} else {
					win_printf(TEXT("- 0x%x\n"), addr);
				}
			} else
			if (res && prevres == -1) {
				win_printf(TEXT("0x%x "), addr);
			}
			prevres = res;
		}
		if (prevres) {
			win_printf(TEXT("\n"));
		}
	}
}

void
display_draw(void)
{
	long addr = 0x13000000;
	int size = 0x80000;
	char* p;
	int i, j, res;
	int x, y;
	int stride = 1280;

	p = (char*)VirtualAlloc(0, size, MEM_RESERVE,
				PAGE_NOACCESS);
	res = VirtualCopy((LPVOID)p, (LPVOID)(addr >> 8), size,
			  PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);
	if (!res) {
		win_printf(TEXT("VirtualCopy() failed."));
	}

	for (i = 0; i < 10000; i++) {
		p[i] = i;
	}
	for (x = 0; x < 640; x += 10) {
		for (y = 0; y < 240; y += 1) {
		        p[stride * y + x] = (char)0xff;
		}
	}
	for (y = 0; y < 240; y += 10) {
		for (x = 0; x < 640; x += 1) {
		        p[stride * y + x] = (char)0xff;
		}
	}
	for (i = 0; i < 16; i++) {
		for (j = 0; j < 16; j++) {
			for (x = i * 32; x < i * 32 + 32; x++) {
				for (y = j * 15; y < j * 15 + 15; y++) {
					p[stride * y + x] = j * 16 + i;
				}
			}
		}
	}

	VirtualFree(p, 0, MEM_RELEASE);
}

#define PCIC_IDENT		0x00
#define	PCIC_REG_INDEX		0
#define	PCIC_REG_DATA		1
#define PCIC_IDENT_EXPECTED	0x83

void
pcic_search(void)
{
	long addr;
	int window_size = 0x10000;
	int i;

	for (addr = 0x14000000; addr < 0x18000000; addr += window_size) {
		int res;
		unsigned char* p;
		p = (char*)VirtualAlloc(0, window_size, MEM_RESERVE,
					PAGE_NOACCESS);
		res = VirtualCopy((LPVOID)p, (LPVOID)(addr >> 8), window_size,
				  PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);
		if (!res) {
			win_printf(TEXT("VirtualCopy() failed."));
		}

		for (i = 0; i < window_size; i += 2) {
			p[i + PCIC_REG_INDEX] = PCIC_IDENT;
			if (p[i + PCIC_REG_DATA] == PCIC_IDENT_EXPECTED) {
				win_printf(TEXT("pcic is found at 0x%x\n"),
					   addr + i);
			}
		}

		VirtualFree(p, 0, MEM_RELEASE);
	}
}

#define VRPCIU_CONFA	(*(u_int32_t*)0xaf000c18)
#define VRPCIU_CONFD	(*(u_int32_t*)0xaf000c14)

void
pci_dump(void)
{
	int mode, i;
	BOOL SetKMode(BOOL);
	int bus, dev;
	u_int32_t addr, val;
	u_int32_t addrs[] = {
		0x00000800,
		0x00001000,
		0x00002000,
		0x00004000,
		0x00008000,
		0x00010000,
		0x00020000,
		0x00040000,
		0x00080000,
		0x00100000,
		0x00200000,
		0x00400000,
		0x00800000,
		0x01000000,
		0x02000000,
		0x04000000,
		0x08000000,
		0x10000000,
		0x20000000,
		0x40000000,
		0x80000000,
	};

#if 0 /* You can find Vrc4173 BCU at 0xb6010000 on Sigmarion II */
	win_printf(TEXT("Vrc4173 CMUCLKMSK:	%04X\n"),
	    *(u_int16_t*)0xb6010040);
	win_printf(TEXT("Vrc4173 CMUSRST:	%04X\n"),
	    *(u_int16_t*)0xb6010042);

	/* enable CARDU clock */
	*(u_int16_t*)0xb6010042 = 0x0006; /* enable CARD1RST and CARD2RST */
	*(u_int16_t*)0xb6010040 = *(u_int16_t*)0xb6010040 | 0x00c0;
	*(u_int16_t*)0xb6010042 = 0x0000; /* disable CARD1RST and CARD2RST */

	win_printf(TEXT("Vrc4173 CMUCLKMSK:	%04X\n"),
	    *(u_int16_t*)0xb6010040);
	win_printf(TEXT("Vrc4173 CMUSRST:	%04X\n"),
	    *(u_int16_t*)0xb6010042);
#endif

	for (i = 0; i < sizeof(addrs)/sizeof(*addrs); i++) {
		VRPCIU_CONFA = addrs[i];
		val = VRPCIU_CONFD;
		win_printf(TEXT("%2d:	%08X	%04X %04X\n"),
		    i, addrs[i], val & 0xffff, (val >> 16) & 0xffff);
	}

	mode = SetKMode(1);
	SetKMode(mode);
}

void
hardware_test(void)
{
	int do_gpio_test = 0;
	int do_register_test = 0;
	int do_serial_test = 0;
	int do_display_draw = 0;
	int do_display_search = 0;
	int do_pcic_search = 0;
	int do_dump_memory = 0;
	int do_pci_dump = 0;

	if (do_gpio_test) {
		gpio_test();
	}
	if (do_register_test) {
		register_test();
	}
	if (do_serial_test) {
		serial_test();
	}
	if (do_display_draw) {
		display_draw();
	}
	if (do_display_search) {
		display_search();
	}
	if (do_pcic_search) {
		pcic_search();
	}
	if (do_dump_memory) {
		dump_memory();
	}
	if (do_pci_dump) {
		pci_dump();
	}
}
