Merge tag 'mac80211-for-john-2014-11-18' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / drivers / mtd / nand / atmel_nand.c
index e321c56..19d1e9d 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;
@@ -128,8 +131,6 @@ struct atmel_nand_host {
        u32                     pmecc_lookup_table_offset_512;
        u32                     pmecc_lookup_table_offset_1024;
 
-       int                     pmecc_bytes_per_sector;
-       int                     pmecc_sector_number;
        int                     pmecc_degree;   /* Degree of remainders */
        int                     pmecc_cw_len;   /* Length of codeword */
 
@@ -841,7 +842,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
                                pos, bit_pos, err_byte, *(buf + byte_pos));
                } else {
                        /* Bit flip in OOB area */
-                       tmp = sector_num * host->pmecc_bytes_per_sector
+                       tmp = sector_num * nand_chip->ecc.bytes
                                        + (byte_pos - sector_size);
                        err_byte = ecc[tmp];
                        ecc[tmp] ^= (1 << bit_pos);
@@ -874,7 +875,7 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
        return 0;
 
 normal_check:
-       for (i = 0; i < host->pmecc_sector_number; i++) {
+       for (i = 0; i < nand_chip->ecc.steps; i++) {
                err_nbr = 0;
                if (pmecc_stat & 0x1) {
                        buf_pos = buf + i * host->pmecc_sector_size;
@@ -890,7 +891,7 @@ normal_check:
                                return -EIO;
                        } else {
                                pmecc_correct_data(mtd, buf_pos, ecc, i,
-                                       host->pmecc_bytes_per_sector, err_nbr);
+                                       nand_chip->ecc.bytes, err_nbr);
                                mtd->ecc_stats.corrected += err_nbr;
                                total_err += err_nbr;
                        }
@@ -984,11 +985,11 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
                cpu_relax();
        }
 
-       for (i = 0; i < host->pmecc_sector_number; i++) {
-               for (j = 0; j < host->pmecc_bytes_per_sector; j++) {
+       for (i = 0; i < chip->ecc.steps; i++) {
+               for (j = 0; j < chip->ecc.bytes; j++) {
                        int pos;
 
-                       pos = i * host->pmecc_bytes_per_sector + j;
+                       pos = i * chip->ecc.bytes + j;
                        chip->oob_poi[eccpos[pos]] =
                                pmecc_readb_ecc_relaxed(host->ecc, i, j);
                }
@@ -1031,7 +1032,7 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
        else if (host->pmecc_sector_size == 1024)
                val |= PMECC_CFG_SECTOR1024;
 
-       switch (host->pmecc_sector_number) {
+       switch (nand_chip->ecc.steps) {
        case 1:
                val |= PMECC_CFG_PAGE_1SECTOR;
                break;
@@ -1148,7 +1149,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 +1156,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 +1163,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,22 +1171,29 @@ 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;
-               host->pmecc_sector_number = mtd->writesize / sector_size;
-               host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes(
-                       cap, sector_size);
                host->pmecc_alpha_to = pmecc_get_alpha_to(host);
                host->pmecc_index_of = host->pmecc_rom_base +
                        host->pmecc_lookup_table_offset;
 
-               nand_chip->ecc.steps = host->pmecc_sector_number;
                nand_chip->ecc.strength = cap;
-               nand_chip->ecc.bytes = host->pmecc_bytes_per_sector;
-               nand_chip->ecc.total = host->pmecc_bytes_per_sector *
-                                      host->pmecc_sector_number;
+               nand_chip->ecc.bytes = pmecc_get_ecc_bytes(cap, sector_size);
+               nand_chip->ecc.steps = mtd->writesize / sector_size;
+               nand_chip->ecc.total = nand_chip->ecc.bytes *
+                       nand_chip->ecc.steps;
                if (nand_chip->ecc.total > mtd->oobsize - 2) {
                        dev_err(host->dev, "No room for ECC bytes\n");
                        err_no = -EINVAL;
@@ -1201,13 +1205,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 +1530,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 +1905,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 +1915,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 +2039,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 +2097,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 +2246,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 +2278,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 +2316,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 = {