/*
 *	CH375 and CH376 driver for Fuzix.
 *
 *	Controller in parallel mode and running internal block firmware rather
 *	than being a raw USB controller.
 *
 *	Caller provides
 *		ch375_rdata();
 *		ch375_rstatus();
 *		ch375_wcmd(cmd);
 *		ch375_wdata(data);
 *		nap20();	nap 20us or so 
 *
 *	Plus the usually asm block copies for the read/write of the block
 *
 *	TODO: handle hotplugging once we get the infrastructure
 *
 *	Q: do we need to check 0x10 status before write  or read ? Push it to host
 *	as 1,5us wont be a problem in many cases
 */
//#include <hitech.h>
#include <stdio.h>
//#include <sys.h>


#include "ff.h"
#include "diskio.h"
#include "ch375.h"

#define ch375_base	0x58
__sfr	__at (ch375_base+0) ch375_data;
__sfr	__at (ch375_base+1) ch375_cmd;
__sfr	__at (ch375_base+1) ch375_status;


//#define ch375_data	ch375_base+0
//#define ch375_cmd	ch375_base+1
//#define ch375_status ch375_base+1


static BYTE ch_ver;
//static uchar ch_rd = 0x28;
//static uchar ch_wd = 0x2B;
//static uchar ch_dev = 0xFF;

// ~20usec delay @ 6mhz clk delay routine
void nap20(void) __naked
{
	__asm
	ld b,#10
loop1:
	ld a, b
	ld a, b
	ld a, b
	ld a, b
	djnz loop1
	ret
	__endasm;
}

/* Some guesswork here on how the get status polling is meant to work */
static BYTE ch375_rpoll(void)
{
    WORD count = 0x8000;
    BYTE r;
    nap20();
    while(--count && ((ch375_status & 0x80) == 0x80))
	{
		nap20();
	}

    if (count == 0)
	{
		printf("\n ** PROBLEM **");
		printf("\npoll timeout");
        return 0xFF;
	}
    /* Get interrupt status, and clear interrupt */
    ch375_cmd = CMD_GET_STATUS;
    nap20();
    r = ch375_data;
    return r;
}


DRESULT ch375_read( BYTE *dptr, LBA_t sector, UINT count )
{
    BYTE n;
    BYTE r;
	BYTE i;
	
	if( count > 255 )
	{
		printf("\n ** PROBLEM **");
		printf("\nNumber of sectors = %u > 255", count);
		return RES_ERROR;
	}

    ch375_cmd = CMD_DISK_READ;

    ch375_data = sector;
    ch375_data = sector >> 8;
    ch375_data = sector >> 16;
	ch375_data = sector >> 24;
    ch375_cmd = count; // number of sectors
    while(count--)
	{
		n = 8; // number of blocks in a sector
		while(n--)
		{
//		for (n = 0; n < 8; n++) {
			r = ch375_rpoll();
			if (r != ANSW_USB_INT_DISK_READ)
			{
				printf("\n ** PROBLEM **");
				printf("\n read incorrect int: %02x", r );
				return RES_ERROR;
			}
			i = 64; // size of block
			ch375_cmd = CMD_RD_USB_DATA0;
			r = ch375_data;	/* Throw byte count away - always 64 */
			if (r != i)
			{
				printf("\n ** PROBLEM **");
				printf("\n read incorrect blk size: %02x", r );
				return RES_ERROR;
			}
			while( i--)
				*dptr++ = ch375_data;
			ch375_cmd = CMD_DISK_READ_GO;
		}
	}
    r = ch375_rpoll();
    if (r != ANSW_USB_INT_SUCCESS)
        return RES_ERROR;
    return RES_OK;        
}


DRESULT ch375_write( const BYTE *dptr, LBA_t sector, UINT count )
{
    BYTE n;
    BYTE r;
	BYTE i;
	
	if( count > 255 )
	{
		printf("\n ** PROBLEM **");
		printf("\nNumber of sectors = %u > 255", count);
		return RES_ERROR;
	}
    ch375_cmd = CMD_DISK_WRITE;
    ch375_data = sector;
    ch375_data = sector >> 8;
    ch375_data = sector >> 16;
	ch375_data = sector >> 24;
    ch375_cmd = count; // number of sectors
    while(count--)
	{
		n = 8; // number of blocks in a sector
		while(n--)
		{
//		for (n = 0; n < 8; n++) {
			r = ch375_rpoll();

			if (r != ANSW_USB_INT_DISK_WRITE)
				return RES_ERROR;
			i = 64; // size of block
			ch375_cmd = CMD_WR_USB_DATA;
			ch375_data = i;	/* Send write count (64)*/
			while( i--)
				ch375_data = *dptr++;
			ch375_cmd =  CMD_DISK_WRITE_GO;
		}
	}
    r = ch375_rpoll();
    if (r != ANSW_USB_INT_SUCCESS)
        return RES_ERROR;
    return RES_OK;        
}

DSTATUS ch375_probe(void)
{
//    uchar chip = 5;
    BYTE n;
    BYTE r;
//printf("\nprobe entered");
    ch375_cmd = CMD_CHECK_EXIST;
	ch375_data = 0x55;
    if (ch375_data != 0xAA) {
        return STA_NODISK;
    }
    ch375_cmd = CMD_GET_IC_VER;
    ch_ver = ch375_data;
    if (ch_ver == 0xFF)
        return STA_NODISK;
//printf("\nprobe reset bus");
    /* Reset the bus */
    ch375_cmd = CMD_SET_USB_MODE;
	ch375_data = MODE_HOST_1;
    nap20();	/* 20 us */
	ch375_cmd = CMD_SET_USB_MODE;
	ch375_data = MODE_HOST_2;
    nap20();
	
    /* 376 - update commands to use */
//    if ((ch_ver & 0xC0) == 0x40) {
//        ch_rd = 0x27;
//        ch_wd = 0x2C;
//        chip = 6;
//    }

    /* Can take a few goes */
    for (n = 0; n < 24; n++) {
        unsigned i;
        ch375_cmd = CMD_DISK_INIT;
        /* FIXME: proper delay would be good! */
        for (i = 0; i < 10000; i++)
			nap20();
        r = ch375_rpoll();
        if (r == ANSW_USB_INT_SUCCESS)
            break;
        nap20();
    }
//printf("\nprobe init result %i", r);
    if (r != ANSW_USB_INT_SUCCESS)
        return STA_NOINIT;
//printf("\nprobe ok");
    return 0;
}

DWORD ch375_disk_capacity(void)
{
	union cv
	{
		DWORD dw;
		BYTE db[4];
	} result;
		
	BYTE num_bytes;
	
	ch375_cmd = CMD_DISK_CAPACITY;
	if( ch375_rpoll() == ANSW_USB_INT_SUCCESS )
	{
		ch375_cmd = CMD_RD_USB_DATA0;
		num_bytes = ch375_data;
		if( num_bytes == 4 )
		{
			result.db[0] = ch375_data;
			result.db[1] = ch375_data;
			result.db[2] = ch375_data;
			result.db[3] = ch375_data;
		}
		else
		{
			printf("\n ** PROBLEM **");
			printf("\nNumber of bytes in size not 4 = %u", num_bytes);
			result.dw = 0;
		}
	}
	else
	{
		result.dw = 0;
	}
	return result.dw;
}

