Skip to content

Commit 7260050

Browse files
saschahauermiquelraynal
authored andcommitted
mtd: rawnand: mxc: support software ECC
With these changes the driver can be used with software BCH ECC which is useful for NAND chips that require a stronger ECC than the i.MX hardware supports. The controller normally interleaves user data with OOB data when accessing the NAND chip. With Software BCH ECC we write the data to the NAND in a way that the raw data on the NAND chip matches the way the NAND layer sees it. This way commands like NAND_CMD_RNDOUT work as expected. This was tested on i.MX27 but should work on the other SoCs supported by this driver as well. Signed-off-by: Sascha Hauer <[email protected]> Signed-off-by: Miquel Raynal <[email protected]> Link: https://lore.kernel.org/linux-mtd/20240522-mtd-nand-mxc-nand-exec-op-v4-3-75b611e0ac44@pengutronix.de
1 parent d3dfbae commit 7260050

File tree

1 file changed

+97
-5
lines changed

1 file changed

+97
-5
lines changed

drivers/mtd/nand/raw/mxc_nand.c

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,10 +1402,10 @@ static int mxcnd_attach_chip(struct nand_chip *chip)
14021402
chip->ecc.bytes = host->devtype_data->eccbytes;
14031403
host->eccsize = host->devtype_data->eccsize;
14041404
chip->ecc.size = 512;
1405-
mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
14061405

14071406
switch (chip->ecc.engine_type) {
14081407
case NAND_ECC_ENGINE_TYPE_ON_HOST:
1408+
mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
14091409
chip->ecc.read_page = mxc_nand_read_page;
14101410
chip->ecc.read_page_raw = mxc_nand_read_page_raw;
14111411
chip->ecc.read_oob = mxc_nand_read_oob;
@@ -1415,6 +1415,8 @@ static int mxcnd_attach_chip(struct nand_chip *chip)
14151415
break;
14161416

14171417
case NAND_ECC_ENGINE_TYPE_SOFT:
1418+
chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
1419+
chip->ecc.read_page_raw = nand_monolithic_read_page_raw;
14181420
break;
14191421

14201422
default:
@@ -1470,6 +1472,88 @@ static int mxcnd_setup_interface(struct nand_chip *chip, int chipnr,
14701472
return host->devtype_data->setup_interface(chip, chipnr, conf);
14711473
}
14721474

1475+
static void memff16_toio(void *buf, int n)
1476+
{
1477+
__iomem u16 *t = buf;
1478+
int i;
1479+
1480+
for (i = 0; i < (n >> 1); i++)
1481+
__raw_writew(0xffff, t++);
1482+
}
1483+
1484+
static void copy_page_to_sram(struct mtd_info *mtd, const void *buf, int buf_len)
1485+
{
1486+
struct nand_chip *this = mtd_to_nand(mtd);
1487+
struct mxc_nand_host *host = nand_get_controller_data(this);
1488+
unsigned int no_subpages = mtd->writesize / 512;
1489+
int oob_per_subpage, i;
1490+
1491+
oob_per_subpage = (mtd->oobsize / no_subpages) & ~1;
1492+
1493+
/*
1494+
* During a page write the i.MX NAND controller will read 512b from
1495+
* main_area0 SRAM, then oob_per_subpage bytes from spare0 SRAM, then
1496+
* 512b from main_area1 SRAM and so on until the full page is written.
1497+
* For software ECC we want to have a 1:1 mapping between the raw page
1498+
* data on the NAND chip and the view of the NAND core. This is
1499+
* necessary to make the NAND_CMD_RNDOUT read the data it expects.
1500+
* To accomplish this we have to write the data in the order the controller
1501+
* reads it. This is reversed in copy_page_from_sram() below.
1502+
*
1503+
* buf_len can either be the full page including the OOB or user data only.
1504+
* When it's user data only make sure that we fill up the rest of the
1505+
* SRAM with 0xff.
1506+
*/
1507+
for (i = 0; i < no_subpages; i++) {
1508+
int now = min(buf_len, 512);
1509+
1510+
if (now)
1511+
memcpy16_toio(host->main_area0 + i * 512, buf, now);
1512+
1513+
if (now < 512)
1514+
memff16_toio(host->main_area0 + i * 512 + now, 512 - now);
1515+
1516+
buf += 512;
1517+
buf_len -= now;
1518+
1519+
now = min(buf_len, oob_per_subpage);
1520+
if (now)
1521+
memcpy16_toio(host->spare0 + i * host->devtype_data->spare_len,
1522+
buf, now);
1523+
1524+
if (now < oob_per_subpage)
1525+
memff16_toio(host->spare0 + i * host->devtype_data->spare_len + now,
1526+
oob_per_subpage - now);
1527+
1528+
buf += oob_per_subpage;
1529+
buf_len -= now;
1530+
}
1531+
}
1532+
1533+
static void copy_page_from_sram(struct mtd_info *mtd)
1534+
{
1535+
struct nand_chip *this = mtd_to_nand(mtd);
1536+
struct mxc_nand_host *host = nand_get_controller_data(this);
1537+
void *buf = host->data_buf;
1538+
unsigned int no_subpages = mtd->writesize / 512;
1539+
int oob_per_subpage, i;
1540+
1541+
/* mtd->writesize is not set during ident scanning */
1542+
if (!no_subpages)
1543+
no_subpages = 1;
1544+
1545+
oob_per_subpage = (mtd->oobsize / no_subpages) & ~1;
1546+
1547+
for (i = 0; i < no_subpages; i++) {
1548+
memcpy16_fromio(buf, host->main_area0 + i * 512, 512);
1549+
buf += 512;
1550+
1551+
memcpy16_fromio(buf, host->spare0 + i * host->devtype_data->spare_len,
1552+
oob_per_subpage);
1553+
buf += oob_per_subpage;
1554+
}
1555+
}
1556+
14731557
static int mxcnd_do_exec_op(struct nand_chip *chip,
14741558
const struct nand_subop *op)
14751559
{
@@ -1508,7 +1592,10 @@ static int mxcnd_do_exec_op(struct nand_chip *chip,
15081592
buf_write = instr->ctx.data.buf.out;
15091593
buf_len = instr->ctx.data.len;
15101594

1511-
memcpy32_toio(host->main_area0, buf_write, buf_len);
1595+
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
1596+
memcpy32_toio(host->main_area0, buf_write, buf_len);
1597+
else
1598+
copy_page_to_sram(mtd, buf_write, buf_len);
15121599

15131600
host->devtype_data->send_page(mtd, NFC_INPUT);
15141601

@@ -1543,10 +1630,15 @@ static int mxcnd_do_exec_op(struct nand_chip *chip,
15431630

15441631
host->devtype_data->read_page(chip);
15451632

1546-
if (IS_ALIGNED(buf_len, 4)) {
1547-
memcpy32_fromio(buf_read, host->main_area0, buf_len);
1633+
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
1634+
if (IS_ALIGNED(buf_len, 4)) {
1635+
memcpy32_fromio(buf_read, host->main_area0, buf_len);
1636+
} else {
1637+
memcpy32_fromio(host->data_buf, host->main_area0, mtd->writesize);
1638+
memcpy(buf_read, host->data_buf, buf_len);
1639+
}
15481640
} else {
1549-
memcpy32_fromio(host->data_buf, host->main_area0, mtd->writesize);
1641+
copy_page_from_sram(mtd);
15501642
memcpy(buf_read, host->data_buf, buf_len);
15511643
}
15521644

0 commit comments

Comments
 (0)