/*  Copyright 2018, Work of Technologic Systems All Rights Reserved.
 * 
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

const char copyright[] = "Copyright (c) Technologic Systems - " __DATE__ ;
/* 
 *  Compile with:
 *  gcc -Wall -mcpu=arm9 spi1-sample.c -o spi1-sample
 *
 * This tool is a simple demo for using SPI1 peripheral on the TS-4200
 * It sets up the required registers for a 1 MHZ, 8-bit wide transaction
 * with a flash device attached to the SPI pins.  It is an example of both send
 * and receive that can be used in real-world applications.
 * 
 * Usage:
 *  The tool expects to read a file from stdin, and will write that via the SPI bus:
 *   ./spi1-sample < binary_file.bin
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <assert.h>

volatile unsigned int *spi1;
volatile unsigned int *gpio;

#define GPIO(offset) gpio[(offset)/4]
#define GPIO_POKE(offset, val) gpio[(offset)/4] = val

static inline int spi(int x) {
	spi1[3] = 0x000e0000 | (x & 0xff);
	while((spi1[4] & 0x201) != 0x201); /* Wait for TX empty + RX rdy */
	return spi1[2] & 0xff; 
}

static inline void spidone(void) {
	spi1[0] = 0x01000000;
}

void wren(void) {
	spi(6);
	spidone();
}

int rdsr(void) {
	int ret;
	spi(5);
	ret = spi(0);
	spidone();
	return ret;
}

void pp(int adr, unsigned char *dat, int len) {
	spi(2);
	spi(adr>>16);
	spi(adr>>8);
	spi(adr);
	while (len--) spi(*dat++);
	spidone();
}

void ce(void) {
	spi(0xc7);
	spidone();
}

int main(void) {
	int fd, i, j;
	unsigned char buf[256];
	fd = open("/dev/mem", O_RDWR|O_SYNC);
	assert(fd != -1);
	spi1 = (unsigned int *) mmap(0, 4096,
	  PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0xfffcc000);
	gpio = (unsigned int *) mmap(0, 4096,
	  PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0xfffff000);

	/* Enable SPI1 pins */
        GPIO(0x670) = 0xf; //Assign pins to peripheral A
        GPIO(0x604) = 0xf; //Enable peripheral mode

	/* Enable SPI1 peripheral clock */
        GPIO(0xc10) = 0x2000;

	/* Enable SPI peripheral */
	spi1[0] = 0x00000001;

	/* 1Mhz SPI, chip select active after txfr, 8-bit, PHA=0/POL=1 */
	spi1[12] = 0x0006409;

	/* Variable chip select, master mode */
	spi1[1] = 0x000f0013;

	/* De-assert any CS# */
	spi1[0] = 0x01000000;

	fprintf(stderr, "Erasing...");
	wren();
	ce();
	while ((rdsr() & 0x1) == 0x1) usleep(10000);
	fprintf(stderr, "done\nProgramming...");

	j = 0;
	do {
		for(i = 0; i < 256; i++) buf[i] = fgetc(stdin);
		wren();
		pp(j, buf, 256);
		j += 256;
		while ((rdsr() & 0x1) == 0x1) usleep(10000);
		fprintf(stderr, ".");
	} while (!feof(stdin));
	fprintf(stderr, "done\n");

        return 0;
}
