diff options
| -rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 183 | ||||
| -rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 2 |
2 files changed, 185 insertions, 0 deletions
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 959cb9b70310..987de1e3821c 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c | |||
| @@ -791,6 +791,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) | |||
| 791 | this->page_buffer_phys); | 791 | this->page_buffer_phys); |
| 792 | kfree(this->cmd_buffer); | 792 | kfree(this->cmd_buffer); |
| 793 | kfree(this->data_buffer_dma); | 793 | kfree(this->data_buffer_dma); |
| 794 | kfree(this->raw_buffer); | ||
| 794 | 795 | ||
| 795 | this->cmd_buffer = NULL; | 796 | this->cmd_buffer = NULL; |
| 796 | this->data_buffer_dma = NULL; | 797 | this->data_buffer_dma = NULL; |
| @@ -837,6 +838,9 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) | |||
| 837 | if (!this->page_buffer_virt) | 838 | if (!this->page_buffer_virt) |
| 838 | goto error_alloc; | 839 | goto error_alloc; |
| 839 | 840 | ||
| 841 | this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); | ||
| 842 | if (!this->raw_buffer) | ||
| 843 | goto error_alloc; | ||
| 840 | 844 | ||
| 841 | /* Slice up the page buffer. */ | 845 | /* Slice up the page buffer. */ |
| 842 | this->payload_virt = this->page_buffer_virt; | 846 | this->payload_virt = this->page_buffer_virt; |
| @@ -1347,6 +1351,183 @@ gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) | |||
| 1347 | return status & NAND_STATUS_FAIL ? -EIO : 0; | 1351 | return status & NAND_STATUS_FAIL ? -EIO : 0; |
| 1348 | } | 1352 | } |
| 1349 | 1353 | ||
| 1354 | /* | ||
| 1355 | * This function reads a NAND page without involving the ECC engine (no HW | ||
| 1356 | * ECC correction). | ||
| 1357 | * The tricky part in the GPMI/BCH controller is that it stores ECC bits | ||
| 1358 | * inline (interleaved with payload DATA), and do not align data chunk on | ||
| 1359 | * byte boundaries. | ||
| 1360 | * We thus need to take care moving the payload data and ECC bits stored in the | ||
| 1361 | * page into the provided buffers, which is why we're using gpmi_copy_bits. | ||
| 1362 | * | ||
| 1363 | * See set_geometry_by_ecc_info inline comments to have a full description | ||
| 1364 | * of the layout used by the GPMI controller. | ||
| 1365 | */ | ||
| 1366 | static int gpmi_ecc_read_page_raw(struct mtd_info *mtd, | ||
| 1367 | struct nand_chip *chip, uint8_t *buf, | ||
| 1368 | int oob_required, int page) | ||
| 1369 | { | ||
| 1370 | struct gpmi_nand_data *this = chip->priv; | ||
| 1371 | struct bch_geometry *nfc_geo = &this->bch_geometry; | ||
| 1372 | int eccsize = nfc_geo->ecc_chunk_size; | ||
| 1373 | int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; | ||
| 1374 | u8 *tmp_buf = this->raw_buffer; | ||
| 1375 | size_t src_bit_off; | ||
| 1376 | size_t oob_bit_off; | ||
| 1377 | size_t oob_byte_off; | ||
| 1378 | uint8_t *oob = chip->oob_poi; | ||
| 1379 | int step; | ||
| 1380 | |||
| 1381 | chip->read_buf(mtd, tmp_buf, | ||
| 1382 | mtd->writesize + mtd->oobsize); | ||
| 1383 | |||
| 1384 | /* | ||
| 1385 | * If required, swap the bad block marker and the data stored in the | ||
| 1386 | * metadata section, so that we don't wrongly consider a block as bad. | ||
| 1387 | * | ||
| 1388 | * See the layout description for a detailed explanation on why this | ||
| 1389 | * is needed. | ||
| 1390 | */ | ||
| 1391 | if (this->swap_block_mark) { | ||
| 1392 | u8 swap = tmp_buf[0]; | ||
| 1393 | |||
| 1394 | tmp_buf[0] = tmp_buf[mtd->writesize]; | ||
| 1395 | tmp_buf[mtd->writesize] = swap; | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | /* | ||
| 1399 | * Copy the metadata section into the oob buffer (this section is | ||
| 1400 | * guaranteed to be aligned on a byte boundary). | ||
| 1401 | */ | ||
| 1402 | if (oob_required) | ||
| 1403 | memcpy(oob, tmp_buf, nfc_geo->metadata_size); | ||
| 1404 | |||
| 1405 | oob_bit_off = nfc_geo->metadata_size * 8; | ||
| 1406 | src_bit_off = oob_bit_off; | ||
| 1407 | |||
| 1408 | /* Extract interleaved payload data and ECC bits */ | ||
| 1409 | for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { | ||
| 1410 | if (buf) | ||
| 1411 | gpmi_copy_bits(buf, step * eccsize * 8, | ||
| 1412 | tmp_buf, src_bit_off, | ||
| 1413 | eccsize * 8); | ||
| 1414 | src_bit_off += eccsize * 8; | ||
| 1415 | |||
| 1416 | /* Align last ECC block to align a byte boundary */ | ||
| 1417 | if (step == nfc_geo->ecc_chunk_count - 1 && | ||
| 1418 | (oob_bit_off + eccbits) % 8) | ||
| 1419 | eccbits += 8 - ((oob_bit_off + eccbits) % 8); | ||
| 1420 | |||
| 1421 | if (oob_required) | ||
| 1422 | gpmi_copy_bits(oob, oob_bit_off, | ||
| 1423 | tmp_buf, src_bit_off, | ||
| 1424 | eccbits); | ||
| 1425 | |||
| 1426 | src_bit_off += eccbits; | ||
| 1427 | oob_bit_off += eccbits; | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | if (oob_required) { | ||
| 1431 | oob_byte_off = oob_bit_off / 8; | ||
| 1432 | |||
| 1433 | if (oob_byte_off < mtd->oobsize) | ||
| 1434 | memcpy(oob + oob_byte_off, | ||
| 1435 | tmp_buf + mtd->writesize + oob_byte_off, | ||
| 1436 | mtd->oobsize - oob_byte_off); | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | return 0; | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | /* | ||
| 1443 | * This function writes a NAND page without involving the ECC engine (no HW | ||
| 1444 | * ECC generation). | ||
| 1445 | * The tricky part in the GPMI/BCH controller is that it stores ECC bits | ||
| 1446 | * inline (interleaved with payload DATA), and do not align data chunk on | ||
| 1447 | * byte boundaries. | ||
| 1448 | * We thus need to take care moving the OOB area at the right place in the | ||
| 1449 | * final page, which is why we're using gpmi_copy_bits. | ||
| 1450 | * | ||
| 1451 | * See set_geometry_by_ecc_info inline comments to have a full description | ||
| 1452 | * of the layout used by the GPMI controller. | ||
| 1453 | */ | ||
| 1454 | static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, | ||
| 1455 | struct nand_chip *chip, | ||
| 1456 | const uint8_t *buf, | ||
| 1457 | int oob_required) | ||
| 1458 | { | ||
| 1459 | struct gpmi_nand_data *this = chip->priv; | ||
| 1460 | struct bch_geometry *nfc_geo = &this->bch_geometry; | ||
| 1461 | int eccsize = nfc_geo->ecc_chunk_size; | ||
| 1462 | int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; | ||
| 1463 | u8 *tmp_buf = this->raw_buffer; | ||
| 1464 | uint8_t *oob = chip->oob_poi; | ||
| 1465 | size_t dst_bit_off; | ||
| 1466 | size_t oob_bit_off; | ||
| 1467 | size_t oob_byte_off; | ||
| 1468 | int step; | ||
| 1469 | |||
| 1470 | /* | ||
| 1471 | * Initialize all bits to 1 in case we don't have a buffer for the | ||
| 1472 | * payload or oob data in order to leave unspecified bits of data | ||
| 1473 | * to their initial state. | ||
| 1474 | */ | ||
| 1475 | if (!buf || !oob_required) | ||
| 1476 | memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize); | ||
| 1477 | |||
| 1478 | /* | ||
| 1479 | * First copy the metadata section (stored in oob buffer) at the | ||
| 1480 | * beginning of the page, as imposed by the GPMI layout. | ||
| 1481 | */ | ||
| 1482 | memcpy(tmp_buf, oob, nfc_geo->metadata_size); | ||
| 1483 | oob_bit_off = nfc_geo->metadata_size * 8; | ||
| 1484 | dst_bit_off = oob_bit_off; | ||
| 1485 | |||
| 1486 | /* Interleave payload data and ECC bits */ | ||
| 1487 | for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { | ||
| 1488 | if (buf) | ||
| 1489 | gpmi_copy_bits(tmp_buf, dst_bit_off, | ||
| 1490 | buf, step * eccsize * 8, eccsize * 8); | ||
| 1491 | dst_bit_off += eccsize * 8; | ||
| 1492 | |||
| 1493 | /* Align last ECC block to align a byte boundary */ | ||
| 1494 | if (step == nfc_geo->ecc_chunk_count - 1 && | ||
| 1495 | (oob_bit_off + eccbits) % 8) | ||
| 1496 | eccbits += 8 - ((oob_bit_off + eccbits) % 8); | ||
| 1497 | |||
| 1498 | if (oob_required) | ||
| 1499 | gpmi_copy_bits(tmp_buf, dst_bit_off, | ||
| 1500 | oob, oob_bit_off, eccbits); | ||
| 1501 | |||
| 1502 | dst_bit_off += eccbits; | ||
| 1503 | oob_bit_off += eccbits; | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | oob_byte_off = oob_bit_off / 8; | ||
| 1507 | |||
| 1508 | if (oob_required && oob_byte_off < mtd->oobsize) | ||
| 1509 | memcpy(tmp_buf + mtd->writesize + oob_byte_off, | ||
| 1510 | oob + oob_byte_off, mtd->oobsize - oob_byte_off); | ||
| 1511 | |||
| 1512 | /* | ||
| 1513 | * If required, swap the bad block marker and the first byte of the | ||
| 1514 | * metadata section, so that we don't modify the bad block marker. | ||
| 1515 | * | ||
| 1516 | * See the layout description for a detailed explanation on why this | ||
| 1517 | * is needed. | ||
| 1518 | */ | ||
| 1519 | if (this->swap_block_mark) { | ||
| 1520 | u8 swap = tmp_buf[0]; | ||
| 1521 | |||
| 1522 | tmp_buf[0] = tmp_buf[mtd->writesize]; | ||
| 1523 | tmp_buf[mtd->writesize] = swap; | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | chip->write_buf(mtd, tmp_buf, mtd->writesize + mtd->oobsize); | ||
| 1527 | |||
| 1528 | return 0; | ||
| 1529 | } | ||
| 1530 | |||
| 1350 | static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) | 1531 | static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) |
| 1351 | { | 1532 | { |
| 1352 | struct nand_chip *chip = mtd->priv; | 1533 | struct nand_chip *chip = mtd->priv; |
| @@ -1664,6 +1845,8 @@ static int gpmi_init_last(struct gpmi_nand_data *this) | |||
| 1664 | ecc->write_page = gpmi_ecc_write_page; | 1845 | ecc->write_page = gpmi_ecc_write_page; |
| 1665 | ecc->read_oob = gpmi_ecc_read_oob; | 1846 | ecc->read_oob = gpmi_ecc_read_oob; |
| 1666 | ecc->write_oob = gpmi_ecc_write_oob; | 1847 | ecc->write_oob = gpmi_ecc_write_oob; |
| 1848 | ecc->read_page_raw = gpmi_ecc_read_page_raw; | ||
| 1849 | ecc->write_page_raw = gpmi_ecc_write_page_raw; | ||
| 1667 | ecc->mode = NAND_ECC_HW; | 1850 | ecc->mode = NAND_ECC_HW; |
| 1668 | ecc->size = bch_geo->ecc_chunk_size; | 1851 | ecc->size = bch_geo->ecc_chunk_size; |
| 1669 | ecc->strength = bch_geo->ecc_strength; | 1852 | ecc->strength = bch_geo->ecc_strength; |
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index 20da1f139853..544062f65020 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h | |||
| @@ -189,6 +189,8 @@ struct gpmi_nand_data { | |||
| 189 | void *auxiliary_virt; | 189 | void *auxiliary_virt; |
| 190 | dma_addr_t auxiliary_phys; | 190 | dma_addr_t auxiliary_phys; |
| 191 | 191 | ||
| 192 | void *raw_buffer; | ||
| 193 | |||
| 192 | /* DMA channels */ | 194 | /* DMA channels */ |
| 193 | #define DMA_CHANS 8 | 195 | #define DMA_CHANS 8 |
| 194 | struct dma_chan *dma_chans[DMA_CHANS]; | 196 | struct dma_chan *dma_chans[DMA_CHANS]; |
