/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 2001. Samsung Electronics, co. ltd  All rights reserved.
--*/

#include <windows.h>
#include <ethdbg.h>
#include <halether.h>
#include "..\..\drivers\pcmcia\sockpd.h"
#include "..\..\drivers\pcmcia\pd6710.h"
#include <S2440.h> 


#define PCMCIA_ATTR_READ(x)   		((*(volatile unsigned char *)(x+attr_addr)))

#define CIS_R0						0x3F8

#define CARD_IS_UNKNOWN				0
#define CARD_IS_MEMORY				1
#define CARD_IS_ENET				2

#define PD6710_MEM_BASE_ADDRESS     (0xA4000000)	//(0x10000000) // nGCS2 (A24 == 0)
#define PD6710_IO_BASE_ADDRESS      (0xA5000000)	//(0x11000000) //       (A24 == 1)

#define rPD6710_INDEX				(*(volatile unsigned char *)(PD6710_IO_BASE_ADDRESS+0x3e0))
#define rPD6710_DATA				(*(volatile unsigned char *)(PD6710_IO_BASE_ADDRESS+0x3e1))

void PCICDataWrite_(unsigned char index, unsigned char data)
{
    rPD6710_INDEX=index;
    rPD6710_DATA=data;
}

unsigned char PCICDataRead_(unsigned char index)
{
    rPD6710_INDEX=index;
    return rPD6710_DATA;
}

void PCICDataModify_(unsigned char index,unsigned char mask,unsigned char data)
{
    rPD6710_INDEX=index;
    rPD6710_DATA=(rPD6710_DATA)&~mask|data;
}

static void Delay(DWORD dwMSecs) 
{	
#define MSEC_DELAY_SCALER	0x200
	volatile DWORD dwScaledSecs = (dwMSecs * MSEC_DELAY_SCALER);

	while (dwScaledSecs)
	{
		dwScaledSecs--;
	}
}
		

int	PrintTuples(unsigned long attr_addr)
{
	UCHAR	data, tuple_id, *pData;
	UCHAR	buf[256];
	int	offset;
	int	size;
	int	i, numBytes;
	ULONG	configBase;

	EdbgOutputDebugString("\n[Tuple Listing]\n");
	offset = 0;
	while (1) {
		tuple_id = PCMCIA_ATTR_READ(offset);

		if (tuple_id == 0xFF) {
			break;		/* End of Chain */
		}

		EdbgOutputDebugString ("[%x] ID=0x%x\n", offset, tuple_id);
		offset+=2;

		size = PCMCIA_ATTR_READ(offset);

		EdbgOutputDebugString ("[%x] SIZE=0x%x", offset, size);
		offset+=2;

		pData = buf;
		for (i=0; i<size; i++, offset+=2) {
			data = PCMCIA_ATTR_READ(offset);

			if ((i%8) == 0) {
				EdbgOutputDebugString ("\n[%x] DATA=", offset);
			}
			*pData ++ = data;
			EdbgOutputDebugString ("0x%x ", data);
		}
		EdbgOutputDebugString ("\n\n");

		pData = buf;
		switch (tuple_id) {
		case 0x15:
			EdbgOutputDebugString ("*  Major version= %s\n",
				(*pData==4) ? "for 2.01/2.1" : "for PCCard 95");
			pData ++;
			EdbgOutputDebugString ("*  Minor version= %s\n",
				(*pData==1) ? "for 2.0/2.01/2.1" : "for PCCard 95");
			pData ++;
			EdbgOutputDebugString ("*   Menufacturer= %s\n", pData);
			pData += (strlen (pData) + 1);
			EdbgOutputDebugString ("*        Product= %s\n", pData);
			pData += (strlen (pData) + 1);
			EdbgOutputDebugString ("* Product info 1= %s\n", pData);
			pData += (strlen (pData) + 1);
			EdbgOutputDebugString ("* Product info 2= %s\n", pData);
			EdbgOutputDebugString ("\n");
			break;

		case 0x1a:
			numBytes = ((*pData) & 0x3)+1;

			pData += 2;
			configBase = 0;
			for (i=numBytes; i>0; i--) {
				configBase <<= 8;
				configBase  |= *(pData + i - 1);
			}
			EdbgOutputDebugString ("* Config Base= 0x%x\n", configBase);
			EdbgOutputDebugString ("\n");
			break;

		default:
			break;
		}	/* switch-tuple_id */
	}

		return 0;
}	/* PrintTuples */


PVOID PCMCIA_Init(void)
{
	unsigned char tmp, uByte;
	int Offset;      
    volatile unsigned char *pAttr;

	volatile IOPreg *v_pIOPRegs 	= (volatile IOPreg *)IOP_BASE;
	volatile MEMreg *v_pMEMRegs		= (volatile MEMreg *)MEMCTRL_BASE;

    UINT EbootDeviceAddress = 0;


    // Initialize S3C2440 for PD6710
    // EINT3(GPF3) is enabled.
    v_pIOPRegs->rGPFCON = (v_pIOPRegs->rGPFCON & ~(0x3<<6)) | (0x2<<6);
    // EINT3 is PULLUP enabled.
    v_pIOPRegs->rGPFUP = (v_pIOPRegs->rGPFUP & ~(0x1<<3));

    // EINT8(GPG0) is enabled.
    v_pIOPRegs->rGPGCON = (v_pIOPRegs->rGPGCON & ~(0x3<<0)) | (0x2<<0);
    // EINT8 is *not* PULLUP enabled.
	v_pIOPRegs->rGPGUP = (v_pIOPRegs->rGPGUP | (0x1<<0));

    // nGCS2=nUB/nLB(nSBHE),nWAIT,16-bit
    v_pMEMRegs->rBWSCON = (v_pMEMRegs->rBWSCON & ~(0xf<<8)) | (0xd<<8);

    // BANK2 access timing
    v_pMEMRegs->rBANKCON2 = ((B6710_Tacs<<13)+(B6710_Tcos<<11)+(B6710_Tacc<<8)+(B6710_Tcoh<<6)\
            +(B6710_Tah<<4)+(B6710_Tacp<<2)+(B6710_PMC));

    // EINT8=Level-high triggered, IRQ3.
    // EINT3=Falling Edge triggering -> connected INTR(controller)
    v_pIOPRegs->rEXTINT1=(v_pIOPRegs->rEXTINT1 & ~(0xf<<0)) | (0x1<<0);
    v_pIOPRegs->rEXTINT0=(v_pIOPRegs->rEXTINT0 & ~(0xf<<12)) | (0x2<<12);

	//
    // 0x3E0 is the standard Intel compatible socket controller I/O port.
    //
	tmp = PCICDataRead_(REG_CHIP_REVISION);
	if ((tmp != 0x83) && (tmp != 0x82)) {
    	EdbgOutputDebugString("PDCardInitServices CHIP_REVISION = 0x%x, expected = 0x83 !!!\r\n", tmp);
		return(NULL);
    }

	//output2card_disable,AUTO_POWER,VCC_POWER_OFF,Vpp1=0V
    PCICDataWrite_(REG_POWER_CONTROL, (0<<7)|(1<<5)|(0<<4)|(0<<0));
     
	// Management int -> edge triggering(PULSE), System int -> LEVEL triggering 
	PCICDataWrite_(REG_GENERAL_CONTROL, (MISC1_VCC_33|MISC1_PM_IRQ|MISC1_SPK_ENABLE));

	// 25Mhz_bypass,low_power_dynamic,IRQ12=drive_LED
//	PCICDataWrite_(REG_GLOBAL_CONTROL, (MISC2_BFS|MISC2_LOW_POWER_MODE|MISC2_LED_ENABLE));
	PCICDataWrite_(REG_GLOBAL_CONTROL, (MISC2_LOW_POWER_MODE|MISC2_LED_ENABLE));

	//status_change_int_enable, card_detect_int_enable, IRQ disabled(IRQ_3)
    PCICDataWrite_(REG_STATUS_CHANGE_INT_CONFIG, (1<<0)|(1<<3)|(3<<4)); 

	//IO0=16bit,timing_set_0
    PCICDataWrite_(REG_IO_WINDOW_CONTROL, 1|(1<<1)|(0<<3));

    // I/O windows must never include 3e0h and 3e1h 		
    // IO AREA=0x300~0x340(NE2K) -> 0x300~0x340
    PCICDataWrite_(REG_IO_MAP0_START_ADDR_LO, 0x00);	
    PCICDataWrite_(REG_IO_MAP0_START_ADDR_HI, 0x3);
    PCICDataWrite_(REG_IO_MAP0_END_ADDR_LO, 0x40);
    PCICDataWrite_(REG_IO_MAP0_END_ADDR_HI, 0x3);           
    PCICDataWrite_(REG_CARD_IO_MAP0_OFFSET_L, 0x0);
    PCICDataWrite_(REG_CARD_IO_MAP0_OFFSET_H, 0x0);

    //If this is memory window, the lowest 64KB should be reserved.
	//MEM AREA=0x0~0xFFFF ->0x0~0xFFFFFF
    PCICDataWrite_(REG_MEM_MAP0_START_ADDR_LO, 0x00); 			//MEM0=8bit data width
    PCICDataWrite_(REG_MEM_MAP0_START_ADDR_HI, 0x00);
    PCICDataWrite_(REG_MEM_MAP0_END_ADDR_LO, 0x01);				//0x0 ~ 0xffff
    PCICDataWrite_(REG_MEM_MAP0_END_ADDR_HI, 0x00|(0<<6)); 		//timing_set_0
    PCICDataWrite_(REG_MEM_MAP0_ADDR_OFFSET_LO, 0x0);
    PCICDataWrite_(REG_MEM_MAP0_ADDR_OFFSET_HI, 0x0|(1<<6)); 	//nREG=active 
    
	//memory map 0 enabled, I/O map 0 enabled
    PCICDataWrite_(REG_WINDOW_ENABLE, 1|(1<<6));
        
    // before configuring timing register, FIFO should be cleared.
	PCICDataWrite_(REG_FIFO_CTRL, FIFO_EMPTY_WRITE);	//Flush FIFO

    //default access time is 300ns
	PCICDataWrite_(REG_SETUP_TIMING0, 2);				//80ns(no spec)
	PCICDataWrite_(REG_CMD_TIMING0, 8);					//320ns(by spec,25Mhz clock)
	PCICDataWrite_(REG_RECOVERY_TIMING0, 2);			//80ns(no spec)

    //default access time is 300ns
	PCICDataWrite_(REG_SETUP_TIMING1, 2);				//80ns(no spec)
	PCICDataWrite_(REG_CMD_TIMING1, 8);					//320ns(by spec,25Mhz clock)
	PCICDataWrite_(REG_RECOVERY_TIMING1, 2);			//80ns(no spec)
        
    PCICDataWrite_(REG_CHIP_INFO,0x0);
    if((PCICDataRead_(REG_CHIP_INFO)&0xc0) != 0xc0 || (PCICDataRead_(REG_CHIP_INFO)&0xc0) != 0x00 )
    {
    	EdbgOutputDebugString("PD6710 hardware identification error!!!\n");
    	return(NULL);
    }
 
	EbootDeviceAddress = PD6710_MEM_BASE_ADDRESS;

    if((PCICDataRead_(REG_INTERFACE_STATUS)&0xc)==0xc) 
    {
		EdbgOutputDebugString("Card is inserted.\n");

		//For card contact stablization. This delay is needed for stable operation.
		//If this delay isn't here, CF card will not be identified.
	    if(PCICDataRead_(REG_GENERAL_CONTROL)&0x1)  //MISC_CTRL1[0] 0=3.3V  1=5.0V 
		{
    		PCICDataModify_(REG_GENERAL_CONTROL,0x2,0x0); //nVCC_5_enabled
    		EdbgOutputDebugString("5.0V card is detected.\n");
    	}
		else
    	{
    		PCICDataModify_(REG_GENERAL_CONTROL,0x2,0x2); //nVCC_3_enabled
    		EdbgOutputDebugString("3.3V card is detected.\n");
    	}

    	PCICDataModify_(REG_POWER_CONTROL,(1<<4)|3,(1<<4)|1);	

    	//VCC_POWER_on,VPP=Vcc(3.3V or 5.0V)
    	Delay(20); 

    	//Delay 10ms should be here for power stabilization.
    	//If this time is not here, the program may halt 
		// in case that the pc card has been inserted before power_on   
		//But, Why?	
    	
		Delay(2); //RESET should be Hi-Z for minimum 1ms
    	PCICDataModify_(REG_INTERRUPT_AND_GENERAL_CONTROL,(1<<6),0);	//RESET=active(H)
    	PCICDataModify_(REG_POWER_CONTROL,(1<<7),(1<<7)); 				//output2card_enable(RESET=Hi-Z -> output)
    	PCICDataModify_(REG_INTERRUPT_AND_GENERAL_CONTROL,(1<<6),0);	//RESET=active(H)	
    	Delay(1); //wait for minimum 10us
    	PCICDataModify_(REG_INTERRUPT_AND_GENERAL_CONTROL, (1<<6),(1<<6)); //RESET=inactive(L)
    	Delay(40);	//wait for 20ms

		// Watch for RDY signal to be asserted by the card.  The NE2000 card will initially come up in memory mode so it's
		// safe to watch for RDY.  Later, we put the NE2000 in IO mode.
		//
    	while(!(PCICDataRead_(REG_INTERFACE_STATUS)&0x20));

    	PCICDataModify_(REG_INTERRUPT_AND_GENERAL_CONTROL,(1<<5),(1<<5));	//mem_card -> mem_io_card
    	PCICDataModify_(REG_INTERRUPT_AND_GENERAL_CONTROL, 0xf, 0x3);		// IREQ == IRQ3.
    
    	Delay(200); 
		//If this delay isn't here, some CF card will not be identified.
   		//In oder to remove this delay, I think, we have to check READY signal.
	}
	else {
		EdbgOutputDebugString("NO card is detected.\n");
		return(NULL);
	}	
 
	// Switch the NE2000 card to 16-bit IO mode.
	//
	if(PCICDataRead_(REG_GENERAL_CONTROL)&0x1)
	{
		// 5V.
		//
		uByte  = 0x21;
		Offset = CIS_R0;
		pAttr  = (PUCHAR) EbootDeviceAddress;
		pAttr += Offset;
		*pAttr = uByte;

		uByte  = 0x1;
		Offset = CIS_R0 + 2;
		pAttr  = (PUCHAR) EbootDeviceAddress;
		pAttr += Offset;
		*pAttr = uByte;
	}
	else
	{
		// 3.3V.
		//
		uByte  = 0x20;
		Offset = CIS_R0;
		pAttr  = (PUCHAR) EbootDeviceAddress;
		pAttr += Offset;
		*pAttr = uByte;
	}

	Delay(200); 
 
#if 0
	// Print tuple information from the PCMCIA/CF card.
	//
	PrintTuples(EbootDeviceAddress);
#endif
 
	return ((PVOID)(PD6710_IO_BASE_ADDRESS + 0x300));
}


