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

Commit 7aa65bfd authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

[MTD] NAND cleanup nand_scan



Seperate functionality out of nand_scan so the code is more
readable. No functional change. First step of simplifying
the nand driver.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 58dd8f2b
Loading
Loading
Loading
Loading
+233 −174
Original line number Original line Diff line number Diff line
@@ -76,6 +76,7 @@
#include <linux/module.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/types.h>
@@ -2333,42 +2334,50 @@ static void nand_free_kmem(struct nand_chip *this)
		kfree(this->controller);
		kfree(this->controller);
}
}


/* 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
 * Allocate buffers and data structures
   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. Buffers are allocated if
 * they are not provided by the board driver
 * The mtd->owner field must be set to the module of the caller
 *
 */
 */
int nand_scan(struct mtd_info *mtd, int maxchips)
static int nand_allocate_kmem(struct mtd_info *mtd, struct nand_chip *this)
{
{
	int i, nand_maf_id, nand_dev_id, busw, maf_id;
	size_t len;
	struct nand_chip *this = mtd->priv;


	/* Many callers got this wrong, so check for it for a while... */
	if (!this->oob_buf) {
	if (!mtd->owner && caller_is_module()) {
		len = mtd->oobsize <<
		printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
			(this->phys_erase_shift - this->page_shift);
		BUG();
		this->oob_buf = kmalloc(len, GFP_KERNEL);
		if (!this->oob_buf)
			goto outerr;
		this->options |= NAND_OOBBUF_ALLOC;
	}
	}


	/* Get buswidth to select the correct functions */
	if (!this->data_buf) {
	busw = this->options & NAND_BUSWIDTH_16;
		len = mtd->oobblock + mtd->oobsize;
		this->data_buf = kmalloc(len, GFP_KERNEL);
		if (!this->data_buf)
			goto outerr;
		this->options |= NAND_DATABUF_ALLOC;
	}

	if (!this->controller) {
		this->controller = kzalloc(sizeof(struct nand_hw_control),
					   GFP_KERNEL);
		if (!this->controller)
			goto outerr;
		this->options |= NAND_CONTROLLER_ALLOC;
	}
	return 0;

 outerr:
	printk(KERN_ERR "nand_scan(): Cannot allocate buffers\n");
	nand_free_kmem(this);
	return -ENOMEM;
}


/*
 * Set default functions
 */
static void nand_set_defaults(struct nand_chip *this, int busw)
{
	/* check for proper chip_delay setup, set 20us if not */
	/* check for proper chip_delay setup, set 20us if not */
	if (!this->chip_delay)
	if (!this->chip_delay)
		this->chip_delay = 20;
		this->chip_delay = 20;
@@ -2403,6 +2412,17 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
	if (!this->scan_bbt)
	if (!this->scan_bbt)
		this->scan_bbt = nand_default_bbt;
		this->scan_bbt = nand_default_bbt;
}

/*
 * Get the flash and manufacturer id and lookup if the typ is supported
 */
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
						  struct nand_chip *this,
						  int busw, int *maf_id)
{
	struct nand_flash_dev *type = NULL;
	int i, dev_id, maf_idx;


	/* Select the device */
	/* Select the device */
	this->select_chip(mtd, 0);
	this->select_chip(mtd, 0);
@@ -2411,20 +2431,23 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
	this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
	this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);


	/* Read manufacturer and device IDs */
	/* Read manufacturer and device IDs */
	nand_maf_id = this->read_byte(mtd);
	*maf_id = this->read_byte(mtd);
	nand_dev_id = this->read_byte(mtd);
	dev_id = this->read_byte(mtd);


	/* Print and store flash device information */
	/* Lookup the flash id */
	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
		if (dev_id == nand_flash_ids[i].id) {
			type =  &nand_flash_ids[i];
			break;
		}
	}


		if (nand_dev_id != nand_flash_ids[i].id)
	if (!type)
			continue;
		return ERR_PTR(-ENODEV);


		if (!mtd->name)
			mtd->name = nand_flash_ids[i].name;
	this->chipsize = nand_flash_ids[i].chipsize << 20;
	this->chipsize = nand_flash_ids[i].chipsize << 20;


		/* New devices have all the information in additional id bytes */
	/* Newer devices have all the information in additional id bytes */
	if (!nand_flash_ids[i].pagesize) {
	if (!nand_flash_ids[i].pagesize) {
		int extid;
		int extid;
		/* The 3rd id byte contains non relevant data ATM */
		/* The 3rd id byte contains non relevant data ATM */
@@ -2444,8 +2467,9 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;


	} else {
	} else {
			/* Old devices have this data hardcoded in the
		/*
			 * device id table */
		 * Old devices have this data hardcoded in the device id table
		 */
		mtd->erasesize = nand_flash_ids[i].erasesize;
		mtd->erasesize = nand_flash_ids[i].erasesize;
		mtd->oobblock = nand_flash_ids[i].pagesize;
		mtd->oobblock = nand_flash_ids[i].pagesize;
		mtd->oobsize = mtd->oobblock / 32;
		mtd->oobsize = mtd->oobblock / 32;
@@ -2453,41 +2477,51 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
	}
	}


	/* Try to identify manufacturer */
	/* Try to identify manufacturer */
		for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) {
	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_id++) {
			if (nand_manuf_ids[maf_id].id == nand_maf_id)
		if (nand_manuf_ids[maf_idx].id == *maf_id)
			break;
			break;
	}
	}


		/* Check, if buswidth is correct. Hardware drivers should set
	/*
		 * this correct ! */
	 * Check, if buswidth is correct. Hardware drivers should set
	 * this correct !
	 */
	if (busw != (this->options & NAND_BUSWIDTH_16)) {
	if (busw != (this->options & NAND_BUSWIDTH_16)) {
		printk(KERN_INFO "NAND device: Manufacturer ID:"
		printk(KERN_INFO "NAND device: Manufacturer ID:"
			       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
			       nand_manuf_ids[maf_id].name, mtd->name);
		       dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
			printk(KERN_WARNING
		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
			       "NAND bus width %d instead %d bit\n",
		       (this->options & NAND_BUSWIDTH_16) ? 16 : 8,
			       (this->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8);
		       busw ? 16 : 8);
			this->select_chip(mtd, -1);
		return ERR_PTR(-EINVAL);
			return 1;
	}
	}


	/* Calculate the address shift from the page size */
	/* Calculate the address shift from the page size */
	this->page_shift = ffs(mtd->oobblock) - 1;
	this->page_shift = ffs(mtd->oobblock) - 1;
		this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
	/* Convert chipsize to number of pages per chip -1. */
	this->pagemask = (this->chipsize >> this->page_shift) - 1;

	this->bbt_erase_shift = this->phys_erase_shift =
		ffs(mtd->erasesize) - 1;
	this->chip_shift = ffs(this->chipsize) - 1;
	this->chip_shift = ffs(this->chipsize) - 1;


	/* Set the bad block position */
	/* Set the bad block position */
		this->badblockpos = mtd->oobblock > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
	this->badblockpos = mtd->oobblock > 512 ?
		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;


	/* Get chip options, preserve non chip based options */
	/* Get chip options, preserve non chip based options */
	this->options &= ~NAND_CHIPOPTIONS_MSK;
	this->options &= ~NAND_CHIPOPTIONS_MSK;
	this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
	this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
		/* Set this as a default. Board drivers can override it, if necessary */

	/*
	 * Set this as a default. Board drivers can override it, if necessary
	 */
	this->options |= NAND_NO_AUTOINCR;
	this->options |= NAND_NO_AUTOINCR;
		/* Check if this is a not a samsung device. Do not clear the options

		 * for chips which are not having an extended id.
	/* Check if this is a not a samsung device. Do not clear the
	 * options for chips which are not having an extended id.
	 */
	 */
		if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
	if (*maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
		this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
		this->options &= ~NAND_SAMSUNG_LP_OPTIONS;


	/* Check for AND chips with 4 page planes */
	/* Check for AND chips with 4 page planes */
@@ -2501,68 +2535,90 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
		this->cmdfunc = nand_command_lp;
		this->cmdfunc = nand_command_lp;


	printk(KERN_INFO "NAND device: Manufacturer ID:"
	printk(KERN_INFO "NAND device: Manufacturer ID:"
		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
	       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
		       nand_manuf_ids[maf_id].name, nand_flash_ids[i].name);
	       nand_manuf_ids[maf_idx].name, type->name);
		break;

	return type;
}
}


	if (!nand_flash_ids[i].name) {
/* 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. Buffers are allocated if
 * they are not provided by the board driver
 * The mtd->owner field must be set to the module of the caller
 *
 */
int nand_scan(struct mtd_info *mtd, int maxchips)
{
	int i, busw, nand_maf_id;
	struct nand_chip *this = 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 = this->options & NAND_BUSWIDTH_16;
	/* Set the default functions */
	nand_set_defaults(this, busw);

	/* Read the flash type */
	type = nand_get_flash_type(mtd, this, busw, &nand_maf_id);

	if (IS_ERR(type)) {
		printk(KERN_WARNING "No NAND device found!!!\n");
		printk(KERN_WARNING "No NAND device found!!!\n");
		this->select_chip(mtd, -1);
		this->select_chip(mtd, -1);
		return 1;
		return PTR_ERR(type);
	}
	}


	/* Check for a chip array */
	for (i = 1; i < maxchips; i++) {
	for (i = 1; i < maxchips; i++) {
		this->select_chip(mtd, i);
		this->select_chip(mtd, i);

		/* Send the command for reading device ID */
		/* Send the command for reading device ID */
		this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
		this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);

		/* Read manufacturer and device IDs */
		/* Read manufacturer and device IDs */
		if (nand_maf_id != this->read_byte(mtd) ||
		if (nand_maf_id != this->read_byte(mtd) ||
		    nand_dev_id != this->read_byte(mtd))
		    type->id != this->read_byte(mtd))
			break;
			break;
	}
	}
	if (i > 1)
	if (i > 1)
		printk(KERN_INFO "%d NAND chips detected\n", i);
		printk(KERN_INFO "%d NAND chips detected\n", i);


	/* Allocate buffers, if necessary */
	if (!this->oob_buf) {
		size_t len;
		len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
		this->oob_buf = kmalloc(len, GFP_KERNEL);
		if (!this->oob_buf) {
			printk(KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
			return -ENOMEM;
		}
		this->options |= NAND_OOBBUF_ALLOC;
	}

	if (!this->data_buf) {
		size_t len;
		len = mtd->oobblock + mtd->oobsize;
		this->data_buf = kmalloc(len, GFP_KERNEL);
		if (!this->data_buf) {
			printk(KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
			nand_free_kmem(this);
			return -ENOMEM;
		}
		this->options |= NAND_DATABUF_ALLOC;
	}

	/* Store the number of chips and calc total size for mtd */
	/* Store the number of chips and calc total size for mtd */
	this->numchips = i;
	this->numchips = i;
	mtd->size = i * this->chipsize;
	mtd->size = i * this->chipsize;
	/* Convert chipsize to number of pages per chip -1. */

	this->pagemask = (this->chipsize >> this->page_shift) - 1;
	/* Allocate buffers and data structures */
	if (nand_allocate_kmem(mtd, this))
		return -ENOMEM;

	/* Preset the internal oob buffer */
	/* Preset the internal oob buffer */
	memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
	memset(this->oob_buf, 0xff,
	       mtd->oobsize << (this->phys_erase_shift - this->page_shift));


	/* If no default placement scheme is given, select an
	/*
	 * appropriate one */
	 * If no default placement scheme is given, select an appropriate one
	 */
	if (!this->autooob) {
	if (!this->autooob) {
		/* Select the appropriate default oob placement scheme for
		 * placement agnostic filesystems */
		switch (mtd->oobsize) {
		switch (mtd->oobsize) {
		case 8:
		case 8:
			this->autooob = &nand_oob_8;
			this->autooob = &nand_oob_8;
@@ -2574,29 +2630,32 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
			this->autooob = &nand_oob_64;
			this->autooob = &nand_oob_64;
			break;
			break;
		default:
		default:
			printk(KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize);
			printk(KERN_WARNING "No oob scheme defined for "
			       "oobsize %d\n", mtd->oobsize);
			BUG();
			BUG();
		}
		}
	}
	}


	/* The number of bytes available for the filesystem to place fs dependend
	/*
	 * oob data */
	 * The number of bytes available for the filesystem to place fs
	 * dependend oob data
	 */
	mtd->oobavail = 0;
	mtd->oobavail = 0;
	for (i = 0; this->autooob->oobfree[i][1]; i++)
	for (i = 0; this->autooob->oobfree[i][1]; i++)
		mtd->oobavail += this->autooob->oobfree[i][1];
		mtd->oobavail += this->autooob->oobfree[i][1];


	/*
	/*
	 * check ECC mode, default to software
	 * check ECC mode, default to software if 3byte/512byte hardware ECC is
	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
	 * selected and we have 256 byte pagesize fallback to software ECC
	 * fallback to software ECC
	 */
	 */
	this->eccsize = 256;	/* set default eccsize */
	this->eccsize = 256;
	this->eccbytes = 3;
	this->eccbytes = 3;


	switch (this->eccmode) {
	switch (this->eccmode) {
	case NAND_ECC_HW12_2048:
	case NAND_ECC_HW12_2048:
		if (mtd->oobblock < 2048) {
		if (mtd->oobblock < 2048) {
			printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
			printk(KERN_WARNING "2048 byte HW ECC not possible on "
			       "%d byte page size, fallback to SW ECC\n",
			       mtd->oobblock);
			       mtd->oobblock);
			this->eccmode = NAND_ECC_SOFT;
			this->eccmode = NAND_ECC_SOFT;
			this->calculate_ecc = nand_calculate_ecc;
			this->calculate_ecc = nand_calculate_ecc;
@@ -2609,7 +2668,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
	case NAND_ECC_HW6_512:
	case NAND_ECC_HW6_512:
	case NAND_ECC_HW8_512:
	case NAND_ECC_HW8_512:
		if (mtd->oobblock == 256) {
		if (mtd->oobblock == 256) {
			printk(KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
			printk(KERN_WARNING "512 byte HW ECC not possible on "
			       "256 Byte pagesize, fallback to SW ECC \n");
			this->eccmode = NAND_ECC_SOFT;
			this->eccmode = NAND_ECC_SOFT;
			this->calculate_ecc = nand_calculate_ecc;
			this->calculate_ecc = nand_calculate_ecc;
			this->correct_data = nand_correct_data;
			this->correct_data = nand_correct_data;
@@ -2621,7 +2681,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
		break;
		break;


	case NAND_ECC_NONE:
	case NAND_ECC_NONE:
		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
		       "This is not recommended !!\n");
		this->eccmode = NAND_ECC_NONE;
		this->eccmode = NAND_ECC_NONE;
		break;
		break;


@@ -2631,12 +2692,14 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
		break;
		break;


	default:
	default:
		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
		       this->eccmode);
		BUG();
		BUG();
	}
	}


	/* Check hardware ecc function availability and adjust number of ecc bytes per
	/*
	 * calculation step
	 * Check hardware ecc function availability and adjust number of ecc
	 * bytes per calculation step
	 */
	 */
	switch (this->eccmode) {
	switch (this->eccmode) {
	case NAND_ECC_HW12_2048:
	case NAND_ECC_HW12_2048:
@@ -2647,15 +2710,20 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
		this->eccbytes += 3;
		this->eccbytes += 3;
	case NAND_ECC_HW3_512:
	case NAND_ECC_HW3_512:
	case NAND_ECC_HW3_256:
	case NAND_ECC_HW3_256:
		if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
		if (this->calculate_ecc && this->correct_data &&
		    this->enable_hwecc)
			break;
			break;
		printk(KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
		printk(KERN_WARNING "No ECC functions supplied, "
		       "Hardware ECC not possible\n");
		BUG();
		BUG();
	}
	}


	mtd->eccsize = this->eccsize;
	mtd->eccsize = this->eccsize;


	/* Set the number of read / write steps for one page to ensure ECC generation */
	/*
	 * Set the number of read / write steps for one page depending on ECC
	 * mode
	 */
	switch (this->eccmode) {
	switch (this->eccmode) {
	case NAND_ECC_HW12_2048:
	case NAND_ECC_HW12_2048:
		this->eccsteps = mtd->oobblock / 2048;
		this->eccsteps = mtd->oobblock / 2048;
@@ -2677,15 +2745,6 @@ int nand_scan(struct mtd_info *mtd, int maxchips)


	/* Initialize state, waitqueue and spinlock */
	/* Initialize state, waitqueue and spinlock */
	this->state = FL_READY;
	this->state = FL_READY;
	if (!this->controller) {
		this->controller = kzalloc(sizeof(struct nand_hw_control),
					   GFP_KERNEL);
		if (!this->controller) {
			nand_free_kmem(this);
			return -ENOMEM;
		}
		this->options |= NAND_CONTROLLER_ALLOC;
	}
	init_waitqueue_head(&this->controller->wq);
	init_waitqueue_head(&this->controller->wq);
	spin_lock_init(&this->controller->lock);
	spin_lock_init(&this->controller->lock);