]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/mtd/nand/fsmc_nand.c
mtd: nand: Pass the CS line to ->setup_data_interface()
[karo-tx-linux.git] / drivers / mtd / nand / fsmc_nand.c
index cea50d2f218c1d33c09005f3a5084372fa7a18e1..9d8b051d318709d454bf5ad832cd13243eb735e7 100644 (file)
@@ -302,25 +302,13 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
  * This routine initializes timing parameters related to NAND memory access in
  * FSMC registers
  */
-static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
-                          uint32_t busw, struct fsmc_nand_timings *timings)
+static void fsmc_nand_setup(struct fsmc_nand_data *host,
+                           struct fsmc_nand_timings *tims)
 {
        uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
        uint32_t tclr, tar, thiz, thold, twait, tset;
-       struct fsmc_nand_timings *tims;
-       struct fsmc_nand_timings default_timings = {
-               .tclr   = FSMC_TCLR_1,
-               .tar    = FSMC_TAR_1,
-               .thiz   = FSMC_THIZ_1,
-               .thold  = FSMC_THOLD_4,
-               .twait  = FSMC_TWAIT_6,
-               .tset   = FSMC_TSET_0,
-       };
-
-       if (timings)
-               tims = timings;
-       else
-               tims = &default_timings;
+       unsigned int bank = host->bank;
+       void __iomem *regs = host->regs_va;
 
        tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
        tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
@@ -329,7 +317,7 @@ static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
        twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT;
        tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
 
-       if (busw)
+       if (host->nand.options & NAND_BUSWIDTH_16)
                writel_relaxed(value | FSMC_DEVWID_16,
                                FSMC_NAND_REG(regs, bank, PC));
        else
@@ -344,6 +332,87 @@ static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
                        FSMC_NAND_REG(regs, bank, ATTRIB));
 }
 
+static int fsmc_calc_timings(struct fsmc_nand_data *host,
+                            const struct nand_sdr_timings *sdrt,
+                            struct fsmc_nand_timings *tims)
+{
+       unsigned long hclk = clk_get_rate(host->clk);
+       unsigned long hclkn = NSEC_PER_SEC / hclk;
+       uint32_t thiz, thold, twait, tset;
+
+       if (sdrt->tRC_min < 30000)
+               return -EOPNOTSUPP;
+
+       tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1;
+       if (tims->tar > FSMC_TAR_MASK)
+               tims->tar = FSMC_TAR_MASK;
+       tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1;
+       if (tims->tclr > FSMC_TCLR_MASK)
+               tims->tclr = FSMC_TCLR_MASK;
+
+       thiz = sdrt->tCS_min - sdrt->tWP_min;
+       tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn);
+
+       thold = sdrt->tDH_min;
+       if (thold < sdrt->tCH_min)
+               thold = sdrt->tCH_min;
+       if (thold < sdrt->tCLH_min)
+               thold = sdrt->tCLH_min;
+       if (thold < sdrt->tWH_min)
+               thold = sdrt->tWH_min;
+       if (thold < sdrt->tALH_min)
+               thold = sdrt->tALH_min;
+       if (thold < sdrt->tREH_min)
+               thold = sdrt->tREH_min;
+       tims->thold = DIV_ROUND_UP(thold / 1000, hclkn);
+       if (tims->thold == 0)
+               tims->thold = 1;
+       else if (tims->thold > FSMC_THOLD_MASK)
+               tims->thold = FSMC_THOLD_MASK;
+
+       twait = max(sdrt->tRP_min, sdrt->tWP_min);
+       tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1;
+       if (tims->twait == 0)
+               tims->twait = 1;
+       else if (tims->twait > FSMC_TWAIT_MASK)
+               tims->twait = FSMC_TWAIT_MASK;
+
+       tset = max(sdrt->tCS_min - sdrt->tWP_min,
+                  sdrt->tCEA_max - sdrt->tREA_max);
+       tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1;
+       if (tims->tset == 0)
+               tims->tset = 1;
+       else if (tims->tset > FSMC_TSET_MASK)
+               tims->tset = FSMC_TSET_MASK;
+
+       return 0;
+}
+
+static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline,
+                                    const struct nand_data_interface *conf)
+{
+       struct nand_chip *nand = mtd_to_nand(mtd);
+       struct fsmc_nand_data *host = nand_get_controller_data(nand);
+       struct fsmc_nand_timings tims;
+       const struct nand_sdr_timings *sdrt;
+       int ret;
+
+       sdrt = nand_get_sdr_timings(conf);
+       if (IS_ERR(sdrt))
+               return PTR_ERR(sdrt);
+
+       ret = fsmc_calc_timings(host, sdrt, &tims);
+       if (ret)
+               return ret;
+
+       if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+               return 0;
+
+       fsmc_nand_setup(host, &tims);
+
+       return 0;
+}
+
 /*
  * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
  */
@@ -796,10 +865,8 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
                return -ENOMEM;
        ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
                                                sizeof(*host->dev_timings));
-       if (ret) {
-               dev_info(&pdev->dev, "No timings in dts specified, using default timings!\n");
+       if (ret)
                host->dev_timings = NULL;
-       }
 
        /* Set default NAND bank to 0 */
        host->bank = 0;
@@ -933,9 +1000,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                break;
        }
 
-       fsmc_nand_setup(host->regs_va, host->bank,
-                       nand->options & NAND_BUSWIDTH_16,
-                       host->dev_timings);
+       if (host->dev_timings)
+               fsmc_nand_setup(host, host->dev_timings);
+       else
+               nand->setup_data_interface = fsmc_setup_data_interface;
 
        if (AMBA_REV_BITS(host->pid) >= 8) {
                nand->ecc.read_page = fsmc_read_page_hwecc;
@@ -986,6 +1054,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                                break;
                        }
 
+               case NAND_ECC_ON_DIE:
+                       break;
+
                default:
                        dev_err(&pdev->dev, "Unsupported ECC mode!\n");
                        goto err_probe;
@@ -1073,9 +1144,8 @@ static int fsmc_nand_resume(struct device *dev)
        struct fsmc_nand_data *host = dev_get_drvdata(dev);
        if (host) {
                clk_prepare_enable(host->clk);
-               fsmc_nand_setup(host->regs_va, host->bank,
-                               host->nand.options & NAND_BUSWIDTH_16,
-                               host->dev_timings);
+               if (host->dev_timings)
+                       fsmc_nand_setup(host, host->dev_timings);
        }
        return 0;
 }