#if defined(__linux__)

#include	<linux/kernel.h>
#include	<linux/delay.h>
#include	<linux/sched.h>

#include	"dagreg.h"
#include	"dagdrv.h"

#elif defined(__FreeBSD__)

#include        <sys/types.h>
#include        <sys/time.h>
#include        <sys/systm.h>
#include        <sys/param.h>
#include        <sys/proc.h>
#include        <sys/kernel.h>
#include	<sys/malloc.h>

#include	<machine/bus.h>
#include	<machine/resource.h>

#include        <sys/bus.h>
#include	<sys/rman.h>

#if (__FreeBSD_version < 504000)
#include        <pci/pcivar.h>
#else
#include <dev/pci/pcivar.h>
#endif

#include	<sys/callout.h>
#include	"dagreg.h"
#include	"dagdrv.h"

#endif  /* FreeBSD */

#include	"dagduck.h"
#include	"dagmon.h"

#if defined(__linux__)
#  define DELAY(X)	udelay(X)
#elif defined(__FreeBSD__)
#  define DELAY(X)
#  define mmio iomres->r_virtual
#endif /* FreeBSD */

/*
 * Dag ARM monitor definitions
 */
#define	DAGMON_PROTOCOL		0x01000000
#define	DAGMON_START		(sc->monseq = ToHM0)
#define	DAGMON_SYNC		(ToAM0 = sc->monseq)
#define	DAGMON_READY		(ToHM0 == sc->monseq)
#define	DAGMON_NEXT		(++sc->monseq)
#define	DAGMON_BUSY		(!(DAGMON_READY))

/*
 * Mailbox access macros
 */
#if defined(__linux__) || (defined(__FreeBSD__) && (__FreeBSD_version < 504000))

#define	ToHM0	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x00))
#define	ToHM1	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x04))
#define	ToHM2	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x08))
#define	ToHM3	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x0c))

#define	ToAM0	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x40))
#define	ToAM1	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x44))
#define	ToAM2	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x48))
#define	ToAM3	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x4c))

#define	ToHDB	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x80))
#define	IntEn	(*(volatile uint32_t *)((u_char *)sc->mmio + 0x8c))

#elif defined(__FreeBSD__) && (__FreeBSD_version >= 504000)

#define	ToHM0	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x00))
#define	ToHM1	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x04))
#define	ToHM2	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x08))
#define	ToHM3	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x0c))

#define	ToAM0	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x40))
#define	ToAM1	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x44))
#define	ToAM2	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x48))
#define	ToAM3	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x4c))

#define	ToHDB	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x80))
#define	IntEn	(*(volatile uint32_t *)((u_char *)rman_get_virtual(sc->iomres) + 0x8c))
#endif /* Linux */


int
dagmon_ready(dag_softc_t *sc)
{
	return DAGMON_READY;
}

int
dagmon_init(dag_softc_t *sc)
{
	DAGMON_START;
	return 0;
}

int
dagmon(dag_softc_t *sc, int command, int *param)
{
	volatile uint32_t	*up;
	int		i, error;

	if((command < DAGMON_MIN) || (command > DAGMON_MAX) || (param == NULL))
		return EINVAL;

	if(DAGMON_BUSY)
		return EAGAIN;	/* monitor busy executing another command */

	/*
	 * Initialize command to be executed
	 */
	switch(command) {
	  case DAGMON_VERSION:
		break;
	  case DAGMON_READ_WORD:
		ToAM2 = param[0];	/* contains address */
		break;
	  case DAGMON_WRITE_WORD:
		ToAM2 = param[0];	/* contains address */
		ToAM3 = param[1];	/* contains datum */
		break;
	  case DAGMON_PROGRAM_XILINX:
		ToAM2 = param[0];	/* contains address or zero for default */
		ToAM3 = param[1];	/* contains Xilinx number, ignored by old monitors */
		break;
	  case DAGMON_ENTER_ADDRESS:
		ToAM2 = param[0];	/* contains address */
		break;
	  case DAGMON_READ_12_WORDS:
		ToAM2 = param[0];	/* contains address */
		break;
	  case DAGMON_WRITE_12_WORDS:
					/* pass address, then 12 words of data */
		for( up = &ToAM2, i = 0 ; i < 13 ; up++, i++ )
			*up = param[i];
		break;
	  default:
		return EINVAL;
	}

	ToAM1 = (command | DAGMON_PROTOCOL);	/* load command */
	ToAM0 = DAGMON_NEXT;			/* do it */

	/* monitor now busy */

	/*
	 * Check for asynchronous (non-blocking) commands
	 */
	switch(command) {
	  case DAGMON_PROGRAM_XILINX:
		if(param[1] != 0)		/* load first Xilinx */
		      break;
		error = dag_pciconfig_restore(sc);
		DAGMON_SYNC;			/* this time the other way around */
		IntEn = 0xffff;
		return error;
		  
	  case DAGMON_ENTER_ADDRESS:
		return 0;			/* do block for results to be returned */
						/* XXX we should rather have a nonblock flag */
	  default:
		break;
	}

	/*
	 * Wait for ARM to finish execution
	 */
	for( i = 0 ; i < 10 ; i++ ) {
		if(DAGMON_READY)
			goto ready;
		DELAY(10);
	}
	for( i = 0 ; i < 1000 ; i++ ) {
		if(DAGMON_READY)
			goto ready;
# ifdef __linux__
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(HZ/100);
# else
		error = tsleep(&sc->monseq, (PUSER|PCATCH), "dagmon", hz/100);
		switch(error) {
		  case 0:
		  case EAGAIN:
			break;
		  default:
			return error;
		}
# endif
	}
	return EIO;		/* ARM not responding after 10 seconds */

ready:
	switch(command) {
	  case DAGMON_VERSION:
		param[0] = ToHM2;
		break;
	  case DAGMON_READ_WORD:
		param[1] = ToHM2;
		break;
	  case DAGMON_READ_12_WORDS:
		for( up = &ToHM3, i = 1 ; i < 13 ; up++, i++ )
			param[i] = *up;
		break;
	  case DAGMON_PROGRAM_XILINX:
	        duck_init(sc);
		break;
	  default:
		break;
	}

	return 0;
}
