linux.kernel - 26 new messages in 14 topics - digest
linux.kernel
http://groups.google.com/group/linux.kernel?hl=en
Today's topics:
* of/gpio: Implement GPIOLIB notifier hooks - 2 messages, 1 author
http://groups.google.com/group/linux.kernel/t/ba7a7613f0e1b508?hl=en
* : cpuidle: Design documentation patch - 1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/32650ac38a3d07b9?hl=en
* blktrans: allow FTL drivers to export sysfs attributes - 7 messages, 1
author
http://groups.google.com/group/linux.kernel/t/908c78315d608847?hl=en
* Oops in kswapd (2.6.31.5) - 1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/bc817685b3c95404?hl=en
* MTD: create lockless versions of {get,put}_mtd_device This will be used to
resolve deadlock in block translation layer. - 3 messages, 2 authors
http://groups.google.com/group/linux.kernel/t/218cbc44b1591b85?hl=en
* gpiolib: Introduce chip addition/removal notifier - 1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/a8c118be63f283f3?hl=en
* [PATCH 5/7] xen: Make event channel work with PV featured HVM - 2 messages,
2 authors
http://groups.google.com/group/linux.kernel/t/ecc3a727bb84d6bb?hl=en
* x86: ptrace and core-dump extensions for xstate - 1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/78d570b1c5c8bad5?hl=en
* of platforms: Move common static initialization to of_node_init() - 3
messages, 1 author
http://groups.google.com/group/linux.kernel/t/174aef7bb6383d0d?hl=en
* of: Introduce safe accessors for node->data - 1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/1681b96c0219740d?hl=en
* [PATCH 8/9] PCI / ACPI / PM: Platform support for PCI PME wake-up (rev. 7) -
1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/2164e63bc6a3877c?hl=en
* LZO irreversible output? - 1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/d82a693a6af1f717?hl=en
* Congratulations - You have won - 1 messages, 1 author
http://groups.google.com/group/linux.kernel/t/0adaefa176a7e02d?hl=en
* Performance regression in scsi sequential throughput (iozone) due to "e084b -
page-allocator: preserve PFN ordering when __GFP_COLD is set" - 1 messages, 1
author
http://groups.google.com/group/linux.kernel/t/0f198fe3053e9f98?hl=en
==============================================================================
TOPIC: of/gpio: Implement GPIOLIB notifier hooks
http://groups.google.com/group/linux.kernel/t/ba7a7613f0e1b508?hl=en
==============================================================================
== 1 of 2 ==
Date: Tues, Feb 9 2010 9:10 am
From: Grant Likely
On Fri, Feb 5, 2010 at 1:32 PM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> This patch implements GPIOLIB notifier hooks, and thus makes device-enabled
> GPIO chips (i.e. the ones that have gpio_chip->dev specified) automatically
> attached to the OpenFirmware subsystem. Which means that now we can handle
> I2C and SPI GPIO chips almost* transparently.
>
> * "Almost" because some chips still require platform data, and for these
> chips OF-glue is still needed, though with this support the glue will
> be much smaller.
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> ---
> drivers/of/gpio.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 100 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
> index 12c4af0..9d8df77 100644
> --- a/drivers/of/gpio.c
> +++ b/drivers/of/gpio.c
> @@ -13,6 +13,7 @@
>
> #include <linux/kernel.h>
> #include <linux/errno.h>
> +#include <linux/notifier.h>
> #include <linux/io.h>
> #include <linux/of.h>
> #include <linux/of_gpio.h>
> @@ -236,3 +237,102 @@ err0:
> return ret;
> }
> EXPORT_SYMBOL(of_mm_gpiochip_add);
> +
> +/**
> + * of_gpiochip_register_simple - Register a chip with the OF GPIO subsystem
> + * @chip pointer to a GPIO chip
> + * @np: device node to register the GPIO chip with
> + *
> + * This function registers a GPIO chip with the OF infrastructure. It is
> + * assumed that the chip was previsously allocated and added to a generic
> + * GPIOLIB framework (using gpiochip_add() function).
> + *
> + * The `simple' name means that the chip is using simple two-cells scheme for
> + * the gpio-specifier.
> + */
> +static int of_gpiochip_register_simple(struct gpio_chip *chip,
> + struct device_node *np)
> +{
> + struct of_gpio_chip *of_gc;
> +
> + if (np->data) {
> + WARN_ON(1);
> + return -EBUSY;
> + }
> +
> + of_gc = kzalloc(sizeof(*of_gc), GFP_KERNEL);
> + if (!of_gc)
> + return -ENOMEM;
> +
> + of_gc->gpio_cells = 2;
> + of_gc->xlate = of_gpio_simple_xlate;
> + of_gc->chip = chip;
One concern.
How does an OF-aware GPIO driver override these settings? What is to
be done when a GPIO chip requires a different xlate hook? Or a
different number of gpio_cells?
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 2 of 2 ==
Date: Tues, Feb 9 2010 9:20 am
From: Grant Likely
On Fri, Feb 5, 2010 at 1:32 PM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> This patch implements GPIOLIB notifier hooks, and thus makes device-enabled
> GPIO chips (i.e. the ones that have gpio_chip->dev specified) automatically
> attached to the OpenFirmware subsystem. Which means that now we can handle
> I2C and SPI GPIO chips almost* transparently.
>
> * "Almost" because some chips still require platform data, and for these
> chips OF-glue is still needed, though with this support the glue will
> be much smaller.
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> ---
> +static struct notifier_block of_gpio_nb = {
> + .notifier_call = of_gpio_notify,
> +};
> +
> +static int __init of_gpio_notifier_init(void)
> +{
> + return blocking_notifier_chain_register(&gpio_notifier, &of_gpio_nb);
> +}
> +arch_initcall(of_gpio_notifier_init);
Another concern; if any gpio chips get registered before this
arch_initcall (not sure if it is possible or not), then those chips
won't get registered with the of gpio infrastructure.
g.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
==============================================================================
TOPIC: : cpuidle: Design documentation patch
http://groups.google.com/group/linux.kernel/t/32650ac38a3d07b9?hl=en
==============================================================================
== 1 of 1 ==
Date: Tues, Feb 9 2010 9:10 am
From: Randy Dunlap
On 02/09/10 00:29, Arun R Bharadwaj wrote:
> * Arun R Bharadwaj <arun@linux.vnet.ibm.com> [2010-02-09 13:58:16]:
>
> This patch adds a little information about the redesigned cpuidle
> infrastructure in Documentation/cpuidle/core.txt
>
> Signed-off-by: Arun R Bharadwaj <arun@linux.vnet.ibm.com>
> ---
> Documentation/cpuidle/core.txt | 35 +++++++++++++++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
>
> Index: linux.trees.git/Documentation/cpuidle/core.txt
> ===================================================================
> --- linux.trees.git.orig/Documentation/cpuidle/core.txt
> +++ linux.trees.git/Documentation/cpuidle/core.txt
> @@ -21,3 +21,38 @@ which can be used to switch governors at
> is meant for developer testing only. In normal usage, kernel picks the
> best governor based on governor ratings.
> SEE ALSO: sysfs.txt in this directory.
> +
> +Design:
> +
> +Cpuidle allows for registration of multiple sets of idle routines.
> +The latest registered set is used by cpuidle governors as the current
> +active set to choose the right idle state. This set is managed as a
> +list and each time the newly registered set is added to the head of the
> +list and made the current active set.
> +
> +An example of how this would work on x86 is shown below.
> +
+----------------- -----------------
+| | | |
+| choose b/w | mwait is chosen | mwait |
+| mwait, poll, |-------------------------------------> |(current active|
+| default, c1e | register to cpuidle | set) |
+| | with mwait as the idle routine | |
+----------------- -----------------
What is "b/w" above? "between"? I've see "btw" used a few times as an
abbreviation for between, but I don't recall ever seeing b/w.
OTOH, I have seen b/w used for bandwidth.
> +
> +
> +----------------- -----------------
> +| | | c1, c2, c3 |
> +| ACPI | register to cpuidle | (current) |
> +| discovery |-------------------------------------> |---------------|
> +| | with c1, c2, c3 | mwait |
> +| | as set of idle routines | |
> +----------------- -----------------
> +
> +With this mechanism, a module can register and unregister its set of
> +idle routines at run time in a clean manner.
> +
> +The main idle routine called inside cpu_idle() of every arch is defined in
> +driver/cpuidle/cpuidle.c which would in turn call the idle routine selected
drivers/
> +by the governor. If the CONFIG_CPU_IDLE is disabled, the arch needs to
> +provide an alternate definition for cpuidle_idle_call().
--
~Randy
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
==============================================================================
TOPIC: blktrans: allow FTL drivers to export sysfs attributes
http://groups.google.com/group/linux.kernel/t/908c78315d608847?hl=en
==============================================================================
== 1 of 7 ==
Date: Tues, Feb 9 2010 9:10 am
From: Maxim Levitsky
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>
---
drivers/mtd/mtd_blkdevs.c | 11 ++++++++++-
include/linux/mtd/blktrans.h | 2 ++
2 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 9992b2d..ac9d3f5 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -316,7 +316,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
set_capacity(gd, (new->size * tr->blksize) >> 9);
-
/* 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 --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h
index e276aca..faf1e54 100644
--- a/include/linux/mtd/blktrans.h
+++ b/include/linux/mtd/blktrans.h
@@ -9,6 +9,7 @@
#define __MTD_TRANS_H__
#include <linux/mutex.h>
+#include <linux/sysfs.h>
struct hd_geometry;
struct mtd_info;
@@ -27,6 +28,7 @@ struct mtd_blktrans_dev {
int deleted;
int open;
struct gendisk *disk;
+ struct attribute_group *disk_attributes;
struct task_struct *thread;
struct request_queue *rq;
spinlock_t queue_lock;
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 2 of 7 ==
Date: Tues, Feb 9 2010 9:10 am
From: Maxim Levitsky
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>
---
drivers/mtd/mtd_blkdevs.c | 17 +++++++++++++++++
include/linux/mtd/blktrans.h | 1 +
2 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 93c87af..9ac8f22 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -128,6 +128,9 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
if (!get_mtd_device(NULL, dev->mtd->index))
goto out;
+ 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 (--dev->open)
+ return 0;
+
if (tr->release)
ret = tr->release(dev);
@@ -304,6 +310,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
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 --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h
index a4b3928..507f7b2 100644
--- a/include/linux/mtd/blktrans.h
+++ b/include/linux/mtd/blktrans.h
@@ -24,6 +24,7 @@ struct mtd_blktrans_dev {
int devnum;
unsigned long size;
int readonly;
+ int open;
struct gendisk *disk;
struct task_struct *thread;
struct request_queue *rq;
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 3 of 7 ==
Date: Tues, Feb 9 2010 9:10 am
From: Maxim Levitsky
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>
---
MAINTAINERS | 6 +
drivers/mtd/nand/Kconfig | 11 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/r822.c | 1110 +++++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/nand/r822.h | 154 +++++++
include/linux/pci_ids.h | 2 +
6 files changed, 1284 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/nand/r822.c
create mode 100644 drivers/mtd/nand/r822.h
diff --git a/MAINTAINERS b/MAINTAINERS
index fca8dae..9a94c6c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4574,6 +4574,12 @@ S: Maintained
F: Documentation/rfkill.txt
F: net/rfkill/
+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 --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 13c1fb2..20803fa 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -113,6 +113,17 @@ config MTD_NAND_TS7250
config MTD_NAND_IDS
tristate
+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 --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 09891f6..dc7c0bb 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -43,5 +43,6 @@ obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
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 --git a/drivers/mtd/nand/r822.c b/drivers/mtd/nand/r822.c
new file mode 100644
index 0000000..7474cfa
--- /dev/null
+++ b/drivers/mtd/nand/r822.c
@@ -0,0 +1,1110 @@
+/*
+ * 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--)
+ *buf++ = r822_read_reg(dev, R822_DATALINE);
+}
+
+/*
+ * Read one byte from nand chip
+ */
+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\n");
+ 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 __init int r822_module_init(void)
+{
+ return pci_register_driver(&r822_pci_driver);
+}
+
+static void __exit r822_module_exit(void)
+{
+ 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 --git a/drivers/mtd/nand/r822.h b/drivers/mtd/nand/r822.h
new file mode 100644
index 0000000..c767921
--- /dev/null
+++ b/drivers/mtd/nand/r822.h
@@ -0,0 +1,154 @@
+/*
+ * 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 __iomem *mmio; /* mmio */
+ struct mtd_info *mtd; /* mtd backpointer */
+ struct nand_chip *chip; /* nand chip backpointer */
+ struct pci_dev *pci_dev; /* pci backpointer */
+
+ /* dma area */
+ dma_addr_t phys_dma_addr; /* bus address of buffer*/
+ 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 "\n", ## __VA_ARGS__)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index cca8a04..cef49f2 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1523,6 +1523,8 @@
#define PCI_DEVICE_ID_RICOH_R5C822 0x0822
#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
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 4 of 7 ==
Date: Tues, Feb 9 2010 9:10 am
From: Maxim Levitsky
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>
---
drivers/mtd/nand/Kconfig | 9 +++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/sm_common.c | 114 ++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/nand/sm_common.h | 61 ++++++++++++++++++++++
4 files changed, 185 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/nand/sm_common.c
create mode 100644 drivers/mtd/nand/sm_common.h
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 677cd53..13c1fb2 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -18,6 +18,10 @@ config MTD_NAND_VERIFY_WRITE
device thinks the write was successful, a bit could have been
flipped accidentally due to device wear or something else.
+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 --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1407bd1..09891f6 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
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 --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
new file mode 100644
index 0000000..64d8ee0
--- /dev/null
+++ b/drivers/mtd/nand/sm_common.c
@@ -0,0 +1,114 @@
+/*
+ * 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/mtd/nand.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)
+{
+ struct mtd_oob_ops ops;
+ struct sm_oob oob;
+ int ret;
+
+ ops.mode = MTD_OOB_PLACE;
+ ops.ooboffs = 0;
+ ops.ooblen = SM_OOB_SIZE;
+ ops.oobbuf = (void *)&oob;
+ ops.datbuf = NULL;
+
+ ret = mtd->read_oob(mtd, ofs, &ops);
+
+ /* We can just assume that read error means bad block... */
+ if (ret < 0 || ops.oobretlen != SM_OOB_SIZE)
+ return 0;
+
+ if (!sm_sector_valid(&oob) || !sm_block_valid(&oob))
+ return 1;
+
+ return 0;
+}
+
+/* Marks block as bad */
+static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ 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;
+
+
+ ret = mtd->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\n",
+ (int)ofs);
+ error = -EIO;
+ } else
+ mtd->ecc_stats.badblocks++;
+
+ 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 --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h
new file mode 100644
index 0000000..2e6b517
--- /dev/null
+++ b/drivers/mtd/nand/sm_common.h
@@ -0,0 +1,61 @@
+/*
+ * 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/bitops.h>
+#include <linux/mtd/mtd.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];
+} __attribute__((packed));
+
+
+/* one sector is always 512 bytes, but it can consist of two nand pages */
+#define SM_SECTOR_SIZE 512
+
+/* oob area is also 16 bytes, but might be from two pages */
+#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;
+}
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 5 of 7 ==
Date: Tues, Feb 9 2010 9:10 am
From: Maxim Levitsky
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>
---
drivers/mtd/mtd_blkdevs.c | 123 ++++++++++++++++++++----------------------
include/linux/mtd/blktrans.h | 10 ++--
2 files changed, 64 insertions(+), 69 deletions(-)
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index c82e09b..93c87af 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -26,11 +26,6 @@
static LIST_HEAD(blktrans_majors);
-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\n",
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 --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h
index 8b4aa05..a4b3928 100644
--- a/include/linux/mtd/blktrans.h
+++ b/include/linux/mtd/blktrans.h
@@ -24,11 +24,13 @@ struct mtd_blktrans_dev {
int devnum;
unsigned long size;
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);
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 6 of 7 ==
Date: Tues, Feb 9 2010 9:10 am
From: Maxim Levitsky
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>
---
drivers/mtd/ftl.c | 1 -
drivers/mtd/inftlcore.c | 1 -
drivers/mtd/mtd_blkdevs.c | 13 +++++++++++++
drivers/mtd/mtdblock.c | 1 -
drivers/mtd/mtdblock_ro.c | 1 -
drivers/mtd/nftlcore.c | 1 -
drivers/mtd/rfd_ftl.c | 1 -
drivers/mtd/ssfdc.c | 1 -
include/linux/mtd/blktrans.h | 1 +
9 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index e56d6b4..62da9eb 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -1082,7 +1082,6 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
{
del_mtd_blktrans_dev(dev);
ftl_freepart((partition_t *)dev);
- kfree(dev);
}
static struct mtd_blktrans_ops ftl_tr = {
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c
index 8aca552..015a7fe 100755
--- a/drivers/mtd/inftlcore.c
+++ b/drivers/mtd/inftlcore.c
@@ -139,7 +139,6 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
kfree(inftl->PUtable);
kfree(inftl->VUtable);
- kfree(inftl);
}
/*
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 9ac8f22..1b89e59 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -131,6 +131,9 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
if (dev->open++)
goto out;
+ 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 --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index 9f41b1a..d8322cc 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -368,7 +368,6 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
{
del_mtd_blktrans_dev(dev);
- kfree(dev);
}
static struct mtd_blktrans_ops mtdblock_tr = {
diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c
index 852165f..54ff288 100644
--- a/drivers/mtd/mtdblock_ro.c
+++ b/drivers/mtd/mtdblock_ro.c
@@ -49,7 +49,6 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
{
del_mtd_blktrans_dev(dev);
- kfree(dev);
}
static struct mtd_blktrans_ops mtdblock_tr = {
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index 1002e18..a4578bf 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -126,7 +126,6 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
del_mtd_blktrans_dev(dev);
kfree(nftl->ReplUnitTable);
kfree(nftl->EUNtable);
- kfree(nftl);
}
/*
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index d2aa9c4..63b83c0 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -817,7 +817,6 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
vfree(part->sector_map);
kfree(part->header_cache);
kfree(part->blocks);
- kfree(part);
}
static struct mtd_blktrans_ops rfd_ftl_tr = {
diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c
index 3f67e00..81c4ecd 100644
--- a/drivers/mtd/ssfdc.c
+++ b/drivers/mtd/ssfdc.c
@@ -375,7 +375,6 @@ static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
del_mtd_blktrans_dev(dev);
kfree(ssfdc->logic_block_map);
- kfree(ssfdc);
}
static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h
index 507f7b2..e276aca 100644
--- a/include/linux/mtd/blktrans.h
+++ b/include/linux/mtd/blktrans.h
@@ -24,6 +24,7 @@ struct mtd_blktrans_dev {
int devnum;
unsigned long size;
int readonly;
+ int deleted;
int open;
struct gendisk *disk;
struct task_struct *thread;
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 7 of 7 ==
Date: Tues, Feb 9 2010 9:10 am
From: Maxim Levitsky
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>
---
drivers/mtd/mtd_blkdevs.c | 14 +++++++++++++-
1 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 082969c..9992b2d 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -115,7 +115,13 @@ static int mtd_blktrans_thread(void *arg)
static void mtd_blktrans_request(struct request_queue *rq)
{
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)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->thread);
}
@@ -357,6 +363,8 @@ error1:
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);
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
==============================================================================
TOPIC: Oops in kswapd (2.6.31.5)
http://groups.google.com/group/linux.kernel/t/bc817685b3c95404?hl=en
==============================================================================
== 1 of 1 ==
Date: Tues, Feb 9 2010 9:20 am
From: Jesper Krogh
Hi list.
One of my servers "hung" itself today, when it got up I had this message
in the logs:
Feb 9 09:40:52 ko kernel: [1341076.770228] Pid: 347, comm: kswapd3 Not
tainted 2.6.31.5 #3 Sun Fire X4600 M2
Feb 9 09:40:52 ko kernel: [1341076.770228] RIP:
0010:[<ffffffff8109fcb2>] [<ffffffff8109fcb2>] find_get_pages+0x52/0xc0
Feb 9 09:40:52 ko kernel: [1341076.770228] RSP: 0018:ffff8800debafc00
EFLAGS: 00000286
Feb 9 09:40:52 ko kernel: [1341076.770228] RAX: ffff8800109fb6c8 RBX:
0000000000000000 RCX: 0000000000000000
Feb 9 09:40:52 ko kernel: [1341076.770228] RDX: 0000000000000040 RSI:
ffff8800debafc80 RDI: ffffea00016e8550
Feb 9 09:40:52 ko kernel: [1341076.770228] RBP: ffffffff8100c76e R08:
ffff8800debafbc0 R09: 0000000000000002
Feb 9 09:40:52 ko kernel: [1341076.770228] R10: 0000000000000040 R11:
0000000000000040 R12: 0000000000000007
Feb 9 09:40:52 ko kernel: [1341076.770228] R13: ffff8800debafcb8 R14:
ffff880000028740 R15: ffffea0001b814d8
Feb 9 09:40:52 ko kernel: [1341076.770228] FS: 00000000450fa950(0000)
GS:ffffc90000c00000(0000) knlGS:00000000f73cb8c0
Feb 9 09:40:52 ko kernel: [1341076.770228] CS: 0010 DS: 0018 ES: 0018
CR0: 000000008005003b
Feb 9 09:40:52 ko kernel: [1341076.770228] CR2: 00007fb194a20f10 CR3:
0000000001001000 CR4: 00000000000006e0
Feb 9 09:40:52 ko kernel: [1341076.770228] DR0: 0000000000000000 DR1:
0000000000000000 DR2: 0000000000000000
Feb 9 09:40:52 ko kernel: [1341076.770228] DR3: 0000000000000000 DR6:
00000000ffff0ff0 DR7: 0000000000000400
Feb 9 09:40:52 ko kernel: [1341076.770228] Call Trace:
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff810a7ec7>] ?
pagevec_lookup+0x17/0x20
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff810a8206>] ?
invalidate_mapping_pages+0x56/0x140
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff810e9819>] ?
shrink_icache_memory+0x299/0x2c0
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff810aa8a4>] ?
shrink_slab+0x124/0x180
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff810ab569>] ?
kswapd+0x3d9/0x600
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff810a8a00>] ?
isolate_pages_global+0x0/0x260
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff8105ac60>] ?
autoremove_wake_function+0x0/0x30
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff810ab190>] ?
kswapd+0x0/0x600
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff8105aaf6>] ?
kthread+0xa6/0xb0
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff8100cc9a>] ?
child_rip+0xa/0x20
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff8105aa50>] ?
kthread+0x0/0xb0
Feb 9 09:40:52 ko kernel: [1341076.770228] [<ffffffff8100cc90>] ?
child_rip+0x0/0x20
I searched the "git log" of 2.6.31.5 up til 2.6.31.latest and didnt find
anything mentioned that looked like that, but please point me to the fix
if it has been posted on the list.
--
Jesper
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
==============================================================================
TOPIC: MTD: create lockless versions of {get,put}_mtd_device This will be used
to resolve deadlock in block translation layer.
http://groups.google.com/group/linux.kernel/t/218cbc44b1591b85?hl=en
==============================================================================
== 1 of 3 ==
Date: Tues, Feb 9 2010 9:20 am
From: Peter Zijlstra
On Tue, 2010-02-09 at 18:57 +0200, Maxim Levitsky wrote:
> 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>
> ---
> drivers/mtd/mtdcore.c | 60 ++++++++++++++++++++++++++++++----------------
> include/linux/mtd/mtd.h | 3 +-
> 2 files changed, 41 insertions(+), 22 deletions(-)
> +int __get_mtd_device(struct mtd_info *mtd)
> +{
> + int err;
> +
> + if (!try_module_get(mtd->owner))
> + return -ENODEV;
> +
> + if (mtd->get_device) {
> +
> + err = mtd->get_device(mtd);
> +
> + if (err) {
> + module_put(mtd->owner);
> + return err;
> + }
> + }
> + mtd->usecount++;
> + return 0;
> }
> +void __put_mtd_device(struct mtd_info *mtd)
> +{
> + --mtd->usecount;
> + BUG_ON(mtd->usecount < 0);
> +
> if (mtd->put_device)
> mtd->put_device(mtd);
>
> module_put(mtd->owner);
> }
That's racy, use kref.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 2 of 3 ==
Date: Tues, Feb 9 2010 9:30 am
From: Maxim Levitsky
On Tue, 2010-02-09 at 18:14 +0100, Peter Zijlstra wrote:
> On Tue, 2010-02-09 at 18:57 +0200, Maxim Levitsky wrote:
> > 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>
> > ---
> > drivers/mtd/mtdcore.c | 60 ++++++++++++++++++++++++++++++----------------
> > include/linux/mtd/mtd.h | 3 +-
> > 2 files changed, 41 insertions(+), 22 deletions(-)
>
> > +int __get_mtd_device(struct mtd_info *mtd)
> > +{
> > + int err;
> > +
> > + if (!try_module_get(mtd->owner))
> > + return -ENODEV;
> > +
> > + if (mtd->get_device) {
> > +
> > + err = mtd->get_device(mtd);
> > +
> > + if (err) {
> > + module_put(mtd->owner);
> > + return err;
> > + }
> > + }
> > + mtd->usecount++;
> > + return 0;
> > }
>
> > +void __put_mtd_device(struct mtd_info *mtd)
> > +{
> > + --mtd->usecount;
> > + BUG_ON(mtd->usecount < 0);
> > +
> > if (mtd->put_device)
> > mtd->put_device(mtd);
> >
> > module_put(mtd->owner);
> > }
>
> That's racy, use kref.
>
Couldn't agree with you more.
However, these functions aren't intended for general use, and probably
will be used by mtd translation layer only. I do have a lock that
protects concurrent use of these functions.
Thus, I better add a comment about this?
Best regards,
Maxim Levitsky
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
== 3 of 3 ==
Date: Tues, Feb 9 2010 9:50 am
From: Maxim Levitsky
On Tue, 2010-02-09 at 19:23 +0200, Maxim Levitsky wrote:
> On Tue, 2010-02-09 at 18:14 +0100, Peter Zijlstra wrote:
> > On Tue, 2010-02-09 at 18:57 +0200, Maxim Levitsky wrote:
> > > 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>
> > > ---
> > > drivers/mtd/mtdcore.c | 60 ++++++++++++++++++++++++++++++----------------
> > > include/linux/mtd/mtd.h | 3 +-
> > > 2 files changed, 41 insertions(+), 22 deletions(-)
> >
> > > +int __get_mtd_device(struct mtd_info *mtd)
> > > +{
> > > + int err;
> > > +
> > > + if (!try_module_get(mtd->owner))
> > > + return -ENODEV;
> > > +
> > > + if (mtd->get_device) {
> > > +
> > > + err = mtd->get_device(mtd);
> > > +
> > > + if (err) {
> > > + module_put(mtd->owner);
> > > + return err;
> > > + }
> > > + }
> > > + mtd->usecount++;
> > > + return 0;
> > > }
> >
> > > +void __put_mtd_device(struct mtd_info *mtd)
> > > +{
> > > + --mtd->usecount;
> > > + BUG_ON(mtd->usecount < 0);
> > > +
> > > if (mtd->put_device)
> > > mtd->put_device(mtd);
> > >
> > > module_put(mtd->owner);
> > > }
> >
> > That's racy, use kref.
> >
> Couldn't agree with you more.
>
> However, these functions aren't intended for general use, and probably
> will be used by mtd translation layer only. I do have a lock that
> protects concurrent use of these functions.
>
> Thus, I better add a comment about this?
However on second thought, there is still a race if two FTLs access same
mtd device.
While this might seem impossible, and I say it is quite dangerous. I can
imagine using both some FTL and mtdblock for testing.
Just using kref (nothing against it) won't help here.
The mtd->get_device/put_device aren't expecting to be called
concurrently ether...
I can add per mtd lock, but it is a bit ugly...
What do you think?
Best regards,
Maxim Levitsky
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
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/
==============================================================================
TOPIC: gpiolib: Introduce chip addition/removal notifier
http://groups.google.com/group/linux.kernel/t/a8c118be63f283f3?hl=en
==============================================================================
== 1 of 1 ==
Date: Tues, Feb 9 2010 9:20 am
From: Grant Likely
On Fri, Feb 5, 2010 at 1:32 PM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> Some platforms (e.g. OpenFirmware) want to know when a particular chip
> added or removed, so that the platforms could add their specifics for
> non-platform devices, like I2C or SPI GPIO chips.
>
> This patch implements the notifier for chip addition and removal events.
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> ---
> drivers/gpio/gpiolib.c | 14 ++++++++++++++
> include/asm-generic/gpio.h | 8 ++++++++
> 2 files changed, 22 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index 350842a..375c03a 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -9,6 +9,7 @@
> #include <linux/seq_file.h>
> #include <linux/gpio.h>
> #include <linux/idr.h>
> +#include <linux/notifier.h>
>
>
> /* Optional implementation infrastructure for GPIO interfaces.
> @@ -1029,6 +1030,9 @@ static inline void gpiochip_unexport(struct gpio_chip *chip)
>
>
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home