/*
 * Copyright (C) 2016 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 docs page 201 ff.
 */

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

struct {
	unsigned int state_avcc;
	unsigned int state_aref;
	unsigned int state_in[8];

	/* ADCMUX 214 */
	uint8_t refs;
	uint8_t adlar;
	uint8_t mux;

	/* ADCSRA 216 */
	uint8_t aden;
	uint8_t adsc;
	uint8_t adate;
	uint8_t adif;
	uint8_t adie;
	uint8_t adps;

	/* ADCL/ADCH 217 */
	uint16_t adc;
} NAME;

#endif /* STATE */
#ifdef EXPORT

/*forward*/ static void
NAME_(mux_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(mux_get)(struct cpssp *cpssp, uint8_t *valp);
/*forward*/ static void
NAME_(sra_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(sra_get)(struct cpssp *cpssp, uint8_t *valp);
/*forward*/ static void
NAME_(h_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(h_get)(struct cpssp *cpssp, uint8_t *valp);
/*forward*/ static void
NAME_(l_set)(struct cpssp *cpssp, uint8_t val);
/*forward*/ static void
NAME_(l_get)(struct cpssp *cpssp, uint8_t *valp);
/*forward*/ static void
NAME_(0_in_set)(struct cpssp *cpssp, unsigned int val);
/*forward*/ static void
NAME_(1_in_set)(struct cpssp *cpssp, unsigned int val);
/*forward*/ static void
NAME_(2_in_set)(struct cpssp *cpssp, unsigned int val);
/*forward*/ static void
NAME_(3_in_set)(struct cpssp *cpssp, unsigned int val);
/*forward*/ static void
NAME_(4_in_set)(struct cpssp *cpssp, unsigned int val);
/*forward*/ static void
NAME_(5_in_set)(struct cpssp *cpssp, unsigned int val);
/*forward*/ static void
NAME_(6_in_set)(struct cpssp *cpssp, unsigned int val);
/*forward*/ static void
NAME_(7_in_set)(struct cpssp *cpssp, unsigned int val);

#endif /* EXPORT */
#ifdef BEHAVIOR

static void
NAME_(start)(struct cpssp *cpssp)
{
	unsigned int valp;
	unsigned int valm;
	unsigned int res;

	switch ((cpssp->NAME.mux >> 3) & 0x3) {
	case 0b00: /* Single Ended */
		valp = cpssp->NAME.state_in[cpssp->NAME.mux & 0x7];
		valm = 0;
		break;

	case 0b01: /* With Gain */
		assert(0); /* FIXME */
		break;

	case 0b10:
	case 0b11: /* ??? */
		assert(0); /*  FIXME */
		break;

	default:
		assert(0); /* Cannot happen. */
	}

	res = valp - valm;
	res *= 0xffff;
	res /= cpssp->NAME.state_aref;

	cpssp->NAME.adc = res & 0xffc0;
	cpssp->NAME.adif = 1;
	if (cpssp->NAME.adie) {
		/* ... */
	}
}

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

	cpssp->NAME.refs = (val >> 6) & 0x3;
	cpssp->NAME.adlar = (val >> 5) & 0x1;
	cpssp->NAME.mux = val & 0x1f;
}

static void
NAME_(mux_get)(struct cpssp *cpssp, uint8_t *valp)
{
	*valp = 0;
	*valp |= cpssp->NAME.refs << 6;
	*valp |= cpssp->NAME.adlar << 5;
	*valp |= cpssp->NAME.mux;

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

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

	cpssp->NAME.aden = (val >> 7) & 0x1;
	if ((val >> 6) & 0x1) {
		NAME_(start)(cpssp);
	}
	cpssp->NAME.adate = (val >> 5) & 0x1;
	cpssp->NAME.adif &= ~((val >> 4) & 0x1); /* Write-Clear */
	cpssp->NAME.adie = (val >> 3) & 0x1;
	cpssp->NAME.adps = val & 0x7;
}

static void
NAME_(sra_get)(struct cpssp *cpssp, uint8_t *valp)
{
	*valp = 0;
	*valp |= cpssp->NAME.aden << 7;
	*valp |= 0 << 6;
	*valp |= cpssp->NAME.adate << 5;
	*valp |= cpssp->NAME.adif << 4;
	*valp |= cpssp->NAME.adie << 3;
	*valp |= cpssp->NAME.adps;

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

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

	/* Read only */
}

static void
NAME_(h_get)(struct cpssp *cpssp, uint8_t *valp)
{
	if (cpssp->NAME.adlar) {
		/* Left adjusted. */
		*valp = (cpssp->NAME.adc >> 8) & 0xff;
	} else {
		/* Right adjusted. */
		*valp = (cpssp->NAME.adc >> (8+6)) & 0xff;
	}

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

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

	/* Read only */
}

static void
NAME_(l_get)(struct cpssp *cpssp, uint8_t *valp)
{
	if (cpssp->NAME.adlar) {
		/* Left adjusted. */
		*valp = (cpssp->NAME.adc >> 0) & 0xff;
	} else {
		/* Right adjusted. */
		*valp = (cpssp->NAME.adc >> (0+6)) & 0xff;
	}

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

static void
NAME_(aref_in_set)(struct cpssp *cpssp, unsigned int val)
{
	cpssp->NAME.state_aref = SIG_mV(val);
}

static void
NAME_(avcc_in_set)(struct cpssp *cpssp, unsigned int val)
{
	cpssp->NAME.state_avcc = SIG_mV(val);
}

static void
NAME_(inN_set)(struct cpssp *cpssp, int n, unsigned int val)
{
	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d, val=%umV\n", __FUNCTION__,
				n, SIG_mV(val));
	}

	cpssp->NAME.state_in[n] = SIG_mV(val);

	if (DEBUG_CONTROL_FLOW) {
		fprintf(stderr, "%s: n=%d val=%umV\n", __FUNCTION__,
				n, cpssp->NAME.state_in[n]);
	}
}

static void
NAME_(0_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 0, val);
}

static void
NAME_(1_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 1, val);
}

static void
NAME_(2_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 2, val);
}

static void
NAME_(3_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 3, val);
}

static void
NAME_(4_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 4, val);
}

static void
NAME_(5_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 5, val);
}

static void
NAME_(6_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 6, val);
}

static void
NAME_(7_in_set)(struct cpssp *cpssp, unsigned int val)
{
	NAME_(inN_set)(cpssp, 7, val);
}

static void
NAME_(create)(struct cpssp *cpssp)
{
	cpssp->NAME.aden = 0;
}

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

#endif /* BEHAVIOR */

#undef DEBUG_CONTROL_FLOW
