Merge tag 'for-linus-4.6-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git...
[deliverable/linux.git] / drivers / mtd / nand / atmel_nand.c
index bddcf83d6859aecd689797c99d5745569f39b713..20cbaabb29590f61f2009411e114e1fc83acec81 100644 (file)
@@ -65,6 +65,11 @@ 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 {
+       uint32_t rb_mask;
 };
 
 /* oob layout for large page size
@@ -111,6 +116,7 @@ struct atmel_nfc {
        /* Point to the sram bank which include readed data via NFC */
        void                    *data_in_sram;
        bool                    will_write_sram;
+       const struct atmel_nand_nfc_caps *caps;
 };
 static struct atmel_nfc        nand_nfc;
 
@@ -140,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 */
@@ -468,6 +475,7 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
  *                8-bits                13-bytes                 14-bytes
  *               12-bits                20-bytes                 21-bytes
  *               24-bits                39-bytes                 42-bytes
+ *               32-bits                52-bytes                 56-bytes
  */
 static int pmecc_get_ecc_bytes(int cap, int sector_size)
 {
@@ -813,7 +821,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;
 
@@ -825,7 +833,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
                        *(buf + byte_pos) ^= (1 << bit_pos);
 
                        pos = sector_num * host->pmecc_sector_size + byte_pos;
-                       dev_info(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
+                       dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
                                pos, bit_pos, err_byte, *(buf + byte_pos));
                } else {
                        /* Bit flip in OOB area */
@@ -835,7 +843,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
                        ecc[tmp] ^= (1 << bit_pos);
 
                        pos = tmp + nand_chip->ecc.layout->eccpos[0];
-                       dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
+                       dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
                                pos, bit_pos, err_byte, ecc[tmp]);
                }
 
@@ -1017,6 +1025,9 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
        case 24:
                val = PMECC_CFG_BCH_ERR24;
                break;
+       case 32:
+               val = PMECC_CFG_BCH_ERR32;
+               break;
        }
 
        if (host->pmecc_sector_size == 512)
@@ -1078,6 +1089,9 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
 
        /* If device tree doesn't specify, use NAND's minimum ECC parameters */
        if (host->pmecc_corr_cap == 0) {
+               if (*cap > host->caps->pmecc_max_correction)
+                       return -EINVAL;
+
                /* use the most fitable ecc bits (the near bigger one ) */
                if (*cap <= 2)
                        host->pmecc_corr_cap = 2;
@@ -1089,6 +1103,8 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
                        host->pmecc_corr_cap = 12;
                else if (*cap <= 24)
                        host->pmecc_corr_cap = 24;
+               else if (*cap <= 32)
+                       host->pmecc_corr_cap = 32;
                else
                        return -EINVAL;
        }
@@ -1205,6 +1221,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);
@@ -1486,8 +1504,6 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
                ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
 }
 
-static const struct of_device_id atmel_nand_dt_ids[];
-
 static int atmel_of_init_port(struct atmel_nand_host *host,
                              struct device_node *np)
 {
@@ -1498,7 +1514,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
        enum of_gpio_flags flags = 0;
 
        host->caps = (struct atmel_nand_caps *)
-               of_match_device(atmel_nand_dt_ids, host->dev)->data;
+               of_device_get_match_data(host->dev);
 
        if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
                if (val >= 32) {
@@ -1547,10 +1563,16 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
         * them from NAND ONFI parameters.
         */
        if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) {
-               if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
-                               (val != 24)) {
+               if (val > host->caps->pmecc_max_correction) {
                        dev_err(host->dev,
-                               "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n",
+                               "Required ECC strength too high: %u max %u\n",
+                               val, host->caps->pmecc_max_correction);
+                       return -EINVAL;
+               }
+               if ((val != 2)  && (val != 4)  && (val != 8) &&
+                   (val != 12) && (val != 24) && (val != 32)) {
+                       dev_err(host->dev,
+                               "Required ECC strength not supported: %u\n",
                                val);
                        return -EINVAL;
                }
@@ -1560,7 +1582,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
        if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) {
                if ((val != 512) && (val != 1024)) {
                        dev_err(host->dev,
-                               "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n",
+                               "Required ECC sector size not supported: %u\n",
                                val);
                        return -EINVAL;
                }
@@ -1677,9 +1699,9 @@ static irqreturn_t hsmc_interrupt(int irq, void *dev_id)
                nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE);
                ret = IRQ_HANDLED;
        }
-       if (pending & NFC_SR_RB_EDGE) {
+       if (pending & host->nfc->caps->rb_mask) {
                complete(&host->nfc->comp_ready);
-               nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE);
+               nfc_writel(host->nfc->hsmc_regs, IDR, host->nfc->caps->rb_mask);
                ret = IRQ_HANDLED;
        }
        if (pending & NFC_SR_CMD_DONE) {
@@ -1697,7 +1719,7 @@ static void nfc_prepare_interrupt(struct atmel_nand_host *host, u32 flag)
        if (flag & NFC_SR_XFR_DONE)
                init_completion(&host->nfc->comp_xfer_done);
 
-       if (flag & NFC_SR_RB_EDGE)
+       if (flag & host->nfc->caps->rb_mask)
                init_completion(&host->nfc->comp_ready);
 
        if (flag & NFC_SR_CMD_DONE)
@@ -1715,7 +1737,7 @@ static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
        if (flag & NFC_SR_XFR_DONE)
                comp[index++] = &host->nfc->comp_xfer_done;
 
-       if (flag & NFC_SR_RB_EDGE)
+       if (flag & host->nfc->caps->rb_mask)
                comp[index++] = &host->nfc->comp_ready;
 
        if (flag & NFC_SR_CMD_DONE)
@@ -1783,7 +1805,7 @@ static int nfc_device_ready(struct mtd_info *mtd)
                dev_err(host->dev, "Lost the interrupt flags: 0x%08x\n",
                                mask & status);
 
-       return status & NFC_SR_RB_EDGE;
+       return status & host->nfc->caps->rb_mask;
 }
 
 static void nfc_select_chip(struct mtd_info *mtd, int chip)
@@ -1956,8 +1978,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
                }
                /* fall through */
        default:
-               nfc_prepare_interrupt(host, NFC_SR_RB_EDGE);
-               nfc_wait_interrupt(host, NFC_SR_RB_EDGE);
+               nfc_prepare_interrupt(host, host->nfc->caps->rb_mask);
+               nfc_wait_interrupt(host, host->nfc->caps->rb_mask);
        }
 }
 
@@ -2304,17 +2326,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 */ }
 };
 
@@ -2354,6 +2393,11 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
                }
        }
 
+       nfc->caps = (const struct atmel_nand_nfc_caps *)
+               of_device_get_match_data(&pdev->dev);
+       if (!nfc->caps)
+               return -ENODEV;
+
        nfc_writel(nfc->hsmc_regs, IDR, 0xffffffff);
        nfc_readl(nfc->hsmc_regs, SR);  /* clear the NFC_SR */
 
@@ -2382,8 +2426,17 @@ static int atmel_nand_nfc_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct atmel_nand_nfc_caps sama5d3_nfc_caps = {
+       .rb_mask = NFC_SR_RB_EDGE0,
+};
+
+static const struct atmel_nand_nfc_caps sama5d4_nfc_caps = {
+       .rb_mask = NFC_SR_RB_EDGE3,
+};
+
 static const struct of_device_id atmel_nand_nfc_match[] = {
-       { .compatible = "atmel,sama5d3-nfc" },
+       { .compatible = "atmel,sama5d3-nfc", .data = &sama5d3_nfc_caps },
+       { .compatible = "atmel,sama5d4-nfc", .data = &sama5d4_nfc_caps },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, atmel_nand_nfc_match);
This page took 0.027543 seconds and 5 git commands to generate.