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

    PCMCIA controller probe

    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"
#include "vg468.h"
#include "tcic.h"

static const char *version =
    "probe.c 1.13 1995/03/24 23:50:49 (David Hinds)\n";

static int i365_base = 0x03e0;

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

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 void i365_bset(u_short sock, u_short reg, u_char mask)
{
    u_char d = i365_get(sock, reg);
    d |= mask;
    i365_set(sock, reg, d);
}

static void i365_bclr(u_short sock, u_short reg, u_char mask)
{
    u_char d = i365_get(sock, reg);
    d &= ~mask;
    i365_set(sock, reg, d);
}

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

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

    if (!module)
	printf("Intel PCIC probe: ");
    if (verbose) printf("\n");
    
    id = sock = done = 0;
    ioperm(i365_base, 4, 1);
    ioperm(0x80, 1, 1);
    for (; sock < 8; sock++) {
	val = i365_get(sock, I365_IDENT);
	if (verbose)
	    printf("  ident(%d)=%#2.2x", sock, val); 
	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 (verbose) printf("\n  ");
    if (sock == 0) {
	if (!module)
	    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 Vadem VG-468 chips */
    if (strcmp(name, "i82365sl") == 0) {
	outb_p(0x0e, I365_PORT(0));
	outb_p(0x37, I365_PORT(0));
	i365_bset(0, VG468_MISC, VG468_MISC_VADEMREV);
	val = i365_get(0, I365_IDENT);
	if (val & I365_IDENT_VADEM) {
	    name = "Vadem VG-468";
	    id = val & 7;
	    i365_bclr(0, VG468_MISC, VG468_MISC_VADEMREV);
	}
    }
    
    /* 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) {
	    if (val & PD67_INFO_SLOTS)
		name = "Cirrus CL-PD672x";
	    else {
		name = "Cirrus CL-PD6710";
		sock = 1;
	    }
	    id = 8 - ((val & PD67_INFO_REV) >> 2);
	}
    }

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

static u_short tcic_getw(u_long base, u_char reg)
{
    u_short val = inw(base+reg);
    return val;
}

static void tcic_setw(u_long base, u_char reg, u_short data)
{
    outw(data, base+reg);
}

int tcic_probe_at(u_long base, int module)
{
    int i;
    u_short old;
    
    /* Anything there?? */
    for (i = 0; i < 0x10; i += 2)
	if (tcic_getw(base, i) == 0xffff)
	    return -1;

    if (!module)
	printf("  at %#3.3lx: ", base); fflush(stdout);

    /* Try to reset the chip */
    tcic_setw(base, TCIC_SCTRL, TCIC_SCTRL_RESET);
    tcic_setw(base, TCIC_SCTRL, 0);
    
    /* Can we set the addr register? */
    old = tcic_getw(base, TCIC_ADDR);
    tcic_setw(base, TCIC_ADDR, 0);
    if (tcic_getw(base, TCIC_ADDR) != 0) {
	tcic_setw(base, TCIC_ADDR, old);
	return -2;
    }
    
    tcic_setw(base, TCIC_ADDR, 0xc3a5);
    if (tcic_getw(base, TCIC_ADDR) != 0xc3a5)
	return -3;

    return 2;
}

int tcic_probe(int verbose, int module, u_long base)
{
    int sock;

    if (!module)
	printf("Databook TCIC-2 probe: "); fflush(stdout);
    
    ioperm(base, 16, 1);
    ioperm(0x80, 1, 1);
    sock = tcic_probe_at(base, module);
    
    if (sock <= 0) {
	if (!module)
	    printf("not found.\n");
	return -ENODEV;
    }
    else {
	if (module)
	    printf("tcic\n");
	else
	    printf("%d sockets at %#6lx\n", sock, base);
	return 0;
    }
    
} /* tcic_probe */

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

void main(int argc, char *argv[])
{
    int optch, errflg;
    extern char *optarg;
    int verbose = 0, module = 0;
    u_long tcic_base = TCIC_BASE;

    
    errflg = 0;
    while ((optch = getopt(argc, argv, "t:vxm")) != -1) {
	switch (optch) {
	case 't':
	    tcic_base = strtoul(optarg, NULL, 0); break;
	case 'v':
	    verbose = 1; break;
	case 'm':
	    module = 1; break;
	default:
	    errflg = 1; break;
	}
    }
    if (errflg || (optind < argc)) {
	fprintf(stderr, "usage: %s [-t tcic_base] [-v] [-m]\n", argv[0]);
	exit(EXIT_FAILURE);
    }

    if (verbose) printf("%s", version);

    if (i365_probe(verbose, module) == 0)
	exit(EXIT_SUCCESS);
    else {
	if (tcic_probe(verbose, module, tcic_base) == 0)
	    exit(EXIT_SUCCESS);
	else
	    exit(EXIT_FAILURE);
    }
}
