#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <asm/io.h>

struct atm_dev { int dummy; };
struct zatm_dev { int dummy; };
 
#include "/usr/src/linux/drivers/atm/uPD98401.h"
#include "/usr/src/linux/drivers/atm/uPD98402.h"

#define MEM_DEV "/dev/mem"
#define PORT_BASE 0xf800
 


#define zin(r) inl_p(PORT_BASE+uPD98401_##r*4)
#define zin_n(r) inl_p(PORT_BASE+(r)*4)
#define zout(v,r) outl_p(v,PORT_BASE+uPD98401_##r*4)
#define zwait while (zin(CMR) & uPD98401_BUSY)
 

int mem;


static void zpokel(unsigned long value,unsigned long addr)
{
	zwait;
	zout(value,CER);
	zout(uPD98401_IND_ACC | uPD98401_IA_BALL |
	    (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
}
 
 
static unsigned long zpeekl(unsigned long addr)
{
	zwait;
	zout(uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |
	    (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
	zwait;
	return zin(CER);
}


struct descr {
    const char *name;
    int shift;	/* -1 = flag */
    unsigned long mask; /* 0 = label */
    const char *table;
};

struct descr direct_regs[] = {
    { "General Mode",		0,			0,			NULL },
    { "Control Mem Parity",	-1,			uPD98401_GMR_CPE,	NULL },
    { "Loopback",		-1,			uPD98401_GMR_LP,	NULL },
    { "Early Write Abort",	-1,			uPD98401_GMR_WA,	NULL },
    { "Early Read Abort",	-1,			uPD98401_GMR_RA,	NULL },
    { "Burst size 16 word",	-1,			uPD98401_BURST16,	NULL },
    { "Burst size 8 word",	-1,			uPD98401_BURST8,	NULL },
    { "Burst size 4 word",	-1,			uPD98401_BURST4,	NULL },
    { "Burst size 2 word",	-1,			uPD98401_BURST2,	NULL },
    { "Address (burst) Disable",-1,			uPD98401_GMR_AD,	NULL },
    { "Byte order",		-1,			uPD98401_GMR_BO,	"little\0big" },
    { "Bus Parity Mode",	-1,			uPD98401_GMR_PM,	"byte\0word" },
    { "Bus Parity Control",	-1,			uPD98401_GMR_PC,	"even\0odd" },
    { "Bus Parity Enable",	-1,			uPD98401_GMR_BPE,	NULL },
    { "Receive Drop Mode",	-1,			uPD98401_GMR_DR,	"drop\0handshake" },
    { "Shapers Enable",		-1,			uPD98401_GMR_SE,	NULL },
    { "Receiver Enable",	-1,			uPD98401_GMR_RE,	NULL },
    { "General Status",		0,			0,			NULL },
    { "PHY Interrupt",		-1,			uPD98401_INT_PI,	"no\0SET" },
    { "Receive Queue Alert",	-1,			uPD98401_INT_RQA,	"no\0SET" },
    { "Receive Queue Underrun",	-1,			uPD98401_INT_RQU,	"no\0SET" },
    { "Receiver Deactivated",	-1,			uPD98401_INT_RD,	"no\0SET" },
    { "System Parity Error",	-1,			uPD98401_INT_SPE,	"no\0SET" },
    { "Control Mem Parity Error",-1,			uPD98401_INT_CPE,	"no\0SET" },
    { "System Bus Error",	-1,			uPD98401_INT_SBE,	"no\0SET" },
    { "Initialization done",	-1,			uPD98401_INT_IND,	"no\0SET" },
    { "Raw Cell Received",	uPD98401_INT_RCR_SHIFT,	uPD98401_INT_RCR,	NULL },
    { "Mailbox Full",		uPD98401_INT_MF_SHIFT,	uPD98401_INT_MF,	NULL },
    { "Mailbox Modified",	0,			uPD98401_INT_MM,	NULL },
    { "Interrupt Mask",		0,			0,			NULL },
    { "PHY Interrupt",		-1,			uPD98401_INT_PI,	"disabled\0enabled" },
    { "Receive Queue Alert",	-1,			uPD98401_INT_RQA,	"disabled\0enabled" },
    { "Receive Queue Underrun",	-1,			uPD98401_INT_RQU,	"disabled\0enabled" },
    { "Receiver Deactivated",	-1,			uPD98401_INT_RD,	"disabled\0enabled" },
    { "System Parity Error",	-1,			uPD98401_INT_SPE,	"disabled\0enabled" },
    { "Control Mem Parity Error",-1,			uPD98401_INT_CPE,	"disabled\0enabled" },
    { "System Bus Error",	-1,			uPD98401_INT_SBE,	"disabled\0enabled" },
    { "Initialization done",	-1,			uPD98401_INT_IND,	"disabled\0enabled" },
    { "Raw Cell Received",	uPD98401_INT_RCR_SHIFT,	uPD98401_INT_RCR,	NULL },
    { "Mailbox Full",		uPD98401_INT_MF_SHIFT,	uPD98401_INT_MF,	NULL },
    { "Mailbox Modified",	0,			uPD98401_INT_MM,	NULL },
    { "",			0,			0,			NULL },
    { "RX Queue Underrun",	0,			0xffffffff,		NULL },
    { "",			0,			0,			NULL },
    { "RX Queue Alert",		0,			0xffffffff,		NULL },
    { "",			0,			0,			NULL },
    { "Burst Address",		0,			0xffffffff,		NULL },
    { "Version Number",		0,			0,			NULL },
    { "Major",			uPD98401_MAJOR_SHIFT,	uPD98401_MAJOR,		NULL },
    { "Minor",			0,			uPD98401_MINOR,		NULL },
    { "Command Register",	2,			0,			NULL },
    { "Busy",			-1,			uPD98401_BUSY,		"idle\0busy" },
    { "Locked",			-1,			uPD98401_LOCKED,	"ready\0locked" },
    { "",			2,			0,			NULL },
    { "CER",			0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr mbx[] = {
    { ">",			0,			0,			NULL },
    { "MBX start high",		0,			0x0000ffff,		NULL },
    { ">",			4,			0,			NULL },
    { "MBX start low",		0,			0x0000ffff,		NULL },
    { ">",			4,			0,			NULL },
    { "MBX bottom",		0,			0x0000ffff,		NULL },
    { ">",			4,			0,			NULL },
    { "MBX tail",		0,			0x0000ffff,		NULL },
    { ">",			4,			0,			NULL },
    { "MBX write",		0,			0x0000ffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr indirect_regs[] = {
    { "Top of stack",		0,			0x0003ffff,		NULL },
    { "",			0x100,			0,			NULL },
    { "Shapers CM start",	0,			0x0003ffff,		NULL },
    { "",			0,			0,			NULL },
    { "RX Pools CM start",	0,			0x0003ffff,		NULL },
    { "",			0xff,			0,			NULL },
    { "T1",			0,			0x0000ffff,		NULL },
    { "VPI/VCI Reduction",	0,			0,			NULL },
    { "Shutdown",		0,			uPD98401_VRR_SDM,	NULL },
    { "VPI/VCI Shift",		uPD98401_VRR_SHIFT_SHIFT,uPD98401_VRR_SHIFT,	NULL },
    { "VPI/VCI mask",		0,			uPD98401_VRR_MASK,	NULL },
    { "",			0,			0,			NULL },
    { "TSR (free)",		0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr scheduler[] = {
    { ">",			0,			0,			NULL },
    { "I cells in ...",		uPD98401_IM_I_SHIFT,	uPD98401_IM_I,		NULL },
    { "M cell periods",		0,			uPD98401_IM_M,		NULL },
    { ">",			16,			0,			NULL },
    { "x",			0,			0xffffffff,		NULL },
    { ">",			16,			0,			NULL },
    { "y",			0,			0xffffffff,		NULL },
    { ">",			16,			0,			NULL },
    { "P (minimum gap)",	uPD98401_PC_P_SHIFT,	uPD98401_PC_P,		NULL },
    { "C (shaper's bucket)",	uPD98401_PC_C_SHIFT,	uPD98401_PC_C,		NULL },
    { "p",			uPD98401_PC_p_SHIFT,	uPD98401_PC_p,		NULL },
    { "c",			0,			uPD98401_PC_c,		NULL },
    { ">",			16,			0,			NULL },
    { "Priority",		uPD98401_PS_PRIO_SHIFT,	uPD98401_PS_PRIO,	NULL },
    { "Scan",			-1,			uPD98401_PS_S,		NULL },
    { "Round Robin",		-1,			uPD98401_PS_R,		NULL },
    { "Active",			-1,			uPD98401_PS_A,		NULL },
    { "Enable",			-1,			uPD98401_PS_E,		NULL },
};

struct descr tx_table[] = {
    { ">",			0,			0,			NULL },
    { "CLP mode",		uPD98401_TXPD_CLPM_SHIFT,uPD98401_TXPD_CLPM,	"CLP=0\0last\0???\0CLP=1" },
    { "PTI pattern",		uPD98401_TXPD_PTI_SHIFT,uPD98401_TXPD_PTI,	NULL },
    { "GFC pattern",		uPD98401_TXPD_GFC_SHIFT,uPD98401_TXPD_GFC,	NULL },
    { "CRC10",			-1,			uPD98401_TXPD_C10,	"off\0on" },
    { "AAL5",			-1,			uPD98401_TXPD_AAL5,	"off\0on" },
    { "Mailbox",		-1,			uPD98401_TXPD_MB,	"#2\0#3" },
    { "CPCS-UU",		uPD98401_TXPD_UU_SHIFT,	uPD98401_TXPD_UU,	NULL },
    { "CPI",			0,			uPD98401_TXPD_CPI,	NULL },
    { ">",			0,			0,			NULL },
    { "Last",			-1,			uPD98401_TXVC_L,	"no\0yes" },
    { "Shaper",			uPD98401_TXVC_SHP_SHIFT,uPD98401_TXVC_SHP,	NULL },
    { "VPI",			uPD98401_TXVC_VPI_SHIFT,uPD98401_TXVC_VPI,	NULL },
    { "VCI",			0,			uPD98401_TXVC_VCI,	NULL },
    { ">",			0,			0,			NULL },
    { "Transmitted",		16,			0xffff,			NULL },
    { "Remaining",		0,			0xffff,			NULL },
    { ">",			0,			0,			NULL },
    { "CRC-32",			0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "Buffer read",		0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "Next buffer",		0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "TX queue read ptr",	0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "Active",			-1,			0x80000000,		NULL },
    { "Backward pointer",	16,			0x7fff,			NULL },
    { "Last",			-1,			0x8000,			NULL },
    { "Forward pointer",	0,			0x7fff,			NULL },
    { NULL,			0,			0,			NULL }
};

struct descr rx_table[] = {
    { ">",			0,			0,			NULL },
    { "Batch size",		uPD98401_RXVC_BTSZ_SHIFT,uPD98401_RXVC_BTSZ,	NULL },
    { "Mailbox",		-1,			uPD98401_RXVC_MB,	"#0\0#1" },
    { "Pool",			uPD98401_RXVC_POOL_SHIFT,uPD98401_RXVC_POOL,	NULL },
    { "UInfo",			0,			uPD98401_RXVC_UINFO,	NULL },
    { ">",			0,			0,			NULL },
    { "T1",			uPD98401_RXVC_T1_SHIFT,	uPD98401_RXVC_T1,	NULL },
    { "Reception",		-1,			uPD98401_RXVC_PR,	"idle\0busy" },
    { "FIFO drop",		-1,			uPD98401_RXVC_DR,	"normal\0dropping" },
    { "OAM drop",		-1,			uPD98401_RXVC_OD,	NULL },
    { "AAAL5/Raw",		-1,			uPD98401_RXVC_AR,	"raw\0AAL5" },
    { "Max. segments",		0,			uPD98401_RXVC_MAXSEG,	NULL },
    { ">",			0,			0,			NULL },
    { "Remaining words",	uPD98401_RXVC_REM_SHIFT,uPD98401_RXVC_REM,	NULL },
    { "CLP 1 received",		-1,			uPD98401_RXVC_CLP,	NULL },
    { "Buffer assigned",	-1,			uPD98401_RXVC_BFA,	NULL },
    { "Batch assigned",		-1,			uPD98401_RXVC_BTA,	NULL },
    { "Congestion indication",	-1,			uPD98401_RXVC_CI,	NULL },
    { "Free pool empty",	-1,			uPD98401_RXVC_DD,	NULL },
    { "PDU in progress",	-1,			uPD98401_RXVC_DP,	NULL },
    { "Segment count",		0,			uPD98401_RXVC_CURSEG,	NULL },
    { ">",			0,			0,			NULL },
    { "CRC32",			0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "Buffer write address",	0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "Current buffer ptr",	0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "Packet start address",	0,			0xffffffff,		NULL },
    { ">",			0,			0,			NULL },
    { "Backward pointer",	16,			0x7fff,			NULL },
    { "Last",			-1,			0x8000,			NULL },
    { "Forward pointer",	0,			0x7fff,			NULL },
    { NULL,			0,			0,			NULL }
};


static unsigned long get_direct(unsigned long base,int index)
{
	return zin_n(base+index);
}


static unsigned long get_indirect(unsigned long base,int index)
{
	return zpeekl(base+index);
}


static unsigned long get_cm(unsigned long base,int index)
{
	return 0;
}


static unsigned long peek_mem(unsigned long base,int index)
{
    unsigned long buf;

    if (lseek(mem,base+4*index,SEEK_SET) < 0) {
	perror("lseek");
	exit(1);
    }
    if (read(mem,&buf,sizeof(unsigned long)) != sizeof(unsigned long)) {
	perror("read");
	exit(1);
    }
    return buf;
}


static void process(unsigned long base,struct descr *list,unsigned long (*pick)(unsigned long base,int index))
{
    int index,fetch,indent;
    unsigned long value,eff;
    const char *here;

    if (!list->mask) {
	index = -1;
	fetch = 0;
    }
    else {
	index = 0;
	fetch = 1;
    }
    indent = 1;
    while (list->name) {
	if (!list->mask) {
	    if (list->shift) index += list->shift;
	    else index++;
	    fetch = 1;
	    if (!*list->name) indent = 0;
	    else {
		if (strcmp(list->name,">"))
		    printf("%s[%d]\n",list->name,index);
		indent = 1;
	    }
	}
	else {
	    printf("%s[0x%08x] %-30s%s",indent ? "  " : "",base+index,
	      list->name,indent ? "" : "  ");
	    indent = 1;
	    if (fetch) {value = pick(base,index);
/*printf("  [%d] = 0x%lx\n",index,value);*/
}
	    fetch = 0;
	    if (list->shift == -1) eff = !!(value & list->mask);
	    else eff = (value & list->mask) >> list->shift;
	    if (!list->table) printf("0x%08lx %10ld\n",eff,eff);
	    else {
		for (here = list->table; eff; eff--)
		    here = strchr(here,0)+1;
		printf("%s\n",here);
	    }
	}
	list++;
    }
}


static void dump_registers(void)
{
    int i;

    process(0,direct_regs,get_direct);
    process(uPD98401_TOS,indirect_regs,get_indirect);
    for (i = 0; i < 4; i++) {
	printf("MBX %d\n",i);
	process(uPD98401_MSH(i),mbx,get_direct);
    }
    for (i = 0; i < 16; i++)
	if (zpeekl(uPD98401_PS(i)) & uPD98401_PS_E) {
	    printf("Scheduler %d\n",i);
	    process(uPD98401_IM(i),scheduler,get_indirect);
	}
}


int main(void)
{
    if (iopl(3) < 0) {
        perror("iopl");
        return 1;
    }
    if ((mem = open(MEM_DEV,O_RDONLY)) < 0) {
	perror("open");
	return 1;
    }
    dump_registers();
}
