[PATCH 2/7] add the common code for GPMI driver
Huang Shijie
b32955 at freescale.com
Tue Mar 22 23:41:25 EDT 2011
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
More information about the linux-arm-kernel
mailing list