/*++
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) 2002. Samsung Electronics, co. ltd  All rights reserved.

Module Name:  

Abstract:  

   Platform dependent PCMCIA memory and I/O window functions and data.

Rev:
	2002.4.14	: S3C2410 port (kwangyoon LEE, kwangyoon@samsung.com)

	2001.12.21	: bug fixup (kwangyoon LEE, kwangyoon@samsung.com)
	2001.12.12	: add S3C2400 Specific definitions (kwangyoon LEE, kwangyoon@samsung.com)

Notes: 
--*/

#include <windows.h>
#include <types.h>
#include <cardserv.h>
#include <sockserv.h>
#include <sockpd.h>

//
// Memory (attribute and common) starting at 0x10000000(physical address)
//
#define PCMCIA_MEM_WIN_BASE0	0x10000000
#define PCMCIA_MEM_WIN_BASE1	PCMCIA_MEM_WIN_BASE0+PCMCIA_MEM_WIN_SIZE
#define PCMCIA_MEM_WIN_BASE2	PCMCIA_MEM_WIN_BASE1+PCMCIA_MEM_WIN_SIZE
#define PCMCIA_MEM_WIN_BASE3	PCMCIA_MEM_WIN_BASE2+PCMCIA_MEM_WIN_SIZE
#define PCMCIA_MEM_WIN_BASE4	PCMCIA_MEM_WIN_BASE3+PCMCIA_MEM_WIN_SIZE
#define PCMCIA_MEM_WIN_SIZE		0x02000

#define MEM_WIN0_START_ADDR_LO	(0x00000000 >> 12)
#define MEM_WIN0_END_ADDR_LO	(PCMCIA_MEM_WIN_SIZE >> 12)

#define PCMCIA_IO_WIN_BASE0		0x0
#define PCMCIA_IO_WIN_BASE1		0x0

#define PCMCIA_IO_WIN_SIZE      65536

#define PCMCIA_NUM_WINDOWS		7

UCHAR EnableMask[PCMCIA_NUM_WINDOWS] = {
    WIN_MEM_MAP0_ENABLE,
    WIN_MEM_MAP1_ENABLE,
    WIN_MEM_MAP2_ENABLE,
    WIN_MEM_MAP3_ENABLE,
    WIN_MEM_MAP4_ENABLE,
    WIN_IO_MAP0_ENABLE,
    WIN_IO_MAP1_ENABLE,
};

// @doc DRIVERS

PDCARD_WINDOW_STATE v_WinState[PCMCIA_NUM_WINDOWS] = {
//
// Window state for the attribute and common memory windows.
//
 {  // Window 0 - socket 0 attribute or common window
    0,                    // socket number
    WIN_STATE_ENABLED|WIN_STATE_ATTRIBUTE,    // state flags
    WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_10|WIN_SPEED_USE_WAIT,
    PCMCIA_MEM_WIN_SIZE,
    PCMCIA_MEM_WIN_BASE0,
    0
 },
 {  // Window 1 - socket 0 attribute or common window
    0,                    // socket number
    WIN_STATE_ENABLED|WIN_STATE_16BIT,    // state flags
    WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_10|WIN_SPEED_USE_WAIT,
    PCMCIA_MEM_WIN_SIZE,
    PCMCIA_MEM_WIN_BASE1,
    0
 },
 {  // Window 2 - socket 0 attribute or common window
    0,                    // socket number
    WIN_STATE_ENABLED|WIN_STATE_16BIT,    // state flags
    WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_10|WIN_SPEED_USE_WAIT,
    PCMCIA_MEM_WIN_SIZE,
    PCMCIA_MEM_WIN_BASE2,
    0
 },
 {  // Window 3 - socket 0 attribute or common window
    0,                    // socket number
    WIN_STATE_ENABLED,    // state flags
    WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_10|WIN_SPEED_USE_WAIT,
    PCMCIA_MEM_WIN_SIZE,
    PCMCIA_MEM_WIN_BASE3,
    0
 },
 {  // Window 4 - socket 0 attribute or common window
    0,                    // socket number
    WIN_STATE_ENABLED,    // state flags
    WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_10|WIN_SPEED_USE_WAIT,
    PCMCIA_MEM_WIN_SIZE,
    PCMCIA_MEM_WIN_BASE4,
    0
 },


//
// Window state for the I/O windows.
//
 {  // Window 5 - socket 0 I/O window
    0,                    // socket number
    WIN_STATE_MAPS_IO|WIN_STATE_16BIT,
    WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_10|WIN_SPEED_USE_WAIT,
    PCMCIA_IO_WIN_SIZE,
    0x11000000,
    0
 },
 {  // Window 6 - socket 0 I/O window
    0,                    // socket number
    WIN_STATE_MAPS_IO|WIN_STATE_16BIT,
    WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_10|WIN_SPEED_USE_WAIT,
    PCMCIA_IO_WIN_SIZE,
    0x11000000,
    0
 }
};



//
// PDCardGetWindow
//
// @func    STATUS | PDCardGetWindow | Report the current state of a memory or I/O window
// @rdesc   Returns one of the CERR_* error codes in cardserv.h
//
STATUS
PDCardGetWindow(
    UINT32 uWindow,                     // @parm Window number (the first window is 0)
    PPDCARD_WINDOW_STATE pWindowState   // @parm Pointer to a PDCARD_WINDOW_STATE structure.
    )
{
    if (uWindow >= PCMCIA_NUM_WINDOWS) {
        return CERR_BAD_WINDOW;
    }

    memcpy(pWindowState, &(v_WinState[uWindow]), sizeof(PDCARD_WINDOW_STATE));
    return CERR_SUCCESS;
}


//
// PDCardSetWindow
//
// @func    STATUS | PDCardSetWindow | Change the characteristics of a memory or I/O window
// @rdesc   Returns one of the CERR_* error codes in cardserv.h
//
STATUS
PDCardSetWindow(
    UINT32 uWindow,                     // @parm Window number (the first window is 0)
    PPDCARD_WINDOW_STATE pWindowState   // @parm Pointer to a PDCARD_WINDOW_STATE structure.
    )
{
    //
    // base_reg will be added to a base register index to get the
    // correct window register index for the specified window.
    //
    UINT8 base_reg;
    UINT8 tmp;
    UINT8 tmp_mask;
    UINT32 start;
    UINT32 address;
    UINT  sock;
    BOOL  bUserMode = TRUE;

    DEBUGMSG (1,(TEXT("++PDCardSetWindow #%x\n\r"), uWindow));

    if ( uWindow & ADP_STATE_KERNEL_MODE) { // check the kernel-mode bit
        ASSERT( !(PCMCIA_NUM_WINDOWS & ADP_STATE_KERNEL_MODE) );
        uWindow &= ~ADP_STATE_KERNEL_MODE;  // clear it
        bUserMode = FALSE;
    }
    
	if (uWindow >= PCMCIA_NUM_WINDOWS) {
		return CERR_BAD_WINDOW;
    }

    sock = v_WinState[uWindow].uSocket;

    if ( bUserMode ) {
        EnterCriticalSection(&g_PCIC_Crit);
    }
            
	if (pWindowState->fState & WIN_STATE_ENABLED) {
        PCICIndex(sock, REG_WINDOW_ENABLE);
        tmp = PCICDataRead()|WIN_MEMCS16_DECODE;

        if (uWindow >= SOCKET0_FIRST_IO_WINDOW) {
            //
            // We need to check here for I/O range collisions since both sockets
            // are mapped onto the same system bus for I/O addresses. 
            // (The memory windows won't collide because they have constant 
            // address ranges in the host address space)
            //
            for (start = SOCKET0_FIRST_IO_WINDOW;
                 start < PCMCIA_NUM_WINDOWS;
                 start++) {
                //
                // Don't worry about collisions with:
                // 1. the same window
                // 2. disabled windows
                //
                if ((start == uWindow) || (!(v_WinState[start].fState & WIN_STATE_ENABLED))) {
                    continue;
                }
                if ((pWindowState->uOffset + pWindowState->uSize) > 
                    v_WinState[start].uOffset) {
                    if (pWindowState->uOffset <
                        (v_WinState[start].uOffset + v_WinState[start].uSize)) {
                        LeaveCriticalSection(&g_PCIC_Crit);
						
						DEBUGMSG(1,
							(TEXT("PCMCIA:PDCardSetWindow I/O range collision with windows %d (0x%x 0x%x) and %d (0x%x 0x%x)\r\n"),
         					uWindow, pWindowState->uOffset, pWindowState->uSize,
         					start, v_WinState[start].uOffset, v_WinState[start].uSize));
                        return CERR_IN_USE;
                    }
                }
            }
        }

        //
        // Disable this window while we work on it.
        //
        tmp_mask = EnableMask[uWindow];
        tmp &= ~tmp_mask;
        PCICDataWrite(tmp);

        if (uWindow >= SOCKET0_FIRST_IO_WINDOW) {
            //
            // I/O window setup
            //
            switch (uWindow) {
            case SOCKET0_FIRST_IO_WINDOW:
            case SOCKET1_FIRST_IO_WINDOW:
                base_reg = REG_IO_MAP0_START_ADDR_LO;
                break;

            case SOCKET0_FIRST_IO_WINDOW+1:
            case SOCKET1_FIRST_IO_WINDOW+1:
                base_reg = REG_IO_MAP1_START_ADDR_LO;
                break;
            }

            //
            // Set the I/O window's mapping
            //
            PCICIndex(sock, base_reg); // REG_IO_MAPn_START_ADDR_LO
            base_reg++;
            PCICDataWrite((UINT8)pWindowState->uOffset);
            PCICIndex(sock, base_reg); // REG_IO_MAPn_START_ADDR_HI
            base_reg++;
            PCICDataWrite((UINT8)(pWindowState->uOffset >> 8));
            address = pWindowState->uOffset + pWindowState->uSize - 1;
            PCICIndex(sock, base_reg); // REG_IO_MAPn_END_ADDR_LO
            base_reg++;
            PCICDataWrite((UINT8)address);
            PCICIndex(sock, base_reg); // REG_IO_MAPn_END_ADDR_HI
            PCICDataWrite((UINT8)(address >> 8));

            //
            // Set the window's data path width
            //
            PCICIndex(sock, REG_IO_WINDOW_CONTROL);
            tmp = PCICDataRead();
#if	0		// LKY 2001.12
            tmp_mask = ICR_0_WAIT_STATE;
#else
            tmp_mask = 0;
#endif
            
            if (pWindowState->fState & WIN_STATE_16BIT) {
                tmp_mask |= ICR_0_IOCS16|ICR_0_IO_16BIT;
            }
            if (!(uWindow & 1)) {
                tmp &= 0x0f;
                tmp_mask <<= 4;
                tmp |= tmp_mask;
            } else {
                tmp &= 0xf0;
                tmp |= tmp_mask;
            }
            PCICDataWrite(tmp);
            tmp_mask = PCICDataRead();
            if (tmp_mask != tmp) {
				DEBUGMSG(1,
					(TEXT("PCMCIA:PDCardSetWindow window %d REG_IO_WINDOW_CONTROL = 0x%x (expected 0x%x\r\n"),
					uWindow, tmp_mask, tmp));
            }

            //
            // Set the card type to I/O
            //
            PCICIndex(sock, REG_INTERRUPT_AND_GENERAL_CONTROL);
            tmp = PCICDataRead();
            tmp  |= INT_CARD_IS_IO;
            PCICDataWrite(tmp);

        } else {

            //
            // Memory window setup
            //
            if (sock) {
                base_reg = (uWindow - SOCKET1_FIRST_MEMORY_WINDOW) * 8;
            } else {
                base_reg = uWindow * 8;
            }

            //
            // Set the window to its predetermined host system address range
            //
            tmp = MEM_WIN0_START_ADDR_LO + (uWindow * MEM_WIN0_END_ADDR_LO);
            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_START_ADDR_LO));
            PCICDataWrite(tmp);
            tmp += MEM_WIN0_END_ADDR_LO - 1;
            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_END_ADDR_LO));
            PCICDataWrite(tmp);

            //
            // Set the window's data path width
            //
            if (pWindowState->fState & WIN_STATE_16BIT) {
                tmp = MSH_MEM_16BIT;
            } else {
                tmp = 0;
            }
            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_START_ADDR_HI));
            PCICDataWrite(tmp);
            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_END_ADDR_HI));
            PCICDataWrite(MTH_WAIT_STATE_BIT0|MTH_WAIT_STATE_BIT1);
            //
            // Set the window's card offset
            // (REG_MEM_MAP0_START_ADDR_HI only has 4 bits of address)
            //
                        
            // LKY. 2001.12 add Missing code!!!
            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_START_ADDR_HI));
            tmp = PCICDataRead();
            
            start = (UINT32)(tmp & 0x0f);
            start <<= 8;
            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_START_ADDR_LO));
            start |= PCICDataRead();
            start <<= 12;

			DEBUGMSG(1,
				(TEXT("PCMCIA:PDCardSetWindow mapping window %d @ 0x%x to card offset 0x%x\r\n"),
    			uWindow, start, pWindowState->uOffset));

            address = pWindowState->uOffset & 0xFFFFF000;
            pWindowState->uOffset = address;
            start = address - start;
            start >>= 12;

            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_ADDR_OFFSET_LO));
            PCICDataWrite((UINT8)start);
            start >>= 8;

            //
            // Set the window's memory space characteristics
            //
            tmp = (UINT8)(start & 0x3f);
            if (pWindowState->fState & WIN_STATE_ATTRIBUTE) {
				DEBUGMSG(1,
					(TEXT("PCMCIA:PDCardSetWindow mapping window %d to attribute space\r\n"),
					uWindow));
                tmp |= MOH_REG_ACTIVE;
            }
            PCICIndex(sock, (UINT8)(base_reg + REG_MEM_MAP0_ADDR_OFFSET_HI));
            PCICDataWrite(tmp);
        }   // else memory window
    }

    //
    // Enable or disable this window as requested.
    //
    PCICIndex(sock, REG_WINDOW_ENABLE);
    tmp = PCICDataRead()|WIN_MEMCS16_DECODE;
    tmp_mask = EnableMask[uWindow];
    if (pWindowState->fState & WIN_STATE_ENABLED) {
        tmp |= tmp_mask;
    } else {
        tmp &= ~tmp_mask;
    }
    PCICDataWrite(tmp);

#ifdef DEBUG
    DeltaSocketRegisters(v_WinState[uWindow].uSocket);
#endif

    if (bUserMode) {
        LeaveCriticalSection(&g_PCIC_Crit);
    }
    v_WinState[uWindow].fState = pWindowState->fState;
    v_WinState[uWindow].uSize = pWindowState->uSize;
    v_WinState[uWindow].uOffset = pWindowState->uOffset;

    // 
    if (uWindow >= SOCKET0_FIRST_IO_WINDOW) {
        pWindowState->uOffset = 0;
    }

    DEBUGMSG (1,(TEXT("--PDCardSetWindow\n\r")));

    return CERR_SUCCESS;
}   // PDCardSetWindow


//
// PDCardInquireWindow
//
// @func    STATUS | PDCardInquireWindow | Report the characteristics and capabilities
//                                         of a memory or I/O window
// @rdesc   Returns one of the CERR_* error codes in cardserv.h
//
STATUS
PDCardInquireWindow(
    UINT32 uWindow,                     // @parm Window number (the first window is 0)
    PPDCARD_WINDOW_INFO pWinInfo        // @parm Pointer to a PDCARD_WINDOW_INFO structure.
    )
{
    UINT uBase;

    if (uWindow < SOCKET1_FIRST_MEMORY_WINDOW) {
        pWinInfo->fSockets = 1;
    } else if (uWindow < SOCKET0_FIRST_IO_WINDOW) {
        pWinInfo->fSockets = 2;
    } else if (uWindow < SOCKET1_FIRST_IO_WINDOW) {
        pWinInfo->fSockets = 1;
    } else if (uWindow < PCMCIA_NUM_WINDOWS) {
        pWinInfo->fSockets = 2;
    } else {
        return CERR_BAD_WINDOW;
    }

    uBase = v_WinState[uWindow].uBase;

	// LKY 2001.12	(Window 0 -> attribute memory, others -> Common memory)
    if (uWindow == 0) {	
        pWinInfo->fWindowCaps = WIN_CAP_ATTRIBUTE;
        pWinInfo->fMemoryCaps = MEM_CAP_PRG_BASE|MEM_CAP_8BIT|MEM_CAP_16BIT;
        pWinInfo->uMemFirstByte = uBase;
        pWinInfo->uMemLastByte = uBase+PCMCIA_MEM_WIN_SIZE-1;
        pWinInfo->uMemMinSize = 4096;
        pWinInfo->uMemMaxSize = PCMCIA_MEM_WIN_SIZE;
        pWinInfo->uMemGranularity = 4096;
        pWinInfo->uMemBase = 0;
        pWinInfo->uMemOffset = 0;
        pWinInfo->fSlowest = WIN_SPEED_EXP_10MS|WIN_SPEED_MANT_12|WIN_SPEED_USE_WAIT;
        pWinInfo->fFastest = WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_12|WIN_SPEED_USE_WAIT;
    } 
    else if (uWindow < SOCKET0_FIRST_IO_WINDOW) {
        pWinInfo->fWindowCaps = WIN_CAP_COMMON;
        pWinInfo->fMemoryCaps = MEM_CAP_PRG_BASE|MEM_CAP_8BIT|MEM_CAP_16BIT;
        pWinInfo->uMemFirstByte = uBase;
        pWinInfo->uMemLastByte = uBase+PCMCIA_MEM_WIN_SIZE-1;
        pWinInfo->uMemMinSize = 4096;
        pWinInfo->uMemMaxSize = PCMCIA_MEM_WIN_SIZE;
        pWinInfo->uMemGranularity = 4096;
        pWinInfo->uMemBase = 0;
        pWinInfo->uMemOffset = 0;
        pWinInfo->fSlowest = WIN_SPEED_EXP_10MS|WIN_SPEED_MANT_12|WIN_SPEED_USE_WAIT;
        pWinInfo->fFastest = WIN_SPEED_EXP_1NS|WIN_SPEED_MANT_12|WIN_SPEED_USE_WAIT;
    } else {
        pWinInfo->fWindowCaps = WIN_CAP_IO|WIN_CAP_WAIT;
        pWinInfo->fIOCaps = IO_CAP_PRG_BASE|IO_CAP_8BIT|IO_CAP_16BIT;
        pWinInfo->uIOFirstByte = uBase;
        pWinInfo->uIOLastByte = uBase+PCMCIA_IO_WIN_SIZE-1;
        pWinInfo->uIOMinSize = 1;
        pWinInfo->uIOMaxSize = PCMCIA_IO_WIN_SIZE;
        pWinInfo->uIOGranularity = 1;
        pWinInfo->uAddressLines = 16;
    }

    return CERR_SUCCESS;
}
