[PATCH v6 1/3] MTD : add the common code for GPMI controller driver
Lothar Waßmann
LW at KARO-electronics.de
Thu Apr 21 03:49:39 EDT 2011
Huang Shijie writes:
> These files contain the common code for the GPMI driver.
>
> Signed-off-by: Huang Shijie <b32955 at freescale.com>
> ---
> 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 <linux/slab.h>
> +#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.
> +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.
[...]
> +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;
> + 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 };
> +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.
> +#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.
Lothar Waßmann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info at karo-electronics.de
___________________________________________________________
More information about the linux-mtd
mailing list