Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 15 Dec 2009 16:58:13 +0000 (08:58 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 15 Dec 2009 16:58:13 +0000 (08:58 -0800)
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6:
  USB: Close usb_find_interface race v3
  Revert "USB: Close usb_find_interface race"

325 files changed:
Documentation/ABI/testing/sysfs-devices-memory
Documentation/ABI/testing/sysfs-devices-system-cpu
Documentation/feature-removal-schedule.txt
Documentation/filesystems/proc.txt
Documentation/hwmon/lis3lv02d
Documentation/hwmon/w83627ehf
Documentation/i2c/writing-clients
Documentation/memory-hotplug.txt
Documentation/misc-devices/ad525x_dpot.txt [new file with mode: 0644]
Documentation/nommu-mmap.txt
Documentation/vm/hugetlbpage.txt
Documentation/vm/ksm.txt
Documentation/vm/page-types.c
MAINTAINERS
arch/alpha/kernel/srm_env.c
arch/arm/mach-at91/include/mach/atmel-mci.h [new file with mode: 0644]
arch/arm/plat-omap/debug-leds.c
arch/arm/plat-omap/gpio.c
arch/avr32/mach-at32ap/at32ap700x.c
arch/avr32/mach-at32ap/include/mach/atmel-mci.h [new file with mode: 0644]
arch/ia64/include/asm/numa.h
arch/s390/appldata/appldata_base.c
arch/s390/kernel/debug.c
arch/um/drivers/mconsole_kern.c
arch/um/drivers/ubd_kern.c
arch/um/kernel/exitcode.c
arch/um/kernel/process.c
arch/x86/Kconfig
arch/x86/include/asm/geode.h
arch/x86/include/asm/olpc.h
arch/x86/include/asm/topology.h
arch/x86/kernel/Makefile
arch/x86/kernel/cpu/mtrr/if.c
arch/x86/kernel/geode_32.c [deleted file]
arch/x86/kernel/mfgpt_32.c [deleted file]
arch/x86/kernel/olpc.c
arch/x86/kernel/reboot_fixups_32.c
drivers/Kconfig
drivers/base/node.c
drivers/block/floppy.c
drivers/char/hvc_iucv.c
drivers/char/mem.c
drivers/char/misc.c
drivers/char/random.c
drivers/char/vt.c
drivers/clocksource/Kconfig [new file with mode: 0644]
drivers/clocksource/Makefile
drivers/clocksource/cs5535-clockevt.c [new file with mode: 0644]
drivers/cpuidle/governors/ladder.c
drivers/dma/at_hdmac.c
drivers/dma/dw_dmac.c
drivers/dma/txx9dmac.c
drivers/firmware/Kconfig
drivers/firmware/dmi_scan.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/cs5535-gpio.c [new file with mode: 0644]
drivers/hwmon/Kconfig
drivers/hwmon/adm1021.c
drivers/hwmon/adm1025.c
drivers/hwmon/adm1026.c
drivers/hwmon/adm1029.c
drivers/hwmon/adm1031.c
drivers/hwmon/adm9240.c
drivers/hwmon/ads7828.c
drivers/hwmon/adt7462.c
drivers/hwmon/adt7470.c
drivers/hwmon/adt7473.c
drivers/hwmon/adt7475.c
drivers/hwmon/applesmc.c
drivers/hwmon/asb100.c
drivers/hwmon/atxp1.c
drivers/hwmon/dme1737.c
drivers/hwmon/ds1621.c
drivers/hwmon/f75375s.c
drivers/hwmon/fschmd.c
drivers/hwmon/gl518sm.c
drivers/hwmon/gl520sm.c
drivers/hwmon/lis3lv02d.c
drivers/hwmon/lis3lv02d.h
drivers/hwmon/lm63.c
drivers/hwmon/lm73.c
drivers/hwmon/lm75.c
drivers/hwmon/lm77.c
drivers/hwmon/lm78.c
drivers/hwmon/lm80.c
drivers/hwmon/lm83.c
drivers/hwmon/lm85.c
drivers/hwmon/lm87.c
drivers/hwmon/lm90.c
drivers/hwmon/lm92.c
drivers/hwmon/lm93.c
drivers/hwmon/lm95241.c
drivers/hwmon/max1619.c
drivers/hwmon/max6650.c
drivers/hwmon/pcf8591.c
drivers/hwmon/smsc47m192.c
drivers/hwmon/thmc50.c
drivers/hwmon/tmp401.c
drivers/hwmon/tmp421.c
drivers/hwmon/w83627ehf.c
drivers/hwmon/w83781d.c
drivers/hwmon/w83791d.c
drivers/hwmon/w83792d.c
drivers/hwmon/w83793.c
drivers/hwmon/w83l785ts.c
drivers/hwmon/w83l786ng.c
drivers/i2c/busses/i2c-pxa.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/busses/i2c-sh_mobile.c
drivers/i2c/i2c-core.c
drivers/input/keyboard/adp5588-keys.c
drivers/input/keyboard/sh_keysc.c
drivers/input/misc/bfin_rotary.c
drivers/input/misc/pcspkr.c
drivers/input/touchscreen/pcap_ts.c
drivers/leds/led-class.c
drivers/leds/ledtrig-timer.c
drivers/md/dm-table.c
drivers/md/md.c
drivers/media/video/davinci/vpfe_capture.c
drivers/media/video/davinci/vpif_capture.c
drivers/media/video/sh_mobile_ceu_camera.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/ad525x_dpot.c [new file with mode: 0644]
drivers/misc/cs5535-mfgpt.c [new file with mode: 0644]
drivers/misc/eeprom/eeprom.c
drivers/misc/ics932s401.c
drivers/misc/ioc4.c
drivers/misc/ti_dac7512.c [new file with mode: 0644]
drivers/mmc/core/Kconfig
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/mmc.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio_cis.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/bfin_sdh.c [new file with mode: 0644]
drivers/mmc/host/davinci_mmc.c [new file with mode: 0644]
drivers/mmc/host/mxcmmc.c
drivers/mmc/host/omap.c
drivers/mmc/host/pxamci.c
drivers/mmc/host/s3cmci.c
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/tmio_mmc.c
drivers/mtd/nand/nomadik_nand.c
drivers/net/3c59x.c
drivers/net/dm9000.c
drivers/net/r8169.c
drivers/net/smsc911x.c
drivers/net/vmxnet3/vmxnet3_drv.c
drivers/parisc/pdc_stable.c
drivers/pci/pcie/portdrv_pci.c
drivers/pcmcia/pxa2xx_base.c
drivers/pcmcia/yenta_socket.c
drivers/platform/x86/acerhdf.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/thinkpad_acpi.c
drivers/pnp/interface.c
drivers/power/wm97xx_battery.c
drivers/rtc/rtc-pxa.c
drivers/rtc/rtc-sa1100.c
drivers/rtc/rtc-sh.c
drivers/rtc/rtc-wm831x.c
drivers/s390/block/dasd_proc.c
drivers/s390/block/dcssblk.c
drivers/s390/block/xpram.c
drivers/s390/char/monreader.c
drivers/s390/char/monwriter.c
drivers/s390/char/sclp.c
drivers/s390/char/sclp_cmd.c
drivers/s390/char/vmlogrdr.c
drivers/s390/cio/ccwgroup.c
drivers/s390/cio/css.c
drivers/s390/cio/device.c
drivers/s390/net/netiucv.c
drivers/s390/net/smsgiucv.c
drivers/scsi/ipr.c
drivers/scsi/sym53c8xx_2/sym_glue.c
drivers/serial/ioc3_serial.c
drivers/serial/ioc4_serial.c
drivers/serial/pxa.c
drivers/serial/sh-sci.c
drivers/sn/ioc3.c
drivers/spi/pxa2xx_spi.c
drivers/spi/spi_s3c24xx.c
drivers/uio/uio_pdrv_genirq.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.h
drivers/usb/core/usb.c
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ohci-au1xxx.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/r8a66597-hcd.c
drivers/usb/musb/musb_core.c
drivers/video/backlight/da903x_bl.c
drivers/video/backlight/lcd.c
drivers/video/display/display-sysfs.c
drivers/video/geode/display_gx.c
drivers/video/geode/gxfb.h
drivers/video/geode/gxfb_core.c
drivers/video/geode/lxfb.h
drivers/video/geode/lxfb_ops.c
drivers/video/geode/suspend_gx.c
drivers/video/geode/video_gx.c
drivers/video/hitfb.c
drivers/video/output.c
drivers/video/pxafb.c
drivers/video/sh_mobile_lcdcfb.c
drivers/watchdog/adx_wdt.c
fs/Kconfig
fs/binfmt_elf_fdpic.c
fs/btrfs/Kconfig
fs/cachefiles/daemon.c
fs/exec.c
fs/ext4/Kconfig
fs/ext4/super.c
fs/gfs2/Kconfig
fs/gfs2/sys.c
fs/hfs/catalog.c
fs/hfs/dir.c
fs/hfs/super.c
fs/jbd/Kconfig
fs/jbd2/Kconfig
fs/nilfs2/Kconfig
fs/proc/base.c
fs/proc/task_mmu.c
fs/proc/task_nommu.c
fs/reiserfs/Kconfig
fs/ubifs/debug.c
fs/ubifs/super.c
fs/xfs/xfs_log_recover.c
include/asm-generic/bug.h
include/asm-generic/mman-common.h
include/linux/atmel-mci.h
include/linux/cs5535.h [new file with mode: 0644]
include/linux/ctype.h
include/linux/dynamic_debug.h
include/linux/efi.h
include/linux/err.h
include/linux/hugetlb.h
include/linux/i2c.h
include/linux/init_task.h
include/linux/kallsyms.h
include/linux/kernel.h
include/linux/ksm.h
include/linux/lis3lv02d.h
include/linux/memory_hotplug.h
include/linux/mempolicy.h
include/linux/migrate.h
include/linux/mm.h
include/linux/node.h
include/linux/nodemask.h
include/linux/numa.h
include/linux/page-flags.h
include/linux/pm.h
include/linux/rmap.h
include/linux/rwsem-spinlock.h
include/linux/sched.h
include/linux/string.h
include/linux/swap.h
include/linux/tty.h
include/linux/vmstat.h
include/linux/vt.h
init/Kconfig
init/main.c
kernel/acct.c
kernel/params.c
kernel/power/console.c
kernel/smp.c
kernel/sys.c
kernel/sysctl.c
lib/Kconfig.debug
lib/argv_split.c
lib/crc32.c
lib/ctype.c
lib/dynamic_debug.c
lib/parser.c
lib/rwsem-spinlock.c
lib/string.c
lib/vsprintf.c
mm/Kconfig
mm/bootmem.c
mm/hugetlb.c
mm/internal.h
mm/ksm.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/memory_hotplug.c
mm/mempolicy.c
mm/migrate.c
mm/mincore.c
mm/mlock.c
mm/mmap.c
mm/nommu.c
mm/oom_kill.c
mm/page_alloc.c
mm/page_io.c
mm/pagewalk.c
mm/rmap.c
mm/shmem.c
mm/swapfile.c
mm/vmalloc.c
mm/vmscan.c
mm/vmstat.c
net/irda/irnet/irnet.h
net/irda/irnet/irnet_ppp.c
net/iucv/af_iucv.c
net/iucv/iucv.c
net/netfilter/xt_recent.c
scripts/get_maintainer.pl
sound/arm/pxa2xx-ac97.c
sound/pci/cs5535audio/Makefile
sound/pci/cs5535audio/cs5535audio.c
sound/pci/cs5535audio/cs5535audio.h
sound/pci/cs5535audio/cs5535audio_olpc.c
sound/pci/hda/hda_hwdep.c
sound/soc/s3c24xx/s3c24xx_simtec.c
sound/soc/s3c24xx/s3c24xx_simtec.h
sound/soc/soc-core.c

index 9fe91c02ee40e4fb4bca0b1e7fb010f14d5554e3..bf1627b02a0357a1f38362ac61ab741f274d6f10 100644 (file)
@@ -60,6 +60,19 @@ Description:
 Users:         hotplug memory remove tools
                https://w3.opensource.ibm.com/projects/powerpc-utils/
 
+
+What:          /sys/devices/system/memoryX/nodeY
+Date:          October 2009
+Contact:       Linux Memory Management list <linux-mm@kvack.org>
+Description:
+               When CONFIG_NUMA is enabled, a symbolic link that
+               points to the corresponding NUMA node directory.
+
+               For example, the following symbolic link is created for
+               memory section 9 on node0:
+               /sys/devices/system/memory/memory9/node0 -> ../../node/node0
+
+
 What:          /sys/devices/system/node/nodeX/memoryY
 Date:          September 2008
 Contact:       Gary Hade <garyhade@us.ibm.com>
@@ -70,4 +83,3 @@ Description:
                memory section directory.  For example, the following symbolic
                link is created for memory section 9 on node0.
                /sys/devices/system/node/node0/memory9 -> ../../memory/memory9
-
index 2aae06fcbed7ae8d57489d89c5fea158beb37748..84a710f87c64b17b2eab79a3a2b860c7a9c97319 100644 (file)
@@ -92,6 +92,20 @@ Description: Discover NUMA node a CPU belongs to
                /sys/devices/system/cpu/cpu42/node2 -> ../../node/node2
 
 
+What:          /sys/devices/system/cpu/cpu#/node
+Date:          October 2009
+Contact:       Linux memory management mailing list <linux-mm@kvack.org>
+Description:   Discover NUMA node a CPU belongs to
+
+               When CONFIG_NUMA is enabled, a symbolic link that points
+               to the corresponding NUMA node directory.
+
+               For example, the following symlink is created for cpu42
+               in NUMA node 2:
+
+               /sys/devices/system/cpu/cpu42/node2 -> ../../node/node2
+
+
 What:          /sys/devices/system/cpu/cpu#/topology/core_id
                /sys/devices/system/cpu/cpu#/topology/core_siblings
                /sys/devices/system/cpu/cpu#/topology/core_siblings_list
index eb2c138c277c45ec66b25116cbd744d42c41878f..21ab9357326d77ce3b007d5f5c34c92f0e41c174 100644 (file)
@@ -291,15 +291,6 @@ Who:       Michael Buesch <mb@bu3sch.de>
 
 ---------------------------
 
-What: print_fn_descriptor_symbol()
-When: October 2009
-Why:  The %pF vsprintf format provides the same functionality in a
-      simpler way.  print_fn_descriptor_symbol() is deprecated but
-      still present to give out-of-tree modules time to change.
-Who:  Bjorn Helgaas <bjorn.helgaas@hp.com>
-
----------------------------
-
 What:  /sys/o2cb symlink
 When:  January 2010
 Why:   /sys/fs/o2cb is the proper location for this information - /sys/o2cb
index 94b9f2056f4cf97fcddcad5c79f1130a95abe6b9..220cc6376ef80e0c9bcfec162d45552e729cdf5a 100644 (file)
@@ -38,6 +38,7 @@ Table of Contents
   3.3  /proc/<pid>/io - Display the IO accounting fields
   3.4  /proc/<pid>/coredump_filter - Core dump filtering settings
   3.5  /proc/<pid>/mountinfo - Information about mounts
+  3.6  /proc/<pid>/comm  & /proc/<pid>/task/<tid>/comm
 
 
 ------------------------------------------------------------------------------
@@ -1409,3 +1410,11 @@ For more information on mount propagation see:
 
   Documentation/filesystems/sharedsubtree.txt
 
+
+3.6    /proc/<pid>/comm  & /proc/<pid>/task/<tid>/comm
+--------------------------------------------------------
+These files provide a method to access a tasks comm value. It also allows for
+a task to set its own or one of its thread siblings comm value. The comm value
+is limited in size compared to the cmdline value, so writing anything longer
+then the kernel's TASK_COMM_LEN (currently 16 chars) will result in a truncated
+comm value.
index effe949a7282cccd600365bffe78dd70541b9aa3..06534f25e643e15be91bb9eda118898a3241e5ed 100644 (file)
@@ -3,7 +3,8 @@ Kernel driver lis3lv02d
 
 Supported chips:
 
-  * STMicroelectronics LIS3LV02DL and LIS3LV02DQ
+  * STMicroelectronics LIS3LV02DL, LIS3LV02DQ (12 bits precision)
+  * STMicroelectronics LIS302DL, LIS3L02DQ, LIS331DL (8 bits)
 
 Authors:
         Yan Burman <burman.yan@gmail.com>
@@ -13,32 +14,52 @@ Authors:
 Description
 -----------
 
-This driver provides support for the accelerometer found in various HP
-laptops sporting the feature officially called "HP Mobile Data
-Protection System 3D" or "HP 3D DriveGuard". It detects automatically
-laptops with this sensor. Known models (for now the HP 2133, nc6420,
-nc2510, nc8510, nc84x0, nw9440 and nx9420) will have their axis
-automatically oriented on standard way (eg: you can directly play
-neverball).  The accelerometer data is readable via
-/sys/devices/platform/lis3lv02d.
+This driver provides support for the accelerometer found in various HP laptops
+sporting the feature officially called "HP Mobile Data Protection System 3D" or
+"HP 3D DriveGuard". It detects automatically laptops with this sensor. Known
+models (full list can be found in drivers/hwmon/hp_accel.c) will have their
+axis automatically oriented on standard way (eg: you can directly play
+neverball). The accelerometer data is readable via
+/sys/devices/platform/lis3lv02d. Reported values are scaled
+to mg values (1/1000th of earth gravity).
 
 Sysfs attributes under /sys/devices/platform/lis3lv02d/:
 position - 3D position that the accelerometer reports. Format: "(x,y,z)"
-calibrate - read: values (x, y, z) that are used as the base for input
-                 class device operation.
-            write: forces the base to be recalibrated with the current
-                  position.
-rate - reports the sampling rate of the accelerometer device in HZ
+rate - read reports the sampling rate of the accelerometer device in HZ.
+       write changes sampling rate of the accelerometer device.
+       Only values which are supported by HW are accepted.
+selftest - performs selftest for the chip as specified by chip manufacturer.
 
 This driver also provides an absolute input class device, allowing
-the laptop to act as a pinball machine-esque joystick.
+the laptop to act as a pinball machine-esque joystick. Joystick device can be
+calibrated. Joystick device can be in two different modes.
+By default output values are scaled between -32768 .. 32767. In joystick raw
+mode, joystick and sysfs position entry have the same scale. There can be
+small difference due to input system fuzziness feature.
+Events are also available as input event device.
+
+Selftest is meant only for hardware diagnostic purposes. It is not meant to be
+used during normal operations. Position data is not corrupted during selftest
+but interrupt behaviour is not guaranteed to work reliably. In test mode, the
+sensing element is internally moved little bit. Selftest measures difference
+between normal mode and test mode. Chip specifications tell the acceptance
+limit for each type of the chip. Limits are provided via platform data
+to allow adjustment of the limits without a change to the actual driver.
+Seltest returns either "OK x y z" or "FAIL x y z" where x, y and z are
+measured difference between modes. Axes are not remapped in selftest mode.
+Measurement values are provided to help HW diagnostic applications to make
+final decision.
+
+On HP laptops, if the led infrastructure is activated, support for a led
+indicating disk protection will be provided as /sys/class/leds/hp::hddprotect.
 
 Another feature of the driver is misc device called "freefall" that
 acts similar to /dev/rtc and reacts on free-fall interrupts received
 from the device. It supports blocking operations, poll/select and
 fasync operation modes. You must read 1 bytes from the device.  The
 result is number of free-fall interrupts since the last successful
-read (or 255 if number of interrupts would not fit).
+read (or 255 if number of interrupts would not fit). See the hpfall.c
+file for an example on using the device.
 
 
 Axes orientation
@@ -55,7 +76,7 @@ the accelerometer are converted into a "standard" organisation of the axes
  * If the laptop is put upside-down, Z becomes negative
 
 If your laptop model is not recognized (cf "dmesg"), you can send an
-email to the authors to add it to the database.  When reporting a new
+email to the maintainer to add it to the database.  When reporting a new
 laptop, please include the output of "dmidecode" plus the value of
 /sys/devices/platform/lis3lv02d/position in these four cases.
 
index 02b74899edaf42e5c1a266399aeb51c890fc5c5c..b7e42ec4b26ba4465691710ba4071ebebb4f61b6 100644 (file)
@@ -81,8 +81,14 @@ pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
           0 (stop) to 255 (full)
 
 pwm[1-4]_enable - this file controls mode of fan/temperature control:
-       * 1 Manual Mode, write to pwm file any value 0-255 (full speed)
-       * 2 Thermal Cruise
+       * 1 Manual mode, write to pwm file any value 0-255 (full speed)
+       * 2 "Thermal Cruise" mode
+       * 3 "Fan Speed Cruise" mode
+       * 4 "Smart Fan III" mode
+
+pwm[1-4]_mode - controls if output is PWM or DC level
+        * 0 DC output (0 - 12v)
+        * 1 PWM output
 
 Thermal Cruise mode
 -------------------
index 7860aafb483dfba05aea315fce424ff6b6d936cb..0a74603eb671c84590b0a91976cc2ea3e0150728 100644 (file)
@@ -44,7 +44,7 @@ static struct i2c_driver foo_driver = {
        /* if device autodetection is needed: */
        .class          = I2C_CLASS_SOMETHING,
        .detect         = foo_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 
        .shutdown       = foo_shutdown, /* optional */
        .suspend        = foo_suspend,  /* optional */
index bbc8a6a3692169a53aeb9ac1ab271018a53531d0..57e7e9cc1870ad095f7daa21ff051a68ec34d12c 100644 (file)
@@ -160,12 +160,15 @@ Under each section, you can see 4 files.
 NOTE:
   These directories/files appear after physical memory hotplug phase.
 
-If CONFIG_NUMA is enabled the
-/sys/devices/system/memory/memoryXXX memory section
-directories can also be accessed via symbolic links located in
-the /sys/devices/system/node/node* directories.  For example:
+If CONFIG_NUMA is enabled the memoryXXX/ directories can also be accessed
+via symbolic links located in the /sys/devices/system/node/node* directories.
+
+For example:
 /sys/devices/system/node/node0/memory9 -> ../../memory/memory9
 
+A backlink will also be created:
+/sys/devices/system/memory/memory9/node0 -> ../../node/node0
+
 --------------------------------
 4. Physical memory hot-add phase
 --------------------------------
diff --git a/Documentation/misc-devices/ad525x_dpot.txt b/Documentation/misc-devices/ad525x_dpot.txt
new file mode 100644 (file)
index 0000000..0c9413b
--- /dev/null
@@ -0,0 +1,57 @@
+---------------------------------
+  AD525x Digital Potentiometers
+---------------------------------
+
+The ad525x_dpot driver exports a simple sysfs interface.  This allows you to
+work with the immediate resistance settings as well as update the saved startup
+settings.  Access to the factory programmed tolerance is also provided, but
+interpretation of this settings is required by the end application according to
+the specific part in use.
+
+---------
+  Files
+---------
+
+Each dpot device will have a set of eeprom, rdac, and tolerance files.  How
+many depends on the actual part you have, as will the range of allowed values.
+
+The eeprom files are used to program the startup value of the device.
+
+The rdac files are used to program the immediate value of the device.
+
+The tolerance files are the read-only factory programmed tolerance settings
+and may vary greatly on a part-by-part basis.  For exact interpretation of
+this field, please consult the datasheet for your part.  This is presented
+as a hex file for easier parsing.
+
+-----------
+  Example
+-----------
+
+Locate the device in your sysfs tree.  This is probably easiest by going into
+the common i2c directory and locating the device by the i2c slave address.
+
+       # ls /sys/bus/i2c/devices/
+       0-0022  0-0027  0-002f
+
+So assuming the device in question is on the first i2c bus and has the slave
+address of 0x2f, we descend (unrelated sysfs entries have been trimmed).
+
+       # ls /sys/bus/i2c/devices/0-002f/
+       eeprom0 rdac0 tolerance0
+
+You can use simple reads/writes to access these files:
+
+       # cd /sys/bus/i2c/devices/0-002f/
+
+       # cat eeprom0
+       0
+       # echo 10 > eeprom0
+       # cat eeprom0
+       10
+
+       # cat rdac0
+       5
+       # echo 3 > rdac0
+       # cat rdac0
+       3
index b565e8279d133f969fbd6cc2642592c3d44058ed..8e1ddec2c78a2b9b9f4e73c745b9ae28e4b963b3 100644 (file)
@@ -119,6 +119,32 @@ FURTHER NOTES ON NO-MMU MMAP
      granule but will only discard the excess if appropriately configured as
      this has an effect on fragmentation.
 
+ (*) The memory allocated by a request for an anonymous mapping will normally
+     be cleared by the kernel before being returned in accordance with the
+     Linux man pages (ver 2.22 or later).
+
+     In the MMU case this can be achieved with reasonable performance as
+     regions are backed by virtual pages, with the contents only being mapped
+     to cleared physical pages when a write happens on that specific page
+     (prior to which, the pages are effectively mapped to the global zero page
+     from which reads can take place).  This spreads out the time it takes to
+     initialize the contents of a page - depending on the write-usage of the
+     mapping.
+
+     In the no-MMU case, however, anonymous mappings are backed by physical
+     pages, and the entire map is cleared at allocation time.  This can cause
+     significant delays during a userspace malloc() as the C library does an
+     anonymous mapping and the kernel then does a memset for the entire map.
+
+     However, for memory that isn't required to be precleared - such as that
+     returned by malloc() - mmap() can take a MAP_UNINITIALIZED flag to
+     indicate to the kernel that it shouldn't bother clearing the memory before
+     returning it.  Note that CONFIG_MMAP_ALLOW_UNINITIALIZED must be enabled
+     to permit this, otherwise the flag will be ignored.
+
+     uClibc uses this to speed up malloc(), and the ELF-FDPIC binfmt uses this
+     to allocate the brk and stack region.
+
  (*) A list of all the private copy and anonymous mappings on the system is
      visible through /proc/maps in no-MMU mode.
 
index 82a7bd1800b2fba2a19aca1a69ac9c0a1274852c..bc31636973e312f27545bcdb38e2936a78e9fe44 100644 (file)
@@ -11,23 +11,21 @@ This optimization is more critical now as bigger and bigger physical memories
 (several GBs) are more readily available.
 
 Users can use the huge page support in Linux kernel by either using the mmap
-system call or standard SYSv shared memory system calls (shmget, shmat).
+system call or standard SYSV shared memory system calls (shmget, shmat).
 
 First the Linux kernel needs to be built with the CONFIG_HUGETLBFS
 (present under "File systems") and CONFIG_HUGETLB_PAGE (selected
 automatically when CONFIG_HUGETLBFS is selected) configuration
 options.
 
-The kernel built with huge page support should show the number of configured
-huge pages in the system by running the "cat /proc/meminfo" command.
+The /proc/meminfo file provides information about the total number of
+persistent hugetlb pages in the kernel's huge page pool.  It also displays
+information about the number of free, reserved and surplus huge pages and the
+default huge page size.  The huge page size is needed for generating the
+proper alignment and size of the arguments to system calls that map huge page
+regions.
 
-/proc/meminfo also provides information about the total number of hugetlb
-pages configured in the kernel.  It also displays information about the
-number of free hugetlb pages at any time.  It also displays information about
-the configured huge page size - this is needed for generating the proper
-alignment and size of the arguments to the above system calls.
-
-The output of "cat /proc/meminfo" will have lines like:
+The output of "cat /proc/meminfo" will include lines like:
 
 .....
 HugePages_Total: vvv
@@ -53,59 +51,63 @@ HugePages_Surp  is short for "surplus," and is the number of huge pages in
 /proc/filesystems should also show a filesystem of type "hugetlbfs" configured
 in the kernel.
 
-/proc/sys/vm/nr_hugepages indicates the current number of configured hugetlb
-pages in the kernel.  Super user can dynamically request more (or free some
-pre-configured) huge pages.
-The allocation (or deallocation) of hugetlb pages is possible only if there are
-enough physically contiguous free pages in system (freeing of huge pages is
-possible only if there are enough hugetlb pages free that can be transferred
-back to regular memory pool).
+/proc/sys/vm/nr_hugepages indicates the current number of "persistent" huge
+pages in the kernel's huge page pool.  "Persistent" huge pages will be
+returned to the huge page pool when freed by a task.  A user with root
+privileges can dynamically allocate more or free some persistent huge pages
+by increasing or decreasing the value of 'nr_hugepages'.
 
-Pages that are used as hugetlb pages are reserved inside the kernel and cannot
-be used for other purposes.
+Pages that are used as huge pages are reserved inside the kernel and cannot
+be used for other purposes.  Huge pages cannot be swapped out under
+memory pressure.
 
-Once the kernel with Hugetlb page support is built and running, a user can
-use either the mmap system call or shared memory system calls to start using
-the huge pages.  It is required that the system administrator preallocate
-enough memory for huge page purposes.
+Once a number of huge pages have been pre-allocated to the kernel huge page
+pool, a user with appropriate privilege can use either the mmap system call
+or shared memory system calls to use the huge pages.  See the discussion of
+Using Huge Pages, below.
 
-The administrator can preallocate huge pages on the kernel boot command line by
-specifying the "hugepages=N" parameter, where 'N' = the number of huge pages
-requested.  This is the most reliable method for preallocating huge pages as
-memory has not yet become fragmented.
+The administrator can allocate persistent huge pages on the kernel boot
+command line by specifying the "hugepages=N" parameter, where 'N' = the
+number of huge pages requested.  This is the most reliable method of
+allocating huge pages as memory has not yet become fragmented.
 
-Some platforms support multiple huge page sizes.  To preallocate huge pages
+Some platforms support multiple huge page sizes.  To allocate huge pages
 of a specific size, one must preceed the huge pages boot command parameters
 with a huge page size selection parameter "hugepagesz=<size>".  <size> must
 be specified in bytes with optional scale suffix [kKmMgG].  The default huge
 page size may be selected with the "default_hugepagesz=<size>" boot parameter.
 
-/proc/sys/vm/nr_hugepages indicates the current number of configured [default
-size] hugetlb pages in the kernel.  Super user can dynamically request more
-(or free some pre-configured) huge pages.
-
-Use the following command to dynamically allocate/deallocate default sized
-huge pages:
+When multiple huge page sizes are supported, /proc/sys/vm/nr_hugepages
+indicates the current number of pre-allocated huge pages of the default size.
+Thus, one can use the following command to dynamically allocate/deallocate
+default sized persistent huge pages:
 
        echo 20 > /proc/sys/vm/nr_hugepages
 
-This command will try to configure 20 default sized huge pages in the system.
+This command will try to adjust the number of default sized huge pages in the
+huge page pool to 20, allocating or freeing huge pages, as required.
+
 On a NUMA platform, the kernel will attempt to distribute the huge page pool
-over the all on-line nodes.  These huge pages, allocated when nr_hugepages
-is increased, are called "persistent huge pages".
+over all the set of allowed nodes specified by the NUMA memory policy of the
+task that modifies nr_hugepages.  The default for the allowed nodes--when the
+task has default memory policy--is all on-line nodes with memory.  Allowed
+nodes with insufficient available, contiguous memory for a huge page will be
+silently skipped when allocating persistent huge pages.  See the discussion
+below of the interaction of task memory policy, cpusets and per node attributes
+with the allocation and freeing of persistent huge pages.
 
 The success or failure of huge page allocation depends on the amount of
-physically contiguous memory that is preset in system at the time of the
+physically contiguous memory that is present in system at the time of the
 allocation attempt.  If the kernel is unable to allocate huge pages from
 some nodes in a NUMA system, it will attempt to make up the difference by
 allocating extra pages on other nodes with sufficient available contiguous
 memory, if any.
 
-System administrators may want to put this command in one of the local rc init
-files.  This will enable the kernel to request huge pages early in the boot
-process when the possibility of getting physical contiguous pages is still
-very high.  Administrators can verify the number of huge pages actually
-allocated by checking the sysctl or meminfo.  To check the per node
+System administrators may want to put this command in one of the local rc
+init files.  This will enable the kernel to allocate huge pages early in
+the boot process when the possibility of getting physical contiguous pages
+is still very high.  Administrators can verify the number of huge pages
+actually allocated by checking the sysctl or meminfo.  To check the per node
 distribution of huge pages in a NUMA system, use:
 
        cat /sys/devices/system/node/node*/meminfo | fgrep Huge
@@ -113,45 +115,47 @@ distribution of huge pages in a NUMA system, use:
 /proc/sys/vm/nr_overcommit_hugepages specifies how large the pool of
 huge pages can grow, if more huge pages than /proc/sys/vm/nr_hugepages are
 requested by applications.  Writing any non-zero value into this file
-indicates that the hugetlb subsystem is allowed to try to obtain "surplus"
-huge pages from the buddy allocator, when the normal pool is exhausted. As
-these surplus huge pages go out of use, they are freed back to the buddy
-allocator.
+indicates that the hugetlb subsystem is allowed to try to obtain that
+number of "surplus" huge pages from the kernel's normal page pool, when the
+persistent huge page pool is exhausted. As these surplus huge pages become
+unused, they are freed back to the kernel's normal page pool.
 
-When increasing the huge page pool size via nr_hugepages, any surplus
+When increasing the huge page pool size via nr_hugepages, any existing surplus
 pages will first be promoted to persistent huge pages.  Then, additional
 huge pages will be allocated, if necessary and if possible, to fulfill
-the new huge page pool size.
+the new persistent huge page pool size.
 
-The administrator may shrink the pool of preallocated huge pages for
+The administrator may shrink the pool of persistent huge pages for
 the default huge page size by setting the nr_hugepages sysctl to a
 smaller value.  The kernel will attempt to balance the freeing of huge pages
-across all on-line nodes.  Any free huge pages on the selected nodes will
-be freed back to the buddy allocator.
-
-Caveat: Shrinking the pool via nr_hugepages such that it becomes less
-than the number of huge pages in use will convert the balance to surplus
-huge pages even if it would exceed the overcommit value.  As long as
-this condition holds, however, no more surplus huge pages will be
-allowed on the system until one of the two sysctls are increased
-sufficiently, or the surplus huge pages go out of use and are freed.
+across all nodes in the memory policy of the task modifying nr_hugepages.
+Any free huge pages on the selected nodes will be freed back to the kernel's
+normal page pool.
+
+Caveat: Shrinking the persistent huge page pool via nr_hugepages such that
+it becomes less than the number of huge pages in use will convert the balance
+of the in-use huge pages to surplus huge pages.  This will occur even if
+the number of surplus pages it would exceed the overcommit value.  As long as
+this condition holds--that is, until nr_hugepages+nr_overcommit_hugepages is
+increased sufficiently, or the surplus huge pages go out of use and are freed--
+no more surplus huge pages will be allowed to be allocated.
 
 With support for multiple huge page pools at run-time available, much of
-the huge page userspace interface has been duplicated in sysfs. The above
-information applies to the default huge page size which will be
-controlled by the /proc interfaces for backwards compatibility. The root
-huge page control directory in sysfs is:
+the huge page userspace interface in /proc/sys/vm has been duplicated in sysfs.
+The /proc interfaces discussed above have been retained for backwards
+compatibility. The root huge page control directory in sysfs is:
 
        /sys/kernel/mm/hugepages
 
 For each huge page size supported by the running kernel, a subdirectory
-will exist, of the form
+will exist, of the form:
 
        hugepages-${size}kB
 
 Inside each of these directories, the same set of files will exist:
 
        nr_hugepages
+       nr_hugepages_mempolicy
        nr_overcommit_hugepages
        free_hugepages
        resv_hugepages
@@ -159,6 +163,102 @@ Inside each of these directories, the same set of files will exist:
 
 which function as described above for the default huge page-sized case.
 
+
+Interaction of Task Memory Policy with Huge Page Allocation/Freeing
+
+Whether huge pages are allocated and freed via the /proc interface or
+the /sysfs interface using the nr_hugepages_mempolicy attribute, the NUMA
+nodes from which huge pages are allocated or freed are controlled by the
+NUMA memory policy of the task that modifies the nr_hugepages_mempolicy
+sysctl or attribute.  When the nr_hugepages attribute is used, mempolicy
+is ignored.
+
+The recommended method to allocate or free huge pages to/from the kernel
+huge page pool, using the nr_hugepages example above, is:
+
+    numactl --interleave <node-list> echo 20 \
+                               >/proc/sys/vm/nr_hugepages_mempolicy
+
+or, more succinctly:
+
+    numactl -m <node-list> echo 20 >/proc/sys/vm/nr_hugepages_mempolicy
+
+This will allocate or free abs(20 - nr_hugepages) to or from the nodes
+specified in <node-list>, depending on whether number of persistent huge pages
+is initially less than or greater than 20, respectively.  No huge pages will be
+allocated nor freed on any node not included in the specified <node-list>.
+
+When adjusting the persistent hugepage count via nr_hugepages_mempolicy, any
+memory policy mode--bind, preferred, local or interleave--may be used.  The
+resulting effect on persistent huge page allocation is as follows:
+
+1) Regardless of mempolicy mode [see Documentation/vm/numa_memory_policy.txt],
+   persistent huge pages will be distributed across the node or nodes
+   specified in the mempolicy as if "interleave" had been specified.
+   However, if a node in the policy does not contain sufficient contiguous
+   memory for a huge page, the allocation will not "fallback" to the nearest
+   neighbor node with sufficient contiguous memory.  To do this would cause
+   undesirable imbalance in the distribution of the huge page pool, or
+   possibly, allocation of persistent huge pages on nodes not allowed by
+   the task's memory policy.
+
+2) One or more nodes may be specified with the bind or interleave policy.
+   If more than one node is specified with the preferred policy, only the
+   lowest numeric id will be used.  Local policy will select the node where
+   the task is running at the time the nodes_allowed mask is constructed.
+   For local policy to be deterministic, the task must be bound to a cpu or
+   cpus in a single node.  Otherwise, the task could be migrated to some
+   other node at any time after launch and the resulting node will be
+   indeterminate.  Thus, local policy is not very useful for this purpose.
+   Any of the other mempolicy modes may be used to specify a single node.
+
+3) The nodes allowed mask will be derived from any non-default task mempolicy,
+   whether this policy was set explicitly by the task itself or one of its
+   ancestors, such as numactl.  This means that if the task is invoked from a
+   shell with non-default policy, that policy will be used.  One can specify a
+   node list of "all" with numactl --interleave or --membind [-m] to achieve
+   interleaving over all nodes in the system or cpuset.
+
+4) Any task mempolicy specifed--e.g., using numactl--will be constrained by
+   the resource limits of any cpuset in which the task runs.  Thus, there will
+   be no way for a task with non-default policy running in a cpuset with a
+   subset of the system nodes to allocate huge pages outside the cpuset
+   without first moving to a cpuset that contains all of the desired nodes.
+
+5) Boot-time huge page allocation attempts to distribute the requested number
+   of huge pages over all on-lines nodes with memory.
+
+Per Node Hugepages Attributes
+
+A subset of the contents of the root huge page control directory in sysfs,
+described above, will be replicated under each the system device of each
+NUMA node with memory in:
+
+       /sys/devices/system/node/node[0-9]*/hugepages/
+
+Under this directory, the subdirectory for each supported huge page size
+contains the following attribute files:
+
+       nr_hugepages
+       free_hugepages
+       surplus_hugepages
+
+The free_' and surplus_' attribute files are read-only.  They return the number
+of free and surplus [overcommitted] huge pages, respectively, on the parent
+node.
+
+The nr_hugepages attribute returns the total number of huge pages on the
+specified node.  When this attribute is written, the number of persistent huge
+pages on the parent node will be adjusted to the specified value, if sufficient
+resources exist, regardless of the task's mempolicy or cpuset constraints.
+
+Note that the number of overcommit and reserve pages remain global quantities,
+as we don't know until fault time, when the faulting task's mempolicy is
+applied, from which node the huge page allocation will be attempted.
+
+
+Using Huge Pages
+
 If the user applications are going to request huge pages using mmap system
 call, then it is required that system administrator mount a file system of
 type hugetlbfs:
@@ -206,9 +306,11 @@ map_hugetlb.c.
  * requesting huge pages.
  *
  * For the ia64 architecture, the Linux kernel reserves Region number 4 for
- * huge pages.  That means the addresses starting with 0x800000... will need
- * to be specified.  Specifying a fixed address is not required on ppc64,
- * i386 or x86_64.
+ * huge pages.  That means that if one requires a fixed address, a huge page
+ * aligned address starting with 0x800000... will be required.  If a fixed
+ * address is not required, the kernel will select an address in the proper
+ * range.
+ * Other architectures, such as ppc64, i386 or x86_64 are not so constrained.
  *
  * Note: The default shared memory limit is quite low on many kernels,
  * you may need to increase it via:
@@ -237,14 +339,8 @@ map_hugetlb.c.
 
 #define dprintf(x)  printf(x)
 
-/* Only ia64 requires this */
-#ifdef __ia64__
-#define ADDR (void *)(0x8000000000000000UL)
-#define SHMAT_FLAGS (SHM_RND)
-#else
-#define ADDR (void *)(0x0UL)
+#define ADDR (void *)(0x0UL)   /* let kernel choose address */
 #define SHMAT_FLAGS (0)
-#endif
 
 int main(void)
 {
@@ -302,10 +398,12 @@ int main(void)
  * example, the app is requesting memory of size 256MB that is backed by
  * huge pages.
  *
- * For ia64 architecture, Linux kernel reserves Region number 4 for huge pages.
- * That means the addresses starting with 0x800000... will need to be
- * specified.  Specifying a fixed address is not required on ppc64, i386
- * or x86_64.
+ * For the ia64 architecture, the Linux kernel reserves Region number 4 for
+ * huge pages.  That means that if one requires a fixed address, a huge page
+ * aligned address starting with 0x800000... will be required.  If a fixed
+ * address is not required, the kernel will select an address in the proper
+ * range.
+ * Other architectures, such as ppc64, i386 or x86_64 are not so constrained.
  */
 #include <stdlib.h>
 #include <stdio.h>
@@ -317,14 +415,8 @@ int main(void)
 #define LENGTH (256UL*1024*1024)
 #define PROTECTION (PROT_READ | PROT_WRITE)
 
-/* Only ia64 requires this */
-#ifdef __ia64__
-#define ADDR (void *)(0x8000000000000000UL)
-#define FLAGS (MAP_SHARED | MAP_FIXED)
-#else
-#define ADDR (void *)(0x0UL)
+#define ADDR (void *)(0x0UL)   /* let kernel choose address */
 #define FLAGS (MAP_SHARED)
-#endif
 
 void check_bytes(char *addr)
 {
index 262d8e6793a3e008c36bb39905f49634f9e570ae..b392e496f816ac6c77fccd9c0c367de4efa57034 100644 (file)
@@ -16,9 +16,9 @@ by sharing the data common between them.  But it can be useful to any
 application which generates many instances of the same data.
 
 KSM only merges anonymous (private) pages, never pagecache (file) pages.
-KSM's merged pages are at present locked into kernel memory for as long
-as they are shared: so cannot be swapped out like the user pages they
-replace (but swapping KSM pages should follow soon in a later release).
+KSM's merged pages were originally locked into kernel memory, but can now
+be swapped out just like other user pages (but sharing is broken when they
+are swapped back in: ksmd must rediscover their identity and merge again).
 
 KSM only operates on those areas of address space which an application
 has advised to be likely candidates for merging, by using the madvise(2)
@@ -44,20 +44,12 @@ includes unmapped gaps (though working on the intervening mapped areas),
 and might fail with EAGAIN if not enough memory for internal structures.
 
 Applications should be considerate in their use of MADV_MERGEABLE,
-restricting its use to areas likely to benefit.  KSM's scans may use
-a lot of processing power, and its kernel-resident pages are a limited
-resource.  Some installations will disable KSM for these reasons.
+restricting its use to areas likely to benefit.  KSM's scans may use a lot
+of processing power: some installations will disable KSM for that reason.
 
 The KSM daemon is controlled by sysfs files in /sys/kernel/mm/ksm/,
 readable by all but writable only by root:
 
-max_kernel_pages - set to maximum number of kernel pages that KSM may use
-                   e.g. "echo 100000 > /sys/kernel/mm/ksm/max_kernel_pages"
-                   Value 0 imposes no limit on the kernel pages KSM may use;
-                   but note that any process using MADV_MERGEABLE can cause
-                   KSM to allocate these pages, unswappable until it exits.
-                   Default: quarter of memory (chosen to not pin too much)
-
 pages_to_scan    - how many present pages to scan before ksmd goes to sleep
                    e.g. "echo 100 > /sys/kernel/mm/ksm/pages_to_scan"
                    Default: 100 (chosen for demonstration purposes)
@@ -75,7 +67,7 @@ run              - set 0 to stop ksmd from running but keep merged pages,
 
 The effectiveness of KSM and MADV_MERGEABLE is shown in /sys/kernel/mm/ksm/:
 
-pages_shared     - how many shared unswappable kernel pages KSM is using
+pages_shared     - how many shared pages are being used
 pages_sharing    - how many more sites are sharing them i.e. how much saved
 pages_unshared   - how many pages unique but repeatedly checked for merging
 pages_volatile   - how many pages changing too fast to be placed in a tree
@@ -87,4 +79,4 @@ pages_volatile embraces several different kinds of activity, but a high
 proportion there would also indicate poor use of madvise MADV_MERGEABLE.
 
 Izik Eidus,
-Hugh Dickins, 24 Sept 2009
+Hugh Dickins, 17 Nov 2009
index ea44ea502da1ef60e2a04b3ab9e42cf643ac7711..7a7d9bab32efe0f09f4ccde04f9fb1defecbfe59 100644 (file)
 #define BIT(name)              (1ULL << KPF_##name)
 #define BITS_COMPOUND          (BIT(COMPOUND_HEAD) | BIT(COMPOUND_TAIL))
 
-static char *page_flag_names[] = {
+static const char *page_flag_names[] = {
        [KPF_LOCKED]            = "L:locked",
        [KPF_ERROR]             = "E:error",
        [KPF_REFERENCED]        = "R:referenced",
@@ -173,7 +173,7 @@ static int          kpageflags_fd;
 static int             opt_hwpoison;
 static int             opt_unpoison;
 
-static char            *hwpoison_debug_fs = "/debug/hwpoison";
+static const char      hwpoison_debug_fs[] = "/debug/hwpoison";
 static int             hwpoison_inject_fd;
 static int             hwpoison_forget_fd;
 
@@ -560,7 +560,7 @@ static void walk_pfn(unsigned long voffset,
 {
        uint64_t buf[KPAGEFLAGS_BATCH];
        unsigned long batch;
-       unsigned long pages;
+       long pages;
        unsigned long i;
 
        while (count) {
@@ -673,30 +673,35 @@ static void usage(void)
 
        printf(
 "page-types [options]\n"
-"            -r|--raw                  Raw mode, for kernel developers\n"
-"            -a|--addr    addr-spec    Walk a range of pages\n"
-"            -b|--bits    bits-spec    Walk pages with specified bits\n"
-"            -p|--pid     pid          Walk process address space\n"
+"            -r|--raw                   Raw mode, for kernel developers\n"
+"            -d|--describe flags        Describe flags\n"
+"            -a|--addr    addr-spec     Walk a range of pages\n"
+"            -b|--bits    bits-spec     Walk pages with specified bits\n"
+"            -p|--pid     pid           Walk process address space\n"
 #if 0 /* planned features */
-"            -f|--file    filename     Walk file address space\n"
+"            -f|--file    filename      Walk file address space\n"
 #endif
-"            -l|--list                 Show page details in ranges\n"
-"            -L|--list-each            Show page details one by one\n"
-"            -N|--no-summary           Don't show summay info\n"
-"            -X|--hwpoison             hwpoison pages\n"
-"            -x|--unpoison             unpoison pages\n"
-"            -h|--help                 Show this usage message\n"
+"            -l|--list                  Show page details in ranges\n"
+"            -L|--list-each             Show page details one by one\n"
+"            -N|--no-summary            Don't show summay info\n"
+"            -X|--hwpoison              hwpoison pages\n"
+"            -x|--unpoison              unpoison pages\n"
+"            -h|--help                  Show this usage message\n"
+"flags:\n"
+"            0x10                       bitfield format, e.g.\n"
+"            anon                       bit-name, e.g.\n"
+"            0x10,anon                  comma-separated list, e.g.\n"
 "addr-spec:\n"
-"            N                         one page at offset N (unit: pages)\n"
-"            N+M                       pages range from N to N+M-1\n"
-"            N,M                       pages range from N to M-1\n"
-"            N,                        pages range from N to end\n"
-"            ,M                        pages range from 0 to M-1\n"
+"            N                          one page at offset N (unit: pages)\n"
+"            N+M                        pages range from N to N+M-1\n"
+"            N,M                        pages range from N to M-1\n"
+"            N,                         pages range from N to end\n"
+"            ,M                         pages range from 0 to M-1\n"
 "bits-spec:\n"
-"            bit1,bit2                 (flags & (bit1|bit2)) != 0\n"
-"            bit1,bit2=bit1            (flags & (bit1|bit2)) == bit1\n"
-"            bit1,~bit2                (flags & (bit1|bit2)) == bit1\n"
-"            =bit1,bit2                flags == (bit1|bit2)\n"
+"            bit1,bit2                  (flags & (bit1|bit2)) != 0\n"
+"            bit1,bit2=bit1             (flags & (bit1|bit2)) == bit1\n"
+"            bit1,~bit2                 (flags & (bit1|bit2)) == bit1\n"
+"            =bit1,bit2                 flags == (bit1|bit2)\n"
 "bit-names:\n"
        );
 
@@ -884,13 +889,23 @@ static void parse_bits_mask(const char *optarg)
        add_bits_filter(mask, bits);
 }
 
+static void describe_flags(const char *optarg)
+{
+       uint64_t flags = parse_flag_names(optarg, 0);
 
-static struct option opts[] = {
+       printf("0x%016llx\t%s\t%s\n",
+               (unsigned long long)flags,
+               page_flag_name(flags),
+               page_flag_longname(flags));
+}
+
+static const struct option opts[] = {
        { "raw"       , 0, NULL, 'r' },
        { "pid"       , 1, NULL, 'p' },
        { "file"      , 1, NULL, 'f' },
        { "addr"      , 1, NULL, 'a' },
        { "bits"      , 1, NULL, 'b' },
+       { "describe"  , 1, NULL, 'd' },
        { "list"      , 0, NULL, 'l' },
        { "list-each" , 0, NULL, 'L' },
        { "no-summary", 0, NULL, 'N' },
@@ -907,7 +922,7 @@ int main(int argc, char *argv[])
        page_size = getpagesize();
 
        while ((c = getopt_long(argc, argv,
-                               "rp:f:a:b:lLNXxh", opts, NULL)) != -1) {
+                               "rp:f:a:b:d:lLNXxh", opts, NULL)) != -1) {
                switch (c) {
                case 'r':
                        opt_raw = 1;
@@ -924,6 +939,9 @@ int main(int argc, char *argv[])
                case 'b':
                        parse_bits_mask(optarg);
                        break;
+               case 'd':
+                       describe_flags(optarg);
+                       exit(0);
                case 'l':
                        opt_list = 1;
                        break;
index 1f21c34124db44d77fabe9ed7f9b8b1decfb299b..0a32c3ec6b1cb9baf0cd6a5de82c4bf98a912489 100644 (file)
@@ -835,13 +835,13 @@ F:        arch/arm/mach-pxa/palmte2.c
 F:     arch/arm/mach-pxa/include/mach/palmtc.h
 F:     arch/arm/mach-pxa/palmtc.c
 
-ARM/PALM TREO 680 SUPPORT
+ARM/PALM TREO SUPPORT
 M:     Tomas Cech <sleep_walker@suse.cz>
 L:     linux-arm-kernel@lists.infradead.org
 W:     http://hackndev.com
 S:     Maintained
-F:     arch/arm/mach-pxa/include/mach/treo680.h
-F:     arch/arm/mach-pxa/treo680.c
+F:     arch/arm/mach-pxa/include/mach/palmtreo.h
+F:     arch/arm/mach-pxa/palmtreo.c
 
 ARM/PALMZ72 SUPPORT
 M:     Sergey Lapin <slapin@ossfans.org>
@@ -1482,8 +1482,8 @@ F:        include/linux/coda*.h
 
 COMMON INTERNET FILE SYSTEM (CIFS)
 M:     Steve French <sfrench@samba.org>
-L:     linux-cifs-client@lists.samba.org
-L:     samba-technical@lists.samba.org
+L:     linux-cifs-client@lists.samba.org (moderated for non-subscribers)
+L:     samba-technical@lists.samba.org (moderated for non-subscribers)
 W:     http://linux-cifs.samba.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git
 S:     Supported
@@ -3081,8 +3081,11 @@ S:       Maintained
 F:     fs/autofs4/
 
 KERNEL BUILD
+M:     Michal Marek <mmarek@suse.cz>
+T:     git git://repo.or.cz/linux-kbuild.git for-next
+T:     git git://repo.or.cz/linux-kbuild.git for-linus
 L:     linux-kbuild@vger.kernel.org
-S:     Orphan
+S:     Maintained
 F:     Documentation/kbuild/
 F:     Makefile
 F:     scripts/Makefile.*
@@ -3124,7 +3127,6 @@ L:        kvm@vger.kernel.org
 W:     http://kvm.qumranet.com
 S:     Supported
 F:     arch/x86/include/asm/svm.h
-F:     arch/x86/kvm/kvm_svm.h
 F:     arch/x86/kvm/svm.c
 
 KERNEL VIRTUAL MACHINE (KVM) FOR POWERPC
@@ -5974,6 +5976,7 @@ M:        Mark Brown <broonie@opensource.wolfsonmicro.com>
 T:     git git://opensource.wolfsonmicro.com/linux-2.6-audioplus
 W:     http://opensource.wolfsonmicro.com/node/8
 S:     Supported
+F:     Documentation/hwmon/wm83??
 F:     drivers/leds/leds-wm83*.c
 F:     drivers/mfd/wm8*.c
 F:     drivers/power/wm83*.c
@@ -5983,9 +5986,9 @@ F:        drivers/video/backlight/wm83*_bl.c
 F:     drivers/watchdog/wm83*_wdt.c
 F:     include/linux/mfd/wm831x/
 F:     include/linux/mfd/wm8350/
-F:     include/linux/mfd/wm8400/
-F:     sound/soc/codecs/wm8350.c
-F:     sound/soc/codecs/wm8400.c
+F:     include/linux/mfd/wm8400*
+F:     sound/soc/codecs/wm8350.*
+F:     sound/soc/codecs/wm8400.*
 
 X.25 NETWORK LAYER
 M:     Henner Eisen <eis@baty.hanse.de>
index d12af472e1c0ec4aa380e8bdc73aab15627d7a2c..dbbf04f9230ed0890011e8c63a2db30f8e8d94fe 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <asm/console.h>
 #include <asm/uaccess.h>
 #include <asm/machvec.h>
@@ -79,42 +80,41 @@ static srm_env_t    srm_named_entries[] = {
 static srm_env_t       srm_numbered_entries[256];
 
 
-static int
-srm_env_read(char *page, char **start, off_t off, int count, int *eof,
-               void *data)
+static int srm_env_proc_show(struct seq_file *m, void *v)
 {
-       int             nbytes;
        unsigned long   ret;
        srm_env_t       *entry;
+       char            *page;
 
-       if (off != 0) {
-               *eof = 1;
-               return 0;
-       }
+       entry = (srm_env_t *)m->private;
+       page = (char *)__get_free_page(GFP_USER);
+       if (!page)
+               return -ENOMEM;
 
-       entry   = (srm_env_t *) data;
-       ret     = callback_getenv(entry->id, page, count);
+       ret = callback_getenv(entry->id, page, PAGE_SIZE);
 
        if ((ret >> 61) == 0) {
-               nbytes = (int) ret;
-               *eof = 1;
+               seq_write(m, page, ret);
+               ret = 0;
        } else
-               nbytes = -EFAULT;
+               ret = -EFAULT;
+       free_page((unsigned long)page);
+       return ret;
+}
 
-       return nbytes;
+static int srm_env_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, srm_env_proc_show, PDE(inode)->data);
 }
 
-static int
-srm_env_write(struct file *file, const char __user *buffer, unsigned long count,
-               void *data)
+static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
+                                 size_t count, loff_t *pos)
 {
        int res;
-       srm_env_t       *entry;
+       srm_env_t       *entry = PDE(file->f_path.dentry->d_inode)->data;
        char            *buf = (char *) __get_free_page(GFP_USER);
        unsigned long   ret1, ret2;
 
-       entry = (srm_env_t *) data;
-
        if (!buf)
                return -ENOMEM;
 
@@ -140,6 +140,15 @@ srm_env_write(struct file *file, const char __user *buffer, unsigned long count,
        return res;
 }
 
+static const struct file_operations srm_env_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = srm_env_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = srm_env_proc_write,
+};
+
 static void
 srm_env_cleanup(void)
 {
@@ -245,15 +254,10 @@ srm_env_init(void)
         */
        entry = srm_named_entries;
        while (entry->name && entry->id) {
-               entry->proc_entry = create_proc_entry(entry->name,
-                               0644, named_dir);
+               entry->proc_entry = proc_create_data(entry->name, 0644, named_dir,
+                                                    &srm_env_proc_fops, entry);
                if (!entry->proc_entry)
                        goto cleanup;
-
-               entry->proc_entry->data         = (void *) entry;
-               entry->proc_entry->read_proc    = srm_env_read;
-               entry->proc_entry->write_proc   = srm_env_write;
-
                entry++;
        }
 
@@ -264,15 +268,12 @@ srm_env_init(void)
                entry = &srm_numbered_entries[var_num];
                entry->name = number[var_num];
 
-               entry->proc_entry = create_proc_entry(entry->name,
-                               0644, numbered_dir);
+               entry->proc_entry = proc_create_data(entry->name, 0644, numbered_dir,
+                                                    &srm_env_proc_fops, entry);
                if (!entry->proc_entry)
                        goto cleanup;
 
                entry->id                       = var_num;
-               entry->proc_entry->data         = (void *) entry;
-               entry->proc_entry->read_proc    = srm_env_read;
-               entry->proc_entry->write_proc   = srm_env_write;
        }
 
        printk(KERN_INFO "%s: version %s loaded successfully\n", NAME,
diff --git a/arch/arm/mach-at91/include/mach/atmel-mci.h b/arch/arm/mach-at91/include/mach/atmel-mci.h
new file mode 100644 (file)
index 0000000..998cb0c
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __MACH_ATMEL_MCI_H
+#define __MACH_ATMEL_MCI_H
+
+#include <mach/at_hdmac.h>
+
+/**
+ * struct mci_dma_data - DMA data for MCI interface
+ */
+struct mci_dma_data {
+       struct at_dma_slave     sdata;
+};
+
+/* accessor macros */
+#define        slave_data_ptr(s)       (&(s)->sdata)
+#define find_slave_dev(s)      ((s)->sdata.dma_dev)
+
+#define        setup_dma_addr(s, t, r) do {            \
+       if (s) {                                \
+               (s)->sdata.tx_reg = (t);        \
+               (s)->sdata.rx_reg = (r);        \
+       }                                       \
+} while (0)
+
+#endif /* __MACH_ATMEL_MCI_H */
index 6c768b71ad64b42268d024012e7dce5f0a366f93..53fcef7c5201b10550305cc96db32538c3f31feb 100644 (file)
@@ -293,7 +293,7 @@ static int fpga_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops fpga_dev_pm_ops = {
+static const struct dev_pm_ops fpga_dev_pm_ops = {
        .suspend_noirq = fpga_suspend_noirq,
        .resume_noirq = fpga_resume_noirq,
 };
index 055160e0620e33db3c31e0d2182e27b3381d2964..04846811d0aaf83696e2b6c641785d902af7f1c5 100644 (file)
@@ -1431,7 +1431,7 @@ static int omap_mpuio_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops omap_mpuio_dev_pm_ops = {
+static const struct dev_pm_ops omap_mpuio_dev_pm_ops = {
        .suspend_noirq = omap_mpuio_suspend_noirq,
        .resume_noirq = omap_mpuio_resume_noirq,
 };
index eb9d4dc2e86dff84df6cc0905505d1efb97cfb59..b40ff39e0ac85e8017059e0916a23cef69b5baf8 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/usb/atmel_usba_udc.h>
+
+#include <mach/atmel-mci.h>
 #include <linux/atmel-mci.h>
 
 #include <asm/io.h>
@@ -1320,7 +1322,7 @@ struct platform_device *__init
 at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
 {
        struct platform_device          *pdev;
-       struct dw_dma_slave             *dws = &data->dma_slave;
+       struct mci_dma_slave            *slave;
        u32                             pioa_mask;
        u32                             piob_mask;
 
@@ -1339,13 +1341,17 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
                                ARRAY_SIZE(atmel_mci0_resource)))
                goto fail;
 
-       dws->dma_dev = &dw_dmac0_device.dev;
-       dws->reg_width = DW_DMA_SLAVE_WIDTH_32BIT;
-       dws->cfg_hi = (DWC_CFGH_SRC_PER(0)
+       slave = kzalloc(sizeof(struct mci_dma_slave), GFP_KERNEL);
+
+       slave->sdata.dma_dev = &dw_dmac0_device.dev;
+       slave->sdata.reg_width = DW_DMA_SLAVE_WIDTH_32BIT;
+       slave->sdata.cfg_hi = (DWC_CFGH_SRC_PER(0)
                                | DWC_CFGH_DST_PER(1));
-       dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL
+       slave->sdata.cfg_lo &= ~(DWC_CFGL_HS_DST_POL
                                | DWC_CFGL_HS_SRC_POL);
 
+       data->dma_slave = slave;
+
        if (platform_device_add_data(pdev, data,
                                sizeof(struct mci_platform_data)))
                goto fail;
@@ -1411,6 +1417,8 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
        return pdev;
 
 fail:
+       data->dma_slave = NULL;
+       kfree(slave);
        platform_device_put(pdev);
        return NULL;
 }
diff --git a/arch/avr32/mach-at32ap/include/mach/atmel-mci.h b/arch/avr32/mach-at32ap/include/mach/atmel-mci.h
new file mode 100644 (file)
index 0000000..a9b3896
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __MACH_ATMEL_MCI_H
+#define __MACH_ATMEL_MCI_H
+
+#include <linux/dw_dmac.h>
+
+/**
+ * struct mci_dma_data - DMA data for MCI interface
+ */
+struct mci_dma_data {
+       struct dw_dma_slave     sdata;
+};
+
+/* accessor macros */
+#define        slave_data_ptr(s)       (&(s)->sdata)
+#define find_slave_dev(s)      ((s)->sdata.dma_dev)
+
+#define        setup_dma_addr(s, t, r) do {            \
+       if (s) {                                \
+               (s)->sdata.tx_reg = (t);        \
+               (s)->sdata.rx_reg = (r);        \
+       }                                       \
+} while (0)
+
+#endif /* __MACH_ATMEL_MCI_H */
index 3499ff57bf425db6544e9bf60f31a334d4aaa4a3..6a8a27cfae3e04f0f35771aa9490ff3b46e8729d 100644 (file)
@@ -22,8 +22,6 @@
 
 #include <asm/mmzone.h>
 
-#define NUMA_NO_NODE   -1
-
 extern u16 cpu_to_node_map[NR_CPUS] __cacheline_aligned;
 extern cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned;
 extern pg_data_t *pgdat_list[MAX_NUMNODES];
index 495589950dc7daab650b316888dde9a58c98453f..5c91995b74e4b598e7eb5e974492ecb13087ff6a 100644 (file)
@@ -551,7 +551,7 @@ static int appldata_thaw(struct device *dev)
        return appldata_restore(dev);
 }
 
-static struct dev_pm_ops appldata_pm_ops = {
+static const struct dev_pm_ops appldata_pm_ops = {
        .freeze         = appldata_freeze,
        .thaw           = appldata_thaw,
        .restore        = appldata_restore,
index 071c81f179ef30d4d1662c20582835c60b81a71f..0168472b2fdfb66ed122e4ebaabf7a7a2d415efd 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/sysctl.h>
 #include <asm/uaccess.h>
 #include <linux/module.h>
@@ -1178,7 +1179,7 @@ debug_get_uint(char *buf)
 {
        int rc;
 
-       for(; isspace(*buf); buf++);
+       buf = skip_spaces(buf);
        rc = simple_strtoul(buf, &buf, 10);
        if(*buf){
                rc = -EINVAL;
index e14629c87de4fb48c15943f52ffcedf7fca48821..51069245b79a207274547c51ce904206f5ffb939 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/console.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/mm.h>
@@ -131,7 +132,7 @@ void mconsole_proc(struct mc_request *req)
        char *ptr = req->request.data, *buf;
 
        ptr += strlen("proc");
-       while (isspace(*ptr)) ptr++;
+       ptr = skip_spaces(ptr);
 
        proc = get_fs_type("proc");
        if (proc == NULL) {
@@ -212,8 +213,7 @@ void mconsole_proc(struct mc_request *req)
        char *ptr = req->request.data;
 
        ptr += strlen("proc");
-       while (isspace(*ptr))
-               ptr++;
+       ptr = skip_spaces(ptr);
        snprintf(path, sizeof(path), "/proc/%s", ptr);
 
        fd = sys_open(path, 0, 0);
@@ -560,8 +560,7 @@ void mconsole_config(struct mc_request *req)
        int err;
 
        ptr += strlen("config");
-       while (isspace(*ptr))
-               ptr++;
+       ptr = skip_spaces(ptr);
        dev = mconsole_find_dev(ptr);
        if (dev == NULL) {
                mconsole_reply(req, "Bad configuration option", 1, 0);
@@ -588,7 +587,7 @@ void mconsole_remove(struct mc_request *req)
        int err, start, end, n;
 
        ptr += strlen("remove");
-       while (isspace(*ptr)) ptr++;
+       ptr = skip_spaces(ptr);
        dev = mconsole_find_dev(ptr);
        if (dev == NULL) {
                mconsole_reply(req, "Bad remove option", 1, 0);
@@ -712,7 +711,7 @@ void mconsole_sysrq(struct mc_request *req)
        char *ptr = req->request.data;
 
        ptr += strlen("sysrq");
-       while (isspace(*ptr)) ptr++;
+       ptr = skip_spaces(ptr);
 
        /*
         * With 'b', the system will shut down without a chance to reply,
@@ -757,8 +756,7 @@ void mconsole_stack(struct mc_request *req)
         */
 
        ptr += strlen("stack");
-       while (isspace(*ptr))
-               ptr++;
+       ptr = skip_spaces(ptr);
 
        /*
         * Should really check for multiple pids or reject bad args here
@@ -833,8 +831,8 @@ static int __init mconsole_init(void)
 
 __initcall(mconsole_init);
 
-static int write_proc_mconsole(struct file *file, const char __user *buffer,
-                              unsigned long count, void *data)
+static ssize_t mconsole_proc_write(struct file *file,
+               const char __user *buffer, size_t count, loff_t *pos)
 {
        char *buf;
 
@@ -855,6 +853,11 @@ static int write_proc_mconsole(struct file *file, const char __user *buffer,
        return count;
 }
 
+static const struct file_operations mconsole_proc_fops = {
+       .owner          = THIS_MODULE,
+       .write          = mconsole_proc_write,
+};
+
 static int create_proc_mconsole(void)
 {
        struct proc_dir_entry *ent;
@@ -862,15 +865,12 @@ static int create_proc_mconsole(void)
        if (notify_socket == NULL)
                return 0;
 
-       ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
+       ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
        if (ent == NULL) {
                printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
                       "failed\n");
                return 0;
        }
-
-       ent->read_proc = NULL;
-       ent->write_proc = write_proc_mconsole;
        return 0;
 }
 
index 635d16d90a80d4158df7769ec11df5ab6c64c38d..5ff554677f4019ad4435bb6d8277b9ec72907d97 100644 (file)
@@ -27,6 +27,7 @@
 #include "linux/init.h"
 #include "linux/cdrom.h"
 #include "linux/proc_fs.h"
+#include "linux/seq_file.h"
 #include "linux/ctype.h"
 #include "linux/capability.h"
 #include "linux/mm.h"
@@ -200,23 +201,25 @@ static void make_proc_ide(void)
        proc_ide = proc_mkdir("ide0", proc_ide_root);
 }
 
-static int proc_ide_read_media(char *page, char **start, off_t off, int count,
-                              int *eof, void *data)
+static int fake_ide_media_proc_show(struct seq_file *m, void *v)
 {
-       int len;
-
-       strcpy(page, "disk\n");
-       len = strlen("disk\n");
-       len -= off;
-       if (len < count){
-               *eof = 1;
-               if (len <= 0) return 0;
-       }
-       else len = count;
-       *start = page + off;
-       return len;
+       seq_puts(m, "disk\n");
+       return 0;
+}
+
+static int fake_ide_media_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, fake_ide_media_proc_show, NULL);
 }
 
+static const struct file_operations fake_ide_media_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = fake_ide_media_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static void make_ide_entries(const char *dev_name)
 {
        struct proc_dir_entry *dir, *ent;
@@ -227,11 +230,8 @@ static void make_ide_entries(const char *dev_name)
        dir = proc_mkdir(dev_name, proc_ide);
        if(!dir) return;
 
-       ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
+       ent = proc_create("media", S_IRUGO, dir, &fake_ide_media_proc_fops);
        if(!ent) return;
-       ent->data = NULL;
-       ent->read_proc = proc_ide_read_media;
-       ent->write_proc = NULL;
        snprintf(name, sizeof(name), "ide0/%s", dev_name);
        proc_symlink(dev_name, proc_ide_root, name);
 }
index 6540d2c9fbb76f684db03539e26e202178cf18f5..829df49dee99c6093d5586d0e2c19e34786dfa97 100644 (file)
@@ -6,7 +6,9 @@
 #include <linux/ctype.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/types.h>
 #include <asm/uaccess.h>
 
  */
 int uml_exitcode = 0;
 
-static int read_proc_exitcode(char *page, char **start, off_t off,
-                             int count, int *eof, void *data)
+static int exitcode_proc_show(struct seq_file *m, void *v)
 {
-       int len, val;
+       int val;
 
        /*
         * Save uml_exitcode in a local so that we don't need to guarantee
         * that sprintf accesses it atomically.
         */
        val = uml_exitcode;
-       len = sprintf(page, "%d\n", val);
-       len -= off;
-       if (len <= off+count)
-               *eof = 1;
-       *start = page + off;
-       if (len > count)
-               len = count;
-       if (len < 0)
-               len = 0;
-       return len;
+       seq_printf(m, "%d\n", val);
+       return 0;
+}
+
+static int exitcode_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, exitcode_proc_show, NULL);
 }
 
-static int write_proc_exitcode(struct file *file, const char __user *buffer,
-                              unsigned long count, void *data)
+static ssize_t exitcode_proc_write(struct file *file,
+               const char __user *buffer, size_t count, loff_t *pos)
 {
        char *end, buf[sizeof("nnnnn\0")];
        int tmp;
@@ -55,20 +53,25 @@ static int write_proc_exitcode(struct file *file, const char __user *buffer,
        return count;
 }
 
+static const struct file_operations exitcode_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = exitcode_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = exitcode_proc_write,
+};
+
 static int make_proc_exitcode(void)
 {
        struct proc_dir_entry *ent;
 
-       ent = create_proc_entry("exitcode", 0600, NULL);
+       ent = proc_create("exitcode", 0600, NULL, &exitcode_proc_fops);
        if (ent == NULL) {
                printk(KERN_WARNING "make_proc_exitcode : Failed to register "
                       "/proc/exitcode\n");
                return 0;
        }
-
-       ent->read_proc = read_proc_exitcode;
-       ent->write_proc = write_proc_exitcode;
-
        return 0;
 }
 
index 4a28a1568d856c7b1605743ca33c143921caacd4..2f910a1b745452fdcccc1c167cdd3671d2275023 100644 (file)
@@ -9,11 +9,13 @@
 #include <linux/hardirq.h>
 #include <linux/gfp.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/personality.h>
 #include <linux/proc_fs.h>
 #include <linux/ptrace.h>
 #include <linux/random.h>
 #include <linux/sched.h>
+#include <linux/seq_file.h>
 #include <linux/tick.h>
 #include <linux/threads.h>
 #include <asm/current.h>
@@ -336,16 +338,19 @@ int get_using_sysemu(void)
        return atomic_read(&using_sysemu);
 }
 
-static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data)
+static int sysemu_proc_show(struct seq_file *m, void *v)
 {
-       if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size)
-               /* No overflow */
-               *eof = 1;
+       seq_printf(m, "%d\n", get_using_sysemu());
+       return 0;
+}
 
-       return strlen(buf);
+static int sysemu_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, sysemu_proc_show, NULL);
 }
 
-static int proc_write_sysemu(struct file *file,const char __user *buf, unsigned long count,void *data)
+static ssize_t sysemu_proc_write(struct file *file, const char __user *buf,
+                                size_t count, loff_t *pos)
 {
        char tmp[2];
 
@@ -358,13 +363,22 @@ static int proc_write_sysemu(struct file *file,const char __user *buf, unsigned
        return count;
 }
 
+static const struct file_operations sysemu_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = sysemu_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = sysemu_proc_write,
+};
+
 int __init make_proc_sysemu(void)
 {
        struct proc_dir_entry *ent;
        if (!sysemu_supported)
                return 0;
 
-       ent = create_proc_entry("sysemu", 0600, NULL);
+       ent = proc_create("sysemu", 0600, NULL, &sysemu_proc_fops);
 
        if (ent == NULL)
        {
@@ -372,9 +386,6 @@ int __init make_proc_sysemu(void)
                return 0;
        }
 
-       ent->read_proc  = proc_read_sysemu;
-       ent->write_proc = proc_write_sysemu;
-
        return 0;
 }
 
index 32a1918e1b880d2ab94f7a62c044bd3d8fff64e9..3b2a5aca4edbde10ed5f05066e03b4998d7b52a7 100644 (file)
@@ -2012,18 +2012,9 @@ config SCx200HR_TIMER
          processor goes idle (as is done by the scheduler).  The
          other workaround is idle=poll boot option.
 
-config GEODE_MFGPT_TIMER
-       def_bool y
-       prompt "Geode Multi-Function General Purpose Timer (MFGPT) events"
-       depends on MGEODE_LX && GENERIC_TIME && GENERIC_CLOCKEVENTS
-       ---help---
-         This driver provides a clock event source based on the MFGPT
-         timer(s) in the CS5535 and CS5536 companion chip for the geode.
-         MFGPTs have a better resolution and max interval than the
-         generic PIT, and are suitable for use as high-res timers.
-
 config OLPC
        bool "One Laptop Per Child support"
+       select GPIOLIB
        default n
        ---help---
          Add support for detecting the unique features of the OLPC
index ad3c2ed7548185f7c9891236e078bb98c62cb67c..7cd73552a4e8021517ddc3bca5d47ed9bfc7b811 100644 (file)
 
 #include <asm/processor.h>
 #include <linux/io.h>
-
-/* Generic southbridge functions */
-
-#define GEODE_DEV_PMS 0
-#define GEODE_DEV_ACPI 1
-#define GEODE_DEV_GPIO 2
-#define GEODE_DEV_MFGPT 3
-
-extern int geode_get_dev_base(unsigned int dev);
-
-/* Useful macros */
-#define geode_pms_base()       geode_get_dev_base(GEODE_DEV_PMS)
-#define geode_acpi_base()      geode_get_dev_base(GEODE_DEV_ACPI)
-#define geode_gpio_base()      geode_get_dev_base(GEODE_DEV_GPIO)
-#define geode_mfgpt_base()     geode_get_dev_base(GEODE_DEV_MFGPT)
-
-/* MSRS */
-
-#define MSR_GLIU_P2D_RO0       0x10000029
-
-#define MSR_LX_GLD_MSR_CONFIG  0x48002001
-#define MSR_LX_MSR_PADSEL      0x48002011      /* NOT 0x48000011; the data
-                                                * sheet has the wrong value */
-#define MSR_GLCP_SYS_RSTPLL    0x4C000014
-#define MSR_GLCP_DOTPLL                0x4C000015
-
-#define MSR_LBAR_SMB           0x5140000B
-#define MSR_LBAR_GPIO          0x5140000C
-#define MSR_LBAR_MFGPT         0x5140000D
-#define MSR_LBAR_ACPI          0x5140000E
-#define MSR_LBAR_PMS           0x5140000F
-
-#define MSR_DIVIL_SOFT_RESET   0x51400017
-
-#define MSR_PIC_YSEL_LOW       0x51400020
-#define MSR_PIC_YSEL_HIGH      0x51400021
-#define MSR_PIC_ZSEL_LOW       0x51400022
-#define MSR_PIC_ZSEL_HIGH      0x51400023
-#define MSR_PIC_IRQM_LPC       0x51400025
-
-#define MSR_MFGPT_IRQ          0x51400028
-#define MSR_MFGPT_NR           0x51400029
-#define MSR_MFGPT_SETUP                0x5140002B
-
-#define MSR_LX_SPARE_MSR       0x80000011      /* DC-specific */
-
-#define MSR_GX_GLD_MSR_CONFIG  0xC0002001
-#define MSR_GX_MSR_PADSEL      0xC0002011
-
-/* Resource Sizes */
-
-#define LBAR_GPIO_SIZE         0xFF
-#define LBAR_MFGPT_SIZE                0x40
-#define LBAR_ACPI_SIZE         0x40
-#define LBAR_PMS_SIZE          0x80
-
-/* ACPI registers (PMS block) */
-
-/*
- * PM1_EN is only valid when VSA is enabled for 16 bit reads.
- * When VSA is not enabled, *always* read both PM1_STS and PM1_EN
- * with a 32 bit read at offset 0x0
- */
-
-#define PM1_STS                        0x00
-#define PM1_EN                 0x02
-#define PM1_CNT                        0x08
-#define PM2_CNT                        0x0C
-#define PM_TMR                 0x10
-#define PM_GPE0_STS            0x18
-#define PM_GPE0_EN             0x1C
-
-/* PMC registers (PMS block) */
-
-#define PM_SSD                 0x00
-#define PM_SCXA                        0x04
-#define PM_SCYA                        0x08
-#define PM_OUT_SLPCTL          0x0C
-#define PM_SCLK                        0x10
-#define PM_SED                 0x1
-#define PM_SCXD                        0x18
-#define PM_SCYD                        0x1C
-#define PM_IN_SLPCTL           0x20
-#define PM_WKD                 0x30
-#define PM_WKXD                        0x34
-#define PM_RD                  0x38
-#define PM_WKXA                        0x3C
-#define PM_FSD                 0x40
-#define PM_TSD                 0x44
-#define PM_PSD                 0x48
-#define PM_NWKD                        0x4C
-#define PM_AWKD                        0x50
-#define PM_SSC                 0x54
-
-/* VSA2 magic values */
-
-#define VSA_VRC_INDEX          0xAC1C
-#define VSA_VRC_DATA           0xAC1E
-#define VSA_VR_UNLOCK          0xFC53  /* unlock virtual register */
-#define VSA_VR_SIGNATURE       0x0003
-#define VSA_VR_MEM_SIZE                0x0200
-#define AMD_VSA_SIG            0x4132  /* signature is ascii 'VSA2' */
-#define GSW_VSA_SIG            0x534d  /* General Software signature */
-/* GPIO */
-
-#define GPIO_OUTPUT_VAL                0x00
-#define GPIO_OUTPUT_ENABLE     0x04
-#define GPIO_OUTPUT_OPEN_DRAIN 0x08
-#define GPIO_OUTPUT_INVERT     0x0C
-#define GPIO_OUTPUT_AUX1       0x10
-#define GPIO_OUTPUT_AUX2       0x14
-#define GPIO_PULL_UP           0x18
-#define GPIO_PULL_DOWN         0x1C
-#define GPIO_INPUT_ENABLE      0x20
-#define GPIO_INPUT_INVERT      0x24
-#define GPIO_INPUT_FILTER      0x28
-#define GPIO_INPUT_EVENT_COUNT 0x2C
-#define GPIO_READ_BACK         0x30
-#define GPIO_INPUT_AUX1                0x34
-#define GPIO_EVENTS_ENABLE     0x38
-#define GPIO_LOCK_ENABLE       0x3C
-#define GPIO_POSITIVE_EDGE_EN  0x40
-#define GPIO_NEGATIVE_EDGE_EN  0x44
-#define GPIO_POSITIVE_EDGE_STS 0x48
-#define GPIO_NEGATIVE_EDGE_STS 0x4C
-
-#define GPIO_MAP_X             0xE0
-#define GPIO_MAP_Y             0xE4
-#define GPIO_MAP_Z             0xE8
-#define GPIO_MAP_W             0xEC
-
-static inline u32 geode_gpio(unsigned int nr)
-{
-       BUG_ON(nr > 28);
-       return 1 << nr;
-}
-
-extern void geode_gpio_set(u32, unsigned int);
-extern void geode_gpio_clear(u32, unsigned int);
-extern int geode_gpio_isset(u32, unsigned int);
-extern void geode_gpio_setup_event(unsigned int, int, int);
-extern void geode_gpio_set_irq(unsigned int, unsigned int);
-
-static inline void geode_gpio_event_irq(unsigned int gpio, int pair)
-{
-       geode_gpio_setup_event(gpio, pair, 0);
-}
-
-static inline void geode_gpio_event_pme(unsigned int gpio, int pair)
-{
-       geode_gpio_setup_event(gpio, pair, 1);
-}
-
-/* Specific geode tests */
+#include <linux/cs5535.h>
 
 static inline int is_geode_gx(void)
 {
@@ -186,68 +33,4 @@ static inline int is_geode(void)
        return (is_geode_gx() || is_geode_lx());
 }
 
-#ifdef CONFIG_MGEODE_LX
-extern int geode_has_vsa2(void);
-#else
-static inline int geode_has_vsa2(void)
-{
-       return 0;
-}
-#endif
-
-/* MFGPTs */
-
-#define MFGPT_MAX_TIMERS       8
-#define MFGPT_TIMER_ANY                (-1)
-
-#define MFGPT_DOMAIN_WORKING   1
-#define MFGPT_DOMAIN_STANDBY   2
-#define MFGPT_DOMAIN_ANY       (MFGPT_DOMAIN_WORKING | MFGPT_DOMAIN_STANDBY)
-
-#define MFGPT_CMP1             0
-#define MFGPT_CMP2             1
-
-#define MFGPT_EVENT_IRQ                0
-#define MFGPT_EVENT_NMI                1
-#define MFGPT_EVENT_RESET      3
-
-#define MFGPT_REG_CMP1         0
-#define MFGPT_REG_CMP2         2
-#define MFGPT_REG_COUNTER      4
-#define MFGPT_REG_SETUP                6
-
-#define MFGPT_SETUP_CNTEN      (1 << 15)
-#define MFGPT_SETUP_CMP2       (1 << 14)
-#define MFGPT_SETUP_CMP1       (1 << 13)
-#define MFGPT_SETUP_SETUP      (1 << 12)
-#define MFGPT_SETUP_STOPEN     (1 << 11)
-#define MFGPT_SETUP_EXTEN      (1 << 10)
-#define MFGPT_SETUP_REVEN      (1 << 5)
-#define MFGPT_SETUP_CLKSEL     (1 << 4)
-
-static inline void geode_mfgpt_write(int timer, u16 reg, u16 value)
-{
-       u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
-       outw(value, base + reg + (timer * 8));
-}
-
-static inline u16 geode_mfgpt_read(int timer, u16 reg)
-{
-       u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
-       return inw(base + reg + (timer * 8));
-}
-
-extern int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable);
-extern int geode_mfgpt_set_irq(int timer, int cmp, int *irq, int enable);
-extern int geode_mfgpt_alloc_timer(int timer, int domain);
-
-#define geode_mfgpt_setup_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 1)
-#define geode_mfgpt_release_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 0)
-
-#ifdef CONFIG_GEODE_MFGPT_TIMER
-extern int __init mfgpt_timer_setup(void);
-#else
-static inline int mfgpt_timer_setup(void) { return 0; }
-#endif
-
 #endif /* _ASM_X86_GEODE_H */
index 834a30295fabbb75740826a39c76e7bfbbe2fe37..3a57385d9fa7e9ade6d666e279fc146086169d7d 100644 (file)
@@ -120,7 +120,7 @@ extern int olpc_ec_mask_unset(uint8_t bits);
 
 /* GPIO assignments */
 
-#define OLPC_GPIO_MIC_AC       geode_gpio(1)
+#define OLPC_GPIO_MIC_AC       1
 #define OLPC_GPIO_DCON_IRQ     geode_gpio(7)
 #define OLPC_GPIO_THRM_ALRM    geode_gpio(10)
 #define OLPC_GPIO_SMB_CLK      geode_gpio(14)
index 40e37b10c6c031ab86ccd08c2123e235e259a8de..c5087d7965871c5d9ec8e93d96c7fc4a8c5b3469 100644 (file)
 # endif
 #endif
 
-/* Node not present */
-#define NUMA_NO_NODE   (-1)
+/*
+ * to preserve the visibility of NUMA_NO_NODE definition,
+ * moved to there from here.  May be used independent of
+ * CONFIG_NUMA.
+ */
+#include <linux/numa.h>
 
 #ifdef CONFIG_NUMA
 #include <linux/cpumask.h>
+
 #include <asm/mpspec.h>
 
 #ifdef CONFIG_X86_32
index 4f2e66e29ecc5cc35e749f18e194b2ad11f398d2..d87f09bc5a524952e7ff7f920f75306b2984c608 100644 (file)
@@ -89,7 +89,6 @@ obj-$(CONFIG_EARLY_PRINTK)    += early_printk.o
 obj-$(CONFIG_HPET_TIMER)       += hpet.o
 
 obj-$(CONFIG_K8_NB)            += k8.o
-obj-$(CONFIG_MGEODE_LX)                += geode_32.o mfgpt_32.o
 obj-$(CONFIG_DEBUG_RODATA_TEST)        += test_rodata.o
 obj-$(CONFIG_DEBUG_NX_TEST)    += test_nx.o
 
index 3c1b12d461d151303d6e2787c5aa9fddc078c1ad..e006e56f699c24d1fe193a307bf799fda3801c6d 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/proc_fs.h>
 #include <linux/module.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/init.h>
 
 #define LINE_SIZE 80
@@ -133,8 +134,7 @@ mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
                return -EINVAL;
 
        base = simple_strtoull(line + 5, &ptr, 0);
-       while (isspace(*ptr))
-               ptr++;
+       ptr = skip_spaces(ptr);
 
        if (strncmp(ptr, "size=", 5))
                return -EINVAL;
@@ -142,14 +142,11 @@ mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
        size = simple_strtoull(ptr + 5, &ptr, 0);
        if ((base & 0xfff) || (size & 0xfff))
                return -EINVAL;
-       while (isspace(*ptr))
-               ptr++;
+       ptr = skip_spaces(ptr);
 
        if (strncmp(ptr, "type=", 5))
                return -EINVAL;
-       ptr += 5;
-       while (isspace(*ptr))
-               ptr++;
+       ptr = skip_spaces(ptr + 5);
 
        for (i = 0; i < MTRR_NUM_TYPES; ++i) {
                if (strcmp(ptr, mtrr_strings[i]))
diff --git a/arch/x86/kernel/geode_32.c b/arch/x86/kernel/geode_32.c
deleted file mode 100644 (file)
index 9b08e85..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * AMD Geode southbridge support code
- * Copyright (C) 2006, Advanced Micro Devices, Inc.
- * Copyright (C) 2007, Andres Salomon <dilinger@debian.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public License
- * as published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/io.h>
-#include <asm/msr.h>
-#include <asm/geode.h>
-
-static struct {
-       char *name;
-       u32 msr;
-       int size;
-       u32 base;
-} lbars[] = {
-       { "geode-pms",   MSR_LBAR_PMS, LBAR_PMS_SIZE, 0 },
-       { "geode-acpi",  MSR_LBAR_ACPI, LBAR_ACPI_SIZE, 0 },
-       { "geode-gpio",  MSR_LBAR_GPIO, LBAR_GPIO_SIZE, 0 },
-       { "geode-mfgpt", MSR_LBAR_MFGPT, LBAR_MFGPT_SIZE, 0 }
-};
-
-static void __init init_lbars(void)
-{
-       u32 lo, hi;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(lbars); i++) {
-               rdmsr(lbars[i].msr, lo, hi);
-               if (hi & 0x01)
-                       lbars[i].base = lo & 0x0000ffff;
-
-               if (lbars[i].base == 0)
-                       printk(KERN_ERR "geode:  Couldn't initialize '%s'\n",
-                                       lbars[i].name);
-       }
-}
-
-int geode_get_dev_base(unsigned int dev)
-{
-       BUG_ON(dev >= ARRAY_SIZE(lbars));
-       return lbars[dev].base;
-}
-EXPORT_SYMBOL_GPL(geode_get_dev_base);
-
-/* === GPIO API === */
-
-void geode_gpio_set(u32 gpio, unsigned int reg)
-{
-       u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
-
-       if (!base)
-               return;
-
-       /* low bank register */
-       if (gpio & 0xFFFF)
-               outl(gpio & 0xFFFF, base + reg);
-       /* high bank register */
-       gpio >>= 16;
-       if (gpio)
-               outl(gpio, base + 0x80 + reg);
-}
-EXPORT_SYMBOL_GPL(geode_gpio_set);
-
-void geode_gpio_clear(u32 gpio, unsigned int reg)
-{
-       u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
-
-       if (!base)
-               return;
-
-       /* low bank register */
-       if (gpio & 0xFFFF)
-               outl((gpio & 0xFFFF) << 16, base + reg);
-       /* high bank register */
-       gpio &= (0xFFFF << 16);
-       if (gpio)
-               outl(gpio, base + 0x80 + reg);
-}
-EXPORT_SYMBOL_GPL(geode_gpio_clear);
-
-int geode_gpio_isset(u32 gpio, unsigned int reg)
-{
-       u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
-       u32 val;
-
-       if (!base)
-               return 0;
-
-       /* low bank register */
-       if (gpio & 0xFFFF) {
-               val = inl(base + reg) & (gpio & 0xFFFF);
-               if ((gpio & 0xFFFF) == val)
-                       return 1;
-       }
-       /* high bank register */
-       gpio >>= 16;
-       if (gpio) {
-               val = inl(base + 0x80 + reg) & gpio;
-               if (gpio == val)
-                       return 1;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(geode_gpio_isset);
-
-void geode_gpio_set_irq(unsigned int group, unsigned int irq)
-{
-       u32 lo, hi;
-
-       if (group > 7 || irq > 15)
-               return;
-
-       rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
-
-       lo &= ~(0xF << (group * 4));
-       lo |= (irq & 0xF) << (group * 4);
-
-       wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
-}
-EXPORT_SYMBOL_GPL(geode_gpio_set_irq);
-
-void geode_gpio_setup_event(unsigned int gpio, int pair, int pme)
-{
-       u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
-       u32 offset, shift, val;
-
-       if (gpio >= 24)
-               offset = GPIO_MAP_W;
-       else if (gpio >= 16)
-               offset = GPIO_MAP_Z;
-       else if (gpio >= 8)
-               offset = GPIO_MAP_Y;
-       else
-               offset = GPIO_MAP_X;
-
-       shift = (gpio % 8) * 4;
-
-       val = inl(base + offset);
-
-       /* Clear whatever was there before */
-       val &= ~(0xF << shift);
-
-       /* And set the new value */
-
-       val |= ((pair & 7) << shift);
-
-       /* Set the PME bit if this is a PME event */
-
-       if (pme)
-               val |= (1 << (shift + 3));
-
-       outl(val, base + offset);
-}
-EXPORT_SYMBOL_GPL(geode_gpio_setup_event);
-
-int geode_has_vsa2(void)
-{
-       static int has_vsa2 = -1;
-
-       if (has_vsa2 == -1) {
-               u16 val;
-
-               /*
-                * The VSA has virtual registers that we can query for a
-                * signature.
-                */
-               outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
-               outw(VSA_VR_SIGNATURE, VSA_VRC_INDEX);
-
-               val = inw(VSA_VRC_DATA);
-               has_vsa2 = (val == AMD_VSA_SIG || val == GSW_VSA_SIG);
-       }
-
-       return has_vsa2;
-}
-EXPORT_SYMBOL_GPL(geode_has_vsa2);
-
-static int __init geode_southbridge_init(void)
-{
-       if (!is_geode())
-               return -ENODEV;
-
-       init_lbars();
-       (void) mfgpt_timer_setup();
-       return 0;
-}
-
-postcore_initcall(geode_southbridge_init);
diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c
deleted file mode 100644 (file)
index 2a62d84..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT)
- *
- * Copyright (C) 2006, Advanced Micro Devices, Inc.
- * Copyright (C) 2007, Andres Salomon <dilinger@debian.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public License
- * as published by the Free Software Foundation.
- *
- * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
- */
-
-/*
- * We are using the 32.768kHz input clock - it's the only one that has the
- * ranges we find desirable.  The following table lists the suitable
- * divisors and the associated Hz, minimum interval and the maximum interval:
- *
- *  Divisor   Hz      Min Delta (s)  Max Delta (s)
- *   1        32768   .00048828125      2.000
- *   2        16384   .0009765625       4.000
- *   4         8192   .001953125        8.000
- *   8         4096   .00390625        16.000
- *   16        2048   .0078125         32.000
- *   32        1024   .015625          64.000
- *   64         512   .03125          128.000
- *  128         256   .0625           256.000
- *  256         128   .125            512.000
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <asm/geode.h>
-
-#define MFGPT_DEFAULT_IRQ      7
-
-static struct mfgpt_timer_t {
-       unsigned int avail:1;
-} mfgpt_timers[MFGPT_MAX_TIMERS];
-
-/* Selected from the table above */
-
-#define MFGPT_DIVISOR 16
-#define MFGPT_SCALE  4     /* divisor = 2^(scale) */
-#define MFGPT_HZ  (32768 / MFGPT_DIVISOR)
-#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
-
-/* Allow for disabling of MFGPTs */
-static int disable;
-static int __init mfgpt_disable(char *s)
-{
-       disable = 1;
-       return 1;
-}
-__setup("nomfgpt", mfgpt_disable);
-
-/* Reset the MFGPT timers. This is required by some broken BIOSes which already
- * do the same and leave the system in an unstable state. TinyBIOS 0.98 is
- * affected at least (0.99 is OK with MFGPT workaround left to off).
- */
-static int __init mfgpt_fix(char *s)
-{
-       u32 val, dummy;
-
-       /* The following udocumented bit resets the MFGPT timers */
-       val = 0xFF; dummy = 0;
-       wrmsr(MSR_MFGPT_SETUP, val, dummy);
-       return 1;
-}
-__setup("mfgptfix", mfgpt_fix);
-
-/*
- * Check whether any MFGPTs are available for the kernel to use.  In most
- * cases, firmware that uses AMD's VSA code will claim all timers during
- * bootup; we certainly don't want to take them if they're already in use.
- * In other cases (such as with VSAless OpenFirmware), the system firmware
- * leaves timers available for us to use.
- */
-
-
-static int timers = -1;
-
-static void geode_mfgpt_detect(void)
-{
-       int i;
-       u16 val;
-
-       timers = 0;
-
-       if (disable) {
-               printk(KERN_INFO "geode-mfgpt:  MFGPT support is disabled\n");
-               goto done;
-       }
-
-       if (!geode_get_dev_base(GEODE_DEV_MFGPT)) {
-               printk(KERN_INFO "geode-mfgpt:  MFGPT LBAR is not set up\n");
-               goto done;
-       }
-
-       for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
-               val = geode_mfgpt_read(i, MFGPT_REG_SETUP);
-               if (!(val & MFGPT_SETUP_SETUP)) {
-                       mfgpt_timers[i].avail = 1;
-                       timers++;
-               }
-       }
-
-done:
-       printk(KERN_INFO "geode-mfgpt:  %d MFGPT timers available.\n", timers);
-}
-
-int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable)
-{
-       u32 msr, mask, value, dummy;
-       int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
-
-       if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
-               return -EIO;
-
-       /*
-        * The register maps for these are described in sections 6.17.1.x of
-        * the AMD Geode CS5536 Companion Device Data Book.
-        */
-       switch (event) {
-       case MFGPT_EVENT_RESET:
-               /*
-                * XXX: According to the docs, we cannot reset timers above
-                * 6; that is, resets for 7 and 8 will be ignored.  Is this
-                * a problem?   -dilinger
-                */
-               msr = MSR_MFGPT_NR;
-               mask = 1 << (timer + 24);
-               break;
-
-       case MFGPT_EVENT_NMI:
-               msr = MSR_MFGPT_NR;
-               mask = 1 << (timer + shift);
-               break;
-
-       case MFGPT_EVENT_IRQ:
-               msr = MSR_MFGPT_IRQ;
-               mask = 1 << (timer + shift);
-               break;
-
-       default:
-               return -EIO;
-       }
-
-       rdmsr(msr, value, dummy);
-
-       if (enable)
-               value |= mask;
-       else
-               value &= ~mask;
-
-       wrmsr(msr, value, dummy);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(geode_mfgpt_toggle_event);
-
-int geode_mfgpt_set_irq(int timer, int cmp, int *irq, int enable)
-{
-       u32 zsel, lpc, dummy;
-       int shift;
-
-       if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
-               return -EIO;
-
-       /*
-        * Unfortunately, MFGPTs come in pairs sharing their IRQ lines. If VSA
-        * is using the same CMP of the timer's Siamese twin, the IRQ is set to
-        * 2, and we mustn't use nor change it.
-        * XXX: Likewise, 2 Linux drivers might clash if the 2nd overwrites the
-        * IRQ of the 1st. This can only happen if forcing an IRQ, calling this
-        * with *irq==0 is safe. Currently there _are_ no 2 drivers.
-        */
-       rdmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
-       shift = ((cmp == MFGPT_CMP1 ? 0 : 4) + timer % 4) * 4;
-       if (((zsel >> shift) & 0xF) == 2)
-               return -EIO;
-
-       /* Choose IRQ: if none supplied, keep IRQ already set or use default */
-       if (!*irq)
-               *irq = (zsel >> shift) & 0xF;
-       if (!*irq)
-               *irq = MFGPT_DEFAULT_IRQ;
-
-       /* Can't use IRQ if it's 0 (=disabled), 2, or routed to LPC */
-       if (*irq < 1 || *irq == 2 || *irq > 15)
-               return -EIO;
-       rdmsr(MSR_PIC_IRQM_LPC, lpc, dummy);
-       if (lpc & (1 << *irq))
-               return -EIO;
-
-       /* All chosen and checked - go for it */
-       if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
-               return -EIO;
-       if (enable) {
-               zsel = (zsel & ~(0xF << shift)) | (*irq << shift);
-               wrmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
-       }
-
-       return 0;
-}
-
-static int mfgpt_get(int timer)
-{
-       mfgpt_timers[timer].avail = 0;
-       printk(KERN_INFO "geode-mfgpt:  Registered timer %d\n", timer);
-       return timer;
-}
-
-int geode_mfgpt_alloc_timer(int timer, int domain)
-{
-       int i;
-
-       if (timers == -1) {
-               /* timers haven't been detected yet */
-               geode_mfgpt_detect();
-       }
-
-       if (!timers)
-               return -1;
-
-       if (timer >= MFGPT_MAX_TIMERS)
-               return -1;
-
-       if (timer < 0) {
-               /* Try to find an available timer */
-               for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
-                       if (mfgpt_timers[i].avail)
-                               return mfgpt_get(i);
-
-                       if (i == 5 && domain == MFGPT_DOMAIN_WORKING)
-                               break;
-               }
-       } else {
-               /* If they requested a specific timer, try to honor that */
-               if (mfgpt_timers[timer].avail)
-                       return mfgpt_get(timer);
-       }
-
-       /* No timers available - too bad */
-       return -1;
-}
-EXPORT_SYMBOL_GPL(geode_mfgpt_alloc_timer);
-
-
-#ifdef CONFIG_GEODE_MFGPT_TIMER
-
-/*
- * The MFPGT timers on the CS5536 provide us with suitable timers to use
- * as clock event sources - not as good as a HPET or APIC, but certainly
- * better than the PIT.  This isn't a general purpose MFGPT driver, but
- * a simplified one designed specifically to act as a clock event source.
- * For full details about the MFGPT, please consult the CS5536 data sheet.
- */
-
-#include <linux/clocksource.h>
-#include <linux/clockchips.h>
-
-static unsigned int mfgpt_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
-static u16 mfgpt_event_clock;
-
-static int irq;
-static int __init mfgpt_setup(char *str)
-{
-       get_option(&str, &irq);
-       return 1;
-}
-__setup("mfgpt_irq=", mfgpt_setup);
-
-static void mfgpt_disable_timer(u16 clock)
-{
-       /* avoid races by clearing CMP1 and CMP2 unconditionally */
-       geode_mfgpt_write(clock, MFGPT_REG_SETUP, (u16) ~MFGPT_SETUP_CNTEN |
-                       MFGPT_SETUP_CMP1 | MFGPT_SETUP_CMP2);
-}
-
-static int mfgpt_next_event(unsigned long, struct clock_event_device *);
-static void mfgpt_set_mode(enum clock_event_mode, struct clock_event_device *);
-
-static struct clock_event_device mfgpt_clockevent = {
-       .name = "mfgpt-timer",
-       .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
-       .set_mode = mfgpt_set_mode,
-       .set_next_event = mfgpt_next_event,
-       .rating = 250,
-       .cpumask = cpu_all_mask,
-       .shift = 32
-};
-
-static void mfgpt_start_timer(u16 delta)
-{
-       geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta);
-       geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
-
-       geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
-                         MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
-}
-
-static void mfgpt_set_mode(enum clock_event_mode mode,
-                          struct clock_event_device *evt)
-{
-       mfgpt_disable_timer(mfgpt_event_clock);
-
-       if (mode == CLOCK_EVT_MODE_PERIODIC)
-               mfgpt_start_timer(MFGPT_PERIODIC);
-
-       mfgpt_tick_mode = mode;
-}
-
-static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
-{
-       mfgpt_start_timer(delta);
-       return 0;
-}
-
-static irqreturn_t mfgpt_tick(int irq, void *dev_id)
-{
-       u16 val = geode_mfgpt_read(mfgpt_event_clock, MFGPT_REG_SETUP);
-
-       /* See if the interrupt was for us */
-       if (!(val & (MFGPT_SETUP_SETUP  | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1)))
-               return IRQ_NONE;
-
-       /* Turn off the clock (and clear the event) */
-       mfgpt_disable_timer(mfgpt_event_clock);
-
-       if (mfgpt_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
-               return IRQ_HANDLED;
-
-       /* Clear the counter */
-       geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0);
-
-       /* Restart the clock in periodic mode */
-
-       if (mfgpt_tick_mode == CLOCK_EVT_MODE_PERIODIC) {
-               geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
-                                 MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
-       }
-
-       mfgpt_clockevent.event_handler(&mfgpt_clockevent);
-       return IRQ_HANDLED;
-}
-
-static struct irqaction mfgptirq  = {
-       .handler = mfgpt_tick,
-       .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
-       .name = "mfgpt-timer"
-};
-
-int __init mfgpt_timer_setup(void)
-{
-       int timer, ret;
-       u16 val;
-
-       timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
-       if (timer < 0) {
-               printk(KERN_ERR
-                      "mfgpt-timer:  Could not allocate a MFPGT timer\n");
-               return -ENODEV;
-       }
-
-       mfgpt_event_clock = timer;
-
-       /* Set up the IRQ on the MFGPT side */
-       if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, &irq)) {
-               printk(KERN_ERR "mfgpt-timer:  Could not set up IRQ %d\n", irq);
-               return -EIO;
-       }
-
-       /* And register it with the kernel */
-       ret = setup_irq(irq, &mfgptirq);
-
-       if (ret) {
-               printk(KERN_ERR
-                      "mfgpt-timer:  Unable to set up the interrupt.\n");
-               goto err;
-       }
-
-       /* Set the clock scale and enable the event mode for CMP2 */
-       val = MFGPT_SCALE | (3 << 8);
-
-       geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, val);
-
-       /* Set up the clock event */
-       mfgpt_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC,
-                                      mfgpt_clockevent.shift);
-       mfgpt_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
-                       &mfgpt_clockevent);
-       mfgpt_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
-                       &mfgpt_clockevent);
-
-       printk(KERN_INFO
-              "mfgpt-timer:  Registering MFGPT timer %d as a clock event, using IRQ %d\n",
-              timer, irq);
-       clockevents_register_device(&mfgpt_clockevent);
-
-       return 0;
-
-err:
-       geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, &irq);
-       printk(KERN_ERR
-              "mfgpt-timer:  Unable to set up the MFGPT clock source\n");
-       return -EIO;
-}
-
-#endif
index 4006c522adc780a0ce016b43b2d5a7ae93ca0f14..9d1d263f786fdd694513ba8383b635bcdcea319e 100644 (file)
@@ -212,7 +212,7 @@ static int __init olpc_init(void)
        unsigned char *romsig;
 
        /* The ioremap check is dangerous; limit what we run it on */
-       if (!is_geode() || geode_has_vsa2())
+       if (!is_geode() || cs5535_has_vsa2())
                return 0;
 
        spin_lock_init(&ec_lock);
@@ -244,7 +244,7 @@ static int __init olpc_init(void)
                        (unsigned char *) &olpc_platform_info.ecver, 1);
 
        /* check to see if the VSA exists */
-       if (geode_has_vsa2())
+       if (cs5535_has_vsa2())
                olpc_platform_info.flags |= OLPC_F_VSA;
 
        printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
index 201eab63b05fda18954dc111f7c15b44c0eafddf..fda313ebbb03dfc98d5d0f953a1b99846e25dc9a 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/interrupt.h>
 #include <asm/reboot_fixups.h>
 #include <asm/msr.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
 
 static void cs5530a_warm_reset(struct pci_dev *dev)
 {
index 26e434ad373cef3d78f5c676844229143d24eb3d..8a07363417ed8aaa924c10f2c7ae0551968fd971 100644 (file)
@@ -96,6 +96,8 @@ source "drivers/edac/Kconfig"
 
 source "drivers/rtc/Kconfig"
 
+source "drivers/clocksource/Kconfig"
+
 source "drivers/dma/Kconfig"
 
 source "drivers/dca/Kconfig"
index 1fe5536d404f134e53b61324f9350c11080337dc..70122791683d5259ed4afbcc145aaad73aa59cd3 100644 (file)
@@ -173,6 +173,47 @@ static ssize_t node_read_distance(struct sys_device * dev,
 }
 static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL);
 
+#ifdef CONFIG_HUGETLBFS
+/*
+ * hugetlbfs per node attributes registration interface:
+ * When/if hugetlb[fs] subsystem initializes [sometime after this module],
+ * it will register its per node attributes for all online nodes with
+ * memory.  It will also call register_hugetlbfs_with_node(), below, to
+ * register its attribute registration functions with this node driver.
+ * Once these hooks have been initialized, the node driver will call into
+ * the hugetlb module to [un]register attributes for hot-plugged nodes.
+ */
+static node_registration_func_t __hugetlb_register_node;
+static node_registration_func_t __hugetlb_unregister_node;
+
+static inline bool hugetlb_register_node(struct node *node)
+{
+       if (__hugetlb_register_node &&
+                       node_state(node->sysdev.id, N_HIGH_MEMORY)) {
+               __hugetlb_register_node(node);
+               return true;
+       }
+       return false;
+}
+
+static inline void hugetlb_unregister_node(struct node *node)
+{
+       if (__hugetlb_unregister_node)
+               __hugetlb_unregister_node(node);
+}
+
+void register_hugetlbfs_with_node(node_registration_func_t doregister,
+                                 node_registration_func_t unregister)
+{
+       __hugetlb_register_node   = doregister;
+       __hugetlb_unregister_node = unregister;
+}
+#else
+static inline void hugetlb_register_node(struct node *node) {}
+
+static inline void hugetlb_unregister_node(struct node *node) {}
+#endif
+
 
 /*
  * register_node - Setup a sysfs device for a node.
@@ -196,6 +237,8 @@ int register_node(struct node *node, int num, struct node *parent)
                sysdev_create_file(&node->sysdev, &attr_distance);
 
                scan_unevictable_register_node(node);
+
+               hugetlb_register_node(node);
        }
        return error;
 }
@@ -216,6 +259,7 @@ void unregister_node(struct node *node)
        sysdev_remove_file(&node->sysdev, &attr_distance);
 
        scan_unevictable_unregister_node(node);
+       hugetlb_unregister_node(node);          /* no-op, if memoryless node */
 
        sysdev_unregister(&node->sysdev);
 }
@@ -227,26 +271,43 @@ struct node node_devices[MAX_NUMNODES];
  */
 int register_cpu_under_node(unsigned int cpu, unsigned int nid)
 {
-       if (node_online(nid)) {
-               struct sys_device *obj = get_cpu_sysdev(cpu);
-               if (!obj)
-                       return 0;
-               return sysfs_create_link(&node_devices[nid].sysdev.kobj,
-                                        &obj->kobj,
-                                        kobject_name(&obj->kobj));
-        }
+       int ret;
+       struct sys_device *obj;
 
-       return 0;
+       if (!node_online(nid))
+               return 0;
+
+       obj = get_cpu_sysdev(cpu);
+       if (!obj)
+               return 0;
+
+       ret = sysfs_create_link(&node_devices[nid].sysdev.kobj,
+                               &obj->kobj,
+                               kobject_name(&obj->kobj));
+       if (ret)
+               return ret;
+
+       return sysfs_create_link(&obj->kobj,
+                                &node_devices[nid].sysdev.kobj,
+                                kobject_name(&node_devices[nid].sysdev.kobj));
 }
 
 int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
 {
-       if (node_online(nid)) {
-               struct sys_device *obj = get_cpu_sysdev(cpu);
-               if (obj)
-                       sysfs_remove_link(&node_devices[nid].sysdev.kobj,
-                                        kobject_name(&obj->kobj));
-       }
+       struct sys_device *obj;
+
+       if (!node_online(nid))
+               return 0;
+
+       obj = get_cpu_sysdev(cpu);
+       if (!obj)
+               return 0;
+
+       sysfs_remove_link(&node_devices[nid].sysdev.kobj,
+                         kobject_name(&obj->kobj));
+       sysfs_remove_link(&obj->kobj,
+                         kobject_name(&node_devices[nid].sysdev.kobj));
+
        return 0;
 }
 
@@ -268,6 +329,7 @@ static int get_nid_for_pfn(unsigned long pfn)
 /* register memory section under specified node if it spans that node */
 int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
 {
+       int ret;
        unsigned long pfn, sect_start_pfn, sect_end_pfn;
 
        if (!mem_blk)
@@ -284,9 +346,15 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
                        continue;
                if (page_nid != nid)
                        continue;
-               return sysfs_create_link_nowarn(&node_devices[nid].sysdev.kobj,
+               ret = sysfs_create_link_nowarn(&node_devices[nid].sysdev.kobj,
                                        &mem_blk->sysdev.kobj,
                                        kobject_name(&mem_blk->sysdev.kobj));
+               if (ret)
+                       return ret;
+
+               return sysfs_create_link_nowarn(&mem_blk->sysdev.kobj,
+                               &node_devices[nid].sysdev.kobj,
+                               kobject_name(&node_devices[nid].sysdev.kobj));
        }
        /* mem section does not span the specified node */
        return 0;
@@ -295,12 +363,16 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
 /* unregister memory section under all nodes that it spans */
 int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
 {
-       nodemask_t unlinked_nodes;
+       NODEMASK_ALLOC(nodemask_t, unlinked_nodes, GFP_KERNEL);
        unsigned long pfn, sect_start_pfn, sect_end_pfn;
 
-       if (!mem_blk)
+       if (!mem_blk) {
+               NODEMASK_FREE(unlinked_nodes);
                return -EFAULT;
-       nodes_clear(unlinked_nodes);
+       }
+       if (!unlinked_nodes)
+               return -ENOMEM;
+       nodes_clear(*unlinked_nodes);
        sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index);
        sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1;
        for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
@@ -311,11 +383,14 @@ int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
                        continue;
                if (!node_online(nid))
                        continue;
-               if (node_test_and_set(nid, unlinked_nodes))
+               if (node_test_and_set(nid, *unlinked_nodes))
                        continue;
                sysfs_remove_link(&node_devices[nid].sysdev.kobj,
                         kobject_name(&mem_blk->sysdev.kobj));
+               sysfs_remove_link(&mem_blk->sysdev.kobj,
+                        kobject_name(&node_devices[nid].sysdev.kobj));
        }
+       NODEMASK_FREE(unlinked_nodes);
        return 0;
 }
 
@@ -345,9 +420,77 @@ static int link_mem_sections(int nid)
        }
        return err;
 }
-#else
+
+#ifdef CONFIG_HUGETLBFS
+/*
+ * Handle per node hstate attribute [un]registration on transistions
+ * to/from memoryless state.
+ */
+static void node_hugetlb_work(struct work_struct *work)
+{
+       struct node *node = container_of(work, struct node, node_work);
+
+       /*
+        * We only get here when a node transitions to/from memoryless state.
+        * We can detect which transition occurred by examining whether the
+        * node has memory now.  hugetlb_register_node() already check this
+        * so we try to register the attributes.  If that fails, then the
+        * node has transitioned to memoryless, try to unregister the
+        * attributes.
+        */
+       if (!hugetlb_register_node(node))
+               hugetlb_unregister_node(node);
+}
+
+static void init_node_hugetlb_work(int nid)
+{
+       INIT_WORK(&node_devices[nid].node_work, node_hugetlb_work);
+}
+
+static int node_memory_callback(struct notifier_block *self,
+                               unsigned long action, void *arg)
+{
+       struct memory_notify *mnb = arg;
+       int nid = mnb->status_change_nid;
+
+       switch (action) {
+       case MEM_ONLINE:
+       case MEM_OFFLINE:
+               /*
+                * offload per node hstate [un]registration to a work thread
+                * when transitioning to/from memoryless state.
+                */
+               if (nid != NUMA_NO_NODE)
+                       schedule_work(&node_devices[nid].node_work);
+               break;
+
+       case MEM_GOING_ONLINE:
+       case MEM_GOING_OFFLINE:
+       case MEM_CANCEL_ONLINE:
+       case MEM_CANCEL_OFFLINE:
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+#endif /* CONFIG_HUGETLBFS */
+#else  /* !CONFIG_MEMORY_HOTPLUG_SPARSE */
+
 static int link_mem_sections(int nid) { return 0; }
-#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+
+#if !defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || \
+    !defined(CONFIG_HUGETLBFS)
+static inline int node_memory_callback(struct notifier_block *self,
+                               unsigned long action, void *arg)
+{
+       return NOTIFY_OK;
+}
+
+static void init_node_hugetlb_work(int nid) { }
+
+#endif
 
 int register_one_node(int nid)
 {
@@ -371,6 +514,9 @@ int register_one_node(int nid)
 
                /* link memory sections under this node */
                error = link_mem_sections(nid);
+
+               /* initialize work queue for memory hot plug */
+               init_node_hugetlb_work(nid);
        }
 
        return error;
@@ -460,13 +606,17 @@ static int node_states_init(void)
        return err;
 }
 
+#define NODE_CALLBACK_PRI      2       /* lower than SLAB */
 static int __init register_node_type(void)
 {
        int ret;
 
        ret = sysdev_class_register(&node_class);
-       if (!ret)
+       if (!ret) {
                ret = node_states_init();
+               hotplug_memory_notifier(node_memory_callback,
+                                       NODE_CALLBACK_PRI);
+       }
 
        /*
         * Note:  we're not going to unregister the node class if we fail
index 5c01f747571b9fe34f54de72d5fa5f03641c17d7..3266b4f65daa37dec3110ff2220f8d4b7c1bfb42 100644 (file)
@@ -3497,6 +3497,9 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
            ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
                return -EPERM;
 
+       if (WARN_ON(size < 0 || size > sizeof(inparam)))
+               return -EINVAL;
+
        /* copyin */
        CLEARSTRUCT(&inparam);
        if (_IOC_DIR(cmd) & _IOC_WRITE)
@@ -4162,7 +4165,7 @@ static int floppy_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops floppy_pm_ops = {
+static const struct dev_pm_ops floppy_pm_ops = {
        .resume = floppy_resume,
        .restore = floppy_resume,
 };
index b8a5d654d3d043e494f6bab085552ab64df24f9f..fe62bd0e17b751f55e359addc54ee4569a4f7488 100644 (file)
@@ -931,7 +931,7 @@ static struct hv_ops hvc_iucv_ops = {
 };
 
 /* Suspend / resume device operations */
-static struct dev_pm_ops hvc_iucv_pm_ops = {
+static const struct dev_pm_ops hvc_iucv_pm_ops = {
        .freeze   = hvc_iucv_pm_freeze,
        .thaw     = hvc_iucv_pm_restore_thaw,
        .restore  = hvc_iucv_pm_restore_thaw,
index fba76fb55abfd302bbd87e6433ed858b8b5d2c4d..be832b6f8279919831df235a11738f7398668722 100644 (file)
 # include <linux/efi.h>
 #endif
 
+static inline unsigned long size_inside_page(unsigned long start,
+                                            unsigned long size)
+{
+       unsigned long sz;
+
+       sz = PAGE_SIZE - (start & (PAGE_SIZE - 1));
+
+       return min(sz, size);
+}
+
 /*
  * Architectures vary in how they handle caching for addresses
  * outside of main memory.
@@ -126,9 +136,7 @@ static ssize_t read_mem(struct file * file, char __user * buf,
 #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
        /* we don't have page 0 mapped on sparc and m68k.. */
        if (p < PAGE_SIZE) {
-               sz = PAGE_SIZE - p;
-               if (sz > count) 
-                       sz = count; 
+               sz = size_inside_page(p, count);
                if (sz > 0) {
                        if (clear_user(buf, sz))
                                return -EFAULT;
@@ -141,15 +149,9 @@ static ssize_t read_mem(struct file * file, char __user * buf,
 #endif
 
        while (count > 0) {
-               /*
-                * Handle first page in case it's not aligned
-                */
-               if (-p & (PAGE_SIZE - 1))
-                       sz = -p & (PAGE_SIZE - 1);
-               else
-                       sz = PAGE_SIZE;
+               unsigned long remaining;
 
-               sz = min_t(unsigned long, sz, count);
+               sz = size_inside_page(p, count);
 
                if (!range_is_allowed(p >> PAGE_SHIFT, count))
                        return -EPERM;
@@ -163,12 +165,10 @@ static ssize_t read_mem(struct file * file, char __user * buf,
                if (!ptr)
                        return -EFAULT;
 
-               if (copy_to_user(buf, ptr, sz)) {
-                       unxlate_dev_mem_ptr(p, ptr);
-                       return -EFAULT;
-               }
-
+               remaining = copy_to_user(buf, ptr, sz);
                unxlate_dev_mem_ptr(p, ptr);
+               if (remaining)
+                       return -EFAULT;
 
                buf += sz;
                p += sz;
@@ -196,9 +196,7 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
 #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
        /* we don't have page 0 mapped on sparc and m68k.. */
        if (p < PAGE_SIZE) {
-               unsigned long sz = PAGE_SIZE - p;
-               if (sz > count)
-                       sz = count;
+               sz = size_inside_page(p, count);
                /* Hmm. Do something? */
                buf += sz;
                p += sz;
@@ -208,15 +206,7 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
 #endif
 
        while (count > 0) {
-               /*
-                * Handle first page in case it's not aligned
-                */
-               if (-p & (PAGE_SIZE - 1))
-                       sz = -p & (PAGE_SIZE - 1);
-               else
-                       sz = PAGE_SIZE;
-
-               sz = min_t(unsigned long, sz, count);
+               sz = size_inside_page(p, count);
 
                if (!range_is_allowed(p >> PAGE_SHIFT, sz))
                        return -EPERM;
@@ -234,16 +224,14 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
                }
 
                copied = copy_from_user(ptr, buf, sz);
+               unxlate_dev_mem_ptr(p, ptr);
                if (copied) {
                        written += sz - copied;
-                       unxlate_dev_mem_ptr(p, ptr);
                        if (written)
                                break;
                        return -EFAULT;
                }
 
-               unxlate_dev_mem_ptr(p, ptr);
-
                buf += sz;
                p += sz;
                count -= sz;
@@ -417,27 +405,18 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
 #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
                /* we don't have page 0 mapped on sparc and m68k.. */
                if (p < PAGE_SIZE && low_count > 0) {
-                       size_t tmp = PAGE_SIZE - p;
-                       if (tmp > low_count) tmp = low_count;
-                       if (clear_user(buf, tmp))
+                       sz = size_inside_page(p, low_count);
+                       if (clear_user(buf, sz))
                                return -EFAULT;
-                       buf += tmp;
-                       p += tmp;
-                       read += tmp;
-                       low_count -= tmp;
-                       count -= tmp;
+                       buf += sz;
+                       p += sz;
+                       read += sz;
+                       low_count -= sz;
+                       count -= sz;
                }
 #endif
                while (low_count > 0) {
-                       /*
-                        * Handle first page in case it's not aligned
-                        */
-                       if (-p & (PAGE_SIZE - 1))
-                               sz = -p & (PAGE_SIZE - 1);
-                       else
-                               sz = PAGE_SIZE;
-
-                       sz = min_t(unsigned long, sz, low_count);
+                       sz = size_inside_page(p, low_count);
 
                        /*
                         * On ia64 if a page has been mapped somewhere as
@@ -461,21 +440,18 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
                if (!kbuf)
                        return -ENOMEM;
                while (count > 0) {
-                       int len = count;
-
-                       if (len > PAGE_SIZE)
-                               len = PAGE_SIZE;
-                       len = vread(kbuf, (char *)p, len);
-                       if (!len)
+                       sz = size_inside_page(p, count);
+                       sz = vread(kbuf, (char *)p, sz);
+                       if (!sz)
                                break;
-                       if (copy_to_user(buf, kbuf, len)) {
+                       if (copy_to_user(buf, kbuf, sz)) {
                                free_page((unsigned long)kbuf);
                                return -EFAULT;
                        }
-                       count -= len;
-                       buf += len;
-                       read += len;
-                       p += len;
+                       count -= sz;
+                       buf += sz;
+                       read += sz;
+                       p += sz;
                }
                free_page((unsigned long)kbuf);
        }
@@ -485,7 +461,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
 
 
 static inline ssize_t
-do_write_kmem(void *p, unsigned long realp, const char __user * buf,
+do_write_kmem(unsigned long p, const char __user *buf,
              size_t count, loff_t *ppos)
 {
        ssize_t written, sz;
@@ -494,14 +470,11 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
        written = 0;
 #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
        /* we don't have page 0 mapped on sparc and m68k.. */
-       if (realp < PAGE_SIZE) {
-               unsigned long sz = PAGE_SIZE - realp;
-               if (sz > count)
-                       sz = count;
+       if (p < PAGE_SIZE) {
+               sz = size_inside_page(p, count);
                /* Hmm. Do something? */
                buf += sz;
                p += sz;
-               realp += sz;
                count -= sz;
                written += sz;
        }
@@ -509,22 +482,15 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
 
        while (count > 0) {
                char *ptr;
-               /*
-                * Handle first page in case it's not aligned
-                */
-               if (-realp & (PAGE_SIZE - 1))
-                       sz = -realp & (PAGE_SIZE - 1);
-               else
-                       sz = PAGE_SIZE;
 
-               sz = min_t(unsigned long, sz, count);
+               sz = size_inside_page(p, count);
 
                /*
                 * On ia64 if a page has been mapped somewhere as
                 * uncached, then it must also be accessed uncached
                 * by the kernel or data corruption may occur
                 */
-               ptr = xlate_dev_kmem_ptr(p);
+               ptr = xlate_dev_kmem_ptr((char *)p);
 
                copied = copy_from_user(ptr, buf, sz);
                if (copied) {
@@ -535,7 +501,6 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf,
                }
                buf += sz;
                p += sz;
-               realp += sz;
                count -= sz;
                written += sz;
        }
@@ -554,19 +519,14 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
        unsigned long p = *ppos;
        ssize_t wrote = 0;
        ssize_t virtr = 0;
-       ssize_t written;
        char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
 
        if (p < (unsigned long) high_memory) {
-
-               wrote = count;
-               if (count > (unsigned long) high_memory - p)
-                       wrote = (unsigned long) high_memory - p;
-
-               written = do_write_kmem((void*)p, p, buf, wrote, ppos);
-               if (written != wrote)
-                       return written;
-               wrote = written;
+               unsigned long to_write = min_t(unsigned long, count,
+                                              (unsigned long)high_memory - p);
+               wrote = do_write_kmem(p, buf, to_write, ppos);
+               if (wrote != to_write)
+                       return wrote;
                p += wrote;
                buf += wrote;
                count -= wrote;
@@ -577,24 +537,21 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
                if (!kbuf)
                        return wrote ? wrote : -ENOMEM;
                while (count > 0) {
-                       int len = count;
-
-                       if (len > PAGE_SIZE)
-                               len = PAGE_SIZE;
-                       if (len) {
-                               written = copy_from_user(kbuf, buf, len);
-                               if (written) {
-                                       if (wrote + virtr)
-                                               break;
-                                       free_page((unsigned long)kbuf);
-                                       return -EFAULT;
-                               }
+                       unsigned long sz = size_inside_page(p, count);
+                       unsigned long n;
+
+                       n = copy_from_user(kbuf, buf, sz);
+                       if (n) {
+                               if (wrote + virtr)
+                                       break;
+                               free_page((unsigned long)kbuf);
+                               return -EFAULT;
                        }
-                       len = vwrite(kbuf, (char *)p, len);
-                       count -= len;
-                       buf += len;
-                       virtr += len;
-                       p += len;
+                       sz = vwrite(kbuf, (char *)p, sz);
+                       count -= sz;
+                       buf += sz;
+                       virtr += sz;
+                       p += sz;
                }
                free_page((unsigned long)kbuf);
        }
index 96f1cd086dd245911cb9be85d93cf175c9b97c6a..94a136e96c06c69b51aea6c31162606382ff6330 100644 (file)
@@ -60,9 +60,7 @@ static DEFINE_MUTEX(misc_mtx);
  * Assigned numbers, used for dynamic minors
  */
 #define DYNAMIC_MINORS 64 /* like dynamic majors */
-static unsigned char misc_minors[DYNAMIC_MINORS / 8];
-
-extern int pmu_device_init(void);
+static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
 
 #ifdef CONFIG_PROC_FS
 static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
@@ -198,24 +196,23 @@ int misc_register(struct miscdevice * misc)
        }
 
        if (misc->minor == MISC_DYNAMIC_MINOR) {
-               int i = DYNAMIC_MINORS;
-               while (--i >= 0)
-                       if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
-                               break;
-               if (i<0) {
+               int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
+               if (i >= DYNAMIC_MINORS) {
                        mutex_unlock(&misc_mtx);
                        return -EBUSY;
                }
-               misc->minor = i;
+               misc->minor = DYNAMIC_MINORS - i - 1;
+               set_bit(i, misc_minors);
        }
 
-       if (misc->minor < DYNAMIC_MINORS)
-               misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
        dev = MKDEV(MISC_MAJOR, misc->minor);
 
        misc->this_device = device_create(misc_class, misc->parent, dev,
                                          misc, "%s", misc->name);
        if (IS_ERR(misc->this_device)) {
+               int i = DYNAMIC_MINORS - misc->minor - 1;
+               if (i < DYNAMIC_MINORS && i >= 0)
+                       clear_bit(i, misc_minors);
                err = PTR_ERR(misc->this_device);
                goto out;
        }
@@ -242,7 +239,7 @@ int misc_register(struct miscdevice * misc)
 
 int misc_deregister(struct miscdevice *misc)
 {
-       int i = misc->minor;
+       int i = DYNAMIC_MINORS - misc->minor - 1;
 
        if (list_empty(&misc->list))
                return -EINVAL;
@@ -250,9 +247,8 @@ int misc_deregister(struct miscdevice *misc)
        mutex_lock(&misc_mtx);
        list_del(&misc->list);
        device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
-       if (i < DYNAMIC_MINORS && i>0) {
-               misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
-       }
+       if (i < DYNAMIC_MINORS && i >= 0)
+               clear_bit(i, misc_minors);
        mutex_unlock(&misc_mtx);
        return 0;
 }
index dcd08635cf1b160e57e5ee250c9a5ae87eeee3d6..8258982b49ec3ab1551416ccd8e558d2f1c5e6df 100644 (file)
@@ -1245,12 +1245,8 @@ static int proc_do_uuid(ctl_table *table, int write,
        if (uuid[8] == 0)
                generate_random_uuid(uuid);
 
-       sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
-               "%02x%02x%02x%02x%02x%02x",
-               uuid[0],  uuid[1],  uuid[2],  uuid[3],
-               uuid[4],  uuid[5],  uuid[6],  uuid[7],
-               uuid[8],  uuid[9],  uuid[10], uuid[11],
-               uuid[12], uuid[13], uuid[14], uuid[15]);
+       sprintf(buf, "%pU", uuid);
+
        fake_table.data = buf;
        fake_table.maxlen = sizeof(buf);
 
@@ -1310,7 +1306,7 @@ ctl_table random_table[] = {
 
 /********************************************************************
  *
- * Random funtions for networking
+ * Random functions for networking
  *
  ********************************************************************/
 
index 1e3d728dbf7e15a9c15664562c1057a376f33434..e43fbc66aef0094557defaa474929675b97034a2 100644 (file)
@@ -184,12 +184,10 @@ static DECLARE_WORK(console_work, console_callback);
  * fg_console is the current virtual console,
  * last_console is the last used one,
  * want_console is the console we want to switch to,
- * kmsg_redirect is the console for kernel messages,
  */
 int fg_console;
 int last_console;
 int want_console = -1;
-int kmsg_redirect;
 
 /*
  * For each existing display, we have a pointer to console currently visible
@@ -2434,6 +2432,37 @@ struct tty_driver *console_driver;
 
 #ifdef CONFIG_VT_CONSOLE
 
+/**
+ * vt_kmsg_redirect() - Sets/gets the kernel message console
+ * @new:       The new virtual terminal number or -1 if the console should stay
+ *             unchanged
+ *
+ * By default, the kernel messages are always printed on the current virtual
+ * console. However, the user may modify that default with the
+ * TIOCL_SETKMSGREDIRECT ioctl call.
+ *
+ * This function sets the kernel message console to be @new. It returns the old
+ * virtual console number. The virtual terminal number 0 (both as parameter and
+ * return value) means no redirection (i.e. always printed on the currently
+ * active console).
+ *
+ * The parameter -1 means that only the current console is returned, but the
+ * value is not modified. You may use the macro vt_get_kmsg_redirect() in that
+ * case to make the code more understandable.
+ *
+ * When the kernel is compiled without CONFIG_VT_CONSOLE, this function ignores
+ * the parameter and always returns 0.
+ */
+int vt_kmsg_redirect(int new)
+{
+       static int kmsg_con;
+
+       if (new != -1)
+               return xchg(&kmsg_con, new);
+       else
+               return kmsg_con;
+}
+
 /*
  *     Console on virtual terminal
  *
@@ -2448,6 +2477,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
        const ushort *start;
        ushort cnt = 0;
        ushort myx;
+       int kmsg_console;
 
        /* console busy or not yet initialized */
        if (!printable)
@@ -2455,8 +2485,9 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
        if (!spin_trylock(&printing_lock))
                return;
 
-       if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))
-               vc = vc_cons[kmsg_redirect - 1].d;
+       kmsg_console = vt_get_kmsg_redirect();
+       if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
+               vc = vc_cons[kmsg_console - 1].d;
 
        /* read `x' only after setting currcons properly (otherwise
           the `x' macro will read the x of the foreground console). */
@@ -2613,7 +2644,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        ret = set_vesa_blanking(p);
                        break;
                case TIOCL_GETKMSGREDIRECT:
-                       data = kmsg_redirect;
+                       data = vt_get_kmsg_redirect();
                        ret = __put_user(data, p);
                        break;
                case TIOCL_SETKMSGREDIRECT:
@@ -2623,7 +2654,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                                if (get_user(data, p+1))
                                        ret = -EFAULT;
                                else
-                                       kmsg_redirect = data;
+                                       vt_kmsg_redirect(data);
                        }
                        break;
                case TIOCL_GETFGCONSOLE:
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
new file mode 100644 (file)
index 0000000..08f726c
--- /dev/null
@@ -0,0 +1,9 @@
+config CS5535_CLOCK_EVENT_SRC
+       tristate "CS5535/CS5536 high-res timer (MFGPT) events"
+       depends on GENERIC_TIME && GENERIC_CLOCKEVENTS && CS5535_MFGPT
+       help
+         This driver provides a clock event source based on the MFGPT
+         timer(s) in the CS5535 and CS5536 companion chips.
+         MFGPTs have a better resolution and max interval than the
+         generic PIT, and are suitable for use as high-res timers.
+
index eef216f7f61d6a80ac57f435003b92f45b2b5579..be61ece6330bee0965ed3e8de273daa38946b984 100644 (file)
@@ -2,6 +2,7 @@ obj-$(CONFIG_ATMEL_TCB_CLKSRC)  += tcb_clksrc.o
 obj-$(CONFIG_X86_CYCLONE_TIMER)        += cyclone.o
 obj-$(CONFIG_X86_PM_TIMER)     += acpi_pm.o
 obj-$(CONFIG_SCx200HR_TIMER)   += scx200_hrt.o
+obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC)   += cs5535-clockevt.o
 obj-$(CONFIG_SH_TIMER_CMT)     += sh_cmt.o
 obj-$(CONFIG_SH_TIMER_MTU2)    += sh_mtu2.o
 obj-$(CONFIG_SH_TIMER_TMU)     += sh_tmu.o
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
new file mode 100644 (file)
index 0000000..27d20fa
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Clock event driver for the CS5535/CS5536
+ *
+ * Copyright (C) 2006, Advanced Micro Devices, Inc.
+ * Copyright (C) 2007  Andres Salomon <dilinger@debian.org>
+ * Copyright (C) 2009  Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/cs5535.h>
+#include <linux/clockchips.h>
+
+#define DRV_NAME "cs5535-clockevt"
+
+static int timer_irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ;
+module_param_named(irq, timer_irq, int, 0644);
+MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
+
+/*
+ * We are using the 32.768kHz input clock - it's the only one that has the
+ * ranges we find desirable.  The following table lists the suitable
+ * divisors and the associated Hz, minimum interval and the maximum interval:
+ *
+ *  Divisor   Hz      Min Delta (s)  Max Delta (s)
+ *   1        32768   .00048828125      2.000
+ *   2        16384   .0009765625       4.000
+ *   4         8192   .001953125        8.000
+ *   8         4096   .00390625        16.000
+ *   16        2048   .0078125         32.000
+ *   32        1024   .015625          64.000
+ *   64         512   .03125          128.000
+ *  128         256   .0625           256.000
+ *  256         128   .125            512.000
+ */
+
+static unsigned int cs5535_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
+static struct cs5535_mfgpt_timer *cs5535_event_clock;
+
+/* Selected from the table above */
+
+#define MFGPT_DIVISOR 16
+#define MFGPT_SCALE  4     /* divisor = 2^(scale) */
+#define MFGPT_HZ  (32768 / MFGPT_DIVISOR)
+#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
+
+/*
+ * The MFPGT timers on the CS5536 provide us with suitable timers to use
+ * as clock event sources - not as good as a HPET or APIC, but certainly
+ * better than the PIT.  This isn't a general purpose MFGPT driver, but
+ * a simplified one designed specifically to act as a clock event source.
+ * For full details about the MFGPT, please consult the CS5536 data sheet.
+ */
+
+static void disable_timer(struct cs5535_mfgpt_timer *timer)
+{
+       /* avoid races by clearing CMP1 and CMP2 unconditionally */
+       cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
+                       (uint16_t) ~MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP1 |
+                               MFGPT_SETUP_CMP2);
+}
+
+static void start_timer(struct cs5535_mfgpt_timer *timer, uint16_t delta)
+{
+       cs5535_mfgpt_write(timer, MFGPT_REG_CMP2, delta);
+       cs5535_mfgpt_write(timer, MFGPT_REG_COUNTER, 0);
+
+       cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
+                       MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
+}
+
+static void mfgpt_set_mode(enum clock_event_mode mode,
+               struct clock_event_device *evt)
+{
+       disable_timer(cs5535_event_clock);
+
+       if (mode == CLOCK_EVT_MODE_PERIODIC)
+               start_timer(cs5535_event_clock, MFGPT_PERIODIC);
+
+       cs5535_tick_mode = mode;
+}
+
+static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
+{
+       start_timer(cs5535_event_clock, delta);
+       return 0;
+}
+
+static struct clock_event_device cs5535_clockevent = {
+       .name = DRV_NAME,
+       .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+       .set_mode = mfgpt_set_mode,
+       .set_next_event = mfgpt_next_event,
+       .rating = 250,
+       .cpumask = cpu_all_mask,
+       .shift = 32
+};
+
+static irqreturn_t mfgpt_tick(int irq, void *dev_id)
+{
+       uint16_t val = cs5535_mfgpt_read(cs5535_event_clock, MFGPT_REG_SETUP);
+
+       /* See if the interrupt was for us */
+       if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1)))
+               return IRQ_NONE;
+
+       /* Turn off the clock (and clear the event) */
+       disable_timer(cs5535_event_clock);
+
+       if (cs5535_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
+               return IRQ_HANDLED;
+
+       /* Clear the counter */
+       cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_COUNTER, 0);
+
+       /* Restart the clock in periodic mode */
+
+       if (cs5535_tick_mode == CLOCK_EVT_MODE_PERIODIC)
+               cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP,
+                               MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
+
+       cs5535_clockevent.event_handler(&cs5535_clockevent);
+       return IRQ_HANDLED;
+}
+
+static struct irqaction mfgptirq  = {
+       .handler = mfgpt_tick,
+       .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
+       .name = DRV_NAME,
+};
+
+static int __init cs5535_mfgpt_init(void)
+{
+       struct cs5535_mfgpt_timer *timer;
+       int ret;
+       uint16_t val;
+
+       timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
+       if (!timer) {
+               printk(KERN_ERR DRV_NAME ": Could not allocate MFPGT timer\n");
+               return -ENODEV;
+       }
+       cs5535_event_clock = timer;
+
+       /* Set up the IRQ on the MFGPT side */
+       if (cs5535_mfgpt_setup_irq(timer, MFGPT_CMP2, &timer_irq)) {
+               printk(KERN_ERR DRV_NAME ": Could not set up IRQ %d\n",
+                               timer_irq);
+               return -EIO;
+       }
+
+       /* And register it with the kernel */
+       ret = setup_irq(timer_irq, &mfgptirq);
+       if (ret) {
+               printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n");
+               goto err;
+       }
+
+       /* Set the clock scale and enable the event mode for CMP2 */
+       val = MFGPT_SCALE | (3 << 8);
+
+       cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val);
+
+       /* Set up the clock event */
+       cs5535_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC,
+                       cs5535_clockevent.shift);
+       cs5535_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
+                       &cs5535_clockevent);
+       cs5535_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
+                       &cs5535_clockevent);
+
+       printk(KERN_INFO DRV_NAME
+               ": Registering MFGPT timer as a clock event, using IRQ %d\n",
+               timer_irq);
+       clockevents_register_device(&cs5535_clockevent);
+
+       return 0;
+
+err:
+       cs5535_mfgpt_release_irq(cs5535_event_clock, MFGPT_CMP2, &timer_irq);
+       printk(KERN_ERR DRV_NAME ": Unable to set up the MFGPT clock source\n");
+       return -EIO;
+}
+
+module_init(cs5535_mfgpt_init);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
+MODULE_DESCRIPTION("CS5535/CS5536 MFGPT clock event driver");
+MODULE_LICENSE("GPL");
index a4bec3f919aa7cb98ddaf8f6f7121dc4d2f4781a..1c1ceb4f218f6299716b6318c3dd593d7b9f3b94 100644 (file)
@@ -69,9 +69,6 @@ static int ladder_select_state(struct cpuidle_device *dev)
        int last_residency, last_idx = ldev->last_state_idx;
        int latency_req = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY);
 
-       if (unlikely(!ldev))
-               return 0;
-
        /* Special case when user has set very strict latency requirement */
        if (unlikely(latency_req == 0)) {
                ladder_do_selection(ldev, last_idx, 0);
index c52ac9efd0bf04547cf7882259daa369a31bf0fb..f15112569c1d686b5a1f6ea9213a7fabd6065f3c 100644 (file)
@@ -1188,7 +1188,7 @@ static int at_dma_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops at_dma_dev_pm_ops = {
+static const struct dev_pm_ops at_dma_dev_pm_ops = {
        .suspend_noirq = at_dma_suspend_noirq,
        .resume_noirq = at_dma_resume_noirq,
 };
index 2eea823516a7aa9797f7908074c4ec68bd564b81..285bed0fe17bbae3e672a9b4d1cebe844cb4888a 100644 (file)
@@ -1427,7 +1427,7 @@ static int dw_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops dw_dev_pm_ops = {
+static const struct dev_pm_ops dw_dev_pm_ops = {
        .suspend_noirq = dw_suspend_noirq,
        .resume_noirq = dw_resume_noirq,
 };
index fb6bb64e88619a729ab9b2cd1fa4574fd29be16e..3ebc61067e548d407af798687db075ae15003385 100644 (file)
@@ -1313,7 +1313,7 @@ static int txx9dmac_resume_noirq(struct device *dev)
 
 }
 
-static struct dev_pm_ops txx9dmac_dev_pm_ops = {
+static const struct dev_pm_ops txx9dmac_dev_pm_ops = {
        .suspend_noirq = txx9dmac_suspend_noirq,
        .resume_noirq = txx9dmac_resume_noirq,
 };
index ebb9e51deb0cd86acb38ea5b0180d7c1b16a862a..1b03ba1d083497d5c1fe2597b2f84ad88399cc1d 100644 (file)
@@ -7,7 +7,7 @@ menu "Firmware Drivers"
 
 config EDD
        tristate "BIOS Enhanced Disk Drive calls determine boot disk"
-       depends on !IA64
+       depends on X86
        help
          Say Y or M here if you want to enable BIOS Enhanced Disk Drive
          Services real mode BIOS calls to determine which disk
@@ -28,7 +28,7 @@ config EDD_OFF
 
 config FIRMWARE_MEMMAP
     bool "Add firmware-provided memory map to sysfs" if EMBEDDED
-    default (X86_64 || X86_32)
+    default X86
     help
       Add the firmware-provided (unmodified) memory map to /sys/firmware/memmap.
       That memory map is used for example by kexec to set up parameter area
index 3a2ccb09e2f85d0cb6c9344dc92d8579e34df590..31b983d9462c7c9f5a01b87fa0a449076bd12aa1 100644 (file)
@@ -169,10 +169,7 @@ static void __init dmi_save_uuid(const struct dmi_header *dm, int slot, int inde
        if (!s)
                return;
 
-       sprintf(s,
-               "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
-               d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
-               d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+       sprintf(s, "%pUB", d);
 
         dmi_ident[slot] = s;
 }
index 2ad0128c63c6995ab11fa7f3e2896bad968fe9f3..57ca339924efa5fceb09018b4f2096609a3194ab 100644 (file)
@@ -174,6 +174,16 @@ config GPIO_ADP5520
 
 comment "PCI GPIO expanders:"
 
+config GPIO_CS5535
+       tristate "AMD CS5535/CS5536 GPIO support"
+       depends on PCI && !CS5535_GPIO
+       help
+         The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that
+         can be used for quite a number of things.  The CS5535/6 is found on
+         AMD Geode and Lemote Yeeloong devices.
+
+         If unsure, say N.
+
 config GPIO_BT8XX
        tristate "BT8XX GPIO abuser"
        depends on PCI && VIDEO_BT848=n
index 00a532c9a1e294afcf6c6224d1b32e6bdda8068e..270b6d7839f5dde53e7d02b01e7065bdf08011d4 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_PL061)      += pl061.o
 obj-$(CONFIG_GPIO_TWL4030)     += twl4030-gpio.o
 obj-$(CONFIG_GPIO_UCB1400)     += ucb1400_gpio.o
 obj-$(CONFIG_GPIO_XILINX)      += xilinx_gpio.o
+obj-$(CONFIG_GPIO_CS5535)      += cs5535-gpio.o
 obj-$(CONFIG_GPIO_BT8XX)       += bt8xxgpio.o
 obj-$(CONFIG_GPIO_VR41XX)      += vr41xx_giu.o
 obj-$(CONFIG_GPIO_WM831X)      += wm831x-gpio.o
diff --git a/drivers/gpio/cs5535-gpio.c b/drivers/gpio/cs5535-gpio.c
new file mode 100644 (file)
index 0000000..0fdbe94
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * AMD CS5535/CS5536 GPIO driver
+ * Copyright (C) 2006  Advanced Micro Devices, Inc.
+ * Copyright (C) 2007-2009  Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/cs5535.h>
+
+#define DRV_NAME "cs5535-gpio"
+#define GPIO_BAR 1
+
+/*
+ * Some GPIO pins
+ *  31-29,23 : reserved (always mask out)
+ *  28       : Power Button
+ *  26       : PME#
+ *  22-16    : LPC
+ *  14,15    : SMBus
+ *  9,8      : UART1
+ *  7        : PCI INTB
+ *  3,4      : UART2/DDC
+ *  2        : IDE_IRQ0
+ *  1        : AC_BEEP
+ *  0        : PCI INTA
+ *
+ * If a mask was not specified, allow all except
+ * reserved and Power Button
+ */
+#define GPIO_DEFAULT_MASK 0x0F7FFFFF
+
+static ulong mask = GPIO_DEFAULT_MASK;
+module_param_named(mask, mask, ulong, 0444);
+MODULE_PARM_DESC(mask, "GPIO channel mask.");
+
+static struct cs5535_gpio_chip {
+       struct gpio_chip chip;
+       resource_size_t base;
+
+       struct pci_dev *pdev;
+       spinlock_t lock;
+} cs5535_gpio_chip;
+
+/*
+ * The CS5535/CS5536 GPIOs support a number of extra features not defined
+ * by the gpio_chip API, so these are exported.  For a full list of the
+ * registers, see include/linux/cs5535.h.
+ */
+
+static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
+               unsigned int reg)
+{
+       if (offset < 16)
+               /* low bank register */
+               outl(1 << offset, chip->base + reg);
+       else
+               /* high bank register */
+               outl(1 << (offset - 16), chip->base + 0x80 + reg);
+}
+
+void cs5535_gpio_set(unsigned offset, unsigned int reg)
+{
+       struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       __cs5535_gpio_set(chip, offset, reg);
+       spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_set);
+
+static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
+               unsigned int reg)
+{
+       if (offset < 16)
+               /* low bank register */
+               outl(1 << (offset + 16), chip->base + reg);
+       else
+               /* high bank register */
+               outl(1 << offset, chip->base + 0x80 + reg);
+}
+
+void cs5535_gpio_clear(unsigned offset, unsigned int reg)
+{
+       struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       __cs5535_gpio_clear(chip, offset, reg);
+       spin_unlock_irqrestore(&chip->lock, flags);
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
+
+int cs5535_gpio_isset(unsigned offset, unsigned int reg)
+{
+       struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
+       unsigned long flags;
+       long val;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       if (offset < 16)
+               /* low bank register */
+               val = inl(chip->base + reg);
+       else {
+               /* high bank register */
+               val = inl(chip->base + 0x80 + reg);
+               offset -= 16;
+       }
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       return (val & (1 << offset)) ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
+
+/*
+ * Generic gpio_chip API support.
+ */
+
+static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
+{
+       struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+
+       /* check if this pin is available */
+       if ((mask & (1 << offset)) == 0) {
+               dev_info(&chip->pdev->dev,
+                       "pin %u is not available (check mask)\n", offset);
+               spin_unlock_irqrestore(&chip->lock, flags);
+               return -EINVAL;
+       }
+
+       /* disable output aux 1 & 2 on this pin */
+       __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
+       __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
+
+       /* disable input aux 1 on this pin */
+       __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
+
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       return 0;
+}
+
+static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       return cs5535_gpio_isset(offset, GPIO_OUTPUT_VAL);
+}
+
+static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+       if (val)
+               cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
+       else
+               cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
+}
+
+static int chip_direction_input(struct gpio_chip *c, unsigned offset)
+{
+       struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       return 0;
+}
+
+static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
+{
+       struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+
+       __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
+       if (val)
+               __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
+       else
+               __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
+
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       return 0;
+}
+
+static char *cs5535_gpio_names[] = {
+       "GPIO0", "GPIO1", "GPIO2", "GPIO3",
+       "GPIO4", "GPIO5", "GPIO6", "GPIO7",
+       "GPIO8", "GPIO9", "GPIO10", "GPIO11",
+       "GPIO12", "GPIO13", "GPIO14", "GPIO15",
+       "GPIO16", "GPIO17", "GPIO18", "GPIO19",
+       "GPIO20", "GPIO21", "GPIO22", NULL,
+       "GPIO24", "GPIO25", "GPIO26", "GPIO27",
+       "GPIO28", NULL, NULL, NULL,
+};
+
+static struct cs5535_gpio_chip cs5535_gpio_chip = {
+       .chip = {
+               .owner = THIS_MODULE,
+               .label = DRV_NAME,
+
+               .base = 0,
+               .ngpio = 32,
+               .names = cs5535_gpio_names,
+               .request = chip_gpio_request,
+
+               .get = chip_gpio_get,
+               .set = chip_gpio_set,
+
+               .direction_input = chip_direction_input,
+               .direction_output = chip_direction_output,
+       },
+};
+
+static int __init cs5535_gpio_probe(struct pci_dev *pdev,
+               const struct pci_device_id *pci_id)
+{
+       int err;
+       ulong mask_orig = mask;
+
+       /* There are two ways to get the GPIO base address; one is by
+        * fetching it from MSR_LBAR_GPIO, the other is by reading the
+        * PCI BAR info.  The latter method is easier (especially across
+        * different architectures), so we'll stick with that for now.  If
+        * it turns out to be unreliable in the face of crappy BIOSes, we
+        * can always go back to using MSRs.. */
+
+       err = pci_enable_device_io(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "can't enable device IO\n");
+               goto done;
+       }
+
+       err = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
+       if (err) {
+               dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
+               goto done;
+       }
+
+       /* set up the driver-specific struct */
+       cs5535_gpio_chip.base = pci_resource_start(pdev, GPIO_BAR);
+       cs5535_gpio_chip.pdev = pdev;
+       spin_lock_init(&cs5535_gpio_chip.lock);
+
+       dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", GPIO_BAR,
+                       (unsigned long long) cs5535_gpio_chip.base);
+
+       /* mask out reserved pins */
+       mask &= 0x1F7FFFFF;
+
+       /* do not allow pin 28, Power Button, as there's special handling
+        * in the PMC needed. (note 12, p. 48) */
+       mask &= ~(1 << 28);
+
+       if (mask_orig != mask)
+               dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
+                               mask_orig, mask);
+
+       /* finally, register with the generic GPIO API */
+       err = gpiochip_add(&cs5535_gpio_chip.chip);
+       if (err)
+               goto release_region;
+
+       dev_info(&pdev->dev, DRV_NAME ": GPIO support successfully loaded.\n");
+       return 0;
+
+release_region:
+       pci_release_region(pdev, GPIO_BAR);
+done:
+       return err;
+}
+
+static void __exit cs5535_gpio_remove(struct pci_dev *pdev)
+{
+       int err;
+
+       err = gpiochip_remove(&cs5535_gpio_chip.chip);
+       if (err) {
+               /* uhh? */
+               dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
+       }
+       pci_release_region(pdev, GPIO_BAR);
+}
+
+static struct pci_device_id cs5535_gpio_pci_tbl[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+       { 0, },
+};
+MODULE_DEVICE_TABLE(pci, cs5535_gpio_pci_tbl);
+
+/*
+ * We can't use the standard PCI driver registration stuff here, since
+ * that allows only one driver to bind to each PCI device (and we want
+ * multiple drivers to be able to bind to the device).  Instead, manually
+ * scan for the PCI device, request a single region, and keep track of the
+ * devices that we're using.
+ */
+
+static int __init cs5535_gpio_scan_pci(void)
+{
+       struct pci_dev *pdev;
+       int err = -ENODEV;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs5535_gpio_pci_tbl); i++) {
+               pdev = pci_get_device(cs5535_gpio_pci_tbl[i].vendor,
+                               cs5535_gpio_pci_tbl[i].device, NULL);
+               if (pdev) {
+                       err = cs5535_gpio_probe(pdev, &cs5535_gpio_pci_tbl[i]);
+                       if (err)
+                               pci_dev_put(pdev);
+
+                       /* we only support a single CS5535/6 southbridge */
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static void __exit cs5535_gpio_free_pci(void)
+{
+       cs5535_gpio_remove(cs5535_gpio_chip.pdev);
+       pci_dev_put(cs5535_gpio_chip.pdev);
+}
+
+static int __init cs5535_gpio_init(void)
+{
+       return cs5535_gpio_scan_pci();
+}
+
+static void __exit cs5535_gpio_exit(void)
+{
+       cs5535_gpio_free_pci();
+}
+
+module_init(cs5535_gpio_init);
+module_exit(cs5535_gpio_exit);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
+MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
+MODULE_LICENSE("GPL");
index 9e640c62ebd9d267c1efcc2800ae07c36854caf8..95ccbe377f9c91398dd0f20300d9dd430eb4deeb 100644 (file)
@@ -1046,25 +1046,27 @@ config SENSORS_ATK0110
          will be called asus_atk0110.
 
 config SENSORS_LIS3LV02D
-       tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer"
+       tristate "STMicroeletronics LIS3* three-axis digital accelerometer"
        depends on INPUT
        select INPUT_POLLDEV
        select NEW_LEDS
        select LEDS_CLASS
        default n
        help
-         This driver provides support for the LIS3LV02Dx accelerometer. In
-         particular, it can be found in a number of HP laptops, which have the
-         "Mobile Data Protection System 3D" or "3D DriveGuard" feature. On such
-         systems the driver should load automatically (via ACPI). The
-         accelerometer might also be found in other systems, connected via SPI
-         or I2C.  The accelerometer data is readable via
-         /sys/devices/platform/lis3lv02d.
+         This driver provides support for the LIS3* accelerometers, such as the
+         LIS3LV02DL or the LIS331DL. In particular, it can be found in a number
+         of HP laptops, which have the "Mobile Data Protection System 3D" or
+         "3D DriveGuard" feature. On such systems the driver should load
+         automatically (via ACPI alias). The accelerometer might also be found
+         in other systems, connected via SPI or I2C. The accelerometer data is
+         readable via /sys/devices/platform/lis3lv02d.
 
          This driver also provides an absolute input class device, allowing
-         the laptop to act as a pinball machine-esque joystick. On HP laptops,
+         a laptop to act as a pinball machine-esque joystick. It provides also
+         a misc device which can be used to detect free-fall. On HP laptops,
          if the led infrastructure is activated, support for a led indicating
-         disk protection will be provided as hp:red:hddprotection.
+         disk protection will be provided as hp::hddprotect. For more
+         information on the feature, refer to Documentation/hwmon/lis3lv02d.
 
          This driver can also be built as modules.  If so, the core module
          will be called lis3lv02d and a specific module for HP laptops will be
index 33acf29531afa30598e684ed82f2a56295d8a835..1ad0a885c5a5795034759dfc93c9c13015d211d5 100644 (file)
@@ -34,9 +34,8 @@
 static const unsigned short normal_i2c[] = {
        0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm,
-                       mc1066);
+enum chips {
+       adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066 };
 
 /* adm1021 constants specified below */
 
@@ -97,7 +96,7 @@ struct adm1021_data {
 
 static int adm1021_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adm1021_detect(struct i2c_client *client, int kind,
+static int adm1021_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static void adm1021_init_client(struct i2c_client *client);
 static int adm1021_remove(struct i2c_client *client);
@@ -130,7 +129,7 @@ static struct i2c_driver adm1021_driver = {
        .remove         = adm1021_remove,
        .id_table       = adm1021_id,
        .detect         = adm1021_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static ssize_t show_temp(struct device *dev,
@@ -284,7 +283,7 @@ static const struct attribute_group adm1021_group = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1021_detect(struct i2c_client *client, int kind,
+static int adm1021_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index db6ac2b04f6f867ba2803b279539059faf4735db..251b63165e2a8ac96fa8314a2f629430561e6c4c 100644 (file)
 
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_2(adm1025, ne1619);
+enum chips { adm1025, ne1619 };
 
 /*
  * The ADM1025 registers
@@ -111,7 +107,7 @@ static const int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
 
 static int adm1025_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adm1025_detect(struct i2c_client *client, int kind,
+static int adm1025_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static void adm1025_init_client(struct i2c_client *client);
 static int adm1025_remove(struct i2c_client *client);
@@ -137,7 +133,7 @@ static struct i2c_driver adm1025_driver = {
        .remove         = adm1025_remove,
        .id_table       = adm1025_id,
        .detect         = adm1025_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -409,7 +405,7 @@ static const struct attribute_group adm1025_group_in4 = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1025_detect(struct i2c_client *client, int kind,
+static int adm1025_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index fb5363985e212d73bbef2132f6ce18e324855709..65335b268fa99860e50a0ad84d794c9640ea7913 100644 (file)
@@ -37,9 +37,6 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adm1026);
-
 static int gpio_input[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
                                -1, -1, -1, -1, -1, -1, -1, -1 };
 static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -293,7 +290,7 @@ struct adm1026_data {
 
 static int adm1026_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adm1026_detect(struct i2c_client *client, int kind,
+static int adm1026_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int adm1026_remove(struct i2c_client *client);
 static int adm1026_read_value(struct i2c_client *client, u8 reg);
@@ -305,7 +302,7 @@ static void adm1026_init_client(struct i2c_client *client);
 
 
 static const struct i2c_device_id adm1026_id[] = {
-       { "adm1026", adm1026 },
+       { "adm1026", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adm1026_id);
@@ -319,7 +316,7 @@ static struct i2c_driver adm1026_driver = {
        .remove         = adm1026_remove,
        .id_table       = adm1026_id,
        .detect         = adm1026_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static int adm1026_read_value(struct i2c_client *client, u8 reg)
@@ -1650,7 +1647,7 @@ static const struct attribute_group adm1026_group_in8_9 = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1026_detect(struct i2c_client *client, int kind,
+static int adm1026_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index ef91e2a4a5673b9045c636a24adda1ae0734d187..0b8a3b145bd24b3577c827383f50144c4d7c0ce7 100644 (file)
@@ -43,12 +43,6 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
                                                0x2e, 0x2f, I2C_CLIENT_END
 };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(adm1029);
-
 /*
  * The ADM1029 registers
  * Manufacturer ID is 0x41 for Analog Devices
@@ -117,7 +111,7 @@ static const u8 ADM1029_REG_FAN_DIV[] = {
 
 static int adm1029_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adm1029_detect(struct i2c_client *client, int kind,
+static int adm1029_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int adm1029_remove(struct i2c_client *client);
 static struct adm1029_data *adm1029_update_device(struct device *dev);
@@ -128,7 +122,7 @@ static int adm1029_init_client(struct i2c_client *client);
  */
 
 static const struct i2c_device_id adm1029_id[] = {
-       { "adm1029", adm1029 },
+       { "adm1029", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adm1029_id);
@@ -142,7 +136,7 @@ static struct i2c_driver adm1029_driver = {
        .remove         = adm1029_remove,
        .id_table       = adm1029_id,
        .detect         = adm1029_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -297,7 +291,7 @@ static const struct attribute_group adm1029_group = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1029_detect(struct i2c_client *client, int kind,
+static int adm1029_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 0e722175aae0640f8b1922caa56d33c972c8b232..1644b92e7cc47ae8f8507d777cd1ecbe64612484 100644 (file)
@@ -64,8 +64,7 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(adm1030, adm1031);
+enum chips { adm1030, adm1031 };
 
 typedef u8 auto_chan_table_t[8][2];
 
@@ -102,7 +101,7 @@ struct adm1031_data {
 
 static int adm1031_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adm1031_detect(struct i2c_client *client, int kind,
+static int adm1031_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static void adm1031_init_client(struct i2c_client *client);
 static int adm1031_remove(struct i2c_client *client);
@@ -125,7 +124,7 @@ static struct i2c_driver adm1031_driver = {
        .remove         = adm1031_remove,
        .id_table       = adm1031_id,
        .detect         = adm1031_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
@@ -813,7 +812,7 @@ static const struct attribute_group adm1031_group_opt = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm1031_detect(struct i2c_client *client, int kind,
+static int adm1031_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 20e0481cc20611aca78cc4fa5f49f68bebcea16e..0727ad250793c866c56a091784d6f4073f28e755 100644 (file)
@@ -55,8 +55,7 @@
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
                                        I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_3(adm9240, ds1780, lm81);
+enum chips { adm9240, ds1780, lm81 };
 
 /* ADM9240 registers */
 #define ADM9240_REG_MAN_ID             0x3e
@@ -132,7 +131,7 @@ static inline unsigned int AOUT_FROM_REG(u8 reg)
 
 static int adm9240_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adm9240_detect(struct i2c_client *client, int kind,
+static int adm9240_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static void adm9240_init_client(struct i2c_client *client);
 static int adm9240_remove(struct i2c_client *client);
@@ -156,7 +155,7 @@ static struct i2c_driver adm9240_driver = {
        .remove         = adm9240_remove,
        .id_table       = adm9240_id,
        .detect         = adm9240_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /* per client data */
@@ -545,7 +544,7 @@ static const struct attribute_group adm9240_group = {
 /*** sensor chip detect and driver install ***/
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adm9240_detect(struct i2c_client *new_client, int kind,
+static int adm9240_detect(struct i2c_client *new_client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
index 451977bca7d6e02767f887951c58e8dd5c3b5cdf..aac85f3aed50e6cdd2c08fe065e464643303f4ca 100644 (file)
 static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
        I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(ads7828);
-
-/* Other module parameters */
+/* Module parameters */
 static int se_input = 1; /* Default is SE, 0 == diff */
 static int int_vref = 1; /* Default is internal ref ON */
 static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */
@@ -72,7 +69,7 @@ struct ads7828_data {
 };
 
 /* Function declaration - necessary due to function dependencies */
-static int ads7828_detect(struct i2c_client *client, int kind,
+static int ads7828_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int ads7828_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
@@ -168,7 +165,7 @@ static int ads7828_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id ads7828_id[] = {
-       { "ads7828", ads7828 },
+       { "ads7828", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ads7828_id);
@@ -183,11 +180,11 @@ static struct i2c_driver ads7828_driver = {
        .remove = ads7828_remove,
        .id_table = ads7828_id,
        .detect = ads7828_detect,
-       .address_data = &addr_data,
+       .address_list = normal_i2c,
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int ads7828_detect(struct i2c_client *client, int kind,
+static int ads7828_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index f9c9562b6a94f0e6f136528c9831922f5119b4ae..a1a7ef14b519f68f80ae5c91f4169c2b5ff110fc 100644 (file)
@@ -32,9 +32,6 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adt7462);
-
 /* ADT7462 registers */
 #define ADT7462_REG_DEVICE                     0x3D
 #define ADT7462_REG_VENDOR                     0x3E
@@ -237,12 +234,12 @@ struct adt7462_data {
 
 static int adt7462_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adt7462_detect(struct i2c_client *client, int kind,
+static int adt7462_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int adt7462_remove(struct i2c_client *client);
 
 static const struct i2c_device_id adt7462_id[] = {
-       { "adt7462", adt7462 },
+       { "adt7462", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adt7462_id);
@@ -256,7 +253,7 @@ static struct i2c_driver adt7462_driver = {
        .remove         = adt7462_remove,
        .id_table       = adt7462_id,
        .detect         = adt7462_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -1902,7 +1899,7 @@ static struct attribute *adt7462_attr[] =
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7462_detect(struct i2c_client *client, int kind,
+static int adt7462_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 32b1750a6890bb41e95bca0df47027637a3e6d65..3445ce1cba81e730d2bc6c4a664587be6e644517 100644 (file)
@@ -33,9 +33,6 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adt7470);
-
 /* ADT7470 registers */
 #define ADT7470_REG_BASE_ADDR                  0x20
 #define ADT7470_REG_TEMP_BASE_ADDR             0x20
@@ -177,12 +174,12 @@ struct adt7470_data {
 
 static int adt7470_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adt7470_detect(struct i2c_client *client, int kind,
+static int adt7470_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int adt7470_remove(struct i2c_client *client);
 
 static const struct i2c_device_id adt7470_id[] = {
-       { "adt7470", adt7470 },
+       { "adt7470", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adt7470_id);
@@ -196,7 +193,7 @@ static struct i2c_driver adt7470_driver = {
        .remove         = adt7470_remove,
        .id_table       = adt7470_id,
        .detect         = adt7470_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -1225,7 +1222,7 @@ static struct attribute *adt7470_attr[] =
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7470_detect(struct i2c_client *client, int kind,
+static int adt7470_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index aea244db974e1acfdf63a552f649ddc1fea063b9..434576f61c84b7572575ceb26f698b23f1fece40 100644 (file)
@@ -32,9 +32,6 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(adt7473);
-
 /* ADT7473 registers */
 #define ADT7473_REG_BASE_ADDR                  0x20
 
@@ -166,12 +163,12 @@ struct adt7473_data {
 
 static int adt7473_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int adt7473_detect(struct i2c_client *client, int kind,
+static int adt7473_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int adt7473_remove(struct i2c_client *client);
 
 static const struct i2c_device_id adt7473_id[] = {
-       { "adt7473", adt7473 },
+       { "adt7473", 0 },
        { }
 };
 
@@ -184,7 +181,7 @@ static struct i2c_driver adt7473_driver = {
        .remove         = adt7473_remove,
        .id_table       = adt7473_id,
        .detect         = adt7473_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -1085,7 +1082,7 @@ static struct attribute *adt7473_attr[] =
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7473_detect(struct i2c_client *client, int kind,
+static int adt7473_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 99abfddedbc3b842be2c017c0eb2e071693917e5..a0c385145686c3d068a9453ce80f8023ecf9cc7c 100644 (file)
 
 static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
-I2C_CLIENT_INSMOD_4(adt7473, adt7475, adt7476, adt7490);
+enum chips { adt7473, adt7475, adt7476, adt7490 };
 
 static const struct i2c_device_id adt7475_id[] = {
        { "adt7473", adt7473 },
@@ -1172,7 +1172,7 @@ static struct attribute_group in4_attr_group = { .attrs = in4_attrs };
 static struct attribute_group in5_attr_group = { .attrs = in5_attrs };
 static struct attribute_group vid_attr_group = { .attrs = vid_attrs };
 
-static int adt7475_detect(struct i2c_client *client, int kind,
+static int adt7475_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
@@ -1412,7 +1412,7 @@ static struct i2c_driver adt7475_driver = {
        .remove         = adt7475_remove,
        .id_table       = adt7475_id,
        .detect         = adt7475_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static void adt7475_read_hystersis(struct i2c_client *client)
index 7ea6a8f66056827970f31f13a3a3cedd135db1fd..c1605b528e8fcccfe26241efc96c138505f67839 100644 (file)
@@ -518,7 +518,7 @@ static int applesmc_pm_restore(struct device *dev)
        return applesmc_pm_resume(dev);
 }
 
-static struct dev_pm_ops applesmc_pm_ops = {
+static const struct dev_pm_ops applesmc_pm_ops = {
        .resume = applesmc_pm_resume,
        .restore = applesmc_pm_restore,
 };
index 480f80ea1fa00c1082ee7d89d381c8ba84a002ae..7dada559b3a19a85815002ce1ff76727306d4a23 100644 (file)
@@ -51,9 +51,6 @@
 /* I2C addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(asb100);
-
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
 MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
@@ -209,14 +206,14 @@ static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val);
 
 static int asb100_probe(struct i2c_client *client,
                        const struct i2c_device_id *id);
-static int asb100_detect(struct i2c_client *client, int kind,
+static int asb100_detect(struct i2c_client *client,
                         struct i2c_board_info *info);
 static int asb100_remove(struct i2c_client *client);
 static struct asb100_data *asb100_update_device(struct device *dev);
 static void asb100_init_client(struct i2c_client *client);
 
 static const struct i2c_device_id asb100_id[] = {
-       { "asb100", asb100 },
+       { "asb100", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, asb100_id);
@@ -230,7 +227,7 @@ static struct i2c_driver asb100_driver = {
        .remove         = asb100_remove,
        .id_table       = asb100_id,
        .detect         = asb100_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /* 7 Voltages */
@@ -697,7 +694,7 @@ ERROR_SC_2:
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int asb100_detect(struct i2c_client *client, int kind,
+static int asb100_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index d6b490d3e36f029660e079f6f42802329cd4bf4b..94cadc19f0c54d36885f80a21530905b99ab5bca 100644 (file)
@@ -44,17 +44,14 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
 
 static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
 
-I2C_CLIENT_INSMOD_1(atxp1);
-
 static int atxp1_probe(struct i2c_client *client,
                       const struct i2c_device_id *id);
 static int atxp1_remove(struct i2c_client *client);
 static struct atxp1_data * atxp1_update_device(struct device *dev);
-static int atxp1_detect(struct i2c_client *client, int kind,
-                       struct i2c_board_info *info);
+static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info);
 
 static const struct i2c_device_id atxp1_id[] = {
-       { "atxp1", atxp1 },
+       { "atxp1", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, atxp1_id);
@@ -68,7 +65,7 @@ static struct i2c_driver atxp1_driver = {
        .remove         = atxp1_remove,
        .id_table       = atxp1_id,
        .detect         = atxp1_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 struct atxp1_data {
@@ -275,7 +272,7 @@ static const struct attribute_group atxp1_group = {
 
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int atxp1_detect(struct i2c_client *new_client, int kind,
+static int atxp1_detect(struct i2c_client *new_client,
                        struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
index 4377bb0cc5269d755e1b20095a095feedfb1e592..823dd28a902cf8c33e7d8de2d8a21b9a56f85db1 100644 (file)
@@ -57,11 +57,7 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(dme1737, sch5027);
-
-/* ISA chip types */
-enum isa_chips { sch311x = sch5027 + 1 };
+enum chips { dme1737, sch5027, sch311x };
 
 /* ---------------------------------------------------------------------
  * Registers
@@ -2208,7 +2204,7 @@ exit:
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int dme1737_i2c_detect(struct i2c_client *client, int kind,
+static int dme1737_i2c_detect(struct i2c_client *client,
                              struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
@@ -2318,7 +2314,7 @@ static struct i2c_driver dme1737_i2c_driver = {
        .remove = dme1737_i2c_remove,
        .id_table = dme1737_id,
        .detect = dme1737_i2c_detect,
-       .address_data = &addr_data,
+       .address_list = normal_i2c,
 };
 
 /* ---------------------------------------------------------------------
index 2a4c6a05b14f594b5760f3d62c98b07d7dbc0bcc..e11363467a8d45cbd32207dde7f157254be76a10 100644 (file)
@@ -38,7 +38,6 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
                                        0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(ds1621);
 static int polarity = -1;
 module_param(polarity, int, 0);
 MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low");
@@ -224,7 +223,7 @@ static const struct attribute_group ds1621_group = {
 
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int ds1621_detect(struct i2c_client *client, int kind,
+static int ds1621_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
@@ -305,8 +304,8 @@ static int ds1621_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id ds1621_id[] = {
-       { "ds1621", ds1621 },
-       { "ds1625", ds1621 },
+       { "ds1621", 0 },
+       { "ds1625", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ds1621_id);
@@ -321,7 +320,7 @@ static struct i2c_driver ds1621_driver = {
        .remove         = ds1621_remove,
        .id_table       = ds1621_id,
        .detect         = ds1621_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static int __init ds1621_init(void)
index 40dfbcd3f3f2f704240a9ab957355208dfb39308..277398f9c93826dddb5233d7d4765c0f515011cc 100644 (file)
@@ -39,8 +39,7 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(f75373, f75375);
+enum chips { f75373, f75375 };
 
 /* Fintek F75375 registers  */
 #define F75375_REG_CONFIG0             0x0
@@ -113,7 +112,7 @@ struct f75375_data {
        s8 temp_max_hyst[2];
 };
 
-static int f75375_detect(struct i2c_client *client, int kind,
+static int f75375_detect(struct i2c_client *client,
                         struct i2c_board_info *info);
 static int f75375_probe(struct i2c_client *client,
                        const struct i2c_device_id *id);
@@ -135,7 +134,7 @@ static struct i2c_driver f75375_driver = {
        .remove = f75375_remove,
        .id_table = f75375_id,
        .detect = f75375_detect,
-       .address_data = &addr_data,
+       .address_list = normal_i2c,
 };
 
 static inline int f75375_read8(struct i2c_client *client, u8 reg)
@@ -677,7 +676,7 @@ static int f75375_remove(struct i2c_client *client)
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int f75375_detect(struct i2c_client *client, int kind,
+static int f75375_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 281829cd1533824b62b12d66d7c7e78c7c5b7623..bd0fc67e804b37cc12b690f902312c7c9ee7f639 100644 (file)
@@ -56,7 +56,8 @@ static int nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, int, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
        __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-I2C_CLIENT_INSMOD_7(fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl);
+
+enum chips { fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl };
 
 /*
  * The FSCHMD registers and other defines
@@ -221,7 +222,7 @@ static const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 };
 
 static int fschmd_probe(struct i2c_client *client,
                        const struct i2c_device_id *id);
-static int fschmd_detect(struct i2c_client *client, int kind,
+static int fschmd_detect(struct i2c_client *client,
                         struct i2c_board_info *info);
 static int fschmd_remove(struct i2c_client *client);
 static struct fschmd_data *fschmd_update_device(struct device *dev);
@@ -251,7 +252,7 @@ static struct i2c_driver fschmd_driver = {
        .remove         = fschmd_remove,
        .id_table       = fschmd_id,
        .detect         = fschmd_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -1000,7 +1001,7 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy)
        }
 }
 
-static int fschmd_detect(struct i2c_client *client, int _kind,
+static int fschmd_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        enum chips kind;
index 1d69458aa0b641cab7da8c51f9b7f1825dd0a6a8..e7ae5743e1817c382c6ab8e56c415d3dea5ab19a 100644 (file)
@@ -46,8 +46,7 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(gl518sm_r00, gl518sm_r80);
+enum chips { gl518sm_r00, gl518sm_r80 };
 
 /* Many GL518 constants specified below */
 
@@ -139,8 +138,7 @@ struct gl518_data {
 
 static int gl518_probe(struct i2c_client *client,
                       const struct i2c_device_id *id);
-static int gl518_detect(struct i2c_client *client, int kind,
-                       struct i2c_board_info *info);
+static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info);
 static void gl518_init_client(struct i2c_client *client);
 static int gl518_remove(struct i2c_client *client);
 static int gl518_read_value(struct i2c_client *client, u8 reg);
@@ -163,7 +161,7 @@ static struct i2c_driver gl518_driver = {
        .remove         = gl518_remove,
        .id_table       = gl518_id,
        .detect         = gl518_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -484,8 +482,7 @@ static const struct attribute_group gl518_group_r80 = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int gl518_detect(struct i2c_client *client, int kind,
-                       struct i2c_board_info *info)
+static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
        int rev;
index 92b5720ceaffd57bdece90ce14bcfd37dfdf7888..ec588026f0a9c46c2ffd37347b5ac69da6f7b721 100644 (file)
@@ -41,9 +41,6 @@ MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=tempe
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(gl520sm);
-
 /* Many GL520 constants specified below
 One of the inputs can be configured as either temp or voltage.
 That's why _TEMP2 and _IN4 access the same register
@@ -81,8 +78,7 @@ static const u8 GL520_REG_TEMP_MAX_HYST[]     = { 0x06, 0x18 };
 
 static int gl520_probe(struct i2c_client *client,
                       const struct i2c_device_id *id);
-static int gl520_detect(struct i2c_client *client, int kind,
-                       struct i2c_board_info *info);
+static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info);
 static void gl520_init_client(struct i2c_client *client);
 static int gl520_remove(struct i2c_client *client);
 static int gl520_read_value(struct i2c_client *client, u8 reg);
@@ -91,7 +87,7 @@ static struct gl520_data *gl520_update_device(struct device *dev);
 
 /* Driver data */
 static const struct i2c_device_id gl520_id[] = {
-       { "gl520sm", gl520sm },
+       { "gl520sm", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, gl520_id);
@@ -105,7 +101,7 @@ static struct i2c_driver gl520_driver = {
        .remove         = gl520_remove,
        .id_table       = gl520_id,
        .detect         = gl520_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /* Client data */
@@ -681,8 +677,7 @@ static const struct attribute_group gl520_group_opt = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int gl520_detect(struct i2c_client *client, int kind,
-                       struct i2c_board_info *info)
+static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
 
index cf5afb9a10abbd9af3432dce36249a23edfcab0d..b2f2277cad3c01c714bc36634e6c23d4f56fc317 100644 (file)
 #define MDPS_POLL_INTERVAL 50
 /*
  * The sensor can also generate interrupts (DRDY) but it's pretty pointless
- * because their are generated even if the data do not change. So it's better
+ * because they are generated even if the data do not change. So it's better
  * to keep the interrupt for the free-fall event. The values are updated at
  * 40Hz (at the lowest frequency), but as it can be pretty time consuming on
  * some low processor, we poll the sensor only at 20Hz... enough for the
  * joystick.
  */
 
+#define LIS3_PWRON_DELAY_WAI_12B       (5000)
+#define LIS3_PWRON_DELAY_WAI_8B                (3000)
+
+/*
+ * LIS3LV02D spec says 1024 LSBs corresponds 1 G -> 1LSB is 1000/1024 mG
+ * LIS302D spec says: 18 mG / digit
+ * LIS3_ACCURACY is used to increase accuracy of the intermediate
+ * calculation results.
+ */
+#define LIS3_ACCURACY                  1024
+/* Sensitivity values for -2G +2G scale */
+#define LIS3_SENSITIVITY_12B           ((LIS3_ACCURACY * 1000) / 1024)
+#define LIS3_SENSITIVITY_8B            (18 * LIS3_ACCURACY)
+
+#define LIS3_DEFAULT_FUZZ              3
+#define LIS3_DEFAULT_FLAT              3
+
 struct lis3lv02d lis3_dev = {
        .misc_wait   = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait),
 };
@@ -65,7 +82,7 @@ static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg)
        return lo;
 }
 
-static s16 lis3lv02d_read_16(struct lis3lv02d *lis3, int reg)
+static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg)
 {
        u8 lo, hi;
 
@@ -102,16 +119,106 @@ static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3])
 static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
 {
        int position[3];
+       int i;
 
+       mutex_lock(&lis3->mutex);
        position[0] = lis3->read_data(lis3, OUTX);
        position[1] = lis3->read_data(lis3, OUTY);
        position[2] = lis3->read_data(lis3, OUTZ);
+       mutex_unlock(&lis3->mutex);
+
+       for (i = 0; i < 3; i++)
+               position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY;
 
        *x = lis3lv02d_get_axis(lis3->ac.x, position);
        *y = lis3lv02d_get_axis(lis3->ac.y, position);
        *z = lis3lv02d_get_axis(lis3->ac.z, position);
 }
 
+/* conversion btw sampling rate and the register values */
+static int lis3_12_rates[4] = {40, 160, 640, 2560};
+static int lis3_8_rates[2] = {100, 400};
+
+/* ODR is Output Data Rate */
+static int lis3lv02d_get_odr(void)
+{
+       u8 ctrl;
+       int shift;
+
+       lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
+       ctrl &= lis3_dev.odr_mask;
+       shift = ffs(lis3_dev.odr_mask) - 1;
+       return lis3_dev.odrs[(ctrl >> shift)];
+}
+
+static int lis3lv02d_set_odr(int rate)
+{
+       u8 ctrl;
+       int i, len, shift;
+
+       lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
+       ctrl &= ~lis3_dev.odr_mask;
+       len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */
+       shift = ffs(lis3_dev.odr_mask) - 1;
+
+       for (i = 0; i < len; i++)
+               if (lis3_dev.odrs[i] == rate) {
+                       lis3_dev.write(&lis3_dev, CTRL_REG1,
+                                       ctrl | (i << shift));
+                       return 0;
+               }
+       return -EINVAL;
+}
+
+static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
+{
+       u8 reg;
+       s16 x, y, z;
+       u8 selftest;
+       int ret;
+
+       mutex_lock(&lis3->mutex);
+       if (lis3_dev.whoami == WAI_12B)
+               selftest = CTRL1_ST;
+       else
+               selftest = CTRL1_STP;
+
+       lis3->read(lis3, CTRL_REG1, &reg);
+       lis3->write(lis3, CTRL_REG1, (reg | selftest));
+       msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+
+       /* Read directly to avoid axis remap */
+       x = lis3->read_data(lis3, OUTX);
+       y = lis3->read_data(lis3, OUTY);
+       z = lis3->read_data(lis3, OUTZ);
+
+       /* back to normal settings */
+       lis3->write(lis3, CTRL_REG1, reg);
+       msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+
+       results[0] = x - lis3->read_data(lis3, OUTX);
+       results[1] = y - lis3->read_data(lis3, OUTY);
+       results[2] = z - lis3->read_data(lis3, OUTZ);
+
+       ret = 0;
+       if (lis3->pdata) {
+               int i;
+               for (i = 0; i < 3; i++) {
+                       /* Check against selftest acceptance limits */
+                       if ((results[i] < lis3->pdata->st_min_limits[i]) ||
+                           (results[i] > lis3->pdata->st_max_limits[i])) {
+                               ret = -EIO;
+                               goto fail;
+                       }
+               }
+       }
+
+       /* test passed */
+fail:
+       mutex_unlock(&lis3->mutex);
+       return ret;
+}
+
 void lis3lv02d_poweroff(struct lis3lv02d *lis3)
 {
        /* disable X,Y,Z axis and power down */
@@ -125,14 +232,19 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
 
        lis3->init(lis3);
 
+       /* LIS3 power on delay is quite long */
+       msleep(lis3->pwron_delay / lis3lv02d_get_odr());
+
        /*
         * Common configuration
-        * BDU: LSB and MSB values are not updated until both have been read.
-        *      So the value read will always be correct.
+        * BDU: (12 bits sensors only) LSB and MSB values are not updated until
+        *      both have been read. So the value read will always be correct.
         */
-       lis3->read(lis3, CTRL_REG2, &reg);
-       reg |= CTRL2_BDU;
-       lis3->write(lis3, CTRL_REG2, reg);
+       if (lis3->whoami ==  WAI_12B) {
+               lis3->read(lis3, CTRL_REG2, &reg);
+               reg |= CTRL2_BDU;
+               lis3->write(lis3, CTRL_REG2, reg);
+       }
 }
 EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
 
@@ -273,22 +385,17 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
        int x, y, z;
 
        lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
-       input_report_abs(pidev->input, ABS_X, x - lis3_dev.xcalib);
-       input_report_abs(pidev->input, ABS_Y, y - lis3_dev.ycalib);
-       input_report_abs(pidev->input, ABS_Z, z - lis3_dev.zcalib);
-}
-
-
-static inline void lis3lv02d_calibrate_joystick(void)
-{
-       lis3lv02d_get_xyz(&lis3_dev,
-               &lis3_dev.xcalib, &lis3_dev.ycalib, &lis3_dev.zcalib);
+       input_report_abs(pidev->input, ABS_X, x);
+       input_report_abs(pidev->input, ABS_Y, y);
+       input_report_abs(pidev->input, ABS_Z, z);
+       input_sync(pidev->input);
 }
 
 int lis3lv02d_joystick_enable(void)
 {
        struct input_dev *input_dev;
        int err;
+       int max_val, fuzz, flat;
 
        if (lis3_dev.idev)
                return -EINVAL;
@@ -301,8 +408,6 @@ int lis3lv02d_joystick_enable(void)
        lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
        input_dev = lis3_dev.idev->input;
 
-       lis3lv02d_calibrate_joystick();
-
        input_dev->name       = "ST LIS3LV02DL Accelerometer";
        input_dev->phys       = DRIVER_NAME "/input0";
        input_dev->id.bustype = BUS_HOST;
@@ -310,9 +415,12 @@ int lis3lv02d_joystick_enable(void)
        input_dev->dev.parent = &lis3_dev.pdev->dev;
 
        set_bit(EV_ABS, input_dev->evbit);
-       input_set_abs_params(input_dev, ABS_X, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3);
-       input_set_abs_params(input_dev, ABS_Y, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3);
-       input_set_abs_params(input_dev, ABS_Z, -lis3_dev.mdps_max_val, lis3_dev.mdps_max_val, 3, 3);
+       max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY;
+       fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY;
+       flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY;
+       input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat);
+       input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
+       input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
 
        err = input_register_polled_device(lis3_dev.idev);
        if (err) {
@@ -332,11 +440,23 @@ void lis3lv02d_joystick_disable(void)
        if (lis3_dev.irq)
                misc_deregister(&lis3lv02d_misc_device);
        input_unregister_polled_device(lis3_dev.idev);
+       input_free_polled_device(lis3_dev.idev);
        lis3_dev.idev = NULL;
 }
 EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable);
 
 /* Sysfs stuff */
+static ssize_t lis3lv02d_selftest_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int result;
+       s16 values[3];
+
+       result = lis3lv02d_selftest(&lis3_dev, values);
+       return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL",
+               values[0], values[1], values[2]);
+}
+
 static ssize_t lis3lv02d_position_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
@@ -346,41 +466,35 @@ static ssize_t lis3lv02d_position_show(struct device *dev,
        return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
 }
 
-static ssize_t lis3lv02d_calibrate_show(struct device *dev,
-                               struct device_attribute *attr, char *buf)
+static ssize_t lis3lv02d_rate_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
 {
-       return sprintf(buf, "(%d,%d,%d)\n", lis3_dev.xcalib, lis3_dev.ycalib, lis3_dev.zcalib);
+       return sprintf(buf, "%d\n", lis3lv02d_get_odr());
 }
 
-static ssize_t lis3lv02d_calibrate_store(struct device *dev,
-                               struct device_attribute *attr,
-                               const char *buf, size_t count)
+static ssize_t lis3lv02d_rate_set(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
 {
-       lis3lv02d_calibrate_joystick();
-       return count;
-}
+       unsigned long rate;
 
-/* conversion btw sampling rate and the register values */
-static int lis3lv02dl_df_val[4] = {40, 160, 640, 2560};
-static ssize_t lis3lv02d_rate_show(struct device *dev,
-                       struct device_attribute *attr, char *buf)
-{
-       u8 ctrl;
-       int val;
+       if (strict_strtoul(buf, 0, &rate))
+               return -EINVAL;
 
-       lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl);
-       val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4;
-       return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]);
+       if (lis3lv02d_set_odr(rate))
+               return -EINVAL;
+
+       return count;
 }
 
+static DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL);
 static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL);
-static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show,
-       lis3lv02d_calibrate_store);
-static DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL);
+static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, lis3lv02d_rate_show,
+                                           lis3lv02d_rate_set);
 
 static struct attribute *lis3lv02d_attributes[] = {
+       &dev_attr_selftest.attr,
        &dev_attr_position.attr,
-       &dev_attr_calibrate.attr,
        &dev_attr_rate.attr,
        NULL
 };
@@ -409,22 +523,30 @@ EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
 
 /*
  * Initialise the accelerometer and the various subsystems.
- * Should be rather independant of the bus system.
+ * Should be rather independent of the bus system.
  */
 int lis3lv02d_init_device(struct lis3lv02d *dev)
 {
        dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
 
        switch (dev->whoami) {
-       case LIS_DOUBLE_ID:
-               printk(KERN_INFO DRIVER_NAME ": 2-byte sensor found\n");
-               dev->read_data = lis3lv02d_read_16;
+       case WAI_12B:
+               printk(KERN_INFO DRIVER_NAME ": 12 bits sensor found\n");
+               dev->read_data = lis3lv02d_read_12;
                dev->mdps_max_val = 2048;
+               dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B;
+               dev->odrs = lis3_12_rates;
+               dev->odr_mask = CTRL1_DF0 | CTRL1_DF1;
+               dev->scale = LIS3_SENSITIVITY_12B;
                break;
-       case LIS_SINGLE_ID:
-               printk(KERN_INFO DRIVER_NAME ": 1-byte sensor found\n");
+       case WAI_8B:
+               printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n");
                dev->read_data = lis3lv02d_read_8;
                dev->mdps_max_val = 128;
+               dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+               dev->odrs = lis3_8_rates;
+               dev->odr_mask = CTRL1_DR;
+               dev->scale = LIS3_SENSITIVITY_8B;
                break;
        default:
                printk(KERN_ERR DRIVER_NAME
@@ -432,6 +554,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
                return -EINVAL;
        }
 
+       mutex_init(&dev->mutex);
+
        lis3lv02d_add_fs(dev);
        lis3lv02d_poweron(dev);
 
@@ -443,7 +567,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
        if (dev->pdata) {
                struct lis3lv02d_platform_data *p = dev->pdata;
 
-               if (p->click_flags && (dev->whoami == LIS_SINGLE_ID)) {
+               if (p->click_flags && (dev->whoami == WAI_8B)) {
                        dev->write(dev, CLICK_CFG, p->click_flags);
                        dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);
                        dev->write(dev, CLICK_LATENCY, p->click_latency);
@@ -454,7 +578,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
                                        (p->click_thresh_y << 4));
                }
 
-               if (p->wakeup_flags && (dev->whoami == LIS_SINGLE_ID)) {
+               if (p->wakeup_flags && (dev->whoami == WAI_8B)) {
                        dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
                        dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
                        /* default to 2.5ms for now */
@@ -484,4 +608,3 @@ EXPORT_SYMBOL_GPL(lis3lv02d_init_device);
 MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");
 MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
 MODULE_LICENSE("GPL");
-
index 3e1ff46f72d3ac2cdfde3f3bf1b83637513c42b8..e6a01f44709b9de41a3821e9cfacd07bb42a9691 100644 (file)
@@ -2,7 +2,7 @@
  *  lis3lv02d.h - ST LIS3LV02DL accelerometer driver
  *
  *  Copyright (C) 2007-2008 Yan Burman
- *  Copyright (C) 2008 Eric Piel
+ *  Copyright (C) 2008-2009 Eric Piel
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include <linux/input-polldev.h>
 
 /*
- * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to
- * be connected via SPI. There exists also several similar chips (such as LIS302DL or
- * LIS3L02DQ) and they have slightly different registers, but we can provide a
- * common interface for all of them.
- * They can also be connected via I²C.
+ * This driver tries to support the "digital" accelerometer chips from
+ * STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL,
+ * LIS35DE, or LIS202DL. They are very similar in terms of programming, with
+ * almost the same registers. In addition to differing on physical properties,
+ * they differ on the number of axes (2/3), precision (8/12 bits), and special
+ * features (freefall detection, click...). Unfortunately, not all the
+ * differences can be probed via a register.
+ * They can be connected either via I²C or SPI.
  */
 
 #include <linux/lis3lv02d.h>
 
-/* 2-byte registers */
-#define LIS_DOUBLE_ID  0x3A /* LIS3LV02D[LQ] */
-/* 1-byte registers */
-#define LIS_SINGLE_ID  0x3B /* LIS[32]02DL and others */
-
 enum lis3_reg {
        WHO_AM_I        = 0x0F,
        OFFSET_X        = 0x16,
@@ -94,7 +92,13 @@ enum lis3lv02d_reg {
        DD_THSE_H       = 0x3F,
 };
 
-enum lis3lv02d_ctrl1 {
+enum lis3_who_am_i {
+       WAI_12B         = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
+       WAI_8B          = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
+       WAI_6B          = 0x52, /* 6 bits: LIS331DLF - not supported */
+};
+
+enum lis3lv02d_ctrl1_12b {
        CTRL1_Xen       = 0x01,
        CTRL1_Yen       = 0x02,
        CTRL1_Zen       = 0x04,
@@ -104,6 +108,16 @@ enum lis3lv02d_ctrl1 {
        CTRL1_PD0       = 0x40,
        CTRL1_PD1       = 0x80,
 };
+
+/* Delta to ctrl1_12b version */
+enum lis3lv02d_ctrl1_8b {
+       CTRL1_STM       = 0x08,
+       CTRL1_STP       = 0x10,
+       CTRL1_FS        = 0x20,
+       CTRL1_PD        = 0x40,
+       CTRL1_DR        = 0x80,
+};
+
 enum lis3lv02d_ctrl2 {
        CTRL2_DAS       = 0x01,
        CTRL2_SIM       = 0x02,
@@ -194,16 +208,20 @@ struct lis3lv02d {
        int (*write) (struct lis3lv02d *lis3, int reg, u8 val);
        int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret);
 
-       u8                      whoami;    /* 3Ah: 2-byte registries, 3Bh: 1-byte registries */
+       int                     *odrs;     /* Supported output data rates */
+       u8                      odr_mask;  /* ODR bit mask */
+       u8                      whoami;    /* indicates measurement precision */
        s16 (*read_data) (struct lis3lv02d *lis3, int reg);
        int                     mdps_max_val;
+       int                     pwron_delay;
+       int                     scale; /*
+                                       * relationship between 1 LBS and mG
+                                       * (1/1000th of earth gravity)
+                                       */
 
        struct input_polled_dev *idev;     /* input device */
        struct platform_device  *pdev;     /* platform device */
        atomic_t                count;     /* interrupt count after last read */
-       int                     xcalib;    /* calibrated null value for x */
-       int                     ycalib;    /* calibrated null value for y */
-       int                     zcalib;    /* calibrated null value for z */
        struct axis_conversion  ac;        /* hw -> logical axis */
 
        u32                     irq;       /* IRQ number */
@@ -212,6 +230,7 @@ struct lis3lv02d {
        unsigned long           misc_opened; /* bit0: whether the device is open */
 
        struct lis3lv02d_platform_data *pdata;  /* for passing board config */
+       struct mutex            mutex;     /* Serialize poll and selftest */
 };
 
 int lis3lv02d_init_device(struct lis3lv02d *lis3);
index 5da66ab04f74d6e5abb05fa2468b25859d55b6c9..bf81aff7051dd44b009a062b9f3f5ba7799429c2 100644 (file)
 
 static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(lm63);
-
 /*
  * The LM63 registers
  */
@@ -134,8 +128,7 @@ static int lm63_remove(struct i2c_client *client);
 
 static struct lm63_data *lm63_update_device(struct device *dev);
 
-static int lm63_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info);
+static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info);
 static void lm63_init_client(struct i2c_client *client);
 
 /*
@@ -143,7 +136,7 @@ static void lm63_init_client(struct i2c_client *client);
  */
 
 static const struct i2c_device_id lm63_id[] = {
-       { "lm63", lm63 },
+       { "lm63", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm63_id);
@@ -157,7 +150,7 @@ static struct i2c_driver lm63_driver = {
        .remove         = lm63_remove,
        .id_table       = lm63_id,
        .detect         = lm63_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -423,7 +416,7 @@ static const struct attribute_group lm63_group_fan1 = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm63_detect(struct i2c_client *new_client, int kind,
+static int lm63_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
index 0bf8b2a8e9f0957723f6ee59b75bcf5cf08a5084..c5f39ba103c0341d5a0207ec805d0b38c9f689e5 100644 (file)
@@ -27,9 +27,6 @@
 static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
                                        0x4d, 0x4e, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm73);
-
 /* LM73 registers */
 #define LM73_REG_INPUT         0x00
 #define LM73_REG_CONF          0x01
@@ -145,13 +142,13 @@ static int lm73_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id lm73_ids[] = {
-       { "lm73", lm73 },
+       { "lm73", 0 },
        { /* LIST END */ }
 };
 MODULE_DEVICE_TABLE(i2c, lm73_ids);
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm73_detect(struct i2c_client *new_client, int kind,
+static int lm73_detect(struct i2c_client *new_client,
                        struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
@@ -182,7 +179,7 @@ static struct i2c_driver lm73_driver = {
        .remove         = lm73_remove,
        .id_table       = lm73_ids,
        .detect         = lm73_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /* module glue */
index e392548cccb8c8c399ab44b511c9b40c197fef53..8ae2cfe2d827257a0e1eedbd61d1dc0e9fc8d572 100644 (file)
 
 /*
  * This driver handles the LM75 and compatible digital temperature sensors.
- * Only types which are _not_ listed in I2C_CLIENT_INSMOD_*() need to be
- * listed here.  We start at 9 since I2C_CLIENT_INSMOD_*() currently allow
- * definition of up to 8 chip types (plus zero).
  */
 
 enum lm75_type {               /* keep sorted in alphabetical order */
-       ds1775 = 9,
+       ds1775,
        ds75,
-       /* lm75 -- in I2C_CLIENT_INSMOD_1() */
+       lm75,
        lm75a,
        max6625,
        max6626,
@@ -58,9 +55,6 @@ enum lm75_type {              /* keep sorted in alphabetical order */
 static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
                                        0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm75);
-
 
 /* The LM75 registers */
 #define LM75_REG_CONF          0x01
@@ -234,7 +228,7 @@ static const struct i2c_device_id lm75_ids[] = {
 MODULE_DEVICE_TABLE(i2c, lm75_ids);
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm75_detect(struct i2c_client *new_client, int kind,
+static int lm75_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
@@ -295,7 +289,7 @@ static struct i2c_driver lm75_driver = {
        .remove         = lm75_remove,
        .id_table       = lm75_ids,
        .detect         = lm75_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*-----------------------------------------------------------------------*/
index ac067fd1948212a42d3972696dbccc771238da05..b28a297be50c68267366139f2e0310bc87fdb54a 100644 (file)
@@ -39,9 +39,6 @@
 static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
                                                I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm77);
-
 /* The LM77 registers */
 #define LM77_REG_TEMP          0x00
 #define LM77_REG_CONF          0x01
@@ -66,8 +63,7 @@ struct lm77_data {
 
 static int lm77_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
-static int lm77_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info);
+static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info);
 static void lm77_init_client(struct i2c_client *client);
 static int lm77_remove(struct i2c_client *client);
 static u16 lm77_read_value(struct i2c_client *client, u8 reg);
@@ -77,7 +73,7 @@ static struct lm77_data *lm77_update_device(struct device *dev);
 
 
 static const struct i2c_device_id lm77_id[] = {
-       { "lm77", lm77 },
+       { "lm77", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm77_id);
@@ -92,7 +88,7 @@ static struct i2c_driver lm77_driver = {
        .remove         = lm77_remove,
        .id_table       = lm77_id,
        .detect         = lm77_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /* straight from the datasheet */
@@ -245,7 +241,7 @@ static const struct attribute_group lm77_group = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm77_detect(struct i2c_client *new_client, int kind,
+static int lm77_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
index 5978291cebb370f63965e510947d48d0fa480e65..cadcbd90ff3bc4f0c89db6cdd9c9da82becc46f1 100644 (file)
@@ -41,8 +41,7 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
                                                0x2e, 0x2f, I2C_CLIENT_END };
 static unsigned short isa_address = 0x290;
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(lm78, lm79);
+enum chips { lm78, lm79 };
 
 /* Many LM78 constants specified below */
 
@@ -142,7 +141,7 @@ struct lm78_data {
 };
 
 
-static int lm78_i2c_detect(struct i2c_client *client, int kind,
+static int lm78_i2c_detect(struct i2c_client *client,
                           struct i2c_board_info *info);
 static int lm78_i2c_probe(struct i2c_client *client,
                          const struct i2c_device_id *id);
@@ -173,7 +172,7 @@ static struct i2c_driver lm78_driver = {
        .remove         = lm78_i2c_remove,
        .id_table       = lm78_i2c_id,
        .detect         = lm78_i2c_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static struct platform_driver lm78_isa_driver = {
@@ -558,7 +557,7 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
        return 1;
 }
 
-static int lm78_i2c_detect(struct i2c_client *client, int kind,
+static int lm78_i2c_detect(struct i2c_client *client,
                           struct i2c_board_info *info)
 {
        int i;
index bcffc189940317186dadbf68798139723fba3fd0..18a0e6c5fe88dac243ede53f817a72d8202daaf4 100644 (file)
@@ -35,9 +35,6 @@
 static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
                                                0x2e, 0x2f, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm80);
-
 /* Many LM80 constants specified below */
 
 /* The LM80 registers */
@@ -133,8 +130,7 @@ struct lm80_data {
 
 static int lm80_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
-static int lm80_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info);
+static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info);
 static void lm80_init_client(struct i2c_client *client);
 static int lm80_remove(struct i2c_client *client);
 static struct lm80_data *lm80_update_device(struct device *dev);
@@ -146,7 +142,7 @@ static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
  */
 
 static const struct i2c_device_id lm80_id[] = {
-       { "lm80", lm80 },
+       { "lm80", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm80_id);
@@ -160,7 +156,7 @@ static struct i2c_driver lm80_driver = {
        .remove         = lm80_remove,
        .id_table       = lm80_id,
        .detect         = lm80_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -447,8 +443,7 @@ static const struct attribute_group lm80_group = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm80_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info)
+static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
        int i, cur;
index 08b03e6ed0b7adf3a21aab5ac42c4062982fa767..8290476aee4ab981d6f918fb92d6b2940d4e4a65 100644 (file)
 static const unsigned short normal_i2c[] = {
        0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_2(lm83, lm82);
+enum chips { lm83, lm82 };
 
 /*
  * The LM83 registers
@@ -118,7 +114,7 @@ static const u8 LM83_REG_W_HIGH[] = {
  * Functions declaration
  */
 
-static int lm83_detect(struct i2c_client *new_client, int kind,
+static int lm83_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info);
 static int lm83_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
@@ -145,7 +141,7 @@ static struct i2c_driver lm83_driver = {
        .remove         = lm83_remove,
        .id_table       = lm83_id,
        .detect         = lm83_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -291,7 +287,7 @@ static const struct attribute_group lm83_group_opt = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm83_detect(struct i2c_client *new_client, int kind,
+static int lm83_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
index d56da2e747080b210b907f0291795f93755bd135..b3841a615595658cc3ddd0675d827becaf53fc45 100644 (file)
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_7(lm85b, lm85c, adm1027, adt7463, adt7468, emc6d100,
-                   emc6d102);
+enum chips {
+       any_chip, lm85b, lm85c,
+       adm1027, adt7463, adt7468,
+       emc6d100, emc6d102
+};
 
 /* The LM85 registers */
 
@@ -323,8 +325,7 @@ struct lm85_data {
        struct lm85_zone zone[3];
 };
 
-static int lm85_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info);
+static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info);
 static int lm85_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
 static int lm85_remove(struct i2c_client *client);
@@ -357,7 +358,7 @@ static struct i2c_driver lm85_driver = {
        .remove         = lm85_remove,
        .id_table       = lm85_id,
        .detect         = lm85_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 
@@ -1156,8 +1157,7 @@ static int lm85_is_fake(struct i2c_client *client)
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm85_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info)
+static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
        int address = client->addr;
index 4929b1815eee47ee1a4c30b89d111b6eb0c468f8..f1e6e7512ffa2e8f308b3de9483b943507d19913 100644 (file)
 
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_2(lm87, adm1024);
+enum chips { lm87, adm1024 };
 
 /*
  * The LM87 registers
@@ -158,7 +154,7 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
 
 static int lm87_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
-static int lm87_detect(struct i2c_client *new_client, int kind,
+static int lm87_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info);
 static void lm87_init_client(struct i2c_client *client);
 static int lm87_remove(struct i2c_client *client);
@@ -184,7 +180,7 @@ static struct i2c_driver lm87_driver = {
        .remove         = lm87_remove,
        .id_table       = lm87_id,
        .detect         = lm87_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -662,7 +658,7 @@ static const struct attribute_group lm87_group_opt = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm87_detect(struct i2c_client *new_client, int kind,
+static int lm87_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
index b7c905f50ed437da556ebd37007b3b8440cb23b8..7c9bdc16742653e0dd2bcb812542f7c1f61a233d 100644 (file)
 static const unsigned short normal_i2c[] = {
        0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680,
-                   max6646);
+enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646 };
 
 /*
  * The LM90 registers
@@ -152,8 +147,7 @@ I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680,
  * Functions declaration
  */
 
-static int lm90_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info);
+static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info);
 static int lm90_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
 static void lm90_init_client(struct i2c_client *client);
@@ -192,7 +186,7 @@ static struct i2c_driver lm90_driver = {
        .remove         = lm90_remove,
        .id_table       = lm90_id,
        .detect         = lm90_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -656,7 +650,7 @@ static int lm90_read_reg(struct i2c_client* client, u8 reg, u8 *value)
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm90_detect(struct i2c_client *new_client, int kind,
+static int lm90_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
index 47ac698709dc2e73650820f8305b6e095de74bef..7c31e6205f8513da02a0b8dc7012a3c3ca4ffbf8 100644 (file)
@@ -54,9 +54,6 @@
 static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
                                                I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm92);
-
 /* The LM92 registers */
 #define LM92_REG_CONFIG                        0x01 /* 8-bit, RW */
 #define LM92_REG_TEMP                  0x00 /* 16-bit, RO */
@@ -319,7 +316,7 @@ static const struct attribute_group lm92_group = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm92_detect(struct i2c_client *new_client, int kind,
+static int lm92_detect(struct i2c_client *new_client,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
@@ -401,7 +398,7 @@ static int lm92_remove(struct i2c_client *client)
  */
 
 static const struct i2c_device_id lm92_id[] = {
-       { "lm92", lm92 },
+       { "lm92", 0 },
        /* max6635 could be added here */
        { }
 };
@@ -416,7 +413,7 @@ static struct i2c_driver lm92_driver = {
        .remove         = lm92_remove,
        .id_table       = lm92_id,
        .detect         = lm92_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static int __init sensors_lm92_init(void)
index 124dd7cea54cb72c2d94a0ecd9203fdc6047ce72..6669255aadcfcefc783c01f0c345a72b411f70c9 100644 (file)
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm93);
 
 static int disable_block;
 module_param(disable_block, bool, 0);
@@ -2501,8 +2500,7 @@ static void lm93_init_client(struct i2c_client *client)
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm93_detect(struct i2c_client *client, int kind,
-                      struct i2c_board_info *info)
+static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
        int mfr, ver;
@@ -2603,7 +2601,7 @@ static int lm93_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id lm93_id[] = {
-       { "lm93", lm93 },
+       { "lm93", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm93_id);
@@ -2617,7 +2615,7 @@ static struct i2c_driver lm93_driver = {
        .remove         = lm93_remove,
        .id_table       = lm93_id,
        .detect         = lm93_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static int __init lm93_init(void)
index 906b896cf1d0fa6616ce5a8750f54f8e1937bf75..8fc8eb8cba47400fd23614a392b135949aab8c76 100644 (file)
@@ -39,9 +39,6 @@
 static const unsigned short normal_i2c[] = {
        0x19, 0x2a, 0x2b, I2C_CLIENT_END};
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(lm95241);
-
 /* LM95241 registers */
 #define LM95241_REG_R_MAN_ID           0xFE
 #define LM95241_REG_R_CHIP_ID          0xFF
@@ -310,7 +307,7 @@ static const struct attribute_group lm95241_group = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm95241_detect(struct i2c_client *new_client, int kind,
+static int lm95241_detect(struct i2c_client *new_client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
@@ -446,7 +443,7 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
 
 /* Driver data (common to all clients) */
 static const struct i2c_device_id lm95241_id[] = {
-       { "lm95241", lm95241 },
+       { "lm95241", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm95241_id);
@@ -460,7 +457,7 @@ static struct i2c_driver lm95241_driver = {
        .remove         = lm95241_remove,
        .id_table       = lm95241_id,
        .detect         = lm95241_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static int __init sensors_lm95241_init(void)
index 7fcf5ff89e7f903b7fee1dc4cfa10ac8769613be..022ded098100edbdb7fe69210f8576a9db66b535 100644 (file)
 static const unsigned short normal_i2c[] = {
        0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(max1619);
-
 /*
  * The MAX1619 registers
  */
@@ -88,7 +82,7 @@ static int temp_to_reg(int val)
 
 static int max1619_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int max1619_detect(struct i2c_client *client, int kind,
+static int max1619_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static void max1619_init_client(struct i2c_client *client);
 static int max1619_remove(struct i2c_client *client);
@@ -99,7 +93,7 @@ static struct max1619_data *max1619_update_device(struct device *dev);
  */
 
 static const struct i2c_device_id max1619_id[] = {
-       { "max1619", max1619 },
+       { "max1619", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max1619_id);
@@ -113,7 +107,7 @@ static struct i2c_driver max1619_driver = {
        .remove         = max1619_remove,
        .id_table       = max1619_id,
        .detect         = max1619_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -226,7 +220,7 @@ static const struct attribute_group max1619_group = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int max1619_detect(struct i2c_client *client, int kind,
+static int max1619_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 1da561e0cb37dbd6e8e8db112a94b4bdd5893a0b..a0160ee5caeffcefc6d7da22a7513a13b32fcf43 100644 (file)
@@ -62,8 +62,6 @@ module_param(fan_voltage, int, S_IRUGO);
 module_param(prescaler, int, S_IRUGO);
 module_param(clock, int, S_IRUGO);
 
-I2C_CLIENT_INSMOD_1(max6650);
-
 /*
  * MAX 6650/6651 registers
  */
@@ -116,7 +114,7 @@ I2C_CLIENT_INSMOD_1(max6650);
 
 static int max6650_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int max6650_detect(struct i2c_client *client, int kind,
+static int max6650_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int max6650_init_client(struct i2c_client *client);
 static int max6650_remove(struct i2c_client *client);
@@ -127,7 +125,7 @@ static struct max6650_data *max6650_update_device(struct device *dev);
  */
 
 static const struct i2c_device_id max6650_id[] = {
-       { "max6650", max6650 },
+       { "max6650", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max6650_id);
@@ -141,7 +139,7 @@ static struct i2c_driver max6650_driver = {
        .remove         = max6650_remove,
        .id_table       = max6650_id,
        .detect         = max6650_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -528,7 +526,7 @@ static struct attribute_group max6650_attr_grp = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int max6650_detect(struct i2c_client *client, int kind,
+static int max6650_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 1d7ffebd679d0cce6f25c9e1946d7e9739c1b4b5..d447879498516d9f385bf37070f8ff0f3fc10f23 100644 (file)
@@ -29,7 +29,6 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
                                        0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(pcf8591);
 
 static int input_mode;
 module_param(input_mode, int, 0);
@@ -169,7 +168,7 @@ static const struct attribute_group pcf8591_attr_group_opt = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int pcf8591_detect(struct i2c_client *client, int kind,
+static int pcf8591_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
@@ -299,7 +298,7 @@ static struct i2c_driver pcf8591_driver = {
 
        .class          = I2C_CLASS_HWMON,      /* Nearest choice */
        .detect         = pcf8591_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static int __init pcf8591_init(void)
index 4d88c045781c6cbac9ebebb83037bc9364318adc..40b26673d87f3563946dd34a8cac909b31ddfdaa 100644 (file)
@@ -36,9 +36,6 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(smsc47m192);
-
 /* SMSC47M192 registers */
 #define SMSC47M192_REG_IN(nr)          ((nr)<6 ? (0x20 + (nr)) : \
                                        (0x50 + (nr) - 6))
@@ -115,13 +112,13 @@ struct smsc47m192_data {
 
 static int smsc47m192_probe(struct i2c_client *client,
                            const struct i2c_device_id *id);
-static int smsc47m192_detect(struct i2c_client *client, int kind,
+static int smsc47m192_detect(struct i2c_client *client,
                             struct i2c_board_info *info);
 static int smsc47m192_remove(struct i2c_client *client);
 static struct smsc47m192_data *smsc47m192_update_device(struct device *dev);
 
 static const struct i2c_device_id smsc47m192_id[] = {
-       { "smsc47m192", smsc47m192 },
+       { "smsc47m192", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, smsc47m192_id);
@@ -135,7 +132,7 @@ static struct i2c_driver smsc47m192_driver = {
        .remove         = smsc47m192_remove,
        .id_table       = smsc47m192_id,
        .detect         = smsc47m192_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /* Voltages */
@@ -481,7 +478,7 @@ static void smsc47m192_init_client(struct i2c_client *client)
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int smsc47m192_detect(struct i2c_client *client, int kind,
+static int smsc47m192_detect(struct i2c_client *client,
                             struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 4b793849c73818eb3fd6d5c8cd2aca986d17d514..7dfb4dec4c5f6ca3fb60d91e398446b1456d3423 100644 (file)
@@ -35,7 +35,7 @@ MODULE_LICENSE("GPL");
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_2(thmc50, adm1022);
+enum chips { thmc50, adm1022 };
 
 static unsigned short adm1022_temp3[16];
 static unsigned int adm1022_temp3_num;
@@ -84,7 +84,7 @@ struct thmc50_data {
        u8 alarms;
 };
 
-static int thmc50_detect(struct i2c_client *client, int kind,
+static int thmc50_detect(struct i2c_client *client,
                         struct i2c_board_info *info);
 static int thmc50_probe(struct i2c_client *client,
                        const struct i2c_device_id *id);
@@ -108,7 +108,7 @@ static struct i2c_driver thmc50_driver = {
        .remove = thmc50_remove,
        .id_table = thmc50_id,
        .detect = thmc50_detect,
-       .address_data = &addr_data,
+       .address_list = normal_i2c,
 };
 
 static ssize_t show_analog_out(struct device *dev,
@@ -286,7 +286,7 @@ static const struct attribute_group temp3_group = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int thmc50_detect(struct i2c_client *client, int kind,
+static int thmc50_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        unsigned company;
index ee9673467c4a7289ac8b0b6313d2ed3a4222a733..a13b30e8d8d8f982f71ff5e22f42f1347e7f55ad 100644 (file)
@@ -42,8 +42,7 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(tmp401, tmp411);
+enum chips { tmp401, tmp411 };
 
 /*
  * The TMP401 registers, note some registers have different addresses for
@@ -98,7 +97,7 @@ static const u8 TMP411_TEMP_HIGHEST_LSB[2]            = { 0x33, 0x37 };
 
 static int tmp401_probe(struct i2c_client *client,
                        const struct i2c_device_id *id);
-static int tmp401_detect(struct i2c_client *client, int kind,
+static int tmp401_detect(struct i2c_client *client,
                         struct i2c_board_info *info);
 static int tmp401_remove(struct i2c_client *client);
 static struct tmp401_data *tmp401_update_device(struct device *dev);
@@ -123,7 +122,7 @@ static struct i2c_driver tmp401_driver = {
        .remove         = tmp401_remove,
        .id_table       = tmp401_id,
        .detect         = tmp401_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -488,7 +487,7 @@ static void tmp401_init_client(struct i2c_client *client)
                i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
 }
 
-static int tmp401_detect(struct i2c_client *client, int _kind,
+static int tmp401_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        enum chips kind;
index bb5464a289cac8e9b8d3cd5d9a39a98656276ad4..4f7c051e2d7b0425308489fcbbc6c04eda68947d 100644 (file)
@@ -39,8 +39,7 @@
 static unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f,
                                       I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_3(tmp421, tmp422, tmp423);
+enum chips { tmp421, tmp422, tmp423 };
 
 /* The TMP421 registers */
 #define TMP421_CONFIG_REG_1                    0x09
@@ -223,7 +222,7 @@ static int tmp421_init_client(struct i2c_client *client)
        return 0;
 }
 
-static int tmp421_detect(struct i2c_client *client, int _kind,
+static int tmp421_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        enum chips kind;
@@ -322,7 +321,7 @@ static struct i2c_driver tmp421_driver = {
        .remove = tmp421_remove,
        .id_table = tmp421_id,
        .detect = tmp421_detect,
-       .address_data = &addr_data,
+       .address_list = normal_i2c,
 };
 
 static int __init tmp421_init(void)
index bb5e78748783b71c465bdcc14c478cdcc06cf9c9..0dcaba9b7189c26cd242a2acfb72804e083a7f70 100644 (file)
@@ -5,6 +5,7 @@
     Copyright (C) 2006  Yuan Mu (Winbond),
                         Rudolf Marek <r.marek@assembler.cz>
                         David Hubbard <david.c.hubbard@gmail.com>
+                       Daniel J Blueman <daniel.blueman@gmail.com>
 
     Shamelessly ripped from the w83627hf driver
     Copyright (C) 2003  Mark Studebaker
@@ -177,12 +178,15 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
 #define W83627EHF_REG_ALARM3           0x45B
 
 /* SmartFan registers */
+#define W83627EHF_REG_FAN_STEPUP_TIME 0x0f
+#define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e
+
 /* DC or PWM output fan configuration */
 static const u8 W83627EHF_REG_PWM_ENABLE[] = {
        0x04,                   /* SYS FAN0 output mode and PWM mode */
        0x04,                   /* CPU FAN0 output mode and PWM mode */
        0x12,                   /* AUX FAN mode */
-       0x62,                   /* CPU fan1 mode */
+       0x62,                   /* CPU FAN1 mode */
 };
 
 static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
@@ -193,10 +197,12 @@ static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
 static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
 static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
 
-
 /* Advanced Fan control, some values are common for all fans */
-static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
-static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
+static const u8 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 };
+static const u8 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
+static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 };
+static const u8 W83627EHF_REG_FAN_MAX_OUTPUT[] = { 0xff, 0x67, 0xff, 0x69 };
+static const u8 W83627EHF_REG_FAN_STEP_OUTPUT[] = { 0xff, 0x68, 0xff, 0x6a };
 
 /*
  * Conversions
@@ -295,14 +301,19 @@ struct w83627ehf_data {
 
        u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
        u8 pwm_enable[4]; /* 1->manual
-                            2->thermal cruise (also called SmartFan I) */
+                            2->thermal cruise mode (also called SmartFan I)
+                            3->fan speed cruise mode
+                            4->variable thermal cruise (also called SmartFan III) */
        u8 pwm_num;             /* number of pwm */
        u8 pwm[4];
        u8 target_temp[4];
        u8 tolerance[4];
 
-       u8 fan_min_output[4]; /* minimum fan speed */
-       u8 fan_stop_time[4];
+       u8 fan_start_output[4]; /* minimum fan speed when spinning up */
+       u8 fan_stop_output[4]; /* minimum fan speed when spinning down */
+       u8 fan_stop_time[4]; /* time at minimum before disabling fan */
+       u8 fan_max_output[4]; /* maximum fan speed */
+       u8 fan_step_output[4]; /* rate of change output value */
 
        u8 vid;
        u8 vrm;
@@ -529,8 +540,10 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
                                                & 3) + 1;
                        data->pwm[i] = w83627ehf_read_value(data,
                                                W83627EHF_REG_PWM[i]);
-                       data->fan_min_output[i] = w83627ehf_read_value(data,
-                                               W83627EHF_REG_FAN_MIN_OUTPUT[i]);
+                       data->fan_start_output[i] = w83627ehf_read_value(data,
+                                               W83627EHF_REG_FAN_START_OUTPUT[i]);
+                       data->fan_stop_output[i] = w83627ehf_read_value(data,
+                                               W83627EHF_REG_FAN_STOP_OUTPUT[i]);
                        data->fan_stop_time[i] = w83627ehf_read_value(data,
                                                W83627EHF_REG_FAN_STOP_TIME[i]);
                        data->target_temp[i] =
@@ -976,7 +989,7 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
        u32 val = simple_strtoul(buf, NULL, 10);
        u16 reg;
 
-       if (!val || (val > 2))  /* only modes 1 and 2 are supported */
+       if (!val || (val > 4))
                return -EINVAL;
        mutex_lock(&data->update_lock);
        reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]);
@@ -1118,7 +1131,10 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
        return count; \
 }
 
-fan_functions(fan_min_output, FAN_MIN_OUTPUT)
+fan_functions(fan_start_output, FAN_START_OUTPUT)
+fan_functions(fan_stop_output, FAN_STOP_OUTPUT)
+fan_functions(fan_max_output, FAN_MAX_OUTPUT)
+fan_functions(fan_step_output, FAN_STEP_OUTPUT)
 
 #define fan_time_functions(reg, REG) \
 static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
@@ -1161,8 +1177,14 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
        SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
                    store_fan_stop_time, 3),
-       SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
-                   store_fan_min_output, 3),
+       SENSOR_ATTR(pwm4_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+                   store_fan_start_output, 3),
+       SENSOR_ATTR(pwm4_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+                   store_fan_stop_output, 3),
+       SENSOR_ATTR(pwm4_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
+                   store_fan_max_output, 3),
+       SENSOR_ATTR(pwm4_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
+                   store_fan_step_output, 3),
 };
 
 static struct sensor_device_attribute sda_sf3_arrays[] = {
@@ -1172,12 +1194,24 @@ static struct sensor_device_attribute sda_sf3_arrays[] = {
                    store_fan_stop_time, 1),
        SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
                    store_fan_stop_time, 2),
-       SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
-                   store_fan_min_output, 0),
-       SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
-                   store_fan_min_output, 1),
-       SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
-                   store_fan_min_output, 2),
+       SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+                   store_fan_start_output, 0),
+       SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+                   store_fan_start_output, 1),
+       SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+                   store_fan_start_output, 2),
+       SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+                   store_fan_stop_output, 0),
+       SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+                   store_fan_stop_output, 1),
+       SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+                   store_fan_stop_output, 2),
+
+       /* pwm1 and pwm3 don't support max and step settings */
+       SENSOR_ATTR(pwm2_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
+                   store_fan_max_output, 1),
+       SENSOR_ATTR(pwm2_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
+                   store_fan_step_output, 1),
 };
 
 static ssize_t
index 7ab7967da0a0359fe1dca5f90518c927752aedf5..05f9225b6f944dd7007c752eb9c15786f41c6740 100644 (file)
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
                                                0x2e, 0x2f, I2C_CLIENT_END };
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f);
 
+enum chips { w83781d, w83782d, w83783s, as99127f };
+
+/* Insmod parameters */
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
 MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
@@ -1051,8 +1052,7 @@ w83781d_create_files(struct device *dev, int kind, int is_isa)
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
 static int
-w83781d_detect(struct i2c_client *client, int kind,
-              struct i2c_board_info *info)
+w83781d_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        int val1, val2;
        struct w83781d_data *isa = w83781d_data_if_isa();
@@ -1537,7 +1537,7 @@ static struct i2c_driver w83781d_driver = {
        .remove         = w83781d_remove,
        .id_table       = w83781d_ids,
        .detect         = w83781d_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
index 0410bf12c5211cbb9bb27116d3dd343726adba8c..400a88bde278dc2d6778993e27c2849c629c799c 100644 (file)
@@ -52,7 +52,6 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
                                                I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83791d);
 
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
@@ -326,7 +325,7 @@ struct w83791d_data {
 
 static int w83791d_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int w83791d_detect(struct i2c_client *client, int kind,
+static int w83791d_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int w83791d_remove(struct i2c_client *client);
 
@@ -341,7 +340,7 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev);
 static void w83791d_init_client(struct i2c_client *client);
 
 static const struct i2c_device_id w83791d_id[] = {
-       { "w83791d", w83791d },
+       { "w83791d", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, w83791d_id);
@@ -355,7 +354,7 @@ static struct i2c_driver w83791d_driver = {
        .remove         = w83791d_remove,
        .id_table       = w83791d_id,
        .detect         = w83791d_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /* following are the sysfs callback functions */
@@ -1259,7 +1258,7 @@ error_sc_0:
 
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83791d_detect(struct i2c_client *client, int kind,
+static int w83791d_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 38978851333f6361cbb62d76d4210c6edf5e4662..679718e6b0173a0e0fec17b755465e1476c04cd1 100644 (file)
@@ -50,7 +50,6 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
                                                I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83792d);
 
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
@@ -302,7 +301,7 @@ struct w83792d_data {
 
 static int w83792d_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int w83792d_detect(struct i2c_client *client, int kind,
+static int w83792d_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int w83792d_remove(struct i2c_client *client);
 static struct w83792d_data *w83792d_update_device(struct device *dev);
@@ -314,7 +313,7 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev);
 static void w83792d_init_client(struct i2c_client *client);
 
 static const struct i2c_device_id w83792d_id[] = {
-       { "w83792d", w83792d },
+       { "w83792d", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, w83792d_id);
@@ -328,7 +327,7 @@ static struct i2c_driver w83792d_driver = {
        .remove         = w83792d_remove,
        .id_table       = w83792d_id,
        .detect         = w83792d_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static inline long in_count_from_reg(int nr, struct w83792d_data *data)
@@ -1263,7 +1262,7 @@ static const struct attribute_group w83792d_group = {
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
 static int
-w83792d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
+w83792d_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
        int val1, val2;
index 80a2191bf1274d0b54581113972e669d6be0822d..9a2022b67495d9d2daa019173dae42f66c5b24ce 100644 (file)
@@ -41,7 +41,6 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
                                                I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83793);
 
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
@@ -230,7 +229,7 @@ static u8 w83793_read_value(struct i2c_client *client, u16 reg);
 static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value);
 static int w83793_probe(struct i2c_client *client,
                        const struct i2c_device_id *id);
-static int w83793_detect(struct i2c_client *client, int kind,
+static int w83793_detect(struct i2c_client *client,
                         struct i2c_board_info *info);
 static int w83793_remove(struct i2c_client *client);
 static void w83793_init_client(struct i2c_client *client);
@@ -238,7 +237,7 @@ static void w83793_update_nonvolatile(struct device *dev);
 static struct w83793_data *w83793_update_device(struct device *dev);
 
 static const struct i2c_device_id w83793_id[] = {
-       { "w83793", w83793 },
+       { "w83793", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, w83793_id);
@@ -252,7 +251,7 @@ static struct i2c_driver w83793_driver = {
        .remove         = w83793_remove,
        .id_table       = w83793_id,
        .detect         = w83793_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static ssize_t
@@ -1161,7 +1160,7 @@ ERROR_SC_0:
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83793_detect(struct i2c_client *client, int kind,
+static int w83793_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
        u8 tmp, bank, chip_id;
index 9b6c4c10fba75feaca0f8ca872619ad17fb6be7c..20781def65edaa280cb3ed2124adfb17c0fe4b57 100644 (file)
 
 static const unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END };
 
-/*
- * Insmod parameters
- */
-
-I2C_CLIENT_INSMOD_1(w83l785ts);
-
 /*
  * The W83L785TS-S registers
  * Manufacturer ID is 0x5CA3 for Winbond.
@@ -83,7 +77,7 @@ I2C_CLIENT_INSMOD_1(w83l785ts);
 
 static int w83l785ts_probe(struct i2c_client *client,
                           const struct i2c_device_id *id);
-static int w83l785ts_detect(struct i2c_client *client, int kind,
+static int w83l785ts_detect(struct i2c_client *client,
                            struct i2c_board_info *info);
 static int w83l785ts_remove(struct i2c_client *client);
 static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval);
@@ -94,7 +88,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev);
  */
  
 static const struct i2c_device_id w83l785ts_id[] = {
-       { "w83l785ts", w83l785ts },
+       { "w83l785ts", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, w83l785ts_id);
@@ -108,7 +102,7 @@ static struct i2c_driver w83l785ts_driver = {
        .remove         = w83l785ts_remove,
        .id_table       = w83l785ts_id,
        .detect         = w83l785ts_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 /*
@@ -146,7 +140,7 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1);
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83l785ts_detect(struct i2c_client *client, int kind,
+static int w83l785ts_detect(struct i2c_client *client,
                            struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 27da7d2b15fb6fb82bbd0b60d63637d2dfb55367..0254e181893d9f2e9bd0d010d37f6544559ad3e5 100644 (file)
@@ -38,7 +38,6 @@
 static const unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(w83l786ng);
 
 static int reset;
 module_param(reset, bool, 0);
@@ -147,14 +146,14 @@ struct w83l786ng_data {
 
 static int w83l786ng_probe(struct i2c_client *client,
                           const struct i2c_device_id *id);
-static int w83l786ng_detect(struct i2c_client *client, int kind,
+static int w83l786ng_detect(struct i2c_client *client,
                            struct i2c_board_info *info);
 static int w83l786ng_remove(struct i2c_client *client);
 static void w83l786ng_init_client(struct i2c_client *client);
 static struct w83l786ng_data *w83l786ng_update_device(struct device *dev);
 
 static const struct i2c_device_id w83l786ng_id[] = {
-       { "w83l786ng", w83l786ng },
+       { "w83l786ng", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, w83l786ng_id);
@@ -168,7 +167,7 @@ static struct i2c_driver w83l786ng_driver = {
        .remove         = w83l786ng_remove,
        .id_table       = w83l786ng_id,
        .detect         = w83l786ng_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static u8
@@ -586,8 +585,7 @@ static const struct attribute_group w83l786ng_group = {
 };
 
 static int
-w83l786ng_detect(struct i2c_client *client, int kind,
-                struct i2c_board_info *info)
+w83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
        u16 man_id;
index 049555777f67da5549cca3d689977809bbef0153..7647a20523a02dc01ccd58b5ed777448f00e57f6 100644 (file)
@@ -1155,7 +1155,7 @@ static int i2c_pxa_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops i2c_pxa_dev_pm_ops = {
+static const struct dev_pm_ops i2c_pxa_dev_pm_ops = {
        .suspend_noirq = i2c_pxa_suspend_noirq,
        .resume_noirq = i2c_pxa_resume_noirq,
 };
index 96aafb91b69a78e482eab02d3df45dbd70a7bfc3..1d8c98613fa057c88f75b4a7bd793348c60b8da8 100644 (file)
@@ -967,7 +967,7 @@ static int s3c24xx_i2c_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
+static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
        .suspend_noirq = s3c24xx_i2c_suspend_noirq,
        .resume = s3c24xx_i2c_resume,
 };
index 86a9d4e8147288dac1866b98588f013d4c02268b..ccc46418ef7f16b2409c28cdf7f7f8ebdb6796d4 100644 (file)
@@ -647,7 +647,7 @@ static int sh_mobile_i2c_runtime_nop(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = {
+static const struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = {
        .runtime_suspend = sh_mobile_i2c_runtime_nop,
        .runtime_resume = sh_mobile_i2c_runtime_nop,
 };
index 4f34823e86b103ffbac7b9f51bb753ddcacf52ba..0ac2f90ab840c8f1863013ba9551a3266247f738 100644 (file)
@@ -155,6 +155,35 @@ static void i2c_device_shutdown(struct device *dev)
                driver->shutdown(client);
 }
 
+#ifdef CONFIG_SUSPEND
+static int i2c_device_pm_suspend(struct device *dev)
+{
+       const struct dev_pm_ops *pm;
+
+       if (!dev->driver)
+               return 0;
+       pm = dev->driver->pm;
+       if (!pm || !pm->suspend)
+               return 0;
+       return pm->suspend(dev);
+}
+
+static int i2c_device_pm_resume(struct device *dev)
+{
+       const struct dev_pm_ops *pm;
+
+       if (!dev->driver)
+               return 0;
+       pm = dev->driver->pm;
+       if (!pm || !pm->resume)
+               return 0;
+       return pm->resume(dev);
+}
+#else
+#define i2c_device_pm_suspend  NULL
+#define i2c_device_pm_resume   NULL
+#endif
+
 static int i2c_device_suspend(struct device *dev, pm_message_t mesg)
 {
        struct i2c_client *client = i2c_verify_client(dev);
@@ -219,6 +248,11 @@ static const struct attribute_group *i2c_dev_attr_groups[] = {
        NULL
 };
 
+const static struct dev_pm_ops i2c_device_pm_ops = {
+       .suspend = i2c_device_pm_suspend,
+       .resume = i2c_device_pm_resume,
+};
+
 struct bus_type i2c_bus_type = {
        .name           = "i2c",
        .match          = i2c_device_match,
@@ -227,6 +261,7 @@ struct bus_type i2c_bus_type = {
        .shutdown       = i2c_device_shutdown,
        .suspend        = i2c_device_suspend,
        .resume         = i2c_device_resume,
+       .pm             = &i2c_device_pm_ops,
 };
 EXPORT_SYMBOL_GPL(i2c_bus_type);
 
@@ -1184,7 +1219,7 @@ static int i2c_detect_address(struct i2c_client *temp_client,
        /* Finally call the custom detection function */
        memset(&info, 0, sizeof(struct i2c_board_info));
        info.addr = addr;
-       err = driver->detect(temp_client, -1, &info);
+       err = driver->detect(temp_client, &info);
        if (err) {
                /* -ENODEV is returned if the detection fails. We catch it
                   here as this isn't an error. */
@@ -1214,13 +1249,13 @@ static int i2c_detect_address(struct i2c_client *temp_client,
 
 static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
 {
-       const struct i2c_client_address_data *address_data;
+       const unsigned short *address_list;
        struct i2c_client *temp_client;
        int i, err = 0;
        int adap_id = i2c_adapter_id(adapter);
 
-       address_data = driver->address_data;
-       if (!driver->detect || !address_data)
+       address_list = driver->address_list;
+       if (!driver->detect || !address_list)
                return 0;
 
        /* Set up a temporary client to help detect callback */
@@ -1235,7 +1270,7 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
 
        /* Stop here if we can't use SMBUS_QUICK */
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
-               if (address_data->normal_i2c[0] == I2C_CLIENT_END)
+               if (address_list[0] == I2C_CLIENT_END)
                        goto exit_free;
 
                dev_warn(&adapter->dev, "SMBus Quick command not supported, "
@@ -1244,11 +1279,10 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
                goto exit_free;
        }
 
-       for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
+       for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
-                       "addr 0x%02x\n", adap_id,
-                       address_data->normal_i2c[i]);
-               temp_client->addr = address_data->normal_i2c[i];
+                       "addr 0x%02x\n", adap_id, address_list[i]);
+               temp_client->addr = address_list[i];
                err = i2c_detect_address(temp_client, driver);
                if (err)
                        goto exit_free;
index d48c808d59281b415505842ae5d1fb0ff55a51ac..1edb596d927bd885dea073593a3183fc63b45a54 100644 (file)
@@ -319,7 +319,7 @@ static int adp5588_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops adp5588_dev_pm_ops = {
+static const struct dev_pm_ops adp5588_dev_pm_ops = {
        .suspend = adp5588_suspend,
        .resume  = adp5588_resume,
 };
index 076111fc72d227e3216f94e975ea1f9eaf675dcf..8e9380bfed4097fcdc283cfc7c3532ed0e911318 100644 (file)
@@ -295,7 +295,7 @@ static int sh_keysc_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sh_keysc_dev_pm_ops = {
+static const struct dev_pm_ops sh_keysc_dev_pm_ops = {
        .suspend = sh_keysc_suspend,
        .resume = sh_keysc_resume,
 };
index 690f3fafa03b9dd66ac4fa42d9d433addaa0b03a..61d10177fa83750b0162468956742564466e9e25 100644 (file)
@@ -247,7 +247,7 @@ static int bfin_rotary_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops bfin_rotary_pm_ops = {
+static const struct dev_pm_ops bfin_rotary_pm_ops = {
        .suspend        = bfin_rotary_suspend,
        .resume         = bfin_rotary_resume,
 };
index 21cb755a54fb925deda36db17933a0991bb0969c..ea4e1fd126516c05a67a0aa58ed27421f1f8de78 100644 (file)
@@ -127,7 +127,7 @@ static void pcspkr_shutdown(struct platform_device *dev)
        pcspkr_event(NULL, EV_SND, SND_BELL, 0);
 }
 
-static struct dev_pm_ops pcspkr_pm_ops = {
+static const struct dev_pm_ops pcspkr_pm_ops = {
        .suspend = pcspkr_suspend,
 };
 
index 67fcd33595def87293469b0caf625dccdaf8d83c..b79097e3028a10e381f1c259c78ce0259985f2f1 100644 (file)
@@ -233,7 +233,7 @@ static int pcap_ts_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops pcap_ts_pm_ops = {
+static const struct dev_pm_ops pcap_ts_pm_ops = {
        .suspend        = pcap_ts_suspend,
        .resume         = pcap_ts_resume,
 };
index f2cc13d76810f457c752d6f92f53759980f465ea..782f95822eab0867f8db83666c5ba08d3a77a23e 100644 (file)
@@ -50,7 +50,7 @@ static ssize_t led_brightness_store(struct device *dev,
        unsigned long state = simple_strtoul(buf, &after, 10);
        size_t count = after - buf;
 
-       if (*after && isspace(*after))
+       if (isspace(*after))
                count++;
 
        if (count == size) {
index 3b83406de7520b8052b73906992f2fa34e1b3891..38b3378be442b3d6501b8ffb9aa67e8eaba21ef4 100644 (file)
@@ -83,7 +83,7 @@ static ssize_t led_delay_on_store(struct device *dev,
        unsigned long state = simple_strtoul(buf, &after, 10);
        size_t count = after - buf;
 
-       if (*after && isspace(*after))
+       if (isspace(*after))
                count++;
 
        if (count == size) {
@@ -127,7 +127,7 @@ static ssize_t led_delay_off_store(struct device *dev,
        unsigned long state = simple_strtoul(buf, &after, 10);
        size_t count = after - buf;
 
-       if (*after && isspace(*after))
+       if (isspace(*after))
                count++;
 
        if (count == size) {
index 1a6cb3c7822ed96c6c0b2eaa764522c5f0e7ae89..91976e8fae5f0326027a534c7b20f132f2d76433 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/blkdev.h>
 #include <linux/namei.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
@@ -600,11 +601,8 @@ int dm_split_args(int *argc, char ***argvp, char *input)
                return -ENOMEM;
 
        while (1) {
-               start = end;
-
                /* Skip whitespace */
-               while (*start && isspace(*start))
-                       start++;
+               start = skip_spaces(end);
 
                if (!*start)
                        break;  /* success, we hit the end */
index e1f3c1715cca2ae459f0a70d7229128685ee2d70..f4f5f82f9f533109fff1898e14d7e30824c62d53 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/buffer_head.h> /* for invalidate_bdev */
 #include <linux/poll.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/hdreg.h>
 #include <linux/proc_fs.h>
 #include <linux/random.h>
@@ -1935,15 +1936,11 @@ static void print_sb_1(struct mdp_superblock_1 *sb)
 
        uuid = sb->set_uuid;
        printk(KERN_INFO
-              "md:  SB: (V:%u) (F:0x%08x) Array-ID:<%02x%02x%02x%02x"
-              ":%02x%02x:%02x%02x:%02x%02x:%02x%02x%02x%02x%02x%02x>\n"
+              "md:  SB: (V:%u) (F:0x%08x) Array-ID:<%pU>\n"
               "md:    Name: \"%s\" CT:%llu\n",
                le32_to_cpu(sb->major_version),
                le32_to_cpu(sb->feature_map),
-               uuid[0], uuid[1], uuid[2], uuid[3],
-               uuid[4], uuid[5], uuid[6], uuid[7],
-               uuid[8], uuid[9], uuid[10], uuid[11],
-               uuid[12], uuid[13], uuid[14], uuid[15],
+               uuid,
                sb->set_name,
                (unsigned long long)le64_to_cpu(sb->ctime)
                       & MD_SUPERBLOCK_1_TIME_SEC_MASK);
@@ -1952,8 +1949,7 @@ static void print_sb_1(struct mdp_superblock_1 *sb)
        printk(KERN_INFO
               "md:       L%u SZ%llu RD:%u LO:%u CS:%u DO:%llu DS:%llu SO:%llu"
                        " RO:%llu\n"
-              "md:     Dev:%08x UUID: %02x%02x%02x%02x:%02x%02x:%02x%02x:%02x%02x"
-                       ":%02x%02x%02x%02x%02x%02x\n"
+              "md:     Dev:%08x UUID: %pU\n"
               "md:       (F:0x%08x) UT:%llu Events:%llu ResyncOffset:%llu CSUM:0x%08x\n"
               "md:         (MaxDev:%u) \n",
                le32_to_cpu(sb->level),
@@ -1966,10 +1962,7 @@ static void print_sb_1(struct mdp_superblock_1 *sb)
                (unsigned long long)le64_to_cpu(sb->super_offset),
                (unsigned long long)le64_to_cpu(sb->recovery_offset),
                le32_to_cpu(sb->dev_number),
-               uuid[0], uuid[1], uuid[2], uuid[3],
-               uuid[4], uuid[5], uuid[6], uuid[7],
-               uuid[8], uuid[9], uuid[10], uuid[11],
-               uuid[12], uuid[13], uuid[14], uuid[15],
+               uuid,
                sb->devflags,
                (unsigned long long)le64_to_cpu(sb->utime) & MD_SUPERBLOCK_1_TIME_SEC_MASK,
                (unsigned long long)le64_to_cpu(sb->events),
@@ -3439,8 +3432,7 @@ bitmap_store(mddev_t *mddev, const char *buf, size_t len)
                }
                if (*end && !isspace(*end)) break;
                bitmap_dirty_bits(mddev->bitmap, chunk, end_chunk);
-               buf = end;
-               while (isspace(*buf)) buf++;
+               buf = skip_spaces(end);
        }
        bitmap_unplug(mddev->bitmap); /* flush the bits to disk */
 out:
index 12a1b3d7132de212c49a992abce536ebea7250ed..c3916a42668e8b136e69c00244f505d75e042ee7 100644 (file)
@@ -2127,7 +2127,7 @@ vpfe_resume(struct device *dev)
        return -1;
 }
 
-static struct dev_pm_ops vpfe_dev_pm_ops = {
+static const struct dev_pm_ops vpfe_dev_pm_ops = {
        .suspend = vpfe_suspend,
        .resume = vpfe_resume,
 };
index d947ee5e4eb4b9b02d0f71ea7b2b7553a6b4fc11..78130721f578fc9bd310a92ac4b6c63771737b3d 100644 (file)
@@ -2107,7 +2107,7 @@ vpif_resume(struct device *dev)
        return -1;
 }
 
-static struct dev_pm_ops vpif_dev_pm_ops = {
+static const struct dev_pm_ops vpif_dev_pm_ops = {
        .suspend = vpif_suspend,
        .resume = vpif_resume,
 };
index a4f3472d4db87025d041d9422c5ed5ea8cd63883..961e4484d72188e724b9ffc5a4b8b30262a733ca 100644 (file)
@@ -1825,7 +1825,7 @@ static int sh_mobile_ceu_runtime_nop(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
+static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
        .runtime_suspend = sh_mobile_ceu_runtime_nop,
        .runtime_resume = sh_mobile_ceu_runtime_nop,
 };
index 2c16ca6501d5f58ab9ab66c9f718be22042a8126..59f4ba1b7034b8336969a37532066f399993e96f 100644 (file)
@@ -13,6 +13,20 @@ menuconfig MISC_DEVICES
 
 if MISC_DEVICES
 
+config AD525X_DPOT
+       tristate "Analog Devices AD525x Digital Potentiometers"
+       depends on I2C && SYSFS
+       help
+         If you say yes here, you get support for the Analog Devices
+         AD5258, AD5259, AD5251, AD5252, AD5253, AD5254 and AD5255
+         digital potentiometer chips.
+
+         See Documentation/misc-devices/ad525x_dpot.txt for the
+         userspace interface.
+
+         This driver can also be built as a module.  If so, the module
+         will be called ad525x_dpot.
+
 config ATMEL_PWM
        tristate "Atmel AT32/AT91 PWM support"
        depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
@@ -173,6 +187,30 @@ config SGI_XP
          this feature will allow for direct communication between SSIs
          based on a network adapter and DMA messaging.
 
+config CS5535_MFGPT
+       tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
+       depends on PCI
+       depends on X86
+       default n
+       help
+         This driver provides access to MFGPT functionality for other
+         drivers that need timers.  MFGPTs are available in the CS5535 and
+         CS5536 companion chips that are found in AMD Geode and several
+         other platforms.  They have a better resolution and max interval
+         than the generic PIT, and are suitable for use as high-res timers.
+         You probably don't want to enable this manually; other drivers that
+         make use of it should enable it.
+
+config CS5535_MFGPT_DEFAULT_IRQ
+       int
+       default 7
+       help
+         MFGPTs on the CS5535 require an interrupt.  The selected IRQ
+         can be overridden as a module option as well as by driver that
+         use the cs5535_mfgpt_ API; however, different architectures might
+         want to use a different IRQ by default.  This is here for
+         architectures to set as necessary.
+
 config HP_ILO
        tristate "Channel interface driver for HP iLO/iLO2 processor"
        depends on PCI
@@ -256,6 +294,16 @@ config DS1682
          This driver can also be built as a module.  If so, the module
          will be called ds1682.
 
+config TI_DAC7512
+       tristate "Texas Instruments DAC7512"
+       depends on SPI && SYSFS
+       help
+         If you say yes here you get support for the Texas Instruments
+         DAC7512 16-bit digital-to-analog converter.
+
+         This driver can also be built as a module. If so, the module
+         will be calles ti_dac7512.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
index 906a0edcea40e16f79d7af76c1635edd995fd684..049ff2482f309999e2be3e7517c89ea727ab4e74 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_IBM_ASM)          += ibmasm/
 obj-$(CONFIG_HDPU_FEATURES)    += hdpuftrs/
+obj-$(CONFIG_AD525X_DPOT)      += ad525x_dpot.o
 obj-$(CONFIG_ATMEL_PWM)                += atmel_pwm.o
 obj-$(CONFIG_ATMEL_SSC)                += atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)      += atmel_tclib.o
@@ -17,10 +18,12 @@ obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
 obj-$(CONFIG_KGDB_TESTS)       += kgdbts.o
 obj-$(CONFIG_SGI_XP)           += sgi-xp/
 obj-$(CONFIG_SGI_GRU)          += sgi-gru/
+obj-$(CONFIG_CS5535_MFGPT)     += cs5535-mfgpt.o
 obj-$(CONFIG_HP_ILO)           += hpilo.o
 obj-$(CONFIG_ISL29003)         += isl29003.o
 obj-$(CONFIG_EP93XX_PWM)       += ep93xx_pwm.o
 obj-$(CONFIG_DS1682)           += ds1682.o
+obj-$(CONFIG_TI_DAC7512)       += ti_dac7512.o
 obj-$(CONFIG_C2PORT)           += c2port/
 obj-$(CONFIG_IWMC3200TOP)      += iwmc3200top/
 obj-y                          += eeprom/
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
new file mode 100644 (file)
index 0000000..30a59f2
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * ad525x_dpot: Driver for the Analog Devices AD525x digital potentiometers
+ * Copyright (c) 2009 Analog Devices, Inc.
+ * Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
+ *
+ * DEVID               #Wipers         #Positions      Resistor Options (kOhm)
+ * AD5258              1               64              1, 10, 50, 100
+ * AD5259              1               256             5, 10, 50, 100
+ * AD5251              2               64              1, 10, 50, 100
+ * AD5252              2               256             1, 10, 50, 100
+ * AD5255              3               512             25, 250
+ * AD5253              4               64              1, 10, 50, 100
+ * AD5254              4               256             1, 10, 50, 100
+ *
+ * See Documentation/misc-devices/ad525x_dpot.txt for more info.
+ *
+ * derived from ad5258.c
+ * Copyright (c) 2009 Cyber Switching, Inc.
+ * Author: Chris Verges <chrisv@cyberswitching.com>
+ *
+ * derived from ad5252.c
+ * Copyright (c) 2006 Michael Hennerich <hennerich@blackfin.uclinux.org>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#define DRIVER_NAME                    "ad525x_dpot"
+#define DRIVER_VERSION                 "0.1"
+
+enum dpot_devid {
+       AD5258_ID,
+       AD5259_ID,
+       AD5251_ID,
+       AD5252_ID,
+       AD5253_ID,
+       AD5254_ID,
+       AD5255_ID,
+};
+
+#define AD5258_MAX_POSITION            64
+#define AD5259_MAX_POSITION            256
+#define AD5251_MAX_POSITION            64
+#define AD5252_MAX_POSITION            256
+#define AD5253_MAX_POSITION            64
+#define AD5254_MAX_POSITION            256
+#define AD5255_MAX_POSITION            512
+
+#define AD525X_RDAC0           0
+#define AD525X_RDAC1           1
+#define AD525X_RDAC2           2
+#define AD525X_RDAC3           3
+
+#define AD525X_REG_TOL         0x18
+#define AD525X_TOL_RDAC0       (AD525X_REG_TOL | AD525X_RDAC0)
+#define AD525X_TOL_RDAC1       (AD525X_REG_TOL | AD525X_RDAC1)
+#define AD525X_TOL_RDAC2       (AD525X_REG_TOL | AD525X_RDAC2)
+#define AD525X_TOL_RDAC3       (AD525X_REG_TOL | AD525X_RDAC3)
+
+/* RDAC-to-EEPROM Interface Commands */
+#define AD525X_I2C_RDAC                (0x00 << 5)
+#define AD525X_I2C_EEPROM      (0x01 << 5)
+#define AD525X_I2C_CMD         (0x80)
+
+#define AD525X_DEC_ALL_6DB     (AD525X_I2C_CMD | (0x4 << 3))
+#define AD525X_INC_ALL_6DB     (AD525X_I2C_CMD | (0x9 << 3))
+#define AD525X_DEC_ALL         (AD525X_I2C_CMD | (0x6 << 3))
+#define AD525X_INC_ALL         (AD525X_I2C_CMD | (0xB << 3))
+
+static s32 ad525x_read(struct i2c_client *client, u8 reg);
+static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value);
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct dpot_data {
+       struct mutex update_lock;
+       unsigned rdac_mask;
+       unsigned max_pos;
+       unsigned devid;
+};
+
+/* sysfs functions */
+
+static ssize_t sysfs_show_reg(struct device *dev,
+                             struct device_attribute *attr, char *buf, u32 reg)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct dpot_data *data = i2c_get_clientdata(client);
+       s32 value;
+
+       mutex_lock(&data->update_lock);
+       value = ad525x_read(client, reg);
+       mutex_unlock(&data->update_lock);
+
+       if (value < 0)
+               return -EINVAL;
+       /*
+        * Let someone else deal with converting this ...
+        * the tolerance is a two-byte value where the MSB
+        * is a sign + integer value, and the LSB is a
+        * decimal value.  See page 18 of the AD5258
+        * datasheet (Rev. A) for more details.
+        */
+
+       if (reg & AD525X_REG_TOL)
+               return sprintf(buf, "0x%04x\n", value & 0xFFFF);
+       else
+               return sprintf(buf, "%u\n", value & data->rdac_mask);
+}
+
+static ssize_t sysfs_set_reg(struct device *dev,
+                            struct device_attribute *attr,
+                            const char *buf, size_t count, u32 reg)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct dpot_data *data = i2c_get_clientdata(client);
+       unsigned long value;
+       int err;
+
+       err = strict_strtoul(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > data->rdac_mask)
+               value = data->rdac_mask;
+
+       mutex_lock(&data->update_lock);
+       ad525x_write(client, reg, value);
+       if (reg & AD525X_I2C_EEPROM)
+               msleep(26);     /* Sleep while the EEPROM updates */
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t sysfs_do_cmd(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count, u32 reg)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct dpot_data *data = i2c_get_clientdata(client);
+
+       mutex_lock(&data->update_lock);
+       ad525x_write(client, reg, 0);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac0(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC0);
+}
+
+static ssize_t set_rdac0(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_RDAC | AD525X_RDAC0);
+}
+
+static DEVICE_ATTR(rdac0, S_IWUSR | S_IRUGO, show_rdac0, set_rdac0);
+
+static ssize_t show_eeprom0(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC0);
+}
+
+static ssize_t set_eeprom0(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_EEPROM | AD525X_RDAC0);
+}
+
+static DEVICE_ATTR(eeprom0, S_IWUSR | S_IRUGO, show_eeprom0, set_eeprom0);
+
+static ssize_t show_tolerance0(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf,
+                             AD525X_I2C_EEPROM | AD525X_TOL_RDAC0);
+}
+
+static DEVICE_ATTR(tolerance0, S_IRUGO, show_tolerance0, NULL);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac1(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC1);
+}
+
+static ssize_t set_rdac1(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_RDAC | AD525X_RDAC1);
+}
+
+static DEVICE_ATTR(rdac1, S_IWUSR | S_IRUGO, show_rdac1, set_rdac1);
+
+static ssize_t show_eeprom1(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC1);
+}
+
+static ssize_t set_eeprom1(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_EEPROM | AD525X_RDAC1);
+}
+
+static DEVICE_ATTR(eeprom1, S_IWUSR | S_IRUGO, show_eeprom1, set_eeprom1);
+
+static ssize_t show_tolerance1(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf,
+                             AD525X_I2C_EEPROM | AD525X_TOL_RDAC1);
+}
+
+static DEVICE_ATTR(tolerance1, S_IRUGO, show_tolerance1, NULL);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac2(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC2);
+}
+
+static ssize_t set_rdac2(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_RDAC | AD525X_RDAC2);
+}
+
+static DEVICE_ATTR(rdac2, S_IWUSR | S_IRUGO, show_rdac2, set_rdac2);
+
+static ssize_t show_eeprom2(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC2);
+}
+
+static ssize_t set_eeprom2(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_EEPROM | AD525X_RDAC2);
+}
+
+static DEVICE_ATTR(eeprom2, S_IWUSR | S_IRUGO, show_eeprom2, set_eeprom2);
+
+static ssize_t show_tolerance2(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf,
+                             AD525X_I2C_EEPROM | AD525X_TOL_RDAC2);
+}
+
+static DEVICE_ATTR(tolerance2, S_IRUGO, show_tolerance2, NULL);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t show_rdac3(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC3);
+}
+
+static ssize_t set_rdac3(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_RDAC | AD525X_RDAC3);
+}
+
+static DEVICE_ATTR(rdac3, S_IWUSR | S_IRUGO, show_rdac3, set_rdac3);
+
+static ssize_t show_eeprom3(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC3);
+}
+
+static ssize_t set_eeprom3(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       return sysfs_set_reg(dev, attr, buf, count,
+                            AD525X_I2C_EEPROM | AD525X_RDAC3);
+}
+
+static DEVICE_ATTR(eeprom3, S_IWUSR | S_IRUGO, show_eeprom3, set_eeprom3);
+
+static ssize_t show_tolerance3(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       return sysfs_show_reg(dev, attr, buf,
+                             AD525X_I2C_EEPROM | AD525X_TOL_RDAC3);
+}
+
+static DEVICE_ATTR(tolerance3, S_IRUGO, show_tolerance3, NULL);
+
+static struct attribute *ad525x_attributes_wipers[4][4] = {
+       {
+               &dev_attr_rdac0.attr,
+               &dev_attr_eeprom0.attr,
+               &dev_attr_tolerance0.attr,
+               NULL
+       }, {
+               &dev_attr_rdac1.attr,
+               &dev_attr_eeprom1.attr,
+               &dev_attr_tolerance1.attr,
+               NULL
+       }, {
+               &dev_attr_rdac2.attr,
+               &dev_attr_eeprom2.attr,
+               &dev_attr_tolerance2.attr,
+               NULL
+       }, {
+               &dev_attr_rdac3.attr,
+               &dev_attr_eeprom3.attr,
+               &dev_attr_tolerance3.attr,
+               NULL
+       }
+};
+
+static const struct attribute_group ad525x_group_wipers[] = {
+       {.attrs = ad525x_attributes_wipers[AD525X_RDAC0]},
+       {.attrs = ad525x_attributes_wipers[AD525X_RDAC1]},
+       {.attrs = ad525x_attributes_wipers[AD525X_RDAC2]},
+       {.attrs = ad525x_attributes_wipers[AD525X_RDAC3]},
+};
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t set_inc_all(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL);
+}
+
+static DEVICE_ATTR(inc_all, S_IWUSR, NULL, set_inc_all);
+
+static ssize_t set_dec_all(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL);
+}
+
+static DEVICE_ATTR(dec_all, S_IWUSR, NULL, set_dec_all);
+
+static ssize_t set_inc_all_6db(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL_6DB);
+}
+
+static DEVICE_ATTR(inc_all_6db, S_IWUSR, NULL, set_inc_all_6db);
+
+static ssize_t set_dec_all_6db(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL_6DB);
+}
+
+static DEVICE_ATTR(dec_all_6db, S_IWUSR, NULL, set_dec_all_6db);
+
+static struct attribute *ad525x_attributes_commands[] = {
+       &dev_attr_inc_all.attr,
+       &dev_attr_dec_all.attr,
+       &dev_attr_inc_all_6db.attr,
+       &dev_attr_dec_all_6db.attr,
+       NULL
+};
+
+static const struct attribute_group ad525x_group_commands = {
+       .attrs = ad525x_attributes_commands,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* i2c device functions */
+
+/**
+ * ad525x_read - return the value contained in the specified register
+ * on the AD5258 device.
+ * @client: value returned from i2c_new_device()
+ * @reg: the register to read
+ *
+ * If the tolerance register is specified, 2 bytes are returned.
+ * Otherwise, 1 byte is returned.  A negative value indicates an error
+ * occurred while reading the register.
+ */
+static s32 ad525x_read(struct i2c_client *client, u8 reg)
+{
+       struct dpot_data *data = i2c_get_clientdata(client);
+
+       if ((reg & AD525X_REG_TOL) || (data->max_pos > 256))
+               return i2c_smbus_read_word_data(client, (reg & 0xF8) |
+                                               ((reg & 0x7) << 1));
+       else
+               return i2c_smbus_read_byte_data(client, reg);
+}
+
+/**
+ * ad525x_write - store the given value in the specified register on
+ * the AD5258 device.
+ * @client: value returned from i2c_new_device()
+ * @reg: the register to write
+ * @value: the byte to store in the register
+ *
+ * For certain instructions that do not require a data byte, "NULL"
+ * should be specified for the "value" parameter.  These instructions
+ * include NOP, RESTORE_FROM_EEPROM, and STORE_TO_EEPROM.
+ *
+ * A negative return value indicates an error occurred while reading
+ * the register.
+ */
+static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value)
+{
+       struct dpot_data *data = i2c_get_clientdata(client);
+
+       /* Only write the instruction byte for certain commands */
+       if (reg & AD525X_I2C_CMD)
+               return i2c_smbus_write_byte(client, reg);
+
+       if (data->max_pos > 256)
+               return i2c_smbus_write_word_data(client, (reg & 0xF8) |
+                                               ((reg & 0x7) << 1), value);
+       else
+               /* All other registers require instruction + data bytes */
+               return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int ad525x_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct dpot_data *data;
+       int err = 0;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
+               dev_err(dev, "missing I2C functionality for this driver\n");
+               goto exit;
+       }
+
+       data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->update_lock);
+
+       switch (id->driver_data) {
+       case AD5258_ID:
+               data->max_pos = AD5258_MAX_POSITION;
+               err = sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC0]);
+               break;
+       case AD5259_ID:
+               data->max_pos = AD5259_MAX_POSITION;
+               err = sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC0]);
+               break;
+       case AD5251_ID:
+               data->max_pos = AD5251_MAX_POSITION;
+               err = sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC1]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC3]);
+               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       case AD5252_ID:
+               data->max_pos = AD5252_MAX_POSITION;
+               err = sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC1]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC3]);
+               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       case AD5253_ID:
+               data->max_pos = AD5253_MAX_POSITION;
+               err = sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC0]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC1]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC2]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC3]);
+               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       case AD5254_ID:
+               data->max_pos = AD5254_MAX_POSITION;
+               err = sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC0]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC1]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC2]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC3]);
+               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       case AD5255_ID:
+               data->max_pos = AD5255_MAX_POSITION;
+               err = sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC0]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC1]);
+               err |= sysfs_create_group(&dev->kobj,
+                                      &ad525x_group_wipers[AD525X_RDAC2]);
+               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       default:
+               err = -ENODEV;
+               goto exit_free;
+       }
+
+       if (err) {
+               dev_err(dev, "failed to register sysfs hooks\n");
+               goto exit_free;
+       }
+
+       data->devid = id->driver_data;
+       data->rdac_mask = data->max_pos - 1;
+
+       dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
+                id->name, data->max_pos);
+
+       return 0;
+
+exit_free:
+       kfree(data);
+       i2c_set_clientdata(client, NULL);
+exit:
+       dev_err(dev, "failed to create client\n");
+       return err;
+}
+
+static int __devexit ad525x_remove(struct i2c_client *client)
+{
+       struct dpot_data *data = i2c_get_clientdata(client);
+       struct device *dev = &client->dev;
+
+       switch (data->devid) {
+       case AD5258_ID:
+       case AD5259_ID:
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC0]);
+               break;
+       case AD5251_ID:
+       case AD5252_ID:
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC1]);
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC3]);
+               sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       case AD5253_ID:
+       case AD5254_ID:
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC0]);
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC1]);
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC2]);
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC3]);
+               sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       case AD5255_ID:
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC0]);
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC1]);
+               sysfs_remove_group(&dev->kobj,
+                                  &ad525x_group_wipers[AD525X_RDAC2]);
+               sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
+               break;
+       }
+
+       i2c_set_clientdata(client, NULL);
+       kfree(data);
+
+       return 0;
+}
+
+static const struct i2c_device_id ad525x_idtable[] = {
+       {"ad5258", AD5258_ID},
+       {"ad5259", AD5259_ID},
+       {"ad5251", AD5251_ID},
+       {"ad5252", AD5252_ID},
+       {"ad5253", AD5253_ID},
+       {"ad5254", AD5254_ID},
+       {"ad5255", AD5255_ID},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad525x_idtable);
+
+static struct i2c_driver ad525x_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = DRIVER_NAME,
+                  },
+       .id_table = ad525x_idtable,
+       .probe = ad525x_probe,
+       .remove = __devexit_p(ad525x_remove),
+};
+
+static int __init ad525x_init(void)
+{
+       return i2c_add_driver(&ad525x_driver);
+}
+
+module_init(ad525x_init);
+
+static void __exit ad525x_exit(void)
+{
+       i2c_del_driver(&ad525x_driver);
+}
+
+module_exit(ad525x_exit);
+
+MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, "
+             "Michael Hennerich <hennerich@blackfin.uclinux.org>, ");
+MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c
new file mode 100644 (file)
index 0000000..8110460
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Driver for the CS5535/CS5536 Multi-Function General Purpose Timers (MFGPT)
+ *
+ * Copyright (C) 2006, Advanced Micro Devices, Inc.
+ * Copyright (C) 2007  Andres Salomon <dilinger@debian.org>
+ * Copyright (C) 2009  Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/cs5535.h>
+
+#define DRV_NAME "cs5535-mfgpt"
+#define MFGPT_BAR 2
+
+static int mfgpt_reset_timers;
+module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644);
+MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; "
+               "required by some broken BIOSes (ie, TinyBIOS < 0.99).");
+
+struct cs5535_mfgpt_timer {
+       struct cs5535_mfgpt_chip *chip;
+       int nr;
+};
+
+static struct cs5535_mfgpt_chip {
+       DECLARE_BITMAP(avail, MFGPT_MAX_TIMERS);
+       resource_size_t base;
+
+       struct pci_dev *pdev;
+       spinlock_t lock;
+       int initialized;
+} cs5535_mfgpt_chip;
+
+int cs5535_mfgpt_toggle_event(struct cs5535_mfgpt_timer *timer, int cmp,
+               int event, int enable)
+{
+       uint32_t msr, mask, value, dummy;
+       int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
+
+       if (!timer) {
+               WARN_ON(1);
+               return -EIO;
+       }
+
+       /*
+        * The register maps for these are described in sections 6.17.1.x of
+        * the AMD Geode CS5536 Companion Device Data Book.
+        */
+       switch (event) {
+       case MFGPT_EVENT_RESET:
+               /*
+                * XXX: According to the docs, we cannot reset timers above
+                * 6; that is, resets for 7 and 8 will be ignored.  Is this
+                * a problem?   -dilinger
+                */
+               msr = MSR_MFGPT_NR;
+               mask = 1 << (timer->nr + 24);
+               break;
+
+       case MFGPT_EVENT_NMI:
+               msr = MSR_MFGPT_NR;
+               mask = 1 << (timer->nr + shift);
+               break;
+
+       case MFGPT_EVENT_IRQ:
+               msr = MSR_MFGPT_IRQ;
+               mask = 1 << (timer->nr + shift);
+               break;
+
+       default:
+               return -EIO;
+       }
+
+       rdmsr(msr, value, dummy);
+
+       if (enable)
+               value |= mask;
+       else
+               value &= ~mask;
+
+       wrmsr(msr, value, dummy);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_toggle_event);
+
+int cs5535_mfgpt_set_irq(struct cs5535_mfgpt_timer *timer, int cmp, int *irq,
+               int enable)
+{
+       uint32_t zsel, lpc, dummy;
+       int shift;
+
+       if (!timer) {
+               WARN_ON(1);
+               return -EIO;
+       }
+
+       /*
+        * Unfortunately, MFGPTs come in pairs sharing their IRQ lines. If VSA
+        * is using the same CMP of the timer's Siamese twin, the IRQ is set to
+        * 2, and we mustn't use nor change it.
+        * XXX: Likewise, 2 Linux drivers might clash if the 2nd overwrites the
+        * IRQ of the 1st. This can only happen if forcing an IRQ, calling this
+        * with *irq==0 is safe. Currently there _are_ no 2 drivers.
+        */
+       rdmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
+       shift = ((cmp == MFGPT_CMP1 ? 0 : 4) + timer->nr % 4) * 4;
+       if (((zsel >> shift) & 0xF) == 2)
+               return -EIO;
+
+       /* Choose IRQ: if none supplied, keep IRQ already set or use default */
+       if (!*irq)
+               *irq = (zsel >> shift) & 0xF;
+       if (!*irq)
+               *irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ;
+
+       /* Can't use IRQ if it's 0 (=disabled), 2, or routed to LPC */
+       if (*irq < 1 || *irq == 2 || *irq > 15)
+               return -EIO;
+       rdmsr(MSR_PIC_IRQM_LPC, lpc, dummy);
+       if (lpc & (1 << *irq))
+               return -EIO;
+
+       /* All chosen and checked - go for it */
+       if (cs5535_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
+               return -EIO;
+       if (enable) {
+               zsel = (zsel & ~(0xF << shift)) | (*irq << shift);
+               wrmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_set_irq);
+
+struct cs5535_mfgpt_timer *cs5535_mfgpt_alloc_timer(int timer_nr, int domain)
+{
+       struct cs5535_mfgpt_chip *mfgpt = &cs5535_mfgpt_chip;
+       struct cs5535_mfgpt_timer *timer = NULL;
+       unsigned long flags;
+       int max;
+
+       if (!mfgpt->initialized)
+               goto done;
+
+       /* only allocate timers from the working domain if requested */
+       if (domain == MFGPT_DOMAIN_WORKING)
+               max = 6;
+       else
+               max = MFGPT_MAX_TIMERS;
+
+       if (timer_nr >= max) {
+               /* programmer error.  silly programmers! */
+               WARN_ON(1);
+               goto done;
+       }
+
+       spin_lock_irqsave(&mfgpt->lock, flags);
+       if (timer_nr < 0) {
+               unsigned long t;
+
+               /* try to find any available timer */
+               t = find_first_bit(mfgpt->avail, max);
+               /* set timer_nr to -1 if no timers available */
+               timer_nr = t < max ? (int) t : -1;
+       } else {
+               /* check if the requested timer's available */
+               if (test_bit(timer_nr, mfgpt->avail))
+                       timer_nr = -1;
+       }
+
+       if (timer_nr >= 0)
+               /* if timer_nr is not -1, it's an available timer */
+               __clear_bit(timer_nr, mfgpt->avail);
+       spin_unlock_irqrestore(&mfgpt->lock, flags);
+
+       if (timer_nr < 0)
+               goto done;
+
+       timer = kmalloc(sizeof(*timer), GFP_KERNEL);
+       if (!timer) {
+               /* aw hell */
+               spin_lock_irqsave(&mfgpt->lock, flags);
+               __set_bit(timer_nr, mfgpt->avail);
+               spin_unlock_irqrestore(&mfgpt->lock, flags);
+               goto done;
+       }
+       timer->chip = mfgpt;
+       timer->nr = timer_nr;
+       dev_info(&mfgpt->pdev->dev, "registered timer %d\n", timer_nr);
+
+done:
+       return timer;
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer);
+
+/*
+ * XXX: This frees the timer memory, but never resets the actual hardware
+ * timer.  The old geode_mfgpt code did this; it would be good to figure
+ * out a way to actually release the hardware timer.  See comments below.
+ */
+void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer)
+{
+       kfree(timer);
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer);
+
+uint16_t cs5535_mfgpt_read(struct cs5535_mfgpt_timer *timer, uint16_t reg)
+{
+       return inw(timer->chip->base + reg + (timer->nr * 8));
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_read);
+
+void cs5535_mfgpt_write(struct cs5535_mfgpt_timer *timer, uint16_t reg,
+               uint16_t value)
+{
+       outw(value, timer->chip->base + reg + (timer->nr * 8));
+}
+EXPORT_SYMBOL_GPL(cs5535_mfgpt_write);
+
+/*
+ * This is a sledgehammer that resets all MFGPT timers. This is required by
+ * some broken BIOSes which leave the system in an unstable state
+ * (TinyBIOS 0.98, for example; fixed in 0.99).  It's uncertain as to
+ * whether or not this secret MSR can be used to release individual timers.
+ * Jordan tells me that he and Mitch once played w/ it, but it's unclear
+ * what the results of that were (and they experienced some instability).
+ */
+static void __init reset_all_timers(void)
+{
+       uint32_t val, dummy;
+
+       /* The following undocumented bit resets the MFGPT timers */
+       val = 0xFF; dummy = 0;
+       wrmsr(MSR_MFGPT_SETUP, val, dummy);
+}
+
+/*
+ * Check whether any MFGPTs are available for the kernel to use.  In most
+ * cases, firmware that uses AMD's VSA code will claim all timers during
+ * bootup; we certainly don't want to take them if they're already in use.
+ * In other cases (such as with VSAless OpenFirmware), the system firmware
+ * leaves timers available for us to use.
+ */
+static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt)
+{
+       struct cs5535_mfgpt_timer timer = { .chip = mfgpt };
+       unsigned long flags;
+       int timers = 0;
+       uint16_t val;
+       int i;
+
+       /* bios workaround */
+       if (mfgpt_reset_timers)
+               reset_all_timers();
+
+       /* just to be safe, protect this section w/ lock */
+       spin_lock_irqsave(&mfgpt->lock, flags);
+       for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
+               timer.nr = i;
+               val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP);
+               if (!(val & MFGPT_SETUP_SETUP)) {
+                       __set_bit(i, mfgpt->avail);
+                       timers++;
+               }
+       }
+       spin_unlock_irqrestore(&mfgpt->lock, flags);
+
+       return timers;
+}
+
+static int __init cs5535_mfgpt_probe(struct pci_dev *pdev,
+               const struct pci_device_id *pci_id)
+{
+       int err, t;
+
+       /* There are two ways to get the MFGPT base address; one is by
+        * fetching it from MSR_LBAR_MFGPT, the other is by reading the
+        * PCI BAR info.  The latter method is easier (especially across
+        * different architectures), so we'll stick with that for now.  If
+        * it turns out to be unreliable in the face of crappy BIOSes, we
+        * can always go back to using MSRs.. */
+
+       err = pci_enable_device_io(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "can't enable device IO\n");
+               goto done;
+       }
+
+       err = pci_request_region(pdev, MFGPT_BAR, DRV_NAME);
+       if (err) {
+               dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", MFGPT_BAR);
+               goto done;
+       }
+
+       /* set up the driver-specific struct */
+       cs5535_mfgpt_chip.base = pci_resource_start(pdev, MFGPT_BAR);
+       cs5535_mfgpt_chip.pdev = pdev;
+       spin_lock_init(&cs5535_mfgpt_chip.lock);
+
+       dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", MFGPT_BAR,
+                       (unsigned long long) cs5535_mfgpt_chip.base);
+
+       /* detect the available timers */
+       t = scan_timers(&cs5535_mfgpt_chip);
+       dev_info(&pdev->dev, DRV_NAME ": %d MFGPT timers available\n", t);
+       cs5535_mfgpt_chip.initialized = 1;
+       return 0;
+
+done:
+       return err;
+}
+
+static struct pci_device_id cs5535_mfgpt_pci_tbl[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+       { 0, },
+};
+MODULE_DEVICE_TABLE(pci, cs5535_mfgpt_pci_tbl);
+
+/*
+ * Just like with the cs5535-gpio driver, we can't use the standard PCI driver
+ * registration stuff.  It only allows only one driver to bind to each PCI
+ * device, and we want the GPIO and MFGPT drivers to be able to share a PCI
+ * device.  Instead, we manually scan for the PCI device, request a single
+ * region, and keep track of the devices that we're using.
+ */
+
+static int __init cs5535_mfgpt_scan_pci(void)
+{
+       struct pci_dev *pdev;
+       int err = -ENODEV;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs5535_mfgpt_pci_tbl); i++) {
+               pdev = pci_get_device(cs5535_mfgpt_pci_tbl[i].vendor,
+                               cs5535_mfgpt_pci_tbl[i].device, NULL);
+               if (pdev) {
+                       err = cs5535_mfgpt_probe(pdev,
+                                       &cs5535_mfgpt_pci_tbl[i]);
+                       if (err)
+                               pci_dev_put(pdev);
+
+                       /* we only support a single CS5535/6 southbridge */
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int __init cs5535_mfgpt_init(void)
+{
+       return cs5535_mfgpt_scan_pci();
+}
+
+module_init(cs5535_mfgpt_init);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
+MODULE_DESCRIPTION("CS5535/CS5536 MFGPT timer driver");
+MODULE_LICENSE("GPL");
index 2c27193aeaa04721ee3159a279e4925abf28edd5..f939ebc2507c48ef64af3e5cfe632b387d744d14 100644 (file)
@@ -32,9 +32,6 @@
 static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
                                        0x55, 0x56, 0x57, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(eeprom);
-
 
 /* Size of EEPROM in bytes */
 #define EEPROM_SIZE            256
@@ -135,8 +132,7 @@ static struct bin_attribute eeprom_attr = {
 };
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int eeprom_detect(struct i2c_client *client, int kind,
-                        struct i2c_board_info *info)
+static int eeprom_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
 
@@ -233,7 +229,7 @@ static struct i2c_driver eeprom_driver = {
 
        .class          = I2C_CLASS_DDC | I2C_CLASS_SPD,
        .detect         = eeprom_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static int __init eeprom_init(void)
index 4bb7a3af9ad97bb1e5e8a2e02a03ebf54165e135..395a4ea64e9cd81623abc06be926d5f3e4eadd6b 100644 (file)
@@ -30,9 +30,6 @@
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END };
 
-/* Insmod parameters */
-I2C_CLIENT_INSMOD_1(ics932s401);
-
 /* ICS932S401 registers */
 #define ICS932S401_REG_CFG2                    0x01
 #define        ICS932S401_CFG1_SPREAD          0x01
@@ -106,12 +103,12 @@ struct ics932s401_data {
 
 static int ics932s401_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int ics932s401_detect(struct i2c_client *client, int kind,
+static int ics932s401_detect(struct i2c_client *client,
                          struct i2c_board_info *info);
 static int ics932s401_remove(struct i2c_client *client);
 
 static const struct i2c_device_id ics932s401_id[] = {
-       { "ics932s401", ics932s401 },
+       { "ics932s401", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ics932s401_id);
@@ -125,7 +122,7 @@ static struct i2c_driver ics932s401_driver = {
        .remove         = ics932s401_remove,
        .id_table       = ics932s401_id,
        .detect         = ics932s401_detect,
-       .address_data   = &addr_data,
+       .address_list   = normal_i2c,
 };
 
 static struct ics932s401_data *ics932s401_update_device(struct device *dev)
@@ -413,7 +410,7 @@ static ssize_t show_spread(struct device *dev,
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int ics932s401_detect(struct i2c_client *client, int kind,
+static int ics932s401_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
index 60b0b1a4fb3a8ff7817c14d59a490f2665f4e933..09dcb699e6674e4ba5d3301149be994fcf17c46d 100644 (file)
@@ -138,7 +138,7 @@ ioc4_unregister_submodule(struct ioc4_submodule *is)
  * even though the following code utilizes external interrupt registers
  * to perform the speed calculation.
  */
-static void
+static void __devinit
 ioc4_clock_calibrate(struct ioc4_driver_data *idd)
 {
        union ioc4_int_out int_out;
@@ -230,7 +230,7 @@ ioc4_clock_calibrate(struct ioc4_driver_data *idd)
  * on the same PCI bus at slot number 3 to differentiate IO9 from IO10.
  * If neither is present, it's a PCI-RT.
  */
-static unsigned int
+static unsigned int __devinit
 ioc4_variant(struct ioc4_driver_data *idd)
 {
        struct pci_dev *pdev = NULL;
@@ -269,7 +269,7 @@ ioc4_variant(struct ioc4_driver_data *idd)
        return IOC4_VARIANT_PCI_RT;
 }
 
-static void
+static void __devinit
 ioc4_load_modules(struct work_struct *work)
 {
        /* arg just has to be freed */
@@ -280,7 +280,7 @@ ioc4_load_modules(struct work_struct *work)
 }
 
 /* Adds a new instance of an IOC4 card */
-static int
+static int __devinit
 ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
 {
        struct ioc4_driver_data *idd;
@@ -425,7 +425,7 @@ out:
 }
 
 /* Removes a particular instance of an IOC4 card. */
-static void
+static void __devexit
 ioc4_remove(struct pci_dev *pdev)
 {
        struct ioc4_submodule *is;
@@ -476,7 +476,7 @@ static struct pci_driver ioc4_driver = {
        .name = "IOC4",
        .id_table = ioc4_id_table,
        .probe = ioc4_probe,
-       .remove = ioc4_remove,
+       .remove = __devexit_p(ioc4_remove),
 };
 
 MODULE_DEVICE_TABLE(pci, ioc4_id_table);
@@ -486,14 +486,14 @@ MODULE_DEVICE_TABLE(pci, ioc4_id_table);
  *********************/
 
 /* Module load */
-static int __devinit
+static int __init
 ioc4_init(void)
 {
        return pci_register_driver(&ioc4_driver);
 }
 
 /* Module unload */
-static void __devexit
+static void __exit
 ioc4_exit(void)
 {
        /* Ensure ioc4_load_modules() has completed before exiting */
diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c
new file mode 100644 (file)
index 0000000..d3f229a
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  dac7512.c - Linux kernel module for
+ *     Texas Instruments DAC7512
+ *
+ *  Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+
+#define DAC7512_DRV_NAME       "dac7512"
+#define DRIVER_VERSION         "1.0"
+
+static ssize_t dac7512_store_val(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       unsigned char tmp[2];
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       tmp[0] = val >> 8;
+       tmp[1] = val & 0xff;
+       spi_write(spi, tmp, sizeof(tmp));
+       return count;
+}
+
+static DEVICE_ATTR(value, S_IWUSR, NULL, dac7512_store_val);
+
+static struct attribute *dac7512_attributes[] = {
+       &dev_attr_value.attr,
+       NULL
+};
+
+static const struct attribute_group dac7512_attr_group = {
+       .attrs = dac7512_attributes,
+};
+
+static int __devinit dac7512_probe(struct spi_device *spi)
+{
+       int ret;
+
+       spi->bits_per_word = 8;
+       spi->mode = SPI_MODE_0;
+       ret = spi_setup(spi);
+       if (ret < 0)
+               return ret;
+
+       return sysfs_create_group(&spi->dev.kobj, &dac7512_attr_group);
+}
+
+static int __devexit dac7512_remove(struct spi_device *spi)
+{
+       sysfs_remove_group(&spi->dev.kobj, &dac7512_attr_group);
+       return 0;
+}
+
+static struct spi_driver dac7512_driver = {
+       .driver = {
+               .name   = DAC7512_DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe  = dac7512_probe,
+       .remove = __devexit_p(dac7512_remove),
+};
+
+static int __init dac7512_init(void)
+{
+       return spi_register_driver(&dac7512_driver);
+}
+
+static void __exit dac7512_exit(void)
+{
+       spi_unregister_driver(&dac7512_driver);
+}
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("DAC7512 16-bit DAC");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(dac7512_init);
+module_exit(dac7512_exit);
index ab37a6d9d32a046d7318b3a89a6358c8456b5ef9..bb22ffd76ef8a2bfba0bbdc29d73faabc51911bc 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 config MMC_UNSAFE_RESUME
-       bool "Allow unsafe resume (DANGEROUS)"
+       bool "Assume MMC/SD cards are non-removable (DANGEROUS)"
        help
          If you say Y here, the MMC layer will assume that all cards
          stayed in their respective slots during the suspend. The
@@ -14,3 +14,5 @@ config MMC_UNSAFE_RESUME
          This option is usually just for embedded systems which use
          a MMC/SD card for rootfs. Most people should say N here.
 
+         This option sets a default which can be overridden by the
+         module parameter "removable=0" or "removable=1".
index 7dab2e5f4bc9c87c53779a3d49cf2cb368114ef7..30acd526582162033ec3d35d4f770bfa876a41fd 100644 (file)
@@ -47,6 +47,22 @@ static struct workqueue_struct *workqueue;
 int use_spi_crc = 1;
 module_param(use_spi_crc, bool, 0);
 
+/*
+ * We normally treat cards as removed during suspend if they are not
+ * known to be on a non-removable bus, to avoid the risk of writing
+ * back data to a different card after resume.  Allow this to be
+ * overridden if necessary.
+ */
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+int mmc_assume_removable;
+#else
+int mmc_assume_removable = 1;
+#endif
+module_param_named(removable, mmc_assume_removable, bool, 0644);
+MODULE_PARM_DESC(
+       removable,
+       "MMC/SD cards are removable and may be removed during suspend");
+
 /*
  * Internal function. Schedule delayed work in the MMC work queue.
  */
index 67ae6abc4230928242109df7f5ab2c773f2971a3..a811c52a165957219bc55e03f7fbe1faab43379f 100644 (file)
@@ -54,7 +54,9 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
 int mmc_attach_sd(struct mmc_host *host, u32 ocr);
 int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
 
+/* Module parameters */
 extern int use_spi_crc;
+extern int mmc_assume_removable;
 
 /* Debugfs information for hosts and cards */
 void mmc_add_host_debugfs(struct mmc_host *host);
index bfefce365ae788ea93d73382298c915fba72288e..c11189446a1f70cd93bcf778e89a41ca06eeac52 100644 (file)
@@ -602,25 +602,6 @@ static int mmc_awake(struct mmc_host *host)
        return err;
 }
 
-#ifdef CONFIG_MMC_UNSAFE_RESUME
-
-static const struct mmc_bus_ops mmc_ops = {
-       .awake = mmc_awake,
-       .sleep = mmc_sleep,
-       .remove = mmc_remove,
-       .detect = mmc_detect,
-       .suspend = mmc_suspend,
-       .resume = mmc_resume,
-       .power_restore = mmc_power_restore,
-};
-
-static void mmc_attach_bus_ops(struct mmc_host *host)
-{
-       mmc_attach_bus(host, &mmc_ops);
-}
-
-#else
-
 static const struct mmc_bus_ops mmc_ops = {
        .awake = mmc_awake,
        .sleep = mmc_sleep,
@@ -645,15 +626,13 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
 {
        const struct mmc_bus_ops *bus_ops;
 
-       if (host->caps & MMC_CAP_NONREMOVABLE)
+       if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
                bus_ops = &mmc_ops_unsafe;
        else
                bus_ops = &mmc_ops;
        mmc_attach_bus(host, bus_ops);
 }
 
-#endif
-
 /*
  * Starting point for MMC card init.
  */
index 10b2a4d20f5a1a6b270592d6b048514126bda96a..fdd414eded09a9eef14968a59d026e369c046aa5 100644 (file)
@@ -606,23 +606,6 @@ static void mmc_sd_power_restore(struct mmc_host *host)
        mmc_release_host(host);
 }
 
-#ifdef CONFIG_MMC_UNSAFE_RESUME
-
-static const struct mmc_bus_ops mmc_sd_ops = {
-       .remove = mmc_sd_remove,
-       .detect = mmc_sd_detect,
-       .suspend = mmc_sd_suspend,
-       .resume = mmc_sd_resume,
-       .power_restore = mmc_sd_power_restore,
-};
-
-static void mmc_sd_attach_bus_ops(struct mmc_host *host)
-{
-       mmc_attach_bus(host, &mmc_sd_ops);
-}
-
-#else
-
 static const struct mmc_bus_ops mmc_sd_ops = {
        .remove = mmc_sd_remove,
        .detect = mmc_sd_detect,
@@ -643,15 +626,13 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
 {
        const struct mmc_bus_ops *bus_ops;
 
-       if (host->caps & MMC_CAP_NONREMOVABLE)
+       if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable)
                bus_ops = &mmc_sd_ops_unsafe;
        else
                bus_ops = &mmc_sd_ops;
        mmc_attach_bus(host, bus_ops);
 }
 
-#endif
-
 /*
  * Starting point for SD card init.
  */
index f85dcd5365082ab27cc77adefcb84f1364892f80..9538389783c10e17d4494e875499c302b93ed01b 100644 (file)
@@ -97,26 +97,56 @@ static const unsigned char speed_val[16] =
 static const unsigned int speed_unit[8] =
        { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
 
-/* FUNCE tuples with these types get passed to SDIO drivers */
-static const unsigned char funce_type_whitelist[] = {
-       4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */
+
+typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
+                          const unsigned char *, unsigned);
+
+struct cis_tpl {
+       unsigned char code;
+       unsigned char min_size;
+       tpl_parse_t *parse;
 };
 
-static int cistpl_funce_whitelisted(unsigned char type)
+static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
+                        const char *tpl_descr,
+                        const struct cis_tpl *tpl, int tpl_count,
+                        unsigned char code,
+                        const unsigned char *buf, unsigned size)
 {
-       int i;
+       int i, ret;
 
-       for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) {
-               if (funce_type_whitelist[i] == type)
-                       return 1;
+       /* look for a matching code in the table */
+       for (i = 0; i < tpl_count; i++, tpl++) {
+               if (tpl->code == code)
+                       break;
        }
-       return 0;
+       if (i < tpl_count) {
+               if (size >= tpl->min_size) {
+                       if (tpl->parse)
+                               ret = tpl->parse(card, func, buf, size);
+                       else
+                               ret = -EILSEQ;  /* known tuple, not parsed */
+               } else {
+                       /* invalid tuple */
+                       ret = -EINVAL;
+               }
+               if (ret && ret != -EILSEQ && ret != -ENOENT) {
+                       printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n",
+                              mmc_hostname(card->host), tpl_descr, code, size);
+               }
+       } else {
+               /* unknown tuple */
+               ret = -ENOENT;
+       }
+
+       return ret;
 }
 
-static int cistpl_funce_common(struct mmc_card *card,
+static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func,
                               const unsigned char *buf, unsigned size)
 {
-       if (size < 0x04 || buf[0] != 0)
+       /* Only valid for the common CIS (function 0) */
+       if (func)
                return -EINVAL;
 
        /* TPLFE_FN0_BLK_SIZE */
@@ -129,20 +159,24 @@ static int cistpl_funce_common(struct mmc_card *card,
        return 0;
 }
 
-static int cistpl_funce_func(struct sdio_func *func,
+static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func,
                             const unsigned char *buf, unsigned size)
 {
        unsigned vsn;
        unsigned min_size;
 
-       /* let SDIO drivers take care of whitelisted FUNCE tuples */
-       if (cistpl_funce_whitelisted(buf[0]))
-               return -EILSEQ;
+       /* Only valid for the individual function's CIS (1-7) */
+       if (!func)
+               return -EINVAL;
 
+       /*
+        * This tuple has a different length depending on the SDIO spec
+        * version.
+        */
        vsn = func->card->cccr.sdio_vsn;
        min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
 
-       if (size < min_size || buf[0] != 1)
+       if (size < min_size)
                return -EINVAL;
 
        /* TPLFE_MAX_BLK_SIZE */
@@ -157,39 +191,32 @@ static int cistpl_funce_func(struct sdio_func *func,
        return 0;
 }
 
+/*
+ * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples.
+ *
+ * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending
+ * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO
+ * TPLFID_FUNCTION is always hardcoded to 0x0C.
+ */
+static const struct cis_tpl cis_tpl_funce_list[] = {
+       {       0x00,   4,      cistpl_funce_common             },
+       {       0x01,   0,      cistpl_funce_func               },
+       {       0x04,   1+1+6,  /* CISTPL_FUNCE_LAN_NODE_ID */  },
+};
+
 static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
                        const unsigned char *buf, unsigned size)
 {
-       int ret;
-
-       /*
-        * There should be two versions of the CISTPL_FUNCE tuple,
-        * one for the common CIS (function 0) and a version used by
-        * the individual function's CIS (1-7). Yet, the later has a
-        * different length depending on the SDIO spec version.
-        */
-       if (func)
-               ret = cistpl_funce_func(func, buf, size);
-       else
-               ret = cistpl_funce_common(card, buf, size);
-
-       if (ret && ret != -EILSEQ) {
-               printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
-                      "type %u\n", mmc_hostname(card->host), size, buf[0]);
-       }
+       if (size < 1)
+               return -EINVAL;
 
-       return ret;
+       return cis_tpl_parse(card, func, "CISTPL_FUNCE",
+                            cis_tpl_funce_list,
+                            ARRAY_SIZE(cis_tpl_funce_list),
+                            buf[0], buf, size);
 }
 
-typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
-                          const unsigned char *, unsigned);
-
-struct cis_tpl {
-       unsigned char code;
-       unsigned char min_size;
-       tpl_parse_t *parse;
-};
-
+/* Known TPL_CODEs table for CIS tuples */
 static const struct cis_tpl cis_tpl_list[] = {
        {       0x15,   3,      cistpl_vers_1           },
        {       0x20,   4,      cistpl_manfid           },
@@ -268,46 +295,38 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
                        break;
                }
 
-               for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
-                       if (cis_tpl_list[i].code == tpl_code)
-                               break;
-               if (i < ARRAY_SIZE(cis_tpl_list)) {
-                       const struct cis_tpl *tpl = cis_tpl_list + i;
-                       if (tpl_link < tpl->min_size) {
-                               printk(KERN_ERR
-                                      "%s: bad CIS tuple 0x%02x"
-                                      " (length = %u, expected >= %u)\n",
-                                      mmc_hostname(card->host),
-                                      tpl_code, tpl_link, tpl->min_size);
-                               ret = -EINVAL;
-                       } else if (tpl->parse) {
-                               ret = tpl->parse(card, func,
-                                                this->data, tpl_link);
-                       }
+               /* Try to parse the CIS tuple */
+               ret = cis_tpl_parse(card, func, "CIS",
+                                   cis_tpl_list, ARRAY_SIZE(cis_tpl_list),
+                                   tpl_code, this->data, tpl_link);
+               if (ret == -EILSEQ || ret == -ENOENT) {
                        /*
-                        * We don't need the tuple anymore if it was
-                        * successfully parsed by the SDIO core or if it is
-                        * not going to be parsed by SDIO drivers.
+                        * The tuple is unknown or known but not parsed.
+                        * Queue the tuple for the function driver.
                         */
-                       if (!ret || ret != -EILSEQ)
-                               kfree(this);
-               } else {
-                       /* unknown tuple */
-                       ret = -EILSEQ;
-               }
-
-               if (ret == -EILSEQ) {
-                       /* this tuple is unknown to the core or whitelisted */
                        this->next = NULL;
                        this->code = tpl_code;
                        this->size = tpl_link;
                        *prev = this;
                        prev = &this->next;
-                       printk(KERN_DEBUG
-                              "%s: queuing CIS tuple 0x%02x length %u\n",
-                              mmc_hostname(card->host), tpl_code, tpl_link);
+
+                       if (ret == -ENOENT) {
+                               /* warn about unknown tuples */
+                               printk(KERN_WARNING "%s: queuing unknown"
+                                      " CIS tuple 0x%02x (%u bytes)\n",
+                                      mmc_hostname(card->host),
+                                      tpl_code, tpl_link);
+                       }
+
                        /* keep on analyzing tuples */
                        ret = 0;
+               } else {
+                       /*
+                        * We don't need the tuple anymore if it was
+                        * successfully parsed by the SDIO core or if it is
+                        * not going to be queued for a driver.
+                        */
+                       kfree(this);
                }
 
                ptr += tpl_link;
index e04b751680d0a763fe980b174bb62fae4917043a..9d405b1817812407469f1cf2b778639237acd940 100644 (file)
@@ -251,6 +251,14 @@ config MMC_MVSDIO
          To compile this driver as a module, choose M here: the
          module will be called mvsdio.
 
+config MMC_DAVINCI
+        tristate "TI DAVINCI Multimedia Card Interface support"
+        depends on ARCH_DAVINCI
+        help
+          This selects the TI DAVINCI Multimedia card Interface.
+          If you have an DAVINCI board with a Multimedia Card slot,
+          say Y or M here.  If unsure, say N.
+
 config MMC_SPI
        tristate "MMC/SD/SDIO over SPI"
        depends on SPI_MASTER && !HIGHMEM && HAS_DMA
@@ -357,3 +365,22 @@ config MMC_VIA_SDMMC
          If you have a controller with this interface, say Y or M here.
 
          If unsure, say N.
+
+config SDH_BFIN
+       tristate "Blackfin Secure Digital Host support"
+       depends on MMC && ((BF54x && !BF544) || (BF51x && !BF512))
+       help
+         If you say yes here you will get support for the Blackfin on-chip
+         Secure Digital Host interface.  This includes support for MMC and
+         SD cards.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bfin_sdh.
+
+         If unsure, say N.
+
+config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
+       bool "Blackfin EZkit Missing SDH_CMD Pull Up Resistor Workaround"
+       depends on SDH_BFIN
+       help
+         If you say yes here SD-Cards may work on the EZkit.
index abcb0400e06d23236b1d1b6cfde623a1bb706abf..ded4d8cdd9d7d371baf3ce225abbe197cedbe4b0 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_MMC_ATMELMCI)    += atmel-mci.o
 obj-$(CONFIG_MMC_TIFM_SD)      += tifm_sd.o
 obj-$(CONFIG_MMC_MSM7X00A)     += msm_sdcc.o
 obj-$(CONFIG_MMC_MVSDIO)       += mvsdio.o
+obj-$(CONFIG_MMC_DAVINCI)       += davinci_mmc.o
 obj-$(CONFIG_MMC_SPI)          += mmc_spi.o
 ifeq ($(CONFIG_OF),y)
 obj-$(CONFIG_MMC_SPI)          += of_mmc_spi.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_MMC_SDRICOH_CS)  += sdricoh_cs.o
 obj-$(CONFIG_MMC_TMIO)         += tmio_mmc.o
 obj-$(CONFIG_MMC_CB710)        += cb710-mmc.o
 obj-$(CONFIG_MMC_VIA_SDMMC)    += via-sdmmc.o
+obj-$(CONFIG_SDH_BFIN)         += bfin_sdh.o
 
 ifeq ($(CONFIG_CB710_DEBUG),y)
        CFLAGS-cb710-mmc        += -DDEBUG
index fc25586b7ee1c94575e5674f89c3382c2a7ffd88..8072128e933b80a8ff6f68f2ed160a964ed04bc2 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/stat.h>
 
 #include <linux/mmc/host.h>
+
+#include <mach/atmel-mci.h>
 #include <linux/atmel-mci.h>
 
 #include <asm/io.h>
@@ -92,6 +94,7 @@ struct atmel_mci_dma {
  * @need_clock_update: Update the clock rate before the next request.
  * @need_reset: Reset controller before next request.
  * @mode_reg: Value of the MR register.
+ * @cfg_reg: Value of the CFG register.
  * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
  *     rate and timeout calculations.
  * @mapbase: Physical address of the MMIO registers.
@@ -155,6 +158,7 @@ struct atmel_mci {
        bool                    need_clock_update;
        bool                    need_reset;
        u32                     mode_reg;
+       u32                     cfg_reg;
        unsigned long           bus_hz;
        unsigned long           mapbase;
        struct clk              *mck;
@@ -222,6 +226,19 @@ static bool mci_has_rwproof(void)
                return true;
 }
 
+/*
+ * The new MCI2 module isn't 100% compatible with the old MCI module,
+ * and it has a few nice features which we want to use...
+ */
+static inline bool atmci_is_mci2(void)
+{
+       if (cpu_is_at91sam9g45())
+               return true;
+
+       return false;
+}
+
+
 /*
  * The debugfs stuff below is mostly optimized away when
  * CONFIG_DEBUG_FS is not set.
@@ -357,12 +374,33 @@ static int atmci_regs_show(struct seq_file *s, void *v)
                        buf[MCI_BLKR / 4],
                        buf[MCI_BLKR / 4] & 0xffff,
                        (buf[MCI_BLKR / 4] >> 16) & 0xffff);
+       if (atmci_is_mci2())
+               seq_printf(s, "CSTOR:\t0x%08x\n", buf[MCI_CSTOR / 4]);
 
        /* Don't read RSPR and RDR; it will consume the data there */
 
        atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
        atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
 
+       if (atmci_is_mci2()) {
+               u32 val;
+
+               val = buf[MCI_DMA / 4];
+               seq_printf(s, "DMA:\t0x%08x OFFSET=%u CHKSIZE=%u%s\n",
+                               val, val & 3,
+                               ((val >> 4) & 3) ?
+                                       1 << (((val >> 4) & 3) + 1) : 1,
+                               val & MCI_DMAEN ? " DMAEN" : "");
+
+               val = buf[MCI_CFG / 4];
+               seq_printf(s, "CFG:\t0x%08x%s%s%s%s\n",
+                               val,
+                               val & MCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "",
+                               val & MCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "",
+                               val & MCI_CFG_HSMODE ? " HSMODE" : "",
+                               val & MCI_CFG_LSYNC ? " LSYNC" : "");
+       }
+
        kfree(buf);
 
        return 0;
@@ -557,6 +595,10 @@ static void atmci_dma_complete(void *arg)
 
        dev_vdbg(&host->pdev->dev, "DMA complete\n");
 
+       if (atmci_is_mci2())
+               /* Disable DMA hardware handshaking on MCI */
+               mci_writel(host, DMA, mci_readl(host, DMA) & ~MCI_DMAEN);
+
        atmci_dma_cleanup(host);
 
        /*
@@ -592,7 +634,7 @@ static void atmci_dma_complete(void *arg)
 }
 
 static int
-atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
 {
        struct dma_chan                 *chan;
        struct dma_async_tx_descriptor  *desc;
@@ -624,6 +666,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
        if (!chan)
                return -ENODEV;
 
+       if (atmci_is_mci2())
+               mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN);
+
        if (data->flags & MMC_DATA_READ)
                direction = DMA_FROM_DEVICE;
        else
@@ -641,10 +686,6 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
        host->dma.data_desc = desc;
        desc->callback = atmci_dma_complete;
        desc->callback_param = host;
-       desc->tx_submit(desc);
-
-       /* Go! */
-       chan->device->device_issue_pending(chan);
 
        return 0;
 unmap_exit:
@@ -652,13 +693,26 @@ unmap_exit:
        return -ENOMEM;
 }
 
+static void atmci_submit_data(struct atmel_mci *host)
+{
+       struct dma_chan                 *chan = host->data_chan;
+       struct dma_async_tx_descriptor  *desc = host->dma.data_desc;
+
+       if (chan) {
+               desc->tx_submit(desc);
+               chan->device->device_issue_pending(chan);
+       }
+}
+
 #else /* CONFIG_MMC_ATMELMCI_DMA */
 
-static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+static int atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
 {
        return -ENOSYS;
 }
 
+static void atmci_submit_data(struct atmel_mci *host) {}
+
 static void atmci_stop_dma(struct atmel_mci *host)
 {
        /* Data transfer was stopped by the interrupt handler */
@@ -672,7 +726,7 @@ static void atmci_stop_dma(struct atmel_mci *host)
  * Returns a mask of interrupt flags to be enabled after the whole
  * request has been prepared.
  */
-static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
+static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
 {
        u32 iflags;
 
@@ -683,7 +737,7 @@ static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
        host->data = data;
 
        iflags = ATMCI_DATA_ERROR_FLAGS;
-       if (atmci_submit_data_dma(host, data)) {
+       if (atmci_prepare_data_dma(host, data)) {
                host->data_chan = NULL;
 
                /*
@@ -729,6 +783,8 @@ static void atmci_start_request(struct atmel_mci *host,
                mci_writel(host, CR, MCI_CR_SWRST);
                mci_writel(host, CR, MCI_CR_MCIEN);
                mci_writel(host, MR, host->mode_reg);
+               if (atmci_is_mci2())
+                       mci_writel(host, CFG, host->cfg_reg);
                host->need_reset = false;
        }
        mci_writel(host, SDCR, slot->sdc_reg);
@@ -744,6 +800,7 @@ static void atmci_start_request(struct atmel_mci *host,
                while (!(mci_readl(host, SR) & MCI_CMDRDY))
                        cpu_relax();
        }
+       iflags = 0;
        data = mrq->data;
        if (data) {
                atmci_set_timeout(host, slot, data);
@@ -753,15 +810,17 @@ static void atmci_start_request(struct atmel_mci *host,
                                | MCI_BLKLEN(data->blksz));
                dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
                        MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
+
+               iflags |= atmci_prepare_data(host, data);
        }
 
-       iflags = MCI_CMDRDY;
+       iflags |= MCI_CMDRDY;
        cmd = mrq->cmd;
        cmdflags = atmci_prepare_command(slot->mmc, cmd);
        atmci_start_command(host, cmd, cmdflags);
 
        if (data)
-               iflags |= atmci_submit_data(host, data);
+               atmci_submit_data(host);
 
        if (mrq->stop) {
                host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
@@ -857,6 +916,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        clk_enable(host->mck);
                        mci_writel(host, CR, MCI_CR_SWRST);
                        mci_writel(host, CR, MCI_CR_MCIEN);
+                       if (atmci_is_mci2())
+                               mci_writel(host, CFG, host->cfg_reg);
                }
 
                /*
@@ -1095,6 +1156,8 @@ static void atmci_detect_change(unsigned long data)
                                mci_writel(host, CR, MCI_CR_SWRST);
                                mci_writel(host, CR, MCI_CR_MCIEN);
                                mci_writel(host, MR, host->mode_reg);
+                               if (atmci_is_mci2())
+                                       mci_writel(host, CFG, host->cfg_reg);
 
                                host->data = NULL;
                                host->cmd = NULL;
@@ -1584,14 +1647,47 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
 #ifdef CONFIG_MMC_ATMELMCI_DMA
 static bool filter(struct dma_chan *chan, void *slave)
 {
-       struct dw_dma_slave *dws = slave;
+       struct mci_dma_data     *sl = slave;
 
-       if (dws->dma_dev == chan->device->dev) {
-               chan->private = dws;
+       if (sl && find_slave_dev(sl) == chan->device->dev) {
+               chan->private = slave_data_ptr(sl);
                return true;
-       } else
+       } else {
                return false;
+       }
 }
+
+static void atmci_configure_dma(struct atmel_mci *host)
+{
+       struct mci_platform_data        *pdata;
+
+       if (host == NULL)
+               return;
+
+       pdata = host->pdev->dev.platform_data;
+
+       if (pdata && find_slave_dev(pdata->dma_slave)) {
+               dma_cap_mask_t mask;
+
+               setup_dma_addr(pdata->dma_slave,
+                              host->mapbase + MCI_TDR,
+                              host->mapbase + MCI_RDR);
+
+               /* Try to grab a DMA channel */
+               dma_cap_zero(mask);
+               dma_cap_set(DMA_SLAVE, mask);
+               host->dma.chan =
+                       dma_request_channel(mask, filter, pdata->dma_slave);
+       }
+       if (!host->dma.chan)
+               dev_notice(&host->pdev->dev, "DMA not available, using PIO\n");
+       else
+               dev_info(&host->pdev->dev,
+                                       "Using %s for DMA transfers\n",
+                                       dma_chan_name(host->dma.chan));
+}
+#else
+static void atmci_configure_dma(struct atmel_mci *host) {}
 #endif
 
 static int __init atmci_probe(struct platform_device *pdev)
@@ -1645,22 +1741,7 @@ static int __init atmci_probe(struct platform_device *pdev)
        if (ret)
                goto err_request_irq;
 
-#ifdef CONFIG_MMC_ATMELMCI_DMA
-       if (pdata->dma_slave.dma_dev) {
-               struct dw_dma_slave *dws = &pdata->dma_slave;
-               dma_cap_mask_t mask;
-
-               dws->tx_reg = regs->start + MCI_TDR;
-               dws->rx_reg = regs->start + MCI_RDR;
-
-               /* Try to grab a DMA channel */
-               dma_cap_zero(mask);
-               dma_cap_set(DMA_SLAVE, mask);
-               host->dma.chan = dma_request_channel(mask, filter, dws);
-       }
-       if (!host->dma.chan)
-               dev_notice(&pdev->dev, "DMA not available, using PIO\n");
-#endif /* CONFIG_MMC_ATMELMCI_DMA */
+       atmci_configure_dma(host);
 
        platform_set_drvdata(pdev, host);
 
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
new file mode 100644 (file)
index 0000000..3343a57
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * bfin_sdh.c - Analog Devices Blackfin SDH Controller
+ *
+ * Copyright (C) 2007-2009 Analog Device Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#define DRIVER_NAME    "bfin-sdh"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/proc_fs.h>
+
+#include <asm/cacheflush.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <asm/bfin_sdh.h>
+
+#if defined(CONFIG_BF51x)
+#define bfin_read_SDH_PWR_CTL          bfin_read_RSI_PWR_CTL
+#define bfin_write_SDH_PWR_CTL         bfin_write_RSI_PWR_CTL
+#define bfin_read_SDH_CLK_CTL          bfin_read_RSI_CLK_CTL
+#define bfin_write_SDH_CLK_CTL         bfin_write_RSI_CLK_CTL
+#define bfin_write_SDH_ARGUMENT                bfin_write_RSI_ARGUMENT
+#define bfin_write_SDH_COMMAND         bfin_write_RSI_COMMAND
+#define bfin_write_SDH_DATA_TIMER      bfin_write_RSI_DATA_TIMER
+#define bfin_read_SDH_RESPONSE0                bfin_read_RSI_RESPONSE0
+#define bfin_read_SDH_RESPONSE1                bfin_read_RSI_RESPONSE1
+#define bfin_read_SDH_RESPONSE2                bfin_read_RSI_RESPONSE2
+#define bfin_read_SDH_RESPONSE3                bfin_read_RSI_RESPONSE3
+#define bfin_write_SDH_DATA_LGTH       bfin_write_RSI_DATA_LGTH
+#define bfin_read_SDH_DATA_CTL         bfin_read_RSI_DATA_CTL
+#define bfin_write_SDH_DATA_CTL                bfin_write_RSI_DATA_CTL
+#define bfin_read_SDH_DATA_CNT         bfin_read_RSI_DATA_CNT
+#define bfin_write_SDH_STATUS_CLR      bfin_write_RSI_STATUS_CLR
+#define bfin_read_SDH_E_STATUS         bfin_read_RSI_E_STATUS
+#define bfin_write_SDH_E_STATUS                bfin_write_RSI_E_STATUS
+#define bfin_read_SDH_STATUS           bfin_read_RSI_STATUS
+#define bfin_write_SDH_MASK0           bfin_write_RSI_MASK0
+#define bfin_read_SDH_CFG              bfin_read_RSI_CFG
+#define bfin_write_SDH_CFG             bfin_write_RSI_CFG
+#endif
+
+struct dma_desc_array {
+       unsigned long   start_addr;
+       unsigned short  cfg;
+       unsigned short  x_count;
+       short           x_modify;
+} __packed;
+
+struct sdh_host {
+       struct mmc_host         *mmc;
+       spinlock_t              lock;
+       struct resource         *res;
+       void __iomem            *base;
+       int                     irq;
+       int                     stat_irq;
+       int                     dma_ch;
+       int                     dma_dir;
+       struct dma_desc_array   *sg_cpu;
+       dma_addr_t              sg_dma;
+       int                     dma_len;
+
+       unsigned int            imask;
+       unsigned int            power_mode;
+       unsigned int            clk_div;
+
+       struct mmc_request      *mrq;
+       struct mmc_command      *cmd;
+       struct mmc_data         *data;
+};
+
+static struct bfin_sd_host *get_sdh_data(struct platform_device *pdev)
+{
+       return pdev->dev.platform_data;
+}
+
+static void sdh_stop_clock(struct sdh_host *host)
+{
+       bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() & ~CLK_E);
+       SSYNC();
+}
+
+static void sdh_enable_stat_irq(struct sdh_host *host, unsigned int mask)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->lock, flags);
+       host->imask |= mask;
+       bfin_write_SDH_MASK0(mask);
+       SSYNC();
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdh_disable_stat_irq(struct sdh_host *host, unsigned int mask)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->lock, flags);
+       host->imask &= ~mask;
+       bfin_write_SDH_MASK0(host->imask);
+       SSYNC();
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
+{
+       unsigned int length;
+       unsigned int data_ctl;
+       unsigned int dma_cfg;
+       struct scatterlist *sg;
+
+       dev_dbg(mmc_dev(host->mmc), "%s enter flags: 0x%x\n", __func__, data->flags);
+       host->data = data;
+       data_ctl = 0;
+       dma_cfg = 0;
+
+       length = data->blksz * data->blocks;
+       bfin_write_SDH_DATA_LGTH(length);
+
+       if (data->flags & MMC_DATA_STREAM)
+               data_ctl |= DTX_MODE;
+
+       if (data->flags & MMC_DATA_READ)
+               data_ctl |= DTX_DIR;
+       /* Only supports power-of-2 block size */
+       if (data->blksz & (data->blksz - 1))
+               return -EINVAL;
+       data_ctl |= ((ffs(data->blksz) - 1) << 4);
+
+       bfin_write_SDH_DATA_CTL(data_ctl);
+
+       bfin_write_SDH_DATA_TIMER(0xFFFF);
+       SSYNC();
+
+       if (data->flags & MMC_DATA_READ) {
+               host->dma_dir = DMA_FROM_DEVICE;
+               dma_cfg |= WNR;
+       } else
+               host->dma_dir = DMA_TO_DEVICE;
+
+       sdh_enable_stat_irq(host, (DAT_CRC_FAIL | DAT_TIME_OUT | DAT_END));
+       host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
+#if defined(CONFIG_BF54x)
+       dma_cfg |= DMAFLOW_ARRAY | NDSIZE_5 | RESTART | WDSIZE_32 | DMAEN;
+       {
+               int i;
+               for_each_sg(data->sg, sg, host->dma_len, i) {
+                       host->sg_cpu[i].start_addr = sg_dma_address(sg);
+                       host->sg_cpu[i].cfg = dma_cfg;
+                       host->sg_cpu[i].x_count = sg_dma_len(sg) / 4;
+                       host->sg_cpu[i].x_modify = 4;
+                       dev_dbg(mmc_dev(host->mmc), "%d: start_addr:0x%lx, "
+                               "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n",
+                               i, host->sg_cpu[i].start_addr,
+                               host->sg_cpu[i].cfg, host->sg_cpu[i].x_count,
+                               host->sg_cpu[i].x_modify);
+               }
+       }
+       flush_dcache_range((unsigned int)host->sg_cpu,
+               (unsigned int)host->sg_cpu +
+                       host->dma_len * sizeof(struct dma_desc_array));
+       /* Set the last descriptor to stop mode */
+       host->sg_cpu[host->dma_len - 1].cfg &= ~(DMAFLOW | NDSIZE);
+       host->sg_cpu[host->dma_len - 1].cfg |= DI_EN;
+
+       set_dma_curr_desc_addr(host->dma_ch, (unsigned long *)host->sg_dma);
+       set_dma_x_count(host->dma_ch, 0);
+       set_dma_x_modify(host->dma_ch, 0);
+       set_dma_config(host->dma_ch, dma_cfg);
+#elif defined(CONFIG_BF51x)
+       /* RSI DMA doesn't work in array mode */
+       dma_cfg |= WDSIZE_32 | DMAEN;
+       set_dma_start_addr(host->dma_ch, sg_dma_address(&data->sg[0]));
+       set_dma_x_count(host->dma_ch, length / 4);
+       set_dma_x_modify(host->dma_ch, 4);
+       set_dma_config(host->dma_ch, dma_cfg);
+#endif
+       bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
+
+       SSYNC();
+
+       dev_dbg(mmc_dev(host->mmc), "%s exit\n", __func__);
+       return 0;
+}
+
+static void sdh_start_cmd(struct sdh_host *host, struct mmc_command *cmd)
+{
+       unsigned int sdh_cmd;
+       unsigned int stat_mask;
+
+       dev_dbg(mmc_dev(host->mmc), "%s enter cmd: 0x%p\n", __func__, cmd);
+       WARN_ON(host->cmd != NULL);
+       host->cmd = cmd;
+
+       sdh_cmd = 0;
+       stat_mask = 0;
+
+       sdh_cmd |= cmd->opcode;
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               sdh_cmd |= CMD_RSP;
+               stat_mask |= CMD_RESP_END;
+       } else {
+               stat_mask |= CMD_SENT;
+       }
+
+       if (cmd->flags & MMC_RSP_136)
+               sdh_cmd |= CMD_L_RSP;
+
+       stat_mask |= CMD_CRC_FAIL | CMD_TIME_OUT;
+
+       sdh_enable_stat_irq(host, stat_mask);
+
+       bfin_write_SDH_ARGUMENT(cmd->arg);
+       bfin_write_SDH_COMMAND(sdh_cmd | CMD_E);
+       bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() | CLK_E);
+       SSYNC();
+}
+
+static void sdh_finish_request(struct sdh_host *host, struct mmc_request *mrq)
+{
+       dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
+       host->mrq = NULL;
+       host->cmd = NULL;
+       host->data = NULL;
+       mmc_request_done(host->mmc, mrq);
+}
+
+static int sdh_cmd_done(struct sdh_host *host, unsigned int stat)
+{
+       struct mmc_command *cmd = host->cmd;
+       int ret = 0;
+
+       dev_dbg(mmc_dev(host->mmc), "%s enter cmd: %p\n", __func__, cmd);
+       if (!cmd)
+               return 0;
+
+       host->cmd = NULL;
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               cmd->resp[0] = bfin_read_SDH_RESPONSE0();
+               if (cmd->flags & MMC_RSP_136) {
+                       cmd->resp[1] = bfin_read_SDH_RESPONSE1();
+                       cmd->resp[2] = bfin_read_SDH_RESPONSE2();
+                       cmd->resp[3] = bfin_read_SDH_RESPONSE3();
+               }
+       }
+       if (stat & CMD_TIME_OUT)
+               cmd->error = -ETIMEDOUT;
+       else if (stat & CMD_CRC_FAIL && cmd->flags & MMC_RSP_CRC)
+               cmd->error = -EILSEQ;
+
+       sdh_disable_stat_irq(host, (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL));
+
+       if (host->data && !cmd->error) {
+               if (host->data->flags & MMC_DATA_WRITE) {
+                       ret = sdh_setup_data(host, host->data);
+                       if (ret)
+                               return 0;
+               }
+
+               sdh_enable_stat_irq(host, DAT_END | RX_OVERRUN | TX_UNDERRUN | DAT_TIME_OUT);
+       } else
+               sdh_finish_request(host, host->mrq);
+
+       return 1;
+}
+
+static int sdh_data_done(struct sdh_host *host, unsigned int stat)
+{
+       struct mmc_data *data = host->data;
+
+       dev_dbg(mmc_dev(host->mmc), "%s enter stat: 0x%x\n", __func__, stat);
+       if (!data)
+               return 0;
+
+       disable_dma(host->dma_ch);
+       dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+                    host->dma_dir);
+
+       if (stat & DAT_TIME_OUT)
+               data->error = -ETIMEDOUT;
+       else if (stat & DAT_CRC_FAIL)
+               data->error = -EILSEQ;
+       else if (stat & (RX_OVERRUN | TX_UNDERRUN))
+               data->error = -EIO;
+
+       if (!data->error)
+               data->bytes_xfered = data->blocks * data->blksz;
+       else
+               data->bytes_xfered = 0;
+
+       sdh_disable_stat_irq(host, DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN);
+       bfin_write_SDH_STATUS_CLR(DAT_END_STAT | DAT_TIMEOUT_STAT | \
+                       DAT_CRC_FAIL_STAT | DAT_BLK_END_STAT | RX_OVERRUN | TX_UNDERRUN);
+       bfin_write_SDH_DATA_CTL(0);
+       SSYNC();
+
+       host->data = NULL;
+       if (host->mrq->stop) {
+               sdh_stop_clock(host);
+               sdh_start_cmd(host, host->mrq->stop);
+       } else {
+               sdh_finish_request(host, host->mrq);
+       }
+
+       return 1;
+}
+
+static void sdh_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct sdh_host *host = mmc_priv(mmc);
+       int ret = 0;
+
+       dev_dbg(mmc_dev(host->mmc), "%s enter, mrp:%p, cmd:%p\n", __func__, mrq, mrq->cmd);
+       WARN_ON(host->mrq != NULL);
+
+       host->mrq = mrq;
+       host->data = mrq->data;
+
+       if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
+               ret = sdh_setup_data(host, mrq->data);
+               if (ret)
+                       return;
+       }
+
+       sdh_start_cmd(host, mrq->cmd);
+}
+
+static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct sdh_host *host;
+       unsigned long flags;
+       u16 clk_ctl = 0;
+       u16 pwr_ctl = 0;
+       u16 cfg;
+       host = mmc_priv(mmc);
+
+       spin_lock_irqsave(&host->lock, flags);
+       if (ios->clock) {
+               unsigned long  sys_clk, ios_clk;
+               unsigned char clk_div;
+               ios_clk = 2 * ios->clock;
+               sys_clk = get_sclk();
+               clk_div = sys_clk / ios_clk;
+               if (sys_clk % ios_clk == 0)
+                       clk_div -= 1;
+               clk_div = min_t(unsigned char, clk_div, 0xFF);
+               clk_ctl |= clk_div;
+               clk_ctl |= CLK_E;
+               host->clk_div = clk_div;
+       } else
+               sdh_stop_clock(host);
+
+       if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+#ifdef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
+               pwr_ctl |= ROD_CTL;
+#else
+               pwr_ctl |= SD_CMD_OD | ROD_CTL;
+#endif
+
+       if (ios->bus_width == MMC_BUS_WIDTH_4) {
+               cfg = bfin_read_SDH_CFG();
+               cfg &= ~PD_SDDAT3;
+               cfg |= PUP_SDDAT3;
+               /* Enable 4 bit SDIO */
+               cfg |= (SD4E | MWE);
+               bfin_write_SDH_CFG(cfg);
+               clk_ctl |= WIDE_BUS;
+       } else {
+               cfg = bfin_read_SDH_CFG();
+               cfg |= MWE;
+               bfin_write_SDH_CFG(cfg);
+       }
+
+       bfin_write_SDH_CLK_CTL(clk_ctl);
+
+       host->power_mode = ios->power_mode;
+       if (ios->power_mode == MMC_POWER_ON)
+               pwr_ctl |= PWR_ON;
+
+       bfin_write_SDH_PWR_CTL(pwr_ctl);
+       SSYNC();
+
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       dev_dbg(mmc_dev(host->mmc), "SDH: clk_div = 0x%x actual clock:%ld expected clock:%d\n",
+               host->clk_div,
+               host->clk_div ? get_sclk() / (2 * (host->clk_div + 1)) : 0,
+               ios->clock);
+}
+
+static const struct mmc_host_ops sdh_ops = {
+       .request        = sdh_request,
+       .set_ios        = sdh_set_ios,
+};
+
+static irqreturn_t sdh_dma_irq(int irq, void *devid)
+{
+       struct sdh_host *host = devid;
+
+       dev_dbg(mmc_dev(host->mmc), "%s enter, irq_stat: 0x%04x\n", __func__,
+               get_dma_curr_irqstat(host->dma_ch));
+       clear_dma_irqstat(host->dma_ch);
+       SSYNC();
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t sdh_stat_irq(int irq, void *devid)
+{
+       struct sdh_host *host = devid;
+       unsigned int status;
+       int handled = 0;
+
+       dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
+       status = bfin_read_SDH_E_STATUS();
+       if (status & SD_CARD_DET) {
+               mmc_detect_change(host->mmc, 0);
+               bfin_write_SDH_E_STATUS(SD_CARD_DET);
+       }
+       status = bfin_read_SDH_STATUS();
+       if (status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL)) {
+               handled |= sdh_cmd_done(host, status);
+               bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | \
+                               CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT);
+               SSYNC();
+       }
+
+       status = bfin_read_SDH_STATUS();
+       if (status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN))
+               handled |= sdh_data_done(host, status);
+
+       dev_dbg(mmc_dev(host->mmc), "%s exit\n\n", __func__);
+
+       return IRQ_RETVAL(handled);
+}
+
+static int __devinit sdh_probe(struct platform_device *pdev)
+{
+       struct mmc_host *mmc;
+       struct sdh_host *host;
+       struct bfin_sd_host *drv_data = get_sdh_data(pdev);
+       int ret;
+
+       if (!drv_data) {
+               dev_err(&pdev->dev, "missing platform driver data\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       mmc = mmc_alloc_host(sizeof(*mmc), &pdev->dev);
+       if (!mmc) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       mmc->ops = &sdh_ops;
+       mmc->max_phys_segs = 32;
+       mmc->max_seg_size = 1 << 16;
+       mmc->max_blk_size = 1 << 11;
+       mmc->max_blk_count = 1 << 11;
+       mmc->max_req_size = PAGE_SIZE;
+       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+       mmc->f_max = get_sclk();
+       mmc->f_min = mmc->f_max >> 9;
+       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL;
+       host = mmc_priv(mmc);
+       host->mmc = mmc;
+
+       spin_lock_init(&host->lock);
+       host->irq = drv_data->irq_int0;
+       host->dma_ch = drv_data->dma_chan;
+
+       ret = request_dma(host->dma_ch, DRIVER_NAME "DMA");
+       if (ret) {
+               dev_err(&pdev->dev, "unable to request DMA channel\n");
+               goto out1;
+       }
+
+       ret = set_dma_callback(host->dma_ch, sdh_dma_irq, host);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to request DMA irq\n");
+               goto out2;
+       }
+
+       host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
+       if (host->sg_cpu == NULL) {
+               ret = -ENOMEM;
+               goto out2;
+       }
+
+       platform_set_drvdata(pdev, mmc);
+       mmc_add_host(mmc);
+
+       ret = request_irq(host->irq, sdh_stat_irq, 0, "SDH Status IRQ", host);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to request status irq\n");
+               goto out3;
+       }
+
+       ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to request peripheral pins\n");
+               goto out4;
+       }
+#if defined(CONFIG_BF54x)
+       /* Secure Digital Host shares DMA with Nand controller */
+       bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
+#endif
+
+       bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
+       SSYNC();
+
+       /* Disable card inserting detection pin. set MMC_CAP_NEES_POLL, and
+        * mmc stack will do the detection.
+        */
+       bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
+       SSYNC();
+
+       return 0;
+
+out4:
+       free_irq(host->irq, host);
+out3:
+       mmc_remove_host(mmc);
+       dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+out2:
+       free_dma(host->dma_ch);
+out1:
+       mmc_free_host(mmc);
+ out:
+       return ret;
+}
+
+static int __devexit sdh_remove(struct platform_device *pdev)
+{
+       struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       if (mmc) {
+               struct sdh_host *host = mmc_priv(mmc);
+
+               mmc_remove_host(mmc);
+
+               sdh_stop_clock(host);
+               free_irq(host->irq, host);
+               free_dma(host->dma_ch);
+               dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+               mmc_free_host(mmc);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int sdh_suspend(struct platform_device *dev, pm_message_t state)
+{
+       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct bfin_sd_host *drv_data = get_sdh_data(dev);
+       int ret = 0;
+
+       if (mmc)
+               ret = mmc_suspend_host(mmc, state);
+
+       bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() & ~PWR_ON);
+       peripheral_free_list(drv_data->pin_req);
+
+       return ret;
+}
+
+static int sdh_resume(struct platform_device *dev)
+{
+       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct bfin_sd_host *drv_data = get_sdh_data(dev);
+       int ret = 0;
+
+       ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
+       if (ret) {
+               dev_err(&dev->dev, "unable to request peripheral pins\n");
+               return ret;
+       }
+
+       bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() | PWR_ON);
+#if defined(CONFIG_BF54x)
+       /* Secure Digital Host shares DMA with Nand controller */
+       bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
+#endif
+       bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
+       SSYNC();
+
+       bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
+       SSYNC();
+
+       if (mmc)
+               ret = mmc_resume_host(mmc);
+
+       return ret;
+}
+#else
+# define sdh_suspend NULL
+# define sdh_resume  NULL
+#endif
+
+static struct platform_driver sdh_driver = {
+       .probe   = sdh_probe,
+       .remove  = __devexit_p(sdh_remove),
+       .suspend = sdh_suspend,
+       .resume  = sdh_resume,
+       .driver  = {
+               .name = DRIVER_NAME,
+       },
+};
+
+static int __init sdh_init(void)
+{
+       return platform_driver_register(&sdh_driver);
+}
+module_init(sdh_init);
+
+static void __exit sdh_exit(void)
+{
+       platform_driver_unregister(&sdh_driver);
+}
+module_exit(sdh_exit);
+
+MODULE_DESCRIPTION("Blackfin Secure Digital Host Driver");
+MODULE_AUTHOR("Cliff Cai, Roy Huang");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
new file mode 100644 (file)
index 0000000..dd45e7c
--- /dev/null
@@ -0,0 +1,1349 @@
+/*
+ * davinci_mmc.c - TI DaVinci MMC/SD/SDIO driver
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ *       Original author: Purushotam Kumar
+ * Copyright (C) 2009 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/cpufreq.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/mmc.h>
+
+#include <mach/mmc.h>
+#include <mach/edma.h>
+
+/*
+ * Register Definitions
+ */
+#define DAVINCI_MMCCTL       0x00 /* Control Register                  */
+#define DAVINCI_MMCCLK       0x04 /* Memory Clock Control Register     */
+#define DAVINCI_MMCST0       0x08 /* Status Register 0                 */
+#define DAVINCI_MMCST1       0x0C /* Status Register 1                 */
+#define DAVINCI_MMCIM        0x10 /* Interrupt Mask Register           */
+#define DAVINCI_MMCTOR       0x14 /* Response Time-Out Register        */
+#define DAVINCI_MMCTOD       0x18 /* Data Read Time-Out Register       */
+#define DAVINCI_MMCBLEN      0x1C /* Block Length Register             */
+#define DAVINCI_MMCNBLK      0x20 /* Number of Blocks Register         */
+#define DAVINCI_MMCNBLC      0x24 /* Number of Blocks Counter Register */
+#define DAVINCI_MMCDRR       0x28 /* Data Receive Register             */
+#define DAVINCI_MMCDXR       0x2C /* Data Transmit Register            */
+#define DAVINCI_MMCCMD       0x30 /* Command Register                  */
+#define DAVINCI_MMCARGHL     0x34 /* Argument Register                 */
+#define DAVINCI_MMCRSP01     0x38 /* Response Register 0 and 1         */
+#define DAVINCI_MMCRSP23     0x3C /* Response Register 0 and 1         */
+#define DAVINCI_MMCRSP45     0x40 /* Response Register 0 and 1         */
+#define DAVINCI_MMCRSP67     0x44 /* Response Register 0 and 1         */
+#define DAVINCI_MMCDRSP      0x48 /* Data Response Register            */
+#define DAVINCI_MMCETOK      0x4C
+#define DAVINCI_MMCCIDX      0x50 /* Command Index Register            */
+#define DAVINCI_MMCCKC       0x54
+#define DAVINCI_MMCTORC      0x58
+#define DAVINCI_MMCTODC      0x5C
+#define DAVINCI_MMCBLNC      0x60
+#define DAVINCI_SDIOCTL      0x64
+#define DAVINCI_SDIOST0      0x68
+#define DAVINCI_SDIOEN       0x6C
+#define DAVINCI_SDIOST       0x70
+#define DAVINCI_MMCFIFOCTL   0x74 /* FIFO Control Register             */
+
+/* DAVINCI_MMCCTL definitions */
+#define MMCCTL_DATRST         (1 << 0)
+#define MMCCTL_CMDRST         (1 << 1)
+#define MMCCTL_WIDTH_4_BIT    (1 << 2)
+#define MMCCTL_DATEG_DISABLED (0 << 6)
+#define MMCCTL_DATEG_RISING   (1 << 6)
+#define MMCCTL_DATEG_FALLING  (2 << 6)
+#define MMCCTL_DATEG_BOTH     (3 << 6)
+#define MMCCTL_PERMDR_LE      (0 << 9)
+#define MMCCTL_PERMDR_BE      (1 << 9)
+#define MMCCTL_PERMDX_LE      (0 << 10)
+#define MMCCTL_PERMDX_BE      (1 << 10)
+
+/* DAVINCI_MMCCLK definitions */
+#define MMCCLK_CLKEN          (1 << 8)
+#define MMCCLK_CLKRT_MASK     (0xFF << 0)
+
+/* IRQ bit definitions, for DAVINCI_MMCST0 and DAVINCI_MMCIM */
+#define MMCST0_DATDNE         BIT(0)   /* data done */
+#define MMCST0_BSYDNE         BIT(1)   /* busy done */
+#define MMCST0_RSPDNE         BIT(2)   /* command done */
+#define MMCST0_TOUTRD         BIT(3)   /* data read timeout */
+#define MMCST0_TOUTRS         BIT(4)   /* command response timeout */
+#define MMCST0_CRCWR          BIT(5)   /* data write CRC error */
+#define MMCST0_CRCRD          BIT(6)   /* data read CRC error */
+#define MMCST0_CRCRS          BIT(7)   /* command response CRC error */
+#define MMCST0_DXRDY          BIT(9)   /* data transmit ready (fifo empty) */
+#define MMCST0_DRRDY          BIT(10)  /* data receive ready (data in fifo)*/
+#define MMCST0_DATED          BIT(11)  /* DAT3 edge detect */
+#define MMCST0_TRNDNE         BIT(12)  /* transfer done */
+
+/* DAVINCI_MMCST1 definitions */
+#define MMCST1_BUSY           (1 << 0)
+
+/* DAVINCI_MMCCMD definitions */
+#define MMCCMD_CMD_MASK       (0x3F << 0)
+#define MMCCMD_PPLEN          (1 << 7)
+#define MMCCMD_BSYEXP         (1 << 8)
+#define MMCCMD_RSPFMT_MASK    (3 << 9)
+#define MMCCMD_RSPFMT_NONE    (0 << 9)
+#define MMCCMD_RSPFMT_R1456   (1 << 9)
+#define MMCCMD_RSPFMT_R2      (2 << 9)
+#define MMCCMD_RSPFMT_R3      (3 << 9)
+#define MMCCMD_DTRW           (1 << 11)
+#define MMCCMD_STRMTP         (1 << 12)
+#define MMCCMD_WDATX          (1 << 13)
+#define MMCCMD_INITCK         (1 << 14)
+#define MMCCMD_DCLR           (1 << 15)
+#define MMCCMD_DMATRIG        (1 << 16)
+
+/* DAVINCI_MMCFIFOCTL definitions */
+#define MMCFIFOCTL_FIFORST    (1 << 0)
+#define MMCFIFOCTL_FIFODIR_WR (1 << 1)
+#define MMCFIFOCTL_FIFODIR_RD (0 << 1)
+#define MMCFIFOCTL_FIFOLEV    (1 << 2) /* 0 = 128 bits, 1 = 256 bits */
+#define MMCFIFOCTL_ACCWD_4    (0 << 3) /* access width of 4 bytes    */
+#define MMCFIFOCTL_ACCWD_3    (1 << 3) /* access width of 3 bytes    */
+#define MMCFIFOCTL_ACCWD_2    (2 << 3) /* access width of 2 bytes    */
+#define MMCFIFOCTL_ACCWD_1    (3 << 3) /* access width of 1 byte     */
+
+
+/* MMCSD Init clock in Hz in opendrain mode */
+#define MMCSD_INIT_CLOCK               200000
+
+/*
+ * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
+ * and we handle up to NR_SG segments.  MMC_BLOCK_BOUNCE kicks in only
+ * for drivers with max_hw_segs == 1, making the segments bigger (64KB)
+ * than the page or two that's otherwise typical.  NR_SG == 16 gives at
+ * least the same throughput boost, using EDMA transfer linkage instead
+ * of spending CPU time copying pages.
+ */
+#define MAX_CCNT       ((1 << 16) - 1)
+
+#define NR_SG          16
+
+static unsigned rw_threshold = 32;
+module_param(rw_threshold, uint, S_IRUGO);
+MODULE_PARM_DESC(rw_threshold,
+               "Read/Write threshold. Default = 32");
+
+static unsigned __initdata use_dma = 1;
+module_param(use_dma, uint, 0);
+MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1");
+
+struct mmc_davinci_host {
+       struct mmc_command *cmd;
+       struct mmc_data *data;
+       struct mmc_host *mmc;
+       struct clk *clk;
+       unsigned int mmc_input_clk;
+       void __iomem *base;
+       struct resource *mem_res;
+       int irq;
+       unsigned char bus_mode;
+
+#define DAVINCI_MMC_DATADIR_NONE       0
+#define DAVINCI_MMC_DATADIR_READ       1
+#define DAVINCI_MMC_DATADIR_WRITE      2
+       unsigned char data_dir;
+
+       /* buffer is used during PIO of one scatterlist segment, and
+        * is updated along with buffer_bytes_left.  bytes_left applies
+        * to all N blocks of the PIO transfer.
+        */
+       u8 *buffer;
+       u32 buffer_bytes_left;
+       u32 bytes_left;
+
+       u32 rxdma, txdma;
+       bool use_dma;
+       bool do_dma;
+
+       /* Scatterlist DMA uses one or more parameter RAM entries:
+        * the main one (associated with rxdma or txdma) plus zero or
+        * more links.  The entries for a given transfer differ only
+        * by memory buffer (address, length) and link field.
+        */
+       struct edmacc_param     tx_template;
+       struct edmacc_param     rx_template;
+       unsigned                n_link;
+       u32                     links[NR_SG - 1];
+
+       /* For PIO we walk scatterlists one segment at a time. */
+       unsigned int            sg_len;
+       struct scatterlist *sg;
+
+       /* Version of the MMC/SD controller */
+       u8 version;
+       /* for ns in one cycle calculation */
+       unsigned ns_in_one_cycle;
+#ifdef CONFIG_CPU_FREQ
+       struct notifier_block   freq_transition;
+#endif
+};
+
+
+/* PIO only */
+static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
+{
+       host->buffer_bytes_left = sg_dma_len(host->sg);
+       host->buffer = sg_virt(host->sg);
+       if (host->buffer_bytes_left > host->bytes_left)
+               host->buffer_bytes_left = host->bytes_left;
+}
+
+static void davinci_fifo_data_trans(struct mmc_davinci_host *host,
+                                       unsigned int n)
+{
+       u8 *p;
+       unsigned int i;
+
+       if (host->buffer_bytes_left == 0) {
+               host->sg = sg_next(host->data->sg);
+               mmc_davinci_sg_to_buf(host);
+       }
+
+       p = host->buffer;
+       if (n > host->buffer_bytes_left)
+               n = host->buffer_bytes_left;
+       host->buffer_bytes_left -= n;
+       host->bytes_left -= n;
+
+       /* NOTE:  we never transfer more than rw_threshold bytes
+        * to/from the fifo here; there's no I/O overlap.
+        * This also assumes that access width( i.e. ACCWD) is 4 bytes
+        */
+       if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
+               for (i = 0; i < (n >> 2); i++) {
+                       writel(*((u32 *)p), host->base + DAVINCI_MMCDXR);
+                       p = p + 4;
+               }
+               if (n & 3) {
+                       iowrite8_rep(host->base + DAVINCI_MMCDXR, p, (n & 3));
+                       p = p + (n & 3);
+               }
+       } else {
+               for (i = 0; i < (n >> 2); i++) {
+                       *((u32 *)p) = readl(host->base + DAVINCI_MMCDRR);
+                       p  = p + 4;
+               }
+               if (n & 3) {
+                       ioread8_rep(host->base + DAVINCI_MMCDRR, p, (n & 3));
+                       p = p + (n & 3);
+               }
+       }
+       host->buffer = p;
+}
+
+static void mmc_davinci_start_command(struct mmc_davinci_host *host,
+               struct mmc_command *cmd)
+{
+       u32 cmd_reg = 0;
+       u32 im_val;
+
+       dev_dbg(mmc_dev(host->mmc), "CMD%d, arg 0x%08x%s\n",
+               cmd->opcode, cmd->arg,
+               ({ char *s;
+               switch (mmc_resp_type(cmd)) {
+               case MMC_RSP_R1:
+                       s = ", R1/R5/R6/R7 response";
+                       break;
+               case MMC_RSP_R1B:
+                       s = ", R1b response";
+                       break;
+               case MMC_RSP_R2:
+                       s = ", R2 response";
+                       break;
+               case MMC_RSP_R3:
+                       s = ", R3/R4 response";
+                       break;
+               default:
+                       s = ", (R? response)";
+                       break;
+               }; s; }));
+       host->cmd = cmd;
+
+       switch (mmc_resp_type(cmd)) {
+       case MMC_RSP_R1B:
+               /* There's some spec confusion about when R1B is
+                * allowed, but if the card doesn't issue a BUSY
+                * then it's harmless for us to allow it.
+                */
+               cmd_reg |= MMCCMD_BSYEXP;
+               /* FALLTHROUGH */
+       case MMC_RSP_R1:                /* 48 bits, CRC */
+               cmd_reg |= MMCCMD_RSPFMT_R1456;
+               break;
+       case MMC_RSP_R2:                /* 136 bits, CRC */
+               cmd_reg |= MMCCMD_RSPFMT_R2;
+               break;
+       case MMC_RSP_R3:                /* 48 bits, no CRC */
+               cmd_reg |= MMCCMD_RSPFMT_R3;
+               break;
+       default:
+               cmd_reg |= MMCCMD_RSPFMT_NONE;
+               dev_dbg(mmc_dev(host->mmc), "unknown resp_type %04x\n",
+                       mmc_resp_type(cmd));
+               break;
+       }
+
+       /* Set command index */
+       cmd_reg |= cmd->opcode;
+
+       /* Enable EDMA transfer triggers */
+       if (host->do_dma)
+               cmd_reg |= MMCCMD_DMATRIG;
+
+       if (host->version == MMC_CTLR_VERSION_2 && host->data != NULL &&
+                       host->data_dir == DAVINCI_MMC_DATADIR_READ)
+               cmd_reg |= MMCCMD_DMATRIG;
+
+       /* Setting whether command involves data transfer or not */
+       if (cmd->data)
+               cmd_reg |= MMCCMD_WDATX;
+
+       /* Setting whether stream or block transfer */
+       if (cmd->flags & MMC_DATA_STREAM)
+               cmd_reg |= MMCCMD_STRMTP;
+
+       /* Setting whether data read or write */
+       if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
+               cmd_reg |= MMCCMD_DTRW;
+
+       if (host->bus_mode == MMC_BUSMODE_PUSHPULL)
+               cmd_reg |= MMCCMD_PPLEN;
+
+       /* set Command timeout */
+       writel(0x1FFF, host->base + DAVINCI_MMCTOR);
+
+       /* Enable interrupt (calculate here, defer until FIFO is stuffed). */
+       im_val =  MMCST0_RSPDNE | MMCST0_CRCRS | MMCST0_TOUTRS;
+       if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
+               im_val |= MMCST0_DATDNE | MMCST0_CRCWR;
+
+               if (!host->do_dma)
+                       im_val |= MMCST0_DXRDY;
+       } else if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
+               im_val |= MMCST0_DATDNE | MMCST0_CRCRD | MMCST0_TOUTRD;
+
+               if (!host->do_dma)
+                       im_val |= MMCST0_DRRDY;
+       }
+
+       /*
+        * Before non-DMA WRITE commands the controller needs priming:
+        * FIFO should be populated with 32 bytes i.e. whatever is the FIFO size
+        */
+       if (!host->do_dma && (host->data_dir == DAVINCI_MMC_DATADIR_WRITE))
+               davinci_fifo_data_trans(host, rw_threshold);
+
+       writel(cmd->arg, host->base + DAVINCI_MMCARGHL);
+       writel(cmd_reg,  host->base + DAVINCI_MMCCMD);
+       writel(im_val, host->base + DAVINCI_MMCIM);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* DMA infrastructure */
+
+static void davinci_abort_dma(struct mmc_davinci_host *host)
+{
+       int sync_dev;
+
+       if (host->data_dir == DAVINCI_MMC_DATADIR_READ)
+               sync_dev = host->rxdma;
+       else
+               sync_dev = host->txdma;
+
+       edma_stop(sync_dev);
+       edma_clean_channel(sync_dev);
+}
+
+static void
+mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data);
+
+static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data)
+{
+       if (DMA_COMPLETE != ch_status) {
+               struct mmc_davinci_host *host = data;
+
+               /* Currently means:  DMA Event Missed, or "null" transfer
+                * request was seen.  In the future, TC errors (like bad
+                * addresses) might be presented too.
+                */
+               dev_warn(mmc_dev(host->mmc), "DMA %s error\n",
+                       (host->data->flags & MMC_DATA_WRITE)
+                               ? "write" : "read");
+               host->data->error = -EIO;
+               mmc_davinci_xfer_done(host, host->data);
+       }
+}
+
+/* Set up tx or rx template, to be modified and updated later */
+static void __init mmc_davinci_dma_setup(struct mmc_davinci_host *host,
+               bool tx, struct edmacc_param *template)
+{
+       unsigned        sync_dev;
+       const u16       acnt = 4;
+       const u16       bcnt = rw_threshold >> 2;
+       const u16       ccnt = 0;
+       u32             src_port = 0;
+       u32             dst_port = 0;
+       s16             src_bidx, dst_bidx;
+       s16             src_cidx, dst_cidx;
+
+       /*
+        * A-B Sync transfer:  each DMA request is for one "frame" of
+        * rw_threshold bytes, broken into "acnt"-size chunks repeated
+        * "bcnt" times.  Each segment needs "ccnt" such frames; since
+        * we tell the block layer our mmc->max_seg_size limit, we can
+        * trust (later) that it's within bounds.
+        *
+        * The FIFOs are read/written in 4-byte chunks (acnt == 4) and
+        * EDMA will optimize memory operations to use larger bursts.
+        */
+       if (tx) {
+               sync_dev = host->txdma;
+
+               /* src_prt, ccnt, and link to be set up later */
+               src_bidx = acnt;
+               src_cidx = acnt * bcnt;
+
+               dst_port = host->mem_res->start + DAVINCI_MMCDXR;
+               dst_bidx = 0;
+               dst_cidx = 0;
+       } else {
+               sync_dev = host->rxdma;
+
+               src_port = host->mem_res->start + DAVINCI_MMCDRR;
+               src_bidx = 0;
+               src_cidx = 0;
+
+               /* dst_prt, ccnt, and link to be set up later */
+               dst_bidx = acnt;
+               dst_cidx = acnt * bcnt;
+       }
+
+       /*
+        * We can't use FIFO mode for the FIFOs because MMC FIFO addresses
+        * are not 256-bit (32-byte) aligned.  So we use INCR, and the W8BIT
+        * parameter is ignored.
+        */
+       edma_set_src(sync_dev, src_port, INCR, W8BIT);
+       edma_set_dest(sync_dev, dst_port, INCR, W8BIT);
+
+       edma_set_src_index(sync_dev, src_bidx, src_cidx);
+       edma_set_dest_index(sync_dev, dst_bidx, dst_cidx);
+
+       edma_set_transfer_params(sync_dev, acnt, bcnt, ccnt, 8, ABSYNC);
+
+       edma_read_slot(sync_dev, template);
+
+       /* don't bother with irqs or chaining */
+       template->opt |= EDMA_CHAN_SLOT(sync_dev) << 12;
+}
+
+static void mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
+               struct mmc_data *data)
+{
+       struct edmacc_param     *template;
+       int                     channel, slot;
+       unsigned                link;
+       struct scatterlist      *sg;
+       unsigned                sg_len;
+       unsigned                bytes_left = host->bytes_left;
+       const unsigned          shift = ffs(rw_threshold) - 1;;
+
+       if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
+               template = &host->tx_template;
+               channel = host->txdma;
+       } else {
+               template = &host->rx_template;
+               channel = host->rxdma;
+       }
+
+       /* We know sg_len and ccnt will never be out of range because
+        * we told the mmc layer which in turn tells the block layer
+        * to ensure that it only hands us one scatterlist segment
+        * per EDMA PARAM entry.  Update the PARAM
+        * entries needed for each segment of this scatterlist.
+        */
+       for (slot = channel, link = 0, sg = data->sg, sg_len = host->sg_len;
+                       sg_len-- != 0 && bytes_left;
+                       sg = sg_next(sg), slot = host->links[link++]) {
+               u32             buf = sg_dma_address(sg);
+               unsigned        count = sg_dma_len(sg);
+
+               template->link_bcntrld = sg_len
+                               ? (EDMA_CHAN_SLOT(host->links[link]) << 5)
+                               : 0xffff;
+
+               if (count > bytes_left)
+                       count = bytes_left;
+               bytes_left -= count;
+
+               if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
+                       template->src = buf;
+               else
+                       template->dst = buf;
+               template->ccnt = count >> shift;
+
+               edma_write_slot(slot, template);
+       }
+
+       if (host->version == MMC_CTLR_VERSION_2)
+               edma_clear_event(channel);
+
+       edma_start(channel);
+}
+
+static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
+               struct mmc_data *data)
+{
+       int i;
+       int mask = rw_threshold - 1;
+
+       host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+                               ((data->flags & MMC_DATA_WRITE)
+                               ? DMA_TO_DEVICE
+                               : DMA_FROM_DEVICE));
+
+       /* no individual DMA segment should need a partial FIFO */
+       for (i = 0; i < host->sg_len; i++) {
+               if (sg_dma_len(data->sg + i) & mask) {
+                       dma_unmap_sg(mmc_dev(host->mmc),
+                                       data->sg, data->sg_len,
+                                       (data->flags & MMC_DATA_WRITE)
+                                       ? DMA_TO_DEVICE
+                                       : DMA_FROM_DEVICE);
+                       return -1;
+               }
+       }
+
+       host->do_dma = 1;
+       mmc_davinci_send_dma_request(host, data);
+
+       return 0;
+}
+
+static void __init_or_module
+davinci_release_dma_channels(struct mmc_davinci_host *host)
+{
+       unsigned        i;
+
+       if (!host->use_dma)
+               return;
+
+       for (i = 0; i < host->n_link; i++)
+               edma_free_slot(host->links[i]);
+
+       edma_free_channel(host->txdma);
+       edma_free_channel(host->rxdma);
+}
+
+static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
+{
+       int r, i;
+
+       /* Acquire master DMA write channel */
+       r = edma_alloc_channel(host->txdma, mmc_davinci_dma_cb, host,
+                       EVENTQ_DEFAULT);
+       if (r < 0) {
+               dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
+                               "tx", r);
+               return r;
+       }
+       mmc_davinci_dma_setup(host, true, &host->tx_template);
+
+       /* Acquire master DMA read channel */
+       r = edma_alloc_channel(host->rxdma, mmc_davinci_dma_cb, host,
+                       EVENTQ_DEFAULT);
+       if (r < 0) {
+               dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
+                               "rx", r);
+               goto free_master_write;
+       }
+       mmc_davinci_dma_setup(host, false, &host->rx_template);
+
+       /* Allocate parameter RAM slots, which will later be bound to a
+        * channel as needed to handle a scatterlist.
+        */
+       for (i = 0; i < ARRAY_SIZE(host->links); i++) {
+               r = edma_alloc_slot(EDMA_CTLR(host->txdma), EDMA_SLOT_ANY);
+               if (r < 0) {
+                       dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n",
+                               r);
+                       break;
+               }
+               host->links[i] = r;
+       }
+       host->n_link = i;
+
+       return 0;
+
+free_master_write:
+       edma_free_channel(host->txdma);
+
+       return r;
+}
+
+/*----------------------------------------------------------------------*/
+
+static void
+mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
+{
+       int fifo_lev = (rw_threshold == 32) ? MMCFIFOCTL_FIFOLEV : 0;
+       int timeout;
+       struct mmc_data *data = req->data;
+
+       if (host->version == MMC_CTLR_VERSION_2)
+               fifo_lev = (rw_threshold == 64) ? MMCFIFOCTL_FIFOLEV : 0;
+
+       host->data = data;
+       if (data == NULL) {
+               host->data_dir = DAVINCI_MMC_DATADIR_NONE;
+               writel(0, host->base + DAVINCI_MMCBLEN);
+               writel(0, host->base + DAVINCI_MMCNBLK);
+               return;
+       }
+
+       dev_dbg(mmc_dev(host->mmc), "%s %s, %d blocks of %d bytes\n",
+               (data->flags & MMC_DATA_STREAM) ? "stream" : "block",
+               (data->flags & MMC_DATA_WRITE) ? "write" : "read",
+               data->blocks, data->blksz);
+       dev_dbg(mmc_dev(host->mmc), "  DTO %d cycles + %d ns\n",
+               data->timeout_clks, data->timeout_ns);
+       timeout = data->timeout_clks +
+               (data->timeout_ns / host->ns_in_one_cycle);
+       if (timeout > 0xffff)
+               timeout = 0xffff;
+
+       writel(timeout, host->base + DAVINCI_MMCTOD);
+       writel(data->blocks, host->base + DAVINCI_MMCNBLK);
+       writel(data->blksz, host->base + DAVINCI_MMCBLEN);
+
+       /* Configure the FIFO */
+       switch (data->flags & MMC_DATA_WRITE) {
+       case MMC_DATA_WRITE:
+               host->data_dir = DAVINCI_MMC_DATADIR_WRITE;
+               writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST,
+                       host->base + DAVINCI_MMCFIFOCTL);
+               writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR,
+                       host->base + DAVINCI_MMCFIFOCTL);
+               break;
+
+       default:
+               host->data_dir = DAVINCI_MMC_DATADIR_READ;
+               writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST,
+                       host->base + DAVINCI_MMCFIFOCTL);
+               writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD,
+                       host->base + DAVINCI_MMCFIFOCTL);
+               break;
+       }
+
+       host->buffer = NULL;
+       host->bytes_left = data->blocks * data->blksz;
+
+       /* For now we try to use DMA whenever we won't need partial FIFO
+        * reads or writes, either for the whole transfer (as tested here)
+        * or for any individual scatterlist segment (tested when we call
+        * start_dma_transfer).
+        *
+        * While we *could* change that, unusual block sizes are rarely
+        * used.  The occasional fallback to PIO should't hurt.
+        */
+       if (host->use_dma && (host->bytes_left & (rw_threshold - 1)) == 0
+                       && mmc_davinci_start_dma_transfer(host, data) == 0) {
+               /* zero this to ensure we take no PIO paths */
+               host->bytes_left = 0;
+       } else {
+               /* Revert to CPU Copy */
+               host->sg_len = data->sg_len;
+               host->sg = host->data->sg;
+               mmc_davinci_sg_to_buf(host);
+       }
+}
+
+static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+       struct mmc_davinci_host *host = mmc_priv(mmc);
+       unsigned long timeout = jiffies + msecs_to_jiffies(900);
+       u32 mmcst1 = 0;
+
+       /* Card may still be sending BUSY after a previous operation,
+        * typically some kind of write.  If so, we can't proceed yet.
+        */
+       while (time_before(jiffies, timeout)) {
+               mmcst1  = readl(host->base + DAVINCI_MMCST1);
+               if (!(mmcst1 & MMCST1_BUSY))
+                       break;
+               cpu_relax();
+       }
+       if (mmcst1 & MMCST1_BUSY) {
+               dev_err(mmc_dev(host->mmc), "still BUSY? bad ... \n");
+               req->cmd->error = -ETIMEDOUT;
+               mmc_request_done(mmc, req);
+               return;
+       }
+
+       host->do_dma = 0;
+       mmc_davinci_prepare_data(host, req);
+       mmc_davinci_start_command(host, req->cmd);
+}
+
+static unsigned int calculate_freq_for_card(struct mmc_davinci_host *host,
+       unsigned int mmc_req_freq)
+{
+       unsigned int mmc_freq = 0, mmc_pclk = 0, mmc_push_pull_divisor = 0;
+
+       mmc_pclk = host->mmc_input_clk;
+       if (mmc_req_freq && mmc_pclk > (2 * mmc_req_freq))
+               mmc_push_pull_divisor = ((unsigned int)mmc_pclk
+                               / (2 * mmc_req_freq)) - 1;
+       else
+               mmc_push_pull_divisor = 0;
+
+       mmc_freq = (unsigned int)mmc_pclk
+               / (2 * (mmc_push_pull_divisor + 1));
+
+       if (mmc_freq > mmc_req_freq)
+               mmc_push_pull_divisor = mmc_push_pull_divisor + 1;
+       /* Convert ns to clock cycles */
+       if (mmc_req_freq <= 400000)
+               host->ns_in_one_cycle = (1000000) / (((mmc_pclk
+                               / (2 * (mmc_push_pull_divisor + 1)))/1000));
+       else
+               host->ns_in_one_cycle = (1000000) / (((mmc_pclk
+                               / (2 * (mmc_push_pull_divisor + 1)))/1000000));
+
+       return mmc_push_pull_divisor;
+}
+
+static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       unsigned int open_drain_freq = 0, mmc_pclk = 0;
+       unsigned int mmc_push_pull_freq = 0;
+       struct mmc_davinci_host *host = mmc_priv(mmc);
+
+       if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
+               u32 temp;
+
+               /* Ignoring the init clock value passed for fixing the inter
+                * operability with different cards.
+                */
+               open_drain_freq = ((unsigned int)mmc_pclk
+                               / (2 * MMCSD_INIT_CLOCK)) - 1;
+
+               if (open_drain_freq > 0xFF)
+                       open_drain_freq = 0xFF;
+
+               temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKRT_MASK;
+               temp |= open_drain_freq;
+               writel(temp, host->base + DAVINCI_MMCCLK);
+
+               /* Convert ns to clock cycles */
+               host->ns_in_one_cycle = (1000000) / (MMCSD_INIT_CLOCK/1000);
+       } else {
+               u32 temp;
+               mmc_push_pull_freq = calculate_freq_for_card(host, ios->clock);
+
+               if (mmc_push_pull_freq > 0xFF)
+                       mmc_push_pull_freq = 0xFF;
+
+               temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKEN;
+               writel(temp, host->base + DAVINCI_MMCCLK);
+
+               udelay(10);
+
+               temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKRT_MASK;
+               temp |= mmc_push_pull_freq;
+               writel(temp, host->base + DAVINCI_MMCCLK);
+
+               writel(temp | MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
+
+               udelay(10);
+       }
+}
+
+static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       unsigned int mmc_pclk = 0;
+       struct mmc_davinci_host *host = mmc_priv(mmc);
+
+       mmc_pclk = host->mmc_input_clk;
+       dev_dbg(mmc_dev(host->mmc),
+               "clock %dHz busmode %d powermode %d Vdd %04x\n",
+               ios->clock, ios->bus_mode, ios->power_mode,
+               ios->vdd);
+       if (ios->bus_width == MMC_BUS_WIDTH_4) {
+               dev_dbg(mmc_dev(host->mmc), "Enabling 4 bit mode\n");
+               writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_WIDTH_4_BIT,
+                       host->base + DAVINCI_MMCCTL);
+       } else {
+               dev_dbg(mmc_dev(host->mmc), "Disabling 4 bit mode\n");
+               writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_WIDTH_4_BIT,
+                       host->base + DAVINCI_MMCCTL);
+       }
+
+       calculate_clk_divider(mmc, ios);
+
+       host->bus_mode = ios->bus_mode;
+       if (ios->power_mode == MMC_POWER_UP) {
+               unsigned long timeout = jiffies + msecs_to_jiffies(50);
+               bool lose = true;
+
+               /* Send clock cycles, poll completion */
+               writel(0, host->base + DAVINCI_MMCARGHL);
+               writel(MMCCMD_INITCK, host->base + DAVINCI_MMCCMD);
+               while (time_before(jiffies, timeout)) {
+                       u32 tmp = readl(host->base + DAVINCI_MMCST0);
+
+                       if (tmp & MMCST0_RSPDNE) {
+                               lose = false;
+                               break;
+                       }
+                       cpu_relax();
+               }
+               if (lose)
+                       dev_warn(mmc_dev(host->mmc), "powerup timeout\n");
+       }
+
+       /* FIXME on power OFF, reset things ... */
+}
+
+static void
+mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
+{
+       host->data = NULL;
+
+       if (host->do_dma) {
+               davinci_abort_dma(host);
+
+               dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+                            (data->flags & MMC_DATA_WRITE)
+                            ? DMA_TO_DEVICE
+                            : DMA_FROM_DEVICE);
+               host->do_dma = false;
+       }
+       host->data_dir = DAVINCI_MMC_DATADIR_NONE;
+
+       if (!data->stop || (host->cmd && host->cmd->error)) {
+               mmc_request_done(host->mmc, data->mrq);
+               writel(0, host->base + DAVINCI_MMCIM);
+       } else
+               mmc_davinci_start_command(host, data->stop);
+}
+
+static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
+                                struct mmc_command *cmd)
+{
+       host->cmd = NULL;
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               if (cmd->flags & MMC_RSP_136) {
+                       /* response type 2 */
+                       cmd->resp[3] = readl(host->base + DAVINCI_MMCRSP01);
+                       cmd->resp[2] = readl(host->base + DAVINCI_MMCRSP23);
+                       cmd->resp[1] = readl(host->base + DAVINCI_MMCRSP45);
+                       cmd->resp[0] = readl(host->base + DAVINCI_MMCRSP67);
+               } else {
+                       /* response types 1, 1b, 3, 4, 5, 6 */
+                       cmd->resp[0] = readl(host->base + DAVINCI_MMCRSP67);
+               }
+       }
+
+       if (host->data == NULL || cmd->error) {
+               if (cmd->error == -ETIMEDOUT)
+                       cmd->mrq->cmd->retries = 0;
+               mmc_request_done(host->mmc, cmd->mrq);
+               writel(0, host->base + DAVINCI_MMCIM);
+       }
+}
+
+static void
+davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
+{
+       u32 temp;
+
+       /* reset command and data state machines */
+       temp = readl(host->base + DAVINCI_MMCCTL);
+       writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST,
+               host->base + DAVINCI_MMCCTL);
+
+       temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
+       udelay(10);
+       writel(temp, host->base + DAVINCI_MMCCTL);
+}
+
+static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
+{
+       struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
+       unsigned int status, qstatus;
+       int end_command = 0;
+       int end_transfer = 0;
+       struct mmc_data *data = host->data;
+
+       if (host->cmd == NULL && host->data == NULL) {
+               status = readl(host->base + DAVINCI_MMCST0);
+               dev_dbg(mmc_dev(host->mmc),
+                       "Spurious interrupt 0x%04x\n", status);
+               /* Disable the interrupt from mmcsd */
+               writel(0, host->base + DAVINCI_MMCIM);
+               return IRQ_NONE;
+       }
+
+       status = readl(host->base + DAVINCI_MMCST0);
+       qstatus = status;
+
+       /* handle FIFO first when using PIO for data.
+        * bytes_left will decrease to zero as I/O progress and status will
+        * read zero over iteration because this controller status
+        * register(MMCST0) reports any status only once and it is cleared
+        * by read. So, it is not unbouned loop even in the case of
+        * non-dma.
+        */
+       while (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) {
+               davinci_fifo_data_trans(host, rw_threshold);
+               status = readl(host->base + DAVINCI_MMCST0);
+               if (!status)
+                       break;
+               qstatus |= status;
+       }
+
+       if (qstatus & MMCST0_DATDNE) {
+               /* All blocks sent/received, and CRC checks passed */
+               if (data != NULL) {
+                       if ((host->do_dma == 0) && (host->bytes_left > 0)) {
+                               /* if datasize < rw_threshold
+                                * no RX ints are generated
+                                */
+                               davinci_fifo_data_trans(host, host->bytes_left);
+                       }
+                       end_transfer = 1;
+                       data->bytes_xfered = data->blocks * data->blksz;
+               } else {
+                       dev_err(mmc_dev(host->mmc),
+                                       "DATDNE with no host->data\n");
+               }
+       }
+
+       if (qstatus & MMCST0_TOUTRD) {
+               /* Read data timeout */
+               data->error = -ETIMEDOUT;
+               end_transfer = 1;
+
+               dev_dbg(mmc_dev(host->mmc),
+                       "read data timeout, status %x\n",
+                       qstatus);
+
+               davinci_abort_data(host, data);
+       }
+
+       if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
+               /* Data CRC error */
+               data->error = -EILSEQ;
+               end_transfer = 1;
+
+               /* NOTE:  this controller uses CRCWR to report both CRC
+                * errors and timeouts (on writes).  MMCDRSP values are
+                * only weakly documented, but 0x9f was clearly a timeout
+                * case and the two three-bit patterns in various SD specs
+                * (101, 010) aren't part of it ...
+                */
+               if (qstatus & MMCST0_CRCWR) {
+                       u32 temp = readb(host->base + DAVINCI_MMCDRSP);
+
+                       if (temp == 0x9f)
+                               data->error = -ETIMEDOUT;
+               }
+               dev_dbg(mmc_dev(host->mmc), "data %s %s error\n",
+                       (qstatus & MMCST0_CRCWR) ? "write" : "read",
+                       (data->error == -ETIMEDOUT) ? "timeout" : "CRC");
+
+               davinci_abort_data(host, data);
+       }
+
+       if (qstatus & MMCST0_TOUTRS) {
+               /* Command timeout */
+               if (host->cmd) {
+                       dev_dbg(mmc_dev(host->mmc),
+                               "CMD%d timeout, status %x\n",
+                               host->cmd->opcode, qstatus);
+                       host->cmd->error = -ETIMEDOUT;
+                       if (data) {
+                               end_transfer = 1;
+                               davinci_abort_data(host, data);
+                       } else
+                               end_command = 1;
+               }
+       }
+
+       if (qstatus & MMCST0_CRCRS) {
+               /* Command CRC error */
+               dev_dbg(mmc_dev(host->mmc), "Command CRC error\n");
+               if (host->cmd) {
+                       host->cmd->error = -EILSEQ;
+                       end_command = 1;
+               }
+       }
+
+       if (qstatus & MMCST0_RSPDNE) {
+               /* End of command phase */
+               end_command = (int) host->cmd;
+       }
+
+       if (end_command)
+               mmc_davinci_cmd_done(host, host->cmd);
+       if (end_transfer)
+               mmc_davinci_xfer_done(host, data);
+       return IRQ_HANDLED;
+}
+
+static int mmc_davinci_get_cd(struct mmc_host *mmc)
+{
+       struct platform_device *pdev = to_platform_device(mmc->parent);
+       struct davinci_mmc_config *config = pdev->dev.platform_data;
+
+       if (!config || !config->get_cd)
+               return -ENOSYS;
+       return config->get_cd(pdev->id);
+}
+
+static int mmc_davinci_get_ro(struct mmc_host *mmc)
+{
+       struct platform_device *pdev = to_platform_device(mmc->parent);
+       struct davinci_mmc_config *config = pdev->dev.platform_data;
+
+       if (!config || !config->get_ro)
+               return -ENOSYS;
+       return config->get_ro(pdev->id);
+}
+
+static struct mmc_host_ops mmc_davinci_ops = {
+       .request        = mmc_davinci_request,
+       .set_ios        = mmc_davinci_set_ios,
+       .get_cd         = mmc_davinci_get_cd,
+       .get_ro         = mmc_davinci_get_ro,
+};
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_CPU_FREQ
+static int mmc_davinci_cpufreq_transition(struct notifier_block *nb,
+                                    unsigned long val, void *data)
+{
+       struct mmc_davinci_host *host;
+       unsigned int mmc_pclk;
+       struct mmc_host *mmc;
+       unsigned long flags;
+
+       host = container_of(nb, struct mmc_davinci_host, freq_transition);
+       mmc = host->mmc;
+       mmc_pclk = clk_get_rate(host->clk);
+
+       if (val == CPUFREQ_POSTCHANGE) {
+               spin_lock_irqsave(&mmc->lock, flags);
+               host->mmc_input_clk = mmc_pclk;
+               calculate_clk_divider(mmc, &mmc->ios);
+               spin_unlock_irqrestore(&mmc->lock, flags);
+       }
+
+       return 0;
+}
+
+static inline int mmc_davinci_cpufreq_register(struct mmc_davinci_host *host)
+{
+       host->freq_transition.notifier_call = mmc_davinci_cpufreq_transition;
+
+       return cpufreq_register_notifier(&host->freq_transition,
+                                        CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host)
+{
+       cpufreq_unregister_notifier(&host->freq_transition,
+                                   CPUFREQ_TRANSITION_NOTIFIER);
+}
+#else
+static inline int mmc_davinci_cpufreq_register(struct mmc_davinci_host *host)
+{
+       return 0;
+}
+
+static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host)
+{
+}
+#endif
+static void __init init_mmcsd_host(struct mmc_davinci_host *host)
+{
+       /* DAT line portion is diabled and in reset state */
+       writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_DATRST,
+               host->base + DAVINCI_MMCCTL);
+
+       /* CMD line portion is diabled and in reset state */
+       writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_CMDRST,
+               host->base + DAVINCI_MMCCTL);
+
+       udelay(10);
+
+       writel(0, host->base + DAVINCI_MMCCLK);
+       writel(MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
+
+       writel(0x1FFF, host->base + DAVINCI_MMCTOR);
+       writel(0xFFFF, host->base + DAVINCI_MMCTOD);
+
+       writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_DATRST,
+               host->base + DAVINCI_MMCCTL);
+       writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_CMDRST,
+               host->base + DAVINCI_MMCCTL);
+
+       udelay(10);
+}
+
+static int __init davinci_mmcsd_probe(struct platform_device *pdev)
+{
+       struct davinci_mmc_config *pdata = pdev->dev.platform_data;
+       struct mmc_davinci_host *host = NULL;
+       struct mmc_host *mmc = NULL;
+       struct resource *r, *mem = NULL;
+       int ret = 0, irq = 0;
+       size_t mem_size;
+
+       /* REVISIT:  when we're fully converted, fail if pdata is NULL */
+
+       ret = -ENODEV;
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq = platform_get_irq(pdev, 0);
+       if (!r || irq == NO_IRQ)
+               goto out;
+
+       ret = -EBUSY;
+       mem_size = resource_size(r);
+       mem = request_mem_region(r->start, mem_size, pdev->name);
+       if (!mem)
+               goto out;
+
+       ret = -ENOMEM;
+       mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev);
+       if (!mmc)
+               goto out;
+
+       host = mmc_priv(mmc);
+       host->mmc = mmc;        /* Important */
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r)
+               goto out;
+       host->rxdma = r->start;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!r)
+               goto out;
+       host->txdma = r->start;
+
+       host->mem_res = mem;
+       host->base = ioremap(mem->start, mem_size);
+       if (!host->base)
+               goto out;
+
+       ret = -ENXIO;
+       host->clk = clk_get(&pdev->dev, "MMCSDCLK");
+       if (IS_ERR(host->clk)) {
+               ret = PTR_ERR(host->clk);
+               goto out;
+       }
+       clk_enable(host->clk);
+       host->mmc_input_clk = clk_get_rate(host->clk);
+
+       init_mmcsd_host(host);
+
+       host->use_dma = use_dma;
+       host->irq = irq;
+
+       if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
+               host->use_dma = 0;
+
+       /* REVISIT:  someday, support IRQ-driven card detection.  */
+       mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+       if (!pdata || pdata->wires == 4 || pdata->wires == 0)
+               mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+       host->version = pdata->version;
+
+       mmc->ops = &mmc_davinci_ops;
+       mmc->f_min = 312500;
+       mmc->f_max = 25000000;
+       if (pdata && pdata->max_freq)
+               mmc->f_max = pdata->max_freq;
+       if (pdata && pdata->caps)
+               mmc->caps |= pdata->caps;
+       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+       /* With no iommu coalescing pages, each phys_seg is a hw_seg.
+        * Each hw_seg uses one EDMA parameter RAM slot, always one
+        * channel and then usually some linked slots.
+        */
+       mmc->max_hw_segs        = 1 + host->n_link;
+       mmc->max_phys_segs      = mmc->max_hw_segs;
+
+       /* EDMA limit per hw segment (one or two MBytes) */
+       mmc->max_seg_size       = MAX_CCNT * rw_threshold;
+
+       /* MMC/SD controller limits for multiblock requests */
+       mmc->max_blk_size       = 4095;  /* BLEN is 12 bits */
+       mmc->max_blk_count      = 65535; /* NBLK is 16 bits */
+       mmc->max_req_size       = mmc->max_blk_size * mmc->max_blk_count;
+
+       dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs);
+       dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs);
+       dev_dbg(mmc_dev(host->mmc), "max_blk_size=%d\n", mmc->max_blk_size);
+       dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size);
+       dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size);
+
+       platform_set_drvdata(pdev, host);
+
+       ret = mmc_davinci_cpufreq_register(host);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register cpufreq\n");
+               goto cpu_freq_fail;
+       }
+
+       ret = mmc_add_host(mmc);
+       if (ret < 0)
+               goto out;
+
+       ret = request_irq(irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host);
+       if (ret)
+               goto out;
+
+       rename_region(mem, mmc_hostname(mmc));
+
+       dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
+               host->use_dma ? "DMA" : "PIO",
+               (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
+
+       return 0;
+
+out:
+       mmc_davinci_cpufreq_deregister(host);
+cpu_freq_fail:
+       if (host) {
+               davinci_release_dma_channels(host);
+
+               if (host->clk) {
+                       clk_disable(host->clk);
+                       clk_put(host->clk);
+               }
+
+               if (host->base)
+                       iounmap(host->base);
+       }
+
+       if (mmc)
+               mmc_free_host(mmc);
+
+       if (mem)
+               release_resource(mem);
+
+       dev_dbg(&pdev->dev, "probe err %d\n", ret);
+
+       return ret;
+}
+
+static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
+{
+       struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+       if (host) {
+               mmc_davinci_cpufreq_deregister(host);
+
+               mmc_remove_host(host->mmc);
+               free_irq(host->irq, host);
+
+               davinci_release_dma_channels(host);
+
+               clk_disable(host->clk);
+               clk_put(host->clk);
+
+               iounmap(host->base);
+
+               release_resource(host->mem_res);
+
+               mmc_free_host(host->mmc);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int davinci_mmcsd_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+       struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+
+       return mmc_suspend_host(host->mmc, msg);
+}
+
+static int davinci_mmcsd_resume(struct platform_device *pdev)
+{
+       struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+
+       return mmc_resume_host(host->mmc);
+}
+#else
+#define davinci_mmcsd_suspend  NULL
+#define davinci_mmcsd_resume   NULL
+#endif
+
+static struct platform_driver davinci_mmcsd_driver = {
+       .driver         = {
+               .name   = "davinci_mmc",
+               .owner  = THIS_MODULE,
+       },
+       .remove         = __exit_p(davinci_mmcsd_remove),
+       .suspend        = davinci_mmcsd_suspend,
+       .resume         = davinci_mmcsd_resume,
+};
+
+static int __init davinci_mmcsd_init(void)
+{
+       return platform_driver_probe(&davinci_mmcsd_driver,
+                                    davinci_mmcsd_probe);
+}
+module_init(davinci_mmcsd_init);
+
+static void __exit davinci_mmcsd_exit(void)
+{
+       platform_driver_unregister(&davinci_mmcsd_driver);
+}
+module_exit(davinci_mmcsd_exit);
+
+MODULE_AUTHOR("Texas Instruments India");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MMC/SD driver for Davinci MMC controller");
+
index 88671529c45d7690061f289d8fe005abd4701a09..60a2b69e54f569e6336c8efa2c25f8c05697a192 100644 (file)
@@ -679,17 +679,17 @@ static int mxcmci_probe(struct platform_device *pdev)
 {
        struct mmc_host *mmc;
        struct mxcmci_host *host = NULL;
-       struct resource *r;
+       struct resource *iores, *r;
        int ret = 0, irq;
 
        printk(KERN_INFO "i.MX SDHC driver\n");
 
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq = platform_get_irq(pdev, 0);
-       if (!r || irq < 0)
+       if (!iores || irq < 0)
                return -EINVAL;
 
-       r = request_mem_region(r->start, resource_size(r), pdev->name);
+       r = request_mem_region(iores->start, resource_size(iores), pdev->name);
        if (!r)
                return -EBUSY;
 
@@ -809,7 +809,7 @@ out_iounmap:
 out_free:
        mmc_free_host(mmc);
 out_release_mem:
-       release_mem_region(host->res->start, resource_size(host->res));
+       release_mem_region(iores->start, resource_size(iores));
        return ret;
 }
 
index 5f970e253e500f2709dea4e04bb3b45565f5c39a..c6d7e8ecadbf586691d484cdbf7b94b7f4550913 100644 (file)
@@ -1459,8 +1459,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
                goto err_ioremap;
 
        host->iclk = clk_get(&pdev->dev, "ick");
-       if (IS_ERR(host->iclk))
+       if (IS_ERR(host->iclk)) {
+               ret = PTR_ERR(host->iclk);
                goto err_free_mmc_host;
+       }
        clk_enable(host->iclk);
 
        host->fclk = clk_get(&pdev->dev, "fck");
@@ -1500,10 +1502,8 @@ err_free_irq:
 err_free_fclk:
        clk_put(host->fclk);
 err_free_iclk:
-       if (host->iclk != NULL) {
-               clk_disable(host->iclk);
-               clk_put(host->iclk);
-       }
+       clk_disable(host->iclk);
+       clk_put(host->iclk);
 err_free_mmc_host:
        iounmap(host->virt_base);
 err_ioremap:
index bb47ff465c04d30acaeb1fc2a0463b785bb63917..0d783f3e79edf609f2297c7eaab42240d678feb0 100644 (file)
@@ -828,7 +828,7 @@ static int pxamci_resume(struct device *dev)
        return ret;
 }
 
-static struct dev_pm_ops pxamci_pm_ops = {
+static const struct dev_pm_ops pxamci_pm_ops = {
        .suspend        = pxamci_suspend,
        .resume         = pxamci_resume,
 };
index 941a4d35ef8da44cdc627bee9dde0a06a9b0455e..d96e1abf2d64a77fae5a52b0c82e9db8c697ecfc 100644 (file)
@@ -820,7 +820,7 @@ fail_request:
 static void finalize_request(struct s3cmci_host *host)
 {
        struct mmc_request *mrq = host->mrq;
-       struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+       struct mmc_command *cmd;
        int debug_as_failure = 0;
 
        if (host->complete_what != COMPLETION_FINALIZE)
@@ -828,6 +828,7 @@ static void finalize_request(struct s3cmci_host *host)
 
        if (!mrq)
                return;
+       cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
 
        if (cmd->data && (cmd->error == 0) &&
            (cmd->data->error == 0)) {
@@ -1302,10 +1303,8 @@ static int s3cmci_get_ro(struct mmc_host *mmc)
        if (pdata->no_wprotect)
                return 0;
 
-       ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
-
-       if (pdata->wprotect_invert)
-               ret = !ret;
+       ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;
+       ret ^= pdata->wprotect_invert;
 
        return ret;
 }
@@ -1654,7 +1653,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
                        goto probe_free_irq;
                }
 
-               host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
+               host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);
 
                if (host->irq_cd >= 0) {
                        if (request_irq(host->irq_cd, s3cmci_irq_cd,
@@ -1892,7 +1891,7 @@ static int s3cmci_resume(struct device *dev)
        return mmc_resume_host(mmc);
 }
 
-static struct dev_pm_ops s3cmci_pm = {
+static const struct dev_pm_ops s3cmci_pm = {
        .suspend        = s3cmci_suspend,
        .resume         = s3cmci_resume,
 };
index e0356644d1aa4592faa28ccccb485d5cab944087..5c3a1767770a2cb78d1c726abeaaec39a84113f7 100644 (file)
@@ -285,6 +285,73 @@ static const struct sdhci_pci_fixes sdhci_jmicron = {
        .resume         = jmicron_resume,
 };
 
+/* SysKonnect CardBus2SDIO extra registers */
+#define SYSKT_CTRL             0x200
+#define SYSKT_RDFIFO_STAT      0x204
+#define SYSKT_WRFIFO_STAT      0x208
+#define SYSKT_POWER_DATA       0x20c
+#define   SYSKT_POWER_330      0xef
+#define   SYSKT_POWER_300      0xf8
+#define   SYSKT_POWER_184      0xcc
+#define SYSKT_POWER_CMD                0x20d
+#define   SYSKT_POWER_START    (1 << 7)
+#define SYSKT_POWER_STATUS     0x20e
+#define   SYSKT_POWER_STATUS_OK        (1 << 0)
+#define SYSKT_BOARD_REV                0x210
+#define SYSKT_CHIP_REV         0x211
+#define SYSKT_CONF_DATA                0x212
+#define   SYSKT_CONF_DATA_1V8  (1 << 2)
+#define   SYSKT_CONF_DATA_2V5  (1 << 1)
+#define   SYSKT_CONF_DATA_3V3  (1 << 0)
+
+static int syskt_probe(struct sdhci_pci_chip *chip)
+{
+       if ((chip->pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
+               chip->pdev->class &= ~0x0000FF;
+               chip->pdev->class |= PCI_SDHCI_IFDMA;
+       }
+       return 0;
+}
+
+static int syskt_probe_slot(struct sdhci_pci_slot *slot)
+{
+       int tm, ps;
+
+       u8 board_rev = readb(slot->host->ioaddr + SYSKT_BOARD_REV);
+       u8  chip_rev = readb(slot->host->ioaddr + SYSKT_CHIP_REV);
+       dev_info(&slot->chip->pdev->dev, "SysKonnect CardBus2SDIO, "
+                                        "board rev %d.%d, chip rev %d.%d\n",
+                                        board_rev >> 4, board_rev & 0xf,
+                                        chip_rev >> 4,  chip_rev & 0xf);
+       if (chip_rev >= 0x20)
+               slot->host->quirks |= SDHCI_QUIRK_FORCE_DMA;
+
+       writeb(SYSKT_POWER_330, slot->host->ioaddr + SYSKT_POWER_DATA);
+       writeb(SYSKT_POWER_START, slot->host->ioaddr + SYSKT_POWER_CMD);
+       udelay(50);
+       tm = 10;  /* Wait max 1 ms */
+       do {
+               ps = readw(slot->host->ioaddr + SYSKT_POWER_STATUS);
+               if (ps & SYSKT_POWER_STATUS_OK)
+                       break;
+               udelay(100);
+       } while (--tm);
+       if (!tm) {
+               dev_err(&slot->chip->pdev->dev,
+                       "power regulator never stabilized");
+               writeb(0, slot->host->ioaddr + SYSKT_POWER_CMD);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_syskt = {
+       .quirks         = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER,
+       .probe          = syskt_probe,
+       .probe_slot     = syskt_probe_slot,
+};
+
 static int via_probe(struct sdhci_pci_chip *chip)
 {
        if (chip->pdev->revision == 0x10)
@@ -362,6 +429,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
                .driver_data    = (kernel_ulong_t)&sdhci_jmicron,
        },
 
+       {
+               .vendor         = PCI_VENDOR_ID_SYSKONNECT,
+               .device         = 0x8000,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_syskt,
+       },
+
        {
                .vendor         = PCI_VENDOR_ID_VIA,
                .device         = 0x95d0,
index 91991b460c4547c7a90a583ca987f087c6babd53..7cccc852374772d4d2479e4eb9f681a5bdb74bb5 100644 (file)
@@ -591,7 +591,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
        disable_mmc_irqs(host, TMIO_MASK_ALL);
 
        ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED |
-               IRQF_TRIGGER_FALLING, "tmio-mmc", host);
+               IRQF_TRIGGER_FALLING, dev_name(&dev->dev), host);
        if (ret)
                goto unmap_cnf;
 
index 7c302d55910e6dfb8f63bcf7ac4f4a4027423180..66123419f65d4561f4912a60e68d499d9e0406e7 100644 (file)
@@ -216,7 +216,7 @@ static int nomadik_nand_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops nomadik_nand_pm_ops = {
+static const struct dev_pm_ops nomadik_nand_pm_ops = {
        .suspend = nomadik_nand_suspend,
        .resume = nomadik_nand_resume,
 };
index 78b7167a8ce36f64e07a4f7fea6703e76d27d3df..39db0e96815dfba17d6dc8cab444a8181c06da71 100644 (file)
@@ -837,7 +837,7 @@ static int vortex_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops vortex_pm_ops = {
+static const struct dev_pm_ops vortex_pm_ops = {
        .suspend = vortex_suspend,
        .resume = vortex_resume,
        .freeze = vortex_suspend,
index 0cbe3c0e7c065b181ebaa725e5f18e6711c454f8..b377300656889cd9db9a2242bcc213611798cd91 100644 (file)
@@ -1646,7 +1646,7 @@ dm9000_drv_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops dm9000_drv_pm_ops = {
+static const struct dev_pm_ops dm9000_drv_pm_ops = {
        .suspend        = dm9000_drv_suspend,
        .resume         = dm9000_drv_resume,
 };
index acfc5a3aa490b5dbe3cb9e638803946944ef0abb..60f96c468a2465e3f3b9c7d3634b4f521d153216 100644 (file)
@@ -4859,7 +4859,7 @@ out:
        return 0;
 }
 
-static struct dev_pm_ops rtl8169_pm_ops = {
+static const struct dev_pm_ops rtl8169_pm_ops = {
        .suspend = rtl8169_suspend,
        .resume = rtl8169_resume,
        .freeze = rtl8169_suspend,
index 20d6095cf4118911b6586cbf13dd85bdaaae931e..494cd91ea39c5956102eb4afee1808295d2c5d18 100644 (file)
@@ -2154,7 +2154,7 @@ static int smsc911x_resume(struct device *dev)
        return (to == 0) ? -EIO : 0;
 }
 
-static struct dev_pm_ops smsc911x_pm_ops = {
+static const struct dev_pm_ops smsc911x_pm_ops = {
        .suspend        = smsc911x_suspend,
        .resume         = smsc911x_resume,
 };
index 1ceb9d0f8b9720309beceb4a2f62581a9bf2e5f8..9cc438282d776c5e8e4d57d96f0267a15371403a 100644 (file)
@@ -2689,7 +2689,7 @@ vmxnet3_resume(struct device *device)
        return 0;
 }
 
-static struct dev_pm_ops vmxnet3_pm_ops = {
+static const struct dev_pm_ops vmxnet3_pm_ops = {
        .suspend = vmxnet3_suspend,
        .resume = vmxnet3_resume,
 };
index 13a64bc081b6762d873204c3ddd0cbe7c35d074c..0bc5d474b1688aa74b4dd96d9b2f016154ce065e 100644 (file)
@@ -779,12 +779,9 @@ static ssize_t pdcs_auto_write(struct kobject *kobj,
        read_unlock(&pathentry->rw_lock);
        
        DPRINTK("%s: flags before: 0x%X\n", __func__, flags);
-                       
-       temp = in;
-       
-       while (*temp && isspace(*temp))
-               temp++;
-       
+
+       temp = skip_spaces(in);
+
        c = *temp++ - '0';
        if ((c != 0) && (c != 1))
                goto parse_error;
index ce52ea34fee5513822ef903a8b27ab8d38dfb359..a49452e2aed9e932d82cd51a9570aaaf3f61c903 100644 (file)
@@ -43,7 +43,7 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev)
 }
 
 #ifdef CONFIG_PM
-static struct dev_pm_ops pcie_portdrv_pm_ops = {
+static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .suspend        = pcie_port_device_suspend,
        .resume         = pcie_port_device_resume,
        .freeze         = pcie_port_device_suspend,
index da346eb7e77eba91474027329794c94ceb8cd2b7..3aabf1e379888e7bf4c025d5d779862132efa007 100644 (file)
@@ -336,7 +336,7 @@ static int pxa2xx_drv_pcmcia_resume(struct device *dev)
        return pcmcia_socket_dev_resume(dev);
 }
 
-static struct dev_pm_ops  pxa2xx_drv_pcmcia_pm_ops = {
+static const struct dev_pm_ops pxa2xx_drv_pcmcia_pm_ops = {
        .suspend        = pxa2xx_drv_pcmcia_suspend,
        .resume         = pxa2xx_drv_pcmcia_resume,
 };
index fe02cfd4b5e905673fddb4f0d9e98391d7379ce2..e4d12acdd525d2a5acb1337f52d2c3a2fc441c9e 100644 (file)
@@ -1330,7 +1330,7 @@ static int yenta_dev_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops yenta_pm_ops = {
+static const struct dev_pm_ops yenta_pm_ops = {
        .suspend_noirq = yenta_dev_suspend_noirq,
        .resume_noirq = yenta_dev_resume_noirq,
        .resume = yenta_dev_resume,
index ab64522aaa6433df3e1fcb3c2d92c5633d2c72ad..be27aa47e8101f0d55c77d3b9ff8b3d9c4a788fa 100644 (file)
@@ -460,7 +460,7 @@ static int acerhdf_remove(struct platform_device *device)
        return 0;
 }
 
-static struct dev_pm_ops acerhdf_pm_ops = {
+static const struct dev_pm_ops acerhdf_pm_ops = {
        .suspend = acerhdf_suspend,
        .freeze  = acerhdf_suspend,
 };
index 4226e535273874fb06aea0344c4c8ce00a8b957c..e647a856b9bf684d9e57a88d4b3321edb770232b 100644 (file)
@@ -154,7 +154,7 @@ static struct eeepc_hotk *ehotk;
 static int eeepc_hotk_thaw(struct device *device);
 static int eeepc_hotk_restore(struct device *device);
 
-static struct dev_pm_ops eeepc_pm_ops = {
+static const struct dev_pm_ops eeepc_pm_ops = {
        .thaw = eeepc_hotk_thaw,
        .restore = eeepc_hotk_restore,
 };
index c2842171cec6b9942c3e844954842612d5db0d20..f00a71c58e69aea037c488e5cbaf03c34ff61a7e 100644 (file)
@@ -94,7 +94,7 @@ static struct rfkill *wifi_rfkill;
 static struct rfkill *bluetooth_rfkill;
 static struct rfkill *wwan_rfkill;
 
-static struct dev_pm_ops hp_wmi_pm_ops = {
+static const struct dev_pm_ops hp_wmi_pm_ops = {
        .resume  = hp_wmi_resume_handler,
        .restore  = hp_wmi_resume_handler,
 };
index 0ed84806f8ae26e24d48d334694c88ee2a465254..cf61d6a8ef6f6410f8a684410fc1f03a9669d0fe 100644 (file)
@@ -1006,11 +1006,8 @@ static int parse_strtoul(const char *buf,
 {
        char *endp;
 
-       while (*buf && isspace(*buf))
-               buf++;
-       *value = simple_strtoul(buf, &endp, 0);
-       while (*endp && isspace(*endp))
-               endp++;
+       *value = simple_strtoul(skip_spaces(buf), &endp, 0);
+       endp = skip_spaces(endp);
        if (*endp || *value > max)
                return -EINVAL;
 
index c3f1c8e9d2545a4859d984ed7237455ec8e8ca36..68b0c04987e4434c69a43d842b1bdc3c422481ad 100644 (file)
@@ -310,8 +310,7 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
                goto done;
        }
 
-       while (isspace(*buf))
-               ++buf;
+       buf = skip_spaces(buf);
        if (!strnicmp(buf, "disable", 7)) {
                retval = pnp_disable_dev(dev);
                goto done;
@@ -353,19 +352,13 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
                pnp_init_resources(dev);
                mutex_lock(&pnp_res_mutex);
                while (1) {
-                       while (isspace(*buf))
-                               ++buf;
+                       buf = skip_spaces(buf);
                        if (!strnicmp(buf, "io", 2)) {
-                               buf += 2;
-                               while (isspace(*buf))
-                                       ++buf;
+                               buf = skip_spaces(buf + 2);
                                start = simple_strtoul(buf, &buf, 0);
-                               while (isspace(*buf))
-                                       ++buf;
+                               buf = skip_spaces(buf);
                                if (*buf == '-') {
-                                       buf += 1;
-                                       while (isspace(*buf))
-                                               ++buf;
+                                       buf = skip_spaces(buf + 1);
                                        end = simple_strtoul(buf, &buf, 0);
                                } else
                                        end = start;
@@ -373,16 +366,11 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
                                continue;
                        }
                        if (!strnicmp(buf, "mem", 3)) {
-                               buf += 3;
-                               while (isspace(*buf))
-                                       ++buf;
+                               buf = skip_spaces(buf + 3);
                                start = simple_strtoul(buf, &buf, 0);
-                               while (isspace(*buf))
-                                       ++buf;
+                               buf = skip_spaces(buf);
                                if (*buf == '-') {
-                                       buf += 1;
-                                       while (isspace(*buf))
-                                               ++buf;
+                                       buf = skip_spaces(buf + 1);
                                        end = simple_strtoul(buf, &buf, 0);
                                } else
                                        end = start;
@@ -390,17 +378,13 @@ static ssize_t pnp_set_current_resources(struct device *dmdev,
                                continue;
                        }
                        if (!strnicmp(buf, "irq", 3)) {
-                               buf += 3;
-                               while (isspace(*buf))
-                                       ++buf;
+                               buf = skip_spaces(buf + 3);
                                start = simple_strtoul(buf, &buf, 0);
                                pnp_add_irq_resource(dev, start, 0);
                                continue;
                        }
                        if (!strnicmp(buf, "dma", 3)) {
-                               buf += 3;
-                               while (isspace(*buf))
-                                       ++buf;
+                               buf = skip_spaces(buf + 3);
                                start = simple_strtoul(buf, &buf, 0);
                                pnp_add_dma_resource(dev, start, 0);
                                continue;
index f2bfd296dbae05133134b02c05daf9df12da6fa0..fa39e759a275a2a49ef7a0ceb306c4eee9c5abbf 100644 (file)
@@ -157,7 +157,7 @@ static int wm97xx_bat_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops wm97xx_bat_pm_ops = {
+static const struct dev_pm_ops wm97xx_bat_pm_ops = {
        .suspend        = wm97xx_bat_suspend,
        .resume         = wm97xx_bat_resume,
 };
index 747ca194fad4ac2ec319eac087bda3396fa3f309..e6351b743da644cd78dd34b0cfd73b667e885cfe 100644 (file)
@@ -456,7 +456,7 @@ static int pxa_rtc_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops pxa_rtc_pm_ops = {
+static const struct dev_pm_ops pxa_rtc_pm_ops = {
        .suspend        = pxa_rtc_suspend,
        .resume         = pxa_rtc_resume,
 };
index 29f98a70586e4e677fb99373f24a62056852ea1f..e4a44b641702677ba99884f03089477f1c04f3cd 100644 (file)
@@ -407,7 +407,7 @@ static int sa1100_rtc_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sa1100_rtc_pm_ops = {
+static const struct dev_pm_ops sa1100_rtc_pm_ops = {
        .suspend        = sa1100_rtc_suspend,
        .resume         = sa1100_rtc_resume,
 };
index e6ed5404bca0e8d32d4623bd123c6d0b8e6a1b54..e95cc6f8d61e28c035fed8d0648fd92463ed595e 100644 (file)
@@ -826,7 +826,7 @@ static int sh_rtc_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sh_rtc_dev_pm_ops = {
+static const struct dev_pm_ops sh_rtc_dev_pm_ops = {
        .suspend = sh_rtc_suspend,
        .resume = sh_rtc_resume,
 };
index 79795cdf6ed81f9796ea914f21180f03694ac184..000c7e481e594ab8216b5c5d05098af188a9d876 100644 (file)
@@ -485,7 +485,7 @@ static int __devexit wm831x_rtc_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct dev_pm_ops wm831x_rtc_pm_ops = {
+static const struct dev_pm_ops wm831x_rtc_pm_ops = {
        .suspend = wm831x_rtc_suspend,
        .resume = wm831x_rtc_resume,
 
index 5f23eca8280429fe0f33b598fbceb1987d1467e9..6315fbd8e68bf0fe1d8bf557c7932adda91c752f 100644 (file)
@@ -14,6 +14,7 @@
 #define KMSG_COMPONENT "dasd"
 
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/seq_file.h>
 #include <linux/vmalloc.h>
 #include <linux/proc_fs.h>
@@ -272,10 +273,10 @@ dasd_statistics_write(struct file *file, const char __user *user_buf,
        DBF_EVENT(DBF_DEBUG, "/proc/dasd/statictics: '%s'\n", buffer);
 
        /* check for valid verbs */
-       for (str = buffer; isspace(*str); str++);
+       str = skip_spaces(buffer);
        if (strncmp(str, "set", 3) == 0 && isspace(str[3])) {
                /* 'set xxx' was given */
-               for (str = str + 4; isspace(*str); str++);
+               str = skip_spaces(str + 4);
                if (strcmp(str, "on") == 0) {
                        /* switch on statistics profiling */
                        dasd_profile_level = DASD_PROFILE_ON;
index f76f4bd82b9f9810e3bae0b4d914f1f8673803ab..9b43ae94beba1dea263e1687d38060e9a1f243d6 100644 (file)
@@ -1005,7 +1005,7 @@ static int dcssblk_thaw(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops dcssblk_pm_ops = {
+static const struct dev_pm_ops dcssblk_pm_ops = {
        .freeze         = dcssblk_freeze,
        .thaw           = dcssblk_thaw,
        .restore        = dcssblk_restore,
index 116d1b3eeb157093002ea32edbf7c45133dc695b..118de392af63895b98b42afc0123911f054168f5 100644 (file)
@@ -407,7 +407,7 @@ static int xpram_restore(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops xpram_pm_ops = {
+static const struct dev_pm_ops xpram_pm_ops = {
        .restore        = xpram_restore,
 };
 
index 60473f86e1f9271e6f12d1fcddcd85991d66df50..33e96484d54fd1bf5d28aeb369751eed9dbef539 100644 (file)
@@ -529,7 +529,7 @@ static int monreader_restore(struct device *dev)
        return monreader_thaw(dev);
 }
 
-static struct dev_pm_ops monreader_pm_ops = {
+static const struct dev_pm_ops monreader_pm_ops = {
        .freeze  = monreader_freeze,
        .thaw    = monreader_thaw,
        .restore = monreader_restore,
index 6532ed8b4afad0a75a2fef4037eb86c121e3f659..668a0579b26b8b7767d2fbc912c220d02d3a874b 100644 (file)
@@ -323,7 +323,7 @@ static int monwriter_thaw(struct device *dev)
        return monwriter_restore(dev);
 }
 
-static struct dev_pm_ops monwriter_pm_ops = {
+static const struct dev_pm_ops monwriter_pm_ops = {
        .freeze         = monwriter_freeze,
        .thaw           = monwriter_thaw,
        .restore        = monwriter_restore,
index a983f50867881643f06ad0b2364fc5b9cbf2bb04..ec88c59842e3532d1b3b6fde3a402a9101b18266 100644 (file)
@@ -1019,7 +1019,7 @@ static int sclp_restore(struct device *dev)
        return sclp_undo_suspend(SCLP_PM_EVENT_RESTORE);
 }
 
-static struct dev_pm_ops sclp_pm_ops = {
+static const struct dev_pm_ops sclp_pm_ops = {
        .freeze         = sclp_freeze,
        .thaw           = sclp_thaw,
        .restore        = sclp_restore,
index 28b5afc129c36be0cc2396a8f965aafef02fd108..b3beab610da445334be1818b4f8ae05e81e941ba 100644 (file)
@@ -547,7 +547,7 @@ struct read_storage_sccb {
        u32 entries[0];
 } __packed;
 
-static struct dev_pm_ops sclp_mem_pm_ops = {
+static const struct dev_pm_ops sclp_mem_pm_ops = {
        .freeze         = sclp_mem_freeze,
 };
 
index 899aa795bf38eef009cb08299b6a27b6d330fbe3..7dfa5412d5a8bc9d5c0b6ddcacffc6fe6d958981 100644 (file)
@@ -675,7 +675,7 @@ static int vmlogrdr_pm_prepare(struct device *dev)
 }
 
 
-static struct dev_pm_ops vmlogrdr_pm_ops = {
+static const struct dev_pm_ops vmlogrdr_pm_ops = {
        .prepare = vmlogrdr_pm_prepare,
 };
 
index a5a62f1f7747c66107c71ea62be654473765541f..5f97ea2ee6b19f8b6ca80ac7e70239ab2f1dca7e 100644 (file)
@@ -560,7 +560,7 @@ static int ccwgroup_pm_restore(struct device *dev)
        return gdrv->restore ? gdrv->restore(gdev) : 0;
 }
 
-static struct dev_pm_ops ccwgroup_pm_ops = {
+static const struct dev_pm_ops ccwgroup_pm_ops = {
        .prepare = ccwgroup_pm_prepare,
        .complete = ccwgroup_pm_complete,
        .freeze = ccwgroup_pm_freeze,
index 92ff88ac11072d95d8249f695212cd9ac19b27b3..7679aee6fa147af0459855a899888850752437a4 100644 (file)
@@ -1148,7 +1148,7 @@ static int css_pm_restore(struct device *dev)
        return drv->restore ? drv->restore(sch) : 0;
 }
 
-static struct dev_pm_ops css_pm_ops = {
+static const struct dev_pm_ops css_pm_ops = {
        .prepare = css_pm_prepare,
        .complete = css_pm_complete,
        .freeze = css_pm_freeze,
index 9fecfb4223a85272246b74d35d24154f86eac57c..73901c9e260ffbfaf29ec1c5c627fe53acb52db4 100644 (file)
@@ -1904,7 +1904,7 @@ out_unlock:
        return ret;
 }
 
-static struct dev_pm_ops ccw_pm_ops = {
+static const struct dev_pm_ops ccw_pm_ops = {
        .prepare = ccw_device_pm_prepare,
        .complete = ccw_device_pm_complete,
        .freeze = ccw_device_pm_freeze,
index 98c04cac43c1d87467ee60f43807c0dbc654409b..65ebee0a326674432d3e1d0e092d6afc3d02ff2b 100644 (file)
@@ -159,7 +159,7 @@ static void netiucv_pm_complete(struct device *);
 static int netiucv_pm_freeze(struct device *);
 static int netiucv_pm_restore_thaw(struct device *);
 
-static struct dev_pm_ops netiucv_pm_ops = {
+static const struct dev_pm_ops netiucv_pm_ops = {
        .prepare = netiucv_pm_prepare,
        .complete = netiucv_pm_complete,
        .freeze = netiucv_pm_freeze,
index 3012355f8304e875953a732d2ab4b4e6dde1681f..67f2485d2372512376c770031aac583201ad27d9 100644 (file)
@@ -168,7 +168,7 @@ static int smsg_pm_restore_thaw(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops smsg_pm_ops = {
+static const struct dev_pm_ops smsg_pm_ops = {
        .freeze = smsg_pm_freeze,
        .thaw = smsg_pm_restore_thaw,
        .restore = smsg_pm_restore_thaw,
index 206c2fa8c1ba1ba51a53681975c7b1976ebacf5a..8643f5089361c220219c6404f17ee5e687354fb1 100644 (file)
@@ -1333,7 +1333,7 @@ static void ipr_log_enhanced_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
 
        error = &hostrcb->hcam.u.error.u.type_17_error;
        error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
-       strstrip(error->failure_reason);
+       strim(error->failure_reason);
 
        ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
                     be32_to_cpu(hostrcb->hcam.u.error.prc));
@@ -1359,7 +1359,7 @@ static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg,
 
        error = &hostrcb->hcam.u.error.u.type_07_error;
        error->failure_reason[sizeof(error->failure_reason) - 1] = '\0';
-       strstrip(error->failure_reason);
+       strim(error->failure_reason);
 
        ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason,
                     be32_to_cpu(hostrcb->hcam.u.error.prc));
index 2b38f6ad6e11153f8e7133d633c18e9ed7d7bc8e..8b955b534a36f7ce836967b49978b2deb7bd8d64 100644 (file)
@@ -984,7 +984,7 @@ static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc)
        }
 }
 
-static int skip_spaces(char *ptr, int len)
+static int sym_skip_spaces(char *ptr, int len)
 {
        int cnt, c;
 
@@ -1012,7 +1012,7 @@ static int is_keyword(char *ptr, int len, char *verb)
 }
 
 #define SKIP_SPACES(ptr, len)                                          \
-       if ((arg_len = skip_spaces(ptr, len)) < 1)                      \
+       if ((arg_len = sym_skip_spaces(ptr, len)) < 1)                  \
                return -EINVAL;                                         \
        ptr += arg_len; len -= arg_len;
 
index d8983dd5c4b22e82e0355f8af1bb53671e561d31..85dc0410ac1a8d3f2bed7a08f8078f23b4de57cd 100644 (file)
@@ -2162,7 +2162,7 @@ static struct ioc3_submodule ioc3uart_ops = {
 /**
  * ioc3_detect - module init called,
  */
-static int __devinit ioc3uart_init(void)
+static int __init ioc3uart_init(void)
 {
        int ret;
 
@@ -2179,7 +2179,7 @@ static int __devinit ioc3uart_init(void)
        return ret;
 }
 
-static void __devexit ioc3uart_exit(void)
+static void __exit ioc3uart_exit(void)
 {
        ioc3_unregister_submodule(&ioc3uart_ops);
        uart_unregister_driver(&ioc3_uart);
index 2e02c3026d24f464ebca356d548ffed7e2e9b189..836d9ab4f729669ac11f941a3b3e246b367fdcb3 100644 (file)
@@ -2904,7 +2904,7 @@ static struct ioc4_submodule ioc4_serial_submodule = {
 /**
  * ioc4_serial_init - module init
  */
-int ioc4_serial_init(void)
+static int __init ioc4_serial_init(void)
 {
        int ret;
 
@@ -2913,20 +2913,30 @@ int ioc4_serial_init(void)
                printk(KERN_WARNING
                        "%s: Couldn't register rs232 IOC4 serial driver\n",
                        __func__);
-               return ret;
+               goto out;
        }
        if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) {
                printk(KERN_WARNING
                        "%s: Couldn't register rs422 IOC4 serial driver\n",
                        __func__);
-               return ret;
+               goto out_uart_rs232;
        }
 
        /* register with IOC4 main module */
-       return ioc4_register_submodule(&ioc4_serial_submodule);
+       ret = ioc4_register_submodule(&ioc4_serial_submodule);
+       if (ret)
+               goto out_uart_rs422;
+       return 0;
+
+out_uart_rs422:
+       uart_unregister_driver(&ioc4_uart_rs422);
+out_uart_rs232:
+       uart_unregister_driver(&ioc4_uart_rs232);
+out:
+       return ret;
 }
 
-static void __devexit ioc4_serial_exit(void)
+static void __exit ioc4_serial_exit(void)
 {
        ioc4_unregister_submodule(&ioc4_serial_submodule);
        uart_unregister_driver(&ioc4_uart_rs232);
index 4a821046baae0124e6bb9a36824979d49e69952e..56ee082157aaf121c755fd3a42ef4005e9667f14 100644 (file)
@@ -756,7 +756,7 @@ static int serial_pxa_resume(struct device *dev)
         return 0;
 }
 
-static struct dev_pm_ops serial_pxa_pm_ops = {
+static const struct dev_pm_ops serial_pxa_pm_ops = {
        .suspend        = serial_pxa_suspend,
        .resume         = serial_pxa_resume,
 };
index ff38dbdb5c6ecf7686371dad4a4e909eae9c52d3..7e3f4ff58cfd346768a3b5f30780357d48c2edf8 100644 (file)
@@ -1312,7 +1312,7 @@ static int sci_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sci_dev_pm_ops = {
+static const struct dev_pm_ops sci_dev_pm_ops = {
        .suspend        = sci_suspend,
        .resume         = sci_resume,
 };
index 816d4c592a3ca6d609e4c9481ad2e4ef2ee05a9c..66802a4390ccff59bd51025dbb62800e6fd92336 100644 (file)
@@ -574,11 +574,11 @@ void ioc3_unregister_submodule(struct ioc3_submodule *is)
  * Device management *
  *********************/
 
-static char *
+static char * __devinitdata
 ioc3_class_names[]={"unknown", "IP27 BaseIO", "IP30 system", "MENET 1/2/3",
                        "MENET 4", "CADduo", "Altix Serial"};
 
-static int ioc3_class(struct ioc3_driver_data *idd)
+static int __devinit ioc3_class(struct ioc3_driver_data *idd)
 {
        int res = IOC3_CLASS_NONE;
        /* NIC-based logic */
@@ -601,7 +601,8 @@ static int ioc3_class(struct ioc3_driver_data *idd)
        return res;
 }
 /* Adds a new instance of an IOC3 card */
-static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+static int __devinit
+ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
 {
        struct ioc3_driver_data *idd;
        uint32_t pcmd;
@@ -753,7 +754,7 @@ out:
 }
 
 /* Removes a particular instance of an IOC3 card. */
-static void ioc3_remove(struct pci_dev *pdev)
+static void __devexit ioc3_remove(struct pci_dev *pdev)
 {
        int id;
        struct ioc3_driver_data *idd;
@@ -805,7 +806,7 @@ static struct pci_driver ioc3_driver = {
        .name = "IOC3",
        .id_table = ioc3_id_table,
        .probe = ioc3_probe,
-       .remove = ioc3_remove,
+       .remove = __devexit_p(ioc3_remove),
 };
 
 MODULE_DEVICE_TABLE(pci, ioc3_id_table);
@@ -815,15 +816,15 @@ MODULE_DEVICE_TABLE(pci, ioc3_id_table);
  *********************/
 
 /* Module load */
-static int __devinit ioc3_init(void)
+static int __init ioc3_init(void)
 {
        if (ia64_platform_is("sn2"))
                return pci_register_driver(&ioc3_driver);
-       return 0;
+       return -ENODEV;
 }
 
 /* Module unload */
-static void __devexit ioc3_exit(void)
+static void __exit ioc3_exit(void)
 {
        pci_unregister_driver(&ioc3_driver);
 }
index c8c2b693ffacbb757b281f7190f02f118e4da3fc..c2f707e5ce74accbc44a4c3766a25ea2fde93272 100644 (file)
@@ -1709,7 +1709,7 @@ static int pxa2xx_spi_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops pxa2xx_spi_pm_ops = {
+static const struct dev_pm_ops pxa2xx_spi_pm_ops = {
        .suspend        = pxa2xx_spi_suspend,
        .resume         = pxa2xx_spi_resume,
 };
index 33d94f76b9ef47fd92bddd54815e72262b6dffda..276591569c8bce421f1c2c9c4b0ed94385d22f44 100644 (file)
@@ -489,7 +489,7 @@ static int s3c24xx_spi_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops s3c24xx_spi_pmops = {
+static const struct dev_pm_ops s3c24xx_spi_pmops = {
        .suspend        = s3c24xx_spi_suspend,
        .resume         = s3c24xx_spi_resume,
 };
index aa53db9f2e88309d739809adf10928fd0a6d1a57..1ef3b8fc50b33063ed3fa988dd7b7ea9740bde08 100644 (file)
@@ -210,7 +210,7 @@ static int uio_pdrv_genirq_runtime_nop(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
+static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
        .runtime_suspend = uio_pdrv_genirq_runtime_nop,
        .runtime_resume = uio_pdrv_genirq_runtime_nop,
 };
index 91f2885b6ee10ca8f6ca396289e0b1ea2ee7060b..2dcf906df569440d18a0cecb9b4801dec581e77f 100644 (file)
@@ -363,7 +363,7 @@ static int hcd_pci_restore(struct device *dev)
        return resume_common(dev, true);
 }
 
-struct dev_pm_ops usb_hcd_pci_pm_ops = {
+const struct dev_pm_ops usb_hcd_pci_pm_ops = {
        .suspend        = hcd_pci_suspend,
        .suspend_noirq  = hcd_pci_suspend_noirq,
        .resume_noirq   = hcd_pci_resume_noirq,
index d8b43aee581e2d3c463efafcb396f9b8dd86a5e5..bbe2b924aae8d168f4876fff4debb7e0e1d3c96b 100644 (file)
@@ -330,7 +330,7 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev);
 extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 
 #ifdef CONFIG_PM_SLEEP
-extern struct dev_pm_ops       usb_hcd_pci_pm_ops;
+extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
 #endif
 #endif /* CONFIG_PCI */
 
index d86276c639c1a3915f9344187b9ff6e5071694b8..2fb42043b305228e6995cff0b870a744237d52af 100644 (file)
@@ -329,7 +329,7 @@ static int usb_dev_restore(struct device *dev)
        return usb_resume(dev, PMSG_RESTORE);
 }
 
-static struct dev_pm_ops usb_device_pm_ops = {
+static const struct dev_pm_ops usb_device_pm_ops = {
        .prepare =      usb_dev_prepare,
        .complete =     usb_dev_complete,
        .suspend =      usb_dev_suspend,
index ed77be76d6bb0102f4a6078a2b76f65d769ce1ac..dbfb482a94e37a7389c2d9c13681a5c9da408325 100644 (file)
@@ -297,7 +297,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops au1xxx_ehci_pmops = {
+static const struct dev_pm_ops au1xxx_ehci_pmops = {
        .suspend        = ehci_hcd_au1xxx_drv_suspend,
        .resume         = ehci_hcd_au1xxx_drv_resume,
 };
index e4380082ebb110a0e77a8b509da9cc72e66837d2..17a6043c1fa072cba3d84c01fe8f6ba83005fff3 100644 (file)
@@ -294,7 +294,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops au1xxx_ohci_pmops = {
+static const struct dev_pm_ops au1xxx_ohci_pmops = {
        .suspend        = ohci_hcd_au1xxx_drv_suspend,
        .resume         = ohci_hcd_au1xxx_drv_resume,
 };
index f1c06202fdf22d8630cc5647509a10c17a77bc97..a18debdd79b8cec346f7f625baf66a47800affd1 100644 (file)
@@ -518,7 +518,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
+static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
        .suspend        = ohci_hcd_pxa27x_drv_suspend,
        .resume         = ohci_hcd_pxa27x_drv_resume,
 };
index 41dbc70ae752eaf41332807d4be86905801c47a8..b7a661c02bcdb5d3edf0a84b39a50a41f2bf411c 100644 (file)
@@ -2353,7 +2353,7 @@ static int r8a66597_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops r8a66597_dev_pm_ops = {
+static const struct dev_pm_ops r8a66597_dev_pm_ops = {
        .suspend = r8a66597_suspend,
        .resume = r8a66597_resume,
        .poweroff = r8a66597_suspend,
index 49f2346afad3e8016d5f548e6f74d57b7322d49a..bfe08f4975a391d5af4af9d1a633274005125348 100644 (file)
@@ -2214,7 +2214,7 @@ static int musb_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops musb_dev_pm_ops = {
+static const struct dev_pm_ops musb_dev_pm_ops = {
        .suspend        = musb_suspend,
        .resume_noirq   = musb_resume_noirq,
 };
index 7fcb0eb54c6061d1214348c4b45f2730ec1c50b3..f2d76dae1eb370813b7c31b338aa951d45e2267b 100644 (file)
@@ -177,7 +177,7 @@ static int da903x_backlight_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops da903x_backlight_pm_ops = {
+static const struct dev_pm_ops da903x_backlight_pm_ops = {
        .suspend        = da903x_backlight_suspend,
        .resume         = da903x_backlight_resume,
 };
index a482dd7b0311bcd5eacdd1ca8cfaf49d5ff0eac8..9b3be74cee5a7f2e9f000bf4b64f9cec8c7a2715 100644 (file)
@@ -101,7 +101,7 @@ static ssize_t lcd_store_power(struct device *dev,
        int power = simple_strtoul(buf, &endp, 0);
        size_t size = endp - buf;
 
-       if (*endp && isspace(*endp))
+       if (isspace(*endp))
                size++;
        if (size != count)
                return -EINVAL;
@@ -140,7 +140,7 @@ static ssize_t lcd_store_contrast(struct device *dev,
        int contrast = simple_strtoul(buf, &endp, 0);
        size_t size = endp - buf;
 
-       if (*endp && isspace(*endp))
+       if (isspace(*endp))
                size++;
        if (size != count)
                return -EINVAL;
index 4830b1bf51e5da7e6fcdadcfe67ce231ea977cc1..80abbf323b99befaa77bd0588463cb01ad1fe578 100644 (file)
@@ -67,7 +67,7 @@ static ssize_t display_store_contrast(struct device *dev,
        contrast = simple_strtoul(buf, &endp, 0);
        size = endp - buf;
 
-       if (*endp && isspace(*endp))
+       if (isspace(*endp))
                size++;
 
        if (size != count)
index e759895bf3d3d19deb02ec6323f7c8ff646b96fc..f0af911a096d9a781f318794fff9053416e0f0c5 100644 (file)
@@ -17,7 +17,7 @@
 #include <asm/io.h>
 #include <asm/div64.h>
 #include <asm/delay.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
 
 #include "gxfb.h"
 
@@ -25,7 +25,7 @@ unsigned int gx_frame_buffer_size(void)
 {
        unsigned int val;
 
-       if (!geode_has_vsa2()) {
+       if (!cs5535_has_vsa2()) {
                uint32_t hi, lo;
 
                /* The number of pages is (PMAX - PMIN)+1 */
index 16a96f8fd8c5f942954ca53fd7379617cce3e43b..d19e9378b0c01423d3776e7171e457c355e17ddc 100644 (file)
@@ -340,7 +340,7 @@ static inline void write_fp(struct gxfb_par *par, int reg, uint32_t val)
 }
 
 
-/* MSRs are defined in asm/geode.h; their bitfields are here */
+/* MSRs are defined in linux/cs5535.h; their bitfields are here */
 
 #define MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3        (1 << 3)
 #define MSR_GLCP_SYS_RSTPLL_DOTPREMULT2        (1 << 2)
index 2552cac39e1c7aa99536c4563933982421f21f04..b3e639d1e12cbf67addef7eaa7455b8684b47b43 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/suspend.h>
 #include <linux/init.h>
 #include <linux/pci.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
 
 #include "gxfb.h"
 
index 6a51448fd3f74d53cf4880a9a22ff71062ea18d3..fc68a8b0a1449cfb2fdd3c8c8d45193e23056df0 100644 (file)
@@ -409,7 +409,7 @@ static inline void write_fp(struct lxfb_par *par, int reg, uint32_t val)
 }
 
 
-/* MSRs are defined in asm/geode.h; their bitfields are here */
+/* MSRs are defined in linux/cs5535.h; their bitfields are here */
 
 #define MSR_GLCP_DOTPLL_LOCK           (1 << 25)       /* r/o */
 #define MSR_GLCP_DOTPLL_HALFPIX                (1 << 24)
index b1cd49c9935618a6af9db4602021f41e8aaf23d4..0e5d8c7c3ebae2ad81a62bd693c857e10771f01a 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/fb.h>
 #include <linux/uaccess.h>
 #include <linux/delay.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
 
 #include "lxfb.h"
 
@@ -307,7 +307,7 @@ unsigned int lx_framebuffer_size(void)
 {
        unsigned int val;
 
-       if (!geode_has_vsa2()) {
+       if (!cs5535_has_vsa2()) {
                uint32_t hi, lo;
 
                /* The number of pages is (PMAX - PMIN)+1 */
index 9aff32ef8bb602c56ba720e34fcb9be1df547b4b..1bb043d70c647004b68e2615cfec9f8ed7d6cd89 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/fb.h>
 #include <asm/io.h>
 #include <asm/msr.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
 #include <asm/delay.h>
 
 #include "gxfb.h"
index b8d52a8360db2436371dc3445a39e3a4fd96c8c2..6082f653c68a448e3e32ea08571f04af97f6acf5 100644 (file)
@@ -16,7 +16,7 @@
 #include <asm/io.h>
 #include <asm/delay.h>
 #include <asm/msr.h>
-#include <asm/geode.h>
+#include <linux/cs5535.h>
 
 #include "gxfb.h"
 
index e7116a6d82d306b51e7b621c1e927fdd156c31ab..73c83a8de2d37a68b35553f7dcc8e18cb5fde890 100644 (file)
@@ -456,7 +456,7 @@ static int hitfb_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops hitfb_dev_pm_ops = {
+static const struct dev_pm_ops hitfb_dev_pm_ops = {
        .suspend        = hitfb_suspend,
        .resume         = hitfb_resume,
 };
index 5e6439ae739471386ea54fda7e603003dff8eb4f..5137aa016b833589b3ed2855daba5e076a2183d6 100644 (file)
@@ -50,7 +50,7 @@ static ssize_t video_output_store_state(struct device *dev,
        int request_state = simple_strtoul(buf,&endp,0);
        size_t size = endp - buf;
 
-       if (*endp && isspace(*endp))
+       if (isspace(*endp))
                size++;
        if (size != count)
                return -EINVAL;
index f58a3aae6ea630f931de2a2cc7118a65a0c344b3..b7e58059b592fc402bb91c7f7fd5c6bbc703e41e 100644 (file)
@@ -1667,7 +1667,7 @@ static int pxafb_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops pxafb_pm_ops = {
+static const struct dev_pm_ops pxafb_pm_ops = {
        .suspend        = pxafb_suspend,
        .resume         = pxafb_resume,
 };
index b4b5de930cf528409bd71a5f36009a373fc83ed4..8a65fb6648a621b06568823fabef62a4d3832f4c 100644 (file)
@@ -890,7 +890,7 @@ static int sh_mobile_lcdc_runtime_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
+static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
        .suspend = sh_mobile_lcdc_suspend,
        .resume = sh_mobile_lcdc_resume,
        .runtime_suspend = sh_mobile_lcdc_runtime_suspend,
index 77afb0acc50045a88a55282e8009f6b2ddd827d2..9c6594473d3b8877a1d128e1db500de1e3d3f7d2 100644 (file)
@@ -314,7 +314,7 @@ static int adx_wdt_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops adx_wdt_pm_ops = {
+static const struct dev_pm_ops adx_wdt_pm_ops = {
        .suspend = adx_wdt_suspend,
        .resume = adx_wdt_resume,
 };
index 64d44efad7a5d6752ad5c867ff7d10dd0b94b1e3..f8fccaaad6282b097f75fae49f2aa4d6c2e6c390 100644 (file)
@@ -6,6 +6,10 @@ menu "File systems"
 
 if BLOCK
 
+config FS_JOURNAL_INFO
+       bool
+       default n
+
 source "fs/ext2/Kconfig"
 source "fs/ext3/Kconfig"
 source "fs/ext4/Kconfig"
index 38502c67987c573541325aaf0f8a5db1ada1f756..79d2b1aa389f656f4952335955ab7bae89e41fa0 100644 (file)
@@ -380,7 +380,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm,
        down_write(&current->mm->mmap_sem);
        current->mm->start_brk = do_mmap(NULL, 0, stack_size,
                                         PROT_READ | PROT_WRITE | PROT_EXEC,
-                                        MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN,
+                                        MAP_PRIVATE | MAP_ANONYMOUS |
+                                        MAP_UNINITIALIZED | MAP_GROWSDOWN,
                                         0);
 
        if (IS_ERR_VALUE(current->mm->start_brk)) {
index 7bb3c020e570d9a1e0d2d640f6c9127df8e57a01..402afe0a0bfbbab01e307800716c8b2553dc6479 100644 (file)
@@ -4,6 +4,7 @@ config BTRFS_FS
        select LIBCRC32C
        select ZLIB_INFLATE
        select ZLIB_DEFLATE
+       select FS_JOURNAL_INFO
        help
          Btrfs is a new filesystem with extents, writable snapshotting,
          support for multiple devices and many more features.
index 4618516dd994b3c2da8a44c13d83a877d89fb220..c2413561ea753f4ede277ec6736c49db738456f5 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mount.h>
 #include <linux/statfs.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/fs_struct.h>
 #include "internal.h"
 
@@ -257,8 +258,7 @@ static ssize_t cachefiles_daemon_write(struct file *file,
                if (args == data)
                        goto error;
                *args = '\0';
-               for (args++; isspace(*args); args++)
-                       continue;
+               args = skip_spaces(++args);
        }
 
        /* run the appropriate command handler */
index c0c636e34f60f5b347407f1c43599e088d7fedca..623a5cc3076a114af8808ca030f04bdd82638829 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -923,6 +923,15 @@ char *get_task_comm(char *buf, struct task_struct *tsk)
 void set_task_comm(struct task_struct *tsk, char *buf)
 {
        task_lock(tsk);
+
+       /*
+        * Threads may access current->comm without holding
+        * the task lock, so write the string carefully.
+        * Readers without a lock may see incomplete new
+        * names but are safe from non-terminating string reads.
+        */
+       memset(tsk->comm, 0, TASK_COMM_LEN);
+       wmb();
        strlcpy(tsk->comm, buf, sizeof(tsk->comm));
        task_unlock(tsk);
        perf_event_comm(tsk);
index 9acf7e808139d3df688da7d66ffb33b5cf081d8e..e5f6774846e46337d9e2e39790c239815f20caf3 100644 (file)
@@ -2,6 +2,7 @@ config EXT4_FS
        tristate "The Extended 4 (ext4) filesystem"
        select JBD2
        select CRC16
+       select FS_JOURNAL_INFO
        help
          This is the next generation of the ext3 filesystem.
 
index 768c111a77ec16a84427544c9aa2ce0faf81d8b0..827bde1f2594b34f4535f6db14a83f53636bfffe 100644 (file)
@@ -2137,11 +2137,8 @@ static int parse_strtoul(const char *buf,
 {
        char *endp;
 
-       while (*buf && isspace(*buf))
-               buf++;
-       *value = simple_strtoul(buf, &endp, 0);
-       while (*endp && isspace(*endp))
-               endp++;
+       *value = simple_strtoul(skip_spaces(buf), &endp, 0);
+       endp = skip_spaces(endp);
        if (*endp || *value > max)
                return -EINVAL;
 
index 4dcddf83326f45f7dd874587ef915b7844d60b0b..b192c661caa69a3c04b8d499c85721297a3cd9f2 100644 (file)
@@ -10,6 +10,7 @@ config GFS2_FS
        select SLOW_WORK
        select QUOTA
        select QUOTACTL
+       select FS_JOURNAL_INFO
        help
          A cluster filesystem.
 
index c5dad1eb7b916d34884883abe192b9cb89134746..0dc34621f6a6ecf73acddc3c82d0a90c731ff601 100644 (file)
@@ -85,11 +85,7 @@ static ssize_t uuid_show(struct gfs2_sbd *sdp, char *buf)
        buf[0] = '\0';
        if (!gfs2_uuid_valid(uuid))
                return 0;
-       return snprintf(buf, PAGE_SIZE, "%02X%02X%02X%02X-%02X%02X-"
-                       "%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
-                       uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
-                       uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
-                       uuid[12], uuid[13], uuid[14], uuid[15]);
+       return snprintf(buf, PAGE_SIZE, "%pUB\n", uuid);
 }
 
 static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf)
@@ -575,14 +571,8 @@ static int gfs2_uevent(struct kset *kset, struct kobject *kobj,
        add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name);
        if (!sdp->sd_args.ar_spectator)
                add_uevent_var(env, "JOURNALID=%u", sdp->sd_lockstruct.ls_jid);
-       if (gfs2_uuid_valid(uuid)) {
-               add_uevent_var(env, "UUID=%02X%02X%02X%02X-%02X%02X-%02X%02X-"
-                              "%02X%02X-%02X%02X%02X%02X%02X%02X",
-                              uuid[0], uuid[1], uuid[2], uuid[3], uuid[4],
-                              uuid[5], uuid[6], uuid[7], uuid[8], uuid[9],
-                              uuid[10], uuid[11], uuid[12], uuid[13],
-                              uuid[14], uuid[15]);
-       }
+       if (gfs2_uuid_valid(uuid))
+               add_uevent_var(env, "UUID=%pUB", uuid);
        return 0;
 }
 
index 6d98f116ca03d610a1477dc77558c899f5abe48d..424b0337f5244b150fcad3fb67b9ef8cb93ae932 100644 (file)
@@ -289,6 +289,10 @@ int hfs_cat_move(u32 cnid, struct inode *src_dir, struct qstr *src_name,
        err = hfs_brec_find(&src_fd);
        if (err)
                goto out;
+       if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
+               err = -EIO;
+               goto out;
+       }
 
        hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
                            src_fd.entrylength);
index 7c69b98a2e45b679cc97b87e00247fa40442c2d8..2b3b8611b41b5010daf2392a522834075654a4ef 100644 (file)
@@ -79,6 +79,11 @@ static int hfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                filp->f_pos++;
                /* fall through */
        case 1:
+               if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
+                       err = -EIO;
+                       goto out;
+               }
+
                hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
                if (entry.type != HFS_CDR_THD) {
                        printk(KERN_ERR "hfs: bad catalog folder thread\n");
@@ -109,6 +114,12 @@ static int hfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        err = -EIO;
                        goto out;
                }
+
+               if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
+                       err = -EIO;
+                       goto out;
+               }
+
                hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
                type = entry.type;
                len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName);
index f7fcbe49da723c40b56c816c8f04bea6e8d24616..5ed7252b7b23246aabdb015341ba19cbb101379a 100644 (file)
@@ -409,8 +409,13 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent)
        /* try to get the root inode */
        hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
        res = hfs_cat_find_brec(sb, HFS_ROOT_CNID, &fd);
-       if (!res)
+       if (!res) {
+               if (fd.entrylength > sizeof(rec) || fd.entrylength < 0) {
+                       res =  -EIO;
+                       goto bail;
+               }
                hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, fd.entrylength);
+       }
        if (res) {
                hfs_find_exit(&fd);
                goto bail_no_root;
index 4e28beeed157236c7a73a9270972f46dddd3d69b..a8408983abd4abbf32c356f4234aa63f63116b15 100644 (file)
@@ -1,5 +1,6 @@
 config JBD
        tristate
+       select FS_JOURNAL_INFO
        help
          This is a generic journalling layer for block devices.  It is
          currently used by the ext3 file system, but it could also be
index f32f346f4b0a521a5b6bbaedc7b0a5a7750c4b1e..0f7d1ceafdfd39a0a5c8a8cd7ddb0b8785cd8d4c 100644 (file)
@@ -1,6 +1,7 @@
 config JBD2
        tristate
        select CRC32
+       select FS_JOURNAL_INFO
        help
          This is a generic journaling layer for block devices that support
          both 32-bit and 64-bit block numbers.  It is currently used by
index 251da07b2a1ddab95bbfb1579ceb20845ddf3e6e..1225af7b21669a02368cea76868f0faae7c05b86 100644 (file)
@@ -2,6 +2,7 @@ config NILFS2_FS
        tristate "NILFS2 file system support (EXPERIMENTAL)"
        depends on EXPERIMENTAL
        select CRC32
+       select FS_JOURNAL_INFO
        help
          NILFS2 is a log-structured file system (LFS) supporting continuous
          snapshotting.  In addition to versioning capability of the entire
index af643b5aefe8909e25b1f741ad1b24a5f53f7a81..4df4a464a919c2ed372b4e93d742216d51692066 100644 (file)
@@ -1265,6 +1265,72 @@ static const struct file_operations proc_pid_sched_operations = {
 
 #endif
 
+static ssize_t comm_write(struct file *file, const char __user *buf,
+                               size_t count, loff_t *offset)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct task_struct *p;
+       char buffer[TASK_COMM_LEN];
+
+       memset(buffer, 0, sizeof(buffer));
+       if (count > sizeof(buffer) - 1)
+               count = sizeof(buffer) - 1;
+       if (copy_from_user(buffer, buf, count))
+               return -EFAULT;
+
+       p = get_proc_task(inode);
+       if (!p)
+               return -ESRCH;
+
+       if (same_thread_group(current, p))
+               set_task_comm(p, buffer);
+       else
+               count = -EINVAL;
+
+       put_task_struct(p);
+
+       return count;
+}
+
+static int comm_show(struct seq_file *m, void *v)
+{
+       struct inode *inode = m->private;
+       struct task_struct *p;
+
+       p = get_proc_task(inode);
+       if (!p)
+               return -ESRCH;
+
+       task_lock(p);
+       seq_printf(m, "%s\n", p->comm);
+       task_unlock(p);
+
+       put_task_struct(p);
+
+       return 0;
+}
+
+static int comm_open(struct inode *inode, struct file *filp)
+{
+       int ret;
+
+       ret = single_open(filp, comm_show, NULL);
+       if (!ret) {
+               struct seq_file *m = filp->private_data;
+
+               m->private = inode;
+       }
+       return ret;
+}
+
+static const struct file_operations proc_pid_set_comm_operations = {
+       .open           = comm_open,
+       .read           = seq_read,
+       .write          = comm_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 /*
  * We added or removed a vma mapping the executable. The vmas are only mapped
  * during exec and are not mapped with the mmap system call.
@@ -2504,6 +2570,7 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_SCHED_DEBUG
        REG("sched",      S_IRUGO|S_IWUSR, proc_pid_sched_operations),
 #endif
+       REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
 #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
        INF("syscall",    S_IRUSR, proc_pid_syscall),
 #endif
@@ -2838,6 +2905,7 @@ static const struct pid_entry tid_base_stuff[] = {
 #ifdef CONFIG_SCHED_DEBUG
        REG("sched",     S_IRUGO|S_IWUSR, proc_pid_sched_operations),
 #endif
+       REG("comm",      S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
 #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
        INF("syscall",   S_IRUSR, proc_pid_syscall),
 #endif
index 2a1bef9203c6e73734e7fa359873bbb45456d0ae..47c03f4336b843ed1ba9a7f28b3f4824e1c663d1 100644 (file)
@@ -650,6 +650,50 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
        return err;
 }
 
+static u64 huge_pte_to_pagemap_entry(pte_t pte, int offset)
+{
+       u64 pme = 0;
+       if (pte_present(pte))
+               pme = PM_PFRAME(pte_pfn(pte) + offset)
+                       | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT;
+       return pme;
+}
+
+static int pagemap_hugetlb_range(pte_t *pte, unsigned long addr,
+                                unsigned long end, struct mm_walk *walk)
+{
+       struct vm_area_struct *vma;
+       struct pagemapread *pm = walk->private;
+       struct hstate *hs = NULL;
+       int err = 0;
+
+       vma = find_vma(walk->mm, addr);
+       if (vma)
+               hs = hstate_vma(vma);
+       for (; addr != end; addr += PAGE_SIZE) {
+               u64 pfn = PM_NOT_PRESENT;
+
+               if (vma && (addr >= vma->vm_end)) {
+                       vma = find_vma(walk->mm, addr);
+                       if (vma)
+                               hs = hstate_vma(vma);
+               }
+
+               if (vma && (vma->vm_start <= addr) && is_vm_hugetlb_page(vma)) {
+                       /* calculate pfn of the "raw" page in the hugepage. */
+                       int offset = (addr & ~huge_page_mask(hs)) >> PAGE_SHIFT;
+                       pfn = huge_pte_to_pagemap_entry(*pte, offset);
+               }
+               err = add_to_pagemap(addr, pfn, pm);
+               if (err)
+                       return err;
+       }
+
+       cond_resched();
+
+       return err;
+}
+
 /*
  * /proc/pid/pagemap - an array mapping virtual pages to pfns
  *
@@ -742,6 +786,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
 
        pagemap_walk.pmd_entry = pagemap_pte_range;
        pagemap_walk.pte_hole = pagemap_pte_hole;
+       pagemap_walk.hugetlb_entry = pagemap_hugetlb_range;
        pagemap_walk.mm = mm;
        pagemap_walk.private = &pm;
 
index 8f5c05d3dbd3e2aaa4abcd3fcc92af79e324daca..5d9fd64ef81a9ad76b1525943cc34a79b0a6a33d 100644 (file)
@@ -110,9 +110,13 @@ int task_statm(struct mm_struct *mm, int *shared, int *text,
                }
        }
 
-       size += (*text = mm->end_code - mm->start_code);
-       size += (*data = mm->start_stack - mm->start_data);
+       *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
+               >> PAGE_SHIFT;
+       *data = (PAGE_ALIGN(mm->start_stack) - (mm->start_data & PAGE_MASK))
+               >> PAGE_SHIFT;
        up_read(&mm->mmap_sem);
+       size >>= PAGE_SHIFT;
+       size += *text + *data;
        *resident = size;
        return size;
 }
index 513f431038f9a749960b6c9b328ece5799684330..ac7cd75c86f8d351e4240bac5d1b12b1c317a4bb 100644 (file)
@@ -1,6 +1,7 @@
 config REISERFS_FS
        tristate "Reiserfs support"
        select CRC32
+       select FS_JOURNAL_INFO
        help
          Stores not just filenames but the files themselves in a balanced
          tree.  Uses journalling.
index 8a771c59ac3eab2e460f5d6f337155215ee014a2..90492327b3835c476eb52ba0399c20c581e1e19f 100644 (file)
@@ -350,13 +350,8 @@ void dbg_dump_node(const struct ubifs_info *c, const void *node)
                       le32_to_cpu(sup->fmt_version));
                printk(KERN_DEBUG "\ttime_gran      %u\n",
                       le32_to_cpu(sup->time_gran));
-               printk(KERN_DEBUG "\tUUID           %02X%02X%02X%02X-%02X%02X"
-                      "-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
-                      sup->uuid[0], sup->uuid[1], sup->uuid[2], sup->uuid[3],
-                      sup->uuid[4], sup->uuid[5], sup->uuid[6], sup->uuid[7],
-                      sup->uuid[8], sup->uuid[9], sup->uuid[10], sup->uuid[11],
-                      sup->uuid[12], sup->uuid[13], sup->uuid[14],
-                      sup->uuid[15]);
+               printk(KERN_DEBUG "\tUUID           %pUB\n",
+                      sup->uuid);
                break;
        }
        case UBIFS_MST_NODE:
index 943ad5624530bfc65d8d47f9d1b5c9721939d454..43f9d19a6f3313ec3df7f358138e153703d4895b 100644 (file)
@@ -1393,12 +1393,7 @@ static int mount_ubifs(struct ubifs_info *c)
                c->leb_size, c->leb_size >> 10);
        dbg_msg("data journal heads:  %d",
                c->jhead_cnt - NONDATA_JHEADS_CNT);
-       dbg_msg("UUID:                %02X%02X%02X%02X-%02X%02X"
-              "-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
-              c->uuid[0], c->uuid[1], c->uuid[2], c->uuid[3],
-              c->uuid[4], c->uuid[5], c->uuid[6], c->uuid[7],
-              c->uuid[8], c->uuid[9], c->uuid[10], c->uuid[11],
-              c->uuid[12], c->uuid[13], c->uuid[14], c->uuid[15]);
+       dbg_msg("UUID:                %pUB", c->uuid);
        dbg_msg("big_lpt              %d", c->big_lpt);
        dbg_msg("log LEBs:            %d (%d - %d)",
                c->log_lebs, UBIFS_LOG_LNUM, c->log_last);
index 1ec98ed914d4c951461dcff2ff4ae1a23fa2d55c..9d4fdcaf897f80a5cd2b9ba17fa5deff60abba81 100644 (file)
@@ -225,16 +225,10 @@ xlog_header_check_dump(
        xfs_mount_t             *mp,
        xlog_rec_header_t       *head)
 {
-       int                     b;
-
-       cmn_err(CE_DEBUG, "%s:  SB : uuid = ", __func__);
-       for (b = 0; b < 16; b++)
-               cmn_err(CE_DEBUG, "%02x", ((__uint8_t *)&mp->m_sb.sb_uuid)[b]);
-       cmn_err(CE_DEBUG, ", fmt = %d\n", XLOG_FMT);
-       cmn_err(CE_DEBUG, "    log : uuid = ");
-       for (b = 0; b < 16; b++)
-               cmn_err(CE_DEBUG, "%02x", ((__uint8_t *)&head->h_fs_uuid)[b]);
-       cmn_err(CE_DEBUG, ", fmt = %d\n", be32_to_cpu(head->h_fmt));
+       cmn_err(CE_DEBUG, "%s:  SB : uuid = %pU, fmt = %d\n",
+               __func__, &mp->m_sb.sb_uuid, XLOG_FMT);
+       cmn_err(CE_DEBUG, "    log : uuid = %pU, fmt = %d\n",
+               &head->h_fs_uuid, be32_to_cpu(head->h_fmt));
 }
 #else
 #define xlog_header_check_dump(mp, head)
index 4b6755984d241261941334dcf5f8c8431a5c6e54..18c435d7c082e387fdeca7a436e1e98f7ee3c2e3 100644 (file)
@@ -113,22 +113,22 @@ extern void warn_slowpath_null(const char *file, const int line);
 #endif
 
 #define WARN_ON_ONCE(condition)        ({                              \
-       static int __warned;                                    \
+       static bool __warned;                                   \
        int __ret_warn_once = !!(condition);                    \
                                                                \
        if (unlikely(__ret_warn_once))                          \
                if (WARN_ON(!__warned))                         \
-                       __warned = 1;                           \
+                       __warned = true;                        \
        unlikely(__ret_warn_once);                              \
 })
 
 #define WARN_ONCE(condition, format...)        ({                      \
-       static int __warned;                                    \
+       static bool __warned;                                   \
        int __ret_warn_once = !!(condition);                    \
                                                                \
        if (unlikely(__ret_warn_once))                          \
                if (WARN(!__warned, format))                    \
-                       __warned = 1;                           \
+                       __warned = true;                        \
        unlikely(__ret_warn_once);                              \
 })
 
index 5ee13b2fd223599422446b509227eadb24b7f7e8..20111265afd80a6cbc5a65d057f5100aa7dac69f 100644 (file)
 #define MAP_TYPE       0x0f            /* Mask for type of mapping */
 #define MAP_FIXED      0x10            /* Interpret addr exactly */
 #define MAP_ANONYMOUS  0x20            /* don't use a file */
+#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED
+# define MAP_UNINITIALIZED 0x4000000   /* For anonymous mmap, memory could be uninitialized */
+#else
+# define MAP_UNINITIALIZED 0x0         /* Don't support this flag */
+#endif
 
 #define MS_ASYNC       1               /* sync memory asynchronously */
 #define MS_INVALIDATE  2               /* invalidate the caches */
index 57b1846a3c876baf138214b2d511c693143fefa8..3e09b345f4d675ffddcab95e6d6b72039d141237 100644 (file)
@@ -3,8 +3,6 @@
 
 #define ATMEL_MCI_MAX_NR_SLOTS 2
 
-#include <linux/dw_dmac.h>
-
 /**
  * struct mci_slot_pdata - board-specific per-slot configuration
  * @bus_width: Number of data lines wired up the slot
@@ -34,7 +32,7 @@ struct mci_slot_pdata {
  * @slot: Per-slot configuration data.
  */
 struct mci_platform_data {
-       struct dw_dma_slave     dma_slave;
+       struct mci_dma_data     *dma_slave;
        struct mci_slot_pdata   slot[ATMEL_MCI_MAX_NR_SLOTS];
 };
 
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
new file mode 100644 (file)
index 0000000..d5a1d48
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * AMD CS5535/CS5536 definitions
+ * Copyright (C) 2006  Advanced Micro Devices, Inc.
+ * Copyright (C) 2009  Andres Salomon <dilinger@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef _CS5535_H
+#define _CS5535_H
+
+/* MSRs */
+#define MSR_GLIU_P2D_RO0       0x10000029
+
+#define MSR_LX_GLD_MSR_CONFIG  0x48002001
+#define MSR_LX_MSR_PADSEL      0x48002011      /* NOT 0x48000011; the data
+                                                * sheet has the wrong value */
+#define MSR_GLCP_SYS_RSTPLL    0x4C000014
+#define MSR_GLCP_DOTPLL                0x4C000015
+
+#define MSR_LBAR_SMB           0x5140000B
+#define MSR_LBAR_GPIO          0x5140000C
+#define MSR_LBAR_MFGPT         0x5140000D
+#define MSR_LBAR_ACPI          0x5140000E
+#define MSR_LBAR_PMS           0x5140000F
+
+#define MSR_DIVIL_SOFT_RESET   0x51400017
+
+#define MSR_PIC_YSEL_LOW       0x51400020
+#define MSR_PIC_YSEL_HIGH      0x51400021
+#define MSR_PIC_ZSEL_LOW       0x51400022
+#define MSR_PIC_ZSEL_HIGH      0x51400023
+#define MSR_PIC_IRQM_LPC       0x51400025
+
+#define MSR_MFGPT_IRQ          0x51400028
+#define MSR_MFGPT_NR           0x51400029
+#define MSR_MFGPT_SETUP                0x5140002B
+
+#define MSR_LX_SPARE_MSR       0x80000011      /* DC-specific */
+
+#define MSR_GX_GLD_MSR_CONFIG  0xC0002001
+#define MSR_GX_MSR_PADSEL      0xC0002011
+
+/* resource sizes */
+#define LBAR_GPIO_SIZE         0xFF
+#define LBAR_MFGPT_SIZE                0x40
+#define LBAR_ACPI_SIZE         0x40
+#define LBAR_PMS_SIZE          0x80
+
+/* VSA2 magic values */
+#define VSA_VRC_INDEX          0xAC1C
+#define VSA_VRC_DATA           0xAC1E
+#define VSA_VR_UNLOCK          0xFC53  /* unlock virtual register */
+#define VSA_VR_SIGNATURE       0x0003
+#define VSA_VR_MEM_SIZE                0x0200
+#define AMD_VSA_SIG            0x4132  /* signature is ascii 'VSA2' */
+#define GSW_VSA_SIG            0x534d  /* General Software signature */
+
+#include <linux/io.h>
+
+static inline int cs5535_has_vsa2(void)
+{
+       static int has_vsa2 = -1;
+
+       if (has_vsa2 == -1) {
+               uint16_t val;
+
+               /*
+                * The VSA has virtual registers that we can query for a
+                * signature.
+                */
+               outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
+               outw(VSA_VR_SIGNATURE, VSA_VRC_INDEX);
+
+               val = inw(VSA_VRC_DATA);
+               has_vsa2 = (val == AMD_VSA_SIG || val == GSW_VSA_SIG);
+       }
+
+       return has_vsa2;
+}
+
+/* GPIOs */
+#define GPIO_OUTPUT_VAL                0x00
+#define GPIO_OUTPUT_ENABLE     0x04
+#define GPIO_OUTPUT_OPEN_DRAIN 0x08
+#define GPIO_OUTPUT_INVERT     0x0C
+#define GPIO_OUTPUT_AUX1       0x10
+#define GPIO_OUTPUT_AUX2       0x14
+#define GPIO_PULL_UP           0x18
+#define GPIO_PULL_DOWN         0x1C
+#define GPIO_INPUT_ENABLE      0x20
+#define GPIO_INPUT_INVERT      0x24
+#define GPIO_INPUT_FILTER      0x28
+#define GPIO_INPUT_EVENT_COUNT 0x2C
+#define GPIO_READ_BACK         0x30
+#define GPIO_INPUT_AUX1                0x34
+#define GPIO_EVENTS_ENABLE     0x38
+#define GPIO_LOCK_ENABLE       0x3C
+#define GPIO_POSITIVE_EDGE_EN  0x40
+#define GPIO_NEGATIVE_EDGE_EN  0x44
+#define GPIO_POSITIVE_EDGE_STS 0x48
+#define GPIO_NEGATIVE_EDGE_STS 0x4C
+
+#define GPIO_MAP_X             0xE0
+#define GPIO_MAP_Y             0xE4
+#define GPIO_MAP_Z             0xE8
+#define GPIO_MAP_W             0xEC
+
+void cs5535_gpio_set(unsigned offset, unsigned int reg);
+void cs5535_gpio_clear(unsigned offset, unsigned int reg);
+int cs5535_gpio_isset(unsigned offset, unsigned int reg);
+
+/* MFGPTs */
+
+#define MFGPT_MAX_TIMERS       8
+#define MFGPT_TIMER_ANY                (-1)
+
+#define MFGPT_DOMAIN_WORKING   1
+#define MFGPT_DOMAIN_STANDBY   2
+#define MFGPT_DOMAIN_ANY       (MFGPT_DOMAIN_WORKING | MFGPT_DOMAIN_STANDBY)
+
+#define MFGPT_CMP1             0
+#define MFGPT_CMP2             1
+
+#define MFGPT_EVENT_IRQ                0
+#define MFGPT_EVENT_NMI                1
+#define MFGPT_EVENT_RESET      3
+
+#define MFGPT_REG_CMP1         0
+#define MFGPT_REG_CMP2         2
+#define MFGPT_REG_COUNTER      4
+#define MFGPT_REG_SETUP                6
+
+#define MFGPT_SETUP_CNTEN      (1 << 15)
+#define MFGPT_SETUP_CMP2       (1 << 14)
+#define MFGPT_SETUP_CMP1       (1 << 13)
+#define MFGPT_SETUP_SETUP      (1 << 12)
+#define MFGPT_SETUP_STOPEN     (1 << 11)
+#define MFGPT_SETUP_EXTEN      (1 << 10)
+#define MFGPT_SETUP_REVEN      (1 << 5)
+#define MFGPT_SETUP_CLKSEL     (1 << 4)
+
+struct cs5535_mfgpt_timer;
+
+extern uint16_t cs5535_mfgpt_read(struct cs5535_mfgpt_timer *timer,
+               uint16_t reg);
+extern void cs5535_mfgpt_write(struct cs5535_mfgpt_timer *timer, uint16_t reg,
+               uint16_t value);
+
+extern int cs5535_mfgpt_toggle_event(struct cs5535_mfgpt_timer *timer, int cmp,
+               int event, int enable);
+extern int cs5535_mfgpt_set_irq(struct cs5535_mfgpt_timer *timer, int cmp,
+               int *irq, int enable);
+extern struct cs5535_mfgpt_timer *cs5535_mfgpt_alloc_timer(int timer,
+               int domain);
+extern void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer);
+
+static inline int cs5535_mfgpt_setup_irq(struct cs5535_mfgpt_timer *timer,
+               int cmp, int *irq)
+{
+       return cs5535_mfgpt_set_irq(timer, cmp, irq, 1);
+}
+
+static inline int cs5535_mfgpt_release_irq(struct cs5535_mfgpt_timer *timer,
+               int cmp, int *irq)
+{
+       return cs5535_mfgpt_set_irq(timer, cmp, irq, 0);
+}
+
+#endif
index afa36392297a341ce79e7d52a0231453776b7a89..a3d6ee0044f974ebf12236de4c5ecd5678d26a6c 100644 (file)
@@ -15,7 +15,7 @@
 #define _X     0x40    /* hex digit */
 #define _SP    0x80    /* hard space (0x20) */
 
-extern unsigned char _ctype[];
+extern const unsigned char _ctype[];
 
 #define __ismask(x) (_ctype[(int)(unsigned char)(x)])
 
@@ -27,6 +27,7 @@ extern unsigned char _ctype[];
 #define islower(c)     ((__ismask(c)&(_L)) != 0)
 #define isprint(c)     ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
 #define ispunct(c)     ((__ismask(c)&(_P)) != 0)
+/* Note: isspace() must return false for %NUL-terminator */
 #define isspace(c)     ((__ismask(c)&(_S)) != 0)
 #define isupper(c)     ((__ismask(c)&(_U)) != 0)
 #define isxdigit(c)    ((__ismask(c)&(_D|_X)) != 0)
index a0d9422a15693cdd5ab978bdff8c18985f570c9a..f8c2e17675002ec1508742ad629610a3e76f19da 100644 (file)
@@ -57,8 +57,7 @@ extern int ddebug_remove_module(char *mod_name);
        { KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH,  \
                DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT };        \
        if (__dynamic_dbg_enabled(descriptor))                          \
-               printk(KERN_DEBUG KBUILD_MODNAME ":" pr_fmt(fmt),       \
-                               ##__VA_ARGS__);                         \
+               printk(KERN_DEBUG pr_fmt(fmt),  ##__VA_ARGS__);         \
        } while (0)
 
 
@@ -69,9 +68,7 @@ extern int ddebug_remove_module(char *mod_name);
        { KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH,  \
                DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT };        \
        if (__dynamic_dbg_enabled(descriptor))                          \
-                       dev_printk(KERN_DEBUG, dev,                     \
-                                       KBUILD_MODNAME ": " fmt,        \
-                                       ##__VA_ARGS__);                 \
+               dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__);        \
        } while (0)
 
 #else
@@ -81,8 +78,10 @@ static inline int ddebug_remove_module(char *mod)
        return 0;
 }
 
-#define dynamic_pr_debug(fmt, ...)  do { } while (0)
-#define dynamic_dev_dbg(dev, format, ...)  do { } while (0)
+#define dynamic_pr_debug(fmt, ...)                                     \
+       do { if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); } while (0)
+#define dynamic_dev_dbg(dev, format, ...)                              \
+       do { if (0) dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); } while (0)
 #endif
 
 #endif
index ce4581fbc08beec42b44882b367f9ef49f3389c4..fb737bc19a8ce233fa016e71ccf77461c5e81dbc 100644 (file)
@@ -280,11 +280,7 @@ efi_guidcmp (efi_guid_t left, efi_guid_t right)
 static inline char *
 efi_guid_unparse(efi_guid_t *guid, char *out)
 {
-       sprintf(out, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
-               guid->b[3], guid->b[2], guid->b[1], guid->b[0],
-               guid->b[5], guid->b[4], guid->b[7], guid->b[6],
-               guid->b[8], guid->b[9], guid->b[10], guid->b[11],
-               guid->b[12], guid->b[13], guid->b[14], guid->b[15]);
+       sprintf(out, "%pUl", guid->b);
         return out;
 }
 
index ec87f3142bf30940c31911e71e6638d749de8f04..1b12642636c7a121f3a3797676cf8d4746ca13a9 100644 (file)
@@ -34,6 +34,11 @@ static inline long IS_ERR(const void *ptr)
        return IS_ERR_VALUE((unsigned long)ptr);
 }
 
+static inline long IS_ERR_OR_NULL(const void *ptr)
+{
+       return !ptr || IS_ERR_VALUE((unsigned long)ptr);
+}
+
 /**
  * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type
  * @ptr: The pointer to cast.
index 41a59afc70faffdccd55fa9d0a0616df15fcb7ff..78b4bc64c0064f19bccce02de2862813d56ff7e0 100644 (file)
@@ -23,6 +23,12 @@ void reset_vma_resv_huge_pages(struct vm_area_struct *vma);
 int hugetlb_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
 int hugetlb_overcommit_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
 int hugetlb_treat_movable_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
+
+#ifdef CONFIG_NUMA
+int hugetlb_mempolicy_sysctl_handler(struct ctl_table *, int,
+                                       void __user *, size_t *, loff_t *);
+#endif
+
 int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *);
 int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *,
                        struct page **, struct vm_area_struct **,
index 419ab546b2661909149eed8f362e40f75c240898..02fc617782ef7b5f5046cc9b6ac7df88536e24b9 100644 (file)
@@ -110,7 +110,7 @@ extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client,
  * @driver: Device driver model driver
  * @id_table: List of I2C devices supported by this driver
  * @detect: Callback for device detection
- * @address_data: The I2C addresses to probe (for detect)
+ * @address_list: The I2C addresses to probe (for detect)
  * @clients: List of detected clients we created (for i2c-core use only)
  *
  * The driver.owner field should be set to the module owner of this driver.
@@ -161,8 +161,8 @@ struct i2c_driver {
        const struct i2c_device_id *id_table;
 
        /* Device detection callback for automatic device creation */
-       int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
-       const struct i2c_client_address_data *address_data;
+       int (*detect)(struct i2c_client *, struct i2c_board_info *);
+       const unsigned short *address_list;
        struct list_head clients;
 };
 #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
@@ -391,14 +391,6 @@ static inline void i2c_unlock_adapter(struct i2c_adapter *adapter)
 #define I2C_CLASS_DDC          (1<<3)  /* DDC bus on graphics adapters */
 #define I2C_CLASS_SPD          (1<<7)  /* SPD EEPROMs and similar */
 
-/* i2c_client_address_data is the struct for holding default client
- * addresses for a driver and for the parameters supplied on the
- * command line
- */
-struct i2c_client_address_data {
-       const unsigned short *normal_i2c;
-};
-
 /* Internal numbers to terminate lists */
 #define I2C_CLIENT_END         0xfffeU
 
@@ -576,82 +568,4 @@ union i2c_smbus_data {
 #define I2C_SMBUS_BLOCK_PROC_CALL   7          /* SMBus 2.0 */
 #define I2C_SMBUS_I2C_BLOCK_DATA    8
 
-
-#ifdef __KERNEL__
-
-/* These defines are used for probing i2c client addresses */
-/* The length of the option lists */
-#define I2C_CLIENT_MAX_OPTS 48
-
-/* Default fill of many variables */
-#define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
-                            I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END}
-
-/* I2C_CLIENT_MODULE_PARM creates a module parameter, and puts it in the
-   module header */
-
-#define I2C_CLIENT_MODULE_PARM(var,desc) \
-  static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \
-  static unsigned int var##_num; \
-  module_param_array(var, short, &var##_num, 0); \
-  MODULE_PARM_DESC(var, desc)
-
-#define I2C_CLIENT_INSMOD_COMMON                                       \
-static const struct i2c_client_address_data addr_data = {              \
-       .normal_i2c     = normal_i2c,                                   \
-}
-
-/* These are the ones you want to use in your own drivers. Pick the one
-   which matches the number of devices the driver differenciates between. */
-#define I2C_CLIENT_INSMOD                                              \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_1(chip1)                                     \
-enum chips { any_chip, chip1 };                                                \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_2(chip1, chip2)                              \
-enum chips { any_chip, chip1, chip2 };                                 \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_3(chip1, chip2, chip3)                       \
-enum chips { any_chip, chip1, chip2, chip3 };                          \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_4(chip1, chip2, chip3, chip4)                        \
-enum chips { any_chip, chip1, chip2, chip3, chip4 };                   \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_5(chip1, chip2, chip3, chip4, chip5)         \
-enum chips { any_chip, chip1, chip2, chip3, chip4, chip5 };            \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_6(chip1, chip2, chip3, chip4, chip5, chip6)  \
-enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6 };     \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_7(chip1, chip2, chip3, chip4, chip5, chip6, chip7) \
-enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6,       \
-            chip7 };                                                   \
-I2C_CLIENT_INSMOD_COMMON
-
-#define I2C_CLIENT_INSMOD_8(chip1, chip2, chip3, chip4, chip5, chip6, chip7, chip8) \
-enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6,       \
-            chip7, chip8 };                                            \
-I2C_CLIENT_INSMOD_COMMON
-#endif /* __KERNEL__ */
 #endif /* _LINUX_I2C_H */
index 8d10aa7fd4c983c6efec569096f82092c837ea3d..8ed0abf06f898bc61d7c5d7ab6415ed3f7092664 100644 (file)
@@ -111,6 +111,12 @@ extern struct cred init_cred;
 # define INIT_PERF_EVENTS(tsk)
 #endif
 
+#ifdef CONFIG_FS_JOURNAL_INFO
+#define INIT_JOURNAL_INFO      .journal_info = NULL,
+#else
+#define INIT_JOURNAL_INFO
+#endif
+
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -162,7 +168,6 @@ extern struct cred init_cred;
                .signal = {{0}}},                                       \
        .blocked        = {{0}},                                        \
        .alloc_lock     = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock),         \
-       .journal_info   = NULL,                                         \
        .cpu_timers     = INIT_CPU_TIMERS(tsk.cpu_timers),              \
        .fs_excl        = ATOMIC_INIT(0),                               \
        .pi_lock        = __SPIN_LOCK_UNLOCKED(tsk.pi_lock),            \
@@ -173,6 +178,7 @@ extern struct cred init_cred;
                [PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),            \
        },                                                              \
        .dirties = INIT_PROP_LOCAL_SINGLE(dirties),                     \
+       INIT_JOURNAL_INFO                                               \
        INIT_IDS                                                        \
        INIT_PERF_EVENTS(tsk)                                           \
        INIT_TRACE_IRQFLAGS                                             \
index 792274269f2b38bfa288af12b87ec952f742966c..d8e9b3d1c23c3b698081c669e07484496d54960b 100644 (file)
@@ -107,18 +107,6 @@ static inline void print_symbol(const char *fmt, unsigned long addr)
                       __builtin_extract_return_addr((void *)addr));
 }
 
-/*
- * Pretty-print a function pointer.  This function is deprecated.
- * Please use the "%pF" vsprintf format instead.
- */
-static inline void __deprecated print_fn_descriptor_symbol(const char *fmt, void *addr)
-{
-#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
-       addr = *(void **)addr;
-#endif
-       print_symbol(fmt, (unsigned long)addr);
-}
-
 static inline void print_ip_sym(unsigned long ip)
 {
        printk("[<%p>] %pS\n", (void *) ip, (void *) ip);
index 3fa4c590cf12de7aef9dd89cf3af1c5dc9661c88..4d9c916d06d91b2686b42ca68b57c89837f65b9c 100644 (file)
@@ -251,10 +251,10 @@ extern int printk_delay_msec;
  * Print a one-time message (analogous to WARN_ONCE() et al):
  */
 #define printk_once(x...) ({                   \
-       static bool __print_once = true;        \
+       static bool __print_once;               \
                                                \
-       if (__print_once) {                     \
-               __print_once = false;           \
+       if (!__print_once) {                    \
+               __print_once = true;            \
                printk(x);                      \
        }                                       \
 })
@@ -397,14 +397,57 @@ static inline char *pack_hex_byte(char *buf, u8 byte)
        printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
 #elif defined(CONFIG_DYNAMIC_DEBUG)
 /* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
-#define pr_debug(fmt, ...) do { \
-       dynamic_pr_debug(fmt, ##__VA_ARGS__); \
-       } while (0)
+#define pr_debug(fmt, ...) \
+       dynamic_pr_debug(fmt, ##__VA_ARGS__)
 #else
 #define pr_debug(fmt, ...) \
        ({ if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); 0; })
 #endif
 
+/*
+ * ratelimited messages with local ratelimit_state,
+ * no local ratelimit_state used in the !PRINTK case
+ */
+#ifdef CONFIG_PRINTK
+#define printk_ratelimited(fmt, ...)  ({               \
+       static struct ratelimit_state _rs = {           \
+               .interval = DEFAULT_RATELIMIT_INTERVAL, \
+               .burst = DEFAULT_RATELIMIT_BURST,       \
+       };                                              \
+                                                       \
+       if (!__ratelimit(&_rs))                         \
+               printk(fmt, ##__VA_ARGS__);             \
+})
+#else
+/* No effect, but we still get type checking even in the !PRINTK case: */
+#define printk_ratelimited printk
+#endif
+
+#define pr_emerg_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_alert_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_crit_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_err_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warning_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_notice_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
+/* no pr_cont_ratelimited, don't do that... */
+/* If you are writing a driver, please use dev_dbg instead */
+#if defined(DEBUG)
+#define pr_debug_ratelimited(fmt, ...) \
+       printk_ratelimited(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define pr_debug_ratelimited(fmt, ...) \
+       ({ if (0) printk_ratelimited(KERN_DEBUG pr_fmt(fmt), \
+                                    ##__VA_ARGS__); 0; })
+#endif
+
 /*
  * General tracing related utility functions - trace_printk(),
  * tracing_on/tracing_off and tracing_start()/tracing_stop
index a485c14ecd5d52f892cbfe5ca3e22f0463c42a48..bed5f16ba8270ee742959f898e1e1fe7aea8970f 100644 (file)
@@ -9,8 +9,12 @@
 
 #include <linux/bitops.h>
 #include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/rmap.h>
 #include <linux/sched.h>
-#include <linux/vmstat.h>
+
+struct stable_node;
+struct mem_cgroup;
 
 #ifdef CONFIG_KSM
 int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
@@ -34,23 +38,60 @@ static inline void ksm_exit(struct mm_struct *mm)
 /*
  * A KSM page is one of those write-protected "shared pages" or "merged pages"
  * which KSM maps into multiple mms, wherever identical anonymous page content
- * is found in VM_MERGEABLE vmas.  It's a PageAnon page, with NULL anon_vma.
+ * is found in VM_MERGEABLE vmas.  It's a PageAnon page, pointing not to any
+ * anon_vma, but to that page's node of the stable tree.
  */
 static inline int PageKsm(struct page *page)
 {
-       return ((unsigned long)page->mapping == PAGE_MAPPING_ANON);
+       return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) ==
+                               (PAGE_MAPPING_ANON | PAGE_MAPPING_KSM);
+}
+
+static inline struct stable_node *page_stable_node(struct page *page)
+{
+       return PageKsm(page) ? page_rmapping(page) : NULL;
+}
+
+static inline void set_page_stable_node(struct page *page,
+                                       struct stable_node *stable_node)
+{
+       page->mapping = (void *)stable_node +
+                               (PAGE_MAPPING_ANON | PAGE_MAPPING_KSM);
 }
 
 /*
- * But we have to avoid the checking which page_add_anon_rmap() performs.
+ * When do_swap_page() first faults in from swap what used to be a KSM page,
+ * no problem, it will be assigned to this vma's anon_vma; but thereafter,
+ * it might be faulted into a different anon_vma (or perhaps to a different
+ * offset in the same anon_vma).  do_swap_page() cannot do all the locking
+ * needed to reconstitute a cross-anon_vma KSM page: for now it has to make
+ * a copy, and leave remerging the pages to a later pass of ksmd.
+ *
+ * We'd like to make this conditional on vma->vm_flags & VM_MERGEABLE,
+ * but what if the vma was unmerged while the page was swapped out?
  */
-static inline void page_add_ksm_rmap(struct page *page)
+struct page *ksm_does_need_to_copy(struct page *page,
+                       struct vm_area_struct *vma, unsigned long address);
+static inline struct page *ksm_might_need_to_copy(struct page *page,
+                       struct vm_area_struct *vma, unsigned long address)
 {
-       if (atomic_inc_and_test(&page->_mapcount)) {
-               page->mapping = (void *) PAGE_MAPPING_ANON;
-               __inc_zone_page_state(page, NR_ANON_PAGES);
-       }
+       struct anon_vma *anon_vma = page_anon_vma(page);
+
+       if (!anon_vma ||
+           (anon_vma == vma->anon_vma &&
+            page->index == linear_page_index(vma, address)))
+               return page;
+
+       return ksm_does_need_to_copy(page, vma, address);
 }
+
+int page_referenced_ksm(struct page *page,
+                       struct mem_cgroup *memcg, unsigned long *vm_flags);
+int try_to_unmap_ksm(struct page *page, enum ttu_flags flags);
+int rmap_walk_ksm(struct page *page, int (*rmap_one)(struct page *,
+                 struct vm_area_struct *, unsigned long, void *), void *arg);
+void ksm_migrate_page(struct page *newpage, struct page *oldpage);
+
 #else  /* !CONFIG_KSM */
 
 static inline int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
@@ -73,7 +114,32 @@ static inline int PageKsm(struct page *page)
        return 0;
 }
 
-/* No stub required for page_add_ksm_rmap(page) */
+static inline struct page *ksm_might_need_to_copy(struct page *page,
+                       struct vm_area_struct *vma, unsigned long address)
+{
+       return page;
+}
+
+static inline int page_referenced_ksm(struct page *page,
+                       struct mem_cgroup *memcg, unsigned long *vm_flags)
+{
+       return 0;
+}
+
+static inline int try_to_unmap_ksm(struct page *page, enum ttu_flags flags)
+{
+       return 0;
+}
+
+static inline int rmap_walk_ksm(struct page *page, int (*rmap_one)(struct page*,
+               struct vm_area_struct *, unsigned long, void *), void *arg)
+{
+       return 0;
+}
+
+static inline void ksm_migrate_page(struct page *newpage, struct page *oldpage)
+{
+}
 #endif /* !CONFIG_KSM */
 
-#endif
+#endif /* __LINUX_KSM_H */
index 3cc2f2c53e4c699abeefed5869c3ca8da1ef6481..f1ca0dcc162877513d922ee69e3b9ee2465df1d8 100644 (file)
@@ -43,6 +43,21 @@ struct lis3lv02d_platform_data {
 #define LIS3_WAKEUP_Z_HI       (1 << 5)
        unsigned char wakeup_flags;
        unsigned char wakeup_thresh;
+#define LIS3_NO_MAP            0
+#define LIS3_DEV_X             1
+#define LIS3_DEV_Y             2
+#define LIS3_DEV_Z             3
+#define LIS3_INV_DEV_X        -1
+#define LIS3_INV_DEV_Y        -2
+#define LIS3_INV_DEV_Z        -3
+       s8 axis_x;
+       s8 axis_y;
+       s8 axis_z;
+       int (*setup_resources)(void);
+       int (*release_resources)(void);
+       /* Limits for selftest are specified in chip data sheet */
+       s16 st_min_limits[3]; /* min pass limit x, y, z */
+       s16 st_max_limits[3]; /* max pass limit x, y, z */
 };
 
 #endif /* __LIS3LV02D_H_ */
index fed969281a418bde82db04885c2e3499f4e4c485..35b07b773e6cbf90589b90b69897bc2043f7495d 100644 (file)
@@ -69,7 +69,6 @@ extern void online_page(struct page *page);
 /* VM interface that may be used by firmware interface */
 extern int online_pages(unsigned long, unsigned long);
 extern void __offline_isolated_pages(unsigned long, unsigned long);
-extern int offline_pages(unsigned long, unsigned long, unsigned long);
 
 /* reasonably generic interface to expand the physical pages in a zone  */
 extern int __add_pages(int nid, struct zone *zone, unsigned long start_pfn,
index 085c903fe0f1832ce0f46debd8ffcd3f9d5d0564..1cc966cd3e5fff90b3cf893a2f91a6ec8747b0f7 100644 (file)
@@ -201,6 +201,7 @@ extern void mpol_fix_fork_child_flag(struct task_struct *p);
 extern struct zonelist *huge_zonelist(struct vm_area_struct *vma,
                                unsigned long addr, gfp_t gfp_flags,
                                struct mempolicy **mpol, nodemask_t **nodemask);
+extern bool init_nodemask_of_mempolicy(nodemask_t *mask);
 extern unsigned slab_node(struct mempolicy *policy);
 
 extern enum zone_type policy_zone;
@@ -328,6 +329,8 @@ static inline struct zonelist *huge_zonelist(struct vm_area_struct *vma,
        return node_zonelist(0, gfp_flags);
 }
 
+static inline bool init_nodemask_of_mempolicy(nodemask_t *m) { return false; }
+
 static inline int do_migrate_pages(struct mm_struct *mm,
                        const nodemask_t *from_nodes,
                        const nodemask_t *to_nodes, int flags)
index 527602cdea1c57211f028d0b81cff7d60934a85d..7f085c97c799dac22dadaf9844ae31424e14816d 100644 (file)
@@ -12,7 +12,8 @@ typedef struct page *new_page_t(struct page *, unsigned long private, int **);
 extern int putback_lru_pages(struct list_head *l);
 extern int migrate_page(struct address_space *,
                        struct page *, struct page *);
-extern int migrate_pages(struct list_head *l, new_page_t x, unsigned long);
+extern int migrate_pages(struct list_head *l, new_page_t x,
+                       unsigned long private, int offlining);
 
 extern int fail_migrate_page(struct address_space *,
                        struct page *, struct page *);
@@ -26,10 +27,7 @@ extern int migrate_vmas(struct mm_struct *mm,
 
 static inline int putback_lru_pages(struct list_head *l) { return 0; }
 static inline int migrate_pages(struct list_head *l, new_page_t x,
-               unsigned long private) { return -ENOSYS; }
-
-static inline int migrate_pages_to(struct list_head *pagelist,
-                       struct vm_area_struct *vma, int dest) { return 0; }
+               unsigned long private, int offlining) { return -ENOSYS; }
 
 static inline int migrate_prep(void) { return -ENOSYS; }
 
index 24c395694f4d3fbbc935287018e8dce157215afb..9d65ae4ba0e0b613717c5d9a1c7a3f15f09b0105 100644 (file)
@@ -620,13 +620,22 @@ void page_address_init(void);
 /*
  * On an anonymous page mapped into a user virtual memory area,
  * page->mapping points to its anon_vma, not to a struct address_space;
- * with the PAGE_MAPPING_ANON bit set to distinguish it.
+ * with the PAGE_MAPPING_ANON bit set to distinguish it.  See rmap.h.
+ *
+ * On an anonymous page in a VM_MERGEABLE area, if CONFIG_KSM is enabled,
+ * the PAGE_MAPPING_KSM bit may be set along with the PAGE_MAPPING_ANON bit;
+ * and then page->mapping points, not to an anon_vma, but to a private
+ * structure which KSM associates with that merged page.  See ksm.h.
+ *
+ * PAGE_MAPPING_KSM without PAGE_MAPPING_ANON is currently never used.
  *
  * Please note that, confusingly, "page_mapping" refers to the inode
  * address_space which maps the page from disk; whereas "page_mapped"
  * refers to user virtual address space into which the page is mapped.
  */
 #define PAGE_MAPPING_ANON      1
+#define PAGE_MAPPING_KSM       2
+#define PAGE_MAPPING_FLAGS     (PAGE_MAPPING_ANON | PAGE_MAPPING_KSM)
 
 extern struct address_space swapper_space;
 static inline struct address_space *page_mapping(struct page *page)
@@ -634,16 +643,19 @@ static inline struct address_space *page_mapping(struct page *page)
        struct address_space *mapping = page->mapping;
 
        VM_BUG_ON(PageSlab(page));
-#ifdef CONFIG_SWAP
        if (unlikely(PageSwapCache(page)))
                mapping = &swapper_space;
-       else
-#endif
-       if (unlikely((unsigned long)mapping & PAGE_MAPPING_ANON))
+       else if (unlikely((unsigned long)mapping & PAGE_MAPPING_ANON))
                mapping = NULL;
        return mapping;
 }
 
+/* Neutral page->mapping pointer to address_space or anon_vma or other */
+static inline void *page_rmapping(struct page *page)
+{
+       return (void *)((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS);
+}
+
 static inline int PageAnon(struct page *page)
 {
        return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
@@ -758,6 +770,7 @@ unsigned long unmap_vmas(struct mmu_gather **tlb,
  * @pmd_entry: if set, called for each non-empty PMD (3rd-level) entry
  * @pte_entry: if set, called for each non-empty PTE (4th-level) entry
  * @pte_hole: if set, called for each hole at all levels
+ * @hugetlb_entry: if set, called for each hugetlb entry
  *
  * (see walk_page_range for more details)
  */
@@ -767,6 +780,8 @@ struct mm_walk {
        int (*pmd_entry)(pmd_t *, unsigned long, unsigned long, struct mm_walk *);
        int (*pte_entry)(pte_t *, unsigned long, unsigned long, struct mm_walk *);
        int (*pte_hole)(unsigned long, unsigned long, struct mm_walk *);
+       int (*hugetlb_entry)(pte_t *, unsigned long, unsigned long,
+                            struct mm_walk *);
        struct mm_struct *mm;
        void *private;
 };
index 681a697b9a8624b0df39c21c9cda475203b91615..06292dac3eab15ea6f4b82ddc868e7f955889e7e 100644 (file)
 
 #include <linux/sysdev.h>
 #include <linux/cpumask.h>
+#include <linux/workqueue.h>
 
 struct node {
        struct sys_device       sysdev;
+
+#if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) && defined(CONFIG_HUGETLBFS)
+       struct work_struct      node_work;
+#endif
 };
 
 struct memory_block;
 extern struct node node_devices[];
+typedef  void (*node_registration_func_t)(struct node *);
 
 extern int register_node(struct node *, int, struct node *);
 extern void unregister_node(struct node *node);
@@ -39,6 +45,11 @@ extern int unregister_cpu_under_node(unsigned int cpu, unsigned int nid);
 extern int register_mem_sect_under_node(struct memory_block *mem_blk,
                                                int nid);
 extern int unregister_mem_sect_under_nodes(struct memory_block *mem_blk);
+
+#ifdef CONFIG_HUGETLBFS
+extern void register_hugetlbfs_with_node(node_registration_func_t doregister,
+                                        node_registration_func_t unregister);
+#endif
 #else
 static inline int register_one_node(int nid)
 {
@@ -65,6 +76,11 @@ static inline int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
 {
        return 0;
 }
+
+static inline void register_hugetlbfs_with_node(node_registration_func_t reg,
+                                               node_registration_func_t unreg)
+{
+}
 #endif
 
 #define to_node(sys_device) container_of(sys_device, struct node, sysdev)
index b359c4a9ec9e8decdaef46083c78d045dc5d156e..454997cccbd8c795aa0210dbd50bc0a7606e06d1 100644 (file)
@@ -245,14 +245,19 @@ static inline int __next_node(int n, const nodemask_t *srcp)
        return min_t(int,MAX_NUMNODES,find_next_bit(srcp->bits, MAX_NUMNODES, n+1));
 }
 
+static inline void init_nodemask_of_node(nodemask_t *mask, int node)
+{
+       nodes_clear(*mask);
+       node_set(node, *mask);
+}
+
 #define nodemask_of_node(node)                                         \
 ({                                                                     \
        typeof(_unused_nodemask_arg_) m;                                \
        if (sizeof(m) == sizeof(unsigned long)) {                       \
-               m.bits[0] = 1UL<<(node);                                \
+               m.bits[0] = 1UL << (node);                              \
        } else {                                                        \
-               nodes_clear(m);                                         \
-               node_set((node), m);                                    \
+               init_nodemask_of_node(&m, (node));                      \
        }                                                               \
        m;                                                              \
 })
@@ -480,15 +485,17 @@ static inline int num_node_state(enum node_states state)
 #define for_each_online_node(node) for_each_node_state(node, N_ONLINE)
 
 /*
- * For nodemask scrach area.(See CPUMASK_ALLOC() in cpumask.h)
+ * For nodemask scrach area.
+ * NODEMASK_ALLOC(type, name) allocates an object with a specified type and
+ * name.
  */
-
-#if NODES_SHIFT > 8 /* nodemask_t > 64 bytes */
-#define NODEMASK_ALLOC(x, m) struct x *m = kmalloc(sizeof(*m), GFP_KERNEL)
-#define NODEMASK_FREE(m) kfree(m)
+#if NODES_SHIFT > 8 /* nodemask_t > 256 bytes */
+#define NODEMASK_ALLOC(type, name, gfp_flags)  \
+                       type *name = kmalloc(sizeof(*name), gfp_flags)
+#define NODEMASK_FREE(m)                       kfree(m)
 #else
-#define NODEMASK_ALLOC(x, m) struct x _m, *m = &_m
-#define NODEMASK_FREE(m)
+#define NODEMASK_ALLOC(type, name, gfp_flags)  type _name, *name = &_name
+#define NODEMASK_FREE(m)                       do {} while (0)
 #endif
 
 /* A example struture for using NODEMASK_ALLOC, used in mempolicy. */
@@ -497,8 +504,10 @@ struct nodemask_scratch {
        nodemask_t      mask2;
 };
 
-#define NODEMASK_SCRATCH(x) NODEMASK_ALLOC(nodemask_scratch, x)
-#define NODEMASK_SCRATCH_FREE(x)  NODEMASK_FREE(x)
+#define NODEMASK_SCRATCH(x)                                            \
+                       NODEMASK_ALLOC(struct nodemask_scratch, x,      \
+                                       GFP_KERNEL | __GFP_NORETRY)
+#define NODEMASK_SCRATCH_FREE(x)       NODEMASK_FREE(x)
 
 
 #endif /* __LINUX_NODEMASK_H */
index a31a7301b159e8b1b8036a14cf734dd55ec0d0e2..3aaa31603a861a98d44f0d2b7862c582c589d934 100644 (file)
@@ -10,4 +10,6 @@
 
 #define MAX_NUMNODES    (1 << NODES_SHIFT)
 
+#define        NUMA_NO_NODE    (-1)
+
 #endif /* _LINUX_NUMA_H */
index 6b202b173955541adbe03ebd8137af6749f1c214..49e907bd067fb50f483e070b6bc656897aafad71 100644 (file)
@@ -99,7 +99,7 @@ enum pageflags {
        PG_buddy,               /* Page is free, on buddy lists */
        PG_swapbacked,          /* Page is backed by RAM/swap */
        PG_unevictable,         /* Page is "unevictable"  */
-#ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT
+#ifdef CONFIG_MMU
        PG_mlocked,             /* Page is vma mlocked */
 #endif
 #ifdef CONFIG_ARCH_USES_PG_UNCACHED
@@ -259,12 +259,10 @@ PAGEFLAG_FALSE(SwapCache)
 PAGEFLAG(Unevictable, unevictable) __CLEARPAGEFLAG(Unevictable, unevictable)
        TESTCLEARFLAG(Unevictable, unevictable)
 
-#ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT
-#define MLOCK_PAGES 1
+#ifdef CONFIG_MMU
 PAGEFLAG(Mlocked, mlocked) __CLEARPAGEFLAG(Mlocked, mlocked)
        TESTSCFLAG(Mlocked, mlocked) __TESTCLEARFLAG(Mlocked, mlocked)
 #else
-#define MLOCK_PAGES 0
 PAGEFLAG_FALSE(Mlocked) SETPAGEFLAG_NOOP(Mlocked)
        TESTCLEARFLAG_FALSE(Mlocked) __TESTCLEARFLAG_FALSE(Mlocked)
 #endif
@@ -393,7 +391,7 @@ static inline void __ClearPageTail(struct page *page)
 
 #endif /* !PAGEFLAGS_EXTENDED */
 
-#ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT
+#ifdef CONFIG_MMU
 #define __PG_MLOCKED           (1 << PG_mlocked)
 #else
 #define __PG_MLOCKED           0
index 0d65934246af8c3ba0997c05059009de684b27ea..198b8f9fe05ec2ab97d351fbfba053f980869100 100644 (file)
@@ -219,7 +219,7 @@ struct dev_pm_ops {
  * to RAM and hibernation.
  */
 #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
-struct dev_pm_ops name = { \
+const struct dev_pm_ops name = { \
        .suspend = suspend_fn, \
        .resume = resume_fn, \
        .freeze = suspend_fn, \
index cb0ba7032609d5602a709a54f4b012413b981587..b019ae64e2ab1d356daf92aaa7e92547227246c7 100644 (file)
@@ -26,6 +26,9 @@
  */
 struct anon_vma {
        spinlock_t lock;        /* Serialize access to vma list */
+#ifdef CONFIG_KSM
+       atomic_t ksm_refcount;
+#endif
        /*
         * NOTE: the LSB of the head.next is set by
         * mm_take_all_locks() _after_ taking the above lock. So the
@@ -38,6 +41,34 @@ struct anon_vma {
 };
 
 #ifdef CONFIG_MMU
+#ifdef CONFIG_KSM
+static inline void ksm_refcount_init(struct anon_vma *anon_vma)
+{
+       atomic_set(&anon_vma->ksm_refcount, 0);
+}
+
+static inline int ksm_refcount(struct anon_vma *anon_vma)
+{
+       return atomic_read(&anon_vma->ksm_refcount);
+}
+#else
+static inline void ksm_refcount_init(struct anon_vma *anon_vma)
+{
+}
+
+static inline int ksm_refcount(struct anon_vma *anon_vma)
+{
+       return 0;
+}
+#endif /* CONFIG_KSM */
+
+static inline struct anon_vma *page_anon_vma(struct page *page)
+{
+       if (((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) !=
+                                           PAGE_MAPPING_ANON)
+               return NULL;
+       return page_rmapping(page);
+}
 
 static inline void anon_vma_lock(struct vm_area_struct *vma)
 {
@@ -62,6 +93,7 @@ void __anon_vma_merge(struct vm_area_struct *, struct vm_area_struct *);
 void anon_vma_unlink(struct vm_area_struct *);
 void anon_vma_link(struct vm_area_struct *);
 void __anon_vma_link(struct vm_area_struct *);
+void anon_vma_free(struct anon_vma *);
 
 /*
  * rmap interfaces called when adding or removing pte of page
@@ -81,6 +113,9 @@ static inline void page_dup_rmap(struct page *page)
  */
 int page_referenced(struct page *, int is_locked,
                        struct mem_cgroup *cnt, unsigned long *vm_flags);
+int page_referenced_one(struct page *, struct vm_area_struct *,
+       unsigned long address, unsigned int *mapcount, unsigned long *vm_flags);
+
 enum ttu_flags {
        TTU_UNMAP = 0,                  /* unmap mode */
        TTU_MIGRATION = 1,              /* migration mode */
@@ -94,6 +129,8 @@ enum ttu_flags {
 #define TTU_ACTION(x) ((x) & TTU_ACTION_MASK)
 
 int try_to_unmap(struct page *, enum ttu_flags flags);
+int try_to_unmap_one(struct page *, struct vm_area_struct *,
+                       unsigned long address, enum ttu_flags flags);
 
 /*
  * Called from mm/filemap_xip.c to unmap empty zero page
@@ -127,6 +164,12 @@ struct anon_vma *page_lock_anon_vma(struct page *page);
 void page_unlock_anon_vma(struct anon_vma *anon_vma);
 int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma);
 
+/*
+ * Called by migrate.c to remove migration ptes, but might be used more later.
+ */
+int rmap_walk(struct page *page, int (*rmap_one)(struct page *,
+               struct vm_area_struct *, unsigned long, void *), void *arg);
+
 #else  /* !CONFIG_MMU */
 
 #define anon_vma_init()                do {} while (0)
index 6c3c0f6c261f456ae3b5058cc6a2bcfea200f9a9..bdfcc2527970d04ed2e3b3ee773519075d2da23a 100644 (file)
@@ -68,11 +68,7 @@ extern int __down_write_trylock(struct rw_semaphore *sem);
 extern void __up_read(struct rw_semaphore *sem);
 extern void __up_write(struct rw_semaphore *sem);
 extern void __downgrade_write(struct rw_semaphore *sem);
-
-static inline int rwsem_is_locked(struct rw_semaphore *sem)
-{
-       return (sem->activity != 0);
-}
+extern int rwsem_is_locked(struct rw_semaphore *sem);
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_RWSEM_SPINLOCK_H */
index 294eb2f8014411540d7be6cf233cd78b1391d5ef..7d388494f45d1399d3ab85adffe55e1104c973a4 100644 (file)
@@ -1446,8 +1446,10 @@ struct task_struct {
        gfp_t lockdep_reclaim_gfp;
 #endif
 
+#ifdef CONFIG_FS_JOURNAL_INFO
 /* journalling filesystem info */
        void *journal_info;
+#endif
 
 /* stacked block device info */
        struct bio *bio_list, **bio_tail;
index b8508868d5ad428ab0c220ee2b39296e92eceb89..651839a2a755ffa63aa862367fb502897df94e3c 100644 (file)
@@ -62,7 +62,15 @@ extern char * strnchr(const char *, size_t, int);
 #ifndef __HAVE_ARCH_STRRCHR
 extern char * strrchr(const char *,int);
 #endif
-extern char * __must_check strstrip(char *);
+extern char * __must_check skip_spaces(const char *);
+
+extern char *strim(char *);
+
+static inline __must_check char *strstrip(char *str)
+{
+       return strim(str);
+}
+
 #ifndef __HAVE_ARCH_STRSTR
 extern char * strstr(const char *,const char *);
 #endif
index 4ec90019c1a4c3997c8da80dd3d36f3b31d09b11..a2602a8207a615ab21abce5f8b0165f90664798d 100644 (file)
@@ -145,38 +145,43 @@ enum {
        SWP_DISCARDABLE = (1 << 2),     /* blkdev supports discard */
        SWP_DISCARDING  = (1 << 3),     /* now discarding a free cluster */
        SWP_SOLIDSTATE  = (1 << 4),     /* blkdev seeks are cheap */
+       SWP_CONTINUED   = (1 << 5),     /* swap_map has count continuation */
                                        /* add others here before... */
        SWP_SCANNING    = (1 << 8),     /* refcount in scan_swap_map */
 };
 
 #define SWAP_CLUSTER_MAX 32
 
-#define SWAP_MAP_MAX   0x7ffe
-#define SWAP_MAP_BAD   0x7fff
-#define SWAP_HAS_CACHE  0x8000         /* There is a swap cache of entry. */
-#define SWAP_COUNT_MASK (~SWAP_HAS_CACHE)
+#define SWAP_MAP_MAX   0x3e    /* Max duplication count, in first swap_map */
+#define SWAP_MAP_BAD   0x3f    /* Note pageblock is bad, in first swap_map */
+#define SWAP_HAS_CACHE 0x40    /* Flag page is cached, in first swap_map */
+#define SWAP_CONT_MAX  0x7f    /* Max count, in each swap_map continuation */
+#define COUNT_CONTINUED        0x80    /* See swap_map continuation for full count */
+#define SWAP_MAP_SHMEM 0xbf    /* Owned by shmem/tmpfs, in first swap_map */
+
 /*
  * The in-memory structure used to track swap areas.
  */
 struct swap_info_struct {
-       unsigned long flags;
-       int prio;                       /* swap priority */
-       int next;                       /* next entry on swap list */
-       struct file *swap_file;
-       struct block_device *bdev;
-       struct list_head extent_list;
-       struct swap_extent *curr_swap_extent;
-       unsigned short *swap_map;
-       unsigned int lowest_bit;
-       unsigned int highest_bit;
+       unsigned long   flags;          /* SWP_USED etc: see above */
+       signed short    prio;           /* swap priority of this type */
+       signed char     type;           /* strange name for an index */
+       signed char     next;           /* next type on the swap list */
+       unsigned int    max;            /* extent of the swap_map */
+       unsigned char *swap_map;        /* vmalloc'ed array of usage counts */
+       unsigned int lowest_bit;        /* index of first free in swap_map */
+       unsigned int highest_bit;       /* index of last free in swap_map */
+       unsigned int pages;             /* total of usable pages of swap */
+       unsigned int inuse_pages;       /* number of those currently in use */
+       unsigned int cluster_next;      /* likely index for next allocation */
+       unsigned int cluster_nr;        /* countdown to next cluster search */
        unsigned int lowest_alloc;      /* while preparing discard cluster */
        unsigned int highest_alloc;     /* while preparing discard cluster */
-       unsigned int cluster_next;
-       unsigned int cluster_nr;
-       unsigned int pages;
-       unsigned int max;
-       unsigned int inuse_pages;
-       unsigned int old_block_size;
+       struct swap_extent *curr_swap_extent;
+       struct swap_extent first_swap_extent;
+       struct block_device *bdev;      /* swap device or bdev of swap file */
+       struct file *swap_file;         /* seldom referenced */
+       unsigned int old_block_size;    /* seldom referenced */
 };
 
 struct swap_list_t {
@@ -273,6 +278,7 @@ extern int scan_unevictable_register_node(struct node *node);
 extern void scan_unevictable_unregister_node(struct node *node);
 
 extern int kswapd_run(int nid);
+extern void kswapd_stop(int nid);
 
 #ifdef CONFIG_MMU
 /* linux/mm/shmem.c */
@@ -309,17 +315,18 @@ extern long total_swap_pages;
 extern void si_swapinfo(struct sysinfo *);
 extern swp_entry_t get_swap_page(void);
 extern swp_entry_t get_swap_page_of_type(int);
-extern void swap_duplicate(swp_entry_t);
-extern int swapcache_prepare(swp_entry_t);
 extern int valid_swaphandles(swp_entry_t, unsigned long *);
+extern int add_swap_count_continuation(swp_entry_t, gfp_t);
+extern void swap_shmem_alloc(swp_entry_t);
+extern int swap_duplicate(swp_entry_t);
+extern int swapcache_prepare(swp_entry_t);
 extern void swap_free(swp_entry_t);
 extern void swapcache_free(swp_entry_t, struct page *page);
 extern int free_swap_and_cache(swp_entry_t);
 extern int swap_type_of(dev_t, sector_t, struct block_device **);
 extern unsigned int count_swap_pages(int, int);
-extern sector_t map_swap_page(struct swap_info_struct *, pgoff_t);
+extern sector_t map_swap_page(struct page *, struct block_device **);
 extern sector_t swapdev_block(int, pgoff_t);
-extern struct swap_info_struct *get_swap_info_struct(unsigned);
 extern int reuse_swap_page(struct page *);
 extern int try_to_free_swap(struct page *);
 struct backing_dev_info;
@@ -384,8 +391,18 @@ static inline void show_swap_cache_info(void)
 #define free_swap_and_cache(swp)       is_migration_entry(swp)
 #define swapcache_prepare(swp)         is_migration_entry(swp)
 
-static inline void swap_duplicate(swp_entry_t swp)
+static inline int add_swap_count_continuation(swp_entry_t swp, gfp_t gfp_mask)
 {
+       return 0;
+}
+
+static inline void swap_shmem_alloc(swp_entry_t swp)
+{
+}
+
+static inline int swap_duplicate(swp_entry_t swp)
+{
+       return 0;
 }
 
 static inline void swap_free(swp_entry_t swp)
index 405a9035fe40601114e02d77a98aa0090bcd8646..ef3a2947b1026a43dfebf0945132d0b1e1fc91c2 100644 (file)
@@ -350,8 +350,6 @@ extern void tty_write_flush(struct tty_struct *);
 
 extern struct ktermios tty_std_termios;
 
-extern int kmsg_redirect;
-
 extern void console_init(void);
 extern int vcs_init(void);
 
index d85889710f9b6a4fbf5040fd941c1cfca7847428..ee03bba9c5df8e9d0b0586fcfff5ef39e254c717 100644 (file)
@@ -40,6 +40,8 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
                PGSCAN_ZONE_RECLAIM_FAILED,
 #endif
                PGINODESTEAL, SLABS_SCANNED, KSWAPD_STEAL, KSWAPD_INODESTEAL,
+               KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY,
+               KSWAPD_SKIP_CONGESTION_WAIT,
                PAGEOUTRUN, ALLOCSTALL, PGROTATED,
 #ifdef CONFIG_HUGETLB_PAGE
                HTLB_BUDDY_PGALLOC, HTLB_BUDDY_PGALLOC_FAIL,
index 7ffa11f062324de1b79bb0d3093dac8523aaee0c..3fb9944e50a650f35075fd37ecb02cdf04d8e8aa 100644 (file)
@@ -84,4 +84,19 @@ struct vt_setactivate {
 
 #define VT_SETACTIVATE 0x560F  /* Activate and set the mode of a console */
 
+#ifdef CONFIG_VT_CONSOLE
+
+extern int vt_kmsg_redirect(int new);
+
+#else
+
+static inline int vt_kmsg_redirect(int new)
+{
+       return 0;
+}
+
+#endif
+
+#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1)
+
 #endif /* _LINUX_VT_H */
index 54c655ce9c049f56da32c4157917e0b6caffd48f..a23da9f0180341dd0cb8809f6108a2911336897a 100644 (file)
@@ -1079,6 +1079,28 @@ config SLOB
 
 endchoice
 
+config MMAP_ALLOW_UNINITIALIZED
+       bool "Allow mmapped anonymous memory to be uninitialized"
+       depends on EMBEDDED && !MMU
+       default n
+       help
+         Normally, and according to the Linux spec, anonymous memory obtained
+         from mmap() has it's contents cleared before it is passed to
+         userspace.  Enabling this config option allows you to request that
+         mmap() skip that if it is given an MAP_UNINITIALIZED flag, thus
+         providing a huge performance boost.  If this option is not enabled,
+         then the flag will be ignored.
+
+         This is taken advantage of by uClibc's malloc(), and also by
+         ELF-FDPIC binfmt's brk and stack allocator.
+
+         Because of the obvious security issues, this option should only be
+         enabled on embedded devices where you control what is run in
+         userspace.  Since that isn't generally a problem on no-MMU systems,
+         it is normally safe to say Y here.
+
+         See Documentation/nommu-mmap.txt for more information.
+
 config PROFILING
        bool "Profiling support (EXPERIMENTAL)"
        help
index 4051d75dd2d64e765b5d2856eac0da7fa796ef95..c3db4a98b3696609e3c3d3da3f7c51ea002e8e29 100644 (file)
@@ -691,10 +691,10 @@ asmlinkage void __init start_kernel(void)
 static void __init do_ctors(void)
 {
 #ifdef CONFIG_CONSTRUCTORS
-       ctor_fn_t *call = (ctor_fn_t *) __ctors_start;
+       ctor_fn_t *fn = (ctor_fn_t *) __ctors_start;
 
-       for (; call < (ctor_fn_t *) __ctors_end; call++)
-               (*call)();
+       for (; fn < (ctor_fn_t *) __ctors_end; fn++)
+               (*fn)();
 #endif
 }
 
@@ -755,10 +755,10 @@ extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
 
 static void __init do_initcalls(void)
 {
-       initcall_t *call;
+       initcall_t *fn;
 
-       for (call = __early_initcall_end; call < __initcall_end; call++)
-               do_one_initcall(*call);
+       for (fn = __early_initcall_end; fn < __initcall_end; fn++)
+               do_one_initcall(*fn);
 
        /* Make sure there is no pending stuff from the initcall sequence */
        flush_scheduled_work();
@@ -785,10 +785,10 @@ static void __init do_basic_setup(void)
 
 static void __init do_pre_smp_initcalls(void)
 {
-       initcall_t *call;
+       initcall_t *fn;
 
-       for (call = __initcall_start; call < __early_initcall_end; call++)
-               do_one_initcall(*call);
+       for (fn = __initcall_start; fn < __early_initcall_end; fn++)
+               do_one_initcall(*fn);
 }
 
 static void run_init_process(char *init_filename)
index 9a4715a2f6bf67ecfc35310bdf7e42ead2ee11c8..a6605ca921b6a6a9538ac645dadee177ef34f9b9 100644 (file)
@@ -536,7 +536,8 @@ static void do_acct_process(struct bsd_acct_struct *acct,
        do_div(elapsed, AHZ);
        ac.ac_btime = get_seconds() - elapsed;
        /* we really need to bite the bullet and change layout */
-       current_uid_gid(&ac.ac_uid, &ac.ac_gid);
+       ac.ac_uid = orig_cred->uid;
+       ac.ac_gid = orig_cred->gid;
 #if ACCT_VERSION==2
        ac.ac_ahz = AHZ;
 #endif
index d656c276508de6a9bbeb62407b59e477ff7ff61e..cf1b691831275f85247ebd93628b8015fac1c70d 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 
 #if 0
 #define DEBUGP printk
@@ -122,9 +123,7 @@ static char *next_arg(char *args, char **param, char **val)
                next = args + i;
 
        /* Chew up trailing spaces. */
-       while (isspace(*next))
-               next++;
-       return next;
+       return skip_spaces(next);
 }
 
 /* Args looks like "foo=bar,bar2 baz=fuz wiz". */
@@ -139,8 +138,7 @@ int parse_args(const char *name,
        DEBUGP("Parsing ARGS: %s\n", args);
 
        /* Chew leading spaces */
-       while (isspace(*args))
-               args++;
+       args = skip_spaces(args);
 
        while (*args) {
                int ret;
index 5187136fe1de961a4ceaee2e8729b9aaa39daa38..218e5af9015637af8808caad1aaf05c476428371 100644 (file)
@@ -6,7 +6,7 @@
 
 #include <linux/vt_kern.h>
 #include <linux/kbd_kern.h>
-#include <linux/console.h>
+#include <linux/vt.h>
 #include <linux/module.h>
 #include "power.h"
 
@@ -21,8 +21,7 @@ int pm_prepare_console(void)
        if (orig_fgconsole < 0)
                return 1;
 
-       orig_kmsg = kmsg_redirect;
-       kmsg_redirect = SUSPEND_CONSOLE;
+       orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
        return 0;
 }
 
@@ -30,7 +29,7 @@ void pm_restore_console(void)
 {
        if (orig_fgconsole >= 0) {
                vt_move_to_console(orig_fgconsole, 0);
-               kmsg_redirect = orig_kmsg;
+               vt_kmsg_redirect(orig_kmsg);
        }
 }
 #endif
index a8c76069cf5042008d5b85b957a132acbeacdad6..00a1d0ede532cad0b3acd9f0dad73c5ec26655d7 100644 (file)
@@ -171,7 +171,7 @@ void generic_exec_single(int cpu, struct call_single_data *data, int wait)
 void generic_smp_call_function_interrupt(void)
 {
        struct call_function_data *data;
-       int cpu = get_cpu();
+       int cpu = smp_processor_id();
 
        /*
         * Shouldn't receive this interrupt on a cpu that is not yet online.
@@ -212,7 +212,6 @@ void generic_smp_call_function_interrupt(void)
                csd_unlock(&data->csd);
        }
 
-       put_cpu();
 }
 
 /*
index 585d6cd10040b8a5bf93eafb6a3dc4893cc48cc6..20ccfb5da6af1bfe9d1eb784b601397a932dff66 100644 (file)
@@ -189,10 +189,10 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval)
                                 !(user = find_user(who)))
                                goto out_unlock;        /* No processes for this user */
 
-                       do_each_thread(g, p)
+                       do_each_thread(g, p) {
                                if (__task_cred(p)->uid == who)
                                        error = set_one_prio(p, niceval, error);
-                       while_each_thread(g, p);
+                       while_each_thread(g, p);
                        if (who != cred->uid)
                                free_uid(user);         /* For find_user() */
                        break;
@@ -252,13 +252,13 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who)
                                 !(user = find_user(who)))
                                goto out_unlock;        /* No processes for this user */
 
-                       do_each_thread(g, p)
+                       do_each_thread(g, p) {
                                if (__task_cred(p)->uid == who) {
                                        niceval = 20 - task_nice(p);
                                        if (niceval > retval)
                                                retval = niceval;
                                }
-                       while_each_thread(g, p);
+                       while_each_thread(g, p);
                        if (who != cred->uid)
                                free_uid(user);         /* for find_user() */
                        break;
index 554ac4894f0f893a6f1e26a06e3076510f6dffe9..45e4bef0012a6747d6608cbb78ba0f700a29d7cb 100644 (file)
@@ -1051,7 +1051,7 @@ static struct ctl_table vm_table[] = {
                .extra2         = &one_hundred,
        },
 #ifdef CONFIG_HUGETLB_PAGE
-        {
+       {
                .procname       = "nr_hugepages",
                .data           = NULL,
                .maxlen         = sizeof(unsigned long),
@@ -1059,7 +1059,18 @@ static struct ctl_table vm_table[] = {
                .proc_handler   = hugetlb_sysctl_handler,
                .extra1         = (void *)&hugetlb_zero,
                .extra2         = (void *)&hugetlb_infinity,
-        },
+       },
+#ifdef CONFIG_NUMA
+       {
+               .procname       = "nr_hugepages_mempolicy",
+               .data           = NULL,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = &hugetlb_mempolicy_sysctl_handler,
+               .extra1         = (void *)&hugetlb_zero,
+               .extra2         = (void *)&hugetlb_infinity,
+       },
+#endif
         {
                .procname       = "hugetlb_shm_group",
                .data           = &sysctl_hugetlb_shm_group,
@@ -1120,7 +1131,8 @@ static struct ctl_table vm_table[] = {
                .data           = &sysctl_max_map_count,
                .maxlen         = sizeof(sysctl_max_map_count),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec
+               .proc_handler   = proc_dointvec,
+               .extra1         = &zero,
        },
 #else
        {
index 2f22cf4576dbe719754d329adf18e041c19e2f19..8cf9938dd147840d1dbc67166fa30fc22208bf78 100644 (file)
@@ -575,7 +575,7 @@ config DEBUG_BUGVERBOSE
        depends on BUG
        depends on ARM || AVR32 || M32R || M68K || SPARC32 || SPARC64 || \
                   FRV || SUPERH || GENERIC_BUG || BLACKFIN || MN10300
-       default !EMBEDDED
+       default y
        help
          Say Y here to make BUG() panics output the file name and line number
          of the BUG call as well as the EIP and oops trace.  This aids
index 5205a8dae5bc08c51a4f0f283dd812e2fab25a04..4b1b083f219cd68fd0424ff6e4c6c84f6d685d25 100644 (file)
@@ -4,17 +4,10 @@
 
 #include <linux/kernel.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 
-static const char *skip_sep(const char *cp)
-{
-       while (*cp && isspace(*cp))
-               cp++;
-
-       return cp;
-}
-
 static const char *skip_arg(const char *cp)
 {
        while (*cp && !isspace(*cp))
@@ -28,7 +21,7 @@ static int count_argc(const char *str)
        int count = 0;
 
        while (*str) {
-               str = skip_sep(str);
+               str = skip_spaces(str);
                if (*str) {
                        count++;
                        str = skip_arg(str);
@@ -82,7 +75,7 @@ char **argv_split(gfp_t gfp, const char *str, int *argcp)
        argvp = argv;
 
        while (*str) {
-               str = skip_sep(str);
+               str = skip_spaces(str);
 
                if (*str) {
                        const char *p = str;
index 49d1c9e3ce3820ccb8dd3d01cec5e11ac39cd7cc..02e3b31b3a79003514138cf5c6a1cad19e3d3cf9 100644 (file)
@@ -42,6 +42,48 @@ MODULE_AUTHOR("Matt Domsch <Matt_Domsch@dell.com>");
 MODULE_DESCRIPTION("Ethernet CRC32 calculations");
 MODULE_LICENSE("GPL");
 
+#if CRC_LE_BITS == 8 || CRC_BE_BITS == 8
+
+static inline u32
+crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 *tab)
+{
+# ifdef __LITTLE_ENDIAN
+#  define DO_CRC(x) crc = tab[(crc ^ (x)) & 255 ] ^ (crc >> 8)
+# else
+#  define DO_CRC(x) crc = tab[((crc >> 24) ^ (x)) & 255] ^ (crc << 8)
+# endif
+       const u32 *b = (const u32 *)buf;
+       size_t    rem_len;
+
+       /* Align it */
+       if (unlikely((long)b & 3 && len)) {
+               u8 *p = (u8 *)b;
+               do {
+                       DO_CRC(*p++);
+               } while ((--len) && ((long)p)&3);
+               b = (u32 *)p;
+       }
+       rem_len = len & 3;
+       /* load data 32 bits wide, xor data 32 bits wide. */
+       len = len >> 2;
+       for (--b; len; --len) {
+               crc ^= *++b; /* use pre increment for speed */
+               DO_CRC(0);
+               DO_CRC(0);
+               DO_CRC(0);
+               DO_CRC(0);
+       }
+       len = rem_len;
+       /* And the last few bytes */
+       if (len) {
+               u8 *p = (u8 *)(b + 1) - 1;
+               do {
+                       DO_CRC(*++p); /* use pre increment for speed */
+               } while (--len);
+       }
+       return crc;
+}
+#endif
 /**
  * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
  * @crc: seed value for computation.  ~0 for Ethernet, sometimes 0 for
@@ -72,48 +114,10 @@ u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len)
 u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len)
 {
 # if CRC_LE_BITS == 8
-       const u32      *b =(u32 *)p;
        const u32      *tab = crc32table_le;
 
-# ifdef __LITTLE_ENDIAN
-#  define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)
-# else
-#  define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8)
-# endif
-
        crc = __cpu_to_le32(crc);
-       /* Align it */
-       if(unlikely(((long)b)&3 && len)){
-               do {
-                       u8 *p = (u8 *)b;
-                       DO_CRC(*p++);
-                       b = (void *)p;
-               } while ((--len) && ((long)b)&3 );
-       }
-       if(likely(len >= 4)){
-               /* load data 32 bits wide, xor data 32 bits wide. */
-               size_t save_len = len & 3;
-               len = len >> 2;
-               --b; /* use pre increment below(*++b) for speed */
-               do {
-                       crc ^= *++b;
-                       DO_CRC(0);
-                       DO_CRC(0);
-                       DO_CRC(0);
-                       DO_CRC(0);
-               } while (--len);
-               b++; /* point to next byte(s) */
-               len = save_len;
-       }
-       /* And the last few bytes */
-       if(len){
-               do {
-                       u8 *p = (u8 *)b;
-                       DO_CRC(*p++);
-                       b = (void *)p;
-               } while (--len);
-       }
-
+       crc = crc32_body(crc, p, len, tab);
        return __le32_to_cpu(crc);
 #undef ENDIAN_SHIFT
 #undef DO_CRC
@@ -170,47 +174,10 @@ u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len)
 u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len)
 {
 # if CRC_BE_BITS == 8
-       const u32      *b =(u32 *)p;
        const u32      *tab = crc32table_be;
 
-# ifdef __LITTLE_ENDIAN
-#  define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)
-# else
-#  define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8)
-# endif
-
        crc = __cpu_to_be32(crc);
-       /* Align it */
-       if(unlikely(((long)b)&3 && len)){
-               do {
-                       u8 *p = (u8 *)b;
-                       DO_CRC(*p++);
-                       b = (u32 *)p;
-               } while ((--len) && ((long)b)&3 );
-       }
-       if(likely(len >= 4)){
-               /* load data 32 bits wide, xor data 32 bits wide. */
-               size_t save_len = len & 3;
-               len = len >> 2;
-               --b; /* use pre increment below(*++b) for speed */
-               do {
-                       crc ^= *++b;
-                       DO_CRC(0);
-                       DO_CRC(0);
-                       DO_CRC(0);
-                       DO_CRC(0);
-               } while (--len);
-               b++; /* point to next byte(s) */
-               len = save_len;
-       }
-       /* And the last few bytes */
-       if(len){
-               do {
-                       u8 *p = (u8 *)b;
-                       DO_CRC(*p++);
-                       b = (void *)p;
-               } while (--len);
-       }
+       crc = crc32_body(crc, p, len, tab);
        return __be32_to_cpu(crc);
 #undef ENDIAN_SHIFT
 #undef DO_CRC
index d02ace14a322e5f85788238865ee70a6b1443e97..26baa620e95b5c57cdd127cb83f2db3f9449f8a2 100644 (file)
@@ -7,30 +7,30 @@
 #include <linux/ctype.h>
 #include <linux/module.h>
 
-unsigned char _ctype[] = {
-_C,_C,_C,_C,_C,_C,_C,_C,                       /* 0-7 */
-_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,                /* 8-15 */
-_C,_C,_C,_C,_C,_C,_C,_C,                       /* 16-23 */
-_C,_C,_C,_C,_C,_C,_C,_C,                       /* 24-31 */
-_S|_SP,_P,_P,_P,_P,_P,_P,_P,                   /* 32-39 */
-_P,_P,_P,_P,_P,_P,_P,_P,                       /* 40-47 */
-_D,_D,_D,_D,_D,_D,_D,_D,                       /* 48-55 */
-_D,_D,_P,_P,_P,_P,_P,_P,                       /* 56-63 */
-_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U,     /* 64-71 */
-_U,_U,_U,_U,_U,_U,_U,_U,                       /* 72-79 */
-_U,_U,_U,_U,_U,_U,_U,_U,                       /* 80-87 */
-_U,_U,_U,_P,_P,_P,_P,_P,                       /* 88-95 */
-_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L,     /* 96-103 */
-_L,_L,_L,_L,_L,_L,_L,_L,                       /* 104-111 */
-_L,_L,_L,_L,_L,_L,_L,_L,                       /* 112-119 */
-_L,_L,_L,_P,_P,_P,_P,_C,                       /* 120-127 */
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,               /* 128-143 */
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,               /* 144-159 */
-_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,   /* 160-175 */
-_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,       /* 176-191 */
-_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,       /* 192-207 */
-_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L,       /* 208-223 */
-_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,       /* 224-239 */
-_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L};      /* 240-255 */
+const unsigned char _ctype[] = {
+_C,_C,_C,_C,_C,_C,_C,_C,                               /* 0-7 */
+_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,                        /* 8-15 */
+_C,_C,_C,_C,_C,_C,_C,_C,                               /* 16-23 */
+_C,_C,_C,_C,_C,_C,_C,_C,                               /* 24-31 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,                           /* 32-39 */
+_P,_P,_P,_P,_P,_P,_P,_P,                               /* 40-47 */
+_D,_D,_D,_D,_D,_D,_D,_D,                               /* 48-55 */
+_D,_D,_P,_P,_P,_P,_P,_P,                               /* 56-63 */
+_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U,             /* 64-71 */
+_U,_U,_U,_U,_U,_U,_U,_U,                               /* 72-79 */
+_U,_U,_U,_U,_U,_U,_U,_U,                               /* 80-87 */
+_U,_U,_U,_P,_P,_P,_P,_P,                               /* 88-95 */
+_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L,             /* 96-103 */
+_L,_L,_L,_L,_L,_L,_L,_L,                               /* 104-111 */
+_L,_L,_L,_L,_L,_L,_L,_L,                               /* 112-119 */
+_L,_L,_L,_P,_P,_P,_P,_C,                               /* 120-127 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                       /* 128-143 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                       /* 144-159 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,   /* 160-175 */
+_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,       /* 176-191 */
+_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,       /* 192-207 */
+_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L,       /* 208-223 */
+_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,       /* 224-239 */
+_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L};      /* 240-255 */
 
 EXPORT_SYMBOL(_ctype);
index e22c148e4b7fff7605e605689561df66d018c6fa..f9350291598825a214068eb338e41f07edffb8c8 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/list.h>
 #include <linux/sysctl.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/uaccess.h>
 #include <linux/dynamic_debug.h>
 #include <linux/debugfs.h>
@@ -209,8 +210,7 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords)
                char *end;
 
                /* Skip leading whitespace */
-               while (*buf && isspace(*buf))
-                       buf++;
+               buf = skip_spaces(buf);
                if (!*buf)
                        break;  /* oh, it was trailing whitespace */
 
index b00d02059a5f8acc3a58a6290251aa487f2cca2e..fb34977246bb052923d5fad6341c2ba80315fdfe 100644 (file)
@@ -56,13 +56,16 @@ static int match_one(char *s, const char *p, substring_t args[])
 
                args[argc].from = s;
                switch (*p++) {
-               case 's':
-                       if (strlen(s) == 0)
+               case 's': {
+                       size_t str_len = strlen(s);
+
+                       if (str_len == 0)
                                return 0;
-                       else if (len == -1 || len > strlen(s))
-                               len = strlen(s);
+                       if (len == -1 || len > str_len)
+                               len = str_len;
                        args[argc].to = s + len;
                        break;
+               }
                case 'd':
                        simple_strtol(s, &args[argc].to, 0);
                        goto num;
index 9df3ca56db11455f39c436a6fa79f423b7e35b67..ccf95bff798439c7afead0834370864ff41a735c 100644 (file)
@@ -17,6 +17,19 @@ struct rwsem_waiter {
 #define RWSEM_WAITING_FOR_WRITE        0x00000002
 };
 
+int rwsem_is_locked(struct rw_semaphore *sem)
+{
+       int ret = 1;
+       unsigned long flags;
+
+       if (spin_trylock_irqsave(&sem->wait_lock, flags)) {
+               ret = (sem->activity != 0);
+               spin_unlock_irqrestore(&sem->wait_lock, flags);
+       }
+       return ret;
+}
+EXPORT_SYMBOL(rwsem_is_locked);
+
 /*
  * initialise the semaphore
  */
@@ -34,6 +47,7 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,
        spin_lock_init(&sem->wait_lock);
        INIT_LIST_HEAD(&sem->wait_list);
 }
+EXPORT_SYMBOL(__init_rwsem);
 
 /*
  * handle the lock release when processes blocked on it that can now run
@@ -305,12 +319,3 @@ void __downgrade_write(struct rw_semaphore *sem)
        spin_unlock_irqrestore(&sem->wait_lock, flags);
 }
 
-EXPORT_SYMBOL(__init_rwsem);
-EXPORT_SYMBOL(__down_read);
-EXPORT_SYMBOL(__down_read_trylock);
-EXPORT_SYMBOL(__down_write_nested);
-EXPORT_SYMBOL(__down_write);
-EXPORT_SYMBOL(__down_write_trylock);
-EXPORT_SYMBOL(__up_read);
-EXPORT_SYMBOL(__up_write);
-EXPORT_SYMBOL(__downgrade_write);
index e96421ab9a9a0a1a8ddd4496e32789acdc4b0749..afce96af3afdf7d6a0e1e343b4260c6f24765f74 100644 (file)
@@ -338,20 +338,34 @@ EXPORT_SYMBOL(strnchr);
 #endif
 
 /**
- * strstrip - Removes leading and trailing whitespace from @s.
+ * skip_spaces - Removes leading whitespace from @s.
+ * @s: The string to be stripped.
+ *
+ * Returns a pointer to the first non-whitespace character in @s.
+ */
+char *skip_spaces(const char *str)
+{
+       while (isspace(*str))
+               ++str;
+       return (char *)str;
+}
+EXPORT_SYMBOL(skip_spaces);
+
+/**
+ * strim - Removes leading and trailing whitespace from @s.
  * @s: The string to be stripped.
  *
  * Note that the first trailing whitespace is replaced with a %NUL-terminator
  * in the given string @s. Returns a pointer to the first non-whitespace
  * character in @s.
  */
-char *strstrip(char *s)
+char *strim(char *s)
 {
        size_t size;
        char *end;
 
+       s = skip_spaces(s);
        size = strlen(s);
-
        if (!size)
                return s;
 
@@ -360,12 +374,9 @@ char *strstrip(char *s)
                end--;
        *(end + 1) = '\0';
 
-       while (*s && isspace(*s))
-               s++;
-
        return s;
 }
-EXPORT_SYMBOL(strstrip);
+EXPORT_SYMBOL(strim);
 
 #ifndef __HAVE_ARCH_STRLEN
 /**
index 6438cd5599eefe583451b9b5bd858b9ab5a0ca38..735343fc857a7b3a6803a59e1ae3f33a7b5090e2 100644 (file)
@@ -9,7 +9,7 @@
  * Wirzenius wrote this portably, Torvalds fucked it up :-)
  */
 
-/* 
+/*
  * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
  * - changed to provide snprintf and vsnprintf functions
  * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
@@ -47,14 +47,14 @@ static unsigned int simple_guess_base(const char *cp)
 }
 
 /**
- * simple_strtoul - convert a string to an unsigned long
+ * simple_strtoull - convert a string to an unsigned long long
  * @cp: The start of the string
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  */
-unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
+unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
 {
-       unsigned long result = 0;
+       unsigned long long result = 0;
 
        if (!base)
                base = simple_guess_base(cp);
@@ -71,58 +71,39 @@ unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
                result = result * base + value;
                cp++;
        }
-
        if (endp)
                *endp = (char *)cp;
+
        return result;
 }
-EXPORT_SYMBOL(simple_strtoul);
+EXPORT_SYMBOL(simple_strtoull);
 
 /**
- * simple_strtol - convert a string to a signed long
+ * simple_strtoul - convert a string to an unsigned long
  * @cp: The start of the string
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  */
-long simple_strtol(const char *cp, char **endp, unsigned int base)
+unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
 {
-       if(*cp == '-')
-               return -simple_strtoul(cp + 1, endp, base);
-       return simple_strtoul(cp, endp, base);
+       return simple_strtoull(cp, endp, base);
 }
-EXPORT_SYMBOL(simple_strtol);
+EXPORT_SYMBOL(simple_strtoul);
 
 /**
- * simple_strtoull - convert a string to an unsigned long long
+ * simple_strtol - convert a string to a signed long
  * @cp: The start of the string
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  */
-unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
+long simple_strtol(const char *cp, char **endp, unsigned int base)
 {
-       unsigned long long result = 0;
-
-       if (!base)
-               base = simple_guess_base(cp);
-
-       if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x')
-               cp += 2;
-
-       while (isxdigit(*cp)) {
-               unsigned int value;
-
-               value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10;
-               if (value >= base)
-                       break;
-               result = result * base + value;
-               cp++;
-       }
+       if (*cp == '-')
+               return -simple_strtoul(cp + 1, endp, base);
 
-       if (endp)
-               *endp = (char *)cp;
-       return result;
+       return simple_strtoul(cp, endp, base);
 }
-EXPORT_SYMBOL(simple_strtoull);
+EXPORT_SYMBOL(simple_strtol);
 
 /**
  * simple_strtoll - convert a string to a signed long long
@@ -132,8 +113,9 @@ EXPORT_SYMBOL(simple_strtoull);
  */
 long long simple_strtoll(const char *cp, char **endp, unsigned int base)
 {
-       if(*cp=='-')
+       if (*cp == '-')
                return -simple_strtoull(cp + 1, endp, base);
+
        return simple_strtoull(cp, endp, base);
 }
 
@@ -173,6 +155,7 @@ int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
        val = simple_strtoul(cp, &tail, base);
        if (tail == cp)
                return -EINVAL;
+
        if ((*tail == '\0') ||
                ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
                *res = val;
@@ -285,10 +268,11 @@ EXPORT_SYMBOL(strict_strtoll);
 
 static int skip_atoi(const char **s)
 {
-       int i=0;
+       int i = 0;
 
        while (isdigit(**s))
                i = i*10 + *((*s)++) - '0';
+
        return i;
 }
 
@@ -302,7 +286,7 @@ static int skip_atoi(const char **s)
 /* Formats correctly any integer in [0,99999].
  * Outputs from one to five digits depending on input.
  * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */
-static charput_dec_trunc(char *buf, unsigned q)
+static char *put_dec_trunc(char *buf, unsigned q)
 {
        unsigned d3, d2, d1, d0;
        d1 = (q>>4) & 0xf;
@@ -331,14 +315,15 @@ static char* put_dec_trunc(char *buf, unsigned q)
                                d3 = d3 - 10*q;
                                *buf++ = d3 + '0';  /* next digit */
                                if (q != 0)
-                                       *buf++ = q + '0';  /* most sign. digit */
+                                       *buf++ = q + '0'; /* most sign. digit */
                        }
                }
        }
+
        return buf;
 }
 /* Same with if's removed. Always emits five digits */
-static charput_dec_full(char *buf, unsigned q)
+static char *put_dec_full(char *buf, unsigned q)
 {
        /* BTW, if q is in [0,9999], 8-bit ints will be enough, */
        /* but anyway, gcc produces better code with full-sized ints */
@@ -347,14 +332,15 @@ static char* put_dec_full(char *buf, unsigned q)
        d2 = (q>>8) & 0xf;
        d3 = (q>>12);
 
-       /* Possible ways to approx. divide by 10 */
-       /* gcc -O2 replaces multiply with shifts and adds */
-       // (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
-       // (x * 0x67) >> 10:  1100111
-       // (x * 0x34) >> 9:    110100 - same
-       // (x * 0x1a) >> 8:     11010 - same
-       // (x * 0x0d) >> 7:      1101 - same, shortest code (on i386)
-
+       /*
+        * Possible ways to approx. divide by 10
+        * gcc -O2 replaces multiply with shifts and adds
+        * (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
+        * (x * 0x67) >> 10:  1100111
+        * (x * 0x34) >> 9:    110100 - same
+        * (x * 0x1a) >> 8:     11010 - same
+        * (x * 0x0d) >> 7:      1101 - same, shortest code (on i386)
+        */
        d0 = 6*(d3 + d2 + d1) + (q & 0xf);
        q = (d0 * 0xcd) >> 11;
        d0 = d0 - 10*q;
@@ -375,10 +361,11 @@ static char* put_dec_full(char *buf, unsigned q)
                                d3 = d3 - 10*q;
                                *buf++ = d3 + '0';
                                        *buf++ = q + '0';
+
        return buf;
 }
 /* No inlining helps gcc to use registers better */
-static noinline charput_dec(char *buf, unsigned long long num)
+static noinline char *put_dec(char *buf, unsigned long long num)
 {
        while (1) {
                unsigned rem;
@@ -448,9 +435,9 @@ static char *number(char *buf, char *end, unsigned long long num,
                spec.flags &= ~ZEROPAD;
        sign = 0;
        if (spec.flags & SIGN) {
-               if ((signed long long) num < 0) {
+               if ((signed long long)num < 0) {
                        sign = '-';
-                       num = - (signed long long) num;
+                       num = -(signed long long)num;
                        spec.field_width--;
                } else if (spec.flags & PLUS) {
                        sign = '+';
@@ -478,7 +465,9 @@ static char *number(char *buf, char *end, unsigned long long num,
        else if (spec.base != 10) { /* 8 or 16 */
                int mask = spec.base - 1;
                int shift = 3;
-               if (spec.base == 16) shift = 4;
+
+               if (spec.base == 16)
+                       shift = 4;
                do {
                        tmp[i++] = (digits[((unsigned char)num) & mask] | locase);
                        num >>= shift;
@@ -493,7 +482,7 @@ static char *number(char *buf, char *end, unsigned long long num,
        /* leading space padding */
        spec.field_width -= spec.precision;
        if (!(spec.flags & (ZEROPAD+LEFT))) {
-               while(--spec.field_width >= 0) {
+               while (--spec.field_width >= 0) {
                        if (buf < end)
                                *buf = ' ';
                        ++buf;
@@ -543,15 +532,16 @@ static char *number(char *buf, char *end, unsigned long long num,
                        *buf = ' ';
                ++buf;
        }
+
        return buf;
 }
 
-static char *string(char *buf, char *end, char *s, struct printf_spec spec)
+static char *string(char *buf, char *end, const char *s, struct printf_spec spec)
 {
        int len, i;
 
        if ((unsigned long)s < PAGE_SIZE)
-               s = "<NULL>";
+               s = "(null)";
 
        len = strnlen(s, spec.precision);
 
@@ -572,6 +562,7 @@ static char *string(char *buf, char *end, char *s, struct printf_spec spec)
                        *buf = ' ';
                ++buf;
        }
+
        return buf;
 }
 
@@ -585,11 +576,13 @@ static char *symbol_string(char *buf, char *end, void *ptr,
                sprint_symbol(sym, value);
        else
                kallsyms_lookup(value, NULL, NULL, NULL, sym);
+
        return string(buf, end, sym, spec);
 #else
-       spec.field_width = 2*sizeof(void *);
+       spec.field_width = 2 * sizeof(void *);
        spec.flags |= SPECIAL | SMALL | ZEROPAD;
        spec.base = 16;
+
        return number(buf, end, value, spec);
 #endif
 }
@@ -718,22 +711,19 @@ static char *ip4_string(char *p, const u8 *addr, bool leading_zeros)
                if (i < 3)
                        *p++ = '.';
        }
-
        *p = '\0';
+
        return p;
 }
 
 static char *ip6_compressed_string(char *p, const char *addr)
 {
-       int i;
-       int j;
-       int range;
+       int i, j, range;
        unsigned char zerolength[8];
        int longest = 1;
        int colonpos = -1;
        u16 word;
-       u8 hi;
-       u8 lo;
+       u8 hi, lo;
        bool needcolon = false;
        bool useIPv4;
        struct in6_addr in6;
@@ -787,8 +777,9 @@ static char *ip6_compressed_string(char *p, const char *addr)
                                p = pack_hex_byte(p, hi);
                        else
                                *p++ = hex_asc_lo(hi);
+                       p = pack_hex_byte(p, lo);
                }
-               if (hi || lo > 0x0f)
+               else if (lo > 0x0f)
                        p = pack_hex_byte(p, lo);
                else
                        *p++ = hex_asc_lo(lo);
@@ -800,22 +791,23 @@ static char *ip6_compressed_string(char *p, const char *addr)
                        *p++ = ':';
                p = ip4_string(p, &in6.s6_addr[12], false);
        }
-
        *p = '\0';
+
        return p;
 }
 
 static char *ip6_string(char *p, const char *addr, const char *fmt)
 {
        int i;
+
        for (i = 0; i < 8; i++) {
                p = pack_hex_byte(p, *addr++);
                p = pack_hex_byte(p, *addr++);
                if (fmt[0] == 'I' && i != 7)
                        *p++ = ':';
        }
-
        *p = '\0';
+
        return p;
 }
 
@@ -842,6 +834,52 @@ static char *ip4_addr_string(char *buf, char *end, const u8 *addr,
        return string(buf, end, ip4_addr, spec);
 }
 
+static char *uuid_string(char *buf, char *end, const u8 *addr,
+                        struct printf_spec spec, const char *fmt)
+{
+       char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")];
+       char *p = uuid;
+       int i;
+       static const u8 be[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+       static const u8 le[16] = {3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15};
+       const u8 *index = be;
+       bool uc = false;
+
+       switch (*(++fmt)) {
+       case 'L':
+               uc = true;              /* fall-through */
+       case 'l':
+               index = le;
+               break;
+       case 'B':
+               uc = true;
+               break;
+       }
+
+       for (i = 0; i < 16; i++) {
+               p = pack_hex_byte(p, addr[index[i]]);
+               switch (i) {
+               case 3:
+               case 5:
+               case 7:
+               case 9:
+                       *p++ = '-';
+                       break;
+               }
+       }
+
+       *p = 0;
+
+       if (uc) {
+               p = uuid;
+               do {
+                       *p = toupper(*p);
+               } while (*(++p));
+       }
+
+       return string(buf, end, uuid, spec);
+}
+
 /*
  * Show a '%p' thing.  A kernel extension is that the '%p' is followed
  * by an extra set of alphanumeric characters that are extended format
@@ -866,6 +904,18 @@ static char *ip4_addr_string(char *buf, char *end, const u8 *addr,
  *       IPv4 uses dot-separated decimal with leading 0's (010.123.045.006)
  * - 'I6c' for IPv6 addresses printed as specified by
  *       http://www.ietf.org/id/draft-kawamura-ipv6-text-representation-03.txt
+ * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
+ *       "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ *       Options for %pU are:
+ *         b big endian lower case hex (default)
+ *         B big endian UPPER case hex
+ *         l little endian lower case hex
+ *         L little endian UPPER case hex
+ *           big endian output byte order is:
+ *             [0][1][2][3]-[4][5]-[6][7]-[8][9]-[10][11][12][13][14][15]
+ *           little endian output byte order is:
+ *             [3][2][1][0]-[5][4]-[7][6]-[8][9]-[10][11][12][13][14][15]
+ *
  * Note: The difference between 'S' and 'F' is that on ia64 and ppc64
  * function pointers are really function descriptors, which contain a
  * pointer to the real address.
@@ -880,9 +930,9 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
        case 'F':
        case 'f':
                ptr = dereference_function_descriptor(ptr);
-       case 's':
                /* Fallthrough */
        case 'S':
+       case 's':
                return symbol_string(buf, end, ptr, spec, *fmt);
        case 'R':
        case 'r':
@@ -906,6 +956,8 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                        return ip4_addr_string(buf, end, ptr, spec, fmt);
                }
                break;
+       case 'U':
+               return uuid_string(buf, end, ptr, spec, fmt);
        }
        spec.flags |= SMALL;
        if (spec.field_width == -1) {
@@ -1023,8 +1075,8 @@ precision:
 qualifier:
        /* get the conversion qualifier */
        spec->qualifier = -1;
-       if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
-           *fmt == 'Z' || *fmt == 'z' || *fmt == 't') {
+       if (*fmt == 'h' || TOLOWER(*fmt) == 'l' ||
+           TOLOWER(*fmt) == 'z' || *fmt == 't') {
                spec->qualifier = *fmt++;
                if (unlikely(spec->qualifier == *fmt)) {
                        if (spec->qualifier == 'l') {
@@ -1091,7 +1143,7 @@ qualifier:
                        spec->type = FORMAT_TYPE_LONG;
                else
                        spec->type = FORMAT_TYPE_ULONG;
-       } else if (spec->qualifier == 'Z' || spec->qualifier == 'z') {
+       } else if (TOLOWER(spec->qualifier) == 'z') {
                spec->type = FORMAT_TYPE_SIZE_T;
        } else if (spec->qualifier == 't') {
                spec->type = FORMAT_TYPE_PTRDIFF;
@@ -1144,8 +1196,7 @@ qualifier:
 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 {
        unsigned long long num;
-       char *str, *end, c;
-       int read;
+       char *str, *end;
        struct printf_spec spec = {0};
 
        /* Reject out-of-range values early.  Large positive sizes are
@@ -1164,8 +1215,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 
        while (*fmt) {
                const char *old_fmt = fmt;
-
-               read = format_decode(fmt, &spec);
+               int read = format_decode(fmt, &spec);
 
                fmt += read;
 
@@ -1189,7 +1239,9 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                        spec.precision = va_arg(args, int);
                        break;
 
-               case FORMAT_TYPE_CHAR:
+               case FORMAT_TYPE_CHAR: {
+                       char c;
+
                        if (!(spec.flags & LEFT)) {
                                while (--spec.field_width > 0) {
                                        if (str < end)
@@ -1208,6 +1260,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                                ++str;
                        }
                        break;
+               }
 
                case FORMAT_TYPE_STR:
                        str = string(str, end, va_arg(args, char *), spec);
@@ -1238,8 +1291,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                        if (qualifier == 'l') {
                                long *ip = va_arg(args, long *);
                                *ip = (str - buf);
-                       } else if (qualifier == 'Z' ||
-                                       qualifier == 'z') {
+                       } else if (TOLOWER(qualifier) == 'z') {
                                size_t *ip = va_arg(args, size_t *);
                                *ip = (str - buf);
                        } else {
@@ -1322,7 +1374,8 @@ int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
 {
        int i;
 
-       i=vsnprintf(buf,size,fmt,args);
+       i = vsnprintf(buf, size, fmt, args);
+
        return (i >= size) ? (size - 1) : i;
 }
 EXPORT_SYMBOL(vscnprintf);
@@ -1341,14 +1394,15 @@ EXPORT_SYMBOL(vscnprintf);
  *
  * See the vsnprintf() documentation for format string extensions over C99.
  */
-int snprintf(char * buf, size_t size, const char *fmt, ...)
+int snprintf(char *buf, size_t size, const char *fmt, ...)
 {
        va_list args;
        int i;
 
        va_start(args, fmt);
-       i=vsnprintf(buf,size,fmt,args);
+       i = vsnprintf(buf, size, fmt, args);
        va_end(args);
+
        return i;
 }
 EXPORT_SYMBOL(snprintf);
@@ -1364,7 +1418,7 @@ EXPORT_SYMBOL(snprintf);
  * the trailing '\0'. If @size is <= 0 the function returns 0.
  */
 
-int scnprintf(char * buf, size_t size, const char *fmt, ...)
+int scnprintf(char *buf, size_t size, const char *fmt, ...)
 {
        va_list args;
        int i;
@@ -1372,6 +1426,7 @@ int scnprintf(char * buf, size_t size, const char *fmt, ...)
        va_start(args, fmt);
        i = vsnprintf(buf, size, fmt, args);
        va_end(args);
+
        return (i >= size) ? (size - 1) : i;
 }
 EXPORT_SYMBOL(scnprintf);
@@ -1409,14 +1464,15 @@ EXPORT_SYMBOL(vsprintf);
  *
  * See the vsnprintf() documentation for format string extensions over C99.
  */
-int sprintf(char * buf, const char *fmt, ...)
+int sprintf(char *buf, const char *fmt, ...)
 {
        va_list args;
        int i;
 
        va_start(args, fmt);
-       i=vsnprintf(buf, INT_MAX, fmt, args);
+       i = vsnprintf(buf, INT_MAX, fmt, args);
        va_end(args);
+
        return i;
 }
 EXPORT_SYMBOL(sprintf);
@@ -1449,7 +1505,6 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
 {
        struct printf_spec spec = {0};
        char *str, *end;
-       int read;
 
        str = (char *)bin_buf;
        end = (char *)(bin_buf + size);
@@ -1474,14 +1529,15 @@ do {                                                                    \
        str += sizeof(type);                                            \
 } while (0)
 
-
        while (*fmt) {
-               read = format_decode(fmt, &spec);
+               int read = format_decode(fmt, &spec);
 
                fmt += read;
 
                switch (spec.type) {
                case FORMAT_TYPE_NONE:
+               case FORMAT_TYPE_INVALID:
+               case FORMAT_TYPE_PERCENT_CHAR:
                        break;
 
                case FORMAT_TYPE_WIDTH:
@@ -1496,13 +1552,14 @@ do {                                                                    \
                case FORMAT_TYPE_STR: {
                        const char *save_str = va_arg(args, char *);
                        size_t len;
+
                        if ((unsigned long)save_str > (unsigned long)-PAGE_SIZE
                                        || (unsigned long)save_str < PAGE_SIZE)
-                               save_str = "<NULL>";
-                       len = strlen(save_str);
-                       if (str + len + 1 < end)
-                               memcpy(str, save_str, len + 1);
-                       str += len + 1;
+                               save_str = "(null)";
+                       len = strlen(save_str) + 1;
+                       if (str + len < end)
+                               memcpy(str, save_str, len);
+                       str += len;
                        break;
                }
 
@@ -1513,19 +1570,13 @@ do {                                                                    \
                                fmt++;
                        break;
 
-               case FORMAT_TYPE_PERCENT_CHAR:
-                       break;
-
-               case FORMAT_TYPE_INVALID:
-                       break;
-
                case FORMAT_TYPE_NRCHARS: {
                        /* skip %n 's argument */
                        int qualifier = spec.qualifier;
                        void *skip_arg;
                        if (qualifier == 'l')
                                skip_arg = va_arg(args, long *);
-                       else if (qualifier == 'Z' || qualifier == 'z')
+                       else if (TOLOWER(qualifier) == 'z')
                                skip_arg = va_arg(args, size_t *);
                        else
                                skip_arg = va_arg(args, int *);
@@ -1561,8 +1612,8 @@ do {                                                                      \
                        }
                }
        }
-       return (u32 *)(PTR_ALIGN(str, sizeof(u32))) - bin_buf;
 
+       return (u32 *)(PTR_ALIGN(str, sizeof(u32))) - bin_buf;
 #undef save_arg
 }
 EXPORT_SYMBOL_GPL(vbin_printf);
@@ -1591,11 +1642,9 @@ EXPORT_SYMBOL_GPL(vbin_printf);
  */
 int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 {
-       unsigned long long num;
-       char *str, *end, c;
-       const char *args = (const char *)bin_buf;
-
        struct printf_spec spec = {0};
+       char *str, *end;
+       const char *args = (const char *)bin_buf;
 
        if (WARN_ON_ONCE((int) size < 0))
                return 0;
@@ -1625,10 +1674,8 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
        }
 
        while (*fmt) {
-               int read;
                const char *old_fmt = fmt;
-
-               read = format_decode(fmt, &spec);
+               int read = format_decode(fmt, &spec);
 
                fmt += read;
 
@@ -1652,7 +1699,9 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
                        spec.precision = get_arg(int);
                        break;
 
-               case FORMAT_TYPE_CHAR:
+               case FORMAT_TYPE_CHAR: {
+                       char c;
+
                        if (!(spec.flags & LEFT)) {
                                while (--spec.field_width > 0) {
                                        if (str < end)
@@ -1670,11 +1719,11 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
                                ++str;
                        }
                        break;
+               }
 
                case FORMAT_TYPE_STR: {
                        const char *str_arg = args;
-                       size_t len = strlen(str_arg);
-                       args += len + 1;
+                       args += strlen(str_arg) + 1;
                        str = string(str, end, (char *)str_arg, spec);
                        break;
                }
@@ -1686,11 +1735,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
                        break;
 
                case FORMAT_TYPE_PERCENT_CHAR:
-                       if (str < end)
-                               *str = '%';
-                       ++str;
-                       break;
-
                case FORMAT_TYPE_INVALID:
                        if (str < end)
                                *str = '%';
@@ -1701,15 +1745,15 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
                        /* skip */
                        break;
 
-               default:
+               default: {
+                       unsigned long long num;
+
                        switch (spec.type) {
 
                        case FORMAT_TYPE_LONG_LONG:
                                num = get_arg(long long);
                                break;
                        case FORMAT_TYPE_ULONG:
-                               num = get_arg(unsigned long);
-                               break;
                        case FORMAT_TYPE_LONG:
                                num = get_arg(unsigned long);
                                break;
@@ -1739,8 +1783,9 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
                        }
 
                        str = number(str, end, num, spec);
-               }
-       }
+               } /* default: */
+               } /* switch(spec.type) */
+       } /* while(*fmt) */
 
        if (size > 0) {
                if (str < end)
@@ -1774,6 +1819,7 @@ int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...)
        va_start(args, fmt);
        ret = vbin_printf(bin_buf, size, fmt, args);
        va_end(args);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(bprintf);
@@ -1786,27 +1832,23 @@ EXPORT_SYMBOL_GPL(bprintf);
  * @fmt:       format of buffer
  * @args:      arguments
  */
-int vsscanf(const char * buf, const char * fmt, va_list args)
+int vsscanf(const char *buf, const char *fmt, va_list args)
 {
        const char *str = buf;
        char *next;
        char digit;
        int num = 0;
-       int qualifier;
-       int base;
-       int field_width;
-       int is_sign = 0;
+       int qualifier, base, field_width;
+       bool is_sign;
 
-       while(*fmt && *str) {
+       while (*fmt && *str) {
                /* skip any white space in format */
                /* white space in format matchs any amount of
                 * white space, including none, in the input.
                 */
                if (isspace(*fmt)) {
-                       while (isspace(*fmt))
-                               ++fmt;
-                       while (isspace(*str))
-                               ++str;
+                       fmt = skip_spaces(++fmt);
+                       str = skip_spaces(str);
                }
 
                /* anything that is not a conversion must match exactly */
@@ -1819,7 +1861,7 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
                if (!*fmt)
                        break;
                ++fmt;
-               
+
                /* skip this conversion.
                 * advance both strings to next white space
                 */
@@ -1838,8 +1880,8 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
 
                /* get conversion qualifier */
                qualifier = -1;
-               if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
-                   *fmt == 'Z' || *fmt == 'z') {
+               if (*fmt == 'h' || TOLOWER(*fmt) == 'l' ||
+                   TOLOWER(*fmt) == 'z') {
                        qualifier = *fmt++;
                        if (unlikely(qualifier == *fmt)) {
                                if (qualifier == 'h') {
@@ -1851,16 +1893,17 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
                                }
                        }
                }
-               base = 10;
-               is_sign = 0;
 
                if (!*fmt || !*str)
                        break;
 
-               switch(*fmt++) {
+               base = 10;
+               is_sign = 0;
+
+               switch (*fmt++) {
                case 'c':
                {
-                       char *s = (char *) va_arg(args,char*);
+                       char *s = (char *)va_arg(args, char*);
                        if (field_width == -1)
                                field_width = 1;
                        do {
@@ -1871,17 +1914,15 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
                continue;
                case 's':
                {
-                       char *s = (char *) va_arg(args, char *);
-                       if(field_width == -1)
+                       char *s = (char *)va_arg(args, char *);
+                       if (field_width == -1)
                                field_width = INT_MAX;
                        /* first, skip leading white space in buffer */
-                       while (isspace(*str))
-                               str++;
+                       str = skip_spaces(str);
 
                        /* now copy until next white space */
-                       while (*str && !isspace(*str) && field_width--) {
+                       while (*str && !isspace(*str) && field_width--)
                                *s++ = *str++;
-                       }
                        *s = '\0';
                        num++;
                }
@@ -1889,7 +1930,7 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
                case 'n':
                        /* return number of characters read so far */
                {
-                       int *i = (int *)va_arg(args,int*);
+                       int *i = (int *)va_arg(args, int*);
                        *i = str - buf;
                }
                continue;
@@ -1901,14 +1942,14 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
                        base = 16;
                        break;
                case 'i':
-                        base = 0;
+                       base = 0;
                case 'd':
                        is_sign = 1;
                case 'u':
                        break;
                case '%':
                        /* looking for '%' in str */
-                       if (*str++ != '%') 
+                       if (*str++ != '%')
                                return num;
                        continue;
                default:
@@ -1919,71 +1960,70 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
                /* have some sort of integer conversion.
                 * first, skip white space in buffer.
                 */
-               while (isspace(*str))
-                       str++;
+               str = skip_spaces(str);
 
                digit = *str;
                if (is_sign && digit == '-')
                        digit = *(str + 1);
 
                if (!digit
-                    || (base == 16 && !isxdigit(digit))
-                    || (base == 10 && !isdigit(digit))
-                    || (base == 8 && (!isdigit(digit) || digit > '7'))
-                    || (base == 0 && !isdigit(digit)))
-                               break;
+                   || (base == 16 && !isxdigit(digit))
+                   || (base == 10 && !isdigit(digit))
+                   || (base == 8 && (!isdigit(digit) || digit > '7'))
+                   || (base == 0 && !isdigit(digit)))
+                       break;
 
-               switch(qualifier) {
+               switch (qualifier) {
                case 'H':       /* that's 'hh' in format */
                        if (is_sign) {
-                               signed char *s = (signed char *) va_arg(args,signed char *);
-                               *s = (signed char) simple_strtol(str,&next,base);
+                               signed char *s = (signed char *)va_arg(args, signed char *);
+                               *s = (signed char)simple_strtol(str, &next, base);
                        } else {
-                               unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
-                               *s = (unsigned char) simple_strtoul(str, &next, base);
+                               unsigned char *s = (unsigned char *)va_arg(args, unsigned char *);
+                               *s = (unsigned char)simple_strtoul(str, &next, base);
                        }
                        break;
                case 'h':
                        if (is_sign) {
-                               short *s = (short *) va_arg(args,short *);
-                               *s = (short) simple_strtol(str,&next,base);
+                               short *s = (short *)va_arg(args, short *);
+                               *s = (short)simple_strtol(str, &next, base);
                        } else {
-                               unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
-                               *s = (unsigned short) simple_strtoul(str, &next, base);
+                               unsigned short *s = (unsigned short *)va_arg(args, unsigned short *);
+                               *s = (unsigned short)simple_strtoul(str, &next, base);
                        }
                        break;
                case 'l':
                        if (is_sign) {
-                               long *l = (long *) va_arg(args,long *);
-                               *l = simple_strtol(str,&next,base);
+                               long *l = (long *)va_arg(args, long *);
+                               *l = simple_strtol(str, &next, base);
                        } else {
-                               unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
-                               *l = simple_strtoul(str,&next,base);
+                               unsigned long *l = (unsigned long *)va_arg(args, unsigned long *);
+                               *l = simple_strtoul(str, &next, base);
                        }
                        break;
                case 'L':
                        if (is_sign) {
-                               long long *l = (long long*) va_arg(args,long long *);
-                               *l = simple_strtoll(str,&next,base);
+                               long long *l = (long long *)va_arg(args, long long *);
+                               *l = simple_strtoll(str, &next, base);
                        } else {
-                               unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
-                               *l = simple_strtoull(str,&next,base);
+                               unsigned long long *l = (unsigned long long *)va_arg(args, unsigned long long *);
+                               *l = simple_strtoull(str, &next, base);
                        }
                        break;
                case 'Z':
                case 'z':
                {
-                       size_t *s = (size_t*) va_arg(args,size_t*);
-                       *s = (size_t) simple_strtoul(str,&next,base);
+                       size_t *s = (size_t *)va_arg(args, size_t *);
+                       *s = (size_t)simple_strtoul(str, &next, base);
                }
                break;
                default:
                        if (is_sign) {
-                               int *i = (int *) va_arg(args, int*);
-                               *i = (int) simple_strtol(str,&next,base);
+                               int *i = (int *)va_arg(args, int *);
+                               *i = (int)simple_strtol(str, &next, base);
                        } else {
-                               unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
-                               *i = (unsigned int) simple_strtoul(str,&next,base);
+                               unsigned int *i = (unsigned int *)va_arg(args, unsigned int*);
+                               *i = (unsigned int)simple_strtoul(str, &next, base);
                        }
                        break;
                }
@@ -2014,14 +2054,15 @@ EXPORT_SYMBOL(vsscanf);
  * @fmt:       formatting of buffer
  * @...:       resulting arguments
  */
-int sscanf(const char * buf, const char * fmt, ...)
+int sscanf(const char *buf, const char *fmt, ...)
 {
        va_list args;
        int i;
 
-       va_start(args,fmt);
-       i = vsscanf(buf,fmt,args);
+       va_start(args, fmt);
+       i = vsscanf(buf, fmt, args);
        va_end(args);
+
        return i;
 }
 EXPORT_SYMBOL(sscanf);
index 44cf6f0a3a6d34f1cdf90a0f4e65dabf3b9c0623..2310984591ed9e88747f9d4e7693e50eab8013d2 100644 (file)
@@ -158,11 +158,13 @@ config PAGEFLAGS_EXTENDED
 # Default to 4 for wider testing, though 8 might be more appropriate.
 # ARM's adjust_pte (unused if VIPT) depends on mm-wide page_table_lock.
 # PA-RISC 7xxx's spinlock_t would enlarge struct page from 32 to 44 bytes.
+# DEBUG_SPINLOCK and DEBUG_LOCK_ALLOC spinlock_t also enlarge struct page.
 #
 config SPLIT_PTLOCK_CPUS
        int
-       default "4096" if ARM && !CPU_CACHE_VIPT
-       default "4096" if PARISC && !PA20
+       default "999999" if ARM && !CPU_CACHE_VIPT
+       default "999999" if PARISC && !PA20
+       default "999999" if DEBUG_SPINLOCK || DEBUG_LOCK_ALLOC
        default "4"
 
 #
@@ -200,14 +202,6 @@ config VIRT_TO_BUS
        def_bool y
        depends on !ARCH_NO_VIRT_TO_BUS
 
-config HAVE_MLOCK
-       bool
-       default y if MMU=y
-
-config HAVE_MLOCKED_PAGE_BIT
-       bool
-       default y if HAVE_MLOCK=y
-
 config MMU_NOTIFIER
        bool
 
@@ -218,7 +212,7 @@ config KSM
          Enable Kernel Samepage Merging: KSM periodically scans those areas
          of an application's address space that an app has advised may be
          mergeable.  When it finds pages of identical content, it replaces
-         the many instances by a single resident page with that content, so
+         the many instances by a single page with that content, so
          saving memory until one or another app needs to modify the content.
          Recommended for use with KVM, or with other duplicative applications.
          See Documentation/vm/ksm.txt for more information: KSM is inactive
index d1dc23cc7f10e78e9598c87c70337bd3d2577d6a..7d1486875e1cc04c1297b4cbb2743412dd553025 100644 (file)
@@ -432,8 +432,8 @@ int __init reserve_bootmem(unsigned long addr, unsigned long size,
        return mark_bootmem(start, end, 1, flags);
 }
 
-static unsigned long align_idx(struct bootmem_data *bdata, unsigned long idx,
-                       unsigned long step)
+static unsigned long __init align_idx(struct bootmem_data *bdata,
+                                     unsigned long idx, unsigned long step)
 {
        unsigned long base = bdata->node_min_pfn;
 
@@ -445,8 +445,8 @@ static unsigned long align_idx(struct bootmem_data *bdata, unsigned long idx,
        return ALIGN(base + idx, step) - base;
 }
 
-static unsigned long align_off(struct bootmem_data *bdata, unsigned long off,
-                       unsigned long align)
+static unsigned long __init align_off(struct bootmem_data *bdata,
+                                     unsigned long off, unsigned long align)
 {
        unsigned long base = PFN_PHYS(bdata->node_min_pfn);
 
index 5d7601b0287487321314c1d969f17425b339f9b1..65f38c218207231e0bf5b612ccacc5858be1b4e0 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/io.h>
 
 #include <linux/hugetlb.h>
+#include <linux/node.h>
 #include "internal.h"
 
 const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL;
@@ -622,42 +623,66 @@ static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
 }
 
 /*
- * Use a helper variable to find the next node and then
- * copy it back to next_nid_to_alloc afterwards:
- * otherwise there's a window in which a racer might
- * pass invalid nid MAX_NUMNODES to alloc_pages_exact_node.
- * But we don't need to use a spin_lock here: it really
- * doesn't matter if occasionally a racer chooses the
- * same nid as we do.  Move nid forward in the mask even
- * if we just successfully allocated a hugepage so that
- * the next caller gets hugepages on the next node.
+ * common helper functions for hstate_next_node_to_{alloc|free}.
+ * We may have allocated or freed a huge page based on a different
+ * nodes_allowed previously, so h->next_node_to_{alloc|free} might
+ * be outside of *nodes_allowed.  Ensure that we use an allowed
+ * node for alloc or free.
  */
-static int hstate_next_node_to_alloc(struct hstate *h)
+static int next_node_allowed(int nid, nodemask_t *nodes_allowed)
 {
-       int next_nid;
-       next_nid = next_node(h->next_nid_to_alloc, node_online_map);
-       if (next_nid == MAX_NUMNODES)
-               next_nid = first_node(node_online_map);
-       h->next_nid_to_alloc = next_nid;
-       return next_nid;
+       nid = next_node(nid, *nodes_allowed);
+       if (nid == MAX_NUMNODES)
+               nid = first_node(*nodes_allowed);
+       VM_BUG_ON(nid >= MAX_NUMNODES);
+
+       return nid;
+}
+
+static int get_valid_node_allowed(int nid, nodemask_t *nodes_allowed)
+{
+       if (!node_isset(nid, *nodes_allowed))
+               nid = next_node_allowed(nid, nodes_allowed);
+       return nid;
+}
+
+/*
+ * returns the previously saved node ["this node"] from which to
+ * allocate a persistent huge page for the pool and advance the
+ * next node from which to allocate, handling wrap at end of node
+ * mask.
+ */
+static int hstate_next_node_to_alloc(struct hstate *h,
+                                       nodemask_t *nodes_allowed)
+{
+       int nid;
+
+       VM_BUG_ON(!nodes_allowed);
+
+       nid = get_valid_node_allowed(h->next_nid_to_alloc, nodes_allowed);
+       h->next_nid_to_alloc = next_node_allowed(nid, nodes_allowed);
+
+       return nid;
 }
 
-static int alloc_fresh_huge_page(struct hstate *h)
+static int alloc_fresh_huge_page(struct hstate *h, nodemask_t *nodes_allowed)
 {
        struct page *page;
        int start_nid;
        int next_nid;
        int ret = 0;
 
-       start_nid = h->next_nid_to_alloc;
+       start_nid = hstate_next_node_to_alloc(h, nodes_allowed);
        next_nid = start_nid;
 
        do {
                page = alloc_fresh_huge_page_node(h, next_nid);
-               if (page)
+               if (page) {
                        ret = 1;
-               next_nid = hstate_next_node_to_alloc(h);
-       } while (!page && next_nid != start_nid);
+                       break;
+               }
+               next_nid = hstate_next_node_to_alloc(h, nodes_allowed);
+       } while (next_nid != start_nid);
 
        if (ret)
                count_vm_event(HTLB_BUDDY_PGALLOC);
@@ -668,17 +693,21 @@ static int alloc_fresh_huge_page(struct hstate *h)
 }
 
 /*
- * helper for free_pool_huge_page() - find next node
- * from which to free a huge page
+ * helper for free_pool_huge_page() - return the previously saved
+ * node ["this node"] from which to free a huge page.  Advance the
+ * next node id whether or not we find a free huge page to free so
+ * that the next attempt to free addresses the next node.
  */
-static int hstate_next_node_to_free(struct hstate *h)
+static int hstate_next_node_to_free(struct hstate *h, nodemask_t *nodes_allowed)
 {
-       int next_nid;
-       next_nid = next_node(h->next_nid_to_free, node_online_map);
-       if (next_nid == MAX_NUMNODES)
-               next_nid = first_node(node_online_map);
-       h->next_nid_to_free = next_nid;
-       return next_nid;
+       int nid;
+
+       VM_BUG_ON(!nodes_allowed);
+
+       nid = get_valid_node_allowed(h->next_nid_to_free, nodes_allowed);
+       h->next_nid_to_free = next_node_allowed(nid, nodes_allowed);
+
+       return nid;
 }
 
 /*
@@ -687,13 +716,14 @@ static int hstate_next_node_to_free(struct hstate *h)
  * balanced over allowed nodes.
  * Called with hugetlb_lock locked.
  */
-static int free_pool_huge_page(struct hstate *h, bool acct_surplus)
+static int free_pool_huge_page(struct hstate *h, nodemask_t *nodes_allowed,
+                                                        bool acct_surplus)
 {
        int start_nid;
        int next_nid;
        int ret = 0;
 
-       start_nid = h->next_nid_to_free;
+       start_nid = hstate_next_node_to_free(h, nodes_allowed);
        next_nid = start_nid;
 
        do {
@@ -715,9 +745,10 @@ static int free_pool_huge_page(struct hstate *h, bool acct_surplus)
                        }
                        update_and_free_page(h, page);
                        ret = 1;
+                       break;
                }
-               next_nid = hstate_next_node_to_free(h);
-       } while (!ret && next_nid != start_nid);
+               next_nid = hstate_next_node_to_free(h, nodes_allowed);
+       } while (next_nid != start_nid);
 
        return ret;
 }
@@ -911,14 +942,14 @@ static void return_unused_surplus_pages(struct hstate *h,
 
        /*
         * We want to release as many surplus pages as possible, spread
-        * evenly across all nodes. Iterate across all nodes until we
-        * can no longer free unreserved surplus pages. This occurs when
-        * the nodes with surplus pages have no free pages.
-        * free_pool_huge_page() will balance the the frees across the
-        * on-line nodes for us and will handle the hstate accounting.
+        * evenly across all nodes with memory. Iterate across these nodes
+        * until we can no longer free unreserved surplus pages. This occurs
+        * when the nodes with surplus pages have no free pages.
+        * free_pool_huge_page() will balance the the freed pages across the
+        * on-line nodes with memory and will handle the hstate accounting.
         */
        while (nr_pages--) {
-               if (!free_pool_huge_page(h, 1))
+               if (!free_pool_huge_page(h, &node_states[N_HIGH_MEMORY], 1))
                        break;
        }
 }
@@ -1022,16 +1053,16 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
 int __weak alloc_bootmem_huge_page(struct hstate *h)
 {
        struct huge_bootmem_page *m;
-       int nr_nodes = nodes_weight(node_online_map);
+       int nr_nodes = nodes_weight(node_states[N_HIGH_MEMORY]);
 
        while (nr_nodes) {
                void *addr;
 
                addr = __alloc_bootmem_node_nopanic(
-                               NODE_DATA(h->next_nid_to_alloc),
+                               NODE_DATA(hstate_next_node_to_alloc(h,
+                                               &node_states[N_HIGH_MEMORY])),
                                huge_page_size(h), huge_page_size(h), 0);
 
-               hstate_next_node_to_alloc(h);
                if (addr) {
                        /*
                         * Use the beginning of the huge page to store the
@@ -1084,7 +1115,8 @@ static void __init hugetlb_hstate_alloc_pages(struct hstate *h)
                if (h->order >= MAX_ORDER) {
                        if (!alloc_bootmem_huge_page(h))
                                break;
-               } else if (!alloc_fresh_huge_page(h))
+               } else if (!alloc_fresh_huge_page(h,
+                                        &node_states[N_HIGH_MEMORY]))
                        break;
        }
        h->max_huge_pages = i;
@@ -1126,14 +1158,15 @@ static void __init report_hugepages(void)
 }
 
 #ifdef CONFIG_HIGHMEM
-static void try_to_free_low(struct hstate *h, unsigned long count)
+static void try_to_free_low(struct hstate *h, unsigned long count,
+                                               nodemask_t *nodes_allowed)
 {
        int i;
 
        if (h->order >= MAX_ORDER)
                return;
 
-       for (i = 0; i < MAX_NUMNODES; ++i) {
+       for_each_node_mask(i, *nodes_allowed) {
                struct page *page, *next;
                struct list_head *freel = &h->hugepage_freelists[i];
                list_for_each_entry_safe(page, next, freel, lru) {
@@ -1149,7 +1182,8 @@ static void try_to_free_low(struct hstate *h, unsigned long count)
        }
 }
 #else
-static inline void try_to_free_low(struct hstate *h, unsigned long count)
+static inline void try_to_free_low(struct hstate *h, unsigned long count,
+                                               nodemask_t *nodes_allowed)
 {
 }
 #endif
@@ -1159,7 +1193,8 @@ static inline void try_to_free_low(struct hstate *h, unsigned long count)
  * balanced by operating on them in a round-robin fashion.
  * Returns 1 if an adjustment was made.
  */
-static int adjust_pool_surplus(struct hstate *h, int delta)
+static int adjust_pool_surplus(struct hstate *h, nodemask_t *nodes_allowed,
+                               int delta)
 {
        int start_nid, next_nid;
        int ret = 0;
@@ -1167,29 +1202,33 @@ static int adjust_pool_surplus(struct hstate *h, int delta)
        VM_BUG_ON(delta != -1 && delta != 1);
 
        if (delta < 0)
-               start_nid = h->next_nid_to_alloc;
+               start_nid = hstate_next_node_to_alloc(h, nodes_allowed);
        else
-               start_nid = h->next_nid_to_free;
+               start_nid = hstate_next_node_to_free(h, nodes_allowed);
        next_nid = start_nid;
 
        do {
                int nid = next_nid;
                if (delta < 0)  {
-                       next_nid = hstate_next_node_to_alloc(h);
                        /*
                         * To shrink on this node, there must be a surplus page
                         */
-                       if (!h->surplus_huge_pages_node[nid])
+                       if (!h->surplus_huge_pages_node[nid]) {
+                               next_nid = hstate_next_node_to_alloc(h,
+                                                               nodes_allowed);
                                continue;
+                       }
                }
                if (delta > 0) {
-                       next_nid = hstate_next_node_to_free(h);
                        /*
                         * Surplus cannot exceed the total number of pages
                         */
                        if (h->surplus_huge_pages_node[nid] >=
-                                               h->nr_huge_pages_node[nid])
+                                               h->nr_huge_pages_node[nid]) {
+                               next_nid = hstate_next_node_to_free(h,
+                                                               nodes_allowed);
                                continue;
+                       }
                }
 
                h->surplus_huge_pages += delta;
@@ -1202,7 +1241,8 @@ static int adjust_pool_surplus(struct hstate *h, int delta)
 }
 
 #define persistent_huge_pages(h) (h->nr_huge_pages - h->surplus_huge_pages)
-static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count)
+static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count,
+                                               nodemask_t *nodes_allowed)
 {
        unsigned long min_count, ret;
 
@@ -1222,7 +1262,7 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count)
         */
        spin_lock(&hugetlb_lock);
        while (h->surplus_huge_pages && count > persistent_huge_pages(h)) {
-               if (!adjust_pool_surplus(h, -1))
+               if (!adjust_pool_surplus(h, nodes_allowed, -1))
                        break;
        }
 
@@ -1233,11 +1273,14 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count)
                 * and reducing the surplus.
                 */
                spin_unlock(&hugetlb_lock);
-               ret = alloc_fresh_huge_page(h);
+               ret = alloc_fresh_huge_page(h, nodes_allowed);
                spin_lock(&hugetlb_lock);
                if (!ret)
                        goto out;
 
+               /* Bail for signals. Probably ctrl-c from user */
+               if (signal_pending(current))
+                       goto out;
        }
 
        /*
@@ -1257,13 +1300,13 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count)
         */
        min_count = h->resv_huge_pages + h->nr_huge_pages - h->free_huge_pages;
        min_count = max(count, min_count);
-       try_to_free_low(h, min_count);
+       try_to_free_low(h, min_count, nodes_allowed);
        while (min_count < persistent_huge_pages(h)) {
-               if (!free_pool_huge_page(h, 0))
+               if (!free_pool_huge_page(h, nodes_allowed, 0))
                        break;
        }
        while (count < persistent_huge_pages(h)) {
-               if (!adjust_pool_surplus(h, 1))
+               if (!adjust_pool_surplus(h, nodes_allowed, 1))
                        break;
        }
 out:
@@ -1282,43 +1325,117 @@ out:
 static struct kobject *hugepages_kobj;
 static struct kobject *hstate_kobjs[HUGE_MAX_HSTATE];
 
-static struct hstate *kobj_to_hstate(struct kobject *kobj)
+static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp);
+
+static struct hstate *kobj_to_hstate(struct kobject *kobj, int *nidp)
 {
        int i;
+
        for (i = 0; i < HUGE_MAX_HSTATE; i++)
-               if (hstate_kobjs[i] == kobj)
+               if (hstate_kobjs[i] == kobj) {
+                       if (nidp)
+                               *nidp = NUMA_NO_NODE;
                        return &hstates[i];
-       BUG();
-       return NULL;
+               }
+
+       return kobj_to_node_hstate(kobj, nidp);
 }
 
-static ssize_t nr_hugepages_show(struct kobject *kobj,
+static ssize_t nr_hugepages_show_common(struct kobject *kobj,
                                        struct kobj_attribute *attr, char *buf)
 {
-       struct hstate *h = kobj_to_hstate(kobj);
-       return sprintf(buf, "%lu\n", h->nr_huge_pages);
+       struct hstate *h;
+       unsigned long nr_huge_pages;
+       int nid;
+
+       h = kobj_to_hstate(kobj, &nid);
+       if (nid == NUMA_NO_NODE)
+               nr_huge_pages = h->nr_huge_pages;
+       else
+               nr_huge_pages = h->nr_huge_pages_node[nid];
+
+       return sprintf(buf, "%lu\n", nr_huge_pages);
 }
-static ssize_t nr_hugepages_store(struct kobject *kobj,
-               struct kobj_attribute *attr, const char *buf, size_t count)
+static ssize_t nr_hugepages_store_common(bool obey_mempolicy,
+                       struct kobject *kobj, struct kobj_attribute *attr,
+                       const char *buf, size_t len)
 {
        int err;
-       unsigned long input;
-       struct hstate *h = kobj_to_hstate(kobj);
+       int nid;
+       unsigned long count;
+       struct hstate *h;
+       NODEMASK_ALLOC(nodemask_t, nodes_allowed, GFP_KERNEL | __GFP_NORETRY);
 
-       err = strict_strtoul(buf, 10, &input);
+       err = strict_strtoul(buf, 10, &count);
        if (err)
                return 0;
 
-       h->max_huge_pages = set_max_huge_pages(h, input);
+       h = kobj_to_hstate(kobj, &nid);
+       if (nid == NUMA_NO_NODE) {
+               /*
+                * global hstate attribute
+                */
+               if (!(obey_mempolicy &&
+                               init_nodemask_of_mempolicy(nodes_allowed))) {
+                       NODEMASK_FREE(nodes_allowed);
+                       nodes_allowed = &node_states[N_HIGH_MEMORY];
+               }
+       } else if (nodes_allowed) {
+               /*
+                * per node hstate attribute: adjust count to global,
+                * but restrict alloc/free to the specified node.
+                */
+               count += h->nr_huge_pages - h->nr_huge_pages_node[nid];
+               init_nodemask_of_node(nodes_allowed, nid);
+       } else
+               nodes_allowed = &node_states[N_HIGH_MEMORY];
+
+       h->max_huge_pages = set_max_huge_pages(h, count, nodes_allowed);
 
-       return count;
+       if (nodes_allowed != &node_states[N_HIGH_MEMORY])
+               NODEMASK_FREE(nodes_allowed);
+
+       return len;
+}
+
+static ssize_t nr_hugepages_show(struct kobject *kobj,
+                                      struct kobj_attribute *attr, char *buf)
+{
+       return nr_hugepages_show_common(kobj, attr, buf);
+}
+
+static ssize_t nr_hugepages_store(struct kobject *kobj,
+              struct kobj_attribute *attr, const char *buf, size_t len)
+{
+       return nr_hugepages_store_common(false, kobj, attr, buf, len);
 }
 HSTATE_ATTR(nr_hugepages);
 
+#ifdef CONFIG_NUMA
+
+/*
+ * hstate attribute for optionally mempolicy-based constraint on persistent
+ * huge page alloc/free.
+ */
+static ssize_t nr_hugepages_mempolicy_show(struct kobject *kobj,
+                                      struct kobj_attribute *attr, char *buf)
+{
+       return nr_hugepages_show_common(kobj, attr, buf);
+}
+
+static ssize_t nr_hugepages_mempolicy_store(struct kobject *kobj,
+              struct kobj_attribute *attr, const char *buf, size_t len)
+{
+       return nr_hugepages_store_common(true, kobj, attr, buf, len);
+}
+HSTATE_ATTR(nr_hugepages_mempolicy);
+#endif
+
+
 static ssize_t nr_overcommit_hugepages_show(struct kobject *kobj,
                                        struct kobj_attribute *attr, char *buf)
 {
-       struct hstate *h = kobj_to_hstate(kobj);
+       struct hstate *h = kobj_to_hstate(kobj, NULL);
        return sprintf(buf, "%lu\n", h->nr_overcommit_huge_pages);
 }
 static ssize_t nr_overcommit_hugepages_store(struct kobject *kobj,
@@ -1326,7 +1443,7 @@ static ssize_t nr_overcommit_hugepages_store(struct kobject *kobj,
 {
        int err;
        unsigned long input;
-       struct hstate *h = kobj_to_hstate(kobj);
+       struct hstate *h = kobj_to_hstate(kobj, NULL);
 
        err = strict_strtoul(buf, 10, &input);
        if (err)
@@ -1343,15 +1460,24 @@ HSTATE_ATTR(nr_overcommit_hugepages);
 static ssize_t free_hugepages_show(struct kobject *kobj,
                                        struct kobj_attribute *attr, char *buf)
 {
-       struct hstate *h = kobj_to_hstate(kobj);
-       return sprintf(buf, "%lu\n", h->free_huge_pages);
+       struct hstate *h;
+       unsigned long free_huge_pages;
+       int nid;
+
+       h = kobj_to_hstate(kobj, &nid);
+       if (nid == NUMA_NO_NODE)
+               free_huge_pages = h->free_huge_pages;
+       else
+               free_huge_pages = h->free_huge_pages_node[nid];
+
+       return sprintf(buf, "%lu\n", free_huge_pages);
 }
 HSTATE_ATTR_RO(free_hugepages);
 
 static ssize_t resv_hugepages_show(struct kobject *kobj,
                                        struct kobj_attribute *attr, char *buf)
 {
-       struct hstate *h = kobj_to_hstate(kobj);
+       struct hstate *h = kobj_to_hstate(kobj, NULL);
        return sprintf(buf, "%lu\n", h->resv_huge_pages);
 }
 HSTATE_ATTR_RO(resv_hugepages);
@@ -1359,8 +1485,17 @@ HSTATE_ATTR_RO(resv_hugepages);
 static ssize_t surplus_hugepages_show(struct kobject *kobj,
                                        struct kobj_attribute *attr, char *buf)
 {
-       struct hstate *h = kobj_to_hstate(kobj);
-       return sprintf(buf, "%lu\n", h->surplus_huge_pages);
+       struct hstate *h;
+       unsigned long surplus_huge_pages;
+       int nid;
+
+       h = kobj_to_hstate(kobj, &nid);
+       if (nid == NUMA_NO_NODE)
+               surplus_huge_pages = h->surplus_huge_pages;
+       else
+               surplus_huge_pages = h->surplus_huge_pages_node[nid];
+
+       return sprintf(buf, "%lu\n", surplus_huge_pages);
 }
 HSTATE_ATTR_RO(surplus_hugepages);
 
@@ -1370,6 +1505,9 @@ static struct attribute *hstate_attrs[] = {
        &free_hugepages_attr.attr,
        &resv_hugepages_attr.attr,
        &surplus_hugepages_attr.attr,
+#ifdef CONFIG_NUMA
+       &nr_hugepages_mempolicy_attr.attr,
+#endif
        NULL,
 };
 
@@ -1377,19 +1515,21 @@ static struct attribute_group hstate_attr_group = {
        .attrs = hstate_attrs,
 };
 
-static int __init hugetlb_sysfs_add_hstate(struct hstate *h)
+static int __init hugetlb_sysfs_add_hstate(struct hstate *h,
+                               struct kobject *parent,
+                               struct kobject **hstate_kobjs,
+                               struct attribute_group *hstate_attr_group)
 {
        int retval;
+       int hi = h - hstates;
 
-       hstate_kobjs[h - hstates] = kobject_create_and_add(h->name,
-                                                       hugepages_kobj);
-       if (!hstate_kobjs[h - hstates])
+       hstate_kobjs[hi] = kobject_create_and_add(h->name, parent);
+       if (!hstate_kobjs[hi])
                return -ENOMEM;
 
-       retval = sysfs_create_group(hstate_kobjs[h - hstates],
-                                                       &hstate_attr_group);
+       retval = sysfs_create_group(hstate_kobjs[hi], hstate_attr_group);
        if (retval)
-               kobject_put(hstate_kobjs[h - hstates]);
+               kobject_put(hstate_kobjs[hi]);
 
        return retval;
 }
@@ -1404,17 +1544,184 @@ static void __init hugetlb_sysfs_init(void)
                return;
 
        for_each_hstate(h) {
-               err = hugetlb_sysfs_add_hstate(h);
+               err = hugetlb_sysfs_add_hstate(h, hugepages_kobj,
+                                        hstate_kobjs, &hstate_attr_group);
                if (err)
                        printk(KERN_ERR "Hugetlb: Unable to add hstate %s",
                                                                h->name);
        }
 }
 
+#ifdef CONFIG_NUMA
+
+/*
+ * node_hstate/s - associate per node hstate attributes, via their kobjects,
+ * with node sysdevs in node_devices[] using a parallel array.  The array
+ * index of a node sysdev or _hstate == node id.
+ * This is here to avoid any static dependency of the node sysdev driver, in
+ * the base kernel, on the hugetlb module.
+ */
+struct node_hstate {
+       struct kobject          *hugepages_kobj;
+       struct kobject          *hstate_kobjs[HUGE_MAX_HSTATE];
+};
+struct node_hstate node_hstates[MAX_NUMNODES];
+
+/*
+ * A subset of global hstate attributes for node sysdevs
+ */
+static struct attribute *per_node_hstate_attrs[] = {
+       &nr_hugepages_attr.attr,
+       &free_hugepages_attr.attr,
+       &surplus_hugepages_attr.attr,
+       NULL,
+};
+
+static struct attribute_group per_node_hstate_attr_group = {
+       .attrs = per_node_hstate_attrs,
+};
+
+/*
+ * kobj_to_node_hstate - lookup global hstate for node sysdev hstate attr kobj.
+ * Returns node id via non-NULL nidp.
+ */
+static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp)
+{
+       int nid;
+
+       for (nid = 0; nid < nr_node_ids; nid++) {
+               struct node_hstate *nhs = &node_hstates[nid];
+               int i;
+               for (i = 0; i < HUGE_MAX_HSTATE; i++)
+                       if (nhs->hstate_kobjs[i] == kobj) {
+                               if (nidp)
+                                       *nidp = nid;
+                               return &hstates[i];
+                       }
+       }
+
+       BUG();
+       return NULL;
+}
+
+/*
+ * Unregister hstate attributes from a single node sysdev.
+ * No-op if no hstate attributes attached.
+ */
+void hugetlb_unregister_node(struct node *node)
+{
+       struct hstate *h;
+       struct node_hstate *nhs = &node_hstates[node->sysdev.id];
+
+       if (!nhs->hugepages_kobj)
+               return;         /* no hstate attributes */
+
+       for_each_hstate(h)
+               if (nhs->hstate_kobjs[h - hstates]) {
+                       kobject_put(nhs->hstate_kobjs[h - hstates]);
+                       nhs->hstate_kobjs[h - hstates] = NULL;
+               }
+
+       kobject_put(nhs->hugepages_kobj);
+       nhs->hugepages_kobj = NULL;
+}
+
+/*
+ * hugetlb module exit:  unregister hstate attributes from node sysdevs
+ * that have them.
+ */
+static void hugetlb_unregister_all_nodes(void)
+{
+       int nid;
+
+       /*
+        * disable node sysdev registrations.
+        */
+       register_hugetlbfs_with_node(NULL, NULL);
+
+       /*
+        * remove hstate attributes from any nodes that have them.
+        */
+       for (nid = 0; nid < nr_node_ids; nid++)
+               hugetlb_unregister_node(&node_devices[nid]);
+}
+
+/*
+ * Register hstate attributes for a single node sysdev.
+ * No-op if attributes already registered.
+ */
+void hugetlb_register_node(struct node *node)
+{
+       struct hstate *h;
+       struct node_hstate *nhs = &node_hstates[node->sysdev.id];
+       int err;
+
+       if (nhs->hugepages_kobj)
+               return;         /* already allocated */
+
+       nhs->hugepages_kobj = kobject_create_and_add("hugepages",
+                                                       &node->sysdev.kobj);
+       if (!nhs->hugepages_kobj)
+               return;
+
+       for_each_hstate(h) {
+               err = hugetlb_sysfs_add_hstate(h, nhs->hugepages_kobj,
+                                               nhs->hstate_kobjs,
+                                               &per_node_hstate_attr_group);
+               if (err) {
+                       printk(KERN_ERR "Hugetlb: Unable to add hstate %s"
+                                       " for node %d\n",
+                                               h->name, node->sysdev.id);
+                       hugetlb_unregister_node(node);
+                       break;
+               }
+       }
+}
+
+/*
+ * hugetlb init time:  register hstate attributes for all registered node
+ * sysdevs of nodes that have memory.  All on-line nodes should have
+ * registered their associated sysdev by this time.
+ */
+static void hugetlb_register_all_nodes(void)
+{
+       int nid;
+
+       for_each_node_state(nid, N_HIGH_MEMORY) {
+               struct node *node = &node_devices[nid];
+               if (node->sysdev.id == nid)
+                       hugetlb_register_node(node);
+       }
+
+       /*
+        * Let the node sysdev driver know we're here so it can
+        * [un]register hstate attributes on node hotplug.
+        */
+       register_hugetlbfs_with_node(hugetlb_register_node,
+                                    hugetlb_unregister_node);
+}
+#else  /* !CONFIG_NUMA */
+
+static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp)
+{
+       BUG();
+       if (nidp)
+               *nidp = -1;
+       return NULL;
+}
+
+static void hugetlb_unregister_all_nodes(void) { }
+
+static void hugetlb_register_all_nodes(void) { }
+
+#endif
+
 static void __exit hugetlb_exit(void)
 {
        struct hstate *h;
 
+       hugetlb_unregister_all_nodes();
+
        for_each_hstate(h) {
                kobject_put(hstate_kobjs[h - hstates]);
        }
@@ -1449,6 +1756,8 @@ static int __init hugetlb_init(void)
 
        hugetlb_sysfs_init();
 
+       hugetlb_register_all_nodes();
+
        return 0;
 }
 module_init(hugetlb_init);
@@ -1472,8 +1781,8 @@ void __init hugetlb_add_hstate(unsigned order)
        h->free_huge_pages = 0;
        for (i = 0; i < MAX_NUMNODES; ++i)
                INIT_LIST_HEAD(&h->hugepage_freelists[i]);
-       h->next_nid_to_alloc = first_node(node_online_map);
-       h->next_nid_to_free = first_node(node_online_map);
+       h->next_nid_to_alloc = first_node(node_states[N_HIGH_MEMORY]);
+       h->next_nid_to_free = first_node(node_states[N_HIGH_MEMORY]);
        snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB",
                                        huge_page_size(h)/1024);
 
@@ -1536,9 +1845,9 @@ static unsigned int cpuset_mems_nr(unsigned int *array)
 }
 
 #ifdef CONFIG_SYSCTL
-int hugetlb_sysctl_handler(struct ctl_table *table, int write,
-                          void __user *buffer,
-                          size_t *length, loff_t *ppos)
+static int hugetlb_sysctl_handler_common(bool obey_mempolicy,
+                        struct ctl_table *table, int write,
+                        void __user *buffer, size_t *length, loff_t *ppos)
 {
        struct hstate *h = &default_hstate;
        unsigned long tmp;
@@ -1550,12 +1859,40 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write,
        table->maxlen = sizeof(unsigned long);
        proc_doulongvec_minmax(table, write, buffer, length, ppos);
 
-       if (write)
-               h->max_huge_pages = set_max_huge_pages(h, tmp);
+       if (write) {
+               NODEMASK_ALLOC(nodemask_t, nodes_allowed,
+                                               GFP_KERNEL | __GFP_NORETRY);
+               if (!(obey_mempolicy &&
+                              init_nodemask_of_mempolicy(nodes_allowed))) {
+                       NODEMASK_FREE(nodes_allowed);
+                       nodes_allowed = &node_states[N_HIGH_MEMORY];
+               }
+               h->max_huge_pages = set_max_huge_pages(h, tmp, nodes_allowed);
+
+               if (nodes_allowed != &node_states[N_HIGH_MEMORY])
+                       NODEMASK_FREE(nodes_allowed);
+       }
 
        return 0;
 }
 
+int hugetlb_sysctl_handler(struct ctl_table *table, int write,
+                         void __user *buffer, size_t *length, loff_t *ppos)
+{
+
+       return hugetlb_sysctl_handler_common(false, table, write,
+                                                       buffer, length, ppos);
+}
+
+#ifdef CONFIG_NUMA
+int hugetlb_mempolicy_sysctl_handler(struct ctl_table *table, int write,
+                         void __user *buffer, size_t *length, loff_t *ppos)
+{
+       return hugetlb_sysctl_handler_common(true, table, write,
+                                                       buffer, length, ppos);
+}
+#endif /* CONFIG_NUMA */
+
 int hugetlb_treat_movable_handler(struct ctl_table *table, int write,
                        void __user *buffer,
                        size_t *length, loff_t *ppos)
@@ -1903,6 +2240,12 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
                + (vma->vm_pgoff >> PAGE_SHIFT);
        mapping = (struct address_space *)page_private(page);
 
+       /*
+        * Take the mapping lock for the duration of the table walk. As
+        * this mapping should be shared between all the VMAs,
+        * __unmap_hugepage_range() is called as the lock is already held
+        */
+       spin_lock(&mapping->i_mmap_lock);
        vma_prio_tree_foreach(iter_vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
                /* Do not unmap the current VMA */
                if (iter_vma == vma)
@@ -1916,10 +2259,11 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
                 * from the time of fork. This would look like data corruption
                 */
                if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
-                       unmap_hugepage_range(iter_vma,
+                       __unmap_hugepage_range(iter_vma,
                                address, address + huge_page_size(h),
                                page);
        }
+       spin_unlock(&mapping->i_mmap_lock);
 
        return 1;
 }
@@ -1959,6 +2303,9 @@ retry_avoidcopy:
                outside_reserve = 1;
 
        page_cache_get(old_page);
+
+       /* Drop page_table_lock as buddy allocator may be called */
+       spin_unlock(&mm->page_table_lock);
        new_page = alloc_huge_page(vma, address, outside_reserve);
 
        if (IS_ERR(new_page)) {
@@ -1976,19 +2323,25 @@ retry_avoidcopy:
                        if (unmap_ref_private(mm, vma, old_page, address)) {
                                BUG_ON(page_count(old_page) != 1);
                                BUG_ON(huge_pte_none(pte));
+                               spin_lock(&mm->page_table_lock);
                                goto retry_avoidcopy;
                        }
                        WARN_ON_ONCE(1);
                }
 
+               /* Caller expects lock to be held */
+               spin_lock(&mm->page_table_lock);
                return -PTR_ERR(new_page);
        }
 
-       spin_unlock(&mm->page_table_lock);
        copy_huge_page(new_page, old_page, address, vma);
        __SetPageUptodate(new_page);
-       spin_lock(&mm->page_table_lock);
 
+       /*
+        * Retake the page_table_lock to check for racing updates
+        * before the page tables are altered
+        */
+       spin_lock(&mm->page_table_lock);
        ptep = huge_pte_offset(mm, address & huge_page_mask(h));
        if (likely(pte_same(huge_ptep_get(ptep), pte))) {
                /* Break COW */
index 22ec8d2b0fb8d641dabc76090b9062e9a5c557be..4fe67a162cb4ef022d3555d336fc34e005a0f0b2 100644 (file)
@@ -63,7 +63,7 @@ static inline unsigned long page_order(struct page *page)
        return page_private(page);
 }
 
-#ifdef CONFIG_HAVE_MLOCK
+#ifdef CONFIG_MMU
 extern long mlock_vma_pages_range(struct vm_area_struct *vma,
                        unsigned long start, unsigned long end);
 extern void munlock_vma_pages_range(struct vm_area_struct *vma,
@@ -72,21 +72,7 @@ static inline void munlock_vma_pages_all(struct vm_area_struct *vma)
 {
        munlock_vma_pages_range(vma, vma->vm_start, vma->vm_end);
 }
-#endif
-
-/*
- * unevictable_migrate_page() called only from migrate_page_copy() to
- * migrate unevictable flag to new page.
- * Note that the old page has been isolated from the LRU lists at this
- * point so we don't need to worry about LRU statistics.
- */
-static inline void unevictable_migrate_page(struct page *new, struct page *old)
-{
-       if (TestClearPageUnevictable(old))
-               SetPageUnevictable(new);
-}
 
-#ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT
 /*
  * Called only in fault path via page_evictable() for a new page
  * to determine if it's being mapped into a LOCKED vma.
@@ -107,9 +93,10 @@ static inline int is_mlocked_vma(struct vm_area_struct *vma, struct page *page)
 }
 
 /*
- * must be called with vma's mmap_sem held for read, and page locked.
+ * must be called with vma's mmap_sem held for read or write, and page locked.
  */
 extern void mlock_vma_page(struct page *page);
+extern void munlock_vma_page(struct page *page);
 
 /*
  * Clear the page's PageMlocked().  This can be useful in a situation where
@@ -144,7 +131,7 @@ static inline void mlock_migrate_page(struct page *newpage, struct page *page)
        }
 }
 
-#else /* CONFIG_HAVE_MLOCKED_PAGE_BIT */
+#else /* !CONFIG_MMU */
 static inline int is_mlocked_vma(struct vm_area_struct *v, struct page *p)
 {
        return 0;
@@ -153,7 +140,7 @@ static inline void clear_page_mlock(struct page *page) { }
 static inline void mlock_vma_page(struct page *page) { }
 static inline void mlock_migrate_page(struct page *new, struct page *old) { }
 
-#endif /* CONFIG_HAVE_MLOCKED_PAGE_BIT */
+#endif /* !CONFIG_MMU */
 
 /*
  * Return the mem_map entry representing the 'offset' subpage within
index 5575f8628fef5306611055092e47ed9f472f4027..56a0da1f9979d7eaa9d85cbc0b20ef76215e4abf 100644 (file)
--- a/mm/ksm.c
+++ b/mm/ksm.c
 #include <linux/wait.h>
 #include <linux/slab.h>
 #include <linux/rbtree.h>
+#include <linux/memory.h>
 #include <linux/mmu_notifier.h>
 #include <linux/swap.h>
 #include <linux/ksm.h>
 
 #include <asm/tlbflush.h>
+#include "internal.h"
 
 /*
  * A few notes about the KSM scanning process,
  * struct mm_slot - ksm information per mm that is being scanned
  * @link: link to the mm_slots hash list
  * @mm_list: link into the mm_slots list, rooted in ksm_mm_head
- * @rmap_list: head for this mm_slot's list of rmap_items
+ * @rmap_list: head for this mm_slot's singly-linked list of rmap_items
  * @mm: the mm that this information is valid for
  */
 struct mm_slot {
        struct hlist_node link;
        struct list_head mm_list;
-       struct list_head rmap_list;
+       struct rmap_item *rmap_list;
        struct mm_struct *mm;
 };
 
@@ -93,7 +95,7 @@ struct mm_slot {
  * struct ksm_scan - cursor for scanning
  * @mm_slot: the current mm_slot we are scanning
  * @address: the next address inside that to be scanned
- * @rmap_item: the current rmap that we are scanning inside the rmap_list
+ * @rmap_list: link to the next rmap to be scanned in the rmap_list
  * @seqnr: count of completed full scans (needed when removing unstable node)
  *
  * There is only the one ksm_scan instance of this cursor structure.
@@ -101,37 +103,51 @@ struct mm_slot {
 struct ksm_scan {
        struct mm_slot *mm_slot;
        unsigned long address;
-       struct rmap_item *rmap_item;
+       struct rmap_item **rmap_list;
        unsigned long seqnr;
 };
 
+/**
+ * struct stable_node - node of the stable rbtree
+ * @node: rb node of this ksm page in the stable tree
+ * @hlist: hlist head of rmap_items using this ksm page
+ * @kpfn: page frame number of this ksm page
+ */
+struct stable_node {
+       struct rb_node node;
+       struct hlist_head hlist;
+       unsigned long kpfn;
+};
+
 /**
  * struct rmap_item - reverse mapping item for virtual addresses
- * @link: link into mm_slot's rmap_list (rmap_list is per mm)
+ * @rmap_list: next rmap_item in mm_slot's singly-linked rmap_list
+ * @anon_vma: pointer to anon_vma for this mm,address, when in stable tree
  * @mm: the memory structure this rmap_item is pointing into
  * @address: the virtual address this rmap_item tracks (+ flags in low bits)
  * @oldchecksum: previous checksum of the page at that virtual address
- * @node: rb_node of this rmap_item in either unstable or stable tree
- * @next: next rmap_item hanging off the same node of the stable tree
- * @prev: previous rmap_item hanging off the same node of the stable tree
+ * @node: rb node of this rmap_item in the unstable tree
+ * @head: pointer to stable_node heading this list in the stable tree
+ * @hlist: link into hlist of rmap_items hanging off that stable_node
  */
 struct rmap_item {
-       struct list_head link;
+       struct rmap_item *rmap_list;
+       struct anon_vma *anon_vma;      /* when stable */
        struct mm_struct *mm;
        unsigned long address;          /* + low bits used for flags below */
+       unsigned int oldchecksum;       /* when unstable */
        union {
-               unsigned int oldchecksum;               /* when unstable */
-               struct rmap_item *next;                 /* when stable */
-       };
-       union {
-               struct rb_node node;                    /* when tree node */
-               struct rmap_item *prev;                 /* in stable list */
+               struct rb_node node;    /* when node of unstable tree */
+               struct {                /* when listed from stable tree */
+                       struct stable_node *head;
+                       struct hlist_node hlist;
+               };
        };
 };
 
 #define SEQNR_MASK     0x0ff   /* low bits of unstable tree seqnr */
-#define NODE_FLAG      0x100   /* is a node of unstable or stable tree */
-#define STABLE_FLAG    0x200   /* is a node or list item of stable tree */
+#define UNSTABLE_FLAG  0x100   /* is a node of the unstable tree */
+#define STABLE_FLAG    0x200   /* is listed from the stable tree */
 
 /* The stable and unstable tree heads */
 static struct rb_root root_stable_tree = RB_ROOT;
@@ -148,6 +164,7 @@ static struct ksm_scan ksm_scan = {
 };
 
 static struct kmem_cache *rmap_item_cache;
+static struct kmem_cache *stable_node_cache;
 static struct kmem_cache *mm_slot_cache;
 
 /* The number of nodes in the stable tree */
@@ -162,9 +179,6 @@ static unsigned long ksm_pages_unshared;
 /* The number of rmap_items in use: to calculate pages_volatile */
 static unsigned long ksm_rmap_items;
 
-/* Limit on the number of unswappable pages used */
-static unsigned long ksm_max_kernel_pages;
-
 /* Number of pages ksmd should scan in one batch */
 static unsigned int ksm_thread_pages_to_scan = 100;
 
@@ -190,13 +204,19 @@ static int __init ksm_slab_init(void)
        if (!rmap_item_cache)
                goto out;
 
+       stable_node_cache = KSM_KMEM_CACHE(stable_node, 0);
+       if (!stable_node_cache)
+               goto out_free1;
+
        mm_slot_cache = KSM_KMEM_CACHE(mm_slot, 0);
        if (!mm_slot_cache)
-               goto out_free;
+               goto out_free2;
 
        return 0;
 
-out_free:
+out_free2:
+       kmem_cache_destroy(stable_node_cache);
+out_free1:
        kmem_cache_destroy(rmap_item_cache);
 out:
        return -ENOMEM;
@@ -205,6 +225,7 @@ out:
 static void __init ksm_slab_free(void)
 {
        kmem_cache_destroy(mm_slot_cache);
+       kmem_cache_destroy(stable_node_cache);
        kmem_cache_destroy(rmap_item_cache);
        mm_slot_cache = NULL;
 }
@@ -226,6 +247,16 @@ static inline void free_rmap_item(struct rmap_item *rmap_item)
        kmem_cache_free(rmap_item_cache, rmap_item);
 }
 
+static inline struct stable_node *alloc_stable_node(void)
+{
+       return kmem_cache_alloc(stable_node_cache, GFP_KERNEL);
+}
+
+static inline void free_stable_node(struct stable_node *stable_node)
+{
+       kmem_cache_free(stable_node_cache, stable_node);
+}
+
 static inline struct mm_slot *alloc_mm_slot(void)
 {
        if (!mm_slot_cache)     /* initialization failed */
@@ -275,7 +306,6 @@ static void insert_to_mm_slots_hash(struct mm_struct *mm,
        bucket = &mm_slots_hash[((unsigned long)mm / sizeof(struct mm_struct))
                                % MM_SLOTS_HASH_HEADS];
        mm_slot->mm = mm;
-       INIT_LIST_HEAD(&mm_slot->rmap_list);
        hlist_add_head(&mm_slot->link, bucket);
 }
 
@@ -284,6 +314,25 @@ static inline int in_stable_tree(struct rmap_item *rmap_item)
        return rmap_item->address & STABLE_FLAG;
 }
 
+static void hold_anon_vma(struct rmap_item *rmap_item,
+                         struct anon_vma *anon_vma)
+{
+       rmap_item->anon_vma = anon_vma;
+       atomic_inc(&anon_vma->ksm_refcount);
+}
+
+static void drop_anon_vma(struct rmap_item *rmap_item)
+{
+       struct anon_vma *anon_vma = rmap_item->anon_vma;
+
+       if (atomic_dec_and_lock(&anon_vma->ksm_refcount, &anon_vma->lock)) {
+               int empty = list_empty(&anon_vma->head);
+               spin_unlock(&anon_vma->lock);
+               if (empty)
+                       anon_vma_free(anon_vma);
+       }
+}
+
 /*
  * ksmd, and unmerge_and_remove_all_rmap_items(), must not touch an mm's
  * page tables after it has passed through ksm_exit() - which, if necessary,
@@ -356,10 +405,18 @@ static int break_ksm(struct vm_area_struct *vma, unsigned long addr)
        return (ret & VM_FAULT_OOM) ? -ENOMEM : 0;
 }
 
-static void break_cow(struct mm_struct *mm, unsigned long addr)
+static void break_cow(struct rmap_item *rmap_item)
 {
+       struct mm_struct *mm = rmap_item->mm;
+       unsigned long addr = rmap_item->address;
        struct vm_area_struct *vma;
 
+       /*
+        * It is not an accident that whenever we want to break COW
+        * to undo, we also need to drop a reference to the anon_vma.
+        */
+       drop_anon_vma(rmap_item);
+
        down_read(&mm->mmap_sem);
        if (ksm_test_exit(mm))
                goto out;
@@ -403,21 +460,77 @@ out:              page = NULL;
        return page;
 }
 
+static void remove_node_from_stable_tree(struct stable_node *stable_node)
+{
+       struct rmap_item *rmap_item;
+       struct hlist_node *hlist;
+
+       hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
+               if (rmap_item->hlist.next)
+                       ksm_pages_sharing--;
+               else
+                       ksm_pages_shared--;
+               drop_anon_vma(rmap_item);
+               rmap_item->address &= PAGE_MASK;
+               cond_resched();
+       }
+
+       rb_erase(&stable_node->node, &root_stable_tree);
+       free_stable_node(stable_node);
+}
+
 /*
- * get_ksm_page: checks if the page at the virtual address in rmap_item
- * is still PageKsm, in which case we can trust the content of the page,
- * and it returns the gotten page; but NULL if the page has been zapped.
+ * get_ksm_page: checks if the page indicated by the stable node
+ * is still its ksm page, despite having held no reference to it.
+ * In which case we can trust the content of the page, and it
+ * returns the gotten page; but if the page has now been zapped,
+ * remove the stale node from the stable tree and return NULL.
+ *
+ * You would expect the stable_node to hold a reference to the ksm page.
+ * But if it increments the page's count, swapping out has to wait for
+ * ksmd to come around again before it can free the page, which may take
+ * seconds or even minutes: much too unresponsive.  So instead we use a
+ * "keyhole reference": access to the ksm page from the stable node peeps
+ * out through its keyhole to see if that page still holds the right key,
+ * pointing back to this stable node.  This relies on freeing a PageAnon
+ * page to reset its page->mapping to NULL, and relies on no other use of
+ * a page to put something that might look like our key in page->mapping.
+ *
+ * include/linux/pagemap.h page_cache_get_speculative() is a good reference,
+ * but this is different - made simpler by ksm_thread_mutex being held, but
+ * interesting for assuming that no other use of the struct page could ever
+ * put our expected_mapping into page->mapping (or a field of the union which
+ * coincides with page->mapping).  The RCU calls are not for KSM at all, but
+ * to keep the page_count protocol described with page_cache_get_speculative.
+ *
+ * Note: it is possible that get_ksm_page() will return NULL one moment,
+ * then page the next, if the page is in between page_freeze_refs() and
+ * page_unfreeze_refs(): this shouldn't be a problem anywhere, the page
+ * is on its way to being freed; but it is an anomaly to bear in mind.
  */
-static struct page *get_ksm_page(struct rmap_item *rmap_item)
+static struct page *get_ksm_page(struct stable_node *stable_node)
 {
        struct page *page;
-
-       page = get_mergeable_page(rmap_item);
-       if (page && !PageKsm(page)) {
+       void *expected_mapping;
+
+       page = pfn_to_page(stable_node->kpfn);
+       expected_mapping = (void *)stable_node +
+                               (PAGE_MAPPING_ANON | PAGE_MAPPING_KSM);
+       rcu_read_lock();
+       if (page->mapping != expected_mapping)
+               goto stale;
+       if (!get_page_unless_zero(page))
+               goto stale;
+       if (page->mapping != expected_mapping) {
                put_page(page);
-               page = NULL;
+               goto stale;
        }
+       rcu_read_unlock();
        return page;
+stale:
+       rcu_read_unlock();
+       remove_node_from_stable_tree(stable_node);
+       return NULL;
 }
 
 /*
@@ -426,35 +539,29 @@ static struct page *get_ksm_page(struct rmap_item *rmap_item)
  */
 static void remove_rmap_item_from_tree(struct rmap_item *rmap_item)
 {
-       if (in_stable_tree(rmap_item)) {
-               struct rmap_item *next_item = rmap_item->next;
-
-               if (rmap_item->address & NODE_FLAG) {
-                       if (next_item) {
-                               rb_replace_node(&rmap_item->node,
-                                               &next_item->node,
-                                               &root_stable_tree);
-                               next_item->address |= NODE_FLAG;
-                               ksm_pages_sharing--;
-                       } else {
-                               rb_erase(&rmap_item->node, &root_stable_tree);
-                               ksm_pages_shared--;
-                       }
-               } else {
-                       struct rmap_item *prev_item = rmap_item->prev;
+       if (rmap_item->address & STABLE_FLAG) {
+               struct stable_node *stable_node;
+               struct page *page;
 
-                       BUG_ON(prev_item->next != rmap_item);
-                       prev_item->next = next_item;
-                       if (next_item) {
-                               BUG_ON(next_item->prev != rmap_item);
-                               next_item->prev = rmap_item->prev;
-                       }
+               stable_node = rmap_item->head;
+               page = get_ksm_page(stable_node);
+               if (!page)
+                       goto out;
+
+               lock_page(page);
+               hlist_del(&rmap_item->hlist);
+               unlock_page(page);
+               put_page(page);
+
+               if (stable_node->hlist.first)
                        ksm_pages_sharing--;
-               }
+               else
+                       ksm_pages_shared--;
 
-               rmap_item->next = NULL;
+               drop_anon_vma(rmap_item);
+               rmap_item->address &= PAGE_MASK;
 
-       } else if (rmap_item->address & NODE_FLAG) {
+       } else if (rmap_item->address & UNSTABLE_FLAG) {
                unsigned char age;
                /*
                 * Usually ksmd can and must skip the rb_erase, because
@@ -467,24 +574,21 @@ static void remove_rmap_item_from_tree(struct rmap_item *rmap_item)
                BUG_ON(age > 1);
                if (!age)
                        rb_erase(&rmap_item->node, &root_unstable_tree);
+
                ksm_pages_unshared--;
+               rmap_item->address &= PAGE_MASK;
        }
-
-       rmap_item->address &= PAGE_MASK;
-
+out:
        cond_resched();         /* we're called from many long loops */
 }
 
 static void remove_trailing_rmap_items(struct mm_slot *mm_slot,
-                                      struct list_head *cur)
+                                      struct rmap_item **rmap_list)
 {
-       struct rmap_item *rmap_item;
-
-       while (cur != &mm_slot->rmap_list) {
-               rmap_item = list_entry(cur, struct rmap_item, link);
-               cur = cur->next;
+       while (*rmap_list) {
+               struct rmap_item *rmap_item = *rmap_list;
+               *rmap_list = rmap_item->rmap_list;
                remove_rmap_item_from_tree(rmap_item);
-               list_del(&rmap_item->link);
                free_rmap_item(rmap_item);
        }
 }
@@ -550,7 +654,7 @@ static int unmerge_and_remove_all_rmap_items(void)
                                goto error;
                }
 
-               remove_trailing_rmap_items(mm_slot, mm_slot->rmap_list.next);
+               remove_trailing_rmap_items(mm_slot, &mm_slot->rmap_list);
 
                spin_lock(&ksm_mmlist_lock);
                ksm_scan.mm_slot = list_entry(mm_slot->mm_list.next,
@@ -646,7 +750,7 @@ static int write_protect_page(struct vm_area_struct *vma, struct page *page,
                 * Check that no O_DIRECT or similar I/O is in progress on the
                 * page
                 */
-               if ((page_mapcount(page) + 2 + swapped) != page_count(page)) {
+               if (page_mapcount(page) + 1 + swapped != page_count(page)) {
                        set_pte_at_notify(mm, addr, ptep, entry);
                        goto out_unlock;
                }
@@ -664,15 +768,15 @@ out:
 
 /**
  * replace_page - replace page in vma by new ksm page
- * @vma:      vma that holds the pte pointing to oldpage
- * @oldpage:  the page we are replacing by newpage
- * @newpage:  the ksm page we replace oldpage by
+ * @vma:      vma that holds the pte pointing to page
+ * @page:     the page we are replacing by kpage
+ * @kpage:    the ksm page we replace page by
  * @orig_pte: the original value of the pte
  *
  * Returns 0 on success, -EFAULT on failure.
  */
-static int replace_page(struct vm_area_struct *vma, struct page *oldpage,
-                       struct page *newpage, pte_t orig_pte)
+static int replace_page(struct vm_area_struct *vma, struct page *page,
+                       struct page *kpage, pte_t orig_pte)
 {
        struct mm_struct *mm = vma->vm_mm;
        pgd_t *pgd;
@@ -681,12 +785,9 @@ static int replace_page(struct vm_area_struct *vma, struct page *oldpage,
        pte_t *ptep;
        spinlock_t *ptl;
        unsigned long addr;
-       pgprot_t prot;
        int err = -EFAULT;
 
-       prot = vm_get_page_prot(vma->vm_flags & ~VM_WRITE);
-
-       addr = page_address_in_vma(oldpage, vma);
+       addr = page_address_in_vma(page, vma);
        if (addr == -EFAULT)
                goto out;
 
@@ -708,15 +809,15 @@ static int replace_page(struct vm_area_struct *vma, struct page *oldpage,
                goto out;
        }
 
-       get_page(newpage);
-       page_add_ksm_rmap(newpage);
+       get_page(kpage);
+       page_add_anon_rmap(kpage, vma, addr);
 
        flush_cache_page(vma, addr, pte_pfn(*ptep));
        ptep_clear_flush(vma, addr, ptep);
-       set_pte_at_notify(mm, addr, ptep, mk_pte(newpage, prot));
+       set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot));
 
-       page_remove_rmap(oldpage);
-       put_page(oldpage);
+       page_remove_rmap(page);
+       put_page(page);
 
        pte_unmap_unlock(ptep, ptl);
        err = 0;
@@ -726,32 +827,27 @@ out:
 
 /*
  * try_to_merge_one_page - take two pages and merge them into one
- * @vma: the vma that hold the pte pointing into oldpage
- * @oldpage: the page that we want to replace with newpage
- * @newpage: the page that we want to map instead of oldpage
- *
- * Note:
- * oldpage should be a PageAnon page, while newpage should be a PageKsm page,
- * or a newly allocated kernel page which page_add_ksm_rmap will make PageKsm.
+ * @vma: the vma that holds the pte pointing to page
+ * @page: the PageAnon page that we want to replace with kpage
+ * @kpage: the PageKsm page that we want to map instead of page,
+ *         or NULL the first time when we want to use page as kpage.
  *
  * This function returns 0 if the pages were merged, -EFAULT otherwise.
  */
 static int try_to_merge_one_page(struct vm_area_struct *vma,
-                                struct page *oldpage,
-                                struct page *newpage)
+                                struct page *page, struct page *kpage)
 {
        pte_t orig_pte = __pte(0);
        int err = -EFAULT;
 
+       if (page == kpage)                      /* ksm page forked */
+               return 0;
+
        if (!(vma->vm_flags & VM_MERGEABLE))
                goto out;
-
-       if (!PageAnon(oldpage))
+       if (!PageAnon(page))
                goto out;
 
-       get_page(newpage);
-       get_page(oldpage);
-
        /*
         * We need the page lock to read a stable PageSwapCache in
         * write_protect_page().  We use trylock_page() instead of
@@ -759,26 +855,39 @@ static int try_to_merge_one_page(struct vm_area_struct *vma,
         * prefer to continue scanning and merging different pages,
         * then come back to this page when it is unlocked.
         */
-       if (!trylock_page(oldpage))
-               goto out_putpage;
+       if (!trylock_page(page))
+               goto out;
        /*
         * If this anonymous page is mapped only here, its pte may need
         * to be write-protected.  If it's mapped elsewhere, all of its
         * ptes are necessarily already write-protected.  But in either
         * case, we need to lock and check page_count is not raised.
         */
-       if (write_protect_page(vma, oldpage, &orig_pte)) {
-               unlock_page(oldpage);
-               goto out_putpage;
+       if (write_protect_page(vma, page, &orig_pte) == 0) {
+               if (!kpage) {
+                       /*
+                        * While we hold page lock, upgrade page from
+                        * PageAnon+anon_vma to PageKsm+NULL stable_node:
+                        * stable_tree_insert() will update stable_node.
+                        */
+                       set_page_stable_node(page, NULL);
+                       mark_page_accessed(page);
+                       err = 0;
+               } else if (pages_identical(page, kpage))
+                       err = replace_page(vma, page, kpage, orig_pte);
        }
-       unlock_page(oldpage);
 
-       if (pages_identical(oldpage, newpage))
-               err = replace_page(vma, oldpage, newpage, orig_pte);
+       if ((vma->vm_flags & VM_LOCKED) && kpage && !err) {
+               munlock_vma_page(page);
+               if (!PageMlocked(kpage)) {
+                       unlock_page(page);
+                       lock_page(kpage);
+                       mlock_vma_page(kpage);
+                       page = kpage;           /* for final unlock */
+               }
+       }
 
-out_putpage:
-       put_page(oldpage);
-       put_page(newpage);
+       unlock_page(page);
 out:
        return err;
 }
@@ -786,26 +895,31 @@ out:
 /*
  * try_to_merge_with_ksm_page - like try_to_merge_two_pages,
  * but no new kernel page is allocated: kpage must already be a ksm page.
+ *
+ * This function returns 0 if the pages were merged, -EFAULT otherwise.
  */
-static int try_to_merge_with_ksm_page(struct mm_struct *mm1,
-                                     unsigned long addr1,
-                                     struct page *page1,
-                                     struct page *kpage)
+static int try_to_merge_with_ksm_page(struct rmap_item *rmap_item,
+                                     struct page *page, struct page *kpage)
 {
+       struct mm_struct *mm = rmap_item->mm;
        struct vm_area_struct *vma;
        int err = -EFAULT;
 
-       down_read(&mm1->mmap_sem);
-       if (ksm_test_exit(mm1))
+       down_read(&mm->mmap_sem);
+       if (ksm_test_exit(mm))
+               goto out;
+       vma = find_vma(mm, rmap_item->address);
+       if (!vma || vma->vm_start > rmap_item->address)
                goto out;
 
-       vma = find_vma(mm1, addr1);
-       if (!vma || vma->vm_start > addr1)
+       err = try_to_merge_one_page(vma, page, kpage);
+       if (err)
                goto out;
 
-       err = try_to_merge_one_page(vma, page1, kpage);
+       /* Must get reference to anon_vma while still holding mmap_sem */
+       hold_anon_vma(rmap_item, vma->anon_vma);
 out:
-       up_read(&mm1->mmap_sem);
+       up_read(&mm->mmap_sem);
        return err;
 }
 
@@ -813,109 +927,73 @@ out:
  * try_to_merge_two_pages - take two identical pages and prepare them
  * to be merged into one page.
  *
- * This function returns 0 if we successfully mapped two identical pages
- * into one page, -EFAULT otherwise.
+ * This function returns the kpage if we successfully merged two identical
+ * pages into one ksm page, NULL otherwise.
  *
- * Note that this function allocates a new kernel page: if one of the pages
+ * Note that this function upgrades page to ksm page: if one of the pages
  * is already a ksm page, try_to_merge_with_ksm_page should be used.
  */
-static int try_to_merge_two_pages(struct mm_struct *mm1, unsigned long addr1,
-                                 struct page *page1, struct mm_struct *mm2,
-                                 unsigned long addr2, struct page *page2)
+static struct page *try_to_merge_two_pages(struct rmap_item *rmap_item,
+                                          struct page *page,
+                                          struct rmap_item *tree_rmap_item,
+                                          struct page *tree_page)
 {
-       struct vm_area_struct *vma;
-       struct page *kpage;
-       int err = -EFAULT;
-
-       /*
-        * The number of nodes in the stable tree
-        * is the number of kernel pages that we hold.
-        */
-       if (ksm_max_kernel_pages &&
-           ksm_max_kernel_pages <= ksm_pages_shared)
-               return err;
-
-       kpage = alloc_page(GFP_HIGHUSER);
-       if (!kpage)
-               return err;
-
-       down_read(&mm1->mmap_sem);
-       if (ksm_test_exit(mm1)) {
-               up_read(&mm1->mmap_sem);
-               goto out;
-       }
-       vma = find_vma(mm1, addr1);
-       if (!vma || vma->vm_start > addr1) {
-               up_read(&mm1->mmap_sem);
-               goto out;
-       }
-
-       copy_user_highpage(kpage, page1, addr1, vma);
-       err = try_to_merge_one_page(vma, page1, kpage);
-       up_read(&mm1->mmap_sem);
+       int err;
 
+       err = try_to_merge_with_ksm_page(rmap_item, page, NULL);
        if (!err) {
-               err = try_to_merge_with_ksm_page(mm2, addr2, page2, kpage);
+               err = try_to_merge_with_ksm_page(tree_rmap_item,
+                                                       tree_page, page);
                /*
                 * If that fails, we have a ksm page with only one pte
                 * pointing to it: so break it.
                 */
                if (err)
-                       break_cow(mm1, addr1);
+                       break_cow(rmap_item);
        }
-out:
-       put_page(kpage);
-       return err;
+       return err ? NULL : page;
 }
 
 /*
- * stable_tree_search - search page inside the stable tree
- * @page: the page that we are searching identical pages to.
- * @page2: pointer into identical page that we are holding inside the stable
- *        tree that we have found.
- * @rmap_item: the reverse mapping item
+ * stable_tree_search - search for page inside the stable tree
  *
  * This function checks if there is a page inside the stable tree
  * with identical content to the page that we are scanning right now.
  *
- * This function return rmap_item pointer to the identical item if found,
+ * This function returns the stable tree node of identical content if found,
  * NULL otherwise.
  */
-static struct rmap_item *stable_tree_search(struct page *page,
-                                           struct page **page2,
-                                           struct rmap_item *rmap_item)
+static struct page *stable_tree_search(struct page *page)
 {
        struct rb_node *node = root_stable_tree.rb_node;
+       struct stable_node *stable_node;
+
+       stable_node = page_stable_node(page);
+       if (stable_node) {                      /* ksm page forked */
+               get_page(page);
+               return page;
+       }
 
        while (node) {
-               struct rmap_item *tree_rmap_item, *next_rmap_item;
+               struct page *tree_page;
                int ret;
 
-               tree_rmap_item = rb_entry(node, struct rmap_item, node);
-               while (tree_rmap_item) {
-                       BUG_ON(!in_stable_tree(tree_rmap_item));
-                       cond_resched();
-                       page2[0] = get_ksm_page(tree_rmap_item);
-                       if (page2[0])
-                               break;
-                       next_rmap_item = tree_rmap_item->next;
-                       remove_rmap_item_from_tree(tree_rmap_item);
-                       tree_rmap_item = next_rmap_item;
-               }
-               if (!tree_rmap_item)
+               cond_resched();
+               stable_node = rb_entry(node, struct stable_node, node);
+               tree_page = get_ksm_page(stable_node);
+               if (!tree_page)
                        return NULL;
 
-               ret = memcmp_pages(page, page2[0]);
+               ret = memcmp_pages(page, tree_page);
 
                if (ret < 0) {
-                       put_page(page2[0]);
+                       put_page(tree_page);
                        node = node->rb_left;
                } else if (ret > 0) {
-                       put_page(page2[0]);
+                       put_page(tree_page);
                        node = node->rb_right;
-               } else {
-                       return tree_rmap_item;
-               }
+               } else
+                       return tree_page;
        }
 
        return NULL;
@@ -925,38 +1003,26 @@ static struct rmap_item *stable_tree_search(struct page *page,
  * stable_tree_insert - insert rmap_item pointing to new ksm page
  * into the stable tree.
  *
- * @page: the page that we are searching identical page to inside the stable
- *       tree.
- * @rmap_item: pointer to the reverse mapping item.
- *
- * This function returns rmap_item if success, NULL otherwise.
+ * This function returns the stable tree node just allocated on success,
+ * NULL otherwise.
  */
-static struct rmap_item *stable_tree_insert(struct page *page,
-                                           struct rmap_item *rmap_item)
+static struct stable_node *stable_tree_insert(struct page *kpage)
 {
        struct rb_node **new = &root_stable_tree.rb_node;
        struct rb_node *parent = NULL;
+       struct stable_node *stable_node;
 
        while (*new) {
-               struct rmap_item *tree_rmap_item, *next_rmap_item;
                struct page *tree_page;
                int ret;
 
-               tree_rmap_item = rb_entry(*new, struct rmap_item, node);
-               while (tree_rmap_item) {
-                       BUG_ON(!in_stable_tree(tree_rmap_item));
-                       cond_resched();
-                       tree_page = get_ksm_page(tree_rmap_item);
-                       if (tree_page)
-                               break;
-                       next_rmap_item = tree_rmap_item->next;
-                       remove_rmap_item_from_tree(tree_rmap_item);
-                       tree_rmap_item = next_rmap_item;
-               }
-               if (!tree_rmap_item)
+               cond_resched();
+               stable_node = rb_entry(*new, struct stable_node, node);
+               tree_page = get_ksm_page(stable_node);
+               if (!tree_page)
                        return NULL;
 
-               ret = memcmp_pages(page, tree_page);
+               ret = memcmp_pages(kpage, tree_page);
                put_page(tree_page);
 
                parent = *new;
@@ -974,22 +1040,24 @@ static struct rmap_item *stable_tree_insert(struct page *page,
                }
        }
 
-       rmap_item->address |= NODE_FLAG | STABLE_FLAG;
-       rmap_item->next = NULL;
-       rb_link_node(&rmap_item->node, parent, new);
-       rb_insert_color(&rmap_item->node, &root_stable_tree);
+       stable_node = alloc_stable_node();
+       if (!stable_node)
+               return NULL;
 
-       ksm_pages_shared++;
-       return rmap_item;
+       rb_link_node(&stable_node->node, parent, new);
+       rb_insert_color(&stable_node->node, &root_stable_tree);
+
+       INIT_HLIST_HEAD(&stable_node->hlist);
+
+       stable_node->kpfn = page_to_pfn(kpage);
+       set_page_stable_node(kpage, stable_node);
+
+       return stable_node;
 }
 
 /*
- * unstable_tree_search_insert - search and insert items into the unstable tree.
- *
- * @page: the page that we are going to search for identical page or to insert
- *       into the unstable tree
- * @page2: pointer into identical page that was found inside the unstable tree
- * @rmap_item: the reverse mapping item of page
+ * unstable_tree_search_insert - search for identical page,
+ * else insert rmap_item into the unstable tree.
  *
  * This function searches for a page in the unstable tree identical to the
  * page currently being scanned; and if no identical page is found in the
@@ -1001,47 +1069,50 @@ static struct rmap_item *stable_tree_insert(struct page *page,
  * This function does both searching and inserting, because they share
  * the same walking algorithm in an rbtree.
  */
-static struct rmap_item *unstable_tree_search_insert(struct page *page,
-                                               struct page **page2,
-                                               struct rmap_item *rmap_item)
+static
+struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
+                                             struct page *page,
+                                             struct page **tree_pagep)
+
 {
        struct rb_node **new = &root_unstable_tree.rb_node;
        struct rb_node *parent = NULL;
 
        while (*new) {
                struct rmap_item *tree_rmap_item;
+               struct page *tree_page;
                int ret;
 
                cond_resched();
                tree_rmap_item = rb_entry(*new, struct rmap_item, node);
-               page2[0] = get_mergeable_page(tree_rmap_item);
-               if (!page2[0])
+               tree_page = get_mergeable_page(tree_rmap_item);
+               if (!tree_page)
                        return NULL;
 
                /*
-                * Don't substitute an unswappable ksm page
-                * just for one good swappable forked page.
+                * Don't substitute a ksm page for a forked page.
                 */
-               if (page == page2[0]) {
-                       put_page(page2[0]);
+               if (page == tree_page) {
+                       put_page(tree_page);
                        return NULL;
                }
 
-               ret = memcmp_pages(page, page2[0]);
+               ret = memcmp_pages(page, tree_page);
 
                parent = *new;
                if (ret < 0) {
-                       put_page(page2[0]);
+                       put_page(tree_page);
                        new = &parent->rb_left;
                } else if (ret > 0) {
-                       put_page(page2[0]);
+                       put_page(tree_page);
                        new = &parent->rb_right;
                } else {
+                       *tree_pagep = tree_page;
                        return tree_rmap_item;
                }
        }
 
-       rmap_item->address |= NODE_FLAG;
+       rmap_item->address |= UNSTABLE_FLAG;
        rmap_item->address |= (ksm_scan.seqnr & SEQNR_MASK);
        rb_link_node(&rmap_item->node, parent, new);
        rb_insert_color(&rmap_item->node, &root_unstable_tree);
@@ -1056,18 +1127,16 @@ static struct rmap_item *unstable_tree_search_insert(struct page *page,
  * the same ksm page.
  */
 static void stable_tree_append(struct rmap_item *rmap_item,
-                              struct rmap_item *tree_rmap_item)
+                              struct stable_node *stable_node)
 {
-       rmap_item->next = tree_rmap_item->next;
-       rmap_item->prev = tree_rmap_item;
-
-       if (tree_rmap_item->next)
-               tree_rmap_item->next->prev = rmap_item;
-
-       tree_rmap_item->next = rmap_item;
+       rmap_item->head = stable_node;
        rmap_item->address |= STABLE_FLAG;
+       hlist_add_head(&rmap_item->hlist, &stable_node->hlist);
 
-       ksm_pages_sharing++;
+       if (rmap_item->hlist.next)
+               ksm_pages_sharing++;
+       else
+               ksm_pages_shared++;
 }
 
 /*
@@ -1081,49 +1150,37 @@ static void stable_tree_append(struct rmap_item *rmap_item,
  */
 static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item)
 {
-       struct page *page2[1];
        struct rmap_item *tree_rmap_item;
+       struct page *tree_page = NULL;
+       struct stable_node *stable_node;
+       struct page *kpage;
        unsigned int checksum;
        int err;
 
-       if (in_stable_tree(rmap_item))
-               remove_rmap_item_from_tree(rmap_item);
+       remove_rmap_item_from_tree(rmap_item);
 
        /* We first start with searching the page inside the stable tree */
-       tree_rmap_item = stable_tree_search(page, page2, rmap_item);
-       if (tree_rmap_item) {
-               if (page == page2[0])                   /* forked */
-                       err = 0;
-               else
-                       err = try_to_merge_with_ksm_page(rmap_item->mm,
-                                                        rmap_item->address,
-                                                        page, page2[0]);
-               put_page(page2[0]);
-
+       kpage = stable_tree_search(page);
+       if (kpage) {
+               err = try_to_merge_with_ksm_page(rmap_item, page, kpage);
                if (!err) {
                        /*
                         * The page was successfully merged:
                         * add its rmap_item to the stable tree.
                         */
-                       stable_tree_append(rmap_item, tree_rmap_item);
+                       lock_page(kpage);
+                       stable_tree_append(rmap_item, page_stable_node(kpage));
+                       unlock_page(kpage);
                }
+               put_page(kpage);
                return;
        }
 
        /*
-        * A ksm page might have got here by fork, but its other
-        * references have already been removed from the stable tree.
-        * Or it might be left over from a break_ksm which failed
-        * when the mem_cgroup had reached its limit: try again now.
-        */
-       if (PageKsm(page))
-               break_cow(rmap_item->mm, rmap_item->address);
-
-       /*
-        * In case the hash value of the page was changed from the last time we
-        * have calculated it, this page to be changed frequely, therefore we
-        * don't want to insert it to the unstable tree, and we don't want to
-        * waste our time to search if there is something identical to it there.
+        * If the hash value of the page has changed from the last time
+        * we calculated it, this page is changing frequently: therefore we
+        * don't want to insert it in the unstable tree, and we don't want
+        * to waste our time searching for something identical to it there.
         */
        checksum = calc_checksum(page);
        if (rmap_item->oldchecksum != checksum) {
@@ -1131,21 +1188,27 @@ static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item)
                return;
        }
 
-       tree_rmap_item = unstable_tree_search_insert(page, page2, rmap_item);
+       tree_rmap_item =
+               unstable_tree_search_insert(rmap_item, page, &tree_page);
        if (tree_rmap_item) {
-               err = try_to_merge_two_pages(rmap_item->mm,
-                                            rmap_item->address, page,
-                                            tree_rmap_item->mm,
-                                            tree_rmap_item->address, page2[0]);
+               kpage = try_to_merge_two_pages(rmap_item, page,
+                                               tree_rmap_item, tree_page);
+               put_page(tree_page);
                /*
                 * As soon as we merge this page, we want to remove the
                 * rmap_item of the page we have merged with from the unstable
                 * tree, and insert it instead as new node in the stable tree.
                 */
-               if (!err) {
-                       rb_erase(&tree_rmap_item->node, &root_unstable_tree);
-                       tree_rmap_item->address &= ~NODE_FLAG;
-                       ksm_pages_unshared--;
+               if (kpage) {
+                       remove_rmap_item_from_tree(tree_rmap_item);
+
+                       lock_page(kpage);
+                       stable_node = stable_tree_insert(kpage);
+                       if (stable_node) {
+                               stable_tree_append(tree_rmap_item, stable_node);
+                               stable_tree_append(rmap_item, stable_node);
+                       }
+                       unlock_page(kpage);
 
                        /*
                         * If we fail to insert the page into the stable tree,
@@ -1153,37 +1216,28 @@ static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item)
                         * to a ksm page left outside the stable tree,
                         * in which case we need to break_cow on both.
                         */
-                       if (stable_tree_insert(page2[0], tree_rmap_item))
-                               stable_tree_append(rmap_item, tree_rmap_item);
-                       else {
-                               break_cow(tree_rmap_item->mm,
-                                               tree_rmap_item->address);
-                               break_cow(rmap_item->mm, rmap_item->address);
+                       if (!stable_node) {
+                               break_cow(tree_rmap_item);
+                               break_cow(rmap_item);
                        }
                }
-
-               put_page(page2[0]);
        }
 }
 
 static struct rmap_item *get_next_rmap_item(struct mm_slot *mm_slot,
-                                           struct list_head *cur,
+                                           struct rmap_item **rmap_list,
                                            unsigned long addr)
 {
        struct rmap_item *rmap_item;
 
-       while (cur != &mm_slot->rmap_list) {
-               rmap_item = list_entry(cur, struct rmap_item, link);
-               if ((rmap_item->address & PAGE_MASK) == addr) {
-                       if (!in_stable_tree(rmap_item))
-                               remove_rmap_item_from_tree(rmap_item);
+       while (*rmap_list) {
+               rmap_item = *rmap_list;
+               if ((rmap_item->address & PAGE_MASK) == addr)
                        return rmap_item;
-               }
                if (rmap_item->address > addr)
                        break;
-               cur = cur->next;
+               *rmap_list = rmap_item->rmap_list;
                remove_rmap_item_from_tree(rmap_item);
-               list_del(&rmap_item->link);
                free_rmap_item(rmap_item);
        }
 
@@ -1192,7 +1246,8 @@ static struct rmap_item *get_next_rmap_item(struct mm_slot *mm_slot,
                /* It has already been zeroed */
                rmap_item->mm = mm_slot->mm;
                rmap_item->address = addr;
-               list_add_tail(&rmap_item->link, cur);
+               rmap_item->rmap_list = *rmap_list;
+               *rmap_list = rmap_item;
        }
        return rmap_item;
 }
@@ -1217,8 +1272,7 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
                spin_unlock(&ksm_mmlist_lock);
 next_mm:
                ksm_scan.address = 0;
-               ksm_scan.rmap_item = list_entry(&slot->rmap_list,
-                                               struct rmap_item, link);
+               ksm_scan.rmap_list = &slot->rmap_list;
        }
 
        mm = slot->mm;
@@ -1244,10 +1298,10 @@ next_mm:
                                flush_anon_page(vma, *page, ksm_scan.address);
                                flush_dcache_page(*page);
                                rmap_item = get_next_rmap_item(slot,
-                                       ksm_scan.rmap_item->link.next,
-                                       ksm_scan.address);
+                                       ksm_scan.rmap_list, ksm_scan.address);
                                if (rmap_item) {
-                                       ksm_scan.rmap_item = rmap_item;
+                                       ksm_scan.rmap_list =
+                                                       &rmap_item->rmap_list;
                                        ksm_scan.address += PAGE_SIZE;
                                } else
                                        put_page(*page);
@@ -1263,14 +1317,13 @@ next_mm:
 
        if (ksm_test_exit(mm)) {
                ksm_scan.address = 0;
-               ksm_scan.rmap_item = list_entry(&slot->rmap_list,
-                                               struct rmap_item, link);
+               ksm_scan.rmap_list = &slot->rmap_list;
        }
        /*
         * Nuke all the rmap_items that are above this current rmap:
         * because there were no VM_MERGEABLE vmas with such addresses.
         */
-       remove_trailing_rmap_items(slot, ksm_scan.rmap_item->link.next);
+       remove_trailing_rmap_items(slot, ksm_scan.rmap_list);
 
        spin_lock(&ksm_mmlist_lock);
        ksm_scan.mm_slot = list_entry(slot->mm_list.next,
@@ -1323,14 +1376,6 @@ static void ksm_do_scan(unsigned int scan_npages)
                        return;
                if (!PageKsm(page) || !in_stable_tree(rmap_item))
                        cmp_and_merge_page(page, rmap_item);
-               else if (page_mapcount(page) == 1) {
-                       /*
-                        * Replace now-unshared ksm page by ordinary page.
-                        */
-                       break_cow(rmap_item->mm, rmap_item->address);
-                       remove_rmap_item_from_tree(rmap_item);
-                       rmap_item->oldchecksum = calc_checksum(page);
-               }
                put_page(page);
        }
 }
@@ -1375,7 +1420,7 @@ int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
                if (*vm_flags & (VM_MERGEABLE | VM_SHARED  | VM_MAYSHARE   |
                                 VM_PFNMAP    | VM_IO      | VM_DONTEXPAND |
                                 VM_RESERVED  | VM_HUGETLB | VM_INSERTPAGE |
-                                VM_MIXEDMAP  | VM_SAO))
+                                VM_NONLINEAR | VM_MIXEDMAP | VM_SAO))
                        return 0;               /* just ignore the advice */
 
                if (!test_bit(MMF_VM_MERGEABLE, &mm->flags)) {
@@ -1452,7 +1497,7 @@ void __ksm_exit(struct mm_struct *mm)
        spin_lock(&ksm_mmlist_lock);
        mm_slot = get_mm_slot(mm);
        if (mm_slot && ksm_scan.mm_slot != mm_slot) {
-               if (list_empty(&mm_slot->rmap_list)) {
+               if (!mm_slot->rmap_list) {
                        hlist_del(&mm_slot->link);
                        list_del(&mm_slot->mm_list);
                        easy_to_free = 1;
@@ -1473,6 +1518,249 @@ void __ksm_exit(struct mm_struct *mm)
        }
 }
 
+struct page *ksm_does_need_to_copy(struct page *page,
+                       struct vm_area_struct *vma, unsigned long address)
+{
+       struct page *new_page;
+
+       unlock_page(page);      /* any racers will COW it, not modify it */
+
+       new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
+       if (new_page) {
+               copy_user_highpage(new_page, page, address, vma);
+
+               SetPageDirty(new_page);
+               __SetPageUptodate(new_page);
+               SetPageSwapBacked(new_page);
+               __set_page_locked(new_page);
+
+               if (page_evictable(new_page, vma))
+                       lru_cache_add_lru(new_page, LRU_ACTIVE_ANON);
+               else
+                       add_page_to_unevictable_list(new_page);
+       }
+
+       page_cache_release(page);
+       return new_page;
+}
+
+int page_referenced_ksm(struct page *page, struct mem_cgroup *memcg,
+                       unsigned long *vm_flags)
+{
+       struct stable_node *stable_node;
+       struct rmap_item *rmap_item;
+       struct hlist_node *hlist;
+       unsigned int mapcount = page_mapcount(page);
+       int referenced = 0;
+       int search_new_forks = 0;
+
+       VM_BUG_ON(!PageKsm(page));
+       VM_BUG_ON(!PageLocked(page));
+
+       stable_node = page_stable_node(page);
+       if (!stable_node)
+               return 0;
+again:
+       hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
+               struct anon_vma *anon_vma = rmap_item->anon_vma;
+               struct vm_area_struct *vma;
+
+               spin_lock(&anon_vma->lock);
+               list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+                       if (rmap_item->address < vma->vm_start ||
+                           rmap_item->address >= vma->vm_end)
+                               continue;
+                       /*
+                        * Initially we examine only the vma which covers this
+                        * rmap_item; but later, if there is still work to do,
+                        * we examine covering vmas in other mms: in case they
+                        * were forked from the original since ksmd passed.
+                        */
+                       if ((rmap_item->mm == vma->vm_mm) == search_new_forks)
+                               continue;
+
+                       if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
+                               continue;
+
+                       referenced += page_referenced_one(page, vma,
+                               rmap_item->address, &mapcount, vm_flags);
+                       if (!search_new_forks || !mapcount)
+                               break;
+               }
+               spin_unlock(&anon_vma->lock);
+               if (!mapcount)
+                       goto out;
+       }
+       if (!search_new_forks++)
+               goto again;
+out:
+       return referenced;
+}
+
+int try_to_unmap_ksm(struct page *page, enum ttu_flags flags)
+{
+       struct stable_node *stable_node;
+       struct hlist_node *hlist;
+       struct rmap_item *rmap_item;
+       int ret = SWAP_AGAIN;
+       int search_new_forks = 0;
+
+       VM_BUG_ON(!PageKsm(page));
+       VM_BUG_ON(!PageLocked(page));
+
+       stable_node = page_stable_node(page);
+       if (!stable_node)
+               return SWAP_FAIL;
+again:
+       hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
+               struct anon_vma *anon_vma = rmap_item->anon_vma;
+               struct vm_area_struct *vma;
+
+               spin_lock(&anon_vma->lock);
+               list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+                       if (rmap_item->address < vma->vm_start ||
+                           rmap_item->address >= vma->vm_end)
+                               continue;
+                       /*
+                        * Initially we examine only the vma which covers this
+                        * rmap_item; but later, if there is still work to do,
+                        * we examine covering vmas in other mms: in case they
+                        * were forked from the original since ksmd passed.
+                        */
+                       if ((rmap_item->mm == vma->vm_mm) == search_new_forks)
+                               continue;
+
+                       ret = try_to_unmap_one(page, vma,
+                                       rmap_item->address, flags);
+                       if (ret != SWAP_AGAIN || !page_mapped(page)) {
+                               spin_unlock(&anon_vma->lock);
+                               goto out;
+                       }
+               }
+               spin_unlock(&anon_vma->lock);
+       }
+       if (!search_new_forks++)
+               goto again;
+out:
+       return ret;
+}
+
+#ifdef CONFIG_MIGRATION
+int rmap_walk_ksm(struct page *page, int (*rmap_one)(struct page *,
+                 struct vm_area_struct *, unsigned long, void *), void *arg)
+{
+       struct stable_node *stable_node;
+       struct hlist_node *hlist;
+       struct rmap_item *rmap_item;
+       int ret = SWAP_AGAIN;
+       int search_new_forks = 0;
+
+       VM_BUG_ON(!PageKsm(page));
+       VM_BUG_ON(!PageLocked(page));
+
+       stable_node = page_stable_node(page);
+       if (!stable_node)
+               return ret;
+again:
+       hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
+               struct anon_vma *anon_vma = rmap_item->anon_vma;
+               struct vm_area_struct *vma;
+
+               spin_lock(&anon_vma->lock);
+               list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+                       if (rmap_item->address < vma->vm_start ||
+                           rmap_item->address >= vma->vm_end)
+                               continue;
+                       /*
+                        * Initially we examine only the vma which covers this
+                        * rmap_item; but later, if there is still work to do,
+                        * we examine covering vmas in other mms: in case they
+                        * were forked from the original since ksmd passed.
+                        */
+                       if ((rmap_item->mm == vma->vm_mm) == search_new_forks)
+                               continue;
+
+                       ret = rmap_one(page, vma, rmap_item->address, arg);
+                       if (ret != SWAP_AGAIN) {
+                               spin_unlock(&anon_vma->lock);
+                               goto out;
+                       }
+               }
+               spin_unlock(&anon_vma->lock);
+       }
+       if (!search_new_forks++)
+               goto again;
+out:
+       return ret;
+}
+
+void ksm_migrate_page(struct page *newpage, struct page *oldpage)
+{
+       struct stable_node *stable_node;
+
+       VM_BUG_ON(!PageLocked(oldpage));
+       VM_BUG_ON(!PageLocked(newpage));
+       VM_BUG_ON(newpage->mapping != oldpage->mapping);
+
+       stable_node = page_stable_node(newpage);
+       if (stable_node) {
+               VM_BUG_ON(stable_node->kpfn != page_to_pfn(oldpage));
+               stable_node->kpfn = page_to_pfn(newpage);
+       }
+}
+#endif /* CONFIG_MIGRATION */
+
+#ifdef CONFIG_MEMORY_HOTREMOVE
+static struct stable_node *ksm_check_stable_tree(unsigned long start_pfn,
+                                                unsigned long end_pfn)
+{
+       struct rb_node *node;
+
+       for (node = rb_first(&root_stable_tree); node; node = rb_next(node)) {
+               struct stable_node *stable_node;
+
+               stable_node = rb_entry(node, struct stable_node, node);
+               if (stable_node->kpfn >= start_pfn &&
+                   stable_node->kpfn < end_pfn)
+                       return stable_node;
+       }
+       return NULL;
+}
+
+static int ksm_memory_callback(struct notifier_block *self,
+                              unsigned long action, void *arg)
+{
+       struct memory_notify *mn = arg;
+       struct stable_node *stable_node;
+
+       switch (action) {
+       case MEM_GOING_OFFLINE:
+               /*
+                * Keep it very simple for now: just lock out ksmd and
+                * MADV_UNMERGEABLE while any memory is going offline.
+                */
+               mutex_lock(&ksm_thread_mutex);
+               break;
+
+       case MEM_OFFLINE:
+               /*
+                * Most of the work is done by page migration; but there might
+                * be a few stable_nodes left over, still pointing to struct
+                * pages which have been offlined: prune those from the tree.
+                */
+               while ((stable_node = ksm_check_stable_tree(mn->start_pfn,
+                                       mn->start_pfn + mn->nr_pages)) != NULL)
+                       remove_node_from_stable_tree(stable_node);
+               /* fallthrough */
+
+       case MEM_CANCEL_OFFLINE:
+               mutex_unlock(&ksm_thread_mutex);
+               break;
+       }
+       return NOTIFY_OK;
+}
+#endif /* CONFIG_MEMORY_HOTREMOVE */
+
 #ifdef CONFIG_SYSFS
 /*
  * This all compiles without CONFIG_SYSFS, but is a waste of space.
@@ -1551,8 +1839,8 @@ static ssize_t run_store(struct kobject *kobj, struct kobj_attribute *attr,
        /*
         * KSM_RUN_MERGE sets ksmd running, and 0 stops it running.
         * KSM_RUN_UNMERGE stops it running and unmerges all rmap_items,
-        * breaking COW to free the unswappable pages_shared (but leaves
-        * mm_slots on the list for when ksmd may be set running again).
+        * breaking COW to free the pages_shared (but leaves mm_slots
+        * on the list for when ksmd may be set running again).
         */
 
        mutex_lock(&ksm_thread_mutex);
@@ -1577,29 +1865,6 @@ static ssize_t run_store(struct kobject *kobj, struct kobj_attribute *attr,
 }
 KSM_ATTR(run);
 
-static ssize_t max_kernel_pages_store(struct kobject *kobj,
-                                     struct kobj_attribute *attr,
-                                     const char *buf, size_t count)
-{
-       int err;
-       unsigned long nr_pages;
-
-       err = strict_strtoul(buf, 10, &nr_pages);
-       if (err)
-               return -EINVAL;
-
-       ksm_max_kernel_pages = nr_pages;
-
-       return count;
-}
-
-static ssize_t max_kernel_pages_show(struct kobject *kobj,
-                                    struct kobj_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%lu\n", ksm_max_kernel_pages);
-}
-KSM_ATTR(max_kernel_pages);
-
 static ssize_t pages_shared_show(struct kobject *kobj,
                                 struct kobj_attribute *attr, char *buf)
 {
@@ -1649,7 +1914,6 @@ static struct attribute *ksm_attrs[] = {
        &sleep_millisecs_attr.attr,
        &pages_to_scan_attr.attr,
        &run_attr.attr,
-       &max_kernel_pages_attr.attr,
        &pages_shared_attr.attr,
        &pages_sharing_attr.attr,
        &pages_unshared_attr.attr,
@@ -1669,8 +1933,6 @@ static int __init ksm_init(void)
        struct task_struct *ksm_thread;
        int err;
 
-       ksm_max_kernel_pages = totalram_pages / 4;
-
        err = ksm_slab_init();
        if (err)
                goto out;
@@ -1698,6 +1960,13 @@ static int __init ksm_init(void)
 
 #endif /* CONFIG_SYSFS */
 
+#ifdef CONFIG_MEMORY_HOTREMOVE
+       /*
+        * Choose a high priority since the callback takes ksm_thread_mutex:
+        * later callbacks could only be taking locks which nest within that.
+        */
+       hotplug_memory_notifier(ksm_memory_callback, 100);
+#endif
        return 0;
 
 out_free2:
index c31a310aa146213d822060ad716a7d94b65a349f..e0c2066495e3fd4adf19254266df619048546cdb 100644 (file)
@@ -1737,11 +1737,12 @@ int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
                goto charge_cur_mm;
        /*
         * A racing thread's fault, or swapoff, may have already updated
-        * the pte, and even removed page from swap cache: return success
-        * to go on to do_swap_page()'s pte_same() test, which should fail.
+        * the pte, and even removed page from swap cache: in those cases
+        * do_swap_page()'s pte_same() test will fail; but there's also a
+        * KSM case which does need to charge the page.
         */
        if (!PageSwapCache(page))
-               return 0;
+               goto charge_cur_mm;
        mem = try_get_mem_cgroup_from_swapcache(page);
        if (!mem)
                goto charge_cur_mm;
index 1ac49fef95ab43b716979a360375c66be706160c..50d4f8d7024a04280c14eb2a47e2ad5151230987 100644 (file)
@@ -582,10 +582,8 @@ static struct page_state {
        { unevict|dirty, unevict|dirty, "unevictable LRU", me_pagecache_dirty},
        { unevict,      unevict,        "unevictable LRU", me_pagecache_clean},
 
-#ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT
        { mlock|dirty,  mlock|dirty,    "mlocked LRU",  me_pagecache_dirty },
        { mlock,        mlock,          "mlocked LRU",  me_pagecache_clean },
-#endif
 
        { lru|dirty,    lru|dirty,      "LRU",          me_pagecache_dirty },
        { lru|dirty,    lru,            "clean LRU",    me_pagecache_clean },
index 6ab19dd4a1990f3f463c1db3eee4f19ce4f5a3de..a54b2c498444dca8d8b41bb59d65640915f1a684 100644 (file)
@@ -572,7 +572,7 @@ out:
  * covered by this vma.
  */
 
-static inline void
+static inline unsigned long
 copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                pte_t *dst_pte, pte_t *src_pte, struct vm_area_struct *vma,
                unsigned long addr, int *rss)
@@ -586,7 +586,9 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                if (!pte_file(pte)) {
                        swp_entry_t entry = pte_to_swp_entry(pte);
 
-                       swap_duplicate(entry);
+                       if (swap_duplicate(entry) < 0)
+                               return entry.val;
+
                        /* make sure dst_mm is on swapoff's mmlist. */
                        if (unlikely(list_empty(&dst_mm->mmlist))) {
                                spin_lock(&mmlist_lock);
@@ -635,6 +637,7 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
 
 out_set_pte:
        set_pte_at(dst_mm, addr, dst_pte, pte);
+       return 0;
 }
 
 static int copy_pte_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
@@ -646,6 +649,7 @@ static int copy_pte_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        spinlock_t *src_ptl, *dst_ptl;
        int progress = 0;
        int rss[2];
+       swp_entry_t entry = (swp_entry_t){0};
 
 again:
        rss[1] = rss[0] = 0;
@@ -674,7 +678,10 @@ again:
                        progress++;
                        continue;
                }
-               copy_one_pte(dst_mm, src_mm, dst_pte, src_pte, vma, addr, rss);
+               entry.val = copy_one_pte(dst_mm, src_mm, dst_pte, src_pte,
+                                                       vma, addr, rss);
+               if (entry.val)
+                       break;
                progress += 8;
        } while (dst_pte++, src_pte++, addr += PAGE_SIZE, addr != end);
 
@@ -684,6 +691,12 @@ again:
        add_mm_rss(dst_mm, rss[0], rss[1]);
        pte_unmap_unlock(orig_dst_pte, dst_ptl);
        cond_resched();
+
+       if (entry.val) {
+               if (add_swap_count_continuation(entry, GFP_KERNEL) < 0)
+                       return -ENOMEM;
+               progress = 0;
+       }
        if (addr != end)
                goto again;
        return 0;
@@ -2514,7 +2527,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        ret = VM_FAULT_HWPOISON;
                } else {
                        print_bad_pte(vma, address, orig_pte, NULL);
-                       ret = VM_FAULT_OOM;
+                       ret = VM_FAULT_SIGBUS;
                }
                goto out;
        }
@@ -2548,6 +2561,12 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
        lock_page(page);
        delayacct_clear_flag(DELAYACCT_PF_SWAPIN);
 
+       page = ksm_might_need_to_copy(page, vma, address);
+       if (!page) {
+               ret = VM_FAULT_OOM;
+               goto out;
+       }
+
        if (mem_cgroup_try_charge_swapin(mm, page, GFP_KERNEL, &ptr)) {
                ret = VM_FAULT_OOM;
                goto out_page;
@@ -2910,7 +2929,7 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                 * Page table corrupted: show pte and kill process.
                 */
                print_bad_pte(vma, address, orig_pte, NULL);
-               return VM_FAULT_OOM;
+               return VM_FAULT_SIGBUS;
        }
 
        pgoff = pte_to_pgoff(orig_pte);
index 2047465cd27cf5b1829979057459857184a42b2e..030ce8a5bb0e758e7ea04cc0c851955275356426 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/page-isolation.h>
 #include <linux/pfn.h>
 #include <linux/suspend.h>
+#include <linux/mm_inline.h>
 
 #include <asm/tlbflush.h>
 
@@ -71,7 +72,9 @@ static void get_page_bootmem(unsigned long info,  struct page *page, int type)
        atomic_inc(&page->_count);
 }
 
-void put_page_bootmem(struct page *page)
+/* reference to __meminit __free_pages_bootmem is valid
+ * so use __ref to tell modpost not to generate a warning */
+void __ref put_page_bootmem(struct page *page)
 {
        int type;
 
@@ -672,6 +675,9 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                if (!ret) { /* Success */
                        list_add_tail(&page->lru, &source);
                        move_pages--;
+                       inc_zone_page_state(page, NR_ISOLATED_ANON +
+                                           page_is_file_cache(page));
+
                } else {
                        /* Becasue we don't have big zone->lock. we should
                           check this again here. */
@@ -694,7 +700,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
        if (list_empty(&source))
                goto out;
        /* this function returns # of failed pages */
-       ret = migrate_pages(&source, hotremove_migrate_alloc, 0);
+       ret = migrate_pages(&source, hotremove_migrate_alloc, 0, 1);
 
 out:
        return ret;
@@ -747,7 +753,7 @@ check_pages_isolated(unsigned long start_pfn, unsigned long end_pfn)
        return offlined;
 }
 
-int offline_pages(unsigned long start_pfn,
+static int offline_pages(unsigned long start_pfn,
                  unsigned long end_pfn, unsigned long timeout)
 {
        unsigned long pfn, nr_pages, expire;
@@ -849,6 +855,10 @@ repeat:
 
        setup_per_zone_wmarks();
        calculate_zone_inactive_ratio(zone);
+       if (!node_present_pages(node)) {
+               node_clear_state(node, N_HIGH_MEMORY);
+               kswapd_stop(node);
+       }
 
        vm_total_pages = nr_free_pagecache_pages();
        writeback_set_ratelimit();
index 4545d59442431e33d7d64e2f98c8f9d1a403780b..290fb5bf0440f7a5e0f05f4adeffd3decb785d00 100644 (file)
 #include <linux/seq_file.h>
 #include <linux/proc_fs.h>
 #include <linux/migrate.h>
+#include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/ctype.h>
+#include <linux/mm_inline.h>
 
 #include <asm/tlbflush.h>
 #include <asm/uaccess.h>
@@ -412,17 +414,11 @@ static int check_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
                if (!page)
                        continue;
                /*
-                * The check for PageReserved here is important to avoid
-                * handling zero pages and other pages that may have been
-                * marked special by the system.
-                *
-                * If the PageReserved would not be checked here then f.e.
-                * the location of the zero page could have an influence
-                * on MPOL_MF_STRICT, zero pages would be counted for
-                * the per node stats, and there would be useless attempts
-                * to put zero pages on the migration list.
+                * vm_normal_page() filters out zero pages, but there might
+                * still be PageReserved pages to skip, perhaps in a VDSO.
+                * And we cannot move PageKsm pages sensibly or safely yet.
                 */
-               if (PageReserved(page))
+               if (PageReserved(page) || PageKsm(page))
                        continue;
                nid = page_to_nid(page);
                if (node_isset(nid, *nodes) == !!(flags & MPOL_MF_INVERT))
@@ -809,6 +805,8 @@ static void migrate_page_add(struct page *page, struct list_head *pagelist,
        if ((flags & MPOL_MF_MOVE_ALL) || page_mapcount(page) == 1) {
                if (!isolate_lru_page(page)) {
                        list_add_tail(&page->lru, pagelist);
+                       inc_zone_page_state(page, NR_ISOLATED_ANON +
+                                           page_is_file_cache(page));
                }
        }
 }
@@ -836,7 +834,7 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
                        flags | MPOL_MF_DISCONTIG_OK, &pagelist);
 
        if (!list_empty(&pagelist))
-               err = migrate_pages(&pagelist, new_node_page, dest);
+               err = migrate_pages(&pagelist, new_node_page, dest, 0);
 
        return err;
 }
@@ -1053,7 +1051,7 @@ static long do_mbind(unsigned long start, unsigned long len,
 
                if (!list_empty(&pagelist))
                        nr_failed = migrate_pages(&pagelist, new_vma_page,
-                                               (unsigned long)vma);
+                                               (unsigned long)vma, 0);
 
                if (!err && nr_failed && (flags & MPOL_MF_STRICT))
                        err = -EIO;
@@ -1565,6 +1563,53 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
        }
        return zl;
 }
+
+/*
+ * init_nodemask_of_mempolicy
+ *
+ * If the current task's mempolicy is "default" [NULL], return 'false'
+ * to indicate default policy.  Otherwise, extract the policy nodemask
+ * for 'bind' or 'interleave' policy into the argument nodemask, or
+ * initialize the argument nodemask to contain the single node for
+ * 'preferred' or 'local' policy and return 'true' to indicate presence
+ * of non-default mempolicy.
+ *
+ * We don't bother with reference counting the mempolicy [mpol_get/put]
+ * because the current task is examining it's own mempolicy and a task's
+ * mempolicy is only ever changed by the task itself.
+ *
+ * N.B., it is the caller's responsibility to free a returned nodemask.
+ */
+bool init_nodemask_of_mempolicy(nodemask_t *mask)
+{
+       struct mempolicy *mempolicy;
+       int nid;
+
+       if (!(mask && current->mempolicy))
+               return false;
+
+       mempolicy = current->mempolicy;
+       switch (mempolicy->mode) {
+       case MPOL_PREFERRED:
+               if (mempolicy->flags & MPOL_F_LOCAL)
+                       nid = numa_node_id();
+               else
+                       nid = mempolicy->v.preferred_node;
+               init_nodemask_of_node(mask, nid);
+               break;
+
+       case MPOL_BIND:
+               /* Fall through */
+       case MPOL_INTERLEAVE:
+               *mask =  mempolicy->v.nodes;
+               break;
+
+       default:
+               BUG();
+       }
+
+       return true;
+}
 #endif
 
 /* Allocate a page in interleaved policy.
index 0bc640fd68fa64c38915d40fe20db545c1784799..efddbf0926b283ae5ef292e076ff9900842c4dd5 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mm_inline.h>
 #include <linux/nsproxy.h>
 #include <linux/pagevec.h>
+#include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/topology.h>
 #include <linux/cpu.h>
@@ -78,8 +79,8 @@ int putback_lru_pages(struct list_head *l)
 /*
  * Restore a potential migration pte to a working pte entry
  */
-static void remove_migration_pte(struct vm_area_struct *vma,
-               struct page *old, struct page *new)
+static int remove_migration_pte(struct page *new, struct vm_area_struct *vma,
+                                unsigned long addr, void *old)
 {
        struct mm_struct *mm = vma->vm_mm;
        swp_entry_t entry;
@@ -88,40 +89,37 @@ static void remove_migration_pte(struct vm_area_struct *vma,
        pmd_t *pmd;
        pte_t *ptep, pte;
        spinlock_t *ptl;
-       unsigned long addr = page_address_in_vma(new, vma);
-
-       if (addr == -EFAULT)
-               return;
 
        pgd = pgd_offset(mm, addr);
        if (!pgd_present(*pgd))
-                return;
+               goto out;
 
        pud = pud_offset(pgd, addr);
        if (!pud_present(*pud))
-                return;
+               goto out;
 
        pmd = pmd_offset(pud, addr);
        if (!pmd_present(*pmd))
-               return;
+               goto out;
 
        ptep = pte_offset_map(pmd, addr);
 
        if (!is_swap_pte(*ptep)) {
                pte_unmap(ptep);
-               return;
+               goto out;
        }
 
        ptl = pte_lockptr(mm, pmd);
        spin_lock(ptl);
        pte = *ptep;
        if (!is_swap_pte(pte))
-               goto out;
+               goto unlock;
 
        entry = pte_to_swp_entry(pte);
 
-       if (!is_migration_entry(entry) || migration_entry_to_page(entry) != old)
-               goto out;
+       if (!is_migration_entry(entry) ||
+           migration_entry_to_page(entry) != old)
+               goto unlock;
 
        get_page(new);
        pte = pte_mkold(mk_pte(new, vma->vm_page_prot));
@@ -137,58 +135,10 @@ static void remove_migration_pte(struct vm_area_struct *vma,
 
        /* No need to invalidate - it was non-present before */
        update_mmu_cache(vma, addr, pte);
-
-out:
+unlock:
        pte_unmap_unlock(ptep, ptl);
-}
-
-/*
- * Note that remove_file_migration_ptes will only work on regular mappings,
- * Nonlinear mappings do not use migration entries.
- */
-static void remove_file_migration_ptes(struct page *old, struct page *new)
-{
-       struct vm_area_struct *vma;
-       struct address_space *mapping = new->mapping;
-       struct prio_tree_iter iter;
-       pgoff_t pgoff = new->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
-
-       if (!mapping)
-               return;
-
-       spin_lock(&mapping->i_mmap_lock);
-
-       vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff)
-               remove_migration_pte(vma, old, new);
-
-       spin_unlock(&mapping->i_mmap_lock);
-}
-
-/*
- * Must hold mmap_sem lock on at least one of the vmas containing
- * the page so that the anon_vma cannot vanish.
- */
-static void remove_anon_migration_ptes(struct page *old, struct page *new)
-{
-       struct anon_vma *anon_vma;
-       struct vm_area_struct *vma;
-       unsigned long mapping;
-
-       mapping = (unsigned long)new->mapping;
-
-       if (!mapping || (mapping & PAGE_MAPPING_ANON) == 0)
-               return;
-
-       /*
-        * We hold the mmap_sem lock. So no need to call page_lock_anon_vma.
-        */
-       anon_vma = (struct anon_vma *) (mapping - PAGE_MAPPING_ANON);
-       spin_lock(&anon_vma->lock);
-
-       list_for_each_entry(vma, &anon_vma->head, anon_vma_node)
-               remove_migration_pte(vma, old, new);
-
-       spin_unlock(&anon_vma->lock);
+out:
+       return SWAP_AGAIN;
 }
 
 /*
@@ -197,10 +147,7 @@ static void remove_anon_migration_ptes(struct page *old, struct page *new)
  */
 static void remove_migration_ptes(struct page *old, struct page *new)
 {
-       if (PageAnon(new))
-               remove_anon_migration_ptes(old, new);
-       else
-               remove_file_migration_ptes(old, new);
+       rmap_walk(new, remove_migration_pte, old);
 }
 
 /*
@@ -341,8 +288,8 @@ static void migrate_page_copy(struct page *newpage, struct page *page)
        if (TestClearPageActive(page)) {
                VM_BUG_ON(PageUnevictable(page));
                SetPageActive(newpage);
-       } else
-               unevictable_migrate_page(newpage, page);
+       } else if (TestClearPageUnevictable(page))
+               SetPageUnevictable(newpage);
        if (PageChecked(page))
                SetPageChecked(newpage);
        if (PageMappedToDisk(page))
@@ -361,6 +308,7 @@ static void migrate_page_copy(struct page *newpage, struct page *page)
        }
 
        mlock_migrate_page(newpage, page);
+       ksm_migrate_page(newpage, page);
 
        ClearPageSwapCache(page);
        ClearPagePrivate(page);
@@ -580,9 +528,9 @@ static int move_to_new_page(struct page *newpage, struct page *page)
        else
                rc = fallback_migrate_page(mapping, newpage, page);
 
-       if (!rc) {
+       if (!rc)
                remove_migration_ptes(page, newpage);
-       else
+       else
                newpage->mapping = NULL;
 
        unlock_page(newpage);
@@ -595,7 +543,7 @@ static int move_to_new_page(struct page *newpage, struct page *page)
  * to the newly allocated page in newpage.
  */
 static int unmap_and_move(new_page_t get_new_page, unsigned long private,
-                       struct page *page, int force)
+                       struct page *page, int force, int offlining)
 {
        int rc = 0;
        int *result = NULL;
@@ -621,6 +569,20 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
                lock_page(page);
        }
 
+       /*
+        * Only memory hotplug's offline_pages() caller has locked out KSM,
+        * and can safely migrate a KSM page.  The other cases have skipped
+        * PageKsm along with PageReserved - but it is only now when we have
+        * the page lock that we can be certain it will not go KSM beneath us
+        * (KSM will not upgrade a page from PageAnon to PageKsm when it sees
+        * its pagecount raised, but only here do we take the page lock which
+        * serializes that).
+        */
+       if (PageKsm(page) && !offlining) {
+               rc = -EBUSY;
+               goto unlock;
+       }
+
        /* charge against new page */
        charge = mem_cgroup_prepare_migration(page, &mem);
        if (charge == -ENOMEM) {
@@ -737,7 +699,7 @@ move_newpage:
  * Return: Number of pages not migrated or error code.
  */
 int migrate_pages(struct list_head *from,
-               new_page_t get_new_page, unsigned long private)
+               new_page_t get_new_page, unsigned long private, int offlining)
 {
        int retry = 1;
        int nr_failed = 0;
@@ -746,13 +708,6 @@ int migrate_pages(struct list_head *from,
        struct page *page2;
        int swapwrite = current->flags & PF_SWAPWRITE;
        int rc;
-       unsigned long flags;
-
-       local_irq_save(flags);
-       list_for_each_entry(page, from, lru)
-               __inc_zone_page_state(page, NR_ISOLATED_ANON +
-                               page_is_file_cache(page));
-       local_irq_restore(flags);
 
        if (!swapwrite)
                current->flags |= PF_SWAPWRITE;
@@ -764,7 +719,7 @@ int migrate_pages(struct list_head *from,
                        cond_resched();
 
                        rc = unmap_and_move(get_new_page, private,
-                                               page, pass > 2);
+                                               page, pass > 2, offlining);
 
                        switch(rc) {
                        case -ENOMEM:
@@ -860,7 +815,8 @@ static int do_move_page_to_node_array(struct mm_struct *mm,
                if (!page)
                        goto set_status;
 
-               if (PageReserved(page))         /* Check for zero page */
+               /* Use PageReserved to check for zero page */
+               if (PageReserved(page) || PageKsm(page))
                        goto put_and_set;
 
                pp->page = page;
@@ -878,8 +834,11 @@ static int do_move_page_to_node_array(struct mm_struct *mm,
                        goto put_and_set;
 
                err = isolate_lru_page(page);
-               if (!err)
+               if (!err) {
                        list_add_tail(&page->lru, &pagelist);
+                       inc_zone_page_state(page, NR_ISOLATED_ANON +
+                                           page_is_file_cache(page));
+               }
 put_and_set:
                /*
                 * Either remove the duplicate refcount from
@@ -894,7 +853,7 @@ set_status:
        err = 0;
        if (!list_empty(&pagelist))
                err = migrate_pages(&pagelist, new_page_node,
-                               (unsigned long)pm);
+                               (unsigned long)pm, 0);
 
        up_read(&mm->mmap_sem);
        return err;
@@ -1015,7 +974,7 @@ static void do_pages_stat_array(struct mm_struct *mm, unsigned long nr_pages,
 
                err = -ENOENT;
                /* Use PageReserved to check for zero page */
-               if (!page || PageReserved(page))
+               if (!page || PageReserved(page) || PageKsm(page))
                        goto set_status;
 
                err = page_to_nid(page);
index 8cb508f84ea49ca03a7005574208ab3ec4a4e567..7a3436ef39eba1ace01efca158e93599aaea5bb7 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/syscalls.h>
 #include <linux/swap.h>
 #include <linux/swapops.h>
+#include <linux/hugetlb.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -72,6 +73,42 @@ static long do_mincore(unsigned long addr, unsigned char *vec, unsigned long pag
        if (!vma || addr < vma->vm_start)
                return -ENOMEM;
 
+#ifdef CONFIG_HUGETLB_PAGE
+       if (is_vm_hugetlb_page(vma)) {
+               struct hstate *h;
+               unsigned long nr_huge;
+               unsigned char present;
+
+               i = 0;
+               nr = min(pages, (vma->vm_end - addr) >> PAGE_SHIFT);
+               h = hstate_vma(vma);
+               nr_huge = ((addr + pages * PAGE_SIZE - 1) >> huge_page_shift(h))
+                         - (addr >> huge_page_shift(h)) + 1;
+               nr_huge = min(nr_huge,
+                             (vma->vm_end - addr) >> huge_page_shift(h));
+               while (1) {
+                       /* hugepage always in RAM for now,
+                        * but generally it needs to be check */
+                       ptep = huge_pte_offset(current->mm,
+                                              addr & huge_page_mask(h));
+                       present = !!(ptep &&
+                                    !huge_pte_none(huge_ptep_get(ptep)));
+                       while (1) {
+                               vec[i++] = present;
+                               addr += PAGE_SIZE;
+                               /* reach buffer limit */
+                               if (i == nr)
+                                       return nr;
+                               /* check hugepage border */
+                               if (!((addr & ~huge_page_mask(h))
+                                     >> PAGE_SHIFT))
+                                       break;
+                       }
+               }
+               return nr;
+       }
+#endif
+
        /*
         * Calculate how many pages there are left in the last level of the
         * PTE array for our address.
index bd6f0e466f6c4f1c51b4bbbe1e605140bb3e6493..2b8335a8940052874a18a9b3657c830c61e13284 100644 (file)
@@ -88,25 +88,22 @@ void mlock_vma_page(struct page *page)
        }
 }
 
-/*
- * called from munlock()/munmap() path with page supposedly on the LRU.
+/**
+ * munlock_vma_page - munlock a vma page
+ * @page - page to be unlocked
  *
- * Note:  unlike mlock_vma_page(), we can't just clear the PageMlocked
- * [in try_to_munlock()] and then attempt to isolate the page.  We must
- * isolate the page to keep others from messing with its unevictable
- * and mlocked state while trying to munlock.  However, we pre-clear the
- * mlocked state anyway as we might lose the isolation race and we might
- * not get another chance to clear PageMlocked.  If we successfully
- * isolate the page and try_to_munlock() detects other VM_LOCKED vmas
- * mapping the page, it will restore the PageMlocked state, unless the page
- * is mapped in a non-linear vma.  So, we go ahead and SetPageMlocked(),
- * perhaps redundantly.
- * If we lose the isolation race, and the page is mapped by other VM_LOCKED
- * vmas, we'll detect this in vmscan--via try_to_munlock() or try_to_unmap()
- * either of which will restore the PageMlocked state by calling
- * mlock_vma_page() above, if it can grab the vma's mmap sem.
+ * called from munlock()/munmap() path with page supposedly on the LRU.
+ * When we munlock a page, because the vma where we found the page is being
+ * munlock()ed or munmap()ed, we want to check whether other vmas hold the
+ * page locked so that we can leave it on the unevictable lru list and not
+ * bother vmscan with it.  However, to walk the page's rmap list in
+ * try_to_munlock() we must isolate the page from the LRU.  If some other
+ * task has removed the page from the LRU, we won't be able to do that.
+ * So we clear the PageMlocked as we might not get another chance.  If we
+ * can't isolate the page, we leave it for putback_lru_page() and vmscan
+ * [page_referenced()/try_to_unmap()] to deal with.
  */
-static void munlock_vma_page(struct page *page)
+void munlock_vma_page(struct page *page)
 {
        BUG_ON(!PageLocked(page));
 
@@ -117,18 +114,18 @@ static void munlock_vma_page(struct page *page)
                        /*
                         * did try_to_unlock() succeed or punt?
                         */
-                       if (ret == SWAP_SUCCESS || ret == SWAP_AGAIN)
+                       if (ret != SWAP_MLOCK)
                                count_vm_event(UNEVICTABLE_PGMUNLOCKED);
 
                        putback_lru_page(page);
                } else {
                        /*
-                        * We lost the race.  let try_to_unmap() deal
-                        * with it.  At least we get the page state and
-                        * mlock stats right.  However, page is still on
-                        * the noreclaim list.  We'll fix that up when
-                        * the page is eventually freed or we scan the
-                        * noreclaim list.
+                        * Some other task has removed the page from the LRU.
+                        * putback_lru_page() will take care of removing the
+                        * page from the unevictable list, if necessary.
+                        * vmscan [page_referenced()] will move the page back
+                        * to the unevictable list if some other vma has it
+                        * mlocked.
                         */
                        if (PageUnevictable(page))
                                count_vm_event(UNEVICTABLE_PGSTRANDED);
index ed70a68e882af069988cfcbe5c54a9a0af88d9fa..d9c77b2dbe9d6489cdf5e40e5f5ae0dfc2be2a5a 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1198,8 +1198,20 @@ munmap_back:
                        goto free_vma;
        }
 
-       if (vma_wants_writenotify(vma))
+       if (vma_wants_writenotify(vma)) {
+               pgprot_t pprot = vma->vm_page_prot;
+
+               /* Can vma->vm_page_prot have changed??
+                *
+                * Answer: Yes, drivers may have changed it in their
+                *         f_op->mmap method.
+                *
+                * Ensures that vmas marked as uncached stay that way.
+                */
                vma->vm_page_prot = vm_get_page_prot(vm_flags & ~VM_SHARED);
+               if (pgprot_val(pprot) == pgprot_val(pgprot_noncached(pprot)))
+                       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       }
 
        vma_link(mm, vma, prev, rb_link, rb_parent);
        file = vma->vm_file;
@@ -1811,10 +1823,10 @@ detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma,
 }
 
 /*
- * Split a vma into two pieces at address 'addr', a new vma is allocated
- * either for the first part or the tail.
+ * __split_vma() bypasses sysctl_max_map_count checking.  We use this on the
+ * munmap path where it doesn't make sense to fail.
  */
-int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
+static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
              unsigned long addr, int new_below)
 {
        struct mempolicy *pol;
@@ -1824,9 +1836,6 @@ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
                                        ~(huge_page_mask(hstate_vma(vma)))))
                return -EINVAL;
 
-       if (mm->map_count >= sysctl_max_map_count)
-               return -ENOMEM;
-
        new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
        if (!new)
                return -ENOMEM;
@@ -1866,6 +1875,19 @@ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
        return 0;
 }
 
+/*
+ * Split a vma into two pieces at address 'addr', a new vma is allocated
+ * either for the first part or the tail.
+ */
+int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
+             unsigned long addr, int new_below)
+{
+       if (mm->map_count >= sysctl_max_map_count)
+               return -ENOMEM;
+
+       return __split_vma(mm, vma, addr, new_below);
+}
+
 /* Munmap is split into 2 main parts -- this part which finds
  * what needs doing, and the areas themselves, which do the
  * work.  This now handles partial unmappings.
@@ -1901,7 +1923,17 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
         * places tmp vma above, and higher split_vma places tmp vma below.
         */
        if (start > vma->vm_start) {
-               int error = split_vma(mm, vma, start, 0);
+               int error;
+
+               /*
+                * Make sure that map_count on return from munmap() will
+                * not exceed its limit; but let map_count go just above
+                * its limit temporarily, to help free resources as expected.
+                */
+               if (end < vma->vm_end && mm->map_count >= sysctl_max_map_count)
+                       return -ENOMEM;
+
+               error = __split_vma(mm, vma, start, 0);
                if (error)
                        return error;
                prev = vma;
@@ -1910,7 +1942,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
        /* Does it split the last one? */
        last = find_vma(mm, end);
        if (last && end > last->vm_start) {
-               int error = split_vma(mm, last, end, 1);
+               int error = __split_vma(mm, last, end, 1);
                if (error)
                        return error;
        }
index 9876fa0c3ad30e75d842f965ac4417e66be6f07a..8687973462bbea8becddfe0941eb94fab60ce304 100644 (file)
@@ -1143,9 +1143,6 @@ static int do_mmap_private(struct vm_area_struct *vma,
                if (ret < rlen)
                        memset(base + ret, 0, rlen - ret);
 
-       } else {
-               /* if it's an anonymous mapping, then just clear it */
-               memset(base, 0, rlen);
        }
 
        return 0;
@@ -1343,6 +1340,11 @@ unsigned long do_mmap_pgoff(struct file *file,
                goto error_just_free;
        add_nommu_region(region);
 
+       /* clear anonymous mappings that don't ask for uninitialized data */
+       if (!vma->vm_file && !(flags & MAP_UNINITIALIZED))
+               memset((void *)region->vm_start, 0,
+                      region->vm_end - region->vm_start);
+
        /* okay... we have a mapping; now we have to register it */
        result = vma->vm_start;
 
index ea2147dabba60febfb381b8fb788c9a0cbf9ed30..492c98624fc19bfd4b6fbb012b9c5661fc7fdb91 100644 (file)
@@ -337,6 +337,21 @@ static void dump_tasks(const struct mem_cgroup *mem)
        } while_each_thread(g, p);
 }
 
+static void dump_header(gfp_t gfp_mask, int order, struct mem_cgroup *mem)
+{
+       pr_warning("%s invoked oom-killer: gfp_mask=0x%x, order=%d, "
+               "oom_adj=%d\n",
+               current->comm, gfp_mask, order, current->signal->oom_adj);
+       task_lock(current);
+       cpuset_print_task_mems_allowed(current);
+       task_unlock(current);
+       dump_stack();
+       mem_cgroup_print_oom_info(mem, current);
+       show_mem();
+       if (sysctl_oom_dump_tasks)
+               dump_tasks(mem);
+}
+
 /*
  * Send SIGKILL to the selected  process irrespective of  CAP_SYS_RAW_IO
  * flag though it's unlikely that  we select a process with CAP_SYS_RAW_IO
@@ -395,20 +410,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
 {
        struct task_struct *c;
 
-       if (printk_ratelimit()) {
-               printk(KERN_WARNING "%s invoked oom-killer: "
-                       "gfp_mask=0x%x, order=%d, oom_adj=%d\n",
-                       current->comm, gfp_mask, order,
-                       current->signal->oom_adj);
-               task_lock(current);
-               cpuset_print_task_mems_allowed(current);
-               task_unlock(current);
-               dump_stack();
-               mem_cgroup_print_oom_info(mem, current);
-               show_mem();
-               if (sysctl_oom_dump_tasks)
-                       dump_tasks(mem);
-       }
+       if (printk_ratelimit())
+               dump_header(gfp_mask, order, mem);
 
        /*
         * If the task is already exiting, don't alarm the sysadmin or kill
@@ -544,6 +547,7 @@ retry:
        /* Found nothing?!?! Either we hang forever, or we panic. */
        if (!p) {
                read_unlock(&tasklist_lock);
+               dump_header(gfp_mask, order, NULL);
                panic("Out of memory and no killable processes...\n");
        }
 
@@ -609,8 +613,10 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order)
                /* Got some memory back in the last second. */
                return;
 
-       if (sysctl_panic_on_oom == 2)
+       if (sysctl_panic_on_oom == 2) {
+               dump_header(gfp_mask, order, NULL);
                panic("out of memory. Compulsory panic_on_oom is selected.\n");
+       }
 
        /*
         * Check if there were limitations on the allocation (only relevant for
@@ -626,8 +632,10 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order)
                break;
 
        case CONSTRAINT_NONE:
-               if (sysctl_panic_on_oom)
+               if (sysctl_panic_on_oom) {
+                       dump_header(gfp_mask, order, NULL);
                        panic("out of memory. panic_on_oom is selected\n");
+               }
                /* Fall-through */
        case CONSTRAINT_CPUSET:
                __out_of_memory(gfp_mask, order);
index 2bc2ac63f41ef8329774a5e444d0be8181ea83bd..59d2e88fb47ceb80a5549c5d69d9c1788ecefe41 100644 (file)
@@ -486,7 +486,6 @@ static inline void __free_one_page(struct page *page,
        zone->free_area[order].nr_free++;
 }
 
-#ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT
 /*
  * free_page_mlock() -- clean up attempts to free and mlocked() page.
  * Page should not be on lru, so no need to fix that up.
@@ -497,9 +496,6 @@ static inline void free_page_mlock(struct page *page)
        __dec_zone_page_state(page, NR_MLOCK);
        __count_vm_event(UNEVICTABLE_MLOCKFREED);
 }
-#else
-static void free_page_mlock(struct page *page) { }
-#endif
 
 static inline int free_pages_check(struct page *page)
 {
index c6f3e5071de3bb57c737cef88ab32c3cbdac4aee..a19af956ee1bb6f55d87df3a3905a486820a49a7 100644 (file)
 #include <linux/writeback.h>
 #include <asm/pgtable.h>
 
-static struct bio *get_swap_bio(gfp_t gfp_flags, pgoff_t index,
+static struct bio *get_swap_bio(gfp_t gfp_flags,
                                struct page *page, bio_end_io_t end_io)
 {
        struct bio *bio;
 
        bio = bio_alloc(gfp_flags, 1);
        if (bio) {
-               struct swap_info_struct *sis;
-               swp_entry_t entry = { .val = index, };
-
-               sis = get_swap_info_struct(swp_type(entry));
-               bio->bi_sector = map_swap_page(sis, swp_offset(entry)) *
-                                       (PAGE_SIZE >> 9);
-               bio->bi_bdev = sis->bdev;
+               bio->bi_sector = map_swap_page(page, &bio->bi_bdev);
+               bio->bi_sector <<= PAGE_SHIFT - 9;
                bio->bi_io_vec[0].bv_page = page;
                bio->bi_io_vec[0].bv_len = PAGE_SIZE;
                bio->bi_io_vec[0].bv_offset = 0;
@@ -102,8 +97,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
                unlock_page(page);
                goto out;
        }
-       bio = get_swap_bio(GFP_NOIO, page_private(page), page,
-                               end_swap_bio_write);
+       bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write);
        if (bio == NULL) {
                set_page_dirty(page);
                unlock_page(page);
@@ -127,8 +121,7 @@ int swap_readpage(struct page *page)
 
        VM_BUG_ON(!PageLocked(page));
        VM_BUG_ON(PageUptodate(page));
-       bio = get_swap_bio(GFP_KERNEL, page_private(page), page,
-                               end_swap_bio_read);
+       bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);
        if (bio == NULL) {
                unlock_page(page);
                ret = -ENOMEM;
index d5878bed78412542b82c03022756db62c9d73d63..7b47a57b66462dae3d9ae42929a1bba543d2c290 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/mm.h>
 #include <linux/highmem.h>
 #include <linux/sched.h>
+#include <linux/hugetlb.h>
 
 static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
                          struct mm_walk *walk)
@@ -107,6 +108,7 @@ int walk_page_range(unsigned long addr, unsigned long end,
        pgd_t *pgd;
        unsigned long next;
        int err = 0;
+       struct vm_area_struct *vma;
 
        if (addr >= end)
                return err;
@@ -117,11 +119,38 @@ int walk_page_range(unsigned long addr, unsigned long end,
        pgd = pgd_offset(walk->mm, addr);
        do {
                next = pgd_addr_end(addr, end);
+
+               /*
+                * handle hugetlb vma individually because pagetable walk for
+                * the hugetlb page is dependent on the architecture and
+                * we can't handled it in the same manner as non-huge pages.
+                */
+               vma = find_vma(walk->mm, addr);
+#ifdef CONFIG_HUGETLB_PAGE
+               if (vma && is_vm_hugetlb_page(vma)) {
+                       pte_t *pte;
+                       struct hstate *hs;
+
+                       if (vma->vm_end < next)
+                               next = vma->vm_end;
+                       hs = hstate_vma(vma);
+                       pte = huge_pte_offset(walk->mm,
+                                             addr & huge_page_mask(hs));
+                       if (pte && !huge_pte_none(huge_ptep_get(pte))
+                           && walk->hugetlb_entry)
+                               err = walk->hugetlb_entry(pte, addr,
+                                                         next, walk);
+                       if (err)
+                               break;
+                       continue;
+               }
+#endif
                if (pgd_none_or_clear_bad(pgd)) {
                        if (walk->pte_hole)
                                err = walk->pte_hole(addr, next, walk);
                        if (err)
                                break;
+                       pgd++;
                        continue;
                }
                if (walk->pgd_entry)
@@ -131,7 +160,8 @@ int walk_page_range(unsigned long addr, unsigned long end,
                        err = walk_pud_range(pgd, addr, next, walk);
                if (err)
                        break;
-       } while (pgd++, addr = next, addr != end);
+               pgd++;
+       } while (addr = next, addr != end);
 
        return err;
 }
index dd43373a483fa764ce35a3bdc541ea4148653af4..98135dbd25ba4c5ab29889dc6a9d5a23b7f0ff4f 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -49,6 +49,7 @@
 #include <linux/swapops.h>
 #include <linux/slab.h>
 #include <linux/init.h>
+#include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/rcupdate.h>
 #include <linux/module.h>
@@ -67,7 +68,7 @@ static inline struct anon_vma *anon_vma_alloc(void)
        return kmem_cache_alloc(anon_vma_cachep, GFP_KERNEL);
 }
 
-static inline void anon_vma_free(struct anon_vma *anon_vma)
+void anon_vma_free(struct anon_vma *anon_vma)
 {
        kmem_cache_free(anon_vma_cachep, anon_vma);
 }
@@ -171,7 +172,7 @@ void anon_vma_unlink(struct vm_area_struct *vma)
        list_del(&vma->anon_vma_node);
 
        /* We must garbage collect the anon_vma if it's empty */
-       empty = list_empty(&anon_vma->head);
+       empty = list_empty(&anon_vma->head) && !ksm_refcount(anon_vma);
        spin_unlock(&anon_vma->lock);
 
        if (empty)
@@ -183,6 +184,7 @@ static void anon_vma_ctor(void *data)
        struct anon_vma *anon_vma = data;
 
        spin_lock_init(&anon_vma->lock);
+       ksm_refcount_init(anon_vma);
        INIT_LIST_HEAD(&anon_vma->head);
 }
 
@@ -202,8 +204,8 @@ struct anon_vma *page_lock_anon_vma(struct page *page)
        unsigned long anon_mapping;
 
        rcu_read_lock();
-       anon_mapping = (unsigned long) page->mapping;
-       if (!(anon_mapping & PAGE_MAPPING_ANON))
+       anon_mapping = (unsigned long) ACCESS_ONCE(page->mapping);
+       if ((anon_mapping & PAGE_MAPPING_FLAGS) != PAGE_MAPPING_ANON)
                goto out;
        if (!page_mapped(page))
                goto out;
@@ -248,8 +250,7 @@ vma_address(struct page *page, struct vm_area_struct *vma)
 unsigned long page_address_in_vma(struct page *page, struct vm_area_struct *vma)
 {
        if (PageAnon(page)) {
-               if ((void *)vma->anon_vma !=
-                   (void *)page->mapping - PAGE_MAPPING_ANON)
+               if (vma->anon_vma != page_anon_vma(page))
                        return -EFAULT;
        } else if (page->mapping && !(vma->vm_flags & VM_NONLINEAR)) {
                if (!vma->vm_file ||
@@ -337,21 +338,15 @@ int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma)
  * Subfunctions of page_referenced: page_referenced_one called
  * repeatedly from either page_referenced_anon or page_referenced_file.
  */
-static int page_referenced_one(struct page *page,
-                              struct vm_area_struct *vma,
-                              unsigned int *mapcount,
-                              unsigned long *vm_flags)
+int page_referenced_one(struct page *page, struct vm_area_struct *vma,
+                       unsigned long address, unsigned int *mapcount,
+                       unsigned long *vm_flags)
 {
        struct mm_struct *mm = vma->vm_mm;
-       unsigned long address;
        pte_t *pte;
        spinlock_t *ptl;
        int referenced = 0;
 
-       address = vma_address(page, vma);
-       if (address == -EFAULT)
-               goto out;
-
        pte = page_check_address(page, mm, address, &ptl, 0);
        if (!pte)
                goto out;
@@ -388,9 +383,10 @@ static int page_referenced_one(struct page *page,
 out_unmap:
        (*mapcount)--;
        pte_unmap_unlock(pte, ptl);
-out:
+
        if (referenced)
                *vm_flags |= vma->vm_flags;
+out:
        return referenced;
 }
 
@@ -409,6 +405,9 @@ static int page_referenced_anon(struct page *page,
 
        mapcount = page_mapcount(page);
        list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+               unsigned long address = vma_address(page, vma);
+               if (address == -EFAULT)
+                       continue;
                /*
                 * If we are reclaiming on behalf of a cgroup, skip
                 * counting on behalf of references from different
@@ -416,7 +415,7 @@ static int page_referenced_anon(struct page *page,
                 */
                if (mem_cont && !mm_match_cgroup(vma->vm_mm, mem_cont))
                        continue;
-               referenced += page_referenced_one(page, vma,
+               referenced += page_referenced_one(page, vma, address,
                                                  &mapcount, vm_flags);
                if (!mapcount)
                        break;
@@ -474,6 +473,9 @@ static int page_referenced_file(struct page *page,
        mapcount = page_mapcount(page);
 
        vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
+               unsigned long address = vma_address(page, vma);
+               if (address == -EFAULT)
+                       continue;
                /*
                 * If we are reclaiming on behalf of a cgroup, skip
                 * counting on behalf of references from different
@@ -481,7 +483,7 @@ static int page_referenced_file(struct page *page,
                 */
                if (mem_cont && !mm_match_cgroup(vma->vm_mm, mem_cont))
                        continue;
-               referenced += page_referenced_one(page, vma,
+               referenced += page_referenced_one(page, vma, address,
                                                  &mapcount, vm_flags);
                if (!mapcount)
                        break;
@@ -507,46 +509,47 @@ int page_referenced(struct page *page,
                    unsigned long *vm_flags)
 {
        int referenced = 0;
+       int we_locked = 0;
 
        if (TestClearPageReferenced(page))
                referenced++;
 
        *vm_flags = 0;
-       if (page_mapped(page) && page->mapping) {
-               if (PageAnon(page))
+       if (page_mapped(page) && page_rmapping(page)) {
+               if (!is_locked && (!PageAnon(page) || PageKsm(page))) {
+                       we_locked = trylock_page(page);
+                       if (!we_locked) {
+                               referenced++;
+                               goto out;
+                       }
+               }
+               if (unlikely(PageKsm(page)))
+                       referenced += page_referenced_ksm(page, mem_cont,
+                                                               vm_flags);
+               else if (PageAnon(page))
                        referenced += page_referenced_anon(page, mem_cont,
                                                                vm_flags);
-               else if (is_locked)
+               else if (page->mapping)
                        referenced += page_referenced_file(page, mem_cont,
                                                                vm_flags);
-               else if (!trylock_page(page))
-                       referenced++;
-               else {
-                       if (page->mapping)
-                               referenced += page_referenced_file(page,
-                                                       mem_cont, vm_flags);
+               if (we_locked)
                        unlock_page(page);
-               }
        }
-
+out:
        if (page_test_and_clear_young(page))
                referenced++;
 
        return referenced;
 }
 
-static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
+static int page_mkclean_one(struct page *page, struct vm_area_struct *vma,
+                           unsigned long address)
 {
        struct mm_struct *mm = vma->vm_mm;
-       unsigned long address;
        pte_t *pte;
        spinlock_t *ptl;
        int ret = 0;
 
-       address = vma_address(page, vma);
-       if (address == -EFAULT)
-               goto out;
-
        pte = page_check_address(page, mm, address, &ptl, 1);
        if (!pte)
                goto out;
@@ -578,8 +581,12 @@ static int page_mkclean_file(struct address_space *mapping, struct page *page)
 
        spin_lock(&mapping->i_mmap_lock);
        vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
-               if (vma->vm_flags & VM_SHARED)
-                       ret += page_mkclean_one(page, vma);
+               if (vma->vm_flags & VM_SHARED) {
+                       unsigned long address = vma_address(page, vma);
+                       if (address == -EFAULT)
+                               continue;
+                       ret += page_mkclean_one(page, vma, address);
+               }
        }
        spin_unlock(&mapping->i_mmap_lock);
        return ret;
@@ -620,14 +627,7 @@ static void __page_set_anon_rmap(struct page *page,
        BUG_ON(!anon_vma);
        anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
        page->mapping = (struct address_space *) anon_vma;
-
        page->index = linear_page_index(vma, address);
-
-       /*
-        * nr_mapped state can be updated without turning off
-        * interrupts because it is not modified via interrupt.
-        */
-       __inc_zone_page_state(page, NR_ANON_PAGES);
 }
 
 /**
@@ -665,14 +665,23 @@ static void __page_check_anon_rmap(struct page *page,
  * @vma:       the vm area in which the mapping is added
  * @address:   the user virtual address mapped
  *
- * The caller needs to hold the pte lock and the page must be locked.
+ * The caller needs to hold the pte lock, and the page must be locked in
+ * the anon_vma case: to serialize mapping,index checking after setting,
+ * and to ensure that PageAnon is not being upgraded racily to PageKsm
+ * (but PageKsm is never downgraded to PageAnon).
  */
 void page_add_anon_rmap(struct page *page,
        struct vm_area_struct *vma, unsigned long address)
 {
+       int first = atomic_inc_and_test(&page->_mapcount);
+       if (first)
+               __inc_zone_page_state(page, NR_ANON_PAGES);
+       if (unlikely(PageKsm(page)))
+               return;
+
        VM_BUG_ON(!PageLocked(page));
        VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end);
-       if (atomic_inc_and_test(&page->_mapcount))
+       if (first)
                __page_set_anon_rmap(page, vma, address);
        else
                __page_check_anon_rmap(page, vma, address);
@@ -694,6 +703,7 @@ void page_add_new_anon_rmap(struct page *page,
        VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end);
        SetPageSwapBacked(page);
        atomic_set(&page->_mapcount, 0); /* increment count (starts at -1) */
+       __inc_zone_page_state(page, NR_ANON_PAGES);
        __page_set_anon_rmap(page, vma, address);
        if (page_evictable(page, vma))
                lru_cache_add_lru(page, LRU_ACTIVE_ANON);
@@ -760,20 +770,15 @@ void page_remove_rmap(struct page *page)
  * Subfunctions of try_to_unmap: try_to_unmap_one called
  * repeatedly from either try_to_unmap_anon or try_to_unmap_file.
  */
-static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
-                               enum ttu_flags flags)
+int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
+                    unsigned long address, enum ttu_flags flags)
 {
        struct mm_struct *mm = vma->vm_mm;
-       unsigned long address;
        pte_t *pte;
        pte_t pteval;
        spinlock_t *ptl;
        int ret = SWAP_AGAIN;
 
-       address = vma_address(page, vma);
-       if (address == -EFAULT)
-               goto out;
-
        pte = page_check_address(page, mm, address, &ptl, 0);
        if (!pte)
                goto out;
@@ -784,10 +789,11 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
         * skipped over this mm) then we should reactivate it.
         */
        if (!(flags & TTU_IGNORE_MLOCK)) {
-               if (vma->vm_flags & VM_LOCKED) {
-                       ret = SWAP_MLOCK;
+               if (vma->vm_flags & VM_LOCKED)
+                       goto out_mlock;
+
+               if (TTU_ACTION(flags) == TTU_MUNLOCK)
                        goto out_unmap;
-               }
        }
        if (!(flags & TTU_IGNORE_ACCESS)) {
                if (ptep_clear_flush_young_notify(vma, address, pte)) {
@@ -822,7 +828,11 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                         * Store the swap location in the pte.
                         * See handle_pte_fault() ...
                         */
-                       swap_duplicate(entry);
+                       if (swap_duplicate(entry) < 0) {
+                               set_pte_at(mm, address, pte, pteval);
+                               ret = SWAP_FAIL;
+                               goto out_unmap;
+                       }
                        if (list_empty(&mm->mmlist)) {
                                spin_lock(&mmlist_lock);
                                if (list_empty(&mm->mmlist))
@@ -849,7 +859,6 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
        } else
                dec_mm_counter(mm, file_rss);
 
-
        page_remove_rmap(page);
        page_cache_release(page);
 
@@ -857,6 +866,27 @@ out_unmap:
        pte_unmap_unlock(pte, ptl);
 out:
        return ret;
+
+out_mlock:
+       pte_unmap_unlock(pte, ptl);
+
+
+       /*
+        * We need mmap_sem locking, Otherwise VM_LOCKED check makes
+        * unstable result and race. Plus, We can't wait here because
+        * we now hold anon_vma->lock or mapping->i_mmap_lock.
+        * if trylock failed, the page remain in evictable lru and later
+        * vmscan could retry to move the page to unevictable lru if the
+        * page is actually mlocked.
+        */
+       if (down_read_trylock(&vma->vm_mm->mmap_sem)) {
+               if (vma->vm_flags & VM_LOCKED) {
+                       mlock_vma_page(page);
+                       ret = SWAP_MLOCK;
+               }
+               up_read(&vma->vm_mm->mmap_sem);
+       }
+       return ret;
 }
 
 /*
@@ -922,11 +952,10 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount,
                return ret;
 
        /*
-        * MLOCK_PAGES => feature is configured.
-        * if we can acquire the mmap_sem for read, and vma is VM_LOCKED,
+        * If we can acquire the mmap_sem for read, and vma is VM_LOCKED,
         * keep the sem while scanning the cluster for mlocking pages.
         */
-       if (MLOCK_PAGES && down_read_trylock(&vma->vm_mm->mmap_sem)) {
+       if (down_read_trylock(&vma->vm_mm->mmap_sem)) {
                locked_vma = (vma->vm_flags & VM_LOCKED);
                if (!locked_vma)
                        up_read(&vma->vm_mm->mmap_sem); /* don't need it */
@@ -976,29 +1005,11 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount,
        return ret;
 }
 
-/*
- * common handling for pages mapped in VM_LOCKED vmas
- */
-static int try_to_mlock_page(struct page *page, struct vm_area_struct *vma)
-{
-       int mlocked = 0;
-
-       if (down_read_trylock(&vma->vm_mm->mmap_sem)) {
-               if (vma->vm_flags & VM_LOCKED) {
-                       mlock_vma_page(page);
-                       mlocked++;      /* really mlocked the page */
-               }
-               up_read(&vma->vm_mm->mmap_sem);
-       }
-       return mlocked;
-}
-
 /**
  * try_to_unmap_anon - unmap or unlock anonymous page using the object-based
  * rmap method
  * @page: the page to unmap/unlock
- * @unlock:  request for unlock rather than unmap [unlikely]
- * @migration:  unmapping for migration - ignored if @unlock
+ * @flags: action and flags
  *
  * Find all the mappings of a page using the mapping pointer and the vma chains
  * contained in the anon_vma struct it points to.
@@ -1014,42 +1025,22 @@ static int try_to_unmap_anon(struct page *page, enum ttu_flags flags)
 {
        struct anon_vma *anon_vma;
        struct vm_area_struct *vma;
-       unsigned int mlocked = 0;
        int ret = SWAP_AGAIN;
-       int unlock = TTU_ACTION(flags) == TTU_MUNLOCK;
-
-       if (MLOCK_PAGES && unlikely(unlock))
-               ret = SWAP_SUCCESS;     /* default for try_to_munlock() */
 
        anon_vma = page_lock_anon_vma(page);
        if (!anon_vma)
                return ret;
 
        list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
-               if (MLOCK_PAGES && unlikely(unlock)) {
-                       if (!((vma->vm_flags & VM_LOCKED) &&
-                             page_mapped_in_vma(page, vma)))
-                               continue;  /* must visit all unlocked vmas */
-                       ret = SWAP_MLOCK;  /* saw at least one mlocked vma */
-               } else {
-                       ret = try_to_unmap_one(page, vma, flags);
-                       if (ret == SWAP_FAIL || !page_mapped(page))
-                               break;
-               }
-               if (ret == SWAP_MLOCK) {
-                       mlocked = try_to_mlock_page(page, vma);
-                       if (mlocked)
-                               break;  /* stop if actually mlocked page */
-               }
+               unsigned long address = vma_address(page, vma);
+               if (address == -EFAULT)
+                       continue;
+               ret = try_to_unmap_one(page, vma, address, flags);
+               if (ret != SWAP_AGAIN || !page_mapped(page))
+                       break;
        }
 
        page_unlock_anon_vma(anon_vma);
-
-       if (mlocked)
-               ret = SWAP_MLOCK;       /* actually mlocked the page */
-       else if (ret == SWAP_MLOCK)
-               ret = SWAP_AGAIN;       /* saw VM_LOCKED vma */
-
        return ret;
 }
 
@@ -1079,48 +1070,30 @@ static int try_to_unmap_file(struct page *page, enum ttu_flags flags)
        unsigned long max_nl_cursor = 0;
        unsigned long max_nl_size = 0;
        unsigned int mapcount;
-       unsigned int mlocked = 0;
-       int unlock = TTU_ACTION(flags) == TTU_MUNLOCK;
-
-       if (MLOCK_PAGES && unlikely(unlock))
-               ret = SWAP_SUCCESS;     /* default for try_to_munlock() */
 
        spin_lock(&mapping->i_mmap_lock);
        vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
-               if (MLOCK_PAGES && unlikely(unlock)) {
-                       if (!((vma->vm_flags & VM_LOCKED) &&
-                                               page_mapped_in_vma(page, vma)))
-                               continue;       /* must visit all vmas */
-                       ret = SWAP_MLOCK;
-               } else {
-                       ret = try_to_unmap_one(page, vma, flags);
-                       if (ret == SWAP_FAIL || !page_mapped(page))
-                               goto out;
-               }
-               if (ret == SWAP_MLOCK) {
-                       mlocked = try_to_mlock_page(page, vma);
-                       if (mlocked)
-                               break;  /* stop if actually mlocked page */
-               }
+               unsigned long address = vma_address(page, vma);
+               if (address == -EFAULT)
+                       continue;
+               ret = try_to_unmap_one(page, vma, address, flags);
+               if (ret != SWAP_AGAIN || !page_mapped(page))
+                       goto out;
        }
 
-       if (mlocked)
+       if (list_empty(&mapping->i_mmap_nonlinear))
                goto out;
 
-       if (list_empty(&mapping->i_mmap_nonlinear))
+       /*
+        * We don't bother to try to find the munlocked page in nonlinears.
+        * It's costly. Instead, later, page reclaim logic may call
+        * try_to_unmap(TTU_MUNLOCK) and recover PG_mlocked lazily.
+        */
+       if (TTU_ACTION(flags) == TTU_MUNLOCK)
                goto out;
 
        list_for_each_entry(vma, &mapping->i_mmap_nonlinear,
                                                shared.vm_set.list) {
-               if (MLOCK_PAGES && unlikely(unlock)) {
-                       if (!(vma->vm_flags & VM_LOCKED))
-                               continue;       /* must visit all vmas */
-                       ret = SWAP_MLOCK;       /* leave mlocked == 0 */
-                       goto out;               /* no need to look further */
-               }
-               if (!MLOCK_PAGES && !(flags & TTU_IGNORE_MLOCK) &&
-                       (vma->vm_flags & VM_LOCKED))
-                       continue;
                cursor = (unsigned long) vma->vm_private_data;
                if (cursor > max_nl_cursor)
                        max_nl_cursor = cursor;
@@ -1153,16 +1126,12 @@ static int try_to_unmap_file(struct page *page, enum ttu_flags flags)
        do {
                list_for_each_entry(vma, &mapping->i_mmap_nonlinear,
                                                shared.vm_set.list) {
-                       if (!MLOCK_PAGES && !(flags & TTU_IGNORE_MLOCK) &&
-                           (vma->vm_flags & VM_LOCKED))
-                               continue;
                        cursor = (unsigned long) vma->vm_private_data;
                        while ( cursor < max_nl_cursor &&
                                cursor < vma->vm_end - vma->vm_start) {
-                               ret = try_to_unmap_cluster(cursor, &mapcount,
-                                                               vma, page);
-                               if (ret == SWAP_MLOCK)
-                                       mlocked = 2;    /* to return below */
+                               if (try_to_unmap_cluster(cursor, &mapcount,
+                                               vma, page) == SWAP_MLOCK)
+                                       ret = SWAP_MLOCK;
                                cursor += CLUSTER_SIZE;
                                vma->vm_private_data = (void *) cursor;
                                if ((int)mapcount <= 0)
@@ -1183,10 +1152,6 @@ static int try_to_unmap_file(struct page *page, enum ttu_flags flags)
                vma->vm_private_data = NULL;
 out:
        spin_unlock(&mapping->i_mmap_lock);
-       if (mlocked)
-               ret = SWAP_MLOCK;       /* actually mlocked the page */
-       else if (ret == SWAP_MLOCK)
-               ret = SWAP_AGAIN;       /* saw VM_LOCKED vma */
        return ret;
 }
 
@@ -1210,7 +1175,9 @@ int try_to_unmap(struct page *page, enum ttu_flags flags)
 
        BUG_ON(!PageLocked(page));
 
-       if (PageAnon(page))
+       if (unlikely(PageKsm(page)))
+               ret = try_to_unmap_ksm(page, flags);
+       else if (PageAnon(page))
                ret = try_to_unmap_anon(page, flags);
        else
                ret = try_to_unmap_file(page, flags);
@@ -1229,17 +1196,98 @@ int try_to_unmap(struct page *page, enum ttu_flags flags)
  *
  * Return values are:
  *
- * SWAP_SUCCESS        - no vma's holding page mlocked.
+ * SWAP_AGAIN  - no vma is holding page mlocked, or,
  * SWAP_AGAIN  - page mapped in mlocked vma -- couldn't acquire mmap sem
+ * SWAP_FAIL   - page cannot be located at present
  * SWAP_MLOCK  - page is now mlocked.
  */
 int try_to_munlock(struct page *page)
 {
        VM_BUG_ON(!PageLocked(page) || PageLRU(page));
 
-       if (PageAnon(page))
+       if (unlikely(PageKsm(page)))
+               return try_to_unmap_ksm(page, TTU_MUNLOCK);
+       else if (PageAnon(page))
                return try_to_unmap_anon(page, TTU_MUNLOCK);
        else
                return try_to_unmap_file(page, TTU_MUNLOCK);
 }
 
+#ifdef CONFIG_MIGRATION
+/*
+ * rmap_walk() and its helpers rmap_walk_anon() and rmap_walk_file():
+ * Called by migrate.c to remove migration ptes, but might be used more later.
+ */
+static int rmap_walk_anon(struct page *page, int (*rmap_one)(struct page *,
+               struct vm_area_struct *, unsigned long, void *), void *arg)
+{
+       struct anon_vma *anon_vma;
+       struct vm_area_struct *vma;
+       int ret = SWAP_AGAIN;
+
+       /*
+        * Note: remove_migration_ptes() cannot use page_lock_anon_vma()
+        * because that depends on page_mapped(); but not all its usages
+        * are holding mmap_sem, which also gave the necessary guarantee
+        * (that this anon_vma's slab has not already been destroyed).
+        * This needs to be reviewed later: avoiding page_lock_anon_vma()
+        * is risky, and currently limits the usefulness of rmap_walk().
+        */
+       anon_vma = page_anon_vma(page);
+       if (!anon_vma)
+               return ret;
+       spin_lock(&anon_vma->lock);
+       list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+               unsigned long address = vma_address(page, vma);
+               if (address == -EFAULT)
+                       continue;
+               ret = rmap_one(page, vma, address, arg);
+               if (ret != SWAP_AGAIN)
+                       break;
+       }
+       spin_unlock(&anon_vma->lock);
+       return ret;
+}
+
+static int rmap_walk_file(struct page *page, int (*rmap_one)(struct page *,
+               struct vm_area_struct *, unsigned long, void *), void *arg)
+{
+       struct address_space *mapping = page->mapping;
+       pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
+       struct vm_area_struct *vma;
+       struct prio_tree_iter iter;
+       int ret = SWAP_AGAIN;
+
+       if (!mapping)
+               return ret;
+       spin_lock(&mapping->i_mmap_lock);
+       vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
+               unsigned long address = vma_address(page, vma);
+               if (address == -EFAULT)
+                       continue;
+               ret = rmap_one(page, vma, address, arg);
+               if (ret != SWAP_AGAIN)
+                       break;
+       }
+       /*
+        * No nonlinear handling: being always shared, nonlinear vmas
+        * never contain migration ptes.  Decide what to do about this
+        * limitation to linear when we need rmap_walk() on nonlinear.
+        */
+       spin_unlock(&mapping->i_mmap_lock);
+       return ret;
+}
+
+int rmap_walk(struct page *page, int (*rmap_one)(struct page *,
+               struct vm_area_struct *, unsigned long, void *), void *arg)
+{
+       VM_BUG_ON(!PageLocked(page));
+
+       if (unlikely(PageKsm(page)))
+               return rmap_walk_ksm(page, rmap_one, arg);
+       else if (PageAnon(page))
+               return rmap_walk_anon(page, rmap_one, arg);
+       else
+               return rmap_walk_file(page, rmap_one, arg);
+}
+#endif /* CONFIG_MIGRATION */
index 356dd99566ecb671cd324ba3dddcee10441312f0..4fb41c83daca8d7c1096c380cc0f579f04908e1a 100644 (file)
@@ -1017,7 +1017,14 @@ int shmem_unuse(swp_entry_t entry, struct page *page)
                        goto out;
        }
        mutex_unlock(&shmem_swaplist_mutex);
-out:   return found;   /* 0 or 1 or -ENOMEM */
+       /*
+        * Can some race bring us here?  We've been holding page lock,
+        * so I think not; but would rather try again later than BUG()
+        */
+       unlock_page(page);
+       page_cache_release(page);
+out:
+       return (found < 0) ? found : 0;
 }
 
 /*
@@ -1080,7 +1087,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
                else
                        inode = NULL;
                spin_unlock(&info->lock);
-               swap_duplicate(swap);
+               swap_shmem_alloc(swap);
                BUG_ON(page_mapped(page));
                page_cache_release(page);       /* pagecache ref */
                swap_writepage(page, wbc);
index 9c590eef79122dc0597dac27912f60e6b7dc6a72..6c0585b16418661529ef1c0399450bf4b9128a45 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/seq_file.h>
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/security.h>
 #include <linux/backing-dev.h>
 #include <linux/swapops.h>
 #include <linux/page_cgroup.h>
 
+static bool swap_count_continued(struct swap_info_struct *, pgoff_t,
+                                unsigned char);
+static void free_swap_count_continuations(struct swap_info_struct *);
+static sector_t map_swap_entry(swp_entry_t, struct block_device**);
+
 static DEFINE_SPINLOCK(swap_lock);
 static unsigned int nr_swapfiles;
 long nr_swap_pages;
 long total_swap_pages;
-static int swap_overflow;
 static int least_priority;
 
 static const char Bad_file[] = "Bad swap file entry ";
@@ -49,42 +54,20 @@ static const char Unused_offset[] = "Unused swap offset entry ";
 
 static struct swap_list_t swap_list = {-1, -1};
 
-static struct swap_info_struct swap_info[MAX_SWAPFILES];
+static struct swap_info_struct *swap_info[MAX_SWAPFILES];
 
 static DEFINE_MUTEX(swapon_mutex);
 
-/* For reference count accounting in swap_map */
-/* enum for swap_map[] handling. internal use only */
-enum {
-       SWAP_MAP = 0,   /* ops for reference from swap users */
-       SWAP_CACHE,     /* ops for reference from swap cache */
-};
-
-static inline int swap_count(unsigned short ent)
-{
-       return ent & SWAP_COUNT_MASK;
-}
-
-static inline bool swap_has_cache(unsigned short ent)
+static inline unsigned char swap_count(unsigned char ent)
 {
-       return !!(ent & SWAP_HAS_CACHE);
+       return ent & ~SWAP_HAS_CACHE;   /* may include SWAP_HAS_CONT flag */
 }
 
-static inline unsigned short encode_swapmap(int count, bool has_cache)
-{
-       unsigned short ret = count;
-
-       if (has_cache)
-               return SWAP_HAS_CACHE | ret;
-       return ret;
-}
-
-/* returnes 1 if swap entry is freed */
+/* returns 1 if swap entry is freed */
 static int
 __try_to_reclaim_swap(struct swap_info_struct *si, unsigned long offset)
 {
-       int type = si - swap_info;
-       swp_entry_t entry = swp_entry(type, offset);
+       swp_entry_t entry = swp_entry(si->type, offset);
        struct page *page;
        int ret = 0;
 
@@ -120,7 +103,7 @@ void swap_unplug_io_fn(struct backing_dev_info *unused_bdi, struct page *page)
        down_read(&swap_unplug_sem);
        entry.val = page_private(page);
        if (PageSwapCache(page)) {
-               struct block_device *bdev = swap_info[swp_type(entry)].bdev;
+               struct block_device *bdev = swap_info[swp_type(entry)]->bdev;
                struct backing_dev_info *bdi;
 
                /*
@@ -146,23 +129,28 @@ void swap_unplug_io_fn(struct backing_dev_info *unused_bdi, struct page *page)
 static int discard_swap(struct swap_info_struct *si)
 {
        struct swap_extent *se;
+       sector_t start_block;
+       sector_t nr_blocks;
        int err = 0;
 
-       list_for_each_entry(se, &si->extent_list, list) {
-               sector_t start_block = se->start_block << (PAGE_SHIFT - 9);
-               sector_t nr_blocks = (sector_t)se->nr_pages << (PAGE_SHIFT - 9);
+       /* Do not discard the swap header page! */
+       se = &si->first_swap_extent;
+       start_block = (se->start_block + 1) << (PAGE_SHIFT - 9);
+       nr_blocks = ((sector_t)se->nr_pages - 1) << (PAGE_SHIFT - 9);
+       if (nr_blocks) {
+               err = blkdev_issue_discard(si->bdev, start_block,
+                               nr_blocks, GFP_KERNEL, DISCARD_FL_BARRIER);
+               if (err)
+                       return err;
+               cond_resched();
+       }
 
-               if (se->start_page == 0) {
-                       /* Do not discard the swap header page! */
-                       start_block += 1 << (PAGE_SHIFT - 9);
-                       nr_blocks -= 1 << (PAGE_SHIFT - 9);
-                       if (!nr_blocks)
-                               continue;
-               }
+       list_for_each_entry(se, &si->first_swap_extent.list, list) {
+               start_block = se->start_block << (PAGE_SHIFT - 9);
+               nr_blocks = (sector_t)se->nr_pages << (PAGE_SHIFT - 9);
 
                err = blkdev_issue_discard(si->bdev, start_block,
-                                               nr_blocks, GFP_KERNEL,
-                                               DISCARD_FL_BARRIER);
+                               nr_blocks, GFP_KERNEL, DISCARD_FL_BARRIER);
                if (err)
                        break;
 
@@ -201,14 +189,11 @@ static void discard_swap_cluster(struct swap_info_struct *si,
                        start_block <<= PAGE_SHIFT - 9;
                        nr_blocks <<= PAGE_SHIFT - 9;
                        if (blkdev_issue_discard(si->bdev, start_block,
-                                                       nr_blocks, GFP_NOIO,
-                                                       DISCARD_FL_BARRIER))
+                                   nr_blocks, GFP_NOIO, DISCARD_FL_BARRIER))
                                break;
                }
 
                lh = se->list.next;
-               if (lh == &si->extent_list)
-                       lh = lh->next;
                se = list_entry(lh, struct swap_extent, list);
        }
 }
@@ -223,7 +208,7 @@ static int wait_for_discard(void *word)
 #define LATENCY_LIMIT          256
 
 static inline unsigned long scan_swap_map(struct swap_info_struct *si,
-                                         int cache)
+                                         unsigned char usage)
 {
        unsigned long offset;
        unsigned long scan_base;
@@ -354,10 +339,7 @@ checks:
                si->lowest_bit = si->max;
                si->highest_bit = 0;
        }
-       if (cache == SWAP_CACHE) /* at usual swap-out via vmscan.c */
-               si->swap_map[offset] = encode_swapmap(0, true);
-       else /* at suspend */
-               si->swap_map[offset] = encode_swapmap(1, false);
+       si->swap_map[offset] = usage;
        si->cluster_next = offset + 1;
        si->flags -= SWP_SCANNING;
 
@@ -467,10 +449,10 @@ swp_entry_t get_swap_page(void)
        nr_swap_pages--;
 
        for (type = swap_list.next; type >= 0 && wrapped < 2; type = next) {
-               si = swap_info + type;
+               si = swap_info[type];
                next = si->next;
                if (next < 0 ||
-                   (!wrapped && si->prio != swap_info[next].prio)) {
+                   (!wrapped && si->prio != swap_info[next]->prio)) {
                        next = swap_list.head;
                        wrapped++;
                }
@@ -482,7 +464,7 @@ swp_entry_t get_swap_page(void)
 
                swap_list.next = next;
                /* This is called for allocating swap entry for cache */
-               offset = scan_swap_map(si, SWAP_CACHE);
+               offset = scan_swap_map(si, SWAP_HAS_CACHE);
                if (offset) {
                        spin_unlock(&swap_lock);
                        return swp_entry(type, offset);
@@ -503,11 +485,11 @@ swp_entry_t get_swap_page_of_type(int type)
        pgoff_t offset;
 
        spin_lock(&swap_lock);
-       si = swap_info + type;
-       if (si->flags & SWP_WRITEOK) {
+       si = swap_info[type];
+       if (si && (si->flags & SWP_WRITEOK)) {
                nr_swap_pages--;
                /* This is called for allocating swap entry, not cache */
-               offset = scan_swap_map(si, SWAP_MAP);
+               offset = scan_swap_map(si, 1);
                if (offset) {
                        spin_unlock(&swap_lock);
                        return swp_entry(type, offset);
@@ -518,9 +500,9 @@ swp_entry_t get_swap_page_of_type(int type)
        return (swp_entry_t) {0};
 }
 
-static struct swap_info_struct * swap_info_get(swp_entry_t entry)
+static struct swap_info_struct *swap_info_get(swp_entry_t entry)
 {
-       struct swap_info_struct * p;
+       struct swap_info_struct *p;
        unsigned long offset, type;
 
        if (!entry.val)
@@ -528,7 +510,7 @@ static struct swap_info_struct * swap_info_get(swp_entry_t entry)
        type = swp_type(entry);
        if (type >= nr_swapfiles)
                goto bad_nofile;
-       p = swap_info[type];
+       p = swap_info[type];
        if (!(p->flags & SWP_USED))
                goto bad_device;
        offset = swp_offset(entry);
@@ -554,41 +536,56 @@ out:
        return NULL;
 }
 
-static int swap_entry_free(struct swap_info_struct *p,
-                          swp_entry_t ent, int cache)
+static unsigned char swap_entry_free(struct swap_info_struct *p,
+                                    swp_entry_t entry, unsigned char usage)
 {
-       unsigned long offset = swp_offset(ent);
-       int count = swap_count(p->swap_map[offset]);
-       bool has_cache;
+       unsigned long offset = swp_offset(entry);
+       unsigned char count;
+       unsigned char has_cache;
 
-       has_cache = swap_has_cache(p->swap_map[offset]);
+       count = p->swap_map[offset];
+       has_cache = count & SWAP_HAS_CACHE;
+       count &= ~SWAP_HAS_CACHE;
 
-       if (cache == SWAP_MAP) { /* dropping usage count of swap */
-               if (count < SWAP_MAP_MAX) {
-                       count--;
-                       p->swap_map[offset] = encode_swapmap(count, has_cache);
-               }
-       } else { /* dropping swap cache flag */
+       if (usage == SWAP_HAS_CACHE) {
                VM_BUG_ON(!has_cache);
-               p->swap_map[offset] = encode_swapmap(count, false);
-
+               has_cache = 0;
+       } else if (count == SWAP_MAP_SHMEM) {
+               /*
+                * Or we could insist on shmem.c using a special
+                * swap_shmem_free() and free_shmem_swap_and_cache()...
+                */
+               count = 0;
+       } else if ((count & ~COUNT_CONTINUED) <= SWAP_MAP_MAX) {
+               if (count == COUNT_CONTINUED) {
+                       if (swap_count_continued(p, offset, count))
+                               count = SWAP_MAP_MAX | COUNT_CONTINUED;
+                       else
+                               count = SWAP_MAP_MAX;
+               } else
+                       count--;
        }
-       /* return code. */
-       count = p->swap_map[offset];
+
+       if (!count)
+               mem_cgroup_uncharge_swap(entry);
+
+       usage = count | has_cache;
+       p->swap_map[offset] = usage;
+
        /* free if no reference */
-       if (!count) {
+       if (!usage) {
                if (offset < p->lowest_bit)
                        p->lowest_bit = offset;
                if (offset > p->highest_bit)
                        p->highest_bit = offset;
-               if (p->prio > swap_info[swap_list.next].prio)
-                       swap_list.next = p - swap_info;
+               if (swap_list.next >= 0 &&
+                   p->prio > swap_info[swap_list.next]->prio)
+                       swap_list.next = p->type;
                nr_swap_pages++;
                p->inuse_pages--;
        }
-       if (!swap_count(count))
-               mem_cgroup_uncharge_swap(ent);
-       return count;
+
+       return usage;
 }
 
 /*
@@ -597,11 +594,11 @@ static int swap_entry_free(struct swap_info_struct *p,
  */
 void swap_free(swp_entry_t entry)
 {
-       struct swap_info_struct * p;
+       struct swap_info_struct *p;
 
        p = swap_info_get(entry);
        if (p) {
-               swap_entry_free(p, entry, SWAP_MAP);
+               swap_entry_free(p, entry, 1);
                spin_unlock(&swap_lock);
        }
 }
@@ -612,26 +609,21 @@ void swap_free(swp_entry_t entry)
 void swapcache_free(swp_entry_t entry, struct page *page)
 {
        struct swap_info_struct *p;
-       int ret;
+       unsigned char count;
 
        p = swap_info_get(entry);
        if (p) {
-               ret = swap_entry_free(p, entry, SWAP_CACHE);
-               if (page) {
-                       bool swapout;
-                       if (ret)
-                               swapout = true; /* the end of swap out */
-                       else
-                               swapout = false; /* no more swap users! */
-                       mem_cgroup_uncharge_swapcache(page, entry, swapout);
-               }
+               count = swap_entry_free(p, entry, SWAP_HAS_CACHE);
+               if (page)
+                       mem_cgroup_uncharge_swapcache(page, entry, count != 0);
                spin_unlock(&swap_lock);
        }
-       return;
 }
 
 /*
  * How many references to page are currently swapped out?
+ * This does not give an exact answer when swap count is continued,
+ * but does include the high COUNT_CONTINUED flag to allow for that.
  */
 static inline int page_swapcount(struct page *page)
 {
@@ -659,6 +651,8 @@ int reuse_swap_page(struct page *page)
        int count;
 
        VM_BUG_ON(!PageLocked(page));
+       if (unlikely(PageKsm(page)))
+               return 0;
        count = page_mapcount(page);
        if (count <= 1 && PageSwapCache(page)) {
                count += page_swapcount(page);
@@ -667,7 +661,7 @@ int reuse_swap_page(struct page *page)
                        SetPageDirty(page);
                }
        }
-       return count == 1;
+       return count <= 1;
 }
 
 /*
@@ -704,7 +698,7 @@ int free_swap_and_cache(swp_entry_t entry)
 
        p = swap_info_get(entry);
        if (p) {
-               if (swap_entry_free(p, entry, SWAP_MAP) == SWAP_HAS_CACHE) {
+               if (swap_entry_free(p, entry, 1) == SWAP_HAS_CACHE) {
                        page = find_get_page(&swapper_space, entry.val);
                        if (page && !trylock_page(page)) {
                                page_cache_release(page);
@@ -741,14 +735,14 @@ int free_swap_and_cache(swp_entry_t entry)
 int swap_type_of(dev_t device, sector_t offset, struct block_device **bdev_p)
 {
        struct block_device *bdev = NULL;
-       int i;
+       int type;
 
        if (device)
                bdev = bdget(device);
 
        spin_lock(&swap_lock);
-       for (i = 0; i < nr_swapfiles; i++) {
-               struct swap_info_struct *sis = swap_info + i;
+       for (type = 0; type < nr_swapfiles; type++) {
+               struct swap_info_struct *sis = swap_info[type];
 
                if (!(sis->flags & SWP_WRITEOK))
                        continue;
@@ -758,20 +752,18 @@ int swap_type_of(dev_t device, sector_t offset, struct block_device **bdev_p)
                                *bdev_p = bdgrab(sis->bdev);
 
                        spin_unlock(&swap_lock);
-                       return i;
+                       return type;
                }
                if (bdev == sis->bdev) {
-                       struct swap_extent *se;
+                       struct swap_extent *se = &sis->first_swap_extent;
 
-                       se = list_entry(sis->extent_list.next,
-                                       struct swap_extent, list);
                        if (se->start_block == offset) {
                                if (bdev_p)
                                        *bdev_p = bdgrab(sis->bdev);
 
                                spin_unlock(&swap_lock);
                                bdput(bdev);
-                               return i;
+                               return type;
                        }
                }
        }
@@ -782,6 +774,21 @@ int swap_type_of(dev_t device, sector_t offset, struct block_device **bdev_p)
        return -ENODEV;
 }
 
+/*
+ * Get the (PAGE_SIZE) block corresponding to given offset on the swapdev
+ * corresponding to given index in swap_info (swap type).
+ */
+sector_t swapdev_block(int type, pgoff_t offset)
+{
+       struct block_device *bdev;
+
+       if ((unsigned int)type >= nr_swapfiles)
+               return 0;
+       if (!(swap_info[type]->flags & SWP_WRITEOK))
+               return 0;
+       return map_swap_entry(swp_entry(type, offset), &bdev);
+}
+
 /*
  * Return either the total number of swap pages of given type, or the number
  * of free pages of that type (depending on @free)
@@ -792,18 +799,20 @@ unsigned int count_swap_pages(int type, int free)
 {
        unsigned int n = 0;
 
-       if (type < nr_swapfiles) {
-               spin_lock(&swap_lock);
-               if (swap_info[type].flags & SWP_WRITEOK) {
-                       n = swap_info[type].pages;
+       spin_lock(&swap_lock);
+       if ((unsigned int)type < nr_swapfiles) {
+               struct swap_info_struct *sis = swap_info[type];
+
+               if (sis->flags & SWP_WRITEOK) {
+                       n = sis->pages;
                        if (free)
-                               n -= swap_info[type].inuse_pages;
+                               n -= sis->inuse_pages;
                }
-               spin_unlock(&swap_lock);
        }
+       spin_unlock(&swap_lock);
        return n;
 }
-#endif
+#endif /* CONFIG_HIBERNATION */
 
 /*
  * No need to decide whether this PTE shares the swap entry with others,
@@ -932,7 +941,7 @@ static int unuse_vma(struct vm_area_struct *vma,
        unsigned long addr, end, next;
        int ret;
 
-       if (page->mapping) {
+       if (page_anon_vma(page)) {
                addr = page_address_in_vma(page, vma);
                if (addr == -EFAULT)
                        return 0;
@@ -988,7 +997,7 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si,
 {
        unsigned int max = si->max;
        unsigned int i = prev;
-       int count;
+       unsigned char count;
 
        /*
         * No need for swap_lock here: we're just looking
@@ -1024,16 +1033,14 @@ static unsigned int find_next_to_unuse(struct swap_info_struct *si,
  */
 static int try_to_unuse(unsigned int type)
 {
-       struct swap_info_struct * si = &swap_info[type];
+       struct swap_info_struct *si = swap_info[type];
        struct mm_struct *start_mm;
-       unsigned short *swap_map;
-       unsigned short swcount;
+       unsigned char *swap_map;
+       unsigned char swcount;
        struct page *page;
        swp_entry_t entry;
        unsigned int i = 0;
        int retval = 0;
-       int reset_overflow = 0;
-       int shmem;
 
        /*
         * When searching mms for an entry, a good strategy is to
@@ -1047,8 +1054,7 @@ static int try_to_unuse(unsigned int type)
         * together, child after parent.  If we race with dup_mmap(), we
         * prefer to resolve parent before child, lest we miss entries
         * duplicated after we scanned child: using last mm would invert
-        * that.  Though it's only a serious concern when an overflowed
-        * swap count is reset from SWAP_MAP_MAX, preventing a rescan.
+        * that.
         */
        start_mm = &init_mm;
        atomic_inc(&init_mm.mm_users);
@@ -1110,17 +1116,18 @@ static int try_to_unuse(unsigned int type)
 
                /*
                 * Remove all references to entry.
-                * Whenever we reach init_mm, there's no address space
-                * to search, but use it as a reminder to search shmem.
                 */
-               shmem = 0;
                swcount = *swap_map;
-               if (swap_count(swcount)) {
-                       if (start_mm == &init_mm)
-                               shmem = shmem_unuse(entry, page);
-                       else
-                               retval = unuse_mm(start_mm, entry, page);
+               if (swap_count(swcount) == SWAP_MAP_SHMEM) {
+                       retval = shmem_unuse(entry, page);
+                       /* page has already been unlocked and released */
+                       if (retval < 0)
+                               break;
+                       continue;
                }
+               if (swap_count(swcount) && start_mm != &init_mm)
+                       retval = unuse_mm(start_mm, entry, page);
+
                if (swap_count(*swap_map)) {
                        int set_start_mm = (*swap_map >= swcount);
                        struct list_head *p = &start_mm->mmlist;
@@ -1131,7 +1138,7 @@ static int try_to_unuse(unsigned int type)
                        atomic_inc(&new_start_mm->mm_users);
                        atomic_inc(&prev_mm->mm_users);
                        spin_lock(&mmlist_lock);
-                       while (swap_count(*swap_map) && !retval && !shmem &&
+                       while (swap_count(*swap_map) && !retval &&
                                        (p = p->next) != &start_mm->mmlist) {
                                mm = list_entry(p, struct mm_struct, mmlist);
                                if (!atomic_inc_not_zero(&mm->mm_users))
@@ -1145,10 +1152,9 @@ static int try_to_unuse(unsigned int type)
                                swcount = *swap_map;
                                if (!swap_count(swcount)) /* any usage ? */
                                        ;
-                               else if (mm == &init_mm) {
+                               else if (mm == &init_mm)
                                        set_start_mm = 1;
-                                       shmem = shmem_unuse(entry, page);
-                               } else
+                               else
                                        retval = unuse_mm(mm, entry, page);
 
                                if (set_start_mm && *swap_map < swcount) {
@@ -1164,43 +1170,12 @@ static int try_to_unuse(unsigned int type)
                        mmput(start_mm);
                        start_mm = new_start_mm;
                }
-               if (shmem) {
-                       /* page has already been unlocked and released */
-                       if (shmem > 0)
-                               continue;
-                       retval = shmem;
-                       break;
-               }
                if (retval) {
                        unlock_page(page);
                        page_cache_release(page);
                        break;
                }
 
-               /*
-                * How could swap count reach 0x7ffe ?
-                * There's no way to repeat a swap page within an mm
-                * (except in shmem, where it's the shared object which takes
-                * the reference count)?
-                * We believe SWAP_MAP_MAX cannot occur.(if occur, unsigned
-                * short is too small....)
-                * If that's wrong, then we should worry more about
-                * exit_mmap() and do_munmap() cases described above:
-                * we might be resetting SWAP_MAP_MAX too early here.
-                * We know "Undead"s can happen, they're okay, so don't
-                * report them; but do report if we reset SWAP_MAP_MAX.
-                */
-               /* We might release the lock_page() in unuse_mm(). */
-               if (!PageSwapCache(page) || page_private(page) != entry.val)
-                       goto retry;
-
-               if (swap_count(*swap_map) == SWAP_MAP_MAX) {
-                       spin_lock(&swap_lock);
-                       *swap_map = encode_swapmap(0, true);
-                       spin_unlock(&swap_lock);
-                       reset_overflow = 1;
-               }
-
                /*
                 * If a reference remains (rare), we would like to leave
                 * the page in the swap cache; but try_to_unmap could
@@ -1213,6 +1188,12 @@ static int try_to_unuse(unsigned int type)
                 * read from disk into another page.  Splitting into two
                 * pages would be incorrect if swap supported "shared
                 * private" pages, but they are handled by tmpfs files.
+                *
+                * Given how unuse_vma() targets one particular offset
+                * in an anon_vma, once the anon_vma has been determined,
+                * this splitting happens to be just what is needed to
+                * handle where KSM pages have been swapped out: re-reading
+                * is unnecessarily slow, but we can fix that later on.
                 */
                if (swap_count(*swap_map) &&
                     PageDirty(page) && PageSwapCache(page)) {
@@ -1242,7 +1223,6 @@ static int try_to_unuse(unsigned int type)
                 * mark page dirty so shrink_page_list will preserve it.
                 */
                SetPageDirty(page);
-retry:
                unlock_page(page);
                page_cache_release(page);
 
@@ -1254,10 +1234,6 @@ retry:
        }
 
        mmput(start_mm);
-       if (reset_overflow) {
-               printk(KERN_WARNING "swapoff: cleared swap entry overflow\n");
-               swap_overflow = 0;
-       }
        return retval;
 }
 
@@ -1270,10 +1246,10 @@ retry:
 static void drain_mmlist(void)
 {
        struct list_head *p, *next;
-       unsigned int i;
+       unsigned int type;
 
-       for (i = 0; i < nr_swapfiles; i++)
-               if (swap_info[i].inuse_pages)
+       for (type = 0; type < nr_swapfiles; type++)
+               if (swap_info[type]->inuse_pages)
                        return;
        spin_lock(&mmlist_lock);
        list_for_each_safe(p, next, &init_mm.mmlist)
@@ -1283,12 +1259,23 @@ static void drain_mmlist(void)
 
 /*
  * Use this swapdev's extent info to locate the (PAGE_SIZE) block which
- * corresponds to page offset `offset'.
+ * corresponds to page offset for the specified swap entry.
+ * Note that the type of this function is sector_t, but it returns page offset
+ * into the bdev, not sector offset.
  */
-sector_t map_swap_page(struct swap_info_struct *sis, pgoff_t offset)
+static sector_t map_swap_entry(swp_entry_t entry, struct block_device **bdev)
 {
-       struct swap_extent *se = sis->curr_swap_extent;
-       struct swap_extent *start_se = se;
+       struct swap_info_struct *sis;
+       struct swap_extent *start_se;
+       struct swap_extent *se;
+       pgoff_t offset;
+
+       sis = swap_info[swp_type(entry)];
+       *bdev = sis->bdev;
+
+       offset = swp_offset(entry);
+       start_se = sis->curr_swap_extent;
+       se = start_se;
 
        for ( ; ; ) {
                struct list_head *lh;
@@ -1298,40 +1285,31 @@ sector_t map_swap_page(struct swap_info_struct *sis, pgoff_t offset)
                        return se->start_block + (offset - se->start_page);
                }
                lh = se->list.next;
-               if (lh == &sis->extent_list)
-                       lh = lh->next;
                se = list_entry(lh, struct swap_extent, list);
                sis->curr_swap_extent = se;
                BUG_ON(se == start_se);         /* It *must* be present */
        }
 }
 
-#ifdef CONFIG_HIBERNATION
 /*
- * Get the (PAGE_SIZE) block corresponding to given offset on the swapdev
- * corresponding to given index in swap_info (swap type).
+ * Returns the page offset into bdev for the specified page's swap entry.
  */
-sector_t swapdev_block(int swap_type, pgoff_t offset)
+sector_t map_swap_page(struct page *page, struct block_device **bdev)
 {
-       struct swap_info_struct *sis;
-
-       if (swap_type >= nr_swapfiles)
-               return 0;
-
-       sis = swap_info + swap_type;
-       return (sis->flags & SWP_WRITEOK) ? map_swap_page(sis, offset) : 0;
+       swp_entry_t entry;
+       entry.val = page_private(page);
+       return map_swap_entry(entry, bdev);
 }
-#endif /* CONFIG_HIBERNATION */
 
 /*
  * Free all of a swapdev's extent information
  */
 static void destroy_swap_extents(struct swap_info_struct *sis)
 {
-       while (!list_empty(&sis->extent_list)) {
+       while (!list_empty(&sis->first_swap_extent.list)) {
                struct swap_extent *se;
 
-               se = list_entry(sis->extent_list.next,
+               se = list_entry(sis->first_swap_extent.list.next,
                                struct swap_extent, list);
                list_del(&se->list);
                kfree(se);
@@ -1352,8 +1330,15 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
        struct swap_extent *new_se;
        struct list_head *lh;
 
-       lh = sis->extent_list.prev;     /* The highest page extent */
-       if (lh != &sis->extent_list) {
+       if (start_page == 0) {
+               se = &sis->first_swap_extent;
+               sis->curr_swap_extent = se;
+               se->start_page = 0;
+               se->nr_pages = nr_pages;
+               se->start_block = start_block;
+               return 1;
+       } else {
+               lh = sis->first_swap_extent.list.prev;  /* Highest extent */
                se = list_entry(lh, struct swap_extent, list);
                BUG_ON(se->start_page + se->nr_pages != start_page);
                if (se->start_block + se->nr_pages == start_block) {
@@ -1373,7 +1358,7 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
        new_se->nr_pages = nr_pages;
        new_se->start_block = start_block;
 
-       list_add_tail(&new_se->list, &sis->extent_list);
+       list_add_tail(&new_se->list, &sis->first_swap_extent.list);
        return 1;
 }
 
@@ -1425,7 +1410,7 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span)
        if (S_ISBLK(inode->i_mode)) {
                ret = add_swap_extent(sis, 0, sis->max, 0);
                *span = sis->pages;
-               goto done;
+               goto out;
        }
 
        blkbits = inode->i_blkbits;
@@ -1496,25 +1481,22 @@ reprobe:
        sis->max = page_no;
        sis->pages = page_no - 1;
        sis->highest_bit = page_no - 1;
-done:
-       sis->curr_swap_extent = list_entry(sis->extent_list.prev,
-                                       struct swap_extent, list);
-       goto out;
+out:
+       return ret;
 bad_bmap:
        printk(KERN_ERR "swapon: swapfile has holes\n");
        ret = -EINVAL;
-out:
-       return ret;
+       goto out;
 }
 
 SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 {
-       struct swap_info_struct * p = NULL;
-       unsigned short *swap_map;
+       struct swap_info_struct *p = NULL;
+       unsigned char *swap_map;
        struct file *swap_file, *victim;
        struct address_space *mapping;
        struct inode *inode;
-       char * pathname;
+       char *pathname;
        int i, type, prev;
        int err;
 
@@ -1535,8 +1517,8 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
        mapping = victim->f_mapping;
        prev = -1;
        spin_lock(&swap_lock);
-       for (type = swap_list.head; type >= 0; type = swap_info[type].next) {
-               p = swap_info + type;
+       for (type = swap_list.head; type >= 0; type = swap_info[type]->next) {
+               p = swap_info[type];
                if (p->flags & SWP_WRITEOK) {
                        if (p->swap_file->f_mapping == mapping)
                                break;
@@ -1555,18 +1537,17 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
                spin_unlock(&swap_lock);
                goto out_dput;
        }
-       if (prev < 0) {
+       if (prev < 0)
                swap_list.head = p->next;
-       } else {
-               swap_info[prev].next = p->next;
-       }
+       else
+               swap_info[prev]->next = p->next;
        if (type == swap_list.next) {
                /* just pick something that's safe... */
                swap_list.next = swap_list.head;
        }
        if (p->prio < 0) {
-               for (i = p->next; i >= 0; i = swap_info[i].next)
-                       swap_info[i].prio = p->prio--;
+               for (i = p->next; i >= 0; i = swap_info[i]->next)
+                       swap_info[i]->prio = p->prio--;
                least_priority++;
        }
        nr_swap_pages -= p->pages;
@@ -1584,16 +1565,16 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
                if (p->prio < 0)
                        p->prio = --least_priority;
                prev = -1;
-               for (i = swap_list.head; i >= 0; i = swap_info[i].next) {
-                       if (p->prio >= swap_info[i].prio)
+               for (i = swap_list.head; i >= 0; i = swap_info[i]->next) {
+                       if (p->prio >= swap_info[i]->prio)
                                break;
                        prev = i;
                }
                p->next = i;
                if (prev < 0)
-                       swap_list.head = swap_list.next = p - swap_info;
+                       swap_list.head = swap_list.next = type;
                else
-                       swap_info[prev].next = p - swap_info;
+                       swap_info[prev]->next = type;
                nr_swap_pages += p->pages;
                total_swap_pages += p->pages;
                p->flags |= SWP_WRITEOK;
@@ -1606,6 +1587,9 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
        up_write(&swap_unplug_sem);
 
        destroy_swap_extents(p);
+       if (p->flags & SWP_CONTINUED)
+               free_swap_count_continuations(p);
+
        mutex_lock(&swapon_mutex);
        spin_lock(&swap_lock);
        drain_mmlist();
@@ -1653,8 +1637,8 @@ out:
 /* iterator */
 static void *swap_start(struct seq_file *swap, loff_t *pos)
 {
-       struct swap_info_struct *ptr = swap_info;
-       int i;
+       struct swap_info_struct *si;
+       int type;
        loff_t l = *pos;
 
        mutex_lock(&swapon_mutex);
@@ -1662,11 +1646,13 @@ static void *swap_start(struct seq_file *swap, loff_t *pos)
        if (!l)
                return SEQ_START_TOKEN;
 
-       for (i = 0; i < nr_swapfiles; i++, ptr++) {
-               if (!(ptr->flags & SWP_USED) || !ptr->swap_map)
+       for (type = 0; type < nr_swapfiles; type++) {
+               smp_rmb();      /* read nr_swapfiles before swap_info[type] */
+               si = swap_info[type];
+               if (!(si->flags & SWP_USED) || !si->swap_map)
                        continue;
                if (!--l)
-                       return ptr;
+                       return si;
        }
 
        return NULL;
@@ -1674,21 +1660,21 @@ static void *swap_start(struct seq_file *swap, loff_t *pos)
 
 static void *swap_next(struct seq_file *swap, void *v, loff_t *pos)
 {
-       struct swap_info_struct *ptr;
-       struct swap_info_struct *endptr = swap_info + nr_swapfiles;
+       struct swap_info_struct *si = v;
+       int type;
 
        if (v == SEQ_START_TOKEN)
-               ptr = swap_info;
-       else {
-               ptr = v;
-               ptr++;
-       }
+               type = 0;
+       else
+               type = si->type + 1;
 
-       for (; ptr < endptr; ptr++) {
-               if (!(ptr->flags & SWP_USED) || !ptr->swap_map)
+       for (; type < nr_swapfiles; type++) {
+               smp_rmb();      /* read nr_swapfiles before swap_info[type] */
+               si = swap_info[type];
+               if (!(si->flags & SWP_USED) || !si->swap_map)
                        continue;
                ++*pos;
-               return ptr;
+               return si;
        }
 
        return NULL;
@@ -1701,24 +1687,24 @@ static void swap_stop(struct seq_file *swap, void *v)
 
 static int swap_show(struct seq_file *swap, void *v)
 {
-       struct swap_info_struct *ptr = v;
+       struct swap_info_struct *si = v;
        struct file *file;
        int len;
 
-       if (ptr == SEQ_START_TOKEN) {
+       if (si == SEQ_START_TOKEN) {
                seq_puts(swap,"Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
                return 0;
        }
 
-       file = ptr->swap_file;
+       file = si->swap_file;
        len = seq_path(swap, &file->f_path, " \t\n\\");
        seq_printf(swap, "%*s%s\t%u\t%u\t%d\n",
                        len < 40 ? 40 - len : 1, " ",
                        S_ISBLK(file->f_path.dentry->d_inode->i_mode) ?
                                "partition" : "file\t",
-                       ptr->pages << (PAGE_SHIFT - 10),
-                       ptr->inuse_pages << (PAGE_SHIFT - 10),
-                       ptr->prio);
+                       si->pages << (PAGE_SHIFT - 10),
+                       si->inuse_pages << (PAGE_SHIFT - 10),
+                       si->prio);
        return 0;
 }
 
@@ -1765,7 +1751,7 @@ late_initcall(max_swapfiles_check);
  */
 SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 {
-       struct swap_info_struct * p;
+       struct swap_info_struct *p;
        char *name = NULL;
        struct block_device *bdev = NULL;
        struct file *swap_file = NULL;
@@ -1779,30 +1765,52 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
        sector_t span;
        unsigned long maxpages = 1;
        unsigned long swapfilepages;
-       unsigned short *swap_map = NULL;
+       unsigned char *swap_map = NULL;
        struct page *page = NULL;
        struct inode *inode = NULL;
        int did_down = 0;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
        spin_lock(&swap_lock);
-       p = swap_info;
-       for (type = 0 ; type < nr_swapfiles ; type++,p++)
-               if (!(p->flags & SWP_USED))
+       for (type = 0; type < nr_swapfiles; type++) {
+               if (!(swap_info[type]->flags & SWP_USED))
                        break;
+       }
        error = -EPERM;
        if (type >= MAX_SWAPFILES) {
                spin_unlock(&swap_lock);
+               kfree(p);
                goto out;
        }
-       if (type >= nr_swapfiles)
-               nr_swapfiles = type+1;
-       memset(p, 0, sizeof(*p));
-       INIT_LIST_HEAD(&p->extent_list);
+       if (type >= nr_swapfiles) {
+               p->type = type;
+               swap_info[type] = p;
+               /*
+                * Write swap_info[type] before nr_swapfiles, in case a
+                * racing procfs swap_start() or swap_next() is reading them.
+                * (We never shrink nr_swapfiles, we never free this entry.)
+                */
+               smp_wmb();
+               nr_swapfiles++;
+       } else {
+               kfree(p);
+               p = swap_info[type];
+               /*
+                * Do not memset this entry: a racing procfs swap_next()
+                * would be relying on p->type to remain valid.
+                */
+       }
+       INIT_LIST_HEAD(&p->first_swap_extent.list);
        p->flags = SWP_USED;
        p->next = -1;
        spin_unlock(&swap_lock);
+
        name = getname(specialfile);
        error = PTR_ERR(name);
        if (IS_ERR(name)) {
@@ -1822,7 +1830,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 
        error = -EBUSY;
        for (i = 0; i < nr_swapfiles; i++) {
-               struct swap_info_struct *q = &swap_info[i];
+               struct swap_info_struct *q = swap_info[i];
 
                if (i == type || !q->swap_file)
                        continue;
@@ -1897,6 +1905,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 
        p->lowest_bit  = 1;
        p->cluster_next = 1;
+       p->cluster_nr = 0;
 
        /*
         * Find out how many pages are allowed for a single swap
@@ -1932,13 +1941,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
                goto bad_swap;
 
        /* OK, set up the swap map and apply the bad block list */
-       swap_map = vmalloc(maxpages * sizeof(short));
+       swap_map = vmalloc(maxpages);
        if (!swap_map) {
                error = -ENOMEM;
                goto bad_swap;
        }
 
-       memset(swap_map, 0, maxpages * sizeof(short));
+       memset(swap_map, 0, maxpages);
        for (i = 0; i < swap_header->info.nr_badpages; i++) {
                int page_nr = swap_header->info.badpages[i];
                if (page_nr <= 0 || page_nr >= swap_header->info.last_page) {
@@ -2003,18 +2012,16 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 
        /* insert swap space into swap_list: */
        prev = -1;
-       for (i = swap_list.head; i >= 0; i = swap_info[i].next) {
-               if (p->prio >= swap_info[i].prio) {
+       for (i = swap_list.head; i >= 0; i = swap_info[i]->next) {
+               if (p->prio >= swap_info[i]->prio)
                        break;
-               }
                prev = i;
        }
        p->next = i;
-       if (prev < 0) {
-               swap_list.head = swap_list.next = p - swap_info;
-       } else {
-               swap_info[prev].next = p - swap_info;
-       }
+       if (prev < 0)
+               swap_list.head = swap_list.next = type;
+       else
+               swap_info[prev]->next = type;
        spin_unlock(&swap_lock);
        mutex_unlock(&swapon_mutex);
        error = 0;
@@ -2051,15 +2058,15 @@ out:
 
 void si_swapinfo(struct sysinfo *val)
 {
-       unsigned int i;
+       unsigned int type;
        unsigned long nr_to_be_unused = 0;
 
        spin_lock(&swap_lock);
-       for (i = 0; i < nr_swapfiles; i++) {
-               if (!(swap_info[i].flags & SWP_USED) ||
-                    (swap_info[i].flags & SWP_WRITEOK))
-                       continue;
-               nr_to_be_unused += swap_info[i].inuse_pages;
+       for (type = 0; type < nr_swapfiles; type++) {
+               struct swap_info_struct *si = swap_info[type];
+
+               if ((si->flags & SWP_USED) && !(si->flags & SWP_WRITEOK))
+                       nr_to_be_unused += si->inuse_pages;
        }
        val->freeswap = nr_swap_pages + nr_to_be_unused;
        val->totalswap = total_swap_pages + nr_to_be_unused;
@@ -2069,101 +2076,107 @@ void si_swapinfo(struct sysinfo *val)
 /*
  * Verify that a swap entry is valid and increment its swap map count.
  *
- * Note: if swap_map[] reaches SWAP_MAP_MAX the entries are treated as
- * "permanent", but will be reclaimed by the next swapoff.
  * Returns error code in following case.
  * - success -> 0
  * - swp_entry is invalid -> EINVAL
  * - swp_entry is migration entry -> EINVAL
  * - swap-cache reference is requested but there is already one. -> EEXIST
  * - swap-cache reference is requested but the entry is not used. -> ENOENT
+ * - swap-mapped reference requested but needs continued swap count. -> ENOMEM
  */
-static int __swap_duplicate(swp_entry_t entry, bool cache)
+static int __swap_duplicate(swp_entry_t entry, unsigned char usage)
 {
-       struct swap_info_struct * p;
+       struct swap_info_struct *p;
        unsigned long offset, type;
-       int result = -EINVAL;
-       int count;
-       bool has_cache;
+       unsigned char count;
+       unsigned char has_cache;
+       int err = -EINVAL;
 
        if (non_swap_entry(entry))
-               return -EINVAL;
+               goto out;
 
        type = swp_type(entry);
        if (type >= nr_swapfiles)
                goto bad_file;
-       p = type + swap_info;
+       p = swap_info[type];
        offset = swp_offset(entry);
 
        spin_lock(&swap_lock);
-
        if (unlikely(offset >= p->max))
                goto unlock_out;
 
-       count = swap_count(p->swap_map[offset]);
-       has_cache = swap_has_cache(p->swap_map[offset]);
+       count = p->swap_map[offset];
+       has_cache = count & SWAP_HAS_CACHE;
+       count &= ~SWAP_HAS_CACHE;
+       err = 0;
 
-       if (cache == SWAP_CACHE) { /* called for swapcache/swapin-readahead */
+       if (usage == SWAP_HAS_CACHE) {
 
                /* set SWAP_HAS_CACHE if there is no cache and entry is used */
-               if (!has_cache && count) {
-                       p->swap_map[offset] = encode_swapmap(count, true);
-                       result = 0;
-               } else if (has_cache) /* someone added cache */
-                       result = -EEXIST;
-               else if (!count) /* no users */
-                       result = -ENOENT;
+               if (!has_cache && count)
+                       has_cache = SWAP_HAS_CACHE;
+               else if (has_cache)             /* someone else added cache */
+                       err = -EEXIST;
+               else                            /* no users remaining */
+                       err = -ENOENT;
 
        } else if (count || has_cache) {
-               if (count < SWAP_MAP_MAX - 1) {
-                       p->swap_map[offset] = encode_swapmap(count + 1,
-                                                            has_cache);
-                       result = 0;
-               } else if (count <= SWAP_MAP_MAX) {
-                       if (swap_overflow++ < 5)
-                               printk(KERN_WARNING
-                                      "swap_dup: swap entry overflow\n");
-                       p->swap_map[offset] = encode_swapmap(SWAP_MAP_MAX,
-                                                             has_cache);
-                       result = 0;
-               }
+
+               if ((count & ~COUNT_CONTINUED) < SWAP_MAP_MAX)
+                       count += usage;
+               else if ((count & ~COUNT_CONTINUED) > SWAP_MAP_MAX)
+                       err = -EINVAL;
+               else if (swap_count_continued(p, offset, count))
+                       count = COUNT_CONTINUED;
+               else
+                       err = -ENOMEM;
        } else
-               result = -ENOENT; /* unused swap entry */
+               err = -ENOENT;                  /* unused swap entry */
+
+       p->swap_map[offset] = count | has_cache;
+
 unlock_out:
        spin_unlock(&swap_lock);
 out:
-       return result;
+       return err;
 
 bad_file:
        printk(KERN_ERR "swap_dup: %s%08lx\n", Bad_file, entry.val);
        goto out;
 }
+
+/*
+ * Help swapoff by noting that swap entry belongs to shmem/tmpfs
+ * (in which case its reference count is never incremented).
+ */
+void swap_shmem_alloc(swp_entry_t entry)
+{
+       __swap_duplicate(entry, SWAP_MAP_SHMEM);
+}
+
 /*
  * increase reference count of swap entry by 1.
  */
-void swap_duplicate(swp_entry_t entry)
+int swap_duplicate(swp_entry_t entry)
 {
-       __swap_duplicate(entry, SWAP_MAP);
+       int err = 0;
+
+       while (!err && __swap_duplicate(entry, 1) == -ENOMEM)
+               err = add_swap_count_continuation(entry, GFP_ATOMIC);
+       return err;
 }
 
 /*
  * @entry: swap entry for which we allocate swap cache.
  *
- * Called when allocating swap cache for exising swap entry,
+ * Called when allocating swap cache for existing swap entry,
  * This can return error codes. Returns 0 at success.
  * -EBUSY means there is a swap cache.
  * Note: return code is different from swap_duplicate().
  */
 int swapcache_prepare(swp_entry_t entry)
 {
-       return __swap_duplicate(entry, SWAP_CACHE);
-}
-
-
-struct swap_info_struct *
-get_swap_info_struct(unsigned type)
-{
-       return &swap_info[type];
+       return __swap_duplicate(entry, SWAP_HAS_CACHE);
 }
 
 /*
@@ -2181,7 +2194,7 @@ int valid_swaphandles(swp_entry_t entry, unsigned long *offset)
        if (!our_page_cluster)  /* no readahead */
                return 0;
 
-       si = &swap_info[swp_type(entry)];
+       si = swap_info[swp_type(entry)];
        target = swp_offset(entry);
        base = (target >> our_page_cluster) << our_page_cluster;
        end = base + (1 << our_page_cluster);
@@ -2217,3 +2230,219 @@ int valid_swaphandles(swp_entry_t entry, unsigned long *offset)
        *offset = ++toff;
        return nr_pages? ++nr_pages: 0;
 }
+
+/*
+ * add_swap_count_continuation - called when a swap count is duplicated
+ * beyond SWAP_MAP_MAX, it allocates a new page and links that to the entry's
+ * page of the original vmalloc'ed swap_map, to hold the continuation count
+ * (for that entry and for its neighbouring PAGE_SIZE swap entries).  Called
+ * again when count is duplicated beyond SWAP_MAP_MAX * SWAP_CONT_MAX, etc.
+ *
+ * These continuation pages are seldom referenced: the common paths all work
+ * on the original swap_map, only referring to a continuation page when the
+ * low "digit" of a count is incremented or decremented through SWAP_MAP_MAX.
+ *
+ * add_swap_count_continuation(, GFP_ATOMIC) can be called while holding
+ * page table locks; if it fails, add_swap_count_continuation(, GFP_KERNEL)
+ * can be called after dropping locks.
+ */
+int add_swap_count_continuation(swp_entry_t entry, gfp_t gfp_mask)
+{
+       struct swap_info_struct *si;
+       struct page *head;
+       struct page *page;
+       struct page *list_page;
+       pgoff_t offset;
+       unsigned char count;
+
+       /*
+        * When debugging, it's easier to use __GFP_ZERO here; but it's better
+        * for latency not to zero a page while GFP_ATOMIC and holding locks.
+        */
+       page = alloc_page(gfp_mask | __GFP_HIGHMEM);
+
+       si = swap_info_get(entry);
+       if (!si) {
+               /*
+                * An acceptable race has occurred since the failing
+                * __swap_duplicate(): the swap entry has been freed,
+                * perhaps even the whole swap_map cleared for swapoff.
+                */
+               goto outer;
+       }
+
+       offset = swp_offset(entry);
+       count = si->swap_map[offset] & ~SWAP_HAS_CACHE;
+
+       if ((count & ~COUNT_CONTINUED) != SWAP_MAP_MAX) {
+               /*
+                * The higher the swap count, the more likely it is that tasks
+                * will race to add swap count continuation: we need to avoid
+                * over-provisioning.
+                */
+               goto out;
+       }
+
+       if (!page) {
+               spin_unlock(&swap_lock);
+               return -ENOMEM;
+       }
+
+       /*
+        * We are fortunate that although vmalloc_to_page uses pte_offset_map,
+        * no architecture is using highmem pages for kernel pagetables: so it
+        * will not corrupt the GFP_ATOMIC caller's atomic pagetable kmaps.
+        */
+       head = vmalloc_to_page(si->swap_map + offset);
+       offset &= ~PAGE_MASK;
+
+       /*
+        * Page allocation does not initialize the page's lru field,
+        * but it does always reset its private field.
+        */
+       if (!page_private(head)) {
+               BUG_ON(count & COUNT_CONTINUED);
+               INIT_LIST_HEAD(&head->lru);
+               set_page_private(head, SWP_CONTINUED);
+               si->flags |= SWP_CONTINUED;
+       }
+
+       list_for_each_entry(list_page, &head->lru, lru) {
+               unsigned char *map;
+
+               /*
+                * If the previous map said no continuation, but we've found
+                * a continuation page, free our allocation and use this one.
+                */
+               if (!(count & COUNT_CONTINUED))
+                       goto out;
+
+               map = kmap_atomic(list_page, KM_USER0) + offset;
+               count = *map;
+               kunmap_atomic(map, KM_USER0);
+
+               /*
+                * If this continuation count now has some space in it,
+                * free our allocation and use this one.
+                */
+               if ((count & ~COUNT_CONTINUED) != SWAP_CONT_MAX)
+                       goto out;
+       }
+
+       list_add_tail(&page->lru, &head->lru);
+       page = NULL;                    /* now it's attached, don't free it */
+out:
+       spin_unlock(&swap_lock);
+outer:
+       if (page)
+               __free_page(page);
+       return 0;
+}
+
+/*
+ * swap_count_continued - when the original swap_map count is incremented
+ * from SWAP_MAP_MAX, check if there is already a continuation page to carry
+ * into, carry if so, or else fail until a new continuation page is allocated;
+ * when the original swap_map count is decremented from 0 with continuation,
+ * borrow from the continuation and report whether it still holds more.
+ * Called while __swap_duplicate() or swap_entry_free() holds swap_lock.
+ */
+static bool swap_count_continued(struct swap_info_struct *si,
+                                pgoff_t offset, unsigned char count)
+{
+       struct page *head;
+       struct page *page;
+       unsigned char *map;
+
+       head = vmalloc_to_page(si->swap_map + offset);
+       if (page_private(head) != SWP_CONTINUED) {
+               BUG_ON(count & COUNT_CONTINUED);
+               return false;           /* need to add count continuation */
+       }
+
+       offset &= ~PAGE_MASK;
+       page = list_entry(head->lru.next, struct page, lru);
+       map = kmap_atomic(page, KM_USER0) + offset;
+
+       if (count == SWAP_MAP_MAX)      /* initial increment from swap_map */
+               goto init_map;          /* jump over SWAP_CONT_MAX checks */
+
+       if (count == (SWAP_MAP_MAX | COUNT_CONTINUED)) { /* incrementing */
+               /*
+                * Think of how you add 1 to 999
+                */
+               while (*map == (SWAP_CONT_MAX | COUNT_CONTINUED)) {
+                       kunmap_atomic(map, KM_USER0);
+                       page = list_entry(page->lru.next, struct page, lru);
+                       BUG_ON(page == head);
+                       map = kmap_atomic(page, KM_USER0) + offset;
+               }
+               if (*map == SWAP_CONT_MAX) {
+                       kunmap_atomic(map, KM_USER0);
+                       page = list_entry(page->lru.next, struct page, lru);
+                       if (page == head)
+                               return false;   /* add count continuation */
+                       map = kmap_atomic(page, KM_USER0) + offset;
+init_map:              *map = 0;               /* we didn't zero the page */
+               }
+               *map += 1;
+               kunmap_atomic(map, KM_USER0);
+               page = list_entry(page->lru.prev, struct page, lru);
+               while (page != head) {
+                       map = kmap_atomic(page, KM_USER0) + offset;
+                       *map = COUNT_CONTINUED;
+                       kunmap_atomic(map, KM_USER0);
+                       page = list_entry(page->lru.prev, struct page, lru);
+               }
+               return true;                    /* incremented */
+
+       } else {                                /* decrementing */
+               /*
+                * Think of how you subtract 1 from 1000
+                */
+               BUG_ON(count != COUNT_CONTINUED);
+               while (*map == COUNT_CONTINUED) {
+                       kunmap_atomic(map, KM_USER0);
+                       page = list_entry(page->lru.next, struct page, lru);
+                       BUG_ON(page == head);
+                       map = kmap_atomic(page, KM_USER0) + offset;
+               }
+               BUG_ON(*map == 0);
+               *map -= 1;
+               if (*map == 0)
+                       count = 0;
+               kunmap_atomic(map, KM_USER0);
+               page = list_entry(page->lru.prev, struct page, lru);
+               while (page != head) {
+                       map = kmap_atomic(page, KM_USER0) + offset;
+                       *map = SWAP_CONT_MAX | count;
+                       count = COUNT_CONTINUED;
+                       kunmap_atomic(map, KM_USER0);
+                       page = list_entry(page->lru.prev, struct page, lru);
+               }
+               return count == COUNT_CONTINUED;
+       }
+}
+
+/*
+ * free_swap_count_continuations - swapoff free all the continuation pages
+ * appended to the swap_map, after swap_map is quiesced, before vfree'ing it.
+ */
+static void free_swap_count_continuations(struct swap_info_struct *si)
+{
+       pgoff_t offset;
+
+       for (offset = 0; offset < si->max; offset += PAGE_SIZE) {
+               struct page *head;
+               head = vmalloc_to_page(si->swap_map + offset);
+               if (page_private(head)) {
+                       struct list_head *this, *next;
+                       list_for_each_safe(this, next, &head->lru) {
+                               struct page *page;
+                               page = list_entry(this, struct page, lru);
+                               list_del(this);
+                               __free_page(page);
+                       }
+               }
+       }
+}
index 9b08d790df6fe441a2b8c2002ed9f0e1a4677641..37e69295f250d585b1a2f7e9bf38f80b7186cdbb 100644 (file)
@@ -1411,6 +1411,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
 {
        struct page **pages;
        unsigned int nr_pages, array_size, i;
+       gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
 
        nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;
        array_size = (nr_pages * sizeof(struct page *));
@@ -1418,13 +1419,11 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
        area->nr_pages = nr_pages;
        /* Please note that the recursion is strictly bounded. */
        if (array_size > PAGE_SIZE) {
-               pages = __vmalloc_node(array_size, 1, gfp_mask | __GFP_ZERO,
+               pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM,
                                PAGE_KERNEL, node, caller);
                area->flags |= VM_VPAGES;
        } else {
-               pages = kmalloc_node(array_size,
-                               (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,
-                               node);
+               pages = kmalloc_node(array_size, nested_gfp, node);
        }
        area->pages = pages;
        area->caller = caller;
index 777af57fd8c8c80971d7abaf4edb974e360288c9..885207a6b6b735cb7ca0dd276b907312300b5df1 100644 (file)
@@ -55,6 +55,11 @@ struct scan_control {
        /* Number of pages freed so far during a call to shrink_zones() */
        unsigned long nr_reclaimed;
 
+       /* How many pages shrink_list() should reclaim */
+       unsigned long nr_to_reclaim;
+
+       unsigned long hibernation_mode;
+
        /* This context's GFP mask */
        gfp_t gfp_mask;
 
@@ -66,12 +71,6 @@ struct scan_control {
        /* Can pages be swapped as part of reclaim? */
        int may_swap;
 
-       /* This context's SWAP_CLUSTER_MAX. If freeing memory for
-        * suspend, we effectively ignore SWAP_CLUSTER_MAX.
-        * In this context, it doesn't matter that we scan the
-        * whole list at once. */
-       int swap_cluster_max;
-
        int swappiness;
 
        int all_unreclaimable;
@@ -358,7 +357,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping,
         * stalls if we need to run get_block().  We could test
         * PagePrivate for that.
         *
-        * If this process is currently in generic_file_write() against
+        * If this process is currently in __generic_file_aio_write() against
         * this page's queue, we can perform writeback even if that
         * will block.
         *
@@ -1132,7 +1131,7 @@ static unsigned long shrink_inactive_list(unsigned long max_scan,
                unsigned long nr_anon;
                unsigned long nr_file;
 
-               nr_taken = sc->isolate_pages(sc->swap_cluster_max,
+               nr_taken = sc->isolate_pages(SWAP_CLUSTER_MAX,
                             &page_list, &nr_scan, sc->order, mode,
                                zone, sc->mem_cgroup, 0, file);
 
@@ -1166,10 +1165,8 @@ static unsigned long shrink_inactive_list(unsigned long max_scan,
                __mod_zone_page_state(zone, NR_ISOLATED_ANON, nr_anon);
                __mod_zone_page_state(zone, NR_ISOLATED_FILE, nr_file);
 
-               reclaim_stat->recent_scanned[0] += count[LRU_INACTIVE_ANON];
-               reclaim_stat->recent_scanned[0] += count[LRU_ACTIVE_ANON];
-               reclaim_stat->recent_scanned[1] += count[LRU_INACTIVE_FILE];
-               reclaim_stat->recent_scanned[1] += count[LRU_ACTIVE_FILE];
+               reclaim_stat->recent_scanned[0] += nr_anon;
+               reclaim_stat->recent_scanned[1] += nr_file;
 
                spin_unlock_irq(&zone->lru_lock);
 
@@ -1464,20 +1461,26 @@ static int inactive_file_is_low(struct zone *zone, struct scan_control *sc)
        return low;
 }
 
+static int inactive_list_is_low(struct zone *zone, struct scan_control *sc,
+                               int file)
+{
+       if (file)
+               return inactive_file_is_low(zone, sc);
+       else
+               return inactive_anon_is_low(zone, sc);
+}
+
 static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,
        struct zone *zone, struct scan_control *sc, int priority)
 {
        int file = is_file_lru(lru);
 
-       if (lru == LRU_ACTIVE_FILE && inactive_file_is_low(zone, sc)) {
-               shrink_active_list(nr_to_scan, zone, sc, priority, file);
+       if (is_active_lru(lru)) {
+               if (inactive_list_is_low(zone, sc, file))
+                   shrink_active_list(nr_to_scan, zone, sc, priority, file);
                return 0;
        }
 
-       if (lru == LRU_ACTIVE_ANON && inactive_anon_is_low(zone, sc)) {
-               shrink_active_list(nr_to_scan, zone, sc, priority, file);
-               return 0;
-       }
        return shrink_inactive_list(nr_to_scan, zone, sc, priority, file);
 }
 
@@ -1567,15 +1570,14 @@ static void get_scan_ratio(struct zone *zone, struct scan_control *sc,
  * until we collected @swap_cluster_max pages to scan.
  */
 static unsigned long nr_scan_try_batch(unsigned long nr_to_scan,
-                                      unsigned long *nr_saved_scan,
-                                      unsigned long swap_cluster_max)
+                                      unsigned long *nr_saved_scan)
 {
        unsigned long nr;
 
        *nr_saved_scan += nr_to_scan;
        nr = *nr_saved_scan;
 
-       if (nr >= swap_cluster_max)
+       if (nr >= SWAP_CLUSTER_MAX)
                *nr_saved_scan = 0;
        else
                nr = 0;
@@ -1594,7 +1596,7 @@ static void shrink_zone(int priority, struct zone *zone,
        unsigned long percent[2];       /* anon @ 0; file @ 1 */
        enum lru_list l;
        unsigned long nr_reclaimed = sc->nr_reclaimed;
-       unsigned long swap_cluster_max = sc->swap_cluster_max;
+       unsigned long nr_to_reclaim = sc->nr_to_reclaim;
        struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(zone, sc);
        int noswap = 0;
 
@@ -1616,15 +1618,15 @@ static void shrink_zone(int priority, struct zone *zone,
                        scan = (scan * percent[file]) / 100;
                }
                nr[l] = nr_scan_try_batch(scan,
-                                         &reclaim_stat->nr_saved_scan[l],
-                                         swap_cluster_max);
+                                         &reclaim_stat->nr_saved_scan[l]);
        }
 
        while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
                                        nr[LRU_INACTIVE_FILE]) {
                for_each_evictable_lru(l) {
                        if (nr[l]) {
-                               nr_to_scan = min(nr[l], swap_cluster_max);
+                               nr_to_scan = min_t(unsigned long,
+                                                  nr[l], SWAP_CLUSTER_MAX);
                                nr[l] -= nr_to_scan;
 
                                nr_reclaimed += shrink_list(l, nr_to_scan,
@@ -1639,8 +1641,7 @@ static void shrink_zone(int priority, struct zone *zone,
                 * with multiple processes reclaiming pages, the total
                 * freeing target can get unreasonably large.
                 */
-               if (nr_reclaimed > swap_cluster_max &&
-                       priority < DEF_PRIORITY && !current_is_kswapd())
+               if (nr_reclaimed >= nr_to_reclaim && priority < DEF_PRIORITY)
                        break;
        }
 
@@ -1738,6 +1739,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
        struct zoneref *z;
        struct zone *zone;
        enum zone_type high_zoneidx = gfp_zone(sc->gfp_mask);
+       unsigned long writeback_threshold;
 
        delayacct_freepages_start();
 
@@ -1773,7 +1775,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
                        }
                }
                total_scanned += sc->nr_scanned;
-               if (sc->nr_reclaimed >= sc->swap_cluster_max) {
+               if (sc->nr_reclaimed >= sc->nr_to_reclaim) {
                        ret = sc->nr_reclaimed;
                        goto out;
                }
@@ -1785,14 +1787,15 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
                 * that's undesirable in laptop mode, where we *want* lumpy
                 * writeout.  So in laptop mode, write out the whole world.
                 */
-               if (total_scanned > sc->swap_cluster_max +
-                                       sc->swap_cluster_max / 2) {
+               writeback_threshold = sc->nr_to_reclaim + sc->nr_to_reclaim / 2;
+               if (total_scanned > writeback_threshold) {
                        wakeup_flusher_threads(laptop_mode ? 0 : total_scanned);
                        sc->may_writepage = 1;
                }
 
                /* Take a nap, wait for some writeback to complete */
-               if (sc->nr_scanned && priority < DEF_PRIORITY - 2)
+               if (!sc->hibernation_mode && sc->nr_scanned &&
+                   priority < DEF_PRIORITY - 2)
                        congestion_wait(BLK_RW_ASYNC, HZ/10);
        }
        /* top priority shrink_zones still had more to do? don't OOM, then */
@@ -1831,7 +1834,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
        struct scan_control sc = {
                .gfp_mask = gfp_mask,
                .may_writepage = !laptop_mode,
-               .swap_cluster_max = SWAP_CLUSTER_MAX,
+               .nr_to_reclaim = SWAP_CLUSTER_MAX,
                .may_unmap = 1,
                .may_swap = 1,
                .swappiness = vm_swappiness,
@@ -1855,7 +1858,6 @@ unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
                .may_writepage = !laptop_mode,
                .may_unmap = 1,
                .may_swap = !noswap,
-               .swap_cluster_max = SWAP_CLUSTER_MAX,
                .swappiness = swappiness,
                .order = 0,
                .mem_cgroup = mem,
@@ -1889,7 +1891,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem_cont,
                .may_writepage = !laptop_mode,
                .may_unmap = 1,
                .may_swap = !noswap,
-               .swap_cluster_max = SWAP_CLUSTER_MAX,
+               .nr_to_reclaim = SWAP_CLUSTER_MAX,
                .swappiness = swappiness,
                .order = 0,
                .mem_cgroup = mem_cont,
@@ -1904,6 +1906,30 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem_cont,
 }
 #endif
 
+/* is kswapd sleeping prematurely? */
+static int sleeping_prematurely(pg_data_t *pgdat, int order, long remaining)
+{
+       int i;
+
+       /* If a direct reclaimer woke kswapd within HZ/10, it's premature */
+       if (remaining)
+               return 1;
+
+       /* If after HZ/10, a zone is below the high mark, it's premature */
+       for (i = 0; i < pgdat->nr_zones; i++) {
+               struct zone *zone = pgdat->node_zones + i;
+
+               if (!populated_zone(zone))
+                       continue;
+
+               if (!zone_watermark_ok(zone, order, high_wmark_pages(zone),
+                                                               0, 0))
+                       return 1;
+       }
+
+       return 0;
+}
+
 /*
  * For kswapd, balance_pgdat() will work across all this node's zones until
  * they are all at high_wmark_pages(zone).
@@ -1936,7 +1962,11 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order)
                .gfp_mask = GFP_KERNEL,
                .may_unmap = 1,
                .may_swap = 1,
-               .swap_cluster_max = SWAP_CLUSTER_MAX,
+               /*
+                * kswapd doesn't want to be bailed out while reclaim. because
+                * we want to put equal scanning pressure on each zone.
+                */
+               .nr_to_reclaim = ULONG_MAX,
                .swappiness = vm_swappiness,
                .order = order,
                .mem_cgroup = NULL,
@@ -1961,6 +1991,7 @@ loop_again:
        for (priority = DEF_PRIORITY; priority >= 0; priority--) {
                int end_zone = 0;       /* Inclusive.  0 = ZONE_DMA */
                unsigned long lru_pages = 0;
+               int has_under_min_watermark_zone = 0;
 
                /* The swap token gets in the way of swapout... */
                if (!priority)
@@ -2067,6 +2098,15 @@ loop_again:
                        if (total_scanned > SWAP_CLUSTER_MAX * 2 &&
                            total_scanned > sc.nr_reclaimed + sc.nr_reclaimed / 2)
                                sc.may_writepage = 1;
+
+                       /*
+                        * We are still under min water mark. it mean we have
+                        * GFP_ATOMIC allocation failure risk. Hurry up!
+                        */
+                       if (!zone_watermark_ok(zone, order, min_wmark_pages(zone),
+                                             end_zone, 0))
+                               has_under_min_watermark_zone = 1;
+
                }
                if (all_zones_ok)
                        break;          /* kswapd: all done */
@@ -2074,8 +2114,12 @@ loop_again:
                 * OK, kswapd is getting into trouble.  Take a nap, then take
                 * another pass across the zones.
                 */
-               if (total_scanned && priority < DEF_PRIORITY - 2)
-                       congestion_wait(BLK_RW_ASYNC, HZ/10);
+               if (total_scanned && (priority < DEF_PRIORITY - 2)) {
+                       if (has_under_min_watermark_zone)
+                               count_vm_event(KSWAPD_SKIP_CONGESTION_WAIT);
+                       else
+                               congestion_wait(BLK_RW_ASYNC, HZ/10);
+               }
 
                /*
                 * We do this so kswapd doesn't build up large priorities for
@@ -2173,6 +2217,7 @@ static int kswapd(void *p)
        order = 0;
        for ( ; ; ) {
                unsigned long new_order;
+               int ret;
 
                prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
                new_order = pgdat->kswapd_max_order;
@@ -2184,19 +2229,45 @@ static int kswapd(void *p)
                         */
                        order = new_order;
                } else {
-                       if (!freezing(current))
-                               schedule();
+                       if (!freezing(current) && !kthread_should_stop()) {
+                               long remaining = 0;
+
+                               /* Try to sleep for a short interval */
+                               if (!sleeping_prematurely(pgdat, order, remaining)) {
+                                       remaining = schedule_timeout(HZ/10);
+                                       finish_wait(&pgdat->kswapd_wait, &wait);
+                                       prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
+                               }
+
+                               /*
+                                * After a short sleep, check if it was a
+                                * premature sleep. If not, then go fully
+                                * to sleep until explicitly woken up
+                                */
+                               if (!sleeping_prematurely(pgdat, order, remaining))
+                                       schedule();
+                               else {
+                                       if (remaining)
+                                               count_vm_event(KSWAPD_LOW_WMARK_HIT_QUICKLY);
+                                       else
+                                               count_vm_event(KSWAPD_HIGH_WMARK_HIT_QUICKLY);
+                               }
+                       }
 
                        order = pgdat->kswapd_max_order;
                }
                finish_wait(&pgdat->kswapd_wait, &wait);
 
-               if (!try_to_freeze()) {
-                       /* We can speed up thawing tasks if we don't call
-                        * balance_pgdat after returning from the refrigerator
-                        */
+               ret = try_to_freeze();
+               if (kthread_should_stop())
+                       break;
+
+               /*
+                * We can speed up thawing tasks if we don't call balance_pgdat
+                * after returning from the refrigerator
+                */
+               if (!ret)
                        balance_pgdat(pgdat, order);
-               }
        }
        return 0;
 }
@@ -2260,148 +2331,43 @@ unsigned long zone_reclaimable_pages(struct zone *zone)
 
 #ifdef CONFIG_HIBERNATION
 /*
- * Helper function for shrink_all_memory().  Tries to reclaim 'nr_pages' pages
- * from LRU lists system-wide, for given pass and priority.
- *
- * For pass > 3 we also try to shrink the LRU lists that contain a few pages
- */
-static void shrink_all_zones(unsigned long nr_pages, int prio,
-                                     int pass, struct scan_control *sc)
-{
-       struct zone *zone;
-       unsigned long nr_reclaimed = 0;
-       struct zone_reclaim_stat *reclaim_stat;
-
-       for_each_populated_zone(zone) {
-               enum lru_list l;
-
-               if (zone_is_all_unreclaimable(zone) && prio != DEF_PRIORITY)
-                       continue;
-
-               for_each_evictable_lru(l) {
-                       enum zone_stat_item ls = NR_LRU_BASE + l;
-                       unsigned long lru_pages = zone_page_state(zone, ls);
-
-                       /* For pass = 0, we don't shrink the active list */
-                       if (pass == 0 && (l == LRU_ACTIVE_ANON ||
-                                               l == LRU_ACTIVE_FILE))
-                               continue;
-
-                       reclaim_stat = get_reclaim_stat(zone, sc);
-                       reclaim_stat->nr_saved_scan[l] +=
-                                               (lru_pages >> prio) + 1;
-                       if (reclaim_stat->nr_saved_scan[l]
-                                               >= nr_pages || pass > 3) {
-                               unsigned long nr_to_scan;
-
-                               reclaim_stat->nr_saved_scan[l] = 0;
-                               nr_to_scan = min(nr_pages, lru_pages);
-                               nr_reclaimed += shrink_list(l, nr_to_scan, zone,
-                                                               sc, prio);
-                               if (nr_reclaimed >= nr_pages) {
-                                       sc->nr_reclaimed += nr_reclaimed;
-                                       return;
-                               }
-                       }
-               }
-       }
-       sc->nr_reclaimed += nr_reclaimed;
-}
-
-/*
- * Try to free `nr_pages' of memory, system-wide, and return the number of
+ * Try to free `nr_to_reclaim' of memory, system-wide, and return the number of
  * freed pages.
  *
  * Rather than trying to age LRUs the aim is to preserve the overall
  * LRU order by reclaiming preferentially
  * inactive > active > active referenced > active mapped
  */
-unsigned long shrink_all_memory(unsigned long nr_pages)
+unsigned long shrink_all_memory(unsigned long nr_to_reclaim)
 {
-       unsigned long lru_pages, nr_slab;
-       int pass;
        struct reclaim_state reclaim_state;
        struct scan_control sc = {
-               .gfp_mask = GFP_KERNEL,
-               .may_unmap = 0,
+               .gfp_mask = GFP_HIGHUSER_MOVABLE,
+               .may_swap = 1,
+               .may_unmap = 1,
                .may_writepage = 1,
+               .nr_to_reclaim = nr_to_reclaim,
+               .hibernation_mode = 1,
+               .swappiness = vm_swappiness,
+               .order = 0,
                .isolate_pages = isolate_pages_global,
-               .nr_reclaimed = 0,
        };
+       struct zonelist * zonelist = node_zonelist(numa_node_id(), sc.gfp_mask);
+       struct task_struct *p = current;
+       unsigned long nr_reclaimed;
 
-       current->reclaim_state = &reclaim_state;
-
-       lru_pages = global_reclaimable_pages();
-       nr_slab = global_page_state(NR_SLAB_RECLAIMABLE);
-       /* If slab caches are huge, it's better to hit them first */
-       while (nr_slab >= lru_pages) {
-               reclaim_state.reclaimed_slab = 0;
-               shrink_slab(nr_pages, sc.gfp_mask, lru_pages);
-               if (!reclaim_state.reclaimed_slab)
-                       break;
-
-               sc.nr_reclaimed += reclaim_state.reclaimed_slab;
-               if (sc.nr_reclaimed >= nr_pages)
-                       goto out;
-
-               nr_slab -= reclaim_state.reclaimed_slab;
-       }
-
-       /*
-        * We try to shrink LRUs in 5 passes:
-        * 0 = Reclaim from inactive_list only
-        * 1 = Reclaim from active list but don't reclaim mapped
-        * 2 = 2nd pass of type 1
-        * 3 = Reclaim mapped (normal reclaim)
-        * 4 = 2nd pass of type 3
-        */
-       for (pass = 0; pass < 5; pass++) {
-               int prio;
-
-               /* Force reclaiming mapped pages in the passes #3 and #4 */
-               if (pass > 2)
-                       sc.may_unmap = 1;
-
-               for (prio = DEF_PRIORITY; prio >= 0; prio--) {
-                       unsigned long nr_to_scan = nr_pages - sc.nr_reclaimed;
-
-                       sc.nr_scanned = 0;
-                       sc.swap_cluster_max = nr_to_scan;
-                       shrink_all_zones(nr_to_scan, prio, pass, &sc);
-                       if (sc.nr_reclaimed >= nr_pages)
-                               goto out;
-
-                       reclaim_state.reclaimed_slab = 0;
-                       shrink_slab(sc.nr_scanned, sc.gfp_mask,
-                                   global_reclaimable_pages());
-                       sc.nr_reclaimed += reclaim_state.reclaimed_slab;
-                       if (sc.nr_reclaimed >= nr_pages)
-                               goto out;
-
-                       if (sc.nr_scanned && prio < DEF_PRIORITY - 2)
-                               congestion_wait(BLK_RW_ASYNC, HZ / 10);
-               }
-       }
-
-       /*
-        * If sc.nr_reclaimed = 0, we could not shrink LRUs, but there may be
-        * something in slab caches
-        */
-       if (!sc.nr_reclaimed) {
-               do {
-                       reclaim_state.reclaimed_slab = 0;
-                       shrink_slab(nr_pages, sc.gfp_mask,
-                                   global_reclaimable_pages());
-                       sc.nr_reclaimed += reclaim_state.reclaimed_slab;
-               } while (sc.nr_reclaimed < nr_pages &&
-                               reclaim_state.reclaimed_slab > 0);
-       }
+       p->flags |= PF_MEMALLOC;
+       lockdep_set_current_reclaim_state(sc.gfp_mask);
+       reclaim_state.reclaimed_slab = 0;
+       p->reclaim_state = &reclaim_state;
 
+       nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
 
-out:
-       current->reclaim_state = NULL;
+       p->reclaim_state = NULL;
+       lockdep_clear_current_reclaim_state();
+       p->flags &= ~PF_MEMALLOC;
 
-       return sc.nr_reclaimed;
+       return nr_reclaimed;
 }
 #endif /* CONFIG_HIBERNATION */
 
@@ -2451,6 +2417,17 @@ int kswapd_run(int nid)
        return ret;
 }
 
+/*
+ * Called by memory hotplug when all memory in a node is offlined.
+ */
+void kswapd_stop(int nid)
+{
+       struct task_struct *kswapd = NODE_DATA(nid)->kswapd;
+
+       if (kswapd)
+               kthread_stop(kswapd);
+}
+
 static int __init kswapd_init(void)
 {
        int nid;
@@ -2553,8 +2530,8 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
                .may_writepage = !!(zone_reclaim_mode & RECLAIM_WRITE),
                .may_unmap = !!(zone_reclaim_mode & RECLAIM_SWAP),
                .may_swap = 1,
-               .swap_cluster_max = max_t(unsigned long, nr_pages,
-                                       SWAP_CLUSTER_MAX),
+               .nr_to_reclaim = max_t(unsigned long, nr_pages,
+                                      SWAP_CLUSTER_MAX),
                .gfp_mask = gfp_mask,
                .swappiness = vm_swappiness,
                .order = order,
index dad2327e45804e16a8ff07564a80eb0cac79f482..6051fbab67ba26533594103c62790f779cd21c08 100644 (file)
@@ -683,6 +683,9 @@ static const char * const vmstat_text[] = {
        "slabs_scanned",
        "kswapd_steal",
        "kswapd_inodesteal",
+       "kswapd_low_wmark_hit_quickly",
+       "kswapd_high_wmark_hit_quickly",
+       "kswapd_skip_congestion_wait",
        "pageoutrun",
        "allocstall",
 
index b001c361ad30976ff3a1de5bfe80fe2f0142b6f9..4300df35d37d0fee2796aa58c53467efa05b7799 100644 (file)
 #include <linux/poll.h>
 #include <linux/capability.h>
 #include <linux/ctype.h>       /* isspace() */
+#include <linux/string.h>      /* skip_spaces() */
 #include <asm/uaccess.h>
 #include <linux/init.h>
 
index 7dea882dbb750649b10616f00dcb9bb68697ec64..156020d138b507685633feda14fa747d11c90538 100644 (file)
@@ -76,9 +76,8 @@ irnet_ctrl_write(irnet_socket *       ap,
       /* Look at the next command */
       start = next;
 
-      /* Scrap whitespaces before the command */
-      while(isspace(*start))
-       start++;
+       /* Scrap whitespaces before the command */
+       start = skip_spaces(start);
 
       /* ',' is our command separator */
       next = strchr(start, ',');
@@ -133,8 +132,7 @@ irnet_ctrl_write(irnet_socket *     ap,
              char *    endp;
 
              /* Scrap whitespaces before the command */
-             while(isspace(*begp))
-               begp++;
+             begp = skip_spaces(begp);
 
              /* Convert argument to a number (last arg is the base) */
              addr = simple_strtoul(begp, &endp, 16);
index 1e428863574fe280966af5f04755f87bc1a4887f..c18286a2167b9987f15dd0f765d1ed8e2aa1625b 100644 (file)
@@ -221,7 +221,7 @@ static int afiucv_pm_restore_thaw(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops afiucv_pm_ops = {
+static const struct dev_pm_ops afiucv_pm_ops = {
        .prepare = afiucv_pm_prepare,
        .complete = afiucv_pm_complete,
        .freeze = afiucv_pm_freeze,
index 3b1f5f5f8de717e79c67576df739f604ea7c3dcf..fd8b28361a6415eaf4d98251a7be40fbc478f7bc 100644 (file)
@@ -93,7 +93,7 @@ static int iucv_pm_freeze(struct device *);
 static int iucv_pm_thaw(struct device *);
 static int iucv_pm_restore(struct device *);
 
-static struct dev_pm_ops iucv_pm_ops = {
+static const struct dev_pm_ops iucv_pm_ops = {
        .prepare = iucv_pm_prepare,
        .complete = iucv_pm_complete,
        .freeze = iucv_pm_freeze,
index eb0ceb8465270d355d44842b1b82e91e41b6830e..fc70a49c0afd5a4effb35d67eaa4ddf2137d4d44 100644 (file)
@@ -482,8 +482,7 @@ static ssize_t recent_old_proc_write(struct file *file,
        if (copy_from_user(buf, input, size))
                return -EFAULT;
 
-       while (isspace(*c))
-               c++;
+       c = skip_spaces(c);
 
        if (size - (c - buf) < 5)
                return c - buf;
index 81a67a458e78ddd494343e5746a195ef4e821440..445e8845f0a4c06ee5109e63471bab7769d2f3d5 100755 (executable)
@@ -13,7 +13,7 @@
 use strict;
 
 my $P = $0;
-my $V = '0.21';
+my $V = '0.23';
 
 use Getopt::Long qw(:config no_auto_abbrev);
 
@@ -23,16 +23,19 @@ my $email_usename = 1;
 my $email_maintainer = 1;
 my $email_list = 1;
 my $email_subscriber_list = 0;
-my $email_git = 1;
 my $email_git_penguin_chiefs = 0;
+my $email_git = 1;
+my $email_git_blame = 0;
 my $email_git_min_signatures = 1;
 my $email_git_max_maintainers = 5;
 my $email_git_min_percent = 5;
 my $email_git_since = "1-year-ago";
-my $email_git_blame = 0;
+my $email_hg_since = "-365";
 my $email_remove_duplicates = 1;
 my $output_multiline = 1;
 my $output_separator = ", ";
+my $output_roles = 0;
+my $output_rolestats = 0;
 my $scm = 0;
 my $web = 0;
 my $subsystem = 0;
@@ -64,21 +67,52 @@ my $penguin_chiefs = "\(" . join("|",@penguin_chief_names) . "\)";
 my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
 my $rfc822_char = '[\\000-\\377]';
 
+# VCS command support: class-like functions and strings
+
+my %VCS_cmds;
+
+my %VCS_cmds_git = (
+    "execute_cmd" => \&git_execute_cmd,
+    "available" => '(which("git") ne "") && (-d ".git")',
+    "find_signers_cmd" => "git log --since=\$email_git_since -- \$file",
+    "find_commit_signers_cmd" => "git log -1 \$commit",
+    "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
+    "blame_file_cmd" => "git blame -l \$file",
+    "commit_pattern" => "^commit [0-9a-f]{40,40}",
+    "blame_commit_pattern" => "^([0-9a-f]+) "
+);
+
+my %VCS_cmds_hg = (
+    "execute_cmd" => \&hg_execute_cmd,
+    "available" => '(which("hg") ne "") && (-d ".hg")',
+    "find_signers_cmd" =>
+       "hg log --date=\$email_hg_since" .
+               " --template='commit {node}\\n{desc}\\n' -- \$file",
+    "find_commit_signers_cmd" => "hg log --template='{desc}\\n' -r \$commit",
+    "blame_range_cmd" => "",           # not supported
+    "blame_file_cmd" => "hg blame -c \$file",
+    "commit_pattern" => "^commit [0-9a-f]{40,40}",
+    "blame_commit_pattern" => "^([0-9a-f]+):"
+);
+
 if (!GetOptions(
                'email!' => \$email,
                'git!' => \$email_git,
+               'git-blame!' => \$email_git_blame,
                'git-chief-penguins!' => \$email_git_penguin_chiefs,
                'git-min-signatures=i' => \$email_git_min_signatures,
                'git-max-maintainers=i' => \$email_git_max_maintainers,
                'git-min-percent=i' => \$email_git_min_percent,
                'git-since=s' => \$email_git_since,
-               'git-blame!' => \$email_git_blame,
+               'hg-since=s' => \$email_hg_since,
                'remove-duplicates!' => \$email_remove_duplicates,
                'm!' => \$email_maintainer,
                'n!' => \$email_usename,
                'l!' => \$email_list,
                's!' => \$email_subscriber_list,
                'multiline!' => \$output_multiline,
+               'roles!' => \$output_roles,
+               'rolestats!' => \$output_rolestats,
                'separator=s' => \$output_separator,
                'subsystem!' => \$subsystem,
                'status!' => \$status,
@@ -90,8 +124,7 @@ if (!GetOptions(
                'v|version' => \$version,
                'h|help' => \$help,
                )) {
-    usage();
-    die "$P: invalid argument\n";
+    die "$P: invalid argument - use --help if necessary\n";
 }
 
 if ($help != 0) {
@@ -113,6 +146,10 @@ if ($output_separator ne ", ") {
     $output_multiline = 0;
 }
 
+if ($output_rolestats) {
+    $output_roles = 1;
+}
+
 my $selections = $email + $scm + $status + $subsystem + $web;
 if ($selections == 0) {
     usage();
@@ -175,7 +212,7 @@ if ($email_remove_duplicates) {
        next if ($line =~ m/^\s*$/);
 
        my ($name, $address) = parse_email($line);
-       $line = format_email($name, $address);
+       $line = format_email($name, $address, $email_usename);
 
        next if ($line =~ m/^\s*$/);
 
@@ -207,12 +244,10 @@ foreach my $file (@ARGV) {
        push(@files, $file);
        if (-f $file && $keywords) {
            open(FILE, "<$file") or die "$P: Can't open ${file}\n";
-           while (<FILE>) {
-               my $patch_line = $_;
-               foreach my $line (keys %keyword_hash) {
-                   if ($patch_line =~ m/^.*$keyword_hash{$line}/x) {
-                       push(@keyword_tvi, $line);
-                   }
+           my $text = do { local($/) ; <FILE> };
+           foreach my $line (keys %keyword_hash) {
+               if ($text =~ m/$keyword_hash{$line}/x) {
+                   push(@keyword_tvi, $line);
                }
            }
            close(FILE);
@@ -304,11 +339,11 @@ foreach my $file (@files) {
     }
 
     if ($email && $email_git) {
-       recent_git_signoffs($file);
+       vcs_file_signoffs($file);
     }
 
     if ($email && $email_git_blame) {
-       git_assign_blame($file);
+       vcs_file_blame($file);
     }
 }
 
@@ -324,11 +359,11 @@ if ($email) {
        if ($chief =~ m/^(.*):(.*)/) {
            my $email_address;
 
-           $email_address = format_email($1, $2);
+           $email_address = format_email($1, $2, $email_usename);
            if ($email_git_penguin_chiefs) {
-               push(@email_to, $email_address);
+               push(@email_to, [$email_address, 'chief penguin']);
            } else {
-               @email_to = grep(!/${email_address}/, @email_to);
+               @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
            }
        }
     }
@@ -342,7 +377,7 @@ if ($email || $email_list) {
     if ($email_list) {
        @to = (@to, @list_to);
     }
-    output(uniq(@to));
+    output(merge_email(@to));
 }
 
 if ($scm) {
@@ -398,13 +433,16 @@ MAINTAINER field selection options:
     --git-min-signatures => number of signatures required (default: 1)
     --git-max-maintainers => maximum maintainers to add (default: 5)
     --git-min-percent => minimum percentage of commits required (default: 5)
-    --git-since => git history to use (default: 1-year-ago)
     --git-blame => use git blame to find modified commits for patch or file
+    --git-since => git history to use (default: 1-year-ago)
+    --hg-since => hg history to use (default: -365)
     --m => include maintainer(s) if any
     --n => include name 'Full Name <addr\@domain.tld>'
     --l => include list(s) if any
     --s => include subscriber only list(s) if any
     --remove-duplicates => minimize duplicate email names/addresses
+    --roles => show roles (status:subsystem, git-signer, list, etc...)
+    --rolestats => show roles and statistics (commits/total_commits, %)
   --scm => print SCM tree(s) if any
   --status => print status if any
   --subsystem => print subsystem name if any
@@ -430,11 +468,24 @@ Notes:
           directory are examined as git recurses directories.
           Any specified X: (exclude) pattern matches are _not_ ignored.
       Used with "--nogit", directory is used as a pattern match,
-         no individual file within the directory or subdirectory
-         is matched.
+          no individual file within the directory or subdirectory
+          is matched.
       Used with "--git-blame", does not iterate all files in directory
   Using "--git-blame" is slow and may add old committers and authors
       that are no longer active maintainers to the output.
+  Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
+      other automated tools that expect only ["name"] <email address>
+      may not work because of additional output after <email address>.
+  Using "--rolestats" and "--git-blame" shows the #/total=% commits,
+      not the percentage of the entire file authored.  # of commits is
+      not a good measure of amount of code authored.  1 major commit may
+      contain a thousand lines, 5 trivial commits may modify a single line.
+  If git is not installed, but mercurial (hg) is installed and an .hg
+      repository exists, the following options apply to mercurial:
+          --git,
+          --git-min-signatures, --git-max-maintainers, --git-min-percent, and
+          --git-blame
+      Use --hg-since not --git-since to control date selection
 EOT
 }
 
@@ -493,7 +544,7 @@ sub parse_email {
 }
 
 sub format_email {
-    my ($name, $address) = @_;
+    my ($name, $address, $usename) = @_;
 
     my $formatted_email;
 
@@ -506,11 +557,11 @@ sub format_email {
        $name = "\"$name\"";
     }
 
-    if ($email_usename) {
+    if ($usename) {
        if ("$name" eq "") {
            $formatted_email = "$address";
        } else {
-           $formatted_email = "$name <${address}>";
+           $formatted_email = "$name <$address>";
        }
     } else {
        $formatted_email = $address;
@@ -547,6 +598,71 @@ sub find_ending_index {
     return $index;
 }
 
+sub get_maintainer_role {
+    my ($index) = @_;
+
+    my $i;
+    my $start = find_starting_index($index);
+    my $end = find_ending_index($index);
+
+    my $role;
+    my $subsystem = $typevalue[$start];
+    if (length($subsystem) > 20) {
+       $subsystem = substr($subsystem, 0, 17);
+       $subsystem =~ s/\s*$//;
+       $subsystem = $subsystem . "...";
+    }
+
+    for ($i = $start + 1; $i < $end; $i++) {
+       my $tv = $typevalue[$i];
+       if ($tv =~ m/^(\C):\s*(.*)/) {
+           my $ptype = $1;
+           my $pvalue = $2;
+           if ($ptype eq "S") {
+               $role = $pvalue;
+           }
+       }
+    }
+
+    $role = lc($role);
+    if      ($role eq "supported") {
+       $role = "supporter";
+    } elsif ($role eq "maintained") {
+       $role = "maintainer";
+    } elsif ($role eq "odd fixes") {
+       $role = "odd fixer";
+    } elsif ($role eq "orphan") {
+       $role = "orphan minder";
+    } elsif ($role eq "obsolete") {
+       $role = "obsolete minder";
+    } elsif ($role eq "buried alive in reporters") {
+       $role = "chief penguin";
+    }
+
+    return $role . ":" . $subsystem;
+}
+
+sub get_list_role {
+    my ($index) = @_;
+
+    my $i;
+    my $start = find_starting_index($index);
+    my $end = find_ending_index($index);
+
+    my $subsystem = $typevalue[$start];
+    if (length($subsystem) > 20) {
+       $subsystem = substr($subsystem, 0, 17);
+       $subsystem =~ s/\s*$//;
+       $subsystem = $subsystem . "...";
+    }
+
+    if ($subsystem eq "THE REST") {
+       $subsystem = "";
+    }
+
+    return $subsystem;
+}
+
 sub add_categories {
     my ($index) = @_;
 
@@ -564,17 +680,22 @@ sub add_categories {
            if ($ptype eq "L") {
                my $list_address = $pvalue;
                my $list_additional = "";
+               my $list_role = get_list_role($i);
+
+               if ($list_role ne "") {
+                   $list_role = ":" . $list_role;
+               }
                if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
                    $list_address = $1;
                    $list_additional = $2;
                }
                if ($list_additional =~ m/subscribers-only/) {
                    if ($email_subscriber_list) {
-                       push(@list_to, $list_address);
+                       push(@list_to, [$list_address, "subscriber list${list_role}"]);
                    }
                } else {
                    if ($email_list) {
-                       push(@list_to, $list_address);
+                       push(@list_to, [$list_address, "open list${list_role}"]);
                    }
                }
            } elsif ($ptype eq "M") {
@@ -585,13 +706,14 @@ sub add_categories {
                        if ($tv =~ m/^(\C):\s*(.*)/) {
                            if ($1 eq "P") {
                                $name = $2;
-                               $pvalue = format_email($name, $address);
+                               $pvalue = format_email($name, $address, $email_usename);
                            }
                        }
                    }
                }
                if ($email_maintainer) {
-                   push_email_addresses($pvalue);
+                   my $role = get_maintainer_role($i);
+                   push_email_addresses($pvalue, $role);
                }
            } elsif ($ptype eq "T") {
                push(@scm, $pvalue);
@@ -618,7 +740,7 @@ sub email_inuse {
 }
 
 sub push_email_address {
-    my ($line) = @_;
+    my ($line, $role) = @_;
 
     my ($name, $address) = parse_email($line);
 
@@ -627,9 +749,9 @@ sub push_email_address {
     }
 
     if (!$email_remove_duplicates) {
-       push(@email_to, format_email($name, $address));
+       push(@email_to, [format_email($name, $address, $email_usename), $role]);
     } elsif (!email_inuse($name, $address)) {
-       push(@email_to, format_email($name, $address));
+       push(@email_to, [format_email($name, $address, $email_usename), $role]);
        $email_hash_name{$name}++;
        $email_hash_address{$address}++;
     }
@@ -638,24 +760,52 @@ sub push_email_address {
 }
 
 sub push_email_addresses {
-    my ($address) = @_;
+    my ($address, $role) = @_;
 
     my @address_list = ();
 
     if (rfc822_valid($address)) {
-       push_email_address($address);
+       push_email_address($address, $role);
     } elsif (@address_list = rfc822_validlist($address)) {
        my $array_count = shift(@address_list);
        while (my $entry = shift(@address_list)) {
-           push_email_address($entry);
+           push_email_address($entry, $role);
        }
     } else {
-       if (!push_email_address($address)) {
+       if (!push_email_address($address, $role)) {
            warn("Invalid MAINTAINERS address: '" . $address . "'\n");
        }
     }
 }
 
+sub add_role {
+    my ($line, $role) = @_;
+
+    my ($name, $address) = parse_email($line);
+    my $email = format_email($name, $address, $email_usename);
+
+    foreach my $entry (@email_to) {
+       if ($email_remove_duplicates) {
+           my ($entry_name, $entry_address) = parse_email($entry->[0]);
+           if ($name eq $entry_name || $address eq $entry_address) {
+               if ($entry->[1] eq "") {
+                   $entry->[1] = "$role";
+               } else {
+                   $entry->[1] = "$entry->[1],$role";
+               }
+           }
+       } else {
+           if ($email eq $entry->[0]) {
+               if ($entry->[1] eq "") {
+                   $entry->[1] = "$role";
+               } else {
+                   $entry->[1] = "$entry->[1],$role";
+               }
+           }
+       }
+    }
+}
+
 sub which {
     my ($bin) = @_;
 
@@ -669,7 +819,7 @@ sub which {
 }
 
 sub mailmap {
-    my @lines = @_;
+    my (@lines) = @_;
     my %hash;
 
     foreach my $line (@lines) {
@@ -678,14 +828,14 @@ sub mailmap {
            $hash{$name} = $address;
        } elsif ($address ne $hash{$name}) {
            $address = $hash{$name};
-           $line = format_email($name, $address);
+           $line = format_email($name, $address, $email_usename);
        }
        if (exists($mailmap{$name})) {
            my $obj = $mailmap{$name};
            foreach my $map_address (@$obj) {
                if (($map_address eq $address) &&
                    ($map_address ne $hash{$name})) {
-                   $line = format_email($name, $hash{$name});
+                   $line = format_email($name, $hash{$name}, $email_usename);
                }
            }
        }
@@ -694,34 +844,38 @@ sub mailmap {
     return @lines;
 }
 
-sub recent_git_signoffs {
-    my ($file) = @_;
-
-    my $sign_offs = "";
-    my $cmd = "";
-    my $output = "";
-    my $count = 0;
+sub git_execute_cmd {
+    my ($cmd) = @_;
     my @lines = ();
-    my %hash;
-    my $total_sign_offs;
 
-    if (which("git") eq "") {
-       warn("$P: git not found.  Add --nogit to options?\n");
-       return;
-    }
-    if (!(-d ".git")) {
-       warn("$P: .git directory not found.  Use a git repository for better results.\n");
-       warn("$P: perhaps 'git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git'\n");
-       return;
-    }
+    my $output = `$cmd`;
+    $output =~ s/^\s*//gm;
+    @lines = split("\n", $output);
 
-    $cmd = "git log --since=${email_git_since} -- ${file}";
+    return @lines;
+}
 
-    $output = `${cmd}`;
-    $output =~ s/^\s*//gm;
+sub hg_execute_cmd {
+    my ($cmd) = @_;
+    my @lines = ();
 
+    my $output = `$cmd`;
     @lines = split("\n", $output);
 
+    return @lines;
+}
+
+sub vcs_find_signers {
+    my ($cmd) = @_;
+    my @lines = ();
+    my $commits;
+
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    my $pattern = $VCS_cmds{"commit_pattern"};
+
+    $commits = grep(/$pattern/, @lines);       # of commits
+
     @lines = grep(/^[-_        a-z]+by:.*\@.*$/i, @lines);
     if (!$email_git_penguin_chiefs) {
        @lines = grep(!/${penguin_chiefs}/i, @lines);
@@ -729,111 +883,183 @@ sub recent_git_signoffs {
     # cut -f2- -d":"
     s/.*:\s*(.+)\s*/$1/ for (@lines);
 
-    $total_sign_offs = @lines;
+## Reformat email addresses (with names) to avoid badly written signatures
 
-    if ($email_remove_duplicates) {
-       @lines = mailmap(@lines);
+    foreach my $line (@lines) {
+       my ($name, $address) = parse_email($line);
+       $line = format_email($name, $address, 1);
     }
 
-    @lines = sort(@lines);
-
-    # uniq -c
-    $hash{$_}++ for @lines;
-
-    # sort -rn
-    foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
-       my $sign_offs = $hash{$line};
-       $count++;
-       last if ($sign_offs < $email_git_min_signatures ||
-                $count > $email_git_max_maintainers ||
-                $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
-       push_email_address($line);
-    }
+    return ($commits, @lines);
 }
 
-sub save_commits {
-    my ($cmd, @commits) = @_;
-    my $output;
+sub vcs_save_commits {
+    my ($cmd) = @_;
     my @lines = ();
+    my @commits = ();
 
-    $output = `${cmd}`;
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
 
-    @lines = split("\n", $output);
     foreach my $line (@lines) {
-       if ($line =~ m/^(\w+) /) {
-           push (@commits, $1);
+       if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
+           push(@commits, $1);
        }
     }
+
     return @commits;
 }
 
-sub git_assign_blame {
+sub vcs_blame {
     my ($file) = @_;
-
-    my @lines = ();
-    my @commits = ();
     my $cmd;
-    my $output;
-    my %hash;
-    my $total_sign_offs;
-    my $count;
+    my @commits = ();
+
+    return @commits if (!(-f $file));
+
+    if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
+       my @all_commits = ();
+
+       $cmd = $VCS_cmds{"blame_file_cmd"};
+       $cmd =~ s/(\$\w+)/$1/eeg;               #interpolate $cmd
+       @all_commits = vcs_save_commits($cmd);
 
-    if (@range) {
        foreach my $file_range_diff (@range) {
            next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
            my $diff_file = $1;
            my $diff_start = $2;
            my $diff_length = $3;
-           next if (!("$file" eq "$diff_file"));
-           $cmd = "git blame -l -L $diff_start,+$diff_length $file";
-           @commits = save_commits($cmd, @commits);
+           next if ("$file" ne "$diff_file");
+           for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
+               push(@commits, $all_commits[$i]);
+           }
        }
-    } else {
-       if (-f $file) {
-           $cmd = "git blame -l $file";
-           @commits = save_commits($cmd, @commits);
+    } elsif (@range) {
+       foreach my $file_range_diff (@range) {
+           next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
+           my $diff_file = $1;
+           my $diff_start = $2;
+           my $diff_length = $3;
+           next if ("$file" ne "$diff_file");
+           $cmd = $VCS_cmds{"blame_range_cmd"};
+           $cmd =~ s/(\$\w+)/$1/eeg;           #interpolate $cmd
+           push(@commits, vcs_save_commits($cmd));
        }
+    } else {
+       $cmd = $VCS_cmds{"blame_file_cmd"};
+       $cmd =~ s/(\$\w+)/$1/eeg;               #interpolate $cmd
+       @commits = vcs_save_commits($cmd);
     }
 
-    $total_sign_offs = 0;
-    @commits = uniq(@commits);
-    foreach my $commit (@commits) {
-       $cmd = "git log -1 ${commit}";
+    return @commits;
+}
 
-       $output = `${cmd}`;
-       $output =~ s/^\s*//gm;
-       @lines = split("\n", $output);
+my $printed_novcs = 0;
+sub vcs_exists {
+    %VCS_cmds = %VCS_cmds_git;
+    return 1 if eval $VCS_cmds{"available"};
+    %VCS_cmds = %VCS_cmds_hg;
+    return 1 if eval $VCS_cmds{"available"};
+    %VCS_cmds = ();
+    if (!$printed_novcs) {
+       warn("$P: No supported VCS found.  Add --nogit to options?\n");
+       warn("Using a git repository produces better results.\n");
+       warn("Try Linus Torvalds' latest git repository using:\n");
+       warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git\n");
+       $printed_novcs = 1;
+    }
+    return 0;
+}
 
-       @lines = grep(/^[-_     a-z]+by:.*\@.*$/i, @lines);
-       if (!$email_git_penguin_chiefs) {
-           @lines = grep(!/${penguin_chiefs}/i, @lines);
-       }
+sub vcs_assign {
+    my ($role, $divisor, @lines) = @_;
 
-       # cut -f2- -d":"
-       s/.*:\s*(.+)\s*/$1/ for (@lines);
+    my %hash;
+    my $count = 0;
 
-       $total_sign_offs += @lines;
+    return if (@lines <= 0);
 
-       if ($email_remove_duplicates) {
-           @lines = mailmap(@lines);
-       }
+    if ($divisor <= 0) {
+       warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
+       $divisor = 1;
+    }
 
-       $hash{$_}++ for @lines;
+    if ($email_remove_duplicates) {
+       @lines = mailmap(@lines);
     }
 
-    $count = 0;
+    @lines = sort(@lines);
+
+    # uniq -c
+    $hash{$_}++ for @lines;
+
+    # sort -rn
     foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
        my $sign_offs = $hash{$line};
+       my $percent = $sign_offs * 100 / $divisor;
+
+       $percent = 100 if ($percent > 100);
        $count++;
        last if ($sign_offs < $email_git_min_signatures ||
                 $count > $email_git_max_maintainers ||
-                $sign_offs * 100 / $total_sign_offs < $email_git_min_percent);
-       push_email_address($line);
+                $percent < $email_git_min_percent);
+       push_email_address($line, '');
+       if ($output_rolestats) {
+           my $fmt_percent = sprintf("%.0f", $percent);
+           add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
+       } else {
+           add_role($line, $role);
+       }
+    }
+}
+
+sub vcs_file_signoffs {
+    my ($file) = @_;
+
+    my @signers = ();
+    my $commits;
+
+    return if (!vcs_exists());
+
+    my $cmd = $VCS_cmds{"find_signers_cmd"};
+    $cmd =~ s/(\$\w+)/$1/eeg;          # interpolate $cmd
+
+    ($commits, @signers) = vcs_find_signers($cmd);
+    vcs_assign("commit_signer", $commits, @signers);
+}
+
+sub vcs_file_blame {
+    my ($file) = @_;
+
+    my @signers = ();
+    my @commits = ();
+    my $total_commits;
+
+    return if (!vcs_exists());
+
+    @commits = vcs_blame($file);
+    @commits = uniq(@commits);
+    $total_commits = @commits;
+
+    foreach my $commit (@commits) {
+       my $commit_count;
+       my @commit_signers = ();
+
+       my $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+       $cmd =~ s/(\$\w+)/$1/eeg;       #interpolate $cmd
+
+       ($commit_count, @commit_signers) = vcs_find_signers($cmd);
+       push(@signers, @commit_signers);
+    }
+
+    if ($from_filename) {
+       vcs_assign("commits", $total_commits, @signers);
+    } else {
+       vcs_assign("modified commits", $total_commits, @signers);
     }
 }
 
 sub uniq {
-    my @parms = @_;
+    my (@parms) = @_;
 
     my %saw;
     @parms = grep(!$saw{$_}++, @parms);
@@ -841,7 +1067,7 @@ sub uniq {
 }
 
 sub sort_and_uniq {
-    my @parms = @_;
+    my (@parms) = @_;
 
     my %saw;
     @parms = sort @parms;
@@ -849,8 +1075,27 @@ sub sort_and_uniq {
     return @parms;
 }
 
+sub merge_email {
+    my @lines;
+    my %saw;
+
+    for (@_) {
+       my ($address, $role) = @$_;
+       if (!$saw{$address}) {
+           if ($output_roles) {
+               push(@lines, "$address ($role)");
+           } else {
+               push(@lines, $address);
+           }
+           $saw{$address} = 1;
+       }
+    }
+
+    return @lines;
+}
+
 sub output {
-    my @parms = @_;
+    my (@parms) = @_;
 
     if ($output_multiline) {
        foreach my $line (@parms) {
@@ -947,11 +1192,9 @@ sub rfc822_validlist ($) {
     if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
        $s =~ m/^$rfc822_char*$/) {
         while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
-            push @r, $1;
+            push(@r, $1);
         }
         return wantarray ? (scalar(@r), @r) : 1;
     }
-    else {
-        return wantarray ? () : 0;
-    }
+    return wantarray ? () : 0;
 }
index b4b48afb6de6087474ae6adf4f5076c4d8877d4b..5d9411839cd7494c54586380ebf6262ebb633227 100644 (file)
@@ -159,7 +159,7 @@ static int pxa2xx_ac97_resume(struct device *dev)
        return ret;
 }
 
-static struct dev_pm_ops pxa2xx_ac97_pm_ops = {
+static const struct dev_pm_ops pxa2xx_ac97_pm_ops = {
        .suspend        = pxa2xx_ac97_suspend,
        .resume         = pxa2xx_ac97_resume,
 };
index fda7a94c992f1094f8b8d2c8d91dcb97fe868876..ccc642269b9e6a71a108df9f03d39d54f5df9029 100644 (file)
@@ -4,9 +4,7 @@
 
 snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
 snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
-ifdef CONFIG_MGEODE_LX
 snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
-endif
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
index 05f56e04849b8e083e05def751b3d17bdb2e96a3..91e7faf69bbb3732dc7eb224635c6728fec31396 100644 (file)
@@ -389,6 +389,7 @@ probefail_out:
 
 static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
 {
+       olpc_quirks_cleanup();
        snd_card_free(pci_get_drvdata(pci));
        pci_set_drvdata(pci, NULL);
 }
index 7a298ac662e322e768cec22c5d3e7cadbe4fe3d9..51966d782a3cb45f02a4186b726f8cdcbc42df40 100644 (file)
@@ -99,10 +99,11 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
 int snd_cs5535audio_resume(struct pci_dev *pci);
 #endif
 
-#if defined(CONFIG_OLPC) && defined(CONFIG_MGEODE_LX)
+#ifdef CONFIG_OLPC
 void __devinit olpc_prequirks(struct snd_card *card,
                struct snd_ac97_template *ac97);
 int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
+void __devexit olpc_quirks_cleanup(void);
 void olpc_analog_input(struct snd_ac97 *ac97, int on);
 void olpc_mic_bias(struct snd_ac97 *ac97, int on);
 
@@ -128,6 +129,7 @@ static inline int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
 {
        return 0;
 }
+static inline void olpc_quirks_cleanup(void) { }
 static inline void olpc_analog_input(struct snd_ac97 *ac97, int on) { }
 static inline void olpc_mic_bias(struct snd_ac97 *ac97, int on) { }
 static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
index 5c6814335cd727a83e55c58a73ef85965f633841..50da49be9ae5be7e18a5fc65be87bcfa4b0dabb1 100644 (file)
 #include <sound/info.h>
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
+#include <linux/gpio.h>
 
 #include <asm/olpc.h>
 #include "cs5535audio.h"
 
+#define DRV_NAME "cs5535audio-olpc"
+
 /*
  * OLPC has an additional feature on top of the regular AD1888 codec features.
  * It has an Analog Input mode that is switched into (after disabling the
@@ -38,10 +41,7 @@ void olpc_analog_input(struct snd_ac97 *ac97, int on)
        }
 
        /* set Analog Input through GPIO */
-       if (on)
-               geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
-       else
-               geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
+       gpio_set_value(OLPC_GPIO_MIC_AC, on);
 }
 
 /*
@@ -73,8 +73,7 @@ static int olpc_dc_info(struct snd_kcontrol *kctl,
 
 static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
 {
-       v->value.integer.value[0] = geode_gpio_isset(OLPC_GPIO_MIC_AC,
-                       GPIO_OUTPUT_VAL);
+       v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC);
        return 0;
 }
 
@@ -153,6 +152,12 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
        if (!machine_is_olpc())
                return 0;
 
+       if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {
+               printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO\n");
+               return -EIO;
+       }
+       gpio_direction_output(OLPC_GPIO_MIC_AC, 0);
+
        /* drop the original AD1888 HPF control */
        memset(&elem, 0, sizeof(elem));
        elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -169,11 +174,18 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
        for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
                err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
                                ac97->private_data));
-               if (err < 0)
+               if (err < 0) {
+                       gpio_free(OLPC_GPIO_MIC_AC);
                        return err;
+               }
        }
 
        /* turn off the mic by default */
        olpc_mic_bias(ac97, 0);
        return 0;
 }
+
+void __devexit olpc_quirks_cleanup(void)
+{
+       gpio_free(OLPC_GPIO_MIC_AC);
+}
index d24328661c6a8f27a935f244d31234726fd906df..40ccb419b6e97a83bdd253a05c529ad8adfec9d2 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/compat.h>
 #include <linux/mutex.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/firmware.h>
 #include <sound/core.h>
 #include "hda_codec.h"
@@ -428,8 +429,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
        char *key, *val;
        struct hda_hint *hint;
 
-       while (isspace(*buf))
-               buf++;
+       buf = skip_spaces(buf);
        if (!*buf || *buf == '#' || *buf == '\n')
                return 0;
        if (*buf == '=')
@@ -444,8 +444,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
                return -EINVAL;
        }
        *val++ = 0;
-       while (isspace(*val))
-               val++;
+       val = skip_spaces(val);
        remove_trail_spaces(key);
        remove_trail_spaces(val);
        hint = get_hint(codec, key);
index d441c3b646317799000c046b10648dc60b3cae5b..4984754f3298dac412f9ee22dfbce4de5f5804f8 100644 (file)
@@ -312,7 +312,7 @@ int simtec_audio_resume(struct device *dev)
        return 0;
 }
 
-struct dev_pm_ops simtec_audio_pmops = {
+const struct dev_pm_ops simtec_audio_pmops = {
        .resume = simtec_audio_resume,
 };
 EXPORT_SYMBOL_GPL(simtec_audio_pmops);
index 2714203af161b369c839d470e673bb500266959c..e18faee30cce0a388b2d06d2ba9a676200117d16 100644 (file)
@@ -15,7 +15,7 @@ extern int simtec_audio_core_probe(struct platform_device *pdev,
 extern int simtec_audio_remove(struct platform_device *pdev);
 
 #ifdef CONFIG_PM
-extern struct dev_pm_ops simtec_audio_pmops;
+extern const struct dev_pm_ops simtec_audio_pmops;
 #define simtec_audio_pm &simtec_audio_pmops
 #else
 #define simtec_audio_pm NULL
index ef8f28284cb900372c7bf536a4d835b6e1ab9306..0a6440c6f54a80a9af52aa10ffe9d898fe5c456d 100644 (file)
@@ -1236,7 +1236,7 @@ static int soc_poweroff(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops soc_pm_ops = {
+static const struct dev_pm_ops soc_pm_ops = {
        .suspend = soc_suspend,
        .resume = soc_resume,
        .poweroff = soc_poweroff,