diff options
Diffstat (limited to 'drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c')
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 188 |
1 files changed, 59 insertions, 129 deletions
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index c2597c8107a0..f6aa358a3452 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -198,17 +198,16 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) * * We may have available oob space in this case. */ -static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) +static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, + unsigned int ecc_strength, + unsigned int ecc_step) { struct bch_geometry *geo = &this->bch_geometry; struct nand_chip *chip = &this->nand; struct mtd_info *mtd = nand_to_mtd(chip); unsigned int block_mark_bit_offset; - if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) - return -EINVAL; - - switch (chip->ecc_step_ds) { + switch (ecc_step) { case SZ_512: geo->gf_len = 13; break; @@ -221,8 +220,8 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) chip->ecc_strength_ds, chip->ecc_step_ds); return -EINVAL; } - geo->ecc_chunk_size = chip->ecc_step_ds; - geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + geo->ecc_chunk_size = ecc_step; + geo->ecc_strength = round_up(ecc_strength, 2); if (!gpmi_check_ecc(this)) return -EINVAL; @@ -230,7 +229,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) if (geo->ecc_chunk_size < mtd->oobsize) { dev_err(this->dev, "unsupported nand chip. ecc size: %d, oob size : %d\n", - chip->ecc_step_ds, mtd->oobsize); + ecc_step, mtd->oobsize); return -EINVAL; } @@ -423,9 +422,20 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) int common_nfc_set_geometry(struct gpmi_nand_data *this) { + struct nand_chip *chip = &this->nand; + + if (chip->ecc.strength > 0 && chip->ecc.size > 0) + return set_geometry_by_ecc_info(this, chip->ecc.strength, + chip->ecc.size); + if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) - || legacy_set_geometry(this)) - return set_geometry_by_ecc_info(this); + || legacy_set_geometry(this)) { + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -EINVAL; + + return set_geometry_by_ecc_info(this, chip->ecc_strength_ds, + chip->ecc_step_ds); + } return 0; } @@ -437,33 +447,32 @@ struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) } /* Can we use the upper's buffer directly for DMA? */ -void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) +bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, + enum dma_data_direction dr) { struct scatterlist *sgl = &this->data_sgl; int ret; /* first try to map the upper buffer directly */ - if (virt_addr_valid(this->upper_buf) && - !object_is_on_stack(this->upper_buf)) { - sg_init_one(sgl, this->upper_buf, this->upper_len); + if (virt_addr_valid(buf) && !object_is_on_stack(buf)) { + sg_init_one(sgl, buf, len); ret = dma_map_sg(this->dev, sgl, 1, dr); if (ret == 0) goto map_fail; - this->direct_dma_map_ok = true; - return; + return true; } map_fail: /* We have to use our own DMA buffer. */ - sg_init_one(sgl, this->data_buffer_dma, this->upper_len); + sg_init_one(sgl, this->data_buffer_dma, len); if (dr == DMA_TO_DEVICE) - memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len); + memcpy(this->data_buffer_dma, buf, len); dma_map_sg(this->dev, sgl, 1, dr); - this->direct_dma_map_ok = false; + return false; } /* This will be called after the DMA operation is finished. */ @@ -472,31 +481,6 @@ static void dma_irq_callback(void *param) struct gpmi_nand_data *this = param; struct completion *dma_c = &this->dma_done; - switch (this->dma_type) { - case DMA_FOR_COMMAND: - dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); - if (this->direct_dma_map_ok == false) - memcpy(this->upper_buf, this->data_buffer_dma, - this->upper_len); - break; - - case DMA_FOR_WRITE_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_ECC_PAGE: - case DMA_FOR_WRITE_ECC_PAGE: - /* We have to wait the BCH interrupt to finish. */ - break; - - default: - dev_err(this->dev, "in wrong DMA operation.\n"); - } - complete(dma_c); } @@ -516,8 +500,7 @@ int start_dma_without_bch_irq(struct gpmi_nand_data *this, /* Wait for the interrupt from the DMA block. */ timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); if (!timeout) { - dev_err(this->dev, "DMA timeout, last DMA :%d\n", - this->last_dma_type); + dev_err(this->dev, "DMA timeout, last DMA\n"); gpmi_dump_info(this); return -ETIMEDOUT; } @@ -546,8 +529,7 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *this, /* Wait for the interrupt from the BCH block. */ timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); if (!timeout) { - dev_err(this->dev, "BCH timeout, last DMA :%d\n", - this->last_dma_type); + dev_err(this->dev, "BCH timeout\n"); gpmi_dump_info(this); return -ETIMEDOUT; } @@ -695,56 +677,6 @@ static void release_resources(struct gpmi_nand_data *this) release_dma_channels(this); } -static int read_page_prepare(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void **use_virt, dma_addr_t *use_phys) -{ - struct device *dev = this->dev; - - if (virt_addr_valid(destination)) { - dma_addr_t dest_phys; - - dest_phys = dma_map_single(dev, destination, - length, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dest_phys)) { - if (alt_size < length) { - dev_err(dev, "Alternate buffer is too small\n"); - return -ENOMEM; - } - goto map_failed; - } - *use_virt = destination; - *use_phys = dest_phys; - this->direct_dma_map_ok = true; - return 0; - } - -map_failed: - *use_virt = alt_virt; - *use_phys = alt_phys; - this->direct_dma_map_ok = false; - return 0; -} - -static inline void read_page_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (this->direct_dma_map_ok) - dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE); -} - -static inline void read_page_swap_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (!this->direct_dma_map_ok) - memcpy(destination, alt_virt, length); -} - static int send_page_prepare(struct gpmi_nand_data *this, const void *source, unsigned length, void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, @@ -946,10 +878,8 @@ static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) struct gpmi_nand_data *this = nand_get_controller_data(chip); dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = buf; - this->upper_len = len; - gpmi_read_data(this); + gpmi_read_data(this, buf, len); } static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) @@ -958,10 +888,8 @@ static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) struct gpmi_nand_data *this = nand_get_controller_data(chip); dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = (uint8_t *)buf; - this->upper_len = len; - gpmi_send_data(this); + gpmi_send_data(this, buf, len); } static uint8_t gpmi_read_byte(struct mtd_info *mtd) @@ -1031,44 +959,46 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, struct mtd_info *mtd = nand_to_mtd(chip); void *payload_virt; dma_addr_t payload_phys; - void *auxiliary_virt; - dma_addr_t auxiliary_phys; unsigned int i; unsigned char *status; unsigned int max_bitflips = 0; int ret; + bool direct = false; dev_dbg(this->dev, "page number is : %d\n", page); - ret = read_page_prepare(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - &payload_virt, &payload_phys); - if (ret) { - dev_err(this->dev, "Inadequate DMA buffer\n"); - ret = -ENOMEM; - return ret; + + payload_virt = this->payload_virt; + payload_phys = this->payload_phys; + + if (virt_addr_valid(buf)) { + dma_addr_t dest_phys; + + dest_phys = dma_map_single(this->dev, buf, nfc_geo->payload_size, + DMA_FROM_DEVICE); + if (!dma_mapping_error(this->dev, dest_phys)) { + payload_virt = buf; + payload_phys = dest_phys; + direct = true; + } } - auxiliary_virt = this->auxiliary_virt; - auxiliary_phys = this->auxiliary_phys; /* go! */ - ret = gpmi_read_page(this, payload_phys, auxiliary_phys); - read_page_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + ret = gpmi_read_page(this, payload_phys, this->auxiliary_phys); + + if (direct) + dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, + DMA_FROM_DEVICE); + if (ret) { dev_err(this->dev, "Error in ECC-based read: %d\n", ret); return ret; } /* Loop over status bytes, accumulating ECC status. */ - status = auxiliary_virt + nfc_geo->auxiliary_status_offset; + status = this->auxiliary_virt + nfc_geo->auxiliary_status_offset; - read_page_swap_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + if (!direct) + memcpy(buf, this->payload_virt, nfc_geo->payload_size); for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) @@ -1123,7 +1053,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, buf + i * nfc_geo->ecc_chunk_size, nfc_geo->ecc_chunk_size, eccbuf, eccbytes, - auxiliary_virt, + this->auxiliary_virt, nfc_geo->metadata_size, nfc_geo->ecc_strength); } else { @@ -1151,7 +1081,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, } /* handle the block mark swapping */ - block_mark_swapping(this, buf, auxiliary_virt); + block_mark_swapping(this, buf, this->auxiliary_virt); if (oob_required) { /* @@ -1165,7 +1095,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, * the block mark. */ memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + chip->oob_poi[0] = ((uint8_t *)this->auxiliary_virt)[0]; } return max_bitflips; |