[ANNOUNCE] [PATCH] [MTD] Flex-OneNAND MTD Driver available.
Kyungmin Park
kmpark at infradead.org
Mon Sep 22 03:11:31 EDT 2008
Hi,
It's another fusion device intergrage SLC & MLC NAND into just one
chip. So I added cc LKML.
It looks good to me except some unclean variable & syntactic usages.
Please clarify it and give more descriptions.
Thank you,
Kyungmin Park
On Fri, Sep 19, 2008 at 9:31 PM, AYYANARPONNUSAMY GANGHEYAMOORTHY
<moorthy.apg at samsung.com> wrote:
> Hi All,
> We are happy to release Flex-OneNAND MTD to open source community.
>
> Brief description of Flex-OneNAND :
> Samsung Flex-OneNAND is a highly reliable embedded memory targeted
> for both consumer electronics, and next generation mobile phone market.
> With Samsung's accumulated NAND flash technologies over the last decade,
> Samsung designs an ideal single memory chip based on NAND architecture
> integrating SRAM buffers and logic interface. Samsung Flex-OneNAND takes
> both advantages from high-speed data read function of NOR flash and the
> advanced data storage function of NAND flash.
>
> Flex-OneNAND combines SLC and MLC technologies into a single device.
> SLC area provides increased reliability and speed, suitable for storing
> code and data, such as bootloader, kernel and root file system.
> MLC area provides high density and is best used for storing user data.
> Users can configure the size of SLC and MLC regions.
>
> Refer (http://www.samsung.com/global/business/semiconductor/products/fusionmemory/Products_FlexOneNAND.html)
>
> Existing MTD does not allow for erase regions with odd number
> of blocks(partitions turn read only). So it is better to set
> the die boundary to odd values. Die boundary setting can be
> done in include/mtd/onenand.h
>
> A partition, theoretically, can span across SLC and MLC regions.
> There is no apparent advantage with it however. Also, such a
> partition will have MLC erase size, with two SLC blocks erased at
> a time during operation. If the second block goes bad, we end up
> marking the first block as bad. So, again, it is not advisable
> to create partitions across erase regions.
>
> Apart from this patch, some more posts for Flex-OneNAND support are
> 1. JFFS2 [PATCH 1/1] [MTD] [JFFS2] MLC NAND support
> [PATCH 1/2] [MTD] [JFFS2] MLC NAND support
>
> 2. MTD-UTILS [PATCH] [MTD-UTILS] Add support for 4KB page flash devices
>
> 3. UBOOT [U-Boot] [PATCH] FlexOneNAND support in U-Boot
> (Posted in http://lists.denx.de/mailman/listinfo/u-boot)
>
> Signed-off-by: Vishak G <vishak.g at samsung.com>
> Signed-off-by: Rohit Hagargundgi <h.rohit at samsung.com>
> ---
> diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
> index 926cf3a..42c19cb 100644
> --- a/drivers/mtd/onenand/onenand_base.c
> +++ b/drivers/mtd/onenand/onenand_base.c
> @@ -9,6 +9,10 @@
> * auto-placement support, read-while load support, various fixes
> * Copyright (C) Nokia Corporation, 2007
> *
> + * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
> + * Flex-OneNAND 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.
> @@ -27,6 +31,37 @@
>
> #include <asm/io.h>
>
> +static int boundary[] = {
> + FLEXONENAND_DIE0_BOUNDARY,
> + FLEXONENAND_DIE1_BOUNDARY,
> +};
> +
> +static int lock[] = {
> + FLEXONENAND_DIE0_ISLOCKED,
> + FLEXONENAND_DIE1_ISLOCKED,
> +};
Is it really needed? and is it changed at runtime? If not please add 'const'.
> +
> +/**
> + * onenand_oob_128 - oob info for Flex-Onenand with 4KB page
> + */
> +static struct nand_ecclayout onenand_oob_128 = {
> + .eccbytes = 80,
> + .eccpos = {
> + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
> + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
> + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
> + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
> + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
> + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
> + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
> + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127
> + },
> + .oobfree = {
> + {2, 4}, {16, 6}, {32, 6}, {48, 6},
> + {64, 6}, {80, 6}, {96, 6}, {112, 6}
> + }
> +};
Even though we use 2nd Bad block Information (BI) we leave it. It uses
provided spare area. As you know, it's enough.
> if (page != -1) {
> /* Now we use page size operation */
> - int sectors = 4, count = 4;
> + int sectors = 0, count = 0;
> int dataram;
The '0' means all sectors.
>
> +/**
> + * flexonenand_unlock_all - [Flex-OneNAND Interface] unlock all blocks
> + * @param mtd MTD device structure
> + *
> + * Unlock all blocks
> + */
> +static int flexonenand_unlock_all(struct mtd_info *mtd)
> +{
> + struct onenand_chip *this = mtd->priv;
> + size_t len = mtd->erasesize;
> +
> + if (mtd->numeraseregions > 1)
> + len >>= 1;
> +
> + onenand_do_lock_cmd(mtd, 0, len, ONENAND_CMD_UNLOCK_ALL);
> + if (ONENAND_IS_DDP(this))
> + onenand_do_lock_cmd(mtd, this->diesize[0], len,
> + ONENAND_CMD_UNLOCK_ALL);
> + onenand_check_lock_status(this);
> + return 0;
> +}
Does it need to define unlock all for Flex-OneNAND?
I think it's same between OneNAND and Flex-OneNAND. Is it some chip
specific problem?
> +
> #ifdef CONFIG_MTD_ONENAND_OTP
>
> - ret = onenand_write_oob_nolock(mtd, from, &ops);
> + ret = FLEXONENAND(this) ?
> + onenand_write_ops_nolock(mtd, (mtd->writesize * 49), &ops)
> + : onenand_write_oob_nolock(mtd, from, &ops);
Please describe why we multiply the '49' as mentioned below.
> @@ -2428,25 +2680,32 @@
> size_t len)
> {
> struct onenand_chip *this = mtd->priv;
> - u_char *oob_buf = this->oob_buf;
> + u_char *oob_buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
How about the define 'buf' instead of 'oob_buf'.
> size_t retlen;
> int ret;
>
> - memset(oob_buf, 0xff, mtd->oobsize);
> + memset(oob_buf, 0xff, FLEXONENAND(this) ? this->writesize
> + : mtd->oobsize);
> /*
> * Note: OTP lock operation
> * OTP block : 0xXXFC
> * 1st block : 0xXXF3 (If chip support)
> * Both : 0xXXF0 (If chip support)
> */
> - oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
> + if (FLEXONENAND(this))
> + oob_buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
> + else
> + oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
>
> /*
> * Write lock mark to 8th word of sector0 of page0 of the spare0.
> * We write 16 bytes spare area instead of 2 bytes.
> + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
> + * main area of page 49.
> */
> +
> from = 0;
> - len = 16;
> + len = FLEXONENAND(this) ? mtd->writesize : 16;
>
> ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER);
>
> @@ -2495,6 +2754,14 @@
> break;
> }
>
>
> ddp = device & ONENAND_DEVICE_IS_DDP;
> density = onenand_get_density(device);
> - printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
> - demuxed ? "" : "Muxed ",
> + flexonenand = device & DEVICE_IS_FLEXONENAND;
> + printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
> + flexonenand ? "Flex-" : "",
> + demuxed ? "" : "Mux",
Please display pin type and Flex such as, Muxed {Flex-}OneNAND
> ddp ? "(DDP)" : "",
> (16 << density),
> vcc ? "2.65/3.3" : "1.8",
> device);
> - printk(KERN_INFO "OneNAND version = 0x%04x\n", version);
> + printk(KERN_INFO "%sOneNAND version = 0x%04x\n",
> + flexonenand ? "Flex-" : "", version);
Does it need to display Flex here?
> }
>
> static const struct onenand_manufacturers onenand_manuf_ids[] = {
> @@ -2558,6 +2828,181 @@
> }
>
> /**
> +* flexonenand_get_boundary - Reads the SLC boundary
> +* @param onenand_info - onenand info structure
> +**/
> +static int flexonenand_get_boundary(struct mtd_info *mtd)
> +{
> + struct onenand_chip *this = mtd->priv;
> + unsigned die, bdry;
> + int ret, syscfg, locked;
> +
> + /* Disable ECC */
> + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
> + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
> +
> + for (die = 0; die < this->dies; die++) {
> + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
> + this->wait(mtd, FL_SYNCING);
> +
> + this->command(mtd, ONENAND_CMD_READ, die, 0);
> + ret = this->wait(mtd, FL_READING);
> +
> + bdry = this->read_word(this->base + ONENAND_DATARAM);
> + locked = bdry >> FLEXONENAND_PI_UNLOCK_SHIFT;
> + locked = (locked == 0x3) ? 0 : 1;
> + this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
> + this->boundary_locked[die] = locked;
> + this->command(mtd, ONENAND_CMD_RESET, 0, 0);
> + ret = this->wait(mtd, FL_RESETING);
> +
> + printk(KERN_INFO "Die %d boundary: %d%s\n", die,
> + this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
> + }
> +
> + /* Enable ECC */
> + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
> + return 0;
> +}
> +
> +/**
> + * get_flexonenand_size - Fill up fields in onenand_chip
> + * boundary[], diesize[], chipsize,
> + * boundary_locked[]
> + * @param mtd - MTD device structure
> + */
> +void get_flexonenand_size(struct mtd_info *mtd)
Please make it 'static'.
> +{
> + struct onenand_chip *this = mtd->priv;
> /* Pages per a block are always 64 in OneNAND */
> mtd->erasesize = mtd->writesize << 6;
> -
> + mtd->erasesize <<= FLEXONENAND(this) ? 1 : 0;
Please add description Flex-OneNAND has always 128 pages per a block.
> this->erase_shift = ffs(mtd->erasesize) - 1;
> this->page_shift = ffs(mtd->writesize) - 1;
> this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
> @@ -2632,7 +3092,20 @@
>
> /* REVIST: Multichip handling */
>
> - mtd->size = this->chipsize;
> + if (FLEXONENAND(this)) {
> + unsigned die;
> +
> + get_flexonenand_size(mtd);
> +
> + /* Change the device boundaries if required */
> + for (die = 0; die < this->dies; die++)
> + if ((!this->boundary_locked[die]) &&
> + (boundary[die] >= 0) &&
> + (boundary[die] != this->boundary[die]))
> + flexonenand_set_boundary(mtd, die,
> + boundary[die], lock[die]);
> + } else
> + mtd->size = this->chipsize;
>
> /* Check OneNAND features */
> onenand_check_features(mtd);
> @@ -2749,6 +3222,10 @@
> * Allow subpage writes up to oobsize.
> */
> switch (mtd->oobsize) {
> + case 128:
> + this->ecclayout = &onenand_oob_128;
> + mtd->subpage_sft = 0;
> + break;
> case 64:
> this->ecclayout = &onenand_oob_64;
> mtd->subpage_sft = 2;
> @@ -2768,6 +3245,10 @@
> break;
> }
>
> + /* Don't allow the sub-page write in MLC */
> + if (ONENAND_IS_MLC(this))
> + mtd->subpage_sft = 0;
> +
> this->subpagesize = mtd->writesize >> mtd->subpage_sft;
>
> /*
> @@ -2812,7 +3293,8 @@
> mtd->owner = THIS_MODULE;
>
> /* Unlock whole block */
> - onenand_unlock_all(mtd);
> + FLEXONENAND(this) ? flexonenand_unlock_all(mtd)
> + : onenand_unlock_all(mtd);
>
> return this->scan_bbt(mtd);
> }
> @@ -2843,6 +3325,8 @@
> kfree(this->page_buf);
> if (this->options & ONENAND_OOBBUF_ALLOC)
> kfree(this->oob_buf);
> + if (FLEXONENAND(this))
> + kfree(mtd->eraseregions);
> }
>
> EXPORT_SYMBOL_GPL(onenand_scan);
> diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
> --- a/drivers/mtd/onenand/onenand_bbt.c
> +++ b/drivers/mtd/onenand/onenand_bbt.c
> @@ -60,6 +60,7 @@
> struct bbm_info *bbm = this->bbm;
> int i, j, numblocks, len, scanlen;
> int startblock;
> + unsigned slc;
> loff_t from;
> size_t readlen, ooblen;
> struct mtd_oob_ops ops;
Initialize the slc as '0'.
> @@ -76,7 +77,7 @@
> /* 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,13 @@
> }
> }
> i += 2;
> - from += (1 << bbm->bbt_erase_shift);
> + if (FLEXONENAND(this)) {
> + onenand_get_block(mtd, from, &slc);
> + from += (1 << bbm->bbt_erase_shift) >> 1;
> + if (!slc)
> + from += (1 << bbm->bbt_erase_shift) >> 1;
> + } else
> + from += (1 << bbm->bbt_erase_shift);
> }
>
> return 0;
> @@ -143,7 +150,7 @@
> uint8_t res;
>
> /* Get block number * 2 */
> - block = (int) (offs >> (bbm->bbt_erase_shift - 1));
> + block = (int) (onenand_get_block(mtd, offs, NULL) << 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 +185,7 @@
> 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) {
> diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
> --- a/include/linux/mtd/onenand.h
> +++ b/include/linux/mtd/onenand.h
> @@ -17,8 +17,24 @@
> #include <linux/mtd/onenand_regs.h>
> #include <linux/mtd/bbm.h>
>
> +#define MAX_DIES 2
> #define MAX_BUFFERRAM 2
>
> +/**
> + * FlexOneNAND device boundary setting
> + * Setting -1 will not change the boundary
> + */
> +#define FLEXONENAND_DIE0_BOUNDARY -1
> +#define FLEXONENAND_DIE1_BOUNDARY -1
> +
> +/**
> + * Setting value 1 locks the boundary
> + * WARNING : Once locked, the boundary cannot be changed.
> + * Use with care.
> + */
> +#define FLEXONENAND_DIE0_ISLOCKED 0
> +#define FLEXONENAND_DIE1_ISLOCKED 0
> +
> /* Scan and identify a OneNAND device */
> extern int onenand_scan(struct mtd_info *mtd, int max_chips);
> /* Free resources held by the OneNAND device */
> @@ -51,6 +67,11 @@
> /**
> * 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
> + * @boundary_locked: [INTERN][FLEX-ONENAND] TRUE indicates die boundary
> + * is locked and cannot be changed
> + * @diesize: [INTERN][FLEX-ONENAND] Size of the dies
> * @chipsize: [INTERN] the size of one chip for multichip arrays
> * @device_id: [INTERN] device ID
> * @density_mask: chip density, used for DDP devices
> @@ -92,9 +113,14 @@
> */
> struct onenand_chip {
> void __iomem *base;
> + unsigned dies;
> + unsigned boundary[MAX_DIES];
> + unsigned int boundary_locked[MAX_DIES];
> + unsigned int 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 +171,8 @@
> #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 +181,9 @@
> #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)
> @@ -189,5 +220,7 @@
>
> int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
> struct mtd_oob_ops *ops);
> +unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr,
> + unsigned *isblkslc);
>
> #endif /* __LINUX_MTD_ONENAND_H */
> diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
> --- a/include/linux/mtd/onenand_regs.h
> +++ b/include/linux/mtd/onenand_regs.h
> @@ -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)
> @@ -84,6 +87,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)
> */
> #define ONENAND_DDP_SHIFT (15)
> @@ -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,6 +134,9 @@
> #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)
> @@ -190,10 +203,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 */
> diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
> --- a/include/mtd/mtd-abi.h
> +++ b/include/mtd/mtd-abi.h
> @@ -102,7 +102,11 @@
> uint32_t useecc;
> uint32_t eccbytes;
> uint32_t oobfree[8][2];
> +#ifdef CONFIG_MTD_ONENAND
> + uint32_t eccpos[128];
> +#else
> uint32_t eccpos[32];
> +#endif
> };
>
> struct nand_oobfree {
> @@ -117,7 +121,11 @@
> */
> struct nand_ecclayout {
> uint32_t eccbytes;
> +#ifdef CONFIG_MTD_ONENAND
> + uint32_t eccpos[128];
> +#else
> uint32_t eccpos[64];
> +#endif
> uint32_t oobavail;
> struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
> };
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>
More information about the linux-mtd
mailing list