[PATCH v2 0/5] NAND: Multi-chip support for FSL-UPM for TQM8548 modules

STALE6386d

8 messages, 2 authors, 2009-03-24 · open the first message on its own page

[PATCH v2 0/5] NAND: Multi-chip support for FSL-UPM for TQM8548 modules

From: Wolfgang Grandegger <hidden>
Date: 2009-03-19 15:24:20

This is the 2nd version of the patch series adding generic support for
multi-chip NAND devices to the FSL-UPM driver and support for the
Micron MT29F8G08FAB NAND flash memory on the TQM8548 modules. It
addresses the issues reported on the mailing list, e.g. the new
bindings are now documented:

[PATCH v2 1/5] NAND: FSL-UPM: add multi chip support
[PATCH v2 2/5] NAND: FSL-UPM: add support for selecting chips via MAR
[PATCH v2 3/5] NAND: FSL-UPM: Add wait flags to support board/chip specific delays
[PATCH v2 4/5] powerpc: NAND: FSL UPM: document new bindings
[PATCH v2 5/5] powerpc/85xx: TQM8548: Update DTS file for multi-chip support

Please consider these patches for kernel inclusion.

Thanks.

Wolfgang.

[PATCH v2 5/5] powerpc/85xx: TQM8548: Update DTS file for multi-chip support

From: Wolfgang Grandegger <hidden>
Date: 2009-03-19 15:24:22

This patch adds multi-chip support for the Micron MT29F8G08FAB NAND
flash memory on the TQM8548 modules.

Signed-off-by: Wolfgang Grandegger <redacted>
---
 arch/powerpc/boot/dts/tqm8548-bigflash.dts |    7 ++++++-
 arch/powerpc/boot/dts/tqm8548.dts          |    7 ++++++-
 2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/boot/dts/tqm8548-bigflash.dts b/arch/powerpc/boot/dts/tqm8548-bigflash.dts
index 29a2b6f..2730467 100644
--- a/arch/powerpc/boot/dts/tqm8548-bigflash.dts
+++ b/arch/powerpc/boot/dts/tqm8548-bigflash.dts
@@ -389,6 +389,11 @@
 			reg = <3 0x0 0x800>;
 			fsl,upm-addr-offset = <0x10>;
 			fsl,upm-cmd-offset = <0x08>;
+			fsl,upm-wait-flags = <0x5>;
+			/* Multi-chip device */
+			fsl,upm-mar-chip-offset = <0x200>;
+			num-chips = <2>;
+			chip-offset = <0x200>;
 			chip-delay = <25>; // in micro-seconds
 
 			nand@0 {
@@ -397,7 +402,7 @@
 
 				partition@0 {
 					    label = "fs";
-					    reg = <0x00000000 0x01000000>;
+					    reg = <0x00000000 0x10000000>;
 				};
 			};
 		};
diff --git a/arch/powerpc/boot/dts/tqm8548.dts b/arch/powerpc/boot/dts/tqm8548.dts
index 81d3fbb..d7477fc 100644
--- a/arch/powerpc/boot/dts/tqm8548.dts
+++ b/arch/powerpc/boot/dts/tqm8548.dts
@@ -389,6 +389,11 @@
 			reg = <3 0x0 0x800>;
 			fsl,upm-addr-offset = <0x10>;
 			fsl,upm-cmd-offset = <0x08>;
+			fsl,upm-wait-flags = <0x5>;
+			/* Multi-chip device */
+			fsl,upm-mar-chip-offset = <0x200>;
+			num-chips = <2>;
+			chip-offset = <0x200>;
 			chip-delay = <25>; // in micro-seconds
 
 			nand@0 {
@@ -397,7 +402,7 @@
 
 				partition@0 {
 					    label = "fs";
-					    reg = <0x00000000 0x01000000>;
+					    reg = <0x00000000 0x10000000>;
 				};
 			};
 		};
-- 
1.6.0.6

[PATCH v2 4/5] powerpc: NAND: FSL UPM: document new bindings

From: Wolfgang Grandegger <hidden>
Date: 2009-03-19 15:24:22

This patch adds documentation for the new NAND FSL UPM bindings for:

 NAND: FSL-UPM: add multi chip support
 NAND: FSL-UPM: Add wait flags to support board/chip specific delays
 NAND: FSL-UPM: add support for selecting chips via MAR

Signed-off-by: Wolfgang Grandegger <redacted>
---
 .../powerpc/dts-bindings/fsl/upm-nand.txt          |   42 +++++++++++++++++++-
 1 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/Documentation/powerpc/dts-bindings/fsl/upm-nand.txt b/Documentation/powerpc/dts-bindings/fsl/upm-nand.txt
index 84a04d5..3919828 100644
--- a/Documentation/powerpc/dts-bindings/fsl/upm-nand.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/upm-nand.txt
@@ -5,9 +5,24 @@ Required properties:
 - reg : should specify localbus chip select and size used for the chip.
 - fsl,upm-addr-offset : UPM pattern offset for the address latch.
 - fsl,upm-cmd-offset : UPM pattern offset for the command latch.
-- gpios : may specify optional GPIO connected to the Ready-Not-Busy pin.
 
-Example:
+Optional properties:
+- fsl,upm-mar-offset : use the UPM machine address register to drive a
+  		       custom chip select logic using the specified
+		       offset.
+- fsl,upm-wait-flags : add chip-dependent short delays after running the
+  		       UPM pattern (0x1), after writing a data byte (0x2)
+		       or after writing out a buffer (0x4).
+- gpios : may specify optional GPIOs connected to the Ready-Not-Busy pins
+	  (R/B#). For multi-chip devices, "num-chips" GPIO definitions are
+	  required.
+- chip-delay : chip dependent delay for transfering data from array to
+	       read registers (tR). Required if property "gpios" is not
+	       used (R/B# pins not connected).
+- num-chips : number of chips per device for multi-chip support.
+- chip-offset : address offset between chips for multi-chip support.
+
+Examples:
 
 upm@1,0 {
 	compatible = "fsl,upm-nand";
@@ -26,3 +41,26 @@ upm@1,0 {
 		};
 	};
 };
+
+upm@3,0 {
+	compatible = "fsl,upm-nand";
+	reg = <3 0x0 0x800>;
+	fsl,upm-addr-offset = <0x10>;
+	fsl,upm-cmd-offset = <0x08>;
+	fsl,upm-wait-flags = <0x5>;
+	/* Multi-chip device */
+	fsl,upm-mar-chip-offset = <0x200>;
+	num-chips = <2>;
+	chip-offset = <0x200>;
+	chip-delay = <25>; // in micro-seconds
+
+	nand@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		partition@0 {
+			    label = "fs";
+			    reg = <0x00000000 0x10000000>;
+		};
+	};
+};
-- 
1.6.0.6

[PATCH v2 2/5] NAND: FSL-UPM: add support for selecting chips via MAR

From: Wolfgang Grandegger <hidden>
Date: 2009-03-19 15:24:34

For the NAND chips on the TQM8548 modules, a special chip-select logic is
used. It uses dedicated address lines to be set via UPM machine address
register (mar). This patch also adds that support to the FSL-UPM driver.

Signed-off-by: Wolfgang Grandegger <redacted>
---
 arch/powerpc/sysdev/fsl_lbc.c |    2 +-
 drivers/mtd/nand/fsl_upm.c    |   13 ++++++++++++-
 2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
index 0494ee5..dceb8d1 100644
--- a/arch/powerpc/sysdev/fsl_lbc.c
+++ b/arch/powerpc/sysdev/fsl_lbc.c
@@ -150,7 +150,7 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
 
 	spin_lock_irqsave(&fsl_lbc_lock, flags);
 
-	out_be32(&fsl_lbc_regs->mar, mar << (32 - upm->width));
+	out_be32(&fsl_lbc_regs->mar, mar);
 
 	switch (upm->width) {
 	case 8:
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index 4b2c19f..d637da7 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -35,6 +35,7 @@ struct fsl_upm_nand {
 	struct fsl_upm upm;
 	uint8_t upm_addr_offset;
 	uint8_t upm_cmd_offset;
+	uint32_t upm_mar_chip_offset;
 	void __iomem *io_base;
 	int rnb_gpio[NAND_MAX_CHIPS];
 	int chip_delay;
@@ -72,7 +73,9 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun)
 
 static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
+	struct nand_chip *chip = mtd->priv;
 	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+	u32 mar;
 
 	if (!(ctrl & fun->last_ctrl)) {
 		fsl_upm_end_pattern(&fun->upm);
@@ -90,7 +93,11 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 			fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
 	}
 
-	fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd);
+	mar = cmd << (32 - fun->upm.width);
+	if (fun->upm_mar_chip_offset && fun->chip_number > 0) {
+		mar |= fun->chip_number * fun->upm_mar_chip_offset;
+	}
+	fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar);
 
 	fun_wait_rnb(fun);
 }
@@ -242,6 +249,10 @@ static int __devinit fun_probe(struct of_device *ofdev,
 	}
 	fun->upm_cmd_offset = *prop;
 
+	prop = of_get_property(ofdev->node, "fsl,upm-mar-chip-offset", &size);
+	if (prop && size == sizeof(uint32_t))
+		fun->upm_mar_chip_offset = *prop;
+
 	prop = of_get_property(ofdev->node, "num-chips", &size);
 	if (prop && size == sizeof(uint32_t)) {
 		fun->num_chips = *prop;
-- 
1.6.0.6

[PATCH v2 1/5] NAND: FSL-UPM: add multi chip support

From: Wolfgang Grandegger <hidden>
Date: 2009-03-19 15:24:37

This patch adds support for multi-chip NAND devices to the FSL-UPM
driver. This requires support for multiple GPIOs for the RNB pins.

Signed-off-by: Wolfgang Grandegger <redacted>
---
 drivers/mtd/nand/fsl_upm.c |   88 +++++++++++++++++++++++++++++++++----------
 1 files changed, 67 insertions(+), 21 deletions(-)
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index 7815a40..4b2c19f 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -36,8 +36,11 @@ struct fsl_upm_nand {
 	uint8_t upm_addr_offset;
 	uint8_t upm_cmd_offset;
 	void __iomem *io_base;
-	int rnb_gpio;
+	int rnb_gpio[NAND_MAX_CHIPS];
 	int chip_delay;
+	uint32_t num_chips;
+	uint32_t chip_number;
+	uint32_t chip_offset;
 };
 
 #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
@@ -46,7 +49,7 @@ static int fun_chip_ready(struct mtd_info *mtd)
 {
 	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
 
-	if (gpio_get_value(fun->rnb_gpio))
+	if (gpio_get_value(fun->rnb_gpio[fun->chip_number]))
 		return 1;
 
 	dev_vdbg(fun->dev, "busy\n");
@@ -55,9 +58,9 @@ static int fun_chip_ready(struct mtd_info *mtd)
 
 static void fun_wait_rnb(struct fsl_upm_nand *fun)
 {
-	int cnt = 1000000;
 
-	if (fun->rnb_gpio >= 0) {
+	if (fun->rnb_gpio[fun->chip_number] >= 0) {
+		int cnt = 1000000;
 		while (--cnt && !fun_chip_ready(&fun->mtd))
 			cpu_relax();
 		if (!cnt)
@@ -92,6 +95,22 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 	fun_wait_rnb(fun);
 }
 
+static void fun_select_chip(struct mtd_info *mtd, int chip_nr)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+
+	if (chip_nr == -1) {
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+	} else if (chip_nr >= 0) {
+		fun->chip_number = chip_nr;
+		chip->IO_ADDR_R = chip->IO_ADDR_W =
+			fun->io_base + chip_nr * fun->chip_offset;
+	} else {
+		BUG();
+	}
+}
+
 static uint8_t fun_read_byte(struct mtd_info *mtd)
 {
 	struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
@@ -137,8 +156,10 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
 	fun->chip.read_buf = fun_read_buf;
 	fun->chip.write_buf = fun_write_buf;
 	fun->chip.ecc.mode = NAND_ECC_SOFT;
+	if (fun->num_chips > 1)
+		fun->chip.select_chip = fun_select_chip;
 
-	if (fun->rnb_gpio >= 0)
+	if (fun->rnb_gpio[0] >= 0)
 		fun->chip.dev_ready = fun_chip_ready;
 
 	fun->mtd.priv = &fun->chip;
@@ -155,7 +176,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
 		goto err;
 	}
 
-	ret = nand_scan(&fun->mtd, 1);
+	ret = nand_scan(&fun->mtd, fun->num_chips);
 	if (ret)
 		goto err;
 
@@ -187,6 +208,7 @@ static int __devinit fun_probe(struct of_device *ofdev,
 	const uint32_t *prop;
 	int ret;
 	int size;
+	int i;
 
 	fun = kzalloc(sizeof(*fun), GFP_KERNEL);
 	if (!fun)
@@ -208,7 +230,7 @@ static int __devinit fun_probe(struct of_device *ofdev,
 	if (!prop || size != sizeof(uint32_t)) {
 		dev_err(&ofdev->dev, "can't get UPM address offset\n");
 		ret = -EINVAL;
-		goto err2;
+		goto err1;
 	}
 	fun->upm_addr_offset = *prop;
 
@@ -216,21 +238,36 @@ static int __devinit fun_probe(struct of_device *ofdev,
 	if (!prop || size != sizeof(uint32_t)) {
 		dev_err(&ofdev->dev, "can't get UPM command offset\n");
 		ret = -EINVAL;
-		goto err2;
+		goto err1;
 	}
 	fun->upm_cmd_offset = *prop;
 
-	fun->rnb_gpio = of_get_gpio(ofdev->node, 0);
-	if (fun->rnb_gpio >= 0) {
-		ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev));
-		if (ret) {
-			dev_err(&ofdev->dev, "can't request RNB gpio\n");
+	prop = of_get_property(ofdev->node, "num-chips", &size);
+	if (prop && size == sizeof(uint32_t)) {
+		fun->num_chips = *prop;
+		if (fun->num_chips >= NAND_MAX_CHIPS) {
+			dev_err(&ofdev->dev, "too much chips");
+			ret = -EINVAL;
+			goto err1;
+		}
+	} else {
+		fun->num_chips = 1;
+	}
+
+	for (i = 0; i < fun->num_chips; i++) {
+		fun->rnb_gpio[i] = of_get_gpio(ofdev->node, i);
+		if (fun->rnb_gpio[i] >= 0) {
+			ret = gpio_request(fun->rnb_gpio[i], 
+					   dev_name(&ofdev->dev));
+			if (ret) {
+				dev_err(&ofdev->dev, "can't request RNB gpio\n");
+				goto err2;
+			}
+			gpio_direction_input(fun->rnb_gpio[i]);
+		} else if (fun->rnb_gpio[i]  == -EINVAL) {
+			dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
 			goto err2;
 		}
-		gpio_direction_input(fun->rnb_gpio);
-	} else if (fun->rnb_gpio == -EINVAL) {
-		dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
-		goto err2;
 	}
 
 	prop = of_get_property(ofdev->node, "chip-delay", NULL);
@@ -239,6 +276,10 @@ static int __devinit fun_probe(struct of_device *ofdev,
 	else
 		fun->chip_delay = 50;
 
+	prop = of_get_property(ofdev->node, "chip-offset", &size);
+	if (prop && size == sizeof(uint32_t))
+		fun->chip_offset = *prop;
+
 	fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
 					  io_res.end - io_res.start + 1);
 	if (!fun->io_base) {
@@ -257,8 +298,10 @@ static int __devinit fun_probe(struct of_device *ofdev,
 
 	return 0;
 err2:
-	if (fun->rnb_gpio >= 0)
-		gpio_free(fun->rnb_gpio);
+	for (i = 0; i < fun->num_chips; i++) {
+		if (fun->rnb_gpio[i] >= 0)
+			gpio_free(fun->rnb_gpio[i]);
+	}
 err1:
 	kfree(fun);
 
@@ -268,12 +311,15 @@ err1:
 static int __devexit fun_remove(struct of_device *ofdev)
 {
 	struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
+	int i;
 
 	nand_release(&fun->mtd);
 	kfree(fun->mtd.name);
 
-	if (fun->rnb_gpio >= 0)
-		gpio_free(fun->rnb_gpio);
+        for (i = 0; i < fun->num_chips; i++) {
+                if (fun->rnb_gpio[i] >= 0)
+                        gpio_free(fun->rnb_gpio[i]);
+        }
 
 	kfree(fun);
 
-- 
1.6.0.6

[PATCH v2 3/5] NAND: FSL-UPM: Add wait flags to support board/chip specific delays

From: Wolfgang Grandegger <hidden>
Date: 2009-03-19 15:24:37

The NAND flash on the TQM8548_BE modules requires a short delay after
running the UPM pattern. The TQM8548_BE requires a further short delay
after writing out a buffer. Normally the R/B pin should be checked, but
it's not connected on the TQM8548_BE. The existing driver uses similar
fixed delay points. To manage these extra delays in a more general way,
I introduced the "wait-flags" property allowing the board-specific driver
to specify various types of extra delay.

Signed-off-by: Wolfgang Grandegger <redacted>
---
 drivers/mtd/nand/fsl_upm.c |   20 ++++++++++++++++++--
 1 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index d637da7..bc878fb 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -23,6 +23,10 @@
 #include <linux/io.h>
 #include <asm/fsl_lbc.h>
 
+#define FSL_UPM_WAIT_RUN_PATTERN  0x1
+#define FSL_UPM_WAIT_WRITE_BYTE   0x2
+#define FSL_UPM_WAIT_WRITE_BUFFER 0x4
+
 struct fsl_upm_nand {
 	struct device *dev;
 	struct mtd_info mtd;
@@ -42,6 +46,7 @@ struct fsl_upm_nand {
 	uint32_t num_chips;
 	uint32_t chip_number;
 	uint32_t chip_offset;
+	uint32_t wait_flags;
 };
 
 #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
@@ -99,7 +104,8 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 	}
 	fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar);
 
-	fun_wait_rnb(fun);
+	if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
+		fun_wait_rnb(fun);
 }
 
 static void fun_select_chip(struct mtd_info *mtd, int chip_nr)
@@ -141,8 +147,11 @@ static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 
 	for (i = 0; i < len; i++) {
 		out_8(fun->chip.IO_ADDR_W, buf[i]);
-		fun_wait_rnb(fun);
+		if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
+			fun_wait_rnb(fun);
 	}
+	if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
+		fun_wait_rnb(fun);
 }
 
 static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
@@ -291,6 +300,13 @@ static int __devinit fun_probe(struct of_device *ofdev,
 	if (prop && size == sizeof(uint32_t))
 		fun->chip_offset = *prop;
 
+	prop = of_get_property(ofdev->node, "fsl,upm-wait-flags", &size);
+	if (prop && size == sizeof(uint32_t))
+		fun->wait_flags = *prop;
+	else
+		fun->wait_flags =
+			FSL_UPM_WAIT_RUN_PATTERN | FSL_UPM_WAIT_WRITE_BYTE;
+
 	fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
 					  io_res.end - io_res.start + 1);
 	if (!fun->io_base) {
-- 
1.6.0.6

Re: [PATCH v2 4/5] powerpc: NAND: FSL UPM: document new bindings

From: Scott Wood <hidden>
Date: 2009-03-19 17:05:35

On Thu, Mar 19, 2009 at 04:16:07PM +0100, Wolfgang Grandegger wrote:
+Optional properties:
+- fsl,upm-mar-offset : use the UPM machine address register to drive a
+  		       custom chip select logic using the specified
+		       offset.
Your example uses the name fsl,upm-mar-chip-offset instead.
+- chip-offset : address offset between chips for multi-chip support.
How is this different from fsl,upm-mar-offset?

-Scott

Re: [PATCH v2 4/5] powerpc: NAND: FSL UPM: document new bindings

From: Wolfgang Grandegger <hidden>
Date: 2009-03-24 18:59:08

Scott Wood wrote:
On Thu, Mar 19, 2009 at 04:16:07PM +0100, Wolfgang Grandegger wrote:
quoted
+Optional properties:
+- fsl,upm-mar-offset : use the UPM machine address register to drive a
+  		       custom chip select logic using the specified
+		       offset.
Your example uses the name fsl,upm-mar-chip-offset instead.
I need to fix the documentation then.
quoted
+- chip-offset : address offset between chips for multi-chip support.
How is this different from fsl,upm-mar-offset?
Well, after a closer look, it's the same, hardware-wise. The offset sets
the corresponding address lines, which are used to select the chip. As
it is currently, "chip-offset" is used for:

 chip->IO_ADDR_R = chip->IO_ADDR_W =
                        fun->upm.io_addr + fun->chip_offset * chip_nr;

and "upm-mar-chip-offset" to set the address lines through the MAR
register when running the command or address patterns:

  mar += fun->upm_mar_chip_offset * fun->chip_nr;

I'm going to remove "upm_mar_chip_offset" and use "chip_offset" instead,
also for the U-Boot version. Thanks for pointing that out.

Wolfgang.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help