#ifndef IODEV_H
#define IODEV_H

#include "iodevs.h"

/* Address for I/O device d, subregister r, offset o */
#define IODEVA(b,r,o)   ((b ## _BASE)+((r) << 2)+(o))

#ifdef __ASSEMBLY__

/*
 * The I/O device range is designed so that it can be addressed via
 * negative offsets from the zero register, so no explicit base
 * pointer register is necesary.
 */
#define IODEVV(d,r) IODEVA(d,r,0)(zero)
#define IODEVB(d,r) IODEVV(d,r,0)
#define IODEVH(d,r) IODEVV(d,r,0)
#define IODEVL(d,r) IODEVV(d,r,0)

#else

#include <stdint.h>

/* Writable registers */
#define IODEVV(d,r)   (*(volatile void     *)IODEVA(d,r,0))
#define IODEVB(d,r)   (*(volatile uint8_t  *)IODEVA(d,r,0))
#define IODEVB0(d,r)  (*(volatile uint8_t  *)IODEVA(d,r,0))
#define IODEVB1(d,r)  (*(volatile uint8_t  *)IODEVA(d,r,1))
#define IODEVB2(d,r)  (*(volatile uint8_t  *)IODEVA(d,r,2))
#define IODEVB3(d,r)  (*(volatile uint8_t  *)IODEVA(d,r,3))
#define IODEVH(d,r)   (*(volatile uint16_t *)IODEVA(d,r,0))
#define IODEVH0(d,r)  (*(volatile uint16_t *)IODEVA(d,r,0))
#define IODEVH1(d,r)  (*(volatile uint16_t *)IODEVA(d,r,2))
#define IODEVL(d,r)   (*(volatile uint32_t *)IODEVA(d,r,0))

/* Readonly registers */
#define IODEVRV(d,r)  (*(const volatile void     *)IODEVA(d,r,0))
#define IODEVRB(d,r)  (*(const volatile uint8_t  *)IODEVA(d,r,0))
#define IODEVRB0(d,r) (*(const volatile uint8_t  *)IODEVA(d,r,0))
#define IODEVRB1(d,r) (*(const volatile uint8_t  *)IODEVA(d,r,1))
#define IODEVRB2(d,r) (*(const volatile uint8_t  *)IODEVA(d,r,2))
#define IODEVRB3(d,r) (*(const volatile uint8_t  *)IODEVA(d,r,3))
#define IODEVRH(d,r)  (*(const volatile uint16_t *)IODEVA(d,r,0))
#define IODEVRH0(d,r) (*(const volatile uint16_t *)IODEVA(d,r,0))
#define IODEVRH1(d,r) (*(const volatile uint16_t *)IODEVA(d,r,2))
#define IODEVRL(d,r)  (*(const volatile uint32_t *)IODEVA(d,r,0))

#endif

#define CPU_HZ			84000000

/* Basic system registers */
#define SYS_MAGIC		IODEVL(SYS,0)
#define SYS_BOARDCFG		IODEVL(SYS,1)
#define SYS_LED			IODEVL(SYS,2)
#define SYS_RESET		IODEVL(SYS,3)
#define SYS_ROMCOPY		IODEVL(SYS,4)
#define SYS_ROMCOPY_DONE	1

#define CON_DATA		IODEVB(CONSOLE,0)
#define CON_BAUDDIV		IODEVL(CONSOLE,1)
#define CON_BAUD_BASE		(CPU_HZ >> 4)
#define CON_BAUD_BITS		24
#define CON_STATUS		IODEVRL(CONSOLE,2)
#define CON_IRQEN		IODEVL(CONSOLE,3)

#define SDCARD_CTL		IODEVL(SDCARD,0)
#define SDCARD_CTL_SPEED	IODEVB0(SDCARD,0)
#define SDCARD_CTL_CLRCRC	IODEVB1(SDCARD,0)
#define SDCARD_CRC7_RD		IODEVRB0(SDCARD,4)
#define SDCARD_CRC16_RD		IODEVRH1(SDCARD,4)
#define SDCARD_CRC7_WR		IODEVRB0(SDCARD,5)
#define SDCARD_CRC16_WR		IODEVRH1(SDCARD,5)

/* Speed values, not including -1 adjustment */
#define SD_SLOW		128	/* 328 kHz */
#define SD_20MHZ	3	/* Really 14 MHz */
#define SD_25MHZ	2	/* Really 21 MHz */
#define SD_50MHZ	1	/* Really 42 MHz */

#define SYSCLOCK_DATETIME	IODEVL(SYSCLOCK,0)
#define SYSCLOCK_TICK		IODEVL(SYSCLOCK,1)
#define SYSCLOCK_TICK_HOLD	IODEVH0(SYSCLOCK,1)
#define SYSCLOCK_TICK_NOW	IODEVH1(SYSCLOCK,1)

#define ABC_STATUS		IODEVL(ABC,0)
#define ABC_STATUS_LIVE		1
#define ABC_STATUS_800		2

#define ABCMEMMAP_PAGE(n)	IODEVL(ABCMEMMAP,n)
#define ABCMEMMAP_WR		0x10000
#define ABCMEMMAP_RD		0x20000

#endif /* IODEV_H */
