[MTD ONENAND] Check OneNAND lock scheme & all block unlock command support
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>
This commit is contained in:
parent
98638ee2ed
commit
28b79ff966
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* linux/drivers/mtd/onenand/onenand_base.c
|
* linux/drivers/mtd/onenand/onenand_base.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005 Samsung Electronics
|
* Copyright (C) 2005-2006 Samsung Electronics
|
||||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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_UNLOCK:
|
||||||
case ONENAND_CMD_LOCK:
|
case ONENAND_CMD_LOCK:
|
||||||
case ONENAND_CMD_LOCK_TIGHT:
|
case ONENAND_CMD_LOCK_TIGHT:
|
||||||
|
case ONENAND_CMD_UNLOCK_ALL:
|
||||||
block = -1;
|
block = -1;
|
||||||
page = -1;
|
page = -1;
|
||||||
break;
|
break;
|
||||||
|
@ -1211,11 +1212,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
||||||
end = len >> this->erase_shift;
|
end = len >> this->erase_shift;
|
||||||
|
|
||||||
/* Continuous lock scheme */
|
/* Continuous lock scheme */
|
||||||
if (this->options & ONENAND_CONT_LOCK) {
|
if (this->options & ONENAND_HAS_CONT_LOCK) {
|
||||||
/* Set start block address */
|
/* Set start block address */
|
||||||
this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
|
this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
|
||||||
/* Set end 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 */
|
/* Write unlock command */
|
||||||
this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
|
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 */
|
/* Block lock scheme */
|
||||||
for (block = start; block < end; block++) {
|
for (block = start; block < start + end; block++) {
|
||||||
/* Set block address */
|
/* Set block address */
|
||||||
value = onenand_block_address(this, block);
|
value = onenand_block_address(this, block);
|
||||||
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
|
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;
|
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
|
#ifdef CONFIG_MTD_ONENAND_OTP
|
||||||
|
|
||||||
/* Interal OTP operation */
|
/* 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 */
|
#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
|
* onenand_print_device_info - Print device ID
|
||||||
* @param device device ID
|
* @param device device ID
|
||||||
*
|
*
|
||||||
* Print 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;
|
int vcc, demuxed, ddp, density;
|
||||||
|
|
||||||
|
@ -1583,6 +1688,7 @@ static void onenand_print_device_info(int device)
|
||||||
(16 << density),
|
(16 << density),
|
||||||
vcc ? "2.65/3.3" : "1.8",
|
vcc ? "2.65/3.3" : "1.8",
|
||||||
device);
|
device);
|
||||||
|
printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct onenand_manufacturers onenand_manuf_ids[] = {
|
static const struct onenand_manufacturers onenand_manuf_ids[] = {
|
||||||
|
@ -1625,8 +1731,7 @@ static int onenand_check_maf(int manuf)
|
||||||
static int onenand_probe(struct mtd_info *mtd)
|
static int onenand_probe(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct onenand_chip *this = mtd->priv;
|
struct onenand_chip *this = mtd->priv;
|
||||||
int bram_maf_id, bram_dev_id, maf_id, dev_id;
|
int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
|
||||||
int version_id;
|
|
||||||
int density;
|
int density;
|
||||||
int syscfg;
|
int syscfg;
|
||||||
|
|
||||||
|
@ -1657,14 +1762,16 @@ static int onenand_probe(struct mtd_info *mtd)
|
||||||
/* Read manufacturer and device IDs from Register */
|
/* Read manufacturer and device IDs from Register */
|
||||||
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
|
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
|
||||||
dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_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 */
|
/* Check OneNAND device */
|
||||||
if (maf_id != bram_maf_id || dev_id != bram_dev_id)
|
if (maf_id != bram_maf_id || dev_id != bram_dev_id)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
/* Flash device information */
|
/* Flash device information */
|
||||||
onenand_print_device_info(dev_id);
|
onenand_print_device_info(dev_id, ver_id);
|
||||||
this->device_id = dev_id;
|
this->device_id = dev_id;
|
||||||
|
this->version_id = ver_id;
|
||||||
|
|
||||||
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
|
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
|
||||||
this->chipsize = (16 << density) << 20;
|
this->chipsize = (16 << density) << 20;
|
||||||
|
@ -1687,16 +1794,8 @@ static int onenand_probe(struct mtd_info *mtd)
|
||||||
|
|
||||||
mtd->size = this->chipsize;
|
mtd->size = this->chipsize;
|
||||||
|
|
||||||
/* Version ID */
|
/* Check OneNAND lock scheme */
|
||||||
version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
|
onenand_lock_scheme(mtd);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1832,7 +1931,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->owner = THIS_MODULE;
|
||||||
|
|
||||||
/* Unlock whole block */
|
/* Unlock whole block */
|
||||||
mtd->unlock(mtd, 0x0, this->chipsize);
|
onenand_unlock_all(mtd);
|
||||||
|
|
||||||
return this->scan_bbt(mtd);
|
return this->scan_bbt(mtd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* linux/include/linux/mtd/onenand.h
|
* linux/include/linux/mtd/onenand.h
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005 Samsung Electronics
|
* Copyright (C) 2005-2006 Samsung Electronics
|
||||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -96,6 +96,7 @@ struct onenand_chip {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned int chipsize;
|
unsigned int chipsize;
|
||||||
unsigned int device_id;
|
unsigned int device_id;
|
||||||
|
unsigned int version_id;
|
||||||
unsigned int density_mask;
|
unsigned int density_mask;
|
||||||
unsigned int options;
|
unsigned int options;
|
||||||
|
|
||||||
|
@ -149,7 +150,8 @@ struct onenand_chip {
|
||||||
/*
|
/*
|
||||||
* Options bits
|
* Options bits
|
||||||
*/
|
*/
|
||||||
#define ONENAND_CONT_LOCK (0x0001)
|
#define ONENAND_HAS_CONT_LOCK (0x0001)
|
||||||
|
#define ONENAND_HAS_UNLOCK_ALL (0x0002)
|
||||||
#define ONENAND_PAGEBUF_ALLOC (0x1000)
|
#define ONENAND_PAGEBUF_ALLOC (0x1000)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* OneNAND Register header file
|
* 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
|
* 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
|
* 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_VCC_MASK (0x3)
|
||||||
|
|
||||||
#define ONENAND_DEVICE_DENSITY_512Mb (0x002)
|
#define ONENAND_DEVICE_DENSITY_512Mb (0x002)
|
||||||
|
#define ONENAND_DEVICE_DENSITY_1Gb (0x003)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Version ID Register F002h (R)
|
* Version ID Register F002h (R)
|
||||||
|
@ -110,6 +111,7 @@
|
||||||
#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)
|
||||||
|
#define ONENAND_CMD_UNLOCK_ALL (0x27)
|
||||||
#define ONENAND_CMD_ERASE (0x94)
|
#define ONENAND_CMD_ERASE (0x94)
|
||||||
#define ONENAND_CMD_RESET (0xF0)
|
#define ONENAND_CMD_RESET (0xF0)
|
||||||
#define ONENAND_CMD_OTP_ACCESS (0x65)
|
#define ONENAND_CMD_OTP_ACCESS (0x65)
|
||||||
|
|
Loading…
Reference in New Issue