#include "fw.h"
#include "io.h"
#include "abcio.h"
#include "sys.h"
#include "console.h"

volatile uint32_t timer_irq_count;
IRQHANDLER(sysclock)
{
    uint32_t count = timer_irq_count;

    count++;
    timer_irq_count = count;
    if (!(count & ((1U << TIMER_SHIFT)-1)))
	CON_DATA = '.';		/* Console heartbeat */
}

static no_return killed(const char *how, size_t pc)
{
    con_printf("ERROR: %s at 0x%08x\n", how, pc);

    con_flush();
    udelay(5000000);
    reset();
}

IRQHANDLER(buserr)
{
    killed("misaligned", pc);
}

IRQHANDLER(ebreak)
{
    killed("invalid instruction", pc);
}

volatile uint32_t __dram_bss test_dram[8];

uint32_t timer_irq_start;
void init(void)
{
    static const char hello[] =
	"\n\n*** Hello, World! ***\n"
	"MAX80 "
#ifdef TEST
	"testing"
#endif
	"firmware compiled on: " __DATE__ " " __TIME__ "\n";

    timer_irq_start = rdtime();

    /* Start ROM copy engine, unmask timer and fatal exceptions */
    unmask_irqs((1U << ROMCOPY_IRQ)|(1U << EBREAK_IRQ)|
		(1U << BUSERR_IRQ)|(1U << SYSCLOCK_IRQ));

    set_leds(7);
    con_set_baudrate(115200);
    con_puts(hello);
    con_flush();

    /* Enable internal timer */
    unmask_irq(SYSCLOCK_IRQ);

    set_leds(6);
    wait_romcopy_done();
    set_leds(5);

    /* This needs to be done as early as possible!!! */
    abc_init_memmap();

    if (SYS_MAGIC != SYS_MAGIC_MAX80) {
	con_puts("Not a MAX80 board?!?!\n\n");
    } else {
	uint32_t boardcfg = SYS_BOARDCFG;
	con_printf("MAX80 ver %u.%u rework flags %04x\n\n",
		   (uint8_t)(boardcfg >> 24),
		   (uint8_t)(boardcfg >> 16),
		   (uint16_t)boardcfg);
    }

    con_puts("Quick DRAM test:\n");
    for (int i = 0; i < 8; i++) {
	uint32_t v = (i*0x11111111) + 0x44332211;
	test_dram[i] = v;
	con_printf("%08x ", v);
    }
    con_putc('\n');

    for (int i = 0; i < 8; i++) {
	con_printf("%08x ", test_dram[i]);
    }
    con_puts("\n\n");

    set_leds(4);

    read_rtc();
    rtc_abc_init();

    set_leds(3);

    disk_cache_init();
    abcdisk_init();

    set_leds(2);

    abc_init();

    set_leds(1);

    /* Release WAIT# if asserted */
    ABC_BUSCTL = 0;

    set_leds(0);
}
