public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] Results of my work on memorystick subsystem
@ 2010-10-16 23:12 Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 1/6] memstick: core: add series of common helpers Maxim Levitsky
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-16 23:12 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML

Hi,

Here is a result of lot of work I did improving the memorystick
subsystem and its drivers.

patch #1 add common code to memorystick core that makes card drivers easier
to understand

patch #2 just add a driver for my card reader

patch #3 reworks the Jmicron driver.
I can write a novel about the problems I had to go through with this device
(mostly hardware bugs I belive).
So I refactored the driver and added a lot of debug output future users.
Currently both MS and MSPro work fine.

patch #4 adds support for lagacy memorysticks.
Everything just works, not a single corruption this far.

patch #5, is what I did recently.
I rewrote large chunks on mspro_blk.c to use the common code I added in patch #1.
I also added lot of debug code, comments.

patch #6 adds more cleanups.

Now register window changes are completely hidden and automatic.
Functions that state machines call are explictly marked as so,
and that assumption is tested.
the card->current_mrq isn't passed as a pointer to functions
as this just complicates the code.

Code is tested with 2 mspro cards and one MS card.

Thanks again to Alex for his work.

***Changes from V1 ***

* Fixed brown paper bug in memstick core.
memset had its 2 and 3 arguments swapped.

* Cleaned a lot the PIO code in my r592 driver.
Now it finally looks sane.
Also tested it throughfully.

* Endiannes fixes in Jmicron driver and r592

Best regards,
	Maxim Levitsky

***Appendix***

Jmicron hardware bugs (the novel):

#1: FIFO write ready bit in INT status register is stuck to 1.
   It is stuck forever as soon as fifo
   is used for writing even once.
   Therefore if interrupt is shared (and here it is), its easy
   to 'service' the device while it doesn't need any service


#2: Its not possible to stuff the FIFO before TPC transfer.
   One really have to wait for write ready interrupt, even though the write ready status is stuck.


#3: TPCs with non 4 aligned length woes:
   Facts:

   * non 4 aligned DMA write hangs the system hard, maybe on bus level.

   * PIO read succedes but controller truncates the data stored in the FIFO to closest 4 byte boundary.
   That is if you read 26 bytes, it will save 24 bytes in the FIFO

   * PIO non-aligned write work, expect that they sometimes hose the DMA.... (regardless of the alignment)

   * TPC_P0, TPC_P1 not aligned transfters work just fine despite a statement in the datasheet
   that they are undefined.... (only mention of this problem)


#4: As soon as write PIO is used, then later write DMA fails.
   Facts:

   * This is triggered only by PIO write of registers
   (only happens in ms_block.c when it writes param + oob. Thats why mspro_blk isn't affected)
   Doing short DMA writes is a nice workaround.

   * Doing PIO writes in h_msb_read_page don't cause the problem.
     Therefore the bug causing sequence should be similiar to h_msb_write_block:

       1. PIO write of param + extra (10 bytes) or even padded to 12 or 16 bytes
       2. inline write (TPC_P0) of MS_CMD_BLOCK_WRITE + wait for int
       3. read of INT register via STATUS
       3. DMA write of MS_TPC_WRITE_LONG_DATA
       4. DMA write of MS_TPC_WRITE_LONG_DATA
           ---------timeout-----------
 
   * In fact first DMA write succedes, but next one fails, and so do all following writes

   * The problem persits till reboot, and shows up even if PIO isn't used again.
   Other way to "fix" it, is to put device in D3 and back to D0

   * Serial/parallel mode don't affect anything.

   After bug reproduction:

    * DMA writes stop working completely, therefore mspro_blk looses writes as well

    * PIO still works just fine. Its possible just to load the driver without dma support, and it works correctly.

    * DMA reads work just fine.

#5: Auto_Get_INT feature just doesn't work.
   Datasheet says that intreg is placed to TPC_P0, but that doesn't happen....
   card status register is 0.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/6] memstick: core: add series of common helpers.
  2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
@ 2010-10-16 23:12 ` Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 2/6] memstick: Add driver for Ricoh R5C592 Card reader Maxim Levitsky
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-16 23:12 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML, Maxim Levitsky

This code is currently unused, but it will be used
in following patches.

Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 drivers/memstick/core/memstick.c |  240 ++++++++++++++++++++++++++++++++++++++
 include/linux/memstick.h         |   82 +++++++++++--
 2 files changed, 310 insertions(+), 12 deletions(-)

diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c
index c00fe82..4c457ae 100644
--- a/drivers/memstick/core/memstick.c
+++ b/drivers/memstick/core/memstick.c
@@ -362,6 +362,13 @@ static int h_memstick_set_rw_addr(struct memstick_dev *card,
 	}
 }
 
+
+static int h_memstick_default_bad(struct memstick_dev *card,
+				     struct memstick_request **mrq)
+{
+	return -ENXIO;
+}
+
 /**
  * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to
  *                        complete
@@ -377,6 +384,239 @@ int memstick_set_rw_addr(struct memstick_dev *card)
 }
 EXPORT_SYMBOL(memstick_set_rw_addr);
 
+
+/**
+ * memstick_allocate_request - create new request for use in request handler
+ * @card - card to use
+ * @mrq - request to initialize
+ */
+void memstick_allocate_request(struct memstick_dev *card,
+					struct memstick_request **mrq)
+{
+	if (*mrq == NULL) {
+		*mrq = &card->current_mrq;
+		(*mrq)->error = 0;
+		(*mrq)->need_card_int = 0;
+		card->int_polling = false;
+		card->state = 0;
+	}
+}
+EXPORT_SYMBOL(memstick_allocate_request);
+
+/**
+ * memstick_complete_req - signal that request is completed
+ * @card - card to use
+ * @mrq - request to use
+ * @error - result of the request
+ *
+ * Card drivers can use that function to signal end of request
+ */
+int memstick_complete_request(struct memstick_dev *card,
+			struct memstick_request *req, int error)
+{
+	if (error)
+		req->error = error;
+
+	card->state = -1;
+	card->int_polling = false;
+	card->next_request = h_memstick_default_bad;
+
+	/* Invalidate reg window on errors */
+	if (req->error)
+		memstick_invalidate_reg_window(card);
+
+	complete(&card->mrq_complete);
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(memstick_complete_request);
+
+/**
+ * memstick_read_int_reg - read INT status from the card
+ * if last request already contains the int flags, will return 0
+ * returns 1 if new request was initialized
+ * Will artifictially return MEMSTICK_INT_CMDNAK if this function was
+ * called more that once in 300 msecs without memstick_finish_int_request
+ * in between
+ * @card - card to use
+ * @req - request to use
+ */
+
+int memstick_read_int_reg(struct memstick_dev *card,
+				struct memstick_request *req, long timeout)
+{
+
+	if (!card->int_polling) {
+		card->int_timeout = jiffies +
+			msecs_to_jiffies(timeout == -1 ? 300 : timeout);
+		card->int_polling = true;
+	} else if (time_after(jiffies, card->int_timeout)) {
+		req->data[0] = MEMSTICK_INT_CMDNAK;
+		return 0;
+	}
+
+	if (((card->caps | card->host->caps) & MEMSTICK_CAP_AUTO_GET_INT) &&
+							req->need_card_int) {
+		BUG_ON(req->error);
+		req->data[0] = req->int_reg;
+		req->need_card_int = 0;
+		return 0;
+	} else {
+		memstick_init_req(req, MS_TPC_GET_INT, NULL, 1);
+		return 1;
+	}
+}
+EXPORT_SYMBOL(memstick_read_int_reg);
+
+
+/**
+ * memstick_read_int_reg_cleanup - cleanup after series of calls to
+ * memstick_read_int_reg. Used to cancel timeout.
+ * Use this if you use memstick_read_int_reg
+ * @card - card to use
+ */
+void memstick_read_int_reg_cleanup(struct memstick_dev *card)
+{
+	card->int_polling = false;
+}
+EXPORT_SYMBOL(memstick_read_int_reg_cleanup);
+
+
+/**
+ * memstick_read_regs - read the ms registers
+ * If there is need to change the R/W window,
+ * it will create the MS_TPC_SET_RW_REG_ADRS request and return 0,
+ * otherwise it will create a request for register read and return 1
+ * @card - card to use
+ * @offset - offset of first register to read
+ * @len - number of bytes to read
+ * @req - request to use
+ */
+
+int memstick_read_regs(struct memstick_dev *card, int offset, int len,
+	struct memstick_request *req)
+{
+	if (card->reg_addr.r_offset != offset ||
+					card->reg_addr.r_length != len) {
+		card->reg_addr.r_offset = offset;
+		card->reg_addr.r_length = len;
+
+		/* Set dummy window after reg invalidation to prevent
+			possible rejection of 0,0 window by the card */
+		if (!card->reg_addr.w_length) {
+			card->reg_addr.w_offset =
+					offsetof(struct ms_register, id);
+			card->reg_addr.w_length = sizeof(struct ms_id_register);
+		}
+
+		memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS, &card->reg_addr,
+			sizeof(card->reg_addr));
+		return 0;
+	}
+
+	memstick_init_req(req, MS_TPC_READ_REG, NULL, len);
+	return 1;
+}
+EXPORT_SYMBOL(memstick_read_regs);
+
+/**
+ * memstick_write_regs - write the ms registers.
+ * If there is need to change the R/W window,
+ * it will create the MS_TPC_SET_RW_REG_ADRS request and return 0,
+ * otherwise it will create a request for register write and return 1
+ * @card - card to use
+ * @offset - offset of first register to read
+ * @len - number of bytes to read
+ * @buf - the register data to write
+ * @req - request to use
+ */
+int memstick_write_regs(struct memstick_dev *card, int offset, int len,
+	char *buf, struct memstick_request *req)
+{
+	if (card->reg_addr.w_offset != offset ||
+					card->reg_addr.w_length != len) {
+		card->reg_addr.w_offset = offset;
+		card->reg_addr.w_length = len;
+
+		/* Set dummy window after reg invalidation to prevent
+			possible rejection of 0,0 window by the card */
+		if (!card->reg_addr.r_length) {
+			card->reg_addr.r_offset =
+					offsetof(struct ms_register, id);
+
+			card->reg_addr.r_length = sizeof(struct ms_id_register);
+		}
+
+		memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS, &card->reg_addr,
+			sizeof(card->reg_addr));
+		return 0;
+	}
+
+	memstick_init_req(req, MS_TPC_WRITE_REG, buf, len);
+	return 1;
+}
+EXPORT_SYMBOL(memstick_write_regs);
+
+
+/**
+ * memstick_run_state_machine - runs state machine untill it calls
+ * memstick_complete_request.
+ * Usefull for blocking IO in the card drivers.
+ * @card - card to use
+ * state_func - the state machine
+ */
+int memstick_run_state_machine(struct memstick_dev *card,
+	int   (*state_func)(struct memstick_dev *card,
+					struct memstick_request **mrq))
+{
+	card->next_request = state_func;
+	memstick_new_req(card->host);
+	wait_for_completion(&card->mrq_complete);
+	return card->current_mrq.error;
+}
+EXPORT_SYMBOL(memstick_run_state_machine);
+
+
+static const char *tpc_names[] = {
+	"MS_TPC_READ_MG_STATUS",
+	"MS_TPC_READ_LONG_DATA",
+	"MS_TPC_READ_SHORT_DATA",
+	"MS_TPC_READ_REG",
+	"MS_TPC_READ_QUAD_DATA",
+	"INVALID",
+	"MS_TPC_GET_INT",
+	"MS_TPC_SET_RW_REG_ADRS",
+	"MS_TPC_EX_SET_CMD",
+	"MS_TPC_WRITE_QUAD_DATA",
+	"MS_TPC_WRITE_REG",
+	"MS_TPC_WRITE_SHORT_DATA",
+	"MS_TPC_WRITE_LONG_DATA",
+	"MS_TPC_SET_CMD",
+};
+
+/**
+ * memstick_debug_get_tpc_name - debug helper that returns string for
+ * a TPC number
+ */
+const char *memstick_debug_get_tpc_name(int tpc)
+{
+	return tpc_names[tpc-1];
+}
+EXPORT_SYMBOL(memstick_debug_get_tpc_name);
+
+
+/**
+ * memstick_invalidate_reg_window - invalidate the card register
+ * read/write window (start, len)
+ * Use when not certain if card still remembers it
+ */
+void memstick_invalidate_reg_window(struct memstick_dev *card)
+{
+	memset(&card->reg_addr, 0, sizeof(card->reg_addr));
+}
+EXPORT_SYMBOL(memstick_invalidate_reg_window);
+
+
+
 static struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
 {
 	struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev),
diff --git a/include/linux/memstick.h b/include/linux/memstick.h
index 690c35a..bb61bfc 100644
--- a/include/linux/memstick.h
+++ b/include/linux/memstick.h
@@ -45,14 +45,14 @@ struct ms_status_register {
 #define MEMSTICK_STATUS1_DTER 0x20
 #define MEMSTICK_STATUS1_FB1  0x40
 #define MEMSTICK_STATUS1_MB   0x80
-} __attribute__((packed));
+} __packed;
 
 struct ms_id_register {
 	unsigned char type;
 	unsigned char if_mode;
 	unsigned char category;
 	unsigned char class;
-} __attribute__((packed));
+} __packed;
 
 struct ms_param_register {
 	unsigned char system;
@@ -68,7 +68,7 @@ struct ms_param_register {
 #define MEMSTICK_CP_OVERWRITE 0x80
 
 	unsigned char page_address;
-} __attribute__((packed));
+} __packed;
 
 struct ms_extra_data_register {
 	unsigned char  overwrite_flag;
@@ -84,7 +84,7 @@ struct ms_extra_data_register {
 #define MEMSTICK_MANAGEMENT_SCMS0  0x20
 
 	unsigned short logical_address;
-} __attribute__((packed));
+} __packed;
 
 struct ms_register {
 	struct ms_status_register     status;
@@ -92,7 +92,7 @@ struct ms_register {
 	unsigned char                 reserved[8];
 	struct ms_param_register      param;
 	struct ms_extra_data_register extra_data;
-} __attribute__((packed));
+} __packed;
 
 struct mspro_param_register {
 	unsigned char  system;
@@ -103,7 +103,7 @@ struct mspro_param_register {
 	__be16 data_count;
 	__be32 data_address;
 	unsigned char  tpc_param;
-} __attribute__((packed));
+} __packed;
 
 struct mspro_io_info_register {
 	unsigned char version;
@@ -111,20 +111,28 @@ struct mspro_io_info_register {
 	unsigned char current_req;
 	unsigned char card_opt_info;
 	unsigned char rdy_wait_time;
-} __attribute__((packed));
+} __packed;
 
 struct mspro_io_func_register {
 	unsigned char func_enable;
 	unsigned char func_select;
 	unsigned char func_intmask;
 	unsigned char transfer_mode;
-} __attribute__((packed));
+} __packed;
 
 struct mspro_io_cmd_register {
 	unsigned short tpc_param;
 	unsigned short data_count;
 	unsigned int   data_address;
-} __attribute__((packed));
+} __packed;
+
+
+struct mspro_cmdex_argument {
+	unsigned char  command;
+	__be16 data_count;
+	__be32 data_address;
+} __packed;
+
 
 struct mspro_register {
 	struct ms_status_register     status;
@@ -138,14 +146,14 @@ struct mspro_register {
 	struct mspro_io_cmd_register  io_cmd;
 	unsigned char                 io_int;
 	unsigned char                 io_int_func;
-} __attribute__((packed));
+} __packed;
 
 struct ms_register_addr {
 	unsigned char r_offset;
 	unsigned char r_length;
 	unsigned char w_offset;
 	unsigned char w_length;
-} __attribute__((packed));
+} __packed;
 
 enum memstick_tpc {
 	MS_TPC_READ_MG_STATUS   = 0x01,
@@ -236,6 +244,24 @@ struct memstick_device_id {
 #define MEMSTICK_CLASS_WP             0x03
 };
 
+/* IO request that host driver gets from memtick core
+ *
+ * Note about the 'need_card_int' flag:
+
+ * In serial mode that flag _hints_ the host driver to wait till card
+ * raises the INT signal, so that core could spare sending redundant
+ * MS_TPC_GET_INT requests.
+ *
+ * In _parallel_ mode, that flag must be honored,
+ * and besides waiting, the host driver must read the INT register
+ * (via data lines)
+ *
+ * In addition to that if hardware is 'smart', it could be able to read
+ * the INT register even in parallel mode by sending MS_TPC_GET_INT
+ * by itself. This capablility is indicated by host via
+ * MEMSTICK_CAP_AUTO_GET_INT.
+ * Then serial mode behavier must be the same as parallel
+ */
 struct memstick_request {
 	unsigned char tpc;
 	unsigned char data_dir:1,
@@ -247,7 +273,7 @@ struct memstick_request {
 		struct scatterlist sg;
 		struct {
 			unsigned char data_len;
-			unsigned char data[15];
+			unsigned char data[32];
 		};
 	};
 };
@@ -270,6 +296,12 @@ struct memstick_dev {
 	void                     (*start)(struct memstick_dev *card);
 
 	struct device            dev;
+
+	/* Private area for request processing */
+	bool			 int_polling;
+	unsigned long		 int_timeout;
+	int			 state;
+	int			 caps;
 };
 
 struct memstick_host {
@@ -329,6 +361,32 @@ void memstick_new_req(struct memstick_host *host);
 
 int memstick_set_rw_addr(struct memstick_dev *card);
 
+/* Helpers for high level drivers */
+void memstick_allocate_request(struct memstick_dev *card,
+					struct memstick_request **mrq);
+
+int memstick_complete_request(struct memstick_dev *card,
+			struct memstick_request *req, int error);
+
+int memstick_read_int_reg(struct memstick_dev *card,
+			struct memstick_request *req, long timeout);
+
+void memstick_read_int_reg_cleanup(struct memstick_dev *card);
+
+int memstick_read_regs(struct memstick_dev *card, int offset, int len,
+	struct memstick_request *req);
+
+int memstick_write_regs(struct memstick_dev *card, int offset, int len,
+	char *buf, struct memstick_request *req);
+
+void memstick_invalidate_reg_window(struct memstick_dev *card);
+
+int memstick_run_state_machine(struct memstick_dev *card,
+	int   (*next_request)(struct memstick_dev *card,
+				struct memstick_request **mrq));
+
+const char *memstick_debug_get_tpc_name(int tpc);
+
 static inline void *memstick_priv(struct memstick_host *host)
 {
 	return (void *)host->private;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/6] memstick: Add driver for Ricoh R5C592 Card reader.
  2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 1/6] memstick: core: add series of common helpers Maxim Levitsky
@ 2010-10-16 23:12 ` Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 3/6] memstick: jmb38x: Driver rework Maxim Levitsky
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-16 23:12 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML, Maxim Levitsky

Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 MAINTAINERS                    |    5 +
 drivers/memstick/host/Kconfig  |   12 +
 drivers/memstick/host/Makefile |    1 +
 drivers/memstick/host/r592.c   |  871 ++++++++++++++++++++++++++++++++++++++++
 drivers/memstick/host/r592.h   |  175 ++++++++
 5 files changed, 1064 insertions(+), 0 deletions(-)
 create mode 100644 drivers/memstick/host/r592.c
 create mode 100644 drivers/memstick/host/r592.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 7679bf3..9c7c41c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4919,6 +4919,11 @@ S:	Maintained
 F:	drivers/mtd/nand/r852.c
 F:	drivers/mtd/nand/r852.h
 
+RICOH R5C592 MEMORYSTICK DRIVER
+M:	Maxim Levitsky <maximlevitsky@gmail.com>
+S:	Maintained
+F:	drivers/memstick/host/r592.*
+
 RISCOM8 DRIVER
 S:	Orphan
 F:	Documentation/serial/riscom8.txt
diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig
index 4ce5c8d..cc0997a 100644
--- a/drivers/memstick/host/Kconfig
+++ b/drivers/memstick/host/Kconfig
@@ -30,3 +30,15 @@ config MEMSTICK_JMICRON_38X
 
           To compile this driver as a module, choose M here: the
 	  module will be called jmb38x_ms.
+
+config MEMSTICK_R592
+	tristate "Ricoh R5C592 MemoryStick interface support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && PCI
+
+	help
+	  Say Y here if you want to be able to access MemoryStick cards with
+	  the Ricoh R5C592 MemoryStick card reader (which is part of 5 in one
+		multifunction reader)
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called r592.
diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile
index 12530e4..ad63c16 100644
--- a/drivers/memstick/host/Makefile
+++ b/drivers/memstick/host/Makefile
@@ -8,3 +8,4 @@ endif
 
 obj-$(CONFIG_MEMSTICK_TIFM_MS)		+= tifm_ms.o
 obj-$(CONFIG_MEMSTICK_JMICRON_38X)	+= jmb38x_ms.o
+obj-$(CONFIG_MEMSTICK_R592)		+= r592.o
diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c
new file mode 100644
index 0000000..7eea769
--- /dev/null
+++ b/drivers/memstick/host/r592.c
@@ -0,0 +1,871 @@
+/*
+ * Copyright (C) 2010 - Maxim Levitsky
+ * driver for Ricoh memstick 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/freezer.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/highmem.h>
+#include <asm/byteorder.h>
+#include <linux/swab.h>
+#include "r592.h"
+
+static int enable_dma = 1;
+static int debug;
+
+
+/* Read a register*/
+static inline u32 r592_read_reg(struct r592_device *dev, int address)
+{
+	u32 value = readl(dev->mmio + address);
+	dbg_reg("reg #%02d == 0x%08x", address, value);
+	return value;
+}
+
+/* Write a register */
+static inline void r592_write_reg(struct r592_device *dev,
+							int address, u32 value)
+{
+	dbg_reg("reg #%02d <- 0x%08x", address, value);
+	writel(value, dev->mmio + address);
+}
+
+/* Reads a big endian DWORD register */
+static inline u32 r592_read_reg_raw_be(struct r592_device *dev, int address)
+{
+	u32 value = __raw_readl(dev->mmio + address);
+	dbg_reg("reg #%02d == 0x%08x", address, value);
+	return be32_to_cpu(value);
+}
+
+/* Writes a big endian DWORD register */
+static inline void r592_write_reg_raw_be(struct r592_device *dev,
+							int address, u32 value)
+{
+	dbg_reg("reg #%02d <- 0x%08x", address, value);
+	__raw_writel(cpu_to_be32(value), dev->mmio + address);
+}
+
+/* Set specific bits in a register (little endian) */
+static inline void r592_set_reg_mask(struct r592_device *dev,
+							int address, u32 mask)
+{
+	u32 reg = readl(dev->mmio + address);
+	dbg_reg("reg #%02d |= 0x%08x (old =0x%08x)", address, mask, reg);
+	writel(reg | mask , dev->mmio + address);
+}
+
+/* Clear specific bits in a register (little endian) */
+static inline void r592_clear_reg_mask(struct r592_device *dev,
+						int address, u32 mask)
+{
+	u32 reg = readl(dev->mmio + address);
+	dbg_reg("reg #%02d &= 0x%08x (old = 0x%08x, mask = 0x%08x)",
+						address, ~mask, reg, mask);
+	writel(reg & ~mask, dev->mmio + address);
+}
+
+
+/* Wait for status bits while checking for errors */
+static int r592_wait_status(struct r592_device *dev, u32 mask, u32 wanted_mask)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+	u32 reg = r592_read_reg(dev, R592_STATUS);
+
+	if ((reg & mask) == wanted_mask)
+		return 0;
+
+	while (time_before(jiffies, timeout)) {
+
+		reg = r592_read_reg(dev, R592_STATUS);
+
+		if ((reg & mask) == wanted_mask)
+			return 0;
+
+		if (reg & (R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR))
+			return -EIO;
+
+		cpu_relax();
+	}
+	return -ETIME;
+}
+
+
+/* Enable/disable device */
+static int r592_enable_device(struct r592_device *dev, bool enable)
+{
+	dbg("%sabling the device", enable ? "en" : "dis");
+
+	if (enable) {
+
+		/* Power up the card */
+		r592_write_reg(dev, R592_POWER, R592_POWER_0 | R592_POWER_1);
+
+		/* Perform a reset */
+		r592_set_reg_mask(dev, R592_IO, R592_IO_RESET);
+
+		msleep(100);
+	} else
+		/* Power down the card */
+		r592_write_reg(dev, R592_POWER, 0);
+
+	return 0;
+}
+
+/* Set serial/parallel mode */
+static int r592_set_mode(struct r592_device *dev, bool parallel_mode)
+{
+	if (!parallel_mode) {
+		dbg("switching to serial mode");
+
+		/* Set serial mode */
+		r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_SERIAL);
+
+		r592_clear_reg_mask(dev, R592_POWER, R592_POWER_20);
+
+	} else {
+		dbg("switching to parallel mode");
+
+		/* This setting should be set _before_ switch TPC */
+		r592_set_reg_mask(dev, R592_POWER, R592_POWER_20);
+
+		r592_clear_reg_mask(dev, R592_IO,
+			R592_IO_SERIAL1 | R592_IO_SERIAL2);
+
+		/* Set the parallel mode now */
+		r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_PARALLEL);
+	}
+
+	dev->parallel_mode = parallel_mode;
+	return 0;
+}
+
+/* Perform a controller reset without powering down the card */
+static void r592_host_reset(struct r592_device *dev)
+{
+	r592_set_reg_mask(dev, R592_IO, R592_IO_RESET);
+	msleep(100);
+	r592_set_mode(dev, dev->parallel_mode);
+}
+
+/* Disable all hardware interrupts */
+static void r592_clear_interrupts(struct r592_device *dev)
+{
+	/* Disable & ACK all interrupts */
+	r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_ACK_MASK);
+	r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_EN_MASK);
+}
+
+/* Tests if there is an CRC error */
+static int r592_test_io_error(struct r592_device *dev)
+{
+	if (!(r592_read_reg(dev, R592_STATUS) &
+		(R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR)))
+		return 0;
+
+	return -EIO;
+}
+
+/* Ensure that FIFO is ready for use */
+static int r592_test_fifo_empty(struct r592_device *dev)
+{
+	if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY)
+		return 0;
+
+	dbg("FIFO not ready, trying to reset the device");
+	r592_host_reset(dev);
+
+	if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY)
+		return 0;
+
+	message("FIFO still not ready, giving up");
+	return -EIO;
+}
+
+/* Activates the DMA transfer from to FIFO */
+static void r592_start_dma(struct r592_device *dev, bool is_write)
+{
+	unsigned long flags;
+	u32 reg;
+	spin_lock_irqsave(&dev->irq_lock, flags);
+
+	/* Ack interrupts (just in case) + enable them */
+	r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK);
+	r592_set_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK);
+
+	/* Set DMA address */
+	r592_write_reg(dev, R592_FIFO_DMA, sg_dma_address(&dev->req->sg));
+
+	/* Enable the DMA */
+	reg = r592_read_reg(dev, R592_FIFO_DMA_SETTINGS);
+	reg |= R592_FIFO_DMA_SETTINGS_EN;
+
+	if (!is_write)
+		reg |= R592_FIFO_DMA_SETTINGS_DIR;
+	else
+		reg &= ~R592_FIFO_DMA_SETTINGS_DIR;
+	r592_write_reg(dev, R592_FIFO_DMA_SETTINGS, reg);
+
+	spin_unlock_irqrestore(&dev->irq_lock, flags);
+}
+
+/* Cleanups DMA related settings */
+static void r592_stop_dma(struct r592_device *dev, int error)
+{
+	r592_clear_reg_mask(dev, R592_FIFO_DMA_SETTINGS,
+		R592_FIFO_DMA_SETTINGS_EN);
+
+	/* This is only a precation */
+	r592_write_reg(dev, R592_FIFO_DMA,
+			dev->dummy_dma_page_physical_address);
+
+	r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK);
+	r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK);
+	dev->dma_error = error;
+}
+
+/* Test if hardware supports DMA */
+static void r592_check_dma(struct r592_device *dev)
+{
+	dev->dma_capable = enable_dma &&
+		(r592_read_reg(dev, R592_FIFO_DMA_SETTINGS) &
+			R592_FIFO_DMA_SETTINGS_CAP);
+}
+
+/* Transfers fifo contents in/out using DMA */
+static int r592_transfer_fifo_dma(struct r592_device *dev)
+{
+	int len, sg_count;
+	bool is_write;
+
+	if (!dev->dma_capable || !dev->req->long_data)
+		return -EINVAL;
+
+	len = dev->req->sg.length;
+	is_write = dev->req->data_dir == WRITE;
+
+	if (len != R592_LFIFO_SIZE)
+		return -EINVAL;
+
+	dbg_verbose("doing dma transfer");
+
+	dev->dma_error = 0;
+	INIT_COMPLETION(dev->dma_done);
+
+	/* TODO: hidden assumption about nenth beeing always 1 */
+	sg_count = dma_map_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ?
+		PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+
+	if (sg_count != 1 ||
+			(sg_dma_len(&dev->req->sg) < dev->req->sg.length)) {
+		message("problem in dma_map_sg");
+		return -EIO;
+	}
+
+	r592_start_dma(dev, is_write);
+
+	/* Wait for DMA completion */
+	if (!wait_for_completion_timeout(
+			&dev->dma_done, msecs_to_jiffies(1000))) {
+		message("DMA timeout");
+		r592_stop_dma(dev, -ETIMEDOUT);
+	}
+
+	dma_unmap_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ?
+		PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+
+
+	return dev->dma_error;
+}
+
+/*
+ * Writes the FIFO in 4 byte chunks.
+ * If length isn't 4 byte aligned, rest of the data if put to a fifo
+ * to be written later
+ * Use r592_flush_fifo_write to flush that fifo when writing for the
+ * last time
+ */
+static void r592_write_fifo_pio(struct r592_device *dev,
+					unsigned char *buffer, int len)
+{
+	/* flush spill from former write */
+	if (!kfifo_is_empty(&dev->pio_fifo)) {
+
+		u8 tmp[4] = {0};
+		int copy_len = kfifo_in(&dev->pio_fifo, buffer, len);
+
+		if (!kfifo_is_full(&dev->pio_fifo))
+			return;
+		len -= copy_len;
+		buffer += copy_len;
+
+		copy_len = kfifo_out(&dev->pio_fifo, tmp, 4);
+		WARN_ON(copy_len != 4);
+		r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)tmp);
+	}
+
+	WARN_ON(!kfifo_is_empty(&dev->pio_fifo));
+
+	/* write full dwords */
+	while (len >= 4) {
+		r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer);
+		buffer += 4;
+		len -= 4;
+	}
+
+	/* put remaining bytes to the spill */
+	if (len)
+		kfifo_in(&dev->pio_fifo, buffer, len);
+}
+
+/* Flushes the temporary FIFO used to make aligned DWORD writes */
+static void r592_flush_fifo_write(struct r592_device *dev)
+{
+	u8 buffer[4] = { 0 };
+	int len;
+
+	if (kfifo_is_empty(&dev->pio_fifo))
+		return;
+
+	len = kfifo_out(&dev->pio_fifo, buffer, 4);
+	r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer);
+}
+
+/*
+ * Read a fifo in 4 bytes chunks.
+ * If input doesn't fit the buffer, it places bytes of last dword in spill
+ * buffer, so that they don't get lost on last read, just throw these away.
+ */
+static void r592_read_fifo_pio(struct r592_device *dev,
+						unsigned char *buffer, int len)
+{
+	u8 tmp[4];
+
+	/* Read from last spill */
+	if (!kfifo_is_empty(&dev->pio_fifo)) {
+		int bytes_copied =
+			kfifo_out(&dev->pio_fifo, buffer, min(4, len));
+		buffer += bytes_copied;
+		len -= bytes_copied;
+
+		if (!kfifo_is_empty(&dev->pio_fifo))
+			return;
+	}
+
+	/* Reads dwords from FIFO */
+	while (len >= 4) {
+		*(u32 *)buffer = r592_read_reg_raw_be(dev, R592_FIFO_PIO);
+		buffer += 4;
+		len -= 4;
+	}
+
+	if (len) {
+		*(u32 *)tmp = r592_read_reg_raw_be(dev, R592_FIFO_PIO);
+		kfifo_in(&dev->pio_fifo, tmp, 4);
+		len -= kfifo_out(&dev->pio_fifo, buffer, len);
+	}
+
+	WARN_ON(len);
+	return;
+}
+
+/* Transfers actual data using PIO. */
+static int r592_transfer_fifo_pio(struct r592_device *dev)
+{
+	unsigned long flags;
+
+	bool is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS;
+	struct sg_mapping_iter miter;
+
+	kfifo_reset(&dev->pio_fifo);
+
+	if (!dev->req->long_data) {
+		if (is_write) {
+			r592_write_fifo_pio(dev, dev->req->data,
+							dev->req->data_len);
+			r592_flush_fifo_write(dev);
+		} else
+			r592_read_fifo_pio(dev, dev->req->data,
+							dev->req->data_len);
+		return 0;
+	}
+
+	local_irq_save(flags);
+	sg_miter_start(&miter, &dev->req->sg, 1, SG_MITER_ATOMIC |
+		(is_write ? SG_MITER_FROM_SG : SG_MITER_TO_SG));
+
+	/* Do the transfer fifo<->memory*/
+	while (sg_miter_next(&miter))
+		if (is_write)
+			r592_write_fifo_pio(dev, miter.addr, miter.length);
+		else
+			r592_read_fifo_pio(dev, miter.addr, miter.length);
+
+
+	/* Write last few non aligned bytes*/
+	if (is_write)
+		r592_flush_fifo_write(dev);
+
+	sg_miter_stop(&miter);
+	local_irq_restore(flags);
+	return 0;
+}
+
+/* Executes one TPC (data is read/written from small or large fifo) */
+static void r592_execute_tpc(struct r592_device *dev)
+{
+	bool is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS;
+	int len, error;
+	u32 status, reg;
+
+	if (!dev->req) {
+		message("BUG: tpc execution without request!");
+		return;
+	}
+
+	len = dev->req->long_data ?
+		dev->req->sg.length : dev->req->data_len;
+
+	/* Ensure that FIFO can hold the input data */
+	if (len > R592_LFIFO_SIZE) {
+		message("IO: hardware doesn't support TPCs longer that 512");
+		error = -ENOSYS;
+		goto out;
+	}
+
+	if (!(r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_PRSNT)) {
+		dbg("IO: refusing to send TPC because card is absent");
+		error = -ENODEV;
+		goto out;
+	}
+
+	dbg("IO: executing %s LEN=%d",
+			memstick_debug_get_tpc_name(dev->req->tpc), len);
+
+	/* Set IO direction */
+	if (is_write)
+		r592_set_reg_mask(dev, R592_IO, R592_IO_DIRECTION);
+	else
+		r592_clear_reg_mask(dev, R592_IO, R592_IO_DIRECTION);
+
+
+	error = r592_test_fifo_empty(dev);
+	if (error)
+		goto out;
+
+	/* Transfer write data */
+	if (is_write) {
+		error = r592_transfer_fifo_dma(dev);
+		if (error == -EINVAL)
+			error = r592_transfer_fifo_pio(dev);
+	}
+
+	if (error)
+		goto out;
+
+	/* Trigger the TPC */
+	reg = (len << R592_TPC_EXEC_LEN_SHIFT) |
+		(dev->req->tpc << R592_TPC_EXEC_TPC_SHIFT) |
+			R592_TPC_EXEC_BIG_FIFO;
+
+	r592_write_reg(dev, R592_TPC_EXEC, reg);
+
+	/* Wait for TPC completion */
+	status = R592_STATUS_RDY;
+	if (dev->req->need_card_int)
+		status |= R592_STATUS_CED;
+
+	error = r592_wait_status(dev, status, status);
+	if (error) {
+		message("card didn't respond");
+		goto out;
+	}
+
+	/* Test IO errors */
+	error = r592_test_io_error(dev);
+	if (error)
+		goto out;
+
+	/* Read data from FIFO */
+	if (!is_write) {
+		error = r592_transfer_fifo_dma(dev);
+		if (error == -EINVAL)
+			error = r592_transfer_fifo_pio(dev);
+	}
+
+	/* read INT reg. This can be shortened with shifts, but that way
+		its more readable */
+	if (dev->parallel_mode && dev->req->need_card_int) {
+
+		dev->req->int_reg = 0;
+		status = r592_read_reg(dev, R592_STATUS);
+
+		if (status & R592_STATUS_P_CMDNACK)
+			dev->req->int_reg |= MEMSTICK_INT_CMDNAK;
+		if (status & R592_STATUS_P_BREQ)
+			dev->req->int_reg |= MEMSTICK_INT_BREQ;
+		if (status & R592_STATUS_P_INTERR)
+			dev->req->int_reg |= MEMSTICK_INT_ERR;
+		if (status & R592_STATUS_P_CED)
+			dev->req->int_reg |= MEMSTICK_INT_CED;
+	}
+out:
+	dev->req->error = error;
+	r592_clear_reg_mask(dev, R592_REG_MSC, R592_REG_MSC_LED);
+	return;
+}
+
+/* Main request processing thread */
+static int r592_process_thread(void *data)
+{
+	int error;
+	struct r592_device *dev = (struct r592_device *)data;
+	unsigned long flags;
+
+	while (!kthread_should_stop()) {
+		spin_lock_irqsave(&dev->io_thread_lock, flags);
+		set_current_state(TASK_INTERRUPTIBLE);
+		error = memstick_next_req(dev->host, &dev->req);
+		spin_unlock_irqrestore(&dev->io_thread_lock, flags);
+
+		if (error) {
+			if (error == -ENXIO) {
+				dbg_verbose("IO: done IO, sleeping");
+			} else {
+				dbg("IO: unknown error from "
+					"memstick_next_req %d", error);
+			}
+
+			if (kthread_should_stop())
+				set_current_state(TASK_RUNNING);
+
+			schedule();
+		} else {
+			set_current_state(TASK_RUNNING);
+			r592_execute_tpc(dev);
+		}
+	}
+	return 0;
+}
+
+/* Reprogram chip to detect change in card state */
+/* eg, if card is detected, arm it to detect removal, and vice versa */
+static void r592_update_card_detect(struct r592_device *dev)
+{
+	u32 reg = r592_read_reg(dev, R592_REG_MSC);
+	bool card_detected = reg & R592_REG_MSC_PRSNT;
+
+	dbg("update card detect. card state: %s", card_detected ?
+		"present" : "absent");
+
+	reg &= ~((R592_REG_MSC_IRQ_REMOVE | R592_REG_MSC_IRQ_INSERT) << 16);
+
+	if (card_detected)
+		reg |= (R592_REG_MSC_IRQ_REMOVE << 16);
+	else
+		reg |= (R592_REG_MSC_IRQ_INSERT << 16);
+
+	r592_write_reg(dev, R592_REG_MSC, reg);
+}
+
+/* Timer routine that fires 1 second after last card detection event, */
+static void r592_detect_timer(long unsigned int data)
+{
+	struct r592_device *dev = (struct r592_device *)data;
+	r592_update_card_detect(dev);
+	memstick_detect_change(dev->host);
+}
+
+/* Interrupt handler */
+static irqreturn_t r592_irq(int irq, void *data)
+{
+	struct r592_device *dev = (struct r592_device *)data;
+	irqreturn_t ret = IRQ_NONE;
+	u32 reg;
+	u16 irq_enable, irq_status;
+	unsigned long flags;
+	int error;
+
+	spin_lock_irqsave(&dev->irq_lock, flags);
+
+	reg = r592_read_reg(dev, R592_REG_MSC);
+	irq_enable = reg >> 16;
+	irq_status = reg & 0xFFFF;
+
+	/* Ack the interrupts */
+	reg &= ~irq_status;
+	r592_write_reg(dev, R592_REG_MSC, reg);
+
+	/* Get the IRQ status minus bits that aren't enabled */
+	irq_status &= (irq_enable);
+
+	/* Due to limitation of memstick core, we don't look at bits that
+		indicate that card was removed/inserted and/or present */
+	if (irq_status & (R592_REG_MSC_IRQ_INSERT | R592_REG_MSC_IRQ_REMOVE)) {
+
+		bool card_was_added = irq_status & R592_REG_MSC_IRQ_INSERT;
+		ret = IRQ_HANDLED;
+
+		message("IRQ: card %s", card_was_added ? "added" : "removed");
+
+		mod_timer(&dev->detect_timer,
+			jiffies + msecs_to_jiffies(card_was_added ? 500 : 50));
+	}
+
+	if (irq_status &
+		(R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR)) {
+		ret = IRQ_HANDLED;
+
+		if (irq_status & R592_REG_MSC_FIFO_DMA_ERR) {
+			message("IRQ: DMA error");
+			error = -EIO;
+		} else {
+			dbg_verbose("IRQ: dma done");
+			error = 0;
+		}
+
+		r592_stop_dma(dev, error);
+		complete(&dev->dma_done);
+	}
+
+	spin_unlock_irqrestore(&dev->irq_lock, flags);
+	return ret;
+}
+
+/* External inteface: set settings */
+static int r592_set_param(struct memstick_host *host,
+			enum memstick_param param, int value)
+{
+	struct r592_device *dev = memstick_priv(host);
+
+	switch (param) {
+	case MEMSTICK_POWER:
+		switch (value) {
+		case MEMSTICK_POWER_ON:
+			return r592_enable_device(dev, true);
+		case MEMSTICK_POWER_OFF:
+			return r592_enable_device(dev, false);
+		default:
+			return -EINVAL;
+		}
+	case MEMSTICK_INTERFACE:
+		switch (value) {
+		case MEMSTICK_SERIAL:
+			return r592_set_mode(dev, 0);
+		case MEMSTICK_PAR4:
+			return r592_set_mode(dev, 1);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+/* External interface: submit requests */
+static void r592_submit_req(struct memstick_host *host)
+{
+	struct r592_device *dev = memstick_priv(host);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->io_thread_lock, flags);
+	if (wake_up_process(dev->io_thread))
+		dbg_verbose("IO thread woken to process requests");
+	spin_unlock_irqrestore(&dev->io_thread_lock, flags);
+}
+
+static const struct pci_device_id r592_pci_id_tbl[] = {
+
+	{ PCI_VDEVICE(RICOH, 0x0592), },
+	{ },
+};
+
+/* Main entry */
+static int r592_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int error = -ENOMEM;
+	struct memstick_host *host;
+	struct r592_device *dev;
+
+	/* Allocate memory */
+	host = memstick_alloc_host(sizeof(struct r592_device), &pdev->dev);
+	if (!host)
+		goto error1;
+
+	dev = memstick_priv(host);
+	dev->host = host;
+	dev->pci_dev = pdev;
+	pci_set_drvdata(pdev, dev);
+
+	/* pci initialization */
+	error = pci_enable_device(pdev);
+	if (error)
+		goto error2;
+
+	pci_set_master(pdev);
+	error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (error)
+		goto error3;
+
+	error = pci_request_regions(pdev, DRV_NAME);
+	if (error)
+		goto error3;
+
+	dev->mmio = pci_ioremap_bar(pdev, 0);
+	if (!dev->mmio)
+		goto error4;
+
+	dev->irq = pdev->irq;
+	spin_lock_init(&dev->irq_lock);
+	spin_lock_init(&dev->io_thread_lock);
+	init_completion(&dev->dma_done);
+	INIT_KFIFO(dev->pio_fifo);
+	setup_timer(&dev->detect_timer,
+		r592_detect_timer, (long unsigned int)dev);
+
+	/* Host initialization */
+	host->caps = MEMSTICK_CAP_PAR4;
+	host->request = r592_submit_req;
+	host->set_param = r592_set_param;
+	r592_check_dma(dev);
+
+	dev->io_thread = kthread_run(r592_process_thread, dev, "r592_io");
+	if (IS_ERR(dev->io_thread)) {
+		error = PTR_ERR(dev->io_thread);
+		goto error5;
+	}
+
+	/* This is just a precation, so don't fail */
+	dev->dummy_dma_page = pci_alloc_consistent(pdev, PAGE_SIZE,
+		&dev->dummy_dma_page_physical_address);
+	r592_stop_dma(dev , 0);
+
+	if (request_irq(dev->irq, &r592_irq, IRQF_SHARED,
+			  DRV_NAME, dev))
+		goto error6;
+
+	r592_update_card_detect(dev);
+	if (memstick_add_host(host))
+		goto error7;
+
+	message("driver succesfully loaded");
+	return 0;
+error7:
+	free_irq(dev->irq, dev);
+error6:
+	if (dev->dummy_dma_page)
+		pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page,
+			dev->dummy_dma_page_physical_address);
+
+	kthread_stop(dev->io_thread);
+error5:
+	iounmap(dev->mmio);
+error4:
+	pci_release_regions(pdev);
+error3:
+	pci_disable_device(pdev);
+error2:
+	memstick_free_host(host);
+error1:
+	return error;
+}
+
+static void r592_remove(struct pci_dev *pdev)
+{
+	int error = 0;
+	struct r592_device *dev = pci_get_drvdata(pdev);
+
+	/* Stop the processing thread.
+	That ensures that we won't take any more requests */
+	kthread_stop(dev->io_thread);
+
+	while (!error && dev->req) {
+		dev->req->error = -ETIME;
+		error = memstick_next_req(dev->host, &dev->req);
+	}
+	memstick_remove_host(dev->host);
+
+	free_irq(dev->irq, dev);
+	iounmap(dev->mmio);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	memstick_free_host(dev->host);
+
+	if (dev->dummy_dma_page)
+		pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page,
+			dev->dummy_dma_page_physical_address);
+}
+
+#ifdef CONFIG_PM
+static int r592_suspend(struct device *core_dev)
+{
+	struct pci_dev *pdev = to_pci_dev(core_dev);
+	struct r592_device *dev = pci_get_drvdata(pdev);
+
+	r592_clear_interrupts(dev);
+	memstick_suspend_host(dev->host);
+	del_timer_sync(&dev->detect_timer);
+	return 0;
+}
+
+static int r592_resume(struct device *core_dev)
+{
+	struct pci_dev *pdev = to_pci_dev(core_dev);
+	struct r592_device *dev = pci_get_drvdata(pdev);
+
+	r592_clear_interrupts(dev);
+	r592_enable_device(dev, false);
+	memstick_resume_host(dev->host);
+	r592_update_card_detect(dev);
+	return 0;
+}
+
+SIMPLE_DEV_PM_OPS(r592_pm_ops, r592_suspend, r592_resume);
+#endif
+
+MODULE_DEVICE_TABLE(pci, r592_pci_id_tbl);
+
+static struct pci_driver r852_pci_driver = {
+	.name		= DRV_NAME,
+	.id_table	= r592_pci_id_tbl,
+	.probe		= r592_probe,
+	.remove		= r592_remove,
+#ifdef CONFIG_PM
+	.driver.pm	= &r592_pm_ops,
+#endif
+};
+
+static __init int r592_module_init(void)
+{
+	return pci_register_driver(&r852_pci_driver);
+}
+
+static void __exit r592_module_exit(void)
+{
+	pci_unregister_driver(&r852_pci_driver);
+}
+
+module_init(r592_module_init);
+module_exit(r592_module_exit);
+
+module_param(enable_dma, bool, S_IRUGO);
+MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level (0-3)");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE_DESCRIPTION("Ricoh R5C592 Memstick/Memstick PRO card reader driver");
diff --git a/drivers/memstick/host/r592.h b/drivers/memstick/host/r592.h
new file mode 100644
index 0000000..eee264e
--- /dev/null
+++ b/drivers/memstick/host/r592.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2010 - Maxim Levitsky
+ * driver for Ricoh memstick 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.
+ */
+
+#ifndef R592_H
+
+#include <linux/memstick.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/kfifo.h>
+#include <linux/ctype.h>
+
+/* write to this reg (number,len) triggers TPC execution */
+#define R592_TPC_EXEC			0x00
+#define R592_TPC_EXEC_LEN_SHIFT		16		/* Bits 16..25 are TPC len */
+#define R592_TPC_EXEC_BIG_FIFO		(1 << 26)	/* If bit 26 is set, large fifo is used (reg 48) */
+#define R592_TPC_EXEC_TPC_SHIFT		28		/* Bits 28..31 are the TPC number */
+
+
+/* Window for small TPC fifo (big endian)*/
+/* reads and writes always are done in  8 byte chunks */
+/* Not used in driver, because large fifo does better job */
+#define R592_SFIFO			0x08
+
+
+/* Status register (ms int, small fifo, IO)*/
+#define R592_STATUS			0x10
+							/* Parallel INT bits */
+#define R592_STATUS_P_CMDNACK		(1 << 16)	/* INT reg: NACK (parallel mode) */
+#define R592_STATUS_P_BREQ		(1 << 17)	/* INT reg: card ready (parallel mode)*/
+#define R592_STATUS_P_INTERR		(1 << 18)	/* INT reg: int error (parallel mode)*/
+#define R592_STATUS_P_CED		(1 << 19)	/* INT reg: command done (parallel mode) */
+
+							/* Fifo status */
+#define R592_STATUS_SFIFO_FULL		(1 << 20)	/* Small Fifo almost full (last chunk is written) */
+#define R592_STATUS_SFIFO_EMPTY		(1 << 21)	/* Small Fifo empty */
+
+							/* Error detection via CRC */
+#define R592_STATUS_SEND_ERR		(1 << 24)	/* Send failed */
+#define R592_STATUS_RECV_ERR		(1 << 25)	/* Recieve failed */
+
+							/* Card state */
+#define R592_STATUS_RDY			(1 << 28)	/* RDY signal recieved */
+#define R592_STATUS_CED			(1 << 29)	/* INT: Command done (serial mode)*/
+#define R592_STATUS_SFIFO_INPUT		(1 << 30)	/* Small fifo recieved data*/
+
+#define R592_SFIFO_SIZE			32		/* total size of small fifo is 32 bytes */
+#define R592_SFIFO_PACKET		8		/* packet size of small fifo */
+
+/* IO control */
+#define R592_IO				0x18
+#define	R592_IO_16			(1 << 16)	/* Set by default, can be cleared */
+#define	R592_IO_18			(1 << 18)	/* Set by default, can be cleared */
+#define	R592_IO_SERIAL1			(1 << 20)	/* Set by default, can be cleared, (cleared on parallel) */
+#define	R592_IO_22			(1 << 22)	/* Set by default, can be cleared */
+#define R592_IO_DIRECTION		(1 << 24)	/* TPC direction (1 write 0 read) */
+#define	R592_IO_26			(1 << 26)	/* Set by default, can be cleared */
+#define	R592_IO_SERIAL2			(1 << 30)	/* Set by default, can be cleared (cleared on parallel), serial doesn't work if unset */
+#define R592_IO_RESET			(1 << 31)	/* Reset, sets defaults*/
+
+
+/* Turns hardware on/off */
+#define R592_POWER			0x20		/* bits 0-7 writeable */
+#define R592_POWER_0			(1 << 0)	/* set on start, cleared on stop - must be set*/
+#define R592_POWER_1			(1 << 1)	/* set on start, cleared on stop - must be set*/
+#define R592_POWER_3			(1 << 3)	/* must be clear */
+#define R592_POWER_20			(1 << 5)	/* set before switch to parallel */
+
+/* IO mode*/
+#define R592_IO_MODE			0x24
+#define R592_IO_MODE_SERIAL		1
+#define R592_IO_MODE_PARALLEL		3
+
+
+/* IRQ,card detection,large fifo (first word irq status, second enable) */
+/* IRQs are ACKed by clearing the bits */
+#define R592_REG_MSC			0x28
+#define R592_REG_MSC_PRSNT		(1 << 1)	/* card present (only status)*/
+#define R592_REG_MSC_IRQ_INSERT		(1 << 8)	/* detect insert / card insered */
+#define R592_REG_MSC_IRQ_REMOVE		(1 << 9)	/* detect removal / card removed */
+#define R592_REG_MSC_FIFO_EMPTY		(1 << 10)	/* fifo is empty */
+#define R592_REG_MSC_FIFO_DMA_DONE	(1 << 11)	/* dma enable / dma done */
+
+#define R592_REG_MSC_FIFO_USER_ORN	(1 << 12)	/* set if software reads empty fifo (if R592_REG_MSC_FIFO_EMPTY is set) */
+#define R592_REG_MSC_FIFO_MISMATH	(1 << 13)	/* set if amount of data in fifo doesn't match amount in TPC */
+#define R592_REG_MSC_FIFO_DMA_ERR	(1 << 14)	/* IO failure */
+#define R592_REG_MSC_LED		(1 << 15)	/* clear to turn led off (only status)*/
+
+#define DMA_IRQ_ACK_MASK \
+	(R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR)
+
+#define DMA_IRQ_EN_MASK (DMA_IRQ_ACK_MASK << 16)
+
+#define IRQ_ALL_ACK_MASK 0x00007F00
+#define IRQ_ALL_EN_MASK (IRQ_ALL_ACK_MASK << 16)
+
+/* DMA address for large FIFO read/writes*/
+#define R592_FIFO_DMA			0x2C
+
+/* PIO access to large FIFO (512 bytes) (big endian)*/
+#define R592_FIFO_PIO			0x30
+#define R592_LFIFO_SIZE			512		/* large fifo size */
+
+
+/* large FIFO DMA settings */
+#define R592_FIFO_DMA_SETTINGS		0x34
+#define R592_FIFO_DMA_SETTINGS_EN	(1 << 0)	/* DMA enabled */
+#define R592_FIFO_DMA_SETTINGS_DIR	(1 << 1)	/* Dma direction (1 read, 0 write) */
+#define R592_FIFO_DMA_SETTINGS_CAP	(1 << 24)	/* Dma is aviable */
+
+/* Maybe just an delay */
+/* Bits 17..19 are just number */
+/* bit 16 is set, then bit 20 is waited */
+/* time to wait is about 50 spins * 2 ^ (bits 17..19) */
+/* seems to be possible just to ignore */
+/* Probably debug register */
+#define R592_REG38			0x38
+#define R592_REG38_CHANGE		(1 << 16)	/* Start bit */
+#define R592_REG38_DONE			(1 << 20)	/* HW set this after the delay */
+#define R592_REG38_SHIFT		17
+
+/* Debug register, written (0xABCDEF00) when error happens - not used*/
+#define R592_REG_3C			0x3C
+
+struct r592_device {
+	struct pci_dev *pci_dev;
+	struct memstick_host	*host;		/* host backpointer */
+	struct memstick_request *req;		/* current request */
+
+	/* Registers, IRQ */
+	void __iomem *mmio;
+	int irq;
+	spinlock_t irq_lock;
+	spinlock_t io_thread_lock;
+	struct timer_list detect_timer;
+
+	struct task_struct *io_thread;
+	bool parallel_mode;
+
+	DECLARE_KFIFO(pio_fifo, u8, sizeof(u32));
+
+	/* DMA area */
+	int dma_capable;
+	int dma_error;
+	struct completion dma_done;
+	void *dummy_dma_page;
+	dma_addr_t dummy_dma_page_physical_address;
+
+};
+
+#define DRV_NAME "r592"
+
+
+#define message(format, ...) \
+	printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__)
+
+#define __dbg(level, format, ...) \
+	do { \
+		if (debug >= level) \
+			printk(KERN_DEBUG DRV_NAME \
+				": " format "\n", ## __VA_ARGS__); \
+	} while (0)
+
+
+#define dbg(format, ...)		__dbg(1, format, ## __VA_ARGS__)
+#define dbg_verbose(format, ...)	__dbg(2, format, ## __VA_ARGS__)
+#define dbg_reg(format, ...)		__dbg(3, format, ## __VA_ARGS__)
+
+#endif
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/6] memstick: jmb38x: Driver rework.
  2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 1/6] memstick: core: add series of common helpers Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 2/6] memstick: Add driver for Ricoh R5C592 Card reader Maxim Levitsky
@ 2010-10-16 23:12 ` Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 4/6] memstick: add support for legacy memorysticks Maxim Levitsky
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-16 23:12 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML, Maxim Levitsky

Many of driver parts were rewritten, code simplified,
a lot of debbuging code added.

Tested with ms standard and ms pro cards.

DMA is now used for all transfers by default.
(except < 8 TPC that are written through TPC_P0/1)
That is used to work around hardware bug.

Clock setup fixed as suggested, serial IO works now.

No performance regressions (even a small improvement).

Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 drivers/memstick/host/jmb38x_ms.c | 1198 ++++++++++++++++++-------------------
 drivers/memstick/host/jmb38x_ms.h |  200 ++++++
 2 files changed, 775 insertions(+), 623 deletions(-)
 create mode 100644 drivers/memstick/host/jmb38x_ms.h

diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c
index f2b894c..4ee68b2 100644
--- a/drivers/memstick/host/jmb38x_ms.c
+++ b/drivers/memstick/host/jmb38x_ms.c
@@ -2,6 +2,7 @@
  *  jmb38x_ms.c - JMicron jmb38x MemoryStick card reader
  *
  *  Copyright (C) 2008 Alex Dubov <oakad@yahoo.com>
+ *  Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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
@@ -17,746 +18,672 @@
 #include <linux/highmem.h>
 #include <linux/memstick.h>
 #include <linux/slab.h>
-
-#define DRIVER_NAME "jmb38x_ms"
+#include "jmb38x_ms.h"
 
 static int no_dma;
-module_param(no_dma, bool, 0644);
-
-enum {
-	DMA_ADDRESS       = 0x00,
-	BLOCK             = 0x04,
-	DMA_CONTROL       = 0x08,
-	TPC_P0            = 0x0c,
-	TPC_P1            = 0x10,
-	TPC               = 0x14,
-	HOST_CONTROL      = 0x18,
-	DATA              = 0x1c,
-	STATUS            = 0x20,
-	INT_STATUS        = 0x24,
-	INT_STATUS_ENABLE = 0x28,
-	INT_SIGNAL_ENABLE = 0x2c,
-	TIMER             = 0x30,
-	TIMER_CONTROL     = 0x34,
-	PAD_OUTPUT_ENABLE = 0x38,
-	PAD_PU_PD         = 0x3c,
-	CLOCK_DELAY       = 0x40,
-	ADMA_ADDRESS      = 0x44,
-	CLOCK_CONTROL     = 0x48,
-	LED_CONTROL       = 0x4c,
-	VERSION           = 0x50
-};
+static int debug;
 
-struct jmb38x_ms_host {
-	struct jmb38x_ms        *chip;
-	void __iomem            *addr;
-	spinlock_t              lock;
-	struct tasklet_struct   notify;
-	int                     id;
-	char                    host_id[32];
-	int                     irq;
-	unsigned int            block_pos;
-	unsigned long           timeout_jiffies;
-	struct timer_list       timer;
-	struct memstick_request *req;
-	unsigned char           cmd_flags;
-	unsigned char           io_pos;
-	unsigned int            io_word[2];
-};
-
-struct jmb38x_ms {
-	struct pci_dev        *pdev;
-	int                   host_cnt;
-	struct memstick_host  *hosts[];
-};
-
-#define BLOCK_COUNT_MASK       0xffff0000
-#define BLOCK_SIZE_MASK        0x00000fff
-
-#define DMA_CONTROL_ENABLE     0x00000001
-
-#define TPC_DATA_SEL           0x00008000
-#define TPC_DIR                0x00004000
-#define TPC_WAIT_INT           0x00002000
-#define TPC_GET_INT            0x00000800
-#define TPC_CODE_SZ_MASK       0x00000700
-#define TPC_DATA_SZ_MASK       0x00000007
-
-#define HOST_CONTROL_TDELAY_EN 0x00040000
-#define HOST_CONTROL_HW_OC_P   0x00010000
-#define HOST_CONTROL_RESET_REQ 0x00008000
-#define HOST_CONTROL_REI       0x00004000
-#define HOST_CONTROL_LED       0x00000400
-#define HOST_CONTROL_FAST_CLK  0x00000200
-#define HOST_CONTROL_RESET     0x00000100
-#define HOST_CONTROL_POWER_EN  0x00000080
-#define HOST_CONTROL_CLOCK_EN  0x00000040
-#define HOST_CONTROL_REO       0x00000008
-#define HOST_CONTROL_IF_SHIFT  4
-
-#define HOST_CONTROL_IF_SERIAL 0x0
-#define HOST_CONTROL_IF_PAR4   0x1
-#define HOST_CONTROL_IF_PAR8   0x3
-
-#define STATUS_BUSY             0x00080000
-#define STATUS_MS_DAT7          0x00040000
-#define STATUS_MS_DAT6          0x00020000
-#define STATUS_MS_DAT5          0x00010000
-#define STATUS_MS_DAT4          0x00008000
-#define STATUS_MS_DAT3          0x00004000
-#define STATUS_MS_DAT2          0x00002000
-#define STATUS_MS_DAT1          0x00001000
-#define STATUS_MS_DAT0          0x00000800
-#define STATUS_HAS_MEDIA        0x00000400
-#define STATUS_FIFO_EMPTY       0x00000200
-#define STATUS_FIFO_FULL        0x00000100
-#define STATUS_MS_CED           0x00000080
-#define STATUS_MS_ERR           0x00000040
-#define STATUS_MS_BRQ           0x00000020
-#define STATUS_MS_CNK           0x00000001
-
-#define INT_STATUS_TPC_ERR      0x00080000
-#define INT_STATUS_CRC_ERR      0x00040000
-#define INT_STATUS_TIMER_TO     0x00020000
-#define INT_STATUS_HSK_TO       0x00010000
-#define INT_STATUS_ANY_ERR      0x00008000
-#define INT_STATUS_FIFO_WRDY    0x00000080
-#define INT_STATUS_FIFO_RRDY    0x00000040
-#define INT_STATUS_MEDIA_OUT    0x00000010
-#define INT_STATUS_MEDIA_IN     0x00000008
-#define INT_STATUS_DMA_BOUNDARY 0x00000004
-#define INT_STATUS_EOTRAN       0x00000002
-#define INT_STATUS_EOTPC        0x00000001
-
-#define INT_STATUS_ALL          0x000f801f
-
-#define PAD_OUTPUT_ENABLE_MS  0x0F3F
-
-#define PAD_PU_PD_OFF         0x7FFF0000
-#define PAD_PU_PD_ON_MS_SOCK0 0x5f8f0000
-#define PAD_PU_PD_ON_MS_SOCK1 0x0f0f0000
-
-#define CLOCK_CONTROL_40MHZ   0x00000001
-#define CLOCK_CONTROL_50MHZ   0x0000000a
-#define CLOCK_CONTROL_60MHZ   0x00000008
-#define CLOCK_CONTROL_62_5MHZ 0x0000000c
-#define CLOCK_CONTROL_OFF     0x00000000
-
-#define PCI_CTL_CLOCK_DLY_ADDR   0x000000b0
-#define PCI_CTL_CLOCK_DLY_MASK_A 0x00000f00
-#define PCI_CTL_CLOCK_DLY_MASK_B 0x0000f000
-
-enum {
-	CMD_READY    = 0x01,
-	FIFO_READY   = 0x02,
-	REG_DATA     = 0x04,
-	DMA_DATA     = 0x08
-};
-
-static unsigned int jmb38x_ms_read_data(struct jmb38x_ms_host *host,
-					unsigned char *buf, unsigned int length)
+/* Read a register*/
+static inline u32 j38ms_read_reg(struct j38ms_host *host, int address)
 {
-	unsigned int off = 0;
+	u32 value =  readl(host->addr + address);
+	dbg_reg(host, "reg 0x%02x == 0x%08x", address, value);
+	return value;
+}
 
-	while (host->io_pos && length) {
-		buf[off++] = host->io_word[0] & 0xff;
-		host->io_word[0] >>= 8;
-		length--;
-		host->io_pos--;
-	}
+/* Write a register */
+static inline void j38ms_write_reg(struct j38ms_host *host,
+						int address, u32 value)
+{
+	dbg_reg(host, "reg 0x%02x <- 0x%08x", address, cpu_to_le32(value));
+	writel(value, host->addr + address);
+}
 
-	if (!length)
-		return off;
+/* Read a register without endiannes conversion*/
+static inline u32 j38ms_read_reg_raw(struct j38ms_host *host, int address)
+{
+	u32 value =  __raw_readl(host->addr + address);
+	dbg_reg(host, "reg 0x%02x == 0x%08x", address, cpu_to_le32(value));
+	return value;
+}
 
-	while (!(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) {
-		if (length < 4)
-			break;
-		*(unsigned int *)(buf + off) = __raw_readl(host->addr + DATA);
-		length -= 4;
-		off += 4;
-	}
-
-	if (length
-	    && !(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) {
-		host->io_word[0] = readl(host->addr + DATA);
-		for (host->io_pos = 4; host->io_pos; --host->io_pos) {
-			buf[off++] = host->io_word[0] & 0xff;
-			host->io_word[0] >>= 8;
-			length--;
-			if (!length)
-				break;
-		}
-	}
+/* Write a register without endiannes conversion */
+static inline void j38ms_write_reg_raw(struct j38ms_host *host,
+						int address, u32 value)
+{
+	dbg_reg(host, "reg 0x%02x <- 0x%08x", address, value);
+	__raw_writel(value, host->addr + address);
+}
 
-	return off;
+/* Set specific bits in a register*/
+static inline void j38ms_set_reg_mask(struct j38ms_host *host,
+						int address, u32 mask)
+{
+	u32 reg = readl(host->addr + address);
+	dbg_reg(host, "reg 0x%02x |= 0x%08x (old =0x%08x)", address, mask, reg);
+	writel(reg | mask , host->addr + address);
 }
 
-static unsigned int jmb38x_ms_read_reg_data(struct jmb38x_ms_host *host,
-					    unsigned char *buf,
-					    unsigned int length)
+/* Clear specific bits in a register*/
+static inline void j38ms_clear_reg_mask(struct j38ms_host *host,
+						int address, u32 mask)
 {
-	unsigned int off = 0;
+	u32 reg = readl(host->addr + address);
+	dbg_reg(host, "reg 0x%02x &= 0x%08x (old = 0x%08x, mask = 0x%08x)",
+						address, ~mask, reg, mask);
+	writel(reg & ~mask, host->addr + address);
+}
 
-	while (host->io_pos > 4 && length) {
-		buf[off++] = host->io_word[0] & 0xff;
-		host->io_word[0] >>= 8;
-		length--;
-		host->io_pos--;
+/* Reads one DWORD via PIO. returns -EAGAIN if fifo is empty */
+static inline int j38ms_read_fifo_dword_pio(struct j38ms_host *host, u32 *dword)
+{
+	if (unlikely(j38ms_read_reg(host, STATUS) & STATUS_FIFO_EMPTY)) {
+		dbg(host, "PIO: FIFO empty");
+		return -EAGAIN;
 	}
 
-	if (!length)
-		return off;
+	*dword = j38ms_read_reg_raw(host, DATA);
+	dbg(host, "PIO: read: %08x", *dword);
+	return 0;
+}
 
-	while (host->io_pos && length) {
-		buf[off++] = host->io_word[1] & 0xff;
-		host->io_word[1] >>= 8;
-		length--;
-		host->io_pos--;
+/* Writes one DWORD via PIO. returns -EAGAIN if fifo is full */
+static inline int j38ms_write_fifo_dword_pio(struct j38ms_host *host, u32 dword)
+{
+	if (unlikely(j38ms_read_reg(host, STATUS) & STATUS_FIFO_FULL)) {
+		dbg(host, "PIO: FIFO full");
+		return -EAGAIN;
 	}
 
-	return off;
+	dbg(host, "PIO: writing: %08x", dword);
+	j38ms_write_reg_raw(host, DATA, dword);
+	return 0;
 }
 
-static unsigned int jmb38x_ms_write_data(struct jmb38x_ms_host *host,
-					 unsigned char *buf,
-					 unsigned int length)
+/* Read TPC data using PIO */
+static unsigned int j38ms_read_fifo_pio(struct j38ms_host *host,
+				unsigned char *buf, unsigned int length)
 {
-	unsigned int off = 0;
+	unsigned int orig_len = length;
+	unsigned char tmp_buf[4];
 
-	if (host->io_pos) {
-		while (host->io_pos < 4 && length) {
-			host->io_word[0] |=  buf[off++] << (host->io_pos * 8);
-			host->io_pos++;
-			length--;
-		}
+	/* Read bytes from last saved part */
+	if (host->pio_tmp_buf_len) {
+		int count = min(host->pio_tmp_buf_len, length);
+		memcpy(buf, host->pio_tmp_buf, count);
+		buf += count;
+		length -= count;
 	}
 
-	if (host->io_pos == 4
-	    && !(STATUS_FIFO_FULL & readl(host->addr + STATUS))) {
-		writel(host->io_word[0], host->addr + DATA);
-		host->io_pos = 0;
-		host->io_word[0] = 0;
-	} else if (host->io_pos) {
-		return off;
-	}
+	/* Read aligned data*/
+	for (; length >= 4 ; buf += 4, length -= 4)
+		if (j38ms_read_fifo_dword_pio(host, (u32 *)buf))
+			return orig_len - length;
 
-	if (!length)
-		return off;
+	/* Read last 4 bytes, and
+			save unconsumed part of it to the pio_tmp_buf */
+	if (length && !j38ms_read_fifo_dword_pio(host, (u32 *)tmp_buf)) {
 
-	while (!(STATUS_FIFO_FULL & readl(host->addr + STATUS))) {
-		if (length < 4)
-			break;
-
-		__raw_writel(*(unsigned int *)(buf + off),
-			     host->addr + DATA);
-		length -= 4;
-		off += 4;
-	}
-
-	switch (length) {
-	case 3:
-		host->io_word[0] |= buf[off + 2] << 16;
-		host->io_pos++;
-	case 2:
-		host->io_word[0] |= buf[off + 1] << 8;
-		host->io_pos++;
-	case 1:
-		host->io_word[0] |= buf[off];
-		host->io_pos++;
+		host->pio_tmp_buf_len = 4 - length;
+		memcpy(buf, tmp_buf, length);
+		memcpy(host->pio_tmp_buf,
+				tmp_buf + length, host->pio_tmp_buf_len);
+		length = 0;
 	}
 
-	off += host->io_pos;
-
-	return off;
+	return orig_len - length;
 }
 
-static unsigned int jmb38x_ms_write_reg_data(struct jmb38x_ms_host *host,
-					     unsigned char *buf,
-					     unsigned int length)
+/* Write TPC data through normal PIO fifo */
+static unsigned int j38ms_write_fifo_pio(struct j38ms_host *host,
+					 unsigned char *buf,
+					 unsigned int length)
 {
-	unsigned int off = 0;
-
-	while (host->io_pos < 4 && length) {
-		host->io_word[0] &= ~(0xff << (host->io_pos * 8));
-		host->io_word[0] |=  buf[off++] << (host->io_pos * 8);
-		host->io_pos++;
-		length--;
+	unsigned int orig_len = length;
+
+	/* Complete the last saved bytes*/
+	if (host->pio_tmp_buf_len) {
+		int count = min(4 - host->pio_tmp_buf_len, length);
+		memcpy(host->pio_tmp_buf + host->pio_tmp_buf_len, buf, count);
+		buf += count;
+		length -= count;
+
+		if (host->pio_tmp_buf_len == 4) {
+			if (j38ms_write_fifo_dword_pio(
+					host, *(u32 *)host->pio_tmp_buf))
+				return orig_len - length;
+			else
+				host->pio_tmp_buf_len = 0;
+		}
 	}
 
-	if (!length)
-		return off;
+	/* Write aligned data to hardware */
+	for (; length >= 4 ; length -= 4, buf += 4)
+		if (j38ms_write_fifo_dword_pio(host, *(u32 *)buf))
+			return orig_len - length;
 
-	while (host->io_pos < 8 && length) {
-		host->io_word[1] &= ~(0xff << (host->io_pos * 8));
-		host->io_word[1] |=  buf[off++] << (host->io_pos * 8);
-		host->io_pos++;
-		length--;
+	/* Save last 3-1 bytes to buffer, because we can't send them now*/
+	if (length) {
+		memset(host->pio_tmp_buf, 0, 4);
+		memcpy(host->pio_tmp_buf, buf, length);
+		host->pio_tmp_buf_len = length;
 	}
 
-	return off;
+	return orig_len - length;
 }
 
-static int jmb38x_ms_transfer_data(struct jmb38x_ms_host *host)
+
+/* Transfer data between current request and FIFO */
+static void j38ms_transfer_pio(struct j38ms_host *host)
 {
-	unsigned int length;
-	unsigned int off;
-	unsigned int t_size, p_cnt;
 	unsigned char *buf;
-	struct page *pg;
+	unsigned int len;
 	unsigned long flags = 0;
 
-	if (host->req->long_data) {
-		length = host->req->sg.length - host->block_pos;
-		off = host->req->sg.offset + host->block_pos;
-	} else {
-		length = host->req->data_len - host->block_pos;
-		off = 0;
-	}
+	if (host->req->long_data)
+		local_irq_save(flags);
 
-	while (length) {
-		unsigned int uninitialized_var(p_off);
+	dbg_v(host, "PIO: new transfer");
 
+	while (1) {
 		if (host->req->long_data) {
-			pg = nth_page(sg_page(&host->req->sg),
-				      off >> PAGE_SHIFT);
-			p_off = offset_in_page(off);
-			p_cnt = PAGE_SIZE - p_off;
-			p_cnt = min(p_cnt, length);
-
-			local_irq_save(flags);
-			buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + p_off;
+			if (!sg_miter_next(&host->pio_sg_iter))
+				break;
+
+			buf = host->pio_sg_iter.addr;
+			len = host->pio_sg_iter.length;
 		} else {
-			buf = host->req->data + host->block_pos;
-			p_cnt = host->req->data_len - host->block_pos;
+			buf = host->req->data + host->pio_offset;
+			len = host->req->data_len - host->pio_offset;
+
+			if (!len)
+				break;
 		}
 
-		if (host->req->data_dir == WRITE)
-			t_size = !(host->cmd_flags & REG_DATA)
-				 ? jmb38x_ms_write_data(host, buf, p_cnt)
-				 : jmb38x_ms_write_reg_data(host, buf, p_cnt);
-		else
-			t_size = !(host->cmd_flags & REG_DATA)
-				 ? jmb38x_ms_read_data(host, buf, p_cnt)
-				 : jmb38x_ms_read_reg_data(host, buf, p_cnt);
+		len = host->req->data_dir == WRITE ?
+			j38ms_write_fifo_pio(host, buf, len) :
+			j38ms_read_fifo_pio(host, buf, len);
 
-		if (host->req->long_data) {
-			kunmap_atomic(buf - p_off, KM_BIO_SRC_IRQ);
-			local_irq_restore(flags);
-		}
+		if (host->req->long_data)
+			host->pio_sg_iter.consumed = len;
+		else
+			host->pio_offset += len;
 
-		if (!t_size)
-			break;
-		host->block_pos += t_size;
-		length -= t_size;
-		off += t_size;
+		dbg(host, "PIO: transfered %d bytes", len);
+		if (!len)
+			goto exit;
 	}
 
-	if (!length && host->req->data_dir == WRITE) {
-		if (host->cmd_flags & REG_DATA) {
-			writel(host->io_word[0], host->addr + TPC_P0);
-			writel(host->io_word[1], host->addr + TPC_P1);
-		} else if (host->io_pos) {
-			writel(host->io_word[0], host->addr + DATA);
-		}
+	/* Write last non-complete dword of the data */
+	if (host->req->data_dir == WRITE && host->pio_tmp_buf_len) {
+		if (!j38ms_write_fifo_dword_pio(host,
+						*(u32 *)host->pio_tmp_buf))
+			host->pio_tmp_buf_len = 0;
 	}
+exit:
+	if (host->req->long_data) {
+		sg_miter_stop(&host->pio_sg_iter);
+		local_irq_restore(flags);
+	}
+}
+
+/* Read short TPC data (up to 8 bytes) via 2 special registers*/
+static void j38ms_read_tpc_inline(struct j38ms_host *host)
+{
+       *(u32 *)(host->req->data + 0) = j38ms_read_reg_raw(host, TPC_P0);
+       *(u32 *)(host->req->data + 4) = j38ms_read_reg_raw(host, TPC_P1);
+}
 
-	return length;
+/* Write short TPC data (up to 8 bytes) through 2 special registers */
+static void j38ms_write_tpc_inline(struct j38ms_host *host)
+{
+	j38ms_write_reg_raw(host, TPC_P0, *(u32 *)(host->req->data + 0));
+	j38ms_write_reg_raw(host, TPC_P1, *(u32 *)(host->req->data + 4));
 }
 
-static int jmb38x_ms_issue_cmd(struct memstick_host *msh)
+/*
+ * Start execution of a TPC:
+ * NOTES:
+ *
+ * PIO writes trigger wierd hardware bug that causes DMA writes
+ *  to fail.
+ *
+ * length alignmemt:
+ *	Short (<=8) TPC don't have any alignment problems.
+ *	DMA read/writes must be 4 aligned
+ *	PIO _reads_ must be aligned. Writes can be not aligned
+ *
+ */
+
+static int j38ms_execute_tpc(struct j38ms_host *host)
 {
-	struct jmb38x_ms_host *host = memstick_priv(msh);
-	unsigned char *data;
-	unsigned int data_len, cmd, t_val;
+	u32 cmd = 0, t_val;
+	unsigned int data_len = host->req->long_data ?
+		host->req->sg.length : host->req->data_len;
+	bool is_read = host->req->data_dir == READ;
+	dma_addr_t dma_address;
 
-	if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) {
-		dev_dbg(&msh->dev, "no media status\n");
+	if (host->dead) {
 		host->req->error = -ETIME;
+		return 0;
+	}
+
+	if (!(j38ms_read_reg(host, STATUS) & STATUS_HAS_MEDIA)) {
+		dbg(host, "IO: card removed, refusing to send TPC");
+		host->req->error = -ENODEV;
 		return host->req->error;
 	}
 
-	dev_dbg(&msh->dev, "control %08x\n", readl(host->addr + HOST_CONTROL));
-	dev_dbg(&msh->dev, "status %08x\n", readl(host->addr + INT_STATUS));
-	dev_dbg(&msh->dev, "hstatus %08x\n", readl(host->addr + STATUS));
+	if (data_len > BLOCK_SIZE_MASK) {
+		dbg(host, "IO: too long TPC (len: %d)", data_len);
+		host->req->error = -ENOSYS;
+		return host->req->error;
+	}
 
-	host->cmd_flags = 0;
-	host->block_pos = 0;
-	host->io_pos = 0;
-	host->io_word[0] = 0;
-	host->io_word[1] = 0;
+	dbg(host, "IO: Start execution of %s",
+		memstick_debug_get_tpc_name(host->req->tpc));
 
-	cmd = host->req->tpc << 16;
-	cmd |= TPC_DATA_SEL;
 
-	if (host->req->data_dir == READ)
-		cmd |= TPC_DIR;
-	if (host->req->need_card_int)
-		cmd |= TPC_WAIT_INT;
+	dbg_v(host, "host control: %08x", j38ms_read_reg(host, HOST_CONTROL));
+	dbg_v(host, "int status: %08x", j38ms_read_reg(host, INT_STATUS));
+	dbg_v(host, "card status: %08x", j38ms_read_reg(host, STATUS));
 
-	data = host->req->data;
+	host->req->error = 0;
+	host->cmd_flags = 0;
 
-	if (!no_dma)
-		host->cmd_flags |= DMA_DATA;
+	cmd = host->req->tpc << 16;
+	if (is_read)
+		cmd |= TPC_DIR;
 
-	if (host->req->long_data) {
-		data_len = host->req->sg.length;
-	} else {
-		data_len = host->req->data_len;
-		host->cmd_flags &= ~DMA_DATA;
+	if (host->req->need_card_int) {
+		dbg_v(host, "IO: Will wait for card interrupt");
+		cmd |= TPC_WAIT_INT;
+		/* No, the TPC_GET_INT doesn't work.... */
 	}
 
-	if (data_len <= 8) {
-		cmd &= ~(TPC_DATA_SEL | 0xf);
+	/* Special case for short TPCs */
+	if (!host->req->long_data && data_len <= 8) {
+		dbg(host, "IO: Using 8 byte register window");
 		host->cmd_flags |= REG_DATA;
 		cmd |= data_len & 0xf;
-		host->cmd_flags &= ~DMA_DATA;
+
+		if (!is_read)
+			j38ms_write_tpc_inline(host);
+		goto exec;
 	}
 
-	if (host->cmd_flags & DMA_DATA) {
-		if (1 != pci_map_sg(host->chip->pdev, &host->req->sg, 1,
-				    host->req->data_dir == READ
-				    ? PCI_DMA_FROMDEVICE
-				    : PCI_DMA_TODEVICE)) {
-			host->req->error = -ENOMEM;
-			return host->req->error;
+	/* Otherwise use internal fifo*/
+	cmd |= TPC_DATA_SEL;
+
+	if (data_len & 0x03) {
+		dbg(host, "Hardware doesn't support not-aligned len TPCs!");
+		host->req->error = -ENOSYS;
+		return host->req->error;
+	}
+
+	/* DMA */
+	if (!no_dma) {
+
+		dbg(host, "IO: Using DMA");
+		host->cmd_flags |= DMA_DATA;
+
+		if (host->req->long_data) {
+
+			if (pci_map_sg(host->chip->pdev,
+				&host->req->sg, 1, is_read
+				? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE) != 1) {
+
+				dbg(host, "IO: DMA map failed");
+				host->req->error = -ENOMEM;
+				return host->req->error;
+			}
+
+			/* We really shouldn't pretend we support that case */
+			if (sg_dma_len(&host->req->sg) != data_len) {
+				dbg(host, "IO: DMA len mismatch");
+				host->req->error = -EFAULT;
+				return host->req->error;
+			}
+
+			dma_address = sg_dma_address(&host->req->sg);
+
+		} else {
+			if (!is_read)
+				memcpy(host->dma_bounce_page,
+					host->req->data, data_len);
+			dma_address = host->dma_bus_address;
 		}
-		data_len = sg_dma_len(&host->req->sg);
-		writel(sg_dma_address(&host->req->sg),
-		       host->addr + DMA_ADDRESS);
-		writel(((1 << 16) & BLOCK_COUNT_MASK)
-		       | (data_len & BLOCK_SIZE_MASK),
-		       host->addr + BLOCK);
-		writel(DMA_CONTROL_ENABLE, host->addr + DMA_CONTROL);
-	} else if (!(host->cmd_flags & REG_DATA)) {
-		writel(((1 << 16) & BLOCK_COUNT_MASK)
-		       | (data_len & BLOCK_SIZE_MASK),
-		       host->addr + BLOCK);
-			t_val = readl(host->addr + INT_STATUS_ENABLE);
-			t_val |= host->req->data_dir == READ
-				 ? INT_STATUS_FIFO_RRDY
-				 : INT_STATUS_FIFO_WRDY;
-
-			writel(t_val, host->addr + INT_STATUS_ENABLE);
-			writel(t_val, host->addr + INT_SIGNAL_ENABLE);
+
+		j38ms_write_reg(host, BLOCK, data_len | BLOCK_COUNT_1BLOCK);
+		j38ms_write_reg(host, DMA_ADDRESS, dma_address);
+		j38ms_write_reg(host, DMA_CONTROL, DMA_CONTROL_ENABLE);
+	/* PIO */
 	} else {
-		cmd &= ~(TPC_DATA_SEL | 0xf);
-		host->cmd_flags |= REG_DATA;
-		cmd |= data_len & 0xf;
 
-		if (host->req->data_dir == WRITE) {
-			jmb38x_ms_transfer_data(host);
-			writel(host->io_word[0], host->addr + TPC_P0);
-			writel(host->io_word[1], host->addr + TPC_P1);
-		}
-	}
+		dbg(host, "IO: Using PIO");
+		host->cmd_flags |= PIO_DATA;
 
-	mod_timer(&host->timer, jiffies + host->timeout_jiffies);
-	writel(HOST_CONTROL_LED | readl(host->addr + HOST_CONTROL),
-	       host->addr + HOST_CONTROL);
-	host->req->error = 0;
+		host->pio_offset = 0;
+		host->pio_tmp_buf_len = 0;
+		memset(host->pio_tmp_buf, 0, 4);
+
+		if (host->req->long_data)
+			sg_miter_start(&host->pio_sg_iter,
+				&host->req->sg, 1, SG_MITER_ATOMIC |
+				(is_read ? SG_MITER_FROM_SG : SG_MITER_TO_SG));
 
-	writel(cmd, host->addr + TPC);
-	dev_dbg(&msh->dev, "executing TPC %08x, len %x\n", cmd, data_len);
+		/* Enable FIFO empty interrupts */
+		t_val = is_read ? INT_STATUS_FIFO_RRDY : INT_STATUS_FIFO_WRDY;
 
+		j38ms_write_reg(host, BLOCK, data_len | BLOCK_COUNT_1BLOCK);
+		j38ms_set_reg_mask(host, INT_SIGNAL_ENABLE, t_val);
+		j38ms_set_reg_mask(host, INT_STATUS_ENABLE, t_val);
+	}
+exec:
+	mod_timer(&host->timer, jiffies + host->timeout_jiffies);
+
+	/* Let the TPC fly... */
+	j38ms_write_reg(host, TPC, cmd);
+	j38ms_set_reg_mask(host, HOST_CONTROL, HOST_CONTROL_LED);
 	return 0;
 }
 
-static void jmb38x_ms_complete_cmd(struct memstick_host *msh, int last)
+/* Cleanups execution of current TPC */
+static void j38ms_complete_tpc(struct memstick_host *msh)
 {
-	struct jmb38x_ms_host *host = memstick_priv(msh);
-	unsigned int t_val = 0;
-	int rc;
-
+	struct j38ms_host *host = memstick_priv(msh);
 	del_timer(&host->timer);
 
-	dev_dbg(&msh->dev, "c control %08x\n",
-		readl(host->addr + HOST_CONTROL));
-	dev_dbg(&msh->dev, "c status %08x\n",
-		readl(host->addr + INT_STATUS));
-	dev_dbg(&msh->dev, "c hstatus %08x\n", readl(host->addr + STATUS));
+	dbg(host, "IO: TPC complete (error : %d)", host->req->error);
 
-	host->req->int_reg = readl(host->addr + STATUS) & 0xff;
+	j38ms_write_reg(host, BLOCK, 0);
+	j38ms_write_reg(host, DMA_CONTROL, 0);
 
-	writel(0, host->addr + BLOCK);
-	writel(0, host->addr + DMA_CONTROL);
+	dbg_v(host, "host control: %08x", j38ms_read_reg(host, HOST_CONTROL));
+	dbg_v(host, "int status: %08x", j38ms_read_reg(host, INT_STATUS));
+	dbg_v(host, "card status: %08x", j38ms_read_reg(host, STATUS));
+
+	if (host->req->need_card_int)
+		host->req->int_reg = j38ms_read_reg(host, STATUS) & 0xFF;
 
 	if (host->cmd_flags & DMA_DATA) {
-		pci_unmap_sg(host->chip->pdev, &host->req->sg, 1,
-			     host->req->data_dir == READ
-			     ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
-	} else {
-		t_val = readl(host->addr + INT_STATUS_ENABLE);
-		if (host->req->data_dir == READ)
-			t_val &= ~INT_STATUS_FIFO_RRDY;
-		else
-			t_val &= ~INT_STATUS_FIFO_WRDY;
+		if (host->req->long_data)
+			pci_unmap_sg(host->chip->pdev, &host->req->sg, 1,
+				host->req->data_dir == READ
+				? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+		else if (host->req->data_dir == READ)
+			memcpy(host->req->data,
+				host->dma_bounce_page, host->req->data_len);
 
-		writel(t_val, host->addr + INT_STATUS_ENABLE);
-		writel(t_val, host->addr + INT_SIGNAL_ENABLE);
-	}
+	} else if (host->cmd_flags & PIO_DATA) {
+		u32 t_val = INT_STATUS_FIFO_RRDY | INT_STATUS_FIFO_WRDY;
 
-	writel((~HOST_CONTROL_LED) & readl(host->addr + HOST_CONTROL),
-	       host->addr + HOST_CONTROL);
+		/* This is not known well */
+		j38ms_clear_reg_mask(host, INT_STATUS_ENABLE, t_val);
 
-	if (!last) {
-		do {
-			rc = memstick_next_req(msh, &host->req);
-		} while (!rc && jmb38x_ms_issue_cmd(msh));
-	} else {
-		do {
-			rc = memstick_next_req(msh, &host->req);
-			if (!rc)
-				host->req->error = -ETIME;
-		} while (!rc);
+		/* This disables the IRQ to host, really */
+		j38ms_clear_reg_mask(host, INT_SIGNAL_ENABLE, t_val);
 	}
+
+	j38ms_clear_reg_mask(host, HOST_CONTROL, HOST_CONTROL_LED);
 }
 
-static irqreturn_t jmb38x_ms_isr(int irq, void *dev_id)
+/* Interrupt handler */
+static irqreturn_t j38ms_isr(int irq, void *dev_id)
 {
 	struct memstick_host *msh = dev_id;
-	struct jmb38x_ms_host *host = memstick_priv(msh);
-	unsigned int irq_status;
+	struct j38ms_host *host = memstick_priv(msh);
+	u32 irq_status;
 
 	spin_lock(&host->lock);
-	irq_status = readl(host->addr + INT_STATUS);
-	dev_dbg(&host->chip->pdev->dev, "irq_status = %08x\n", irq_status);
+	irq_status = j38ms_read_reg(host, INT_STATUS);
+
 	if (irq_status == 0 || irq_status == (~0)) {
 		spin_unlock(&host->lock);
 		return IRQ_NONE;
 	}
 
-	if (host->req) {
-		if (irq_status & INT_STATUS_ANY_ERR) {
-			if (irq_status & INT_STATUS_CRC_ERR)
-				host->req->error = -EILSEQ;
-			else
-				host->req->error = -ETIME;
-		} else {
-			if (host->cmd_flags & DMA_DATA) {
-				if (irq_status & INT_STATUS_EOTRAN)
-					host->cmd_flags |= FIFO_READY;
-			} else {
-				if (irq_status & (INT_STATUS_FIFO_RRDY
-						  | INT_STATUS_FIFO_WRDY))
-					jmb38x_ms_transfer_data(host);
-
-				if (irq_status & INT_STATUS_EOTRAN) {
-					jmb38x_ms_transfer_data(host);
-					host->cmd_flags |= FIFO_READY;
-				}
-			}
+	dbg(host, "IRQ: status: %08x", irq_status);
 
-			if (irq_status & INT_STATUS_EOTPC) {
-				host->cmd_flags |= CMD_READY;
-				if (host->cmd_flags & REG_DATA) {
-					if (host->req->data_dir == READ) {
-						host->io_word[0]
-							= readl(host->addr
-								+ TPC_P0);
-						host->io_word[1]
-							= readl(host->addr
-								+ TPC_P1);
-						host->io_pos = 8;
-
-						jmb38x_ms_transfer_data(host);
-					}
-					host->cmd_flags |= FIFO_READY;
-				}
-			}
+	if (irq_status & INT_STATUS_ANY_ERR)
+		dbg(host, "IRQ: error");
+
+	if (irq_status & INT_STATUS_FIFO_RRDY)
+		dbg(host, "IRQ: FIFO is now ready for reading");
+
+	if (irq_status & INT_STATUS_FIFO_WRDY)
+		dbg(host, "IRQ: FIFO is now ready for writing");
+
+	if (irq_status & INT_STATUS_EOTRAN)
+		dbg(host, "IRQ: FIFO IO tranfer done");
+
+	if (irq_status & INT_STATUS_EOTPC)
+		dbg(host, "IRQ: TPC complete");
+
+	if (!host->req)
+		goto out;
+
+	/* Errors */
+	if (irq_status & INT_STATUS_ANY_ERR) {
+		host->req->error =
+			(irq_status & INT_STATUS_CRC_ERR) ? -EILSEQ : -EIO;
+		goto out;
+	}
+
+	/* End of TPC interrupt */
+	if (irq_status & INT_STATUS_EOTPC) {
+		host->cmd_flags |= CMD_READY;
+
+		if (host->cmd_flags & REG_DATA) {
+			if (host->req->data_dir == READ)
+				j38ms_read_tpc_inline(host);
+			host->cmd_flags |= FIFO_READY;
 		}
 	}
 
+	/* Fifo handling */
+	if (irq_status & INT_STATUS_EOTRAN)
+		host->cmd_flags |= FIFO_READY;
+	else if (irq_status & (INT_STATUS_FIFO_RRDY | INT_STATUS_FIFO_WRDY))
+		if (host->cmd_flags & PIO_DATA)
+			j38ms_transfer_pio(host);
+
+out:
 	if (irq_status & (INT_STATUS_MEDIA_IN | INT_STATUS_MEDIA_OUT)) {
-		dev_dbg(&host->chip->pdev->dev, "media changed\n");
+		dbg(host, "IRQ: media changed");
 		memstick_detect_change(msh);
 	}
 
-	writel(irq_status, host->addr + INT_STATUS);
+	/* Yes, interrupt status is cleared by setting the bits as usual */
+	j38ms_write_reg(host, INT_STATUS, irq_status);
 
-	if (host->req
-	    && (((host->cmd_flags & CMD_READY)
-		 && (host->cmd_flags & FIFO_READY))
-		|| host->req->error))
-		jmb38x_ms_complete_cmd(msh, 0);
+	if (host->req && (((host->cmd_flags & CMD_READY)
+		&& (host->cmd_flags & FIFO_READY)) || host->req->error)) {
+		j38ms_complete_tpc(msh);
+		tasklet_schedule(&host->tasklet);
+	}
 
 	spin_unlock(&host->lock);
 	return IRQ_HANDLED;
 }
 
-static void jmb38x_ms_abort(unsigned long data)
+/* Timer that is executed in absense of the interrupt */
+static void j38ms_irq_timeout(unsigned long data)
 {
-	struct memstick_host *msh = (struct memstick_host *)data;
-	struct jmb38x_ms_host *host = memstick_priv(msh);
+	struct j38ms_host *host = (struct j38ms_host *)data;
 	unsigned long flags;
 
-	dev_dbg(&host->chip->pdev->dev, "abort\n");
+	dbg(host, "interrupt timeout");
+
 	spin_lock_irqsave(&host->lock, flags);
 	if (host->req) {
 		host->req->error = -ETIME;
-		jmb38x_ms_complete_cmd(msh, 0);
+		j38ms_complete_tpc(host->msh);
+		tasklet_schedule(&host->tasklet);
 	}
+
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void jmb38x_ms_req_tasklet(unsigned long data)
+static void j38ms_submit_tasklet(unsigned long data)
 {
-	struct memstick_host *msh = (struct memstick_host *)data;
-	struct jmb38x_ms_host *host = memstick_priv(msh);
+	struct j38ms_host *host = (struct j38ms_host *)data;
 	unsigned long flags;
-	int rc;
 
 	spin_lock_irqsave(&host->lock, flags);
-	if (!host->req) {
-		do {
-			rc = memstick_next_req(msh, &host->req);
-			dev_dbg(&host->chip->pdev->dev, "tasklet req %d\n", rc);
-		} while (!rc && jmb38x_ms_issue_cmd(msh));
-	}
+	j38ms_next_request(host);
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void jmb38x_ms_dummy_submit(struct memstick_host *msh)
+/* Asks for next request from upper layer and starts its execution */
+static void j38ms_next_request(struct j38ms_host *host)
 {
-	return;
+	while (!memstick_next_req(host->msh, &host->req))
+		if (!j38ms_execute_tpc(host))
+			return;
 }
 
-static void jmb38x_ms_submit_req(struct memstick_host *msh)
+/* hardware reset */
+static int j38ms_reset(struct j38ms_host *host, u32 reset_bit)
 {
-	struct jmb38x_ms_host *host = memstick_priv(msh);
+	int i;
+	j38ms_set_reg_mask(host,
+			HOST_CONTROL, reset_bit | HOST_CONTROL_CLOCK_EN);
 
-	tasklet_schedule(&host->notify);
+	for (i = 0; i < 20; ++i) {
+		if (!(j38ms_read_reg(host, HOST_CONTROL) & reset_bit))
+			break;
+		ndelay(20);
+	}
+	return (j38ms_read_reg(host, HOST_CONTROL) & reset_bit) ? EIO : 0;
 }
 
-static int jmb38x_ms_reset(struct jmb38x_ms_host *host)
+/* Enable/disable the device */
+static int j38ms_power_device(struct j38ms_host *host, bool enable)
 {
-	int cnt;
+	int error;
 
-	writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN
-	       | readl(host->addr + HOST_CONTROL),
-	       host->addr + HOST_CONTROL);
-	mmiowb();
+	if (enable) {
 
-	for (cnt = 0; cnt < 20; ++cnt) {
-		if (!(HOST_CONTROL_RESET_REQ
-		      & readl(host->addr + HOST_CONTROL)))
-			goto reset_next;
+		error = j38ms_reset(host, HOST_CONTROL_RESET_REQ);
+		error = j38ms_reset(host, HOST_CONTROL_RESET);
 
-		ndelay(20);
-	}
-	dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n");
-	/* return -EIO; */
+		if (error)
+			return error;
 
-reset_next:
-	writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN
-	       | readl(host->addr + HOST_CONTROL),
-	       host->addr + HOST_CONTROL);
-	mmiowb();
+		j38ms_write_reg(host, INT_SIGNAL_ENABLE, INT_STATUS_ALL);
+		j38ms_write_reg(host, INT_STATUS_ENABLE, INT_STATUS_ALL);
+		j38ms_write_reg(host, CLOCK_CONTROL, CLOCK_CONTROL_RESET);
 
-	for (cnt = 0; cnt < 20; ++cnt) {
-		if (!(HOST_CONTROL_RESET
-		      & readl(host->addr + HOST_CONTROL)))
-			goto reset_ok;
+		j38ms_write_reg(host, HOST_CONTROL,
+			HOST_CONTROL_POWER_EN |
+			HOST_CONTROL_CLOCK_EN |
+			HOST_CONTROL_HW_OC_P |
+			HOST_CONTROL_TDELAY_EN |
+			HOST_CONTROL_BSY_TIME);
+
+		j38ms_write_reg(host, PAD_PU_PD, host->id ?
+			PAD_PU_PD_ON_MS_SOCK1 : PAD_PU_PD_ON_MS_SOCK0);
+
+		j38ms_write_reg(host, PAD_OUTPUT_ENABLE, PAD_OUTPUT_ENABLE_MS);
+
+	} else {
+		j38ms_clear_reg_mask(host, HOST_CONTROL,
+			HOST_CONTROL_POWER_EN | HOST_CONTROL_CLOCK_EN);
+		j38ms_write_reg(host, PAD_OUTPUT_ENABLE, 0);
+		j38ms_write_reg(host, PAD_PU_PD, PAD_PU_PD_OFF);
+		j38ms_write_reg(host, CLOCK_CONTROL, CLOCK_CONTROL_OFF);
 
-		ndelay(20);
 	}
-	dev_dbg(&host->chip->pdev->dev, "reset timeout\n");
-	return -EIO;
+	msleep(100);
+	return 0;
+}
+
+/* Switch interface */
+static int j38ms_set_interface(struct j38ms_host *host, int interface)
+{
+	u32 host_ctl = j38ms_read_reg(host, HOST_CONTROL);
+	u32 clock_ctl = CLOCK_CONTROL_40MHZ;
+	u32 clock_delay;
+
+	host_ctl &= ~HOST_CONTROL_IF_MASK;
+	pci_read_config_dword(host->chip->pdev, PCI_CTL_CLOCK_DLY_ADDR,
+				&clock_delay);
+
+	clock_delay &= host->id ? ~PCI_CTL_CLOCK_DLY_MASK_B
+				: ~PCI_CTL_CLOCK_DLY_MASK_A;
+
+	if (interface == MEMSTICK_SERIAL) {
+
+		host_ctl |= HOST_CONTROL_IF_SERIAL;
+		clock_ctl = CLOCK_CONTROL_40MHZ;
+
+		host_ctl &= ~HOST_CONTROL_FAST_CLK;
+		host_ctl &= ~HOST_CONTROL_REO;
+		host_ctl |= HOST_CONTROL_REI;
+
+	} else if (interface == MEMSTICK_PAR4) {
+
+		host_ctl |= HOST_CONTROL_IF_PAR4;
+		clock_ctl = CLOCK_CONTROL_40MHZ;
+
+		host_ctl |= HOST_CONTROL_FAST_CLK;
+		host_ctl |= HOST_CONTROL_REO;
+		host_ctl &= ~HOST_CONTROL_REI;
+
+		clock_delay |= host->id ? (4 << 12) : (4 << 8);
+
+	} else if (interface == MEMSTICK_PAR8) {
+
+		host_ctl |= HOST_CONTROL_IF_PAR8;
+		clock_ctl = CLOCK_CONTROL_50MHZ;
+
+		host_ctl |= HOST_CONTROL_FAST_CLK;
+		host_ctl &= ~HOST_CONTROL_REO;
+		host_ctl &= ~HOST_CONTROL_REI;
+	} else
+		return -EINVAL;
 
-reset_ok:
-	mmiowb();
-	writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE);
-	writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE);
+	j38ms_write_reg(host, HOST_CONTROL, host_ctl);
+	j38ms_write_reg(host, CLOCK_CONTROL, clock_ctl);
+
+	pci_write_config_dword(host->chip->pdev,
+		PCI_CTL_CLOCK_DLY_ADDR, clock_delay);
+
+	host->interface = interface;
 	return 0;
 }
 
-static int jmb38x_ms_set_param(struct memstick_host *msh,
+/* external interface: control hardware settings */
+static int j38ms_set_param(struct memstick_host *msh,
 			       enum memstick_param param,
 			       int value)
 {
-	struct jmb38x_ms_host *host = memstick_priv(msh);
-	unsigned int host_ctl = readl(host->addr + HOST_CONTROL);
-	unsigned int clock_ctl = CLOCK_CONTROL_40MHZ, clock_delay = 0;
-	int rc = 0;
+	struct j38ms_host *host = memstick_priv(msh);
 
 	switch (param) {
 	case MEMSTICK_POWER:
-		if (value == MEMSTICK_POWER_ON) {
-			rc = jmb38x_ms_reset(host);
-			if (rc)
-				return rc;
-
-			host_ctl = 7;
-			host_ctl |= HOST_CONTROL_POWER_EN
-				    | HOST_CONTROL_CLOCK_EN
-				    | HOST_CONTROL_HW_OC_P
-				    | HOST_CONTROL_TDELAY_EN;
-			writel(host_ctl, host->addr + HOST_CONTROL);
-
-			writel(host->id ? PAD_PU_PD_ON_MS_SOCK1
-					: PAD_PU_PD_ON_MS_SOCK0,
-			       host->addr + PAD_PU_PD);
-
-			writel(PAD_OUTPUT_ENABLE_MS,
-			       host->addr + PAD_OUTPUT_ENABLE);
-
-			msleep(10);
-			dev_dbg(&host->chip->pdev->dev, "power on\n");
-		} else if (value == MEMSTICK_POWER_OFF) {
-			host_ctl &= ~(HOST_CONTROL_POWER_EN
-				      | HOST_CONTROL_CLOCK_EN);
-			writel(host_ctl, host->addr +  HOST_CONTROL);
-			writel(0, host->addr + PAD_OUTPUT_ENABLE);
-			writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD);
-			dev_dbg(&host->chip->pdev->dev, "power off\n");
-		} else
-			return -EINVAL;
-		break;
+		return j38ms_power_device(host, (value == MEMSTICK_POWER_ON));
 	case MEMSTICK_INTERFACE:
-		host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT);
-		pci_read_config_dword(host->chip->pdev,
-				      PCI_CTL_CLOCK_DLY_ADDR,
-				      &clock_delay);
-		clock_delay &= host->id ? ~PCI_CTL_CLOCK_DLY_MASK_B
-					: ~PCI_CTL_CLOCK_DLY_MASK_A;
-
-		if (value == MEMSTICK_SERIAL) {
-			host_ctl &= ~HOST_CONTROL_FAST_CLK;
-			host_ctl &= ~HOST_CONTROL_REO;
-			host_ctl |= HOST_CONTROL_IF_SERIAL
-				    << HOST_CONTROL_IF_SHIFT;
-			host_ctl |= HOST_CONTROL_REI;
-			clock_ctl = CLOCK_CONTROL_40MHZ;
-		} else if (value == MEMSTICK_PAR4) {
-			host_ctl |= HOST_CONTROL_FAST_CLK | HOST_CONTROL_REO;
-			host_ctl |= HOST_CONTROL_IF_PAR4
-				    << HOST_CONTROL_IF_SHIFT;
-			host_ctl &= ~HOST_CONTROL_REI;
-			clock_ctl = CLOCK_CONTROL_40MHZ;
-			clock_delay |= host->id ? (4 << 12) : (4 << 8);
-		} else if (value == MEMSTICK_PAR8) {
-			host_ctl |= HOST_CONTROL_FAST_CLK;
-			host_ctl |= HOST_CONTROL_IF_PAR8
-				    << HOST_CONTROL_IF_SHIFT;
-			host_ctl &= ~(HOST_CONTROL_REI | HOST_CONTROL_REO);
-			clock_ctl = CLOCK_CONTROL_50MHZ;
-		} else
-			return -EINVAL;
-
-		writel(host_ctl, host->addr + HOST_CONTROL);
-		writel(clock_ctl, host->addr + CLOCK_CONTROL);
-		pci_write_config_dword(host->chip->pdev,
-				       PCI_CTL_CLOCK_DLY_ADDR,
-				       clock_delay);
-		break;
+		return j38ms_set_interface(host, value);
+	default:
+		return -EINVAL;
 	};
-	return 0;
+}
+
+/* external interface: Submit new request from upper layer */
+static void j38ms_submit_req(struct memstick_host *msh)
+{
+	struct j38ms_host *host = memstick_priv(msh);
+	if (!host->req)
+		tasklet_schedule(&host->tasklet);
 }
 
 #ifdef CONFIG_PM
 
-static int jmb38x_ms_suspend(struct pci_dev *dev, pm_message_t state)
+static int j38ms_suspend(struct pci_dev *dev, pm_message_t state)
 {
-	struct jmb38x_ms *jm = pci_get_drvdata(dev);
+	struct j38ms *jm = pci_get_drvdata(dev);
 	int cnt;
 
 	for (cnt = 0; cnt < jm->host_cnt; ++cnt) {
@@ -772,9 +699,9 @@ static int jmb38x_ms_suspend(struct pci_dev *dev, pm_message_t state)
 	return 0;
 }
 
-static int jmb38x_ms_resume(struct pci_dev *dev)
+static int j38ms_resume(struct pci_dev *dev)
 {
-	struct jmb38x_ms *jm = pci_get_drvdata(dev);
+	struct j38ms *jm = pci_get_drvdata(dev);
 	int rc;
 
 	pci_set_power_state(dev, PCI_D0);
@@ -799,12 +726,12 @@ static int jmb38x_ms_resume(struct pci_dev *dev)
 
 #else
 
-#define jmb38x_ms_suspend NULL
-#define jmb38x_ms_resume NULL
+#define j38ms_suspend NULL
+#define j38ms_resume NULL
 
 #endif /* CONFIG_PM */
 
-static int jmb38x_ms_count_slots(struct pci_dev *pdev)
+static int j38ms_count_slots(struct pci_dev *pdev)
 {
 	int cnt, rc = 0;
 
@@ -814,23 +741,23 @@ static int jmb38x_ms_count_slots(struct pci_dev *pdev)
 
 		if (256 != pci_resource_len(pdev, cnt))
 			break;
-
 		++rc;
 	}
 	return rc;
 }
 
-static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt)
+static struct memstick_host *j38ms_alloc_host(struct j38ms *jm, int cnt)
 {
 	struct memstick_host *msh;
-	struct jmb38x_ms_host *host;
+	struct j38ms_host *host;
 
-	msh = memstick_alloc_host(sizeof(struct jmb38x_ms_host),
+	msh = memstick_alloc_host(sizeof(struct j38ms_host),
 				  &jm->pdev->dev);
 	if (!msh)
 		return NULL;
 
 	host = memstick_priv(msh);
+	host->msh = msh;
 	host->chip = jm;
 	host->addr = ioremap(pci_resource_start(jm->pdev, cnt),
 			     pci_resource_len(jm->pdev, cnt));
@@ -842,39 +769,53 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt)
 	snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d",
 		 host->id);
 	host->irq = jm->pdev->irq;
-	host->timeout_jiffies = msecs_to_jiffies(1000);
 
-	tasklet_init(&host->notify, jmb38x_ms_req_tasklet, (unsigned long)msh);
-	msh->request = jmb38x_ms_submit_req;
-	msh->set_param = jmb38x_ms_set_param;
+	msh->request = j38ms_submit_req;
+	msh->set_param = j38ms_set_param;
 
 	msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8;
 
-	setup_timer(&host->timer, jmb38x_ms_abort, (unsigned long)msh);
+	host->timeout_jiffies = msecs_to_jiffies(1000);
+	setup_timer(&host->timer, j38ms_irq_timeout, (unsigned long)host);
+	tasklet_init(&host->tasklet, j38ms_submit_tasklet, (unsigned long)host);
+
+	host->dma_bounce_page = pci_alloc_consistent(
+				jm->pdev, PAGE_SIZE, &host->dma_bus_address);
 
-	if (!request_irq(host->irq, jmb38x_ms_isr, IRQF_SHARED, host->host_id,
+	if (!host->dma_bounce_page)
+		goto err_out_free;
+
+	if (!request_irq(host->irq, j38ms_isr, IRQF_SHARED, host->host_id,
 			 msh))
 		return msh;
 
 	iounmap(host->addr);
+
 err_out_free:
+	if (host->dma_bounce_page)
+		pci_free_consistent(jm->pdev, PAGE_SIZE,
+			host->dma_bounce_page, host->dma_bus_address);
 	kfree(msh);
 	return NULL;
 }
 
-static void jmb38x_ms_free_host(struct memstick_host *msh)
+static void j38ms_free_host(struct memstick_host *msh)
 {
-	struct jmb38x_ms_host *host = memstick_priv(msh);
+	struct j38ms_host *host = memstick_priv(msh);
 
 	free_irq(host->irq, msh);
 	iounmap(host->addr);
+
+	pci_free_consistent(host->chip->pdev, PAGE_SIZE,
+		host->dma_bounce_page, host->dma_bus_address);
+
 	memstick_free_host(msh);
 }
 
-static int jmb38x_ms_probe(struct pci_dev *pdev,
+static int j38ms_probe(struct pci_dev *pdev,
 			   const struct pci_device_id *dev_id)
 {
-	struct jmb38x_ms *jm;
+	struct j38ms *jm;
 	int pci_dev_busy = 0;
 	int rc, cnt;
 
@@ -897,14 +838,14 @@ static int jmb38x_ms_probe(struct pci_dev *pdev,
 	pci_read_config_dword(pdev, 0xac, &rc);
 	pci_write_config_dword(pdev, 0xac, rc | 0x00470000);
 
-	cnt = jmb38x_ms_count_slots(pdev);
+	cnt = j38ms_count_slots(pdev);
 	if (!cnt) {
 		rc = -ENODEV;
 		pci_dev_busy = 1;
 		goto err_out;
 	}
 
-	jm = kzalloc(sizeof(struct jmb38x_ms)
+	jm = kzalloc(sizeof(struct j38ms)
 		     + cnt * sizeof(struct memstick_host *), GFP_KERNEL);
 	if (!jm) {
 		rc = -ENOMEM;
@@ -916,14 +857,14 @@ static int jmb38x_ms_probe(struct pci_dev *pdev,
 	pci_set_drvdata(pdev, jm);
 
 	for (cnt = 0; cnt < jm->host_cnt; ++cnt) {
-		jm->hosts[cnt] = jmb38x_ms_alloc_host(jm, cnt);
+		jm->hosts[cnt] = j38ms_alloc_host(jm, cnt);
 		if (!jm->hosts[cnt])
 			break;
 
 		rc = memstick_add_host(jm->hosts[cnt]);
 
 		if (rc) {
-			jmb38x_ms_free_host(jm->hosts[cnt]);
+			j38ms_free_host(jm->hosts[cnt]);
 			jm->hosts[cnt] = NULL;
 			break;
 		}
@@ -944,10 +885,10 @@ err_out:
 	return rc;
 }
 
-static void jmb38x_ms_remove(struct pci_dev *dev)
+static void j38ms_remove(struct pci_dev *dev)
 {
-	struct jmb38x_ms *jm = pci_get_drvdata(dev);
-	struct jmb38x_ms_host *host;
+	struct j38ms *jm = pci_get_drvdata(dev);
+	struct j38ms_host *host;
 	int cnt;
 	unsigned long flags;
 
@@ -957,23 +898,28 @@ static void jmb38x_ms_remove(struct pci_dev *dev)
 
 		host = memstick_priv(jm->hosts[cnt]);
 
-		jm->hosts[cnt]->request = jmb38x_ms_dummy_submit;
-		tasklet_kill(&host->notify);
-		writel(0, host->addr + INT_SIGNAL_ENABLE);
-		writel(0, host->addr + INT_STATUS_ENABLE);
-		mmiowb();
-		dev_dbg(&jm->pdev->dev, "interrupts off\n");
+		tasklet_kill(&host->tasklet);
+
 		spin_lock_irqsave(&host->lock, flags);
+		host->dead = true;
+
+		j38ms_write_reg(host, INT_SIGNAL_ENABLE, 0);
+		j38ms_write_reg(host, INT_STATUS_ENABLE, 0);
+
+		mmiowb();
+		dbg(host, "interrupts off");
+
 		if (host->req) {
 			host->req->error = -ETIME;
-			jmb38x_ms_complete_cmd(jm->hosts[cnt], 1);
+			j38ms_complete_tpc(jm->hosts[cnt]);
 		}
+
 		spin_unlock_irqrestore(&host->lock, flags);
 
 		memstick_remove_host(jm->hosts[cnt]);
-		dev_dbg(&jm->pdev->dev, "host removed\n");
+		dbg(host, "host removed");
 
-		jmb38x_ms_free_host(jm->hosts[cnt]);
+		j38ms_free_host(jm->hosts[cnt]);
 	}
 
 	pci_set_drvdata(dev, NULL);
@@ -982,35 +928,41 @@ static void jmb38x_ms_remove(struct pci_dev *dev)
 	kfree(jm);
 }
 
-static struct pci_device_id jmb38x_ms_id_tbl [] = {
+static struct pci_device_id j38ms_id_tbl[] = {
 	{ PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_MS, PCI_ANY_ID,
 	  PCI_ANY_ID, 0, 0, 0 },
 	{ }
 };
 
-static struct pci_driver jmb38x_ms_driver = {
+static struct pci_driver j38ms_driver = {
 	.name = DRIVER_NAME,
-	.id_table = jmb38x_ms_id_tbl,
-	.probe = jmb38x_ms_probe,
-	.remove = jmb38x_ms_remove,
-	.suspend = jmb38x_ms_suspend,
-	.resume = jmb38x_ms_resume
+	.id_table = j38ms_id_tbl,
+	.probe = j38ms_probe,
+	.remove = j38ms_remove,
+	.suspend = j38ms_suspend,
+	.resume = j38ms_resume
 };
 
-static int __init jmb38x_ms_init(void)
+static int __init j38ms_init(void)
 {
-	return pci_register_driver(&jmb38x_ms_driver);
+	return pci_register_driver(&j38ms_driver);
 }
 
-static void __exit jmb38x_ms_exit(void)
+static void __exit j38ms_exit(void)
 {
-	pci_unregister_driver(&jmb38x_ms_driver);
+	pci_unregister_driver(&j38ms_driver);
 }
 
 MODULE_AUTHOR("Alex Dubov");
-MODULE_DESCRIPTION("JMicron jmb38x MemoryStick driver");
+MODULE_DESCRIPTION("JMicron jm38x_ms MemoryStick driver");
 MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(pci, jmb38x_ms_id_tbl);
+MODULE_DEVICE_TABLE(pci, j38ms_id_tbl);
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level (0-3)");
+
+module_param(no_dma, bool, 0644);
+MODULE_PARM_DESC(no_dma, "Disable the dma");
 
-module_init(jmb38x_ms_init);
-module_exit(jmb38x_ms_exit);
+module_init(j38ms_init);
+module_exit(j38ms_exit);
diff --git a/drivers/memstick/host/jmb38x_ms.h b/drivers/memstick/host/jmb38x_ms.h
new file mode 100644
index 0000000..0d83365
--- /dev/null
+++ b/drivers/memstick/host/jmb38x_ms.h
@@ -0,0 +1,200 @@
+/*
+ *  jmb38x_ms.c - JMicron jmb38x MemoryStick card reader
+ *
+ *  Copyright (C) 2008 Alex Dubov <oakad@yahoo.com>
+ *  Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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.
+ *
+ */
+
+#define DRIVER_NAME "jmb38x_ms"
+
+/* physical address of a page for DMA */
+#define	DMA_ADDRESS            0x00
+
+/* controls the size of the TPC */
+#define	BLOCK                  0x04
+#define BLOCK_COUNT_MASK        0xffff0000
+#define BLOCK_COUNT_1BLOCK      0x00010000
+#define BLOCK_SIZE_MASK         0x00000fff
+
+/* Enables the DMA */
+#define	DMA_CONTROL            0x08
+#define DMA_CONTROL_ENABLE      0x00000001
+
+/* A window for small TPCs to send the contents inline */
+#define	TPC_P0                 0x0c
+#define	TPC_P1                 0x10
+
+/* TPC execution register */
+#define	TPC                    0x14
+#define TPC_DATA_SEL            0x00008000
+#define TPC_DIR                 0x00004000
+#define TPC_WAIT_INT            0x00002000
+#define TPC_GET_INT             0x00000800
+#define TPC_CODE_SZ_MASK        0x00000700
+#define TPC_DATA_SZ_MASK        0x00000007
+
+/* General device settings */
+#define	HOST_CONTROL           0x18
+#define HOST_CONTROL_TDELAY_EN  0x00040000
+#define HOST_CONTROL_HW_OC_P    0x00010000
+#define HOST_CONTROL_RESET_REQ  0x00008000
+#define HOST_CONTROL_REI        0x00004000
+#define HOST_CONTROL_LED        0x00000400
+#define HOST_CONTROL_FAST_CLK   0x00000200
+#define HOST_CONTROL_RESET      0x00000100
+#define HOST_CONTROL_POWER_EN   0x00000080
+#define HOST_CONTROL_IF_PAR4    0x00000010
+#define HOST_CONTROL_IF_PAR8    0x00000030
+#define HOST_CONTROL_IF_MASK    0x00000030
+#define HOST_CONTROL_CLOCK_EN   0x00000040
+#define HOST_CONTROL_REO        0x00000008
+#define HOST_CONTROL_BSY_TIME	0x00000007
+#define HOST_CONTROL_IF_SERIAL  0x00000000
+
+/* IO window for PIO access to internal FIFO*/
+#define	DATA                   0x1c
+
+/* MS status */
+#define	STATUS                 0x20
+#define STATUS_BUSY             0x00080000
+#define STATUS_MS_DAT7          0x00040000
+#define STATUS_MS_DAT6          0x00020000
+#define STATUS_MS_DAT5          0x00010000
+#define STATUS_MS_DAT4          0x00008000
+#define STATUS_MS_DAT3          0x00004000
+#define STATUS_MS_DAT2          0x00002000
+#define STATUS_MS_DAT1          0x00001000
+#define STATUS_MS_DAT0          0x00000800
+#define STATUS_HAS_MEDIA        0x00000400
+#define STATUS_FIFO_EMPTY       0x00000200
+#define STATUS_FIFO_FULL        0x00000100
+#define STATUS_MS_CED           0x00000080
+#define STATUS_MS_ERR           0x00000040
+#define STATUS_MS_BRQ           0x00000020
+#define STATUS_MS_CNK           0x00000001
+
+/* Device status */
+#define	INT_STATUS             0x24
+#define INT_STATUS_TPC_ERR      0x00080000
+#define INT_STATUS_CRC_ERR      0x00040000
+#define INT_STATUS_TIMER_TO     0x00020000
+#define INT_STATUS_HSK_TO       0x00010000
+#define INT_STATUS_ANY_ERR      0x00008000
+#define INT_STATUS_FIFO_WRDY    0x00000080
+#define INT_STATUS_FIFO_RRDY    0x00000040
+#define INT_STATUS_MEDIA_OUT    0x00000010
+#define INT_STATUS_MEDIA_IN     0x00000008
+#define INT_STATUS_DMA_BOUNDARY 0x00000004
+#define INT_STATUS_EOTRAN       0x00000002
+#define INT_STATUS_EOTPC        0x00000001
+#define INT_STATUS_ALL          0x000f801f
+
+/* Interrupt enable - ???*/
+#define	INT_STATUS_ENABLE      0x28
+
+/* Interrupt enable*/
+#define	INT_SIGNAL_ENABLE      0x2c
+
+/* Current timer value */
+#define	TIMER                  0x30
+
+/* Enable/disable the timer */
+#define	TIMER_CONTROL          0x34
+
+/* Output pad enable */
+#define	PAD_OUTPUT_ENABLE      0x38
+#define PAD_OUTPUT_ENABLE_MS    0x0F3F
+
+/* Ouput pad pull-up, pull-down control */
+#define	PAD_PU_PD              0x3c
+#define PAD_PU_PD_OFF           0x7fff0000
+#define PAD_PU_PD_ON_MS_SOCK0   0x5f8f0000
+#define PAD_PU_PD_ON_MS_SOCK1   0x0f0f0000
+
+/* Internal clock delay */
+#define	CLOCK_DELAY            0x40
+
+/* ??? */
+#define	ADMA_ADDRESS           0x44
+
+/* Set the device clock */
+#define	CLOCK_CONTROL          0x48
+#define CLOCK_CONTROL_RESET     0x00000008
+#define CLOCK_CONTROL_40MHZ     0x00000009
+#define CLOCK_CONTROL_50MHZ     0x0000000a
+#define CLOCK_CONTROL_62_5MHZ   0x0000000c
+#define CLOCK_CONTROL_OFF       0x00000000
+
+/* Led blink period */
+#define	LED_CONTROL            0x4c
+
+/* Hardware version */
+#define	VERSION                0x50
+
+#define PCI_CTL_CLOCK_DLY_ADDR   0x000000b0
+#define PCI_CTL_CLOCK_DLY_MASK_A 0x00000f00
+#define PCI_CTL_CLOCK_DLY_MASK_B 0x0000f000
+
+struct j38ms_host {
+	struct j38ms            *chip;
+	struct memstick_host    *msh;
+	void __iomem            *addr;
+	bool                    dead;
+	spinlock_t              lock;
+	int                     id;
+	char                    host_id[32];
+	int                     irq;
+	struct memstick_request *req;
+	unsigned char           cmd_flags;
+	struct timer_list       timer;
+	struct tasklet_struct	tasklet;
+	unsigned long		timeout_jiffies;
+	int                     interface;
+
+	/* PIO state */
+	struct sg_mapping_iter  pio_sg_iter;
+	unsigned char		pio_offset;
+	unsigned char           pio_tmp_buf[4];
+	unsigned int            pio_tmp_buf_len;
+
+	/* DMA bounce buffer */
+	void			*dma_bounce_page;
+	dma_addr_t		dma_bus_address;
+
+};
+
+struct j38ms {
+	struct pci_dev        *pdev;
+	int                   host_cnt;
+	struct memstick_host  *hosts[];
+};
+
+enum {
+	CMD_READY    = 0x01,
+	FIFO_READY   = 0x02,
+
+	REG_DATA     = 0x04,
+	DMA_DATA     = 0x08,
+	PIO_DATA     = 0x10
+};
+
+#define __dbg(host, level, format, ...) \
+	do { \
+		if (debug >= level) \
+			printk(KERN_DEBUG \
+				"%s: " format "\n", dev_name(&host->msh->dev) \
+					 , ## __VA_ARGS__); \
+		else \
+			dev_dbg(&host->msh->dev, format, ## __VA_ARGS__); \
+	} while (0)
+
+#define dbg(host, format, ...)		__dbg(host, 1, format, ## __VA_ARGS__)
+#define dbg_v(host, format, ...)	__dbg(host, 2, format, ## __VA_ARGS__)
+#define dbg_reg(host, format, ...)	__dbg(host, 3, format, ## __VA_ARGS__)
+
+static void j38ms_next_request(struct j38ms_host *host);
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/6] memstick: add support for legacy memorysticks
  2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
                   ` (2 preceding siblings ...)
  2010-10-16 23:12 ` [PATCH 3/6] memstick: jmb38x: Driver rework Maxim Levitsky
@ 2010-10-16 23:12 ` Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 5/6] memstick: mspro_block: refactoring Maxim Levitsky
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-16 23:12 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML, Maxim Levitsky

Huge thanks for Alex Dubov for code that this is based on and for lot,
really lot of help he gave me in understanding MemorySticks and
implementing this driver.  His help made this driver possible.

As any code that works with user data this driver isn't recommened to use
with valuable data.

It tries its best though to avoid data corruption and possible damage to
the card.

Tested with MS DUO 64 MB card on Ricoh and Jmicron reader.

Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
Acked-by: Alex Dubov <oakad@yahoo.com>
---
 MAINTAINERS                      |    5 +
 drivers/memstick/core/Kconfig    |   12 +
 drivers/memstick/core/Makefile   |    2 +-
 drivers/memstick/core/ms_block.c | 2232 ++++++++++++++++++++++++++++++++++++++
 drivers/memstick/core/ms_block.h |  229 ++++
 5 files changed, 2479 insertions(+), 1 deletions(-)
 create mode 100644 drivers/memstick/core/ms_block.c
 create mode 100644 drivers/memstick/core/ms_block.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 9c7c41c..4ff3fce 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5448,6 +5448,11 @@ W:	http://tifmxx.berlios.de/
 S:	Maintained
 F:	drivers/memstick/host/tifm_ms.c
 
+SONY MEMORYSTICK STANDARD SUPPORT
+M:	Maxim Levitsky <maximlevitsky@gmail.com>
+S:	Maintained
+F:	drivers/memstick/core/ms_block.*
+
 SOUND
 M:	Jaroslav Kysela <perex@perex.cz>
 M:	Takashi Iwai <tiwai@suse.de>
diff --git a/drivers/memstick/core/Kconfig b/drivers/memstick/core/Kconfig
index 95f1814..f79f2a8 100644
--- a/drivers/memstick/core/Kconfig
+++ b/drivers/memstick/core/Kconfig
@@ -24,3 +24,15 @@ config MSPRO_BLOCK
 	  support. This provides a block device driver, which you can use
 	  to mount the filesystem. Almost everyone wishing MemoryStick
 	  support should say Y or M here.
+
+config MS_BLOCK
+	tristate "MemoryStick Standard device driver"
+	depends on BLOCK && EXPERIMENTAL
+	help
+	  Say Y here to enable the MemoryStick Standard device driver
+	  support. This provides a block device driver, which you can use
+	  to mount the filesystem.
+	  This driver works with old (bulky) MemoryStick and MemoryStick Duo
+	  but not PRO. Say Y if you have such card.
+	  Driver is new and not yet well tested, thus it can damage your card
+	  (even permanently)
diff --git a/drivers/memstick/core/Makefile b/drivers/memstick/core/Makefile
index 8b2b529..19d960b 100644
--- a/drivers/memstick/core/Makefile
+++ b/drivers/memstick/core/Makefile
@@ -7,5 +7,5 @@ ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
 endif
 
 obj-$(CONFIG_MEMSTICK)		+= memstick.o
-
+obj-$(CONFIG_MS_BLOCK)		+= ms_block.o
 obj-$(CONFIG_MSPRO_BLOCK)	+= mspro_block.o
diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c
new file mode 100644
index 0000000..75ec529
--- /dev/null
+++ b/drivers/memstick/core/ms_block.c
@@ -0,0 +1,2232 @@
+/*
+ *  ms_block.c - Sony MemoryStick (legacy) storage support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *  Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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.
+ *
+ * Special thanks to Carlos Corbacho for providing various MemoryStick cards
+ * that made this driver possible.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/mm.h>
+#include <linux/idr.h>
+#include <linux/hdreg.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/memstick.h>
+#include <linux/bitmap.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include "ms_block.h"
+
+static int major;
+static int debug;
+static int cache_flush_timeout = 1000;
+static bool verify_writes;
+
+
+/*
+ * Advance scatterlist by 'consumed' bytes
+ * Returns new scatterlist, or NULL if can't advance that much
+ */
+static struct scatterlist *sg_advance(struct scatterlist *sg, int consumed)
+{
+	while (consumed >= sg->length) {
+		consumed -= sg->length;
+
+		sg = sg_next(sg);
+		if (!sg)
+			break;
+	}
+
+	WARN_ON(!sg && consumed);
+
+	if (!sg)
+		return NULL;
+
+	sg->offset += consumed;
+	sg->length -= consumed;
+
+	if (sg->offset >= PAGE_SIZE) {
+		struct page *page =
+			nth_page(sg_page(sg), sg->offset / PAGE_SIZE);
+		sg_set_page(sg, page, sg->length, sg->offset % PAGE_SIZE);
+	}
+
+	return sg;
+}
+
+/* Calculate number of sg entries in sg list */
+static int sg_nents(struct scatterlist *sg)
+{
+	int nents = 0;
+	while (sg) {
+		nents++;
+		sg = sg_next(sg);
+	}
+
+	return nents;
+}
+
+/* Calculate total lenght of scatterlist */
+static int sg_total_len(struct scatterlist *sg)
+{
+	int len = 0;
+	while (sg) {
+		len += sg->length;
+		sg = sg_next(sg);
+	}
+	return len;
+}
+
+/* Compare contents of an sg to a buffer */
+static bool sg_compare_to_buffer(struct scatterlist *sg, u8 *buffer, size_t len)
+{
+	unsigned long flags;
+	int retval = 0;
+	struct sg_mapping_iter miter;
+
+	if (sg_total_len(sg) < len)
+		return 1;
+
+	local_irq_save(flags);
+	sg_miter_start(&miter, sg, sg_nents(sg),
+				SG_MITER_ATOMIC | SG_MITER_FROM_SG);
+
+	while (sg_miter_next(&miter) && len > 0) {
+
+		int cmplen = min(miter.length, len);
+		if (memcmp(miter.addr, buffer, cmplen)) {
+			retval = 1;
+			break;
+		}
+
+		buffer += cmplen;
+		len -= cmplen;
+	}
+
+	sg_miter_stop(&miter);
+	local_irq_restore(flags);
+	return retval;
+}
+
+/* Get zone at which block with logical address 'lba' lives
+ * Flash is broken into zones.
+ * Each zone consists of 512 eraseblocks, out of which in first
+ * zone 494 are used and 496 are for all following zones.
+ * Therefore zone #0 hosts blocks 0-493, zone #1 blocks 494-988, etc...
+*/
+static int msb_get_zone_from_lba(int lba)
+{
+	if (lba < 494)
+		return 0;
+	return ((lba - 494) / 496) + 1;
+}
+
+/* Get zone of physical block. Trivial */
+static int msb_get_zone_from_pba(int pba)
+{
+	return pba / MS_BLOCKS_IN_ZONE;
+}
+
+/* Debug test to validate free block counts */
+#ifdef DEBUG
+static int msb_validate_used_block_bitmap(struct msb_data *msb)
+{
+	int total_free_blocks = 0;
+	int i;
+
+	for (i = 0 ; i < msb->zone_count ; i++)
+		total_free_blocks += msb->free_block_count[i];
+
+	if (msb->block_count - bitmap_weight(msb->used_blocks_bitmap,
+					msb->block_count) == total_free_blocks)
+		return 0;
+
+	ms_printk("BUG: free block counts don't match the bitmap");
+	msb->read_only = true;
+	return -EINVAL;
+}
+#endif
+
+/* Mark physical block as used */
+static void msb_mark_block_used(struct msb_data *msb, int pba)
+{
+	int zone = msb_get_zone_from_pba(pba);
+
+	if (test_bit(pba, msb->used_blocks_bitmap)) {
+		ms_printk("BUG: attempt to mark "
+			"already used pba %d as used", pba);
+		msb->read_only = true;
+		return;
+	}
+
+#ifdef DEBUG
+	if (msb_validate_used_block_bitmap(msb))
+		return;
+#endif
+	set_bit(pba, msb->used_blocks_bitmap);
+	msb->free_block_count[zone]--;
+}
+
+/* Mark physical block as free */
+static void msb_mark_block_unused(struct msb_data *msb, int pba)
+{
+	int zone = msb_get_zone_from_pba(pba);
+
+	if (!test_bit(pba, msb->used_blocks_bitmap)) {
+		ms_printk("BUG: attempt to mark "
+				"already unused pba %d as unused" , pba);
+		msb->read_only = true;
+		return;
+	}
+
+#ifdef DEBUG
+	if (msb_validate_used_block_bitmap(msb))
+		return;
+#endif
+	clear_bit(pba, msb->used_blocks_bitmap);
+	msb->free_block_count[zone]++;
+}
+
+/*
+ * Create a sg that spans page from current scatterlist
+ * used by read/write functions
+ */
+static void msb_set_sg(struct msb_data *msb, struct scatterlist *sg)
+{
+	int offset = msb->current_sg->offset + msb->sg_offset;
+	struct page *page = nth_page(sg_page(msb->current_sg),
+						offset >> PAGE_SHIFT);
+	sg_init_table(sg, 1);
+	sg_set_page(sg, page, msb->page_size, offset_in_page(offset));
+}
+
+/* Advances the current sg by one page. Returns error if can't */
+static int msb_advance_sg(struct msb_data *msb)
+{
+	msb->sg_offset += msb->page_size;
+
+	if (msb->sg_offset & (msb->page_size - 1)) {
+		ms_printk("BUG: sg not aligned");
+		return -EINVAL;
+	}
+
+	if (msb->sg_offset > msb->current_sg->length) {
+		dbg("BUG: sg overrun");
+		return -EINVAL;
+	}
+
+	if (msb->sg_offset == msb->current_sg->length) {
+		msb->current_sg = sg_next(msb->current_sg);
+		msb->sg_offset = 0;
+	}
+
+	if (!msb->current_sg)
+		return -EINVAL;
+	return 0;
+}
+
+/*
+ * This function is a handler for reads of one page from device.
+ * Writes output to msb->current_sg, takes sector address from msb->reg.param
+ * Can also be used to read extra data only. Set params accordintly.
+ */
+static int h_msb_read_page(struct memstick_dev *card,
+				 struct memstick_request **mrq)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	struct scatterlist sg;
+	u8 command, intreg;
+
+	memstick_allocate_request(card, mrq);
+	if ((*mrq)->error) {
+		dbg("read_page, unknown error");
+		return memstick_complete_request(card, *mrq, 0);
+	}
+again:
+	switch (card->state) {
+	case 0: /* Write the sector address */
+		if (!memstick_write_regs(card,
+			offsetof(struct ms_register, param),
+			sizeof(struct ms_param_register),
+			(unsigned char *)&msb->regs.param,
+			*mrq))
+			return 0;
+		break;
+
+	case 1: /* Execute the read command*/
+		command = MS_CMD_BLOCK_READ;
+		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
+		break;
+
+	case 2: /* send INT request */
+		if (memstick_read_int_reg(card, *mrq, -1))
+			break;
+		card->state++;
+
+	case 3: /* get result of the INT request*/
+		intreg = (*mrq)->data[0];
+		msb->regs.status.interrupt = intreg;
+
+		if (intreg & MEMSTICK_INT_CMDNAK)
+			return memstick_complete_request(card, *mrq, -EIO);
+
+		if (!(intreg & MEMSTICK_INT_CED)) {
+			card->state--;
+			goto again;
+		}
+
+		memstick_read_int_reg_cleanup(card);
+
+		if (intreg & MEMSTICK_INT_ERR)
+			card->state++;
+		else
+			card->state = 6;
+
+		goto again;
+
+	case 4: /* read the status register to understand source of the INT_ERR */
+		if (!memstick_read_regs(card,
+			offsetof(struct ms_register, status),
+			sizeof(struct ms_status_register),
+			*mrq))
+			return 0;
+		break;
+
+	case 5: /* get results of status check */
+		msb->regs.status = *(struct ms_status_register *)(*mrq)->data;
+		card->state++;
+
+	case 6: /* Send extra data read request */
+		if (!memstick_read_regs(card,
+			offsetof(struct ms_register, extra_data),
+			sizeof(struct ms_extra_data_register),
+			*mrq))
+			return 0;
+		break;
+
+	case 7: /* Save result of extra data request */
+		msb->regs.extra_data =
+			*(struct ms_extra_data_register *) (*mrq)->data;
+		card->state++;
+
+	case 8: /* Send the  MS_TPC_READ_LONG_DATA to read IO buffer */
+
+		/* Skip that state if we only read the oob */
+		if (msb->regs.param.cp == MEMSTICK_CP_EXTRA) {
+			card->state++;
+			goto again;
+		}
+
+		msb_set_sg(msb, &sg);
+		memstick_init_req_sg(*mrq, MS_TPC_READ_LONG_DATA, &sg);
+		break;
+
+	case 9: /* check validity of data buffer & done */
+
+		if (!(msb->regs.status.interrupt & MEMSTICK_INT_ERR))
+			return memstick_complete_request(card, *mrq, 0);
+
+		if (msb->regs.status.status1 & MEMSTICK_UNCORR_ERROR) {
+			dbg("read_page: uncorrectable error");
+			return memstick_complete_request(card, *mrq, -EBADMSG);
+		}
+
+		if (msb->regs.status.status1 & MEMSTICK_CORR_ERROR) {
+			dbg("read_page: correctable error");
+			return memstick_complete_request(card, *mrq, -EUCLEAN);
+		} else {
+			dbg("read_page: INT error, but no status error bits");
+			return memstick_complete_request(card, *mrq, -EIO);
+		}
+	default:
+		BUG();
+	}
+	card->state++;
+	return 0;
+}
+
+/*
+ * Handler of writes of exactly one block.
+ * Takes address from msb->regs.param. Writes same extra data to blocks, also taken
+ * from msb->regs.extra
+ * Returns -EBADMSG if write fails due to uncorrectable error, or -EIO if
+ * device refuses to take the command or something else
+ */
+static int h_msb_write_block(struct memstick_dev *card,
+				 struct memstick_request **mrq)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	struct scatterlist sg;
+	u8 intreg, command;
+
+	memstick_allocate_request(card, mrq);
+	if ((*mrq)->error)
+		return memstick_complete_request(card, *mrq, 0);
+
+again:
+	switch (card->state) {
+
+	/* HACK: align write on 4 boundary to workaround Jmicron limitation
+	 * It doen't make sense to use ALIGN here, because its very hardware
+	   specific anyway
+	*/
+
+	case 0: /* write param register*/
+		if (!memstick_write_regs(card,
+			offsetof(struct ms_register, param),
+			sizeof(struct ms_param_register) +
+			sizeof(struct ms_extra_data_register) + 2,
+			(unsigned char *)&msb->regs.param,
+			*mrq))
+			return 0;
+		break;
+
+	case 1: /* execute the write command*/
+		command = MS_CMD_BLOCK_WRITE;
+		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
+		break;
+
+	case 2: /* send INT request */
+		if (memstick_read_int_reg(card, *mrq, -1))
+			break;
+		card->state++;
+
+	case 3: /* read INT response */
+		intreg = (*mrq)->data[0];
+		msb->regs.status.interrupt = intreg;
+
+		/* errors mean out of here, and fast... */
+		if (intreg & (MEMSTICK_INT_CMDNAK))
+			return memstick_complete_request(card, *mrq, -EIO);
+
+		if (intreg & MEMSTICK_INT_ERR)
+			return memstick_complete_request(card, *mrq, -EBADMSG);
+
+
+		/* for last page we need to poll CED */
+		if (msb->current_page == msb->pages_in_block) {
+			if (intreg & MEMSTICK_INT_CED)
+				return memstick_complete_request(card, *mrq, 0);
+			card->state--;
+			goto again;
+
+		}
+
+		/* for non-last page we need BREQ before writing next chunk */
+		if (!(intreg & MEMSTICK_INT_BREQ)) {
+			card->state--;
+			goto again;
+		}
+
+		memstick_read_int_reg_cleanup(card);
+		card->state++;
+
+	case 4: /* send the MS_TPC_WRITE_LONG_DATA to perform the write*/
+		msb_set_sg(msb, &sg);
+		memstick_init_req_sg(*mrq, MS_TPC_WRITE_LONG_DATA, &sg);
+		(*mrq)->need_card_int = 1;
+		break;
+
+	case 5: /* Switch to next page + go back to int polling */
+		msb->current_page++;
+
+		if (msb->current_page < msb->pages_in_block) {
+			if (msb_advance_sg(msb)) {
+				ms_printk(
+				"BUG: out of data while writing block!");
+				return memstick_complete_request(card,
+								*mrq, -EFAULT);
+			}
+		}
+		card->state = 2;
+		goto again;
+	default:
+		BUG();
+	}
+	card->state++;
+	return 0;
+}
+
+/*
+ * This function is used to send simple IO requests to device that consist
+ * of register write + command
+ */
+static int h_msb_send_command(struct memstick_dev *card,
+				 struct memstick_request **mrq)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	u8 intreg;
+	int len;
+
+	memstick_allocate_request(card, mrq);
+	if ((*mrq)->error) {
+		dbg("send_command: unknown error");
+		return memstick_complete_request(card, *mrq, 0);
+	}
+
+again:
+	switch (card->state) {
+	case 0: /* write regs */
+		len = sizeof(struct ms_param_register);
+		if (msb->command_need_oob)
+			/* See h_msb_write_block */
+			len += (sizeof(struct ms_extra_data_register) + 2);
+
+		if (!memstick_write_regs(card,
+			offsetof(struct ms_register, param),
+			len,
+			(unsigned char *)&msb->regs.param,
+			*mrq))
+			return 0;
+		break;
+
+	case 1: /* execute the command*/
+		memstick_init_req(*mrq, MS_TPC_SET_CMD, &msb->command_value, 1);
+		break;
+
+	case 2: /* send INT request */
+		if (memstick_read_int_reg(card, *mrq, -1))
+			break;
+		card->state++;
+
+	case 3: /* poll for int bits */
+		intreg = (*mrq)->data[0];
+
+		if (intreg & MEMSTICK_INT_CMDNAK)
+			return memstick_complete_request(card, *mrq, -EIO);
+		if (intreg & MEMSTICK_INT_ERR)
+			return memstick_complete_request(card, *mrq, -EBADMSG);
+
+
+		if (!(intreg & MEMSTICK_INT_CED)) {
+			card->state--;
+			goto again;
+		}
+
+		memstick_read_int_reg_cleanup(card);
+		return memstick_complete_request(card, *mrq, 0);
+	}
+	card->state++;
+	return 0;
+}
+
+/* Small handler for card reset */
+static int h_msb_reset(struct memstick_dev *card,
+				 struct memstick_request **mrq)
+{
+	u8 command = MS_CMD_RESET;
+
+	memstick_allocate_request(card, mrq);
+	if ((*mrq)->error)
+		return memstick_complete_request(card, *mrq, 0);
+
+	switch (card->state) {
+	case 0:
+		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
+		(*mrq)->need_card_int = 0;
+		break;
+	case 1:
+		return memstick_complete_request(card, *mrq, 0);
+	}
+	card->state++;
+	return 0;
+}
+
+/* This handler is used to do serial->parallel switch */
+static int h_msb_parallel_switch(struct memstick_dev *card,
+					struct memstick_request **mrq)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	struct memstick_host *host = card->host;
+
+	memstick_allocate_request(card, mrq);
+	if ((*mrq)->error) {
+		dbg("parallel_switch: error");
+		msb->regs.param.system &= ~MEMSTICK_SYS_PAM;
+		return memstick_complete_request(card, *mrq, 0);
+	}
+
+	switch (card->state) {
+
+	case 0: /* Set the parallel interface on memstick side */
+		msb->regs.param.system |= MEMSTICK_SYS_PAM;
+
+		if (!memstick_write_regs(card,
+			offsetof(struct ms_register, param),
+			1,
+			(unsigned char *)&msb->regs.param,
+			*mrq))
+			return 0;
+		break;
+
+	case 1: /* Set parallel interface on our side + send a dummy request
+			to see if card responds */
+		host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_PAR4);
+		memstick_init_req(&card->current_mrq, MS_TPC_GET_INT, NULL, 1);
+		break;
+
+	case 2:
+		return memstick_complete_request(card, *mrq, 0);
+	}
+	card->state++;
+	return 0;
+}
+
+static int msb_switch_to_parallel(struct msb_data *msb);
+
+/* Reset the card, to guard against hw errors beeing treated as bad blocks */
+static int msb_reset(struct msb_data *msb)
+{
+	bool was_parallel = msb->regs.param.system & MEMSTICK_SYS_PAM;
+	struct memstick_dev *card = msb->card;
+	struct memstick_host *host = card->host;
+	int error;
+
+	memstick_invalidate_reg_window(card);
+
+	/* Reset the controller */
+	host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+	msleep(100);
+	host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
+	host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+
+
+	/* Reset the card */
+	msb->regs.param.system = MEMSTICK_SYS_BAMD;
+	error = memstick_run_state_machine(card, h_msb_reset);
+	if (error) {
+		dbg("Failed to reset the card");
+		msb->read_only = true;
+		return -ENODEV;
+	}
+
+	/* Set parallel mode */
+	if (was_parallel)
+		msb_switch_to_parallel(msb);
+	return 0;
+}
+
+/* Attempts to switch interface to parallel mode */
+static int msb_switch_to_parallel(struct msb_data *msb)
+{
+	int error;
+
+	error = memstick_run_state_machine(msb->card, h_msb_parallel_switch);
+	if (error) {
+		ms_printk("Switch to parallel failed");
+		msb->regs.param.system &= ~MEMSTICK_SYS_PAM;
+		msb_reset(msb);
+		return -EFAULT;
+	}
+
+	msb->card->caps |= MEMSTICK_CAP_AUTO_GET_INT;
+	return 0;
+}
+
+/* Changes overwrite flag on a page */
+static int msb_set_overwrite_flag(struct msb_data *msb,
+						u16 pba, u8 page, u8 flag)
+{
+	if (msb->read_only)
+		return -EROFS;
+
+	msb->regs.param.block_address = cpu_to_be16(pba);
+	msb->regs.param.page_address = page;
+	msb->regs.param.cp = MEMSTICK_CP_OVERWRITE;
+	msb->regs.extra_data.overwrite_flag = flag;
+	msb->command_value = MS_CMD_BLOCK_WRITE;
+	msb->command_need_oob = true;
+
+	dbg_verbose("changing overwrite flag to %02x for sector %d, page %d",
+							flag, pba, page);
+	return memstick_run_state_machine(msb->card, h_msb_send_command);
+}
+
+static int msb_mark_bad(struct msb_data *msb, int pba)
+{
+	ms_printk("marking pba %d as bad", pba);
+	msb_reset(msb);
+	return msb_set_overwrite_flag(
+			msb, pba, 0, 0xFF & ~MEMSTICK_OVERWRITE_BKST);
+}
+
+static int msb_mark_page_bad(struct msb_data *msb, int pba, int page)
+{
+	dbg("marking page %d of pba %d as bad", page, pba);
+	msb_reset(msb);
+	return msb_set_overwrite_flag(msb,
+		pba, page, ~MEMSTICK_OVERWRITE_PGST0);
+}
+
+/* Erases one physical block */
+static int msb_erase_block(struct msb_data *msb, u16 pba)
+{
+	int error, try;
+	if (msb->read_only)
+		return -EROFS;
+
+	dbg_verbose("erasing pba %d", pba);
+
+	for (try = 1 ; try < 3 ; try++) {
+		msb->regs.param.block_address = cpu_to_be16(pba);
+		msb->regs.param.page_address = 0;
+		msb->regs.param.cp = MEMSTICK_CP_BLOCK;
+		msb->command_value = MS_CMD_BLOCK_ERASE;
+		msb->command_need_oob = false;
+
+
+		error = memstick_run_state_machine(msb->card,
+							h_msb_send_command);
+		if (!error || msb_reset(msb))
+			break;
+	}
+
+	if (error) {
+		ms_printk("erase failed, marking pba %d as bad", pba);
+		msb_mark_bad(msb, pba);
+	}
+
+	dbg_verbose("erase success, marking pba %d as unused", pba);
+	msb_mark_block_unused(msb, pba);
+	set_bit(pba, msb->erased_blocks_bitmap);
+	return error;
+}
+
+/* Reads one page from device */
+static int msb_read_page(struct msb_data *msb,
+	u16 pba, u8 page, struct ms_extra_data_register *extra,
+						struct scatterlist *sg)
+{
+	int try, error;
+
+	if (sg && sg->length < msb->page_size) {
+		ms_printk(
+			"BUG: attempt to read pba %d page %d with too small sg",
+								pba, page);
+		return -EINVAL;
+	}
+
+	if (pba == MS_BLOCK_INVALID) {
+		u8 *ptr;
+		unsigned long flags;
+
+		dbg_verbose("read unmapped sector. returning 0xFF");
+
+		local_irq_save(flags);
+		ptr = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset;
+		memset(ptr, 0xFF, msb->page_size);
+		kunmap_atomic(ptr - sg->offset, KM_IRQ0);
+		local_irq_restore(flags);
+
+		if (extra)
+			memset(extra, 0xFF, sizeof(*extra));
+		return 0;
+	}
+
+	if (pba >= msb->block_count) {
+		ms_printk("BUG: attempt to read beyond"
+					" the end of the card at pba %d", pba);
+		return -EINVAL;
+	}
+
+	for (try = 1 ; try < 3 ; try++) {
+		msb->regs.param.block_address = cpu_to_be16(pba);
+		msb->regs.param.page_address = page;
+		msb->regs.param.cp = MEMSTICK_CP_PAGE;
+
+		msb->current_sg = sg;
+		msb->sg_offset = 0;
+		error = memstick_run_state_machine(msb->card, h_msb_read_page);
+
+
+		if (error == -EUCLEAN) {
+			ms_printk("correctable error on pba %d, page %d",
+				pba, page);
+			error = 0;
+		}
+
+		if (!error && extra)
+			*extra = msb->regs.extra_data;
+
+		if (!error || msb_reset(msb))
+			break;
+
+	}
+
+	/* Mark bad pages */
+	if (error == -EBADMSG) {
+		ms_printk("uncorrectable error on read of pba %d, page %d",
+			pba, page);
+
+		if (msb->regs.extra_data.overwrite_flag &
+					MEMSTICK_OVERWRITE_PGST0)
+			msb_mark_page_bad(msb, pba, page);
+		return -EBADMSG;
+	}
+
+	if (error)
+		ms_printk("read of pba %d, page %d failed with error %d",
+			pba, page, error);
+	return error;
+}
+
+/* Reads oob of page only */
+static int msb_read_oob(struct msb_data *msb, u16 pba, u16 page,
+	struct ms_extra_data_register *extra)
+{
+	int error;
+	BUG_ON(!extra);
+
+	msb->regs.param.block_address = cpu_to_be16(pba);
+	msb->regs.param.page_address = page;
+	msb->regs.param.cp = MEMSTICK_CP_EXTRA;
+
+	if (pba > msb->block_count) {
+		ms_printk("BUG: attempt to read beyond"
+					" the end of card at pba %d", pba);
+		return -EINVAL;
+	}
+
+	error = memstick_run_state_machine(msb->card, h_msb_read_page);
+	*extra = msb->regs.extra_data;
+
+	if (error == -EUCLEAN) {
+		ms_printk("correctable error on pba %d, page %d",
+			pba, page);
+		return 0;
+	}
+
+	return error;
+}
+
+
+/* Reads a block and compares it with data contained in scatterlist orig_sg */
+static bool msb_verify_block(struct msb_data *msb, u16 pba,
+					struct scatterlist *orig_sg)
+{
+	struct scatterlist sg;
+	int page = 0, error;
+
+	while (page < msb->pages_in_block) {
+		sg_init_one(&sg, msb->block_buffer +
+					page * msb->page_size, msb->page_size);
+
+		error = msb_read_page(msb, pba, page, NULL, &sg);
+		if (error)
+			return -EIO;
+		page++;
+	}
+
+	if (sg_compare_to_buffer(orig_sg, msb->block_buffer, msb->block_size))
+		return -EIO;
+	return 0;
+}
+
+/* Writes exectly one block + oob */
+static int msb_write_block(struct msb_data *msb,
+			u16 pba, u32 lba, struct scatterlist *sg)
+{
+	int error, current_try = 1;
+	BUG_ON(sg->length < msb->page_size);
+
+	if (msb->read_only)
+		return -EROFS;
+
+	if (sg_total_len(sg) < msb->block_size) {
+		ms_printk("BUG: write: sg underrrun");
+		return -EINVAL;
+	}
+
+	if (pba == MS_BLOCK_INVALID) {
+		ms_printk(
+			"BUG: write: attempt to write MS_BLOCK_INVALID block");
+		return -EINVAL;
+	}
+
+	if (pba >= msb->block_count || lba >= msb->logical_block_count) {
+		ms_printk(
+		"BUG: write: attempt to write beyond the end of device");
+		return -EINVAL;
+	}
+
+	if (msb_get_zone_from_lba(lba) != msb_get_zone_from_pba(pba)) {
+		ms_printk("BUG: write: lba zone mismatch");
+		return -EINVAL;
+	}
+
+	if (pba == msb->boot_block_locations[0] ||
+		pba == msb->boot_block_locations[1]) {
+		ms_printk("BUG: write: attempt to write to boot blocks!");
+		return -EINVAL;
+	}
+
+	while (1) {
+
+		if (msb->read_only)
+			return -EROFS;
+
+		msb->regs.param.cp = MEMSTICK_CP_BLOCK;
+		msb->regs.param.page_address = 0;
+		msb->regs.param.block_address = cpu_to_be16(pba);
+
+		msb->regs.extra_data.management_flag = 0xFF;
+		msb->regs.extra_data.overwrite_flag = 0xF8;
+		msb->regs.extra_data.logical_address = cpu_to_be16(lba);
+
+		msb->current_sg = sg;
+		msb->current_page = 0;
+		msb->sg_offset = 0;
+
+		error = memstick_run_state_machine(msb->card,
+							h_msb_write_block);
+
+		/* Sector we just wrote to is assumed erased since its pba
+			was erased. If it wasn't erased, write will succeed
+			and will just clear the bits that were set in the block
+			thus test that what we have written,
+			matches what we expect.
+			We do trust the blocks that we erased */
+		if (!error && (verify_writes ||
+				!test_bit(pba, msb->erased_blocks_bitmap)))
+			error = msb_verify_block(msb, pba, sg);
+
+		if (!error)
+			break;
+
+		if (current_try > 1 || msb_reset(msb))
+			break;
+
+		ms_printk("write failed, trying to erase the pba %d", pba);
+		error = msb_erase_block(msb, pba);
+		if (error)
+			break;
+
+		current_try++;
+	}
+	return error;
+}
+
+/* Finds a free block for write replacement */
+static u16 msb_get_free_block(struct msb_data *msb, int zone)
+{
+	u16 pos;
+	int pba = zone * MS_BLOCKS_IN_ZONE;
+	int i;
+
+	get_random_bytes(&pos, sizeof(pos));
+
+	if (!msb->free_block_count[zone]) {
+		ms_printk("NO free blocks in the zone %d, to use for a write, "
+			"(media is WORN out) switching to RO mode", zone);
+		msb->read_only = true;
+		return MS_BLOCK_INVALID;
+	}
+
+	pos %= msb->free_block_count[zone];
+
+	dbg_verbose("have %d choices for a free block, selected randomally: %d",
+		msb->free_block_count[zone], pos);
+
+	pba = find_next_zero_bit(msb->used_blocks_bitmap,
+							msb->block_count, pba);
+	for (i = 0 ; i < pos ; ++i)
+		pba = find_next_zero_bit(msb->used_blocks_bitmap,
+						msb->block_count, pba + 1);
+
+	dbg_verbose("result of the free blocks scan: pba %d", pba);
+
+	if (pba == msb->block_count || (msb_get_zone_from_pba(pba)) != zone) {
+		ms_printk("BUG: cant get a free block");
+		msb->read_only = true;
+		return MS_BLOCK_INVALID;
+	}
+
+	msb_mark_block_used(msb, pba);
+	return pba;
+}
+
+static int msb_update_block(struct msb_data *msb, u16 lba,
+	struct scatterlist *sg)
+{
+	u16 pba, new_pba;
+	int error, try;
+
+	pba = msb->lba_to_pba_table[lba];
+	dbg_verbose("start of a block update at lba  %d, pba %d", lba, pba);
+
+	if (pba != MS_BLOCK_INVALID) {
+		dbg_verbose("setting the update flag on the block");
+		msb_set_overwrite_flag(msb, pba, 0,
+				0xFF & ~MEMSTICK_OVERWRITE_UDST);
+	}
+
+	for (try = 0 ; try < 3 ; try++) {
+		new_pba = msb_get_free_block(msb,
+			msb_get_zone_from_lba(lba));
+
+		if (new_pba == MS_BLOCK_INVALID) {
+			error = -EIO;
+			goto out;
+		}
+
+		dbg_verbose("block update: writing updated block to the pba %d",
+								new_pba);
+		error = msb_write_block(msb, new_pba, lba, sg);
+		if (error == -EBADMSG) {
+			msb_mark_bad(msb, new_pba);
+			continue;
+		}
+
+		if (error)
+			goto out;
+
+		dbg_verbose("block update: erasing the old block");
+		msb_erase_block(msb, pba);
+		msb->lba_to_pba_table[lba] = new_pba;
+		return 0;
+	}
+out:
+	if (error) {
+		ms_printk("block update error after %d tries, "
+						"switching to r/o mode", try);
+		msb->read_only = true;
+	}
+	return error;
+}
+
+/* Converts endiannes in the boot block for easy use */
+static void msb_fix_boot_page_endianness(struct ms_boot_page *p)
+{
+	p->header.block_id = be16_to_cpu(p->header.block_id);
+	p->header.format_reserved = be16_to_cpu(p->header.format_reserved);
+	p->entry.disabled_block.start_addr
+		= be32_to_cpu(p->entry.disabled_block.start_addr);
+	p->entry.disabled_block.data_size
+		= be32_to_cpu(p->entry.disabled_block.data_size);
+	p->entry.cis_idi.start_addr
+		= be32_to_cpu(p->entry.cis_idi.start_addr);
+	p->entry.cis_idi.data_size
+		= be32_to_cpu(p->entry.cis_idi.data_size);
+	p->attr.block_size = be16_to_cpu(p->attr.block_size);
+	p->attr.number_of_blocks = be16_to_cpu(p->attr.number_of_blocks);
+	p->attr.number_of_effective_blocks
+		= be16_to_cpu(p->attr.number_of_effective_blocks);
+	p->attr.page_size = be16_to_cpu(p->attr.page_size);
+	p->attr.memory_manufacturer_code
+		= be16_to_cpu(p->attr.memory_manufacturer_code);
+	p->attr.memory_device_code = be16_to_cpu(p->attr.memory_device_code);
+	p->attr.implemented_capacity
+		= be16_to_cpu(p->attr.implemented_capacity);
+	p->attr.controller_number = be16_to_cpu(p->attr.controller_number);
+	p->attr.controller_function = be16_to_cpu(p->attr.controller_function);
+}
+
+static int msb_read_boot_blocks(struct msb_data *msb)
+{
+	int pba = 0;
+	struct scatterlist sg;
+	struct ms_extra_data_register extra;
+	struct ms_boot_page *page;
+
+	msb->boot_block_locations[0] = MS_BLOCK_INVALID;
+	msb->boot_block_locations[1] = MS_BLOCK_INVALID;
+	msb->boot_block_count = 0;
+
+	dbg_verbose("Start of a scan for the boot blocks");
+
+	if (!msb->boot_page) {
+		page = kmalloc(sizeof(struct ms_boot_page) * 2, GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+
+		msb->boot_page = page;
+	}
+
+	msb->block_count = MS_BLOCK_MAX_BOOT_ADDR;
+
+	for (pba = 0 ; pba < MS_BLOCK_MAX_BOOT_ADDR ; pba++) {
+
+		sg_init_one(&sg, page, sizeof(*page));
+		if (msb_read_page(msb, pba, 0, &extra, &sg)) {
+			dbg("boot scan: can't read pba %d", pba);
+			continue;
+		}
+
+		if (extra.management_flag & MEMSTICK_MANAGEMENT_SYSFLG) {
+			dbg("managment flag doesn't indicate boot block %d",
+									pba);
+			continue;
+		}
+
+		if (be16_to_cpu(page->header.block_id) != MS_BLOCK_BOOT_ID) {
+			dbg("the pba at %d doesn' contain boot block ID", pba);
+			continue;
+		}
+
+		msb_fix_boot_page_endianness(page);
+		msb->boot_block_locations[msb->boot_block_count] = pba;
+
+		page++;
+		msb->boot_block_count++;
+
+		if (msb->boot_block_count == 2)
+			break;
+	}
+
+	if (!msb->boot_block_count) {
+		ms_printk("media doesn't contain master page, aborting");
+		return -EIO;
+	}
+
+	dbg_verbose("End of scan for boot blocks");
+	return 0;
+}
+
+static int msb_read_bad_block_table(struct msb_data *msb, int block_nr)
+{
+	struct ms_boot_page *boot_block;
+	struct scatterlist sg;
+	struct scatterlist *sg_ptr = &sg;
+	u16 *buffer = NULL;
+
+	int i, error = 0;
+	int data_size, data_offset, page, page_offset, size_to_read;
+	u16 pba;
+
+	BUG_ON(block_nr > 1);
+
+	boot_block = &msb->boot_page[block_nr];
+	pba = msb->boot_block_locations[block_nr];
+
+	if (msb->boot_block_locations[block_nr] == MS_BLOCK_INVALID)
+		return -EINVAL;
+
+	data_size = boot_block->entry.disabled_block.data_size;
+	data_offset = sizeof(struct ms_boot_page) +
+			boot_block->entry.disabled_block.start_addr;
+	if (!data_size)
+		return 0;
+
+	page = data_offset / msb->page_size;
+	page_offset = data_offset % msb->page_size;
+	size_to_read =
+		DIV_ROUND_UP(data_size + page_offset, msb->page_size) *
+			msb->page_size;
+
+	dbg("reading bad block of boot block at pba %d, offset %d len %d",
+		pba, data_offset, data_size);
+
+	buffer = kzalloc(size_to_read, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Read the buffer */
+	sg_init_one(&sg, buffer, size_to_read);
+
+	while (sg_ptr) {
+		error = msb_read_page(msb, pba, page, NULL, sg_ptr);
+		if (error)
+			goto out;
+
+		sg_ptr = sg_advance(sg_ptr, msb->page_size);
+		page++;
+		if (page == msb->pages_in_block) {
+			ms_printk(
+			"bad block table extends beyond the boot block");
+			break;
+		}
+	}
+
+	/* Process the bad block table */
+	for (i = page_offset ; i < data_size / sizeof(u16) ; i++) {
+
+		u16 bad_block = be16_to_cpu(buffer[i]);
+
+		if (bad_block >= msb->block_count) {
+			dbg("bad block table contains invalid block %d",
+								bad_block);
+			continue;
+		}
+
+		if (test_bit(bad_block, msb->used_blocks_bitmap))  {
+			dbg("duplicate bad block %d in the table",
+				bad_block);
+			continue;
+		}
+
+		dbg("block %d is marked as factory bad", bad_block);
+		msb_mark_block_used(msb, bad_block);
+	}
+out:
+	kfree(buffer);
+	return error;
+}
+
+static int msb_ftl_initialize(struct msb_data *msb)
+{
+	int i;
+
+	if (msb->ftl_initialized)
+		return 0;
+
+	msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE;
+	msb->logical_block_count = msb->zone_count * 496 - 2;
+
+	msb->used_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL);
+	msb->erased_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL);
+	msb->lba_to_pba_table =
+		kmalloc(msb->logical_block_count * sizeof(u16), GFP_KERNEL);
+
+	if (!msb->used_blocks_bitmap || !msb->lba_to_pba_table ||
+						!msb->erased_blocks_bitmap) {
+		kfree(msb->used_blocks_bitmap);
+		kfree(msb->lba_to_pba_table);
+		kfree(msb->erased_blocks_bitmap);
+		return -ENOMEM;
+	}
+
+	for (i = 0 ; i < msb->zone_count ; i++)
+		msb->free_block_count[i] = MS_BLOCKS_IN_ZONE;
+
+	memset(msb->lba_to_pba_table, MS_BLOCK_INVALID,
+			msb->logical_block_count * sizeof(u16));
+
+	dbg("initial FTL tables created. Zone count = %d, "
+					"Logical block count = %d",
+		msb->zone_count, msb->logical_block_count);
+
+	msb->ftl_initialized = true;
+	return 0;
+}
+
+static int msb_ftl_scan(struct msb_data *msb)
+{
+	u16 pba, lba, other_block;
+	u8 overwrite_flag, managment_flag, other_overwrite_flag;
+	int error;
+	struct ms_extra_data_register extra;
+	u8 *overwrite_flags = kzalloc(msb->block_count, GFP_KERNEL);
+
+	if (!overwrite_flags)
+		return -ENOMEM;
+
+	dbg("Start of media scanning");
+	for (pba = 0 ; pba < msb->block_count ; pba++) {
+
+		if (pba == msb->boot_block_locations[0] ||
+			pba == msb->boot_block_locations[1]) {
+			dbg_verbose("pba %05d -> [boot block]", pba);
+			msb_mark_block_used(msb, pba);
+			continue;
+		}
+
+		if (test_bit(pba, msb->used_blocks_bitmap)) {
+			dbg_verbose("pba %05d -> [factory bad]", pba);
+			continue;
+		}
+
+		error = msb_read_oob(msb, pba, 0, &extra);
+
+		/* can't trust the page if we can't read the oob */
+		if (error == -EBADMSG) {
+			ms_printk(
+			"oob of pba %d damaged, will try to erase it", pba);
+			msb_mark_block_used(msb, pba);
+			msb_erase_block(msb, pba);
+			continue;
+		} else if (error)
+			return error;
+
+		lba = be16_to_cpu(extra.logical_address);
+		managment_flag = extra.management_flag;
+		overwrite_flag = extra.overwrite_flag;
+		overwrite_flags[pba] = overwrite_flag;
+
+		/* Skip bad blocks */
+		if (!(overwrite_flag & MEMSTICK_OVERWRITE_BKST)) {
+			dbg("pba %05d -> [BAD]", pba);
+			msb_mark_block_used(msb, pba);
+			continue;
+		}
+
+		/* Skip system/drm blocks */
+		if ((managment_flag & MEMSTICK_MANAGMENT_FLAG_NORMAL) !=
+			MEMSTICK_MANAGMENT_FLAG_NORMAL) {
+			dbg("pba %05d -> [reserved managment flag %02x]",
+							pba, managment_flag);
+			msb_mark_block_used(msb, pba);
+			continue;
+		}
+
+		/* Erase temporary tables */
+		if (!(managment_flag & MEMSTICK_MANAGEMENT_ATFLG)) {
+			dbg("pba %05d -> [temp table] - will erase", pba);
+
+			msb_mark_block_used(msb, pba);
+			msb_erase_block(msb, pba);
+			continue;
+		}
+
+		if (lba == MS_BLOCK_INVALID) {
+			dbg_verbose("pba %05d -> [free]", pba);
+			continue;
+		}
+
+		msb_mark_block_used(msb, pba);
+
+		/* Block has LBA not according to zoning*/
+		if (msb_get_zone_from_lba(lba) != msb_get_zone_from_pba(pba)) {
+			ms_printk("pba %05d -> [bad lba %05d] - will erase",
+								pba, lba);
+			msb_erase_block(msb, pba);
+			continue;
+		}
+
+		/* No collisions - great */
+		if (msb->lba_to_pba_table[lba] == MS_BLOCK_INVALID) {
+			dbg_verbose("pba %05d -> [lba %05d]", pba, lba);
+			msb->lba_to_pba_table[lba] = pba;
+			continue;
+		}
+
+		other_block = msb->lba_to_pba_table[lba];
+		other_overwrite_flag = overwrite_flags[other_block];
+
+		ms_printk("Collision between pba %d and pba %d",
+			pba, other_block);
+
+		if (!(overwrite_flag & MEMSTICK_OVERWRITE_UDST)) {
+			ms_printk("pba %d is marked as stable, use it", pba);
+			msb_erase_block(msb, other_block);
+			msb->lba_to_pba_table[lba] = pba;
+			continue;
+		}
+
+		if (!(other_overwrite_flag & MEMSTICK_OVERWRITE_UDST)) {
+			ms_printk("pba %d is marked as stable, use it",
+								other_block);
+			msb_erase_block(msb, pba);
+			continue;
+		}
+
+		ms_printk("collision between blocks %d and %d with"
+		" without stable flag set on both, erasing pba %d",
+				pba, other_block, other_block);
+
+		msb_erase_block(msb, other_block);
+		msb->lba_to_pba_table[lba] = pba;
+	}
+
+	dbg("End of media scanning");
+	kfree(overwrite_flags);
+	return 0;
+}
+
+static void msb_cache_flush_timer(unsigned long data)
+{
+	struct msb_data *msb = (struct msb_data *)data;
+	msb->need_flush_cache = true;
+	wake_up_process(msb->io_thread);
+}
+
+
+static void msb_cache_discard(struct msb_data *msb)
+{
+	if (msb->cache_block_lba == MS_BLOCK_INVALID)
+		return;
+
+	del_timer_sync(&msb->cache_flush_timer);
+
+	dbg_verbose("Discarding the write cache");
+	msb->cache_block_lba = MS_BLOCK_INVALID;
+	bitmap_zero(&msb->valid_cache_bitmap, msb->pages_in_block);
+}
+
+static int msb_cache_init(struct msb_data *msb)
+{
+	setup_timer(&msb->cache_flush_timer, msb_cache_flush_timer,
+		(unsigned long)msb);
+
+	if (!msb->cache)
+		msb->cache = kzalloc(msb->block_size, GFP_KERNEL);
+	if (!msb->cache)
+		return -ENOMEM;
+
+	msb_cache_discard(msb);
+	return 0;
+}
+
+static int msb_cache_flush(struct msb_data *msb)
+{
+	struct scatterlist sg;
+	struct ms_extra_data_register extra;
+	int page, offset, error;
+	u16 pba, lba;
+
+	if (msb->read_only)
+		return -EROFS;
+
+	if (msb->cache_block_lba == MS_BLOCK_INVALID)
+		return 0;
+
+	lba = msb->cache_block_lba;
+	pba = msb->lba_to_pba_table[lba];
+
+	dbg_verbose("Flushing the write cache of pba %d (LBA %d)",
+						pba, msb->cache_block_lba);
+
+	/* Read all missing pages in cache */
+	for (page = 0 ; page < msb->pages_in_block ; page++) {
+
+		if (test_bit(page, &msb->valid_cache_bitmap))
+			continue;
+
+		offset = page * msb->page_size;
+		sg_init_one(&sg, msb->cache + offset , msb->page_size);
+
+
+		dbg_verbose("reading non-present sector %d of cache block %d",
+			page, lba);
+		error = msb_read_page(msb, pba, page, &extra, &sg);
+
+		/* Bad pages are copied with 00 page status */
+		if (error == -EBADMSG) {
+			ms_printk("read error on sector %d, contents probably"
+				" damaged", page);
+			continue;
+		}
+
+		if (error)
+			return error;
+
+		if ((extra.overwrite_flag & MEMSTICK_OV_PG_NORMAL) !=
+							MEMSTICK_OV_PG_NORMAL) {
+			dbg("page %d is marked as bad", page);
+			continue;
+		}
+
+		set_bit(page, &msb->valid_cache_bitmap);
+	}
+
+	/* Write the cache now */
+	sg_init_one(&sg, msb->cache , msb->block_size);
+	error = msb_update_block(msb, msb->cache_block_lba, &sg);
+	pba = msb->lba_to_pba_table[msb->cache_block_lba];
+
+	/* Mark invalid pages */
+	if (!error) {
+		for (page = 0 ; page < msb->pages_in_block ; page++) {
+
+			if (test_bit(page, &msb->valid_cache_bitmap))
+				continue;
+
+			dbg("marking page %d as containing damaged data",
+				page);
+			msb_set_overwrite_flag(msb,
+				pba , page, 0xFF & ~MEMSTICK_OV_PG_NORMAL);
+		}
+	}
+
+	msb_cache_discard(msb);
+	return error;
+}
+
+static int msb_cache_write(struct msb_data *msb, int lba,
+		int page, bool add_to_cache_only, struct scatterlist *sg)
+{
+	int error;
+	if (msb->read_only)
+		return -EROFS;
+
+	if (msb->cache_block_lba == MS_BLOCK_INVALID ||
+						lba != msb->cache_block_lba)
+		if (add_to_cache_only)
+			return 0;
+
+	/* If we need to write different block */
+	if (msb->cache_block_lba != MS_BLOCK_INVALID &&
+						lba != msb->cache_block_lba) {
+		dbg_verbose("first flush the cache");
+		error = msb_cache_flush(msb);
+		if (error)
+			return error;
+	}
+
+	if (msb->cache_block_lba  == MS_BLOCK_INVALID) {
+		msb->cache_block_lba  = lba;
+		mod_timer(&msb->cache_flush_timer,
+			jiffies + msecs_to_jiffies(cache_flush_timeout));
+	}
+
+	dbg_verbose("Write of LBA %d page %d to cache ", lba, page);
+
+	sg_copy_to_buffer(sg, 1, msb->cache + page * msb->page_size,
+			msb->page_size);
+	set_bit(page, &msb->valid_cache_bitmap);
+	return 0;
+}
+
+static int msb_cache_read(struct msb_data *msb, int lba,
+					int page, struct scatterlist *sg)
+{
+	int pba = msb->lba_to_pba_table[lba];
+	int error = 0;
+
+	if (lba == msb->cache_block_lba &&
+			test_bit(page, &msb->valid_cache_bitmap)) {
+
+		dbg_verbose("Read of LBA %d (pba %d) sector %d from cache",
+							lba, pba, page);
+		sg_copy_from_buffer(sg, 1,
+			msb->cache + msb->page_size * page,
+							msb->page_size);
+	} else {
+		dbg_verbose("Read of LBA %d (pba %d) sector %d from device",
+							lba, pba, page);
+
+		error = msb_read_page(msb, pba, page, NULL, sg);
+		if (error)
+			return error;
+
+		msb_cache_write(msb, lba, page, true, sg);
+	}
+	return error;
+}
+
+/* Emulated geometry table
+ * This table content isn't that importaint,
+ * One could put here different values, providing that they still
+ * cover whole disk.
+ * 64 MB entry is what windows reports for my 64M memstick */
+
+static const struct chs_entry chs_table[] = {
+/*        size sectors cylynders  heads */
+	{ 4,    16,    247,       2  },
+	{ 8,    16,    495,       2  },
+	{ 16,   16,    495,       4  },
+	{ 32,   16,    991,       4  },
+	{ 64,   16,    991,       8  },
+	{128,   16,    991,       16 },
+	{ 0 }
+};
+
+/* Load information about the card */
+static int msb_init_card(struct memstick_dev *card)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	struct memstick_host *host = card->host;
+	struct ms_boot_page *boot_block;
+	int error = 0, i, raw_size_in_megs;
+
+	card->caps = 0;
+
+	if (card->id.class >= MEMSTICK_CLASS_ROM &&
+				card->id.class <= MEMSTICK_CLASS_ROM)
+		msb->read_only = true;
+
+	error = msb_reset(msb);
+	if (error)
+		return error;
+
+	msb->page_size = sizeof(struct ms_boot_page);
+
+	/* Read the boot page */
+	error = msb_read_boot_blocks(msb);
+	if (error)
+		return -EIO;
+
+	boot_block = &msb->boot_page[0];
+
+	/* Save intersting attributes from boot page */
+	msb->block_count = boot_block->attr.number_of_blocks;
+	msb->page_size = boot_block->attr.page_size;
+
+	msb->pages_in_block = boot_block->attr.block_size * 2;
+	msb->block_size = msb->page_size * msb->pages_in_block;
+
+	if (msb->page_size > PAGE_SIZE) {
+		/* this is nonsensional, so don't bother */
+		dbg("device page %d size isn't supported", msb->page_size);
+		return -EINVAL;
+	}
+
+	msb->block_buffer = kzalloc(msb->block_size, GFP_KERNEL);
+	if (!msb->block_buffer)
+		return -ENOMEM;
+
+	raw_size_in_megs = (msb->block_size * msb->block_count) >> 20;
+
+	for (i = 0 ; chs_table[i].size ; i++) {
+
+		if (chs_table[i].size != raw_size_in_megs)
+			continue;
+
+		msb->geometry.cylinders = chs_table[i].cyl;
+		msb->geometry.heads = chs_table[i].head;
+		msb->geometry.sectors = chs_table[i].sec;
+		break;
+	}
+
+	if (boot_block->attr.transfer_supporting == 1)
+		card->caps |= MEMSTICK_CAP_PAR4;
+
+	if (boot_block->attr.device_type & 0x03)
+		msb->read_only = true;
+
+	dbg("Total block count = %d", msb->block_count);
+	dbg("Each block consists of %d pages", msb->pages_in_block);
+	dbg("Page size = %d bytes", msb->page_size);
+	dbg("Parallel mode supported: %d", !!(card->caps & MEMSTICK_CAP_PAR4));
+	dbg("Read only: %d", msb->read_only);
+
+	/* Now we can switch the interface */
+	if (host->caps & card->caps & MEMSTICK_CAP_PAR4)
+		msb_switch_to_parallel(msb);
+
+
+	error = msb_cache_init(msb);
+	if (error)
+		return error;
+
+	error = msb_ftl_initialize(msb);
+	if (error)
+		return error;
+
+
+	/* Read the bad block table */
+	error = msb_read_bad_block_table(msb, 0);
+
+	if (error && error != -ENOMEM) {
+		dbg("failed to read bad block table from primary boot block,"
+							" trying from backup");
+		error = msb_read_bad_block_table(msb, 1);
+	}
+
+	if (error)
+		return error;
+
+	/* *drum roll* Scan the media */
+	error = msb_ftl_scan(msb);
+	if (error) {
+		ms_printk("Scan of media failed");
+		return error;
+	}
+
+	return 0;
+
+}
+
+static int msb_do_write_request(struct msb_data *msb, int lba,
+		int page, struct scatterlist *sg, int *sucessfuly_written)
+{
+	int error = 0;
+	*sucessfuly_written = 0;
+
+	while (sg) {
+		if (page == 0 && sg_total_len(sg) >= msb->block_size) {
+
+			if (msb->cache_block_lba == lba)
+				msb_cache_discard(msb);
+
+			dbg_verbose("Writing whole lba %d", lba);
+			error = msb_update_block(msb, lba, sg);
+			if (error)
+				return error;
+
+			sg = sg_advance(sg, msb->block_size);
+			*sucessfuly_written += msb->block_size;
+			lba++;
+			continue;
+		}
+
+		error = msb_cache_write(msb, lba, page, false, sg);
+		if (error)
+			return error;
+
+		sg = sg_advance(sg, msb->page_size);
+		*sucessfuly_written += msb->page_size;
+
+		page++;
+		if (page == msb->pages_in_block) {
+			page = 0;
+			lba++;
+		}
+	}
+	return 0;
+}
+
+static int msb_do_read_request(struct msb_data *msb, int lba,
+		int page, struct scatterlist *sg, int *sucessfuly_read)
+{
+	int error = 0;
+	*sucessfuly_read = 0;
+
+	while (sg) {
+
+		error = msb_cache_read(msb, lba, page, sg);
+		if (error)
+			return error;
+
+		sg = sg_advance(sg, msb->page_size);
+		*sucessfuly_read += msb->page_size;
+
+		page++;
+		if (page == msb->pages_in_block) {
+			page = 0;
+			lba++;
+		}
+	}
+	return 0;
+}
+
+static int msb_io_thread(void *data)
+{
+	struct msb_data *msb = data;
+	int page, error, len;
+	sector_t lba;
+	unsigned long flags;
+
+	dbg("IO: thread started");
+
+	while (1) {
+
+		if (kthread_should_stop()) {
+			if (msb->req)
+				blk_requeue_request(msb->queue, msb->req);
+			break;
+		}
+
+		spin_lock_irqsave(&msb->q_lock, flags);
+
+		if (msb->need_flush_cache) {
+			msb->need_flush_cache = false;
+			spin_unlock_irqrestore(&msb->q_lock, flags);
+			msb_cache_flush(msb);
+			continue;
+		}
+
+		if (!msb->req) {
+			msb->req = blk_fetch_request(msb->queue);
+
+			if (!msb->req) {
+				dbg_verbose("IO: no more requests, sleeping");
+				set_current_state(TASK_INTERRUPTIBLE);
+				spin_unlock_irqrestore(&msb->q_lock, flags);
+				schedule();
+				dbg_verbose("IO: thread woken up");
+				continue;
+			}
+		}
+
+		spin_unlock_irqrestore(&msb->q_lock, flags);
+
+		/* If card was removed meanwhile */
+		if (!msb->req)
+			continue;
+
+		/* process the request */
+		dbg_verbose("IO: thread processing new request");
+		blk_rq_map_sg(msb->queue, msb->req, msb->req_sg);
+
+		lba = blk_rq_pos(msb->req);
+
+		sector_div(lba, msb->page_size / 512);
+		page = do_div(lba, msb->pages_in_block);
+
+		if (rq_data_dir(msb->req) == READ)
+			error = msb_do_read_request(
+					msb, lba, page, msb->req_sg, &len);
+		else
+			error = msb_do_write_request(
+					msb, lba, page, msb->req_sg, &len);
+
+		spin_lock_irqsave(&msb->q_lock, flags);
+
+		if (len)
+			if (!__blk_end_request(msb->req, 0, len))
+				msb->req = NULL;
+
+		if (error && msb->req) {
+			dbg_verbose("IO: ending one sector "
+					"of the request with error");
+			if (!__blk_end_request(msb->req, error, msb->page_size))
+				msb->req = NULL;
+		}
+
+		if (msb->req)
+			dbg_verbose("IO: request still pending");
+
+		spin_unlock_irqrestore(&msb->q_lock, flags);
+	}
+	return 0;
+}
+
+static DEFINE_IDR(msb_disk_idr);
+static DEFINE_MUTEX(msb_disk_lock);
+
+static int msb_bd_open(struct block_device *bdev, fmode_t mode)
+{
+	struct gendisk *disk = bdev->bd_disk;
+	struct msb_data *msb = disk->private_data;
+
+	dbg_verbose("block device open");
+
+	mutex_lock(&msb_disk_lock);
+
+	if (msb && msb->card)
+		msb->usage_count++;
+
+	mutex_unlock(&msb_disk_lock);
+	return 0;
+}
+
+static void msb_data_clear(struct msb_data *msb)
+{
+	kfree(msb->boot_page);
+	kfree(msb->used_blocks_bitmap);
+	kfree(msb->lba_to_pba_table);
+	kfree(msb->cache);
+	msb->card = NULL;
+}
+
+static int msb_disk_release(struct gendisk *disk)
+{
+	struct msb_data *msb = disk->private_data;
+	int disk_id = MINOR(disk_devt(disk)) >> MS_BLOCK_PART_SHIFT;
+
+	dbg_verbose("block device release");
+
+	mutex_lock(&msb_disk_lock);
+
+	if (msb) {
+		if (msb->usage_count)
+			msb->usage_count--;
+
+		if (!msb->usage_count) {
+			kfree(msb);
+			disk->private_data = NULL;
+			idr_remove(&msb_disk_idr, disk_id);
+			put_disk(disk);
+		}
+	}
+	mutex_unlock(&msb_disk_lock);
+	return 0;
+}
+
+static int msb_bd_release(struct gendisk *disk, fmode_t mode)
+{
+	return msb_disk_release(disk);
+}
+
+static int msb_bd_getgeo(struct block_device *bdev,
+				 struct hd_geometry *geo)
+{
+	struct msb_data *msb = bdev->bd_disk->private_data;
+	*geo = msb->geometry;
+	return 0;
+}
+
+static int msb_prepare_req(struct request_queue *q, struct request *req)
+{
+	if (req->cmd_type != REQ_TYPE_FS &&
+				req->cmd_type != REQ_TYPE_BLOCK_PC) {
+		blk_dump_rq_flags(req, "MS unsupported request");
+		return BLKPREP_KILL;
+	}
+	req->cmd_flags |= REQ_DONTPREP;
+	return BLKPREP_OK;
+}
+
+static void msb_submit_req(struct request_queue *q)
+{
+	struct memstick_dev *card = q->queuedata;
+	struct msb_data *msb = memstick_get_drvdata(card);
+	struct request *req = NULL;
+
+	dbg_verbose("Submit request");
+
+	if (msb->card_dead) {
+		dbg("Refusing requests on removed card");
+
+		WARN_ON(msb->io_thread);
+
+		while ((req = blk_fetch_request(q)) != NULL)
+			__blk_end_request_all(req, -ENODEV);
+		return;
+	}
+
+	if (msb->req)
+		return;
+
+	if (msb->io_thread)
+		wake_up_process(msb->io_thread);
+}
+
+static int msb_check_card(struct memstick_dev *card)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	return (msb->card_dead == 0);
+}
+
+static void msb_stop(struct memstick_dev *card)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	unsigned long flags;
+	struct task_struct *io_thread;
+
+	dbg("Stopping all msblock IO");
+
+	/* Just stop the IO thread.
+	   Be carefull not to race against submit_request
+	   If it is called, all pending requests will be processed by
+	   the IO thread as soon as msb_start is called */
+
+	spin_lock_irqsave(&msb->q_lock, flags);
+	blk_stop_queue(msb->queue);
+	io_thread = msb->io_thread;
+	msb->io_thread = NULL;
+	spin_unlock_irqrestore(&msb->q_lock, flags);
+
+	del_timer_sync(&msb->cache_flush_timer);
+
+	if (io_thread)
+		kthread_stop(io_thread);
+}
+
+static void msb_start(struct memstick_dev *card)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	unsigned long flags;
+	int disk_id = MINOR(disk_devt(msb->disk)) >> MS_BLOCK_PART_SHIFT;
+
+	dbg("Resuming IO from msblock");
+
+	spin_lock_irqsave(&msb->q_lock, flags);
+	if (msb->io_thread || msb->card_dead) {
+		spin_unlock_irqrestore(&msb->q_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&msb->q_lock, flags);
+
+	/* Kick cache flush anyway, its harmless */
+	msb->need_flush_cache = true;
+
+	msb->io_thread = kthread_run(msb_io_thread, msb, "kms_block%d",
+		disk_id);
+
+	spin_lock_irqsave(&msb->q_lock, flags);
+	blk_start_queue(msb->queue);
+	spin_unlock_irqrestore(&msb->q_lock, flags);
+}
+
+static const struct block_device_operations msb_bdops = {
+	.open    = msb_bd_open,
+	.release = msb_bd_release,
+	.getgeo  = msb_bd_getgeo,
+	.owner   = THIS_MODULE
+};
+
+/* Registers the block device */
+static int msb_init_disk(struct memstick_dev *card)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	struct memstick_host *host = card->host;
+	int rc, disk_id;
+	u64 limit = BLK_BOUNCE_HIGH;
+	unsigned long capacity;
+
+	if (host->dev.dma_mask && *(host->dev.dma_mask))
+		limit = *(host->dev.dma_mask);
+
+	mutex_lock(&msb_disk_lock);
+
+	if (!idr_pre_get(&msb_disk_idr, GFP_KERNEL)) {
+		mutex_unlock(&msb_disk_lock);
+		return -ENOMEM;
+	}
+
+	rc = idr_get_new(&msb_disk_idr, card, &disk_id);
+	mutex_unlock(&msb_disk_lock);
+
+	if (rc)
+		return rc;
+
+	if ((disk_id << MS_BLOCK_PART_SHIFT) > 255) {
+		rc = -ENOSPC;
+		goto out_release_id;
+	}
+
+	msb->disk = alloc_disk(1 << MS_BLOCK_PART_SHIFT);
+	if (!msb->disk) {
+		rc = -ENOMEM;
+		goto out_release_id;
+	}
+
+	msb->queue = blk_init_queue(msb_submit_req, &msb->q_lock);
+	if (!msb->queue) {
+		rc = -ENOMEM;
+		goto out_put_disk;
+	}
+
+	msb->queue->queuedata = card;
+	blk_queue_prep_rq(msb->queue, msb_prepare_req);
+
+	blk_queue_bounce_limit(msb->queue, limit);
+	blk_queue_max_hw_sectors(msb->queue, MS_BLOCK_MAX_PAGES);
+	blk_queue_max_segments(msb->queue, MS_BLOCK_MAX_SEGS);
+	blk_queue_max_segment_size(msb->queue,
+				   MS_BLOCK_MAX_PAGES * msb->page_size);
+	msb->disk->major = major;
+	msb->disk->first_minor = disk_id << MS_BLOCK_PART_SHIFT;
+	msb->disk->fops = &msb_bdops;
+	msb->usage_count = 1;
+	msb->disk->private_data = msb;
+	msb->disk->queue = msb->queue;
+	msb->disk->driverfs_dev = &card->dev;
+	sg_init_table(msb->req_sg, MS_BLOCK_MAX_SEGS+1);
+
+	sprintf(msb->disk->disk_name, "msblk%d", disk_id);
+
+	blk_queue_logical_block_size(msb->queue, msb->page_size);
+
+	capacity = msb->pages_in_block * msb->logical_block_count;
+	capacity *= (msb->page_size / 512);
+
+	set_capacity(msb->disk, capacity);
+	dbg("Set total disk size to %lu sectors", capacity);
+
+	if (msb->read_only)
+		set_disk_ro(msb->disk, 1);
+
+	msb_start(card);
+	add_disk(msb->disk);
+	dbg("Disk added");
+	return 0;
+
+out_put_disk:
+	put_disk(msb->disk);
+out_release_id:
+	mutex_lock(&msb_disk_lock);
+	idr_remove(&msb_disk_idr, disk_id);
+	mutex_unlock(&msb_disk_lock);
+	return rc;
+}
+
+static int msb_probe(struct memstick_dev *card)
+{
+	struct msb_data *msb;
+	int rc = 0;
+
+	msb = kzalloc(sizeof(struct msb_data), GFP_KERNEL);
+	if (!msb)
+		return -ENOMEM;
+	memstick_set_drvdata(card, msb);
+	msb->card = card;
+	spin_lock_init(&msb->q_lock);
+
+	rc = msb_init_card(card);
+	if (rc)
+		goto out_free;
+
+	rc = msb_init_disk(card);
+	if (!rc) {
+		card->check = msb_check_card;
+		card->stop = msb_stop;
+		card->start = msb_start;
+		return 0;
+	}
+out_free:
+	memstick_set_drvdata(card, NULL);
+	msb_data_clear(msb);
+	kfree(msb);
+	return rc;
+}
+
+static void msb_remove(struct memstick_dev *card)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	unsigned long flags;
+
+	if (msb->io_thread)
+		msb_stop(card);
+
+	dbg("Removing the disk device");
+
+	/* Take care of unhandled + new requests from now on */
+	spin_lock_irqsave(&msb->q_lock, flags);
+	msb->card_dead = true;
+	blk_start_queue(msb->queue);
+	spin_unlock_irqrestore(&msb->q_lock, flags);
+
+	/* Remove the disk */
+	del_gendisk(msb->disk);
+	blk_cleanup_queue(msb->queue);
+	msb->queue = NULL;
+
+	mutex_lock(&msb_disk_lock);
+	msb_data_clear(msb);
+	mutex_unlock(&msb_disk_lock);
+
+	msb_disk_release(msb->disk);
+	memstick_set_drvdata(card, NULL);
+}
+
+#ifdef CONFIG_PM
+
+static int msb_suspend(struct memstick_dev *card, pm_message_t state)
+{
+	msb_stop(card);
+	return 0;
+}
+
+static int msb_resume(struct memstick_dev *card)
+{
+	struct msb_data *msb = memstick_get_drvdata(card);
+	struct msb_data *new_msb = NULL;
+	bool card_dead = true;
+
+#ifndef CONFIG_MEMSTICK_UNSAFE_RESUME
+	msb->card_dead = true;
+	return 0;
+#endif
+	mutex_lock(&card->host->lock);
+
+	new_msb = kzalloc(sizeof(struct msb_data), GFP_KERNEL);
+	if (!new_msb)
+		goto out;
+
+	new_msb->card = card;
+	memstick_set_drvdata(card, new_msb);
+
+	if (msb_init_card(card))
+		goto out;
+
+	if (msb->block_size != new_msb->block_size)
+		goto out;
+
+	if (memcmp(msb->boot_page, new_msb->boot_page,
+					sizeof(struct ms_boot_page)))
+		goto out;
+
+	if (msb->logical_block_count != new_msb->logical_block_count ||
+		memcmp(msb->lba_to_pba_table, new_msb->lba_to_pba_table,
+						msb->logical_block_count))
+		goto out;
+
+	if (msb->block_count != new_msb->block_count ||
+		memcmp(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap,
+							msb->block_count / 8))
+		goto out;
+
+	card_dead = false;
+out:
+	if (card_dead)
+		dbg("Card was removed/replaced during suspend");
+
+	msb->card_dead = card_dead;
+	memstick_set_drvdata(card, msb);
+
+	if (new_msb) {
+		msb_data_clear(new_msb);
+		kfree(new_msb);
+	}
+
+	msb_start(card);
+	mutex_unlock(&card->host->lock);
+	return 0;
+}
+#else
+
+#define msb_suspend NULL
+#define msb_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct memstick_device_id msb_id_tbl[] = {
+	{MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE,
+	 MEMSTICK_CLASS_FLASH},
+
+	{MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE,
+	 MEMSTICK_CLASS_ROM},
+
+	{MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE,
+	 MEMSTICK_CLASS_RO},
+
+	{MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE,
+	 MEMSTICK_CLASS_WP},
+
+	{MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_DUO, MEMSTICK_CATEGORY_STORAGE_DUO,
+	 MEMSTICK_CLASS_DUO},
+	{}
+};
+MODULE_DEVICE_TABLE(memstick, msb_id_tbl);
+
+
+static struct memstick_driver msb_driver = {
+	.driver = {
+		.name  = DRIVER_NAME,
+		.owner = THIS_MODULE
+	},
+	.id_table = msb_id_tbl,
+	.probe    = msb_probe,
+	.remove   = msb_remove,
+	.suspend  = msb_suspend,
+	.resume   = msb_resume
+};
+
+static int __init msb_init(void)
+{
+	int rc = -ENOMEM;
+
+	rc = register_blkdev(major, DRIVER_NAME);
+	if (rc < 0) {
+		printk(KERN_ERR DRIVER_NAME ": failed to register "
+		       "major %d, error %d\n", major, rc);
+		return rc;
+	}
+	if (!major)
+		major = rc;
+
+	rc = memstick_register_driver(&msb_driver);
+	if (rc)
+		unregister_blkdev(major, DRIVER_NAME);
+	return rc;
+}
+
+static void __exit msb_exit(void)
+{
+	memstick_unregister_driver(&msb_driver);
+	unregister_blkdev(major, DRIVER_NAME);
+	idr_destroy(&msb_disk_idr);
+}
+
+module_init(msb_init);
+module_exit(msb_exit);
+
+module_param(major, int, S_IRUGO);
+MODULE_PARM_DESC(major, "Major to use for block device (default auto)");
+
+module_param(cache_flush_timeout, int, S_IRUGO);
+MODULE_PARM_DESC(cache_flush_timeout,
+				"Cache flush timeout in msec (1000 default)");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level (0-3)");
+
+module_param(verify_writes, bool, S_IRUGO);
+MODULE_PARM_DESC(verify_writes, "Read back and check all data that is written");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alex Dubov");
+MODULE_AUTHOR("Maxim Levitsky");
+MODULE_DESCRIPTION("Sony MemoryStick block device driver");
diff --git a/drivers/memstick/core/ms_block.h b/drivers/memstick/core/ms_block.h
new file mode 100644
index 0000000..492bfef
--- /dev/null
+++ b/drivers/memstick/core/ms_block.h
@@ -0,0 +1,229 @@
+/*
+ *  ms_block.h - Sony MemoryStick (legacy) storage support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *  Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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.
+ *
+ * Special thanks to Carlos Corbacho for providing various MemoryStick cards
+ * that made this driver possible.
+ *
+ */
+
+#ifndef MS_BLOCK_NEW_H
+#define MS_BLOCK_NEW_H
+
+#define MS_BLOCK_MAX_SEGS      32
+#define MS_BLOCK_MAX_PAGES     ((2 << 16) - 1)
+
+#define MS_BLOCK_MAX_BOOT_ADDR 0x000c
+#define MS_BLOCK_BOOT_ID       0x0001
+#define MS_BLOCK_INVALID       0xffff
+#define MS_MAX_ZONES           16
+#define MS_BLOCKS_IN_ZONE      512
+
+#define MS_BLOCK_MAP_LINE_SZ   16
+#define MS_BLOCK_PART_SHIFT    3
+
+
+#define MEMSTICK_UNCORR_ERROR (MEMSTICK_STATUS1_UCFG | \
+		MEMSTICK_STATUS1_UCEX | MEMSTICK_STATUS1_UCDT)
+
+#define MEMSTICK_CORR_ERROR (MEMSTICK_STATUS1_FGER | MEMSTICK_STATUS1_EXER | \
+	MEMSTICK_STATUS1_DTER)
+
+#define MEMSTICK_INT_ERROR (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)
+
+#define MEMSTICK_OVERWRITE_FLAG_NORMAL \
+	(MEMSTICK_OVERWRITE_PGST1 | \
+	MEMSTICK_OVERWRITE_PGST0  | \
+	MEMSTICK_OVERWRITE_BKST)
+
+#define MEMSTICK_OV_PG_NORMAL \
+	(MEMSTICK_OVERWRITE_PGST1 | MEMSTICK_OVERWRITE_PGST0)
+
+#define MEMSTICK_MANAGMENT_FLAG_NORMAL \
+	(MEMSTICK_MANAGEMENT_SYSFLG |  \
+	MEMSTICK_MANAGEMENT_SCMS1   |  \
+	MEMSTICK_MANAGEMENT_SCMS0)     \
+
+struct ms_boot_header {
+	unsigned short block_id;
+	unsigned short format_reserved;
+	unsigned char  reserved0[184];
+	unsigned char  data_entry;
+	unsigned char  reserved1[179];
+} __packed;
+
+
+struct ms_system_item {
+	unsigned int  start_addr;
+	unsigned int  data_size;
+	unsigned char data_type_id;
+	unsigned char reserved[3];
+} __packed;
+
+struct ms_system_entry {
+	struct ms_system_item disabled_block;
+	struct ms_system_item cis_idi;
+	unsigned char         reserved[24];
+} __packed;
+
+struct ms_boot_attr_info {
+	unsigned char      memorystick_class;
+	unsigned char      format_unique_value1;
+	unsigned short     block_size;
+	unsigned short     number_of_blocks;
+	unsigned short     number_of_effective_blocks;
+	unsigned short     page_size;
+	unsigned char      extra_data_size;
+	unsigned char      format_unique_value2;
+	unsigned char      assembly_time[8];
+	unsigned char      format_unique_value3;
+	unsigned char      serial_number[3];
+	unsigned char      assembly_manufacturer_code;
+	unsigned char      assembly_model_code[3];
+	unsigned short     memory_manufacturer_code;
+	unsigned short     memory_device_code;
+	unsigned short     implemented_capacity;
+	unsigned char      format_unique_value4[2];
+	unsigned char      vcc;
+	unsigned char      vpp;
+	unsigned short     controller_number;
+	unsigned short     controller_function;
+	unsigned char      reserved0[9];
+	unsigned char      transfer_supporting;
+	unsigned short     format_unique_value5;
+	unsigned char      format_type;
+	unsigned char      memorystick_application;
+	unsigned char      device_type;
+	unsigned char      reserved1[22];
+	unsigned char      format_uniqure_value6[2];
+	unsigned char      reserved2[15];
+} __packed;
+
+struct ms_cis_idi {
+	unsigned short general_config;
+	unsigned short logical_cylinders;
+	unsigned short reserved0;
+	unsigned short logical_heads;
+	unsigned short track_size;
+	unsigned short page_size;
+	unsigned short pages_per_track;
+	unsigned short msw;
+	unsigned short lsw;
+	unsigned short reserved1;
+	unsigned char  serial_number[20];
+	unsigned short buffer_type;
+	unsigned short buffer_size_increments;
+	unsigned short long_command_ecc;
+	unsigned char  firmware_version[28];
+	unsigned char  model_name[18];
+	unsigned short reserved2[5];
+	unsigned short pio_mode_number;
+	unsigned short dma_mode_number;
+	unsigned short field_validity;
+	unsigned short current_logical_cylinders;
+	unsigned short current_logical_heads;
+	unsigned short current_pages_per_track;
+	unsigned int   current_page_capacity;
+	unsigned short mutiple_page_setting;
+	unsigned int   addressable_pages;
+	unsigned short single_word_dma;
+	unsigned short multi_word_dma;
+	unsigned char  reserved3[128];
+} __packed;
+
+
+struct ms_boot_page {
+	struct ms_boot_header    header;
+	struct ms_system_entry   entry;
+	struct ms_boot_attr_info attr;
+} __packed;
+
+struct msb_data {
+	unsigned int			usage_count;
+	struct memstick_dev		*card;
+	struct gendisk			*disk;
+	struct request_queue		*queue;
+	spinlock_t			q_lock;
+	struct hd_geometry		geometry;
+	struct attribute_group		attr_group;
+	struct request			*req;
+
+	/* IO */
+	struct task_struct		*io_thread;
+	bool				card_dead;
+
+	/* Media properties */
+	struct ms_boot_page		*boot_page;
+	u16				boot_block_locations[2];
+	int				boot_block_count;
+
+	bool				read_only;
+	unsigned short			page_size;
+	int				block_size;
+	int				pages_in_block;
+	int				zone_count;
+	int				block_count;
+	int				logical_block_count;
+
+	/* FTL tables */
+	unsigned long			*used_blocks_bitmap;
+	unsigned long			*erased_blocks_bitmap;
+	u16				*lba_to_pba_table;
+	int				free_block_count[MS_MAX_ZONES];
+	bool				ftl_initialized;
+
+	/* Cache */
+	unsigned char			*cache;
+	unsigned long			valid_cache_bitmap;
+	int				cache_block_lba;
+	bool				need_flush_cache;
+	struct timer_list		cache_flush_timer;
+
+
+	struct scatterlist		req_sg[MS_BLOCK_MAX_SEGS+1];
+	unsigned char			*block_buffer;
+
+	/* handler's local data */
+	u8				command_value;
+	bool				command_need_oob;
+	struct scatterlist		*current_sg;
+	int				sg_offset;
+
+	struct ms_register		regs;
+	int				current_page;
+};
+
+
+struct chs_entry {
+	unsigned long size;
+	unsigned char sec;
+	unsigned short cyl;
+	unsigned char head;
+};
+
+static int msb_reset(struct msb_data *msb);
+
+
+#define DRIVER_NAME "ms_block"
+
+#define ms_printk(format, ...) \
+	printk(KERN_INFO DRIVER_NAME ": " format "\n", ## __VA_ARGS__)
+
+#define __dbg(level, format, ...) \
+	do { \
+		if (debug >= level) \
+			printk(KERN_DEBUG DRIVER_NAME \
+				": " format "\n", ## __VA_ARGS__); \
+	} while (0)
+
+
+#define dbg(format, ...)		__dbg(1, format, ## __VA_ARGS__)
+#define dbg_verbose(format, ...)	__dbg(2, format, ## __VA_ARGS__)
+
+#endif
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 5/6] memstick: mspro_block: refactoring.
  2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
                   ` (3 preceding siblings ...)
  2010-10-16 23:12 ` [PATCH 4/6] memstick: add support for legacy memorysticks Maxim Levitsky
@ 2010-10-16 23:12 ` Maxim Levitsky
  2010-10-16 23:12 ` [PATCH 6/6] memstick: stop passing useless pointer to card->current_mrq + refactoring Maxim Levitsky
  2010-10-17 22:55 ` [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
  6 siblings, 0 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-16 23:12 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML, Maxim Levitsky

This patch is a result of my attempt to make mspro_block to use the
new helpers I wrote for my ms_block

Among the way, I added support for CMDEX, a new feature of PRO sticks,
and that gives nice speed improvments.

Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 drivers/memstick/core/mspro_block.c | 1097 +++++++++++++++--------------------
 drivers/memstick/core/mspro_block.h |  176 ++++++
 2 files changed, 653 insertions(+), 620 deletions(-)
 create mode 100644 drivers/memstick/core/mspro_block.h

diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index d3f1a08..c589d26 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -2,6 +2,7 @@
  *  Sony MemoryStick Pro storage support
  *
  *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *  Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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
@@ -11,7 +12,6 @@
  * that made this driver possible.
  *
  */
-
 #include <linux/blkdev.h>
 #include <linux/idr.h>
 #include <linux/hdreg.h>
@@ -20,158 +20,14 @@
 #include <linux/slab.h>
 #include <linux/smp_lock.h>
 #include <linux/memstick.h>
-
-#define DRIVER_NAME "mspro_block"
+#include "mspro_block.h"
 
 static int major;
-module_param(major, int, 0644);
-
-#define MSPRO_BLOCK_MAX_SEGS  32
-#define MSPRO_BLOCK_MAX_PAGES ((2 << 16) - 1)
-
-#define MSPRO_BLOCK_SIGNATURE        0xa5c3
-#define MSPRO_BLOCK_MAX_ATTRIBUTES   41
-
-#define MSPRO_BLOCK_PART_SHIFT 3
-
-enum {
-	MSPRO_BLOCK_ID_SYSINFO         = 0x10,
-	MSPRO_BLOCK_ID_MODELNAME       = 0x15,
-	MSPRO_BLOCK_ID_MBR             = 0x20,
-	MSPRO_BLOCK_ID_PBR16           = 0x21,
-	MSPRO_BLOCK_ID_PBR32           = 0x22,
-	MSPRO_BLOCK_ID_SPECFILEVALUES1 = 0x25,
-	MSPRO_BLOCK_ID_SPECFILEVALUES2 = 0x26,
-	MSPRO_BLOCK_ID_DEVINFO         = 0x30
-};
-
-struct mspro_sys_attr {
-	size_t                  size;
-	void                    *data;
-	unsigned char           id;
-	char                    name[32];
-	struct device_attribute dev_attr;
-};
-
-struct mspro_attr_entry {
-	__be32 address;
-	__be32 size;
-	unsigned char id;
-	unsigned char reserved[3];
-} __attribute__((packed));
-
-struct mspro_attribute {
-	__be16 signature;
-	unsigned short          version;
-	unsigned char           count;
-	unsigned char           reserved[11];
-	struct mspro_attr_entry entries[];
-} __attribute__((packed));
-
-struct mspro_sys_info {
-	unsigned char  class;
-	unsigned char  reserved0;
-	__be16 block_size;
-	__be16 block_count;
-	__be16 user_block_count;
-	__be16 page_size;
-	unsigned char  reserved1[2];
-	unsigned char  assembly_date[8];
-	__be32 serial_number;
-	unsigned char  assembly_maker_code;
-	unsigned char  assembly_model_code[3];
-	__be16 memory_maker_code;
-	__be16 memory_model_code;
-	unsigned char  reserved2[4];
-	unsigned char  vcc;
-	unsigned char  vpp;
-	__be16 controller_number;
-	__be16 controller_function;
-	__be16 start_sector;
-	__be16 unit_size;
-	unsigned char  ms_sub_class;
-	unsigned char  reserved3[4];
-	unsigned char  interface_type;
-	__be16 controller_code;
-	unsigned char  format_type;
-	unsigned char  reserved4;
-	unsigned char  device_type;
-	unsigned char  reserved5[7];
-	unsigned char  mspro_id[16];
-	unsigned char  reserved6[16];
-} __attribute__((packed));
-
-struct mspro_mbr {
-	unsigned char boot_partition;
-	unsigned char start_head;
-	unsigned char start_sector;
-	unsigned char start_cylinder;
-	unsigned char partition_type;
-	unsigned char end_head;
-	unsigned char end_sector;
-	unsigned char end_cylinder;
-	unsigned int  start_sectors;
-	unsigned int  sectors_per_partition;
-} __attribute__((packed));
-
-struct mspro_specfile {
-	char           name[8];
-	char           ext[3];
-	unsigned char  attr;
-	unsigned char  reserved[10];
-	unsigned short time;
-	unsigned short date;
-	unsigned short cluster;
-	unsigned int   size;
-} __attribute__((packed));
-
-struct mspro_devinfo {
-	__be16 cylinders;
-	__be16 heads;
-	__be16 bytes_per_track;
-	__be16 bytes_per_sector;
-	__be16 sectors_per_track;
-	unsigned char  reserved[6];
-} __attribute__((packed));
-
-struct mspro_block_data {
-	struct memstick_dev   *card;
-	unsigned int          usage_count;
-	unsigned int          caps;
-	struct gendisk        *disk;
-	struct request_queue  *queue;
-	struct request        *block_req;
-	spinlock_t            q_lock;
-
-	unsigned short        page_size;
-	unsigned short        cylinders;
-	unsigned short        heads;
-	unsigned short        sectors_per_track;
-
-	unsigned char         system;
-	unsigned char         read_only:1,
-			      eject:1,
-			      has_request:1,
-			      data_dir:1,
-			      active:1;
-	unsigned char         transfer_cmd;
-
-	int                   (*mrq_handler)(struct memstick_dev *card,
-					     struct memstick_request **mrq);
-
-	struct attribute_group attr_group;
-
-	struct scatterlist    req_sg[MSPRO_BLOCK_MAX_SEGS];
-	unsigned int          seg_count;
-	unsigned int          current_seg;
-	unsigned int          current_page;
-};
+static int debug;
 
 static DEFINE_IDR(mspro_block_disk_idr);
 static DEFINE_MUTEX(mspro_block_disk_lock);
 
-static int mspro_block_complete_req(struct memstick_dev *card, int error);
-
 /*** Block device ***/
 
 static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode)
@@ -180,7 +36,6 @@ static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode)
 	struct mspro_block_data *msb = disk->private_data;
 	int rc = -ENXIO;
 
-	lock_kernel();
 	mutex_lock(&mspro_block_disk_lock);
 
 	if (msb && msb->card) {
@@ -192,8 +47,6 @@ static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode)
 	}
 
 	mutex_unlock(&mspro_block_disk_lock);
-	unlock_kernel();
-
 	return rc;
 }
 
@@ -218,16 +71,13 @@ static int mspro_block_disk_release(struct gendisk *disk)
 	}
 
 	mutex_unlock(&mspro_block_disk_lock);
-
 	return 0;
 }
 
 static int mspro_block_bd_release(struct gendisk *disk, fmode_t mode)
 {
 	int ret;
-	lock_kernel();
 	ret = mspro_block_disk_release(disk);
-	unlock_kernel();
 	return ret;
 }
 
@@ -522,263 +372,483 @@ static sysfs_show_t mspro_block_attr_show(unsigned char tag)
  * finished (and request processor should come back some time later).
  */
 
-static int h_mspro_block_req_init(struct memstick_dev *card,
-				  struct memstick_request **mrq)
+static int h_mspro_block_reset(struct memstick_dev *card,
+					struct memstick_request **mrq)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
+	struct ms_status_register *status;
+	u8 intreg;
 
-	*mrq = &card->current_mrq;
-	card->next_request = msb->mrq_handler;
-	return 0;
-}
+	memstick_allocate_request(card, mrq);
+	if ((*mrq)->error) {
+		dbg(card, "Error here");
+		return memstick_complete_request(card, *mrq, 0);
+	}
 
-static int h_mspro_block_default(struct memstick_dev *card,
-				 struct memstick_request **mrq)
-{
-	return mspro_block_complete_req(card, (*mrq)->error);
-}
+again:
+	switch (card->state) {
 
-static int h_mspro_block_default_bad(struct memstick_dev *card,
-				     struct memstick_request **mrq)
-{
-	return -ENXIO;
-}
+	case 0: /* send request for INT reg */
+		if (memstick_read_int_reg(card, *mrq, 10000))
+			break;
+		card->state++;
 
-static int h_mspro_block_get_ro(struct memstick_dev *card,
-				struct memstick_request **mrq)
-{
-	struct mspro_block_data *msb = memstick_get_drvdata(card);
+	case 1: /* process the INT reg and loop if nessesary */
+		intreg = (*mrq)->data[0];
 
-	if (!(*mrq)->error) {
-		if ((*mrq)->data[offsetof(struct ms_status_register, status0)]
-		    & MEMSTICK_STATUS0_WP)
-			msb->read_only = 1;
-		else
-			msb->read_only = 0;
+		if (intreg & MEMSTICK_INT_ERR) {
+			if ((intreg & MEMSTICK_INT_CMDNAK)) {
+				msb->read_only = true;
+				return memstick_complete_request(card, *mrq, 0);
+			}
+			return memstick_complete_request(card, *mrq, -EIO);
+		}
+
+		if (intreg & MEMSTICK_INT_CMDNAK)
+			return memstick_complete_request(card, *mrq, -EIO);
+
+
+		if (!(intreg & MEMSTICK_INT_CED)) {
+			card->state--;
+			goto again;
+		}
+
+		memstick_read_int_reg_cleanup(card);
+		card->state++;
+
+	case 2: /* read the R/O status */
+
+		if (!memstick_read_regs(card,
+			offsetof(struct mspro_register, status),
+			sizeof(struct ms_status_register),
+			*mrq))
+			return 0;
+		break;
+
+	case 3: /* process the result & done */
+		status = (struct ms_status_register *)(*mrq)->data;
+		msb->read_only = status->status0 & MEMSTICK_STATUS0_WP;
+		return memstick_complete_request(card, *mrq, 0);
 	}
 
-	return mspro_block_complete_req(card, (*mrq)->error);
+	card->state++;
+	return 0;
+
 }
 
-static int h_mspro_block_wait_for_ced(struct memstick_dev *card,
-				      struct memstick_request **mrq)
+static int h_mspro_block_switch_interface(struct memstick_dev *card,
+					struct memstick_request **mrq)
 {
-	dev_dbg(&card->dev, "wait for ced: value %x\n", (*mrq)->data[0]);
+	struct mspro_block_data *msb = memstick_get_drvdata(card);
+	int error;
+	memstick_allocate_request(card, mrq);
+	if ((*mrq)->error)
+		return memstick_complete_request(card, *mrq, 0);
+
+	switch (card->state) {
 
-	if (!(*mrq)->error) {
-		if ((*mrq)->data[0] & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR))
-			(*mrq)->error = -EFAULT;
-		else if (!((*mrq)->data[0] & MEMSTICK_INT_CED))
+	case 0: /* send switch command to the device */
+		if (!memstick_write_regs(card,
+			offsetof(struct mspro_register, param),
+			1,
+			&msb->system,
+			*mrq))
 			return 0;
+		break;
+
+	case 1: /* switch the host intrface + send dummy read to verify that */
+		/* connection works */
+		error = card->host->set_param(card->host,
+				MEMSTICK_INTERFACE, msb->target_interface);
+		if (error)
+			return memstick_complete_request(card, *mrq, -EFAULT);
+
+		memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
+		break;
+
+	case 2: /* done */
+		return memstick_complete_request(card, *mrq, 0);
+
 	}
 
-	return mspro_block_complete_req(card, (*mrq)->error);
+	card->state++;
+	return 0;
 }
 
+/* State machine that handles the IO. Heart of the driver */
 static int h_mspro_block_transfer_data(struct memstick_dev *card,
 				       struct memstick_request **mrq)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	unsigned char t_val = 0;
+	unsigned char intreg = 0, command;
 	struct scatterlist t_sg = { 0 };
+	unsigned long flags;
+	int error;
 	size_t t_offset;
 
-	if ((*mrq)->error)
-		return mspro_block_complete_req(card, (*mrq)->error);
+	memstick_allocate_request(card, mrq);
 
-	switch ((*mrq)->tpc) {
-	case MS_TPC_WRITE_REG:
-		memstick_init_req(*mrq, MS_TPC_SET_CMD, &msb->transfer_cmd, 1);
-		(*mrq)->need_card_int = 1;
-		return 0;
-	case MS_TPC_SET_CMD:
-		t_val = (*mrq)->int_reg;
-		memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
-		if (msb->caps & MEMSTICK_CAP_AUTO_GET_INT)
-			goto has_int_reg;
-		return 0;
-	case MS_TPC_GET_INT:
-		t_val = (*mrq)->data[0];
-has_int_reg:
-		if (t_val & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) {
-			t_val = MSPRO_CMD_STOP;
-			memstick_init_req(*mrq, MS_TPC_SET_CMD, &t_val, 1);
-			card->next_request = h_mspro_block_default;
-			return 0;
+	if ((*mrq)->error) {
+		dbg(card, "IO: error (%d) executing %s", (*mrq)->error,
+			memstick_debug_get_tpc_name((*mrq)->tpc));
+		msb->io_error = true;
+		card->state = 5;
+	}
+again:
+	switch (card->state) {
+
+	case 0: /* send read/write command + args */
+		memstick_init_req(*mrq, MS_TPC_EX_SET_CMD,
+					&msb->arg, sizeof(msb->arg));
+		break;
+
+	case 1: /* read the INT register */
+		if (memstick_read_int_reg(card, *mrq, -1))
+			break;
+		card->state++;
+
+	case 2: /* process the int register */
+		intreg = (*mrq)->data[0];
+
+		if (intreg & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) {
+			dbg(card, "IO: card I/O error");
+			card->state = 5;
+			msb->io_error = true;
+			goto again;
 		}
 
-		if (msb->current_page
-		    == (msb->req_sg[msb->current_seg].length
-			/ msb->page_size)) {
-			msb->current_page = 0;
-			msb->current_seg++;
-
-			if (msb->current_seg == msb->seg_count) {
-				if (t_val & MEMSTICK_INT_CED) {
-					return mspro_block_complete_req(card,
-									0);
-				} else {
-					card->next_request
-						= h_mspro_block_wait_for_ced;
-					memstick_init_req(*mrq, MS_TPC_GET_INT,
-							  NULL, 1);
-					return 0;
-				}
+		/* we need CED for last page */
+		if (!msb->current_sg) {
+			if (!(intreg & MEMSTICK_INT_CED)) {
+				card->state--;
+				goto again;
 			}
+
+			memstick_read_int_reg_cleanup(card);
+			card->state = 6;
+			goto again;
 		}
 
-		if (!(t_val & MEMSTICK_INT_BREQ)) {
-			memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
-			return 0;
+		/* And BREQ for non last... */
+		if (!(intreg & MEMSTICK_INT_BREQ)) {
+			card->state--;
+			goto again;
 		}
 
-		t_offset = msb->req_sg[msb->current_seg].offset;
-		t_offset += msb->current_page * msb->page_size;
+		memstick_read_int_reg_cleanup(card);
+		card->state++;
+
+	case 3: /* init transfer of the data */
+		t_offset = msb->current_sg->offset;
+		t_offset += msb->current_sg_offset;
 
+		sg_init_table(&t_sg, 1);
 		sg_set_page(&t_sg,
-			    nth_page(sg_page(&(msb->req_sg[msb->current_seg])),
-				     t_offset >> PAGE_SHIFT),
-			    msb->page_size, offset_in_page(t_offset));
+			nth_page(sg_page(msb->current_sg),
+			t_offset >> PAGE_SHIFT),
+			msb->page_size,
+			offset_in_page(t_offset));
 
 		memstick_init_req_sg(*mrq, msb->data_dir == READ
 					   ? MS_TPC_READ_LONG_DATA
 					   : MS_TPC_WRITE_LONG_DATA,
 				     &t_sg);
 		(*mrq)->need_card_int = 1;
-		return 0;
-	case MS_TPC_READ_LONG_DATA:
-	case MS_TPC_WRITE_LONG_DATA:
-		msb->current_page++;
-		if (msb->caps & MEMSTICK_CAP_AUTO_GET_INT) {
-			t_val = (*mrq)->int_reg;
-			goto has_int_reg;
-		} else {
-			memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
-			return 0;
+		break;
+
+	case 4: /* switch to next page */
+		msb->current_sg_offset += msb->page_size;
+		msb->data_transferred += msb->page_size;
+
+		if (msb->current_sg_offset == msb->current_sg->length) {
+			msb->current_sg_offset = 0;
+			msb->current_sg = sg_next(msb->current_sg);
 		}
 
+		card->state = 1;
+		goto again;
+
+	case 5: /* after a error send STOP command */
+		command = MSPRO_CMD_STOP;
+		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
+		break;
+
+	case 6: /* request complete - get next one*/
+		spin_lock_irqsave(&msb->q_lock, flags);
+
+		if (msb->io_error)
+			(*mrq)->error = -EIO;
+
+		if (msb->block_req) {
+			mspro_block_complete_req(card, (*mrq)->error);
+			error = mspro_block_issue_req(card);
+
+			if (!msb->block_req) {
+				dbg_v(card, "IO: out of requests");
+				memstick_complete_request(card, *mrq, 0);
+				spin_unlock_irqrestore(&msb->q_lock, flags);
+				return -EAGAIN;
+			}
+
+			spin_unlock_irqrestore(&msb->q_lock, flags);
+			(*mrq)->error = 0;
+			card->state = 0;
+			goto again;
+
+		} else {
+			spin_unlock_irqrestore(&msb->q_lock, flags);
+			return memstick_complete_request(card, *mrq, 0);
+		}
 	default:
 		BUG();
 	}
+
+	card->state++;
+	return 0;
 }
 
-/*** Data transfer ***/
+/* Common helper that prepares state for IO (read, write, read attributes) */
+static int mspro_block_setup_io(struct memstick_dev *card, int direction,
+				int sector, int pages, unsigned char command)
+{
+	struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+	msb->current_sg_offset = 0;
+	msb->current_sg = msb->req_sg;
+	msb->data_transferred = 0;
+	msb->io_error = false;
+
+	msb->arg.command = command;
+	msb->arg.data_count = cpu_to_be16(pages);
+	msb->arg.data_address = cpu_to_be32(sector);
+	msb->data_dir = direction;
+	return 0;
+}
 
-static int mspro_block_issue_req(struct memstick_dev *card, int chunk)
+/*
+ * Start execution of next block request,
+ * or continue execution of the current one
+ */
+static int mspro_block_issue_req(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	sector_t t_sec;
-	unsigned int count;
-	struct mspro_param_register param;
-
-try_again:
-	while (chunk) {
-		msb->current_page = 0;
-		msb->current_seg = 0;
-		msb->seg_count = blk_rq_map_sg(msb->block_req->q,
-					       msb->block_req,
-					       msb->req_sg);
-
-		if (!msb->seg_count) {
-			chunk = __blk_end_request_cur(msb->block_req, -ENOMEM);
-			continue;
+	sector_t sec;
+	bool is_read;
+	unsigned int pages;
+again:
+	/* get next request from block core if we don't have one */
+	if (!msb->block_req) {
+		dbg(card, "IO: fetching new request from block queue");
+		msb->block_req = blk_fetch_request(msb->queue);
+		if (!msb->block_req) {
+			dbg(card, "IO: failed, because queue is empty");
+			return -EAGAIN;
 		}
+	}
 
-		t_sec = blk_rq_pos(msb->block_req) << 9;
-		sector_div(t_sec, msb->page_size);
+	sec = blk_rq_pos(msb->block_req) << 9;
+	sector_div(sec, msb->page_size);
+	pages = blk_rq_bytes(msb->block_req) / msb->page_size;
 
-		count = blk_rq_bytes(msb->block_req);
-		count /= msb->page_size;
+	if (!blk_rq_map_sg(msb->block_req->q, msb->block_req, msb->req_sg)) {
+		dbg(card, "IO: out of memory");
+		if (!__blk_end_request_cur(msb->block_req, -ENOMEM))
+			msb->block_req = NULL;
+		goto again;
+	}
 
-		param.system = msb->system;
-		param.data_count = cpu_to_be16(count);
-		param.data_address = cpu_to_be32((uint32_t)t_sec);
-		param.tpc_param = 0;
+	is_read = rq_data_dir(msb->block_req) == READ;
 
-		msb->data_dir = rq_data_dir(msb->block_req);
-		msb->transfer_cmd = msb->data_dir == READ
-				    ? MSPRO_CMD_READ_DATA
-				    : MSPRO_CMD_WRITE_DATA;
+	mspro_block_setup_io(card, rq_data_dir(msb->block_req), sec, pages,
+		is_read ? MSPRO_CMD_READ_DATA : MSPRO_CMD_WRITE_DATA);
 
-		dev_dbg(&card->dev, "data transfer: cmd %x, "
-			"lba %x, count %x\n", msb->transfer_cmd,
-			be32_to_cpu(param.data_address), count);
+	dbg(card, "IO: %s: lba %x, pages %x", is_read ? "read" : "write",
+					(unsigned int)sec, pages);
 
-		card->next_request = h_mspro_block_req_init;
-		msb->mrq_handler = h_mspro_block_transfer_data;
-		memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
-				  &param, sizeof(param));
-		memstick_new_req(card->host);
-		return 0;
+	/* can't use memstick_run_state_machine because we don't block */
+	card->next_request = h_mspro_block_transfer_data;
+	return 0;
+}
+
+/*
+ * Completes execution of current block request.
+ *  After execution of this function, the msb->block_req might or might not
+ *  be NULL. If it is, it means we don't have any more requests to process
+ */
+static void mspro_block_complete_req(struct memstick_dev *card, int error)
+{
+	struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+	dbg(card, "IO: transferred %d bytes ", msb->data_transferred);
+
+	if (msb->data_transferred) {
+		if (!__blk_end_request(msb->block_req, 0,
+						msb->data_transferred))
+			msb->block_req = NULL;
 	}
 
-	dev_dbg(&card->dev, "blk_fetch\n");
-	msb->block_req = blk_fetch_request(msb->queue);
-	if (!msb->block_req) {
-		dev_dbg(&card->dev, "issue end\n");
-		return -EAGAIN;
+	if (error && msb->block_req) {
+		dbg(card, "IO: failure (%d) of transfter of one sector", error);
+		if (!__blk_end_request(msb->block_req, 0, msb->page_size))
+			msb->block_req = NULL;
 	}
+}
+
+/* Read data from attribute space */
+static int mspro_block_read_attribute(struct memstick_dev *card,
+					int offset, void *buffer, int len)
+{
+	struct mspro_block_data *msb = memstick_get_drvdata(card);
 
-	dev_dbg(&card->dev, "trying again\n");
-	chunk = 1;
-	goto try_again;
+	dbg(card, "reading attribute space at offset %08x, size %d",
+								offset, len);
+	sg_init_one(&msb->req_sg[0], buffer, len);
+	mspro_block_setup_io(card, READ, offset / msb->page_size,
+				len / msb->page_size, MSPRO_CMD_READ_ATRB);
+	return memstick_run_state_machine(card, h_mspro_block_transfer_data);
 }
 
-static int mspro_block_complete_req(struct memstick_dev *card, int error)
+/* Memory allocated for attributes by this function should be freed by
+ * mspro_block_data_clear, no matter if the initialization process succeded
+ * or failed.
+ */
+static int mspro_block_read_attributes(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	int chunk, cnt;
-	unsigned int t_len = 0;
-	unsigned long flags;
+	struct mspro_attribute *attr = NULL;
+	struct mspro_sys_attr *s_attr = NULL;
 
-	spin_lock_irqsave(&msb->q_lock, flags);
-	dev_dbg(&card->dev, "complete %d, %d\n", msb->has_request ? 1 : 0,
-		error);
+	unsigned char *buffer = NULL;
+	unsigned int buffer_size, new_buffer_size, buffer_address = 0;
+	unsigned int attr_size, attr_addr, attr_page_offset;
 
-	if (msb->has_request) {
-		/* Nothing to do - not really an error */
-		if (error == -EAGAIN)
-			error = 0;
+	int cnt, error, attr_count;
 
-		if (error || (card->current_mrq.tpc == MSPRO_CMD_STOP)) {
-			if (msb->data_dir == READ) {
-				for (cnt = 0; cnt < msb->current_seg; cnt++)
-					t_len += msb->req_sg[cnt].length
-						 / msb->page_size;
+	/* Allocate initial buffer */
+	buffer_size = msb->page_size;
+	buffer_address = 0;
+	buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
 
-					if (msb->current_page)
-						t_len += msb->current_page - 1;
+	/* Read the attribute list */
+	error = mspro_block_read_attribute(card, 0, buffer, buffer_size);
+	if (error)
+		goto out_free_buffer;
 
-					t_len *= msb->page_size;
-			}
-		} else
-			t_len = blk_rq_bytes(msb->block_req);
+	attr = kmalloc(buffer_size, GFP_KERNEL);
+	if (!attr)
+		goto out_free_buffer;
+	memcpy(attr, buffer, buffer_size);
+
+
+	/* Test it for sanity */
+	if (be16_to_cpu(attr->signature) != MSPRO_BLOCK_SIGNATURE) {
+		printk(KERN_ERR "%s: unrecognized device signature %x\n",
+		       dev_name(&card->dev), be16_to_cpu(attr->signature));
+		error = -ENODEV;
+		goto out_free_buffer;
+	}
 
-		dev_dbg(&card->dev, "transferred %x (%d)\n", t_len, error);
+	if (attr->count > MSPRO_BLOCK_MAX_ATTRIBUTES)
+		printk(KERN_WARNING "%s: way too many attribute entries\n",
+		       dev_name(&card->dev));
 
-		if (error && !t_len)
-			t_len = blk_rq_cur_bytes(msb->block_req);
+	attr_count = min((int)attr->count, (int)MSPRO_BLOCK_MAX_ATTRIBUTES);
 
-		chunk = __blk_end_request(msb->block_req, error, t_len);
+	/* Allocate attribute group */
+	error = -ENOMEM;
+	msb->attr_group.attrs = kzalloc((attr_count + 1) *
+					sizeof(struct attribute), GFP_KERNEL);
+	if (!msb->attr_group.attrs)
+		goto out_free_buffer;
+
+	msb->attr_group.name = "media_attributes";
+
+	/* Read all other attributes */
+	for (cnt = 0; cnt < attr_count; ++cnt) {
+
+		/* Create new sysfs attribute */
+		s_attr = kzalloc(sizeof(struct mspro_sys_attr), GFP_KERNEL);
+		error = -ENOMEM;
+		if (!s_attr)
+			goto out_free_buffer;
 
-		error = mspro_block_issue_req(card, chunk);
+		sysfs_attr_init(&s_attr->dev_attr.attr);
+		s_attr->dev_attr.attr.name = s_attr->name;
+		s_attr->dev_attr.attr.mode = S_IRUGO;
+		s_attr->id = attr->entries[cnt].id;
+		s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id);
 
-		if (!error)
-			goto out;
+		if (mspro_block_attr_name(s_attr->id))
+			snprintf(s_attr->name, sizeof(s_attr->name), "%s",
+				 mspro_block_attr_name(attr->entries[cnt].id));
 		else
-			msb->has_request = 0;
-	} else {
-		if (!error)
-			error = -EAGAIN;
+			snprintf(s_attr->name, sizeof(s_attr->name),
+				 "attr_x%02x", attr->entries[cnt].id);
+
+		msb->attr_group.attrs[cnt] = &s_attr->dev_attr.attr;
+
+
+		attr_addr = be32_to_cpu(attr->entries[cnt].address);
+		attr_size = be32_to_cpu(attr->entries[cnt].size);
+		attr_page_offset = attr_addr % msb->page_size;
+
+		if (!attr_size)
+			continue;
+
+		dbg(card, "adding attribute %d: id %x, address %x, size %x",
+			cnt, s_attr->id, attr_addr, attr_size);
+
+		s_attr->size = attr_size;
+		s_attr->data = kmalloc(attr_size, GFP_KERNEL);
+		if (!s_attr->data) {
+			error = -ENOMEM;
+			goto out_free_buffer;
+		}
+
+		/* If we already read that attribute as part of last one,
+								use that */
+		if (round_down(attr_addr, msb->page_size) == buffer_address &&
+		    attr_addr + attr_size <= buffer_address + buffer_size) {
+
+			memcpy(s_attr->data, buffer + attr_page_offset,
+								attr_size);
+			continue;
+		}
+
+		/* Reallocate buffer if nessesary */
+		new_buffer_size = roundup(attr_page_offset + attr_size,
+								msb->page_size);
+		buffer_address = round_down(attr_addr, msb->page_size);
+
+		if (new_buffer_size > buffer_size) {
+			kfree(buffer);
+			buffer_size = new_buffer_size;
+			buffer = kmalloc(buffer_size, GFP_KERNEL);
+			if (!buffer)
+				goto out_free_buffer;
+
+		}
+
+		/* Finaly read and save the attrubute */
+		error = mspro_block_read_attribute(card,
+			buffer_address,
+			buffer, new_buffer_size);
+		memcpy(s_attr->data, buffer + attr_page_offset, attr_size);
 	}
 
-	card->next_request = h_mspro_block_default_bad;
-	complete_all(&card->mrq_complete);
-out:
-	spin_unlock_irqrestore(&msb->q_lock, flags);
+	error = 0;
+out_free_buffer:
+	kfree(buffer);
+	kfree(attr);
 	return error;
 }
 
+/*
+ * This pauses driver.
+ * Makes sure that driver won't send any commands to the device
+ */
 static void mspro_block_stop(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
@@ -787,7 +857,7 @@ static void mspro_block_stop(struct memstick_dev *card)
 
 	while (1) {
 		spin_lock_irqsave(&msb->q_lock, flags);
-		if (!msb->has_request) {
+		if (!msb->block_req) {
 			blk_stop_queue(msb->queue);
 			rc = 1;
 		}
@@ -800,6 +870,7 @@ static void mspro_block_stop(struct memstick_dev *card)
 	}
 }
 
+/* This undoes effects of mspro_block_stop */
 static void mspro_block_start(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
@@ -829,348 +900,129 @@ static void mspro_block_submit_req(struct request_queue *q)
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
 	struct request *req = NULL;
 
-	if (msb->has_request)
+	dbg(card, "IO: block layer submits us new request");
+
+	if (msb->block_req) {
+		dbg(card, "IO: Already have an request");
 		return;
+	}
 
 	if (msb->eject) {
+		dbg(card, "IO: Refusing requests on removed card");
 		while ((req = blk_fetch_request(q)) != NULL)
 			__blk_end_request_all(req, -ENODEV);
-
 		return;
 	}
 
-	msb->has_request = 1;
-	if (mspro_block_issue_req(card, 0))
-		msb->has_request = 0;
-}
-
-/*** Initialization ***/
-
-static int mspro_block_wait_for_ced(struct memstick_dev *card)
-{
-	struct mspro_block_data *msb = memstick_get_drvdata(card);
-
-	card->next_request = h_mspro_block_req_init;
-	msb->mrq_handler = h_mspro_block_wait_for_ced;
-	memstick_init_req(&card->current_mrq, MS_TPC_GET_INT, NULL, 1);
-	memstick_new_req(card->host);
-	wait_for_completion(&card->mrq_complete);
-	return card->current_mrq.error;
+	if (mspro_block_issue_req(card)) {
+		dbg(card, "IO: Falure to issue request");
+		msb->block_req = NULL;
+	} else
+		memstick_new_req(card->host);
 }
 
-static int mspro_block_set_interface(struct memstick_dev *card,
-				     unsigned char sys_reg)
+/* Reset the card */
+static int mspro_block_reset(struct memstick_dev *card, bool full)
 {
 	struct memstick_host *host = card->host;
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	struct mspro_param_register param = {
-		.system = sys_reg,
-		.data_count = 0,
-		.data_address = 0,
-		.tpc_param = 0
-	};
+	int error;
 
-	card->next_request = h_mspro_block_req_init;
-	msb->mrq_handler = h_mspro_block_default;
-	memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, &param,
-			  sizeof(param));
-	memstick_new_req(host);
-	wait_for_completion(&card->mrq_complete);
-	return card->current_mrq.error;
-}
+	dbg(card, "resetting the card");
 
-static int mspro_block_switch_interface(struct memstick_dev *card)
-{
-	struct memstick_host *host = card->host;
-	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	int rc = 0;
+	msb->system = MEMSTICK_SYS_SERIAL;
 
-try_again:
-	if (msb->caps & MEMSTICK_CAP_PAR4)
-		rc = mspro_block_set_interface(card, MEMSTICK_SYS_PAR4);
-	else
-		return 0;
+	error = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+	if (error)
+		return error;
+	msleep(50);
 
-	if (rc) {
-		printk(KERN_WARNING
-		       "%s: could not switch to 4-bit mode, error %d\n",
-		       dev_name(&card->dev), rc);
-		return 0;
-	}
+	error = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
+	if (error)
+		return error;
 
-	msb->system = MEMSTICK_SYS_PAR4;
-	host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_PAR4);
-	printk(KERN_INFO "%s: switching to 4-bit parallel mode\n",
-	       dev_name(&card->dev));
-
-	if (msb->caps & MEMSTICK_CAP_PAR8) {
-		rc = mspro_block_set_interface(card, MEMSTICK_SYS_PAR8);
-
-		if (!rc) {
-			msb->system = MEMSTICK_SYS_PAR8;
-			host->set_param(host, MEMSTICK_INTERFACE,
-					MEMSTICK_PAR8);
-			printk(KERN_INFO
-			       "%s: switching to 8-bit parallel mode\n",
-			       dev_name(&card->dev));
-		} else
-			printk(KERN_WARNING
-			       "%s: could not switch to 8-bit mode, error %d\n",
-			       dev_name(&card->dev), rc);
-	}
+	error = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+	if (error)
+		return error;
 
-	card->next_request = h_mspro_block_req_init;
-	msb->mrq_handler = h_mspro_block_default;
-	memstick_init_req(&card->current_mrq, MS_TPC_GET_INT, NULL, 1);
-	memstick_new_req(card->host);
-	wait_for_completion(&card->mrq_complete);
-	rc = card->current_mrq.error;
+	msleep(150);
 
-	if (rc) {
-		printk(KERN_WARNING
-		       "%s: interface error, trying to fall back to serial\n",
-		       dev_name(&card->dev));
-		msb->system = MEMSTICK_SYS_SERIAL;
-		host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
-		msleep(10);
-		host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
-		host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
-
-		rc = memstick_set_rw_addr(card);
-		if (!rc)
-			rc = mspro_block_set_interface(card, msb->system);
-
-		if (!rc) {
-			msleep(150);
-			rc = mspro_block_wait_for_ced(card);
-			if (rc)
-				return rc;
-
-			if (msb->caps & MEMSTICK_CAP_PAR8) {
-				msb->caps &= ~MEMSTICK_CAP_PAR8;
-				goto try_again;
-			}
-		}
+	/* Invalidate the register window */
+	memstick_invalidate_reg_window(card);
+
+	error = memstick_run_state_machine(card, h_mspro_block_reset);
+	if (error) {
+		dbg(card, "card reset failure");
+		return error;
 	}
-	return rc;
+
+	return mspro_block_switch_interface(card);
 }
 
-/* Memory allocated for attributes by this function should be freed by
- * mspro_block_data_clear, no matter if the initialization process succeded
- * or failed.
- */
-static int mspro_block_read_attributes(struct memstick_dev *card)
+/* Attempts to switch to faster interface. Clears capablity flags if fails */
+static int mspro_block_switch_interface(struct memstick_dev *card)
 {
+	struct memstick_host *host = card->host;
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	struct mspro_param_register param = {
-		.system = msb->system,
-		.data_count = cpu_to_be16(1),
-		.data_address = 0,
-		.tpc_param = 0
-	};
-	struct mspro_attribute *attr = NULL;
-	struct mspro_sys_attr *s_attr = NULL;
-	unsigned char *buffer = NULL;
-	int cnt, rc, attr_count;
-	unsigned int addr;
-	unsigned short page_count;
+	int error = 0;
 
-	attr = kmalloc(msb->page_size, GFP_KERNEL);
-	if (!attr)
-		return -ENOMEM;
-
-	sg_init_one(&msb->req_sg[0], attr, msb->page_size);
-	msb->seg_count = 1;
-	msb->current_seg = 0;
-	msb->current_page = 0;
-	msb->data_dir = READ;
-	msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
-
-	card->next_request = h_mspro_block_req_init;
-	msb->mrq_handler = h_mspro_block_transfer_data;
-	memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, &param,
-			  sizeof(param));
-	memstick_new_req(card->host);
-	wait_for_completion(&card->mrq_complete);
-	if (card->current_mrq.error) {
-		rc = card->current_mrq.error;
-		goto out_free_attr;
-	}
-
-	if (be16_to_cpu(attr->signature) != MSPRO_BLOCK_SIGNATURE) {
-		printk(KERN_ERR "%s: unrecognized device signature %x\n",
-		       dev_name(&card->dev), be16_to_cpu(attr->signature));
-		rc = -ENODEV;
-		goto out_free_attr;
-	}
-
-	if (attr->count > MSPRO_BLOCK_MAX_ATTRIBUTES) {
-		printk(KERN_WARNING "%s: way too many attribute entries\n",
-		       dev_name(&card->dev));
-		attr_count = MSPRO_BLOCK_MAX_ATTRIBUTES;
-	} else
-		attr_count = attr->count;
+	if (!(card->caps & host->caps & MEMSTICK_CAP_PAR4))
+		return 0;
 
-	msb->attr_group.attrs = kzalloc((attr_count + 1)
-					* sizeof(struct attribute),
-					GFP_KERNEL);
-	if (!msb->attr_group.attrs) {
-		rc = -ENOMEM;
-		goto out_free_attr;
-	}
-	msb->attr_group.name = "media_attributes";
+	dbg(card, "trying to switch to parallel 4 bit inteface");
 
-	buffer = kmalloc(msb->page_size, GFP_KERNEL);
-	if (!buffer) {
-		rc = -ENOMEM;
-		goto out_free_attr;
+	msb->system = MEMSTICK_SYS_PAR4;
+	msb->target_interface = MEMSTICK_PAR4;
+	error = memstick_run_state_machine(card,
+						h_mspro_block_switch_interface);
+	if (error) {
+		dbg(card, "failure to switch to parallel 4 bit inteface");
+		card->caps &= ~MEMSTICK_CAP_PAR4;
+		return mspro_block_reset(card, true);
 	}
-	memcpy(buffer, (char *)attr, msb->page_size);
-	page_count = 1;
-
-	for (cnt = 0; cnt < attr_count; ++cnt) {
-		s_attr = kzalloc(sizeof(struct mspro_sys_attr), GFP_KERNEL);
-		if (!s_attr) {
-			rc = -ENOMEM;
-			goto out_free_buffer;
-		}
-
-		msb->attr_group.attrs[cnt] = &s_attr->dev_attr.attr;
-		addr = be32_to_cpu(attr->entries[cnt].address);
-		rc = be32_to_cpu(attr->entries[cnt].size);
-		dev_dbg(&card->dev, "adding attribute %d: id %x, address %x, "
-			"size %x\n", cnt, attr->entries[cnt].id, addr, rc);
-		s_attr->id = attr->entries[cnt].id;
-		if (mspro_block_attr_name(s_attr->id))
-			snprintf(s_attr->name, sizeof(s_attr->name), "%s",
-				 mspro_block_attr_name(attr->entries[cnt].id));
-		else
-			snprintf(s_attr->name, sizeof(s_attr->name),
-				 "attr_x%02x", attr->entries[cnt].id);
-
-		sysfs_attr_init(&s_attr->dev_attr.attr);
-		s_attr->dev_attr.attr.name = s_attr->name;
-		s_attr->dev_attr.attr.mode = S_IRUGO;
-		s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id);
 
-		if (!rc)
-			continue;
-
-		s_attr->size = rc;
-		s_attr->data = kmalloc(rc, GFP_KERNEL);
-		if (!s_attr->data) {
-			rc = -ENOMEM;
-			goto out_free_buffer;
-		}
-
-		if (((addr / msb->page_size)
-		     == be32_to_cpu(param.data_address))
-		    && (((addr + rc - 1) / msb->page_size)
-			== be32_to_cpu(param.data_address))) {
-			memcpy(s_attr->data, buffer + addr % msb->page_size,
-			       rc);
-			continue;
-		}
-
-		if (page_count <= (rc / msb->page_size)) {
-			kfree(buffer);
-			page_count = (rc / msb->page_size) + 1;
-			buffer = kmalloc(page_count * msb->page_size,
-					 GFP_KERNEL);
-			if (!buffer) {
-				rc = -ENOMEM;
-				goto out_free_attr;
-			}
-		}
-
-		param.system = msb->system;
-		param.data_count = cpu_to_be16((rc / msb->page_size) + 1);
-		param.data_address = cpu_to_be32(addr / msb->page_size);
-		param.tpc_param = 0;
-
-		sg_init_one(&msb->req_sg[0], buffer,
-			    be16_to_cpu(param.data_count) * msb->page_size);
-		msb->seg_count = 1;
-		msb->current_seg = 0;
-		msb->current_page = 0;
-		msb->data_dir = READ;
-		msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
-
-		dev_dbg(&card->dev, "reading attribute pages %x, %x\n",
-			be32_to_cpu(param.data_address),
-			be16_to_cpu(param.data_count));
-
-		card->next_request = h_mspro_block_req_init;
-		msb->mrq_handler = h_mspro_block_transfer_data;
-		memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
-				  (char *)&param, sizeof(param));
-		memstick_new_req(card->host);
-		wait_for_completion(&card->mrq_complete);
-		if (card->current_mrq.error) {
-			rc = card->current_mrq.error;
-			goto out_free_buffer;
-		}
+	if (!(card->caps & host->caps & MEMSTICK_CAP_PAR8))
+		return 0;
 
-		memcpy(s_attr->data, buffer + addr % msb->page_size, rc);
+	msb->system = MEMSTICK_SYS_PAR8;
+	msb->target_interface = MEMSTICK_PAR8;
+	dbg(card, "trying to switch to parallel 8 bit inteface");
+	error = memstick_run_state_machine(card,
+					h_mspro_block_switch_interface);
+	if (error) {
+		dbg(card, "failure to switch to parallel 8 bit inteface");
+		card->caps &= ~MEMSTICK_CAP_PAR8;
+		return mspro_block_reset(card, true);
 	}
 
-	rc = 0;
-out_free_buffer:
-	kfree(buffer);
-out_free_attr:
-	kfree(attr);
-	return rc;
+	return error;
 }
 
 static int mspro_block_init_card(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	struct memstick_host *host = card->host;
 	int rc = 0;
 
-	msb->system = MEMSTICK_SYS_SERIAL;
-	card->reg_addr.r_offset = offsetof(struct mspro_register, status);
-	card->reg_addr.r_length = sizeof(struct ms_status_register);
-	card->reg_addr.w_offset = offsetof(struct mspro_register, param);
-	card->reg_addr.w_length = sizeof(struct mspro_param_register);
-
-	if (memstick_set_rw_addr(card))
-		return -EIO;
-
-	msb->caps = host->caps;
+	/* TODO: can't we know what card supports from attributes? */
+	card->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8;
 
-	msleep(150);
-	rc = mspro_block_wait_for_ced(card);
+	rc = mspro_block_reset(card, false);
 	if (rc)
 		return rc;
 
-	rc = mspro_block_switch_interface(card);
-	if (rc)
-		return rc;
-
-	dev_dbg(&card->dev, "card activated\n");
+	dbg(card, "card activated");
 	if (msb->system != MEMSTICK_SYS_SERIAL)
-		msb->caps |= MEMSTICK_CAP_AUTO_GET_INT;
-
-	card->next_request = h_mspro_block_req_init;
-	msb->mrq_handler = h_mspro_block_get_ro;
-	memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL,
-			  sizeof(struct ms_status_register));
-	memstick_new_req(card->host);
-	wait_for_completion(&card->mrq_complete);
-	if (card->current_mrq.error)
-		return card->current_mrq.error;
+		card->caps |= MEMSTICK_CAP_AUTO_GET_INT;
 
-	dev_dbg(&card->dev, "card r/w status %d\n", msb->read_only ? 0 : 1);
+	dbg(card, "card r/w status %d", msb->read_only ? 0 : 1);
 
 	msb->page_size = 512;
 	rc = mspro_block_read_attributes(card);
 	if (rc)
 		return rc;
 
-	dev_dbg(&card->dev, "attributes loaded\n");
+	dbg(card, "attributes loaded");
 	return 0;
-
 }
 
 static int mspro_block_init_disk(struct memstick_dev *card)
@@ -1202,6 +1054,7 @@ static int mspro_block_init_disk(struct memstick_dev *card)
 	msb->cylinders = be16_to_cpu(dev_info->cylinders);
 	msb->heads = be16_to_cpu(dev_info->heads);
 	msb->sectors_per_track = be16_to_cpu(dev_info->sectors_per_track);
+	sg_init_table(msb->req_sg, MSPRO_BLOCK_MAX_SEGS);
 
 	msb->page_size = be16_to_cpu(sys_info->unit_size);
 
@@ -1257,7 +1110,7 @@ static int mspro_block_init_disk(struct memstick_dev *card)
 	capacity *= be16_to_cpu(sys_info->block_size);
 	capacity *= msb->page_size >> 9;
 	set_capacity(msb->disk, capacity);
-	dev_dbg(&card->dev, "capacity set %ld\n", capacity);
+	dbg(card, "capacity set %ld", capacity);
 
 	add_disk(msb->disk);
 	msb->active = 1;
@@ -1345,7 +1198,7 @@ static void mspro_block_remove(struct memstick_dev *card)
 	spin_unlock_irqrestore(&msb->q_lock, flags);
 
 	del_gendisk(msb->disk);
-	dev_dbg(&card->dev, "mspro block remove\n");
+	dbg(card, "mspro block remove");
 
 	blk_cleanup_queue(msb->queue);
 	msb->queue = NULL;
@@ -1485,6 +1338,10 @@ static void __exit mspro_block_exit(void)
 module_init(mspro_block_init);
 module_exit(mspro_block_exit);
 
+module_param(major, int, 0644);
+module_param(debug, int, 0644);
+
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Alex Dubov");
 MODULE_DESCRIPTION("Sony MemoryStickPro block device driver");
diff --git a/drivers/memstick/core/mspro_block.h b/drivers/memstick/core/mspro_block.h
new file mode 100644
index 0000000..86a340d
--- /dev/null
+++ b/drivers/memstick/core/mspro_block.h
@@ -0,0 +1,176 @@
+/*
+ *  Sony MemoryStick Pro storage support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *  Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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.
+ *
+ * Special thanks to Carlos Corbacho for providing various MemoryStick cards
+ * that made this driver possible.
+ *
+ */
+
+#define DRIVER_NAME "mspro_block"
+
+#define MSPRO_BLOCK_MAX_SEGS  32
+#define MSPRO_BLOCK_MAX_PAGES ((2 << 16) - 1)
+
+#define MSPRO_BLOCK_SIGNATURE        0xa5c3
+#define MSPRO_BLOCK_MAX_ATTRIBUTES   41
+
+#define MSPRO_BLOCK_PART_SHIFT 3
+
+enum {
+	MSPRO_BLOCK_ID_SYSINFO         = 0x10,
+	MSPRO_BLOCK_ID_MODELNAME       = 0x15,
+	MSPRO_BLOCK_ID_MBR             = 0x20,
+	MSPRO_BLOCK_ID_PBR16           = 0x21,
+	MSPRO_BLOCK_ID_PBR32           = 0x22,
+	MSPRO_BLOCK_ID_SPECFILEVALUES1 = 0x25,
+	MSPRO_BLOCK_ID_SPECFILEVALUES2 = 0x26,
+	MSPRO_BLOCK_ID_DEVINFO         = 0x30
+};
+
+struct mspro_sys_attr {
+	size_t                  size;
+	void                    *data;
+	unsigned char           id;
+	char                    name[32];
+	struct device_attribute dev_attr;
+};
+
+struct mspro_attr_entry {
+	__be32 address;
+	__be32 size;
+	unsigned char id;
+	unsigned char reserved[3];
+} __packed;
+
+struct mspro_attribute {
+	__be16 signature;
+	unsigned short          version;
+	unsigned char           count;
+	unsigned char           reserved[11];
+	struct mspro_attr_entry entries[];
+} __packed;
+
+struct mspro_sys_info {
+	unsigned char  class;
+	unsigned char  reserved0;
+	__be16 block_size;
+	__be16 block_count;
+	__be16 user_block_count;
+	__be16 page_size;
+	unsigned char  reserved1[2];
+	unsigned char  assembly_date[8];
+	__be32 serial_number;
+	unsigned char  assembly_maker_code;
+	unsigned char  assembly_model_code[3];
+	__be16 memory_maker_code;
+	__be16 memory_model_code;
+	unsigned char  reserved2[4];
+	unsigned char  vcc;
+	unsigned char  vpp;
+	__be16 controller_number;
+	__be16 controller_function;
+	__be16 start_sector;
+	__be16 unit_size;
+	unsigned char  ms_sub_class;
+	unsigned char  reserved3[4];
+	unsigned char  interface_type;
+	__be16 controller_code;
+	unsigned char  format_type;
+	unsigned char  reserved4;
+	unsigned char  device_type;
+	unsigned char  reserved5[7];
+	unsigned char  mspro_id[16];
+	unsigned char  reserved6[16];
+} __packed;
+
+struct mspro_mbr {
+	unsigned char boot_partition;
+	unsigned char start_head;
+	unsigned char start_sector;
+	unsigned char start_cylinder;
+	unsigned char partition_type;
+	unsigned char end_head;
+	unsigned char end_sector;
+	unsigned char end_cylinder;
+	unsigned int  start_sectors;
+	unsigned int  sectors_per_partition;
+} __packed;
+
+struct mspro_specfile {
+	char           name[8];
+	char           ext[3];
+	unsigned char  attr;
+	unsigned char  reserved[10];
+	unsigned short time;
+	unsigned short date;
+	unsigned short cluster;
+	unsigned int   size;
+} __packed;
+
+struct mspro_devinfo {
+	__be16 cylinders;
+	__be16 heads;
+	__be16 bytes_per_track;
+	__be16 bytes_per_sector;
+	__be16 sectors_per_track;
+	unsigned char  reserved[6];
+} __packed;
+
+
+
+struct mspro_block_data {
+	struct memstick_dev              *card;
+	unsigned int                     usage_count;
+	unsigned int                     caps;
+	struct gendisk                   *disk;
+	struct request_queue             *queue;
+	struct request                   *block_req;
+	spinlock_t                       q_lock;
+	struct scatterlist               req_sg[MSPRO_BLOCK_MAX_SEGS];
+
+	unsigned char                    read_only:1;
+	unsigned char                    eject:1;
+	unsigned char                    active:1;
+
+	/* Media info */
+	struct attribute_group           attr_group;
+	unsigned short                   page_size;
+	unsigned short                   cylinders;
+	unsigned short                   heads;
+	unsigned short                   sectors_per_track;
+
+	/* Handlers state */
+	unsigned char                    system;
+	struct mspro_cmdex_argument      arg;
+
+	struct scatterlist               *current_sg;
+	unsigned int                     current_sg_offset;
+	unsigned int	                 data_transferred;
+	unsigned char                    data_dir:1;
+	unsigned int                     target_interface;
+	bool                             io_error;
+};
+
+static void mspro_block_complete_req(struct memstick_dev *card, int error);
+static int mspro_block_switch_interface(struct memstick_dev *card);
+static int mspro_block_issue_req(struct memstick_dev *card);
+
+#define __dbg(card, level, format, ...) \
+	do { \
+		if (debug >= level) \
+			printk(KERN_DEBUG \
+				"%s: " format "\n", dev_name(&card->dev) \
+					 , ## __VA_ARGS__); \
+		else \
+			dev_dbg(&card->dev, format, ## __VA_ARGS__); \
+	} while (0)
+
+#define dbg(card, format, ...)		__dbg(card, 1, format, ## __VA_ARGS__)
+#define dbg_v(card, format, ...)	__dbg(card, 2, format, ## __VA_ARGS__)
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 6/6] memstick: stop passing useless pointer to card->current_mrq + refactoring
  2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
                   ` (4 preceding siblings ...)
  2010-10-16 23:12 ` [PATCH 5/6] memstick: mspro_block: refactoring Maxim Levitsky
@ 2010-10-16 23:12 ` Maxim Levitsky
  2010-10-17 22:55 ` [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
  6 siblings, 0 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-16 23:12 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML, Maxim Levitsky

Now that both card drivers use new support code that can be safely done.
In addition to that few handlers in memstick.c are converted to new style.
register window is now completely dynamic, and nobody but register read/write
functions send the MS_TPC_SET_RW_REG_ADRS.

In addition to that, I did more cosmetic refactoring, and few fixes
there and there.

Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 drivers/memstick/core/memstick.c    |  611 +++++++++++++++++------------------
 drivers/memstick/core/ms_block.c    |  166 +++++-----
 drivers/memstick/core/mspro_block.c |  185 +++++------
 drivers/memstick/core/mspro_block.h |    2 +-
 include/linux/memstick.h            |   57 ++--
 5 files changed, 484 insertions(+), 537 deletions(-)

diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c
index 4c457ae..a2be4e0 100644
--- a/drivers/memstick/core/memstick.c
+++ b/drivers/memstick/core/memstick.c
@@ -202,61 +202,207 @@ static int memstick_dummy_check(struct memstick_dev *card)
 	return 0;
 }
 
-/**
- * memstick_detect_change - schedule media detection on memstick host
- * @host - host to use
+/*
+ * Functions prefixed with "h_" are protocol callbacks. They can be called from
+ * interrupt context. Return value of 0 means that request processing is still
+ * ongoing, while special error value of -EAGAIN means that current request is
+ * finished (and request processor should come back some time later).
  */
-void memstick_detect_change(struct memstick_host *host)
+
+static int h_memstick_read_dev_id(struct memstick_dev *card)
 {
-	queue_work(workqueue, &host->media_checker);
+	struct ms_id_register id_reg;
+	struct memstick_request *mrq = &card->current_mrq;
+
+	if (mrq->error)
+		return memstick_state_machine_exit(card, -EIO);
+
+	switch (card->state) {
+	case 0:
+		if (!memstick_read_regs(card,
+			offsetof(struct ms_register, id),
+			sizeof(struct ms_id_register)))
+			return 0;
+		break;
+	case 1:
+		memcpy(&id_reg, mrq->data, sizeof(id_reg));
+		card->id.match_flags = MEMSTICK_MATCH_ALL;
+		card->id.type = id_reg.type;
+		card->id.category = id_reg.category;
+		card->id.class = id_reg.class;
+		dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode);
+		return memstick_state_machine_exit(card, 0);
+	}
+
+	card->state++;
+	return 0;
 }
-EXPORT_SYMBOL(memstick_detect_change);
 
-/**
- * memstick_next_req - called by host driver to obtain next request to process
- * @host - host to use
- * @mrq - pointer to stick the request to
- *
- * Host calls this function from idle state (*mrq == NULL) or after finishing
- * previous request (*mrq should point to it). If previous request was
- * unsuccessful, it is retried for predetermined number of times. Return value
- * of 0 means that new request was assigned to the host.
- */
-int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq)
+static int h_memstick_default_bad(struct memstick_dev *card)
 {
-	int rc = -ENXIO;
+	return -ENXIO;
+}
 
-	if ((*mrq) && (*mrq)->error && host->retries) {
-		(*mrq)->error = rc;
-		host->retries--;
-		return 0;
+static void memstick_invalidate_reg_window(struct memstick_dev *card)
+{
+	memset(&card->reg_addr, 0, sizeof(card->reg_addr));
+}
+
+static struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
+{
+	struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev),
+					    GFP_KERNEL);
+	struct memstick_dev *old_card = host->card;
+
+	if (card) {
+		card->host = host;
+		dev_set_name(&card->dev, "%s", dev_name(&host->dev));
+		card->dev.parent = &host->dev;
+		card->dev.bus = &memstick_bus_type;
+		card->dev.release = memstick_free_card;
+		card->check = memstick_dummy_check;
+		card->next_request = h_memstick_default_bad;
+		init_completion(&card->mrq_complete);
+		host->card = card;
+
+		if (memstick_run_state_machine(card,
+						h_memstick_read_dev_id, true))
+			goto err_out;
 	}
+	host->card = old_card;
+	return card;
+err_out:
+	host->card = old_card;
+	kfree(card);
+	return NULL;
+}
 
-	if (host->card && host->card->next_request)
-		rc = host->card->next_request(host->card, mrq);
+static int memstick_power_on(struct memstick_host *host)
+{
+	int rc = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
 
 	if (!rc)
-		host->retries = cmd_retries > 1 ? cmd_retries - 1 : 1;
-	else
-		*mrq = NULL;
+		rc = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
 
 	return rc;
 }
-EXPORT_SYMBOL(memstick_next_req);
+
+static void memstick_check(struct work_struct *work)
+{
+	struct memstick_host *host = container_of(work, struct memstick_host,
+						  media_checker);
+	struct memstick_dev *card;
+
+	dev_dbg(&host->dev, "memstick_check started\n");
+	mutex_lock(&host->lock);
+	if (!host->card) {
+		if (memstick_power_on(host))
+			goto out_power_off;
+	} else if (host->card->stop) {
+		host->card->stop(host->card);
+		memstick_invalidate_reg_window(host->card);
+	}
+
+	card = memstick_alloc_card(host);
+
+	if (!card) {
+		if (host->card) {
+			device_unregister(&host->card->dev);
+			host->card = NULL;
+		}
+	} else {
+		dev_dbg(&host->dev, "new card %02x, %02x, %02x\n",
+			card->id.type, card->id.category, card->id.class);
+		if (host->card) {
+			if (!memstick_dev_match(host->card, &card->id)
+			    || !(host->card->check(host->card))) {
+				device_unregister(&host->card->dev);
+				host->card = NULL;
+			} else if (host->card->start)
+				host->card->start(host->card);
+		}
+
+		if (!host->card) {
+			host->card = card;
+			if (device_register(&card->dev)) {
+				kfree(host->card);
+				host->card = NULL;
+			}
+		} else
+			kfree(card);
+	}
+
+out_power_off:
+	if (!host->card)
+		host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+
+	mutex_unlock(&host->lock);
+	dev_dbg(&host->dev, "memstick_check finished\n");
+}
+
+
+/*** card driver interface ***/
 
 /**
- * memstick_new_req - notify the host that some requests are pending
- * @host - host to use
+ * memstick_register_driver - register new card driver
+ * @drv - the driver info structure
  */
-void memstick_new_req(struct memstick_host *host)
+int memstick_register_driver(struct memstick_driver *drv)
 {
-	if (host->card) {
-		host->retries = cmd_retries;
-		INIT_COMPLETION(host->card->mrq_complete);
-		host->request(host);
-	}
+	drv->driver.bus = &memstick_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(memstick_register_driver);
+
+/**
+ * memstick_unregister_driver - unregister a card driver
+ * usually called on card driver unload
+ * @drv - the driver info structure
+ */
+void memstick_unregister_driver(struct memstick_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(memstick_unregister_driver);
+
+/**
+ * memstick_run_state_machine - runs IO state machine
+ * DON'T call from state machine code!
+ * Usefull for blocking IO in the card drivers.
+ * @card - card to use
+ * @state_func - the state machine
+ * @sync - if true, will wait till state machine exits using
+ * memstick_state_machine_exit
+ */
+int memstick_run_state_machine(struct memstick_dev *card,
+	int   (*state_func)(struct memstick_dev *card), bool sync)
+{
+	/* Init everything to prevent stale data from creeping in */
+	memset(&card->current_mrq, 0, sizeof(card->current_mrq));
+	card->int_polling = false;
+	card->state = 0;
+
+	WARN_ON(card->state_machine_running);
+	WARN_ON(card->next_request != h_memstick_default_bad);
+
+	card->next_request = state_func;
+	card->state_machine_running = true;
+
+	card->host->retries = cmd_retries;
+	INIT_COMPLETION(card->mrq_complete);
+	card->host->request(card->host);
+
+	if (!sync)
+		return 0;
+
+	wait_for_completion(&card->mrq_complete);
+	return card->current_mrq.error;
 }
-EXPORT_SYMBOL(memstick_new_req);
+EXPORT_SYMBOL(memstick_run_state_machine);
+
+
+/*** functions to be called by state machines ***/
 
 /**
  * memstick_init_req_sg - set request fields needed for bulk data transfer
@@ -264,9 +410,12 @@ EXPORT_SYMBOL(memstick_new_req);
  * @tpc - memstick Transport Protocol Command
  * @sg - TPC argument
  */
-void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
+void memstick_init_req_sg(struct memstick_dev *card, unsigned char tpc,
 			  const struct scatterlist *sg)
 {
+	struct memstick_request *mrq = &card->current_mrq;
+	WARN_ON(!card->state_machine_running);
+
 	mrq->tpc = tpc;
 	if (tpc & 8)
 		mrq->data_dir = WRITE;
@@ -280,6 +429,9 @@ void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
 		mrq->need_card_int = 1;
 	else
 		mrq->need_card_int = 0;
+
+	if (tpc != MS_TPC_GET_INT)
+		card->int_polling = false;
 }
 EXPORT_SYMBOL(memstick_init_req_sg);
 
@@ -294,9 +446,12 @@ EXPORT_SYMBOL(memstick_init_req_sg);
  * in size) allows us to just copy the value between request structure and
  * user supplied buffer.
  */
-void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
+void memstick_init_req(struct memstick_dev *card, unsigned char tpc,
 		       const void *buf, size_t length)
 {
+	struct memstick_request *mrq = &card->current_mrq;
+	WARN_ON(!card->state_machine_running);
+
 	mrq->tpc = tpc;
 	if (tpc & 8)
 		mrq->data_dir = WRITE;
@@ -316,152 +471,38 @@ void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
 }
 EXPORT_SYMBOL(memstick_init_req);
 
-/*
- * Functions prefixed with "h_" are protocol callbacks. They can be called from
- * interrupt context. Return value of 0 means that request processing is still
- * ongoing, while special error value of -EAGAIN means that current request is
- * finished (and request processor should come back some time later).
- */
-
-static int h_memstick_read_dev_id(struct memstick_dev *card,
-				  struct memstick_request **mrq)
-{
-	struct ms_id_register id_reg;
-
-	if (!(*mrq)) {
-		memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL,
-				  sizeof(struct ms_id_register));
-		*mrq = &card->current_mrq;
-		return 0;
-	} else {
-		if (!(*mrq)->error) {
-			memcpy(&id_reg, (*mrq)->data, sizeof(id_reg));
-			card->id.match_flags = MEMSTICK_MATCH_ALL;
-			card->id.type = id_reg.type;
-			card->id.category = id_reg.category;
-			card->id.class = id_reg.class;
-			dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode);
-		}
-		complete(&card->mrq_complete);
-		return -EAGAIN;
-	}
-}
-
-static int h_memstick_set_rw_addr(struct memstick_dev *card,
-				  struct memstick_request **mrq)
-{
-	if (!(*mrq)) {
-		memstick_init_req(&card->current_mrq, MS_TPC_SET_RW_REG_ADRS,
-				  (char *)&card->reg_addr,
-				  sizeof(card->reg_addr));
-		*mrq = &card->current_mrq;
-		return 0;
-	} else {
-		complete(&card->mrq_complete);
-		return -EAGAIN;
-	}
-}
-
-
-static int h_memstick_default_bad(struct memstick_dev *card,
-				     struct memstick_request **mrq)
-{
-	return -ENXIO;
-}
-
-/**
- * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to
- *                        complete
- * @card - media device to use
- */
-int memstick_set_rw_addr(struct memstick_dev *card)
-{
-	card->next_request = h_memstick_set_rw_addr;
-	memstick_new_req(card->host);
-	wait_for_completion(&card->mrq_complete);
-
-	return card->current_mrq.error;
-}
-EXPORT_SYMBOL(memstick_set_rw_addr);
-
-
-/**
- * memstick_allocate_request - create new request for use in request handler
- * @card - card to use
- * @mrq - request to initialize
- */
-void memstick_allocate_request(struct memstick_dev *card,
-					struct memstick_request **mrq)
-{
-	if (*mrq == NULL) {
-		*mrq = &card->current_mrq;
-		(*mrq)->error = 0;
-		(*mrq)->need_card_int = 0;
-		card->int_polling = false;
-		card->state = 0;
-	}
-}
-EXPORT_SYMBOL(memstick_allocate_request);
-
-/**
- * memstick_complete_req - signal that request is completed
- * @card - card to use
- * @mrq - request to use
- * @error - result of the request
- *
- * Card drivers can use that function to signal end of request
- */
-int memstick_complete_request(struct memstick_dev *card,
-			struct memstick_request *req, int error)
-{
-	if (error)
-		req->error = error;
-
-	card->state = -1;
-	card->int_polling = false;
-	card->next_request = h_memstick_default_bad;
-
-	/* Invalidate reg window on errors */
-	if (req->error)
-		memstick_invalidate_reg_window(card);
-
-	complete(&card->mrq_complete);
-	return -EAGAIN;
-}
-EXPORT_SYMBOL(memstick_complete_request);
-
 /**
  * memstick_read_int_reg - read INT status from the card
  * if last request already contains the int flags, will return 0
  * returns 1 if new request was initialized
- * Will artifictially return MEMSTICK_INT_CMDNAK if this function was
- * called more that once in 300 msecs without memstick_finish_int_request
- * in between
+ * Will artifictially return MEMSTICK_INT_CMDNAK if more that 'timeout' msecs
+ * passed after last MS_TPC_GET_INT request (and no requests in between)
+ * if timeout == -1, then it is set to default value of 300 msec
  * @card - card to use
- * @req - request to use
+ * @timeout - the timeout. if -1, then uses default
  */
 
-int memstick_read_int_reg(struct memstick_dev *card,
-				struct memstick_request *req, long timeout)
+int memstick_read_int_reg(struct memstick_dev *card, long timeout)
 {
+	struct memstick_request *mrq = &card->current_mrq;
+	WARN_ON(!card->state_machine_running);
 
 	if (!card->int_polling) {
 		card->int_timeout = jiffies +
 			msecs_to_jiffies(timeout == -1 ? 300 : timeout);
 		card->int_polling = true;
 	} else if (time_after(jiffies, card->int_timeout)) {
-		req->data[0] = MEMSTICK_INT_CMDNAK;
+		mrq->data[0] = MEMSTICK_INT_CMDNAK;
 		return 0;
 	}
 
 	if (((card->caps | card->host->caps) & MEMSTICK_CAP_AUTO_GET_INT) &&
-							req->need_card_int) {
-		BUG_ON(req->error);
-		req->data[0] = req->int_reg;
-		req->need_card_int = 0;
+				mrq->need_card_int && !mrq->error) {
+		mrq->data[0] = mrq->int_reg;
+		mrq->need_card_int = false;
 		return 0;
 	} else {
-		memstick_init_req(req, MS_TPC_GET_INT, NULL, 1);
+		memstick_init_req(card, MS_TPC_GET_INT, NULL, 1);
 		return 1;
 	}
 }
@@ -469,19 +510,6 @@ EXPORT_SYMBOL(memstick_read_int_reg);
 
 
 /**
- * memstick_read_int_reg_cleanup - cleanup after series of calls to
- * memstick_read_int_reg. Used to cancel timeout.
- * Use this if you use memstick_read_int_reg
- * @card - card to use
- */
-void memstick_read_int_reg_cleanup(struct memstick_dev *card)
-{
-	card->int_polling = false;
-}
-EXPORT_SYMBOL(memstick_read_int_reg_cleanup);
-
-
-/**
  * memstick_read_regs - read the ms registers
  * If there is need to change the R/W window,
  * it will create the MS_TPC_SET_RW_REG_ADRS request and return 0,
@@ -492,9 +520,10 @@ EXPORT_SYMBOL(memstick_read_int_reg_cleanup);
  * @req - request to use
  */
 
-int memstick_read_regs(struct memstick_dev *card, int offset, int len,
-	struct memstick_request *req)
+int memstick_read_regs(struct memstick_dev *card, int offset, int len)
 {
+	WARN_ON(!card->state_machine_running);
+
 	if (card->reg_addr.r_offset != offset ||
 					card->reg_addr.r_length != len) {
 		card->reg_addr.r_offset = offset;
@@ -508,12 +537,12 @@ int memstick_read_regs(struct memstick_dev *card, int offset, int len,
 			card->reg_addr.w_length = sizeof(struct ms_id_register);
 		}
 
-		memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS, &card->reg_addr,
+		memstick_init_req(card, MS_TPC_SET_RW_REG_ADRS, &card->reg_addr,
 			sizeof(card->reg_addr));
 		return 0;
 	}
 
-	memstick_init_req(req, MS_TPC_READ_REG, NULL, len);
+	memstick_init_req(card, MS_TPC_READ_REG, NULL, len);
 	return 1;
 }
 EXPORT_SYMBOL(memstick_read_regs);
@@ -527,11 +556,12 @@ EXPORT_SYMBOL(memstick_read_regs);
  * @offset - offset of first register to read
  * @len - number of bytes to read
  * @buf - the register data to write
- * @req - request to use
  */
-int memstick_write_regs(struct memstick_dev *card, int offset, int len,
-	char *buf, struct memstick_request *req)
+int memstick_write_regs(struct memstick_dev *card,
+					int offset, int len, void *buf)
 {
+	WARN_ON(!card->state_machine_running);
+
 	if (card->reg_addr.w_offset != offset ||
 					card->reg_addr.w_length != len) {
 		card->reg_addr.w_offset = offset;
@@ -546,35 +576,47 @@ int memstick_write_regs(struct memstick_dev *card, int offset, int len,
 			card->reg_addr.r_length = sizeof(struct ms_id_register);
 		}
 
-		memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS, &card->reg_addr,
+		memstick_init_req(card, MS_TPC_SET_RW_REG_ADRS, &card->reg_addr,
 			sizeof(card->reg_addr));
 		return 0;
 	}
 
-	memstick_init_req(req, MS_TPC_WRITE_REG, buf, len);
+	memstick_init_req(card, MS_TPC_WRITE_REG, buf, len);
 	return 1;
 }
 EXPORT_SYMBOL(memstick_write_regs);
 
 
 /**
- * memstick_run_state_machine - runs state machine untill it calls
- * memstick_complete_request.
- * Usefull for blocking IO in the card drivers.
+ * memstick_state_machine_exit - signal that request is completed
  * @card - card to use
- * state_func - the state machine
+ * @mrq - request to use
+ * @error - result of the request
+ *
+ * State machines call this to signal quit
  */
-int memstick_run_state_machine(struct memstick_dev *card,
-	int   (*state_func)(struct memstick_dev *card,
-					struct memstick_request **mrq))
+int memstick_state_machine_exit(struct memstick_dev *card, int error)
 {
-	card->next_request = state_func;
-	memstick_new_req(card->host);
-	wait_for_completion(&card->mrq_complete);
-	return card->current_mrq.error;
-}
-EXPORT_SYMBOL(memstick_run_state_machine);
+	struct memstick_request *mrq = &card->current_mrq;
+	WARN_ON(!card->state_machine_running);
+
+	/* Invalidate reg window on errors */
 
+	if (error)
+		memstick_invalidate_reg_window(card);
+
+	/* Save return value */
+	mrq->error = error;
+	card->next_request = h_memstick_default_bad;
+
+	/* Place guards against bugs */
+	card->state = -1;
+	card->state_machine_running = false;
+
+	complete(&card->mrq_complete);
+	return -ENXIO;
+}
+EXPORT_SYMBOL(memstick_state_machine_exit);
 
 static const char *tpc_names[] = {
 	"MS_TPC_READ_MG_STATUS",
@@ -605,120 +647,35 @@ EXPORT_SYMBOL(memstick_debug_get_tpc_name);
 
 
 /**
- * memstick_invalidate_reg_window - invalidate the card register
- * read/write window (start, len)
- * Use when not certain if card still remembers it
+ * memstick_reset_card - power cycles the card
+ * @card - card to use
  */
-void memstick_invalidate_reg_window(struct memstick_dev *card)
+int memstick_reset_card(struct memstick_dev *card)
 {
-	memset(&card->reg_addr, 0, sizeof(card->reg_addr));
-}
-EXPORT_SYMBOL(memstick_invalidate_reg_window);
+	int error;
+	struct memstick_host *host = card->host;
 
+	memstick_invalidate_reg_window(card);
 
+	error = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+	if (error)
+		return error;
 
-static struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
-{
-	struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev),
-					    GFP_KERNEL);
-	struct memstick_dev *old_card = host->card;
-	struct ms_id_register id_reg;
-
-	if (card) {
-		card->host = host;
-		dev_set_name(&card->dev, "%s", dev_name(&host->dev));
-		card->dev.parent = &host->dev;
-		card->dev.bus = &memstick_bus_type;
-		card->dev.release = memstick_free_card;
-		card->check = memstick_dummy_check;
-
-		card->reg_addr.r_offset = offsetof(struct ms_register, id);
-		card->reg_addr.r_length = sizeof(id_reg);
-		card->reg_addr.w_offset = offsetof(struct ms_register, id);
-		card->reg_addr.w_length = sizeof(id_reg);
-
-		init_completion(&card->mrq_complete);
-
-		host->card = card;
-		if (memstick_set_rw_addr(card))
-			goto err_out;
-
-		card->next_request = h_memstick_read_dev_id;
-		memstick_new_req(host);
-		wait_for_completion(&card->mrq_complete);
-
-		if (card->current_mrq.error)
-			goto err_out;
-	}
-	host->card = old_card;
-	return card;
-err_out:
-	host->card = old_card;
-	kfree(card);
-	return NULL;
-}
+	msleep(50);
 
-static int memstick_power_on(struct memstick_host *host)
-{
-	int rc = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
+	error = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
+	if (error)
+		return error;
 
-	if (!rc)
-		rc = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+	error = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+	if (error)
+		return error;
 
-	return rc;
+	return 0;
 }
+EXPORT_SYMBOL(memstick_reset_card);
 
-static void memstick_check(struct work_struct *work)
-{
-	struct memstick_host *host = container_of(work, struct memstick_host,
-						  media_checker);
-	struct memstick_dev *card;
-
-	dev_dbg(&host->dev, "memstick_check started\n");
-	mutex_lock(&host->lock);
-	if (!host->card) {
-		if (memstick_power_on(host))
-			goto out_power_off;
-	} else if (host->card->stop)
-		host->card->stop(host->card);
-
-	card = memstick_alloc_card(host);
-
-	if (!card) {
-		if (host->card) {
-			device_unregister(&host->card->dev);
-			host->card = NULL;
-		}
-	} else {
-		dev_dbg(&host->dev, "new card %02x, %02x, %02x\n",
-			card->id.type, card->id.category, card->id.class);
-		if (host->card) {
-			if (memstick_set_rw_addr(host->card)
-			    || !memstick_dev_match(host->card, &card->id)
-			    || !(host->card->check(host->card))) {
-				device_unregister(&host->card->dev);
-				host->card = NULL;
-			} else if (host->card->start)
-				host->card->start(host->card);
-		}
-
-		if (!host->card) {
-			host->card = card;
-			if (device_register(&card->dev)) {
-				kfree(host->card);
-				host->card = NULL;
-			}
-		} else
-			kfree(card);
-	}
-
-out_power_off:
-	if (!host->card)
-		host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
-
-	mutex_unlock(&host->lock);
-	dev_dbg(&host->dev, "memstick_check finished\n");
-}
+/*** host driver interface ***/
 
 /**
  * memstick_alloc_host - allocate a memstick_host structure
@@ -837,20 +794,48 @@ void memstick_resume_host(struct memstick_host *host)
 }
 EXPORT_SYMBOL(memstick_resume_host);
 
-int memstick_register_driver(struct memstick_driver *drv)
+/**
+ * memstick_detect_change - schedule media detection on memstick host
+ * @host - host to use
+ */
+void memstick_detect_change(struct memstick_host *host)
 {
-	drv->driver.bus = &memstick_bus_type;
-
-	return driver_register(&drv->driver);
+	queue_work(workqueue, &host->media_checker);
 }
-EXPORT_SYMBOL(memstick_register_driver);
+EXPORT_SYMBOL(memstick_detect_change);
 
-void memstick_unregister_driver(struct memstick_driver *drv)
+/**
+ * memstick_next_req - called by host driver to obtain next request to process
+ * @host - host to use
+ * @mrq - pointer to stick the request to
+ *
+ * Host calls this function from idle state (*mrq == NULL) or after finishing
+ * previous request (*mrq should point to it). If previous request was
+ * unsuccessful, it is retried for predetermined number of times. Return value
+ * of 0 means that new request was assigned to the host.
+ */
+int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq)
 {
-	driver_unregister(&drv->driver);
-}
-EXPORT_SYMBOL(memstick_unregister_driver);
+	int rc = -ENXIO;
+
+	if ((*mrq) && (*mrq)->error && host->retries) {
+		(*mrq)->error = rc;
+		host->retries--;
+		return 0;
+	}
+
+	if (host->card && host->card->next_request)
+		rc = host->card->next_request(host->card);
 
+	if (!rc) {
+		*mrq = &host->card->current_mrq;
+		host->retries = cmd_retries > 1 ? cmd_retries - 1 : 1;
+	} else
+		*mrq = NULL;
+
+	return rc;
+}
+EXPORT_SYMBOL(memstick_next_req);
 
 static int __init memstick_init(void)
 {
diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c
index 75ec529..9a9aa3a 100644
--- a/drivers/memstick/core/ms_block.c
+++ b/drivers/memstick/core/ms_block.c
@@ -241,17 +241,17 @@ static int msb_advance_sg(struct msb_data *msb)
  * Writes output to msb->current_sg, takes sector address from msb->reg.param
  * Can also be used to read extra data only. Set params accordintly.
  */
-static int h_msb_read_page(struct memstick_dev *card,
-				 struct memstick_request **mrq)
+static int h_msb_read_page(struct memstick_dev *card)
 {
 	struct msb_data *msb = memstick_get_drvdata(card);
+	struct memstick_request *mrq = &card->current_mrq;
+
 	struct scatterlist sg;
 	u8 command, intreg;
 
-	memstick_allocate_request(card, mrq);
-	if ((*mrq)->error) {
+	if (mrq->error) {
 		dbg("read_page, unknown error");
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, mrq->error);
 	}
 again:
 	switch (card->state) {
@@ -259,35 +259,32 @@ again:
 		if (!memstick_write_regs(card,
 			offsetof(struct ms_register, param),
 			sizeof(struct ms_param_register),
-			(unsigned char *)&msb->regs.param,
-			*mrq))
+			(unsigned char *)&msb->regs.param))
 			return 0;
 		break;
 
 	case 1: /* Execute the read command*/
 		command = MS_CMD_BLOCK_READ;
-		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
+		memstick_init_req(card, MS_TPC_SET_CMD, &command, 1);
 		break;
 
 	case 2: /* send INT request */
-		if (memstick_read_int_reg(card, *mrq, -1))
+		if (memstick_read_int_reg(card, -1))
 			break;
 		card->state++;
 
 	case 3: /* get result of the INT request*/
-		intreg = (*mrq)->data[0];
+		intreg = mrq->data[0];
 		msb->regs.status.interrupt = intreg;
 
 		if (intreg & MEMSTICK_INT_CMDNAK)
-			return memstick_complete_request(card, *mrq, -EIO);
+			return memstick_state_machine_exit(card, -EIO);
 
 		if (!(intreg & MEMSTICK_INT_CED)) {
 			card->state--;
 			goto again;
 		}
 
-		memstick_read_int_reg_cleanup(card);
-
 		if (intreg & MEMSTICK_INT_ERR)
 			card->state++;
 		else
@@ -298,26 +295,24 @@ again:
 	case 4: /* read the status register to understand source of the INT_ERR */
 		if (!memstick_read_regs(card,
 			offsetof(struct ms_register, status),
-			sizeof(struct ms_status_register),
-			*mrq))
+			sizeof(struct ms_status_register)))
 			return 0;
 		break;
 
 	case 5: /* get results of status check */
-		msb->regs.status = *(struct ms_status_register *)(*mrq)->data;
+		msb->regs.status = *(struct ms_status_register *)mrq->data;
 		card->state++;
 
 	case 6: /* Send extra data read request */
 		if (!memstick_read_regs(card,
 			offsetof(struct ms_register, extra_data),
-			sizeof(struct ms_extra_data_register),
-			*mrq))
+			sizeof(struct ms_extra_data_register)))
 			return 0;
 		break;
 
 	case 7: /* Save result of extra data request */
 		msb->regs.extra_data =
-			*(struct ms_extra_data_register *) (*mrq)->data;
+			*(struct ms_extra_data_register *) mrq->data;
 		card->state++;
 
 	case 8: /* Send the  MS_TPC_READ_LONG_DATA to read IO buffer */
@@ -329,25 +324,25 @@ again:
 		}
 
 		msb_set_sg(msb, &sg);
-		memstick_init_req_sg(*mrq, MS_TPC_READ_LONG_DATA, &sg);
+		memstick_init_req_sg(card, MS_TPC_READ_LONG_DATA, &sg);
 		break;
 
 	case 9: /* check validity of data buffer & done */
 
 		if (!(msb->regs.status.interrupt & MEMSTICK_INT_ERR))
-			return memstick_complete_request(card, *mrq, 0);
+			return memstick_state_machine_exit(card, 0);
 
 		if (msb->regs.status.status1 & MEMSTICK_UNCORR_ERROR) {
 			dbg("read_page: uncorrectable error");
-			return memstick_complete_request(card, *mrq, -EBADMSG);
+			return memstick_state_machine_exit(card, -EBADMSG);
 		}
 
 		if (msb->regs.status.status1 & MEMSTICK_CORR_ERROR) {
 			dbg("read_page: correctable error");
-			return memstick_complete_request(card, *mrq, -EUCLEAN);
+			return memstick_state_machine_exit(card, -EUCLEAN);
 		} else {
 			dbg("read_page: INT error, but no status error bits");
-			return memstick_complete_request(card, *mrq, -EIO);
+			return memstick_state_machine_exit(card, -EIO);
 		}
 	default:
 		BUG();
@@ -363,16 +358,16 @@ again:
  * Returns -EBADMSG if write fails due to uncorrectable error, or -EIO if
  * device refuses to take the command or something else
  */
-static int h_msb_write_block(struct memstick_dev *card,
-				 struct memstick_request **mrq)
+static int h_msb_write_block(struct memstick_dev *card)
 {
 	struct msb_data *msb = memstick_get_drvdata(card);
+	struct memstick_request *mrq = &card->current_mrq;
+
 	struct scatterlist sg;
 	u8 intreg, command;
 
-	memstick_allocate_request(card, mrq);
-	if ((*mrq)->error)
-		return memstick_complete_request(card, *mrq, 0);
+	if (mrq->error)
+		return memstick_state_machine_exit(card, mrq->error);
 
 again:
 	switch (card->state) {
@@ -387,37 +382,36 @@ again:
 			offsetof(struct ms_register, param),
 			sizeof(struct ms_param_register) +
 			sizeof(struct ms_extra_data_register) + 2,
-			(unsigned char *)&msb->regs.param,
-			*mrq))
+			(unsigned char *)&msb->regs.param))
 			return 0;
 		break;
 
 	case 1: /* execute the write command*/
 		command = MS_CMD_BLOCK_WRITE;
-		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
+		memstick_init_req(card, MS_TPC_SET_CMD, &command, 1);
 		break;
 
 	case 2: /* send INT request */
-		if (memstick_read_int_reg(card, *mrq, -1))
+		if (memstick_read_int_reg(card, -1))
 			break;
 		card->state++;
 
 	case 3: /* read INT response */
-		intreg = (*mrq)->data[0];
+		intreg = mrq->data[0];
 		msb->regs.status.interrupt = intreg;
 
 		/* errors mean out of here, and fast... */
 		if (intreg & (MEMSTICK_INT_CMDNAK))
-			return memstick_complete_request(card, *mrq, -EIO);
+			return memstick_state_machine_exit(card, -EIO);
 
 		if (intreg & MEMSTICK_INT_ERR)
-			return memstick_complete_request(card, *mrq, -EBADMSG);
+			return memstick_state_machine_exit(card, -EBADMSG);
 
 
 		/* for last page we need to poll CED */
 		if (msb->current_page == msb->pages_in_block) {
 			if (intreg & MEMSTICK_INT_CED)
-				return memstick_complete_request(card, *mrq, 0);
+				return memstick_state_machine_exit(card, 0);
 			card->state--;
 			goto again;
 
@@ -429,13 +423,12 @@ again:
 			goto again;
 		}
 
-		memstick_read_int_reg_cleanup(card);
 		card->state++;
 
 	case 4: /* send the MS_TPC_WRITE_LONG_DATA to perform the write*/
 		msb_set_sg(msb, &sg);
-		memstick_init_req_sg(*mrq, MS_TPC_WRITE_LONG_DATA, &sg);
-		(*mrq)->need_card_int = 1;
+		memstick_init_req_sg(card, MS_TPC_WRITE_LONG_DATA, &sg);
+		mrq->need_card_int = 1;
 		break;
 
 	case 5: /* Switch to next page + go back to int polling */
@@ -445,8 +438,8 @@ again:
 			if (msb_advance_sg(msb)) {
 				ms_printk(
 				"BUG: out of data while writing block!");
-				return memstick_complete_request(card,
-								*mrq, -EFAULT);
+				return memstick_state_machine_exit(
+								card, -EFAULT);
 			}
 		}
 		card->state = 2;
@@ -462,17 +455,17 @@ again:
  * This function is used to send simple IO requests to device that consist
  * of register write + command
  */
-static int h_msb_send_command(struct memstick_dev *card,
-				 struct memstick_request **mrq)
+static int h_msb_send_command(struct memstick_dev *card)
 {
 	struct msb_data *msb = memstick_get_drvdata(card);
+	struct memstick_request *mrq = &card->current_mrq;
+
 	u8 intreg;
 	int len;
 
-	memstick_allocate_request(card, mrq);
-	if ((*mrq)->error) {
+	if (mrq->error) {
 		dbg("send_command: unknown error");
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, mrq->error);
 	}
 
 again:
@@ -486,27 +479,26 @@ again:
 		if (!memstick_write_regs(card,
 			offsetof(struct ms_register, param),
 			len,
-			(unsigned char *)&msb->regs.param,
-			*mrq))
+			(unsigned char *)&msb->regs.param))
 			return 0;
 		break;
 
 	case 1: /* execute the command*/
-		memstick_init_req(*mrq, MS_TPC_SET_CMD, &msb->command_value, 1);
+		memstick_init_req(card, MS_TPC_SET_CMD, &msb->command_value, 1);
 		break;
 
 	case 2: /* send INT request */
-		if (memstick_read_int_reg(card, *mrq, -1))
+		if (memstick_read_int_reg(card, -1))
 			break;
 		card->state++;
 
 	case 3: /* poll for int bits */
-		intreg = (*mrq)->data[0];
+		intreg = mrq->data[0];
 
 		if (intreg & MEMSTICK_INT_CMDNAK)
-			return memstick_complete_request(card, *mrq, -EIO);
+			return memstick_state_machine_exit(card, -EIO);
 		if (intreg & MEMSTICK_INT_ERR)
-			return memstick_complete_request(card, *mrq, -EBADMSG);
+			return memstick_state_machine_exit(card, -EBADMSG);
 
 
 		if (!(intreg & MEMSTICK_INT_CED)) {
@@ -514,47 +506,45 @@ again:
 			goto again;
 		}
 
-		memstick_read_int_reg_cleanup(card);
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, 0);
 	}
 	card->state++;
 	return 0;
 }
 
 /* Small handler for card reset */
-static int h_msb_reset(struct memstick_dev *card,
-				 struct memstick_request **mrq)
+static int h_msb_reset(struct memstick_dev *card)
 {
 	u8 command = MS_CMD_RESET;
+	struct memstick_request *mrq = &card->current_mrq;
 
-	memstick_allocate_request(card, mrq);
-	if ((*mrq)->error)
-		return memstick_complete_request(card, *mrq, 0);
+	if (mrq->error)
+		return memstick_state_machine_exit(card, mrq->error);
 
 	switch (card->state) {
 	case 0:
-		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
-		(*mrq)->need_card_int = 0;
+		memstick_init_req(card, MS_TPC_SET_CMD, &command, 1);
+		mrq->need_card_int = 0;
 		break;
 	case 1:
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, 0);
 	}
 	card->state++;
 	return 0;
 }
 
 /* This handler is used to do serial->parallel switch */
-static int h_msb_parallel_switch(struct memstick_dev *card,
-					struct memstick_request **mrq)
+static int h_msb_parallel_switch(struct memstick_dev *card)
 {
 	struct msb_data *msb = memstick_get_drvdata(card);
+	struct memstick_request *mrq = &card->current_mrq;
+
 	struct memstick_host *host = card->host;
 
-	memstick_allocate_request(card, mrq);
-	if ((*mrq)->error) {
+	if (mrq->error) {
 		dbg("parallel_switch: error");
 		msb->regs.param.system &= ~MEMSTICK_SYS_PAM;
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, mrq->error);
 	}
 
 	switch (card->state) {
@@ -565,19 +555,18 @@ static int h_msb_parallel_switch(struct memstick_dev *card,
 		if (!memstick_write_regs(card,
 			offsetof(struct ms_register, param),
 			1,
-			(unsigned char *)&msb->regs.param,
-			*mrq))
+			(unsigned char *)&msb->regs.param))
 			return 0;
 		break;
 
 	case 1: /* Set parallel interface on our side + send a dummy request
 			to see if card responds */
 		host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_PAR4);
-		memstick_init_req(&card->current_mrq, MS_TPC_GET_INT, NULL, 1);
+		memstick_init_req(card, MS_TPC_GET_INT, NULL, 1);
 		break;
 
 	case 2:
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, 0);
 	}
 	card->state++;
 	return 0;
@@ -590,21 +579,15 @@ static int msb_reset(struct msb_data *msb)
 {
 	bool was_parallel = msb->regs.param.system & MEMSTICK_SYS_PAM;
 	struct memstick_dev *card = msb->card;
-	struct memstick_host *host = card->host;
 	int error;
 
-	memstick_invalidate_reg_window(card);
-
-	/* Reset the controller */
-	host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
-	msleep(100);
-	host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
-	host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
-
-
 	/* Reset the card */
 	msb->regs.param.system = MEMSTICK_SYS_BAMD;
-	error = memstick_run_state_machine(card, h_msb_reset);
+	error = memstick_reset_card(card);
+
+	if (!error)
+		error = memstick_run_state_machine(card, h_msb_reset, true);
+
 	if (error) {
 		dbg("Failed to reset the card");
 		msb->read_only = true;
@@ -622,7 +605,8 @@ static int msb_switch_to_parallel(struct msb_data *msb)
 {
 	int error;
 
-	error = memstick_run_state_machine(msb->card, h_msb_parallel_switch);
+	error = memstick_run_state_machine(msb->card,
+						h_msb_parallel_switch, true);
 	if (error) {
 		ms_printk("Switch to parallel failed");
 		msb->regs.param.system &= ~MEMSTICK_SYS_PAM;
@@ -650,7 +634,7 @@ static int msb_set_overwrite_flag(struct msb_data *msb,
 
 	dbg_verbose("changing overwrite flag to %02x for sector %d, page %d",
 							flag, pba, page);
-	return memstick_run_state_machine(msb->card, h_msb_send_command);
+	return memstick_run_state_machine(msb->card, h_msb_send_command, true);
 }
 
 static int msb_mark_bad(struct msb_data *msb, int pba)
@@ -687,7 +671,7 @@ static int msb_erase_block(struct msb_data *msb, u16 pba)
 
 
 		error = memstick_run_state_machine(msb->card,
-							h_msb_send_command);
+						h_msb_send_command, true);
 		if (!error || msb_reset(msb))
 			break;
 	}
@@ -747,7 +731,8 @@ static int msb_read_page(struct msb_data *msb,
 
 		msb->current_sg = sg;
 		msb->sg_offset = 0;
-		error = memstick_run_state_machine(msb->card, h_msb_read_page);
+		error = memstick_run_state_machine(msb->card,
+						h_msb_read_page, true);
 
 
 		if (error == -EUCLEAN) {
@@ -798,7 +783,7 @@ static int msb_read_oob(struct msb_data *msb, u16 pba, u16 page,
 		return -EINVAL;
 	}
 
-	error = memstick_run_state_machine(msb->card, h_msb_read_page);
+	error = memstick_run_state_machine(msb->card, h_msb_read_page, true);
 	*extra = msb->regs.extra_data;
 
 	if (error == -EUCLEAN) {
@@ -889,7 +874,7 @@ static int msb_write_block(struct msb_data *msb,
 		msb->sg_offset = 0;
 
 		error = memstick_run_state_machine(msb->card,
-							h_msb_write_block);
+						h_msb_write_block, true);
 
 		/* Sector we just wrote to is assumed erased since its pba
 			was erased. If it wasn't erased, write will succeed
@@ -2108,6 +2093,7 @@ static int msb_resume(struct memstick_dev *card)
 
 	new_msb->card = card;
 	memstick_set_drvdata(card, new_msb);
+	spin_lock_init(&new_msb->q_lock);
 
 	if (msb_init_card(card))
 		goto out;
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index c589d26..c575f26 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -372,40 +372,38 @@ static sysfs_show_t mspro_block_attr_show(unsigned char tag)
  * finished (and request processor should come back some time later).
  */
 
-static int h_mspro_block_reset(struct memstick_dev *card,
-					struct memstick_request **mrq)
+static int h_mspro_block_reset(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
 	struct ms_status_register *status;
+	struct memstick_request *mrq = &card->current_mrq;
+
 	u8 intreg;
 
-	memstick_allocate_request(card, mrq);
-	if ((*mrq)->error) {
-		dbg(card, "Error here");
-		return memstick_complete_request(card, *mrq, 0);
-	}
+	if (mrq->error)
+		return memstick_state_machine_exit(card, mrq->error);
 
 again:
 	switch (card->state) {
 
 	case 0: /* send request for INT reg */
-		if (memstick_read_int_reg(card, *mrq, 10000))
+		if (memstick_read_int_reg(card, 10000))
 			break;
 		card->state++;
 
 	case 1: /* process the INT reg and loop if nessesary */
-		intreg = (*mrq)->data[0];
+		intreg = mrq->data[0];
 
 		if (intreg & MEMSTICK_INT_ERR) {
 			if ((intreg & MEMSTICK_INT_CMDNAK)) {
 				msb->read_only = true;
-				return memstick_complete_request(card, *mrq, 0);
+				return memstick_state_machine_exit(card, 0);
 			}
-			return memstick_complete_request(card, *mrq, -EIO);
+			return memstick_state_machine_exit(card, -EIO);
 		}
 
 		if (intreg & MEMSTICK_INT_CMDNAK)
-			return memstick_complete_request(card, *mrq, -EIO);
+			return memstick_state_machine_exit(card, -EIO);
 
 
 		if (!(intreg & MEMSTICK_INT_CED)) {
@@ -413,22 +411,20 @@ again:
 			goto again;
 		}
 
-		memstick_read_int_reg_cleanup(card);
 		card->state++;
 
 	case 2: /* read the R/O status */
 
 		if (!memstick_read_regs(card,
 			offsetof(struct mspro_register, status),
-			sizeof(struct ms_status_register),
-			*mrq))
+			sizeof(struct ms_status_register)))
 			return 0;
 		break;
 
 	case 3: /* process the result & done */
-		status = (struct ms_status_register *)(*mrq)->data;
+		status = (struct ms_status_register *)mrq->data;
 		msb->read_only = status->status0 & MEMSTICK_STATUS0_WP;
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, 0);
 	}
 
 	card->state++;
@@ -436,14 +432,14 @@ again:
 
 }
 
-static int h_mspro_block_switch_interface(struct memstick_dev *card,
-					struct memstick_request **mrq)
+static int h_mspro_block_switch_interface(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
+	struct memstick_request *mrq = &card->current_mrq;
+
 	int error;
-	memstick_allocate_request(card, mrq);
-	if ((*mrq)->error)
-		return memstick_complete_request(card, *mrq, 0);
+	if (mrq->error)
+		return memstick_state_machine_exit(card, mrq->error);
 
 	switch (card->state) {
 
@@ -451,8 +447,7 @@ static int h_mspro_block_switch_interface(struct memstick_dev *card,
 		if (!memstick_write_regs(card,
 			offsetof(struct mspro_register, param),
 			1,
-			&msb->system,
-			*mrq))
+			&msb->system))
 			return 0;
 		break;
 
@@ -461,13 +456,13 @@ static int h_mspro_block_switch_interface(struct memstick_dev *card,
 		error = card->host->set_param(card->host,
 				MEMSTICK_INTERFACE, msb->target_interface);
 		if (error)
-			return memstick_complete_request(card, *mrq, -EFAULT);
+			return memstick_state_machine_exit(card, -EFAULT);
 
-		memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
+		memstick_init_req(card, MS_TPC_GET_INT, NULL, 1);
 		break;
 
 	case 2: /* done */
-		return memstick_complete_request(card, *mrq, 0);
+		return memstick_state_machine_exit(card, 0);
 
 	}
 
@@ -476,21 +471,20 @@ static int h_mspro_block_switch_interface(struct memstick_dev *card,
 }
 
 /* State machine that handles the IO. Heart of the driver */
-static int h_mspro_block_transfer_data(struct memstick_dev *card,
-				       struct memstick_request **mrq)
+static int h_mspro_block_transfer_data(struct memstick_dev *card)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
+	struct memstick_request *mrq = &card->current_mrq;
+
 	unsigned char intreg = 0, command;
 	struct scatterlist t_sg = { 0 };
 	unsigned long flags;
 	int error;
 	size_t t_offset;
 
-	memstick_allocate_request(card, mrq);
-
-	if ((*mrq)->error) {
-		dbg(card, "IO: error (%d) executing %s", (*mrq)->error,
-			memstick_debug_get_tpc_name((*mrq)->tpc));
+	if (mrq->error) {
+		dbg(card, "IO: error (%d) executing %s", mrq->error,
+			memstick_debug_get_tpc_name(mrq->tpc));
 		msb->io_error = true;
 		card->state = 5;
 	}
@@ -498,17 +492,17 @@ again:
 	switch (card->state) {
 
 	case 0: /* send read/write command + args */
-		memstick_init_req(*mrq, MS_TPC_EX_SET_CMD,
+		memstick_init_req(card, MS_TPC_EX_SET_CMD,
 					&msb->arg, sizeof(msb->arg));
 		break;
 
 	case 1: /* read the INT register */
-		if (memstick_read_int_reg(card, *mrq, -1))
+		if (memstick_read_int_reg(card, -1))
 			break;
 		card->state++;
 
 	case 2: /* process the int register */
-		intreg = (*mrq)->data[0];
+		intreg = mrq->data[0];
 
 		if (intreg & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) {
 			dbg(card, "IO: card I/O error");
@@ -524,7 +518,6 @@ again:
 				goto again;
 			}
 
-			memstick_read_int_reg_cleanup(card);
 			card->state = 6;
 			goto again;
 		}
@@ -535,7 +528,6 @@ again:
 			goto again;
 		}
 
-		memstick_read_int_reg_cleanup(card);
 		card->state++;
 
 	case 3: /* init transfer of the data */
@@ -549,11 +541,11 @@ again:
 			msb->page_size,
 			offset_in_page(t_offset));
 
-		memstick_init_req_sg(*mrq, msb->data_dir == READ
+		memstick_init_req_sg(card, msb->data_dir == READ
 					   ? MS_TPC_READ_LONG_DATA
 					   : MS_TPC_WRITE_LONG_DATA,
 				     &t_sg);
-		(*mrq)->need_card_int = 1;
+		mrq->need_card_int = 1;
 		break;
 
 	case 4: /* switch to next page */
@@ -570,34 +562,36 @@ again:
 
 	case 5: /* after a error send STOP command */
 		command = MSPRO_CMD_STOP;
-		memstick_init_req(*mrq, MS_TPC_SET_CMD, &command, 1);
+		memstick_init_req(card, MS_TPC_SET_CMD, &command, 1);
 		break;
 
 	case 6: /* request complete - get next one*/
 		spin_lock_irqsave(&msb->q_lock, flags);
 
 		if (msb->io_error)
-			(*mrq)->error = -EIO;
+			mrq->error = -EIO;
 
 		if (msb->block_req) {
-			mspro_block_complete_req(card, (*mrq)->error);
-			error = mspro_block_issue_req(card);
+			mspro_block_complete_req(card, mrq->error);
+			error = mspro_block_issue_req(card, false);
+
+			if (error) {
+				WARN_ON(msb->block_req);
 
-			if (!msb->block_req) {
 				dbg_v(card, "IO: out of requests");
-				memstick_complete_request(card, *mrq, 0);
+				error = memstick_state_machine_exit(card, 0);
 				spin_unlock_irqrestore(&msb->q_lock, flags);
-				return -EAGAIN;
+				return error;
 			}
 
 			spin_unlock_irqrestore(&msb->q_lock, flags);
-			(*mrq)->error = 0;
+			mrq->error = 0;
 			card->state = 0;
 			goto again;
 
 		} else {
 			spin_unlock_irqrestore(&msb->q_lock, flags);
-			return memstick_complete_request(card, *mrq, 0);
+			return memstick_state_machine_exit(card, 0);
 		}
 	default:
 		BUG();
@@ -629,7 +623,7 @@ static int mspro_block_setup_io(struct memstick_dev *card, int direction,
  * Start execution of next block request,
  * or continue execution of the current one
  */
-static int mspro_block_issue_req(struct memstick_dev *card)
+static int mspro_block_issue_req(struct memstick_dev *card, bool first)
 {
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
 	sector_t sec;
@@ -642,10 +636,17 @@ again:
 		msb->block_req = blk_fetch_request(msb->queue);
 		if (!msb->block_req) {
 			dbg(card, "IO: failed, because queue is empty");
-			return -EAGAIN;
+			return -ENXIO;
 		}
 	}
 
+	if (msb->eject) {
+		dbg(card, "IO: Refusing request, because card is removed");
+		__blk_end_request_all(msb->block_req, -ENODEV);
+		msb->block_req = NULL;
+		goto again;
+	}
+
 	sec = blk_rq_pos(msb->block_req) << 9;
 	sector_div(sec, msb->page_size);
 	pages = blk_rq_bytes(msb->block_req) / msb->page_size;
@@ -665,15 +666,14 @@ again:
 	dbg(card, "IO: %s: lba %x, pages %x", is_read ? "read" : "write",
 					(unsigned int)sec, pages);
 
-	/* can't use memstick_run_state_machine because we don't block */
-	card->next_request = h_mspro_block_transfer_data;
+	if (first)
+		memstick_run_state_machine(card,
+					h_mspro_block_transfer_data, false);
 	return 0;
 }
 
 /*
- * Completes execution of current block request.
- *  After execution of this function, the msb->block_req might or might not
- *  be NULL. If it is, it means we don't have any more requests to process
+ * Completes execution of current block request
  */
 static void mspro_block_complete_req(struct memstick_dev *card, int error)
 {
@@ -705,7 +705,8 @@ static int mspro_block_read_attribute(struct memstick_dev *card,
 	sg_init_one(&msb->req_sg[0], buffer, len);
 	mspro_block_setup_io(card, READ, offset / msb->page_size,
 				len / msb->page_size, MSPRO_CMD_READ_ATRB);
-	return memstick_run_state_machine(card, h_mspro_block_transfer_data);
+	return memstick_run_state_machine(card,
+					h_mspro_block_transfer_data, true);
 }
 
 /* Memory allocated for attributes by this function should be freed by
@@ -724,7 +725,7 @@ static int mspro_block_read_attributes(struct memstick_dev *card)
 
 	int cnt, error, attr_count;
 
-	/* Allocate initial buffer */
+	/* Allocate cache buffer */
 	buffer_size = msb->page_size;
 	buffer_address = 0;
 	buffer = kmalloc(buffer_size, GFP_KERNEL);
@@ -898,61 +899,42 @@ static void mspro_block_submit_req(struct request_queue *q)
 {
 	struct memstick_dev *card = q->queuedata;
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
-	struct request *req = NULL;
-
-	dbg(card, "IO: block layer submits us new request");
+	int error;
 
 	if (msb->block_req) {
-		dbg(card, "IO: Already have an request");
+		dbg_v(card,
+			"IO: block layer submits request, while we have one");
 		return;
 	}
 
-	if (msb->eject) {
-		dbg(card, "IO: Refusing requests on removed card");
-		while ((req = blk_fetch_request(q)) != NULL)
-			__blk_end_request_all(req, -ENODEV);
-		return;
-	}
+	dbg(card, "IO: block layer wakes us up for request processing");
 
-	if (mspro_block_issue_req(card)) {
-		dbg(card, "IO: Falure to issue request");
-		msb->block_req = NULL;
-	} else
-		memstick_new_req(card->host);
+	error = mspro_block_issue_req(card, true);
+	WARN_ON(error && msb->block_req != NULL);
 }
 
 /* Reset the card */
-static int mspro_block_reset(struct memstick_dev *card, bool full)
+static int mspro_block_reset(struct memstick_dev *card)
 {
-	struct memstick_host *host = card->host;
 	struct mspro_block_data *msb = memstick_get_drvdata(card);
 	int error;
 
 	dbg(card, "resetting the card");
 
 	msb->system = MEMSTICK_SYS_SERIAL;
+	error = memstick_reset_card(card);
 
-	error = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
-	if (error)
-		return error;
-	msleep(50);
-
-	error = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
-	if (error)
-		return error;
-
-	error = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
-	if (error)
+	if (error) {
+		dbg(card, "host controller reset failed");
 		return error;
+	}
 
 	msleep(150);
+	error = memstick_run_state_machine(card,
+				h_mspro_block_reset, true);
 
-	/* Invalidate the register window */
-	memstick_invalidate_reg_window(card);
-
-	error = memstick_run_state_machine(card, h_mspro_block_reset);
 	if (error) {
-		dbg(card, "card reset failure");
+		dbg(card, "card reset failed");
 		return error;
 	}
 
@@ -974,11 +956,11 @@ static int mspro_block_switch_interface(struct memstick_dev *card)
 	msb->system = MEMSTICK_SYS_PAR4;
 	msb->target_interface = MEMSTICK_PAR4;
 	error = memstick_run_state_machine(card,
-						h_mspro_block_switch_interface);
+					h_mspro_block_switch_interface, true);
 	if (error) {
 		dbg(card, "failure to switch to parallel 4 bit inteface");
 		card->caps &= ~MEMSTICK_CAP_PAR4;
-		return mspro_block_reset(card, true);
+		return mspro_block_reset(card);
 	}
 
 	if (!(card->caps & host->caps & MEMSTICK_CAP_PAR8))
@@ -988,11 +970,11 @@ static int mspro_block_switch_interface(struct memstick_dev *card)
 	msb->target_interface = MEMSTICK_PAR8;
 	dbg(card, "trying to switch to parallel 8 bit inteface");
 	error = memstick_run_state_machine(card,
-					h_mspro_block_switch_interface);
+				h_mspro_block_switch_interface, true);
 	if (error) {
 		dbg(card, "failure to switch to parallel 8 bit inteface");
 		card->caps &= ~MEMSTICK_CAP_PAR8;
-		return mspro_block_reset(card, true);
+		return mspro_block_reset(card);
 	}
 
 	return error;
@@ -1006,7 +988,7 @@ static int mspro_block_init_card(struct memstick_dev *card)
 	/* TODO: can't we know what card supports from attributes? */
 	card->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8;
 
-	rc = mspro_block_reset(card, false);
+	rc = mspro_block_reset(card);
 	if (rc)
 		return rc;
 
@@ -1058,10 +1040,13 @@ static int mspro_block_init_disk(struct memstick_dev *card)
 
 	msb->page_size = be16_to_cpu(sys_info->unit_size);
 
-	if (!idr_pre_get(&mspro_block_disk_idr, GFP_KERNEL))
+	mutex_lock(&mspro_block_disk_lock);
+
+	if (!idr_pre_get(&mspro_block_disk_idr, GFP_KERNEL)) {
+		mutex_unlock(&mspro_block_disk_lock);
 		return -ENOMEM;
+	}
 
-	mutex_lock(&mspro_block_disk_lock);
 	rc = idr_get_new(&mspro_block_disk_idr, card, &disk_id);
 	mutex_unlock(&mspro_block_disk_lock);
 
@@ -1250,6 +1235,8 @@ static int mspro_block_resume(struct memstick_dev *card)
 
 	new_msb->card = card;
 	memstick_set_drvdata(card, new_msb);
+	spin_lock_init(&new_msb->q_lock);
+
 	if (mspro_block_init_card(card))
 		goto out_free;
 
diff --git a/drivers/memstick/core/mspro_block.h b/drivers/memstick/core/mspro_block.h
index 86a340d..120f306 100644
--- a/drivers/memstick/core/mspro_block.h
+++ b/drivers/memstick/core/mspro_block.h
@@ -160,7 +160,7 @@ struct mspro_block_data {
 
 static void mspro_block_complete_req(struct memstick_dev *card, int error);
 static int mspro_block_switch_interface(struct memstick_dev *card);
-static int mspro_block_issue_req(struct memstick_dev *card);
+static int mspro_block_issue_req(struct memstick_dev *card, bool first);
 
 #define __dbg(card, level, format, ...) \
 	do { \
diff --git a/include/linux/memstick.h b/include/linux/memstick.h
index bb61bfc..6328913 100644
--- a/include/linux/memstick.h
+++ b/include/linux/memstick.h
@@ -256,11 +256,11 @@ struct memstick_device_id {
  * and besides waiting, the host driver must read the INT register
  * (via data lines)
  *
- * In addition to that if hardware is 'smart', it could be able to read
- * the INT register even in parallel mode by sending MS_TPC_GET_INT
- * by itself. This capablility is indicated by host via
- * MEMSTICK_CAP_AUTO_GET_INT.
- * Then serial mode behavier must be the same as parallel
+ * In addition to that if hardware is 'smart', and is able to read
+ * the INT register even in serial mode by sending MS_TPC_GET_INT
+ * by itself (This capablility is indicated by host via
+ * MEMSTICK_CAP_AUTO_GET_INT),
+ * then the serial mode behavier must be the same as the parallel.
  */
 struct memstick_request {
 	unsigned char tpc;
@@ -288,20 +288,20 @@ struct memstick_dev {
 	/* Check that media driver is still willing to operate the device. */
 	int                      (*check)(struct memstick_dev *card);
 	/* Get next request from the media driver.                         */
-	int                      (*next_request)(struct memstick_dev *card,
-						 struct memstick_request **mrq);
+	int                      (*next_request)(struct memstick_dev *card);
 	/* Tell the media driver to stop doing things                      */
 	void                     (*stop)(struct memstick_dev *card);
 	/* Allow the media driver to continue                              */
 	void                     (*start)(struct memstick_dev *card);
 
 	struct device            dev;
+	int			 caps;
 
-	/* Private area for request processing */
+	/* Private area for state machines */
 	bool			 int_polling;
 	unsigned long		 int_timeout;
 	int			 state;
-	int			 caps;
+	bool			 state_machine_running;
 };
 
 struct memstick_host {
@@ -338,9 +338,6 @@ struct memstick_driver {
 	struct device_driver      driver;
 };
 
-int memstick_register_driver(struct memstick_driver *drv);
-void memstick_unregister_driver(struct memstick_driver *drv);
-
 struct memstick_host *memstick_alloc_host(unsigned int extra,
 					  struct device *dev);
 
@@ -350,40 +347,32 @@ void memstick_free_host(struct memstick_host *host);
 void memstick_detect_change(struct memstick_host *host);
 void memstick_suspend_host(struct memstick_host *host);
 void memstick_resume_host(struct memstick_host *host);
-
-void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
-			  const struct scatterlist *sg);
-void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
-		       const void *buf, size_t length);
 int memstick_next_req(struct memstick_host *host,
 		      struct memstick_request **mrq);
-void memstick_new_req(struct memstick_host *host);
-
-int memstick_set_rw_addr(struct memstick_dev *card);
 
 /* Helpers for high level drivers */
-void memstick_allocate_request(struct memstick_dev *card,
-					struct memstick_request **mrq);
 
-int memstick_complete_request(struct memstick_dev *card,
-			struct memstick_request *req, int error);
+int memstick_register_driver(struct memstick_driver *drv);
+void memstick_unregister_driver(struct memstick_driver *drv);
+
+int memstick_run_state_machine(struct memstick_dev *card,
+	int   (*next_request)(struct memstick_dev *card), bool async);
 
-int memstick_read_int_reg(struct memstick_dev *card,
-			struct memstick_request *req, long timeout);
+void memstick_init_req_sg(struct memstick_dev *card, unsigned char tpc,
+			  const struct scatterlist *sg);
+void memstick_init_req(struct memstick_dev *card, unsigned char tpc,
+		       const void *buf, size_t length);
 
-void memstick_read_int_reg_cleanup(struct memstick_dev *card);
+int memstick_read_int_reg(struct memstick_dev *card, long timeout);
 
-int memstick_read_regs(struct memstick_dev *card, int offset, int len,
-	struct memstick_request *req);
+int memstick_read_regs(struct memstick_dev *card, int offset, int len);
 
 int memstick_write_regs(struct memstick_dev *card, int offset, int len,
-	char *buf, struct memstick_request *req);
+	void *buf);
 
-void memstick_invalidate_reg_window(struct memstick_dev *card);
+int memstick_state_machine_exit(struct memstick_dev *card, int error);
 
-int memstick_run_state_machine(struct memstick_dev *card,
-	int   (*next_request)(struct memstick_dev *card,
-				struct memstick_request **mrq));
+int memstick_reset_card(struct memstick_dev *card);
 
 const char *memstick_debug_get_tpc_name(int tpc);
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/6] Results of my work on memorystick subsystem
  2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
                   ` (5 preceding siblings ...)
  2010-10-16 23:12 ` [PATCH 6/6] memstick: stop passing useless pointer to card->current_mrq + refactoring Maxim Levitsky
@ 2010-10-17 22:55 ` Maxim Levitsky
  2010-10-18  7:05   ` Alex Dubov
  6 siblings, 1 reply; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-17 22:55 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML

On Sun, 2010-10-17 at 01:12 +0200, Maxim Levitsky wrote:
> Hi,
> 
> Here is a result of lot of work I did improving the memorystick
> subsystem and its drivers.
Comments?
Flames?
Suggestions?

Best regards,
	Maxim Levitsky

> 
> patch #1 add common code to memorystick core that makes card drivers easier
> to understand
> 
> patch #2 just add a driver for my card reader
> 
> patch #3 reworks the Jmicron driver.
> I can write a novel about the problems I had to go through with this device
> (mostly hardware bugs I belive).
> So I refactored the driver and added a lot of debug output future users.
> Currently both MS and MSPro work fine.
> 
> patch #4 adds support for lagacy memorysticks.
> Everything just works, not a single corruption this far.
> 
> patch #5, is what I did recently.
> I rewrote large chunks on mspro_blk.c to use the common code I added in patch #1.
> I also added lot of debug code, comments.
> 
> patch #6 adds more cleanups.
> 
> Now register window changes are completely hidden and automatic.
> Functions that state machines call are explictly marked as so,
> and that assumption is tested.
> the card->current_mrq isn't passed as a pointer to functions
> as this just complicates the code.
> 
> Code is tested with 2 mspro cards and one MS card.
> 
> Thanks again to Alex for his work.
> 
> ***Changes from V1 ***
> 
> * Fixed brown paper bug in memstick core.
> memset had its 2 and 3 arguments swapped.
> 
> * Cleaned a lot the PIO code in my r592 driver.
> Now it finally looks sane.
> Also tested it throughfully.
> 
> * Endiannes fixes in Jmicron driver and r592
> 
> Best regards,
> 	Maxim Levitsky
> 
> ***Appendix***
> 
> Jmicron hardware bugs (the novel):
> 
> #1: FIFO write ready bit in INT status register is stuck to 1.
>    It is stuck forever as soon as fifo
>    is used for writing even once.
>    Therefore if interrupt is shared (and here it is), its easy
>    to 'service' the device while it doesn't need any service
> 
> 
> #2: Its not possible to stuff the FIFO before TPC transfer.
>    One really have to wait for write ready interrupt, even though the write ready status is stuck.
> 
> 
> #3: TPCs with non 4 aligned length woes:
>    Facts:
> 
>    * non 4 aligned DMA write hangs the system hard, maybe on bus level.
> 
>    * PIO read succedes but controller truncates the data stored in the FIFO to closest 4 byte boundary.
>    That is if you read 26 bytes, it will save 24 bytes in the FIFO
> 
>    * PIO non-aligned write work, expect that they sometimes hose the DMA.... (regardless of the alignment)
> 
>    * TPC_P0, TPC_P1 not aligned transfters work just fine despite a statement in the datasheet
>    that they are undefined.... (only mention of this problem)
> 
> 
> #4: As soon as write PIO is used, then later write DMA fails.
>    Facts:
> 
>    * This is triggered only by PIO write of registers
>    (only happens in ms_block.c when it writes param + oob. Thats why mspro_blk isn't affected)
>    Doing short DMA writes is a nice workaround.
> 
>    * Doing PIO writes in h_msb_read_page don't cause the problem.
>      Therefore the bug causing sequence should be similiar to h_msb_write_block:
> 
>        1. PIO write of param + extra (10 bytes) or even padded to 12 or 16 bytes
>        2. inline write (TPC_P0) of MS_CMD_BLOCK_WRITE + wait for int
>        3. read of INT register via STATUS
>        3. DMA write of MS_TPC_WRITE_LONG_DATA
>        4. DMA write of MS_TPC_WRITE_LONG_DATA
>            ---------timeout-----------
>  
>    * In fact first DMA write succedes, but next one fails, and so do all following writes
> 
>    * The problem persits till reboot, and shows up even if PIO isn't used again.
>    Other way to "fix" it, is to put device in D3 and back to D0
> 
>    * Serial/parallel mode don't affect anything.
> 
>    After bug reproduction:
> 
>     * DMA writes stop working completely, therefore mspro_blk looses writes as well
> 
>     * PIO still works just fine. Its possible just to load the driver without dma support, and it works correctly.
> 
>     * DMA reads work just fine.
> 
> #5: Auto_Get_INT feature just doesn't work.
>    Datasheet says that intreg is placed to TPC_P0, but that doesn't happen....
>    card status register is 0.
> 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/6] Results of my work on memorystick subsystem
  2010-10-17 22:55 ` [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
@ 2010-10-18  7:05   ` Alex Dubov
  2010-10-18 14:06     ` Maxim Levitsky
  0 siblings, 1 reply; 12+ messages in thread
From: Alex Dubov @ 2010-10-18  7:05 UTC (permalink / raw)
  To: Maxim Levitsky; +Cc: Andrew Morton, LKML


--- On Sun, 17/10/10, Maxim Levitsky <maximlevitsky@gmail.com> wrote:

> > 
> > Here is a result of lot of work I did improving the
> memorystick
> > subsystem and its drivers.
> Comments?
> Flames?
> Suggestions?
> 

My opinion is, that in the current form your patches are completely
unworkable.

I suggest a following approach, consisting of 3 clearly separated steps:

Step 1 - a set of functional patches to jmicron driver.
Step 2 - a set of functional patches to mspro driver.
Step 3 - common code elimination (the whole thing is not that large, it
can tolerate some code duplication).

When we done with this, we can discuss whatever cosmetic changes you want
to do (moving stuff around and , possibly, renaming, which I don't see as
necessary at all).






      

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/6] Results of my work on memorystick subsystem
  2010-10-18  7:05   ` Alex Dubov
@ 2010-10-18 14:06     ` Maxim Levitsky
  2010-10-19  9:40       ` Alex Dubov
  0 siblings, 1 reply; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-18 14:06 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML

On Mon, 2010-10-18 at 00:05 -0700, Alex Dubov wrote:
> --- On Sun, 17/10/10, Maxim Levitsky <maximlevitsky@gmail.com> wrote:
> 
> > > 
> > > Here is a result of lot of work I did improving the
> > memorystick
> > > subsystem and its drivers.
> > Comments?
> > Flames?
> > Suggestions?
> > 
> 
> My opinion is, that in the current form your patches are completely
> unworkable.
> 
> I suggest a following approach, consisting of 3 clearly separated steps:
> 
> Step 1 - a set of functional patches to jmicron driver.
> Step 2 - a set of functional patches to mspro driver.
> Step 3 - common code elimination (the whole thing is not that large, it
> can tolerate some code duplication).
> 
> When we done with this, we can discuss whatever cosmetic changes you want
> to do (moving stuff around and , possibly, renaming, which I don't see as
> necessary at all).
Alex,
In jmicron driver I did move functions around and renamed them.
I am not against doing that in separate patch.

But for mspro driver, it would be really hard for me to split it.
I have rewritten a lot of code there.
Can't we just review the end result instead of the patch?

To split the patches, I pretty much need to rewrite the whole thing once
gain.
I don't want to do that.

Best regards,
	Maxim Levitsky


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/6] Results of my work on memorystick subsystem
  2010-10-18 14:06     ` Maxim Levitsky
@ 2010-10-19  9:40       ` Alex Dubov
  2010-10-19 10:47         ` Maxim Levitsky
  0 siblings, 1 reply; 12+ messages in thread
From: Alex Dubov @ 2010-10-19  9:40 UTC (permalink / raw)
  To: Maxim Levitsky; +Cc: Andrew Morton, LKML

--- On Mon, 18/10/10, Maxim Levitsky <maximlevitsky@gmail.com> wrote:

> 
> But for mspro driver, it would be really hard for me to
> split it.
> I have rewritten a lot of code there.
> Can't we just review the end result instead of the patch?
> 
> To split the patches, I pretty much need to rewrite the
> whole thing once
> gain.
> I don't want to do that.
> 

First, a useful citation (Documentation/HOWTO:548):
"The Linux kernel community does not gladly accept large chunks of code
dropped on it all at once.  The changes need to be properly introduced,
discussed, and broken up into tiny, individual portions."

I actually attempted to submit a large "full rewrite" patch early on
during development of one of my drivers. I was a sole developer and it was
not even on a stable tree, as far as I can remember. Yet, that patch was
dropped immediately with the above given reason.

And yes, I had to rewrite the whole thing again, this time in small steps.



      

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/6] Results of my work on memorystick subsystem
  2010-10-19  9:40       ` Alex Dubov
@ 2010-10-19 10:47         ` Maxim Levitsky
  0 siblings, 0 replies; 12+ messages in thread
From: Maxim Levitsky @ 2010-10-19 10:47 UTC (permalink / raw)
  To: Alex Dubov; +Cc: Andrew Morton, LKML

On Tue, 2010-10-19 at 02:40 -0700, Alex Dubov wrote:
> --- On Mon, 18/10/10, Maxim Levitsky <maximlevitsky@gmail.com> wrote:
> 
> > 
> > But for mspro driver, it would be really hard for me to
> > split it.
> > I have rewritten a lot of code there.
> > Can't we just review the end result instead of the patch?
> > 
> > To split the patches, I pretty much need to rewrite the
> > whole thing once
> > gain.
> > I don't want to do that.
> > 
> 
> First, a useful citation (Documentation/HOWTO:548):
> "The Linux kernel community does not gladly accept large chunks of code
> dropped on it all at once.  The changes need to be properly introduced,
> discussed, and broken up into tiny, individual portions."
> 
> I actually attempted to submit a large "full rewrite" patch early on
> during development of one of my drivers. I was a sole developer and it was
> not even on a stable tree, as far as I can remember. Yet, that patch was
> dropped immediately with the above given reason.
> 
> And yes, I had to rewrite the whole thing again, this time in small steps.
Understood...

Fortunately, yesterday I have finished splitting all but jmicron patch,
and I finish it today, test patches for working (final state does work,
I tested), and resumbit.

Best regards,
	Maxim Levitsky




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2010-10-19 10:47 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-16 23:12 [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
2010-10-16 23:12 ` [PATCH 1/6] memstick: core: add series of common helpers Maxim Levitsky
2010-10-16 23:12 ` [PATCH 2/6] memstick: Add driver for Ricoh R5C592 Card reader Maxim Levitsky
2010-10-16 23:12 ` [PATCH 3/6] memstick: jmb38x: Driver rework Maxim Levitsky
2010-10-16 23:12 ` [PATCH 4/6] memstick: add support for legacy memorysticks Maxim Levitsky
2010-10-16 23:12 ` [PATCH 5/6] memstick: mspro_block: refactoring Maxim Levitsky
2010-10-16 23:12 ` [PATCH 6/6] memstick: stop passing useless pointer to card->current_mrq + refactoring Maxim Levitsky
2010-10-17 22:55 ` [PATCH 0/6] Results of my work on memorystick subsystem Maxim Levitsky
2010-10-18  7:05   ` Alex Dubov
2010-10-18 14:06     ` Maxim Levitsky
2010-10-19  9:40       ` Alex Dubov
2010-10-19 10:47         ` Maxim Levitsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox