OSDN Git Service

mtd: nand: atmel: Avoid ECC errors when leaving backup mode
authorRomain Izard <romain.izard.pro@gmail.com>
Thu, 28 Sep 2017 09:46:23 +0000 (11:46 +0200)
committerBoris Brezillon <boris.brezillon@free-electrons.com>
Sat, 7 Oct 2017 09:28:05 +0000 (11:28 +0200)
During backup mode, the contents of all registers will be cleared as the
SoC will be completely powered down. For a product that boots on NAND
Flash memory, the bootloader will obviously use the related controller
to read the Flash and correct any detected error in the memory, before
handling back control to the kernel's resuming entry point.

But it does not clean the NAND controller registers after use and on its
side the kernel driver expects the error locator to be powered down and
in a clean state. Add a resume hook for the PMECC error locator, and
reset its registers.

Signed-off-by: Romain Izard <romain.izard.pro@gmail.com>
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
drivers/mtd/nand/atmel/nand-controller.c
drivers/mtd/nand/atmel/pmecc.c
drivers/mtd/nand/atmel/pmecc.h

index 7bc8d20..2918216 100644 (file)
@@ -2529,6 +2529,9 @@ static __maybe_unused int atmel_nand_controller_resume(struct device *dev)
        struct atmel_nand_controller *nc = dev_get_drvdata(dev);
        struct atmel_nand *nand;
 
+       if (nc->pmecc)
+               atmel_pmecc_reset(nc->pmecc);
+
        list_for_each_entry(nand, &nc->chips, node) {
                int i;
 
index 146af82..0a3f121 100644 (file)
@@ -765,6 +765,13 @@ void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
 
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
+{
+       writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+       writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_reset);
+
 int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
 {
        struct atmel_pmecc *pmecc = user->pmecc;
@@ -797,10 +804,7 @@ EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
 
 void atmel_pmecc_disable(struct atmel_pmecc_user *user)
 {
-       struct atmel_pmecc *pmecc = user->pmecc;
-
-       writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-       writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+       atmel_pmecc_reset(user->pmecc);
        mutex_unlock(&user->pmecc->lock);
 }
 EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
@@ -855,10 +859,7 @@ static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev,
 
        /* Disable all interrupts before registering the PMECC handler. */
        writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
-
-       /* Reset the ECC engine */
-       writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
-       writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+       atmel_pmecc_reset(pmecc);
 
        return pmecc;
 }
index a8ddbfc..817e0dd 100644 (file)
@@ -61,6 +61,7 @@ atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
                        struct atmel_pmecc_user_req *req);
 void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
 
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc);
 int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
 void atmel_pmecc_disable(struct atmel_pmecc_user *user);
 int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);