espressif_tinyusb/boards/embedded_artists/ea4357/nand.c

547 lines
15 KiB
C
Raw Blame History

/*****************************************************************************
*
* Copyright(C) 2011, Embedded Artists AB
* All rights reserved.
*
******************************************************************************
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* products. This software is supplied "AS IS" without any warranties.
* Embedded Artists AB assumes no responsibility or liability for the
* use of the software, conveys no license or title under any patent,
* copyright, or mask work right to the product. Embedded Artists AB
* reserves the right to make changes in the software without
* notification. Embedded Artists AB also make no representation or
* warranty that such application will be suitable for the specified
* use without further testing or modification.
*****************************************************************************/
/******************************************************************************
* Includes
*****************************************************************************/
#include "../../board.h"
#if BOARD == BOARD_EA4357
#include "lpc_types.h"
#include "lpc43xx_scu.h"
#include "lpc43xx_timer.h"
#include "nand.h"
/******************************************************************************
* Defines and typedefs
*****************************************************************************/
#define K9F1G_CLE ((volatile uint8_t *)0x1D100000)
#define K9F1G_ALE ((volatile uint8_t *)0x1D080000)
#define K9F1G_DATA ((volatile uint8_t *)0x1D000000)
#define K9FXX_WAIT()
#define K9FXX_READ_1 0x00
#define K9FXX_READ_2 0x30
#define K9FXX_SET_ADDR_A 0x00
#define K9FXX_SET_ADDR_B 0x01
#define K9FXX_SET_ADDR_C 0x50
#define K9FXX_READ_ID 0x90
#define K9FXX_RESET 0xff
#define K9FXX_BLOCK_PROGRAM_1 0x80
#define K9FXX_BLOCK_PROGRAM_2 0x10
#define K9FXX_BLOCK_ERASE_1 0x60
#define K9FXX_BLOCK_ERASE_2 0xd0
#define K9FXX_READ_STATUS 0x70
#define K9FXX_BUSY (1 << 6)
#define K9FXX_OK (1 << 0)
#define ID_MARKER_CODE (0xEC)
#define ID_SAMSUNG (0xF1)
#define ID_PAGE_SZ_1KB (0x00)
#define ID_PAGE_SZ_2KB (0x01)
#define ID_PAGE_SZ_4KB (0x02)
#define ID_PAGE_SZ_8KB (0x03)
#define ID_BLOCK_SZ_64KB (0x00)
#define ID_BLOCK_SZ_128KB (0x01)
#define ID_BLOCK_SZ_256KB (0x02)
#define ID_BLOCK_SZ_512KB (0x03)
#define ID_PAGE_SZ_1KB (0x00)
#define ID_PAGE_SZ_2KB (0x01)
#define ID_PAGE_SZ_4KB (0x02)
#define ID_PAGE_SZ_8KB (0x03)
#define ID_REDUND_SZ_8 (0x00)
#define ID_REDUND_SZ_16 (0x01)
/* This macro could be changed to check the ready pin */
#define WAIT_READY() (TIM_Waitus(25))
/******************************************************************************
* External global variables
*****************************************************************************/
/******************************************************************************
* Local variables
*****************************************************************************/
static uint32_t pageSize = 0;
static uint32_t blockSize = 0;
static uint32_t reduntSize = 0;
/******************************************************************************
* Local Functions
*****************************************************************************/
static void pinConfig(void)
{
/* Set up EMC pin */
scu_pinmux( 2 , 9 , MD_PLN_FAST , 3 );//A0
scu_pinmux( 2 , 10 , MD_PLN_FAST , 3 );//A1
scu_pinmux( 2 , 11 , MD_PLN_FAST , 3 );//A2
scu_pinmux( 2 , 12 , MD_PLN_FAST , 3 );//A3
scu_pinmux( 2 , 13 , MD_PLN_FAST , 3 );//A4
scu_pinmux( 1 , 0 , MD_PLN_FAST , 2 );//A5
scu_pinmux( 1 , 1 , MD_PLN_FAST , 2 );//A6
scu_pinmux( 1 , 2 , MD_PLN_FAST , 2 );//A7
scu_pinmux( 2 , 8 , MD_PLN_FAST , 3 );//A8
scu_pinmux( 2 , 7 , MD_PLN_FAST , 3 );//A9
scu_pinmux( 2 , 6 , MD_PLN_FAST , 2 );//A10
scu_pinmux( 2 , 2 , MD_PLN_FAST , 2 );//A11
scu_pinmux( 2 , 1 , MD_PLN_FAST , 2 );//A12
scu_pinmux( 2 , 0 , MD_PLN_FAST , 2 );//A13
scu_pinmux( 6 , 8 , MD_PLN_FAST , 1 );//A14
scu_pinmux( 6 , 7 , MD_PLN_FAST , 1 );//A15
scu_pinmux( 13 , 16 , MD_PLN_FAST , 2 );//A16
scu_pinmux( 13 , 15 , MD_PLN_FAST , 2 );//A17
scu_pinmux( 14 , 0 , MD_PLN_FAST , 3 );//A18
scu_pinmux( 14 , 1 , MD_PLN_FAST , 3 );//A19
scu_pinmux( 14 , 2 , MD_PLN_FAST , 3 );//A20
scu_pinmux( 14 , 3 , MD_PLN_FAST , 3 );//A21
scu_pinmux( 14 , 4 , MD_PLN_FAST , 3 );//A22
scu_pinmux( 10 , 4 , MD_PLN_FAST , 3 );//A23
scu_pinmux( 1 , 7 , MD_PLN_FAST , 3 );//D0
scu_pinmux( 1 , 8 , MD_PLN_FAST , 3 );//D1
scu_pinmux( 1 , 9 , MD_PLN_FAST , 3 );//D2
scu_pinmux( 1 , 10 , MD_PLN_FAST , 3 );//D3
scu_pinmux( 1 , 11 , MD_PLN_FAST , 3 );//D4
scu_pinmux( 1 , 12 , MD_PLN_FAST , 3 );//D5
scu_pinmux( 1 , 13 , MD_PLN_FAST , 3 );//D6
scu_pinmux( 1 , 14 , MD_PLN_FAST , 3 );//D7
scu_pinmux( 5 , 4 , MD_PLN_FAST , 2 );//D8
scu_pinmux( 5 , 5 , MD_PLN_FAST , 2 );//D9
scu_pinmux( 5 , 6 , MD_PLN_FAST , 2 );//D10
scu_pinmux( 5 , 7 , MD_PLN_FAST , 2 );//D11
scu_pinmux( 5 , 0 , MD_PLN_FAST , 2 );//D12
scu_pinmux( 5 , 1 , MD_PLN_FAST , 2 );//D13
scu_pinmux( 5 , 2 , MD_PLN_FAST , 2 );//D14
scu_pinmux( 5 , 3 , MD_PLN_FAST , 2 );//D15
scu_pinmux( 13 , 2 , MD_PLN_FAST , 2 );//D16
scu_pinmux( 13 , 3 , MD_PLN_FAST , 2 );//D17
scu_pinmux( 13 , 4 , MD_PLN_FAST , 2 );//D18
scu_pinmux( 13 , 5 , MD_PLN_FAST , 2 );//D19
scu_pinmux( 13 , 6 , MD_PLN_FAST , 2 );//D20
scu_pinmux( 13 , 7 , MD_PLN_FAST , 2 );//D21
scu_pinmux( 13 , 8 , MD_PLN_FAST , 2 );//D22
scu_pinmux( 13 , 9 , MD_PLN_FAST , 2 );//D23
scu_pinmux( 14 , 5 , MD_PLN_FAST , 3 );//D24
scu_pinmux( 14 , 6 , MD_PLN_FAST , 3 );//D25
scu_pinmux( 14 , 7 , MD_PLN_FAST , 3 );//D26
scu_pinmux( 14 , 8 , MD_PLN_FAST , 3 );//D27
scu_pinmux( 14 , 9 , MD_PLN_FAST , 3 );//D28
scu_pinmux( 14 , 10 , MD_PLN_FAST , 3 );//D29
scu_pinmux( 14 , 11 , MD_PLN_FAST , 3 );//D30
scu_pinmux( 14 , 12 , MD_PLN_FAST , 3 );//D31
scu_pinmux( 1 , 3 , MD_PLN_FAST , 3 );//OE
scu_pinmux( 1 , 6 , MD_PLN_FAST , 3 );//WE
scu_pinmux( 1 , 4 , MD_PLN_FAST , 3 );//BLS0
scu_pinmux( 6 , 6 , MD_PLN_FAST , 1 );//BLS1
scu_pinmux( 13 , 13 , MD_PLN_FAST , 2 );//BLS2
scu_pinmux( 13 , 10 , MD_PLN_FAST , 2 );//BLS3
scu_pinmux( 1 , 5 , MD_PLN_FAST , 3 );//CS0
scu_pinmux( 6 , 3 , MD_PLN_FAST , 3 );//CS1
scu_pinmux( 13 , 12 , MD_PLN_FAST , 2 );//CS2
scu_pinmux( 13 , 11 , MD_PLN_FAST , 2 );//CS3
}
static uint32_t nandReadId(void)
{
uint8_t a, b, c, d;
volatile uint8_t *pCLE;
volatile uint8_t *pALE;
volatile uint8_t *pData;
pCLE = K9F1G_CLE;
pALE = K9F1G_ALE;
pData = K9F1G_DATA;
*pCLE = K9FXX_READ_ID;
*pALE = 0;
a = *pData;
b = *pData;
c = *pData;
d = *pData;
return (a << 24) | (b << 16) | (c << 8) | d;
}
static uint8_t nandStatus(void)
{
uint8_t status = 0;
volatile uint8_t *pCLE;
volatile uint8_t *pALE;
volatile uint8_t *pData;
pCLE = K9F1G_CLE;
pALE = K9F1G_ALE;
pData = K9F1G_DATA;
*pCLE = K9FXX_READ_STATUS;
*pALE = 0;
status = *pData;
/* remove bits not used */
return (status & 0xC1);
}
static void nandWaitReady(void)
{
while( !(nandStatus() & (1<<6)) );
}
/******************************************************************************
* Public Functions
*****************************************************************************/
/******************************************************************************
*
* Description:
* Initialize the NAND Flash
*
* Returns:
* TRUE if initialization successful; otherwise FALSE
*
*****************************************************************************/
uint32_t nand_init (void)
{
uint32_t nandId = 0;
TIM_TIMERCFG_Type timerCfg;
// LPC_SC->PCONP |= 0x00000800;
LPC_EMC->CONTROL = 0x00000001;
LPC_EMC->CONFIG = 0x00000000;
pinConfig();
TIM_ConfigStructInit(TIM_TIMER_MODE, &timerCfg);
TIM_Init(LPC_TIMER0, TIM_TIMER_MODE, &timerCfg);
LPC_EMC->STATICCONFIG1 = 0x00000080;
LPC_EMC->STATICWAITWEN1 = 0x00000002;
LPC_EMC->STATICWAITOEN1 = 0x00000002;
LPC_EMC->STATICWAITRD1 = 0x00000008;
LPC_EMC->STATICWAITPAG1 = 0x0000001f;
LPC_EMC->STATICWAITWR1 = 0x00000008;
LPC_EMC->STATICWAITTURN1 = 0x0000000f;
nandId = nandReadId();
if ((nandId & 0xffff0000) !=
(((uint32_t)(ID_MARKER_CODE) << 24) | ID_SAMSUNG << 16)) {
/* unknown NAND chip */
return FALSE;
}
pageSize = 1024 * (1 << (nandId & 0x03));
blockSize = 64*1024 * (1 << ((nandId>>4) & 0x03));
reduntSize = 8 * (1 << ((nandId >> 1) & 0x1));
return TRUE;
}
/******************************************************************************
*
* Description:
* Get the page size of the NAND flash
*
* Returns:
* page size in bytes
*
*****************************************************************************/
uint32_t nand_getPageSize(void)
{
return pageSize;
}
/******************************************************************************
*
* Description:
* Get the block size of the NAND flash
*
* Returns:
* block size in bytes
*
*****************************************************************************/
uint32_t nand_getBlockSize(void)
{
return blockSize;
}
/******************************************************************************
*
* Description:
* Get the redundant (spare) size per page
*
* Returns:
* redundant/spare size in bytes
*
*****************************************************************************/
uint32_t nand_getRedundantSize(void)
{
return reduntSize * (pageSize/512);
}
/******************************************************************************
*
* Description:
* Check if a block is valid
*
* Returns:
* TRUE if the block is valid; otherwise FALSE
*
*****************************************************************************/
uint32_t nand_isBlockValid(uint32_t block)
{
uint32_t addr = 0;
uint32_t page = 0;
volatile uint8_t *pCLE;
volatile uint8_t *pALE;
volatile uint8_t *pData;
pCLE = K9F1G_CLE;
pALE = K9F1G_ALE;
pData = K9F1G_DATA;
if (block >= NAND_NUM_BLOCKS) {
return FALSE;
}
addr = block * (blockSize/pageSize);
/*
* Check page 0 and page 1 in each block. If the first byte
* in the spare area (of either page 0 or page 1) is != 0xFF
* the block is invalid.
*/
nandWaitReady();
for (page = 0; page < 2; page++) {
addr += page;
*pCLE = K9FXX_READ_1;
*pALE = (uint8_t) (pageSize & 0x00FF);
*pALE = (uint8_t)((pageSize & 0xFF00) >> 8);
*pALE = (uint8_t)((addr & 0x00FF));
*pALE = (uint8_t)((addr & 0xFF00) >> 8);
*pCLE = K9FXX_READ_2;
WAIT_READY();
if (*pData != 0xFF) {
return FALSE;
}
}
return TRUE;
}
/******************************************************************************
*
* Description:
* Read a page from the NAND memory
*
* Params:
* block - block number to read from
* page - page with<74>n block to read from
* pageBuf - data is copied to this buffer. The size must be at least
* pageSize.
*
* Returns:
* TRUE if read successful; otherwise FALSE
*
*****************************************************************************/
uint32_t nand_readPage(uint32_t block, uint32_t page, uint8_t* pageBuf)
{
uint32_t i = 0;
uint32_t addr = 0;
volatile uint8_t *pCLE;
volatile uint8_t *pALE;
volatile uint8_t *pData;
pCLE = K9F1G_CLE;
pALE = K9F1G_ALE;
pData = K9F1G_DATA;
if (block >= NAND_NUM_BLOCKS) {
return FALSE;
}
if (page >= blockSize/pageSize) {
return FALSE;
}
addr = block * (blockSize/pageSize) + page;
/*
* Always reading from start of a page address.
* This means that the column address is always 0.
*/
*pCLE = K9FXX_READ_1;
*pALE = 0;
*pALE = 0;
*pALE = (uint8_t)((addr & 0x00FF));
*pALE = (uint8_t)((addr & 0xFF00) >> 8);
*pCLE = K9FXX_READ_2;
WAIT_READY();
for (i = 0; i < pageSize; i++) {
*pageBuf++ = *pData;
}
return TRUE;
}
/******************************************************************************
*
* Description:
* Write a page of data to the NAND memory
*
* Params:
* block - block number to write to
* page - page within block to write to
* pageBuf - data is copied from this buffer. The size must be at least
* pageSize.
*
* Returns:
* TRUE if write successful; otherwise FALSE
*
*****************************************************************************/
uint32_t nand_writePage(uint32_t block, uint32_t page, uint8_t* pageBuf)
{
uint32_t i = 0;
uint32_t addr = 0;
volatile uint8_t *pCLE;
volatile uint8_t *pALE;
volatile uint8_t *pData;
pCLE = K9F1G_CLE;
pALE = K9F1G_ALE;
pData = K9F1G_DATA;
if (block >= NAND_NUM_BLOCKS) {
return FALSE;
}
if (page >= blockSize/pageSize) {
return FALSE;
}
addr = block * (blockSize/pageSize) + page;
/*
* Always writing to start of a page address.
* This means that the column address is always 0.
*/
*pCLE = K9FXX_BLOCK_PROGRAM_1;
*pALE = 0;
*pALE = 0;
*pALE = (uint8_t)((addr & 0x00FF));
*pALE = (uint8_t)((addr & 0xFF00) >> 8);
for (i = 0; i < pageSize; i++) {
*pData = *pageBuf++;
}
*pCLE = K9FXX_BLOCK_PROGRAM_2;
TIM_Waitus(700);
nandWaitReady();
return ((nandStatus() & 0x01) != 0x01);
}
/******************************************************************************
*
* Description:
* Erase a block
*
* Params:
* block - block number to erase
*
* Returns:
* TRUE if eras successful; otherwise FALSE
*
*****************************************************************************/
uint32_t nand_eraseBlock(uint32_t block)
{
uint32_t addr = 0;
volatile uint8_t *pCLE;
volatile uint8_t *pALE;
pCLE = K9F1G_CLE;
pALE = K9F1G_ALE;
if (block >= NAND_NUM_BLOCKS) {
return FALSE;
}
addr = block * (blockSize/pageSize);
*pCLE = K9FXX_BLOCK_ERASE_1;
*pALE = (uint8_t)(addr & 0x00FF);
*pALE = (uint8_t)((addr & 0xFF00) >> 8);
*pCLE = K9FXX_BLOCK_ERASE_2;
TIM_Waitus(700);
nandWaitReady();
return ((nandStatus() & 0x01) != 0x01);
}
#endif