/*  Copyright 2011, Unpublished Work of Technologic Systems
 *  All Rights Reserved.
 *
 *  THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,
 *  PROPRIETARY AND TRADE SECRET INFORMATION OF TECHNOLOGIC SYSTEMS.
 *  ACCESS TO THIS WORK IS RESTRICTED TO (I) TECHNOLOGIC SYSTEMS EMPLOYEES
 *  WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF THEIR
 *  ASSIGNMENTS  AND (II) ENTITIES OTHER THAN TECHNOLOGIC SYSTEMS WHO
 *  HAVE ENTERED INTO  APPROPRIATE LICENSE AGREEMENTS.  NO PART OF THIS
 *  WORK MAY BE USED, PRACTICED, PERFORMED, COPIED, DISTRIBUTED, REVISED,
 *  MODIFIED, TRANSLATED, ABRIDGED, CONDENSED, EXPANDED, COLLECTED,
 *  COMPILED,LINKED,RECAST, TRANSFORMED, ADAPTED IN ANY FORM OR BY ANY
 *  MEANS,MANUAL, MECHANICAL, CHEMICAL, ELECTRICAL, ELECTRONIC, OPTICAL,
 *  BIOLOGICAL, OR OTHERWISE WITHOUT THE PRIOR WRITTEN PERMISSION AND
 *  CONSENT OF TECHNOLOGIC SYSTEMS . ANY USE OR EXPLOITATION OF THIS WORK
 *  WITHOUT THE PRIOR WRITTEN CONSENT OF TECHNOLOGIC SYSTEMS  COULD
 *  SUBJECT THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
 */
#include <assert.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include "ts8820.h"

volatile unsigned short *syscon;
volatile unsigned short *offbd;
volatile unsigned short *sram;
volatile unsigned int *dio;
volatile unsigned int *syscon32;

static void usage(char **argv) {
	fprintf(stderr, "Usage: %s [OPTION] ...\n"
	  "Technologic Systems TS-8820 FPGA manipulation\n"
	  "\n"
	  "General options:\n"
	  "  -s, --sample=N          Display N samples per ADC channel in mV\n"
          "  -a, --acquire=N         Send N raw samples per ADC channel to stdout\n"
          "  -r, --rate=SPEED        Sample at SPEED Hz (default 10000)\n"
          "  -m, --mask=MASK         Sample only channels actived in 16 bit MASK\n"
          "  -d, --setdac=N          Set DAC #N (1-4)\n"
          "  -v, --mvolts=VALUE      DAC/PWM voltage in mV (0 to 10000, default 0)\n"
          "  -p, --pwm=N             Put PWM on digital out #N (1-6)\n"
          "  -P, --prescalar=VALUE   PWM freq will be (12207/(2^VALUE)) Hz (0-7)\n"
          "  -1, --hbridge1=ARG      Control H-bridge #1 (0/1/2, default 1)\n"
          "  -2, --hbridge2=ARG      Control H-bridge #2 (0/1/2, default 1)\n"
          "  -c, --counter=N         Read pulse counter for digital input #N (1-14)\n"
	  "  -D, --setdio=LVAL       Set DIO output to LVAL\n"
	  "  -G, --getdio            Get DIO input\n"
          "  -R  --read=BYTES        Read BYTES bytes from TS-8820 SRAM to stdout\n"
          "  -W  --write=BYTES       Write BYTES bytes from stdin to TS-8820 SRAM\n"
	  "  -h, --help              This help\n\n"
          "PWMs 1-6 feed digital outputs; PWMs 7 and 8 feed H-bridges.\n"
          "The --pwm option overrides the DIO setting and makes the pin a PWM.\n"
          "The --pwm=N --mvolts=10000 gives a 100%% duty cycle.\n"
          "To revert back to simple DIO, use --mvolts=-1.\n"
          "H-bridge arguments: 1=run forward; 2=run backward; 0=disable.\n"
          "To \"free-wheel\" an H-bridge, set its PWM to 0%% and leave it enabled.\n"
          "A disabled H-bridge will have high-impedance on both sides.\n\n",
	  argv[0]
	);
}

int main(int argc, char **argv) {
	int devmem, c, x;
        int opt_sample = 0, opt_acquire = 0, opt_setdac = 0;
        int opt_rate = 10000, opt_mask = 0xffff, opt_mvolts = 0;
        int opt_pwm = 0, opt_prescalar = 0, opt_hb = 0, opt_hbarg = 1;
	static struct option long_options[] = {
	  { "sample", 1, 0, 's' },
	  { "acquire", 1, 0, 'a' },
	  { "rate", 1, 0, 'r' },
	  { "mask", 1, 0, 'm' },
	  { "setdac", 1, 0, 'd' },
	  { "mvolts", 1, 0, 'v' },
	  { "pwm", 1, 0, 'p' },
	  { "prescalar", 1, 0, 'P' },
	  { "hbridge1", 1, 0, '1' },
	  { "hbridge2", 1, 0, '2' },
	  { "counter", 1, 0, 'c' },
	  { "setdio", 1, 0, 'D'},
	  { "getdio", 0, 0, 'G'},
          { "read", 1, 0, 'R' },
          { "write", 1, 0, 'W' },
	  { "help", 0, 0, 'h' },
	  { 0, 0, 0, 0 }
	};
	
	if (is_ts4700()) {
		devmem = open("/dev/mem", O_RDWR|O_SYNC);
		assert(devmem != -1);
		syscon = (unsigned short *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0x80004000);
		offbd = (unsigned short *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0x80008000);
		sram = (unsigned short *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0x80009000);
		dio = (unsigned int *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0xd4019000);
		syscon[2] = 0xf0ff; // MUXBUS timing config
		syscon[1] |= 0x800; // Enable base board clock
		usleep(20000); // Give base board FPGA PLL time to lock
		// set up Marvell DIO for no ADC oversampling:
		dio[0x58/4] = 0x1480;
		dio[0x28/4] = 0x1480;
	} else if (is_ts4800()) {
		devmem = open("/dev/mem", O_RDWR|O_SYNC);
		assert(devmem != -1);
		syscon = (unsigned short *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0xb0010000);
		offbd = (unsigned short *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0xb0011000);
		sram = (unsigned short *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0xb0017000);
		dio = (unsigned int *) mmap(0, 4096,
		  PROT_READ | PROT_WRITE, MAP_SHARED, devmem, 0x73f84000); // bank 1
		syscon[0x12/2] = 0x321; // MUXBUS timing config
		syscon[1] |= 0x800; // Enable base board clock
		usleep(20000); // Give base board FPGA PLL time to lock
        
		// set up TS-4800 DIO for no ADC oversampling, no 5V range:
		dio[1] |= 0xa8;
		dio[0] |= 0x88;
		dio[0] &= ~0x20;
		syscon[0x2c/2] |= 0xc;
		syscon[0x2a/2] &= ~0xc;
	}
        
	if (ts8820_init(offbd, sram, syscon)) {
			printf("TS-8820 not detected.\n");
			return -1;
	}

	while((c = getopt_long(argc, argv,
	  "c:p:P:1:2:r:v:m:hs:a:d:D:GR:W:", long_options, NULL)) != -1) {
		switch (c) {
                case 'r':
			opt_rate = strtoul(optarg, NULL, 0);
			break;
                case 'v':
			opt_mvolts = strtoul(optarg, NULL, 0);
			break;
                case 'm':
			opt_mask = strtoul(optarg, NULL, 0);
			break;
                case 's':
                        opt_sample = strtoul(optarg, NULL, 0);
			break;
                case 'a':
                        opt_acquire = strtoul(optarg, NULL, 0);
			break;
                case 'd':
                        opt_setdac = strtoul(optarg, NULL, 0);
			break;
                case 'p':
                        opt_pwm = strtoul(optarg, NULL, 0);
			break;
                case 'P':
                        opt_prescalar = strtoul(optarg, NULL, 0);
			break;
                case '1':
                        opt_hb = 1;
                        opt_hbarg = strtoul(optarg, NULL, 0);
			break;
                case '2':
                        opt_hb = 2;
                        opt_hbarg = strtoul(optarg, NULL, 0);
			break;
                case 'D':
                        ts8820_do_set(strtoul(optarg, NULL, 0));
			break;
                case 'G':
                        printf("dio=0x%x\n", ts8820_di_get());
			break;
                case 'c':
                        x = strtoul(optarg, NULL, 0);
                        printf("counter%d=%d\n", x, ts8820_counter(x));
			break;
                case 'R':
                        ts8820_sram_read(strtoul(optarg, NULL, 0));
			break;
                case 'W':
                        ts8820_sram_write(strtoul(optarg, NULL, 0));
			break;
		case 'h':
		default:
			usage(argv);
			return(1);
		}
	}
        if (opt_sample) ts8820_adc_sam(opt_rate, opt_sample);
        if (opt_acquire) ts8820_adc_acq(opt_rate, opt_acquire, opt_mask);
        if (opt_setdac) ts8820_dac_set(opt_setdac, opt_mvolts);
        if (opt_pwm) {
                if (opt_mvolts < 0) ts8820_pwm_disable(opt_pwm);
                else ts8820_pwm_set(opt_pwm, opt_prescalar, 
                                opt_mvolts*0x1000/10000);
        }
        if (opt_hb) {
                if (opt_hbarg) ts8820_hb_set(opt_hb, opt_hbarg - 1);
                else ts8820_hb_disable(opt_hb);
        }
	return 0;
}

