📄 drv.c
字号:
/******************************************************************************
* Flash File System (ffs)
* Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com
*
* ffs low level flash driver
*
* $Id: drv.c,v 1.1.1.1 2004/06/19 06:00:30 root Exp $
*
******************************************************************************/
#ifndef TARGET
#include "ffs.cfg"
#endif
#include "board.cfg"
#include "ffs.h"
#include "drv.h"
#include "ffstrace.h"
#if (TARGET == 0)
#include "stdio.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/mman.h"
#include "fcntl.h"
#include "unistd.h"
#else
#include "../Os/nucleus.h"
#endif
// Note that all code notes and comments pertaining to single-bank flash
// drivers are located in the AMD SB driver (amdsbdrv.c). Consider this as
// the reference.
/******************************************************************************
* Globals
******************************************************************************/
#if (BOARD == 34)
extern const unsigned char FlashImage[]; // image of the file system - 16 kB
#endif
#if (TARGET == 1)
// NOTE: This is the size in bytes of the single-bank driver code that is
// copied to RAM. The only way to determine the amount of memory needed is
// to look into the linker output file (.map) or the assembler output of
// both amdsbdrv.obj and intelsbdrv.obj files.
#define FFSDRV_CODE_SIZE (0x200)
uint8 ffsdrv_code[FFSDRV_CODE_SIZE];
#endif
struct dev_s dev;
struct ffsdrv_s ffsdrv;
uint32 int_disable(void);
void int_enable(uint32 tmp);
/******************************************************************************
* Macros
******************************************************************************/
#define addr2offset(address) ( (int) (address) - (int) dev.base )
/******************************************************************************
* Generic Driver Functions
******************************************************************************/
void ffsdrv_generic_write(void *dst, const void *src, uint16 size)
{
uint8 *mydst = dst;
const uint8 *mysrc = src;
if (size > 0)
{
if ((unsigned int) mydst & 1) {
ffsdrv_write_byte(mydst++, *mysrc++);
size--;
}
while (size >= 2) {
ffsdrv.write_halfword((uint16 *) mydst,
mysrc[0] | (mysrc[1] << 8));
size -= 2;
mysrc += 2;
mydst += 2;
}
if (size == 1)
ffsdrv_write_byte(mydst++, *mysrc++);
}
}
/******************************************************************************
* AMD Single Bank Driver Functions
******************************************************************************/
#if (TARGET == 1)
// Forward declaration of functions in file amdsbdrv.c
void ffsdrv_ram_amd_sb_write_halfword(volatile uint16 *addr, uint16 value);
void ffsdrv_ram_amd_sb_erase(uint8 block);
#else // (TARGET == 0)
// On PC these functions are empty
void ffsdrv_ram_amd_sb_write_halfword(volatile uint16 *addr, uint16 value) {}
void ffsdrv_ram_amd_sb_erase(uint8 block) {}
#endif // (TARGET == 1)
/******************************************************************************
* AMD Pseudo Single Bank Driver Functions
******************************************************************************/
// This is a pseudo single-bank flash driver. It simulates a single-bank
// flash device on a dual-bank device.
#if (TARGET == 1)
void ffsdrv_amd_pseudo_sb_write_halfword(volatile uint16 *addr, uint16 value)
{
volatile char *flash = dev.base;
uint32 cpsr, i, x;
ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value));
if (~*addr & value) {
ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value));
return;
}
cpsr = int_disable();
tlw(led_on(LED_WRITE));
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
flash[0xAAAA] = 0xA0;
*addr = value;
while ((*dev.addr ^ dev.data) & 0x80)
;
tlw(led_off(LED_WRITE));
int_enable(cpsr);
}
// This VERY simple way of erase suspending only works because we run under
// a pre-emptive operating system, so whenever an interrupt occurs, another
// task takes the CPU, and at the end of the interrupt, FFS gets the CPU
// again.
void ffsdrv_amd_pseudo_sb_erase(uint8 block)
{
volatile char *flash = dev.base;
volatile char *addr;
uint32 cpsr;
uint16 flashpoll;
addr = block2addr(block);
ttw(ttr(TTrDrvErase, "e(%d)" NL, block));
cpsr = int_disable();
tlw(led_on(LED_ERASE));
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
flash[0xAAAA] = 0x80;
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
*addr = 0x30; // AMD erase sector command
// Wait for erase to finish.
while ((*addr & 0x80) == 0) {
tlw(led_toggle(LED_ERASE));
// Poll interrupts, taking interrupt mask into account.
if (INT_REQUESTED)
{
// 1. suspend erase
// 2. enable interrupts
// .. now the interrupt code executes
// 3. disable interrupts
// 4. resume erase
tlw(led_on(LED_ERASE_SUSPEND));
*addr = 0xB0;
// wait for erase suspend to finish
while ((*addr & 0x80) == 0)
;
tlw(led_off(LED_ERASE_SUSPEND));
int_enable(cpsr);
// Other interrupts and tasks run now...
cpsr = int_disable();
tlw(led_on(LED_ERASE_SUSPEND));
// Before resuming erase we must? check if the erase is really
// suspended or if it did finish
flashpoll = *addr;
*addr = 0x30;
tlw(led_off(LED_ERASE_SUSPEND));
}
}
tlw(led_on(LED_ERASE));
tlw(led_off(LED_ERASE));
int_enable(cpsr);
}
#else // (TARGET == 0)
void ffsdrv_amd_pseudo_sb_write_halfword(volatile uint16 *addr, uint16 value) {}
void ffsdrv_amd_pseudo_sb_erase(uint8 block) {}
#endif // (TARGET == 1)
/******************************************************************************
* AMD Dual/Multi Bank Driver Functions
******************************************************************************/
// All erase and write operations are performed atomically (interrupts
// disabled). Otherwise we cannot trust the value of dev.state and we cannot
// determine exactly how many of the command words have already been
// written.
// in ffs_end() when we resume an erasure that was previously suspended, how
// does that affect multiple tasks doing that simultaneously?
void ffsdrv_amd_write_end(void);
void ffsdrv_amd_erase_end(void);
void ffsdrv_amd_write_halfword(volatile uint16 *addr, uint16 value)
{
volatile char *flash = dev.base;
uint32 cpsr;
tlw(led_on(LED_WRITE));
ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value));
dev.addr = addr;
dev.data = value;
if (~*addr & value) {
ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value));
return;
}
cpsr = int_disable();
tlw(led_toggle(LED_WRITE_SUSPEND));
dev.state = DEV_WRITE;
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
flash[0xAAAA] = 0xA0;
*addr = value;
int_enable(cpsr);
tlw(led_toggle(LED_WRITE_SUSPEND));
ffsdrv_amd_write_end();
}
void ffsdrv_amd_write(void *dst, const void *src, uint16 size)
{
uint8 *mydst = dst;
const uint8 *mysrc = src;
if (size > 0)
{
if ((unsigned int) mydst & 1) {
ffsdrv_write_byte(mydst++, *mysrc++);
size--;
}
while (size >= 2) {
ffsdrv_amd_write_halfword((uint16 *) mydst,
mysrc[0] | (mysrc[1] << 8));
size -= 2;
mysrc += 2;
mydst += 2;
}
if (size == 1)
ffsdrv_write_byte(mydst++, *mysrc++);
}
}
void ffsdrv_amd_write_end(void)
{
while ((*dev.addr ^ dev.data) & 0x80)
tlw(led_toggle(LED_WRITE_SUSPEND));
dev.state = DEV_READ;
tlw(led_off(LED_WRITE));
}
void ffsdrv_amd_erase(uint8 block)
{
volatile char *flash = dev.base;
uint32 cpsr;
tlw(led_on(LED_ERASE));
ttw(ttr(TTrDrvErase, "e(%d)" NL, block));
dev.addr = (uint16 *) block2addr(block);
cpsr = int_disable();
dev.state = DEV_ERASE;
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
flash[0xAAAA] = 0x80;
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
*dev.addr = 0x30; // AMD erase sector command
int_enable(cpsr);
ffsdrv_amd_erase_end();
}
void ffsdrv_amd_erase_end(void)
{
while ((*dev.addr & 0x80) == 0)
;
dev.state = DEV_READ;
tlw(led_off(LED_ERASE));
}
void ffsdrv_amd_erase_suspend(void)
{
uint32 cpsr;
tlw(led_on(LED_ERASE_SUSPEND));
ttw(str(TTrDrvErase, "es" NL));
// if erase has finished then all is ok
if (*dev.addr & 0x80) {
ffsdrv_amd_erase_end();
tlw(led_off(LED_ERASE_SUSPEND));
return;
}
// NOTEME: As there is no way to be absolutely certain that erase
// doesn't finish between last poll and the following erase suspend
// command, we assume that the erase suspend is safe even though the
// erase IS actually already finished.
cpsr = int_disable();
dev.state = DEV_ERASE_SUSPEND;
*dev.addr = 0xB0;
// Wait for erase suspend to finish
while ((*dev.addr & 0x80) == 0)
;
int_enable(cpsr);
}
void ffsdrv_amd_erase_resume(void)
{
uint32 cpsr;
ttw(str(TTrDrvErase, "er" NL));
// NOTEME: See note in erase_suspend()... We assume that the erase
// resume is safe even though the erase IS actually already finished.
cpsr = int_disable();
dev.state = DEV_ERASE;
*dev.addr = 0x30;
int_enable(cpsr);
tlw(led_off(LED_ERASE_SUSPEND));
}
/******************************************************************************
* SST Dual/Multi Bank Driver Functions
******************************************************************************/
// SST flashes use almost same command set as AMD flashes. Only the command
// addresses (4 more bits) and erase command data (0x50 instead of 0x30) are
// different. SST flashes have no erase suspend/resume commands because they
// are so fast at erasing!
void ffsdrv_sst_write_end(void);
void ffsdrv_sst_erase_end(void);
void ffsdrv_sst_write(void *dst, const void *src, uint16 size)
{
uint8 *mydst = dst;
const uint8 *mysrc = src;
if (size > 0)
{
if ((unsigned int) mydst & 1) {
ffsdrv_write_byte(mydst++, *mysrc++);
size--;
}
while (size >= 2) {
ffsdrv_amd_write_halfword((uint16 *) mydst,
mysrc[0] | (mysrc[1] << 8));
size -= 2;
mysrc += 2;
mydst += 2;
}
if (size == 1)
ffsdrv_write_byte(mydst++, *mysrc++);
}
}
// Note that SST flashes have smaller sectors than other flash families.
// Fortunately they support erasure of several of these sectors in a logical
// unit called a "block".
void ffsdrv_sst_erase(uint8 block)
{
volatile char *flash = dev.base;
uint32 cpsr;
tlw(led_on(LED_ERASE));
ttw(ttr(TTrDrvErase, "e(%d)" NL, block));
dev.addr = (uint16 *) block2addr(block);
cpsr = int_disable();
dev.state = DEV_ERASE;
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
flash[0xAAAA] = 0x80;
flash[0xAAAA] = 0xAA; // unlock cycle 1
flash[0x5555] = 0x55; // unlock cycle 2
*dev.addr = 0x50; // SST erase block command
int_enable(cpsr);
ffsdrv_sst_erase_end();
}
void ffsdrv_sst_erase_end(void)
{
// Wait for erase end
while ((*dev.addr & 0x80) == 0)
;
dev.state = DEV_READ;
tlw(led_off(LED_ERASE));
}
// Erase suspend/resume commands do not exist for SST flashes, so we just
// poll for the end of the erase operation...
void ffsdrv_sst_erase_suspend(void)
{
ttw(str(TTrDrvErase, "es" NL));
ffsdrv_sst_erase_end();
}
/******************************************************************************
* Intel Single Bank Driver Functions
******************************************************************************/
#if (TARGET == 1)
// Forward declaration of functions in file intelsbdrv.c
int ffsdrv_ram_intel_sb_init(void);
void ffsdrv_ram_intel_sb_write_halfword(volatile uint16 *addr, uint16 value);
void ffsdrv_ram_intel_sb_erase(uint8 block);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -