#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/device.h>

#include <machine/cpu.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/bswap.h>

#include <amiga/amiga/cia.h>
#include <amiga/amiga/custom.h>
#include <amiga/amiga/device.h>
#include <amiga/dev/zbusvar.h>

#include <dev/ata/atavar.h>
#include <dev/ic/wdcvar.h>

#define FASTATA4K_MAX_CHANNELS 2

	/* we're accesing the bus with stride 4
	   so multiply any address by 4! */

#define FASTATA4K_REG_SIZE 0x400	/* 0x1000 */
#define FASTATA4K_OFF_PIO0 0x0		/* 0x0 */
#define FASTATA4K_OFF_PIO3 0x800	/* 0x2000 */
#define FASTATA4K_OFF_PIO4 0x1800	/* 0x6000 */

#define FASTATA4K_OFF_DATAL   0x0 
#define FASTATA4K_OFF_ERROR   0x80 /* error/feature */
#define FASTATA4K_OFF_DATA    0xC0
#define FASTATA4K_OFF_SECCNT  0x100
#define FASTATA4K_OFF_INTSTAT 0x140 /* int status (bit 7 set if int. set on any port) */
#define FASTATA4K_OFF_SECTOR  0x180
#define FASTATA4K_OFF_CYL_LO  0x200
#define FASTATA4K_OFF_CYL_HI  0x280
#define FASTATA4K_OFF_SDH     0x300 /* device/head? 101nNNNN is this standard? */
#define FASTATA4K_OFF_COMMAND 0x380 /* status/command */

struct wdc_fastata4k_softc {
	struct wdc_softc sc_wdcdev;
	struct ata_channel *wdc_chanarray[FASTATA4K_MAX_CHANNELS];
	struct ata_channel channels[FASTATA4K_MAX_CHANNELS];
	struct bus_space_tag sc_iot;
	struct isr sc_isr;
	volatile char *ba;
};

int	wdc_fastata4k_probe(device_t, cfdata_t, void *);
void	wdc_fastata4k_attach(device_t, device_t, void *);
/*int	wdc_fastata4k_intr(void *);*/
void	wdc_fastata4k_dumpregs(bus_space_tag_t iot, bus_space_handle_t ioh, struct wdc_regs *wdr);

CFATTACH_DECL_NEW(wdc_fastata4k, sizeof(struct wdc_fastata4k_softc),
    wdc_fastata4k_probe, wdc_fastata4k_attach, NULL, NULL);

int
wdc_fastata4k_probe(device_t parent, cfdata_t cfp, void *aux)
{
	struct zbus_args *zap;

	zap = aux;

	if (zap->manid != 2206)
		return 0;

	if ((zap->prodid != 25) &&	/* FastATA 4000 Mk-I         */
	    (zap->prodid != 29) &&	/* FastATA 4000 Mk-II/Mk-III */
	    (zap->prodid != 62))	/* FastATA 4000 Mk-V         */
		return 0;

	return 1;
}

void 
wdc_fastata4k_dumpregs(bus_space_tag_t iot, bus_space_handle_t ioh, struct wdc_regs *wdr) {
	uint8_t error, seccnt, intstat, sector, cyl_lo, cyl_hi, sdh, command;
	uint16_t datal, data; /* data == datal in pio0? */

	datal = bus_space_read_2(iot, ioh, FASTATA4K_OFF_DATAL);
	error = bus_space_read_1(iot, ioh, FASTATA4K_OFF_ERROR);
	data = bus_space_read_2(iot, ioh, FASTATA4K_OFF_DATA);
	seccnt = bus_space_read_1(iot, ioh, FASTATA4K_OFF_SECCNT);
	intstat = bus_space_read_1(iot, ioh, FASTATA4K_OFF_INTSTAT);
	sector = bus_space_read_1(iot, ioh, FASTATA4K_OFF_SECTOR);
	cyl_lo = bus_space_read_1(iot, ioh, FASTATA4K_OFF_CYL_LO);
	cyl_hi = bus_space_read_1(iot, ioh, FASTATA4K_OFF_CYL_HI);
	sdh = bus_space_read_1(iot, ioh, FASTATA4K_OFF_SDH);
	command = bus_space_read_1(iot, ioh, FASTATA4K_OFF_COMMAND);
	/* print what do we have here */
	aprint_normal("FastATA regs: %x %x %x %x %x %x %x %x %x %x\n", 
		datal, error, data, seccnt, sector, cyl_lo, cyl_hi, sdh, command, intstat);

	data = bus_space_read_2(wdr->cmd_iot, wdr->cmd_iohs[wd_data], 0);
	error = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_error], 0);
	seccnt = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_seccnt], 0);
	sector = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sector], 0);
	cyl_lo = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_cyl_lo], 0);
	cyl_hi = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_cyl_hi], 0);
	sdh = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0);
	command = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_command], 0);
	/* as seen by wdc layer */
	aprint_normal("wdc regs    : %x %x XX %x %x %x %x %x %x XX\n", 
		data, error, seccnt, sector, cyl_lo, cyl_hi, sdh, command);
}

void
wdc_fastata4k_attach(device_t parent, device_t self, void *aux)
{
	struct wdc_fastata4k_softc *sc;
	struct zbus_args *zap;
	int nchannels;
	int ch;

	sc = device_private(self);
	sc->sc_wdcdev.sc_atac.atac_dev = self;
	zap = aux;

	sc->ba = zap->va;

	sc->sc_iot.base = (bus_addr_t)sc->ba;
	sc->sc_iot.absm = &amiga_bus_stride_4swap;

	nchannels = 2;

	if (zap->prodid == 25) 
		aprint_normal(": FastATA 4000 Mk-I\n");
	else if (zap->prodid == 29)
		aprint_normal(": FastATA 4000 Mk-II/Mk-III\n");
	else if (zap->prodid == 62)
		aprint_normal(": FastATA 4000 Mk-V\n");
	else
		aprint_normal(": FastATA 4000 unknown model");

	/* XXX pio mode setting not implemented yet. */
	sc->sc_wdcdev.sc_atac.atac_cap = ATAC_CAP_DATA16|ATAC_CAP_NOIRQ;
	sc->sc_wdcdev.sc_atac.atac_pio_cap = 0;
	sc->sc_wdcdev.sc_atac.atac_channels = sc->wdc_chanarray;
	sc->sc_wdcdev.sc_atac.atac_nchannels = nchannels;

	wdc_allocate_regs(&sc->sc_wdcdev);

	for (ch = 0; ch < nchannels; ch++) {
		struct ata_channel *cp;
		struct wdc_regs *wdr;

		cp = &sc->channels[ch];
		sc->wdc_chanarray[ch] = cp;

		cp->ch_channel = ch;
		cp->ch_atac = &sc->sc_wdcdev.sc_atac;
		cp->ch_queue =
		    malloc(sizeof(struct ata_queue), M_DEVBUF, M_NOWAIT);
		if (cp->ch_queue == NULL) {
			aprint_error_dev(self,
			    "can't allocate memory for command queue\n");
			return;
		}
		cp->ch_ndrive = 2;

		wdr = CHAN_TO_WDC_REGS(cp);

		wdr->cmd_iot = &sc->sc_iot;
		/* map command registers space, 
		   for PIO0 port 0 is at 0x0, port 1 is at +0x1000, 
		   one set of registers is 0x1000 wide */
		if (bus_space_map(wdr->cmd_iot, FASTATA4K_OFF_PIO0+ch*FASTATA4K_REG_SIZE, 
		    FASTATA4K_REG_SIZE, 0, &wdr->cmd_baseioh)) {
			aprint_error_dev(self, "couldn't map cmd registers\n");
			return;
		}

		/* wdr->ctl_iot = &sc->sc_iot; */
		/* FastATA does not have aux ctl regs? */

		/* subregion individual command registers */
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_DATA, 2, &wdr->cmd_iohs[wd_data]);
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_ERROR, 1, &wdr->cmd_iohs[wd_error]);
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_SECCNT, 1, &wdr->cmd_iohs[wd_seccnt]);
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_SECTOR, 1, &wdr->cmd_iohs[wd_sector]);
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_CYL_LO, 1, &wdr->cmd_iohs[wd_cyl_lo]);
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_CYL_HI, 1, &wdr->cmd_iohs[wd_cyl_hi]);
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_SDH, 1, &wdr->cmd_iohs[wd_sdh]);
		bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh, 
			FASTATA4K_OFF_COMMAND, 1, &wdr->cmd_iohs[wd_command]);

		wdc_init_shadow_regs(cp);
		wdcattach(cp); 

		bus_space_write_1(wdr->cmd_iot, wdr->cmd_baseioh, FASTATA4K_OFF_CYL_LO, 0x02);
		bus_space_write_1(wdr->cmd_iot, wdr->cmd_baseioh, FASTATA4K_OFF_CYL_HI, 0x02);
		wdc_fastata4k_dumpregs(wdr->cmd_iot, wdr->cmd_baseioh, wdr);
	}
/*
	XXX: no hardware interrupt?
	sc->sc_isr.isr_intr = wdc_fastata4k_intr;
	sc->sc_isr.isr_arg = sc;
	sc->sc_isr.isr_ipl = 2;
	add_isr (&sc->sc_isr);
*/
}

/* no hardware interrupt? */
/* int
wdc_fastata4k_intr(void *arg)
{
	struct wdc_fastata4k_softc *sc = (struct wdc_fastata4k_softc *)arg;
	int nchannels, ret;
	volatile char *p;

	aprint_normal("FastATA interrupt start: ");

	ret = 0;
	nchannels = sc->sc_wdcdev.sc_atac.atac_nchannels;
	p = sc->ba;

	if (p[FASTATA4K_OFF_PIO0+FASTATA4K_OFF_INTSTAT] & 0x40) { 
		aprint_normal("interrupt on port 0\n");
		wdcintr(&sc->channels[0]);
		ret = 1;
	} 

	if (p[FASTATA4K_OFF_PIO0+FASTATA4K_OFF_INTSTAT] & 0x20) { 
		aprint_normal("interrupt on port 1\n");
		wdcintr(&sc->channels[1]);
		ret = 1;
	} 

	aprint_normal("FastATA interrupt end\n");

	return ret;
}
*/
