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

/*
 * See: ATMEL ATmega328PB docs page 58 ff.
 */

#ifdef INCLUDE
#endif /* INCLUDE */
#ifdef STATE

struct {
	unsigned int cnt;

#if CONFIG_CLOCK_PRESCALER
	/* Clock Prescaler Register 52 */
	uint8_t clkpce;
	uint8_t clkps;
#endif

	/* Sleep Mode 63 */
	uint8_t sm;
	/* Sleep Enable 63 */
	uint8_t se;
} NAME;

#endif /* STATE */
#ifdef EXPORT

/*forward*/ static void                                       
NAME_(sm_set)(struct cpssp *cpssp, uint8_t val);                  
/*forward*/ static void                                       
NAME_(sm_get)(struct cpssp *cpssp, uint8_t *valp);                
/*forward*/ static void                                       
NAME_(se_set)(struct cpssp *cpssp, uint8_t val);                  
/*forward*/ static void                                       
NAME_(se_get)(struct cpssp *cpssp, uint8_t *valp);                
#if defined(CONFIG_ATMEGA328PB)                               
/*forward*/ static void                                       
NAME_(clkpr_set)(struct cpssp *cpssp, uint8_t val);               
/*forward*/ static void                                       
NAME_(clkpr_get)(struct cpssp *cpssp, uint8_t *valp);
#endif

#endif /* EXPORT */
#ifdef BEHAVIOR

/*
 * Sleep Mode Register 63
 */
static void
NAME_(sm_set)(struct cpssp *cpssp, uint8_t val)
{
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=0x%02x\n", __FUNCTION__, val);
	}

	cpssp->NAME.sm = val & 7;
}

static void
NAME_(sm_get)(struct cpssp *cpssp, uint8_t *valp)
{
	*valp = cpssp->NAME.sm;

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

/*
 * Sleep Enable Register 63
 */
static void
NAME_(se_set)(struct cpssp *cpssp, uint8_t val)
{
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: val=0x%02x\n", __FUNCTION__, val);
	}

	/* Bit 7-4: Reserved */
	cpssp->NAME.se = val & 1;
}

static void
NAME_(se_get)(struct cpssp *cpssp, uint8_t *valp)
{
	*valp = cpssp->NAME.se;

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

#if CONFIG_CLOCK_PRESCALER
/*
 * Clock Prescaler Register
 */
static void
NAME_(clkpr_set)(struct cpssp *cpssp, uint8_t val)
{
	if (cpssp->NAME.clkpce) {
		cpssp->NAME.clkps = val & 0xf;
		cpssp->NAME.clkpce = 0; /* FIXME */
	} else if (val == 0x80) {
		cpssp->NAME.clkpce = 1;
	}
}

static void
NAME_(clkpr_get)(struct cpssp *cpssp, uint8_t *valp)
{
	*valp = (cpssp->NAME.clkpce << 7)
		| (cpssp->NAME.clkps << 0);
}
#endif

static void
NAME_(clk_control)(struct cpssp *cpssp, unsigned int cnt, unsigned int div)
{
	cpssp->NAME.cnt += cnt;
	while (div <= cpssp->NAME.cnt) {
		NAME_(clk_io)(cpssp);
		NAME_(clk_adc)(cpssp);
		NAME_(clk_cpu)(cpssp);

		cpssp->NAME.cnt -= div;
	}
}

#if CONFIG_CLOCK_PRESCALER
static void
NAME_(clk_prescaler)(struct cpssp *cpssp, unsigned int cnt)
{
	NAME_(clk_control)(cpssp, cnt, 1 << cpssp->NAME.clkps);
}
#endif

#if 0
static void
NAME_(clk_ext)(struct cpssp *cpssp)
{
}
#endif

static void
NAME_(step)(struct cpssp *cpssp, unsigned int cnt)
{
	/* Clock Multiplexer */
	/* FIXME */
#if CONFIG_CLOCK_PRESCALER
	NAME_(clk_prescaler)(cpssp, cnt);
#else
	NAME_(clk_control)(cpssp, cnt, 1);
#endif
}

static void
NAME_(reset)(struct cpssp *cpssp)
{
#if CONFIG_CLOCK_PRESCALER
	/* Clock Prescaler Register 52 */
	cpssp->NAME.clkpce = 0;
	cpssp->NAME.clkps = 4; /* Depends on CKDIV8 fuse. FIXME */
#endif

	/* Sleep Mode Register 63 */
	cpssp->NAME.sm = 0;

	/* Sleep Enable Register 63 */
	cpssp->NAME.se = 0;
}

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

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

#endif /* BEHAVIOR */

#undef DEBUG_CONTROL_FLOW
