[PATCH] cs553x_nand driver PIO mode support, cs553x_cleanup() fix

Indrek Kruusa indrek.kruusa at artecdesign.ee
Wed Jun 14 09:13:00 EDT 2006


Hi!

Attatched patch for drivers/mtd/nand/cs553x_nand.c (in linux-2.6.17-rc6-mm1):
- add PIO mode support
- fix bug in cs553x_cleanup: 'for' loop shouldn't be broked if device is
missing
- added some informative printk's

Signed-off-by: Indrek Kruusa <indrek.kruusa at artecdesign.ee>


Indrek


--- cs553x_nand.c	2006-06-13 10:19:19.000000000 +0300
+++ cs553x_nand_mix_working.c	2006-06-13 11:29:40.000000000 +0300
@@ -30,6 +30,11 @@
 #include <asm/io.h>
 
 #define NR_CS553X_CONTROLLERS	4
+#define DEVICE_NAME "cs553x_nand"
+#define IO_REG_CNT 16
+
+/* I/O base address will be fetched from controller's MSR's  */
+static unsigned long io_base[NR_CS553X_CONTROLLERS];
 
 #define MSR_DIVIL_GLD_CAP	0x51400000	/* DIVIL capabilitiies */
 #define CAP_CS5535		0x2df000ULL
@@ -93,6 +98,109 @@
 #define CS_NAND_ECC_CLRECC	(1<<1)
 #define CS_NAND_ECC_ENECC	(1<<0)
 
+/*
+ * 
+ * functions for PIO mode
+ * 
+ * Currently all I/O is 1 byte at a time.
+ * If your hardware can do more: you could get more
+ * performance by changing read/write functions here
+ * 
+ */
+
+static void cs553x_write_byte_pio(struct mtd_info *mtd, u_char byte);
+
+static void cs553x_read_buf_pio(struct mtd_info *mtd, u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	ioread8_rep(this->IO_ADDR_R, buf, len);
+}
+
+
+static void cs553x_write_buf_pio(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+	    cs553x_write_byte_pio(mtd, buf[i]);
+}
+
+
+static unsigned char cs553x_read_byte_pio(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return ioread8(this->IO_ADDR_R);
+}
+
+
+static void cs553x_write_byte_pio(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	int i = 100000;
+
+	while (i && ioread8(this->IO_ADDR_R + IO_NAND_STS) & CS_NAND_CTLR_BUSY) {
+		udelay(1);
+		i--;
+	}
+	iowrite8(byte, this->IO_ADDR_W + IO_NAND_IO);
+}
+
+
+static void cs553x_hwcontrol_pio(struct mtd_info *mtd, int cmd,
+			     unsigned int ctrl)
+{
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
+		iowrite8(ctl, mmio_base + IO_NAND_CTL);
+	}
+	if (cmd != NAND_CMD_NONE)
+		cs553x_write_byte_pio(mtd, cmd);
+}
+
+
+static int cs553x_device_ready_pio(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+	unsigned char foo = ioread8(mmio_base + IO_NAND_STS);
+
+	return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
+}
+
+
+static void cs_enable_hwecc_pio(struct mtd_info *mtd, int mode)
+{
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+
+	iowrite8(0x07, mmio_base + IO_NAND_ECC_CTL);
+}
+
+
+static int cs_calculate_ecc_pio(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+	uint32_t ecc;
+	struct nand_chip *this = mtd->priv;
+	void __iomem *mmio_base = this->IO_ADDR_R;
+
+	ecc = ioread16(mmio_base + IO_NAND_STS);
+
+	ecc_code[1] = ecc >> 8;
+	ecc_code[0] = ecc >> 16;
+	ecc_code[2] = ecc >> 24;
+	return 0;
+}
+
+
+/*
+ * 
+ * functions for MMIO mode
+ * 
+ * 
+ */
+
+
 static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
 	struct nand_chip *this = mtd->priv;
@@ -187,12 +295,7 @@
 	struct nand_chip *this;
 	struct mtd_info *new_mtd;
 
-	printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr);
-
-	if (mmio) {
-		printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n");
-		return -ENXIO;
-	}
+	printk(KERN_NOTICE "Probing CS553x NAND controller CS%d# at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr);
 
 	/* Allocate memory for MTD device structure and private data */
 	new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
@@ -214,27 +317,54 @@
 	new_mtd->owner = THIS_MODULE;
 
 	/* map physical address */
-	this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
+	if (mmio) {
+		this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
+		io_base[cs] = 0;
+	} else {
+		io_base[cs] = adr;
+
+		if (!request_region(io_base[cs], IO_REG_CNT, DEVICE_NAME)) {
+			printk(KERN_ERR "%s: requested I/O region %#lx:%#lx is "
+		       "in use\n", DEVICE_NAME, io_base[cs], io_base[cs] + IO_REG_CNT - 1);
+			return -ENODEV;
+		} else {
+			printk(KERN_INFO "I/O region %#lx:%#lx for %s\n", io_base[cs], io_base[cs] + IO_REG_CNT - 1, DEVICE_NAME);
+		}
+
+		this->IO_ADDR_R = this->IO_ADDR_W = ioport_map(io_base[cs], IO_REG_CNT);
+	}
+
 	if (!this->IO_ADDR_R) {
-		printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr);
+		printk(KERN_WARNING "%s cs553x NAND @0x%08lx failed\n", mmio? "ioremap" : "ioport_map",adr);
 		err = -EIO;
 		goto out_mtd;
 	}
 
-	this->cmd_ctrl = cs553x_hwcontrol;
-	this->dev_ready = cs553x_device_ready;
-	this->read_byte = cs553x_read_byte;
-	this->read_buf = cs553x_read_buf;
-	this->write_buf = cs553x_write_buf;
+	this->ecc.correct  = nand_correct_data;
 
 	this->chip_delay = 0;
 
 	this->ecc.mode = NAND_ECC_HW;
 	this->ecc.size = 256;
 	this->ecc.bytes = 3;
-	this->ecc.hwctl  = cs_enable_hwecc;
-	this->ecc.calculate = cs_calculate_ecc;
-	this->ecc.correct  = nand_correct_data;
+
+	if (mmio) {
+		this->cmd_ctrl = cs553x_hwcontrol;
+		this->dev_ready = cs553x_device_ready;
+		this->read_byte = cs553x_read_byte;
+		this->read_buf = cs553x_read_buf;
+		this->write_buf = cs553x_write_buf;
+		this->ecc.hwctl  = cs_enable_hwecc;
+		this->ecc.calculate = cs_calculate_ecc;
+	} else {
+		this->cmd_ctrl = cs553x_hwcontrol_pio;
+		this->dev_ready = cs553x_device_ready_pio;
+		this->read_byte = cs553x_read_byte_pio;
+		this->read_buf = cs553x_read_buf_pio;
+		this->write_buf = cs553x_write_buf_pio;
+		this->ecc.hwctl  = cs_enable_hwecc_pio;
+		this->ecc.calculate = cs_calculate_ecc_pio;
+	}
 
 	/* Enable the following for a flash based bad block table */
 	this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
@@ -249,9 +379,16 @@
 	goto out;
 
 out_ior:
-	iounmap((void *)this->IO_ADDR_R);
+	if (mmio)
+		iounmap((void *)this->IO_ADDR_R);
+	else {
+		ioport_unmap((void *)this->IO_ADDR_R);
+		release_region(io_base[cs], IO_REG_CNT);
+	}
 out_mtd:
 	kfree(new_mtd);
+	if (!mmio)
+		release_region(io_base[cs], IO_REG_CNT);
 out:
 	return err;
 }
@@ -329,7 +466,7 @@
 		void __iomem *mmio_base;
 
 		if (!mtd)
-			break;
+			continue;
 
 		this = cs553x_mtd[i]->priv;
 		mmio_base = this->IO_ADDR_R;
@@ -339,7 +476,14 @@
 		cs553x_mtd[i] = NULL;
 
 		/* unmap physical adress */
-		iounmap(mmio_base);
+		if (!io_base[i]) {
+			iounmap(mmio_base);
+		} else {
+			ioport_unmap(mmio_base);
+			release_region(io_base[i], IO_REG_CNT);
+		}
+
+		printk(KERN_INFO "CS553x NAND driver: resources released for device at CS%d# \n", i);
 
 		/* Free the MTD device structure */
 		kfree(mtd);




More information about the linux-mtd mailing list