All of lore.kernel.org
 help / color / mirror / Atom feed
From: Huang Shijie <b32955@freescale.com>
To: "Lothar Waßmann" <LW@KARO-electronics.de>
Cc: linux@arm.linux.org.uk, linux-mtd@lists.infradead.org,
	dwmw2@infradead.org, linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH 2/7] add the common code for GPMI driver
Date: Wed, 23 Mar 2011 11:41:25 +0800	[thread overview]
Message-ID: <4D896BE5.7030208@freescale.com> (raw)
In-Reply-To: <19848.38860.624803.339369@ipc1.ka-ro>

Hi:

>> +
>> +static int acquire_register_block(struct gpmi_nfc_data *this,
>> +			const char *resource_name, void **reg_block_base)
>> +{
>> +	struct platform_device  *pdev = this->pdev;
>> +	struct resource         *r;
>> +	void                    *p;
>> +
>> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, resource_name);
>> +	if (!r) {
>> +		log("Can't get resource information for '%s'", resource_name);
>> +		return -ENXIO;
>> +	}
>> +
>> +	/* remap the register block */
>> +	p = ioremap(r->start, r->end - r->start + 1);
>                                ^^^^^^^^^^^^^^^^^^^^^
> resource_size(r)
>
thanks.

>
>> +static int acquire_interrupt(struct gpmi_nfc_data *this,
>> +			const char *resource_name,
>> +			irq_handler_t interrupt_handler, int *lno, int *hno)
>> +{
>> +	struct platform_device  *pdev = this->pdev;
>> +	struct resource         *r;
>> +	int                     err = 0;
>>
> Useless initialization.
>
ok.
>> +	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, resource_name);
>> +	if (!r) {
>> +		log("Can't get resource information for '%s'", resource_name);
>> +		return -ENXIO;
>> +	}
>> +
>> +	BUG_ON(r->start != r->end);
>> +	err = request_irq(r->start, interrupt_handler, 0, resource_name, this);
>> +	if (err) {
>> +		log("Can't own %s", resource_name);
>> +		return -EIO;
>>
> Promote the error code from request_irq().
>
thanks.
>> +	}
>> +
>> +	*lno = r->start;
>> +	*hno = r->end;
>> +	return 0;
>> +}
>> +
>> +static void release_interrupt(struct gpmi_nfc_data *this,
>> +			int low_interrupt_number, int high_interrupt_number)
>> +{
>> +	int i;
>> +	for (i = low_interrupt_number; i<= high_interrupt_number; i++)
>> +		free_irq(i, this);
>> +}
>> +
>>
>> +
>> +static void release_dma_channels(struct gpmi_nfc_data *this)
>> +{
>> +	unsigned int   i;
>> +	for (i = 0; i<= DMA_CHANS; i++)
>>
> Obiwan error! should be 'i<  DMA_CHANS'
>
  thanks a lot!
>> +		if (this->dma_chans[i]) {
>> +			dma_release_channel(this->dma_chans[i]);
>> +			this->dma_chans[i] = NULL;
>> +		}
>> +}
>> +
>>
>> +static inline int acquire_clock(struct gpmi_nfc_data *this,
>> +			const char *clock_name, struct clk **clock)
>> +{
>> +	struct clk *c;
>> +
>> +	c = clk_get(this->dev, clock_name);
>> +	if (IS_ERR(c)) {
>> +		log("Can't own clock %s", clock_name);
>> +		return PTR_ERR(c);
>> +	}
>> +	*clock = c;
>> +	return 0;
>> +}
>> +
>> +static void release_clock(struct gpmi_nfc_data *this, struct clk *clock)
>> +{
>> +	clk_disable(clock);
>>
> Unbalanced clk_disable().
thanks.
>> +	clk_put(clock);
>> +}
>> +
>> +static int acquire_resources(struct gpmi_nfc_data *this)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata     =  this->pdata;
>> +	struct resources               *resources =&this->resources;
>> +	int                            error      = 0;
>>
> Useless initialization.
ok.
>> +
>> +	/* Attempt to acquire our clock. */
>> +	error = acquire_clock(this, pdata->clock_name,&resources->clock);
>>
> Abuse of the clock API. Don't pass clock names via platform_data.
>
thanks, I will change it in next version.
>> +static int set_up_nfc_hal(struct gpmi_nfc_data *this)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata = this->pdata;
>> +	struct nfc_hal                 *nfc;
>> +	int                            error = 0;
>>
> Useless initialization.
>
>> +	/*
>> +	 * This structure contains the "safe" GPMI timing that should succeed
>> +	 * with any NAND Flash device
>> +	 * (although, with less-than-optimal performance).
>> +	 */
>> +	static 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,
>> +	};
>> +
>> +	switch (pdata->chip_version) {
>> +	case CHIP_VERSION_IMX23:
>> +		nfc =&gpmi_nfc_hal_imx23;
>> +		break;
>> +	case CHIP_VERSION_IMX28:
>> +		nfc =&gpmi_nfc_hal_imx28;
>> +		break;
>> +	default:
>> +		log("Unkown NFC version %u", pdata->chip_version);
>> +		return -ENXIO;
>> +	}
>>
> Use platform_ids to distinguish between HW variants.
>
ok.
>> +
>> +	this->nfc = nfc;
>> +
>> +	/* Initialize the NFC HAL. */
>> +	error = nfc->init(this);
>> +	if (error)
>> +		return error;
>> +
>> +	/* Set up safe timing. */
>> +	nfc->set_timing(this,&safe_timing);
>> +	return 0;
>> +}
>> +
>> +static int set_up_boot_rom_helper(struct gpmi_nfc_data *this)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata = this->pdata;
>> +	struct boot_rom_helper         *rom;
>> +
>> +	switch (pdata->chip_version) {
>> +	case CHIP_VERSION_IMX23:
>> +		rom =&gpmi_nfc_boot_rom_imx23;
>> +		break;
>> +	case CHIP_VERSION_IMX28:
>> +		rom =&gpmi_nfc_boot_rom_imx28;
>> +		break;
>> +	default:
>> +		log("Unkown NFC version %u", pdata->chip_version);
>> +		return -ENXIO;
>> +	}
>> +
>> +	pr_info("Boot ROM: Version %u, %s\n", rom->version, rom->description);
>> +	this->rom = rom;
>> +	return 0;
>> +}
>> +
>> +/* Creates/Removes sysfs files for this device.*/
>> +static void manage_sysfs_files(struct gpmi_nfc_data *this, int create)
>> +{
>> +	struct device            *dev = this->dev;
>> +	int                      error;
>> +	unsigned int             i;
>> +	struct device_attribute  **attr;
>> +
>> +	for (i = 0, attr = device_attributes;
>> +			i<  ARRAY_SIZE(device_attributes); i++, attr++) {
>> +
>> +		if (create) {
>> +			error = device_create_file(dev, *attr);
>> +			if (error) {
>> +				while (--attr>= device_attributes)
>> +					device_remove_file(dev, *attr);
>> +				return;
>> +			}
>> +		} else {
>> +			device_remove_file(dev, *attr);
>> +		}
>> +	}
>> +}
>> +
>> +static int gpmi_nfc_probe(struct platform_device *pdev)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata = pdev->dev.platform_data;
>> +	struct gpmi_nfc_data           *this  = 0;
>> +	int                            error  = 0;
>>
> Useless initialization (NB: pointers are initialized with 'NULL' not
> '0').
>
thanks.

I have already changed a lot, but i missed this one.
>> +/* This structure represents this driver to the platform management system. */
>> +static struct platform_driver gpmi_nfc_driver = {
>> +	.driver = {
>> +		.name = GPMI_NFC_DRIVER_NAME,
>> +	},
>> +	.probe   = gpmi_nfc_probe,
>> +	.remove  = __exit_p(gpmi_nfc_remove),
>> +	.suspend = gpmi_nfc_suspend,
>> +	.resume  = gpmi_nfc_resume,
>> +};
>> +
>> +static int __init gpmi_nfc_init(void)
>> +{
>> +	printk(KERN_INFO "GPMI NFC driver registered. (IMX)\n");
>> +	if (platform_driver_register(&gpmi_nfc_driver) != 0) {
>> +		pr_err("i.MX GPMI NFC driver registration failed\n");
>> +		return -ENODEV;
>>
> Promote the error code from platform_driver_register().
>
ok
>> +	}
>> +	return 0;
>> +}
>> +
>> +static void __exit gpmi_nfc_exit(void)
>> +{
>> +	platform_driver_unregister(&gpmi_nfc_driver);
>> +}
>> +
>> +module_init(gpmi_nfc_init);
>> +module_exit(gpmi_nfc_exit);
>> +
>> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
>> +MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
>> new file mode 100644
>> index 0000000..b5a2d77
>> --- /dev/null
>> +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
>> @@ -0,0 +1,520 @@
>> +/*
>> + * 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.
>> + */
>> +
>> +#ifndef __DRIVERS_MTD_NAND_GPMI_NFC_H
>> +#define __DRIVERS_MTD_NAND_GPMI_NFC_H
>> +
>> +/* Linux header files. */
>> +#include<linux/err.h>
>> +#include<linux/init.h>
>> +#include<linux/module.h>
>> +#include<linux/io.h>
>> +#include<linux/interrupt.h>
>> +#include<linux/clk.h>
>> +#include<linux/delay.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/dma-mapping.h>
>> +#include<linux/mtd/mtd.h>
>> +#include<linux/mtd/nand.h>
>> +#include<linux/mtd/partitions.h>
>> +#include<linux/mtd/concat.h>
>> +#include<linux/dmaengine.h>
>> +#include<asm/sizes.h>
>> +
>> +/* Platform header files. */
>> +#include<mach/mxs.h>
>> +#include<mach/common.h>
>> +#include<mach/dma.h>
>> +#include<mach/gpmi-nfc.h>
>> +#include<mach/system.h>
>> +#include<mach/clock.h>
>> +
>> +/* Driver header files. */
>> +#include "nand_device_info.h"
>> +
>> +/*
>> + *------------------------------------------------------------------------------
>> + * Fundamental Data Structures
>> + *------------------------------------------------------------------------------
>> + */
>> +
>> +/**
>> + * struct resources - The collection of resources the driver needs.
>> + *
>> + * @gpmi_regs:         A pointer to the GPMI registers.
>> + * @bch_regs:          A pointer to the BCH registers.
>> + * @bch_interrupt:     The BCH interrupt number.
>> + * @dma_low_channel:   The low  DMA channel.
>> + * @dma_high_channel:  The high DMA channel.
>> + * @clock:             A pointer to the struct clk for the NFC's clock.
>> + */
>> +struct resources {
>> +	void          *gpmi_regs;
>> +	void          *bch_regs;
>> +	unsigned int  bch_low_interrupt;
>> +	unsigned int  bch_high_interrupt;
>> +	unsigned int  dma_low_channel;
>> +	unsigned int  dma_high_channel;
>> +	struct clk    *clock;
>> +};
>> +
>> +/**
>> + * struct mil - State for the MTD Interface Layer.
>> + *
>> + * @nand:                    The NAND Flash MTD data structure that represents
>> + *                           the NAND Flash medium.
>> + * @mtd:                     The MTD data structure that represents the NAND
>> + *                           Flash medium.
>> + * @oob_layout:              A structure that describes how bytes are laid out
>> + *                           in the OOB.
>> + * @general_use_mtd:         A pointer to an MTD we export for general use.
>> + *                           This *may* simply be a pointer to the mtd field, if
>> + *                           we've been instructed NOT to protect the boot
>> + *                           areas.
>> + * @partitions:              A pointer to a set of partitions applied to the
>> + *                           general use MTD.
>> + * @partition_count:         The number of partitions.
>> + * @current_chip:            The chip currently selected by the NAND Fash MTD
>> + *                           code. A negative value indicates that no chip is
>> + *                           selected.
>> + * @command_length:          The length of the command that appears in the
>> + *                           command buffer (see cmd_virt, below).
>> + * @ignore_bad_block_marks:  Indicates we are ignoring bad block marks.
>> + * @saved_bbt:               A saved pointer to the in-memory NAND Flash MTD bad
>> + *                           block table. See show_device_ignorebad() for more
>> + *                           details.
>> + * @marking_a_bad_block:     Indicates the caller is marking a bad block. See
>> + *                           mil_ecc_write_oob() for details.
>> + * @hooked_block_markbad:    A pointer to the block_markbad() function we
>> + *                           we "hooked." See mil_ecc_write_oob() for details.
>> + * @upper_buf:               The buffer passed from upper layer.
>> + * @upper_len:               The buffer len passed from upper layer.
>> + * @direct_dma_map_ok:       Is the direct DMA map is good for the upper_buf?
>> + * @cmd_sgl/cmd_buffer:      For NAND command.
>> + * @data_sgl/data_buffer_dma:For NAND DATA ops.
>> + * @page_buffer_virt:        A pointer to a DMA-coherent buffer we use for
>> + *                           reading and writing pages. This buffer includes
>> + *                           space for both the payload data and the auxiliary
>> + *                           data (including status bytes, but not syndrome
>> + *                           bytes).
>> + * @page_buffer_phys:        The physical address for the page_buffer_virt
>> + *                           buffer.
>> + * @page_buffer_size:        The size of the page buffer.
>> + * @payload_virt:            A pointer to a location in the page buffer used
>> + *                           for payload bytes. The size of this buffer is
>> + *                           determined by struct nfc_geometry.
>> + * @payload_phys:            The physical address for payload_virt.
>> + * @auxiliary_virt:          A pointer to a location in the page buffer used
>> + *                           for auxiliary bytes. The size of this buffer is
>> + *                           determined by struct nfc_geometry.
>> + * @auxiliary_phys:          The physical address for auxiliary_virt.
>> + */
>> +struct mil {
>> +	/* MTD Data Structures */
>> +	struct nand_chip       nand;
>> +	struct mtd_info        mtd;
>> +	struct nand_ecclayout  oob_layout;
>> +
>> +	/* Partitioning and Boot Area Protection */
>> +	struct mtd_info        *general_use_mtd;
>> +	struct mtd_partition   *partitions;
>> +	unsigned int           partition_count;
>> +
>> +	/* General-use Variables */
>> +	int                    current_chip;
>> +	unsigned int           command_length;
>> +	int                    ignore_bad_block_marks;
>> +	void                   *saved_bbt;
>> +
>> +	/* MTD Function Pointer Hooks */
>> +	int                    marking_a_bad_block;
>> +	int                    (*hooked_block_markbad)(struct mtd_info *mtd,
>> +					loff_t ofs);
>> +
>> +	/* from upper layer */
>> +	uint8_t			*upper_buf;
>> +	int			upper_len;
>> +
>> +	/* DMA */
>> +	bool			direct_dma_map_ok;
>> +
>> +	struct scatterlist	cmd_sgl;
>> +	char			*cmd_buffer;
>> +
>> +	struct scatterlist	data_sgl;
>> +	char			*data_buffer_dma;
>> +
>> +	void                   *page_buffer_virt;
>> +	dma_addr_t             page_buffer_phys;
>> +	unsigned int           page_buffer_size;
>> +
>> +	void                   *payload_virt;
>> +	dma_addr_t             payload_phys;
>> +
>> +	void                   *auxiliary_virt;
>> +	dma_addr_t             auxiliary_phys;
>> +};
>>
> IMO this struct should be removed and the members integrated into the
> struct gpmi_nfc_data.
>
>> +
>> +/**
>> + * struct nfc_geometry - NFC geometry description.
>> + *
>> + * This structure describes the NFC's view of the medium geometry.
>> + *
>> + * @ecc_algorithm:            The human-readable name of the ECC algorithm
>> + *                            (e.g., "Reed-Solomon" or "BCH").
>> + * @ecc_strength:             A number that describes the strength of the ECC
>> + *                            algorithm.
>> + * @page_size_in_bytes:       The size, in bytes, of a physical page, including
>> + *                            both data and OOB.
>> + * @metadata_size_in_bytes:   The size, in bytes, of the metadata.
>> + * @ecc_chunk_size_in_bytes:  The size, in bytes, of a single ECC chunk. Note
>> + *                            the first chunk in the page includes both data and
>> + *                            metadata, so it's a bit larger than this value.
>> + * @ecc_chunk_count:          The number of ECC chunks in the page,
>> + * @payload_size_in_bytes:    The size, in bytes, of the payload buffer.
>> + * @auxiliary_size_in_bytes:  The size, in bytes, of the auxiliary buffer.
>> + * @auxiliary_status_offset:  The offset into the auxiliary buffer at which
>> + *                            the ECC status appears.
>> + * @block_mark_byte_offset:   The byte offset in the ECC-based page view at
>> + *                            which the underlying physical block mark appears.
>> + * @block_mark_bit_offset:    The bit offset into the ECC-based page view at
>> + *                            which the underlying physical block mark appears.
>> + */
>> +struct nfc_geometry {
>> +	char          *ecc_algorithm;
>> +	unsigned int  ecc_strength;
>> +	unsigned int  page_size_in_bytes;
>> +	unsigned int  metadata_size_in_bytes;
>> +	unsigned int  ecc_chunk_size_in_bytes;
>> +	unsigned int  ecc_chunk_count;
>> +	unsigned int  payload_size_in_bytes;
>> +	unsigned int  auxiliary_size_in_bytes;
>> +	unsigned int  auxiliary_status_offset;
>> +	unsigned int  block_mark_byte_offset;
>> +	unsigned int  block_mark_bit_offset;
>> +};
> Most of these parameters are already available in the mtd_info
> structure. No need to duplicate them here!
I will remove some ones.
>> +
>> +/**
>> + * struct boot_rom_geometry - Boot ROM geometry description.
>> + *
>> + * This structure encapsulates decisions made by the Boot ROM Helper.
>> + *
>> + * @boot_area_count:             The number of boot areas. The first boot area
>> + *                               appears at the beginning of chip 0, the next
>> + *                               at the beginning of chip 1, etc.
>> + * @boot_area_size_in_bytes:     The size, in bytes, of each boot area.
>> + * @stride_size_in_pages:        The size of a boot block stride, in pages.
>> + * @search_area_stride_exponent: The logarithm to base 2 of the size of a
>> + *                               search area in boot block strides.
>> + */
>> +struct boot_rom_geometry {
>> +	unsigned int  boot_area_count;
>> +	unsigned int  boot_area_size_in_bytes;
>> +	unsigned int  stride_size_in_pages;
>> +	unsigned int  search_area_stride_exponent;
>> +};
>>
> IMO the whole boot_rom stuff should not be part of an mtd chip driver,
> but has its place in the mtd partition handlers.
>
I have explained this in preview email. thanks
>> +/* Can we use the upper's buffer directly for DMA? */
>> +void prepare_data_dma(struct gpmi_nfc_data *this, enum dma_data_direction dr)
>> +{
>> +	struct mil *mil =&this->mil;
>> +	struct scatterlist *sgl =&mil->data_sgl;
>> +	int ret = 0;
>> +
>> +	mil->direct_dma_map_ok = true;
>> +
>> +	/* first try to map the upper buffer directly */
>> +	sg_init_one(sgl, mil->upper_buf, mil->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, mil->data_buffer_dma, PAGE_SIZE);
>> +		ret = dma_map_sg(this->dev, sgl, 1, dr);
>> +		BUG_ON(ret == 0);
>> +
>> +		if (dr == DMA_TO_DEVICE)
>> +			memcpy(mil->data_buffer_dma, mil->upper_buf,
>> +				mil->upper_len);
>> +		mil->direct_dma_map_ok = false;
>> +	}
>> +	sgl->length = mil->upper_len;
>>
> This has already been done by sg_init_one().
>
thanks.
>> +}
>> +
>> +/* 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:
>> +		if (mil->direct_dma_map_ok == false)
>> +			memcpy(mil->upper_buf, (char *)mil->data_buffer_dma,
>> +				mil->upper_len);
>> +		dma_unmap_sg(this->dev,&mil->data_sgl, 1, DMA_FROM_DEVICE);
>> +		break;
>> +
>> +	case DMA_FOR_WRITE_DATA:
>> +		dma_unmap_sg(this->dev,&mil->data_sgl, 1, DMA_TO_DEVICE);
>> +		break;
>> +
>> +	case DMA_FOR_READ_ECC_PAGE:
>> +	case DMA_FOR_WRITE_ECC_PAGE:
>> +		break;
>> +
>> +	default:
>> +		BUG();
>> +	}
>> +}
>> +
>>
>> +static uint8_t mil_read_byte(struct mtd_info *mtd)
>> +{
>> +	uint8_t  byte = 0;
>> +	mil_read_buf(mtd, (uint8_t *)&byte, 1);
>>
> With CONFIG_DMA_API_DEBUG enabled this would blow up right in your
> face:
> |gpmi-nfc gpmi-nfc: DMA-API: device driver maps memory fromstack [addr=c732bd3f]
>
Thanks a lot.
>> +	return byte;
>> +}
>> +
>> +/**
>> + * mil_handle_block_mark_swapping() - Handles block mark swapping.
>> + *
>> + * Note that, when this function is called, it doesn't know whether it's
>> + * swapping the block mark, or swapping it *back* -- but it doesn't matter
>> + * because the the operation is the same.
>> + *
>> + * @this:       Per-device data.
>> + * @payload:    A pointer to the payload buffer.
>> + * @auxiliary:  A pointer to the auxiliary buffer.
>> + */
>> +static void mil_handle_block_mark_swapping(struct gpmi_nfc_data *this,
>> +						void *payload, void *auxiliary)
>> +{
>> +	struct nfc_geometry     *nfc_geo =&this->nfc_geometry;
>> +	struct boot_rom_helper  *rom     =  this->rom;
>> +	unsigned char           *p;
>> +	unsigned char           *a;
>> +	unsigned int            bit;
>> +	unsigned char           mask;
>> +	unsigned char           from_data;
>> +	unsigned char           from_oob;
>> +
>> +	/* Check if we're doing block mark swapping. */
>> +	if (!rom->swap_block_mark)
>> +		return;
>> +
>> +	/*
>> +	 * If control arrives here, we're swapping. Make some convenience
>> +	 * variables.
>> +	 */
>> +	bit = nfc_geo->block_mark_bit_offset;
>> +	p   = ((unsigned char *) payload) + nfc_geo->block_mark_byte_offset;
>>
> Useless cast.
ok.
>> +	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 mil_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
>> +				uint8_t *buf, int page)
>> +{
>> +	struct gpmi_nfc_data    *this    = nand->priv;
>> +	struct nfc_hal          *nfc     =  this->nfc;
>> +	struct nfc_geometry     *nfc_geo =&this->nfc_geometry;
>> +	struct mil              *mil     =&this->mil;
>> +	void                    *payload_virt   =  0;
>> +	dma_addr_t              payload_phys    = ~0;
>> +	void                    *auxiliary_virt =  0;
>> +	dma_addr_t              auxiliary_phys  = ~0;
> Useless initialization.
>
ok.
>> +	unsigned int            i;
>> +	unsigned char           *status;
>> +	unsigned int            failed;
>> +	unsigned int            corrected;
>> +	int                     error = 0;
>> +
> Dto.
>
>> +	error = read_page_prepare(this, buf, mtd->writesize,
>> +					mil->payload_virt, mil->payload_phys,
>> +					nfc_geo->payload_size_in_bytes,
>> +					&payload_virt,&payload_phys);
>> +	if (error) {
>> +		log("Inadequate DMA buffer");
>> +		error = -ENOMEM;
>> +		return error;
>> +	}
>> +	auxiliary_virt = mil->auxiliary_virt;
>> +	auxiliary_phys = mil->auxiliary_phys;
>> +
>> +	/* ask the NFC */
>> +	error = nfc->read_page(this, payload_phys, auxiliary_phys);
>> +	if (error) {
>> +		log("Error in ECC-based read: %d", error);
>> +		goto exit_nfc;
>> +	}
>> +
>> +	/* handle the block mark swapping */
>> +	mil_handle_block_mark_swapping(this, payload_virt, auxiliary_virt);
>> +
>> +	/* Loop over status bytes, accumulating ECC status. */
>> +	failed		= 0;
>> +	corrected	= 0;
>> +	status		= ((unsigned char *) auxiliary_virt) +
>> +					nfc_geo->auxiliary_status_offset;
>>
> Useless cast.
>> +
>> +	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 mil_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(nand->oob_poi, ~0, mtd->oobsize);
>> +	nand->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
>> +
>> +exit_nfc:
>> +	read_page_end(this, buf, mtd->writesize,
>> +					mil->payload_virt, mil->payload_phys,
>> +					nfc_geo->payload_size_in_bytes,
>> +					payload_virt, payload_phys);
>> +	return error;
>> +}
>> +
>> +static void mil_ecc_write_page(struct mtd_info *mtd,
>> +				struct nand_chip *nand, const uint8_t *buf)
>> +{
>> +	struct gpmi_nfc_data    *this    = nand->priv;
>> +	struct nfc_hal          *nfc     =  this->nfc;
>> +	struct nfc_geometry     *nfc_geo =&this->nfc_geometry;
>> +	struct boot_rom_helper  *rom     =  this->rom;
>> +	struct mil              *mil     =&this->mil;
>> +	const void              *payload_virt   =  0;
>> +	dma_addr_t              payload_phys    = ~0;
>> +	const void              *auxiliary_virt =  0;
>> +	dma_addr_t              auxiliary_phys  = ~0;
>>
> Useless initialization.
>
>> +	int                     error;
>> +
>> +	if (rom->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(mil->payload_virt, buf, mtd->writesize);
>> +		payload_virt = mil->payload_virt;
>> +		payload_phys = mil->payload_phys;
>> +
>> +		memcpy(mil->auxiliary_virt, nand->oob_poi,
>> +				nfc_geo->auxiliary_size_in_bytes);
>> +		auxiliary_virt = mil->auxiliary_virt;
>> +		auxiliary_phys = mil->auxiliary_phys;
>> +
>> +		/* Handle block mark swapping. */
>> +		mil_handle_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.
>> +		 */
>> +		error = send_page_prepare(this,
>> +				buf, mtd->writesize,
>> +				mil->payload_virt, mil->payload_phys,
>> +				nfc_geo->payload_size_in_bytes,
>> +				&payload_virt,&payload_phys);
>> +		if (error) {
>> +			log("Inadequate payload DMA buffer");
>> +			return;
>> +		}
>> +
>> +		error = send_page_prepare(this,
>> +				nand->oob_poi, mtd->oobsize,
>> +				mil->auxiliary_virt, mil->auxiliary_phys,
>> +				nfc_geo->auxiliary_size_in_bytes,
>> +				&auxiliary_virt,&auxiliary_phys);
>> +		if (error) {
>> +			log("Inadequate auxiliary DMA buffer");
>> +			goto exit_auxiliary;
>> +		}
>> +	}
>> +
>> +	/* Ask the NFC. */
>> +	error = nfc->send_page(this, payload_phys, auxiliary_phys);
>> +	if (error)
>> +		log("Error in ECC-based write: %d", error);
>> +
>> +	if (!rom->swap_block_mark) {
>> +		send_page_end(this, nand->oob_poi, mtd->oobsize,
>> +				mil->auxiliary_virt, mil->auxiliary_phys,
>> +				nfc_geo->auxiliary_size_in_bytes,
>> +				auxiliary_virt, auxiliary_phys);
>> +exit_auxiliary:
>> +		send_page_end(this, buf, mtd->writesize,
>> +				mil->payload_virt, mil->payload_phys,
>> +				nfc_geo->payload_size_in_bytes,
>> +				payload_virt, payload_phys);
>> +	}
>> +}
>> +
>> +/**
>> + * mil_hook_block_markbad() - Hooked MTD Interface block_markbad().
>> + *
>> + * This function is a veneer that replaces the function originally installed by
>> + * the NAND Flash MTD code. See the description of the marking_a_bad_block field
>> + * in struct mil for more information about this.
>> + *
>> + * @mtd:  A pointer to the MTD.
>> + * @ofs:  Byte address of the block to mark.
>> + */
>> +static int mil_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)
>> +{
>> +	register struct nand_chip  *chip = mtd->priv;
>> +	struct gpmi_nfc_data       *this = chip->priv;
>> +	struct mil                 *mil  =&this->mil;
>> +	int                        ret;
>> +
>> +	mil->marking_a_bad_block = true;
>> +	ret = mil->hooked_block_markbad(mtd, ofs);
>> +	mil->marking_a_bad_block = false;
>> +	return ret;
>> +}
>> +
>> +/**
>> + * mil_ecc_read_oob() - MTD Interface ecc.read_oob().
>> + *
>> + * 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, so all
>> + *    write operations take measures to protect it.
>> + *
>> + * 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.
>> + *
>> + * @mtd:     A pointer to the owning MTD.
>> + * @nand:    A pointer to the owning NAND Flash MTD.
>> + * @page:    The page number to read.
>> + * @sndcmd:  Indicates this function should send a command to the chip before
>> + *           reading the out-of-band bytes. This is only false for small page
>> + *           chips that support auto-increment.
>> + */
>> +static int mil_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
>> +							int page, int sndcmd)
>> +{
>> +	struct gpmi_nfc_data      *this     = nand->priv;
>> +	struct boot_rom_helper    *rom      =  this->rom;
>> +
>> +	/* clear the OOB buffer */
>> +	memset(nand->oob_poi, ~0, mtd->oobsize);
>> +
>> +	/* Read out the conventional OOB. */
>> +	nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
>> +	nand->read_buf(mtd, nand->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 (!rom->swap_block_mark) {
>> +		/* Read the block mark into the first byte of the OOB buffer. */
>> +		nand->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
>> +		nand->oob_poi[0] = nand->read_byte(mtd);
>> +	}
>> +
>> +	/*
>> +	 * Return true, indicating that the next call to this function must send
>> +	 * a command.
>> +	 */
>> +	return true;
>> +}
>> +
>> +static int mil_ecc_write_oob(struct mtd_info *mtd,
>> +					struct nand_chip *nand, int page)
>> +{
>> +	struct gpmi_nfc_data      *this     = nand->priv;
>> +	struct device             *dev      =  this->dev;
>> +	struct mil                *mil      =&this->mil;
>> +	struct boot_rom_helper    *rom      =  this->rom;
>> +	uint8_t                   block_mark = 0;
>> +	int                       block_mark_column;
>> +	int                       status;
>> +	int                       error = 0;
>> +
>> +	/*
>> +	 * There are fundamental incompatibilities between the i.MX GPMI NFC and
>> +	 * the NAND Flash MTD model that make it essentially impossible to write
>> +	 * the out-of-band bytes.
>> +	 *
>> +	 * We permit *ONE* exception. If the *intent* of writing the OOB is to
>> +	 * mark a block bad, we can do that.
>> +	 */
>> +	if (!mil->marking_a_bad_block) {
>> +		dev_emerg(dev, "This driver doesn't support writing the OOB\n");
>> +		WARN_ON(1);
>> +		error = -EIO;
>> +		goto exit;
>> +	}
>> +
>> +	/*
>> +	 * If control arrives here, we're marking a block bad. First, figure out
>> +	 * where the block mark is.
>> +	 *
>> +	 * If we're using swapping, the block mark is in the conventional
>> +	 * location. Otherwise, we're using transcription, and the block mark
>> +	 * appears in the first byte of the page.
>> +	 */
>> +	if (rom->swap_block_mark)
>> +		block_mark_column = mtd->writesize;
>> +	else
>> +		block_mark_column = 0;
>> +
>> +	/* Write the block mark. */
>> +	nand->cmdfunc(mtd, NAND_CMD_SEQIN, block_mark_column, page);
>> +	nand->write_buf(mtd,&block_mark, 1);
>>
> DMA mapping memory from stack again.
thanks.
>> +	nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
>> +
>> +	status = nand->waitfunc(mtd, nand);
>> +
>> +	/* Check if it worked. */
>> +	if (status&  NAND_STATUS_FAIL)
>> +		error = -EIO;
>> +exit:
>> +	return error;
>> +}
>> +
>> +/**
>> + * mil_block_bad - Claims all blocks are good.
>> + *
>> + * In principle, this function is *only* called when the NAND Flash MTD system
>> + * isn't allowed to keep an in-memory bad block table, so it is forced to ask
>> + * the driver for bad block information.
>> + *
>> + * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
>> + * this function is *only* called when we take it away.
>> + *
>> + * We take away the in-memory BBT when the user sets the "ignorebad" parameter,
>> + * which indicates that all blocks should be reported good.
>> + *
>> + * Thus, this function is only called when we want *all* blocks to look good,
>> + * so it *always* return success.
>> + *
>> + * @mtd:      Ignored.
>> + * @ofs:      Ignored.
>> + * @getchip:  Ignored.
>> + */
>> +static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
>> +{
>> +	return 0;
>> +}
>> +
>> +/* Set up the Boot ROM Helper geometry. */
>> +static int mil_set_boot_rom_helper_geometry(struct gpmi_nfc_data  *this)
>> +{
>> +	struct boot_rom_helper    *rom =  this->rom;
>> +	struct boot_rom_geometry  *geo =&this->rom_geometry;
>> +
>> +	if (rom->set_geometry(this))
>> +		return !0;
>> +
>> +	pr_info("--------------------------------------------\n");
>> +	pr_info("	Boot ROM Geometry\n");
>> +	pr_info("--------------------------------------------\n");
>> +	pr_info("Boot Area Count            : %u\n", geo->boot_area_count);
>> +	pr_info("Boot Area Size in Bytes    : %u (0x%x)\n",
>> +					geo->boot_area_size_in_bytes,
>> +					geo->boot_area_size_in_bytes);
>> +	pr_info("Stride Size in Pages       : %u\n", geo->stride_size_in_pages);
>> +	pr_info("Search Area Stride Exponent: %u\n",
>> +					geo->search_area_stride_exponent);
>> +	return 0;
>> +}
>> +
>> +static int mil_set_geometry(struct gpmi_nfc_data *this)
>> +{
>> +	struct nfc_hal *nfc = this->nfc;
>> +	struct nfc_geometry *geo =&this->nfc_geometry;
>> +
>> +	/* Free the temporary DMA memory for read ID case */
>> +	mil_free_dma_buffer(this);
>> +
>> +	/* Set up the NFC geometry which is used by BCH. */
>> +	if (nfc->set_geometry(this))
>> +		return -ENXIO;
>>
> Promote the error code returned by set_geometry().
>
ok. thanks.
>> +	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);
>> +
>> +	/* 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 boot_rom_helper	*rom	= this->rom;
>> +	int			error	= 0;
>> +
>> +	if (mil_set_boot_rom_helper_geometry(this))
>> +		return -ENXIO;
>>
> Promote the error code returned by mil_set_boot_rom_helper_geometry().
ok.


Huang Shijie

WARNING: multiple messages have this Message-ID (diff)
From: b32955@freescale.com (Huang Shijie)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/7] add the common code for GPMI driver
Date: Wed, 23 Mar 2011 11:41:25 +0800	[thread overview]
Message-ID: <4D896BE5.7030208@freescale.com> (raw)
In-Reply-To: <19848.38860.624803.339369@ipc1.ka-ro>

Hi:

>> +
>> +static int acquire_register_block(struct gpmi_nfc_data *this,
>> +			const char *resource_name, void **reg_block_base)
>> +{
>> +	struct platform_device  *pdev = this->pdev;
>> +	struct resource         *r;
>> +	void                    *p;
>> +
>> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, resource_name);
>> +	if (!r) {
>> +		log("Can't get resource information for '%s'", resource_name);
>> +		return -ENXIO;
>> +	}
>> +
>> +	/* remap the register block */
>> +	p = ioremap(r->start, r->end - r->start + 1);
>                                ^^^^^^^^^^^^^^^^^^^^^
> resource_size(r)
>
thanks.

>
>> +static int acquire_interrupt(struct gpmi_nfc_data *this,
>> +			const char *resource_name,
>> +			irq_handler_t interrupt_handler, int *lno, int *hno)
>> +{
>> +	struct platform_device  *pdev = this->pdev;
>> +	struct resource         *r;
>> +	int                     err = 0;
>>
> Useless initialization.
>
ok.
>> +	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, resource_name);
>> +	if (!r) {
>> +		log("Can't get resource information for '%s'", resource_name);
>> +		return -ENXIO;
>> +	}
>> +
>> +	BUG_ON(r->start != r->end);
>> +	err = request_irq(r->start, interrupt_handler, 0, resource_name, this);
>> +	if (err) {
>> +		log("Can't own %s", resource_name);
>> +		return -EIO;
>>
> Promote the error code from request_irq().
>
thanks.
>> +	}
>> +
>> +	*lno = r->start;
>> +	*hno = r->end;
>> +	return 0;
>> +}
>> +
>> +static void release_interrupt(struct gpmi_nfc_data *this,
>> +			int low_interrupt_number, int high_interrupt_number)
>> +{
>> +	int i;
>> +	for (i = low_interrupt_number; i<= high_interrupt_number; i++)
>> +		free_irq(i, this);
>> +}
>> +
>>
>> +
>> +static void release_dma_channels(struct gpmi_nfc_data *this)
>> +{
>> +	unsigned int   i;
>> +	for (i = 0; i<= DMA_CHANS; i++)
>>
> Obiwan error! should be 'i<  DMA_CHANS'
>
  thanks a lot!
>> +		if (this->dma_chans[i]) {
>> +			dma_release_channel(this->dma_chans[i]);
>> +			this->dma_chans[i] = NULL;
>> +		}
>> +}
>> +
>>
>> +static inline int acquire_clock(struct gpmi_nfc_data *this,
>> +			const char *clock_name, struct clk **clock)
>> +{
>> +	struct clk *c;
>> +
>> +	c = clk_get(this->dev, clock_name);
>> +	if (IS_ERR(c)) {
>> +		log("Can't own clock %s", clock_name);
>> +		return PTR_ERR(c);
>> +	}
>> +	*clock = c;
>> +	return 0;
>> +}
>> +
>> +static void release_clock(struct gpmi_nfc_data *this, struct clk *clock)
>> +{
>> +	clk_disable(clock);
>>
> Unbalanced clk_disable().
thanks.
>> +	clk_put(clock);
>> +}
>> +
>> +static int acquire_resources(struct gpmi_nfc_data *this)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata     =  this->pdata;
>> +	struct resources               *resources =&this->resources;
>> +	int                            error      = 0;
>>
> Useless initialization.
ok.
>> +
>> +	/* Attempt to acquire our clock. */
>> +	error = acquire_clock(this, pdata->clock_name,&resources->clock);
>>
> Abuse of the clock API. Don't pass clock names via platform_data.
>
thanks, I will change it in next version.
>> +static int set_up_nfc_hal(struct gpmi_nfc_data *this)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata = this->pdata;
>> +	struct nfc_hal                 *nfc;
>> +	int                            error = 0;
>>
> Useless initialization.
>
>> +	/*
>> +	 * This structure contains the "safe" GPMI timing that should succeed
>> +	 * with any NAND Flash device
>> +	 * (although, with less-than-optimal performance).
>> +	 */
>> +	static 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,
>> +	};
>> +
>> +	switch (pdata->chip_version) {
>> +	case CHIP_VERSION_IMX23:
>> +		nfc =&gpmi_nfc_hal_imx23;
>> +		break;
>> +	case CHIP_VERSION_IMX28:
>> +		nfc =&gpmi_nfc_hal_imx28;
>> +		break;
>> +	default:
>> +		log("Unkown NFC version %u", pdata->chip_version);
>> +		return -ENXIO;
>> +	}
>>
> Use platform_ids to distinguish between HW variants.
>
ok.
>> +
>> +	this->nfc = nfc;
>> +
>> +	/* Initialize the NFC HAL. */
>> +	error = nfc->init(this);
>> +	if (error)
>> +		return error;
>> +
>> +	/* Set up safe timing. */
>> +	nfc->set_timing(this,&safe_timing);
>> +	return 0;
>> +}
>> +
>> +static int set_up_boot_rom_helper(struct gpmi_nfc_data *this)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata = this->pdata;
>> +	struct boot_rom_helper         *rom;
>> +
>> +	switch (pdata->chip_version) {
>> +	case CHIP_VERSION_IMX23:
>> +		rom =&gpmi_nfc_boot_rom_imx23;
>> +		break;
>> +	case CHIP_VERSION_IMX28:
>> +		rom =&gpmi_nfc_boot_rom_imx28;
>> +		break;
>> +	default:
>> +		log("Unkown NFC version %u", pdata->chip_version);
>> +		return -ENXIO;
>> +	}
>> +
>> +	pr_info("Boot ROM: Version %u, %s\n", rom->version, rom->description);
>> +	this->rom = rom;
>> +	return 0;
>> +}
>> +
>> +/* Creates/Removes sysfs files for this device.*/
>> +static void manage_sysfs_files(struct gpmi_nfc_data *this, int create)
>> +{
>> +	struct device            *dev = this->dev;
>> +	int                      error;
>> +	unsigned int             i;
>> +	struct device_attribute  **attr;
>> +
>> +	for (i = 0, attr = device_attributes;
>> +			i<  ARRAY_SIZE(device_attributes); i++, attr++) {
>> +
>> +		if (create) {
>> +			error = device_create_file(dev, *attr);
>> +			if (error) {
>> +				while (--attr>= device_attributes)
>> +					device_remove_file(dev, *attr);
>> +				return;
>> +			}
>> +		} else {
>> +			device_remove_file(dev, *attr);
>> +		}
>> +	}
>> +}
>> +
>> +static int gpmi_nfc_probe(struct platform_device *pdev)
>> +{
>> +	struct gpmi_nfc_platform_data  *pdata = pdev->dev.platform_data;
>> +	struct gpmi_nfc_data           *this  = 0;
>> +	int                            error  = 0;
>>
> Useless initialization (NB: pointers are initialized with 'NULL' not
> '0').
>
thanks.

I have already changed a lot, but i missed this one.
>> +/* This structure represents this driver to the platform management system. */
>> +static struct platform_driver gpmi_nfc_driver = {
>> +	.driver = {
>> +		.name = GPMI_NFC_DRIVER_NAME,
>> +	},
>> +	.probe   = gpmi_nfc_probe,
>> +	.remove  = __exit_p(gpmi_nfc_remove),
>> +	.suspend = gpmi_nfc_suspend,
>> +	.resume  = gpmi_nfc_resume,
>> +};
>> +
>> +static int __init gpmi_nfc_init(void)
>> +{
>> +	printk(KERN_INFO "GPMI NFC driver registered. (IMX)\n");
>> +	if (platform_driver_register(&gpmi_nfc_driver) != 0) {
>> +		pr_err("i.MX GPMI NFC driver registration failed\n");
>> +		return -ENODEV;
>>
> Promote the error code from platform_driver_register().
>
ok
>> +	}
>> +	return 0;
>> +}
>> +
>> +static void __exit gpmi_nfc_exit(void)
>> +{
>> +	platform_driver_unregister(&gpmi_nfc_driver);
>> +}
>> +
>> +module_init(gpmi_nfc_init);
>> +module_exit(gpmi_nfc_exit);
>> +
>> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
>> +MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
>> new file mode 100644
>> index 0000000..b5a2d77
>> --- /dev/null
>> +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
>> @@ -0,0 +1,520 @@
>> +/*
>> + * 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.
>> + */
>> +
>> +#ifndef __DRIVERS_MTD_NAND_GPMI_NFC_H
>> +#define __DRIVERS_MTD_NAND_GPMI_NFC_H
>> +
>> +/* Linux header files. */
>> +#include<linux/err.h>
>> +#include<linux/init.h>
>> +#include<linux/module.h>
>> +#include<linux/io.h>
>> +#include<linux/interrupt.h>
>> +#include<linux/clk.h>
>> +#include<linux/delay.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/dma-mapping.h>
>> +#include<linux/mtd/mtd.h>
>> +#include<linux/mtd/nand.h>
>> +#include<linux/mtd/partitions.h>
>> +#include<linux/mtd/concat.h>
>> +#include<linux/dmaengine.h>
>> +#include<asm/sizes.h>
>> +
>> +/* Platform header files. */
>> +#include<mach/mxs.h>
>> +#include<mach/common.h>
>> +#include<mach/dma.h>
>> +#include<mach/gpmi-nfc.h>
>> +#include<mach/system.h>
>> +#include<mach/clock.h>
>> +
>> +/* Driver header files. */
>> +#include "nand_device_info.h"
>> +
>> +/*
>> + *------------------------------------------------------------------------------
>> + * Fundamental Data Structures
>> + *------------------------------------------------------------------------------
>> + */
>> +
>> +/**
>> + * struct resources - The collection of resources the driver needs.
>> + *
>> + * @gpmi_regs:         A pointer to the GPMI registers.
>> + * @bch_regs:          A pointer to the BCH registers.
>> + * @bch_interrupt:     The BCH interrupt number.
>> + * @dma_low_channel:   The low  DMA channel.
>> + * @dma_high_channel:  The high DMA channel.
>> + * @clock:             A pointer to the struct clk for the NFC's clock.
>> + */
>> +struct resources {
>> +	void          *gpmi_regs;
>> +	void          *bch_regs;
>> +	unsigned int  bch_low_interrupt;
>> +	unsigned int  bch_high_interrupt;
>> +	unsigned int  dma_low_channel;
>> +	unsigned int  dma_high_channel;
>> +	struct clk    *clock;
>> +};
>> +
>> +/**
>> + * struct mil - State for the MTD Interface Layer.
>> + *
>> + * @nand:                    The NAND Flash MTD data structure that represents
>> + *                           the NAND Flash medium.
>> + * @mtd:                     The MTD data structure that represents the NAND
>> + *                           Flash medium.
>> + * @oob_layout:              A structure that describes how bytes are laid out
>> + *                           in the OOB.
>> + * @general_use_mtd:         A pointer to an MTD we export for general use.
>> + *                           This *may* simply be a pointer to the mtd field, if
>> + *                           we've been instructed NOT to protect the boot
>> + *                           areas.
>> + * @partitions:              A pointer to a set of partitions applied to the
>> + *                           general use MTD.
>> + * @partition_count:         The number of partitions.
>> + * @current_chip:            The chip currently selected by the NAND Fash MTD
>> + *                           code. A negative value indicates that no chip is
>> + *                           selected.
>> + * @command_length:          The length of the command that appears in the
>> + *                           command buffer (see cmd_virt, below).
>> + * @ignore_bad_block_marks:  Indicates we are ignoring bad block marks.
>> + * @saved_bbt:               A saved pointer to the in-memory NAND Flash MTD bad
>> + *                           block table. See show_device_ignorebad() for more
>> + *                           details.
>> + * @marking_a_bad_block:     Indicates the caller is marking a bad block. See
>> + *                           mil_ecc_write_oob() for details.
>> + * @hooked_block_markbad:    A pointer to the block_markbad() function we
>> + *                           we "hooked." See mil_ecc_write_oob() for details.
>> + * @upper_buf:               The buffer passed from upper layer.
>> + * @upper_len:               The buffer len passed from upper layer.
>> + * @direct_dma_map_ok:       Is the direct DMA map is good for the upper_buf?
>> + * @cmd_sgl/cmd_buffer:      For NAND command.
>> + * @data_sgl/data_buffer_dma:For NAND DATA ops.
>> + * @page_buffer_virt:        A pointer to a DMA-coherent buffer we use for
>> + *                           reading and writing pages. This buffer includes
>> + *                           space for both the payload data and the auxiliary
>> + *                           data (including status bytes, but not syndrome
>> + *                           bytes).
>> + * @page_buffer_phys:        The physical address for the page_buffer_virt
>> + *                           buffer.
>> + * @page_buffer_size:        The size of the page buffer.
>> + * @payload_virt:            A pointer to a location in the page buffer used
>> + *                           for payload bytes. The size of this buffer is
>> + *                           determined by struct nfc_geometry.
>> + * @payload_phys:            The physical address for payload_virt.
>> + * @auxiliary_virt:          A pointer to a location in the page buffer used
>> + *                           for auxiliary bytes. The size of this buffer is
>> + *                           determined by struct nfc_geometry.
>> + * @auxiliary_phys:          The physical address for auxiliary_virt.
>> + */
>> +struct mil {
>> +	/* MTD Data Structures */
>> +	struct nand_chip       nand;
>> +	struct mtd_info        mtd;
>> +	struct nand_ecclayout  oob_layout;
>> +
>> +	/* Partitioning and Boot Area Protection */
>> +	struct mtd_info        *general_use_mtd;
>> +	struct mtd_partition   *partitions;
>> +	unsigned int           partition_count;
>> +
>> +	/* General-use Variables */
>> +	int                    current_chip;
>> +	unsigned int           command_length;
>> +	int                    ignore_bad_block_marks;
>> +	void                   *saved_bbt;
>> +
>> +	/* MTD Function Pointer Hooks */
>> +	int                    marking_a_bad_block;
>> +	int                    (*hooked_block_markbad)(struct mtd_info *mtd,
>> +					loff_t ofs);
>> +
>> +	/* from upper layer */
>> +	uint8_t			*upper_buf;
>> +	int			upper_len;
>> +
>> +	/* DMA */
>> +	bool			direct_dma_map_ok;
>> +
>> +	struct scatterlist	cmd_sgl;
>> +	char			*cmd_buffer;
>> +
>> +	struct scatterlist	data_sgl;
>> +	char			*data_buffer_dma;
>> +
>> +	void                   *page_buffer_virt;
>> +	dma_addr_t             page_buffer_phys;
>> +	unsigned int           page_buffer_size;
>> +
>> +	void                   *payload_virt;
>> +	dma_addr_t             payload_phys;
>> +
>> +	void                   *auxiliary_virt;
>> +	dma_addr_t             auxiliary_phys;
>> +};
>>
> IMO this struct should be removed and the members integrated into the
> struct gpmi_nfc_data.
>
>> +
>> +/**
>> + * struct nfc_geometry - NFC geometry description.
>> + *
>> + * This structure describes the NFC's view of the medium geometry.
>> + *
>> + * @ecc_algorithm:            The human-readable name of the ECC algorithm
>> + *                            (e.g., "Reed-Solomon" or "BCH").
>> + * @ecc_strength:             A number that describes the strength of the ECC
>> + *                            algorithm.
>> + * @page_size_in_bytes:       The size, in bytes, of a physical page, including
>> + *                            both data and OOB.
>> + * @metadata_size_in_bytes:   The size, in bytes, of the metadata.
>> + * @ecc_chunk_size_in_bytes:  The size, in bytes, of a single ECC chunk. Note
>> + *                            the first chunk in the page includes both data and
>> + *                            metadata, so it's a bit larger than this value.
>> + * @ecc_chunk_count:          The number of ECC chunks in the page,
>> + * @payload_size_in_bytes:    The size, in bytes, of the payload buffer.
>> + * @auxiliary_size_in_bytes:  The size, in bytes, of the auxiliary buffer.
>> + * @auxiliary_status_offset:  The offset into the auxiliary buffer at which
>> + *                            the ECC status appears.
>> + * @block_mark_byte_offset:   The byte offset in the ECC-based page view at
>> + *                            which the underlying physical block mark appears.
>> + * @block_mark_bit_offset:    The bit offset into the ECC-based page view at
>> + *                            which the underlying physical block mark appears.
>> + */
>> +struct nfc_geometry {
>> +	char          *ecc_algorithm;
>> +	unsigned int  ecc_strength;
>> +	unsigned int  page_size_in_bytes;
>> +	unsigned int  metadata_size_in_bytes;
>> +	unsigned int  ecc_chunk_size_in_bytes;
>> +	unsigned int  ecc_chunk_count;
>> +	unsigned int  payload_size_in_bytes;
>> +	unsigned int  auxiliary_size_in_bytes;
>> +	unsigned int  auxiliary_status_offset;
>> +	unsigned int  block_mark_byte_offset;
>> +	unsigned int  block_mark_bit_offset;
>> +};
> Most of these parameters are already available in the mtd_info
> structure. No need to duplicate them here!
I will remove some ones.
>> +
>> +/**
>> + * struct boot_rom_geometry - Boot ROM geometry description.
>> + *
>> + * This structure encapsulates decisions made by the Boot ROM Helper.
>> + *
>> + * @boot_area_count:             The number of boot areas. The first boot area
>> + *                               appears at the beginning of chip 0, the next
>> + *                               at the beginning of chip 1, etc.
>> + * @boot_area_size_in_bytes:     The size, in bytes, of each boot area.
>> + * @stride_size_in_pages:        The size of a boot block stride, in pages.
>> + * @search_area_stride_exponent: The logarithm to base 2 of the size of a
>> + *                               search area in boot block strides.
>> + */
>> +struct boot_rom_geometry {
>> +	unsigned int  boot_area_count;
>> +	unsigned int  boot_area_size_in_bytes;
>> +	unsigned int  stride_size_in_pages;
>> +	unsigned int  search_area_stride_exponent;
>> +};
>>
> IMO the whole boot_rom stuff should not be part of an mtd chip driver,
> but has its place in the mtd partition handlers.
>
I have explained this in preview email. thanks
>> +/* Can we use the upper's buffer directly for DMA? */
>> +void prepare_data_dma(struct gpmi_nfc_data *this, enum dma_data_direction dr)
>> +{
>> +	struct mil *mil =&this->mil;
>> +	struct scatterlist *sgl =&mil->data_sgl;
>> +	int ret = 0;
>> +
>> +	mil->direct_dma_map_ok = true;
>> +
>> +	/* first try to map the upper buffer directly */
>> +	sg_init_one(sgl, mil->upper_buf, mil->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, mil->data_buffer_dma, PAGE_SIZE);
>> +		ret = dma_map_sg(this->dev, sgl, 1, dr);
>> +		BUG_ON(ret == 0);
>> +
>> +		if (dr == DMA_TO_DEVICE)
>> +			memcpy(mil->data_buffer_dma, mil->upper_buf,
>> +				mil->upper_len);
>> +		mil->direct_dma_map_ok = false;
>> +	}
>> +	sgl->length = mil->upper_len;
>>
> This has already been done by sg_init_one().
>
thanks.
>> +}
>> +
>> +/* 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:
>> +		if (mil->direct_dma_map_ok == false)
>> +			memcpy(mil->upper_buf, (char *)mil->data_buffer_dma,
>> +				mil->upper_len);
>> +		dma_unmap_sg(this->dev,&mil->data_sgl, 1, DMA_FROM_DEVICE);
>> +		break;
>> +
>> +	case DMA_FOR_WRITE_DATA:
>> +		dma_unmap_sg(this->dev,&mil->data_sgl, 1, DMA_TO_DEVICE);
>> +		break;
>> +
>> +	case DMA_FOR_READ_ECC_PAGE:
>> +	case DMA_FOR_WRITE_ECC_PAGE:
>> +		break;
>> +
>> +	default:
>> +		BUG();
>> +	}
>> +}
>> +
>>
>> +static uint8_t mil_read_byte(struct mtd_info *mtd)
>> +{
>> +	uint8_t  byte = 0;
>> +	mil_read_buf(mtd, (uint8_t *)&byte, 1);
>>
> With CONFIG_DMA_API_DEBUG enabled this would blow up right in your
> face:
> |gpmi-nfc gpmi-nfc: DMA-API: device driver maps memory fromstack [addr=c732bd3f]
>
Thanks a lot.
>> +	return byte;
>> +}
>> +
>> +/**
>> + * mil_handle_block_mark_swapping() - Handles block mark swapping.
>> + *
>> + * Note that, when this function is called, it doesn't know whether it's
>> + * swapping the block mark, or swapping it *back* -- but it doesn't matter
>> + * because the the operation is the same.
>> + *
>> + * @this:       Per-device data.
>> + * @payload:    A pointer to the payload buffer.
>> + * @auxiliary:  A pointer to the auxiliary buffer.
>> + */
>> +static void mil_handle_block_mark_swapping(struct gpmi_nfc_data *this,
>> +						void *payload, void *auxiliary)
>> +{
>> +	struct nfc_geometry     *nfc_geo =&this->nfc_geometry;
>> +	struct boot_rom_helper  *rom     =  this->rom;
>> +	unsigned char           *p;
>> +	unsigned char           *a;
>> +	unsigned int            bit;
>> +	unsigned char           mask;
>> +	unsigned char           from_data;
>> +	unsigned char           from_oob;
>> +
>> +	/* Check if we're doing block mark swapping. */
>> +	if (!rom->swap_block_mark)
>> +		return;
>> +
>> +	/*
>> +	 * If control arrives here, we're swapping. Make some convenience
>> +	 * variables.
>> +	 */
>> +	bit = nfc_geo->block_mark_bit_offset;
>> +	p   = ((unsigned char *) payload) + nfc_geo->block_mark_byte_offset;
>>
> Useless cast.
ok.
>> +	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 mil_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
>> +				uint8_t *buf, int page)
>> +{
>> +	struct gpmi_nfc_data    *this    = nand->priv;
>> +	struct nfc_hal          *nfc     =  this->nfc;
>> +	struct nfc_geometry     *nfc_geo =&this->nfc_geometry;
>> +	struct mil              *mil     =&this->mil;
>> +	void                    *payload_virt   =  0;
>> +	dma_addr_t              payload_phys    = ~0;
>> +	void                    *auxiliary_virt =  0;
>> +	dma_addr_t              auxiliary_phys  = ~0;
> Useless initialization.
>
ok.
>> +	unsigned int            i;
>> +	unsigned char           *status;
>> +	unsigned int            failed;
>> +	unsigned int            corrected;
>> +	int                     error = 0;
>> +
> Dto.
>
>> +	error = read_page_prepare(this, buf, mtd->writesize,
>> +					mil->payload_virt, mil->payload_phys,
>> +					nfc_geo->payload_size_in_bytes,
>> +					&payload_virt,&payload_phys);
>> +	if (error) {
>> +		log("Inadequate DMA buffer");
>> +		error = -ENOMEM;
>> +		return error;
>> +	}
>> +	auxiliary_virt = mil->auxiliary_virt;
>> +	auxiliary_phys = mil->auxiliary_phys;
>> +
>> +	/* ask the NFC */
>> +	error = nfc->read_page(this, payload_phys, auxiliary_phys);
>> +	if (error) {
>> +		log("Error in ECC-based read: %d", error);
>> +		goto exit_nfc;
>> +	}
>> +
>> +	/* handle the block mark swapping */
>> +	mil_handle_block_mark_swapping(this, payload_virt, auxiliary_virt);
>> +
>> +	/* Loop over status bytes, accumulating ECC status. */
>> +	failed		= 0;
>> +	corrected	= 0;
>> +	status		= ((unsigned char *) auxiliary_virt) +
>> +					nfc_geo->auxiliary_status_offset;
>>
> Useless cast.
>> +
>> +	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 mil_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(nand->oob_poi, ~0, mtd->oobsize);
>> +	nand->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
>> +
>> +exit_nfc:
>> +	read_page_end(this, buf, mtd->writesize,
>> +					mil->payload_virt, mil->payload_phys,
>> +					nfc_geo->payload_size_in_bytes,
>> +					payload_virt, payload_phys);
>> +	return error;
>> +}
>> +
>> +static void mil_ecc_write_page(struct mtd_info *mtd,
>> +				struct nand_chip *nand, const uint8_t *buf)
>> +{
>> +	struct gpmi_nfc_data    *this    = nand->priv;
>> +	struct nfc_hal          *nfc     =  this->nfc;
>> +	struct nfc_geometry     *nfc_geo =&this->nfc_geometry;
>> +	struct boot_rom_helper  *rom     =  this->rom;
>> +	struct mil              *mil     =&this->mil;
>> +	const void              *payload_virt   =  0;
>> +	dma_addr_t              payload_phys    = ~0;
>> +	const void              *auxiliary_virt =  0;
>> +	dma_addr_t              auxiliary_phys  = ~0;
>>
> Useless initialization.
>
>> +	int                     error;
>> +
>> +	if (rom->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(mil->payload_virt, buf, mtd->writesize);
>> +		payload_virt = mil->payload_virt;
>> +		payload_phys = mil->payload_phys;
>> +
>> +		memcpy(mil->auxiliary_virt, nand->oob_poi,
>> +				nfc_geo->auxiliary_size_in_bytes);
>> +		auxiliary_virt = mil->auxiliary_virt;
>> +		auxiliary_phys = mil->auxiliary_phys;
>> +
>> +		/* Handle block mark swapping. */
>> +		mil_handle_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.
>> +		 */
>> +		error = send_page_prepare(this,
>> +				buf, mtd->writesize,
>> +				mil->payload_virt, mil->payload_phys,
>> +				nfc_geo->payload_size_in_bytes,
>> +				&payload_virt,&payload_phys);
>> +		if (error) {
>> +			log("Inadequate payload DMA buffer");
>> +			return;
>> +		}
>> +
>> +		error = send_page_prepare(this,
>> +				nand->oob_poi, mtd->oobsize,
>> +				mil->auxiliary_virt, mil->auxiliary_phys,
>> +				nfc_geo->auxiliary_size_in_bytes,
>> +				&auxiliary_virt,&auxiliary_phys);
>> +		if (error) {
>> +			log("Inadequate auxiliary DMA buffer");
>> +			goto exit_auxiliary;
>> +		}
>> +	}
>> +
>> +	/* Ask the NFC. */
>> +	error = nfc->send_page(this, payload_phys, auxiliary_phys);
>> +	if (error)
>> +		log("Error in ECC-based write: %d", error);
>> +
>> +	if (!rom->swap_block_mark) {
>> +		send_page_end(this, nand->oob_poi, mtd->oobsize,
>> +				mil->auxiliary_virt, mil->auxiliary_phys,
>> +				nfc_geo->auxiliary_size_in_bytes,
>> +				auxiliary_virt, auxiliary_phys);
>> +exit_auxiliary:
>> +		send_page_end(this, buf, mtd->writesize,
>> +				mil->payload_virt, mil->payload_phys,
>> +				nfc_geo->payload_size_in_bytes,
>> +				payload_virt, payload_phys);
>> +	}
>> +}
>> +
>> +/**
>> + * mil_hook_block_markbad() - Hooked MTD Interface block_markbad().
>> + *
>> + * This function is a veneer that replaces the function originally installed by
>> + * the NAND Flash MTD code. See the description of the marking_a_bad_block field
>> + * in struct mil for more information about this.
>> + *
>> + * @mtd:  A pointer to the MTD.
>> + * @ofs:  Byte address of the block to mark.
>> + */
>> +static int mil_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)
>> +{
>> +	register struct nand_chip  *chip = mtd->priv;
>> +	struct gpmi_nfc_data       *this = chip->priv;
>> +	struct mil                 *mil  =&this->mil;
>> +	int                        ret;
>> +
>> +	mil->marking_a_bad_block = true;
>> +	ret = mil->hooked_block_markbad(mtd, ofs);
>> +	mil->marking_a_bad_block = false;
>> +	return ret;
>> +}
>> +
>> +/**
>> + * mil_ecc_read_oob() - MTD Interface ecc.read_oob().
>> + *
>> + * 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, so all
>> + *    write operations take measures to protect it.
>> + *
>> + * 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.
>> + *
>> + * @mtd:     A pointer to the owning MTD.
>> + * @nand:    A pointer to the owning NAND Flash MTD.
>> + * @page:    The page number to read.
>> + * @sndcmd:  Indicates this function should send a command to the chip before
>> + *           reading the out-of-band bytes. This is only false for small page
>> + *           chips that support auto-increment.
>> + */
>> +static int mil_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
>> +							int page, int sndcmd)
>> +{
>> +	struct gpmi_nfc_data      *this     = nand->priv;
>> +	struct boot_rom_helper    *rom      =  this->rom;
>> +
>> +	/* clear the OOB buffer */
>> +	memset(nand->oob_poi, ~0, mtd->oobsize);
>> +
>> +	/* Read out the conventional OOB. */
>> +	nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
>> +	nand->read_buf(mtd, nand->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 (!rom->swap_block_mark) {
>> +		/* Read the block mark into the first byte of the OOB buffer. */
>> +		nand->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
>> +		nand->oob_poi[0] = nand->read_byte(mtd);
>> +	}
>> +
>> +	/*
>> +	 * Return true, indicating that the next call to this function must send
>> +	 * a command.
>> +	 */
>> +	return true;
>> +}
>> +
>> +static int mil_ecc_write_oob(struct mtd_info *mtd,
>> +					struct nand_chip *nand, int page)
>> +{
>> +	struct gpmi_nfc_data      *this     = nand->priv;
>> +	struct device             *dev      =  this->dev;
>> +	struct mil                *mil      =&this->mil;
>> +	struct boot_rom_helper    *rom      =  this->rom;
>> +	uint8_t                   block_mark = 0;
>> +	int                       block_mark_column;
>> +	int                       status;
>> +	int                       error = 0;
>> +
>> +	/*
>> +	 * There are fundamental incompatibilities between the i.MX GPMI NFC and
>> +	 * the NAND Flash MTD model that make it essentially impossible to write
>> +	 * the out-of-band bytes.
>> +	 *
>> +	 * We permit *ONE* exception. If the *intent* of writing the OOB is to
>> +	 * mark a block bad, we can do that.
>> +	 */
>> +	if (!mil->marking_a_bad_block) {
>> +		dev_emerg(dev, "This driver doesn't support writing the OOB\n");
>> +		WARN_ON(1);
>> +		error = -EIO;
>> +		goto exit;
>> +	}
>> +
>> +	/*
>> +	 * If control arrives here, we're marking a block bad. First, figure out
>> +	 * where the block mark is.
>> +	 *
>> +	 * If we're using swapping, the block mark is in the conventional
>> +	 * location. Otherwise, we're using transcription, and the block mark
>> +	 * appears in the first byte of the page.
>> +	 */
>> +	if (rom->swap_block_mark)
>> +		block_mark_column = mtd->writesize;
>> +	else
>> +		block_mark_column = 0;
>> +
>> +	/* Write the block mark. */
>> +	nand->cmdfunc(mtd, NAND_CMD_SEQIN, block_mark_column, page);
>> +	nand->write_buf(mtd,&block_mark, 1);
>>
> DMA mapping memory from stack again.
thanks.
>> +	nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
>> +
>> +	status = nand->waitfunc(mtd, nand);
>> +
>> +	/* Check if it worked. */
>> +	if (status&  NAND_STATUS_FAIL)
>> +		error = -EIO;
>> +exit:
>> +	return error;
>> +}
>> +
>> +/**
>> + * mil_block_bad - Claims all blocks are good.
>> + *
>> + * In principle, this function is *only* called when the NAND Flash MTD system
>> + * isn't allowed to keep an in-memory bad block table, so it is forced to ask
>> + * the driver for bad block information.
>> + *
>> + * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
>> + * this function is *only* called when we take it away.
>> + *
>> + * We take away the in-memory BBT when the user sets the "ignorebad" parameter,
>> + * which indicates that all blocks should be reported good.
>> + *
>> + * Thus, this function is only called when we want *all* blocks to look good,
>> + * so it *always* return success.
>> + *
>> + * @mtd:      Ignored.
>> + * @ofs:      Ignored.
>> + * @getchip:  Ignored.
>> + */
>> +static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
>> +{
>> +	return 0;
>> +}
>> +
>> +/* Set up the Boot ROM Helper geometry. */
>> +static int mil_set_boot_rom_helper_geometry(struct gpmi_nfc_data  *this)
>> +{
>> +	struct boot_rom_helper    *rom =  this->rom;
>> +	struct boot_rom_geometry  *geo =&this->rom_geometry;
>> +
>> +	if (rom->set_geometry(this))
>> +		return !0;
>> +
>> +	pr_info("--------------------------------------------\n");
>> +	pr_info("	Boot ROM Geometry\n");
>> +	pr_info("--------------------------------------------\n");
>> +	pr_info("Boot Area Count            : %u\n", geo->boot_area_count);
>> +	pr_info("Boot Area Size in Bytes    : %u (0x%x)\n",
>> +					geo->boot_area_size_in_bytes,
>> +					geo->boot_area_size_in_bytes);
>> +	pr_info("Stride Size in Pages       : %u\n", geo->stride_size_in_pages);
>> +	pr_info("Search Area Stride Exponent: %u\n",
>> +					geo->search_area_stride_exponent);
>> +	return 0;
>> +}
>> +
>> +static int mil_set_geometry(struct gpmi_nfc_data *this)
>> +{
>> +	struct nfc_hal *nfc = this->nfc;
>> +	struct nfc_geometry *geo =&this->nfc_geometry;
>> +
>> +	/* Free the temporary DMA memory for read ID case */
>> +	mil_free_dma_buffer(this);
>> +
>> +	/* Set up the NFC geometry which is used by BCH. */
>> +	if (nfc->set_geometry(this))
>> +		return -ENXIO;
>>
> Promote the error code returned by set_geometry().
>
ok. thanks.
>> +	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);
>> +
>> +	/* 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 boot_rom_helper	*rom	= this->rom;
>> +	int			error	= 0;
>> +
>> +	if (mil_set_boot_rom_helper_geometry(this))
>> +		return -ENXIO;
>>
> Promote the error code returned by mil_set_boot_rom_helper_geometry().
ok.


Huang Shijie

  reply	other threads:[~2011-03-23  3:41 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-03-16  1:42 [PATCH 0/7] add the GPMI controller driver for IMX23/IMX28 Huang Shijie
2011-03-16  1:42 ` [PATCH 1/7] ARM: add GPMI support for imx23/imx28 Huang Shijie
2011-03-16 10:13   ` Lothar Waßmann
     [not found]     ` <4D809453.4090603@freescale.com>
     [not found]       ` <19840.43943.592336.854865@ipc1.ka-ro>
2011-03-17  2:19         ` Huang Shijie
2011-03-17  2:19           ` Huang Shijie
2011-03-17 10:36           ` Lothar Waßmann
2011-03-17 10:36             ` Lothar Waßmann
2011-03-18  2:06             ` Huang Shijie
2011-03-18  2:06               ` Huang Shijie
2011-03-16  1:42 ` [PATCH 2/7] add the common code for GPMI driver Huang Shijie
2011-03-22 12:36   ` =?utf-8?Q?Lothar_Wa=C3=9Fmann?=
2011-03-23  3:41     ` Huang Shijie [this message]
2011-03-23  3:41       ` Huang Shijie
2011-03-16  1:42 ` [PATCH 3/7] add the database for the NANDs Huang Shijie
2011-03-16  1:42 ` [PATCH 4/7] add GPMI support for imx23 Huang Shijie
2011-03-16  1:42 ` [PATCH 5/7] add GPMI support for imx28 Huang Shijie
2011-03-22 12:46   ` Lothar Waßmann
2011-03-23  3:11     ` Huang Shijie
2011-03-23  3:11       ` Huang Shijie
2011-03-23 14:56       ` Lothar Waßmann
2011-03-23 14:56         ` Lothar Waßmann
2011-03-23 15:16         ` Florian Fainelli
2011-03-23 15:16           ` Florian Fainelli
2011-03-24  3:07           ` Huang Shijie
2011-03-24  3:07             ` Huang Shijie
2011-03-24  3:03         ` Huang Shijie
2011-03-24  3:03           ` Huang Shijie
2011-03-24  7:34           ` Lothar Waßmann
2011-03-24  7:34             ` Lothar Waßmann
2011-03-24  8:26             ` Jason Liu
2011-03-24  8:26               ` Jason Liu
2011-03-24  8:32               ` Wolfram Sang
2011-03-24  8:32                 ` Wolfram Sang
2011-03-24  8:33               ` Lothar Waßmann
2011-03-24  8:33                 ` Lothar Waßmann
2011-03-24  8:51             ` Huang Shijie
2011-03-24  8:51               ` Huang Shijie
2011-03-24 13:54               ` Florian Fainelli
2011-03-24 13:54                 ` Florian Fainelli
2011-03-25  2:50                 ` Huang Shijie
2011-03-25  2:50                   ` Huang Shijie
2011-03-16  1:42 ` [PATCH 6/7] dmaengine: change the flags of request_irq() Huang Shijie
2011-03-16 10:13   ` Lothar Waßmann
2011-03-16 12:31     ` 答复: " Huang Shijie-B32955
2011-03-18  6:12     ` Huang Shijie
2011-03-16  1:42 ` [PATCH 7/7] MTD : add GPMI driver in the config and Makefile Huang Shijie
  -- strict thread matches above, loose matches on Subject: below --
2011-03-16  1:55 [PATCH 0/7] add the GPMI controller driver for IMX23/IMX28 Huang Shijie
2011-03-16  1:55 ` [PATCH 2/7] add the common code for GPMI driver Huang Shijie
2011-03-16  1:55   ` Huang Shijie

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4D896BE5.7030208@freescale.com \
    --to=b32955@freescale.com \
    --cc=LW@KARO-electronics.de \
    --cc=dwmw2@infradead.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=linux@arm.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.