/*
 * Copyright (C) 2014 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

/*
 * For infos look at:
 * "KL02 Sub-Family Reference Manual for 48 MHz devices 32 pin package
 * Reference Manual".
 *
 * "23: Flash Memory Controller (FMC)"
 * "24: Flash Memory Module (FTFA)"
 */

#define DEBUG	0

#ifdef INCLUDE

#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "system.h" /* FIXME */
#include "conv_s19.h" /* FIXME */
#include "umutil.h" /* FIXME */
#include "glue.h"

#endif /* INCLUDE */
#ifdef STATE

struct {
	void *media;
	uint8_t code[FLASH_SIZE];
} NAME;

#endif /* STATE */
#ifdef EXPORT

/*forward*/ static void
NAME_(read)(struct cpssp *cpssp, uint32_t addr, uint32_t *valp);
/*forward*/ static void
NAME_(erase)(struct cpssp *cpssp, uint32_t addr, uint32_t len);
/*forward*/ static void
NAME_(write)(struct cpssp *cpssp, uint32_t addr, uint32_t val);
/*forward*/ static void
NAME_(create)(struct cpssp *cpssp, const char *name, const char *nvram);
/*forward*/ static void
NAME_(destroy)(struct cpssp *cpssp);

#endif /* EXPORT */
#ifdef BEHAVIOR

static void
NAME_(read)(struct cpssp *cpssp, uint32_t addr, uint32_t *valp)
{
	addr &= sizeof(cpssp->NAME.code) - 1;

	*valp = (cpssp->NAME.code[addr + 0] << 0)
		| (cpssp->NAME.code[addr + 1] << 8)
		| (cpssp->NAME.code[addr + 2] << 16)
		| (cpssp->NAME.code[addr + 3] << 24);

	if (DEBUG) {
		fprintf(stderr, "%s: addr=0x%08lx val=0x%08lx\n",
				__FUNCTION__, addr, *valp);
	}
}

static void
NAME_(erase)(struct cpssp *cpssp, uint32_t addr, uint32_t len)
{
	uint32_t a;

	addr &= ~(len - 1);

	for (a = addr; a < addr + len; a++) {
		cpssp->NAME.code[a] = 0xff;
	}
}

static void
NAME_(write)(struct cpssp *cpssp, uint32_t addr, uint32_t val)
{
	assert(! (addr & 0b11));

	assert(cpssp->NAME.code[addr + 0] == 0xff);
	assert(cpssp->NAME.code[addr + 1] == 0xff);
	assert(cpssp->NAME.code[addr + 2] == 0xff);
	assert(cpssp->NAME.code[addr + 3] == 0xff);

	cpssp->NAME.code[addr + 0] = (val >> 0) & 0xff;
	cpssp->NAME.code[addr + 1] = (val >> 8) & 0xff;
	cpssp->NAME.code[addr + 2] = (val >> 16) & 0xff;
	cpssp->NAME.code[addr + 3] = (val >> 24) & 0xff;
}

static void
NAME_(create)(struct cpssp *cpssp, const char *name, const char *nvram)
{
	int ret;

	system_name_push(name);

	cpssp->NAME.media = storage_create(system_path(),
			sizeof(cpssp->NAME.code),
			nvram,
			conv_s19_open, conv_s19_close, conv_s19_read);
	assert(cpssp->NAME.media);

	ret = storage_read(cpssp->NAME.media, cpssp->NAME.code,
			sizeof(cpssp->NAME.code), 0);
	assert(ret == sizeof(cpssp->NAME.code));

	system_name_pop();
}

static void
NAME_(destroy)(struct cpssp *cpssp)
{
	int ret;

	ret = storage_write(cpssp->NAME.media, cpssp->NAME.code,
			sizeof(cpssp->NAME.code), 0);
	assert(ret == sizeof(cpssp->NAME.code));

	ret = storage_destroy(cpssp->NAME.media);
	assert(0 <= ret);
}

#endif /* BEHAVIOR */

#undef DEBUG
