|
@@ -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;
|