//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
//  File:  flash.c
//
//  This file implements CFI flash memory erase and write functions. Current
//  implementation doesn't support flash/block protection bit and it doesn't
//  use buffer write even if flash supports it. 
//
#pragma warning(push)
#pragma warning(disable: 4115 4201 4204 4214)
#include <windows.h>
#include <oal.h>
#pragma warning(pop)

//------------------------------------------------------------------------------

static BOOL BitZero(UINT32 code, UINT32 bit, UINT32 width, UINT32 parallel)
{
    BOOL rc = FALSE;
    
    switch (width) {
    case 4:
        if (parallel == 4) {
            rc = (code & (1 << bit)) == 0 || (code & (1 << (bit + 8))) == 0 ||
                (code & (1 << (bit + 16))) == 0 ||
                (code & (1 << (bit + 24))) == 0;
        } else if (parallel == 2) {
            rc = (code & (1 << bit)) == 0 || (code & (1 << (bit + 16))) == 0;
        } else if (parallel == 1) {
            rc = (code & (1 << bit)) == 0;
        }
        break;
    case 2:
        if (parallel == 2) {
            rc = (code & (1 << bit)) == 0 || (code & (1 << (bit + 8))) == 0;
        } else if (parallel == 1) {
            rc = (code & (1 << bit)) == 0;
        }
        break;
    case 1:
        rc = (code & (1 << bit)) == 0;
        break;
    }
    return rc;
}

//------------------------------------------------------------------------------


// 
// adjusts offset for width, reads width worth of data from that address
// 
static UINT32 ReadIdentification(UINT32 base, UINT32 offset, UINT32 width)
{
    UINT32 data = 0;
    
    switch (width) {
    case 4:        
        data = INREG32((UINT32*)(base + (offset << 2)));
        break;
    case 2:        
         data = INREG16((UINT16*)(base + (offset << 1))) & 0xFFFF;
        break;
    case 1:
         data = INREG8((UINT8*)(base + offset)) & 0xFF;
        break;
    }

    return data;            
}


static UINT32 ReadInfo(UINT32 base, UINT32 offset, UINT32 size, UINT32 width)
{
    UINT32 data = 0;
    INT32 count = (INT32)size;
    
    switch (width) {
    case 4:        
        while (count-- > 0) {
            data <<= 8;
            data |= INREG32((UINT32*)(base + ((offset + count) << 2))) & 0xFF;
        }
        break;
    case 2:        
        while (count-- > 0) {
            data <<= 8;
            data |= INREG16((UINT16*)(base + ((offset + count) << 1))) & 0xFF;
        }
        break;
    case 1:
        while (count-- > 0) {
            data <<= 8;
            data |= INREG8((UINT8*)(base + offset + count));
        }
        break;
    }

    return data;            
}

//------------------------------------------------------------------------------

static VOID WriteCommand(
    UINT32 base, UINT32 offset, UINT8 cmd, UINT32 width, UINT32 parallel
) {
    UINT32 code;
        
    switch (width) {
    case 4:
        if (parallel == 4) {
            code = cmd | (cmd << 8) | (cmd << 16) | (cmd << 24);
        } else if (parallel == 2) {
            code = cmd | (cmd << 16);
        } else if (parallel == 1) {
            code = cmd;
        } else {
            break;
        }
        OUTREG32((UINT32*)(base + (offset << 2)), code);
        break;
    case 2:
        if (parallel == 2) {
            code = cmd | (cmd << 8);
        } else if (parallel == 1) {
            code = cmd;
        } else {
            break;
        }
        OUTREG16((UINT16*)(base + (offset << 1)), code);
        break;
    case 1:
        if (parallel != 1) break;
        code = cmd;
        OUTREG8((UINT8*)(base + offset), code);
        break;
    }
}

//------------------------------------------------------------------------------

static BOOL UnlockBlock1(UINT32 base, UINT32 block, OAL_FLASH_INFO *pInfo)
{
    //BOOL rc;
    //UINT32 code;
   
    // Start block unlock
    WriteCommand(base,  0, 0x50, pInfo->width, pInfo->parallel);
    WriteCommand(block, 0, 0x60, pInfo->width, pInfo->parallel);
    WriteCommand(block, 0, 0xD0, pInfo->width, pInfo->parallel);

	// Note: We could (should?) verify the unlock worked

    // Switch back to read mode
    WriteCommand(base, 0, 0xFF, pInfo->width, pInfo->parallel);

    return TRUE;
}

//------------------------------------------------------------------------------

static BOOL EraseBlock1(UINT32 base, UINT32 block, OAL_FLASH_INFO *pInfo)
{
    BOOL rc;
    UINT32 code;
   
    // Start block reset
    WriteCommand(base,  0, 0x50, pInfo->width, pInfo->parallel);
    WriteCommand(block, 0, 0x20, pInfo->width, pInfo->parallel);
    WriteCommand(block, 0, 0xD0, pInfo->width, pInfo->parallel);

    // Wait until it is done
    do {
        WriteCommand(base, 0, 0x70, pInfo->width, pInfo->parallel);
        code = INREG32((UINT32*)base);
    } while (BitZero(code, 7, pInfo->width, pInfo->parallel));

    // Reset memory back to normal state
    WriteCommand(base, 0, 0xFF, pInfo->width, pInfo->parallel);

    // Bit 5 in code is zero if erase succeeded
    rc = BitZero(code, 5, pInfo->width, pInfo->parallel);
    
    // Switch back to read mode
    WriteCommand(base, 0, 0xFF, pInfo->width, pInfo->parallel);

    return rc;
}

//------------------------------------------------------------------------------

static BOOL EraseBlock2(UINT32 base, UINT32 block, OAL_FLASH_INFO *pInfo)
{
    BOOL rc = FALSE;
    UINT32 code, code2;
   
    // Start block reset
    WriteCommand(base, 0x555, 0xAA, pInfo->width, pInfo->parallel);
    WriteCommand(base, 0x2AA, 0x55, pInfo->width, pInfo->parallel);
    WriteCommand(base, 0x555, 0x80, pInfo->width, pInfo->parallel);
    WriteCommand(base, 0x555, 0xAA, pInfo->width, pInfo->parallel);
    WriteCommand(base, 0x2AA, 0x55, pInfo->width, pInfo->parallel);
   
    // Wait until it is done
    switch (pInfo->set) {
    case 2:
        WriteCommand(block, 0, 0x30, pInfo->width, pInfo->parallel);

        // Poll DQ3
        for (;;) {
            code = INREG16((UINT16 *)block);
            if (code  & (1U << 3)) break; 
        }

        // Poll DQ7 (done) and DQ5 (error)
        for (;;) {
            code = INREG16((UINT16 *)block);

            // DQ7 goes high when successful erase completes
            if (code & (1U << 7)) {
                rc = TRUE;
                break;
            }

            // DQ5 goes high if erase error
            if (code & (1U << 5)) break;
        }

        break;
    case 0x0701:
        WriteCommand(block, 0, 0x80, pInfo->width, pInfo->parallel);
        for (;;) {
            code  = INREG32((UINT32*)block);
            code2 = INREG32((UINT32*)block);
            if ((code & (1<<6)) == (code2 & (1<<6)))
                break;
        }
        rc = TRUE;
    }

    // Switch back to read mode
    WriteCommand(base, 0, 0xF0,  pInfo->width, pInfo->parallel);

    return rc;
}

//------------------------------------------------------------------------------

static UINT32 WriteData1(
    ULONG base, ULONG pos, OAL_FLASH_INFO *pInfo, VOID *pBuffer
) {
    UINT32 size = 0, code;

    // Issue write command
    WriteCommand(base, 0, 0x40, pInfo->width, pInfo->parallel);
   
    // Now write info and wait until it is done
    switch (pInfo->width) {
    case 4:
        OUTREG32((UINT32*)pos, *(UINT32*)pBuffer);
        do {
            WriteCommand(base, 0, 0x70, pInfo->width, pInfo->parallel);
            code = INREG32((UINT32*)pos);
        } while (BitZero(code, 7, pInfo->width, pInfo->parallel));
        if (BitZero(code, 4, pInfo->width, pInfo->parallel)) size = 4;
        break;
    case 2:
        OUTREG16((UINT16*)pos, *(UINT16*)pBuffer);
        do {
            WriteCommand(base, 0, 0x70, pInfo->width, pInfo->parallel);
            code = INREG16((UINT16*)pos);
        } while (BitZero(code, 7, pInfo->width, pInfo->parallel));
        if (BitZero(code, 4, pInfo->width, pInfo->parallel)) size = 2;
        break;
   case 1:
        OUTREG8((UINT8*)pos, *(UINT8*)pBuffer);
        do {
            WriteCommand(base, 0, 0x70, pInfo->width, pInfo->parallel);
            code = INREG8((UINT8*)pos);
        } while (BitZero(code, 7, pInfo->width, pInfo->parallel));
        if (BitZero(code, 4, pInfo->width, pInfo->parallel)) size = 1;
        break;
   }

   // Reset memory back to normal state
   WriteCommand(base, 0, 0xFF, pInfo->width, pInfo->parallel);

   // Return result
   return size;
}

//------------------------------------------------------------------------------

static ULONG WriteData2(
    UINT32 base, UINT32 pos, OAL_FLASH_INFO *pInfo, VOID *pBuffer
) {
    UINT32 size = 0, code = 0, code2 = 0;

    // Set flash memory to write mode
    WriteCommand(base, 0x555, 0xAA, pInfo->width, pInfo->parallel);
    WriteCommand(base, 0x2AA, 0x55, pInfo->width, pInfo->parallel);
    WriteCommand(base, 0x555, 0xA0, pInfo->width, pInfo->parallel);

    // Now write info and wait until it is done
    switch (pInfo->width) {
    case 4:
        OUTREG32((UINT32*)pos, *(UINT32*)pBuffer);
        switch (pInfo->set) {
        case 2:
            for (;;) {
               code = INREG32((UINT32*)pos);
               if (code == *(UINT32*)pBuffer) break;
               if (BitZero(code, 5, pInfo->width, pInfo->parallel)) continue;
               code = INREG32((UINT32*)pos);
               break;
            }
            break;
        case 0x0701:
            for (;;) {
                code  = INREG32((UINT32*)pos);
                code2 = INREG32((UINT32*)pos);
                if ((code & (1<<6)) == (code2 & (1<<6)))
                    break;
            }
            code = INREG32((UINT32*)pos);
            break;
        }
        if (code == *(UINT32*)pBuffer) size = sizeof(UINT32);
        break;
    case 2:
        OUTREG16((UINT16*)pos, *(UINT16*)pBuffer);
        switch (pInfo->set) {
        case 2:
            for (;;) {
               code = INREG16((UINT16*)pos);
               if (code == *(UINT16*)pBuffer) break;
               if (BitZero(code, 5, pInfo->width, pInfo->parallel)) continue;
               code = INREG16((UINT16*)pos);
               break;
            }
            break;
        case 0x0701:
            for (;;) {
                code  = INREG16((UINT32*)pos);
                code2 = INREG16((UINT32*)pos);
                if ((code & (1<<6)) == (code2 & (1<<6)))
                    break;
            }
            code = INREG16((UINT32*)pos);
            break;
        }
        if (code == *(UINT16*)pBuffer) size = sizeof(UINT16);
        break;
    case 1:
        OUTREG8((UINT8*)pos, *(UINT8*)pBuffer);
        switch (pInfo->set) {
        case 2:
            for (;;) {
               code = INREG8((UINT8*)pos);
               if (code == *(UINT8*)pBuffer) break;
               if (BitZero(code, 5, pInfo->width, pInfo->parallel)) continue;
               code = INREG8((UINT8*)pos);
               break;
            }
            break;
        case 0x0701:
            for (;;) {
                code  = INREG8((UINT32*)pos);
                code2 = INREG8((UINT32*)pos);
                if ((code & (1<<6)) == (code2 & (1<<6)))
                    break;
            }
            code = INREG8((UINT32*)pos);
            break;
        }
        if (code == *(UINT8*)pBuffer) size = sizeof(UINT8);
        break;
    }

    return size;
}

//------------------------------------------------------------------------------

static BOOL ReadIdent1(UINT32 base, UINT32 block, OAL_FLASH_INFO *pInfo, BYTE* pId, UINT32 count)
{
    UINT32 offset = 0x81;
    UINT32 i = 0;

    // Start read device identification command
    WriteCommand(base,  0, 0x90, pInfo->width, pInfo->parallel);

    // Now read count bytes into the pId buffer 
    // bytes are contiguous, so don't read every other byte
    while (i < count)
    {
        // read a width at the following offset
        UINT32 Ident = ReadIdentification(block, offset, pInfo->width);
        memcpy(pId, &Ident, pInfo->width);
        
        // read a width at a time
        pId += pInfo->width;
        i+= pInfo->width;

        // each width == 1 offset (ie reading 0x81-0x88 with a width = 2 will get 16 bytes)
        offset++;
    }

    // Switch back to read mode
    WriteCommand(base, 0, 0xFF, pInfo->width, pInfo->parallel);

    return TRUE;
}

//------------------------------------------------------------------------------

BOOL OALFlashInfo(VOID *pBase, OAL_FLASH_INFO *pInfo)
{
    BOOL rc = FALSE;
    UINT32 base, code1, code2, code3, i;
    

    OALMSG(OAL_FUNC, (L"+OALFlashInfo(0x%08x, ...)\r\n", pBase));

    // Just to avoid possible problems
    memset(pInfo, 0, sizeof(*pInfo));

    // Make sure that we talk to uncached address
    base = (UINT32)OALCAtoUA(pBase);
    
    // Try 32-bit geometry
    //OUTREG32((UINT32*)(base + 4 * 0x5555), 0xAAAAAAAA);
    //OUTREG32((UINT32*)(base + 4 * 0x2AAA), 0x55555555);
    OUTREG32((UINT32*)(base + 4 * 0x5555), 0x98989898);
    code1 = INREG32((UINT32*)(base + 4 * 0x10));
    code2 = INREG32((UINT32*)(base + 4 * 0x11));
    code3 = INREG32((UINT32*)(base + 4 * 0x12));
    if (code1 == 'QQQQ' && code2 == 'RRRR' && code3 == 'YYYY') {
        pInfo->width = 4;
        pInfo->parallel = 4;
    } else if (code1 == 'Q\0Q\0' && code2 == 'R\0R\0' && code3 == 'Y\0Y\0') {
        pInfo->width = 4;
        pInfo->parallel = 2;
    } else if (code1 == 'Q\0\0\0' && code2 == 'R\0\0\0' && code3 == 'Y\0\0\0') {
        pInfo->width = 4;
        pInfo->parallel = 1;
    } else {
        // Now try luck with 16-bit geometry
        //OUTREG16((UINT16*)(base + 2 * 0x5555), 0xAAAA);
        //OUTREG16((UINT16*)(base + 2 * 0x2AAA), 0x5555);
        OUTREG16((UINT16*)(base + 2 * 0x5555), 0x9898);
        code1 = INREG16((UINT16*)(base + 2 * 0x10));
        code2 = INREG16((UINT16*)(base + 2 * 0x11));
        code3 = INREG16((UINT16*)(base + 2 * 0x12));
        if (code1 == 'QQ' && code2 == 'RR' && code3 == 'YY') {
            pInfo->width = 2;
            pInfo->parallel = 2;
        } else if (code1 == '\0Q' && code2 == '\0R' && code3 == '\0Y') {
            pInfo->width = 2;
            pInfo->parallel = 1;
        } else {
            // So last oppurtinity is 8-bit mode
            //OUTREG8((UINT8*)(base + 0x5555), 0xAA);
            //OUTREG8((UINT8*)(base + 0x2AAA), 0x55);
            OUTREG8((UINT8*)(base + 0x5555), 0x98);
            code1 = INREG8((UINT8*)(base + 0x10));
            code2 = INREG8((UINT8*)(base + 0x11));
            code3 = INREG8((UINT8*)(base + 0x12));
            if (code1 == 'Q' && code2 == 'R' && code3 == 'Y') {
                pInfo->width = 1;
                pInfo->parallel = 1;
            } else {
                goto cleanUp;
            }                
        }
    }        

    // Read primary command set, size, burst size and number of regions
    pInfo->set = ReadInfo(base, 0x13, 2, pInfo->width);
    pInfo->size = 1 << ReadInfo(base, 0x27, 1, pInfo->width);
    pInfo->burst = 1 << ReadInfo(base, 0x2A, 1, pInfo->width);
    pInfo->regions = ReadInfo(base, 0x2C, 1, pInfo->width);

    // If there is more regions than expected
    if (pInfo->regions > 8) goto cleanUp;
   
    // Read region info
    for (i = 0; i < pInfo->regions; i++) {
        code1 = ReadInfo(base, 0x2d + (i << 2), 4, pInfo->width);
        pInfo->aBlocks[i] = (code1 & 0xFFFF) + 1;
        pInfo->aBlockSize[i] = (code1 >> 8) & 0x00FFFF00;
        if (pInfo->aBlockSize[i] == 0) pInfo->aBlockSize[i] = 128;
    }

    // Switch back to read mode
    switch (pInfo->set) {
    case 1:  // Intel/Sharp
    case 3: // New Intel parts
        WriteCommand(base, 0, 0xFF, pInfo->width, pInfo->parallel);
        break;
    case 2:  // AMD/Fujitsu
    case 0x0701: // SST
        WriteCommand(base, 0, 0xF0,  pInfo->width, pInfo->parallel);
        break;
    }      

    rc = TRUE;

cleanUp:
    OALMSG(OAL_FUNC, (L"-OALFlashInfo(rc = %d)\r\n", rc));
    return rc;
}

//------------------------------------------------------------------------------

BOOL OALFlashErase(VOID *pBase, VOID *pStart, UINT32 size)
{
    BOOL rc = FALSE;
    OAL_FLASH_INFO info;
    UINT32 base, start, end, chip, block;
    UINT32 ixReg, ixBlock;

    OALMSG(1, (
        L"+OALFlashErase(0x%08x, 0x%08x, 0x%08x)\r\n", pBase, pStart, size
    ));


    // Erase must work from uncached memory
    base = (UINT32)OALCAtoUA(pBase);
    start = (UINT32)OALCAtoUA(pStart);
    
    // First get end address
    end = start + size;
   
    // Ther read first chip info
    if (!OALFlashInfo((VOID*)base, &info)) {
        OALMSG(OAL_ERROR, (
            L"ERROR: OALFlashErase failed get flash memory info\r\n"
        ));
        goto cleanUp;
    }

    ixReg = ixBlock = 0;
    block = chip = base;
    while (block < end) {

        // Should block be erased?
        if (
            start < (block + info.aBlockSize[ixReg] * info.parallel) && 
            end >=  block
            ) {
            switch (info.set) {
            case 1: // Intel/Sharp
            case 3: // New Intel parts
                if (!UnlockBlock1(chip, block, &info)) {
                    goto cleanUp;
                }
                if (!EraseBlock1(chip, block, &info)) {
                    goto cleanUp;
                }
                break;
            case 2: // AMD
            case 0x0701: // SST
                if (!EraseBlock2(chip, block, &info)) {
                    goto cleanUp;
                }                    
                break;
            default:
                OALMSG(OAL_ERROR, (
                    L"ERROR: Flash type %d isn't supported\r\n", info.set
                    ));
                goto cleanUp;
            }
        }         

        // Move to next block
        block += info.aBlockSize[ixReg] * info.parallel;
        if (block >= end) break;
        if (++ixBlock >= info.aBlocks[ixReg]) {
            ixBlock = 0;
            if (++ixReg >= info.regions) {
                // Try read next chip info
                if (!OALFlashInfo((VOID*)block, &info)) break;
                ixReg = 0;
                chip = block;
            }
        }
    }

    rc = TRUE;
   
cleanUp:
    OALMSG(OAL_FUNC, (L"-OALFlashErase(rc = %d)\r\n", rc));
    return rc;
}

//------------------------------------------------------------------------------

BOOL OALFlashWrite(VOID *pBase, VOID *pStart, UINT32 size, VOID *pBuffer)
{
    BOOL rc = FALSE;
    OAL_FLASH_INFO info;
    UINT32 base, start, end, chip, pos, count;
    UINT8 *pPos;

    OALMSG(OAL_FUNC, (
        L"+OALFlashWrite(0x%08x, 0x%08x, 0x%08x, 0x%08x)\r\n", 
        pBase, pStart, size, pBuffer
    ));

    // Flash must work from uncached memory
    base = (UINT32)OALCAtoUA(pBase);
    start = (UINT32)OALCAtoUA(pStart);

    // First get end address
    end = start + size;
    chip = base;
    pPos = (UINT8*)pBuffer;

    // First read first chip info
    for (;;) {
        if (!OALFlashInfo((VOID*)chip, &info)) {
            OALMSG(OAL_ERROR, (
                L"ERROR: OALFlashWrite - failed get flash info at 0x%08x\r\n",
                chip
            ));
            goto cleanUp;
        }
        // Is start address on this chip
        if (start >= chip && start < (chip + info.size * info.parallel)) break;
        // Move to next chip
        chip += info.size * info.parallel;
    }
   
    pos = start;
    while (pos < end) {

        // Program data chunk
        switch (info.set) {
        case 1:
        case 3:
            count = WriteData1(chip, pos, &info, pPos);
            break;
        case 2:
        case 0x0701: // SST
            count = WriteData2(chip, pos, &info, pPos);
            break;
        default:
            OALMSG(OAL_ERROR, (
                L"ERROR: Flash type %d isn't supported\r\n", info.set
            ));
            goto cleanUp;
        }

        // If we write nothing some problem happen
        if (count == 0) {
            rc = FALSE;
            OALMSG(OAL_ERROR, (
                L"ERROR: Flash write at 0x%08x failed\r\n", pos
            ));
            goto cleanUp;
        }
      
        // Move position
        pos += count;
        pPos += count;

        // If we run out of chip move to next one
        if (pos > (chip + info.size * info.parallel)) {
            switch (info.set) {
            case 1:
            case 3:
                WriteCommand(chip, 0, 0xFF, info.width, info.parallel);
                break;
            case 2:
                WriteCommand(chip, 0, 0xF0, info.width, info.parallel);
                break;
            }
            chip += info.size * info.parallel;
            if (!OALFlashInfo((VOID*)chip, &info)) break;
        }         
    }

    switch (info.set) {
    case 1:
    case 3:
        WriteCommand(chip, 0, 0xFF, info.width, info.parallel);
        break;
    case 2:
    case 0x0701: // SST
        WriteCommand(chip, 0, 0xF0, info.width, info.parallel);
        break;
    }

    // Do final check
    pPos = (UINT8*)pBuffer;
    for (pos = start; pos < end - sizeof(UINT32) + 1; pos += sizeof(UINT32)) {
        if (*(UINT32*)pPos != *(UINT32*)pos) break;
        pPos += sizeof(UINT32);
    }

    // If we reach end, all is ok
    rc = (pos >= end - sizeof(UINT32) + 1);
    OALMSG(!rc&&OAL_ERROR, (
        L"ERROR: Flash failed at 0x%08x -- write 0x%08x, but read 0x%08x\r\n",
        pos, *(UINT32*)pPos, *(UINT32*)pos
    ));
    
cleanUp:
    OALMSG(OAL_FUNC, (L"-OALFlashWrite(rc = %d)", rc));
    return rc;
}

//------------------------------------------------------------------------------

BOOL OALFlashIdentifier(VOID *pBase, BYTE* pIdent, DWORD* pdwSize)
{
    BOOL rc = FALSE;
    OAL_FLASH_INFO info;
    UINT32 base;

    OALMSG(OAL_FUNC, (L"+OALFlashIdentifier(0x%08x, ...)\r\n", pBase));

    if (pdwSize == NULL || *pdwSize == 0)
    {
        goto cleanUp;
    }

    // Make sure that we talk to uncached address
    base = (UINT32)OALCAtoUA(pBase);
    
    if (pIdent == NULL)
        goto cleanUp;

    if (!OALFlashInfo((VOID*)base, &info)) {
        OALMSG(OAL_ERROR, (
            L"ERROR: OALFlashIdentifier - failed get flash info at 0x%08x\r\n",
            base
        ));
        goto cleanUp;
    }

    // TODO: This works for Intel parts. How about others?
    switch (info.set) {
    case 1:        
    case 3:
        if (*pdwSize < 8 * info.parallel)
            goto cleanUp;
        *pdwSize = 8 * info.parallel;
        // read 8 bytes of protection register from each chip into pIdent buffer
        rc = ReadIdent1(base, base, &info, pIdent, *pdwSize);
        break;
    case 2:
        *pdwSize = 0; // I'm unaware of any chips with set=2 which have an ident
        break;
    default:
        rc = FALSE;
    }


cleanUp:
    OALMSG(OAL_FUNC, (L"-OALFlashIdentifier(rc = %d)\r\n", rc));
    return rc;
}

//------------------------------------------------------------------------------

