|
@@ -418,6 +418,128 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
|
|
|
nand_writel(info, NDTR1CS0, ndtr1);
|
|
|
}
|
|
|
|
|
|
+static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host,
|
|
|
+ const struct nand_sdr_timings *t)
|
|
|
+{
|
|
|
+ struct pxa3xx_nand_info *info = host->info_data;
|
|
|
+ struct nand_chip *chip = &host->chip;
|
|
|
+ unsigned long nand_clk = clk_get_rate(info->clk);
|
|
|
+ uint32_t ndtr0, ndtr1;
|
|
|
+
|
|
|
+ u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000);
|
|
|
+ u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000);
|
|
|
+ u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000);
|
|
|
+ u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000);
|
|
|
+ u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000);
|
|
|
+ u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000);
|
|
|
+ u32 tR = chip->chip_delay * 1000;
|
|
|
+ u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000);
|
|
|
+ u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);
|
|
|
+
|
|
|
+ /* fallback to a default value if tR = 0 */
|
|
|
+ if (!tR)
|
|
|
+ tR = 20000;
|
|
|
+
|
|
|
+ ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) |
|
|
|
+ NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) |
|
|
|
+ NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) |
|
|
|
+ NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) |
|
|
|
+ NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) |
|
|
|
+ NDTR0_tRP(ns2cycle(tRP_min, nand_clk));
|
|
|
+
|
|
|
+ ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) |
|
|
|
+ NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) |
|
|
|
+ NDTR1_tAR(ns2cycle(tAR_min, nand_clk));
|
|
|
+
|
|
|
+ info->ndtr0cs0 = ndtr0;
|
|
|
+ info->ndtr1cs0 = ndtr1;
|
|
|
+ nand_writel(info, NDTR0CS0, ndtr0);
|
|
|
+ nand_writel(info, NDTR1CS0, ndtr1);
|
|
|
+}
|
|
|
+
|
|
|
+static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host,
|
|
|
+ unsigned int *flash_width,
|
|
|
+ unsigned int *dfc_width)
|
|
|
+{
|
|
|
+ struct nand_chip *chip = &host->chip;
|
|
|
+ struct pxa3xx_nand_info *info = host->info_data;
|
|
|
+ const struct pxa3xx_nand_flash *f = NULL;
|
|
|
+ int i, id, ntypes;
|
|
|
+
|
|
|
+ ntypes = ARRAY_SIZE(builtin_flash_types);
|
|
|
+
|
|
|
+ chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
+
|
|
|
+ id = chip->read_byte(host->mtd);
|
|
|
+ id |= chip->read_byte(host->mtd) << 0x8;
|
|
|
+
|
|
|
+ for (i = 0; i < ntypes; i++) {
|
|
|
+ f = &builtin_flash_types[i];
|
|
|
+
|
|
|
+ if (f->chip_id == id)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i == ntypes) {
|
|
|
+ dev_err(&info->pdev->dev, "Error: timings not found\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ pxa3xx_nand_set_timing(host, f->timing);
|
|
|
+
|
|
|
+ *flash_width = f->flash_width;
|
|
|
+ *dfc_width = f->dfc_width;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host,
|
|
|
+ int mode)
|
|
|
+{
|
|
|
+ const struct nand_sdr_timings *timings;
|
|
|
+
|
|
|
+ mode = fls(mode) - 1;
|
|
|
+ if (mode < 0)
|
|
|
+ mode = 0;
|
|
|
+
|
|
|
+ timings = onfi_async_timing_mode_to_sdr_timings(mode);
|
|
|
+ if (IS_ERR(timings))
|
|
|
+ return PTR_ERR(timings);
|
|
|
+
|
|
|
+ pxa3xx_nand_set_sdr_timing(host, timings);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int pxa3xx_nand_init(struct pxa3xx_nand_host *host)
|
|
|
+{
|
|
|
+ struct nand_chip *chip = &host->chip;
|
|
|
+ struct pxa3xx_nand_info *info = host->info_data;
|
|
|
+ unsigned int flash_width = 0, dfc_width = 0;
|
|
|
+ int mode, err;
|
|
|
+
|
|
|
+ mode = onfi_get_async_timing_mode(chip);
|
|
|
+ if (mode == ONFI_TIMING_MODE_UNKNOWN) {
|
|
|
+ err = pxa3xx_nand_init_timings_compat(host, &flash_width,
|
|
|
+ &dfc_width);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (flash_width == 16) {
|
|
|
+ info->reg_ndcr |= NDCR_DWIDTH_M;
|
|
|
+ chip->options |= NAND_BUSWIDTH_16;
|
|
|
+ }
|
|
|
+
|
|
|
+ info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0;
|
|
|
+ } else {
|
|
|
+ err = pxa3xx_nand_init_timings_onfi(host, mode);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Set the data and OOB size, depending on the selected
|
|
|
* spare and ECC configuration.
|