Skip to content
Snippets Groups Projects
nand_base.c 74.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    			this->pagebuf = page;
    			this->data_poi = this->data_buf;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			numpages = 1;
    
    			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
    			ret = nand_write_page (mtd, this, page & this->pagemask,
    				oobbuf, oobsel, 0);
    			if (ret)
    				goto out;
    			page++;
    		}
    
    		this->data_poi = bufstart;
    		ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
    		if (ret)
    			goto out;
    
    		written += mtd->oobblock * numpages;
    		/* All done ? */
    		if (!count)
    			break;
    
    		startpage = page & this->pagemask;
    		/* Check, if we cross a chip boundary */
    		if (!startpage) {
    			chipnr++;
    			this->select_chip(mtd, -1);
    			this->select_chip(mtd, chipnr);
    		}
    	}
    	ret = 0;
    out:
    	/* Deselect and wake up anyone waiting on the device */
    	nand_release_device(mtd);
    
    	*retlen = written;
    	return ret;
    }
    #endif
    
    /**
     * single_erease_cmd - [GENERIC] NAND standard block erase command function
     * @mtd:	MTD device structure
     * @page:	the page address of the block which will be erased
     *
     * Standard erase command for NAND chips
     */
    static void single_erase_cmd (struct mtd_info *mtd, int page)
    {
    	struct nand_chip *this = mtd->priv;
    	/* Send commands to erase a block */
    	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
    	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
    }
    
    /**
     * multi_erease_cmd - [GENERIC] AND specific block erase command function
     * @mtd:	MTD device structure
     * @page:	the page address of the block which will be erased
     *
     * AND multi block erase command function
     * Erase 4 consecutive blocks
     */
    static void multi_erase_cmd (struct mtd_info *mtd, int page)
    {
    	struct nand_chip *this = mtd->priv;
    	/* Send commands to erase a block */
    	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
    	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
    	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
    	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
    	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
    }
    
    /**
     * nand_erase - [MTD Interface] erase block(s)
     * @mtd:	MTD device structure
     * @instr:	erase instruction
     *
     * Erase one ore more blocks
     */
    static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
    {
    	return nand_erase_nand (mtd, instr, 0);
    }
    
    /**
     * nand_erase_intern - [NAND Interface] erase block(s)
     * @mtd:	MTD device structure
     * @instr:	erase instruction
     * @allowbbt:	allow erasing the bbt area
     *
     * Erase one ore more blocks
     */
    int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
    {
    	int page, len, status, pages_per_block, ret, chipnr;
    	struct nand_chip *this = mtd->priv;
    
    	DEBUG (MTD_DEBUG_LEVEL3,
    	       "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
    
    	/* Start address must align on block boundary */
    	if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
    		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
    		return -EINVAL;
    	}
    
    	/* Length must align on block boundary */
    	if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
    		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
    		return -EINVAL;
    	}
    
    	/* Do not allow erase past end of device */
    	if ((instr->len + instr->addr) > mtd->size) {
    		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
    		return -EINVAL;
    	}
    
    	instr->fail_addr = 0xffffffff;
    
    	/* Grab the lock and see if the device is available */
    	nand_get_device (this, mtd, FL_ERASING);
    
    	/* Shift to get first page */
    	page = (int) (instr->addr >> this->page_shift);
    	chipnr = (int) (instr->addr >> this->chip_shift);
    
    	/* Calculate pages in each block */
    	pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
    
    	/* Select the NAND device */
    	this->select_chip(mtd, chipnr);
    
    	/* Check the WP bit */
    	/* Check, if it is write protected */
    	if (nand_check_wp(mtd)) {
    		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
    		instr->state = MTD_ERASE_FAILED;
    		goto erase_exit;
    	}
    
    	/* Loop through the pages */
    	len = instr->len;
    
    	instr->state = MTD_ERASING;
    
    	while (len) {
    
    #ifndef NAND_ALLOW_ERASE_ALL
    
    		/* Check if we have a bad block, we do not erase bad blocks ! */
    		if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
    			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
    			instr->state = MTD_ERASE_FAILED;
    			goto erase_exit;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* Invalidate the page cache, if we erase the block which contains
    
    		   the current cached page */
    		if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
    			this->pagebuf = -1;
    
    		this->erase_cmd (mtd, page & this->pagemask);
    
    		status = this->waitfunc (mtd, this, FL_ERASING);
    
    		/* See if block erase succeeded */
    		if (status & 0x01) {
    			DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
    			instr->state = MTD_ERASE_FAILED;
    			instr->fail_addr = (page << this->page_shift);
    			goto erase_exit;
    		}
    
    		/* Increment page address and decrement length */
    		len -= (1 << this->phys_erase_shift);
    		page += pages_per_block;
    
    		/* Check, if we cross a chip boundary */
    		if (len && !(page & this->pagemask)) {
    			chipnr++;
    			this->select_chip(mtd, -1);
    			this->select_chip(mtd, chipnr);
    		}
    	}
    	instr->state = MTD_ERASE_DONE;
    
    erase_exit:
    
    	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
    	/* Do call back function */
    	if (!ret)
    		mtd_erase_callback(instr);
    
    	/* Deselect and wake up anyone waiting on the device */
    	nand_release_device(mtd);
    
    	/* Return more or less happy */
    	return ret;
    }
    
    /**
     * nand_sync - [MTD Interface] sync
     * @mtd:	MTD device structure
     *
     * Sync is actually a wait for chip ready function
     */
    static void nand_sync (struct mtd_info *mtd)
    {
    	struct nand_chip *this = mtd->priv;
    
    	DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
    
    	/* Grab the lock and see if the device is available */
    	nand_get_device (this, mtd, FL_SYNCING);
    	/* Release it and go back */
    	nand_release_device (mtd);
    }
    
    
    /**
     * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
     * @mtd:	MTD device structure
     * @ofs:	offset relative to mtd start
     */
    static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
    {
    	/* Check for invalid offset */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if (ofs > mtd->size)
    
    	return nand_block_checkbad (mtd, ofs, 1, 0);
    }
    
    /**
     * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
     * @mtd:	MTD device structure
     * @ofs:	offset relative to mtd start
     */
    static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
    {
    	struct nand_chip *this = mtd->priv;
    	int ret;
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if ((ret = nand_block_isbad(mtd, ofs))) {
    		/* If it was bad already, return success and do nothing. */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return ret;
    	}
    
    
    	return this->block_markbad(mtd, ofs);
    }
    
    /**
     * 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 not initialized 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
     *
     */
    int nand_scan (struct mtd_info *mtd, int maxchips)
    {
    	int i, j, nand_maf_id, nand_dev_id, busw;
    	struct nand_chip *this = mtd->priv;
    
    	/* Get buswidth to select the correct functions*/
    	busw = this->options & NAND_BUSWIDTH_16;
    
    	/* check for proper chip_delay setup, set 20us if not */
    	if (!this->chip_delay)
    		this->chip_delay = 20;
    
    	/* check, if a user supplied command function given */
    	if (this->cmdfunc == NULL)
    		this->cmdfunc = nand_command;
    
    	/* check, if a user supplied wait function given */
    	if (this->waitfunc == NULL)
    		this->waitfunc = nand_wait;
    
    	if (!this->select_chip)
    		this->select_chip = nand_select_chip;
    	if (!this->write_byte)
    		this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
    	if (!this->read_byte)
    		this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
    	if (!this->write_word)
    		this->write_word = nand_write_word;
    	if (!this->read_word)
    		this->read_word = nand_read_word;
    	if (!this->block_bad)
    		this->block_bad = nand_block_bad;
    	if (!this->block_markbad)
    		this->block_markbad = nand_default_block_markbad;
    	if (!this->write_buf)
    		this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
    	if (!this->read_buf)
    		this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
    	if (!this->verify_buf)
    		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
    	if (!this->scan_bbt)
    		this->scan_bbt = nand_default_bbt;
    
    	/* Select the device */
    	this->select_chip(mtd, 0);
    
    	/* Send the command for reading device ID */
    	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
    
    	/* Read manufacturer and device IDs */
    	nand_maf_id = this->read_byte(mtd);
    	nand_dev_id = this->read_byte(mtd);
    
    	/* Print and store flash device information */
    	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		if (nand_dev_id != nand_flash_ids[i].id)
    
    			continue;
    
    		if (!mtd->name) mtd->name = nand_flash_ids[i].name;
    		this->chipsize = nand_flash_ids[i].chipsize << 20;
    
    		/* New devices have all the information in additional id bytes */
    		if (!nand_flash_ids[i].pagesize) {
    			int extid;
    			/* The 3rd id byte contains non relevant data ATM */
    			extid = this->read_byte(mtd);
    			/* The 4th id byte is the important one */
    			extid = this->read_byte(mtd);
    			/* Calc pagesize */
    			mtd->oobblock = 1024 << (extid & 0x3);
    			extid >>= 2;
    			/* Calc oobsize */
    
    			mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512);
    
    			extid >>= 2;
    			/* Calc blocksize. Blocksize is multiples of 64KiB */
    			mtd->erasesize = (64 * 1024)  << (extid & 0x03);
    			extid >>= 2;
    			/* Get buswidth information */
    			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
    
    		} else {
    			/* Old devices have this data hardcoded in the
    			 * device id table */
    			mtd->erasesize = nand_flash_ids[i].erasesize;
    			mtd->oobblock = nand_flash_ids[i].pagesize;
    			mtd->oobsize = mtd->oobblock / 32;
    			busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
    		}
    
    		/* Check, if buswidth is correct. Hardware drivers should set
    		 * this correct ! */
    		if (busw != (this->options & NAND_BUSWIDTH_16)) {
    			printk (KERN_INFO "NAND device: Manufacturer ID:"
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
    
    				nand_manuf_ids[i].name , mtd->name);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			printk (KERN_WARNING
    				"NAND bus width %d instead %d bit\n",
    
    					(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
    					busw ? 16 : 8);
    			this->select_chip(mtd, -1);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			return 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		/* Calculate the address shift from the page size */
    
    		this->page_shift = ffs(mtd->oobblock) - 1;
    		this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
    		this->chip_shift = ffs(this->chipsize) - 1;
    
    		/* Set the bad block position */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		this->badblockpos = mtd->oobblock > 512 ?
    
    			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
    
    		/* Get chip options, preserve non chip based options */
    		this->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 neccecary */
    		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.
    
    		if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
    			this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
    
    		/* Check for AND chips with 4 page planes */
    		if (this->options & NAND_4PAGE_ARRAY)
    			this->erase_cmd = multi_erase_cmd;
    		else
    			this->erase_cmd = single_erase_cmd;
    
    		/* Do not replace user supplied command function ! */
    		if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
    			this->cmdfunc = nand_command_lp;
    
    		/* Try to identify manufacturer */
    		for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
    			if (nand_manuf_ids[j].id == nand_maf_id)
    				break;
    		}
    		break;
    	}
    
    	if (!nand_flash_ids[i].name) {
    
    		printk (KERN_WARNING "No NAND device found!!!\n");
    
    		this->select_chip(mtd, -1);
    		return 1;
    	}
    
    	for (i=1; i < maxchips; i++) {
    		this->select_chip(mtd, i);
    
    		/* Send the command for reading device ID */
    		this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
    
    		/* Read manufacturer and device IDs */
    		if (nand_maf_id != this->read_byte(mtd) ||
    		    nand_dev_id != this->read_byte(mtd))
    			break;
    	}
    	if (i > 1)
    		printk(KERN_INFO "%d NAND chips detected\n", i);
    
    	/* Allocate buffers, if neccecary */
    	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) {
    			if (this->options & NAND_OOBBUF_ALLOC)
    				kfree (this->oob_buf);
    			printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
    			return -ENOMEM;
    		}
    		this->options |= NAND_DATABUF_ALLOC;
    	}
    
    	/* Store the number of chips and calc total size for mtd */
    	this->numchips = i;
    	mtd->size = i * this->chipsize;
    	/* Convert chipsize to number of pages per chip -1. */
    	this->pagemask = (this->chipsize >> this->page_shift) - 1;
    	/* Preset the internal oob buffer */
    	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 (!this->autooob) {
    		/* Select the appropriate default oob placement scheme for
    		 * placement agnostic filesystems */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		switch (mtd->oobsize) {
    
    		case 8:
    			this->autooob = &nand_oob_8;
    			break;
    		case 16:
    			this->autooob = &nand_oob_16;
    			break;
    		case 64:
    			this->autooob = &nand_oob_64;
    			break;
    		default:
    			printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
    				mtd->oobsize);
    /*			BUG(); */
    		}
    	}
    
    	/* The number of bytes available for the filesystem to place fs dependend
    	 * oob data */
    	if (this->options & NAND_BUSWIDTH_16) {
    		mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
    		if (this->autooob->eccbytes & 0x01)
    			mtd->oobavail--;
    	} else
    		mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
    
    
    	 * check ECC mode, default to software
    	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	 * fallback to software ECC
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	this->eccsize = 256;	/* set default eccsize */
    
    	this->eccbytes = 3;
    
    	switch (this->eccmode) {
    	case NAND_ECC_HW12_2048:
    		if (mtd->oobblock < 2048) {
    			printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
    			       mtd->oobblock);
    			this->eccmode = NAND_ECC_SOFT;
    			this->calculate_ecc = nand_calculate_ecc;
    			this->correct_data = nand_correct_data;
    		} else
    			this->eccsize = 2048;
    		break;
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	case NAND_ECC_HW3_512:
    	case NAND_ECC_HW6_512:
    	case NAND_ECC_HW8_512:
    
    		if (mtd->oobblock == 256) {
    			printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
    			this->eccmode = NAND_ECC_SOFT;
    			this->calculate_ecc = nand_calculate_ecc;
    			this->correct_data = nand_correct_data;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		} else
    
    			this->eccsize = 512; /* set eccsize to 512 */
    		break;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	case NAND_ECC_NONE:
    
    		printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
    		this->eccmode = NAND_ECC_NONE;
    		break;
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	case NAND_ECC_SOFT:
    
    		this->calculate_ecc = nand_calculate_ecc;
    		this->correct_data = nand_correct_data;
    		break;
    
    	default:
    		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
    /*		BUG(); */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* Check hardware ecc function availability and adjust number of ecc bytes per
    
    	 * calculation step
    	*/
    	switch (this->eccmode) {
    	case NAND_ECC_HW12_2048:
    		this->eccbytes += 4;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	case NAND_ECC_HW8_512:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	case NAND_ECC_HW6_512:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	case NAND_ECC_HW3_512:
    
    	case NAND_ECC_HW3_256:
    		if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
    			break;
    		printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
    /*		BUG();	*/
    	}
    
    	mtd->eccsize = this->eccsize;
    
    	/* Set the number of read / write steps for one page to ensure ECC generation */
    	switch (this->eccmode) {
    	case NAND_ECC_HW12_2048:
    		this->eccsteps = mtd->oobblock / 2048;
    		break;
    	case NAND_ECC_HW3_512:
    	case NAND_ECC_HW6_512:
    	case NAND_ECC_HW8_512:
    		this->eccsteps = mtd->oobblock / 512;
    		break;
    	case NAND_ECC_HW3_256:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	case NAND_ECC_SOFT:
    
    		this->eccsteps = mtd->oobblock / 256;
    		break;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	case NAND_ECC_NONE:
    
    		this->eccsteps = 1;
    		break;
    	}
    
    /* XXX U-BOOT XXX */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #if 0
    
    	/* Initialize state, waitqueue and spinlock */
    	this->state = FL_READY;
    	init_waitqueue_head (&this->wq);
    	spin_lock_init (&this->chip_lock);
    #endif
    
    	/* De-select the device */
    	this->select_chip(mtd, -1);
    
    	/* Invalidate the pagebuffer reference */
    	this->pagebuf = -1;
    
    	/* Fill in remaining MTD driver data */
    	mtd->type = MTD_NANDFLASH;
    	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
    	mtd->ecctype = MTD_ECC_SW;
    	mtd->erase = nand_erase;
    	mtd->point = NULL;
    	mtd->unpoint = NULL;
    	mtd->read = nand_read;
    	mtd->write = nand_write;
    	mtd->read_ecc = nand_read_ecc;
    	mtd->write_ecc = nand_write_ecc;
    	mtd->read_oob = nand_read_oob;
    	mtd->write_oob = nand_write_oob;
    /* XXX U-BOOT XXX */
    #if 0
    	mtd->readv = NULL;
    	mtd->writev = nand_writev;
    	mtd->writev_ecc = nand_writev_ecc;
    #endif
    	mtd->sync = nand_sync;
    /* XXX U-BOOT XXX */
    #if 0
    	mtd->lock = NULL;
    	mtd->unlock = NULL;
    	mtd->suspend = NULL;
    	mtd->resume = NULL;
    #endif
    	mtd->block_isbad = nand_block_isbad;
    	mtd->block_markbad = nand_block_markbad;
    
    	/* and make the autooob the default one */
    	memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
    /* XXX U-BOOT XXX */
    #if 0
    	mtd->owner = THIS_MODULE;
    #endif
    	/* Build bad block table */
    	return this->scan_bbt (mtd);
    }
    
    /**
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * nand_release - [NAND Interface] Free resources held by the NAND device
    
     * @mtd:	MTD device structure
    
    void nand_release (struct mtd_info *mtd)
    {
    	struct nand_chip *this = mtd->priv;
    
    #ifdef CONFIG_MTD_PARTITIONS
    	/* Deregister partitions */
    	del_mtd_partitions (mtd);
    #endif
    	/* Deregister the device */
    /* XXX U-BOOT XXX */
    #if 0
    	del_mtd_device (mtd);
    #endif
    	/* Free bad block table memory, if allocated */
    	if (this->bbt)
    		kfree (this->bbt);
    	/* Buffer allocated by nand_scan ? */
    	if (this->options & NAND_OOBBUF_ALLOC)
    		kfree (this->oob_buf);
    	/* Buffer allocated by nand_scan ? */
    	if (this->options & NAND_DATABUF_ALLOC)
    		kfree (this->data_buf);
    }
    
    #endif