[MTD] Refactor NAND hwcontrol to cmd_ctrl

The hwcontrol function enforced a step by step state machine
for any kind of hardware chip access. Let the hardware driver
know which control bits are set and inform it about a change
of the control lines. Let the hardware driver write out the
command and address bytes directly. This gives a peformance
advantage for address bus controlled chips and simplifies the
quirks in the hardware drivers.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index aeaf2de..c0e9686 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -34,13 +34,6 @@
 
 #define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP)
 
-#define T_NAND_CTL_CLRALE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_ALE, 0)
-#define T_NAND_CTL_SETALE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_ALE, AMS_DELTA_LATCH2_NAND_ALE)
-#define T_NAND_CTL_CLRCLE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE, 0)
-#define T_NAND_CTL_SETCLE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE, AMS_DELTA_LATCH2_NAND_CLE)
-#define T_NAND_CTL_SETNCE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NCE, 0)
-#define T_NAND_CTL_CLRNCE(iob) ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NCE, AMS_DELTA_LATCH2_NAND_NCE)
-
 /*
  * Define partitions for flash devices
  */
@@ -66,25 +59,6 @@
 	  .size		=  3 * SZ_256K },
 };
 
-/*
- *	hardware specific access to control-lines
-*/
-
-static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd)
-{
-	switch (cmd) {
-
-		case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
-		case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
-
-		case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
-		case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
-
-		case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
-		case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
-	}
-}
-
 static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
 {
 	struct nand_chip *this = mtd->priv;
@@ -141,6 +115,32 @@
 	return 0;
 }
 
+/*
+ * Command control function
+ *
+ * ctrl:
+ * NAND_NCE: bit 0 -> bit 2
+ * NAND_CLE: bit 1 -> bit 7
+ * NAND_ALE: bit 2 -> bit 6
+ */
+static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
+				unsigned int ctrl)
+{
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned long bits;
+
+		bits = (~ctrl & NAND_NCE) << 2;
+		bits |= (ctrl & NAND_CLE) << 7;
+		bits |= (ctrl & NAND_ALE) << 6;
+
+		ams_delta_latch2_write(0xC2, bits);
+	}
+
+	if (cmd != NAND_CMD_NONE)
+		ams_delta_write_byte(mtd, cmd);
+}
+
 static int ams_delta_nand_ready(struct mtd_info *mtd)
 {
 	return omap_get_gpio_datain(AMS_DELTA_GPIO_PIN_NAND_RB);
@@ -183,7 +183,7 @@
 	this->write_buf = ams_delta_write_buf;
 	this->read_buf = ams_delta_read_buf;
 	this->verify_buf = ams_delta_verify_buf;
-	this->hwcontrol = ams_delta_hwcontrol;
+	this->cmd_ctrl = ams_delta_hwcontrol;
 	if (!omap_request_gpio(AMS_DELTA_GPIO_PIN_NAND_RB)) {
 		this->dev_ready = ams_delta_nand_ready;
 	} else {
@@ -200,7 +200,7 @@
 					  AMS_DELTA_LATCH2_NAND_NCE |
 					  AMS_DELTA_LATCH2_NAND_NWP);
 
-        /* Scan to find existance of the device */
+	/* Scan to find existance of the device */
 	if (nand_scan(ams_delta_mtd, 1)) {
 		err = -ENXIO;
 		goto out_mtd;
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 29dde7d..275453e 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -269,6 +269,18 @@
 	return 0;
 }
 
+/* Select the chip by setting nCE to low */
+#define NAND_CTL_SETNCE		1
+/* Deselect the chip by setting nCE to high */
+#define NAND_CTL_CLRNCE		2
+/* Select the command latch by setting CLE to high */
+#define NAND_CTL_SETCLE		3
+/* Deselect the command latch by setting CLE to low */
+#define NAND_CTL_CLRCLE		4
+/* Select the address latch by setting ALE to high */
+#define NAND_CTL_SETALE		5
+/* Deselect the address latch by setting ALE to low */
+#define NAND_CTL_CLRALE		6
 
 static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
 {
@@ -349,7 +361,7 @@
 	ulong flags;
 
 	/* Begin command latch cycle */
-	this->hwcontrol(mtd, NAND_CTL_SETCLE);
+	au1550_hwcontrol(mtd, NAND_CTL_SETCLE);
 	/*
 	 * Write out the command to the device.
 	 */
@@ -372,10 +384,10 @@
 	this->write_byte(mtd, command);
 
 	/* Set ALE and clear CLE to start address cycle */
-	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+	au1550_hwcontrol(mtd, NAND_CTL_CLRCLE);
 
 	if (column != -1 || page_addr != -1) {
-		this->hwcontrol(mtd, NAND_CTL_SETALE);
+		au1550_hwcontrol(mtd, NAND_CTL_SETALE);
 
 		/* Serially input address */
 		if (column != -1) {
@@ -400,7 +412,7 @@
 				 */
 				ce_override = 1;
 				local_irq_save(flags);
-				this->hwcontrol(mtd, NAND_CTL_SETNCE);
+				au1550_hwcontrol(mtd, NAND_CTL_SETNCE);
 			}
 
 			this->write_byte(mtd, (u8)(page_addr >> 8));
@@ -410,7 +422,7 @@
 				this->write_byte(mtd, (u8)((page_addr >> 16) & 0x0f));
 		}
 		/* Latch in address */
-		this->hwcontrol(mtd, NAND_CTL_CLRALE);
+		au1550_hwcontrol(mtd, NAND_CTL_CLRALE);
 	}
 
 	/*
@@ -443,7 +455,7 @@
 			udelay(1);
 
 		/* Release -CE and re-enable interrupts. */
-		this->hwcontrol(mtd, NAND_CTL_CLRNCE);
+		au1550_hwcontrol(mtd, NAND_CTL_CLRNCE);
 		local_irq_restore(flags);
 		return;
 	}
@@ -571,7 +583,6 @@
 		nand_width = au_readl(MEM_STCFG3) & (1 << 22);
 
 	/* Set address of hardware control function */
-	this->hwcontrol = au1550_hwcontrol;
 	this->dev_ready = au1550_device_ready;
 	this->select_chip = au1550_select_chip;
 	this->cmdfunc = au1550_command;
diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c
index dbb1b626..fe94ae9a 100644
--- a/drivers/mtd/nand/autcpu12.c
+++ b/drivers/mtd/nand/autcpu12.c
@@ -4,7 +4,7 @@
  *  Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
  *
  *  Derived from drivers/mtd/spia.c
- * 	 Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *	 Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
  * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $
  *
@@ -42,11 +42,6 @@
  * MTD structure for AUTCPU12 board
  */
 static struct mtd_info *autcpu12_mtd = NULL;
-
-static int autcpu12_io_base = CS89712_VIRT_BASE;
-static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC;
-static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET;
-static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET;
 static void __iomem *autcpu12_fio_base;
 
 /*
@@ -94,31 +89,42 @@
 #define NUM_PARTITIONS128K 2
 /*
  *	hardware specific access to control-lines
-*/
-
-static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
+ *
+ *	ALE bit 4 autcpu12_pedr
+ *	CLE bit 5 autcpu12_pedr
+ *	NCE bit 0 fio_ctrl
+ *
+ */
+static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd,
+			       unsigned int ctrl)
 {
-	switch (cmd) {
+	struct nand_chip *chip = mtd->priv;
 
-		case NAND_CTL_SETCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |=  AUTCPU12_SMC_CLE; break;
-		case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_CLE; break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		void __iomem *addr
+		unsigned char bits;
 
-		case NAND_CTL_SETALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |=  AUTCPU12_SMC_ALE; break;
-		case NAND_CTL_CLRALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_ALE; break;
+		addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET;
+		bits = (ctrl & NAND_CLE) << 4;
+		bits |= (ctrl & NAND_ALE) << 2;
+		writeb((readb(addr) & ~0x30) | bits, addr);
 
-		case NAND_CTL_SETNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x01; break;
-		case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x00; break;
+		addr = autcpu12_fio_base + AUTCPU12_SMC_SELECT_OFFSET;
+		writeb((readb(addr) & ~0x1) | (ctrl & NAND_NCE), addr);
 	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
 /*
-*	read device ready pin
-*/
+ *	read device ready pin
+ */
 int autcpu12_device_ready(struct mtd_info *mtd)
 {
+	void __iomem *addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET;
 
-	return ((*(volatile unsigned char *)(autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
-
+	return readb(addr) & AUTCPU12_SMC_RDY;
 }
 
 /*
@@ -130,7 +136,8 @@
 	int err = 0;
 
 	/* Allocate memory for MTD device structure and private data */
-	autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
+	autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
+			       GFP_KERNEL);
 	if (!autcpu12_mtd) {
 		printk("Unable to allocate AUTCPU12 NAND MTD device structure.\n");
 		err = -ENOMEM;
@@ -138,7 +145,7 @@
 	}
 
 	/* map physical adress */
-	autcpu12_fio_base = ioremap(autcpu12_fio_pbase, SZ_1K);
+	autcpu12_fio_base = ioremap(AUTCPU12_PHYS_SMC, SZ_1K);
 	if (!autcpu12_fio_base) {
 		printk("Ioremap autcpu12 SmartMedia Card failed\n");
 		err = -EIO;
@@ -159,7 +166,7 @@
 	/* Set address of NAND IO lines */
 	this->IO_ADDR_R = autcpu12_fio_base;
 	this->IO_ADDR_W = autcpu12_fio_base;
-	this->hwcontrol = autcpu12_hwcontrol;
+	this->cmd_ctrl = autcpu12_hwcontrol;
 	this->dev_ready = autcpu12_device_ready;
 	/* 20 us command delay time */
 	this->chip_delay = 20;
@@ -179,10 +186,22 @@
 
 	/* Register the partitions */
 	switch (autcpu12_mtd->size) {
-		case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
-		case SZ_32M: add_mtd_partitions(autcpu12_mtd, partition_info32k, NUM_PARTITIONS32K); break;
-		case SZ_64M: add_mtd_partitions(autcpu12_mtd, partition_info64k, NUM_PARTITIONS64K); break;
-		case SZ_128M: add_mtd_partitions(autcpu12_mtd, partition_info128k, NUM_PARTITIONS128K); break;
+		case SZ_16M:
+			add_mtd_partitions(autcpu12_mtd, partition_info16k,
+					   NUM_PARTITIONS16K);
+			break;
+		case SZ_32M:
+			add_mtd_partitions(autcpu12_mtd, partition_info32k,
+					   NUM_PARTITIONS32K);
+			break;
+		case SZ_64M:
+			add_mtd_partitions(autcpu12_mtd, partition_info64k,
+					   NUM_PARTITIONS64K);
+			break;
+		case SZ_128M:
+			add_mtd_partitions(autcpu12_mtd, partition_info128k,
+					   NUM_PARTITIONS128K);
+			break;
 		default:
 			printk("Unsupported SmartMedia device\n");
 			err = -ENXIO;
@@ -191,7 +210,7 @@
 	goto out;
 
  out_ior:
-	iounmap((void *)autcpu12_fio_base);
+	iounmap(autcpu12_fio_base);
  out_mtd:
 	kfree(autcpu12_mtd);
  out:
@@ -209,7 +228,7 @@
 	nand_release(autcpu12_mtd);
 
 	/* unmap physical adress */
-	iounmap((void *)autcpu12_fio_base);
+	iounmap(autcpu12_fio_base);
 
 	/* Free the MTD device structure */
 	kfree(autcpu12_mtd);
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index 064f3fe..cd3d7eb 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -131,33 +131,17 @@
 	writeb(byte, this->IO_ADDR_W + 0x801);
 }
 
-static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd)
+static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd,
+			     unsigned int ctrl)
 {
 	struct nand_chip *this = mtd->priv;
 	void __iomem *mmio_base = this->IO_ADDR_R;
-	unsigned char ctl;
-
-	switch (cmd) {
-	case NAND_CTL_SETCLE:
-		ctl = CS_NAND_CTL_CLE;
-		break;
-
-	case NAND_CTL_CLRCLE:
-	case NAND_CTL_CLRALE:
-	case NAND_CTL_SETNCE:
-		ctl = 0;
-		break;
-
-	case NAND_CTL_SETALE:
-		ctl = CS_NAND_CTL_ALE;
-		break;
-
-	default:
-	case NAND_CTL_CLRNCE:
-		ctl = CS_NAND_CTL_CE;
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
+		writeb(ctl, mmio_base + MM_NAND_CTL);
 	}
-	writeb(ctl, mmio_base + MM_NAND_CTL);
+	if (cmd != NAND_CMD_NONE)
+		cs553x_write_byte(mtd, cmd);
 }
 
 static int cs553x_device_ready(struct mtd_info *mtd)
@@ -233,7 +217,7 @@
 		goto out_mtd;
 	}
 
-	this->hwcontrol = cs553x_hwcontrol;
+	this->cmd_ctrl = cs553x_hwcontrol;
 	this->dev_ready = cs553x_device_ready;
 	this->read_byte = cs553x_read_byte;
 	this->write_byte = cs553x_write_byte;
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index f77298f..e4bb6b4 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -95,7 +95,8 @@
 #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
 #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
 
-static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
+			      unsigned int bitmask);
 static void doc200x_select_chip(struct mtd_info *mtd, int chip);
 
 static int debug = 0;
@@ -402,12 +403,10 @@
 	uint16_t ret;
 
 	doc200x_select_chip(mtd, nr);
-	doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
-	this->write_byte(mtd, NAND_CMD_READID);
-	doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
-	doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
-	this->write_byte(mtd, 0);
-	doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+	doc200x_hwcontrol(mtd, NAND_CMD_READID,
+			  NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+	doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+	doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 	/* We cant' use dev_ready here, but at least we wait for the
 	 * command to complete
@@ -425,12 +424,11 @@
 		} ident;
 		void __iomem *docptr = doc->virtadr;
 
-		doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
-		doc2000_write_byte(mtd, NAND_CMD_READID);
-		doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
-		doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
-		doc2000_write_byte(mtd, 0);
-		doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+		doc200x_hwcontrol(mtd, NAND_CMD_READID,
+				  NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+		doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+		doc200x_hwcontrol(mtd, NAND_CMD_NONE,
+				  NAND_NCE | NAND_CTRL_CHANGE);
 
 		udelay(50);
 
@@ -690,54 +688,37 @@
 	chip -= (floor * doc->chips_per_floor);
 
 	/* 11.4.4 -- deassert CE before changing chip */
-	doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+	doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
 
 	WriteDOC(floor, docptr, FloorSelect);
 	WriteDOC(chip, docptr, CDSNDeviceSelect);
 
-	doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
+	doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 	doc->curchip = chip;
 	doc->curfloor = floor;
 }
 
-static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
+#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE)
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
+			      unsigned int ctrl)
 {
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	void __iomem *docptr = doc->virtadr;
 
-	switch (cmd) {
-	case NAND_CTL_SETNCE:
-		doc->CDSNControl |= CDSN_CTRL_CE;
-		break;
-	case NAND_CTL_CLRNCE:
-		doc->CDSNControl &= ~CDSN_CTRL_CE;
-		break;
-	case NAND_CTL_SETCLE:
-		doc->CDSNControl |= CDSN_CTRL_CLE;
-		break;
-	case NAND_CTL_CLRCLE:
-		doc->CDSNControl &= ~CDSN_CTRL_CLE;
-		break;
-	case NAND_CTL_SETALE:
-		doc->CDSNControl |= CDSN_CTRL_ALE;
-		break;
-	case NAND_CTL_CLRALE:
-		doc->CDSNControl &= ~CDSN_CTRL_ALE;
-		break;
-	case NAND_CTL_SETWP:
-		doc->CDSNControl |= CDSN_CTRL_WP;
-		break;
-	case NAND_CTL_CLRWP:
-		doc->CDSNControl &= ~CDSN_CTRL_WP;
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		doc->CDSNControl &= ~CDSN_CTRL_MSK;
+		doc->CDSNControl |= ctrl & CDSN_CTRL_MSK;
+		if (debug)
+			printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
+		WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+		/* 11.4.3 -- 4 NOPs after CSDNControl write */
+		DoC_Delay(doc, 4);
 	}
-	if (debug)
-		printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
-	WriteDOC(doc->CDSNControl, docptr, CDSNControl);
-	/* 11.4.3 -- 4 NOPs after CSDNControl write */
-	DoC_Delay(doc, 4);
+	if (cmd != NAND_CMD_NONE)
+		this->write_byte(mtd, cmd);
 }
 
 static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
@@ -1510,7 +1491,7 @@
 	this->read_buf = doc2001plus_readbuf;
 	this->verify_buf = doc2001plus_verifybuf;
 	this->scan_bbt = inftl_scan_bbt;
-	this->hwcontrol = NULL;
+	this->cmd_ctrl = NULL;
 	this->select_chip = doc2001plus_select_chip;
 	this->cmdfunc = doc2001plus_command;
 	this->ecc.hwctl = doc2001plus_enable_hwecc;
@@ -1670,7 +1651,7 @@
 
 	nand->priv		= doc;
 	nand->select_chip	= doc200x_select_chip;
-	nand->hwcontrol		= doc200x_hwcontrol;
+	nand->cmd_ctrl		= doc200x_hwcontrol;
 	nand->dev_ready		= doc200x_dev_ready;
 	nand->waitfunc		= doc200x_wait;
 	nand->block_bad		= doc200x_block_bad;
diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c
index 8e56570..ba5a217 100644
--- a/drivers/mtd/nand/edb7312.c
+++ b/drivers/mtd/nand/edb7312.c
@@ -73,32 +73,26 @@
 
 /*
  *	hardware specific access to control-lines
+ *
+ *	NAND_NCE: bit 0 -> bit 7
+ *	NAND_CLE: bit 1 -> bit 4
+ *	NAND_ALE: bit 2 -> bit 5
  */
-static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
+static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
-	switch (cmd) {
+	struct nand_chip *chip = mtd->priv;
 
-	case NAND_CTL_SETCLE:
-		clps_writeb(clps_readb(ep7312_pxdr) | 0x10, ep7312_pxdr);
-		break;
-	case NAND_CTL_CLRCLE:
-		clps_writeb(clps_readb(ep7312_pxdr) & ~0x10, ep7312_pxdr);
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char bits;
 
-	case NAND_CTL_SETALE:
-		clps_writeb(clps_readb(ep7312_pxdr) | 0x20, ep7312_pxdr);
-		break;
-	case NAND_CTL_CLRALE:
-		clps_writeb(clps_readb(ep7312_pxdr) & ~0x20, ep7312_pxdr);
-		break;
+		bits = (ctrl & (NAND_CLE | NAND_ALE)) << 3;
+		bits = (ctrl & NAND_NCE) << 7;
 
-	case NAND_CTL_SETNCE:
-		clps_writeb((clps_readb(ep7312_pxdr) | 0x80) & ~0x40, ep7312_pxdr);
-		break;
-	case NAND_CTL_CLRNCE:
-		clps_writeb((clps_readb(ep7312_pxdr) | 0x80) | 0x40, ep7312_pxdr);
-		break;
+		clps_writeb((clps_readb(ep7312_pxdr)  & 0xB0) | 0x10,
+			    ep7312_pxdr);
 	}
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
 /*
@@ -159,7 +153,7 @@
 	/* insert callbacks */
 	this->IO_ADDR_R = ep7312_fio_base;
 	this->IO_ADDR_W = ep7312_fio_base;
-	this->hwcontrol = ep7312_hwcontrol;
+	this->cmd_ctrl = ep7312_hwcontrol;
 	this->dev_ready = ep7312_device_ready;
 	/* 15 us command delay time */
 	this->chip_delay = 15;
diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c
index 06e91fa..2d585d2 100644
--- a/drivers/mtd/nand/h1910.c
+++ b/drivers/mtd/nand/h1910.c
@@ -56,36 +56,18 @@
 
 /*
  *	hardware specific access to control-lines
+ *
+ *	NAND_NCE: bit 0 - don't care
+ *	NAND_CLE: bit 1 - address bit 2
+ *	NAND_ALE: bit 2 - address bit 3
  */
-static void h1910_hwcontrol(struct mtd_info *mtd, int cmd)
+static void h1910_hwcontrol(struct mtd_info *mtd, int cmd,
+			    unsigned int ctrl)
 {
-	struct nand_chip *this = (struct nand_chip *)(mtd->priv);
+	struct nand_chip *chip = mtd->priv;
 
-	switch (cmd) {
-
-	case NAND_CTL_SETCLE:
-		this->IO_ADDR_R |= (1 << 2);
-		this->IO_ADDR_W |= (1 << 2);
-		break;
-	case NAND_CTL_CLRCLE:
-		this->IO_ADDR_R &= ~(1 << 2);
-		this->IO_ADDR_W &= ~(1 << 2);
-		break;
-
-	case NAND_CTL_SETALE:
-		this->IO_ADDR_R |= (1 << 3);
-		this->IO_ADDR_W |= (1 << 3);
-		break;
-	case NAND_CTL_CLRALE:
-		this->IO_ADDR_R &= ~(1 << 3);
-		this->IO_ADDR_W &= ~(1 << 3);
-		break;
-
-	case NAND_CTL_SETNCE:
-		break;
-	case NAND_CTL_CLRNCE:
-		break;
-	}
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W | ((ctrl & 0x6) << 1));
 }
 
 /*
@@ -145,7 +127,7 @@
 	/* insert callbacks */
 	this->IO_ADDR_R = nandaddr;
 	this->IO_ADDR_W = nandaddr;
-	this->hwcontrol = h1910_hwcontrol;
+	this->cmd_ctrl = h1910_hwcontrol;
 	this->dev_ready = NULL;	/* unknown whether that was correct or not so we will just do it like this */
 	/* 15 us command delay time */
 	this->chip_delay = 50;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index aa2e145..f6997fb 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -276,10 +276,10 @@
 	struct nand_chip *this = mtd->priv;
 	switch (chip) {
 	case -1:
-		this->hwcontrol(mtd, NAND_CTL_CLRNCE);
+		this->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
 		break;
 	case 0:
-		this->hwcontrol(mtd, NAND_CTL_SETNCE);
+		this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 		break;
 
 	default:
@@ -548,13 +548,12 @@
  * Send command to NAND device. This function is used for small page
  * devices (256/512 Bytes per page)
  */
-static void nand_command(struct mtd_info *mtd, unsigned command, int column,
-			 int page_addr)
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+			 int column, int page_addr)
 {
 	register struct nand_chip *this = mtd->priv;
+	int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
 
-	/* Begin command latch cycle */
-	this->hwcontrol(mtd, NAND_CTL_SETCLE);
 	/*
 	 * Write out the command to the device.
 	 */
@@ -572,33 +571,32 @@
 			column -= 256;
 			readcmd = NAND_CMD_READ1;
 		}
-		this->write_byte(mtd, readcmd);
+		this->cmd_ctrl(mtd, readcmd, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
 	}
-	this->write_byte(mtd, command);
+	this->cmd_ctrl(mtd, command, ctrl);
 
-	/* Set ALE and clear CLE to start address cycle */
-	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-
-	if (column != -1 || page_addr != -1) {
-		this->hwcontrol(mtd, NAND_CTL_SETALE);
-
-		/* Serially input address */
-		if (column != -1) {
-			/* Adjust columns for 16 bit buswidth */
-			if (this->options & NAND_BUSWIDTH_16)
-				column >>= 1;
-			this->write_byte(mtd, column);
-		}
-		if (page_addr != -1) {
-			this->write_byte(mtd, (uint8_t)(page_addr & 0xff));
-			this->write_byte(mtd, (uint8_t)((page_addr >> 8) & 0xff));
-			/* One more address cycle for devices > 32MiB */
-			if (this->chipsize > (32 << 20))
-				this->write_byte(mtd, (uint8_t)((page_addr >> 16) & 0x0f));
-		}
-		/* Latch in address */
-		this->hwcontrol(mtd, NAND_CTL_CLRALE);
+	/*
+	 * Address cycle, when necessary
+	 */
+	ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
+	/* Serially input address */
+	if (column != -1) {
+		/* Adjust columns for 16 bit buswidth */
+		if (this->options & NAND_BUSWIDTH_16)
+			column >>= 1;
+		this->cmd_ctrl(mtd, column, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
 	}
+	if (page_addr != -1) {
+		this->cmd_ctrl(mtd, page_addr, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
+		this->cmd_ctrl(mtd, page_addr >> 8, ctrl);
+		/* One more address cycle for devices > 32MiB */
+		if (this->chipsize > (32 << 20))
+			this->cmd_ctrl(mtd, page_addr >> 16, ctrl);
+	}
+	this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 	/*
 	 * program and erase have their own busy handlers
@@ -611,15 +609,16 @@
 	case NAND_CMD_ERASE2:
 	case NAND_CMD_SEQIN:
 	case NAND_CMD_STATUS:
+		this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE);
 		return;
 
 	case NAND_CMD_RESET:
 		if (this->dev_ready)
 			break;
 		udelay(this->chip_delay);
-		this->hwcontrol(mtd, NAND_CTL_SETCLE);
-		this->write_byte(mtd, NAND_CMD_STATUS);
-		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		this->cmd_ctrl(mtd, NAND_CMD_STATUS,
+			       NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+		this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE);
 		while (!(this->read_byte(mtd) & NAND_STATUS_READY)) ;
 		return;
 
@@ -648,12 +647,13 @@
  * @column:	the column address for this command, -1 if none
  * @page_addr:	the page address for this command, -1 if none
  *
- * Send command to NAND device. This is the version for the new large page devices
- * We dont have the separate regions as we have in the small page devices.
- * We must emulate NAND_CMD_READOOB to keep the code compatible.
+ * Send command to NAND device. This is the version for the new large page
+ * devices We dont have the separate regions as we have in the small page
+ * devices.  We must emulate NAND_CMD_READOOB to keep the code compatible.
  *
  */
-static void nand_command_lp(struct mtd_info *mtd, unsigned command, int column, int page_addr)
+static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+			    int column, int page_addr)
 {
 	register struct nand_chip *this = mtd->priv;
 
@@ -663,34 +663,33 @@
 		command = NAND_CMD_READ0;
 	}
 
-	/* Begin command latch cycle */
-	this->hwcontrol(mtd, NAND_CTL_SETCLE);
-	/* Write out the command to the device. */
-	this->write_byte(mtd, (command & 0xff));
-	/* End command latch cycle */
-	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+	/* Command latch cycle */
+	this->cmd_ctrl(mtd, command & 0xff,
+		       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
 
 	if (column != -1 || page_addr != -1) {
-		this->hwcontrol(mtd, NAND_CTL_SETALE);
+		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
 
 		/* Serially input address */
 		if (column != -1) {
 			/* Adjust columns for 16 bit buswidth */
 			if (this->options & NAND_BUSWIDTH_16)
 				column >>= 1;
-			this->write_byte(mtd, column & 0xff);
-			this->write_byte(mtd, column >> 8);
+			this->cmd_ctrl(mtd, column, ctrl);
+			ctrl &= ~NAND_CTRL_CHANGE;
+			this->cmd_ctrl(mtd, column >> 8, ctrl);
 		}
 		if (page_addr != -1) {
-			this->write_byte(mtd, (uint8_t)(page_addr & 0xff));
-			this->write_byte(mtd, (uint8_t)((page_addr >> 8) & 0xff));
+			this->cmd_ctrl(mtd, page_addr, ctrl);
+			this->cmd_ctrl(mtd, page_addr >> 8,
+				       NAND_NCE | NAND_ALE);
 			/* One more address cycle for devices > 128MiB */
 			if (this->chipsize > (128 << 20))
-				this->write_byte(mtd, (uint8_t)((page_addr >> 16) & 0xff));
+				this->cmd_ctrl(mtd, page_addr >> 16,
+					       NAND_NCE | NAND_ALE);
 		}
-		/* Latch in address */
-		this->hwcontrol(mtd, NAND_CTL_CLRALE);
 	}
+	this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
 
 	/*
 	 * program and erase have their own busy handlers
@@ -722,20 +721,14 @@
 		if (this->dev_ready)
 			break;
 		udelay(this->chip_delay);
-		this->hwcontrol(mtd, NAND_CTL_SETCLE);
-		this->write_byte(mtd, NAND_CMD_STATUS);
-		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		this->cmd_ctrl(mtd, NAND_CMD_STATUS, NAND_NCE | NAND_CLE);
+		this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE);
 		while (!(this->read_byte(mtd) & NAND_STATUS_READY)) ;
 		return;
 
 	case NAND_CMD_READ0:
-		/* Begin command latch cycle */
-		this->hwcontrol(mtd, NAND_CTL_SETCLE);
-		/* Write out the start read command */
-		this->write_byte(mtd, NAND_CMD_READSTART);
-		/* End command latch cycle */
-		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-		/* Fall through into ready check */
+		this->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_NCE | NAND_CLE);
+		this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE);
 
 		/* This applies to read commands */
 	default:
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index 22af9b2..ecf727b 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -1071,68 +1071,6 @@
 	}
 }
 
-static void
-ns_hwcontrol(struct mtd_info *mtd, int cmd)
-{
-	struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
-
-	switch (cmd) {
-
-	/* set CLE line high */
-	case NAND_CTL_SETCLE:
-		NS_DBG("ns_hwcontrol: start command latch cycles\n");
-		ns->lines.cle  = 1;
-		break;
-
-	/* set CLE line low */
-	case NAND_CTL_CLRCLE:
-		NS_DBG("ns_hwcontrol: stop command latch cycles\n");
-		ns->lines.cle  = 0;
-		break;
-
-	/* set ALE line high */
-	case NAND_CTL_SETALE:
-		NS_DBG("ns_hwcontrol: start address latch cycles\n");
-		ns->lines.ale   = 1;
-		break;
-
-	/* set ALE line low */
-	case NAND_CTL_CLRALE:
-		NS_DBG("ns_hwcontrol: stop address latch cycles\n");
-		ns->lines.ale  = 0;
-		break;
-
-	/* set WP line high */
-	case NAND_CTL_SETWP:
-		NS_DBG("ns_hwcontrol: enable write protection\n");
-		ns->lines.wp = 1;
-		break;
-
-	/* set WP line low */
-	case NAND_CTL_CLRWP:
-		NS_DBG("ns_hwcontrol: disable write protection\n");
-		ns->lines.wp = 0;
-		break;
-
-	/* set CE line low */
-	case NAND_CTL_SETNCE:
-		NS_DBG("ns_hwcontrol: enable chip\n");
-		ns->lines.ce = 1;
-		break;
-
-	/* set CE line high */
-	case NAND_CTL_CLRNCE:
-		NS_DBG("ns_hwcontrol: disable chip\n");
-		ns->lines.ce = 0;
-		break;
-
-	default:
-		NS_ERR("hwcontrol: unknown command\n");
-        }
-
-	return;
-}
-
 static u_char
 ns_nand_read_byte(struct mtd_info *mtd)
 {
@@ -1359,6 +1297,18 @@
 	return;
 }
 
+static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
+{
+	struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+
+	ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
+	ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
+	ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
+
+	if (cmd != NAND_CMD_NONE)
+		ns_nand_write_byte(mtd, cmd);
+}
+
 static int
 ns_device_ready(struct mtd_info *mtd)
 {
@@ -1514,7 +1464,7 @@
 	/*
 	 * Register simulator's callbacks.
 	 */
-	chip->hwcontrol  = ns_hwcontrol;
+	chip->cmd_ctrl	 = ns_hwcontrol;
 	chip->read_byte  = ns_nand_read_byte;
 	chip->dev_ready  = ns_device_ready;
 	chip->write_byte = ns_nand_write_byte;
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index e2dc81d..481541a 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -60,22 +60,17 @@
 	writel(ccr, ndfc->ndfcbase + NDFC_CCR);
 }
 
-static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd)
+static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
-	struct ndfc_controller *ndfc = &ndfc_ctrl;
 	struct nand_chip *chip = mtd->priv;
 
-	switch (cmd) {
-	case NAND_CTL_SETCLE:
-		chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_CMD;
-		break;
-	case NAND_CTL_SETALE:
-		chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_ALE;
-		break;
-	default:
-		chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
-		break;
-	}
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	if (ctrl & NAND_CLE)
+		writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_CMD);
+	else
+		writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_ALE);
 }
 
 static int ndfc_ready(struct mtd_info *mtd)
@@ -158,7 +153,7 @@
 
 	chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
 	chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
-	chip->hwcontrol = ndfc_hwcontrol;
+	chip->cmd_ctrl = ndfc_hwcontrol;
 	chip->dev_ready = ndfc_ready;
 	chip->select_chip = ndfc_select_chip;
 	chip->chip_delay = 50;
diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c
index 9fab099..22fa65c 100644
--- a/drivers/mtd/nand/ppchameleonevb.c
+++ b/drivers/mtd/nand/ppchameleonevb.c
@@ -108,54 +108,68 @@
 /*
  *	hardware specific access to control-lines
  */
-static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd,
+				  unsigned int ctrl)
 {
-	switch (cmd) {
+	struct nand_chip *chip = mtd->priv;
 
-	case NAND_CTL_SETCLE:
-		MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_CLRCLE:
-		MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_SETALE:
-		MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_CLRALE:
-		MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_SETNCE:
-		MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
-		break;
-	case NAND_CTL_CLRNCE:
-		MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+#error Missing headerfiles. No way to fix this. -tglx
+		switch (cmd) {
+		case NAND_CTL_SETCLE:
+			MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_CLRCLE:
+			MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_SETALE:
+			MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_CLRALE:
+			MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_SETNCE:
+			MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		case NAND_CTL_CLRNCE:
+			MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
+			break;
+		}
 	}
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
-static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd,
+				     unsigned int ctrl)
 {
-	switch (cmd) {
+	struct nand_chip *chip = mtd->priv;
 
-	case NAND_CTL_SETCLE:
-		MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_CLRCLE:
-		MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_SETALE:
-		MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_CLRALE:
-		MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_SETNCE:
-		MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
-		break;
-	case NAND_CTL_CLRNCE:
-		MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+#error Missing headerfiles. No way to fix this. -tglx
+		switch (cmd) {
+		case NAND_CTL_SETCLE:
+			MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_CLRCLE:
+			MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_SETALE:
+			MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_CLRALE:
+			MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_SETNCE:
+			MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		case NAND_CTL_CLRNCE:
+			MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
+			break;
+		}
 	}
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
 #ifdef USE_READY_BUSY_PIN
@@ -251,7 +265,7 @@
 	/* insert callbacks */
 	this->IO_ADDR_R = ppchameleon_fio_base;
 	this->IO_ADDR_W = ppchameleon_fio_base;
-	this->hwcontrol = ppchameleon_hwcontrol;
+	this->cmd_ctrl = ppchameleon_hwcontrol;
 #ifdef USE_READY_BUSY_PIN
 	this->dev_ready = ppchameleon_device_ready;
 #endif
@@ -351,7 +365,7 @@
 	/* insert callbacks */
 	this->IO_ADDR_R = ppchameleonevb_fio_base;
 	this->IO_ADDR_W = ppchameleonevb_fio_base;
-	this->hwcontrol = ppchameleonevb_hwcontrol;
+	this->cmd_ctrl = ppchameleonevb_hwcontrol;
 #ifdef USE_READY_BUSY_PIN
 	this->dev_ready = ppchameleonevb_device_ready;
 #endif
diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c
index f8e631c..6c97bfa 100644
--- a/drivers/mtd/nand/rtc_from4.c
+++ b/drivers/mtd/nand/rtc_from4.c
@@ -208,32 +208,18 @@
  * Address lines (A24-A22), so no action is required here.
  *
  */
-static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
+static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd,
+				unsigned int ctrl)
 {
-	struct nand_chip *this = (struct nand_chip *)(mtd->priv);
+	struct nand_chip *chip = (mtd->priv);
 
-	switch (cmd) {
+	if (cmd == NAND_CMD_NONE)
+		return;
 
-	case NAND_CTL_SETCLE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE);
-		break;
-	case NAND_CTL_CLRCLE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE);
-		break;
-
-	case NAND_CTL_SETALE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE);
-		break;
-	case NAND_CTL_CLRALE:
-		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE);
-		break;
-
-	case NAND_CTL_SETNCE:
-		break;
-	case NAND_CTL_CLRNCE:
-		break;
-
-	}
+	if (ctrl & NAND_CLE)
+		writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_CLE);
+	else
+		writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_ALE);
 }
 
 /*
@@ -559,7 +545,7 @@
 	this->IO_ADDR_R = rtc_from4_fio_base;
 	this->IO_ADDR_W = rtc_from4_fio_base;
 	/* Set address of hardware control function */
-	this->hwcontrol = rtc_from4_hwcontrol;
+	this->cmd_ctrl = rtc_from4_hwcontrol;
 	/* Set address of chip select function */
 	this->select_chip = rtc_from4_nand_select_chip;
 	/* command delay time (in us) */
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 608340a..215227d 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -256,60 +256,36 @@
  *
 */
 
-static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+				   unsigend int ctrl)
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	struct nand_chip *chip = mtd->priv;
 
-	switch (cmd) {
-	case NAND_CTL_SETNCE:
-	case NAND_CTL_CLRNCE:
-		printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
-		break;
+	if (cmd == NAND_CMD_NONE)
+		return;
 
-	case NAND_CTL_SETCLE:
-		chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;
-		break;
-
-	case NAND_CTL_SETALE:
-		chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;
-		break;
-
-		/* NAND_CTL_CLRCLE: */
-		/* NAND_CTL_CLRALE: */
-	default:
-		chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
-		break;
-	}
+	if (cmd & NAND_CLE)
+		writeb(cmd, info->regs + S3C2410_NFCMD);
+	else
+		writeb(cmd, info->regs + S3C2410_NFADDR);
 }
 
 /* command and control functions */
 
-static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+				   unsigend int ctrl)
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
 	struct nand_chip *chip = mtd->priv;
 
-	switch (cmd) {
-	case NAND_CTL_SETNCE:
-	case NAND_CTL_CLRNCE:
-		printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
-		break;
+	if (cmd == NAND_CMD_NONE)
+		return;
 
-	case NAND_CTL_SETCLE:
-		chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
-		break;
-
-	case NAND_CTL_SETALE:
-		chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
-		break;
-
-		/* NAND_CTL_CLRCLE: */
-		/* NAND_CTL_CLRALE: */
-	default:
-		chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
-		break;
-	}
+	if (cmd & NAND_CLE)
+		writeb(cmd, info->regs + S3C2440_NFCMD);
+	else
+		writeb(cmd, info->regs + S3C2440_NFADDR);
 }
 
 /* s3c2410_nand_devready()
@@ -498,7 +474,7 @@
 
 	chip->IO_ADDR_R	   = info->regs + S3C2410_NFDATA;
 	chip->IO_ADDR_W    = info->regs + S3C2410_NFDATA;
-	chip->hwcontrol    = s3c2410_nand_hwcontrol;
+	chip->cmd_ctrl     = s3c2410_nand_hwcontrol;
 	chip->dev_ready    = s3c2410_nand_devready;
 	chip->write_buf    = s3c2410_nand_write_buf;
 	chip->read_buf     = s3c2410_nand_read_buf;
@@ -511,7 +487,7 @@
 	if (info->is_s3c2440) {
 		chip->IO_ADDR_R	 = info->regs + S3C2440_NFDATA;
 		chip->IO_ADDR_W  = info->regs + S3C2440_NFDATA;
-		chip->hwcontrol  = s3c2440_nand_hwcontrol;
+		chip->cmd_ctrl   = s3c2440_nand_hwcontrol;
 	}
 
 	nmtd->info	   = info;
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
index 5554d0b..45a1da7 100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/sharpsl.c
@@ -77,31 +77,26 @@
 
 /*
  *	hardware specific access to control-lines
+ *	ctrl:
+ *	NAND_CNE: bit 0 -> bit 0 & 4
+ *	NAND_CLE: bit 1 -> bit 1
+ *	NAND_ALE: bit 2 -> bit 2
+ *
  */
-static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+				   unsigned int ctrl)
 {
-	switch (cmd) {
-	case NAND_CTL_SETCLE:
-		writeb(readb(FLASHCTL) | FLCLE, FLASHCTL);
-		break;
-	case NAND_CTL_CLRCLE:
-		writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL);
-		break;
+	struct nand_chip *chip = mtd->priv;
 
-	case NAND_CTL_SETALE:
-		writeb(readb(FLASHCTL) | FLALE, FLASHCTL);
-		break;
-	case NAND_CTL_CLRALE:
-		writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL);
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char bits = ctrl & 0x07;
 
-	case NAND_CTL_SETNCE:
-		writeb(readb(FLASHCTL) & ~(FLCE0 | FLCE1), FLASHCTL);
-		break;
-	case NAND_CTL_CLRNCE:
-		writeb(readb(FLASHCTL) | (FLCE0 | FLCE1), FLASHCTL);
-		break;
+		bits |= (ctrl & 0x01) << 4;
+		writeb((readb(FLASHCTL) & 0x17) | bits, FLASHCTL);
 	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
 static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
@@ -196,7 +191,7 @@
 	this->IO_ADDR_R = FLASHIO;
 	this->IO_ADDR_W = FLASHIO;
 	/* Set address of hardware control function */
-	this->hwcontrol = sharpsl_nand_hwcontrol;
+	this->cmd_ctrl = sharpsl_nand_hwcontrol;
 	this->dev_ready = sharpsl_nand_dev_ready;
 	/* 15 us command delay time */
 	this->chip_delay = 15;
diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c
index 9737f1d..1f6d429 100644
--- a/drivers/mtd/nand/spia.c
+++ b/drivers/mtd/nand/spia.c
@@ -82,20 +82,27 @@
 
 /*
  *	hardware specific access to control-lines
-*/
+ *
+ *	ctrl:
+ *	NAND_CNE: bit 0 -> bit 2
+ *	NAND_CLE: bit 1 -> bit 0
+ *	NAND_ALE: bit 2 -> bit 1
+ */
 static void spia_hwcontrol(struct mtd_info *mtd, int cmd)
 {
-	switch (cmd) {
+	struct nand_chip *chip = mtd->priv;
 
-	case NAND_CTL_SETCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |=  0x01; break;
-	case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x01; break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		void __iomem *addr = spia_io_base + spia_pedr;
+		unsigned char bits;
 
-	case NAND_CTL_SETALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |=  0x02; break;
-	case NAND_CTL_CLRALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x02; break;
-
-	case NAND_CTL_SETNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x04; break;
-	case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |=  0x04; break;
+		bits = (ctrl & NAND_CNE) << 2;
+		bits |= (ctrl & NAND_CLE | NAND_ALE) >> 1;
+		writeb((readb(addr) & ~0x7) | bits, addr);
 	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
 /*
@@ -133,7 +140,7 @@
 	this->IO_ADDR_R = (void __iomem *)spia_fio_base;
 	this->IO_ADDR_W = (void __iomem *)spia_fio_base;
 	/* Set address of hardware control function */
-	this->hwcontrol = spia_hwcontrol;
+	this->cmd_ctrl = spia_hwcontrol;
 	/* 15 us command delay time */
 	this->chip_delay = 15;
 
diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c
index 50aa6a4..a9cf019 100644
--- a/drivers/mtd/nand/toto.c
+++ b/drivers/mtd/nand/toto.c
@@ -32,6 +32,8 @@
 #include <asm/arch-omap1510/hardware.h>
 #include <asm/arch/gpio.h>
 
+#define CONFIG_NAND_WORKAROUND 1
+
 /*
  * MTD structure for TOTO board
  */
@@ -39,25 +41,6 @@
 
 static unsigned long toto_io_base = OMAP_FLASH_1_BASE;
 
-#define CONFIG_NAND_WORKAROUND 1
-
-#define NAND_NCE 0x4000
-#define NAND_CLE 0x1000
-#define NAND_ALE 0x0002
-#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
-
-#define T_NAND_CTL_CLRALE(iob)  gpiosetout(NAND_ALE, 0)
-#define T_NAND_CTL_SETALE(iob)  gpiosetout(NAND_ALE, NAND_ALE)
-#ifdef CONFIG_NAND_WORKAROUND	/* "some" dev boards busted, blue wired to rts2 :( */
-#define T_NAND_CTL_CLRCLE(iob)  gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
-#define T_NAND_CTL_SETCLE(iob)  gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
-#else
-#define T_NAND_CTL_CLRCLE(iob)  gpiosetout(NAND_CLE, 0)
-#define T_NAND_CTL_SETCLE(iob)  gpiosetout(NAND_CLE, NAND_CLE)
-#endif
-#define T_NAND_CTL_SETNCE(iob)  gpiosetout(NAND_NCE, 0)
-#define T_NAND_CTL_CLRNCE(iob)  gpiosetout(NAND_NCE, NAND_NCE)
-
 /*
  * Define partitions for flash devices
  */
@@ -91,25 +74,43 @@
 
 #define NUM_PARTITIONS32M 3
 #define NUM_PARTITIONS64M 4
+
 /*
  *	hardware specific access to control-lines
-*/
-
-static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
+ *
+ *	ctrl:
+ *	NAND_NCE: bit 0 -> bit 14 (0x4000)
+ *	NAND_CLE: bit 1 -> bit 12 (0x1000)
+ *	NAND_ALE: bit 2 -> bit 1  (0x0002)
+ */
+static void toto_hwcontrol(struct mtd_info *mtd, int cmd,
+			   unsigned int ctrl)
 {
+	struct nand_chip *chip = mtd->priv;
 
-	udelay(1);		/* hopefully enough time for tc make proceding write to clear */
-	switch (cmd) {
-		case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
-		case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned long bits;
 
-		case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
-		case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
+		/* hopefully enough time for tc make proceding write to clear */
+		udelay(1);
 
-		case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
-		case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
+		bits = (~ctrl & NAND_NCE) << 14;
+		bits |= (ctrl & NAND_CLE) << 12;
+		bits |= (ctrl & NAND_ALE) >> 1;
+
+#warning Wild guess as gpiosetout() is nowhere defined in the kernel source - tglx
+		gpiosetout(0x5002, bits);
+
+#ifdef CONFIG_NAND_WORKAROUND
+		/* "some" dev boards busted, blue wired to rts2 :( */
+		rts2setout(2, (ctrl & NAND_CLE) << 1);
+#endif
+		/* allow time to ensure gpio state to over take memory write */
+		udelay(1);
 	}
-	udelay(1);		/* allow time to ensure gpio state to over take memory write */
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
 /*
@@ -142,7 +143,7 @@
 	/* Set address of NAND IO lines */
 	this->IO_ADDR_R = toto_io_base;
 	this->IO_ADDR_W = toto_io_base;
-	this->hwcontrol = toto_hwcontrol;
+	this->cmd_ctrl = toto_hwcontrol;
 	this->dev_ready = NULL;
 	/* 25 us command delay time */
 	this->chip_delay = 30;
diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c
index 70bce1b..a0b4b1e 100644
--- a/drivers/mtd/nand/ts7250.c
+++ b/drivers/mtd/nand/ts7250.c
@@ -83,31 +83,29 @@
 
 /*
  *	hardware specific access to control-lines
+ *
+ *	ctrl:
+ *	NAND_NCE: bit 0 -> bit 2
+ *	NAND_CLE: bit 1 -> bit 1
+ *	NAND_ALE: bit 2 -> bit 0
  */
-static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd)
+static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
-	unsigned long ctrl = TS72XX_NAND_CONTROL_VIRT_BASE;
+	struct nand_chip *chip = mtd->priv;
 
-	switch (cmd) {
-	case NAND_CTL_SETCLE:
-		__raw_writeb(__raw_readb(ctrl) | 0x2, ctrl);
-		break;
-	case NAND_CTL_CLRCLE:
-		__raw_writeb(__raw_readb(ctrl) & ~0x2, ctrl);
-		break;
-	case NAND_CTL_SETALE:
-		__raw_writeb(__raw_readb(ctrl) | 0x1, ctrl);
-		break;
-	case NAND_CTL_CLRALE:
-		__raw_writeb(__raw_readb(ctrl) & ~0x1, ctrl);
-		break;
-	case NAND_CTL_SETNCE:
-		__raw_writeb(__raw_readb(ctrl) | 0x4, ctrl);
-		break;
-	case NAND_CTL_CLRNCE:
-		__raw_writeb(__raw_readb(ctrl) & ~0x4, ctrl);
-		break;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE;
+		unsigned char bits;
+
+		bits = (ctrl & NAND_CNE) << 2;
+		bits |= ctrl & NAND_CLE;
+		bits |= (ctrl & NAND_ALE) >> 2;
+
+		__raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr);
 	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, chip->IO_ADDR_W);
 }
 
 /*
@@ -152,7 +150,7 @@
 	/* insert callbacks */
 	this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE;
 	this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE;
-	this->hwcontrol = ts7250_hwcontrol;
+	this->cmd_ctrl = ts7250_hwcontrol;
 	this->dev_ready = ts7250_device_ready;
 	this->chip_delay = 15;
 	this->ecc.mode = NAND_ECC_SOFT;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 8362b46..e9a9352 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -50,23 +50,20 @@
 
 /*
  * Constants for hardware specific CLE/ALE/NCE function
-*/
+ *
+ * These are bits which can be or'ed to set/clear multiple
+ * bits in one go.
+ */
 /* Select the chip by setting nCE to low */
-#define NAND_CTL_SETNCE		1
-/* Deselect the chip by setting nCE to high */
-#define NAND_CTL_CLRNCE		2
+#define NAND_NCE		0x01
 /* Select the command latch by setting CLE to high */
-#define NAND_CTL_SETCLE		3
-/* Deselect the command latch by setting CLE to low */
-#define NAND_CTL_CLRCLE		4
+#define NAND_CLE		0x02
 /* Select the address latch by setting ALE to high */
-#define NAND_CTL_SETALE		5
-/* Deselect the address latch by setting ALE to low */
-#define NAND_CTL_CLRALE		6
-/* Set write protection by setting WP to high. Not used! */
-#define NAND_CTL_SETWP		7
-/* Clear write protection by setting WP to low. Not used! */
-#define NAND_CTL_CLRWP		8
+#define NAND_ALE		0x04
+
+#define NAND_CTRL_CLE		(NAND_NCE | NAND_CLE)
+#define NAND_CTRL_ALE		(NAND_NCE | NAND_ALE)
+#define NAND_CTRL_CHANGE	0x80
 
 /*
  * Standard NAND flash commands
@@ -106,6 +103,8 @@
 #define NAND_CMD_STATUS_RESET	0x7f
 #define NAND_CMD_STATUS_CLEAR	0xff
 
+#define NAND_CMD_NONE		-1
+
 /* Status bits */
 #define NAND_STATUS_FAIL	0x01
 #define NAND_STATUS_FAIL_N1	0x02
@@ -263,7 +262,8 @@
  * @select_chip:	[REPLACEABLE] select chip nr
  * @block_bad:		[REPLACEABLE] check, if the block is bad
  * @block_markbad:	[REPLACEABLE] mark the block bad
- * @hwcontrol:		[BOARDSPECIFIC] hardwarespecific function for accesing control-lines
+ * @cmd_ctrl:		[BOARDSPECIFIC] hardwarespecific funtion for controlling
+ *			ALE/CLE/nCE. Also used to write command and address
  * @dev_ready:		[BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
  *			If set to NULL no access to ready/busy is available and the ready/busy information
  *			is read from the chip status register
@@ -317,7 +317,8 @@
 	void		(*select_chip)(struct mtd_info *mtd, int chip);
 	int		(*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
 	int		(*block_markbad)(struct mtd_info *mtd, loff_t ofs);
-	void		(*hwcontrol)(struct mtd_info *mtd, int cmd);
+	void		(*cmd_ctrl)(struct mtd_info *mtd, int dat,
+				    unsigned int ctrl);
 	int		(*dev_ready)(struct mtd_info *mtd);
 	void		(*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
 	int		(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);