Skip to contentSkip to conversation

Fix 104MHz support for OMAP OneNAND

PATCH New Adrian Hunter 3 participants 8 messages Feb 7, 2011 – Feb 18, 2011

Subsystems (8)
lore
Share & tools
lore
#0 lore raw

cover Fix 104MHz support for OMAP OneNAND

Hi

Adrian Hunter (3):
OMAP: OneNAND: fix 104MHz support
OMAP: OneNAND: determine frequency in one place
OMAP: OneNAND: let boards determine OneNAND frequency

Roman Tereshonkov (1):
mtd: OneNAND: OMAP2: increase multiblock erase verify timeout

 arch/arm/mach-omap2/gpmc-onenand.c        |  113 +++++++++++++++++++++--------
 arch/arm/plat-omap/include/plat/onenand.h |   10 +++-
 drivers/mtd/onenand/omap2.c               |   36 ++-------
 include/linux/mtd/onenand_regs.h          |    1 +
 4 files changed, 100 insertions(+), 60 deletions(-)

Regards
Adrian

#1 lore raw

PATCH 1/4 OMAP: OneNAND: fix 104MHz support

104MHz needs a latency of 8 clock cycles and the VHF flag must be set. Also t_rdyo is specified as "not applicable" so pick a lower value, and force at least 1 clk between AVD High to OE Low.

---
 arch/arm/mach-omap2/gpmc-onenand.c |   23 +++++++++++++++++------
 include/linux/mtd/onenand_regs.h   |    1 +
 2 files changed, 18 insertions(+), 6 deletions(-)
arch/arm/mach-omap2/gpmc-onenand.c +17 -6
index 3a7d25f..3a4307b 100644
@@ -94,7 +94,7 @@ static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
 }
 
 static void set_onenand_cfg(void __iomem *onenand_base, int latency,
-				int sync_read, int sync_write, int hf)
+				int sync_read, int sync_write, int hf, int vhf)
 {
 	u32 reg;
 
@@ -114,6 +114,10 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency,
 		reg |= ONENAND_SYS_CFG1_HF;
 	else
 		reg &= ~ONENAND_SYS_CFG1_HF;
+	if (vhf)
+		reg |= ONENAND_SYS_CFG1_VHF;
+	else
+		reg &= ~ONENAND_SYS_CFG1_VHF;
 	writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
 }
 
@@ -130,7 +134,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 	const int t_wph  = 30;
 	int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
 	int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;
-	int first_time = 0, hf = 0, sync_read = 0, sync_write = 0;
+	int first_time = 0, hf = 0, vhf = 0, sync_read = 0, sync_write = 0;
 	int err, ticks_cez;
 	int cs = cfg->cs;
 	u32 reg;
@@ -180,7 +184,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 		t_avdh  = 2;
 		t_ach   = 3;
 		t_aavdh = 6;
-		t_rdyo  = 9;
+		t_rdyo  = 6;
 		break;
 	case 83:
 		min_gpmc_clk_period = 12000; /* 83 MHz */
@@ -217,7 +221,11 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 	gpmc_clk_ns = gpmc_ticks_to_ns(div);
 	if (gpmc_clk_ns < 15) /* >66Mhz */
 		hf = 1;
-	if (hf)
+	if (gpmc_clk_ns < 12) /* >83Mhz */
+		vhf = 1;
+	if (vhf)
+		latency = 8;
+	else if (hf)
 		latency = 6;
 	else if (gpmc_clk_ns >= 25) /* 40 MHz*/
 		latency = 3;
@@ -226,7 +234,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 
 	if (first_time)
 		set_onenand_cfg(onenand_base, latency,
-					sync_read, sync_write, hf);
+					sync_read, sync_write, hf, vhf);
 
 	if (div == 1) {
 		reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
@@ -264,6 +272,9 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 	/* Read */
 	t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh));
 	t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach));
+	/* Force at least 1 clk between AVD High to OE Low */
+	if (t.oe_on <= t.adv_rd_off)
+		t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(1);
 	t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div);
 	t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
 	t.cs_rd_off = t.oe_off;
@@ -317,7 +328,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 	if (err)
 		return err;
 
-	set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf);
+	set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf, vhf);
 
 	return 0;
 }
include/linux/mtd/onenand_regs.h +1 -0
index cd6f3b4..d60130f 100644
@@ -168,6 +168,7 @@
 #define ONENAND_SYS_CFG1_INT		(1 << 6)
 #define ONENAND_SYS_CFG1_IOBE		(1 << 5)
 #define ONENAND_SYS_CFG1_RDY_CONF	(1 << 4)
+#define ONENAND_SYS_CFG1_VHF		(1 << 3)
 #define ONENAND_SYS_CFG1_HF		(1 << 2)
 #define ONENAND_SYS_CFG1_SYNC_WRITE	(1 << 1)
 
Signature · 1 line
1.7.0.4
#2 lore raw

PATCH 2/4 OMAP: OneNAND: determine frequency in one place

In reply to Adrian Hunter

OneNAND frequency is determined when calculating GPMC timings. Return that value instead of determining it again in the OMAP OneNAND driver.

---
 arch/arm/mach-omap2/gpmc-onenand.c        |   10 ++++++----
 arch/arm/plat-omap/include/plat/onenand.h |    2 +-
 drivers/mtd/onenand/omap2.c               |   28 +++++-----------------------
 3 files changed, 12 insertions(+), 28 deletions(-)
arch/arm/mach-omap2/gpmc-onenand.c +6 -4
index 3a4307b..46786a6 100644
@@ -123,7 +123,7 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency,
 
 static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 					void __iomem *onenand_base,
-					int freq)
+					int *freq_ptr)
 {
 	struct gpmc_timings t;
 	const int t_cer  = 15;
@@ -136,7 +136,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 	int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;
 	int first_time = 0, hf = 0, vhf = 0, sync_read = 0, sync_write = 0;
 	int err, ticks_cez;
-	int cs = cfg->cs;
+	int cs = cfg->cs, freq = *freq_ptr;
 	u32 reg;
 
 	if (cfg->flags & ONENAND_SYNC_READ) {
@@ -330,16 +330,18 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 
 	set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf, vhf);
 
+	*freq_ptr = freq;
+
 	return 0;
 }
 
-static int gpmc_onenand_setup(void __iomem *onenand_base, int freq)
+static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr)
 {
 	struct device *dev = &gpmc_onenand_device.dev;
 
 	/* Set sync timings in GPMC */
 	if (omap2_onenand_set_sync_mode(gpmc_onenand_data, onenand_base,
-			freq) < 0) {
+			freq_ptr) < 0) {
 		dev_err(dev, "Unable to set synchronous mode\n");
 		return -EINVAL;
 	}
arch/arm/plat-omap/include/plat/onenand.h +1 -1
index affe87e..86118dc 100644
@@ -20,7 +20,7 @@ struct omap_onenand_platform_data {
 	int			gpio_irq;
 	struct mtd_partition	*parts;
 	int			nr_parts;
-	int                     (*onenand_setup)(void __iomem *, int freq);
+	int			(*onenand_setup)(void __iomem *, int *freq_ptr);
 	int			dma_channel;
 	u8			flags;
 	u8			regulator_can_sleep;
drivers/mtd/onenand/omap2.c +5 -23
index ac31f46..3e1bb95 100644
@@ -63,7 +63,7 @@ struct omap2_onenand {
 	struct completion dma_done;
 	int dma_channel;
 	int freq;
-	int (*setup)(void __iomem *base, int freq);
+	int (*setup)(void __iomem *base, int *freq_ptr);
 	struct regulator *regulator;
 };
 
@@ -581,7 +581,7 @@ static int __adjust_timing(struct device *dev, void *data)
 
 	/* DMA is not in use so this is all that is needed */
 	/* Revisit for OMAP3! */
-	ret = c->setup(c->onenand.base, c->freq);
+	ret = c->setup(c->onenand.base, &c->freq);
 
 	return ret;
 }
@@ -673,7 +673,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
 	}
 
 	if (pdata->onenand_setup != NULL) {
-		r = pdata->onenand_setup(c->onenand.base, c->freq);
+		r = pdata->onenand_setup(c->onenand.base, &c->freq);
 		if (r < 0) {
 			dev_err(&pdev->dev, "Onenand platform setup failed: "
 				"%d\n", r);
@@ -718,8 +718,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
 	}
 
 	dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual "
-		 "base %p\n", c->gpmc_cs, c->phys_base,
-		 c->onenand.base);
+		 "base %p, freq %d MHz\n", c->gpmc_cs, c->phys_base,
+		 c->onenand.base, c->freq);
 
 	c->pdev = pdev;
 	c->mtd.name = dev_name(&pdev->dev);
@@ -754,24 +754,6 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
 	if ((r = onenand_scan(&c->mtd, 1)) < 0)
 		goto err_release_regulator;
 
-	switch ((c->onenand.version_id >> 4) & 0xf) {
-	case 0:
-		c->freq = 40;
-		break;
-	case 1:
-		c->freq = 54;
-		break;
-	case 2:
-		c->freq = 66;
-		break;
-	case 3:
-		c->freq = 83;
-		break;
-	case 4:
-		c->freq = 104;
-		break;
-	}
-
 #ifdef CONFIG_MTD_PARTITIONS
 	r = parse_mtd_partitions(&c->mtd, part_probes, &c->parts, 0);
 	if (r > 0)
Signature · 1 line
1.7.0.4
#3 lore raw

PATCH 3/4 OMAP: OneNAND: let boards determine OneNAND frequency

In reply to Adrian Hunter

OneNAND version ID may not give the highest frequency supported and some OneNAND's have setup times that are clock dependent. Let the board provide that information.

---
 arch/arm/mach-omap2/gpmc-onenand.c        |   80 +++++++++++++++++++++--------
 arch/arm/plat-omap/include/plat/onenand.h |    8 +++
 2 files changed, 67 insertions(+), 21 deletions(-)
arch/arm/mach-omap2/gpmc-onenand.c +59 -21
index 46786a6..d776ded 100644
@@ -121,6 +121,47 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency,
 	writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
 }
 
+static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg,
+				  void __iomem *onenand_base, bool *clk_dep)
+{
+	u16 ver = readw(onenand_base + ONENAND_REG_VERSION_ID);
+	int freq = 0;
+
+	if (cfg->get_freq) {
+		struct onenand_freq_info fi;
+
+		fi.maf_id = readw(onenand_base + ONENAND_REG_MANUFACTURER_ID);
+		fi.dev_id = readw(onenand_base + ONENAND_REG_DEVICE_ID);
+		fi.ver_id = ver;
+		freq = cfg->get_freq(&fi, clk_dep);
+		if (freq)
+			return freq;
+	}
+
+	switch ((ver >> 4) & 0xf) {
+	case 0:
+		freq = 40;
+		break;
+	case 1:
+		freq = 54;
+		break;
+	case 2:
+		freq = 66;
+		break;
+	case 3:
+		freq = 83;
+		break;
+	case 4:
+		freq = 104;
+		break;
+	default:
+		freq = 54;
+		break;
+	}
+
+	return freq;
+}
+
 static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 					void __iomem *onenand_base,
 					int *freq_ptr)
@@ -138,6 +179,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 	int err, ticks_cez;
 	int cs = cfg->cs, freq = *freq_ptr;
 	u32 reg;
+	bool clk_dep = false;
 
 	if (cfg->flags & ONENAND_SYNC_READ) {
 		sync_read = 1;
@@ -152,27 +194,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 		err = omap2_onenand_set_async_mode(cs, onenand_base);
 		if (err)
 			return err;
-		reg = readw(onenand_base + ONENAND_REG_VERSION_ID);
-		switch ((reg >> 4) & 0xf) {
-		case 0:
-			freq = 40;
-			break;
-		case 1:
-			freq = 54;
-			break;
-		case 2:
-			freq = 66;
-			break;
-		case 3:
-			freq = 83;
-			break;
-		case 4:
-			freq = 104;
-			break;
-		default:
-			freq = 54;
-			break;
-		}
+		freq = omap2_onenand_get_freq(cfg, onenand_base, &clk_dep);
 		first_time = 1;
 	}
 
@@ -232,6 +254,22 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
 	else
 		latency = 4;
 
+	if (clk_dep) {
+		if (gpmc_clk_ns < 12) { /* >83Mhz */
+			t_ces   = 3;
+			t_avds  = 4;
+		} else if (gpmc_clk_ns < 15) { /* >66Mhz */
+			t_ces   = 5;
+			t_avds  = 4;
+		} else if (gpmc_clk_ns < 25) { /* >40Mhz */
+			t_ces   = 6;
+			t_avds  = 5;
+		} else {
+			t_ces   = 7;
+			t_avds  = 7;
+		}
+	}
+
 	if (first_time)
 		set_onenand_cfg(onenand_base, latency,
 					sync_read, sync_write, hf, vhf);
arch/arm/plat-omap/include/plat/onenand.h +8 -0
index 86118dc..cbe897c 100644
@@ -15,12 +15,20 @@
 #define ONENAND_SYNC_READ	(1 << 0)
 #define ONENAND_SYNC_READWRITE	(1 << 1)
 
+struct onenand_freq_info {
+	u16			maf_id;
+	u16			dev_id;
+	u16			ver_id;
+};
+
 struct omap_onenand_platform_data {
 	int			cs;
 	int			gpio_irq;
 	struct mtd_partition	*parts;
 	int			nr_parts;
 	int			(*onenand_setup)(void __iomem *, int *freq_ptr);
+	int		(*get_freq)(const struct onenand_freq_info *freq_info,
+				    bool *clk_dep);
 	int			dma_channel;
 	u8			flags;
 	u8			regulator_can_sleep;
Signature · 1 line
1.7.0.4
#4 lore raw

PATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout

In reply to Adrian Hunter

From: Roman Tereshonkov <[email protected]>

The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).

For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.

---
 drivers/mtd/onenand/omap2.c |    8 +++-----
 1 files changed, 3 insertions(+), 5 deletions(-)
drivers/mtd/onenand/omap2.c +3 -5
index 3e1bb95..ec26399 100644
@@ -148,11 +148,9 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
 			wait_err("controller error", state, ctrl, intr);
 			return -EIO;
 		}
-		if ((intr & intr_flags) != intr_flags) {
-			wait_err("timeout", state, ctrl, intr);
-			return -EIO;
-		}
-		return 0;
+		if ((intr & intr_flags) == intr_flags)
+			return 0;
+		/* Continue in wait for interrupt branch */
 	}
 
 	if (state != FL_READING) {
Signature · 1 line
1.7.0.4
maintainer
#5 lore raw

PATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout

Quoted 7 lines · Adrian Hunter

From: Roman Tereshonkov <[email protected]>

The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).

Quoted 5 lines

For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.

I'll queue this series. Anybody from MTD list care to ack this patch?

Regards,

Tony

Quoted 26 lines
---
 drivers/mtd/onenand/omap2.c |    8 +++-----
 1 files changed, 3 insertions(+), 5 deletions(-)
drivers/mtd/onenand/omap2.c +3 -5
index 3e1bb95..ec26399 100644
@@ -148,11 +148,9 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
 			wait_err("controller error", state, ctrl, intr);
 			return -EIO;
 		}
-		if ((intr & intr_flags) != intr_flags) {
-			wait_err("timeout", state, ctrl, intr);
-			return -EIO;
-		}
-		return 0;
+		if ((intr & intr_flags) == intr_flags)
+			return 0;
+		/* Continue in wait for interrupt branch */
 	}
 
 	if (state != FL_READING) {
Signature · 1 line
1.7.0.4
#6 lore raw

PATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout

Hi,

Quoted 8 lines · Tony Lindgren
Quoted 7 lines · Adrian Hunter

From: Roman Tereshonkov <[email protected]>

The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).

Quoted 7 lines
Quoted 5 lines

For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.

I'll queue this series. Anybody from MTD list care to ack this patch?

Signature · 2 lines
Best Regards,
Artem Bityutskiy (????? ????????)
maintainer
#7 lore raw

PATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout

Quoted 15 lines · Artem Bityutskiy

Hi,

Quoted 12 lines · Tony Lindgren
Quoted 11 lines · Adrian Hunter

From: Roman Tereshonkov <[email protected]>

The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).

For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

I'll queue this series. Anybody from MTD list care to ack this patch?

Thanks!

Tony