903 lines
34 KiB
C
903 lines
34 KiB
C
// *****************************************************************************
|
|
//
|
|
// File: H2fmi_iop.c
|
|
//
|
|
// *****************************************************************************
|
|
//
|
|
// Notes:
|
|
//
|
|
// *****************************************************************************
|
|
//
|
|
// 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 Computer, Inc.
|
|
//
|
|
// *****************************************************************************
|
|
|
|
#include "H2fmi_iop.h"
|
|
#include "H2fmi_private.h"
|
|
#include "H2fmi_ppn.h"
|
|
#include "fmiss.h"
|
|
|
|
#include <platform/soc/hwclocks.h>
|
|
#include <platform/soc/pmgr.h>
|
|
#include <platform/memmap.h>
|
|
#include <platform.h>
|
|
#include <drivers/dma.h>
|
|
#include <drivers/aes.h>
|
|
#include <debug.h>
|
|
|
|
// =============================================================================
|
|
// external global variable definitions
|
|
// =============================================================================
|
|
|
|
extern UInt8 nand_whitening_key[16];
|
|
|
|
// =============================================================================
|
|
// private function declarations
|
|
// =============================================================================
|
|
|
|
|
|
bool h2fmi_setup_aes(h2fmi_t* fmi, IOPFMI* cmd, hfmi_aes_iv* aes_iv_array, UInt32 aes_chain_size, UInt32 aes_key_type, UInt32* aes_key, UInt32* seeds)
|
|
{
|
|
if (cmd->flags & (kIOPFMI_FLAGS_USE_AES | kIOPFMI_FLAGS_HOMOGENIZE))
|
|
{
|
|
switch (cmd->opcode)
|
|
{
|
|
case kIOPFMI_OPCODE_WRITE_SINGLE:
|
|
case kIOPFMI_OPCODE_WRITE_MULTIPLE:
|
|
case kIOPFMI_OPCODE_PPN_WRITE:
|
|
fmi->aes_cxt.command = AES_CMD_ENC;
|
|
break;
|
|
case kIOPFMI_OPCODE_READ_SINGLE:
|
|
case kIOPFMI_OPCODE_READ_MULTIPLE:
|
|
case kIOPFMI_OPCODE_PPN_READ:
|
|
fmi->aes_cxt.command = AES_CMD_DEC;
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
return false;
|
|
break;
|
|
}
|
|
if (cmd->flags & kIOPFMI_FLAGS_USE_AES)
|
|
{
|
|
fmi->current_aes_iv_array = (hfmi_aes_iv*)aes_iv_array;
|
|
switch (aes_key_type)
|
|
{
|
|
case kIOPFMI_AES_KEY_TYPE_USER128:
|
|
fmi->aes_cxt.keying = AES_KEY_TYPE_USER | AES_KEY_SIZE_128;
|
|
fmi->aes_cxt.key = aes_key;
|
|
WMR_PRINT(CRYPTO, "Using HW key IV: 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
aes_key[0], aes_key[1], aes_key[2], aes_key[3]);
|
|
break;
|
|
case kIOPFMI_AES_KEY_TYPE_USER192:
|
|
fmi->aes_cxt.keying = AES_KEY_TYPE_USER | AES_KEY_SIZE_192;
|
|
fmi->aes_cxt.key = aes_key;
|
|
WMR_PRINT(CRYPTO, "Using HW key IV: 0x%08x 0x%08x 0x%08x 0x%08x 0X%08x 0x%08x\n",
|
|
aes_key[0], aes_key[1], aes_key[2], aes_key[3], aes_key[4], aes_key[5]);
|
|
break;
|
|
case kIOPFMI_AES_KEY_TYPE_USER256:
|
|
fmi->aes_cxt.keying = AES_KEY_TYPE_USER | AES_KEY_SIZE_256;
|
|
fmi->aes_cxt.key = aes_key;
|
|
WMR_PRINT(CRYPTO, "Using HW key IV: 0x%08x 0x%08x 0x%08x 0x%08x 0X%08x 0x%08x 0X%08x 0x%08x\n",
|
|
aes_key[0], aes_key[1], aes_key[2], aes_key[3], aes_key[4], aes_key[5], aes_key[6], aes_key[7]);
|
|
break;
|
|
case kIOPFMI_AES_KEY_TYPE_UID0:
|
|
fmi->aes_cxt.keying = AES_KEY_TYPE_UID0;
|
|
fmi->aes_cxt.key = NULL;
|
|
WMR_PRINT(CRYPTO, "Using HW UID0\n");
|
|
break;
|
|
case kIOPFMI_AES_KEY_TYPE_GID0:
|
|
fmi->aes_cxt.keying = AES_KEY_TYPE_GID0;
|
|
fmi->aes_cxt.key = NULL;
|
|
WMR_PRINT(CRYPTO, "Using HW GID0\n");
|
|
break;
|
|
case kIOPFMI_AES_KEY_TYPE_GID1:
|
|
fmi->aes_cxt.keying = AES_KEY_TYPE_GID1;
|
|
fmi->aes_cxt.key = NULL;
|
|
WMR_PRINT(CRYPTO, "Using HW GID1\n");
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
return false;
|
|
break;
|
|
}
|
|
fmi->aes_cxt.chunk_size = aes_chain_size;
|
|
}
|
|
else
|
|
{
|
|
fmi->aes_cxt.keying = (AES_KEY_TYPE_USER | AES_KEY_SIZE_128);
|
|
fmi->aes_cxt.key = nand_whitening_key;
|
|
fmi->current_aes_iv_array = NULL;
|
|
fmi->aes_cxt.chunk_size = fmi->logical_page_size;
|
|
WMR_PRINT(CRYPTO, "Using constant AES for whitening\n");
|
|
}
|
|
|
|
fmi->aes_cxt.chunk_size = aes_chain_size;
|
|
fmi->aes_cxt.iv_func = h2fmi_aes_iv;
|
|
fmi->aes_cxt.iv_func_arg = fmi;
|
|
fmi->iv_seed_array = seeds;
|
|
|
|
fmi->current_aes_cxt = &fmi->aes_cxt;
|
|
}
|
|
else
|
|
{
|
|
//WMR_PRINT(CRYPTO, "Disabling AES\n");
|
|
fmi->current_aes_cxt = NULL;
|
|
fmi->current_aes_iv_array = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void h2fmi_restoreFmiRegs(h2fmi_t *fmi)
|
|
{
|
|
// Restore any register state that may have been lost
|
|
// due to power gating
|
|
#if FMI_VERSION > 2
|
|
restoreTimingRegs(fmi);
|
|
turn_on_fmc(fmi);
|
|
#endif
|
|
|
|
#if FMISS_ENABLED
|
|
if (!fmi->is_ppn)
|
|
{
|
|
fmiss_raw_init_sequences(fmi);
|
|
}
|
|
else
|
|
{
|
|
fmiss_init_sequences(fmi);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// =============================================================================
|
|
// implementation function declarations
|
|
// =============================================================================
|
|
|
|
void h2fmi_iop_set_config(h2fmi_t* fmi, IOPFMI_SetConfig* cmd)
|
|
{
|
|
// record which interface is being used
|
|
dprintf(DEBUG_SPEW, "configuring FMI%d\n", cmd->fmi);
|
|
fmi->regs = (cmd->fmi == 0) ? FMI0 : FMI1;
|
|
fmi->bus_id = cmd->fmi;
|
|
|
|
// record necessary device geometry information
|
|
fmi->bytes_per_spare = cmd->bytes_per_spare;
|
|
fmi->num_of_ce = cmd->num_of_ce;
|
|
fmi->pages_per_block = cmd->pages_per_block;
|
|
fmi->blocks_per_ce = cmd->blocks_per_ce;
|
|
fmi->total_bytes_per_meta = cmd->total_bytes_per_meta;
|
|
fmi->valid_bytes_per_meta = cmd->valid_bytes_per_meta;
|
|
fmi->bytes_per_page = cmd->bytes_per_page;
|
|
fmi->sectors_per_page = fmi->bytes_per_page / H2FMI_BYTES_PER_SECTOR;
|
|
fmi->valid_ces = cmd->valid_ces;
|
|
fmi->is_ppn = cmd->ppn;
|
|
fmi->logical_page_size = cmd->logical_page_size;
|
|
fmi->clock_speed_khz = cmd->clock_speed_khz;
|
|
|
|
// TODO: decide on appropriate infrastructure in support of
|
|
// vendor-specific superblock formats and command sequences
|
|
fmi->banks_per_ce = 1;
|
|
|
|
// calculate and store bus interface timing control
|
|
WMR_PRINT(INF, "cmd->read_sample_cycles : %d\n", cmd->read_sample_cycles);
|
|
WMR_PRINT(INF, "cmd->read_setup_cycles : %d\n", cmd->read_setup_cycles);
|
|
WMR_PRINT(INF, "cmd->read_hold_cycles : %d\n", cmd->read_hold_cycles);
|
|
WMR_PRINT(INF, "cmd->write_setup_cycles : %d\n", cmd->write_setup_cycles);
|
|
WMR_PRINT(INF, "cmd->write_hold_cycles : %d\n", cmd->write_hold_cycles);
|
|
|
|
fmi->if_ctrl = (FMC_IF_CTRL__DCCYCLE(cmd->read_sample_cycles) |
|
|
FMC_IF_CTRL__REB_SETUP(cmd->read_setup_cycles) |
|
|
FMC_IF_CTRL__REB_HOLD(cmd->read_hold_cycles) |
|
|
FMC_IF_CTRL__WEB_SETUP(cmd->write_setup_cycles) |
|
|
FMC_IF_CTRL__WEB_HOLD(cmd->write_hold_cycles));
|
|
|
|
WMR_PRINT(INF, "cmd->retire_on_invalid_refresh : %d\n", cmd->retire_on_invalid_refresh);
|
|
fmi->retire_on_invalid_refresh = cmd->retire_on_invalid_refresh;
|
|
|
|
#if SUPPORT_TOGGLE_NAND
|
|
fmi->is_toggle_system = cmd->toggle_system;
|
|
fmi->is_toggle = cmd->toggle;
|
|
|
|
if (fmi->is_toggle)
|
|
{
|
|
WMR_PRINT(INF, "cmd->ce_hold_cycles : %d\n", cmd->ce_hold_cycles);
|
|
WMR_PRINT(INF, "cmd->ce_setup_cycles : %d\n", cmd->ce_setup_cycles);
|
|
WMR_PRINT(INF, "cmd->adl_cycles : %d\n", cmd->adl_cycles);
|
|
WMR_PRINT(INF, "cmd->whr_cycles : %d\n", cmd->whr_cycles);
|
|
WMR_PRINT(INF, "cmd->read_pre_cycles : %d\n", cmd->read_pre_cycles);
|
|
WMR_PRINT(INF, "cmd->read_post_cycles : %d\n", cmd->read_post_cycles);
|
|
WMR_PRINT(INF, "cmd->write_pre_cycles : %d\n", cmd->write_pre_cycles);
|
|
WMR_PRINT(INF, "cmd->write_post_cycles : %d\n", cmd->write_post_cycles);
|
|
WMR_PRINT(INF, "cmd->enable_diff_DQS : %d\n", cmd->enable_diff_DQS);
|
|
WMR_PRINT(INF, "cmd->enable_diff_RE : %d\n", cmd->enable_diff_RE);
|
|
WMR_PRINT(INF, "cmd->enable_VREF : %d\n", cmd->enable_VREF);
|
|
|
|
fmi->dqs_ctrl = cmd->reg_dqs_delay;
|
|
fmi->timing_ctrl_1 = (FMC_TOGGLE_CTRL_1_DDR_RD_PRE_TIME(cmd->read_pre_cycles) |
|
|
FMC_TOGGLE_CTRL_1_DDR_RD_POST_TIME(cmd->read_post_cycles) |
|
|
FMC_TOGGLE_CTRL_1_DDR_WR_PRE_TIME(cmd->write_pre_cycles) |
|
|
FMC_TOGGLE_CTRL_1_DDR_WR_POST_TIME(cmd->write_post_cycles));
|
|
fmi->timing_ctrl_2 = (FMC_TOGGLE_CTRL_2_CE_SETUP_TIME(cmd->ce_setup_cycles) |
|
|
FMC_TOGGLE_CTRL_2_CE_HOLD_TIME(cmd->ce_hold_cycles) |
|
|
FMC_TOGGLE_CTRL_2_NAND_TIMING_ADL(cmd->adl_cycles) |
|
|
FMC_TOGGLE_CTRL_2_NAND_TIMING_WHR(cmd->whr_cycles));
|
|
fmi->toggle_if_ctrl = (FMC_IF_CTRL__DCCYCLE(0) |
|
|
FMC_IF_CTRL__REB_SETUP(cmd->dqs_half_cycles) |
|
|
FMC_IF_CTRL__REB_HOLD(0) |
|
|
FMC_IF_CTRL__WEB_SETUP(cmd->write_setup_cycles) |
|
|
FMC_IF_CTRL__WEB_HOLD(cmd->write_hold_cycles));
|
|
fmi->useDiffDQS = cmd->enable_diff_DQS;
|
|
fmi->useDiffRE = cmd->enable_diff_RE;
|
|
fmi->useVREF = cmd->enable_VREF;
|
|
}
|
|
#endif
|
|
|
|
h2fmi_restoreFmiRegs(fmi);
|
|
|
|
// always report config information
|
|
WMR_PRINT(INF, "fmi->regs : %p\n", fmi->regs);
|
|
WMR_PRINT(INF, "fmi->valid_ces : %p\n", fmi->valid_ces);
|
|
WMR_PRINT(INF, "fmi->num_of_ce : %p\n", fmi->num_of_ce);
|
|
WMR_PRINT(INF, "fmi->banks_per_ce : %p\n", fmi->banks_per_ce);
|
|
WMR_PRINT(INF, "fmi->pages_per_block : %p\n", fmi->pages_per_block);
|
|
WMR_PRINT(INF, "fmi->sectors_per_page : %p\n", fmi->sectors_per_page);
|
|
WMR_PRINT(INF, "fmi->bytes_per_spare : %p\n", fmi->bytes_per_spare);
|
|
WMR_PRINT(INF, "fmi->blocks_per_ce : %p\n", fmi->blocks_per_ce);
|
|
WMR_PRINT(INF, "fmi->valid_bytes_per_meta : %p\n", fmi->valid_bytes_per_meta);
|
|
WMR_PRINT(INF, "fmi->total_bytes_per_meta : %p\n", fmi->total_bytes_per_meta);
|
|
WMR_PRINT(INF, "fmi->if_ctrl : %p\n", fmi->if_ctrl);
|
|
#if SUPPORT_TOGGLE_NAND
|
|
if (fmi->is_toggle)
|
|
{
|
|
WMR_PRINT(INF, "fmi->dqs_ctrl : %p\n", fmi->dqs_ctrl);
|
|
WMR_PRINT(INF, "fmi->timing_ctrl_1 : %p\n", fmi->timing_ctrl_1);
|
|
WMR_PRINT(INF, "fmi->timing_ctrl_2 : %p\n", fmi->timing_ctrl_2);
|
|
WMR_PRINT(INF, "fmi->toggle_if_ctrl : %p\n", fmi->toggle_if_ctrl);
|
|
WMR_PRINT(INF, "fmi->is_toggle : %p\n", fmi->is_toggle);
|
|
}
|
|
#endif
|
|
WMR_PRINT(INF, "fmi->bytes_per_page : %p\n", fmi->bytes_per_page);
|
|
WMR_PRINT(INF, "fmi->is_ppn : %p\n", fmi->is_ppn);
|
|
|
|
// these steps are only taken when fmi is first configured
|
|
if (!fmi->initialized)
|
|
{
|
|
// Make certain the FMC is enabled upon init.
|
|
turn_on_fmc(fmi);
|
|
|
|
// Perform system-specific initialization.
|
|
h2fmi_init_sys(fmi);
|
|
|
|
// indicate that fmi is initialized
|
|
fmi->initialized = TRUE32;
|
|
|
|
fmi->stateMachine.currentMode = fmiNone;
|
|
fmi->wMaxOutstandingCEWriteOperations = cmd->wMaxOutstandingCEWriteOperations;
|
|
}
|
|
|
|
fmi->activeCe = ~0;
|
|
if (fmi->is_ppn && (!fmi->is_ppn_channel_init))
|
|
{
|
|
#if SUPPORT_PPN
|
|
if (!h2fmi_ppn_init_channel(fmi,
|
|
cmd->ppn_debug_flags_valid,
|
|
cmd->ppn_debug_flags,
|
|
cmd->ppn_vs_debug_flags_valid,
|
|
cmd->ppn_vs_debug_flags,
|
|
cmd->ppn_allow_saving_debug_data,
|
|
cmd->ppn_version))
|
|
{
|
|
WMR_PANIC("[FIL:ERR] Unable to initialize PPN device\n");
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_FAILURE;
|
|
return;
|
|
}
|
|
fmi->is_ppn_channel_init = TRUE32;
|
|
#else
|
|
WMR_PANIC("!SUPPORT_PPN");
|
|
#endif
|
|
}
|
|
|
|
// Cache page config registers
|
|
if (fmi->is_ppn)
|
|
{
|
|
#if SUPPORT_PPN
|
|
fmi->fmi_config_reg = h2fmi_ppn_calculate_fmi_config(fmi);
|
|
|
|
#if FMI_VERSION > 0
|
|
fmi->fmi_data_size_reg = h2fmi_ppn_calculate_fmi_data_size(fmi);
|
|
#endif /* FMI_VERSION > 0*/
|
|
#else //SUPPORT_PPN
|
|
WMR_PANIC("!SUPPORT_PPN");
|
|
#endif //SUPPORT_PPN
|
|
}
|
|
else
|
|
{
|
|
// Cache ECC configuration
|
|
fmi->correctable_bits = h2fmi_calculate_ecc_bits(fmi);
|
|
|
|
fmi->fmi_config_reg = h2fmi_calculate_fmi_config(fmi);
|
|
#if FMI_VERSION > 0
|
|
fmi->fmi_data_size_reg = h2fmi_calculate_fmi_data_size(fmi);
|
|
#endif /* FMI_VERSION > 0*/
|
|
}
|
|
// record success status in command structure
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
|
|
}
|
|
|
|
void h2fmi_iop_read_chip_ids(h2fmi_t* fmi, IOPFMI_ReadChipIDs* cmd)
|
|
{
|
|
IOPFMI_chipid_t* ids_buf = (IOPFMI_chipid_t*)mem_static_map_cached(cmd->chip_id_buffer);
|
|
IOPFMI_status_t status = kIOPFMI_STATUS_SUCCESS;
|
|
UInt32 valid_ces;
|
|
UInt32 ce;
|
|
|
|
WMR_ASSERT(ids_buf != MAP_FAILED);
|
|
|
|
valid_ces = fmi->valid_ces;
|
|
fmi->valid_ces = 0xffff;
|
|
|
|
for (ce = 0; ce < H2FMI_MAX_CE_TOTAL; ce++)
|
|
{
|
|
h2fmi_chipid_t* chipid = (h2fmi_chipid_t*)&ids_buf[ce];
|
|
UInt8 *ptr = (UInt8 *)&ids_buf[ce];
|
|
|
|
if (!h2fmi_nand_read_id(fmi, ce, chipid, CHIPID_ADDR))
|
|
{
|
|
status = kIOPFMI_STATUS_FAILED_READ_ID;
|
|
break;
|
|
}
|
|
|
|
WMR_PRINT(INF, "Read Chip ID %02X %02X %02X %02X %02X %02X to %p\n",
|
|
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], chipid);
|
|
}
|
|
fmi->valid_ces = valid_ces;
|
|
cmd->iopfmi.status = status;
|
|
|
|
WMR_PREPARE_WRITE_BUFFER(ids_buf, H2FMI_MAX_CE_TOTAL * sizeof(IOPFMI_chipid_t));
|
|
}
|
|
|
|
|
|
void h2fmi_iop_reset_everything(h2fmi_t* fmi, IOPFMI_ResetEverything* cmd)
|
|
{
|
|
IOPFMI_status_t status = kIOPFMI_STATUS_SUCCESS;
|
|
BOOL32 ret = TRUE32;
|
|
|
|
h2fmi_reset(fmi);
|
|
|
|
h2fmi_init_sys(fmi);
|
|
|
|
ret = h2fmi_nand_reset_all(fmi);
|
|
if (!ret)
|
|
{
|
|
status = kIOPFMI_STATUS_FAILED_RESET_ALL;
|
|
}
|
|
}
|
|
|
|
void h2fmi_iop_erase_multiple(h2fmi_t* fmi, IOPFMI_EraseMultiple* cmd)
|
|
{
|
|
BOOL32 erase_failed = FALSE32;
|
|
|
|
WMR_PRINT(ERASE, "IOP received Multi-Erase command %d elements\n", cmd->number_of_elements);
|
|
|
|
if (!h2fmi_erase_blocks(fmi,
|
|
cmd->number_of_elements,
|
|
cmd->ce,
|
|
cmd->block_number,
|
|
&erase_failed))
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_ERASE_BLOCK;
|
|
}
|
|
else if (erase_failed)
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Copy erase status back out ...
|
|
*/
|
|
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
|
|
cmd->failure_details.wFirstFailingCE = fmi->failureDetails.wFirstFailingCE;
|
|
}
|
|
|
|
void h2fmi_iop_erase_single(h2fmi_t* fmi, IOPFMI_EraseSingle* cmd)
|
|
{
|
|
BOOL32 erase_failed = FALSE32;
|
|
|
|
WMR_PRINT(ERASE, "IOP received Erase command CE %d Block %d\n", cmd->ce, cmd->block_number);
|
|
|
|
fmi->failureDetails.wCEStatusArray = &fmi->failureDetails.wSingleCEStatus;
|
|
fmi->failureDetails.wCEStatusArrayModulusMask = 0;
|
|
|
|
if (!h2fmi_erase_blocks(fmi,
|
|
1,
|
|
&cmd->ce,
|
|
&cmd->block_number,
|
|
&erase_failed))
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_ERASE_BLOCK;
|
|
}
|
|
else if (erase_failed)
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
|
|
}
|
|
/**
|
|
* Copy erase status back out ...
|
|
*/
|
|
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
|
|
cmd->failure_details.wSingleCEStatus = fmi->failureDetails.wSingleCEStatus;
|
|
}
|
|
|
|
void h2fmi_iop_read_single(h2fmi_t* fmi, IOPFMI_IOSingle* cmd)
|
|
{
|
|
BOOL32 gather_corrections = (0 != cmd->correction_address);
|
|
|
|
UInt8* correct_buf = (gather_corrections
|
|
? (UInt8*)mem_static_map_cached(cmd->correction_address)
|
|
: NULL);
|
|
|
|
UInt8* data_buf = (UInt8*)cmd->data_address;
|
|
UInt8* meta_buf = (UInt8*)cmd->meta_address;
|
|
hfmi_aes_iv* aes_iv_array = NULL;
|
|
|
|
WMR_ASSERT(correct_buf != MAP_FAILED);
|
|
|
|
WMR_PRINT(READ, "IOP received Read Single command CE %d Block %d\n", cmd->ce, cmd->page_number);
|
|
|
|
if (cmd->aes_iv_array)
|
|
{
|
|
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
|
|
WMR_ASSERT(aes_iv_array != MAP_FAILED);
|
|
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
|
|
}
|
|
|
|
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, &cmd->page_number);
|
|
|
|
cmd->iopfmi.status = h2fmi_read_page(fmi,
|
|
cmd->ce,
|
|
cmd->page_number,
|
|
data_buf,
|
|
meta_buf,
|
|
correct_buf,
|
|
NULL);
|
|
|
|
// clean corrections buffer, if not NULL
|
|
if (gather_corrections)
|
|
{
|
|
WMR_PREPARE_WRITE_BUFFER(correct_buf,
|
|
fmi->sectors_per_page * sizeof(IOPFMI_correction_t));
|
|
}
|
|
}
|
|
|
|
void h2fmi_iop_read_multiple(h2fmi_t* fmi, IOPFMI_IOMultiple* cmd)
|
|
{
|
|
const UInt32 page_count = cmd->page_count;
|
|
|
|
BOOL32 gather_corrections = (0 != cmd->corrections_array);
|
|
UInt8* corrections_array = (gather_corrections
|
|
? (UInt8*)mem_static_map_cached(cmd->corrections_array)
|
|
: NULL);
|
|
|
|
h2fmi_ce_t* chip_enable_array = (h2fmi_ce_t*)mem_static_map_cached(cmd->chip_enable_array);
|
|
UInt32* page_number_array = (UInt32*)mem_static_map_cached(cmd->page_number_array);
|
|
hfmi_aes_iv* aes_iv_array = NULL;
|
|
|
|
|
|
WMR_ASSERT(corrections_array != MAP_FAILED);
|
|
WMR_ASSERT(chip_enable_array != MAP_FAILED);
|
|
WMR_ASSERT(page_number_array != MAP_FAILED);
|
|
|
|
if (cmd->aes_iv_array)
|
|
{
|
|
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
|
|
WMR_ASSERT(aes_iv_array != MAP_FAILED);
|
|
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
|
|
}
|
|
|
|
// Invalidate control arrays, ensuring that these values are
|
|
// re-read from physical memory.
|
|
WMR_PREPARE_READ_BUFFER(chip_enable_array, page_count * sizeof(UInt32));
|
|
WMR_PREPARE_READ_BUFFER(page_number_array, page_count * sizeof(UInt32));
|
|
|
|
UInt32* data_segments_array = (UInt32*)mem_static_map_cached(cmd->data_segments_array);
|
|
UInt32* meta_segments_array = (UInt32*)mem_static_map_cached(cmd->meta_segments_array);
|
|
WMR_ASSERT(data_segments_array != MAP_FAILED);
|
|
WMR_ASSERT(meta_segments_array != MAP_FAILED);
|
|
|
|
WMR_PREPARE_READ_BUFFER(data_segments_array, cmd->data_segment_array_length_in_bytes);
|
|
WMR_PREPARE_READ_BUFFER(meta_segments_array, cmd->meta_segment_array_length_in_bytes);
|
|
|
|
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, page_number_array);
|
|
|
|
WMR_PRINT(READ, "IOP received Read Multiple command NumPages %d\n", page_count);
|
|
|
|
// perform multi-read
|
|
cmd->iopfmi.status = h2fmi_read_multi(fmi,
|
|
page_count,
|
|
chip_enable_array,
|
|
page_number_array,
|
|
(void*)data_segments_array,
|
|
(void*)meta_segments_array,
|
|
corrections_array,
|
|
NULL);
|
|
|
|
// clean corrections buffer, if not NULL
|
|
if (gather_corrections)
|
|
{
|
|
WMR_PREPARE_WRITE_BUFFER(corrections_array,
|
|
page_count * fmi->sectors_per_page * sizeof(IOPFMI_correction_t));
|
|
}
|
|
/**
|
|
* Copy failure data.
|
|
*/
|
|
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
|
|
cmd->failure_details.wOverallOperationFailure = fmi->failureDetails.wOverallOperationFailure;
|
|
cmd->failure_details.wFirstFailingCE = fmi->failureDetails.wFirstFailingCE;
|
|
#if (defined (WMR_DEBUG) && WMR_DEBUG)
|
|
if ( (kIOPFMI_STATUS_SUCCESS != cmd->iopfmi.status) &&
|
|
(kIOPFMI_STATUS_BLANK != cmd->iopfmi.status) &&
|
|
(kIOPFMI_STATUS_FUSED != cmd->iopfmi.status))
|
|
{
|
|
unsigned k = fmi->failureDetails.wCEStatusArrayModulusMask;
|
|
WMR_PRINT(ERROR, "IOP --> err 0x%08x, %d completed on %d\n",cmd->iopfmi.status, fmi->failureDetails.wNumCE_Executed,fmi->bus_id);
|
|
while ( k>0 )
|
|
{
|
|
WMR_PRINT(ERROR, "Status[%d] = 0x%x\n",k,fmi->failureDetails.wCEStatusArray[k]);
|
|
k--;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void h2fmi_iop_write_single(h2fmi_t* fmi, IOPFMI_IOSingle* cmd)
|
|
{
|
|
UInt8* data_buf = (UInt8*)cmd->data_address;
|
|
UInt8* meta_buf = (UInt8*)cmd->meta_address;
|
|
BOOL32 write_failed = FALSE32;
|
|
hfmi_aes_iv* aes_iv_array = NULL;
|
|
|
|
if (cmd->aes_iv_array)
|
|
{
|
|
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
|
|
WMR_ASSERT(aes_iv_array != MAP_FAILED);
|
|
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
|
|
}
|
|
WMR_PRINT(WRITE, "IOP received Write Single command CE %d Block %d\n", cmd->ce, cmd->page_number);
|
|
|
|
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, &cmd->page_number);
|
|
|
|
if (!h2fmi_write_page(fmi,
|
|
cmd->ce,
|
|
cmd->page_number,
|
|
data_buf,
|
|
meta_buf,
|
|
&write_failed))
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_WRITE_PAGE;
|
|
}
|
|
else if (write_failed)
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
void h2fmi_iop_write_multiple(h2fmi_t* fmi, IOPFMI_IOMultiple* cmd)
|
|
{
|
|
const UInt32 page_count = cmd->page_count;
|
|
|
|
h2fmi_ce_t* chip_enable_array = (h2fmi_ce_t*)mem_static_map_cached(cmd->chip_enable_array);
|
|
UInt32* page_number_array = (UInt32*)mem_static_map_cached(cmd->page_number_array);
|
|
|
|
BOOL32 write_failed = FALSE32;
|
|
hfmi_aes_iv* aes_iv_array = NULL;
|
|
|
|
WMR_ASSERT(chip_enable_array != MAP_FAILED);
|
|
WMR_ASSERT(page_number_array != MAP_FAILED);
|
|
|
|
if (cmd->aes_iv_array)
|
|
{
|
|
aes_iv_array = (hfmi_aes_iv*)mem_static_map_cached(cmd->aes_iv_array);
|
|
WMR_ASSERT(aes_iv_array != MAP_FAILED);
|
|
WMR_PREPARE_READ_BUFFER(aes_iv_array, cmd->aes_num_chains * sizeof(hfmi_aes_iv));
|
|
}
|
|
// Invalidate control arrays, ensuring that these values are
|
|
// re-read from physical memory.
|
|
WMR_PREPARE_READ_BUFFER(chip_enable_array, page_count * sizeof(UInt32));
|
|
WMR_PREPARE_READ_BUFFER(page_number_array, page_count * sizeof(UInt32));
|
|
|
|
UInt32* data_segments_array = (UInt32*)mem_static_map_cached(cmd->data_segments_array);
|
|
UInt32* meta_segments_array = (UInt32*)mem_static_map_cached(cmd->meta_segments_array);
|
|
|
|
WMR_ASSERT(data_segments_array != MAP_FAILED);
|
|
WMR_ASSERT(meta_segments_array != MAP_FAILED);
|
|
WMR_PREPARE_READ_BUFFER(data_segments_array, cmd->data_segment_array_length_in_bytes);
|
|
WMR_PREPARE_READ_BUFFER(meta_segments_array, cmd->meta_segment_array_length_in_bytes);
|
|
|
|
WMR_PRINT(WRITE, "IOP received Write Multiple command NumPages %d\n", page_count);
|
|
|
|
h2fmi_setup_aes(fmi, &cmd->iopfmi, aes_iv_array, cmd->aes_chain_size, cmd->aes_key_type, (UInt32*)cmd->aes_key_bytes, page_number_array);
|
|
|
|
// perform multi-write
|
|
if (!h2fmi_write_multi(fmi,
|
|
page_count,
|
|
chip_enable_array,
|
|
page_number_array,
|
|
(void*)data_segments_array,
|
|
(void*)meta_segments_array,
|
|
&write_failed,
|
|
cmd->vendorProtocol))
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_WRITE_MULTI;
|
|
}
|
|
else if (write_failed)
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_DEVICE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
|
|
}
|
|
/**
|
|
* Copy failure data.
|
|
*/
|
|
cmd->failure_details.wNumCE_Executed = fmi->failureDetails.wNumCE_Executed;
|
|
cmd->failure_details.wFirstFailingCE = fmi->failureDetails.wFirstFailingCE;
|
|
}
|
|
|
|
void h2fmi_iop_read_raw(h2fmi_t* fmi, IOPFMI_IORaw* cmd)
|
|
{
|
|
UInt8* page_buf = (UInt8*)(cmd->buffer_address);
|
|
|
|
UInt8* data_buf = page_buf + 0;
|
|
UInt8* spare_buf = page_buf + fmi->bytes_per_page;
|
|
|
|
fmi->current_aes_cxt = NULL;
|
|
|
|
WMR_PRINT(READ, "IOP received Read Raw command CE %d Page %d\n", cmd->ce, cmd->page_number);
|
|
|
|
if (!h2fmi_read_raw_page(fmi,
|
|
cmd->ce,
|
|
cmd->page_number,
|
|
data_buf,
|
|
spare_buf))
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_FAILED_READ_RAW;
|
|
}
|
|
else
|
|
{
|
|
cmd->iopfmi.status = kIOPFMI_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
void h2fmi_iop_write_raw(h2fmi_t* fmi, IOPFMI_IORaw* cmd)
|
|
{
|
|
const UInt32 data_bytes_per_page = (fmi->sectors_per_page * H2FMI_BYTES_PER_SECTOR);
|
|
const UInt32 bytes_per_page = data_bytes_per_page + fmi->bytes_per_spare;
|
|
UInt8* page_buf = (UInt8*)cmd->buffer_address;
|
|
UInt8* data_buf = page_buf;
|
|
|
|
// deal with cache coherency issues
|
|
const UInt32 data_clean_size = bytes_per_page;
|
|
|
|
fmi->current_aes_cxt = NULL;
|
|
|
|
WMR_PRINT(WRITE, "IOP sending write raw ce %d page %d data_buf %08x size %d\n",
|
|
cmd->ce,
|
|
cmd->page_number,
|
|
data_buf,
|
|
bytes_per_page);
|
|
|
|
// Invalidate data and meta buffers, ensuring their
|
|
// contents are re-read from physical memory
|
|
WMR_PREPARE_READ_BUFFER(data_buf, data_clean_size);
|
|
|
|
cmd->iopfmi.status = h2fmi_write_raw_page(fmi,
|
|
cmd->ce,
|
|
cmd->page_number,
|
|
data_buf);
|
|
}
|
|
|
|
void h2fmi_iop_read_bootpage(h2fmi_t* fmi, IOPFMI_IOBootPage* cmd)
|
|
{
|
|
BOOL32 gather_corrections = (0 != cmd->corrections_array);
|
|
|
|
UInt8* corrections_array = (gather_corrections
|
|
? (UInt8*)mem_static_map_cached(cmd->corrections_array)
|
|
: NULL);
|
|
|
|
UInt8* page_buf = (UInt8*)cmd->buffer_address;
|
|
|
|
WMR_ASSERT(corrections_array != MAP_FAILED);
|
|
|
|
fmi->current_aes_cxt = NULL;
|
|
|
|
WMR_PRINT(READ, "IOP received Read Boot Page command CE %d Page %d\n", cmd->ce, cmd->page_number);
|
|
|
|
cmd->iopfmi.status = h2fmi_read_bootpage(fmi,
|
|
cmd->ce,
|
|
cmd->page_number,
|
|
page_buf,
|
|
corrections_array);
|
|
|
|
// clean corrections buffer, if not NULL
|
|
if (gather_corrections)
|
|
{
|
|
WMR_PREPARE_WRITE_BUFFER(corrections_array,
|
|
fmi->sectors_per_page * sizeof(IOPFMI_correction_t));
|
|
}
|
|
}
|
|
|
|
void h2fmi_iop_write_bootpage(h2fmi_t* fmi, IOPFMI_IOBootPage* cmd)
|
|
{
|
|
UInt8* page_buf = (UInt8*)mem_static_map_cached(cmd->buffer_address);
|
|
|
|
WMR_ASSERT(page_buf != MAP_FAILED);
|
|
|
|
WMR_PREPARE_READ_BUFFER(page_buf, H2FMI_BOOT_BYTES_PER_PAGE);
|
|
|
|
fmi->current_aes_cxt = NULL;
|
|
|
|
WMR_PRINT(WRITE, "IOP received Write Boot Page command CE %d Page %d\n", cmd->ce, cmd->page_number);
|
|
|
|
cmd->iopfmi.status = h2fmi_write_bootpage(fmi,
|
|
cmd->ce,
|
|
cmd->page_number,
|
|
page_buf);
|
|
}
|
|
|
|
|
|
|
|
void h2fmi_iop_sleep(h2fmi_t* fmi)
|
|
{
|
|
dprintf(DEBUG_CRITICAL, "sleeping FMI%d\n", h2fmi_bus_index(fmi));
|
|
}
|
|
|
|
void h2fmi_iop_wake(h2fmi_t* fmi)
|
|
{
|
|
dprintf(DEBUG_CRITICAL, "waking FMI%d\n", h2fmi_bus_index(fmi));
|
|
}
|
|
|
|
void dump_fmi_state(h2fmi_t *pFMI, uint32_t index, BOOL32 withHWRegs, BOOL32 withECC)
|
|
{
|
|
/**
|
|
* Dump 'important' parts of state machine ...
|
|
*/
|
|
printf("FMI[%u] - stateMachine{mode:%d, state: %d, CEsav: %d, page_idx: %d, page_count: %d, currentCE: %d, currentPage: 0x%x, fSuccessful: %d, lastCE: %d, needsPrepare: %d, cleanPages: %d, uecc_pages: %d, cmdStartTick: 0x%llx, wVendorType: %d}\n",
|
|
index,
|
|
(int)pFMI->stateMachine.currentMode,
|
|
(int)pFMI->stateMachine.state.rd,
|
|
pFMI->stateMachine.savedCurrentCE,
|
|
pFMI->stateMachine.page_idx,
|
|
pFMI->stateMachine.page_count,
|
|
pFMI->stateMachine.currentCE,
|
|
pFMI->stateMachine.currentPage,
|
|
(int)pFMI->stateMachine.fSuccessful,
|
|
pFMI->stateMachine.lastCE,
|
|
pFMI->stateMachine.needs_prepare,
|
|
pFMI->stateMachine.clean_pages,
|
|
pFMI->stateMachine.uecc_pages,
|
|
pFMI->stateMachine.wCmdStartTicks,
|
|
pFMI->stateMachine.wVendorType);
|
|
|
|
/**
|
|
* Dump failure details.
|
|
*/
|
|
printf("FMI[%u] - failureDetails{wNumCEexec:%d, hldoff: %d, outstd: %d/%d, overall: 0x%x, firstFailingCE: 0x%x, wSingleCEStatus: 0x%x}\n",
|
|
index,
|
|
pFMI->failureDetails.wNumCE_Executed,
|
|
pFMI->stateMachine.wNumTimesHoldoffExecuted,
|
|
pFMI->stateMachine.wOutstandingCEWriteOperations,
|
|
pFMI->stateMachine.wMaxOutstandingCEWriteOperations,
|
|
pFMI->failureDetails.wOverallOperationFailure,
|
|
pFMI->failureDetails.wFirstFailingCE,
|
|
pFMI->failureDetails.wSingleCEStatus
|
|
);
|
|
|
|
if (withHWRegs)
|
|
{
|
|
/**
|
|
* Dump important FMI registers ...
|
|
*/
|
|
#if FMI_VERSION <=2
|
|
printf("FMI[%u] - fmi{bus: %d, CFG: 0x%x, CTRL: 0x%x, STAT: 0x%x, INTPND: 0x%x, INT_EN: 0x%x, DBG0: 0x%x, DBG1: 0x%x, DBG2: 0x%x}\n",
|
|
index,
|
|
pFMI->bus_id,
|
|
h2fmi_rd(pFMI,FMI_CONFIG),
|
|
h2fmi_rd(pFMI,FMI_CONTROL),
|
|
h2fmi_rd(pFMI,FMI_STATUS),
|
|
h2fmi_rd(pFMI,FMI_INT_PEND),
|
|
h2fmi_rd(pFMI,FMI_INT_EN),
|
|
h2fmi_rd(pFMI,FMI_DEBUG0),
|
|
h2fmi_rd(pFMI,FMI_DEBUG1),
|
|
h2fmi_rd(pFMI,FMI_DEBUG2)
|
|
);
|
|
#else
|
|
printf("FMI[%u] - fmi{bus: %d, CFG: 0x%x, CTRL: 0x%x, STAT: 0x%x, INTPND: 0x%x, INT_EN: 0x%x, DBG0: 0x%x, DATSIZ: 0x%x}\n",
|
|
index,
|
|
pFMI->bus_id,
|
|
h2fmi_rd(pFMI,FMI_CONFIG),
|
|
h2fmi_rd(pFMI,FMI_CONTROL),
|
|
h2fmi_rd(pFMI,FMI_STATUS),
|
|
h2fmi_rd(pFMI,FMI_INT_PEND),
|
|
h2fmi_rd(pFMI,FMI_INT_EN),
|
|
h2fmi_rd(pFMI,FMI_DEBUG0),
|
|
h2fmi_rd(pFMI,FMI_DATA_SIZE)
|
|
);
|
|
#endif
|
|
|
|
#if ((FMI_VERSION > 0) && (FMI_VERSION <= 2))
|
|
printf("FMI[%u] - fmi{DBG3: 0x%x, DATSIZ: 0x%x}\n",index,h2fmi_rd(pFMI,FMI_DEBUG3),h2fmi_rd(pFMI,FMI_DATA_SIZE));
|
|
#endif
|
|
/**
|
|
* Dump important FMC registers ...
|
|
*/
|
|
printf("FMI[%u] - fmc{ON: 0x%x, IFCTRL: 0x%x, CECTRL: 0x%x, RWCTRL: 0x%x, CMD: 0x%x, ADDR0: 0x%x, ADDR1: 0x%x, ANUM: 0x%x, DNUM: 0x%x, FSTATUS: 0x%x, NSTATUS: 0x%x, RBB_CFG: 0x%x}\n",
|
|
index,
|
|
h2fmi_rd(pFMI,FMC_ON),
|
|
h2fmi_rd(pFMI,FMC_IF_CTRL),
|
|
h2fmi_rd(pFMI,FMC_CE_CTRL),
|
|
h2fmi_rd(pFMI,FMC_RW_CTRL),
|
|
h2fmi_rd(pFMI,FMC_CMD),
|
|
h2fmi_rd(pFMI,FMC_ADDR0),
|
|
h2fmi_rd(pFMI,FMC_ADDR1),
|
|
h2fmi_rd(pFMI,FMC_ADDRNUM),
|
|
h2fmi_rd(pFMI,FMC_DATANUM),
|
|
h2fmi_rd(pFMI,FMC_STATUS),
|
|
h2fmi_rd(pFMI,FMC_NAND_STATUS),
|
|
h2fmi_rd(pFMI,FMC_RBB_CONFIG)
|
|
);
|
|
|
|
#if FMI_VERSION > 2
|
|
printf("FMI[%u] - fmc{DQST: 0x%x, NANDT1: 0x%x, NANDT2: 0x%x, NANDT3: 0x%x}\n",
|
|
index,
|
|
h2fmi_rd(pFMI,FMC_DQS_TIMING_CTRL),
|
|
h2fmi_rd(pFMI,FMC_TOGGLE_CTRL_1),
|
|
h2fmi_rd(pFMI,FMC_TOGGLE_CTRL_2),
|
|
h2fmi_rd(pFMI,FMC_TOGGLE_CTRL_3)
|
|
);
|
|
#endif // FMI_VERSION > 2
|
|
|
|
// ECC might be clocked off for PPN
|
|
if (withECC)
|
|
{
|
|
/**
|
|
* Dump important ECC registers ...
|
|
*/
|
|
printf("FMI[%u] - ecc{PND: 0x%x, MASK: 0x%x}\n",
|
|
index,
|
|
h2fmi_rd(pFMI,ECC_PND),
|
|
h2fmi_rd(pFMI,ECC_MASK)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
printf("FMI[%u} - omitting ecc registers for ppn device\n", index);
|
|
}
|
|
|
|
#if FMISS_DUMP_ENABLED
|
|
// FMISS
|
|
if (0 != (FMI_CONTROL__SEQUENCER_TIMEOUT_ENABLE & h2fmi_rd(pFMI, FMI_CONTROL)))
|
|
{
|
|
fmiss_dump(pFMI);
|
|
}
|
|
#endif // FMISS_DUMP_ENABLED
|
|
}
|
|
}
|