/*
 * gpk8_13w.c: Writes GPK8 and GPK13 at mini6410 if there are 3 arguments.
 * Without arguments it shows status info of GPK8 and GPK13.
 *
 * use : ./gpk8_13w a b
 * a : 0 or 1 to be written to GPK8
 * b : 0 or 1 to be written to GPK13
 * for instance : ./gpk8_13w 0 1 (It writes 0 to GPK8 and 1 to GPK13)
 * 
 * To compile : arm-linux-gcc gpk8_13w.c -o gpk8_13w
 * 
 * By Jordi Binefa at www.electronics.cat (electronics [dot] cat [at] gmail [dot] com)
 * 
 * 20100915
 * 
 * Modified version of gpk8_13.c (http://www.electronics.cat)
 * -------------------------------------------------------------------
 * 
 * gpk8_13.c: Status info of GPK8 and GPK13 at mini6410
 * 
 * By Jordi Binefa at www.electronics.cat (electronics [dot] cat [at] gmail [dot] com)
 * 
 * 20100914
 * 
 * Modified version of devmem2.c (http://www.simtec.co.uk/appnotes/AN0014/)
 * -------------------------------------------------------------------
 * 
 * devmem2.c: Simple program to read/write from/to any location in memory.
 *
 *  Copyright (C) 2000, Jan-Derk Bakker (jdb@lartmaker.nl)
 *
 *
 * This software has been developed for the LART computing board
 * (http://www.lart.tudelft.nl/). The development has been sponsored by
 * the Mobile MultiMedia Communications (http://www.mmc.tudelft.nl/)
 * and Ubiquitous Communications (http://www.ubicom.tudelft.nl/)
 * projects.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
  
#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
  __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
 
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)

#define GPKCON1 0x7F008804
#define GPKDAT	0x7F008808
#define GPKPUD	0x7F00880C

#define GPKCON1_GPK8_MASK	0x0000000F
#define GPKCON1_GPK13_MASK	0x00F00000
#define GPKDAT_GPK8			0x0100
#define GPKDAT_GPK13		0x2000
#define GPKPUD_GPK8_MASK	0x00030000
#define GPKPUD_GPK13_MASK	0x0C000000
#define GPKCON1_W_GPK8		0x00000001
#define GPKCON1_W_GPK13		0x00100000
#define GPKPUD_PD_GPK8		0x00010000
#define GPKPUD_PD_GPK13		0x04000000
#define GPKPUD_PU_GPK8		0x00020000
#define GPKPUD_PU_GPK13		0x08000000

static char *szCfgType(unsigned long ul){
	switch(ul){
		case 0 : return "Input";
		case 1 : return "Output";
		case 2 : return "Host I/F Data";
		case 3 : return "Key pad ROW";
		case 5 : return "DATA_CF";
		default : return "Reserved";
	}
}

static char *szPudType(unsigned long ul){
	switch(ul){
		case 0 : return "Pull-up/down disabled";
		case 1 : return "Pull-down enabled";
		case 2 : return "Pull-up enabled";
		default : return "Reserved";
	}
}

#ifndef BOOL
	#define BOOL unsigned char
#endif
#ifndef TRUE
	#define TRUE 1
#endif
#ifndef FALSE
	#define FALSE 0
#endif

BOOL bValidArguments(char *s1, char*s2){
	if(!((*s1 == '0')||(*s1 == '1')))
		return FALSE;
	if(!((*s2 == '0')||(*s2 == '1')))
		return FALSE;
	return TRUE;
}

int main(int argc, char **argv) {
    int fd;
    void *map_base, *virt_addr; 
	unsigned long read_result, writeval,ulRead;
	unsigned short usRead;
	off_t target;
	int access_type = 'w';	

    if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
    printf("/dev/mem opened.\n"); 
    fflush(stdout);
    
    /* Map one page */
	target = GPKCON1;
    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
    if(map_base == (void *) -1) FATAL;
    printf("Memory mapped at address %p.\n", map_base); 
    fflush(stdout);
    
	if(argc == 3) {
		if(bValidArguments(argv[1],argv[2])){
			target = GPKCON1;
			virt_addr = map_base + (target & MAP_MASK);
			ulRead = *((unsigned long *) virt_addr);
			*((unsigned long *) virt_addr) = (ulRead & ~GPKCON1_GPK8_MASK) | GPKCON1_W_GPK8; // GPK8 as output
			ulRead = *((unsigned long *) virt_addr); // Now it has GPK8 as output
			*((unsigned long *) virt_addr) = (ulRead & ~GPKCON1_GPK13_MASK) | GPKCON1_W_GPK13; // GPK13 as output
			target = GPKPUD;
			virt_addr = map_base + (target & MAP_MASK);
			ulRead = *((unsigned long *) virt_addr);
			*((unsigned long *) virt_addr) = (ulRead & ~GPKPUD_GPK8_MASK) | GPKPUD_PU_GPK8; // GPK8 pull up 
			ulRead = *((unsigned long *) virt_addr);
			*((unsigned long *) virt_addr) = (ulRead & ~GPKPUD_GPK13_MASK) | GPKPUD_PU_GPK13; // GPK13 pull up
			target = GPKDAT;
			virt_addr = map_base + (target & MAP_MASK);
			usRead = *((unsigned short *) virt_addr);		
			if(argv[1][0] == '0'){
				printf("argv[1][0] == '0' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
				*((unsigned short *) virt_addr) = usRead & ~GPKDAT_GPK8;
				usRead = *((unsigned short *) virt_addr);
				printf("argv[1][0] == '0' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
			}else{
				printf("argv[1][0] == '1' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
				*((unsigned short *) virt_addr) = usRead | GPKDAT_GPK8;
				usRead = *((unsigned short *) virt_addr);
				printf("argv[1][0] == '1' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
			}
			usRead = *((unsigned short *) virt_addr);		
			if(argv[2][0] == '0'){
				printf("argv[2][0] == '0' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
				*((unsigned short *) virt_addr) = usRead & ~GPKDAT_GPK13;
				usRead = *((unsigned short *) virt_addr);
				printf("argv[2][0] == '0' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
			}else{
				printf("argv[2][0] == '1' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
				*((unsigned short *) virt_addr) = usRead | GPKDAT_GPK13;
				usRead = *((unsigned short *) virt_addr);
				printf("argv[2][0] == '1' - Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
			}
		}else
			printf("Arguments are not valid. They should be 0 or 1.\n");
		//printf("Written 0x%X; readback 0x%X\n", writeval, read_result); 
		fflush(stdout);
	}
	target = GPKCON1;
    virt_addr = map_base + (target & MAP_MASK);
    ulRead = *((unsigned long *) virt_addr);
    printf("Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKCON1",target, virt_addr, virt_addr, ulRead);
    printf("GPK8 is configured as %s.\n",szCfgType( ulRead & GPKCON1_GPK8_MASK )); 
    printf("GPK13 is configured as %s.\n",szCfgType( (ulRead & GPKCON1_GPK13_MASK) >> 20)); 
    target = GPKPUD;
    virt_addr = map_base + (target & MAP_MASK);
    ulRead = *((unsigned long *) virt_addr);
    printf("Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKPUD",target, virt_addr, virt_addr, ulRead);
    printf("GPK8 is configured as %s.\n",szPudType( (ulRead & GPKPUD_GPK8_MASK) >> 16)); 
    printf("GPK13 is configured as %s.\n",szPudType( (ulRead & GPKPUD_GPK13_MASK) >> 26)); 
    target = GPKDAT;
    virt_addr = map_base + (target & MAP_MASK);
    usRead = *((unsigned short *) virt_addr);
    printf("Value at %s address 0x%X (%p := 0x%X): 0x%X\n", "GPKDAT",target, virt_addr, virt_addr, usRead);
    printf("GPK8 is %d.\n",!(!(usRead & GPKDAT_GPK8))); 
    printf("GPK13 is %d.\n",!(!(usRead & GPKDAT_GPK13))); 
    fflush(stdout);
	
	if(munmap(map_base, MAP_SIZE) == -1) FATAL;
    close(fd);
    return 0;
}


