From a6b1d82d079a99d09761ee5fbc66c49f33b42324 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 4 Oct 2006 07:57:18 -0400 Subject: [JFFS2] kill warning RE debug-only variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc emits the following warning on a 'allmodconfig' build: fs/jffs2/xattr.c: In function ‘unrefer_xattr_datum’: fs/jffs2/xattr.c:402: warning: unused variable ‘version’ fs/jffs2/xattr.c:402: warning: unused variable ‘xid’ Given that these variables are only used in the debug printk, and they merely remove a deref, we can easily kill the warning by adding the derefs to the debug printk. Signed-off-by: Jeff Garzik Signed-off-by: David Woodhouse --- fs/jffs2/xattr.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 4da09ce1d1f5..4bb3f1897330 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -399,8 +399,6 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu { /* must be called under down_write(xattr_sem) */ if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) { - uint32_t xid = xd->xid, version = xd->version; - unload_xattr_datum(c, xd); xd->flags |= JFFS2_XFLAGS_DEAD; if (xd->node == (void *)xd) { @@ -411,7 +409,8 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu } spin_unlock(&c->erase_completion_lock); - dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version); + dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", + xd->xid, xd->version); } } -- cgit v1.2.3 From 5467fb025537eb92313fd3a557b2051cb41ba5e8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 6 Oct 2006 15:36:29 +0100 Subject: =?UTF-8?q?[MTD=20NAND]=20Initial=20import=20of=20CAF=C3=89=20NAND?= =?UTF-8?q?=20driver.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/cafe_nand.c | 635 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 643 insertions(+) create mode 100644 drivers/mtd/nand/cafe_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index c99302ed3823..5e97e63e127a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -232,6 +232,13 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". +config MTD_NAND_CAFE + tristate "NAND support for OLPC CAFÉ chip" + depends on PCI + help + Use NAND flash attached to the CAFÉ chip designed for the $100 + laptop. + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" depends on MTD_NAND && MTD_PARTITIONS diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f74759351c91..9346b831bc3f 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_TOTO) += toto.o diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c new file mode 100644 index 000000000000..8d7a795a9478 --- /dev/null +++ b/drivers/mtd/nand/cafe_nand.c @@ -0,0 +1,635 @@ +/* + * cafe_nand.c + * + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2006 David Woodhouse + */ + +//#define DEBUG + +#include +#undef DEBUG +#include +#include +#include +#include +#include +#include + +#define CAFE_NAND_CTRL1 0x00 +#define CAFE_NAND_CTRL2 0x04 +#define CAFE_NAND_CTRL3 0x08 +#define CAFE_NAND_STATUS 0x0c +#define CAFE_NAND_IRQ 0x10 +#define CAFE_NAND_IRQ_MASK 0x14 +#define CAFE_NAND_DATA_LEN 0x18 +#define CAFE_NAND_ADDR1 0x1c +#define CAFE_NAND_ADDR2 0x20 +#define CAFE_NAND_TIMING1 0x24 +#define CAFE_NAND_TIMING2 0x28 +#define CAFE_NAND_TIMING3 0x2c +#define CAFE_NAND_NONMEM 0x30 +#define CAFE_NAND_DMA_CTRL 0x40 +#define CAFE_NAND_DMA_ADDR0 0x44 +#define CAFE_NAND_DMA_ADDR1 0x48 +#define CAFE_NAND_READ_DATA 0x1000 +#define CAFE_NAND_WRITE_DATA 0x2000 + +struct cafe_priv { + struct nand_chip nand; + struct pci_dev *pdev; + void __iomem *mmio; + uint32_t ctl1; + uint32_t ctl2; + int datalen; + int nr_data; + int data_pos; + int page_addr; + dma_addr_t dmaaddr; + unsigned char *dmabuf; + +}; + +static int usedma = 1; +module_param(usedma, int, 0644); + +static int cafe_device_ready(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); + + uint32_t irqs = readl(cafe->mmio + 0x10); + writel(irqs, cafe->mmio+0x10); + dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", + result?"":" not", irqs, readl(cafe->mmio + 0x10), + readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); + return result; +} + + +static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(cafe->dmabuf + cafe->datalen, buf, len); + else + memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); + cafe->datalen += len; + + dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", + len, cafe->datalen); +} + +static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct cafe_priv *cafe = mtd->priv; + + if (usedma) + memcpy(buf, cafe->dmabuf + cafe->datalen, len); + else + memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); + + dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", + len, cafe->datalen); + cafe->datalen += len; +} + +static uint8_t cafe_read_byte(struct mtd_info *mtd) +{ + struct cafe_priv *cafe = mtd->priv; + uint8_t d; + + cafe_read_buf(mtd, &d, 1); + dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); + + return d; +} + +static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct cafe_priv *cafe = mtd->priv; + int adrbytes = 0; + uint32_t ctl1; + uint32_t doneint = 0x80000000; + int i; + + dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", + command, column, page_addr); + + if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { + /* Second half of a command we already calculated */ + writel(cafe->ctl2 | 0x100 | command, cafe->mmio + 0x04); + ctl1 = cafe->ctl1; + dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", + cafe->ctl1, cafe->nr_data); + goto do_command; + } + /* Reset ECC engine */ + writel(0, cafe->mmio + CAFE_NAND_CTRL2); + + /* Emulate NAND_CMD_READOOB on large-page chips */ + if (mtd->writesize > 512 && + command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* FIXME: Do we need to send read command before sending data + for small-page chips, to position the buffer correctly? */ + + if (column != -1) { + writel(column, cafe->mmio + 0x1c); + adrbytes = 2; + if (page_addr != -1) + goto write_adr2; + } else if (page_addr != -1) { + writel(page_addr & 0xffff, cafe->mmio + 0x1c); + page_addr >>= 16; + write_adr2: + writel(page_addr, cafe->mmio+0x20); + adrbytes += 2; + if (mtd->size > mtd->writesize << 16) + adrbytes++; + } + + cafe->data_pos = cafe->datalen = 0; + + /* Set command valid bit */ + ctl1 = 0x80000000 | command; + + /* Set RD or WR bits as appropriate */ + if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { + ctl1 |= (1<<26); /* rd */ + /* Always 5 bytes, for now */ + cafe->datalen = 5; + /* And one address cycle -- even for STATUS, since the controller doesn't work without */ + adrbytes = 1; + } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) { + ctl1 |= 1<<26; /* rd */ + /* For now, assume just read to end of page */ + cafe->datalen = mtd->writesize + mtd->oobsize - column; + } else if (command == NAND_CMD_SEQIN) + ctl1 |= 1<<25; /* wr */ + + /* Set number of address bytes */ + if (adrbytes) + ctl1 |= ((adrbytes-1)|8) << 27; + + if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) { + /* Ignore the first command of a pair; the hardware + deals with them both at once, later */ + cafe->ctl1 = ctl1; + cafe->ctl2 = 0; + dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", + cafe->ctl1, cafe->datalen); + return; + } + /* RNDOUT and READ0 commands need a following byte */ + if (command == NAND_CMD_RNDOUT) + writel(cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, cafe->mmio + CAFE_NAND_CTRL2); + else if (command == NAND_CMD_READ0 && mtd->writesize > 512) + writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); + + do_command: + if (cafe->datalen == 2112) + cafe->datalen = 2062; + dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); + /* NB: The datasheet lies -- we really should be subtracting 1 here */ + writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); + writel(0x90000000, cafe->mmio + 0x10); + if (usedma && (ctl1 & (3<<25))) { + uint32_t dmactl = 0xc0000000 + cafe->datalen; + /* If WR or RD bits set, set up DMA */ + if (ctl1 & (1<<26)) { + /* It's a read */ + dmactl |= (1<<29); + /* ... so it's done when the DMA is done, not just + the command. */ + doneint = 0x10000000; + } + writel(dmactl, cafe->mmio + 0x40); + } +#if 0 + printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); + printk("DMA setup is %x, status %x, ctl1 %x\n", readl(cafe->mmio + 0x40), readl(cafe->mmio + 0x0c), readl(cafe->mmio)); +#endif + cafe->datalen = 0; + +#if 0 + printk("About to write command %08x\n", ctl1); + for (i=0; i< 0x5c; i+=4) + printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); +#endif + writel(ctl1, cafe->mmio + CAFE_NAND_CTRL1); + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + if (1) { + int c = 50000; + uint32_t irqs; + + while (c--) { + irqs = readl(cafe->mmio + 0x10); + if (irqs & doneint) + break; + udelay(1); + if (!(c & 1000)) + dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); + cpu_relax(); + } + writel(doneint, cafe->mmio + 0x10); + dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + 0x10)); + } + + + cafe->ctl2 &= ~(1<<8); + cafe->ctl2 &= ~(1<<30); + + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + case NAND_CMD_RNDOUT: + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); + return; + } + nand_wait_ready(mtd); + writel(cafe->ctl2, cafe->mmio + CAFE_NAND_CTRL2); +} + +static void cafe_select_chip(struct mtd_info *mtd, int chipnr) +{ + //struct cafe_priv *cafe = mtd->priv; + // dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); +} +static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) +{ + struct mtd_info *mtd = id; + struct cafe_priv *cafe = mtd->priv; + uint32_t irqs = readl(cafe->mmio + 0x10); + writel(irqs & ~0x90000000, cafe->mmio + 0x10); + if (!irqs) + return IRQ_NONE; + + dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + 0x10)); + return IRQ_HANDLED; +} + +static void cafe_nand_bug(struct mtd_info *mtd) +{ + BUG(); +} + +static int cafe_nand_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int status = 0; + + WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* Don't use -- use nand_read_oob_std for now */ +static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 1; +} +/** + * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + WARN_ON(chip->oob_poi != chip->buffers->oobrbuf); + + dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static char foo[14]; +static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + struct cafe_priv *cafe = mtd->priv; + + WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); + + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, foo, 14); + // chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Set up ECC autogeneration */ + cafe->ctl2 |= (1<<27) | (1<<30); + if (mtd->writesize == 2048) + cafe->ctl2 |= (1<<29); +} + +static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw) +{ + int status; + + WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; +} + + +static int __devinit cafe_nand_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mtd_info *mtd; + struct cafe_priv *cafe; + uint32_t ctrl; + int err = 0; + + err = pci_enable_device(pdev); + if (err) + return err; + + pci_set_master(pdev); + + mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); + if (!mtd) { + dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); + return -ENOMEM; + } + cafe = (void *)(&mtd[1]); + + mtd->priv = cafe; + mtd->owner = THIS_MODULE; + + cafe->pdev = pdev; + cafe->mmio = pci_iomap(pdev, 0, 0); + if (!cafe->mmio) { + dev_warn(&pdev->dev, "failed to iomap\n"); + err = -ENOMEM; + goto out_free_mtd; + } + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) { + err = -ENOMEM; + goto out_ior; + } + cafe->nand.buffers = (void *)cafe->dmabuf + 2112; + + cafe->nand.cmdfunc = cafe_nand_cmdfunc; + cafe->nand.dev_ready = cafe_device_ready; + cafe->nand.read_byte = cafe_read_byte; + cafe->nand.read_buf = cafe_read_buf; + cafe->nand.write_buf = cafe_write_buf; + cafe->nand.select_chip = cafe_select_chip; + + cafe->nand.chip_delay = 0; + + /* Enable the following for a flash based bad block table */ + cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + + /* Timings from Marvell's test code (not verified or calculated by us) */ + writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); +#if 1 + writel(0x01010a0a, cafe->mmio + CAFE_NAND_TIMING1); + writel(0x24121212, cafe->mmio + CAFE_NAND_TIMING2); + writel(0x11000000, cafe->mmio + CAFE_NAND_TIMING3); +#else + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING1); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); + writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); +#endif + writel(0xdfffffff, cafe->mmio + 0x14); + err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); + if (err) { + dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); + + goto out_free_dma; + } +#if 1 + /* Disable master reset, enable NAND clock */ + ctrl = readl(cafe->mmio + 0x3004); + ctrl &= 0xffffeff0; + ctrl |= 0x00007000; + writel(ctrl | 0x05, cafe->mmio + 0x3004); + writel(ctrl | 0x0a, cafe->mmio + 0x3004); + writel(0, cafe->mmio + 0x40); + + writel(0x7006, cafe->mmio + 0x3004); + writel(0x700a, cafe->mmio + 0x3004); + + /* Set up DMA address */ + writel(cafe->dmaaddr & 0xffffffff, cafe->mmio + 0x44); + if (sizeof(cafe->dmaaddr) > 4) + writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + 0x48); + else + writel(0, cafe->mmio + 0x48); + dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + readl(cafe->mmio+0x44), cafe->dmabuf); + + /* Enable NAND IRQ in global IRQ mask register */ + writel(0x80000007, cafe->mmio + 0x300c); + dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + readl(cafe->mmio + 0x3004), readl(cafe->mmio + 0x300c)); +#endif +#if 1 + mtd->writesize=2048; + mtd->oobsize = 0x40; + memset(cafe->dmabuf, 0xa5, 2112); + cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); +#endif +#if 0 + cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); + // nand_wait_ready(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); + cafe->nand.read_byte(mtd); +#endif +#if 0 + writel(0x84600070, cafe->mmio); + udelay(10); + dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); +#endif + /* Scan to find existance of the device */ + if (nand_scan_ident(mtd, 1)) { + err = -ENXIO; + goto out_irq; + } + + cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ + if (mtd->writesize == 2048) + cafe->ctl2 |= 1<<29; /* 2KiB page size */ + + /* Set up ECC according to the type of chip we found */ + if (mtd->writesize == 512 || mtd->writesize == 2048) { + cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.size = mtd->writesize; + cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; + cafe->nand.ecc.calculate = (void *)cafe_nand_bug; + cafe->nand.ecc.correct = (void *)cafe_nand_bug; + cafe->nand.write_page = cafe_nand_write_page; + cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; + cafe->nand.ecc.write_oob = cafe_nand_write_oob; + cafe->nand.ecc.read_page = cafe_nand_read_page; + cafe->nand.ecc.read_oob = cafe_nand_read_oob; + + } else { + printk(KERN_WARNING "Unexpected NAND flash writesize %d. Using software ECC\n", + mtd->writesize); + cafe->nand.ecc.mode = NAND_ECC_NONE; + } + + err = nand_scan_tail(mtd); + if (err) + goto out_irq; + + + pci_set_drvdata(pdev, mtd); + add_mtd_device(mtd); + goto out; + + out_irq: + /* Disable NAND IRQ in global IRQ mask register */ + writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + free_irq(pdev->irq, mtd); + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + out_ior: + pci_iounmap(pdev, cafe->mmio); + out_free_mtd: + kfree(mtd); + out: + return err; +} + +static void __devexit cafe_nand_remove(struct pci_dev *pdev) +{ + struct mtd_info *mtd = pci_get_drvdata(pdev); + struct cafe_priv *cafe = mtd->priv; + + del_mtd_device(mtd); + /* Disable NAND IRQ in global IRQ mask register */ + writel(~1 & readl(cafe->mmio + 0x300c), cafe->mmio + 0x300c); + free_irq(pdev->irq, mtd); + nand_release(mtd); + pci_iounmap(pdev, cafe->mmio); + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + kfree(mtd); +} + +static struct pci_device_id cafe_nand_tbl[] = { + { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 } +}; + +MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); + +static struct pci_driver cafe_nand_pci_driver = { + .name = "CAFÉ NAND", + .id_table = cafe_nand_tbl, + .probe = cafe_nand_probe, + .remove = __devexit_p(cafe_nand_remove), +#ifdef CONFIG_PMx + .suspend = cafe_nand_suspend, + .resume = cafe_nand_resume, +#endif +}; + +static int cafe_nand_init(void) +{ + return pci_register_driver(&cafe_nand_pci_driver); +} + +static void cafe_nand_exit(void) +{ + pci_unregister_driver(&cafe_nand_pci_driver); +} +module_init(cafe_nand_init); +module_exit(cafe_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); + +/* Correct ECC for 2048 bytes of 0xff: + 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ -- cgit v1.2.3 From 1ef93a0f668c8736cb6b6c3a43a5b8101efa24af Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 9 Oct 2006 01:16:38 +0200 Subject: [MTD] SSFDC must depend on BLOCK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following compile error with CONFIG_SSFDC=m, CONFIG_BLOCK=n: <-- snip --> ... CC [M] drivers/mtd/mtd_blkdevs.o /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:40: warning: ‘struct request’ declared inside parameter list /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:40: warning: its scope is only this definition or declaration, which is probably not what you want /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c: In function ‘do_blktrans_request’: /home/bunk/linux/kernel-2.6/git/linux-2.6/drivers/mtd/mtd_blkdevs.c:45: error: dereferencing pointer to incomplete type ... make[3]: *** [drivers/mtd/mtd_blkdevs.o] Error 1 <-- snip --> Bug report by Jesper Juhl. This patch also removes a pointless "default n" from the SSFDC option. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index a304b34c2632..291660abe3a6 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -265,8 +265,7 @@ config RFD_FTL config SSFDC tristate "NAND SSFDC (SmartMedia) read only translation layer" - depends on MTD - default n + depends on MTD && BLOCK help This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. -- cgit v1.2.3 From 8dd851de8184bb39c4ea86de20a0ed2496e6eb0d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 20 Oct 2006 02:11:40 +0100 Subject: =?UTF-8?q?[MTD=20NAND]=20OLPC=20CAF=C3=89=20driver=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix OOB handling, bad block table marker placement - Some cleanups, enable runtime-optional debugging - Allow BBT stuff to be skipped Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 135 +++++++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 36 deletions(-) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 8d7a795a9478..60cb019b1406 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -5,7 +5,7 @@ * Copyright © 2006 David Woodhouse */ -//#define DEBUG +#define DEBUG #include #undef DEBUG @@ -53,15 +53,24 @@ struct cafe_priv { static int usedma = 1; module_param(usedma, int, 0644); +static int skipbbt = 0; +module_param(skipbbt, int, 0644); + +static int debug = 0; +module_param(debug, int, 0644); + +#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) + + static int cafe_device_ready(struct mtd_info *mtd) { struct cafe_priv *cafe = mtd->priv; int result = !!(readl(cafe->mmio + CAFE_NAND_STATUS) | 0x40000000); - uint32_t irqs = readl(cafe->mmio + 0x10); - writel(irqs, cafe->mmio+0x10); - dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", - result?"":" not", irqs, readl(cafe->mmio + 0x10), + uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + writel(irqs, cafe->mmio+CAFE_NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n", + result?"":" not", irqs, readl(cafe->mmio + CAFE_NAND_IRQ), readl(cafe->mmio + 0x3008), readl(cafe->mmio + 0x300c)); return result; } @@ -77,7 +86,7 @@ static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); cafe->datalen += len; - dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n", len, cafe->datalen); } @@ -90,7 +99,7 @@ static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) else memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); - dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", + cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n", len, cafe->datalen); cafe->datalen += len; } @@ -101,7 +110,7 @@ static uint8_t cafe_read_byte(struct mtd_info *mtd) uint8_t d; cafe_read_buf(mtd, &d, 1); - dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); + cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d); return d; } @@ -115,14 +124,14 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, uint32_t doneint = 0x80000000; int i; - dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", + cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n", command, column, page_addr); if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) { /* Second half of a command we already calculated */ writel(cafe->ctl2 | 0x100 | command, cafe->mmio + 0x04); ctl1 = cafe->ctl1; - dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", + cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n", cafe->ctl1, cafe->nr_data); goto do_command; } @@ -163,7 +172,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) { ctl1 |= (1<<26); /* rd */ /* Always 5 bytes, for now */ - cafe->datalen = 5; + cafe->datalen = 4; /* And one address cycle -- even for STATUS, since the controller doesn't work without */ adrbytes = 1; } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || @@ -183,7 +192,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, deals with them both at once, later */ cafe->ctl1 = ctl1; cafe->ctl2 = 0; - dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", + cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n", cafe->ctl1, cafe->datalen); return; } @@ -194,13 +203,14 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); do_command: - if (cafe->datalen == 2112) - cafe->datalen = 2062; - dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", + // ECC on read only works if we ... + // if (cafe->datalen == 2112) + // cafe->datalen = 2062; + cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); /* NB: The datasheet lies -- we really should be subtracting 1 here */ writel(cafe->datalen, cafe->mmio + CAFE_NAND_DATA_LEN); - writel(0x90000000, cafe->mmio + 0x10); + writel(0x90000000, cafe->mmio + CAFE_NAND_IRQ); if (usedma && (ctl1 & (3<<25))) { uint32_t dmactl = 0xc0000000 + cafe->datalen; /* If WR or RD bits set, set up DMA */ @@ -230,20 +240,20 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ndelay(100); if (1) { - int c = 50000; + int c = 500000; uint32_t irqs; while (c--) { - irqs = readl(cafe->mmio + 0x10); + irqs = readl(cafe->mmio + CAFE_NAND_IRQ); if (irqs & doneint) break; udelay(1); - if (!(c & 1000)) - dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); + if (!(c % 100000)) + cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs); cpu_relax(); } - writel(doneint, cafe->mmio + 0x10); - dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + 0x10)); + writel(doneint, cafe->mmio + CAFE_NAND_IRQ); + cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n", command, 50000-c, irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); } @@ -276,18 +286,18 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, static void cafe_select_chip(struct mtd_info *mtd, int chipnr) { //struct cafe_priv *cafe = mtd->priv; - // dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); + // cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr); } static int cafe_nand_interrupt(int irq, void *id, struct pt_regs *regs) { struct mtd_info *mtd = id; struct cafe_priv *cafe = mtd->priv; - uint32_t irqs = readl(cafe->mmio + 0x10); - writel(irqs & ~0x90000000, cafe->mmio + 0x10); + uint32_t irqs = readl(cafe->mmio + CAFE_NAND_IRQ); + writel(irqs & ~0x90000000, cafe->mmio + CAFE_NAND_IRQ); if (!irqs) return IRQ_NONE; - dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + 0x10)); + cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, readl(cafe->mmio + CAFE_NAND_IRQ)); return IRQ_HANDLED; } @@ -335,7 +345,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, WARN_ON(chip->oob_poi != chip->buffers->oobrbuf); - dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); + cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -343,7 +353,44 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, return 0; } -static char foo[14]; +static struct nand_ecclayout cafe_oobinfo_2048 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 50}} +}; + +/* Ick. The BBT code really ought to be able to work this bit out + for itself from the above */ +static uint8_t cafe_bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t cafe_mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_bbt_pattern +}; + +static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 14, + .len = 4, + .veroffs = 18, + .maxblocks = 4, + .pattern = cafe_mirror_pattern +}; + +static struct nand_ecclayout cafe_oobinfo_512 = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = {{14, 2}} +}; + + static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { @@ -352,8 +399,7 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, WARN_ON(chip->oob_poi != chip->buffers->oobwbuf); chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, foo, 14); - // chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); /* Set up ECC autogeneration */ cafe->ctl2 |= (1<<27) | (1<<30); @@ -410,6 +456,10 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} static int __devinit cafe_nand_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -461,6 +511,11 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, /* Enable the following for a flash based bad block table */ cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + + if (skipbbt) { + cafe->nand.options |= NAND_SKIP_BBTSCAN; + cafe->nand.block_bad = cafe_nand_block_bad; + } /* Timings from Marvell's test code (not verified or calculated by us) */ writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); @@ -473,7 +528,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING2); writel(0xffffffff, cafe->mmio + CAFE_NAND_TIMING3); #endif - writel(0xdfffffff, cafe->mmio + 0x14); + writel(0xffffffff, cafe->mmio + CAFE_NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); @@ -498,18 +553,18 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, writel((cafe->dmaaddr >> 16) >> 16, cafe->mmio + 0x48); else writel(0, cafe->mmio + 0x48); - dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", readl(cafe->mmio+0x44), cafe->dmabuf); /* Enable NAND IRQ in global IRQ mask register */ writel(0x80000007, cafe->mmio + 0x300c); - dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", readl(cafe->mmio + 0x3004), readl(cafe->mmio + 0x300c)); #endif #if 1 mtd->writesize=2048; mtd->oobsize = 0x40; - memset(cafe->dmabuf, 0xa5, 2112); + memset(cafe->dmabuf, 0x5a, 2112); cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); cafe->nand.read_byte(mtd); cafe->nand.read_byte(mtd); @@ -528,7 +583,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, #if 0 writel(0x84600070, cafe->mmio); udelay(10); - dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); + cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", readl(cafe->mmio + 0x30)); #endif /* Scan to find existance of the device */ if (nand_scan_ident(mtd, 1)) { @@ -545,6 +600,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.layout = &cafe_oobinfo_2048; + cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; cafe->nand.ecc.calculate = (void *)cafe_nand_bug; cafe->nand.ecc.correct = (void *)cafe_nand_bug; @@ -564,7 +622,6 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, if (err) goto out_irq; - pci_set_drvdata(pdev, mtd); add_mtd_device(mtd); goto out; @@ -633,3 +690,9 @@ MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); /* Correct ECC for 2048 bytes of 0xff: 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ + +/* dwmw2's B-test board, in case of completely screwing it: +Bad eraseblock 2394 at 0x12b40000 +Bad eraseblock 2627 at 0x14860000 +Bad eraseblock 3349 at 0x1a2a0000 +*/ -- cgit v1.2.3 From c9073ce02adfa273a3d6d53eac8c4c035510ad9c Mon Sep 17 00:00:00 2001 From: Ryan Jackson Date: Fri, 20 Oct 2006 14:41:01 -0700 Subject: [MTD] MAPS: Add parameter to amd76xrom to override rom window size The 2 bits controlling the window size are often set to allow reading the BIOS, but too small to allow writing, since the lock registers are 4MiB lower in the address space than the data. This is intended to prevent flashing the bios, perhaps accidentally. The bits are 6 and 7. If both bits are set, it is a 5MiB window. If only the 7 Bit is set, it is a 4MiB window. Otherwise, it is a 64KiB window. This parameter allows the driver to override the BIOS settings. Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/amd76xrom.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 797caffb20b1..78b671172bb2 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,23 @@ struct amd76xrom_map_info { char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; +/* The 2 bits controlling the window size are often set to allow reading + * the BIOS, but too small to allow writing, since the lock registers are + * 4MiB lower in the address space than the data. + * + * This is intended to prevent flashing the bios, perhaps accidentally. + * + * This parameter allows the normal driver to over-ride the BIOS settings. + * + * The bits are 6 and 7. If both bits are set, it is a 5MiB window. + * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a + * 64KiB window. + * + */ +static uint win_size_bits; +module_param(win_size_bits, uint, 0); +MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x43 byte, normally set by BIOS."); + static struct amd76xrom_window amd76xrom_window = { .maps = LIST_HEAD_INIT(amd76xrom_window.maps), }; @@ -95,6 +113,16 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, /* Remember the pci dev I find the window in - already have a ref */ window->pdev = pdev; + /* Enable the selected rom window. This is often incorrectly + * set up by the BIOS, and the 4MiB offset for the lock registers + * requires the full 5MiB of window space. + * + * This 'write, then read' approach leaves the bits for + * other uses of the hardware info. + */ + pci_read_config_byte(pdev, 0x43, &byte); + pci_write_config_byte(pdev, 0x43, byte | win_size_bits ); + /* Assume the rom window is properly setup, and find it's size */ pci_read_config_byte(pdev, 0x43, &byte); if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) { @@ -129,12 +157,6 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, (unsigned long long)window->rsrc.end); } -#if 0 - - /* Enable the selected rom window */ - pci_read_config_byte(pdev, 0x43, &byte); - pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits); -#endif /* Enable writes through the rom window */ pci_read_config_byte(pdev, 0x40, &byte); -- cgit v1.2.3 From 89072ef99367cd6fab37b85d6a59a575084c2d2c Mon Sep 17 00:00:00 2001 From: Ryan Jackson Date: Fri, 20 Oct 2006 14:41:03 -0700 Subject: [MTD] CHIPS: Support for SST 49LF040B flash chip Add chip driver and JEDEC probe support for the SST 49LF040B flash chip. Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 8 ++++++-- drivers/mtd/chips/jedec_probe.c | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 702ae4cd8691..ca0882b5819f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -48,6 +48,7 @@ #define MANUFACTURER_ATMEL 0x001F #define MANUFACTURER_SST 0x00BF #define SST49LF004B 0x0060 +#define SST49LF040B 0x0050 #define SST49LF008A 0x005a #define AT49BV6416 0x00d6 @@ -233,6 +234,7 @@ static struct cfi_fixup cfi_fixup_table[] = { }; static struct cfi_fixup jedec_fixup_table[] = { { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -519,10 +521,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ goto sleep; - if (!(mode == FL_READY || mode == FL_POINT + if (!( mode == FL_READY + || mode == FL_POINT || !cfip || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) - || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) + || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1) + ))) goto sleep; /* We could check to see if we're trying to access the sector diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 1154dac715aa..63d12874590d 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -154,6 +154,7 @@ #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6 #define SST49LF004B 0x0060 +#define SST49LF040B 0x0050 #define SST49LF008A 0x005a #define SST49LF030A 0x001C #define SST49LF040A 0x0051 @@ -1400,6 +1401,20 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,64), } }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF040B, + .name = "SST 49LF040B", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, { + .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF004B, .name = "SST 49LF004B", -- cgit v1.2.3 From 29175778b07aa60e7f8030bd95d69f70070cc1f7 Mon Sep 17 00:00:00 2001 From: Lew Glendenning Date: Fri, 20 Oct 2006 14:41:04 -0700 Subject: [MTD] MAPS: Support for BIOS flash chips on Intel ESB2 southbridge Add MTD map driver for BIOS flash chips connected to the Intel ESB2 southbridge. [akpm@osdl.org: coding-style fixes, build fix] Signed-off-by: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/esb2rom.c | 449 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 459 insertions(+) create mode 100644 drivers/mtd/maps/esb2rom.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 24747bdc3e19..7514a9bee015 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -184,6 +184,15 @@ config MTD_ICHXROM BE VERY CAREFUL. +config MTD_ESB2ROM + tristate "BIOS flash chip on Intel ESB Controller Hub 2" + depends on X86 && MTD_JEDECPROBE + help + Support for treating the BIOS flash chip on ESB2 motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + config MTD_SCB2_FLASH tristate "BIOS flash chip on Intel SCB2 boards" depends on X86 && MTD_JEDECPROBE diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 191c1928bbec..9061432c5e1a 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c new file mode 100644 index 000000000000..e1c781482bfa --- /dev/null +++ b/drivers/mtd/maps/esb2rom.c @@ -0,0 +1,449 @@ +/* + * esb2rom.c + * + * Normal mappings of flash chips in physical memory + * through the Intel ESB2 Southbridge. + * + * This was derived from ichxrom.c in May 2006 by + * Lew Glendenning + * + * Eric Biederman, of course, was a major help in this effort. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_NAME KBUILD_BASENAME + +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ + +#define BIOS_CNTL 0xDC +#define BIOS_LOCK_ENABLE 0x02 +#define BIOS_WRITE_ENABLE 0x01 + +/* This became a 16-bit register, and EN2 has disappeared */ +#define FWH_DEC_EN1 0xD8 +#define FWH_F8_EN 0x8000 +#define FWH_F0_EN 0x4000 +#define FWH_E8_EN 0x2000 +#define FWH_E0_EN 0x1000 +#define FWH_D8_EN 0x0800 +#define FWH_D0_EN 0x0400 +#define FWH_C8_EN 0x0200 +#define FWH_C0_EN 0x0100 +#define FWH_LEGACY_F_EN 0x0080 +#define FWH_LEGACY_E_EN 0x0040 +/* reserved 0x0020 and 0x0010 */ +#define FWH_70_EN 0x0008 +#define FWH_60_EN 0x0004 +#define FWH_50_EN 0x0002 +#define FWH_40_EN 0x0001 + +/* these are 32-bit values */ +#define FWH_SEL1 0xD0 +#define FWH_SEL2 0xD4 + +#define FWH_8MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN) + +#define FWH_7MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN | FWH_50_EN) + +#define FWH_6MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN | FWH_60_EN) + +#define FWH_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \ + FWH_70_EN) + +#define FWH_4MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN) + +#define FWH_3_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN | FWH_C8_EN) + +#define FWH_3MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN | FWH_D0_EN) + +#define FWH_2_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \ + FWH_D8_EN) + +#define FWH_2MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN) + +#define FWH_1_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN) + +#define FWH_1MiB (FWH_F8_EN | FWH_F0_EN) + +#define FWH_0_5MiB (FWH_F8_EN) + + +struct esb2rom_window { + void __iomem* virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; + +struct esb2rom_map_info { + struct list_head list; + struct map_info map; + struct mtd_info *mtd; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; +}; + +static struct esb2rom_window esb2rom_window = { + .maps = LIST_HEAD_INIT(esb2rom_window.maps), +}; + +static void esb2rom_cleanup(struct esb2rom_window *window) +{ + struct esb2rom_map_info *map, *scratch; + u8 byte; + + /* Disable writes through the rom window */ + pci_read_config_byte(window->pdev, BIOS_CNTL, &byte); + pci_write_config_byte(window->pdev, BIOS_CNTL, + byte & ~BIOS_WRITE_ENABLE); + + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) + release_resource(&map->rsrc); + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; + } +} + +static int __devinit esb2rom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + struct esb2rom_window *window = &esb2rom_window; + struct esb2rom_map_info *map = NULL; + unsigned long map_top; + u8 byte; + u16 word; + + /* For now I just handle the ecb2 and I assume there + * are not a lot of resources up at the top of the address + * space. It is possible to handle other devices in the + * top 16MiB but it is very painful. Also since + * you can only really attach a FWH to an ICHX there + * a number of simplifications you can make. + * + * Also you can page firmware hubs if an 8MiB window isn't enough + * but don't currently handle that case either. + */ + window->pdev = pdev; + + /* RLG: experiment 2. Force the window registers to the widest values */ + +/* + pci_read_config_word(pdev, FWH_DEC_EN1, &word); + printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word); + pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff); + pci_read_config_byte(pdev, FWH_DEC_EN1, &byte); + printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte); + + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte); + pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f); + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte); +*/ + + /* Find a region continuous to the end of the ROM window */ + window->phys = 0; + pci_read_config_word(pdev, FWH_DEC_EN1, &word); + printk(KERN_DEBUG "pci_read_config_byte : %x\n", word); + + if ((word & FWH_8MiB) == FWH_8MiB) + window->phys = 0xff400000; + else if ((word & FWH_7MiB) == FWH_7MiB) + window->phys = 0xff500000; + else if ((word & FWH_6MiB) == FWH_6MiB) + window->phys = 0xff600000; + else if ((word & FWH_5MiB) == FWH_5MiB) + window->phys = 0xFF700000; + else if ((word & FWH_4MiB) == FWH_4MiB) + window->phys = 0xffc00000; + else if ((word & FWH_3_5MiB) == FWH_3_5MiB) + window->phys = 0xffc80000; + else if ((word & FWH_3MiB) == FWH_3MiB) + window->phys = 0xffd00000; + else if ((word & FWH_2_5MiB) == FWH_2_5MiB) + window->phys = 0xffd80000; + else if ((word & FWH_2MiB) == FWH_2MiB) + window->phys = 0xffe00000; + else if ((word & FWH_1_5MiB) == FWH_1_5MiB) + window->phys = 0xffe80000; + else if ((word & FWH_1MiB) == FWH_1MiB) + window->phys = 0xfff00000; + else if ((word & FWH_0_5MiB) == FWH_0_5MiB) + window->phys = 0xfff80000; + + /* reserved 0x0020 and 0x0010 */ + window->phys -= 0x400000UL; + window->size = (0xffffffffUL - window->phys) + 1UL; + + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, BIOS_CNTL, &byte); + if (!(byte & BIOS_WRITE_ENABLE) && (byte & (BIOS_LOCK_ENABLE))) { + /* The BIOS will generate an error if I enable + * this device, so don't even try. + */ + printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n"); + goto out; + } + pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE); + + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to the window being "reseved" by the BIOS. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_DEBUG MOD_NAME + ": %s(): Unable to register resource" + " 0x%.08llx-0x%.08llx - kernel bug?\n", + __func__, + (unsigned long long)window->rsrc.start, + (unsigned long long)window->rsrc.end); + } + + /* Map the firmware hub into my address space. */ + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } + + /* Get the first address to look for an rom chip at */ + map_top = window->phys; + if ((window->phys & 0x3fffff) != 0) { + /* if not aligned on 4MiB, look 4MiB lower in address space */ + map_top = window->phys + 0x400000; + } +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * (Insane hardware design, but most copied Intel's.) + * ==> Probe at most the last 4M of the address space. + */ + if (map_top < 0xffc00000) + map_top = 0xffc00000; +#endif + /* Loop through and look for rom chips */ + while ((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; + + if (!map) + map = kmalloc(sizeof(*map), GFP_KERNEL); + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* Firmware hubs only use vpp when being programmed + * in a factory setting. So in-place programming + * needs to use a different method. + */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; + } + } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; + } + } + + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) + cfi->chips[i].start += offset; + + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; + } + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; + } + + out: + /* Free any left over map structures */ + kfree(map); + + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + esb2rom_cleanup(window); + return -ENODEV; + } + return 0; +} + +static void __devexit esb2rom_remove_one (struct pci_dev *pdev) +{ + struct esb2rom_window *window = &esb2rom_window; + esb2rom_cleanup(window); +} + +static struct pci_device_id esb2rom_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, +}; + +#if 0 +MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl); + +static struct pci_driver esb2rom_driver = { + .name = MOD_NAME, + .id_table = esb2rom_pci_tbl, + .probe = esb2rom_init_one, + .remove = esb2rom_remove_one, +}; +#endif + +static int __init init_esb2rom(void) +{ + struct pci_dev *pdev; + struct pci_device_id *id; + int retVal; + + pdev = NULL; + for (id = esb2rom_pci_tbl; id->vendor; id++) { + printk(KERN_DEBUG "device id = %x\n", id->device); + pdev = pci_find_device(id->vendor, id->device, NULL); + if (pdev) { + printk(KERN_DEBUG "matched device = %x\n", id->device); + break; + } + } + if (pdev) { + printk(KERN_DEBUG "matched device id %x\n", id->device); + retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]); + printk(KERN_DEBUG "retVal = %d\n", retVal); + return retVal; + } + return -ENXIO; +#if 0 + return pci_register_driver(&esb2rom_driver); +#endif +} + +static void __exit cleanup_esb2rom(void) +{ + esb2rom_remove_one(esb2rom_window.pdev); +} + +module_init(init_esb2rom); +module_exit(cleanup_esb2rom); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lew Glendenning "); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge"); -- cgit v1.2.3 From ea7415cca922389b4f3c0cf75e0af9fbf827880e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 20 Oct 2006 14:41:05 -0700 Subject: [JFFS2] Use rb_first() and rb_last() cleanup Use rb_first() and rb_last() to implement frag_first() and frag_last(). Signed-off-by: Akinbou Mita Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/nodelist.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 0ddfd70307fb..4178b4b55948 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -294,23 +294,21 @@ static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev) static inline struct jffs2_node_frag *frag_first(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_first(root); if (!node) return NULL; - while(node->rb_left) - node = node->rb_left; + return rb_entry(node, struct jffs2_node_frag, rb); } static inline struct jffs2_node_frag *frag_last(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_last(root); if (!node) return NULL; - while(node->rb_right) - node = node->rb_right; + return rb_entry(node, struct jffs2_node_frag, rb); } -- cgit v1.2.3 From f33686b5a79674bec0e1aa553d420485e3a12899 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 20 Oct 2006 14:41:05 -0700 Subject: [MTD] JEDEC probe: fix comment typo (devic) Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 63d12874590d..58e561e87699 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1889,7 +1889,7 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) /* - * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing + * There is a BIG problem properly ID'ing the JEDEC device and guaranteeing * the mapped address, unlock addresses, and proper chip ID. This function * attempts to minimize errors. It is doubtfull that this probe will ever * be perfect - consequently there should be some module parameters that -- cgit v1.2.3 From c7438d02b384e82261e28fc280167f4e7a65e822 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 20 Oct 2006 14:41:06 -0700 Subject: [MTD] MAPS: esb2rom: use hotplug safe interfaces Fairly self explanatory. Keep a reference initially, drop it when we free up the driver resources. Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/esb2rom.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index e1c781482bfa..a9d808a617c9 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -140,8 +140,8 @@ static void esb2rom_cleanup(struct esb2rom_window *window) window->virt = NULL; window->phys = 0; window->size = 0; - window->pdev = NULL; } + pci_dev_put(window->pdev); } static int __devinit esb2rom_init_one(struct pci_dev *pdev, @@ -164,7 +164,7 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, * Also you can page firmware hubs if an 8MiB window isn't enough * but don't currently handle that case either. */ - window->pdev = pdev; + window->pdev = pci_dev_get(pdev); /* RLG: experiment 2. Force the window registers to the widest values */ @@ -418,7 +418,7 @@ static int __init init_esb2rom(void) pdev = NULL; for (id = esb2rom_pci_tbl; id->vendor; id++) { printk(KERN_DEBUG "device id = %x\n", id->device); - pdev = pci_find_device(id->vendor, id->device, NULL); + pdev = pci_get_device(id->vendor, id->device, NULL); if (pdev) { printk(KERN_DEBUG "matched device = %x\n", id->device); break; @@ -427,6 +427,7 @@ static int __init init_esb2rom(void) if (pdev) { printk(KERN_DEBUG "matched device id %x\n", id->device); retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]); + pci_dev_put(pdev); printk(KERN_DEBUG "retVal = %d\n", retVal); return retVal; } -- cgit v1.2.3 From c7cf0c68ea4e2020db7204eb0ed4412b2cbb167d Mon Sep 17 00:00:00 2001 From: Qi Yong Date: Mon, 16 Oct 2006 19:42:23 +0800 Subject: [JFFS2] Fix jffs2_follow_link() typo typo fix: noticed this typo while reading the patch "jffs2: fix symlink error handling" Signed-off-by: David Woodhouse --- fs/jffs2/symlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index fc211b6e9b03..b90d5aa3d969 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -51,7 +51,7 @@ static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) */ if (!p) { - printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); + printk(KERN_ERR "jffs2_follow_link(): can't find symlink target\n"); p = ERR_PTR(-EIO); } D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target)); -- cgit v1.2.3 From 42cb1403af8a755b3dfebeb9d2a5f73bc48832a1 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Thu, 19 Oct 2006 18:24:35 +0200 Subject: [MTD] NAND: AT91 NAND driver This version only differs from version posted by Savin Zlobec (20 Jun 2006) in that the AT91RM9200-specific chip-select / bus setup code has been moved from the at91_nand.c driver into the processor-specific file. From: Savin Zlobec Signed-off-by: Andrew Victor Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 7 ++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/at91_nand.c | 220 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 drivers/mtd/nand/at91_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1831340e5f51..b4b1656735bd 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -232,6 +232,13 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". +config MTD_NAND_AT91 + bool "Support for NAND Flash / SmartMedia on AT91" + depends on MTD_NAND && ARCH_AT91 + help + Enables support for NAND Fl