/*
 * Copyright (C) 2014-2017 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.
 */

#define DEBUG_CONTROL_FLOW	0

#ifdef INCLUDE

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>

#endif /* INCLUDE */
#ifdef STATE

struct {
	uint8_t countflag;
	uint8_t clksource;
	uint8_t tickint;
	uint32_t reload;
	uint32_t current;
	uint8_t enabled;
} NAME;

#endif /* STATE */
#ifdef EXPORT

/*forward*/ static void
NAME_(st)(struct cpssp *cpssp, uint32_t addr, uint32_t val);
/*forward*/ static void
NAME_(ld)(struct cpssp *cpssp, uint32_t addr, uint32_t *valp);
/*forward*/ static void
NAME_(exttick)(struct cpssp *cpssp);
/*forward*/ static void
NAME_(clk)(struct cpssp *cpssp);
/*forward*/ static void
NAME_(reset)(struct cpssp *cpssp);
/*forward*/ static void
NAME_(create)(struct cpssp *cpssp);
/*forward*/ static void
NAME_(destroy)(struct cpssp *cpssp);

#endif /* EXPORT */
#ifdef BEHAVIOR

static void
NAME_(st)(struct cpssp *cpssp, uint32_t addr, uint32_t val)
{
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: addr=0x%08x val=0x%08x\n",
				__FUNCTION__, addr, val);
	}

	/* System Timer Register */
	/* B3.3.2 */
	switch (addr & 0xff) {
	case 0x10:
		/* SysTick Control and Status Register (SYST_CSR) */
		/* B3-277 */
		/* 31-17: Reserved */
		/* 16: Read-only */
		/* 15-3: Reserved */
		cpssp->NAME.clksource = (val >> 2) & 1;
		cpssp->NAME.tickint = (val >> 1) & 1;
		cpssp->NAME.enabled = (val >> 0) & 1;
		break;
	case 0x14:
		/* SysTick Reload Value Register (SYST_RVR) */
		/* B3-278 */
		/* 31-24: Reserved */
		cpssp->NAME.reload = (val >> 0) & 0xffffff;
		break;
	case 0x18:
		/* SysTick Current Value Register (SYST_CVR) */
		/* B3-279 */
		cpssp->NAME.current = 0;
		break;
	case 0x1c:
		/* SysTick Calibration Value Register (SYST_CALIB) */
		/* B3.3.6 */
		/* 31-30: Read-only */
		/* 29-24: Reserved */
		/* 23-0: Read-only */
		break;
	default:
		/* Reserved */
		fprintf(stderr, "WARNING: %s: addr=0x%08lx val=0x%08lx\n",
				__FUNCTION__, addr, val);
		assert(0); /* FIXME */
	}
}

static void
NAME_(ld)(struct cpssp *cpssp, uint32_t addr, uint32_t *valp)
{
	/* System Timer Register */
	/* B3.3.2 */
	*valp = 0;
	switch (addr & 0xff) {
	case 0x10:
		/* SysTick Control and Status Register */
		/* B3-277 */
		/* 31-27: Reserved */
		*valp |= cpssp->NAME.countflag << 16;
		cpssp->NAME.countflag = 0;
		/* 15-3: Reserved */
		*valp |= cpssp->NAME.clksource << 2;
		*valp |= cpssp->NAME.tickint << 1;
		*valp |= cpssp->NAME.enabled << 0;
		break;
	case 0x14:
		/* SysTick Reload Value Register */
		/* B3-278 */
		/* 31-24: Reserved */
		*valp = cpssp->NAME.reload;
		break;
	case 0x18:
		/* SysTick Current Value Register */
		/* B3-279 */
		/* 31-24: Reserved */
		*valp = cpssp->NAME.current;
		break;
	case 0x1c:
		/* SysTick Calibration Value Register */
		/* B3.3.6 */
		assert(0); /* FIXME */
	default:
		/* Reserved */
		fprintf(stderr, "WARNING: %s: addr=0x%08lx\n",
				__FUNCTION__, addr);
		assert(0); /* FIXME */
	}

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

static void
NAME_(tick)(struct cpssp *cpssp)
{
	if (! cpssp->NAME.enabled) {
		return;
	}

	if (cpssp->NAME.current == 0) {
		cpssp->NAME.current = cpssp->NAME.reload;

	} else {
		if (cpssp->NAME.current == 1) {
			cpssp->NAME.countflag = 1;
			if (cpssp->NAME.tickint) {
				NAME_(irq_set)(cpssp);
			}
		}
		cpssp->NAME.current--;
	}
#if 0
	fprintf(stderr, "%s: curr=%ld int=%d enabled=%d\n",
			__FUNCTION__,
			cpssp->NAME.current,
			cpssp->NAME.tickint,
			cpssp->NAME.enabled);
#endif
}

static void
NAME_(exttick)(struct cpssp *cpssp)
{
	if (cpssp->NAME.clksource == 0) {
		NAME_(tick)(cpssp);
	}
}

static void
NAME_(clk)(struct cpssp *cpssp)
{
	if (cpssp->NAME.clksource == 1) {
		NAME_(tick)(cpssp);
	}
}

static void
NAME_(reset)(struct cpssp *cpssp)
{
	cpssp->NAME.countflag = 0;
	cpssp->NAME.clksource = 1; /* Use core clock. */
	cpssp->NAME.tickint = 0;
	cpssp->NAME.reload = 0;
	cpssp->NAME.current = 0;
	cpssp->NAME.enabled = 0;
}

static void
NAME_(create)(struct cpssp *cpssp)
{
}

static void
NAME_(destroy)(struct cpssp *cpssp)
{
}

#endif /* BEHAVIOR */

#undef DEBUG_CONTROL_FLOW
