mtd: atmel_nand: Support PMECC on SAMA5D2
authorRomain Izard <romain.izard.pro@gmail.com>
Wed, 10 Feb 2016 09:56:25 +0000 (10:56 +0100)
committerBrian Norris <computersforpeace@gmail.com>
Fri, 12 Feb 2016 18:27:47 +0000 (10:27 -0800)
Starting with the SAMA5D2, there is a new revision of the Atmel PMECC
controller that can correct 32 bits in each sector. This controller is
not 100% compatible with the previous revision that corrected a maximum
of 24 bits by sector, as some register addresses overlap.

Using information from the device tree, we can configure the driver to
work with both versions.

For the binding:
Acked-by: Rob Herring <robh@kernel.org>
Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Romain Izard <romain.izard.pro@gmail.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Documentation/devicetree/bindings/mtd/atmel-nand.txt
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/atmel_nand_ecc.h

index e68ab40..e1887b7 100644 (file)
@@ -1,7 +1,10 @@
 Atmel NAND flash
 
 Required properties:
-- compatible : should be "atmel,at91rm9200-nand" or "atmel,sama5d4-nand".
+- compatible: The possible values are:
+       "atmel,at91rm9200-nand"
+       "atmel,sama5d2-nand"
+       "atmel,sama5d4-nand"
 - reg : should specify localbus address and size used for the chip,
        and hardware ECC controller if available.
        If the hardware ECC is PMECC, it should contain address and size for
index 06a3e11..d883279 100644 (file)
@@ -65,6 +65,7 @@ module_param(on_flash_bbt, int, 0);
 
 struct atmel_nand_caps {
        bool pmecc_correct_erase_page;
+       uint8_t pmecc_max_correction;
 };
 
 struct atmel_nand_nfc_caps {
@@ -145,6 +146,7 @@ struct atmel_nand_host {
        int                     pmecc_cw_len;   /* Length of codeword */
 
        void __iomem            *pmerrloc_base;
+       void __iomem            *pmerrloc_el_base;
        void __iomem            *pmecc_rom_base;
 
        /* lookup table for alpha_to and index_of */
@@ -818,7 +820,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
        sector_size = host->pmecc_sector_size;
 
        while (err_nbr) {
-               tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
+               tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_el_base, i) - 1;
                byte_pos = tmp / 8;
                bit_pos  = tmp % 8;
 
@@ -1210,6 +1212,8 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
                err_no = PTR_ERR(host->pmerrloc_base);
                goto err;
        }
+       host->pmerrloc_el_base = host->pmerrloc_base + ATMEL_PMERRLOC_SIGMAx +
+               (host->caps->pmecc_max_correction + 1) * 4;
 
        if (!host->has_no_lookup_table) {
                regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
@@ -2307,17 +2311,34 @@ static int atmel_nand_remove(struct platform_device *pdev)
        return 0;
 }
 
+/*
+ * AT91RM9200 does not have PMECC or PMECC Errloc peripherals for
+ * BCH ECC. Combined with the "atmel,has-pmecc", it is used to describe
+ * devices from the SAM9 family that have those.
+ */
 static const struct atmel_nand_caps at91rm9200_caps = {
        .pmecc_correct_erase_page = false,
+       .pmecc_max_correction = 24,
 };
 
 static const struct atmel_nand_caps sama5d4_caps = {
        .pmecc_correct_erase_page = true,
+       .pmecc_max_correction = 24,
+};
+
+/*
+ * The PMECC Errloc controller starting in SAMA5D2 is not compatible,
+ * as the increased correction strength requires more registers.
+ */
+static const struct atmel_nand_caps sama5d2_caps = {
+       .pmecc_correct_erase_page = true,
+       .pmecc_max_correction = 32,
 };
 
 static const struct of_device_id atmel_nand_dt_ids[] = {
        { .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps },
        { .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps },
+       { .compatible = "atmel,sama5d2-nand", .data = &sama5d2_caps },
        { /* sentinel */ }
 };
 
index 668e735..ec964c4 100644 (file)
 #define                PMERRLOC_ERR_NUM_MASK           (0x1f << 8)
 #define                PMERRLOC_CALC_DONE              (1 << 0)
 #define ATMEL_PMERRLOC_SIGMAx          0x028   /* Error location SIGMA x */
-#define ATMEL_PMERRLOC_ELx             0x08c   /* Error location x */
+
+/*
+ * The ATMEL_PMERRLOC_ELx register location depends from the number of
+ * bits corrected by the PMECC controller. Do not use it.
+ */
 
 /* Register access macros for PMECC */
 #define pmecc_readl_relaxed(addr, reg) \
        readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
 
 #define pmerrloc_readl_el_relaxed(addr, n) \
-       readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4))
+       readl_relaxed((addr) + ((n) * 4))
 
 /* Galois field dimension */
 #define PMECC_GF_DIMENSION_13                  13