Commit ee9745fc authored by Kyungmin Park's avatar Kyungmin Park Committed by David Woodhouse

[MTD] [OneNAND] 2X program support

The 2X Program is an extension of Program Operation.

Since the device is equipped with two DataRAMs, and two-plane NAND Flash 
memory array, these two component enables simultaneous program of 4KiB.
Plane1 has only even blocks such as block0, block2, block4 while Plane2 
has only odd blocks such as block1, block3, block5.
So MTD regards it as 4KiB page size and 256KiB block size

Now the following chips support it. (KFXXX16Q2M)
Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M,
Mux:   KFM2G16Q2M, KFN4G16Q2M,

And more recent chips
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 1bddb9a8
...@@ -40,4 +40,20 @@ config MTD_ONENAND_OTP ...@@ -40,4 +40,20 @@ config MTD_ONENAND_OTP
OTP block is fully-guaranteed to be a valid block. OTP block is fully-guaranteed to be a valid block.
config MTD_ONENAND_2X_PROGRAM
bool "OneNAND 2X program support"
help
The 2X Program is an extension of Program Operation.
Since the device is equipped with two DataRAMs, and two-plane NAND
Flash memory array, these two component enables simultaneous program
of 4KiB. Plane1 has only even blocks such as block0, block2, block4
while Plane2 has only odd blocks such as block1, block3, block5.
So MTD regards it as 4KiB page size and 256KiB block size
Now the following chips support it. (KFXXX16Q2M)
Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M,
Mux: KFM2G16Q2M, KFN4G16Q2M,
And more recent chips
endif # MTD_ONENAND endif # MTD_ONENAND
...@@ -206,6 +206,15 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -206,6 +206,15 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
default: default:
block = (int) (addr >> this->erase_shift); block = (int) (addr >> this->erase_shift);
page = (int) (addr >> this->page_shift); page = (int) (addr >> this->page_shift);
if (ONENAND_IS_2PLANE(this)) {
/* Make the even block number */
block &= ~1;
/* Is it the odd plane? */
if (addr & this->writesize)
block++;
page >>= 1;
}
page &= this->page_mask; page &= this->page_mask;
break; break;
} }
...@@ -216,6 +225,10 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -216,6 +225,10 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
value = onenand_bufferram_address(this, block); value = onenand_bufferram_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
if (ONENAND_IS_2PLANE(this))
/* It is always BufferRAM0 */
ONENAND_SET_BUFFERRAM0(this);
else
/* Switch to the next data buffer */ /* Switch to the next data buffer */
ONENAND_SET_NEXT_BUFFERRAM(this); ONENAND_SET_NEXT_BUFFERRAM(this);
...@@ -247,6 +260,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -247,6 +260,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
break; break;
default: default:
if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
cmd = ONENAND_CMD_2X_PROG;
dataram = ONENAND_CURRENT_BUFFERRAM(this); dataram = ONENAND_CURRENT_BUFFERRAM(this);
break; break;
} }
...@@ -445,8 +460,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) ...@@ -445,8 +460,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
if (ONENAND_CURRENT_BUFFERRAM(this)) { if (ONENAND_CURRENT_BUFFERRAM(this)) {
/* Note: the 'this->writesize' is a real page size */
if (area == ONENAND_DATARAM) if (area == ONENAND_DATARAM)
return mtd->writesize; return this->writesize;
if (area == ONENAND_SPARERAM) if (area == ONENAND_SPARERAM)
return mtd->oobsize; return mtd->oobsize;
} }
...@@ -571,6 +587,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, ...@@ -571,6 +587,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
return 0; return 0;
} }
/**
* onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
* @param mtd MTD data structure
* @param addr address to check
* @return blockpage address
*
* Get blockpage address at 2x program mode
*/
static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
{
struct onenand_chip *this = mtd->priv;
int blockpage, block, page;
/* Calculate the even block number */
block = (int) (addr >> this->erase_shift) & ~1;
/* Is it the odd plane? */
if (addr & this->writesize)
block++;
page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
blockpage = (block << 7) | page;
return blockpage;
}
/** /**
* onenand_check_bufferram - [GENERIC] Check BufferRAM information * onenand_check_bufferram - [GENERIC] Check BufferRAM information
* @param mtd MTD data structure * @param mtd MTD data structure
...@@ -585,6 +625,9 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) ...@@ -585,6 +625,9 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
int blockpage, found = 0; int blockpage, found = 0;
unsigned int i; unsigned int i;
if (ONENAND_IS_2PLANE(this))
blockpage = onenand_get_2x_blockpage(mtd, addr);
else
blockpage = (int) (addr >> this->page_shift); blockpage = (int) (addr >> this->page_shift);
/* Is there valid data? */ /* Is there valid data? */
...@@ -625,6 +668,9 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, ...@@ -625,6 +668,9 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
int blockpage; int blockpage;
unsigned int i; unsigned int i;
if (ONENAND_IS_2PLANE(this))
blockpage = onenand_get_2x_blockpage(mtd, addr);
else
blockpage = (int) (addr >> this->page_shift); blockpage = (int) (addr >> this->page_shift);
/* Invalidate another BufferRAM */ /* Invalidate another BufferRAM */
...@@ -734,6 +780,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -734,6 +780,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
int read = 0, column; int read = 0, column;
int thislen; int thislen;
int ret = 0, boundary = 0; int ret = 0, boundary = 0;
int writesize = this->writesize;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
...@@ -754,22 +801,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -754,22 +801,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
/* Do first load to bufferRAM */ /* Do first load to bufferRAM */
if (read < len) { if (read < len) {
if (!onenand_check_bufferram(mtd, from)) { if (!onenand_check_bufferram(mtd, from)) {
this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); this->command(mtd, ONENAND_CMD_READ, from, writesize);
ret = this->wait(mtd, FL_READING); ret = this->wait(mtd, FL_READING);
onenand_update_bufferram(mtd, from, !ret); onenand_update_bufferram(mtd, from, !ret);
} }
} }
thislen = min_t(int, mtd->writesize, len - read); thislen = min_t(int, writesize, len - read);
column = from & (mtd->writesize - 1); column = from & (writesize - 1);
if (column + thislen > mtd->writesize) if (column + thislen > writesize)
thislen = mtd->writesize - column; thislen = writesize - column;
while (!ret) { while (!ret) {
/* If there is more to load then start next load */ /* If there is more to load then start next load */
from += thislen; from += thislen;
if (read + thislen < len) { if (read + thislen < len) {
this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); this->command(mtd, ONENAND_CMD_READ, from, writesize);
/* /*
* Chip boundary handling in DDP * Chip boundary handling in DDP
* Now we issued chip 1 read and pointed chip 1 * Now we issued chip 1 read and pointed chip 1
...@@ -794,7 +841,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -794,7 +841,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
ONENAND_SET_NEXT_BUFFERRAM(this); ONENAND_SET_NEXT_BUFFERRAM(this);
buf += thislen; buf += thislen;
thislen = min_t(int, mtd->writesize, len - read); thislen = min_t(int, writesize, len - read);
column = 0; column = 0;
cond_resched(); cond_resched();
/* Now wait for load */ /* Now wait for load */
...@@ -1079,7 +1126,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -1079,7 +1126,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
/* Read more? */ /* Read more? */
if (read < len) { if (read < len) {
/* Update Page size */ /* Update Page size */
from += mtd->writesize; from += this->writesize;
column = 0; column = 0;
} }
} }
...@@ -1135,12 +1182,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, ...@@ -1135,12 +1182,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
int thislen, column; int thislen, column;
while (len != 0) { while (len != 0) {
thislen = min_t(int, mtd->writesize, len); thislen = min_t(int, this->writesize, len);
column = addr & (mtd->writesize - 1); column = addr & (this->writesize - 1);
if (column + thislen > mtd->writesize) if (column + thislen > this->writesize)
thislen = mtd->writesize - column; thislen = this->writesize - column;
this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
onenand_update_bufferram(mtd, addr, 0); onenand_update_bufferram(mtd, addr, 0);
...@@ -1236,6 +1283,10 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -1236,6 +1283,10 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
/* In partial page write we don't update bufferram */ /* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, to, !ret && !subpage); onenand_update_bufferram(mtd, to, !ret && !subpage);
if (ONENAND_IS_2PLANE(this)) {
ONENAND_SET_BUFFERRAM1(this);
onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
}
if (ret) { if (ret) {
printk(KERN_ERR "onenand_write: write filaed %d\n", ret); printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
...@@ -1384,6 +1435,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -1384,6 +1435,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0); onenand_update_bufferram(mtd, to, 0);
if (ONENAND_IS_2PLANE(this)) {
ONENAND_SET_BUFFERRAM1(this);
onenand_update_bufferram(mtd, to + this->writesize, 0);
}
ret = this->wait(mtd, FL_WRITING); ret = this->wait(mtd, FL_WRITING);
if (ret) { if (ret) {
...@@ -2107,6 +2162,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, ...@@ -2107,6 +2162,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
* *
* Check and set OneNAND features * Check and set OneNAND features
* - lock scheme * - lock scheme
* - two plane
*/ */
static void onenand_check_features(struct mtd_info *mtd) static void onenand_check_features(struct mtd_info *mtd)
{ {
...@@ -2118,19 +2174,35 @@ static void onenand_check_features(struct mtd_info *mtd) ...@@ -2118,19 +2174,35 @@ static void onenand_check_features(struct mtd_info *mtd)
process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
/* Lock scheme */ /* Lock scheme */
if (density >= ONENAND_DEVICE_DENSITY_1Gb) { switch (density) {
case ONENAND_DEVICE_DENSITY_4Gb:
this->options |= ONENAND_HAS_2PLANE;
case ONENAND_DEVICE_DENSITY_2Gb:
/* 2Gb DDP don't have 2 plane */
if (!ONENAND_IS_DDP(this))
this->options |= ONENAND_HAS_2PLANE;
this->options |= ONENAND_HAS_UNLOCK_ALL;
case ONENAND_DEVICE_DENSITY_1Gb:
/* A-Die has all block unlock */ /* A-Die has all block unlock */
if (process) { if (process)
printk(KERN_DEBUG "Chip support all block unlock\n");
this->options |= ONENAND_HAS_UNLOCK_ALL; this->options |= ONENAND_HAS_UNLOCK_ALL;
} break;
} else {
/* Some OneNAND has continues lock scheme */ default:
if (!process) { /* Some OneNAND has continuous lock scheme */
printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); if (!process)
this->options |= ONENAND_HAS_CONT_LOCK; this->options |= ONENAND_HAS_CONT_LOCK;
break;
} }
}
if (this->options & ONENAND_HAS_CONT_LOCK)
printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
if (this->options & ONENAND_HAS_UNLOCK_ALL)
printk(KERN_DEBUG "Chip support all block unlock\n");
if (this->options & ONENAND_HAS_2PLANE)
printk(KERN_DEBUG "Chip has 2 plane\n");
} }
/** /**
...@@ -2257,6 +2329,8 @@ static int onenand_probe(struct mtd_info *mtd) ...@@ -2257,6 +2329,8 @@ static int onenand_probe(struct mtd_info *mtd)
this->erase_shift = ffs(mtd->erasesize) - 1; this->erase_shift = ffs(mtd->erasesize) - 1;
this->page_shift = ffs(mtd->writesize) - 1; this->page_shift = ffs(mtd->writesize) - 1;
this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
/* It's real page size */
this->writesize = mtd->writesize;
/* REVIST: Multichip handling */ /* REVIST: Multichip handling */
...@@ -2265,6 +2339,17 @@ static int onenand_probe(struct mtd_info *mtd) ...@@ -2265,6 +2339,17 @@ static int onenand_probe(struct mtd_info *mtd)
/* Check OneNAND features */ /* Check OneNAND features */
onenand_check_features(mtd); onenand_check_features(mtd);
/*
* We emulate the 4KiB page and 256KiB erase block size
* But oobsize is still 64 bytes.
* It is only valid if you turn on 2X program support,
* Otherwise it will be ignored by compiler.
*/
if (ONENAND_IS_2PLANE(this)) {
mtd->writesize <<= 1;
mtd->erasesize <<= 1;
}
return 0; return 0;
} }
......
...@@ -60,6 +60,7 @@ struct onenand_bufferram { ...@@ -60,6 +60,7 @@ struct onenand_bufferram {
* @erase_shift: [INTERN] number of address bits in a block * @erase_shift: [INTERN] number of address bits in a block
* @page_shift: [INTERN] number of address bits in a page * @page_shift: [INTERN] number of address bits in a page
* @page_mask: [INTERN] a page per block mask * @page_mask: [INTERN] a page per block mask
* @writesize: [INTERN] a real page size
* @bufferram_index: [INTERN] BufferRAM index * @bufferram_index: [INTERN] BufferRAM index
* @bufferram: [INTERN] BufferRAM info * @bufferram: [INTERN] BufferRAM info
* @readw: [REPLACEABLE] hardware specific function for read short * @readw: [REPLACEABLE] hardware specific function for read short
...@@ -100,6 +101,7 @@ struct onenand_chip { ...@@ -100,6 +101,7 @@ struct onenand_chip {
unsigned int erase_shift; unsigned int erase_shift;
unsigned int page_shift; unsigned int page_shift;
unsigned int page_mask; unsigned int page_mask;
unsigned int writesize;
unsigned int bufferram_index; unsigned int bufferram_index;
struct onenand_bufferram bufferram[MAX_BUFFERRAM]; struct onenand_bufferram bufferram[MAX_BUFFERRAM];
...@@ -140,6 +142,8 @@ struct onenand_chip { ...@@ -140,6 +142,8 @@ struct onenand_chip {
#define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) #define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1)
#define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1) #define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1)
#define ONENAND_SET_PREV_BUFFERRAM(this) (this->bufferram_index ^= 1) #define ONENAND_SET_PREV_BUFFERRAM(this) (this->bufferram_index ^= 1)
#define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0)
#define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
#define ONENAND_GET_SYS_CFG1(this) \ #define ONENAND_GET_SYS_CFG1(this) \
(this->read_word(this->base + ONENAND_REG_SYS_CFG1)) (this->read_word(this->base + ONENAND_REG_SYS_CFG1))
...@@ -149,6 +153,13 @@ struct onenand_chip { ...@@ -149,6 +153,13 @@ struct onenand_chip {
#define ONENAND_IS_DDP(this) \ #define ONENAND_IS_DDP(this) \
(this->device_id & ONENAND_DEVICE_IS_DDP) (this->device_id & ONENAND_DEVICE_IS_DDP)
#ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
#define ONENAND_IS_2PLANE(this) \
(this->options & ONENAND_HAS_2PLANE)
#else
#define ONENAND_IS_2PLANE(this) (0)
#endif
/* Check byte access in OneNAND */ /* Check byte access in OneNAND */
#define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1)
...@@ -157,6 +168,7 @@ struct onenand_chip { ...@@ -157,6 +168,7 @@ struct onenand_chip {
*/ */
#define ONENAND_HAS_CONT_LOCK (0x0001) #define ONENAND_HAS_CONT_LOCK (0x0001)
#define ONENAND_HAS_UNLOCK_ALL (0x0002) #define ONENAND_HAS_UNLOCK_ALL (0x0002)
#define ONENAND_HAS_2PLANE (0x0004)
#define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_PAGEBUF_ALLOC (0x1000)
#define ONENAND_OOBBUF_ALLOC (0x2000) #define ONENAND_OOBBUF_ALLOC (0x2000)
......
...@@ -74,6 +74,8 @@ ...@@ -74,6 +74,8 @@
#define ONENAND_DEVICE_DENSITY_512Mb (0x002) #define ONENAND_DEVICE_DENSITY_512Mb (0x002)
#define ONENAND_DEVICE_DENSITY_1Gb (0x003) #define ONENAND_DEVICE_DENSITY_1Gb (0x003)
#define ONENAND_DEVICE_DENSITY_2Gb (0x004)
#define ONENAND_DEVICE_DENSITY_4Gb (0x005)
/* /*
* Version ID Register F002h (R) * Version ID Register F002h (R)
...@@ -111,6 +113,8 @@ ...@@ -111,6 +113,8 @@
#define ONENAND_CMD_READOOB (0x13) #define ONENAND_CMD_READOOB (0x13)
#define ONENAND_CMD_PROG (0x80) #define ONENAND_CMD_PROG (0x80)
#define ONENAND_CMD_PROGOOB (0x1A) #define ONENAND_CMD_PROGOOB (0x1A)
#define ONENAND_CMD_2X_PROG (0x7D)
#define ONENAND_CMD_2X_CACHE_PROG (0x7F)
#define ONENAND_CMD_UNLOCK (0x23) #define ONENAND_CMD_UNLOCK (0x23)
#define ONENAND_CMD_LOCK (0x2A) #define ONENAND_CMD_LOCK (0x2A)
#define ONENAND_CMD_LOCK_TIGHT (0x2C) #define ONENAND_CMD_LOCK_TIGHT (0x2C)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment