#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
#define TIMER_HZ		(1 << TIMER_SHIFT)

/* Basic system registers */
#define SYS_MAGIC		IODEVL(SYS,0)
#define SYS_BOARDCFG		IODEVL(SYS,1)
#define SYS_BOARDFIX		IODEVH0(SYS,1)
#define SYS_BOARDFIX_RTC32	1
#define SYS_BOARDREV		IODEVH1(SYS,1)
#define SYS_BOARDMINOR		IODEVB2(SYS,1)
#define SYS_BOARDMAJOR		IODEVB3(SYS,1)
#define SYS_LED			IODEVL(SYS,2)
#define SYS_RESET		IODEVL(SYS,3)

#define ROMCOPY_RAMADDR		IODEVL(ROMCOPY,0)
#define ROMCOPY_ROMADDR		IODEVL(ROMCOPY,1)
#define ROMCOPY_DATALEN		IODEVL(ROMCOPY,2)
#define ROMCOPY_STATUS		IODEVL(ROMCOPY,3)
#define ROMCOPY_STATUS_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_IRQEN	IODEVB1(SDCARD,0)
#define SDCARD_CTL_CLRCRC	IODEVB2(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)
#define SDCARD_IRQ_READY	1
#define SDCARD_IRQ_CD		2
#define SDCARD_IRQ_EXT		4

/* 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 I2C_WDATA		IODEVL(I2C,0)
#define I2C_WDATA_DATA		IODEVB1(I2C,0)
#define I2C_RDATA		IODEVL(I2C,1)
#define I2C_RDATA_DATA		IODEVB1(I2C,1)
#define I2C_BUSY		1
#define I2C_SR			2
#define I2C_P			4
#define I2C_DUMMY		6
#define I2C_STARTED		0x10
#define I2C_SCL			0x20
#define I2C_SDA			0x40
#define I2C_NAK			0x80
#define I2C_DIVISOR		IODEVL(I2C,2)

#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_RST		2
#define ABC_STATUS_800		4
#define ABC_IOSEL		IODEVL(ABC,1)
#define ABC_IOSEL_INVALID	0x100
#define ABC_BUSY		IODEVL(ABC,2)
#define ABC_BUSY_STATUS		IODEVH0(ABC,2)
#define ABC_BUSY_MASK		IODEVH1(ABC,2)
#define ABC_BUSY_OUT		0x00ff
#define ABC_BUSY_INP		0x0300
#define ABC_BUSY_BUSCHG		0xf000
#define ABC_BUSCTL		IODEVL(ABC,3)
#define ABC_BUSCTL_WAIT		1
#define ABC_BUSCTL_INT		2
#define ABC_BUSCTL_NMI		4
#define ABC_BUSCTL_RESET	8
#define ABC_OUT			IODEVL(ABC,4)
#define ABC_OUT_DATA		IODEVB0(ABC,4)
#define ABC_OUT_ADDR		IODEVB1(ABC,4)
#define ABC_INP			IODEVL(ABC,5)
#define ABC_INP0_DATA		IODEVB0(ABC,5)
#define ABC_INP1_DATA		IODEVB1(ABC,5)
#define ABC_INP_ENABLE		IODEVB2(ABC,5)

#define ABCMEMMAP_PAGE(n)	IODEVL(ABCMEMMAP,n)
#define ABCMEMMAP_WRPORT(n)	IODEVL(ABCMEMMAP,128+((n) << 1))
#define ABCMEMMAP_RDPORT(n)	IODEVL(ABCMEMMAP,129+((n) << 1))
#define ABCMEMMAP_WRCOUNT(n)	IODEVL(ABCMEMMAP,384+((n) << 1))
#define ABCMEMMAP_RDCOUNT(n)	IODEVL(ABCMEMMAP,385+((n) << 1))
#define ABCMEMMAP_WR		(1 << 30)
#define ABCMEMMAP_RD		(1 << 31)

#define ABCMEMMAP_STATUS(n)	IODEVL(ABCMEMMAP,512+(n))
#define ABCMEMMAP_CLR7DMA	0x800
#define ABCMEMMAP_CLR1WEMP	0x400
#define ABCMEMMAP_CLR0WEMP	0x200
#define ABCMEMMAP_CLR0REMP	0x100

#endif /* IODEV_H */
