mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-11-29 05:53:44 +02:00
dc3d3f1c49
it's basically also provided by ingenic and nativly based on 2.6.27, adjusted to fit into the OpenWrt-environment
532 lines
13 KiB
C
532 lines
13 KiB
C
#include <linux/fs.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/hdreg.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/nand.h>
|
|
#include <linux/mtd/blktrans.h>
|
|
#include <linux/mutex.h>
|
|
#include <asm/jzsoc.h>
|
|
|
|
#define CACHE_MAX_NUM 256
|
|
#define SECTOR_SIZE 512
|
|
|
|
//#define UDC_CACHE_DEBUG
|
|
|
|
#ifdef UDC_CACHE_DEBUG
|
|
#define dprintk(a...) printk(a)
|
|
#else
|
|
#define dprintk(a...) while(0){}
|
|
#endif
|
|
|
|
typedef struct {
|
|
unsigned short CacheState;
|
|
unsigned short UseCount;
|
|
unsigned short CacheChange;
|
|
unsigned short CacheReserve;
|
|
unsigned int BlockId;
|
|
unsigned char *aBlockData;
|
|
} SSFDC__LB_CACHE;
|
|
|
|
#define FREE_CACHE 0
|
|
#define PREWRITE_CACHE 2
|
|
#define OFTEN_USE_CACHE 3
|
|
#define SECTOR_SHIFT 9
|
|
|
|
#define CACHE_TO_UNCATCH(x) ((unsigned int)x | 0xa0000000)
|
|
static unsigned int __aBlockData[SECTOR_SIZE * CACHE_MAX_NUM / 4] __attribute__ ((aligned (32)));
|
|
static SSFDC__LB_CACHE ssfdc_cache[CACHE_MAX_NUM];
|
|
static unsigned short Cur_CacheCount = 0;
|
|
int FlushDataState = 0;
|
|
static struct mtdblk_dev *g_udc_mtdblk;
|
|
static struct mtd_info *g_udc_mtd;
|
|
|
|
extern int udc_mtdblock_readsect(struct mtdblk_dev *, unsigned long, char *, int);
|
|
extern int udc_mtdblock_writesect(struct mtdblk_dev *, unsigned long, char *);
|
|
extern struct mtdblk_dev *udc_get_mtdblk(void);
|
|
extern struct mtd_info *udc_get_mtd(void);
|
|
extern void udc_flush_cache(struct mtdblk_dev *mtdblk);
|
|
|
|
#define _NAND_LB_Write(pCache) udc_mtdblock_writesect(g_udc_mtdblk, pCache->BlockId,pCache->aBlockData)
|
|
#define _NAND_LB_Read(Sector,pBuffer) udc_mtdblock_readsect(g_udc_mtdblk, Sector, pBuffer, SECTOR_SIZE);
|
|
|
|
#define DMA_ENABLE 0
|
|
|
|
#if DMA_ENABLE
|
|
#define DMA_CHANNEL 5
|
|
#define PHYSADDR(x) virt_to_phys((void *)x)
|
|
#else
|
|
#define lb_memcpy memcpy
|
|
#endif
|
|
|
|
#if DMA_ENABLE
|
|
static void lb_memcpy(void *target,void* source,unsigned int len)
|
|
{
|
|
int ch = DMA_CHANNEL;
|
|
if(((unsigned int)source < 0xa0000000) && len)
|
|
dma_cache_wback_inv((unsigned long)source, len);
|
|
if(((unsigned int)target < 0xa0000000) && len)
|
|
dma_cache_wback_inv((unsigned long)target, len);
|
|
|
|
REG_DMAC_DSAR(ch) = PHYSADDR((unsigned long)source);
|
|
REG_DMAC_DTAR(ch) = PHYSADDR((unsigned long)target);
|
|
REG_DMAC_DTCR(ch) = len / 32;
|
|
REG_DMAC_DRSR(ch) = DMAC_DRSR_RS_AUTO;
|
|
REG_DMAC_DCMD(ch) = DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32|DMAC_DCMD_DS_32BYTE;
|
|
REG_DMAC_DCCSR(ch) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES;
|
|
while ( REG_DMAC_DTCR(ch) );
|
|
}
|
|
#endif
|
|
|
|
static void _NAND_LB_InitCache(void)
|
|
{
|
|
int i;
|
|
SSFDC__LB_CACHE *pCache = ssfdc_cache;
|
|
#if DMA_ENABLE
|
|
unsigned char * ptr = (unsigned char *)CACHE_TO_UNCATCH(__aBlockData);
|
|
#else
|
|
unsigned char * ptr = (unsigned char *)(__aBlockData);
|
|
#endif
|
|
for(i = 0;i < CACHE_MAX_NUM;i++)
|
|
{
|
|
pCache->CacheState = FREE_CACHE;
|
|
pCache->UseCount = 0;
|
|
pCache->CacheChange = 0;
|
|
pCache->aBlockData = ptr;
|
|
ptr+=SECTOR_SIZE;
|
|
pCache++;
|
|
}
|
|
Cur_CacheCount = 0;
|
|
}
|
|
|
|
static SSFDC__LB_CACHE * _NAND_LB_GetFreeCache(void)
|
|
{
|
|
int ret = 0;
|
|
SSFDC__LB_CACHE *pCacheInfo = &ssfdc_cache[Cur_CacheCount];
|
|
while(1)
|
|
{
|
|
if(ret >= CACHE_MAX_NUM)
|
|
return 0;
|
|
if(pCacheInfo >= &ssfdc_cache[CACHE_MAX_NUM])
|
|
{
|
|
pCacheInfo = ssfdc_cache;
|
|
Cur_CacheCount = 0;
|
|
}
|
|
|
|
if(pCacheInfo->CacheState == FREE_CACHE)
|
|
{
|
|
return pCacheInfo;
|
|
}
|
|
pCacheInfo++;
|
|
Cur_CacheCount++;
|
|
ret++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void _NAND_LB_CloseCACHES(unsigned int sectorstart,unsigned int sectorend)
|
|
{
|
|
unsigned int i;
|
|
SSFDC__LB_CACHE *pCache = ssfdc_cache;
|
|
for( i = 0;i < CACHE_MAX_NUM;i++){
|
|
if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){
|
|
pCache->CacheChange = 0;
|
|
pCache->CacheState = FREE_CACHE;
|
|
pCache->UseCount = 0;
|
|
}
|
|
pCache++;
|
|
}
|
|
}
|
|
|
|
static void _NAND_LB_FLUSHCACHES(unsigned int sectorstart,unsigned int sectorend)
|
|
{
|
|
unsigned int i;
|
|
SSFDC__LB_CACHE *pCache = ssfdc_cache;
|
|
for( i = 0;i < CACHE_MAX_NUM;i++){
|
|
if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){
|
|
if(pCache->CacheChange)
|
|
_NAND_LB_Write(pCache);
|
|
pCache->CacheChange = 0;
|
|
pCache->CacheState = FREE_CACHE;
|
|
pCache->UseCount = 0;
|
|
}
|
|
pCache++;
|
|
|
|
}
|
|
}
|
|
|
|
inline static int Get_NAND_CacheFreeCount(void)
|
|
{
|
|
SSFDC__LB_CACHE *pCache = ssfdc_cache;
|
|
SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM];
|
|
unsigned int count = 0;
|
|
while(pCache < pEndCache)
|
|
{
|
|
if(pCache->CacheState == FREE_CACHE)
|
|
count++;
|
|
pCache++;
|
|
}
|
|
return count;
|
|
|
|
}
|
|
|
|
static unsigned int _NAND_LB_PreWiteToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update)
|
|
{
|
|
SSFDC__LB_CACHE *pWriteCache;
|
|
SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM];
|
|
unsigned int sector = -1;
|
|
unsigned int flag;
|
|
while(1)
|
|
{
|
|
sector = -1;
|
|
flag = 0;
|
|
pWriteCache = ssfdc_cache;
|
|
while(pWriteCache < pEndCache)
|
|
{
|
|
if(pWriteCache->CacheState == update) //PREWRITE_CACHE
|
|
{
|
|
if(pWriteCache->BlockId < sector)
|
|
{
|
|
sector = pWriteCache->BlockId;
|
|
pCache = pWriteCache;
|
|
}
|
|
}else
|
|
flag++;
|
|
pWriteCache++;
|
|
}
|
|
|
|
if(flag < CACHE_MAX_NUM)
|
|
{
|
|
if(pCache->CacheChange)
|
|
{
|
|
_NAND_LB_Write(pCache);
|
|
pCache->CacheChange = 0;
|
|
}
|
|
pCache->CacheState = FREE_CACHE;
|
|
pCache->UseCount = 0;
|
|
(*count)++;
|
|
}else
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void _NAND_LB_OftenToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update)
|
|
{
|
|
SSFDC__LB_CACHE *pWriteCache = pCache;
|
|
SSFDC__LB_CACHE *pOldCache = pCache;
|
|
SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM];
|
|
|
|
dprintk("%s!\n",__FUNCTION__);
|
|
while(pCache)
|
|
{
|
|
if(pCache->CacheState == OFTEN_USE_CACHE)
|
|
{
|
|
if(pWriteCache->CacheState != OFTEN_USE_CACHE)
|
|
pWriteCache = pCache;
|
|
else if(pWriteCache->UseCount > pCache->UseCount)
|
|
{
|
|
pWriteCache = pCache;
|
|
}
|
|
}
|
|
pCache++;
|
|
if(pCache >= pEndCache)
|
|
break;
|
|
}
|
|
if(pWriteCache->CacheState == OFTEN_USE_CACHE)
|
|
{
|
|
(*count)++;
|
|
if(pWriteCache->CacheChange)
|
|
_NAND_LB_Write(pWriteCache);
|
|
pWriteCache->CacheState = FREE_CACHE;
|
|
|
|
pWriteCache->UseCount = 0;
|
|
pWriteCache->CacheChange = 0;
|
|
if(update != -1)
|
|
update--;
|
|
if(update != 0)
|
|
_NAND_LB_OftenToNand(pOldCache,count,update);
|
|
}
|
|
}
|
|
|
|
static int _NAND_LB_FreeCache(unsigned int update)
|
|
{
|
|
unsigned short freecount = 0,totalfree = 0;
|
|
|
|
freecount = 0;
|
|
_NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE);
|
|
|
|
totalfree += freecount;
|
|
dprintk("free count = %d\n",freecount);
|
|
if(freecount == 0)
|
|
{
|
|
freecount = 0;
|
|
_NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE);
|
|
totalfree += freecount;
|
|
update = 0;
|
|
}
|
|
if(update)
|
|
{
|
|
if(Get_NAND_CacheFreeCount() < CACHE_MAX_NUM * 1 / 4) // because fat is 4 sector
|
|
{
|
|
freecount = 0;
|
|
_NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE);
|
|
totalfree += freecount;
|
|
}
|
|
}
|
|
|
|
dprintk("Free = %d\r\n",totalfree);
|
|
return totalfree;
|
|
}
|
|
|
|
static int _NAND_LB_GetFromCache(unsigned int Sector, void *pBuffer) {
|
|
|
|
SSFDC__LB_CACHE *pCache = &ssfdc_cache[Cur_CacheCount];
|
|
SSFDC__LB_CACHE *pUseCache = 0;
|
|
unsigned short i;
|
|
dprintk("sector = %x pBuffer = %x\n",Sector,pBuffer);
|
|
if(pCache >= &ssfdc_cache[CACHE_MAX_NUM])
|
|
pCache = ssfdc_cache;
|
|
|
|
i = 0;
|
|
while (1) {
|
|
if(pCache->CacheState != FREE_CACHE)
|
|
{
|
|
if (Sector == pCache->BlockId) {
|
|
dprintk("Cache is use = %d\r\n",pCache->BlockId);
|
|
pUseCache = pCache;
|
|
pCache->UseCount++;
|
|
if(pCache->UseCount == 0)
|
|
pCache->UseCount = -1;
|
|
pCache->CacheState = OFTEN_USE_CACHE;
|
|
}
|
|
}
|
|
pCache--;
|
|
if(pCache < ssfdc_cache)
|
|
pCache = &ssfdc_cache[CACHE_MAX_NUM - 1];
|
|
|
|
i++;
|
|
if (i >= CACHE_MAX_NUM) {
|
|
break; /* Sector not in cache */
|
|
}
|
|
}
|
|
if (pUseCache) {
|
|
dprintk("From Cache %d\r\n",Sector);
|
|
lb_memcpy(pBuffer, pUseCache->aBlockData, SECTOR_SIZE);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void _NAND_LB_ClearCache(void) {
|
|
|
|
unsigned short freecount = 0;
|
|
dprintk("Clear Cache\r\n");
|
|
|
|
_NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE);
|
|
_NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE);
|
|
}
|
|
|
|
static void _NAND_LB_CopyToCache(unsigned int Sector, void *pBuffer,unsigned short rw)
|
|
{
|
|
SSFDC__LB_CACHE *pCache = _NAND_LB_GetFreeCache();
|
|
dprintk("Copy to Cache = 0x%08x 0x%08x\r\n",pCache,ssfdc_cache);
|
|
|
|
if(!pCache)
|
|
{
|
|
_NAND_LB_FreeCache(rw);
|
|
|
|
pCache = _NAND_LB_GetFreeCache();
|
|
}
|
|
pCache->BlockId = Sector;
|
|
pCache->CacheState = PREWRITE_CACHE;
|
|
pCache->UseCount = 0;
|
|
pCache->CacheChange = rw;
|
|
|
|
lb_memcpy(pCache->aBlockData,pBuffer,SECTOR_SIZE);
|
|
}
|
|
|
|
|
|
static int _NAND_LB_UpdateInCache(unsigned int Sector, void *pBuffer) {
|
|
short i,ret = 0;
|
|
i = Cur_CacheCount;
|
|
if(Cur_CacheCount > CACHE_MAX_NUM)
|
|
i = 0;
|
|
while(1)
|
|
{
|
|
if(ret >= CACHE_MAX_NUM)
|
|
return -1;
|
|
if(ssfdc_cache[i].CacheState != FREE_CACHE)
|
|
{
|
|
|
|
if(ssfdc_cache[i].BlockId == Sector)
|
|
{
|
|
dprintk("UpdateInCache = %d\r\n",Sector);
|
|
ssfdc_cache[i].CacheState = OFTEN_USE_CACHE;
|
|
ssfdc_cache[i].UseCount++;
|
|
ssfdc_cache[i].CacheChange = 1;
|
|
lb_memcpy(ssfdc_cache[i].aBlockData,pBuffer,SECTOR_SIZE);
|
|
return 0;
|
|
}
|
|
}
|
|
i--;
|
|
if(i < 0)
|
|
i = CACHE_MAX_NUM - 1;
|
|
ret++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int NAND_LB_MultiRead(unsigned int Sector, void *pBuffer,unsigned int SectorCount)
|
|
{
|
|
int i,ret,end;
|
|
void *p;
|
|
|
|
dprintk("NAND_LB_MultiRead = %d %d \n",Sector,SectorCount);
|
|
end = Sector + SectorCount;
|
|
_NAND_LB_FLUSHCACHES(Sector,end);
|
|
|
|
p = pBuffer;
|
|
for (i = Sector; i < end; i ++)
|
|
{
|
|
ret = udc_mtdblock_readsect(g_udc_mtdblk, i, p, SECTOR_SIZE);
|
|
p += SECTOR_SIZE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int NAND_LB_Read(unsigned int Sector, void *pBuffer)
|
|
{
|
|
int x;
|
|
#if DMA_ENABLE
|
|
unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer);
|
|
dma_cache_wback_inv(pBuffer,SECTOR_SIZE);
|
|
#else
|
|
unsigned char *ptr = (unsigned char *)pBuffer;
|
|
#endif
|
|
dprintk("LB_Read = %d \n",Sector);
|
|
if(_NAND_LB_GetFromCache(Sector,ptr))
|
|
{
|
|
x = _NAND_LB_Read(Sector,ptr);
|
|
_NAND_LB_CopyToCache(Sector,ptr,0);
|
|
}
|
|
return 512;
|
|
}
|
|
|
|
static int NAND_LB_MultiWrite(unsigned int Sector, void *pBuffer,unsigned int SectorCount)
|
|
{
|
|
int i,ret;
|
|
unsigned char *p;
|
|
|
|
_NAND_LB_CloseCACHES(Sector,Sector + SectorCount);
|
|
p = (unsigned char *)pBuffer;
|
|
for (i = Sector; i < Sector + SectorCount; i ++)
|
|
{
|
|
ret = udc_mtdblock_writesect(g_udc_mtdblk, i, p);
|
|
p += 512;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int NAND_LB_Write(unsigned int Sector, void *pBuffer)
|
|
{
|
|
#if DMA_ENABLE
|
|
unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer);
|
|
dma_cache_wback_inv(pBuffer,SECTOR_SIZE);
|
|
#else
|
|
unsigned char *ptr = (unsigned char *)pBuffer;
|
|
#endif
|
|
dprintk("LB_Write = %x %x\r\n",Sector,pBuffer);
|
|
if(_NAND_LB_UpdateInCache(Sector,ptr))
|
|
{
|
|
_NAND_LB_CopyToCache(Sector,ptr,1);
|
|
}
|
|
return 512;
|
|
}
|
|
/*********************************************************************
|
|
*
|
|
* Global functions
|
|
*
|
|
***********************************************************************/
|
|
|
|
int NAND_LB_Init(void)
|
|
{
|
|
dprintk("UDC CACHE Init \n");
|
|
_NAND_LB_InitCache();
|
|
g_udc_mtdblk = udc_get_mtdblk();
|
|
g_udc_mtd = udc_get_mtd();
|
|
return 0;
|
|
}
|
|
|
|
int NAND_LB_FLASHCACHE(void)
|
|
{
|
|
dprintk("Flush lb cache !\n");
|
|
_NAND_LB_ClearCache();
|
|
// dprintk("Flush mtd cache !\n");
|
|
// udc_flush_cache(g_udc_mtdblk);
|
|
return 0;
|
|
}
|
|
|
|
int NAND_MTD_FLASHCACHE(void)
|
|
{
|
|
dprintk("Flush mtd cache !\n");
|
|
udc_flush_cache(g_udc_mtdblk);
|
|
return 0;
|
|
}
|
|
|
|
int udc_read(unsigned int offset, unsigned int len, unsigned char *buf)
|
|
{
|
|
unsigned long block,sector,i;
|
|
|
|
block = offset >> SECTOR_SHIFT;
|
|
sector = len >> SECTOR_SHIFT;
|
|
dprintk("read dev = ia:%x, s:%d c:%d\r\n",buf,block,sector);
|
|
|
|
if (sector <= 8)
|
|
{
|
|
for(i = 0;i < sector; i++)
|
|
{
|
|
NAND_LB_Read(block + i,(void *)(buf));
|
|
buf += 512;
|
|
}
|
|
}
|
|
else
|
|
NAND_LB_MultiRead(block, buf, sector);
|
|
|
|
return len;
|
|
}
|
|
|
|
int udc_write(unsigned int offset, unsigned int len, unsigned char *buf)
|
|
{
|
|
unsigned long block,sector,i;
|
|
|
|
block = offset >> SECTOR_SHIFT;
|
|
sector = len >> SECTOR_SHIFT;
|
|
dprintk("write dev s:%d c:%d\r\n",block,sector);
|
|
|
|
if(sector <= 8)
|
|
{
|
|
for(i = 0;i < sector; i++)
|
|
{
|
|
NAND_LB_Write(block + i,(void *)(buf));
|
|
buf += 512;
|
|
FlushDataState = 1;
|
|
}
|
|
}else
|
|
NAND_LB_MultiWrite(block,(void *)(buf),sector);
|
|
|
|
return len;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(udc_write);
|
|
EXPORT_SYMBOL_GPL(udc_read);
|
|
EXPORT_SYMBOL_GPL(NAND_LB_Init);
|
|
EXPORT_SYMBOL_GPL(NAND_LB_FLASHCACHE);
|
|
EXPORT_SYMBOL_GPL(FlushDataState);
|
|
EXPORT_SYMBOL_GPL(NAND_MTD_FLASHCACHE);
|