/*======================================================================

    Register dump for Intel 82365SL controller

    Written by David Hinds, dhinds@allegro.stanford.edu
    
======================================================================*/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <asm/io.h>

#include "i82365.h"
#include "pd67xx.h"

static const char *version =
    "dump_i365.c v1.8: 1995/01/28 19:13:42 (David Hinds)\n";

static int i365_base = 0x03e0;
static int cirrus = 0;

/*====================================================================*/

static u_char i365_get(u_short sock, u_short reg)
{
    u_short port = I365_PORT(sock);
    u_char val = I365_REG(sock, reg);
    outb_p(val, port); val = inb_p(port+1);
    return val;
}

static void i365_set(u_short sock, u_short reg, u_char data)
{
    u_short port = I365_PORT(sock);
    u_char val = I365_REG(sock, reg);
    outb_p(val, port); outb_p(data, port+1);
}

static u_short i365_get_pair(u_short sock, u_short reg)
{
    u_short a, b;
    a = i365_get(sock, reg);
    b = i365_get(sock, reg+1);
    return (a + (b<<8));
}

/*====================================================================*/

int i365_probe(void)
{
    int val, id, sock, done;
    char *name = "i82365sl";

    printf("Intel PCIC probe: ");
    
    id = sock = done = 0;
    ioperm(i365_base, 4, 1);
    ioperm(0x80, 1, 1);
    for (; sock < 8; sock++) {
	val = i365_get(sock, I365_IDENT);
	switch (val) {
	case 0x82: case 0x83:
	    name = "i82365sl";
	    id = val & 7;
	    break;
	case 0x84:
	    name = "VLSI 82C146";
	    id = 0;
	    break;
	case 0x88: case 0x89:
	    name = "IBM Clone";
	    id = val & 7;
	    break;
	default:
	    done = 1;
	}
	if (done) break;
	i365_set(sock, I365_MEM(3)+I365_W_OFF, sock);
    }
    
    if (sock == 0) {
	printf("not found.\n");
	return -ENODEV;
    }
    
    /* Check for bogus clones that ignore top bit of index register */
    if ((sock == 4) && (i365_get(0, I365_MEM(3)+I365_W_OFF) == 2))
	sock = 2;
    
    /* Check for Cirrus CL-PD67xx chips */
    i365_set(0, PD67_CHIP_INFO, 0);
    val = i365_get(0, PD67_CHIP_INFO);
    if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
	val = i365_get(0, PD67_CHIP_INFO);
	if ((val & PD67_INFO_CHIP_ID) == 0) {
	    cirrus = 1;
	    if (val & PD67_INFO_SLOTS)
		name = "Cirrus CL-PD672x";
	    else {
		name = "Cirrus CL-PD6710";
		sock = 1;
	    }
	    id = 8 - ((val & PD67_INFO_REV) >> 2);
	}
    }

    printf("%s rev %d found, %d sockets\n", name, id, sock);
    return sock;
    
} /* i365_probe */
  
/*====================================================================*/

static void dump_status(int s)
{
    int v = i365_get(s, I365_STATUS);
    printf("  Interface status = %#2.2x\n", v);
    printf("   ");
    if (v & I365_CS_BVD1) printf(" [BVD1/STSCHG]");
    if (v & I365_CS_BVD2) printf(" [BVD2/SPKR]");
    if (v & I365_CS_DETECT) printf(" [DETECT]");
    if (v & I365_CS_WRPROT) printf(" [WRPROT]");
    if (v & I365_CS_READY) printf(" [READY]");
    if (v & I365_CS_POWERON) printf(" [POWERON]");
    if (v & I365_CS_GPI) printf(" [GPI]");
    printf("\n");
}

static void dump_power(int s)
{
    int v = i365_get(s, I365_POWER);
    printf("  Power control = %#2.2x\n", v);
    printf("   ");
    if (v & I365_PWR_OUT) printf(" [OUTPUT]");
    if (!(v & I365_PWR_NORESET)) printf(" [RESETDRV]");
    if (v & I365_PWR_AUTO) printf(" [AUTO]");
    switch (v & I365_VCC_MASK) {
    case I365_VCC_5V:
	printf(" [Vcc=5v]"); break;
    case I365_VCC_3V:
	printf(" [Vcc=3v]"); break;
    case 0:
	printf(" [Vcc off]"); break;
    }
    switch (v & I365_VPP_MASK) {
    case I365_VPP_5V:
	printf(" [Vpp=5v]"); break;
    case I365_VPP_12V:
	printf(" [Vpp=12v]"); break;
    case 0:
	printf(" [Vpp off]"); break;
    }
    printf("\n");
}

static void dump_intctl(int s)
{
    int v = i365_get(s, I365_INTCTL);
    printf("  Interrupt and general control = %#2.2x\n", v);
    printf("   ");
    if (v & I365_RING_ENA) printf(" [RING_ENA]");
    if (!(v & I365_PC_RESET)) printf(" [RESET]");
    if (v & I365_PC_IOCARD) printf(" [IOCARD]");
    if (v & I365_INTR_ENA) printf(" [INTR_ENA]");
    printf(" [irq=%d]\n", v & I365_IRQ_MASK);
}

static void dump_csc(int s)
{
    int v = i365_get(s, I365_CSC);
    printf("  Card status change = %#2.2x\n", v);
    printf("   ");
    if (v & I365_CSC_BVD1) printf(" [BVD1/STSCHG]");
    if (v & I365_CSC_BVD2) printf(" [BVD2]");
    if (v & I365_CSC_DETECT) printf(" [DETECT]");
    if (v & I365_CSC_READY) printf(" [READY]");
    if (v & I365_CSC_GPI) printf(" [GPI]");
    printf("\n");
}

static void dump_cscint(int s)
{
    int v = i365_get(s, I365_CSCINT);
    printf("  Card status change interrupt control = %#2.2x\n", v);
    printf("   ");
    if (v & I365_CSC_BVD1) printf(" [BVD1/STSCHG]");
    if (v & I365_CSC_BVD2) printf(" [BVD2]");
    if (v & I365_CSC_DETECT) printf(" [DETECT]");
    if (v & I365_CSC_READY) printf(" [READY]");
    printf(" [irq = %d]\n", v >> 4);
}

static void dump_genctl(int s)
{
    int v = i365_get(s, I365_GENCTL);
    printf("  Card detect and general control = %#2.2x\n", v);
    printf("   ");
    if (v & I365_CTL_16DELAY) printf(" [16DELAY]");
    if (v & I365_CTL_RESET) printf(" [RESET]");
    if (v & I365_CTL_GPI_ENA) printf(" [GPI_ENA]");
    if (v & I365_CTL_GPI_CTL) printf(" [GPI_CTL]");
    if (v & I365_CTL_RESUME) printf(" [RESUME]");
    printf("\n");
}

static void dump_gblctl(int s)
{
    int v = i365_get(s, I365_GBLCTL);
    printf("  Global control = %#2.2x\n", v);
    printf("   ");
    if (v & I365_GBL_PWRDOWN) printf(" [PWRDOWN]");
    if (v & I365_GBL_CSC_LEV) printf(" [CSC_LEV]");
    if (v & I365_GBL_WRBACK) printf(" [WRBACK]");
    if (v & I365_GBL_IRQ_0_LEV) printf(" [IRQ_0_LEV]");
    if (v & I365_GBL_IRQ_1_LEV) printf(" [IRQ_1_LEV]");
    printf("\n");
}

static void dump_misc1(int s)
{
    int v = i365_get(s, PD67_MISC_CTL_1);
    printf("  Misc control 1 = %#2.2x\n", v);
    printf("   ");
    if (v & PD67_MC1_5V_DET) printf(" [5V_DET]");
    if (v & PD67_MC1_VCC_3V) printf(" [VCC_3V]");
    if (v & PD67_MC1_PULSE_MGMT) printf(" [PULSE_MGMT]");
    if (v & PD67_MC1_PULSE_IRQ) printf(" [PULSE_IRQ]");
    if (v & PD67_MC1_SPKR_ENA) printf(" [SPKR]");
    if (v & PD67_MC1_INPACK_ENA) printf(" [INPACK]");
    printf("\n");
}

static void dump_misc2(int s)
{
    int v = i365_get(s, PD67_MISC_CTL_2);
    printf("  Misc control 2 = %#2.2x\n", v);
    printf("   ");
    if (v & PD67_MC2_FREQ_BYPASS) printf(" [FREQ_BYPASS]");
    if (v & PD67_MC2_DYNAMIC_MODE) printf(" [DYNAMIC_MODE]");
    if (v & PD67_MC2_SUSPEND) printf(" [SUSPEND]");
    if (v & PD67_MC2_5V_CORE) printf(" [5V_CORE]");
    if (v & PD67_MC2_LED_ENA) printf(" [LED_ENA]");
    if (v & PD67_MC2_3STATE_BIT7) printf(" [3STATE_BIT7]");
    if (v & PD67_MC2_DMA_MODE) printf(" [DMA_MODE]");
    if (v & PD67_MC2_IRQ15_RI) printf(" [IRQ15_RI]");
    printf("\n");
}

static void print_time(char *s, int v)
{
    printf("%s = %d", s, v & PD67_TIME_MULT);
    switch (v & PD67_TIME_SCALE) {
    case PD67_TIME_SCALE_16:
	printf(" [*16]"); break;
    case PD67_TIME_SCALE_256:
	printf(" [*256]"); break;
    case PD67_TIME_SCALE_4096:
	printf(" [*4096]"); break;
    }
}

static void dump_timing(int s, int i)
{
    printf("  Timing set %d: ", i);
    print_time("setup", i365_get(s, PD67_TIME_SETUP(i)));
    print_time(", command", i365_get(s, PD67_TIME_CMD(i)));
    print_time(", recovery", i365_get(s, PD67_TIME_RECOV(i)));
    printf("\n");
}

void dump_ext(int s)
{
    u_char v;
    printf("  Extension registers:");
    printf("    ");
    i365_set(s, PD67_EXT_INDEX, PD67_DATA_MASK0);
    v = i365_get(s, PD67_EXT_DATA);
    printf("mask 0 = %#2.2x", v);
    i365_set(s, PD67_EXT_INDEX, PD67_DATA_MASK1);
    v = i365_get(s, PD67_EXT_DATA);
    printf(", mask 1 = %#2.2x", v);
    i365_set(s, PD67_EXT_INDEX, PD67_DMA_CTL);
    v = i365_get(s, PD67_EXT_DATA);
    printf(", DMA ctl = %#2.2x", v);
    switch (v & PD67_DMA_MODE) {
    case PD67_DMA_OFF:
	printf(" [OFF]"); break;
    case PD67_DMA_DREQ_INPACK:
	printf(" [DREQ INPACK]"); break;
    case PD67_DMA_DREQ_WP:
	printf(" [DREQ WP]"); break;
    case PD67_DMA_DREQ_BVD2:
	printf(" [DREQ BVD2]"); break;
    }
    if (v & PD67_DMA_PULLUP)
	printf(" [PULLUP]");
    printf("\n");
}

static void dump_memwin(int s, int w)
{
    u_short start, stop, off;
    printf("  Memory window %d:", w);
    if (i365_get(s, I365_ADDRWIN) & I365_ENA_MEM(w))
	printf(" [ON]");
    else
	printf(" [OFF]");
    start = i365_get_pair(s, I365_MEM(w)+I365_W_START);
    stop = i365_get_pair(s, I365_MEM(w)+I365_W_STOP);
    off = i365_get_pair(s, I365_MEM(w)+I365_W_OFF);
    if (start & I365_MEM_16BIT) printf(" [16BIT]");
    if (cirrus) {
	if (stop & I365_MEM_WS1)
	    printf(" [TIME1]");
	else
	    printf(" [TIME0]");
    }
    else {
	if (start & I365_MEM_0WS) printf(" [0WS]");
	if (stop & I365_MEM_WS1) printf(" [WS1]");
	if (stop & I365_MEM_WS0) printf(" [WS0]");
    }
    if (off & I365_MEM_WRPROT) printf(" [WRPROT]");
    if (off & I365_MEM_REG) printf(" [REG]");
    printf("\n    start = %#4.4x", start & 0x3fff);
    printf(", stop = %#4.4x", stop & 0x3fff);
    printf(", offset = %#4.4x\n", off & 0x3fff);
}

static void dump_iowin(int s, int w)
{
    u_short ctl, start, stop, off;
    printf("  I/O window %d:", w);
    if (i365_get(s, I365_ADDRWIN) & I365_ENA_IO(w))
	printf(" [ON]");
    else
	printf(" [OFF]");
    
    ctl = i365_get(s, I365_IOCTL);
    if (cirrus) {
	if (ctl & I365_IOCTL_WAIT(w))
	    printf(" [TIME1]");
	else
	    printf(" [TIME0]");
    }
    else {
	if (ctl & I365_IOCTL_WAIT(w)) printf(" [WAIT]");
	if (ctl & I365_IOCTL_0WS(w)) printf(" [0WS]");
    }
    if (ctl & I365_IOCTL_IOCS16(w)) printf(" [IOCS16]");
    if (ctl & I365_IOCTL_16BIT(w)) printf(" [16BIT]");
    
    start = i365_get_pair(s, I365_IO(w)+I365_W_START);
    stop = i365_get_pair(s, I365_IO(w)+I365_W_STOP);
    printf("\n    start = %#4.4x, stop = %#4.4x", start, stop);

    if (cirrus) {
	off = i365_get_pair(s, PD67_IO_OFF(w));
	printf(", offset = %#4.4x", off);
    }
    printf("\n");
}

/*====================================================================*/

void dump_sock(int s)
{
    int i;
    printf("  Identification and revision = %#2.2x\n",
	   i365_get(s, I365_IDENT));
    if (cirrus)
	printf("  Chip information = %#2.2x\n",
	       i365_get(s, PD67_CHIP_INFO));
    dump_status(s);
    dump_power(s);
    dump_intctl(s);
    dump_csc(s);
    dump_cscint(s);
    if (cirrus) {
	dump_misc1(s);
	dump_misc2(s);
    }
    else {
	dump_genctl(s);
	dump_gblctl(s);
    }
    for (i = 0; i < 5; i++)
	dump_memwin(s, i);
    for (i = 0; i < 2; i++)
	dump_iowin(s, i);
    if (cirrus) {
	for (i = 0; i < 2; i++)
	    dump_timing(s, i);
	dump_ext(s);
    }
    printf("\n");
} /* dump_sock */

/*====================================================================*/

void main(int argc, char *argv[])
{
    int sock, i;
    
    if ((argc > 1) && (strcmp(argv[1], "-v") == 0))
	printf("%s", version);
    
    sock = i365_probe();
    for (i = 0; i < sock; i++) {
	printf("Socket %d:\n", i);
	dump_sock(i);
    }
}
