view pcmon/pcmonhelper.c @ 0:c34b37680055 default tip

Inital commit of random SuperIO code.
author Daniel O'Connor <darius@dons.net.au>
date Thu, 20 Oct 2011 16:48:24 +1030
parents
children
line wrap: on
line source

#define _WITH_GETLINE

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

/* Prototypes */
void	ec_outb(uint16_t base, uint8_t adr, uint8_t val);
uint8_t	ec_inb(uint16_t base, uint8_t adr);
void	ec_run(uint16_t base);
void	freeargs(char **args, size_t nargs);
void	splitline(char *line, char ***args, size_t *nargs);
void	superio_enter(uint16_t base, char type);
void	superio_exit(uint16_t base, char type);
uint8_t	superio_inb(uint8_t base, uint8_t adr);
void	superio_outb(uint16_t base, uint8_t adr, uint8_t val);
void	usage(char *name);

void
usage(char *name) {
    fprintf(stderr,
	    "Bad usage\n"
	    "\t%s [-b base] [-t type]\n"
	    "\n"
	    "Read/write registers in the EC function of Winbond/ITE parts\n"
	    "base is the base address for the chip (default: 0x2e)\n"
	    "type is the chip type (i = ITE, w = WinBond)\n", name);
    exit(1);
}

int
main(int argc, char **argv) {
    int	fd, base, ch, ecbase, devid;
    char *progname, type;
    
    type = 'w';
    base = 0x2e;
    
    progname = argv[0];
    
    while ((ch = getopt(argc, argv, "b:t:")) != -1) {
	switch (ch) {
	    case 'b':
		base = strtol(optarg, NULL, 0) & 0xffff;
		break;
		    
	    case 't':
		if (optarg[0] != 'w' && optarg[0] != 'i') {
		    fprintf(stderr, "type must be w or i\n\n");
		    usage(progname);
		}
		
		type = optarg[0];
		break;

	    default:
		usage(progname);
		break;
	}
    }
    argc -= optind;
    argv += optind;
	
    if (argc != 0)
	usage(progname);

    if ((fd = open("/dev/io", O_RDWR)) == -1) {
	fprintf(stderr, "Can't open /dev/io: %s\n", strerror(errno));
	exit(1);
    }

    /* Enter extended function mode */
    superio_enter(base, type);
    
    /* Check device ID */
    devid = superio_inb(base, 0x20) << 8 | superio_inb(base, 0x21);
    printf("Device ID = 0x%04x\n", devid);
    if (devid != 0x8720) {
	fprintf(stderr, "Device ID mismatch\n");
	superio_exit(base, type);
	exit(1);
    }

    /* Select EC config registers */
    superio_outb(base, 0x7, 4);

    /* Determine EC base address */
    ecbase = superio_inb(base, 0x60) << 8;
    ecbase |= superio_inb(base, 0x61);

    /* Exit extended function mode */
    superio_exit(base, type);

    ec_run(ecbase);
    exit(0);
}

/* Enter extended function mode */
void
superio_enter(uint16_t base, char type) {
    if (type == 'w') {
	outb(base, 0x87);
	outb(base, 0x87);
    } else {
	outb(base, 0x87);
	outb(base, 0x01);
	outb(base, 0x55);
	outb(base, 0x55);
    }
}

/* Exit extended function mode */
void
superio_exit(uint16_t base, char type) {
    if (type == 'w') {
	outb(base, 0xaa);
	outb(base, 0xaa);
    } else {
	outb(base, 0x02);
	outb(base, 0x02);
    }
}

void
superio_outb(uint16_t base, uint8_t adr, uint8_t val) {
    outb(base, adr);
    outb(base + 1, val);
}

uint8_t
superio_inb(uint8_t base, uint8_t adr) {
    outb(base, adr);
    return(inb(base + 1));
}

void
ec_outb(uint16_t base, uint8_t adr, uint8_t val) {
    outb(base + 5, adr);
    outb(base + 6, val);
}

uint8_t
ec_inb(uint16_t base, uint8_t adr) {
    outb(base + 5, adr);
    return(inb(base + 6));
}

void
ec_run(uint16_t base) {
    char	*line = NULL, **args = NULL, *p;
    size_t	llen = 0, nargs;
    long	tmp;
    uint8_t	adr, val;
    
    printf("EC base address = 0x%04x\n", base);
    printf("EC Vendor ID = 0x%02x\n", ec_inb(base, 0x58));
    printf("EC Core ID = 0x%02x\n", ec_inb(base, 0x5b));

    while (1) {
	if (getline(&line, &llen, stdin) == -1)
	    break;

	/* Snip trailing new line*/
	*rindex(line, '\n')= '\0';
	
	splitline(line, &args, &nargs);

	if (nargs != 2 && nargs != 3) {
	    printf("Unable to parse line\n");
	    continue;
	}

	tmp = strtol(args[1], &p, 0);
	if (p == args[1]) {
	    printf("Unable to parse address\n");
	    continue;
	}
	if (tmp < 0 || tmp > 255) {
	    printf("Address out of range\n");
	    continue;
	}
	adr = tmp;

	if (!strcmp("read", args[0])) {
	    if (nargs != 2) {
		printf("Bad usage of read command\n");
		continue;
	    }
	    
	    printf("0x%02x -> 0x%02x\n", adr, ec_inb(base, adr));
	} else if  (!strcmp("write", args[0])) {
	    if (nargs != 3) {
		printf("Bad usage of write command\n");
		continue;
	    }
	    tmp = strtol(args[2], &p, 0);
	    if (p == args[1]) {
		printf("Unable to parse value\n");
		continue;
	    }
	    if (tmp < 0 || tmp > 255) {
		printf("Value out of range\n");
		continue;
	    }
	    val = tmp;
	    ec_outb(base, adr, val);
	    printf("0x%02x <- 0x%02x\n", adr, val);
	} else {
	    printf("Unable to parse line\n");
	}
    }
    free(line);
}

void
splitline(char *line, char ***args, size_t *nargs) {
    char	*token;
    
    freeargs(*args, *nargs);
    *args = NULL;
    *nargs = 0;
    
    while ((token = strsep(&line, " ")) != NULL) {
	*args = realloc(*args, sizeof(char *) * (*nargs + 1));
	if (args == NULL) {
	    fprintf(stderr, "Unable to allocate memory\n");
	    exit(1);
	}
	
	(*args)[*nargs] = malloc(strlen(token) + 1);
	strcpy((*args)[*nargs], token);
	
	(*nargs)++;
    }
}

void
freeargs(char **args, size_t nargs) {
    int i;

    if (args != NULL) {
	for (i = 0; i < nargs; i++)
	    free(args[i]);
	free(args);
    }
}