Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 28b79ff9 authored by Kyungmin Park's avatar Kyungmin Park Committed by David Woodhouse
Browse files

[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: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 98638ee2
Loading
Loading
Loading
Loading
+118 −19
Original line number Diff line number Diff line
/*
 *  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,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;

@@ -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;
@@ -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;
}
@@ -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);
}
+4 −2
Original line number Diff line number Diff line
/*
 *  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 −1
Original line number Diff line number Diff line
@@ -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)