/*
 * 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: ATmega328PB docs page 217 ff.
 */

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

struct {
	uint8_t state_mosi;
	uint8_t state_miso;

	/* Control Register */
	uint8_t spie;
	uint8_t spe;
	uint8_t dord;
	uint8_t mstr;
	uint8_t cpol;
	uint8_t cpha;
	uint8_t spr;

	/* Status Register */
	uint8_t spif;
	uint8_t wcol;
	/* uint8_t spr; see above. */

	/* Data Register */
	uint8_t dr;

	/* Prescaler */
	uint8_t cnt;

	/* State */
	int8_t state;
} NAME;

#endif /* STATE */
#ifdef EXPORT

/*forward*/ static void                                       
NAME_(spcr_set)(struct cpssp *cpssp, uint8_t val);              
/*forward*/ static void                                       
NAME_(spsr_set)(struct cpssp *cpssp, uint8_t val);              
/*forward*/ static void                                       
NAME_(spdr_set)(struct cpssp *cpssp, uint8_t val);              
/*forward*/ static void                                       
NAME_(spcr_get)(struct cpssp *cpssp, uint8_t *valp);            
/*forward*/ static void                                       
NAME_(spsr_get)(struct cpssp *cpssp, uint8_t *valp);            
/*forward*/ static void                                       
NAME_(spdr_get)(struct cpssp *cpssp, uint8_t *valp);            
/*forward*/ static void                                       
NAME_(ack)(struct cpssp *cpssp);                                
/*forward*/ static void                                       
NAME_(miso_in_set)(struct cpssp *cpssp, unsigned int val);      
/*forward*/ static void                                       
NAME_(step)(struct cpssp *cpssp);

#endif /* EXPORT */
#ifdef BEHAVIOR

static void
NAME_(update_irq)(struct cpssp *cpssp)
{
	int irq;

	irq = cpssp->NAME.spif & cpssp->NAME.spie;
	NAME_(irq_set)(cpssp, irq);
}

static void
NAME_(update_master)(struct cpssp *cpssp)
{
	if (! cpssp->NAME.spe) {
		/* Disabled */
		NAME_(mosi_out_set)(cpssp, SIG_STD_LOGIC_Z);
		NAME_(sck_out_set)(cpssp, SIG_STD_LOGIC_Z);

	} else if (cpssp->NAME.state == -1
		|| cpssp->NAME.state == 16) {
		/* Idle/Done State */
		NAME_(mosi_out_set)(cpssp, SIG_STD_LOGIC_1);
		NAME_(sck_out_set)(cpssp,
				cpssp->NAME.cpol
				? SIG_STD_LOGIC_1 : SIG_STD_LOGIC_0);

	} else if (cpssp->NAME.state & 1) {
		NAME_(sck_out_set)(cpssp,
				cpssp->NAME.cpol ^ cpssp->NAME.cpha ^ 1
				? SIG_STD_LOGIC_1 : SIG_STD_LOGIC_0);
	} else {
		NAME_(mosi_out_set)(cpssp,
				cpssp->NAME.state_mosi
				? SIG_STD_LOGIC_1 : SIG_STD_LOGIC_0);
		NAME_(sck_out_set)(cpssp,
				cpssp->NAME.cpol ^ cpssp->NAME.cpha
				? SIG_STD_LOGIC_1 : SIG_STD_LOGIC_0);
	}
}

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

	cpssp->NAME.spie = (val >> 7) & 1;
	cpssp->NAME.spe = (val >> 6) & 1;
	cpssp->NAME.dord = (val >> 5) & 1;
	cpssp->NAME.mstr = (val >> 4) & 1;
	cpssp->NAME.cpol = (val >> 3) & 1;
	cpssp->NAME.cpha = (val >> 2) & 1;
	cpssp->NAME.spr &= ~0b110;
	cpssp->NAME.spr |= ((val >> 0) & 0b11) << 1;

	NAME_(update_irq)(cpssp);
	NAME_(update_master)(cpssp);
}

static void
NAME_(spcr_get)(struct cpssp *cpssp, uint8_t *valp)
{
	*valp = 0;
	*valp |= cpssp->NAME.spie << 7;
	*valp |= cpssp->NAME.spe << 6;
	*valp |= cpssp->NAME.dord << 5;
	*valp |= cpssp->NAME.mstr << 4;
	*valp |= cpssp->NAME.cpol << 3;
	*valp |= cpssp->NAME.cpha << 2;
	*valp |= ((cpssp->NAME.spr >> 1) & 0b11) << 0;

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

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

	/* Bit 7-6: Read-only */
	/* Bit 5-1: Reserved */
	cpssp->NAME.spr &= ~0b001;
	cpssp->NAME.spr |= ((val >> 0) & 1) ^ 1;
}

static void
NAME_(spsr_get)(struct cpssp *cpssp, uint8_t *valp)
{
	*valp = 0;
	*valp |= cpssp->NAME.spif << 7;
	*valp |= cpssp->NAME.wcol << 6;
	/* Bit 5-1: Reserved */
	*valp |= ((cpssp->NAME.spr & 0b001) ^ 1) << 0;

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

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

	cpssp->NAME.spif = 0;
	NAME_(update_irq)(cpssp);

	assert(cpssp->NAME.state == -1); /* FIXME */

	cpssp->NAME.dr = val;
	cpssp->NAME.state = 0;
}

static void
NAME_(spdr_get)(struct cpssp *cpssp, uint8_t *valp)
{
	assert(cpssp->NAME.state == -1); /* FIXME */

	cpssp->NAME.spif = 0;
	NAME_(update_irq)(cpssp);

	*valp = cpssp->NAME.dr;

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

static void
NAME_(ack)(struct cpssp *cpssp)
{
	cpssp->NAME.spif = 0;
	NAME_(update_irq)(cpssp);
}

static void
NAME_(step2)(struct cpssp *cpssp)
{
	switch (cpssp->NAME.state) {
	case -1: /* Idle */
		break;

	case 0: case 2: case 4: case 6:
	case 8: case 10: case 12: case 14:
		/* Set MOSI, get MISO. */
		if (cpssp->NAME.dord) {
			/* LSB first. */
			cpssp->NAME.state_mosi = (cpssp->NAME.dr >> 0) & 1;
			cpssp->NAME.dr >>= 1;
			cpssp->NAME.dr |= cpssp->NAME.state_miso << 7;
		} else {
			/* MSB first. */
			cpssp->NAME.state_mosi = (cpssp->NAME.dr >> 7) & 1;
			cpssp->NAME.dr <<= 1;
			cpssp->NAME.dr |= cpssp->NAME.state_miso << 0;
		}

		NAME_(update_master)(cpssp);

		cpssp->NAME.state++;
		break;

	case 1: case 3: case 5: case 7:
	case 9: case 11: case 13: case 15:
		/* Set SCK. */
		NAME_(update_master)(cpssp);

		cpssp->NAME.state++;
		break;

	case 16: /* Done */
		NAME_(update_master)(cpssp);

		cpssp->NAME.spif = 1;
		NAME_(update_irq)(cpssp);

		cpssp->NAME.state = -1;
		break;

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

static void
NAME_(step)(struct cpssp *cpssp)
{
	if (cpssp->NAME.cnt == 0) {
		NAME_(step2)(cpssp);
		cpssp->NAME.cnt = 1 << cpssp->NAME.spr;
	}
	cpssp->NAME.cnt--;
}

static void
NAME_(miso_in_set)(struct cpssp *cpssp, unsigned int val)
{
	cpssp->NAME.state_miso = 2500 <= SIG_mV(val);
}

static void
NAME_(reset)(struct cpssp *cpssp)
{
	/* Control Register */
	cpssp->NAME.spie = 0;
	cpssp->NAME.spe = 0;
	cpssp->NAME.dord = 0;
	cpssp->NAME.mstr = 0;
	cpssp->NAME.cpol = 0;
	cpssp->NAME.cpha = 0;
	cpssp->NAME.spr = 0b001;

	/* Status Register */
	cpssp->NAME.spif = 0;
	cpssp->NAME.wcol = 0;
	/* spr: see above. */

	/* Data Register */
	cpssp->NAME.dr = 0;

	/* Prescaler */
	cpssp->NAME.cnt = 0;

	/* State */
	cpssp->NAME.state = -1;
}

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

	if (0) {
		NAME_(ss_out_set)(cpssp, 0);
		NAME_(mosi_out_set)(cpssp, 0);
		NAME_(sck_out_set)(cpssp, 0);
	}
}

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

#endif /* BEHAVIOR */

#undef DEBUG_CONTROL_FLOW
