From mboxrd@z Thu Jan 1 00:00:00 1970 From: b32955@freescale.com (Huang Shijie) Date: Fri, 22 Apr 2011 17:39:53 +0800 Subject: [PATCH v6 1/3] MTD : add the common code for GPMI controller driver In-Reply-To: <19887.57747.43423.753598@ipc1.ka-ro> References: <1303267549-18377-1-git-send-email-b32955@freescale.com> <1303267549-18377-2-git-send-email-b32955@freescale.com> <19887.57747.43423.753598@ipc1.ka-ro> Message-ID: <4DB14CE9.2040602@freescale.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Hi: > Huang Shijie writes: >> These files contain the common code for the GPMI driver. >> >> Signed-off-by: Huang Shijie >> --- >> drivers/mtd/nand/gpmi-nfc/gpmi-nfc.c | 2501 ++++++++++++++++++++++++++++++++++ >> drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h | 488 +++++++ >> 2 files changed, 2989 insertions(+), 0 deletions(-) >> create mode 100644 drivers/mtd/nand/gpmi-nfc/gpmi-nfc.c >> create mode 100644 drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h >> >> diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.c >> new file mode 100644 >> index 0000000..53d6915 >> --- /dev/null >> +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.c >> @@ -0,0 +1,2501 @@ >> +/* >> + * Freescale GPMI NFC 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 >> +#include "gpmi-nfc.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 >> +}; >> + >> +/* debug control */ >> +int gpmi_debug; >> + > This could be a module_param. > thanks. >> +static ssize_t show_gpmi_debug(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + return sprintf(buf, "%d\n", gpmi_debug); >> +} >> + >> +static ssize_t >> +store_gpmi_debug(struct device *dev, struct device_attribute *attr, >> + const char *buf, size_t size) >> +{ >> + const char *p = buf; >> + unsigned long v; >> + >> + if (strict_strtoul(p, 0,&v)< 0) >> + return size; >> + >> + gpmi_debug = v; >> + return size; >> +} >> + >> +static ssize_t show_ignorebad(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct gpmi_nfc_data *this = dev_get_drvdata(dev); >> + struct mil *mil =&this->mil; >> + >> + return sprintf(buf, "%d\n", mil->ignore_bad_block_marks); >> +} >> + >> +static ssize_t >> +store_ignorebad(struct device *dev, struct device_attribute *attr, >> + const char *buf, size_t size) >> +{ >> + struct gpmi_nfc_data *this = dev_get_drvdata(dev); >> + struct mil *mil =&this->mil; >> + const char *p = buf; >> + unsigned long v; >> + >> + if (strict_strtoul(p, 0,&v)< 0) >> + return size; >> > return -EINVAL would be more appropriate here. > >> + >> + if (v> 0) >> + v = 1; >> + >> + if (v != mil->ignore_bad_block_marks) { >> > These lines could be reduced to: > if (!v == mil->ignore_bad_block_marks) { > :) >> + if (v) { >> + /* >> + * This will cause the NAND Flash MTD code to believe >> + * that it never created a BBT and force it to call our >> + * block_bad function. >> + * >> + * See mil_block_bad for more details. >> + */ >> + mil->saved_bbt = mil->nand.bbt; >> + mil->nand.bbt = NULL; >> + } else { >> + /* >> + * Restore the NAND Flash MTD's pointer >> + * to its in-memory BBT. >> + */ >> + mil->nand.bbt = mil->saved_bbt; >> + } >> + mil->ignore_bad_block_marks = v; >> + } >> + return size; >> +} >> + > [...] >> +/* This will be called after the DMA operation is finished. */ >> +static void dma_irq_callback(void *param) >> +{ >> + struct gpmi_nfc_data *this = param; >> + struct nfc_hal *nfc = this->nfc; >> + struct mil *mil =&this->mil; >> + >> + complete(&nfc->dma_done); >> + >> + switch (this->dma_type) { >> + case DMA_FOR_COMMAND: >> + dma_unmap_sg(this->dev,&mil->cmd_sgl, 1, DMA_TO_DEVICE); >> + break; >> + >> + case DMA_FOR_READ_DATA: >> + dma_unmap_sg(this->dev,&mil->data_sgl, 1, DMA_FROM_DEVICE); >> + if (mil->direct_dma_map_ok == false) >> + memcpy(mil->upper_buf, (char *)mil->data_buffer_dma, >> > pointless cast. thanks. > [...] >> +static int acquire_dma_channels(struct gpmi_nfc_data *this, >> + const char *resource_name, >> + unsigned *low_channel, unsigned *high_channel) >> +{ >> + struct platform_device *pdev = this->pdev; > struct gpmi_nfc_platform_data *pdata = pdev->dev.platform_data; > see below. > >> + struct resource *r, *r_dma; >> + unsigned int i; >> + >> + r = platform_get_resource_byname(pdev, IORESOURCE_DMA, resource_name); >> + r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ, >> + GPMI_NFC_DMA_INTERRUPT_RES_NAME); >> + if (!r || !r_dma) { >> + log("Can't get resource for DMA"); >> + return -ENXIO; >> + } >> + >> + /* used in gpmi_dma_filter() */ >> + this->private = r; >> + >> + for (i = r->start; i<= r->end; i++) { >> + dma_cap_mask_t mask; >> + struct dma_chan *dma_chan; >> + > if (i - r->start>= pdata->max_chip_count) > break; > This is a good idea. it can limits the real dma channels. >> + dma_cap_zero(mask); >> + dma_cap_set(DMA_SLAVE, mask); >> + >> + /* get the DMA interrupt */ >> + this->dma_data.chan_irq = r_dma->start + >> + ((r_dma->start != r_dma->end) ? (i - r->start) : 0); >> + >> + 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; >> + } >> + >> + *low_channel = r->start; >> + *high_channel = r->end; > *high_channel = i; > > This will acquire only those DMA channels that are actually needed. > > [...] >> +static int read_page_prepare(struct gpmi_nfc_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; >> + dma_addr_t destination_phys = ~0; >> + >> + if (virt_addr_valid(destination)) >> + destination_phys = dma_map_single(dev, (void *)destination, >> > pointless cast. > > [...] >> +static int mil_alloc_dma_buffer(struct gpmi_nfc_data *this) >> +{ >> + struct nfc_geometry *geo =&this->nfc_geometry; >> + struct device *dev = this->dev; >> + struct mil *mil =&this->mil; >> + >> + /* [1] Allocate a command buffer. PAGE_SIZE is enough. */ >> + mil->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA); >> + if (mil->cmd_buffer == NULL) >> + goto error_alloc; >> + >> + /* [2] Allocate a read/write data buffer. PAGE_SIZE is enough. */ >> + mil->data_buffer_dma = kzalloc(PAGE_SIZE, GFP_DMA); >> + if (mil->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. >> + */ >> + mil->page_buffer_size = geo->payload_size_in_bytes + >> + geo->auxiliary_size_in_bytes; >> + >> + mil->page_buffer_virt = dma_alloc_coherent(dev, mil->page_buffer_size, >> + &mil->page_buffer_phys, GFP_DMA); >> + if (!mil->page_buffer_virt) >> + goto error_alloc; >> + >> + >> + /* Slice up the page buffer. */ >> + mil->payload_virt = mil->page_buffer_virt; >> + mil->payload_phys = mil->page_buffer_phys; >> + mil->auxiliary_virt = ((char *) mil->payload_virt) + >> > unnecessary cast. > > [...] > > The functions from here thru gpmi_nfc_probe could be marked __devinit, > so they can be discarded after boot. >> +static int nand_boot_set_geometry(struct gpmi_nfc_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; >> + >> + if (gpmi_debug& GPMI_DEBUG_INIT) >> + log("stride size in page : %d, search areas : %d", >> + geometry->stride_size_in_pages, >> + geometry->search_area_stride_exponent); >> + return 0; >> +} >> + >> +static const char *fingerprint = "STMP"; >> +static int mx23_check_transcription_stamp(struct gpmi_nfc_data *this) >> +{ >> + struct boot_rom_geometry *rom_geo =&this->rom_geometry; >> + struct mil *mil =&this->mil; >> + struct mtd_info *mtd =&mil->mtd; >> + struct nand_chip *nand =&mil->nand; >> + unsigned int search_area_size_in_strides; >> + unsigned int stride; >> + unsigned int page; >> + loff_t byte; >> + uint8_t *buffer = nand->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; >> + >> + /* Select chip 0. */ >> + saved_chip_number = mil->current_chip; >> + nand->select_chip(mtd, 0); >> + >> + /* >> + * Loop through the first search area, looking for the NCB fingerprint. >> + */ >> + pr_info("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; >> + >> + pr_info(" 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. >> + */ >> + nand->cmdfunc(mtd, NAND_CMD_READ0, 12, page); >> + nand->read_buf(mtd, buffer, strlen(fingerprint)); >> + >> + /* Look for the fingerprint. */ >> + if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { >> + found_an_ncb_fingerprint = true; >> + break; >> + } >> + >> + } >> + >> + /* Deselect chip 0. */ >> + nand->select_chip(mtd, saved_chip_number); >> + >> + if (found_an_ncb_fingerprint) >> + pr_info(" Found a fingerprint\n"); >> + else >> + pr_info(" No fingerprint found\n"); >> + return found_an_ncb_fingerprint; >> +} >> + >> +/* Writes a transcription stamp. */ >> +static int mx23_write_transcription_stamp(struct gpmi_nfc_data *this) >> +{ >> + struct device *dev = this->dev; >> + struct boot_rom_geometry *rom_geo =&this->rom_geometry; >> + struct mil *mil =&this->mil; >> + struct mtd_info *mtd =&mil->mtd; >> + struct nand_chip *nand =&mil->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 = nand->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; >> + >> + pr_info("-------------------------------------------\n"); >> + pr_info("Search Area Geometry\n"); >> + pr_info("-------------------------------------------\n"); >> + pr_info("Search Area Size in Blocks : %u", search_area_size_in_blocks); >> + pr_info("Search Area Size in Strides: %u", search_area_size_in_strides); >> + pr_info("Search Area Size in Pages : %u", search_area_size_in_pages); >> + >> + /* Select chip 0. */ >> + saved_chip_number = mil->current_chip; >> + nand->select_chip(mtd, 0); >> + >> + /* Loop over blocks in the first search area, erasing them. */ >> + pr_info("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. */ >> + pr_info(" Erasing block 0x%x\n", block); >> + nand->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); >> + nand->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); >> + >> + /* Wait for the erase to finish. */ >> + status = nand->waitfunc(mtd, nand); >> + 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(nand->oob_poi, ~0, mtd->oobsize); >> + memcpy(buffer + 12, fingerprint, strlen(fingerprint)); >> + >> + /* Loop through the first search area, writing NCB fingerprints. */ >> + pr_info("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. */ >> + pr_info(" Writing an NCB fingerprint in page 0x%x\n", page); >> + nand->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); >> + nand->ecc.write_page_raw(mtd, nand, buffer); >> + nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); >> + >> + /* Wait for the write to finish. */ >> + status = nand->waitfunc(mtd, nand); >> + if (status& NAND_STATUS_FAIL) >> + dev_err(dev, "[%s] Write failed.\n", __func__); >> + } >> + >> + /* Deselect chip 0. */ >> + nand->select_chip(mtd, saved_chip_number); >> + return 0; >> +} >> + >> +int mx23_boot_init(struct gpmi_nfc_data *this) >> +{ >> + struct device *dev = this->dev; >> + struct mil *mil =&this->mil; >> + struct nand_chip *nand =&mil->nand; >> + struct mtd_info *mtd =&mil->mtd; >> + unsigned int block_count; >> + unsigned int block; >> + int chip; >> + int page; >> + loff_t byte; >> + uint8_t block_mark; >> + int error = 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. >> + */ >> + pr_info("Transcribing bad block marks...\n"); >> + >> + /* Compute the number of blocks in the entire medium. */ >> + block_count = nand->chipsize>> nand->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. >> + */ >> + chip = block>> (nand->chip_shift - nand->phys_erase_shift); >> + page = block<< (nand->phys_erase_shift - nand->page_shift); >> + byte = block<< nand->phys_erase_shift; >> + >> + /* Select the chip. */ >> + nand->select_chip(mtd, chip); >> + >> + /* Send the command to read the conventional block mark. */ >> + nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); >> + >> + /* Read the conventional block mark. */ >> + block_mark = nand->read_byte(mtd); >> + >> + /* >> + * 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. >> + * >> + * Notice that we have to explicitly set the marking_a_bad_block >> + * member before we call through the block_markbad function >> + * pointer in the owning struct nand_chip. If we could call >> + * though the block_markbad function pointer in the owning >> + * struct mtd_info, which we have hooked, then this would be >> + * taken care of for us. Unfortunately, we can't because that >> + * higher-level code path will do things like consulting the >> + * in-memory bad block table -- which doesn't even exist yet! >> + * So, we have to call at a lower level and handle some details >> + * ourselves. >> + */ >> + if (block_mark != 0xff) { >> + pr_info("Transcribing mark in block %u\n", block); >> + mil->marking_a_bad_block = true; >> + error = nand->block_markbad(mtd, byte); >> + mil->marking_a_bad_block = false; >> + if (error) >> + dev_err(dev, "Failed to mark block bad with " >> + "error %d\n", error); >> + } >> + >> + /* Deselect the chip. */ >> + nand->select_chip(mtd, -1); >> + } >> + >> + /* Write the stamp that indicates we've transcribed the block marks. */ >> + mx23_write_transcription_stamp(this); >> + return 0; >> +} >> + >> +static int nand_boot_init(struct gpmi_nfc_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 void show_nfc_geometry(struct nfc_geometry *geo) >> +{ >> + pr_info("---------------------------------------\n"); >> + pr_info(" NFC Geometry (used by BCH)\n"); >> + pr_info("---------------------------------------\n"); >> + pr_info("ECC Algorithm : %s\n", geo->ecc_algorithm); >> + pr_info("ECC Strength : %u\n", geo->ecc_strength); >> + pr_info("Page Size in Bytes : %u\n", geo->page_size_in_bytes); >> + pr_info("Metadata Size in Bytes : %u\n", geo->metadata_size_in_bytes); >> + pr_info("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size_in_bytes); >> + pr_info("ECC Chunk Count : %u\n", geo->ecc_chunk_count); >> + pr_info("Payload Size in Bytes : %u\n", geo->payload_size_in_bytes); >> + pr_info("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size_in_bytes); >> + pr_info("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset); >> + pr_info("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset); >> + pr_info("Block Mark Bit Offset : %u\n", geo->block_mark_bit_offset); >> +} >> + >> +static int mil_set_geometry(struct gpmi_nfc_data *this) >> +{ >> + struct nfc_hal *nfc = this->nfc; >> + struct nfc_geometry *geo =&this->nfc_geometry; >> + int error; >> + >> + /* Free the temporary DMA memory for read ID case */ >> + mil_free_dma_buffer(this); >> + >> + /* Set up the NFC geometry which is used by BCH. */ >> + error = nfc->set_geometry(this); >> + if (error != 0) { >> + log("NFC set geometry error : %d", error); >> + return error; >> + } >> + if (gpmi_debug& GPMI_DEBUG_INIT) >> + show_nfc_geometry(geo); >> + >> + /* Alloc the new DMA buffers according to the pagesize and oobsize */ >> + return mil_alloc_dma_buffer(this); >> +} >> + >> +static int mil_pre_bbt_scan(struct gpmi_nfc_data *this) >> +{ >> + struct nand_chip *nand =&this->mil.nand; >> + struct mtd_info *mtd =&this->mil.mtd; >> + struct nand_ecclayout *layout = nand->ecc.layout; >> + struct nfc_hal *nfc = this->nfc; >> + int error; >> + >> + /* fix the ECC layout before the scanning */ >> + layout->eccbytes = 0; >> + layout->oobavail = mtd->oobsize; >> + layout->oobfree[0].offset = 0; >> + layout->oobfree[0].length = mtd->oobsize; >> + >> + mtd->oobavail = nand->ecc.layout->oobavail; >> + >> + /* Set up swap block-mark, must be set before the mil_set_geometry() */ >> + if (GPMI_IS_MX23(this)) >> + this->swap_block_mark = false; >> + else >> + this->swap_block_mark = true; >> + >> + /* Set up the medium geometry */ >> + error = mil_set_geometry(this); >> + if (error) >> + return error; >> + >> + /* extra init */ >> + if (nfc->extra_init) { >> + error = nfc->extra_init(this); >> + if (error != 0) >> + return error; >> + } >> + >> + /* NAND boot init, depends on the mil_set_geometry(). */ >> + return nand_boot_init(this); >> +} >> + >> +static int mil_scan_bbt(struct mtd_info *mtd) >> +{ >> + struct nand_chip *nand = mtd->priv; >> + struct gpmi_nfc_data *this = nand->priv; >> + int error; >> + >> + /* Prepare for the BBT scan. */ >> + error = mil_pre_bbt_scan(this); >> + if (error) >> + return error; >> + >> + /* use the default BBT implementation */ >> + return nand_default_bbt(mtd); >> +} >> + >> +static const char *cmd_parse = "cmdlinepart"; >> > This should be a NULL terminated list of strings: > static const char *cmd_parse[] = { "cmdlinepart", NULL }; > thanks a lot. >> +static int mil_partitions_init(struct gpmi_nfc_data *this) >> +{ >> + struct gpmi_nfc_platform_data *pdata = this->pdata; >> + struct mil *mil =&this->mil; >> + struct mtd_info *mtd =&mil->mtd; >> + >> + /* use the command line for simple partitions layout */ >> + mil->partition_count = parse_mtd_partitions(mtd,&cmd_parse, >> + &mil->partitions, 0); >> + if (mil->partition_count) >> + return add_mtd_partitions(mtd, mil->partitions, >> + mil->partition_count); >> + >> + /* The complicated partitions layout uses this. */ >> + if (pdata->partitions&& pdata->partition_count> 0) >> + return add_mtd_partitions(mtd, pdata->partitions, >> + pdata->partition_count); >> + return 0; > How about: > return mtd_add_device(mtd); > so you will get the whole flash registered in case there are no > partitions defined. > no problem. >> +#ifdef CONFIG_PM >> +static int gpmi_nfc_suspend(struct platform_device *pdev, pm_message_t state) >> +{ >> + return 0; >> +} >> +static int gpmi_nfc_resume(struct platform_device *pdev) >> +{ >> + return 0; >> +} >> +#else >> +#define gpmi_nfc_suspend NULL >> +#define gpmi_nfc_resume NULL >> +#endif >> + > There is no point in adding empty suspend/resume functions. > Furthermore you should use dev_pm_ops. > Ok. I think the gpmi needs to do nothing when suspend. Best Regards Huang shijie > Lothar Wa?mann