Hi,
This version fixes few bugs I discovered, especially
place where I forgot to unlock a mutex, another mistake in suspend/resume code
that prevented driver from working if card was removed during low power state.
David, merge window will soon open, so could you somehow review this?
Best regards,
Maxim Levitsky
PATCH 02/17 - blktrans: nuke mtd_blkcore_priv and make both thread and disk queue be per device by Maxim Levitsky on
2010-02-04T22:31:38+00:00
This is the biggest change. To make hotplug possible, and this layer clearer, now
mtd-blktrans-dev contains everything for a single mtd block translation device.
Also removed some very old leftovers
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
-struct mtd-blkcore-priv {
- struct task-struct *thread;
- struct request-queue *rq;
- spinlock-t queue-lock;
-};
static int do-blktrans-request(struct mtd-blktrans-ops *tr,
struct mtd-blktrans-dev *dev,
@@ -80,14 +75,13 @@ static int do-blktrans-request(struct mtd-blktrans-ops *tr,
static int mtd-blktrans-thread(void *arg)
{
- struct mtd-blktrans-ops *tr = arg;
- struct request-queue *rq = tr->blkcore-priv->rq;
+ struct mtd-blktrans-dev *dev = arg;
+ struct request-queue *rq = dev->rq;
struct request *req = NULL;
spin-lock-irq(rq->queue-lock);
while (!kthread-should-stop()) {
- struct mtd-blktrans-dev *dev;
int res;
if (!req && !(req = blk-fetch-request(rq))) {
@@ -98,13 +92,10 @@ static int mtd-blktrans-thread(void *arg)
continue;
}
- dev = req->rq-disk->private-data;
- tr = dev->tr;
-
spin-unlock-irq(rq->queue-lock);
mutex-lock(&dev->lock);
- res = do-blktrans-request(tr, dev, req);
+ res = do-blktrans-request(dev->tr, dev, req);
mutex-unlock(&dev->lock);
spin-lock-irq(rq->queue-lock);
@@ -123,8 +114,8 @@ static int mtd-blktrans-thread(void *arg)
static void mtd-blktrans-request(struct request-queue *rq)
{
- struct mtd-blktrans-ops *tr = rq->queuedata;
- wake-up-process(tr->blkcore-priv->thread);
+ struct mtd-blktrans-dev *dev = rq->queuedata;
+ wake-up-process(dev->thread);
}
@@ -214,6 +205,7 @@ int add-mtd-blktrans-dev(struct mtd-blktrans-dev *new)
struct mtd-blktrans-dev *d;
int last-devnum = -1;
struct gendisk *gd;
+ int ret;
if (mutex-trylock(&mtd-table-mutex)) {
mutex-unlock(&mtd-table-mutex);
@@ -239,12 +231,13 @@ int add-mtd-blktrans-dev(struct mtd-blktrans-dev *new)
}
last-devnum = d->devnum;
}
+
+ ret = -EBUSY;
if (new->devnum == -1)
new->devnum = last-devnum+1;
- if ((new->devnum << tr->part-bits) > 256) {
- return -EBUSY;
- }
+ if ((new->devnum << tr->part-bits) > 256)
+ goto error1;
list-add-tail(&new->list, &tr->devs);
added:
@@ -252,11 +245,16 @@ int add-mtd-blktrans-dev(struct mtd-blktrans-dev *new)
if (!tr->writesect)
new->readonly = 1;
+
+ /* Create gendisk */
+ ret = -ENOMEM;
gd = alloc-disk(1 << tr->part-bits);
- if (!gd) {
- list-del(&new->list);
- return -ENOMEM;
- }
+
+ if (!gd)
+ goto error2;
+
+ new->disk = gd;
+ gd->private-data = new;
gd->major = tr->major;
gd->first-minor = (new->devnum) << tr->part-bits;
gd->fops = &mtd-blktrans-ops;
@@ -274,13 +272,33 @@ int add-mtd-blktrans-dev(struct mtd-blktrans-dev *new)
snprintf(gd->disk-name, sizeof(gd->disk-name),
"%s%d", tr->name, new->devnum);
- /* 2.5 has capacity in units of 512 bytes while still
- having BLOCK-SIZE-BITS set to 10. Just to keep us amused. */
set-capacity(gd, (new->size * tr->blksize) >> 9);
- gd->private-data = new;
- new->blkcore-priv = gd;
- gd->queue = tr->blkcore-priv->rq;
+
+ /* Create the request queue */
+ spin-lock-init(&new->queue-lock);
+ new->rq = blk-init-queue(mtd-blktrans-request, &new->queue-lock);
+
+ if (!new->rq)
+ goto error3;
+
+ new->rq->queuedata = new;
+ blk-queue-logical-block-size(new->rq, tr->blksize);
+
+ if (tr->discard)
+ queue-flag-set-unlocked(QUEUE-FLAG-DISCARD,
+ new->rq);
+
+ gd->queue = new->rq;
+
+ /* Create processing thread */
+ /* TODO: workqueue ? */
+ new->thread = kthread-run(mtd-blktrans-thread, new,
+ "%s%d", tr->name, new->mtd->index);
+ if (IS-ERR(new->thread)) {
+ ret = PTR-ERR(new->thread);
+ goto error4;
+ }
gd->driverfs-dev = &new->mtd->dev;
if (new->readonly)
@@ -289,6 +307,15 @@ int add-mtd-blktrans-dev(struct mtd-blktrans-dev *new)
add-disk(gd);
return 0;
+error4:
+ blk-cleanup-queue(new->rq);
+error3:
+ put-disk(new->disk);
+error2:
+ list-del(&new->list);
+error1:
+ kfree(new);
+ return ret;
}
int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
@@ -300,9 +327,13 @@ int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
list-del(&old->list);
- del-gendisk(old->blkcore-priv);
- put-disk(old->blkcore-priv);
+ /* stop new requests to arrive */
+ del-gendisk(old->disk);
+
+ /* Stop the thread */
+ kthread-stop(old->thread);
+ blk-cleanup-queue(old->rq);
return 0;
}
@@ -343,9 +374,6 @@ int register-mtd-blktrans(struct mtd-blktrans-ops *tr)
if (!blktrans-notifier.list.next)
register-mtd-user(&blktrans-notifier);
- tr->blkcore-priv = kzalloc(sizeof(*tr->blkcore-priv), GFP-KERNEL);
- if (!tr->blkcore-priv)
- return -ENOMEM;
mutex-lock(&mtd-table-mutex);
@@ -353,39 +381,12 @@ int register-mtd-blktrans(struct mtd-blktrans-ops *tr)
if (ret) {
printk(KERN-WARNING "Unable to register %s block device on major %d: %d
",
tr->name, tr->major, ret);
- kfree(tr->blkcore-priv);
mutex-unlock(&mtd-table-mutex);
return ret;
}
- spin-lock-init(&tr->blkcore-priv->queue-lock);
-
- tr->blkcore-priv->rq = blk-init-queue(mtd-blktrans-request, &tr->blkcore-priv->queue-lock);
- if (!tr->blkcore-priv->rq) {
- unregister-blkdev(tr->major, tr->name);
- kfree(tr->blkcore-priv);
- mutex-unlock(&mtd-table-mutex);
- return -ENOMEM;
- }
-
- tr->blkcore-priv->rq->queuedata = tr;
- blk-queue-logical-block-size(tr->blkcore-priv->rq, tr->blksize);
- if (tr->discard)
- queue-flag-set-unlocked(QUEUE-FLAG-DISCARD,
- tr->blkcore-priv->rq);
tr->blkshift = ffs(tr->blksize) - 1;
- tr->blkcore-priv->thread = kthread-run(mtd-blktrans-thread, tr,
- "%sd", tr->name);
- if (IS-ERR(tr->blkcore-priv->thread)) {
- ret = PTR-ERR(tr->blkcore-priv->thread);
- blk-cleanup-queue(tr->blkcore-priv->rq);
- unregister-blkdev(tr->major, tr->name);
- kfree(tr->blkcore-priv);
- mutex-unlock(&mtd-table-mutex);
- return ret;
- }
-
INIT-LIST-HEAD(&tr->devs);
list-add(&tr->list, &blktrans-majors);
@@ -405,8 +406,6 @@ int deregister-mtd-blktrans(struct mtd-blktrans-ops *tr)
mutex-lock(&mtd-table-mutex);
- /* Clean up the kernel thread */
- kthread-stop(tr->blkcore-priv->thread);
/* Remove it from the list of active majors */
list-del(&tr->list);
@@ -414,13 +413,9 @@ int deregister-mtd-blktrans(struct mtd-blktrans-ops *tr)
list-for-each-entry-safe(dev, next, &tr->devs, list)
tr->remove-dev(dev);
- blk-cleanup-queue(tr->blkcore-priv->rq);
unregister-blkdev(tr->major, tr->name);
-
mutex-unlock(&mtd-table-mutex);
- kfree(tr->blkcore-priv);
-
BUG-ON(!list-empty(&tr->devs));
return 0;
}
diff int readonly;
- void *blkcore-priv; /* gendisk in 2.5, devfs-handle in 2.4 */
+ struct gendisk *disk;
+ struct task-struct *thread;
+ struct request-queue *rq;
+ spinlock-t queue-lock;
+ void *priv;
};
-struct blkcore-priv; /* Differs for 2.4 and 2.5 kernels; private */
-
struct mtd-blktrans-ops {
char *name;
int major;
@@ -60,8 +62,6 @@ struct mtd-blktrans-ops {
struct list-head devs;
struct list-head list;
struct module *owner;
-
- struct mtd-blkcore-priv *blkcore-priv;
};
extern int register-mtd-blktrans(struct mtd-blktrans-ops *tr);
PATCH 04/17 - blktrans: don't free mtd_blktrans_dev, core will do that for you by Maxim Levitsky on
2010-02-04T22:31:39+00:00
We need that structure till last user exits, and that might be years after
mtd device disappered.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff ftl-freepart((partition-t *)dev);
- kfree(dev);
}
static struct mtd-blktrans-ops ftl-tr = {
diff kfree(inftl->VUtable);
- kfree(inftl);
}
/*
diff
+ if (dev->deleted)
+ goto out;
+
if (!try-module-get(tr->owner))
goto out-tr;
@@ -168,6 +171,14 @@ static int blktrans-release(struct gendisk *disk, fmode-t mode)
module-put(tr->owner);
}
+ /* Free the private data */
+ if (dev->deleted) {
+ module-put(tr->owner);
+ mutex-unlock(&dev->lock);
+ kfree(dev);
+ return 0;
+ }
+
return ret;
}
@@ -337,6 +348,8 @@ int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
/* stop new requests to arrive */
del-gendisk(old->disk);
+ old->deleted = 1;
+
/* Stop the thread */
kthread-stop(old->thread);
diff del-mtd-blktrans-dev(dev);
- kfree(dev);
}
static struct mtd-blktrans-ops mtdblock-tr = {
diff del-mtd-blktrans-dev(dev);
- kfree(dev);
}
static struct mtd-blktrans-ops mtdblock-tr = {
diff kfree(nftl->EUNtable);
- kfree(nftl);
}
/*
diff kfree(part->blocks);
- kfree(part);
}
static struct mtd-blktrans-ops rfd-ftl-tr = {
diff kfree(ssfdc->logic-block-map);
- kfree(ssfdc);
}
static int ssfdcr-readsect(struct mtd-blktrans-dev *dev,
diff int readonly;
+ int deleted;
int open;
struct gendisk *disk;
struct task-struct *thread;
PATCH 08/17 - MTD: call remove notifiers before removing the device by Maxim Levitsky on
2010-02-04T22:31:54+00:00
Now that mtd block common layer is prepared for proper hotplug support, enable it here
Now all users of the mtd device have a chance to put the mtd device
when they are notified to do so, and they have to do so to make hotplug work.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff int ret;
+ struct mtd-notifier *not;
mutex-lock(&mtd-table-mutex);
if (mtd-table[mtd->index] != mtd) {
ret = -ENODEV;
- } else if (mtd->usecount) {
+ goto out-error;
+ }
+
+ /* No need to get a refcount on the module containing
+ the notifier, since we hold the mtd-table-mutex */
+ list-for-each-entry(not, &mtd-notifiers, list)
+ not->remove(mtd);
+
+ if (mtd->usecount) {
printk(KERN-NOTICE "Removing MTD device #%d (%s) with use count %d
",
mtd->index, mtd->name, mtd->usecount);
ret = -EBUSY;
} else {
- struct mtd-notifier *not;
-
device-unregister(&mtd->dev);
-
- /* No need to get a refcount on the module containing
- the notifier, since we hold the mtd-table-mutex */
- list-for-each-entry(not, &mtd-notifiers, list)
- not->remove(mtd);
-
mtd-table[mtd->index] = NULL;
-
module-put(THIS-MODULE);
ret = 0;
}
+out-error:
mutex-unlock(&mtd-table-mutex);
return ret;
}
PATCH 14/17 - MTD: common module for smartmedia/xD support by Maxim Levitsky on
2010-02-04T22:32:08+00:00
This small module implements few helpers that are usefull
for nand drivers for SmartMedia/xD card readers.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
+config MTD-NAND-SMARTMEDIA
+ boolean
+ default n
+
config MTD-NAND-ECC-SMC
bool "NAND ECC Smart Media byte order"
default n
@@ -25,6 +29,11 @@ config MTD-NAND-ECC-SMC
Software ECC according to the Smart Media Specification.
The original Linux implementation had byte 0 and 1 swapped.
+config MTD-SM-COMMON
+ select MTD-NAND-SMARTMEDIA
+ tristate
+ default n
+
config MTD-NAND-MUSEUM-IDS
bool "Enable chip ids for obsolete ancient NAND devices"
depends on MTD-NAND
diff obj-$(CONFIG-MTD-NAND-IDS) += nand-ids.o
+obj-$(CONFIG-MTD-SM-COMMON) += sm-common.o
obj-$(CONFIG-MTD-NAND-CAFE) += cafe-nand.o
obj-$(CONFIG-MTD-NAND-SPIA) += spia.o
diff + * Copyright (C) 2009 - Maxim Levitsky
+ * Common routines & support for xD format
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/log2.h>
+#include "sm-common.h"
+
+static struct nand-ecclayout nand-oob-sm = {
+ .eccbytes = 6,
+ .eccpos = {8, 9, 10, 13, 14, 15},
+ .oobfree = {
+ {.offset = 0 , .length = 4}, /* reserved */
+ {.offset = 6 , .length = 2}, /* LBA1 */
+ {.offset = 11, .length = 2} /* LBA2 */
+ }
+};
+
+/* Tests if block (more correctly page) is bad */
+static int sm-block-bad(struct mtd-info *mtd, loff-t ofs, int getchip)
+{
+ struct nand-chip *chip = (struct nand-chip *)mtd->priv;
+ struct mtd-oob-ops ops;
+ struct sm-oob oob;
+ int bad = 0, ret;
+
+ ops.mode = MTD-OOB-PLACE;
+ ops.ooboffs = 0;
+ ops.ooblen = SM-OOB-SIZE;
+ ops.oobbuf = (void *)&oob;
+ ops.datbuf = NULL;
+
+ if (getchip) {
+ nand-get-device(chip, mtd, FL-READING);
+ chip->select-chip(mtd, 0);
+ }
+
+ ret = nand-do-read-oob(mtd, ofs, &ops);
+ if (ret < 0 || ops.oobretlen != SM-OOB-SIZE)
+ goto out;
+
+ if (!sm-sector-valid(&oob) || !sm-block-valid(&oob)) {
+ bad = 1;
+ goto out;
+ }
+out:
+ if (getchip)
+ nand-release-device(mtd);
+ return bad;
+}
+
+/* Marks block as bad */
+static int sm-block-markbad(struct mtd-info *mtd, loff-t ofs)
+{
+ struct nand-chip *chip = (struct nand-chip *)mtd->priv;
+ struct mtd-oob-ops ops;
+ struct sm-oob oob;
+ int ret, error = 0;
+
+ memset(&oob, -1, SM-OOB-SIZE);
+ oob.data-status = 0;
+
+ ops.mode = MTD-OOB-PLACE;
+ ops.ooboffs = 0;
+ ops.ooblen = SM-OOB-SIZE;
+ ops.oobbuf = (void *)&oob;
+ ops.datbuf = NULL;
+
+ nand-get-device(chip, mtd, FL-WRITING);
+
+ ret = nand-do-write-oob(mtd, ofs, &ops);
+ if (ret < 0 || ops.oobretlen != SM-OOB-SIZE) {
+ printk(KERN-NOTICE
+ "sm-common: can't mark sector at %i as bad
",
+ (int)ofs);
+ error = -EIO;
+ } else
+ mtd->ecc-stats.badblocks++;
+
+ nand-release-device(mtd);
+ return error;
+}
+
+int sm-register-device(struct mtd-info *mtd)
+{
+ struct nand-chip *chip = (struct nand-chip *)mtd->priv;
+ int ret;
+
+ chip->options |= NAND-SKIP-BBTSCAN | NAND-SMARTMEDIA;
+
+ /* Scan for card properties */
+ ret = nand-scan-ident(mtd, 1);
+
+ if (ret)
+ return ret;
+
+ /* Set oob handling functions. */
+ if (mtd->writesize == SM-SECTOR-SIZE) {
+ chip->block-bad = sm-block-bad;
+ chip->block-markbad = sm-block-markbad;
+ chip->ecc.layout = &nand-oob-sm;
+
+ /* SmartMedia on small page nand, has page depedent oob layout,
+ thus let FTL do that hard job */
+ } else if (mtd->writesize != SM-SMALL-PAGE)
+ return -ENODEV;
+
+ ret = nand-scan-tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = add-mtd-device(mtd);
+ if (ret)
+ return ret;
+ return 0;
+}
+EXPORT-SYMBOL-GPL(sm-register-device);
+
+MODULE-LICENSE("GPL");
+MODULE-AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE-DESCRIPTION("Common SmartMedia/xD functions");
diff + * Copyright (C) 2009 - Maxim Levitsky
+ * Common routines & support for SmartMedia/xD format
+ *
+ * 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.
+ */
+
+#include <linux/mtd/nand.h>
+
+/* Full oob structure as written on the flash */
+struct sm-oob {
+ u32 reserved;
+ u8 data-status;
+ u8 block-status;
+ u8 lba-copy1[2];
+ u8 ecc2[3];
+ u8 lba-copy2[2];
+ u8 ecc1[3];
+} +#define SM-OOB-SIZE 16
+
+/* This is maximum zone size, and all devices that have more that one zone
+ have this size */
+#define SM-MAX-ZONE-SIZE 1024
+
+/* support for small page nand */
+#define SM-SMALL-PAGE 256
+#define SM-SMALL-OOB-SIZE 8
+
+
+extern int sm-register-device(struct mtd-info *mtd);
+
+
+inline int sm-sector-valid(struct sm-oob *oob)
+{
+ return hweight16(oob->data-status) >= 5;
+}
+
+inline int sm-block-valid(struct sm-oob *oob)
+{
+ return hweight16(oob->block-status) >= 7;
+}
+
+inline int sm-block-erased(struct sm-oob *oob)
+{
+ static const u32 erased-pattern[4] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+
+ /* First test for erased block */
+ if (!memcmp(oob, erased-pattern, sizeof(*oob)))
+ return 1;
+ return 0;
+}
PATCH 16/17 - MTD: Add nand driver for ricoh xD/SmartMedia reader by Maxim Levitsky on
2010-02-04T22:32:25+00:00
This adds a driver for Ricoh xD card reader with PCI id 0x0852
Since the reader is a part of larger mulifunction chip, it
is hard to determine the correct model name. Might be R5C852.
Driver is complete, but bewere of the fact that some
(probably only type M) xD cards are 'fake' which means that
they have an on board CPU and expose emulated nand command set
These cards don't even store the oob area on the flash,
but generate it on the fly from something else.
Thus they demand to have proper values written in the oob area,
and therefore only useful with SmartMedia FTL.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
+RICOH SMARTMEDIA/XD DRIVER
+M: Maxim Levitsky <maximlevitsky@gmail.com>
+S: Maintained
+F: drivers/mtd/nand/r822.c
+F: drivers/mtd/nand/r822.h
+
RISCOM8 DRIVER
S: Orphan
F: Documentation/serial/riscom8.txt
diff
+config MTD-NAND-RICOH
+ tristate "Ricoh xD card reader"
+ default n
+ select MTD-SM-COMMON
+ help
+ Enable support for Ricoh xD card reader
+ You also need to enable ether
+ NAND SSFDC (SmartMedia) read only translation layer' or new
+ expermental, readwrite
+ 'SmartMedia/xD new translation layer'
+
config MTD-NAND-AU1550
tristate "Au1550/1200 NAND support"
depends on SOC-AU1200 || SOC-AU1550
diff obj-$(CONFIG-MTD-NAND-BCM-UMI) += bcm-umi-nand.o nand-bcm-umi.o
+obj-$(CONFIG-MTD-NAND-RICOH) += r822.o
nand-objs := nand-base.o nand-bbt.o
diff + * Copyright (C) 2009 - Maxim Levitsky
+ * driver for Ricoh xD readers
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/pci-ids.h>
+#include <asm/byteorder.h>
+#include <linux/sched.h>
+#include "sm-common.h"
+#include "r822.h"
+
+
+static int enable-dma = 1;
+module-param(enable-dma, bool, S-IRUGO);
+MODULE-PARM-DESC(enable-dma, "Enable usage of DMA (default)");
+
+
+/* read register */
+static inline u8 r822-read-reg(struct r822-device *dev, int address)
+{
+ u8 reg = readb(dev->mmio + address);
+ return reg;
+}
+
+/* write register */
+static inline void r822-write-reg(struct r822-device *dev,
+ int address, u8 value)
+{
+ writeb(value, dev->mmio + address);
+}
+
+
+/* read dword sized register */
+static inline u32 r822-read-reg-dword(struct r822-device *dev, int address)
+{
+ u32 reg = le32-to-cpu(readl(dev->mmio + address));
+ return reg;
+}
+
+/* write dword sized register */
+static inline void r822-write-reg-dword(struct r822-device *dev,
+ int address, u32 value)
+{
+ writel(cpu-to-le32(value), dev->mmio + address);
+}
+
+/* returns pointer to our private structure */
+static inline struct r822-device *r822-get-dev(struct mtd-info *mtd)
+{
+ struct nand-chip *chip = (struct nand-chip *)mtd->priv;
+ return (struct r822-device *)chip->priv;
+}
+
+
+/* check if controller supports dma */
+static void r822-dma-test(struct r822-device *dev)
+{
+ dev->dma-usable = (r822-read-reg(dev, R822-DMA-CAP) &
+ (R822-DMA1 | R822-DMA2)) == (R822-DMA1 | R822-DMA2);
+
+ if (!dev->dma-usable)
+ dbg("Non dma capable device detected, dma disabled");
+
+ if (!enable-dma) {
+ dbg("disabling dma on user request");
+ dev->dma-usable = 0;
+ }
+}
+
+/*
+ * Enable dma. Enables ether first or second stage of the DMA,
+ * Expects dev->dma-dir and dev->dma-state be set
+ */
+static void r822-dma-enable(struct r822-device *dev)
+{
+ u8 dma-reg = dev->dma-dir ? R822-DMA-READ : 0;
+
+ if (dev->dma-state == DMA-INTERNAL)
+ dma-reg |= R822-DMA-INTERNAL;
+ else {
+ dma-reg |= R822-DMA-MEMORY;
+ r822-write-reg-dword(dev, R822-DMA-ADDR,
+ cpu-to-le32(dev->phys-dma-addr));
+ }
+
+ r822-write-reg(dev, R822-DMA-IRQ-STA,
+ r822-read-reg(dev, R822-DMA-IRQ-STA));
+
+ r822-write-reg(dev, R822-DMA-SETTINGS, dma-reg);
+ r822-write-reg(dev, R822-DMA-IRQ-ENABLE,
+ R822-DMA-IRQ-INTERNAL |
+ R822-DMA-IRQ-ERROR |
+ R822-DMA-IRQ-MEMORY);
+}
+
+/*
+ * Disable dma, called from the interrupt handler, which specifies
+ * success of the operation via 'error' argument
+ */
+static void r822-dma-done(struct r822-device *dev, int error)
+{
+ WARN-ON(dev->dma-stage == 0);
+
+ if (error)
+ dbg("dma: complete with error");
+
+ r822-write-reg(dev, R822-DMA-IRQ-STA,
+ r822-read-reg(dev, R822-DMA-IRQ-STA));
+
+ r822-write-reg(dev, R822-DMA-SETTINGS, 0);
+ r822-write-reg(dev, R822-DMA-IRQ-ENABLE, 0);
+
+ dev->dma-error = error;
+ dev->dma-stage = 0;
+
+ if (dev->phys-dma-addr && dev->phys-dma-addr != dev->phys-bounce-buffer)
+ pci-unmap-single(dev->pci-dev, dev->phys-dma-addr, R822-DMA-LEN,
+ dev->dma-dir ? PCI-DMA-FROMDEVICE : PCI-DMA-TODEVICE);
+ complete(&dev->dma-done);
+}
+
+/*
+ * Wait, till dma is done, which includes both phases of it
+ */
+static int r822-dma-wait(struct r822-device *dev)
+{
+ long timeout = wait-for-completion-timeout(&dev->dma-done,
+ msecs-to-jiffies(1000));
+ if (!timeout)
+ return -ETIMEDOUT;
+ return 0;
+}
+
+/*
+ * Read/Write one page using dma. Only pages can be read (512 bytes)
+*/
+static void r822-do-dma(struct r822-device *dev, uint8-t *buf, int do-read)
+{
+ int bounce = 0;
+ unsigned long flags;
+ int error;
+
+ dev->dma-error = 0;
+
+ /* Set dma direction */
+ dev->dma-dir = do-read;
+ dev->dma-stage = 1;
+
+ /* Set intial dma state: for reading first fill on board buffer,
+ from device, for writes first fill the buffer from memory*/
+ dev->dma-state = do-read ? DMA-INTERNAL : DMA-MEMORY;
+
+ /* if incoming buffer is not page aligned, we should do bounce */
+ if ((unsigned long)buf & (R822-DMA-LEN-1))
+ bounce = 1;
+
+ if (!bounce) {
+ dev->phys-dma-addr = pci-map-single(dev->pci-dev, (void *)buf,
+ R822-DMA-LEN,
+ (do-read ? PCI-DMA-FROMDEVICE : PCI-DMA-TODEVICE));
+
+ if (dev->phys-dma-addr == DMA-ERROR-CODE)
+ bounce = 1;
+ }
+
+ if (bounce) {
+ dev->phys-dma-addr = dev->phys-bounce-buffer;
+ if (!do-read)
+ memcpy(dev->bounce-buffer, buf, R822-DMA-LEN);
+ }
+
+ /* Enable DMA */
+ spin-lock-irqsave(&dev->irqlock, flags);
+ r822-dma-enable(dev);
+ spin-unlock-irqrestore(&dev->irqlock, flags);
+
+ /* Wait till complete */
+ error = r822-dma-wait(dev);
+
+ if (error) {
+ r822-dma-done(dev, error);
+ return;
+ }
+
+ if (do-read && bounce)
+ memcpy((void *)buf, dev->bounce-buffer, R822-DMA-LEN);
+}
+
+/*
+ * Program data lines of the nand chip to send data to it
+ */
+void r822-write-buf(struct mtd-info *mtd, const uint8-t *buf, int len)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+ u32 reg;
+
+ /* Don't allow any access to hardware if we suspect card removal */
+ if (dev->card-unstable)
+ return;
+
+ /* Special case for whole sector read */
+ if (len == R822-DMA-LEN && dev->dma-usable) {
+ r822-do-dma(dev, (uint8-t *)buf, 0);
+ return;
+ }
+
+ /* write DWORD chinks - faster */
+ while (len) {
+ reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
+ r822-write-reg-dword(dev, R822-DATALINE, reg);
+ buf += 4;
+ len -= 4;
+
+ }
+
+ /* write rest */
+ while (len)
+ r822-write-reg(dev, R822-DATALINE, *buf++);
+}
+
+/*
+ * Read data lines of the nand chip to retrieve data
+ */
+void r822-read-buf(struct mtd-info *mtd, uint8-t *buf, int len)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+ u32 reg;
+
+ if (dev->card-unstable) {
+ /* since we can't signal error here, at least, return
+ predictable buffer */
+ memset(buf, 0, len);
+ return;
+ }
+
+ /* special case for whole sector read */
+ if (len == R822-DMA-LEN && dev->dma-usable) {
+ r822-do-dma(dev, buf, 1);
+ return;
+ }
+
+ /* read in dword sized chunks */
+ while (len >= 4) {
+
+ reg = r822-read-reg-dword(dev, R822-DATALINE);
+ *buf++ = reg & 0xFF;
+ *buf++ = (reg >> 8) & 0xFF;
+ *buf++ = (reg >> 16) & 0xFF;
+ *buf++ = (reg >> 24) & 0xFF;
+ len -= 4;
+ }
+
+ /* read the reset by bytes */
+ while (len+static uint8-t r822-read-byte(struct mtd-info *mtd)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+
+ /* Same problem as in r822-read-buf.... */
+ if (dev->card-unstable)
+ return 0;
+
+ return r822-read-reg(dev, R822-DATALINE);
+}
+
+
+/*
+ * Readback the buffer to verify it
+ */
+int r822-verify-buf(struct mtd-info *mtd, const uint8-t *buf, int len)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+
+ /* We can't be sure about anything here... */
+ if (dev->card-unstable)
+ return -1;
+
+ /* This will never happen, unless you wired up a nand chip
+ with > 512 bytes page to the reader */
+ if (len > SM-SECTOR-SIZE)
+ return 0;
+
+ r822-read-buf(mtd, dev->tmp-buffer, len);
+ return memcmp(buf, dev->tmp-buffer, len);
+}
+
+/*
+ * Control several chip lines & send commands
+ */
+void r822-cmdctl(struct mtd-info *mtd, int dat, unsigned int ctrl)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+
+ if (dev->card-unstable)
+ return;
+
+ if (ctrl & NAND-CTRL-CHANGE) {
+
+ dev->ctlreg &= ~(R822-CTL-DATA | R822-CTL-COMMAND |
+ R822-CTL-ON | R822-CTL-CARDENABLE);
+
+ if (ctrl & NAND-ALE)
+ dev->ctlreg |= R822-CTL-DATA;
+
+ if (ctrl & NAND-CLE)
+ dev->ctlreg |= R822-CTL-COMMAND;
+
+ if (ctrl & NAND-NCE)
+ dev->ctlreg |= (R822-CTL-CARDENABLE | R822-CTL-ON);
+ else
+ dev->ctlreg &= ~R822-CTL-WRITE;
+
+ /* when write is stareted, enable write access */
+ if (dat == NAND-CMD-ERASE1)
+ dev->ctlreg |= R822-CTL-WRITE;
+
+ r822-write-reg(dev, R822-CTL, dev->ctlreg);
+ }
+
+
+ /* HACK: NAND-CMD-SEQIN is called without NAND-CTRL-CHANGE, but we need
+ to set write mode */
+ if (dat == NAND-CMD-SEQIN && (dev->ctlreg & R822-CTL-COMMAND)) {
+ dev->ctlreg |= R822-CTL-WRITE;
+ r822-write-reg(dev, R822-CTL, dev->ctlreg);
+ }
+
+ if (dat != NAND-CMD-NONE)
+ r822-write-reg(dev, R822-DATALINE, dat);
+}
+
+/*
+ * Wait till card is ready.
+ * based on nand-wait, but returns errors if DMA error happened
+ */
+int r822-wait(struct mtd-info *mtd, struct nand-chip *chip)
+{
+ struct r822-device *dev = (struct r822-device *)chip->priv;
+
+ unsigned long timeout;
+ int status;
+
+ timeout = jiffies + (chip->state == FL-ERASING ?
+ msecs-to-jiffies(400) : msecs-to-jiffies(20));
+
+ while (time-before(jiffies, timeout))
+ if (chip->dev-ready(mtd))
+ break;
+
+ chip->cmdfunc(mtd, NAND-CMD-STATUS, -1, -1);
+ status = (int)chip->read-byte(mtd);
+
+ /* Unfortunelly, no way to send detailed error status... */
+ if (dev->dma-error) {
+ status |= NAND-STATUS-FAIL;
+ dev->dma-error = 0;
+ }
+ return status;
+}
+
+/*
+ * Check if card is ready
+ */
+
+int r822-ready(struct mtd-info *mtd)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+ return !(r822-read-reg(dev, R822-CARD-STA) & R822-CARD-STA-BUSY);
+}
+
+
+/*
+ * Set ECC engine mode
+*/
+
+void r822-ecc-hwctl(struct mtd-info *mtd, int mode)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+
+ if (dev->card-unstable)
+ return;
+
+ switch (mode) {
+ case NAND-ECC-READ:
+ case NAND-ECC-WRITE:
+ /* enable ecc generation/check*/
+ dev->ctlreg |= R822-CTL-ECC-ENABLE;
+
+ /* flush ecc buffer */
+ r822-write-reg(dev, R822-CTL,
+ dev->ctlreg | R822-CTL-ECC-ACCESS);
+
+ r822-read-reg(dev, R822-DATALINE);
+ r822-write-reg(dev, R822-CTL, dev->ctlreg);
+ return;
+
+ case NAND-ECC-READSYN:
+ /* disable ecc generation */
+ dev->ctlreg &= ~R822-CTL-ECC-ENABLE;
+ r822-write-reg(dev, R822-CTL, dev->ctlreg);
+ }
+}
+
+/*
+ * Calculate ECC, only used for writes
+ */
+
+int r822-ecc-calculate(struct mtd-info *mtd, const uint8-t *dat,
+ uint8-t *ecc-code)
+{
+ struct r822-device *dev = r822-get-dev(mtd);
+ struct sm-oob *oob = (struct sm-oob *)ecc-code;
+ u32 ecc1, ecc2;
+
+ if (dev->card-unstable)
+ return 0;
+
+ dev->ctlreg &= ~R822-CTL-ECC-ENABLE;
+ r822-write-reg(dev, R822-CTL, dev->ctlreg | R822-CTL-ECC-ACCESS);
+
+ ecc1 = r822-read-reg-dword(dev, R822-DATALINE);
+ ecc2 = r822-read-reg-dword(dev, R822-DATALINE);
+
+ oob->ecc1[0] = (ecc1) & 0xFF;
+ oob->ecc1[1] = (ecc1 >> 8) & 0xFF;
+ oob->ecc1[2] = (ecc1 >> 16) & 0xFF;
+
+ oob->ecc2[0] = (ecc2) & 0xFF;
+ oob->ecc2[1] = (ecc2 >> 8) & 0xFF;
+ oob->ecc2[2] = (ecc2 >> 16) & 0xFF;
+
+ r822-write-reg(dev, R822-CTL, dev->ctlreg);
+ return 0;
+}
+
+/*
+ * Correct the data using ECC, hw did almost everything for us
+ */
+
+int r822-ecc-correct(struct mtd-info *mtd, uint8-t *dat,
+ uint8-t *read-ecc, uint8-t *calc-ecc)
+{
+ u16 ecc-reg;
+ u8 ecc-status, err-byte;
+ int i, error = 0;
+
+ struct r822-device *dev = r822-get-dev(mtd);
+
+ if (dev->card-unstable)
+ return 0;
+
+ r822-write-reg(dev, R822-CTL, dev->ctlreg | R822-CTL-ECC-ACCESS);
+ ecc-reg = r822-read-reg-dword(dev, R822-DATALINE);
+ r822-write-reg(dev, R822-CTL, dev->ctlreg);
+
+ for (i = 0 ; i <= 1 ; i++) {
+
+ ecc-status = (ecc-reg >> 8) & 0xFF;
+
+ /* ecc uncorrectable error */
+ if (ecc-status & R822-ECC-FAIL) {
+ dbg("ecc: unrecoverable error, in half %d", i);
+ error = -1;
+ goto exit;
+ }
+
+ /* correctable error */
+ if (ecc-status & R822-ECC-CORRECTABLE) {
+
+ err-byte = ecc-reg & 0xFF;
+ dbg("ecc: recoverable error, "
+ "in half %d, byte %d, bit %d", i,
+ err-byte, ecc-status & R822-ECC-ERR-BIT-MSK);
+
+ dat[err-byte] ^=
+ 1 << (ecc-status & R822-ECC-ERR-BIT-MSK);
+ error++;
+ }
+
+ dat += 256;
+ ecc-reg >>= 16;
+ }
+exit:
+ return error;
+}
+
+/*
+ * This is copy of nand-read-oob-std
+ * nand-read-oob-syndrome assumes we can send column address - we can't
+ */
+static int r822-read-oob(struct mtd-info *mtd, struct nand-chip *chip,
+ int page, int sndcmd)
+{
+ if (sndcmd) {
+ chip->cmdfunc(mtd, NAND-CMD-READOOB, 0, page);
+ sndcmd = 0;
+ }
+ chip->read-buf(mtd, chip->oob-poi, mtd->oobsize);
+ return sndcmd;
+}
+
+/*
+ * Start the hardware
+ */
+
+void r822-device-start(struct r822-device *dev)
+{
+ if (r822-read-reg(dev, R822-HW) & R822-HW-UNKNOWN) {
+ r822-write-reg(dev, R822-CTL, R822-CTL-RESET | R822-CTL-ON);
+ r822-write-reg-dword(dev, R822-HW, R822-HW-ENABLED);
+ } else {
+ r822-write-reg(dev, R822-HW, R822-HW-ENABLED);
+ r822-write-reg(dev, R822-CTL, R822-CTL-RESET | R822-CTL-ON);
+ r822-write-reg(dev, R822-CTL, 0);
+ }
+ msleep(200);
+}
+
+
+/*
+ * Shutdown the hardware
+ */
+
+void r822-device-shutdown(struct r822-device *dev)
+{
+ r822-write-reg(dev, R822-HW, 0);
+ r822-write-reg(dev, R822-CTL, R822-CTL-RESET);
+}
+
+/*
+ * Test if card is present
+ */
+
+void r822-card-update-present(struct r822-device *dev)
+{
+ unsigned long flags;
+ u8 reg;
+
+ spin-lock-irqsave(&dev->irqlock, flags);
+ reg = r822-read-reg(dev, R822-CARD-STA);
+ dev->card-detected = !!(reg & R822-CARD-STA-PRESENT);
+ spin-unlock-irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Update card detection IRQ state according to current card state
+ * which is read in r822-card-update-present
+ */
+void r822-update-card-detect(struct r822-device *dev)
+{
+ int card-detect-reg = R822-CARD-IRQ-GENABLE;
+ card-detect-reg |= dev->card-detected ?
+ R822-CARD-IRQ-REMOVE : R822-CARD-IRQ-INSERT;
+
+ r822-write-reg(dev, R822-CARD-IRQ-ENABLE, card-detect-reg);
+}
+
+ssize-t r822-media-type-show(struct device *sys-dev,
+ struct device-attribute *attr, char *buf)
+{
+ struct mtd-info *mtd = container-of(sys-dev, struct mtd-info, dev);
+ struct r822-device *dev = r822-get-dev(mtd);
+ char *data = dev->sm ? "smartmedia" : "xd";
+
+ strcpy(buf, data);
+ return strlen(data);
+}
+
+DEVICE-ATTR(media-type, S-IRUGO, r822-media-type-show, NULL);
+
+
+/* Detect properties of card in slot */
+void r822-update-media-status(struct r822-device *dev)
+{
+ u8 reg;
+ unsigned long flags;
+ int readonly;
+
+ spin-lock-irqsave(&dev->irqlock, flags);
+ if (!dev->card-detected) {
+ dbg("card removed");
+ spin-unlock-irqrestore(&dev->irqlock, flags);
+ return ;
+ }
+
+ readonly = r822-read-reg(dev, R822-CARD-STA) & R822-CARD-STA-RO;
+ reg = r822-read-reg(dev, R822-DMA-CAP);
+ dev->sm = (reg & (R822-DMA1 | R822-DMA2)) && (reg & R822-SMBIT);
+
+ dbg("detected %s %s card in slot",
+ dev->sm ? "SmartMedia" : "xD",
+ readonly ? "readonly" : "writeable");
+
+ dev->readonly = readonly;
+ spin-unlock-irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Register the nand device
+ * Called when the card is detected
+ */
+int r822-register-nand-device(struct r822-device *dev)
+{
+ dev->mtd = kzalloc(sizeof(struct mtd-info), GFP-KERNEL);
+
+ if (!dev->mtd)
+ goto error1;
+
+ WARN-ON(dev->card-registred);
+
+ dev->mtd->owner = THIS-MODULE;
+ dev->mtd->priv = dev->chip;
+ dev->mtd->dev.parent = &dev->pci-dev->dev;
+
+ if (dev->readonly)
+ dev->chip->options |= NAND-ROM;
+
+ r822-device-start(dev);
+ if (sm-register-device(dev->mtd))
+ goto error2;
+
+ sysfs-create-file(&dev->mtd->dev.kobj, &dev-attr-media-type.attr);
+
+ dev->card-registred = 1;
+ return 0;
+error2:
+ kfree(dev->mtd);
+error1:
+ /* Force card redetect */
+ dev->card-detected = 0;
+ return -1;
+}
+
+/*
+ * Unregister the card
+ */
+
+void r822-unregister-nand-device(struct r822-device *dev)
+{
+ if (!dev->card-registred)
+ return;
+
+
+ nand-release(dev->mtd);
+ r822-device-shutdown(dev);
+ dev->card-registred = 0;
+
+ sysfs-remove-file(&dev->mtd->dev.kobj, &dev-attr-media-type.attr);
+ kfree(dev->mtd);
+ dev->mtd = NULL;
+}
+
+
+/* Card state updater */
+void r822-card-detect-work(struct work-struct *work)
+{
+ struct r822-device *dev =
+ container-of(work, struct r822-device, card-detect-work.work);
+
+ r822-card-update-present(dev);
+ dev->card-unstable = 0;
+
+ /* false alarm */
+ if (dev->card-detected == dev->card-registred)
+ goto exit;
+
+ /* Read media properties */
+ r822-update-media-status(dev);
+
+ /* Register the card */
+ if (dev->card-detected)
+ r822-register-nand-device(dev);
+ else
+ r822-unregister-nand-device(dev);
+exit:
+ /* Update detection logic */
+ r822-update-card-detect(dev);
+}
+
+
+/* Ack + disable IRQ generation */
+static void r822-disable-irqs(struct r822-device *dev)
+{
+ u8 reg;
+ reg = r822-read-reg(dev, R822-CARD-IRQ-ENABLE);
+ r822-write-reg(dev, R822-CARD-IRQ-ENABLE, reg & ~R822-CARD-IRQ-MASK);
+
+ reg = r822-read-reg(dev, R822-DMA-IRQ-ENABLE);
+ r822-write-reg(dev, R822-DMA-IRQ-ENABLE, reg & ~R822-DMA-IRQ-MASK);
+
+ reg = r822-read-reg(dev, R822-CARD-IRQ-STA);
+ r822-write-reg(dev, R822-CARD-IRQ-STA, reg);
+
+ reg = r822-read-reg(dev, R822-DMA-IRQ-STA);
+ r822-write-reg(dev, R822-DMA-IRQ-STA, reg);
+
+}
+
+/* Interrupt handler */
+static irqreturn-t r822-irq(int irq, void *data)
+{
+ struct r822-device *dev = (struct r822-device *)data;
+
+ u8 card-status, dma-status;
+ unsigned long flags;
+ irqreturn-t ret = IRQ-NONE;
+
+ spin-lock-irqsave(&dev->irqlock, flags);
+
+ /* We can recieve shared interrupt while pci is suspended
+ in that case reads will return 0xFFFFFFFF.... */
+ if (dev->insuspend)
+ goto out;
+
+ /* handle card detection interrupts first */
+ card-status = r822-read-reg(dev, R822-CARD-IRQ-STA);
+ r822-write-reg(dev, R822-CARD-IRQ-STA, card-status);
+
+ if (card-status & (R822-CARD-IRQ-INSERT|R822-CARD-IRQ-REMOVE)) {
+
+ ret = IRQ-HANDLED;
+ dev->card-detected = !!(card-status & R822-CARD-IRQ-INSERT);
+
+ /* we shouldn't recieve any interrupts if we wait for card
+ to settle */
+ WARN-ON(dev->card-unstable);
+
+ /* disable irqs while card is unstable */
+ /* this will timeout DMA if active, but better that garbage */
+ r822-disable-irqs(dev);
+
+ if (dev->card-unstable)
+ goto out;
+
+ /* let, card state to settle a bit, and then do the work */
+ dev->card-unstable = 1;
+ queue-delayed-work(dev->card-workqueue,
+ &dev->card-detect-work, msecs-to-jiffies(100));
+ goto out;
+ }
+
+
+ /* Handle dma interrupts */
+ dma-status = r822-read-reg(dev, R822-DMA-IRQ-STA);
+ r822-write-reg(dev, R822-DMA-IRQ-STA, dma-status);
+
+ if (dma-status & R822-DMA-IRQ-MASK) {
+
+ ret = IRQ-HANDLED;
+
+ if (dma-status & R822-DMA-IRQ-ERROR) {
+ dbg("recieved dma error IRQ");
+ r822-dma-done(dev, -EIO);
+ goto out;
+ }
+
+ /* recieved DMA interrupt out of nowhere? */
+ WARN-ON-ONCE(dev->dma-stage == 0);
+
+ if (dev->dma-stage == 0)
+ goto out;
+
+ /* done device access */
+ if (dev->dma-state == DMA-INTERNAL &&
+ (dma-status & R822-DMA-IRQ-INTERNAL)) {
+
+ dev->dma-state = DMA-MEMORY;
+ dev->dma-stage++;
+ }
+
+ /* done memory DMA */
+ if (dev->dma-state == DMA-MEMORY &&
+ (dma-status & R822-DMA-IRQ-MEMORY)) {
+ dev->dma-state = DMA-INTERNAL;
+ dev->dma-stage++;
+ }
+
+ /* Enable 2nd half of dma dance */
+ if (dev->dma-stage == 2)
+ r822-dma-enable(dev);
+
+ /* Operation done */
+ if (dev->dma-stage == 3)
+ r822-dma-done(dev, 0);
+ goto out;
+ }
+
+ /* Handle unknown interrupts */
+ if (dma-status)
+ dbg("bad dma IRQ status = %x", dma-status);
+
+ if (card-status & ~R822-CARD-STA-CD)
+ dbg("strange card status = %x", card-status);
+
+out:
+ spin-unlock-irqrestore(&dev->irqlock, flags);
+ return ret;
+}
+
+int r822-probe(struct pci-dev *pci-dev, const struct pci-device-id *id)
+{
+ int error;
+ struct nand-chip *chip;
+ struct r822-device *dev;
+
+ /* pci initialization */
+ error = pci-enable-device(pci-dev);
+
+ if (error)
+ goto error1;
+
+ pci-set-master(pci-dev);
+
+ error = pci-set-dma-mask(pci-dev, DMA-32BIT-MASK);
+ if (error)
+ goto error2;
+
+ error = pci-request-regions(pci-dev, DRV-NAME);
+
+ if (error)
+ goto error3;
+
+ error = -ENOMEM;
+
+ /* init nand chip, but register it only on card insert */
+ chip = kzalloc(sizeof(struct nand-chip), GFP-KERNEL);
+
+ if (!chip)
+ goto error4;
+
+ /* commands */
+ chip->cmd-ctrl = r822-cmdctl;
+ chip->waitfunc = r822-wait;
+ chip->dev-ready = r822-ready;
+
+ /* I/O */
+ chip->read-byte = r822-read-byte;
+ chip->read-buf = r822-read-buf;
+ chip->write-buf = r822-write-buf;
+ chip->verify-buf = r822-verify-buf;
+
+ /* ecc */
+ chip->ecc.mode = NAND-ECC-HW-SYNDROME;
+ chip->ecc.size = R822-DMA-LEN;
+ chip->ecc.bytes = SM-OOB-SIZE;
+ chip->ecc.hwctl = r822-ecc-hwctl;
+ chip->ecc.calculate = r822-ecc-calculate;
+ chip->ecc.correct = r822-ecc-correct;
+
+ /* TODO: hack */
+ chip->ecc.read-oob = r822-read-oob;
+
+ /* init our device structure */
+ dev = kzalloc(sizeof(struct r822-device), GFP-KERNEL);
+
+ if (!dev)
+ goto error5;
+
+ chip->priv = dev;
+ dev->chip = chip;
+ dev->pci-dev = pci-dev;
+ pci-set-drvdata(pci-dev, dev);
+
+ dev->bounce-buffer = pci-alloc-consistent(pci-dev, R822-DMA-LEN,
+ &dev->phys-bounce-buffer);
+
+ if (!dev->bounce-buffer)
+ goto error6;
+
+
+ error = -ENODEV;
+ dev->mmio = pci-ioremap-bar(pci-dev, 0);
+
+ if (!dev->mmio)
+ goto error7;
+
+ error = -ENOMEM;
+ dev->tmp-buffer = kzalloc(SM-SECTOR-SIZE, GFP-KERNEL);
+
+ if (!dev->tmp-buffer)
+ goto error8;
+
+ init-completion(&dev->dma-done);
+
+ dev->card-workqueue = create-freezeable-workqueue(DRV-NAME);
+
+ if (!dev->card-workqueue)
+ goto error9;
+
+ INIT-DELAYED-WORK(&dev->card-detect-work, r822-card-detect-work);
+
+ /* shutdown everything - precation */
+ r822-device-shutdown(dev);
+ r822-disable-irqs(dev);
+
+ r822-dma-test(dev);
+
+ /*register irq handler*/
+ error = -ENODEV;
+ if (request-irq(pci-dev->irq, &r822-irq, IRQF-SHARED,
+ DRV-NAME, dev))
+ goto error10;
+
+ dev->irq = pci-dev->irq;
+ spin-lock-init(&dev->irqlock);
+
+ /* kick initial present test */
+ dev->card-detected = 0;
+ r822-card-update-present(dev);
+ queue-delayed-work(dev->card-workqueue,
+ &dev->card-detect-work, 0);
+
+ /* Load the FTL */
+ request-module-nowait("sm-ftl");
+
+ printk(KERN-NOTICE DRV-NAME ": driver loaded succesfully
");
+ return 0;
+
+error10:
+ destroy-workqueue(dev->card-workqueue);
+error9:
+ kfree(dev->tmp-buffer);
+error8:
+ pci-iounmap(pci-dev, dev->mmio);
+error7:
+ pci-free-consistent(pci-dev, R822-DMA-LEN,
+ dev->bounce-buffer, dev->phys-bounce-buffer);
+error6:
+ kfree(dev);
+error5:
+ kfree(chip);
+error4:
+ pci-release-regions(pci-dev);
+error3:
+error2:
+ pci-disable-device(pci-dev);
+error1:
+ return error;
+}
+
+
+void r822-remove(struct pci-dev *pci-dev)
+{
+ struct r822-device *dev = pci-get-drvdata(pci-dev);
+
+ /* Stop detect workqueue -
+ we are going to unregister the device anyway*/
+ cancel-delayed-work-sync(&dev->card-detect-work);
+ destroy-workqueue(dev->card-workqueue);
+
+ /* Unregister the device, this might make more IO */
+ r822-unregister-nand-device(dev);
+
+ /* Stop interrupts */
+ r822-disable-irqs(dev);
+ synchronize-irq(dev->irq);
+ free-irq(dev->irq, dev);
+
+ /* Cleanup */
+ kfree(dev->tmp-buffer);
+ pci-iounmap(pci-dev, dev->mmio);
+ pci-free-consistent(pci-dev, R822-DMA-LEN,
+ dev->bounce-buffer, dev->phys-bounce-buffer);
+ kfree(dev);
+ kfree(dev->chip);
+
+ /* Shutdown the PCI device */
+ pci-release-regions(pci-dev);
+ pci-disable-device(pci-dev);
+}
+
+void r822-shutdown(struct pci-dev *pci-dev)
+{
+ struct r822-device *dev = pci-get-drvdata(pci-dev);
+
+ cancel-delayed-work-sync(&dev->card-detect-work);
+ r822-disable-irqs(dev);
+ synchronize-irq(dev->irq);
+ pci-disable-device(pci-dev);
+}
+
+int r822-suspend(struct device *device)
+{
+ struct r822-device *dev = pci-get-drvdata(to-pci-dev(device));
+ unsigned long flags;
+
+ if (dev->ctlreg & R822-CTL-CARDENABLE)
+ return -EBUSY;
+
+ /* First make sure the detect work is gone */
+ cancel-delayed-work-sync(&dev->card-detect-work);
+
+ /* Turn off the interrupts and stop the device */
+ r822-disable-irqs(dev);
+ r822-device-shutdown(dev);
+
+ spin-lock-irqsave(&dev->irqlock, flags);
+ dev->insuspend = 1;
+ spin-unlock-irqrestore(&dev->irqlock, flags);
+
+ /* At that point, even if interrupt handler is running, it will quit */
+ /* So wait for this to happen explictly */
+ synchronize-irq(dev->irq);
+
+ /* If card was pulled off just during the suspend, which is very
+ unlikely, we will remove it on resume, it too late now
+ anyway... */
+ dev->card-unstable = 0;
+
+ pci-save-state(to-pci-dev(device));
+ pci-set-power-state(to-pci-dev(device), PCI-D3cold);
+ return 0;
+}
+
+int r822-resume(struct device *device)
+{
+ struct r822-device *dev = pci-get-drvdata(to-pci-dev(device));
+ unsigned long flags;
+
+ /* Turn on the hardware */
+ pci-set-power-state(to-pci-dev(device), PCI-D0);
+ pci-restore-state(to-pci-dev(device));
+
+ /* Disable everything - precation */
+ r822-disable-irqs(dev);
+ r822-device-shutdown(dev);
+
+ /* Detect the card change */
+ r822-card-update-present(dev);
+
+ /* Now its safe for IRQ to run */
+ spin-lock-irqsave(&dev->irqlock, flags);
+ dev->insuspend = 0;
+ spin-unlock-irqrestore(&dev->irqlock, flags);
+
+
+ /* If card status changed, just do the work */
+ if (dev->card-detected != dev->card-registred) {
+ dbg("card was %s during low power state",
+ dev->card-detected ? "added" : "removed");
+
+ queue-delayed-work(dev->card-workqueue,
+ &dev->card-detect-work, 0);
+ return 0;
+ }
+
+ /* Otherwise, initialize the card */
+ if (dev->card-registred) {
+ r822-device-start(dev);
+ dev->chip->select-chip(dev->mtd, 0);
+ dev->chip->cmdfunc(dev->mtd, NAND-CMD-RESET, -1, -1);
+ dev->chip->select-chip(dev->mtd, -1);
+ }
+
+ /* Program card detection IRQ */
+ r822-update-card-detect(dev);
+ return 0;
+}
+
+static const struct pci-device-id r822-pci-id-tbl[] = {
+
+ { PCI-VDEVICE(RICOH, PCI-DEVICE-ID-RICOH-R5C852), },
+ { },
+};
+
+MODULE-DEVICE-TABLE(pci, r822-pci-id-tbl);
+
+SIMPLE-DEV-PM-OPS(r822-pm-ops, r822-suspend, r822-resume);
+
+
+static struct pci-driver r822-pci-driver = {
+ .name = DRV-NAME,
+ .id-table = r822-pci-id-tbl,
+ .probe = r822-probe,
+ .remove = r822-remove,
+ .shutdown = r822-shutdown,
+ .driver.pm = &r822-pm-ops,
+};
+
+static + pci-unregister-driver(&r822-pci-driver);
+}
+
+module-init(r822-module-init);
+module-exit(r822-module-exit);
+
+MODULE-LICENSE("GPL");
+MODULE-AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE-DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver");
diff + * Copyright (C) 2009 - Maxim Levitsky
+ * driver for Ricoh xD readers
+ *
+ * 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.
+ */
+
+#include <linux/pci.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mtd/nand.h>
+#include <linux/spinlock.h>
+
+
+/* nand interface + ecc
+ byte write/read does one cycle on nand data lines.
+ dword write/read does 4 cycles
+ if R822-CTL-ECC-ACCESS is set in R822-CTL, then dword read reads
+ results of ecc correction, if DMA read was done before.
+ If write was done two dword reads read generated ecc checksums
+*/
+#define R822-DATALINE 0x00
+
+/* control register */
+#define R822-CTL 0x04
+#define R822-CTL-COMMAND 0x01 /* send command (#CLE)*/
+#define R822-CTL-DATA 0x02 /* read/write data (#ALE)*/
+#define R822-CTL-ON 0x04 /* only seem to controls the hd led, */
+ /* but has to be set on start...*/
+#define R822-CTL-RESET 0x08 /* unknown, set only on start once*/
+#define R822-CTL-CARDENABLE 0x10 /* probably (#CE) - always set*/
+#define R822-CTL-ECC-ENABLE 0x20 /* enable ecc engine */
+#define R822-CTL-ECC-ACCESS 0x40 /* read/write ecc via reg #0*/
+#define R822-CTL-WRITE 0x80 /* set when performing writes (#WP) */
+
+
+/* card detection status */
+#define R822-CARD-STA 0x05
+
+#define R822-CARD-STA-CD 0x01 /* state of #CD line, same as 0x04 */
+#define R822-CARD-STA-RO 0x02 /* card is readonly */
+#define R822-CARD-STA-PRESENT 0x04 /* card is present (#CD) */
+#define R822-CARD-STA-ABSENT 0x08 /* card is absent */
+#define R822-CARD-STA-BUSY 0x80 /* card is busy - (#R/B) */
+
+
+/* card detection irq status & enable*/
+#define R822-CARD-IRQ-STA 0x06 /* IRQ status */
+#define R822-CARD-IRQ-ENABLE 0x07 /* IRQ enable */
+
+#define R822-CARD-IRQ-CD 0x01 /* fire when #CD lights, same as 0x04*/
+#define R822-CARD-IRQ-REMOVE 0x04 /* detect card removal */
+#define R822-CARD-IRQ-INSERT 0x08 /* detect card insert */
+#define R822-CARD-IRQ-UNK1 0x10 /* unknown */
+#define R822-CARD-IRQ-GENABLE 0x80 /* general enable */
+#define R822-CARD-IRQ-MASK 0x1D
+
+
+/* hardware enable */
+#define R822-HW 0x08
+#define R822-HW-ENABLED 0x01 /* hw enabled */
+#define R822-HW-UNKNOWN 0x80
+
+
+/* dma capabilities */
+#define R822-DMA-CAP 0x09
+#define R822-SMBIT 0x20 /* if set with bit #6 or bit #7, then */
+ /* hw is smartmedia */
+#define R822-DMA1 0x40 /* if set w/bit #7, dma is supported */
+#define R822-DMA2 0x80 /* if set w/bit #6, dma is supported */
+
+
+/* physical DMA address - 32 bit value*/
+#define R822-DMA-ADDR 0x0C
+
+
+/* dma settings */
+#define R822-DMA-SETTINGS 0x10
+#define R822-DMA-MEMORY 0x01 /* (memory <-> internal hw buffer) */
+#define R822-DMA-READ 0x02 /* 0 = write, 1 = read */
+#define R822-DMA-INTERNAL 0x04 /* (internal hw buffer <-> card) */
+
+/* dma IRQ status */
+#define R822-DMA-IRQ-STA 0x14
+
+/* dma IRQ enable */
+#define R822-DMA-IRQ-ENABLE 0x18
+
+#define R822-DMA-IRQ-MEMORY 0x01 /* (memory <-> internal hw buffer) */
+#define R822-DMA-IRQ-ERROR 0x02 /* error did happen */
+#define R822-DMA-IRQ-INTERNAL 0x04 /* (internal hw buffer <-> card) */
+#define R822-DMA-IRQ-MASK 0x07 /* mask of all IRQ bits */
+
+
+/* ECC syndrome format - read from reg #0 will return two copies of these for
+ each half of the page.
+ first byte is error byte location, and second, bit location + flags */
+#define R822-ECC-ERR-BIT-MSK 0x07 /* error bit location */
+#define R822-ECC-CORRECT 0x10 /* no errors - (guessed) */
+#define R822-ECC-CORRECTABLE 0x20 /* correctable error exist */
+#define R822-ECC-FAIL 0x40 /* non correctable error detected */
+
+#define R822-DMA-LEN 512
+
+#define DMA-INTERNAL 0
+#define DMA-MEMORY 1
+
+struct r822-device {
+ void + struct completion dma-done; /* data transfer done */
+
+ dma-addr-t phys-bounce-buffer; /* bus address of bounce buffer */
+ u8 *bounce-buffer; /* virtual address of bounce buffer */
+
+ int dma-dir; /* 1 = read, 0 = write */
+ int dma-stage; /* 0 - idle, 1 - first step,
+ 2 - second step */
+
+ int dma-state; /* 0 = internal, 1 = memory */
+ int dma-error; /* dma errors */
+ int dma-usable; /* is it possible to use dma */
+
+ /* card status area */
+ struct delayed-work card-detect-work;
+ struct workqueue-struct *card-workqueue;
+ int card-registred; /* card registered with mtd */
+ int card-detected; /* card detected in slot */
+ int card-unstable; /* whenever the card is inserted,
+ is not known yet */
+ int readonly; /* card is readonly */
+ int sm; /* Is card smartmedia */
+
+ /* interrupt handling */
+ spinlock-t irqlock; /* IRQ protecting lock */
+ int irq; /* irq num */
+ int insuspend; /* device is suspended */
+
+ /* misc */
+ void *tmp-buffer; /* temporary buffer */
+ u8 ctlreg; /* cached contents of control reg */
+};
+
+#define DRV-NAME "r822"
+
+#define dbg(format, ...)
+ printk(KERN-ERR DRV-NAME ": " format "
", ## #define PCI-DEVICE-ID-RICOH-R5C832 0x0832
#define PCI-DEVICE-ID-RICOH-R5C843 0x0843
+#define PCI-DEVICE-ID-RICOH-R5C852 0x0852
+
#define PCI-VENDOR-ID-DLINK 0x1186
#define PCI-DEVICE-ID-DLINK-DGE510T 0x4c00
PATCH 15/17 - MTD: add few workarounds to nand system for SmartMedia/xD chips. by Maxim Levitsky on
2010-02-04T22:32:44+00:00
* Add an option NAND-SMARTMEDIA that can be set by nand driver
If set, it will cause separate ID table to be used, which includes
mask rom devices and new xD cards
* Workaround for wrong write protect status on some xD cards
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff struct nand-chip *chip = mtd->priv;
+ int wp;
+
+ /* broken xD cards report WP despite beeing writable */
+ if (chip->options & NAND-BROKEN-XD)
+ return 0;
+
/* Check the WP bit */
chip->cmdfunc(mtd, NAND-CMD-STATUS, -1, -1);
- return (chip->read-byte(mtd) & NAND-STATUS-WP) ? 0 : 1;
+ wp = (chip->read-byte(mtd) & NAND-STATUS-WP) ? 0 : 1;
+
+
+ return wp;
}
/**
@@ -2622,14 +2631,18 @@ static struct nand-flash-dev *nand-get-flash-type(struct mtd-info *mtd,
}
/* Lookup the flash id */
- for (i = 0; nand-flash-ids[i].name != NULL; i++) {
- if (dev-id == nand-flash-ids[i].id) {
- type = &nand-flash-ids[i];
+#ifdef CONFIG-MTD-NAND-SMARTMEDIA
+ if (chip->options & NAND-SMARTMEDIA)
+ type = nand-smartmedia-flash-ids;
+ else
+#endif
+ type = nand-flash-ids;
+
+ for (i = 0; type->name != NULL; type++)
+ if (dev-id == type->id)
break;
- }
- }
- if (!type)
+ if (!type->name)
return ERR-PTR(-ENODEV);
if (!mtd->name)
@@ -2986,7 +2999,8 @@ int nand-scan-tail(struct mtd-info *mtd)
/* Fill in remaining MTD driver data */
mtd->type = MTD-NANDFLASH;
- mtd->flags = MTD-CAP-NANDFLASH;
+ mtd->flags = chip->options & NAND-ROM ? MTD-CAP-ROM :
+ MTD-CAP-NANDFLASH;
mtd->erase = nand-erase;
mtd->point = NULL;
mtd->unpoint = NULL;
diff
+#ifdef CONFIG-MTD-NAND-SMARTMEDIA
+struct nand-flash-dev nand-smartmedia-flash-ids[] = {
+
+ /* SmartMedia */
+ {"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0},
+ {"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0},
+ {"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0},
+ {"SmartMedia 2MiB 3,3V", 0xea, 256, 2, 0x1000, 0},
+ {"SmartMedia 2MiB 5V", 0x64, 256, 2, 0x1000, 0},
+ {"SmartMedia 2MiB 3,3V ROM", 0x5d, 512, 2, 0x2000, NAND-ROM},
+ {"SmartMedia 4MiB 3,3V", 0xe3, 512, 4, 0x2000, 0},
+ {"SmartMedia 4MiB 3,3/5V", 0xe5, 512, 4, 0x2000, 0},
+ {"SmartMedia 4MiB 5V", 0x6b, 512, 4, 0x2000, 0},
+ {"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND-ROM},
+ {"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0},
+ {"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND-ROM},
+
+#define XD-TYPEM (NAND-NO-AUTOINCR | NAND-BROKEN-XD)
+ /* xD / SmartMedia */
+ {"SmartMedia/xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0},
+ {"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND-ROM},
+ {"SmartMedia/xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0},
+ {"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND-ROM},
+ {"SmartMedia/xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0},
+ {"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND-ROM},
+ {"SmartMedia/xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0},
+ {"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND-ROM},
+ {"SmartMedia/xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD-TYPEM},
+ {"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND-ROM},
+
+ /* xD only */
+ {"xD 512MiB 3,3V", 0xDC, 512, 512, 0x4000, XD-TYPEM},
+ {"xD 1GiB 3,3V", 0xD3, 512, 1024, 0x4000, XD-TYPEM},
+ {"xD 2GiB 3,3V", 0xD5, 512, 2048, 0x4000, XD-TYPEM},
+ {NULL,}
+};
+EXPORT-SYMBOL(nand-smartmedia-flash-ids);
+#endif
+
/*
* Manufacturer ID list
*/
diff
+/* Device is one of 'new' xD cards that expose fake nand command set */
+#define NAND-BROKEN-XD 0x00000400
+
+/* Device behaves just like nand, but is readonly */
+#define NAND-ROM 0x00000800
+
/* Options valid for Samsung large page devices */
#define NAND-SAMSUNG-LP-OPTIONS
(NAND-NO-PADDING | NAND-CACHEPRG | NAND-COPYBACK)
@@ -195,9 +201,13 @@ typedef enum {
/* This option is defined if the board driver allocates its own buffers
(e.g. because it needs them DMA-coherent */
#define NAND-OWN-BUFFERS 0x00040000
+
/* Chip may not exist, so silence any errors in scan */
#define NAND-SCAN-SILENT-NODEV 0x00080000
+/* controller supports only xD/SmartMedia cards*/
+#define NAND-SMARTMEDIA 0x00100000
+
/* Options set by nand scan */
/* Nand scan has allocated controller struct */
#define NAND-CONTROLLER-ALLOC 0x80000000
@@ -458,6 +468,7 @@ struct nand-manufacturers {
};
extern struct nand-flash-dev nand-flash-ids[];
+extern struct nand-flash-dev nand-smartmedia-flash-ids[];
extern struct nand-manufacturers nand-manuf-ids[];
extern int nand-scan-bbt(struct mtd-info *mtd, struct nand-bbt-descr *bd);
PATCH 17/17 - MTD: Add new SmartMedia/xD FTL by Maxim Levitsky on
2010-02-04T22:32:44+00:00
This implements new readwrite SmartMedia/xd FTL.
mtd driver must have support proper ECC and badblock vertification
based on oob parts for 512 bytes nand.
Also mtd driver must define read-oob and write-oob, which are used
to read and write both data and oob together.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
+
+config SM-FTL
+ tristate "SmartMedia/xD new translation layer"
+ depends on EXPERIMENTAL && BLOCK
+ select MTD-BLKDEVS
+ help
+ This enables new and very EXPERMENTAL support for SmartMedia/xD
+ FTL (Flash tanslation layer)
+ Write support isn't yet well tested, therefore this code IS likely to
+ eat your card, so please don't use it together with valuable data.
+ Use readonly driver (CONFIG-SSFDC) instead.
+
+config SM-FTL-MUSEUM
+ boolean "Additional Support for 1MB and 2MB SmartMedia cards"
+ depends on SM-FTL && MTD-NAND
+ select MTD-NAND-ECC-SMC
+ help
+ Very old SmartMedia cards need ECC to be caluculated in the FTL
+ Such cards are very rare, thus enabling this option is mostly useless
+ Also this support is completely UNTESTED.
+
config MTD-OOPS
tristate "Log panic/oops to an MTD buffer"
depends on MTD
diff obj-$(CONFIG-SSFDC) += ssfdc.o
+obj-$(CONFIG-SM-FTL) += sm-ftl.o
obj-$(CONFIG-MTD-OOPS) += mtdoops.o
nftl-objs := nftlcore.o nftlmount.o
diff + * Copyright (C) 2009 - Maxim Levitsky
+ * SmartMedia/xD translation layer
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/hdreg.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/sysfs.h>
+#include <linux/bitops.h>
+#include "nand/sm-common.h"
+#include "sm-ftl.h"
+
+#ifdef CONFIG-SM-FTL-MUSEUM
+#include <linux/mtd/nand-ecc.h>
+#endif
+
+
+struct workqueue-struct *cache-flush-workqueue;
+
+static int cache-timeout = 1000;
+module-param(cache-timeout, bool, S-IRUGO);
+MODULE-PARM-DESC(cache-timeout, "Timeout in ms for cache flush (1000 default");
+
+/* +ssize-t sm-attr-show(struct device *dev, struct device-attribute *attr,
+ char *buf)
+{
+ struct sm-sysfs-attribute *sm-attr =
+ container-of(attr, struct sm-sysfs-attribute, dev-attr);
+
+ strncpy(buf, sm-attr->data, sm-attr->len);
+ return sm-attr->len;
+}
+
+
+#define NUM-ATTRIBUTES 1
+#define SM-CIS-VENDOR-OFFSET 0x59
+struct attribute-group *sm-create-sysfs-attributes(struct sm-ftl *ftl)
+{
+ struct attribute-group *attr-group;
+ struct attribute **attributes;
+ struct sm-sysfs-attribute *vendor-attribute;
+
+ int vendor-len = strnlen(ftl->cis-buffer + SM-CIS-VENDOR-OFFSET,
+ SM-SMALL-PAGE - SM-CIS-VENDOR-OFFSET);
+
+ char *vendor = kmalloc(vendor-len, GFP-KERNEL);
+ memcpy(vendor, ftl->cis-buffer + SM-CIS-VENDOR-OFFSET, vendor-len);
+ vendor[vendor-len] = 0;
+
+ /* Initialize sysfs attributes */
+ vendor-attribute =
+ kzalloc(sizeof(struct sm-sysfs-attribute), GFP-KERNEL);
+
+ vendor-attribute->data = vendor;
+ vendor-attribute->len = vendor-len;
+ vendor-attribute->dev-attr.attr.name = "vendor";
+ vendor-attribute->dev-attr.attr.mode = S-IRUGO;
+ vendor-attribute->dev-attr.show = sm-attr-show;
+
+
+ /* Create array of pointers to the attributes */
+ attributes = kzalloc(sizeof(struct attribute *) * (NUM-ATTRIBUTES + 1),
+ GFP-KERNEL);
+ attributes[0] = &vendor-attribute->dev-attr.attr;
+
+ /* Finally create the attribute group */
+ attr-group = kzalloc(sizeof(struct attribute-group), GFP-KERNEL);
+ attr-group->attrs = attributes;
+ return attr-group;
+}
+
+void sm-delete-sysfs-attributes(struct sm-ftl *ftl)
+{
+ struct attribute **attributes = ftl->trans->disk-attributes->attrs;
+ int i;
+
+ for (i = 0; attributes[i] ; i++) {
+
+ struct device-attribute *dev-attr = container-of(attributes[i],
+ struct device-attribute, attr);
+
+ struct sm-sysfs-attribute *sm-attr =
+ container-of(dev-attr,
+ struct sm-sysfs-attribute, dev-attr);
+
+ kfree(sm-attr->data);
+ kfree(sm-attr);
+ }
+
+ kfree(ftl->trans->disk-attributes->attrs);
+ kfree(ftl->trans->disk-attributes);
+}
+
+
+/* +
+ /* check parity - endianess doesn't matter */
+ if (hweight16(*(u16 *)lba) & 1)
+ return -2;
+
+ return (lba[1] >> 1) | ((lba[0] & 0x07) << 7);
+}
+
+
+/*
+ * Read LBA asscociated with block
+ * returns -1, if block is erased
+ * returns -2 if error happens
+ */
+static int sm-read-lba(struct sm-oob *oob)
+{
+ static const u32 erased-pattern[4] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+
+ u16 lba-test;
+ int lba;
+
+ /* First test for erased block */
+ if (!memcmp(oob, erased-pattern, SM-OOB-SIZE))
+ return -1;
+
+ /* Now check is both copies of the LBA differ too much */
+ lba-test = *(u16 *)oob->lba-copy1 ^ *(u16*)oob->lba-copy2;
+ if (lba-test && !is-power-of-2(lba-test))
+ return -2;
+
+ /* And read it */
+ lba = sm-get-lba(oob->lba-copy1);
+
+ if (lba == -2)
+ lba = sm-get-lba(oob->lba-copy2);
+
+ return lba;
+}
+
+static void sm-write-lba(struct sm-oob *oob, u16 lba)
+{
+ u8 tmp[2];
+
+ WARN-ON(lba >= 1000);
+
+ tmp[0] = 0x10 | ((lba >> 7) & 0x07);
+ tmp[1] = (lba << 1) & 0xFF;
+
+ if (hweight16(*(u16 *)tmp) & 0x01)
+ tmp[1] |= 1;
+
+ oob->lba-copy1[0] = oob->lba-copy2[0] = tmp[0];
+ oob->lba-copy1[1] = oob->lba-copy2[1] = tmp[1];
+}
+
+
+/* Make offset from parts */
+static loff-t sm-mkoffset(struct sm-ftl *ftl, int zone, int block, int boffset)
+{
+ WARN-ON(boffset & (SM-SECTOR-SIZE - 1));
+ WARN-ON(zone < 0 || zone >= ftl->zone-count);
+ WARN-ON(block >= ftl->zone-size);
+ WARN-ON(boffset >= ftl->block-size);
+
+ if (block == -1)
+ return -1;
+
+ return (zone * SM-MAX-ZONE-SIZE + block) * ftl->block-size + boffset;
+}
+
+/* Breaks offset into parts */
+static void sm-break-offset(struct sm-ftl *ftl, loff-t offset,
+ int *zone, int *block, int *boffset)
+{
+ *boffset = offset % ftl->block-size;
+ offset /= ftl->block-size;
+ *block = offset % ftl->max-lba;
+ offset /= ftl->max-lba;
+ *zone = offset >= ftl->zone-count ? -1 : offset;
+}
+
+/* + + if (+static int sm-read-sector(struct sm-ftl *ftl,
+ int zone, int block, int boffset,
+ u8 *buffer, struct sm-oob *oob)
+{
+ struct mtd-info *mtd = ftl->trans->mtd;
+ struct mtd-oob-ops ops;
+ struct sm-oob tmp-oob;
+ int ret;
+ int try = 0;
+
+ /* FTL can contain -1 entries that are by default filled with bits */
+ if (block == -1) {
+ memset(buffer, 0xFF, SM-SECTOR-SIZE);
+ return 0;
+ }
+
+ /* User might not need the oob, but we do for data vertification */
+ if (!oob)
+ oob = &tmp-oob;
+
+ ops.mode = ftl->smallpagenand ? MTD-OOB-RAW : MTD-OOB-PLACE;
+ ops.ooboffs = 0;
+ ops.ooblen = SM-OOB-SIZE;
+ ops.oobbuf = (void *)oob;
+ ops.len = SM-SECTOR-SIZE;
+ ops.datbuf = buffer;
+
+again:
+ /* Retrial mode */
+ if (try++) {
+ /* Avoid infinite recursion on CIS reads, sm-recheck-media
+ won't help anyway */
+ if (zone == 0 && block == ftl->cis-block && boffset ==
+ ftl->cis-boffset)
+ return ret;
+
+ /* Test if media is stable */
+ if (try == 3 || sm-recheck-media(ftl))
+ return ret;
+ }
+
+
+ /* Unfortunelly, oob read will -always- succeed,
+ despite card removal..... */
+ ret = mtd->read-oob(mtd, sm-mkoffset(ftl, zone, block, boffset), &ops);
+
+ /* Test for unknown errors */
+ if (ret != 0 && ret != -EUCLEAN && ret != -EBADMSG) {
+ dbg("read of block %d at zone %d, failed due to error (%d)",
+ block, zone, ret);
+ goto again;
+ }
+
+ /* Do a basic test on the oob, to guard against returned garbage */
+ if (oob->reserved != 0xFFFFFFFF)
+ goto again;
+
+ /* This should never happen, unless there is a bug in the mtd driver */
+ WARN-ON(ops.oobretlen != SM-OOB-SIZE);
+ WARN-ON(buffer && ops.retlen != SM-SECTOR-SIZE);
+
+ if (!buffer)
+ return 0;
+
+ /* Test if sector marked as bad */
+ if (!sm-sector-valid(oob)) {
+ dbg("read of block %d at zone %d, failed because it is marked"
+ " as bad" , block, zone);
+ goto again;
+ }
+
+ /* Test ECC*/
+ if (ret == -EBADMSG ||
+ (ftl->smallpagenand && sm-correct-sector(buffer, oob))) {
+
+ dbg("read of block %d at zone %d, failed due to ECC error",
+ block, zone);
+ goto again;
+ }
+
+ return 0;
+}
+
+/* Writes a sector to media */
+static int sm-write-sector(struct sm-ftl *ftl,
+ int zone, int block, int boffset,
+ u8 *buffer, struct sm-oob *oob)
+{
+ struct mtd-oob-ops ops;
+ struct mtd-info *mtd = ftl->trans->mtd;
+ int ret;
+
+ BUG-ON(ftl->readonly);
+
+ if (zone == 0 && (block == ftl->cis-block || block == 0)) {
+ dbg("attempted to write the CIS!");
+ return -EIO;
+ }
+
+ if (ftl->unstable)
+ return -EIO;
+
+ ops.mode = ftl->smallpagenand ? MTD-OOB-RAW : MTD-OOB-PLACE;
+ ops.len = SM-SECTOR-SIZE;
+ ops.datbuf = buffer;
+ ops.ooboffs = 0;
+ ops.ooblen = SM-OOB-SIZE;
+ ops.oobbuf = (void *)oob;
+
+ ret = mtd->write-oob(mtd, sm-mkoffset(ftl, zone, block, boffset), &ops);
+
+ /* Now we assume that hardware will catch write bitflip errors */
+ /* If you are paranoid, use CONFIG-MTD-NAND-VERIFY-WRITE */
+
+ if (ret) {
+ dbg("write to block %d at zone %d, failed with error %d",
+ block, zone, ret);
+
+ sm-recheck-media(ftl);
+ return ret;
+ }
+
+ /* This should never happen, unless there is a bug in the driver */
+ WARN-ON(ops.oobretlen != SM-OOB-SIZE);
+ WARN-ON(buffer && ops.retlen != SM-SECTOR-SIZE);
+
+ return 0;
+}
+
+/* + struct sm-oob oob;
+ int boffset;
+ int retry = 0;
+
+ /* Initialize the oob with requested values */
+ memset(&oob, 0xFF, SM-OOB-SIZE);
+ sm-write-lba(&oob, lba);
+restart:
+ if (ftl->unstable)
+ return -EIO;
+
+ for (boffset = 0; boffset < ftl->block-size;
+ boffset += SM-SECTOR-SIZE) {
+
+ oob.data-status = 0xFF;
+
+ if (test-bit(boffset / SM-SECTOR-SIZE, &invalid-bitmap)) {
+
+ sm-printk("sector %d of block at LBA %d of zone %d"
+ " coudn't be read, marking it as invalid",
+ boffset / SM-SECTOR-SIZE, lba, zone);
+
+ oob.data-status = 0;
+ }
+
+#ifdef CONFIG-SM-FTL-MUSEUM
+ if (ftl->smallpagenand) {
+ + if (!sm-write-sector(ftl, zone, block, boffset,
+ buf + boffset, &oob))
+ continue;
+
+ if (!retry) {
+
+ /* If write fails. try to erase the block */
+ /* This is safe, because we never write in blocks
+ that contain valuable data.
+ This is intended to repair block that are marked
+ as erased, but that isn't fully erased*/
+
+ if (sm-erase-block(ftl, zone, block, 0))
+ return -EIO;
+
+ retry = 1;
+ goto restart;
+ } else {
+ sm-mark-block-bad(ftl, zone, block);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+
+/* Mark whole block at offset 'offs' as bad. */
+static void sm-mark-block-bad(struct sm-ftl *ftl, int zone, int block)
+{
+ struct sm-oob oob;
+ int boffset;
+
+ memset(&oob, 0xFF, SM-OOB-SIZE);
+ oob.block-status = 0xF0;
+
+ if (ftl->unstable)
+ return;
+
+ if (sm-recheck-media(ftl))
+ return;
+
+ sm-printk("marking block %d of zone %d as bad", block, zone);
+
+ /* We aren't checking the return value, because we don't care */
+ /* This also fails on fake xD cards, but I guess these won't expose
+ any bad blocks till fail completly */
+ for (boffset = 0; boffset < ftl->block-size; boffset += SM-SECTOR-SIZE)
+ sm-write-sector(ftl, zone, block, boffset, NULL, &oob);
+}
+
+/*
+ * Erase a block within a zone
+ * If erase succedes, it updates free block fifo, otherwise marks block as bad
+ */
+static int sm-erase-block(struct sm-ftl *ftl, int zone-num, u16 block,
+ int put-free)
+{
+ struct ftl-zone *zone = &ftl->zones[zone-num];
+ struct mtd-info *mtd = ftl->trans->mtd;
+ struct erase-info erase;
+
+ erase.mtd = mtd;
+ erase.callback = sm-erase-callback;
+ erase.addr = sm-mkoffset(ftl, zone-num, block, 0);
+ erase.len = ftl->block-size;
+ erase.priv = (u-long)ftl;
+
+ if (ftl->unstable)
+ return -EIO;
+
+ BUG-ON(ftl->readonly);
+
+ if (zone-num == 0 && (block == ftl->cis-block || block == 0)) {
+ sm-printk("attempted to erase the CIS!");
+ return -EIO;
+ }
+
+ if (mtd->erase(mtd, &erase)) {
+ sm-printk("erase of block %d in zone %d failed",
+ block, zone-num);
+ goto error;
+ }
+
+ if (erase.state == MTD-ERASE-PENDING)
+ wait-for-completion(&ftl->erase-completion);
+
+ if (erase.state != MTD-ERASE-DONE) {
+ sm-printk("erase of block %d in zone %d failed after wait",
+ block, zone-num);
+ goto error;
+ }
+
+ if (put-free)
+ kfifo-in(&zone->free-sectors,
+ (const unsigned char *)&block, sizeof(block));
+
+ return 0;
+error:
+ sm-mark-block-bad(ftl, zone-num, block);
+ return -EIO;
+}
+
+static void sm-erase-callback(struct erase-info *self)
+{
+ struct sm-ftl *ftl = (struct sm-ftl *)self->priv;
+ complete(&ftl->erase-completion);
+}
+
+/* Throughtly test that block is valid. */
+static int sm-check-block(struct sm-ftl *ftl, int zone, int block)
+{
+ int boffset;
+ struct sm-oob oob;
+ int lbas[] = { -3, 0, 0, 0 };
+ int i = 0;
+ int test-lba;
+
+
+ /* First just check that block doesn't look fishy */
+ /* Only blocks that are valid or are sliced in two parts, are
+ accepted */
+ for (boffset = 0; boffset < ftl->block-size;
+ boffset += SM-SECTOR-SIZE) {
+
+ /* This shoudn't happen anyway */
+ if (sm-read-sector(ftl, zone, block, boffset, NULL, &oob))
+ return -2;
+
+ test-lba = sm-read-lba(&oob);
+
+ if (lbas[i] != test-lba)
+ lbas[++i] = test-lba;
+
+ /* If we found three different LBAs, something is fishy */
+ if (i == 3)
+ return -EIO;
+ }
+
+ /* If the block is sliced (partialy erased usually) erase it */
+ if (i == 2) {
+ sm-erase-block(ftl, zone, block, 1);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* + { 32, 500, 8, 16 },
+ { 64, 500, 8, 32 },
+ { 128, 500, 16, 32 },
+ { 256, 1000, 16, 32 },
+ { 512, 1015, 32, 63 },
+ { 1024, 985, 33, 63 },
+ { 2048, 985, 33, 63 },
+ { 0 },
+};
+
+
+static const u8 cis-signature[] = {
+ 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
+};
+/* Find out media parameters.
+ * This ideally has to be based on nand id, but for now device size is enough */
+int sm-get-media-info(struct sm-ftl *ftl, struct mtd-info *mtd)
+{
+ int i;
+ int size-in-megs = mtd->size / (1024 * 1024);
+
+ ftl->readonly = mtd->type == MTD-ROM;
+
+ /* Manual settings for very old devices */
+ ftl->zone-count = 1;
+ ftl->smallpagenand = 0;
+
+ switch (size-in-megs) {
+ case 1:
+ /* 1 Mb flash/rom SmartMedia card (256 byte pages)*/
+ ftl->zone-size = 256;
+ ftl->max-lba = 250;
+ ftl->block-size = 8 * SM-SECTOR-SIZE;
+ ftl->smallpagenand = 1;
+
+ break;
+ case 2:
+ /* 2 Mb flash SmartMedia (256 byte pages)*/
+ if (mtd->writesize == SM-SMALL-PAGE) {
+ ftl->zone-size = 512;
+ ftl->max-lba = 500;
+ ftl->block-size = 8 * SM-SECTOR-SIZE;
+ ftl->smallpagenand = 1;
+ /* 2 Mb rom SmartMedia */
+ } else {
+
+ if (!ftl->readonly)
+ return -ENODEV;
+
+ ftl->zone-size = 256;
+ ftl->max-lba = 250;
+ ftl->block-size = 16 * SM-SECTOR-SIZE;
+ }
+ break;
+ case 4:
+ /* 4 Mb flash/rom SmartMedia device */
+ ftl->zone-size = 512;
+ ftl->max-lba = 500;
+ ftl->block-size = 16 * SM-SECTOR-SIZE;
+ break;
+ case 8:
+ /* 8 Mb flash/rom SmartMedia device */
+ ftl->zone-size = 1024;
+ ftl->max-lba = 1000;
+ ftl->block-size = 16 * SM-SECTOR-SIZE;
+ }
+
+ /* Minimum xD size is 16M. Also, all xD cards have standard zone
+ sizes. SmartMedia cards exist up to 128 Mb and have same layout*/
+ if (size-in-megs >= 16) {
+ ftl->zone-count = size-in-megs / 16;
+ ftl->zone-size = 1024;
+ ftl->max-lba = 1000;
+ ftl->block-size = 32 * SM-SECTOR-SIZE;
+ }
+
+ /* Test for proper write,erase and oob sizes */
+ if (mtd->erasesize > ftl->block-size)
+ return -ENODEV;
+
+ if (mtd->writesize > SM-SECTOR-SIZE)
+ return -ENODEV;
+
+ if (ftl->smallpagenand && mtd->oobsize < SM-SMALL-OOB-SIZE)
+ return -ENODEV;
+
+ if (!ftl->smallpagenand && mtd->oobsize < SM-OOB-SIZE)
+ return -ENODEV;
+
+ /* We use these functions for IO */
+ if (!mtd->read-oob || !mtd->write-oob)
+ return -ENODEV;
+
+ /* Find geometry information */
+ for (i = 0 ; i < ARRAY-SIZE(chs-table) ; i++) {
+ if (chs-table[i].size == size-in-megs) {
+ ftl->cylinders = chs-table[i].cyl;
+ ftl->heads = chs-table[i].head;
+ ftl->sectors = chs-table[i].sec;
+ return 0;
+ }
+ }
+
+ sm-printk("media has unknown size : %dMB", size-in-megs);
+ ftl->cylinders = 985;
+ ftl->heads = 33;
+ ftl->sectors = 63;
+ return 0;
+}
+
+/* Validate the CIS */
+static int sm-read-cis(struct sm-ftl *ftl)
+{
+ struct sm-oob oob;
+
+ if (sm-read-sector(ftl,
+ 0, ftl->cis-block, ftl->cis-boffset, ftl->cis-buffer, &oob))
+ return -EIO;
+
+ if (!sm-sector-valid(&oob) || !sm-block-valid(&oob))
+ return -EIO;
+
+ if (!memcmp(ftl->cis-buffer, cis-signature, sizeof(cis-signature)))
+ return 0;
+
+ if (!memcmp(ftl->cis-buffer + SM-SMALL-PAGE, cis-signature,
+ sizeof(cis-signature)))
+ return 0;
+ return -EIO;
+}
+
+/* Scan the media for the CIS */
+static int sm-find-cis(struct sm-ftl *ftl)
+{
+ struct sm-oob oob;
+ int block, boffset;
+ int block-found = 0;
+
+ /* Search for first valid block */
+ for (block = 0 ; block < ftl->zone-size - ftl->max-lba ; block++) {
+
+ if (sm-read-sector(ftl, 0, block, 0, NULL, &oob))
+ continue;
+
+ if (!sm-block-valid(&oob))
+ continue;
+ block-found = 1;
+ break;
+ }
+
+ if (!block-found)
+ return -EIO;
+
+ /* Search for first valid sector in this block */
+ for (boffset = 0 ; boffset < ftl->block-size;
+ boffset += SM-SECTOR-SIZE) {
+
+ if (sm-read-sector(ftl, 0, block, boffset, NULL, &oob))
+ continue;
+
+ if (!sm-sector-valid(&oob))
+ continue;
+ break;
+ }
+
+ if (boffset == ftl->block-size)
+ return -EIO;
+
+ ftl->cis-block = block;
+ ftl->cis-boffset = boffset;
+
+ if (!sm-read-cis(ftl)) {
+ dbg("CIS block found at offset %d",
+ block * ftl->block-size + boffset);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/* Basic test to determine if underlying mtd device if functional */
+static int sm-recheck-media(struct sm-ftl *ftl)
+{
+ if (sm-read-cis(ftl)) {
+
+ if (!ftl->unstable) {
+ sm-printk("media unstable, not allowing writes");
+ ftl->unstable = 1;
+ }
+ return -EIO;
+ }
+ return 0;
+}
+
+/* Initialize a FTL zone */
+static int sm-init-zone(struct sm-ftl *ftl, int zone-num)
+{
+ struct ftl-zone *zone = &ftl->zones[zone-num];
+ struct sm-oob oob;
+ u16 block;
+ int lba;
+ int i = 0;
+
+ dbg("initializing zone %d", zone-num);
+
+ /* Allocate memory for FTL table */
+ zone->lba-to-phys-table = kmalloc(ftl->max-lba * 2, GFP-KERNEL);
+
+ if (!zone->lba-to-phys-table)
+ return -ENOMEM;
+ memset(zone->lba-to-phys-table, -1, ftl->max-lba * 2);
+
+
+ /* Allocate memory for free sectors FIFO */
+ if (kfifo-alloc(&zone->free-sectors, ftl->zone-size * 2, GFP-KERNEL)) {
+ kfree(zone->lba-to-phys-table);
+ return -ENOMEM;
+ }
+
+ /* Now scan the zone */
+ for (block = 0 ; block < ftl->zone-size ; block++) {
+
+ /* Skip blocks till the CIS (including) */
+ if (zone-num == 0 && block <= ftl->cis-block)
+ continue;
+
+ /* Read the oob of first sector */
+ if (sm-read-sector(ftl, zone-num, block, 0, NULL, &oob))
+ return -EIO;
+
+ /* Test to see if block is erased. It is enough to test
+ first sector, because erase happens in one shot */
+ if (sm-block-erased(&oob)) {
+ kfifo-in(&zone->free-sectors,
+ (unsigned char *)&block, 2);
+ continue;
+ }
+
+ /* If block is marked as bad, skip it */
+ /* This assumes we can trust first sector*/
+ /* However the way the block valid status is defined, ensures
+ very low probability of failure here */
+ if (!sm-block-valid(&oob)) {
+ dbg("PH %04d <-> <marked bad>", block);
+ continue;
+ }
+
+
+ lba = sm-read-lba(&oob);
+
+ /* Invalid LBA means that block is damaged. */
+ /* We can try to erase it, or mark it as bad, but
+ lets leave that to recovery application */
+ if (lba == -2 || lba >= ftl->max-lba) {
+ dbg("PH %04d <-> LBA %04d(bad)", block, lba);
+ continue;
+ }
+
+
+ /* If there is no collision,
+ just put the sector in the FTL table */
+ if (zone->lba-to-phys-table[lba] < 0) {
+ dbg("PH %04d <-> LBA %04d", block, lba);
+ zone->lba-to-phys-table[lba] = block;
+ continue;
+ }
+
+ sm-printk("collision"
+ " of LBA %d between blocks %d and %d in zone %d",
+ lba, zone->lba-to-phys-table[lba], block, zone-num);
+
+ /* Test that this block is valid*/
+ if (sm-check-block(ftl, zone-num, block))
+ continue;
+
+ /* Test now the old block */
+ if (sm-check-block(ftl, zone-num,
+ zone->lba-to-phys-table[lba])) {
+ zone->lba-to-phys-table[lba] = block;
+ continue;
+ }
+
+ /* If both blocks are valid and share same LBA, it means that
+ they hold different versions of same data. It not
+ known which is more recent, thus just erase one of them
+ */
+ sm-printk("both blocks are valid, erasing the later");
+ sm-erase-block(ftl, zone-num, block, 1);
+ }
+
+ dbg("zone initialized");
+ zone->initialized = 1;
+
+ /* No free sectors, means that the zone is heavily damaged, write won't
+ work, but it can still can be (partially) read */
+ if (!kfifo-len(&zone->free-sectors)) {
+ sm-printk("no free blocks in zone %d", zone-num);
+ return 0;
+ }
+
+ /* Randomize first block we write to */
+ get-random-bytes(&i, 2);
+ i %= (kfifo-len(&zone->free-sectors) / 2);
+
+ while (i+/* Get and automaticly initialize an FTL mapping for one zone */
+struct ftl-zone *sm-get-zone(struct sm-ftl *ftl, int zone-num)
+{
+ struct ftl-zone *zone;
+ int error;
+
+ BUG-ON(zone-num >= ftl->zone-count);
+ zone = &ftl->zones[zone-num];
+
+ if (!zone->initialized) {
+ error = sm-init-zone(ftl, zone-num);
+
+ if (error)
+ return ERR-PTR(error);
+ }
+ return zone;
+}
+
+
+/* + ftl->cache-zone = -1;
+ ftl->cache-block = -1;
+ /*memset(ftl->cache-data, 0xAA, ftl->block-size);*/
+}
+
+/* Put sector in one block cache */
+void sm-cache-put(struct sm-ftl *ftl, char *buffer, int boffset)
+{
+ memcpy(ftl->cache-data + boffset, buffer, SM-SECTOR-SIZE);
+ clear-bit(boffset / SM-SECTOR-SIZE, &ftl->cache-data-invalid-bitmap);
+ ftl->cache-clean = 0;
+}
+
+/* Read a sector from the cache */
+int sm-cache-get(struct sm-ftl *ftl, char *buffer, int boffset)
+{
+ if (test-bit(boffset / SM-SECTOR-SIZE,
+ &ftl->cache-data-invalid-bitmap))
+ return -1;
+
+ memcpy(buffer, ftl->cache-data + boffset, SM-SECTOR-SIZE);
+ return 0;
+}
+
+/* Write the cache to hardware */
+int sm-cache-flush(struct sm-ftl *ftl)
+{
+ struct ftl-zone *zone;
+
+ int sector-num;
+ u16 write-sector;
+ int zone-num = ftl->cache-zone;
+ int block-num;
+
+ if (ftl->cache-clean)
+ return 0;
+
+ if (ftl->unstable)
+ return -EIO;
+
+ BUG-ON(zone-num < 0);
+ zone = &ftl->zones[zone-num];
+ block-num = zone->lba-to-phys-table[ftl->cache-block];
+
+
+ /* Try to read all unread areas of the cache block*/
+ for-each-bit(sector-num, &ftl->cache-data-invalid-bitmap,
+ ftl->block-size / SM-SECTOR-SIZE) {
+
+ if (!sm-read-sector(ftl,
+ zone-num, block-num, sector-num * SM-SECTOR-SIZE,
+ ftl->cache-data + sector-num * SM-SECTOR-SIZE, NULL))
+ clear-bit(sector-num,
+ &ftl->cache-data-invalid-bitmap);
+ }
+restart:
+
+ if (ftl->unstable)
+ return -EIO;
+ /* No spare blocks */
+ /* We could still continue by erasing the current block,
+ but for such worn out media it doesn't worth the trouble,
+ and the dangers */
+
+ if (!kfifo-len(&zone->free-sectors)) {
+ dbg("no free sectors for write!");
+ return -EIO;
+ }
+
+ kfifo-out(&zone->free-sectors, (unsigned char *)&write-sector, 2);
+
+ if (sm-write-block(ftl, ftl->cache-data, zone-num, write-sector,
+ ftl->cache-block, ftl->cache-data-invalid-bitmap))
+ goto restart;
+
+ /* Update the FTL table */
+ zone->lba-to-phys-table[ftl->cache-block] = write-sector;
+
+ /* Write succesfull, so erase and free the old block */
+ if (block-num > 0)
+ sm-erase-block(ftl, zone-num, block-num, 1);
+
+ sm-cache-init(ftl);
+ return 0;
+}
+
+
+/* flush timer, runs a second after last write */
+static void sm-cache-flush-timer(unsigned long data)
+{
+ struct sm-ftl *ftl = (struct sm-ftl *)data;
+ queue-work(cache-flush-workqueue, &ftl->flush-work);
+}
+
+/* cache flush work, kicked by timer */
+static void sm-cache-flush-work(struct work-struct *work)
+{
+ struct sm-ftl *ftl = container-of(work, struct sm-ftl, flush-work);
+ mutex-lock(&ftl->mutex);
+ sm-cache-flush(ftl);
+ mutex-unlock(&ftl->mutex);
+ return;
+}
+
+/* + struct ftl-zone *zone;
+ int error = 0, in-cache = 0;
+ int zone-num, block, boffset;
+
+ sm-break-offset(ftl, sect-no << 9, &zone-num, &block, &boffset);
+ mutex-lock(&ftl->mutex);
+
+
+ zone = sm-get-zone(ftl, zone-num);
+ if (IS-ERR(zone)) {
+ error = PTR-ERR(zone);
+ goto unlock;
+ }
+
+ /* Have to look at cache first */
+ if (ftl->cache-zone == zone-num && ftl->cache-block == block) {
+ in-cache = 1;
+ if (!sm-cache-get(ftl, buf, boffset))
+ goto unlock;
+ }
+
+ /* Translate the block and return if doesn't exist in the table */
+ block = zone->lba-to-phys-table[block];
+
+ if (block == -1) {
+ memset(buf, 0xFF, SM-SECTOR-SIZE);
+ goto unlock;
+ }
+
+ if (sm-read-sector(ftl, zone-num, block, boffset, buf, NULL)) {
+ error = -EIO;
+ goto unlock;
+ }
+
+ if (in-cache)
+ sm-cache-put(ftl, buf, boffset);
+unlock:
+ mutex-unlock(&ftl->mutex);
+ return error;
+}
+
+/* outside interface: write a sector */
+static int sm-write(struct mtd-blktrans-dev *dev,
+ unsigned long sec-no, char *buf)
+{
+ struct sm-ftl *ftl = dev->priv;
+ struct ftl-zone *zone;
+ int error, zone-num, block, boffset;
+
+ BUG-ON(ftl->readonly);
+ sm-break-offset(ftl, sec-no << 9, &zone-num, &block, &boffset);
+
+ /* No need in flush thread running now */
+ del-timer(&ftl->timer);
+ mutex-lock(&ftl->mutex);
+
+ zone = sm-get-zone(ftl, zone-num);
+ if (IS-ERR(zone)) {
+ error = PTR-ERR(zone);
+ goto unlock;
+ }
+
+ /* If entry is not in cache, flush it */
+ if (ftl->cache-block != block || ftl->cache-zone != zone-num) {
+
+ error = sm-cache-flush(ftl);
+ if (error)
+ goto unlock;
+
+ ftl->cache-block = block;
+ ftl->cache-zone = zone-num;
+ }
+
+ sm-cache-put(ftl, buf, boffset);
+unlock:
+ mod-timer(&ftl->timer, jiffies + msecs-to-jiffies(cache-timeout));
+ mutex-unlock(&ftl->mutex);
+ return error;
+}
+
+/* outside interface: flush everything */
+static int sm-flush(struct mtd-blktrans-dev *dev)
+{
+ struct sm-ftl *ftl = dev->priv;
+ int retval;
+
+ mutex-lock(&ftl->mutex);
+ retval = sm-cache-flush(ftl);
+ mutex-unlock(&ftl->mutex);
+ return retval;
+}
+
+/* outside interface: device is released */
+static int sm-release(struct mtd-blktrans-dev *dev)
+{
+ struct sm-ftl *ftl = dev->priv;
+
+ mutex-lock(&ftl->mutex);
+ del-timer-sync(&ftl->timer);
+ cancel-work-sync(&ftl->flush-work);
+ sm-cache-flush(ftl);
+ mutex-unlock(&ftl->mutex);
+ return 0;
+}
+
+/* outside interface: get geometry */
+static int sm-getgeo(struct mtd-blktrans-dev *dev, struct hd-geometry *geo)
+{
+ struct sm-ftl *ftl = dev->priv;
+ geo->heads = ftl->heads;
+ geo->sectors = ftl->sectors;
+ geo->cylinders = ftl->cylinders;
+ return 0;
+}
+
+/* external interface: main initialization function */
+static void sm-add-mtd(struct mtd-blktrans-ops *tr, struct mtd-info *mtd)
+{
+ struct mtd-blktrans-dev *trans;
+ struct sm-ftl *ftl;
+
+ /* Allocate & initialize our private structure */
+ ftl = kzalloc(sizeof(struct sm-ftl), GFP-KERNEL);
+ if (!ftl)
+ goto error1;
+
+ mutex-init(&ftl->mutex);
+ setup-timer(&ftl->timer, sm-cache-flush-timer, (unsigned long)ftl);
+ INIT-WORK(&ftl->flush-work, sm-cache-flush-work);
+ init-completion(&ftl->erase-completion);
+
+
+ /* Read media information */
+ if (sm-get-media-info(ftl, mtd))
+ goto error2;
+
+ /* Allocate temporary CIS buffer for read retry support */
+ ftl->cis-buffer = kzalloc(SM-SECTOR-SIZE, GFP-KERNEL);
+ if (!ftl->cis-buffer)
+ goto error2;
+
+ /* Allocate zone array, it will be initialized on demand */
+ ftl->zones = kzalloc(sizeof(struct ftl-zone) * ftl->zone-count,
+ GFP-KERNEL);
+ if (!ftl->zones)
+ goto error3;
+
+ /* Allocate the cache*/
+ ftl->cache-data = kzalloc(ftl->block-size, GFP-KERNEL);
+
+ if (!ftl->cache-data)
+ goto error4;
+
+ sm-cache-init(ftl);
+
+ /* Allocate upper layer structure and initialize it */
+ trans = kzalloc(sizeof(struct mtd-blktrans-dev), GFP-KERNEL);
+ if (!trans)
+ goto error5;
+
+ ftl->trans = trans;
+ trans->priv = ftl;
+
+ trans->tr = tr;
+ trans->mtd = mtd;
+ trans->devnum = -1;
+ trans->size = (ftl->block-size * ftl->max-lba * ftl->zone-count) >> 9;
+ trans->readonly = ftl->readonly;
+
+
+ if (sm-find-cis(ftl))
+ goto error6;
+
+ trans->disk-attributes = sm-create-sysfs-attributes(ftl);
+
+ sm-printk("Found %d MiB xD/SmartMedia FTL on mtd%d",
+ (int)(mtd->size / (1024 * 1024)), mtd->index);
+
+ dbg("FTL layout:");
+ dbg("%d zone(s), each consists of %d blocks (+%d spares)",
+ ftl->zone-count, ftl->max-lba,
+ ftl->zone-size - ftl->max-lba);
+ dbg("each block consists of %d bytes",
+ ftl->block-size);
+
+ /* Register device*/
+ if (add-mtd-blktrans-dev(trans))
+ goto error6;
+
+ return;
+error6:
+ kfree(trans);
+error5:
+ kfree(ftl->cache-data);
+error4:
+ kfree(ftl->zones);
+error3:
+ kfree(ftl->cis-buffer);
+error2:
+ kfree(ftl);
+error1:
+ return;
+}
+
+/* main interface: device {surprise,} removal */
+static void sm-remove-dev(struct mtd-blktrans-dev *dev)
+{
+ struct sm-ftl *ftl = dev->priv;
+ int i;
+
+ del-mtd-blktrans-dev(dev);
+
+ for (i = 0 ; i < ftl->zone-count; i++) {
+
+ if (!ftl->zones[i].initialized)
+ continue;
+
+ kfree(ftl->zones[i].lba-to-phys-table);
+ kfifo-free(&ftl->zones[i].free-sectors);
+ }
+
+ sm-delete-sysfs-attributes(ftl);
+ kfree(ftl->zones);
+ kfree(ftl->cache-data);
+ kfree(ftl);
+}
+
+static struct mtd-blktrans-ops sm-ftl-ops = {
+ .name = "smblk",
+ .major = -1,
+ .part-bits = SM-FTL-PARTN-BITS,
+ .blksize = SM-SECTOR-SIZE,
+ .getgeo = sm-getgeo,
+
+ .add-mtd = sm-add-mtd,
+ .remove-dev = sm-remove-dev,
+
+ .readsect = sm-read,
+ .writesect = sm-write,
+
+ .flush = sm-flush,
+ .release = sm-release,
+
+ .owner = THIS-MODULE,
+};
+
+static +
+ error = register-mtd-blktrans(&sm-ftl-ops);
+ if (error)
+ destroy-workqueue(cache-flush-workqueue);
+ return error;
+
+}
+
+static void +module-exit(sm-module-exit);
+
+MODULE-LICENSE("GPL");
+MODULE-AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE-DESCRIPTION("Smartmedia/xD mtd translation layer");
diff + * Copyright (C) 2009 - Maxim Levitsky
+ * SmartMedia/xD translation layer
+ *
+ * Based loosly on ssfdc.c which is
+ * (c) 2005 Eptar srl
+ * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
+ *
+ * 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.
+ */
+
+#include <linux/mtd/blktrans.h>
+#include <linux/kfifo.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+
+
+
+struct ftl-zone {
+ int initialized;
+ s16 *lba-to-phys-table; /* LBA to physical table */
+ struct kfifo free-sectors; /* queue of free sectors */
+};
+
+struct sm-ftl {
+ struct mtd-blktrans-dev *trans;
+
+ struct mutex mutex; /* protects the structure */
+ struct ftl-zone *zones; /* FTL tables for each zone */
+
+ /* Media information */
+ int block-size; /* block size in bytes */
+ int zone-size; /* zone size in blocks */
+ int zone-count; /* number of zones */
+ int max-lba; /* maximum lba in a zone */
+ int smallpagenand; /* 256 bytes/page nand */
+ int readonly; /* is FS readonly */
+ int unstable;
+ int cis-block; /* CIS block location */
+ int cis-boffset; /* CIS offset in the block */
+ void *cis-buffer; /* tmp buffer for cis reads */
+
+ /* Cache */
+ int cache-block; /* block number of cached block */
+ int cache-zone; /* zone of cached block */
+ unsigned char *cache-data; /* cached block data */
+ long unsigned int cache-data-invalid-bitmap;
+ int cache-clean;
+ struct work-struct flush-work;
+ struct timer-list timer;
+
+ /* Async erase stuff */
+ struct completion erase-completion;
+
+ /* Geometry stuff */
+ int heads;
+ int sectors;
+ int cylinders;
+};
+
+struct chs-entry {
+ unsigned long size;
+ unsigned short cyl;
+ unsigned char head;
+ unsigned char sec;
+};
+
+
+#define SM-FTL-PARTN-BITS 3
+
+#define sm-printk(format, ...)
+ printk(KERN-WARNING "sm-ftl" ": " format "
", ## +#else
+
+#define dbg(format, ...)
+
+#endif
+
+static void sm-erase-callback(struct erase-info *self);
+static int sm-erase-block(struct sm-ftl *ftl, int zone-num, u16 block,
+ int put-free);
+static void sm-mark-block-bad(struct sm-ftl *ftl, int zone-num, int block);
+
+static int sm-recheck-media(struct sm-ftl *ftl);
PATCH 13/17 - MTD: export few functions from nand_base.c by Maxim Levitsky on
2010-02-04T22:33:04+00:00
This exports:
nand-do-read-oob
nand-do-write-oob
nand-get-device
nand-put-device
This functions will be used to implement custom oob based
bad block handling in upcoming smartmedia common module
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
-static int nand-get-device(struct nand-chip *chip, struct mtd-info *mtd,
- int new-state);
-
-static int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
- struct mtd-oob-ops *ops);
/*
* For devices which display every fart in the system on a separate LED. Is
@@ -114,7 +109,7 @@ DEFINE-LED-TRIGGER(nand-led-trigger);
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
-static void nand-release-device(struct mtd-info *mtd)
+void nand-release-device(struct mtd-info *mtd)
{
struct nand-chip *chip = mtd->priv;
@@ -128,6 +123,7 @@ static void nand-release-device(struct mtd-info *mtd)
wake-up(&chip->controller->wq);
spin-unlock(&chip->controller->lock);
}
+EXPORT-SYMBOL-GPL(nand-release-device);
/**
* nand-read-byte - [DEFAULT] read one byte from the chip
@@ -721,7 +717,7 @@ static void panic-nand-get-device(struct nand-chip *chip,
*
* Get the device and lock it for exclusive access
*/
-static int
+int
nand-get-device(struct nand-chip *chip, struct mtd-info *mtd, int new-state)
{
spinlock-t *lock = &chip->controller->lock;
@@ -753,6 +749,7 @@ nand-get-device(struct nand-chip *chip, struct mtd-info *mtd, int new-state)
remove-wait-queue(wq, &wait);
goto retry;
}
+EXPORT-SYMBOL-GPL(nand-get-device);
/**
* panic-nand-wait - [GENERIC] wait until the command is done
@@ -1532,7 +1529,7 @@ static int nand-write-oob-syndrome(struct mtd-info *mtd,
*
* NAND read out-of-band data from the spare area
*/
-static int nand-do-read-oob(struct mtd-info *mtd, loff-t from,
+int nand-do-read-oob(struct mtd-info *mtd, loff-t from,
struct mtd-oob-ops *ops)
{
int page, realpage, chipnr, sndcmd = 1;
@@ -1616,6 +1613,7 @@ static int nand-do-read-oob(struct mtd-info *mtd, loff-t from,
ops->oobretlen = ops->ooblen;
return 0;
}
+EXPORT-SYMBOL-GPL(nand-do-read-oob);
/**
* nand-read-oob - [MTD Interface] NAND read data and/or out-of-band
@@ -2108,7 +2106,7 @@ static int nand-write(struct mtd-info *mtd, loff-t to, size-t len,
*
* NAND write out-of-band
*/
-static int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
+int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
struct mtd-oob-ops *ops)
{
int chipnr, page, status, len;
@@ -2179,6 +2177,7 @@ static int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
return 0;
}
+EXPORT-SYMBOL-GPL(nand-do-write-oob);
/**
* nand-write-oob - [MTD Interface] NAND write data and/or out-of-band
@@ -3080,6 +3079,7 @@ EXPORT-SYMBOL-GPL(nand-scan-ident);
EXPORT-SYMBOL-GPL(nand-scan-tail);
EXPORT-SYMBOL-GPL(nand-release);
+
static int @@ -469,6 +469,18 @@ extern int nand-erase-nand(struct mtd-info *mtd, struct erase-info *instr,
extern int nand-do-read(struct mtd-info *mtd, loff-t from, size-t len,
size-t * retlen, uint8-t * buf);
+extern int nand-do-read-oob(struct mtd-info *mtd, loff-t from,
+ struct mtd-oob-ops *ops);
+
+extern int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
+ struct mtd-oob-ops *ops);
+
+extern int nand-get-device(struct nand-chip *chip,
+ struct mtd-info *mtd, int new-state);
+
+extern void nand-release-device(struct mtd-info *mtd);
+
+
/**
* struct platform-nand-chip - chip level device structure
* @nr-chips: max. number of chips to scan for
PATCH 09/17 - MTD: nand: make MTD_OOB_PLACE work correctly. by Maxim Levitsky on
2010-02-04T22:33:04+00:00
MTD-OOB-PLACE is supposed to read/write raw oob data similiar to MTD-OOB-RAW
however due to a bug, currently its not possible to read more data that
specified in oob 'free' regions
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff uint32-t oobreadlen = ops->ooblen;
+ uint32-t max-oobsize = ops->mode == MTD-OOB-AUTO ?
+ mtd->oobavail : mtd->oobsize;
+
uint8-t *bufpoi, *oob, *buf;
stats = mtd->ecc-stats;
@@ -1282,10 +1285,11 @@ static int nand-do-read-ops(struct mtd-info *mtd, loff-t from,
buf += bytes;
if (unlikely(oob)) {
+
/* Raw mode does data:oob:data:oob */
if (ops->mode != MTD-OOB-RAW) {
int toread = min(oobreadlen,
- chip->ecc.layout->oobavail);
+ max-oobsize);
if (toread) {
oob = nand-transfer-oob(chip,
oob, ops, toread);
PATCH 12/17 - MTD: nand: make suspend work if device is accessed by kernel threads. by Maxim Levitsky on
2010-02-04T22:33:31+00:00
Since all userspace threads are frozen at the time the nand-suspend is called,
they aren't inside any nand function.
We don't call try-to-freeze in nand ether. Thus the only user that can be insize
the nand functions is an non freezeable kernel thread.
Thus we can safely wait for it to finish.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff return 0;
- } else {
- spin-unlock(lock);
- return -EAGAIN;
}
}
set-current-state(TASK-UNINTERRUPTIBLE);
PATCH 11/17 - MTD: nand: fix bug that prevented write of more that one page by ->write_oob by Maxim Levitsky on
2010-02-04T22:33:32+00:00
Although nand-do-write-ops intends to allow such mode, it fails do do so
Probably this was never tested
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff */
-static uint8-t *nand-fill-oob(struct nand-chip *chip, uint8-t *oob,
- struct mtd-oob-ops *ops)
+static uint8-t *nand-fill-oob(struct nand-chip *chip, uint8-t *oob, size-t len,
+ struct mtd-oob-ops *ops)
{
- size-t len = ops->ooblen;
-
switch(ops->mode) {
case MTD-OOB-PLACE:
@@ -1938,6 +1936,7 @@ static int nand-do-write-ops(struct mtd-info *mtd, loff-t to,
int chipnr, realpage, page, blockmask, column;
struct nand-chip *chip = mtd->priv;
uint32-t writelen = ops->len;
+ uint32-t oobwritelen = ops->ooblen;
uint8-t *oob = ops->oobbuf;
uint8-t *buf = ops->datbuf;
int ret, subpage;
@@ -1994,8 +1993,11 @@ static int nand-do-write-ops(struct mtd-info *mtd, loff-t to,
wbuf = chip->buffers->databuf;
}
- if (unlikely(oob))
- oob = nand-fill-oob(chip, oob, ops);
+ if (unlikely(oob)) {
+ size-t len = min(oobwritelen, mtd->oobsize);
+ oob = nand-fill-oob(chip, oob, len, ops);
+ oobwritelen -= len;
+ }
ret = chip->write-page(mtd, chip, wbuf, page, cached,
(ops->mode == MTD-OOB-RAW));
@@ -2169,7 +2171,7 @@ static int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
chip->pagebuf = -1;
memset(chip->oob-poi, 0xff, mtd->oobsize);
- nand-fill-oob(chip, ops->oobbuf, ops);
+ nand-fill-oob(chip, ops->oobbuf, ops->ooblen, ops);
status = chip->ecc.write-oob(mtd, chip, page & chip->pagemask);
memset(chip->oob-poi, 0xff, mtd->oobsize);
PATCH 10/17 - MTD: nand: make reads using MTD_OOB_RAW affect only ECC validation by Maxim Levitsky on
2010-02-04T22:33:52+00:00
This changes the behavier of MTD-OOB-RAW. It used to read both OOB and data
to the data buffer, however you would still need to specify the dummy oob buffer.
This is only used in one place, but makes it hard to read data+oob without ECC
test, thus I removed that behavier, and fixed the user.
Now MTD-OOB-RAW behaves like MTD-OOB-PLACE, but doesn't do ECC validation
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
- /* Raw mode does data:oob:data:oob */
- if (ops->mode != MTD-OOB-RAW) {
int toread = min(oobreadlen,
max-oobsize);
if (toread) {
@@ -1295,9 +1293,6 @@ static int nand-do-read-ops(struct mtd-info *mtd, loff-t from,
oob, ops, toread);
oobreadlen -= toread;
}
- } else
- buf = nand-transfer-oob(chip,
- buf, ops, mtd->oobsize);
}
if (!(chip->options & NAND-NO-READRDY)) {
diff struct mtd-oob-ops ops;
+ int res;
ops.mode = MTD-OOB-RAW;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
- ops.oobbuf = buf;
- ops.datbuf = buf;
- ops.len = len;
- return mtd->read-oob(mtd, offs, &ops);
+
+ while (len > 0) {
+ if (len <= mtd->writesize) {
+ ops.oobbuf = buf + len;
+ ops.datbuf = buf;
+ ops.len = len;
+ return mtd->read-oob(mtd, offs, &ops);
+ } else {
+ ops.oobbuf = buf + mtd->writesize;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ res = mtd->read-oob(mtd, offs, &ops);
+
+ if (res)
+ return res;
+ }
+
+ buf += mtd->oobsize + mtd->writesize;
+ len -= mtd->writesize;
+ }
+ return 0;
}
/*
diff * which are defined by the ecclayout
- * MTD-OOB-RAW: mode to read raw data+oob in one chunk. The oob data
- * is inserted into the data. Thats a raw image of the
- * flash contents.
+ * MTD-OOB-RAW: mode to read oob and data without doing ECC checking
*/
typedef enum {
MTD-OOB-PLACE,
PATCH 07/17 - blktrans: allow FTL drivers to export sysfs attributes by Maxim Levitsky on
2010-02-04T22:34:06+00:00
This patch add ability to export sysfs attributes below
block disk device.
This can be used to pass UDEV information about the FTL
and could include vendor, serial, version, etc...
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
-
/* Create the request queue */
spin-lock-init(&new->queue-lock);
new->rq = blk-init-queue(mtd-blktrans-request, &new->queue-lock);
@@ -349,6 +348,11 @@ int add-mtd-blktrans-dev(struct mtd-blktrans-dev *new)
new->open = 0;
add-disk(gd);
+ if (new->disk-attributes)
+ sysfs-create-group(&disk-to-dev(gd)->kobj,
+ new->disk-attributes);
+
+
return 0;
error4:
blk-cleanup-queue(new->rq);
@@ -375,6 +379,11 @@ int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
/* stop new requests to arrive */
del-gendisk(old->disk);
+
+ if (old->disk-attributes)
+ sysfs-remove-group(&disk-to-dev(old->disk)->kobj,
+ old->disk-attributes);
+
/* flush current requests */
spin-lock-irqsave(&old->queue-lock, flags);
old->deleted = 1;
diff the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
PATCH 05/17 - blktrans: add proper locking by Maxim Levitsky on
2010-02-04T22:34:25+00:00
First, use lockless versions of get/put-mtd-device
We don't care what happened to mtd table, because we have a pointer
to mtd device. It guaranteed to exist till we do final put-mtd-device
Also add locking to prevent all kinds or races
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff struct mtd-blktrans-ops *tr = dev->tr;
- int ret = -ENODEV;
-
- if (!get-mtd-device(NULL, dev->mtd->index))
- goto out;
+ int ret = 0;
+ mutex-lock(&dev->lock);
if (dev->open++)
goto out;
if (dev->deleted)
goto out;
+ ret = -ENODEV;
if (!try-module-get(tr->owner))
- goto out-tr;
+ goto out;
- /* FIXME: Locking. A hot pluggable device can go away
- (del-mtd-device can be called for it) without its module
- being unloaded. */
- dev->mtd->usecount++;
+ if (- dev->mtd->usecount }
out:
+ mutex-unlock(&dev->lock);
return ret;
}
@@ -159,17 +159,10 @@ static int blktrans-release(struct gendisk *disk, fmode-t mode)
struct mtd-blktrans-ops *tr = dev->tr;
int ret = 0;
+ mutex-lock(&dev->lock);
if (- if (!ret) {
- dev->mtd->usecount@@ -179,33 +172,59 @@ static int blktrans-release(struct gendisk *disk, fmode-t mode)
return 0;
}
+ ret = tr->release ? tr->release(dev) : 0;
+ module-put(tr->owner);
+
+ if (dev->mtd)
+ {
struct mtd-blktrans-dev *dev = bdev->bd-disk->private-data;
+ int error;
+
+ mutex-lock(&dev->lock);
+ error = -ENODEV;
+ if (dev->deleted)
+ goto out;
+
+ error = -ENOTTY;
if (dev->tr->getgeo)
- return dev->tr->getgeo(dev, geo);
- return -ENOTTY;
+ error = dev->tr->getgeo(dev, geo);
+out:
+ mutex-unlock(&dev->lock);
+ return error;
}
static int blktrans-ioctl(struct block-device *bdev, fmode-t mode,
unsigned int cmd, unsigned long arg)
{
struct mtd-blktrans-dev *dev = bdev->bd-disk->private-data;
- struct mtd-blktrans-ops *tr = dev->tr;
+ int error = -ENODEV;
+
+ mutex-lock(&dev->lock);
+
+ if (dev->deleted)
+ goto out;
+
+ error = -ENOTTY;
switch (cmd) {
case BLKFLSBUF:
- if (tr->flush)
- return tr->flush(dev);
- /* The core code did the work, we had nothing to do. */
- return 0;
+ if (dev->tr->flush)
+ error = dev->tr->flush(dev);
+ break;
default:
- return -ENOTTY;
+ break;
}
+out:
+ mutex-unlock(&dev->lock);
+ return error;
}
static const struct block-device-operations mtd-blktrans-ops = {
@@ -353,16 +372,19 @@ int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
/* Stop the thread */
kthread-stop(old->thread);
+ /* Tell trans driver to release the device */
+ mutex-lock(&old->lock);
+
if (old->open) {
if (old->tr->release)
old->tr->release(old);
- put-mtd-device(old->mtd);
+ + mutex-unlock(&old->lock);
blk-cleanup-queue(old->rq);
return 0;
}
PATCH 06/17 - blktrans: flush all requests before we remove the device by Maxim Levitsky on
2010-02-04T22:34:38+00:00
Flush all requests, so we can be sure we don't deadlock the system later
when we remove the disk queue.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff struct mtd-blktrans-dev *dev = rq->queuedata;
- wake-up-process(dev->thread);
+ struct request *req = NULL;
+
+ if (dev->deleted)
+ while ((req = blk-fetch-request(rq)) != NULL)
+
int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
{
+ unsigned long flags;
+
if (mutex-trylock(&mtd-table-mutex)) {
mutex-unlock(&mtd-table-mutex);
BUG();
@@ -367,7 +375,11 @@ int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
/* stop new requests to arrive */
del-gendisk(old->disk);
+ /* flush current requests */
+ spin-lock-irqsave(&old->queue-lock, flags);
old->deleted = 1;
+ blk-start-queue(old->rq);
+ spin-unlock-irqrestore(&old->queue-lock, flags);
/* Stop the thread */
kthread-stop(old->thread);
PATCH 03/17 - blktrans: track open and close calls. by Maxim Levitsky on
2010-02-04T22:34:47+00:00
This patch adds tracking for open and close calls.
Now trans ->open and ->release are never called twise in a row
->release is also called once before mtd device disappers
Proper locking will be added in follow up patch
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
+ if (dev->open++)
+ goto out;
+
if (!try-module-get(tr->owner))
goto out-tr;
@@ -153,6 +156,9 @@ static int blktrans-release(struct gendisk *disk, fmode-t mode)
struct mtd-blktrans-ops *tr = dev->tr;
int ret = 0;
+ if ( if (new->readonly)
set-disk-ro(gd, 1);
+ new->open = 0;
add-disk(gd);
return 0;
@@ -333,6 +340,16 @@ int del-mtd-blktrans-dev(struct mtd-blktrans-dev *old)
/* Stop the thread */
kthread-stop(old->thread);
+ if (old->open) {
+ if (old->tr->release)
+ old->tr->release(old);
+ put-mtd-device(old->mtd);
+ }
+
+ /* From now on, no calls into trans can be made */
+ /* Mtd device will be gone real soon now */
+ old->mtd = NULL;
+
blk-cleanup-queue(old->rq);
return 0;
}
diff int readonly;
+ int open;
struct gendisk *disk;
struct task-struct *thread;
struct request-queue *rq;
PATCH 01/17 - MTD: create lockless versions of {get,put}_mtd_device This will be used to resolve deadlock in block translation l by Maxim Levitsky on
2010-02-04T22:35:00+00:00
These functions can be used as long as we don't need access to global mtd table, but have
a pointer to the mtd device.
Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
-diff
- if (!ret)
- goto out-unlock;
-
- if (!try-module-get(ret->owner))
- goto out-unlock;
-
- if (ret->get-device) {
- err = ret->get-device(ret);
- if (err)
- goto out-put;
+ if (!ret) {
+ ret = ERR-PTR(err);
+ goto out;
}
- ret->usecount++;
+ err =
-out-put:
- module-put(ret->owner);
-out-unlock:
- mutex-unlock(&mtd-table-mutex);
- return ERR-PTR(err);
+
+int + if (mtd->get-device) {
+
+ err = mtd->get-device(mtd);
+
+ if (err) {
+ module-put(mtd->owner);
+ return err;
+ }
+ }
+ mtd->usecount++;
+ return 0;
}
/**
@@ -524,14 +535,19 @@ out-unlock:
void put-mtd-device(struct mtd-info *mtd)
{
- int c;
-
mutex-lock(&mtd-table-mutex);
- c = +{
+
module-put(mtd->owner);
}
@@ -569,7 +585,9 @@ EXPORT-SYMBOL-GPL(add-mtd-device);
EXPORT-SYMBOL-GPL(del-mtd-device);
EXPORT-SYMBOL-GPL(get-mtd-device);
EXPORT-SYMBOL-GPL(get-mtd-device-nm);
+EXPORT-SYMBOL-GPL(index 0f32a9b..662d747 100644
+extern void the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Re: PATCH 11/17 - MTD: nand: fix bug that prevented write of more that one page by ->write_oob by stanley.miao on
2010-02-05T01:21:08+00:00
ops->ooblen is the oob bytes to write, you add a argument for
nand-fill-oob to do this,
it is redundant.
I know the bug you want to fix, the ops->ooblen may be illegal. But this
patch can't this
problem. If (ops->offset + ops->ooblen) > mtd->oobsize, it still will
write beyond a page.
I think the right method is that nand-do-write-ops do the check like
nand-do-write-oob,
I have the patch yesterday.
http://patchwork.ozlabs.org/patch/44450/
Stanley.
Maxim Levitsky wrote:
> Although nand-do-write-ops intends to allow such mode, it fails do do so
> Probably this was never tested
>
> Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
> -ops->ooblen is the oob bytes to write, you add a prara
> {
> - size-t len = ops->ooblen;
> -
> switch(ops->mode) {
>
> case MTD-OOB-PLACE:
> @@ -1938,6 +1936,7 @@ static int nand-do-write-ops(struct mtd-info *mtd, loff-t to,
> int chipnr, realpage, page, blockmask, column;
> struct nand-chip *chip = mtd->priv;
> uint32-t writelen = ops->len;
> + uint32-t oobwritelen = ops->ooblen;
> uint8-t *oob = ops->oobbuf;
> uint8-t *buf = ops->datbuf;
> int ret, subpage;
> @@ -1994,8 +1993,11 @@ static int nand-do-write-ops(struct mtd-info *mtd, loff-t to,
> wbuf = chip->buffers->databuf;
> }
>
> - if (unlikely(oob))
> - oob = nand-fill-oob(chip, oob, ops);
> + if (unlikely(oob)) {
> + size-t len = min(oobwritelen, mtd->oobsize);
> + oob = nand-fill-oob(chip, oob, len, ops);
> + oobwritelen -= len;
> + }
>
> ret = chip->write-page(mtd, chip, wbuf, page, cached,
> (ops->mode == MTD-OOB-RAW));
> @@ -2169,7 +2171,7 @@ static int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
> chip->pagebuf = -1;
>
> memset(chip->oob-poi, 0xff, mtd->oobsize);
> - nand-fill-oob(chip, ops->oobbuf, ops);
> + nand-fill-oob(chip, ops->oobbuf, ops->ooblen, ops);
> status = chip->ecc.write-oob(mtd, chip, page & chip->pagemask);
> memset(chip->oob-poi, 0xff, mtd->oobsize);
>
>
Re: PATCH 13/17 - MTD: export few functions from nand_base.c by stanley.miao on
2010-02-05T01:27:50+00:00
Maxim Levitsky wrote:
> This exports:
>
> nand-do-read-oob
> nand-do-write-oob
>
nand-do-read-oob and nand-do-write-oob can't be exported. They are internal
functions in NAND subsystem. If you want use them, please use mtd->read-oob
and mtd->write-oob.
Stanley.
> nand-get-device
> nand-put-device
>
> This functions will be used to implement custom oob based
> bad block handling in upcoming smartmedia common module
>
> Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
>
Re: PATCH 11/17 - MTD: nand: fix bug that prevented write of more that one page by ->write_oob by stanley.miao on
2010-02-05T01:29:50+00:00
ops->ooblen is the oob bytes to write, you add a argument for
nand-fill-oob to do this, it is redundant.
I know the bug you want to fix, the ops->ooblen may be illegal. But this
patch can't solve this problem. If (ops->offset + ops->ooblen) >
mtd->oobsize, it still will write beyond one page.
I think the right method to solve it is that nand-do-write-ops do the
check like nand-do-write-oob. I have sent the patch yesterday.
http://patchwork.ozlabs.org/patch/44450/
Stanley.
Maxim Levitsky wrote:
> Although nand-do-write-ops intends to allow such mode, it fails do do so
> Probably this was never tested
>
> Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
> -ops->ooblen is the oob bytes to write, you add a prara
> {
> - size-t len = ops->ooblen;
> -
> switch(ops->mode) {
>
> case MTD-OOB-PLACE:
> @@ -1938,6 +1936,7 @@ static int nand-do-write-ops(struct mtd-info *mtd, loff-t to,
> int chipnr, realpage, page, blockmask, column;
> struct nand-chip *chip = mtd->priv;
> uint32-t writelen = ops->len;
> + uint32-t oobwritelen = ops->ooblen;
> uint8-t *oob = ops->oobbuf;
> uint8-t *buf = ops->datbuf;
> int ret, subpage;
> @@ -1994,8 +1993,11 @@ static int nand-do-write-ops(struct mtd-info *mtd, loff-t to,
> wbuf = chip->buffers->databuf;
> }
>
> - if (unlikely(oob))
> - oob = nand-fill-oob(chip, oob, ops);
> + if (unlikely(oob)) {
> + size-t len = min(oobwritelen, mtd->oobsize);
> + oob = nand-fill-oob(chip, oob, len, ops);
> + oobwritelen -= len;
> + }
>
> ret = chip->write-page(mtd, chip, wbuf, page, cached,
> (ops->mode == MTD-OOB-RAW));
> @@ -2169,7 +2171,7 @@ static int nand-do-write-oob(struct mtd-info *mtd, loff-t to,
> chip->pagebuf = -1;
>
> memset(chip->oob-poi, 0xff, mtd->oobsize);
> - nand-fill-oob(chip, ops->oobbuf, ops);
> + nand-fill-oob(chip, ops->oobbuf, ops->ooblen, ops);
> status = chip->ecc.write-oob(mtd, chip, page & chip->pagemask);
> memset(chip->oob-poi, 0xff, mtd->oobsize);
>
>
Re: PATCH 10/17 - MTD: nand: make reads using MTD_OOB_RAW affect only ECC validation by stanley.miao on
2010-02-05T01:43:20+00:00
Maxim Levitsky wrote:
> This changes the behavier of MTD-OOB-RAW. It used to read both OOB and data
> to the data buffer, however you would still need to specify the dummy oob buffer.
>
> This is only used in one place, but makes it hard to read data+oob without ECC
> test, thus I removed that behavier, and fixed the user.
>
> Now MTD-OOB-RAW behaves like MTD-OOB-PLACE, but doesn't do ECC validation
>
> Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
> -I am not sure if it is legal to modify the behavior of MTD-OOB-RAW. But
if you
remove the "if" command, the following code should be indent again.
Stanley.
> int toread = min(oobreadlen,
> max-oobsize);
> if (toread) {
> @@ -1295,9 +1293,6 @@ static int nand-do-read-ops(struct mtd-info *mtd, loff-t from,
> oob, ops, toread);
> oobreadlen -= toread;
> }
> - } else
> - buf = nand-transfer-oob(chip,
> - buf, ops, mtd->oobsize);
> }
>
> if (!(chip->options & NAND-NO-READRDY)) {
> diff > struct mtd-oob-ops ops;
> + int res;
>
> ops.mode = MTD-OOB-RAW;
> ops.ooboffs = 0;
> ops.ooblen = mtd->oobsize;
> - ops.oobbuf = buf;
> - ops.datbuf = buf;
> - ops.len = len;
>
> - return mtd->read-oob(mtd, offs, &ops);
> +
> + while (len > 0) {
> + if (len <= mtd->writesize) {
> + ops.oobbuf = buf + len;
> + ops.datbuf = buf;
> + ops.len = len;
> + return mtd->read-oob(mtd, offs, &ops);
> + } else {
> + ops.oobbuf = buf + mtd->writesize;
> + ops.datbuf = buf;
> + ops.len = mtd->writesize;
> + res = mtd->read-oob(mtd, offs, &ops);
> +
> + if (res)
> + return res;
> + }
> +
> + buf += mtd->oobsize + mtd->writesize;
> + len -= mtd->writesize;
> + }
> + return 0;
> }
>
> /*
> diff > * which are defined by the ecclayout
> - * MTD-OOB-RAW: mode to read raw data+oob in one chunk. The oob data
> - * is inserted into the data. Thats a raw image of the
> - * flash contents.
> + * MTD-OOB-RAW: mode to read oob and data without doing ECC checking
> */
> typedef enum {
> MTD-OOB-PLACE,
>
Re: PATCH 10/17 - MTD: nand: make reads using MTD_OOB_RAW affect only ECC validation by Vitaly Wool on
2010-02-05T07:56:17+00:00
On Fri, Feb 5, 2010 at 12:30 AM, Maxim Levitsky <maximlevitsky@gmail.com> wrote:
> This changes the behavier of MTD-OOB-RAW. It used to read both OOB and data
> to the data buffer, however you would still need to specify the dummy oob buffer.
>
> This is only used in one place, but makes it hard to read data+oob without ECC
> test, thus I removed that behavier, and fixed the user.
>
> Now MTD-OOB-RAW behaves like MTD-OOB-PLACE, but doesn't do ECC validation
>
> Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
> -Could you please provide a clearer description of why you think this
is necessary?
Thanks,
Vitaly