summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/00-INDEX8
-rw-r--r--Documentation/laptops/00-INDEX10
-rw-r--r--Documentation/laptops/acer-wmi.txt202
-rw-r--r--Documentation/laptops/sony-laptop.txt (renamed from Documentation/sony-laptop.txt)1
-rw-r--r--Documentation/laptops/sonypi.txt (renamed from Documentation/sonypi.txt)0
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt (renamed from Documentation/thinkpad-acpi.txt)0
-rw-r--r--MAINTAINERS10
-rw-r--r--arch/x86/Kconfig3
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/Makefile2
-rw-r--r--drivers/acpi/sbs.c2
-rw-r--r--drivers/acpi/sbshc.c6
-rw-r--r--drivers/block/ub.c2
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/cpuidle/cpuidle.c13
-rw-r--r--drivers/memstick/Kconfig26
-rw-r--r--drivers/memstick/Makefile11
-rw-r--r--drivers/memstick/core/Kconfig26
-rw-r--r--drivers/memstick/core/Makefile11
-rw-r--r--drivers/memstick/core/memstick.c614
-rw-r--r--drivers/memstick/core/mspro_block.c1351
-rw-r--r--drivers/memstick/host/Kconfig22
-rw-r--r--drivers/memstick/host/Makefile10
-rw-r--r--drivers/memstick/host/tifm_ms.c685
-rw-r--r--drivers/misc/Kconfig13
-rw-r--r--drivers/misc/acer-wmi.c6
-rw-r--r--drivers/misc/tifm_7xx1.c17
-rw-r--r--drivers/misc/tifm_core.c7
-rw-r--r--drivers/mmc/host/Kconfig8
-rw-r--r--drivers/mmc/host/at91_mci.c114
-rw-r--r--drivers/mmc/host/ricoh_mmc.c162
-rw-r--r--drivers/mmc/host/sdhci.c13
-rw-r--r--drivers/mmc/host/sdhci.h1
-rw-r--r--drivers/mtd/nand/cs553x_nand.c31
-rw-r--r--fs/hostfs/hostfs_kern.c1
-rw-r--r--fs/ioctl.c8
-rw-r--r--include/asm-blackfin/page.h1
-rw-r--r--include/asm-h8300/page.h1
-rw-r--r--include/asm-m68knommu/page.h1
-rw-r--r--include/asm-v850/page.h1
-rw-r--r--include/linux/memcontrol.h10
-rw-r--r--include/linux/memstick.h299
-rw-r--r--include/linux/swapops.h2
-rw-r--r--include/linux/thermal.h23
-rw-r--r--include/linux/tifm.h4
-rw-r--r--mm/memcontrol.c2
-rw-r--r--mm/rmap.c4
48 files changed, 3640 insertions, 109 deletions
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index 6e9c4050a41b..8d556707bb68 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -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
index 000000000000..729c2c062e10
--- /dev/null
+++ b/Documentation/laptops/00-INDEX
@@ -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
index 000000000000..b06696329cff
--- /dev/null
+++ b/Documentation/laptops/acer-wmi.txt
@@ -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.
diff --git a/Documentation/sony-laptop.txt b/Documentation/laptops/sony-laptop.txt
index 7a5c1a81905c..8b2bc1572d98 100644
--- a/Documentation/sony-laptop.txt
+++ b/Documentation/laptops/sony-laptop.txt
@@ -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.
-
diff --git a/Documentation/sonypi.txt b/Documentation/laptops/sonypi.txt
index 4857acfc50f1..4857acfc50f1 100644
--- a/Documentation/sonypi.txt
+++ b/Documentation/laptops/sonypi.txt
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 6c2477754a2a..6c2477754a2a 100644
--- a/Documentation/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
diff --git a/MAINTAINERS b/MAINTAINERS
index 0d6f5119a6da..c40f0ae96552 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 3bd42dadbc4d..aaed1a3b92d6 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -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
diff --git a/drivers/Kconfig b/drivers/Kconfig
index b86877bdc7ac..3a0e3549739f 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -80,6 +80,8 @@ source "drivers/usb/Kconfig"
source "drivers/mmc/Kconfig"
+source "drivers/memstick/Kconfig"
+
source "drivers/leds/Kconfig"
source "drivers/infiniband/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 30ba97ec5eb5..e5e394a7e6c0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -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/
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index f29812a86533..40b0fcae4c78 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -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
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index 1194105cc3ca..585ae3c9c8ea 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -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;
}
diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c
index ae9a90438e2f..a2cf3008ce6c 100644
--- a/drivers/acpi/sbshc.c
+++ b/drivers/acpi/sbshc.c
@@ -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;
}
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index a70c1c29a7aa..c452e2d355ee 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -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. */
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index f01ac9a07bf5..47c6be84fc84 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -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.
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 2c4b2d47973e..60f71e6345e3 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -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
index 000000000000..1093fdb07297
--- /dev/null
+++ b/drivers/memstick/Kconfig
@@ -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
index 000000000000..dc160fb43515
--- /dev/null
+++ b/drivers/memstick/Makefile
@@ -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
index 000000000000..95f1814b5368
--- /dev/null
+++ b/drivers/memstick/core/Kconfig
@@ -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
index 000000000000..8b2b5293877e
--- /dev/null
+++ b/drivers/memstick/core/Makefile
@@ -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
index 000000000000..bba467fe4bce
--- /dev/null
+++ b/drivers/memstick/core/memstick.c
@@ -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
index 000000000000..423ad8cf4bb9
--- /dev/null
+++ b/drivers/memstick/core/mspro_block.c
@@ -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
index 000000000000..c002fcc3c879
--- /dev/null
+++ b/drivers/memstick/host/Kconfig
@@ -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
index 000000000000..ee666380efa1
--- /dev/null
+++ b/drivers/memstick/host/Makefile
@@ -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
index 000000000000..f55b71a4337d
--- /dev/null
+++ b/drivers/memstick/host/tifm_ms.c
@@ -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);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c143a86c2ea6..1abc95ca9dfa 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -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.
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c
index a4d677504250..d7aea93081f2 100644
--- a/drivers/misc/acer-wmi.c
+++ b/drivers/misc/acer-wmi.c
@@ -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;
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c
index 54380da343a5..63a089b29545 100644
--- a/drivers/misc/tifm_7xx1.c
+++ b/drivers/misc/tifm_7xx1.c
@@ -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);
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c
index 97544052e768..82dc72a1484f 100644
--- a/drivers/misc/tifm_core.c
+++ b/drivers/misc/tifm_core.c
@@ -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)
{
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5fef6783c716..3b3cd0e74715 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -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
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index b1edcefdd4f9..21acecc9fe3a 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -70,10 +70,11 @@
#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);
diff --git a/drivers/mmc/host/ricoh_mmc.c b/drivers/mmc/host/ricoh_mmc.c
index 1e8704533bc5..a16d7609e4ee 100644
--- a/drivers/mmc/host/ricoh_mmc.c
+++ b/drivers/mmc/host/ricoh_mmc.c
@@ -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,
};
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 785bbdcf4a58..4b673aa2dc3c 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -30,6 +30,10 @@
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);
}
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index e4d77b038bfa..d5a38f1b755a 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -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 */
};
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index 19e1594421a4..8dab69657b19 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -13,9 +13,12 @@
* 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 */
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index d0549cb4fb23..5222345ddccf 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -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"
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 683002fefa55..f32fbde2175e 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -18,12 +18,12 @@
/**
* 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.
diff --git a/include/asm-blackfin/page.h b/include/asm-blackfin/page.h
index d5c9d1433781..c7db0220fbd6 100644
--- a/include/asm-blackfin/page.h
+++ b/include/asm-blackfin/page.h
@@ -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])
diff --git a/include/asm-h8300/page.h b/include/asm-h8300/page.h
index a83492449130..d6a3eaf3b27e 100644
--- a/include/asm-h8300/page.h
+++ b/include/asm-h8300/page.h
@@ -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])
diff --git a/include/asm-m68knommu/page.h b/include/asm-m68knommu/page.h
index 6af480c7f291..1e82ebb7d644 100644
--- a/include/asm-m68knommu/page.h
+++ b/include/asm-m68knommu/page.h
@@ -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])
diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h
index 661d8cd08839..74a539a9bd59 100644
--- a/include/asm-v850/page.h
+++ b/include/asm-v850/page.h
@@ -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)
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 9815951ec995..925d57b236aa 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -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
index 000000000000..334d059d6794
--- /dev/null
+++ b/include/linux/memstick.h
@@ -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
diff --git a/include/linux/swapops.h b/include/linux/swapops.h
index 7bf2d149d209..6ec39ab27b4b 100644
--- a/include/linux/swapops.h
+++ b/include/linux/swapops.h
@@ -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
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index bba7712cadc7..818ca1cf0b6d 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -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__ */
diff --git a/include/linux/tifm.h b/include/linux/tifm.h
index 2096b76d0cee..da76ed85f595 100644
--- a/include/linux/tifm.h
+++ b/include/linux/tifm.h
@@ -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,
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 5c2c702af617..6bded84c20c8 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -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;
}
diff --git a/mm/rmap.c b/mm/rmap.c
index a0e92a263d12..8fd527c4e2bf 100644
--- 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)) {
OpenPOWER on IntegriCloud