mtd: Flex-OneNAND support

Add support for Samsung Flex-OneNAND devices.

Flex-OneNAND combines SLC and MLC technologies into a single device.
SLC area provides increased reliability and speed, suitable for storing
code such as bootloader, kernel and root file system.  MLC area
provides high density and is suitable for storing user data.

SLC and MLC regions can be configured through kernel parameter.

[akpm@linux-foundation.org: export flexoand_region and onenand_addr]
Signed-off-by: Rohit Hagargundgi <h.rohit@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Vishak G <vishak.g@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
Rohit Hagargundgi 2009-05-12 13:46:57 -07:00 committed by David Woodhouse
parent 67ce04bf27
commit 5988af2319
6 changed files with 917 additions and 91 deletions

View File

@ -1380,6 +1380,16 @@ and is between 256 and 4096 characters. It is defined in the file
mtdparts= [MTD]
See drivers/mtd/cmdlinepart.c.
onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration
Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock]
boundary - index of last SLC block on Flex-OneNAND.
The remaining blocks are configured as MLC blocks.
lock - Configure if Flex-OneNAND boundary should be locked.
Once locked, the boundary cannot be changed.
1 indicates lock status, 0 indicates unlock status.
mtdset= [ARM]
ARM/S3C2412 JIVE boot control

File diff suppressed because it is too large Load Diff

View File

@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
loff_t from;
size_t readlen, ooblen;
struct mtd_oob_ops ops;
int rgn;
printk(KERN_INFO "Scanning device for bad blocks\n");
@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
/* Note that numblocks is 2 * (real numblocks) here;
* see i += 2 below as it makses shifting and masking less painful
*/
numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
startblock = 0;
from = 0;
@ -106,7 +107,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
}
}
i += 2;
from += (1 << bbm->bbt_erase_shift);
if (FLEXONENAND(this)) {
rgn = flexonenand_region(mtd, from);
from += mtd->eraseregions[rgn].erasesize;
} else
from += (1 << bbm->bbt_erase_shift);
}
return 0;
@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
uint8_t res;
/* Get block number * 2 */
block = (int) (offs >> (bbm->bbt_erase_shift - 1));
block = (int) (onenand_block(this, offs) << 1);
res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
@ -178,7 +184,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
struct bbm_info *bbm = this->bbm;
int len, ret = 0;
len = mtd->size >> (this->erase_shift + 2);
len = this->chipsize >> (this->erase_shift + 2);
/* Allocate memory (2bit per block) and clear the memory bad block table */
bbm->bbt = kzalloc(len, GFP_KERNEL);
if (!bbm->bbt) {

View File

@ -6,6 +6,10 @@
* Copyright © 2005-2007 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
*
* Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
* Flex-OneNAND simulator support
* Copyright (C) Samsung Electronics, 2008
*
* 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
* published by the Free Software Foundation.
@ -24,16 +28,38 @@
#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec
#endif
#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04
#endif
#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1)
#ifndef CONFIG_ONENAND_SIM_VERSION_ID
#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e
#endif
#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID
#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND
#endif
/* Initial boundary values for Flex-OneNAND Simulator */
#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY
#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01
#endif
#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY
#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01
#endif
static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER;
static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID;
static int version_id = CONFIG_ONENAND_SIM_VERSION_ID;
static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID;
static int boundary[] = {
CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY,
CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY,
};
struct onenand_flash {
void __iomem *base;
@ -57,12 +83,18 @@ struct onenand_flash {
(writew(v, this->base + ONENAND_REG_WP_STATUS))
/* It has all 0xff chars */
#define MAX_ONENAND_PAGESIZE (2048 + 64)
#define MAX_ONENAND_PAGESIZE (4096 + 128)
static unsigned char *ffchars;
#if CONFIG_FLEXONENAND
#define PARTITION_NAME "Flex-OneNAND simulator partition"
#else
#define PARTITION_NAME "OneNAND simulator partition"
#endif
static struct mtd_partition os_partitions[] = {
{
.name = "OneNAND simulator partition",
.name = PARTITION_NAME,
.offset = 0,
.size = MTDPART_SIZ_FULL,
},
@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd)
switch (cmd) {
case ONENAND_CMD_UNLOCK:
case ONENAND_CMD_UNLOCK_ALL:
if (block_lock_scheme)
ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
else
@ -228,10 +261,12 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
{
struct mtd_info *mtd = &info->mtd;
struct onenand_flash *flash = this->priv;
int main_offset, spare_offset;
int main_offset, spare_offset, die = 0;
void __iomem *src;
void __iomem *dest;
unsigned int i;
static int pi_operation;
int erasesize, rgn;
if (dataram) {
main_offset = mtd->writesize;
@ -241,10 +276,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
spare_offset = 0;
}
if (pi_operation) {
die = readw(this->base + ONENAND_REG_START_ADDRESS2);
die >>= ONENAND_DDP_SHIFT;
}
switch (cmd) {
case FLEXONENAND_CMD_PI_ACCESS:
pi_operation = 1;
break;
case ONENAND_CMD_RESET:
pi_operation = 0;
break;
case ONENAND_CMD_READ:
src = ONENAND_CORE(flash) + offset;
dest = ONENAND_MAIN_AREA(this, main_offset);
if (pi_operation) {
writew(boundary[die], this->base + ONENAND_DATARAM);
break;
}
memcpy(dest, src, mtd->writesize);
/* Fall through */
@ -257,6 +309,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
case ONENAND_CMD_PROG:
src = ONENAND_MAIN_AREA(this, main_offset);
dest = ONENAND_CORE(flash) + offset;
if (pi_operation) {
boundary[die] = readw(this->base + ONENAND_DATARAM);
break;
}
/* To handle partial write */
for (i = 0; i < (1 << mtd->subpage_sft); i++) {
int off = i * this->subpagesize;
@ -284,9 +340,18 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
break;
case ONENAND_CMD_ERASE:
memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
if (pi_operation)
break;
if (FLEXONENAND(this)) {
rgn = flexonenand_region(mtd, offset);
erasesize = mtd->eraseregions[rgn].erasesize;
} else
erasesize = mtd->erasesize;
memset(ONENAND_CORE(flash) + offset, 0xff, erasesize);
memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
(mtd->erasesize >> 5));
(erasesize >> 5));
break;
default:
@ -339,7 +404,7 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd)
}
if (block != -1)
offset += block << this->erase_shift;
offset = onenand_addr(this, block);
if (page != -1)
offset += page << this->page_shift;
@ -390,6 +455,7 @@ static int __init flash_init(struct onenand_flash *flash)
}
density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
density &= ONENAND_DEVICE_DENSITY_MASK;
size = ((16 << 20) << density);
ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
@ -405,8 +471,9 @@ static int __init flash_init(struct onenand_flash *flash)
writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY);
if (density < 2)
if (density < 2 && (!CONFIG_FLEXONENAND))
buffer_size = 0x0400; /* 1KiB page */
else
buffer_size = 0x0800; /* 2KiB page */

View File

@ -17,6 +17,7 @@
#include <linux/mtd/onenand_regs.h>
#include <linux/mtd/bbm.h>
#define MAX_DIES 2
#define MAX_BUFFERRAM 2
/* Scan and identify a OneNAND device */
@ -51,7 +52,12 @@ struct onenand_bufferram {
/**
* struct onenand_chip - OneNAND Private Flash Chip Data
* @base: [BOARDSPECIFIC] address to access OneNAND
* @dies: [INTERN][FLEX-ONENAND] number of dies on chip
* @boundary: [INTERN][FLEX-ONENAND] Boundary of the dies
* @diesize: [INTERN][FLEX-ONENAND] Size of the dies
* @chipsize: [INTERN] the size of one chip for multichip arrays
* FIXME For Flex-OneNAND, chipsize holds maximum possible
* device size ie when all blocks are considered MLC
* @device_id: [INTERN] device ID
* @density_mask: chip density, used for DDP devices
* @verstion_id: [INTERN] version ID
@ -92,9 +98,13 @@ struct onenand_bufferram {
*/
struct onenand_chip {
void __iomem *base;
unsigned dies;
unsigned boundary[MAX_DIES];
loff_t diesize[MAX_DIES];
unsigned int chipsize;
unsigned int device_id;
unsigned int version_id;
unsigned int technology;
unsigned int density_mask;
unsigned int options;
@ -145,6 +155,8 @@ struct onenand_chip {
#define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0)
#define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
#define FLEXONENAND(this) \
(this->device_id & DEVICE_IS_FLEXONENAND)
#define ONENAND_GET_SYS_CFG1(this) \
(this->read_word(this->base + ONENAND_REG_SYS_CFG1))
#define ONENAND_SET_SYS_CFG1(v, this) \
@ -153,6 +165,9 @@ struct onenand_chip {
#define ONENAND_IS_DDP(this) \
(this->device_id & ONENAND_DEVICE_IS_DDP)
#define ONENAND_IS_MLC(this) \
(this->technology & ONENAND_TECHNOLOGY_IS_MLC)
#ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
#define ONENAND_IS_2PLANE(this) \
(this->options & ONENAND_HAS_2PLANE)
@ -190,5 +205,8 @@ struct onenand_manufacturers {
int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
unsigned onenand_block(struct onenand_chip *this, loff_t addr);
loff_t onenand_addr(struct onenand_chip *this, int block);
int flexonenand_region(struct mtd_info *mtd, loff_t addr);
#endif /* __LINUX_MTD_ONENAND_H */

View File

@ -67,6 +67,9 @@
/*
* Device ID Register F001h (R)
*/
#define DEVICE_IS_FLEXONENAND (1 << 9)
#define FLEXONENAND_PI_MASK (0x3ff)
#define FLEXONENAND_PI_UNLOCK_SHIFT (14)
#define ONENAND_DEVICE_DENSITY_MASK (0xf)
#define ONENAND_DEVICE_DENSITY_SHIFT (4)
#define ONENAND_DEVICE_IS_DDP (1 << 3)
@ -83,6 +86,11 @@
*/
#define ONENAND_VERSION_PROCESS_SHIFT (8)
/*
* Technology Register F006h (R)
*/
#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0)
/*
* Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W)
*/
@ -93,7 +101,8 @@
/*
* Start Address 8 F107h (R/W)
*/
#define ONENAND_FPA_MASK (0x3f)
/* Note: It's actually 0x3f in case of SLC */
#define ONENAND_FPA_MASK (0x7f)
#define ONENAND_FPA_SHIFT (2)
#define ONENAND_FSA_MASK (0x03)
@ -105,7 +114,8 @@
#define ONENAND_BSA_BOOTRAM (0 << 2)
#define ONENAND_BSA_DATARAM0 (2 << 2)
#define ONENAND_BSA_DATARAM1 (3 << 2)
#define ONENAND_BSC_MASK (0x03)
/* Note: It's actually 0x03 in case of SLC */
#define ONENAND_BSC_MASK (0x07)
/*
* Command Register F220h (R/W)
@ -124,9 +134,13 @@
#define ONENAND_CMD_RESET (0xF0)
#define ONENAND_CMD_OTP_ACCESS (0x65)
#define ONENAND_CMD_READID (0x90)
#define FLEXONENAND_CMD_PI_UPDATE (0x05)
#define FLEXONENAND_CMD_PI_ACCESS (0x66)
#define FLEXONENAND_CMD_RECOVER_LSB (0x05)
/* NOTE: Those are not *REAL* commands */
#define ONENAND_CMD_BUFFERRAM (0x1978)
#define FLEXONENAND_CMD_READ_PI (0x1985)
/*
* System Configuration 1 Register F221h (R, R/W)
@ -192,10 +206,12 @@
#define ONENAND_ECC_1BIT_ALL (0x5555)
#define ONENAND_ECC_2BIT (1 << 1)
#define ONENAND_ECC_2BIT_ALL (0xAAAA)
#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010)
/*
* One-Time Programmable (OTP)
*/
#define FLEXONENAND_OTP_LOCK_OFFSET (2048)
#define ONENAND_OTP_LOCK_OFFSET (14)
#endif /* __ONENAND_REG_H */