Merge tag 'pnp-extra-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[deliverable/linux.git] / drivers / mtd / nand / sunxi_nand.c
index b9a2e5d2875434affd133a01a2e8afb1235bb620..e414b31b71c17111e923825ec8c178a3c40e8948 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
+#include <linux/reset.h>
 
 #define NFC_REG_CTL            0x0000
 #define NFC_REG_ST             0x0004
@@ -270,6 +271,7 @@ struct sunxi_nfc {
        void __iomem *regs;
        struct clk *ahb_clk;
        struct clk *mod_clk;
+       struct reset_control *reset;
        unsigned long assigned_cs;
        unsigned long clk_rate;
        struct list_head chips;
@@ -1352,6 +1354,36 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
        return 0;
 }
 
+static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
+                                         struct nand_chip *chip,
+                                         u32 data_offs, u32 data_len,
+                                         const u8 *buf, int oob_required,
+                                         int page)
+{
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       int ret, i, cur_off = 0;
+
+       sunxi_nfc_hw_ecc_enable(mtd);
+
+       for (i = data_offs / ecc->size;
+            i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
+               int data_off = i * ecc->size;
+               int oob_off = i * (ecc->bytes + 4);
+               const u8 *data = buf + data_off;
+               const u8 *oob = chip->oob_poi + oob_off;
+
+               ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
+                                                  oob_off + mtd->writesize,
+                                                  &cur_off, !i, page);
+               if (ret)
+                       return ret;
+       }
+
+       sunxi_nfc_hw_ecc_disable(mtd);
+
+       return 0;
+}
+
 static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,
                                           struct nand_chip *chip,
                                           const u8 *buf,
@@ -1782,10 +1814,19 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
        int ret;
        int i;
 
+       if (ecc->size != 512 && ecc->size != 1024)
+               return -EINVAL;
+
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
+       /* Prefer 1k ECC chunk over 512 ones */
+       if (ecc->size == 512 && mtd->writesize > 512) {
+               ecc->size = 1024;
+               ecc->strength *= 2;
+       }
+
        /* Add ECC info retrieval from DT */
        for (i = 0; i < ARRAY_SIZE(strengths); i++) {
                if (ecc->strength <= strengths[i])
@@ -1855,7 +1896,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
                ecc->write_page = sunxi_nfc_hw_ecc_write_page;
        }
 
-       /* TODO: support DMA for raw accesses */
+       /* TODO: support DMA for raw accesses and subpage write */
+       ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
        ecc->read_oob_raw = nand_read_oob_std;
        ecc->write_oob_raw = nand_write_oob_std;
        ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
@@ -2169,15 +2211,27 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
        if (ret)
                goto out_ahb_clk_unprepare;
 
+       nfc->reset = devm_reset_control_get_optional(dev, "ahb");
+       if (!IS_ERR(nfc->reset)) {
+               ret = reset_control_deassert(nfc->reset);
+               if (ret) {
+                       dev_err(dev, "reset err %d\n", ret);
+                       goto out_mod_clk_unprepare;
+               }
+       } else if (PTR_ERR(nfc->reset) != -ENOENT) {
+               ret = PTR_ERR(nfc->reset);
+               goto out_mod_clk_unprepare;
+       }
+
        ret = sunxi_nfc_rst(nfc);
        if (ret)
-               goto out_mod_clk_unprepare;
+               goto out_ahb_reset_reassert;
 
        writel(0, nfc->regs + NFC_REG_INT);
        ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
                               0, "sunxi-nand", nfc);
        if (ret)
-               goto out_mod_clk_unprepare;
+               goto out_ahb_reset_reassert;
 
        nfc->dmac = dma_request_slave_channel(dev, "rxtx");
        if (nfc->dmac) {
@@ -2207,6 +2261,9 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
 out_release_dmac:
        if (nfc->dmac)
                dma_release_channel(nfc->dmac);
+out_ahb_reset_reassert:
+       if (!IS_ERR(nfc->reset))
+               reset_control_assert(nfc->reset);
 out_mod_clk_unprepare:
        clk_disable_unprepare(nfc->mod_clk);
 out_ahb_clk_unprepare:
@@ -2220,6 +2277,10 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
        struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
 
        sunxi_nand_chips_cleanup(nfc);
+
+       if (!IS_ERR(nfc->reset))
+               reset_control_assert(nfc->reset);
+
        if (nfc->dmac)
                dma_release_channel(nfc->dmac);
        clk_disable_unprepare(nfc->mod_clk);
This page took 0.038027 seconds and 5 git commands to generate.