mtd: nand: add driver for diskonchip g4 nand flash

This patch adds a driver for the diskonchip G4 nand flash device.  It is based
on the driver from the linux kernel.

This also includes a separate SPL driver.  A separate SPL driver is used because
the device operates in a different mode (reliable mode) when loading a boot
image, and also because the storage format of the boot image is different from
normal data (pages are stored redundantly).  The SPL driver basically mimics how
a typical IPL reads data from the device.  The special operating mode and
storage format are used to compensate for the fact that the IPL does not contain
the BCH ecc decoding algorithm (due to size constraints).  Although the u-boot
SPL *could* use ecc, it operates like an IPL for the sake of simplicity and
uniformity, since the IPL and SPL share the task of loading the u-boot image.
As a side benefit, the SPL driver is very small.

[port from linux kernel 3.4 commit 570469f3bde7f71cc1ece07a18d54a05b6a8775d]

Signed-off-by: Mike Dunn <mikedunn@newsguy.com>
master
Mike Dunn 11 years ago committed by Marek Vasut
parent e0e89e236f
commit 956b03e180
  1. 2
      drivers/mtd/nand/Makefile
  2. 1028
      drivers/mtd/nand/docg4.c
  3. 222
      drivers/mtd/nand/docg4_spl.c
  4. 134
      include/linux/mtd/docg4.h

@ -34,6 +34,7 @@ NORMAL_DRIVERS=y
endif
COBJS-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o
COBJS-$(CONFIG_SPL_NAND_DOCG4) += docg4_spl.o
COBJS-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o
COBJS-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o
COBJS-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o
@ -77,6 +78,7 @@ COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o
COBJS-$(CONFIG_TEGRA_NAND) += tegra_nand.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o
COBJS-$(CONFIG_NAND_DOCG4) += docg4.o
else # minimal SPL drivers

File diff suppressed because it is too large Load Diff

@ -0,0 +1,222 @@
/*
* SPL driver for Diskonchip G4 nand flash
*
* Copyright (C) 2013 Mike Dunn <mikedunn@newsguy.com>
*
* This file is released under the terms of GPL v2 and any later version.
* See the file COPYING in the root directory of the source tree for details.
*
*
* This driver basically mimics the load functionality of a typical IPL (initial
* program loader) resident in the 2k NOR-like region of the docg4 that is
* mapped to the reset vector. It allows the u-boot SPL to continue loading if
* the IPL loads a fixed number of flash blocks that is insufficient to contain
* the entire u-boot image. In this case, a concatenated spl + u-boot image is
* written at the flash offset from which the IPL loads an image, and when the
* IPL jumps to the SPL, the SPL resumes loading where the IPL left off. See
* the palmtreo680 for an example.
*
* This driver assumes that the data was written to the flash using the device's
* "reliable" mode, and also assumes that each 512 byte page is stored
* redundantly in the subsequent page. This storage format is likely to be used
* by all boards that boot from the docg4. The format compensates for the lack
* of ecc in the IPL.
*
* Reliable mode reduces the capacity of a block by half, and the redundant
* pages reduce it by half again. As a result, the normal 256k capacity of a
* block is reduced to 64k for the purposes of the IPL/SPL.
*/
#include <asm/io.h>
#include <linux/mtd/docg4.h>
/* forward declarations */
static inline void write_nop(void __iomem *docptr);
static int poll_status(void __iomem *docptr);
static void write_addr(void __iomem *docptr, uint32_t docg4_addr);
static void address_sequence(unsigned int g4_page, unsigned int g4_index,
void __iomem *docptr);
static int docg4_load_block_reliable(uint32_t flash_offset, void *dest_addr);
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
{
void *load_addr = dst;
uint32_t flash_offset = offs;
const unsigned int block_count =
(size + DOCG4_BLOCK_CAPACITY_SPL - 1)
/ DOCG4_BLOCK_CAPACITY_SPL;
int i;
for (i = 0; i < block_count; i++) {
int ret = docg4_load_block_reliable(flash_offset, load_addr);
if (ret)
return ret;
load_addr += DOCG4_BLOCK_CAPACITY_SPL;
flash_offset += DOCG4_BLOCK_SIZE;
}
return 0;
}
static inline void write_nop(void __iomem *docptr)
{
writew(0, docptr + DOC_NOP);
}
static int poll_status(void __iomem *docptr)
{
/*
* Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL
* register. Operations known to take a long time (e.g., block erase)
* should sleep for a while before calling this.
*/
uint8_t flash_status;
/* hardware quirk requires reading twice initially */
flash_status = readb(docptr + DOC_FLASHCONTROL);
do {
flash_status = readb(docptr + DOC_FLASHCONTROL);
} while (!(flash_status & DOC_CTRL_FLASHREADY));
return 0;
}
static void write_addr(void __iomem *docptr, uint32_t docg4_addr)
{
/* write the four address bytes packed in docg4_addr to the device */
writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
docg4_addr >>= 8;
writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
docg4_addr >>= 8;
writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
docg4_addr >>= 8;
writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
}
static void address_sequence(unsigned int g4_page, unsigned int g4_index,
void __iomem *docptr)
{
writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE);
writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND);
write_nop(docptr);
write_addr(docptr, ((uint32_t)g4_page << 16) | g4_index);
write_nop(docptr);
}
static int docg4_load_block_reliable(uint32_t flash_offset, void *dest_addr)
{
void __iomem *docptr = (void *)CONFIG_SYS_NAND_BASE;
unsigned int g4_page = flash_offset >> 11; /* 2k page */
const unsigned int last_g4_page = g4_page + 0x80; /* last in block */
int g4_index = 0;
uint16_t flash_status;
uint16_t *buf;
uint16_t discard, magic_high, magic_low;
/* flash_offset must be aligned to the start of a block */
if (flash_offset & 0x3ffff)
return -1;
writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE);
writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND);
write_nop(docptr);
write_nop(docptr);
poll_status(docptr);
write_nop(docptr);
writew(0x45, docptr + DOC_FLASHSEQUENCE);
writew(0xa3, docptr + DOC_FLASHCOMMAND);
write_nop(docptr);
writew(0x22, docptr + DOC_FLASHCOMMAND);
write_nop(docptr);
/* read 1st 4 oob bytes of first subpage of block */
address_sequence(g4_page, 0x0100, docptr); /* index at oob */
write_nop(docptr);
flash_status = readw(docptr + DOC_FLASHCONTROL);
flash_status = readw(docptr + DOC_FLASHCONTROL);
if (flash_status & 0x06) /* sequence or protection errors */
return -1;
writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND);
write_nop(docptr);
write_nop(docptr);
poll_status(docptr);
writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
write_nop(docptr);
write_nop(docptr);
write_nop(docptr);
write_nop(docptr);
write_nop(docptr);
/*
* Here we read the first four oob bytes of the first page of the block.
* The IPL on the palmtreo680 requires that this contain a 32 bit magic
* number, or the load aborts. We'll ignore it.
*/
discard = readw(docptr + 0x103c); /* hw quirk; 1st read discarded */
magic_low = readw(docptr + 0x103c);
magic_high = readw(docptr + DOCG4_MYSTERY_REG);
writew(0, docptr + DOC_DATAEND);
write_nop(docptr);
write_nop(docptr);
/* load contents of block to memory */
buf = (uint16_t *)dest_addr;
do {
int i;
address_sequence(g4_page, g4_index, docptr);
writew(DOCG4_CMD_READ2,
docptr + DOC_FLASHCOMMAND);
write_nop(docptr);
write_nop(docptr);
poll_status(docptr);
writew(DOC_ECCCONF0_READ_MODE |
DOC_ECCCONF0_ECC_ENABLE |
DOCG4_BCH_SIZE,
docptr + DOC_ECCCONF0);
write_nop(docptr);
write_nop(docptr);
write_nop(docptr);
write_nop(docptr);
write_nop(docptr);
/* read the 512 bytes of page data, 2 bytes at a time */
discard = readw(docptr + 0x103c);
for (i = 0; i < 256; i++)
*buf++ = readw(docptr + 0x103c);
/* read oob, but discard it */
for (i = 0; i < 7; i++)
discard = readw(docptr + 0x103c);
discard = readw(docptr + DOCG4_OOB_6_7);
discard = readw(docptr + DOCG4_OOB_6_7);
writew(0, docptr + DOC_DATAEND);
write_nop(docptr);
write_nop(docptr);
if (!(g4_index & 0x100)) {
/* not redundant subpage read; check for ecc error */
write_nop(docptr);
flash_status = readw(docptr + DOC_ECCCONF1);
flash_status = readw(docptr + DOC_ECCCONF1);
if (flash_status & 0x80) { /* ecc error */
g4_index += 0x108; /* read redundant subpage */
buf -= 256; /* back up ram ptr */
continue;
} else /* no ecc error */
g4_index += 0x210; /* skip redundant subpage */
} else /* redundant page was just read; skip ecc error check */
g4_index += 0x108;
if (g4_index == 0x420) { /* finished with 2k page */
g4_index = 0;
g4_page += 2; /* odd-numbered 2k pages skipped */
}
} while (g4_page != last_g4_page); /* while still on same block */
return 0;
}

@ -0,0 +1,134 @@
/*
* Copyright (C) 2013 Mike Dunn <mikedunn@newsguy.com>
*
* This file is released under the terms of GPL v2 and any later version.
* See the file COPYING in the root directory of the source tree for details.
*
*/
#ifndef __DOCG4_H__
#define __DOCG4_H__
#include <common.h>
#include <linux/mtd/nand.h>
extern int docg4_nand_init(struct mtd_info *mtd,
struct nand_chip *nand, int devnum);
/* SPL-related definitions */
#define DOCG4_IPL_LOAD_BLOCK_COUNT 2 /* number of blocks that IPL loads */
#define DOCG4_BLOCK_CAPACITY_SPL 0x10000 /* reliable mode; redundant pages */
#define DOC_IOSPACE_DATA 0x0800
/* register offsets */
#define DOC_CHIPID 0x1000
#define DOC_DEVICESELECT 0x100a
#define DOC_ASICMODE 0x100c
#define DOC_DATAEND 0x101e
#define DOC_NOP 0x103e
#define DOC_FLASHSEQUENCE 0x1032
#define DOC_FLASHCOMMAND 0x1034
#define DOC_FLASHADDRESS 0x1036
#define DOC_FLASHCONTROL 0x1038
#define DOC_ECCCONF0 0x1040
#define DOC_ECCCONF1 0x1042
#define DOC_HAMMINGPARITY 0x1046
#define DOC_BCH_SYNDROM(idx) (0x1048 + idx)
#define DOC_ASICMODECONFIRM 0x1072
#define DOC_CHIPID_INV 0x1074
#define DOC_POWERMODE 0x107c
#define DOCG4_MYSTERY_REG 0x1050
/* apparently used only to write oob bytes 6 and 7 */
#define DOCG4_OOB_6_7 0x1052
/* DOC_FLASHSEQUENCE register commands */
#define DOC_SEQ_RESET 0x00
#define DOCG4_SEQ_PAGE_READ 0x03
#define DOCG4_SEQ_FLUSH 0x29
#define DOCG4_SEQ_PAGEWRITE 0x16
#define DOCG4_SEQ_PAGEPROG 0x1e
#define DOCG4_SEQ_BLOCKERASE 0x24
/* DOC_FLASHCOMMAND register commands */
#define DOCG4_CMD_PAGE_READ 0x00
#define DOC_CMD_ERASECYCLE2 0xd0
#define DOCG4_CMD_FLUSH 0x70
#define DOCG4_CMD_READ2 0x30
#define DOC_CMD_PROG_BLOCK_ADDR 0x60
#define DOCG4_CMD_PAGEWRITE 0x80
#define DOC_CMD_PROG_CYCLE2 0x10
#define DOC_CMD_RESET 0xff
/* DOC_POWERMODE register bits */
#define DOC_POWERDOWN_READY 0x80
/* DOC_FLASHCONTROL register bits */
#define DOC_CTRL_CE 0x10
#define DOC_CTRL_UNKNOWN 0x40
#define DOC_CTRL_FLASHREADY 0x01
/* DOC_ECCCONF0 register bits */
#define DOC_ECCCONF0_READ_MODE 0x8000
#define DOC_ECCCONF0_UNKNOWN 0x2000
#define DOC_ECCCONF0_ECC_ENABLE 0x1000
#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff
/* DOC_ECCCONF1 register bits */
#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80
#define DOC_ECCCONF1_ECC_ENABLE 0x07
#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20
/* DOC_ASICMODE register bits */
#define DOC_ASICMODE_RESET 0x00
#define DOC_ASICMODE_NORMAL 0x01
#define DOC_ASICMODE_POWERDOWN 0x02
#define DOC_ASICMODE_MDWREN 0x04
#define DOC_ASICMODE_BDETCT_RESET 0x08
#define DOC_ASICMODE_RSTIN_RESET 0x10
#define DOC_ASICMODE_RAM_WE 0x20
/* good status values read after read/write/erase operations */
#define DOCG4_PROGSTATUS_GOOD 0x51
#define DOCG4_PROGSTATUS_GOOD_2 0xe0
/*
* On read operations (page and oob-only), the first byte read from I/O reg is a
* status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read
* after reset only) or 0x51, so bit 1 is presumed to be an error indicator.
*/
#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */
/* anatomy of the device */
#define DOCG4_CHIP_SIZE 0x8000000
#define DOCG4_PAGE_SIZE 0x200
#define DOCG4_PAGES_PER_BLOCK 0x200
#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE)
#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE)
#define DOCG4_OOB_SIZE 0x10
#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */
#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */
#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */
/* all but the last byte is included in ecc calculation */
#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1)
#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */
/* expected values from the ID registers */
#define DOCG4_IDREG1_VALUE 0x0400
#define DOCG4_IDREG2_VALUE 0xfbff
/* primitive polynomial used to build the Galois field used by hw ecc gen */
#define DOCG4_PRIMITIVE_POLY 0x4443
#define DOCG4_M 14 /* Galois field is of order 2^14 */
#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */
#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */
#endif /* __DOCG4_H__ */
Loading…
Cancel
Save