mirror of
https://github.com/hathach/tinyusb.git
synced 2025-01-17 05:32:55 +08:00
547 lines
15 KiB
C
547 lines
15 KiB
C
/*****************************************************************************
|
||
*
|
||
* 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
|