Skip to content
Snippets Groups Projects
nand.c 24.2 KiB
Newer Older
/*
 * Driver for NAND support, Rick Bronson
 * borrowed heavily from:
 * (c) 1999 Machine Vision Holdings, Inc.
 * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
 * Ported 'dynenv' to 'nand env.oob' command
 * (C) 2010 Nanometrics, Inc.
 * 'dynenv' -- Dynamic environment offset in NAND OOB
 * (C) Copyright 2006-2007 OpenMoko, Inc.
 * Added 16-bit nand support
 * (C) 2004 Texas Instruments
 * Copyright 2010, 2012 Freescale Semiconductor
 * The portions of this file whose copyright is held by Freescale and which
 * are not considered a derived work of GPL v2-only code may be distributed
 * and/or modified under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 */

#include <common.h>
#include <linux/mtd/mtd.h>
#include <console.h>
#include <watchdog.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <jffs2/jffs2.h>
#include <nand.h>

#if defined(CONFIG_CMD_MTDPARTS)
Wolfgang Denk's avatar
Wolfgang Denk committed
/* partition handling routines */
int mtdparts_init(void);
int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
int find_dev_and_part(const char *id, struct mtd_device **dev,
		      u8 *part_num, struct part_info **part);
static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob,
		     int repeat)
	u_char *datbuf, *oobbuf, *p;
	static loff_t last;
		off = last + mtd->writesize;
	datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize);
		puts("No memory for page buffer\n");
		return 1;
	}
	oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize);
	if (!oobbuf) {
		puts("No memory for page buffer\n");
		ret = 1;
		goto free_dat;
	}
	off &= ~(mtd->writesize - 1);
	loff_t addr = (loff_t) off;
	struct mtd_oob_ops ops;
	memset(&ops, 0, sizeof(ops));
	ops.datbuf = datbuf;
	ops.oobbuf = oobbuf;
	ops.len = mtd->writesize;
	ops.ooblen = mtd->oobsize;
	ops.mode = MTD_OPS_RAW;
	i = mtd_read_oob(mtd, addr, &ops);
		printf("Error (%d) reading page %08lx\n", i, off);
	printf("Page %08lx dump:\n", off);
		i = mtd->writesize >> 4;
			printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
			       "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
			       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
Scott Wood's avatar
Scott Wood committed
			       p[8], p[9], p[10], p[11], p[12], p[13], p[14],
			       p[15]);
	i = mtd->oobsize >> 3;
		printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
		       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
	free(oobbuf);
}

/* ------------------------------------------------------------------------- */

static int set_dev(int dev)
{
	struct mtd_info *mtd = get_nand_dev_by_index(dev);

	if (!mtd)
		return -ENODEV;

	if (nand_curr_device == dev)
		return 0;

	printf("Device %d: %s", dev, mtd->name);
	puts("... is now current device\n");
	nand_curr_device = dev;

#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
	board_nand_select_device(mtd->priv, dev);
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
static void print_status(ulong start, ulong end, ulong erasesize, int status)
{
	/*
	 * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is
	 * not the same as others.  Instead of bit 1 being lock, it is
	 * #lock_tight. To make the driver support either format, ignore bit 1
	 * and use only bit 0 and bit 2.
	 */
	printf("%08lx - %08lx: %08lx blocks %s%s%s\n",
		start,
		end - 1,
		(end - start) / erasesize,
		((status & NAND_LOCK_STATUS_TIGHT) ?  "TIGHT " : ""),
		(!(status & NAND_LOCK_STATUS_UNLOCK) ?  "LOCK " : ""),
		((status & NAND_LOCK_STATUS_UNLOCK) ?  "UNLOCK " : ""));
}

static void do_nand_status(struct mtd_info *mtd)
{
	ulong block_start = 0;
	ulong off;
	int last_status = -1;

	struct nand_chip *nand_chip = mtd_to_nand(mtd);
	/* check the WP bit */
	nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
	printf("device is %swrite protected\n",
		(nand_chip->read_byte(mtd) & 0x80 ?
		 "NOT " : ""));
	for (off = 0; off < mtd->size; off += mtd->erasesize) {
		int s = nand_get_lock_status(mtd, off);

		/* print message only if status has changed */
		if (s != last_status && off != 0) {
			print_status(block_start, off, mtd->erasesize,
					last_status);
			block_start = off;
		}
		last_status = s;
	}
	/* Print the last block info */
	print_status(block_start, off, mtd->erasesize, last_status);
#ifdef CONFIG_ENV_OFFSET_OOB
unsigned long nand_env_oob_offset;

int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[])
{
	int ret;
	uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)];
	struct mtd_info *mtd = get_nand_dev_by_index(0);
	if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !mtd) {
		puts("no devices available\n");
		return 1;
	}

	set_dev(0);

Loading
Loading full blame...