2012-04-25 07:20:50 +00:00
|
|
|
/*
|
|
|
|
* nand.c - NAND flash functions
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008 Hugo Villeneuve <hugo@hugovil.com>
|
|
|
|
*
|
|
|
|
* Based on TI DaVinci Flash and Boot Utilities, original copyright follows:
|
|
|
|
* Copyright 2008 Texas Instruments, Inc. <www.ti.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "davinci.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "uart.h"
|
|
|
|
#include "nand.h"
|
|
|
|
|
|
|
|
/* BUS width defines */
|
|
|
|
#define BUS_8BIT 0x01
|
|
|
|
#define BUS_16BIT 0x02
|
|
|
|
#define BUS_32BIT 0x04
|
|
|
|
|
|
|
|
/* NAND flash addresses */
|
|
|
|
#define NAND_DATA_OFFSET 0x00
|
|
|
|
#define NAND_ALE_OFFSET 0x08
|
|
|
|
#define NAND_CLE_OFFSET 0x10
|
|
|
|
|
2014-07-10 07:24:48 +00:00
|
|
|
#define NAND_TIMEOUT 50000 /*us*/
|
2012-04-25 07:20:50 +00:00
|
|
|
|
|
|
|
/* NAND flash commands */
|
|
|
|
#define NAND_LO_PAGE 0x00
|
|
|
|
#define NAND_HI_PAGE 0x01
|
|
|
|
#define NAND_LOCK 0x2A
|
|
|
|
#define NAND_UNLOCK_START 0x23
|
|
|
|
#define NAND_UNLOCK_END 0x24
|
|
|
|
#define NAND_READ_30H 0x30
|
|
|
|
#define NAND_EXTRA_PAGE 0x50
|
|
|
|
#define NAND_RDID 0x90
|
|
|
|
#define NAND_RDIDADD 0x00
|
|
|
|
#define NAND_RESET 0xFF
|
|
|
|
#define NAND_PGRM_START 0x80
|
|
|
|
#define NAND_PGRM_END 0x10
|
|
|
|
#define NAND_RDY 0x40
|
|
|
|
#define NAND_PGM_FAIL 0x01
|
|
|
|
#define NAND_BERASEC1 0x60
|
|
|
|
#define NAND_BERASEC2 0xD0
|
|
|
|
#define NAND_STATUS 0x70
|
|
|
|
|
|
|
|
/* Status output */
|
|
|
|
#define NAND_NANDFSR_READY 0x01
|
|
|
|
#define NAND_STATUS_WRITEREADY 0xC0
|
|
|
|
#define NAND_STATUS_ERROR 0x01
|
|
|
|
#define NAND_STATUS_BUSY 0x40
|
|
|
|
|
|
|
|
#define UNKNOWN_NAND 0xFF /* Unknown device id */
|
|
|
|
|
|
|
|
/* Gives the page size in bytes without the spare bytes */
|
|
|
|
#define NANDFLASH_PAGESIZE(x) ((x >> 8) << 8)
|
|
|
|
|
|
|
|
union flash_data {
|
|
|
|
uint8_t c;
|
|
|
|
uint16_t w;
|
|
|
|
uint32_t l;
|
|
|
|
};
|
|
|
|
|
|
|
|
union flash_ptr {
|
|
|
|
volatile uint8_t *cp;
|
|
|
|
volatile uint16_t *wp;
|
|
|
|
volatile uint32_t *lp;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct nand_dev_infos_t {
|
|
|
|
uint8_t id; /* Device ID */
|
|
|
|
uint16_t num_blocks; /* Number of blocks */
|
|
|
|
uint8_t pages_per_block; /* Number of pages per block */
|
|
|
|
uint16_t bytes_per_page; /* Number of bytes per page (with spare) */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct nand_info_t {
|
|
|
|
uint32_t base_addr; /* Base address of NAND CS memory space. */
|
|
|
|
int bus_width; /* Bus width: 0 = 8 bits, 1 = 16 bits */
|
|
|
|
int id; /* Index into nand_dev_infos_t array. */
|
|
|
|
int num_blocks; /* Number of blocks */
|
|
|
|
int pages_per_block; /* Number of pages per block */
|
|
|
|
int bytes_per_page; /* Number of bytes per page (with spare) */
|
|
|
|
int num_cab; /* Number of Column address cycles */
|
|
|
|
int num_rab; /* Number of Row address cycles */
|
|
|
|
uint32_t ecc_mask; /* Mask for ECC register */
|
|
|
|
int large_page; /* True if page size >= 2048 bytes */
|
|
|
|
int ecc_index; /* ECC position is different for small and
|
|
|
|
* large page devices. */
|
|
|
|
int chunk_size; /* Always read/write in 512 bytes chunk max.
|
|
|
|
* This will be set based on page size. */
|
|
|
|
int spare_bytes; /* Number of spare area bytes per page. */
|
|
|
|
int blk_addr_shift; /* Number of bits by which to shift block address */
|
|
|
|
int page_addr_shift; /* Number of bits by which to shift page address */
|
|
|
|
int cs_offset; /*
|
|
|
|
* Chip-select offset:
|
|
|
|
* 0 = CS2 space
|
|
|
|
* 1 = CS3 space
|
|
|
|
* 2 = CS4 space
|
|
|
|
* 3 = CS5 space
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Buffer for storing data read from NAND flash */
|
|
|
|
static uint8_t read_buf[MAX_PAGE_SIZE] __attribute__((section(".ddrram")));
|
|
|
|
|
|
|
|
/* Symbol from linker script */
|
|
|
|
extern uint32_t __NANDFlash;
|
|
|
|
|
|
|
|
/* structure for holding details about the NAND device itself */
|
|
|
|
static volatile struct nand_info_t nand_info;
|
|
|
|
|
|
|
|
/* Table of ROM supported NAND devices */
|
|
|
|
static const struct nand_dev_infos_t nand_dev_infos[] = {
|
|
|
|
/* id, num_blocks, pages_per_block, bytes_per_page */
|
|
|
|
{0x6E, 256, 16, 256+8}, /* 1 MB */
|
|
|
|
{0x68, 256, 16, 256+8}, /* 1 MB */
|
|
|
|
{0xEC, 256, 16, 256+8}, /* 1 MB */
|
|
|
|
{0xE8, 256, 16, 256+8}, /* 1 MB */
|
|
|
|
{0xEA, 512, 16, 256+8}, /* 2 MB */
|
|
|
|
{0xE3, 512, 16, 512+16}, /* 4 MB */
|
|
|
|
{0xE5, 512, 16, 512+16}, /* 4 MB */
|
|
|
|
{0xE6, 1024, 16, 512+16}, /* 8 MB */
|
|
|
|
|
|
|
|
{0x39, 1024, 16, 512+16}, /* 8 MB */
|
|
|
|
{0x6B, 1024, 16, 512+16}, /* 8 MB */
|
|
|
|
{0x73, 1024, 32, 512+16}, /* 16 MB */
|
|
|
|
{0x33, 1024, 32, 512+16}, /* 16 MB */
|
|
|
|
{0x75, 2048, 32, 512+16}, /* 32 MB */
|
|
|
|
{0x35, 2048, 32, 512+16}, /* 32 MB */
|
|
|
|
|
|
|
|
{0x43, 1024, 32, 512+16}, /* 16 MB 0x1243 */
|
|
|
|
{0x45, 2048, 32, 512+16}, /* 32 MB 0x1245 */
|
|
|
|
{0x53, 1024, 32, 512+16}, /* 16 MB 0x1253 */
|
|
|
|
{0x55, 2048, 32, 512+16}, /* 32 MB 0x1255 */
|
|
|
|
{0x36, 4096, 32, 512+16}, /* 64 MB */
|
|
|
|
{0x46, 4096, 32, 512+16}, /* 64 MB 0x1346 */
|
|
|
|
{0x56, 4096, 32, 512+16}, /* 64 MB 0x1356 */
|
|
|
|
|
|
|
|
{0x76, 4096, 32, 512+16}, /* 64 MB */
|
|
|
|
|
|
|
|
{0x74, 8192, 32, 512+16}, /* 128 MB 0x1374 */
|
|
|
|
{0x79, 8192, 32, 512+16}, /* 128 MB */
|
|
|
|
{0x71, 16384, 32, 512+16}, /* 256 MB */
|
|
|
|
{0xF1, 1024, 64, 2048+64}, /* 128 MB - Big Block */
|
|
|
|
{0xA1, 1024, 64, 2048+64}, /* 128 MB - Big Block */
|
|
|
|
{0xAA, 2048, 64, 2048+64}, /* 256 MB - Big Block */
|
|
|
|
{0xDA, 2048, 64, 2048+64}, /* 256 MB - Big Block */
|
|
|
|
{0xDC, 4096, 64, 2048+64}, /* 512 MB - Big Block */
|
|
|
|
{0xAC, 4096, 64, 2048+64}, /* 512 MB - Big Block */
|
|
|
|
{0xB1, 1024, 64, 2048+64}, /* 128 MB - Big Block */
|
|
|
|
{0xC1, 1024, 64, 2048+64}, /* 128 MB - Big Block */
|
|
|
|
{0xD3, 4096, 64, 2048+64}, /* 512 MB - Big Block */
|
|
|
|
{0x00, 0, 0, 0} /* Indicate end of table */
|
|
|
|
};
|
|
|
|
|
|
|
|
static volatile uint8_t *
|
|
|
|
flash_make_addr(uint32_t baseAddr, uint32_t offset)
|
|
|
|
{
|
|
|
|
return (volatile uint8_t *) (baseAddr + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_write_data(uint32_t offset, uint32_t data)
|
|
|
|
{
|
|
|
|
volatile union flash_ptr addr;
|
|
|
|
union flash_data dataword;
|
|
|
|
dataword.l = data;
|
|
|
|
|
|
|
|
addr.cp = flash_make_addr(nand_info.base_addr, offset);
|
|
|
|
switch (nand_info.bus_width) {
|
|
|
|
case BUS_8BIT:
|
|
|
|
*addr.cp = dataword.c;
|
|
|
|
break;
|
|
|
|
case BUS_16BIT:
|
|
|
|
*addr.wp = dataword.w;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_write_cmd(uint32_t cmd)
|
|
|
|
{
|
|
|
|
flash_write_data(NAND_CLE_OFFSET, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_write_addr(uint32_t addr)
|
|
|
|
{
|
|
|
|
flash_write_data(NAND_ALE_OFFSET, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_write_bytes(const uint8_t *src, uint32_t numBytes)
|
|
|
|
{
|
|
|
|
volatile union flash_ptr destAddr, srcAddr;
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
srcAddr.cp = (volatile uint8_t *) src;
|
|
|
|
destAddr.cp = flash_make_addr(nand_info.base_addr, NAND_DATA_OFFSET);
|
|
|
|
switch (nand_info.bus_width) {
|
|
|
|
case BUS_8BIT:
|
|
|
|
for (i = 0; i < numBytes; i++)
|
|
|
|
*destAddr.cp = *srcAddr.cp++;
|
|
|
|
break;
|
|
|
|
case BUS_16BIT:
|
|
|
|
for (i = 0; i < (numBytes >> 1); i++)
|
|
|
|
*destAddr.wp = *srcAddr.wp++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_write_addr_bytes(uint32_t numAddrBytes, uint32_t addr)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < numAddrBytes; i++)
|
|
|
|
flash_write_addr((addr >> (8*i)) & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_write_row_addr_bytes(uint32_t block, uint32_t page)
|
|
|
|
{
|
|
|
|
uint32_t row_addr;
|
|
|
|
row_addr =
|
|
|
|
(block << (nand_info.blk_addr_shift - nand_info.page_addr_shift)) | page;
|
|
|
|
flash_write_addr_bytes(nand_info.num_rab, row_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_write_addr_cycles(uint32_t block, uint32_t page)
|
|
|
|
{
|
|
|
|
flash_write_addr_bytes(nand_info.num_cab, 0x00000000);
|
|
|
|
flash_write_row_addr_bytes(block, page);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
flash_read_data(void)
|
|
|
|
{
|
|
|
|
volatile union flash_ptr addr;
|
|
|
|
union flash_data cmdword;
|
|
|
|
|
|
|
|
cmdword.l = 0x0;
|
|
|
|
addr.cp = flash_make_addr(nand_info.base_addr, NAND_DATA_OFFSET);
|
|
|
|
switch (nand_info.bus_width) {
|
|
|
|
case BUS_8BIT:
|
|
|
|
cmdword.c = *addr.cp;
|
|
|
|
break;
|
|
|
|
case BUS_16BIT:
|
|
|
|
cmdword.w = *addr.wp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return cmdword.l;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flash_read_bytes(uint8_t *dest, uint32_t numBytes)
|
|
|
|
{
|
|
|
|
volatile union flash_ptr destAddr, srcAddr;
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
destAddr.cp = (volatile uint8_t *) dest;
|
|
|
|
srcAddr.cp = flash_make_addr(nand_info.base_addr, NAND_DATA_OFFSET);
|
|
|
|
switch (nand_info.bus_width) {
|
|
|
|
case BUS_8BIT:
|
|
|
|
for (i = 0; i < numBytes; i++)
|
|
|
|
*destAddr.cp++ = *srcAddr.cp;
|
|
|
|
break;
|
|
|
|
case BUS_16BIT:
|
|
|
|
for (i = 0; i < (numBytes >> 1); i++)
|
|
|
|
*destAddr.wp++ = *srcAddr.wp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Poll bit of NANDFSR to indicate ready */
|
|
|
|
static int
|
2014-07-09 17:38:07 +00:00
|
|
|
nand_wait_for_ready(uint32_t timeout_us)
|
2012-04-25 07:20:50 +00:00
|
|
|
{
|
|
|
|
uint32_t ready;
|
2014-07-09 17:38:07 +00:00
|
|
|
uint32_t timerStatus;
|
2012-04-25 07:20:50 +00:00
|
|
|
|
|
|
|
waitloop(200);
|
2014-07-09 17:38:07 +00:00
|
|
|
timer0_start(SYSTEM_CLK_MHZ * timeout_us);
|
2012-04-25 07:20:50 +00:00
|
|
|
do {
|
|
|
|
ready = AEMIF->NANDFSR & NAND_NANDFSR_READY;
|
2014-07-09 17:38:07 +00:00
|
|
|
timerStatus = timer0_status();
|
|
|
|
} while (!ready && timerStatus);
|
2012-04-25 07:20:50 +00:00
|
|
|
|
2014-07-09 17:38:07 +00:00
|
|
|
if (timerStatus == 0) {
|
2012-04-25 07:20:50 +00:00
|
|
|
log_info("NAND busy timeout");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for the status to be ready in NAND register
|
|
|
|
* There were some problems reported in DM320 with Ready/Busy pin
|
|
|
|
* not working with all NANDs. So this check has also been added.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
nand_wait_for_status(uint32_t timeout)
|
|
|
|
{
|
|
|
|
volatile uint32_t cnt;
|
|
|
|
uint32_t status;
|
|
|
|
cnt = timeout;
|
|
|
|
|
|
|
|
do {
|
|
|
|
flash_write_cmd(NAND_STATUS);
|
|
|
|
status = flash_read_data() &
|
|
|
|
(NAND_STATUS_ERROR | NAND_STATUS_BUSY);
|
|
|
|
cnt--;
|
|
|
|
} while ((cnt > 0) && !status);
|
|
|
|
|
|
|
|
if (cnt == 0) {
|
|
|
|
log_info("NAND status timeout");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the current ECC calculation and restart process */
|
|
|
|
static uint32_t
|
|
|
|
nand_read_ecc(void)
|
|
|
|
{
|
|
|
|
uint32_t retval;
|
|
|
|
|
|
|
|
/* Read and mask appropriate (based on CSn space flash is in)
|
|
|
|
* ECC register */
|
|
|
|
retval = ((uint32_t *)(&(AEMIF->NANDF1ECC)))[nand_info.cs_offset] &
|
|
|
|
nand_info.ecc_mask;
|
|
|
|
|
|
|
|
waitloop(5);
|
|
|
|
|
|
|
|
#ifdef NAND_DEBUG
|
|
|
|
uart_send_str("Value read from ECC register: ");
|
|
|
|
uart_send_hexnum(retval, 8);
|
|
|
|
uart_send_lf();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Write appropriate bit to start ECC calculations */
|
|
|
|
AEMIF->NANDFCR |= (1<<(8 + (nand_info.cs_offset)));
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get details of the NAND flash used from the id and the table of NAND
|
|
|
|
* devices. */
|
|
|
|
static int
|
2014-07-08 16:23:52 +00:00
|
|
|
nand_get_details(uint8_t *out_manID, uint8_t *out_deviceID)
|
2012-04-25 07:20:50 +00:00
|
|
|
{
|
2014-07-08 16:23:52 +00:00
|
|
|
uint32_t manID, deviceID, i, j;
|
2012-04-25 07:20:50 +00:00
|
|
|
|
|
|
|
/* Issue device read ID command. */
|
|
|
|
flash_write_cmd(NAND_RDID);
|
|
|
|
flash_write_addr(NAND_RDIDADD);
|
|
|
|
|
|
|
|
/* Read ID bytes */
|
2014-07-08 16:23:52 +00:00
|
|
|
manID = flash_read_data() & 0xFF;
|
2012-04-25 07:20:50 +00:00
|
|
|
deviceID = flash_read_data() & 0xFF;
|
|
|
|
j = flash_read_data() & 0xFF;
|
|
|
|
j = flash_read_data() & 0xFF;
|
|
|
|
|
2014-07-08 16:23:52 +00:00
|
|
|
*out_manID = manID;
|
|
|
|
*out_deviceID = deviceID;
|
|
|
|
|
2012-04-25 07:20:50 +00:00
|
|
|
uart_send_str(" ID:");
|
|
|
|
uart_send_hexnum(deviceID, 2);
|
|
|
|
if (nand_info.bus_width == BUS_16BIT)
|
|
|
|
uart_send_str(", 16");
|
|
|
|
else
|
|
|
|
uart_send_str(", 8");
|
|
|
|
|
|
|
|
log_info("-bit bus");
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (nand_dev_infos[i].id != 0x00) {
|
|
|
|
if (deviceID == nand_dev_infos[i].id) {
|
|
|
|
nand_info.id = (uint8_t) nand_dev_infos[i].id;
|
|
|
|
nand_info.pages_per_block =
|
|
|
|
nand_dev_infos[i].pages_per_block;
|
|
|
|
nand_info.num_blocks = nand_dev_infos[i].num_blocks;
|
|
|
|
nand_info.bytes_per_page = NANDFLASH_PAGESIZE(
|
|
|
|
nand_dev_infos[i].bytes_per_page);
|
|
|
|
|
|
|
|
nand_info.spare_bytes = nand_dev_infos[i].bytes_per_page -
|
|
|
|
nand_info.bytes_per_page;
|
|
|
|
|
|
|
|
/* Configure small or large page device. */
|
|
|
|
if (nand_info.bytes_per_page >= 2048) {
|
|
|
|
/* Set the large page flag */
|
|
|
|
nand_info.large_page = true;
|
|
|
|
nand_info.ecc_index = 2;
|
|
|
|
nand_info.chunk_size = 512; /* Limit to 512 bytes */
|
|
|
|
} else {
|
|
|
|
/* Clear the large page flag */
|
|
|
|
nand_info.large_page = false;
|
|
|
|
nand_info.ecc_index = 0;
|
|
|
|
nand_info.chunk_size = nand_info.bytes_per_page;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup address shift values */
|
|
|
|
j = 0;
|
|
|
|
while ((nand_info.pages_per_block >> j) > 1)
|
|
|
|
j++;
|
|
|
|
|
|
|
|
nand_info.blk_addr_shift = j;
|
|
|
|
nand_info.page_addr_shift = (nand_info.large_page) ? 16 : 8;
|
|
|
|
|
|
|
|
nand_info.blk_addr_shift += nand_info.page_addr_shift;
|
|
|
|
|
|
|
|
/* Set number of column address bytes needed */
|
|
|
|
nand_info.num_cab = nand_info.page_addr_shift >> 3;
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
while ((nand_info.num_blocks >> j) > 1)
|
|
|
|
j++;
|
|
|
|
|
|
|
|
/* Set number of row address bytes needed */
|
|
|
|
if ((nand_info.blk_addr_shift + j) <= 24)
|
|
|
|
nand_info.num_rab = 3 -
|
|
|
|
nand_info.num_cab;
|
|
|
|
else if ((nand_info.blk_addr_shift + j) <= 32)
|
|
|
|
nand_info.num_rab = 4 -
|
|
|
|
nand_info.num_cab;
|
|
|
|
else
|
|
|
|
nand_info.num_rab = 5 -
|
|
|
|
nand_info.num_cab;
|
|
|
|
|
|
|
|
/* Set the ECC bit mask */
|
|
|
|
if (nand_info.bytes_per_page < 512)
|
|
|
|
nand_info.ecc_mask = 0x07FF07FF;
|
|
|
|
else
|
|
|
|
nand_info.ecc_mask = 0x0FFF0FFF;
|
|
|
|
|
|
|
|
/* Report informations */
|
|
|
|
uart_send_str(" Blocks: ");
|
|
|
|
uart_send_hexnum(nand_info.num_blocks, 5);
|
|
|
|
uart_send_str(", Pages/block: ");
|
|
|
|
uart_send_hexnum(nand_info.pages_per_block, 3);
|
|
|
|
uart_send_str(", Bytes per page: ");
|
|
|
|
uart_send_hexnum(nand_info.bytes_per_page, 4);
|
|
|
|
uart_send_lf();
|
|
|
|
|
|
|
|
/* Report additional debug informations */
|
|
|
|
#ifdef NAND_DEBUG
|
|
|
|
uart_send_str(" Page shift: ");
|
|
|
|
uart_send_hexnum(nand_info.page_addr_shift, 2);
|
|
|
|
uart_send_lf();
|
|
|
|
uart_send_str(" Block shift: ");
|
|
|
|
uart_send_hexnum(nand_info.blk_addr_shift, 2);
|
|
|
|
uart_send_lf();
|
|
|
|
uart_send_str(" Column address bytes: ");
|
|
|
|
uart_send_hexnum(nand_info.num_cab, 2);
|
|
|
|
uart_send_lf();
|
|
|
|
uart_send_str(" Row address bytes: ");
|
|
|
|
uart_send_hexnum(nand_info.num_rab, 2);
|
|
|
|
uart_send_lf();
|
|
|
|
uart_send_str(" ECC mask: ");
|
|
|
|
uart_send_hexnum(nand_info.ecc_mask, 8);
|
|
|
|
uart_send_lf();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
/* No match was found for the device ID */
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
2014-07-08 16:23:52 +00:00
|
|
|
static int
|
|
|
|
nand_set_a1cr(uint8_t manID, uint8_t deviceID)
|
|
|
|
{
|
|
|
|
if (deviceID != 0xA1) {
|
|
|
|
log_info( "Unsupported NAND device" );
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (manID) {
|
|
|
|
/* ST/Numonyx */
|
|
|
|
case 0x20:
|
|
|
|
AEMIF->A1CR = 0x1844431C;
|
|
|
|
break;
|
|
|
|
/* Micron */
|
|
|
|
case 0x2C:
|
|
|
|
AEMIF->A1CR = 0x102442EC;
|
|
|
|
break;
|
|
|
|
/* Spansion */
|
|
|
|
case 0x01:
|
|
|
|
AEMIF->A1CR = 0x1844437C;
|
|
|
|
break;
|
|
|
|
/* Toshiba */
|
|
|
|
case 0x98:
|
|
|
|
AEMIF->A1CR = 0x102442DC;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_info( "Unsupported NAND device" );
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
2012-04-25 07:20:50 +00:00
|
|
|
static void
|
|
|
|
nand_write_spare(uint32_t eccvalue)
|
|
|
|
{
|
|
|
|
uint32_t spare_data[4] = {
|
|
|
|
0xFFFFFFFF,
|
|
|
|
0xFFFFFFFF,
|
|
|
|
0xFFFFFFFF,
|
|
|
|
0xFFFFFFFF
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Place the ECC values where the RBL expects them */
|
|
|
|
spare_data[nand_info.ecc_index] = eccvalue;
|
|
|
|
|
|
|
|
/* Write spare bytes infos */
|
|
|
|
if (nand_info.bytes_per_page == 256)
|
|
|
|
flash_write_bytes((uint8_t *) spare_data, 8);
|
|
|
|
else
|
|
|
|
flash_write_bytes((uint8_t *) spare_data, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RBL-expected layout for large page NAND (ex: 2048 bytes/page):
|
|
|
|
*
|
|
|
|
* DM35x DM644x
|
|
|
|
* -----------------------------
|
|
|
|
* 512 DATA 2048 DATA
|
|
|
|
* 16 SPARE 64 SPARE
|
|
|
|
* 512 DATA
|
|
|
|
* 16 SPARE
|
|
|
|
* 512 DATA
|
|
|
|
* 16 SPARE
|
|
|
|
* 512 DATA
|
|
|
|
* 16 SPARE
|
|
|
|
*
|
|
|
|
* So for big block NAND devices (bytes per page > 512) on the DM35x, we must
|
|
|
|
* write 512 bytes and write the ECC immediately after that data, and repeat
|
|
|
|
* until all the page is written.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Generic routine to write a page of data to NAND */
|
|
|
|
static int
|
|
|
|
nand_write_page(uint32_t block, uint32_t page, const uint8_t *src)
|
|
|
|
{
|
|
|
|
uint32_t hw_ecc[4]; /* Maximum of 2048 bytes/page (4 * 512 = 2048) */
|
|
|
|
uint8_t numWrites, i;
|
|
|
|
|
|
|
|
numWrites = (nand_info.bytes_per_page >> 9); /* Divide by 512 */
|
|
|
|
if (numWrites == 0)
|
|
|
|
numWrites++;
|
|
|
|
|
|
|
|
/* Write program command */
|
|
|
|
flash_write_cmd(NAND_PGRM_START);
|
|
|
|
|
|
|
|
/* Write address bytes */
|
|
|
|
flash_write_addr_cycles(block, page);
|
|
|
|
|
|
|
|
/* Starting the ECC in the NANDFCR register for CS2 (bit no.8) */
|
|
|
|
nand_read_ecc();
|
|
|
|
|
|
|
|
/* Write data */
|
|
|
|
for (i = 0; i < numWrites; i++) {
|
|
|
|
/* Write data to page */
|
|
|
|
flash_write_bytes(src, nand_info.chunk_size);
|
|
|
|
|
|
|
|
/* Read the ECC value */
|
|
|
|
hw_ecc[i] = nand_read_ecc();
|
|
|
|
|
|
|
|
/* Format ECC */
|
|
|
|
endian_data(&(hw_ecc[i]));
|
|
|
|
|
|
|
|
#if defined(DM35x)
|
|
|
|
/* Write spare area */
|
|
|
|
nand_write_spare(hw_ecc[i]);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Increment the pointer */
|
|
|
|
src += nand_info.chunk_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(DM644x)
|
|
|
|
for (i = 0; i < numWrites; i++) {
|
|
|
|
nand_write_spare(hw_ecc[i]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Write program end command */
|
|
|
|
flash_write_cmd(NAND_PGRM_END);
|
|
|
|
|
|
|
|
/* Wait for the device to be ready */
|
|
|
|
if (nand_wait_for_ready(NAND_TIMEOUT) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
/* Return status check result */
|
|
|
|
return nand_wait_for_status(NAND_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
nand_read_spare(void)
|
|
|
|
{
|
|
|
|
uint32_t spare_ecc[4], spare_ecc_temp;
|
|
|
|
|
|
|
|
/* Read the stored ECC value(s) */
|
|
|
|
if (nand_info.bytes_per_page == 256)
|
|
|
|
flash_read_bytes((uint8_t *) spare_ecc, 8);
|
|
|
|
else
|
|
|
|
flash_read_bytes((uint8_t *) spare_ecc, 16);
|
|
|
|
|
|
|
|
spare_ecc_temp = spare_ecc[nand_info.ecc_index];
|
|
|
|
|
|
|
|
/* Format ECC */
|
|
|
|
endian_data(&spare_ecc_temp);
|
|
|
|
|
|
|
|
return spare_ecc_temp;
|
|
|
|
}
|
|
|
|
|
2014-04-29 14:35:49 +00:00
|
|
|
/*
|
|
|
|
* GPLv2 code taken from the kernel to correct the bit error.
|
|
|
|
* The content of the ECC register is in the sprue20c.pdf. For
|
|
|
|
* the odd/even there are four bits spare each. The kernel is
|
|
|
|
* shifting this around and it is the easiest just to do the
|
|
|
|
* same.
|
|
|
|
*/
|
|
|
|
static uint32_t squeeze_ecc_bits(uint32_t tmp)
|
|
|
|
{
|
|
|
|
/* Squeeze 4 bytes ECC into 3 bytes by removing RESERVED bits
|
|
|
|
* and shifting. RESERVED bits are 31 to 28 and 15 to 12. */
|
|
|
|
tmp = (tmp & 0x00000fff) | ((tmp & 0x0fff0000) >> 4);
|
|
|
|
|
|
|
|
/* Invert so that erased block ECC is correct */
|
|
|
|
return ~tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nand_check_fix(
|
|
|
|
const uint32_t _calc_ecc,
|
|
|
|
const uint32_t _ecc_nand, uint8_t *dat, int size)
|
|
|
|
{
|
|
|
|
uint32_t ecc_nand, ecc_calc;
|
|
|
|
|
|
|
|
ecc_calc = squeeze_ecc_bits(_calc_ecc);
|
|
|
|
ecc_nand = squeeze_ecc_bits(_ecc_nand);
|
|
|
|
|
|
|
|
uint32_t diff = ecc_calc ^ ecc_nand;
|
|
|
|
|
|
|
|
if (diff) {
|
|
|
|
if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
|
|
|
|
/* Correctable error */
|
|
|
|
if ((diff >> (12 + 3)) < size) {
|
|
|
|
uint8_t find_bit = 1 << ((diff >> 12) & 7);
|
|
|
|
uint32_t find_byte = diff >> (12 + 3);
|
|
|
|
|
|
|
|
dat[find_byte] ^= find_bit;
|
|
|
|
log_info("Correcting single bit error.");
|
|
|
|
uart_send_str("BYTE=");
|
|
|
|
uart_send_hexnum(find_byte, 8);
|
|
|
|
uart_send_str(" BIT=");
|
|
|
|
uart_send_hexnum(find_bit, 8);
|
|
|
|
uart_send_lf();
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
log_info("Too many bit errors:");
|
|
|
|
uart_send_hexnum(diff >> (12+3), 8);
|
|
|
|
uart_send_lf();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (!(diff & (diff - 1))) {
|
|
|
|
/* Single bit ECC error in the ECC itself,
|
|
|
|
nothing to fix */
|
|
|
|
log_info("Bit error in the spare data. Ignoring");
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
/* Uncorrectable error */
|
|
|
|
log_info("Uncorrectable error");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-25 07:20:50 +00:00
|
|
|
/* Read a page from NAND */
|
|
|
|
int
|
2014-04-29 14:35:49 +00:00
|
|
|
nand_read_page(uint32_t block, uint32_t page, uint8_t *orig_dest)
|
2012-04-25 07:20:50 +00:00
|
|
|
{
|
|
|
|
uint32_t hw_ecc[4];
|
|
|
|
uint32_t spare_ecc[4];
|
|
|
|
uint8_t numReads, i;
|
2014-04-29 14:35:49 +00:00
|
|
|
uint8_t *dest = orig_dest;
|
2012-04-25 07:20:50 +00:00
|
|
|
|
|
|
|
numReads = (nand_info.bytes_per_page >> 9); /* Divide by 512 */
|
|
|
|
if (numReads == 0)
|
|
|
|
numReads++;
|
|
|
|
|
|
|
|
/* Write read command */
|
|
|
|
flash_write_cmd(NAND_LO_PAGE);
|
|
|
|
|
|
|
|
/* Write address bytes */
|
|
|
|
flash_write_addr_cycles(block, page);
|
|
|
|
|
|
|
|
/* Additional confirm command for big_block devices */
|
|
|
|
if (nand_info.large_page)
|
|
|
|
flash_write_cmd(NAND_READ_30H);
|
|
|
|
|
|
|
|
/* Wait for data to be available */
|
|
|
|
if (nand_wait_for_ready(NAND_TIMEOUT) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
/* Starting the ECC in the NANDFCR register for CS2(bit no.8) */
|
|
|
|
nand_read_ecc();
|
|
|
|
|
|
|
|
/* Read the page data */
|
|
|
|
for (i = 0; i < numReads; i++) {
|
|
|
|
/* Read data bytes */
|
|
|
|
flash_read_bytes(dest, nand_info.chunk_size);
|
|
|
|
|
|
|
|
/* Read hardware computed ECC */
|
|
|
|
hw_ecc[i] = nand_read_ecc();
|
|
|
|
|
|
|
|
#if defined(DM35x)
|
|
|
|
/* Read spare area ECC */
|
|
|
|
spare_ecc[i] = nand_read_spare();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Increment the pointer */
|
|
|
|
dest += nand_info.chunk_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(DM644x)
|
|
|
|
for (i = 0; i < numReads; i++) {
|
|
|
|
spare_ecc[i] = nand_read_spare();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef NAND_BYPASS_READ_PAGE_ECC_CHECK
|
|
|
|
for (i = 0; i < numReads; i++) {
|
|
|
|
/* Verify ECC values */
|
2014-04-29 14:35:49 +00:00
|
|
|
if (nand_check_fix(hw_ecc[i], spare_ecc[i], &orig_dest[i * nand_info.chunk_size], nand_info.chunk_size) < 0) {
|
2012-04-25 07:20:50 +00:00
|
|
|
log_info("NAND ECC failure:");
|
|
|
|
uart_send_str("HW = ");
|
|
|
|
uart_send_hexnum(hw_ecc[i], 8);
|
|
|
|
uart_send_lf();
|
|
|
|
uart_send_str("SPARE =");
|
|
|
|
uart_send_hexnum(spare_ecc[i], 8);
|
|
|
|
uart_send_lf();
|
|
|
|
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* NAND_BYPASS_READ_PAGE_ECC_CHECK */
|
|
|
|
|
|
|
|
/* Return status check result */
|
|
|
|
return nand_wait_for_status(NAND_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Verify data written by reading and comparing byte for byte */
|
|
|
|
static int
|
|
|
|
nand_verify_page(int block, int page, const uint8_t *src)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (nand_read_page(block, page, read_buf) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
for (i = 0; i < nand_info.bytes_per_page; i++) {
|
|
|
|
/* Check for data read errors */
|
|
|
|
if (src[i] != read_buf[i]) {
|
|
|
|
int k = i;
|
|
|
|
|
|
|
|
uart_send_str("NAND verify page failed at block ");
|
|
|
|
uart_send_hexnum(block, 4);
|
|
|
|
uart_send_str(", page ");
|
|
|
|
uart_send_hexnum(page, 4);
|
|
|
|
uart_send_str(", offset ");
|
|
|
|
uart_send_hexnum(i, 4);
|
|
|
|
uart_send_lf();
|
|
|
|
|
|
|
|
for (k = i - 8; k < (i + 20); k += 4) {
|
|
|
|
uart_send_str("offset ");
|
|
|
|
uart_send_hexnum(k, 4);
|
|
|
|
uart_send_str(", ram=");
|
|
|
|
uart_send_hexnum(*((uint32_t *) &src[k]), 8);
|
|
|
|
uart_send_str(", nand=");
|
|
|
|
uart_send_hexnum(*((uint32_t *) &read_buf[k]), 8);
|
|
|
|
uart_send_lf();
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NAND Flash unprotect command */
|
|
|
|
static uint32_t
|
|
|
|
nand_unprotect_blocks(uint32_t startBlkNum, uint32_t blkCnt)
|
|
|
|
{
|
|
|
|
uint32_t endBlkNum;
|
|
|
|
endBlkNum = startBlkNum + blkCnt - 1;
|
|
|
|
|
|
|
|
uart_send_str("Unprotecting blocks ");
|
|
|
|
uart_send_hexnum(startBlkNum, 4);
|
|
|
|
uart_send_str(" to ");
|
|
|
|
uart_send_hexnum(endBlkNum, 4);
|
|
|
|
uart_send_lf();
|
|
|
|
|
|
|
|
/* Do bounds checking */
|
|
|
|
if (endBlkNum >= nand_info.num_blocks) {
|
|
|
|
log_fail("Invalid last block");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
flash_write_cmd(NAND_UNLOCK_START);
|
|
|
|
flash_write_row_addr_bytes(startBlkNum, 0);
|
|
|
|
|
|
|
|
flash_write_cmd(NAND_UNLOCK_END);
|
|
|
|
flash_write_row_addr_bytes(endBlkNum, 0);
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NAND Flash protect command */
|
|
|
|
static void
|
|
|
|
nand_protect_blocks(void)
|
|
|
|
{
|
|
|
|
log_info("Protecting the entire NAND flash");
|
|
|
|
flash_write_cmd(NAND_LOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NAND Flash erase block function */
|
|
|
|
static uint32_t
|
|
|
|
nand_erase_blocks(uint32_t startBlkNum, uint32_t blkCnt)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
/* Do bounds checking */
|
|
|
|
if ((startBlkNum + blkCnt - 1) >= nand_info.num_blocks)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
/* Output info about what we are doing */
|
|
|
|
uart_send_str("Erasing blocks ");
|
|
|
|
uart_send_hexnum(startBlkNum, 4);
|
|
|
|
uart_send_str(" to ");
|
|
|
|
uart_send_hexnum(startBlkNum + blkCnt - 1, 4);
|
|
|
|
uart_send_lf();
|
|
|
|
|
|
|
|
for (i = 0; i < blkCnt; i++) {
|
|
|
|
/* Start erase command */
|
|
|
|
flash_write_cmd(NAND_BERASEC1);
|
|
|
|
|
|
|
|
/* Write the row addr bytes only */
|
|
|
|
flash_write_row_addr_bytes(startBlkNum + i, 0);
|
|
|
|
|
|
|
|
/* Confirm erase command */
|
|
|
|
flash_write_cmd(NAND_BERASEC2);
|
|
|
|
|
|
|
|
/* Wait for the device to be ready */
|
|
|
|
if (nand_wait_for_ready(NAND_TIMEOUT) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
/* Verify the op succeeded by reading status from flash */
|
|
|
|
if (nand_wait_for_status(NAND_TIMEOUT) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize NAND interface and find the details of the NAND used */
|
|
|
|
int
|
|
|
|
nand_init(void)
|
|
|
|
{
|
|
|
|
uint32_t width;
|
|
|
|
uint32_t *CSRegs;
|
2014-07-08 16:23:52 +00:00
|
|
|
uint8_t manID, deviceID;
|
2012-04-25 07:20:50 +00:00
|
|
|
|
|
|
|
log_info("Initializing NAND flash:");
|
|
|
|
|
|
|
|
#ifdef NAND_BYPASS_READ_PAGE_ECC_CHECK
|
|
|
|
log_info(" Bypassing ECC checks");
|
|
|
|
#endif /* NAND_BYPASS_READ_PAGE_ECC_CHECK */
|
|
|
|
|
|
|
|
/* Set NAND flash base address */
|
|
|
|
nand_info.base_addr = (uint32_t) &__NANDFlash;
|
|
|
|
|
|
|
|
/* Get the cs_offset (can be 0 through 3 - corresponds with CS2 through
|
|
|
|
* CS5) */
|
|
|
|
nand_info.cs_offset = (nand_info.base_addr >> 25) - 1;
|
|
|
|
|
|
|
|
/* Setting the nand_width = 0(8 bit NAND) or 1(16 bit NAND). AEMIF CS2
|
|
|
|
* bus Width is given by the BOOTCFG(bit no.5). */
|
|
|
|
width = (((SYSTEM->BOOTCFG) & 0x20) >> 5);
|
|
|
|
nand_info.bus_width = (width)?BUS_16BIT:BUS_8BIT;
|
|
|
|
|
|
|
|
/* Setup AEMIF registers for NAND */
|
|
|
|
CSRegs = (uint32_t *) &(AEMIF->A1CR);
|
|
|
|
|
|
|
|
/* Set correct AxCR reg */
|
|
|
|
CSRegs[nand_info.cs_offset] = 0x3FFFFFFC | width;
|
|
|
|
|
|
|
|
/* NAND enable for CSx. */
|
|
|
|
AEMIF->NANDFCR |= (0x1 << (nand_info.cs_offset));
|
|
|
|
nand_read_ecc();
|
|
|
|
|
|
|
|
/* Send reset command to NAND */
|
|
|
|
flash_write_cmd(NAND_RESET);
|
|
|
|
|
|
|
|
if (nand_wait_for_ready(NAND_TIMEOUT) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
2014-07-08 16:23:52 +00:00
|
|
|
if (nand_get_details(&manID, &deviceID) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
return nand_set_a1cr(manID, deviceID);
|
2012-04-25 07:20:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nand_write_verify_page(int block, int page, const uint8_t *src)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = nand_write_page(block, page, src);
|
|
|
|
if (status != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
waitloop(200);
|
|
|
|
|
|
|
|
/* Verify the page just written */
|
|
|
|
return nand_verify_page(block, page, src);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nand_write_prog(struct nand_image_descriptor_t *im_desc, const uint8_t *src,
|
|
|
|
size_t size)
|
|
|
|
{
|
|
|
|
int num_blocks;
|
|
|
|
int max_block_num;
|
|
|
|
int page_num;
|
|
|
|
uint32_t count_mask;
|
|
|
|
|
|
|
|
im_desc->page_num = 1; /* Always start data in page 1 */
|
|
|
|
|
|
|
|
/* Do some rounding based on data buffer size */
|
|
|
|
im_desc->size_in_pages = 0;
|
|
|
|
while ((im_desc->size_in_pages * nand_info.bytes_per_page) < size)
|
|
|
|
im_desc->size_in_pages++;
|
|
|
|
|
|
|
|
/* Get total number of blocks needed */
|
|
|
|
num_blocks = 0;
|
|
|
|
while ((num_blocks * nand_info.pages_per_block) <
|
|
|
|
(im_desc->size_in_pages + 1))
|
|
|
|
num_blocks++;
|
|
|
|
|
|
|
|
uart_send_str("Needed blocks: ");
|
|
|
|
uart_send_hexnum(num_blocks, 4);
|
|
|
|
uart_send_lf();
|
|
|
|
uart_send_str("Needed pages: ");
|
|
|
|
uart_send_hexnum(im_desc->size_in_pages, 4);
|
|
|
|
uart_send_lf();
|
|
|
|
|
|
|
|
/* Check whether writing UBL or APP (based on destination block) */
|
2014-05-19 06:20:46 +00:00
|
|
|
if (im_desc->block_num <= END_UBL_BLOCK_NUM)
|
2012-04-25 07:20:50 +00:00
|
|
|
max_block_num = END_UBL_BLOCK_NUM;
|
|
|
|
else
|
2014-05-19 06:20:46 +00:00
|
|
|
max_block_num = im_desc->block_num + MAX_BLOCK_PER_UBOOT - 1;
|
2012-04-25 07:20:50 +00:00
|
|
|
|
|
|
|
NAND_WRITE_RETRY:
|
|
|
|
if (im_desc->block_num > max_block_num) {
|
|
|
|
log_fail("Block > last block");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
uart_send_str("Trying block ");
|
|
|
|
uart_send_hexnum(im_desc->block_num, 4);
|
|
|
|
uart_send_lf();
|
|
|
|
|
|
|
|
/* Unprotect all needed blocks of the Flash */
|
|
|
|
if (nand_unprotect_blocks(im_desc->block_num, num_blocks) != E_PASS) {
|
|
|
|
im_desc->block_num++;
|
|
|
|
log_info("Unprotect failed");
|
|
|
|
goto NAND_WRITE_RETRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Erase the block where the header goes and the data starts */
|
|
|
|
if (nand_erase_blocks(im_desc->block_num, num_blocks) != E_PASS) {
|
|
|
|
im_desc->block_num++;
|
|
|
|
log_info("Erase failed");
|
|
|
|
goto NAND_WRITE_RETRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NAND_DEBUG_WRITE_RAMP
|
|
|
|
{
|
|
|
|
int k;
|
|
|
|
|
|
|
|
/* Usefull for debugging NAND ECC and spare bytes errors. */
|
|
|
|
for (k = 0; k < 512; k++)
|
|
|
|
ptr[k] = 0xCAFE0000 | k;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
page_num = 0; /* Start in page 0. */
|
|
|
|
|
|
|
|
if (im_desc->magic != UBL_CMD_FLASH_DATA) {
|
|
|
|
/* Write the header to page 0. */
|
|
|
|
log_info("Writing header");
|
|
|
|
|
|
|
|
if (nand_write_verify_page(im_desc->block_num, page_num,
|
|
|
|
(uint8_t *) im_desc) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
/* Set starting page number for next data portion. */
|
|
|
|
page_num = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The following assumes power of 2 page_cnt - *should* always be
|
|
|
|
* valid. */
|
|
|
|
count_mask = nand_info.pages_per_block - 1;
|
|
|
|
log_info("Writing data");
|
|
|
|
do {
|
|
|
|
/* Write data on a per page basis */
|
|
|
|
if (nand_write_verify_page(im_desc->block_num,
|
|
|
|
page_num & count_mask, src)
|
|
|
|
!= E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
page_num++;
|
|
|
|
src += nand_info.bytes_per_page;
|
|
|
|
if (!(page_num & count_mask))
|
|
|
|
im_desc->block_num++;
|
|
|
|
} while (page_num <= im_desc->size_in_pages);
|
|
|
|
|
|
|
|
nand_protect_blocks();
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nand_erase_all(void)
|
|
|
|
{
|
|
|
|
/* Unprotect the NAND Flash */
|
|
|
|
nand_unprotect_blocks(0, nand_info.num_blocks - 1);
|
|
|
|
|
|
|
|
/* Erase all the pages */
|
|
|
|
if (nand_erase_blocks(0, nand_info.num_blocks - 1) != E_PASS)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
/* Protect the device */
|
|
|
|
nand_protect_blocks();
|
|
|
|
|
|
|
|
return E_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nand_get_pages_per_block(void)
|
|
|
|
{
|
|
|
|
return nand_info.pages_per_block;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nand_get_bytes_per_page(void)
|
|
|
|
{
|
|
|
|
return nand_info.bytes_per_page;
|
|
|
|
}
|
2012-04-25 07:19:54 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
nand_get_bytes_per_block(void)
|
|
|
|
{
|
|
|
|
return nand_info.pages_per_block * nand_info.bytes_per_page;
|
|
|
|
}
|