Skip to content

Commit

Permalink
[MTD ONENAND] Check OneNAND lock scheme & all block unlock command su…
Browse files Browse the repository at this point in the history
…pport

OneNAND lock scheme depends on density and process of chip.
Some OneNAND chips support all block unlock

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
  • Loading branch information
Kyungmin Park authored and David Woodhouse committed Sep 26, 2006
1 parent 98638ee commit 28b79ff
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 22 deletions.
137 changes: 118 additions & 19 deletions drivers/mtd/onenand/onenand_base.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand All @@ -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[] = {
Expand Down Expand Up @@ -1625,8 +1731,7 @@ 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;

Expand Down Expand Up @@ -1657,14 +1762,16 @@ static int onenand_probe(struct mtd_info *mtd)
/* 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;
Expand All @@ -1687,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;
}
Expand Down Expand Up @@ -1832,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);
}
Expand Down
6 changes: 4 additions & 2 deletions include/linux/mtd/onenand.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)

/*
Expand Down
4 changes: 3 additions & 1 deletion include/linux/mtd/onenand_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 28b79ff

Please sign in to comment.