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: 3+ 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
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox