add the GPMI controller driver for IMX23/IMX28
Subsystems (2)
v10 cover add the GPMI controller driver for IMX23/IMX28
The patch set is based on Artem's tree:
http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
The general-purpose media interface(GPMI) controller is a flexible interface to up to several NAND flashs.
The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
With the help of BCH, the GPMI controller can choose to do the hardware ECC or not.
This driver is a _pure_ MTD NAND controller driver now.
The driver depends on another GPMI-NAND device patch set, you can find them at :
[1] http://marc.info/?l=linux-arm-kernel&m=131416901319573&w=2 [2] http://marc.info/?l=linux-arm-kernel&m=131416912319668&w=2 [3] http://marc.info/?l=linux-arm-kernel&m=131416891119504&w=2 [4] http://marc.info/?l=linux-arm-kernel&m=131416896219539&w=2
Test environment:
Using imx23 and imx28 boards for test.
imx23 :
console=ttyAMA0,115200 mtdparts=gpmi-nfc:20m(boot),-(user)
Tested by USB boot and NAND boot.
imx28 :
#console=ttyAMA0,115200 root=/dev/mmcblk0p3 rw rootwait
Tested by SD card boot mode.v9 --> v10:
[0] fix DMA timeout bug by adding custom routine for reset blocks.
[1] remove mil{}
[2] add gpmi_hw_ecclayout.
[3] add ecc_write_oob() again to prohibit the mtd ioctl : MEMWRITEOOB
[4] change pr_info to dev_dbg() or pr_err().
[5] merge the all the dump functions into gpmi_dump_info().
[6] remove parse_mtd_partitions(), and use mtd_device_parse_register().
[7] move the the debug control code from gpmi-nand.h to gpmi-nand.c
[8] renames
[9] others
v8 --> v9:
[0] remove the ONFI nand code, it will cause the DMA timeout in the ONFI nand.
[1] remove sysfs entry.
[2] remove kernel command parameter.
[3] report to ecclayout that we will use all the page + OOB. remove the ->ecc_write_oob().
[4] add our own block_markbad() hook.
[5] rename some functions from mil_* to gpmi_*.
[6] replace the __raw_readl/__raw_writel to readl/writel.
[7] others
v7 --> v8:
[0] rename the name from GPMI-NFC to GPMI-NAND
[1] remove the hal layer, and change it to function library.
[2] Do not use ~0 to initialize the DMA address.
[3] fix the issue : several DMA channels share the same IRQ.
[4] DMA timeout issue. I use the .config created by make mxs_defconfig and the bug never occur. It seems some other module has impact to the DMA.
Of course, you should enable the UBIFS,MTD_CHAR and GPMI for the .config. You can also disable the SPIN-LOCK/MUTEX debug features.
[5] add function to print out the GPMI registers.
[6] others.
v6 --> v7:
[0] remove the function wrapping the clock.
[1] use the module_param() for debugging.
[2] use the new interface of MTD partitions.
replace add_mtd_partitions() with mtd_device_register().
[3] use pr_info() to print log.
[4] add __devinit for some functions, etc.
[5] add gpmi_nfc to control the GPMI-NFC driver's initialization.
[6] others.
v5 --> v6:
[0] split out the IMX23/IMX28 arch code to separate patches.
[1] fix bug : missing empty item in the end of platform_id array.
[2] inconsistent identation.
[3] others
v4 --> v5:
[0] rename the files.
[1] fix PM bug
[2] remove the rom_helper code, and move the necessary initialization code to the main file.
[3] change the macros from CPU_IS_* to GPMI_IS_*
[4] remove the default partition layout init code. revert back the partition parsing command line code.
[5] others
v3 --> v4:
[0] use the nand_ids{} as the nand database, drop my own database.
[1] remove the patch for DMA enginer, Shawn will submit his own version.
[2] use the platform_id to distinguish different Archs.
[3] fix the strange coding style.
[4] others.
v2 --> v3:
[0] merge the imx23 and imx28 into one file(including the header file).
[1] remove the unuse registers in the headers.
[2] fix DMA bugs
[3] add bus width field to nand_attr{}
[4] others
v1 --> v2:
[0] merge the common files into the gpmi-nfc-main.c
[1] change the code to get the clock.
[2] remove the timing in the nand_device_info{}
[3] fix DMA errors
[4] add the nand_device_info.[ch] to generic code
[5] use the chip->onfi_version for the ONFI nand
[6] useless init
[7] others
Huang Shijie (3):
MTD : add the common code for GPMI-NAND controller driver MTD : add helper functions library and header files for GPMI NAND
driver
MTD : add GPMI-NAND driver in the config and Makefile
drivers/mtd/nand/Kconfig | 13 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/gpmi-nand/Makefile | 3 + drivers/mtd/nand/gpmi-nand/bch-regs.h | 84 ++ drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 1056 ++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1674 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 274 ++++++ drivers/mtd/nand/gpmi-nand/gpmi-regs.h | 168 ++++ 8 files changed, 3273 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/gpmi-nand/Makefile create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.c create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.h create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h
PATCH v10 1/3 MTD : add the common code for GPMI-NAND controller driver
These files contain the common code for the GPMI-NAND driver.
--- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1674 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 274 ++++++ 2 files changed, 1948 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.c create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.h
drivers/mtd/nand/gpmi-nand/gpmi-nand.c new file +1674 -0
@@ -0,0 +1,1674 @@ +/* +* Freescale GPMI NAND Flash Driver +* +* Copyright (C) 2010-2011 Freescale Semiconductor, Inc. +* Copyright (C) 2008 Embedded Alley Solutions, Inc. +* +* This program is free software; you can redistribute it and/or modify +* it 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/mtd/gpmi-nand.h> +#include <linux/mtd/partitions.h> + +#include "gpmi-nand.h" + +/* add our owner bbt descriptor */ +static uint8_t scan_ff_pattern[] = {0xff }; +static struct nand_bbt_descr gpmi_bbt_descr = { + .options =0 , + .offs =0 , + .len =1 , + .pattern = scan_ff_pattern +}; + +/* We will use all the (page + OOB). */ +static struct nand_ecclayout gpmi_hw_ecclayout = { + .eccbytes =0 , + .eccpos = {0 , }, + .oobfree = { {.offset =0 , .length =0 } } +}; + +/* debug control */ +#define GPMI_DEBUG_READ_OOB 0x0001 +#define GPMI_DEBUG_READ 0x0002 +#define GPMI_DEBUG_WRITE 0x0004 +#define GPMI_DEBUG_ECC_READ 0x0008 +#define GPMI_DEBUG_ECC_WRITE 0x0010 +#define GPMI_DEBUG_VERBOSE 0x0020 + +#define logio (level) \ + do { \ + if (gpmi_debug & level) \ + pr_info("%s : %d\n", __func__, __LINE__); \ + } while (0) + +int gpmi_debug; +module_param (gpmi_debug, int,S_IRUGO |S_IWUSR ); +MODULE_PARM_DESC (gpmi_debug,"print out the debug info." ); + +static irqreturn_t bch_irq (int irq,void *cookie) +{ +struct gpmi_nand_data *this = cookie; + +gpmi_clear_bch (this); +complete (&this->bch_done); +return IRQ_HANDLED ; +} + +/* calculate the ECC strength by hand */ +static inline int get_ecc_strength (struct gpmi_nand_data *this) +{ +struct mtd_info *mtd = &this->mtd; +int ecc_strength =0 ; + +switch (mtd->writesize) { +case 2048 : + ecc_strength =8 ; +break ; +case 4096 : +switch (mtd->oobsize) { +case 128 : + ecc_strength =8 ; +break ; +case 224 : +case 218 : + ecc_strength =16 ; +break ; + } +break ; +case 8192 : + ecc_strength =24 ; +break ; + } + +return ecc_strength; +} + +int common_nfc_set_geometry (struct gpmi_nand_data *this) +{ +struct bch_geometry *geo = &this->bch_geometry; +struct mtd_info *mtd = &this->mtd; +unsigned int metadata_size; +unsigned int status_size; +unsigned int chunk_data_size_in_bits; +unsigned int chunk_ecc_size_in_bits; +unsigned int chunk_total_size_in_bits; +unsigned int block_mark_chunk_number; +unsigned int block_mark_chunk_bit_offset; +unsigned int block_mark_bit_offset; +int gf_len =13 ;/* use GP13 by default */ + +/* We only support BCH now. */ + geo->ecc_algorithm ="BCH" ; + +/* +* The size of the metadata can be changed, though we set it to 10 +* bytes now. But it can't be too large, because we have to save +* enough space for BCH. +*/ + geo->metadata_size_in_bytes =10 ; + +/* ECC chunks, used by default. */ + geo->ecc_chunk_size_in_bytes =512 ; + +/* +* Compute the total number of ECC chunks in a page. This includes the +* slightly larger chunk at the beginning of the page, which contains +* both data and metadata. +*/ + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size_in_bytes; + +/* +* We use the same ECC strength for all chunks, including the first one. +*/ + geo->ecc_strength =get_ecc_strength (this); +if (!geo->ecc_strength) { +pr_err ("Page size:%d, OOB:%d\n" , mtd->writesize, mtd->oobsize); +return -EINVAL ; + } + +/* Compute the page size, include page and oob. */ + geo->page_size_in_bytes = mtd->writesize + mtd->oobsize; + geo->payload_size_in_bytes = mtd->writesize; + +/* +* In principle, computing the auxiliary buffer geometry is NFC +* version-specific. However, at this writing, all versions share the +* same model, so this code can also be shared. +* +* The auxiliary buffer contains the metadata and the ECC status. The +* metadata is padded to the nearest 32-bit boundary. The ECC status +* contains one byte for every ECC chunk, and is also padded to the +* nearest 32-bit boundary. +*/ + metadata_size =ALIGN (geo->metadata_size_in_bytes,4 ); + status_size =ALIGN (geo->ecc_chunk_count,4 ); + + geo->auxiliary_size_in_bytes = metadata_size + status_size; + geo->auxiliary_status_offset = metadata_size; + +/* Check if we're going to do block mark swapping. */ +if (!this->swap_block_mark) +return 0 ; + +/* +* If control arrives here, we're doing block mark swapping, so we need +* to compute the byte and bit offsets of the physical block mark within +* the ECC-based view of the page data. In principle, this isn't a +* difficult computation -- but it's very important and it's easy to get +* it wrong, so we do it carefully. +* +* Note that this calculation is simpler because we use the same ECC +* strength for all chunks, including the zero'th one, which contains +* the metadata. The calculation would be slightly more complicated +* otherwise. +* +* We start by computing the physical bit offset of the block mark. We +* then subtract the number of metadata and ECC bits appearing before +* the mark to arrive at its bit offset within the data alone. +*/ + +/* Compute some important facts about chunk geometry. */ + chunk_data_size_in_bits = geo->ecc_chunk_size_in_bytes *8 ; + chunk_ecc_size_in_bits = geo->ecc_strength * gf_len; + chunk_total_size_in_bits = chunk_data_size_in_bits + + chunk_ecc_size_in_bits; + +/* Compute the bit offset of the block mark within the physical page. */ + block_mark_bit_offset = mtd->writesize *8 ; + +/* Subtract the metadata bits. */ + block_mark_bit_offset -= geo->metadata_size_in_bytes *8 ; + +/* +* Compute the chunk number (starting at zero) in which the block mark +* appears. +*/ + block_mark_chunk_number = + block_mark_bit_offset / chunk_total_size_in_bits; + +/* +* Compute the bit offset of the block mark within its chunk, and +* validate it. +*/ + block_mark_chunk_bit_offset = + block_mark_bit_offset - + (block_mark_chunk_number * chunk_total_size_in_bits); + +if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) { +/* +* If control arrives here, the block mark actually appears in +* the ECC bits of this chunk. This wont' work. +*/ +pr_err ("Unsupported page geometry : %u:%u\n" , + mtd->writesize, mtd->oobsize); +return -EINVAL ; + } + +/* +* Now that we know the chunk number in which the block mark appears, +* we can subtract all the ECC bits that appear before it. +*/ + block_mark_bit_offset -= + block_mark_chunk_number * chunk_ecc_size_in_bits; + +/* +* We now know the absolute bit offset of the block mark within the +* ECC-based data. We can now compute the byte offset and the bit +* offset within the byte. +*/ + geo->block_mark_byte_offset = block_mark_bit_offset /8 ; + geo->block_mark_bit_offset = block_mark_bit_offset %8 ; + +return 0 ; +} + +struct dma_chan *get_dma_chan (struct gpmi_nand_data *this) +{ +int chipnr = this->current_chip; + +BUG_ON (chipnr <0 ); +return this->dma_chans[chipnr]; +} + +/* Can we use the upper's buffer directly for DMA? */ +void prepare_data_dma (struct gpmi_nand_data *this,enum dma_data_direction dr) +{ +struct scatterlist *sgl = &this->data_sgl; +int ret; + + this->direct_dma_map_ok = true; + +/* first try to map the upper buffer directly */ +sg_init_one (sgl, this->upper_buf, this->upper_len); + ret =dma_map_sg (this->dev, sgl,1 , dr); +if (ret ==0 ) { +/* We have to use our own DMA buffer. */ +sg_init_one (sgl, this->data_buffer_dma,PAGE_SIZE ); + +if (dr ==DMA_TO_DEVICE ) +memcpy (this->data_buffer_dma, this->upper_buf, + this->upper_len); + + ret =dma_map_sg (this->dev, sgl,1 , dr); +BUG_ON (ret ==0 ); + + this->direct_dma_map_ok = false; + } +} + +/* This will be called after the DMA operation is finished. */ +static void dma_irq_callback (void *param) +{ +struct gpmi_nand_data *this = param; +struct completion *dma_c = &this->dma_done; + +complete (dma_c); + +switch (this->dma_type) { +case DMA_FOR_COMMAND : +dma_unmap_sg (this->dev, &this->cmd_sgl,1 ,DMA_TO_DEVICE ); +break ; + +case DMA_FOR_READ_DATA : +dma_unmap_sg (this->dev, &this->data_sgl,1 ,DMA_FROM_DEVICE ); +if (this->direct_dma_map_ok == false) +memcpy (this->upper_buf, this->data_buffer_dma, + this->upper_len); +break ; + +case DMA_FOR_WRITE_DATA : +dma_unmap_sg (this->dev, &this->data_sgl,1 ,DMA_TO_DEVICE ); +break ; + +case DMA_FOR_READ_ECC_PAGE : +case DMA_FOR_WRITE_ECC_PAGE : +/* We have to wait the BCH interrupt to finish. */ +break ; + +default : +BUG (); + } +} + +int start_dma_without_bch_irq (struct gpmi_nand_data *this, +struct dma_async_tx_descriptor *desc) +{ +struct completion *dma_c = &this->dma_done; +int err; + +init_completion (dma_c); + + desc->callback = dma_irq_callback; + desc->callback_param = this; +dmaengine_submit (desc); + +/* Wait for the interrupt from the DMA block. */ + err =wait_for_completion_timeout (dma_c,msecs_to_jiffies (1000 )); + err = (!err) ? -ETIMEDOUT :0 ; +if (err) { +pr_err ("DMA timeout, last DMA :%d\n" , this->last_dma_type); +if (gpmi_debug &GPMI_DEBUG_VERBOSE ) +gpmi_dump_info (this); + } +return err; +} + +/* +* This function is used in BCH reading or BCH writing pages. +* It will wait for the BCH interrupt as long as ONE second. +* Actually, we must wait for two interrupts : +* [1] firstly the DMA interrupt and +* [2] secondly the BCH interrupt. +*/ +int start_dma_with_bch_irq (struct gpmi_nand_data *this, +struct dma_async_tx_descriptor *desc) +{ +int err; + +/* Prepare to receive an interrupt from the BCH block. */ +init_completion (&this->bch_done); + +/* start the DMA */ +start_dma_without_bch_irq (this, desc); + +/* Wait for the interrupt from the BCH block. */ + err =wait_for_completion_timeout (&this->bch_done, +msecs_to_jiffies (1000 )); + err = (!err) ? -ETIMEDOUT :0 ; +if (err) { +pr_err ("BCH timeout!\n" ); +if (gpmi_debug &GPMI_DEBUG_VERBOSE ) +gpmi_dump_info (this); + } +return err; +} + +static int __devinit +acquire_register_block (struct gpmi_nand_data *this,const char *res_name) +{ +struct platform_device *pdev = this->pdev; +struct resources *res = &this->resources; +struct resource *r; +void *p; + + r =platform_get_resource_byname (pdev,IORESOURCE_MEM , res_name); +if (!r) { +pr_err ("Can't get resource for %s\n" , res_name); +return -ENXIO ; + } + + p =ioremap (r->start,resource_size (r)); +if (!p) { +pr_err ("Can't remap %s\n" , res_name); +return -ENOMEM ; + } + +if (res_name ==GPMI_NAND_GPMI_REGS_ADDR_RES_NAME ) + res->gpmi_regs = p; +else if (res_name ==GPMI_NAND_BCH_REGS_ADDR_RES_NAME ) + res->bch_regs = p; +else +BUG (); + +return 0 ; +} + +static void release_register_block (struct gpmi_nand_data *this) +{ +struct resources *res = &this->resources; +if (res->gpmi_regs) +iounmap (res->gpmi_regs); +if (res->bch_regs) +iounmap (res->bch_regs); + res->gpmi_regs =NULL ; + res->bch_regs =NULL ; +} + +static int __devinit +acquire_bch_irq (struct gpmi_nand_data *this,irq_handler_t irq_h) +{ +struct platform_device *pdev = this->pdev; +struct resources *res = &this->resources; +const char *res_name =GPMI_NAND_BCH_INTERRUPT_RES_NAME ; +struct resource *r; +int err; + + r =platform_get_resource_byname (pdev,IORESOURCE_IRQ , res_name); +if (!r) { +pr_err ("Can't get resource for %s\n" , res_name); +return -ENXIO ; + } + +BUG_ON (r->start != r->end); + err =request_irq (r->start, irq_h,0 , res_name, this); +if (err) { +pr_err ("Can't own %s\n" , res_name); +return err; + } + + res->bch_low_interrupt = r->start; + res->bch_high_interrupt = r->end; +return 0 ; +} + +static void release_bch_irq (struct gpmi_nand_data *this) +{ +struct resources *res = &this->resources; +int i = res->bch_low_interrupt; + +for (; i <= res->bch_high_interrupt; i++) +free_irq (i, this); +} + +static bool gpmi_dma_filter (struct dma_chan *chan,void *param) +{ +struct gpmi_nand_data *this = param; +struct resource *r = this->private; + +if (!mxs_dma_is_apbh (chan)) +return false; +/* +* only catch the GPMI dma channels : +* for mx23 : MX23_DMA_GPMI0 ~ MX23_DMA_GPMI3 +* (These four channels share the same IRQ!) +* +* for mx28 : MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7 +* (These eight channels share the same IRQ!) +*/ +if (r->start <= chan->chan_id && chan->chan_id <= r->end) { + chan->private = &this->dma_data; +return true; + } +return false; +} + +static void release_dma_channels (struct gpmi_nand_data *this) +{ +unsigned int i; +for (i =0 ; i <DMA_CHANS ; i++) +if (this->dma_chans[i]) { +dma_release_channel (this->dma_chans[i]); + this->dma_chans[i] =NULL ; + } +} + +static int __devinit acquire_dma_channels (struct gpmi_nand_data *this) +{ +struct platform_device *pdev = this->pdev; +struct gpmi_nand_platform_data *pdata = this->pdata; +struct resources *res = &this->resources; +struct resource *r, *r_dma; +unsigned int i; + + r =platform_get_resource_byname (pdev,IORESOURCE_DMA , +GPMI_NAND_DMA_CHANNELS_RES_NAME ); + r_dma =platform_get_resource_byname (pdev,IORESOURCE_IRQ , +GPMI_NAND_DMA_INTERRUPT_RES_NAME ); +if (!r || !r_dma) { +pr_err ("Can't get resource for DMA\n" ); +return -ENXIO ; + } + +/* used in gpmi_dma_filter() */ + this->private = r; + +for (i = r->start; i <= r->end; i++) { +struct dma_chan *dma_chan; +dma_cap_mask_t mask; + +if (i - r->start >= pdata->max_chip_count) +break ; + +dma_cap_zero (mask); +dma_cap_set (DMA_SLAVE , mask); + +/* get the DMA interrupt */ +if (r_dma->start == r_dma->end) { +/* only register the first. */ +if (i == r->start) + this->dma_data.chan_irq = r_dma->start; +else + this->dma_data.chan_irq =NO_IRQ ; + }else + this->dma_data.chan_irq = r_dma->start + (i - r->start); + + dma_chan =dma_request_channel (mask, gpmi_dma_filter, this); +if (!dma_chan) + goto acquire_err; + +/* fill the first empty item */ + this->dma_chans[i - r->start] = dma_chan; + } + + res->dma_low_channel = r->start; + res->dma_high_channel = i; +return 0 ; + +acquire_err: +pr_err ("Can't acquire DMA channel %u\n" , i); +release_dma_channels (this); +return -EINVAL ; +} + +static int __devinit acquire_resources (struct gpmi_nand_data *this) +{ +struct resources *res = &this->resources; +int ret; + + ret =acquire_register_block (this,GPMI_NAND_GPMI_REGS_ADDR_RES_NAME ); +if (ret) + goto exit_regs; + + ret =acquire_register_block (this,GPMI_NAND_BCH_REGS_ADDR_RES_NAME ); +if (ret) + goto exit_regs; + + ret =acquire_bch_irq (this, bch_irq); +if (ret) + goto exit_regs; + + ret =acquire_dma_channels (this); +if (ret) + goto exit_dma_channels; + + res->clock =clk_get (&this->pdev->dev,NULL ); +if (IS_ERR (res->clock)) { +pr_err ("can not get the clock\n" ); + ret = -ENOENT ; + goto exit_clock; + } +return 0 ; + +exit_clock: +release_dma_channels (this); +exit_dma_channels: +release_bch_irq (this); +exit_regs: +release_register_block (this); +return ret; +} + +static void release_resources (struct gpmi_nand_data *this) +{ +struct resources *r = &this->resources; + +clk_put (r->clock); +release_register_block (this); +release_bch_irq (this); +release_dma_channels (this); +} + +static int __devinit init_hardware (struct gpmi_nand_data *this) +{ +int ret; + +/* +* This structure contains the "safe" GPMI timing that should succeed +* with any NAND Flash device +* (although, with less-than-optimal performance). +*/ +struct nand_timing safe_timing = { + .data_setup_in_ns =80 , + .data_hold_in_ns =60 , + .address_setup_in_ns =25 , + .gpmi_sample_delay_in_ns =6 , + .tREA_in_ns =-1 , + .tRLOH_in_ns =-1 , + .tRHOH_in_ns =-1 , + }; + +/* Initialize the hardwares. */ + ret =gpmi_init (this); +if (ret) +return ret; + + this->timing = safe_timing; +return 0 ; +} + +static int read_page_prepare (struct gpmi_nand_data *this, +void *destination,unsigned length, +void *alt_virt,dma_addr_t alt_phys,unsigned alt_size, +void **use_virt,dma_addr_t *use_phys) +{ +struct device *dev = this->dev; + +if (virt_addr_valid (destination)) { +dma_addr_t dest_phys; + + dest_phys =dma_map_single (dev, destination, + length,DMA_FROM_DEVICE ); +if (dma_mapping_error (dev, dest_phys)) { +if (alt_size < length) { +pr_err ("Alternate buffer is too small\n" ); +return -ENOMEM ; + } + goto map_failed; + } + *use_virt = destination; + *use_phys = dest_phys; + this->direct_dma_map_ok = true; +return 0 ; + } + +map_failed: + *use_virt = alt_virt; + *use_phys = alt_phys; + this->direct_dma_map_ok = false; +return 0 ; +} + +static inline void read_page_end (struct gpmi_nand_data *this, +void *destination,unsigned length, +void *alt_virt,dma_addr_t alt_phys,unsigned alt_size, +void *used_virt,dma_addr_t used_phys) +{ +if (this->direct_dma_map_ok) +dma_unmap_single (this->dev, used_phys, length,DMA_FROM_DEVICE ); +} + +static inline void read_page_swap_end (struct gpmi_nand_data *this, +void *destination,unsigned length, +void *alt_virt,dma_addr_t alt_phys,unsigned alt_size, +void *used_virt,dma_addr_t used_phys) +{ +if (!this->direct_dma_map_ok) +memcpy (destination, alt_virt, length); +} + +static int send_page_prepare (struct gpmi_nand_data *this, +const void *source,unsigned length, +void *alt_virt,dma_addr_t alt_phys,unsigned alt_size, +const void **use_virt,dma_addr_t *use_phys) +{ +struct device *dev = this->dev; + +if (virt_addr_valid (source)) { +dma_addr_t source_phys; + + source_phys =dma_map_single (dev, (void *)source, length, +DMA_TO_DEVICE ); +if (dma_mapping_error (dev, source_phys)) { +if (alt_size < length) { +pr_err ("Alternate buffer is too small\n" ); +return -ENOMEM ; + } + goto map_failed; + } + *use_virt = source; + *use_phys = source_phys; +return 0 ; + } +map_failed: +/* +* Copy the content of the source buffer into the alternate +* buffer and set up the return values accordingly. +*/ +memcpy (alt_virt, source, length); + + *use_virt = alt_virt; + *use_phys = alt_phys; +return 0 ; +} + +static void send_page_end (struct gpmi_nand_data *this, +const void *source,unsigned length, +void *alt_virt,dma_addr_t alt_phys,unsigned alt_size, +const void *used_virt,dma_addr_t used_phys) +{ +struct device *dev = this->dev; +if (used_virt == source) +dma_unmap_single (dev, used_phys, length,DMA_TO_DEVICE ); +} + +static void gpmi_free_dma_buffer (struct gpmi_nand_data *this) +{ +struct device *dev = this->dev; + +if (this->page_buffer_virt &&virt_addr_valid (this->page_buffer_virt)) +dma_free_coherent (dev, this->page_buffer_size, + this->page_buffer_virt, + this->page_buffer_phys); +kfree (this->cmd_buffer); +kfree (this->data_buffer_dma); + + this->cmd_buffer =NULL ; + this->data_buffer_dma =NULL ; + this->page_buffer_virt =NULL ; + this->page_buffer_size =0 ; +} + +/* Allocate the DMA buffers */ +static int gpmi_alloc_dma_buffer (struct gpmi_nand_data *this) +{ +struct bch_geometry *geo = &this->bch_geometry; +struct device *dev = this->dev; + +/* [1] Allocate a command buffer. PAGE_SIZE is enough. */ + this->cmd_buffer =kzalloc (PAGE_SIZE ,GFP_DMA ); +if (this->cmd_buffer ==NULL ) + goto error_alloc; + +/* [2] Allocate a read/write data buffer. PAGE_SIZE is enough. */ + this->data_buffer_dma =kzalloc (PAGE_SIZE ,GFP_DMA ); +if (this->data_buffer_dma ==NULL ) + goto error_alloc; + +/* +* [3] Allocate the page buffer. +* +* Both the payload buffer and the auxiliary buffer must appear on +* 32-bit boundaries. We presume the size of the payload buffer is a +* power of two and is much larger than four, which guarantees the +* auxiliary buffer will appear on a 32-bit boundary. +*/ + this->page_buffer_size = geo->payload_size_in_bytes + + geo->auxiliary_size_in_bytes; + + this->page_buffer_virt =dma_alloc_coherent (dev, this->page_buffer_size, + &this->page_buffer_phys,GFP_DMA ); +if (!this->page_buffer_virt) + goto error_alloc; + + +/* Slice up the page buffer. */ + this->payload_virt = this->page_buffer_virt; + this->payload_phys = this->page_buffer_phys; + this->auxiliary_virt = this->payload_virt + geo->payload_size_in_bytes; + this->auxiliary_phys = this->payload_phys + geo->payload_size_in_bytes; +return 0 ; + +error_alloc: +gpmi_free_dma_buffer (this); +pr_err ("allocate DMA buffer ret!!\n" ); +return -ENOMEM ; +} + +static void gpmi_cmd_ctrl (struct mtd_info *mtd,int data,unsigned int ctrl) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; +int ret; + +/* +* Every operation begins with a command byte and a series of zero or +* more address bytes. These are distinguished by either the Address +* Latch Enable (ALE) or Command Latch Enable (CLE) signals being +* asserted. When MTD is ready to execute the command, it will deassert +* both latch enables. +* +* Rather than run a separate DMA operation for every single byte, we +* queue them up and run a single DMA operation for the entire series +* of command and data bytes. NAND_CMD_NONE means the END of the queue. +*/ +if ((ctrl & (NAND_ALE |NAND_CLE ))) { +if (data !=NAND_CMD_NONE ) + this->cmd_buffer[this->command_length++] = data; +return ; + } + +if (!this->command_length) +return ; + + ret =gpmi_send_command (this); +if (ret) +pr_err ("Chip: %u, Error %d\n" , this->current_chip, ret); + + this->command_length =0 ; +} + +static int gpmi_dev_ready (struct mtd_info *mtd) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; + +return gpmi_is_ready (this, this->current_chip); +} + +static void gpmi_select_chip (struct mtd_info *mtd,int chipnr) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; + +if ((this->current_chip <0 ) && (chipnr >=0 )) +gpmi_begin (this); +else if ((this->current_chip >=0 ) && (chipnr <0 )) +gpmi_end (this); + + this->current_chip = chipnr; +} + +static void gpmi_read_buf (struct mtd_info *mtd,uint8_t *buf,int len) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; + +logio (GPMI_DEBUG_READ ); + this->upper_buf = buf; + this->upper_len = len; + +gpmi_read_data (this); +} + +static void gpmi_write_buf (struct mtd_info *mtd,const uint8_t *buf,int len) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; + +logio (GPMI_DEBUG_WRITE ); + this->upper_buf = (uint8_t *)buf; + this->upper_len = len; + +gpmi_send_data (this); +} + +static uint8_t gpmi_read_byte (struct mtd_info *mtd) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; +uint8_t *buf = this->data_buffer_dma; + +gpmi_read_buf (mtd, buf,1 ); +return buf[0 ]; +} + +/* +* Handles block mark swapping. +* It can be called in swapping the block mark, or swapping it back, +* because the the operations are the same. +*/ +static void block_mark_swapping (struct gpmi_nand_data *this, +void *payload,void *auxiliary) +{ +struct bch_geometry *nfc_geo = &this->bch_geometry; +unsigned char *p; +unsigned char *a; +unsigned int bit; +unsigned char mask; +unsigned char from_data; +unsigned char from_oob; + +if (!this->swap_block_mark) +return ; + +/* +* If control arrives here, we're swapping. Make some convenience +* variables. +*/ + bit = nfc_geo->block_mark_bit_offset; + p = payload + nfc_geo->block_mark_byte_offset; + a = auxiliary; + +/* +* Get the byte from the data area that overlays the block mark. Since +* the ECC engine applies its own view to the bits in the page, the +* physical block mark won't (in general) appear on a byte boundary in +* the data. +*/ + from_data = (p[0 ] >> bit) | (p[1 ] << (8 - bit)); + +/* Get the byte from the OOB. */ + from_oob = a[0 ]; + +/* Swap them. */ + a[0 ] = from_data; + + mask = (0x1 << bit) -1 ; + p[0 ] = (p[0 ] & mask) | (from_oob << bit); + + mask = ~0 << bit; + p[1 ] = (p[1 ] & mask) | (from_oob >> (8 - bit)); +} + +static int gpmi_ecc_read_page (struct mtd_info *mtd,struct nand_chip *chip, +uint8_t *buf,int page) +{ +struct gpmi_nand_data *this = chip->priv; +struct bch_geometry *nfc_geo = &this->bch_geometry; +void *payload_virt; +dma_addr_t payload_phys; +void *auxiliary_virt; +dma_addr_t auxiliary_phys; +unsigned int i; +unsigned char *status; +unsigned int failed; +unsigned int corrected; +int ret; + +logio (GPMI_DEBUG_ECC_READ ); + ret =read_page_prepare (this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size_in_bytes, + &payload_virt, &payload_phys); +if (ret) { +pr_err ("Inadequate DMA buffer\n" ); + ret = -ENOMEM ; +return ret; + } + auxiliary_virt = this->auxiliary_virt; + auxiliary_phys = this->auxiliary_phys; + +/* go! */ + ret =gpmi_read_page (this, payload_phys, auxiliary_phys); +read_page_end (this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size_in_bytes, + payload_virt, payload_phys); +if (ret) { +pr_err ("Error in ECC-based read: %d\n" , ret); + goto exit_nfc; + } + +/* handle the block mark swapping */ +block_mark_swapping (this, payload_virt, auxiliary_virt); + +/* Loop over status bytes, accumulating ECC status. */ + failed =0 ; + corrected =0 ; + status = auxiliary_virt + nfc_geo->auxiliary_status_offset; + +for (i =0 ; i < nfc_geo->ecc_chunk_count; i++, status++) { +if ((*status ==STATUS_GOOD ) || (*status ==STATUS_ERASED )) +continue ; + +if (*status ==STATUS_UNCORRECTABLE ) { + failed++; +continue ; + } + corrected += *status; + } + +/* +* Propagate ECC status to the owning MTD only when failed or +* corrected times nearly reaches our ECC correction threshold. +*/ +if (failed || corrected >= (nfc_geo->ecc_strength -1 )) { + mtd->ecc_stats.failed += failed; + mtd->ecc_stats.corrected += corrected; + } + +/* +* It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() for +* details about our policy for delivering the OOB. +* +* We fill the caller's buffer with set bits, and then copy the block +* mark to th caller's buffer. Note that, if block mark swapping was +* necessary, it has already been done, so we can rely on the first +* byte of the auxiliary buffer to contain the block mark. +*/ +memset (chip->oob_poi, ~0 , mtd->oobsize); + chip->oob_poi[0 ] = ((uint8_t *) auxiliary_virt)[0 ]; + +read_page_swap_end (this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size_in_bytes, + payload_virt, payload_phys); +exit_nfc: +return ret; +} + +static void gpmi_ecc_write_page (struct mtd_info *mtd, +struct nand_chip *chip,const uint8_t *buf) +{ +struct gpmi_nand_data *this = chip->priv; +struct bch_geometry *nfc_geo = &this->bch_geometry; +const void *payload_virt; +dma_addr_t payload_phys; +const void *auxiliary_virt; +dma_addr_t auxiliary_phys; +int ret; + +logio (GPMI_DEBUG_ECC_WRITE ); +if (this->swap_block_mark) { +/* +* If control arrives here, we're doing block mark swapping. +* Since we can't modify the caller's buffers, we must copy them +* into our own. +*/ +memcpy (this->payload_virt, buf, mtd->writesize); + payload_virt = this->payload_virt; + payload_phys = this->payload_phys; + +memcpy (this->auxiliary_virt, chip->oob_poi, + nfc_geo->auxiliary_size_in_bytes); + auxiliary_virt = this->auxiliary_virt; + auxiliary_phys = this->auxiliary_phys; + +/* Handle block mark swapping. */ +block_mark_swapping (this, + (void *) payload_virt, (void *) auxiliary_virt); + }else { +/* +* If control arrives here, we're not doing block mark swapping, +* so we can to try and use the caller's buffers. +*/ + ret =send_page_prepare (this, + buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size_in_bytes, + &payload_virt, &payload_phys); +if (ret) { +pr_err ("Inadequate payload DMA buffer\n" ); +return ; + } + + ret =send_page_prepare (this, + chip->oob_poi, mtd->oobsize, + this->auxiliary_virt, this->auxiliary_phys, + nfc_geo->auxiliary_size_in_bytes, + &auxiliary_virt, &auxiliary_phys); +if (ret) { +pr_err ("Inadequate auxiliary DMA buffer\n" ); + goto exit_auxiliary; + } + } + +/* Ask the NFC. */ + ret =gpmi_send_page (this, payload_phys, auxiliary_phys); +if (ret) +pr_err ("Error in ECC-based write: %d\n" , ret); + +if (!this->swap_block_mark) { +send_page_end (this, chip->oob_poi, mtd->oobsize, + this->auxiliary_virt, this->auxiliary_phys, + nfc_geo->auxiliary_size_in_bytes, + auxiliary_virt, auxiliary_phys); +exit_auxiliary: +send_page_end (this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size_in_bytes, + payload_virt, payload_phys); + } +} + +/* +* There are several places in this driver where we have to handle the OOB and +* block marks. This is the function where things are the most complicated, so +* this is where we try to explain it all. All the other places refer back to +* here. +* +* These are the rules, in order of decreasing importance: +* +* 1) Nothing the caller does can be allowed to imperil the block mark. +* +* 2) In read operations, the first byte of the OOB we return must reflect the +* true state of the block mark, no matter where that block mark appears in +* the physical page. +* +* 3) ECC-based read operations return an OOB full of set bits (since we never +* allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads +* return). +* +* 4) "Raw" read operations return a direct view of the physical bytes in the +* page, using the conventional definition of which bytes are data and which +* are OOB. This gives the caller a way to see the actual, physical bytes +* in the page, without the distortions applied by our ECC engine. +* +* +* What we do for this specific read operation depends on two questions: +* +* 1) Are we doing a "raw" read, or an ECC-based read? +* +* 2) Are we using block mark swapping or transcription? +* +* There are four cases, illustrated by the following Karnaugh map: +* +* | Raw | ECC-based | +* -------------+-------------------------+-------------------------+ +* | Read the conventional | | +* | OOB at the end of the | | +* Swapping | page and return it. It | | +* | contains exactly what | | +* | we want. | Read the block mark and | +* -------------+-------------------------+ return it in a buffer | +* | Read the conventional | full of set bits. | +* | OOB at the end of the | | +* | page and also the block | | +* Transcribing | mark in the metadata. | | +* | Copy the block mark | | +* | into the first byte of | | +* | the OOB. | | +* -------------+-------------------------+-------------------------+ +* +* Note that we break rule #4 in the Transcribing/Raw case because we're not +* giving an accurate view of the actual, physical bytes in the page (we're +* overwriting the block mark). That's OK because it's more important to follow +* rule #2. +* +* It turns out that knowing whether we want an "ECC-based" or "raw" read is not +* easy. When reading a page, for example, the NAND Flash MTD code calls our +* ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an +* ECC-based or raw view of the page is implicit in which function it calls +* (there is a similar pair of ECC-based/raw functions for writing). +* +* Since MTD assumes the OOB is not covered by ECC, there is no pair of +* ECC-based/raw functions for reading or or writing the OOB. The fact that the +* caller wants an ECC-based or raw view of the page is not propagated down to +* this driver. +*/ +static int gpmi_ecc_read_oob (struct mtd_info *mtd,struct nand_chip *chip, +int page,int sndcmd) +{ +struct gpmi_nand_data *this = chip->priv; + +logio (GPMI_DEBUG_READ_OOB ); +/* clear the OOB buffer */ +memset (chip->oob_poi, ~0 , mtd->oobsize); + +/* Read out the conventional OOB. */ + chip->cmdfunc (mtd,NAND_CMD_READ0 , mtd->writesize, page); + chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); + +/* +* Now, we want to make sure the block mark is correct. In the +* Swapping/Raw case, we already have it. Otherwise, we need to +* explicitly read it. +*/ +if (!this->swap_block_mark) { +/* Read the block mark into the first byte of the OOB buffer. */ + chip->cmdfunc (mtd,NAND_CMD_READ0 ,0 , page); + chip->oob_poi[0 ] = chip->read_byte (mtd); + } + +/* +* Return true, indicating that the next call to this function must send +* a command. +*/ +return true; +} + +static int +gpmi_ecc_write_oob (struct mtd_info *mtd,struct nand_chip *chip,int page) +{ +/* +* The BCH will use all the (page + oob). +* Our gpmi_hw_ecclayout can only prohibit the JFFS2 to write the oob. +* But it can not stop some ioctls such MEMWRITEOOB which uses +* MTD_OOB_PLACE. So We have to implement this function to prohibit +* these ioctls too. +*/ +return -EPERM ; +} + +static int gpmi_block_markbad (struct mtd_info *mtd,loff_t ofs) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; +int block, ret =0 ; +uint8_t *block_mark; +int column, page, status, chipnr; + +/* Get block number */ + block = (int )(ofs >> chip->bbt_erase_shift); +if (chip->bbt) + chip->bbt[block >>2 ] |=0x01 << ((block &0x03 ) <<1 ); + +/* Do we have a flash based bad block table ? */ +if (chip->options &NAND_BBT_USE_FLASH ) + ret =nand_update_bbt (mtd, ofs); +else { + chipnr = (int )(ofs >> chip->chip_shift); + chip->select_chip (mtd, chipnr); + + column = this->swap_block_mark ? mtd->writesize :0 ; + +/* Write the block mark. */ + block_mark = this->data_buffer_dma; + block_mark[0 ] =0 ;/* bad block marker */ + +/* Shift to get page */ + page = (int )(ofs >> chip->page_shift); + + chip->cmdfunc (mtd,NAND_CMD_SEQIN , column, page); + chip->write_buf (mtd, block_mark,1 ); + chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); + + status = chip->waitfunc (mtd, chip); +if (status &NAND_STATUS_FAIL ) + ret = -EIO ; + + chip->select_chip (mtd,-1 ); + } +if (!ret) + mtd->ecc_stats.badblocks++; + +return ret; +} + +static int __devinit nand_boot_set_geometry (struct gpmi_nand_data *this) +{ +struct boot_rom_geometry *geometry = &this->rom_geometry; + +/* +* Set the boot block stride size. +* +* In principle, we should be reading this from the OTP bits, since +* that's where the ROM is going to get it. In fact, we don't have any +* way to read the OTP bits, so we go with the default and hope for the +* best. +*/ + geometry->stride_size_in_pages =64 ; + +/* +* Set the search area stride exponent. +* +* In principle, we should be reading this from the OTP bits, since +* that's where the ROM is going to get it. In fact, we don't have any +* way to read the OTP bits, so we go with the default and hope for the +* best. +*/ + geometry->search_area_stride_exponent =2 ; +return 0 ; +} + +static const char *fingerprint ="STMP" ; +static int __devinit mx23_check_transcription_stamp (struct gpmi_nand_data *this) +{ +struct boot_rom_geometry *rom_geo = &this->rom_geometry; +struct device *dev = this->dev; +struct mtd_info *mtd = &this->mtd; +struct nand_chip *chip = &this->nand; +unsigned int search_area_size_in_strides; +unsigned int stride; +unsigned int page; +loff_t byte; +uint8_t *buffer = chip->buffers->databuf; +int saved_chip_number; +int found_an_ncb_fingerprint = false; + +/* Compute the number of strides in a search area. */ + search_area_size_in_strides =1 << rom_geo->search_area_stride_exponent; + + saved_chip_number = this->current_chip; + chip->select_chip (mtd,0 ); + +/* +* Loop through the first search area, looking for the NCB fingerprint. +*/ +dev_dbg (dev,"Scanning for an NCB fingerprint...\n" ); + +for (stride =0 ; stride < search_area_size_in_strides; stride++) { +/* Compute the page and byte addresses. */ + page = stride * rom_geo->stride_size_in_pages; + byte = page * mtd->writesize; + +dev_dbg (dev,"Looking for a fingerprint in page 0x%x\n" , page); + +/* +* Read the NCB fingerprint. The fingerprint is four bytes long +* and starts in the 12th byte of the page. +*/ + chip->cmdfunc (mtd,NAND_CMD_READ0 ,12 , page); + chip->read_buf (mtd, buffer,strlen (fingerprint)); + +/* Look for the fingerprint. */ +if (!memcmp (buffer, fingerprint,strlen (fingerprint))) { + found_an_ncb_fingerprint = true; +break ; + } + + } + + chip->select_chip (mtd, saved_chip_number); + +if (found_an_ncb_fingerprint) +dev_dbg (dev,"\tFound a fingerprint\n" ); +else +dev_dbg (dev,"\tNo fingerprint found\n" ); +return found_an_ncb_fingerprint; +} + +/* Writes a transcription stamp. */ +static int __devinit mx23_write_transcription_stamp (struct gpmi_nand_data *this) +{ +struct device *dev = this->dev; +struct boot_rom_geometry *rom_geo = &this->rom_geometry; +struct mtd_info *mtd = &this->mtd; +struct nand_chip *chip = &this->nand; +unsigned int block_size_in_pages; +unsigned int search_area_size_in_strides; +unsigned int search_area_size_in_pages; +unsigned int search_area_size_in_blocks; +unsigned int block; +unsigned int stride; +unsigned int page; +loff_t byte; +uint8_t *buffer = chip->buffers->databuf; +int saved_chip_number; +int status; + +/* Compute the search area geometry. */ + block_size_in_pages = mtd->erasesize / mtd->writesize; + search_area_size_in_strides =1 << rom_geo->search_area_stride_exponent; + search_area_size_in_pages = search_area_size_in_strides * + rom_geo->stride_size_in_pages; + search_area_size_in_blocks = + (search_area_size_in_pages + (block_size_in_pages -1 )) / + block_size_in_pages; + +dev_dbg (dev,"Search Area Geometry :\n" ); +dev_dbg (dev,"\tin Blocks : %u\n" , search_area_size_in_blocks); +dev_dbg (dev,"\tin Strides: %u\n" , search_area_size_in_strides); +dev_dbg (dev,"\tin Pages : %u\n" , search_area_size_in_pages); + +/* Select chip 0. */ + saved_chip_number = this->current_chip; + chip->select_chip (mtd,0 ); + +/* Loop over blocks in the first search area, erasing them. */ +dev_dbg (dev,"Erasing the search area...\n" ); + +for (block =0 ; block < search_area_size_in_blocks; block++) { +/* Compute the page address. */ + page = block * block_size_in_pages; + +/* Erase this block. */ +dev_dbg (dev,"\tErasing block 0x%x\n" , block); + chip->cmdfunc (mtd,NAND_CMD_ERASE1 ,-1 , page); + chip->cmdfunc (mtd,NAND_CMD_ERASE2 ,-1 ,-1 ); + +/* Wait for the erase to finish. */ + status = chip->waitfunc (mtd, chip); +if (status &NAND_STATUS_FAIL ) +dev_err (dev,"[%s] Erase failed.\n" , __func__); + } + +/* Write the NCB fingerprint into the page buffer. */ +memset (buffer, ~0 , mtd->writesize); +memset (chip->oob_poi, ~0 , mtd->oobsize); +memcpy (buffer +12 , fingerprint,strlen (fingerprint)); + +/* Loop through the first search area, writing NCB fingerprints. */ +dev_dbg (dev,"Writing NCB fingerprints...\n" ); +for (stride =0 ; stride < search_area_size_in_strides; stride++) { +/* Compute the page and byte addresses. */ + page = stride * rom_geo->stride_size_in_pages; + byte = page * mtd->writesize; + +/* Write the first page of the current stride. */ +dev_dbg (dev,"Writing an NCB fingerprint in page 0x%x\n" , page); + chip->cmdfunc (mtd,NAND_CMD_SEQIN ,0x00 , page); + chip->ecc.write_page_raw (mtd, chip, buffer); + chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); + +/* Wait for the write to finish. */ + status = chip->waitfunc (mtd, chip); +if (status &NAND_STATUS_FAIL ) +dev_err (dev,"[%s] Write failed.\n" , __func__); + } + +/* Deselect chip 0. */ + chip->select_chip (mtd, saved_chip_number); +return 0 ; +} + +static int __devinit mx23_boot_init (struct gpmi_nand_data *this) +{ +struct device *dev = this->dev; +struct nand_chip *chip = &this->nand; +struct mtd_info *mtd = &this->mtd; +unsigned int block_count; +unsigned int block; +int chipnr; +int page; +loff_t byte; +uint8_t block_mark; +int ret =0 ; + +/* +* If control arrives here, we can't use block mark swapping, which +* means we're forced to use transcription. First, scan for the +* transcription stamp. If we find it, then we don't have to do +* anything -- the block marks are already transcribed. +*/ +if (mx23_check_transcription_stamp (this)) +return 0 ; + +/* +* If control arrives here, we couldn't find a transcription stamp, so +* so we presume the block marks are in the conventional location. +*/ +dev_dbg (dev,"Transcribing bad block marks...\n" ); + +/* Compute the number of blocks in the entire medium. */ + block_count = chip->chipsize >> chip->phys_erase_shift; + +/* +* Loop over all the blocks in the medium, transcribing block marks as +* we go. +*/ +for (block =0 ; block < block_count; block++) { +/* +* Compute the chip, page and byte addresses for this block's +* conventional mark. +*/ + chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); + page = block << (chip->phys_erase_shift - chip->page_shift); + byte = block << chip->phys_erase_shift; + +/* Send the command to read the conventional block mark. */ + chip->select_chip (mtd, chipnr); + chip->cmdfunc (mtd,NAND_CMD_READ0 , mtd->writesize, page); + block_mark = chip->read_byte (mtd); + chip->select_chip (mtd,-1 ); + +/* +* Check if the block is marked bad. If so, we need to mark it +* again, but this time the result will be a mark in the +* location where we transcribe block marks. +*/ +if (block_mark !=0xff ) { +dev_dbg (dev,"Transcribing mark in block %u\n" , block); + ret = chip->block_markbad (mtd, byte); +if (ret) +dev_err (dev,"Failed to mark block bad with " +"ret %d\n" , ret); + } + } + +/* Write the stamp that indicates we've transcribed the block marks. */ +mx23_write_transcription_stamp (this); +return 0 ; +} + +static int __devinit nand_boot_init (struct gpmi_nand_data *this) +{ +nand_boot_set_geometry (this); + +/* This is ROM arch-specific initilization before the BBT scanning. */ +if (GPMI_IS_MX23 (this)) +return mx23_boot_init (this); +return 0 ; +} + +static int __devinit gpmi_set_geometry (struct gpmi_nand_data *this) +{ +int ret; + +/* Free the temporary DMA memory for reading ID. */ +gpmi_free_dma_buffer (this); + +/* Set up the NFC geometry which is used by BCH. */ + ret =bch_set_geometry (this); +if (ret) { +pr_err ("set geometry ret : %d\n" , ret); +return ret; + } + +/* Alloc the new DMA buffers according to the pagesize and oobsize */ +return gpmi_alloc_dma_buffer (this); +} + +static int gpmi_pre_bbt_scan (struct gpmi_nand_data *this) +{ +int ret; + +/* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ +if (GPMI_IS_MX23 (this)) + this->swap_block_mark = false; +else + this->swap_block_mark = true; + +/* Set up the medium geometry */ + ret =gpmi_set_geometry (this); +if (ret) +return ret; + +/* NAND boot init, depends on the gpmi_set_geometry(). */ +return nand_boot_init (this); +} + +static int gpmi_scan_bbt (struct mtd_info *mtd) +{ +struct nand_chip *chip = mtd->priv; +struct gpmi_nand_data *this = chip->priv; +int ret; + +/* Prepare for the BBT scan. */ + ret =gpmi_pre_bbt_scan (this); +if (ret) +return ret; + +/* use the default BBT implementation */ +return nand_default_bbt (mtd); +} + +void gpmi_nfc_exit (struct gpmi_nand_data *this) +{ +nand_release (&this->mtd); +gpmi_free_dma_buffer (this); +} + +static int __devinit gpmi_nfc_init (struct gpmi_nand_data *this) +{ +struct gpmi_nand_platform_data *pdata = this->pdata; +struct mtd_info *mtd = &this->mtd; +struct nand_chip *chip = &this->nand; +int ret; + +/* init current chip */ + this->current_chip =-1 ; + +/* init the MTD data structures */ + mtd->priv = chip; + mtd->name ="gpmi-nand" ; + mtd->owner =THIS_MODULE ; + +/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ + chip->priv = this; + chip->select_chip = gpmi_select_chip; + chip->cmd_ctrl = gpmi_cmd_ctrl; + chip->dev_ready = gpmi_dev_ready; + chip->read_byte = gpmi_read_byte; + chip->read_buf = gpmi_read_buf; + chip->write_buf = gpmi_write_buf; + chip->ecc.read_page = gpmi_ecc_read_page; + chip->ecc.write_page = gpmi_ecc_write_page; + chip->ecc.read_oob = gpmi_ecc_read_oob; + chip->ecc.write_oob = gpmi_ecc_write_oob; + chip->scan_bbt = gpmi_scan_bbt; + chip->badblock_pattern = &gpmi_bbt_descr; + chip->block_markbad = gpmi_block_markbad; + chip->options |=NAND_NO_SUBPAGE_WRITE ; + chip->ecc.mode =NAND_ECC_HW ; + chip->ecc.size =1 ; + chip->ecc.layout = &gpmi_hw_ecclayout; + +/* Allocate a temporary DMA buffer for reading ID in the nand_scan() */ + this->bch_geometry.payload_size_in_bytes =1024 ; + this->bch_geometry.auxiliary_size_in_bytes =128 ; + ret =gpmi_alloc_dma_buffer (this); +if (ret) + goto err_out; + + ret =nand_scan (mtd, pdata->max_chip_count); +if (ret) { +pr_err ("Chip scan failed\n" ); + goto err_out; + } + + ret =mtd_device_parse_register (mtd,NULL ,NULL , + pdata->partitions, pdata->partition_count); +if (ret) + goto err_out; +return 0 ; + +err_out: +gpmi_nfc_exit (this); +return ret; +} + +static int __devinit gpmi_nand_probe (struct platform_device *pdev) +{ +struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data; +struct gpmi_nand_data *this; +int ret; + + this =kzalloc (sizeof (*this),GFP_KERNEL ); +if (!this) { +pr_err ("Failed to allocate per-device memory\n" ); +return -ENOMEM ; + } + +platform_set_drvdata (pdev, this); + this->pdev = pdev; + this->dev = &pdev->dev; + this->pdata = pdata; + +if (pdata->platform_init) { + ret = pdata->platform_init (); +if (ret) + goto platform_init_error; + } + + ret =acquire_resources (this); +if (ret) + goto exit_acquire_resources; + + ret =init_hardware (this); +if (ret) + goto exit_nfc_init; + + ret =gpmi_nfc_init (this); +if (ret) + goto exit_nfc_init; + +return 0 ; + +exit_nfc_init: +release_resources (this); +platform_init_error: +exit_acquire_resources: +platform_set_drvdata (pdev,NULL ); +kfree (this); +return ret; +} + +static int __exit gpmi_nand_remove(struct platform_device *pdev) +{ +struct gpmi_nand_data *this =platform_get_drvdata (pdev); + +gpmi_nfc_exit (this); +release_resources (this); +platform_set_drvdata (pdev,NULL ); +kfree (this); +return 0 ; +} + +static const struct platform_device_id gpmi_ids[] = { + { + .name ="imx23-gpmi-nand" , + .driver_data =IS_MX23 , + }, { + .name ="imx28-gpmi-nand" , + .driver_data =IS_MX28 , + }, {}, +}; + +static struct platform_driver gpmi_nand_driver = { + .driver = { + .name ="gpmi-nand" , + }, + .probe = gpmi_nand_probe, + .remove =__exit_p (gpmi_nand_remove), + .id_table = gpmi_ids, +}; + +static int __init gpmi_nand_init (void ) +{ +int err; + + err =platform_driver_register (&gpmi_nand_driver); +if (err ==0 ) +printk (KERN_INFO "GPMI NAND driver registered. (IMX)\n" ); +else +pr_err ("i.MX GPMI NAND driver registration failed\n" ); +return err; +} + +static void __exit gpmi_nand_exit(void) +{ +platform_driver_unregister (&gpmi_nand_driver); +} + +module_init (gpmi_nand_init); +module_exit (gpmi_nand_exit); + +MODULE_AUTHOR ("Freescale Semiconductor, Inc." ); +MODULE_DESCRIPTION ("i.MX GPMI NAND Flash Controller Driver" ); +MODULE_LICENSE ("GPL" );
drivers/mtd/nand/gpmi-nand/gpmi-nand.h new file +274 -0
274 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH v10 1/3 MTD : add the common code for GPMI-NAND controller driver
Quoted 3 lines · Huang Shijie
These files contain the common code for the GPMI-NAND driver.
Signed-off-by Huang Shijie <[email protected]>
[...]
Hi Huang, please don't forget to CC me next time, but there will be next time for this patch I guess ;-)
Quoted 5 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c new file +5
-0+int start_dma_without_bch_irq (struct gpmi_nand_data *this, +struct dma_async_tx_descriptor *desc) +{ +struct completion *dma_c = &this->dma_done; +int err;
drivers/mtd/nand/gpmi-nand/gpmi-nand.c line 322
Quoted 10 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c new file +10
-0+ +init_completion (dma_c); + + desc->callback = dma_irq_callback; + desc->callback_param = this; +dmaengine_submit (desc); + +/* Wait for the interrupt from the DMA block. */ + err =wait_for_completion_timeout (dma_c,msecs_to_jiffies (1000 )); + err = (!err) ? -ETIMEDOUT :0 ;
This might need a tiny correction.
drivers/mtd/nand/gpmi-nand/gpmi-nand.c line 329
Quoted 7 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c new file +7
-0+if (err) { +pr_err ("DMA timeout, last DMA :%d\n" , this->last_dma_type); +if (gpmi_debug &GPMI_DEBUG_VERBOSE ) +gpmi_dump_info (this); + } +return err; +}
But that's a minor thing. Even like this:
PATCH v10 2/3 MTD : add helper functions library and header files for GPMI NAND driver
In reply to Huang Shijiebch-regs.h : registers file for BCH modulegpmi-regs.h: registers file for GPMI modulegpmi-lib.c: helper functions library.
--- drivers/mtd/nand/gpmi-nand/bch-regs.h | 84 +++ drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 1056 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-regs.h | 168 +++++ 3 files changed, 1308 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h
drivers/mtd/nand/gpmi-nand/bch-regs.h new file +84 -0
@@ -0,0 +1,84 @@ +/* +* Freescale GPMI NAND Flash Driver +* +* Copyright 2008-2011 Freescale Semiconductor, Inc. +* Copyright 2008 Embedded Alley Solutions, Inc. +* +* This program is free software; you can redistribute it and/or modify +* it 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#ifndef __GPMI_NAND_BCH_REGS_H +#define __GPMI_NAND_BCH_REGS_H + +#define HW_BCH_CTRL 0x00000000 +#define HW_BCH_CTRL_SET 0x00000004 +#define HW_BCH_CTRL_CLR 0x00000008 +#define HW_BCH_CTRL_TOG 0x0000000c + +#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) +#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) + +#define HW_BCH_STATUS0 0x00000010 +#define HW_BCH_MODE 0x00000020 +#define HW_BCH_ENCODEPTR 0x00000030 +#define HW_BCH_DATAPTR 0x00000040 +#define HW_BCH_METAPTR 0x00000050 +#define HW_BCH_LAYOUTSELECT 0x00000070 + +#define HW_BCH_FLASH0LAYOUT0 0x00000080 + +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS (v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) + +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE) +#define BF_BCH_FLASH0LAYOUT0_META_SIZE (v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_META_SIZE) + +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 +#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0 (v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0) + +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE (v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) + +#define HW_BCH_FLASH0LAYOUT1 0x00000090 + +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ + (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE (v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) + +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 +#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN (v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN) + +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE (v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#endif
drivers/mtd/nand/gpmi-nand/gpmi-lib.c new file +1056 -0
1,056 lines not on this page · raw message
drivers/mtd/nand/gpmi-nand/gpmi-regs.h new file +168 -0
168 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH v10 2/3 MTD : add helper functions library and header files for GPMI NAND driver
Quoted 5 lines · Huang Shijie
bch-regs.h: registers file for BCH modulegpmi-regs.h: registers file for GPMI modulegpmi-lib.c: helper functions library.Signed-off-by Huang Shijie <[email protected]>
[...]
Quoted 6 lines
drivers/mtd/nand/gpmi-nand/gpmi-lib.c new file +6
-0+/* Returns the Ready/Busy status of the given chip. */ +int gpmi_is_ready (struct gpmi_nand_data *this,unsigned chip) +{ +struct resources *r = &this->resources; +uint32_t mask; +uint32_t reg;
drivers/mtd/nand/gpmi-nand/gpmi-lib.c line 800
Quoted 10 lines
drivers/mtd/nand/gpmi-nand/gpmi-lib.c new file +10
-0+ +if (GPMI_IS_MX23 (this)) { + mask =MX23_BM_GPMI_DEBUG_READY0 << chip; + reg =readl (r->gpmi_regs +HW_GPMI_DEBUG ); + }else if (GPMI_IS_MX28 (this)) { + mask =MX28_BF_GPMI_STAT_READY_BUSY (1 << chip); + reg =readl (r->gpmi_regs +HW_GPMI_STAT ); + }else +BUG (); +return !!(reg & mask);
Maybe you don't need that double negation here ?
Quoted 1 line
+}
[...]
Quoted 8 lines
drivers/mtd/nand/gpmi-nand/gpmi-regs.h new file +4
-0+#define HW_GPMI_TIMING1 0x00000080 +#define HW_GPMI_TIMING2 0x00000090 +#define HW_GPMI_DATA 0x000000a0 +/*============================ MX28 uses this to detect READY ==============*/ +#define
HW_GPMI_STAT0x000000b0(quoted diff) +2
-0+#define MX28_BP_GPMI_STAT_READY_BUSY 24 +#define MX28_BM_GPMI_STAT_READY_BUSY (0xff <<
MX28_BP_GPMI_STAT_READY_BUSY) +#defineMX28_BF_GPMI_STAT_READY_BUSY(v)
\
drivers/mtd/nand/gpmi-nand/gpmi-regs.h line 168
Quoted 6 lines
(quoted diff) +2
-0+ (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) +/*============================ MX23 uses this to detect READY==============*/ +#define
HW_GPMI_DEBUG0x000000c0drivers/mtd/nand/gpmi-nand/gpmi-regs.h new file +3
-0+#define MX23_BP_GPMI_DEBUG_READY0 28 +#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) +#endif
Maybe remove those "==========" above.
Minor things,
PATCH v10 3/3 MTD : add GPMI-NAND driver in the config and Makefile
In reply to Huang Shijieadd the GPMI-NAND driver in the relevant Kconfig and Makefile in the MTD.
--- drivers/mtd/nand/Kconfig | 13 +++++++++++++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/gpmi-nand/Makefile | 3 +++ 3 files changed, 17 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/gpmi-nand/Makefile
drivers/mtd/nand/Kconfig +13 -0
@@ -417,6 +417,19 @@ config MTD_NAND_NANDSIM The simulator may simulate various NAND flash chips for the MTD nand layer. +config MTD_NAND_GPMI_NAND + bool "GPMI NAND Flash Controller driver" + depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28) + select MTD_PARTITIONS + select MTD_CMDLINE_PARTS + help + Enables NAND Flash support for IMX23 or IMX28. + The GPMI controller is very powerful, with the help of BCH + module, it can do the hardware ECC. The GPMI supports several + NAND flashs at the same time. The GPMI may conflicts with other + block, such as SD card. So pay attention to it when you enable + the GPMI. + config MTD_NAND_PLATFORM tristate "Support for generic platform NAND driver" help
drivers/mtd/nand/Makefile +1 -0
@@ -48,5 +48,6 @@ obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ nand-objs := nand_base.o nand_bbt.o
drivers/mtd/nand/gpmi-nand/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o +gpmi_nand-objs += gpmi-nand.o +gpmi_nand-objs += gpmi-lib.o
Signature · 1 line
1.7.0.4
PATCH v10 3/3 MTD : add GPMI-NAND driver in the config and Makefile
Quoted 3 lines · Huang Shijie
add the GPMI-NAND driver in the relevant
KconfigandMakefilein the MTD.Signed-off-by Huang Shijie <[email protected]>
[...]
v10 cover add the GPMI controller driver for IMX23/IMX28
In reply to Huang ShijieQuoted 6 lines · Huang Shijie
The patch set is based on Artem's tree:
http://git.infradead.org/users/dedekind/l2-mtd-2.6.gitThe general-purpose media interface(GPMI) controller is a flexible interface to up to several NAND flashs.
Quoted 6 lines
The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
With the help of BCH, the GPMI controller can choose to do the hardware ECC or not.
This driver is a _pure_ MTD NAND controller driver now.
I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are independent of the other patches of yours: "[PATCH v8 0/4] add the GPMI-NAND support for imx23/imx28".
I did not really review the driver carefully, but the code looks neat at the first glance. My tree is in linux-next, so you should see your driver there.
Also, if someone gives it more review, and you send a new version, I can always pick it up.
Thanks.
Signature · 2 lines
Best Regards, Artem Bityutskiy
v10 cover add the GPMI controller driver for IMX23/IMX28
Hi Artem:
Quoted 9 lines · Huang Shijie
Quoted 8 lines
The patch set is based on Artem's tree:
http://git.infradead.org/users/dedekind/l2-mtd-2.6.gitThe general-purpose media interface(GPMI) controller is a flexible interface to up to several NAND flashs.
The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
Quoted 8 lines
Quoted 4 lines
With the help of BCH, the GPMI controller can choose to do the hardware ECC or not.
This driver is a _pure_ MTD NAND controller driver now.
I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are independent of the other patches of yours: "[PATCH v8 0/4] add the GPMI-NAND support for imx23/imx28".
thanks very much :)
Huang Shijie
Quoted 9 lines
I did not really review the driver carefully, but the code looks neat at the first glance. My tree is in linux-next, so you should see your driver there.
Also, if someone gives it more review, and you send a new version, I can always pick it up.
Thanks.
v10 cover add the GPMI controller driver for IMX23/IMX28
Quoted 44 lines · Huang Shijie
Hi Artem:
Quoted 17 lines · Huang Shijie
Quoted 15 lines
The patch set is based on Artem's tree:
? ? ? ?http://git.infradead.org/users/dedekind/l2-mtd-2.6.gitThe general-purpose media interface(GPMI) controller is a flexible interface
to up to several NAND flashs.The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
With the help of BCH, the GPMI controller can choose to do the hardware ECC or
not.Quoted 6 lines
Quoted 1 line
This driver is a _pure_ MTD NAND controller driver now.
I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are independent of the other patches of yours: "[PATCH v8 0/4] add the GPMI-NAND support for imx23/imx28".
thanks very much :)
Huang Shijie
Quoted 10 lines
I did not really review the driver carefully, but the code looks neat at the first glance. My tree is in linux-next, so you should see your driver there.
Also, if someone gives it more review, and you send a new version, I can always pick it up.
Thanks.
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
v10 cover add the GPMI controller driver for IMX23/IMX28
In reply to Artem BityutskiyQuoted 19 lines · Artem Bityutskiy
Quoted 6 lines · Huang Shijie
The patch set is based on Artem's tree:
http://git.infradead.org/users/dedekind/l2-mtd-2.6.gitThe general-purpose media interface(GPMI) controller is a flexible interface to up to several NAND flashs.
Quoted 7 lines
The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
With the help of BCH, the GPMI controller can choose to do the hardware ECC or not.
This driver is a _pure_ MTD NAND controller driver now.
I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are independent of the other patches of yours: "[PATCH v8 0/4] add the GPMI-NAND support for imx23/imx28".
Quoted 6 lines
I did not really review the driver carefully, but the code looks neat at the first glance. My tree is in linux-next, so you should see your driver there.
Also, if someone gives it more review, and you send a new version, I can always pick it up.
Yea, add my Acked-by's to that version ;-)
Cheers
Quoted 2 lines
Thanks.
v10 cover add the GPMI controller driver for IMX23/IMX28
Hi Marek:
Quoted 20 lines · Marek Vasut
Quoted 19 lines · Artem Bityutskiy
Quoted 6 lines · Huang Shijie
The patch set is based on Artem's tree:
? ? http://git.infradead.org/users/dedekind/l2-mtd-2.6.gitThe general-purpose media interface(GPMI) controller is a flexible interface to up to several NAND flashs.
Quoted 7 lines
The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
With the help of BCH, the GPMI controller can choose to do the hardware ECC or not.
This driver is a _pure_ MTD NAND controller driver now.
I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are independent of the other patches of yours: "[PATCH v8 0/4] add the GPMI-NAND support for imx23/imx28".
Quoted 8 lines
Quoted 6 lines
I did not really review the driver carefully, but the code looks neat at the first glance. My tree is in linux-next, so you should see your driver there.
Also, if someone gives it more review, and you send a new version, I can always pick it up.
Yea, add my Acked-by's to that version ;-)
Thanks for your comments. :)
I will send out the new version next week.
Best Regards
Huang Shijie
Quoted 5 lines
Cheers
Quoted 2 lines
Thanks.