mtd: nand: atmel_nand: retrieve NFC clock
[cascardo/linux.git] / drivers / mtd / nand / atmel_nand.c
index e321c56..d1e502f 100644 (file)
@@ -27,6 +27,7 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -96,6 +97,8 @@ struct atmel_nfc {
        bool                    use_nfc_sram;
        bool                    write_by_sram;
 
+       struct clk              *clk;
+
        bool                    is_initialized;
        struct completion       comp_ready;
        struct completion       comp_cmd_done;
@@ -1148,7 +1151,6 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
 
        host->ecc = devm_ioremap_resource(&pdev->dev, regs);
        if (IS_ERR(host->ecc)) {
-               dev_err(host->dev, "ioremap failed\n");
                err_no = PTR_ERR(host->ecc);
                goto err;
        }
@@ -1156,8 +1158,6 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
        regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2);
        host->pmerrloc_base = devm_ioremap_resource(&pdev->dev, regs_pmerr);
        if (IS_ERR(host->pmerrloc_base)) {
-               dev_err(host->dev,
-                       "Can not get I/O resource for PMECC ERRLOC controller!\n");
                err_no = PTR_ERR(host->pmerrloc_base);
                goto err;
        }
@@ -1165,7 +1165,6 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
        regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
        host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom);
        if (IS_ERR(host->pmecc_rom_base)) {
-               dev_err(host->dev, "Can not get I/O resource for ROM!\n");
                err_no = PTR_ERR(host->pmecc_rom_base);
                goto err;
        }
@@ -1174,7 +1173,17 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
 
        /* set ECC page size and oob layout */
        switch (mtd->writesize) {
+       case 512:
+       case 1024:
        case 2048:
+       case 4096:
+       case 8192:
+               if (sector_size > mtd->writesize) {
+                       dev_err(host->dev, "pmecc sector size is bigger than the page size!\n");
+                       err_no = -EINVAL;
+                       goto err;
+               }
+
                host->pmecc_degree = (sector_size == 512) ?
                        PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14;
                host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
@@ -1201,13 +1210,9 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
 
                nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
                break;
-       case 512:
-       case 1024:
-       case 4096:
-               /* TODO */
+       default:
                dev_warn(host->dev,
                        "Unsupported page size for PMECC, use Software ECC\n");
-       default:
                /* page size not handled by HW ECC */
                /* switching back to soft ECC */
                nand_chip->ecc.mode = NAND_ECC_SOFT;
@@ -1530,10 +1535,8 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev,
        }
 
        host->ecc = devm_ioremap_resource(&pdev->dev, regs);
-       if (IS_ERR(host->ecc)) {
-               dev_err(host->dev, "ioremap failed\n");
+       if (IS_ERR(host->ecc))
                return PTR_ERR(host->ecc);
-       }
 
        /* ECC is calculated for the whole page (1 step) */
        nand_chip->ecc.size = mtd->writesize;
@@ -1907,15 +1910,7 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
        if (offset || (data_len < mtd->writesize))
                return -EINVAL;
 
-       cfg = nfc_readl(host->nfc->hsmc_regs, CFG);
        len = mtd->writesize;
-
-       if (unlikely(raw)) {
-               len += mtd->oobsize;
-               nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE);
-       } else
-               nfc_writel(host->nfc->hsmc_regs, CFG, cfg & ~NFC_CFG_WSPARE);
-
        /* Copy page data to sram that will write to nand via NFC */
        if (use_dma) {
                if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0)
@@ -1925,6 +1920,15 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
                memcpy32_toio(sram, buf, len);
        }
 
+       cfg = nfc_readl(host->nfc->hsmc_regs, CFG);
+       if (unlikely(raw) && oob_required) {
+               memcpy32_toio(sram + len, chip->oob_poi, mtd->oobsize);
+               len += mtd->oobsize;
+               nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE);
+       } else {
+               nfc_writel(host->nfc->hsmc_regs, CFG, cfg & ~NFC_CFG_WSPARE);
+       }
+
        if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc)
                /*
                 * When use NFC sram, need set up PMECC before send
@@ -2040,7 +2044,6 @@ static int atmel_nand_probe(struct platform_device *pdev)
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        host->io_base = devm_ioremap_resource(&pdev->dev, mem);
        if (IS_ERR(host->io_base)) {
-               dev_err(&pdev->dev, "atmel_nand: ioremap resource failed\n");
                res = PTR_ERR(host->io_base);
                goto err_nand_ioremap;
        }
@@ -2099,7 +2102,7 @@ static int atmel_nand_probe(struct platform_device *pdev)
        }
 
        nand_chip->ecc.mode = host->board.ecc_mode;
-       nand_chip->chip_delay = 20;             /* 20us command delay time */
+       nand_chip->chip_delay = 40;             /* 40us command delay time */
 
        if (host->board.bus_width_16)   /* 16-bit bus width */
                nand_chip->options |= NAND_BUSWIDTH_16;
@@ -2248,6 +2251,7 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
 {
        struct atmel_nfc *nfc = &nand_nfc;
        struct resource *nfc_cmd_regs, *nfc_hsmc_regs, *nfc_sram;
+       int ret;
 
        nfc_cmd_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        nfc->base_cmd_regs = devm_ioremap_resource(&pdev->dev, nfc_cmd_regs);
@@ -2279,8 +2283,28 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
        nfc_writel(nfc->hsmc_regs, IDR, 0xffffffff);
        nfc_readl(nfc->hsmc_regs, SR);  /* clear the NFC_SR */
 
+       nfc->clk = devm_clk_get(&pdev->dev, NULL);
+       if (!IS_ERR(nfc->clk)) {
+               ret = clk_prepare_enable(nfc->clk);
+               if (ret)
+                       return ret;
+       } else {
+               dev_warn(&pdev->dev, "NFC clock missing, update your Device Tree");
+       }
+
        nfc->is_initialized = true;
        dev_info(&pdev->dev, "NFC is probed.\n");
+
+       return 0;
+}
+
+static int atmel_nand_nfc_remove(struct platform_device *pdev)
+{
+       struct atmel_nfc *nfc = &nand_nfc;
+
+       if (!IS_ERR(nfc->clk))
+               clk_disable_unprepare(nfc->clk);
+
        return 0;
 }
 
@@ -2297,6 +2321,7 @@ static struct platform_driver atmel_nand_nfc_driver = {
                .of_match_table = of_match_ptr(atmel_nand_nfc_match),
        },
        .probe = atmel_nand_nfc_probe,
+       .remove = atmel_nand_nfc_remove,
 };
 
 static struct platform_driver atmel_nand_driver = {