You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
12 KiB
316 lines
12 KiB
/* |
|
* Copyright (C) 2017 MediaTek Inc. |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
* See http://www.gnu.org/licenses/gpl-2.0.html for more details. |
|
*/ |
|
|
|
#ifndef __LINUX_MTFTL_H__ |
|
#define __LINUX_MTFTL_H__ |
|
|
|
#include <linux/mutex.h> |
|
#include <linux/workqueue.h> |
|
#include "linux/mtd/ubi.h" |
|
|
|
/* #define PROFILE */ |
|
/*#define MTK_FTL_DEBUG */ |
|
|
|
#define MT_FTL_SUCCESS 0 |
|
#define MT_FTL_FAIL -1 |
|
|
|
#define MT_INVALID_BLOCKPAGE 0xFFFFFFFE |
|
#define MT_PAGE_HAD_BEEN_READ 0x9EAD |
|
#define MT_MAGIC_NUMBER 0x3105 |
|
|
|
#define NAND_DEFAULT_VALUE 0xFFFFFFFF |
|
#define NAND_VOLUME (8 * 1024 * 1024) /* Unit: kbytes */ |
|
#define NAND_PAGE_SIZE (16 * 1024) |
|
#define NAND_PAGE_NUM_PER_BLOCK 256 |
|
#define NAND_SECTOR_PER_PAGE 16 |
|
/* NAND_OOB_SIZE is not accurate, OOB size can be found from ubi_device->mtd->oobsize */ |
|
#define NAND_OOB_SIZE (NAND_SECTOR_PER_PAGE * 8) |
|
#define NAND_BLOCK_SIZE (NAND_PAGE_SIZE * NAND_PAGE_NUM_PER_BLOCK) |
|
#define NAND_TOTAL_PAGE_NUM (NAND_VOLUME / (NAND_PAGE_SIZE >> 10)) |
|
#define NAND_TOTAL_BLOCK_NUM (NAND_VOLUME / (NAND_BLOCK_SIZE >> 10)) |
|
|
|
#define FS_PAGE_SIZE (4 * 1024) |
|
#define FS_TOTAL_PAGE_NUM (NAND_VOLUME / (FS_PAGE_SIZE >> 10)) |
|
|
|
/* Page Mapping per nand page (must be power of 2)*/ |
|
#define PM_PER_NANDPAGE (NAND_PAGE_SIZE >> 2) |
|
/*#define MAX_OFFSET_PER_BLOCK (NAND_PAGE_SIZE * (NAND_PAGE_NUM_PER_BLOCK - 3))*/ |
|
|
|
#define PMT_ADDRESSES_PER_PAGE 4 |
|
#define PMT_INDICATOR_PAGE_SHIFT 10 |
|
#define PMT_INDICATOR_DIRTY_SHIFT 1 |
|
#define PMT_INDICATOR_CACHED_SHIFT 3 |
|
#define PMT_LEB_PAGE_INDICATOR_PAGE_SHIFT 12 |
|
#define PMT_PAGE_SHIFT 12 |
|
#define PMT_PART_SHIFT 12 |
|
#define PMT_DATACACHE_BUFF_NUM_SHIFT 4 |
|
#define PMT_TOTAL_CLUSTER_NUM (FS_TOTAL_PAGE_NUM / PM_PER_NANDPAGE) |
|
#define PMT_CACHE_NUM 4 /* Max: 7 */ |
|
#define PMT_BLOCK_NUM 3 |
|
|
|
#define CONFIG_START_BLOCK 0 |
|
#define REPLAY_BLOCK 2 |
|
#define PMT_START_BLOCK 3 |
|
#define DATA_START_BLOCK (PMT_START_BLOCK + PMT_BLOCK_NUM) |
|
#define MTKFTL_MAX_DATA_NUM_PER_PAGE (1 << PMT_PART_SHIFT) |
|
|
|
/**** MACROS ****/ |
|
|
|
/* mt_ftl error messages */ |
|
#define mt_ftl_err(dev, fmt, ...) pr_err("[MTK FTL][%s][0x%lx] " fmt "\n", \ |
|
__func__, (unsigned long int)dev, ##__VA_ARGS__) |
|
/*#define mt_ftl_err(fmt, ...) */ |
|
|
|
#define PMT_LEB_PAGE_INDICATOR_SET_BLOCKPAGE(p, blk, page) \ |
|
((p) = (((blk) << PMT_LEB_PAGE_INDICATOR_PAGE_SHIFT) | (page))) |
|
|
|
#define PMT_LEB_PAGE_INDICATOR_GET_BLOCK(p) ((p) >> PMT_LEB_PAGE_INDICATOR_PAGE_SHIFT) |
|
#define PMT_LEB_PAGE_INDICATOR_GET_PAGE(p) ((p) & ((1 << PMT_LEB_PAGE_INDICATOR_PAGE_SHIFT) - 1)) |
|
|
|
|
|
|
|
#define PMT_INDICATOR_SET_BLOCKPAGE(p, blk, page, dirty, cache_num) \ |
|
((p) = (((blk) << \ |
|
(PMT_INDICATOR_PAGE_SHIFT + PMT_INDICATOR_DIRTY_SHIFT + PMT_INDICATOR_CACHED_SHIFT)) \ |
|
| ((page) << (PMT_INDICATOR_DIRTY_SHIFT + PMT_INDICATOR_CACHED_SHIFT)) | \ |
|
((dirty) << PMT_INDICATOR_CACHED_SHIFT) | ((cache_num) + 1))) |
|
|
|
#define PMT_INDICATOR_GET_BLOCK(p) ((p) >> (PMT_INDICATOR_PAGE_SHIFT + PMT_INDICATOR_DIRTY_SHIFT \ |
|
+ PMT_INDICATOR_CACHED_SHIFT)) |
|
|
|
#define PMT_INDICATOR_GET_PAGE(p) (((p) >> (PMT_INDICATOR_DIRTY_SHIFT + PMT_INDICATOR_CACHED_SHIFT)) \ |
|
& ((1 << PMT_INDICATOR_PAGE_SHIFT) - 1)) |
|
|
|
#define PMT_INDICATOR_IS_INCACHE(p) ((p) & ((1 << PMT_INDICATOR_CACHED_SHIFT) - 1)) |
|
#define PMT_INDICATOR_CACHE_BUF_NUM(p) (((p) & ((1 << PMT_INDICATOR_CACHED_SHIFT) - 1)) - 1) |
|
#define PMT_INDICATOR_IS_DIRTY(p) (((p) & (1 << PMT_INDICATOR_CACHED_SHIFT)) \ |
|
>> PMT_INDICATOR_CACHED_SHIFT) |
|
#define PMT_INDICATOR_SET_CACHE_BUF_NUM(p, num) ((p) = (((p) & (~((1 << PMT_INDICATOR_CACHED_SHIFT) - 1))) \ |
|
| ((num) + 1))) |
|
#define PMT_INDICATOR_SET_DIRTY(p) ((p) |= (1 << PMT_INDICATOR_CACHED_SHIFT)) |
|
#define PMT_INDICATOR_RESET_INCACHE(p) ((p) &= (~((1 << PMT_INDICATOR_CACHED_SHIFT) - 1))) |
|
#define PMT_INDICATOR_RESET_DIRTY(p) ((p) &= (~(1 << PMT_INDICATOR_CACHED_SHIFT))) |
|
|
|
/*#define PMT_SET_BLOCKPAGE(p, blk, page, part, cache_num) \ |
|
((p) = (((blk) << (PMT_PAGE_SHIFT + PMT_PART_SHIFT + PMT_DATACACHE_BUFF_NUM_SHIFT)) \ |
|
| ((page) << (PMT_PART_SHIFT + PMT_DATACACHE_BUFF_NUM_SHIFT)) \ |
|
| ((part) << PMT_DATACACHE_BUFF_NUM_SHIFT) | ((cache_num) + 1)))*/ |
|
#define PMT_SET_BLOCKPAGE(p, blk, page) \ |
|
((p) = (((blk) << PMT_PAGE_SHIFT) | (page))) |
|
|
|
#define META_PMT_SET_DATA(p, data_size, part, cache_num) \ |
|
((p) = (((data_size) << (PMT_PART_SHIFT + PMT_DATACACHE_BUFF_NUM_SHIFT)) \ |
|
| ((part) << PMT_DATACACHE_BUFF_NUM_SHIFT) \ |
|
| ((cache_num) + 1))) |
|
|
|
#define PMT_GET_BLOCK(p) ((p) >> PMT_PAGE_SHIFT) |
|
#define PMT_GET_PAGE(p) ((p) & ((1 << PMT_PAGE_SHIFT) - 1)) |
|
#define PMT_GET_DATASIZE(p) ((p) >> (PMT_PAGE_SHIFT + PMT_DATACACHE_BUFF_NUM_SHIFT)) |
|
#define PMT_GET_PART(p) (((p) >> PMT_DATACACHE_BUFF_NUM_SHIFT) & ((1 << PMT_PART_SHIFT) - 1)) |
|
#define PMT_GET_DATACACHENUM(p) (((p) & ((1 << PMT_DATACACHE_BUFF_NUM_SHIFT) - 1)) - 1) |
|
#define PMT_SET_DATACACHE_BUF_NUM(p, num) ((p) = (((p) & (~((1 << PMT_DATACACHE_BUFF_NUM_SHIFT) - 1))) \ |
|
| ((num) + 1))) |
|
#define PMT_IS_DATA_INCACHE(p) ((p) & ((1 << PMT_DATACACHE_BUFF_NUM_SHIFT) - 1)) |
|
#define PMT_RESET_DATA_INCACHE(p) ((p) &= (~((1 << PMT_DATACACHE_BUFF_NUM_SHIFT) - 1))) |
|
|
|
/* |
|
//#define BIT_UPDATE(p) ((p) += NAND_PAGE_SIZE) |
|
//#define BIT_UPDATE_FSPAGE(p) ((p) += FS_PAGE_SIZE) |
|
*/ |
|
#define BIT_UPDATE(p, size) ((p) += (size)) |
|
#define PAGE_GET_DATA_NUM(p) ((p) & 0x7FFFFFFF) |
|
#define PAGE_BEEN_READ(p) ((p) & 0x80000000) |
|
#define PAGE_SET_READ(p) ((p) |= 0x80000000) |
|
|
|
struct mt_ftl_data_header { |
|
sector_t sector; |
|
unsigned int offset_len; /* offset(2bytes):length(2bytes) */ |
|
}; |
|
|
|
struct mt_ftl_param { |
|
/* Indicate next used replay page. */ |
|
unsigned int u4NextReplayOffsetIndicator; /* 4 bytes */ |
|
|
|
/* Indicate next used leb/page. leb(20bits):Page(12bits) */ |
|
unsigned int u4NextLebPageIndicator; /* 4 bytes */ |
|
|
|
/* Indicate current used leb/page for PMT indicator. leb(20bits):Page(12bits) */ |
|
/* Leb From 3 to PMT_BLOCK_NUM + 3 - 1 */ |
|
unsigned int u4CurrentPMTLebPageIndicator; /* 4 bytes */ |
|
|
|
/* Indicate next free Leb or invalid number for no free leb. |
|
* It is at most desc->vol->ubi->volumes[0]->reserved_pebs - 1 */ /* TODO: volume number need to change */ |
|
unsigned int u4NextFreeLebIndicator; /* 4 bytes */ |
|
/* Leb From 3 to PMT_BLOCK_NUM + 3 - 1 */ |
|
unsigned int u4NextFreePMTLebIndicator; /* 4 bytes */ |
|
|
|
/* Reserved leb for Garbage Collection */ |
|
unsigned int u4GCReserveLeb; /* 4 bytes */ |
|
/* Leb From 3 to PMT_BLOCK_NUM + 3 - 1 */ |
|
unsigned int u4GCReservePMTLeb; /* 4 bytes */ |
|
|
|
/* Compressor handler */ |
|
struct crypto_comp *cc; |
|
|
|
/* Page Mapping Table Indicator, used to indicate PMT position in NAND. |
|
* (Block_num(18bits):Page_num(10bits):Dirty(1bit):CachedBuffer(3bits))*/ |
|
unsigned int *u4PMTIndicator; /* 2K bytes */ |
|
/* TODO: need to adjust and consider to use information from ubi */ |
|
|
|
/* Page Mapping Table in cache. */ |
|
/* Block_num(20bits):Page_num(12bits) */ |
|
unsigned int *u4PMTCache; /* 16K * 4 */ |
|
/* Data_size(16bits):Part_num(12bits):CachedBuffer(4bits) */ |
|
unsigned int *u4MetaPMTCache; /* 16K * 4 */ |
|
int *i4CurrentPMTClusterInCache; |
|
unsigned int *u4ReadPMTCache; /* 16K */ |
|
unsigned int *u4ReadMetaPMTCache; /* 16K */ |
|
int i4CurrentReadPMTClusterInCache; |
|
|
|
/* Block Invalid Table. Store invalid page amount of a block |
|
* Unit of content: bytes */ |
|
unsigned int *u4BIT; /* 8K */ |
|
|
|
/* Block Data Cache Buffer, collect data and store to NAND flash when it is full */ |
|
unsigned char *u1DataCache; /* 16K */ |
|
|
|
/* Data information, including address (4bytes), page offset (2bytes) and data length (2bytes) */ |
|
struct mt_ftl_data_header *u4Header; |
|
struct mt_ftl_data_header *u4ReadHeader; |
|
unsigned int u4DataNum; |
|
|
|
/* Replay Block Record */ |
|
unsigned int *replay_blk_rec; |
|
int replay_blk_index; |
|
|
|
/* Page buffers */ |
|
unsigned int *general_page_buffer; /* 16K */ |
|
unsigned int *replay_page_buffer; /* 16K */ |
|
unsigned int *commit_page_buffer; /* 16K */ |
|
unsigned int *gc_page_buffer; /* 16K */ |
|
unsigned char *cmpr_page_buffer; /* 16K */ |
|
unsigned int *tmp_page_buffer; /* 16K */ |
|
}; |
|
|
|
struct mt_ftl_blk { |
|
struct ubi_volume_desc *desc; |
|
struct mt_ftl_param *param; |
|
int ubi_num; |
|
int vol_id; |
|
int refcnt; |
|
int leb_size; |
|
|
|
struct gendisk *gd; |
|
struct request_queue *rq; |
|
|
|
struct workqueue_struct *wq; |
|
struct work_struct work; |
|
|
|
struct mutex dev_mutex; |
|
spinlock_t queue_lock; |
|
struct list_head list; |
|
|
|
enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; |
|
void *cache; |
|
int cache_leb_num; |
|
int sync; |
|
}; |
|
|
|
#ifdef PROFILE |
|
enum { |
|
MT_FTL_PROFILE_WRITE_ALL, |
|
MT_FTL_PROFILE_WRITE_COPYTOCACHE, |
|
MT_FTL_PROFILE_WRITE_UPDATEPMT, |
|
MT_FTL_PROFILE_UPDATE_PMT_MODIFYPMT, |
|
MT_FTL_PROFILE_UPDATE_PMT_FINDCACHE_COMMITPMT, |
|
MT_FTL_PROFILE_COMMIT_PMT, |
|
MT_FTL_PROFILE_UPDATE_PMT_DOWNLOADPMT, |
|
MT_FTL_PROFILE_WRITE_COMPRESS, |
|
MT_FTL_PROFILE_WRITE_WRITEPAGE, |
|
MT_FTL_PROFILE_WRITE_PAGE_WRITEOOB, |
|
MT_FTL_PROFILE_WRITE_PAGE_GETFREEBLK, |
|
MT_FTL_PROFILE_GETFREEBLOCK_GETLEB, |
|
MT_FTL_PROFILE_GC_FINDBLK, |
|
MT_FTL_PROFILE_GC_CPVALID, |
|
MT_FTL_PROFILE_GC_DATA_READOOB, |
|
MT_FTL_PROFILE_GC_DATA_READ_UPDATE_PMT, |
|
MT_FTL_PROFILE_GC_DATA_WRITEOOB, |
|
MT_FTL_PROFILE_GC_REMAP, |
|
MT_FTL_PROFILE_GETFREEBLOCK_PUTREPLAY_COMMIT, |
|
MT_FTL_PROFILE_COMMIT, |
|
MT_FTL_PROFILE_WRITE_PAGE_RESET, |
|
MT_FTL_PROFILE_READ_ALL, |
|
MT_FTL_PROFILE_READ_GETPMT, |
|
MT_FTL_PROFILE_READ_DATATOCACHE, |
|
MT_FTL_PROFILE_READ_DATATOCACHE_TEST1, |
|
MT_FTL_PROFILE_READ_DATATOCACHE_TEST2, |
|
MT_FTL_PROFILE_READ_DATATOCACHE_TEST3, |
|
MT_FTL_PROFILE_READ_ADDRNOMATCH, |
|
MT_FTL_PROFILE_READ_DECOMP, |
|
MT_FTL_PROFILE_READ_COPYTOBUFF, |
|
MT_FTL_PROFILE_TOTAL_PROFILE_NUM |
|
}; |
|
|
|
static char *mtk_ftl_profile_message[MT_FTL_PROFILE_TOTAL_PROFILE_NUM] = { |
|
"MT_FTL_PROFILE_WRITE_ALL", |
|
"MT_FTL_PROFILE_WRITE_COPYTOCACHE", |
|
"MT_FTL_PROFILE_WRITE_UPDATEPMT", |
|
"MT_FTL_PROFILE_UPDATE_PMT_MODIFYPMT", |
|
"MT_FTL_PROFILE_UPDATE_PMT_FINDCACHE_COMMITPMT", |
|
"MT_FTL_PROFILE_COMMIT_PMT", |
|
"MT_FTL_PROFILE_UPDATE_PMT_DOWNLOADPMT", |
|
"MT_FTL_PROFILE_WRITE_COMPRESS", |
|
"MT_FTL_PROFILE_WRITE_WRITEPAGE", |
|
"MT_FTL_PROFILE_WRITE_PAGE_WRITEOOB", |
|
"MT_FTL_PROFILE_WRITE_PAGE_GETFREEBLK", |
|
"MT_FTL_PROFILE_GETFREEBLOCK_GETLEB", |
|
"MT_FTL_PROFILE_GC_FINDBLK", |
|
"MT_FTL_PROFILE_GC_CPVALID", |
|
"MT_FTL_PROFILE_GC_DATA_READOOB", |
|
"MT_FTL_PROFILE_GC_DATA_READ_UPDATE_PMT", |
|
"MT_FTL_PROFILE_GC_DATA_WRITEOOB", |
|
"MT_FTL_PROFILE_GC_REMAP", |
|
"MT_FTL_PROFILE_GETFREEBLOCK_PUTREPLAY_COMMIT", |
|
"MT_FTL_PROFILE_COMMIT", |
|
"MT_FTL_PROFILE_WRITE_PAGE_RESET", |
|
"MT_FTL_PROFILE_READ_ALL", |
|
"MT_FTL_PROFILE_READ_GETPMT", |
|
"MT_FTL_PROFILE_READ_DATATOCACHE", |
|
"MT_FTL_PROFILE_READ_DATATOCACHE_TEST1", |
|
"MT_FTL_PROFILE_READ_DATATOCACHE_TEST2", |
|
"MT_FTL_PROFILE_READ_DATATOCACHE_TEST3", |
|
"MT_FTL_PROFILE_READ_ADDRNOMATCH", |
|
"MT_FTL_PROFILE_READ_DECOMP", |
|
"MT_FTL_PROFILE_READ_COPYTOBUFF", |
|
}; |
|
#endif /* PROFILE */ |
|
|
|
|
|
int mt_ftl_create(struct mt_ftl_blk *dev); |
|
int mt_ftl_remove(struct mt_ftl_blk *dev); |
|
int mt_ftl_write(struct mt_ftl_blk *dev, const char *buffer, sector_t address, int len); |
|
int mt_ftl_read(struct mt_ftl_blk *dev, const char *buffer, sector_t address, int len); |
|
int mt_ftl_commit(struct mt_ftl_blk *dev); |
|
|
|
int mt_ftl_blk_create(struct ubi_volume_desc *desc); |
|
int mt_ftl_blk_remove(struct ubi_volume_info *vi); |
|
int mt_ftl_discard(struct mt_ftl_blk *dev, unsigned long sector, unsigned nr_sects); |
|
|
|
|
|
int mt_ftl_updatePMT(struct mt_ftl_blk *dev, int cluster, int sec_offset, int leb, int offset, int part, |
|
unsigned int cmpr_data_size, bool isReplay, bool isCommitDataCache); /* Temporary */ |
|
int mt_ftl_commit_indicators(struct mt_ftl_blk *dev); /* Temporary */ |
|
int mt_ftl_commitPMT(struct mt_ftl_blk *dev, bool isReplay, bool isCommitDataCache); /* Temporary */ |
|
int mt_ftl_commit(struct mt_ftl_blk *dev); /* Temporary */ |
|
|
|
#endif /* !__LINUX_MTFTL_H__ */
|
|
|