1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-29 05:53:44 +02:00
openwrt-xburst/target/linux/xburst/files-2.6.27/drivers/mtd/udc_cache.c
Mirko Vogt dc3d3f1c49 yet another patchset - 2.6.27
it's basically also provided by ingenic and nativly based on 2.6.27,
adjusted to fit into the OpenWrt-environment
2009-10-28 03:13:11 +08:00

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);