#include "board.hpp"
#include <avr/io.h>
#include <avr/fuse.h>

FUSES = {
	.WDTCFG = 0x00,
	.BODCFG = ACTIVE_DISABLE_gc,
	.OSCCFG = CLKSEL_OSCHF_gc,
	.reserved_1 = {0x00, 0x00},
	.SYSCFG0 = CRCSRC_NOCRC_gc,
	.SYSCFG1 = MVSYSCFG_DUAL_gc,
	.CODESIZE = 0x00,
	.BOOTSIZE = 0x00
};

spi_avrdx<SPI_SDCARD> spi_sdcard;

void board_pins_init()
{
	// We have a lot of analog inputs. Just disable ALL input buffers, then
	// go back and enable the ones we care about.

	PORTA.PINCONFIG = PORT_ISC_INPUT_DISABLE_gc; // Mirrored to all PORTx
	PORTA.PINCTRLUPD = 0xFF;
	PORTB.PINCTRLUPD = 0; // This is PORT_ADDR
	PORTC.PINCTRLUPD = 0xFF;
	PORTD.PINCTRLUPD = 0xFF;
	PORTE.PINCTRLUPD = 0xFF;
	PORTF.PINCTRLUPD = 0xFF;
	PORTG.PINCTRLUPD = 0xFF;
	PINCTRL_FOR(PIN_GATE_IN) = 0;
	PINCTRL_FOR(PIN_INTMIDI_RX) = 0;
	PINCTRL_FOR(PIN_USBMIDI_RX) = 0;
	PINCTRL_FOR(PIN_MIDI_RX) = 0;

	PIN_DRIVE_TO(PIN_DEBUG0, 0);
	PIN_DRIVE_TO(PIN_DEBUG1, 0);
	PIN_DRIVE_TO(PIN_DEBUG2, 0);
	PIN_DRIVE_TO(PIN_DEBUG3, 0);

	// Drive all the chip selects high
	PIN_DRIVE_TO(PIN_MIXER_nCS, 1);
	PIN_DRIVE_TO(PIN_LFO_nCS,   1);
	PIN_DRIVE_TO(PIN_SD_nCS,    1);
	PIN_DRIVE_TO(PIN_nVS,       1);

	// Drive other outputs to decent defaults
	PIN_DRIVE_TO(PIN_INTMIDI_TX, 1);
	PIN_DRIVE_TO(PIN_USBMIDI_TX, 1);
	PIN_DRIVE_TO(PIN_MIDI_TX, 1);
	PIN_DRIVE_TO(PIN_COPI,    0);
	PIN_DRIVE_TO(PIN_SCK,     0);
	PINCTRL_FOR(PIN_CIPO) = PORT_PULLUPEN_bm;
	PINCTRL_FOR(PIN_SD_CIPO) = PORT_PULLUPEN_bm;

	// Set the alternates and inversions (we invert usbmidi.rx to allow
	// detection of cable disconnects - because the opto is ON by default,
	// depowering the USB region will generate a break)
	//
	// USART2 -> PF4,5 and invert PF5
	// SPI0   -> PG4-7
	PORTMUX.USARTROUTEA = PORTMUX_USART2_ALT1_gc;
	PINCTRL_FOR(PIN_USBMIDI_RX) = PORT_INVEN_bm;
	PORTMUX.SPIROUTEA = PORTMUX_SPI0_ALT2_gc;

	// Set up the clocks
	_PROTECTED_WRITE(CLKCTRL.XOSCHFCTRLA,
		CLKCTRL_RUNSTDBY_bm |
		CLKCTRL_CSUTHF_4K_gc | CLKCTRL_FRQRANGE_24M_gc
		| CLKCTRL_ENABLE_bm);

	while (!(CLKCTRL.MCLKSTATUS & CLKCTRL_EXTS_bm));

	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_EXTCLK_gc);
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0); // No prescaler
}

void board_peripherals_init()
{
	spi_sdcard.init(spi_sdcard.clockdiv::div2, true, 0);
}
