Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: [MTD] Cleanup of 'ioremap balanced with iounmap for drivers/mtd subsystem' [MTD] fix nftl_write warning [MTD] fix printk warning [MTD ONENAND] Check OneNAND lock scheme & all block unlock command support [MTD ONENAND] Remove unused MTD_ONENAND_SYNC_READ configuration [MTD ONENAND] Fix OneNAND probe [MTD NAND] Provide prototype for newly-exported nand_wait_ready() [MTD] Remove #ifndef __KERNEL__ hack in <mtd/mtd-abi.h> [MTD NAND] Allow override of page read and write functions. [MTD NAND] Allocate chip->buffers separately to allow it to be overridden [MTD NAND] Split nand_scan() into two parts; allow board driver to intervene [MTD NAND] Export nand_wait_ready() for use by board drivers
This commit is contained in:
commit
3e04767a46
17 changed files with 289 additions and 121 deletions
|
@ -96,7 +96,7 @@ static struct mtd_partition arctic_partitions[PARTITIONS] = {
|
|||
static int __init
|
||||
init_arctic_mtd(void)
|
||||
{
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
|
||||
|
||||
|
@ -112,7 +112,7 @@ init_arctic_mtd(void)
|
|||
arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map);
|
||||
|
||||
if (!arctic_mtd) {
|
||||
iounmap((void *) arctic_mtd_map.virt);
|
||||
iounmap(arctic_mtd_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ init_arctic_mtd(void)
|
|||
err = add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS);
|
||||
if (err) {
|
||||
printk("%s: add_mtd_partitions failed\n", NAME);
|
||||
iounmap((void *) arctic_mtd_map.virt);
|
||||
iounmap(arctic_mtd_map.virt);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
@ -72,7 +72,7 @@ static struct mtd_partition beech_partitions[2] = {
|
|||
static int __init
|
||||
init_beech_mtd(void)
|
||||
{
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
|
||||
|
||||
|
@ -89,7 +89,7 @@ init_beech_mtd(void)
|
|||
beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map);
|
||||
|
||||
if (!beech_mtd) {
|
||||
iounmap((void *) beech_mtd_map.virt);
|
||||
iounmap(beech_mtd_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ init_beech_mtd(void)
|
|||
err = add_mtd_partitions(beech_mtd, beech_partitions, 2);
|
||||
if (err) {
|
||||
printk("%s: add_mtd_partitions failed\n", NAME);
|
||||
iounmap((void *) beech_mtd_map.virt);
|
||||
iounmap(beech_mtd_map.virt);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
@ -175,8 +175,8 @@ int __init init_cstm_mips_ixx(void)
|
|||
printk(KERN_WARNING "Failed to ioremap\n");
|
||||
for (j = 0; j < i; j++) {
|
||||
if (cstm_mips_ixx_map[j].virt) {
|
||||
iounmap((void *)cstm_mips_ixx_map[j].virt);
|
||||
cstm_mips_ixx_map[j].virt = 0;
|
||||
iounmap(cstm_mips_ixx_map[j].virt);
|
||||
cstm_mips_ixx_map[j].virt = NULL;
|
||||
}
|
||||
}
|
||||
return -EIO;
|
||||
|
@ -214,8 +214,8 @@ int __init init_cstm_mips_ixx(void)
|
|||
else {
|
||||
for (i = 0; i < PHYSMAP_NUMBER; i++) {
|
||||
if (cstm_mips_ixx_map[i].virt) {
|
||||
iounmap((void *)cstm_mips_ixx_map[i].virt);
|
||||
cstm_mips_ixx_map[i].virt = 0;
|
||||
iounmap(cstm_mips_ixx_map[i].virt);
|
||||
cstm_mips_ixx_map[i].virt = NULL;
|
||||
}
|
||||
}
|
||||
return -ENXIO;
|
||||
|
|
|
@ -463,7 +463,7 @@ int __init nettel_init(void)
|
|||
|
||||
#ifdef CONFIG_MTD_CFI_INTELEXT
|
||||
out_unmap1:
|
||||
iounmap((void *) nettel_intel_map.virt);
|
||||
iounmap(nettel_intel_map.virt);
|
||||
#endif
|
||||
|
||||
out_unmap2:
|
||||
|
|
|
@ -126,7 +126,7 @@ static struct mtd_info *redwood_mtd;
|
|||
|
||||
int __init init_redwood_flash(void)
|
||||
{
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n",
|
||||
WINDOW_SIZE, WINDOW_ADDR);
|
||||
|
|
|
@ -69,7 +69,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
|
|||
return 1;
|
||||
|
||||
default:
|
||||
printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req));
|
||||
printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ static void __exit ep7312_cleanup(void)
|
|||
nand_release(ap7312_mtd);
|
||||
|
||||
/* Release io resource */
|
||||
iounmap((void *)this->IO_ADDR_R);
|
||||
iounmap(this->IO_ADDR_R);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree(ep7312_mtd);
|
||||
|
|
|
@ -415,7 +415,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
|
|||
* Wait for the ready pin, after a command
|
||||
* The timeout is catched later.
|
||||
*/
|
||||
static void nand_wait_ready(struct mtd_info *mtd)
|
||||
void nand_wait_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
unsigned long timeo = jiffies + 2;
|
||||
|
@ -429,6 +429,7 @@ static void nand_wait_ready(struct mtd_info *mtd)
|
|||
} while (time_before(jiffies, timeo));
|
||||
led_trigger_event(nand_led_trigger, LED_OFF);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_wait_ready);
|
||||
|
||||
/**
|
||||
* nand_command - [DEFAULT] Send command to NAND device
|
||||
|
@ -766,8 +767,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_calc = chip->buffers.ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers.ecccode;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
int *eccpos = chip->ecc.layout->eccpos;
|
||||
|
||||
nand_read_page_raw(mtd, chip, buf);
|
||||
|
@ -808,8 +809,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_calc = chip->buffers.ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers.ecccode;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
int *eccpos = chip->ecc.layout->eccpos;
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
|
@ -970,7 +971,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
page = realpage & chip->pagemask;
|
||||
|
||||
col = (int)(from & (mtd->writesize - 1));
|
||||
chip->oob_poi = chip->buffers.oobrbuf;
|
||||
chip->oob_poi = chip->buffers->oobrbuf;
|
||||
|
||||
buf = ops->datbuf;
|
||||
oob = ops->oobbuf;
|
||||
|
@ -981,7 +982,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/* Is the current page in the buffer ? */
|
||||
if (realpage != chip->pagebuf || oob) {
|
||||
bufpoi = aligned ? buf : chip->buffers.databuf;
|
||||
bufpoi = aligned ? buf : chip->buffers->databuf;
|
||||
|
||||
if (likely(sndcmd)) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||
|
@ -989,14 +990,17 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
}
|
||||
|
||||
/* Now read the page into the buffer */
|
||||
ret = chip->ecc.read_page(mtd, chip, bufpoi);
|
||||
if (unlikely(ops->mode == MTD_OOB_RAW))
|
||||
ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
|
||||
else
|
||||
ret = chip->ecc.read_page(mtd, chip, bufpoi);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/* Transfer not aligned data */
|
||||
if (!aligned) {
|
||||
chip->pagebuf = realpage;
|
||||
memcpy(buf, chip->buffers.databuf + col, bytes);
|
||||
memcpy(buf, chip->buffers->databuf + col, bytes);
|
||||
}
|
||||
|
||||
buf += bytes;
|
||||
|
@ -1023,7 +1027,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
nand_wait_ready(mtd);
|
||||
}
|
||||
} else {
|
||||
memcpy(buf, chip->buffers.databuf + col, bytes);
|
||||
memcpy(buf, chip->buffers->databuf + col, bytes);
|
||||
buf += bytes;
|
||||
}
|
||||
|
||||
|
@ -1266,7 +1270,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
realpage = (int)(from >> chip->page_shift);
|
||||
page = realpage & chip->pagemask;
|
||||
|
||||
chip->oob_poi = chip->buffers.oobrbuf;
|
||||
chip->oob_poi = chip->buffers->oobrbuf;
|
||||
|
||||
while(1) {
|
||||
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
|
||||
|
@ -1322,8 +1326,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf) = NULL;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
int ret = -ENOTSUPP;
|
||||
|
||||
|
@ -1341,12 +1343,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
switch(ops->mode) {
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_AUTO:
|
||||
break;
|
||||
|
||||
case MTD_OOB_RAW:
|
||||
/* Replace the read_page algorithm temporary */
|
||||
read_page = chip->ecc.read_page;
|
||||
chip->ecc.read_page = nand_read_page_raw;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1358,8 +1355,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
else
|
||||
ret = nand_do_read_ops(mtd, from, ops);
|
||||
|
||||
if (unlikely(ops->mode == MTD_OOB_RAW))
|
||||
chip->ecc.read_page = read_page;
|
||||
out:
|
||||
nand_release_device(mtd);
|
||||
return ret;
|
||||
|
@ -1391,7 +1386,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *ecc_calc = chip->buffers.ecccalc;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
const uint8_t *p = buf;
|
||||
int *eccpos = chip->ecc.layout->eccpos;
|
||||
|
||||
|
@ -1417,7 +1412,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *ecc_calc = chip->buffers.ecccalc;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
const uint8_t *p = buf;
|
||||
int *eccpos = chip->ecc.layout->eccpos;
|
||||
|
||||
|
@ -1478,7 +1473,7 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
|
|||
}
|
||||
|
||||
/**
|
||||
* nand_write_page - [INTERNAL] write one page
|
||||
* nand_write_page - [REPLACEABLE] write one page
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip descriptor
|
||||
* @buf: the data to write
|
||||
|
@ -1486,13 +1481,16 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
|
|||
* @cached: cached programming
|
||||
*/
|
||||
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached)
|
||||
const uint8_t *buf, int page, int cached, int raw)
|
||||
{
|
||||
int status;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
||||
|
||||
chip->ecc.write_page(mtd, chip, buf);
|
||||
if (unlikely(raw))
|
||||
chip->ecc.write_page_raw(mtd, chip, buf);
|
||||
else
|
||||
chip->ecc.write_page(mtd, chip, buf);
|
||||
|
||||
/*
|
||||
* Cached progamming disabled for now, Not sure if its worth the
|
||||
|
@ -1627,7 +1625,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|||
(chip->pagebuf << chip->page_shift) < (to + ops->len))
|
||||
chip->pagebuf = -1;
|
||||
|
||||
chip->oob_poi = chip->buffers.oobwbuf;
|
||||
chip->oob_poi = chip->buffers->oobwbuf;
|
||||
|
||||
while(1) {
|
||||
int cached = writelen > bytes && page != blockmask;
|
||||
|
@ -1635,7 +1633,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|||
if (unlikely(oob))
|
||||
oob = nand_fill_oob(chip, oob, ops);
|
||||
|
||||
ret = nand_write_page(mtd, chip, buf, page, cached);
|
||||
ret = chip->write_page(mtd, chip, buf, page, cached,
|
||||
(ops->mode == MTD_OOB_RAW));
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
@ -1745,7 +1744,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
if (page == chip->pagebuf)
|
||||
chip->pagebuf = -1;
|
||||
|
||||
chip->oob_poi = chip->buffers.oobwbuf;
|
||||
chip->oob_poi = chip->buffers->oobwbuf;
|
||||
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
||||
nand_fill_oob(chip, ops->oobbuf, ops);
|
||||
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
|
||||
|
@ -1768,8 +1767,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf) = NULL;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
int ret = -ENOTSUPP;
|
||||
|
||||
|
@ -1787,12 +1784,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
switch(ops->mode) {
|
||||
case MTD_OOB_PLACE:
|
||||
case MTD_OOB_AUTO:
|
||||
break;
|
||||
|
||||
case MTD_OOB_RAW:
|
||||
/* Replace the write_page algorithm temporary */
|
||||
write_page = chip->ecc.write_page;
|
||||
chip->ecc.write_page = nand_write_page_raw;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1804,8 +1796,6 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
else
|
||||
ret = nand_do_write_ops(mtd, to, ops);
|
||||
|
||||
if (unlikely(ops->mode == MTD_OOB_RAW))
|
||||
chip->ecc.write_page = write_page;
|
||||
out:
|
||||
nand_release_device(mtd);
|
||||
return ret;
|
||||
|
@ -2288,40 +2278,22 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|||
return type;
|
||||
}
|
||||
|
||||
/* module_text_address() isn't exported, and it's mostly a pointless
|
||||
test if this is a module _anyway_ -- they'd have to try _really_ hard
|
||||
to call us from in-kernel code if the core NAND support is modular. */
|
||||
#ifdef MODULE
|
||||
#define caller_is_module() (1)
|
||||
#else
|
||||
#define caller_is_module() \
|
||||
module_text_address((unsigned long)__builtin_return_address(0))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* nand_scan - [NAND Interface] Scan for the NAND device
|
||||
* @mtd: MTD device structure
|
||||
* @maxchips: Number of chips to scan for
|
||||
* nand_scan_ident - [NAND Interface] Scan for the NAND device
|
||||
* @mtd: MTD device structure
|
||||
* @maxchips: Number of chips to scan for
|
||||
*
|
||||
* This fills out all the uninitialized function pointers
|
||||
* with the defaults.
|
||||
* The flash ID is read and the mtd/chip structures are
|
||||
* filled with the appropriate values.
|
||||
* The mtd->owner field must be set to the module of the caller
|
||||
* This is the first phase of the normal nand_scan() function. It
|
||||
* reads the flash ID and sets up MTD fields accordingly.
|
||||
*
|
||||
* The mtd->owner field must be set to the module of the caller.
|
||||
*/
|
||||
int nand_scan(struct mtd_info *mtd, int maxchips)
|
||||
int nand_scan_ident(struct mtd_info *mtd, int maxchips)
|
||||
{
|
||||
int i, busw, nand_maf_id;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct nand_flash_dev *type;
|
||||
|
||||
/* Many callers got this wrong, so check for it for a while... */
|
||||
if (!mtd->owner && caller_is_module()) {
|
||||
printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Get buswidth to select the correct functions */
|
||||
busw = chip->options & NAND_BUSWIDTH_16;
|
||||
/* Set the default functions */
|
||||
|
@ -2353,8 +2325,31 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
|
|||
chip->numchips = i;
|
||||
mtd->size = i * chip->chipsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nand_scan_tail - [NAND Interface] Scan for the NAND device
|
||||
* @mtd: MTD device structure
|
||||
* @maxchips: Number of chips to scan for
|
||||
*
|
||||
* This is the second phase of the normal nand_scan() function. It
|
||||
* fills out all the uninitialized function pointers with the defaults
|
||||
* and scans for a bad block table if appropriate.
|
||||
*/
|
||||
int nand_scan_tail(struct mtd_info *mtd)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (!(chip->options & NAND_OWN_BUFFERS))
|
||||
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
|
||||
if (!chip->buffers)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Preset the internal oob write buffer */
|
||||
memset(chip->buffers.oobwbuf, 0xff, mtd->oobsize);
|
||||
memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize);
|
||||
|
||||
/*
|
||||
* If no default placement scheme is given, select an appropriate one
|
||||
|
@ -2377,10 +2372,18 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
|
|||
}
|
||||
}
|
||||
|
||||
if (!chip->write_page)
|
||||
chip->write_page = nand_write_page;
|
||||
|
||||
/*
|
||||
* check ECC mode, default to software if 3byte/512byte hardware ECC is
|
||||
* selected and we have 256 byte pagesize fallback to software ECC
|
||||
*/
|
||||
if (!chip->ecc.read_page_raw)
|
||||
chip->ecc.read_page_raw = nand_read_page_raw;
|
||||
if (!chip->ecc.write_page_raw)
|
||||
chip->ecc.write_page_raw = nand_write_page_raw;
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
/* Use standard hwecc read page function ? */
|
||||
|
@ -2438,6 +2441,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
|
|||
chip->ecc.size = mtd->writesize;
|
||||
chip->ecc.bytes = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
|
||||
chip->ecc.mode);
|
||||
|
@ -2503,6 +2507,44 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
|
|||
return chip->scan_bbt(mtd);
|
||||
}
|
||||
|
||||
/* module_text_address() isn't exported, and it's mostly a pointless
|
||||
test if this is a module _anyway_ -- they'd have to try _really_ hard
|
||||
to call us from in-kernel code if the core NAND support is modular. */
|
||||
#ifdef MODULE
|
||||
#define caller_is_module() (1)
|
||||
#else
|
||||
#define caller_is_module() \
|
||||
module_text_address((unsigned long)__builtin_return_address(0))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* nand_scan - [NAND Interface] Scan for the NAND device
|
||||
* @mtd: MTD device structure
|
||||
* @maxchips: Number of chips to scan for
|
||||
*
|
||||
* This fills out all the uninitialized function pointers
|
||||
* with the defaults.
|
||||
* The flash ID is read and the mtd/chip structures are
|
||||
* filled with the appropriate values.
|
||||
* The mtd->owner field must be set to the module of the caller
|
||||
*
|
||||
*/
|
||||
int nand_scan(struct mtd_info *mtd, int maxchips)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Many callers got this wrong, so check for it for a while... */
|
||||
if (!mtd->owner && caller_is_module()) {
|
||||
printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
ret = nand_scan_ident(mtd, maxchips);
|
||||
if (!ret)
|
||||
ret = nand_scan_tail(mtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_release - [NAND Interface] Free resources held by the NAND device
|
||||
* @mtd: MTD device structure
|
||||
|
@ -2520,9 +2562,13 @@ void nand_release(struct mtd_info *mtd)
|
|||
|
||||
/* Free bad block table memory */
|
||||
kfree(chip->bbt);
|
||||
if (!(chip->options & NAND_OWN_BUFFERS))
|
||||
kfree(chip->buffers);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(nand_scan);
|
||||
EXPORT_SYMBOL_GPL(nand_scan_ident);
|
||||
EXPORT_SYMBOL_GPL(nand_scan_tail);
|
||||
EXPORT_SYMBOL_GPL(nand_release);
|
||||
|
||||
static int __init nand_base_init(void)
|
||||
|
|
|
@ -759,7 +759,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
|
|||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
bd->options &= ~NAND_BBT_SCANEMPTY;
|
||||
return create_bbt(mtd, this->buffers.databuf, bd, -1);
|
||||
return create_bbt(mtd, this->buffers->databuf, bd, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -175,6 +175,8 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
|||
return res;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFTL_RW
|
||||
|
||||
/*
|
||||
* Write data and oob to flash
|
||||
*/
|
||||
|
@ -196,8 +198,6 @@ static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
|
|||
return res;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFTL_RW
|
||||
|
||||
/* Actual NFTL access routines */
|
||||
/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
|
||||
* when the give Virtual Unit Chain
|
||||
|
|
|
@ -43,10 +43,4 @@ config MTD_ONENAND_OTP
|
|||
|
||||
OTP block is fully-guaranteed to be a valid block.
|
||||
|
||||
config MTD_ONENAND_SYNC_READ
|
||||
bool "OneNAND Sync. Burst Read Support"
|
||||
depends on ARCH_OMAP
|
||||
help
|
||||
This enables support for Sync. Burst Read.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* linux/drivers/mtd/onenand/onenand_base.c
|
||||
*
|
||||
* Copyright (C) 2005 Samsung Electronics
|
||||
* Copyright (C) 2005-2006 Samsung Electronics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -199,6 +199,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
|
|||
case ONENAND_CMD_UNLOCK:
|
||||
case ONENAND_CMD_LOCK:
|
||||
case ONENAND_CMD_LOCK_TIGHT:
|
||||
case ONENAND_CMD_UNLOCK_ALL:
|
||||
block = -1;
|
||||
page = -1;
|
||||
break;
|
||||
|
@ -1211,11 +1212,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
end = len >> this->erase_shift;
|
||||
|
||||
/* Continuous lock scheme */
|
||||
if (this->options & ONENAND_CONT_LOCK) {
|
||||
if (this->options & ONENAND_HAS_CONT_LOCK) {
|
||||
/* Set start block address */
|
||||
this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
|
||||
/* Set end block address */
|
||||
this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
|
||||
this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
|
||||
/* Write unlock command */
|
||||
this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
|
||||
|
||||
|
@ -1236,7 +1237,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
}
|
||||
|
||||
/* Block lock scheme */
|
||||
for (block = start; block < end; block++) {
|
||||
for (block = start; block < start + end; block++) {
|
||||
/* Set block address */
|
||||
value = onenand_block_address(this, block);
|
||||
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
|
||||
|
@ -1265,6 +1266,79 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_check_lock_status - [OneNAND Interface] Check lock status
|
||||
* @param this onenand chip data structure
|
||||
*
|
||||
* Check lock status
|
||||
*/
|
||||
static void onenand_check_lock_status(struct onenand_chip *this)
|
||||
{
|
||||
unsigned int value, block, status;
|
||||
unsigned int end;
|
||||
|
||||
end = this->chipsize >> this->erase_shift;
|
||||
for (block = 0; block < end; block++) {
|
||||
/* Set block address */
|
||||
value = onenand_block_address(this, block);
|
||||
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
|
||||
/* Select DataRAM for DDP */
|
||||
value = onenand_bufferram_address(this, block);
|
||||
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
|
||||
/* Set start block address */
|
||||
this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
|
||||
|
||||
/* Check lock status */
|
||||
status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
|
||||
if (!(status & ONENAND_WP_US))
|
||||
printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_unlock_all - [OneNAND Interface] unlock all blocks
|
||||
* @param mtd MTD device structure
|
||||
*
|
||||
* Unlock all blocks
|
||||
*/
|
||||
static int onenand_unlock_all(struct mtd_info *mtd)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
|
||||
if (this->options & ONENAND_HAS_UNLOCK_ALL) {
|
||||
/* Write unlock command */
|
||||
this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
|
||||
|
||||
/* There's no return value */
|
||||
this->wait(mtd, FL_UNLOCKING);
|
||||
|
||||
/* Sanity check */
|
||||
while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
|
||||
& ONENAND_CTRL_ONGO)
|
||||
continue;
|
||||
|
||||
/* Workaround for all block unlock in DDP */
|
||||
if (this->device_id & ONENAND_DEVICE_IS_DDP) {
|
||||
loff_t ofs;
|
||||
size_t len;
|
||||
|
||||
/* 1st block on another chip */
|
||||
ofs = this->chipsize >> 1;
|
||||
len = 1 << this->erase_shift;
|
||||
|
||||
onenand_unlock(mtd, ofs, len);
|
||||
}
|
||||
|
||||
onenand_check_lock_status(this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
mtd->unlock(mtd, 0x0, this->chipsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_ONENAND_OTP
|
||||
|
||||
/* Interal OTP operation */
|
||||
|
@ -1563,13 +1637,44 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
}
|
||||
#endif /* CONFIG_MTD_ONENAND_OTP */
|
||||
|
||||
/**
|
||||
* onenand_lock_scheme - Check and set OneNAND lock scheme
|
||||
* @param mtd MTD data structure
|
||||
*
|
||||
* Check and set OneNAND lock scheme
|
||||
*/
|
||||
static void onenand_lock_scheme(struct mtd_info *mtd)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
unsigned int density, process;
|
||||
|
||||
/* Lock scheme depends on density and process */
|
||||
density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
|
||||
process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
|
||||
|
||||
/* Lock scheme */
|
||||
if (density >= ONENAND_DEVICE_DENSITY_1Gb) {
|
||||
/* A-Die has all block unlock */
|
||||
if (process) {
|
||||
printk(KERN_DEBUG "Chip support all block unlock\n");
|
||||
this->options |= ONENAND_HAS_UNLOCK_ALL;
|
||||
}
|
||||
} else {
|
||||
/* Some OneNAND has continues lock scheme */
|
||||
if (!process) {
|
||||
printk(KERN_DEBUG "Lock scheme is Continues Lock\n");
|
||||
this->options |= ONENAND_HAS_CONT_LOCK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_print_device_info - Print device ID
|
||||
* @param device device ID
|
||||
*
|
||||
* Print device ID
|
||||
*/
|
||||
static void onenand_print_device_info(int device)
|
||||
static void onenand_print_device_info(int device, int version)
|
||||
{
|
||||
int vcc, demuxed, ddp, density;
|
||||
|
||||
|
@ -1583,6 +1688,7 @@ static void onenand_print_device_info(int device)
|
|||
(16 << density),
|
||||
vcc ? "2.65/3.3" : "1.8",
|
||||
device);
|
||||
printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
|
||||
}
|
||||
|
||||
static const struct onenand_manufacturers onenand_manuf_ids[] = {
|
||||
|
@ -1625,9 +1731,14 @@ static int onenand_check_maf(int manuf)
|
|||
static int onenand_probe(struct mtd_info *mtd)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
int bram_maf_id, bram_dev_id, maf_id, dev_id;
|
||||
int version_id;
|
||||
int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
|
||||
int density;
|
||||
int syscfg;
|
||||
|
||||
/* Save system configuration 1 */
|
||||
syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
|
||||
/* Clear Sync. Burst Read mode to read BootRAM */
|
||||
this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
|
||||
|
||||
/* Send the command for reading device ID from BootRAM */
|
||||
this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
|
||||
|
@ -1636,24 +1747,31 @@ static int onenand_probe(struct mtd_info *mtd)
|
|||
bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
|
||||
bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
|
||||
|
||||
/* Reset OneNAND to read default register values */
|
||||
this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
|
||||
/* Wait reset */
|
||||
this->wait(mtd, FL_RESETING);
|
||||
|
||||
/* Restore system configuration 1 */
|
||||
this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
|
||||
|
||||
/* Check manufacturer ID */
|
||||
if (onenand_check_maf(bram_maf_id))
|
||||
return -ENXIO;
|
||||
|
||||
/* Reset OneNAND to read default register values */
|
||||
this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
|
||||
|
||||
/* Read manufacturer and device IDs from Register */
|
||||
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
|
||||
dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
|
||||
ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID);
|
||||
|
||||
/* Check OneNAND device */
|
||||
if (maf_id != bram_maf_id || dev_id != bram_dev_id)
|
||||
return -ENXIO;
|
||||
|
||||
/* Flash device information */
|
||||
onenand_print_device_info(dev_id);
|
||||
onenand_print_device_info(dev_id, ver_id);
|
||||
this->device_id = dev_id;
|
||||
this->version_id = ver_id;
|
||||
|
||||
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
|
||||
this->chipsize = (16 << density) << 20;
|
||||
|
@ -1676,16 +1794,8 @@ static int onenand_probe(struct mtd_info *mtd)
|
|||
|
||||
mtd->size = this->chipsize;
|
||||
|
||||
/* Version ID */
|
||||
version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
|
||||
printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id);
|
||||
|
||||
/* Lock scheme */
|
||||
if (density <= ONENAND_DEVICE_DENSITY_512Mb &&
|
||||
!(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) {
|
||||
printk(KERN_INFO "Lock scheme is Continues Lock\n");
|
||||
this->options |= ONENAND_CONT_LOCK;
|
||||
}
|
||||
/* Check OneNAND lock scheme */
|
||||
onenand_lock_scheme(mtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1821,7 +1931,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|||
mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Unlock whole block */
|
||||
mtd->unlock(mtd, 0x0, this->chipsize);
|
||||
onenand_unlock_all(mtd);
|
||||
|
||||
return this->scan_bbt(mtd);
|
||||
}
|
||||
|
|
|
@ -27,9 +27,17 @@
|
|||
struct mtd_info;
|
||||
/* Scan and identify a NAND device */
|
||||
extern int nand_scan (struct mtd_info *mtd, int max_chips);
|
||||
/* Separate phases of nand_scan(), allowing board driver to intervene
|
||||
* and override command or ECC setup according to flash type */
|
||||
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips);
|
||||
extern int nand_scan_tail(struct mtd_info *mtd);
|
||||
|
||||
/* Free resources held by the NAND device */
|
||||
extern void nand_release (struct mtd_info *mtd);
|
||||
|
||||
/* Internal helper for board drivers which need to override command function */
|
||||
extern void nand_wait_ready(struct mtd_info *mtd);
|
||||
|
||||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
|
||||
|
@ -178,7 +186,9 @@ typedef enum {
|
|||
#define NAND_USE_FLASH_BBT 0x00010000
|
||||
/* This option skips the bbt scan during initialization. */
|
||||
#define NAND_SKIP_BBTSCAN 0x00020000
|
||||
|
||||
/* This option is defined if the board driver allocates its own buffers
|
||||
(e.g. because it needs them DMA-coherent */
|
||||
#define NAND_OWN_BUFFERS 0x00040000
|
||||
/* Options set by nand scan */
|
||||
/* Nand scan has allocated controller struct */
|
||||
#define NAND_CONTROLLER_ALLOC 0x80000000
|
||||
|
@ -228,6 +238,8 @@ struct nand_hw_control {
|
|||
* be provided if an hardware ECC is available
|
||||
* @calculate: function for ecc calculation or readback from ecc hardware
|
||||
* @correct: function for ecc correction, matching to ecc generator (sw/hw)
|
||||
* @read_page_raw: function to read a raw page without ECC
|
||||
* @write_page_raw: function to write a raw page without ECC
|
||||
* @read_page: function to read a page according to the ecc generator requirements
|
||||
* @write_page: function to write a page according to the ecc generator requirements
|
||||
* @read_oob: function to read chip OOB data
|
||||
|
@ -249,6 +261,12 @@ struct nand_ecc_ctrl {
|
|||
int (*correct)(struct mtd_info *mtd, uint8_t *dat,
|
||||
uint8_t *read_ecc,
|
||||
uint8_t *calc_ecc);
|
||||
int (*read_page_raw)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf);
|
||||
void (*write_page_raw)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
int (*read_page)(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf);
|
||||
|
@ -337,6 +355,7 @@ struct nand_buffers {
|
|||
* @priv: [OPTIONAL] pointer to private chip date
|
||||
* @errstat: [OPTIONAL] hardware specific function to perform additional error status checks
|
||||
* (determine if errors are correctable)
|
||||
* @write_page [REPLACEABLE] High-level page write function
|
||||
*/
|
||||
|
||||
struct nand_chip {
|
||||
|
@ -359,6 +378,8 @@ struct nand_chip {
|
|||
void (*erase_cmd)(struct mtd_info *mtd, int page);
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached, int raw);
|
||||
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
|
@ -380,7 +401,7 @@ struct nand_chip {
|
|||
struct nand_ecclayout *ecclayout;
|
||||
|
||||
struct nand_ecc_ctrl ecc;
|
||||
struct nand_buffers buffers;
|
||||
struct nand_buffers *buffers;
|
||||
struct nand_hw_control hwcontrol;
|
||||
|
||||
struct mtd_oob_ops ops;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* linux/include/linux/mtd/onenand.h
|
||||
*
|
||||
* Copyright (C) 2005 Samsung Electronics
|
||||
* Copyright (C) 2005-2006 Samsung Electronics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -96,6 +96,7 @@ struct onenand_chip {
|
|||
void __iomem *base;
|
||||
unsigned int chipsize;
|
||||
unsigned int device_id;
|
||||
unsigned int version_id;
|
||||
unsigned int density_mask;
|
||||
unsigned int options;
|
||||
|
||||
|
@ -149,7 +150,8 @@ struct onenand_chip {
|
|||
/*
|
||||
* Options bits
|
||||
*/
|
||||
#define ONENAND_CONT_LOCK (0x0001)
|
||||
#define ONENAND_HAS_CONT_LOCK (0x0001)
|
||||
#define ONENAND_HAS_UNLOCK_ALL (0x0002)
|
||||
#define ONENAND_PAGEBUF_ALLOC (0x1000)
|
||||
|
||||
/*
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* OneNAND Register header file
|
||||
*
|
||||
* Copyright (C) 2005 Samsung Electronics
|
||||
* Copyright (C) 2005-2006 Samsung Electronics
|
||||
*
|
||||
* 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
|
||||
|
@ -72,6 +72,7 @@
|
|||
#define ONENAND_DEVICE_VCC_MASK (0x3)
|
||||
|
||||
#define ONENAND_DEVICE_DENSITY_512Mb (0x002)
|
||||
#define ONENAND_DEVICE_DENSITY_1Gb (0x003)
|
||||
|
||||
/*
|
||||
* Version ID Register F002h (R)
|
||||
|
@ -110,6 +111,7 @@
|
|||
#define ONENAND_CMD_UNLOCK (0x23)
|
||||
#define ONENAND_CMD_LOCK (0x2A)
|
||||
#define ONENAND_CMD_LOCK_TIGHT (0x2C)
|
||||
#define ONENAND_CMD_UNLOCK_ALL (0x27)
|
||||
#define ONENAND_CMD_ERASE (0x94)
|
||||
#define ONENAND_CMD_RESET (0xF0)
|
||||
#define ONENAND_CMD_OTP_ACCESS (0x65)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
header-y += inftl-user.h
|
||||
header-y += jffs2-user.h
|
||||
header-y += mtd-abi.h
|
||||
header-y += mtd-user.h
|
||||
header-y += nftl-user.h
|
||||
|
||||
unifdef-y += mtd-abi.h
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
#ifndef __MTD_ABI_H__
|
||||
#define __MTD_ABI_H__
|
||||
|
||||
#ifndef __KERNEL__
|
||||
/* Urgh. The whole point of splitting this out into
|
||||
separate files was to avoid #ifdef __KERNEL__ */
|
||||
#define __user
|
||||
#endif
|
||||
|
||||
struct erase_info_user {
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
|
|
Loading…
Reference in a new issue