Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Sun, 10 Feb 2008 07:28:26 +0000 (23:28 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Sun, 10 Feb 2008 07:28:26 +0000 (23:28 -0800)
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6: (24 commits)
  [SPARC]: Add solaris/sunos binary support to feature removal schedule.
  [SPARC]: Merge asm-sparc{,64}/a.out.h
  [SPARC]: Merge asm-sparc{,64}/fb.h
  [SPARC]: Merge asm-sparc{,64}/errno.h
  [SPARC]: Merge asm-sparc{,64}/emergency-restart.h
  [SPARC]: Merge asm-sparc{,64}/div64.h
  [SPARC]: Merge asm-sparc{,64}/device.h
  [SPARC]: Merge asm-sparc{,64}/current.h
  [SPARC]: Merge asm-sparc{,64}/cputime.h
  [SPARC]: Merge asm-sparc{,64}/cache.h
  [SPARC]: Merge asm-sparc{,64}/byteorder.h
  [SPARC]: Merge asm-sparc{,64}/bugs.h
  [SPARC]: Merge asm-sparc{,64}/bug.h
  [SPARC]: Kill BSD errno translation table and header files.
  [SPARC]: Merge asm-sparc{,64}/bpp.h
  [SPARC]: Merge include/asm-sparc{,64}/auxvec.h
  [SPARC]: Merge include/asm-sparc{,64}/of_device.h
  [SPARC]: Merge include/asm-sparc{,64}/prom.h
  [SPARC]: Remove of_platform_device_create
  [SPARC64]: Add kretprobe support.
  ...

120 files changed:
Documentation/00-INDEX
Documentation/laptops/00-INDEX [new file with mode: 0644]
Documentation/laptops/acer-wmi.txt [new file with mode: 0644]
Documentation/laptops/sony-laptop.txt [new file with mode: 0644]
Documentation/laptops/sonypi.txt [new file with mode: 0644]
Documentation/laptops/thinkpad-acpi.txt [new file with mode: 0644]
Documentation/sony-laptop.txt [deleted file]
Documentation/sonypi.txt [deleted file]
Documentation/thinkpad-acpi.txt [deleted file]
MAINTAINERS
arch/alpha/Kconfig
arch/arm/Kconfig
arch/avr32/Kconfig
arch/blackfin/Kconfig
arch/cris/Kconfig
arch/frv/Kconfig
arch/h8300/Kconfig
arch/ia64/Kconfig
arch/m32r/Kconfig
arch/m68k/Kconfig
arch/m68knommu/Kconfig
arch/mips/Kconfig
arch/parisc/Kconfig
arch/powerpc/Kconfig
arch/ppc/Kconfig
arch/s390/defconfig
arch/s390/kernel/binfmt_elf32.c
arch/s390/kernel/compat_wrapper.S
arch/s390/kernel/process.c
arch/s390/kernel/setup.c
arch/s390/kernel/syscalls.S
arch/s390/kernel/traps.c
arch/s390/mm/fault.c
arch/s390/mm/init.c
arch/s390/mm/mmap.c
arch/s390/mm/pgtable.c
arch/s390/mm/vmem.c
arch/sh/Kconfig
arch/sparc/Kconfig
arch/sparc64/Kconfig
arch/v850/Kconfig
arch/x86/Kconfig
arch/x86/boot/.gitignore
arch/xtensa/Kconfig
drivers/Kconfig
drivers/Makefile
drivers/acpi/Makefile
drivers/acpi/sbs.c
drivers/acpi/sbshc.c
drivers/block/ub.c
drivers/char/Kconfig
drivers/cpuidle/cpuidle.c
drivers/ide/Kconfig
drivers/media/video/em28xx/Kconfig
drivers/memstick/Kconfig [new file with mode: 0644]
drivers/memstick/Makefile [new file with mode: 0644]
drivers/memstick/core/Kconfig [new file with mode: 0644]
drivers/memstick/core/Makefile [new file with mode: 0644]
drivers/memstick/core/memstick.c [new file with mode: 0644]
drivers/memstick/core/mspro_block.c [new file with mode: 0644]
drivers/memstick/host/Kconfig [new file with mode: 0644]
drivers/memstick/host/Makefile [new file with mode: 0644]
drivers/memstick/host/tifm_ms.c [new file with mode: 0644]
drivers/misc/Kconfig
drivers/misc/acer-wmi.c
drivers/misc/tifm_7xx1.c
drivers/misc/tifm_core.c
drivers/mmc/host/Kconfig
drivers/mmc/host/at91_mci.c
drivers/mmc/host/ricoh_mmc.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mtd/nand/cs553x_nand.c
drivers/net/wan/x25_asy.c
drivers/pcmcia/Kconfig
drivers/rtc/rtc-r9701.c
drivers/s390/char/sclp_vt220.c
drivers/s390/cio/qdio.c
drivers/s390/crypto/ap_bus.c
drivers/scsi/aic7xxx/Makefile
fs/ext4/inode.c
fs/ext4/mballoc.c
fs/ext4/migrate.c
fs/ext4/namei.c
fs/ext4/super.c
fs/hostfs/hostfs_kern.c
fs/ioctl.c
fs/jbd/commit.c
fs/jbd2/commit.c
fs/jbd2/recovery.c
include/asm-blackfin/page.h
include/asm-h8300/page.h
include/asm-m68knommu/page.h
include/asm-s390/a.out.h [deleted file]
include/asm-s390/bitops.h
include/asm-s390/elf.h
include/asm-s390/mmu.h
include/asm-s390/mmu_context.h
include/asm-s390/page.h
include/asm-s390/pgalloc.h
include/asm-s390/pgtable.h
include/asm-s390/processor.h
include/asm-s390/tlb.h
include/asm-s390/tlbflush.h
include/asm-s390/unistd.h
include/asm-v850/page.h
include/linux/ext4_fs.h
include/linux/memcontrol.h
include/linux/memstick.h [new file with mode: 0644]
include/linux/swapops.h
include/linux/thermal.h
include/linux/tifm.h
init/Makefile
kernel/.gitignore
mm/memcontrol.c
mm/rmap.c
scripts/.gitignore
scripts/Kbuild.include
scripts/mod/modpost.c
scripts/package/builddeb

index 6e9c405..8d55670 100644 (file)
@@ -227,6 +227,8 @@ kref.txt
        - docs on adding reference counters (krefs) to kernel objects.
 laptop-mode.txt
        - how to conserve battery power using laptop-mode.
+laptops/
+       - directory with laptop related info and laptop driver documentation.
 ldm.txt
        - a brief description of LDM (Windows Dynamic Disks).
 leds-class.txt
@@ -351,10 +353,6 @@ sh/
        - directory with info on porting Linux to a new architecture.
 smart-config.txt
        - description of the Smart Config makefile feature.
-sony-laptop.txt
-       - Sony Notebook Control Driver (SNC) Readme.
-sonypi.txt
-       - info on Linux Sony Programmable I/O Device support.
 sound/
        - directory with info on sound card support.
 sparc/
@@ -385,8 +383,6 @@ sysrq.txt
        - info on the magic SysRq key.
 telephony/
        - directory with info on telephony (e.g. voice over IP) support.
-thinkpad-acpi.txt
-       - information on the (IBM and Lenovo) ThinkPad ACPI Extras driver.
 time_interpolators.txt
        - info on time interpolators.
 tipar.txt
diff --git a/Documentation/laptops/00-INDEX b/Documentation/laptops/00-INDEX
new file mode 100644 (file)
index 0000000..729c2c0
--- /dev/null
@@ -0,0 +1,10 @@
+00-INDEX
+       - This file
+acer-wmi.txt
+       - information on the Acer Laptop WMI Extras driver.
+sony-laptop.txt
+       - Sony Notebook Control Driver (SNC) Readme.
+sonypi.txt
+       - info on Linux Sony Programmable I/O Device support.
+thinkpad-acpi.txt
+       - information on the (IBM and Lenovo) ThinkPad ACPI Extras driver.
diff --git a/Documentation/laptops/acer-wmi.txt b/Documentation/laptops/acer-wmi.txt
new file mode 100644 (file)
index 0000000..b066963
--- /dev/null
@@ -0,0 +1,202 @@
+Acer Laptop WMI Extras Driver
+http://code.google.com/p/aceracpi
+Version 0.1
+9th February 2008
+
+Copyright 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
+
+acer-wmi is a driver to allow you to control various parts of your Acer laptop
+hardware under Linux which are exposed via ACPI-WMI.
+
+This driver completely replaces the old out-of-tree acer_acpi, which I am
+currently maintaining for bug fixes only on pre-2.6.25 kernels. All development
+work is now focused solely on acer-wmi.
+
+Disclaimer
+**********
+
+Acer and Wistron have provided nothing towards the development acer_acpi or
+acer-wmi. All information we have has been through the efforts of the developers
+and the users to discover as much as possible about the hardware.
+
+As such, I do warn that this could break your hardware - this is extremely
+unlikely of course, but please bear this in mind.
+
+Background
+**********
+
+acer-wmi is derived from acer_acpi, originally developed by Mark
+Smith in 2005, then taken over by Carlos Corbacho in 2007, in order to activate
+the wireless LAN card under a 64-bit version of Linux, as acerhk[1] (the
+previous solution to the problem) relied on making 32 bit BIOS calls which are
+not possible in kernel space from a 64 bit OS.
+
+[1] acerhk: http://www.cakey.de/acerhk/
+
+Supported Hardware
+******************
+
+Please see the website for the current list of known working hardare:
+
+http://code.google.com/p/aceracpi/wiki/SupportedHardware
+
+If your laptop is not listed, or listed as unknown, and works with acer-wmi,
+please contact me with a copy of the DSDT.
+
+If your Acer laptop doesn't work with acer-wmi, I would also like to see the
+DSDT.
+
+To send me the DSDT, as root/sudo:
+
+cat /sys/firmware/acpi/DSDT > dsdt
+
+And send me the resulting 'dsdt' file.
+
+Usage
+*****
+
+On Acer laptops, acer-wmi should already be autoloaded based on DMI matching.
+For non-Acer laptops, until WMI based autoloading support is added, you will
+need to manually load acer-wmi.
+
+acer-wmi creates /sys/devices/platform/acer-wmi, and fills it with various
+files whose usage is detailed below, which enables you to control some of the
+following (varies between models):
+
+* the wireless LAN card radio
+* inbuilt Bluetooth adapter
+* inbuilt 3G card
+* mail LED of your laptop
+* brightness of the LCD panel
+
+Wireless
+********
+
+With regards to wireless, all acer-wmi does is enable the radio on the card. It
+is not responsible for the wireless LED - once the radio is enabled, this is
+down to the wireless driver for your card. So the behaviour of the wireless LED,
+once you enable the radio, will depend on your hardware and driver combination.
+
+e.g. With the BCM4318 on the Acer Aspire 5020 series:
+
+ndiswrapper: Light blinks on when transmitting
+bcm43xx/b43: Solid light, blinks off when transmitting
+
+Wireless radio control is unconditionally enabled - all Acer laptops that support
+acer-wmi come with built-in wireless. However, should you feel so inclined to
+ever wish to remove the card, or swap it out at some point, please get in touch
+with me, as we may well be able to gain some data on wireless card detection.
+
+To read the status of the wireless radio (0=off, 1=on):
+cat /sys/devices/platform/acer-wmi/wireless
+
+To enable the wireless radio:
+echo 1 > /sys/devices/platform/acer-wmi/wireless
+
+To disable the wireless radio:
+echo 0 > /sys/devices/platform/acer-wmi/wireless
+
+To set the state of the wireless radio when loading acer-wmi, pass:
+wireless=X (where X is 0 or 1)
+
+Bluetooth
+*********
+
+For bluetooth, this is an internal USB dongle, so once enabled, you will get
+a USB device connection event, and a new USB device appears. When you disable
+bluetooth, you get the reverse - a USB device disconnect event, followed by the
+device disappearing again.
+
+Bluetooth is autodetected by acer-wmi, so if you do not have a bluetooth module
+installed in your laptop, this file won't exist (please be aware that it is
+quite common for Acer not to fit bluetooth to their laptops - so just because
+you have a bluetooth button on the laptop, doesn't mean that bluetooth is
+installed).
+
+For the adventurously minded - if you want to buy an internal bluetooth
+module off the internet that is compatible with your laptop and fit it, then
+it will work just fine with acer-wmi.
+
+To read the status of the bluetooth module (0=off, 1=on):
+cat /sys/devices/platform/acer-wmi/wireless
+
+To enable the bluetooth module:
+echo 1 > /sys/devices/platform/acer-wmi/bluetooth
+
+To disable the bluetooth module:
+echo 0 > /sys/devices/platform/acer-wmi/bluetooth
+
+To set the state of the bluetooth module when loading acer-wmi, pass:
+bluetooth=X (where X is 0 or 1)
+
+3G
+**
+
+3G is currently not autodetected, so the 'threeg' file is always created under
+sysfs. So far, no-one in possession of an Acer laptop with 3G built-in appears to
+have tried Linux, or reported back, so we don't have any information on this.
+
+If you have an Acer laptop that does have a 3G card in, please contact me so we
+can properly detect these, and find out a bit more about them.
+
+To read the status of the 3G card (0=off, 1=on):
+cat /sys/devices/platform/acer-wmi/threeg
+
+To enable the 3G card:
+echo 1 > /sys/devices/platform/acer-wmi/threeg
+
+To disable the 3G card:
+echo 0 > /sys/devices/platform/acer-wmi/threeg
+
+To set the state of the 3G card when loading acer-wmi, pass:
+threeg=X (where X is 0 or 1)
+
+Mail LED
+********
+
+This can be found in most older Acer laptops supported by acer-wmi, and many
+newer ones - it is built into the 'mail' button, and blinks when active.
+
+On newer (WMID) laptops though, we have no way of detecting the mail LED. If
+your laptop identifies itself in dmesg as a WMID model, then please try loading
+acer_acpi with:
+
+force_series=2490
+
+This will use a known alternative method of reading/ writing the mail LED. If
+it works, please report back to me with the DMI data from your laptop so this
+can be added to acer-wmi.
+
+The LED is exposed through the LED subsystem, and can be found in:
+
+/sys/devices/platform/acer-wmi/leds/acer-mail:green/
+
+The mail LED is autodetected, so if you don't have one, the LED device won't
+be registered.
+
+If you have a mail LED that is not green, please report this to me.
+
+Backlight
+*********
+
+The backlight brightness control is available on all acer-wmi supported
+hardware. The maximum brightness level is usually 15, but on some newer laptops
+it's 10 (this is again autodetected).
+
+The backlight is exposed through the backlight subsystem, and can be found in:
+
+/sys/devices/platform/acer-wmi/backlight/acer-wmi/
+
+Credits
+*******
+
+Olaf Tauber, who did the real hard work when he developed acerhk
+http://www.informatik.hu-berlin.de/~tauber/acerhk
+All the authors of laptop ACPI modules in the kernel, whose work
+was an inspiration in the early days of acer_acpi
+Mathieu Segaud, who solved the problem with having to modprobe the driver
+twice in acer_acpi 0.2.
+Jim Ramsay, who added support for the WMID interface
+Mark Smith, who started the original acer_acpi
+
+And the many people who have used both acer_acpi and acer-wmi.
diff --git a/Documentation/laptops/sony-laptop.txt b/Documentation/laptops/sony-laptop.txt
new file mode 100644 (file)
index 0000000..8b2bc15
--- /dev/null
@@ -0,0 +1,116 @@
+Sony Notebook Control Driver (SNC) Readme
+-----------------------------------------
+       Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
+       Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
+
+This mini-driver drives the SNC and SPIC device present in the ACPI BIOS of the
+Sony Vaio laptops. This driver mixes both devices functions under the same
+(hopefully consistent) interface. This also means that the sonypi driver is
+obsoleted by sony-laptop now.
+
+Fn keys (hotkeys):
+------------------
+Some models report hotkeys through the SNC or SPIC devices, such events are
+reported both through the ACPI subsystem as acpi events and through the INPUT
+subsystem. See the logs of acpid or /proc/acpi/event and
+/proc/bus/input/devices to find out what those events are and which input
+devices are created by the driver.
+
+Backlight control:
+------------------
+If your laptop model supports it, you will find sysfs files in the
+/sys/class/backlight/sony/
+directory. You will be able to query and set the current screen
+brightness:
+       brightness              get/set screen brightness (an iteger
+                               between 0 and 7)
+       actual_brightness       reading from this file will query the HW
+                               to get real brightness value
+       max_brightness          the maximum brightness value
+
+
+Platform specific:
+------------------
+Loading the sony-laptop module will create a
+/sys/devices/platform/sony-laptop/
+directory populated with some files.
+
+You then read/write integer values from/to those files by using
+standard UNIX tools.
+
+The files are:
+       brightness_default      screen brightness which will be set
+                               when the laptop will be rebooted
+       cdpower                 power on/off the internal CD drive
+       audiopower              power on/off the internal sound card
+       lanpower                power on/off the internal ethernet card
+                               (only in debug mode)
+       bluetoothpower          power on/off the internal bluetooth device
+       fanspeed                get/set the fan speed
+
+Note that some files may be missing if they are not supported
+by your particular laptop model.
+
+Example usage:
+       # echo "1" > /sys/devices/platform/sony-laptop/brightness_default
+sets the lowest screen brightness for the next and later reboots,
+       # echo "8" > /sys/devices/platform/sony-laptop/brightness_default
+sets the highest screen brightness for the next and later reboots,
+       # cat /sys/devices/platform/sony-laptop/brightness_default
+retrieves the value.
+
+       # echo "0" > /sys/devices/platform/sony-laptop/audiopower
+powers off the sound card,
+       # echo "1" > /sys/devices/platform/sony-laptop/audiopower
+powers on the sound card.
+
+Development:
+------------
+
+If you want to help with the development of this driver (and
+you are not afraid of any side effects doing strange things with
+your ACPI BIOS could have on your laptop), load the driver and
+pass the option 'debug=1'.
+
+REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
+
+In your kernel logs you will find the list of all ACPI methods
+the SNC device has on your laptop. You can see the GCDP/GCDP methods
+used to pwer on/off the CD drive, but there are others.
+
+I HAVE NO IDEA WHAT THOSE METHODS DO.
+
+The sony-laptop driver creates, for some of those methods (the most
+current ones found on several Vaio models), an entry under
+/sys/devices/platform/sony-laptop, just like the 'cdpower' one.
+You can create other entries corresponding to your own laptop methods by
+further editing the source (see the 'sony_nc_values' table, and add a new
+entry to this table with your get/set method names using the
+SNC_HANDLE_NAMES macro).
+
+Your mission, should you accept it, is to try finding out what
+those entries are for, by reading/writing random values from/to those
+files and find out what is the impact on your laptop.
+
+Should you find anything interesting, please report it back to me,
+I will not disavow all knowledge of your actions :)
+
+See also http://www.linux.it/~malattia/wiki/index.php/Sony_drivers for other
+useful info.
+
+Bugs/Limitations:
+-----------------
+
+* This driver is not based on official documentation from Sony
+  (because there is none), so there is no guarantee this driver
+  will work at all, or do the right thing. Although this hasn't
+  happened to me, this driver could do very bad things to your
+  laptop, including permanent damage.
+
+* The sony-laptop and sonypi drivers do not interact at all. In the
+  future, sonypi could use sony-laptop to do (part of) its business.
+
+* spicctrl, which is the userspace tool used to communicate with the
+  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/laptops/sonypi.txt b/Documentation/laptops/sonypi.txt
new file mode 100644 (file)
index 0000000..4857acf
--- /dev/null
@@ -0,0 +1,152 @@
+Sony Programmable I/O Control Device Driver Readme
+--------------------------------------------------
+       Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
+       Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+       Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
+       Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
+       Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
+       Copyright (C) 2000 Andrew Tridgell <tridge@samba.org>
+
+This driver enables access to the Sony Programmable I/O Control Device which
+can be found in many Sony Vaio laptops. Some newer Sony laptops (seems to be
+limited to new FX series laptops, at least the FX501 and the FX702) lack a
+sonypi device and are not supported at all by this driver.
+
+It will give access (through a user space utility) to some events those laptops
+generate, like:
+       - jogdial events (the small wheel on the side of Vaios)
+       - capture button events (only on Vaio Picturebook series)
+       - Fn keys
+       - bluetooth button (only on C1VR model)
+       - programmable keys, back, help, zoom, thumbphrase buttons, etc.
+         (when available)
+
+Those events (see linux/sonypi.h) can be polled using the character device node
+/dev/sonypi (major 10, minor auto allocated or specified as a option).
+A simple daemon which translates the jogdial movements into mouse wheel events
+can be downloaded at: <http://popies.net/sonypi/>
+
+Another option to intercept the events is to get them directly through the
+input layer.
+
+This driver supports also some ioctl commands for setting the LCD screen
+brightness and querying the batteries charge information (some more
+commands may be added in the future).
+
+This driver can also be used to set the camera controls on Picturebook series
+(brightness, contrast etc), and is used by the video4linux driver for the
+Motion Eye camera.
+
+Please note that this driver was created by reverse engineering the Windows
+driver and the ACPI BIOS, because Sony doesn't agree to release any programming
+specs for its laptops. If someone convinces them to do so, drop me a note.
+
+Driver options:
+---------------
+
+Several options can be passed to the sonypi driver using the standard
+module argument syntax (<param>=<value> when passing the option to the
+module or sonypi.<param>=<value> on the kernel boot line when sonypi is
+statically linked into the kernel). Those options are:
+
+       minor:          minor number of the misc device /dev/sonypi,
+                       default is -1 (automatic allocation, see /proc/misc
+                       or kernel logs)
+
+       camera:         if you have a PictureBook series Vaio (with the
+                       integrated MotionEye camera), set this parameter to 1
+                       in order to let the driver access to the camera
+
+       fnkeyinit:      on some Vaios (C1VE, C1VR etc), the Fn key events don't
+                       get enabled unless you set this parameter to 1.
+                       Do not use this option unless it's actually necessary,
+                       some Vaio models don't deal well with this option.
+                       This option is available only if the kernel is
+                       compiled without ACPI support (since it conflicts
+                       with it and it shouldn't be required anyway if
+                       ACPI is already enabled).
+
+       verbose:        set to 1 to print unknown events received from the
+                       sonypi device.
+                       set to 2 to print all events received from the
+                       sonypi device.
+
+       compat:         uses some compatibility code for enabling the sonypi
+                       events. If the driver worked for you in the past
+                       (prior to version 1.5) and does not work anymore,
+                       add this option and report to the author.
+
+       mask:           event mask telling the driver what events will be
+                       reported to the user. This parameter is required for
+                       some Vaio models where the hardware reuses values
+                       used in other Vaio models (like the FX series who does
+                       not have a jogdial but reuses the jogdial events for
+                       programmable keys events). The default event mask is
+                       set to 0xffffffff, meaning that all possible events
+                       will be tried. You can use the following bits to
+                       construct your own event mask (from
+                       drivers/char/sonypi.h):
+                               SONYPI_JOGGER_MASK              0x0001
+                               SONYPI_CAPTURE_MASK             0x0002
+                               SONYPI_FNKEY_MASK               0x0004
+                               SONYPI_BLUETOOTH_MASK           0x0008
+                               SONYPI_PKEY_MASK                0x0010
+                               SONYPI_BACK_MASK                0x0020
+                               SONYPI_HELP_MASK                0x0040
+                               SONYPI_LID_MASK                 0x0080
+                               SONYPI_ZOOM_MASK                0x0100
+                               SONYPI_THUMBPHRASE_MASK         0x0200
+                               SONYPI_MEYE_MASK                0x0400
+                               SONYPI_MEMORYSTICK_MASK         0x0800
+                               SONYPI_BATTERY_MASK             0x1000
+                               SONYPI_WIRELESS_MASK            0x2000
+
+       useinput:       if set (which is the default) two input devices are
+                       created, one which interprets the jogdial events as
+                       mouse events, the other one which acts like a
+                       keyboard reporting the pressing of the special keys.
+
+Module use:
+-----------
+
+In order to automatically load the sonypi module on use, you can put those
+lines in your /etc/modprobe.conf file:
+
+       alias char-major-10-250 sonypi
+       options sonypi minor=250
+
+This supposes the use of minor 250 for the sonypi device:
+
+       # mknod /dev/sonypi c 10 250
+
+Bugs:
+-----
+
+       - several users reported that this driver disables the BIOS-managed
+         Fn-keys which put the laptop in sleeping state, or switch the
+         external monitor on/off. There is no workaround yet, since this
+         driver disables all APM management for those keys, by enabling the
+         ACPI management (and the ACPI core stuff is not complete yet). If
+         you have one of those laptops with working Fn keys and want to
+         continue to use them, don't use this driver.
+
+       - some users reported that the laptop speed is lower (dhrystone
+         tested) when using the driver with the fnkeyinit parameter. I cannot
+         reproduce it on my laptop and not all users have this problem.
+         This happens because the fnkeyinit parameter enables the ACPI
+         mode (but without additional ACPI control, like processor
+         speed handling etc). Use ACPI instead of APM if it works on your
+         laptop.
+
+       - sonypi lacks the ability to distinguish between certain key
+         events on some models.
+
+       - some models with the nvidia card (geforce go 6200 tc) uses a
+         different way to adjust the backlighting of the screen. There
+         is a userspace utility to adjust the brightness on those models,
+         which can be downloaded from
+         http://www.acc.umu.se/~erikw/program/smartdimmer-0.1.tar.bz2
+
+       - since all development was done by reverse engineering, there is
+         _absolutely no guarantee_ that this driver will not crash your
+         laptop. Permanently.
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
new file mode 100644 (file)
index 0000000..6c24777
--- /dev/null
@@ -0,0 +1,1365 @@
+                    ThinkPad ACPI Extras Driver
+
+                            Version 0.19
+                         January 06th, 2008
+
+               Borislav Deianov <borislav@users.sf.net>
+             Henrique de Moraes Holschuh <hmh@hmh.eng.br>
+                      http://ibm-acpi.sf.net/
+
+
+This is a Linux driver for the IBM and Lenovo ThinkPad laptops. It
+supports various features of these laptops which are accessible
+through the ACPI and ACPI EC framework, but not otherwise fully
+supported by the generic Linux ACPI drivers.
+
+This driver used to be named ibm-acpi until kernel 2.6.21 and release
+0.13-20070314.  It used to be in the drivers/acpi tree, but it was
+moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel
+2.6.22, and release 0.14.
+
+
+Status
+------
+
+The features currently supported are the following (see below for
+detailed description):
+
+       - Fn key combinations
+       - Bluetooth enable and disable
+       - video output switching, expansion control
+       - ThinkLight on and off
+       - limited docking and undocking
+       - UltraBay eject
+       - CMOS control
+       - LED control
+       - ACPI sounds
+       - temperature sensors
+       - Experimental: embedded controller register dump
+       - LCD brightness control
+       - Volume control
+       - Fan control and monitoring: fan speed, fan enable/disable
+       - Experimental: WAN enable and disable
+
+A compatibility table by model and feature is maintained on the web
+site, http://ibm-acpi.sf.net/. I appreciate any success or failure
+reports, especially if they add to or correct the compatibility table.
+Please include the following information in your report:
+
+       - ThinkPad model name
+       - a copy of your DSDT, from /proc/acpi/dsdt
+       - a copy of the output of dmidecode, with serial numbers
+         and UUIDs masked off
+       - which driver features work and which don't
+       - the observed behavior of non-working features
+
+Any other comments or patches are also more than welcome.
+
+
+Installation
+------------
+
+If you are compiling this driver as included in the Linux kernel
+sources, simply enable the CONFIG_THINKPAD_ACPI option, and optionally
+enable the CONFIG_THINKPAD_ACPI_BAY option if you want the
+thinkpad-specific bay functionality.
+
+Features
+--------
+
+The driver exports two different interfaces to userspace, which can be
+used to access the features it provides.  One is a legacy procfs-based
+interface, which will be removed at some time in the distant future.
+The other is a new sysfs-based interface which is not complete yet.
+
+The procfs interface creates the /proc/acpi/ibm directory.  There is a
+file under that directory for each feature it supports.  The procfs
+interface is mostly frozen, and will change very little if at all: it
+will not be extended to add any new functionality in the driver, instead
+all new functionality will be implemented on the sysfs interface.
+
+The sysfs interface tries to blend in the generic Linux sysfs subsystems
+and classes as much as possible.  Since some of these subsystems are not
+yet ready or stabilized, it is expected that this interface will change,
+and any and all userspace programs must deal with it.
+
+
+Notes about the sysfs interface:
+
+Unlike what was done with the procfs interface, correctness when talking
+to the sysfs interfaces will be enforced, as will correctness in the
+thinkpad-acpi's implementation of sysfs interfaces.
+
+Also, any bugs in the thinkpad-acpi sysfs driver code or in the
+thinkpad-acpi's implementation of the sysfs interfaces will be fixed for
+maximum correctness, even if that means changing an interface in
+non-compatible ways.  As these interfaces mature both in the kernel and
+in thinkpad-acpi, such changes should become quite rare.
+
+Applications interfacing to the thinkpad-acpi sysfs interfaces must
+follow all sysfs guidelines and correctly process all errors (the sysfs
+interface makes extensive use of errors).  File descriptors and open /
+close operations to the sysfs inodes must also be properly implemented.
+
+The version of thinkpad-acpi's sysfs interface is exported by the driver
+as a driver attribute (see below).
+
+Sysfs driver attributes are on the driver's sysfs attribute space,
+for 2.6.23 this is /sys/bus/platform/drivers/thinkpad_acpi/ and
+/sys/bus/platform/drivers/thinkpad_hwmon/
+
+Sysfs device attributes are on the thinkpad_acpi device sysfs attribute
+space, for 2.6.23 this is /sys/devices/platform/thinkpad_acpi/.
+
+Sysfs device attributes for the sensors and fan are on the
+thinkpad_hwmon device's sysfs attribute space, but you should locate it
+looking for a hwmon device with the name attribute of "thinkpad".
+
+Driver version
+--------------
+
+procfs: /proc/acpi/ibm/driver
+sysfs driver attribute: version
+
+The driver name and version. No commands can be written to this file.
+
+Sysfs interface version
+-----------------------
+
+sysfs driver attribute: interface_version
+
+Version of the thinkpad-acpi sysfs interface, as an unsigned long
+(output in hex format: 0xAAAABBCC), where:
+       AAAA - major revision
+       BB - minor revision
+       CC - bugfix revision
+
+The sysfs interface version changelog for the driver can be found at the
+end of this document.  Changes to the sysfs interface done by the kernel
+subsystems are not documented here, nor are they tracked by this
+attribute.
+
+Changes to the thinkpad-acpi sysfs interface are only considered
+non-experimental when they are submitted to Linux mainline, at which
+point the changes in this interface are documented and interface_version
+may be updated.  If you are using any thinkpad-acpi features not yet
+sent to mainline for merging, you do so on your own risk: these features
+may disappear, or be implemented in a different and incompatible way by
+the time they are merged in Linux mainline.
+
+Changes that are backwards-compatible by nature (e.g. the addition of
+attributes that do not change the way the other attributes work) do not
+always warrant an update of interface_version.  Therefore, one must
+expect that an attribute might not be there, and deal with it properly
+(an attribute not being there *is* a valid way to make it clear that a
+feature is not available in sysfs).
+
+Hot keys
+--------
+
+procfs: /proc/acpi/ibm/hotkey
+sysfs device attribute: hotkey_*
+
+In a ThinkPad, the ACPI HKEY handler is responsible for comunicating
+some important events and also keyboard hot key presses to the operating
+system.  Enabling the hotkey functionality of thinkpad-acpi signals the
+firmware that such a driver is present, and modifies how the ThinkPad
+firmware will behave in many situations.
+
+The driver enables the hot key feature automatically when loaded.  The
+feature can later be disabled and enabled back at runtime.  The driver
+will also restore the hot key feature to its previous state and mask
+when it is unloaded.
+
+When the hotkey feature is enabled and the hot key mask is set (see
+below), the driver will report HKEY events in the following format:
+
+       ibm/hotkey HKEY 00000080 0000xxxx
+
+Some of these events refer to hot key presses, but not all.
+
+The driver will generate events over the input layer for hot keys and
+radio switches, and over the ACPI netlink layer for other events.  The
+input layer support accepts the standard IOCTLs to remap the keycodes
+assigned to each hot key.
+
+The hot key bit mask allows some control over which hot keys generate
+events.  If a key is "masked" (bit set to 0 in the mask), the firmware
+will handle it.  If it is "unmasked", it signals the firmware that
+thinkpad-acpi would prefer to handle it, if the firmware would be so
+kind to allow it (and it often doesn't!).
+
+Not all bits in the mask can be modified.  Not all bits that can be
+modified do anything.  Not all hot keys can be individually controlled
+by the mask.  Some models do not support the mask at all, and in those
+models, hot keys cannot be controlled individually.  The behaviour of
+the mask is, therefore, higly dependent on the ThinkPad model.
+
+Note that unmasking some keys prevents their default behavior.  For
+example, if Fn+F5 is unmasked, that key will no longer enable/disable
+Bluetooth by itself.
+
+Note also that not all Fn key combinations are supported through ACPI.
+For example, on the X40, the brightness, volume and "Access IBM" buttons
+do not generate ACPI events even with this driver.  They *can* be used
+through the "ThinkPad Buttons" utility, see http://www.nongnu.org/tpb/
+
+procfs notes:
+
+The following commands can be written to the /proc/acpi/ibm/hotkey file:
+
+       echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
+       echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
+       echo 0xffffffff > /proc/acpi/ibm/hotkey -- enable all hot keys
+       echo 0 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
+       ... any other 8-hex-digit mask ...
+       echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
+
+The procfs interface does not support NVRAM polling control.  So as to
+maintain maximum bug-to-bug compatibility, it does not report any masks,
+nor does it allow one to manipulate the hot key mask when the firmware
+does not support masks at all, even if NVRAM polling is in use.
+
+sysfs notes:
+
+       hotkey_bios_enabled:
+               Returns the status of the hot keys feature when
+               thinkpad-acpi was loaded.  Upon module unload, the hot
+               key feature status will be restored to this value.
+
+               0: hot keys were disabled
+               1: hot keys were enabled (unusual)
+
+       hotkey_bios_mask:
+               Returns the hot keys mask when thinkpad-acpi was loaded.
+               Upon module unload, the hot keys mask will be restored
+               to this value.
+
+       hotkey_enable:
+               Enables/disables the hot keys feature in the ACPI
+               firmware, and reports current status of the hot keys
+               feature.  Has no effect on the NVRAM hot key polling
+               functionality.
+
+               0: disables the hot keys feature / feature disabled
+               1: enables the hot keys feature / feature enabled
+
+       hotkey_mask:
+               bit mask to enable driver-handling (and depending on
+               the firmware, ACPI event generation) for each hot key
+               (see above).  Returns the current status of the hot keys
+               mask, and allows one to modify it.
+
+               Note: when NVRAM polling is active, the firmware mask
+               will be different from the value returned by
+               hotkey_mask.  The driver will retain enabled bits for
+               hotkeys that are under NVRAM polling even if the
+               firmware refuses them, and will not set these bits on
+               the firmware hot key mask.
+
+       hotkey_all_mask:
+               bit mask that should enable event reporting for all
+               supported hot keys, when echoed to hotkey_mask above.
+               Unless you know which events need to be handled
+               passively (because the firmware *will* handle them
+               anyway), do *not* use hotkey_all_mask.  Use
+               hotkey_recommended_mask, instead. You have been warned.
+
+       hotkey_recommended_mask:
+               bit mask that should enable event reporting for all
+               supported hot keys, except those which are always
+               handled by the firmware anyway.  Echo it to
+               hotkey_mask above, to use.
+
+       hotkey_source_mask:
+               bit mask that selects which hot keys will the driver
+               poll the NVRAM for.  This is auto-detected by the driver
+               based on the capabilities reported by the ACPI firmware,
+               but it can be overridden at runtime.
+
+               Hot keys whose bits are set in both hotkey_source_mask
+               and also on hotkey_mask are polled for in NVRAM.  Only a
+               few hot keys are available through CMOS NVRAM polling.
+
+               Warning: when in NVRAM mode, the volume up/down/mute
+               keys are synthesized according to changes in the mixer,
+               so you have to use volume up or volume down to unmute,
+               as per the ThinkPad volume mixer user interface.  When
+               in ACPI event mode, volume up/down/mute are reported as
+               separate events, but this behaviour may be corrected in
+               future releases of this driver, in which case the
+               ThinkPad volume mixer user interface semanthics will be
+               enforced.
+
+       hotkey_poll_freq:
+               frequency in Hz for hot key polling. It must be between
+               0 and 25 Hz.  Polling is only carried out when strictly
+               needed.
+
+               Setting hotkey_poll_freq to zero disables polling, and
+               will cause hot key presses that require NVRAM polling
+               to never be reported.
+
+               Setting hotkey_poll_freq too low will cause repeated
+               pressings of the same hot key to be misreported as a
+               single key press, or to not even be detected at all.
+               The recommended polling frequency is 10Hz.
+
+       hotkey_radio_sw:
+               if the ThinkPad has a hardware radio switch, this
+               attribute will read 0 if the switch is in the "radios
+               disabled" postition, and 1 if the switch is in the
+               "radios enabled" position.
+
+               This attribute has poll()/select() support.
+
+       hotkey_report_mode:
+               Returns the state of the procfs ACPI event report mode
+               filter for hot keys.  If it is set to 1 (the default),
+               all hot key presses are reported both through the input
+               layer and also as ACPI events through procfs (but not
+               through netlink).  If it is set to 2, hot key presses
+               are reported only through the input layer.
+
+               This attribute is read-only in kernels 2.6.23 or later,
+               and read-write on earlier kernels.
+
+               May return -EPERM (write access locked out by module
+               parameter) or -EACCES (read-only).
+
+       wakeup_reason:
+               Set to 1 if the system is waking up because the user
+               requested a bay ejection.  Set to 2 if the system is
+               waking up because the user requested the system to
+               undock.  Set to zero for normal wake-ups or wake-ups
+               due to unknown reasons.
+
+               This attribute has poll()/select() support.
+
+       wakeup_hotunplug_complete:
+               Set to 1 if the system was waken up because of an
+               undock or bay ejection request, and that request
+               was sucessfully completed.  At this point, it might
+               be useful to send the system back to sleep, at the
+               user's choice.  Refer to HKEY events 0x4003 and
+               0x3003, below.
+
+               This attribute has poll()/select() support.
+
+input layer notes:
+
+A Hot key is mapped to a single input layer EV_KEY event, possibly
+followed by an EV_MSC MSC_SCAN event that shall contain that key's scan
+code.  An EV_SYN event will always be generated to mark the end of the
+event block.
+
+Do not use the EV_MSC MSC_SCAN events to process keys.  They are to be
+used as a helper to remap keys, only.  They are particularly useful when
+remapping KEY_UNKNOWN keys.
+
+The events are available in an input device, with the following id:
+
+       Bus:            BUS_HOST
+       vendor:         0x1014 (PCI_VENDOR_ID_IBM)  or
+                       0x17aa (PCI_VENDOR_ID_LENOVO)
+       product:        0x5054 ("TP")
+       version:        0x4101
+
+The version will have its LSB incremented if the keymap changes in a
+backwards-compatible way.  The MSB shall always be 0x41 for this input
+device.  If the MSB is not 0x41, do not use the device as described in
+this section, as it is either something else (e.g. another input device
+exported by a thinkpad driver, such as HDAPS) or its functionality has
+been changed in a non-backwards compatible way.
+
+Adding other event types for other functionalities shall be considered a
+backwards-compatible change for this input device.
+
+Thinkpad-acpi Hot Key event map (version 0x4101):
+
+ACPI   Scan
+event  code    Key             Notes
+
+0x1001 0x00    FN+F1           -
+0x1002 0x01    FN+F2           IBM: battery (rare)
+                               Lenovo: Screen lock
+
+0x1003 0x02    FN+F3           Many IBM models always report
+                               this hot key, even with hot keys
+                               disabled or with Fn+F3 masked
+                               off
+                               IBM: screen lock
+                               Lenovo: battery
+
+0x1004 0x03    FN+F4           Sleep button (ACPI sleep button
+                               semanthics, i.e. sleep-to-RAM).
+                               It is always generate some kind
+                               of event, either the hot key
+                               event or a ACPI sleep button
+                               event. The firmware may
+                               refuse to generate further FN+F4
+                               key presses until a S3 or S4 ACPI
+                               sleep cycle is performed or some
+                               time passes.
+
+0x1005 0x04    FN+F5           Radio.  Enables/disables
+                               the internal BlueTooth hardware
+                               and W-WAN card if left in control
+                               of the firmware.  Does not affect
+                               the WLAN card.
+                               Should be used to turn on/off all
+                               radios (bluetooth+W-WAN+WLAN),
+                               really.
+
+0x1006 0x05    FN+F6           -
+
+0x1007 0x06    FN+F7           Video output cycle.
+                               Do you feel lucky today?
+
+0x1008 0x07    FN+F8           IBM: toggle screen expand
+                               Lenovo: configure ultranav
+
+0x1009 0x08    FN+F9           -
+       ..      ..              ..
+0x100B 0x0A    FN+F11          -
+
+0x100C 0x0B    FN+F12          Sleep to disk.  You are always
+                               supposed to handle it yourself,
+                               either through the ACPI event,
+                               or through a hotkey event.
+                               The firmware may refuse to
+                               generate further FN+F4 key
+                               press events until a S3 or S4
+                               ACPI sleep cycle is performed,
+                               or some time passes.
+
+0x100D 0x0C    FN+BACKSPACE    -
+0x100E 0x0D    FN+INSERT       -
+0x100F 0x0E    FN+DELETE       -
+
+0x1010 0x0F    FN+HOME         Brightness up.  This key is
+                               always handled by the firmware
+                               in IBM ThinkPads, even when
+                               unmasked.  Just leave it alone.
+                               For Lenovo ThinkPads with a new
+                               BIOS, it has to be handled either
+                               by the ACPI OSI, or by userspace.
+0x1011 0x10    FN+END          Brightness down.  See brightness
+                               up for details.
+
+0x1012 0x11    FN+PGUP         Thinklight toggle.  This key is
+                               always handled by the firmware,
+                               even when unmasked.
+
+0x1013 0x12    FN+PGDOWN       -
+
+0x1014 0x13    FN+SPACE        Zoom key
+
+0x1015 0x14    VOLUME UP       Internal mixer volume up. This
+                               key is always handled by the
+                               firmware, even when unmasked.
+                               NOTE: Lenovo seems to be changing
+                               this.
+0x1016 0x15    VOLUME DOWN     Internal mixer volume up. This
+                               key is always handled by the
+                               firmware, even when unmasked.
+                               NOTE: Lenovo seems to be changing
+                               this.
+0x1017 0x16    MUTE            Mute internal mixer. This
+                               key is always handled by the
+                               firmware, even when unmasked.
+
+0x1018 0x17    THINKPAD        Thinkpad/Access IBM/Lenovo key
+
+0x1019 0x18    unknown
+..     ..      ..
+0x1020 0x1F    unknown
+
+The ThinkPad firmware does not allow one to differentiate when most hot
+keys are pressed or released (either that, or we don't know how to, yet).
+For these keys, the driver generates a set of events for a key press and
+immediately issues the same set of events for a key release.  It is
+unknown by the driver if the ThinkPad firmware triggered these events on
+hot key press or release, but the firmware will do it for either one, not
+both.
+
+If a key is mapped to KEY_RESERVED, it generates no input events at all.
+If a key is mapped to KEY_UNKNOWN, it generates an input event that
+includes an scan code.  If a key is mapped to anything else, it will
+generate input device EV_KEY events.
+
+Non hot-key ACPI HKEY event map:
+0x5001         Lid closed
+0x5002         Lid opened
+0x7000         Radio Switch may have changed state
+
+The above events are not propagated by the driver, except for legacy
+compatibility purposes when hotkey_report_mode is set to 1.
+
+0x2304         System is waking up from suspend to undock
+0x2305         System is waking up from suspend to eject bay
+0x2404         System is waking up from hibernation to undock
+0x2405         System is waking up from hibernation to eject bay
+
+The above events are never propagated by the driver.
+
+0x3003         Bay ejection (see 0x2x05) complete, can sleep again
+0x4003         Undocked (see 0x2x04), can sleep again
+0x5009         Tablet swivel: switched to tablet mode
+0x500A         Tablet swivel: switched to normal mode
+0x500B         Tablet pen insterted into its storage bay
+0x500C         Tablet pen removed from its storage bay
+0x5010         Brightness level changed (newer Lenovo BIOSes)
+
+The above events are propagated by the driver.
+
+Compatibility notes:
+
+ibm-acpi and thinkpad-acpi 0.15 (mainline kernels before 2.6.23) never
+supported the input layer, and sent events over the procfs ACPI event
+interface.
+
+To avoid sending duplicate events over the input layer and the ACPI
+event interface, thinkpad-acpi 0.16 implements a module parameter
+(hotkey_report_mode), and also a sysfs device attribute with the same
+name.
+
+Make no mistake here: userspace is expected to switch to using the input
+layer interface of thinkpad-acpi, together with the ACPI netlink event
+interface in kernels 2.6.23 and later, or with the ACPI procfs event
+interface in kernels 2.6.22 and earlier.
+
+If no hotkey_report_mode module parameter is specified (or it is set to
+zero), the driver defaults to mode 1 (see below), and on kernels 2.6.22
+and earlier, also allows one to change the hotkey_report_mode through
+sysfs.  In kernels 2.6.23 and later, where the netlink ACPI event
+interface is available, hotkey_report_mode cannot be changed through
+sysfs (it is read-only).
+
+If the hotkey_report_mode module parameter is set to 1 or 2, it cannot
+be changed later through sysfs (any writes will return -EPERM to signal
+that hotkey_report_mode was locked.  On 2.6.23 and later, where
+hotkey_report_mode cannot be changed at all, writes will return -EACES).
+
+hotkey_report_mode set to 1 makes the driver export through the procfs
+ACPI event interface all hot key presses (which are *also* sent to the
+input layer).  This is a legacy compatibility behaviour, and it is also
+the default mode of operation for the driver.
+
+hotkey_report_mode set to 2 makes the driver filter out the hot key
+presses from the procfs ACPI event interface, so these events will only
+be sent through the input layer.  Userspace that has been updated to use
+the thinkpad-acpi input layer interface should set hotkey_report_mode to
+2.
+
+Hot key press events are never sent to the ACPI netlink event interface.
+Really up-to-date userspace under kernel 2.6.23 and later is to use the
+netlink interface and the input layer interface, and don't bother at all
+with hotkey_report_mode.
+
+
+Bluetooth
+---------
+
+procfs: /proc/acpi/ibm/bluetooth
+sysfs device attribute: bluetooth_enable
+
+This feature shows the presence and current state of a ThinkPad
+Bluetooth device in the internal ThinkPad CDC slot.
+
+Procfs notes:
+
+If Bluetooth is installed, the following commands can be used:
+
+       echo enable > /proc/acpi/ibm/bluetooth
+       echo disable > /proc/acpi/ibm/bluetooth
+
+Sysfs notes:
+
+       If the Bluetooth CDC card is installed, it can be enabled /
+       disabled through the "bluetooth_enable" thinkpad-acpi device
+       attribute, and its current status can also be queried.
+
+       enable:
+               0: disables Bluetooth / Bluetooth is disabled
+               1: enables Bluetooth / Bluetooth is enabled.
+
+       Note: this interface will be probably be superseeded by the
+       generic rfkill class, so it is NOT to be considered stable yet.
+
+Video output control -- /proc/acpi/ibm/video
+--------------------------------------------
+
+This feature allows control over the devices used for video output -
+LCD, CRT or DVI (if available). The following commands are available:
+
+       echo lcd_enable > /proc/acpi/ibm/video
+       echo lcd_disable > /proc/acpi/ibm/video
+       echo crt_enable > /proc/acpi/ibm/video
+       echo crt_disable > /proc/acpi/ibm/video
+       echo dvi_enable > /proc/acpi/ibm/video
+       echo dvi_disable > /proc/acpi/ibm/video
+       echo auto_enable > /proc/acpi/ibm/video
+       echo auto_disable > /proc/acpi/ibm/video
+       echo expand_toggle > /proc/acpi/ibm/video
+       echo video_switch > /proc/acpi/ibm/video
+
+Each video output device can be enabled or disabled individually.
+Reading /proc/acpi/ibm/video shows the status of each device.
+
+Automatic video switching can be enabled or disabled.  When automatic
+video switching is enabled, certain events (e.g. opening the lid,
+docking or undocking) cause the video output device to change
+automatically. While this can be useful, it also causes flickering
+and, on the X40, video corruption. By disabling automatic switching,
+the flickering or video corruption can be avoided.
+
+The video_switch command cycles through the available video outputs
+(it simulates the behavior of Fn-F7).
+
+Video expansion can be toggled through this feature. This controls
+whether the display is expanded to fill the entire LCD screen when a
+mode with less than full resolution is used. Note that the current
+video expansion status cannot be determined through this feature.
+
+Note that on many models (particularly those using Radeon graphics
+chips) the X driver configures the video card in a way which prevents
+Fn-F7 from working. This also disables the video output switching
+features of this driver, as it uses the same ACPI methods as
+Fn-F7. Video switching on the console should still work.
+
+UPDATE: There's now a patch for the X.org Radeon driver which
+addresses this issue. Some people are reporting success with the patch
+while others are still having problems. For more information:
+
+https://bugs.freedesktop.org/show_bug.cgi?id=2000
+
+ThinkLight control -- /proc/acpi/ibm/light
+------------------------------------------
+
+The current status of the ThinkLight can be found in this file. A few
+models which do not make the status available will show it as
+"unknown". The available commands are:
+
+       echo on  > /proc/acpi/ibm/light
+       echo off > /proc/acpi/ibm/light
+
+Docking / undocking -- /proc/acpi/ibm/dock
+------------------------------------------
+
+Docking and undocking (e.g. with the X4 UltraBase) requires some
+actions to be taken by the operating system to safely make or break
+the electrical connections with the dock.
+
+The docking feature of this driver generates the following ACPI events:
+
+       ibm/dock GDCK 00000003 00000001 -- eject request
+       ibm/dock GDCK 00000003 00000002 -- undocked
+       ibm/dock GDCK 00000000 00000003 -- docked
+
+NOTE: These events will only be generated if the laptop was docked
+when originally booted. This is due to the current lack of support for
+hot plugging of devices in the Linux ACPI framework. If the laptop was
+booted while not in the dock, the following message is shown in the
+logs:
+
+       Mar 17 01:42:34 aero kernel: thinkpad_acpi: dock device not present
+
+In this case, no dock-related events are generated but the dock and
+undock commands described below still work. They can be executed
+manually or triggered by Fn key combinations (see the example acpid
+configuration files included in the driver tarball package available
+on the web site).
+
+When the eject request button on the dock is pressed, the first event
+above is generated. The handler for this event should issue the
+following command:
+
+       echo undock > /proc/acpi/ibm/dock
+
+After the LED on the dock goes off, it is safe to eject the laptop.
+Note: if you pressed this key by mistake, go ahead and eject the
+laptop, then dock it back in. Otherwise, the dock may not function as
+expected.
+
+When the laptop is docked, the third event above is generated. The
+handler for this event should issue the following command to fully
+enable the dock:
+
+       echo dock > /proc/acpi/ibm/dock
+
+The contents of the /proc/acpi/ibm/dock file shows the current status
+of the dock, as provided by the ACPI framework.
+
+The docking support in this driver does not take care of enabling or
+disabling any other devices you may have attached to the dock. For
+example, a CD drive plugged into the UltraBase needs to be disabled or
+enabled separately. See the provided example acpid configuration files
+for how this can be accomplished.
+
+There is no support yet for PCI devices that may be attached to a
+docking station, e.g. in the ThinkPad Dock II. The driver currently
+does not recognize, enable or disable such devices. This means that
+the only docking stations currently supported are the X-series
+UltraBase docks and "dumb" port replicators like the Mini Dock (the
+latter don't need any ACPI support, actually).
+
+UltraBay eject -- /proc/acpi/ibm/bay
+------------------------------------
+
+Inserting or ejecting an UltraBay device requires some actions to be
+taken by the operating system to safely make or break the electrical
+connections with the device.
+
+This feature generates the following ACPI events:
+
+       ibm/bay MSTR 00000003 00000000 -- eject request
+       ibm/bay MSTR 00000001 00000000 -- eject lever inserted
+
+NOTE: These events will only be generated if the UltraBay was present
+when the laptop was originally booted (on the X series, the UltraBay
+is in the dock, so it may not be present if the laptop was undocked).
+This is due to the current lack of support for hot plugging of devices
+in the Linux ACPI framework. If the laptop was booted without the
+UltraBay, the following message is shown in the logs:
+
+       Mar 17 01:42:34 aero kernel: thinkpad_acpi: bay device not present
+
+In this case, no bay-related events are generated but the eject
+command described below still works. It can be executed manually or
+triggered by a hot key combination.
+
+Sliding the eject lever generates the first event shown above. The
+handler for this event should take whatever actions are necessary to
+shut down the device in the UltraBay (e.g. call idectl), then issue
+the following command:
+
+       echo eject > /proc/acpi/ibm/bay
+
+After the LED on the UltraBay goes off, it is safe to pull out the
+device.
+
+When the eject lever is inserted, the second event above is
+generated. The handler for this event should take whatever actions are
+necessary to enable the UltraBay device (e.g. call idectl).
+
+The contents of the /proc/acpi/ibm/bay file shows the current status
+of the UltraBay, as provided by the ACPI framework.
+
+EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use
+this feature, you need to supply the experimental=1 parameter when
+loading the module):
+
+These models do not have a button near the UltraBay device to request
+a hot eject but rather require the laptop to be put to sleep
+(suspend-to-ram) before the bay device is ejected or inserted).
+The sequence of steps to eject the device is as follows:
+
+       echo eject > /proc/acpi/ibm/bay
+       put the ThinkPad to sleep
+       remove the drive
+       resume from sleep
+       cat /proc/acpi/ibm/bay should show that the drive was removed
+
+On the A3x, both the UltraBay 2000 and UltraBay Plus devices are
+supported. Use "eject2" instead of "eject" for the second bay.
+
+Note: the UltraBay eject support on the 600e/x, A22p and A3x is
+EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
+
+CMOS control
+------------
+
+procfs: /proc/acpi/ibm/cmos
+sysfs device attribute: cmos_command
+
+This feature is mostly used internally by the ACPI firmware to keep the legacy
+CMOS NVRAM bits in sync with the current machine state, and to record this
+state so that the ThinkPad will retain such settings across reboots.
+
+Some of these commands actually perform actions in some ThinkPad models, but
+this is expected to disappear more and more in newer models.  As an example, in
+a T43 and in a X40, commands 12 and 13 still control the ThinkLight state for
+real, but commands 0 to 2 don't control the mixer anymore (they have been
+phased out) and just update the NVRAM.
+
+The range of valid cmos command numbers is 0 to 21, but not all have an
+effect and the behavior varies from model to model.  Here is the behavior
+on the X40 (tpb is the ThinkPad Buttons utility):
+
+       0 - Related to "Volume down" key press
+       1 - Related to "Volume up" key press
+       2 - Related to "Mute on" key press
+       3 - Related to "Access IBM" key press
+       4 - Related to "LCD brightness up" key pess
+       5 - Related to "LCD brightness down" key press
+       11 - Related to "toggle screen expansion" key press/function
+       12 - Related to "ThinkLight on"
+       13 - Related to "ThinkLight off"
+       14 - Related to "ThinkLight" key press (toggle thinklight)
+
+The cmos command interface is prone to firmware split-brain problems, as
+in newer ThinkPads it is just a compatibility layer.  Do not use it, it is
+exported just as a debug tool.
+
+LED control -- /proc/acpi/ibm/led
+---------------------------------
+
+Some of the LED indicators can be controlled through this feature. The
+available commands are:
+
+       echo '<led number> on' >/proc/acpi/ibm/led
+       echo '<led number> off' >/proc/acpi/ibm/led
+       echo '<led number> blink' >/proc/acpi/ibm/led
+
+The <led number> range is 0 to 7. The set of LEDs that can be
+controlled varies from model to model. Here is the mapping on the X40:
+
+       0 - power
+       1 - battery (orange)
+       2 - battery (green)
+       3 - UltraBase
+       4 - UltraBay
+       7 - standby
+
+All of the above can be turned on and off and can be made to blink.
+
+ACPI sounds -- /proc/acpi/ibm/beep
+----------------------------------
+
+The BEEP method is used internally by the ACPI firmware to provide
+audible alerts in various situations. This feature allows the same
+sounds to be triggered manually.
+
+The commands are non-negative integer numbers:
+
+       echo <number> >/proc/acpi/ibm/beep
+
+The valid <number> range is 0 to 17. Not all numbers trigger sounds
+and the sounds vary from model to model. Here is the behavior on the
+X40:
+
+       0 - stop a sound in progress (but use 17 to stop 16)
+       2 - two beeps, pause, third beep ("low battery")
+       3 - single beep
+       4 - high, followed by low-pitched beep ("unable")
+       5 - single beep
+       6 - very high, followed by high-pitched beep ("AC/DC")
+       7 - high-pitched beep
+       9 - three short beeps
+       10 - very long beep
+       12 - low-pitched beep
+       15 - three high-pitched beeps repeating constantly, stop with 0
+       16 - one medium-pitched beep repeating constantly, stop with 17
+       17 - stop 16
+
+Temperature sensors
+-------------------
+
+procfs: /proc/acpi/ibm/thermal
+sysfs device attributes: (hwmon "thinkpad") temp*_input
+
+Most ThinkPads include six or more separate temperature sensors but only
+expose the CPU temperature through the standard ACPI methods.  This
+feature shows readings from up to eight different sensors on older
+ThinkPads, and up to sixteen different sensors on newer ThinkPads.
+
+For example, on the X40, a typical output may be:
+temperatures:   42 42 45 41 36 -128 33 -128
+
+On the T43/p, a typical output may be:
+temperatures:   48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128
+
+The mapping of thermal sensors to physical locations varies depending on
+system-board model (and thus, on ThinkPad model).
+
+http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that
+tries to track down these locations for various models.
+
+Most (newer?) models seem to follow this pattern:
+
+1:  CPU
+2:  (depends on model)
+3:  (depends on model)
+4:  GPU
+5:  Main battery: main sensor
+6:  Bay battery: main sensor
+7:  Main battery: secondary sensor
+8:  Bay battery: secondary sensor
+9-15: (depends on model)
+
+For the R51 (source: Thomas Gruber):
+2:  Mini-PCI
+3:  Internal HDD
+
+For the T43, T43/p (source: Shmidoax/Thinkwiki.org)
+http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p
+2:  System board, left side (near PCMCIA slot), reported as HDAPS temp
+3:  PCMCIA slot
+9:  MCH (northbridge) to DRAM Bus
+10: Clock-generator, mini-pci card and ICH (southbridge), under Mini-PCI
+    card, under touchpad
+11: Power regulator, underside of system board, below F2 key
+
+The A31 has a very atypical layout for the thermal sensors
+(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31)
+1:  CPU
+2:  Main Battery: main sensor
+3:  Power Converter
+4:  Bay Battery: main sensor
+5:  MCH (northbridge)
+6:  PCMCIA/ambient
+7:  Main Battery: secondary sensor
+8:  Bay Battery: secondary sensor
+
+
+Procfs notes:
+       Readings from sensors that are not available return -128.
+       No commands can be written to this file.
+
+Sysfs notes:
+       Sensors that are not available return the ENXIO error.  This
+       status may change at runtime, as there are hotplug thermal
+       sensors, like those inside the batteries and docks.
+
+       thinkpad-acpi thermal sensors are reported through the hwmon
+       subsystem, and follow all of the hwmon guidelines at
+       Documentation/hwmon.
+
+
+EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump
+------------------------------------------------------------------------
+
+This feature is marked EXPERIMENTAL because the implementation
+directly accesses hardware registers and may not work as expected. USE
+WITH CAUTION! To use this feature, you need to supply the
+experimental=1 parameter when loading the module.
+
+This feature dumps the values of 256 embedded controller
+registers. Values which have changed since the last time the registers
+were dumped are marked with a star:
+
+[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
+EC       +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
+EC 0x00:  a7  47  87  01  fe  96  00  08  01  00  cb  00  00  00  40  00
+EC 0x10:  00  00  ff  ff  f4  3c  87  09  01  ff  42  01  ff  ff  0d  00
+EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
+EC 0x30:  01  07  1a  00  30  04  00  00 *85  00  00  10  00  50  00  00
+EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
+EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03 *bc *02 *bc
+EC 0x60: *02 *bc *02  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0x70:  00  00  00  00  00  12  30  40 *24 *26 *2c *27 *20  80 *1f  80
+EC 0x80:  00  00  00  06 *37 *0e  03  00  00  00  0e  07  00  00  00  00
+EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xa0: *ff  09  ff  09  ff  ff *64  00 *00 *00 *a2  41 *ff *ff *e0  00
+EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
+EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
+
+This feature can be used to determine the register holding the fan
+speed on some models. To do that, do the following:
+
+       - make sure the battery is fully charged
+       - make sure the fan is running
+       - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so
+
+The first step makes sure various charging-related values don't
+vary. The second ensures that the fan-related values do vary, since
+the fan speed fluctuates a bit. The third will (hopefully) mark the
+fan register with a star:
+
+[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
+EC       +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
+EC 0x00:  a7  47  87  01  fe  96  00  08  01  00  cb  00  00  00  40  00
+EC 0x10:  00  00  ff  ff  f4  3c  87  09  01  ff  42  01  ff  ff  0d  00
+EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
+EC 0x30:  01  07  1a  00  30  04  00  00  85  00  00  10  00  50  00  00
+EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
+EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03  bc  02  bc
+EC 0x60:  02  bc  02  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0x70:  00  00  00  00  00  12  30  40  24  27  2c  27  21  80  1f  80
+EC 0x80:  00  00  00  06 *be  0d  03  00  00  00  0e  07  00  00  00  00
+EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xa0:  ff  09  ff  09  ff  ff  64  00  00  00  a2  41  ff  ff  e0  00
+EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
+EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
+EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
+
+Another set of values that varies often is the temperature
+readings. Since temperatures don't change vary fast, you can take
+several quick dumps to eliminate them.
+
+You can use a similar method to figure out the meaning of other
+embedded controller registers - e.g. make sure nothing else changes
+except the charging or discharging battery to determine which
+registers contain the current battery capacity, etc. If you experiment
+with this, do send me your results (including some complete dumps with
+a description of the conditions when they were taken.)
+
+LCD brightness control
+----------------------
+
+procfs: /proc/acpi/ibm/brightness
+sysfs backlight device "thinkpad_screen"
+
+This feature allows software control of the LCD brightness on ThinkPad
+models which don't have a hardware brightness slider.
+
+It has some limitations: the LCD backlight cannot be actually turned on or
+off by this interface, and in many ThinkPad models, the "dim while on
+battery" functionality will be enabled by the BIOS when this interface is
+used, and cannot be controlled.
+
+On IBM (and some of the earlier Lenovo) ThinkPads, the backlight control
+has eight brightness levels, ranging from 0 to 7.  Some of the levels
+may not be distinct.  Later Lenovo models that implement the ACPI
+display backlight brightness control methods have 16 levels, ranging
+from 0 to 15.
+
+There are two interfaces to the firmware for direct brightness control,
+EC and CMOS.  To select which one should be used, use the
+brightness_mode module parameter: brightness_mode=1 selects EC mode,
+brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC
+and CMOS.  The driver tries to autodetect which interface to use.
+
+When display backlight brightness controls are available through the
+standard ACPI interface, it is best to use it instead of this direct
+ThinkPad-specific interface.  The driver will disable its native
+backlight brightness control interface if it detects that the standard
+ACPI interface is available in the ThinkPad.
+
+The brightness_enable module parameter can be used to control whether
+the LCD brightness control feature will be enabled when available.
+brightness_enable=0 forces it to be disabled.  brightness_enable=1
+forces it to be enabled when available, even if the standard ACPI
+interface is also available.
+
+Procfs notes:
+
+       The available commands are:
+
+       echo up   >/proc/acpi/ibm/brightness
+       echo down >/proc/acpi/ibm/brightness
+       echo 'level <level>' >/proc/acpi/ibm/brightness
+
+Sysfs notes:
+
+The interface is implemented through the backlight sysfs class, which is
+poorly documented at this time.
+
+Locate the thinkpad_screen device under /sys/class/backlight, and inside
+it there will be the following attributes:
+
+       max_brightness:
+               Reads the maximum brightness the hardware can be set to.
+               The minimum is always zero.
+
+       actual_brightness:
+               Reads what brightness the screen is set to at this instant.
+
+       brightness:
+               Writes request the driver to change brightness to the
+               given value.  Reads will tell you what brightness the
+               driver is trying to set the display to when "power" is set
+               to zero and the display has not been dimmed by a kernel
+               power management event.
+
+       power:
+               power management mode, where 0 is "display on", and 1 to 3
+               will dim the display backlight to brightness level 0
+               because thinkpad-acpi cannot really turn the backlight
+               off.  Kernel power management events can temporarily
+               increase the current power management level, i.e. they can
+               dim the display.
+
+
+Volume control -- /proc/acpi/ibm/volume
+---------------------------------------
+
+This feature allows volume control on ThinkPad models which don't have
+a hardware volume knob. The available commands are:
+
+       echo up   >/proc/acpi/ibm/volume
+       echo down >/proc/acpi/ibm/volume
+       echo mute >/proc/acpi/ibm/volume
+       echo 'level <level>' >/proc/acpi/ibm/volume
+
+The <level> number range is 0 to 15 although not all of them may be
+distinct. The unmute the volume after the mute command, use either the
+up or down command (the level command will not unmute the volume).
+The current volume level and mute state is shown in the file.
+
+Fan control and monitoring: fan speed, fan enable/disable
+---------------------------------------------------------
+
+procfs: /proc/acpi/ibm/fan
+sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1,
+                         pwm1_enable
+sysfs hwmon driver attributes: fan_watchdog
+
+NOTE NOTE NOTE: fan control operations are disabled by default for
+safety reasons.  To enable them, the module parameter "fan_control=1"
+must be given to thinkpad-acpi.
+
+This feature attempts to show the current fan speed, control mode and
+other fan data that might be available.  The speed is read directly
+from the hardware registers of the embedded controller.  This is known
+to work on later R, T, X and Z series ThinkPads but may show a bogus
+value on other models.
+
+Fan levels:
+
+Most ThinkPad fans work in "levels" at the firmware interface.  Level 0
+stops the fan.  The higher the level, the higher the fan speed, although
+adjacent levels often map to the same fan speed.  7 is the highest
+level, where the fan reaches the maximum recommended speed.
+
+Level "auto" means the EC changes the fan level according to some
+internal algorithm, usually based on readings from the thermal sensors.
+
+There is also a "full-speed" level, also known as "disengaged" level.
+In this level, the EC disables the speed-locked closed-loop fan control,
+and drives the fan as fast as it can go, which might exceed hardware
+limits, so use this level with caution.
+
+The fan usually ramps up or down slowly from one speed to another, and
+it is normal for the EC to take several seconds to react to fan
+commands.  The full-speed level may take up to two minutes to ramp up to
+maximum speed, and in some ThinkPads, the tachometer readings go stale
+while the EC is transitioning to the full-speed level.
+
+WARNING WARNING WARNING: do not leave the fan disabled unless you are
+monitoring all of the temperature sensor readings and you are ready to
+enable it if necessary to avoid overheating.
+
+An enabled fan in level "auto" may stop spinning if the EC decides the
+ThinkPad is cool enough and doesn't need the extra airflow.  This is
+normal, and the EC will spin the fan up if the various thermal readings
+rise too much.
+
+On the X40, this seems to depend on the CPU and HDD temperatures.
+Specifically, the fan is turned on when either the CPU temperature
+climbs to 56 degrees or the HDD temperature climbs to 46 degrees.  The
+fan is turned off when the CPU temperature drops to 49 degrees and the
+HDD temperature drops to 41 degrees.  These thresholds cannot
+currently be controlled.
+
+The ThinkPad's ACPI DSDT code will reprogram the fan on its own when
+certain conditions are met.  It will override any fan programming done
+through thinkpad-acpi.
+
+The thinkpad-acpi kernel driver can be programmed to revert the fan
+level to a safe setting if userspace does not issue one of the procfs
+fan commands: "enable", "disable", "level" or "watchdog", or if there
+are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is
+set to 1, manual mode) within a configurable amount of time of up to
+120 seconds.  This functionality is called fan safety watchdog.
+
+Note that the watchdog timer stops after it enables the fan.  It will be
+rearmed again automatically (using the same interval) when one of the
+above mentioned fan commands is received.  The fan watchdog is,
+therefore, not suitable to protect against fan mode changes made through
+means other than the "enable", "disable", and "level" procfs fan
+commands, or the hwmon fan control sysfs interface.
+
+Procfs notes:
+
+The fan may be enabled or disabled with the following commands:
+
+       echo enable  >/proc/acpi/ibm/fan
+       echo disable >/proc/acpi/ibm/fan
+
+Placing a fan on level 0 is the same as disabling it.  Enabling a fan
+will try to place it in a safe level if it is too slow or disabled.
+
+The fan level can be controlled with the command:
+
+       echo 'level <level>' > /proc/acpi/ibm/fan
+
+Where <level> is an integer from 0 to 7, or one of the words "auto" or
+"full-speed" (without the quotes).  Not all ThinkPads support the "auto"
+and "full-speed" levels.  The driver accepts "disengaged" as an alias for
+"full-speed", and reports it as "disengaged" for backwards
+compatibility.
+
+On the X31 and X40 (and ONLY on those models), the fan speed can be
+controlled to a certain degree.  Once the fan is running, it can be
+forced to run faster or slower with the following command:
+
+       echo 'speed <speed>' > /proc/acpi/ibm/fan
+
+The sustainable range of fan speeds on the X40 appears to be from about
+3700 to about 7350. Values outside this range either do not have any
+effect or the fan speed eventually settles somewhere in that range.  The
+fan cannot be stopped or started with this command.  This functionality
+is incomplete, and not available through the sysfs interface.
+
+To program the safety watchdog, use the "watchdog" command.
+
+       echo 'watchdog <interval in seconds>' > /proc/acpi/ibm/fan
+
+If you want to disable the watchdog, use 0 as the interval.
+
+Sysfs notes:
+
+The sysfs interface follows the hwmon subsystem guidelines for the most
+part, and the exception is the fan safety watchdog.
+
+Writes to any of the sysfs attributes may return the EINVAL error if
+that operation is not supported in a given ThinkPad or if the parameter
+is out-of-bounds, and EPERM if it is forbidden.  They may also return
+EINTR (interrupted system call), and EIO (I/O error while trying to talk
+to the firmware).
+
+Features not yet implemented by the driver return ENOSYS.
+
+hwmon device attribute pwm1_enable:
+       0: PWM offline (fan is set to full-speed mode)
+       1: Manual PWM control (use pwm1 to set fan level)
+       2: Hardware PWM control (EC "auto" mode)
+       3: reserved (Software PWM control, not implemented yet)
+
+       Modes 0 and 2 are not supported by all ThinkPads, and the
+       driver is not always able to detect this.  If it does know a
+       mode is unsupported, it will return -EINVAL.
+
+hwmon device attribute pwm1:
+       Fan level, scaled from the firmware values of 0-7 to the hwmon
+       scale of 0-255.  0 means fan stopped, 255 means highest normal
+       speed (level 7).
+
+       This attribute only commands the fan if pmw1_enable is set to 1
+       (manual PWM control).
+
+hwmon device attribute fan1_input:
+       Fan tachometer reading, in RPM.  May go stale on certain
+       ThinkPads while the EC transitions the PWM to offline mode,
+       which can take up to two minutes.  May return rubbish on older
+       ThinkPads.
+
+hwmon driver attribute fan_watchdog:
+       Fan safety watchdog timer interval, in seconds.  Minimum is
+       1 second, maximum is 120 seconds.  0 disables the watchdog.
+
+To stop the fan: set pwm1 to zero, and pwm1_enable to 1.
+
+To start the fan in a safe mode: set pwm1_enable to 2.  If that fails
+with EINVAL, try to set pwm1_enable to 1 and pwm1 to at least 128 (255
+would be the safest choice, though).
+
+
+EXPERIMENTAL: WAN
+-----------------
+
+procfs: /proc/acpi/ibm/wan
+sysfs device attribute: wwan_enable
+
+This feature is marked EXPERIMENTAL because the implementation
+directly accesses hardware registers and may not work as expected. USE
+WITH CAUTION! To use this feature, you need to supply the
+experimental=1 parameter when loading the module.
+
+This feature shows the presence and current state of a W-WAN (Sierra
+Wireless EV-DO) device.
+
+It was tested on a Lenovo Thinkpad X60. It should probably work on other
+Thinkpad models which come with this module installed.
+
+Procfs notes:
+
+If the W-WAN card is installed, the following commands can be used:
+
+       echo enable > /proc/acpi/ibm/wan
+       echo disable > /proc/acpi/ibm/wan
+
+Sysfs notes:
+
+       If the W-WAN card is installed, it can be enabled /
+       disabled through the "wwan_enable" thinkpad-acpi device
+       attribute, and its current status can also be queried.
+
+       enable:
+               0: disables WWAN card / WWAN card is disabled
+               1: enables WWAN card / WWAN card is enabled.
+
+       Note: this interface will be probably be superseeded by the
+       generic rfkill class, so it is NOT to be considered stable yet.
+
+Multiple Commands, Module Parameters
+------------------------------------
+
+Multiple commands can be written to the proc files in one shot by
+separating them with commas, for example:
+
+       echo enable,0xffff > /proc/acpi/ibm/hotkey
+       echo lcd_disable,crt_enable > /proc/acpi/ibm/video
+
+Commands can also be specified when loading the thinkpad-acpi module,
+for example:
+
+       modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable
+
+Enabling debugging output
+-------------------------
+
+The module takes a debug parameter which can be used to selectively
+enable various classes of debugging output, for example:
+
+        modprobe ibm_acpi debug=0xffff
+
+will enable all debugging output classes.  It takes a bitmask, so
+to enable more than one output class, just add their values.
+
+       Debug bitmask           Description
+       0x0001                  Initialization and probing
+       0x0002                  Removal
+
+There is also a kernel build option to enable more debugging
+information, which may be necessary to debug driver problems.
+
+The level of debugging information output by the driver can be changed
+at runtime through sysfs, using the driver attribute debug_level.  The
+attribute takes the same bitmask as the debug module parameter above.
+
+Force loading of module
+-----------------------
+
+If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify
+the module parameter force_load=1.  Regardless of whether this works or
+not, please contact ibm-acpi-devel@lists.sourceforge.net with a report.
+
+
+Sysfs interface changelog:
+
+0x000100:      Initial sysfs support, as a single platform driver and
+               device.
+0x000200:      Hot key support for 32 hot keys, and radio slider switch
+               support.
+0x010000:      Hot keys are now handled by default over the input
+               layer, the radio switch generates input event EV_RADIO,
+               and the driver enables hot key handling by default in
+               the firmware.
+
+0x020000:      ABI fix: added a separate hwmon platform device and
+               driver, which must be located by name (thinkpad)
+               and the hwmon class for libsensors4 (lm-sensors 3)
+               compatibility.  Moved all hwmon attributes to this
+               new platform device.
+
+0x020100:      Marker for thinkpad-acpi with hot key NVRAM polling
+               support.  If you must, use it to know you should not
+               start an userspace NVRAM poller (allows to detect when
+               NVRAM is compiled out by the user because it is
+               unneeded/undesired in the first place).
+0x020101:      Marker for thinkpad-acpi with hot key NVRAM polling
+               and proper hotkey_mask semanthics (version 8 of the
+               NVRAM polling patch).  Some development snapshots of
+               0.18 had an earlier version that did strange things
+               to hotkey_mask.
+
+0x020200:      Add poll()/select() support to the following attributes:
+               hotkey_radio_sw, wakeup_hotunplug_complete, wakeup_reason
diff --git a/Documentation/sony-laptop.txt b/Documentation/sony-laptop.txt
deleted file mode 100644 (file)
index 7a5c1a8..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-Sony Notebook Control Driver (SNC) Readme
------------------------------------------
-       Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
-       Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
-
-This mini-driver drives the SNC and SPIC device present in the ACPI BIOS of the
-Sony Vaio laptops. This driver mixes both devices functions under the same
-(hopefully consistent) interface. This also means that the sonypi driver is
-obsoleted by sony-laptop now.
-
-Fn keys (hotkeys):
-------------------
-Some models report hotkeys through the SNC or SPIC devices, such events are
-reported both through the ACPI subsystem as acpi events and through the INPUT
-subsystem. See the logs of acpid or /proc/acpi/event and
-/proc/bus/input/devices to find out what those events are and which input
-devices are created by the driver.
-
-Backlight control:
-------------------
-If your laptop model supports it, you will find sysfs files in the
-/sys/class/backlight/sony/
-directory. You will be able to query and set the current screen
-brightness:
-       brightness              get/set screen brightness (an iteger
-                               between 0 and 7)
-       actual_brightness       reading from this file will query the HW
-                               to get real brightness value
-       max_brightness          the maximum brightness value
-
-
-Platform specific:
-------------------
-Loading the sony-laptop module will create a
-/sys/devices/platform/sony-laptop/
-directory populated with some files.
-
-You then read/write integer values from/to those files by using
-standard UNIX tools.
-
-The files are:
-       brightness_default      screen brightness which will be set
-                               when the laptop will be rebooted
-       cdpower                 power on/off the internal CD drive
-       audiopower              power on/off the internal sound card
-       lanpower                power on/off the internal ethernet card
-                               (only in debug mode)
-       bluetoothpower          power on/off the internal bluetooth device
-       fanspeed                get/set the fan speed
-
-Note that some files may be missing if they are not supported
-by your particular laptop model.
-
-Example usage:
-       # echo "1" > /sys/devices/platform/sony-laptop/brightness_default
-sets the lowest screen brightness for the next and later reboots,
-       # echo "8" > /sys/devices/platform/sony-laptop/brightness_default
-sets the highest screen brightness for the next and later reboots,
-       # cat /sys/devices/platform/sony-laptop/brightness_default
-retrieves the value.
-
-       # echo "0" > /sys/devices/platform/sony-laptop/audiopower
-powers off the sound card,
-       # echo "1" > /sys/devices/platform/sony-laptop/audiopower
-powers on the sound card.
-
-Development:
-------------
-
-If you want to help with the development of this driver (and
-you are not afraid of any side effects doing strange things with
-your ACPI BIOS could have on your laptop), load the driver and
-pass the option 'debug=1'.
-
-REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
-
-In your kernel logs you will find the list of all ACPI methods
-the SNC device has on your laptop. You can see the GCDP/GCDP methods
-used to pwer on/off the CD drive, but there are others.
-
-I HAVE NO IDEA WHAT THOSE METHODS DO.
-
-The sony-laptop driver creates, for some of those methods (the most
-current ones found on several Vaio models), an entry under
-/sys/devices/platform/sony-laptop, just like the 'cdpower' one.
-You can create other entries corresponding to your own laptop methods by
-further editing the source (see the 'sony_nc_values' table, and add a new
-entry to this table with your get/set method names using the
-SNC_HANDLE_NAMES macro).
-
-Your mission, should you accept it, is to try finding out what
-those entries are for, by reading/writing random values from/to those
-files and find out what is the impact on your laptop.
-
-Should you find anything interesting, please report it back to me,
-I will not disavow all knowledge of your actions :)
-
-See also http://www.linux.it/~malattia/wiki/index.php/Sony_drivers for other
-useful info.
-
-Bugs/Limitations:
------------------
-
-* This driver is not based on official documentation from Sony
-  (because there is none), so there is no guarantee this driver
-  will work at all, or do the right thing. Although this hasn't
-  happened to me, this driver could do very bad things to your
-  laptop, including permanent damage.
-
-* The sony-laptop and sonypi drivers do not interact at all. In the
-  future, sonypi could use sony-laptop to do (part of) its business.
-
-* spicctrl, which is the userspace tool used to communicate with the
-  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/sonypi.txt
deleted file mode 100644 (file)
index 4857acf..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-Sony Programmable I/O Control Device Driver Readme
---------------------------------------------------
-       Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
-       Copyright (C) 2001-2002 Alcôve <www.alcove.com>
-       Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
-       Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
-       Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
-       Copyright (C) 2000 Andrew Tridgell <tridge@samba.org>
-
-This driver enables access to the Sony Programmable I/O Control Device which
-can be found in many Sony Vaio laptops. Some newer Sony laptops (seems to be
-limited to new FX series laptops, at least the FX501 and the FX702) lack a
-sonypi device and are not supported at all by this driver.
-
-It will give access (through a user space utility) to some events those laptops
-generate, like:
-       - jogdial events (the small wheel on the side of Vaios)
-       - capture button events (only on Vaio Picturebook series)
-       - Fn keys
-       - bluetooth button (only on C1VR model)
-       - programmable keys, back, help, zoom, thumbphrase buttons, etc.
-         (when available)
-
-Those events (see linux/sonypi.h) can be polled using the character device node
-/dev/sonypi (major 10, minor auto allocated or specified as a option).
-A simple daemon which translates the jogdial movements into mouse wheel events
-can be downloaded at: <http://popies.net/sonypi/>
-
-Another option to intercept the events is to get them directly through the
-input layer.
-
-This driver supports also some ioctl commands for setting the LCD screen
-brightness and querying the batteries charge information (some more
-commands may be added in the future).
-
-This driver can also be used to set the camera controls on Picturebook series
-(brightness, contrast etc), and is used by the video4linux driver for the
-Motion Eye camera.
-
-Please note that this driver was created by reverse engineering the Windows
-driver and the ACPI BIOS, because Sony doesn't agree to release any programming
-specs for its laptops. If someone convinces them to do so, drop me a note.
-
-Driver options:
----------------
-
-Several options can be passed to the sonypi driver using the standard
-module argument syntax (<param>=<value> when passing the option to the
-module or sonypi.<param>=<value> on the kernel boot line when sonypi is
-statically linked into the kernel). Those options are:
-
-       minor:          minor number of the misc device /dev/sonypi,
-                       default is -1 (automatic allocation, see /proc/misc
-                       or kernel logs)
-
-       camera:         if you have a PictureBook series Vaio (with the
-                       integrated MotionEye camera), set this parameter to 1
-                       in order to let the driver access to the camera
-
-       fnkeyinit:      on some Vaios (C1VE, C1VR etc), the Fn key events don't
-                       get enabled unless you set this parameter to 1.
-                       Do not use this option unless it's actually necessary,
-                       some Vaio models don't deal well with this option.
-                       This option is available only if the kernel is
-                       compiled without ACPI support (since it conflicts
-                       with it and it shouldn't be required anyway if
-                       ACPI is already enabled).
-
-       verbose:        set to 1 to print unknown events received from the
-                       sonypi device.
-                       set to 2 to print all events received from the
-                       sonypi device.
-
-       compat:         uses some compatibility code for enabling the sonypi
-                       events. If the driver worked for you in the past
-                       (prior to version 1.5) and does not work anymore,
-                       add this option and report to the author.
-
-       mask:           event mask telling the driver what events will be
-                       reported to the user. This parameter is required for
-                       some Vaio models where the hardware reuses values
-                       used in other Vaio models (like the FX series who does
-                       not have a jogdial but reuses the jogdial events for
-                       programmable keys events). The default event mask is
-                       set to 0xffffffff, meaning that all possible events
-                       will be tried. You can use the following bits to
-                       construct your own event mask (from
-                       drivers/char/sonypi.h):
-                               SONYPI_JOGGER_MASK              0x0001
-                               SONYPI_CAPTURE_MASK             0x0002
-                               SONYPI_FNKEY_MASK               0x0004
-                               SONYPI_BLUETOOTH_MASK           0x0008
-                               SONYPI_PKEY_MASK                0x0010
-                               SONYPI_BACK_MASK                0x0020
-                               SONYPI_HELP_MASK                0x0040
-                               SONYPI_LID_MASK                 0x0080
-                               SONYPI_ZOOM_MASK                0x0100
-                               SONYPI_THUMBPHRASE_MASK         0x0200
-                               SONYPI_MEYE_MASK                0x0400
-                               SONYPI_MEMORYSTICK_MASK         0x0800
-                               SONYPI_BATTERY_MASK             0x1000
-                               SONYPI_WIRELESS_MASK            0x2000
-
-       useinput:       if set (which is the default) two input devices are
-                       created, one which interprets the jogdial events as
-                       mouse events, the other one which acts like a
-                       keyboard reporting the pressing of the special keys.
-
-Module use:
------------
-
-In order to automatically load the sonypi module on use, you can put those
-lines in your /etc/modprobe.conf file:
-
-       alias char-major-10-250 sonypi
-       options sonypi minor=250
-
-This supposes the use of minor 250 for the sonypi device:
-
-       # mknod /dev/sonypi c 10 250
-
-Bugs:
------
-
-       - several users reported that this driver disables the BIOS-managed
-         Fn-keys which put the laptop in sleeping state, or switch the
-         external monitor on/off. There is no workaround yet, since this
-         driver disables all APM management for those keys, by enabling the
-         ACPI management (and the ACPI core stuff is not complete yet). If
-         you have one of those laptops with working Fn keys and want to
-         continue to use them, don't use this driver.
-
-       - some users reported that the laptop speed is lower (dhrystone
-         tested) when using the driver with the fnkeyinit parameter. I cannot
-         reproduce it on my laptop and not all users have this problem.
-         This happens because the fnkeyinit parameter enables the ACPI
-         mode (but without additional ACPI control, like processor
-         speed handling etc). Use ACPI instead of APM if it works on your
-         laptop.
-
-       - sonypi lacks the ability to distinguish between certain key
-         events on some models.
-
-       - some models with the nvidia card (geforce go 6200 tc) uses a
-         different way to adjust the backlighting of the screen. There
-         is a userspace utility to adjust the brightness on those models,
-         which can be downloaded from
-         http://www.acc.umu.se/~erikw/program/smartdimmer-0.1.tar.bz2
-
-       - since all development was done by reverse engineering, there is
-         _absolutely no guarantee_ that this driver will not crash your
-         laptop. Permanently.
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt
deleted file mode 100644 (file)
index 6c24777..0000000
+++ /dev/null
@@ -1,1365 +0,0 @@
-                    ThinkPad ACPI Extras Driver
-
-                            Version 0.19
-                         January 06th, 2008
-
-               Borislav Deianov <borislav@users.sf.net>
-             Henrique de Moraes Holschuh <hmh@hmh.eng.br>
-                      http://ibm-acpi.sf.net/
-
-
-This is a Linux driver for the IBM and Lenovo ThinkPad laptops. It
-supports various features of these laptops which are accessible
-through the ACPI and ACPI EC framework, but not otherwise fully
-supported by the generic Linux ACPI drivers.
-
-This driver used to be named ibm-acpi until kernel 2.6.21 and release
-0.13-20070314.  It used to be in the drivers/acpi tree, but it was
-moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel
-2.6.22, and release 0.14.
-
-
-Status
-------
-
-The features currently supported are the following (see below for
-detailed description):
-
-       - Fn key combinations
-       - Bluetooth enable and disable
-       - video output switching, expansion control
-       - ThinkLight on and off
-       - limited docking and undocking
-       - UltraBay eject
-       - CMOS control
-       - LED control
-       - ACPI sounds
-       - temperature sensors
-       - Experimental: embedded controller register dump
-       - LCD brightness control
-       - Volume control
-       - Fan control and monitoring: fan speed, fan enable/disable
-       - Experimental: WAN enable and disable
-
-A compatibility table by model and feature is maintained on the web
-site, http://ibm-acpi.sf.net/. I appreciate any success or failure
-reports, especially if they add to or correct the compatibility table.
-Please include the following information in your report:
-
-       - ThinkPad model name
-       - a copy of your DSDT, from /proc/acpi/dsdt
-       - a copy of the output of dmidecode, with serial numbers
-         and UUIDs masked off
-       - which driver features work and which don't
-       - the observed behavior of non-working features
-
-Any other comments or patches are also more than welcome.
-
-
-Installation
-------------
-
-If you are compiling this driver as included in the Linux kernel
-sources, simply enable the CONFIG_THINKPAD_ACPI option, and optionally
-enable the CONFIG_THINKPAD_ACPI_BAY option if you want the
-thinkpad-specific bay functionality.
-
-Features
---------
-
-The driver exports two different interfaces to userspace, which can be
-used to access the features it provides.  One is a legacy procfs-based
-interface, which will be removed at some time in the distant future.
-The other is a new sysfs-based interface which is not complete yet.
-
-The procfs interface creates the /proc/acpi/ibm directory.  There is a
-file under that directory for each feature it supports.  The procfs
-interface is mostly frozen, and will change very little if at all: it
-will not be extended to add any new functionality in the driver, instead
-all new functionality will be implemented on the sysfs interface.
-
-The sysfs interface tries to blend in the generic Linux sysfs subsystems
-and classes as much as possible.  Since some of these subsystems are not
-yet ready or stabilized, it is expected that this interface will change,
-and any and all userspace programs must deal with it.
-
-
-Notes about the sysfs interface:
-
-Unlike what was done with the procfs interface, correctness when talking
-to the sysfs interfaces will be enforced, as will correctness in the
-thinkpad-acpi's implementation of sysfs interfaces.
-
-Also, any bugs in the thinkpad-acpi sysfs driver code or in the
-thinkpad-acpi's implementation of the sysfs interfaces will be fixed for
-maximum correctness, even if that means changing an interface in
-non-compatible ways.  As these interfaces mature both in the kernel and
-in thinkpad-acpi, such changes should become quite rare.
-
-Applications interfacing to the thinkpad-acpi sysfs interfaces must
-follow all sysfs guidelines and correctly process all errors (the sysfs
-interface makes extensive use of errors).  File descriptors and open /
-close operations to the sysfs inodes must also be properly implemented.
-
-The version of thinkpad-acpi's sysfs interface is exported by the driver
-as a driver attribute (see below).
-
-Sysfs driver attributes are on the driver's sysfs attribute space,
-for 2.6.23 this is /sys/bus/platform/drivers/thinkpad_acpi/ and
-/sys/bus/platform/drivers/thinkpad_hwmon/
-
-Sysfs device attributes are on the thinkpad_acpi device sysfs attribute
-space, for 2.6.23 this is /sys/devices/platform/thinkpad_acpi/.
-
-Sysfs device attributes for the sensors and fan are on the
-thinkpad_hwmon device's sysfs attribute space, but you should locate it
-looking for a hwmon device with the name attribute of "thinkpad".
-
-Driver version
---------------
-
-procfs: /proc/acpi/ibm/driver
-sysfs driver attribute: version
-
-The driver name and version. No commands can be written to this file.
-
-Sysfs interface version
------------------------
-
-sysfs driver attribute: interface_version
-
-Version of the thinkpad-acpi sysfs interface, as an unsigned long
-(output in hex format: 0xAAAABBCC), where:
-       AAAA - major revision
-       BB - minor revision
-       CC - bugfix revision
-
-The sysfs interface version changelog for the driver can be found at the
-end of this document.  Changes to the sysfs interface done by the kernel
-subsystems are not documented here, nor are they tracked by this
-attribute.
-
-Changes to the thinkpad-acpi sysfs interface are only considered
-non-experimental when they are submitted to Linux mainline, at which
-point the changes in this interface are documented and interface_version
-may be updated.  If you are using any thinkpad-acpi features not yet
-sent to mainline for merging, you do so on your own risk: these features
-may disappear, or be implemented in a different and incompatible way by
-the time they are merged in Linux mainline.
-
-Changes that are backwards-compatible by nature (e.g. the addition of
-attributes that do not change the way the other attributes work) do not
-always warrant an update of interface_version.  Therefore, one must
-expect that an attribute might not be there, and deal with it properly
-(an attribute not being there *is* a valid way to make it clear that a
-feature is not available in sysfs).
-
-Hot keys
---------
-
-procfs: /proc/acpi/ibm/hotkey
-sysfs device attribute: hotkey_*
-
-In a ThinkPad, the ACPI HKEY handler is responsible for comunicating
-some important events and also keyboard hot key presses to the operating
-system.  Enabling the hotkey functionality of thinkpad-acpi signals the
-firmware that such a driver is present, and modifies how the ThinkPad
-firmware will behave in many situations.
-
-The driver enables the hot key feature automatically when loaded.  The
-feature can later be disabled and enabled back at runtime.  The driver
-will also restore the hot key feature to its previous state and mask
-when it is unloaded.
-
-When the hotkey feature is enabled and the hot key mask is set (see
-below), the driver will report HKEY events in the following format:
-
-       ibm/hotkey HKEY 00000080 0000xxxx
-
-Some of these events refer to hot key presses, but not all.
-
-The driver will generate events over the input layer for hot keys and
-radio switches, and over the ACPI netlink layer for other events.  The
-input layer support accepts the standard IOCTLs to remap the keycodes
-assigned to each hot key.
-
-The hot key bit mask allows some control over which hot keys generate
-events.  If a key is "masked" (bit set to 0 in the mask), the firmware
-will handle it.  If it is "unmasked", it signals the firmware that
-thinkpad-acpi would prefer to handle it, if the firmware would be so
-kind to allow it (and it often doesn't!).
-
-Not all bits in the mask can be modified.  Not all bits that can be
-modified do anything.  Not all hot keys can be individually controlled
-by the mask.  Some models do not support the mask at all, and in those
-models, hot keys cannot be controlled individually.  The behaviour of
-the mask is, therefore, higly dependent on the ThinkPad model.
-
-Note that unmasking some keys prevents their default behavior.  For
-example, if Fn+F5 is unmasked, that key will no longer enable/disable
-Bluetooth by itself.
-
-Note also that not all Fn key combinations are supported through ACPI.
-For example, on the X40, the brightness, volume and "Access IBM" buttons
-do not generate ACPI events even with this driver.  They *can* be used
-through the "ThinkPad Buttons" utility, see http://www.nongnu.org/tpb/
-
-procfs notes:
-
-The following commands can be written to the /proc/acpi/ibm/hotkey file:
-
-       echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
-       echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
-       echo 0xffffffff > /proc/acpi/ibm/hotkey -- enable all hot keys
-       echo 0 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
-       ... any other 8-hex-digit mask ...
-       echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
-
-The procfs interface does not support NVRAM polling control.  So as to
-maintain maximum bug-to-bug compatibility, it does not report any masks,
-nor does it allow one to manipulate the hot key mask when the firmware
-does not support masks at all, even if NVRAM polling is in use.
-
-sysfs notes:
-
-       hotkey_bios_enabled:
-               Returns the status of the hot keys feature when
-               thinkpad-acpi was loaded.  Upon module unload, the hot
-               key feature status will be restored to this value.
-
-               0: hot keys were disabled
-               1: hot keys were enabled (unusual)
-
-       hotkey_bios_mask:
-               Returns the hot keys mask when thinkpad-acpi was loaded.
-               Upon module unload, the hot keys mask will be restored
-               to this value.
-
-       hotkey_enable:
-               Enables/disables the hot keys feature in the ACPI
-               firmware, and reports current status of the hot keys
-               feature.  Has no effect on the NVRAM hot key polling
-               functionality.
-
-               0: disables the hot keys feature / feature disabled
-               1: enables the hot keys feature / feature enabled
-
-       hotkey_mask:
-               bit mask to enable driver-handling (and depending on
-               the firmware, ACPI event generation) for each hot key
-               (see above).  Returns the current status of the hot keys
-               mask, and allows one to modify it.
-
-               Note: when NVRAM polling is active, the firmware mask
-               will be different from the value returned by
-               hotkey_mask.  The driver will retain enabled bits for
-               hotkeys that are under NVRAM polling even if the
-               firmware refuses them, and will not set these bits on
-               the firmware hot key mask.
-
-       hotkey_all_mask:
-               bit mask that should enable event reporting for all
-               supported hot keys, when echoed to hotkey_mask above.
-               Unless you know which events need to be handled
-               passively (because the firmware *will* handle them
-               anyway), do *not* use hotkey_all_mask.  Use
-               hotkey_recommended_mask, instead. You have been warned.
-
-       hotkey_recommended_mask:
-               bit mask that should enable event reporting for all
-               supported hot keys, except those which are always
-               handled by the firmware anyway.  Echo it to
-               hotkey_mask above, to use.
-
-       hotkey_source_mask:
-               bit mask that selects which hot keys will the driver
-               poll the NVRAM for.  This is auto-detected by the driver
-               based on the capabilities reported by the ACPI firmware,
-               but it can be overridden at runtime.
-
-               Hot keys whose bits are set in both hotkey_source_mask
-               and also on hotkey_mask are polled for in NVRAM.  Only a
-               few hot keys are available through CMOS NVRAM polling.
-
-               Warning: when in NVRAM mode, the volume up/down/mute
-               keys are synthesized according to changes in the mixer,
-               so you have to use volume up or volume down to unmute,
-               as per the ThinkPad volume mixer user interface.  When
-               in ACPI event mode, volume up/down/mute are reported as
-               separate events, but this behaviour may be corrected in
-               future releases of this driver, in which case the
-               ThinkPad volume mixer user interface semanthics will be
-               enforced.
-
-       hotkey_poll_freq:
-               frequency in Hz for hot key polling. It must be between
-               0 and 25 Hz.  Polling is only carried out when strictly
-               needed.
-
-               Setting hotkey_poll_freq to zero disables polling, and
-               will cause hot key presses that require NVRAM polling
-               to never be reported.
-
-               Setting hotkey_poll_freq too low will cause repeated
-               pressings of the same hot key to be misreported as a
-               single key press, or to not even be detected at all.
-               The recommended polling frequency is 10Hz.
-
-       hotkey_radio_sw:
-               if the ThinkPad has a hardware radio switch, this
-               attribute will read 0 if the switch is in the "radios
-               disabled" postition, and 1 if the switch is in the
-               "radios enabled" position.
-
-               This attribute has poll()/select() support.
-
-       hotkey_report_mode:
-               Returns the state of the procfs ACPI event report mode
-               filter for hot keys.  If it is set to 1 (the default),
-               all hot key presses are reported both through the input
-               layer and also as ACPI events through procfs (but not
-               through netlink).  If it is set to 2, hot key presses
-               are reported only through the input layer.
-
-               This attribute is read-only in kernels 2.6.23 or later,
-               and read-write on earlier kernels.
-
-               May return -EPERM (write access locked out by module
-               parameter) or -EACCES (read-only).
-
-       wakeup_reason:
-               Set to 1 if the system is waking up because the user
-               requested a bay ejection.  Set to 2 if the system is
-               waking up because the user requested the system to
-               undock.  Set to zero for normal wake-ups or wake-ups
-               due to unknown reasons.
-
-               This attribute has poll()/select() support.
-
-       wakeup_hotunplug_complete:
-               Set to 1 if the system was waken up because of an
-               undock or bay ejection request, and that request
-               was sucessfully completed.  At this point, it might
-               be useful to send the system back to sleep, at the
-               user's choice.  Refer to HKEY events 0x4003 and
-               0x3003, below.
-
-               This attribute has poll()/select() support.
-
-input layer notes:
-
-A Hot key is mapped to a single input layer EV_KEY event, possibly
-followed by an EV_MSC MSC_SCAN event that shall contain that key's scan
-code.  An EV_SYN event will always be generated to mark the end of the
-event block.
-
-Do not use the EV_MSC MSC_SCAN events to process keys.  They are to be
-used as a helper to remap keys, only.  They are particularly useful when
-remapping KEY_UNKNOWN keys.
-
-The events are available in an input device, with the following id:
-
-       Bus:            BUS_HOST
-       vendor:         0x1014 (PCI_VENDOR_ID_IBM)  or
-                       0x17aa (PCI_VENDOR_ID_LENOVO)
-       product:        0x5054 ("TP")
-       version:        0x4101
-
-The version will have its LSB incremented if the keymap changes in a
-backwards-compatible way.  The MSB shall always be 0x41 for this input
-device.  If the MSB is not 0x41, do not use the device as described in
-this section, as it is either something else (e.g. another input device
-exported by a thinkpad driver, such as HDAPS) or its functionality has
-been changed in a non-backwards compatible way.
-
-Adding other event types for other functionalities shall be considered a
-backwards-compatible change for this input device.
-
-Thinkpad-acpi Hot Key event map (version 0x4101):
-
-ACPI   Scan
-event  code    Key             Notes
-
-0x1001 0x00    FN+F1           -
-0x1002 0x01    FN+F2           IBM: battery (rare)
-                               Lenovo: Screen lock
-
-0x1003 0x02    FN+F3           Many IBM models always report
-                               this hot key, even with hot keys
-                               disabled or with Fn+F3 masked
-                               off
-                               IBM: screen lock
-                               Lenovo: battery
-
-0x1004 0x03    FN+F4           Sleep button (ACPI sleep button
-                               semanthics, i.e. sleep-to-RAM).
-                               It is always generate some kind
-                               of event, either the hot key
-                               event or a ACPI sleep button
-                               event. The firmware may
-                               refuse to generate further FN+F4
-                               key presses until a S3 or S4 ACPI
-                               sleep cycle is performed or some
-                               time passes.
-
-0x1005 0x04    FN+F5           Radio.  Enables/disables
-                               the internal BlueTooth hardware
-                               and W-WAN card if left in control
-                               of the firmware.  Does not affect
-                               the WLAN card.
-                               Should be used to turn on/off all
-                               radios (bluetooth+W-WAN+WLAN),
-                               really.
-
-0x1006 0x05    FN+F6           -
-
-0x1007 0x06    FN+F7           Video output cycle.
-                               Do you feel lucky today?
-
-0x1008 0x07    FN+F8           IBM: toggle screen expand
-                               Lenovo: configure ultranav
-
-0x1009 0x08    FN+F9           -
-       ..      ..              ..
-0x100B 0x0A    FN+F11          -
-
-0x100C 0x0B    FN+F12          Sleep to disk.  You are always
-                               supposed to handle it yourself,
-                               either through the ACPI event,
-                               or through a hotkey event.
-                               The firmware may refuse to
-                               generate further FN+F4 key
-                               press events until a S3 or S4
-                               ACPI sleep cycle is performed,
-                               or some time passes.
-
-0x100D 0x0C    FN+BACKSPACE    -
-0x100E 0x0D    FN+INSERT       -
-0x100F 0x0E    FN+DELETE       -
-
-0x1010 0x0F    FN+HOME         Brightness up.  This key is
-                               always handled by the firmware
-                               in IBM ThinkPads, even when
-                               unmasked.  Just leave it alone.
-                               For Lenovo ThinkPads with a new
-                               BIOS, it has to be handled either
-                               by the ACPI OSI, or by userspace.
-0x1011 0x10    FN+END          Brightness down.  See brightness
-                               up for details.
-
-0x1012 0x11    FN+PGUP         Thinklight toggle.  This key is
-                               always handled by the firmware,
-                               even when unmasked.
-
-0x1013 0x12    FN+PGDOWN       -
-
-0x1014 0x13    FN+SPACE        Zoom key
-
-0x1015 0x14    VOLUME UP       Internal mixer volume up. This
-                               key is always handled by the
-                               firmware, even when unmasked.
-                               NOTE: Lenovo seems to be changing
-                               this.
-0x1016 0x15    VOLUME DOWN     Internal mixer volume up. This
-                               key is always handled by the
-                               firmware, even when unmasked.
-                               NOTE: Lenovo seems to be changing
-                               this.
-0x1017 0x16    MUTE            Mute internal mixer. This
-                               key is always handled by the
-                               firmware, even when unmasked.
-
-0x1018 0x17    THINKPAD        Thinkpad/Access IBM/Lenovo key
-
-0x1019 0x18    unknown
-..     ..      ..
-0x1020 0x1F    unknown
-
-The ThinkPad firmware does not allow one to differentiate when most hot
-keys are pressed or released (either that, or we don't know how to, yet).
-For these keys, the driver generates a set of events for a key press and
-immediately issues the same set of events for a key release.  It is
-unknown by the driver if the ThinkPad firmware triggered these events on
-hot key press or release, but the firmware will do it for either one, not
-both.
-
-If a key is mapped to KEY_RESERVED, it generates no input events at all.
-If a key is mapped to KEY_UNKNOWN, it generates an input event that
-includes an scan code.  If a key is mapped to anything else, it will
-generate input device EV_KEY events.
-
-Non hot-key ACPI HKEY event map:
-0x5001         Lid closed
-0x5002         Lid opened
-0x7000         Radio Switch may have changed state
-
-The above events are not propagated by the driver, except for legacy
-compatibility purposes when hotkey_report_mode is set to 1.
-
-0x2304         System is waking up from suspend to undock
-0x2305         System is waking up from suspend to eject bay
-0x2404         System is waking up from hibernation to undock
-0x2405         System is waking up from hibernation to eject bay
-
-The above events are never propagated by the driver.
-
-0x3003         Bay ejection (see 0x2x05) complete, can sleep again
-0x4003         Undocked (see 0x2x04), can sleep again
-0x5009         Tablet swivel: switched to tablet mode
-0x500A         Tablet swivel: switched to normal mode
-0x500B         Tablet pen insterted into its storage bay
-0x500C         Tablet pen removed from its storage bay
-0x5010         Brightness level changed (newer Lenovo BIOSes)
-
-The above events are propagated by the driver.
-
-Compatibility notes:
-
-ibm-acpi and thinkpad-acpi 0.15 (mainline kernels before 2.6.23) never
-supported the input layer, and sent events over the procfs ACPI event
-interface.
-
-To avoid sending duplicate events over the input layer and the ACPI
-event interface, thinkpad-acpi 0.16 implements a module parameter
-(hotkey_report_mode), and also a sysfs device attribute with the same
-name.
-
-Make no mistake here: userspace is expected to switch to using the input
-layer interface of thinkpad-acpi, together with the ACPI netlink event
-interface in kernels 2.6.23 and later, or with the ACPI procfs event
-interface in kernels 2.6.22 and earlier.
-
-If no hotkey_report_mode module parameter is specified (or it is set to
-zero), the driver defaults to mode 1 (see below), and on kernels 2.6.22
-and earlier, also allows one to change the hotkey_report_mode through
-sysfs.  In kernels 2.6.23 and later, where the netlink ACPI event
-interface is available, hotkey_report_mode cannot be changed through
-sysfs (it is read-only).
-
-If the hotkey_report_mode module parameter is set to 1 or 2, it cannot
-be changed later through sysfs (any writes will return -EPERM to signal
-that hotkey_report_mode was locked.  On 2.6.23 and later, where
-hotkey_report_mode cannot be changed at all, writes will return -EACES).
-
-hotkey_report_mode set to 1 makes the driver export through the procfs
-ACPI event interface all hot key presses (which are *also* sent to the
-input layer).  This is a legacy compatibility behaviour, and it is also
-the default mode of operation for the driver.
-
-hotkey_report_mode set to 2 makes the driver filter out the hot key
-presses from the procfs ACPI event interface, so these events will only
-be sent through the input layer.  Userspace that has been updated to use
-the thinkpad-acpi input layer interface should set hotkey_report_mode to
-2.
-
-Hot key press events are never sent to the ACPI netlink event interface.
-Really up-to-date userspace under kernel 2.6.23 and later is to use the
-netlink interface and the input layer interface, and don't bother at all
-with hotkey_report_mode.
-
-
-Bluetooth
----------
-
-procfs: /proc/acpi/ibm/bluetooth
-sysfs device attribute: bluetooth_enable
-
-This feature shows the presence and current state of a ThinkPad
-Bluetooth device in the internal ThinkPad CDC slot.
-
-Procfs notes:
-
-If Bluetooth is installed, the following commands can be used:
-
-       echo enable > /proc/acpi/ibm/bluetooth
-       echo disable > /proc/acpi/ibm/bluetooth
-
-Sysfs notes:
-
-       If the Bluetooth CDC card is installed, it can be enabled /
-       disabled through the "bluetooth_enable" thinkpad-acpi device
-       attribute, and its current status can also be queried.
-
-       enable:
-               0: disables Bluetooth / Bluetooth is disabled
-               1: enables Bluetooth / Bluetooth is enabled.
-
-       Note: this interface will be probably be superseeded by the
-       generic rfkill class, so it is NOT to be considered stable yet.
-
-Video output control -- /proc/acpi/ibm/video
---------------------------------------------
-
-This feature allows control over the devices used for video output -
-LCD, CRT or DVI (if available). The following commands are available:
-
-       echo lcd_enable > /proc/acpi/ibm/video
-       echo lcd_disable > /proc/acpi/ibm/video
-       echo crt_enable > /proc/acpi/ibm/video
-       echo crt_disable > /proc/acpi/ibm/video
-       echo dvi_enable > /proc/acpi/ibm/video
-       echo dvi_disable > /proc/acpi/ibm/video
-       echo auto_enable > /proc/acpi/ibm/video
-       echo auto_disable > /proc/acpi/ibm/video
-       echo expand_toggle > /proc/acpi/ibm/video
-       echo video_switch > /proc/acpi/ibm/video
-
-Each video output device can be enabled or disabled individually.
-Reading /proc/acpi/ibm/video shows the status of each device.
-
-Automatic video switching can be enabled or disabled.  When automatic
-video switching is enabled, certain events (e.g. opening the lid,
-docking or undocking) cause the video output device to change
-automatically. While this can be useful, it also causes flickering
-and, on the X40, video corruption. By disabling automatic switching,
-the flickering or video corruption can be avoided.
-
-The video_switch command cycles through the available video outputs
-(it simulates the behavior of Fn-F7).
-
-Video expansion can be toggled through this feature. This controls
-whether the display is expanded to fill the entire LCD screen when a
-mode with less than full resolution is used. Note that the current
-video expansion status cannot be determined through this feature.
-
-Note that on many models (particularly those using Radeon graphics
-chips) the X driver configures the video card in a way which prevents
-Fn-F7 from working. This also disables the video output switching
-features of this driver, as it uses the same ACPI methods as
-Fn-F7. Video switching on the console should still work.
-
-UPDATE: There's now a patch for the X.org Radeon driver which
-addresses this issue. Some people are reporting success with the patch
-while others are still having problems. For more information:
-
-https://bugs.freedesktop.org/show_bug.cgi?id=2000
-
-ThinkLight control -- /proc/acpi/ibm/light
-------------------------------------------
-
-The current status of the ThinkLight can be found in this file. A few
-models which do not make the status available will show it as
-"unknown". The available commands are:
-
-       echo on  > /proc/acpi/ibm/light
-       echo off > /proc/acpi/ibm/light
-
-Docking / undocking -- /proc/acpi/ibm/dock
-------------------------------------------
-
-Docking and undocking (e.g. with the X4 UltraBase) requires some
-actions to be taken by the operating system to safely make or break
-the electrical connections with the dock.
-
-The docking feature of this driver generates the following ACPI events:
-
-       ibm/dock GDCK 00000003 00000001 -- eject request
-       ibm/dock GDCK 00000003 00000002 -- undocked
-       ibm/dock GDCK 00000000 00000003 -- docked
-
-NOTE: These events will only be generated if the laptop was docked
-when originally booted. This is due to the current lack of support for
-hot plugging of devices in the Linux ACPI framework. If the laptop was
-booted while not in the dock, the following message is shown in the
-logs:
-
-       Mar 17 01:42:34 aero kernel: thinkpad_acpi: dock device not present
-
-In this case, no dock-related events are generated but the dock and
-undock commands described below still work. They can be executed
-manually or triggered by Fn key combinations (see the example acpid
-configuration files included in the driver tarball package available
-on the web site).
-
-When the eject request button on the dock is pressed, the first event
-above is generated. The handler for this event should issue the
-following command:
-
-       echo undock > /proc/acpi/ibm/dock
-
-After the LED on the dock goes off, it is safe to eject the laptop.
-Note: if you pressed this key by mistake, go ahead and eject the
-laptop, then dock it back in. Otherwise, the dock may not function as
-expected.
-
-When the laptop is docked, the third event above is generated. The
-handler for this event should issue the following command to fully
-enable the dock:
-
-       echo dock > /proc/acpi/ibm/dock
-
-The contents of the /proc/acpi/ibm/dock file shows the current status
-of the dock, as provided by the ACPI framework.
-
-The docking support in this driver does not take care of enabling or
-disabling any other devices you may have attached to the dock. For
-example, a CD drive plugged into the UltraBase needs to be disabled or
-enabled separately. See the provided example acpid configuration files
-for how this can be accomplished.
-
-There is no support yet for PCI devices that may be attached to a
-docking station, e.g. in the ThinkPad Dock II. The driver currently
-does not recognize, enable or disable such devices. This means that
-the only docking stations currently supported are the X-series
-UltraBase docks and "dumb" port replicators like the Mini Dock (the
-latter don't need any ACPI support, actually).
-
-UltraBay eject -- /proc/acpi/ibm/bay
-------------------------------------
-
-Inserting or ejecting an UltraBay device requires some actions to be
-taken by the operating system to safely make or break the electrical
-connections with the device.
-
-This feature generates the following ACPI events:
-
-       ibm/bay MSTR 00000003 00000000 -- eject request
-       ibm/bay MSTR 00000001 00000000 -- eject lever inserted
-
-NOTE: These events will only be generated if the UltraBay was present
-when the laptop was originally booted (on the X series, the UltraBay
-is in the dock, so it may not be present if the laptop was undocked).
-This is due to the current lack of support for hot plugging of devices
-in the Linux ACPI framework. If the laptop was booted without the
-UltraBay, the following message is shown in the logs:
-
-       Mar 17 01:42:34 aero kernel: thinkpad_acpi: bay device not present
-
-In this case, no bay-related events are generated but the eject
-command described below still works. It can be executed manually or
-triggered by a hot key combination.
-
-Sliding the eject lever generates the first event shown above. The
-handler for this event should take whatever actions are necessary to
-shut down the device in the UltraBay (e.g. call idectl), then issue
-the following command:
-
-       echo eject > /proc/acpi/ibm/bay
-
-After the LED on the UltraBay goes off, it is safe to pull out the
-device.
-
-When the eject lever is inserted, the second event above is
-generated. The handler for this event should take whatever actions are
-necessary to enable the UltraBay device (e.g. call idectl).
-
-The contents of the /proc/acpi/ibm/bay file shows the current status
-of the UltraBay, as provided by the ACPI framework.
-
-EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use
-this feature, you need to supply the experimental=1 parameter when
-loading the module):
-
-These models do not have a button near the UltraBay device to request
-a hot eject but rather require the laptop to be put to sleep
-(suspend-to-ram) before the bay device is ejected or inserted).
-The sequence of steps to eject the device is as follows:
-
-       echo eject > /proc/acpi/ibm/bay
-       put the ThinkPad to sleep
-       remove the drive
-       resume from sleep
-       cat /proc/acpi/ibm/bay should show that the drive was removed
-
-On the A3x, both the UltraBay 2000 and UltraBay Plus devices are
-supported. Use "eject2" instead of "eject" for the second bay.
-
-Note: the UltraBay eject support on the 600e/x, A22p and A3x is
-EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
-
-CMOS control
-------------
-
-procfs: /proc/acpi/ibm/cmos
-sysfs device attribute: cmos_command
-
-This feature is mostly used internally by the ACPI firmware to keep the legacy
-CMOS NVRAM bits in sync with the current machine state, and to record this
-state so that the ThinkPad will retain such settings across reboots.
-
-Some of these commands actually perform actions in some ThinkPad models, but
-this is expected to disappear more and more in newer models.  As an example, in
-a T43 and in a X40, commands 12 and 13 still control the ThinkLight state for
-real, but commands 0 to 2 don't control the mixer anymore (they have been
-phased out) and just update the NVRAM.
-
-The range of valid cmos command numbers is 0 to 21, but not all have an
-effect and the behavior varies from model to model.  Here is the behavior
-on the X40 (tpb is the ThinkPad Buttons utility):
-
-       0 - Related to "Volume down" key press
-       1 - Related to "Volume up" key press
-       2 - Related to "Mute on" key press
-       3 - Related to "Access IBM" key press
-       4 - Related to "LCD brightness up" key pess
-       5 - Related to "LCD brightness down" key press
-       11 - Related to "toggle screen expansion" key press/function
-       12 - Related to "ThinkLight on"
-       13 - Related to "ThinkLight off"
-       14 - Related to "ThinkLight" key press (toggle thinklight)
-
-The cmos command interface is prone to firmware split-brain problems, as
-in newer ThinkPads it is just a compatibility layer.  Do not use it, it is
-exported just as a debug tool.
-
-LED control -- /proc/acpi/ibm/led
----------------------------------
-
-Some of the LED indicators can be controlled through this feature. The
-available commands are:
-
-       echo '<led number> on' >/proc/acpi/ibm/led
-       echo '<led number> off' >/proc/acpi/ibm/led
-       echo '<led number> blink' >/proc/acpi/ibm/led
-
-The <led number> range is 0 to 7. The set of LEDs that can be
-controlled varies from model to model. Here is the mapping on the X40:
-
-       0 - power
-       1 - battery (orange)
-       2 - battery (green)
-       3 - UltraBase
-       4 - UltraBay
-       7 - standby
-
-All of the above can be turned on and off and can be made to blink.
-
-ACPI sounds -- /proc/acpi/ibm/beep
-----------------------------------
-
-The BEEP method is used internally by the ACPI firmware to provide
-audible alerts in various situations. This feature allows the same
-sounds to be triggered manually.
-
-The commands are non-negative integer numbers:
-
-       echo <number> >/proc/acpi/ibm/beep
-
-The valid <number> range is 0 to 17. Not all numbers trigger sounds
-and the sounds vary from model to model. Here is the behavior on the
-X40:
-
-       0 - stop a sound in progress (but use 17 to stop 16)
-       2 - two beeps, pause, third beep ("low battery")
-       3 - single beep
-       4 - high, followed by low-pitched beep ("unable")
-       5 - single beep
-       6 - very high, followed by high-pitched beep ("AC/DC")
-       7 - high-pitched beep
-       9 - three short beeps
-       10 - very long beep
-       12 - low-pitched beep
-       15 - three high-pitched beeps repeating constantly, stop with 0
-       16 - one medium-pitched beep repeating constantly, stop with 17
-       17 - stop 16
-
-Temperature sensors
--------------------
-
-procfs: /proc/acpi/ibm/thermal
-sysfs device attributes: (hwmon "thinkpad") temp*_input
-
-Most ThinkPads include six or more separate temperature sensors but only
-expose the CPU temperature through the standard ACPI methods.  This
-feature shows readings from up to eight different sensors on older
-ThinkPads, and up to sixteen different sensors on newer ThinkPads.
-
-For example, on the X40, a typical output may be:
-temperatures:   42 42 45 41 36 -128 33 -128
-
-On the T43/p, a typical output may be:
-temperatures:   48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128
-
-The mapping of thermal sensors to physical locations varies depending on
-system-board model (and thus, on ThinkPad model).
-
-http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that
-tries to track down these locations for various models.
-
-Most (newer?) models seem to follow this pattern:
-
-1:  CPU
-2:  (depends on model)
-3:  (depends on model)
-4:  GPU
-5:  Main battery: main sensor
-6:  Bay battery: main sensor
-7:  Main battery: secondary sensor
-8:  Bay battery: secondary sensor
-9-15: (depends on model)
-
-For the R51 (source: Thomas Gruber):
-2:  Mini-PCI
-3:  Internal HDD
-
-For the T43, T43/p (source: Shmidoax/Thinkwiki.org)
-http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p
-2:  System board, left side (near PCMCIA slot), reported as HDAPS temp
-3:  PCMCIA slot
-9:  MCH (northbridge) to DRAM Bus
-10: Clock-generator, mini-pci card and ICH (southbridge), under Mini-PCI
-    card, under touchpad
-11: Power regulator, underside of system board, below F2 key
-
-The A31 has a very atypical layout for the thermal sensors
-(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31)
-1:  CPU
-2:  Main Battery: main sensor
-3:  Power Converter
-4:  Bay Battery: main sensor
-5:  MCH (northbridge)
-6:  PCMCIA/ambient
-7:  Main Battery: secondary sensor
-8:  Bay Battery: secondary sensor
-
-
-Procfs notes:
-       Readings from sensors that are not available return -128.
-       No commands can be written to this file.
-
-Sysfs notes:
-       Sensors that are not available return the ENXIO error.  This
-       status may change at runtime, as there are hotplug thermal
-       sensors, like those inside the batteries and docks.
-
-       thinkpad-acpi thermal sensors are reported through the hwmon
-       subsystem, and follow all of the hwmon guidelines at
-       Documentation/hwmon.
-
-
-EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump
-------------------------------------------------------------------------
-
-This feature is marked EXPERIMENTAL because the implementation
-directly accesses hardware registers and may not work as expected. USE
-WITH CAUTION! To use this feature, you need to supply the
-experimental=1 parameter when loading the module.
-
-This feature dumps the values of 256 embedded controller
-registers. Values which have changed since the last time the registers
-were dumped are marked with a star:
-
-[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
-EC       +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
-EC 0x00:  a7  47  87  01  fe  96  00  08  01  00  cb  00  00  00  40  00
-EC 0x10:  00  00  ff  ff  f4  3c  87  09  01  ff  42  01  ff  ff  0d  00
-EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
-EC 0x30:  01  07  1a  00  30  04  00  00 *85  00  00  10  00  50  00  00
-EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
-EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03 *bc *02 *bc
-EC 0x60: *02 *bc *02  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0x70:  00  00  00  00  00  12  30  40 *24 *26 *2c *27 *20  80 *1f  80
-EC 0x80:  00  00  00  06 *37 *0e  03  00  00  00  0e  07  00  00  00  00
-EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xa0: *ff  09  ff  09  ff  ff *64  00 *00 *00 *a2  41 *ff *ff *e0  00
-EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
-EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
-
-This feature can be used to determine the register holding the fan
-speed on some models. To do that, do the following:
-
-       - make sure the battery is fully charged
-       - make sure the fan is running
-       - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so
-
-The first step makes sure various charging-related values don't
-vary. The second ensures that the fan-related values do vary, since
-the fan speed fluctuates a bit. The third will (hopefully) mark the
-fan register with a star:
-
-[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
-EC       +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
-EC 0x00:  a7  47  87  01  fe  96  00  08  01  00  cb  00  00  00  40  00
-EC 0x10:  00  00  ff  ff  f4  3c  87  09  01  ff  42  01  ff  ff  0d  00
-EC 0x20:  00  00  00  00  00  00  00  00  00  00  00  03  43  00  00  80
-EC 0x30:  01  07  1a  00  30  04  00  00  85  00  00  10  00  50  00  00
-EC 0x40:  00  00  00  00  00  00  14  01  00  04  00  00  00  00  00  00
-EC 0x50:  00  c0  02  0d  00  01  01  02  02  03  03  03  03  bc  02  bc
-EC 0x60:  02  bc  02  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0x70:  00  00  00  00  00  12  30  40  24  27  2c  27  21  80  1f  80
-EC 0x80:  00  00  00  06 *be  0d  03  00  00  00  0e  07  00  00  00  00
-EC 0x90:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xa0:  ff  09  ff  09  ff  ff  64  00  00  00  a2  41  ff  ff  e0  00
-EC 0xb0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xc0:  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xd0:  03  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
-EC 0xe0:  00  00  00  00  00  00  00  00  11  20  49  04  24  06  55  03
-EC 0xf0:  31  55  48  54  35  38  57  57  08  2f  45  73  07  65  6c  1a
-
-Another set of values that varies often is the temperature
-readings. Since temperatures don't change vary fast, you can take
-several quick dumps to eliminate them.
-
-You can use a similar method to figure out the meaning of other
-embedded controller registers - e.g. make sure nothing else changes
-except the charging or discharging battery to determine which
-registers contain the current battery capacity, etc. If you experiment
-with this, do send me your results (including some complete dumps with
-a description of the conditions when they were taken.)
-
-LCD brightness control
-----------------------
-
-procfs: /proc/acpi/ibm/brightness
-sysfs backlight device "thinkpad_screen"
-
-This feature allows software control of the LCD brightness on ThinkPad
-models which don't have a hardware brightness slider.
-
-It has some limitations: the LCD backlight cannot be actually turned on or
-off by this interface, and in many ThinkPad models, the "dim while on
-battery" functionality will be enabled by the BIOS when this interface is
-used, and cannot be controlled.
-
-On IBM (and some of the earlier Lenovo) ThinkPads, the backlight control
-has eight brightness levels, ranging from 0 to 7.  Some of the levels
-may not be distinct.  Later Lenovo models that implement the ACPI
-display backlight brightness control methods have 16 levels, ranging
-from 0 to 15.
-
-There are two interfaces to the firmware for direct brightness control,
-EC and CMOS.  To select which one should be used, use the
-brightness_mode module parameter: brightness_mode=1 selects EC mode,
-brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC
-and CMOS.  The driver tries to autodetect which interface to use.
-
-When display backlight brightness controls are available through the
-standard ACPI interface, it is best to use it instead of this direct
-ThinkPad-specific interface.  The driver will disable its native
-backlight brightness control interface if it detects that the standard
-ACPI interface is available in the ThinkPad.
-
-The brightness_enable module parameter can be used to control whether
-the LCD brightness control feature will be enabled when available.
-brightness_enable=0 forces it to be disabled.  brightness_enable=1
-forces it to be enabled when available, even if the standard ACPI
-interface is also available.
-
-Procfs notes:
-
-       The available commands are:
-
-       echo up   >/proc/acpi/ibm/brightness
-       echo down >/proc/acpi/ibm/brightness
-       echo 'level <level>' >/proc/acpi/ibm/brightness
-
-Sysfs notes:
-
-The interface is implemented through the backlight sysfs class, which is
-poorly documented at this time.
-
-Locate the thinkpad_screen device under /sys/class/backlight, and inside
-it there will be the following attributes:
-
-       max_brightness:
-               Reads the maximum brightness the hardware can be set to.
-               The minimum is always zero.
-
-       actual_brightness:
-               Reads what brightness the screen is set to at this instant.
-
-       brightness:
-               Writes request the driver to change brightness to the
-               given value.  Reads will tell you what brightness the
-               driver is trying to set the display to when "power" is set
-               to zero and the display has not been dimmed by a kernel
-               power management event.
-
-       power:
-               power management mode, where 0 is "display on", and 1 to 3
-               will dim the display backlight to brightness level 0
-               because thinkpad-acpi cannot really turn the backlight
-               off.  Kernel power management events can temporarily
-               increase the current power management level, i.e. they can
-               dim the display.
-
-
-Volume control -- /proc/acpi/ibm/volume
----------------------------------------
-
-This feature allows volume control on ThinkPad models which don't have
-a hardware volume knob. The available commands are:
-
-       echo up   >/proc/acpi/ibm/volume
-       echo down >/proc/acpi/ibm/volume
-       echo mute >/proc/acpi/ibm/volume
-       echo 'level <level>' >/proc/acpi/ibm/volume
-
-The <level> number range is 0 to 15 although not all of them may be
-distinct. The unmute the volume after the mute command, use either the
-up or down command (the level command will not unmute the volume).
-The current volume level and mute state is shown in the file.
-
-Fan control and monitoring: fan speed, fan enable/disable
----------------------------------------------------------
-
-procfs: /proc/acpi/ibm/fan
-sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1,
-                         pwm1_enable
-sysfs hwmon driver attributes: fan_watchdog
-
-NOTE NOTE NOTE: fan control operations are disabled by default for
-safety reasons.  To enable them, the module parameter "fan_control=1"
-must be given to thinkpad-acpi.
-
-This feature attempts to show the current fan speed, control mode and
-other fan data that might be available.  The speed is read directly
-from the hardware registers of the embedded controller.  This is known
-to work on later R, T, X and Z series ThinkPads but may show a bogus
-value on other models.
-
-Fan levels:
-
-Most ThinkPad fans work in "levels" at the firmware interface.  Level 0
-stops the fan.  The higher the level, the higher the fan speed, although
-adjacent levels often map to the same fan speed.  7 is the highest
-level, where the fan reaches the maximum recommended speed.
-
-Level "auto" means the EC changes the fan level according to some
-internal algorithm, usually based on readings from the thermal sensors.
-
-There is also a "full-speed" level, also known as "disengaged" level.
-In this level, the EC disables the speed-locked closed-loop fan control,
-and drives the fan as fast as it can go, which might exceed hardware
-limits, so use this level with caution.
-
-The fan usually ramps up or down slowly from one speed to another, and
-it is normal for the EC to take several seconds to react to fan
-commands.  The full-speed level may take up to two minutes to ramp up to
-maximum speed, and in some ThinkPads, the tachometer readings go stale
-while the EC is transitioning to the full-speed level.
-
-WARNING WARNING WARNING: do not leave the fan disabled unless you are
-monitoring all of the temperature sensor readings and you are ready to
-enable it if necessary to avoid overheating.
-
-An enabled fan in level "auto" may stop spinning if the EC decides the
-ThinkPad is cool enough and doesn't need the extra airflow.  This is
-normal, and the EC will spin the fan up if the various thermal readings
-rise too much.
-
-On the X40, this seems to depend on the CPU and HDD temperatures.
-Specifically, the fan is turned on when either the CPU temperature
-climbs to 56 degrees or the HDD temperature climbs to 46 degrees.  The
-fan is turned off when the CPU temperature drops to 49 degrees and the
-HDD temperature drops to 41 degrees.  These thresholds cannot
-currently be controlled.
-
-The ThinkPad's ACPI DSDT code will reprogram the fan on its own when
-certain conditions are met.  It will override any fan programming done
-through thinkpad-acpi.
-
-The thinkpad-acpi kernel driver can be programmed to revert the fan
-level to a safe setting if userspace does not issue one of the procfs
-fan commands: "enable", "disable", "level" or "watchdog", or if there
-are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is
-set to 1, manual mode) within a configurable amount of time of up to
-120 seconds.  This functionality is called fan safety watchdog.
-
-Note that the watchdog timer stops after it enables the fan.  It will be
-rearmed again automatically (using the same interval) when one of the
-above mentioned fan commands is received.  The fan watchdog is,
-therefore, not suitable to protect against fan mode changes made through
-means other than the "enable", "disable", and "level" procfs fan
-commands, or the hwmon fan control sysfs interface.
-
-Procfs notes:
-
-The fan may be enabled or disabled with the following commands:
-
-       echo enable  >/proc/acpi/ibm/fan
-       echo disable >/proc/acpi/ibm/fan
-
-Placing a fan on level 0 is the same as disabling it.  Enabling a fan
-will try to place it in a safe level if it is too slow or disabled.
-
-The fan level can be controlled with the command:
-
-       echo 'level <level>' > /proc/acpi/ibm/fan
-
-Where <level> is an integer from 0 to 7, or one of the words "auto" or
-"full-speed" (without the quotes).  Not all ThinkPads support the "auto"
-and "full-speed" levels.  The driver accepts "disengaged" as an alias for
-"full-speed", and reports it as "disengaged" for backwards
-compatibility.
-
-On the X31 and X40 (and ONLY on those models), the fan speed can be
-controlled to a certain degree.  Once the fan is running, it can be
-forced to run faster or slower with the following command:
-
-       echo 'speed <speed>' > /proc/acpi/ibm/fan
-
-The sustainable range of fan speeds on the X40 appears to be from about
-3700 to about 7350. Values outside this range either do not have any
-effect or the fan speed eventually settles somewhere in that range.  The
-fan cannot be stopped or started with this command.  This functionality
-is incomplete, and not available through the sysfs interface.
-
-To program the safety watchdog, use the "watchdog" command.
-
-       echo 'watchdog <interval in seconds>' > /proc/acpi/ibm/fan
-
-If you want to disable the watchdog, use 0 as the interval.
-
-Sysfs notes:
-
-The sysfs interface follows the hwmon subsystem guidelines for the most
-part, and the exception is the fan safety watchdog.
-
-Writes to any of the sysfs attributes may return the EINVAL error if
-that operation is not supported in a given ThinkPad or if the parameter
-is out-of-bounds, and EPERM if it is forbidden.  They may also return
-EINTR (interrupted system call), and EIO (I/O error while trying to talk
-to the firmware).
-
-Features not yet implemented by the driver return ENOSYS.
-
-hwmon device attribute pwm1_enable:
-       0: PWM offline (fan is set to full-speed mode)
-       1: Manual PWM control (use pwm1 to set fan level)
-       2: Hardware PWM control (EC "auto" mode)
-       3: reserved (Software PWM control, not implemented yet)
-
-       Modes 0 and 2 are not supported by all ThinkPads, and the
-       driver is not always able to detect this.  If it does know a
-       mode is unsupported, it will return -EINVAL.
-
-hwmon device attribute pwm1:
-       Fan level, scaled from the firmware values of 0-7 to the hwmon
-       scale of 0-255.  0 means fan stopped, 255 means highest normal
-       speed (level 7).
-
-       This attribute only commands the fan if pmw1_enable is set to 1
-       (manual PWM control).
-
-hwmon device attribute fan1_input:
-       Fan tachometer reading, in RPM.  May go stale on certain
-       ThinkPads while the EC transitions the PWM to offline mode,
-       which can take up to two minutes.  May return rubbish on older
-       ThinkPads.
-
-hwmon driver attribute fan_watchdog:
-       Fan safety watchdog timer interval, in seconds.  Minimum is
-       1 second, maximum is 120 seconds.  0 disables the watchdog.
-
-To stop the fan: set pwm1 to zero, and pwm1_enable to 1.
-
-To start the fan in a safe mode: set pwm1_enable to 2.  If that fails
-with EINVAL, try to set pwm1_enable to 1 and pwm1 to at least 128 (255
-would be the safest choice, though).
-
-
-EXPERIMENTAL: WAN
------------------
-
-procfs: /proc/acpi/ibm/wan
-sysfs device attribute: wwan_enable
-
-This feature is marked EXPERIMENTAL because the implementation
-directly accesses hardware registers and may not work as expected. USE
-WITH CAUTION! To use this feature, you need to supply the
-experimental=1 parameter when loading the module.
-
-This feature shows the presence and current state of a W-WAN (Sierra
-Wireless EV-DO) device.
-
-It was tested on a Lenovo Thinkpad X60. It should probably work on other
-Thinkpad models which come with this module installed.
-
-Procfs notes:
-
-If the W-WAN card is installed, the following commands can be used:
-
-       echo enable > /proc/acpi/ibm/wan
-       echo disable > /proc/acpi/ibm/wan
-
-Sysfs notes:
-
-       If the W-WAN card is installed, it can be enabled /
-       disabled through the "wwan_enable" thinkpad-acpi device
-       attribute, and its current status can also be queried.
-
-       enable:
-               0: disables WWAN card / WWAN card is disabled
-               1: enables WWAN card / WWAN card is enabled.
-
-       Note: this interface will be probably be superseeded by the
-       generic rfkill class, so it is NOT to be considered stable yet.
-
-Multiple Commands, Module Parameters
-------------------------------------
-
-Multiple commands can be written to the proc files in one shot by
-separating them with commas, for example:
-
-       echo enable,0xffff > /proc/acpi/ibm/hotkey
-       echo lcd_disable,crt_enable > /proc/acpi/ibm/video
-
-Commands can also be specified when loading the thinkpad-acpi module,
-for example:
-
-       modprobe thinkpad_acpi hotkey=enable,0xffff video=auto_disable
-
-Enabling debugging output
--------------------------
-
-The module takes a debug parameter which can be used to selectively
-enable various classes of debugging output, for example:
-
-        modprobe ibm_acpi debug=0xffff
-
-will enable all debugging output classes.  It takes a bitmask, so
-to enable more than one output class, just add their values.
-
-       Debug bitmask           Description
-       0x0001                  Initialization and probing
-       0x0002                  Removal
-
-There is also a kernel build option to enable more debugging
-information, which may be necessary to debug driver problems.
-
-The level of debugging information output by the driver can be changed
-at runtime through sysfs, using the driver attribute debug_level.  The
-attribute takes the same bitmask as the debug module parameter above.
-
-Force loading of module
------------------------
-
-If thinkpad-acpi refuses to detect your ThinkPad, you can try to specify
-the module parameter force_load=1.  Regardless of whether this works or
-not, please contact ibm-acpi-devel@lists.sourceforge.net with a report.
-
-
-Sysfs interface changelog:
-
-0x000100:      Initial sysfs support, as a single platform driver and
-               device.
-0x000200:      Hot key support for 32 hot keys, and radio slider switch
-               support.
-0x010000:      Hot keys are now handled by default over the input
-               layer, the radio switch generates input event EV_RADIO,
-               and the driver enables hot key handling by default in
-               the firmware.
-
-0x020000:      ABI fix: added a separate hwmon platform device and
-               driver, which must be located by name (thinkpad)
-               and the hwmon class for libsensors4 (lm-sensors 3)
-               compatibility.  Moved all hwmon attributes to this
-               new platform device.
-
-0x020100:      Marker for thinkpad-acpi with hot key NVRAM polling
-               support.  If you must, use it to know you should not
-               start an userspace NVRAM poller (allows to detect when
-               NVRAM is compiled out by the user because it is
-               unneeded/undesired in the first place).
-0x020101:      Marker for thinkpad-acpi with hot key NVRAM polling
-               and proper hotkey_mask semanthics (version 8 of the
-               NVRAM polling patch).  Some development snapshots of
-               0.18 had an earlier version that did strange things
-               to hotkey_mask.
-
-0x020200:      Add poll()/select() support to the following attributes:
-               hotkey_radio_sw, wakeup_hotunplug_complete, wakeup_reason
index 0d6f511..c40f0ae 100644 (file)
@@ -2682,7 +2682,6 @@ MOTOROLA IMX MMC/SD HOST CONTROLLER INTERFACE DRIVER
 P:     Pavel Pisa
 M:     ppisa@pikron.com
 L:     linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
-W:     http://mmc.drzeus.cx/wiki/Controllers/Freescale/SDHC
 S:     Maintained
 
 MOUSE AND MISC DEVICES [GENERAL]
@@ -3627,6 +3626,13 @@ L:       linux-acpi@vger.kernel.org
 W:     http://www.linux.it/~malattia/wiki/index.php/Sony_drivers
 S:     Maintained
 
+SONY MEMORYSTICK CARD SUPPORT
+P:     Alex Dubov
+M:     oakad@yahoo.com
+L:     linux-kernel@vger.kernel.org
+W:     http://tifmxx.berlios.de/
+S:     Maintained
+
 SOUND
 P:     Jaroslav Kysela
 M:     perex@perex.cz
@@ -3709,7 +3715,6 @@ SECURE DIGITAL HOST CONTROLLER INTERFACE DRIVER
 P:     Pierre Ossman
 M:     drzeus-sdhci@drzeus.cx
 L:     sdhci-devel@list.drzeus.cx
-W:     http://mmc.drzeus.cx/wiki/Linux/Drivers/sdhci
 S:     Maintained
 
 SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS
@@ -4279,7 +4284,6 @@ W83L51xD SD/MMC CARD INTERFACE DRIVER
 P:     Pierre Ossman
 M:     drzeus-wbsd@drzeus.cx
 L:     linux-kernel@vger.kernel.org
-W:     http://projects.drzeus.cx/wbsd
 S:     Maintained
 
 WATCHDOG DEVICE DRIVERS
index 5b7dcd5..002703b 100644 (file)
@@ -5,6 +5,7 @@
 config ALPHA
        bool
        default y
+       select HAVE_IDE
        select HAVE_OPROFILE
        help
          The Alpha is a 64-bit general-purpose processor designed and
index 4127af9..9619c43 100644 (file)
@@ -222,6 +222,7 @@ config ARCH_CLPS7500
        select TIMER_ACORN
        select ISA
        select NO_IOPORT
+       select HAVE_IDE
        help
          Support for the Cirrus Logic PS7500FE system-on-a-chip.
 
@@ -234,6 +235,7 @@ config ARCH_CO285
        bool "Co-EBSA285"
        select FOOTBRIDGE
        select FOOTBRIDGE_ADDIN
+       select HAVE_IDE
        help
          Support for Intel's EBSA285 companion chip.
 
@@ -258,6 +260,7 @@ config ARCH_EP93XX
 config ARCH_FOOTBRIDGE
        bool "FootBridge"
        select FOOTBRIDGE
+       select HAVE_IDE
        help
          Support for systems based on the DC21285 companion chip
          ("FootBridge"), such as the Simtec CATS and the Rebel NetWinder.
@@ -296,6 +299,7 @@ config ARCH_IOP32X
        depends on MMU
        select PLAT_IOP
        select PCI
+       select HAVE_IDE
        help
          Support for Intel's 80219 and IOP32X (XScale) family of
          processors.
@@ -305,12 +309,14 @@ config ARCH_IOP33X
        depends on MMU
        select PLAT_IOP
        select PCI
+       select HAVE_IDE
        help
          Support for Intel's IOP33X (XScale) family of processors.
 
 config ARCH_IXP23XX
        bool "IXP23XX-based"
        depends on MMU
+       select HAVE_IDE
        select PCI
        help
          Support for Intel's IXP23xx (XScale) family of processors.
@@ -328,12 +334,14 @@ config ARCH_IXP4XX
        select GENERIC_GPIO
        select GENERIC_TIME
        select GENERIC_CLOCKEVENTS
+       select HAVE_IDE
        help
          Support for Intel's IXP4XX (XScale) family of processors.
 
 config ARCH_L7200
        bool "LinkUp-L7200"
        select FIQ
+       select HAVE_IDE
        help
          Say Y here if you intend to run this kernel on a LinkUp Systems
          L7200 Software Development Board which uses an ARM720T processor.
@@ -388,6 +396,7 @@ config ARCH_PXA
        depends on MMU
        select ARCH_MTD_XIP
        select GENERIC_GPIO
+       select HAVE_IDE
        select HAVE_GPIO_LIB
        select GENERIC_TIME
        select GENERIC_CLOCKEVENTS
@@ -403,6 +412,7 @@ config ARCH_RPC
        select ARCH_MAY_HAVE_PC_FDC
        select ISA_DMA_API
        select NO_IOPORT
+       select HAVE_IDE
        help
          On the Acorn Risc-PC, Linux can support the internal IDE disk and
          CD-ROM interface, serial and parallel port, and the floppy drive.
@@ -414,12 +424,14 @@ config ARCH_SA1100
        select ARCH_MTD_XIP
        select GENERIC_GPIO
        select GENERIC_TIME
+       select HAVE_IDE
        help
          Support for StrongARM 11x0 based boards.
 
 config ARCH_S3C2410
        bool "Samsung S3C2410, S3C2412, S3C2413, S3C2440, S3C2442, S3C2443"
        select GENERIC_GPIO
+       select HAVE_IDE
        help
          Samsung S3C2410X CPU based systems, such as the Simtec Electronics
          BAST (<http://www.simtec.co.uk/products/EB110ITX/>), the IPAQ 1940 or
@@ -427,6 +439,7 @@ config ARCH_S3C2410
 
 config ARCH_SHARK
        bool "Shark"
+       select HAVE_IDE
        select ISA
        select ISA_DMA
        select PCI
@@ -436,6 +449,7 @@ config ARCH_SHARK
 
 config ARCH_LH7A40X
        bool "Sharp LH7A40X"
+       select HAVE_IDE
        help
          Say Y here for systems based on one of the Sharp LH7A40X
          System on a Chip processors.  These CPUs include an ARM922T
@@ -1093,12 +1107,7 @@ source "drivers/block/Kconfig"
 
 source "drivers/misc/Kconfig"
 
-if PCMCIA || ARCH_CLPS7500 || ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX \
-       || ARCH_L7200 || ARCH_LH7A40X || ARCH_PXA || ARCH_RPC \
-       || ARCH_S3C2410 || ARCH_SA1100 || ARCH_SHARK || FOOTBRIDGE \
-       || ARCH_IXP23XX
 source "drivers/ide/Kconfig"
-endif
 
 source "drivers/scsi/Kconfig"
 
index 28e0caf..c75d708 100644 (file)
@@ -10,6 +10,7 @@ config AVR32
        # With EMBEDDED=n, we get lots of stuff automatically selected
        # that we usually don't need on AVR32.
        select EMBEDDED
+       select HAVE_IDE
        select HAVE_OPROFILE
        select HAVE_KPROBES
        help
index 368bc7f..589c6ac 100644 (file)
@@ -24,6 +24,7 @@ config RWSEM_XCHGADD_ALGORITHM
 config BLACKFIN
        bool
        default y
+       select HAVE_IDE
        select HAVE_OPROFILE
 
 config ZONE_DMA
index 8456bc8..9389d38 100644 (file)
@@ -54,6 +54,7 @@ config FORCE_MAX_ZONEORDER
 config CRIS
        bool
        default y
+       select HAVE_IDE
 
 config HZ
        int
index 9e561ed..a5aac1b 100644 (file)
@@ -5,6 +5,7 @@
 config FRV
        bool
        default y
+       select HAVE_IDE
 
 config ZONE_DMA
        bool
index f69e5ea..085dc6e 100644 (file)
@@ -8,6 +8,7 @@ mainmenu "uClinux/h8300 (w/o MMU) Kernel Configuration"
 config H8300
        bool
        default y
+       select HAVE_IDE
 
 config MMU
        bool
index b0de113..2d4fcd0 100644 (file)
@@ -15,6 +15,7 @@ config IA64
        select ACPI if (!IA64_HP_SIM)
        select PM if (!IA64_HP_SIM)
        select ARCH_SUPPORTS_MSI
+       select HAVE_IDE
        select HAVE_OPROFILE
        select HAVE_KPROBES
        default y
index d4679ab..de153de 100644 (file)
@@ -8,6 +8,7 @@ mainmenu "Linux/M32R Kernel Configuration"
 config M32R
        bool
        default y
+       select HAVE_IDE
        select HAVE_OPROFILE
 
 config SBUS
index 2b0ed89..65db226 100644 (file)
@@ -5,6 +5,7 @@
 config M68K
        bool
        default y
+       select HAVE_IDE
 
 config MMU
        bool
index 548a7b3..07eb4c4 100644 (file)
@@ -8,6 +8,7 @@ mainmenu "uClinux/68k (w/o MMU) Kernel Configuration"
 config M68K
        bool
        default y
+       select HAVE_IDE
 
 config MMU
        bool
index ec78a57..ade230d 100644 (file)
@@ -1,6 +1,7 @@
 config MIPS
        bool
        default y
+       select HAVE_IDE
        select HAVE_OPROFILE
        # Horrible source of confusion.  Die, die, die ...
        select EMBEDDED
index d929ac8..bc7a19d 100644 (file)
@@ -7,6 +7,7 @@ mainmenu "Linux/PA-RISC Kernel Configuration"
 
 config PARISC
        def_bool y
+       select HAVE_IDE
        select HAVE_OPROFILE
        help
          The PA-RISC microprocessor is designed by Hewlett-Packard and used
index 26b963c..485513c 100644 (file)
@@ -87,6 +87,7 @@ config ARCH_NO_VIRT_TO_BUS
 config PPC
        bool
        default y
+       select HAVE_IDE
        select HAVE_OPROFILE
        select HAVE_KPROBES
 
index 531156f..abc877f 100644 (file)
@@ -42,6 +42,7 @@ config GENERIC_CALIBRATE_DELAY
 config PPC
        bool
        default y
+       select HAVE_IDE
        select HAVE_OPROFILE
        select HAVE_KPROBES
 
index ece7b99..39921f3 100644 (file)
@@ -1,12 +1,13 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.23
-# Mon Oct 22 12:10:44 2007
+# Linux kernel version: 2.6.24
+# Sat Feb  9 12:13:01 2008
 #
 CONFIG_MMU=y
 CONFIG_ZONE_DMA=y
 CONFIG_LOCKDEP_SUPPORT=y
 CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
 # CONFIG_ARCH_HAS_ILOG2_U32 is not set
 # CONFIG_ARCH_HAS_ILOG2_U64 is not set
@@ -15,6 +16,7 @@ CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_BUG=y
 CONFIG_NO_IOMEM=y
 CONFIG_NO_DMA=y
+CONFIG_GENERIC_LOCKBREAK=y
 CONFIG_S390=y
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
 
@@ -32,7 +34,6 @@ CONFIG_SYSVIPC_SYSCTL=y
 CONFIG_POSIX_MQUEUE=y
 # CONFIG_BSD_PROCESS_ACCT is not set
 # CONFIG_TASKSTATS is not set
-# CONFIG_USER_NS is not set
 CONFIG_AUDIT=y
 # CONFIG_AUDITSYSCALL is not set
 CONFIG_IKCONFIG=y
@@ -41,13 +42,19 @@ CONFIG_LOG_BUF_SHIFT=17
 CONFIG_CGROUPS=y
 # CONFIG_CGROUP_DEBUG is not set
 CONFIG_CGROUP_NS=y
-CONFIG_CGROUP_CPUACCT=y
 # CONFIG_CPUSETS is not set
 CONFIG_FAIR_GROUP_SCHED=y
 CONFIG_FAIR_USER_SCHED=y
 # CONFIG_FAIR_CGROUP_SCHED is not set
+# CONFIG_CGROUP_CPUACCT is not set
+# CONFIG_RESOURCE_COUNTERS is not set
 CONFIG_SYSFS_DEPRECATED=y
 # CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
@@ -61,17 +68,26 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 CONFIG_ELF_CORE=y
+# CONFIG_COMPAT_BRK is not set
 CONFIG_BASE_FULL=y
 CONFIG_FUTEX=y
 CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
 CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
 CONFIG_EVENTFD=y
 CONFIG_SHMEM=y
 CONFIG_VM_EVENT_COUNTERS=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
 CONFIG_RT_MUTEXES=y
 # CONFIG_TINY_SHMEM is not set
 CONFIG_BASE_SMALL=0
@@ -99,6 +115,8 @@ CONFIG_DEFAULT_DEADLINE=y
 # CONFIG_DEFAULT_CFQ is not set
 # CONFIG_DEFAULT_NOOP is not set
 CONFIG_DEFAULT_IOSCHED="deadline"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_PREEMPT_RCU is not set
 
 #
 # Base setup
@@ -137,7 +155,7 @@ CONFIG_ARCH_POPULATES_NODE_MAP=y
 # CONFIG_PREEMPT_NONE is not set
 # CONFIG_PREEMPT_VOLUNTARY is not set
 CONFIG_PREEMPT=y
-CONFIG_PREEMPT_BKL=y
+# CONFIG_RCU_TRACE is not set
 CONFIG_SELECT_MEMORY_MODEL=y
 CONFIG_FLATMEM_MANUAL=y
 # CONFIG_DISCONTIGMEM_MANUAL is not set
@@ -151,7 +169,6 @@ CONFIG_RESOURCES_64BIT=y
 CONFIG_ZONE_DMA_FLAG=1
 CONFIG_BOUNCE=y
 CONFIG_VIRT_TO_BUS=y
-CONFIG_HOLES_IN_ZONE=y
 
 #
 # I/O subsystem configuration
@@ -180,6 +197,7 @@ CONFIG_HZ_100=y
 # CONFIG_HZ_300 is not set
 # CONFIG_HZ_1000 is not set
 CONFIG_HZ=100
+# CONFIG_SCHED_HRTICK is not set
 CONFIG_NO_IDLE_HZ=y
 CONFIG_NO_IDLE_HZ_INIT=y
 CONFIG_S390_HYPFS_FS=y
@@ -201,6 +219,7 @@ CONFIG_XFRM=y
 # CONFIG_XFRM_USER is not set
 # CONFIG_XFRM_SUB_POLICY is not set
 # CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
 CONFIG_NET_KEY=y
 # CONFIG_NET_KEY_MIGRATE is not set
 CONFIG_IUCV=m
@@ -251,6 +270,7 @@ CONFIG_IPV6_SIT=y
 # CONFIG_NETWORK_SECMARK is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
 
 #
 # Core Netfilter Configuration
@@ -258,7 +278,6 @@ CONFIG_NETFILTER=y
 CONFIG_NETFILTER_NETLINK=m
 CONFIG_NETFILTER_NETLINK_QUEUE=m
 CONFIG_NETFILTER_NETLINK_LOG=m
-CONFIG_NF_CONNTRACK_ENABLED=m
 CONFIG_NF_CONNTRACK=m
 # CONFIG_NF_CT_ACCT is not set
 # CONFIG_NF_CONNTRACK_MARK is not set
@@ -286,7 +305,7 @@ CONFIG_NF_CONNTRACK=m
 # CONFIG_IP_NF_ARPTABLES is not set
 
 #
-# IPv6: Netfilter Configuration (EXPERIMENTAL)
+# IPv6: Netfilter Configuration
 #
 # CONFIG_NF_CONNTRACK_IPV6 is not set
 # CONFIG_IP6_NF_QUEUE is not set
@@ -343,6 +362,7 @@ CONFIG_NET_CLS_U32=m
 CONFIG_CLS_U32_MARK=y
 CONFIG_NET_CLS_RSVP=m
 CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
 # CONFIG_NET_EMATCH is not set
 CONFIG_NET_CLS_ACT=y
 CONFIG_NET_ACT_POLICE=y
@@ -351,7 +371,6 @@ CONFIG_NET_ACT_POLICE=y
 CONFIG_NET_ACT_NAT=m
 # CONFIG_NET_ACT_PEDIT is not set
 # CONFIG_NET_ACT_SIMP is not set
-CONFIG_NET_CLS_POLICE=y
 # CONFIG_NET_CLS_IND is not set
 CONFIG_NET_SCH_FIFO=y
 
@@ -360,6 +379,15 @@ CONFIG_NET_SCH_FIFO=y
 #
 # CONFIG_NET_PKTGEN is not set
 # CONFIG_NET_TCPPROBE is not set
+CONFIG_CAN=m
+CONFIG_CAN_RAW=m
+CONFIG_CAN_BCM=m
+
+#
+# CAN Device Drivers
+#
+CONFIG_CAN_VCAN=m
+# CONFIG_CAN_DEBUG_DEVICES is not set
 # CONFIG_AF_RXRPC is not set
 # CONFIG_RFKILL is not set
 # CONFIG_NET_9P is not set
@@ -389,7 +417,7 @@ CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_COUNT=16
 CONFIG_BLK_DEV_RAM_SIZE=4096
-CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+CONFIG_BLK_DEV_XIP=y
 # CONFIG_CDROM_PKTCDVD is not set
 # CONFIG_ATA_OVER_ETH is not set
 
@@ -406,6 +434,7 @@ CONFIG_DASD_DIAG=y
 CONFIG_DASD_EER=y
 CONFIG_MISC_DEVICES=y
 # CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
 
 #
 # SCSI device support
@@ -487,6 +516,7 @@ CONFIG_NET_ETHERNET=y
 # CONFIG_IBM_NEW_EMAC_TAH is not set
 # CONFIG_IBM_NEW_EMAC_EMAC4 is not set
 CONFIG_NETDEV_1000=y
+# CONFIG_E1000E_ENABLED is not set
 CONFIG_NETDEV_10000=y
 # CONFIG_TR is not set
 # CONFIG_WAN is not set
@@ -508,7 +538,6 @@ CONFIG_QETH=y
 CONFIG_CCWGROUP=y
 # CONFIG_PPP is not set
 # CONFIG_SLIP is not set
-# CONFIG_SHAPER is not set
 # CONFIG_NETCONSOLE is not set
 # CONFIG_NETPOLL is not set
 # CONFIG_NET_POLL_CONTROLLER is not set
@@ -558,6 +587,7 @@ CONFIG_S390_TAPE_34XX=m
 CONFIG_MONWRITER=m
 CONFIG_S390_VMUR=m
 # CONFIG_POWER_SUPPLY is not set
+# CONFIG_THERMAL is not set
 # CONFIG_WATCHDOG is not set
 
 #
@@ -584,12 +614,10 @@ CONFIG_FS_POSIX_ACL=y
 # CONFIG_XFS_FS is not set
 # CONFIG_GFS2_FS is not set
 # CONFIG_OCFS2_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
+CONFIG_DNOTIFY=y
 CONFIG_INOTIFY=y
 CONFIG_INOTIFY_USER=y
 # CONFIG_QUOTA is not set
-CONFIG_DNOTIFY=y
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_FUSE_FS is not set
@@ -632,8 +660,10 @@ CONFIG_CONFIGFS_FS=m
 # CONFIG_EFS_FS is not set
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
 # CONFIG_HPFS_FS is not set
 # CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
 CONFIG_NETWORK_FILESYSTEMS=y
@@ -686,16 +716,13 @@ CONFIG_MSDOS_PARTITION=y
 # CONFIG_NLS is not set
 CONFIG_DLM=m
 # CONFIG_DLM_DEBUG is not set
-CONFIG_INSTRUMENTATION=y
-# CONFIG_PROFILING is not set
-CONFIG_KPROBES=y
-# CONFIG_MARKERS is not set
 
 #
 # Kernel hacking
 #
 CONFIG_TRACE_IRQFLAGS_SUPPORT=y
 # CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
 CONFIG_ENABLE_MUST_CHECK=y
 CONFIG_MAGIC_SYSRQ=y
 # CONFIG_UNUSED_SYMBOLS is not set
@@ -721,12 +748,18 @@ CONFIG_DEBUG_BUGVERBOSE=y
 # CONFIG_DEBUG_INFO is not set
 # CONFIG_DEBUG_VM is not set
 # CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
 # CONFIG_FRAME_POINTER is not set
 CONFIG_FORCED_INLINING=y
 # CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_LKDTM is not set
 # CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
 CONFIG_SAMPLES=y
+# CONFIG_SAMPLE_KOBJECT is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
 
 #
 # Security options
@@ -738,6 +771,7 @@ CONFIG_CRYPTO=y
 CONFIG_CRYPTO_ALGAPI=y
 CONFIG_CRYPTO_AEAD=m
 CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_SEQIV=m
 CONFIG_CRYPTO_HASH=m
 CONFIG_CRYPTO_MANAGER=y
 CONFIG_CRYPTO_HMAC=m
@@ -745,17 +779,20 @@ CONFIG_CRYPTO_HMAC=m
 # CONFIG_CRYPTO_NULL is not set
 # CONFIG_CRYPTO_MD4 is not set
 CONFIG_CRYPTO_MD5=m
-# CONFIG_CRYPTO_SHA1 is not set
+CONFIG_CRYPTO_SHA1=m
 # CONFIG_CRYPTO_SHA256 is not set
 # CONFIG_CRYPTO_SHA512 is not set
 # CONFIG_CRYPTO_WP512 is not set
 # CONFIG_CRYPTO_TGR192 is not set
-# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_GF128MUL=m
 CONFIG_CRYPTO_ECB=m
 CONFIG_CRYPTO_CBC=y
 CONFIG_CRYPTO_PCBC=m
 # CONFIG_CRYPTO_LRW is not set
 # CONFIG_CRYPTO_XTS is not set
+CONFIG_CRYPTO_CTR=m
+CONFIG_CRYPTO_GCM=m
+CONFIG_CRYPTO_CCM=m
 # CONFIG_CRYPTO_CRYPTD is not set
 # CONFIG_CRYPTO_DES is not set
 CONFIG_CRYPTO_FCRYPT=m
@@ -770,20 +807,22 @@ CONFIG_CRYPTO_FCRYPT=m
 # CONFIG_CRYPTO_KHAZAD is not set
 # CONFIG_CRYPTO_ANUBIS is not set
 CONFIG_CRYPTO_SEED=m
+CONFIG_CRYPTO_SALSA20=m
 # CONFIG_CRYPTO_DEFLATE is not set
 # CONFIG_CRYPTO_MICHAEL_MIC is not set
 # CONFIG_CRYPTO_CRC32C is not set
 CONFIG_CRYPTO_CAMELLIA=m
 # CONFIG_CRYPTO_TEST is not set
 CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_LZO=m
 CONFIG_CRYPTO_HW=y
+CONFIG_ZCRYPT=m
+# CONFIG_ZCRYPT_MONOLITHIC is not set
 # CONFIG_CRYPTO_SHA1_S390 is not set
 # CONFIG_CRYPTO_SHA256_S390 is not set
 # CONFIG_CRYPTO_DES_S390 is not set
 # CONFIG_CRYPTO_AES_S390 is not set
 CONFIG_S390_PRNG=m
-CONFIG_ZCRYPT=m
-# CONFIG_ZCRYPT_MONOLITHIC is not set
 
 #
 # Library routines
@@ -794,5 +833,7 @@ CONFIG_BITREVERSE=m
 # CONFIG_CRC_ITU_T is not set
 CONFIG_CRC32=m
 CONFIG_CRC7=m
-# CONFIG_LIBCRC32C is not set
+CONFIG_LIBCRC32C=m
+CONFIG_LZO_COMPRESS=m
+CONFIG_LZO_DECOMPRESS=m
 CONFIG_PLIST=y
index f1e40ca..3e1c315 100644 (file)
@@ -134,6 +134,7 @@ static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
 }
 
 #include <asm/processor.h>
+#include <asm/pgalloc.h>
 #include <linux/module.h>
 #include <linux/elfcore.h>
 #include <linux/binfmts.h>
@@ -183,6 +184,16 @@ struct elf_prpsinfo32
 #undef start_thread
 #define start_thread                    start_thread31 
 
+static inline void start_thread31(struct pt_regs *regs, unsigned long new_psw,
+                                 unsigned long new_stackp)
+{
+       set_fs(USER_DS);
+       regs->psw.mask  = psw_user32_bits;
+       regs->psw.addr  = new_psw;
+       regs->gprs[15]  = new_stackp;
+       crst_table_downgrade(current->mm, 1UL << 31);
+}
+
 MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries,"
                    " Copyright 2000 IBM Corporation"); 
 MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>");
index 062c3d4..743d54f 100644 (file)
@@ -1712,3 +1712,23 @@ sys_fallocate_wrapper:
        sllg    %r5,%r6,32              # get high word of 64bit loff_t
        l       %r5,164(%r15)           # get low word of 64bit loff_t
        jg      sys_fallocate
+
+       .globl  sys_timerfd_create_wrapper
+sys_timerfd_create_wrapper:
+       lgfr    %r2,%r2                 # int
+       lgfr    %r3,%r3                 # int
+       jg      sys_timerfd_create
+
+       .globl  compat_sys_timerfd_settime_wrapper
+compat_sys_timerfd_settime_wrapper:
+       lgfr    %r2,%r2                 # int
+       lgfr    %r3,%r3                 # int
+       llgtr   %r4,%r4                 # struct compat_itimerspec *
+       llgtr   %r5,%r5                 # struct compat_itimerspec *
+       jg      compat_sys_timerfd_settime
+
+       .globl  compat_sys_timerfd_gettime_wrapper
+compat_sys_timerfd_gettime_wrapper:
+       lgfr    %r2,%r2                 # int
+       llgtr   %r3,%r3                 # struct compat_itimerspec *
+       jg      compat_sys_timerfd_gettime
index 0e7aca0..a6a4729 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/user.h>
-#include <linux/a.out.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/reboot.h>
index f9f8779..290e504 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/ptrace.h>
 #include <linux/slab.h>
 #include <linux/user.h>
-#include <linux/a.out.h>
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
index 25eac78..c87ec68 100644 (file)
@@ -327,3 +327,6 @@ SYSCALL(sys_utimensat,sys_utimensat,compat_sys_utimensat_wrapper)   /* 315 */
 SYSCALL(sys_signalfd,sys_signalfd,compat_sys_signalfd_wrapper)
 NI_SYSCALL                                             /* 317 old sys_timer_fd */
 SYSCALL(sys_eventfd,sys_eventfd,sys_eventfd_wrapper)
+SYSCALL(sys_timerfd_create,sys_timerfd_create,sys_timerfd_create_wrapper)
+SYSCALL(sys_timerfd_settime,sys_timerfd_settime,compat_sys_timerfd_settime_wrapper) /* 320 */
+SYSCALL(sys_timerfd_gettime,sys_timerfd_gettime,compat_sys_timerfd_gettime_wrapper)
index a4d2902..60f728a 100644 (file)
@@ -60,6 +60,7 @@ int sysctl_userprocess_debug = 0;
 extern pgm_check_handler_t do_protection_exception;
 extern pgm_check_handler_t do_dat_exception;
 extern pgm_check_handler_t do_monitor_call;
+extern pgm_check_handler_t do_asce_exception;
 
 #define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; })
 
@@ -730,7 +731,7 @@ void __init trap_init(void)
         pgm_check_table[0x12] = &translation_exception;
         pgm_check_table[0x13] = &special_op_exception;
 #ifdef CONFIG_64BIT
-        pgm_check_table[0x38] = &do_dat_exception;
+       pgm_check_table[0x38] = &do_asce_exception;
        pgm_check_table[0x39] = &do_dat_exception;
        pgm_check_table[0x3A] = &do_dat_exception;
         pgm_check_table[0x3B] = &do_dat_exception;
index 2456b52..ed13d42 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/s390_ext.h>
+#include <asm/mmu_context.h>
 
 #ifndef CONFIG_64BIT
 #define __FAIL_ADDR_MASK 0x7ffff000
@@ -444,6 +445,45 @@ void __kprobes do_dat_exception(struct pt_regs *regs, unsigned long error_code)
        do_exception(regs, error_code & 0xff, 0);
 }
 
+#ifdef CONFIG_64BIT
+void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
+{
+       struct mm_struct *mm;
+       struct vm_area_struct *vma;
+       unsigned long address;
+       int space;
+
+       mm = current->mm;
+       address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
+       space = check_space(current);
+
+       if (unlikely(space == 0 || in_atomic() || !mm))
+               goto no_context;
+
+       local_irq_enable();
+
+       down_read(&mm->mmap_sem);
+       vma = find_vma(mm, address);
+       up_read(&mm->mmap_sem);
+
+       if (vma) {
+               update_mm(mm, current);
+               return;
+       }
+
+       /* User mode accesses just cause a SIGSEGV */
+       if (regs->psw.mask & PSW_MASK_PSTATE) {
+               current->thread.prot_addr = address;
+               current->thread.trap_no = error_code;
+               do_sigsegv(regs, error_code, SEGV_MAPERR, address);
+               return;
+       }
+
+no_context:
+       do_no_context(regs, error_code, address);
+}
+#endif
+
 #ifdef CONFIG_PFAULT 
 /*
  * 'pfault' pseudo page faults routines.
index 983ec6e..8053245 100644 (file)
@@ -112,6 +112,7 @@ void __init paging_init(void)
        init_mm.pgd = swapper_pg_dir;
        S390_lowcore.kernel_asce = __pa(init_mm.pgd) & PAGE_MASK;
 #ifdef CONFIG_64BIT
+       /* A three level page table (4TB) is enough for the kernel space. */
        S390_lowcore.kernel_asce |= _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH;
        pgd_type = _REGION3_ENTRY_EMPTY;
 #else
@@ -184,7 +185,7 @@ void kernel_map_pages(struct page *page, int numpages, int enable)
                pmd = pmd_offset(pud, address);
                pte = pte_offset_kernel(pmd, address);
                if (!enable) {
-                       ptep_invalidate(address, pte);
+                       ptep_invalidate(&init_mm, address, pte);
                        continue;
                }
                *pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW));
index 356257c..5932a82 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/personality.h>
 #include <linux/mm.h>
 #include <linux/module.h>
+#include <asm/pgalloc.h>
 
 /*
  * Top of mmap area (just below the process stack).
@@ -62,6 +63,8 @@ static inline int mmap_is_legacy(void)
            current->signal->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY;
 }
 
+#ifndef CONFIG_64BIT
+
 /*
  * This function, called very early during the creation of a new
  * process VM image, sets up which VM layout function to use:
@@ -84,3 +87,65 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 }
 EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
 
+#else
+
+static unsigned long
+s390_get_unmapped_area(struct file *filp, unsigned long addr,
+               unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       int rc;
+
+       addr = arch_get_unmapped_area(filp, addr, len, pgoff, flags);
+       if (addr & ~PAGE_MASK)
+               return addr;
+       if (unlikely(mm->context.asce_limit < addr + len)) {
+               rc = crst_table_upgrade(mm, addr + len);
+               if (rc)
+                       return (unsigned long) rc;
+       }
+       return addr;
+}
+
+static unsigned long
+s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
+                         const unsigned long len, const unsigned long pgoff,
+                         const unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       unsigned long addr = addr0;
+       int rc;
+
+       addr = arch_get_unmapped_area_topdown(filp, addr, len, pgoff, flags);
+       if (addr & ~PAGE_MASK)
+               return addr;
+       if (unlikely(mm->context.asce_limit < addr + len)) {
+               rc = crst_table_upgrade(mm, addr + len);
+               if (rc)
+                       return (unsigned long) rc;
+       }
+       return addr;
+}
+/*
+ * This function, called very early during the creation of a new
+ * process VM image, sets up which VM layout function to use:
+ */
+void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+       /*
+        * Fall back to the standard layout if the personality
+        * bit is set, or if the expected stack growth is unlimited:
+        */
+       if (mmap_is_legacy()) {
+               mm->mmap_base = TASK_UNMAPPED_BASE;
+               mm->get_unmapped_area = s390_get_unmapped_area;
+               mm->unmap_area = arch_unmap_area;
+       } else {
+               mm->mmap_base = mmap_base();
+               mm->get_unmapped_area = s390_get_unmapped_area_topdown;
+               mm->unmap_area = arch_unmap_area_topdown;
+       }
+}
+EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
+
+#endif
index 019f518..fd07201 100644 (file)
 #include <asm/pgalloc.h>
 #include <asm/tlb.h>
 #include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
 
 #ifndef CONFIG_64BIT
 #define ALLOC_ORDER    1
+#define TABLES_PER_PAGE        4
+#define FRAG_MASK      15UL
+#define SECOND_HALVES  10UL
 #else
 #define ALLOC_ORDER    2
+#define TABLES_PER_PAGE        2
+#define FRAG_MASK      3UL
+#define SECOND_HALVES  2UL
 #endif
 
 unsigned long *crst_table_alloc(struct mm_struct *mm, int noexec)
@@ -45,52 +52,179 @@ unsigned long *crst_table_alloc(struct mm_struct *mm, int noexec)
                }
                page->index = page_to_phys(shadow);
        }
+       spin_lock(&mm->page_table_lock);
+       list_add(&page->lru, &mm->context.crst_list);
+       spin_unlock(&mm->page_table_lock);
        return (unsigned long *) page_to_phys(page);
 }
 
-void crst_table_free(unsigned long *table)
+void crst_table_free(struct mm_struct *mm, unsigned long *table)
 {
        unsigned long *shadow = get_shadow_table(table);
+       struct page *page = virt_to_page(table);
 
+       spin_lock(&mm->page_table_lock);
+       list_del(&page->lru);
+       spin_unlock(&mm->page_table_lock);
        if (shadow)
                free_pages((unsigned long) shadow, ALLOC_ORDER);
        free_pages((unsigned long) table, ALLOC_ORDER);
 }
 
+#ifdef CONFIG_64BIT
+int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
+{
+       unsigned long *table, *pgd;
+       unsigned long entry;
+
+       BUG_ON(limit > (1UL << 53));
+repeat:
+       table = crst_table_alloc(mm, mm->context.noexec);
+       if (!table)
+               return -ENOMEM;
+       spin_lock(&mm->page_table_lock);
+       if (mm->context.asce_limit < limit) {
+               pgd = (unsigned long *) mm->pgd;
+               if (mm->context.asce_limit <= (1UL << 31)) {
+                       entry = _REGION3_ENTRY_EMPTY;
+                       mm->context.asce_limit = 1UL << 42;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_REGION3;
+               } else {
+                       entry = _REGION2_ENTRY_EMPTY;
+                       mm->context.asce_limit = 1UL << 53;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_REGION2;
+               }
+               crst_table_init(table, entry);
+               pgd_populate(mm, (pgd_t *) table, (pud_t *) pgd);
+               mm->pgd = (pgd_t *) table;
+               table = NULL;
+       }
+       spin_unlock(&mm->page_table_lock);
+       if (table)
+               crst_table_free(mm, table);
+       if (mm->context.asce_limit < limit)
+               goto repeat;
+       update_mm(mm, current);
+       return 0;
+}
+
+void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
+{
+       pgd_t *pgd;
+
+       if (mm->context.asce_limit <= limit)
+               return;
+       __tlb_flush_mm(mm);
+       while (mm->context.asce_limit > limit) {
+               pgd = mm->pgd;
+               switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) {
+               case _REGION_ENTRY_TYPE_R2:
+                       mm->context.asce_limit = 1UL << 42;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_REGION3;
+                       break;
+               case _REGION_ENTRY_TYPE_R3:
+                       mm->context.asce_limit = 1UL << 31;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_SEGMENT;
+                       break;
+               default:
+                       BUG();
+               }
+               mm->pgd = (pgd_t *) (pgd_val(*pgd) & _REGION_ENTRY_ORIGIN);
+               crst_table_free(mm, (unsigned long *) pgd);
+       }
+       update_mm(mm, current);
+}
+#endif
+
 /*
  * page table entry allocation/free routines.
  */
-unsigned long *page_table_alloc(int noexec)
+unsigned long *page_table_alloc(struct mm_struct *mm)
 {
-       struct page *page = alloc_page(GFP_KERNEL);
+       struct page *page;
        unsigned long *table;
+       unsigned long bits;
 
-       if (!page)
-               return NULL;
-       page->index = 0;
-       if (noexec) {
-               struct page *shadow = alloc_page(GFP_KERNEL);
-               if (!shadow) {
-                       __free_page(page);
+       bits = mm->context.noexec ? 3UL : 1UL;
+       spin_lock(&mm->page_table_lock);
+       page = NULL;
+       if (!list_empty(&mm->context.pgtable_list)) {
+               page = list_first_entry(&mm->context.pgtable_list,
+                                       struct page, lru);
+               if ((page->flags & FRAG_MASK) == ((1UL << TABLES_PER_PAGE) - 1))
+                       page = NULL;
+       }
+       if (!page) {
+               spin_unlock(&mm->page_table_lock);
+               page = alloc_page(GFP_KERNEL|__GFP_REPEAT);
+               if (!page)
                        return NULL;
-               }
-               table = (unsigned long *) page_to_phys(shadow);
+               pgtable_page_ctor(page);
+               page->flags &= ~FRAG_MASK;
+               table = (unsigned long *) page_to_phys(page);
                clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
-               page->index = (addr_t) table;
+               spin_lock(&mm->page_table_lock);
+               list_add(&page->lru, &mm->context.pgtable_list);
        }
-       pgtable_page_ctor(page);
        table = (unsigned long *) page_to_phys(page);
-       clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
+       while (page->flags & bits) {
+               table += 256;
+               bits <<= 1;
+       }
+       page->flags |= bits;
+       if ((page->flags & FRAG_MASK) == ((1UL << TABLES_PER_PAGE) - 1))
+               list_move_tail(&page->lru, &mm->context.pgtable_list);
+       spin_unlock(&mm->page_table_lock);
        return table;
 }
 
-void page_table_free(unsigned long *table)
+void page_table_free(struct mm_struct *mm, unsigned long *table)
 {
-       unsigned long *shadow = get_shadow_pte(table);
+       struct page *page;
+       unsigned long bits;
 
-       pgtable_page_dtor(virt_to_page(table));
-       if (shadow)
-               free_page((unsigned long) shadow);
-       free_page((unsigned long) table);
+       bits = mm->context.noexec ? 3UL : 1UL;
+       bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long);
+       page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
+       spin_lock(&mm->page_table_lock);
+       page->flags ^= bits;
+       if (page->flags & FRAG_MASK) {
+               /* Page now has some free pgtable fragments. */
+               list_move(&page->lru, &mm->context.pgtable_list);
+               page = NULL;
+       } else
+               /* All fragments of the 4K page have been freed. */
+               list_del(&page->lru);
+       spin_unlock(&mm->page_table_lock);
+       if (page) {
+               pgtable_page_dtor(page);
+               __free_page(page);
+       }
+}
 
+void disable_noexec(struct mm_struct *mm, struct task_struct *tsk)
+{
+       struct page *page;
+
+       spin_lock(&mm->page_table_lock);
+       /* Free shadow region and segment tables. */
+       list_for_each_entry(page, &mm->context.crst_list, lru)
+               if (page->index) {
+                       free_pages((unsigned long) page->index, ALLOC_ORDER);
+                       page->index = 0;
+               }
+       /* "Free" second halves of page tables. */
+       list_for_each_entry(page, &mm->context.pgtable_list, lru)
+               page->flags &= ~SECOND_HALVES;
+       spin_unlock(&mm->page_table_lock);
+       mm->context.noexec = 0;
+       update_mm(mm, tsk);
 }
index 7c1287c..35d90a4 100644 (file)
@@ -69,7 +69,19 @@ static void __ref *vmem_alloc_pages(unsigned int order)
        return alloc_bootmem_pages((1 << order) * PAGE_SIZE);
 }
 
-#define vmem_pud_alloc()       ({ BUG(); ((pud_t *) NULL); })
+static inline pud_t *vmem_pud_alloc(void)
+{
+       pud_t *pud = NULL;
+
+#ifdef CONFIG_64BIT
+       pud = vmem_alloc_pages(2);
+       if (!pud)
+               return NULL;
+       pud_val(*pud) = _REGION3_ENTRY_EMPTY;
+       memcpy(pud + 1, pud, (PTRS_PER_PUD - 1)*sizeof(pud_t));
+#endif
+       return pud;
+}
 
 static inline pmd_t *vmem_pmd_alloc(void)
 {
@@ -84,13 +96,18 @@ static inline pmd_t *vmem_pmd_alloc(void)
        return pmd;
 }
 
-static inline pte_t *vmem_pte_alloc(void)
+static pte_t __init_refok *vmem_pte_alloc(void)
 {
-       pte_t *pte = vmem_alloc_pages(0);
+       pte_t *pte;
 
+       if (slab_is_available())
+               pte = (pte_t *) page_table_alloc(&init_mm);
+       else
+               pte = alloc_bootmem(PTRS_PER_PTE * sizeof(pte_t));
        if (!pte)
                return NULL;
-       clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY, PAGE_SIZE);
+       clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY,
+                   PTRS_PER_PTE * sizeof(pte_t));
        return pte;
 }
 
@@ -360,6 +377,9 @@ void __init vmem_map_init(void)
 {
        int i;
 
+       INIT_LIST_HEAD(&init_mm.context.crst_list);
+       INIT_LIST_HEAD(&init_mm.context.pgtable_list);
+       init_mm.context.noexec = 0;
        NODE_DATA(0)->node_mem_map = VMEM_MAP;
        for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++)
                vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size);
index 6e035d1..d87d4bf 100644 (file)
@@ -8,6 +8,7 @@ mainmenu "Linux/SuperH Kernel Configuration"
 config SUPERH
        def_bool y
        select EMBEDDED
+       select HAVE_IDE
        select HAVE_OPROFILE
        help
          The SuperH is a RISC processor targeted for use in embedded systems
index 7c674a3..c40343c 100644 (file)
@@ -69,6 +69,7 @@ config NR_CPUS
 config SPARC
        bool
        default y
+       select HAVE_IDE
        select HAVE_OPROFILE
 
 # Identify this as a Sparc32 build
index 4ac22f4..3af378d 100644 (file)
@@ -14,6 +14,7 @@ config SPARC
 config SPARC64
        bool
        default y
+       select HAVE_IDE
        help
          SPARC is a family of RISC microprocessors designed and marketed by
          Sun Microsystems, incorporated.  This port covers the newer 64-bit
index 7b6d371..4379f43 100644 (file)
@@ -78,6 +78,7 @@ config MCA
 config V850
        bool
        default y
+       select HAVE_IDE
 
 menu "Processor type and features"
 
index 65a70b7..aaed1a3 100644 (file)
@@ -18,6 +18,7 @@ config X86_64
 ### Arch settings
 config X86
        def_bool y
+       select HAVE_IDE
        select HAVE_OPROFILE
        select HAVE_KPROBES
 
@@ -102,6 +103,9 @@ config ARCH_HAS_ILOG2_U32
 config ARCH_HAS_ILOG2_U64
        def_bool n
 
+config ARCH_HAS_CPU_IDLE_WAIT
+       def_bool y
+
 config GENERIC_CALIBRATE_DELAY
        def_bool y
 
index 1846514..b1bdc4c 100644 (file)
@@ -3,3 +3,5 @@ bzImage
 setup
 setup.bin
 setup.elf
+cpustr.h
+mkcpustr
index fd36764..9fc8551 100644 (file)
@@ -14,6 +14,7 @@ config ZONE_DMA
 config XTENSA
        bool
        default y
+       select HAVE_IDE
        help
          Xtensa processors are 32-bit RISC machines designed by Tensilica
          primarily for embedded systems.  These processors are both
index b86877b..3a0e354 100644 (file)
@@ -80,6 +80,8 @@ source "drivers/usb/Kconfig"
 
 source "drivers/mmc/Kconfig"
 
+source "drivers/memstick/Kconfig"
+
 source "drivers/leds/Kconfig"
 
 source "drivers/infiniband/Kconfig"
index 30ba97e..e5e394a 100644 (file)
@@ -78,6 +78,7 @@ obj-y                         += lguest/
 obj-$(CONFIG_CPU_FREQ)         += cpufreq/
 obj-$(CONFIG_CPU_IDLE)         += cpuidle/
 obj-$(CONFIG_MMC)              += mmc/
+obj-$(CONFIG_MEMSTICK)         += memstick/
 obj-$(CONFIG_NEW_LEDS)         += leds/
 obj-$(CONFIG_INFINIBAND)       += infiniband/
 obj-$(CONFIG_SGI_SN)           += sn/
index f29812a..40b0fca 100644 (file)
@@ -60,5 +60,5 @@ obj-$(CONFIG_ACPI_ASUS)               += asus_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)     += toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)      += acpi_memhotplug.o
 obj-$(CONFIG_ACPI_PROCFS_POWER)        += cm_sbs.o
-obj-$(CONFIG_ACPI_SBS)         += sbs.o
 obj-$(CONFIG_ACPI_SBS)         += sbshc.o
+obj-$(CONFIG_ACPI_SBS)         += sbs.o
index 1194105..585ae3c 100644 (file)
@@ -827,7 +827,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
 #endif
        printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
               ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
-              battery->name, sbs->battery->present ? "present" : "absent");
+              battery->name, battery->present ? "present" : "absent");
        return result;
 }
 
index ae9a904..a2cf300 100644 (file)
@@ -117,6 +117,11 @@ static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol,
        int ret = -EFAULT, i;
        u8 temp, sz = 0;
 
+       if (!hc) {
+               printk(KERN_ERR PREFIX "host controller is not configured\n");
+               return ret;
+       }
+
        mutex_lock(&hc->lock);
        if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
                goto end;
@@ -292,6 +297,7 @@ static int acpi_smbus_hc_remove(struct acpi_device *device, int type)
        hc = acpi_driver_data(device);
        acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
        kfree(hc);
+       acpi_driver_data(device) = NULL;
        return 0;
 }
 
index a70c1c2..c452e2d 100644 (file)
@@ -657,7 +657,6 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
        if ((cmd = ub_get_cmd(lun)) == NULL)
                return -1;
        memset(cmd, 0, sizeof(struct ub_scsi_cmd));
-       sg_init_table(cmd->sgv, UB_MAX_REQ_SG);
 
        blkdev_dequeue_request(rq);
 
@@ -668,6 +667,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
        /*
         * get scatterlist from block layer
         */
+       sg_init_table(&urq->sgv[0], UB_MAX_REQ_SG);
        n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]);
        if (n_elem < 0) {
                /* Impossible, because blk_rq_map_sg should not hit ENOMEM. */
index f01ac9a..47c6be8 100644 (file)
@@ -875,7 +875,7 @@ config SONYPI
          Device which can be found in many (all ?) Sony Vaio laptops.
 
          If you have one of those laptops, read
-         <file:Documentation/sonypi.txt>, and say Y or M here.
+         <file:Documentation/laptops/sonypi.txt>, and say Y or M here.
 
          To compile this driver as a module, choose M here: the
          module will be called sonypi.
index 2c4b2d4..60f71e6 100644 (file)
@@ -27,6 +27,17 @@ static void (*pm_idle_old)(void);
 
 static int enabled_devices;
 
+#if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT)
+static void cpuidle_kick_cpus(void)
+{
+       cpu_idle_wait();
+}
+#elif defined(CONFIG_SMP)
+# error "Arch needs cpu_idle_wait() equivalent here"
+#else /* !CONFIG_ARCH_HAS_CPU_IDLE_WAIT && !CONFIG_SMP */
+static void cpuidle_kick_cpus(void) {}
+#endif
+
 /**
  * cpuidle_idle_call - the main idle loop
  *
@@ -83,7 +94,7 @@ void cpuidle_uninstall_idle_handler(void)
 {
        if (enabled_devices && (pm_idle != pm_idle_old)) {
                pm_idle = pm_idle_old;
-               cpu_idle_wait();
+               cpuidle_kick_cpus();
        }
 }
 
index ab8fb25..043c34a 100644 (file)
@@ -4,10 +4,14 @@
 # Andre Hedrick <andre@linux-ide.org>
 #
 
+# Select HAVE_IDE if IDE is supported
+config HAVE_IDE
+       def_bool n
+
 menuconfig IDE
        tristate "ATA/ATAPI/MFM/RLL support"
+       depends on HAVE_IDE
        depends on BLOCK
-       depends on HAS_IOMEM
        ---help---
          If you say Y here, your kernel will be able to manage low cost mass
          storage units such as ATA/(E)IDE and ATAPI units. The most common
index abbd38c..0f7a0bd 100644 (file)
@@ -13,7 +13,8 @@ config VIDEO_EM28XX
          module will be called em28xx
 
 config VIDEO_EM28XX_ALSA
-       depends on VIDEO_EM28XX
+       depends on VIDEO_EM28XX && SND
+       select SND_PCM
        tristate "Empia EM28xx ALSA audio module"
        ---help---
          This is an ALSA driver for some Empia 28xx based TV cards.
diff --git a/drivers/memstick/Kconfig b/drivers/memstick/Kconfig
new file mode 100644 (file)
index 0000000..1093fdb
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# MemoryStick subsystem configuration
+#
+
+menuconfig MEMSTICK
+       tristate "Sony MemoryStick card support (EXPERIMENTAL)"
+       help
+         Sony MemoryStick is a proprietary storage/extension card protocol.
+
+         If you want MemoryStick support, you should say Y here and also
+         to the specific driver for your MMC interface.
+
+if MEMSTICK
+
+config MEMSTICK_DEBUG
+       bool "MemoryStick debugging"
+       help
+         This is an option for use by developers; most people should
+         say N here.  This enables MemoryStick core and driver debugging.
+
+
+source "drivers/memstick/core/Kconfig"
+
+source "drivers/memstick/host/Kconfig"
+
+endif # MEMSTICK
diff --git a/drivers/memstick/Makefile b/drivers/memstick/Makefile
new file mode 100644 (file)
index 0000000..dc160fb
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile for the kernel MemoryStick device drivers.
+#
+
+ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
+       EXTRA_CFLAGS            += -DDEBUG
+endif
+
+obj-$(CONFIG_MEMSTICK)         += core/
+obj-$(CONFIG_MEMSTICK)         += host/
+
diff --git a/drivers/memstick/core/Kconfig b/drivers/memstick/core/Kconfig
new file mode 100644 (file)
index 0000000..95f1814
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# MemoryStick core configuration
+#
+
+comment "MemoryStick drivers"
+
+config MEMSTICK_UNSAFE_RESUME
+        bool "Allow unsafe resume (DANGEROUS)"
+        help
+          If you say Y here, the MemoryStick layer will assume that all
+          cards stayed in their respective slots during the suspend. The
+          normal behaviour is to remove them at suspend and
+          redetecting them at resume. Breaking this assumption will
+          in most cases result in data corruption.
+
+          This option is usually just for embedded systems which use
+          a MemoryStick card for rootfs. Most people should say N here.
+
+config MSPRO_BLOCK
+       tristate "MemoryStick Pro block device driver"
+       depends on BLOCK
+       help
+         Say Y here to enable the MemoryStick Pro block device driver
+         support. This provides a block device driver, which you can use
+         to mount the filesystem. Almost everyone wishing MemoryStick
+         support should say Y or M here.
diff --git a/drivers/memstick/core/Makefile b/drivers/memstick/core/Makefile
new file mode 100644 (file)
index 0000000..8b2b529
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile for the kernel MemoryStick core.
+#
+
+ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
+       EXTRA_CFLAGS            += -DDEBUG
+endif
+
+obj-$(CONFIG_MEMSTICK)         += memstick.o
+
+obj-$(CONFIG_MSPRO_BLOCK)      += mspro_block.o
diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c
new file mode 100644 (file)
index 0000000..bba467f
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ *  Sony MemoryStick support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Special thanks to Carlos Corbacho for providing various MemoryStick cards
+ * that made this driver possible.
+ *
+ */
+
+#include <linux/memstick.h>
+#include <linux/idr.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+
+#define DRIVER_NAME "memstick"
+#define DRIVER_VERSION "0.2"
+
+static unsigned int cmd_retries = 3;
+module_param(cmd_retries, uint, 0644);
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(memstick_host_idr);
+static DEFINE_SPINLOCK(memstick_host_lock);
+
+static int memstick_dev_match(struct memstick_dev *card,
+                             struct memstick_device_id *id)
+{
+       if (id->match_flags & MEMSTICK_MATCH_ALL) {
+               if ((id->type == card->id.type)
+                   && (id->category == card->id.category)
+                   && (id->class == card->id.class))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static int memstick_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                dev);
+       struct memstick_driver *ms_drv = container_of(drv,
+                                                     struct memstick_driver,
+                                                     driver);
+       struct memstick_device_id *ids = ms_drv->id_table;
+
+       if (ids) {
+               while (ids->match_flags) {
+                       if (memstick_dev_match(card, ids))
+                               return 1;
+                       ++ids;
+               }
+       }
+       return 0;
+}
+
+static int memstick_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+
+       if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "MEMSTICK_CATEGORY=%02X", card->id.category))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "MEMSTICK_CLASS=%02X", card->id.class))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int memstick_device_probe(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+       int rc = -ENODEV;
+
+       if (dev->driver && drv->probe) {
+               rc = drv->probe(card);
+               if (!rc)
+                       get_device(dev);
+       }
+       return rc;
+}
+
+static int memstick_device_remove(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+
+       if (dev->driver && drv->remove) {
+               drv->remove(card);
+               card->dev.driver = NULL;
+       }
+
+       put_device(dev);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int memstick_device_suspend(struct device *dev, pm_message_t state)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+
+       if (dev->driver && drv->suspend)
+               return drv->suspend(card, state);
+       return 0;
+}
+
+static int memstick_device_resume(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                 dev);
+       struct memstick_driver *drv = container_of(dev->driver,
+                                                  struct memstick_driver,
+                                                  driver);
+
+       if (dev->driver && drv->resume)
+               return drv->resume(card);
+       return 0;
+}
+
+#else
+
+#define memstick_device_suspend NULL
+#define memstick_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+#define MEMSTICK_ATTR(name, format)                                           \
+static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
+                           char *buf)                                        \
+{                                                                             \
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,    \
+                                                dev);                        \
+       return sprintf(buf, format, card->id.name);                           \
+}
+
+MEMSTICK_ATTR(type, "%02X");
+MEMSTICK_ATTR(category, "%02X");
+MEMSTICK_ATTR(class, "%02X");
+
+#define MEMSTICK_ATTR_RO(name) __ATTR(name, S_IRUGO, name##_show, NULL)
+
+static struct device_attribute memstick_dev_attrs[] = {
+       MEMSTICK_ATTR_RO(type),
+       MEMSTICK_ATTR_RO(category),
+       MEMSTICK_ATTR_RO(class),
+       __ATTR_NULL
+};
+
+static struct bus_type memstick_bus_type = {
+       .name           = "memstick",
+       .dev_attrs      = memstick_dev_attrs,
+       .match          = memstick_bus_match,
+       .uevent         = memstick_uevent,
+       .probe          = memstick_device_probe,
+       .remove         = memstick_device_remove,
+       .suspend        = memstick_device_suspend,
+       .resume         = memstick_device_resume
+};
+
+static void memstick_free(struct class_device *cdev)
+{
+       struct memstick_host *host = container_of(cdev, struct memstick_host,
+                                                 cdev);
+       kfree(host);
+}
+
+static struct class memstick_host_class = {
+       .name       = "memstick_host",
+       .release    = memstick_free
+};
+
+static void memstick_free_card(struct device *dev)
+{
+       struct memstick_dev *card = container_of(dev, struct memstick_dev,
+                                                dev);
+       kfree(card);
+}
+
+static int memstick_dummy_check(struct memstick_dev *card)
+{
+       return 0;
+}
+
+/**
+ * memstick_detect_change - schedule media detection on memstick host
+ * @host - host to use
+ */
+void memstick_detect_change(struct memstick_host *host)
+{
+       queue_work(workqueue, &host->media_checker);
+}
+EXPORT_SYMBOL(memstick_detect_change);
+
+/**
+ * memstick_next_req - called by host driver to obtain next request to process
+ * @host - host to use
+ * @mrq - pointer to stick the request to
+ *
+ * Host calls this function from idle state (*mrq == NULL) or after finishing
+ * previous request (*mrq should point to it). If previous request was
+ * unsuccessful, it is retried for predetermined number of times. Return value
+ * of 0 means that new request was assigned to the host.
+ */
+int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq)
+{
+       int rc = -ENXIO;
+
+       if ((*mrq) && (*mrq)->error && host->retries) {
+               (*mrq)->error = rc;
+               host->retries--;
+               return 0;
+       }
+
+       if (host->card && host->card->next_request)
+               rc = host->card->next_request(host->card, mrq);
+
+       if (!rc)
+               host->retries = cmd_retries;
+       else
+               *mrq = NULL;
+
+       return rc;
+}
+EXPORT_SYMBOL(memstick_next_req);
+
+/**
+ * memstick_new_req - notify the host that some requests are pending
+ * @host - host to use
+ */
+void memstick_new_req(struct memstick_host *host)
+{
+       host->retries = cmd_retries;
+       host->request(host);
+}
+EXPORT_SYMBOL(memstick_new_req);
+
+/**
+ * memstick_init_req_sg - set request fields needed for bulk data transfer
+ * @mrq - request to use
+ * @tpc - memstick Transport Protocol Command
+ * @sg - TPC argument
+ */
+void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
+                         struct scatterlist *sg)
+{
+       mrq->tpc = tpc;
+       if (tpc & 8)
+               mrq->data_dir = WRITE;
+       else
+               mrq->data_dir = READ;
+
+       mrq->sg = *sg;
+       mrq->io_type = MEMSTICK_IO_SG;
+
+       if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
+               mrq->need_card_int = 1;
+       else
+               mrq->need_card_int = 0;
+
+       mrq->get_int_reg = 0;
+}
+EXPORT_SYMBOL(memstick_init_req_sg);
+
+/**
+ * memstick_init_req - set request fields needed for short data transfer
+ * @mrq - request to use
+ * @tpc - memstick Transport Protocol Command
+ * @buf - TPC argument buffer
+ * @length - TPC argument size
+ *
+ * The intended use of this function (transfer of data items several bytes
+ * in size) allows us to just copy the value between request structure and
+ * user supplied buffer.
+ */
+void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
+                      void *buf, size_t length)
+{
+       mrq->tpc = tpc;
+       if (tpc & 8)
+               mrq->data_dir = WRITE;
+       else
+               mrq->data_dir = READ;
+
+       mrq->data_len = length > sizeof(mrq->data) ? sizeof(mrq->data) : length;
+       if (mrq->data_dir == WRITE)
+               memcpy(mrq->data, buf, mrq->data_len);
+
+       mrq->io_type = MEMSTICK_IO_VAL;
+
+       if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
+               mrq->need_card_int = 1;
+       else
+               mrq->need_card_int = 0;
+
+       mrq->get_int_reg = 0;
+}
+EXPORT_SYMBOL(memstick_init_req);
+
+/*
+ * Functions prefixed with "h_" are protocol callbacks. They can be called from
+ * interrupt context. Return value of 0 means that request processing is still
+ * ongoing, while special error value of -EAGAIN means that current request is
+ * finished (and request processor should come back some time later).
+ */
+
+static int h_memstick_read_dev_id(struct memstick_dev *card,
+                                 struct memstick_request **mrq)
+{
+       struct ms_id_register id_reg;
+
+       if (!(*mrq)) {
+               memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL,
+                                 sizeof(struct ms_id_register));
+               *mrq = &card->current_mrq;
+               return 0;
+       } else {
+               if (!(*mrq)->error) {
+                       memcpy(&id_reg, (*mrq)->data, sizeof(id_reg));
+                       card->id.match_flags = MEMSTICK_MATCH_ALL;
+                       card->id.type = id_reg.type;
+                       card->id.category = id_reg.category;
+                       card->id.class = id_reg.class;
+               }
+               complete(&card->mrq_complete);
+               return -EAGAIN;
+       }
+}
+
+static int h_memstick_set_rw_addr(struct memstick_dev *card,
+                                 struct memstick_request **mrq)
+{
+       if (!(*mrq)) {
+               memstick_init_req(&card->current_mrq, MS_TPC_SET_RW_REG_ADRS,
+                                 (char *)&card->reg_addr,
+                                 sizeof(card->reg_addr));
+               *mrq = &card->current_mrq;
+               return 0;
+       } else {
+               complete(&card->mrq_complete);
+               return -EAGAIN;
+       }
+}
+
+/**
+ * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to
+ *                        complete
+ * @card - media device to use
+ */
+int memstick_set_rw_addr(struct memstick_dev *card)
+{
+       card->next_request = h_memstick_set_rw_addr;
+       memstick_new_req(card->host);
+       wait_for_completion(&card->mrq_complete);
+
+       return card->current_mrq.error;
+}
+EXPORT_SYMBOL(memstick_set_rw_addr);
+
+static struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
+{
+       struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev),
+                                           GFP_KERNEL);
+       struct memstick_dev *old_card = host->card;
+       struct ms_id_register id_reg;
+
+       if (card) {
+               card->host = host;
+               snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
+                        "%s", host->cdev.class_id);
+               card->dev.parent = host->cdev.dev;
+               card->dev.bus = &memstick_bus_type;
+               card->dev.release = memstick_free_card;
+               card->check = memstick_dummy_check;
+
+               card->reg_addr.r_offset = offsetof(struct ms_register, id);
+               card->reg_addr.r_length = sizeof(id_reg);
+               card->reg_addr.w_offset = offsetof(struct ms_register, id);
+               card->reg_addr.w_length = sizeof(id_reg);
+
+               init_completion(&card->mrq_complete);
+
+               host->card = card;
+               if (memstick_set_rw_addr(card))
+                       goto err_out;
+
+               card->next_request = h_memstick_read_dev_id;
+               memstick_new_req(host);
+               wait_for_completion(&card->mrq_complete);
+
+               if (card->current_mrq.error)
+                       goto err_out;
+       }
+       host->card = old_card;
+       return card;
+err_out:
+       host->card = old_card;
+       kfree(card);
+       return NULL;
+}
+
+static void memstick_power_on(struct memstick_host *host)
+{
+       host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
+       host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+       msleep(1);
+}
+
+static void memstick_check(struct work_struct *work)
+{
+       struct memstick_host *host = container_of(work, struct memstick_host,
+                                                 media_checker);
+       struct memstick_dev *card;
+
+       dev_dbg(host->cdev.dev, "memstick_check started\n");
+       mutex_lock(&host->lock);
+       if (!host->card)
+               memstick_power_on(host);
+
+       card = memstick_alloc_card(host);
+
+       if (!card) {
+               if (host->card) {
+                       device_unregister(&host->card->dev);
+                       host->card = NULL;
+               }
+       } else {
+               dev_dbg(host->cdev.dev, "new card %02x, %02x, %02x\n",
+                       card->id.type, card->id.category, card->id.class);
+               if (host->card) {
+                       if (memstick_set_rw_addr(host->card)
+                           || !memstick_dev_match(host->card, &card->id)
+                           || !(host->card->check(host->card))) {
+                               device_unregister(&host->card->dev);
+                               host->card = NULL;
+                       }
+               }
+
+               if (!host->card) {
+                       host->card = card;
+                       if (device_register(&card->dev)) {
+                               kfree(host->card);
+                               host->card = NULL;
+                       }
+               } else
+                       kfree(card);
+       }
+
+       if (!host->card)
+               host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+
+       mutex_unlock(&host->lock);
+       dev_dbg(host->cdev.dev, "memstick_check finished\n");
+}
+
+/**
+ * memstick_alloc_host - allocate a memstick_host structure
+ * @extra: size of the user private data to allocate
+ * @dev: parent device of the host
+ */
+struct memstick_host *memstick_alloc_host(unsigned int extra,
+                                         struct device *dev)
+{
+       struct memstick_host *host;
+
+       host = kzalloc(sizeof(struct memstick_host) + extra, GFP_KERNEL);
+       if (host) {
+               mutex_init(&host->lock);
+               INIT_WORK(&host->media_checker, memstick_check);
+               host->cdev.class = &memstick_host_class;
+               host->cdev.dev = dev;
+               class_device_initialize(&host->cdev);
+       }
+       return host;
+}
+EXPORT_SYMBOL(memstick_alloc_host);
+
+/**
+ * memstick_add_host - start request processing on memstick host
+ * @host - host to use
+ */
+int memstick_add_host(struct memstick_host *host)
+{
+       int rc;
+
+       if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL))
+               return -ENOMEM;
+
+       spin_lock(&memstick_host_lock);
+       rc = idr_get_new(&memstick_host_idr, host, &host->id);
+       spin_unlock(&memstick_host_lock);
+       if (rc)
+               return rc;
+
+       snprintf(host->cdev.class_id, BUS_ID_SIZE,
+                "memstick%u", host->id);
+
+       rc = class_device_add(&host->cdev);
+       if (rc) {
+               spin_lock(&memstick_host_lock);
+               idr_remove(&memstick_host_idr, host->id);
+               spin_unlock(&memstick_host_lock);
+               return rc;
+       }
+
+       host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+       memstick_detect_change(host);
+       return 0;
+}
+EXPORT_SYMBOL(memstick_add_host);
+
+/**
+ * memstick_remove_host - stop request processing on memstick host
+ * @host - host to use
+ */
+void memstick_remove_host(struct memstick_host *host)
+{
+       flush_workqueue(workqueue);
+       mutex_lock(&host->lock);
+       if (host->card)
+               device_unregister(&host->card->dev);
+       host->card = NULL;
+       host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
+       mutex_unlock(&host->lock);
+
+       spin_lock(&memstick_host_lock);
+       idr_remove(&memstick_host_idr, host->id);
+       spin_unlock(&memstick_host_lock);
+       class_device_del(&host->cdev);
+}
+EXPORT_SYMBOL(memstick_remove_host);
+
+/**
+ * memstick_free_host - free memstick host
+ * @host - host to use
+ */
+void memstick_free_host(struct memstick_host *host)
+{
+       mutex_destroy(&host->lock);
+       class_device_put(&host->cdev);
+}
+EXPORT_SYMBOL(memstick_free_host);
+
+int memstick_register_driver(struct memstick_driver *drv)
+{
+       drv->driver.bus = &memstick_bus_type;
+
+       return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(memstick_register_driver);
+
+void memstick_unregister_driver(struct memstick_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(memstick_unregister_driver);
+
+
+static int __init memstick_init(void)
+{
+       int rc;
+
+       workqueue = create_freezeable_workqueue("kmemstick");
+       if (!workqueue)
+               return -ENOMEM;
+
+       rc = bus_register(&memstick_bus_type);
+       if (!rc)
+               rc = class_register(&memstick_host_class);
+
+       if (!rc)
+               return 0;
+
+       bus_unregister(&memstick_bus_type);
+       destroy_workqueue(workqueue);
+
+       return rc;
+}
+
+static void __exit memstick_exit(void)
+{
+       class_unregister(&memstick_host_class);
+       bus_unregister(&memstick_bus_type);
+       destroy_workqueue(workqueue);
+       idr_destroy(&memstick_host_idr);
+}
+
+module_init(memstick_init);
+module_exit(memstick_exit);
+
+MODULE_AUTHOR("Alex Dubov");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Sony MemoryStick core driver");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
new file mode 100644 (file)
index 0000000..423ad8c
--- /dev/null
@@ -0,0 +1,1351 @@
+/*
+ *  Sony MemoryStick Pro storage support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Special thanks to Carlos Corbacho for providing various MemoryStick cards
+ * that made this driver possible.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/idr.h>
+#include <linux/hdreg.h>
+#include <linux/kthread.h>
+#include <linux/memstick.h>
+
+#define DRIVER_NAME "mspro_block"
+#define DRIVER_VERSION "0.2"
+
+static int major;
+module_param(major, int, 0644);
+
+#define MSPRO_BLOCK_MAX_SEGS  32
+#define MSPRO_BLOCK_MAX_PAGES ((2 << 16) - 1)
+
+#define MSPRO_BLOCK_SIGNATURE        0xa5c3
+#define MSPRO_BLOCK_MAX_ATTRIBUTES   41
+
+enum {
+       MSPRO_BLOCK_ID_SYSINFO         = 0x10,
+       MSPRO_BLOCK_ID_MODELNAME       = 0x15,
+       MSPRO_BLOCK_ID_MBR             = 0x20,
+       MSPRO_BLOCK_ID_PBR16           = 0x21,
+       MSPRO_BLOCK_ID_PBR32           = 0x22,
+       MSPRO_BLOCK_ID_SPECFILEVALUES1 = 0x25,
+       MSPRO_BLOCK_ID_SPECFILEVALUES2 = 0x26,
+       MSPRO_BLOCK_ID_DEVINFO         = 0x30
+};
+
+struct mspro_sys_attr {
+       size_t                  size;
+       void                    *data;
+       unsigned char           id;
+       char                    name[32];
+       struct device_attribute dev_attr;
+};
+
+struct mspro_attr_entry {
+       unsigned int  address;
+       unsigned int  size;
+       unsigned char id;
+       unsigned char reserved[3];
+} __attribute__((packed));
+
+struct mspro_attribute {
+       unsigned short          signature;
+       unsigned short          version;
+       unsigned char           count;
+       unsigned char           reserved[11];
+       struct mspro_attr_entry entries[];
+} __attribute__((packed));
+
+struct mspro_sys_info {
+       unsigned char  class;
+       unsigned char  reserved0;
+       unsigned short block_size;
+       unsigned short block_count;
+       unsigned short user_block_count;
+       unsigned short page_size;
+       unsigned char  reserved1[2];
+       unsigned char  assembly_date[8];
+       unsigned int   serial_number;
+       unsigned char  assembly_maker_code;
+       unsigned char  assembly_model_code[3];
+       unsigned short memory_maker_code;
+       unsigned short memory_model_code;
+       unsigned char  reserved2[4];
+       unsigned char  vcc;
+       unsigned char  vpp;
+       unsigned short controller_number;
+       unsigned short controller_function;
+       unsigned short start_sector;
+       unsigned short unit_size;
+       unsigned char  ms_sub_class;
+       unsigned char  reserved3[4];
+       unsigned char  interface_type;
+       unsigned short controller_code;
+       unsigned char  format_type;
+       unsigned char  reserved4;
+       unsigned char  device_type;
+       unsigned char  reserved5[7];
+       unsigned char  mspro_id[16];
+       unsigned char  reserved6[16];
+} __attribute__((packed));
+
+struct mspro_mbr {
+       unsigned char boot_partition;
+       unsigned char start_head;
+       unsigned char start_sector;
+       unsigned char start_cylinder;
+       unsigned char partition_type;
+       unsigned char end_head;
+       unsigned char end_sector;
+       unsigned char end_cylinder;
+       unsigned int  start_sectors;
+       unsigned int  sectors_per_partition;
+} __attribute__((packed));
+
+struct mspro_devinfo {
+       unsigned short cylinders;
+       unsigned short heads;
+       unsigned short bytes_per_track;
+       unsigned short bytes_per_sector;
+       unsigned short sectors_per_track;
+       unsigned char  reserved[6];
+} __attribute__((packed));
+
+struct mspro_block_data {
+       struct memstick_dev   *card;
+       unsigned int          usage_count;
+       struct gendisk        *disk;
+       struct request_queue  *queue;
+       spinlock_t            q_lock;
+       wait_queue_head_t     q_wait;
+       struct task_struct    *q_thread;
+
+       unsigned short        page_size;
+       unsigned short        cylinders;
+       unsigned short        heads;
+       unsigned short        sectors_per_track;
+
+       unsigned char         system;
+       unsigned char         read_only:1,
+                             active:1,
+                             has_request:1,
+                             data_dir:1;
+       unsigned char         transfer_cmd;
+
+       int                   (*mrq_handler)(struct memstick_dev *card,
+                                            struct memstick_request **mrq);
+
+       struct attribute_group attr_group;
+
+       struct scatterlist    req_sg[MSPRO_BLOCK_MAX_SEGS];
+       unsigned int          seg_count;
+       unsigned int          current_seg;
+       unsigned short        current_page;
+};
+
+static DEFINE_IDR(mspro_block_disk_idr);
+static DEFINE_MUTEX(mspro_block_disk_lock);
+
+/*** Block device ***/
+
+static int mspro_block_bd_open(struct inode *inode, struct file *filp)
+{
+       struct gendisk *disk = inode->i_bdev->bd_disk;
+       struct mspro_block_data *msb = disk->private_data;
+       int rc = -ENXIO;
+
+       mutex_lock(&mspro_block_disk_lock);
+
+       if (msb && msb->card) {
+               msb->usage_count++;
+               if ((filp->f_mode & FMODE_WRITE) && msb->read_only)
+                       rc = -EROFS;
+               else
+                       rc = 0;
+       }
+
+       mutex_unlock(&mspro_block_disk_lock);
+
+       return rc;
+}
+
+
+static int mspro_block_disk_release(struct gendisk *disk)
+{
+       struct mspro_block_data *msb = disk->private_data;
+       int disk_id = disk->first_minor >> MEMSTICK_PART_SHIFT;
+
+       mutex_lock(&mspro_block_disk_lock);
+
+       if (msb->usage_count) {
+               msb->usage_count--;
+               if (!msb->usage_count) {
+                       kfree(msb);
+                       disk->private_data = NULL;
+                       idr_remove(&mspro_block_disk_idr, disk_id);
+                       put_disk(disk);
+               }
+       }
+
+       mutex_unlock(&mspro_block_disk_lock);
+
+       return 0;
+}
+
+static int mspro_block_bd_release(struct inode *inode, struct file *filp)
+{
+       struct gendisk *disk = inode->i_bdev->bd_disk;
+       return mspro_block_disk_release(disk);
+}
+
+static int mspro_block_bd_getgeo(struct block_device *bdev,
+                                struct hd_geometry *geo)
+{
+       struct mspro_block_data *msb = bdev->bd_disk->private_data;
+
+       geo->heads = msb->heads;
+       geo->sectors = msb->sectors_per_track;
+       geo->cylinders = msb->cylinders;
+
+       return 0;
+}
+
+static struct block_device_operations ms_block_bdops = {
+       .open    = mspro_block_bd_open,
+       .release = mspro_block_bd_release,
+       .getgeo  = mspro_block_bd_getgeo,
+       .owner   = THIS_MODULE
+};
+
+/*** Information ***/
+
+static struct mspro_sys_attr *mspro_from_sysfs_attr(struct attribute *attr)
+{
+       struct device_attribute *dev_attr
+               = container_of(attr, struct device_attribute, attr);
+       return container_of(dev_attr, struct mspro_sys_attr, dev_attr);
+}
+
+static const char *mspro_block_attr_name(unsigned char tag)
+{
+       switch (tag) {
+       case MSPRO_BLOCK_ID_SYSINFO:
+               return "attr_sysinfo";
+       case MSPRO_BLOCK_ID_MODELNAME:
+               return "attr_modelname";
+       case MSPRO_BLOCK_ID_MBR:
+               return "attr_mbr";
+       case MSPRO_BLOCK_ID_PBR16:
+               return "attr_pbr16";
+       case MSPRO_BLOCK_ID_PBR32:
+               return "attr_pbr32";
+       case MSPRO_BLOCK_ID_SPECFILEVALUES1:
+               return "attr_specfilevalues1";
+       case MSPRO_BLOCK_ID_SPECFILEVALUES2:
+               return "attr_specfilevalues2";
+       case MSPRO_BLOCK_ID_DEVINFO:
+               return "attr_devinfo";
+       default:
+               return NULL;
+       };
+}
+
+typedef ssize_t (*sysfs_show_t)(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buffer);
+
+static ssize_t mspro_block_attr_show_default(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buffer)
+{
+       struct mspro_sys_attr *s_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+
+       ssize_t cnt, rc = 0;
+
+       for (cnt = 0; cnt < s_attr->size; cnt++) {
+               if (cnt && !(cnt % 16)) {
+                       if (PAGE_SIZE - rc)
+                               buffer[rc++] = '\n';
+               }
+
+               rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "%02x ",
+                               ((unsigned char *)s_attr->data)[cnt]);
+       }
+       return rc;
+}
+
+static ssize_t mspro_block_attr_show_sysinfo(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buffer)
+{
+       struct mspro_sys_attr *x_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+       struct mspro_sys_info *x_sys = x_attr->data;
+       ssize_t rc = 0;
+
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "class: %x\n",
+                       x_sys->class);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block size: %x\n",
+                       be16_to_cpu(x_sys->block_size));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block count: %x\n",
+                       be16_to_cpu(x_sys->block_count));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "user block count: %x\n",
+                       be16_to_cpu(x_sys->user_block_count));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "page size: %x\n",
+                       be16_to_cpu(x_sys->page_size));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly date: "
+                       "%d %04u-%02u-%02u %02u:%02u:%02u\n",
+                       x_sys->assembly_date[0],
+                       be16_to_cpu(*(unsigned short *)
+                                   &x_sys->assembly_date[1]),
+                       x_sys->assembly_date[3], x_sys->assembly_date[4],
+                       x_sys->assembly_date[5], x_sys->assembly_date[6],
+                       x_sys->assembly_date[7]);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "serial number: %x\n",
+                       be32_to_cpu(x_sys->serial_number));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc,
+                       "assembly maker code: %x\n",
+                       x_sys->assembly_maker_code);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly model code: "
+                       "%02x%02x%02x\n", x_sys->assembly_model_code[0],
+                       x_sys->assembly_model_code[1],
+                       x_sys->assembly_model_code[2]);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory maker code: %x\n",
+                       be16_to_cpu(x_sys->memory_maker_code));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory model code: %x\n",
+                       be16_to_cpu(x_sys->memory_model_code));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vcc: %x\n",
+                       x_sys->vcc);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vpp: %x\n",
+                       x_sys->vpp);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller number: %x\n",
+                       be16_to_cpu(x_sys->controller_number));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc,
+                       "controller function: %x\n",
+                       be16_to_cpu(x_sys->controller_function));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n",
+                       be16_to_cpu(x_sys->start_sector));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "unit size: %x\n",
+                       be16_to_cpu(x_sys->unit_size));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sub class: %x\n",
+                       x_sys->ms_sub_class);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "interface type: %x\n",
+                       x_sys->interface_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller code: %x\n",
+                       be16_to_cpu(x_sys->controller_code));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "format type: %x\n",
+                       x_sys->format_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "device type: %x\n",
+                       x_sys->device_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "mspro id: %s\n",
+                       x_sys->mspro_id);
+       return rc;
+}
+
+static ssize_t mspro_block_attr_show_modelname(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buffer)
+{
+       struct mspro_sys_attr *s_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+
+       return scnprintf(buffer, PAGE_SIZE, "%s", (char *)s_attr->data);
+}
+
+static ssize_t mspro_block_attr_show_mbr(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buffer)
+{
+       struct mspro_sys_attr *x_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+       struct mspro_mbr *x_mbr = x_attr->data;
+       ssize_t rc = 0;
+
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "boot partition: %x\n",
+                       x_mbr->boot_partition);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start head: %x\n",
+                       x_mbr->start_head);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n",
+                       x_mbr->start_sector);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start cylinder: %x\n",
+                       x_mbr->start_cylinder);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "partition type: %x\n",
+                       x_mbr->partition_type);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end head: %x\n",
+                       x_mbr->end_head);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end sector: %x\n",
+                       x_mbr->end_sector);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end cylinder: %x\n",
+                       x_mbr->end_cylinder);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sectors: %x\n",
+                       x_mbr->start_sectors);
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc,
+                       "sectors per partition: %x\n",
+                       x_mbr->sectors_per_partition);
+       return rc;
+}
+
+static ssize_t mspro_block_attr_show_devinfo(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buffer)
+{
+       struct mspro_sys_attr *x_attr = container_of(attr,
+                                                    struct mspro_sys_attr,
+                                                    dev_attr);
+       struct mspro_devinfo *x_devinfo = x_attr->data;
+       ssize_t rc = 0;
+
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "cylinders: %x\n",
+                       be16_to_cpu(x_devinfo->cylinders));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "heads: %x\n",
+                       be16_to_cpu(x_devinfo->heads));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per track: %x\n",
+                       be16_to_cpu(x_devinfo->bytes_per_track));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per sector: %x\n",
+                       be16_to_cpu(x_devinfo->bytes_per_sector));
+       rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sectors per track: %x\n",
+                       be16_to_cpu(x_devinfo->sectors_per_track));
+       return rc;
+}
+
+static sysfs_show_t mspro_block_attr_show(unsigned char tag)
+{
+       switch (tag) {
+       case MSPRO_BLOCK_ID_SYSINFO:
+               return mspro_block_attr_show_sysinfo;
+       case MSPRO_BLOCK_ID_MODELNAME:
+               return mspro_block_attr_show_modelname;
+       case MSPRO_BLOCK_ID_MBR:
+               return mspro_block_attr_show_mbr;
+       case MSPRO_BLOCK_ID_DEVINFO:
+               return mspro_block_attr_show_devinfo;
+       default:
+               return mspro_block_attr_show_default;
+       }
+}
+
+/*** Protocol handlers ***/
+
+/*
+ * Functions prefixed with "h_" are protocol callbacks. They can be called from
+ * interrupt context. Return value of 0 means that request processing is still
+ * ongoing, while special error value of -EAGAIN means that current request is
+ * finished (and request processor should come back some time later).
+ */
+
+static int h_mspro_block_req_init(struct memstick_dev *card,
+                                 struct memstick_request **mrq)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+       *mrq = &card->current_mrq;
+       card->next_request = msb->mrq_handler;
+       return 0;
+}
+
+static int h_mspro_block_default(struct memstick_dev *card,
+                                struct memstick_request **mrq)
+{
+       complete(&card->mrq_complete);
+       if (!(*mrq)->error)
+               return -EAGAIN;
+       else
+               return (*mrq)->error;
+}
+
+static int h_mspro_block_get_ro(struct memstick_dev *card,
+                               struct memstick_request **mrq)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+       if ((*mrq)->error) {
+               complete(&card->mrq_complete);
+               return (*mrq)->error;
+       }
+
+       if ((*mrq)->data[offsetof(struct ms_status_register, status0)]
+           & MEMSTICK_STATUS0_WP)
+               msb->read_only = 1;
+       else
+               msb->read_only = 0;
+
+       complete(&card->mrq_complete);
+       return -EAGAIN;
+}
+
+static int h_mspro_block_wait_for_ced(struct memstick_dev *card,
+                                     struct memstick_request **mrq)
+{
+       if ((*mrq)->error) {
+               complete(&card->mrq_complete);
+               return (*mrq)->error;
+       }
+
+       dev_dbg(&card->dev, "wait for ced: value %x\n", (*mrq)->data[0]);
+
+       if ((*mrq)->data[0] & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) {
+               card->current_mrq.error = -EFAULT;
+               complete(&card->mrq_complete);
+               return card->current_mrq.error;
+       }
+
+       if (!((*mrq)->data[0] & MEMSTICK_INT_CED))
+               return 0;
+       else {
+               card->current_mrq.error = 0;
+               complete(&card->mrq_complete);
+               return -EAGAIN;
+       }
+}
+
+static int h_mspro_block_transfer_data(struct memstick_dev *card,
+                                      struct memstick_request **mrq)
+{
+       struct memstick_host *host = card->host;
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       unsigned char t_val = 0;
+       struct scatterlist t_sg = { 0 };
+       size_t t_offset;
+
+       if ((*mrq)->error) {
+               complete(&card->mrq_complete);
+               return (*mrq)->error;
+       }
+
+       switch ((*mrq)->tpc) {
+       case MS_TPC_WRITE_REG:
+               memstick_init_req(*mrq, MS_TPC_SET_CMD, &msb->transfer_cmd, 1);
+               (*mrq)->get_int_reg = 1;
+               return 0;
+       case MS_TPC_SET_CMD:
+               t_val = (*mrq)->int_reg;
+               memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
+               if (host->caps & MEMSTICK_CAP_AUTO_GET_INT)
+                       goto has_int_reg;
+               return 0;
+       case MS_TPC_GET_INT:
+               t_val = (*mrq)->data[0];
+has_int_reg:
+               if (t_val & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) {
+                       t_val = MSPRO_CMD_STOP;
+                       memstick_init_req(*mrq, MS_TPC_SET_CMD, &t_val, 1);
+                       card->next_request = h_mspro_block_default;
+                       return 0;
+               }
+
+               if (msb->current_page
+                   == (msb->req_sg[msb->current_seg].length
+                       / msb->page_size)) {
+                       msb->current_page = 0;
+                       msb->current_seg++;
+
+                       if (msb->current_seg == msb->seg_count) {
+                               if (t_val & MEMSTICK_INT_CED) {
+                                       complete(&card->mrq_complete);
+                                       return -EAGAIN;
+                               } else {
+                                       card->next_request
+                                               = h_mspro_block_wait_for_ced;
+                                       memstick_init_req(*mrq, MS_TPC_GET_INT,
+                                                         NULL, 1);
+                                       return 0;
+                               }
+                       }
+               }
+
+               if (!(t_val & MEMSTICK_INT_BREQ)) {
+                       memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
+                       return 0;
+               }
+
+               t_offset = msb->req_sg[msb->current_seg].offset;
+               t_offset += msb->current_page * msb->page_size;
+
+               sg_set_page(&t_sg,
+                           nth_page(sg_page(&(msb->req_sg[msb->current_seg])),
+                                    t_offset >> PAGE_SHIFT),
+                           msb->page_size, offset_in_page(t_offset));
+
+               memstick_init_req_sg(*mrq, msb->data_dir == READ
+                                          ? MS_TPC_READ_LONG_DATA
+                                          : MS_TPC_WRITE_LONG_DATA,
+                                    &t_sg);
+               (*mrq)->get_int_reg = 1;
+               return 0;
+       case MS_TPC_READ_LONG_DATA:
+       case MS_TPC_WRITE_LONG_DATA:
+               msb->current_page++;
+               if (host->caps & MEMSTICK_CAP_AUTO_GET_INT) {
+                       t_val = (*mrq)->int_reg;
+                       goto has_int_reg;
+               } else {
+                       memstick_init_req(*mrq, MS_TPC_GET_INT, NULL, 1);
+                       return 0;
+               }
+
+       default:
+               BUG();
+       }
+}
+
+/*** Data transfer ***/
+
+static void mspro_block_process_request(struct memstick_dev *card,
+                                       struct request *req)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct mspro_param_register param;
+       int rc, chunk, cnt;
+       unsigned short page_count;
+       sector_t t_sec;
+       unsigned long flags;
+
+       do {
+               page_count = 0;
+               msb->current_seg = 0;
+               msb->seg_count = blk_rq_map_sg(req->q, req, msb->req_sg);
+
+               if (msb->seg_count) {
+                       msb->current_page = 0;
+                       for (rc = 0; rc < msb->seg_count; rc++)
+                               page_count += msb->req_sg[rc].length
+                                             / msb->page_size;
+
+                       t_sec = req->sector;
+                       sector_div(t_sec, msb->page_size >> 9);
+                       param.system = msb->system;
+                       param.data_count = cpu_to_be16(page_count);
+                       param.data_address = cpu_to_be32((uint32_t)t_sec);
+                       param.cmd_param = 0;
+
+                       msb->data_dir = rq_data_dir(req);
+                       msb->transfer_cmd = msb->data_dir == READ
+                                           ? MSPRO_CMD_READ_DATA
+                                           : MSPRO_CMD_WRITE_DATA;
+
+                       dev_dbg(&card->dev, "data transfer: cmd %x, "
+                               "lba %x, count %x\n", msb->transfer_cmd,
+                               be32_to_cpu(param.data_address),
+                               page_count);
+
+                       card->next_request = h_mspro_block_req_init;
+                       msb->mrq_handler = h_mspro_block_transfer_data;
+                       memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
+                                         &param, sizeof(param));
+                       memstick_new_req(card->host);
+                       wait_for_completion(&card->mrq_complete);
+                       rc = card->current_mrq.error;
+
+                       if (rc || (card->current_mrq.tpc == MSPRO_CMD_STOP)) {
+                               for (cnt = 0; cnt < msb->current_seg; cnt++)
+                                       page_count += msb->req_sg[cnt].length
+                                                     / msb->page_size;
+
+                               if (msb->current_page)
+                                       page_count += msb->current_page - 1;
+
+                               if (page_count && (msb->data_dir == READ))
+                                       rc = msb->page_size * page_count;
+                               else
+                                       rc = -EIO;
+                       } else
+                               rc = msb->page_size * page_count;
+               } else
+                       rc = -EFAULT;
+
+               spin_lock_irqsave(&msb->q_lock, flags);
+               if (rc >= 0)
+                       chunk = __blk_end_request(req, 0, rc);
+               else
+                       chunk = __blk_end_request(req, rc, 0);
+
+               dev_dbg(&card->dev, "end chunk %d, %d\n", rc, chunk);
+               spin_unlock_irqrestore(&msb->q_lock, flags);
+       } while (chunk);
+}
+
+static int mspro_block_has_request(struct mspro_block_data *msb)
+{
+       int rc = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&msb->q_lock, flags);
+       if (kthread_should_stop() || msb->has_request)
+               rc = 1;
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+       return rc;
+}
+
+static int mspro_block_queue_thread(void *data)
+{
+       struct memstick_dev *card = data;
+       struct memstick_host *host = card->host;
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct request *req;
+       unsigned long flags;
+
+       while (1) {
+               wait_event(msb->q_wait, mspro_block_has_request(msb));
+               dev_dbg(&card->dev, "thread iter\n");
+
+               spin_lock_irqsave(&msb->q_lock, flags);
+               req = elv_next_request(msb->queue);
+               dev_dbg(&card->dev, "next req %p\n", req);
+               if (!req) {
+                       msb->has_request = 0;
+                       if (kthread_should_stop()) {
+                               spin_unlock_irqrestore(&msb->q_lock, flags);
+                               break;
+                       }
+               } else
+                       msb->has_request = 1;
+               spin_unlock_irqrestore(&msb->q_lock, flags);
+
+               if (req) {
+                       mutex_lock(&host->lock);
+                       mspro_block_process_request(card, req);
+                       mutex_unlock(&host->lock);
+               }
+       }
+       dev_dbg(&card->dev, "thread finished\n");
+       return 0;
+}
+
+static void mspro_block_request(struct request_queue *q)
+{
+       struct memstick_dev *card = q->queuedata;
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct request *req = NULL;
+
+       if (msb->q_thread) {
+               msb->has_request = 1;
+               wake_up_all(&msb->q_wait);
+       } else {
+               while ((req = elv_next_request(q)) != NULL)
+                       end_queued_request(req, -ENODEV);
+       }
+}
+
+/*** Initialization ***/
+
+static int mspro_block_wait_for_ced(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+       card->next_request = h_mspro_block_req_init;
+       msb->mrq_handler = h_mspro_block_wait_for_ced;
+       memstick_init_req(&card->current_mrq, MS_TPC_GET_INT, NULL, 1);
+       memstick_new_req(card->host);
+       wait_for_completion(&card->mrq_complete);
+       return card->current_mrq.error;
+}
+
+static int mspro_block_switch_to_parallel(struct memstick_dev *card)
+{
+       struct memstick_host *host = card->host;
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct mspro_param_register param = {
+               .system = 0,
+               .data_count = 0,
+               .data_address = 0,
+               .cmd_param = 0
+       };
+
+       card->next_request = h_mspro_block_req_init;
+       msb->mrq_handler = h_mspro_block_default;
+       memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, &param,
+                         sizeof(param));
+       memstick_new_req(host);
+       wait_for_completion(&card->mrq_complete);
+       if (card->current_mrq.error)
+               return card->current_mrq.error;
+
+       msb->system = 0;
+       host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_PARALLEL);
+
+       card->next_request = h_mspro_block_req_init;
+       msb->mrq_handler = h_mspro_block_default;
+       memstick_init_req(&card->current_mrq, MS_TPC_GET_INT, NULL, 1);
+       memstick_new_req(card->host);
+       wait_for_completion(&card->mrq_complete);
+
+       if (card->current_mrq.error) {
+               msb->system = 0x80;
+               host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/* Memory allocated for attributes by this function should be freed by
+ * mspro_block_data_clear, no matter if the initialization process succeded
+ * or failed.
+ */
+static int mspro_block_read_attributes(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct mspro_param_register param = {
+               .system = msb->system,
+               .data_count = cpu_to_be16(1),
+               .data_address = 0,
+               .cmd_param = 0
+       };
+       struct mspro_attribute *attr = NULL;
+       struct mspro_sys_attr *s_attr = NULL;
+       unsigned char *buffer = NULL;
+       int cnt, rc, attr_count;
+       unsigned int addr;
+       unsigned short page_count;
+
+       attr = kmalloc(msb->page_size, GFP_KERNEL);
+       if (!attr)
+               return -ENOMEM;
+
+       sg_init_one(&msb->req_sg[0], attr, msb->page_size);
+       msb->seg_count = 1;
+       msb->current_seg = 0;
+       msb->current_page = 0;
+       msb->data_dir = READ;
+       msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
+
+       card->next_request = h_mspro_block_req_init;
+       msb->mrq_handler = h_mspro_block_transfer_data;
+       memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, &param,
+                         sizeof(param));
+       memstick_new_req(card->host);
+       wait_for_completion(&card->mrq_complete);
+       if (card->current_mrq.error) {
+               rc = card->current_mrq.error;
+               goto out_free_attr;
+       }
+
+       if (be16_to_cpu(attr->signature) != MSPRO_BLOCK_SIGNATURE) {
+               printk(KERN_ERR "%s: unrecognized device signature %x\n",
+                      card->dev.bus_id, be16_to_cpu(attr->signature));
+               rc = -ENODEV;
+               goto out_free_attr;
+       }
+
+       if (attr->count > MSPRO_BLOCK_MAX_ATTRIBUTES) {
+               printk(KERN_WARNING "%s: way too many attribute entries\n",
+                      card->dev.bus_id);
+               attr_count = MSPRO_BLOCK_MAX_ATTRIBUTES;
+       } else
+               attr_count = attr->count;
+
+       msb->attr_group.attrs = kzalloc((attr_count + 1)
+                                       * sizeof(struct attribute),
+                                       GFP_KERNEL);
+       if (!msb->attr_group.attrs) {
+               rc = -ENOMEM;
+               goto out_free_attr;
+       }
+       msb->attr_group.name = "media_attributes";
+
+       buffer = kmalloc(msb->page_size, GFP_KERNEL);
+       if (!buffer) {
+               rc = -ENOMEM;
+               goto out_free_attr;
+       }
+       memcpy(buffer, (char *)attr, msb->page_size);
+       page_count = 1;
+
+       for (cnt = 0; cnt < attr_count; ++cnt) {
+               s_attr = kzalloc(sizeof(struct mspro_sys_attr), GFP_KERNEL);
+               if (!s_attr) {
+                       rc = -ENOMEM;
+                       goto out_free_buffer;
+               }
+
+               msb->attr_group.attrs[cnt] = &s_attr->dev_attr.attr;
+               addr = be32_to_cpu(attr->entries[cnt].address);
+               rc = be32_to_cpu(attr->entries[cnt].size);
+               dev_dbg(&card->dev, "adding attribute %d: id %x, address %x, "
+                       "size %x\n", cnt, attr->entries[cnt].id, addr, rc);
+               s_attr->id = attr->entries[cnt].id;
+               if (mspro_block_attr_name(s_attr->id))
+                       snprintf(s_attr->name, sizeof(s_attr->name), "%s",
+                                mspro_block_attr_name(attr->entries[cnt].id));
+               else
+                       snprintf(s_attr->name, sizeof(s_attr->name),
+                                "attr_x%02x", attr->entries[cnt].id);
+
+               s_attr->dev_attr.attr.name = s_attr->name;
+               s_attr->dev_attr.attr.mode = S_IRUGO;
+               s_attr->dev_attr.attr.owner = THIS_MODULE;
+               s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id);
+
+               if (!rc)
+                       continue;
+
+               s_attr->size = rc;
+               s_attr->data = kmalloc(rc, GFP_KERNEL);
+               if (!s_attr->data) {
+                       rc = -ENOMEM;
+                       goto out_free_buffer;
+               }
+
+               if (((addr / msb->page_size)
+                    == be32_to_cpu(param.data_address))
+                   && (((addr + rc - 1) / msb->page_size)
+                       == be32_to_cpu(param.data_address))) {
+                       memcpy(s_attr->data, buffer + addr % msb->page_size,
+                              rc);
+                       continue;
+               }
+
+               if (page_count <= (rc / msb->page_size)) {
+                       kfree(buffer);
+                       page_count = (rc / msb->page_size) + 1;
+                       buffer = kmalloc(page_count * msb->page_size,
+                                        GFP_KERNEL);
+                       if (!buffer) {
+                               rc = -ENOMEM;
+                               goto out_free_attr;
+                       }
+               }
+
+               param.system = msb->system;
+               param.data_count = cpu_to_be16((rc / msb->page_size) + 1);
+               param.data_address = cpu_to_be32(addr / msb->page_size);
+               param.cmd_param = 0;
+
+               sg_init_one(&msb->req_sg[0], buffer,
+                           be16_to_cpu(param.data_count) * msb->page_size);
+               msb->seg_count = 1;
+               msb->current_seg = 0;
+               msb->current_page = 0;
+               msb->data_dir = READ;
+               msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
+
+               dev_dbg(&card->dev, "reading attribute pages %x, %x\n",
+                       be32_to_cpu(param.data_address),
+                       be16_to_cpu(param.data_count));
+
+               card->next_request = h_mspro_block_req_init;
+               msb->mrq_handler = h_mspro_block_transfer_data;
+               memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG,
+                                 (char *)&param, sizeof(param));
+               memstick_new_req(card->host);
+               wait_for_completion(&card->mrq_complete);
+               if (card->current_mrq.error) {
+                       rc = card->current_mrq.error;
+                       goto out_free_buffer;
+               }
+
+               memcpy(s_attr->data, buffer + addr % msb->page_size, rc);
+       }
+
+       rc = 0;
+out_free_buffer:
+       kfree(buffer);
+out_free_attr:
+       kfree(attr);
+       return rc;
+}
+
+static int mspro_block_init_card(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct memstick_host *host = card->host;
+       int rc = 0;
+
+       msb->system = 0x80;
+       card->reg_addr.r_offset = offsetof(struct mspro_register, status);
+       card->reg_addr.r_length = sizeof(struct ms_status_register);
+       card->reg_addr.w_offset = offsetof(struct mspro_register, param);
+       card->reg_addr.w_length = sizeof(struct mspro_param_register);
+
+       if (memstick_set_rw_addr(card))
+               return -EIO;
+
+       if (host->caps & MEMSTICK_CAP_PARALLEL) {
+               if (mspro_block_switch_to_parallel(card))
+                       printk(KERN_WARNING "%s: could not switch to "
+                              "parallel interface\n", card->dev.bus_id);
+       }
+
+       rc = mspro_block_wait_for_ced(card);
+       if (rc)
+               return rc;
+       dev_dbg(&card->dev, "card activated\n");
+
+       card->next_request = h_mspro_block_req_init;
+       msb->mrq_handler = h_mspro_block_get_ro;
+       memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL,
+                         sizeof(struct ms_status_register));
+       memstick_new_req(card->host);
+       wait_for_completion(&card->mrq_complete);
+       if (card->current_mrq.error)
+               return card->current_mrq.error;
+
+       dev_dbg(&card->dev, "card r/w status %d\n", msb->read_only ? 0 : 1);
+
+       msb->page_size = 512;
+       rc = mspro_block_read_attributes(card);
+       if (rc)
+               return rc;
+
+       dev_dbg(&card->dev, "attributes loaded\n");
+       return 0;
+
+}
+
+static int mspro_block_init_disk(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct memstick_host *host = card->host;
+       struct mspro_devinfo *dev_info = NULL;
+       struct mspro_sys_info *sys_info = NULL;
+       struct mspro_sys_attr *s_attr = NULL;
+       int rc, disk_id;
+       u64 limit = BLK_BOUNCE_HIGH;
+       unsigned long capacity;
+
+       if (host->cdev.dev->dma_mask && *(host->cdev.dev->dma_mask))
+               limit = *(host->cdev.dev->dma_mask);
+
+       for (rc = 0; msb->attr_group.attrs[rc]; ++rc) {
+               s_attr = mspro_from_sysfs_attr(msb->attr_group.attrs[rc]);
+
+               if (s_attr->id == MSPRO_BLOCK_ID_DEVINFO)
+                       dev_info = s_attr->data;
+               else if (s_attr->id == MSPRO_BLOCK_ID_SYSINFO)
+                       sys_info = s_attr->data;
+       }
+
+       if (!dev_info || !sys_info)
+               return -ENODEV;
+
+       msb->cylinders = be16_to_cpu(dev_info->cylinders);
+       msb->heads = be16_to_cpu(dev_info->heads);
+       msb->sectors_per_track = be16_to_cpu(dev_info->sectors_per_track);
+
+       msb->page_size = be16_to_cpu(sys_info->unit_size);
+
+       if (!idr_pre_get(&mspro_block_disk_idr, GFP_KERNEL))
+               return -ENOMEM;
+
+       mutex_lock(&mspro_block_disk_lock);
+       rc = idr_get_new(&mspro_block_disk_idr, card, &disk_id);
+       mutex_unlock(&mspro_block_disk_lock);
+
+       if (rc)
+               return rc;
+
+       if ((disk_id << MEMSTICK_PART_SHIFT) > 255) {
+               rc = -ENOSPC;
+               goto out_release_id;
+       }
+
+       msb->disk = alloc_disk(1 << MEMSTICK_PART_SHIFT);
+       if (!msb->disk) {
+               rc = -ENOMEM;
+               goto out_release_id;
+       }
+
+       spin_lock_init(&msb->q_lock);
+       init_waitqueue_head(&msb->q_wait);
+
+       msb->queue = blk_init_queue(mspro_block_request, &msb->q_lock);
+       if (!msb->queue) {
+               rc = -ENOMEM;
+               goto out_put_disk;
+       }
+
+       msb->queue->queuedata = card;
+
+       blk_queue_bounce_limit(msb->queue, limit);
+       blk_queue_max_sectors(msb->queue, MSPRO_BLOCK_MAX_PAGES);
+       blk_queue_max_phys_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS);
+       blk_queue_max_hw_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS);
+       blk_queue_max_segment_size(msb->queue,
+                                  MSPRO_BLOCK_MAX_PAGES * msb->page_size);
+
+       msb->disk->major = major;
+       msb->disk->first_minor = disk_id << MEMSTICK_PART_SHIFT;
+       msb->disk->fops = &ms_block_bdops;
+       msb->usage_count = 1;
+       msb->disk->private_data = msb;
+       msb->disk->queue = msb->queue;
+       msb->disk->driverfs_dev = &card->dev;
+
+       sprintf(msb->disk->disk_name, "mspblk%d", disk_id);
+
+       blk_queue_hardsect_size(msb->queue, msb->page_size);
+
+       capacity = be16_to_cpu(sys_info->user_block_count);
+       capacity *= be16_to_cpu(sys_info->block_size);
+       capacity *= msb->page_size >> 9;
+       set_capacity(msb->disk, capacity);
+       dev_dbg(&card->dev, "capacity set %ld\n", capacity);
+       msb->q_thread = kthread_run(mspro_block_queue_thread, card,
+                                   DRIVER_NAME"d");
+       if (IS_ERR(msb->q_thread))
+               goto out_put_disk;
+
+       mutex_unlock(&host->lock);
+       add_disk(msb->disk);
+       mutex_lock(&host->lock);
+       msb->active = 1;
+       return 0;
+
+out_put_disk:
+       put_disk(msb->disk);
+out_release_id:
+       mutex_lock(&mspro_block_disk_lock);
+       idr_remove(&mspro_block_disk_idr, disk_id);
+       mutex_unlock(&mspro_block_disk_lock);
+       return rc;
+}
+
+static void mspro_block_data_clear(struct mspro_block_data *msb)
+{
+       int cnt;
+       struct mspro_sys_attr *s_attr;
+
+       if (msb->attr_group.attrs) {
+               for (cnt = 0; msb->attr_group.attrs[cnt]; ++cnt) {
+                       s_attr = mspro_from_sysfs_attr(msb->attr_group
+                                                          .attrs[cnt]);
+                       kfree(s_attr->data);
+                       kfree(s_attr);
+               }
+               kfree(msb->attr_group.attrs);
+       }
+
+       msb->card = NULL;
+}
+
+static int mspro_block_check_card(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+
+       return (msb->active == 1);
+}
+
+static int mspro_block_probe(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb;
+       int rc = 0;
+
+       msb = kzalloc(sizeof(struct mspro_block_data), GFP_KERNEL);
+       if (!msb)
+               return -ENOMEM;
+       memstick_set_drvdata(card, msb);
+       msb->card = card;
+
+       rc = mspro_block_init_card(card);
+
+       if (rc)
+               goto out_free;
+
+       rc = sysfs_create_group(&card->dev.kobj, &msb->attr_group);
+       if (rc)
+               goto out_free;
+
+       rc = mspro_block_init_disk(card);
+       if (!rc) {
+               card->check = mspro_block_check_card;
+               return 0;
+       }
+
+       sysfs_remove_group(&card->dev.kobj, &msb->attr_group);
+out_free:
+       memstick_set_drvdata(card, NULL);
+       mspro_block_data_clear(msb);
+       kfree(msb);
+       return rc;
+}
+
+static void mspro_block_remove(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct task_struct *q_thread = NULL;
+       unsigned long flags;
+
+       del_gendisk(msb->disk);
+       dev_dbg(&card->dev, "mspro block remove\n");
+       spin_lock_irqsave(&msb->q_lock, flags);
+       q_thread = msb->q_thread;
+       msb->q_thread = NULL;
+       msb->active = 0;
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+
+       if (q_thread) {
+               mutex_unlock(&card->host->lock);
+               kthread_stop(q_thread);
+               mutex_lock(&card->host->lock);
+       }
+
+       dev_dbg(&card->dev, "queue thread stopped\n");
+
+       blk_cleanup_queue(msb->queue);
+
+       sysfs_remove_group(&card->dev.kobj, &msb->attr_group);
+
+       mutex_lock(&mspro_block_disk_lock);
+       mspro_block_data_clear(msb);
+       mutex_unlock(&mspro_block_disk_lock);
+
+       mspro_block_disk_release(msb->disk);
+       memstick_set_drvdata(card, NULL);
+}
+
+#ifdef CONFIG_PM
+
+static int mspro_block_suspend(struct memstick_dev *card, pm_message_t state)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       struct task_struct *q_thread = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&msb->q_lock, flags);
+       q_thread = msb->q_thread;
+       msb->q_thread = NULL;
+       msb->active = 0;
+       blk_stop_queue(msb->queue);
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+
+       if (q_thread)
+               kthread_stop(q_thread);
+
+       return 0;
+}
+
+static int mspro_block_resume(struct memstick_dev *card)
+{
+       struct mspro_block_data *msb = memstick_get_drvdata(card);
+       unsigned long flags;
+       int rc = 0;
+
+#ifdef CONFIG_MEMSTICK_UNSAFE_RESUME
+
+       struct mspro_block_data *new_msb;
+       struct memstick_host *host = card->host;
+       struct mspro_sys_attr *s_attr, *r_attr;
+       unsigned char cnt;
+
+       mutex_lock(&host->lock);
+       new_msb = kzalloc(sizeof(struct mspro_block_data), GFP_KERNEL);
+       if (!new_msb) {
+               rc = -ENOMEM;
+               goto out_unlock;
+       }
+
+       new_msb->card = card;
+       memstick_set_drvdata(card, new_msb);
+       if (mspro_block_init_card(card))
+               goto out_free;
+
+       for (cnt = 0; new_msb->attr_group.attrs[cnt]
+                     && msb->attr_group.attrs[cnt]; ++cnt) {
+               s_attr = mspro_from_sysfs_attr(new_msb->attr_group.attrs[cnt]);
+               r_attr = mspro_from_sysfs_attr(msb->attr_group.attrs[cnt]);
+
+               if (s_attr->id == MSPRO_BLOCK_ID_SYSINFO
+                   && r_attr->id == s_attr->id) {
+                       if (memcmp(s_attr->data, r_attr->data, s_attr->size))
+                               break;
+
+                       memstick_set_drvdata(card, msb);
+                       msb->q_thread = kthread_run(mspro_block_queue_thread,
+                                                   card, DRIVER_NAME"d");
+                       if (IS_ERR(msb->q_thread))
+                               msb->q_thread = NULL;
+                       else
+                               msb->active = 1;
+
+                       break;
+               }
+       }
+
+out_free:
+       memstick_set_drvdata(card, msb);
+       mspro_block_data_clear(new_msb);
+       kfree(new_msb);
+out_unlock:
+       mutex_unlock(&host->lock);
+
+#endif /* CONFIG_MEMSTICK_UNSAFE_RESUME */
+
+       spin_lock_irqsave(&msb->q_lock, flags);
+       blk_start_queue(msb->queue);
+       spin_unlock_irqrestore(&msb->q_lock, flags);
+       return rc;
+}
+
+#else
+
+#define mspro_block_suspend NULL
+#define mspro_block_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct memstick_device_id mspro_block_id_tbl[] = {
+       {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_PRO, MEMSTICK_CATEGORY_STORAGE_DUO,
+        MEMSTICK_CLASS_GENERIC_DUO},
+       {}
+};
+
+
+static struct memstick_driver mspro_block_driver = {
+       .driver = {
+               .name  = DRIVER_NAME,
+               .owner = THIS_MODULE
+       },
+       .id_table = mspro_block_id_tbl,
+       .probe    = mspro_block_probe,
+       .remove   = mspro_block_remove,
+       .suspend  = mspro_block_suspend,
+       .resume   = mspro_block_resume
+};
+
+static int __init mspro_block_init(void)
+{
+       int rc = -ENOMEM;
+
+       rc = register_blkdev(major, DRIVER_NAME);
+       if (rc < 0) {
+               printk(KERN_ERR DRIVER_NAME ": failed to register "
+                      "major %d, error %d\n", major, rc);
+               return rc;
+       }
+       if (!major)
+               major = rc;
+
+       rc = memstick_register_driver(&mspro_block_driver);
+       if (rc)
+               unregister_blkdev(major, DRIVER_NAME);
+       return rc;
+}
+
+static void __exit mspro_block_exit(void)
+{
+       memstick_unregister_driver(&mspro_block_driver);
+       unregister_blkdev(major, DRIVER_NAME);
+       idr_destroy(&mspro_block_disk_idr);
+}
+
+module_init(mspro_block_init);
+module_exit(mspro_block_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alex Dubov");
+MODULE_DESCRIPTION("Sony MemoryStickPro block device driver");
+MODULE_DEVICE_TABLE(memstick, mspro_block_id_tbl);
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig
new file mode 100644 (file)
index 0000000..c002fcc
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# MemoryStick host controller drivers
+#
+
+comment "MemoryStick Host Controller Drivers"
+
+config MEMSTICK_TIFM_MS
+       tristate "TI Flash Media MemoryStick Interface support  (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && PCI
+       select TIFM_CORE
+       help
+         Say Y here if you want to be able to access MemoryStick cards with
+         the Texas Instruments(R) Flash Media card reader, found in many
+         laptops.
+         This option 'selects' (turns on, enables) 'TIFM_CORE', but you
+         probably also need appropriate card reader host adapter, such as
+         'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
+         (TIFM_7XX1)'.
+
+          To compile this driver as a module, choose M here: the
+         module will be called tifm_ms.
+
diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile
new file mode 100644 (file)
index 0000000..ee66638
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for MemoryStick host controller drivers
+#
+
+ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
+       EXTRA_CFLAGS            += -DDEBUG
+endif
+
+obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
+
diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c
new file mode 100644 (file)
index 0000000..f55b71a
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ *  TI FlashMedia driver
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Special thanks to Carlos Corbacho for providing various MemoryStick cards
+ * that made this driver possible.
+ *
+ */
+
+#include <linux/tifm.h>
+#include <linux/memstick.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <linux/log2.h>
+#include <asm/io.h>
+
+#define DRIVER_NAME "tifm_ms"
+#define DRIVER_VERSION "0.1"
+
+static int no_dma;
+module_param(no_dma, bool, 0644);
+
+#define TIFM_MS_TIMEOUT      0x00100
+#define TIFM_MS_BADCRC       0x00200
+#define TIFM_MS_EOTPC        0x01000
+#define TIFM_MS_INT          0x02000
+
+/* The meaning of the bit majority in this constant is unknown. */
+#define TIFM_MS_SERIAL       0x04010
+
+#define TIFM_MS_SYS_LATCH    0x00100
+#define TIFM_MS_SYS_NOT_RDY  0x00800
+#define TIFM_MS_SYS_DATA     0x10000
+
+/* Hardware flags */
+enum {
+       CMD_READY  = 0x0001,
+       FIFO_READY = 0x0002,
+       CARD_READY = 0x0004,
+       DATA_CARRY = 0x0008
+};
+
+struct tifm_ms {
+       struct tifm_dev         *dev;
+       unsigned short          eject:1,
+                               no_dma:1;
+       unsigned short          cmd_flags;
+       unsigned int            mode_mask;
+       unsigned int            block_pos;
+       unsigned long           timeout_jiffies;
+
+       struct timer_list       timer;
+       struct memstick_request *req;
+       unsigned int            io_word;
+};
+
+static void tifm_ms_read_fifo(struct tifm_ms *host, unsigned int fifo_offset,
+                             struct page *pg, unsigned int page_off,
+                             unsigned int length)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned int cnt = 0, off = 0;
+       unsigned char *buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + page_off;
+
+       if (host->cmd_flags & DATA_CARRY) {
+               while ((fifo_offset & 3) && length) {
+                       buf[off++] = host->io_word & 0xff;
+                       host->io_word >>= 8;
+                       length--;
+                       fifo_offset++;
+               }
+               if (!(fifo_offset & 3))
+                       host->cmd_flags &= ~DATA_CARRY;
+               if (!length)
+                       return;
+       }
+
+       do {
+               host->io_word = readl(sock->addr + SOCK_FIFO_ACCESS
+                                     + fifo_offset);
+               cnt = 4;
+               while (length && cnt) {
+                       buf[off++] = (host->io_word >> 8) & 0xff;
+                       cnt--;
+                       length--;
+               }
+               fifo_offset += 4 - cnt;
+       } while (length);
+
+       if (cnt)
+               host->cmd_flags |= DATA_CARRY;
+
+       kunmap_atomic(buf - page_off, KM_BIO_DST_IRQ);
+}
+
+static void tifm_ms_write_fifo(struct tifm_ms *host, unsigned int fifo_offset,
+                              struct page *pg, unsigned int page_off,
+                              unsigned int length)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned int cnt = 0, off = 0;
+       unsigned char *buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + page_off;
+
+       if (host->cmd_flags & DATA_CARRY) {
+               while (fifo_offset & 3) {
+                       host->io_word |= buf[off++] << (8 * (fifo_offset & 3));
+                       length--;
+                       fifo_offset++;
+               }
+               if (!(fifo_offset & 3)) {
+                       writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
+                              + fifo_offset - 4);
+
+                       host->cmd_flags &= ~DATA_CARRY;
+               }
+               if (!length)
+                       return;
+       }
+
+       do {
+               cnt = 4;
+               host->io_word = 0;
+               while (length && cnt) {
+                       host->io_word |= buf[off++] << (4 - cnt);
+                       cnt--;
+                       length--;
+               }
+               fifo_offset += 4 - cnt;
+               if (!cnt)
+                       writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
+                                             + fifo_offset - 4);
+
+       } while (length);
+
+       if (cnt)
+               host->cmd_flags |= DATA_CARRY;
+
+       kunmap_atomic(buf - page_off, KM_BIO_SRC_IRQ);
+}
+
+static void tifm_ms_move_block(struct tifm_ms *host, unsigned int length)
+{
+       unsigned int t_size;
+       unsigned int off = host->req->sg.offset + host->block_pos;
+       unsigned int p_off, p_cnt;
+       struct page *pg;
+       unsigned long flags;
+
+       dev_dbg(&host->dev->dev, "moving block\n");
+       local_irq_save(flags);
+       t_size = length;
+       while (t_size) {
+               pg = nth_page(sg_page(&host->req->sg), off >> PAGE_SHIFT);
+               p_off = offset_in_page(off);
+               p_cnt = PAGE_SIZE - p_off;
+               p_cnt = min(p_cnt, t_size);
+
+               if (host->req->data_dir == WRITE)
+                       tifm_ms_write_fifo(host, length - t_size,
+                                          pg, p_off, p_cnt);
+               else
+                       tifm_ms_read_fifo(host, length - t_size,
+                                         pg, p_off, p_cnt);
+
+               t_size -= p_cnt;
+       }
+       local_irq_restore(flags);
+}
+
+static int tifm_ms_transfer_data(struct tifm_ms *host, int skip)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned int length = host->req->sg.length - host->block_pos;
+
+       if (!length)
+               return 1;
+
+       if (length > TIFM_FIFO_SIZE)
+               length = TIFM_FIFO_SIZE;
+
+       if (!skip) {
+               tifm_ms_move_block(host, length);
+               host->block_pos += length;
+       }
+
+       if ((host->req->data_dir == READ)
+           && (host->block_pos == host->req->sg.length))
+               return 1;
+
+       writel(ilog2(length) - 2, sock->addr + SOCK_FIFO_PAGE_SIZE);
+       if (host->req->data_dir == WRITE)
+               writel((1 << 8) | TIFM_DMA_TX, sock->addr + SOCK_DMA_CONTROL);
+       else
+               writel((1 << 8), sock->addr + SOCK_DMA_CONTROL);
+
+       return 0;
+}
+
+static int tifm_ms_issue_cmd(struct tifm_ms *host)
+{
+       struct tifm_dev *sock = host->dev;
+       unsigned char *data;
+       unsigned int data_len = 0, cmd = 0, cmd_mask = 0, cnt, tval = 0;
+
+       host->cmd_flags = 0;
+
+       if (host->req->io_type == MEMSTICK_IO_SG) {
+               if (!host->no_dma) {
+                       if (1 != tifm_map_sg(sock, &host->req->sg, 1,
+                                            host->req->data_dir == READ
+                                            ? PCI_DMA_FROMDEVICE
+                                            : PCI_DMA_TODEVICE)) {
+                               host->req->error = -ENOMEM;
+                               return host->req->error;
+                       }
+                       data_len = sg_dma_len(&host->req->sg);
+               } else
+                       data_len = host->req->sg.length;
+
+               writel(TIFM_FIFO_INT_SETALL,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+               writel(TIFM_FIFO_ENABLE,
+                      sock->addr + SOCK_FIFO_CONTROL);
+               writel(TIFM_FIFO_INTMASK,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+
+               if (!host->no_dma) {
+                       writel(ilog2(data_len) - 2,
+                              sock->addr + SOCK_FIFO_PAGE_SIZE);
+                       writel(sg_dma_address(&host->req->sg),
+                              sock->addr + SOCK_DMA_ADDRESS);
+                       if (host->req->data_dir == WRITE)
+                               writel((1 << 8) | TIFM_DMA_TX | TIFM_DMA_EN,
+                                      sock->addr + SOCK_DMA_CONTROL);
+                       else
+                               writel((1 << 8) | TIFM_DMA_EN,
+                                      sock->addr + SOCK_DMA_CONTROL);
+               } else {
+                       tifm_ms_transfer_data(host,
+                                             host->req->data_dir == READ);
+               }
+
+               cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
+               cmd_mask |= TIFM_MS_SYS_DATA | TIFM_MS_SYS_NOT_RDY;
+               writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+       } else if (host->req->io_type == MEMSTICK_IO_VAL) {
+               data = host->req->data;
+               data_len = host->req->data_len;
+
+               cmd_mask = host->mode_mask | 0x2607; /* unknown constant */
+
+               if (host->req->data_dir == WRITE) {
+                       cmd_mask |= TIFM_MS_SYS_LATCH;
+                       writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+                       for (cnt = 0; (data_len - cnt) >= 4; cnt += 4) {
+                               writel(TIFM_MS_SYS_LATCH
+                                      | readl(sock->addr + SOCK_MS_SYSTEM),
+                                      sock->addr + SOCK_MS_SYSTEM);
+                               __raw_writel(*(unsigned int *)(data + cnt),
+                                            sock->addr + SOCK_MS_DATA);
+                               dev_dbg(&sock->dev, "writing %x\n",
+                                       *(int *)(data + cnt));
+                       }
+                       switch (data_len - cnt) {
+                       case 3:
+                               tval |= data[cnt + 2] << 16;
+                       case 2:
+                               tval |= data[cnt + 1] << 8;
+                       case 1:
+                               tval |= data[cnt];
+                               writel(TIFM_MS_SYS_LATCH
+                                      | readl(sock->addr + SOCK_MS_SYSTEM),
+                                      sock->addr + SOCK_MS_SYSTEM);
+                               writel(tval, sock->addr + SOCK_MS_DATA);
+                               dev_dbg(&sock->dev, "writing %x\n", tval);
+                       }
+
+                       writel(TIFM_MS_SYS_LATCH
+                              | readl(sock->addr + SOCK_MS_SYSTEM),
+                              sock + SOCK_MS_SYSTEM);
+                       writel(0, sock->addr + SOCK_MS_DATA);
+                       dev_dbg(&sock->dev, "writing %x\n", 0);
+
+               } else
+                       writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+
+               cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
+               cmd_mask &= ~TIFM_MS_SYS_DATA;
+               cmd_mask |= TIFM_MS_SYS_NOT_RDY;
+               dev_dbg(&sock->dev, "mask %x\n", cmd_mask);
+               writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
+       } else
+               BUG();
+
+       mod_timer(&host->timer, jiffies + host->timeout_jiffies);
+       writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
+              sock->addr + SOCK_CONTROL);
+       host->req->error = 0;
+
+       cmd = (host->req->tpc & 0xf) << 12;
+       cmd |= data_len;
+       writel(cmd, sock->addr + SOCK_MS_COMMAND);
+
+       dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, cmd_mask);
+       return 0;
+}
+
+static void tifm_ms_complete_cmd(struct tifm_ms *host)
+{
+       struct tifm_dev *sock = host->dev;
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+       unsigned int tval = 0, data_len;
+       unsigned char *data;
+       int rc;
+
+       del_timer(&host->timer);
+       if (host->req->io_type == MEMSTICK_IO_SG) {
+               if (!host->no_dma)
+                       tifm_unmap_sg(sock, &host->req->sg, 1,
+                                     host->req->data_dir == READ
+                                     ? PCI_DMA_FROMDEVICE
+                                     : PCI_DMA_TODEVICE);
+       } else if (host->req->io_type == MEMSTICK_IO_VAL) {
+               writel(~TIFM_MS_SYS_DATA & readl(sock->addr + SOCK_MS_SYSTEM),
+                      sock->addr + SOCK_MS_SYSTEM);
+
+               data = host->req->data;
+               data_len = host->req->data_len;
+
+               if (host->req->data_dir == READ) {
+                       for (rc = 0; (data_len - rc) >= 4; rc += 4)
+                               *(int *)(data + rc)
+                                       = __raw_readl(sock->addr
+                                                     + SOCK_MS_DATA);
+
+                       if (data_len - rc)
+                               tval = readl(sock->addr + SOCK_MS_DATA);
+                       switch (data_len - rc) {
+                       case 3:
+                               data[rc + 2] = (tval >> 16) & 0xff;
+                       case 2:
+                               data[rc + 1] = (tval >> 8) & 0xff;
+                       case 1:
+                               data[rc] = tval & 0xff;
+                       }
+                       readl(sock->addr + SOCK_MS_DATA);
+               }
+       }
+
+       writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
+              sock->addr + SOCK_CONTROL);
+
+       do {
+               rc = memstick_next_req(msh, &host->req);
+       } while (!rc && tifm_ms_issue_cmd(host));
+}
+
+static int tifm_ms_check_status(struct tifm_ms *host)
+{
+       if (!host->req->error) {
+               if (!(host->cmd_flags & CMD_READY))
+                       return 1;
+               if ((host->req->io_type == MEMSTICK_IO_SG)
+                   && !(host->cmd_flags & FIFO_READY))
+                       return 1;
+               if (host->req->need_card_int
+                   && !(host->cmd_flags & CARD_READY))
+                       return 1;
+       }
+       return 0;
+}
+
+/* Called from interrupt handler */
+static void tifm_ms_data_event(struct tifm_dev *sock)
+{
+       struct tifm_ms *host;
+       unsigned int fifo_status = 0;
+       int rc = 1;
+
+       spin_lock(&sock->lock);
+       host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
+       fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
+       dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
+               fifo_status, host->cmd_flags);
+
+       if (host->req) {
+               if (fifo_status & TIFM_FIFO_READY) {
+                       if (!host->no_dma || tifm_ms_transfer_data(host, 0)) {
+                               host->cmd_flags |= FIFO_READY;
+                               rc = tifm_ms_check_status(host);
+                       }
+               }
+       }
+
+       writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
+       if (!rc)
+               tifm_ms_complete_cmd(host);
+
+       spin_unlock(&sock->lock);
+}
+
+
+/* Called from interrupt handler */
+static void tifm_ms_card_event(struct tifm_dev *sock)
+{
+       struct tifm_ms *host;
+       unsigned int host_status = 0;
+       int rc = 1;
+
+       spin_lock(&sock->lock);
+       host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
+       host_status = readl(sock->addr + SOCK_MS_STATUS);
+       dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
+               host_status, host->cmd_flags);
+
+       if (host->req) {
+               if (host_status & TIFM_MS_TIMEOUT)
+                       host->req->error = -ETIME;
+               else if (host_status & TIFM_MS_BADCRC)
+                       host->req->error = -EILSEQ;
+
+               if (host->req->error) {
+                       writel(TIFM_FIFO_INT_SETALL,
+                              sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+                       writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
+               }
+
+               if (host_status & TIFM_MS_EOTPC)
+                       host->cmd_flags |= CMD_READY;
+               if (host_status & TIFM_MS_INT)
+                       host->cmd_flags |= CARD_READY;
+
+               rc = tifm_ms_check_status(host);
+
+       }
+
+       writel(TIFM_MS_SYS_NOT_RDY | readl(sock->addr + SOCK_MS_SYSTEM),
+              sock->addr + SOCK_MS_SYSTEM);
+       writel((~TIFM_MS_SYS_DATA) & readl(sock->addr + SOCK_MS_SYSTEM),
+              sock->addr + SOCK_MS_SYSTEM);
+
+       if (!rc)
+               tifm_ms_complete_cmd(host);
+
+       spin_unlock(&sock->lock);
+       return;
+}
+
+static void tifm_ms_request(struct memstick_host *msh)
+{
+       struct tifm_ms *host = memstick_priv(msh);
+       struct tifm_dev *sock = host->dev;
+       unsigned long flags;
+       int rc;
+
+       spin_lock_irqsave(&sock->lock, flags);
+       if (host->req) {
+               printk(KERN_ERR "%s : unfinished request detected\n",
+                      sock->dev.bus_id);
+               spin_unlock_irqrestore(&sock->lock, flags);
+               tifm_eject(host->dev);
+               return;
+       }
+
+       if (host->eject) {
+               do {
+                       rc = memstick_next_req(msh, &host->req);
+                       if (!rc)
+                               host->req->error = -ETIME;
+               } while (!rc);
+               spin_unlock_irqrestore(&sock->lock, flags);
+               return;
+       }
+
+       do {
+               rc = memstick_next_req(msh, &host->req);
+       } while (!rc && tifm_ms_issue_cmd(host));
+
+       spin_unlock_irqrestore(&sock->lock, flags);
+       return;
+}
+
+static void tifm_ms_set_param(struct memstick_host *msh,
+                             enum memstick_param param,
+                             int value)
+{
+       struct tifm_ms *host = memstick_priv(msh);
+       struct tifm_dev *sock = host->dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sock->lock, flags);
+
+       switch (param) {
+       case MEMSTICK_POWER:
+               /* this is set by card detection mechanism */
+               break;
+       case MEMSTICK_INTERFACE:
+               if (value == MEMSTICK_SERIAL) {
+                       host->mode_mask = TIFM_MS_SERIAL;
+                       writel((~TIFM_CTRL_FAST_CLK)
+                              & readl(sock->addr + SOCK_CONTROL),
+                              sock->addr + SOCK_CONTROL);
+               } else if (value == MEMSTICK_PARALLEL) {
+                       host->mode_mask = 0;
+                       writel(TIFM_CTRL_FAST_CLK
+                              | readl(sock->addr + SOCK_CONTROL),
+                              sock->addr + SOCK_CONTROL);
+               }
+               break;
+       };
+
+       spin_unlock_irqrestore(&sock->lock, flags);
+}
+
+static void tifm_ms_abort(unsigned long data)
+{
+       struct tifm_ms *host = (struct tifm_ms *)data;
+
+       dev_dbg(&host->dev->dev, "status %x\n",
+               readl(host->dev->addr + SOCK_MS_STATUS));
+       printk(KERN_ERR
+              "%s : card failed to respond for a long period of time "
+              "(%x, %x)\n",
+              host->dev->dev.bus_id, host->req ? host->req->tpc : 0,
+              host->cmd_flags);
+
+       tifm_eject(host->dev);
+}
+
+static int tifm_ms_initialize_host(struct tifm_ms *host)
+{
+       struct tifm_dev *sock = host->dev;
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+
+       host->mode_mask = TIFM_MS_SERIAL;
+       writel(0x8000, sock->addr + SOCK_MS_SYSTEM);
+       writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
+       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
+       if (tifm_has_ms_pif(sock))
+               msh->caps |= MEMSTICK_CAP_PARALLEL;
+
+       return 0;
+}
+
+static int tifm_ms_probe(struct tifm_dev *sock)
+{
+       struct memstick_host *msh;
+       struct tifm_ms *host;
+       int rc = -EIO;
+
+       if (!(TIFM_SOCK_STATE_OCCUPIED
+             & readl(sock->addr + SOCK_PRESENT_STATE))) {
+               printk(KERN_WARNING "%s : card gone, unexpectedly\n",
+                      sock->dev.bus_id);
+               return rc;
+       }
+
+       msh = memstick_alloc_host(sizeof(struct tifm_ms), &sock->dev);
+       if (!msh)
+               return -ENOMEM;
+
+       host = memstick_priv(msh);
+       tifm_set_drvdata(sock, msh);
+       host->dev = sock;
+       host->timeout_jiffies = msecs_to_jiffies(1000);
+       host->no_dma = no_dma;
+
+       setup_timer(&host->timer, tifm_ms_abort, (unsigned long)host);
+
+       msh->request = tifm_ms_request;
+       msh->set_param = tifm_ms_set_param;
+       sock->card_event = tifm_ms_card_event;
+       sock->data_event = tifm_ms_data_event;
+       rc = tifm_ms_initialize_host(host);
+
+       if (!rc)
+               rc = memstick_add_host(msh);
+       if (!rc)
+               return 0;
+
+       memstick_free_host(msh);
+       return rc;
+}
+
+static void tifm_ms_remove(struct tifm_dev *sock)
+{
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+       struct tifm_ms *host = memstick_priv(msh);
+       int rc = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sock->lock, flags);
+       host->eject = 1;
+       if (host->req) {
+               del_timer(&host->timer);
+               writel(TIFM_FIFO_INT_SETALL,
+                      sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+               writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
+               if ((host->req->io_type == MEMSTICK_IO_SG) && !host->no_dma)
+                       tifm_unmap_sg(sock, &host->req->sg, 1,
+                                     host->req->data_dir == READ
+                                     ? PCI_DMA_TODEVICE
+                                     : PCI_DMA_FROMDEVICE);
+               host->req->error = -ETIME;
+
+               do {
+                       rc = memstick_next_req(msh, &host->req);
+                       if (!rc)
+                               host->req->error = -ETIME;
+               } while (!rc);
+       }
+       spin_unlock_irqrestore(&sock->lock, flags);
+
+       memstick_remove_host(msh);
+
+       writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
+       writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
+
+       memstick_free_host(msh);
+}
+
+#ifdef CONFIG_PM
+
+static int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state)
+{
+       return 0;
+}
+
+static int tifm_ms_resume(struct tifm_dev *sock)
+{
+       struct memstick_host *msh = tifm_get_drvdata(sock);
+       struct tifm_ms *host = memstick_priv(msh);
+
+       tifm_ms_initialize_host(host);
+       memstick_detect_change(msh);
+
+       return 0;
+}
+
+#else
+
+#define tifm_ms_suspend NULL
+#define tifm_ms_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct tifm_device_id tifm_ms_id_tbl[] = {
+       { TIFM_TYPE_MS }, { 0 }
+};
+
+static struct tifm_driver tifm_ms_driver = {
+       .driver = {
+               .name  = DRIVER_NAME,
+               .owner = THIS_MODULE
+       },
+       .id_table = tifm_ms_id_tbl,
+       .probe    = tifm_ms_probe,
+       .remove   = tifm_ms_remove,
+       .suspend  = tifm_ms_suspend,
+       .resume   = tifm_ms_resume
+};
+
+static int __init tifm_ms_init(void)
+{
+       return tifm_register_driver(&tifm_ms_driver);
+}
+
+static void __exit tifm_ms_exit(void)
+{
+       tifm_unregister_driver(&tifm_ms_driver);
+}
+
+MODULE_AUTHOR("Alex Dubov");
+MODULE_DESCRIPTION("TI FlashMedia MemoryStick driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl);
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(tifm_ms_init);
+module_exit(tifm_ms_exit);
index c143a86..1abc95c 100644 (file)
@@ -114,6 +114,9 @@ config ACER_WMI
          wireless radio and bluetooth control, and on some laptops,
          exposes the mail LED and LCD backlight.
 
+         For more information about this driver see
+         <file:Documentation/laptops/acer-wmi.txt>
+
          If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
          here.
 
@@ -152,8 +155,9 @@ config FUJITSU_LAPTOP
          If you have a Fujitsu laptop, say Y or M here.
 
 config TC1100_WMI
-       tristate "HP Compaq TC1100 Tablet WMI Extras"
+       tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
        depends on X86 && !X86_64
+       depends on EXPERIMENTAL
        depends on ACPI
        select ACPI_WMI
        ---help---
@@ -192,7 +196,7 @@ config SONY_LAPTOP
          screen brightness control, Fn keys and allows powering on/off some
          devices.
 
-         Read <file:Documentation/sony-laptop.txt> for more information.
+         Read <file:Documentation/laptops/sony-laptop.txt> for more information.
 
 config SONYPI_COMPAT
        bool "Sonypi compatibility"
@@ -211,8 +215,9 @@ config THINKPAD_ACPI
          This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
          support for Fn-Fx key combinations, Bluetooth control, video
          output switching, ThinkLight control, UltraBay eject and more.
-         For more information about this driver see 
-         <file:Documentation/thinkpad-acpi.txt> and <http://ibm-acpi.sf.net/> .
+         For more information about this driver see
+         <file:Documentation/laptops/thinkpad-acpi.txt> and
+         <http://ibm-acpi.sf.net/> .
 
          This driver was formerly known as ibm-acpi.
 
index a4d6775..d7aea93 100644 (file)
@@ -428,11 +428,9 @@ static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
                if (value > max_brightness)
                        return AE_BAD_PARAMETER;
                switch (quirks->brightness) {
-               case 1:
-                       return ec_write(0x83, value);
                default:
-                       return AE_BAD_ADDRESS;
-               break;
+                       return ec_write(0x83, value);
+                       break;
                }
        default:
                return AE_BAD_ADDRESS;
index 54380da..63a089b 100644 (file)
@@ -302,6 +302,21 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
 
 #endif /* CONFIG_PM */
 
+static int tifm_7xx1_dummy_has_ms_pif(struct tifm_adapter *fm,
+                                     struct tifm_dev *sock)
+{
+       return 0;
+}
+
+static int tifm_7xx1_has_ms_pif(struct tifm_adapter *fm, struct tifm_dev *sock)
+{
+       if (((fm->num_sockets == 4) && (sock->socket_id == 2))
+           || ((fm->num_sockets == 2) && (sock->socket_id == 0)))
+               return 1;
+
+       return 0;
+}
+
 static int tifm_7xx1_probe(struct pci_dev *dev,
                           const struct pci_device_id *dev_id)
 {
@@ -336,6 +351,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
 
        INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
        fm->eject = tifm_7xx1_eject;
+       fm->has_ms_pif = tifm_7xx1_has_ms_pif;
        pci_set_drvdata(dev, fm);
 
        fm->addr = ioremap(pci_resource_start(dev, 0),
@@ -377,6 +393,7 @@ static void tifm_7xx1_remove(struct pci_dev *dev)
        int cnt;
 
        fm->eject = tifm_7xx1_dummy_eject;
+       fm->has_ms_pif = tifm_7xx1_dummy_has_ms_pif;
        writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
        mmiowb();
        free_irq(dev->irq, fm);
index 9754405..82dc72a 100644 (file)
@@ -284,6 +284,13 @@ void tifm_eject(struct tifm_dev *sock)
 }
 EXPORT_SYMBOL(tifm_eject);
 
+int tifm_has_ms_pif(struct tifm_dev *sock)
+{
+       struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);
+       return fm->has_ms_pif(fm, sock);
+}
+EXPORT_SYMBOL(tifm_has_ms_pif);
+
 int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
                int direction)
 {
index 5fef678..3b3cd0e 100644 (file)
@@ -25,8 +25,8 @@ config MMC_PXA
          If unsure, say N.
 
 config MMC_SDHCI
-       tristate "Secure Digital Host Controller Interface support  (EXPERIMENTAL)"
-       depends on PCI && EXPERIMENTAL
+       tristate "Secure Digital Host Controller Interface support"
+       depends on PCI
        help
          This select the generic Secure Digital Host Controller Interface.
          It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
@@ -118,8 +118,8 @@ config MMC_TIFM_SD
          module will be called tifm_sd.
 
 config MMC_SPI
-       tristate "MMC/SD over SPI (EXPERIMENTAL)"
-       depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
+       tristate "MMC/SD over SPI"
+       depends on MMC && SPI_MASTER && !HIGHMEM
        select CRC7
        select CRC_ITU_T
        help
index b1edcef..21acecc 100644 (file)
 
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/gpio.h>
+
 #include <asm/mach/mmc.h>
 #include <asm/arch/board.h>
 #include <asm/arch/cpu.h>
-#include <asm/arch/gpio.h>
 #include <asm/arch/at91_mci.h>
 
 #define DRIVER_NAME "at91_mci"
@@ -659,11 +660,11 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (host->board->vcc_pin) {
                switch (ios->power_mode) {
                        case MMC_POWER_OFF:
-                               at91_set_gpio_value(host->board->vcc_pin, 0);
+                               gpio_set_value(host->board->vcc_pin, 0);
                                break;
                        case MMC_POWER_UP:
                        case MMC_POWER_ON:
-                               at91_set_gpio_value(host->board->vcc_pin, 1);
+                               gpio_set_value(host->board->vcc_pin, 1);
                                break;
                }
        }
@@ -768,7 +769,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
 static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
 {
        struct at91mci_host *host = _host;
-       int present = !at91_get_gpio_value(irq);
+       int present = !gpio_get_value(irq_to_gpio(irq));
 
        /*
         * we expect this irq on both insert and remove,
@@ -793,7 +794,7 @@ static int at91_mci_get_ro(struct mmc_host *mmc)
        struct at91mci_host *host = mmc_priv(mmc);
 
        if (host->board->wp_pin) {
-               read_only = at91_get_gpio_value(host->board->wp_pin);
+               read_only = gpio_get_value(host->board->wp_pin);
                printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc),
                                (read_only ? "read-only" : "read-write") );
        }
@@ -820,8 +821,6 @@ static int __init at91_mci_probe(struct platform_device *pdev)
        struct resource *res;
        int ret;
 
-       pr_debug("Probe MCI devices\n");
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENXIO;
@@ -831,9 +830,9 @@ static int __init at91_mci_probe(struct platform_device *pdev)
 
        mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
        if (!mmc) {
-               pr_debug("Failed to allocate mmc host\n");
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               dev_dbg(&pdev->dev, "couldn't allocate mmc host\n");
+               goto fail6;
        }
 
        mmc->ops = &at91_mci_ops;
@@ -853,19 +852,44 @@ static int __init at91_mci_probe(struct platform_device *pdev)
                if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
                        mmc->caps |= MMC_CAP_4_BIT_DATA;
                else
-                       printk("AT91 MMC: 4 wire bus mode not supported"
+                       dev_warn(&pdev->dev, "4 wire bus mode not supported"
                                " - using 1 wire\n");
        }
 
+       /*
+        * Reserve GPIOs ... board init code makes sure these pins are set
+        * up as GPIOs with the right direction (input, except for vcc)
+        */
+       if (host->board->det_pin) {
+               ret = gpio_request(host->board->det_pin, "mmc_detect");
+               if (ret < 0) {
+                       dev_dbg(&pdev->dev, "couldn't claim card detect pin\n");
+                       goto fail5;
+               }
+       }
+       if (host->board->wp_pin) {
+               ret = gpio_request(host->board->wp_pin, "mmc_wp");
+               if (ret < 0) {
+                       dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n");
+                       goto fail4;
+               }
+       }
+       if (host->board->vcc_pin) {
+               ret = gpio_request(host->board->vcc_pin, "mmc_vcc");
+               if (ret < 0) {
+                       dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n");
+                       goto fail3;
+               }
+       }
+
        /*
         * Get Clock
         */
        host->mci_clk = clk_get(&pdev->dev, "mci_clk");
        if (IS_ERR(host->mci_clk)) {
-               printk(KERN_ERR "AT91 MMC: no clock defined.\n");
-               mmc_free_host(mmc);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENODEV;
+               ret = -ENODEV;
+               dev_dbg(&pdev->dev, "no mci_clk?\n");
+               goto fail2;
        }
 
        /*
@@ -873,10 +897,8 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         */
        host->baseaddr = ioremap(res->start, res->end - res->start + 1);
        if (!host->baseaddr) {
-               clk_put(host->mci_clk);
-               mmc_free_host(mmc);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto fail1;
        }
 
        /*
@@ -890,15 +912,11 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * Allocate the MCI interrupt
         */
        host->irq = platform_get_irq(pdev, 0);
-       ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
+       ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED,
+                       mmc_hostname(mmc), host);
        if (ret) {
-               printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n");
-               clk_disable(host->mci_clk);
-               clk_put(host->mci_clk);
-               mmc_free_host(mmc);
-               iounmap(host->baseaddr);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return ret;
+               dev_dbg(&pdev->dev, "request MCI interrupt failed\n");
+               goto fail0;
        }
 
        platform_set_drvdata(pdev, mmc);
@@ -907,8 +925,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * Add host to MMC layer
         */
        if (host->board->det_pin) {
-               host->present = !at91_get_gpio_value(host->board->det_pin);
-               device_init_wakeup(&pdev->dev, 1);
+               host->present = !gpio_get_value(host->board->det_pin);
        }
        else
                host->present = -1;
@@ -919,15 +936,38 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * monitor card insertion/removal if we can
         */
        if (host->board->det_pin) {
-               ret = request_irq(host->board->det_pin, at91_mmc_det_irq,
-                               0, DRIVER_NAME, host);
+               ret = request_irq(gpio_to_irq(host->board->det_pin),
+                               at91_mmc_det_irq, 0, mmc_hostname(mmc), host);
                if (ret)
-                       printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n");
+                       dev_warn(&pdev->dev, "request MMC detect irq failed\n");
+               else
+                       device_init_wakeup(&pdev->dev, 1);
        }
 
        pr_debug("Added MCI driver\n");
 
        return 0;
+
+fail0:
+       clk_disable(host->mci_clk);
+       iounmap(host->baseaddr);
+fail1:
+       clk_put(host->mci_clk);
+fail2:
+       if (host->board->vcc_pin)
+               gpio_free(host->board->vcc_pin);
+fail3:
+       if (host->board->wp_pin)
+               gpio_free(host->board->wp_pin);
+fail4:
+       if (host->board->det_pin)
+               gpio_free(host->board->det_pin);
+fail5:
+       mmc_free_host(mmc);
+fail6:
+       release_mem_region(res->start, res->end - res->start + 1);
+       dev_err(&pdev->dev, "probe failed, err %d\n", ret);
+       return ret;
 }
 
 /*
@@ -945,9 +985,10 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
        host = mmc_priv(mmc);
 
        if (host->board->det_pin) {
+               if (device_can_wakeup(&pdev->dev))
+                       free_irq(gpio_to_irq(host->board->det_pin), host);
                device_init_wakeup(&pdev->dev, 0);
-               free_irq(host->board->det_pin, host);
-               cancel_delayed_work(&host->mmc->detect);
+               gpio_free(host->board->det_pin);
        }
 
        at91_mci_disable(host);
@@ -957,6 +998,11 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
        clk_disable(host->mci_clk);                     /* Disable the peripheral clock */
        clk_put(host->mci_clk);
 
+       if (host->board->vcc_pin)
+               gpio_free(host->board->vcc_pin);
+       if (host->board->wp_pin)
+               gpio_free(host->board->wp_pin);
+
        iounmap(host->baseaddr);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(res->start, res->end - res->start + 1);
index 1e87045..a16d760 100644 (file)
@@ -41,10 +41,91 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
 
 MODULE_DEVICE_TABLE(pci, pci_ids);
 
+static int ricoh_mmc_disable(struct pci_dev *fw_dev)
+{
+       u8 write_enable;
+       u8 write_target;
+       u8 disable;
+
+       if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
+               /* via RL5C476 */
+
+               pci_read_config_byte(fw_dev, 0xB7, &disable);
+               if (disable & 0x02) {
+                       printk(KERN_INFO DRIVER_NAME
+                               ": Controller already disabled. " \
+                               "Nothing to do.\n");
+                       return -ENODEV;
+               }
+
+               pci_read_config_byte(fw_dev, 0x8E, &write_enable);
+               pci_write_config_byte(fw_dev, 0x8E, 0xAA);
+               pci_read_config_byte(fw_dev, 0x8D, &write_target);
+               pci_write_config_byte(fw_dev, 0x8D, 0xB7);
+               pci_write_config_byte(fw_dev, 0xB7, disable | 0x02);
+               pci_write_config_byte(fw_dev, 0x8E, write_enable);
+               pci_write_config_byte(fw_dev, 0x8D, write_target);
+       } else {
+               /* via R5C832 */
+
+               pci_read_config_byte(fw_dev, 0xCB, &disable);
+               if (disable & 0x02) {
+                       printk(KERN_INFO DRIVER_NAME
+                              ": Controller already disabled. " \
+                               "Nothing to do.\n");
+                       return -ENODEV;
+               }
+
+               pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+               pci_write_config_byte(fw_dev, 0xCA, 0x57);
+               pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
+               pci_write_config_byte(fw_dev, 0xCA, write_enable);
+       }
+
+       printk(KERN_INFO DRIVER_NAME
+              ": Controller is now disabled.\n");
+
+       return 0;
+}
+
+static int ricoh_mmc_enable(struct pci_dev *fw_dev)
+{
+       u8 write_enable;
+       u8 write_target;
+       u8 disable;
+
+       if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
+               /* via RL5C476 */
+
+               pci_read_config_byte(fw_dev, 0x8E, &write_enable);
+               pci_write_config_byte(fw_dev, 0x8E, 0xAA);
+               pci_read_config_byte(fw_dev, 0x8D, &write_target);
+               pci_write_config_byte(fw_dev, 0x8D, 0xB7);
+               pci_read_config_byte(fw_dev, 0xB7, &disable);
+               pci_write_config_byte(fw_dev, 0xB7, disable & ~0x02);
+               pci_write_config_byte(fw_dev, 0x8E, write_enable);
+               pci_write_config_byte(fw_dev, 0x8D, write_target);
+       } else {
+               /* via R5C832 */
+
+               pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+               pci_read_config_byte(fw_dev, 0xCB, &disable);
+               pci_write_config_byte(fw_dev, 0xCA, 0x57);
+               pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
+               pci_write_config_byte(fw_dev, 0xCA, write_enable);
+       }
+
+       printk(KERN_INFO DRIVER_NAME
+              ": Controller is now re-enabled.\n");
+
+       return 0;
+}
+
 static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
                                     const struct pci_device_id *ent)
 {
        u8 rev;
+       u8 ctrlfound = 0;
 
        struct pci_dev *fw_dev = NULL;
 
@@ -58,34 +139,38 @@ static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
                pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
                (int)rev);
 
-       while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
+       while ((fw_dev =
+               pci_get_device(PCI_VENDOR_ID_RICOH,
+                       PCI_DEVICE_ID_RICOH_RL5C476, fw_dev))) {
                if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
                    pdev->bus == fw_dev->bus) {
-                       u8 write_enable;
-                       u8 disable;
-
-                       pci_read_config_byte(fw_dev, 0xCB, &disable);
-                       if (disable & 0x02) {
-                               printk(KERN_INFO DRIVER_NAME
-                                      ": Controller already disabled. Nothing to do.\n");
+                       if (ricoh_mmc_disable(fw_dev) != 0)
                                return -ENODEV;
-                       }
-
-                       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
-                       pci_write_config_byte(fw_dev, 0xCA, 0x57);
-                       pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
-                       pci_write_config_byte(fw_dev, 0xCA, write_enable);
 
                        pci_set_drvdata(pdev, fw_dev);
 
-                       printk(KERN_INFO DRIVER_NAME
-                              ": Controller is now disabled.\n");
-
+                       ++ctrlfound;
                        break;
                }
        }
 
-       if (pci_get_drvdata(pdev) == NULL) {
+       fw_dev = NULL;
+
+       while (!ctrlfound &&
+           (fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH,
+                                       PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
+               if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
+                   pdev->bus == fw_dev->bus) {
+                       if (ricoh_mmc_disable(fw_dev) != 0)
+                               return -ENODEV;
+
+                       pci_set_drvdata(pdev, fw_dev);
+
+                       ++ctrlfound;
+               }
+       }
+
+       if (!ctrlfound) {
                printk(KERN_WARNING DRIVER_NAME
                       ": Main firewire function not found. Cannot disable controller.\n");
                return -ENODEV;
@@ -96,30 +181,51 @@ static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
 
 static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
 {
-       u8 write_enable;
-       u8 disable;
        struct pci_dev *fw_dev = NULL;
 
        fw_dev = pci_get_drvdata(pdev);
        BUG_ON(fw_dev == NULL);
 
-       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
-       pci_read_config_byte(fw_dev, 0xCB, &disable);
-       pci_write_config_byte(fw_dev, 0xCA, 0x57);
-       pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
-       pci_write_config_byte(fw_dev, 0xCA, write_enable);
-
-       printk(KERN_INFO DRIVER_NAME
-              ": Controller is now re-enabled.\n");
+       ricoh_mmc_enable(fw_dev);
 
        pci_set_drvdata(pdev, NULL);
 }
 
+static int ricoh_mmc_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct pci_dev *fw_dev = NULL;
+
+       fw_dev = pci_get_drvdata(pdev);
+       BUG_ON(fw_dev == NULL);
+
+       printk(KERN_INFO DRIVER_NAME ": Suspending.\n");
+
+       ricoh_mmc_enable(fw_dev);
+
+       return 0;
+}
+
+static int ricoh_mmc_resume(struct pci_dev *pdev)
+{
+       struct pci_dev *fw_dev = NULL;
+
+       fw_dev = pci_get_drvdata(pdev);
+       BUG_ON(fw_dev == NULL);
+
+       printk(KERN_INFO DRIVER_NAME ": Resuming.\n");
+
+       ricoh_mmc_disable(fw_dev);
+
+       return 0;
+}
+
 static struct pci_driver ricoh_mmc_driver = {
        .name =         DRIVER_NAME,
        .id_table =     pci_ids,
        .probe =        ricoh_mmc_probe,
        .remove =       __devexit_p(ricoh_mmc_remove),
+       .suspend =      ricoh_mmc_suspend,
+       .resume =       ricoh_mmc_resume,
 };
 
 /*****************************************************************************\
index 785bbdc..4b673aa 100644 (file)
 
 static unsigned int debug_quirks = 0;
 
+/* For multi controllers in one platform case */
+static u16 chip_index = 0;
+static spinlock_t index_lock;
+
 /*
  * Different quirks to handle when the hardware deviates from a strict
  * interpretation of the SDHCI specification.
@@ -1320,7 +1324,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
 
        DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq);
 
-       snprintf(host->slot_descr, 20, "sdhci:slot%d", slot);
+       snprintf(host->slot_descr, 20, "sdhc%d:slot%d", chip->index, slot);
 
        ret = pci_request_region(pdev, host->bar, host->slot_descr);
        if (ret)
@@ -1585,6 +1589,11 @@ static int __devinit sdhci_probe(struct pci_dev *pdev,
        chip->num_slots = slots;
        pci_set_drvdata(pdev, chip);
 
+       /* Add for multi controller case */
+       spin_lock(&index_lock);
+       chip->index = chip_index++;
+       spin_unlock(&index_lock);
+
        for (i = 0;i < slots;i++) {
                ret = sdhci_probe_slot(pdev, i);
                if (ret) {
@@ -1645,6 +1654,8 @@ static int __init sdhci_drv_init(void)
                ": Secure Digital Host Controller Interface driver\n");
        printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
 
+       spin_lock_init(&index_lock);
+
        return pci_register_driver(&sdhci_driver);
 }
 
index e4d77b0..d5a38f1 100644 (file)
@@ -208,6 +208,7 @@ struct sdhci_chip {
 
        unsigned long           quirks;
 
+       int                     index;          /* Index for chip0, chip1 ...*/
        int                     num_slots;      /* Slots on controller */
        struct sdhci_host       *hosts[0];      /* Pointers to hosts */
 };
index 19e1594..8dab696 100644 (file)
  *  Overview:
  *   This is a device driver for the NAND flash controller found on
  *   the AMD CS5535/CS5536 companion chipsets for the Geode processor.
+ *   mtd-id for command line partitioning is cs553x_nand_cs[0-3]
+ *   where 0-3 reflects the chip select for NAND.
  *
  */
 
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -244,6 +247,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
                goto out_ior;
        }
 
+       new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
+
        cs553x_mtd[cs] = new_mtd;
        goto out;
 
@@ -272,12 +277,21 @@ static int is_geode(void)
        return 0;
 }
 
+
+#ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+
 static int __init cs553x_init(void)
 {
        int err = -ENXIO;
        int i;
        uint64_t val;
 
+       int mtd_parts_nb = 0;
+       struct mtd_partition *mtd_parts = NULL;
+
        /* If the CPU isn't a Geode GX or LX, abort */
        if (!is_geode())
                return -ENXIO;
@@ -290,7 +304,7 @@ static int __init cs553x_init(void)
 
        /* If it doesn't have the NAND controller enabled, abort */
        rdmsrl(MSR_DIVIL_BALL_OPTS, val);
-       if (val & 1) {
+       if (val & PIN_OPT_IDE) {
                printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
                return -ENXIO;
        }
@@ -306,9 +320,19 @@ static int __init cs553x_init(void)
           do mtdconcat etc. if we want to. */
        for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
                if (cs553x_mtd[i]) {
-                       add_mtd_device(cs553x_mtd[i]);
 
                        /* If any devices registered, return success. Else the last error. */
+#ifdef CONFIG_MTD_PARTITIONS
+                       mtd_parts_nb = parse_mtd_partitions(cs553x_mtd[i], part_probes, &mtd_parts, 0);
+                       if (mtd_parts_nb > 0) {
+                               printk(KERN_NOTICE "Using command line partition definition\n");
+                               add_mtd_partitions(cs553x_mtd[i], mtd_parts, mtd_parts_nb);
+                       } else {
+                               add_mtd_device(cs553x_mtd[i]);
+                       }
+#else
+                       add_mtd_device(cs553x_mtd[i]);
+#endif
                        err = 0;
                }
        }
@@ -328,13 +352,14 @@ static void __exit cs553x_cleanup(void)
                void __iomem *mmio_base;
 
                if (!mtd)
-                       break;
+                       continue;
 
                this = cs553x_mtd[i]->priv;
                mmio_base = this->IO_ADDR_R;
 
                /* Release resources, unregister device */
                nand_release(cs553x_mtd[i]);
+               kfree(cs553x_mtd[i]->name);
                cs553x_mtd[i] = NULL;
 
                /* unmap physical address */
index 5e2d763..0f8aca8 100644 (file)
@@ -554,7 +554,6 @@ static void x25_asy_receive_buf(struct tty_struct *tty, const unsigned char *cp,
 static int x25_asy_open_tty(struct tty_struct *tty)
 {
        struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
-       struct tty_ldisc *ld;
        int err;
 
        /* First make sure we're not already connected. */
index 519b4ff..8b22281 100644 (file)
@@ -38,6 +38,7 @@ config PCMCIA_DEBUG
 config PCMCIA
        tristate "16-bit PCMCIA support"
        select CRC32
+       select HAVE_IDE
        default y
        ---help---
           This option enables support for 16-bit PCMCIA cards. Most older
index a64626a..b35f9bf 100644 (file)
@@ -71,7 +71,6 @@ static int read_regs(struct device *dev, unsigned char *regs, int no_regs)
 
 static int r9701_get_datetime(struct device *dev, struct rtc_time *dt)
 {
-       unsigned long time;
        int ret;
        unsigned char buf[] = { RSECCNT, RMINCNT, RHRCNT,
                                RDAYCNT, RMONCNT, RYRCNT };
index 6807162..f47f4a7 100644 (file)
@@ -3,7 +3,7 @@
  *    SCLP VT220 terminal driver.
  *
  *  S390 version
- *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Copyright IBM Corp. 2003,2008
  *    Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
  */
 
@@ -632,6 +632,9 @@ static void __init __sclp_vt220_cleanup(void)
                else
                        free_bootmem((unsigned long) page, PAGE_SIZE);
        }
+       if (!list_empty(&sclp_vt220_register.list))
+               sclp_unregister(&sclp_vt220_register);
+       sclp_vt220_initialized = 0;
 }
 
 static int __init __sclp_vt220_init(void)
@@ -639,6 +642,7 @@ static int __init __sclp_vt220_init(void)
        void *page;
        int i;
        int num_pages;
+       int rc;
 
        if (sclp_vt220_initialized)
                return 0;
@@ -667,7 +671,14 @@ static int __init __sclp_vt220_init(void)
                }
                list_add_tail((struct list_head *) page, &sclp_vt220_empty);
        }
-       return 0;
+       rc = sclp_register(&sclp_vt220_register);
+       if (rc) {
+               printk(KERN_ERR SCLP_VT220_PRINT_HEADER
+                      "could not register vt220 - "
+                      "sclp_register returned %d\n", rc);
+               __sclp_vt220_cleanup();
+       }
+       return rc;
 }
 
 static const struct tty_operations sclp_vt220_ops = {
@@ -688,22 +699,17 @@ static int __init sclp_vt220_tty_init(void)
 {
        struct tty_driver *driver;
        int rc;
+       int cleanup;
 
        /* Note: we're not testing for CONSOLE_IS_SCLP here to preserve
         * symmetry between VM and LPAR systems regarding ttyS1. */
        driver = alloc_tty_driver(1);
        if (!driver)
                return -ENOMEM;
+       cleanup = !sclp_vt220_initialized;
        rc = __sclp_vt220_init();
        if (rc)
                goto out_driver;
-       rc = sclp_register(&sclp_vt220_register);
-       if (rc) {
-               printk(KERN_ERR SCLP_VT220_PRINT_HEADER
-                      "could not register tty - "
-                      "sclp_register returned %d\n", rc);
-               goto out_init;
-       }
 
        driver->owner = THIS_MODULE;
        driver->driver_name = SCLP_VT220_DRIVER_NAME;
@@ -721,15 +727,14 @@ static int __init sclp_vt220_tty_init(void)
                printk(KERN_ERR SCLP_VT220_PRINT_HEADER
                       "could not register tty - "
                       "tty_register_driver returned %d\n", rc);
-               goto out_sclp;
+               goto out_init;
        }
        sclp_vt220_driver = driver;
        return 0;
 
-out_sclp:
-       sclp_unregister(&sclp_vt220_register);
 out_init:
-       __sclp_vt220_cleanup();
+       if (cleanup)
+               __sclp_vt220_cleanup();
 out_driver:
        put_tty_driver(driver);
        return rc;
index e2a781b..097fc09 100644 (file)
@@ -3189,13 +3189,11 @@ qdio_establish(struct qdio_initialize *init_data)
        spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags);
 
        ccw_device_set_options_mask(cdev, 0);
-       result=ccw_device_start_timeout(cdev,&irq_ptr->ccw,
-                                       QDIO_DOING_ESTABLISH,0, 0,
-                                       QDIO_ESTABLISH_TIMEOUT);
+       result = ccw_device_start(cdev, &irq_ptr->ccw,
+                               QDIO_DOING_ESTABLISH, 0, 0);
        if (result) {
-               result2=ccw_device_start_timeout(cdev,&irq_ptr->ccw,
-                                                QDIO_DOING_ESTABLISH,0,0,
-                                                QDIO_ESTABLISH_TIMEOUT);
+               result2 = ccw_device_start(cdev, &irq_ptr->ccw,
+                                       QDIO_DOING_ESTABLISH, 0, 0);
                sprintf(dbf_text,"eq:io%4x",result);
                QDIO_DBF_TEXT2(1,setup,dbf_text);
                if (result2) {
@@ -3219,10 +3217,10 @@ qdio_establish(struct qdio_initialize *init_data)
                return result;
        }
        
-       /* Timeout is cared for already by using ccw_device_start_timeout(). */
-       wait_event_interruptible(cdev->private->wait_q,
-                irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED ||
-                irq_ptr->state == QDIO_IRQ_STATE_ERR);
+       wait_event_interruptible_timeout(cdev->private->wait_q,
+               irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED ||
+               irq_ptr->state == QDIO_IRQ_STATE_ERR,
+               QDIO_ESTABLISH_TIMEOUT);
 
        if (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED)
                result = 0;
index 67aaff3..d0c6fd3 100644 (file)
@@ -61,9 +61,9 @@ module_param_named(domain, ap_domain_index, int, 0000);
 MODULE_PARM_DESC(domain, "domain index for ap devices");
 EXPORT_SYMBOL(ap_domain_index);
 
-static int ap_thread_flag = 1;
+static int ap_thread_flag = 0;
 module_param_named(poll_thread, ap_thread_flag, int, 0000);
-MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on).");
+MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
 
 static struct device *ap_root_device = NULL;
 static DEFINE_SPINLOCK(ap_device_lock);
index 4c54954..741d818 100644 (file)
@@ -44,8 +44,8 @@ clean-files += aic79xx_seq.h aic79xx_reg.h aic79xx_reg_print.c
 
 # Dependencies for generated files need to be listed explicitly
 
-$(addprefix $(src)/,$(aic7xxx-y:.o=.c)): $(obj)/aic7xxx_seq.h $(obj)/aic7xxx_reg.h
-$(addprefix $(src)/,$(aic79xx-y:.o=.c)): $(obj)/aic79xx_seq.h $(obj)/aic79xx_reg.h
+$(addprefix $(obj)/,$(aic7xxx-y)): $(obj)/aic7xxx_seq.h $(obj)/aic7xxx_reg.h
+$(addprefix $(obj)/,$(aic79xx-y)): $(obj)/aic79xx_seq.h $(obj)/aic79xx_reg.h
 
 aic7xxx-gen-$(CONFIG_AIC7XXX_BUILD_FIRMWARE)   := $(obj)/aic7xxx_reg.h
 aic7xxx-gen-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) += $(obj)/aic7xxx_reg_print.c
index f4e3874..7dd9b50 100644 (file)
@@ -892,7 +892,16 @@ out:
        return err;
 }
 
-#define DIO_CREDITS (EXT4_RESERVE_TRANS_BLOCKS + 32)
+/* Maximum number of blocks we map for direct IO at once. */
+#define DIO_MAX_BLOCKS 4096
+/*
+ * Number of credits we need for writing DIO_MAX_BLOCKS:
+ * We need sb + group descriptor + bitmap + inode -> 4
+ * For B blocks with A block pointers per block we need:
+ * 1 (triple ind.) + (B/A/A + 2) (doubly ind.) + (B/A + 2) (indirect).
+ * If we plug in 4096 for B and 256 for A (for 1KB block size), we get 25.
+ */
+#define DIO_CREDITS 25
 
 int ext4_get_blocks_wrap(handle_t *handle, struct inode *inode, sector_t block,
                        unsigned long max_blocks, struct buffer_head *bh,
@@ -939,49 +948,31 @@ static int ext4_get_block(struct inode *inode, sector_t iblock,
                        struct buffer_head *bh_result, int create)
 {
        handle_t *handle = ext4_journal_current_handle();
-       int ret = 0;
+       int ret = 0, started = 0;
        unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
 
-       if (!create)
-               goto get_block;         /* A read */
-
-       if (max_blocks == 1)
-               goto get_block;         /* A single block get */
-
-       if (handle->h_transaction->t_state == T_LOCKED) {
-               /*
-                * Huge direct-io writes can hold off commits for long
-                * periods of time.  Let this commit run.
-                */
-               ext4_journal_stop(handle);
-               handle = ext4_journal_start(inode, DIO_CREDITS);
-               if (IS_ERR(handle))
+       if (create && !handle) {
+               /* Direct IO write... */
+               if (max_blocks > DIO_MAX_BLOCKS)
+                       max_blocks = DIO_MAX_BLOCKS;
+               handle = ext4_journal_start(inode, DIO_CREDITS +
+                             2 * EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb));
+               if (IS_ERR(handle)) {
                        ret = PTR_ERR(handle);
-               goto get_block;
-       }
-
-       if (handle->h_buffer_credits <= EXT4_RESERVE_TRANS_BLOCKS) {
-               /*
-                * Getting low on buffer credits...
-                */
-               ret = ext4_journal_extend(handle, DIO_CREDITS);
-               if (ret > 0) {
-                       /*
-                        * Couldn't extend the transaction.  Start a new one.
-                        */
-                       ret = ext4_journal_restart(handle, DIO_CREDITS);
+                       goto out;
                }
+               started = 1;
        }
 
-get_block:
-       if (ret == 0) {
-               ret = ext4_get_blocks_wrap(handle, inode, iblock,
+       ret = ext4_get_blocks_wrap(handle, inode, iblock,
                                        max_blocks, bh_result, create, 0);
-               if (ret > 0) {
-                       bh_result->b_size = (ret << inode->i_blkbits);
-                       ret = 0;
-               }
+       if (ret > 0) {
+               bh_result->b_size = (ret << inode->i_blkbits);
+               ret = 0;
        }
+       if (started)
+               ext4_journal_stop(handle);
+out:
        return ret;
 }
 
@@ -1671,7 +1662,8 @@ static int ext4_releasepage(struct page *page, gfp_t wait)
  * if the machine crashes during the write.
  *
  * If the O_DIRECT write is intantiating holes inside i_size and the machine
- * crashes then stale disk data _may_ be exposed inside the file.
+ * crashes then stale disk data _may_ be exposed inside the file. But current
+ * VFS code falls back into buffered path in that case so we are safe.
  */
 static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
                        const struct iovec *iov, loff_t offset,
@@ -1680,7 +1672,7 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
        struct ext4_inode_info *ei = EXT4_I(inode);
-       handle_t *handle = NULL;
+       handle_t *handle;
        ssize_t ret;
        int orphan = 0;
        size_t count = iov_length(iov, nr_segs);
@@ -1688,17 +1680,21 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
        if (rw == WRITE) {
                loff_t final_size = offset + count;
 
-               handle = ext4_journal_start(inode, DIO_CREDITS);
-               if (IS_ERR(handle)) {
-                       ret = PTR_ERR(handle);
-                       goto out;
-               }
                if (final_size > inode->i_size) {
+                       /* Credits for sb + inode write */
+                       handle = ext4_journal_start(inode, 2);
+                       if (IS_ERR(handle)) {
+                               ret = PTR_ERR(handle);
+                               goto out;
+                       }
                        ret = ext4_orphan_add(handle, inode);
-                       if (ret)
-                               goto out_stop;
+                       if (ret) {
+                               ext4_journal_stop(handle);
+                               goto out;
+                       }
                        orphan = 1;
                        ei->i_disksize = inode->i_size;
+                       ext4_journal_stop(handle);
                }
        }
 
@@ -1706,18 +1702,21 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
                                 offset, nr_segs,
                                 ext4_get_block, NULL);
 
-       /*
-        * Reacquire the handle: ext4_get_block() can restart the transaction
-        */
-       handle = ext4_journal_current_handle();
-
-out_stop:
-       if (handle) {
+       if (orphan) {
                int err;
 
-               if (orphan && inode->i_nlink)
+               /* Credits for sb + inode write */
+               handle = ext4_journal_start(inode, 2);
+               if (IS_ERR(handle)) {
+                       /* This is really bad luck. We've written the data
+                        * but cannot extend i_size. Bail out and pretend
+                        * the write failed... */
+                       ret = PTR_ERR(handle);
+                       goto out;
+               }
+               if (inode->i_nlink)
                        ext4_orphan_del(handle, inode);
-               if (orphan && ret > 0) {
+               if (ret > 0) {
                        loff_t end = offset + ret;
                        if (end > inode->i_size) {
                                ei->i_disksize = end;
@@ -2758,13 +2757,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                ei->i_data[block] = raw_inode->i_block[block];
        INIT_LIST_HEAD(&ei->i_orphan);
 
-       if (inode->i_ino >= EXT4_FIRST_INO(inode->i_sb) + 1 &&
-           EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
-               /*
-                * When mke2fs creates big inodes it does not zero out
-                * the unused bytes above EXT4_GOOD_OLD_INODE_SIZE,
-                * so ignore those first few inodes.
-                */
+       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
                ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
                if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
                    EXT4_INODE_SIZE(inode->i_sb)) {
index 76e5fed..dd0fcfc 100644 (file)
 #define MB_DEFAULT_GROUP_PREALLOC      512
 
 static struct kmem_cache *ext4_pspace_cachep;
+static struct kmem_cache *ext4_ac_cachep;
 
 #ifdef EXT4_BB_MAX_BLOCKS
 #undef EXT4_BB_MAX_BLOCKS
@@ -680,7 +681,6 @@ static void *mb_find_buddy(struct ext4_buddy *e4b, int order, int *max)
 {
        char *bb;
 
-       /* FIXME!! is this needed */
        BUG_ON(EXT4_MB_BITMAP(e4b) == EXT4_MB_BUDDY(e4b));
        BUG_ON(max == NULL);
 
@@ -964,7 +964,7 @@ static void ext4_mb_generate_buddy(struct super_block *sb,
        grp->bb_fragments = fragments;
 
        if (free != grp->bb_free) {
-               printk(KERN_DEBUG
+               ext4_error(sb, __FUNCTION__,
                        "EXT4-fs: group %lu: %u blocks in bitmap, %u in gd\n",
                        group, free, grp->bb_free);
                grp->bb_free = free;
@@ -1821,13 +1821,24 @@ static void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
                i = ext4_find_next_zero_bit(bitmap,
                                                EXT4_BLOCKS_PER_GROUP(sb), i);
                if (i >= EXT4_BLOCKS_PER_GROUP(sb)) {
-                       BUG_ON(free != 0);
+                       /*
+                        * IF we corrupt the bitmap  we won't find any
+                        * free blocks even though group info says we
+                        * we have free blocks
+                        */
+                       ext4_error(sb, __FUNCTION__, "%d free blocks as per "
+                                       "group info. But bitmap says 0\n",
+                                       free);
                        break;
                }
 
                mb_find_extent(e4b, 0, i, ac->ac_g_ex.fe_len, &ex);
                BUG_ON(ex.fe_len <= 0);
-               BUG_ON(free < ex.fe_len);
+               if (free < ex.fe_len) {
+                       ext4_error(sb, __FUNCTION__, "%d free blocks as per "
+                                       "group info. But got %d blocks\n",
+                                       free, ex.fe_len);
+               }
 
                ext4_mb_measure_extent(ac, &ex, e4b);
 
@@ -2959,12 +2970,19 @@ int __init init_ext4_mballoc(void)
        if (ext4_pspace_cachep == NULL)
                return -ENOMEM;
 
+       ext4_ac_cachep =
+               kmem_cache_create("ext4_alloc_context",
+                                    sizeof(struct ext4_allocation_context),
+                                    0, SLAB_RECLAIM_ACCOUNT, NULL);
+       if (ext4_ac_cachep == NULL) {
+               kmem_cache_destroy(ext4_pspace_cachep);
+               return -ENOMEM;
+       }
 #ifdef CONFIG_PROC_FS
        proc_root_ext4 = proc_mkdir(EXT4_ROOT, proc_root_fs);
        if (proc_root_ext4 == NULL)
                printk(KERN_ERR "EXT4-fs: Unable to create %s\n", EXT4_ROOT);
 #endif
-
        return 0;
 }
 
@@ -2972,6 +2990,7 @@ void exit_ext4_mballoc(void)
 {
        /* XXX: synchronize_rcu(); */
        kmem_cache_destroy(ext4_pspace_cachep);
+       kmem_cache_destroy(ext4_ac_cachep);
 #ifdef CONFIG_PROC_FS
        remove_proc_entry(EXT4_ROOT, proc_root_fs);
 #endif
@@ -3069,7 +3088,7 @@ static int ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
 
 out_err:
        sb->s_dirt = 1;
-       put_bh(bitmap_bh);
+       brelse(bitmap_bh);
        return err;
 }
 
@@ -3354,13 +3373,10 @@ static void ext4_mb_use_group_pa(struct ext4_allocation_context *ac,
        ac->ac_pa = pa;
 
        /* we don't correct pa_pstart or pa_plen here to avoid
-        * possible race when tte group is being loaded concurrently
+        * possible race when the group is being loaded concurrently
         * instead we correct pa later, after blocks are marked
-        * in on-disk bitmap -- see ext4_mb_release_context() */
-       /*
-        * FIXME!! but the other CPUs can look at this particular
-        * pa and think that it have enought free blocks if we
-        * don't update pa_free here right ?
+        * in on-disk bitmap -- see ext4_mb_release_context()
+        * Other CPUs are prevented from allocating from this pa by lg_mutex
         */
        mb_debug("use %u/%u from group pa %p\n", pa->pa_lstart-len, len, pa);
 }
@@ -3699,7 +3715,7 @@ static int ext4_mb_release_inode_pa(struct ext4_buddy *e4b,
                                struct buffer_head *bitmap_bh,
                                struct ext4_prealloc_space *pa)
 {
-       struct ext4_allocation_context ac;
+       struct ext4_allocation_context *ac;
        struct super_block *sb = e4b->bd_sb;
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        unsigned long end;
@@ -3715,9 +3731,13 @@ static int ext4_mb_release_inode_pa(struct ext4_buddy *e4b,
        BUG_ON(group != e4b->bd_group && pa->pa_len != 0);
        end = bit + pa->pa_len;
 
-       ac.ac_sb = sb;
-       ac.ac_inode = pa->pa_inode;
-       ac.ac_op = EXT4_MB_HISTORY_DISCARD;
+       ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
+
+       if (ac) {
+               ac->ac_sb = sb;
+               ac->ac_inode = pa->pa_inode;
+               ac->ac_op = EXT4_MB_HISTORY_DISCARD;
+       }
 
        while (bit < end) {
                bit = ext4_find_next_zero_bit(bitmap_bh->b_data, end, bit);
@@ -3733,24 +3753,28 @@ static int ext4_mb_release_inode_pa(struct ext4_buddy *e4b,
                                (unsigned) group);
                free += next - bit;
 
-               ac.ac_b_ex.fe_group = group;
-               ac.ac_b_ex.fe_start = bit;
-               ac.ac_b_ex.fe_len = next - bit;
-               ac.ac_b_ex.fe_logical = 0;
-               ext4_mb_store_history(&ac);
+               if (ac) {
+                       ac->ac_b_ex.fe_group = group;
+                       ac->ac_b_ex.fe_start = bit;
+                       ac->ac_b_ex.fe_len = next - bit;
+                       ac->ac_b_ex.fe_logical = 0;
+                       ext4_mb_store_history(ac);
+               }
 
                mb_free_blocks(pa->pa_inode, e4b, bit, next - bit);
                bit = next + 1;
        }
        if (free != pa->pa_free) {
-               printk(KERN_ERR "pa %p: logic %lu, phys. %lu, len %lu\n",
+               printk(KERN_CRIT "pa %p: logic %lu, phys. %lu, len %lu\n",
                        pa, (unsigned long) pa->pa_lstart,
                        (unsigned long) pa->pa_pstart,
                        (unsigned long) pa->pa_len);
-               printk(KERN_ERR "free %u, pa_free %u\n", free, pa->pa_free);
+               ext4_error(sb, __FUNCTION__, "free %u, pa_free %u\n",
+                                               free, pa->pa_free);
        }
-       BUG_ON(free != pa->pa_free);
        atomic_add(free, &sbi->s_mb_discarded);
+       if (ac)
+               kmem_cache_free(ext4_ac_cachep, ac);
 
        return err;
 }
@@ -3758,12 +3782,15 @@ static int ext4_mb_release_inode_pa(struct ext4_buddy *e4b,
 static int ext4_mb_release_group_pa(struct ext4_buddy *e4b,
                                struct ext4_prealloc_space *pa)
 {
-       struct ext4_allocation_context ac;
+       struct ext4_allocation_context *ac;
        struct super_block *sb = e4b->bd_sb;
        ext4_group_t group;
        ext4_grpblk_t bit;
 
-       ac.ac_op = EXT4_MB_HISTORY_DISCARD;
+       ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
+
+       if (ac)
+               ac->ac_op = EXT4_MB_HISTORY_DISCARD;
 
        BUG_ON(pa->pa_deleted == 0);
        ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, &bit);
@@ -3771,13 +3798,16 @@ static int ext4_mb_release_group_pa(struct ext4_buddy *e4b,
        mb_free_blocks(pa->pa_inode, e4b, bit, pa->pa_len);
        atomic_add(pa->pa_len, &EXT4_SB(sb)->s_mb_discarded);
 
-       ac.ac_sb = sb;
-       ac.ac_inode = NULL;
-       ac.ac_b_ex.fe_group = group;
-       ac.ac_b_ex.fe_start = bit;
-       ac.ac_b_ex.fe_len = pa->pa_len;
-       ac.ac_b_ex.fe_logical = 0;
-       ext4_mb_store_history(&ac);
+       if (ac) {
+               ac->ac_sb = sb;
+               ac->ac_inode = NULL;
+               ac->ac_b_ex.fe_group = group;
+               ac->ac_b_ex.fe_start = bit;
+               ac->ac_b_ex.fe_len = pa->pa_len;
+               ac->ac_b_ex.fe_logical = 0;
+               ext4_mb_store_history(ac);
+               kmem_cache_free(ext4_ac_cachep, ac);
+       }
 
        return 0;
 }
@@ -4231,7 +4261,7 @@ static int ext4_mb_discard_preallocations(struct super_block *sb, int needed)
 ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
                                 struct ext4_allocation_request *ar, int *errp)
 {
-       struct ext4_allocation_context ac;
+       struct ext4_allocation_context *ac = NULL;
        struct ext4_sb_info *sbi;
        struct super_block *sb;
        ext4_fsblk_t block = 0;
@@ -4257,53 +4287,60 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
        }
        inquota = ar->len;
 
+       ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
+       if (!ac) {
+               *errp = -ENOMEM;
+               return 0;
+       }
+
        ext4_mb_poll_new_transaction(sb, handle);
 
-       *errp = ext4_mb_initialize_context(&ac, ar);
+       *errp = ext4_mb_initialize_context(ac, ar);
        if (*errp) {
                ar->len = 0;
                goto out;
        }
 
-       ac.ac_op = EXT4_MB_HISTORY_PREALLOC;
-       if (!ext4_mb_use_preallocated(&ac)) {
+       ac->ac_op = EXT4_MB_HISTORY_PREALLOC;
+       if (!ext4_mb_use_preallocated(ac)) {
 
-               ac.ac_op = EXT4_MB_HISTORY_ALLOC;
-               ext4_mb_normalize_request(&ac, ar);
+               ac->ac_op = EXT4_MB_HISTORY_ALLOC;
+               ext4_mb_normalize_request(ac, ar);
 
 repeat:
                /* allocate space in core */
-               ext4_mb_regular_allocator(&ac);
+               ext4_mb_regular_allocator(ac);
 
                /* as we've just preallocated more space than
                 * user requested orinally, we store allocated
                 * space in a special descriptor */
-               if (ac.ac_status == AC_STATUS_FOUND &&
-                               ac.ac_o_ex.fe_len < ac.ac_b_ex.fe_len)
-                       ext4_mb_new_preallocation(&ac);
+               if (ac->ac_status == AC_STATUS_FOUND &&
+                               ac->ac_o_ex.fe_len < ac->ac_b_ex.fe_len)
+                       ext4_mb_new_preallocation(ac);
        }
 
-       if (likely(ac.ac_status == AC_STATUS_FOUND)) {
-               ext4_mb_mark_diskspace_used(&ac, handle);
+       if (likely(ac->ac_status == AC_STATUS_FOUND)) {
+               ext4_mb_mark_diskspace_used(ac, handle);
                *errp = 0;
-               block = ext4_grp_offs_to_block(sb, &ac.ac_b_ex);
-               ar->len = ac.ac_b_ex.fe_len;
+               block = ext4_grp_offs_to_block(sb, &ac->ac_b_ex);
+               ar->len = ac->ac_b_ex.fe_len;
        } else {
-               freed  = ext4_mb_discard_preallocations(sb, ac.ac_o_ex.fe_len);
+               freed  = ext4_mb_discard_preallocations(sb, ac->ac_o_ex.fe_len);
                if (freed)
                        goto repeat;
                *errp = -ENOSPC;
-               ac.ac_b_ex.fe_len = 0;
+               ac->ac_b_ex.fe_len = 0;
                ar->len = 0;
-               ext4_mb_show_ac(&ac);
+               ext4_mb_show_ac(ac);
        }
 
-       ext4_mb_release_context(&ac);
+       ext4_mb_release_context(ac);
 
 out:
        if (ar->len < inquota)
                DQUOT_FREE_BLOCK(ar->inode, inquota - ar->len);
 
+       kmem_cache_free(ext4_ac_cachep, ac);
        return block;
 }
 static void ext4_mb_poll_new_transaction(struct super_block *sb,
@@ -4405,9 +4442,9 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
                        unsigned long block, unsigned long count,
                        int metadata, unsigned long *freed)
 {
-       struct buffer_head *bitmap_bh = 0;
+       struct buffer_head *bitmap_bh = NULL;
        struct super_block *sb = inode->i_sb;
-       struct ext4_allocation_context ac;
+       struct ext4_allocation_context *ac = NULL;
        struct ext4_group_desc *gdp;
        struct ext4_super_block *es;
        unsigned long overflow;
@@ -4436,9 +4473,12 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
 
        ext4_debug("freeing block %lu\n", block);
 
-       ac.ac_op = EXT4_MB_HISTORY_FREE;
-       ac.ac_inode = inode;
-       ac.ac_sb = sb;
+       ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
+       if (ac) {
+               ac->ac_op = EXT4_MB_HISTORY_FREE;
+               ac->ac_inode = inode;
+               ac->ac_sb = sb;
+       }
 
 do_more:
        overflow = 0;
@@ -4504,10 +4544,12 @@ do_more:
        BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
        err = ext4_journal_dirty_metadata(handle, bitmap_bh);
 
-       ac.ac_b_ex.fe_group = block_group;
-       ac.ac_b_ex.fe_start = bit;
-       ac.ac_b_ex.fe_len = count;
-       ext4_mb_store_history(&ac);
+       if (ac) {
+               ac->ac_b_ex.fe_group = block_group;
+               ac->ac_b_ex.fe_start = bit;
+               ac->ac_b_ex.fe_len = count;
+               ext4_mb_store_history(ac);
+       }
 
        if (metadata) {
                /* blocks being freed are metadata. these blocks shouldn't
@@ -4548,5 +4590,7 @@ do_more:
 error_return:
        brelse(bitmap_bh);
        ext4_std_error(sb, err);
+       if (ac)
+               kmem_cache_free(ext4_ac_cachep, ac);
        return;
 }
index 3ebc233..8c6c685 100644 (file)
@@ -61,10 +61,9 @@ static int finish_range(handle_t *handle, struct inode *inode,
                retval = ext4_journal_restart(handle, needed);
                if (retval)
                        goto err_out;
-       }
-       if (needed) {
+       } else if (needed) {
                retval = ext4_journal_extend(handle, needed);
-               if (retval != 0) {
+               if (retval) {
                        /*
                         * IF not able to extend the journal restart the journal
                         */
@@ -220,6 +219,26 @@ static int update_tind_extent_range(handle_t *handle, struct inode *inode,
 
 }
 
+static int extend_credit_for_blkdel(handle_t *handle, struct inode *inode)
+{
+       int retval = 0, needed;
+
+       if (handle->h_buffer_credits > EXT4_RESERVE_TRANS_BLOCKS)
+               return 0;
+       /*
+        * We are freeing a blocks. During this we touch
+        * superblock, group descriptor and block bitmap.
+        * So allocate a credit of 3. We may update
+        * quota (user and group).
+        */
+       needed = 3 + 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb);
+
+       if (ext4_journal_extend(handle, needed) != 0)
+               retval = ext4_journal_restart(handle, needed);
+
+       return retval;
+}
+
 static int free_dind_blocks(handle_t *handle,
                                struct inode *inode, __le32 i_data)
 {
@@ -234,11 +253,14 @@ static int free_dind_blocks(handle_t *handle,
 
        tmp_idata = (__le32 *)bh->b_data;
        for (i = 0; i < max_entries; i++) {
-               if (tmp_idata[i])
+               if (tmp_idata[i]) {
+                       extend_credit_for_blkdel(handle, inode);
                        ext4_free_blocks(handle, inode,
                                        le32_to_cpu(tmp_idata[i]), 1, 1);
+               }
        }
        put_bh(bh);
+       extend_credit_for_blkdel(handle, inode);
        ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1, 1);
        return 0;
 }
@@ -267,29 +289,32 @@ static int free_tind_blocks(handle_t *handle,
                }
        }
        put_bh(bh);
+       extend_credit_for_blkdel(handle, inode);
        ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1, 1);
        return 0;
 }
 
-static int free_ind_block(handle_t *handle, struct inode *inode)
+static int free_ind_block(handle_t *handle, struct inode *inode, __le32 *i_data)
 {
        int retval;
-       struct ext4_inode_info *ei = EXT4_I(inode);
 
-       if (ei->i_data[EXT4_IND_BLOCK])
+       /* ei->i_data[EXT4_IND_BLOCK] */
+       if (i_data[0]) {
+               extend_credit_for_blkdel(handle, inode);
                ext4_free_blocks(handle, inode,
-                               le32_to_cpu(ei->i_data[EXT4_IND_BLOCK]), 1, 1);
+                               le32_to_cpu(i_data[0]), 1, 1);
+       }
 
-       if (ei->i_data[EXT4_DIND_BLOCK]) {
-               retval = free_dind_blocks(handle, inode,
-                                               ei->i_data[EXT4_DIND_BLOCK]);
+       /* ei->i_data[EXT4_DIND_BLOCK] */
+       if (i_data[1]) {
+               retval = free_dind_blocks(handle, inode, i_data[1]);
                if (retval)
                        return retval;
        }
 
-       if (ei->i_data[EXT4_TIND_BLOCK]) {
-               retval = free_tind_blocks(handle, inode,
-                                               ei->i_data[EXT4_TIND_BLOCK]);
+       /* ei->i_data[EXT4_TIND_BLOCK] */
+       if (i_data[2]) {
+               retval = free_tind_blocks(handle, inode, i_data[2]);
                if (retval)
                        return retval;
        }
@@ -297,15 +322,13 @@ static int free_ind_block(handle_t *handle, struct inode *inode)
 }
 
 static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,
-                               struct inode *tmp_inode, int retval)
+                               struct inode *tmp_inode)
 {
+       int retval;
+       __le32  i_data[3];
        struct ext4_inode_info *ei = EXT4_I(inode);
        struct ext4_inode_info *tmp_ei = EXT4_I(tmp_inode);
 
-       retval = free_ind_block(handle, inode);
-       if (retval)
-               goto err_out;
-
        /*
         * One credit accounted for writing the
         * i_data field of the original inode
@@ -317,6 +340,11 @@ static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,
                        goto err_out;
        }
 
+       i_data[0] = ei->i_data[EXT4_IND_BLOCK];
+       i_data[1] = ei->i_data[EXT4_DIND_BLOCK];
+       i_data[2] = ei->i_data[EXT4_TIND_BLOCK];
+
+       down_write(&EXT4_I(inode)->i_data_sem);
        /*
         * We have the extent map build with the tmp inode.
         * Now copy the i_data across
@@ -336,8 +364,15 @@ static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,
        spin_lock(&inode->i_lock);
        inode->i_blocks += tmp_inode->i_blocks;
        spin_unlock(&inode->i_lock);
+       up_write(&EXT4_I(inode)->i_data_sem);
 
+       /*
+        * We mark the inode dirty after, because we decrement the
+        * i_blocks when freeing the indirect meta-data blocks
+        */
+       retval = free_ind_block(handle, inode, i_data);
        ext4_mark_inode_dirty(handle, inode);
+
 err_out:
        return retval;
 }
@@ -365,6 +400,7 @@ static int free_ext_idx(handle_t *handle, struct inode *inode,
                }
        }
        put_bh(bh);
+       extend_credit_for_blkdel(handle, inode);
        ext4_free_blocks(handle, inode, block, 1, 1);
        return retval;
 }
@@ -414,7 +450,12 @@ int ext4_ext_migrate(struct inode *inode, struct file *filp,
        if ((EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))
                return -EINVAL;
 
-       down_write(&EXT4_I(inode)->i_data_sem);
+       if (S_ISLNK(inode->i_mode) && inode->i_blocks == 0)
+               /*
+                * don't migrate fast symlink
+                */
+               return retval;
+
        handle = ext4_journal_start(inode,
                                        EXT4_DATA_TRANS_BLOCKS(inode->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
@@ -448,13 +489,6 @@ int ext4_ext_migrate(struct inode *inode, struct file *filp,
        ext4_orphan_add(handle, tmp_inode);
        ext4_journal_stop(handle);
 
-       ei = EXT4_I(inode);
-       i_data = ei->i_data;
-       memset(&lb, 0, sizeof(lb));
-
-       /* 32 bit block address 4 bytes */
-       max_entries = inode->i_sb->s_blocksize >> 2;
-
        /*
         * start with one credit accounted for
         * superblock modification.
@@ -463,7 +497,20 @@ int ext4_ext_migrate(struct inode *inode, struct file *filp,
         * trascation that created the inode. Later as and
         * when we add extents we extent the journal
         */
+       /*
+        * inode_mutex prevent write and truncate on the file. Read still goes
+        * through. We take i_data_sem in ext4_ext_swap_inode_data before we
+        * switch the inode format to prevent read.
+        */
+       mutex_lock(&(inode->i_mutex));
        handle = ext4_journal_start(inode, 1);
+
+       ei = EXT4_I(inode);
+       i_data = ei->i_data;
+       memset(&lb, 0, sizeof(lb));
+
+       /* 32 bit block address 4 bytes */
+       max_entries = inode->i_sb->s_blocksize >> 2;
        for (i = 0; i < EXT4_NDIR_BLOCKS; i++, blk_count++) {
                if (i_data[i]) {
                        retval = update_extent_range(handle, tmp_inode,
@@ -501,19 +548,6 @@ int ext4_ext_migrate(struct inode *inode, struct file *filp,
         */
        retval = finish_range(handle, tmp_inode, &lb);
 err_out:
-       /*
-        * We are either freeing extent information or indirect
-        * blocks. During this we touch superblock, group descriptor
-        * and block bitmap. Later we mark the tmp_inode dirty
-        * via ext4_ext_tree_init. So allocate a credit of 4
-        * We may update quota (user and group).
-        *
-        * FIXME!! we may be touching bitmaps in different block groups.
-        */
-       if (ext4_journal_extend(handle,
-                       4 + 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb)) != 0)
-               ext4_journal_restart(handle,
-                               4 + 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb));
        if (retval)
                /*
                 * Failure case delete the extent information with the
@@ -522,7 +556,11 @@ err_out:
                free_ext_block(handle, tmp_inode);
        else
                retval = ext4_ext_swap_inode_data(handle, inode,
-                                                       tmp_inode, retval);
+                                                       tmp_inode);
+
+       /* We mark the tmp_inode dirty via ext4_ext_tree_init. */
+       if (ext4_journal_extend(handle, 1) != 0)
+               ext4_journal_restart(handle, 1);
 
        /*
         * Mark the tmp_inode as of size zero
@@ -550,8 +588,7 @@ err_out:
        tmp_inode->i_nlink = 0;
 
        ext4_journal_stop(handle);
-
-       up_write(&EXT4_I(inode)->i_data_sem);
+       mutex_unlock(&(inode->i_mutex));
 
        if (tmp_inode)
                iput(tmp_inode);
index d153bb5..a9347fb 100644 (file)
@@ -2223,6 +2223,7 @@ retry:
                inode->i_op = &ext4_fast_symlink_inode_operations;
                memcpy((char*)&EXT4_I(inode)->i_data,symname,l);
                inode->i_size = l-1;
+               EXT4_I(inode)->i_flags &= ~EXT4_EXTENTS_FL;
        }
        EXT4_I(inode)->i_disksize = inode->i_size;
        err = ext4_add_nondir(handle, dentry, inode);
index 93beb86..0072da7 100644 (file)
@@ -1919,6 +1919,17 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent)
                printk(KERN_WARNING
                       "EXT4-fs warning: feature flags set on rev 0 fs, "
                       "running e2fsck is recommended\n");
+
+       /*
+        * Since ext4 is still considered development code, we require
+        * that the TEST_FILESYS flag in s->flags be set.
+        */
+       if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)) {
+               printk(KERN_WARNING "EXT4-fs: %s: not marked "
+                      "OK to use with test code.\n", sb->s_id);
+               goto failed_mount;
+       }
+
        /*
         * Check feature flags regardless of the revision level, since we
         * previously didn't change the revision level when setting the flags,
index d0549cb..5222345 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/pagemap.h>
 #include <linux/statfs.h>
 #include <linux/seq_file.h>
+#include <linux/mount.h>
 #include "hostfs.h"
 #include "init.h"
 #include "kern.h"
index 683002f..f32fbde 100644 (file)
 
 /**
  * vfs_ioctl - call filesystem specific ioctl methods
- * @filp: [in]     open file to invoke ioctl method on
- * @cmd:  [in]     ioctl command to execute
- * @arg:  [in/out] command-specific argument for ioctl
+ * @filp:      open file to invoke ioctl method on
+ * @cmd:       ioctl command to execute
+ * @arg:       command-specific argument for ioctl
  *
  * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise
- * invokes filesystem specific ->ioctl method.  If neither method exists,
+ * invokes filesystem specific ->ioctl method.  If neither method exists,
  * returns -ENOTTY.
  *
  * Returns 0 on success, -errno on error.
index 8e08efc..a38c718 100644 (file)
@@ -104,7 +104,8 @@ static int journal_write_commit_record(journal_t *journal,
 {
        struct journal_head *descriptor;
        struct buffer_head *bh;
-       int i, ret;
+       journal_header_t *header;
+       int ret;
        int barrier_done = 0;
 
        if (is_journal_aborted(journal))
@@ -116,13 +117,10 @@ static int journal_write_commit_record(journal_t *journal,
 
        bh = jh2bh(descriptor);
 
-       /* AKPM: buglet - add `i' to tmp! */
-       for (i = 0; i < bh->b_size; i += 512) {
-               journal_header_t *tmp = (journal_header_t*)bh->b_data;
-               tmp->h_magic = cpu_to_be32(JFS_MAGIC_NUMBER);
-               tmp->h_blocktype = cpu_to_be32(JFS_COMMIT_BLOCK);
-               tmp->h_sequence = cpu_to_be32(commit_transaction->t_tid);
-       }
+       header = (journal_header_t *)(bh->b_data);
+       header->h_magic = cpu_to_be32(JFS_MAGIC_NUMBER);
+       header->h_blocktype = cpu_to_be32(JFS_COMMIT_BLOCK);
+       header->h_sequence = cpu_to_be32(commit_transaction->t_tid);
 
        JBUFFER_TRACE(descriptor, "write commit block");
        set_buffer_dirty(bh);
index 4f302d2..a817308 100644 (file)
@@ -136,18 +136,20 @@ static int journal_submit_commit_record(journal_t *journal,
 
        JBUFFER_TRACE(descriptor, "submit commit block");
        lock_buffer(bh);
-
+       get_bh(bh);
        set_buffer_dirty(bh);
        set_buffer_uptodate(bh);
        bh->b_end_io = journal_end_buffer_io_sync;
 
        if (journal->j_flags & JBD2_BARRIER &&
-               !JBD2_HAS_COMPAT_FEATURE(journal,
+               !JBD2_HAS_INCOMPAT_FEATURE(journal,
                                         JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
                set_buffer_ordered(bh);
                barrier_done = 1;
        }
        ret = submit_bh(WRITE, bh);
+       if (barrier_done)
+               clear_buffer_ordered(bh);
 
        /* is it possible for another commit to fail at roughly
         * the same time as this one?  If so, we don't want to
@@ -166,7 +168,6 @@ static int journal_submit_commit_record(journal_t *journal,
                spin_unlock(&journal->j_state_lock);
 
                /* And try again, without the barrier */
-               clear_buffer_ordered(bh);
                set_buffer_uptodate(bh);
                set_buffer_dirty(bh);
                ret = submit_bh(WRITE, bh);
@@ -872,7 +873,8 @@ wait_for_iobuf:
                if (err)
                        __jbd2_journal_abort_hard(journal);
        }
-       err = journal_wait_on_commit_record(cbh);
+       if (!err && !is_journal_aborted(journal))
+               err = journal_wait_on_commit_record(cbh);
 
        if (err)
                jbd2_journal_abort(journal, err);
index d36356f..1464113 100644 (file)
@@ -641,7 +641,7 @@ static int do_one_pass(journal_t *journal,
                                if (chksum_err) {
                                        info->end_transaction = next_commit_ID;
 
-                                       if (!JBD2_HAS_COMPAT_FEATURE(journal,
+                                       if (!JBD2_HAS_INCOMPAT_FEATURE(journal,
                                           JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)){
                                                printk(KERN_ERR
                                                       "JBD: Transaction %u "
index d5c9d14..c7db022 100644 (file)
@@ -39,6 +39,7 @@ typedef struct {
 typedef struct {
        unsigned long pgprot;
 } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)     ((x).pte)
 #define pmd_val(x)     ((&x)->pmd[0])
index a834924..d6a3eaf 100644 (file)
@@ -31,6 +31,7 @@ typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long pmd[16]; } pmd_t;
 typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)     ((x).pte)
 #define pmd_val(x)     ((&x)->pmd[0])
index 6af480c..1e82ebb 100644 (file)
@@ -31,6 +31,7 @@ typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long pmd[16]; } pmd_t;
 typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)     ((x).pte)
 #define pmd_val(x)     ((&x)->pmd[0])
diff --git a/include/asm-s390/a.out.h b/include/asm-s390/a.out.h
deleted file mode 100644 (file)
index 8d6bd9c..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- *  include/asm-s390/a.out.h
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *
- *  Derived from "include/asm-i386/a.out.h"
- *    Copyright (C) 1992, Linus Torvalds
- *
- * I don't think we'll ever need a.out ...
- */
-
-#ifndef __S390_A_OUT_H__
-#define __S390_A_OUT_H__
-
-struct exec
-{
-  unsigned long a_info;                /* Use macros N_MAGIC, etc for access */
-  unsigned a_text;             /* length of text, in bytes */
-  unsigned a_data;             /* length of data, in bytes */
-  unsigned a_bss;              /* length of uninitialized data area for file, in bytes */
-  unsigned a_syms;             /* length of symbol table data in file, in bytes */
-  unsigned a_entry;            /* start address */
-  unsigned a_trsize;           /* length of relocation info for text, in bytes */
-  unsigned a_drsize;           /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a)    ((a).a_trsize)
-#define N_DRSIZE(a)    ((a).a_drsize)
-#define N_SYMSIZE(a)   ((a).a_syms)
-
-#endif /* __A_OUT_GNU_H__ */
index 882db05..ab83c84 100644 (file)
@@ -472,7 +472,7 @@ static inline unsigned long __ffz_word_loop(const unsigned long *addr,
                "       brct    %1,0b\n"
                "1:\n"
 #endif
-               : "+a" (bytes), "+d" (size)
+               : "+&a" (bytes), "+&d" (size)
                : "d" (-1UL), "a" (addr), "m" (*(addrtype *) addr)
                : "cc" );
        return bytes;
@@ -507,7 +507,7 @@ static inline unsigned long __ffs_word_loop(const unsigned long *addr,
                "       brct    %1,0b\n"
                "1:\n"
 #endif
-               : "+a" (bytes), "+a" (size)
+               : "+&a" (bytes), "+&a" (size)
                : "d" (0UL), "a" (addr), "m" (*(addrtype *) addr)
                : "cc" );
        return bytes;
index b73a424..b3ac262 100644 (file)
@@ -115,6 +115,7 @@ typedef s390_regs elf_gregset_t;
 
 #include <linux/sched.h>       /* for task_struct */
 #include <asm/system.h>                /* for save_access_regs */
+#include <asm/mmu_context.h>
 
 /*
  * This is used to ensure we don't load something for the wrong architecture.
@@ -137,14 +138,7 @@ typedef s390_regs elf_gregset_t;
    use of this is to invoke "./ld.so someprog" to test out a new version of
    the loader.  We need to make sure that it is out of the way of the program
    that it will "exec", and that there is sufficient room for the brk.  */
-
-#ifndef __s390x__
-#define ELF_ET_DYN_BASE         ((TASK_SIZE & 0x80000000) \
-                                ? TASK_SIZE / 3 * 2 \
-                                : 2 * TASK_SIZE / 3)
-#else /* __s390x__ */
-#define ELF_ET_DYN_BASE         (TASK_SIZE / 3 * 2)
-#endif /* __s390x__ */
+#define ELF_ET_DYN_BASE                (STACK_TOP / 3 * 2)
 
 /* Wow, the "main" arch needs arch dependent functions too.. :) */
 
@@ -214,4 +208,16 @@ do {                                                       \
 } while (0)
 #endif /* __s390x__ */
 
+/*
+ * An executable for which elf_read_implies_exec() returns TRUE will
+ * have the READ_IMPLIES_EXEC personality flag set automatically.
+ */
+#define elf_read_implies_exec(ex, executable_stack)    \
+({                                                     \
+       if (current->mm->context.noexec &&              \
+           executable_stack != EXSTACK_DISABLE_X)      \
+               disable_noexec(current->mm, current);   \
+       current->mm->context.noexec == 0;               \
+})
+
 #endif
index ccd36d2..1698e29 100644 (file)
@@ -1,7 +1,12 @@
 #ifndef __MMU_H
 #define __MMU_H
 
-/* Default "unsigned long" context */
-typedef unsigned long mm_context_t;
+typedef struct {
+       struct list_head crst_list;
+       struct list_head pgtable_list;
+       unsigned long asce_bits;
+       unsigned long asce_limit;
+       int noexec;
+} mm_context_t;
 
 #endif
index a77d4ba..b5a34c6 100644 (file)
 #define __S390_MMU_CONTEXT_H
 
 #include <asm/pgalloc.h>
+#include <asm/uaccess.h>
 #include <asm-generic/mm_hooks.h>
 
 static inline int init_new_context(struct task_struct *tsk,
                                   struct mm_struct *mm)
 {
-       mm->context = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
+       mm->context.asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
 #ifdef CONFIG_64BIT
-       mm->context |= _ASCE_TYPE_REGION3;
+       mm->context.asce_bits |= _ASCE_TYPE_REGION3;
 #endif
+       mm->context.noexec = s390_noexec;
+       mm->context.asce_limit = STACK_TOP_MAX;
+       crst_table_init((unsigned long *) mm->pgd, pgd_entry_type(mm));
        return 0;
 }
 
@@ -32,24 +36,25 @@ static inline int init_new_context(struct task_struct *tsk,
 
 static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
 {
-       S390_lowcore.user_asce = mm->context | __pa(mm->pgd);
+       pgd_t *pgd = mm->pgd;
+
+       S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd);
        if (switch_amode) {
                /* Load primary space page table origin. */
-               pgd_t *shadow_pgd = get_shadow_table(mm->pgd) ? : mm->pgd;
-               S390_lowcore.user_exec_asce = mm->context | __pa(shadow_pgd);
+               pgd = mm->context.noexec ? get_shadow_table(pgd) : pgd;
+               S390_lowcore.user_exec_asce = mm->context.asce_bits | __pa(pgd);
                asm volatile(LCTL_OPCODE" 1,1,%0\n"
                             : : "m" (S390_lowcore.user_exec_asce) );
        } else
                /* Load home space page table origin. */
                asm volatile(LCTL_OPCODE" 13,13,%0"
                             : : "m" (S390_lowcore.user_asce) );
+       set_fs(current->thread.mm_segment);
 }
 
 static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
                             struct task_struct *tsk)
 {
-       if (unlikely(prev == next))
-               return;
        cpu_set(smp_processor_id(), next->cpu_vm_mask);
        update_mm(next, tsk);
 }
@@ -61,7 +66,6 @@ static inline void activate_mm(struct mm_struct *prev,
                                struct mm_struct *next)
 {
         switch_mm(prev, next, current);
-       set_fs(current->thread.mm_segment);
 }
 
 #endif /* __S390_MMU_CONTEXT_H */
index 7f29a98..fe7f92b 100644 (file)
@@ -74,43 +74,17 @@ static inline void copy_page(void *to, void *from)
 
 typedef struct { unsigned long pgprot; } pgprot_t;
 typedef struct { unsigned long pte; } pte_t;
-
-#define pte_val(x)      ((x).pte)
-#define pgprot_val(x)   ((x).pgprot)
-
-#ifndef __s390x__
-
 typedef struct { unsigned long pmd; } pmd_t;
 typedef struct { unsigned long pud; } pud_t;
-typedef struct {
-        unsigned long pgd0;
-        unsigned long pgd1;
-        unsigned long pgd2;
-        unsigned long pgd3;
-        } pgd_t;
-
-#define pmd_val(x)      ((x).pmd)
-#define pud_val(x)     ((x).pud)
-#define pgd_val(x)      ((x).pgd0)
-
-#else /* __s390x__ */
-
-typedef struct { 
-        unsigned long pmd0;
-        unsigned long pmd1; 
-        } pmd_t;
-typedef struct { unsigned long pud; } pud_t;
 typedef struct { unsigned long pgd; } pgd_t;
+typedef pte_t *pgtable_t;
 
-#define pmd_val(x)      ((x).pmd0)
-#define pmd_val1(x)     ((x).pmd1)
+#define pgprot_val(x)  ((x).pgprot)
+#define pte_val(x)     ((x).pte)
+#define pmd_val(x)     ((x).pmd)
 #define pud_val(x)     ((x).pud)
 #define pgd_val(x)      ((x).pgd)
 
-#endif /* __s390x__ */
-
-typedef struct page *pgtable_t;
-
 #define __pte(x)        ((pte_t) { (x) } )
 #define __pmd(x)        ((pmd_t) { (x) } )
 #define __pgd(x)        ((pgd_t) { (x) } )
@@ -167,7 +141,7 @@ static inline int pfn_valid(unsigned long pfn)
 #define page_to_phys(page)     (page_to_pfn(page) << PAGE_SHIFT)
 #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
 
-#define VM_DATA_DEFAULT_FLAGS  (VM_READ | VM_WRITE | VM_EXEC | \
+#define VM_DATA_DEFAULT_FLAGS  (VM_READ | VM_WRITE | \
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #include <asm-generic/memory_model.h>
index 900d448..f5b2bf3 100644 (file)
 #define check_pgt_cache()      do {} while (0)
 
 unsigned long *crst_table_alloc(struct mm_struct *, int);
-void crst_table_free(unsigned long *);
+void crst_table_free(struct mm_struct *, unsigned long *);
 
-unsigned long *page_table_alloc(int);
-void page_table_free(unsigned long *);
+unsigned long *page_table_alloc(struct mm_struct *);
+void page_table_free(struct mm_struct *, unsigned long *);
+void disable_noexec(struct mm_struct *, struct task_struct *);
 
 static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
 {
@@ -72,23 +73,49 @@ static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 
 static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 {
-       return _REGION3_ENTRY_EMPTY;
+       if (mm->context.asce_limit <= (1UL << 31))
+               return _SEGMENT_ENTRY_EMPTY;
+       if (mm->context.asce_limit <= (1UL << 42))
+               return _REGION3_ENTRY_EMPTY;
+       return _REGION2_ENTRY_EMPTY;
 }
 
-#define pud_alloc_one(mm,address)              ({ BUG(); ((pud_t *)2); })
-#define pud_free(mm, x)                                do { } while (0)
+int crst_table_upgrade(struct mm_struct *, unsigned long limit);
+void crst_table_downgrade(struct mm_struct *, unsigned long limit);
+
+static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+       unsigned long *table = crst_table_alloc(mm, mm->context.noexec);
+       if (table)
+               crst_table_init(table, _REGION3_ENTRY_EMPTY);
+       return (pud_t *) table;
+}
+#define pud_free(mm, pud) crst_table_free(mm, (unsigned long *) pud)
 
 static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr)
 {
-       unsigned long *crst = crst_table_alloc(mm, s390_noexec);
-       if (crst)
-               crst_table_init(crst, _SEGMENT_ENTRY_EMPTY);
-       return (pmd_t *) crst;
+       unsigned long *table = crst_table_alloc(mm, mm->context.noexec);
+       if (table)
+               crst_table_init(table, _SEGMENT_ENTRY_EMPTY);
+       return (pmd_t *) table;
 }
-#define pmd_free(mm, pmd) crst_table_free((unsigned long *)pmd)
+#define pmd_free(mm, pmd) crst_table_free(mm, (unsigned long *) pmd)
 
-#define pgd_populate(mm, pgd, pud)             BUG()
-#define pgd_populate_kernel(mm, pgd, pud)      BUG()
+static inline void pgd_populate_kernel(struct mm_struct *mm,
+                                      pgd_t *pgd, pud_t *pud)
+{
+       pgd_val(*pgd) = _REGION2_ENTRY | __pa(pud);
+}
+
+static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud)
+{
+       pgd_populate_kernel(mm, pgd, pud);
+       if (mm->context.noexec) {
+               pgd = get_shadow_table(pgd);
+               pud = get_shadow_table(pud);
+               pgd_populate_kernel(mm, pgd, pud);
+       }
+}
 
 static inline void pud_populate_kernel(struct mm_struct *mm,
                                       pud_t *pud, pmd_t *pmd)
@@ -98,63 +125,50 @@ static inline void pud_populate_kernel(struct mm_struct *mm,
 
 static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
 {
-       pud_t *shadow_pud = get_shadow_table(pud);
-       pmd_t *shadow_pmd = get_shadow_table(pmd);
-
-       if (shadow_pud && shadow_pmd)
-               pud_populate_kernel(mm, shadow_pud, shadow_pmd);
        pud_populate_kernel(mm, pud, pmd);
+       if (mm->context.noexec) {
+               pud = get_shadow_table(pud);
+               pmd = get_shadow_table(pmd);
+               pud_populate_kernel(mm, pud, pmd);
+       }
 }
 
 #endif /* __s390x__ */
 
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
-       unsigned long *crst = crst_table_alloc(mm, s390_noexec);
-       if (crst)
-               crst_table_init(crst, pgd_entry_type(mm));
-       return (pgd_t *) crst;
+       INIT_LIST_HEAD(&mm->context.crst_list);
+       INIT_LIST_HEAD(&mm->context.pgtable_list);
+       return (pgd_t *) crst_table_alloc(mm, s390_noexec);
 }
-#define pgd_free(mm, pgd) crst_table_free((unsigned long *) pgd)
+#define pgd_free(mm, pgd) crst_table_free(mm, (unsigned long *) pgd)
 
-static inline void 
-pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte)
+static inline void pmd_populate_kernel(struct mm_struct *mm,
+                                      pmd_t *pmd, pte_t *pte)
 {
-#ifndef __s390x__
-       pmd_val(pmd[0]) = _SEGMENT_ENTRY + __pa(pte);
-       pmd_val(pmd[1]) = _SEGMENT_ENTRY + __pa(pte+256);
-       pmd_val(pmd[2]) = _SEGMENT_ENTRY + __pa(pte+512);
-       pmd_val(pmd[3]) = _SEGMENT_ENTRY + __pa(pte+768);
-#else /* __s390x__ */
        pmd_val(*pmd) = _SEGMENT_ENTRY + __pa(pte);
-       pmd_val1(*pmd) = _SEGMENT_ENTRY + __pa(pte+256);
-#endif /* __s390x__ */
 }
 
-static inline void
-pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t page)
+static inline void pmd_populate(struct mm_struct *mm,
+                               pmd_t *pmd, pgtable_t pte)
 {
-       pte_t *pte = (pte_t *)page_to_phys(page);
-       pmd_t *shadow_pmd = get_shadow_table(pmd);
-       pte_t *shadow_pte = get_shadow_pte(pte);
-
        pmd_populate_kernel(mm, pmd, pte);
-       if (shadow_pmd && shadow_pte)
-               pmd_populate_kernel(mm, shadow_pmd, shadow_pte);
+       if (mm->context.noexec) {
+               pmd = get_shadow_table(pmd);
+               pmd_populate_kernel(mm, pmd, pte + PTRS_PER_PTE);
+       }
 }
-#define pmd_pgtable(pmd) pmd_page(pmd)
+
+#define pmd_pgtable(pmd) \
+       (pgtable_t)(pmd_val(pmd) & -sizeof(pte_t)*PTRS_PER_PTE)
 
 /*
  * page table entry allocation/free routines.
  */
-#define pte_alloc_one_kernel(mm, vmaddr) \
-       ((pte_t *) page_table_alloc(s390_noexec))
-#define pte_alloc_one(mm, vmaddr) \
-       virt_to_page(page_table_alloc(s390_noexec))
-
-#define pte_free_kernel(mm, pte) \
-       page_table_free((unsigned long *) pte)
-#define pte_free(mm, pte) \
-       page_table_free((unsigned long *) page_to_phys((struct page *) pte))
+#define pte_alloc_one_kernel(mm, vmaddr) ((pte_t *) page_table_alloc(mm))
+#define pte_alloc_one(mm, vmaddr) ((pte_t *) page_table_alloc(mm))
+
+#define pte_free_kernel(mm, pte) page_table_free(mm, (unsigned long *) pte)
+#define pte_free(mm, pte) page_table_free(mm, (unsigned long *) pte)
 
 #endif /* _S390_PGALLOC_H */
index 3f52075..65154dc 100644 (file)
@@ -57,21 +57,21 @@ extern char empty_zero_page[PAGE_SIZE];
  * PGDIR_SHIFT determines what a third-level page table entry can map
  */
 #ifndef __s390x__
-# define PMD_SHIFT     22
-# define PUD_SHIFT     22
-# define PGDIR_SHIFT   22
+# define PMD_SHIFT     20
+# define PUD_SHIFT     20
+# define PGDIR_SHIFT   20
 #else /* __s390x__ */
-# define PMD_SHIFT     21
+# define PMD_SHIFT     20
 # define PUD_SHIFT     31
-# define PGDIR_SHIFT   31
+# define PGDIR_SHIFT   42
 #endif /* __s390x__ */
 
 #define PMD_SIZE        (1UL << PMD_SHIFT)
 #define PMD_MASK        (~(PMD_SIZE-1))
 #define PUD_SIZE       (1UL << PUD_SHIFT)
 #define PUD_MASK       (~(PUD_SIZE-1))
-#define PGDIR_SIZE      (1UL << PGDIR_SHIFT)
-#define PGDIR_MASK      (~(PGDIR_SIZE-1))
+#define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK     (~(PGDIR_SIZE-1))
 
 /*
  * entries per page directory level: the S390 is two-level, so
@@ -79,17 +79,15 @@ extern char empty_zero_page[PAGE_SIZE];
  * for S390 segment-table entries are combined to one PGD
  * that leads to 1024 pte per pgd
  */
+#define PTRS_PER_PTE   256
 #ifndef __s390x__
-# define PTRS_PER_PTE    1024
-# define PTRS_PER_PMD    1
-# define PTRS_PER_PUD  1
-# define PTRS_PER_PGD    512
+#define PTRS_PER_PMD   1
+#define PTRS_PER_PUD   1
 #else /* __s390x__ */
-# define PTRS_PER_PTE    512
-# define PTRS_PER_PMD    1024
-# define PTRS_PER_PUD  1
-# define PTRS_PER_PGD    2048
+#define PTRS_PER_PMD   2048
+#define PTRS_PER_PUD   2048
 #endif /* __s390x__ */
+#define PTRS_PER_PGD   2048
 
 #define FIRST_USER_ADDRESS  0
 
@@ -127,8 +125,9 @@ extern char empty_zero_page[PAGE_SIZE];
  * mapping. This needs to be calculated at compile time since the size of the
  * VMEM_MAP is static but the size of struct page can change.
  */
-#define VMEM_MAX_PHYS  min(VMALLOC_START, ((VMEM_MAP_END - VMALLOC_END) / \
-                         sizeof(struct page) * PAGE_SIZE) & ~((16 << 20) - 1))
+#define VMEM_MAX_PAGES ((VMEM_MAP_END - VMALLOC_END) / sizeof(struct page))
+#define VMEM_MAX_PFN   min(VMALLOC_START >> PAGE_SHIFT, VMEM_MAX_PAGES)
+#define VMEM_MAX_PHYS  ((VMEM_MAX_PFN << PAGE_SHIFT) & ~((16 << 20) - 1))
 #define VMEM_MAP       ((struct page *) VMALLOC_END)
 
 /*
@@ -375,24 +374,6 @@ extern char empty_zero_page[PAGE_SIZE];
 # define PxD_SHADOW_SHIFT      2
 #endif /* __s390x__ */
 
-static inline struct page *get_shadow_page(struct page *page)
-{
-       if (s390_noexec && page->index)
-               return virt_to_page((void *)(addr_t) page->index);
-       return NULL;
-}
-
-static inline void *get_shadow_pte(void *table)
-{
-       unsigned long addr, offset;
-       struct page *page;
-
-       addr = (unsigned long) table;
-       offset = addr & (PAGE_SIZE - 1);
-       page = virt_to_page((void *)(addr ^ offset));
-       return (void *)(addr_t)(page->index ? (page->index | offset) : 0UL);
-}
-
 static inline void *get_shadow_table(void *table)
 {
        unsigned long addr, offset;
@@ -410,17 +391,16 @@ static inline void *get_shadow_table(void *table)
  * hook is made available.
  */
 static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
-                             pte_t *pteptr, pte_t pteval)
+                             pte_t *ptep, pte_t entry)
 {
-       pte_t *shadow_pte = get_shadow_pte(pteptr);
-
-       *pteptr = pteval;
-       if (shadow_pte) {
-               if (!(pte_val(pteval) & _PAGE_INVALID) &&
-                   (pte_val(pteval) & _PAGE_SWX))
-                       pte_val(*shadow_pte) = pte_val(pteval) | _PAGE_RO;
+       *ptep = entry;
+       if (mm->context.noexec) {
+               if (!(pte_val(entry) & _PAGE_INVALID) &&
+                   (pte_val(entry) & _PAGE_SWX))
+                       pte_val(entry) |= _PAGE_RO;
                else
-                       pte_val(*shadow_pte) = _PAGE_TYPE_EMPTY;
+                       pte_val(entry) = _PAGE_TYPE_EMPTY;
+               ptep[PTRS_PER_PTE] = entry;
        }
 }
 
@@ -439,24 +419,58 @@ static inline int pud_bad(pud_t pud)       { return 0; }
 
 #else /* __s390x__ */
 
-static inline int pgd_present(pgd_t pgd) { return 1; }
-static inline int pgd_none(pgd_t pgd)   { return 0; }
-static inline int pgd_bad(pgd_t pgd)    { return 0; }
+static inline int pgd_present(pgd_t pgd)
+{
+       if ((pgd_val(pgd) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R2)
+               return 1;
+       return (pgd_val(pgd) & _REGION_ENTRY_ORIGIN) != 0UL;
+}
+
+static inline int pgd_none(pgd_t pgd)
+{
+       if ((pgd_val(pgd) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R2)
+               return 0;
+       return (pgd_val(pgd) & _REGION_ENTRY_INV) != 0UL;
+}
+
+static inline int pgd_bad(pgd_t pgd)
+{
+       /*
+        * With dynamic page table levels the pgd can be a region table
+        * entry or a segment table entry. Check for the bit that are
+        * invalid for either table entry.
+        */
+       unsigned long mask =
+               ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
+               ~_REGION_ENTRY_TYPE_MASK & ~_REGION_ENTRY_LENGTH;
+       return (pgd_val(pgd) & mask) != 0;
+}
 
 static inline int pud_present(pud_t pud)
 {
+       if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R3)
+               return 1;
        return (pud_val(pud) & _REGION_ENTRY_ORIGIN) != 0UL;
 }
 
 static inline int pud_none(pud_t pud)
 {
+       if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R3)
+               return 0;
        return (pud_val(pud) & _REGION_ENTRY_INV) != 0UL;
 }
 
 static inline int pud_bad(pud_t pud)
 {
-       unsigned long mask = ~_REGION_ENTRY_ORIGIN & ~_REGION_ENTRY_INV;
-       return (pud_val(pud) & mask) != _REGION3_ENTRY;
+       /*
+        * With dynamic page table levels the pud can be a region table
+        * entry or a segment table entry. Check for the bit that are
+        * invalid for either table entry.
+        */
+       unsigned long mask =
+               ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
+               ~_REGION_ENTRY_TYPE_MASK & ~_REGION_ENTRY_LENGTH;
+       return (pud_val(pud) & mask) != 0;
 }
 
 #endif /* __s390x__ */
@@ -535,24 +549,30 @@ static inline int pte_young(pte_t pte)
 #define pgd_clear(pgd)         do { } while (0)
 #define pud_clear(pud)         do { } while (0)
 
-static inline void pmd_clear_kernel(pmd_t * pmdp)
+#else /* __s390x__ */
+
+static inline void pgd_clear_kernel(pgd_t * pgd)
 {
-       pmd_val(pmdp[0]) = _SEGMENT_ENTRY_EMPTY;
-       pmd_val(pmdp[1]) = _SEGMENT_ENTRY_EMPTY;
-       pmd_val(pmdp[2]) = _SEGMENT_ENTRY_EMPTY;
-       pmd_val(pmdp[3]) = _SEGMENT_ENTRY_EMPTY;
+       if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
+               pgd_val(*pgd) = _REGION2_ENTRY_EMPTY;
 }
 
-#else /* __s390x__ */
+static inline void pgd_clear(pgd_t * pgd)
+{
+       pgd_t *shadow = get_shadow_table(pgd);
 
-#define pgd_clear(pgd)         do { } while (0)
+       pgd_clear_kernel(pgd);
+       if (shadow)
+               pgd_clear_kernel(shadow);
+}
 
 static inline void pud_clear_kernel(pud_t *pud)
 {
-       pud_val(*pud) = _REGION3_ENTRY_EMPTY;
+       if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
+               pud_val(*pud) = _REGION3_ENTRY_EMPTY;
 }
 
-static inline void pud_clear(pud_t * pud)
+static inline void pud_clear(pud_t *pud)
 {
        pud_t *shadow = get_shadow_table(pud);
 
@@ -561,30 +581,27 @@ static inline void pud_clear(pud_t * pud)
                pud_clear_kernel(shadow);
 }
 
+#endif /* __s390x__ */
+
 static inline void pmd_clear_kernel(pmd_t * pmdp)
 {
        pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY;
-       pmd_val1(*pmdp) = _SEGMENT_ENTRY_EMPTY;
 }
 
-#endif /* __s390x__ */
-
-static inline void pmd_clear(pmd_t * pmdp)
+static inline void pmd_clear(pmd_t *pmd)
 {
-       pmd_t *shadow_pmd = get_shadow_table(pmdp);
+       pmd_t *shadow = get_shadow_table(pmd);
 
-       pmd_clear_kernel(pmdp);
-       if (shadow_pmd)
-               pmd_clear_kernel(shadow_pmd);
+       pmd_clear_kernel(pmd);
+       if (shadow)
+               pmd_clear_kernel(shadow);
 }
 
 static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
 {
-       pte_t *shadow_pte = get_shadow_pte(ptep);
-
        pte_val(*ptep) = _PAGE_TYPE_EMPTY;
-       if (shadow_pte)
-               pte_val(*shadow_pte) = _PAGE_TYPE_EMPTY;
+       if (mm->context.noexec)
+               pte_val(ptep[PTRS_PER_PTE]) = _PAGE_TYPE_EMPTY;
 }
 
 /*
@@ -665,7 +682,7 @@ static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
 {
        if (!(pte_val(*ptep) & _PAGE_INVALID)) {
 #ifndef __s390x__
-               /* S390 has 1mb segments, we are emulating 4MB segments */
+               /* pto must point to the start of the segment table */
                pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00);
 #else
                /* ipte in zarch mode can do the math */
@@ -679,12 +696,12 @@ static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
        pte_val(*ptep) = _PAGE_TYPE_EMPTY;
 }
 
-static inline void ptep_invalidate(unsigned long address, pte_t *ptep)
+static inline void ptep_invalidate(struct mm_struct *mm,
+                                  unsigned long address, pte_t *ptep)
 {
        __ptep_ipte(address, ptep);
-       ptep = get_shadow_pte(ptep);
-       if (ptep)
-               __ptep_ipte(address, ptep);
+       if (mm->context.noexec)
+               __ptep_ipte(address, ptep + PTRS_PER_PTE);
 }
 
 /*
@@ -706,7 +723,7 @@ static inline void ptep_invalidate(unsigned long address, pte_t *ptep)
        pte_t __pte = *(__ptep);                                        \
        if (atomic_read(&(__mm)->mm_users) > 1 ||                       \
            (__mm) != current->active_mm)                               \
-               ptep_invalidate(__address, __ptep);                     \
+               ptep_invalidate(__mm, __address, __ptep);               \
        else                                                            \
                pte_clear((__mm), (__address), (__ptep));               \
        __pte;                                                          \
@@ -717,7 +734,7 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
                                     unsigned long address, pte_t *ptep)
 {
        pte_t pte = *ptep;
-       ptep_invalidate(address, ptep);
+       ptep_invalidate(vma->vm_mm, address, ptep);
        return pte;
 }
 
@@ -738,7 +755,7 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
        if (full)
                pte_clear(mm, addr, ptep);
        else
-               ptep_invalidate(addr, ptep);
+               ptep_invalidate(mm, addr, ptep);
        return pte;
 }
 
@@ -749,7 +766,7 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
        if (pte_write(__pte)) {                                         \
                if (atomic_read(&(__mm)->mm_users) > 1 ||               \
                    (__mm) != current->active_mm)                       \
-                       ptep_invalidate(__addr, __ptep);                \
+                       ptep_invalidate(__mm, __addr, __ptep);          \
                set_pte_at(__mm, __addr, __ptep, pte_wrprotect(__pte)); \
        }                                                               \
 })
@@ -759,7 +776,7 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
 ({                                                                     \
        int __changed = !pte_same(*(__ptep), __entry);                  \
        if (__changed) {                                                \
-               ptep_invalidate(__addr, __ptep);                        \
+               ptep_invalidate((__vma)->vm_mm, __addr, __ptep);        \
                set_pte_at((__vma)->vm_mm, __addr, __ptep, __entry);    \
        }                                                               \
        __changed;                                                      \
@@ -840,13 +857,21 @@ static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
 
 #define pmd_deref(pmd) (pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN)
 #define pud_deref(pud) (pud_val(pud) & _REGION_ENTRY_ORIGIN)
-#define pgd_deref(pgd) ({ BUG(); 0UL; })
+#define pgd_deref(pgd) (pgd_val(pgd) & _REGION_ENTRY_ORIGIN)
 
-#define pud_offset(pgd, address) ((pud_t *) pgd)
+static inline pud_t *pud_offset(pgd_t *pgd, unsigned long address)
+{
+       pud_t *pud = (pud_t *) pgd;
+       if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
+               pud = (pud_t *) pgd_deref(*pgd);
+       return pud  + pud_index(address);
+}
 
 static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
 {
-       pmd_t *pmd = (pmd_t *) pud_deref(*pud);
+       pmd_t *pmd = (pmd_t *) pud;
+       if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
+               pmd = (pmd_t *) pud_deref(*pud);
        return pmd + pmd_index(address);
 }
 
index e878563..51d8891 100644 (file)
@@ -64,24 +64,29 @@ extern int get_cpu_capability(unsigned int *);
  */
 #ifndef __s390x__
 
-# define TASK_SIZE             (0x80000000UL)
-# define TASK_UNMAPPED_BASE    (TASK_SIZE / 2)
-# define DEFAULT_TASK_SIZE     (0x80000000UL)
+#define TASK_SIZE              (1UL << 31)
+#define TASK_UNMAPPED_BASE     (1UL << 30)
 
 #else /* __s390x__ */
 
-# define TASK_SIZE_OF(tsk)     (test_tsk_thread_flag(tsk, TIF_31BIT) ? \
-                                       (0x80000000UL) : (0x40000000000UL))
-# define TASK_SIZE             TASK_SIZE_OF(current)
-# define TASK_UNMAPPED_BASE    (TASK_SIZE / 2)
-# define DEFAULT_TASK_SIZE     (0x40000000000UL)
+#define TASK_SIZE_OF(tsk)      (test_tsk_thread_flag(tsk,TIF_31BIT) ? \
+                                       (1UL << 31) : (1UL << 53))
+#define TASK_UNMAPPED_BASE     (test_thread_flag(TIF_31BIT) ? \
+                                       (1UL << 30) : (1UL << 41))
+#define TASK_SIZE              TASK_SIZE_OF(current)
 
 #endif /* __s390x__ */
 
 #ifdef __KERNEL__
 
-#define STACK_TOP              TASK_SIZE
-#define STACK_TOP_MAX          DEFAULT_TASK_SIZE
+#ifndef __s390x__
+#define STACK_TOP              (1UL << 31)
+#define STACK_TOP_MAX          (1UL << 31)
+#else /* __s390x__ */
+#define STACK_TOP              (1UL << (test_thread_flag(TIF_31BIT) ? 31:42))
+#define STACK_TOP_MAX          (1UL << 42)
+#endif /* __s390x__ */
+
 
 #endif
 
@@ -138,8 +143,6 @@ struct stack_frame {
 /*
  * Do necessary setup to start up a new thread.
  */
-#ifndef __s390x__
-
 #define start_thread(regs, new_psw, new_stackp) do {            \
        set_fs(USER_DS);                                        \
        regs->psw.mask  = psw_user_bits;                        \
@@ -147,24 +150,6 @@ struct stack_frame {
         regs->gprs[15]  = new_stackp ;                          \
 } while (0)
 
-#else /* __s390x__ */
-
-#define start_thread(regs, new_psw, new_stackp) do {            \
-       set_fs(USER_DS);                                        \
-       regs->psw.mask  = psw_user_bits;                        \
-        regs->psw.addr  = new_psw;                              \
-        regs->gprs[15]  = new_stackp;                           \
-} while (0)
-
-#define start_thread31(regs, new_psw, new_stackp) do {          \
-       set_fs(USER_DS);                                        \
-       regs->psw.mask  = psw_user32_bits;                      \
-        regs->psw.addr  = new_psw;                              \
-        regs->gprs[15]  = new_stackp;                           \
-} while (0)
-
-#endif /* __s390x__ */
-
 /* Forward declaration, a strange C thing */
 struct task_struct;
 struct mm_struct;
index 3c8177f..3d8a96d 100644 (file)
@@ -38,7 +38,7 @@ struct mmu_gather {
        struct mm_struct *mm;
        unsigned int fullmm;
        unsigned int nr_ptes;
-       unsigned int nr_pmds;
+       unsigned int nr_pxds;
        void *array[TLB_NR_PTRS];
 };
 
@@ -53,7 +53,7 @@ static inline struct mmu_gather *tlb_gather_mmu(struct mm_struct *mm,
        tlb->fullmm = full_mm_flush || (num_online_cpus() == 1) ||
                (atomic_read(&mm->mm_users) <= 1 && mm == current->active_mm);
        tlb->nr_ptes = 0;
-       tlb->nr_pmds = TLB_NR_PTRS;
+       tlb->nr_pxds = TLB_NR_PTRS;
        if (tlb->fullmm)
                __tlb_flush_mm(mm);
        return tlb;
@@ -62,12 +62,13 @@ static inline struct mmu_gather *tlb_gather_mmu(struct mm_struct *mm,
 static inline void tlb_flush_mmu(struct mmu_gather *tlb,
                                 unsigned long start, unsigned long end)
 {
-       if (!tlb->fullmm && (tlb->nr_ptes > 0 || tlb->nr_pmds < TLB_NR_PTRS))
+       if (!tlb->fullmm && (tlb->nr_ptes > 0 || tlb->nr_pxds < TLB_NR_PTRS))
                __tlb_flush_mm(tlb->mm);
        while (tlb->nr_ptes > 0)
                pte_free(tlb->mm, tlb->array[--tlb->nr_ptes]);
-       while (tlb->nr_pmds < TLB_NR_PTRS)
-               pmd_free(tlb->mm, (pmd_t *) tlb->array[tlb->nr_pmds++]);
+       while (tlb->nr_pxds < TLB_NR_PTRS)
+               /* pgd_free frees the pointer as region or segment table */
+               pgd_free(tlb->mm, tlb->array[tlb->nr_pxds++]);
 }
 
 static inline void tlb_finish_mmu(struct mmu_gather *tlb,
@@ -95,33 +96,57 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
  * pte_free_tlb frees a pte table and clears the CRSTE for the
  * page table from the tlb.
  */
-static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t page)
+static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte)
 {
        if (!tlb->fullmm) {
-               tlb->array[tlb->nr_ptes++] = page;
-               if (tlb->nr_ptes >= tlb->nr_pmds)
+               tlb->array[tlb->nr_ptes++] = pte;
+               if (tlb->nr_ptes >= tlb->nr_pxds)
                        tlb_flush_mmu(tlb, 0, 0);
        } else
-               pte_free(tlb->mm, page);
+               pte_free(tlb->mm, pte);
 }
 
 /*
  * pmd_free_tlb frees a pmd table and clears the CRSTE for the
  * segment table entry from the tlb.
+ * If the mm uses a two level page table the single pmd is freed
+ * as the pgd. pmd_free_tlb checks the asce_limit against 2GB
+ * to avoid the double free of the pmd in this case.
  */
 static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
 {
 #ifdef __s390x__
+       if (tlb->mm->context.asce_limit <= (1UL << 31))
+               return;
        if (!tlb->fullmm) {
-               tlb->array[--tlb->nr_pmds] = (struct page *) pmd;
-               if (tlb->nr_ptes >= tlb->nr_pmds)
+               tlb->array[--tlb->nr_pxds] = pmd;
+               if (tlb->nr_ptes >= tlb->nr_pxds)
                        tlb_flush_mmu(tlb, 0, 0);
        } else
                pmd_free(tlb->mm, pmd);
 #endif
 }
 
-#define pud_free_tlb(tlb, pud)                 do { } while (0)
+/*
+ * pud_free_tlb frees a pud table and clears the CRSTE for the
+ * region third table entry from the tlb.
+ * If the mm uses a three level page table the single pud is freed
+ * as the pgd. pud_free_tlb checks the asce_limit against 4TB
+ * to avoid the double free of the pud in this case.
+ */
+static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud)
+{
+#ifdef __s390x__
+       if (tlb->mm->context.asce_limit <= (1UL << 42))
+               return;
+       if (!tlb->fullmm) {
+               tlb->array[--tlb->nr_pxds] = pud;
+               if (tlb->nr_ptes >= tlb->nr_pxds)
+                       tlb_flush_mmu(tlb, 0, 0);
+       } else
+               pud_free(tlb->mm, pud);
+#endif
+}
 
 #define tlb_start_vma(tlb, vma)                        do { } while (0)
 #define tlb_end_vma(tlb, vma)                  do { } while (0)
index 70fa5ae..35fb4f9 100644 (file)
@@ -61,11 +61,12 @@ static inline void __tlb_flush_mm(struct mm_struct * mm)
         * only ran on the local cpu.
         */
        if (MACHINE_HAS_IDTE) {
-               pgd_t *shadow = get_shadow_table(mm->pgd);
-
-               if (shadow)
-                       __tlb_flush_idte((unsigned long) shadow | mm->context);
-               __tlb_flush_idte((unsigned long) mm->pgd | mm->context);
+               if (mm->context.noexec)
+                       __tlb_flush_idte((unsigned long)
+                                        get_shadow_table(mm->pgd) |
+                                        mm->context.asce_bits);
+               __tlb_flush_idte((unsigned long) mm->pgd |
+                                mm->context.asce_bits);
                return;
        }
        preempt_disable();
index f04acb2..583da80 100644 (file)
 #define __NR_signalfd          316
 #define __NR_timerfd           317
 #define __NR_eventfd           318
-#define NR_syscalls 319
+#define __NR_timerfd_create    319
+#define __NR_timerfd_settime   320
+#define __NR_timerfd_gettime   321
+#define NR_syscalls 322
 
 /* 
  * There are some system calls that are not present on 64 bit, some
index 661d8cd..74a539a 100644 (file)
@@ -57,6 +57,7 @@ typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long pmd; } pmd_t;
 typedef struct { unsigned long pgd; } pgd_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
+typedef struct page *pgtable_t;
 
 #define pte_val(x)      ((x).pte)
 #define pmd_val(x)      ((x).pmd)
index c4f635a..2500325 100644 (file)
@@ -489,6 +489,13 @@ do {                                                                              \
 #define        EXT4_ERROR_FS                   0x0002  /* Errors detected */
 #define        EXT4_ORPHAN_FS                  0x0004  /* Orphans being recovered */
 
+/*
+ * Misc. filesystem flags
+ */
+#define EXT2_FLAGS_SIGNED_HASH         0x0001  /* Signed dirhash in use */
+#define EXT2_FLAGS_UNSIGNED_HASH       0x0002  /* Unsigned dirhash in use */
+#define EXT2_FLAGS_TEST_FILESYS                0x0004  /* to test development code */
+
 /*
  * Mount flags
  */
index 9815951..925d57b 100644 (file)
@@ -51,10 +51,8 @@ extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
                                        gfp_t gfp_mask);
 int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);
 
-static inline struct mem_cgroup *mm_cgroup(const struct mm_struct *mm)
-{
-       return rcu_dereference(mm->mem_cgroup);
-}
+#define vm_match_cgroup(mm, cgroup)    \
+       ((cgroup) == rcu_dereference((mm)->mem_cgroup))
 
 extern int mem_cgroup_prepare_migration(struct page *page);
 extern void mem_cgroup_end_migration(struct page *page);
@@ -123,9 +121,9 @@ static inline int mem_cgroup_cache_charge(struct page *page,
        return 0;
 }
 
-static inline struct mem_cgroup *mm_cgroup(const struct mm_struct *mm)
+static inline int vm_match_cgroup(struct mm_struct *mm, struct mem_cgroup *mem)
 {
-       return NULL;
+       return 1;
 }
 
 static inline int task_in_mem_cgroup(struct task_struct *task,
diff --git a/include/linux/memstick.h b/include/linux/memstick.h
new file mode 100644 (file)
index 0000000..334d059
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *  Sony MemoryStick support
+ *
+ *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MEMSTICK_H
+#define _MEMSTICK_H
+
+#include <linux/workqueue.h>
+#include <linux/scatterlist.h>
+#include <linux/device.h>
+
+/*** Hardware based structures ***/
+
+struct ms_status_register {
+       unsigned char reserved;
+       unsigned char interrupt;
+#define MEMSTICK_INT_CMDNAK             0x0001
+#define MEMSTICK_INT_BREQ               0x0020
+#define MEMSTICK_INT_ERR                0x0040
+#define MEMSTICK_INT_CED                0x0080
+
+       unsigned char status0;
+#define MEMSTICK_STATUS0_WP             0x0001
+#define MEMSTICK_STATUS0_SL             0x0002
+#define MEMSTICK_STATUS0_BF             0x0010
+#define MEMSTICK_STATUS0_BE             0x0020
+#define MEMSTICK_STATUS0_FB0            0x0040
+#define MEMSTICK_STATUS0_MB             0x0080
+
+       unsigned char status1;
+#define MEMSTICK_STATUS1_UCFG           0x0001
+#define MEMSTICK_STATUS1_FGER           0x0002
+#define MEMSTICK_STATUS1_UCEX           0x0004
+#define MEMSTICK_STATUS1_EXER           0x0008
+#define MEMSTICK_STATUS1_UCDT           0x0010
+#define MEMSTICK_STATUS1_DTER           0x0020
+#define MEMSTICK_STATUS1_FBI            0x0040
+#define MEMSTICK_STATUS1_MB             0x0080
+} __attribute__((packed));
+
+struct ms_id_register {
+       unsigned char type;
+       unsigned char reserved;
+       unsigned char category;
+       unsigned char class;
+} __attribute__((packed));
+
+struct ms_param_register {
+       unsigned char system;
+       unsigned char block_address_msb;
+       unsigned short block_address;
+       unsigned char cp;
+#define MEMSTICK_CP_BLOCK               0x0000
+#define MEMSTICK_CP_PAGE                0x0020
+#define MEMSTICK_CP_EXTRA               0x0040
+#define MEMSTICK_CP_OVERWRITE           0x0080
+
+       unsigned char page_address;
+} __attribute__((packed));
+
+struct ms_extra_data_register {
+       unsigned char  overwrite_flag;
+#define MEMSTICK_OVERWRITE_UPDATA       0x0010
+#define MEMSTICK_OVERWRITE_PAGE         0x0060
+#define MEMSTICK_OVERWRITE_BLOCK        0x0080
+
+       unsigned char  management_flag;
+#define MEMSTICK_MANAGEMENT_SYSTEM      0x0004
+#define MEMSTICK_MANAGEMENT_TRANS_TABLE 0x0008
+#define MEMSTICK_MANAGEMENT_COPY        0x0010
+#define MEMSTICK_MANAGEMENT_ACCESS      0x0020
+
+       unsigned short logical_address;
+} __attribute__((packed));
+
+struct ms_register {
+       struct ms_status_register     status;
+       struct ms_id_register         id;
+       unsigned char                 reserved[8];
+       struct ms_param_register      param;
+       struct ms_extra_data_register extra_data;
+} __attribute__((packed));
+
+struct mspro_param_register {
+       unsigned char  system;
+       unsigned short data_count;
+       unsigned int   data_address;
+       unsigned char  cmd_param;
+} __attribute__((packed));
+
+struct mspro_register {
+       struct ms_status_register    status;
+       struct ms_id_register        id;
+       unsigned char                reserved[8];
+       struct mspro_param_register  param;
+} __attribute__((packed));
+
+struct ms_register_addr {
+       unsigned char r_offset;
+       unsigned char r_length;
+       unsigned char w_offset;
+       unsigned char w_length;
+} __attribute__((packed));
+
+enum {
+       MS_TPC_READ_LONG_DATA   = 0x02,
+       MS_TPC_READ_SHORT_DATA  = 0x03,
+       MS_TPC_READ_REG         = 0x04,
+       MS_TPC_READ_IO_DATA     = 0x05, /* unverified */
+       MS_TPC_GET_INT          = 0x07,
+       MS_TPC_SET_RW_REG_ADRS  = 0x08,
+       MS_TPC_EX_SET_CMD       = 0x09,
+       MS_TPC_WRITE_IO_DATA    = 0x0a, /* unverified */
+       MS_TPC_WRITE_REG        = 0x0b,
+       MS_TPC_WRITE_SHORT_DATA = 0x0c,
+       MS_TPC_WRITE_LONG_DATA  = 0x0d,
+       MS_TPC_SET_CMD          = 0x0e
+};
+
+enum {
+       MS_CMD_BLOCK_END     = 0x33,
+       MS_CMD_RESET         = 0x3c,
+       MS_CMD_BLOCK_WRITE   = 0x55,
+       MS_CMD_SLEEP         = 0x5a,
+       MS_CMD_BLOCK_ERASE   = 0x99,
+       MS_CMD_BLOCK_READ    = 0xaa,
+       MS_CMD_CLEAR_BUF     = 0xc3,
+       MS_CMD_FLASH_STOP    = 0xcc,
+       MSPRO_CMD_FORMAT     = 0x10,
+       MSPRO_CMD_SLEEP      = 0x11,
+       MSPRO_CMD_READ_DATA  = 0x20,
+       MSPRO_CMD_WRITE_DATA = 0x21,
+       MSPRO_CMD_READ_ATRB  = 0x24,
+       MSPRO_CMD_STOP       = 0x25,
+       MSPRO_CMD_ERASE      = 0x26,
+       MSPRO_CMD_SET_IBA    = 0x46,
+       MSPRO_CMD_SET_IBD    = 0x47
+/*
+       MSPRO_CMD_RESET
+       MSPRO_CMD_WAKEUP
+       MSPRO_CMD_IN_IO_DATA
+       MSPRO_CMD_OUT_IO_DATA
+       MSPRO_CMD_READ_IO_ATRB
+       MSPRO_CMD_IN_IO_FIFO
+       MSPRO_CMD_OUT_IO_FIFO
+       MSPRO_CMD_IN_IOM
+       MSPRO_CMD_OUT_IOM
+*/
+};
+
+/*** Driver structures and functions ***/
+
+#define MEMSTICK_PART_SHIFT 3
+
+enum memstick_param { MEMSTICK_POWER = 1, MEMSTICK_INTERFACE };
+
+#define MEMSTICK_POWER_OFF 0
+#define MEMSTICK_POWER_ON  1
+
+#define MEMSTICK_SERIAL   0
+#define MEMSTICK_PARALLEL 1
+
+struct memstick_host;
+struct memstick_driver;
+
+#define MEMSTICK_MATCH_ALL            0x01
+
+#define MEMSTICK_TYPE_LEGACY          0xff
+#define MEMSTICK_TYPE_DUO             0x00
+#define MEMSTICK_TYPE_PRO             0x01
+
+#define MEMSTICK_CATEGORY_STORAGE     0xff
+#define MEMSTICK_CATEGORY_STORAGE_DUO 0x00
+
+#define MEMSTICK_CLASS_GENERIC        0xff
+#define MEMSTICK_CLASS_GENERIC_DUO    0x00
+
+
+struct memstick_device_id {
+       unsigned char match_flags;
+       unsigned char type;
+       unsigned char category;
+       unsigned char class;
+};
+
+struct memstick_request {
+       unsigned char tpc;
+       unsigned char data_dir:1,
+                     need_card_int:1,
+                     get_int_reg:1,
+                     io_type:2;
+#define               MEMSTICK_IO_NONE 0
+#define               MEMSTICK_IO_VAL  1
+#define               MEMSTICK_IO_SG   2
+
+       unsigned char int_reg;
+       int           error;
+       union {
+               struct scatterlist sg;
+               struct {
+                       unsigned char data_len;
+                       unsigned char data[15];
+               };
+       };
+};
+
+struct memstick_dev {
+       struct memstick_device_id id;
+       struct memstick_host     *host;
+       struct ms_register_addr  reg_addr;
+       struct completion        mrq_complete;
+       struct memstick_request  current_mrq;
+
+       /* Check that media driver is still willing to operate the device. */
+       int                      (*check)(struct memstick_dev *card);
+       /* Get next request from the media driver.                         */
+       int                      (*next_request)(struct memstick_dev *card,
+                                                struct memstick_request **mrq);
+
+       struct device            dev;
+};
+
+struct memstick_host {
+       struct mutex        lock;
+       unsigned int        id;
+       unsigned int        caps;
+#define MEMSTICK_CAP_PARALLEL      1
+#define MEMSTICK_CAP_AUTO_GET_INT  2
+
+       struct work_struct  media_checker;
+       struct class_device cdev;
+
+       struct memstick_dev *card;
+       unsigned int        retries;
+
+       /* Notify the host that some requests are pending. */
+       void                (*request)(struct memstick_host *host);
+       /* Set host IO parameters (power, clock, etc).     */
+       void                (*set_param)(struct memstick_host *host,
+                                        enum memstick_param param,
+                                        int value);
+       unsigned long       private[0] ____cacheline_aligned;
+};
+
+struct memstick_driver {
+       struct memstick_device_id *id_table;
+       int                       (*probe)(struct memstick_dev *card);
+       void                      (*remove)(struct memstick_dev *card);
+       int                       (*suspend)(struct memstick_dev *card,
+                                            pm_message_t state);
+       int                       (*resume)(struct memstick_dev *card);
+
+       struct device_driver      driver;
+};
+
+int memstick_register_driver(struct memstick_driver *drv);
+void memstick_unregister_driver(struct memstick_driver *drv);
+
+struct memstick_host *memstick_alloc_host(unsigned int extra,
+                                         struct device *dev);
+
+int memstick_add_host(struct memstick_host *host);
+void memstick_remove_host(struct memstick_host *host);
+void memstick_free_host(struct memstick_host *host);
+void memstick_detect_change(struct memstick_host *host);
+
+void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
+                         struct scatterlist *sg);
+void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
+                      void *buf, size_t length);
+int memstick_next_req(struct memstick_host *host,
+                     struct memstick_request **mrq);
+void memstick_new_req(struct memstick_host *host);
+
+int memstick_set_rw_addr(struct memstick_dev *card);
+
+static inline void *memstick_priv(struct memstick_host *host)
+{
+       return (void *)host->private;
+}
+
+static inline void *memstick_get_drvdata(struct memstick_dev *card)
+{
+       return dev_get_drvdata(&card->dev);
+}
+
+static inline void memstick_set_drvdata(struct memstick_dev *card, void *data)
+{
+       dev_set_drvdata(&card->dev, data);
+}
+
+#endif
index 7bf2d14..6ec39ab 100644 (file)
@@ -42,11 +42,13 @@ static inline pgoff_t swp_offset(swp_entry_t entry)
        return entry.val & SWP_OFFSET_MASK(entry);
 }
 
+#ifdef CONFIG_MMU
 /* check whether a pte points to a swap entry */
 static inline int is_swap_pte(pte_t pte)
 {
        return !pte_none(pte) && !pte_present(pte) && !pte_file(pte);
 }
+#endif
 
 /*
  * Convert the arch-dependent pte representation of a swp_entry_t into an
index bba7712..818ca1c 100644 (file)
@@ -79,7 +79,9 @@ struct thermal_zone_device {
 };
 
 struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,
-                                       struct thermal_zone_device_ops *);
+                                                        struct
+                                                        thermal_zone_device_ops
+                                                        *);
 void thermal_zone_device_unregister(struct thermal_zone_device *);
 
 int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
@@ -87,8 +89,23 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
                                       struct thermal_cooling_device *);
 
+#ifdef CONFIG_THERMAL
 struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
-                                       struct thermal_cooling_device_ops *);
+                                                              struct
+                                                              thermal_cooling_device_ops
+                                                              *);
 void thermal_cooling_device_unregister(struct thermal_cooling_device *);
+#else
+static inline struct thermal_cooling_device
+*thermal_cooling_device_register(char *c, void *v,
+                                struct thermal_cooling_device_ops *t)
+{
+       return NULL;
+}
+static inline
+    void thermal_cooling_device_unregister(struct thermal_cooling_device *t)
+{
+};
+#endif
 
-#endif                         /* __THERMAL_H__ */
+#endif /* __THERMAL_H__ */
index 2096b76..da76ed8 100644 (file)
@@ -72,6 +72,7 @@ enum {
 #define TIFM_FIFO_READY           0x00000001
 #define TIFM_FIFO_INT_SETALL      0x0000ffff
 #define TIFM_FIFO_INTMASK         0x00000005
+#define TIFM_FIFO_SIZE            0x00000200
 
 #define TIFM_DMA_RESET            0x00000002
 #define TIFM_DMA_TX               0x00008000
@@ -124,6 +125,8 @@ struct tifm_adapter {
 
        void                (*eject)(struct tifm_adapter *fm,
                                     struct tifm_dev *sock);
+       int                 (*has_ms_pif)(struct tifm_adapter *fm,
+                                         struct tifm_dev *sock);
 
        struct tifm_dev     *sockets[0];
 };
@@ -141,6 +144,7 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
 int tifm_register_driver(struct tifm_driver *drv);
 void tifm_unregister_driver(struct tifm_driver *drv);
 void tifm_eject(struct tifm_dev *sock);
+int tifm_has_ms_pif(struct tifm_dev *sock);
 int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
                int direction);
 void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
index 633392f..c5f157c 100644 (file)
@@ -27,7 +27,9 @@ $(obj)/version.o: include/linux/compile.h
 # mkcompile_h will make sure to only update the
 # actual file if its content has changed.
 
+ quiet_chk_compile.h = echo '  CHK     $@'
+silent_chk_compile.h = :
 include/linux/compile.h: FORCE
-       @echo '  CHK     $@'
+       @$($(quiet)chk_compile.h)
        $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkcompile_h $@ \
        "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT)" "$(CC) $(KBUILD_CFLAGS)"
index f2ab700..ab4f109 100644 (file)
@@ -3,3 +3,4 @@
 #
 config_data.h
 config_data.gz
+timeconst.h
index 5c2c702..6bded84 100644 (file)
@@ -399,7 +399,7 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem)
        int ret;
 
        task_lock(task);
-       ret = task->mm && mm_cgroup(task->mm) == mem;
+       ret = task->mm && vm_match_cgroup(task->mm, mem);
        task_unlock(task);
        return ret;
 }
index a0e92a2..8fd527c 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -321,7 +321,7 @@ static int page_referenced_anon(struct page *page,
                 * counting on behalf of references from different
                 * cgroups
                 */
-               if (mem_cont && (mm_cgroup(vma->vm_mm) != mem_cont))
+               if (mem_cont && !vm_match_cgroup(vma->vm_mm, mem_cont))
                        continue;
                referenced += page_referenced_one(page, vma, &mapcount);
                if (!mapcount)
@@ -382,7 +382,7 @@ static int page_referenced_file(struct page *page,
                 * counting on behalf of references from different
                 * cgroups
                 */
-               if (mem_cont && (mm_cgroup(vma->vm_mm) != mem_cont))
+               if (mem_cont && !vm_match_cgroup(vma->vm_mm, mem_cont))
                        continue;
                if ((vma->vm_flags & (VM_LOCKED|VM_MAYSHARE))
                                  == (VM_LOCKED|VM_MAYSHARE)) {
index a1f52cb..b939fbd 100644 (file)
@@ -6,3 +6,4 @@ kallsyms
 pnmtologo
 bin2c
 unifdef
+binoffset
index b96ea8d..da3559e 100644 (file)
@@ -39,15 +39,19 @@ escsq = $(subst $(squote),'\$(squote)',$1)
 # - If they are equal no change, and no timestamp update
 # - stdin is piped in from the first prerequisite ($<) so one has
 #   to specify a valid file as first prerequisite (often the kbuild file)
+ quiet_chk_filechk = echo '  CHK     $@'
+silent_chk_filechk = :
+ quiet_upd_filechk = echo '  UPD     $@'
+silent_upd_filechk = :
 define filechk
        $(Q)set -e;                             \
-       echo '  CHK     $@';                    \
+       $($(quiet)chk_filechk);                 \
        mkdir -p $(dir $@);                     \
        $(filechk_$(1)) < $< > $@.tmp;          \
        if [ -r $@ ] && cmp -s $@ $@.tmp; then  \
                rm -f $@.tmp;                   \
        else                                    \
-               echo '  UPD     $@';            \
+               $($(quiet)upd_filechk);         \
                mv -f $@.tmp $@;                \
        fi
 endef
index 5d54646..dbe1fb5 100644 (file)
@@ -613,7 +613,7 @@ static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
        if (sym)
                return elf->strtab + sym->st_name;
        else
-               return "";
+               return "(unknown)";
 }
 
 static const char *sec_name(struct elf_info *elf, int shndx)
@@ -1102,7 +1102,7 @@ static int is_function(Elf_Sym *sym)
        if (sym)
                return ELF_ST_TYPE(sym->st_info) == STT_FUNC;
        else
-               return 0;
+               return -1;
 }
 
 /*
@@ -1120,24 +1120,31 @@ static void report_sec_mismatch(const char *modname, enum mismatch mismatch,
 {
        const char *from, *from_p;
        const char *to, *to_p;
-       from = from_is_func ? "function" : "variable";
-       from_p = from_is_func ? "()" : "";
-       to = to_is_func ? "function" : "variable";
-       to_p = to_is_func ? "()" : "";
+
+       switch (from_is_func) {
+       case 0: from = "variable"; from_p = "";   break;
+       case 1: from = "function"; from_p = "()"; break;
+       default: from = "(unknown reference)"; from_p = ""; break;
+       }
+       switch (to_is_func) {
+       case 0: to = "variable"; to_p = "";   break;
+       case 1: to = "function"; to_p = "()"; break;
+       default: to = "(unknown reference)"; to_p = ""; break;
+       }
 
        sec_mismatch_count++;
        if (!sec_mismatch_verbose)
                return;
 
-       fprintf(stderr, "WARNING: %s(%s+0x%llx): Section mismatch in"
-                       " reference from the %s %s%s to the %s %s:%s%s\n",
-                        modname, fromsec, fromaddr, from, fromsym, from_p,
-                       to, tosec, tosym, to_p);
+       warn("%s(%s+0x%llx): Section mismatch in reference from the %s %s%s "
+            "to the %s %s:%s%s\n",
+            modname, fromsec, fromaddr, from, fromsym, from_p, to, tosec,
+            tosym, to_p);
 
        switch (mismatch) {
        case TEXT_TO_INIT:
                fprintf(stderr,
-               "The function %s %s() references\n"
+               "The function %s%s() references\n"
                "the %s %s%s%s.\n"
                "This is often because %s lacks a %s\n"
                "annotation or the annotation of %s is wrong.\n",
@@ -1938,10 +1945,10 @@ int main(int argc, char **argv)
        if (dump_write)
                write_dump(dump_write);
        if (sec_mismatch_count && !sec_mismatch_verbose)
-               fprintf(stderr, "modpost: Found %d section mismatch(es).\n"
-                       "To see full details build your kernel with:\n"
-                       "'make CONFIG_DEBUG_SECTION_MISMATCH=y'\n",
-                       sec_mismatch_count);
+               warn("modpost: Found %d section mismatch(es).\n"
+                    "To see full details build your kernel with:\n"
+                    "'make CONFIG_DEBUG_SECTION_MISMATCH=y'\n",
+                    sec_mismatch_count);
 
        return err;
 }
index 0f657b5..ba6bf5d 100644 (file)
@@ -13,6 +13,7 @@ set -e
 
 # Some variables and settings used throughout the script
 version=$KERNELRELEASE
+revision=`cat .version`
 tmpdir="$objtree/debian/tmp"
 packagename=linux-$version
 
@@ -65,7 +66,7 @@ done
 name="Kernel Compiler <$(id -nu)@$(hostname -f)>"
 # Generate a simple changelog template
 cat <<EOF > debian/changelog
-linux ($version) unstable; urgency=low
+linux ($version-$revision) unstable; urgency=low
 
   * A standard release