Merge git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Sat, 9 Feb 2008 19:14:20 +0000 (11:14 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Sat, 9 Feb 2008 19:14:20 +0000 (11:14 -0800)
* git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild:
  Kbuild: Fix deb-pkg target to work with kernel versions ending with -<text-without-digit>
  ide: introduce HAVE_IDE
  kbuild: silence CHK/UPD messages according to $(quiet)
  scsi: fix makefile for aic7(3*x)
  kbuild/modpost: Use warn() for announcing section mismatches
  Add binoffset to gitignore
  kbuild/modpost: improve warnings if symbol is unknown

48 files changed:
Documentation/00-INDEX
Documentation/laptops/00-INDEX [new file with mode: 0644]
Documentation/laptops/acer-wmi.txt [new file with mode: 0644]
Documentation/laptops/sony-laptop.txt [moved from Documentation/sony-laptop.txt with 99% similarity]
Documentation/laptops/sonypi.txt [moved from Documentation/sonypi.txt with 100% similarity]
Documentation/laptops/thinkpad-acpi.txt [moved from Documentation/thinkpad-acpi.txt with 100% similarity]
MAINTAINERS
arch/x86/Kconfig
drivers/Kconfig
drivers/Makefile
drivers/acpi/Makefile
drivers/acpi/sbs.c
drivers/acpi/sbshc.c
drivers/block/ub.c
drivers/char/Kconfig
drivers/cpuidle/cpuidle.c
drivers/memstick/Kconfig [new file with mode: 0644]
drivers/memstick/Makefile [new file with mode: 0644]
drivers/memstick/core/Kconfig [new file with mode: 0644]
drivers/memstick/core/Makefile [new file with mode: 0644]
drivers/memstick/core/memstick.c [new file with mode: 0644]
drivers/memstick/core/mspro_block.c [new file with mode: 0644]
drivers/memstick/host/Kconfig [new file with mode: 0644]
drivers/memstick/host/Makefile [new file with mode: 0644]
drivers/memstick/host/tifm_ms.c [new file with mode: 0644]
drivers/misc/Kconfig
drivers/misc/acer-wmi.c
drivers/misc/tifm_7xx1.c
drivers/misc/tifm_core.c
drivers/mmc/host/Kconfig
drivers/mmc/host/at91_mci.c
drivers/mmc/host/ricoh_mmc.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mtd/nand/cs553x_nand.c
fs/hostfs/hostfs_kern.c
fs/ioctl.c
include/asm-blackfin/page.h
include/asm-h8300/page.h
include/asm-m68knommu/page.h
include/asm-v850/page.h
include/linux/memcontrol.h
include/linux/memstick.h [new file with mode: 0644]
include/linux/swapops.h
include/linux/thermal.h
include/linux/tifm.h
mm/memcontrol.c
mm/rmap.c

index 6e9c4050a41baedb072d0d0ba60d2391c7fd4e88..8d556707bb68e9ca8357143f0c318027b16319bc 100644 (file)
@@ -227,6 +227,8 @@ kref.txt
        - docs on adding reference counters (krefs) to kernel objects.
 laptop-mode.txt
        - how to conserve battery power using laptop-mode.
+laptops/
+       - directory with laptop related info and laptop driver documentation.
 ldm.txt
        - a brief description of LDM (Windows Dynamic Disks).
 leds-class.txt
@@ -351,10 +353,6 @@ sh/
        - directory with info on porting Linux to a new architecture.
 smart-config.txt
        - description of the Smart Config makefile feature.
-sony-laptop.txt
-       - Sony Notebook Control Driver (SNC) Readme.
-sonypi.txt
-       - info on Linux Sony Programmable I/O Device support.
 sound/
        - directory with info on sound card support.
 sparc/
@@ -385,8 +383,6 @@ sysrq.txt
        - info on the magic SysRq key.
 telephony/
        - directory with info on telephony (e.g. voice over IP) support.
-thinkpad-acpi.txt
-       - information on the (IBM and Lenovo) ThinkPad ACPI Extras driver.
 time_interpolators.txt
        - info on time interpolators.
 tipar.txt
diff --git a/Documentation/laptops/00-INDEX b/Documentation/laptops/00-INDEX
new file mode 100644 (file)
index 0000000..729c2c0
--- /dev/null
@@ -0,0 +1,10 @@
+00-INDEX
+       - This file
+acer-wmi.txt
+       - information on the Acer Laptop WMI Extras driver.
+sony-laptop.txt
+       - Sony Notebook Control Driver (SNC) Readme.
+sonypi.txt
+       - info on Linux Sony Programmable I/O Device support.
+thinkpad-acpi.txt
+       - information on the (IBM and Lenovo) ThinkPad ACPI Extras driver.
diff --git a/Documentation/laptops/acer-wmi.txt b/Documentation/laptops/acer-wmi.txt
new file mode 100644 (file)
index 0000000..b066963
--- /dev/null
@@ -0,0 +1,202 @@
+Acer Laptop WMI Extras Driver
+http://code.google.com/p/aceracpi
+Version 0.1
+9th February 2008
+
+Copyright 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
+
+acer-wmi is a driver to allow you to control various parts of your Acer laptop
+hardware under Linux which are exposed via ACPI-WMI.
+
+This driver completely replaces the old out-of-tree acer_acpi, which I am
+currently maintaining for bug fixes only on pre-2.6.25 kernels. All development
+work is now focused solely on acer-wmi.
+
+Disclaimer
+**********
+
+Acer and Wistron have provided nothing towards the development acer_acpi or
+acer-wmi. All information we have has been through the efforts of the developers
+and the users to discover as much as possible about the hardware.
+
+As such, I do warn that this could break your hardware - this is extremely
+unlikely of course, but please bear this in mind.
+
+Background
+**********
+
+acer-wmi is derived from acer_acpi, originally developed by Mark
+Smith in 2005, then taken over by Carlos Corbacho in 2007, in order to activate
+the wireless LAN card under a 64-bit version of Linux, as acerhk[1] (the
+previous solution to the problem) relied on making 32 bit BIOS calls which are
+not possible in kernel space from a 64 bit OS.
+
+[1] acerhk: http://www.cakey.de/acerhk/
+
+Supported Hardware
+******************
+
+Please see the website for the current list of known working hardare:
+
+http://code.google.com/p/aceracpi/wiki/SupportedHardware
+
+If your laptop is not listed, or listed as unknown, and works with acer-wmi,
+please contact me with a copy of the DSDT.
+
+If your Acer laptop doesn't work with acer-wmi, I would also like to see the
+DSDT.
+
+To send me the DSDT, as root/sudo:
+
+cat /sys/firmware/acpi/DSDT > dsdt
+
+And send me the resulting 'dsdt' file.
+
+Usage
+*****
+
+On Acer laptops, acer-wmi should already be autoloaded based on DMI matching.
+For non-Acer laptops, until WMI based autoloading support is added, you will
+need to manually load acer-wmi.
+
+acer-wmi creates /sys/devices/platform/acer-wmi, and fills it with various
+files whose usage is detailed below, which enables you to control some of the
+following (varies between models):
+
+* the wireless LAN card radio
+* inbuilt Bluetooth adapter
+* inbuilt 3G card
+* mail LED of your laptop
+* brightness of the LCD panel
+
+Wireless
+********
+
+With regards to wireless, all acer-wmi does is enable the radio on the card. It
+is not responsible for the wireless LED - once the radio is enabled, this is
+down to the wireless driver for your card. So the behaviour of the wireless LED,
+once you enable the radio, will depend on your hardware and driver combination.
+
+e.g. With the BCM4318 on the Acer Aspire 5020 series:
+
+ndiswrapper: Light blinks on when transmitting
+bcm43xx/b43: Solid light, blinks off when transmitting
+
+Wireless radio control is unconditionally enabled - all Acer laptops that support
+acer-wmi come with built-in wireless. However, should you feel so inclined to
+ever wish to remove the card, or swap it out at some point, please get in touch
+with me, as we may well be able to gain some data on wireless card detection.
+
+To read the status of the wireless radio (0=off, 1=on):
+cat /sys/devices/platform/acer-wmi/wireless
+
+To enable the wireless radio:
+echo 1 > /sys/devices/platform/acer-wmi/wireless
+
+To disable the wireless radio:
+echo 0 > /sys/devices/platform/acer-wmi/wireless
+
+To set the state of the wireless radio when loading acer-wmi, pass:
+wireless=X (where X is 0 or 1)
+
+Bluetooth
+*********
+
+For bluetooth, this is an internal USB dongle, so once enabled, you will get
+a USB device connection event, and a new USB device appears. When you disable
+bluetooth, you get the reverse - a USB device disconnect event, followed by the
+device disappearing again.
+
+Bluetooth is autodetected by acer-wmi, so if you do not have a bluetooth module
+installed in your laptop, this file won't exist (please be aware that it is
+quite common for Acer not to fit bluetooth to their laptops - so just because
+you have a bluetooth button on the laptop, doesn't mean that bluetooth is
+installed).
+
+For the adventurously minded - if you want to buy an internal bluetooth
+module off the internet that is compatible with your laptop and fit it, then
+it will work just fine with acer-wmi.
+
+To read the status of the bluetooth module (0=off, 1=on):
+cat /sys/devices/platform/acer-wmi/wireless
+
+To enable the bluetooth module:
+echo 1 > /sys/devices/platform/acer-wmi/bluetooth
+
+To disable the bluetooth module:
+echo 0 > /sys/devices/platform/acer-wmi/bluetooth
+
+To set the state of the bluetooth module when loading acer-wmi, pass:
+bluetooth=X (where X is 0 or 1)
+
+3G
+**
+
+3G is currently not autodetected, so the 'threeg' file is always created under
+sysfs. So far, no-one in possession of an Acer laptop with 3G built-in appears to
+have tried Linux, or reported back, so we don't have any information on this.
+
+If you have an Acer laptop that does have a 3G card in, please contact me so we
+can properly detect these, and find out a bit more about them.
+
+To read the status of the 3G card (0=off, 1=on):
+cat /sys/devices/platform/acer-wmi/threeg
+
+To enable the 3G card:
+echo 1 > /sys/devices/platform/acer-wmi/threeg
+
+To disable the 3G card:
+echo 0 > /sys/devices/platform/acer-wmi/threeg
+
+To set the state of the 3G card when loading acer-wmi, pass:
+threeg=X (where X is 0 or 1)
+
+Mail LED
+********
+
+This can be found in most older Acer laptops supported by acer-wmi, and many
+newer ones - it is built into the 'mail' button, and blinks when active.
+
+On newer (WMID) laptops though, we have no way of detecting the mail LED. If
+your laptop identifies itself in dmesg as a WMID model, then please try loading
+acer_acpi with:
+
+force_series=2490
+
+This will use a known alternative method of reading/ writing the mail LED. If
+it works, please report back to me with the DMI data from your laptop so this
+can be added to acer-wmi.
+
+The LED is exposed through the LED subsystem, and can be found in:
+
+/sys/devices/platform/acer-wmi/leds/acer-mail:green/
+
+The mail LED is autodetected, so if you don't have one, the LED device won't
+be registered.
+
+If you have a mail LED that is not green, please report this to me.
+
+Backlight
+*********
+
+The backlight brightness control is available on all acer-wmi supported
+hardware. The maximum brightness level is usually 15, but on some newer laptops
+it's 10 (this is again autodetected).
+
+The backlight is exposed through the backlight subsystem, and can be found in:
+
+/sys/devices/platform/acer-wmi/backlight/acer-wmi/
+
+Credits
+*******
+
+Olaf Tauber, who did the real hard work when he developed acerhk
+http://www.informatik.hu-berlin.de/~tauber/acerhk
+All the authors of laptop ACPI modules in the kernel, whose work
+was an inspiration in the early days of acer_acpi
+Mathieu Segaud, who solved the problem with having to modprobe the driver
+twice in acer_acpi 0.2.
+Jim Ramsay, who added support for the WMID interface
+Mark Smith, who started the original acer_acpi
+
+And the many people who have used both acer_acpi and acer-wmi.
similarity index 99%
rename from Documentation/sony-laptop.txt
rename to Documentation/laptops/sony-laptop.txt
index 7a5c1a81905c66940fe2c3a2d516b237f957b970..8b2bc1572d98eae5fe8e2c78dd0be77bee1d9326 100644 (file)
@@ -114,4 +114,3 @@ Bugs/Limitations:
   sonypi driver (through /dev/sonypi) does not try to use the
   sony-laptop driver. In the future, spicctrl could try sonypi first,
   and if it isn't present, try sony-laptop instead.
-
index 0d6f5119a6da08e49ab50e316a606c4acaf9a9a6..c40f0ae965523a04ef327be9c13c97923e67fb6c 100644 (file)
@@ -2682,7 +2682,6 @@ MOTOROLA IMX MMC/SD HOST CONTROLLER INTERFACE DRIVER
 P:     Pavel Pisa
 M:     ppisa@pikron.com
 L:     linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
-W:     http://mmc.drzeus.cx/wiki/Controllers/Freescale/SDHC
 S:     Maintained
 
 MOUSE AND MISC DEVICES [GENERAL]
@@ -3627,6 +3626,13 @@ L:       linux-acpi@vger.kernel.org
 W:     http://www.linux.it/~malattia/wiki/index.php/Sony_drivers
 S:     Maintained
 
+SONY MEMORYSTICK CARD SUPPORT
+P:     Alex Dubov
+M:     oakad@yahoo.com
+L:     linux-kernel@vger.kernel.org
+W:     http://tifmxx.berlios.de/
+S:     Maintained
+
 SOUND
 P:     Jaroslav Kysela
 M:     perex@perex.cz
@@ -3709,7 +3715,6 @@ SECURE DIGITAL HOST CONTROLLER INTERFACE DRIVER
 P:     Pierre Ossman
 M:     drzeus-sdhci@drzeus.cx
 L:     sdhci-devel@list.drzeus.cx
-W:     http://mmc.drzeus.cx/wiki/Linux/Drivers/sdhci
 S:     Maintained
 
 SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS
@@ -4279,7 +4284,6 @@ W83L51xD SD/MMC CARD INTERFACE DRIVER
 P:     Pierre Ossman
 M:     drzeus-wbsd@drzeus.cx
 L:     linux-kernel@vger.kernel.org
-W:     http://projects.drzeus.cx/wbsd
 S:     Maintained
 
 WATCHDOG DEVICE DRIVERS
index 3bd42dadbc4d212da284123dc9e641c4548f5072..aaed1a3b92d65bfab4804aec952256c9dc25ef72 100644 (file)
@@ -103,6 +103,9 @@ config ARCH_HAS_ILOG2_U32
 config ARCH_HAS_ILOG2_U64
        def_bool n
 
+config ARCH_HAS_CPU_IDLE_WAIT
+       def_bool y
+
 config GENERIC_CALIBRATE_DELAY
        def_bool y
 
index b86877bdc7ac50ede83ed0e311c52e692548ef3f..3a0e3549739f5980c6458d53c135f33878346ead 100644 (file)
@@ -80,6 +80,8 @@ source "drivers/usb/Kconfig"
 
 source "drivers/mmc/Kconfig"
 
+source "drivers/memstick/Kconfig"
+
 source "drivers/leds/Kconfig"
 
 source "drivers/infiniband/Kconfig"
index 30ba97ec5eb525eefd89807600490c7f0dac0854..e5e394a7e6c0873be132cb10086b7a89c93332d7 100644 (file)
@@ -78,6 +78,7 @@ obj-y                         += lguest/
 obj-$(CONFIG_CPU_FREQ)         += cpufreq/
 obj-$(CONFIG_CPU_IDLE)         += cpuidle/
 obj-$(CONFIG_MMC)              += mmc/
+obj-$(CONFIG_MEMSTICK)         += memstick/
 obj-$(CONFIG_NEW_LEDS)         += leds/
 obj-$(CONFIG_INFINIBAND)       += infiniband/
 obj-$(CONFIG_SGI_SN)           += sn/
index f29812a86533778a28b211b5c601bcf4863fadc9..40b0fcae4c78a71ac083119c96f6396e6e3bab07 100644 (file)
@@ -60,5 +60,5 @@ obj-$(CONFIG_ACPI_ASUS)               += asus_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)     += toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)      += acpi_memhotplug.o
 obj-$(CONFIG_ACPI_PROCFS_POWER)        += cm_sbs.o
-obj-$(CONFIG_ACPI_SBS)         += sbs.o
 obj-$(CONFIG_ACPI_SBS)         += sbshc.o
+obj-$(CONFIG_ACPI_SBS)         += sbs.o
index 1194105cc3cad977c5472a528ba8a261a870d2a8..585ae3c9c8ea98651e33f46de53876fac237f48d 100644 (file)
@@ -827,7 +827,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
 #endif
        printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
               ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
-              battery->name, sbs->battery->present ? "present" : "absent");
+              battery->name, battery->present ? "present" : "absent");
        return result;
 }
 
index ae9a90438e2f32f497e207889bf4677c431c3383..a2cf3008ce6c03eb40ad564d3063700896a68d03 100644 (file)
@@ -117,6 +117,11 @@ static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol,
        int ret = -EFAULT, i;
        u8 temp, sz = 0;
 
+       if (!hc) {
+               printk(KERN_ERR PREFIX "host controller is not configured\n");
+               return ret;
+       }
+
        mutex_lock(&hc->lock);
        if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
                goto end;
@@ -292,6 +297,7 @@ static int acpi_smbus_hc_remove(struct acpi_device *device, int type)
        hc = acpi_driver_data(device);
        acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
        kfree(hc);
+       acpi_driver_data(device) = NULL;
        return 0;
 }
 
index a70c1c29a7aa2c28e99c9fa352fa6f42b2048d17..c452e2d355eeb303bce96fbcddec96002f0449e2 100644 (file)
@@ -657,7 +657,6 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
        if ((cmd = ub_get_cmd(lun)) == NULL)
                return -1;
        memset(cmd, 0, sizeof(struct ub_scsi_cmd));
-       sg_init_table(cmd->sgv, UB_MAX_REQ_SG);
 
        blkdev_dequeue_request(rq);
 
@@ -668,6 +667,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
        /*
         * get scatterlist from block layer
         */
+       sg_init_table(&urq->sgv[0], UB_MAX_REQ_SG);
        n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]);
        if (n_elem < 0) {
                /* Impossible, because blk_rq_map_sg should not hit ENOMEM. */
index f01ac9a07bf5533a7b8917775aff88f0eb5ff901..47c6be84fc842c7855883d4fb93d2257a5637fe7 100644 (file)
@@ -875,7 +875,7 @@ config SONYPI
          Device which can be found in many (all ?) Sony Vaio laptops.
 
          If you have one of those laptops, read
-         <file:Documentation/sonypi.txt>, and say Y or M here.
+         <file:Documentation/laptops/sonypi.txt>, and say Y or M here.
 
          To compile this driver as a module, choose M here: the
          module will be called sonypi.
index 2c4b2d47973e195cbb0422a9b539400a2ad5ef5e..60f71e6345e32ac11747d1dbd608349650172189 100644 (file)
@@ -27,6 +27,17 @@ static void (*pm_idle_old)(void);
 
 static int enabled_devices;
 
+#if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT)
+static void cpuidle_kick_cpus(void)
+{
+       cpu_idle_wait();
+}
+#elif defined(CONFIG_SMP)
+# error "Arch needs cpu_idle_wait() equivalent here"
+#else /* !CONFIG_ARCH_HAS_CPU_IDLE_WAIT && !CONFIG_SMP */
+static void cpuidle_kick_cpus(void) {}
+#endif
+
 /**
  * cpuidle_idle_call - the main idle loop
  *
@@ -83,7 +94,7 @@ void cpuidle_uninstall_idle_handler(void)
 {
        if (enabled_devices && (pm_idle != pm_idle_old)) {
                pm_idle = pm_idle_old;
-               cpu_idle_wait();
+               cpuidle_kick_cpus();
        }
 }
 
diff --git a/drivers/memstick/Kconfig b/drivers/memstick/Kconfig
new file mode 100644 (file)
index 0000000..1093fdb
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# MemoryStick subsystem configuration
+#
+
+menuconfig MEMSTICK
+       tristate "Sony MemoryStick card support (EXPERIMENTAL)"
+       help
+         Sony MemoryStick is a proprietary storage/extension card protocol.
+
+         If you want MemoryStick support, you should say Y here and also
+         to the specific driver for your MMC interface.
+
+if MEMSTICK
+
+config MEMSTICK_DEBUG
+       bool "MemoryStick debugging"
+       help
+         This is an option for use by developers; most people should
+         say N here.  This enables MemoryStick core and driver debugging.
+
+
+source "drivers/memstick/core/Kconfig"
+
+source "drivers/memstick/host/Kconfig"
+
+endif # MEMSTICK
diff --git a/drivers/memstick/Makefile b/drivers/memstick/Makefile
new file mode 100644 (file)
index 0000000..dc160fb
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile for the kernel MemoryStick device drivers.
+#
+
+ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
+       EXTRA_CFLAGS            += -DDEBUG
+endif
+
+obj-$(CONFIG_MEMSTICK)         += core/
+obj-$(CONFIG_MEMSTICK)         += host/
+
diff --git a/drivers/memstick/core/Kconfig b/drivers/memstick/core/Kconfig
new file mode 100644 (file)
index 0000000..95f1814
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# MemoryStick core configuration
+#
+
+comment "MemoryStick drivers"
+
+config MEMSTICK_UNSAFE_RESUME
+        bool "Allow unsafe resume (DANGEROUS)"
+        help
+          If you say Y here, the MemoryStick layer will assume that all
+          cards stayed in their respective slots during the suspend. The
+          normal behaviour is to remove them at suspend and
+          redetecting them at resume. Breaking this assumption will
+          in most cases result in data corruption.
+
+          This option is usually just for embedded systems which use
+          a MemoryStick card for rootfs. Most people should say N here.
+
+config MSPRO_BLOCK
+       tristate "MemoryStick Pro block device driver"
+       depends on BLOCK
+       help
+         Say Y here to enable the MemoryStick Pro block device driver
+         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.
diff --git a/drivers/memstick/core/Makefile b/drivers/memstick/core/Makefile
new file mode 100644 (file)
index 0000000..8b2b529
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile for the kernel MemoryStick core.
+#
+
+ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
+       EXTRA_CFLAGS            += -DDEBUG
+endif
+
+obj-$(CONFIG_MEMSTICK)         += memstick.o
+
+obj-$(CONFIG_MSPRO_BLOCK)      += mspro_block.o
diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c
new file mode 100644 (file)
index 0000000..bba467f
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ *  Sony MemoryStick support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.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/memstick.h>
+#include <linux/idr.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+
+#define DRIVER_NAME "memstick"
+#define DRIVER_VERSION "0.2"
+
+static unsigned int cmd_retries = 3;
+module_param(cmd_retries, uint, 0644);
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(memstick_host_idr);
+static DEFINE_SPINLOCK(memstick_host_lock);
+
+static int memstick_dev_match(struct memstick_dev *card,
+                             struct memstick_device_id *id)
+{
+       if (id->match_flags & MEMSTICK_MATCH_ALL) {
+               if ((id->type == card->id.type)
+                   && (id->category == card->id.category)
+                   && (id->class == card->id.class))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static int memstick_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                dev);
+       struct memstick_driver *ms_drv = container_of(drv,
+                                                     struct memstick_driver,
+                                                     driver);
+       struct memstick_device_id *ids = ms_drv->id_table;
+
+       if (ids) {
+               while (ids->match_flags) {
+                       if (memstick_dev_match(card, ids))
+                               return 1;
+                       ++ids;
+               }
+       }
+       return 0;
+}
+
+static int memstick_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+
+       if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "MEMSTICK_CATEGORY=%02X", card->id.category))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "MEMSTICK_CLASS=%02X", card->id.class))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int memstick_device_probe(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+       int rc = -ENODEV;
+
+       if (dev->driver && drv->probe) {
+               rc = drv->probe(card);
+               if (!rc)
+                       get_device(dev);
+       }
+       return rc;
+}
+
+static int memstick_device_remove(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+
+       if (dev->driver && drv->remove) {
+               drv->remove(card);
+               card->dev.driver = NULL;
+       }
+
+       put_device(dev);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int memstick_device_suspend(struct device *dev, pm_message_t state)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+
+       if (dev->driver && drv->suspend)
+               return drv->suspend(card, state);
+       return 0;
+}
+
+static int memstick_device_resume(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+
+       if (dev->driver && drv->resume)
+               return drv->resume(card);
+       return 0;
+}
+
+#else
+
+#define memstick_device_suspend NULL
+#define memstick_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+#define MEMSTICK_ATTR(name, format)                                           \
+static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
+                           char *buf)                                        \
+{                                                                             \
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,    \
+                                                dev);                        \
+       return sprintf(buf, format, card->id.name);                           \
+}
+
+MEMSTICK_ATTR(type, "%02X");
+MEMSTICK_ATTR(category, "%02X");
+MEMSTICK_ATTR(class, "%02X");
+
+#define MEMSTICK_ATTR_RO(name) __ATTR(name, S_IRUGO, name##_show, NULL)
+
+static struct device_attribute memstick_dev_attrs[] = {
+       MEMSTICK_ATTR_RO(type),
+       MEMSTICK_ATTR_RO(category),
+       MEMSTICK_ATTR_RO(class),
+       __ATTR_NULL
+};
+
+static struct bus_type memstick_bus_type = {
+       .name           = "memstick",
+       .dev_attrs      = memstick_dev_attrs,
+       .match          = memstick_bus_match,
+       .uevent         = memstick_uevent,
+       .probe          = memstick_device_probe,
+       .remove         = memstick_device_remove,
+       .suspend        = memstick_device_suspend,
+       .resume         = memstick_device_resume
+};
+
+static void memstick_free(struct class_device *cdev)
+{
+       struct memstick_host *host = container_of(cdev, struct memstick_host,
+                                                 cdev);
+       kfree(host);
+}
+
+static struct class memstick_host_class = {
+       .name       = "memstick_host",
+       .release    = memstick_free
+};
+
+static void memstick_free_card(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                dev);
+       kfree(card);
+}
+
+static int memstick_dummy_check(struct memstick_dev *card)
+{
+       return 0;
+}
+
+/**
+ * memstick_detect_change - schedule media detection on memstick host
+ * @host - host to use
+ */
+void memstick_detect_change(struct memstick_host *host)
+{
+       queue_work(workqueue, &host->media_checker);
+}
+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)
+{
+       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, mrq);
+
+       if (!rc)
+               host->retries = cmd_retries;
+       else
+               *mrq = NULL;
+
+       return rc;
+}
+EXPORT_SYMBOL(memstick_next_req);
+
+/**
+ * memstick_new_req - notify the host that some requests are pending
+ * @host - host to use
+ */
+void memstick_new_req(struct memstick_host *host)
+{
+       host->retries = cmd_retries;
+       host->request(host);
+}
+EXPORT_SYMBOL(memstick_new_req);
+
+/**
+ * memstick_init_req_sg - set request fields needed for bulk data transfer
+ * @mrq - request to use
+ * @tpc - memstick Transport Protocol Command
+ * @sg - TPC argument
+ */
+void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
+                         struct scatterlist *sg)
+{
+       mrq->tpc = tpc;
+       if (tpc & 8)
+               mrq->data_dir = WRITE;
+       else
+               mrq->data_dir = READ;
+
+       mrq->sg = *sg;
+       mrq->io_type = MEMSTICK_IO_SG;
+
+       if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
+               mrq->need_card_int = 1;
+       else
+               mrq->need_card_int = 0;
+
+       mrq->get_int_reg = 0;
+}
+EXPORT_SYMBOL(memstick_init_req_sg);
+
+/**
+ * memstick_init_req - set request fields needed for short data transfer
+ * @mrq - request to use
+ * @tpc - memstick Transport Protocol Command
+ * @buf - TPC argument buffer
+ * @length - TPC argument size
+ *
+ * The intended use of this function (transfer of data items several bytes
+ * 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 *buf, size_t length)
+{
+       mrq->tpc = tpc;
+       if (tpc & 8)
+               mrq->data_dir = WRITE;
+       else
+               mrq->data_dir = READ;
+
+       mrq->data_len = length > sizeof(mrq->data) ? sizeof(mrq->data) : length;
+       if (mrq->data_dir == WRITE)
+               memcpy(mrq->data, buf, mrq->data_len);
+
+       mrq->io_type = MEMSTICK_IO_VAL;
+
+       if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
+               mrq->need_card_int = 1;
+       else
+               mrq->need_card_int = 0;
+
+       mrq->get_int_reg = 0;
+}
+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;
+               }
+               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;
+       }
+}
+
+/**
+ * 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);
+
+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;
+               snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
+                        "%s", host->cdev.class_id);
+               card->dev.parent = host->cdev.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;
+}
+
+static void memstick_power_on(struct memstick_host *host)
+{
+       host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
+       host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+       msleep(1);
+}
+
+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->cdev.dev, "memstick_check started\n");
+       mutex_lock(&host->lock);
+       if (!host->card)
+               memstick_power_on(host);
+
+       card = memstick_alloc_card(host);
+
+       if (!card) {
+               if (host->card) {
+                       device_unregister(&host->card->dev);
+                       host->card = NULL;
+               }
+       } else {
+               dev_dbg(host->cdev.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;
+                       }
+               }
+
+               if (!host->card) {
+                       host->card = card;
+                       if (device_register(&card->dev)) {
+                               kfree(host->card);
+                               host->card = NULL;
+                       }
+               } else
+                       kfree(card);
+       }
+
+       if (!host->card)
+               host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+
+       mutex_unlock(&host->lock);
+       dev_dbg(host->cdev.dev, "memstick_check finished\n");
+}
+
+/**
+ * memstick_alloc_host - allocate a memstick_host structure
+ * @extra: size of the user private data to allocate
+ * @dev: parent device of the host
+ */
+struct memstick_host *memstick_alloc_host(unsigned int extra,
+                                         struct device *dev)
+{
+       struct memstick_host *host;
+
+       host = kzalloc(sizeof(struct memstick_host) + extra, GFP_KERNEL);
+       if (host) {
+               mutex_init(&host->lock);
+               INIT_WORK(&host->media_checker, memstick_check);
+               host->cdev.class = &memstick_host_class;
+               host->cdev.dev = dev;
+               class_device_initialize(&host->cdev);
+       }
+       return host;
+}
+EXPORT_SYMBOL(memstick_alloc_host);
+
+/**
+ * memstick_add_host - start request processing on memstick host
+ * @host - host to use
+ */
+int memstick_add_host(struct memstick_host *host)
+{
+       int rc;
+
+       if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL))
+               return -ENOMEM;
+
+       spin_lock(&memstick_host_lock);
+       rc = idr_get_new(&memstick_host_idr, host, &host->id);
+       spin_unlock(&memstick_host_lock);
+       if (rc)
+               return rc;
+
+       snprintf(host->cdev.class_id, BUS_ID_SIZE,
+                "memstick%u", host->id);
+
+       rc = class_device_add(&host->cdev);
+       if (rc) {
+               spin_lock(&memstick_host_lock);
+               idr_remove(&memstick_host_idr, host->id);
+               spin_unlock(&memstick_host_lock);
+               return rc;
+       }
+
+       host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+       memstick_detect_change(host);
+       return 0;
+}
+EXPORT_SYMBOL(memstick_add_host);
+
+/**
+ * memstick_remove_host - stop request processing on memstick host
+ * @host - host to use
+ */
+void memstick_remove_host(struct memstick_host *host)
+{
+       flush_workqueue(workqueue);
+       mutex_lock(&host->lock);
+       if (host->card)
+               device_unregister(&host->card->dev);
+       host->card = NULL;
+       host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+       mutex_unlock(&host->lock);
+
+       spin_lock(&memstick_host_lock);
+       idr_remove(&memstick_host_idr, host->id);
+       spin_unlock(&memstick_host_lock);
+       class_device_del(&host->cdev);
+}
+EXPORT_SYMBOL(memstick_remove_host);
+
+/**
+ * memstick_free_host - free memstick host
+ * @host - host to use
+ */
+void memstick_free_host(struct memstick_host *host)
+{
+       mutex_destroy(&host->lock);
+       class_device_put(&host->cdev);
+}
+EXPORT_SYMBOL(memstick_free_host);
+
+int memstick_register_driver(struct memstick_driver *drv)
+{
+       drv->driver.bus = &memstick_bus_type;
+
+       return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(memstick_register_driver);
+
+void memstick_unregister_driver(struct memstick_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(memstick_unregister_driver);
+
+
+static int __init memstick_init(void)
+{
+       int rc;
+
+       workqueue = create_freezeable_workqueue("kmemstick");
+       if (!workqueue)
+               return -ENOMEM;
+
+       rc = bus_register(&memstick_bus_type);
+       if (!rc)
+               rc = class_register(&memstick_host_class);
+
+       if (!rc)
+               return 0;
+
+       bus_unregister(&memstick_bus_type);
+       destroy_workqueue(workqueue);
+
+       return rc;
+}
+
+static void __exit memstick_exit(void)
+{
+       class_unregister(&memstick_host_class);
+       bus_unregister(&memstick_bus_type);
+       destroy_workqueue(workqueue);
+       idr_destroy(&memstick_host_idr);
+}
+
+module_init(memstick_init);
+module_exit(memstick_exit);
+
+MODULE_AUTHOR("Alex Dubov");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Sony MemoryStick core driver");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
new file mode 100644 (file)
index 0000000..423ad8c
--- /dev/null
@@ -0,0 +1,1351 @@
+/*
+ *  Sony MemoryStick Pro storage support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.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/idr.h>
+#include <linux/hdreg.h>
+#include <linux/kthread.h>
+#include <linux/memstick.h>
+
+#define DRIVER_NAME "mspro_block"
+#define DRIVER_VERSION "0.2"
+
+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
+
+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 {
+       unsigned int  address;
+       unsigned int  size;
+       unsigned char id;
+       unsigned char reserved[3];
+} __attribute__((packed));
+
+struct mspro_attribute {
+       unsigned short          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;
+       unsigned short block_size;
+       unsigned short block_count;
+       unsigned short user_block_count;
+       unsigned short page_size;
+       unsigned char  reserved1[2];
+       unsigned char  assembly_date[8];
+       unsigned int   serial_number;
+       unsigned char  assembly_maker_code;
+       unsigned char  assembly_model_code[3];
+       unsigned short memory_maker_code;
+       unsigned short memory_model_code;
+       unsigned char  reserved2[4];
+       unsigned char  vcc;
+       unsigned char  vpp;
+       unsigned short controller_number;
+       unsigned short controller_function;
+       unsigned short start_sector;
+       unsigned short unit_size;
+       unsigned char  ms_sub_class;
+       unsigned char  reserved3[4];
+       unsigned char  interface_type;
+       unsigned short 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_devinfo {
+       unsigned short cylinders;
+       unsigned short heads;
+       unsigned short bytes_per_track;
+       unsigned short bytes_per_sector;
+       unsigned short sectors_per_track;
+       unsigned char  reserved[6];
+} __attribute__((packed));
+
+struct mspro_block_data {
+       struct memstick_dev   *card;
+       unsigned int          usage_count;
+       struct gendisk        *disk;
+       struct request_queue  *queue;
+       spinlock_t            q_lock;
+       wait_queue_head_t     q_wait;
+       struct task_struct    *q_thread;
+
+       unsigned short        page_size;
+       unsigned short        cylinders;
+       unsigned short        heads;
+       unsigned short        sectors_per_track;
+
+       unsigned char         system;
+       unsigned char         read_only:1,
+                             active:1,
+                             has_request:1,
+                             data_dir: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 short        current_page;
+};
+
+static DEFINE_IDR(mspro_block_disk_idr);
+static DEFINE_MUTEX(mspro_block_disk_lock);
+
+/*** Block device ***/
+
+static int mspro_block_bd_open(struct inode *inode, struct file *filp)
+{
+       struct gendisk *disk = inode->i_bdev->bd_disk;
+       struct mspro_block_data *msb = disk->private_data;
+       int rc = -ENXIO;
+
+       mutex_lock(&mspro_block_disk_lock);
+
+       if (msb && msb->card) {
+               msb->usage_count++;
+               if ((filp->f_mode & FMODE_WRITE) && msb->read_only)
+                       rc = -EROFS;
+               else
+                       rc = 0;
+       }
+
+       mutex_unlock(&mspro_block_disk_lock);
+
+       return rc;
+}
+
+
+static int mspro_block_disk_release(struct gendisk *disk)
+{
+       struct mspro_block_data *msb = disk->private_data;
+       int disk_id = disk->first_minor >> MEMSTICK_PART_SHIFT;
+
+       mutex_lock(&mspro_block_disk_lock);
+
+       if (msb->usage_count) {
+               msb->usage_count--;
+               if (!msb->usage_count) {
+                       kfree(msb);
+                       disk->private_data = NULL;
+                       idr_remove(&mspro_block_disk_idr, disk_id);
+                       put_disk(disk);
+               }
+       }
+
+       mutex_unlock(&mspro_block_disk_lock);
+
+       return 0;
+}
+
+static int mspro_block_bd_release(struct inode *inode, struct file *filp)
+{
+       struct gendisk *disk = inode->i_bdev->bd_disk;
+       return mspro_block_disk_release(disk);
+}
+
+static int mspro_block_bd_getgeo(struct block_device *bdev,
+                                struct hd_geometry *geo)
+{
+       struct mspro_block_data *msb = bdev->bd_disk->private_data;
+
+       geo->heads = msb->heads;
+       geo->sectors = msb->sectors_per_track;
+       geo->cylinders = msb->cylinders;
+
+       return 0;
+}
+
+static struct block_device_operations ms_block_bdops = {
+       .open    = mspro_block_bd_open,
+       .release = mspro_block_bd_release,
+       .getgeo  = mspro_block_bd_getgeo,
+       .owner   = THIS_MODULE
+};
+
+/*** Information ***/
+
+static struct mspro_sys_attr *mspro_from_sysfs_attr(struct attribute *attr)
+{
+       struct device_attribute *dev_attr
+               = container_of(attr, struct device_attribute, attr);
+       return container_of(dev_attr, struct mspro_sys_attr, dev_attr);
+}
+
+static const char *mspro_block_attr_name(unsigned char tag)
+{
+       switch (tag) {
+       case MSPRO_BLOCK_ID_SYSINFO:
+               return "attr_sysinfo";
+       case MSPRO_BLOCK_ID_MODELNAME:
+               return "attr_modelname";
+       case MSPRO_BLOCK_ID_MBR:
+               return "attr_mbr";
+       case MSPRO_BLOCK_ID_PBR16:
+               return "attr_pbr16";
+       case MSPRO_BLOCK_ID_PBR32:
+               return "attr_pbr32";
+       case MSPRO_BLOCK_ID_SPECFILEVALUES1:
+               return "attr_specfilevalues1";
+       case MSPRO_BLOCK_ID_SPECFILEVALUES2:
+               return "attr_specfilevalues2";
+       case MSPRO_BLOCK_ID_DEVINFO:
+               return "attr_devinfo";
+       default:
+               return NULL;
+       };
+}
+
+typedef ssize_t (*sysfs_show_t)(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buffer);
+
+static ssize_t mspro_block_attr_show_default(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buffer)
+{
+       struct mspro_sys_attr *s_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+
+       ssize_t cnt, rc = 0;
+
+       for (cnt = 0; cnt < s_attr->size; cnt++) {
+               if (cnt && !(cnt % 16)) {
+                       if (PAGE_SIZE - rc)
+                               buffer[rc++] = '\n';
+               }
+
+               rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "%02x ",
+                               ((unsigned char *)s_attr->data)[cnt]);
+       }
+       return rc;
+}
+
+static ssize_t mspro_block_attr_show_sysinfo(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buffer)
+{
+       struct mspro_sys_attr *x_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+       struct mspro_sys_info *x_sys = x_attr->data;
+       ssize_t rc = 0;
+
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "class: %x\n",
+                       x_sys->class);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block size: %x\n",
+                       be16_to_cpu(x_sys->block_size));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block count: %x\n",
+                       be16_to_cpu(x_sys->block_count));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "user block count: %x\n",
+                       be16_to_cpu(x_sys->user_block_count));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "page size: %x\n",
+                       be16_to_cpu(x_sys->page_size));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly date: "
+                       "%d %04u-%02u-%02u %02u:%02u:%02u\n",
+                       x_sys->assembly_date[0],
+                       be16_to_cpu(*(unsigned short *)
+                                   &x_sys->assembly_date[1]),
+                       x_sys->assembly_date[3], x_sys->assembly_date[4],
+                       x_sys->assembly_date[5], x_sys->assembly_date[6],
+                       x_sys->assembly_date[7]);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "serial number: %x\n",
+                       be32_to_cpu(x_sys->serial_number));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc,
+                       "assembly maker code: %x\n",
+                       x_sys->assembly_maker_code);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly model code: "
+                       "%02x%02x%02x\n", x_sys->assembly_model_code[0],
+                       x_sys->assembly_model_code[1],
+                       x_sys->assembly_model_code[2]);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory maker code: %x\n",
+                       be16_to_cpu(x_sys->memory_maker_code));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory model code: %x\n",
+                       be16_to_cpu(x_sys->memory_model_code));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vcc: %x\n",
+                       x_sys->vcc);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vpp: %x\n",
+                       x_sys->vpp);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller number: %x\n",
+                       be16_to_cpu(x_sys->controller_number));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc,
+                       "controller function: %x\n",
+                       be16_to_cpu(x_sys->controller_function));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n",
+                       be16_to_cpu(x_sys->start_sector));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "unit size: %x\n",
+                       be16_to_cpu(x_sys->unit_size));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sub class: %x\n",
+                       x_sys->ms_sub_class);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "interface type: %x\n",
+                       x_sys->interface_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller code: %x\n",
+                       be16_to_cpu(x_sys->controller_code));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "format type: %x\n",
+                       x_sys->format_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "device type: %x\n",
+                       x_sys->device_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "mspro id: %s\n",
+                       x_sys->mspro_id);
+       return rc;
+}
+
+static ssize_t mspro_block_attr_show_modelname(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buffer)
+{
+       struct mspro_sys_attr *s_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+
+       return scnprintf(buffer, PAGE_SIZE, "%s", (char *)s_attr->data);
+}
+
+static ssize_t mspro_block_attr_show_mbr(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buffer)
+{
+       struct mspro_sys_attr *x_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+       struct mspro_mbr *x_mbr = x_attr->data;
+       ssize_t rc = 0;
+
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "boot partition: %x\n",
+                       x_mbr->boot_partition);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start head: %x\n",
+                       x_mbr->start_head);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n",
+                       x_mbr->start_sector);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start cylinder: %x\n",
+                       x_mbr->start_cylinder);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "partition type: %x\n",
+                       x_mbr->partition_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end head: %x\n",
+                       x_mbr->end_head);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end sector: %x\n",
+                       x_mbr->end_sector);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end cylinder: %x\n",
+                       x_mbr->end_cylinder);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sectors: %x\n",
+                       x_mbr->start_sectors);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc,
+                       "sectors per partition: %x\n",
+                       x_mbr->sectors_per_partition);
+       return rc;
+}
+
+static ssize_t mspro_block_attr_show_devinfo(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buffer)
+{
+       struct mspro_sys_attr *x_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+       struct mspro_devinfo *x_devinfo = x_attr->data;
+       ssize_t rc = 0;
+
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "cylinders: %x\n",
+                       be16_to_cpu(x_devinfo->cylinders));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "heads: %x\n",
+                       be16_to_cpu(x_devinfo->heads));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per track: %x\n",
+                       be16_to_cpu(x_devinfo->bytes_per_track));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per sector: %x\n",
+                       be16_to_cpu(x_devinfo->bytes_per_sector));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sectors per track: %x\n",
+                       be16_to_cpu(x_devinfo->sectors_per_track));
+       return rc;
+}
+
+static sysfs_show_t mspro_block_attr_show(unsigned char tag)
+{
+       switch (tag) {
+       case MSPRO_BLOCK_ID_SYSINFO:
+               return mspro_block_attr_show_sysinfo;
+       case MSPRO_BLOCK_ID_MODELNAME:
+               return mspro_block_attr_show_modelname;
+       case MSPRO_BLOCK_ID_MBR:
+               return mspro_block_attr_show_mbr;
+       case MSPRO_BLOCK_ID_DEVINFO:
+               return mspro_block_attr_show_devinfo;
+       default:
+               return mspro_block_attr_show_default;
+       }
+}
+
+/*** Protocol handlers ***/
+
+/*
+ * 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_mspro_block_req_init(struct memstick_dev *card,
+                                 struct memstick_request **mrq)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+       *mrq = &card->current_mrq;
+       card->next_request = msb->mrq_handler;
+       return 0;
+}
+
+static int h_mspro_block_default(struct memstick_dev *card,
+                                struct memstick_request **mrq)
+{
+       complete(&card->mrq_complete);
+       if (!(*mrq)->error)
+               return -EAGAIN;
+       else
+               return (*mrq)->error;
+}
+
+static int h_mspro_block_get_ro(struct memstick_dev *card,
+                               struct memstick_request **mrq)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+       if ((*mrq)->error) {
+               complete(&card->mrq_complete);
+               return (*mrq)->error;
+       }
+
+       if ((*mrq)->data[offsetof(struct ms_status_register, status0)]
+           & MEMSTICK_STATUS0_WP)
+               msb->read_only = 1;
+       else
+               msb->read_only = 0;
+
+       complete(&card->mrq_complete);
+       return -EAGAIN;
+}
+
+static int h_mspro_block_wait_for_ced(struct memstick_dev *card,
+                                     struct memstick_request **mrq)
+{
+       if ((*mrq)->error) {
+               complete(&card->mrq_complete);
+               return (*mrq)->error;
+       }
+
+       dev_dbg(&card->dev, "wait for ced: value %x\n", (*mrq)->data[0]);
+
+       if ((*mrq)->data[0] & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) {
+               card->current_mrq.error = -EFAULT;
+               complete(&card->mrq_complete);
+               return card->current_mrq.error;
+       }
+
+       if (!((*mrq)->data[0] & MEMSTICK_INT_CED))
+               return 0;
+       else {
+               card->current_mrq.error = 0;
+               complete(&card->mrq_complete);
+               return -EAGAIN;
+       }
+}
+
+static int h_mspro_block_transfer_data(struct memstick_dev *card,
+                                      struct memstick_request **mrq)
+{
+       struct memstick_host *host = card->host;
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       unsigned char t_val = 0;
+       struct scatterlist t_sg = { 0 };
+       size_t t_offset;
+
+       if ((*mrq)->error) {
+               complete(&card->mrq_complete);
+               return (*mrq)->error;
+       }
+
+       switch ((*mrq)->tpc) {
+       case MS_TPC_WRITE_REG:
+               memstick_init_req(*mrq, MS_TPC_SET_CMD, &msb->transfer_cmd, 1);
+               (*mrq)->get_int_reg = 1;
+               return 0;
+       case MS_TPC_SET_CMD:
+               t_val = (*mrq)->int_reg;
+               memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
+               if (host->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 (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) {
+                                       complete(&card->mrq_complete);
+                                       return -EAGAIN;
+                               } else {
+                                       card->next_request
+                                               = h_mspro_block_wait_for_ced;
+                                       memstick_init_req(*mrq, MS_TPC_GET_INT,
+                                                         NULL, 1);
+                                       return 0;
+                               }
+                       }
+               }
+
+               if (!(t_val & MEMSTICK_INT_BREQ)) {
+                       memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
+                       return 0;
+               }
+
+               t_offset = msb->req_sg[msb->current_seg].offset;
+               t_offset += msb->current_page * msb->page_size;
+
+               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));
+
+               memstick_init_req_sg(*mrq, msb->data_dir == READ
+                                          ? MS_TPC_READ_LONG_DATA
+                                          : MS_TPC_WRITE_LONG_DATA,
+                                    &t_sg);
+               (*mrq)->get_int_reg = 1;
+               return 0;
+       case MS_TPC_READ_LONG_DATA:
+       case MS_TPC_WRITE_LONG_DATA:
+               msb->current_page++;
+               if (host->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;
+               }
+
+       default:
+               BUG();
+       }
+}
+
+/*** Data transfer ***/
+
+static void mspro_block_process_request(struct memstick_dev *card,
+                                       struct request *req)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct mspro_param_register param;
+       int rc, chunk, cnt;
+       unsigned short page_count;
+       sector_t t_sec;
+       unsigned long flags;
+
+       do {
+               page_count = 0;
+               msb->current_seg = 0;
+               msb->seg_count = blk_rq_map_sg(req->q, req, msb->req_sg);
+
+               if (msb->seg_count) {
+                       msb->current_page = 0;
+                       for (rc = 0; rc < msb->seg_count; rc++)
+                               page_count += msb->req_sg[rc].length
+                                             / msb->page_size;
+
+                       t_sec = req->sector;
+                       sector_div(t_sec, msb->page_size >> 9);
+                       param.system = msb->system;
+                       param.data_count = cpu_to_be16(page_count);
+                       param.data_address = cpu_to_be32((uint32_t)t_sec);
+                       param.cmd_param = 0;
+
+                       msb->data_dir = rq_data_dir(req);
+                       msb->transfer_cmd = msb->data_dir == 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),
+                               page_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,
+                                         &param, sizeof(param));
+                       memstick_new_req(card->host);
+                       wait_for_completion(&card->mrq_complete);
+                       rc = card->current_mrq.error;
+
+                       if (rc || (card->current_mrq.tpc == MSPRO_CMD_STOP)) {
+                               for (cnt = 0; cnt < msb->current_seg; cnt++)
+                                       page_count += msb->req_sg[cnt].length
+                                                     / msb->page_size;
+
+                               if (msb->current_page)
+                                       page_count += msb->current_page - 1;
+
+                               if (page_count && (msb->data_dir == READ))
+                                       rc = msb->page_size * page_count;
+                               else
+                                       rc = -EIO;
+                       } else
+                               rc = msb->page_size * page_count;
+               } else
+                       rc = -EFAULT;
+
+               spin_lock_irqsave(&msb->q_lock, flags);
+               if (rc >= 0)
+                       chunk = __blk_end_request(req, 0, rc);
+               else
+                       chunk = __blk_end_request(req, rc, 0);
+
+               dev_dbg(&card->dev, "end chunk %d, %d\n", rc, chunk);
+               spin_unlock_irqrestore(&msb->q_lock, flags);
+       } while (chunk);
+}
+
+static int mspro_block_has_request(struct mspro_block_data *msb)
+{
+       int rc = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&msb->q_lock, flags);
+       if (kthread_should_stop() || msb->has_request)
+               rc = 1;
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+       return rc;
+}
+
+static int mspro_block_queue_thread(void *data)
+{
+       struct memstick_dev *card = data;
+       struct memstick_host *host = card->host;
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct request *req;
+       unsigned long flags;
+
+       while (1) {
+               wait_event(msb->q_wait, mspro_block_has_request(msb));
+               dev_dbg(&card->dev, "thread iter\n");
+
+               spin_lock_irqsave(&msb->q_lock, flags);
+               req = elv_next_request(msb->queue);
+               dev_dbg(&card->dev, "next req %p\n", req);
+               if (!req) {
+                       msb->has_request = 0;
+                       if (kthread_should_stop()) {
+                               spin_unlock_irqrestore(&msb->q_lock, flags);
+                               break;
+                       }
+               } else
+                       msb->has_request = 1;
+               spin_unlock_irqrestore(&msb->q_lock, flags);
+
+               if (req) {
+                       mutex_lock(&host->lock);
+                       mspro_block_process_request(card, req);
+                       mutex_unlock(&host->lock);
+               }
+       }
+       dev_dbg(&card->dev, "thread finished\n");
+       return 0;
+}
+
+static void mspro_block_request(struct request_queue *q)
+{
+       struct memstick_dev *card = q->queuedata;
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct request *req = NULL;
+
+       if (msb->q_thread) {
+               msb->has_request = 1;
+               wake_up_all(&msb->q_wait);
+       } else {
+               while ((req = elv_next_request(q)) != NULL)
+                       end_queued_request(req, -ENODEV);
+       }
+}
+
+/*** 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;
+}
+
+static int mspro_block_switch_to_parallel(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 = 0,
+               .data_count = 0,
+               .data_address = 0,
+               .cmd_param = 0
+       };
+
+       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);
+       if (card->current_mrq.error)
+               return card->current_mrq.error;
+
+       msb->system = 0;
+       host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_PARALLEL);
+
+       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);
+
+       if (card->current_mrq.error) {
+               msb->system = 0x80;
+               host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/* 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);
+       struct mspro_param_register param = {
+               .system = msb->system,
+               .data_count = cpu_to_be16(1),
+               .data_address = 0,
+               .cmd_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;
+
+       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",
+                      card->dev.bus_id, 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",
+                      card->dev.bus_id);
+               attr_count = MSPRO_BLOCK_MAX_ATTRIBUTES;
+       } else
+               attr_count = attr->count;
+
+       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";
+
+       buffer = kmalloc(msb->page_size, GFP_KERNEL);
+       if (!buffer) {
+               rc = -ENOMEM;
+               goto out_free_attr;
+       }
+       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);
+
+               s_attr->dev_attr.attr.name = s_attr->name;
+               s_attr->dev_attr.attr.mode = S_IRUGO;
+               s_attr->dev_attr.attr.owner = THIS_MODULE;
+               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.cmd_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;
+               }
+
+               memcpy(s_attr->data, buffer + addr % msb->page_size, rc);
+       }
+
+       rc = 0;
+out_free_buffer:
+       kfree(buffer);
+out_free_attr:
+       kfree(attr);
+       return rc;
+}
+
+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 = 0x80;
+       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;
+
+       if (host->caps & MEMSTICK_CAP_PARALLEL) {
+               if (mspro_block_switch_to_parallel(card))
+                       printk(KERN_WARNING "%s: could not switch to "
+                              "parallel interface\n", card->dev.bus_id);
+       }
+
+       rc = mspro_block_wait_for_ced(card);
+       if (rc)
+               return rc;
+       dev_dbg(&card->dev, "card activated\n");
+
+       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;
+
+       dev_dbg(&card->dev, "card r/w status %d\n", 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");
+       return 0;
+
+}
+
+static int mspro_block_init_disk(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct memstick_host *host = card->host;
+       struct mspro_devinfo *dev_info = NULL;
+       struct mspro_sys_info *sys_info = NULL;
+       struct mspro_sys_attr *s_attr = NULL;
+       int rc, disk_id;
+       u64 limit = BLK_BOUNCE_HIGH;
+       unsigned long capacity;
+
+       if (host->cdev.dev->dma_mask && *(host->cdev.dev->dma_mask))
+               limit = *(host->cdev.dev->dma_mask);
+
+       for (rc = 0; msb->attr_group.attrs[rc]; ++rc) {
+               s_attr = mspro_from_sysfs_attr(msb->attr_group.attrs[rc]);
+
+               if (s_attr->id == MSPRO_BLOCK_ID_DEVINFO)
+                       dev_info = s_attr->data;
+               else if (s_attr->id == MSPRO_BLOCK_ID_SYSINFO)
+                       sys_info = s_attr->data;
+       }
+
+       if (!dev_info || !sys_info)
+               return -ENODEV;
+
+       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);
+
+       msb->page_size = be16_to_cpu(sys_info->unit_size);
+
+       if (!idr_pre_get(&mspro_block_disk_idr, GFP_KERNEL))
+               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);
+
+       if (rc)
+               return rc;
+
+       if ((disk_id << MEMSTICK_PART_SHIFT) > 255) {
+               rc = -ENOSPC;
+               goto out_release_id;
+       }
+
+       msb->disk = alloc_disk(1 << MEMSTICK_PART_SHIFT);
+       if (!msb->disk) {
+               rc = -ENOMEM;
+               goto out_release_id;
+       }
+
+       spin_lock_init(&msb->q_lock);
+       init_waitqueue_head(&msb->q_wait);
+
+       msb->queue = blk_init_queue(mspro_block_request, &msb->q_lock);
+       if (!msb->queue) {
+               rc = -ENOMEM;
+               goto out_put_disk;
+       }
+
+       msb->queue->queuedata = card;
+
+       blk_queue_bounce_limit(msb->queue, limit);
+       blk_queue_max_sectors(msb->queue, MSPRO_BLOCK_MAX_PAGES);
+       blk_queue_max_phys_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS);
+       blk_queue_max_hw_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS);
+       blk_queue_max_segment_size(msb->queue,
+                                  MSPRO_BLOCK_MAX_PAGES * msb->page_size);
+
+       msb->disk->major = major;
+       msb->disk->first_minor = disk_id << MEMSTICK_PART_SHIFT;
+       msb->disk->fops = &ms_block_bdops;
+       msb->usage_count = 1;
+       msb->disk->private_data = msb;
+       msb->disk->queue = msb->queue;
+       msb->disk->driverfs_dev = &card->dev;
+
+       sprintf(msb->disk->disk_name, "mspblk%d", disk_id);
+
+       blk_queue_hardsect_size(msb->queue, msb->page_size);
+
+       capacity = be16_to_cpu(sys_info->user_block_count);
+       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);
+       msb->q_thread = kthread_run(mspro_block_queue_thread, card,
+                                   DRIVER_NAME"d");
+       if (IS_ERR(msb->q_thread))
+               goto out_put_disk;
+
+       mutex_unlock(&host->lock);
+       add_disk(msb->disk);
+       mutex_lock(&host->lock);
+       msb->active = 1;
+       return 0;
+
+out_put_disk:
+       put_disk(msb->disk);
+out_release_id:
+       mutex_lock(&mspro_block_disk_lock);
+       idr_remove(&mspro_block_disk_idr, disk_id);
+       mutex_unlock(&mspro_block_disk_lock);
+       return rc;
+}
+
+static void mspro_block_data_clear(struct mspro_block_data *msb)
+{
+       int cnt;
+       struct mspro_sys_attr *s_attr;
+
+       if (msb->attr_group.attrs) {
+               for (cnt = 0; msb->attr_group.attrs[cnt]; ++cnt) {
+                       s_attr = mspro_from_sysfs_attr(msb->attr_group
+                                                          .attrs[cnt]);
+                       kfree(s_attr->data);
+                       kfree(s_attr);
+               }
+               kfree(msb->attr_group.attrs);
+       }
+
+       msb->card = NULL;
+}
+
+static int mspro_block_check_card(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+       return (msb->active == 1);
+}
+
+static int mspro_block_probe(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb;
+       int rc = 0;
+
+       msb = kzalloc(sizeof(struct mspro_block_data), GFP_KERNEL);
+       if (!msb)
+               return -ENOMEM;
+       memstick_set_drvdata(card, msb);
+       msb->card = card;
+
+       rc = mspro_block_init_card(card);
+
+       if (rc)
+               goto out_free;
+
+       rc = sysfs_create_group(&card->dev.kobj, &msb->attr_group);
+       if (rc)
+               goto out_free;
+
+       rc = mspro_block_init_disk(card);
+       if (!rc) {
+               card->check = mspro_block_check_card;
+               return 0;
+       }
+
+       sysfs_remove_group(&card->dev.kobj, &msb->attr_group);
+out_free:
+       memstick_set_drvdata(card, NULL);
+       mspro_block_data_clear(msb);
+       kfree(msb);
+       return rc;
+}
+
+static void mspro_block_remove(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct task_struct *q_thread = NULL;
+       unsigned long flags;
+
+       del_gendisk(msb->disk);
+       dev_dbg(&card->dev, "mspro block remove\n");
+       spin_lock_irqsave(&msb->q_lock, flags);
+       q_thread = msb->q_thread;
+       msb->q_thread = NULL;
+       msb->active = 0;
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+
+       if (q_thread) {
+               mutex_unlock(&card->host->lock);
+               kthread_stop(q_thread);
+               mutex_lock(&card->host->lock);
+       }
+
+       dev_dbg(&card->dev, "queue thread stopped\n");
+
+       blk_cleanup_queue(msb->queue);
+
+       sysfs_remove_group(&card->dev.kobj, &msb->attr_group);
+
+       mutex_lock(&mspro_block_disk_lock);
+       mspro_block_data_clear(msb);
+       mutex_unlock(&mspro_block_disk_lock);
+
+       mspro_block_disk_release(msb->disk);
+       memstick_set_drvdata(card, NULL);
+}
+
+#ifdef CONFIG_PM
+
+static int mspro_block_suspend(struct memstick_dev *card, pm_message_t state)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct task_struct *q_thread = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&msb->q_lock, flags);
+       q_thread = msb->q_thread;
+       msb->q_thread = NULL;
+       msb->active = 0;
+       blk_stop_queue(msb->queue);
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+
+       if (q_thread)
+               kthread_stop(q_thread);
+
+       return 0;
+}
+
+static int mspro_block_resume(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       unsigned long flags;
+       int rc = 0;
+
+#ifdef CONFIG_MEMSTICK_UNSAFE_RESUME
+
+       struct mspro_block_data *new_msb;
+       struct memstick_host *host = card->host;
+       struct mspro_sys_attr *s_attr, *r_attr;
+       unsigned char cnt;
+
+       mutex_lock(&host->lock);
+       new_msb = kzalloc(sizeof(struct mspro_block_data), GFP_KERNEL);
+       if (!new_msb) {
+               rc = -ENOMEM;
+               goto out_unlock;
+       }
+
+       new_msb->card = card;
+       memstick_set_drvdata(card, new_msb);
+       if (mspro_block_init_card(card))
+               goto out_free;
+
+       for (cnt = 0; new_msb->attr_group.attrs[cnt]
+                     && msb->attr_group.attrs[cnt]; ++cnt) {
+               s_attr = mspro_from_sysfs_attr(new_msb->attr_group.attrs[cnt]);
+               r_attr = mspro_from_sysfs_attr(msb->attr_group.attrs[cnt]);
+
+               if (s_attr->id == MSPRO_BLOCK_ID_SYSINFO
+                   && r_attr->id == s_attr->id) {
+                       if (memcmp(s_attr->data, r_attr->data, s_attr->size))
+                               break;
+
+                       memstick_set_drvdata(card, msb);
+                       msb->q_thread = kthread_run(mspro_block_queue_thread,
+                                                   card, DRIVER_NAME"d");
+                       if (IS_ERR(msb->q_thread))
+                               msb->q_thread = NULL;
+                       else
+                               msb->active = 1;
+
+                       break;
+               }
+       }
+
+out_free:
+       memstick_set_drvdata(card, msb);
+       mspro_block_data_clear(new_msb);
+       kfree(new_msb);
+out_unlock:
+       mutex_unlock(&host->lock);
+
+#endif /* CONFIG_MEMSTICK_UNSAFE_RESUME */
+
+       spin_lock_irqsave(&msb->q_lock, flags);
+       blk_start_queue(msb->queue);
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+       return rc;
+}
+
+#else
+
+#define mspro_block_suspend NULL
+#define mspro_block_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct memstick_device_id mspro_block_id_tbl[] = {
+       {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_PRO, MEMSTICK_CATEGORY_STORAGE_DUO,
+        MEMSTICK_CLASS_GENERIC_DUO},
+       {}
+};
+
+
+static struct memstick_driver mspro_block_driver = {
+       .driver = {
+               .name  = DRIVER_NAME,
+               .owner = THIS_MODULE
+       },
+       .id_table = mspro_block_id_tbl,
+       .probe    = mspro_block_probe,
+       .remove   = mspro_block_remove,
+       .suspend  = mspro_block_suspend,
+       .resume   = mspro_block_resume
+};
+
+static int __init mspro_block_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(&mspro_block_driver);
+       if (rc)
+               unregister_blkdev(major, DRIVER_NAME);
+       return rc;
+}
+
+static void __exit mspro_block_exit(void)
+{
+       memstick_unregister_driver(&mspro_block_driver);
+       unregister_blkdev(major, DRIVER_NAME);
+       idr_destroy(&mspro_block_disk_idr);
+}
+
+module_init(mspro_block_init);
+module_exit(mspro_block_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alex Dubov");
+MODULE_DESCRIPTION("Sony MemoryStickPro block device driver");
+MODULE_DEVICE_TABLE(memstick, mspro_block_id_tbl);
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig
new file mode 100644 (file)
index 0000000..c002fcc
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# MemoryStick host controller drivers
+#
+
+comment "MemoryStick Host Controller Drivers"
+
+config MEMSTICK_TIFM_MS
+       tristate "TI Flash Media MemoryStick Interface support  (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && PCI
+       select TIFM_CORE
+       help
+         Say Y here if you want to be able to access MemoryStick cards with
+         the Texas Instruments(R) Flash Media card reader, found in many
+         laptops.
+         This option 'selects' (turns on, enables) 'TIFM_CORE', but you
+         probably also need appropriate card reader host adapter, such as
+         'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
+         (TIFM_7XX1)'.
+
+          To compile this driver as a module, choose M here: the
+         module will be called tifm_ms.
+
diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile
new file mode 100644 (file)
index 0000000..ee66638
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for MemoryStick host controller drivers
+#
+
+ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
+       EXTRA_CFLAGS            += -DDEBUG
+endif
+
+obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
+
diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c
new file mode 100644 (file)
index 0000000..f55b71a
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ *  TI FlashMedia driver
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.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/tifm.h>
+#include <linux/memstick.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <linux/log2.h>
+#include <asm/io.h>
+
+#define DRIVER_NAME "tifm_ms"
+#define DRIVER_VERSION "0.1"
+
+static int no_dma;
+module_param(no_dma, bool, 0644);
+
+#define TIFM_MS_TIMEOUT      0x00100
+#define TIFM_MS_BADCRC       0x00200
+#define TIFM_MS_EOTPC        0x01000
+#define TIFM_MS_INT          0x02000
+
+/* The meaning of the bit majority in this constant is unknown. */
+#define TIFM_MS_SERIAL       0x04010
+
+#define TIFM_MS_SYS_LATCH    0x00100
+#define TIFM_MS_SYS_NOT_RDY  0x00800
+#define TIFM_MS_SYS_DATA     0x10000
+
+/* Hardware flags */
+enum {
+       CMD_READY  = 0x0001,
+       FIFO_READY = 0x0002,
+       CARD_READY = 0x0004,
+       DATA_CARRY = 0x0008
+};
+
+struct tifm_ms {
+       struct tifm_dev         *dev;
+       unsigned short          eject:1,
+                               no_dma:1;
+       unsigned short          cmd_flags;
+       unsigned int            mode_mask;
+       unsigned int            block_pos;
+       unsigned long           timeout_jiffies;
+
+       struct timer_list       timer;
+       struct memstick_request *req;
+       unsigned int            io_word;
+};
+
+static void tifm_ms_read_fifo(struct tifm_ms *host, unsigned int fifo_offset,
+                             struct page *pg, unsigned int page_off,
+                             unsigned int length)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned int cnt = 0, off = 0;
+       unsigned char *buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + page_off;
+
+       if (host->cmd_flags & DATA_CARRY) {
+               while ((fifo_offset & 3) && length) {
+                       buf[off++] = host->io_word & 0xff;
+                       host->io_word >>= 8;
+                       length--;
+                       fifo_offset++;
+               }
+               if (!(fifo_offset & 3))
+                       host->cmd_flags &= ~DATA_CARRY;
+               if (!length)
+                       return;
+       }
+
+       do {
+               host->io_word = readl(sock->addr + SOCK_FIFO_ACCESS
+                                     + fifo_offset);
+               cnt = 4;
+               while (length && cnt) {
+                       buf[off++] = (host->io_word >> 8) & 0xff;
+                       cnt--;
+                       length--;
+               }
+               fifo_offset += 4 - cnt;
+       } while (length);
+
+       if (cnt)
+               host->cmd_flags |= DATA_CARRY;
+
+       kunmap_atomic(buf - page_off, KM_BIO_DST_IRQ);
+}
+
+static void tifm_ms_write_fifo(struct tifm_ms *host, unsigned int fifo_offset,
+                              struct page *pg, unsigned int page_off,
+                              unsigned int length)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned int cnt = 0, off = 0;
+       unsigned char *buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + page_off;
+
+       if (host->cmd_flags & DATA_CARRY) {
+               while (fifo_offset & 3) {
+                       host->io_word |= buf[off++] << (8 * (fifo_offset & 3));
+                       length--;
+                       fifo_offset++;
+               }
+               if (!(fifo_offset & 3)) {
+                       writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
+                              + fifo_offset - 4);
+
+                       host->cmd_flags &= ~DATA_CARRY;
+               }
+               if (!length)
+                       return;
+       }
+
+       do {
+               cnt = 4;
+               host->io_word = 0;
+               while (length && cnt) {
+                       host->io_word |= buf[off++] << (4 - cnt);
+                       cnt--;
+                       length--;
+               }
+               fifo_offset += 4 - cnt;
+               if (!cnt)
+                       writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
+                                             + fifo_offset - 4);
+
+       } while (length);
+
+       if (cnt)
+               host->cmd_flags |= DATA_CARRY;
+
+       kunmap_atomic(buf - page_off, KM_BIO_SRC_IRQ);
+}
+
+static void tifm_ms_move_block(struct tifm_ms *host, unsigned int length)
+{
+       unsigned int t_size;
+       unsigned int off = host->req->sg.offset + host->block_pos;
+       unsigned int p_off, p_cnt;
+       struct page *pg;
+       unsigned long flags;
+
+       dev_dbg(&host->dev->dev, "moving block\n");
+       local_irq_save(flags);
+       t_size = length;
+       while (t_size) {
+               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, t_size);
+
+               if (host->req->data_dir == WRITE)
+                       tifm_ms_write_fifo(host, length - t_size,
+                                          pg, p_off, p_cnt);
+               else
+                       tifm_ms_read_fifo(host, length - t_size,
+                                         pg, p_off, p_cnt);
+
+               t_size -= p_cnt;
+       }
+       local_irq_restore(flags);
+}
+
+static int tifm_ms_transfer_data(struct tifm_ms *host, int skip)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned int length = host->req->sg.length - host->block_pos;
+
+       if (!length)
+               return 1;
+
+       if (length > TIFM_FIFO_SIZE)
+               length = TIFM_FIFO_SIZE;
+
+       if (!skip) {
+               tifm_ms_move_block(host, length);
+               host->block_pos += length;
+       }
+
+       if ((host->req->data_dir == READ)
+           && (host->block_pos == host->req->sg.length))
+               return 1;
+
+       writel(ilog2(length) - 2, sock->addr + SOCK_FIFO_PAGE_SIZE);
+       if (host->req->data_dir == WRITE)
+               writel((1 << 8) | TIFM_DMA_TX, sock->addr + SOCK_DMA_CONTROL);
+       else
+               writel((1 << 8), sock->addr + SOCK_DMA_CONTROL);
+
+       return 0;
+}
+
+static int tifm_ms_issue_cmd(struct tifm_ms *host)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned char *data;
+       unsigned int data_len = 0, cmd = 0, cmd_mask = 0, cnt, tval = 0;
+
+       host->cmd_flags = 0;
+
+       if (host->req->io_type == MEMSTICK_IO_SG) {
+               if (!host->no_dma) {
+                       if (1 != tifm_map_sg(sock, &host->req->sg, 1,
+                                            host->req->data_dir == READ
+                                            ? PCI_DMA_FROMDEVICE
+                                            : PCI_DMA_TODEVICE)) {
+                               host->req->error = -ENOMEM;
+                               return host->req->error;
+                       }
+                       data_len = sg_dma_len(&host->req->sg);
+               } else
+                       data_len = host->req->sg.length;
+
+               writel(TIFM_FIFO_INT_SETALL,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+               writel(TIFM_FIFO_ENABLE,
+                      sock->addr + SOCK_FIFO_CONTROL);
+               writel(TIFM_FIFO_INTMASK,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+
+               if (!host->no_dma) {
+                       writel(ilog2(data_len) - 2,
+                              sock->addr + SOCK_FIFO_PAGE_SIZE);
+                       writel(sg_dma_address(&host->req->sg),
+                              sock->addr + SOCK_DMA_ADDRESS);
+                       if (host->req->data_dir == WRITE)
+                               writel((1 << 8) | TIFM_DMA_TX | TIFM_DMA_EN,
+                                      sock->addr + SOCK_DMA_CONTROL);
+                       else
+                               writel((1 << 8) | TIFM_DMA_EN,
+                                      sock->addr + SOCK_DMA_CONTROL);
+               } else {
+                       tifm_ms_transfer_data(host,
+                                             host->req->data_dir == READ);
+               }
+
+               cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
+               cmd_mask |= TIFM_MS_SYS_DATA | TIFM_MS_SYS_NOT_RDY;
+               writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+       } else if (host->req->io_type == MEMSTICK_IO_VAL) {
+               data = host->req->data;
+               data_len = host->req->data_len;
+
+               cmd_mask = host->mode_mask | 0x2607; /* unknown constant */
+
+               if (host->req->data_dir == WRITE) {
+                       cmd_mask |= TIFM_MS_SYS_LATCH;
+                       writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+                       for (cnt = 0; (data_len - cnt) >= 4; cnt += 4) {
+                               writel(TIFM_MS_SYS_LATCH
+                                      | readl(sock->addr + SOCK_MS_SYSTEM),
+                                      sock->addr + SOCK_MS_SYSTEM);
+                               __raw_writel(*(unsigned int *)(data + cnt),
+                                            sock->addr + SOCK_MS_DATA);
+                               dev_dbg(&sock->dev, "writing %x\n",
+                                       *(int *)(data + cnt));
+                       }
+                       switch (data_len - cnt) {
+                       case 3:
+                               tval |= data[cnt + 2] << 16;
+                       case 2:
+                               tval |= data[cnt + 1] << 8;
+                       case 1:
+                               tval |= data[cnt];
+                               writel(TIFM_MS_SYS_LATCH
+                                      | readl(sock->addr + SOCK_MS_SYSTEM),
+                                      sock->addr + SOCK_MS_SYSTEM);
+                               writel(tval, sock->addr + SOCK_MS_DATA);
+                               dev_dbg(&sock->dev, "writing %x\n", tval);
+                       }
+
+                       writel(TIFM_MS_SYS_LATCH
+                              | readl(sock->addr + SOCK_MS_SYSTEM),
+                              sock + SOCK_MS_SYSTEM);
+                       writel(0, sock->addr + SOCK_MS_DATA);
+                       dev_dbg(&sock->dev, "writing %x\n", 0);
+
+               } else
+                       writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+
+               cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
+               cmd_mask &= ~TIFM_MS_SYS_DATA;
+               cmd_mask |= TIFM_MS_SYS_NOT_RDY;
+               dev_dbg(&sock->dev, "mask %x\n", cmd_mask);
+               writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+       } else
+               BUG();
+
+       mod_timer(&host->timer, jiffies + host->timeout_jiffies);
+       writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
+              sock->addr + SOCK_CONTROL);
+       host->req->error = 0;
+
+       cmd = (host->req->tpc & 0xf) << 12;
+       cmd |= data_len;
+       writel(cmd, sock->addr + SOCK_MS_COMMAND);
+
+       dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, cmd_mask);
+       return 0;
+}
+
+static void tifm_ms_complete_cmd(struct tifm_ms *host)
+{
+       struct tifm_dev *sock = host->dev;
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+       unsigned int tval = 0, data_len;
+       unsigned char *data;
+       int rc;
+
+       del_timer(&host->timer);
+       if (host->req->io_type == MEMSTICK_IO_SG) {
+               if (!host->no_dma)
+                       tifm_unmap_sg(sock, &host->req->sg, 1,
+                                     host->req->data_dir == READ
+                                     ? PCI_DMA_FROMDEVICE
+                                     : PCI_DMA_TODEVICE);
+       } else if (host->req->io_type == MEMSTICK_IO_VAL) {
+               writel(~TIFM_MS_SYS_DATA & readl(sock->addr + SOCK_MS_SYSTEM),
+                      sock->addr + SOCK_MS_SYSTEM);
+
+               data = host->req->data;
+               data_len = host->req->data_len;
+
+               if (host->req->data_dir == READ) {
+                       for (rc = 0; (data_len - rc) >= 4; rc += 4)
+                               *(int *)(data + rc)
+                                       = __raw_readl(sock->addr
+                                                     + SOCK_MS_DATA);
+
+                       if (data_len - rc)
+                               tval = readl(sock->addr + SOCK_MS_DATA);
+                       switch (data_len - rc) {
+                       case 3:
+                               data[rc + 2] = (tval >> 16) & 0xff;
+                       case 2:
+                               data[rc + 1] = (tval >> 8) & 0xff;
+                       case 1:
+                               data[rc] = tval & 0xff;
+                       }
+                       readl(sock->addr + SOCK_MS_DATA);
+               }
+       }
+
+       writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
+              sock->addr + SOCK_CONTROL);
+
+       do {
+               rc = memstick_next_req(msh, &host->req);
+       } while (!rc && tifm_ms_issue_cmd(host));
+}
+
+static int tifm_ms_check_status(struct tifm_ms *host)
+{
+       if (!host->req->error) {
+               if (!(host->cmd_flags & CMD_READY))
+                       return 1;
+               if ((host->req->io_type == MEMSTICK_IO_SG)
+                   && !(host->cmd_flags & FIFO_READY))
+                       return 1;
+               if (host->req->need_card_int
+                   && !(host->cmd_flags & CARD_READY))
+                       return 1;
+       }
+       return 0;
+}
+
+/* Called from interrupt handler */
+static void tifm_ms_data_event(struct tifm_dev *sock)
+{
+       struct tifm_ms *host;
+       unsigned int fifo_status = 0;
+       int rc = 1;
+
+       spin_lock(&sock->lock);
+       host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
+       fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
+       dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
+               fifo_status, host->cmd_flags);
+
+       if (host->req) {
+               if (fifo_status & TIFM_FIFO_READY) {
+                       if (!host->no_dma || tifm_ms_transfer_data(host, 0)) {
+                               host->cmd_flags |= FIFO_READY;
+                               rc = tifm_ms_check_status(host);
+                       }
+               }
+       }
+
+       writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
+       if (!rc)
+               tifm_ms_complete_cmd(host);
+
+       spin_unlock(&sock->lock);
+}
+
+
+/* Called from interrupt handler */
+static void tifm_ms_card_event(struct tifm_dev *sock)
+{
+       struct tifm_ms *host;
+       unsigned int host_status = 0;
+       int rc = 1;
+
+       spin_lock(&sock->lock);
+       host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
+       host_status = readl(sock->addr + SOCK_MS_STATUS);
+       dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
+               host_status, host->cmd_flags);
+
+       if (host->req) {
+               if (host_status & TIFM_MS_TIMEOUT)
+                       host->req->error = -ETIME;
+               else if (host_status & TIFM_MS_BADCRC)
+                       host->req->error = -EILSEQ;
+
+               if (host->req->error) {
+                       writel(TIFM_FIFO_INT_SETALL,
+                              sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+                       writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
+               }
+
+               if (host_status & TIFM_MS_EOTPC)
+                       host->cmd_flags |= CMD_READY;
+               if (host_status & TIFM_MS_INT)
+                       host->cmd_flags |= CARD_READY;
+
+               rc = tifm_ms_check_status(host);
+
+       }
+
+       writel(TIFM_MS_SYS_NOT_RDY | readl(sock->addr + SOCK_MS_SYSTEM),
+              sock->addr + SOCK_MS_SYSTEM);
+       writel((~TIFM_MS_SYS_DATA) & readl(sock->addr + SOCK_MS_SYSTEM),
+              sock->addr + SOCK_MS_SYSTEM);
+
+       if (!rc)
+               tifm_ms_complete_cmd(host);
+
+       spin_unlock(&sock->lock);
+       return;
+}
+
+static void tifm_ms_request(struct memstick_host *msh)
+{
+       struct tifm_ms *host = memstick_priv(msh);
+       struct tifm_dev *sock = host->dev;
+       unsigned long flags;
+       int rc;
+
+       spin_lock_irqsave(&sock->lock, flags);
+       if (host->req) {
+               printk(KERN_ERR "%s : unfinished request detected\n",
+                      sock->dev.bus_id);
+               spin_unlock_irqrestore(&sock->lock, flags);
+               tifm_eject(host->dev);
+               return;
+       }
+
+       if (host->eject) {
+               do {
+                       rc = memstick_next_req(msh, &host->req);
+                       if (!rc)
+                               host->req->error = -ETIME;
+               } while (!rc);
+               spin_unlock_irqrestore(&sock->lock, flags);
+               return;
+       }
+
+       do {
+               rc = memstick_next_req(msh, &host->req);
+       } while (!rc && tifm_ms_issue_cmd(host));
+
+       spin_unlock_irqrestore(&sock->lock, flags);
+       return;
+}
+
+static void tifm_ms_set_param(struct memstick_host *msh,
+                             enum memstick_param param,
+                             int value)
+{
+       struct tifm_ms *host = memstick_priv(msh);
+       struct tifm_dev *sock = host->dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sock->lock, flags);
+
+       switch (param) {
+       case MEMSTICK_POWER:
+               /* this is set by card detection mechanism */
+               break;
+       case MEMSTICK_INTERFACE:
+               if (value == MEMSTICK_SERIAL) {
+                       host->mode_mask = TIFM_MS_SERIAL;
+                       writel((~TIFM_CTRL_FAST_CLK)
+                              & readl(sock->addr + SOCK_CONTROL),
+                              sock->addr + SOCK_CONTROL);
+               } else if (value == MEMSTICK_PARALLEL) {
+                       host->mode_mask = 0;
+                       writel(TIFM_CTRL_FAST_CLK
+                              | readl(sock->addr + SOCK_CONTROL),
+                              sock->addr + SOCK_CONTROL);
+               }
+               break;
+       };
+
+       spin_unlock_irqrestore(&sock->lock, flags);
+}
+
+static void tifm_ms_abort(unsigned long data)
+{
+       struct tifm_ms *host = (struct tifm_ms *)data;
+
+       dev_dbg(&host->dev->dev, "status %x\n",
+               readl(host->dev->addr + SOCK_MS_STATUS));
+       printk(KERN_ERR
+              "%s : card failed to respond for a long period of time "
+              "(%x, %x)\n",
+              host->dev->dev.bus_id, host->req ? host->req->tpc : 0,
+              host->cmd_flags);
+
+       tifm_eject(host->dev);
+}
+
+static int tifm_ms_initialize_host(struct tifm_ms *host)
+{
+       struct tifm_dev *sock = host->dev;
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+
+       host->mode_mask = TIFM_MS_SERIAL;
+       writel(0x8000, sock->addr + SOCK_MS_SYSTEM);
+       writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
+       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
+       if (tifm_has_ms_pif(sock))
+               msh->caps |= MEMSTICK_CAP_PARALLEL;
+
+       return 0;
+}
+
+static int tifm_ms_probe(struct tifm_dev *sock)
+{
+       struct memstick_host *msh;
+       struct tifm_ms *host;
+       int rc = -EIO;
+
+       if (!(TIFM_SOCK_STATE_OCCUPIED
+             & readl(sock->addr + SOCK_PRESENT_STATE))) {
+               printk(KERN_WARNING "%s : card gone, unexpectedly\n",
+                      sock->dev.bus_id);
+               return rc;
+       }
+
+       msh = memstick_alloc_host(sizeof(struct tifm_ms), &sock->dev);
+       if (!msh)
+               return -ENOMEM;
+
+       host = memstick_priv(msh);
+       tifm_set_drvdata(sock, msh);
+       host->dev = sock;
+       host->timeout_jiffies = msecs_to_jiffies(1000);
+       host->no_dma = no_dma;
+
+       setup_timer(&host->timer, tifm_ms_abort, (unsigned long)host);
+
+       msh->request = tifm_ms_request;
+       msh->set_param = tifm_ms_set_param;
+       sock->card_event = tifm_ms_card_event;
+       sock->data_event = tifm_ms_data_event;
+       rc = tifm_ms_initialize_host(host);
+
+       if (!rc)
+               rc = memstick_add_host(msh);
+       if (!rc)
+               return 0;
+
+       memstick_free_host(msh);
+       return rc;
+}
+
+static void tifm_ms_remove(struct tifm_dev *sock)
+{
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+       struct tifm_ms *host = memstick_priv(msh);
+       int rc = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sock->lock, flags);
+       host->eject = 1;
+       if (host->req) {
+               del_timer(&host->timer);
+               writel(TIFM_FIFO_INT_SETALL,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+               writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
+               if ((host->req->io_type == MEMSTICK_IO_SG) && !host->no_dma)
+                       tifm_unmap_sg(sock, &host->req->sg, 1,
+                                     host->req->data_dir == READ
+                                     ? PCI_DMA_TODEVICE
+                                     : PCI_DMA_FROMDEVICE);
+               host->req->error = -ETIME;
+
+               do {
+                       rc = memstick_next_req(msh, &host->req);
+                       if (!rc)
+                               host->req->error = -ETIME;
+               } while (!rc);
+       }
+       spin_unlock_irqrestore(&sock->lock, flags);
+
+       memstick_remove_host(msh);
+
+       writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
+       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
+
+       memstick_free_host(msh);
+}
+
+#ifdef CONFIG_PM
+
+static int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state)
+{
+       return 0;
+}
+
+static int tifm_ms_resume(struct tifm_dev *sock)
+{
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+       struct tifm_ms *host = memstick_priv(msh);
+
+       tifm_ms_initialize_host(host);
+       memstick_detect_change(msh);
+
+       return 0;
+}
+
+#else
+
+#define tifm_ms_suspend NULL
+#define tifm_ms_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct tifm_device_id tifm_ms_id_tbl[] = {
+       { TIFM_TYPE_MS }, { 0 }
+};
+
+static struct tifm_driver tifm_ms_driver = {
+       .driver = {
+               .name  = DRIVER_NAME,
+               .owner = THIS_MODULE
+       },
+       .id_table = tifm_ms_id_tbl,
+       .probe    = tifm_ms_probe,
+       .remove   = tifm_ms_remove,
+       .suspend  = tifm_ms_suspend,
+       .resume   = tifm_ms_resume
+};
+
+static int __init tifm_ms_init(void)
+{
+       return tifm_register_driver(&tifm_ms_driver);
+}
+
+static void __exit tifm_ms_exit(void)
+{
+       tifm_unregister_driver(&tifm_ms_driver);
+}
+
+MODULE_AUTHOR("Alex Dubov");
+MODULE_DESCRIPTION("TI FlashMedia MemoryStick driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl);
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(tifm_ms_init);
+module_exit(tifm_ms_exit);
index c143a86c2ea6f03b6379220a20e04d254ecc54be..1abc95ca9dfa2fe67aaa85405b259d16427c639c 100644 (file)
@@ -114,6 +114,9 @@ config ACER_WMI
          wireless radio and bluetooth control, and on some laptops,
          exposes the mail LED and LCD backlight.
 
+         For more information about this driver see
+         <file:Documentation/laptops/acer-wmi.txt>
+
          If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
          here.
 
@@ -152,8 +155,9 @@ config FUJITSU_LAPTOP
          If you have a Fujitsu laptop, say Y or M here.
 
 config TC1100_WMI
-       tristate "HP Compaq TC1100 Tablet WMI Extras"
+       tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
        depends on X86 && !X86_64
+       depends on EXPERIMENTAL
        depends on ACPI
        select ACPI_WMI
        ---help---
@@ -192,7 +196,7 @@ config SONY_LAPTOP
          screen brightness control, Fn keys and allows powering on/off some
          devices.
 
-         Read <file:Documentation/sony-laptop.txt> for more information.
+         Read <file:Documentation/laptops/sony-laptop.txt> for more information.
 
 config SONYPI_COMPAT
        bool "Sonypi compatibility"
@@ -211,8 +215,9 @@ config THINKPAD_ACPI
          This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
          support for Fn-Fx key combinations, Bluetooth control, video
          output switching, ThinkLight control, UltraBay eject and more.
-         For more information about this driver see 
-         <file:Documentation/thinkpad-acpi.txt> and <http://ibm-acpi.sf.net/> .
+         For more information about this driver see
+         <file:Documentation/laptops/thinkpad-acpi.txt> and
+         <http://ibm-acpi.sf.net/> .
 
          This driver was formerly known as ibm-acpi.
 
index a4d6775042506a66d87bb3dd80dcf20a190b7f43..d7aea93081f26f98310ebbb1db6a4e29e1ed7ffb 100644 (file)
@@ -428,11 +428,9 @@ static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
                if (value > max_brightness)
                        return AE_BAD_PARAMETER;
                switch (quirks->brightness) {
-               case 1:
-                       return ec_write(0x83, value);
                default:
-                       return AE_BAD_ADDRESS;
-               break;
+                       return ec_write(0x83, value);
+                       break;
                }
        default:
                return AE_BAD_ADDRESS;
index 54380da343a530ab4a4513eef7501d9e52b6d487..63a089b29545a3bd34eb1e64327c6b693ce04e85 100644 (file)
@@ -302,6 +302,21 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
 
 #endif /* CONFIG_PM */
 
+static int tifm_7xx1_dummy_has_ms_pif(struct tifm_adapter *fm,
+                                     struct tifm_dev *sock)
+{
+       return 0;
+}
+
+static int tifm_7xx1_has_ms_pif(struct tifm_adapter *fm, struct tifm_dev *sock)
+{
+       if (((fm->num_sockets == 4) && (sock->socket_id == 2))
+           || ((fm->num_sockets == 2) && (sock->socket_id == 0)))
+               return 1;
+
+       return 0;
+}
+
 static int tifm_7xx1_probe(struct pci_dev *dev,
                           const struct pci_device_id *dev_id)
 {
@@ -336,6 +351,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
 
        INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
        fm->eject = tifm_7xx1_eject;
+       fm->has_ms_pif = tifm_7xx1_has_ms_pif;
        pci_set_drvdata(dev, fm);
 
        fm->addr = ioremap(pci_resource_start(dev, 0),
@@ -377,6 +393,7 @@ static void tifm_7xx1_remove(struct pci_dev *dev)
        int cnt;
 
        fm->eject = tifm_7xx1_dummy_eject;
+       fm->has_ms_pif = tifm_7xx1_dummy_has_ms_pif;
        writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
        mmiowb();
        free_irq(dev->irq, fm);
index 97544052e7684dda18611c356ca65eab5d9da839..82dc72a1484f888b29665151d61fb35e7aa169e3 100644 (file)
@@ -284,6 +284,13 @@ void tifm_eject(struct tifm_dev *sock)
 }
 EXPORT_SYMBOL(tifm_eject);
 
+int tifm_has_ms_pif(struct tifm_dev *sock)
+{
+       struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);
+       return fm->has_ms_pif(fm, sock);
+}
+EXPORT_SYMBOL(tifm_has_ms_pif);
+
 int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
                int direction)
 {
index 5fef6783c7161fc8cf95b836e49dcc13259ff655..3b3cd0e74715c63161a5a3746ce0155cec420f6a 100644 (file)
@@ -25,8 +25,8 @@ config MMC_PXA
          If unsure, say N.
 
 config MMC_SDHCI
-       tristate "Secure Digital Host Controller Interface support  (EXPERIMENTAL)"
-       depends on PCI && EXPERIMENTAL
+       tristate "Secure Digital Host Controller Interface support"
+       depends on PCI
        help
          This select the generic Secure Digital Host Controller Interface.
          It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
@@ -118,8 +118,8 @@ config MMC_TIFM_SD
          module will be called tifm_sd.
 
 config MMC_SPI
-       tristate "MMC/SD over SPI (EXPERIMENTAL)"
-       depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
+       tristate "MMC/SD over SPI"
+       depends on MMC && SPI_MASTER && !HIGHMEM
        select CRC7
        select CRC_ITU_T
        help
index b1edcefdd4f9e7a615e2e38238f4d43e8456a9b4..21acecc9fe3a929063f626a4d06990cf0216dbc6 100644 (file)
 
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/gpio.h>
+
 #include <asm/mach/mmc.h>
 #include <asm/arch/board.h>
 #include <asm/arch/cpu.h>
-#include <asm/arch/gpio.h>
 #include <asm/arch/at91_mci.h>
 
 #define DRIVER_NAME "at91_mci"
@@ -659,11 +660,11 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (host->board->vcc_pin) {
                switch (ios->power_mode) {
                        case MMC_POWER_OFF:
-                               at91_set_gpio_value(host->board->vcc_pin, 0);
+                               gpio_set_value(host->board->vcc_pin, 0);
                                break;
                        case MMC_POWER_UP:
                        case MMC_POWER_ON:
-                               at91_set_gpio_value(host->board->vcc_pin, 1);
+                               gpio_set_value(host->board->vcc_pin, 1);
                                break;
                }
        }
@@ -768,7 +769,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
 static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
 {
        struct at91mci_host *host = _host;
-       int present = !at91_get_gpio_value(irq);
+       int present = !gpio_get_value(irq_to_gpio(irq));
 
        /*
         * we expect this irq on both insert and remove,
@@ -793,7 +794,7 @@ static int at91_mci_get_ro(struct mmc_host *mmc)
        struct at91mci_host *host = mmc_priv(mmc);
 
        if (host->board->wp_pin) {
-               read_only = at91_get_gpio_value(host->board->wp_pin);
+               read_only = gpio_get_value(host->board->wp_pin);
                printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc),
                                (read_only ? "read-only" : "read-write") );
        }
@@ -820,8 +821,6 @@ static int __init at91_mci_probe(struct platform_device *pdev)
        struct resource *res;
        int ret;
 
-       pr_debug("Probe MCI devices\n");
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENXIO;
@@ -831,9 +830,9 @@ static int __init at91_mci_probe(struct platform_device *pdev)
 
        mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
        if (!mmc) {
-               pr_debug("Failed to allocate mmc host\n");
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               dev_dbg(&pdev->dev, "couldn't allocate mmc host\n");
+               goto fail6;
        }
 
        mmc->ops = &at91_mci_ops;
@@ -853,19 +852,44 @@ static int __init at91_mci_probe(struct platform_device *pdev)
                if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
                        mmc->caps |= MMC_CAP_4_BIT_DATA;
                else
-                       printk("AT91 MMC: 4 wire bus mode not supported"
+                       dev_warn(&pdev->dev, "4 wire bus mode not supported"
                                " - using 1 wire\n");
        }
 
+       /*
+        * Reserve GPIOs ... board init code makes sure these pins are set
+        * up as GPIOs with the right direction (input, except for vcc)
+        */
+       if (host->board->det_pin) {
+               ret = gpio_request(host->board->det_pin, "mmc_detect");
+               if (ret < 0) {
+                       dev_dbg(&pdev->dev, "couldn't claim card detect pin\n");
+                       goto fail5;
+               }
+       }
+       if (host->board->wp_pin) {
+               ret = gpio_request(host->board->wp_pin, "mmc_wp");
+               if (ret < 0) {
+                       dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n");
+                       goto fail4;
+               }
+       }
+       if (host->board->vcc_pin) {
+               ret = gpio_request(host->board->vcc_pin, "mmc_vcc");
+               if (ret < 0) {
+                       dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n");
+                       goto fail3;
+               }
+       }
+
        /*
         * Get Clock
         */
        host->mci_clk = clk_get(&pdev->dev, "mci_clk");
        if (IS_ERR(host->mci_clk)) {
-               printk(KERN_ERR "AT91 MMC: no clock defined.\n");
-               mmc_free_host(mmc);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENODEV;
+               ret = -ENODEV;
+               dev_dbg(&pdev->dev, "no mci_clk?\n");
+               goto fail2;
        }
 
        /*
@@ -873,10 +897,8 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         */
        host->baseaddr = ioremap(res->start, res->end - res->start + 1);
        if (!host->baseaddr) {
-               clk_put(host->mci_clk);
-               mmc_free_host(mmc);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto fail1;
        }
 
        /*
@@ -890,15 +912,11 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * Allocate the MCI interrupt
         */
        host->irq = platform_get_irq(pdev, 0);
-       ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
+       ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED,
+                       mmc_hostname(mmc), host);
        if (ret) {
-               printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n");
-               clk_disable(host->mci_clk);
-               clk_put(host->mci_clk);
-               mmc_free_host(mmc);
-               iounmap(host->baseaddr);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return ret;
+               dev_dbg(&pdev->dev, "request MCI interrupt failed\n");
+               goto fail0;
        }
 
        platform_set_drvdata(pdev, mmc);
@@ -907,8 +925,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * Add host to MMC layer
         */
        if (host->board->det_pin) {
-               host->present = !at91_get_gpio_value(host->board->det_pin);
-               device_init_wakeup(&pdev->dev, 1);
+               host->present = !gpio_get_value(host->board->det_pin);
        }
        else
                host->present = -1;
@@ -919,15 +936,38 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * monitor card insertion/removal if we can
         */
        if (host->board->det_pin) {
-               ret = request_irq(host->board->det_pin, at91_mmc_det_irq,
-                               0, DRIVER_NAME, host);
+               ret = request_irq(gpio_to_irq(host->board->det_pin),
+                               at91_mmc_det_irq, 0, mmc_hostname(mmc), host);
                if (ret)
-                       printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n");
+                       dev_warn(&pdev->dev, "request MMC detect irq failed\n");
+               else
+                       device_init_wakeup(&pdev->dev, 1);
        }
 
        pr_debug("Added MCI driver\n");
 
        return 0;
+
+fail0:
+       clk_disable(host->mci_clk);
+       iounmap(host->baseaddr);
+fail1:
+       clk_put(host->mci_clk);
+fail2:
+       if (host->board->vcc_pin)
+               gpio_free(host->board->vcc_pin);
+fail3:
+       if (host->board->wp_pin)
+               gpio_free(host->board->wp_pin);
+fail4:
+       if (host->board->det_pin)
+               gpio_free(host->board->det_pin);
+fail5:
+       mmc_free_host(mmc);
+fail6:
+       release_mem_region(res->start, res->end - res->start + 1);
+       dev_err(&pdev->dev, "probe failed, err %d\n", ret);
+       return ret;
 }
 
 /*
@@ -945,9 +985,10 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
        host = mmc_priv(mmc);
 
        if (host->board->det_pin) {
+               if (device_can_wakeup(&pdev->dev))
+                       free_irq(gpio_to_irq(host->board->det_pin), host);
                device_init_wakeup(&pdev->dev, 0);
-               free_irq(host->board->det_pin, host);
-               cancel_delayed_work(&host->mmc->detect);
+               gpio_free(host->board->det_pin);
        }
 
        at91_mci_disable(host);
@@ -957,6 +998,11 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
        clk_disable(host->mci_clk);                     /* Disable the peripheral clock */
        clk_put(host->mci_clk);
 
+       if (host->board->vcc_pin)
+               gpio_free(host->board->vcc_pin);
+       if (host->board->wp_pin)
+               gpio_free(host->board->wp_pin);
+
        iounmap(host->baseaddr);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(res->start, res->end - res->start + 1);
index 1e8704533bc5280ecf15ccdae202feb289f2d4fe..a16d7609e4eede4fa88f4ecc6b3ac9dce699038a 100644 (file)
@@ -41,10 +41,91 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
 
 MODULE_DEVICE_TABLE(pci, pci_ids);
 
+static int ricoh_mmc_disable(struct pci_dev *fw_dev)
+{
+       u8 write_enable;
+       u8 write_target;
+       u8 disable;
+
+       if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
+               /* via RL5C476 */
+
+               pci_read_config_byte(fw_dev, 0xB7, &disable);
+               if (disable & 0x02) {
+                       printk(KERN_INFO DRIVER_NAME
+                               ": Controller already disabled. " \
+                               "Nothing to do.\n");
+                       return -ENODEV;
+               }
+
+               pci_read_config_byte(fw_dev, 0x8E, &write_enable);
+               pci_write_config_byte(fw_dev, 0x8E, 0xAA);
+               pci_read_config_byte(fw_dev, 0x8D, &write_target);
+               pci_write_config_byte(fw_dev, 0x8D, 0xB7);
+               pci_write_config_byte(fw_dev, 0xB7, disable | 0x02);
+               pci_write_config_byte(fw_dev, 0x8E, write_enable);
+               pci_write_config_byte(fw_dev, 0x8D, write_target);
+       } else {
+               /* via R5C832 */
+
+               pci_read_config_byte(fw_dev, 0xCB, &disable);
+               if (disable & 0x02) {
+                       printk(KERN_INFO DRIVER_NAME
+                              ": Controller already disabled. " \
+                               "Nothing to do.\n");
+                       return -ENODEV;
+               }
+
+               pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+               pci_write_config_byte(fw_dev, 0xCA, 0x57);
+               pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
+               pci_write_config_byte(fw_dev, 0xCA, write_enable);
+       }
+
+       printk(KERN_INFO DRIVER_NAME
+              ": Controller is now disabled.\n");
+
+       return 0;
+}
+
+static int ricoh_mmc_enable(struct pci_dev *fw_dev)
+{
+       u8 write_enable;
+       u8 write_target;
+       u8 disable;
+
+       if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
+               /* via RL5C476 */
+
+               pci_read_config_byte(fw_dev, 0x8E, &write_enable);
+               pci_write_config_byte(fw_dev, 0x8E, 0xAA);
+               pci_read_config_byte(fw_dev, 0x8D, &write_target);
+               pci_write_config_byte(fw_dev, 0x8D, 0xB7);
+               pci_read_config_byte(fw_dev, 0xB7, &disable);
+               pci_write_config_byte(fw_dev, 0xB7, disable & ~0x02);
+               pci_write_config_byte(fw_dev, 0x8E, write_enable);
+               pci_write_config_byte(fw_dev, 0x8D, write_target);
+       } else {
+               /* via R5C832 */
+
+               pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+               pci_read_config_byte(fw_dev, 0xCB, &disable);
+               pci_write_config_byte(fw_dev, 0xCA, 0x57);
+               pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
+               pci_write_config_byte(fw_dev, 0xCA, write_enable);
+       }
+
+       printk(KERN_INFO DRIVER_NAME
+              ": Controller is now re-enabled.\n");
+
+       return 0;
+}
+
 static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
                                     const struct pci_device_id *ent)
 {
        u8 rev;
+       u8 ctrlfound = 0;
 
        struct pci_dev *fw_dev = NULL;
 
@@ -58,34 +139,38 @@ static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
                pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
                (int)rev);
 
-       while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
+       while ((fw_dev =
+               pci_get_device(PCI_VENDOR_ID_RICOH,
+                       PCI_DEVICE_ID_RICOH_RL5C476, fw_dev))) {
                if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
                    pdev->bus == fw_dev->bus) {
-                       u8 write_enable;
-                       u8 disable;
-
-                       pci_read_config_byte(fw_dev, 0xCB, &disable);
-                       if (disable & 0x02) {
-                               printk(KERN_INFO DRIVER_NAME
-                                      ": Controller already disabled. Nothing to do.\n");
+                       if (ricoh_mmc_disable(fw_dev) != 0)
                                return -ENODEV;
-                       }
-
-                       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
-                       pci_write_config_byte(fw_dev, 0xCA, 0x57);
-                       pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
-                       pci_write_config_byte(fw_dev, 0xCA, write_enable);
 
                        pci_set_drvdata(pdev, fw_dev);
 
-                       printk(KERN_INFO DRIVER_NAME
-                              ": Controller is now disabled.\n");
-
+                       ++ctrlfound;
                        break;
                }
        }
 
-       if (pci_get_drvdata(pdev) == NULL) {
+       fw_dev = NULL;
+
+       while (!ctrlfound &&
+           (fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH,
+                                       PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
+               if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
+                   pdev->bus == fw_dev->bus) {
+                       if (ricoh_mmc_disable(fw_dev) != 0)
+                               return -ENODEV;
+
+                       pci_set_drvdata(pdev, fw_dev);
+
+                       ++ctrlfound;
+               }
+       }
+
+       if (!ctrlfound) {
                printk(KERN_WARNING DRIVER_NAME
                       ": Main firewire function not found. Cannot disable controller.\n");
                return -ENODEV;
@@ -96,30 +181,51 @@ static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
 
 static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
 {
-       u8 write_enable;
-       u8 disable;
        struct pci_dev *fw_dev = NULL;
 
        fw_dev = pci_get_drvdata(pdev);
        BUG_ON(fw_dev == NULL);
 
-       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
-       pci_read_config_byte(fw_dev, 0xCB, &disable);
-       pci_write_config_byte(fw_dev, 0xCA, 0x57);
-       pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
-       pci_write_config_byte(fw_dev, 0xCA, write_enable);
-
-       printk(KERN_INFO DRIVER_NAME
-              ": Controller is now re-enabled.\n");
+       ricoh_mmc_enable(fw_dev);
 
        pci_set_drvdata(pdev, NULL);
 }
 
+static int ricoh_mmc_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct pci_dev *fw_dev = NULL;
+
+       fw_dev = pci_get_drvdata(pdev);
+       BUG_ON(fw_dev == NULL);
+
+       printk(KERN_INFO DRIVER_NAME ": Suspending.\n");
+
+       ricoh_mmc_enable(fw_dev);
+
+       return 0;
+}
+
+static int ricoh_mmc_resume(struct pci_dev *pdev)
+{
+       struct pci_dev *fw_dev = NULL;
+
+       fw_dev = pci_get_drvdata(pdev);
+       BUG_ON(fw_dev == NULL);
+
+       printk(KERN_INFO DRIVER_NAME ": Resuming.\n");
+
+       ricoh_mmc_disable(fw_dev);
+
+       return 0;
+}
+
 static struct pci_driver ricoh_mmc_driver = {
        .name =         DRIVER_NAME,
        .id_table =     pci_ids,
        .probe =        ricoh_mmc_probe,
        .remove =       __devexit_p(ricoh_mmc_remove),
+       .suspend =      ricoh_mmc_suspend,
+       .resume =       ricoh_mmc_resume,
 };
 
 /*****************************************************************************\
index 785bbdcf4a58212e4371fbdc024a1ef1919ffc88..4b673aa2dc3cae7a7e588e1d306e59437848f54f 100644 (file)
 
 static unsigned int debug_quirks = 0;
 
+/* For multi controllers in one platform case */
+static u16 chip_index = 0;
+static spinlock_t index_lock;
+
 /*
  * Different quirks to handle when the hardware deviates from a strict
  * interpretation of the SDHCI specification.
@@ -1320,7 +1324,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
 
        DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq);
 
-       snprintf(host->slot_descr, 20, "sdhci:slot%d", slot);
+       snprintf(host->slot_descr, 20, "sdhc%d:slot%d", chip->index, slot);
 
        ret = pci_request_region(pdev, host->bar, host->slot_descr);
        if (ret)
@@ -1585,6 +1589,11 @@ static int __devinit sdhci_probe(struct pci_dev *pdev,
        chip->num_slots = slots;
        pci_set_drvdata(pdev, chip);
 
+       /* Add for multi controller case */
+       spin_lock(&index_lock);
+       chip->index = chip_index++;
+       spin_unlock(&index_lock);
+
        for (i = 0;i < slots;i++) {
                ret = sdhci_probe_slot(pdev, i);
                if (ret) {
@@ -1645,6 +1654,8 @@ static int __init sdhci_drv_init(void)
                ": Secure Digital Host Controller Interface driver\n");
        printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
 
+       spin_lock_init(&index_lock);
+
        return pci_register_driver(&sdhci_driver);
 }
 
index e4d77b038bfa5e9e12c976294c3dfecb5b86d156..d5a38f1b755ae1855cca9a0d40713fe3a7c77d65 100644 (file)
@@ -208,6 +208,7 @@ struct sdhci_chip {
 
        unsigned long           quirks;
 
+       int                     index;          /* Index for chip0, chip1 ...*/
        int                     num_slots;      /* Slots on controller */
        struct sdhci_host       *hosts[0];      /* Pointers to hosts */
 };
index 19e1594421a476cae392e4b5f2b6b48c6fb18bb3..8dab69657b1914eefb5ebb0eebf1420f2b78edba 100644 (file)
  *  Overview:
  *   This is a device driver for the NAND flash controller found on
  *   the AMD CS5535/CS5536 companion chipsets for the Geode processor.
+ *   mtd-id for command line partitioning is cs553x_nand_cs[0-3]
+ *   where 0-3 reflects the chip select for NAND.
  *
  */
 
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -244,6 +247,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
                goto out_ior;
        }
 
+       new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
+
        cs553x_mtd[cs] = new_mtd;
        goto out;
 
@@ -272,12 +277,21 @@ static int is_geode(void)
        return 0;
 }
 
+
+#ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+
 static int __init cs553x_init(void)
 {
        int err = -ENXIO;
        int i;
        uint64_t val;
 
+       int mtd_parts_nb = 0;
+       struct mtd_partition *mtd_parts = NULL;
+
        /* If the CPU isn't a Geode GX or LX, abort */
        if (!is_geode())
                return -ENXIO;
@@ -290,7 +304,7 @@ static int __init cs553x_init(void)
 
        /* If it doesn't have the NAND controller enabled, abort */
        rdmsrl(MSR_DIVIL_BALL_OPTS, val);
-       if (val & 1) {
+       if (val & PIN_OPT_IDE) {
                printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
                return -ENXIO;
        }
@@ -306,9 +320,19 @@ static int __init cs553x_init(void)
           do mtdconcat etc. if we want to. */
        for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
                if (cs553x_mtd[i]) {
-                       add_mtd_device(cs553x_mtd[i]);
 
                        /* If any devices registered, return success. Else the last error. */
+#ifdef CONFIG_MTD_PARTITIONS
+                       mtd_parts_nb = parse_mtd_partitions(cs553x_mtd[i], part_probes, &mtd_parts, 0);
+                       if (mtd_parts_nb > 0) {
+                               printk(KERN_NOTICE "Using command line partition definition\n");
+                               add_mtd_partitions(cs553x_mtd[i], mtd_parts, mtd_parts_nb);
+                       } else {
+                               add_mtd_device(cs553x_mtd[i]);
+                       }
+#else
+                       add_mtd_device(cs553x_mtd[i]);
+#endif
                        err = 0;
                }
        }
@@ -328,13 +352,14 @@ static void __exit cs553x_cleanup(void)
                void __iomem *mmio_base;
 
                if (!mtd)
-                       break;
+                       continue;
 
                this = cs553x_mtd[i]->priv;
                mmio_base = this->IO_ADDR_R;
 
                /* Release resources, unregister device */
                nand_release(cs553x_mtd[i]);
+               kfree(cs553x_mtd[i]->name);
                cs553x_mtd[i] = NULL;
 
                /* unmap physical address */
index d0549cb4fb23640a79d0221262b14742b5c1a468..5222345ddccf6525a13cd74fe801da1bde50a304 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/pagemap.h>
 #include <linux/statfs.h>
 #include <linux/seq_file.h>
+#include <linux/mount.h>
 #include "hostfs.h"
 #include "init.h"
 #include "kern.h"
index 683002fefa556875a84c7a0fd79c5249cba04557..f32fbde2175e84d6bb3bc59fdeacc9860f5837b3 100644 (file)
 
 /**
  * vfs_ioctl - call filesystem specific ioctl methods
- * @filp: [in]     open file to invoke ioctl method on
- * @cmd:  [in]     ioctl command to execute
- * @arg:  [in/out] command-specific argument for ioctl
+ * @filp:      open file to invoke ioctl method on
+ * @cmd:       ioctl command to execute
+ * @arg:       command-specific argument for ioctl
  *
  * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise
- * invokes filesystem specific ->ioctl method.  If neither method exists,
+ * invokes filesystem specific ->ioctl method.  If neither method exists,
  * returns -ENOTTY.
  *
  * Returns 0 on success, -errno on error.
index d5c9d1433781233e75871debc2bdebdfaf994b17..c7db0220fbd6dd74d7a8b97a4bae4fe801fdfc25 100644 (file)
@@ -39,6 +39,7 @@ typedef struct {
 typedef struct {
        unsigned long pgprot;
 } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)     ((x).pte)
 #define pmd_val(x)     ((&x)->pmd[0])
index a83492449130eb994a650ddb6b986e180aa298c6..d6a3eaf3b27eebfb53395cbeb9ac50cf492124a1 100644 (file)
@@ -31,6 +31,7 @@ typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long pmd[16]; } pmd_t;
 typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)     ((x).pte)
 #define pmd_val(x)     ((&x)->pmd[0])
index 6af480c7f2916b5583ba0feb6a1a25afbfebd4ec..1e82ebb7d6442d755266ac2b452f04964ab52b61 100644 (file)
@@ -31,6 +31,7 @@ typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long pmd[16]; } pmd_t;
 typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)     ((x).pte)
 #define pmd_val(x)     ((&x)->pmd[0])
index 661d8cd0883954cd0f0312586481bb915dee145e..74a539a9bd5952b246e4450cbb6136b22e59bb3c 100644 (file)
@@ -57,6 +57,7 @@ typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long pmd; } pmd_t;
 typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)      ((x).pte)
 #define pmd_val(x)      ((x).pmd)
index 9815951ec995d43f02ad87ce78f1c0fbf5ecdb2b..925d57b236aad811344b7b6a787c31c32c45eb2b 100644 (file)
@@ -51,10 +51,8 @@ extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
                                        gfp_t gfp_mask);
 int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);
 
-static inline struct mem_cgroup *mm_cgroup(const struct mm_struct *mm)
-{
-       return rcu_dereference(mm->mem_cgroup);
-}
+#define vm_match_cgroup(mm, cgroup)    \
+       ((cgroup) == rcu_dereference((mm)->mem_cgroup))
 
 extern int mem_cgroup_prepare_migration(struct page *page);
 extern void mem_cgroup_end_migration(struct page *page);
@@ -123,9 +121,9 @@ static inline int mem_cgroup_cache_charge(struct page *page,
        return 0;
 }
 
-static inline struct mem_cgroup *mm_cgroup(const struct mm_struct *mm)
+static inline int vm_match_cgroup(struct mm_struct *mm, struct mem_cgroup *mem)
 {
-       return NULL;
+       return 1;
 }
 
 static inline int task_in_mem_cgroup(struct task_struct *task,
diff --git a/include/linux/memstick.h b/include/linux/memstick.h
new file mode 100644 (file)
index 0000000..334d059
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *  Sony MemoryStick support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.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.
+ *
+ */
+
+#ifndef _MEMSTICK_H
+#define _MEMSTICK_H
+
+#include <linux/workqueue.h>
+#include <linux/scatterlist.h>
+#include <linux/device.h>
+
+/*** Hardware based structures ***/
+
+struct ms_status_register {
+       unsigned char reserved;
+       unsigned char interrupt;
+#define MEMSTICK_INT_CMDNAK             0x0001
+#define MEMSTICK_INT_BREQ               0x0020
+#define MEMSTICK_INT_ERR                0x0040
+#define MEMSTICK_INT_CED                0x0080
+
+       unsigned char status0;
+#define MEMSTICK_STATUS0_WP             0x0001
+#define MEMSTICK_STATUS0_SL             0x0002
+#define MEMSTICK_STATUS0_BF             0x0010
+#define MEMSTICK_STATUS0_BE             0x0020
+#define MEMSTICK_STATUS0_FB0            0x0040
+#define MEMSTICK_STATUS0_MB             0x0080
+
+       unsigned char status1;
+#define MEMSTICK_STATUS1_UCFG           0x0001
+#define MEMSTICK_STATUS1_FGER           0x0002
+#define MEMSTICK_STATUS1_UCEX           0x0004
+#define MEMSTICK_STATUS1_EXER           0x0008
+#define MEMSTICK_STATUS1_UCDT           0x0010
+#define MEMSTICK_STATUS1_DTER           0x0020
+#define MEMSTICK_STATUS1_FBI            0x0040
+#define MEMSTICK_STATUS1_MB             0x0080
+} __attribute__((packed));
+
+struct ms_id_register {
+       unsigned char type;
+       unsigned char reserved;
+       unsigned char category;
+       unsigned char class;
+} __attribute__((packed));
+
+struct ms_param_register {
+       unsigned char system;
+       unsigned char block_address_msb;
+       unsigned short block_address;
+       unsigned char cp;
+#define MEMSTICK_CP_BLOCK               0x0000
+#define MEMSTICK_CP_PAGE                0x0020
+#define MEMSTICK_CP_EXTRA               0x0040
+#define MEMSTICK_CP_OVERWRITE           0x0080
+
+       unsigned char page_address;
+} __attribute__((packed));
+
+struct ms_extra_data_register {
+       unsigned char  overwrite_flag;
+#define MEMSTICK_OVERWRITE_UPDATA       0x0010
+#define MEMSTICK_OVERWRITE_PAGE         0x0060
+#define MEMSTICK_OVERWRITE_BLOCK        0x0080
+
+       unsigned char  management_flag;
+#define MEMSTICK_MANAGEMENT_SYSTEM      0x0004
+#define MEMSTICK_MANAGEMENT_TRANS_TABLE 0x0008
+#define MEMSTICK_MANAGEMENT_COPY        0x0010
+#define MEMSTICK_MANAGEMENT_ACCESS      0x0020
+
+       unsigned short logical_address;
+} __attribute__((packed));
+
+struct ms_register {
+       struct ms_status_register     status;
+       struct ms_id_register         id;
+       unsigned char                 reserved[8];
+       struct ms_param_register      param;
+       struct ms_extra_data_register extra_data;
+} __attribute__((packed));
+
+struct mspro_param_register {
+       unsigned char  system;
+       unsigned short data_count;
+       unsigned int   data_address;
+       unsigned char  cmd_param;
+} __attribute__((packed));
+
+struct mspro_register {
+       struct ms_status_register    status;
+       struct ms_id_register        id;
+       unsigned char                reserved[8];
+       struct mspro_param_register  param;
+} __attribute__((packed));
+
+struct ms_register_addr {
+       unsigned char r_offset;
+       unsigned char r_length;
+       unsigned char w_offset;
+       unsigned char w_length;
+} __attribute__((packed));
+
+enum {
+       MS_TPC_READ_LONG_DATA   = 0x02,
+       MS_TPC_READ_SHORT_DATA  = 0x03,
+       MS_TPC_READ_REG         = 0x04,
+       MS_TPC_READ_IO_DATA     = 0x05, /* unverified */
+       MS_TPC_GET_INT          = 0x07,
+       MS_TPC_SET_RW_REG_ADRS  = 0x08,
+       MS_TPC_EX_SET_CMD       = 0x09,
+       MS_TPC_WRITE_IO_DATA    = 0x0a, /* unverified */
+       MS_TPC_WRITE_REG        = 0x0b,
+       MS_TPC_WRITE_SHORT_DATA = 0x0c,
+       MS_TPC_WRITE_LONG_DATA  = 0x0d,
+       MS_TPC_SET_CMD          = 0x0e
+};
+
+enum {
+       MS_CMD_BLOCK_END     = 0x33,
+       MS_CMD_RESET         = 0x3c,
+       MS_CMD_BLOCK_WRITE   = 0x55,
+       MS_CMD_SLEEP         = 0x5a,
+       MS_CMD_BLOCK_ERASE   = 0x99,
+       MS_CMD_BLOCK_READ    = 0xaa,
+       MS_CMD_CLEAR_BUF     = 0xc3,
+       MS_CMD_FLASH_STOP    = 0xcc,
+       MSPRO_CMD_FORMAT     = 0x10,
+       MSPRO_CMD_SLEEP      = 0x11,
+       MSPRO_CMD_READ_DATA  = 0x20,
+       MSPRO_CMD_WRITE_DATA = 0x21,
+       MSPRO_CMD_READ_ATRB  = 0x24,
+       MSPRO_CMD_STOP       = 0x25,
+       MSPRO_CMD_ERASE      = 0x26,
+       MSPRO_CMD_SET_IBA    = 0x46,
+       MSPRO_CMD_SET_IBD    = 0x47
+/*
+       MSPRO_CMD_RESET
+       MSPRO_CMD_WAKEUP
+       MSPRO_CMD_IN_IO_DATA
+       MSPRO_CMD_OUT_IO_DATA
+       MSPRO_CMD_READ_IO_ATRB
+       MSPRO_CMD_IN_IO_FIFO
+       MSPRO_CMD_OUT_IO_FIFO
+       MSPRO_CMD_IN_IOM
+       MSPRO_CMD_OUT_IOM
+*/
+};
+
+/*** Driver structures and functions ***/
+
+#define MEMSTICK_PART_SHIFT 3
+
+enum memstick_param { MEMSTICK_POWER = 1, MEMSTICK_INTERFACE };
+
+#define MEMSTICK_POWER_OFF 0
+#define MEMSTICK_POWER_ON  1
+
+#define MEMSTICK_SERIAL   0
+#define MEMSTICK_PARALLEL 1
+
+struct memstick_host;
+struct memstick_driver;
+
+#define MEMSTICK_MATCH_ALL            0x01
+
+#define MEMSTICK_TYPE_LEGACY          0xff
+#define MEMSTICK_TYPE_DUO             0x00
+#define MEMSTICK_TYPE_PRO             0x01
+
+#define MEMSTICK_CATEGORY_STORAGE     0xff
+#define MEMSTICK_CATEGORY_STORAGE_DUO 0x00
+
+#define MEMSTICK_CLASS_GENERIC        0xff
+#define MEMSTICK_CLASS_GENERIC_DUO    0x00
+
+
+struct memstick_device_id {
+       unsigned char match_flags;
+       unsigned char type;
+       unsigned char category;
+       unsigned char class;
+};
+
+struct memstick_request {
+       unsigned char tpc;
+       unsigned char data_dir:1,
+                     need_card_int:1,
+                     get_int_reg:1,
+                     io_type:2;
+#define               MEMSTICK_IO_NONE 0
+#define               MEMSTICK_IO_VAL  1
+#define               MEMSTICK_IO_SG   2
+
+       unsigned char int_reg;
+       int           error;
+       union {
+               struct scatterlist sg;
+               struct {
+                       unsigned char data_len;
+                       unsigned char data[15];
+               };
+       };
+};
+
+struct memstick_dev {
+       struct memstick_device_id id;
+       struct memstick_host     *host;
+       struct ms_register_addr  reg_addr;
+       struct completion        mrq_complete;
+       struct memstick_request  current_mrq;
+
+       /* 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);
+
+       struct device            dev;
+};
+
+struct memstick_host {
+       struct mutex        lock;
+       unsigned int        id;
+       unsigned int        caps;
+#define MEMSTICK_CAP_PARALLEL      1
+#define MEMSTICK_CAP_AUTO_GET_INT  2
+
+       struct work_struct  media_checker;
+       struct class_device cdev;
+
+       struct memstick_dev *card;
+       unsigned int        retries;
+
+       /* Notify the host that some requests are pending. */
+       void                (*request)(struct memstick_host *host);
+       /* Set host IO parameters (power, clock, etc).     */
+       void                (*set_param)(struct memstick_host *host,
+                                        enum memstick_param param,
+                                        int value);
+       unsigned long       private[0] ____cacheline_aligned;
+};
+
+struct memstick_driver {
+       struct memstick_device_id *id_table;
+       int                       (*probe)(struct memstick_dev *card);
+       void                      (*remove)(struct memstick_dev *card);
+       int                       (*suspend)(struct memstick_dev *card,
+                                            pm_message_t state);
+       int                       (*resume)(struct memstick_dev *card);
+
+       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);
+
+int memstick_add_host(struct memstick_host *host);
+void memstick_remove_host(struct memstick_host *host);
+void memstick_free_host(struct memstick_host *host);
+void memstick_detect_change(struct memstick_host *host);
+
+void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
+                         struct scatterlist *sg);
+void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
+                      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);
+
+static inline void *memstick_priv(struct memstick_host *host)
+{
+       return (void *)host->private;
+}
+
+static inline void *memstick_get_drvdata(struct memstick_dev *card)
+{
+       return dev_get_drvdata(&card->dev);
+}
+
+static inline void memstick_set_drvdata(struct memstick_dev *card, void *data)
+{
+       dev_set_drvdata(&card->dev, data);
+}
+
+#endif
index 7bf2d149d209615be247da2dadc4385f37636be5..6ec39ab27b4b48aaf31b02a19bf5a164217abcce 100644 (file)
@@ -42,11 +42,13 @@ static inline pgoff_t swp_offset(swp_entry_t entry)
        return entry.val & SWP_OFFSET_MASK(entry);
 }
 
+#ifdef CONFIG_MMU
 /* check whether a pte points to a swap entry */
 static inline int is_swap_pte(pte_t pte)
 {
        return !pte_none(pte) && !pte_present(pte) && !pte_file(pte);
 }
+#endif
 
 /*
  * Convert the arch-dependent pte representation of a swp_entry_t into an
index bba7712cadc749cc63513c583cce475dce899888..818ca1cf0b6d81d4f74faebcbdb6eef9123d90a7 100644 (file)
@@ -79,7 +79,9 @@ struct thermal_zone_device {
 };
 
 struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,
-                                       struct thermal_zone_device_ops *);
+                                                        struct
+                                                        thermal_zone_device_ops
+                                                        *);
 void thermal_zone_device_unregister(struct thermal_zone_device *);
 
 int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
@@ -87,8 +89,23 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
                                       struct thermal_cooling_device *);
 
+#ifdef CONFIG_THERMAL
 struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
-                                       struct thermal_cooling_device_ops *);
+                                                              struct
+                                                              thermal_cooling_device_ops
+                                                              *);
 void thermal_cooling_device_unregister(struct thermal_cooling_device *);
+#else
+static inline struct thermal_cooling_device
+*thermal_cooling_device_register(char *c, void *v,
+                                struct thermal_cooling_device_ops *t)
+{
+       return NULL;
+}
+static inline
+    void thermal_cooling_device_unregister(struct thermal_cooling_device *t)
+{
+};
+#endif
 
-#endif                         /* __THERMAL_H__ */
+#endif /* __THERMAL_H__ */
index 2096b76d0cee68efe8cd91571e69da3c36bffd2c..da76ed85f5958fb941cbb837a7f824b0981fd575 100644 (file)
@@ -72,6 +72,7 @@ enum {
 #define TIFM_FIFO_READY           0x00000001
 #define TIFM_FIFO_INT_SETALL      0x0000ffff
 #define TIFM_FIFO_INTMASK         0x00000005
+#define TIFM_FIFO_SIZE            0x00000200
 
 #define TIFM_DMA_RESET            0x00000002
 #define TIFM_DMA_TX               0x00008000
@@ -124,6 +125,8 @@ struct tifm_adapter {
 
        void                (*eject)(struct tifm_adapter *fm,
                                     struct tifm_dev *sock);
+       int                 (*has_ms_pif)(struct tifm_adapter *fm,
+                                         struct tifm_dev *sock);
 
        struct tifm_dev     *sockets[0];
 };
@@ -141,6 +144,7 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
 int tifm_register_driver(struct tifm_driver *drv);
 void tifm_unregister_driver(struct tifm_driver *drv);
 void tifm_eject(struct tifm_dev *sock);
+int tifm_has_ms_pif(struct tifm_dev *sock);
 int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
                int direction);
 void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
index 5c2c702af6172e34bced06b2d5693ce2d461b000..6bded84c20c88b7b4687a2954f31509ef1527c0d 100644 (file)
@@ -399,7 +399,7 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem)
        int ret;
 
        task_lock(task);
-       ret = task->mm && mm_cgroup(task->mm) == mem;
+       ret = task->mm && vm_match_cgroup(task->mm, mem);
        task_unlock(task);
        return ret;
 }
index a0e92a263d12eb734e8a8171e9d575e35dc0d768..8fd527c4e2bff8e45c929c42f01c60834c573ef9 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -321,7 +321,7 @@ static int page_referenced_anon(struct page *page,
                 * counting on behalf of references from different
                 * cgroups
                 */
-               if (mem_cont && (mm_cgroup(vma->vm_mm) != mem_cont))
+               if (mem_cont && !vm_match_cgroup(vma->vm_mm, mem_cont))
                        continue;
                referenced += page_referenced_one(page, vma, &mapcount);
                if (!mapcount)
@@ -382,7 +382,7 @@ static int page_referenced_file(struct page *page,
                 * counting on behalf of references from different
                 * cgroups
                 */
-               if (mem_cont && (mm_cgroup(vma->vm_mm) != mem_cont))
+               if (mem_cont && !vm_match_cgroup(vma->vm_mm, mem_cont))
                        continue;
                if ((vma->vm_flags & (VM_LOCKED|VM_MAYSHARE))
                                  == (VM_LOCKED|VM_MAYSHARE)) {