From: Ian Molton <spyro@f2s.com>
To: linux-mtd@lists.infradead.org
Cc: tglx@linutronix.de
Subject: New driver
Date: Tue, 24 Aug 2004 01:09:27 +0100 [thread overview]
Message-ID: <20040824010927.04e6ccb3.spyro@f2s.com> (raw)
[-- 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.");
next reply other threads:[~2004-08-24 0:09 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-08-24 0:09 Ian Molton [this message]
2004-08-26 19:54 ` New driver Ian Molton
2004-08-26 20:52 ` Josh Boyer
-- strict thread matches above, loose matches on Subject: below --
2004-02-19 0:27 new driver Doelfel, Hardy
2004-02-18 23:39 Tomita, Haruo
2004-02-18 23:57 ` Ken Schneider
2004-02-19 5:32 ` Jeff Garzik
2004-02-18 8:51 Tomita, Haruo
2004-02-18 12:29 ` Arjan van de Ven
2004-02-18 23:29 ` Ken Schneider
2004-02-15 0:06 Ken Schneider
2004-02-15 9:29 ` Christoph Hellwig
2004-02-15 10:57 ` Arjan van de Ven
2004-02-15 20:35 ` Jeff Garzik
2003-03-04 23:35 New driver mjander
2003-03-04 23:27 ` Paul Davis
2003-03-05 8:48 ` Takashi Iwai
2000-12-28 21:19 Pixel
2000-12-28 21:53 ` Keith Owens
2000-12-29 2:49 ` Pixel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20040824010927.04e6ccb3.spyro@f2s.com \
--to=spyro@f2s.com \
--cc=linux-mtd@lists.infradead.org \
--cc=tglx@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.