#ifndef IO_H
#define IO_H

#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#include "ioregs.h"

static inline void pause(void)
{
    /* Placeholder for anything that might want to be done while waiting */
}

static inline void set_led(uint8_t leds)
{
    SYS_LED = leds;
}

static inline __attribute__((noreturn)) void reset(void)
{
    while (1)
	SYS_RESET = 1;
}

extern const uint32_t time_zero;

static inline uint32_t rdtime(void)
{
    uint32_t t;

    asm volatile("rdtime %0" : "=r" (t));
    return t;
}

static inline uint64_t rdtimeq(void)
{
    uint32_t l, h1, h0;

    asm volatile("rdtimeh %0; rdtime %1; %rdtimeh %2"
		 : "=r" (h1), "=r" (l), "=r" (h0));

    return ((uint64_t)(((int32_t)l < 0) ? h1 : h0) << 32) + l;
}

static inline void udelay(uint32_t us)
{
    uint32_t cycles = us * (CPU_HZ / 1000000);
    uint32_t start = rdtime();

    while (rdtime() - start < cycles)
	pause();
}

static inline void sd_set_mode(uint8_t divisor, bool cs)
{
    SDCARD_CTL_SPEED = (divisor - 1) | (cs << 7);
}

/* Read/write SD card and start transaction */
enum sd_data_flags {
    SD_B0       = 0x00,		/* Byte offset 0 */
    SD_B1       = 0x01,		/* Byte offset 1 */
    SD_B2       = 0x02,		/* Byte offset 2 */
    SD_B3       = 0x03,		/* Byte offset 3 */
    SD_GO8      = 0x04,		/* Start 8-bit transaction */
    SD_GO16     = 0x08,		/* Start 16-bit transaction */
    SD_GO32     = 0x0c,		/* Start 32-bit transaction */
    SD_BE       = 0x10,		/* Bigendian data */
    SD_DATA	= 0x20,		/* Data register access (assumed) */
    SD_CLEARCRC = 0x40		/* Clear CRC registers */
};

static inline void sd_writeb(uint8_t d, enum sd_data_flags flags)
{
    flags ^= (flags & SD_BE) ? 3 : 0;
    *(volatile uint8_t *)IODEVA(SDCARD,0,flags | SD_DATA) = d;
}
static inline void sd_writeh(uint16_t d, enum sd_data_flags flags)
{
    flags ^= (flags & SD_BE) ? 2 : 0;
    *(volatile uint16_t *)IODEVA(SDCARD,0,flags | SD_DATA) = d;
}
static inline void sd_writel(uint32_t d, enum sd_data_flags flags)
{
    *(volatile uint32_t *)IODEVA(SDCARD,0,flags | SD_DATA) = d;
}
static inline uint8_t sd_readb(enum sd_data_flags flags)
{
    flags ^= (flags & SD_BE) ? 0 : 3;
    return *(volatile uint8_t *)IODEVA(SDCARD,0,flags | SD_DATA);
}
static inline uint16_t sd_readh(enum sd_data_flags flags)
{
    flags ^= (flags & SD_BE) ? 0 : 2;
    return *(volatile uint16_t *)IODEVA(SDCARD,0,flags | SD_DATA);
}
static inline uint32_t sd_readl(enum sd_data_flags flags)
{
    return *(volatile uint32_t *)IODEVA(SDCARD,0,flags | SD_DATA);
}

static inline uint8_t sd_crc7_rd(void)
{
    return SDCARD_CRC7_RD;
}
static inline uint8_t sd_crc7_wr(void)
{
    return SDCARD_CRC7_WR;
}
static inline uint16_t sd_crc16_rd(void)
{
    return SDCARD_CRC16_RD;
}
static inline uint8_t sd_crc16_wr(void)
{
    return SDCARD_CRC16_WR;
}


#endif /* IO_H */
