998 lines
32 KiB
C
998 lines
32 KiB
C
|
// *****************************************************************************
|
||
|
//
|
||
|
// File: H2fmi_ppn_fil.c
|
||
|
//
|
||
|
// Copyright (C) 2010 Apple Inc. All rights reserved.
|
||
|
//
|
||
|
// This document is the property of Apple Computer, Inc.
|
||
|
// It is considered confidential and proprietary.
|
||
|
//
|
||
|
// This document may not be reproduced or transmitted in any form,
|
||
|
// in whole or in part, without the express written permission of
|
||
|
// Apple Inc.
|
||
|
//
|
||
|
// *****************************************************************************
|
||
|
|
||
|
#include "WMROAM.h"
|
||
|
#include <FIL.h>
|
||
|
#include <FILTypes.h>
|
||
|
#include "H2fmi_private.h"
|
||
|
#include "H2fmi.h"
|
||
|
#include "H2fmi_ppn.h"
|
||
|
|
||
|
#if SUPPORT_PPN
|
||
|
|
||
|
extern UInt32 *g_pScratchBuf;
|
||
|
|
||
|
extern h2fmi_t g_fmi0;
|
||
|
extern h2fmi_t g_fmi1;
|
||
|
|
||
|
#if (defined(AND_COLLECT_FIL_STATISTICS) && AND_COLLECT_FIL_STATISTICS)
|
||
|
#define AND_FIL_STAT(x) (x)
|
||
|
extern FILStatistics stFILStatistics;
|
||
|
#else
|
||
|
#define AND_FIL_STAT(x)
|
||
|
#endif /* AND_COLLECT_FIL_STATISTICS */
|
||
|
|
||
|
void h2fmi_ppn_recover_nand(void);
|
||
|
static Int32 h2fmiPpnReadMulti(h2fmi_t *fmi, PPNCommandStruct *ppnCommand);
|
||
|
static Int32 h2fmiPpnReadMultiWithSingles(h2fmi_t *fmi, PPNCommandStruct *ppnCommand);
|
||
|
static PPNCommandStruct *topLevelSingle = NULL;
|
||
|
static PPNCommandStruct *lowLevelSingle = NULL;
|
||
|
static struct dma_segment *dataSegments = NULL;
|
||
|
static struct dma_segment *metaSegments = NULL;
|
||
|
|
||
|
#if (defined(AND_SUPPORT_BLOCK_STORAGE) && AND_SUPPORT_BLOCK_STORAGE)
|
||
|
static Int32 h2fmiPpnWriteMulti(h2fmi_t *fmi, PPNCommandStruct *ppnCommand);
|
||
|
#endif
|
||
|
|
||
|
BOOL32 h2fmi_ppn_fil_init(void)
|
||
|
{
|
||
|
if (NULL == lowLevelSingle)
|
||
|
{
|
||
|
lowLevelSingle = WMR_MALLOC(sizeof(*lowLevelSingle));
|
||
|
if (NULL == lowLevelSingle)
|
||
|
{
|
||
|
return FALSE32;
|
||
|
}
|
||
|
}
|
||
|
if (NULL == topLevelSingle)
|
||
|
{
|
||
|
topLevelSingle = WMR_MALLOC(sizeof(*topLevelSingle));
|
||
|
if (NULL == topLevelSingle)
|
||
|
{
|
||
|
return FALSE32;
|
||
|
}
|
||
|
}
|
||
|
if (NULL == dataSegments)
|
||
|
{
|
||
|
dataSegments = WMR_MALLOC(PPN_MAX_PAGES * sizeof(struct dma_segment));
|
||
|
if (NULL == dataSegments)
|
||
|
{
|
||
|
return FALSE32;
|
||
|
}
|
||
|
}
|
||
|
if (NULL == metaSegments)
|
||
|
{
|
||
|
metaSegments = WMR_MALLOC(PPN_MAX_PAGES * sizeof(struct dma_segment));
|
||
|
if (NULL == metaSegments)
|
||
|
{
|
||
|
return FALSE32;
|
||
|
}
|
||
|
}
|
||
|
return TRUE32;
|
||
|
}
|
||
|
|
||
|
void h2fmiPpnPerformCommandList(PPNCommandStruct **commands, UInt32 num_commands)
|
||
|
{
|
||
|
UInt32 i;
|
||
|
|
||
|
for (i = 0; i < num_commands; i++)
|
||
|
{
|
||
|
PPNCommandStruct *command = commands[i];
|
||
|
h2fmi_t *fmi = (command->context.bus_num == 1) ? &g_fmi1 : &g_fmi0;
|
||
|
Int32 status;
|
||
|
|
||
|
if (command->num_pages == 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch(command->command)
|
||
|
{
|
||
|
case PPN_COMMAND_READ:
|
||
|
#if (defined(H2FMI_ALLOW_MULTIS) && H2FMI_ALLOW_MULTIS)
|
||
|
if (!(command->options & PPN_OPTIONS_REPORT_HEALTH))
|
||
|
{
|
||
|
status = h2fmiPpnReadMulti(fmi, command);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
status = h2fmiPpnReadMultiWithSingles(fmi, command);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
#if !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM
|
||
|
#if (defined(AND_SUPPORT_BLOCK_STORAGE) && AND_SUPPORT_BLOCK_STORAGE)
|
||
|
case PPN_COMMAND_PROGRAM:
|
||
|
status = h2fmiPpnWriteMulti(fmi, command);
|
||
|
break;
|
||
|
#endif
|
||
|
|
||
|
case PPN_COMMAND_ERASE:
|
||
|
status = h2fmi_ppn_erase_blocks(fmi, command);
|
||
|
break;
|
||
|
#endif // !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM
|
||
|
|
||
|
case PPN_COMMAND_CAUBBT:
|
||
|
status = h2fmi_ppn_read_cau_bbt(fmi, command, command->mem_buffers[0].data);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
WMR_PANIC("Invalid PPN FIL command: %d", command->command);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch(status)
|
||
|
{
|
||
|
case _kIOPFMI_STATUS_PPN_GENERAL_ERROR:
|
||
|
WMR_PRINT(ERROR, "Command type 0x%08x failed due to PPN Controller General Error on bus %lu ce %lu\n",
|
||
|
command->command, fmi->bus_id, fmi->ppn->general_error_ce);
|
||
|
|
||
|
h2fmi_ppn_recover_nand();
|
||
|
break;
|
||
|
|
||
|
case _kIOPFMI_STATUS_READY_BUSY_TIMEOUT:
|
||
|
WMR_PRINT(ERROR, "Command type 0x%08x failed due to PPN controller status timeout on bus %lu\n",
|
||
|
command->command, fmi->bus_id);
|
||
|
|
||
|
h2fmi_ppn_recover_nand();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Int32 h2fmiPpnReadMulti(h2fmi_t *fmi,
|
||
|
PPNCommandStruct *ppnCommand)
|
||
|
{
|
||
|
const UInt32 numPages = ppnCommand->num_pages;
|
||
|
UInt32 page;
|
||
|
Int32 ret;
|
||
|
UInt8 *metaBounceBuffer;
|
||
|
UInt8 *metaSrc;
|
||
|
|
||
|
WMR_ASSERT(metaSegments != NULL);
|
||
|
WMR_ASSERT(dataSegments != NULL);
|
||
|
|
||
|
WMR_MEMSET(dataSegments, 0, PPN_MAX_PAGES * sizeof(struct dma_segment));
|
||
|
WMR_MEMSET(metaSegments, 0, PPN_MAX_PAGES * sizeof(struct dma_segment));
|
||
|
|
||
|
metaBounceBuffer = (UInt8 *)g_pScratchBuf;
|
||
|
|
||
|
WMR_MEMSET(metaBounceBuffer, 0xAB, ppnCommand->lbas * fmi->valid_bytes_per_meta);
|
||
|
|
||
|
metaSegments[0].paddr = (UInt32)metaBounceBuffer;
|
||
|
metaSegments[0].length = ppnCommand->lbas * fmi->valid_bytes_per_meta;
|
||
|
|
||
|
WMR_PREPARE_READ_BUFFER(metaBounceBuffer, ppnCommand->lbas * fmi->valid_bytes_per_meta);
|
||
|
|
||
|
for (page = 0; page < numPages; page++)
|
||
|
{
|
||
|
const PPNCommandEntry *entry = &ppnCommand->entry[page];
|
||
|
const PPNMemoryIndex *memIndex = &ppnCommand->mem_index[page];
|
||
|
const PPNMemoryEntry *memoryEntry = &ppnCommand->mem_buffers[memIndex->idx];
|
||
|
UInt8 *dataPtr;
|
||
|
UInt32 dataLength;
|
||
|
|
||
|
dataPtr = (UInt8 *)memoryEntry->data + (memIndex->offset * fmi->logical_page_size);
|
||
|
dataLength = entry->lbas * fmi->logical_page_size;
|
||
|
|
||
|
WMR_PREPARE_READ_BUFFER(dataPtr, dataLength);
|
||
|
|
||
|
dataSegments[page].paddr = (UInt32)dataPtr;
|
||
|
dataSegments[page].length = dataLength;
|
||
|
}
|
||
|
|
||
|
#if FMISS_ENABLED
|
||
|
ret = fmiss_ppn_read_multi(fmi, ppnCommand, dataSegments, metaSegments);
|
||
|
#else
|
||
|
ret = h2fmi_ppn_read_multi(fmi, ppnCommand, dataSegments, metaSegments);
|
||
|
#endif
|
||
|
|
||
|
WMR_COMPLETE_READ_BUFFER(metaBounceBuffer, ppnCommand->lbas * fmi->valid_bytes_per_meta);
|
||
|
|
||
|
metaSrc = metaBounceBuffer;
|
||
|
for (page = 0; page < ppnCommand->num_pages; page++)
|
||
|
{
|
||
|
const PPNCommandEntry *entry = &ppnCommand->entry[page];
|
||
|
const PPNMemoryIndex *memIndex = &ppnCommand->mem_index[page];
|
||
|
const PPNMemoryEntry *memoryEntry = &ppnCommand->mem_buffers[memIndex->idx];
|
||
|
|
||
|
WMR_COMPLETE_READ_BUFFER((void *)dataSegments[page].paddr, dataSegments[page].length);
|
||
|
if (memoryEntry->meta)
|
||
|
{
|
||
|
UInt8 *metaDest;
|
||
|
|
||
|
metaDest = (UInt8 *)memoryEntry->meta + (memIndex->offset * fmi->total_bytes_per_meta);
|
||
|
|
||
|
WMR_MEMCPY(metaDest,
|
||
|
metaSrc,
|
||
|
entry->lbas * fmi->valid_bytes_per_meta);
|
||
|
metaSrc += entry->lbas * fmi->valid_bytes_per_meta;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static Int32 h2fmiPpnReadMultiWithSingles(h2fmi_t *fmi,
|
||
|
PPNCommandStruct *ppnCommand)
|
||
|
{
|
||
|
UInt32 i;
|
||
|
UInt8 *metaBounceBuffer;
|
||
|
Int32 status = _kIOPFMI_STATUS_SUCCESS;
|
||
|
|
||
|
metaBounceBuffer = (UInt8 *)g_pScratchBuf;
|
||
|
|
||
|
ppnCommand->page_status_summary = 0;
|
||
|
|
||
|
for (i = 0; (i < ppnCommand->num_pages) && (status == _kIOPFMI_STATUS_SUCCESS); i++)
|
||
|
{
|
||
|
const PPNCommandEntry *entry = &ppnCommand->entry[i];
|
||
|
const PPNCommandCeInfo *ceInfo = &ppnCommand->ceInfo[entry->ceIdx];
|
||
|
const PPNMemoryIndex *memIndex = &ppnCommand->mem_index[i];
|
||
|
const PPNMemoryEntry *memEntry = &ppnCommand->mem_buffers[memIndex->idx];
|
||
|
struct dma_segment dataSegment;
|
||
|
struct dma_segment metaSegment;
|
||
|
UInt8 *dataPtr;
|
||
|
UInt8 *metaPtr = NULL;
|
||
|
|
||
|
WMR_MEMSET(lowLevelSingle, 0, sizeof(*lowLevelSingle));
|
||
|
|
||
|
lowLevelSingle->context = ppnCommand->context;
|
||
|
lowLevelSingle->command = PPN_COMMAND_READ;
|
||
|
lowLevelSingle->options = ppnCommand->options;
|
||
|
lowLevelSingle->entry[0].addr = entry->addr;
|
||
|
lowLevelSingle->entry[0].ceIdx = 0;
|
||
|
lowLevelSingle->entry[0].lbas = entry->lbas;
|
||
|
lowLevelSingle->ceInfo[0].pages = 1;
|
||
|
lowLevelSingle->ceInfo[0].ce = ceInfo->ce;
|
||
|
lowLevelSingle->mem_index[0] = *memIndex;
|
||
|
|
||
|
dataPtr = (UInt8 *)memEntry->data + (memIndex->offset * fmi->logical_page_size);
|
||
|
if (memEntry->meta)
|
||
|
{
|
||
|
metaPtr = (UInt8 *)memEntry->meta + (memIndex->offset * fmi->total_bytes_per_meta);
|
||
|
}
|
||
|
|
||
|
WMR_PREPARE_READ_BUFFER(dataPtr, entry->lbas * fmi->logical_page_size);
|
||
|
WMR_PREPARE_READ_BUFFER(metaBounceBuffer, entry->lbas * fmi->total_bytes_per_meta);
|
||
|
|
||
|
dataSegment.paddr = (UInt32)dataPtr;
|
||
|
dataSegment.length = entry->lbas * fmi->logical_page_size;
|
||
|
metaSegment.paddr = (UInt32)metaBounceBuffer;
|
||
|
metaSegment.length = entry->lbas * fmi->valid_bytes_per_meta;
|
||
|
|
||
|
lowLevelSingle->num_pages = 1;
|
||
|
lowLevelSingle->lbas = entry->lbas;
|
||
|
|
||
|
#if FMISS_ENABLED
|
||
|
status = fmiss_ppn_read_multi(fmi, lowLevelSingle, &dataSegment, &metaSegment);
|
||
|
#else
|
||
|
status = h2fmi_ppn_read_multi(fmi, lowLevelSingle, &dataSegment, &metaSegment);
|
||
|
#endif
|
||
|
WMR_COMPLETE_READ_BUFFER(metaBounceBuffer, entry->lbas * fmi->total_bytes_per_meta);
|
||
|
WMR_COMPLETE_READ_BUFFER(dataPtr, entry->lbas * fmi->logical_page_size);
|
||
|
|
||
|
if ( NULL != metaPtr )
|
||
|
{
|
||
|
WMR_MEMCPY(metaPtr, metaBounceBuffer, entry->lbas * fmi->total_bytes_per_meta);
|
||
|
}
|
||
|
|
||
|
ppnCommand->entry[i].status = lowLevelSingle->entry[0].status;
|
||
|
ppnCommand->page_status_summary |= lowLevelSingle->page_status_summary;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
#if !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM
|
||
|
#if (defined(AND_SUPPORT_BLOCK_STORAGE) && AND_SUPPORT_BLOCK_STORAGE)
|
||
|
static Int32 h2fmiPpnWriteMulti(h2fmi_t *fmi,
|
||
|
PPNCommandStruct *ppnCommand)
|
||
|
{
|
||
|
UInt32 i;
|
||
|
UInt8 *metaBounceBuffer;
|
||
|
Int32 status;
|
||
|
|
||
|
status = _kIOPFMI_STATUS_SUCCESS;
|
||
|
metaBounceBuffer = (UInt8 *)g_pScratchBuf;
|
||
|
|
||
|
ppnCommand->page_status_summary = 0;
|
||
|
|
||
|
for (i = 0; (i < ppnCommand->num_pages) && (status == _kIOPFMI_STATUS_SUCCESS); i++)
|
||
|
{
|
||
|
const PPNCommandEntry *entry = &ppnCommand->entry[i];
|
||
|
const PPNCommandCeInfo *ceInfo = &ppnCommand->ceInfo[entry->ceIdx];
|
||
|
const PPNMemoryIndex *memIndex = &ppnCommand->mem_index[i];
|
||
|
const PPNMemoryEntry *memEntry = &ppnCommand->mem_buffers[memIndex->idx];
|
||
|
struct dma_segment dataSegment;
|
||
|
struct dma_segment metaSegment;
|
||
|
UInt8 *dataPtr;
|
||
|
UInt8 *metaPtr = NULL;
|
||
|
|
||
|
WMR_MEMSET(lowLevelSingle, 0, sizeof(*lowLevelSingle));
|
||
|
|
||
|
lowLevelSingle->context = ppnCommand->context;
|
||
|
lowLevelSingle->command = PPN_COMMAND_PROGRAM;
|
||
|
lowLevelSingle->options = ppnCommand->options;
|
||
|
lowLevelSingle->entry[0].addr = entry->addr;
|
||
|
lowLevelSingle->entry[0].ceIdx = 0;
|
||
|
lowLevelSingle->entry[0].lbas = entry->lbas;
|
||
|
lowLevelSingle->ceInfo[0].pages = 1;
|
||
|
lowLevelSingle->ceInfo[0].ce = ceInfo->ce;
|
||
|
lowLevelSingle->mem_index[0] = *memIndex;
|
||
|
|
||
|
dataPtr = (UInt8 *)memEntry->data + (memIndex->offset * fmi->logical_page_size);
|
||
|
if (memEntry->meta)
|
||
|
{
|
||
|
metaPtr = (UInt8 *)memEntry->meta + (memIndex->offset * fmi->total_bytes_per_meta);
|
||
|
WMR_MEMCPY(metaBounceBuffer, metaPtr, entry->lbas * fmi->total_bytes_per_meta);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WMR_MEMSET(metaBounceBuffer, 0xA5, entry->lbas * fmi->total_bytes_per_meta);
|
||
|
}
|
||
|
|
||
|
WMR_PREPARE_WRITE_BUFFER(dataPtr, entry->lbas * fmi->logical_page_size);
|
||
|
WMR_PREPARE_WRITE_BUFFER(metaBounceBuffer, entry->lbas * fmi->total_bytes_per_meta);
|
||
|
|
||
|
dataSegment.paddr = (UInt32)dataPtr;
|
||
|
dataSegment.length = entry->lbas * fmi->logical_page_size;
|
||
|
metaSegment.paddr = (UInt32)metaBounceBuffer;
|
||
|
metaSegment.length = entry->lbas * fmi->valid_bytes_per_meta;
|
||
|
|
||
|
lowLevelSingle->num_pages = 1;
|
||
|
lowLevelSingle->lbas = entry->lbas;
|
||
|
|
||
|
#if FMISS_ENABLED
|
||
|
status = fmiss_ppn_write_multi(fmi, lowLevelSingle, &dataSegment, &metaSegment);
|
||
|
#else
|
||
|
status = h2fmi_ppn_write_multi(fmi, lowLevelSingle, &dataSegment, &metaSegment);
|
||
|
#endif
|
||
|
ppnCommand->entry[i].status = lowLevelSingle->entry[0].status;
|
||
|
ppnCommand->page_status_summary |= lowLevelSingle->page_status_summary;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
#endif
|
||
|
#endif // !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM
|
||
|
|
||
|
Int32 h2fmiPpnReadSinglePage(UInt16 wVirtualCE,
|
||
|
UInt32 dwPpn,
|
||
|
UInt8* pabDataBuf,
|
||
|
UInt8* pabMetaBuf,
|
||
|
UInt8* pbMaxCorrectedBits,
|
||
|
UInt8* pdwaSectorStats,
|
||
|
BOOL32 bDisableWhitening)
|
||
|
{
|
||
|
PPNCommandStruct *ppnCommand;
|
||
|
h2fmi_t *fmi = h2fmiTranslateVirtualCEToBus(wVirtualCE);
|
||
|
UInt16 ce = h2fmiTranslateVirtualCEToCe(wVirtualCE);
|
||
|
Int32 ret;
|
||
|
|
||
|
WMR_ASSERT(NULL != fmi);
|
||
|
|
||
|
ppnCommand = topLevelSingle;
|
||
|
WMR_MEMSET(ppnCommand, 0, sizeof(PPNCommandStruct));
|
||
|
|
||
|
if (pbMaxCorrectedBits)
|
||
|
{
|
||
|
*pbMaxCorrectedBits = 0;
|
||
|
}
|
||
|
|
||
|
ppnCommand->context.device_info = NULL;
|
||
|
ppnCommand->context.handle = NULL;
|
||
|
ppnCommand->context.bus_num = fmi->bus_id;
|
||
|
ppnCommand->command = PPN_COMMAND_READ;
|
||
|
ppnCommand->options = 0;
|
||
|
ppnCommand->entry[0].ceIdx = 0;
|
||
|
ppnCommand->entry[0].addr.length = fmi->bytes_per_page + fmi->total_bytes_per_meta;
|
||
|
ppnCommand->entry[0].addr.column = 0;
|
||
|
ppnCommand->entry[0].addr.row = dwPpn;
|
||
|
ppnCommand->ceInfo[0].pages = 1;
|
||
|
ppnCommand->ceInfo[0].ce = ce;
|
||
|
ppnCommand->mem_index[0].offset = 0;
|
||
|
ppnCommand->mem_index[0].idx = 0;
|
||
|
ppnCommand->entry[0].lbas = fmi->bytes_per_page / fmi->logical_page_size;
|
||
|
ppnCommand->mem_buffers[0].data = pabDataBuf;
|
||
|
ppnCommand->mem_buffers[0].meta = pabMetaBuf;
|
||
|
ppnCommand->mem_buffers[0].num_of_lbas = 1;
|
||
|
ppnCommand->num_pages = 1;
|
||
|
ppnCommand->lbas = fmi->bytes_per_page / fmi->logical_page_size;
|
||
|
|
||
|
if(pdwaSectorStats != NULL)
|
||
|
{
|
||
|
ppnCommand->options = PPN_OPTIONS_REPORT_HEALTH;
|
||
|
}
|
||
|
|
||
|
h2fmiPpnPerformCommandList(&ppnCommand, 1);
|
||
|
|
||
|
if(pdwaSectorStats != NULL)
|
||
|
{
|
||
|
WMR_MEMCPY(pdwaSectorStats, pabDataBuf, fmi->sectors_per_page);
|
||
|
}
|
||
|
|
||
|
switch (ppnCommand->page_status_summary)
|
||
|
{
|
||
|
case 0x40:
|
||
|
ret = FIL_SUCCESS;
|
||
|
break;
|
||
|
|
||
|
case 0x42:
|
||
|
ret = FIL_SUCCESS;
|
||
|
WMR_PRINT(ERROR, "Single Page PPN Read saw refresh - read status: 0x%02X, CE: %d, Page: 0x%08X\n",
|
||
|
ppnCommand->page_status_summary, wVirtualCE, dwPpn);
|
||
|
break;
|
||
|
|
||
|
case 0x44:
|
||
|
case 0x43:
|
||
|
case 0x45:
|
||
|
ret = FIL_U_ECC_ERROR;
|
||
|
break;
|
||
|
|
||
|
case 0x49:
|
||
|
ret = FIL_SUCCESS_CLEAN;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
WMR_PRINT(ALWAYS, "Single Page PPN Read failed - overall NAND status 0x%02x\n",
|
||
|
ppnCommand->page_status_summary);
|
||
|
ret = FIL_CRITICAL_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
Int32 h2fmiPpnReadBootpage(UInt16 wVirtualCE,
|
||
|
UInt32 dwPpn,
|
||
|
UInt8 *pabData)
|
||
|
{
|
||
|
Int32 nRet = FIL_SUCCESS;
|
||
|
UInt32 status;
|
||
|
|
||
|
h2fmi_t* fmi = h2fmiTranslateVirtualCEToBus(wVirtualCE);
|
||
|
h2fmi_ce_t ce = h2fmiTranslateVirtualCEToCe(wVirtualCE);
|
||
|
|
||
|
WMR_ASSERT(NULL != fmi);
|
||
|
WMR_ASSERT(WMR_CHECK_BUFFER(pabData, H2FMI_BOOT_BYTES_PER_PAGE));
|
||
|
WMR_PREPARE_READ_BUFFER(pabData, H2FMI_BOOT_BYTES_PER_PAGE);
|
||
|
|
||
|
status = h2fmi_ppn_read_bootpage(fmi, ce, dwPpn, pabData, NULL);
|
||
|
|
||
|
switch (status)
|
||
|
{
|
||
|
case _kIOPFMI_STATUS_BLANK:
|
||
|
WMR_PRINT(READ, "[FIL:LOG] Clean Page\n");
|
||
|
AND_FIL_STAT(stFILStatistics.ddwReadCleanCnt++);
|
||
|
nRet = FIL_SUCCESS_CLEAN;
|
||
|
break;
|
||
|
case _kIOPFMI_STATUS_SUCCESS:
|
||
|
WMR_PRINT(READ, "[FIL:LOG] Good page\n");
|
||
|
nRet = FIL_SUCCESS;
|
||
|
break;
|
||
|
case _kIOPFMI_STATUS_AT_LEAST_ONE_UECC:
|
||
|
WMR_PRINT(ERROR, "UECC ce: %d p:0x%08x\n", (UInt32) wVirtualCE, dwPpn);
|
||
|
AND_FIL_STAT(stFILStatistics.ddwReadECCErrorCnt++);
|
||
|
nRet = FIL_U_ECC_ERROR;
|
||
|
break;
|
||
|
case _kIOPFMI_STATUS_PPN_GENERAL_ERROR:
|
||
|
WMR_PRINT(ERROR, "GEB ce: %d p:0x%08x\n", (UInt32) wVirtualCE, dwPpn);
|
||
|
nRet = FIL_CRITICAL_ERROR;
|
||
|
h2fmi_ppn_recover_nand();
|
||
|
break;
|
||
|
default:
|
||
|
WMR_PRINT(ERROR, "Error status: 0x%08x ce: %d p:0x%08x\n", status, (UInt32) wVirtualCE, dwPpn);
|
||
|
AND_FIL_STAT(stFILStatistics.ddwReadHWErrorCnt++);
|
||
|
nRet = FIL_CRITICAL_ERROR;
|
||
|
h2fmi_ppn_recover_nand();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
AND_FIL_STAT(stFILStatistics.ddwPagesReadCnt++);
|
||
|
|
||
|
WMR_COMPLETE_READ_BUFFER(pabData, H2FMI_BOOT_BYTES_PER_PAGE);
|
||
|
|
||
|
return nRet;
|
||
|
}
|
||
|
|
||
|
#if !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM || !H2FMI_READONLY
|
||
|
Int32 h2fmiPpnWriteSinglePage(UInt16 wVirtualCE,
|
||
|
UInt32 dwPpn,
|
||
|
UInt8 *pabDataBuf,
|
||
|
UInt8 *pabMetaBuf,
|
||
|
BOOL32 disableWhitening)
|
||
|
{
|
||
|
PPNCommandStruct *ppnCommand;
|
||
|
Int32 result;
|
||
|
h2fmi_t *fmi = h2fmiTranslateVirtualCEToBus(wVirtualCE);
|
||
|
UInt16 ce = h2fmiTranslateVirtualCEToCe(wVirtualCE);
|
||
|
|
||
|
WMR_ASSERT(NULL != fmi);
|
||
|
ppnCommand = topLevelSingle;
|
||
|
WMR_ASSERT(ppnCommand != NULL);
|
||
|
|
||
|
WMR_MEMSET(ppnCommand, 0, sizeof(PPNCommandStruct));
|
||
|
|
||
|
ppnCommand->context.device_info = NULL;
|
||
|
ppnCommand->context.handle = NULL;
|
||
|
ppnCommand->context.bus_num = fmi->bus_id;
|
||
|
ppnCommand->command = PPN_COMMAND_PROGRAM;
|
||
|
ppnCommand->options = 0;
|
||
|
ppnCommand->entry[0].ceIdx = 0;
|
||
|
ppnCommand->entry[0].addr.row = dwPpn;
|
||
|
ppnCommand->ceInfo[0].pages = 1;
|
||
|
ppnCommand->ceInfo[0].ce = ce;
|
||
|
ppnCommand->mem_index[0].offset = 0;
|
||
|
ppnCommand->mem_index[0].idx = 0;
|
||
|
ppnCommand->entry[0].lbas = (fmi->sectors_per_page * H2FMI_BYTES_PER_SECTOR) / fmi->logical_page_size;
|
||
|
ppnCommand->mem_buffers[0].data = pabDataBuf;
|
||
|
ppnCommand->mem_buffers[0].meta = pabMetaBuf;
|
||
|
ppnCommand->mem_buffers[0].num_of_lbas = 1;
|
||
|
ppnCommand->num_pages = 1;
|
||
|
|
||
|
h2fmiPpnPerformCommandList(&ppnCommand, 1);
|
||
|
|
||
|
if (ppnCommand->page_status_summary & PPN_CONTROLLER_STATUS__PENDING_ERRORS)
|
||
|
{
|
||
|
result = FIL_WRITE_FAIL_ERROR;
|
||
|
}
|
||
|
else if (ppnCommand->page_status_summary & PPN_CONTROLLER_STATUS__GENERAL_ERROR)
|
||
|
{
|
||
|
result = FIL_CRITICAL_ERROR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = FIL_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
#endif // !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM || !H2FMI_READONLY
|
||
|
|
||
|
Int32 h2fmiPpnWriteBootpage(UInt16 wVirtualCE,
|
||
|
UInt32 dwPpn,
|
||
|
UInt8 *pabData)
|
||
|
{
|
||
|
Int32 nRet;
|
||
|
UInt32 status;
|
||
|
h2fmi_t* fmi = h2fmiTranslateVirtualCEToBus(wVirtualCE);
|
||
|
h2fmi_ce_t ce = h2fmiTranslateVirtualCEToCe(wVirtualCE);
|
||
|
|
||
|
WMR_ASSERT(NULL != fmi);
|
||
|
WMR_ASSERT(WMR_CHECK_BUFFER(pabData, H2FMI_BOOT_BYTES_PER_PAGE));
|
||
|
WMR_PREPARE_READ_BUFFER(pabData, H2FMI_BOOT_BYTES_PER_PAGE);
|
||
|
|
||
|
status = h2fmi_ppn_write_bootpage(fmi, ce, dwPpn, pabData);
|
||
|
|
||
|
switch (status)
|
||
|
{
|
||
|
case _kIOPFMI_STATUS_PGM_ERROR:
|
||
|
WMR_PRINT(ERROR, "[FIL:LOG] Error programming boot page\n");
|
||
|
AND_FIL_STAT(stFILStatistics.ddwWriteNANDErrCnt++);
|
||
|
nRet = FIL_WRITE_FAIL_ERROR;
|
||
|
break;
|
||
|
case _kIOPFMI_STATUS_SUCCESS:
|
||
|
WMR_PRINT(READ, "[FIL:LOG] Wrote Good Boot page\n");
|
||
|
nRet = FIL_SUCCESS;
|
||
|
break;
|
||
|
case _kIOPFMI_STATUS_PPN_GENERAL_ERROR:
|
||
|
WMR_PRINT(ERROR, "[FIL:ERR] General Error writing bootpage\n");
|
||
|
nRet = FIL_CRITICAL_ERROR;
|
||
|
h2fmi_ppn_recover_nand();
|
||
|
break;
|
||
|
default:
|
||
|
WMR_PRINT(ERROR, "[FIL:ERR] Hardware error: 0x%08x\n", status);
|
||
|
AND_FIL_STAT(stFILStatistics.ddwWriteHWErrCnt++);
|
||
|
nRet = FIL_CRITICAL_ERROR;
|
||
|
h2fmi_ppn_recover_nand();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
AND_FIL_STAT(stFILStatistics.ddwPagesWrittenCnt++);
|
||
|
|
||
|
WMR_COMPLETE_READ_BUFFER(pabData, H2FMI_BOOT_BYTES_PER_PAGE);
|
||
|
|
||
|
return nRet;
|
||
|
}
|
||
|
|
||
|
#if !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM
|
||
|
Int32 h2fmiPpnEraseSingleBlock(UInt16 wVirtualCE,
|
||
|
UInt32 wPbn)
|
||
|
{
|
||
|
PPNCommandStruct *ppnCommand;
|
||
|
h2fmi_t* fmi = h2fmiTranslateVirtualCEToBus(wVirtualCE);
|
||
|
UInt16 ce = h2fmiTranslateVirtualCEToCe(wVirtualCE);
|
||
|
UInt32 wPpn = (wPbn << fmi->ppn->device_params.page_address_bits);
|
||
|
Int32 ret;
|
||
|
|
||
|
WMR_ASSERT(NULL != fmi);
|
||
|
ppnCommand = topLevelSingle;
|
||
|
WMR_ASSERT(ppnCommand != NULL);
|
||
|
|
||
|
WMR_MEMSET(ppnCommand, 0, sizeof(PPNCommandStruct));
|
||
|
|
||
|
ppnCommand->context.device_info = NULL;
|
||
|
ppnCommand->context.handle = NULL;
|
||
|
ppnCommand->context.bus_num = fmi->bus_id;
|
||
|
ppnCommand->command = PPN_COMMAND_ERASE;
|
||
|
ppnCommand->options = 0;
|
||
|
ppnCommand->entry[0].ceIdx = 0;
|
||
|
ppnCommand->entry[0].addr.row = wPpn;
|
||
|
ppnCommand->ceInfo[0].pages = 1;
|
||
|
ppnCommand->ceInfo[0].ce = ce;
|
||
|
ppnCommand->num_pages = 1;
|
||
|
|
||
|
h2fmiPpnPerformCommandList(&ppnCommand, 1);
|
||
|
|
||
|
switch(ppnCommand->page_status_summary)
|
||
|
{
|
||
|
case 0x40:
|
||
|
ret = FIL_SUCCESS;
|
||
|
break;
|
||
|
case 0x45:
|
||
|
ret = FIL_WRITE_FAIL_ERROR;
|
||
|
break;
|
||
|
default:
|
||
|
ret = FIL_CRITICAL_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif // !(defined(AND_READONLY)) || AND_SUPPORT_NVRAM
|
||
|
|
||
|
Int32 h2fmiPpnGetFirmwareVersion(UInt16 virtualCe, UInt8 *buffer)
|
||
|
{
|
||
|
h2fmi_t *fmi;
|
||
|
h2fmi_ce_t ce;
|
||
|
BOOL32 ret = FALSE32;
|
||
|
|
||
|
fmi = h2fmiTranslateVirtualCEToBus(virtualCe);
|
||
|
ce = h2fmiTranslateVirtualCEToCe(virtualCe);
|
||
|
|
||
|
if (NULL != fmi)
|
||
|
{
|
||
|
ret = h2fmi_ppn_get_fw_version(fmi, ce, buffer);
|
||
|
}
|
||
|
|
||
|
return (ret ? FIL_SUCCESS : FIL_CRITICAL_ERROR);
|
||
|
}
|
||
|
|
||
|
BOOL32 h2fmiPpnValidateFirmwareVersions(UInt8 *expected, UInt32 ce_count)
|
||
|
{
|
||
|
UInt8 buffer[NAND_DEV_PARAM_LEN_PPN];
|
||
|
UInt32 virtualCe;
|
||
|
Int32 ret;
|
||
|
BOOL32 mismatch = FALSE32;
|
||
|
|
||
|
for(virtualCe = 0; virtualCe < ce_count; virtualCe++)
|
||
|
{
|
||
|
ret = h2fmiPpnGetFirmwareVersion(virtualCe, buffer);
|
||
|
if (ret == FIL_SUCCESS)
|
||
|
{
|
||
|
if (WMR_MEMCMP(buffer, expected, NAND_DEV_PARAM_LEN_PPN) != 0)
|
||
|
{
|
||
|
WMR_PRINT(ERROR, "Bank %d reports incorrect PPN firmware version: expected \"%16s\", got \"%16s\"\n",
|
||
|
virtualCe, expected, buffer);
|
||
|
mismatch = TRUE32;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WMR_PRINT(ERROR, "Failed to retrieve PPN firmware version from bank %d\n", virtualCe);
|
||
|
mismatch = TRUE32;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mismatch ? FALSE32 : TRUE32;
|
||
|
}
|
||
|
|
||
|
Int32 h2fmiPpnGetControllerHwId(UInt16 virtualCe, UInt8 *buffer)
|
||
|
{
|
||
|
h2fmi_t *fmi;
|
||
|
h2fmi_ce_t ce;
|
||
|
BOOL32 ret = FALSE32;
|
||
|
|
||
|
fmi = h2fmiTranslateVirtualCEToBus(virtualCe);
|
||
|
ce = h2fmiTranslateVirtualCEToCe(virtualCe);
|
||
|
|
||
|
if (fmi)
|
||
|
{
|
||
|
ret = h2fmi_ppn_get_controller_hw_id(fmi, ce, buffer);
|
||
|
}
|
||
|
|
||
|
return (ret ? FIL_SUCCESS : FIL_CRITICAL_ERROR);
|
||
|
}
|
||
|
|
||
|
Int32 h2fmiPpnGetManufacturerId(UInt16 virtualCe, UInt8 *buffer)
|
||
|
{
|
||
|
h2fmi_t *fmi;
|
||
|
h2fmi_ce_t ce;
|
||
|
BOOL32 ret = FALSE32;
|
||
|
|
||
|
fmi = h2fmiTranslateVirtualCEToBus(virtualCe);
|
||
|
ce = h2fmiTranslateVirtualCEToCe(virtualCe);
|
||
|
|
||
|
if (fmi)
|
||
|
{
|
||
|
ret = h2fmi_ppn_get_manufacturer_id(fmi, ce, buffer);
|
||
|
}
|
||
|
return (ret ? FIL_SUCCESS : FIL_CRITICAL_ERROR);
|
||
|
}
|
||
|
|
||
|
BOOL32 h2fmiPpnValidateManufacturerIds(UInt8 *expected, UInt32 ce_count)
|
||
|
{
|
||
|
UInt8 buffer[NAND_DEV_PARAM_LEN_PPN];
|
||
|
UInt32 virtualCe;
|
||
|
Int32 ret;
|
||
|
BOOL32 mismatch = FALSE32;
|
||
|
|
||
|
for(virtualCe = 0; virtualCe < ce_count; virtualCe++)
|
||
|
{
|
||
|
ret = h2fmiPpnGetManufacturerId(virtualCe, buffer);
|
||
|
if (ret == FIL_SUCCESS)
|
||
|
{
|
||
|
if (WMR_MEMCMP(buffer, expected, PPN_DEVICE_ID_LEN) != 0)
|
||
|
{
|
||
|
WMR_PRINT(ERROR, "Bank %d reports incorrect PPN manufacturer id: expected %02X %02X %02X %02X %02X %02X, got %02X %02X %02X %02X %02X %02X\n",
|
||
|
virtualCe,
|
||
|
expected[0], expected[1], expected[2], expected[3], expected[4], expected[5],
|
||
|
buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
|
||
|
mismatch = TRUE32;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WMR_PRINT(ERROR, "Failed to retrieve PPN manufacturer id from bank %d\n", virtualCe);
|
||
|
mismatch = TRUE32;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mismatch ? FALSE32 : TRUE32;
|
||
|
}
|
||
|
|
||
|
Int32 h2fmiPpnGetControllerInfo(UInt16 virtualCe, UInt8 *mfg_id, UInt8 *fw_version)
|
||
|
{
|
||
|
Int32 ret = FIL_SUCCESS;
|
||
|
|
||
|
if (FIL_SUCCESS == h2fmiPpnGetManufacturerId(0, mfg_id))
|
||
|
{
|
||
|
WMR_PRINT(INIT, "PPN Manufacturer ID: %02X %02X %02X %02X %02X %02X\n",
|
||
|
mfg_id[0], mfg_id[1], mfg_id[2], mfg_id[3], mfg_id[4], mfg_id[5]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WMR_PRINT(ERROR, "Retrieving PPN Manufacturer ID failed!\n");
|
||
|
ret = FIL_CRITICAL_ERROR;
|
||
|
}
|
||
|
|
||
|
if (FIL_SUCCESS == h2fmiPpnGetFirmwareVersion(0, fw_version))
|
||
|
{
|
||
|
WMR_PRINT(INIT, "PPN Controller Version: %16.16s\n", fw_version);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WMR_PRINT(ERROR, "Retrieving PPN Controller Version failed!\n");
|
||
|
ret = FIL_CRITICAL_ERROR;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#define MAX_ATTEMPTS (3)
|
||
|
Int32 h2fmiPpnUpdateFw(UInt16 wVirtualCE, UInt8 *fw, UInt32 fw_length, UInt8 *fwa, UInt32 fwa_length)
|
||
|
{
|
||
|
h2fmi_t *fmi;
|
||
|
h2fmi_ce_t ce;
|
||
|
struct dma_segment sgl;
|
||
|
int i;
|
||
|
Int32 status;
|
||
|
Int32 ret_val = FIL_CRITICAL_ERROR;
|
||
|
|
||
|
fmi = h2fmiTranslateVirtualCEToBus(wVirtualCE);
|
||
|
ce = h2fmiTranslateVirtualCEToCe(wVirtualCE);
|
||
|
|
||
|
if (NULL == fmi)
|
||
|
{
|
||
|
return FIL_UNSUPPORTED_ERROR;
|
||
|
}
|
||
|
|
||
|
for( i = 0; (i < MAX_ATTEMPTS) && (ret_val != FIL_SUCCESS); i++)
|
||
|
{
|
||
|
if (NULL != fw)
|
||
|
{
|
||
|
WMR_PRINT(ALWAYS, "Updating fw on fmi %d, ce %d\n", fmi->bus_id, ce);
|
||
|
sgl.paddr = (UInt32)fw;
|
||
|
sgl.length = fw_length;
|
||
|
|
||
|
status = h2fmi_ppn_fw_update(fmi, ce, &sgl, fw_length, ppnFwTypeFw);
|
||
|
if (status == FIL_SUCCESS)
|
||
|
{
|
||
|
ret_val = FIL_SUCCESS;
|
||
|
}
|
||
|
else if (status == FIL_UNSUPPORTED_ERROR)
|
||
|
{
|
||
|
return status;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret_val = status;
|
||
|
WMR_PRINT(ERROR, "Firmware Update Failed: attempt %d/%d\n", i+1, MAX_ATTEMPTS);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if (NULL != fwa)
|
||
|
{
|
||
|
WMR_PRINT(ALWAYS, "Updating fw args on fmi %d, ce %d\n", fmi->bus_id, ce);
|
||
|
sgl.paddr = (UInt32)fwa;
|
||
|
sgl.length = fwa_length;
|
||
|
|
||
|
status = h2fmi_ppn_fw_update(fmi, ce, &sgl, fwa_length, ppnFwTypeFwa);
|
||
|
if (status == FIL_SUCCESS)
|
||
|
{
|
||
|
ret_val = FIL_SUCCESS;
|
||
|
}
|
||
|
else if (status == FIL_UNSUPPORTED_ERROR)
|
||
|
{
|
||
|
return status;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret_val = status;
|
||
|
WMR_PRINT(ERROR, "Firmware Arguments Update Failed: attempt %d/%d\n", i+1, MAX_ATTEMPTS);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret_val;
|
||
|
}
|
||
|
|
||
|
void h2fmi_ppn_recover_nand(void)
|
||
|
{
|
||
|
h2fmi_nand_reset_all(&g_fmi0);
|
||
|
if(g_fmi1.num_of_ce)
|
||
|
{
|
||
|
h2fmi_nand_reset_all(&g_fmi1);
|
||
|
}
|
||
|
|
||
|
#if SUPPORT_TOGGLE_NAND
|
||
|
if (g_fmi0.is_toggle_system)
|
||
|
{
|
||
|
g_fmi0.is_toggle = FALSE32;
|
||
|
h2fmi_reset(&g_fmi0);
|
||
|
|
||
|
if (g_fmi1.num_of_ce)
|
||
|
{
|
||
|
WMR_ASSERT(g_fmi1.is_toggle_system);
|
||
|
g_fmi1.is_toggle = FALSE32;
|
||
|
h2fmi_reset(&g_fmi1);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
h2fmi_ppn_post_rst_pre_pwrstate_operations(&g_fmi0);
|
||
|
if (g_fmi1.num_of_ce)
|
||
|
{
|
||
|
h2fmi_ppn_post_rst_pre_pwrstate_operations(&g_fmi1);
|
||
|
}
|
||
|
|
||
|
h2fmi_ppn_set_channel_power_state(&g_fmi0,
|
||
|
#if SUPPORT_TOGGLE_NAND
|
||
|
(g_fmi0.is_toggle_system) ? PPN_PS_TRANS_LOW_POWER_TO_DDR :
|
||
|
#endif
|
||
|
PPN_PS_TRANS_LOW_POWER_TO_ASYNC);
|
||
|
|
||
|
if (g_fmi1.num_of_ce)
|
||
|
{
|
||
|
h2fmi_ppn_set_channel_power_state(&g_fmi1,
|
||
|
#if SUPPORT_TOGGLE_NAND
|
||
|
(g_fmi1.is_toggle_system) ? PPN_PS_TRANS_LOW_POWER_TO_DDR :
|
||
|
#endif
|
||
|
PPN_PS_TRANS_LOW_POWER_TO_ASYNC);
|
||
|
}
|
||
|
|
||
|
#if SUPPORT_TOGGLE_NAND
|
||
|
if (g_fmi0.is_toggle_system)
|
||
|
{
|
||
|
g_fmi0.is_toggle = TRUE32;
|
||
|
h2fmi_reset(&g_fmi0);
|
||
|
if(g_fmi1.num_of_ce)
|
||
|
{
|
||
|
WMR_ASSERT(g_fmi1.is_toggle_system);
|
||
|
g_fmi1.is_toggle = TRUE32;
|
||
|
h2fmi_reset(&g_fmi1);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
g_fmi0.ppn->general_error = FALSE32;
|
||
|
g_fmi1.ppn->general_error = FALSE32;
|
||
|
|
||
|
#if H2FMI_PPN_VERIFY_SET_FEATURES
|
||
|
if(g_fmi0.num_of_ce)
|
||
|
{
|
||
|
h2fmi_ppn_verify_feature_shadow(&g_fmi0);
|
||
|
}
|
||
|
if(g_fmi1.num_of_ce)
|
||
|
{
|
||
|
h2fmi_ppn_verify_feature_shadow(&g_fmi1);
|
||
|
}
|
||
|
#endif // H2FMI_PPN_VERIFY_SET_FEATURES
|
||
|
}
|
||
|
|
||
|
Int32 h2fmiDmaDebugData(UInt8 virCE, UInt32 page, UInt32 pageCount, struct dma_segment *sgl, BOOL32 waitFortGDD)
|
||
|
{
|
||
|
h2fmi_t *fmi;
|
||
|
h2fmi_ce_t ce;
|
||
|
|
||
|
WMR_ASSERT(sgl != NULL);
|
||
|
|
||
|
fmi = h2fmiTranslateVirtualCEToBus(virCE);
|
||
|
ce = h2fmiTranslateVirtualCEToCe(virCE);
|
||
|
|
||
|
if (!fmi)
|
||
|
{
|
||
|
return FIL_UNSUPPORTED_ERROR;
|
||
|
}
|
||
|
|
||
|
h2fmi_ppn_dma_debug_data_payload(fmi, ce, page, pageCount, sgl, waitFortGDD);
|
||
|
|
||
|
return FIL_SUCCESS;
|
||
|
}
|
||
|
|
||
|
Int32 h2fmiVthSweepSetup(UInt8 virCE, UInt32 blk, h2fmi_ppn_failure_info_t *failInfo, UInt32 *totalDataToRead)
|
||
|
{
|
||
|
h2fmi_t *fmi;
|
||
|
h2fmi_ce_t ce;
|
||
|
UInt32 rowAddr;
|
||
|
LowFuncTbl *fil = FIL_GetFuncTbl();
|
||
|
const UInt32 block_shift = fil->GetDeviceInfo(AND_DEVINFO_BITS_PER_PAGE_ADDRESS);
|
||
|
|
||
|
if (!failInfo || !totalDataToRead)
|
||
|
{
|
||
|
return FIL_CRITICAL_ERROR;
|
||
|
}
|
||
|
|
||
|
fmi = h2fmiTranslateVirtualCEToBus(virCE);
|
||
|
ce = h2fmiTranslateVirtualCEToCe(virCE);
|
||
|
|
||
|
if (!fmi)
|
||
|
{
|
||
|
return FIL_UNSUPPORTED_ERROR;
|
||
|
}
|
||
|
|
||
|
rowAddr = blk << block_shift;
|
||
|
h2fmi_ppn_set_debug_data(fmi, ce, PPN_SET_DEBUG_DATA__EMB_SWEEP_ADDRESS, (UInt8*)&rowAddr, sizeof(rowAddr));
|
||
|
|
||
|
if (!fmi->ppn->general_error)
|
||
|
{
|
||
|
WMR_PRINT(ERROR, "No GEB during Vth Setup!\n");
|
||
|
return FIL_CRITICAL_ERROR;
|
||
|
}
|
||
|
|
||
|
if (h2fmi_ppn_get_general_error_info(fmi, ce, failInfo, totalDataToRead, NULL))
|
||
|
{
|
||
|
return FIL_SUCCESS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FIL_CRITICAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif //SUPPORT_PPN
|