public inbox for linux-mtd@lists.infradead.org
 help / color / mirror / Atom feed
* New driver
@ 2004-08-24  0:09 Ian Molton
  2004-08-26 19:54 ` Ian Molton
  0 siblings, 1 reply; 3+ messages in thread
From: Ian Molton @ 2004-08-24  0:09 UTC (permalink / raw)
  To: linux-mtd; +Cc: tglx

[-- Attachment #1: Type: text/plain, Size: 460 bytes --]

Hi.

Here is my first run at the TC6393XB NAND/Smartmedia controller driver.

Currently the chip init is nonexistant - its done by wince on my board and I have no way of booting without wince on it yet, so I have no plan to implement it until I can test it (this will be soon, probably).

Hardware ECC seems to be broken. the code is present but disabled, for educational purposes.

this driver requires the read_id override patch (which I will post shortly).

[-- Attachment #2: nand_tmio.c --]
[-- Type: application/octet-stream, Size: 10482 bytes --]

/*
 *  drivers/mtd/nand/nand_tmio.c
 *
 * (c) Ian Molton and Sebastian Carlier
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This is a driver for the NAND flash controller on the TMIO
 * (Toshiba Multio IO Controller) as found in the toshiba e750 and e800 PDAs.
 *
 * This Driver should support the TC6393XB SoC 'smartmedia' controller.
 *
 * This is alpha quality. This chip did not mesh well with the linux
 * NAND flash base code as it has some funny requirements as to when you can
 * write bytes to the data register (eg. you need to write the address as a
 * 32 bit word).
 *
 * I have used the default mtd NAND read byte and write byte functions since
 * the few places they are used seem to be OK with this. The exception was the
 * read_id code, which had to be split off as an ovverideable function thanks
 * to his chip requiring a word (32bit) read, not two byte accesses, doh!)
 *
 * This chip has a hard ECC unit, but this appears to be buggy, at least for
 * reads. This may be solved by reading halfwords, but thats a hunch I havent
 * tried yet.
 *
 * Oh, also - this code assumes all buffers are a multiple of 4 bytes (1 word)
 * long (due to the use of word read/writes in {read,write,verify}_buf).
 *
 *                                          -Ian Molton <spyro@f2s.com>
 *  Revision history:
 *    23/08/2004     First working version
 * 
 *  TO DO list
 *    Do full chip initialisation (rather than rely on winCE)
 *    Fix hwECC if poss.
 *    Make use of chip 'ready' interrupt.
 *
 */

#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/sizes.h>

/*
 * MTD structure for TMIO nand controller
 */
static struct mtd_info *tmio_mtd = NULL;

#define TMIO_MODE_REG      0x04  //offset from data reg

#define TMIO_MODE_CLE      0x01
#define TMIO_MODE_ALE      0x02
#define TMIO_MODE_PCNTL    0x04   // Power control
#define TMIO_MODE_LED      0x08   // Optional LED control
#define TMIO_MODE_CE       0x10
#define TMIO_MODE_ECC0     0x20   // ECC control regs.
#define TMIO_MODE_ECC1     0x40   // ...
#define TMIO_MODE_WP       0x80

#define TMIO_STATUS_REG    0x05  // offset from data reg

#define TMIO_STATUS_WPD    0x01    // Write protect seal detected
#define TMIO_STATUS_EJREQ  0x02    // Card Eject Request
#define TMIO_STATUS_CENB   0x04    // Card Enable
#define TMIO_STATUS_STCHG  0x08    // Card Status Change
#define TMIO_STATUS_PWON   0x10    // SmartMedia Power On
#define TMIO_STATUS_MODEL  0x40    // Smartmedia voltage (1 = 5.0 0 = 3.3V)
#define TMIO_STATUS_BUSY   0x80    // High when busy

/* 
 *	hardware specific access to control-lines
 */

static void tmio_read_id(struct mtd_info *mtd, int *maf_id, int *dev_id) {
	struct nand_chip *this = mtd->priv;
	unsigned long id;

	/* Send the command for reading device ID */
	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);

	id = readl(this->IO_ADDR_R);
	*maf_id = id & 0xff;
	*dev_id = (id >> 8) & 0xff;
}

static void tmio_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
	struct nand_chip *this = mtd->priv;
	u32 *p = (u32*)buf;
	int i;

	if(len & 0x3)
		BUG();

	len >>= 2;

	for (i=0; i < len; i++)
		p[i] = readl(this->IO_ADDR_R);
}

static void tmio_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
	struct nand_chip *this = mtd->priv;
	u32 *p = (u32*)buf;
	int i;

	if(len & 0x3)
		BUG();

	len >>= 2;

	for (i=0; i < len; i++)
		writel(p[i], this->IO_ADDR_W);
}

static int tmio_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
	struct nand_chip *this = mtd->priv;
	u32 *p = (u32*)buf;
	int i;

	if(len & 0x3)
		BUG();

	len >>= 2;

	for (i=0; i<len; i++)
		if (p[i] != readl(this->IO_ADDR_R))
			return -EFAULT;

	return 0;
}

static unsigned char stat;
#if 0
// HARDWARE ECC DOESNT WORK YET (hardware bug?)
static void tmio_enable_hwecc(struct mtd_info *mtd, int mode)
{
        struct nand_chip *this = mtd->priv;
	// Reset ECC
	writeb(stat | 0x60, this->IO_ADDR_W + TMIO_MODE_REG);
	readb(this->IO_ADDR_R);
	// Enable ECC
	writeb(stat | 0x20, this->IO_ADDR_W + TMIO_MODE_REG);
}

static int tmio_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
                                 unsigned char *ecc_code)
{
        struct nand_chip *this = mtd->priv;
	unsigned char data_1[4], data_2[4];

	//FIXME: not big-endian safe

	// Read ECC result
	writeb(stat | 0x40, this->IO_ADDR_W + TMIO_MODE_REG);
	*(u32*)data_1 = readl(this->IO_ADDR_R);
	*(u32*)data_2 = readl(this->IO_ADDR_R);
	// Disable ECC
	writeb(stat, this->IO_ADDR_W + TMIO_MODE_REG);
	ecc_code[0] = data_2[0];
	ecc_code[1] = data_1[3];
	ecc_code[2] = data_2[1];
	ecc_code[3] = data_1[1];
	ecc_code[4] = data_1[0];
	ecc_code[5] = data_1[2];
	return 0;
}
#endif

static void tmio_hwcontrol(struct mtd_info *mtd, int cmd) 
{
	struct nand_chip *this = mtd->priv;

	switch(cmd) {

		case NAND_CTL_SETCLE: stat |=  TMIO_MODE_CLE; break;
		case NAND_CTL_CLRCLE: stat &= ~TMIO_MODE_CLE; break;

		case NAND_CTL_SETALE: stat |=  TMIO_MODE_ALE; break;
		case NAND_CTL_CLRALE: stat &= ~TMIO_MODE_ALE; break;

		case NAND_CTL_SETNCE: stat |=  TMIO_MODE_CE; break;
		case NAND_CTL_CLRNCE: stat &= ~TMIO_MODE_CE; break;
	}

	writeb(stat, this->IO_ADDR_W + TMIO_MODE_REG);
}

// This is the main hack to the default drivers - this chip is funy in
// that the address must be written as a word, not three bytes writes.

static void tmio_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
	register struct nand_chip *this = mtd->priv;

	/* Begin command latch cycle */
	this->hwcontrol(mtd, NAND_CTL_SETCLE);
	/*
	 * Write out the command to the device.
	 */
	if (command == NAND_CMD_SEQIN) {
		int readcmd;

		if (column >= mtd->oobblock) {
			/* OOB area */
			column -= mtd->oobblock;
			readcmd = NAND_CMD_READOOB;
		} else if (column < 256) {
			/* First 256 bytes --> READ0 */
			readcmd = NAND_CMD_READ0;
		} else {
			column -= 256;
			readcmd = NAND_CMD_READ1;
		}
		writeb(readcmd, this->IO_ADDR_W);
	}
	writeb(command, this->IO_ADDR_W);

	/* 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);

		if (column != -1) {
			/* Adjust columns for 16 bit buswidth */
			writeb(column, this->IO_ADDR_W);
		}
		if (page_addr != -1)
			writel(page_addr & 0x00ffffff, this->IO_ADDR_W);

		/* Latch in address */
		this->hwcontrol(mtd, NAND_CTL_CLRALE);
	}

	/*
	 * program and erase have their own busy handlers
	 * status and sequential in needs no delay
	 */
	switch (command) {
		case NAND_CMD_PAGEPROG:
		case NAND_CMD_ERASE1:
		case NAND_CMD_ERASE2:
		case NAND_CMD_SEQIN:
		case NAND_CMD_STATUS:
			return;
	}

	/* Apply this short delay always to ensure that we do wait tWB in
	 * any case on any machine. */
	ndelay (100);
	/* wait until command is processed */
	while (!this->dev_ready(mtd));
}

/*
 *	read device ready pin
 */
static int tmio_device_ready(struct mtd_info *mtd)
{
	register struct nand_chip *this = mtd->priv;
	return (readb(this->IO_ADDR_R + TMIO_STATUS_REG) & TMIO_STATUS_BUSY) ? 0 : 1;
}

#ifdef CONFIG_MTD_PARTITIONS
// Nice and simple - one big partition.
static struct mtd_partition partition_a = {
	.name = "Internal NAND flash",
	.offset =  0,
	.size =  MTDPART_SIZ_FULL,
};
#endif

// WinCE uses this ECC layout (it uses SSFDC). lets play nice.
struct nand_oobinfo tmio_oobinfo  = {
	.eccpos = {14,13,15,9,8,10},
	.oobfree = {{0,4},{6,2},{11,2}},
	.eccbytes = 6,
	.useecc = MTD_NANDECC_AUTOPLACE,
};

/*
 * Main initialization routine
 */
static int __init tmio_init (void)
{
	struct nand_chip *this;
	int tmio_pbase=0x10000b00;  //FIXME - hardcoded for toshiba eseries PDAs
	int tmio_base;

	// Initial status - write enabled, power on.
	stat = TMIO_MODE_PCNTL | TMIO_MODE_WP;

	/* Allocate memory for MTD device structure and private data */
	tmio_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
	                   GFP_KERNEL);
	if (!tmio_mtd)
		return -ENOMEM;

	/* map physical adress */
	//FIXME - map 0x100? is that the minimum?
	tmio_base = (unsigned long)ioremap(tmio_pbase, 0x100);
	if(!tmio_base) {
		kfree(tmio_mtd);
		return -EIO;
	}

	/* Get pointer to private data */
	this = (struct nand_chip *) (&tmio_mtd[1]);

	/* Initialize structures */
	memset((char *) tmio_mtd, 0, sizeof(struct mtd_info));
	memset((char *) this, 0, sizeof(struct nand_chip));

	/* Link the private data with the MTD structure */
	tmio_mtd->priv = this;

	/* insert callbacks */
	this->IO_ADDR_R  = tmio_base;
	this->IO_ADDR_W  = tmio_base;
	this->cmdfunc    = tmio_command;
	this->hwcontrol  = tmio_hwcontrol;
	this->dev_ready  = tmio_device_ready;
	this->read_id    = tmio_read_id;
	this->read_buf   = tmio_read_buf;
	this->write_buf  = tmio_write_buf;
	this->verify_buf = tmio_verify_buf;
	this->autooob    = &tmio_oobinfo;
	this->eccmode    = NAND_ECC_SOFT;
#if 0
	// HW ECC doesnt work (hardware bug?)
	this->enable_hwecc = tmio_enable_hwecc;
	this->calculate_ecc = tmio_calculate_ecc;
	this->correct_data = nand_correct_data;
	this->eccmode = NAND_ECC_HW6_512;
#endif

	/* Scan to find existence of the device */
	if (nand_scan (tmio_mtd, 1)) {
		iounmap((void *)tmio_base);
		kfree (tmio_mtd);
		return -ENXIO;
	}

	/* Allocate memory for internal data buffer */
	this->data_buf = kmalloc (sizeof(u_char) * (tmio_mtd->oobblock + tmio_mtd->oobsize), GFP_KERNEL);
	if (!this->data_buf) {
		iounmap((void *)tmio_base);
		kfree (tmio_mtd);
		return -ENOMEM;
	}

#ifdef CONFIG_MTD_PARTITIONS
	/* Register the partitions */
	add_mtd_partitions(tmio_mtd, &partition_a, 1);
#endif

	/* Return happy */
	return 0;
}

/*
 * Clean up routine
 */
static void __exit tmio_cleanup (void)
{
	struct nand_chip *this = (struct nand_chip *) &tmio_mtd[1];

	//FIXME - do we need to remove the partition?

	/* Unregister the device */
	del_mtd_device (tmio_mtd);

	/* Free internal data buffer */
	kfree (this->data_buf);

	/* Free the MTD device structure */
	kfree (tmio_mtd);
}

module_init(tmio_init);
module_exit(tmio_cleanup);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ian Molton and Sebastian Carlier");
MODULE_DESCRIPTION("MTD map driver for TMIO NAND controller.");

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: New driver
  2004-08-24  0:09 New driver Ian Molton
@ 2004-08-26 19:54 ` Ian Molton
  2004-08-26 20:52   ` Josh Boyer
  0 siblings, 1 reply; 3+ messages in thread
From: Ian Molton @ 2004-08-26 19:54 UTC (permalink / raw)
  To: linux-mtd

On Tue, 24 Aug 2004 01:09:27 +0100
Ian Molton <spyro@f2s.com> wrote:

> Hi.
> 
> Here is my first run at the TC6393XB NAND/Smartmedia controller driver.

No comments ?

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: New driver
  2004-08-26 19:54 ` Ian Molton
@ 2004-08-26 20:52   ` Josh Boyer
  0 siblings, 0 replies; 3+ messages in thread
From: Josh Boyer @ 2004-08-26 20:52 UTC (permalink / raw)
  To: Ian Molton; +Cc: linux-mtd

On Thu, 2004-08-26 at 14:54, Ian Molton wrote:
> On Tue, 24 Aug 2004 01:09:27 +0100
> Ian Molton <spyro@f2s.com> wrote:
> 
> > Hi.
> > 
> > Here is my first run at the TC6393XB NAND/Smartmedia controller driver.
> 
> No comments ?

You forgot to iounmap in your tmio_cleanup function.

Also, you could put:

if(len & 0x3)
	BUG();

into a macro.  Later, if you can ever guarantee that will never be true,
you can just define the macro to be a no-op.  Just a coding style thing
though.

josh

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2004-08-26 20:52 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-08-24  0:09 New driver Ian Molton
2004-08-26 19:54 ` Ian Molton
2004-08-26 20:52   ` Josh Boyer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox