📄 lpc32xx_fmd.cpp
字号:
//*********************************************************************
//* 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.
//* NXP Semiconductors 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. NXP Semiconductors
//* reserves the right to make changes in the software without
//* notification. NXP Semiconductors also make no representation or
//* warranty that such application will be suitable for the specified
//* use without further testing or modification.
//*
//* Copyright NXP Semiconductors
//*********************************************************************
//
// lpc32xx_fmd.c
//
// NAND FLASH functions
//
#include <fmd.h>
#include <ceddk.h>
#include <ddkreg.h>
#include <Nkintr.h>
#include <Winbase.h>
#include "lbecc.h"
#include "lpc32xx_fmd.h"
#include "lpc32xx_clkpwr.h"
#include "lpc32xx_slcnand.h"
#include "lpc32xx_gpio.h"
#include "bsp.h"
// NAND info structure
typedef struct
{
SLCNAND_REGS_T *pSLCRegs; // Pointer to SLC registers
CLKPWR_REGS_T *pCLKPWRRegs; // Pointer to CLKPWR registers
GPIO_REGS_T *pGPIORegs; // Pointer to GPIO registers
DWORD dwSysIntr; // SLC NAND sysIntr value
HANDLE dwEvent; // SLC NAND event
#ifdef FMDACCESSLOCKS
HANDLE Lockmutex; // Access mutex
#endif
// Queried device geometry
DWORD dwNumBlocks;
DWORD dwBytesPerBlock;
WORD dwSectorsPerBlock;
WORD dwDataBytesPerSector;
DWORD addressCycles; // NAND access address cycles
} NANDDRVDAT_T;
static NANDDRVDAT_T nandDrvDat;
//------------------------------------------------------------------------------
//
// nandMutexLock
//
// Get mutex lock
//
static void nandMutexLock(void)
{
#ifdef FMDACCESSLOCKS
WaitForSingleObject(nandDrvDat.Lockmutex, INFINITE);
#endif
}
//------------------------------------------------------------------------------
//
// nandMutexUnlock
//
// Return mutex lock
//
static void nandMutexUnlock(void)
{
#ifdef FMDACCESSLOCKS
ReleaseMutex(nandDrvDat.Lockmutex);
#endif
}
//------------------------------------------------------------------------------
//
// nandWpEnable
//
// Enable or disable NAND write protect
//
void nandWpEnable(BOOL enable)
{
if (enable == TRUE)
{
nandDrvDat.pGPIORegs->pio_outp_set = OUTP_STATE_GPO(19);
}
else
{
nandDrvDat.pGPIORegs->pio_outp_clr = OUTP_STATE_GPO(19);
}
}
//------------------------------------------------------------------------------
//
// nandCSLock
//
// Lock or unlock NAND chip select signal
//
static void nandCSLock(BOOL lock)
{
if (lock != FALSE)
{
nandDrvDat.pSLCRegs->slc_cfg |= SLCCFG_CE_LOW;
}
else
{
nandDrvDat.pSLCRegs->slc_cfg &= ~SLCCFG_CE_LOW;
}
}
//------------------------------------------------------------------------------
//
// nandCheckReady
//
// Check NAND ready
//
BOOL nandCheckReady(void)
{
BOOL rdy = FALSE;
if ((nandDrvDat.pSLCRegs->slc_stat & SLCSTAT_NAND_READY) != 0)
{
rdy = TRUE;
}
return rdy;
}
//------------------------------------------------------------------------------
//
// nandWriteAddress
//
// Write FLASH address
//
void nandWriteAddress (unsigned char *addr,
int bytes)
{
int idx = 0;
for (idx = 0; idx < bytes; idx++)
{
nandDrvDat.pSLCRegs->slc_addr = (UNS_32) addr[idx];
}
}
//------------------------------------------------------------------------------
//
// nandWriteCommand
//
// Write FLASH command
//
void nandWriteCommand (unsigned char cmd)
{
nandDrvDat.pSLCRegs->slc_cmd = (UNS_32) cmd;
}
//------------------------------------------------------------------------------
//
// nandWriteData
//
// Write to FLASH data
//
void nandWriteData (void *data,
int bytes)
{
int idx;
unsigned char *datab = (unsigned char *) data;
for (idx = 0; idx < bytes; idx++)
{
nandDrvDat.pSLCRegs->slc_data = (UINT32) datab[idx];
}
}
//------------------------------------------------------------------------------
//
// nandReadData
//
// Read from FLASH data
//
void nandReadData (void *addr,
int bytes)
{
int idx;
unsigned char *datab = (unsigned char *) addr;
for (idx = 0; idx < bytes; idx++)
{
datab[idx] = (UNS_8) nandDrvDat.pSLCRegs->slc_data;
}
}
//------------------------------------------------------------------------------
//
// nandWaitReady
//
// Wait for data ready from device
//
BOOL nandWaitReady(DWORD dataTimeout)
{
if (dataTimeout > 0)
{
// Clear and enable NAND interrupts
nandDrvDat.pSLCRegs->slc_icr = (SLCSTAT_INT_TC |
SLCSTAT_INT_RDY_EN);
InterruptDone(nandDrvDat.dwSysIntr);
// Wait for interrupt
WaitForSingleObject(nandDrvDat.dwEvent, dataTimeout);
}
// Return RDY status
return nandCheckReady();
}
//------------------------------------------------------------------------------
//
// nandGetGeom
//
// Get device geometry
//
BOOL nandGetGeom (void)
{
unsigned char temp[2];
int to = 50;
BOOL goodid = FALSE;
// Send read ID command and wait for response
nandCSLock(TRUE);
nandWriteCommand(LPCNAND_CMD_READ_ID);
temp[0] = 0;
nandWriteAddress(temp, 1);
while ((nandCheckReady() == FALSE) && (to > 0))
{
Sleep(1);
to--;
}
// RDY failed?
if (nandWaitReady(0) == FALSE)
{
RETAILMSG(1, (TEXT("FMD: Failed RDY response on NAND\r\n")));
nandCSLock(FALSE);
return FALSE;
}
// Read response
nandReadData(temp, 2);
// Verify manufacturer
if (temp[0] == LPCNAND_VENDOR_STMICRO)
{
nandDrvDat.dwSectorsPerBlock = 32;
nandDrvDat.dwDataBytesPerSector = 512;
// Determine geometry based on device ID
goodid = TRUE;
switch (temp[1])
{
case 0x73:
// 128MBit device
nandDrvDat.dwNumBlocks = 1024;
nandDrvDat.addressCycles = 3;
break;
case 0x35:
case 0x75:
// 256MBit device
nandDrvDat.dwNumBlocks = 2048;
nandDrvDat.addressCycles = 3;
break;
case 0x36:
case 0x76:
// 512MBit device
nandDrvDat.dwNumBlocks = 4096;
nandDrvDat.addressCycles = 4;
break;
case 0x39:
case 0x79:
// 1024MBit device
nandDrvDat.dwNumBlocks = 8192;
nandDrvDat.addressCycles = 4;
break;
default:
goodid = FALSE;
break;
}
#if BYPASSBLOCKS>0
nandDrvDat.dwNumBlocks = nandDrvDat.dwNumBlocks - BYPASSBLOCKS;
#endif
nandDrvDat.dwBytesPerBlock = (nandDrvDat.dwSectorsPerBlock *
nandDrvDat.dwDataBytesPerSector);
}
nandCSLock(FALSE);
return goodid;
}
//------------------------------------------------------------------------------
//
// nandGetPageIndex
//
// Get an address from a sector number
//
void nandGetPageIndex(ULONG SectorAddr,
ULONG offset,
unsigned char *addrbytes) {
ULONG block, page, nandaddr;
// Limit offset
if (offset >= 256)
{
offset = 0;
}
// Determine block and page offsets from passed sector address
block = SectorAddr / nandDrvDat.dwSectorsPerBlock;
page = SectorAddr - (block * nandDrvDat.dwSectorsPerBlock);
// Block Page Index
// 31..13 12..8 7..0
nandaddr = offset + ((page & 0x1F) << 8);
nandaddr = nandaddr | ((block & 0xFFF) << 13);
// Save block and page address
addrbytes[0] = (UNS_8) ((nandaddr >> 0) & 0xFF);
addrbytes[1] = (UNS_8) ((nandaddr >> 8) & 0xFF);
addrbytes[2] = (UNS_8) ((nandaddr >> 16) & 0xFF);
if (nandDrvDat.addressCycles == 4)
{
addrbytes[3] = (UNS_8) ((nandaddr >> 24) & 0xFF);
}
}
//------------------------------------------------------------------------------
//
// FMD_Init
//
// Initialize FLASH interface and data
//
extern "C"
PVOID FMD_Init(LPCTSTR lpActiveReg,
PPCI_REG_INFO pRegIn,
PPCI_REG_INFO pRegOut)
{
PHYSICAL_ADDRESS pa;
DWORD irq;
UINT32 clk, bytesret;
PVOID ret = NULL;
BOOL validconfig = FALSE;
/* Unused parameters */
(void) pRegIn;
(void) lpActiveReg;
// Defaults
nandDrvDat.pCLKPWRRegs = NULL;
nandDrvDat.pSLCRegs = NULL;
nandDrvDat.pGPIORegs = NULL;
nandDrvDat.dwSysIntr = SYSINTR_UNDEFINED;
nandDrvDat.dwEvent = NULL;
#ifdef FMDACCESSLOCKS
nandDrvDat.Lockmutex = NULL;
#endif
// MAP CLKPWR and SLC registers
pa.QuadPart = CLK_PM_BASE;
nandDrvDat.pCLKPWRRegs = (CLKPWR_REGS_T *) MmMapIoSpace(pa,
sizeof (CLKPWR_REGS_T), FALSE);
pa.QuadPart = SLC_BASE;
nandDrvDat.pSLCRegs = (SLCNAND_REGS_T *) MmMapIoSpace(pa,
sizeof (SLCNAND_REGS_T), FALSE);
pa.QuadPart = GPIO_BASE;
nandDrvDat.pGPIORegs = (GPIO_REGS_T *) MmMapIoSpace(pa,
sizeof (GPIO_REGS_T), FALSE);
if ((nandDrvDat.pCLKPWRRegs == NULL) || (nandDrvDat.pSLCRegs == NULL) ||
(nandDrvDat.pGPIORegs == NULL))
{
RETAILMSG(1,
(TEXT("FMD: Failed to map registers\r\n")));
goto cleanup;
}
// Setup SLC mode and enable SLC clock
nandDrvDat.pCLKPWRRegs->clkpwr_nand_clk_ctrl = (CLKPWR_NANDCLK_SEL_SLC |
CLKPWR_NANDCLK_SLCCLK_EN);
// Reset SLC controller and setup for 8-bit mode, disable and clear interrupts
nandDrvDat.pSLCRegs->slc_ctrl = SLCCTRL_SW_RESET;
Sleep(1);
nandDrvDat.pSLCRegs->slc_cfg = 0;
nandDrvDat.pSLCRegs->slc_ien = SLCSTAT_INT_RDY_EN;
nandDrvDat.pSLCRegs->slc_icr = (SLCSTAT_INT_TC |
SLCSTAT_INT_RDY_EN);
// Get current system clock speed for the SLC block
if (KernelIoControl(IOCTL_LPC32XX_GETHCLK, NULL, 0, &clk,
sizeof (clk), (LPDWORD) &bytesret) == FALSE)
{
// Cannot get clock, use default
RETAILMSG(1,
(TEXT("FMD: Error getting SLC base clock rate.\r\n")));
clk = 104000000;
}
// Setup SLC timing based on current clock
nandDrvDat.pSLCRegs->slc_tac = (
SLCTAC_WDR(1 + PHY_NAND_WDR) |
SLCTAC_WWIDTH(1 + (clk / PHY_NAND_WWIDTH)) |
SLCTAC_WHOLD(1 + (clk / PHY_NAND_WHOLD)) |
SLCTAC_WSETUP(1 + (clk / PHY_NAND_WSETUP)) |
SLCTAC_RDR(1 + PHY_NAND_RDR) |
SLCTAC_RWIDTH(1 + (clk / PHY_NAND_RWIDTH)) |
SLCTAC_RHOLD((1 + clk / PHY_NAND_RHOLD)) |
SLCTAC_RSETUP(1 + (clk / PHY_NAND_RSETUP)));
// Get device geometry
if (nandGetGeom() == FALSE)
{
// Unsupported type
RETAILMSG(1, (TEXT("FMD: Unsupported device type.\r\n")));
goto cleanup;
}
// Map sysIntr value to the NAND interrupt
irq = OAL_INTR_IRQ_NAND;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &irq,
sizeof(irq), &nandDrvDat.dwSysIntr, sizeof(nandDrvDat.dwSysIntr), NULL))
{
RETAILMSG(1,
(TEXT("FMD: Error obtaining SYSINTR value!\r\n")));
nandDrvDat.dwSysIntr = SYSINTR_UNDEFINED;
goto cleanup;
}
// Create NAND interrupt event
nandDrvDat.dwEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (nandDrvDat.dwEvent == NULL)
{
RETAILMSG(1,
(TEXT("FMD: Failed to create NAND handler event.\r\n")));
nandDrvDat.dwEvent = NULL;
goto cleanup;
}
// Bind interrupt to events
if (InterruptInitialize(nandDrvDat.dwSysIntr, nandDrvDat.dwEvent, NULL,
0) == FALSE)
{
// Cannot initialize interrupt
RETAILMSG(1,
(TEXT("FMD: Cannot initialize NAND interrupt\r\n")));
goto cleanup;
}
#ifdef FMDACCESSLOCKS
// Create lock
nandDrvDat.Lockmutex = CreateMutex(NULL, FALSE, NULL);
if (nandDrvDat.Lockmutex == NULL) {
RETAILMSG(1,
(TEXT("FMD: Cannot create mutex lock\r\n")));
goto cleanup;
}
#endif
// Initialize ECC
eccInitTables();
// Reset device
nandWriteCommand(LPCNAND_CMD_RESET);
nandWaitReady(10);
ret = &nandDrvDat;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -