aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/filesystems/vfat.txt5
-rw-r--r--Documentation/hwmon/f71882fg12
-rw-r--r--Documentation/hwmon/ibmaem2
-rw-r--r--Documentation/hwmon/sysfs-interface19
-rw-r--r--Documentation/hwmon/tmp40142
-rw-r--r--Documentation/hwmon/w83627ehf11
-rw-r--r--Documentation/i2c/busses/i2c-viapro4
-rw-r--r--arch/blackfin/Kconfig60
-rw-r--r--arch/blackfin/Makefile7
-rw-r--r--arch/blackfin/boot/.gitignore3
-rw-r--r--arch/blackfin/boot/Makefile31
-rw-r--r--arch/blackfin/include/asm/atomic.h16
-rw-r--r--arch/blackfin/include/asm/bfin-global.h11
-rw-r--r--arch/blackfin/include/asm/bitops.h3
-rw-r--r--arch/blackfin/include/asm/bug.h57
-rw-r--r--arch/blackfin/include/asm/cache.h11
-rw-r--r--arch/blackfin/include/asm/cacheflush.h3
-rw-r--r--arch/blackfin/include/asm/cpu.h1
-rw-r--r--arch/blackfin/include/asm/ftrace.h14
-rw-r--r--arch/blackfin/include/asm/ipipe.h28
-rw-r--r--arch/blackfin/include/asm/irq.h271
-rw-r--r--arch/blackfin/include/asm/irqflags.h63
-rw-r--r--arch/blackfin/include/asm/mutex-dec.h112
-rw-r--r--arch/blackfin/include/asm/sections.h11
-rw-r--r--arch/blackfin/include/asm/system.h4
-rw-r--r--arch/blackfin/include/asm/unistd.h3
-rw-r--r--arch/blackfin/kernel/Makefile5
-rw-r--r--arch/blackfin/kernel/bfin_dma_5xx.c4
-rw-r--r--arch/blackfin/kernel/bfin_ksyms.c5
-rw-r--r--arch/blackfin/kernel/cplb-mpu/cplbmgr.c2
-rw-r--r--arch/blackfin/kernel/cplb-nompu/cplbmgr.c54
-rw-r--r--arch/blackfin/kernel/early_printk.c8
-rw-r--r--arch/blackfin/kernel/ftrace-entry.S140
-rw-r--r--arch/blackfin/kernel/ftrace.c42
-rw-r--r--arch/blackfin/kernel/ipipe.c42
-rw-r--r--arch/blackfin/kernel/setup.c16
-rw-r--r--arch/blackfin/kernel/stacktrace.c53
-rw-r--r--arch/blackfin/kernel/traps.c128
-rw-r--r--arch/blackfin/kernel/vmlinux.lds.S17
-rw-r--r--arch/blackfin/lib/checksum.c2
-rw-r--r--arch/blackfin/mach-bf518/boards/ezbrd.c12
-rw-r--r--arch/blackfin/mach-bf527/boards/cm_bf527.c5
-rw-r--r--arch/blackfin/mach-bf527/boards/ezbrd.c5
-rw-r--r--arch/blackfin/mach-bf527/boards/ezkit.c5
-rw-r--r--arch/blackfin/mach-bf533/boards/H8606.c5
-rw-r--r--arch/blackfin/mach-bf533/boards/blackstamp.c5
-rw-r--r--arch/blackfin/mach-bf533/boards/cm_bf533.c5
-rw-r--r--arch/blackfin/mach-bf533/boards/ezkit.c5
-rw-r--r--arch/blackfin/mach-bf533/boards/stamp.c5
-rw-r--r--arch/blackfin/mach-bf537/boards/cm_bf537.c7
-rw-r--r--arch/blackfin/mach-bf537/boards/minotaur.c5
-rw-r--r--arch/blackfin/mach-bf537/boards/pnav10.c7
-rw-r--r--arch/blackfin/mach-bf537/boards/stamp.c2
-rw-r--r--arch/blackfin/mach-bf537/boards/tcm_bf537.c5
-rw-r--r--arch/blackfin/mach-bf538/boards/ezkit.c10
-rw-r--r--arch/blackfin/mach-bf548/boards/cm_bf548.c10
-rw-r--r--arch/blackfin/mach-bf548/boards/ezkit.c23
-rw-r--r--arch/blackfin/mach-bf561/boards/cm_bf561.c7
-rw-r--r--arch/blackfin/mach-bf561/boards/ezkit.c5
-rw-r--r--arch/blackfin/mach-common/cache-c.c14
-rw-r--r--arch/blackfin/mach-common/entry.S12
-rw-r--r--arch/blackfin/mach-common/smp.c24
-rw-r--r--arch/mips/configs/bigsur_defconfig2
-rw-r--r--arch/mips/configs/mtx1_defconfig2
-rw-r--r--arch/powerpc/Kconfig5
-rw-r--r--arch/powerpc/boot/install.sh3
-rw-r--r--arch/powerpc/configs/ppc6xx_defconfig2
-rw-r--r--arch/powerpc/include/asm/atomic.h3
-rw-r--r--arch/powerpc/include/asm/hw_irq.h5
-rw-r--r--arch/powerpc/include/asm/iommu.h10
-rw-r--r--arch/powerpc/include/asm/ps3.h18
-rw-r--r--arch/powerpc/include/asm/ps3gpu.h86
-rw-r--r--arch/powerpc/include/asm/reg.h4
-rw-r--r--arch/powerpc/include/asm/systbl.h1
-rw-r--r--arch/powerpc/include/asm/unistd.h3
-rw-r--r--arch/powerpc/kernel/Makefile2
-rw-r--r--arch/powerpc/kernel/setup_64.c4
-rw-r--r--arch/powerpc/kernel/time.c10
-rw-r--r--arch/powerpc/platforms/cell/axon_msi.c4
-rw-r--r--arch/powerpc/platforms/cell/iommu.c37
-rw-r--r--arch/powerpc/platforms/iseries/dt.c3
-rw-r--r--arch/powerpc/platforms/iseries/mf.c3
-rw-r--r--arch/powerpc/platforms/ps3/mm.c12
-rw-r--r--arch/powerpc/platforms/ps3/os-area.c142
-rw-r--r--arch/powerpc/platforms/ps3/platform.h10
-rw-r--r--arch/powerpc/platforms/ps3/setup.c1
-rw-r--r--arch/powerpc/platforms/ps3/system-bus.c15
-rw-r--r--arch/s390/Kconfig9
-rw-r--r--arch/s390/Makefile4
-rw-r--r--arch/s390/appldata/appldata_base.c119
-rw-r--r--arch/s390/include/asm/ccwdev.h19
-rw-r--r--arch/s390/include/asm/ccwgroup.h10
-rw-r--r--arch/s390/include/asm/suspend.h10
-rw-r--r--arch/s390/include/asm/system.h22
-rw-r--r--arch/s390/kernel/early.c6
-rw-r--r--arch/s390/kernel/mem_detect.c19
-rw-r--r--arch/s390/kernel/smp.c38
-rw-r--r--arch/s390/mm/pgtable.c19
-rw-r--r--arch/s390/power/Makefile8
-rw-r--r--arch/s390/power/suspend.c40
-rw-r--r--arch/s390/power/swsusp.c30
-rw-r--r--arch/s390/power/swsusp_64.c17
-rw-r--r--arch/s390/power/swsusp_asm64.S199
-rw-r--r--block/blk-core.c6
-rw-r--r--block/blk-settings.c37
-rw-r--r--block/cfq-iosched.c7
-rw-r--r--drivers/acpi/acpica/acevents.h2
-rw-r--r--drivers/acpi/acpica/acglobal.h3
-rw-r--r--drivers/acpi/acpica/aclocal.h12
-rw-r--r--drivers/acpi/acpica/acnamesp.h13
-rw-r--r--drivers/acpi/acpica/amlcode.h2
-rw-r--r--drivers/acpi/acpica/dsobject.c5
-rw-r--r--drivers/acpi/acpica/dsopcode.c17
-rw-r--r--drivers/acpi/acpica/dswstate.c4
-rw-r--r--drivers/acpi/acpica/evregion.c12
-rw-r--r--drivers/acpi/acpica/evxfevnt.c4
-rw-r--r--drivers/acpi/acpica/exconfig.c125
-rw-r--r--drivers/acpi/acpica/excreate.c2
-rw-r--r--drivers/acpi/acpica/exdump.c6
-rw-r--r--drivers/acpi/acpica/exfldio.c20
-rw-r--r--drivers/acpi/acpica/exmutex.c45
-rw-r--r--drivers/acpi/acpica/exstore.c4
-rw-r--r--drivers/acpi/acpica/hwregs.c4
-rw-r--r--drivers/acpi/acpica/nsalloc.c14
-rw-r--r--drivers/acpi/acpica/nsnames.c2
-rw-r--r--drivers/acpi/acpica/nsobject.c9
-rw-r--r--drivers/acpi/acpica/nspredef.c7
-rw-r--r--drivers/acpi/acpica/nssearch.c4
-rw-r--r--drivers/acpi/acpica/nswalk.c69
-rw-r--r--drivers/acpi/acpica/nsxfname.c150
-rw-r--r--drivers/acpi/acpica/nsxfobj.c9
-rw-r--r--drivers/acpi/acpica/rscalc.c5
-rw-r--r--drivers/acpi/acpica/rsxface.c8
-rw-r--r--drivers/acpi/acpica/tbfadt.c16
-rw-r--r--drivers/acpi/acpica/tbinstal.c2
-rw-r--r--drivers/acpi/acpica/utcopy.c23
-rw-r--r--drivers/acpi/acpica/utdebug.c8
-rw-r--r--drivers/acpi/acpica/utdelete.c21
-rw-r--r--drivers/acpi/acpica/utmisc.c20
-rw-r--r--drivers/acpi/acpica/utmutex.c26
-rw-r--r--drivers/block/cciss.c1
-rw-r--r--drivers/block/mg_disk.c28
-rw-r--r--drivers/block/ps3disk.c18
-rw-r--r--drivers/block/ps3vram.c168
-rw-r--r--drivers/char/hvc_iucv.c204
-rw-r--r--drivers/char/ps3flash.c296
-rw-r--r--drivers/char/pty.c6
-rw-r--r--drivers/char/tty_ldisc.c6
-rw-r--r--drivers/hwmon/Kconfig16
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/f71882fg.c250
-rw-r--r--drivers/hwmon/hwmon.c29
-rw-r--r--drivers/hwmon/ibmaem.c1
-rw-r--r--drivers/hwmon/max6650.c86
-rw-r--r--drivers/hwmon/sht15.c10
-rw-r--r--drivers/hwmon/tmp401.c690
-rw-r--r--drivers/hwmon/w83627ehf.c10
-rw-r--r--drivers/i2c/busses/Kconfig6
-rw-r--r--drivers/i2c/busses/i2c-viapro.c4
-rw-r--r--drivers/i2c/busses/i2c-voodoo3.c1
-rw-r--r--drivers/i2c/chips/Kconfig15
-rw-r--r--drivers/i2c/chips/Makefile1
-rw-r--r--drivers/i2c/i2c-core.c43
-rw-r--r--drivers/macintosh/therm_adt746x.c84
-rw-r--r--drivers/macintosh/therm_pm72.c95
-rw-r--r--drivers/macintosh/therm_windtunnel.c126
-rw-r--r--drivers/macintosh/windfarm_lm75_sensor.c129
-rw-r--r--drivers/macintosh/windfarm_max6690_sensor.c103
-rw-r--r--drivers/macintosh/windfarm_smu_sat.c109
-rw-r--r--drivers/md/dm.c1
-rw-r--r--drivers/misc/eeprom/Kconfig14
-rw-r--r--drivers/misc/eeprom/Makefile1
-rw-r--r--drivers/misc/eeprom/max6875.c (renamed from drivers/i2c/chips/max6875.c)2
-rw-r--r--drivers/net/ps3_gelic_net.c8
-rw-r--r--drivers/ps3/ps3-sys-manager.c2
-rw-r--r--drivers/ps3/ps3av.c8
-rw-r--r--drivers/ps3/ps3av_cmd.c3
-rw-r--r--drivers/s390/block/dasd.c109
-rw-r--r--drivers/s390/block/dasd_devmap.c1
-rw-r--r--drivers/s390/block/dasd_eckd.c108
-rw-r--r--drivers/s390/block/dasd_fba.c6
-rw-r--r--drivers/s390/block/dasd_int.h13
-rw-r--r--drivers/s390/block/dcssblk.c132
-rw-r--r--drivers/s390/block/xpram.c129
-rw-r--r--drivers/s390/char/con3215.c205
-rw-r--r--drivers/s390/char/con3270.c12
-rw-r--r--drivers/s390/char/fs3270.c16
-rw-r--r--drivers/s390/char/monreader.c140
-rw-r--r--drivers/s390/char/monwriter.c98
-rw-r--r--drivers/s390/char/raw3270.c84
-rw-r--r--drivers/s390/char/raw3270.h12
-rw-r--r--drivers/s390/char/sclp.c248
-rw-r--r--drivers/s390/char/sclp.h23
-rw-r--r--drivers/s390/char/sclp_cmd.c42
-rw-r--r--drivers/s390/char/sclp_con.c139
-rw-r--r--drivers/s390/char/sclp_rw.c20
-rw-r--r--drivers/s390/char/sclp_rw.h12
-rw-r--r--drivers/s390/char/sclp_vt220.c118
-rw-r--r--drivers/s390/char/tape.h3
-rw-r--r--drivers/s390/char/tape_34xx.c3
-rw-r--r--drivers/s390/char/tape_3590.c3
-rw-r--r--drivers/s390/char/tape_core.c52
-rw-r--r--drivers/s390/char/vmlogrdr.c27
-rw-r--r--drivers/s390/char/vmur.c26
-rw-r--r--drivers/s390/char/vmwatchdog.c81
-rw-r--r--drivers/s390/cio/ccwgroup.c78
-rw-r--r--drivers/s390/cio/chsc.c3
-rw-r--r--drivers/s390/cio/chsc.h1
-rw-r--r--drivers/s390/cio/chsc_sch.c32
-rw-r--r--drivers/s390/cio/cmf.c5
-rw-r--r--drivers/s390/cio/css.c157
-rw-r--r--drivers/s390/cio/css.h10
-rw-r--r--drivers/s390/cio/device.c260
-rw-r--r--drivers/s390/cio/device.h3
-rw-r--r--drivers/s390/cio/device_fsm.c96
-rw-r--r--drivers/s390/cio/device_ops.c26
-rw-r--r--drivers/s390/cio/io_sch.h1
-rw-r--r--drivers/s390/net/claw.c14
-rw-r--r--drivers/s390/net/ctcm_main.c37
-rw-r--r--drivers/s390/net/lcs.c74
-rw-r--r--drivers/s390/net/netiucv.c115
-rw-r--r--drivers/s390/net/qeth_core_main.c51
-rw-r--r--drivers/s390/net/qeth_l2_main.c52
-rw-r--r--drivers/s390/net/qeth_l3_main.c52
-rw-r--r--drivers/s390/net/smsgiucv.c63
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c33
-rw-r--r--drivers/spi/spi_bfin5xx.c4
-rw-r--r--drivers/usb/host/ehci-ps3.c7
-rw-r--r--drivers/usb/host/ohci-ps3.c7
-rw-r--r--drivers/video/ps3fb.c272
-rw-r--r--drivers/video/tdfxfb.c1
-rw-r--r--fs/bio.c1
-rw-r--r--fs/btrfs/disk-io.c26
-rw-r--r--fs/btrfs/transaction.c12
-rw-r--r--fs/fat/cache.c6
-rw-r--r--fs/fat/dir.c2
-rw-r--r--fs/fat/fat.h7
-rw-r--r--fs/fat/fatent.c4
-rw-r--r--fs/fat/file.c184
-rw-r--r--fs/fat/inode.c28
-rw-r--r--fs/fat/misc.c22
-rw-r--r--fs/fat/namei_msdos.c2
-rw-r--r--fs/fat/namei_vfat.c2
-rw-r--r--fs/ubifs/super.c3
-rw-r--r--include/acpi/acpixf.h9
-rw-r--r--include/acpi/actypes.h20
-rw-r--r--include/acpi/platform/acgcc.h4
-rw-r--r--include/acpi/platform/aclinux.h63
-rw-r--r--include/asm-generic/atomic64.h42
-rw-r--r--include/linux/bio.h5
-rw-r--r--include/linux/blkdev.h1
-rw-r--r--include/linux/kernel.h4
-rw-r--r--include/linux/mg_disk.h45
-rw-r--r--kernel/module.c2
-rw-r--r--kernel/printk.c33
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/Makefile2
-rw-r--r--lib/atomic64.c175
-rw-r--r--mm/Kconfig4
-rw-r--r--mm/bounce.c1
-rw-r--r--mm/highmem.c1
-rw-r--r--net/iucv/af_iucv.c147
-rw-r--r--net/iucv/iucv.c268
263 files changed, 7774 insertions, 2432 deletions
diff --git a/Documentation/filesystems/vfat.txt b/Documentation/filesystems/vfat.txt
index 5147be5e13c..b58b84b50fa 100644
--- a/Documentation/filesystems/vfat.txt
+++ b/Documentation/filesystems/vfat.txt
@@ -132,6 +132,11 @@ rodir -- FAT has the ATTR_RO (read-only) attribute. On Windows,
If you want to use ATTR_RO as read-only flag even for
the directory, set this option.
+errors=panic|continue|remount-ro
+ -- specify FAT behavior on critical errors: panic, continue
+ without doing anything or remount the partition in
+ read-only mode (default behavior).
+
<bool>: 0,1,yes,no,true,false
TODO
diff --git a/Documentation/hwmon/f71882fg b/Documentation/hwmon/f71882fg
index a8321267b5b..bee4c30bc1e 100644
--- a/Documentation/hwmon/f71882fg
+++ b/Documentation/hwmon/f71882fg
@@ -2,14 +2,18 @@ Kernel driver f71882fg
======================
Supported chips:
- * Fintek F71882FG and F71883FG
- Prefix: 'f71882fg'
+ * Fintek F71858FG
+ Prefix: 'f71858fg'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Available from the Fintek website
* Fintek F71862FG and F71863FG
Prefix: 'f71862fg'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Available from the Fintek website
+ * Fintek F71882FG and F71883FG
+ Prefix: 'f71882fg'
+ Addresses scanned: none, address read from Super I/O config space
+ Datasheet: Available from the Fintek website
* Fintek F8000
Prefix: 'f8000'
Addresses scanned: none, address read from Super I/O config space
@@ -66,13 +70,13 @@ printed when loading the driver.
Three different fan control modes are supported; the mode number is written
to the pwm#_enable file. Note that not all modes are supported on all
-chips, and some modes may only be available in RPM / PWM mode on the F8000.
+chips, and some modes may only be available in RPM / PWM mode.
Writing an unsupported mode will result in an invalid parameter error.
* 1: Manual mode
You ask for a specific PWM duty cycle / DC voltage or a specific % of
fan#_full_speed by writing to the pwm# file. This mode is only
- available on the F8000 if the fan channel is in RPM mode.
+ available on the F71858FG / F8000 if the fan channel is in RPM mode.
* 2: Normal auto mode
You can define a number of temperature/fan speed trip points, which % the
diff --git a/Documentation/hwmon/ibmaem b/Documentation/hwmon/ibmaem
index e98bdfea346..1e0d59e000b 100644
--- a/Documentation/hwmon/ibmaem
+++ b/Documentation/hwmon/ibmaem
@@ -7,7 +7,7 @@ henceforth as AEM.
Supported systems:
* Any recent IBM System X server with AEM support.
This includes the x3350, x3550, x3650, x3655, x3755, x3850 M2,
- x3950 M2, and certain HS2x/LS2x/QS2x blades. The IPMI host interface
+ x3950 M2, and certain HC10/HS2x/LS2x/QS2x blades. The IPMI host interface
driver ("ipmi-si") needs to be loaded for this driver to do anything.
Prefix: 'ibmaem'
Datasheet: Not available
diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index 004ee161721..dcbd502c879 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -70,6 +70,7 @@ are interpreted as 0! For more on how written strings are interpreted see the
[0-*] denotes any positive number starting from 0
[1-*] denotes any positive number starting from 1
RO read only value
+WO write only value
RW read/write value
Read/write values may be read-only for some chips, depending on the
@@ -295,6 +296,24 @@ temp[1-*]_label Suggested temperature channel label.
user-space.
RO
+temp[1-*]_lowest
+ Historical minimum temperature
+ Unit: millidegree Celsius
+ RO
+
+temp[1-*]_highest
+ Historical maximum temperature
+ Unit: millidegree Celsius
+ RO
+
+temp[1-*]_reset_history
+ Reset temp_lowest and temp_highest
+ WO
+
+temp_reset_history
+ Reset temp_lowest and temp_highest for all sensors
+ WO
+
Some chips measure temperature using external thermistors and an ADC, and
report the temperature measurement as a voltage. Converting this voltage
back to a temperature (or the other way around for limits) requires
diff --git a/Documentation/hwmon/tmp401 b/Documentation/hwmon/tmp401
new file mode 100644
index 00000000000..9fc44724921
--- /dev/null
+++ b/Documentation/hwmon/tmp401
@@ -0,0 +1,42 @@
+Kernel driver tmp401
+====================
+
+Supported chips:
+ * Texas Instruments TMP401
+ Prefix: 'tmp401'
+ Addresses scanned: I2C 0x4c
+ Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp401.html
+ * Texas Instruments TMP411
+ Prefix: 'tmp411'
+ Addresses scanned: I2C 0x4c
+ Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp411.html
+
+Authors:
+ Hans de Goede <hdegoede@redhat.com>
+ Andre Prendel <andre.prendel@gmx.de>
+
+Description
+-----------
+
+This driver implements support for Texas Instruments TMP401 and
+TMP411 chips. These chips implements one remote and one local
+temperature sensor. Temperature is measured in degrees
+Celsius. Resolution of the remote sensor is 0.0625 degree. Local
+sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
+supported by the driver so far, so using the default resolution of 0.5
+degree).
+
+The driver provides the common sysfs-interface for temperatures (see
+/Documentation/hwmon/sysfs-interface under Temperatures).
+
+The TMP411 chip is compatible with TMP401. It provides some additional
+features.
+
+* Minimum and Maximum temperature measured since power-on, chip-reset
+
+ Exported via sysfs attributes tempX_lowest and tempX_highest.
+
+* Reset of historical minimum/maximum temperature measurements
+
+ Exported via sysfs attribute temp_reset_history. Writing 1 to this
+ file triggers a reset.
diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf
index b6eb59384bb..02b74899eda 100644
--- a/Documentation/hwmon/w83627ehf
+++ b/Documentation/hwmon/w83627ehf
@@ -12,6 +12,10 @@ Supported chips:
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet:
http://www.nuvoton.com.tw/NR/rdonlyres/7885623D-A487-4CF9-A47F-30C5F73D6FE6/0/W83627DHG.pdf
+ * Winbond W83627DHG-P
+ Prefix: 'w83627dhg'
+ Addresses scanned: ISA address retrieved from Super I/O registers
+ Datasheet: not available
* Winbond W83667HG
Prefix: 'w83667hg'
Addresses scanned: ISA address retrieved from Super I/O registers
@@ -28,8 +32,8 @@ Description
-----------
This driver implements support for the Winbond W83627EHF, W83627EHG,
-W83627DHG and W83667HG super I/O chips. We will refer to them collectively
-as Winbond chips.
+W83627DHG, W83627DHG-P and W83667HG super I/O chips. We will refer to them
+collectively as Winbond chips.
The chips implement three temperature sensors, five fan rotation
speed sensors, ten analog voltage sensors (only nine for the 627DHG), one
@@ -135,3 +139,6 @@ done in the driver for all register addresses.
The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
the ICH8 southbridge gets that data via PECI from the DHG, so that the
southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.
+
+The DHG-P has an additional automatic fan speed control mode named Smart Fan
+(TM) III+. This mode is not yet supported by the driver.
diff --git a/Documentation/i2c/busses/i2c-viapro b/Documentation/i2c/busses/i2c-viapro
index 22efedf60c8..2e758b0e945 100644
--- a/Documentation/i2c/busses/i2c-viapro
+++ b/Documentation/i2c/busses/i2c-viapro
@@ -19,6 +19,9 @@ Supported adapters:
* VIA Technologies, Inc. VX800/VX820
Datasheet: available on http://linux.via.com.tw
+ * VIA Technologies, Inc. VX855/VX875
+ Datasheet: Availability unknown
+
Authors:
Kyösti Mälkki <kmalkki@cc.hut.fi>,
Mark D. Studebaker <mdsxyz123@yahoo.com>,
@@ -53,6 +56,7 @@ Your lspci -n listing must show one of these :
device 1106:3287 (VT8251)
device 1106:8324 (CX700)
device 1106:8353 (VX800/VX820)
+ device 1106:8409 (VX855/VX875)
If none of these show up, you should look in the BIOS for settings like
enable ACPI / SMBus or even USB.
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index a60cfe75791..8ea0d942cde 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -6,59 +6,65 @@
mainmenu "Blackfin Kernel Configuration"
config MMU
- bool
- default n
+ def_bool n
config FPU
- bool
- default n
+ def_bool n
config RWSEM_GENERIC_SPINLOCK
- bool
- default y
+ def_bool y
config RWSEM_XCHGADD_ALGORITHM
- bool
- default n
+ def_bool n
config BLACKFIN
- bool
- default y
+ def_bool y
+ select HAVE_FUNCTION_GRAPH_TRACER
+ select HAVE_FUNCTION_TRACER
select HAVE_IDE
+ select HAVE_KERNEL_GZIP
+ select HAVE_KERNEL_BZIP2
+ select HAVE_KERNEL_LZMA
select HAVE_OPROFILE
select ARCH_WANT_OPTIONAL_GPIOLIB
+config GENERIC_BUG
+ def_bool y
+ depends on BUG
+
config ZONE_DMA
- bool
- default y
+ def_bool y
config GENERIC_FIND_NEXT_BIT
- bool
- default y
+ def_bool y
config GENERIC_HWEIGHT
- bool
- default y
+ def_bool y
config GENERIC_HARDIRQS
- bool
- default y
+ def_bool y
config GENERIC_IRQ_PROBE
- bool
- default y
+ def_bool y
config GENERIC_GPIO
- bool
- default y
+ def_bool y
config FORCE_MAX_ZONEORDER
int
default "14"
config GENERIC_CALIBRATE_DELAY
- bool
- default y
+ def_bool y
+
+config LOCKDEP_SUPPORT
+ def_bool y
+
+config STACKTRACE_SUPPORT
+ def_bool y
+
+config TRACE_IRQFLAGS_SUPPORT
+ def_bool y
source "init/Kconfig"
@@ -408,12 +414,12 @@ comment "Clock/PLL Setup"
config CLKIN_HZ
int "Frequency of the crystal on the board in Hz"
+ default "10000000" if BFIN532_IP0X
default "11059200" if BFIN533_STAMP
+ default "24576000" if PNAV10
+ default "25000000" # most people use this
default "27000000" if BFIN533_EZKIT
- default "25000000" if (BFIN537_STAMP || BFIN527_EZKIT || H8606_HVSISTEMAS || BLACKSTAMP || BFIN526_EZBRD || BFIN538_EZKIT || BFIN518F-EZBRD)
default "30000000" if BFIN561_EZKIT
- default "24576000" if PNAV10
- default "10000000" if BFIN532_IP0X
help
The frequency of CLKIN crystal oscillator on the board in Hz.
Warning: This value should match the crystal on the board. Otherwise,
diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile
index d54c8283825..6f9533c3d75 100644
--- a/arch/blackfin/Makefile
+++ b/arch/blackfin/Makefile
@@ -137,7 +137,7 @@ archclean:
INSTALL_PATH ?= /tftpboot
boot := arch/$(ARCH)/boot
-BOOT_TARGETS = vmImage
+BOOT_TARGETS = vmImage vmImage.bz2 vmImage.gz vmImage.lzma
PHONY += $(BOOT_TARGETS) install
KBUILD_IMAGE := $(boot)/vmImage
@@ -150,7 +150,10 @@ install:
$(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install
define archhelp
- echo '* vmImage - Kernel-only image for U-Boot (arch/$(ARCH)/boot/vmImage)'
+ echo '* vmImage - Alias to selected kernel format (vmImage.gz by default)'
+ echo ' vmImage.bz2 - Kernel-only image for U-Boot (arch/$(ARCH)/boot/vmImage.bz2)'
+ echo '* vmImage.gz - Kernel-only image for U-Boot (arch/$(ARCH)/boot/vmImage.gz)'
+ echo ' vmImage.lzma - Kernel-only image for U-Boot (arch/$(ARCH)/boot/vmImage.lzma)'
echo ' install - Install kernel using'
echo ' (your) ~/bin/$(CROSS_COMPILE)installkernel or'
echo ' (distribution) PATH: $(CROSS_COMPILE)installkernel or'
diff --git a/arch/blackfin/boot/.gitignore b/arch/blackfin/boot/.gitignore
index 3ae03994b88..229e5080867 100644
--- a/arch/blackfin/boot/.gitignore
+++ b/arch/blackfin/boot/.gitignore
@@ -1 +1,2 @@
-+vmImage
+vmImage*
+vmlinux*
diff --git a/arch/blackfin/boot/Makefile b/arch/blackfin/boot/Makefile
index e028d13481a..3ab6f23561d 100644
--- a/arch/blackfin/boot/Makefile
+++ b/arch/blackfin/boot/Makefile
@@ -8,24 +8,41 @@
MKIMAGE := $(srctree)/scripts/mkuboot.sh
-targets := vmImage
-extra-y += vmlinux.bin vmlinux.gz
+targets := vmImage vmImage.bz2 vmImage.gz vmImage.lzma
+extra-y += vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma
quiet_cmd_uimage = UIMAGE $@
cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A $(ARCH) -O linux -T kernel \
- -C gzip -n 'Linux-$(KERNELRELEASE)' -a $(CONFIG_BOOT_LOAD) \
+ -C $(2) -n 'Linux-$(KERNELRELEASE)' -a $(CONFIG_BOOT_LOAD) \
-e $(shell $(NM) vmlinux | awk '$$NF == "__start" {print $$1}') \
-d $< $@
$(obj)/vmlinux.bin: vmlinux FORCE
$(call if_changed,objcopy)
-$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE
+$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
$(call if_changed,gzip)
-$(obj)/vmImage: $(obj)/vmlinux.gz
- $(call if_changed,uimage)
- @$(kecho) 'Kernel: $@ is ready'
+$(obj)/vmlinux.bin.bz2: $(obj)/vmlinux.bin FORCE
+ $(call if_changed,bzip2)
+
+$(obj)/vmlinux.bin.lzma: $(obj)/vmlinux.bin FORCE
+ $(call if_changed,lzma)
+
+$(obj)/vmImage.bz2: $(obj)/vmlinux.bin.bz2
+ $(call if_changed,uimage,bzip2)
+
+$(obj)/vmImage.gz: $(obj)/vmlinux.bin.gz
+ $(call if_changed,uimage,gzip)
+
+$(obj)/vmImage.lzma: $(obj)/vmlinux.bin.lzma
+ $(call if_changed,uimage,lzma)
+
+suffix-$(CONFIG_KERNEL_GZIP) := gz
+suffix-$(CONFIG_KERNEL_BZIP2) := bz2
+suffix-$(CONFIG_KERNEL_LZMA) := lzma
+$(obj)/vmImage: $(obj)/vmImage.$(suffix-y)
+ @ln -sf $(notdir $<) $@
install:
sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)"
diff --git a/arch/blackfin/include/asm/atomic.h b/arch/blackfin/include/asm/atomic.h
index 7bbf44e4ddf..b1d92f13ef9 100644
--- a/arch/blackfin/include/asm/atomic.h
+++ b/arch/blackfin/include/asm/atomic.h
@@ -90,7 +90,7 @@ static inline int atomic_test_mask(int mask, atomic_t *v)
static inline void atomic_add(int i, atomic_t *v)
{
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter += i;
@@ -99,7 +99,7 @@ static inline void atomic_add(int i, atomic_t *v)
static inline void atomic_sub(int i, atomic_t *v)
{
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter -= i;
@@ -110,7 +110,7 @@ static inline void atomic_sub(int i, atomic_t *v)
static inline int atomic_add_return(int i, atomic_t *v)
{
int __temp = 0;
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter += i;
@@ -124,7 +124,7 @@ static inline int atomic_add_return(int i, atomic_t *v)
static inline int atomic_sub_return(int i, atomic_t *v)
{
int __temp = 0;
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter -= i;
@@ -136,7 +136,7 @@ static inline int atomic_sub_return(int i, atomic_t *v)
static inline void atomic_inc(volatile atomic_t *v)
{
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter++;
@@ -145,7 +145,7 @@ static inline void atomic_inc(volatile atomic_t *v)
static inline void atomic_dec(volatile atomic_t *v)
{
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter--;
@@ -154,7 +154,7 @@ static inline void atomic_dec(volatile atomic_t *v)
static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
{
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter &= ~mask;
@@ -163,7 +163,7 @@ static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
{
- long flags;
+ unsigned long flags;
local_irq_save_hw(flags);
v->counter |= mask;
diff --git a/arch/blackfin/include/asm/bfin-global.h b/arch/blackfin/include/asm/bfin-global.h
index daffc0684e7..e39277ea43e 100644
--- a/arch/blackfin/include/asm/bfin-global.h
+++ b/arch/blackfin/include/asm/bfin-global.h
@@ -31,7 +31,7 @@
#ifndef __ASSEMBLY__
-#include <asm-generic/sections.h>
+#include <asm/sections.h>
#include <asm/ptrace.h>
#include <asm/user.h>
#include <linux/linkage.h>
@@ -99,15 +99,6 @@ extern const char bfin_board_name[];
extern unsigned long bfin_sic_iwr[];
extern unsigned vr_wakeup;
extern u16 _bfin_swrst; /* shadow for Software Reset Register (SWRST) */
-extern unsigned long _ramstart, _ramend, _rambase;
-extern unsigned long memory_start, memory_end, physical_mem_end;
-extern char _stext_l1[], _etext_l1[], _sdata_l1[], _edata_l1[], _sbss_l1[],
- _ebss_l1[], _l1_lma_start[], _sdata_b_l1[], _sbss_b_l1[], _ebss_b_l1[],
- _stext_l2[], _etext_l2[], _sdata_l2[], _edata_l2[], _sbss_l2[],
- _ebss_l2[], _l2_lma_start[];
-
-/* only used when MTD_UCLINUX */
-extern unsigned long memory_mtd_start, memory_mtd_end, mtd_size;
#ifdef CONFIG_BFIN_ICACHE_LOCK
extern void cache_grab_lock(int way);
diff --git a/arch/blackfin/include/asm/bitops.h b/arch/blackfin/include/asm/bitops.h
index 21b036eadab..75fee2f7d9f 100644
--- a/arch/blackfin/include/asm/bitops.h
+++ b/arch/blackfin/include/asm/bitops.h
@@ -109,7 +109,8 @@ static inline void clear_bit(int nr, volatile unsigned long *addr)
static inline void change_bit(int nr, volatile unsigned long *addr)
{
- int mask, flags;
+ int mask;
+ unsigned long flags;
unsigned long *ADDR = (unsigned long *)addr;
ADDR += nr >> 5;
diff --git a/arch/blackfin/include/asm/bug.h b/arch/blackfin/include/asm/bug.h
index 6d3e11b1fc5..655e49540e4 100644
--- a/arch/blackfin/include/asm/bug.h
+++ b/arch/blackfin/include/asm/bug.h
@@ -2,13 +2,58 @@
#define _BLACKFIN_BUG_H
#ifdef CONFIG_BUG
-#define HAVE_ARCH_BUG
-#define BUG() do { \
- dump_bfin_trace_buffer(); \
- printk(KERN_EMERG "BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
- panic("BUG!"); \
-} while (0)
+#define BFIN_BUG_OPCODE 0xefcd
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+
+#define _BUG_OR_WARN(flags) \
+ asm volatile( \
+ "1: .hword %0\n" \
+ " .section __bug_table,\"a\",@progbits\n" \
+ "2: .long 1b\n" \
+ " .long %1\n" \
+ " .short %2\n" \
+ " .short %3\n" \
+ " .org 2b + %4\n" \
+ " .previous" \
+ : \
+ : "i"(BFIN_BUG_OPCODE), "i"(__FILE__), \
+ "i"(__LINE__), "i"(flags), \
+ "i"(sizeof(struct bug_entry)))
+
+#else
+
+#define _BUG_OR_WARN(flags) \
+ asm volatile( \
+ "1: .hword %0\n" \
+ " .section __bug_table,\"a\",@progbits\n" \
+ "2: .long 1b\n" \
+ " .short %1\n" \
+ " .org 2b + %2\n" \
+ " .previous" \
+ : \
+ : "i"(BFIN_BUG_OPCODE), "i"(flags), \
+ "i"(sizeof(struct bug_entry)))
+
+#endif /* CONFIG_DEBUG_BUGVERBOSE */
+
+#define BUG() \
+ do { \
+ _BUG_OR_WARN(0); \
+ for (;;); \
+ } while (0)
+
+#define WARN_ON(condition) \
+ ({ \
+ int __ret_warn_on = !!(condition); \
+ if (unlikely(__ret_warn_on)) \
+ _BUG_OR_WARN(BUGFLAG_WARNING); \
+ unlikely(__ret_warn_on); \
+ })
+
+#define HAVE_ARCH_BUG
+#define HAVE_ARCH_WARN_ON
#endif
diff --git a/arch/blackfin/include/asm/cache.h b/arch/blackfin/include/asm/cache.h
index 86637814cf2..2ef669ed922 100644
--- a/arch/blackfin/include/asm/cache.h
+++ b/arch/blackfin/include/asm/cache.h
@@ -34,9 +34,13 @@
#define L1_CACHE_SHIFT_MAX 5
#if defined(CONFIG_SMP) && \
- !defined(CONFIG_BFIN_CACHE_COHERENT) && \
- defined(CONFIG_BFIN_DCACHE)
-#define __ARCH_SYNC_CORE_DCACHE
+ !defined(CONFIG_BFIN_CACHE_COHERENT)
+# if defined(CONFIG_BFIN_ICACHE)
+# define __ARCH_SYNC_CORE_ICACHE
+# endif
+# if defined(CONFIG_BFIN_DCACHE)
+# define __ARCH_SYNC_CORE_DCACHE
+# endif
#ifndef __ASSEMBLY__
asmlinkage void __raw_smp_mark_barrier_asm(void);
asmlinkage void __raw_smp_check_barrier_asm(void);
@@ -51,6 +55,7 @@ static inline void smp_check_barrier(void)
}
void resync_core_dcache(void);
+void resync_core_icache(void);
#endif
#endif
diff --git a/arch/blackfin/include/asm/cacheflush.h b/arch/blackfin/include/asm/cacheflush.h
index 94697f0f6f4..5c17dee53b5 100644
--- a/arch/blackfin/include/asm/cacheflush.h
+++ b/arch/blackfin/include/asm/cacheflush.h
@@ -37,6 +37,7 @@ extern void blackfin_dcache_flush_range(unsigned long start_address, unsigned lo
extern void blackfin_dcache_invalidate_range(unsigned long start_address, unsigned long end_address);
extern void blackfin_dflush_page(void *page);
extern void blackfin_invalidate_entire_dcache(void);
+extern void blackfin_invalidate_entire_icache(void);
#define flush_dcache_mmap_lock(mapping) do { } while (0)
#define flush_dcache_mmap_unlock(mapping) do { } while (0)
@@ -97,7 +98,7 @@ do { memcpy(dst, src, len); \
extern unsigned long reserved_mem_dcache_on;
extern unsigned long reserved_mem_icache_on;
-static inline int bfin_addr_dcachable(unsigned long addr)
+static inline int bfin_addr_dcacheable(unsigned long addr)
{
#ifdef CONFIG_BFIN_DCACHE
if (addr < (_ramend - DMA_UNCACHED_REGION))
diff --git a/arch/blackfin/include/asm/cpu.h b/arch/blackfin/include/asm/cpu.h
index c2594ef877f..565b8136855 100644
--- a/arch/blackfin/include/asm/cpu.h
+++ b/arch/blackfin/include/asm/cpu.h
@@ -34,6 +34,7 @@ struct blackfin_cpudata {
unsigned int dmemctl;
unsigned long loops_per_jiffy;
unsigned long dcache_invld_count;
+ unsigned long icache_invld_count;
};
DECLARE_PER_CPU(struct blackfin_cpudata, cpu_data);
diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h
index 40a8c178f10..8643680f0f7 100644
--- a/arch/blackfin/include/asm/ftrace.h
+++ b/arch/blackfin/include/asm/ftrace.h
@@ -1 +1,13 @@
-/* empty */
+/*
+ * Blackfin ftrace code
+ *
+ * Copyright 2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __ASM_BFIN_FTRACE_H__
+#define __ASM_BFIN_FTRACE_H__
+
+#define MCOUNT_INSN_SIZE 8 /* sizeof mcount call: LINK + CALL */
+
+#endif
diff --git a/arch/blackfin/include/asm/ipipe.h b/arch/blackfin/include/asm/ipipe.h
index 51d0bf5e289..bbe1c3726b6 100644
--- a/arch/blackfin/include/asm/ipipe.h
+++ b/arch/blackfin/include/asm/ipipe.h
@@ -35,10 +35,10 @@
#include <asm/atomic.h>
#include <asm/traps.h>
-#define IPIPE_ARCH_STRING "1.9-01"
+#define IPIPE_ARCH_STRING "1.10-00"
#define IPIPE_MAJOR_NUMBER 1
-#define IPIPE_MINOR_NUMBER 9
-#define IPIPE_PATCH_NUMBER 1
+#define IPIPE_MINOR_NUMBER 10
+#define IPIPE_PATCH_NUMBER 0
#ifdef CONFIG_SMP
#error "I-pipe/blackfin: SMP not implemented"
@@ -54,10 +54,11 @@ do { \
#define task_hijacked(p) \
({ \
- int __x__ = ipipe_current_domain != ipipe_root_domain; \
- /* We would need to clear the SYNC flag for the root domain */ \
- /* over the current processor in SMP mode. */ \
- local_irq_enable_hw(); __x__; \
+ int __x__ = __ipipe_root_domain_p; \
+ __clear_bit(IPIPE_SYNC_FLAG, &ipipe_root_cpudom_var(status)); \
+ if (__x__) \
+ local_irq_enable_hw(); \
+ !__x__; \
})
struct ipipe_domain;
@@ -179,23 +180,24 @@ static inline unsigned long __ipipe_ffnz(unsigned long ul)
#define __ipipe_run_isr(ipd, irq) \
do { \
- if (ipd == ipipe_root_domain) { \
+ if (!__ipipe_pipeline_head_p(ipd)) \
local_irq_enable_hw(); \
- if (ipipe_virtual_irq_p(irq)) \
+ if (ipd == ipipe_root_domain) { \
+ if (unlikely(ipipe_virtual_irq_p(irq))) { \
+ irq_enter(); \
ipd->irqs[irq].handler(irq, ipd->irqs[irq].cookie); \
- else \
+ irq_exit(); \
+ } else \
ipd->irqs[irq].handler(irq, &__raw_get_cpu_var(__ipipe_tick_regs)); \
- local_irq_disable_hw(); \
} else { \
__clear_bit(IPIPE_SYNC_FLAG, &ipipe_cpudom_var(ipd, status)); \
- local_irq_enable_nohead(ipd); \
ipd->irqs[irq].handler(irq, ipd->irqs[irq].cookie); \
/* Attempt to exit the outer interrupt level before \
* starting the deferred IRQ processing. */ \
- local_irq_disable_nohead(ipd); \
__ipipe_run_irqtail(); \
__set_bit(IPIPE_SYNC_FLAG, &ipipe_cpudom_var(ipd, status)); \
} \
+ local_irq_disable_hw(); \
} while (0)
#define __ipipe_syscall_watched_p(p, sc) \
diff --git a/arch/blackfin/include/asm/irq.h b/arch/blackfin/include/asm/irq.h
index 7645e85a5f6..400bdd52ce8 100644
--- a/arch/blackfin/include/asm/irq.h
+++ b/arch/blackfin/include/asm/irq.h
@@ -17,270 +17,17 @@
#ifndef _BFIN_IRQ_H_
#define _BFIN_IRQ_H_
-/* SYS_IRQS and NR_IRQS are defined in <mach-bf5xx/irq.h>*/
-#include <mach/irq.h>
-#include <asm/pda.h>
-#include <asm/processor.h>
-
-#ifdef CONFIG_SMP
-/* Forward decl needed due to cdef inter dependencies */
-static inline uint32_t __pure bfin_dspid(void);
-# define blackfin_core_id() (bfin_dspid() & 0xff)
-# define bfin_irq_flags cpu_pda[blackfin_core_id()].imask
-#else
-extern unsigned long bfin_irq_flags;
-#endif
-
-#ifdef CONFIG_IPIPE
-
-#include <linux/ipipe_trace.h>
+#include <linux/irqflags.h>
-void __ipipe_unstall_root(void);
-
-void __ipipe_restore_root(unsigned long flags);
-
-#ifdef CONFIG_DEBUG_HWERR
-# define __all_masked_irq_flags 0x3f
-# define __save_and_cli_hw(x) \
- __asm__ __volatile__( \
- "cli %0;" \
- "sti %1;" \
- : "=&d"(x) \
- : "d" (0x3F) \
- )
-#else
-# define __all_masked_irq_flags 0x1f
-# define __save_and_cli_hw(x) \
- __asm__ __volatile__( \
- "cli %0;" \
- : "=&d"(x) \
- )
-#endif
-
-#define irqs_enabled_from_flags_hw(x) ((x) != __all_masked_irq_flags)
-#define raw_irqs_disabled_flags(flags) (!irqs_enabled_from_flags_hw(flags))
-#define local_test_iflag_hw(x) irqs_enabled_from_flags_hw(x)
-
-#define local_save_flags(x) \
- do { \
- (x) = __ipipe_test_root() ? \
- __all_masked_irq_flags : bfin_irq_flags; \
- barrier(); \
- } while (0)
-
-#define local_irq_save(x) \
- do { \
- (x) = __ipipe_test_and_stall_root() ? \
- __all_masked_irq_flags : bfin_irq_flags; \
- barrier(); \
- } while (0)
-
-static inline void local_irq_restore(unsigned long x)
-{
- barrier();
- __ipipe_restore_root(x == __all_masked_irq_flags);
-}
-
-#define local_irq_disable() \
- do { \
- __ipipe_stall_root(); \
- barrier(); \
- } while (0)
-
-static inline void local_irq_enable(void)
-{
- barrier();
- __ipipe_unstall_root();
-}
-
-#define irqs_disabled() __ipipe_test_root()
-
-#define local_save_flags_hw(x) \
- __asm__ __volatile__( \
- "cli %0;" \
- "sti %0;" \
- : "=d"(x) \
- )
-
-#define irqs_disabled_hw() \
- ({ \
- unsigned long flags; \
- local_save_flags_hw(flags); \
- !irqs_enabled_from_flags_hw(flags); \
- })
-
-static inline unsigned long raw_mangle_irq_bits(int virt, unsigned long real)
-{
- /* Merge virtual and real interrupt mask bits into a single
- 32bit word. */
- return (real & ~(1 << 31)) | ((virt != 0) << 31);
-}
-
-static inline int raw_demangle_irq_bits(unsigned long *x)
-{
- int virt = (*x & (1 << 31)) != 0;
- *x &= ~(1L << 31);
- return virt;
-}
-
-#ifdef CONFIG_IPIPE_TRACE_IRQSOFF
-
-#define local_irq_disable_hw() \
- do { \
- int _tmp_dummy; \
- if (!irqs_disabled_hw()) \
- ipipe_trace_begin(0x80000000); \
- __asm__ __volatile__ ("cli %0;" : "=d" (_tmp_dummy) : ); \
- } while (0)
-
-#define local_irq_enable_hw() \
- do { \
- if (irqs_disabled_hw()) \
- ipipe_trace_end(0x80000000); \
- __asm__ __volatile__ ("sti %0;" : : "d"(bfin_irq_flags)); \
- } while (0)
-
-#define local_irq_save_hw(x) \
- do { \
- __save_and_cli_hw(x); \
- if (local_test_iflag_hw(x)) \
- ipipe_trace_begin(0x80000001); \
- } while (0)
-
-#define local_irq_restore_hw(x) \
- do { \
- if (local_test_iflag_hw(x)) { \
- ipipe_trace_end(0x80000001); \
- local_irq_enable_hw_notrace(); \
- } \
- } while (0)
-
-#define local_irq_disable_hw_notrace() \
- do { \
- int _tmp_dummy; \
- __asm__ __volatile__ ("cli %0;" : "=d" (_tmp_dummy) : ); \
- } while (0)
-
-#define local_irq_enable_hw_notrace() \
- __asm__ __volatile__( \
- "sti %0;" \
- : \
- : "d"(bfin_irq_flags) \
- )
-
-#define local_irq_save_hw_notrace(x) __save_and_cli_hw(x)
-
-#define local_irq_restore_hw_notrace(x) \
- do { \
- if (local_test_iflag_hw(x)) \
- local_irq_enable_hw_notrace(); \
- } while (0)
-
-#else /* CONFIG_IPIPE_TRACE_IRQSOFF */
-
-#define local_irq_enable_hw() \
- __asm__ __volatile__( \
- "sti %0;" \
- : \
- : "d"(bfin_irq_flags) \
- )
-
-#define local_irq_disable_hw() \
- do { \
- int _tmp_dummy; \
- __asm__ __volatile__ ( \
- "cli %0;" \
- : "=d" (_tmp_dummy)); \
- } while (0)
-
-#define local_irq_restore_hw(x) \
- do { \
- if (irqs_enabled_from_flags_hw(x)) \
- local_irq_enable_hw(); \
- } while (0)
-
-#define local_irq_save_hw(x) __save_and_cli_hw(x)
-
-#define local_irq_disable_hw_notrace() local_irq_disable_hw()
-#define local_irq_enable_hw_notrace() local_irq_enable_hw()
-#define local_irq_save_hw_notrace(x) local_irq_save_hw(x)
-#define local_irq_restore_hw_notrace(x) local_irq_restore_hw(x)
-
-#endif /* CONFIG_IPIPE_TRACE_IRQSOFF */
-
-#else /* !CONFIG_IPIPE */
-
-/*
- * Interrupt configuring macros.
- */
-#define local_irq_disable() \
- do { \
- int __tmp_dummy; \
- __asm__ __volatile__( \
- "cli %0;" \
- : "=d" (__tmp_dummy) \
- ); \
- } while (0)
-
-#define local_irq_enable() \
- __asm__ __volatile__( \
- "sti %0;" \
- : \
- : "d" (bfin_irq_flags) \
- )
-
-#ifdef CONFIG_DEBUG_HWERR
-# define __save_and_cli(x) \
- __asm__ __volatile__( \
- "cli %0;" \
- "sti %1;" \
- : "=&d" (x) \
- : "d" (0x3F) \
- )
-#else
-# define __save_and_cli(x) \
- __asm__ __volatile__( \
- "cli %0;" \
- : "=&d" (x) \
- )
-#endif
-
-#define local_save_flags(x) \
- __asm__ __volatile__( \
- "cli %0;" \
- "sti %0;" \
- : "=d" (x) \
- )
-
-#ifdef CONFIG_DEBUG_HWERR
-#define irqs_enabled_from_flags(x) (((x) & ~0x3f) != 0)
-#else
-#define irqs_enabled_from_flags(x) ((x) != 0x1f)
-#endif
-
-#define local_irq_restore(x) \
- do { \
- if (irqs_enabled_from_flags(x)) \
- local_irq_enable(); \
- } while (0)
-
-/* For spinlocks etc */
-#define local_irq_save(x) __save_and_cli(x)
-
-#define irqs_disabled() \
-({ \
- unsigned long flags; \
- local_save_flags(flags); \
- !irqs_enabled_from_flags(flags); \
-})
-
-#define local_irq_save_hw(x) local_irq_save(x)
-#define local_irq_restore_hw(x) local_irq_restore(x)
-#define local_irq_enable_hw() local_irq_enable()
-#define local_irq_disable_hw() local_irq_disable()
-#define irqs_disabled_hw() irqs_disabled()
+/* SYS_IRQS and NR_IRQS are defined in <mach-bf5xx/irq.h> */
+#include <mach/irq.h>
-#endif /* !CONFIG_IPIPE */
+/* Xenomai IPIPE helpers */
+#define local_irq_restore_hw(x) local_irq_restore(x)
+#define local_irq_save_hw(x) local_irq_save(x)
+#define local_irq_enable_hw(x) local_irq_enable(x)
+#define local_irq_disable_hw(x) local_irq_disable(x)
+#define irqs_disabled_hw(x) irqs_disabled(x)
#if ANOMALY_05000244 && defined(CONFIG_BFIN_ICACHE)
# define NOP_PAD_ANOMALY_05000244 "nop; nop;"
diff --git a/arch/blackfin/include/asm/irqflags.h b/arch/blackfin/include/asm/irqflags.h
new file mode 100644
index 00000000000..139cba4651b
--- /dev/null
+++ b/arch/blackfin/include/asm/irqflags.h
@@ -0,0 +1,63 @@
+/*
+ * interface to Blackfin CEC
+ *
+ * Copyright 2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __ASM_BFIN_IRQFLAGS_H__
+#define __ASM_BFIN_IRQFLAGS_H__
+
+#ifdef CONFIG_SMP
+# include <asm/pda.h>
+# include <asm/processor.h>
+/* Forward decl needed due to cdef inter dependencies */
+static inline uint32_t __pure bfin_dspid(void);
+# define blackfin_core_id() (bfin_dspid() & 0xff)
+# define bfin_irq_flags cpu_pda[blackfin_core_id()].imask
+#else
+extern unsigned long bfin_irq_flags;
+#endif
+
+static inline void bfin_sti(unsigned long flags)
+{
+ asm volatile("sti %0;" : : "d" (flags));
+}
+
+static inline unsigned long bfin_cli(void)
+{
+ unsigned long flags;
+ asm volatile("cli %0;" : "=d" (flags));
+ return flags;
+}
+
+static inline void raw_local_irq_disable(void)
+{
+ bfin_cli();
+}
+static inline void raw_local_irq_enable(void)
+{
+ bfin_sti(bfin_irq_flags);
+}
+
+#define raw_local_save_flags(flags) do { (flags) = bfin_read_IMASK(); } while (0)
+
+#define raw_irqs_disabled_flags(flags) (((flags) & ~0x3f) == 0)
+
+static inline void raw_local_irq_restore(unsigned long flags)
+{
+ if (!raw_irqs_disabled_flags(flags))
+ raw_local_irq_enable();
+}
+
+static inline unsigned long __raw_local_irq_save(void)
+{
+ unsigned long flags = bfin_cli();
+#ifdef CONFIG_DEBUG_HWERR
+ bfin_sti(0x3f);
+#endif
+ return flags;
+}
+#define raw_local_irq_save(flags) do { (flags) = __raw_local_irq_save(); } while (0)
+
+#endif
diff --git a/arch/blackfin/include/asm/mutex-dec.h b/arch/blackfin/include/asm/mutex-dec.h
deleted file mode 100644
index 0134151656a..00000000000
--- a/arch/blackfin/include/asm/mutex-dec.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * include/asm-generic/mutex-dec.h
- *
- * Generic implementation of the mutex fastpath, based on atomic
- * decrement/increment.
- */
-#ifndef _ASM_GENERIC_MUTEX_DEC_H
-#define _ASM_GENERIC_MUTEX_DEC_H
-
-/**
- * __mutex_fastpath_lock - try to take the lock by moving the count
- * from 1 to a 0 value
- * @count: pointer of type atomic_t
- * @fail_fn: function to call if the original value was not 1
- *
- * Change the count from 1 to a value lower than 1, and call <fail_fn> if
- * it wasn't 1 originally. This function MUST leave the value lower than
- * 1 even when the "1" assertion wasn't true.
- */
-static inline void
-__mutex_fastpath_lock(atomic_t *count, fastcall void (*fail_fn)(atomic_t *))
-{
- if (unlikely(atomic_dec_return(count) < 0))
- fail_fn(count);
- else
- smp_mb();
-}
-
-/**
- * __mutex_fastpath_lock_retval - try to take the lock by moving the count
- * from 1 to a 0 value
- * @count: pointer of type atomic_t
- * @fail_fn: function to call if the original value was not 1
- *
- * Change the count from 1 to a value lower than 1, and call <fail_fn> if
- * it wasn't 1 originally. This function returns 0 if the fastpath succeeds,
- * or anything the slow path function returns.
- */
-static inline int
-__mutex_fastpath_lock_retval(atomic_t *count, fastcall int (*fail_fn)(atomic_t *))
-{
- if (unlikely(atomic_dec_return(count) < 0))
- return fail_fn(count);
- else {
- smp_mb();
- return 0;
- }
-}
-
-/**
- * __mutex_fastpath_unlock - try to promote the count from 0 to 1
- * @count: pointer of type atomic_t
- * @fail_fn: function to call if the original value was not 0
- *
- * Try to promote the count from 0 to 1. If it wasn't 0, call <fail_fn>.
- * In the failure case, this function is allowed to either set the value to
- * 1, or to set it to a value lower than 1.
- *
- * If the implementation sets it to a value of lower than 1, then the
- * __mutex_slowpath_needs_to_unlock() macro needs to return 1, it needs
- * to return 0 otherwise.
- */
-static inline void
-__mutex_fastpath_unlock(atomic_t *count, fastcall void (*fail_fn)(atomic_t *))
-{
- smp_mb();
- if (unlikely(atomic_inc_return(count) <= 0))
- fail_fn(count);
-}
-
-#define __mutex_slowpath_needs_to_unlock() 1
-
-/**
- * __mutex_fastpath_trylock - try to acquire the mutex, without waiting
- *
- * @count: pointer of type atomic_t
- * @fail_fn: fallback function
- *
- * Change the count from 1 to a value lower than 1, and return 0 (failure)
- * if it wasn't 1 originally, or return 1 (success) otherwise. This function
- * MUST leave the value lower than 1 even when the "1" assertion wasn't true.
- * Additionally, if the value was < 0 originally, this function must not leave
- * it to 0 on failure.
- *
- * If the architecture has no effective trylock variant, it should call the
- * <fail_fn> spinlock-based trylock variant unconditionally.
- */
-static inline int
-__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
-{
- /*
- * We have two variants here. The cmpxchg based one is the best one
- * because it never induce a false contention state. It is included
- * here because architectures using the inc/dec algorithms over the
- * xchg ones are much more likely to support cmpxchg natively.
- *
- * If not we fall back to the spinlock based variant - that is
- * just as efficient (and simpler) as a 'destructive' probing of
- * the mutex state would be.
- */
-#ifdef __HAVE_ARCH_CMPXCHG
- if (likely(atomic_cmpxchg(count, 1, 0) == 1)) {
- smp_mb();
- return 1;
- }
- return 0;
-#else
- return fail_fn(count);
-#endif
-}
-
-#endif
diff --git a/arch/blackfin/include/asm/sections.h b/arch/blackfin/include/asm/sections.h
index 1443c3353a8..e7fd0ecd73f 100644
--- a/arch/blackfin/include/asm/sections.h
+++ b/arch/blackfin/include/asm/sections.h
@@ -4,4 +4,15 @@
/* nothing to see, move along */
#include <asm-generic/sections.h>
+/* only used when MTD_UCLINUX */
+extern unsigned long memory_mtd_start, memory_mtd_end, mtd_size;
+
+extern unsigned long _ramstart, _ramend, _rambase;
+extern unsigned long memory_start, memory_end, physical_mem_end;
+
+extern char _stext_l1[], _etext_l1[], _sdata_l1[], _edata_l1[], _sbss_l1[],
+ _ebss_l1[], _l1_lma_start[], _sdata_b_l1[], _sbss_b_l1[], _ebss_b_l1[],
+ _stext_l2[], _etext_l2[], _sdata_l2[], _edata_l2[], _sbss_l2[],
+ _ebss_l2[], _l2_lma_start[];
+
#endif
diff --git a/arch/blackfin/include/asm/system.h b/arch/blackfin/include/asm/system.h
index a4c8254bec5..294dbda2416 100644
--- a/arch/blackfin/include/asm/system.h
+++ b/arch/blackfin/include/asm/system.h
@@ -35,10 +35,10 @@
#define _BLACKFIN_SYSTEM_H
#include <linux/linkage.h>
-#include <linux/compiler.h>
+#include <linux/irqflags.h>
#include <mach/anomaly.h>
+#include <asm/cache.h>
#include <asm/pda.h>
-#include <asm/processor.h>
#include <asm/irq.h>
/*
diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h
index cf5066d3efd..da35133c171 100644
--- a/arch/blackfin/include/asm/unistd.h
+++ b/arch/blackfin/include/asm/unistd.h
@@ -380,8 +380,9 @@
#define __NR_inotify_init1 365
#define __NR_preadv 366
#define __NR_pwritev 367
+#define __NR_rt_tgsigqueueinfo 368
-#define __NR_syscall 368
+#define __NR_syscall 369
#define NR_syscalls __NR_syscall
/* Old optional stuff no one actually uses */
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile
index fd4d4328a0f..3731088e181 100644
--- a/arch/blackfin/kernel/Makefile
+++ b/arch/blackfin/kernel/Makefile
@@ -15,6 +15,10 @@ else
obj-y += time.o
endif
+obj-$(CONFIG_FUNCTION_TRACER) += ftrace-entry.o
+obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
+CFLAGS_REMOVE_ftrace.o = -pg
+
obj-$(CONFIG_IPIPE) += ipipe.o
obj-$(CONFIG_IPIPE_TRACE_MCOUNT) += mcount.o
obj-$(CONFIG_BFIN_GPTIMERS) += gptimers.o
@@ -23,6 +27,7 @@ obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KGDB_TESTS) += kgdb_test.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_STACKTRACE) += stacktrace.o
# the kgdb test puts code into L2 and without linker
# relaxation, we need to force long calls to/from it
diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c
index 763ed84ba45..e0bf8cc0690 100644
--- a/arch/blackfin/kernel/bfin_dma_5xx.c
+++ b/arch/blackfin/kernel/bfin_dma_5xx.c
@@ -453,10 +453,10 @@ void *dma_memcpy(void *pdst, const void *psrc, size_t size)
unsigned long src = (unsigned long)psrc;
size_t bulk, rest;
- if (bfin_addr_dcachable(src))
+ if (bfin_addr_dcacheable(src))
blackfin_dcache_flush_range(src, src + size);
- if (bfin_addr_dcachable(dst))
+ if (bfin_addr_dcacheable(dst))
blackfin_dcache_invalidate_range(dst, dst + size);
bulk = size & ~0xffff;
diff --git a/arch/blackfin/kernel/bfin_ksyms.c b/arch/blackfin/kernel/bfin_ksyms.c
index 53e893ff708..aa05e638fb7 100644
--- a/arch/blackfin/kernel/bfin_ksyms.c
+++ b/arch/blackfin/kernel/bfin_ksyms.c
@@ -103,3 +103,8 @@ EXPORT_SYMBOL(__raw_smp_mark_barrier_asm);
EXPORT_SYMBOL(__raw_smp_check_barrier_asm);
#endif
#endif
+
+#ifdef CONFIG_FUNCTION_TRACER
+extern void _mcount(void);
+EXPORT_SYMBOL(_mcount);
+#endif
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
index 87463ce87f5..784923e52a9 100644
--- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
+++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
@@ -151,7 +151,7 @@ static noinline int dcplb_miss(unsigned int cpu)
d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
#ifdef CONFIG_BFIN_DCACHE
- if (bfin_addr_dcachable(addr)) {
+ if (bfin_addr_dcacheable(addr)) {
d_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND;
#ifdef CONFIG_BFIN_WT
d_data |= CPLB_L1_AOW | CPLB_WT;
diff --git a/arch/blackfin/kernel/cplb-nompu/cplbmgr.c b/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
index 8cbb47c7b66..12b030842fd 100644
--- a/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
+++ b/arch/blackfin/kernel/cplb-nompu/cplbmgr.c
@@ -28,6 +28,7 @@
#include <asm/cplbinit.h>
#include <asm/cplb.h>
#include <asm/mmu_context.h>
+#include <asm/traps.h>
/*
* WARNING
@@ -100,28 +101,6 @@ static inline void write_icplb_data(int cpu, int idx, unsigned long data,
#endif
}
-/*
- * Given the contents of the status register, return the index of the
- * CPLB that caused the fault.
- */
-static inline int faulting_cplb_index(int status)
-{
- int signbits = __builtin_bfin_norm_fr1x32(status & 0xFFFF);
- return 30 - signbits;
-}
-
-/*
- * Given the contents of the status register and the DCPLB_DATA contents,
- * return true if a write access should be permitted.
- */
-static inline int write_permitted(int status, unsigned long data)
-{
- if (status & FAULT_USERSUPV)
- return !!(data & CPLB_SUPV_WR);
- else
- return !!(data & CPLB_USER_WR);
-}
-
/* Counters to implement round-robin replacement. */
static int icplb_rr_index[NR_CPUS] PDT_ATTR;
static int dcplb_rr_index[NR_CPUS] PDT_ATTR;
@@ -245,43 +224,16 @@ MGR_ATTR static int dcplb_miss(int cpu)
return CPLB_RELOADED;
}
-MGR_ATTR static noinline int dcplb_protection_fault(int cpu)
-{
- int status = bfin_read_DCPLB_STATUS();
-
- nr_dcplb_prot[cpu]++;
-
- if (likely(status & FAULT_RW)) {
- int idx = faulting_cplb_index(status);
- unsigned long regaddr = DCPLB_DATA0 + idx * 4;
- unsigned long data = bfin_read32(regaddr);
-
- /* Check if fault is to dirty a clean page */
- if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) &&
- write_permitted(status, data)) {
-
- dcplb_tbl[cpu][idx].data = data;
- bfin_write32(regaddr, data);
- return CPLB_RELOADED;
- }
- }
-
- return CPLB_PROT_VIOL;
-}
-
MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs)
{
int cause = seqstat & 0x3f;
unsigned int cpu = smp_processor_id();
switch (cause) {
- case 0x2C:
+ case VEC_CPLB_I_M:
return icplb_miss(cpu);
- case 0x26:
+ case VEC_CPLB_M:
return dcplb_miss(cpu);
default:
- if (unlikely(cause == 0x23))
- return dcplb_protection_fault(cpu);
-
return CPLB_UNKNOWN_ERR;
}
}
diff --git a/arch/blackfin/kernel/early_printk.c b/arch/blackfin/kernel/early_printk.c
index 3302719173c..2ab56811841 100644
--- a/arch/blackfin/kernel/early_printk.c
+++ b/arch/blackfin/kernel/early_printk.c
@@ -202,11 +202,15 @@ asmlinkage void __init init_early_exception_vectors(void)
asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr)
{
/* This can happen before the uart is initialized, so initialize
- * the UART now
+ * the UART now (but only if we are running on the processor we think
+ * we are compiled for - otherwise we write to MMRs that don't exist,
+ * and cause other problems. Nothing comes out the UART, but it does
+ * end up in the __buf_log.
*/
- if (likely(early_console == NULL))
+ if (likely(early_console == NULL) && CPUID == bfin_cpuid())
setup_early_printk(DEFAULT_EARLY_PORT);
+ printk(KERN_EMERG "Early panic\n");
dump_bfin_mem(fp);
show_regs(fp);
dump_bfin_trace_buffer();
diff --git a/arch/blackfin/kernel/ftrace-entry.S b/arch/blackfin/kernel/ftrace-entry.S
new file mode 100644
index 00000000000..6980b7a0615
--- /dev/null
+++ b/arch/blackfin/kernel/ftrace-entry.S
@@ -0,0 +1,140 @@
+/*
+ * mcount and friends -- ftrace stuff
+ *
+ * Copyright (C) 2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/linkage.h>
+#include <asm/ftrace.h>
+
+.text
+
+/* GCC will have called us before setting up the function prologue, so we
+ * can clobber the normal scratch registers, but we need to make sure to
+ * save/restore the registers used for argument passing (R0-R2) in case
+ * the profiled function is using them. With data registers, R3 is the
+ * only one we can blow away. With pointer registers, we have P0-P2.
+ *
+ * Upon entry, the RETS will point to the top of the current profiled
+ * function. And since GCC setup the frame for us, the previous function
+ * will be waiting there. mmmm pie.
+ */
+ENTRY(__mcount)
+ /* save third function arg early so we can do testing below */
+ [--sp] = r2;
+
+ /* load the function pointer to the tracer */
+ p0.l = _ftrace_trace_function;
+ p0.h = _ftrace_trace_function;
+ r3 = [p0];
+
+ /* optional micro optimization: don't call the stub tracer */
+ r2.l = _ftrace_stub;
+ r2.h = _ftrace_stub;
+ cc = r2 == r3;
+ if ! cc jump .Ldo_trace;
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ /* if the ftrace_graph_return function pointer is not set to
+ * the ftrace_stub entry, call prepare_ftrace_return().
+ */
+ p0.l = _ftrace_graph_return;
+ p0.h = _ftrace_graph_return;
+ r3 = [p0];
+ cc = r2 == r3;
+ if ! cc jump _ftrace_graph_caller;
+
+ /* similarly, if the ftrace_graph_entry function pointer is not
+ * set to the ftrace_graph_entry_stub entry, ...
+ */
+ p0.l = _ftrace_graph_entry;
+ p0.h = _ftrace_graph_entry;
+ r2.l = _ftrace_graph_entry_stub;
+ r2.h = _ftrace_graph_entry_stub;
+ r3 = [p0];
+ cc = r2 == r3;
+ if ! cc jump _ftrace_graph_caller;
+#endif
+
+ r2 = [sp++];
+ rts;
+
+.Ldo_trace:
+
+ /* save first/second function arg and the return register */
+ [--sp] = r0;
+ [--sp] = r1;
+ [--sp] = rets;
+
+ /* setup the tracer function */
+ p0 = r3;
+
+ /* tracer(ulong frompc, ulong selfpc):
+ * frompc: the pc that did the call to ...
+ * selfpc: ... this location
+ * the selfpc itself will need adjusting for the mcount call
+ */
+ r1 = rets;
+ r0 = [fp + 4];
+ r1 += -MCOUNT_INSN_SIZE;
+
+ /* call the tracer */
+ call (p0);
+
+ /* restore state and get out of dodge */
+.Lfinish_trace:
+ rets = [sp++];
+ r1 = [sp++];
+ r0 = [sp++];
+ r2 = [sp++];
+
+.globl _ftrace_stub
+_ftrace_stub:
+ rts;
+ENDPROC(__mcount)
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+/* The prepare_ftrace_return() function is similar to the trace function
+ * except it takes a pointer to the location of the frompc. This is so
+ * the prepare_ftrace_return() can hijack it temporarily for probing
+ * purposes.
+ */
+ENTRY(_ftrace_graph_caller)
+ /* save first/second function arg and the return register */
+ [--sp] = r0;
+ [--sp] = r1;
+ [--sp] = rets;
+
+ r0 = fp;
+ r1 = rets;
+ r0 += 4;
+ r1 += -MCOUNT_INSN_SIZE;
+ call _prepare_ftrace_return;
+
+ jump .Lfinish_trace;
+ENDPROC(_ftrace_graph_caller)
+
+/* Undo the rewrite caused by ftrace_graph_caller(). The common function
+ * ftrace_return_to_handler() will return the original rets so we can
+ * restore it and be on our way.
+ */
+ENTRY(_return_to_handler)
+ /* make sure original return values are saved */
+ [--sp] = p0;
+ [--sp] = r0;
+ [--sp] = r1;
+
+ /* get original return address */
+ call _ftrace_return_to_handler;
+ rets = r0;
+
+ /* anomaly 05000371 - make sure we have at least three instructions
+ * between rets setting and the return
+ */
+ r1 = [sp++];
+ r0 = [sp++];
+ p0 = [sp++];
+ rts;
+ENDPROC(_return_to_handler)
+#endif
diff --git a/arch/blackfin/kernel/ftrace.c b/arch/blackfin/kernel/ftrace.c
new file mode 100644
index 00000000000..905bfc40a00
--- /dev/null
+++ b/arch/blackfin/kernel/ftrace.c
@@ -0,0 +1,42 @@
+/*
+ * ftrace graph code
+ *
+ * Copyright (C) 2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/ftrace.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/atomic.h>
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+/*
+ * Hook the return address and push it in the stack of return addrs
+ * in current thread info.
+ */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
+{
+ struct ftrace_graph_ent trace;
+ unsigned long return_hooker = (unsigned long)&return_to_handler;
+
+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
+ return;
+
+ if (ftrace_push_return_trace(*parent, self_addr, &trace.depth) == -EBUSY)
+ return;
+
+ trace.func = self_addr;
+
+ /* Only trace if the calling function expects to */
+ if (!ftrace_graph_entry(&trace)) {
+ current->curr_ret_stack--;
+ return;
+ }
+
+ /* all is well in the world ! hijack RETS ... */
+ *parent = return_hooker;
+}
+
+#endif
diff --git a/arch/blackfin/kernel/ipipe.c b/arch/blackfin/kernel/ipipe.c
index 5fc424803a1..d8cde1fc5cb 100644
--- a/arch/blackfin/kernel/ipipe.c
+++ b/arch/blackfin/kernel/ipipe.c
@@ -99,7 +99,7 @@ void __ipipe_handle_irq(unsigned irq, struct pt_regs *regs)
* interrupt.
*/
m_ack = (regs == NULL || irq == IRQ_SYSTMR || irq == IRQ_CORETMR);
- this_domain = ipipe_current_domain;
+ this_domain = __ipipe_current_domain;
if (unlikely(test_bit(IPIPE_STICKY_FLAG, &this_domain->irqs[irq].control)))
head = &this_domain->p_link;
@@ -212,7 +212,9 @@ void __ipipe_unstall_root_raw(void)
int __ipipe_syscall_root(struct pt_regs *regs)
{
+ struct ipipe_percpu_domain_data *p;
unsigned long flags;
+ int ret;
/*
* We need to run the IRQ tail hook whenever we don't
@@ -231,29 +233,31 @@ int __ipipe_syscall_root(struct pt_regs *regs)
/*
* This routine either returns:
* 0 -- if the syscall is to be passed to Linux;
- * 1 -- if the syscall should not be passed to Linux, and no
+ * >0 -- if the syscall should not be passed to Linux, and no
* tail work should be performed;
- * -1 -- if the syscall should not be passed to Linux but the
+ * <0 -- if the syscall should not be passed to Linux but the
* tail work has to be performed (for handling signals etc).
*/
- if (__ipipe_event_monitored_p(IPIPE_EVENT_SYSCALL) &&
- __ipipe_dispatch_event(IPIPE_EVENT_SYSCALL, regs) > 0) {
- if (ipipe_root_domain_p && !in_atomic()) {
- /*
- * Sync pending VIRQs before _TIF_NEED_RESCHED
- * is tested.
- */
- local_irq_save_hw(flags);
- if ((ipipe_root_cpudom_var(irqpend_himask) & IPIPE_IRQMASK_VIRT) != 0)
- __ipipe_sync_pipeline(IPIPE_IRQMASK_VIRT);
- local_irq_restore_hw(flags);
- return -1;
- }
+ if (!__ipipe_event_monitored_p(IPIPE_EVENT_SYSCALL))
+ return 0;
+
+ ret = __ipipe_dispatch_event(IPIPE_EVENT_SYSCALL, regs);
+
+ local_irq_save_hw(flags);
+
+ if (!__ipipe_root_domain_p) {
+ local_irq_restore_hw(flags);
return 1;
}
- return 0;
+ p = ipipe_root_cpudom_ptr();
+ if ((p->irqpend_himask & IPIPE_IRQMASK_VIRT) != 0)
+ __ipipe_sync_pipeline(IPIPE_IRQMASK_VIRT);
+
+ local_irq_restore_hw(flags);
+
+ return -ret;
}
unsigned long ipipe_critical_enter(void (*syncfn) (void))
@@ -329,9 +333,7 @@ asmlinkage void __ipipe_sync_root(void)
void ___ipipe_sync_pipeline(unsigned long syncmask)
{
- struct ipipe_domain *ipd = ipipe_current_domain;
-
- if (ipd == ipipe_root_domain) {
+ if (__ipipe_root_domain_p) {
if (test_bit(IPIPE_SYNCDEFER_FLAG, &ipipe_root_cpudom_var(status)))
return;
}
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c
index 80447f99c2b..6454babdfaf 100644
--- a/arch/blackfin/kernel/setup.c
+++ b/arch/blackfin/kernel/setup.c
@@ -1098,7 +1098,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
CPUID, bfin_cpuid());
seq_printf(m, "model name\t: ADSP-%s %lu(MHz CCLK) %lu(MHz SCLK) (%s)\n"
- "stepping\t: %d\n",
+ "stepping\t: %d ",
cpu, cclk/1000000, sclk/1000000,
#ifdef CONFIG_MPU
"mpu on",
@@ -1107,7 +1107,16 @@ static int show_cpuinfo(struct seq_file *m, void *v)
#endif
revid);
- seq_printf(m, "cpu MHz\t\t: %lu.%03lu/%lu.%03lu\n",
+ if (bfin_revid() != bfin_compiled_revid()) {
+ if (bfin_compiled_revid() == -1)
+ seq_printf(m, "(Compiled for Rev none)");
+ else if (bfin_compiled_revid() == 0xffff)
+ seq_printf(m, "(Compiled for Rev any)");
+ else
+ seq_printf(m, "(Compiled for Rev %d)", bfin_compiled_revid());
+ }
+
+ seq_printf(m, "\ncpu MHz\t\t: %lu.%03lu/%lu.%03lu\n",
cclk/1000000, cclk%1000000,
sclk/1000000, sclk%1000000);
seq_printf(m, "bogomips\t: %lu.%02lu\n"
@@ -1172,6 +1181,9 @@ static int show_cpuinfo(struct seq_file *m, void *v)
#ifdef __ARCH_SYNC_CORE_DCACHE
seq_printf(m, "SMP Dcache Flushes\t: %lu\n\n", cpudata->dcache_invld_count);
#endif
+#ifdef __ARCH_SYNC_CORE_ICACHE
+ seq_printf(m, "SMP Icache Flushes\t: %lu\n\n", cpudata->icache_invld_count);
+#endif
#ifdef CONFIG_BFIN_ICACHE_LOCK
switch ((cpudata->imemctl >> 3) & WAYALL_L) {
case WAY0_L:
diff --git a/arch/blackfin/kernel/stacktrace.c b/arch/blackfin/kernel/stacktrace.c
new file mode 100644
index 00000000000..30301e1eace
--- /dev/null
+++ b/arch/blackfin/kernel/stacktrace.c
@@ -0,0 +1,53 @@
+/*
+ * Blackfin stacktrace code (mostly copied from avr32)
+ *
+ * Copyright 2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <linux/thread_info.h>
+#include <linux/module.h>
+
+register unsigned long current_frame_pointer asm("FP");
+
+struct stackframe {
+ unsigned long fp;
+ unsigned long rets;
+};
+
+/*
+ * Save stack-backtrace addresses into a stack_trace buffer.
+ */
+void save_stack_trace(struct stack_trace *trace)
+{
+ unsigned long low, high;
+ unsigned long fp;
+ struct stackframe *frame;
+ int skip = trace->skip;
+
+ low = (unsigned long)task_stack_page(current);
+ high = low + THREAD_SIZE;
+ fp = current_frame_pointer;
+
+ while (fp >= low && fp <= (high - sizeof(*frame))) {
+ frame = (struct stackframe *)fp;
+
+ if (skip) {
+ skip--;
+ } else {
+ trace->entries[trace->nr_entries++] = frame->rets;
+ if (trace->nr_entries >= trace->max_entries)
+ break;
+ }
+
+ /*
+ * The next frame must be at a higher address than the
+ * current frame.
+ */
+ low = fp + sizeof(*frame);
+ fp = frame->fp;
+ }
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c
index aa76dfb0226..d279552fe9b 100644
--- a/arch/blackfin/kernel/traps.c
+++ b/arch/blackfin/kernel/traps.c
@@ -27,6 +27,7 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <linux/bug.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -238,6 +239,11 @@ asmlinkage void double_fault_c(struct pt_regs *fp)
}
+static int kernel_mode_regs(struct pt_regs *regs)
+{
+ return regs->ipend & 0xffc0;
+}
+
asmlinkage void trap_c(struct pt_regs *fp)
{
#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
@@ -246,6 +252,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
unsigned int cpu = smp_processor_id();
#endif
+ const char *strerror = NULL;
int sig = 0;
siginfo_t info;
unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
@@ -259,27 +266,10 @@ asmlinkage void trap_c(struct pt_regs *fp)
* double faults if the stack has become corrupt
*/
- /* If the fault was caused by a kernel thread, or interrupt handler
- * we will kernel panic, so the system reboots.
- * If KGDB is enabled, don't set this for kernel breakpoints
- */
-
- /* TODO: check to see if we are in some sort of deferred HWERR
- * that we should be able to recover from, not kernel panic
- */
- if ((bfin_read_IPEND() & 0xFFC0) && (trapnr != VEC_STEP)
-#ifdef CONFIG_KGDB
- && (trapnr != VEC_EXCPT02)
+#ifndef CONFIG_KGDB
+ /* IPEND is skipped if KGDB isn't enabled (see entry code) */
+ fp->ipend = bfin_read_IPEND();
#endif
- ){
- console_verbose();
- oops_in_progress = 1;
- } else if (current) {
- if (current->mm == NULL) {
- console_verbose();
- oops_in_progress = 1;
- }
- }
/* trap_c() will be called for exceptions. During exceptions
* processing, the pc value should be set with retx value.
@@ -307,15 +297,15 @@ asmlinkage void trap_c(struct pt_regs *fp)
sig = SIGTRAP;
CHK_DEBUGGER_TRAP_MAYBE();
/* Check if this is a breakpoint in kernel space */
- if (fp->ipend & 0xffc0)
- return;
+ if (kernel_mode_regs(fp))
+ goto traps_done;
else
break;
/* 0x03 - User Defined, userspace stack overflow */
case VEC_EXCPT03:
info.si_code = SEGV_STACKFLOW;
sig = SIGSEGV;
- verbose_printk(KERN_NOTICE EXC_0x03(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x03(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x02 - KGDB initial connection and break signal trap */
@@ -324,7 +314,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
info.si_code = TRAP_ILLTRAP;
sig = SIGTRAP;
CHK_DEBUGGER_TRAP();
- return;
+ goto traps_done;
#endif
/* 0x04 - User Defined */
/* 0x05 - User Defined */
@@ -344,7 +334,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
case VEC_EXCPT04 ... VEC_EXCPT15:
info.si_code = ILL_ILLPARAOP;
sig = SIGILL;
- verbose_printk(KERN_NOTICE EXC_0x04(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x04(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x10 HW Single step, handled here */
@@ -353,15 +343,15 @@ asmlinkage void trap_c(struct pt_regs *fp)
sig = SIGTRAP;
CHK_DEBUGGER_TRAP_MAYBE();
/* Check if this is a single step in kernel space */
- if (fp->ipend & 0xffc0)
- return;
+ if (kernel_mode_regs(fp))
+ goto traps_done;
else
break;
/* 0x11 - Trace Buffer Full, handled here */
case VEC_OVFLOW:
info.si_code = TRAP_TRACEFLOW;
sig = SIGTRAP;
- verbose_printk(KERN_NOTICE EXC_0x11(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x11(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x12 - Reserved, Caught by default */
@@ -381,37 +371,54 @@ asmlinkage void trap_c(struct pt_regs *fp)
/* 0x20 - Reserved, Caught by default */
/* 0x21 - Undefined Instruction, handled here */
case VEC_UNDEF_I:
+#ifdef CONFIG_BUG
+ if (kernel_mode_regs(fp)) {
+ switch (report_bug(fp->pc, fp)) {
+ case BUG_TRAP_TYPE_NONE:
+ break;
+ case BUG_TRAP_TYPE_WARN:
+ dump_bfin_trace_buffer();
+ fp->pc += 2;
+ goto traps_done;
+ case BUG_TRAP_TYPE_BUG:
+ /* call to panic() will dump trace, and it is
+ * off at this point, so it won't be clobbered
+ */
+ panic("BUG()");
+ }
+ }
+#endif
info.si_code = ILL_ILLOPC;
sig = SIGILL;
- verbose_printk(KERN_NOTICE EXC_0x21(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x21(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x22 - Illegal Instruction Combination, handled here */
case VEC_ILGAL_I:
info.si_code = ILL_ILLPARAOP;
sig = SIGILL;
- verbose_printk(KERN_NOTICE EXC_0x22(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x22(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x23 - Data CPLB protection violation, handled here */
case VEC_CPLB_VL:
info.si_code = ILL_CPLB_VI;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE EXC_0x23(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x23(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x24 - Data access misaligned, handled here */
case VEC_MISALI_D:
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE EXC_0x24(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x24(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x25 - Unrecoverable Event, handled here */
case VEC_UNCOV:
info.si_code = ILL_ILLEXCPT;
sig = SIGILL;
- verbose_printk(KERN_NOTICE EXC_0x25(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x25(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x26 - Data CPLB Miss, normal case is handled in _cplb_hdr,
@@ -419,7 +426,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
case VEC_CPLB_M:
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE EXC_0x26(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x26(KERN_NOTICE);
break;
/* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero, handled here */
case VEC_CPLB_MHIT:
@@ -427,10 +434,10 @@ asmlinkage void trap_c(struct pt_regs *fp)
sig = SIGSEGV;
#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
if (cpu_pda[cpu].dcplb_fault_addr < FIXED_CODE_START)
- verbose_printk(KERN_NOTICE "NULL pointer access\n");
+ strerror = KERN_NOTICE "NULL pointer access\n";
else
#endif
- verbose_printk(KERN_NOTICE EXC_0x27(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x27(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x28 - Emulation Watchpoint, handled here */
@@ -440,8 +447,8 @@ asmlinkage void trap_c(struct pt_regs *fp)
pr_debug(EXC_0x28(KERN_DEBUG));
CHK_DEBUGGER_TRAP_MAYBE();
/* Check if this is a watchpoint in kernel space */
- if (fp->ipend & 0xffc0)
- return;
+ if (kernel_mode_regs(fp))
+ goto traps_done;
else
break;
#ifdef CONFIG_BF535
@@ -449,7 +456,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
case VEC_ISTRU_VL: /* ADSP-BF535 only (MH) */
info.si_code = BUS_OPFETCH;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE "BF535: VEC_ISTRU_VL\n");
+ strerror = KERN_NOTICE "BF535: VEC_ISTRU_VL\n";
CHK_DEBUGGER_TRAP_MAYBE();
break;
#else
@@ -459,21 +466,21 @@ asmlinkage void trap_c(struct pt_regs *fp)
case VEC_MISALI_I:
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE EXC_0x2A(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x2A(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2B - Instruction CPLB protection violation, handled here */
case VEC_CPLB_I_VL:
info.si_code = ILL_CPLB_VI;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE EXC_0x2B(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x2B(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2C - Instruction CPLB miss, handled in _cplb_hdr */
case VEC_CPLB_I_M:
info.si_code = ILL_CPLB_MISS;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE EXC_0x2C(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x2C(KERN_NOTICE);
break;
/* 0x2D - Instruction CPLB Multiple Hits, handled here */
case VEC_CPLB_I_MHIT:
@@ -481,17 +488,17 @@ asmlinkage void trap_c(struct pt_regs *fp)
sig = SIGSEGV;
#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
if (cpu_pda[cpu].icplb_fault_addr < FIXED_CODE_START)
- verbose_printk(KERN_NOTICE "Jump to NULL address\n");
+ strerror = KERN_NOTICE "Jump to NULL address\n";
else
#endif
- verbose_printk(KERN_NOTICE EXC_0x2D(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x2D(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2E - Illegal use of Supervisor Resource, handled here */
case VEC_ILL_RES:
info.si_code = ILL_PRVOPC;
sig = SIGILL;
- verbose_printk(KERN_NOTICE EXC_0x2E(KERN_NOTICE));
+ strerror = KERN_NOTICE EXC_0x2E(KERN_NOTICE);
CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2F - Reserved, Caught by default */
@@ -519,17 +526,17 @@ asmlinkage void trap_c(struct pt_regs *fp)
case (SEQSTAT_HWERRCAUSE_SYSTEM_MMR):
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE HWC_x2(KERN_NOTICE));
+ strerror = KERN_NOTICE HWC_x2(KERN_NOTICE);
break;
/* External Memory Addressing Error */
case (SEQSTAT_HWERRCAUSE_EXTERN_ADDR):
info.si_code = BUS_ADRERR;
sig = SIGBUS;
- verbose_printk(KERN_NOTICE HWC_x3(KERN_NOTICE));
+ strerror = KERN_NOTICE HWC_x3(KERN_NOTICE);
break;
/* Performance Monitor Overflow */
case (SEQSTAT_HWERRCAUSE_PERF_FLOW):
- verbose_printk(KERN_NOTICE HWC_x12(KERN_NOTICE));
+ strerror = KERN_NOTICE HWC_x12(KERN_NOTICE);
break;
/* RAISE 5 instruction */
case (SEQSTAT_HWERRCAUSE_RAISE_5):
@@ -546,7 +553,6 @@ asmlinkage void trap_c(struct pt_regs *fp)
* if we get here we hit a reserved one, so panic
*/
default:
- oops_in_progress = 1;
info.si_code = ILL_ILLPARAOP;
sig = SIGILL;
verbose_printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n",
@@ -557,6 +563,16 @@ asmlinkage void trap_c(struct pt_regs *fp)
BUG_ON(sig == 0);
+ /* If the fault was caused by a kernel thread, or interrupt handler
+ * we will kernel panic, so the system reboots.
+ */
+ if (kernel_mode_regs(fp) || (current && !current->mm)) {
+ console_verbose();
+ oops_in_progress = 1;
+ if (strerror)
+ verbose_printk(strerror);
+ }
+
if (sig != SIGTRAP) {
dump_bfin_process(fp);
dump_bfin_mem(fp);
@@ -606,8 +622,8 @@ asmlinkage void trap_c(struct pt_regs *fp)
if (ANOMALY_05000461 && trapnr == VEC_HWERR && !access_ok(VERIFY_READ, fp->pc, 8))
fp->pc = SAFE_USER_INSTRUCTION;
+ traps_done:
trace_buffer_restore(j);
- return;
}
/* Typical exception handling routines */
@@ -792,6 +808,18 @@ void dump_bfin_trace_buffer(void)
}
EXPORT_SYMBOL(dump_bfin_trace_buffer);
+#ifdef CONFIG_BUG
+int is_valid_bugaddr(unsigned long addr)
+{
+ unsigned short opcode;
+
+ if (!get_instruction(&opcode, (unsigned short *)addr))
+ return 0;
+
+ return opcode == BFIN_BUG_OPCODE;
+}
+#endif
+
/*
* Checks to see if the address pointed to is either a
* 16-bit CALL instruction, or a 32-bit CALL instruction
diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S
index 8b67167cb4f..6ac307ca0d8 100644
--- a/arch/blackfin/kernel/vmlinux.lds.S
+++ b/arch/blackfin/kernel/vmlinux.lds.S
@@ -54,6 +54,7 @@ SECTIONS
SCHED_TEXT
#endif
LOCK_TEXT
+ IRQENTRY_TEXT
KPROBES_TEXT
*(.text.*)
*(.fixup)
@@ -166,6 +167,20 @@ SECTIONS
}
PERCPU(4)
SECURITY_INIT
+
+ /* we have to discard exit text and such at runtime, not link time, to
+ * handle embedded cross-section references (alt instructions, bug
+ * table, eh_frame, etc...)
+ */
+ .exit.text :
+ {
+ EXIT_TEXT
+ }
+ .exit.data :
+ {
+ EXIT_DATA
+ }
+
.init.ramfs :
{
. = ALIGN(4);
@@ -264,8 +279,6 @@ SECTIONS
/DISCARD/ :
{
- EXIT_TEXT
- EXIT_DATA
*(.exitcall.exit)
}
}
diff --git a/arch/blackfin/lib/checksum.c b/arch/blackfin/lib/checksum.c
index 762a7f02970..cd605e7d851 100644
--- a/arch/blackfin/lib/checksum.c
+++ b/arch/blackfin/lib/checksum.c
@@ -116,6 +116,7 @@ __sum16 ip_compute_csum(const void *buff, int len)
{
return (__force __sum16)~do_csum(buff, len);
}
+EXPORT_SYMBOL(ip_compute_csum);
/*
* copy from fs while checksumming, otherwise like csum_partial
@@ -130,6 +131,7 @@ csum_partial_copy_from_user(const void __user *src, void *dst,
memcpy(dst, (__force void *)src, len);
return csum_partial(dst, len, sum);
}
+EXPORT_SYMBOL(csum_partial_copy_from_user);
/*
* copy from ds while checksumming, otherwise like csum_partial
diff --git a/arch/blackfin/mach-bf518/boards/ezbrd.c b/arch/blackfin/mach-bf518/boards/ezbrd.c
index 62bba09bcce..1382f038235 100644
--- a/arch/blackfin/mach-bf518/boards/ezbrd.c
+++ b/arch/blackfin/mach-bf518/boards/ezbrd.c
@@ -246,7 +246,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
.modalias = "m25p80", /* Name of spi_driver for this device */
.max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */
.bus_num = 0, /* Framework bus number */
- .chip_select = 1, /* Framework chip select. On STAMP537 it is SPISSEL1*/
+ .chip_select = 2, /* On BF518F-EZBRD it's SPI0_SSEL2 */
.platform_data = &bfin_spi_flash_data,
.controller_data = &spi_flash_chip_info,
.mode = SPI_MODE_3,
@@ -369,6 +369,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI0,
.end = CH_SPI0,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI0,
+ .end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
},
};
@@ -399,6 +404,11 @@ static struct resource bfin_spi1_resource[] = {
[1] = {
.start = CH_SPI1,
.end = CH_SPI1,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI1,
+ .end = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/blackfin/mach-bf527/boards/cm_bf527.c b/arch/blackfin/mach-bf527/boards/cm_bf527.c
index 6d6f9effa0b..1eaf27ff722 100644
--- a/arch/blackfin/mach-bf527/boards/cm_bf527.c
+++ b/arch/blackfin/mach-bf527/boards/cm_bf527.c
@@ -664,6 +664,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/blackfin/mach-bf527/boards/ezbrd.c b/arch/blackfin/mach-bf527/boards/ezbrd.c
index 1435c5d38cd..9f9c0005dcf 100644
--- a/arch/blackfin/mach-bf527/boards/ezbrd.c
+++ b/arch/blackfin/mach-bf527/boards/ezbrd.c
@@ -467,6 +467,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c
index 147edd1eb1a..3e5b7db6b06 100644
--- a/arch/blackfin/mach-bf527/boards/ezkit.c
+++ b/arch/blackfin/mach-bf527/boards/ezkit.c
@@ -723,6 +723,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/blackfin/mach-bf533/boards/H8606.c b/arch/blackfin/mach-bf533/boards/H8606.c
index 895f213ea45..38cf8ffd6d7 100644
--- a/arch/blackfin/mach-bf533/boards/H8606.c
+++ b/arch/blackfin/mach-bf533/boards/H8606.c
@@ -266,6 +266,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf533/boards/blackstamp.c b/arch/blackfin/mach-bf533/boards/blackstamp.c
index 0765872a8ad..9ecdc361fa6 100644
--- a/arch/blackfin/mach-bf533/boards/blackstamp.c
+++ b/arch/blackfin/mach-bf533/boards/blackstamp.c
@@ -162,6 +162,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf533/boards/cm_bf533.c b/arch/blackfin/mach-bf533/boards/cm_bf533.c
index a727e538fa2..1443e92d8b6 100644
--- a/arch/blackfin/mach-bf533/boards/cm_bf533.c
+++ b/arch/blackfin/mach-bf533/boards/cm_bf533.c
@@ -160,6 +160,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf533/boards/ezkit.c b/arch/blackfin/mach-bf533/boards/ezkit.c
index 842f1c9c239..89a5ec4ca04 100644
--- a/arch/blackfin/mach-bf533/boards/ezkit.c
+++ b/arch/blackfin/mach-bf533/boards/ezkit.c
@@ -196,6 +196,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf533/boards/stamp.c b/arch/blackfin/mach-bf533/boards/stamp.c
index e19c565ade1..a68ade8a3ca 100644
--- a/arch/blackfin/mach-bf533/boards/stamp.c
+++ b/arch/blackfin/mach-bf533/boards/stamp.c
@@ -299,6 +299,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf537/boards/cm_bf537.c b/arch/blackfin/mach-bf537/boards/cm_bf537.c
index 4fee1967312..2a87d1cfcd0 100644
--- a/arch/blackfin/mach-bf537/boards/cm_bf537.c
+++ b/arch/blackfin/mach-bf537/boards/cm_bf537.c
@@ -182,8 +182,13 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
- }
+ },
};
/* SPI controller data */
diff --git a/arch/blackfin/mach-bf537/boards/minotaur.c b/arch/blackfin/mach-bf537/boards/minotaur.c
index 3c159819e55..399f81da7b9 100644
--- a/arch/blackfin/mach-bf537/boards/minotaur.c
+++ b/arch/blackfin/mach-bf537/boards/minotaur.c
@@ -184,6 +184,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/blackfin/mach-bf537/boards/pnav10.c b/arch/blackfin/mach-bf537/boards/pnav10.c
index 26707ce39f2..838240f151f 100644
--- a/arch/blackfin/mach-bf537/boards/pnav10.c
+++ b/arch/blackfin/mach-bf537/boards/pnav10.c
@@ -398,8 +398,13 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
- }
+ },
};
/* SPI controller data */
diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c
index dfb5036f8a6..ff7228caa7d 100644
--- a/arch/blackfin/mach-bf537/boards/stamp.c
+++ b/arch/blackfin/mach-bf537/boards/stamp.c
@@ -1345,7 +1345,7 @@ static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
#if defined(CONFIG_PMIC_ADP5520) || defined(CONFIG_PMIC_ADP5520_MODULE)
{
I2C_BOARD_INFO("pmic-adp5520", 0x32),
- .irq = IRQ_PF7,
+ .irq = IRQ_PG0,
.platform_data = (void *)&adp5520_pdev_data,
},
#endif
diff --git a/arch/blackfin/mach-bf537/boards/tcm_bf537.c b/arch/blackfin/mach-bf537/boards/tcm_bf537.c
index 28057459120..e523e6e610d 100644
--- a/arch/blackfin/mach-bf537/boards/tcm_bf537.c
+++ b/arch/blackfin/mach-bf537/boards/tcm_bf537.c
@@ -182,6 +182,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf538/boards/ezkit.c b/arch/blackfin/mach-bf538/boards/ezkit.c
index e37cb937888..57695b4c3c0 100644
--- a/arch/blackfin/mach-bf538/boards/ezkit.c
+++ b/arch/blackfin/mach-bf538/boards/ezkit.c
@@ -352,6 +352,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI0,
.end = CH_SPI0,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI0,
+ .end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
@@ -366,6 +371,11 @@ static struct resource bfin_spi1_resource[] = {
[1] = {
.start = CH_SPI1,
.end = CH_SPI1,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI1,
+ .end = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf548/boards/cm_bf548.c b/arch/blackfin/mach-bf548/boards/cm_bf548.c
index f53ad682530..f5a3c30a41b 100644
--- a/arch/blackfin/mach-bf548/boards/cm_bf548.c
+++ b/arch/blackfin/mach-bf548/boards/cm_bf548.c
@@ -612,6 +612,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI0,
.end = CH_SPI0,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI0,
+ .end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
@@ -626,6 +631,11 @@ static struct resource bfin_spi1_resource[] = {
[1] = {
.start = CH_SPI1,
.end = CH_SPI1,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI1,
+ .end = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c
index add5a17452c..805a57b5e65 100644
--- a/arch/blackfin/mach-bf548/boards/ezkit.c
+++ b/arch/blackfin/mach-bf548/boards/ezkit.c
@@ -396,6 +396,8 @@ static struct platform_device bfin_sir3_device = {
#endif
#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+#include <linux/smsc911x.h>
+
static struct resource smsc911x_resources[] = {
{
.name = "smsc911x-memory",
@@ -409,11 +411,22 @@ static struct resource smsc911x_resources[] = {
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
},
};
+
+static struct smsc911x_platform_config smsc911x_config = {
+ .flags = SMSC911X_USE_32BIT,
+ .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
+ .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
+ .phy_interface = PHY_INTERFACE_MODE_MII,
+};
+
static struct platform_device smsc911x_device = {
.name = "smsc911x",
.id = 0,
.num_resources = ARRAY_SIZE(smsc911x_resources),
.resource = smsc911x_resources,
+ .dev = {
+ .platform_data = &smsc911x_config,
+ },
};
#endif
@@ -741,6 +754,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI0,
.end = CH_SPI0,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI0,
+ .end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
@@ -755,6 +773,11 @@ static struct resource bfin_spi1_resource[] = {
[1] = {
.start = CH_SPI1,
.end = CH_SPI1,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI1,
+ .end = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-bf561/boards/cm_bf561.c b/arch/blackfin/mach-bf561/boards/cm_bf561.c
index 0dd9685e5d5..0c9d72c5f5b 100644
--- a/arch/blackfin/mach-bf561/boards/cm_bf561.c
+++ b/arch/blackfin/mach-bf561/boards/cm_bf561.c
@@ -177,8 +177,13 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
- }
+ },
};
/* SPI controller data */
diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c
index 0e2178a1aec..b5ef7ff7b7b 100644
--- a/arch/blackfin/mach-bf561/boards/ezkit.c
+++ b/arch/blackfin/mach-bf561/boards/ezkit.c
@@ -304,6 +304,11 @@ static struct resource bfin_spi0_resource[] = {
[1] = {
.start = CH_SPI,
.end = CH_SPI,
+ .flags = IORESOURCE_DMA,
+ },
+ [2] = {
+ .start = IRQ_SPI,
+ .end = IRQ_SPI,
.flags = IORESOURCE_IRQ,
}
};
diff --git a/arch/blackfin/mach-common/cache-c.c b/arch/blackfin/mach-common/cache-c.c
index e6ab1f81512..b59ce3cb380 100644
--- a/arch/blackfin/mach-common/cache-c.c
+++ b/arch/blackfin/mach-common/cache-c.c
@@ -16,9 +16,21 @@
void blackfin_invalidate_entire_dcache(void)
{
u32 dmem = bfin_read_DMEM_CONTROL();
- SSYNC();
bfin_write_DMEM_CONTROL(dmem & ~0xc);
SSYNC();
bfin_write_DMEM_CONTROL(dmem);
SSYNC();
}
+
+/* Invalidate the Entire Instruction cache by
+ * clearing IMC bit
+ */
+void blackfin_invalidate_entire_icache(void)
+{
+ u32 imem = bfin_read_IMEM_CONTROL();
+ bfin_write_IMEM_CONTROL(imem & ~0x4);
+ SSYNC();
+ bfin_write_IMEM_CONTROL(imem);
+ SSYNC();
+}
+
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index da0558ad1b1..31fa313e81c 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -42,6 +42,7 @@
#include <asm/thread_info.h> /* TIF_NEED_RESCHED */
#include <asm/asm-offsets.h>
#include <asm/trace.h>
+#include <asm/traps.h>
#include <asm/context.S>
@@ -84,13 +85,15 @@ ENTRY(_ex_workaround_261)
if !cc jump _bfin_return_from_exception;
/* fall through */
R7 = P4;
- R6 = 0x26; /* Data CPLB Miss */
+ R6 = VEC_CPLB_M; /* Data CPLB Miss */
cc = R6 == R7;
if cc jump _ex_dcplb_miss (BP);
- R6 = 0x23; /* Data CPLB Miss */
+#ifdef CONFIG_MPU
+ R6 = VEC_CPLB_VL; /* Data CPLB Violation */
cc = R6 == R7;
if cc jump _ex_dcplb_viol (BP);
- /* Handle 0x23 Data CPLB Protection Violation
+#endif
+ /* Handle Data CPLB Protection Violation
* and Data CPLB Multiple Hits - Linux Trap Zero
*/
jump _ex_trap_c;
@@ -270,7 +273,7 @@ ENTRY(_bfin_return_from_exception)
r6.l = lo(SEQSTAT_EXCAUSE);
r6.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6;
- r6 = 0x25;
+ r6 = VEC_UNCOV;
CC = R7 == R6;
if CC JUMP _double_fault;
#endif
@@ -1605,6 +1608,7 @@ ENTRY(_sys_call_table)
.long _sys_inotify_init1 /* 365 */
.long _sys_preadv
.long _sys_pwritev
+ .long _sys_rt_tgsigqueueinfo
.rept NR_syscalls-(.-_sys_call_table)/4
.long _sys_ni_syscall
diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c
index 3b8ebaee77f..61840059dfa 100644
--- a/arch/blackfin/mach-common/smp.c
+++ b/arch/blackfin/mach-common/smp.c
@@ -144,7 +144,7 @@ static void ipi_call_function(unsigned int cpu, struct ipi_message *msg)
static irqreturn_t ipi_handler(int irq, void *dev_instance)
{
- struct ipi_message *msg, *mg;
+ struct ipi_message *msg;
struct ipi_message_queue *msg_queue;
unsigned int cpu = smp_processor_id();
@@ -154,7 +154,8 @@ static irqreturn_t ipi_handler(int irq, void *dev_instance)
msg_queue->count++;
spin_lock(&msg_queue->lock);
- list_for_each_entry_safe(msg, mg, &msg_queue->head, list) {
+ while (!list_empty(&msg_queue->head)) {
+ msg = list_entry(msg_queue->head.next, typeof(*msg), list);
list_del(&msg->list);
switch (msg->type) {
case BFIN_IPI_RESCHEDULE:
@@ -221,7 +222,7 @@ int smp_call_function(void (*func)(void *info), void *info, int wait)
for_each_cpu_mask(cpu, callmap) {
msg_queue = &per_cpu(ipi_msg_queue, cpu);
spin_lock_irqsave(&msg_queue->lock, flags);
- list_add(&msg->list, &msg_queue->head);
+ list_add_tail(&msg->list, &msg_queue->head);
spin_unlock_irqrestore(&msg_queue->lock, flags);
platform_send_ipi_cpu(cpu);
}
@@ -261,7 +262,7 @@ int smp_call_function_single(int cpuid, void (*func) (void *info), void *info,
msg_queue = &per_cpu(ipi_msg_queue, cpu);
spin_lock_irqsave(&msg_queue->lock, flags);
- list_add(&msg->list, &msg_queue->head);
+ list_add_tail(&msg->list, &msg_queue->head);
spin_unlock_irqrestore(&msg_queue->lock, flags);
platform_send_ipi_cpu(cpu);
@@ -292,7 +293,7 @@ void smp_send_reschedule(int cpu)
msg_queue = &per_cpu(ipi_msg_queue, cpu);
spin_lock_irqsave(&msg_queue->lock, flags);
- list_add(&msg->list, &msg_queue->head);
+ list_add_tail(&msg->list, &msg_queue->head);
spin_unlock_irqrestore(&msg_queue->lock, flags);
platform_send_ipi_cpu(cpu);
@@ -320,7 +321,7 @@ void smp_send_stop(void)
for_each_cpu_mask(cpu, callmap) {
msg_queue = &per_cpu(ipi_msg_queue, cpu);
spin_lock_irqsave(&msg_queue->lock, flags);
- list_add(&msg->list, &msg_queue->head);
+ list_add_tail(&msg->list, &msg_queue->head);
spin_unlock_irqrestore(&msg_queue->lock, flags);
platform_send_ipi_cpu(cpu);
}
@@ -468,6 +469,17 @@ void smp_icache_flush_range_others(unsigned long start, unsigned long end)
}
EXPORT_SYMBOL_GPL(smp_icache_flush_range_others);
+#ifdef __ARCH_SYNC_CORE_ICACHE
+void resync_core_icache(void)
+{
+ unsigned int cpu = get_cpu();
+ blackfin_invalidate_entire_icache();
+ ++per_cpu(cpu_data, cpu).icache_invld_count;
+ put_cpu();
+}
+EXPORT_SYMBOL(resync_core_icache);
+#endif
+
#ifdef __ARCH_SYNC_CORE_DCACHE
unsigned long barrier_mask __attribute__ ((__section__(".l2.bss")));
diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig
index 783da855a2e..d6d35b2e5fe 100644
--- a/arch/mips/configs/bigsur_defconfig
+++ b/arch/mips/configs/bigsur_defconfig
@@ -963,7 +963,7 @@ CONFIG_EEPROM_LEGACY=y
CONFIG_SENSORS_PCF8574=y
# CONFIG_PCF8575 is not set
CONFIG_SENSORS_PCF8591=y
-CONFIG_SENSORS_MAX6875=y
+CONFIG_EEPROM_MAX6875=y
# CONFIG_SENSORS_TSL2550 is not set
CONFIG_I2C_DEBUG_CORE=y
CONFIG_I2C_DEBUG_ALGO=y
diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig
index 8426d3b9501..fadb351d249 100644
--- a/arch/mips/configs/mtx1_defconfig
+++ b/arch/mips/configs/mtx1_defconfig
@@ -1849,7 +1849,7 @@ CONFIG_EEPROM_LEGACY=m
CONFIG_SENSORS_PCF8574=m
CONFIG_SENSORS_PCA9539=m
CONFIG_SENSORS_PCF8591=m
-CONFIG_SENSORS_MAX6875=m
+CONFIG_EEPROM_MAX6875=m
# CONFIG_SENSORS_TSL2550 is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 93a61898b25..9fb344d5a86 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -93,10 +93,6 @@ config GENERIC_HWEIGHT
bool
default y
-config GENERIC_CALIBRATE_DELAY
- bool
- default y
-
config GENERIC_FIND_NEXT_BIT
bool
default y
@@ -129,6 +125,7 @@ config PPC
select USE_GENERIC_SMP_HELPERS if SMP
select HAVE_OPROFILE
select HAVE_SYSCALL_WRAPPERS if PPC64
+ select GENERIC_ATOMIC64 if PPC32
config EARLY_PRINTK
bool
diff --git a/arch/powerpc/boot/install.sh b/arch/powerpc/boot/install.sh
index 51b2387bdba..98312d169c8 100644
--- a/arch/powerpc/boot/install.sh
+++ b/arch/powerpc/boot/install.sh
@@ -18,6 +18,9 @@
# $5 and more - kernel boot files; zImage*, uImage, cuImage.*, etc.
#
+# Bail with error code if anything goes wrong
+set -e
+
# User may have a custom install script
if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi
diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig
index 7d044dfd923..12dc7c40961 100644
--- a/arch/powerpc/configs/ppc6xx_defconfig
+++ b/arch/powerpc/configs/ppc6xx_defconfig
@@ -1808,7 +1808,7 @@ CONFIG_PCF8575=m
CONFIG_SENSORS_PCA9539=m
CONFIG_SENSORS_PCF8591=m
# CONFIG_TPS65010 is not set
-CONFIG_SENSORS_MAX6875=m
+CONFIG_EEPROM_MAX6875=m
CONFIG_SENSORS_TSL2550=m
CONFIG_MCU_MPC8349EMITX=m
# CONFIG_I2C_DEBUG_CORE is not set
diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h
index b7d2d07b6f9..4012483b189 100644
--- a/arch/powerpc/include/asm/atomic.h
+++ b/arch/powerpc/include/asm/atomic.h
@@ -470,6 +470,9 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
+#else /* __powerpc64__ */
+#include <asm-generic/atomic64.h>
+
#endif /* __powerpc64__ */
#include <asm-generic/atomic-long.h>
diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h
index 53512374e1c..b7f8f4a87cc 100644
--- a/arch/powerpc/include/asm/hw_irq.h
+++ b/arch/powerpc/include/asm/hw_irq.h
@@ -80,7 +80,7 @@ static inline void local_irq_disable(void)
__asm__ __volatile__("wrteei 0": : :"memory");
#else
unsigned long msr;
- __asm__ __volatile__("": : :"memory");
+
msr = mfmsr();
SET_MSR_EE(msr & ~MSR_EE);
#endif
@@ -92,7 +92,7 @@ static inline void local_irq_enable(void)
__asm__ __volatile__("wrteei 1": : :"memory");
#else
unsigned long msr;
- __asm__ __volatile__("": : :"memory");
+
msr = mfmsr();
SET_MSR_EE(msr | MSR_EE);
#endif
@@ -108,7 +108,6 @@ static inline void local_irq_save_ptr(unsigned long *flags)
#else
SET_MSR_EE(msr & ~MSR_EE);
#endif
- __asm__ __volatile__("": : :"memory");
}
#define local_save_flags(flags) ((flags) = mfmsr())
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index 7464c0daddd..7ead7c16fb7 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -35,6 +35,16 @@
#define IOMMU_PAGE_MASK (~((1 << IOMMU_PAGE_SHIFT) - 1))
#define IOMMU_PAGE_ALIGN(addr) _ALIGN_UP(addr, IOMMU_PAGE_SIZE)
+/* Cell page table entries */
+#define CBE_IOPTE_PP_W 0x8000000000000000ul /* protection: write */
+#define CBE_IOPTE_PP_R 0x4000000000000000ul /* protection: read */
+#define CBE_IOPTE_M 0x2000000000000000ul /* coherency required */
+#define CBE_IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
+#define CBE_IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
+#define CBE_IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
+#define CBE_IOPTE_H 0x0000000000000800ul /* cache hint */
+#define CBE_IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
+
/* Boot time flags */
extern int iommu_is_off;
extern int iommu_force_on;
diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h
index cdb6fd814de..7f065e178ec 100644
--- a/arch/powerpc/include/asm/ps3.h
+++ b/arch/powerpc/include/asm/ps3.h
@@ -53,6 +53,13 @@ enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void);
extern u64 ps3_os_area_get_rtc_diff(void);
extern void ps3_os_area_set_rtc_diff(u64 rtc_diff);
+struct ps3_os_area_flash_ops {
+ ssize_t (*read)(void *buf, size_t count, loff_t pos);
+ ssize_t (*write)(const void *buf, size_t count, loff_t pos);
+};
+
+extern void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops);
+
/* dma routines */
enum ps3_dma_page_size {
@@ -418,15 +425,15 @@ static inline struct ps3_system_bus_driver *
* @data: Data to set
*/
-static inline void ps3_system_bus_set_driver_data(
+static inline void ps3_system_bus_set_drvdata(
struct ps3_system_bus_device *dev, void *data)
{
- dev->core.driver_data = data;
+ dev_set_drvdata(&dev->core, data);
}
-static inline void *ps3_system_bus_get_driver_data(
+static inline void *ps3_system_bus_get_drvdata(
struct ps3_system_bus_device *dev)
{
- return dev->core.driver_data;
+ return dev_get_drvdata(&dev->core);
}
/* These two need global scope for get_dma_ops(). */
@@ -520,7 +527,4 @@ void ps3_sync_irq(int node);
u32 ps3_get_hw_thread_id(int cpu);
u64 ps3_get_spe_id(void *arg);
-/* mutex synchronizing GPU accesses and video mode changes */
-extern struct mutex ps3_gpu_mutex;
-
#endif
diff --git a/arch/powerpc/include/asm/ps3gpu.h b/arch/powerpc/include/asm/ps3gpu.h
new file mode 100644
index 00000000000..b2b89591907
--- /dev/null
+++ b/arch/powerpc/include/asm/ps3gpu.h
@@ -0,0 +1,86 @@
+/*
+ * PS3 GPU declarations.
+ *
+ * Copyright 2009 Sony Corporation
+ *
+ * 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; version 2 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ASM_POWERPC_PS3GPU_H
+#define _ASM_POWERPC_PS3GPU_H
+
+#include <linux/mutex.h>
+
+#include <asm/lv1call.h>
+
+
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101
+#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102
+
+#define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP 0x600
+#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
+#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT_SYNC 0x602
+#define L1GPU_CONTEXT_ATTRIBUTE_FB_CLOSE 0x603
+
+#define L1GPU_FB_BLIT_WAIT_FOR_COMPLETION (1ULL << 32)
+
+#define L1GPU_DISPLAY_SYNC_HSYNC 1
+#define L1GPU_DISPLAY_SYNC_VSYNC 2
+
+
+/* mutex synchronizing GPU accesses and video mode changes */
+extern struct mutex ps3_gpu_mutex;
+
+
+static inline int lv1_gpu_display_sync(u64 context_handle, u64 head,
+ u64 ddr_offset)
+{
+ return lv1_gpu_context_attribute(context_handle,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
+ head, ddr_offset, 0, 0);
+}
+
+static inline int lv1_gpu_display_flip(u64 context_handle, u64 head,
+ u64 ddr_offset)
+{
+ return lv1_gpu_context_attribute(context_handle,
+ L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
+ head, ddr_offset, 0, 0);
+}
+
+static inline int lv1_gpu_fb_setup(u64 context_handle, u64 xdr_lpar,
+ u64 xdr_size, u64 ioif_offset)
+{
+ return lv1_gpu_context_attribute(context_handle,
+ L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP,
+ xdr_lpar, xdr_size, ioif_offset, 0);
+}
+
+static inline int lv1_gpu_fb_blit(u64 context_handle, u64 ddr_offset,
+ u64 ioif_offset, u64 sync_width, u64 pitch)
+{
+ return lv1_gpu_context_attribute(context_handle,
+ L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
+ ddr_offset, ioif_offset, sync_width,
+ pitch);
+}
+
+static inline int lv1_gpu_fb_close(u64 context_handle)
+{
+ return lv1_gpu_context_attribute(context_handle,
+ L1GPU_CONTEXT_ATTRIBUTE_FB_CLOSE, 0,
+ 0, 0, 0);
+}
+
+#endif /* _ASM_POWERPC_PS3GPU_H */
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index fb359b0a693..a3c28e46947 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -745,11 +745,11 @@
asm volatile("mfmsr %0" : "=r" (rval)); rval;})
#ifdef CONFIG_PPC64
#define __mtmsrd(v, l) asm volatile("mtmsrd %0," __stringify(l) \
- : : "r" (v))
+ : : "r" (v) : "memory")
#define mtmsrd(v) __mtmsrd((v), 0)
#define mtmsr(v) mtmsrd(v)
#else
-#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v))
+#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v) : "memory")
#endif
#define mfspr(rn) ({unsigned long rval; \
diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h
index a0b92de51c7..370600ca276 100644
--- a/arch/powerpc/include/asm/systbl.h
+++ b/arch/powerpc/include/asm/systbl.h
@@ -325,3 +325,4 @@ SYSCALL(inotify_init1)
SYSCALL_SPU(perf_counter_open)
COMPAT_SYS_SPU(preadv)
COMPAT_SYS_SPU(pwritev)
+COMPAT_SYS(rt_tgsigqueueinfo)
diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
index 4badac2d11d..cef080bfc60 100644
--- a/arch/powerpc/include/asm/unistd.h
+++ b/arch/powerpc/include/asm/unistd.h
@@ -344,10 +344,11 @@
#define __NR_perf_counter_open 319
#define __NR_preadv 320
#define __NR_pwritev 321
+#define __NR_rt_tgsigqueueinfo 322
#ifdef __KERNEL__
-#define __NR_syscalls 322
+#define __NR_syscalls 323
#define __NR__exit __NR_exit
#define NR_syscalls __NR_syscalls
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index a7def5f90ca..612b0c4dc26 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -125,6 +125,7 @@ PHONY += systbl_chk
systbl_chk: $(src)/systbl_chk.sh $(obj)/systbl_chk.i
$(call cmd,systbl_chk)
+ifeq ($(CONFIG_PPC_OF_BOOT_TRAMPOLINE),y)
$(obj)/built-in.o: prom_init_check
quiet_cmd_prom_init_check = CALL $<
@@ -133,5 +134,6 @@ quiet_cmd_prom_init_check = CALL $<
PHONY += prom_init_check
prom_init_check: $(src)/prom_init_check.sh $(obj)/prom_init.o
$(call cmd,prom_init_check)
+endif
clean-files := vmlinux.lds
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index f46548e6604..1f6816003eb 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -424,8 +424,8 @@ void __init setup_system(void)
printk("htab_hash_mask = 0x%lx\n", htab_hash_mask);
#endif /* CONFIG_PPC_STD_MMU_64 */
if (PHYSICAL_START > 0)
- printk("physical_start = 0x%lx\n",
- PHYSICAL_START);
+ printk("physical_start = 0x%llx\n",
+ (unsigned long long)PHYSICAL_START);
printk("-----------------------------------------------------\n");
DBG(" <- setup_system()\n");
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index bee1443da76..15391c2ab01 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -52,6 +52,7 @@
#include <linux/jiffies.h>
#include <linux/posix-timers.h>
#include <linux/irq.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/processor.h>
@@ -1143,6 +1144,15 @@ void div128_by_32(u64 dividend_high, u64 dividend_low,
}
+/* We don't need to calibrate delay, we use the CPU timebase for that */
+void calibrate_delay(void)
+{
+ /* Some generic code (such as spinlock debug) use loops_per_jiffy
+ * as the number of __delay(1) in a jiffy, so make it so
+ */
+ loops_per_jiffy = tb_ticks_per_jiffy;
+}
+
static int __init rtc_init(void)
{
struct platform_device *pdev;
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index 0ce45c2b42f..c71498dbf21 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -329,7 +329,7 @@ static struct irq_host_ops msic_host_ops = {
static int axon_msi_shutdown(struct of_device *device)
{
- struct axon_msic *msic = device->dev.platform_data;
+ struct axon_msic *msic = dev_get_drvdata(&device->dev);
u32 tmp;
pr_debug("axon_msi: disabling %s\n",
@@ -416,7 +416,7 @@ static int axon_msi_probe(struct of_device *device,
msic->read_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG)
& MSIC_FIFO_SIZE_MASK;
- device->dev.platform_data = msic;
+ dev_set_drvdata(&device->dev, msic);
ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index bed4690de39..5b34fc211f3 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -100,16 +100,6 @@
#define IOSTE_PS_1M 0x0000000000000005ul /* - 1MB */
#define IOSTE_PS_16M 0x0000000000000007ul /* - 16MB */
-/* Page table entries */
-#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */
-#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */
-#define IOPTE_M 0x2000000000000000ul /* coherency required */
-#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
-#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
-#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
-#define IOPTE_H 0x0000000000000800ul /* cache hint */
-#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
-
/* IOMMU sizing */
#define IO_SEGMENT_SHIFT 28
@@ -193,19 +183,21 @@ static int tce_build_cell(struct iommu_table *tbl, long index, long npages,
*/
const unsigned long prot = 0xc48;
base_pte =
- ((prot << (52 + 4 * direction)) & (IOPTE_PP_W | IOPTE_PP_R))
- | IOPTE_M | IOPTE_SO_RW | (window->ioid & IOPTE_IOID_Mask);
+ ((prot << (52 + 4 * direction)) &
+ (CBE_IOPTE_PP_W | CBE_IOPTE_PP_R)) |
+ CBE_IOPTE_M | CBE_IOPTE_SO_RW |
+ (window->ioid & CBE_IOPTE_IOID_Mask);
#else
- base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW |
- (window->ioid & IOPTE_IOID_Mask);
+ base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
+ CBE_IOPTE_SO_RW | (window->ioid & CBE_IOPTE_IOID_Mask);
#endif
if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)))
- base_pte &= ~IOPTE_SO_RW;
+ base_pte &= ~CBE_IOPTE_SO_RW;
io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
for (i = 0; i < npages; i++, uaddr += IOMMU_PAGE_SIZE)
- io_pte[i] = base_pte | (__pa(uaddr) & IOPTE_RPN_Mask);
+ io_pte[i] = base_pte | (__pa(uaddr) & CBE_IOPTE_RPN_Mask);
mb();
@@ -231,8 +223,9 @@ static void tce_free_cell(struct iommu_table *tbl, long index, long npages)
#else
/* spider bridge does PCI reads after freeing - insert a mapping
* to a scratch page instead of an invalid entry */
- pte = IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | __pa(window->iommu->pad_page)
- | (window->ioid & IOPTE_IOID_Mask);
+ pte = CBE_IOPTE_PP_R | CBE_IOPTE_M | CBE_IOPTE_SO_RW |
+ __pa(window->iommu->pad_page) |
+ (window->ioid & CBE_IOPTE_IOID_Mask);
#endif
io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
@@ -1001,7 +994,7 @@ static void insert_16M_pte(unsigned long addr, unsigned long *ptab,
pr_debug("iommu: addr %lx ptab %p segment %lx offset %lx\n",
addr, ptab, segment, offset);
- ptab[offset] = base_pte | (__pa(addr) & IOPTE_RPN_Mask);
+ ptab[offset] = base_pte | (__pa(addr) & CBE_IOPTE_RPN_Mask);
}
static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu,
@@ -1016,14 +1009,14 @@ static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu,
pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase);
- base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M
- | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask);
+ base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
+ (cell_iommu_get_ioid(np) & CBE_IOPTE_IOID_Mask);
if (iommu_fixed_is_weak)
pr_info("IOMMU: Using weak ordering for fixed mapping\n");
else {
pr_info("IOMMU: Using strong ordering for fixed mapping\n");
- base_pte |= IOPTE_SO_RW;
+ base_pte |= CBE_IOPTE_SO_RW;
}
for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) {
diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c
index 4543c4bc3a5..c5a87a72057 100644
--- a/arch/powerpc/platforms/iseries/dt.c
+++ b/arch/powerpc/platforms/iseries/dt.c
@@ -204,7 +204,8 @@ static void __init dt_prop_u32(struct iseries_flat_dt *dt, const char *name,
dt_prop(dt, name, &data, sizeof(u32));
}
-static void __init dt_prop_u64(struct iseries_flat_dt *dt, const char *name,
+static void __init __maybe_unused dt_prop_u64(struct iseries_flat_dt *dt,
+ const char *name,
u64 data)
{
dt_prop(dt, name, &data, sizeof(u64));
diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c
index 3689c2413d2..fef4d515051 100644
--- a/arch/powerpc/platforms/iseries/mf.c
+++ b/arch/powerpc/platforms/iseries/mf.c
@@ -267,7 +267,8 @@ static struct pending_event *new_pending_event(void)
return ev;
}
-static int signal_vsp_instruction(struct vsp_cmd_data *vsp_cmd)
+static int __maybe_unused
+signal_vsp_instruction(struct vsp_cmd_data *vsp_cmd)
{
struct pending_event *ev = new_pending_event();
int rc;
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index 9a2b6d94861..846eb8b57fd 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -24,6 +24,7 @@
#include <linux/lmb.h>
#include <asm/firmware.h>
+#include <asm/iommu.h>
#include <asm/prom.h>
#include <asm/udbg.h>
#include <asm/lv1call.h>
@@ -605,9 +606,8 @@ static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
r->ioid,
iopte_flag);
if (result) {
- printk(KERN_WARNING "%s:%d: lv1_map_device_dma_region "
- "failed: %s\n", __func__, __LINE__,
- ps3_result(result));
+ pr_warning("%s:%d: lv1_put_iopte failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
goto fail_map;
}
DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__,
@@ -1001,7 +1001,8 @@ static int dma_sb_region_create_linear(struct ps3_dma_region *r)
if (len > r->len)
len = r->len;
result = dma_sb_map_area(r, virt_addr, len, &tmp,
- IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
+ CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW |
+ CBE_IOPTE_M);
BUG_ON(result);
}
@@ -1014,7 +1015,8 @@ static int dma_sb_region_create_linear(struct ps3_dma_region *r)
else
len -= map.rm.size - r->offset;
result = dma_sb_map_area(r, virt_addr, len, &tmp,
- IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
+ CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW |
+ CBE_IOPTE_M);
BUG_ON(result);
}
diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c
index cf1cd0f8c18..d6487a9c801 100644
--- a/arch/powerpc/platforms/ps3/os-area.c
+++ b/arch/powerpc/platforms/ps3/os-area.c
@@ -226,6 +226,44 @@ static struct property property_av_multi_out = {
.value = &saved_params.av_multi_out,
};
+
+static DEFINE_MUTEX(os_area_flash_mutex);
+
+static const struct ps3_os_area_flash_ops *os_area_flash_ops;
+
+void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops)
+{
+ mutex_lock(&os_area_flash_mutex);
+ os_area_flash_ops = ops;
+ mutex_unlock(&os_area_flash_mutex);
+}
+EXPORT_SYMBOL_GPL(ps3_os_area_flash_register);
+
+static ssize_t os_area_flash_read(void *buf, size_t count, loff_t pos)
+{
+ ssize_t res = -ENODEV;
+
+ mutex_lock(&os_area_flash_mutex);
+ if (os_area_flash_ops)
+ res = os_area_flash_ops->read(buf, count, pos);
+ mutex_unlock(&os_area_flash_mutex);
+
+ return res;
+}
+
+static ssize_t os_area_flash_write(const void *buf, size_t count, loff_t pos)
+{
+ ssize_t res = -ENODEV;
+
+ mutex_lock(&os_area_flash_mutex);
+ if (os_area_flash_ops)
+ res = os_area_flash_ops->write(buf, count, pos);
+ mutex_unlock(&os_area_flash_mutex);
+
+ return res;
+}
+
+
/**
* os_area_set_property - Add or overwrite a saved_params value to the device tree.
*
@@ -352,12 +390,12 @@ static int db_verify(const struct os_area_db *db)
if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM,
sizeof(db->magic_num))) {
pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
- return -1;
+ return -EINVAL;
}
if (db->version != 1) {
pr_debug("%s:%d version failed\n", __func__, __LINE__);
- return -1;
+ return -EINVAL;
}
return 0;
@@ -578,59 +616,48 @@ static void os_area_db_init(struct os_area_db *db)
*
*/
-static void __maybe_unused update_flash_db(void)
+static int update_flash_db(void)
{
- int result;
- int file;
- off_t offset;
+ const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
+ struct os_area_header *header;
ssize_t count;
- static const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
- const struct os_area_header *header;
+ int error;
+ loff_t pos;
struct os_area_db* db;
/* Read in header and db from flash. */
- file = sys_open("/dev/ps3flash", O_RDWR, 0);
-
- if (file < 0) {
- pr_debug("%s:%d sys_open failed\n", __func__, __LINE__);
- goto fail_open;
- }
-
header = kmalloc(buf_len, GFP_KERNEL);
-
if (!header) {
- pr_debug("%s:%d kmalloc failed\n", __func__, __LINE__);
- goto fail_malloc;
+ pr_debug("%s: kmalloc failed\n", __func__);
+ return -ENOMEM;
}
- offset = sys_lseek(file, 0, SEEK_SET);
-
- if (offset != 0) {
- pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__);
- goto fail_header_seek;
+ count = os_area_flash_read(header, buf_len, 0);
+ if (count < 0) {
+ pr_debug("%s: os_area_flash_read failed %zd\n", __func__,
+ count);
+ error = count;
+ goto fail;
}
- count = sys_read(file, (char __user *)header, buf_len);
-
- result = count < OS_AREA_SEGMENT_SIZE || verify_header(header)
- || count < header->db_area_offset * OS_AREA_SEGMENT_SIZE;
-
- if (result) {
- pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
+ pos = header->db_area_offset * OS_AREA_SEGMENT_SIZE;
+ if (count < OS_AREA_SEGMENT_SIZE || verify_header(header) ||
+ count < pos) {
+ pr_debug("%s: verify_header failed\n", __func__);
dump_header(header);
- goto fail_header;
+ error = -EINVAL;
+ goto fail;
}
/* Now got a good db offset and some maybe good db data. */
- db = (void*)header + header->db_area_offset * OS_AREA_SEGMENT_SIZE;
+ db = (void *)header + pos;
- result = db_verify(db);
-
- if (result) {
- printk(KERN_NOTICE "%s:%d: Verify of flash database failed, "
- "formatting.\n", __func__, __LINE__);
+ error = db_verify(db);
+ if (error) {
+ pr_notice("%s: Verify of flash database failed, formatting.\n",
+ __func__);
dump_db(db);
os_area_db_init(db);
}
@@ -639,29 +666,16 @@ static void __maybe_unused update_flash_db(void)
db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff);
- offset = sys_lseek(file, header->db_area_offset * OS_AREA_SEGMENT_SIZE,
- SEEK_SET);
-
- if (offset != header->db_area_offset * OS_AREA_SEGMENT_SIZE) {
- pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__);
- goto fail_db_seek;
- }
-
- count = sys_write(file, (const char __user *)db,
- sizeof(struct os_area_db));
-
+ count = os_area_flash_write(db, sizeof(struct os_area_db), pos);
if (count < sizeof(struct os_area_db)) {
- pr_debug("%s:%d sys_write failed\n", __func__, __LINE__);
+ pr_debug("%s: os_area_flash_write failed %zd\n", __func__,
+ count);
+ error = count < 0 ? count : -EIO;
}
-fail_db_seek:
-fail_header:
-fail_header_seek:
+fail:
kfree(header);
-fail_malloc:
- sys_close(file);
-fail_open:
- return;
+ return error;
}
/**
@@ -674,11 +688,11 @@ fail_open:
static void os_area_queue_work_handler(struct work_struct *work)
{
struct device_node *node;
+ int error;
pr_debug(" -> %s:%d\n", __func__, __LINE__);
node = of_find_node_by_path("/");
-
if (node) {
os_area_set_property(node, &property_rtc_diff);
of_node_put(node);
@@ -686,12 +700,10 @@ static void os_area_queue_work_handler(struct work_struct *work)
pr_debug("%s:%d of_find_node_by_path failed\n",
__func__, __LINE__);
-#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
- update_flash_db();
-#else
- printk(KERN_WARNING "%s:%d: No flash rom driver configured.\n",
- __func__, __LINE__);
-#endif
+ error = update_flash_db();
+ if (error)
+ pr_warning("%s: Could not update FLASH ROM\n", __func__);
+
pr_debug(" <- %s:%d\n", __func__, __LINE__);
}
@@ -808,7 +820,7 @@ u64 ps3_os_area_get_rtc_diff(void)
{
return saved_params.rtc_diff;
}
-EXPORT_SYMBOL(ps3_os_area_get_rtc_diff);
+EXPORT_SYMBOL_GPL(ps3_os_area_get_rtc_diff);
/**
* ps3_os_area_set_rtc_diff - Set the rtc diff value.
@@ -824,7 +836,7 @@ void ps3_os_area_set_rtc_diff(u64 rtc_diff)
os_area_queue_work();
}
}
-EXPORT_SYMBOL(ps3_os_area_set_rtc_diff);
+EXPORT_SYMBOL_GPL(ps3_os_area_set_rtc_diff);
/**
* ps3_os_area_get_av_multi_out - Returns the default video mode.
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 136aa0637d9..9a196a88eda 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -232,14 +232,4 @@ int ps3_repository_read_spu_resource_id(unsigned int res_index,
int ps3_repository_read_vuart_av_port(unsigned int *port);
int ps3_repository_read_vuart_sysmgr_port(unsigned int *port);
-/* Page table entries */
-#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */
-#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */
-#define IOPTE_M 0x2000000000000000ul /* coherency required */
-#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
-#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
-#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
-#define IOPTE_H 0x0000000000000800ul /* cache hint */
-#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
-
#endif
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index 1a7b5ae0c83..149bea2ce58 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -32,6 +32,7 @@
#include <asm/udbg.h>
#include <asm/prom.h>
#include <asm/lv1call.h>
+#include <asm/ps3gpu.h>
#include "platform.h"
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
index 9a73d023863..9fead0faf38 100644
--- a/arch/powerpc/platforms/ps3/system-bus.c
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -27,6 +27,7 @@
#include <asm/udbg.h>
#include <asm/lv1call.h>
#include <asm/firmware.h>
+#include <asm/iommu.h>
#include "platform.h"
@@ -531,7 +532,8 @@ static void * ps3_alloc_coherent(struct device *_dev, size_t size,
}
result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle,
- IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
+ CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |
+ CBE_IOPTE_SO_RW | CBE_IOPTE_M);
if (result) {
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -575,7 +577,8 @@ static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,
result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
&bus_addr,
- IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M);
+ CBE_IOPTE_PP_R | CBE_IOPTE_PP_W |
+ CBE_IOPTE_SO_RW | CBE_IOPTE_M);
if (result) {
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -596,16 +599,16 @@ static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
u64 iopte_flag;
void *ptr = page_address(page) + offset;
- iopte_flag = IOPTE_M;
+ iopte_flag = CBE_IOPTE_M;
switch (direction) {
case DMA_BIDIRECTIONAL:
- iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW;
+ iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;
break;
case DMA_TO_DEVICE:
- iopte_flag |= IOPTE_PP_R | IOPTE_SO_R;
+ iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_SO_R;
break;
case DMA_FROM_DEVICE:
- iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW;
+ iopte_flag |= CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;
break;
default:
/* not happned */
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 99dc3ded6b4..a14dba0e4d6 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -348,6 +348,9 @@ config ARCH_ENABLE_MEMORY_HOTPLUG
config ARCH_ENABLE_MEMORY_HOTREMOVE
def_bool y
+config ARCH_HIBERNATION_POSSIBLE
+ def_bool y if 64BIT
+
source "mm/Kconfig"
comment "I/O subsystem configuration"
@@ -592,6 +595,12 @@ config SECCOMP
endmenu
+menu "Power Management"
+
+source "kernel/power/Kconfig"
+
+endmenu
+
source "net/Kconfig"
config PCMCIA
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index 578c61f15a4..0ff387cebf8 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -88,7 +88,9 @@ LDFLAGS_vmlinux := -e start
head-y := arch/s390/kernel/head.o arch/s390/kernel/init_task.o
core-y += arch/s390/mm/ arch/s390/kernel/ arch/s390/crypto/ \
- arch/s390/appldata/ arch/s390/hypfs/ arch/s390/kvm/
+ arch/s390/appldata/ arch/s390/hypfs/ arch/s390/kvm/ \
+ arch/s390/power/
+
libs-y += arch/s390/lib/
drivers-y += drivers/s390/
drivers-$(CONFIG_MATHEMU) += arch/s390/math-emu/
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c
index 1dfc7100c7e..264528e4f58 100644
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -5,7 +5,7 @@
* Exports appldata_register_ops() and appldata_unregister_ops() for the
* data gathering modules.
*
- * Copyright IBM Corp. 2003, 2008
+ * Copyright IBM Corp. 2003, 2009
*
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
@@ -26,6 +26,8 @@
#include <linux/notifier.h>
#include <linux/cpu.h>
#include <linux/workqueue.h>
+#include <linux/suspend.h>
+#include <linux/platform_device.h>
#include <asm/appldata.h>
#include <asm/timer.h>
#include <asm/uaccess.h>
@@ -41,6 +43,9 @@
#define TOD_MICRO 0x01000 /* nr. of TOD clock units
for 1 microsecond */
+
+static struct platform_device *appldata_pdev;
+
/*
* /proc entries (sysctl)
*/
@@ -86,6 +91,7 @@ static atomic_t appldata_expire_count = ATOMIC_INIT(0);
static DEFINE_SPINLOCK(appldata_timer_lock);
static int appldata_interval = APPLDATA_CPU_INTERVAL;
static int appldata_timer_active;
+static int appldata_timer_suspended = 0;
/*
* Work queue
@@ -475,6 +481,93 @@ void appldata_unregister_ops(struct appldata_ops *ops)
/********************** module-ops management <END> **************************/
+/**************************** suspend / resume *******************************/
+static int appldata_freeze(struct device *dev)
+{
+ struct appldata_ops *ops;
+ int rc;
+ struct list_head *lh;
+
+ get_online_cpus();
+ spin_lock(&appldata_timer_lock);
+ if (appldata_timer_active) {
+ __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
+ appldata_timer_suspended = 1;
+ }
+ spin_unlock(&appldata_timer_lock);
+ put_online_cpus();
+
+ mutex_lock(&appldata_ops_mutex);
+ list_for_each(lh, &appldata_ops_list) {
+ ops = list_entry(lh, struct appldata_ops, list);
+ if (ops->active == 1) {
+ rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
+ (unsigned long) ops->data, ops->size,
+ ops->mod_lvl);
+ if (rc != 0)
+ pr_err("Stopping the data collection for %s "
+ "failed with rc=%d\n", ops->name, rc);
+ }
+ }
+ mutex_unlock(&appldata_ops_mutex);
+ return 0;
+}
+
+static int appldata_restore(struct device *dev)
+{
+ struct appldata_ops *ops;
+ int rc;
+ struct list_head *lh;
+
+ get_online_cpus();
+ spin_lock(&appldata_timer_lock);
+ if (appldata_timer_suspended) {
+ __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
+ appldata_timer_suspended = 0;
+ }
+ spin_unlock(&appldata_timer_lock);
+ put_online_cpus();
+
+ mutex_lock(&appldata_ops_mutex);
+ list_for_each(lh, &appldata_ops_list) {
+ ops = list_entry(lh, struct appldata_ops, list);
+ if (ops->active == 1) {
+ ops->callback(ops->data); // init record
+ rc = appldata_diag(ops->record_nr,
+ APPLDATA_START_INTERVAL_REC,
+ (unsigned long) ops->data, ops->size,
+ ops->mod_lvl);
+ if (rc != 0) {
+ pr_err("Starting the data collection for %s "
+ "failed with rc=%d\n", ops->name, rc);
+ }
+ }
+ }
+ mutex_unlock(&appldata_ops_mutex);
+ return 0;
+}
+
+static int appldata_thaw(struct device *dev)
+{
+ return appldata_restore(dev);
+}
+
+static struct dev_pm_ops appldata_pm_ops = {
+ .freeze = appldata_freeze,
+ .thaw = appldata_thaw,
+ .restore = appldata_restore,
+};
+
+static struct platform_driver appldata_pdrv = {
+ .driver = {
+ .name = "appldata",
+ .owner = THIS_MODULE,
+ .pm = &appldata_pm_ops,
+ },
+};
+/************************* suspend / resume <END> ****************************/
+
+
/******************************* init / exit *********************************/
static void __cpuinit appldata_online_cpu(int cpu)
@@ -531,11 +624,23 @@ static struct notifier_block __cpuinitdata appldata_nb = {
*/
static int __init appldata_init(void)
{
- int i;
+ int i, rc;
+
+ rc = platform_driver_register(&appldata_pdrv);
+ if (rc)
+ return rc;
+ appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
+ 0);
+ if (IS_ERR(appldata_pdev)) {
+ rc = PTR_ERR(appldata_pdev);
+ goto out_driver;
+ }
appldata_wq = create_singlethread_workqueue("appldata");
- if (!appldata_wq)
- return -ENOMEM;
+ if (!appldata_wq) {
+ rc = -ENOMEM;
+ goto out_device;
+ }
get_online_cpus();
for_each_online_cpu(i)
@@ -547,6 +652,12 @@ static int __init appldata_init(void)
appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
return 0;
+
+out_device:
+ platform_device_unregister(appldata_pdev);
+out_driver:
+ platform_driver_unregister(&appldata_pdrv);
+ return rc;
}
__initcall(appldata_init);
diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h
index ba007d8df94..2a541955117 100644
--- a/arch/s390/include/asm/ccwdev.h
+++ b/arch/s390/include/asm/ccwdev.h
@@ -1,11 +1,9 @@
/*
- * include/asm-s390/ccwdev.h
- * include/asm-s390x/ccwdev.h
+ * Copyright IBM Corp. 2002, 2009
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Arnd Bergmann <arndb@de.ibm.com>
+ * Author(s): Arnd Bergmann <arndb@de.ibm.com>
*
- * Interface for CCW device drivers
+ * Interface for CCW device drivers
*/
#ifndef _S390_CCWDEV_H_
#define _S390_CCWDEV_H_
@@ -104,6 +102,11 @@ struct ccw_device {
* @set_offline: called when setting device offline
* @notify: notify driver of device state changes
* @shutdown: called at device shutdown
+ * @prepare: prepare for pm state transition
+ * @complete: undo work done in @prepare
+ * @freeze: callback for freezing during hibernation snapshotting
+ * @thaw: undo work done in @freeze
+ * @restore: callback for restoring after hibernation
* @driver: embedded device driver structure
* @name: device driver name
*/
@@ -116,6 +119,11 @@ struct ccw_driver {
int (*set_offline) (struct ccw_device *);
int (*notify) (struct ccw_device *, int);
void (*shutdown) (struct ccw_device *);
+ int (*prepare) (struct ccw_device *);
+ void (*complete) (struct ccw_device *);
+ int (*freeze)(struct ccw_device *);
+ int (*thaw) (struct ccw_device *);
+ int (*restore)(struct ccw_device *);
struct device_driver driver;
char *name;
};
@@ -184,6 +192,7 @@ extern void ccw_device_get_id(struct ccw_device *, struct ccw_dev_id *);
#define to_ccwdrv(n) container_of(n, struct ccw_driver, driver)
extern struct ccw_device *ccw_device_probe_console(void);
+extern int ccw_device_force_console(void);
// FIXME: these have to go
extern int _ccw_device_get_subchannel_number(struct ccw_device *);
diff --git a/arch/s390/include/asm/ccwgroup.h b/arch/s390/include/asm/ccwgroup.h
index a27f68985a7..c79c1e787b8 100644
--- a/arch/s390/include/asm/ccwgroup.h
+++ b/arch/s390/include/asm/ccwgroup.h
@@ -38,6 +38,11 @@ struct ccwgroup_device {
* @set_online: function called when device is set online
* @set_offline: function called when device is set offline
* @shutdown: function called when device is shut down
+ * @prepare: prepare for pm state transition
+ * @complete: undo work done in @prepare
+ * @freeze: callback for freezing during hibernation snapshotting
+ * @thaw: undo work done in @freeze
+ * @restore: callback for restoring after hibernation
* @driver: embedded driver structure
*/
struct ccwgroup_driver {
@@ -51,6 +56,11 @@ struct ccwgroup_driver {
int (*set_online) (struct ccwgroup_device *);
int (*set_offline) (struct ccwgroup_device *);
void (*shutdown)(struct ccwgroup_device *);
+ int (*prepare) (struct ccwgroup_device *);
+ void (*complete) (struct ccwgroup_device *);
+ int (*freeze)(struct ccwgroup_device *);
+ int (*thaw) (struct ccwgroup_device *);
+ int (*restore)(struct ccwgroup_device *);
struct device_driver driver;
};
diff --git a/arch/s390/include/asm/suspend.h b/arch/s390/include/asm/suspend.h
new file mode 100644
index 00000000000..dc75c616eaf
--- /dev/null
+++ b/arch/s390/include/asm/suspend.h
@@ -0,0 +1,10 @@
+#ifndef __ASM_S390_SUSPEND_H
+#define __ASM_S390_SUSPEND_H
+
+static inline int arch_prepare_suspend(void)
+{
+ return 0;
+}
+
+#endif
+
diff --git a/arch/s390/include/asm/system.h b/arch/s390/include/asm/system.h
index 3a8b26eb1f2..4fb83c1cdb7 100644
--- a/arch/s390/include/asm/system.h
+++ b/arch/s390/include/asm/system.h
@@ -1,11 +1,7 @@
/*
- * include/asm-s390/system.h
+ * Copyright IBM Corp. 1999, 2009
*
- * S390 version
- * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
- *
- * Derived from "include/asm-i386/system.h"
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef __ASM_SYSTEM_H
@@ -469,6 +465,20 @@ extern psw_t sysc_restore_trace_psw;
extern psw_t io_restore_trace_psw;
#endif
+static inline int tprot(unsigned long addr)
+{
+ int rc = -EFAULT;
+
+ asm volatile(
+ " tprot 0(%1),0\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "+d" (rc) : "a" (addr) : "cc");
+ return rc;
+}
+
#endif /* __KERNEL__ */
#endif
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index fb263736826..f9b144049dc 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -1,7 +1,7 @@
/*
* arch/s390/kernel/early.c
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2009
* Author(s): Hongjie Yang <hongjie@us.ibm.com>,
* Heiko Carstens <heiko.carstens@de.ibm.com>
*/
@@ -210,7 +210,7 @@ static noinline __init void detect_machine_type(void)
machine_flags |= MACHINE_FLAG_VM;
}
-static __init void early_pgm_check_handler(void)
+static void early_pgm_check_handler(void)
{
unsigned long addr;
const struct exception_table_entry *fixup;
@@ -222,7 +222,7 @@ static __init void early_pgm_check_handler(void)
S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE;
}
-static noinline __init void setup_lowcore_early(void)
+void setup_lowcore_early(void)
{
psw_t psw;
diff --git a/arch/s390/kernel/mem_detect.c b/arch/s390/kernel/mem_detect.c
index 9872999c66d..559af0d0787 100644
--- a/arch/s390/kernel/mem_detect.c
+++ b/arch/s390/kernel/mem_detect.c
@@ -1,6 +1,7 @@
/*
- * Copyright IBM Corp. 2008
- * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Copyright IBM Corp. 2008, 2009
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
*/
#include <linux/kernel.h>
@@ -9,20 +10,6 @@
#include <asm/sclp.h>
#include <asm/setup.h>
-static inline int tprot(unsigned long addr)
-{
- int rc = -EFAULT;
-
- asm volatile(
- " tprot 0(%1),0\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1:\n"
- EX_TABLE(0b,1b)
- : "+d" (rc) : "a" (addr) : "cc");
- return rc;
-}
-
#define ADDR2G (1ULL << 31)
static void find_memory_chunks(struct mem_chunk chunk[])
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index cc8c484984e..fd8e3111a4e 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -1,7 +1,7 @@
/*
* arch/s390/kernel/smp.c
*
- * Copyright IBM Corp. 1999,2007
+ * Copyright IBM Corp. 1999, 2009
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
* Heiko Carstens (heiko.carstens@de.ibm.com)
@@ -1031,6 +1031,42 @@ out:
static SYSDEV_CLASS_ATTR(dispatching, 0644, dispatching_show,
dispatching_store);
+/*
+ * If the resume kernel runs on another cpu than the suspended kernel,
+ * we have to switch the cpu IDs in the logical map.
+ */
+void smp_switch_boot_cpu_in_resume(u32 resume_phys_cpu_id,
+ struct _lowcore *suspend_lowcore)
+{
+ int cpu, suspend_cpu_id, resume_cpu_id;
+ u32 suspend_phys_cpu_id;
+
+ suspend_phys_cpu_id = __cpu_logical_map[suspend_lowcore->cpu_nr];
+ suspend_cpu_id = suspend_lowcore->cpu_nr;
+
+ for_each_present_cpu(cpu) {
+ if (__cpu_logical_map[cpu] == resume_phys_cpu_id) {
+ resume_cpu_id = cpu;
+ goto found;
+ }
+ }
+ panic("Could not find resume cpu in logical map.\n");
+
+found:
+ printk("Resume cpu ID: %i/%i\n", resume_phys_cpu_id, resume_cpu_id);
+ printk("Suspend cpu ID: %i/%i\n", suspend_phys_cpu_id, suspend_cpu_id);
+
+ __cpu_logical_map[resume_cpu_id] = suspend_phys_cpu_id;
+ __cpu_logical_map[suspend_cpu_id] = resume_phys_cpu_id;
+
+ lowcore_ptr[suspend_cpu_id]->cpu_addr = resume_phys_cpu_id;
+}
+
+u32 smp_get_phys_cpu_id(void)
+{
+ return __cpu_logical_map[smp_processor_id()];
+}
+
static int __init topology_init(void)
{
int cpu;
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 4ca8e826bf3..56566720798 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -313,3 +313,22 @@ int s390_enable_sie(void)
return 0;
}
EXPORT_SYMBOL_GPL(s390_enable_sie);
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+#ifdef CONFIG_HIBERNATION
+bool kernel_page_present(struct page *page)
+{
+ unsigned long addr;
+ int cc;
+
+ addr = page_to_phys(page);
+ asm("lra %1,0(%1)\n"
+ "ipm %0\n"
+ "srl %0,28"
+ :"=d"(cc),"+a"(addr)::"cc");
+ return cc == 0;
+}
+
+#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_DEBUG_PAGEALLOC */
+
diff --git a/arch/s390/power/Makefile b/arch/s390/power/Makefile
new file mode 100644
index 00000000000..973bb45a8fe
--- /dev/null
+++ b/arch/s390/power/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for s390 PM support
+#
+
+obj-$(CONFIG_HIBERNATION) += suspend.o
+obj-$(CONFIG_HIBERNATION) += swsusp.o
+obj-$(CONFIG_HIBERNATION) += swsusp_64.o
+obj-$(CONFIG_HIBERNATION) += swsusp_asm64.o
diff --git a/arch/s390/power/suspend.c b/arch/s390/power/suspend.c
new file mode 100644
index 00000000000..b3351eceebb
--- /dev/null
+++ b/arch/s390/power/suspend.c
@@ -0,0 +1,40 @@
+/*
+ * Suspend support specific for s390.
+ *
+ * Copyright IBM Corp. 2009
+ *
+ * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
+ */
+
+#include <linux/mm.h>
+#include <linux/suspend.h>
+#include <linux/reboot.h>
+#include <linux/pfn.h>
+#include <asm/sections.h>
+#include <asm/ipl.h>
+
+/*
+ * References to section boundaries
+ */
+extern const void __nosave_begin, __nosave_end;
+
+/*
+ * check if given pfn is in the 'nosave' or in the read only NSS section
+ */
+int pfn_is_nosave(unsigned long pfn)
+{
+ unsigned long nosave_begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
+ unsigned long nosave_end_pfn = PAGE_ALIGN(__pa(&__nosave_end))
+ >> PAGE_SHIFT;
+ unsigned long eshared_pfn = PFN_DOWN(__pa(&_eshared)) - 1;
+ unsigned long stext_pfn = PFN_DOWN(__pa(&_stext));
+
+ if (pfn >= nosave_begin_pfn && pfn < nosave_end_pfn)
+ return 1;
+ if (pfn >= stext_pfn && pfn <= eshared_pfn) {
+ if (ipl_info.type == IPL_TYPE_NSS)
+ return 1;
+ } else if ((tprot(pfn * PAGE_SIZE) && pfn > 0))
+ return 1;
+ return 0;
+}
diff --git a/arch/s390/power/swsusp.c b/arch/s390/power/swsusp.c
new file mode 100644
index 00000000000..e6a4fe9f5f2
--- /dev/null
+++ b/arch/s390/power/swsusp.c
@@ -0,0 +1,30 @@
+/*
+ * Support for suspend and resume on s390
+ *
+ * Copyright IBM Corp. 2009
+ *
+ * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
+ *
+ */
+
+
+/*
+ * save CPU registers before creating a hibernation image and before
+ * restoring the memory state from it
+ */
+void save_processor_state(void)
+{
+ /* implentation contained in the
+ * swsusp_arch_suspend function
+ */
+}
+
+/*
+ * restore the contents of CPU registers
+ */
+void restore_processor_state(void)
+{
+ /* implentation contained in the
+ * swsusp_arch_resume function
+ */
+}
diff --git a/arch/s390/power/swsusp_64.c b/arch/s390/power/swsusp_64.c
new file mode 100644
index 00000000000..9516a517d72
--- /dev/null
+++ b/arch/s390/power/swsusp_64.c
@@ -0,0 +1,17 @@
+/*
+ * Support for suspend and resume on s390
+ *
+ * Copyright IBM Corp. 2009
+ *
+ * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
+ *
+ */
+
+#include <asm/system.h>
+#include <linux/interrupt.h>
+
+void do_after_copyback(void)
+{
+ mb();
+}
+
diff --git a/arch/s390/power/swsusp_asm64.S b/arch/s390/power/swsusp_asm64.S
new file mode 100644
index 00000000000..3c74e7d827c
--- /dev/null
+++ b/arch/s390/power/swsusp_asm64.S
@@ -0,0 +1,199 @@
+/*
+ * S390 64-bit swsusp implementation
+ *
+ * Copyright IBM Corp. 2009
+ *
+ * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
+ * Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * Save register context in absolute 0 lowcore and call swsusp_save() to
+ * create in-memory kernel image. The context is saved in the designated
+ * "store status" memory locations (see POP).
+ * We return from this function twice. The first time during the suspend to
+ * disk process. The second time via the swsusp_arch_resume() function
+ * (see below) in the resume process.
+ * This function runs with disabled interrupts.
+ */
+ .section .text
+ .align 2
+ .globl swsusp_arch_suspend
+swsusp_arch_suspend:
+ stmg %r6,%r15,__SF_GPRS(%r15)
+ lgr %r1,%r15
+ aghi %r15,-STACK_FRAME_OVERHEAD
+ stg %r1,__SF_BACKCHAIN(%r15)
+
+ /* Deactivate DAT */
+ stnsm __SF_EMPTY(%r15),0xfb
+
+ /* Switch off lowcore protection */
+ stctg %c0,%c0,__SF_EMPTY(%r15)
+ ni __SF_EMPTY+4(%r15),0xef
+ lctlg %c0,%c0,__SF_EMPTY(%r15)
+
+ /* Store prefix register on stack */
+ stpx __SF_EMPTY(%r15)
+
+ /* Setup base register for lowcore (absolute 0) */
+ llgf %r1,__SF_EMPTY(%r15)
+
+ /* Get pointer to save area */
+ aghi %r1,0x1000
+
+ /* Store registers */
+ mvc 0x318(4,%r1),__SF_EMPTY(%r15) /* move prefix to lowcore */
+ stfpc 0x31c(%r1) /* store fpu control */
+ std 0,0x200(%r1) /* store f0 */
+ std 1,0x208(%r1) /* store f1 */
+ std 2,0x210(%r1) /* store f2 */
+ std 3,0x218(%r1) /* store f3 */
+ std 4,0x220(%r1) /* store f4 */
+ std 5,0x228(%r1) /* store f5 */
+ std 6,0x230(%r1) /* store f6 */
+ std 7,0x238(%r1) /* store f7 */
+ std 8,0x240(%r1) /* store f8 */
+ std 9,0x248(%r1) /* store f9 */
+ std 10,0x250(%r1) /* store f10 */
+ std 11,0x258(%r1) /* store f11 */
+ std 12,0x260(%r1) /* store f12 */
+ std 13,0x268(%r1) /* store f13 */
+ std 14,0x270(%r1) /* store f14 */
+ std 15,0x278(%r1) /* store f15 */
+ stam %a0,%a15,0x340(%r1) /* store access registers */
+ stctg %c0,%c15,0x380(%r1) /* store control registers */
+ stmg %r0,%r15,0x280(%r1) /* store general registers */
+
+ stpt 0x328(%r1) /* store timer */
+ stckc 0x330(%r1) /* store clock comparator */
+
+ /* Activate DAT */
+ stosm __SF_EMPTY(%r15),0x04
+
+ /* Set prefix page to zero */
+ xc __SF_EMPTY(4,%r15),__SF_EMPTY(%r15)
+ spx __SF_EMPTY(%r15)
+
+ /* Setup lowcore */
+ brasl %r14,setup_lowcore_early
+
+ /* Save image */
+ brasl %r14,swsusp_save
+
+ /* Switch on lowcore protection */
+ stctg %c0,%c0,__SF_EMPTY(%r15)
+ oi __SF_EMPTY+4(%r15),0x10
+ lctlg %c0,%c0,__SF_EMPTY(%r15)
+
+ /* Restore prefix register and return */
+ lghi %r1,0x1000
+ spx 0x318(%r1)
+ lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
+ lghi %r2,0
+ br %r14
+
+/*
+ * Restore saved memory image to correct place and restore register context.
+ * Then we return to the function that called swsusp_arch_suspend().
+ * swsusp_arch_resume() runs with disabled interrupts.
+ */
+ .globl swsusp_arch_resume
+swsusp_arch_resume:
+ stmg %r6,%r15,__SF_GPRS(%r15)
+ lgr %r1,%r15
+ aghi %r15,-STACK_FRAME_OVERHEAD
+ stg %r1,__SF_BACKCHAIN(%r15)
+
+ /* Save boot cpu number */
+ brasl %r14,smp_get_phys_cpu_id
+ lgr %r10,%r2
+
+ /* Deactivate DAT */
+ stnsm __SF_EMPTY(%r15),0xfb
+
+ /* Switch off lowcore protection */
+ stctg %c0,%c0,__SF_EMPTY(%r15)
+ ni __SF_EMPTY+4(%r15),0xef
+ lctlg %c0,%c0,__SF_EMPTY(%r15)
+
+ /* Set prefix page to zero */
+ xc __SF_EMPTY(4,%r15),__SF_EMPTY(%r15)
+ spx __SF_EMPTY(%r15)
+
+ /* Restore saved image */
+ larl %r1,restore_pblist
+ lg %r1,0(%r1)
+ ltgr %r1,%r1
+ jz 2f
+0:
+ lg %r2,8(%r1)
+ lg %r4,0(%r1)
+ lghi %r3,PAGE_SIZE
+ lghi %r5,PAGE_SIZE
+1:
+ mvcle %r2,%r4,0
+ jo 1b
+ lg %r1,16(%r1)
+ ltgr %r1,%r1
+ jnz 0b
+2:
+ ptlb /* flush tlb */
+
+ /* Restore registers */
+ lghi %r13,0x1000 /* %r1 = pointer to save arae */
+
+ spt 0x328(%r13) /* reprogram timer */
+ //sckc 0x330(%r13) /* set clock comparator */
+
+ lctlg %c0,%c15,0x380(%r13) /* load control registers */
+ lam %a0,%a15,0x340(%r13) /* load access registers */
+
+ lfpc 0x31c(%r13) /* load fpu control */
+ ld 0,0x200(%r13) /* load f0 */
+ ld 1,0x208(%r13) /* load f1 */
+ ld 2,0x210(%r13) /* load f2 */
+ ld 3,0x218(%r13) /* load f3 */
+ ld 4,0x220(%r13) /* load f4 */
+ ld 5,0x228(%r13) /* load f5 */
+ ld 6,0x230(%r13) /* load f6 */
+ ld 7,0x238(%r13) /* load f7 */
+ ld 8,0x240(%r13) /* load f8 */
+ ld 9,0x248(%r13) /* load f9 */
+ ld 10,0x250(%r13) /* load f10 */
+ ld 11,0x258(%r13) /* load f11 */
+ ld 12,0x260(%r13) /* load f12 */
+ ld 13,0x268(%r13) /* load f13 */
+ ld 14,0x270(%r13) /* load f14 */
+ ld 15,0x278(%r13) /* load f15 */
+
+ /* Load old stack */
+ lg %r15,0x2f8(%r13)
+
+ /* Pointer to save arae */
+ lghi %r13,0x1000
+
+ /* Switch CPUs */
+ lgr %r2,%r10 /* get cpu id */
+ llgf %r3,0x318(%r13)
+ brasl %r14,smp_switch_boot_cpu_in_resume
+
+ /* Restore prefix register */
+ spx 0x318(%r13)
+
+ /* Switch on lowcore protection */
+ stctg %c0,%c0,__SF_EMPTY(%r15)
+ oi __SF_EMPTY+4(%r15),0x10
+ lctlg %c0,%c0,__SF_EMPTY(%r15)
+
+ /* Activate DAT */
+ stosm __SF_EMPTY(%r15),0x04
+
+ /* Return 0 */
+ lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
+ lghi %r2,0
+ br %r14
diff --git a/block/blk-core.c b/block/blk-core.c
index f6452f69250..b06cf5c2a82 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -26,7 +26,6 @@
#include <linux/swap.h>
#include <linux/writeback.h>
#include <linux/task_io_accounting_ops.h>
-#include <linux/blktrace_api.h>
#include <linux/fault-inject.h>
#define CREATE_TRACE_POINTS
@@ -498,6 +497,11 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
q->backing_dev_info.unplug_io_fn = blk_backing_dev_unplug;
q->backing_dev_info.unplug_io_data = q;
+ q->backing_dev_info.ra_pages =
+ (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
+ q->backing_dev_info.state = 0;
+ q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY;
+
err = bdi_init(&q->backing_dev_info);
if (err) {
kmem_cache_free(blk_requestq_cachep, q);
diff --git a/block/blk-settings.c b/block/blk-settings.c
index d71cedc09c4..7541ea4bf9f 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -96,6 +96,31 @@ void blk_queue_lld_busy(struct request_queue *q, lld_busy_fn *fn)
EXPORT_SYMBOL_GPL(blk_queue_lld_busy);
/**
+ * blk_set_default_limits - reset limits to default values
+ * @limits: the queue_limits structure to reset
+ *
+ * Description:
+ * Returns a queue_limit struct to its default state. Can be used by
+ * stacking drivers like DM that stage table swaps and reuse an
+ * existing device queue.
+ */
+void blk_set_default_limits(struct queue_limits *lim)
+{
+ lim->max_phys_segments = MAX_PHYS_SEGMENTS;
+ lim->max_hw_segments = MAX_HW_SEGMENTS;
+ lim->seg_boundary_mask = BLK_SEG_BOUNDARY_MASK;
+ lim->max_segment_size = MAX_SEGMENT_SIZE;
+ lim->max_sectors = lim->max_hw_sectors = SAFE_MAX_SECTORS;
+ lim->logical_block_size = lim->physical_block_size = lim->io_min = 512;
+ lim->bounce_pfn = BLK_BOUNCE_ANY;
+ lim->alignment_offset = 0;
+ lim->io_opt = 0;
+ lim->misaligned = 0;
+ lim->no_cluster = 0;
+}
+EXPORT_SYMBOL(blk_set_default_limits);
+
+/**
* blk_queue_make_request - define an alternate make_request function for a device
* @q: the request queue for the device to be affected
* @mfn: the alternate make_request function
@@ -123,18 +148,8 @@ void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
* set defaults
*/
q->nr_requests = BLKDEV_MAX_RQ;
- blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);
- blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);
- blk_queue_segment_boundary(q, BLK_SEG_BOUNDARY_MASK);
- blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);
q->make_request_fn = mfn;
- q->backing_dev_info.ra_pages =
- (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
- q->backing_dev_info.state = 0;
- q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY;
- blk_queue_max_sectors(q, SAFE_MAX_SECTORS);
- blk_queue_logical_block_size(q, 512);
blk_queue_dma_alignment(q, 511);
blk_queue_congestion_threshold(q);
q->nr_batching = BLK_BATCH_REQ;
@@ -147,6 +162,8 @@ void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
q->unplug_timer.function = blk_unplug_timeout;
q->unplug_timer.data = (unsigned long)q;
+ blk_set_default_limits(&q->limits);
+
/*
* by default assume old behaviour and bounce for any highmem page
*/
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index ef2f72d4243..833ec18eaa6 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -122,7 +122,6 @@ struct cfq_data {
struct cfq_queue *async_idle_cfqq;
sector_t last_position;
- unsigned long last_end_request;
/*
* tunables, see top of file
@@ -1253,7 +1252,7 @@ static int cfq_forced_dispatch(struct cfq_data *cfqd)
BUG_ON(cfqd->busy_queues);
- cfq_log(cfqd, "forced_dispatch=%d\n", dispatched);
+ cfq_log(cfqd, "forced_dispatch=%d", dispatched);
return dispatched;
}
@@ -2164,9 +2163,6 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq)
if (cfq_cfqq_sync(cfqq))
cfqd->sync_flight--;
- if (!cfq_class_idle(cfqq))
- cfqd->last_end_request = now;
-
if (sync)
RQ_CIC(rq)->last_end_request = now;
@@ -2479,7 +2475,6 @@ static void *cfq_init_queue(struct request_queue *q)
INIT_WORK(&cfqd->unplug_work, cfq_kick_queue);
- cfqd->last_end_request = jiffies;
cfqd->cfq_quantum = cfq_quantum;
cfqd->cfq_fifo_expire[0] = cfq_fifo_expire[0];
cfqd->cfq_fifo_expire[1] = cfq_fifo_expire[1];
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 07e20135f01..0bba148a2c6 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -139,7 +139,7 @@ acpi_status acpi_ev_initialize_op_regions(void);
acpi_status
acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
u32 function,
- acpi_physical_address address,
+ u32 region_offset,
u32 bit_width, acpi_integer * value);
acpi_status
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 16e5210ae93..3d87362d17e 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -362,9 +362,6 @@ extern u8 acpi_gbl_method_executing;
extern u8 acpi_gbl_abort_method;
extern u8 acpi_gbl_db_terminate_threads;
-ACPI_EXTERN int optind;
-ACPI_EXTERN char *optarg;
-
ACPI_EXTERN u8 acpi_gbl_db_opt_tables;
ACPI_EXTERN u8 acpi_gbl_db_opt_stats;
ACPI_EXTERN u8 acpi_gbl_db_opt_ini_methods;
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 2ec394a328e..ee986edfa0d 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -205,6 +205,7 @@ struct acpi_namespace_node {
#define ANOBJ_METHOD_LOCAL 0x08 /* Node is a method local */
#define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */
#define ANOBJ_EVALUATED 0x20 /* Set on first evaluation of node */
+#define ANOBJ_ALLOCATED_BUFFER 0x40 /* Method AML buffer is dynamic (install_method) */
#define ANOBJ_IS_EXTERNAL 0x08 /* i_aSL only: This object created via External() */
#define ANOBJ_METHOD_NO_RETVAL 0x10 /* i_aSL only: Method has no return value */
@@ -788,11 +789,14 @@ struct acpi_bit_register_info {
/* For control registers, both ignored and reserved bits must be preserved */
/*
- * The ACPI spec says to ignore PM1_CTL.SCI_EN (bit 0)
- * but we need to be able to write ACPI_BITREG_SCI_ENABLE directly
- * as a BIOS workaround on some machines.
+ * For PM1 control, the SCI enable bit (bit 0, SCI_EN) is defined by the
+ * ACPI specification to be a "preserved" bit - "OSPM always preserves this
+ * bit position", section 4.7.3.2.1. However, on some machines the OS must
+ * write a one to this bit after resume for the machine to work properly.
+ * To enable this, we no longer attempt to preserve this bit. No machines
+ * are known to fail if the bit is not preserved. (May 2009)
*/
-#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0200 /* Bits 9 */
+#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0200 /* Bit 9 */
#define ACPI_PM1_CONTROL_RESERVED_BITS 0xC1F8 /* Bits 14-15, 3-8 */
#define ACPI_PM1_CONTROL_PRESERVED_BITS \
(ACPI_PM1_CONTROL_IGNORED_BITS | ACPI_PM1_CONTROL_RESERVED_BITS)
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 46cb5b46d28..94cdc2b8cb9 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -99,10 +99,19 @@ acpi_ns_walk_namespace(acpi_object_type type,
acpi_walk_callback user_function,
void *context, void **return_value);
-struct acpi_namespace_node *acpi_ns_get_next_node(acpi_object_type type, struct acpi_namespace_node
- *parent, struct acpi_namespace_node
+struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
+ *parent,
+ struct acpi_namespace_node
*child);
+struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
+ struct
+ acpi_namespace_node
+ *parent,
+ struct
+ acpi_namespace_node
+ *child);
+
/*
* nsparse - table parsing
*/
diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h
index ff851c5df69..067f967eb38 100644
--- a/drivers/acpi/acpica/amlcode.h
+++ b/drivers/acpi/acpica/amlcode.h
@@ -483,7 +483,7 @@ typedef enum {
#define AML_METHOD_ARG_COUNT 0x07
#define AML_METHOD_SERIALIZED 0x08
-#define AML_METHOD_SYNCH_LEVEL 0xF0
+#define AML_METHOD_SYNC_LEVEL 0xF0
/* METHOD_FLAGS_ARG_COUNT is not used internally, define additional flags */
diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c
index dab3f48f0b4..02e6caad4a7 100644
--- a/drivers/acpi/acpica/dsobject.c
+++ b/drivers/acpi/acpica/dsobject.c
@@ -734,7 +734,8 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state,
/* Local ID (0-7) is (AML opcode - base AML_LOCAL_OP) */
- obj_desc->reference.value = opcode - AML_LOCAL_OP;
+ obj_desc->reference.value =
+ ((u32)opcode) - AML_LOCAL_OP;
obj_desc->reference.class = ACPI_REFCLASS_LOCAL;
#ifndef ACPI_NO_METHOD_EXECUTION
@@ -754,7 +755,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state,
/* Arg ID (0-6) is (AML opcode - base AML_ARG_OP) */
- obj_desc->reference.value = opcode - AML_ARG_OP;
+ obj_desc->reference.value = ((u32)opcode) - AML_ARG_OP;
obj_desc->reference.class = ACPI_REFCLASS_ARG;
#ifndef ACPI_NO_METHOD_EXECUTION
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index b4c87b5053e..584d766e6f1 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -1386,14 +1386,19 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state,
case AML_BREAK_POINT_OP:
- /* Call up to the OS service layer to handle this */
-
- status =
- acpi_os_signal(ACPI_SIGNAL_BREAKPOINT,
- "Executed AML Breakpoint opcode");
+ /*
+ * Set the single-step flag. This will cause the debugger (if present)
+ * to break to the console within the AML debugger at the start of the
+ * next AML instruction.
+ */
+ ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE);
+ ACPI_DEBUGGER_EXEC(acpi_os_printf
+ ("**break** Executed AML BreakPoint opcode\n"));
- /* If and when it returns, all done. */
+ /* Call to the OSL in case OS wants a piece of the action */
+ status = acpi_os_signal(ACPI_SIGNAL_BREAKPOINT,
+ "Executed AML Breakpoint opcode");
break;
case AML_BREAK_OP:
diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c
index 40f92bf7dce..e46c821cf57 100644
--- a/drivers/acpi/acpica/dswstate.c
+++ b/drivers/acpi/acpica/dswstate.c
@@ -102,7 +102,7 @@ acpi_ds_result_pop(union acpi_operand_object **object,
/* Return object of the top element and clean that top element result stack */
walk_state->result_count--;
- index = walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM;
+ index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM;
*object = state->results.obj_desc[index];
if (!*object) {
@@ -186,7 +186,7 @@ acpi_ds_result_push(union acpi_operand_object * object,
/* Assign the address of object to the top free element of result stack */
- index = walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM;
+ index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM;
state->results.obj_desc[index] = object;
walk_state->result_count++;
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 538d6326455..98c7f9c6265 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -275,7 +275,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)
*
* PARAMETERS: region_obj - Internal region object
* Function - Read or Write operation
- * Address - Where in the space to read or write
+ * region_offset - Where in the region to read or write
* bit_width - Field width in bits (8, 16, 32, or 64)
* Value - Pointer to in or out value, must be
* full 64-bit acpi_integer
@@ -290,7 +290,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)
acpi_status
acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
u32 function,
- acpi_physical_address address,
+ u32 region_offset,
u32 bit_width, acpi_integer * value)
{
acpi_status status;
@@ -396,7 +396,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
"Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
&region_obj->region.handler->address_space, handler,
- ACPI_FORMAT_NATIVE_UINT(address),
+ ACPI_FORMAT_NATIVE_UINT(region_obj->region.address +
+ region_offset),
acpi_ut_get_region_name(region_obj->region.
space_id)));
@@ -412,8 +413,9 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
/* Call the handler */
- status = handler(function, address, bit_width, value,
- handler_desc->address_space.context,
+ status = handler(function,
+ (region_obj->region.address + region_offset),
+ bit_width, value, handler_desc->address_space.context,
region_obj2->extra.region_context);
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index d0a080747ec..4721f58fe42 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -51,7 +51,7 @@
ACPI_MODULE_NAME("evxfevnt")
/* Local prototypes */
-acpi_status
+static acpi_status
acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
struct acpi_gpe_block_info *gpe_block, void *context);
@@ -785,7 +785,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_gpe_device)
* block device. NULL if the GPE is one of the FADT-defined GPEs.
*
******************************************************************************/
-acpi_status
+static acpi_status
acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
struct acpi_gpe_block_info *gpe_block, void *context)
{
diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c
index 3deb20a126b..277fd609611 100644
--- a/drivers/acpi/acpica/exconfig.c
+++ b/drivers/acpi/acpica/exconfig.c
@@ -47,6 +47,7 @@
#include "acnamesp.h"
#include "actables.h"
#include "acdispat.h"
+#include "acevents.h"
#define _COMPONENT ACPI_EXECUTER
ACPI_MODULE_NAME("exconfig")
@@ -57,6 +58,10 @@ acpi_ex_add_table(u32 table_index,
struct acpi_namespace_node *parent_node,
union acpi_operand_object **ddb_handle);
+static acpi_status
+acpi_ex_region_read(union acpi_operand_object *obj_desc,
+ u32 length, u8 *buffer);
+
/*******************************************************************************
*
* FUNCTION: acpi_ex_add_table
@@ -91,6 +96,7 @@ acpi_ex_add_table(u32 table_index,
/* Init the table handle */
+ obj_desc->common.flags |= AOPOBJ_DATA_VALID;
obj_desc->reference.class = ACPI_REFCLASS_TABLE;
*ddb_handle = obj_desc;
@@ -229,6 +235,8 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
walk_state);
if (ACPI_FAILURE(status)) {
(void)acpi_ex_unload_table(ddb_handle);
+
+ acpi_ut_remove_reference(ddb_handle);
return_ACPI_STATUS(status);
}
}
@@ -254,6 +262,47 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
/*******************************************************************************
*
+ * FUNCTION: acpi_ex_region_read
+ *
+ * PARAMETERS: obj_desc - Region descriptor
+ * Length - Number of bytes to read
+ * Buffer - Pointer to where to put the data
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Read data from an operation region. The read starts from the
+ * beginning of the region.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_region_read(union acpi_operand_object *obj_desc, u32 length, u8 *buffer)
+{
+ acpi_status status;
+ acpi_integer value;
+ u32 region_offset = 0;
+ u32 i;
+
+ /* Bytewise reads */
+
+ for (i = 0; i < length; i++) {
+ status = acpi_ev_address_space_dispatch(obj_desc, ACPI_READ,
+ region_offset, 8,
+ &value);
+ if (ACPI_FAILURE(status)) {
+ return status;
+ }
+
+ *buffer = (u8)value;
+ buffer++;
+ region_offset++;
+ }
+
+ return AE_OK;
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ex_load_op
*
* PARAMETERS: obj_desc - Region or Buffer/Field where the table will be
@@ -314,18 +363,23 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
}
}
- /*
- * Map the table header and get the actual table length. The region
- * length is not guaranteed to be the same as the table length.
- */
- table = acpi_os_map_memory(obj_desc->region.address,
- sizeof(struct acpi_table_header));
+ /* Get the table header first so we can get the table length */
+
+ table = ACPI_ALLOCATE(sizeof(struct acpi_table_header));
if (!table) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
+ status =
+ acpi_ex_region_read(obj_desc,
+ sizeof(struct acpi_table_header),
+ ACPI_CAST_PTR(u8, table));
length = table->length;
- acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
+ ACPI_FREE(table);
+
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
/* Must have at least an ACPI table header */
@@ -334,10 +388,19 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
}
/*
- * The memory region is not guaranteed to remain stable and we must
- * copy the table to a local buffer. For example, the memory region
- * is corrupted after suspend on some machines. Dynamically loaded
- * tables are usually small, so this overhead is minimal.
+ * The original implementation simply mapped the table, with no copy.
+ * However, the memory region is not guaranteed to remain stable and
+ * we must copy the table to a local buffer. For example, the memory
+ * region is corrupted after suspend on some machines. Dynamically
+ * loaded tables are usually small, so this overhead is minimal.
+ *
+ * The latest implementation (5/2009) does not use a mapping at all.
+ * We use the low-level operation region interface to read the table
+ * instead of the obvious optimization of using a direct mapping.
+ * This maintains a consistent use of operation regions across the
+ * entire subsystem. This is important if additional processing must
+ * be performed in the (possibly user-installed) operation region
+ * handler. For example, acpi_exec and ASLTS depend on this.
*/
/* Allocate a buffer for the table */
@@ -347,17 +410,16 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
return_ACPI_STATUS(AE_NO_MEMORY);
}
- /* Map the entire table and copy it */
+ /* Read the entire table */
- table = acpi_os_map_memory(obj_desc->region.address, length);
- if (!table) {
+ status = acpi_ex_region_read(obj_desc, length,
+ ACPI_CAST_PTR(u8,
+ table_desc.pointer));
+ if (ACPI_FAILURE(status)) {
ACPI_FREE(table_desc.pointer);
- return_ACPI_STATUS(AE_NO_MEMORY);
+ return_ACPI_STATUS(status);
}
- ACPI_MEMCPY(table_desc.pointer, table, length);
- acpi_os_unmap_memory(table, length);
-
table_desc.address = obj_desc->region.address;
break;
@@ -454,6 +516,10 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
return_ACPI_STATUS(status);
}
+ /* Remove the reference by added by acpi_ex_store above */
+
+ acpi_ut_remove_reference(ddb_handle);
+
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
@@ -495,13 +561,18 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
/*
* Validate the handle
- * Although the handle is partially validated in acpi_ex_reconfiguration(),
+ * Although the handle is partially validated in acpi_ex_reconfiguration()
* when it calls acpi_ex_resolve_operands(), the handle is more completely
* validated here.
+ *
+ * Handle must be a valid operand object of type reference. Also, the
+ * ddb_handle must still be marked valid (table has not been previously
+ * unloaded)
*/
if ((!ddb_handle) ||
(ACPI_GET_DESCRIPTOR_TYPE(ddb_handle) != ACPI_DESC_TYPE_OPERAND) ||
- (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE)) {
+ (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE) ||
+ (!(ddb_handle->common.flags & AOPOBJ_DATA_VALID))) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
@@ -509,6 +580,12 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
table_index = table_desc->reference.value;
+ /* Ensure the table is still loaded */
+
+ if (!acpi_tb_is_table_loaded(table_index)) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
@@ -530,8 +607,10 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
(void)acpi_tb_release_owner_id(table_index);
acpi_tb_set_table_loaded_flag(table_index, FALSE);
- /* Table unloaded, remove a reference to the ddb_handle object */
-
- acpi_ut_remove_reference(ddb_handle);
+ /*
+ * Invalidate the handle. We do this because the handle may be stored
+ * in a named object and may not be actually deleted until much later.
+ */
+ ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID;
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c
index a57ad2564ab..02b25d233d9 100644
--- a/drivers/acpi/acpica/excreate.c
+++ b/drivers/acpi/acpica/excreate.c
@@ -502,7 +502,7 @@ acpi_ex_create_method(u8 * aml_start,
* ACPI 2.0: sync_level = sync_level in method declaration
*/
obj_desc->method.sync_level = (u8)
- ((method_flags & AML_METHOD_SYNCH_LEVEL) >> 4);
+ ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4);
}
/* Attach the new object to the method Node */
diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c
index 89d141fdae0..ec524614e70 100644
--- a/drivers/acpi/acpica/exdump.c
+++ b/drivers/acpi/acpica/exdump.c
@@ -120,9 +120,11 @@ static struct acpi_exdump_info acpi_ex_dump_event[2] = {
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET(event.os_semaphore), "OsSemaphore"}
};
-static struct acpi_exdump_info acpi_ex_dump_method[8] = {
+static struct acpi_exdump_info acpi_ex_dump_method[9] = {
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_method), NULL},
- {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.param_count), "ParamCount"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.method_flags), "Method Flags"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.param_count),
+ "Parameter Count"},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.sync_level), "Sync Level"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET(method.mutex), "Mutex"},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.owner_id), "Owner Id"},
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
index 99cee61e655..d4075b82102 100644
--- a/drivers/acpi/acpica/exfldio.c
+++ b/drivers/acpi/acpica/exfldio.c
@@ -222,7 +222,7 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc,
{
acpi_status status;
union acpi_operand_object *rgn_desc;
- acpi_physical_address address;
+ u32 region_offset;
ACPI_FUNCTION_TRACE(ex_access_region);
@@ -243,7 +243,7 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc,
* 3) The current offset into the field
*/
rgn_desc = obj_desc->common_field.region_obj;
- address = rgn_desc->region.address +
+ region_offset =
obj_desc->common_field.base_byte_offset + field_datum_byte_offset;
if ((function & ACPI_IO_MASK) == ACPI_READ) {
@@ -260,16 +260,18 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc,
obj_desc->common_field.access_byte_width,
obj_desc->common_field.base_byte_offset,
field_datum_byte_offset, ACPI_CAST_PTR(void,
- address)));
+ (rgn_desc->
+ region.
+ address +
+ region_offset))));
/* Invoke the appropriate address_space/op_region handler */
- status = acpi_ev_address_space_dispatch(rgn_desc, function,
- address,
- ACPI_MUL_8(obj_desc->
- common_field.
- access_byte_width),
- value);
+ status =
+ acpi_ev_address_space_dispatch(rgn_desc, function, region_offset,
+ ACPI_MUL_8(obj_desc->common_field.
+ access_byte_width),
+ value);
if (ACPI_FAILURE(status)) {
if (status == AE_NOT_IMPLEMENTED) {
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
index d301c1f363e..2f0114202b0 100644
--- a/drivers/acpi/acpica/exmutex.c
+++ b/drivers/acpi/acpica/exmutex.c
@@ -83,6 +83,15 @@ void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc)
if (obj_desc->mutex.prev) {
(obj_desc->mutex.prev)->mutex.next = obj_desc->mutex.next;
+
+ /*
+ * Migrate the previous sync level associated with this mutex to the
+ * previous mutex on the list so that it may be preserved. This handles
+ * the case where several mutexes have been acquired at the same level,
+ * but are not released in opposite order.
+ */
+ (obj_desc->mutex.prev)->mutex.original_sync_level =
+ obj_desc->mutex.original_sync_level;
} else {
thread->acquired_mutex_list = obj_desc->mutex.next;
}
@@ -349,6 +358,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{
acpi_status status = AE_OK;
+ u8 previous_sync_level;
ACPI_FUNCTION_TRACE(ex_release_mutex);
@@ -373,11 +383,12 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
walk_state->thread->thread_id)
&& (obj_desc != acpi_gbl_global_lock_mutex)) {
ACPI_ERROR((AE_INFO,
- "Thread %lX cannot release Mutex [%4.4s] acquired by thread %lX",
- (unsigned long)walk_state->thread->thread_id,
+ "Thread %p cannot release Mutex [%4.4s] acquired by thread %p",
+ ACPI_CAST_PTR(void, walk_state->thread->thread_id),
acpi_ut_get_node_name(obj_desc->mutex.node),
- (unsigned long)obj_desc->mutex.owner_thread->
- thread_id));
+ ACPI_CAST_PTR(void,
+ obj_desc->mutex.owner_thread->
+ thread_id)));
return_ACPI_STATUS(AE_AML_NOT_OWNER);
}
@@ -391,10 +402,14 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
}
/*
- * The sync level of the mutex must be less than or equal to the current
- * sync level
+ * The sync level of the mutex must be equal to the current sync level. In
+ * other words, the current level means that at least one mutex at that
+ * level is currently being held. Attempting to release a mutex of a
+ * different level can only mean that the mutex ordering rule is being
+ * violated. This behavior is clarified in ACPI 4.0 specification.
*/
- if (obj_desc->mutex.sync_level > walk_state->thread->current_sync_level) {
+ if (obj_desc->mutex.sync_level !=
+ walk_state->thread->current_sync_level) {
ACPI_ERROR((AE_INFO,
"Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %d current %d",
acpi_ut_get_node_name(obj_desc->mutex.node),
@@ -403,14 +418,24 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
return_ACPI_STATUS(AE_AML_MUTEX_ORDER);
}
+ /*
+ * Get the previous sync_level from the head of the acquired mutex list.
+ * This handles the case where several mutexes at the same level have been
+ * acquired, but are not released in reverse order.
+ */
+ previous_sync_level =
+ walk_state->thread->acquired_mutex_list->mutex.original_sync_level;
+
status = acpi_ex_release_mutex_object(obj_desc);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
if (obj_desc->mutex.acquisition_depth == 0) {
- /* Restore the original sync_level */
+ /* Restore the previous sync_level */
- walk_state->thread->current_sync_level =
- obj_desc->mutex.original_sync_level;
+ walk_state->thread->current_sync_level = previous_sync_level;
}
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c
index 90d606196c9..6efd07a4f77 100644
--- a/drivers/acpi/acpica/exstore.c
+++ b/drivers/acpi/acpica/exstore.c
@@ -193,10 +193,12 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
case ACPI_REFCLASS_TABLE:
+ /* Case for ddb_handle */
+
ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT,
"Table Index 0x%X\n",
source_desc->reference.value));
- break;
+ return;
default:
break;
diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c
index 7b2fb602b5c..23d5505cb1f 100644
--- a/drivers/acpi/acpica/hwregs.c
+++ b/drivers/acpi/acpica/hwregs.c
@@ -81,9 +81,9 @@ acpi_status acpi_hw_clear_acpi_status(void)
ACPI_FUNCTION_TRACE(hw_clear_acpi_status);
- ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %0llX\n",
+ ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n",
ACPI_BITMASK_ALL_FIXED_STATUS,
- acpi_gbl_xpm1a_status.address));
+ ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address)));
lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock);
diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c
index aceb9311196..efc971ab7d6 100644
--- a/drivers/acpi/acpica/nsalloc.c
+++ b/drivers/acpi/acpica/nsalloc.c
@@ -334,9 +334,7 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node)
/* Get the next node in this scope (NULL if none) */
- child_node =
- acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node,
- child_node);
+ child_node = acpi_ns_get_next_node(parent_node, child_node);
if (child_node) {
/* Found a child node - detach any attached object */
@@ -345,8 +343,7 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node)
/* Check if this node has any children */
- if (acpi_ns_get_next_node
- (ACPI_TYPE_ANY, child_node, NULL)) {
+ if (child_node->child) {
/*
* There is at least one child of this node,
* visit the node
@@ -432,9 +429,7 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id)
* Get the next child of this parent node. When child_node is NULL,
* the first child of the parent is returned
*/
- child_node =
- acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node,
- child_node);
+ child_node = acpi_ns_get_next_node(parent_node, child_node);
if (deletion_node) {
acpi_ns_delete_children(deletion_node);
@@ -452,8 +447,7 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id)
/* Check if this node has any children */
- if (acpi_ns_get_next_node
- (ACPI_TYPE_ANY, child_node, NULL)) {
+ if (child_node->child) {
/*
* There is at least one child of this node,
* visit the node
diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c
index ae3dc10a7e8..af8e6bcee07 100644
--- a/drivers/acpi/acpica/nsnames.c
+++ b/drivers/acpi/acpica/nsnames.c
@@ -149,7 +149,7 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node)
name_buffer = ACPI_ALLOCATE_ZEROED(size);
if (!name_buffer) {
- ACPI_ERROR((AE_INFO, "Allocation failure"));
+ ACPI_ERROR((AE_INFO, "Could not allocate %u bytes", (u32)size));
return_PTR(NULL);
}
diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c
index 3eb20bfda9d..60f3af08d28 100644
--- a/drivers/acpi/acpica/nsobject.c
+++ b/drivers/acpi/acpica/nsobject.c
@@ -213,6 +213,15 @@ void acpi_ns_detach_object(struct acpi_namespace_node *node)
return_VOID;
}
+ if (node->flags & ANOBJ_ALLOCATED_BUFFER) {
+
+ /* Free the dynamic aml buffer */
+
+ if (obj_desc->common.type == ACPI_TYPE_METHOD) {
+ ACPI_FREE(obj_desc->method.aml_start);
+ }
+ }
+
/* Clear the entry in all cases */
node->object = NULL;
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index d9e8cbc6e67..7f8e066b12a 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -144,7 +144,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
pathname = acpi_ns_get_external_pathname(node);
if (!pathname) {
- pathname = ACPI_CAST_PTR(char, predefined->info.name);
+ return AE_OK; /* Could not get pathname, ignore */
}
/*
@@ -230,10 +230,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
}
exit:
- if (pathname != predefined->info.name) {
- ACPI_FREE(pathname);
- }
-
+ ACPI_FREE(pathname);
return (status);
}
diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c
index f9b4f51bf8f..7e865639a92 100644
--- a/drivers/acpi/acpica/nssearch.c
+++ b/drivers/acpi/acpica/nssearch.c
@@ -45,6 +45,10 @@
#include "accommon.h"
#include "acnamesp.h"
+#ifdef ACPI_ASL_COMPILER
+#include "amlcode.h"
+#endif
+
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nssearch")
diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c
index 83e3aa6d4b9..35539df5c75 100644
--- a/drivers/acpi/acpica/nswalk.c
+++ b/drivers/acpi/acpica/nswalk.c
@@ -52,8 +52,7 @@ ACPI_MODULE_NAME("nswalk")
*
* FUNCTION: acpi_ns_get_next_node
*
- * PARAMETERS: Type - Type of node to be searched for
- * parent_node - Parent node whose children we are
+ * PARAMETERS: parent_node - Parent node whose children we are
* getting
* child_node - Previous child that was found.
* The NEXT child will be returned
@@ -66,27 +65,68 @@ ACPI_MODULE_NAME("nswalk")
* within Scope is returned.
*
******************************************************************************/
-struct acpi_namespace_node *acpi_ns_get_next_node(acpi_object_type type, struct acpi_namespace_node
- *parent_node, struct acpi_namespace_node
+struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
+ *parent_node,
+ struct acpi_namespace_node
*child_node)
{
- struct acpi_namespace_node *next_node = NULL;
-
ACPI_FUNCTION_ENTRY();
if (!child_node) {
/* It's really the parent's _scope_ that we want */
- next_node = parent_node->child;
+ return parent_node->child;
}
- else {
- /* Start search at the NEXT node */
-
- next_node = acpi_ns_get_next_valid_node(child_node);
+ /*
+ * Get the next node.
+ *
+ * If we are at the end of this peer list, return NULL
+ */
+ if (child_node->flags & ANOBJ_END_OF_PEER_LIST) {
+ return NULL;
}
+ /* Otherwise just return the next peer */
+
+ return child_node->peer;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_next_node_typed
+ *
+ * PARAMETERS: Type - Type of node to be searched for
+ * parent_node - Parent node whose children we are
+ * getting
+ * child_node - Previous child that was found.
+ * The NEXT child will be returned
+ *
+ * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if
+ * none is found.
+ *
+ * DESCRIPTION: Return the next peer node within the namespace. If Handle
+ * is valid, Scope is ignored. Otherwise, the first node
+ * within Scope is returned.
+ *
+ ******************************************************************************/
+
+struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
+ struct
+ acpi_namespace_node
+ *parent_node,
+ struct
+ acpi_namespace_node
+ *child_node)
+{
+ struct acpi_namespace_node *next_node = NULL;
+
+ ACPI_FUNCTION_ENTRY();
+
+ next_node = acpi_ns_get_next_node(parent_node, child_node);
+
+
/* If any type is OK, we are done */
if (type == ACPI_TYPE_ANY) {
@@ -186,9 +226,7 @@ acpi_ns_walk_namespace(acpi_object_type type,
/* Get the next node in this scope. Null if not found */
status = AE_OK;
- child_node =
- acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node,
- child_node);
+ child_node = acpi_ns_get_next_node(parent_node, child_node);
if (child_node) {
/* Found next child, get the type if we are not searching for ANY */
@@ -269,8 +307,7 @@ acpi_ns_walk_namespace(acpi_object_type type,
* function has specified that the maximum depth has been reached.
*/
if ((level < max_depth) && (status != AE_CTRL_DEPTH)) {
- if (acpi_ns_get_next_node
- (ACPI_TYPE_ANY, child_node, NULL)) {
+ if (child_node->child) {
/* There is at least one child of this node, visit it */
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
index 9589fea2499..f23593d6add 100644
--- a/drivers/acpi/acpica/nsxfname.c
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -45,6 +45,8 @@
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
+#include "acparser.h"
+#include "amlcode.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsxfname")
@@ -358,3 +360,151 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer)
}
ACPI_EXPORT_SYMBOL(acpi_get_object_info)
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_install_method
+ *
+ * PARAMETERS: Buffer - An ACPI table containing one control method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a control method into the namespace. If the method
+ * name already exists in the namespace, it is overwritten. The
+ * input buffer must contain a valid DSDT or SSDT containing a
+ * single control method.
+ *
+ ******************************************************************************/
+acpi_status acpi_install_method(u8 *buffer)
+{
+ struct acpi_table_header *table =
+ ACPI_CAST_PTR(struct acpi_table_header, buffer);
+ u8 *aml_buffer;
+ u8 *aml_start;
+ char *path;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *method_obj;
+ struct acpi_parse_state parser_state;
+ u32 aml_length;
+ u16 opcode;
+ u8 method_flags;
+ acpi_status status;
+
+ /* Parameter validation */
+
+ if (!buffer) {
+ return AE_BAD_PARAMETER;
+ }
+
+ /* Table must be a DSDT or SSDT */
+
+ if (!ACPI_COMPARE_NAME(table->signature, ACPI_SIG_DSDT) &&
+ !ACPI_COMPARE_NAME(table->signature, ACPI_SIG_SSDT)) {
+ return AE_BAD_HEADER;
+ }
+
+ /* First AML opcode in the table must be a control method */
+
+ parser_state.aml = buffer + sizeof(struct acpi_table_header);
+ opcode = acpi_ps_peek_opcode(&parser_state);
+ if (opcode != AML_METHOD_OP) {
+ return AE_BAD_PARAMETER;
+ }
+
+ /* Extract method information from the raw AML */
+
+ parser_state.aml += acpi_ps_get_opcode_size(opcode);
+ parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state);
+ path = acpi_ps_get_next_namestring(&parser_state);
+ method_flags = *parser_state.aml++;
+ aml_start = parser_state.aml;
+ aml_length = ACPI_PTR_DIFF(parser_state.pkg_end, aml_start);
+
+ /*
+ * Allocate resources up-front. We don't want to have to delete a new
+ * node from the namespace if we cannot allocate memory.
+ */
+ aml_buffer = ACPI_ALLOCATE(aml_length);
+ if (!aml_buffer) {
+ return AE_NO_MEMORY;
+ }
+
+ method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
+ if (!method_obj) {
+ ACPI_FREE(aml_buffer);
+ return AE_NO_MEMORY;
+ }
+
+ /* Lock namespace for acpi_ns_lookup, we may be creating a new node */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* The lookup either returns an existing node or creates a new one */
+
+ status =
+ acpi_ns_lookup(NULL, path, ACPI_TYPE_METHOD, ACPI_IMODE_LOAD_PASS1,
+ ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND,
+ NULL, &node);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ if (ACPI_FAILURE(status)) { /* ns_lookup */
+ if (status != AE_ALREADY_EXISTS) {
+ goto error_exit;
+ }
+
+ /* Node existed previously, make sure it is a method node */
+
+ if (node->type != ACPI_TYPE_METHOD) {
+ status = AE_TYPE;
+ goto error_exit;
+ }
+ }
+
+ /* Copy the method AML to the local buffer */
+
+ ACPI_MEMCPY(aml_buffer, aml_start, aml_length);
+
+ /* Initialize the method object with the new method's information */
+
+ method_obj->method.aml_start = aml_buffer;
+ method_obj->method.aml_length = aml_length;
+
+ method_obj->method.param_count = (u8)
+ (method_flags & AML_METHOD_ARG_COUNT);
+
+ method_obj->method.method_flags = (u8)
+ (method_flags & ~AML_METHOD_ARG_COUNT);
+
+ if (method_flags & AML_METHOD_SERIALIZED) {
+ method_obj->method.sync_level = (u8)
+ ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4);
+ }
+
+ /*
+ * Now that it is complete, we can attach the new method object to
+ * the method Node (detaches/deletes any existing object)
+ */
+ status = acpi_ns_attach_object(node, method_obj, ACPI_TYPE_METHOD);
+
+ /*
+ * Flag indicates AML buffer is dynamic, must be deleted later.
+ * Must be set only after attach above.
+ */
+ node->flags |= ANOBJ_ALLOCATED_BUFFER;
+
+ /* Remove local reference to the method object */
+
+ acpi_ut_remove_reference(method_obj);
+ return status;
+
+error_exit:
+
+ ACPI_FREE(aml_buffer);
+ ACPI_FREE(method_obj);
+ return status;
+}
+ACPI_EXPORT_SYMBOL(acpi_install_method)
diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c
index 1c7efc15225..4071bad4458 100644
--- a/drivers/acpi/acpica/nsxfobj.c
+++ b/drivers/acpi/acpica/nsxfobj.c
@@ -162,6 +162,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_type)
acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle)
{
struct acpi_namespace_node *node;
+ struct acpi_namespace_node *parent_node;
acpi_status status;
if (!ret_handle) {
@@ -189,12 +190,12 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle)
/* Get the parent entry */
- *ret_handle =
- acpi_ns_convert_entry_to_handle(acpi_ns_get_parent_node(node));
+ parent_node = acpi_ns_get_parent_node(node);
+ *ret_handle = acpi_ns_convert_entry_to_handle(parent_node);
/* Return exception if parent is null */
- if (!acpi_ns_get_parent_node(node)) {
+ if (!parent_node) {
status = AE_NULL_ENTRY;
}
@@ -268,7 +269,7 @@ acpi_get_next_object(acpi_object_type type,
/* Internal function does the real work */
- node = acpi_ns_get_next_node(type, parent_node, child_node);
+ node = acpi_ns_get_next_node_typed(type, parent_node, child_node);
if (!node) {
status = AE_NOT_FOUND;
goto unlock_and_exit;
diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c
index 88b5a2c4814..3c4dcc3d106 100644
--- a/drivers/acpi/acpica/rscalc.c
+++ b/drivers/acpi/acpica/rscalc.c
@@ -547,7 +547,7 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object,
if (!package_element ||
(package_element->common.type != ACPI_TYPE_PACKAGE)) {
- return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
}
/*
@@ -593,9 +593,6 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object,
} else {
temp_size_needed +=
acpi_ns_get_pathname_length((*sub_object_list)->reference.node);
- if (!temp_size_needed) {
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
}
} else {
/*
diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c
index 69a2aa5b5d8..395212bcd19 100644
--- a/drivers/acpi/acpica/rsxface.c
+++ b/drivers/acpi/acpica/rsxface.c
@@ -338,13 +338,17 @@ acpi_resource_to_address64(struct acpi_resource *resource,
switch (resource->type) {
case ACPI_RESOURCE_TYPE_ADDRESS16:
- address16 = (struct acpi_resource_address16 *)&resource->data;
+ address16 =
+ ACPI_CAST_PTR(struct acpi_resource_address16,
+ &resource->data);
ACPI_COPY_ADDRESS(out, address16);
break;
case ACPI_RESOURCE_TYPE_ADDRESS32:
- address32 = (struct acpi_resource_address32 *)&resource->data;
+ address32 =
+ ACPI_CAST_PTR(struct acpi_resource_address32,
+ &resource->data);
ACPI_COPY_ADDRESS(out, address32);
break;
diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c
index 71e655d14cb..82b02dcb942 100644
--- a/drivers/acpi/acpica/tbfadt.c
+++ b/drivers/acpi/acpica/tbfadt.c
@@ -284,9 +284,9 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length)
if (length > sizeof(struct acpi_table_fadt)) {
ACPI_WARNING((AE_INFO,
"FADT (revision %u) is longer than ACPI 2.0 version, "
- "truncating length 0x%X to 0x%zX",
- table->revision, (unsigned)length,
- sizeof(struct acpi_table_fadt)));
+ "truncating length 0x%X to 0x%X",
+ table->revision, length,
+ (u32)sizeof(struct acpi_table_fadt)));
}
/* Clear the entire local FADT */
@@ -441,7 +441,7 @@ static void acpi_tb_convert_fadt(void)
&acpi_gbl_FADT,
fadt_info_table
[i].length),
- address32);
+ (u64) address32);
}
}
}
@@ -469,7 +469,6 @@ static void acpi_tb_convert_fadt(void)
static void acpi_tb_validate_fadt(void)
{
char *name;
- u32 *address32;
struct acpi_generic_address *address64;
u8 length;
u32 i;
@@ -505,15 +504,12 @@ static void acpi_tb_validate_fadt(void)
for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) {
/*
- * Generate pointers to the 32-bit and 64-bit addresses, get the
- * register length (width), and the register name
+ * Generate pointer to the 64-bit address, get the register
+ * length (width) and the register name
*/
address64 = ACPI_ADD_PTR(struct acpi_generic_address,
&acpi_gbl_FADT,
fadt_info_table[i].address64);
- address32 =
- ACPI_ADD_PTR(u32, &acpi_gbl_FADT,
- fadt_info_table[i].address32);
length =
*ACPI_ADD_PTR(u8, &acpi_gbl_FADT,
fadt_info_table[i].length);
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index f865d5a096d..63e82329a9e 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -472,7 +472,7 @@ acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index)
* lock may block, and also since the execution of a namespace walk
* must be allowed to use the interpreter.
*/
- acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
+ (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
acpi_ns_delete_namespace_by_owner(owner_id);
diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c
index 919624f123d..0f0c64bf8ac 100644
--- a/drivers/acpi/acpica/utcopy.c
+++ b/drivers/acpi/acpica/utcopy.c
@@ -676,6 +676,7 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc,
{
u16 reference_count;
union acpi_operand_object *next_object;
+ acpi_status status;
/* Save fields from destination that we don't want to overwrite */
@@ -768,6 +769,28 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc,
}
break;
+ /*
+ * For Mutex and Event objects, we cannot simply copy the underlying
+ * OS object. We must create a new one.
+ */
+ case ACPI_TYPE_MUTEX:
+
+ status = acpi_os_create_mutex(&dest_desc->mutex.os_mutex);
+ if (ACPI_FAILURE(status)) {
+ return status;
+ }
+ break;
+
+ case ACPI_TYPE_EVENT:
+
+ status = acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0,
+ &dest_desc->event.
+ os_semaphore);
+ if (ACPI_FAILURE(status)) {
+ return status;
+ }
+ break;
+
default:
/* Nothing to do for other simple objects */
break;
diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c
index 38821f53042..527d729f681 100644
--- a/drivers/acpi/acpica/utdebug.c
+++ b/drivers/acpi/acpica/utdebug.c
@@ -179,9 +179,9 @@ acpi_debug_print(u32 requested_debug_level,
if (thread_id != acpi_gbl_prev_thread_id) {
if (ACPI_LV_THREADS & acpi_dbg_level) {
acpi_os_printf
- ("\n**** Context Switch from TID %lX to TID %lX ****\n\n",
- (unsigned long)acpi_gbl_prev_thread_id,
- (unsigned long)thread_id);
+ ("\n**** Context Switch from TID %p to TID %p ****\n\n",
+ ACPI_CAST_PTR(void, acpi_gbl_prev_thread_id),
+ ACPI_CAST_PTR(void, thread_id));
}
acpi_gbl_prev_thread_id = thread_id;
@@ -194,7 +194,7 @@ acpi_debug_print(u32 requested_debug_level,
acpi_os_printf("%8s-%04ld ", module_name, line_number);
if (ACPI_LV_THREADS & acpi_dbg_level) {
- acpi_os_printf("[%04lX] ", (unsigned long)thread_id);
+ acpi_os_printf("[%p] ", ACPI_CAST_PTR(void, thread_id));
}
acpi_os_printf("[%02ld] %-22.22s: ",
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index a5ee23bc4f5..bc171031508 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -75,6 +75,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
union acpi_operand_object *handler_desc;
union acpi_operand_object *second_desc;
union acpi_operand_object *next_desc;
+ union acpi_operand_object **last_obj_ptr;
ACPI_FUNCTION_TRACE_PTR(ut_delete_internal_obj, object);
@@ -223,6 +224,26 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
*/
handler_desc = object->region.handler;
if (handler_desc) {
+ next_desc =
+ handler_desc->address_space.region_list;
+ last_obj_ptr =
+ &handler_desc->address_space.region_list;
+
+ /* Remove the region object from the handler's list */
+
+ while (next_desc) {
+ if (next_desc == object) {
+ *last_obj_ptr =
+ next_desc->region.next;
+ break;
+ }
+
+ /* Walk the linked list of handler */
+
+ last_obj_ptr = &next_desc->region.next;
+ next_desc = next_desc->region.next;
+ }
+
if (handler_desc->address_space.handler_flags &
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) {
diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c
index 1c9e250caef..fbe782348b0 100644
--- a/drivers/acpi/acpica/utmisc.c
+++ b/drivers/acpi/acpica/utmisc.c
@@ -1033,11 +1033,12 @@ acpi_error(const char *module_name, u32 line_number, const char *format, ...)
{
va_list args;
- acpi_os_printf("ACPI Error (%s-%04d): ", module_name, line_number);
+ acpi_os_printf("ACPI Error: ");
va_start(args, format);
acpi_os_vprintf(format, args);
- acpi_os_printf(" [%X]\n", ACPI_CA_VERSION);
+ acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name,
+ line_number);
va_end(args);
}
@@ -1047,12 +1048,12 @@ acpi_exception(const char *module_name,
{
va_list args;
- acpi_os_printf("ACPI Exception (%s-%04d): %s, ", module_name,
- line_number, acpi_format_exception(status));
+ acpi_os_printf("ACPI Exception: %s, ", acpi_format_exception(status));
va_start(args, format);
acpi_os_vprintf(format, args);
- acpi_os_printf(" [%X]\n", ACPI_CA_VERSION);
+ acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name,
+ line_number);
va_end(args);
}
@@ -1061,11 +1062,12 @@ acpi_warning(const char *module_name, u32 line_number, const char *format, ...)
{
va_list args;
- acpi_os_printf("ACPI Warning (%s-%04d): ", module_name, line_number);
+ acpi_os_printf("ACPI Warning: ");
va_start(args, format);
acpi_os_vprintf(format, args);
- acpi_os_printf(" [%X]\n", ACPI_CA_VERSION);
+ acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name,
+ line_number);
va_end(args);
}
@@ -1074,10 +1076,6 @@ acpi_info(const char *module_name, u32 line_number, const char *format, ...)
{
va_list args;
- /*
- * Removed module_name, line_number, and acpica version, not needed
- * for info output
- */
acpi_os_printf("ACPI: ");
va_start(args, format);
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index 26c93a748e6..80bb6515411 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -230,17 +230,18 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id)
if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) {
if (i == mutex_id) {
ACPI_ERROR((AE_INFO,
- "Mutex [%s] already acquired by this thread [%X]",
+ "Mutex [%s] already acquired by this thread [%p]",
acpi_ut_get_mutex_name
(mutex_id),
- this_thread_id));
+ ACPI_CAST_PTR(void,
+ this_thread_id)));
return (AE_ALREADY_ACQUIRED);
}
ACPI_ERROR((AE_INFO,
- "Invalid acquire order: Thread %X owns [%s], wants [%s]",
- this_thread_id,
+ "Invalid acquire order: Thread %p owns [%s], wants [%s]",
+ ACPI_CAST_PTR(void, this_thread_id),
acpi_ut_get_mutex_name(i),
acpi_ut_get_mutex_name(mutex_id)));
@@ -251,24 +252,24 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id)
#endif
ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
- "Thread %lX attempting to acquire Mutex [%s]\n",
- (unsigned long)this_thread_id,
+ "Thread %p attempting to acquire Mutex [%s]\n",
+ ACPI_CAST_PTR(void, this_thread_id),
acpi_ut_get_mutex_name(mutex_id)));
status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex,
ACPI_WAIT_FOREVER);
if (ACPI_SUCCESS(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
- "Thread %lX acquired Mutex [%s]\n",
- (unsigned long)this_thread_id,
+ "Thread %p acquired Mutex [%s]\n",
+ ACPI_CAST_PTR(void, this_thread_id),
acpi_ut_get_mutex_name(mutex_id)));
acpi_gbl_mutex_info[mutex_id].use_count++;
acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id;
} else {
ACPI_EXCEPTION((AE_INFO, status,
- "Thread %lX could not acquire Mutex [%X]",
- (unsigned long)this_thread_id, mutex_id));
+ "Thread %p could not acquire Mutex [%X]",
+ ACPI_CAST_PTR(void, this_thread_id), mutex_id));
}
return (status);
@@ -293,9 +294,8 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id)
ACPI_FUNCTION_NAME(ut_release_mutex);
this_thread_id = acpi_os_get_thread_id();
- ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
- "Thread %lX releasing Mutex [%s]\n",
- (unsigned long)this_thread_id,
+ ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %p releasing Mutex [%s]\n",
+ ACPI_CAST_PTR(void, this_thread_id),
acpi_ut_get_mutex_name(mutex_id)));
if (mutex_id > ACPI_MAX_MUTEX) {
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index b22cec97ea1..c7a527c08a0 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -38,7 +38,6 @@
#include <linux/hdreg.h>
#include <linux/spinlock.h>
#include <linux/compat.h>
-#include <linux/blktrace_api.h>
#include <asm/uaccess.h>
#include <asm/io.h>
diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c
index 60de5a01e71..f703f547824 100644
--- a/drivers/block/mg_disk.c
+++ b/drivers/block/mg_disk.c
@@ -22,13 +22,12 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
+#include <linux/mg_disk.h>
#define MG_RES_SEC (CONFIG_MG_DISK_RES << 1)
/* name for block device */
#define MG_DISK_NAME "mgd"
-/* name for platform device */
-#define MG_DEV_NAME "mg_disk"
#define MG_DISK_MAJ 0
#define MG_DISK_MAX_PART 16
@@ -103,33 +102,8 @@
#define MG_TMAX_SWRST_TO_RDY 500
#define MG_TMAX_RSTOUT 3000
-/* device attribution */
-/* use mflash as boot device */
-#define MG_BOOT_DEV (1 << 0)
-/* use mflash as storage device */
-#define MG_STORAGE_DEV (1 << 1)
-/* same as MG_STORAGE_DEV, but bootloader already done reset sequence */
-#define MG_STORAGE_DEV_SKIP_RST (1 << 2)
-
#define MG_DEV_MASK (MG_BOOT_DEV | MG_STORAGE_DEV | MG_STORAGE_DEV_SKIP_RST)
-/* names of GPIO resource */
-#define MG_RST_PIN "mg_rst"
-/* except MG_BOOT_DEV, reset-out pin should be assigned */
-#define MG_RSTOUT_PIN "mg_rstout"
-
-/* private driver data */
-struct mg_drv_data {
- /* disk resource */
- u32 use_polling;
-
- /* device attribution */
- u32 dev_attr;
-
- /* internally used */
- struct mg_host *host;
-};
-
/* main structure for mflash driver */
struct mg_host {
struct device *dev;
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index aaeeb544228..34cbb7f3efa 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -120,7 +120,7 @@ static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
struct request *req)
{
- struct ps3disk_private *priv = dev->sbd.core.driver_data;
+ struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
int write = rq_data_dir(req), res;
const char *op = write ? "write" : "read";
u64 start_sector, sectors;
@@ -168,7 +168,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
static int ps3disk_submit_flush_request(struct ps3_storage_device *dev,
struct request *req)
{
- struct ps3disk_private *priv = dev->sbd.core.driver_data;
+ struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
u64 res;
dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
@@ -213,7 +213,7 @@ static void ps3disk_do_request(struct ps3_storage_device *dev,
static void ps3disk_request(struct request_queue *q)
{
struct ps3_storage_device *dev = q->queuedata;
- struct ps3disk_private *priv = dev->sbd.core.driver_data;
+ struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
if (priv->req) {
dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
@@ -245,7 +245,7 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
- priv = dev->sbd.core.driver_data;
+ priv = ps3_system_bus_get_drvdata(&dev->sbd);
req = priv->req;
if (!req) {
dev_dbg(&dev->sbd.core,
@@ -364,7 +364,7 @@ static void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs,
static int ps3disk_identify(struct ps3_storage_device *dev)
{
- struct ps3disk_private *priv = dev->sbd.core.driver_data;
+ struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
struct lv1_ata_cmnd_block ata_cmnd;
u16 *id = dev->bounce_buf;
u64 res;
@@ -445,7 +445,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
goto fail;
}
- dev->sbd.core.driver_data = priv;
+ ps3_system_bus_set_drvdata(_dev, priv);
spin_lock_init(&priv->lock);
dev->bounce_size = BOUNCE_SIZE;
@@ -523,7 +523,7 @@ fail_free_bounce:
kfree(dev->bounce_buf);
fail_free_priv:
kfree(priv);
- dev->sbd.core.driver_data = NULL;
+ ps3_system_bus_set_drvdata(_dev, NULL);
fail:
mutex_lock(&ps3disk_mask_mutex);
__clear_bit(devidx, &ps3disk_mask);
@@ -534,7 +534,7 @@ fail:
static int ps3disk_remove(struct ps3_system_bus_device *_dev)
{
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
- struct ps3disk_private *priv = dev->sbd.core.driver_data;
+ struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
mutex_lock(&ps3disk_mask_mutex);
__clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS,
@@ -548,7 +548,7 @@ static int ps3disk_remove(struct ps3_system_bus_device *_dev)
ps3stor_teardown(dev);
kfree(dev->bounce_buf);
kfree(priv);
- dev->sbd.core.driver_data = NULL;
+ ps3_system_bus_set_drvdata(_dev, NULL);
return 0;
}
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index 8eddef373a9..095f97e6066 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -14,8 +14,10 @@
#include <linux/seq_file.h>
#include <asm/firmware.h>
+#include <asm/iommu.h>
#include <asm/lv1call.h>
#include <asm/ps3.h>
+#include <asm/ps3gpu.h>
#define DEVICE_NAME "ps3vram"
@@ -45,8 +47,6 @@
#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
-#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
-
#define CACHE_PAGE_PRESENT 1
#define CACHE_PAGE_DIRTY 2
@@ -72,8 +72,7 @@ struct ps3vram_priv {
u64 memory_handle;
u64 context_handle;
u32 *ctrl;
- u32 *reports;
- u8 __iomem *ddr_base;
+ void *reports;
u8 *xdr_buf;
u32 *fifo_base;
@@ -81,8 +80,8 @@ struct ps3vram_priv {
struct ps3vram_cache cache;
- /* Used to serialize cache/DMA operations */
- struct mutex lock;
+ spinlock_t lock; /* protecting list of bios */
+ struct bio_list list;
};
@@ -103,15 +102,15 @@ static char *size = "256M";
module_param(size, charp, 0);
MODULE_PARM_DESC(size, "memory size");
-static u32 *ps3vram_get_notifier(u32 *reports, int notifier)
+static u32 *ps3vram_get_notifier(void *reports, int notifier)
{
- return (void *)reports + DMA_NOTIFIER_OFFSET_BASE +
+ return reports + DMA_NOTIFIER_OFFSET_BASE +
DMA_NOTIFIER_SIZE * notifier;
}
static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
int i;
@@ -122,7 +121,7 @@ static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev)
static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
unsigned int timeout_ms)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
@@ -137,7 +136,7 @@ static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
static void ps3vram_init_ring(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET;
@@ -146,7 +145,7 @@ static void ps3vram_init_ring(struct ps3_system_bus_device *dev)
static int ps3vram_wait_ring(struct ps3_system_bus_device *dev,
unsigned int timeout_ms)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
@@ -175,7 +174,7 @@ static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, u32 tag,
static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
int status;
ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
@@ -183,20 +182,17 @@ static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev)
priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
/* asking the HV for a blit will kick the FIFO */
- status = lv1_gpu_context_attribute(priv->context_handle,
- L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0,
- 0, 0, 0);
+ status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0);
if (status)
- dev_err(&dev->core,
- "%s: lv1_gpu_context_attribute failed %d\n", __func__,
- status);
+ dev_err(&dev->core, "%s: lv1_gpu_fb_blit failed %d\n",
+ __func__, status);
priv->fifo_ptr = priv->fifo_base;
}
static void ps3vram_fire_ring(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
int status;
mutex_lock(&ps3_gpu_mutex);
@@ -205,13 +201,10 @@ static void ps3vram_fire_ring(struct ps3_system_bus_device *dev)
(priv->fifo_ptr - priv->fifo_base) * sizeof(u32);
/* asking the HV for a blit will kick the FIFO */
- status = lv1_gpu_context_attribute(priv->context_handle,
- L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0,
- 0, 0, 0);
+ status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0);
if (status)
- dev_err(&dev->core,
- "%s: lv1_gpu_context_attribute failed %d\n", __func__,
- status);
+ dev_err(&dev->core, "%s: lv1_gpu_fb_blit failed %d\n",
+ __func__, status);
if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) >
FIFO_SIZE - 1024) {
@@ -225,7 +218,7 @@ static void ps3vram_fire_ring(struct ps3_system_bus_device *dev)
static void ps3vram_bind(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1);
ps3vram_out_ring(priv, 0x31337303);
@@ -248,7 +241,7 @@ static int ps3vram_upload(struct ps3_system_bus_device *dev,
unsigned int src_offset, unsigned int dst_offset,
int len, int count)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
ps3vram_begin_ring(priv, UPLOAD_SUBCH,
NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
@@ -280,7 +273,7 @@ static int ps3vram_download(struct ps3_system_bus_device *dev,
unsigned int src_offset, unsigned int dst_offset,
int len, int count)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
@@ -310,7 +303,7 @@ static int ps3vram_download(struct ps3_system_bus_device *dev,
static void ps3vram_cache_evict(struct ps3_system_bus_device *dev, int entry)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
struct ps3vram_cache *cache = &priv->cache;
if (!(cache->tags[entry].flags & CACHE_PAGE_DIRTY))
@@ -332,7 +325,7 @@ static void ps3vram_cache_evict(struct ps3_system_bus_device *dev, int entry)
static void ps3vram_cache_load(struct ps3_system_bus_device *dev, int entry,
unsigned int address)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
struct ps3vram_cache *cache = &priv->cache;
dev_dbg(&dev->core, "Fetching %d: 0x%08x\n", entry, address);
@@ -352,7 +345,7 @@ static void ps3vram_cache_load(struct ps3_system_bus_device *dev, int entry,
static void ps3vram_cache_flush(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
struct ps3vram_cache *cache = &priv->cache;
int i;
@@ -366,7 +359,7 @@ static void ps3vram_cache_flush(struct ps3_system_bus_device *dev)
static unsigned int ps3vram_cache_match(struct ps3_system_bus_device *dev,
loff_t address)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
struct ps3vram_cache *cache = &priv->cache;
unsigned int base;
unsigned int offset;
@@ -400,7 +393,7 @@ static unsigned int ps3vram_cache_match(struct ps3_system_bus_device *dev,
static int ps3vram_cache_init(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
priv->cache.page_count = CACHE_PAGE_COUNT;
priv->cache.page_size = CACHE_PAGE_SIZE;
@@ -419,7 +412,7 @@ static int ps3vram_cache_init(struct ps3_system_bus_device *dev)
static void ps3vram_cache_cleanup(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
ps3vram_cache_flush(dev);
kfree(priv->cache.tags);
@@ -428,7 +421,7 @@ static void ps3vram_cache_cleanup(struct ps3_system_bus_device *dev)
static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
unsigned int cached, count;
dev_dbg(&dev->core, "%s: from=0x%08x len=0x%zx\n", __func__,
@@ -449,8 +442,6 @@ static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from,
offset = (unsigned int) (from & (priv->cache.page_size - 1));
avail = priv->cache.page_size - offset;
- mutex_lock(&priv->lock);
-
entry = ps3vram_cache_match(dev, from);
cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
@@ -462,8 +453,6 @@ static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from,
avail = count;
memcpy(buf, priv->xdr_buf + cached, avail);
- mutex_unlock(&priv->lock);
-
buf += avail;
count -= avail;
from += avail;
@@ -476,7 +465,7 @@ static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from,
static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to,
size_t len, size_t *retlen, const u_char *buf)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
unsigned int cached, count;
if (to >= priv->size)
@@ -494,8 +483,6 @@ static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to,
offset = (unsigned int) (to & (priv->cache.page_size - 1));
avail = priv->cache.page_size - offset;
- mutex_lock(&priv->lock);
-
entry = ps3vram_cache_match(dev, to);
cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
@@ -509,8 +496,6 @@ static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to,
priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY;
- mutex_unlock(&priv->lock);
-
buf += avail;
count -= avail;
to += avail;
@@ -543,28 +528,26 @@ static const struct file_operations ps3vram_proc_fops = {
static void __devinit ps3vram_proc_init(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
struct proc_dir_entry *pde;
- pde = proc_create(DEVICE_NAME, 0444, NULL, &ps3vram_proc_fops);
- if (!pde) {
+ pde = proc_create_data(DEVICE_NAME, 0444, NULL, &ps3vram_proc_fops,
+ priv);
+ if (!pde)
dev_warn(&dev->core, "failed to create /proc entry\n");
- return;
- }
- pde->data = priv;
}
-static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
+static struct bio *ps3vram_do_bio(struct ps3_system_bus_device *dev,
+ struct bio *bio)
{
- struct ps3_system_bus_device *dev = q->queuedata;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
int write = bio_data_dir(bio) == WRITE;
const char *op = write ? "write" : "read";
loff_t offset = bio->bi_sector << 9;
int error = 0;
struct bio_vec *bvec;
unsigned int i;
-
- dev_dbg(&dev->core, "%s\n", __func__);
+ struct bio *next;
bio_for_each_segment(bvec, bio, i) {
/* PS3 is ppc64, so we don't handle highmem */
@@ -585,6 +568,7 @@ static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
if (retlen != len) {
dev_err(&dev->core, "Short %s\n", op);
+ error = -EIO;
goto out;
}
@@ -594,7 +578,35 @@ static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
dev_dbg(&dev->core, "%s completed\n", op);
out:
+ spin_lock_irq(&priv->lock);
+ bio_list_pop(&priv->list);
+ next = bio_list_peek(&priv->list);
+ spin_unlock_irq(&priv->lock);
+
bio_endio(bio, error);
+ return next;
+}
+
+static int ps3vram_make_request(struct request_queue *q, struct bio *bio)
+{
+ struct ps3_system_bus_device *dev = q->queuedata;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
+ int busy;
+
+ dev_dbg(&dev->core, "%s\n", __func__);
+
+ spin_lock_irq(&priv->lock);
+ busy = !bio_list_empty(&priv->list);
+ bio_list_add(&priv->list, bio);
+ spin_unlock_irq(&priv->lock);
+
+ if (busy)
+ return 0;
+
+ do {
+ bio = ps3vram_do_bio(dev, bio);
+ } while (bio);
+
return 0;
}
@@ -604,8 +616,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
int error, status;
struct request_queue *queue;
struct gendisk *gendisk;
- u64 ddr_lpar, ctrl_lpar, info_lpar, reports_lpar, ddr_size,
- reports_size;
+ u64 ddr_size, ddr_lpar, ctrl_lpar, info_lpar, reports_lpar,
+ reports_size, xdr_lpar;
char *rest;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -614,10 +626,9 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
goto fail;
}
- mutex_init(&priv->lock);
- dev->core.driver_data = priv;
-
- priv = dev->core.driver_data;
+ spin_lock_init(&priv->lock);
+ bio_list_init(&priv->list);
+ ps3_system_bus_set_drvdata(dev, priv);
/* Allocate XDR buffer (1MiB aligned) */
priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL,
@@ -636,7 +647,7 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
if (ps3_open_hv_device(dev)) {
dev_err(&dev->core, "ps3_open_hv_device failed\n");
error = -EAGAIN;
- goto out_close_gpu;
+ goto out_free_xdr_buf;
}
/* Request memory */
@@ -660,7 +671,7 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
dev_err(&dev->core, "lv1_gpu_memory_allocate failed %d\n",
status);
error = -ENOMEM;
- goto out_free_xdr_buf;
+ goto out_close_gpu;
}
/* Request context */
@@ -676,9 +687,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
}
/* Map XDR buffer to RSX */
+ xdr_lpar = ps3_mm_phys_to_lpar(__pa(priv->xdr_buf));
status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
- ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
- XDR_BUF_SIZE, 0);
+ xdr_lpar, XDR_BUF_SIZE,
+ CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |
+ CBE_IOPTE_M);
if (status) {
dev_err(&dev->core, "lv1_gpu_context_iomap failed %d\n",
status);
@@ -686,19 +699,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
goto out_free_context;
}
- priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE);
-
- if (!priv->ddr_base) {
- dev_err(&dev->core, "ioremap DDR failed\n");
- error = -ENOMEM;
- goto out_free_context;
- }
-
priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
if (!priv->ctrl) {
dev_err(&dev->core, "ioremap CTRL failed\n");
error = -ENOMEM;
- goto out_unmap_vram;
+ goto out_unmap_context;
}
priv->reports = ioremap(reports_lpar, reports_size);
@@ -775,8 +780,9 @@ out_unmap_reports:
iounmap(priv->reports);
out_unmap_ctrl:
iounmap(priv->ctrl);
-out_unmap_vram:
- iounmap(priv->ddr_base);
+out_unmap_context:
+ lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, xdr_lpar,
+ XDR_BUF_SIZE, CBE_IOPTE_M);
out_free_context:
lv1_gpu_context_free(priv->context_handle);
out_free_memory:
@@ -787,14 +793,14 @@ out_free_xdr_buf:
free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
fail_free_priv:
kfree(priv);
- dev->core.driver_data = NULL;
+ ps3_system_bus_set_drvdata(dev, NULL);
fail:
return error;
}
static int ps3vram_remove(struct ps3_system_bus_device *dev)
{
- struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
del_gendisk(priv->gendisk);
put_disk(priv->gendisk);
@@ -803,13 +809,15 @@ static int ps3vram_remove(struct ps3_system_bus_device *dev)
ps3vram_cache_cleanup(dev);
iounmap(priv->reports);
iounmap(priv->ctrl);
- iounmap(priv->ddr_base);
+ lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
+ ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
+ XDR_BUF_SIZE, CBE_IOPTE_M);
lv1_gpu_context_free(priv->context_handle);
lv1_gpu_memory_free(priv->memory_handle);
ps3_close_hv_device(dev);
free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
kfree(priv);
- dev->core.driver_data = NULL;
+ ps3_system_bus_set_drvdata(dev, NULL);
return 0;
}
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index 54481a88776..86105efb4eb 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -4,7 +4,7 @@
* This HVC device driver provides terminal access using
* z/VM IUCV communication paths.
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008, 2009
*
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
*/
@@ -15,6 +15,7 @@
#include <asm/ebcdic.h>
#include <linux/ctype.h>
#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/init.h>
#include <linux/mempool.h>
#include <linux/moduleparam.h>
@@ -74,6 +75,7 @@ struct hvc_iucv_private {
wait_queue_head_t sndbuf_waitq; /* wait for send completion */
struct list_head tty_outqueue; /* outgoing IUCV messages */
struct list_head tty_inqueue; /* incoming IUCV messages */
+ struct device *dev; /* device structure */
};
struct iucv_tty_buffer {
@@ -542,7 +544,68 @@ static void flush_sndbuf_sync(struct hvc_iucv_private *priv)
if (sync_wait)
wait_event_timeout(priv->sndbuf_waitq,
- tty_outqueue_empty(priv), HZ);
+ tty_outqueue_empty(priv), HZ/10);
+}
+
+/**
+ * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up
+ * @priv: Pointer to hvc_iucv_private structure
+ *
+ * This routine severs an existing IUCV communication path and hangs
+ * up the underlying HVC terminal device.
+ * The hang-up occurs only if an IUCV communication path is established;
+ * otherwise there is no need to hang up the terminal device.
+ *
+ * The IUCV HVC hang-up is separated into two steps:
+ * 1. After the IUCV path has been severed, the iucv_state is set to
+ * IUCV_SEVERED.
+ * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the
+ * IUCV_SEVERED state causes the tty hang-up in the HVC layer.
+ *
+ * If the tty has not yet been opened, clean up the hvc_iucv_private
+ * structure to allow re-connects.
+ * If the tty has been opened, let get_chars() return -EPIPE to signal
+ * the HVC layer to hang up the tty and, if so, wake up the HVC thread
+ * to call get_chars()...
+ *
+ * Special notes on hanging up a HVC terminal instantiated as console:
+ * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops)
+ * 2. do_tty_hangup() calls tty->ops->close() for console_filp
+ * => no hangup notifier is called by HVC (default)
+ * 2. hvc_close() returns because of tty_hung_up_p(filp)
+ * => no delete notifier is called!
+ * Finally, the back-end is not being notified, thus, the tty session is
+ * kept active (TTY_OPEN) to be ready for re-connects.
+ *
+ * Locking: spin_lock(&priv->lock) w/o disabling bh
+ */
+static void hvc_iucv_hangup(struct hvc_iucv_private *priv)
+{
+ struct iucv_path *path;
+
+ path = NULL;
+ spin_lock(&priv->lock);
+ if (priv->iucv_state == IUCV_CONNECTED) {
+ path = priv->path;
+ priv->path = NULL;
+ priv->iucv_state = IUCV_SEVERED;
+ if (priv->tty_state == TTY_CLOSED)
+ hvc_iucv_cleanup(priv);
+ else
+ /* console is special (see above) */
+ if (priv->is_console) {
+ hvc_iucv_cleanup(priv);
+ priv->tty_state = TTY_OPENED;
+ } else
+ hvc_kick();
+ }
+ spin_unlock(&priv->lock);
+
+ /* finally sever path (outside of priv->lock due to lock ordering) */
+ if (path) {
+ iucv_path_sever(path, NULL);
+ iucv_path_free(path);
+ }
}
/**
@@ -735,11 +798,8 @@ out_path_handled:
* @ipuser: User specified data for this path
* (AF_IUCV: port/service name and originator port)
*
- * The function also severs the path (as required by the IUCV protocol) and
- * sets the iucv state to IUCV_SEVERED for the associated struct
- * hvc_iucv_private instance. Later, the IUCV_SEVERED state triggers a tty
- * hangup (hvc_iucv_get_chars() / hvc_iucv_write()).
- * If tty portion of the HVC is closed, clean up the outqueue.
+ * This function calls the hvc_iucv_hangup() function for the
+ * respective IUCV HVC terminal.
*
* Locking: struct hvc_iucv_private->lock
*/
@@ -747,33 +807,7 @@ static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
{
struct hvc_iucv_private *priv = path->private;
- spin_lock(&priv->lock);
- priv->iucv_state = IUCV_SEVERED;
-
- /* If the tty has not yet been opened, clean up the hvc_iucv_private
- * structure to allow re-connects.
- * This is also done for our console device because console hangups
- * are handled specially and no notifier is called by HVC.
- * The tty session is active (TTY_OPEN) and ready for re-connects...
- *
- * If it has been opened, let get_chars() return -EPIPE to signal the
- * HVC layer to hang up the tty.
- * If so, we need to wake up the HVC thread to call get_chars()...
- */
- priv->path = NULL;
- if (priv->tty_state == TTY_CLOSED)
- hvc_iucv_cleanup(priv);
- else
- if (priv->is_console) {
- hvc_iucv_cleanup(priv);
- priv->tty_state = TTY_OPENED;
- } else
- hvc_kick();
- spin_unlock(&priv->lock);
-
- /* finally sever path (outside of priv->lock due to lock ordering) */
- iucv_path_sever(path, ipuser);
- iucv_path_free(path);
+ hvc_iucv_hangup(priv);
}
/**
@@ -853,6 +887,37 @@ static void hvc_iucv_msg_complete(struct iucv_path *path,
destroy_tty_buffer_list(&list_remove);
}
+/**
+ * hvc_iucv_pm_freeze() - Freeze PM callback
+ * @dev: IUVC HVC terminal device
+ *
+ * Sever an established IUCV communication path and
+ * trigger a hang-up of the underlying HVC terminal.
+ */
+static int hvc_iucv_pm_freeze(struct device *dev)
+{
+ struct hvc_iucv_private *priv = dev_get_drvdata(dev);
+
+ local_bh_disable();
+ hvc_iucv_hangup(priv);
+ local_bh_enable();
+
+ return 0;
+}
+
+/**
+ * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback
+ * @dev: IUVC HVC terminal device
+ *
+ * Wake up the HVC thread to trigger hang-up and respective
+ * HVC back-end notifier invocations.
+ */
+static int hvc_iucv_pm_restore_thaw(struct device *dev)
+{
+ hvc_kick();
+ return 0;
+}
+
/* HVC operations */
static struct hv_ops hvc_iucv_ops = {
@@ -863,6 +928,20 @@ static struct hv_ops hvc_iucv_ops = {
.notifier_hangup = hvc_iucv_notifier_hangup,
};
+/* Suspend / resume device operations */
+static 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,
+};
+
+/* IUCV HVC device driver */
+static struct device_driver hvc_iucv_driver = {
+ .name = KMSG_COMPONENT,
+ .bus = &iucv_bus,
+ .pm = &hvc_iucv_pm_ops,
+};
+
/**
* hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance
* @id: hvc_iucv_table index
@@ -897,14 +976,12 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console)
/* set console flag */
priv->is_console = is_console;
- /* finally allocate hvc */
+ /* allocate hvc device */
priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */
HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256);
if (IS_ERR(priv->hvc)) {
rc = PTR_ERR(priv->hvc);
- free_page((unsigned long) priv->sndbuf);
- kfree(priv);
- return rc;
+ goto out_error_hvc;
}
/* notify HVC thread instead of using polling */
@@ -915,8 +992,45 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console)
memcpy(priv->srv_name, name, 8);
ASCEBC(priv->srv_name, 8);
+ /* create and setup device */
+ priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL);
+ if (!priv->dev) {
+ rc = -ENOMEM;
+ goto out_error_dev;
+ }
+ dev_set_name(priv->dev, "hvc_iucv%d", id);
+ dev_set_drvdata(priv->dev, priv);
+ priv->dev->bus = &iucv_bus;
+ priv->dev->parent = iucv_root;
+ priv->dev->driver = &hvc_iucv_driver;
+ priv->dev->release = (void (*)(struct device *)) kfree;
+ rc = device_register(priv->dev);
+ if (rc) {
+ kfree(priv->dev);
+ goto out_error_dev;
+ }
+
hvc_iucv_table[id] = priv;
return 0;
+
+out_error_dev:
+ hvc_remove(priv->hvc);
+out_error_hvc:
+ free_page((unsigned long) priv->sndbuf);
+ kfree(priv);
+
+ return rc;
+}
+
+/**
+ * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances
+ */
+static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv)
+{
+ hvc_remove(priv->hvc);
+ device_unregister(priv->dev);
+ free_page((unsigned long) priv->sndbuf);
+ kfree(priv);
}
/**
@@ -1109,6 +1223,11 @@ static int __init hvc_iucv_init(void)
goto out_error;
}
+ /* register IUCV HVC device driver */
+ rc = driver_register(&hvc_iucv_driver);
+ if (rc)
+ goto out_error;
+
/* parse hvc_iucv_allow string and create z/VM user ID filter list */
if (hvc_iucv_filter_string) {
rc = hvc_iucv_setup_filter(hvc_iucv_filter_string);
@@ -1183,15 +1302,14 @@ out_error_iucv:
iucv_unregister(&hvc_iucv_handler, 0);
out_error_hvc:
for (i = 0; i < hvc_iucv_devices; i++)
- if (hvc_iucv_table[i]) {
- if (hvc_iucv_table[i]->hvc)
- hvc_remove(hvc_iucv_table[i]->hvc);
- kfree(hvc_iucv_table[i]);
- }
+ if (hvc_iucv_table[i])
+ hvc_iucv_destroy(hvc_iucv_table[i]);
out_error_memory:
mempool_destroy(hvc_iucv_mempool);
kmem_cache_destroy(hvc_iucv_buffer_cache);
out_error:
+ if (hvc_iucv_filter)
+ kfree(hvc_iucv_filter);
hvc_iucv_devices = 0; /* ensure that we do not provide any device */
return rc;
}
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index afbe45676d7..f424d394a28 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -33,48 +33,64 @@
struct ps3flash_private {
struct mutex mutex; /* Bounce buffer mutex */
+ u64 chunk_sectors;
+ int tag; /* Start sector of buffer, -1 if invalid */
+ bool dirty;
};
static struct ps3_storage_device *ps3flash_dev;
-static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev,
- u64 lpar, u64 start_sector,
- u64 sectors, int write)
+static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,
+ u64 start_sector, int write)
{
- u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors,
+ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
+ start_sector, priv->chunk_sectors,
write);
if (res) {
dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
__LINE__, write ? "write" : "read", res);
return -EIO;
}
- return sectors;
+ return 0;
}
-static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev,
- u64 start_sector, u64 sectors,
- unsigned int sector_offset)
+static int ps3flash_writeback(struct ps3_storage_device *dev)
{
- u64 max_sectors, lpar;
+ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ int res;
- max_sectors = dev->bounce_size / dev->blk_size;
- if (sectors > max_sectors) {
- dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %llu\n",
- __func__, __LINE__, max_sectors);
- sectors = max_sectors;
- }
+ if (!priv->dirty || priv->tag < 0)
+ return 0;
- lpar = dev->bounce_lpar + sector_offset * dev->blk_size;
- return ps3flash_read_write_sectors(dev, lpar, start_sector, sectors,
- 0);
+ res = ps3flash_read_write_sectors(dev, priv->tag, 1);
+ if (res)
+ return res;
+
+ priv->dirty = false;
+ return 0;
}
-static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev,
- u64 start_sector)
+static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
{
- u64 sectors = dev->bounce_size / dev->blk_size;
- return ps3flash_read_write_sectors(dev, dev->bounce_lpar, start_sector,
- sectors, 1);
+ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ int res;
+
+ if (start_sector == priv->tag)
+ return 0;
+
+ res = ps3flash_writeback(dev);
+ if (res)
+ return res;
+
+ priv->tag = -1;
+
+ res = ps3flash_read_write_sectors(dev, start_sector, 0);
+ if (res)
+ return res;
+
+ priv->tag = start_sector;
+ return 0;
}
static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
@@ -104,18 +120,19 @@ out:
return res;
}
-static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count,
- loff_t *pos)
+static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
+ size_t count, loff_t *pos)
{
struct ps3_storage_device *dev = ps3flash_dev;
- struct ps3flash_private *priv = dev->sbd.core.driver_data;
- u64 size, start_sector, end_sector, offset;
- ssize_t sectors_read;
+ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ u64 size, sector, offset;
+ int res;
size_t remaining, n;
+ const void *src;
dev_dbg(&dev->sbd.core,
- "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n",
- __func__, __LINE__, count, *pos, buf);
+ "%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",
+ __func__, __LINE__, count, *pos, userbuf, kernelbuf);
size = dev->regions[dev->region_idx].size*dev->blk_size;
if (*pos >= size || !count)
@@ -128,61 +145,63 @@ static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count,
count = size - *pos;
}
- start_sector = *pos / dev->blk_size;
- offset = *pos % dev->blk_size;
- end_sector = DIV_ROUND_UP(*pos + count, dev->blk_size);
+ sector = *pos / dev->bounce_size * priv->chunk_sectors;
+ offset = *pos % dev->bounce_size;
remaining = count;
do {
+ n = min_t(u64, remaining, dev->bounce_size - offset);
+ src = dev->bounce_buf + offset;
+
mutex_lock(&priv->mutex);
- sectors_read = ps3flash_read_sectors(dev, start_sector,
- end_sector-start_sector,
- 0);
- if (sectors_read < 0) {
- mutex_unlock(&priv->mutex);
+ res = ps3flash_fetch(dev, sector);
+ if (res)
goto fail;
- }
- n = min_t(u64, remaining, sectors_read*dev->blk_size-offset);
dev_dbg(&dev->sbd.core,
- "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n",
- __func__, __LINE__, n, dev->bounce_buf+offset, buf);
- if (copy_to_user(buf, dev->bounce_buf+offset, n)) {
- mutex_unlock(&priv->mutex);
- sectors_read = -EFAULT;
- goto fail;
+ "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n",
+ __func__, __LINE__, n, src, userbuf, kernelbuf);
+ if (userbuf) {
+ if (copy_to_user(userbuf, src, n)) {
+ res = -EFAULT;
+ goto fail;
+ }
+ userbuf += n;
+ }
+ if (kernelbuf) {
+ memcpy(kernelbuf, src, n);
+ kernelbuf += n;
}
mutex_unlock(&priv->mutex);
*pos += n;
- buf += n;
remaining -= n;
- start_sector += sectors_read;
+ sector += priv->chunk_sectors;
offset = 0;
} while (remaining > 0);
return count;
fail:
- return sectors_read;
+ mutex_unlock(&priv->mutex);
+ return res;
}
-static ssize_t ps3flash_write(struct file *file, const char __user *buf,
- size_t count, loff_t *pos)
+static ssize_t ps3flash_write(const char __user *userbuf,
+ const void *kernelbuf, size_t count, loff_t *pos)
{
struct ps3_storage_device *dev = ps3flash_dev;
- struct ps3flash_private *priv = dev->sbd.core.driver_data;
- u64 size, chunk_sectors, start_write_sector, end_write_sector,
- end_read_sector, start_read_sector, head, tail, offset;
- ssize_t res;
+ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
+ u64 size, sector, offset;
+ int res = 0;
size_t remaining, n;
- unsigned int sec_off;
+ void *dst;
dev_dbg(&dev->sbd.core,
- "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n",
- __func__, __LINE__, count, *pos, buf);
+ "%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",
+ __func__, __LINE__, count, *pos, userbuf, kernelbuf);
size = dev->regions[dev->region_idx].size*dev->blk_size;
if (*pos >= size || !count)
@@ -195,89 +214,46 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf,
count = size - *pos;
}
- chunk_sectors = dev->bounce_size / dev->blk_size;
-
- start_write_sector = *pos / dev->bounce_size * chunk_sectors;
+ sector = *pos / dev->bounce_size * priv->chunk_sectors;
offset = *pos % dev->bounce_size;
- end_write_sector = DIV_ROUND_UP(*pos + count, dev->bounce_size) *
- chunk_sectors;
-
- end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size);
- start_read_sector = (*pos + count) / dev->blk_size;
-
- /*
- * As we have to write in 256 KiB chunks, while we can read in blk_size
- * (usually 512 bytes) chunks, we perform the following steps:
- * 1. Read from start_write_sector to end_read_sector ("head")
- * 2. Read from start_read_sector to end_write_sector ("tail")
- * 3. Copy data to buffer
- * 4. Write from start_write_sector to end_write_sector
- * All of this is complicated by using only one 256 KiB bounce buffer.
- */
-
- head = end_read_sector - start_write_sector;
- tail = end_write_sector - start_read_sector;
remaining = count;
do {
+ n = min_t(u64, remaining, dev->bounce_size - offset);
+ dst = dev->bounce_buf + offset;
+
mutex_lock(&priv->mutex);
- if (end_read_sector >= start_read_sector) {
- /* Merge head and tail */
- dev_dbg(&dev->sbd.core,
- "Merged head and tail: %llu sectors at %llu\n",
- chunk_sectors, start_write_sector);
- res = ps3flash_read_sectors(dev, start_write_sector,
- chunk_sectors, 0);
- if (res < 0)
+ if (n != dev->bounce_size)
+ res = ps3flash_fetch(dev, sector);
+ else if (sector != priv->tag)
+ res = ps3flash_writeback(dev);
+ if (res)
+ goto fail;
+
+ dev_dbg(&dev->sbd.core,
+ "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n",
+ __func__, __LINE__, n, userbuf, kernelbuf, dst);
+ if (userbuf) {
+ if (copy_from_user(dst, userbuf, n)) {
+ res = -EFAULT;
goto fail;
- } else {
- if (head) {
- /* Read head */
- dev_dbg(&dev->sbd.core,
- "head: %llu sectors at %llu\n", head,
- start_write_sector);
- res = ps3flash_read_sectors(dev,
- start_write_sector,
- head, 0);
- if (res < 0)
- goto fail;
- }
- if (start_read_sector <
- start_write_sector+chunk_sectors) {
- /* Read tail */
- dev_dbg(&dev->sbd.core,
- "tail: %llu sectors at %llu\n", tail,
- start_read_sector);
- sec_off = start_read_sector-start_write_sector;
- res = ps3flash_read_sectors(dev,
- start_read_sector,
- tail, sec_off);
- if (res < 0)
- goto fail;
}
+ userbuf += n;
}
-
- n = min_t(u64, remaining, dev->bounce_size-offset);
- dev_dbg(&dev->sbd.core,
- "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n",
- __func__, __LINE__, n, buf, dev->bounce_buf+offset);
- if (copy_from_user(dev->bounce_buf+offset, buf, n)) {
- res = -EFAULT;
- goto fail;
+ if (kernelbuf) {
+ memcpy(dst, kernelbuf, n);
+ kernelbuf += n;
}
- res = ps3flash_write_chunk(dev, start_write_sector);
- if (res < 0)
- goto fail;
+ priv->tag = sector;
+ priv->dirty = true;
mutex_unlock(&priv->mutex);
*pos += n;
- buf += n;
remaining -= n;
- start_write_sector += chunk_sectors;
- head = 0;
+ sector += priv->chunk_sectors;
offset = 0;
} while (remaining > 0);
@@ -288,6 +264,51 @@ fail:
return res;
}
+static ssize_t ps3flash_user_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return ps3flash_read(buf, NULL, count, pos);
+}
+
+static ssize_t ps3flash_user_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return ps3flash_write(buf, NULL, count, pos);
+}
+
+static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos)
+{
+ return ps3flash_read(NULL, buf, count, &pos);
+}
+
+static ssize_t ps3flash_kernel_write(const void *buf, size_t count,
+ loff_t pos)
+{
+ ssize_t res;
+ int wb;
+
+ res = ps3flash_write(NULL, buf, count, &pos);
+ if (res < 0)
+ return res;
+
+ /* Make kernel writes synchronous */
+ wb = ps3flash_writeback(ps3flash_dev);
+ if (wb)
+ return wb;
+
+ return res;
+}
+
+static int ps3flash_flush(struct file *file, fl_owner_t id)
+{
+ return ps3flash_writeback(ps3flash_dev);
+}
+
+static int ps3flash_fsync(struct file *file, struct dentry *dentry,
+ int datasync)
+{
+ return ps3flash_writeback(ps3flash_dev);
+}
static irqreturn_t ps3flash_interrupt(int irq, void *data)
{
@@ -312,12 +333,18 @@ static irqreturn_t ps3flash_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
-
static const struct file_operations ps3flash_fops = {
.owner = THIS_MODULE,
.llseek = ps3flash_llseek,
- .read = ps3flash_read,
- .write = ps3flash_write,
+ .read = ps3flash_user_read,
+ .write = ps3flash_user_write,
+ .flush = ps3flash_flush,
+ .fsync = ps3flash_fsync,
+};
+
+static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {
+ .read = ps3flash_kernel_read,
+ .write = ps3flash_kernel_write,
};
static struct miscdevice ps3flash_misc = {
@@ -366,11 +393,13 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
goto fail;
}
- dev->sbd.core.driver_data = priv;
+ ps3_system_bus_set_drvdata(&dev->sbd, priv);
mutex_init(&priv->mutex);
+ priv->tag = -1;
dev->bounce_size = ps3flash_bounce_buffer.size;
dev->bounce_buf = ps3flash_bounce_buffer.address;
+ priv->chunk_sectors = dev->bounce_size / dev->blk_size;
error = ps3stor_setup(dev, ps3flash_interrupt);
if (error)
@@ -386,13 +415,15 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n",
__func__, __LINE__, ps3flash_misc.minor);
+
+ ps3_os_area_flash_register(&ps3flash_kernel_ops);
return 0;
fail_teardown:
ps3stor_teardown(dev);
fail_free_priv:
kfree(priv);
- dev->sbd.core.driver_data = NULL;
+ ps3_system_bus_set_drvdata(&dev->sbd, NULL);
fail:
ps3flash_dev = NULL;
return error;
@@ -402,10 +433,11 @@ static int ps3flash_remove(struct ps3_system_bus_device *_dev)
{
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+ ps3_os_area_flash_register(NULL);
misc_deregister(&ps3flash_misc);
ps3stor_teardown(dev);
- kfree(dev->sbd.core.driver_data);
- dev->sbd.core.driver_data = NULL;
+ kfree(ps3_system_bus_get_drvdata(&dev->sbd));
+ ps3_system_bus_set_drvdata(&dev->sbd, NULL);
ps3flash_dev = NULL;
return 0;
}
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 5acd29e6e04..3910ce112a9 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -104,7 +104,7 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf,
struct tty_struct *to = tty->link;
int c;
- if (!to || tty->stopped)
+ if (!to || !to->ldisc || tty->stopped)
return 0;
c = to->receive_room;
@@ -148,7 +148,7 @@ static int pty_chars_in_buffer(struct tty_struct *tty)
int count;
/* We should get the line discipline lock for "tty->link" */
- if (!to || !to->ldisc->ops->chars_in_buffer)
+ if (!to || !to->ldisc || !to->ldisc->ops->chars_in_buffer)
return 0;
/* The ldisc must report 0 if no characters available to be read */
@@ -183,7 +183,7 @@ static void pty_flush_buffer(struct tty_struct *tty)
struct tty_struct *to = tty->link;
unsigned long flags;
- if (!to)
+ if (!to || !to->ldisc)
return;
if (to->ldisc->ops->flush_buffer)
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index 39c8f86dedd..94b3e06d73e 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -148,8 +148,10 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc)
}
}
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- if (err)
+ if (err) {
+ kfree(ld);
return ERR_PTR(err);
+ }
return ld;
}
@@ -262,7 +264,7 @@ const struct file_operations tty_ldiscs_proc_fops = {
* @ld: line discipline
*
* Install an instance of a line discipline into a tty structure. The
- * ldisc must have a reference count above zero to ensure it remains/
+ * ldisc must have a reference count above zero to ensure it remains.
* The tty instance refcount starts at zero.
*
* Locking:
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index d73f5f473e3..f8090e137fe 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -306,11 +306,11 @@ config SENSORS_F71805F
will be called f71805f.
config SENSORS_F71882FG
- tristate "Fintek F71862FG, F71882FG and F8000"
+ tristate "Fintek F71858FG, F71862FG, F71882FG and F8000"
depends on EXPERIMENTAL
help
If you say yes here you get support for hardware monitoring
- features of the Fintek F71882FG/F71883FG, F71862FG/71863FG
+ features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG
and F8000 Super-I/O chips.
This driver can also be built as a module. If so, the module
@@ -418,7 +418,7 @@ config SENSORS_IBMAEM
power sensors and capping hardware in various IBM System X
servers that support Active Energy Manager. This includes
the x3350, x3550, x3650, x3655, x3755, x3850 M2, x3950 M2,
- and certain HS2x/LS2x/QS2x blades.
+ and certain HC10/HS2x/LS2x/QS2x blades.
This driver can also be built as a module. If so, the module
will be called ibmaem.
@@ -787,6 +787,16 @@ config SENSORS_THMC50
This driver can also be built as a module. If so, the module
will be called thmc50.
+config SENSORS_TMP401
+ tristate "Texas Instruments TMP401 and compatibles"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for Texas Instruments TMP401 and
+ TMP411 temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tmp401.
+
config SENSORS_VIA686A
tristate "VIA686A"
depends on PCI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 0ae26984ba4..b793dce6bed 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
+obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c
index 5f81ddf7150..4146105f1a5 100644
--- a/drivers/hwmon/f71882fg.c
+++ b/drivers/hwmon/f71882fg.c
@@ -1,6 +1,6 @@
/***************************************************************************
* Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
- * Copyright (C) 2007,2008 by Hans de Goede <hdegoede@redhat.com> *
+ * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> *
* *
* 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 *
@@ -32,6 +32,7 @@
#define DRVNAME "f71882fg"
+#define SIO_F71858FG_LD_HWM 0x02 /* Hardware monitor logical device */
#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */
#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */
@@ -44,6 +45,7 @@
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
+#define SIO_F71858_ID 0x0507 /* Chipset ID */
#define SIO_F71862_ID 0x0601 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */
#define SIO_F8000_ID 0x0581 /* Chipset ID */
@@ -70,6 +72,7 @@
#define F71882FG_REG_TEMP_HIGH(nr) (0x81 + 2 * (nr))
#define F71882FG_REG_TEMP_STATUS 0x62
#define F71882FG_REG_TEMP_BEEP 0x63
+#define F71882FG_REG_TEMP_CONFIG 0x69
#define F71882FG_REG_TEMP_HYST(nr) (0x6C + (nr))
#define F71882FG_REG_TEMP_TYPE 0x6B
#define F71882FG_REG_TEMP_DIODE_OPEN 0x6F
@@ -92,9 +95,10 @@ static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
-enum chips { f71862fg, f71882fg, f8000 };
+enum chips { f71858fg, f71862fg, f71882fg, f8000 };
static const char *f71882fg_names[] = {
+ "f71858fg",
"f71862fg",
"f71882fg",
"f8000",
@@ -119,6 +123,7 @@ struct f71882fg_data {
struct device *hwmon_dev;
struct mutex update_lock;
+ int temp_start; /* temp numbering start (0 or 1) */
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
unsigned long last_limits; /* In jiffies */
@@ -136,7 +141,7 @@ struct f71882fg_data {
/* Note: all models have only 3 temperature channels, but on some
they are addressed as 0-2 and on others as 1-3, so for coding
convenience we reserve space for 4 channels */
- u8 temp[4];
+ u16 temp[4];
u8 temp_ovt[4];
u8 temp_high[4];
u8 temp_hyst[2]; /* 2 hysts stored per reg */
@@ -144,6 +149,7 @@ struct f71882fg_data {
u8 temp_status;
u8 temp_beep;
u8 temp_diode_open;
+ u8 temp_config;
u8 pwm[4];
u8 pwm_enable;
u8 pwm_auto_point_hyst[2];
@@ -247,11 +253,55 @@ static struct platform_driver f71882fg_driver = {
.name = DRVNAME,
},
.probe = f71882fg_probe,
- .remove = __devexit_p(f71882fg_remove),
+ .remove = f71882fg_remove,
};
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+/* Temp and in attr for the f71858fg */
+static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
+ SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
+ SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
+ SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+ SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 0),
+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 0),
+ SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0),
+ SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 0),
+ SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 0),
+ SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
+ SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 1),
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 1),
+ SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 1),
+ SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 1),
+ SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+ SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 2),
+ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 2),
+ SENSOR_ATTR_2(temp3_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2),
+ SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 2),
+ SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 2),
+ SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+ SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
+};
+
/* Temp and in attr common to both the f71862fg and f71882fg */
static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
@@ -344,6 +394,7 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
store_temp_max, 0, 0),
SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
+ SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit,
store_temp_crit, 0, 1),
@@ -351,12 +402,14 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
store_temp_max, 0, 1),
SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit,
store_temp_crit, 0, 2),
SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
store_temp_max, 0, 2),
SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+ SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
};
/* Fan / PWM attr common to all models */
@@ -395,6 +448,9 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[] = {
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 1),
+ SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
+ SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+ store_pwm_enable, 0, 2),
SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR,
show_pwm_interpolate, store_pwm_interpolate, 0, 2),
SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
@@ -450,9 +506,6 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
- SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
- SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
- store_pwm_enable, 0, 2),
SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
1, 2),
@@ -473,22 +526,8 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
};
-/* Fan / PWM attr for the f71882fg */
-static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
- SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 0),
- SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 1),
- SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 2),
- SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
- SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
- show_fan_full_speed,
- store_fan_full_speed, 0, 3),
- SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 3),
- SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
-
+/* Fan / PWM attr common to both the f71882fg and f71858fg */
+static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 0),
@@ -565,9 +604,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
- SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
- SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
- store_pwm_enable, 0, 2),
SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 2),
@@ -605,6 +641,24 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 2),
SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+};
+
+/* Fan / PWM attr found on the f71882fg but not on the f71858fg */
+static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
+ SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ store_fan_beep, 0, 0),
+ SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ store_fan_beep, 0, 1),
+ SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ store_fan_beep, 0, 2),
+
+ SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+ SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 3),
+ SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ store_fan_beep, 0, 3),
+ SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
@@ -659,8 +713,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
static struct sensor_device_attribute_2 f8000_fan_attr[] = {
SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
- SENSOR_ATTR_2(pwm3, S_IRUGO, show_pwm, NULL, 0, 2),
-
SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 2),
@@ -857,13 +909,20 @@ static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val)
outb(val & 255, data->addr + DATA_REG_OFFSET);
}
+static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr)
+{
+ if (data->type == f71858fg)
+ return f71882fg_read16(data, F71882FG_REG_TEMP(nr));
+ else
+ return f71882fg_read8(data, F71882FG_REG_TEMP(nr));
+}
+
static struct f71882fg_data *f71882fg_update_device(struct device *dev)
{
struct f71882fg_data *data = dev_get_drvdata(dev);
int nr, reg = 0, reg2;
int nr_fans = (data->type == f71882fg) ? 4 : 3;
- int nr_ins = (data->type == f8000) ? 3 : 9;
- int temp_start = (data->type == f8000) ? 0 : 1;
+ int nr_ins = (data->type == f71858fg || data->type == f8000) ? 3 : 9;
mutex_lock(&data->update_lock);
@@ -878,7 +937,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
}
/* Get High & boundary temps*/
- for (nr = temp_start; nr < 3 + temp_start; nr++) {
+ for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) {
data->temp_ovt[nr] = f71882fg_read8(data,
F71882FG_REG_TEMP_OVT(nr));
data->temp_high[nr] = f71882fg_read8(data,
@@ -886,14 +945,17 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
}
if (data->type != f8000) {
- data->fan_beep = f71882fg_read8(data,
- F71882FG_REG_FAN_BEEP);
- data->temp_beep = f71882fg_read8(data,
- F71882FG_REG_TEMP_BEEP);
data->temp_hyst[0] = f71882fg_read8(data,
F71882FG_REG_TEMP_HYST(0));
data->temp_hyst[1] = f71882fg_read8(data,
F71882FG_REG_TEMP_HYST(1));
+ }
+
+ if (data->type == f71862fg || data->type == f71882fg) {
+ data->fan_beep = f71882fg_read8(data,
+ F71882FG_REG_FAN_BEEP);
+ data->temp_beep = f71882fg_read8(data,
+ F71882FG_REG_TEMP_BEEP);
/* Have to hardcode type, because temp1 is special */
reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
data->temp_type[2] = (reg & 0x04) ? 2 : 4;
@@ -904,10 +966,10 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
data->temp_type[1] = 6 /* PECI */;
else if ((reg2 & 0x03) == 0x02)
data->temp_type[1] = 5 /* AMDSI */;
- else if (data->type != f8000)
+ else if (data->type == f71862fg || data->type == f71882fg)
data->temp_type[1] = (reg & 0x02) ? 2 : 4;
else
- data->temp_type[1] = 2; /* F8000 only supports BJT */
+ data->temp_type[1] = 2; /* Only supports BJT */
data->pwm_enable = f71882fg_read8(data,
F71882FG_REG_PWM_ENABLE);
@@ -963,9 +1025,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
F71882FG_REG_TEMP_STATUS);
data->temp_diode_open = f71882fg_read8(data,
F71882FG_REG_TEMP_DIODE_OPEN);
- for (nr = temp_start; nr < 3 + temp_start; nr++)
- data->temp[nr] = f71882fg_read8(data,
- F71882FG_REG_TEMP(nr));
+ for (nr = data->temp_start; nr < 3 + data->temp_start; nr++)
+ data->temp[nr] = f71882fg_read_temp(data, nr);
data->fan_status = f71882fg_read8(data,
F71882FG_REG_FAN_STATUS);
@@ -1168,8 +1229,24 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
{
struct f71882fg_data *data = f71882fg_update_device(dev);
int nr = to_sensor_dev_attr_2(devattr)->index;
+ int sign, temp;
+
+ if (data->type == f71858fg) {
+ /* TEMP_TABLE_SEL 1 or 3 ? */
+ if (data->temp_config & 1) {
+ sign = data->temp[nr] & 0x0001;
+ temp = (data->temp[nr] >> 5) & 0x7ff;
+ } else {
+ sign = data->temp[nr] & 0x8000;
+ temp = (data->temp[nr] >> 5) & 0x3ff;
+ }
+ temp *= 125;
+ if (sign)
+ temp -= 128000;
+ } else
+ temp = data->temp[nr] * 1000;
- return sprintf(buf, "%d\n", data->temp[nr] * 1000);
+ return sprintf(buf, "%d\n", temp);
}
static ssize_t show_temp_max(struct device *dev, struct device_attribute
@@ -1440,6 +1517,10 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
int nr = to_sensor_dev_attr_2(devattr)->index;
long val = simple_strtol(buf, NULL, 10);
+ /* Special case for F8000 pwm channel 3 which only does auto mode */
+ if (data->type == f8000 && nr == 2 && val != 2)
+ return -EINVAL;
+
mutex_lock(&data->update_lock);
data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
/* Special case for F8000 auto PWM mode / Thermostat mode */
@@ -1458,6 +1539,12 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
} else {
switch (val) {
case 1:
+ /* The f71858fg does not support manual RPM mode */
+ if (data->type == f71858fg &&
+ ((data->pwm_enable >> (2 * nr)) & 1)) {
+ count = -EINVAL;
+ goto leave;
+ }
data->pwm_enable |= 2 << (2 * nr);
break; /* Manual */
case 2:
@@ -1616,9 +1703,9 @@ static ssize_t show_pwm_auto_point_channel(struct device *dev,
int result;
struct f71882fg_data *data = f71882fg_update_device(dev);
int nr = to_sensor_dev_attr_2(devattr)->index;
- int temp_start = (data->type == f8000) ? 0 : 1;
- result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - temp_start);
+ result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) -
+ data->temp_start);
return sprintf(buf, "%d\n", result);
}
@@ -1629,7 +1716,6 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
{
struct f71882fg_data *data = dev_get_drvdata(dev);
int nr = to_sensor_dev_attr_2(devattr)->index;
- int temp_start = (data->type == f8000) ? 0 : 1;
long val = simple_strtol(buf, NULL, 10);
switch (val) {
@@ -1645,7 +1731,7 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
default:
return -EINVAL;
}
- val += temp_start;
+ val += data->temp_start;
mutex_lock(&data->update_lock);
data->pwm_auto_point_mapping[nr] =
f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr));
@@ -1721,6 +1807,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
data->type = sio_data->type;
+ data->temp_start =
+ (data->type == f71858fg || data->type == f8000) ? 0 : 1;
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
@@ -1736,19 +1824,6 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
goto exit_free;
}
- data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
- /* If it is a 71862 and the fan / pwm part is enabled sanity check
- the pwm settings */
- if (data->type == f71862fg && (start_reg & 0x02)) {
- if ((data->pwm_enable & 0x15) != 0x15) {
- dev_err(&pdev->dev,
- "Invalid (reserved) pwm settings: 0x%02x\n",
- (unsigned int)data->pwm_enable);
- err = -ENODEV;
- goto exit_free;
- }
- }
-
/* Register sysfs interface files */
err = device_create_file(&pdev->dev, &dev_attr_name);
if (err)
@@ -1756,6 +1831,20 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
if (start_reg & 0x01) {
switch (data->type) {
+ case f71858fg:
+ data->temp_config =
+ f71882fg_read8(data, F71882FG_REG_TEMP_CONFIG);
+ if (data->temp_config & 0x10)
+ /* The f71858fg temperature alarms behave as
+ the f8000 alarms in this mode */
+ err = f71882fg_create_sysfs_files(pdev,
+ f8000_in_temp_attr,
+ ARRAY_SIZE(f8000_in_temp_attr));
+ else
+ err = f71882fg_create_sysfs_files(pdev,
+ f71858fg_in_temp_attr,
+ ARRAY_SIZE(f71858fg_in_temp_attr));
+ break;
case f71882fg:
err = f71882fg_create_sysfs_files(pdev,
f71882fg_in_temp_attr,
@@ -1779,6 +1868,35 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
}
if (start_reg & 0x02) {
+ data->pwm_enable =
+ f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
+
+ /* Sanity check the pwm settings */
+ switch (data->type) {
+ case f71858fg:
+ err = 0;
+ for (i = 0; i < nr_fans; i++)
+ if (((data->pwm_enable >> (i * 2)) & 3) == 3)
+ err = 1;
+ break;
+ case f71862fg:
+ err = (data->pwm_enable & 0x15) != 0x15;
+ break;
+ case f71882fg:
+ err = 0;
+ break;
+ case f8000:
+ err = data->pwm_enable & 0x20;
+ break;
+ }
+ if (err) {
+ dev_err(&pdev->dev,
+ "Invalid (reserved) pwm settings: 0x%02x\n",
+ (unsigned int)data->pwm_enable);
+ err = -ENODEV;
+ goto exit_unregister_sysfs;
+ }
+
err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr,
ARRAY_SIZE(fxxxx_fan_attr));
if (err)
@@ -1794,6 +1912,13 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
err = f71882fg_create_sysfs_files(pdev,
f71882fg_fan_attr,
ARRAY_SIZE(f71882fg_fan_attr));
+ if (err)
+ goto exit_unregister_sysfs;
+ /* fall through! */
+ case f71858fg:
+ err = f71882fg_create_sysfs_files(pdev,
+ f71882fg_f71858fg_fan_attr,
+ ARRAY_SIZE(f71882fg_f71858fg_fan_attr));
break;
case f8000:
err = f71882fg_create_sysfs_files(pdev,
@@ -1878,6 +2003,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
switch (devid) {
+ case SIO_F71858_ID:
+ sio_data->type = f71858fg;
+ break;
case SIO_F71862_ID:
sio_data->type = f71862fg;
break;
@@ -1892,7 +2020,11 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
goto exit;
}
- superio_select(sioaddr, SIO_F71882FG_LD_HWM);
+ if (sio_data->type == f71858fg)
+ superio_select(sioaddr, SIO_F71858FG_LD_HWM);
+ else
+ superio_select(sioaddr, SIO_F71882FG_LD_HWM);
+
if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
printk(KERN_WARNING DRVNAME ": Device not activated\n");
goto exit;
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index e15c3e7b07e..29ea6753f3b 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -18,6 +18,7 @@
#include <linux/hwmon.h>
#include <linux/gfp.h>
#include <linux/spinlock.h>
+#include <linux/pci.h>
#define HWMON_ID_PREFIX "hwmon"
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
@@ -86,8 +87,36 @@ void hwmon_device_unregister(struct device *dev)
"hwmon_device_unregister() failed: bad class ID!\n");
}
+static void __init hwmon_pci_quirks(void)
+{
+#if defined CONFIG_X86 && defined CONFIG_PCI
+ struct pci_dev *sb;
+ u16 base;
+ u8 enable;
+
+ /* Open access to 0x295-0x296 on MSI MS-7031 */
+ sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL);
+ if (sb &&
+ (sb->subsystem_vendor == 0x1462 && /* MSI */
+ sb->subsystem_device == 0x0031)) { /* MS-7031 */
+
+ pci_read_config_byte(sb, 0x48, &enable);
+ pci_read_config_word(sb, 0x64, &base);
+
+ if (base == 0 && !(enable & BIT(2))) {
+ dev_info(&sb->dev,
+ "Opening wide generic port at 0x295\n");
+ pci_write_config_word(sb, 0x64, 0x295);
+ pci_write_config_byte(sb, 0x48, enable | BIT(2));
+ }
+ }
+#endif
+}
+
static int __init hwmon_init(void)
{
+ hwmon_pci_quirks();
+
hwmon_class = class_create(THIS_MODULE, "hwmon");
if (IS_ERR(hwmon_class)) {
printk(KERN_ERR "hwmon.c: couldn't create sysfs class\n");
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index fe74609a7fe..405d3fb5d76 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -1127,3 +1127,4 @@ MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMBladeHC10-*");
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index f27af6a9da4..86142a85823 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -12,7 +12,7 @@
* also work with the MAX6651. It does not distinguish max6650 and max6651
* chips.
*
- * Tha datasheet was last seen at:
+ * The datasheet was last seen at:
*
* http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
*
@@ -98,6 +98,16 @@ I2C_CLIENT_INSMOD_1(max6650);
#define MAX6650_CFG_MODE_OPEN_LOOP 0x30
#define MAX6650_COUNT_MASK 0x03
+/*
+ * Alarm status register bits
+ */
+
+#define MAX6650_ALRM_MAX 0x01
+#define MAX6650_ALRM_MIN 0x02
+#define MAX6650_ALRM_TACH 0x04
+#define MAX6650_ALRM_GPIO1 0x08
+#define MAX6650_ALRM_GPIO2 0x10
+
/* Minimum and maximum values of the FAN-RPM */
#define FAN_RPM_MIN 240
#define FAN_RPM_MAX 30000
@@ -151,6 +161,7 @@ struct max6650_data
u8 tach[4];
u8 count;
u8 dac;
+ u8 alarm;
};
static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
@@ -418,6 +429,33 @@ static ssize_t set_div(struct device *dev, struct device_attribute *devattr,
return count;
}
+/*
+ * Get alarm stati:
+ * Possible values:
+ * 0 = no alarm
+ * 1 = alarm
+ */
+
+static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct max6650_data *data = max6650_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+ int alarm = 0;
+
+ if (data->alarm & attr->index) {
+ mutex_lock(&data->update_lock);
+ alarm = 1;
+ data->alarm &= ~attr->index;
+ data->alarm |= i2c_smbus_read_byte_data(client,
+ MAX6650_REG_ALARM);
+ mutex_unlock(&data->update_lock);
+ }
+
+ return sprintf(buf, "%d\n", alarm);
+}
+
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
@@ -426,7 +464,41 @@ static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target);
static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div);
static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable);
static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm);
+static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, get_alarm, NULL,
+ MAX6650_ALRM_MAX);
+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, get_alarm, NULL,
+ MAX6650_ALRM_MIN);
+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_alarm, NULL,
+ MAX6650_ALRM_TACH);
+static SENSOR_DEVICE_ATTR(gpio1_alarm, S_IRUGO, get_alarm, NULL,
+ MAX6650_ALRM_GPIO1);
+static SENSOR_DEVICE_ATTR(gpio2_alarm, S_IRUGO, get_alarm, NULL,
+ MAX6650_ALRM_GPIO2);
+
+static mode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
+ int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN);
+ struct device_attribute *devattr;
+ /*
+ * Hide the alarms that have not been enabled by the firmware
+ */
+
+ devattr = container_of(a, struct device_attribute, attr);
+ if (devattr == &sensor_dev_attr_fan1_max_alarm.dev_attr
+ || devattr == &sensor_dev_attr_fan1_min_alarm.dev_attr
+ || devattr == &sensor_dev_attr_fan1_fault.dev_attr
+ || devattr == &sensor_dev_attr_gpio1_alarm.dev_attr
+ || devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) {
+ if (!(alarm_en & to_sensor_dev_attr(devattr)->index))
+ return 0;
+ }
+
+ return a->mode;
+}
static struct attribute *max6650_attrs[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
@@ -437,11 +509,17 @@ static struct attribute *max6650_attrs[] = {
&dev_attr_fan1_div.attr,
&dev_attr_pwm1_enable.attr,
&dev_attr_pwm1.attr,
+ &sensor_dev_attr_fan1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_fault.dev_attr.attr,
+ &sensor_dev_attr_gpio1_alarm.dev_attr.attr,
+ &sensor_dev_attr_gpio2_alarm.dev_attr.attr,
NULL
};
static struct attribute_group max6650_attr_grp = {
.attrs = max6650_attrs,
+ .is_visible = max6650_attrs_visible,
};
/*
@@ -659,6 +737,12 @@ static struct max6650_data *max6650_update_device(struct device *dev)
MAX6650_REG_COUNT);
data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC);
+ /* Alarms are cleared on read in case the condition that
+ * caused the alarm is removed. Keep the value latched here
+ * for providing the register through different alarm files. */
+ data->alarm |= i2c_smbus_read_byte_data(client,
+ MAX6650_REG_ALARM);
+
data->last_updated = jiffies;
data->valid = 1;
}
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index 6cbdc2fea73..56cd6004da3 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -627,35 +627,35 @@ static struct platform_driver sht_drivers[] = {
.owner = THIS_MODULE,
},
.probe = sht15_probe,
- .remove = sht15_remove,
+ .remove = __devexit_p(sht15_remove),
}, {
.driver = {
.name = "sht11",
.owner = THIS_MODULE,
},
.probe = sht15_probe,
- .remove = sht15_remove,
+ .remove = __devexit_p(sht15_remove),
}, {
.driver = {
.name = "sht15",
.owner = THIS_MODULE,
},
.probe = sht15_probe,
- .remove = sht15_remove,
+ .remove = __devexit_p(sht15_remove),
}, {
.driver = {
.name = "sht71",
.owner = THIS_MODULE,
},
.probe = sht15_probe,
- .remove = sht15_remove,
+ .remove = __devexit_p(sht15_remove),
}, {
.driver = {
.name = "sht75",
.owner = THIS_MODULE,
},
.probe = sht15_probe,
- .remove = sht15_remove,
+ .remove = __devexit_p(sht15_remove),
},
};
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
new file mode 100644
index 00000000000..7b34f2cd08b
--- /dev/null
+++ b/drivers/hwmon/tmp401.c
@@ -0,0 +1,690 @@
+/* tmp401.c
+ *
+ * Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com>
+ * Preliminary tmp411 support by:
+ * Gabriel Konat, Sander Leget, Wouter Willems
+ * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.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.
+ */
+
+/*
+ * Driver for the Texas Instruments TMP401 SMBUS temperature sensor IC.
+ *
+ * Note this IC is in some aspect similar to the LM90, but it has quite a
+ * few differences too, for example the local temp has a higher resolution
+ * and thus has 16 bits registers for its value and limit instead of 8 bits.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_2(tmp401, tmp411);
+
+/*
+ * The TMP401 registers, note some registers have different addresses for
+ * reading and writing
+ */
+#define TMP401_STATUS 0x02
+#define TMP401_CONFIG_READ 0x03
+#define TMP401_CONFIG_WRITE 0x09
+#define TMP401_CONVERSION_RATE_READ 0x04
+#define TMP401_CONVERSION_RATE_WRITE 0x0A
+#define TMP401_TEMP_CRIT_HYST 0x21
+#define TMP401_CONSECUTIVE_ALERT 0x22
+#define TMP401_MANUFACTURER_ID_REG 0xFE
+#define TMP401_DEVICE_ID_REG 0xFF
+#define TMP411_N_FACTOR_REG 0x18
+
+static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 };
+static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 };
+static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 };
+static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2] = { 0x0C, 0x0E };
+static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 };
+static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2] = { 0x05, 0x07 };
+static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2] = { 0x0B, 0x0D };
+static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 };
+/* These are called the THERM limit / hysteresis / mask in the datasheet */
+static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 };
+
+static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 };
+static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 };
+static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 };
+static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 };
+
+/* Flags */
+#define TMP401_CONFIG_RANGE 0x04
+#define TMP401_CONFIG_SHUTDOWN 0x40
+#define TMP401_STATUS_LOCAL_CRIT 0x01
+#define TMP401_STATUS_REMOTE_CRIT 0x02
+#define TMP401_STATUS_REMOTE_OPEN 0x04
+#define TMP401_STATUS_REMOTE_LOW 0x08
+#define TMP401_STATUS_REMOTE_HIGH 0x10
+#define TMP401_STATUS_LOCAL_LOW 0x20
+#define TMP401_STATUS_LOCAL_HIGH 0x40
+
+/* Manufacturer / Device ID's */
+#define TMP401_MANUFACTURER_ID 0x55
+#define TMP401_DEVICE_ID 0x11
+#define TMP411_DEVICE_ID 0x12
+
+/*
+ * Functions declarations
+ */
+
+static int tmp401_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int tmp401_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info);
+static int tmp401_remove(struct i2c_client *client);
+static struct tmp401_data *tmp401_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id tmp401_id[] = {
+ { "tmp401", tmp401 },
+ { "tmp411", tmp411 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp401_id);
+
+static struct i2c_driver tmp401_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "tmp401",
+ },
+ .probe = tmp401_probe,
+ .remove = tmp401_remove,
+ .id_table = tmp401_id,
+ .detect = tmp401_detect,
+ .address_data = &addr_data,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct tmp401_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+ int kind;
+
+ /* register values */
+ u8 status;
+ u8 config;
+ u16 temp[2];
+ u16 temp_low[2];
+ u16 temp_high[2];
+ u8 temp_crit[2];
+ u8 temp_crit_hyst;
+ u16 temp_lowest[2];
+ u16 temp_highest[2];
+};
+
+/*
+ * Sysfs attr show / store functions
+ */
+
+static int tmp401_register_to_temp(u16 reg, u8 config)
+{
+ int temp = reg;
+
+ if (config & TMP401_CONFIG_RANGE)
+ temp -= 64 * 256;
+
+ return (temp * 625 + 80) / 160;
+}
+
+static u16 tmp401_temp_to_register(long temp, u8 config)
+{
+ if (config & TMP401_CONFIG_RANGE) {
+ temp = SENSORS_LIMIT(temp, -64000, 191000);
+ temp += 64000;
+ } else
+ temp = SENSORS_LIMIT(temp, 0, 127000);
+
+ return (temp * 160 + 312) / 625;
+}
+
+static int tmp401_crit_register_to_temp(u8 reg, u8 config)
+{
+ int temp = reg;
+
+ if (config & TMP401_CONFIG_RANGE)
+ temp -= 64;
+
+ return temp * 1000;
+}
+
+static u8 tmp401_crit_temp_to_register(long temp, u8 config)
+{
+ if (config & TMP401_CONFIG_RANGE) {
+ temp = SENSORS_LIMIT(temp, -64000, 191000);
+ temp += 64000;
+ } else
+ temp = SENSORS_LIMIT(temp, 0, 127000);
+
+ return (temp + 500) / 1000;
+}
+
+static ssize_t show_temp_value(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_register_to_temp(data->temp[index], data->config));
+}
+
+static ssize_t show_temp_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_register_to_temp(data->temp_low[index], data->config));
+}
+
+static ssize_t show_temp_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_register_to_temp(data->temp_high[index], data->config));
+}
+
+static ssize_t show_temp_crit(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_crit_register_to_temp(data->temp_crit[index],
+ data->config));
+}
+
+static ssize_t show_temp_crit_hyst(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int temp, index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ mutex_lock(&data->update_lock);
+ temp = tmp401_crit_register_to_temp(data->temp_crit[index],
+ data->config);
+ temp -= data->temp_crit_hyst * 1000;
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t show_temp_lowest(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_register_to_temp(data->temp_lowest[index],
+ data->config));
+}
+
+static ssize_t show_temp_highest(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ tmp401_register_to_temp(data->temp_highest[index],
+ data->config));
+}
+
+static ssize_t show_status(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int mask = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+
+ if (data->status & mask)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t store_temp_min(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+ long val;
+ u16 reg;
+
+ if (strict_strtol(buf, 10, &val))
+ return -EINVAL;
+
+ reg = tmp401_temp_to_register(val, data->config);
+
+ mutex_lock(&data->update_lock);
+
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8);
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF);
+
+ data->temp_low[index] = reg;
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t store_temp_max(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+ long val;
+ u16 reg;
+
+ if (strict_strtol(buf, 10, &val))
+ return -EINVAL;
+
+ reg = tmp401_temp_to_register(val, data->config);
+
+ mutex_lock(&data->update_lock);
+
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8);
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF);
+
+ data->temp_high[index] = reg;
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t store_temp_crit(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+ long val;
+ u8 reg;
+
+ if (strict_strtol(buf, 10, &val))
+ return -EINVAL;
+
+ reg = tmp401_crit_temp_to_register(val, data->config);
+
+ mutex_lock(&data->update_lock);
+
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ TMP401_TEMP_CRIT_LIMIT[index], reg);
+
+ data->temp_crit[index] = reg;
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ int temp, index = to_sensor_dev_attr(devattr)->index;
+ struct tmp401_data *data = tmp401_update_device(dev);
+ long val;
+ u8 reg;
+
+ if (strict_strtol(buf, 10, &val))
+ return -EINVAL;
+
+ if (data->config & TMP401_CONFIG_RANGE)
+ val = SENSORS_LIMIT(val, -64000, 191000);
+ else
+ val = SENSORS_LIMIT(val, 0, 127000);
+
+ mutex_lock(&data->update_lock);
+ temp = tmp401_crit_register_to_temp(data->temp_crit[index],
+ data->config);
+ val = SENSORS_LIMIT(val, temp - 255000, temp);
+ reg = ((temp - val) + 500) / 1000;
+
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ TMP401_TEMP_CRIT_HYST, reg);
+
+ data->temp_crit_hyst = reg;
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * Resets the historical measurements of minimum and maximum temperatures.
+ * This is done by writing any value to any of the minimum/maximum registers
+ * (0x30-0x37).
+ */
+static ssize_t reset_temp_history(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ long val;
+
+ if (strict_strtol(buf, 10, &val))
+ return -EINVAL;
+
+ if (val != 1) {
+ dev_err(dev, "temp_reset_history value %ld not"
+ " supported. Use 1 to reset the history!\n", val);
+ return -EINVAL;
+ }
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ TMP411_TEMP_LOWEST_MSB[0], val);
+
+ return count;
+}
+
+static struct sensor_device_attribute tmp401_attr[] = {
+ SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
+ SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0),
+ SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0),
+ SENSOR_ATTR(temp1_crit, 0644, show_temp_crit, store_temp_crit, 0),
+ SENSOR_ATTR(temp1_crit_hyst, 0644, show_temp_crit_hyst,
+ store_temp_crit_hyst, 0),
+ SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL,
+ TMP401_STATUS_LOCAL_LOW),
+ SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL,
+ TMP401_STATUS_LOCAL_HIGH),
+ SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL,
+ TMP401_STATUS_LOCAL_CRIT),
+ SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
+ SENSOR_ATTR(temp2_min, 0644, show_temp_min, store_temp_min, 1),
+ SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1),
+ SENSOR_ATTR(temp2_crit, 0644, show_temp_crit, store_temp_crit, 1),
+ SENSOR_ATTR(temp2_crit_hyst, 0444, show_temp_crit_hyst, NULL, 1),
+ SENSOR_ATTR(temp2_fault, 0444, show_status, NULL,
+ TMP401_STATUS_REMOTE_OPEN),
+ SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL,
+ TMP401_STATUS_REMOTE_LOW),
+ SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL,
+ TMP401_STATUS_REMOTE_HIGH),
+ SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL,
+ TMP401_STATUS_REMOTE_CRIT),
+};
+
+/*
+ * Additional features of the TMP411 chip.
+ * The TMP411 stores the minimum and maximum
+ * temperature measured since power-on, chip-reset, or
+ * minimum and maximum register reset for both the local
+ * and remote channels.
+ */
+static struct sensor_device_attribute tmp411_attr[] = {
+ SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0),
+ SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0),
+ SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1),
+ SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1),
+ SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0),
+};
+
+/*
+ * Begin non sysfs callback code (aka Real code)
+ */
+
+static void tmp401_init_client(struct i2c_client *client)
+{
+ int config, config_orig;
+
+ /* Set the conversion rate to 2 Hz */
+ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
+
+ /* Start conversions (disable shutdown if necessary) */
+ config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+ if (config < 0) {
+ dev_warn(&client->dev, "Initialization failed!\n");
+ return;
+ }
+
+ config_orig = config;
+ config &= ~TMP401_CONFIG_SHUTDOWN;
+
+ if (config != config_orig)
+ i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
+}
+
+static int tmp401_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Detect and identify the chip */
+ if (kind <= 0) {
+ u8 reg;
+
+ reg = i2c_smbus_read_byte_data(client,
+ TMP401_MANUFACTURER_ID_REG);
+ if (reg != TMP401_MANUFACTURER_ID)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
+
+ switch (reg) {
+ case TMP401_DEVICE_ID:
+ kind = tmp401;
+ break;
+ case TMP411_DEVICE_ID:
+ kind = tmp411;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+ if (reg & 0x1b)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client,
+ TMP401_CONVERSION_RATE_READ);
+ /* Datasheet says: 0x1-0x6 */
+ if (reg > 15)
+ return -ENODEV;
+ }
+ strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int tmp401_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i, err = 0;
+ struct tmp401_data *data;
+ const char *names[] = { "TMP401", "TMP411" };
+
+ data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ data->kind = id->driver_data;
+
+ /* Initialize the TMP401 chip */
+ tmp401_init_client(client);
+
+ /* Register sysfs hooks */
+ for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) {
+ err = device_create_file(&client->dev,
+ &tmp401_attr[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ /* Register aditional tmp411 sysfs hooks */
+ if (data->kind == tmp411) {
+ for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) {
+ err = device_create_file(&client->dev,
+ &tmp411_attr[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ data->hwmon_dev = NULL;
+ goto exit_remove;
+ }
+
+ dev_info(&client->dev, "Detected TI %s chip\n",
+ names[data->kind - 1]);
+
+ return 0;
+
+exit_remove:
+ tmp401_remove(client); /* will also free data for us */
+ return err;
+}
+
+static int tmp401_remove(struct i2c_client *client)
+{
+ struct tmp401_data *data = i2c_get_clientdata(client);
+ int i;
+
+ if (data->hwmon_dev)
+ hwmon_device_unregister(data->hwmon_dev);
+
+ for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
+ device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
+
+ if (data->kind == tmp411) {
+ for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
+ device_remove_file(&client->dev,
+ &tmp411_attr[i].dev_attr);
+ }
+
+ kfree(data);
+ return 0;
+}
+
+static struct tmp401_data *tmp401_update_device_reg16(
+ struct i2c_client *client, struct tmp401_data *data)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ /*
+ * High byte must be read first immediately followed
+ * by the low byte
+ */
+ data->temp[i] = i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_MSB[i]) << 8;
+ data->temp[i] |= i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_LSB[i]);
+ data->temp_low[i] = i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
+ data->temp_low[i] |= i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_LOW_LIMIT_LSB[i]);
+ data->temp_high[i] = i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
+ data->temp_high[i] |= i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_HIGH_LIMIT_LSB[i]);
+ data->temp_crit[i] = i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_CRIT_LIMIT[i]);
+
+ if (data->kind == tmp411) {
+ data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
+ TMP411_TEMP_LOWEST_MSB[i]) << 8;
+ data->temp_lowest[i] |= i2c_smbus_read_byte_data(
+ client, TMP411_TEMP_LOWEST_LSB[i]);
+
+ data->temp_highest[i] = i2c_smbus_read_byte_data(
+ client, TMP411_TEMP_HIGHEST_MSB[i]) << 8;
+ data->temp_highest[i] |= i2c_smbus_read_byte_data(
+ client, TMP411_TEMP_HIGHEST_LSB[i]);
+ }
+ }
+ return data;
+}
+
+static struct tmp401_data *tmp401_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tmp401_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
+ data->config = i2c_smbus_read_byte_data(client,
+ TMP401_CONFIG_READ);
+ tmp401_update_device_reg16(client, data);
+
+ data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
+ TMP401_TEMP_CRIT_HYST);
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static int __init tmp401_init(void)
+{
+ return i2c_add_driver(&tmp401_driver);
+}
+
+static void __exit tmp401_exit(void)
+{
+ i2c_del_driver(&tmp401_driver);
+}
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Texas Instruments TMP401 temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(tmp401_init);
+module_exit(tmp401_exit);
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index e64b42058b2..0e9746913d2 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -36,6 +36,7 @@
w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
0x8860 0xa1
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
+ w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
*/
@@ -53,12 +54,13 @@
#include <asm/io.h>
#include "lm75.h"
-enum kinds { w83627ehf, w83627dhg, w83667hg };
+enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg };
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * w83627ehf_device_names[] = {
"w83627ehf",
"w83627dhg",
+ "w83627dhg",
"w83667hg",
};
@@ -86,6 +88,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
#define SIO_W83627EHF_ID 0x8850
#define SIO_W83627EHG_ID 0x8860
#define SIO_W83627DHG_ID 0xa020
+#define SIO_W83627DHG_P_ID 0xb070
#define SIO_W83667HG_ID 0xa510
#define SIO_ID_MASK 0xFFF0
@@ -1517,6 +1520,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
static const char __initdata sio_name_W83627EHF[] = "W83627EHF";
static const char __initdata sio_name_W83627EHG[] = "W83627EHG";
static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
+ static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
static const char __initdata sio_name_W83667HG[] = "W83667HG";
u16 val;
@@ -1542,6 +1546,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83627dhg;
sio_name = sio_name_W83627DHG;
break;
+ case SIO_W83627DHG_P_ID:
+ sio_data->kind = w83627dhg_p;
+ sio_name = sio_name_W83627DHG_P;
+ break;
case SIO_W83667HG_ID:
sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG;
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c8460fa9cfa..0d04d3ebfc2 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -211,7 +211,7 @@ config I2C_VIA
will be called i2c-via.
config I2C_VIAPRO
- tristate "VIA VT82C596/82C686/82xx and CX700/VX800/VX820"
+ tristate "VIA VT82C596/82C686/82xx and CX700/VX8xx"
depends on PCI
help
If you say yes to this option, support will be included for the VIA
@@ -225,8 +225,8 @@ config I2C_VIAPRO
VT8237R/A/S
VT8251
CX700
- VX800
- VX820
+ VX800/VX820
+ VX855/VX875
This driver can also be built as a module. If so, the module
will be called i2c-viapro.
diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c
index 02e6f724b05..54d810a4d00 100644
--- a/drivers/i2c/busses/i2c-viapro.c
+++ b/drivers/i2c/busses/i2c-viapro.c
@@ -37,6 +37,7 @@
VT8251 0x3287 yes
CX700 0x8324 yes
VX800/VX820 0x8353 yes
+ VX855/VX875 0x8409 yes
Note: we assume there can only be one device, with one SMBus interface.
*/
@@ -404,6 +405,7 @@ found:
switch (pdev->device) {
case PCI_DEVICE_ID_VIA_CX700:
case PCI_DEVICE_ID_VIA_VX800:
+ case PCI_DEVICE_ID_VIA_VX855:
case PCI_DEVICE_ID_VIA_8251:
case PCI_DEVICE_ID_VIA_8237:
case PCI_DEVICE_ID_VIA_8237A:
@@ -469,6 +471,8 @@ static struct pci_device_id vt596_ids[] = {
.driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800),
.driver_data = SMBBA3 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855),
+ .driver_data = SMBBA3 },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c
index 1a474acc0dd..7663d57833a 100644
--- a/drivers/i2c/busses/i2c-voodoo3.c
+++ b/drivers/i2c/busses/i2c-voodoo3.c
@@ -163,7 +163,6 @@ static struct i2c_algo_bit_data voo_i2c_bit_data = {
static struct i2c_adapter voodoo3_i2c_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_TV_ANALOG,
.name = "I2C Voodoo3/Banshee adapter",
.algo_data = &voo_i2c_bit_data,
};
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index 8f8c81eb0ae..02d746c9c47 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -64,21 +64,6 @@ config SENSORS_PCA9539
This driver is deprecated and will be dropped soon. Use
drivers/gpio/pca953x.c instead.
-config SENSORS_MAX6875
- tristate "Maxim MAX6875 Power supply supervisor"
- depends on EXPERIMENTAL
- help
- If you say yes here you get support for the Maxim MAX6875
- EEPROM-programmable, quad power-supply sequencer/supervisor.
-
- This provides an interface to program the EEPROM and reset the chip.
-
- This driver also supports the Maxim MAX6874 hex power-supply
- sequencer/supervisor if found at a compatible address.
-
- This driver can also be built as a module. If so, the module
- will be called max6875.
-
config SENSORS_TSL2550
tristate "Taos TSL2550 ambient light sensor"
depends on EXPERIMENTAL
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index 55a37603718..f4680d16ee3 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -11,7 +11,6 @@
#
obj-$(CONFIG_DS1682) += ds1682.o
-obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_PCF8575) += pcf8575.o
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 85e2e919d1c..5ed622ee65c 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -29,7 +29,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/idr.h>
-#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/hardirq.h>
@@ -451,16 +450,6 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
mutex_lock(&core_lock);
- /* Add the adapter to the driver core.
- * If the parent pointer is not set up,
- * we add this adapter to the host bus.
- */
- if (adap->dev.parent == NULL) {
- adap->dev.parent = &platform_bus;
- pr_debug("I2C adapter driver [%s] forgot to specify "
- "physical device\n", adap->name);
- }
-
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
@@ -1022,7 +1011,8 @@ module_exit(i2c_exit);
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
- int ret;
+ unsigned long orig_jiffies;
+ int ret, try;
/* REVISIT the fault reporting model here is weak:
*
@@ -1060,7 +1050,15 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
mutex_lock_nested(&adap->bus_lock, adap->level);
}
- ret = adap->algo->master_xfer(adap,msgs,num);
+ /* Retry automatically on arbitration loss */
+ orig_jiffies = jiffies;
+ for (ret = 0, try = 0; try <= adap->retries; try++) {
+ ret = adap->algo->master_xfer(adap, msgs, num);
+ if (ret != -EAGAIN)
+ break;
+ if (time_after(jiffies, orig_jiffies + adap->timeout))
+ break;
+ }
mutex_unlock(&adap->bus_lock);
return ret;
@@ -1509,7 +1507,7 @@ struct i2c_adapter* i2c_get_adapter(int id)
struct i2c_adapter *adapter;
mutex_lock(&core_lock);
- adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id);
+ adapter = idr_find(&i2c_adapter_idr, id);
if (adapter && !try_module_get(adapter->owner))
adapter = NULL;
@@ -1995,14 +1993,27 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
{
+ unsigned long orig_jiffies;
+ int try;
s32 res;
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
if (adapter->algo->smbus_xfer) {
mutex_lock(&adapter->bus_lock);
- res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,
- command, protocol, data);
+
+ /* Retry automatically on arbitration loss */
+ orig_jiffies = jiffies;
+ for (res = 0, try = 0; try <= adapter->retries; try++) {
+ res = adapter->algo->smbus_xfer(adapter, addr, flags,
+ read_write, command,
+ protocol, data);
+ if (res != -EAGAIN)
+ break;
+ if (time_after(jiffies,
+ orig_jiffies + adapter->timeout))
+ break;
+ }
mutex_unlock(&adapter->bus_lock);
} else
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index 0ddf9044948..fde377c60cc 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -72,7 +72,7 @@ MODULE_PARM_DESC(verbose,"Verbose log operations "
"(default 0)");
struct thermostat {
- struct i2c_client clt;
+ struct i2c_client *clt;
u8 temps[3];
u8 cached_temp[3];
u8 initial_limits[3];
@@ -87,9 +87,6 @@ static struct of_device * of_dev;
static struct thermostat* thermostat;
static struct task_struct *thread_therm = NULL;
-static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
- int busno);
-
static void write_both_fan_speed(struct thermostat *th, int speed);
static void write_fan_speed(struct thermostat *th, int speed, int fan);
@@ -101,7 +98,7 @@ write_reg(struct thermostat* th, int reg, u8 data)
tmp[0] = reg;
tmp[1] = data;
- rc = i2c_master_send(&th->clt, (const char *)tmp, 2);
+ rc = i2c_master_send(th->clt, (const char *)tmp, 2);
if (rc < 0)
return rc;
if (rc != 2)
@@ -116,12 +113,12 @@ read_reg(struct thermostat* th, int reg)
int rc;
reg_addr = (u8)reg;
- rc = i2c_master_send(&th->clt, &reg_addr, 1);
+ rc = i2c_master_send(th->clt, &reg_addr, 1);
if (rc < 0)
return rc;
if (rc != 1)
return -ENODEV;
- rc = i2c_master_recv(&th->clt, (char *)&data, 1);
+ rc = i2c_master_recv(th->clt, (char *)&data, 1);
if (rc < 0)
return rc;
return data;
@@ -131,26 +128,36 @@ static int
attach_thermostat(struct i2c_adapter *adapter)
{
unsigned long bus_no;
+ struct i2c_board_info info;
+ struct i2c_client *client;
if (strncmp(adapter->name, "uni-n", 5))
return -ENODEV;
bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
if (bus_no != therm_bus)
return -ENODEV;
- return attach_one_thermostat(adapter, therm_address, bus_no);
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE);
+ info.addr = therm_address;
+ client = i2c_new_device(adapter, &info);
+ if (!client)
+ return -ENODEV;
+
+ /*
+ * Let i2c-core delete that device on driver removal.
+ * This is safe because i2c-core holds the core_lock mutex for us.
+ */
+ list_add_tail(&client->detected, &client->driver->clients);
+ return 0;
}
static int
-detach_thermostat(struct i2c_adapter *adapter)
+remove_thermostat(struct i2c_client *client)
{
- struct thermostat* th;
+ struct thermostat *th = i2c_get_clientdata(client);
int i;
- if (thermostat == NULL)
- return 0;
-
- th = thermostat;
-
if (thread_therm != NULL) {
kthread_stop(thread_therm);
}
@@ -166,8 +173,6 @@ detach_thermostat(struct i2c_adapter *adapter)
write_both_fan_speed(th, -1);
- i2c_detach_client(&th->clt);
-
thermostat = NULL;
kfree(th);
@@ -175,14 +180,6 @@ detach_thermostat(struct i2c_adapter *adapter)
return 0;
}
-static struct i2c_driver thermostat_driver = {
- .driver = {
- .name = "therm_adt746x",
- },
- .attach_adapter = attach_thermostat,
- .detach_adapter = detach_thermostat,
-};
-
static int read_fan_speed(struct thermostat *th, u8 addr)
{
u8 tmp[2];
@@ -371,8 +368,8 @@ static void set_limit(struct thermostat *th, int i)
th->limits[i] = default_limits_local[i] + limit_adjust;
}
-static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
- int busno)
+static int probe_thermostat(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct thermostat* th;
int rc;
@@ -385,16 +382,12 @@ static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
if (!th)
return -ENOMEM;
- th->clt.addr = addr;
- th->clt.adapter = adapter;
- th->clt.driver = &thermostat_driver;
- strcpy(th->clt.name, "thermostat");
+ i2c_set_clientdata(client, th);
+ th->clt = client;
rc = read_reg(th, 0);
if (rc < 0) {
- printk(KERN_ERR "adt746x: Thermostat failed to read config "
- "from bus %d !\n",
- busno);
+ dev_err(&client->dev, "Thermostat failed to read config!\n");
kfree(th);
return -ENODEV;
}
@@ -423,14 +416,6 @@ static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
thermostat = th;
- if (i2c_attach_client(&th->clt)) {
- printk(KERN_INFO "adt746x: Thermostat failed to attach "
- "client !\n");
- thermostat = NULL;
- kfree(th);
- return -ENODEV;
- }
-
/* be sure to really write fan speed the first time */
th->last_speed[0] = -2;
th->last_speed[1] = -2;
@@ -456,6 +441,21 @@ static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
return 0;
}
+static const struct i2c_device_id therm_adt746x_id[] = {
+ { "therm_adt746x", 0 },
+ { }
+};
+
+static struct i2c_driver thermostat_driver = {
+ .driver = {
+ .name = "therm_adt746x",
+ },
+ .attach_adapter = attach_thermostat,
+ .probe = probe_thermostat,
+ .remove = remove_thermostat,
+ .id_table = therm_adt746x_id,
+};
+
/*
* Now, unfortunately, sysfs doesn't give us a nice void * we could
* pass around to the attribute functions, so we don't really have
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index 817607e2af6..a028598af2d 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -287,22 +287,6 @@ struct fcu_fan_table fcu_fans[] = {
};
/*
- * i2c_driver structure to attach to the host i2c controller
- */
-
-static int therm_pm72_attach(struct i2c_adapter *adapter);
-static int therm_pm72_detach(struct i2c_adapter *adapter);
-
-static struct i2c_driver therm_pm72_driver =
-{
- .driver = {
- .name = "therm_pm72",
- },
- .attach_adapter = therm_pm72_attach,
- .detach_adapter = therm_pm72_detach,
-};
-
-/*
* Utility function to create an i2c_client structure and
* attach it to one of u3 adapters
*/
@@ -310,6 +294,7 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name)
{
struct i2c_client *clt;
struct i2c_adapter *adap;
+ struct i2c_board_info info;
if (id & 0x200)
adap = k2;
@@ -320,31 +305,21 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name)
if (adap == NULL)
return NULL;
- clt = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (clt == NULL)
- return NULL;
-
- clt->addr = (id >> 1) & 0x7f;
- clt->adapter = adap;
- clt->driver = &therm_pm72_driver;
- strncpy(clt->name, name, I2C_NAME_SIZE-1);
-
- if (i2c_attach_client(clt)) {
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = (id >> 1) & 0x7f;
+ strlcpy(info.type, "therm_pm72", I2C_NAME_SIZE);
+ clt = i2c_new_device(adap, &info);
+ if (!clt) {
printk(KERN_ERR "therm_pm72: Failed to attach to i2c ID 0x%x\n", id);
- kfree(clt);
return NULL;
}
- return clt;
-}
-/*
- * Utility function to get rid of the i2c_client structure
- * (will also detach from the adapter hopepfully)
- */
-static void detach_i2c_chip(struct i2c_client *clt)
-{
- i2c_detach_client(clt);
- kfree(clt);
+ /*
+ * Let i2c-core delete that device on driver removal.
+ * This is safe because i2c-core holds the core_lock mutex for us.
+ */
+ list_add_tail(&clt->detected, &clt->driver->clients);
+ return clt;
}
/*
@@ -1203,8 +1178,6 @@ static int init_cpu_state(struct cpu_pid_state *state, int index)
return 0;
fail:
- if (state->monitor)
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
return -ENODEV;
@@ -1232,7 +1205,6 @@ static void dispose_cpu_state(struct cpu_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
}
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1407,7 +1379,6 @@ static void dispose_backside_state(struct backside_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_backside_temperature);
device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1532,7 +1503,6 @@ static void dispose_drives_state(struct drives_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_drives_temperature);
device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1654,7 +1624,6 @@ static void dispose_dimms_state(struct dimm_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -1779,7 +1748,6 @@ static void dispose_slots_state(struct slots_pid_state *state)
device_remove_file(&of_dev->dev, &dev_attr_slots_temperature);
device_remove_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
@@ -2008,8 +1976,6 @@ static int attach_fcu(void)
*/
static void detach_fcu(void)
{
- if (fcu)
- detach_i2c_chip(fcu);
fcu = NULL;
}
@@ -2060,12 +2026,21 @@ static int therm_pm72_attach(struct i2c_adapter *adapter)
return 0;
}
+static int therm_pm72_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ /* Always succeed, the real work was done in therm_pm72_attach() */
+ return 0;
+}
+
/*
- * Called on every adapter when the driver or the i2c controller
+ * Called when any of the devices which participates into thermal management
* is going away.
*/
-static int therm_pm72_detach(struct i2c_adapter *adapter)
+static int therm_pm72_remove(struct i2c_client *client)
{
+ struct i2c_adapter *adapter = client->adapter;
+
mutex_lock(&driver_lock);
if (state != state_detached)
@@ -2096,6 +2071,30 @@ static int therm_pm72_detach(struct i2c_adapter *adapter)
return 0;
}
+/*
+ * i2c_driver structure to attach to the host i2c controller
+ */
+
+static const struct i2c_device_id therm_pm72_id[] = {
+ /*
+ * Fake device name, thermal management is done by several
+ * chips but we don't need to differentiate between them at
+ * this point.
+ */
+ { "therm_pm72", 0 },
+ { }
+};
+
+static struct i2c_driver therm_pm72_driver = {
+ .driver = {
+ .name = "therm_pm72",
+ },
+ .attach_adapter = therm_pm72_attach,
+ .probe = therm_pm72_probe,
+ .remove = therm_pm72_remove,
+ .id_table = therm_pm72_id,
+};
+
static int fan_check_loc_match(const char *loc, int fan)
{
char tmp[64];
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 3da0a02efd7..40023313a76 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -48,16 +48,6 @@
#define LOG_TEMP 0 /* continously log temperature */
-static int do_probe( struct i2c_adapter *adapter, int addr, int kind);
-
-/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
-static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
- 0x4c, 0x4d, 0x4e, 0x4f,
- 0x2c, 0x2d, 0x2e, 0x2f,
- I2C_CLIENT_END };
-
-I2C_CLIENT_INSMOD;
-
static struct {
volatile int running;
struct task_struct *poll_task;
@@ -315,53 +305,54 @@ static int control_loop(void *dummy)
static int
do_attach( struct i2c_adapter *adapter )
{
- int ret = 0;
+ /* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
+ static const unsigned short scan_ds1775[] = {
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ I2C_CLIENT_END
+ };
+ static const unsigned short scan_adm1030[] = {
+ 0x2c, 0x2d, 0x2e, 0x2f,
+ I2C_CLIENT_END
+ };
if( strncmp(adapter->name, "uni-n", 5) )
return 0;
if( !x.running ) {
- ret = i2c_probe( adapter, &addr_data, &do_probe );
+ struct i2c_board_info info;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "therm_ds1775", I2C_NAME_SIZE);
+ i2c_new_probed_device(adapter, &info, scan_ds1775);
+
+ strlcpy(info.type, "therm_adm1030", I2C_NAME_SIZE);
+ i2c_new_probed_device(adapter, &info, scan_adm1030);
+
if( x.thermostat && x.fan ) {
x.running = 1;
x.poll_task = kthread_run(control_loop, NULL, "g4fand");
}
}
- return ret;
+ return 0;
}
static int
-do_detach( struct i2c_client *client )
+do_remove(struct i2c_client *client)
{
- int err;
-
- if( (err=i2c_detach_client(client)) )
- printk(KERN_ERR "failed to detach thermostat client\n");
- else {
- if( x.running ) {
- x.running = 0;
- kthread_stop(x.poll_task);
- x.poll_task = NULL;
- }
- if( client == x.thermostat )
- x.thermostat = NULL;
- else if( client == x.fan )
- x.fan = NULL;
- else {
- printk(KERN_ERR "g4fan: bad client\n");
- }
- kfree( client );
+ if (x.running) {
+ x.running = 0;
+ kthread_stop(x.poll_task);
+ x.poll_task = NULL;
}
- return err;
-}
+ if (client == x.thermostat)
+ x.thermostat = NULL;
+ else if (client == x.fan)
+ x.fan = NULL;
+ else
+ printk(KERN_ERR "g4fan: bad client\n");
-static struct i2c_driver g4fan_driver = {
- .driver = {
- .name = "therm_windtunnel",
- },
- .attach_adapter = do_attach,
- .detach_client = do_detach,
-};
+ return 0;
+}
static int
attach_fan( struct i2c_client *cl )
@@ -374,13 +365,8 @@ attach_fan( struct i2c_client *cl )
goto out;
printk("ADM1030 fan controller [@%02x]\n", cl->addr );
- strlcpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) );
-
- if( !i2c_attach_client(cl) )
- x.fan = cl;
+ x.fan = cl;
out:
- if( cl != x.fan )
- kfree( cl );
return 0;
}
@@ -412,39 +398,47 @@ attach_thermostat( struct i2c_client *cl )
x.temp = temp;
x.overheat_temp = os_temp;
x.overheat_hyst = hyst_temp;
-
- strlcpy( cl->name, "DS1775 thermostat", sizeof(cl->name) );
-
- if( !i2c_attach_client(cl) )
- x.thermostat = cl;
+ x.thermostat = cl;
out:
- if( cl != x.thermostat )
- kfree( cl );
return 0;
}
+enum chip { ds1775, adm1030 };
+
+static const struct i2c_device_id therm_windtunnel_id[] = {
+ { "therm_ds1775", ds1775 },
+ { "therm_adm1030", adm1030 },
+ { }
+};
+
static int
-do_probe( struct i2c_adapter *adapter, int addr, int kind )
+do_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
- struct i2c_client *cl;
+ struct i2c_adapter *adapter = cl->adapter;
if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE) )
return 0;
- if( !(cl=kzalloc(sizeof(*cl), GFP_KERNEL)) )
- return -ENOMEM;
-
- cl->addr = addr;
- cl->adapter = adapter;
- cl->driver = &g4fan_driver;
- cl->flags = 0;
-
- if( addr < 0x48 )
+ switch (id->driver_data) {
+ case adm1030:
return attach_fan( cl );
- return attach_thermostat( cl );
+ case ds1775:
+ return attach_thermostat(cl);
+ }
+ return 0;
}
+static struct i2c_driver g4fan_driver = {
+ .driver = {
+ .name = "therm_windtunnel",
+ },
+ .attach_adapter = do_attach,
+ .probe = do_probe,
+ .remove = do_remove,
+ .id_table = therm_windtunnel_id,
+};
+
/************************************************************************/
/* initialization / cleanup */
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index b92b959fe16..529886c7a82 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -37,34 +37,22 @@
struct wf_lm75_sensor {
int ds1775 : 1;
int inited : 1;
- struct i2c_client i2c;
+ struct i2c_client *i2c;
struct wf_sensor sens;
};
#define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens)
-#define i2c_to_lm75(c) container_of(c, struct wf_lm75_sensor, i2c)
-
-static int wf_lm75_attach(struct i2c_adapter *adapter);
-static int wf_lm75_detach(struct i2c_client *client);
-
-static struct i2c_driver wf_lm75_driver = {
- .driver = {
- .name = "wf_lm75",
- },
- .attach_adapter = wf_lm75_attach,
- .detach_client = wf_lm75_detach,
-};
static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
{
struct wf_lm75_sensor *lm = wf_to_lm75(sr);
s32 data;
- if (lm->i2c.adapter == NULL)
+ if (lm->i2c == NULL)
return -ENODEV;
/* Init chip if necessary */
if (!lm->inited) {
- u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(&lm->i2c, 1);
+ u8 cfg_new, cfg = (u8)i2c_smbus_read_byte_data(lm->i2c, 1);
DBG("wf_lm75: Initializing %s, cfg was: %02x\n",
sr->name, cfg);
@@ -73,7 +61,7 @@ static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
* the firmware for now
*/
cfg_new = cfg & ~0x01;
- i2c_smbus_write_byte_data(&lm->i2c, 1, cfg_new);
+ i2c_smbus_write_byte_data(lm->i2c, 1, cfg_new);
lm->inited = 1;
/* If we just powered it up, let's wait 200 ms */
@@ -81,7 +69,7 @@ static int wf_lm75_get(struct wf_sensor *sr, s32 *value)
}
/* Read temperature register */
- data = (s32)le16_to_cpu(i2c_smbus_read_word_data(&lm->i2c, 0));
+ data = (s32)le16_to_cpu(i2c_smbus_read_word_data(lm->i2c, 0));
data <<= 8;
*value = data;
@@ -92,12 +80,6 @@ static void wf_lm75_release(struct wf_sensor *sr)
{
struct wf_lm75_sensor *lm = wf_to_lm75(sr);
- /* check if client is registered and detach from i2c */
- if (lm->i2c.adapter) {
- i2c_detach_client(&lm->i2c);
- lm->i2c.adapter = NULL;
- }
-
kfree(lm);
}
@@ -107,59 +89,77 @@ static struct wf_sensor_ops wf_lm75_ops = {
.owner = THIS_MODULE,
};
-static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
- u8 addr, int ds1775,
- const char *loc)
+static int wf_lm75_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct wf_lm75_sensor *lm;
int rc;
- DBG("wf_lm75: creating %s device at address 0x%02x\n",
- ds1775 ? "ds1775" : "lm75", addr);
-
lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL);
if (lm == NULL)
- return NULL;
+ return -ENODEV;
+
+ lm->inited = 0;
+ lm->ds1775 = id->driver_data;
+ lm->i2c = client;
+ lm->sens.name = client->dev.platform_data;
+ lm->sens.ops = &wf_lm75_ops;
+ i2c_set_clientdata(client, lm);
+
+ rc = wf_register_sensor(&lm->sens);
+ if (rc) {
+ i2c_set_clientdata(client, NULL);
+ kfree(lm);
+ }
+
+ return rc;
+}
+
+static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter,
+ u8 addr, int ds1775,
+ const char *loc)
+{
+ struct i2c_board_info info;
+ struct i2c_client *client;
+ char *name;
+
+ DBG("wf_lm75: creating %s device at address 0x%02x\n",
+ ds1775 ? "ds1775" : "lm75", addr);
/* Usual rant about sensor names not beeing very consistent in
* the device-tree, oh well ...
* Add more entries below as you deal with more setups
*/
if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
- lm->sens.name = "hd-temp";
+ name = "hd-temp";
else if (!strcmp(loc, "Incoming Air Temp"))
- lm->sens.name = "incoming-air-temp";
+ name = "incoming-air-temp";
else if (!strcmp(loc, "ODD Temp"))
- lm->sens.name = "optical-drive-temp";
+ name = "optical-drive-temp";
else if (!strcmp(loc, "HD Temp"))
- lm->sens.name = "hard-drive-temp";
+ name = "hard-drive-temp";
else
goto fail;
- lm->inited = 0;
- lm->sens.ops = &wf_lm75_ops;
- lm->ds1775 = ds1775;
- lm->i2c.addr = (addr >> 1) & 0x7f;
- lm->i2c.adapter = adapter;
- lm->i2c.driver = &wf_lm75_driver;
- strncpy(lm->i2c.name, lm->sens.name, I2C_NAME_SIZE-1);
-
- rc = i2c_attach_client(&lm->i2c);
- if (rc) {
- printk(KERN_ERR "windfarm: failed to attach %s %s to i2c,"
- " err %d\n", ds1775 ? "ds1775" : "lm75",
- lm->i2c.name, rc);
- goto fail;
- }
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = (addr >> 1) & 0x7f;
+ info.platform_data = name;
+ strlcpy(info.type, ds1775 ? "wf_ds1775" : "wf_lm75", I2C_NAME_SIZE);
- if (wf_register_sensor(&lm->sens)) {
- i2c_detach_client(&lm->i2c);
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL) {
+ printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n",
+ ds1775 ? "ds1775" : "lm75", name);
goto fail;
}
- return lm;
+ /*
+ * Let i2c-core delete that device on driver removal.
+ * This is safe because i2c-core holds the core_lock mutex for us.
+ */
+ list_add_tail(&client->detected, &client->driver->clients);
+ return client;
fail:
- kfree(lm);
return NULL;
}
@@ -202,21 +202,38 @@ static int wf_lm75_attach(struct i2c_adapter *adapter)
return 0;
}
-static int wf_lm75_detach(struct i2c_client *client)
+static int wf_lm75_remove(struct i2c_client *client)
{
- struct wf_lm75_sensor *lm = i2c_to_lm75(client);
+ struct wf_lm75_sensor *lm = i2c_get_clientdata(client);
DBG("wf_lm75: i2c detatch called for %s\n", lm->sens.name);
/* Mark client detached */
- lm->i2c.adapter = NULL;
+ lm->i2c = NULL;
/* release sensor */
wf_unregister_sensor(&lm->sens);
+ i2c_set_clientdata(client, NULL);
return 0;
}
+static const struct i2c_device_id wf_lm75_id[] = {
+ { "wf_lm75", 0 },
+ { "wf_ds1775", 1 },
+ { }
+};
+
+static struct i2c_driver wf_lm75_driver = {
+ .driver = {
+ .name = "wf_lm75",
+ },
+ .attach_adapter = wf_lm75_attach,
+ .probe = wf_lm75_probe,
+ .remove = wf_lm75_remove,
+ .id_table = wf_lm75_id,
+};
+
static int __init wf_lm75_sensor_init(void)
{
/* Don't register on old machines that use therm_pm72 for now */
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index e207a90d6b2..e2a55ecda2b 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -26,34 +26,22 @@
#define MAX6690_EXTERNAL_TEMP 1
struct wf_6690_sensor {
- struct i2c_client i2c;
+ struct i2c_client *i2c;
struct wf_sensor sens;
};
#define wf_to_6690(x) container_of((x), struct wf_6690_sensor, sens)
-#define i2c_to_6690(x) container_of((x), struct wf_6690_sensor, i2c)
-
-static int wf_max6690_attach(struct i2c_adapter *adapter);
-static int wf_max6690_detach(struct i2c_client *client);
-
-static struct i2c_driver wf_max6690_driver = {
- .driver = {
- .name = "wf_max6690",
- },
- .attach_adapter = wf_max6690_attach,
- .detach_client = wf_max6690_detach,
-};
static int wf_max6690_get(struct wf_sensor *sr, s32 *value)
{
struct wf_6690_sensor *max = wf_to_6690(sr);
s32 data;
- if (max->i2c.adapter == NULL)
+ if (max->i2c == NULL)
return -ENODEV;
/* chip gets initialized by firmware */
- data = i2c_smbus_read_byte_data(&max->i2c, MAX6690_EXTERNAL_TEMP);
+ data = i2c_smbus_read_byte_data(max->i2c, MAX6690_EXTERNAL_TEMP);
if (data < 0)
return data;
*value = data << 16;
@@ -64,10 +52,6 @@ static void wf_max6690_release(struct wf_sensor *sr)
{
struct wf_6690_sensor *max = wf_to_6690(sr);
- if (max->i2c.adapter) {
- i2c_detach_client(&max->i2c);
- max->i2c.adapter = NULL;
- }
kfree(max);
}
@@ -77,19 +61,40 @@ static struct wf_sensor_ops wf_max6690_ops = {
.owner = THIS_MODULE,
};
-static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
- const char *loc)
+static int wf_max6690_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct wf_6690_sensor *max;
- char *name;
+ int rc;
max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
if (max == NULL) {
- printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: "
- "no memory\n", loc);
- return;
+ printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: "
+ "no memory\n");
+ return -ENOMEM;
+ }
+
+ max->i2c = client;
+ max->sens.name = client->dev.platform_data;
+ max->sens.ops = &wf_max6690_ops;
+ i2c_set_clientdata(client, max);
+
+ rc = wf_register_sensor(&max->sens);
+ if (rc) {
+ i2c_set_clientdata(client, NULL);
+ kfree(max);
}
+ return rc;
+}
+
+static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter,
+ u8 addr, const char *loc)
+{
+ struct i2c_board_info info;
+ struct i2c_client *client;
+ char *name;
+
if (!strcmp(loc, "BACKSIDE"))
name = "backside-temp";
else if (!strcmp(loc, "NB Ambient"))
@@ -99,27 +104,26 @@ static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
else
goto fail;
- max->sens.ops = &wf_max6690_ops;
- max->sens.name = name;
- max->i2c.addr = addr >> 1;
- max->i2c.adapter = adapter;
- max->i2c.driver = &wf_max6690_driver;
- strncpy(max->i2c.name, name, I2C_NAME_SIZE-1);
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = addr >> 1;
+ info.platform_data = name;
+ strlcpy(info.type, "wf_max6690", I2C_NAME_SIZE);
- if (i2c_attach_client(&max->i2c)) {
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL) {
printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n");
goto fail;
}
- if (wf_register_sensor(&max->sens)) {
- i2c_detach_client(&max->i2c);
- goto fail;
- }
-
- return;
+ /*
+ * Let i2c-core delete that device on driver removal.
+ * This is safe because i2c-core holds the core_lock mutex for us.
+ */
+ list_add_tail(&client->detected, &client->driver->clients);
+ return client;
fail:
- kfree(max);
+ return NULL;
}
static int wf_max6690_attach(struct i2c_adapter *adapter)
@@ -154,16 +158,31 @@ static int wf_max6690_attach(struct i2c_adapter *adapter)
return 0;
}
-static int wf_max6690_detach(struct i2c_client *client)
+static int wf_max6690_remove(struct i2c_client *client)
{
- struct wf_6690_sensor *max = i2c_to_6690(client);
+ struct wf_6690_sensor *max = i2c_get_clientdata(client);
- max->i2c.adapter = NULL;
+ max->i2c = NULL;
wf_unregister_sensor(&max->sens);
return 0;
}
+static const struct i2c_device_id wf_max6690_id[] = {
+ { "wf_max6690", 0 },
+ { }
+};
+
+static struct i2c_driver wf_max6690_driver = {
+ .driver = {
+ .name = "wf_max6690",
+ },
+ .attach_adapter = wf_max6690_attach,
+ .probe = wf_max6690_probe,
+ .remove = wf_max6690_remove,
+ .id_table = wf_max6690_id,
+};
+
static int __init wf_max6690_sensor_init(void)
{
/* Don't register on old machines that use therm_pm72 for now */
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index 7847e981ac3..5da729e58f9 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -39,7 +39,7 @@ struct wf_sat {
struct mutex mutex;
unsigned long last_read; /* jiffies when cache last updated */
u8 cache[16];
- struct i2c_client i2c;
+ struct i2c_client *i2c;
struct device_node *node;
};
@@ -54,18 +54,6 @@ struct wf_sat_sensor {
};
#define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens)
-#define i2c_to_sat(c) container_of(c, struct wf_sat, i2c)
-
-static int wf_sat_attach(struct i2c_adapter *adapter);
-static int wf_sat_detach(struct i2c_client *client);
-
-static struct i2c_driver wf_sat_driver = {
- .driver = {
- .name = "wf_smu_sat",
- },
- .attach_adapter = wf_sat_attach,
- .detach_client = wf_sat_detach,
-};
struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
unsigned int *size)
@@ -81,13 +69,13 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
if (sat_id > 1 || (sat = sats[sat_id]) == NULL)
return NULL;
- err = i2c_smbus_write_word_data(&sat->i2c, 8, id << 8);
+ err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8);
if (err) {
printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err);
return NULL;
}
- err = i2c_smbus_read_word_data(&sat->i2c, 9);
+ err = i2c_smbus_read_word_data(sat->i2c, 9);
if (err < 0) {
printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");
return NULL;
@@ -105,7 +93,7 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
return NULL;
for (i = 0; i < len; i += 4) {
- err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0xa, 4, data);
+ err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data);
if (err < 0) {
printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
err);
@@ -138,7 +126,7 @@ static int wf_sat_read_cache(struct wf_sat *sat)
{
int err;
- err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0x3f, 16, sat->cache);
+ err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache);
if (err < 0)
return err;
sat->last_read = jiffies;
@@ -161,7 +149,7 @@ static int wf_sat_get(struct wf_sensor *sr, s32 *value)
int i, err;
s32 val;
- if (sat->i2c.adapter == NULL)
+ if (sat->i2c == NULL)
return -ENODEV;
mutex_lock(&sat->mutex);
@@ -193,10 +181,6 @@ static void wf_sat_release(struct wf_sensor *sr)
struct wf_sat *sat = sens->sat;
if (atomic_dec_and_test(&sat->refcnt)) {
- if (sat->i2c.adapter) {
- i2c_detach_client(&sat->i2c);
- sat->i2c.adapter = NULL;
- }
if (sat->nr >= 0)
sats[sat->nr] = NULL;
kfree(sat);
@@ -212,38 +196,58 @@ static struct wf_sensor_ops wf_sat_ops = {
static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
{
+ struct i2c_board_info info;
+ struct i2c_client *client;
+ const u32 *reg;
+ u8 addr;
+
+ reg = of_get_property(dev, "reg", NULL);
+ if (reg == NULL)
+ return;
+ addr = *reg;
+ DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr);
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = (addr >> 1) & 0x7f;
+ info.platform_data = dev;
+ strlcpy(info.type, "wf_sat", I2C_NAME_SIZE);
+
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL) {
+ printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n");
+ return;
+ }
+
+ /*
+ * Let i2c-core delete that device on driver removal.
+ * This is safe because i2c-core holds the core_lock mutex for us.
+ */
+ list_add_tail(&client->detected, &client->driver->clients);
+}
+
+static int wf_sat_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *dev = client->dev.platform_data;
struct wf_sat *sat;
struct wf_sat_sensor *sens;
const u32 *reg;
const char *loc, *type;
- u8 addr, chip, core;
+ u8 chip, core;
struct device_node *child;
int shift, cpu, index;
char *name;
int vsens[2], isens[2];
- reg = of_get_property(dev, "reg", NULL);
- if (reg == NULL)
- return;
- addr = *reg;
- DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr);
-
sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL);
if (sat == NULL)
- return;
+ return -ENOMEM;
sat->nr = -1;
sat->node = of_node_get(dev);
atomic_set(&sat->refcnt, 0);
mutex_init(&sat->mutex);
- sat->i2c.addr = (addr >> 1) & 0x7f;
- sat->i2c.adapter = adapter;
- sat->i2c.driver = &wf_sat_driver;
- strncpy(sat->i2c.name, "smu-sat", I2C_NAME_SIZE-1);
-
- if (i2c_attach_client(&sat->i2c)) {
- printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n");
- goto fail;
- }
+ sat->i2c = client;
+ i2c_set_clientdata(client, sat);
vsens[0] = vsens[1] = -1;
isens[0] = isens[1] = -1;
@@ -344,10 +348,7 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
if (sat->nr >= 0)
sats[sat->nr] = sat;
- return;
-
- fail:
- kfree(sat);
+ return 0;
}
static int wf_sat_attach(struct i2c_adapter *adapter)
@@ -366,16 +367,32 @@ static int wf_sat_attach(struct i2c_adapter *adapter)
return 0;
}
-static int wf_sat_detach(struct i2c_client *client)
+static int wf_sat_remove(struct i2c_client *client)
{
- struct wf_sat *sat = i2c_to_sat(client);
+ struct wf_sat *sat = i2c_get_clientdata(client);
/* XXX TODO */
- sat->i2c.adapter = NULL;
+ sat->i2c = NULL;
+ i2c_set_clientdata(client, NULL);
return 0;
}
+static const struct i2c_device_id wf_sat_id[] = {
+ { "wf_sat", 0 },
+ { }
+};
+
+static struct i2c_driver wf_sat_driver = {
+ .driver = {
+ .name = "wf_smu_sat",
+ },
+ .attach_adapter = wf_sat_attach,
+ .probe = wf_sat_probe,
+ .remove = wf_sat_remove,
+ .id_table = wf_sat_id,
+};
+
static int __init sat_sensors_init(void)
{
return i2c_add_driver(&wf_sat_driver);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 3fd8b1e6548..48db308fae6 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -19,7 +19,6 @@
#include <linux/slab.h>
#include <linux/idr.h>
#include <linux/hdreg.h>
-#include <linux/blktrace_api.h>
#include <trace/events/block.h>
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 89fec052f3b..9118613af32 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -48,6 +48,20 @@ config EEPROM_LEGACY
This driver can also be built as a module. If so, the module
will be called eeprom.
+config EEPROM_MAX6875
+ tristate "Maxim MAX6874/5 power supply supervisor"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get read-only support for the user EEPROM of
+ the Maxim MAX6874/5 EEPROM-programmable, quad power-supply
+ sequencer/supervisor.
+
+ All other features of this chip should be accessed via i2c-dev.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6875.
+
+
config EEPROM_93CX6
tristate "EEPROM 93CX6 support"
help
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 539dd8f8812..df3d68ffa9d 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_EEPROM_AT24) += at24.o
obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
+obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
diff --git a/drivers/i2c/chips/max6875.c b/drivers/misc/eeprom/max6875.c
index 033d9d81ec8..3c0c58eed34 100644
--- a/drivers/i2c/chips/max6875.c
+++ b/drivers/misc/eeprom/max6875.c
@@ -3,7 +3,7 @@
Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
- Based on i2c/chips/eeprom.c
+ Based on eeprom.c
The MAX6875 has a bank of registers and two banks of EEPROM.
Address ranges are defined as follows:
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c
index 30900b30d53..2b38f39924a 100644
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -1648,7 +1648,7 @@ static int ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
result = -ENOMEM;
goto fail_alloc_card;
}
- ps3_system_bus_set_driver_data(dev, card);
+ ps3_system_bus_set_drvdata(dev, card);
card->dev = dev;
/* get internal vlan info */
@@ -1749,7 +1749,7 @@ fail_alloc_irq:
bus_id(card),
0, 0);
fail_status_indicator:
- ps3_system_bus_set_driver_data(dev, NULL);
+ ps3_system_bus_set_drvdata(dev, NULL);
kfree(netdev_card(netdev)->unalign);
free_netdev(netdev);
fail_alloc_card:
@@ -1766,7 +1766,7 @@ fail_open:
static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
{
- struct gelic_card *card = ps3_system_bus_get_driver_data(dev);
+ struct gelic_card *card = ps3_system_bus_get_drvdata(dev);
struct net_device *netdev0;
pr_debug("%s: called\n", __func__);
@@ -1803,7 +1803,7 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
kfree(netdev_card(netdev0)->unalign);
free_netdev(netdev0);
- ps3_system_bus_set_driver_data(dev, NULL);
+ ps3_system_bus_set_drvdata(dev, NULL);
ps3_dma_region_free(dev->d_region);
diff --git a/drivers/ps3/ps3-sys-manager.c b/drivers/ps3/ps3-sys-manager.c
index f17513dd9d4..88cb7408861 100644
--- a/drivers/ps3/ps3-sys-manager.c
+++ b/drivers/ps3/ps3-sys-manager.c
@@ -706,7 +706,7 @@ static void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
}
-static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
+static int __devinit ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
{
int result;
struct ps3_sys_manager_ops ops;
diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c
index 235e87fcb49..e82d8c9c6cd 100644
--- a/drivers/ps3/ps3av.c
+++ b/drivers/ps3/ps3av.c
@@ -80,12 +80,12 @@ static const struct avset_video_mode {
{ 0, }, /* auto */
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480},
- {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_N, 1280, 720},
+ {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_W, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576},
- {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_N, 1280, 720},
+ {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_W, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080},
{ RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768},
@@ -937,7 +937,7 @@ int ps3av_audio_mute(int mute)
EXPORT_SYMBOL_GPL(ps3av_audio_mute);
-static int ps3av_probe(struct ps3_system_bus_device *dev)
+static int __devinit ps3av_probe(struct ps3_system_bus_device *dev)
{
int res;
int id;
@@ -1048,7 +1048,7 @@ static struct ps3_vuart_port_driver ps3av_driver = {
.shutdown = ps3av_shutdown,
};
-static int ps3av_module_init(void)
+static int __init ps3av_module_init(void)
{
int error;
diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c
index 716596e8e5b..f555fedd507 100644
--- a/drivers/ps3/ps3av_cmd.c
+++ b/drivers/ps3/ps3av_cmd.c
@@ -21,9 +21,10 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+
#include <asm/ps3av.h>
-#include <asm/ps3fb.h>
#include <asm/ps3.h>
+#include <asm/ps3gpu.h>
#include "vuart.h"
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 442bb98a282..e5b84db0aa0 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -5,8 +5,7 @@
* Carsten Otte <Cotte@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
- *
+ * Copyright IBM Corp. 1999, 2009
*/
#define KMSG_COMPONENT "dasd"
@@ -61,6 +60,7 @@ static int dasd_flush_block_queue(struct dasd_block *);
static void dasd_device_tasklet(struct dasd_device *);
static void dasd_block_tasklet(struct dasd_block *);
static void do_kick_device(struct work_struct *);
+static void do_restore_device(struct work_struct *);
static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
static void dasd_device_timeout(unsigned long);
static void dasd_block_timeout(unsigned long);
@@ -109,6 +109,7 @@ struct dasd_device *dasd_alloc_device(void)
device->timer.function = dasd_device_timeout;
device->timer.data = (unsigned long) device;
INIT_WORK(&device->kick_work, do_kick_device);
+ INIT_WORK(&device->restore_device, do_restore_device);
device->state = DASD_STATE_NEW;
device->target = DASD_STATE_NEW;
@@ -512,6 +513,25 @@ void dasd_kick_device(struct dasd_device *device)
}
/*
+ * dasd_restore_device will schedule a call do do_restore_device to the kernel
+ * event daemon.
+ */
+static void do_restore_device(struct work_struct *work)
+{
+ struct dasd_device *device = container_of(work, struct dasd_device,
+ restore_device);
+ device->cdev->drv->restore(device->cdev);
+ dasd_put_device(device);
+}
+
+void dasd_restore_device(struct dasd_device *device)
+{
+ dasd_get_device(device);
+ /* queue call to dasd_restore_device to the kernel event daemon. */
+ schedule_work(&device->restore_device);
+}
+
+/*
* Set the target state for a device and starts the state change.
*/
void dasd_set_target_state(struct dasd_device *device, int target)
@@ -908,6 +928,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
"start_IO: -EIO device gone, retry");
break;
+ case -EINVAL:
+ /* most likely caused in power management context */
+ DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
+ "start_IO: -EINVAL device currently "
+ "not accessible");
+ break;
default:
/* internal error 11 - unknown rc */
snprintf(errorstring, ERRORLENGTH, "11 %d", rc);
@@ -2400,6 +2426,12 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
case CIO_OPER:
/* FIXME: add a sanity check. */
device->stopped &= ~DASD_STOPPED_DC_WAIT;
+ if (device->stopped & DASD_UNRESUMED_PM) {
+ device->stopped &= ~DASD_UNRESUMED_PM;
+ dasd_restore_device(device);
+ ret = 1;
+ break;
+ }
dasd_schedule_device_bh(device);
if (device->block)
dasd_schedule_block_bh(device->block);
@@ -2410,6 +2442,79 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
return ret;
}
+int dasd_generic_pm_freeze(struct ccw_device *cdev)
+{
+ struct dasd_ccw_req *cqr, *n;
+ int rc;
+ struct list_head freeze_queue;
+ struct dasd_device *device = dasd_device_from_cdev(cdev);
+
+ if (IS_ERR(device))
+ return PTR_ERR(device);
+ /* disallow new I/O */
+ device->stopped |= DASD_STOPPED_PM;
+ /* clear active requests */
+ INIT_LIST_HEAD(&freeze_queue);
+ spin_lock_irq(get_ccwdev_lock(cdev));
+ rc = 0;
+ list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) {
+ /* Check status and move request to flush_queue */
+ if (cqr->status == DASD_CQR_IN_IO) {
+ rc = device->discipline->term_IO(cqr);
+ if (rc) {
+ /* unable to terminate requeust */
+ dev_err(&device->cdev->dev,
+ "Unable to terminate request %p "
+ "on suspend\n", cqr);
+ spin_unlock_irq(get_ccwdev_lock(cdev));
+ dasd_put_device(device);
+ return rc;
+ }
+ }
+ list_move_tail(&cqr->devlist, &freeze_queue);
+ }
+
+ spin_unlock_irq(get_ccwdev_lock(cdev));
+
+ list_for_each_entry_safe(cqr, n, &freeze_queue, devlist) {
+ wait_event(dasd_flush_wq,
+ (cqr->status != DASD_CQR_CLEAR_PENDING));
+ if (cqr->status == DASD_CQR_CLEARED)
+ cqr->status = DASD_CQR_QUEUED;
+ }
+ /* move freeze_queue to start of the ccw_queue */
+ spin_lock_irq(get_ccwdev_lock(cdev));
+ list_splice_tail(&freeze_queue, &device->ccw_queue);
+ spin_unlock_irq(get_ccwdev_lock(cdev));
+
+ if (device->discipline->freeze)
+ rc = device->discipline->freeze(device);
+
+ dasd_put_device(device);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(dasd_generic_pm_freeze);
+
+int dasd_generic_restore_device(struct ccw_device *cdev)
+{
+ struct dasd_device *device = dasd_device_from_cdev(cdev);
+ int rc = 0;
+
+ if (IS_ERR(device))
+ return PTR_ERR(device);
+
+ dasd_schedule_device_bh(device);
+ if (device->block)
+ dasd_schedule_block_bh(device->block);
+
+ if (device->discipline->restore)
+ rc = device->discipline->restore(device);
+
+ dasd_put_device(device);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(dasd_generic_restore_device);
+
static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
void *rdc_buffer,
int rdc_buffer_size,
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index e77666c8e6c..4cac5b54f26 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -1098,6 +1098,7 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
spin_unlock(&dasd_devmap_lock);
return 0;
}
+EXPORT_SYMBOL_GPL(dasd_get_uid);
/*
* Register the given device unique identifier into devmap struct.
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index cf0cfdba124..1c28ec3e4cc 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -5,10 +5,9 @@
* Carsten Otte <Cotte@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+ * Copyright IBM Corp. 1999, 2009
* EMC Symmetrix ioctl Copyright EMC Corporation, 2008
* Author.........: Nigel Hislop <hislop_nigel@emc.com>
- *
*/
#define KMSG_COMPONENT "dasd"
@@ -104,17 +103,6 @@ dasd_eckd_set_online(struct ccw_device *cdev)
return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
}
-static struct ccw_driver dasd_eckd_driver = {
- .name = "dasd-eckd",
- .owner = THIS_MODULE,
- .ids = dasd_eckd_ids,
- .probe = dasd_eckd_probe,
- .remove = dasd_generic_remove,
- .set_offline = dasd_generic_set_offline,
- .set_online = dasd_eckd_set_online,
- .notify = dasd_generic_notify,
-};
-
static const int sizes_trk0[] = { 28, 148, 84 };
#define LABEL_SIZE 140
@@ -3236,6 +3224,98 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
dasd_eckd_dump_sense_ccw(device, req, irb);
}
+int dasd_eckd_pm_freeze(struct dasd_device *device)
+{
+ /*
+ * the device should be disconnected from our LCU structure
+ * on restore we will reconnect it and reread LCU specific
+ * information like PAV support that might have changed
+ */
+ dasd_alias_remove_device(device);
+ dasd_alias_disconnect_device_from_lcu(device);
+
+ return 0;
+}
+
+int dasd_eckd_restore_device(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ int is_known, rc;
+ struct dasd_uid temp_uid;
+
+ /* allow new IO again */
+ device->stopped &= ~DASD_STOPPED_PM;
+
+ private = (struct dasd_eckd_private *) device->private;
+
+ /* Read Configuration Data */
+ rc = dasd_eckd_read_conf(device);
+ if (rc)
+ goto out_err;
+
+ /* Generate device unique id and register in devmap */
+ rc = dasd_eckd_generate_uid(device, &private->uid);
+ dasd_get_uid(device->cdev, &temp_uid);
+ if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0)
+ dev_err(&device->cdev->dev, "The UID of the DASD has changed\n");
+ if (rc)
+ goto out_err;
+ dasd_set_uid(device->cdev, &private->uid);
+
+ /* register lcu with alias handling, enable PAV if this is a new lcu */
+ is_known = dasd_alias_make_device_known_to_lcu(device);
+ if (is_known < 0)
+ return is_known;
+ if (!is_known) {
+ /* new lcu found */
+ rc = dasd_eckd_validate_server(device); /* will switch pav on */
+ if (rc)
+ goto out_err;
+ }
+
+ /* Read Feature Codes */
+ rc = dasd_eckd_read_features(device);
+ if (rc)
+ goto out_err;
+
+ /* Read Device Characteristics */
+ memset(&private->rdc_data, 0, sizeof(private->rdc_data));
+ rc = dasd_generic_read_dev_chars(device, "ECKD",
+ &private->rdc_data, 64);
+ if (rc) {
+ DBF_EVENT(DBF_WARNING,
+ "Read device characteristics failed, rc=%d for "
+ "device: %s", rc, dev_name(&device->cdev->dev));
+ goto out_err;
+ }
+
+ /* add device to alias management */
+ dasd_alias_add_device(device);
+
+ return 0;
+
+out_err:
+ /*
+ * if the resume failed for the DASD we put it in
+ * an UNRESUMED stop state
+ */
+ device->stopped |= DASD_UNRESUMED_PM;
+ return 0;
+}
+
+static struct ccw_driver dasd_eckd_driver = {
+ .name = "dasd-eckd",
+ .owner = THIS_MODULE,
+ .ids = dasd_eckd_ids,
+ .probe = dasd_eckd_probe,
+ .remove = dasd_generic_remove,
+ .set_offline = dasd_generic_set_offline,
+ .set_online = dasd_eckd_set_online,
+ .notify = dasd_generic_notify,
+ .freeze = dasd_generic_pm_freeze,
+ .thaw = dasd_generic_restore_device,
+ .restore = dasd_generic_restore_device,
+};
/*
* max_blocks is dependent on the amount of storage that is available
@@ -3274,6 +3354,8 @@ static struct dasd_discipline dasd_eckd_discipline = {
.dump_sense_dbf = dasd_eckd_dump_sense_dbf,
.fill_info = dasd_eckd_fill_info,
.ioctl = dasd_eckd_ioctl,
+ .freeze = dasd_eckd_pm_freeze,
+ .restore = dasd_eckd_restore_device,
};
static int __init
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 597c6ffdb9f..e21ee735f92 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -2,8 +2,7 @@
* File...........: linux/drivers/s390/block/dasd_fba.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
- *
+ * Copyright IBM Corp. 1999, 2009
*/
#define KMSG_COMPONENT "dasd"
@@ -75,6 +74,9 @@ static struct ccw_driver dasd_fba_driver = {
.set_offline = dasd_generic_set_offline,
.set_online = dasd_fba_set_online,
.notify = dasd_generic_notify,
+ .freeze = dasd_generic_pm_freeze,
+ .thaw = dasd_generic_restore_device,
+ .restore = dasd_generic_restore_device,
};
static void
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index f97ceb79507..fd63b2f2bda 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -4,8 +4,7 @@
* Horst Hummel <Horst.Hummel@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
- *
+ * Copyright IBM Corp. 1999, 2009
*/
#ifndef DASD_INT_H
@@ -295,6 +294,10 @@ struct dasd_discipline {
int (*fill_geometry) (struct dasd_block *, struct hd_geometry *);
int (*fill_info) (struct dasd_device *, struct dasd_information2_t *);
int (*ioctl) (struct dasd_block *, unsigned int, void __user *);
+
+ /* suspend/resume functions */
+ int (*freeze) (struct dasd_device *);
+ int (*restore) (struct dasd_device *);
};
extern struct dasd_discipline *dasd_diag_discipline_pointer;
@@ -367,6 +370,7 @@ struct dasd_device {
atomic_t tasklet_scheduled;
struct tasklet_struct tasklet;
struct work_struct kick_work;
+ struct work_struct restore_device;
struct timer_list timer;
debug_info_t *debug_area;
@@ -410,6 +414,8 @@ struct dasd_block {
#define DASD_STOPPED_PENDING 4 /* long busy */
#define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */
#define DASD_STOPPED_SU 16 /* summary unit check handling */
+#define DASD_STOPPED_PM 32 /* pm state transition */
+#define DASD_UNRESUMED_PM 64 /* pm resume failed state */
/* per device flags */
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
@@ -556,6 +562,7 @@ void dasd_free_block(struct dasd_block *);
void dasd_enable_device(struct dasd_device *);
void dasd_set_target_state(struct dasd_device *, int);
void dasd_kick_device(struct dasd_device *);
+void dasd_restore_device(struct dasd_device *);
void dasd_add_request_head(struct dasd_ccw_req *);
void dasd_add_request_tail(struct dasd_ccw_req *);
@@ -578,6 +585,8 @@ int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
int dasd_generic_set_offline (struct ccw_device *cdev);
int dasd_generic_notify(struct ccw_device *, int);
void dasd_generic_handle_state_change(struct dasd_device *);
+int dasd_generic_pm_freeze(struct ccw_device *);
+int dasd_generic_restore_device(struct ccw_device *);
int dasd_generic_read_dev_chars(struct dasd_device *, char *, void *, int);
char *dasd_get_sense(struct irb *);
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index b21caf177e3..016f9e9d259 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -14,10 +14,11 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
-#include <asm/extmem.h>
-#include <asm/io.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <asm/extmem.h>
+#include <asm/io.h>
#define DCSSBLK_NAME "dcssblk"
#define DCSSBLK_MINORS_PER_DISK 1
@@ -940,11 +941,94 @@ dcssblk_check_params(void)
}
/*
+ * Suspend / Resume
+ */
+static int dcssblk_freeze(struct device *dev)
+{
+ struct dcssblk_dev_info *dev_info;
+ int rc = 0;
+
+ list_for_each_entry(dev_info, &dcssblk_devices, lh) {
+ switch (dev_info->segment_type) {
+ case SEG_TYPE_SR:
+ case SEG_TYPE_ER:
+ case SEG_TYPE_SC:
+ if (!dev_info->is_shared)
+ rc = -EINVAL;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ if (rc)
+ break;
+ }
+ if (rc)
+ pr_err("Suspend failed because device %s is writeable.\n",
+ dev_info->segment_name);
+ return rc;
+}
+
+static int dcssblk_restore(struct device *dev)
+{
+ struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry;
+ unsigned long start, end;
+ int rc = 0;
+
+ list_for_each_entry(dev_info, &dcssblk_devices, lh) {
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ segment_unload(entry->segment_name);
+ rc = segment_load(entry->segment_name, SEGMENT_SHARED,
+ &start, &end);
+ if (rc < 0) {
+// TODO in_use check ?
+ segment_warning(rc, entry->segment_name);
+ goto out_panic;
+ }
+ if (start != entry->start || end != entry->end) {
+ pr_err("Mismatch of start / end address after "
+ "resuming device %s\n",
+ entry->segment_name);
+ goto out_panic;
+ }
+ }
+ }
+ return 0;
+out_panic:
+ panic("fatal dcssblk resume error\n");
+}
+
+static int dcssblk_thaw(struct device *dev)
+{
+ return 0;
+}
+
+static struct dev_pm_ops dcssblk_pm_ops = {
+ .freeze = dcssblk_freeze,
+ .thaw = dcssblk_thaw,
+ .restore = dcssblk_restore,
+};
+
+static struct platform_driver dcssblk_pdrv = {
+ .driver = {
+ .name = "dcssblk",
+ .owner = THIS_MODULE,
+ .pm = &dcssblk_pm_ops,
+ },
+};
+
+static struct platform_device *dcssblk_pdev;
+
+
+/*
* The init/exit functions.
*/
static void __exit
dcssblk_exit(void)
{
+ platform_device_unregister(dcssblk_pdev);
+ platform_driver_unregister(&dcssblk_pdrv);
root_device_unregister(dcssblk_root_dev);
unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
}
@@ -954,30 +1038,44 @@ dcssblk_init(void)
{
int rc;
- dcssblk_root_dev = root_device_register("dcssblk");
- if (IS_ERR(dcssblk_root_dev))
- return PTR_ERR(dcssblk_root_dev);
- rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
- if (rc) {
- root_device_unregister(dcssblk_root_dev);
+ rc = platform_driver_register(&dcssblk_pdrv);
+ if (rc)
return rc;
+
+ dcssblk_pdev = platform_device_register_simple("dcssblk", -1, NULL,
+ 0);
+ if (IS_ERR(dcssblk_pdev)) {
+ rc = PTR_ERR(dcssblk_pdev);
+ goto out_pdrv;
}
- rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
- if (rc) {
- root_device_unregister(dcssblk_root_dev);
- return rc;
+
+ dcssblk_root_dev = root_device_register("dcssblk");
+ if (IS_ERR(dcssblk_root_dev)) {
+ rc = PTR_ERR(dcssblk_root_dev);
+ goto out_pdev;
}
+ rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
+ if (rc)
+ goto out_root;
+ rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
+ if (rc)
+ goto out_root;
rc = register_blkdev(0, DCSSBLK_NAME);
- if (rc < 0) {
- root_device_unregister(dcssblk_root_dev);
- return rc;
- }
+ if (rc < 0)
+ goto out_root;
dcssblk_major = rc;
init_rwsem(&dcssblk_devices_sem);
dcssblk_check_params();
-
return 0;
+
+out_root:
+ root_device_unregister(dcssblk_root_dev);
+out_pdev:
+ platform_device_unregister(dcssblk_pdev);
+out_pdrv:
+ platform_driver_unregister(&dcssblk_pdrv);
+ return rc;
}
module_init(dcssblk_init);
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 0ae0c83ef87..2e9e1ecd6d8 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -39,7 +39,10 @@
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/sysdev.h>
#include <linux/bio.h>
+#include <linux/suspend.h>
+#include <linux/platform_device.h>
#include <asm/uaccess.h>
+#include <asm/checksum.h>
#define XPRAM_NAME "xpram"
#define XPRAM_DEVS 1 /* one partition */
@@ -48,6 +51,7 @@
typedef struct {
unsigned int size; /* size of xpram segment in pages */
unsigned int offset; /* start page of xpram segment */
+ unsigned int csum; /* partition checksum for suspend */
} xpram_device_t;
static xpram_device_t xpram_devices[XPRAM_MAX_DEVS];
@@ -138,7 +142,7 @@ static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index)
/*
* Check if xpram is available.
*/
-static int __init xpram_present(void)
+static int xpram_present(void)
{
unsigned long mem_page;
int rc;
@@ -154,7 +158,7 @@ static int __init xpram_present(void)
/*
* Return index of the last available xpram page.
*/
-static unsigned long __init xpram_highest_page_index(void)
+static unsigned long xpram_highest_page_index(void)
{
unsigned int page_index, add_bit;
unsigned long mem_page;
@@ -383,6 +387,106 @@ out:
}
/*
+ * Save checksums for all partitions.
+ */
+static int xpram_save_checksums(void)
+{
+ unsigned long mem_page;
+ int rc, i;
+
+ rc = 0;
+ mem_page = (unsigned long) __get_free_page(GFP_KERNEL);
+ if (!mem_page)
+ return -ENOMEM;
+ for (i = 0; i < xpram_devs; i++) {
+ rc = xpram_page_in(mem_page, xpram_devices[i].offset);
+ if (rc)
+ goto fail;
+ xpram_devices[i].csum = csum_partial((const void *) mem_page,
+ PAGE_SIZE, 0);
+ }
+fail:
+ free_page(mem_page);
+ return rc ? -ENXIO : 0;
+}
+
+/*
+ * Verify checksums for all partitions.
+ */
+static int xpram_validate_checksums(void)
+{
+ unsigned long mem_page;
+ unsigned int csum;
+ int rc, i;
+
+ rc = 0;
+ mem_page = (unsigned long) __get_free_page(GFP_KERNEL);
+ if (!mem_page)
+ return -ENOMEM;
+ for (i = 0; i < xpram_devs; i++) {
+ rc = xpram_page_in(mem_page, xpram_devices[i].offset);
+ if (rc)
+ goto fail;
+ csum = csum_partial((const void *) mem_page, PAGE_SIZE, 0);
+ if (xpram_devices[i].csum != csum) {
+ rc = -EINVAL;
+ goto fail;
+ }
+ }
+fail:
+ free_page(mem_page);
+ return rc ? -ENXIO : 0;
+}
+
+/*
+ * Resume failed: Print error message and call panic.
+ */
+static void xpram_resume_error(const char *message)
+{
+ pr_err("Resume error: %s\n", message);
+ panic("xpram resume error\n");
+}
+
+/*
+ * Check if xpram setup changed between suspend and resume.
+ */
+static int xpram_restore(struct device *dev)
+{
+ if (!xpram_pages)
+ return 0;
+ if (xpram_present() != 0)
+ xpram_resume_error("xpram disappeared");
+ if (xpram_pages != xpram_highest_page_index() + 1)
+ xpram_resume_error("Size of xpram changed");
+ if (xpram_validate_checksums())
+ xpram_resume_error("Data of xpram changed");
+ return 0;
+}
+
+/*
+ * Save necessary state in suspend.
+ */
+static int xpram_freeze(struct device *dev)
+{
+ return xpram_save_checksums();
+}
+
+static struct dev_pm_ops xpram_pm_ops = {
+ .freeze = xpram_freeze,
+ .restore = xpram_restore,
+};
+
+static struct platform_driver xpram_pdrv = {
+ .driver = {
+ .name = XPRAM_NAME,
+ .owner = THIS_MODULE,
+ .pm = &xpram_pm_ops,
+ },
+};
+
+static struct platform_device *xpram_pdev;
+
+/*
* Finally, the init/exit functions.
*/
static void __exit xpram_exit(void)
@@ -394,6 +498,8 @@ static void __exit xpram_exit(void)
put_disk(xpram_disks[i]);
}
unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
+ platform_device_unregister(xpram_pdev);
+ platform_driver_unregister(&xpram_pdrv);
}
static int __init xpram_init(void)
@@ -411,7 +517,24 @@ static int __init xpram_init(void)
rc = xpram_setup_sizes(xpram_pages);
if (rc)
return rc;
- return xpram_setup_blkdev();
+ rc = platform_driver_register(&xpram_pdrv);
+ if (rc)
+ return rc;
+ xpram_pdev = platform_device_register_simple(XPRAM_NAME, -1, NULL, 0);
+ if (IS_ERR(xpram_pdev)) {
+ rc = PTR_ERR(xpram_pdev);
+ goto fail_platform_driver_unregister;
+ }
+ rc = xpram_setup_blkdev();
+ if (rc)
+ goto fail_platform_device_unregister;
+ return 0;
+
+fail_platform_device_unregister:
+ platform_device_unregister(xpram_pdev);
+fail_platform_driver_unregister:
+ platform_driver_unregister(&xpram_pdrv);
+ return rc;
}
module_init(xpram_init);
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 9ab06e0dad4..b79f31add39 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -1,14 +1,12 @@
/*
- * drivers/s390/char/con3215.c
- * 3215 line mode terminal driver.
+ * 3215 line mode terminal driver.
*
- * S390 version
- * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Copyright IBM Corp. 1999, 2009
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*
- * Updated:
- * Aug-2000: Added tab support
- * Dan Morrison, IBM Corporation (dmorriso@cse.buffalo.edu)
+ * Updated:
+ * Aug-2000: Added tab support
+ * Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu>
*/
#include <linux/module.h>
@@ -56,6 +54,7 @@
#define RAW3215_CLOSING 32 /* set while in close process */
#define RAW3215_TIMER_RUNS 64 /* set if the output delay timer is on */
#define RAW3215_FLUSHING 128 /* set to flush buffer (no delay) */
+#define RAW3215_FROZEN 256 /* set if 3215 is frozen for suspend */
#define TAB_STOP_SIZE 8 /* tab stop size */
@@ -111,8 +110,8 @@ static struct tty_driver *tty3215_driver;
/*
* Get a request structure from the free list
*/
-static inline struct raw3215_req *
-raw3215_alloc_req(void) {
+static inline struct raw3215_req *raw3215_alloc_req(void)
+{
struct raw3215_req *req;
unsigned long flags;
@@ -126,8 +125,8 @@ raw3215_alloc_req(void) {
/*
* Put a request structure back to the free list
*/
-static inline void
-raw3215_free_req(struct raw3215_req *req) {
+static inline void raw3215_free_req(struct raw3215_req *req)
+{
unsigned long flags;
if (req->type == RAW3215_FREE)
@@ -145,8 +144,7 @@ raw3215_free_req(struct raw3215_req *req) {
* because a 3215 terminal won't accept a new read before the old one is
* completed.
*/
-static void
-raw3215_mk_read_req(struct raw3215_info *raw)
+static void raw3215_mk_read_req(struct raw3215_info *raw)
{
struct raw3215_req *req;
struct ccw1 *ccw;
@@ -174,8 +172,7 @@ raw3215_mk_read_req(struct raw3215_info *raw)
* buffer to the 3215 device. If a queued write exists it is replaced by
* the new, probably lengthened request.
*/
-static void
-raw3215_mk_write_req(struct raw3215_info *raw)
+static void raw3215_mk_write_req(struct raw3215_info *raw)
{
struct raw3215_req *req;
struct ccw1 *ccw;
@@ -251,8 +248,7 @@ raw3215_mk_write_req(struct raw3215_info *raw)
/*
* Start a read or a write request
*/
-static void
-raw3215_start_io(struct raw3215_info *raw)
+static void raw3215_start_io(struct raw3215_info *raw)
{
struct raw3215_req *req;
int res;
@@ -290,8 +286,7 @@ raw3215_start_io(struct raw3215_info *raw)
/*
* Function to start a delayed output after RAW3215_TIMEOUT seconds
*/
-static void
-raw3215_timeout(unsigned long __data)
+static void raw3215_timeout(unsigned long __data)
{
struct raw3215_info *raw = (struct raw3215_info *) __data;
unsigned long flags;
@@ -300,8 +295,10 @@ raw3215_timeout(unsigned long __data)
if (raw->flags & RAW3215_TIMER_RUNS) {
del_timer(&raw->timer);
raw->flags &= ~RAW3215_TIMER_RUNS;
- raw3215_mk_write_req(raw);
- raw3215_start_io(raw);
+ if (!(raw->flags & RAW3215_FROZEN)) {
+ raw3215_mk_write_req(raw);
+ raw3215_start_io(raw);
+ }
}
spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
}
@@ -312,10 +309,9 @@ raw3215_timeout(unsigned long __data)
* amount of data is bigger than RAW3215_MIN_WRITE. If a write is not
* done immediately a timer is started with a delay of RAW3215_TIMEOUT.
*/
-static inline void
-raw3215_try_io(struct raw3215_info *raw)
+static inline void raw3215_try_io(struct raw3215_info *raw)
{
- if (!(raw->flags & RAW3215_ACTIVE))
+ if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FROZEN))
return;
if (raw->queued_read != NULL)
raw3215_start_io(raw);
@@ -359,8 +355,8 @@ static void raw3215_next_io(struct raw3215_info *raw)
/*
* Interrupt routine, called from common io layer
*/
-static void
-raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
+static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,
+ struct irb *irb)
{
struct raw3215_info *raw;
struct raw3215_req *req;
@@ -459,14 +455,40 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
/*
+ * Drop the oldest line from the output buffer.
+ */
+static void raw3215_drop_line(struct raw3215_info *raw)
+{
+ int ix;
+ char ch;
+
+ BUG_ON(raw->written != 0);
+ ix = (raw->head - raw->count) & (RAW3215_BUFFER_SIZE - 1);
+ while (raw->count > 0) {
+ ch = raw->buffer[ix];
+ ix = (ix + 1) & (RAW3215_BUFFER_SIZE - 1);
+ raw->count--;
+ if (ch == 0x15)
+ break;
+ }
+ raw->head = ix;
+}
+
+/*
* Wait until length bytes are available int the output buffer.
* Has to be called with the s390irq lock held. Can be called
* disabled.
*/
-static void
-raw3215_make_room(struct raw3215_info *raw, unsigned int length)
+static void raw3215_make_room(struct raw3215_info *raw, unsigned int length)
{
while (RAW3215_BUFFER_SIZE - raw->count < length) {
+ /* While console is frozen for suspend we have no other
+ * choice but to drop message from the buffer to make
+ * room for even more messages. */
+ if (raw->flags & RAW3215_FROZEN) {
+ raw3215_drop_line(raw);
+ continue;
+ }
/* there might be a request pending */
raw->flags |= RAW3215_FLUSHING;
raw3215_mk_write_req(raw);
@@ -488,8 +510,8 @@ raw3215_make_room(struct raw3215_info *raw, unsigned int length)
/*
* String write routine for 3215 devices
*/
-static void
-raw3215_write(struct raw3215_info *raw, const char *str, unsigned int length)
+static void raw3215_write(struct raw3215_info *raw, const char *str,
+ unsigned int length)
{
unsigned long flags;
int c, count;
@@ -529,8 +551,7 @@ raw3215_write(struct raw3215_info *raw, const char *str, unsigned int length)
/*
* Put character routine for 3215 devices
*/
-static void
-raw3215_putchar(struct raw3215_info *raw, unsigned char ch)
+static void raw3215_putchar(struct raw3215_info *raw, unsigned char ch)
{
unsigned long flags;
unsigned int length, i;
@@ -566,8 +587,7 @@ raw3215_putchar(struct raw3215_info *raw, unsigned char ch)
* Flush routine, it simply sets the flush flag and tries to start
* pending IO.
*/
-static void
-raw3215_flush_buffer(struct raw3215_info *raw)
+static void raw3215_flush_buffer(struct raw3215_info *raw)
{
unsigned long flags;
@@ -583,8 +603,7 @@ raw3215_flush_buffer(struct raw3215_info *raw)
/*
* Fire up a 3215 device.
*/
-static int
-raw3215_startup(struct raw3215_info *raw)
+static int raw3215_startup(struct raw3215_info *raw)
{
unsigned long flags;
@@ -602,8 +621,7 @@ raw3215_startup(struct raw3215_info *raw)
/*
* Shutdown a 3215 device.
*/
-static void
-raw3215_shutdown(struct raw3215_info *raw)
+static void raw3215_shutdown(struct raw3215_info *raw)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
@@ -628,8 +646,7 @@ raw3215_shutdown(struct raw3215_info *raw)
spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
}
-static int
-raw3215_probe (struct ccw_device *cdev)
+static int raw3215_probe (struct ccw_device *cdev)
{
struct raw3215_info *raw;
int line;
@@ -675,8 +692,7 @@ raw3215_probe (struct ccw_device *cdev)
return 0;
}
-static void
-raw3215_remove (struct ccw_device *cdev)
+static void raw3215_remove (struct ccw_device *cdev)
{
struct raw3215_info *raw;
@@ -689,8 +705,7 @@ raw3215_remove (struct ccw_device *cdev)
}
}
-static int
-raw3215_set_online (struct ccw_device *cdev)
+static int raw3215_set_online (struct ccw_device *cdev)
{
struct raw3215_info *raw;
@@ -701,8 +716,7 @@ raw3215_set_online (struct ccw_device *cdev)
return raw3215_startup(raw);
}
-static int
-raw3215_set_offline (struct ccw_device *cdev)
+static int raw3215_set_offline (struct ccw_device *cdev)
{
struct raw3215_info *raw;
@@ -715,6 +729,36 @@ raw3215_set_offline (struct ccw_device *cdev)
return 0;
}
+static int raw3215_pm_stop(struct ccw_device *cdev)
+{
+ struct raw3215_info *raw;
+ unsigned long flags;
+
+ /* Empty the output buffer, then prevent new I/O. */
+ raw = cdev->dev.driver_data;
+ spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
+ raw3215_make_room(raw, RAW3215_BUFFER_SIZE);
+ raw->flags |= RAW3215_FROZEN;
+ spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
+ return 0;
+}
+
+static int raw3215_pm_start(struct ccw_device *cdev)
+{
+ struct raw3215_info *raw;
+ unsigned long flags;
+
+ /* Allow I/O again and flush output buffer. */
+ raw = cdev->dev.driver_data;
+ spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
+ raw->flags &= ~RAW3215_FROZEN;
+ raw->flags |= RAW3215_FLUSHING;
+ raw3215_try_io(raw);
+ raw->flags &= ~RAW3215_FLUSHING;
+ spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
+ return 0;
+}
+
static struct ccw_device_id raw3215_id[] = {
{ CCW_DEVICE(0x3215, 0) },
{ /* end of list */ },
@@ -728,14 +772,17 @@ static struct ccw_driver raw3215_ccw_driver = {
.remove = &raw3215_remove,
.set_online = &raw3215_set_online,
.set_offline = &raw3215_set_offline,
+ .freeze = &raw3215_pm_stop,
+ .thaw = &raw3215_pm_start,
+ .restore = &raw3215_pm_start,
};
#ifdef CONFIG_TN3215_CONSOLE
/*
* Write a string to the 3215 console
*/
-static void
-con3215_write(struct console *co, const char *str, unsigned int count)
+static void con3215_write(struct console *co, const char *str,
+ unsigned int count)
{
struct raw3215_info *raw;
int i;
@@ -768,13 +815,17 @@ static struct tty_driver *con3215_device(struct console *c, int *index)
* panic() calls con3215_flush through a panic_notifier
* before the system enters a disabled, endless loop.
*/
-static void
-con3215_flush(void)
+static void con3215_flush(void)
{
struct raw3215_info *raw;
unsigned long flags;
raw = raw3215[0]; /* console 3215 is the first one */
+ if (raw->flags & RAW3215_FROZEN)
+ /* The console is still frozen for suspend. */
+ if (ccw_device_force_console())
+ /* Forcing didn't work, no panic message .. */
+ return;
spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
raw3215_make_room(raw, RAW3215_BUFFER_SIZE);
spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
@@ -811,8 +862,7 @@ static struct console con3215 = {
* 3215 console initialization code called from console_init().
* NOTE: This is called before kmalloc is available.
*/
-static int __init
-con3215_init(void)
+static int __init con3215_init(void)
{
struct ccw_device *cdev;
struct raw3215_info *raw;
@@ -875,8 +925,7 @@ console_initcall(con3215_init);
*
* This routine is called whenever a 3215 tty is opened.
*/
-static int
-tty3215_open(struct tty_struct *tty, struct file * filp)
+static int tty3215_open(struct tty_struct *tty, struct file * filp)
{
struct raw3215_info *raw;
int retval, line;
@@ -909,8 +958,7 @@ tty3215_open(struct tty_struct *tty, struct file * filp)
* This routine is called when the 3215 tty is closed. We wait
* for the remaining request to be completed. Then we clean up.
*/
-static void
-tty3215_close(struct tty_struct *tty, struct file * filp)
+static void tty3215_close(struct tty_struct *tty, struct file * filp)
{
struct raw3215_info *raw;
@@ -927,8 +975,7 @@ tty3215_close(struct tty_struct *tty, struct file * filp)
/*
* Returns the amount of free space in the output buffer.
*/
-static int
-tty3215_write_room(struct tty_struct *tty)
+static int tty3215_write_room(struct tty_struct *tty)
{
struct raw3215_info *raw;
@@ -944,9 +991,8 @@ tty3215_write_room(struct tty_struct *tty)
/*
* String write routine for 3215 ttys
*/
-static int
-tty3215_write(struct tty_struct * tty,
- const unsigned char *buf, int count)
+static int tty3215_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
{
struct raw3215_info *raw;
@@ -960,8 +1006,7 @@ tty3215_write(struct tty_struct * tty,
/*
* Put character routine for 3215 ttys
*/
-static int
-tty3215_put_char(struct tty_struct *tty, unsigned char ch)
+static int tty3215_put_char(struct tty_struct *tty, unsigned char ch)
{
struct raw3215_info *raw;
@@ -972,16 +1017,14 @@ tty3215_put_char(struct tty_struct *tty, unsigned char ch)
return 1;
}
-static void
-tty3215_flush_chars(struct tty_struct *tty)
+static void tty3215_flush_chars(struct tty_struct *tty)
{
}
/*
* Returns the number of characters in the output buffer
*/
-static int
-tty3215_chars_in_buffer(struct tty_struct *tty)
+static int tty3215_chars_in_buffer(struct tty_struct *tty)
{
struct raw3215_info *raw;
@@ -989,8 +1032,7 @@ tty3215_chars_in_buffer(struct tty_struct *tty)
return raw->count;
}
-static void
-tty3215_flush_buffer(struct tty_struct *tty)
+static void tty3215_flush_buffer(struct tty_struct *tty)
{
struct raw3215_info *raw;
@@ -1002,9 +1044,8 @@ tty3215_flush_buffer(struct tty_struct *tty)
/*
* Currently we don't have any io controls for 3215 ttys
*/
-static int
-tty3215_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
+static int tty3215_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
{
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
@@ -1019,8 +1060,7 @@ tty3215_ioctl(struct tty_struct *tty, struct file * file,
/*
* Disable reading from a 3215 tty
*/
-static void
-tty3215_throttle(struct tty_struct * tty)
+static void tty3215_throttle(struct tty_struct * tty)
{
struct raw3215_info *raw;
@@ -1031,8 +1071,7 @@ tty3215_throttle(struct tty_struct * tty)
/*
* Enable reading from a 3215 tty
*/
-static void
-tty3215_unthrottle(struct tty_struct * tty)
+static void tty3215_unthrottle(struct tty_struct * tty)
{
struct raw3215_info *raw;
unsigned long flags;
@@ -1049,8 +1088,7 @@ tty3215_unthrottle(struct tty_struct * tty)
/*
* Disable writing to a 3215 tty
*/
-static void
-tty3215_stop(struct tty_struct *tty)
+static void tty3215_stop(struct tty_struct *tty)
{
struct raw3215_info *raw;
@@ -1061,8 +1099,7 @@ tty3215_stop(struct tty_struct *tty)
/*
* Enable writing to a 3215 tty
*/
-static void
-tty3215_start(struct tty_struct *tty)
+static void tty3215_start(struct tty_struct *tty)
{
struct raw3215_info *raw;
unsigned long flags;
@@ -1096,8 +1133,7 @@ static const struct tty_operations tty3215_ops = {
* 3215 tty registration code called from tty_init().
* Most kernel services (incl. kmalloc) are available at this poimt.
*/
-static int __init
-tty3215_init(void)
+static int __init tty3215_init(void)
{
struct tty_driver *driver;
int ret;
@@ -1142,8 +1178,7 @@ tty3215_init(void)
return 0;
}
-static void __exit
-tty3215_exit(void)
+static void __exit tty3215_exit(void)
{
tty_unregister_driver(tty3215_driver);
put_tty_driver(tty3215_driver);
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index ed5396dae58..44d02e371c0 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -1,11 +1,10 @@
/*
- * drivers/s390/char/con3270.c
- * IBM/3270 Driver - console view.
+ * IBM/3270 Driver - console view.
*
- * Author(s):
- * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
- * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
- * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s):
+ * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 2003, 2009
*/
#include <linux/bootmem.h>
@@ -530,6 +529,7 @@ con3270_flush(void)
cp = condev;
if (!cp->view.dev)
return;
+ raw3270_pm_unfreeze(&cp->view);
spin_lock_irqsave(&cp->view.lock, flags);
con3270_wait_write(cp);
cp->nr_up = 0;
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index 40759c33477..097d3846a82 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -1,11 +1,10 @@
/*
- * drivers/s390/char/fs3270.c
- * IBM/3270 Driver - fullscreen driver.
+ * IBM/3270 Driver - fullscreen driver.
*
- * Author(s):
- * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
- * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com>
- * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s):
+ * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 2003, 2009
*/
#include <linux/bootmem.h>
@@ -399,6 +398,11 @@ fs3270_free_view(struct raw3270_view *view)
static void
fs3270_release(struct raw3270_view *view)
{
+ struct fs3270 *fp;
+
+ fp = (struct fs3270 *) view;
+ if (fp->fs_pid)
+ kill_pid(fp->fs_pid, SIGHUP, 1);
}
/* View to a 3270 device. Can be console, tty or fullscreen. */
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 97e63cf4694..75a8831eebb 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -1,10 +1,9 @@
/*
- * drivers/s390/char/monreader.c
- *
* Character device driver for reading z/VM *MONITOR service records.
*
- * Copyright IBM Corp. 2004, 2008
- * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
+ * Copyright IBM Corp. 2004, 2009
+ *
+ * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
#define KMSG_COMPONENT "monreader"
@@ -22,6 +21,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
+#include <linux/device.h>
#include <net/iucv/iucv.h>
#include <asm/uaccess.h>
#include <asm/ebcdic.h>
@@ -78,6 +78,7 @@ static u8 user_data_sever[16] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
+static struct device *monreader_device;
/******************************************************************************
* helper functions *
@@ -319,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp)
goto out_path;
}
filp->private_data = monpriv;
+ monreader_device->driver_data = monpriv;
unlock_kernel();
return nonseekable_open(inode, filp);
out_path:
- kfree(monpriv->path);
+ iucv_path_free(monpriv->path);
out_priv:
mon_free_mem(monpriv);
out_use:
@@ -341,10 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp)
/*
* Close IUCV connection and unregister
*/
- rc = iucv_path_sever(monpriv->path, user_data_sever);
- if (rc)
- pr_warning("Disconnecting the z/VM *MONITOR system service "
- "failed with rc=%i\n", rc);
+ if (monpriv->path) {
+ rc = iucv_path_sever(monpriv->path, user_data_sever);
+ if (rc)
+ pr_warning("Disconnecting the z/VM *MONITOR system "
+ "service failed with rc=%i\n", rc);
+ iucv_path_free(monpriv->path);
+ }
atomic_set(&monpriv->iucv_severed, 0);
atomic_set(&monpriv->iucv_connected, 0);
@@ -452,6 +457,94 @@ static struct miscdevice mon_dev = {
.minor = MISC_DYNAMIC_MINOR,
};
+
+/******************************************************************************
+ * suspend / resume *
+ *****************************************************************************/
+static int monreader_freeze(struct device *dev)
+{
+ struct mon_private *monpriv = dev->driver_data;
+ int rc;
+
+ if (!monpriv)
+ return 0;
+ if (monpriv->path) {
+ rc = iucv_path_sever(monpriv->path, user_data_sever);
+ if (rc)
+ pr_warning("Disconnecting the z/VM *MONITOR system "
+ "service failed with rc=%i\n", rc);
+ iucv_path_free(monpriv->path);
+ }
+ atomic_set(&monpriv->iucv_severed, 0);
+ atomic_set(&monpriv->iucv_connected, 0);
+ atomic_set(&monpriv->read_ready, 0);
+ atomic_set(&monpriv->msglim_count, 0);
+ monpriv->write_index = 0;
+ monpriv->read_index = 0;
+ monpriv->path = NULL;
+ return 0;
+}
+
+static int monreader_thaw(struct device *dev)
+{
+ struct mon_private *monpriv = dev->driver_data;
+ int rc;
+
+ if (!monpriv)
+ return 0;
+ rc = -ENOMEM;
+ monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL);
+ if (!monpriv->path)
+ goto out;
+ rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
+ MON_SERVICE, NULL, user_data_connect, monpriv);
+ if (rc) {
+ pr_err("Connecting to the z/VM *MONITOR system service "
+ "failed with rc=%i\n", rc);
+ goto out_path;
+ }
+ wait_event(mon_conn_wait_queue,
+ atomic_read(&monpriv->iucv_connected) ||
+ atomic_read(&monpriv->iucv_severed));
+ if (atomic_read(&monpriv->iucv_severed))
+ goto out_path;
+ return 0;
+out_path:
+ rc = -EIO;
+ iucv_path_free(monpriv->path);
+ monpriv->path = NULL;
+out:
+ atomic_set(&monpriv->iucv_severed, 1);
+ return rc;
+}
+
+static int monreader_restore(struct device *dev)
+{
+ int rc;
+
+ segment_unload(mon_dcss_name);
+ rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
+ &mon_dcss_start, &mon_dcss_end);
+ if (rc < 0) {
+ segment_warning(rc, mon_dcss_name);
+ panic("fatal monreader resume error: no monitor dcss\n");
+ }
+ return monreader_thaw(dev);
+}
+
+static struct dev_pm_ops monreader_pm_ops = {
+ .freeze = monreader_freeze,
+ .thaw = monreader_thaw,
+ .restore = monreader_restore,
+};
+
+static struct device_driver monreader_driver = {
+ .name = "monreader",
+ .bus = &iucv_bus,
+ .pm = &monreader_pm_ops,
+};
+
+
/******************************************************************************
* module init/exit *
*****************************************************************************/
@@ -475,16 +568,33 @@ static int __init mon_init(void)
return rc;
}
+ rc = driver_register(&monreader_driver);
+ if (rc)
+ goto out_iucv;
+ monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!monreader_device)
+ goto out_driver;
+ dev_set_name(monreader_device, "monreader-dev");
+ monreader_device->bus = &iucv_bus;
+ monreader_device->parent = iucv_root;
+ monreader_device->driver = &monreader_driver;
+ monreader_device->release = (void (*)(struct device *))kfree;
+ rc = device_register(monreader_device);
+ if (rc) {
+ kfree(monreader_device);
+ goto out_driver;
+ }
+
rc = segment_type(mon_dcss_name);
if (rc < 0) {
segment_warning(rc, mon_dcss_name);
- goto out_iucv;
+ goto out_device;
}
if (rc != SEG_TYPE_SC) {
pr_err("The specified *MONITOR DCSS %s does not have the "
"required type SC\n", mon_dcss_name);
rc = -EINVAL;
- goto out_iucv;
+ goto out_device;
}
rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
@@ -492,7 +602,7 @@ static int __init mon_init(void)
if (rc < 0) {
segment_warning(rc, mon_dcss_name);
rc = -EINVAL;
- goto out_iucv;
+ goto out_device;
}
dcss_mkname(mon_dcss_name, &user_data_connect[8]);
@@ -503,6 +613,10 @@ static int __init mon_init(void)
out:
segment_unload(mon_dcss_name);
+out_device:
+ device_unregister(monreader_device);
+out_driver:
+ driver_unregister(&monreader_driver);
out_iucv:
iucv_unregister(&monreader_iucv_handler, 1);
return rc;
@@ -512,6 +626,8 @@ static void __exit mon_exit(void)
{
segment_unload(mon_dcss_name);
WARN_ON(misc_deregister(&mon_dev) != 0);
+ device_unregister(monreader_device);
+ driver_unregister(&monreader_driver);
iucv_unregister(&monreader_iucv_handler, 1);
return;
}
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index c7d7483bab9..66fb8eba93f 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -1,9 +1,7 @@
/*
- * drivers/s390/char/monwriter.c
- *
* Character device driver for writing z/VM *MONITOR service records.
*
- * Copyright (C) IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2009
*
* Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
*/
@@ -22,6 +20,7 @@
#include <linux/ctype.h>
#include <linux/poll.h>
#include <linux/mutex.h>
+#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
@@ -40,7 +39,10 @@ struct mon_buf {
char *data;
};
+static LIST_HEAD(mon_priv_list);
+
struct mon_private {
+ struct list_head priv_list;
struct list_head list;
struct monwrite_hdr hdr;
size_t hdr_to_read;
@@ -188,6 +190,7 @@ static int monwrite_open(struct inode *inode, struct file *filp)
monpriv->hdr_to_read = sizeof(monpriv->hdr);
mutex_init(&monpriv->thread_mutex);
filp->private_data = monpriv;
+ list_add_tail(&monpriv->priv_list, &mon_priv_list);
unlock_kernel();
return nonseekable_open(inode, filp);
}
@@ -206,6 +209,7 @@ static int monwrite_close(struct inode *inode, struct file *filp)
kfree(entry->data);
kfree(entry);
}
+ list_del(&monpriv->priv_list);
kfree(monpriv);
return 0;
}
@@ -281,20 +285,102 @@ static struct miscdevice mon_dev = {
};
/*
+ * suspend/resume
+ */
+
+static int monwriter_freeze(struct device *dev)
+{
+ struct mon_private *monpriv;
+ struct mon_buf *monbuf;
+
+ list_for_each_entry(monpriv, &mon_priv_list, priv_list) {
+ list_for_each_entry(monbuf, &monpriv->list, list) {
+ if (monbuf->hdr.mon_function != MONWRITE_GEN_EVENT)
+ monwrite_diag(&monbuf->hdr, monbuf->data,
+ APPLDATA_STOP_REC);
+ }
+ }
+ return 0;
+}
+
+static int monwriter_restore(struct device *dev)
+{
+ struct mon_private *monpriv;
+ struct mon_buf *monbuf;
+
+ list_for_each_entry(monpriv, &mon_priv_list, priv_list) {
+ list_for_each_entry(monbuf, &monpriv->list, list) {
+ if (monbuf->hdr.mon_function == MONWRITE_START_INTERVAL)
+ monwrite_diag(&monbuf->hdr, monbuf->data,
+ APPLDATA_START_INTERVAL_REC);
+ if (monbuf->hdr.mon_function == MONWRITE_START_CONFIG)
+ monwrite_diag(&monbuf->hdr, monbuf->data,
+ APPLDATA_START_CONFIG_REC);
+ }
+ }
+ return 0;
+}
+
+static int monwriter_thaw(struct device *dev)
+{
+ return monwriter_restore(dev);
+}
+
+static struct dev_pm_ops monwriter_pm_ops = {
+ .freeze = monwriter_freeze,
+ .thaw = monwriter_thaw,
+ .restore = monwriter_restore,
+};
+
+static struct platform_driver monwriter_pdrv = {
+ .driver = {
+ .name = "monwriter",
+ .owner = THIS_MODULE,
+ .pm = &monwriter_pm_ops,
+ },
+};
+
+static struct platform_device *monwriter_pdev;
+
+/*
* module init/exit
*/
static int __init mon_init(void)
{
- if (MACHINE_IS_VM)
- return misc_register(&mon_dev);
- else
+ int rc;
+
+ if (!MACHINE_IS_VM)
return -ENODEV;
+
+ rc = platform_driver_register(&monwriter_pdrv);
+ if (rc)
+ return rc;
+
+ monwriter_pdev = platform_device_register_simple("monwriter", -1, NULL,
+ 0);
+ if (IS_ERR(monwriter_pdev)) {
+ rc = PTR_ERR(monwriter_pdev);
+ goto out_driver;
+ }
+
+ rc = misc_register(&mon_dev);
+ if (rc)
+ goto out_device;
+ return 0;
+
+out_device:
+ platform_device_unregister(monwriter_pdev);
+out_driver:
+ platform_driver_unregister(&monwriter_pdrv);
+ return rc;
}
static void __exit mon_exit(void)
{
WARN_ON(misc_deregister(&mon_dev) != 0);
+ platform_device_unregister(monwriter_pdev);
+ platform_driver_unregister(&monwriter_pdrv);
}
module_init(mon_init);
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index 0b15cf107ec..81c151b5f0a 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -1,11 +1,10 @@
/*
- * drivers/s390/char/raw3270.c
- * IBM/3270 Driver - core functions.
+ * IBM/3270 Driver - core functions.
*
- * Author(s):
- * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
- * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
- * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s):
+ * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 2003, 2009
*/
#include <linux/bootmem.h>
@@ -61,6 +60,7 @@ struct raw3270 {
#define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */
#define RAW3270_FLAGS_READY 4 /* Device is useable by views */
#define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */
+#define RAW3270_FLAGS_FROZEN 16 /* set if 3270 is frozen for suspend */
/* Semaphore to protect global data of raw3270 (devices, views, etc). */
static DEFINE_MUTEX(raw3270_mutex);
@@ -306,7 +306,8 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq)
spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
rp = view->dev;
- if (!rp || rp->view != view)
+ if (!rp || rp->view != view ||
+ test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
rc = -EACCES;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
@@ -323,7 +324,8 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq)
int rc;
rp = view->dev;
- if (!rp || rp->view != view)
+ if (!rp || rp->view != view ||
+ test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
rc = -EACCES;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
@@ -764,7 +766,8 @@ raw3270_reset(struct raw3270_view *view)
int rc;
rp = view->dev;
- if (!rp || rp->view != view)
+ if (!rp || rp->view != view ||
+ test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
rc = -EACCES;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
@@ -922,6 +925,8 @@ raw3270_activate_view(struct raw3270_view *view)
rc = 0;
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
+ else if (test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
+ rc = -EACCES;
else {
oldview = NULL;
if (rp->view) {
@@ -969,7 +974,8 @@ raw3270_deactivate_view(struct raw3270_view *view)
list_del_init(&view->list);
list_add_tail(&view->list, &rp->view_list);
/* Try to activate another view. */
- if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
+ if (test_bit(RAW3270_FLAGS_READY, &rp->flags) &&
+ !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) {
list_for_each_entry(view, &rp->view_list, list) {
rp->view = view;
if (view->fn->activate(view) == 0)
@@ -1068,7 +1074,8 @@ raw3270_del_view(struct raw3270_view *view)
rp->view = NULL;
}
list_del_init(&view->list);
- if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
+ if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags) &&
+ !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) {
/* Try to activate another view. */
list_for_each_entry(nv, &rp->view_list, list) {
if (nv->fn->activate(nv) == 0) {
@@ -1337,6 +1344,58 @@ raw3270_set_offline (struct ccw_device *cdev)
return 0;
}
+static int raw3270_pm_stop(struct ccw_device *cdev)
+{
+ struct raw3270 *rp;
+ struct raw3270_view *view;
+ unsigned long flags;
+
+ rp = cdev->dev.driver_data;
+ if (!rp)
+ return 0;
+ spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+ if (rp->view)
+ rp->view->fn->deactivate(rp->view);
+ if (!test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) {
+ /*
+ * Release tty and fullscreen for all non-console
+ * devices.
+ */
+ list_for_each_entry(view, &rp->view_list, list) {
+ if (view->fn->release)
+ view->fn->release(view);
+ }
+ }
+ set_bit(RAW3270_FLAGS_FROZEN, &rp->flags);
+ spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+ return 0;
+}
+
+static int raw3270_pm_start(struct ccw_device *cdev)
+{
+ struct raw3270 *rp;
+ unsigned long flags;
+
+ rp = cdev->dev.driver_data;
+ if (!rp)
+ return 0;
+ spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
+ clear_bit(RAW3270_FLAGS_FROZEN, &rp->flags);
+ if (rp->view)
+ rp->view->fn->activate(rp->view);
+ spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);
+ return 0;
+}
+
+void raw3270_pm_unfreeze(struct raw3270_view *view)
+{
+ struct raw3270 *rp;
+
+ rp = view->dev;
+ if (rp && test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))
+ ccw_device_force_console();
+}
+
static struct ccw_device_id raw3270_id[] = {
{ CCW_DEVICE(0x3270, 0) },
{ CCW_DEVICE(0x3271, 0) },
@@ -1360,6 +1419,9 @@ static struct ccw_driver raw3270_ccw_driver = {
.remove = &raw3270_remove,
.set_online = &raw3270_set_online,
.set_offline = &raw3270_set_offline,
+ .freeze = &raw3270_pm_stop,
+ .thaw = &raw3270_pm_start,
+ .restore = &raw3270_pm_start,
};
static int
diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h
index 90beaa80a78..ed34eb2199c 100644
--- a/drivers/s390/char/raw3270.h
+++ b/drivers/s390/char/raw3270.h
@@ -1,11 +1,10 @@
/*
- * drivers/s390/char/raw3270.h
- * IBM/3270 Driver
+ * IBM/3270 Driver
*
- * Author(s):
- * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
- * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
- * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s):
+ * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)
+ * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 2003, 2009
*/
#include <asm/idals.h>
@@ -195,6 +194,7 @@ void raw3270_wait_cons_dev(struct raw3270 *);
/* Notifier for device addition/removal */
int raw3270_register_notifier(void (*notifier)(int, int));
void raw3270_unregister_notifier(void (*notifier)(int, int));
+void raw3270_pm_unfreeze(struct raw3270_view *);
/*
* Little memory allocator for string objects.
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 4377e93a43d..a983f508678 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -1,11 +1,10 @@
/*
- * drivers/s390/char/sclp.c
- * core function to access sclp interface
+ * core function to access sclp interface
*
- * S390 version
- * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Martin Peschke <mpeschke@de.ibm.com>
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 1999, 2009
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/module.h>
@@ -16,6 +15,9 @@
#include <linux/reboot.h>
#include <linux/jiffies.h>
#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
#include <asm/types.h>
#include <asm/s390_ext.h>
@@ -47,6 +49,16 @@ static struct sclp_req sclp_init_req;
static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+/* Suspend request */
+static DECLARE_COMPLETION(sclp_request_queue_flushed);
+
+static void sclp_suspend_req_cb(struct sclp_req *req, void *data)
+{
+ complete(&sclp_request_queue_flushed);
+}
+
+static struct sclp_req sclp_suspend_req;
+
/* Timer for request retries. */
static struct timer_list sclp_request_timer;
@@ -84,6 +96,12 @@ static volatile enum sclp_mask_state_t {
sclp_mask_state_initializing
} sclp_mask_state = sclp_mask_state_idle;
+/* Internal state: is the driver suspended? */
+static enum sclp_suspend_state_t {
+ sclp_suspend_state_running,
+ sclp_suspend_state_suspended,
+} sclp_suspend_state = sclp_suspend_state_running;
+
/* Maximum retry counts */
#define SCLP_INIT_RETRY 3
#define SCLP_MASK_RETRY 3
@@ -211,6 +229,8 @@ sclp_process_queue(void)
del_timer(&sclp_request_timer);
while (!list_empty(&sclp_req_queue)) {
req = list_entry(sclp_req_queue.next, struct sclp_req, list);
+ if (!req->sccb)
+ goto do_post;
rc = __sclp_start_request(req);
if (rc == 0)
break;
@@ -222,6 +242,7 @@ sclp_process_queue(void)
sclp_request_timeout, 0);
break;
}
+do_post:
/* Post-processing for aborted request */
list_del(&req->list);
if (req->callback) {
@@ -233,6 +254,19 @@ sclp_process_queue(void)
spin_unlock_irqrestore(&sclp_lock, flags);
}
+static int __sclp_can_add_request(struct sclp_req *req)
+{
+ if (req == &sclp_suspend_req || req == &sclp_init_req)
+ return 1;
+ if (sclp_suspend_state != sclp_suspend_state_running)
+ return 0;
+ if (sclp_init_state != sclp_init_state_initialized)
+ return 0;
+ if (sclp_activation_state != sclp_activation_state_active)
+ return 0;
+ return 1;
+}
+
/* Queue a new request. Return zero on success, non-zero otherwise. */
int
sclp_add_request(struct sclp_req *req)
@@ -241,9 +275,7 @@ sclp_add_request(struct sclp_req *req)
int rc;
spin_lock_irqsave(&sclp_lock, flags);
- if ((sclp_init_state != sclp_init_state_initialized ||
- sclp_activation_state != sclp_activation_state_active) &&
- req != &sclp_init_req) {
+ if (!__sclp_can_add_request(req)) {
spin_unlock_irqrestore(&sclp_lock, flags);
return -EIO;
}
@@ -254,10 +286,16 @@ sclp_add_request(struct sclp_req *req)
/* Start if request is first in list */
if (sclp_running_state == sclp_running_state_idle &&
req->list.prev == &sclp_req_queue) {
+ if (!req->sccb) {
+ list_del(&req->list);
+ rc = -ENODATA;
+ goto out;
+ }
rc = __sclp_start_request(req);
if (rc)
list_del(&req->list);
}
+out:
spin_unlock_irqrestore(&sclp_lock, flags);
return rc;
}
@@ -560,6 +598,7 @@ sclp_register(struct sclp_register *reg)
/* Trigger initial state change callback */
reg->sclp_receive_mask = 0;
reg->sclp_send_mask = 0;
+ reg->pm_event_posted = 0;
list_add(&reg->list, &sclp_reg_list);
spin_unlock_irqrestore(&sclp_lock, flags);
rc = sclp_init_mask(1);
@@ -880,20 +919,134 @@ static struct notifier_block sclp_reboot_notifier = {
.notifier_call = sclp_reboot_event
};
+/*
+ * Suspend/resume SCLP notifier implementation
+ */
+
+static void sclp_pm_event(enum sclp_pm_event sclp_pm_event, int rollback)
+{
+ struct sclp_register *reg;
+ unsigned long flags;
+
+ if (!rollback) {
+ spin_lock_irqsave(&sclp_lock, flags);
+ list_for_each_entry(reg, &sclp_reg_list, list)
+ reg->pm_event_posted = 0;
+ spin_unlock_irqrestore(&sclp_lock, flags);
+ }
+ do {
+ spin_lock_irqsave(&sclp_lock, flags);
+ list_for_each_entry(reg, &sclp_reg_list, list) {
+ if (rollback && reg->pm_event_posted)
+ goto found;
+ if (!rollback && !reg->pm_event_posted)
+ goto found;
+ }
+ spin_unlock_irqrestore(&sclp_lock, flags);
+ return;
+found:
+ spin_unlock_irqrestore(&sclp_lock, flags);
+ if (reg->pm_event_fn)
+ reg->pm_event_fn(reg, sclp_pm_event);
+ reg->pm_event_posted = rollback ? 0 : 1;
+ } while (1);
+}
+
+/*
+ * Susend/resume callbacks for platform device
+ */
+
+static int sclp_freeze(struct device *dev)
+{
+ unsigned long flags;
+ int rc;
+
+ sclp_pm_event(SCLP_PM_EVENT_FREEZE, 0);
+
+ spin_lock_irqsave(&sclp_lock, flags);
+ sclp_suspend_state = sclp_suspend_state_suspended;
+ spin_unlock_irqrestore(&sclp_lock, flags);
+
+ /* Init supend data */
+ memset(&sclp_suspend_req, 0, sizeof(sclp_suspend_req));
+ sclp_suspend_req.callback = sclp_suspend_req_cb;
+ sclp_suspend_req.status = SCLP_REQ_FILLED;
+ init_completion(&sclp_request_queue_flushed);
+
+ rc = sclp_add_request(&sclp_suspend_req);
+ if (rc == 0)
+ wait_for_completion(&sclp_request_queue_flushed);
+ else if (rc != -ENODATA)
+ goto fail_thaw;
+
+ rc = sclp_deactivate();
+ if (rc)
+ goto fail_thaw;
+ return 0;
+
+fail_thaw:
+ spin_lock_irqsave(&sclp_lock, flags);
+ sclp_suspend_state = sclp_suspend_state_running;
+ spin_unlock_irqrestore(&sclp_lock, flags);
+ sclp_pm_event(SCLP_PM_EVENT_THAW, 1);
+ return rc;
+}
+
+static int sclp_undo_suspend(enum sclp_pm_event event)
+{
+ unsigned long flags;
+ int rc;
+
+ rc = sclp_reactivate();
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&sclp_lock, flags);
+ sclp_suspend_state = sclp_suspend_state_running;
+ spin_unlock_irqrestore(&sclp_lock, flags);
+
+ sclp_pm_event(event, 0);
+ return 0;
+}
+
+static int sclp_thaw(struct device *dev)
+{
+ return sclp_undo_suspend(SCLP_PM_EVENT_THAW);
+}
+
+static int sclp_restore(struct device *dev)
+{
+ return sclp_undo_suspend(SCLP_PM_EVENT_RESTORE);
+}
+
+static struct dev_pm_ops sclp_pm_ops = {
+ .freeze = sclp_freeze,
+ .thaw = sclp_thaw,
+ .restore = sclp_restore,
+};
+
+static struct platform_driver sclp_pdrv = {
+ .driver = {
+ .name = "sclp",
+ .owner = THIS_MODULE,
+ .pm = &sclp_pm_ops,
+ },
+};
+
+static struct platform_device *sclp_pdev;
+
/* Initialize SCLP driver. Return zero if driver is operational, non-zero
* otherwise. */
static int
sclp_init(void)
{
unsigned long flags;
- int rc;
+ int rc = 0;
spin_lock_irqsave(&sclp_lock, flags);
/* Check for previous or running initialization */
- if (sclp_init_state != sclp_init_state_uninitialized) {
- spin_unlock_irqrestore(&sclp_lock, flags);
- return 0;
- }
+ if (sclp_init_state != sclp_init_state_uninitialized)
+ goto fail_unlock;
sclp_init_state = sclp_init_state_initializing;
/* Set up variables */
INIT_LIST_HEAD(&sclp_req_queue);
@@ -904,27 +1057,17 @@ sclp_init(void)
spin_unlock_irqrestore(&sclp_lock, flags);
rc = sclp_check_interface();
spin_lock_irqsave(&sclp_lock, flags);
- if (rc) {
- sclp_init_state = sclp_init_state_uninitialized;
- spin_unlock_irqrestore(&sclp_lock, flags);
- return rc;
- }
+ if (rc)
+ goto fail_init_state_uninitialized;
/* Register reboot handler */
rc = register_reboot_notifier(&sclp_reboot_notifier);
- if (rc) {
- sclp_init_state = sclp_init_state_uninitialized;
- spin_unlock_irqrestore(&sclp_lock, flags);
- return rc;
- }
+ if (rc)
+ goto fail_init_state_uninitialized;
/* Register interrupt handler */
rc = register_early_external_interrupt(0x2401, sclp_interrupt_handler,
&ext_int_info_hwc);
- if (rc) {
- unregister_reboot_notifier(&sclp_reboot_notifier);
- sclp_init_state = sclp_init_state_uninitialized;
- spin_unlock_irqrestore(&sclp_lock, flags);
- return rc;
- }
+ if (rc)
+ goto fail_unregister_reboot_notifier;
sclp_init_state = sclp_init_state_initialized;
spin_unlock_irqrestore(&sclp_lock, flags);
/* Enable service-signal external interruption - needs to happen with
@@ -932,11 +1075,56 @@ sclp_init(void)
ctl_set_bit(0, 9);
sclp_init_mask(1);
return 0;
+
+fail_unregister_reboot_notifier:
+ unregister_reboot_notifier(&sclp_reboot_notifier);
+fail_init_state_uninitialized:
+ sclp_init_state = sclp_init_state_uninitialized;
+fail_unlock:
+ spin_unlock_irqrestore(&sclp_lock, flags);
+ return rc;
}
+/*
+ * SCLP panic notifier: If we are suspended, we thaw SCLP in order to be able
+ * to print the panic message.
+ */
+static int sclp_panic_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ if (sclp_suspend_state == sclp_suspend_state_suspended)
+ sclp_undo_suspend(SCLP_PM_EVENT_THAW);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block sclp_on_panic_nb = {
+ .notifier_call = sclp_panic_notify,
+ .priority = SCLP_PANIC_PRIO,
+};
+
static __init int sclp_initcall(void)
{
+ int rc;
+
+ rc = platform_driver_register(&sclp_pdrv);
+ if (rc)
+ return rc;
+ sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0);
+ rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0;
+ if (rc)
+ goto fail_platform_driver_unregister;
+ rc = atomic_notifier_chain_register(&panic_notifier_list,
+ &sclp_on_panic_nb);
+ if (rc)
+ goto fail_platform_device_unregister;
+
return sclp_init();
+
+fail_platform_device_unregister:
+ platform_device_unregister(sclp_pdev);
+fail_platform_driver_unregister:
+ platform_driver_unregister(&sclp_pdrv);
+ return rc;
}
arch_initcall(sclp_initcall);
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index bac80e856f9..60e7cb07095 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -1,10 +1,8 @@
/*
- * drivers/s390/char/sclp.h
+ * Copyright IBM Corp. 1999, 2009
*
- * S390 version
- * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Martin Peschke <mpeschke@de.ibm.com>
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef __SCLP_H__
@@ -17,7 +15,7 @@
/* maximum number of pages concerning our own memory management */
#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
-#define MAX_CONSOLE_PAGES 4
+#define MAX_CONSOLE_PAGES 6
#define EVTYP_OPCMD 0x01
#define EVTYP_MSG 0x02
@@ -68,6 +66,15 @@ typedef unsigned int sclp_cmdw_t;
#define GDS_KEY_SELFDEFTEXTMSG 0x31
+enum sclp_pm_event {
+ SCLP_PM_EVENT_FREEZE,
+ SCLP_PM_EVENT_THAW,
+ SCLP_PM_EVENT_RESTORE,
+};
+
+#define SCLP_PANIC_PRIO 1
+#define SCLP_PANIC_PRIO_CLIENT 0
+
typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */
struct sccb_header {
@@ -134,6 +141,10 @@ struct sclp_register {
void (*state_change_fn)(struct sclp_register *);
/* called for events in cp_receive_mask/sclp_receive_mask */
void (*receiver_fn)(struct evbuf_header *);
+ /* called for power management events */
+ void (*pm_event_fn)(struct sclp_register *, enum sclp_pm_event);
+ /* pm event posted flag */
+ int pm_event_posted;
};
/* externals from sclp.c */
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 77ab6e34a10..5cc11c636d3 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -1,9 +1,8 @@
/*
- * drivers/s390/char/sclp_cmd.c
+ * Copyright IBM Corp. 2007, 2009
*
- * Copyright IBM Corp. 2007
- * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
- * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
#define KMSG_COMPONENT "sclp_cmd"
@@ -12,11 +11,13 @@
#include <linux/completion.h>
#include <linux/init.h>
#include <linux/errno.h>
+#include <linux/err.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/mmzone.h>
#include <linux/memory.h>
+#include <linux/platform_device.h>
#include <asm/chpid.h>
#include <asm/sclp.h>
#include <asm/setup.h>
@@ -292,6 +293,7 @@ static DEFINE_MUTEX(sclp_mem_mutex);
static LIST_HEAD(sclp_mem_list);
static u8 sclp_max_storage_id;
static unsigned long sclp_storage_ids[256 / BITS_PER_LONG];
+static int sclp_mem_state_changed;
struct memory_increment {
struct list_head list;
@@ -450,6 +452,8 @@ static int sclp_mem_notifier(struct notifier_block *nb,
rc = -EINVAL;
break;
}
+ if (!rc)
+ sclp_mem_state_changed = 1;
mutex_unlock(&sclp_mem_mutex);
return rc ? NOTIFY_BAD : NOTIFY_OK;
}
@@ -525,6 +529,14 @@ static void __init insert_increment(u16 rn, int standby, int assigned)
list_add(&new_incr->list, prev);
}
+static int sclp_mem_freeze(struct device *dev)
+{
+ if (!sclp_mem_state_changed)
+ return 0;
+ pr_err("Memory hotplug state changed, suspend refused.\n");
+ return -EPERM;
+}
+
struct read_storage_sccb {
struct sccb_header header;
u16 max_id;
@@ -534,8 +546,20 @@ struct read_storage_sccb {
u32 entries[0];
} __packed;
+static struct dev_pm_ops sclp_mem_pm_ops = {
+ .freeze = sclp_mem_freeze,
+};
+
+static struct platform_driver sclp_mem_pdrv = {
+ .driver = {
+ .name = "sclp_mem",
+ .pm = &sclp_mem_pm_ops,
+ },
+};
+
static int __init sclp_detect_standby_memory(void)
{
+ struct platform_device *sclp_pdev;
struct read_storage_sccb *sccb;
int i, id, assigned, rc;
@@ -588,7 +612,17 @@ static int __init sclp_detect_standby_memory(void)
rc = register_memory_notifier(&sclp_mem_nb);
if (rc)
goto out;
+ rc = platform_driver_register(&sclp_mem_pdrv);
+ if (rc)
+ goto out;
+ sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0);
+ rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0;
+ if (rc)
+ goto out_driver;
sclp_add_standby_memory();
+ goto out;
+out_driver:
+ platform_driver_unregister(&sclp_mem_pdrv);
out:
free_page((unsigned long) sccb);
return rc;
diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c
index 9a25c4bd142..336811a7767 100644
--- a/drivers/s390/char/sclp_con.c
+++ b/drivers/s390/char/sclp_con.c
@@ -1,11 +1,9 @@
/*
- * drivers/s390/char/sclp_con.c
- * SCLP line mode console driver
+ * SCLP line mode console driver
*
- * S390 version
- * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Martin Peschke <mpeschke@de.ibm.com>
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 1999, 2009
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/kmod.h>
@@ -32,13 +30,14 @@ static spinlock_t sclp_con_lock;
static struct list_head sclp_con_pages;
/* List of full struct sclp_buffer structures ready for output */
static struct list_head sclp_con_outqueue;
-/* Counter how many buffers are emitted (max 1) and how many */
-/* are on the output queue. */
-static int sclp_con_buffer_count;
/* Pointer to current console buffer */
static struct sclp_buffer *sclp_conbuf;
/* Timer for delayed output of console messages */
static struct timer_list sclp_con_timer;
+/* Suspend mode flag */
+static int sclp_con_suspended;
+/* Flag that output queue is currently running */
+static int sclp_con_queue_running;
/* Output format for console messages */
static unsigned short sclp_con_columns;
@@ -53,42 +52,71 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
do {
page = sclp_unmake_buffer(buffer);
spin_lock_irqsave(&sclp_con_lock, flags);
+
/* Remove buffer from outqueue */
list_del(&buffer->list);
- sclp_con_buffer_count--;
list_add_tail((struct list_head *) page, &sclp_con_pages);
+
/* Check if there is a pending buffer on the out queue. */
buffer = NULL;
if (!list_empty(&sclp_con_outqueue))
- buffer = list_entry(sclp_con_outqueue.next,
- struct sclp_buffer, list);
+ buffer = list_first_entry(&sclp_con_outqueue,
+ struct sclp_buffer, list);
+ if (!buffer || sclp_con_suspended) {
+ sclp_con_queue_running = 0;
+ spin_unlock_irqrestore(&sclp_con_lock, flags);
+ break;
+ }
spin_unlock_irqrestore(&sclp_con_lock, flags);
- } while (buffer && sclp_emit_buffer(buffer, sclp_conbuf_callback));
+ } while (sclp_emit_buffer(buffer, sclp_conbuf_callback));
}
-static void
-sclp_conbuf_emit(void)
+/*
+ * Finalize and emit first pending buffer.
+ */
+static void sclp_conbuf_emit(void)
{
struct sclp_buffer* buffer;
unsigned long flags;
- int count;
int rc;
spin_lock_irqsave(&sclp_con_lock, flags);
- buffer = sclp_conbuf;
+ if (sclp_conbuf)
+ list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue);
sclp_conbuf = NULL;
- if (buffer == NULL) {
- spin_unlock_irqrestore(&sclp_con_lock, flags);
- return;
- }
- list_add_tail(&buffer->list, &sclp_con_outqueue);
- count = sclp_con_buffer_count++;
+ if (sclp_con_queue_running || sclp_con_suspended)
+ goto out_unlock;
+ if (list_empty(&sclp_con_outqueue))
+ goto out_unlock;
+ buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer,
+ list);
+ sclp_con_queue_running = 1;
spin_unlock_irqrestore(&sclp_con_lock, flags);
- if (count)
- return;
+
rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
if (rc)
sclp_conbuf_callback(buffer, rc);
+ return;
+out_unlock:
+ spin_unlock_irqrestore(&sclp_con_lock, flags);
+}
+
+/*
+ * Wait until out queue is empty
+ */
+static void sclp_console_sync_queue(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sclp_con_lock, flags);
+ if (timer_pending(&sclp_con_timer))
+ del_timer_sync(&sclp_con_timer);
+ while (sclp_con_queue_running) {
+ spin_unlock_irqrestore(&sclp_con_lock, flags);
+ sclp_sync_wait();
+ spin_lock_irqsave(&sclp_con_lock, flags);
+ }
+ spin_unlock_irqrestore(&sclp_con_lock, flags);
}
/*
@@ -123,6 +151,8 @@ sclp_console_write(struct console *console, const char *message,
/* make sure we have a console output buffer */
if (sclp_conbuf == NULL) {
while (list_empty(&sclp_con_pages)) {
+ if (sclp_con_suspended)
+ goto out;
spin_unlock_irqrestore(&sclp_con_lock, flags);
sclp_sync_wait();
spin_lock_irqsave(&sclp_con_lock, flags);
@@ -157,6 +187,7 @@ sclp_console_write(struct console *console, const char *message,
sclp_con_timer.expires = jiffies + HZ/10;
add_timer(&sclp_con_timer);
}
+out:
spin_unlock_irqrestore(&sclp_con_lock, flags);
}
@@ -168,30 +199,43 @@ sclp_console_device(struct console *c, int *index)
}
/*
- * This routine is called from panic when the kernel
- * is going to give up. We have to make sure that all buffers
- * will be flushed to the SCLP.
+ * Make sure that all buffers will be flushed to the SCLP.
*/
static void
sclp_console_flush(void)
{
+ sclp_conbuf_emit();
+ sclp_console_sync_queue();
+}
+
+/*
+ * Resume console: If there are cached messages, emit them.
+ */
+static void sclp_console_resume(void)
+{
unsigned long flags;
+ spin_lock_irqsave(&sclp_con_lock, flags);
+ sclp_con_suspended = 0;
+ spin_unlock_irqrestore(&sclp_con_lock, flags);
sclp_conbuf_emit();
+}
+
+/*
+ * Suspend console: Set suspend flag and flush console
+ */
+static void sclp_console_suspend(void)
+{
+ unsigned long flags;
+
spin_lock_irqsave(&sclp_con_lock, flags);
- if (timer_pending(&sclp_con_timer))
- del_timer(&sclp_con_timer);
- while (sclp_con_buffer_count > 0) {
- spin_unlock_irqrestore(&sclp_con_lock, flags);
- sclp_sync_wait();
- spin_lock_irqsave(&sclp_con_lock, flags);
- }
+ sclp_con_suspended = 1;
spin_unlock_irqrestore(&sclp_con_lock, flags);
+ sclp_console_flush();
}
-static int
-sclp_console_notify(struct notifier_block *self,
- unsigned long event, void *data)
+static int sclp_console_notify(struct notifier_block *self,
+ unsigned long event, void *data)
{
sclp_console_flush();
return NOTIFY_OK;
@@ -199,7 +243,7 @@ sclp_console_notify(struct notifier_block *self,
static struct notifier_block on_panic_nb = {
.notifier_call = sclp_console_notify,
- .priority = 1,
+ .priority = SCLP_PANIC_PRIO_CLIENT,
};
static struct notifier_block on_reboot_nb = {
@@ -221,6 +265,22 @@ static struct console sclp_console =
};
/*
+ * This function is called for SCLP suspend and resume events.
+ */
+void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event)
+{
+ switch (sclp_pm_event) {
+ case SCLP_PM_EVENT_FREEZE:
+ sclp_console_suspend();
+ break;
+ case SCLP_PM_EVENT_RESTORE:
+ case SCLP_PM_EVENT_THAW:
+ sclp_console_resume();
+ break;
+ }
+}
+
+/*
* called by console_init() in drivers/char/tty_io.c at boot-time.
*/
static int __init
@@ -243,7 +303,6 @@ sclp_console_init(void)
}
INIT_LIST_HEAD(&sclp_con_outqueue);
spin_lock_init(&sclp_con_lock);
- sclp_con_buffer_count = 0;
sclp_conbuf = NULL;
init_timer(&sclp_con_timer);
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 710af42603f..4be63be7344 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -1,11 +1,10 @@
/*
- * drivers/s390/char/sclp_rw.c
- * driver: reading from and writing to system console on S/390 via SCLP
+ * driver: reading from and writing to system console on S/390 via SCLP
*
- * S390 version
- * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Martin Peschke <mpeschke@de.ibm.com>
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 1999, 2009
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/kmod.h>
@@ -26,9 +25,16 @@
*/
#define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer))
+static void sclp_rw_pm_event(struct sclp_register *reg,
+ enum sclp_pm_event sclp_pm_event)
+{
+ sclp_console_pm_event(sclp_pm_event);
+}
+
/* Event type structure for write message and write priority message */
static struct sclp_register sclp_rw_event = {
- .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK
+ .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK,
+ .pm_event_fn = sclp_rw_pm_event,
};
/*
diff --git a/drivers/s390/char/sclp_rw.h b/drivers/s390/char/sclp_rw.h
index 6aa7a6948bc..85f491ea929 100644
--- a/drivers/s390/char/sclp_rw.h
+++ b/drivers/s390/char/sclp_rw.h
@@ -1,11 +1,10 @@
/*
- * drivers/s390/char/sclp_rw.h
- * interface to the SCLP-read/write driver
+ * interface to the SCLP-read/write driver
*
- * S390 version
- * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Martin Peschke <mpeschke@de.ibm.com>
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corporation 1999, 2009
+ *
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef __SCLP_RW_H__
@@ -93,4 +92,5 @@ void sclp_set_columns(struct sclp_buffer *, unsigned short);
void sclp_set_htab(struct sclp_buffer *, unsigned short);
int sclp_chars_in_buffer(struct sclp_buffer *);
+void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event);
#endif /* __SCLP_RW_H__ */
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index a839aa531d7..5518e24946a 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -1,10 +1,9 @@
/*
- * drivers/s390/char/sclp_vt220.c
- * SCLP VT220 terminal driver.
+ * SCLP VT220 terminal driver.
*
- * S390 version
- * Copyright IBM Corp. 2003,2008
- * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
+ * Copyright IBM Corp. 2003, 2009
+ *
+ * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
*/
#include <linux/module.h>
@@ -69,8 +68,11 @@ static struct list_head sclp_vt220_empty;
/* List of pending requests */
static struct list_head sclp_vt220_outqueue;
-/* Number of requests in outqueue */
-static int sclp_vt220_outqueue_count;
+/* Suspend mode flag */
+static int sclp_vt220_suspended;
+
+/* Flag that output queue is currently running */
+static int sclp_vt220_queue_running;
/* Timer used for delaying write requests to merge subsequent messages into
* a single buffer */
@@ -92,6 +94,8 @@ static int __initdata sclp_vt220_init_count;
static int sclp_vt220_flush_later;
static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf);
+static void sclp_vt220_pm_event_fn(struct sclp_register *reg,
+ enum sclp_pm_event sclp_pm_event);
static int __sclp_vt220_emit(struct sclp_vt220_request *request);
static void sclp_vt220_emit_current(void);
@@ -100,7 +104,8 @@ static struct sclp_register sclp_vt220_register = {
.send_mask = EVTYP_VT220MSG_MASK,
.receive_mask = EVTYP_VT220MSG_MASK,
.state_change_fn = NULL,
- .receiver_fn = sclp_vt220_receiver_fn
+ .receiver_fn = sclp_vt220_receiver_fn,
+ .pm_event_fn = sclp_vt220_pm_event_fn,
};
@@ -120,15 +125,19 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request)
spin_lock_irqsave(&sclp_vt220_lock, flags);
/* Move request from outqueue to empty queue */
list_del(&request->list);
- sclp_vt220_outqueue_count--;
list_add_tail((struct list_head *) page, &sclp_vt220_empty);
/* Check if there is a pending buffer on the out queue. */
request = NULL;
if (!list_empty(&sclp_vt220_outqueue))
request = list_entry(sclp_vt220_outqueue.next,
struct sclp_vt220_request, list);
+ if (!request || sclp_vt220_suspended) {
+ sclp_vt220_queue_running = 0;
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+ break;
+ }
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
- } while (request && __sclp_vt220_emit(request));
+ } while (__sclp_vt220_emit(request));
if (request == NULL && sclp_vt220_flush_later)
sclp_vt220_emit_current();
/* Check if the tty needs a wake up call */
@@ -212,26 +221,7 @@ __sclp_vt220_emit(struct sclp_vt220_request *request)
}
/*
- * Queue and emit given request.
- */
-static void
-sclp_vt220_emit(struct sclp_vt220_request *request)
-{
- unsigned long flags;
- int count;
-
- spin_lock_irqsave(&sclp_vt220_lock, flags);
- list_add_tail(&request->list, &sclp_vt220_outqueue);
- count = sclp_vt220_outqueue_count++;
- spin_unlock_irqrestore(&sclp_vt220_lock, flags);
- /* Emit only the first buffer immediately - callback takes care of
- * the rest */
- if (count == 0 && __sclp_vt220_emit(request))
- sclp_vt220_process_queue(request);
-}
-
-/*
- * Queue and emit current request. Return zero on success, non-zero otherwise.
+ * Queue and emit current request.
*/
static void
sclp_vt220_emit_current(void)
@@ -241,22 +231,33 @@ sclp_vt220_emit_current(void)
struct sclp_vt220_sccb *sccb;
spin_lock_irqsave(&sclp_vt220_lock, flags);
- request = NULL;
- if (sclp_vt220_current_request != NULL) {
+ if (sclp_vt220_current_request) {
sccb = (struct sclp_vt220_sccb *)
sclp_vt220_current_request->sclp_req.sccb;
/* Only emit buffers with content */
if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) {
- request = sclp_vt220_current_request;
+ list_add_tail(&sclp_vt220_current_request->list,
+ &sclp_vt220_outqueue);
sclp_vt220_current_request = NULL;
if (timer_pending(&sclp_vt220_timer))
del_timer(&sclp_vt220_timer);
}
sclp_vt220_flush_later = 0;
}
+ if (sclp_vt220_queue_running || sclp_vt220_suspended)
+ goto out_unlock;
+ if (list_empty(&sclp_vt220_outqueue))
+ goto out_unlock;
+ request = list_first_entry(&sclp_vt220_outqueue,
+ struct sclp_vt220_request, list);
+ sclp_vt220_queue_running = 1;
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+
+ if (__sclp_vt220_emit(request))
+ sclp_vt220_process_queue(request);
+ return;
+out_unlock:
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
- if (request != NULL)
- sclp_vt220_emit(request);
}
#define SCLP_NORMAL_WRITE 0x00
@@ -396,7 +397,7 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
if (sclp_vt220_current_request == NULL) {
while (list_empty(&sclp_vt220_empty)) {
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
- if (may_fail)
+ if (may_fail || sclp_vt220_suspended)
goto out;
else
sclp_sync_wait();
@@ -531,7 +532,7 @@ sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
static void
sclp_vt220_flush_chars(struct tty_struct *tty)
{
- if (sclp_vt220_outqueue_count == 0)
+ if (!sclp_vt220_queue_running)
sclp_vt220_emit_current();
else
sclp_vt220_flush_later = 1;
@@ -635,7 +636,6 @@ static int __init __sclp_vt220_init(int num_pages)
init_timer(&sclp_vt220_timer);
sclp_vt220_current_request = NULL;
sclp_vt220_buffered_chars = 0;
- sclp_vt220_outqueue_count = 0;
sclp_vt220_tty = NULL;
sclp_vt220_flush_later = 0;
@@ -736,7 +736,7 @@ static void __sclp_vt220_flush_buffer(void)
spin_lock_irqsave(&sclp_vt220_lock, flags);
if (timer_pending(&sclp_vt220_timer))
del_timer(&sclp_vt220_timer);
- while (sclp_vt220_outqueue_count > 0) {
+ while (sclp_vt220_queue_running) {
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
sclp_sync_wait();
spin_lock_irqsave(&sclp_vt220_lock, flags);
@@ -744,6 +744,46 @@ static void __sclp_vt220_flush_buffer(void)
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
}
+/*
+ * Resume console: If there are cached messages, emit them.
+ */
+static void sclp_vt220_resume(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sclp_vt220_lock, flags);
+ sclp_vt220_suspended = 0;
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+ sclp_vt220_emit_current();
+}
+
+/*
+ * Suspend console: Set suspend flag and flush console
+ */
+static void sclp_vt220_suspend(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sclp_vt220_lock, flags);
+ sclp_vt220_suspended = 1;
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+ __sclp_vt220_flush_buffer();
+}
+
+static void sclp_vt220_pm_event_fn(struct sclp_register *reg,
+ enum sclp_pm_event sclp_pm_event)
+{
+ switch (sclp_pm_event) {
+ case SCLP_PM_EVENT_FREEZE:
+ sclp_vt220_suspend();
+ break;
+ case SCLP_PM_EVENT_RESTORE:
+ case SCLP_PM_EVENT_THAW:
+ sclp_vt220_resume();
+ break;
+ }
+}
+
static int
sclp_vt220_notify(struct notifier_block *self,
unsigned long event, void *data)
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h
index 5469e099597..a2633377470 100644
--- a/drivers/s390/char/tape.h
+++ b/drivers/s390/char/tape.h
@@ -3,7 +3,7 @@
* tape device driver for 3480/3490E/3590 tapes.
*
* S390 and zSeries version
- * Copyright IBM Corp. 2001,2006
+ * Copyright IBM Corp. 2001, 2009
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Tuan Ngo-Anh <ngoanh@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
@@ -286,6 +286,7 @@ extern void tape_state_set(struct tape_device *, enum tape_state);
extern int tape_generic_online(struct tape_device *, struct tape_discipline *);
extern int tape_generic_offline(struct ccw_device *);
+extern int tape_generic_pm_suspend(struct ccw_device *);
/* Externals from tape_devmap.c */
extern int tape_generic_probe(struct ccw_device *);
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 2d00a383a47..144d2a5e1a9 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -2,7 +2,7 @@
* drivers/s390/char/tape_34xx.c
* tape device discipline for 3480/3490 tapes.
*
- * Copyright (C) IBM Corp. 2001,2006
+ * Copyright IBM Corp. 2001, 2009
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Tuan Ngo-Anh <ngoanh@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
@@ -1302,6 +1302,7 @@ static struct ccw_driver tape_34xx_driver = {
.remove = tape_generic_remove,
.set_online = tape_34xx_online,
.set_offline = tape_generic_offline,
+ .freeze = tape_generic_pm_suspend,
};
static int
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index c453b2f3e9f..23e6598bc4b 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -2,7 +2,7 @@
* drivers/s390/char/tape_3590.c
* tape device discipline for 3590 tapes.
*
- * Copyright IBM Corp. 2001,2006
+ * Copyright IBM Corp. 2001, 2009
* Author(s): Stefan Bader <shbader@de.ibm.com>
* Michael Holzheu <holzheu@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
@@ -1715,6 +1715,7 @@ static struct ccw_driver tape_3590_driver = {
.remove = tape_generic_remove,
.set_offline = tape_generic_offline,
.set_online = tape_3590_online,
+ .freeze = tape_generic_pm_suspend,
};
/*
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index 8a109f3b69c..3ebaa8eb5c8 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -3,7 +3,7 @@
* basic function of the tape device driver
*
* S390 and zSeries version
- * Copyright IBM Corp. 2001,2006
+ * Copyright IBM Corp. 2001, 2009
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Michael Holzheu <holzheu@de.ibm.com>
* Tuan Ngo-Anh <ngoanh@de.ibm.com>
@@ -380,6 +380,55 @@ tape_cleanup_device(struct tape_device *device)
}
/*
+ * Suspend device.
+ *
+ * Called by the common I/O layer if the drive should be suspended on user
+ * request. We refuse to suspend if the device is loaded or in use for the
+ * following reason:
+ * While the Linux guest is suspended, it might be logged off which causes
+ * devices to be detached. Tape devices are automatically rewound and unloaded
+ * during DETACH processing (unless the tape device was attached with the
+ * NOASSIGN or MULTIUSER option). After rewind/unload, there is no way to
+ * resume the original state of the tape device, since we would need to
+ * manually re-load the cartridge which was active at suspend time.
+ */
+int tape_generic_pm_suspend(struct ccw_device *cdev)
+{
+ struct tape_device *device;
+
+ device = cdev->dev.driver_data;
+ if (!device) {
+ return -ENODEV;
+ }
+
+ DBF_LH(3, "(%08x): tape_generic_pm_suspend(%p)\n",
+ device->cdev_id, device);
+
+ if (device->medium_state != MS_UNLOADED) {
+ pr_err("A cartridge is loaded in tape device %s, "
+ "refusing to suspend\n", dev_name(&cdev->dev));
+ return -EBUSY;
+ }
+
+ spin_lock_irq(get_ccwdev_lock(device->cdev));
+ switch (device->tape_state) {
+ case TS_INIT:
+ case TS_NOT_OPER:
+ case TS_UNUSED:
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ break;
+ default:
+ pr_err("Tape device %s is busy, refusing to "
+ "suspend\n", dev_name(&cdev->dev));
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ return -EBUSY;
+ }
+
+ DBF_LH(3, "(%08x): Drive suspended.\n", device->cdev_id);
+ return 0;
+}
+
+/*
* Set device offline.
*
* Called by the common I/O layer if the drive should set offline on user
@@ -1273,6 +1322,7 @@ EXPORT_SYMBOL(tape_generic_remove);
EXPORT_SYMBOL(tape_generic_probe);
EXPORT_SYMBOL(tape_generic_online);
EXPORT_SYMBOL(tape_generic_offline);
+EXPORT_SYMBOL(tape_generic_pm_suspend);
EXPORT_SYMBOL(tape_put_device);
EXPORT_SYMBOL(tape_get_device_reference);
EXPORT_SYMBOL(tape_state_verbose);
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index d8a2289fcb6..e925808c214 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -3,7 +3,7 @@
* character device driver for reading z/VM system service records
*
*
- * Copyright 2004 IBM Corporation
+ * Copyright IBM Corp. 2004, 2009
* character device driver for reading z/VM system service records,
* Version 1.0
* Author(s): Xenia Tkatschow <xenia@us.ibm.com>
@@ -660,6 +660,29 @@ static struct attribute *vmlogrdr_attrs[] = {
NULL,
};
+static int vmlogrdr_pm_prepare(struct device *dev)
+{
+ int rc;
+ struct vmlogrdr_priv_t *priv = dev->driver_data;
+
+ rc = 0;
+ if (priv) {
+ spin_lock_bh(&priv->priv_lock);
+ if (priv->dev_in_use)
+ rc = -EBUSY;
+ spin_unlock_bh(&priv->priv_lock);
+ }
+ if (rc)
+ pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n",
+ dev_name(dev));
+ return rc;
+}
+
+
+static struct dev_pm_ops vmlogrdr_pm_ops = {
+ .prepare = vmlogrdr_pm_prepare,
+};
+
static struct attribute_group vmlogrdr_attr_group = {
.attrs = vmlogrdr_attrs,
};
@@ -668,6 +691,7 @@ static struct class *vmlogrdr_class;
static struct device_driver vmlogrdr_driver = {
.name = "vmlogrdr",
.bus = &iucv_bus,
+ .pm = &vmlogrdr_pm_ops,
};
@@ -729,6 +753,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
dev->bus = &iucv_bus;
dev->parent = iucv_root;
dev->driver = &vmlogrdr_driver;
+ dev->driver_data = priv;
/*
* The release function could be called after the
* module has been unloaded. It's _only_ task is to
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 5dcef81fc9d..92458219a9e 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -2,7 +2,7 @@
* Linux driver for System z and s390 unit record devices
* (z/VM virtual punch, reader, printer)
*
- * Copyright IBM Corp. 2001, 2007
+ * Copyright IBM Corp. 2001, 2009
* Authors: Malcolm Beattie <beattiem@uk.ibm.com>
* Michael Holzheu <holzheu@de.ibm.com>
* Frank Munzert <munzert@de.ibm.com>
@@ -60,6 +60,7 @@ static int ur_probe(struct ccw_device *cdev);
static void ur_remove(struct ccw_device *cdev);
static int ur_set_online(struct ccw_device *cdev);
static int ur_set_offline(struct ccw_device *cdev);
+static int ur_pm_suspend(struct ccw_device *cdev);
static struct ccw_driver ur_driver = {
.name = "vmur",
@@ -69,6 +70,7 @@ static struct ccw_driver ur_driver = {
.remove = ur_remove,
.set_online = ur_set_online,
.set_offline = ur_set_offline,
+ .freeze = ur_pm_suspend,
};
static DEFINE_MUTEX(vmur_mutex);
@@ -158,6 +160,28 @@ static void urdev_put(struct urdev *urd)
}
/*
+ * State and contents of ur devices can be changed by class D users issuing
+ * CP commands such as PURGE or TRANSFER, while the Linux guest is suspended.
+ * Also the Linux guest might be logged off, which causes all active spool
+ * files to be closed.
+ * So we cannot guarantee that spool files are still the same when the Linux
+ * guest is resumed. In order to avoid unpredictable results at resume time
+ * we simply refuse to suspend if a ur device node is open.
+ */
+static int ur_pm_suspend(struct ccw_device *cdev)
+{
+ struct urdev *urd = cdev->dev.driver_data;
+
+ TRACE("ur_pm_suspend: cdev=%p\n", cdev);
+ if (urd->open_flag) {
+ pr_err("Unit record device %s is busy, %s refusing to "
+ "suspend.\n", dev_name(&cdev->dev), ur_banner);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/*
* Low-level functions to do I/O to a ur device.
* alloc_chan_prog
* free_chan_prog
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index 21a2a829bf4..cb7854c10c0 100644
--- a/drivers/s390/char/vmwatchdog.c
+++ b/drivers/s390/char/vmwatchdog.c
@@ -1,17 +1,23 @@
/*
* Watchdog implementation based on z/VM Watchdog Timer API
*
+ * Copyright IBM Corp. 2004,2009
+ *
* The user space watchdog daemon can use this driver as
* /dev/vmwatchdog to have z/VM execute the specified CP
* command when the timeout expires. The default command is
* "IPL", which which cause an immediate reboot.
*/
+#define KMSG_COMPONENT "vmwatchdog"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/suspend.h>
#include <linux/watchdog.h>
#include <linux/smp_lock.h>
@@ -43,6 +49,9 @@ static unsigned int vmwdt_interval = 60;
static unsigned long vmwdt_is_open;
static int vmwdt_expect_close;
+#define VMWDT_OPEN 0 /* devnode is open or suspend in progress */
+#define VMWDT_RUNNING 1 /* The watchdog is armed */
+
enum vmwdt_func {
/* function codes */
wdt_init = 0,
@@ -92,6 +101,7 @@ static int vmwdt_keepalive(void)
EBC_TOUPPER(ebc_cmd, MAX_CMDLEN);
func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init;
+ set_bit(VMWDT_RUNNING, &vmwdt_is_open);
ret = __diag288(func, vmwdt_interval, ebc_cmd, len);
WARN_ON(ret != 0);
kfree(ebc_cmd);
@@ -102,6 +112,7 @@ static int vmwdt_disable(void)
{
int ret = __diag288(wdt_cancel, 0, "", 0);
WARN_ON(ret != 0);
+ clear_bit(VMWDT_RUNNING, &vmwdt_is_open);
return ret;
}
@@ -123,13 +134,13 @@ static int vmwdt_open(struct inode *i, struct file *f)
{
int ret;
lock_kernel();
- if (test_and_set_bit(0, &vmwdt_is_open)) {
+ if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) {
unlock_kernel();
return -EBUSY;
}
ret = vmwdt_keepalive();
if (ret)
- clear_bit(0, &vmwdt_is_open);
+ clear_bit(VMWDT_OPEN, &vmwdt_is_open);
unlock_kernel();
return ret ? ret : nonseekable_open(i, f);
}
@@ -139,7 +150,7 @@ static int vmwdt_close(struct inode *i, struct file *f)
if (vmwdt_expect_close == 42)
vmwdt_disable();
vmwdt_expect_close = 0;
- clear_bit(0, &vmwdt_is_open);
+ clear_bit(VMWDT_OPEN, &vmwdt_is_open);
return 0;
}
@@ -223,6 +234,57 @@ static ssize_t vmwdt_write(struct file *f, const char __user *buf,
return count;
}
+static int vmwdt_resume(void)
+{
+ clear_bit(VMWDT_OPEN, &vmwdt_is_open);
+ return NOTIFY_DONE;
+}
+
+/*
+ * It makes no sense to go into suspend while the watchdog is running.
+ * Depending on the memory size, the watchdog might trigger, while we
+ * are still saving the memory.
+ * We reuse the open flag to ensure that suspend and watchdog open are
+ * exclusive operations
+ */
+static int vmwdt_suspend(void)
+{
+ if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) {
+ pr_err("The watchdog is in use. "
+ "This prevents hibernation or suspend.\n");
+ return NOTIFY_BAD;
+ }
+ if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) {
+ clear_bit(VMWDT_OPEN, &vmwdt_is_open);
+ pr_err("The watchdog is running. "
+ "This prevents hibernation or suspend.\n");
+ return NOTIFY_BAD;
+ }
+ return NOTIFY_DONE;
+}
+
+/*
+ * This function is called for suspend and resume.
+ */
+static int vmwdt_power_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ return vmwdt_resume();
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ return vmwdt_suspend();
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static struct notifier_block vmwdt_power_notifier = {
+ .notifier_call = vmwdt_power_event,
+};
+
static const struct file_operations vmwdt_fops = {
.open = &vmwdt_open,
.release = &vmwdt_close,
@@ -244,12 +306,21 @@ static int __init vmwdt_init(void)
ret = vmwdt_probe();
if (ret)
return ret;
- return misc_register(&vmwdt_dev);
+ ret = register_pm_notifier(&vmwdt_power_notifier);
+ if (ret)
+ return ret;
+ ret = misc_register(&vmwdt_dev);
+ if (ret) {
+ unregister_pm_notifier(&vmwdt_power_notifier);
+ return ret;
+ }
+ return 0;
}
module_init(vmwdt_init);
static void __exit vmwdt_exit(void)
{
- WARN_ON(misc_deregister(&vmwdt_dev) != 0);
+ unregister_pm_notifier(&vmwdt_power_notifier);
+ misc_deregister(&vmwdt_dev);
}
module_exit(vmwdt_exit);
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 22ce765d537..a5a62f1f774 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -1,11 +1,10 @@
/*
- * drivers/s390/cio/ccwgroup.c
* bus driver for ccwgroup
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Arnd Bergmann (arndb@de.ibm.com)
- * Cornelia Huck (cornelia.huck@de.ibm.com)
+ * Copyright IBM Corp. 2002, 2009
+ *
+ * Author(s): Arnd Bergmann (arndb@de.ibm.com)
+ * Cornelia Huck (cornelia.huck@de.ibm.com)
*/
#include <linux/module.h>
#include <linux/errno.h>
@@ -501,6 +500,74 @@ static void ccwgroup_shutdown(struct device *dev)
gdrv->shutdown(gdev);
}
+static int ccwgroup_pm_prepare(struct device *dev)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
+
+ /* Fail while device is being set online/offline. */
+ if (atomic_read(&gdev->onoff))
+ return -EAGAIN;
+
+ if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
+ return 0;
+
+ return gdrv->prepare ? gdrv->prepare(gdev) : 0;
+}
+
+static void ccwgroup_pm_complete(struct device *dev)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
+
+ if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
+ return;
+
+ if (gdrv->complete)
+ gdrv->complete(gdev);
+}
+
+static int ccwgroup_pm_freeze(struct device *dev)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
+
+ if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
+ return 0;
+
+ return gdrv->freeze ? gdrv->freeze(gdev) : 0;
+}
+
+static int ccwgroup_pm_thaw(struct device *dev)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
+
+ if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
+ return 0;
+
+ return gdrv->thaw ? gdrv->thaw(gdev) : 0;
+}
+
+static int ccwgroup_pm_restore(struct device *dev)
+{
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
+
+ if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
+ return 0;
+
+ return gdrv->restore ? gdrv->restore(gdev) : 0;
+}
+
+static struct dev_pm_ops ccwgroup_pm_ops = {
+ .prepare = ccwgroup_pm_prepare,
+ .complete = ccwgroup_pm_complete,
+ .freeze = ccwgroup_pm_freeze,
+ .thaw = ccwgroup_pm_thaw,
+ .restore = ccwgroup_pm_restore,
+};
+
static struct bus_type ccwgroup_bus_type = {
.name = "ccwgroup",
.match = ccwgroup_bus_match,
@@ -508,6 +575,7 @@ static struct bus_type ccwgroup_bus_type = {
.probe = ccwgroup_probe,
.remove = ccwgroup_remove,
.shutdown = ccwgroup_shutdown,
+ .pm = &ccwgroup_pm_ops,
};
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 883f16f96f2..1ecd3e56764 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -549,8 +549,7 @@ cleanup:
return ret;
}
-static int
-__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
+int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
{
struct {
struct chsc_header request;
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index ba59bceace9..425e8f89a6c 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -90,6 +90,7 @@ extern void chsc_free_sei_area(void);
extern int chsc_enable_facility(int);
struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
+int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page);
int chsc_chp_vary(struct chp_id chpid, int on);
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index 93eca1731b8..cc5144b6f9d 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -1,7 +1,8 @@
/*
* Driver for s390 chsc subchannels
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008, 2009
+ *
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*
*/
@@ -112,6 +113,31 @@ static void chsc_subchannel_shutdown(struct subchannel *sch)
cio_disable_subchannel(sch);
}
+static int chsc_subchannel_prepare(struct subchannel *sch)
+{
+ int cc;
+ struct schib schib;
+ /*
+ * Don't allow suspend while the subchannel is not idle
+ * since we don't have a way to clear the subchannel and
+ * cannot disable it with a request running.
+ */
+ cc = stsch(sch->schid, &schib);
+ if (!cc && scsw_stctl(&schib.scsw))
+ return -EAGAIN;
+ return 0;
+}
+
+static int chsc_subchannel_freeze(struct subchannel *sch)
+{
+ return cio_disable_subchannel(sch);
+}
+
+static int chsc_subchannel_restore(struct subchannel *sch)
+{
+ return cio_enable_subchannel(sch, (u32)(unsigned long)sch);
+}
+
static struct css_device_id chsc_subchannel_ids[] = {
{ .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, },
{ /* end of list */ },
@@ -125,6 +151,10 @@ static struct css_driver chsc_subchannel_driver = {
.probe = chsc_subchannel_probe,
.remove = chsc_subchannel_remove,
.shutdown = chsc_subchannel_shutdown,
+ .prepare = chsc_subchannel_prepare,
+ .freeze = chsc_subchannel_freeze,
+ .thaw = chsc_subchannel_restore,
+ .restore = chsc_subchannel_restore,
.name = "chsc_subchannel",
};
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index dc98b2c6386..30f51611130 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -1204,6 +1204,11 @@ static ssize_t cmb_enable_store(struct device *dev,
DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store);
+int ccw_set_cmf(struct ccw_device *cdev, int enable)
+{
+ return cmbops->set(cdev, enable ? 2 : 0);
+}
+
/**
* enable_cmf() - switch on the channel measurement for a specific device
* @cdev: The ccw device to be enabled
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 0085d890179..85d43c6bcb6 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -1,10 +1,10 @@
/*
- * drivers/s390/cio/css.c
- * driver for channel subsystem
+ * driver for channel subsystem
*
- * Copyright IBM Corp. 2002,2008
- * Author(s): Arnd Bergmann (arndb@de.ibm.com)
- * Cornelia Huck (cornelia.huck@de.ibm.com)
+ * Copyright IBM Corp. 2002, 2009
+ *
+ * Author(s): Arnd Bergmann (arndb@de.ibm.com)
+ * Cornelia Huck (cornelia.huck@de.ibm.com)
*/
#define KMSG_COMPONENT "cio"
@@ -17,6 +17,7 @@
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/reboot.h>
+#include <linux/suspend.h>
#include <asm/isc.h>
#include <asm/crw.h>
@@ -780,6 +781,79 @@ static struct notifier_block css_reboot_notifier = {
};
/*
+ * Since the css devices are neither on a bus nor have a class
+ * nor have a special device type, we cannot stop/restart channel
+ * path measurements via the normal suspend/resume callbacks, but have
+ * to use notifiers.
+ */
+static int css_power_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ void *secm_area;
+ int ret, i;
+
+ switch (event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ ret = NOTIFY_DONE;
+ for (i = 0; i <= __MAX_CSSID; i++) {
+ struct channel_subsystem *css;
+
+ css = channel_subsystems[i];
+ mutex_lock(&css->mutex);
+ if (!css->cm_enabled) {
+ mutex_unlock(&css->mutex);
+ continue;
+ }
+ secm_area = (void *)get_zeroed_page(GFP_KERNEL |
+ GFP_DMA);
+ if (secm_area) {
+ if (__chsc_do_secm(css, 0, secm_area))
+ ret = NOTIFY_BAD;
+ free_page((unsigned long)secm_area);
+ } else
+ ret = NOTIFY_BAD;
+
+ mutex_unlock(&css->mutex);
+ }
+ break;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ ret = NOTIFY_DONE;
+ for (i = 0; i <= __MAX_CSSID; i++) {
+ struct channel_subsystem *css;
+
+ css = channel_subsystems[i];
+ mutex_lock(&css->mutex);
+ if (!css->cm_enabled) {
+ mutex_unlock(&css->mutex);
+ continue;
+ }
+ secm_area = (void *)get_zeroed_page(GFP_KERNEL |
+ GFP_DMA);
+ if (secm_area) {
+ if (__chsc_do_secm(css, 1, secm_area))
+ ret = NOTIFY_BAD;
+ free_page((unsigned long)secm_area);
+ } else
+ ret = NOTIFY_BAD;
+
+ mutex_unlock(&css->mutex);
+ }
+ /* search for subchannels, which appeared during hibernation */
+ css_schedule_reprobe();
+ break;
+ default:
+ ret = NOTIFY_DONE;
+ }
+ return ret;
+
+}
+static struct notifier_block css_power_notifier = {
+ .notifier_call = css_power_event,
+};
+
+/*
* Now that the driver core is running, we can setup our channel subsystem.
* The struct subchannel's are created during probing (except for the
* static console subchannel).
@@ -852,6 +926,11 @@ init_channel_subsystem (void)
ret = register_reboot_notifier(&css_reboot_notifier);
if (ret)
goto out_unregister;
+ ret = register_pm_notifier(&css_power_notifier);
+ if (ret) {
+ unregister_reboot_notifier(&css_reboot_notifier);
+ goto out_unregister;
+ }
css_init_done = 1;
/* Enable default isc for I/O subchannels. */
@@ -953,6 +1032,73 @@ static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
return ret;
}
+static int css_pm_prepare(struct device *dev)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ struct css_driver *drv;
+
+ if (mutex_is_locked(&sch->reg_mutex))
+ return -EAGAIN;
+ if (!sch->dev.driver)
+ return 0;
+ drv = to_cssdriver(sch->dev.driver);
+ /* Notify drivers that they may not register children. */
+ return drv->prepare ? drv->prepare(sch) : 0;
+}
+
+static void css_pm_complete(struct device *dev)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ struct css_driver *drv;
+
+ if (!sch->dev.driver)
+ return;
+ drv = to_cssdriver(sch->dev.driver);
+ if (drv->complete)
+ drv->complete(sch);
+}
+
+static int css_pm_freeze(struct device *dev)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ struct css_driver *drv;
+
+ if (!sch->dev.driver)
+ return 0;
+ drv = to_cssdriver(sch->dev.driver);
+ return drv->freeze ? drv->freeze(sch) : 0;
+}
+
+static int css_pm_thaw(struct device *dev)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ struct css_driver *drv;
+
+ if (!sch->dev.driver)
+ return 0;
+ drv = to_cssdriver(sch->dev.driver);
+ return drv->thaw ? drv->thaw(sch) : 0;
+}
+
+static int css_pm_restore(struct device *dev)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ struct css_driver *drv;
+
+ if (!sch->dev.driver)
+ return 0;
+ drv = to_cssdriver(sch->dev.driver);
+ return drv->restore ? drv->restore(sch) : 0;
+}
+
+static struct dev_pm_ops css_pm_ops = {
+ .prepare = css_pm_prepare,
+ .complete = css_pm_complete,
+ .freeze = css_pm_freeze,
+ .thaw = css_pm_thaw,
+ .restore = css_pm_restore,
+};
+
struct bus_type css_bus_type = {
.name = "css",
.match = css_bus_match,
@@ -960,6 +1106,7 @@ struct bus_type css_bus_type = {
.remove = css_remove,
.shutdown = css_shutdown,
.uevent = css_uevent,
+ .pm = &css_pm_ops,
};
/**
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 57ebf120f82..9763eeec745 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -70,6 +70,11 @@ struct chp_link;
* @probe: function called on probe
* @remove: function called on remove
* @shutdown: called at device shutdown
+ * @prepare: prepare for pm state transition
+ * @complete: undo work done in @prepare
+ * @freeze: callback for freezing during hibernation snapshotting
+ * @thaw: undo work done in @freeze
+ * @restore: callback for restoring after hibernation
* @name: name of the device driver
*/
struct css_driver {
@@ -82,6 +87,11 @@ struct css_driver {
int (*probe)(struct subchannel *);
int (*remove)(struct subchannel *);
void (*shutdown)(struct subchannel *);
+ int (*prepare) (struct subchannel *);
+ void (*complete) (struct subchannel *);
+ int (*freeze)(struct subchannel *);
+ int (*thaw) (struct subchannel *);
+ int (*restore)(struct subchannel *);
const char *name;
};
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 35441fa16be..3c57c1a18bb 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -138,6 +138,19 @@ static struct css_device_id io_subchannel_ids[] = {
};
MODULE_DEVICE_TABLE(css, io_subchannel_ids);
+static int io_subchannel_prepare(struct subchannel *sch)
+{
+ struct ccw_device *cdev;
+ /*
+ * Don't allow suspend while a ccw device registration
+ * is still outstanding.
+ */
+ cdev = sch_get_cdev(sch);
+ if (cdev && !device_is_registered(&cdev->dev))
+ return -EAGAIN;
+ return 0;
+}
+
static struct css_driver io_subchannel_driver = {
.owner = THIS_MODULE,
.subchannel_type = io_subchannel_ids,
@@ -148,6 +161,7 @@ static struct css_driver io_subchannel_driver = {
.probe = io_subchannel_probe,
.remove = io_subchannel_remove,
.shutdown = io_subchannel_shutdown,
+ .prepare = io_subchannel_prepare,
};
struct workqueue_struct *ccw_device_work;
@@ -1775,6 +1789,15 @@ ccw_device_probe_console(void)
return &console_cdev;
}
+static int ccw_device_pm_restore(struct device *dev);
+
+int ccw_device_force_console(void)
+{
+ if (!console_cdev_in_use)
+ return -ENODEV;
+ return ccw_device_pm_restore(&console_cdev.dev);
+}
+EXPORT_SYMBOL_GPL(ccw_device_force_console);
const char *cio_get_console_cdev_name(struct subchannel *sch)
{
@@ -1895,6 +1918,242 @@ static void ccw_device_shutdown(struct device *dev)
disable_cmf(cdev);
}
+static int ccw_device_pm_prepare(struct device *dev)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+
+ if (work_pending(&cdev->private->kick_work))
+ return -EAGAIN;
+ /* Fail while device is being set online/offline. */
+ if (atomic_read(&cdev->private->onoff))
+ return -EAGAIN;
+
+ if (cdev->online && cdev->drv && cdev->drv->prepare)
+ return cdev->drv->prepare(cdev);
+
+ return 0;
+}
+
+static void ccw_device_pm_complete(struct device *dev)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+
+ if (cdev->online && cdev->drv && cdev->drv->complete)
+ cdev->drv->complete(cdev);
+}
+
+static int ccw_device_pm_freeze(struct device *dev)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int ret, cm_enabled;
+
+ /* Fail suspend while device is in transistional state. */
+ if (!dev_fsm_final_state(cdev))
+ return -EAGAIN;
+ if (!cdev->online)
+ return 0;
+ if (cdev->drv && cdev->drv->freeze) {
+ ret = cdev->drv->freeze(cdev);
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irq(sch->lock);
+ cm_enabled = cdev->private->cmb != NULL;
+ spin_unlock_irq(sch->lock);
+ if (cm_enabled) {
+ /* Don't have the css write on memory. */
+ ret = ccw_set_cmf(cdev, 0);
+ if (ret)
+ return ret;
+ }
+ /* From here on, disallow device driver I/O. */
+ spin_lock_irq(sch->lock);
+ ret = cio_disable_subchannel(sch);
+ spin_unlock_irq(sch->lock);
+
+ return ret;
+}
+
+static int ccw_device_pm_thaw(struct device *dev)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int ret, cm_enabled;
+
+ if (!cdev->online)
+ return 0;
+
+ spin_lock_irq(sch->lock);
+ /* Allow device driver I/O again. */
+ ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
+ cm_enabled = cdev->private->cmb != NULL;
+ spin_unlock_irq(sch->lock);
+ if (ret)
+ return ret;
+
+ if (cm_enabled) {
+ ret = ccw_set_cmf(cdev, 1);
+ if (ret)
+ return ret;
+ }
+
+ if (cdev->drv && cdev->drv->thaw)
+ ret = cdev->drv->thaw(cdev);
+
+ return ret;
+}
+
+static void __ccw_device_pm_restore(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int ret;
+
+ if (cio_is_console(sch->schid))
+ goto out;
+ /*
+ * While we were sleeping, devices may have gone or become
+ * available again. Kick re-detection.
+ */
+ spin_lock_irq(sch->lock);
+ cdev->private->flags.resuming = 1;
+ ret = ccw_device_recognition(cdev);
+ spin_unlock_irq(sch->lock);
+ if (ret) {
+ CIO_MSG_EVENT(0, "Couldn't start recognition for device "
+ "%s (ret=%d)\n", dev_name(&cdev->dev), ret);
+ spin_lock_irq(sch->lock);
+ cdev->private->state = DEV_STATE_DISCONNECTED;
+ spin_unlock_irq(sch->lock);
+ /* notify driver after the resume cb */
+ goto out;
+ }
+ wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) ||
+ cdev->private->state == DEV_STATE_DISCONNECTED);
+
+out:
+ cdev->private->flags.resuming = 0;
+}
+
+static int resume_handle_boxed(struct ccw_device *cdev)
+{
+ cdev->private->state = DEV_STATE_BOXED;
+ if (ccw_device_notify(cdev, CIO_BOXED))
+ return 0;
+ ccw_device_schedule_sch_unregister(cdev);
+ return -ENODEV;
+}
+
+static int resume_handle_disc(struct ccw_device *cdev)
+{
+ cdev->private->state = DEV_STATE_DISCONNECTED;
+ if (ccw_device_notify(cdev, CIO_GONE))
+ return 0;
+ ccw_device_schedule_sch_unregister(cdev);
+ return -ENODEV;
+}
+
+static int ccw_device_pm_restore(struct device *dev)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int ret = 0, cm_enabled;
+
+ __ccw_device_pm_restore(cdev);
+ spin_lock_irq(sch->lock);
+ if (cio_is_console(sch->schid)) {
+ cio_enable_subchannel(sch, (u32)(addr_t)sch);
+ spin_unlock_irq(sch->lock);
+ goto out_restore;
+ }
+ cdev->private->flags.donotify = 0;
+ /* check recognition results */
+ switch (cdev->private->state) {
+ case DEV_STATE_OFFLINE:
+ break;
+ case DEV_STATE_BOXED:
+ ret = resume_handle_boxed(cdev);
+ spin_unlock_irq(sch->lock);
+ if (ret)
+ goto out;
+ goto out_restore;
+ case DEV_STATE_DISCONNECTED:
+ goto out_disc_unlock;
+ default:
+ goto out_unreg_unlock;
+ }
+ /* check if the device id has changed */
+ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
+ CIO_MSG_EVENT(0, "resume: sch %s: failed (devno changed from "
+ "%04x to %04x)\n", dev_name(&sch->dev),
+ cdev->private->dev_id.devno,
+ sch->schib.pmcw.dev);
+ goto out_unreg_unlock;
+ }
+ /* check if the device type has changed */
+ if (!ccw_device_test_sense_data(cdev)) {
+ ccw_device_update_sense_data(cdev);
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_do_unbind_bind);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+ if (!cdev->online) {
+ ret = 0;
+ goto out_unlock;
+ }
+ ret = ccw_device_online(cdev);
+ if (ret)
+ goto out_disc_unlock;
+
+ cm_enabled = cdev->private->cmb != NULL;
+ spin_unlock_irq(sch->lock);
+
+ wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
+ if (cdev->private->state != DEV_STATE_ONLINE) {
+ spin_lock_irq(sch->lock);
+ goto out_disc_unlock;
+ }
+ if (cm_enabled) {
+ ret = ccw_set_cmf(cdev, 1);
+ if (ret) {
+ CIO_MSG_EVENT(2, "resume: cdev %s: cmf failed "
+ "(rc=%d)\n", dev_name(&cdev->dev), ret);
+ ret = 0;
+ }
+ }
+
+out_restore:
+ if (cdev->online && cdev->drv && cdev->drv->restore)
+ ret = cdev->drv->restore(cdev);
+out:
+ return ret;
+
+out_disc_unlock:
+ ret = resume_handle_disc(cdev);
+ spin_unlock_irq(sch->lock);
+ if (ret)
+ return ret;
+ goto out_restore;
+
+out_unreg_unlock:
+ ccw_device_schedule_sch_unregister(cdev);
+ ret = -ENODEV;
+out_unlock:
+ spin_unlock_irq(sch->lock);
+ return ret;
+}
+
+static struct dev_pm_ops ccw_pm_ops = {
+ .prepare = ccw_device_pm_prepare,
+ .complete = ccw_device_pm_complete,
+ .freeze = ccw_device_pm_freeze,
+ .thaw = ccw_device_pm_thaw,
+ .restore = ccw_device_pm_restore,
+};
+
struct bus_type ccw_bus_type = {
.name = "ccw",
.match = ccw_bus_match,
@@ -1902,6 +2161,7 @@ struct bus_type ccw_bus_type = {
.probe = ccw_device_probe,
.remove = ccw_device_remove,
.shutdown = ccw_device_shutdown,
+ .pm = &ccw_pm_ops,
};
/**
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index f1cbbd94ad4..e3975107a57 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -87,6 +87,8 @@ int ccw_device_is_orphan(struct ccw_device *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
+void ccw_device_update_sense_data(struct ccw_device *);
+int ccw_device_test_sense_data(struct ccw_device *);
void ccw_device_schedule_sch_unregister(struct ccw_device *);
int ccw_purge_blacklisted(void);
@@ -133,5 +135,6 @@ extern struct bus_type ccw_bus_type;
void retry_set_schib(struct ccw_device *cdev);
void cmf_retry_copy_block(struct ccw_device *);
int cmf_reenable(struct ccw_device *);
+int ccw_set_cmf(struct ccw_device *cdev, int enable);
extern struct device_attribute dev_attr_cmb_enable;
#endif
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index e4604926156..3db88c52d28 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -177,29 +177,21 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
panic("Can't stop i/o on subchannel.\n");
}
-static int
-ccw_device_handle_oper(struct ccw_device *cdev)
+void ccw_device_update_sense_data(struct ccw_device *cdev)
{
- struct subchannel *sch;
+ memset(&cdev->id, 0, sizeof(cdev->id));
+ cdev->id.cu_type = cdev->private->senseid.cu_type;
+ cdev->id.cu_model = cdev->private->senseid.cu_model;
+ cdev->id.dev_type = cdev->private->senseid.dev_type;
+ cdev->id.dev_model = cdev->private->senseid.dev_model;
+}
- sch = to_subchannel(cdev->dev.parent);
- cdev->private->flags.recog_done = 1;
- /*
- * Check if cu type and device type still match. If
- * not, it is certainly another device and we have to
- * de- and re-register.
- */
- if (cdev->id.cu_type != cdev->private->senseid.cu_type ||
- cdev->id.cu_model != cdev->private->senseid.cu_model ||
- cdev->id.dev_type != cdev->private->senseid.dev_type ||
- cdev->id.dev_model != cdev->private->senseid.dev_model) {
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_do_unbind_bind);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- return 0;
- }
- cdev->private->flags.donotify = 1;
- return 1;
+int ccw_device_test_sense_data(struct ccw_device *cdev)
+{
+ return cdev->id.cu_type == cdev->private->senseid.cu_type &&
+ cdev->id.cu_model == cdev->private->senseid.cu_model &&
+ cdev->id.dev_type == cdev->private->senseid.dev_type &&
+ cdev->id.dev_model == cdev->private->senseid.dev_model;
}
/*
@@ -233,7 +225,7 @@ static void
ccw_device_recog_done(struct ccw_device *cdev, int state)
{
struct subchannel *sch;
- int notify, old_lpm, same_dev;
+ int old_lpm;
sch = to_subchannel(cdev->dev.parent);
@@ -263,8 +255,12 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
wake_up(&cdev->private->wait_q);
return;
}
- notify = 0;
- same_dev = 0; /* Keep the compiler quiet... */
+ if (cdev->private->flags.resuming) {
+ cdev->private->state = state;
+ cdev->private->flags.recog_done = 1;
+ wake_up(&cdev->private->wait_q);
+ return;
+ }
switch (state) {
case DEV_STATE_NOT_OPER:
CIO_MSG_EVENT(2, "SenseID : unknown device %04x on "
@@ -273,34 +269,31 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
sch->schid.ssid, sch->schid.sch_no);
break;
case DEV_STATE_OFFLINE:
- if (cdev->online) {
- same_dev = ccw_device_handle_oper(cdev);
- notify = 1;
+ if (!cdev->online) {
+ ccw_device_update_sense_data(cdev);
+ /* Issue device info message. */
+ CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: "
+ "CU Type/Mod = %04X/%02X, Dev Type/Mod "
+ "= %04X/%02X\n",
+ cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno,
+ cdev->id.cu_type, cdev->id.cu_model,
+ cdev->id.dev_type, cdev->id.dev_model);
+ break;
}
- /* fill out sense information */
- memset(&cdev->id, 0, sizeof(cdev->id));
- cdev->id.cu_type = cdev->private->senseid.cu_type;
- cdev->id.cu_model = cdev->private->senseid.cu_model;
- cdev->id.dev_type = cdev->private->senseid.dev_type;
- cdev->id.dev_model = cdev->private->senseid.dev_model;
- if (notify) {
- cdev->private->state = DEV_STATE_OFFLINE;
- if (same_dev) {
- /* Get device online again. */
- ccw_device_online(cdev);
- wake_up(&cdev->private->wait_q);
- }
- return;
+ cdev->private->state = DEV_STATE_OFFLINE;
+ cdev->private->flags.recog_done = 1;
+ if (ccw_device_test_sense_data(cdev)) {
+ cdev->private->flags.donotify = 1;
+ ccw_device_online(cdev);
+ wake_up(&cdev->private->wait_q);
+ } else {
+ ccw_device_update_sense_data(cdev);
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_do_unbind_bind);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
}
- /* Issue device info message. */
- CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: "
- "CU Type/Mod = %04X/%02X, Dev Type/Mod = "
- "%04X/%02X\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno,
- cdev->id.cu_type, cdev->id.cu_model,
- cdev->id.dev_type, cdev->id.dev_model);
- break;
+ return;
case DEV_STATE_BOXED:
CIO_MSG_EVENT(0, "SenseID : boxed device %04x on "
" subchannel 0.%x.%04x\n",
@@ -502,9 +495,6 @@ ccw_device_recognition(struct ccw_device *cdev)
struct subchannel *sch;
int ret;
- if ((cdev->private->state != DEV_STATE_NOT_OPER) &&
- (cdev->private->state != DEV_STATE_BOXED))
- return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
if (ret != 0)
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index bf0a24af39a..2d0efee8a29 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -1,10 +1,8 @@
/*
- * drivers/s390/cio/device_ops.c
+ * Copyright IBM Corp. 2002, 2009
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
- * Cornelia Huck (cornelia.huck@de.ibm.com)
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Cornelia Huck (cornelia.huck@de.ibm.com)
*/
#include <linux/module.h>
#include <linux/init.h>
@@ -116,12 +114,15 @@ int ccw_device_clear(struct ccw_device *cdev, unsigned long intparm)
if (!cdev || !cdev->dev.parent)
return -ENODEV;
+ sch = to_subchannel(cdev->dev.parent);
+ if (!sch->schib.pmcw.ena)
+ return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE)
return -EINVAL;
- sch = to_subchannel(cdev->dev.parent);
+
ret = cio_clear(sch);
if (ret == 0)
cdev->private->intparm = intparm;
@@ -162,6 +163,8 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
+ if (!sch->schib.pmcw.ena)
+ return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state == DEV_STATE_VERIFY ||
@@ -337,12 +340,15 @@ int ccw_device_halt(struct ccw_device *cdev, unsigned long intparm)
if (!cdev || !cdev->dev.parent)
return -ENODEV;
+ sch = to_subchannel(cdev->dev.parent);
+ if (!sch->schib.pmcw.ena)
+ return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE &&
cdev->private->state != DEV_STATE_W4SENSE)
return -EINVAL;
- sch = to_subchannel(cdev->dev.parent);
+
ret = cio_halt(sch);
if (ret == 0)
cdev->private->intparm = intparm;
@@ -369,6 +375,8 @@ int ccw_device_resume(struct ccw_device *cdev)
if (!cdev || !cdev->dev.parent)
return -ENODEV;
sch = to_subchannel(cdev->dev.parent);
+ if (!sch->schib.pmcw.ena)
+ return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE ||
@@ -580,6 +588,8 @@ int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
int rc;
sch = to_subchannel(cdev->dev.parent);
+ if (!sch->schib.pmcw.ena)
+ return -EINVAL;
if (cdev->private->state != DEV_STATE_ONLINE)
return -EIO;
/* Adjust requested path mask to excluded varied off paths. */
@@ -669,6 +679,8 @@ int ccw_device_tm_intrg(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ if (!sch->schib.pmcw.ena)
+ return -EINVAL;
if (cdev->private->state != DEV_STATE_ONLINE)
return -EIO;
if (!scsw_is_tm(&sch->schib.scsw) ||
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index c4f3e7c9a85..0b8f381bd20 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -107,6 +107,7 @@ struct ccw_device_private {
unsigned int recog_done:1; /* dev. recog. complete */
unsigned int fake_irb:1; /* deliver faked irb */
unsigned int intretry:1; /* retry internal operation */
+ unsigned int resuming:1; /* recognition while resume */
} __attribute__((packed)) flags;
unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data;
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 7b6f46ddf3c..d40f7a934f9 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -3,12 +3,12 @@
* ESCON CLAW network driver
*
* Linux for zSeries version
- * Copyright (C) 2002,2005 IBM Corporation
+ * Copyright IBM Corp. 2002, 2009
* Author(s) Original code written by:
- * Kazuo Iimura (iimura@jp.ibm.com)
+ * Kazuo Iimura <iimura@jp.ibm.com>
* Rewritten by
- * Andy Richter (richtera@us.ibm.com)
- * Marc Price (mwprice@us.ibm.com)
+ * Andy Richter <richtera@us.ibm.com>
+ * Marc Price <mwprice@us.ibm.com>
*
* sysfs parms:
* group x.x.rrrr,x.x.wwww
@@ -253,6 +253,11 @@ static void claw_free_wrt_buf(struct net_device *dev);
/* Functions for unpack reads */
static void unpack_read(struct net_device *dev);
+static int claw_pm_prepare(struct ccwgroup_device *gdev)
+{
+ return -EPERM;
+}
+
/* ccwgroup table */
static struct ccwgroup_driver claw_group_driver = {
@@ -264,6 +269,7 @@ static struct ccwgroup_driver claw_group_driver = {
.remove = claw_remove_device,
.set_online = claw_new_device,
.set_offline = claw_shutdown_device,
+ .prepare = claw_pm_prepare,
};
/*
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index 54c4649a493..222e4739443 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/ctcm_main.c
*
- * Copyright IBM Corp. 2001, 2007
+ * Copyright IBM Corp. 2001, 2009
* Author(s):
* Original CTC driver(s):
* Fritz Elfert (felfert@millenux.com)
@@ -1688,6 +1688,38 @@ static void ctcm_remove_device(struct ccwgroup_device *cgdev)
put_device(&cgdev->dev);
}
+static int ctcm_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev);
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ netif_device_detach(priv->channel[READ]->netdev);
+ ctcm_close(priv->channel[READ]->netdev);
+ ccw_device_set_offline(gdev->cdev[1]);
+ ccw_device_set_offline(gdev->cdev[0]);
+ return 0;
+}
+
+static int ctcm_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev);
+ int rc;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ rc = ccw_device_set_online(gdev->cdev[1]);
+ if (rc)
+ goto err_out;
+ rc = ccw_device_set_online(gdev->cdev[0]);
+ if (rc)
+ goto err_out;
+ ctcm_open(priv->channel[READ]->netdev);
+err_out:
+ netif_device_attach(priv->channel[READ]->netdev);
+ return rc;
+}
+
static struct ccwgroup_driver ctcm_group_driver = {
.owner = THIS_MODULE,
.name = CTC_DRIVER_NAME,
@@ -1697,6 +1729,9 @@ static struct ccwgroup_driver ctcm_group_driver = {
.remove = ctcm_remove_device,
.set_online = ctcm_new_device,
.set_offline = ctcm_shutdown_device,
+ .freeze = ctcm_pm_suspend,
+ .thaw = ctcm_pm_resume,
+ .restore = ctcm_pm_resume,
};
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index a45bc24eb5f..07a25c3f94b 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1,15 +1,12 @@
/*
- * linux/drivers/s390/net/lcs.c
- *
* Linux for S/390 Lan Channel Station Network Driver
*
- * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Original Code written by
- * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- * Rewritten by
- * Frank Pavlic (fpavlic@de.ibm.com) and
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 1999, 2009
+ * Author(s): Original Code written by
+ * DJ Barrow <djbarrow@de.ibm.com,barrow_dj@yahoo.com>
+ * Rewritten by
+ * Frank Pavlic <fpavlic@de.ibm.com> and
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*
* 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
@@ -2313,6 +2310,60 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev)
put_device(&ccwgdev->dev);
}
+static int lcs_pm_suspend(struct lcs_card *card)
+{
+ if (card->dev)
+ netif_device_detach(card->dev);
+ lcs_set_allowed_threads(card, 0);
+ lcs_wait_for_threads(card, 0xffffffff);
+ if (card->state != DEV_STATE_DOWN)
+ __lcs_shutdown_device(card->gdev, 1);
+ return 0;
+}
+
+static int lcs_pm_resume(struct lcs_card *card)
+{
+ int rc = 0;
+
+ if (card->state == DEV_STATE_RECOVER)
+ rc = lcs_new_device(card->gdev);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "The lcs device driver "
+ "failed to recover the device\n");
+ }
+ return rc;
+}
+
+static int lcs_prepare(struct ccwgroup_device *gdev)
+{
+ return 0;
+}
+
+static void lcs_complete(struct ccwgroup_device *gdev)
+{
+ return;
+}
+
+static int lcs_freeze(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_suspend(card);
+}
+
+static int lcs_thaw(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_resume(card);
+}
+
+static int lcs_restore(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_resume(card);
+}
+
/**
* LCS ccwgroup driver registration
*/
@@ -2325,6 +2376,11 @@ static struct ccwgroup_driver lcs_group_driver = {
.remove = lcs_remove_device,
.set_online = lcs_new_device,
.set_offline = lcs_shutdown_device,
+ .prepare = lcs_prepare,
+ .complete = lcs_complete,
+ .freeze = lcs_freeze,
+ .thaw = lcs_thaw,
+ .restore = lcs_restore,
};
/**
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index aec9e5d3cf4..fdb02d043d3 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -1,11 +1,15 @@
/*
* IUCV network driver
*
- * Copyright 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+ * Copyright IBM Corp. 2001, 2009
*
- * Sysfs integration and all bugs therein by Cornelia Huck
- * (cornelia.huck@de.ibm.com)
+ * Author(s):
+ * Original netiucv driver:
+ * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+ * Sysfs integration and all bugs therein:
+ * Cornelia Huck (cornelia.huck@de.ibm.com)
+ * PM functions:
+ * Ursula Braun (ursula.braun@de.ibm.com)
*
* Documentation used:
* the source of the original IUCV driver by:
@@ -149,10 +153,27 @@ PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
#define PRINTK_HEADER " iucv: " /* for debugging */
+/* dummy device to make sure netiucv_pm functions are called */
+static struct device *netiucv_dev;
+
+static int netiucv_pm_prepare(struct device *);
+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 = {
+ .prepare = netiucv_pm_prepare,
+ .complete = netiucv_pm_complete,
+ .freeze = netiucv_pm_freeze,
+ .thaw = netiucv_pm_restore_thaw,
+ .restore = netiucv_pm_restore_thaw,
+};
+
static struct device_driver netiucv_driver = {
.owner = THIS_MODULE,
.name = "netiucv",
.bus = &iucv_bus,
+ .pm = &netiucv_pm_ops,
};
static int netiucv_callback_connreq(struct iucv_path *,
@@ -233,6 +254,7 @@ struct netiucv_priv {
fsm_instance *fsm;
struct iucv_connection *conn;
struct device *dev;
+ int pm_state;
};
/**
@@ -1265,6 +1287,72 @@ static int netiucv_close(struct net_device *dev)
return 0;
}
+static int netiucv_pm_prepare(struct device *dev)
+{
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ return 0;
+}
+
+static void netiucv_pm_complete(struct device *dev)
+{
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ return;
+}
+
+/**
+ * netiucv_pm_freeze() - Freeze PM callback
+ * @dev: netiucv device
+ *
+ * close open netiucv interfaces
+ */
+static int netiucv_pm_freeze(struct device *dev)
+{
+ struct netiucv_priv *priv = dev->driver_data;
+ struct net_device *ndev = NULL;
+ int rc = 0;
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ if (priv && priv->conn)
+ ndev = priv->conn->netdev;
+ if (!ndev)
+ goto out;
+ netif_device_detach(ndev);
+ priv->pm_state = fsm_getstate(priv->fsm);
+ rc = netiucv_close(ndev);
+out:
+ return rc;
+}
+
+/**
+ * netiucv_pm_restore_thaw() - Thaw and restore PM callback
+ * @dev: netiucv device
+ *
+ * re-open netiucv interfaces closed during freeze
+ */
+static int netiucv_pm_restore_thaw(struct device *dev)
+{
+ struct netiucv_priv *priv = dev->driver_data;
+ struct net_device *ndev = NULL;
+ int rc = 0;
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ if (priv && priv->conn)
+ ndev = priv->conn->netdev;
+ if (!ndev)
+ goto out;
+ switch (priv->pm_state) {
+ case DEV_STATE_RUNNING:
+ case DEV_STATE_STARTWAIT:
+ rc = netiucv_open(ndev);
+ break;
+ default:
+ break;
+ }
+ netif_device_attach(ndev);
+out:
+ return rc;
+}
+
/**
* Start transmission of a packet.
* Called from generic network device layer.
@@ -1731,7 +1819,6 @@ static int netiucv_register_device(struct net_device *ndev)
struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
int ret;
-
IUCV_DBF_TEXT(trace, 3, __func__);
if (dev) {
@@ -2100,6 +2187,7 @@ static void __exit netiucv_exit(void)
netiucv_unregister_device(dev);
}
+ device_unregister(netiucv_dev);
driver_unregister(&netiucv_driver);
iucv_unregister(&netiucv_handler, 1);
iucv_unregister_dbf_views();
@@ -2125,10 +2213,25 @@ static int __init netiucv_init(void)
IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
goto out_iucv;
}
-
+ /* establish dummy device */
+ netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!netiucv_dev) {
+ rc = -ENOMEM;
+ goto out_driver;
+ }
+ dev_set_name(netiucv_dev, "netiucv");
+ netiucv_dev->bus = &iucv_bus;
+ netiucv_dev->parent = iucv_root;
+ netiucv_dev->release = (void (*)(struct device *))kfree;
+ netiucv_dev->driver = &netiucv_driver;
+ rc = device_register(netiucv_dev);
+ if (rc)
+ goto out_driver;
netiucv_banner();
return rc;
+out_driver:
+ driver_unregister(&netiucv_driver);
out_iucv:
iucv_unregister(&netiucv_handler, 1);
out_dbf:
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 74c49d9a8db..d53621c4acb 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/qeth_core_main.c
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2009
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
* Frank Pavlic <fpavlic@de.ibm.com>,
* Thomas Spatzier <tspat@de.ibm.com>,
@@ -4195,6 +4195,50 @@ static void qeth_core_shutdown(struct ccwgroup_device *gdev)
card->discipline.ccwgdriver->shutdown(gdev);
}
+static int qeth_core_prepare(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->prepare)
+ return card->discipline.ccwgdriver->prepare(gdev);
+ return 0;
+}
+
+static void qeth_core_complete(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->complete)
+ card->discipline.ccwgdriver->complete(gdev);
+}
+
+static int qeth_core_freeze(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->freeze)
+ return card->discipline.ccwgdriver->freeze(gdev);
+ return 0;
+}
+
+static int qeth_core_thaw(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->thaw)
+ return card->discipline.ccwgdriver->thaw(gdev);
+ return 0;
+}
+
+static int qeth_core_restore(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->restore)
+ return card->discipline.ccwgdriver->restore(gdev);
+ return 0;
+}
+
static struct ccwgroup_driver qeth_core_ccwgroup_driver = {
.owner = THIS_MODULE,
.name = "qeth",
@@ -4204,6 +4248,11 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = {
.set_online = qeth_core_set_online,
.set_offline = qeth_core_set_offline,
.shutdown = qeth_core_shutdown,
+ .prepare = qeth_core_prepare,
+ .complete = qeth_core_complete,
+ .freeze = qeth_core_freeze,
+ .thaw = qeth_core_thaw,
+ .restore = qeth_core_restore,
};
static ssize_t
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index ecd3d06c0d5..81d7f268418 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/qeth_l2_main.c
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2009
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
* Frank Pavlic <fpavlic@de.ibm.com>,
* Thomas Spatzier <tspat@de.ibm.com>,
@@ -1141,12 +1141,62 @@ static void qeth_l2_shutdown(struct ccwgroup_device *gdev)
qeth_clear_qdio_buffers(card);
}
+static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ if (card->dev)
+ netif_device_detach(card->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ if (card->state == CARD_STATE_UP) {
+ card->use_hard_stop = 1;
+ __qeth_l2_set_offline(card->gdev, 1);
+ } else
+ __qeth_l2_set_offline(card->gdev, 0);
+ return 0;
+}
+
+static int qeth_l2_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ goto out;
+
+ if (card->state == CARD_STATE_RECOVER) {
+ rc = __qeth_l2_set_online(card->gdev, 1);
+ if (rc) {
+ if (card->dev) {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ }
+ } else
+ rc = __qeth_l2_set_online(card->gdev, 0);
+out:
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc)
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ return rc;
+}
+
struct ccwgroup_driver qeth_l2_ccwgroup_driver = {
.probe = qeth_l2_probe_device,
.remove = qeth_l2_remove_device,
.set_online = qeth_l2_set_online,
.set_offline = qeth_l2_set_offline,
.shutdown = qeth_l2_shutdown,
+ .freeze = qeth_l2_pm_suspend,
+ .thaw = qeth_l2_pm_resume,
+ .restore = qeth_l2_pm_resume,
};
EXPORT_SYMBOL_GPL(qeth_l2_ccwgroup_driver);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 6f2386e9d6e..54872406864 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/qeth_l3_main.c
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2009
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
* Frank Pavlic <fpavlic@de.ibm.com>,
* Thomas Spatzier <tspat@de.ibm.com>,
@@ -3283,12 +3283,62 @@ static void qeth_l3_shutdown(struct ccwgroup_device *gdev)
qeth_clear_qdio_buffers(card);
}
+static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ if (card->dev)
+ netif_device_detach(card->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ if (card->state == CARD_STATE_UP) {
+ card->use_hard_stop = 1;
+ __qeth_l3_set_offline(card->gdev, 1);
+ } else
+ __qeth_l3_set_offline(card->gdev, 0);
+ return 0;
+}
+
+static int qeth_l3_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ goto out;
+
+ if (card->state == CARD_STATE_RECOVER) {
+ rc = __qeth_l3_set_online(card->gdev, 1);
+ if (rc) {
+ if (card->dev) {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ }
+ } else
+ rc = __qeth_l3_set_online(card->gdev, 0);
+out:
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc)
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ return rc;
+}
+
struct ccwgroup_driver qeth_l3_ccwgroup_driver = {
.probe = qeth_l3_probe_device,
.remove = qeth_l3_remove_device,
.set_online = qeth_l3_set_online,
.set_offline = qeth_l3_set_offline,
.shutdown = qeth_l3_shutdown,
+ .freeze = qeth_l3_pm_suspend,
+ .thaw = qeth_l3_pm_resume,
+ .restore = qeth_l3_pm_resume,
};
EXPORT_SYMBOL_GPL(qeth_l3_ccwgroup_driver);
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 164e090c262..e76a320d373 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -1,7 +1,8 @@
/*
* IUCV special message driver
*
- * Copyright 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 2003, 2009
+ *
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* This program is free software; you can redistribute it and/or modify
@@ -40,6 +41,8 @@ MODULE_AUTHOR
MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
static struct iucv_path *smsg_path;
+/* dummy device used as trigger for PM functions */
+static struct device *smsg_dev;
static DEFINE_SPINLOCK(smsg_list_lock);
static LIST_HEAD(smsg_list);
@@ -132,14 +135,51 @@ void smsg_unregister_callback(char *prefix,
kfree(cb);
}
+static int smsg_pm_freeze(struct device *dev)
+{
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "smsg_pm_freeze\n");
+#endif
+ if (smsg_path)
+ iucv_path_sever(smsg_path, NULL);
+ return 0;
+}
+
+static int smsg_pm_restore_thaw(struct device *dev)
+{
+ int rc;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "smsg_pm_restore_thaw\n");
+#endif
+ if (smsg_path) {
+ memset(smsg_path, 0, sizeof(*smsg_path));
+ smsg_path->msglim = 255;
+ smsg_path->flags = 0;
+ rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ",
+ NULL, NULL, NULL);
+ printk(KERN_ERR "iucv_path_connect returned with rc %i\n", rc);
+ }
+ return 0;
+}
+
+static struct dev_pm_ops smsg_pm_ops = {
+ .freeze = smsg_pm_freeze,
+ .thaw = smsg_pm_restore_thaw,
+ .restore = smsg_pm_restore_thaw,
+};
+
static struct device_driver smsg_driver = {
+ .owner = THIS_MODULE,
.name = "SMSGIUCV",
.bus = &iucv_bus,
+ .pm = &smsg_pm_ops,
};
static void __exit smsg_exit(void)
{
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
+ device_unregister(smsg_dev);
iucv_unregister(&smsg_handler, 1);
driver_unregister(&smsg_driver);
}
@@ -166,12 +206,29 @@ static int __init smsg_init(void)
rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ",
NULL, NULL, NULL);
if (rc)
- goto out_free;
+ goto out_free_path;
+ smsg_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!smsg_dev) {
+ rc = -ENOMEM;
+ goto out_free_path;
+ }
+ dev_set_name(smsg_dev, "smsg_iucv");
+ smsg_dev->bus = &iucv_bus;
+ smsg_dev->parent = iucv_root;
+ smsg_dev->release = (void (*)(struct device *))kfree;
+ smsg_dev->driver = &smsg_driver;
+ rc = device_register(smsg_dev);
+ if (rc)
+ goto out_free_dev;
+
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
return 0;
-out_free:
+out_free_dev:
+ kfree(smsg_dev);
+out_free_path:
iucv_path_free(smsg_path);
+ smsg_path = NULL;
out_register:
iucv_unregister(&smsg_handler, 1);
out_driver:
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index b2fe5cdbcae..d9da5c42ccb 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -13,6 +13,36 @@
#define ZFCP_MODEL_PRIV 0x4
+static int zfcp_ccw_suspend(struct ccw_device *cdev)
+
+{
+ struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
+
+ down(&zfcp_data.config_sema);
+
+ zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL);
+ zfcp_erp_wait(adapter);
+
+ up(&zfcp_data.config_sema);
+
+ return 0;
+}
+
+static int zfcp_ccw_activate(struct ccw_device *cdev)
+
+{
+ struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
+
+ zfcp_erp_modify_adapter_status(adapter, "ccresu1", NULL,
+ ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
+ zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
+ "ccresu2", NULL);
+ zfcp_erp_wait(adapter);
+ flush_work(&adapter->scan_work);
+
+ return 0;
+}
+
static struct ccw_device_id zfcp_ccw_device_id[] = {
{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, ZFCP_MODEL_PRIV) },
@@ -227,6 +257,9 @@ static struct ccw_driver zfcp_ccw_driver = {
.set_offline = zfcp_ccw_set_offline,
.notify = zfcp_ccw_notify,
.shutdown = zfcp_ccw_shutdown,
+ .freeze = zfcp_ccw_suspend,
+ .thaw = zfcp_ccw_activate,
+ .restore = zfcp_ccw_activate,
};
/**
diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c
index f014cc21e81..011c5bddba6 100644
--- a/drivers/spi/spi_bfin5xx.c
+++ b/drivers/spi/spi_bfin5xx.c
@@ -803,7 +803,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
drv_data->rx, drv_data->len_in_bytes);
/* invalidate caches, if needed */
- if (bfin_addr_dcachable((unsigned long) drv_data->rx))
+ if (bfin_addr_dcacheable((unsigned long) drv_data->rx))
invalidate_dcache_range((unsigned long) drv_data->rx,
(unsigned long) (drv_data->rx +
drv_data->len_in_bytes));
@@ -816,7 +816,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n");
/* flush caches, if needed */
- if (bfin_addr_dcachable((unsigned long) drv_data->tx))
+ if (bfin_addr_dcacheable((unsigned long) drv_data->tx))
flush_dcache_range((unsigned long) drv_data->tx,
(unsigned long) (drv_data->tx +
drv_data->len_in_bytes));
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 1ba9f9a8c30..bb870b8f81b 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -162,7 +162,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__,
(unsigned long)virq);
- ps3_system_bus_set_driver_data(dev, hcd);
+ ps3_system_bus_set_drvdata(dev, hcd);
result = usb_add_hcd(hcd, virq, IRQF_DISABLED);
@@ -195,8 +195,7 @@ fail_start:
static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
{
unsigned int tmp;
- struct usb_hcd *hcd =
- (struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
+ struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
BUG_ON(!hcd);
@@ -208,7 +207,7 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
ehci_shutdown(hcd);
usb_remove_hcd(hcd);
- ps3_system_bus_set_driver_data(dev, NULL);
+ ps3_system_bus_set_drvdata(dev, NULL);
BUG_ON(!hcd->regs);
iounmap(hcd->regs);
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index 3d191031732..1d56259c5db 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -162,7 +162,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__,
(unsigned long)virq);
- ps3_system_bus_set_driver_data(dev, hcd);
+ ps3_system_bus_set_drvdata(dev, hcd);
result = usb_add_hcd(hcd, virq, IRQF_DISABLED);
@@ -195,8 +195,7 @@ fail_start:
static int ps3_ohci_remove(struct ps3_system_bus_device *dev)
{
unsigned int tmp;
- struct usb_hcd *hcd =
- (struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
+ struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
BUG_ON(!hcd);
@@ -208,7 +207,7 @@ static int ps3_ohci_remove(struct ps3_system_bus_device *dev)
ohci_shutdown(hcd);
usb_remove_hcd(hcd);
- ps3_system_bus_set_driver_data(dev, NULL);
+ ps3_system_bus_set_drvdata(dev, NULL);
BUG_ON(!hcd->regs);
iounmap(hcd->regs);
diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
index e00c1dff55d..c0af638fe70 100644
--- a/drivers/video/ps3fb.c
+++ b/drivers/video/ps3fb.c
@@ -32,25 +32,16 @@
#include <linux/init.h>
#include <asm/abs_addr.h>
+#include <asm/iommu.h>
#include <asm/lv1call.h>
#include <asm/ps3av.h>
#include <asm/ps3fb.h>
#include <asm/ps3.h>
+#include <asm/ps3gpu.h>
#define DEVICE_NAME "ps3fb"
-#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101
-#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102
-#define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP 0x600
-#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
-#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT_SYNC 0x602
-
-#define L1GPU_FB_BLIT_WAIT_FOR_COMPLETION (1ULL << 32)
-
-#define L1GPU_DISPLAY_SYNC_HSYNC 1
-#define L1GPU_DISPLAY_SYNC_VSYNC 2
-
#define GPU_CMD_BUF_SIZE (2 * 1024 * 1024)
#define GPU_FB_START (64 * 1024)
#define GPU_IOIF (0x0d000000UL)
@@ -462,33 +453,27 @@ static void ps3fb_sync_image(struct device *dev, u64 frame_offset,
src_offset += GPU_FB_START;
mutex_lock(&ps3_gpu_mutex);
- status = lv1_gpu_context_attribute(ps3fb.context_handle,
- L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
- dst_offset, GPU_IOIF + src_offset,
- L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |
- (width << 16) | height,
- line_length);
+ status = lv1_gpu_fb_blit(ps3fb.context_handle, dst_offset,
+ GPU_IOIF + src_offset,
+ L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |
+ (width << 16) | height,
+ line_length);
mutex_unlock(&ps3_gpu_mutex);
if (status)
- dev_err(dev,
- "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n",
- __func__, status);
+ dev_err(dev, "%s: lv1_gpu_fb_blit failed: %d\n", __func__,
+ status);
#ifdef HEAD_A
- status = lv1_gpu_context_attribute(ps3fb.context_handle,
- L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
- 0, frame_offset, 0, 0);
+ status = lv1_gpu_display_flip(ps3fb.context_handle, 0, frame_offset);
if (status)
- dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
- __func__, status);
+ dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__,
+ status);
#endif
#ifdef HEAD_B
- status = lv1_gpu_context_attribute(ps3fb.context_handle,
- L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
- 1, frame_offset, 0, 0);
+ status = lv1_gpu_display_flip(ps3fb.context_handle, 1, frame_offset);
if (status)
- dev_err(dev, "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
- __func__, status);
+ dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__,
+ status);
#endif
}
@@ -956,73 +941,6 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr)
}
-static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo,
- struct device *dev)
-{
- int error;
-
- dev_dbg(dev, "version_driver:%x\n", dinfo->version_driver);
- dev_dbg(dev, "irq outlet:%x\n", dinfo->irq.irq_outlet);
- dev_dbg(dev,
- "version_gpu: %x memory_size: %x ch: %x core_freq: %d "
- "mem_freq:%d\n",
- dinfo->version_gpu, dinfo->memory_size, dinfo->hardware_channel,
- dinfo->nvcore_frequency/1000000, dinfo->memory_frequency/1000000);
-
- if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) {
- dev_err(dev, "%s: version_driver err:%x\n", __func__,
- dinfo->version_driver);
- return -EINVAL;
- }
-
- error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
- &ps3fb.irq_no);
- if (error) {
- dev_err(dev, "%s: ps3_alloc_irq failed %d\n", __func__, error);
- return error;
- }
-
- error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED,
- DEVICE_NAME, dev);
- if (error) {
- dev_err(dev, "%s: request_irq failed %d\n", __func__, error);
- ps3_irq_plug_destroy(ps3fb.irq_no);
- return error;
- }
-
- dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) |
- (1 << GPU_INTR_STATUS_FLIP_1);
- return 0;
-}
-
-static int ps3fb_xdr_settings(u64 xdr_lpar, struct device *dev)
-{
- int status;
-
- status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF,
- xdr_lpar, ps3fb_videomemory.size, 0);
- if (status) {
- dev_err(dev, "%s: lv1_gpu_context_iomap failed: %d\n",
- __func__, status);
- return -ENXIO;
- }
- dev_dbg(dev, "video:%p ioif:%lx lpar:%llx size:%lx\n",
- ps3fb_videomemory.address, GPU_IOIF, xdr_lpar,
- ps3fb_videomemory.size);
-
- status = lv1_gpu_context_attribute(ps3fb.context_handle,
- L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP,
- xdr_lpar, GPU_CMD_BUF_SIZE,
- GPU_IOIF, 0);
- if (status) {
- dev_err(dev,
- "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n",
- __func__, status);
- return -ENXIO;
- }
- return 0;
-}
-
static struct fb_ops ps3fb_ops = {
.fb_open = ps3fb_open,
.fb_release = ps3fb_release,
@@ -1048,49 +966,18 @@ static struct fb_fix_screeninfo ps3fb_fix __initdata = {
.accel = FB_ACCEL_NONE,
};
-static int ps3fb_set_sync(struct device *dev)
-{
- int status;
-
-#ifdef HEAD_A
- status = lv1_gpu_context_attribute(0x0,
- L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
- 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
- if (status) {
- dev_err(dev,
- "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: "
- "%d\n",
- __func__, status);
- return -1;
- }
-#endif
-#ifdef HEAD_B
- status = lv1_gpu_context_attribute(0x0,
- L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
- 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0);
-
- if (status) {
- dev_err(dev,
- "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: "
- "%d\n",
- __func__, status);
- return -1;
- }
-#endif
- return 0;
-}
-
static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
{
struct fb_info *info;
struct ps3fb_par *par;
- int retval = -ENOMEM;
+ int retval;
u64 ddr_lpar = 0;
u64 lpar_dma_control = 0;
u64 lpar_driver_info = 0;
u64 lpar_reports = 0;
u64 lpar_reports_size = 0;
u64 xdr_lpar;
+ struct gpu_driver_info *dinfo;
void *fb_start;
int status;
struct task_struct *task;
@@ -1101,8 +988,8 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
return -ENOMEM;
}
- status = ps3_open_hv_device(dev);
- if (status) {
+ retval = ps3_open_hv_device(dev);
+ if (retval) {
dev_err(&dev->core, "%s: ps3_open_hv_device failed\n",
__func__);
goto err;
@@ -1116,7 +1003,24 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */
init_waitqueue_head(&ps3fb.wait_vsync);
- ps3fb_set_sync(&dev->core);
+#ifdef HEAD_A
+ status = lv1_gpu_display_sync(0x0, 0, L1GPU_DISPLAY_SYNC_VSYNC);
+ if (status) {
+ dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n",
+ __func__, status);
+ retval = -ENODEV;
+ goto err_close_device;
+ }
+#endif
+#ifdef HEAD_B
+ status = lv1_gpu_display_sync(0x0, 1, L1GPU_DISPLAY_SYNC_VSYNC);
+ if (status) {
+ dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n",
+ __func__, status);
+ retval = -ENODEV;
+ goto err_close_device;
+ }
+#endif
max_ps3fb_size = _ALIGN_UP(GPU_IOIF, 256*1024*1024) - GPU_IOIF;
if (ps3fb_videomemory.size > max_ps3fb_size) {
@@ -1131,7 +1035,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
if (status) {
dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n",
__func__, status);
- goto err;
+ goto err_close_device;
}
dev_dbg(&dev->core, "ddr:lpar:0x%llx\n", ddr_lpar);
@@ -1141,33 +1045,85 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
&lpar_reports, &lpar_reports_size);
if (status) {
dev_err(&dev->core,
- "%s: lv1_gpu_context_attribute failed: %d\n", __func__,
+ "%s: lv1_gpu_context_allocate failed: %d\n", __func__,
status);
goto err_gpu_memory_free;
}
/* vsync interrupt */
- ps3fb.dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024);
- if (!ps3fb.dinfo) {
+ dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024);
+ if (!dinfo) {
dev_err(&dev->core, "%s: ioremap failed\n", __func__);
goto err_gpu_context_free;
}
- retval = ps3fb_vsync_settings(ps3fb.dinfo, &dev->core);
- if (retval)
+ ps3fb.dinfo = dinfo;
+ dev_dbg(&dev->core, "version_driver:%x\n", dinfo->version_driver);
+ dev_dbg(&dev->core, "irq outlet:%x\n", dinfo->irq.irq_outlet);
+ dev_dbg(&dev->core, "version_gpu: %x memory_size: %x ch: %x "
+ "core_freq: %d mem_freq:%d\n", dinfo->version_gpu,
+ dinfo->memory_size, dinfo->hardware_channel,
+ dinfo->nvcore_frequency/1000000,
+ dinfo->memory_frequency/1000000);
+
+ if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) {
+ dev_err(&dev->core, "%s: version_driver err:%x\n", __func__,
+ dinfo->version_driver);
+ retval = -EINVAL;
+ goto err_iounmap_dinfo;
+ }
+
+ retval = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
+ &ps3fb.irq_no);
+ if (retval) {
+ dev_err(&dev->core, "%s: ps3_alloc_irq failed %d\n", __func__,
+ retval);
goto err_iounmap_dinfo;
+ }
+
+ retval = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt,
+ IRQF_DISABLED, DEVICE_NAME, &dev->core);
+ if (retval) {
+ dev_err(&dev->core, "%s: request_irq failed %d\n", __func__,
+ retval);
+ goto err_destroy_plug;
+ }
+
+ dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) |
+ (1 << GPU_INTR_STATUS_FLIP_1);
/* Clear memory to prevent kernel info leakage into userspace */
memset(ps3fb_videomemory.address, 0, ps3fb_videomemory.size);
xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address));
- retval = ps3fb_xdr_settings(xdr_lpar, &dev->core);
- if (retval)
+
+ status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF,
+ xdr_lpar, ps3fb_videomemory.size,
+ CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |
+ CBE_IOPTE_M);
+ if (status) {
+ dev_err(&dev->core, "%s: lv1_gpu_context_iomap failed: %d\n",
+ __func__, status);
+ retval = -ENXIO;
goto err_free_irq;
+ }
+
+ dev_dbg(&dev->core, "video:%p ioif:%lx lpar:%llx size:%lx\n",
+ ps3fb_videomemory.address, GPU_IOIF, xdr_lpar,
+ ps3fb_videomemory.size);
+
+ status = lv1_gpu_fb_setup(ps3fb.context_handle, xdr_lpar,
+ GPU_CMD_BUF_SIZE, GPU_IOIF);
+ if (status) {
+ dev_err(&dev->core, "%s: lv1_gpu_fb_setup failed: %d\n",
+ __func__, status);
+ retval = -ENXIO;
+ goto err_context_unmap;
+ }
info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core);
if (!info)
- goto err_free_irq;
+ goto err_context_fb_close;
par = info->par;
par->mode_id = ~ps3fb_mode; /* != ps3fb_mode, to trigger change */
@@ -1210,7 +1166,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
if (retval < 0)
goto err_fb_dealloc;
- dev->core.driver_data = info;
+ ps3_system_bus_set_drvdata(dev, info);
dev_info(info->device, "%s %s, using %u KiB of video memory\n",
dev_driver_string(info->dev), dev_name(info->dev),
@@ -1232,8 +1188,14 @@ err_fb_dealloc:
fb_dealloc_cmap(&info->cmap);
err_framebuffer_release:
framebuffer_release(info);
+err_context_fb_close:
+ lv1_gpu_fb_close(ps3fb.context_handle);
+err_context_unmap:
+ lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar,
+ ps3fb_videomemory.size, CBE_IOPTE_M);
err_free_irq:
free_irq(ps3fb.irq_no, &dev->core);
+err_destroy_plug:
ps3_irq_plug_destroy(ps3fb.irq_no);
err_iounmap_dinfo:
iounmap((u8 __force __iomem *)ps3fb.dinfo);
@@ -1241,14 +1203,16 @@ err_gpu_context_free:
lv1_gpu_context_free(ps3fb.context_handle);
err_gpu_memory_free:
lv1_gpu_memory_free(ps3fb.memory_handle);
+err_close_device:
+ ps3_close_hv_device(dev);
err:
return retval;
}
static int ps3fb_shutdown(struct ps3_system_bus_device *dev)
{
- int status;
- struct fb_info *info = dev->core.driver_data;
+ struct fb_info *info = ps3_system_bus_get_drvdata(dev);
+ u64 xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address));
dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
@@ -1268,20 +1232,14 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev)
unregister_framebuffer(info);
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
- info = dev->core.driver_data = NULL;
+ ps3_system_bus_set_drvdata(dev, NULL);
}
iounmap((u8 __force __iomem *)ps3fb.dinfo);
-
- status = lv1_gpu_context_free(ps3fb.context_handle);
- if (status)
- dev_dbg(&dev->core, "lv1_gpu_context_free failed: %d\n",
- status);
-
- status = lv1_gpu_memory_free(ps3fb.memory_handle);
- if (status)
- dev_dbg(&dev->core, "lv1_gpu_memory_free failed: %d\n",
- status);
-
+ lv1_gpu_fb_close(ps3fb.context_handle);
+ lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar,
+ ps3fb_videomemory.size, CBE_IOPTE_M);
+ lv1_gpu_context_free(ps3fb.context_handle);
+ lv1_gpu_memory_free(ps3fb.memory_handle);
ps3_close_hv_device(dev);
dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c
index 89f231dc443..ff43c888502 100644
--- a/drivers/video/tdfxfb.c
+++ b/drivers/video/tdfxfb.c
@@ -1315,7 +1315,6 @@ static int __devinit tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan,
strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name));
chan->adapter.owner = THIS_MODULE;
- chan->adapter.class = I2C_CLASS_TV_ANALOG;
chan->adapter.algo_data = &chan->algo;
chan->adapter.dev.parent = dev;
chan->algo.setsda = tdfxfb_i2c_setsda;
diff --git a/fs/bio.c b/fs/bio.c
index 5f80848c320..24c91404353 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -25,7 +25,6 @@
#include <linux/module.h>
#include <linux/mempool.h>
#include <linux/workqueue.h>
-#include <linux/blktrace_api.h>
#include <scsi/sg.h> /* for struct sg_iovec */
#include <trace/events/block.h>
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 0d50d49d990..d28d29c95f7 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -42,6 +42,8 @@
static struct extent_io_ops btree_extent_io_ops;
static void end_workqueue_fn(struct btrfs_work *work);
+static atomic_t btrfs_bdi_num = ATOMIC_INIT(0);
+
/*
* end_io_wq structs are used to do processing in task context when an IO is
* complete. This is used during reads to verify checksums, and it is used
@@ -1342,12 +1344,25 @@ static void btrfs_unplug_io_fn(struct backing_dev_info *bdi, struct page *page)
free_extent_map(em);
}
+/*
+ * If this fails, caller must call bdi_destroy() to get rid of the
+ * bdi again.
+ */
static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi)
{
- bdi_init(bdi);
+ int err;
+
+ bdi->capabilities = BDI_CAP_MAP_COPY;
+ err = bdi_init(bdi);
+ if (err)
+ return err;
+
+ err = bdi_register(bdi, NULL, "btrfs-%d",
+ atomic_inc_return(&btrfs_bdi_num));
+ if (err)
+ return err;
+
bdi->ra_pages = default_backing_dev_info.ra_pages;
- bdi->state = 0;
- bdi->capabilities = default_backing_dev_info.capabilities;
bdi->unplug_io_fn = btrfs_unplug_io_fn;
bdi->unplug_io_data = info;
bdi->congested_fn = btrfs_congested_fn;
@@ -1569,7 +1584,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
fs_info->sb = sb;
fs_info->max_extent = (u64)-1;
fs_info->max_inline = 8192 * 1024;
- setup_bdi(fs_info, &fs_info->bdi);
+ if (setup_bdi(fs_info, &fs_info->bdi))
+ goto fail_bdi;
fs_info->btree_inode = new_inode(sb);
fs_info->btree_inode->i_ino = 1;
fs_info->btree_inode->i_nlink = 1;
@@ -1946,8 +1962,8 @@ fail_iput:
btrfs_close_devices(fs_info->fs_devices);
btrfs_mapping_tree_free(&fs_info->mapping_tree);
+fail_bdi:
bdi_destroy(&fs_info->bdi);
-
fail:
kfree(extent_root);
kfree(tree_root);
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 2e177d7f4bb..4e83457ea25 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -543,13 +543,13 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
btrfs_free_log(trans, root);
btrfs_update_reloc_root(trans, root);
- if (root->commit_root == root->node)
- continue;
-
- free_extent_buffer(root->commit_root);
- root->commit_root = btrfs_root_node(root);
+ if (root->commit_root != root->node) {
+ free_extent_buffer(root->commit_root);
+ root->commit_root = btrfs_root_node(root);
+ btrfs_set_root_node(&root->root_item,
+ root->node);
+ }
- btrfs_set_root_node(&root->root_item, root->node);
err = btrfs_update_root(trans, fs_info->tree_root,
&root->root_key,
&root->root_item);
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index b4260229808..923990e4f16 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -241,7 +241,7 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
while (*fclus < cluster) {
/* prevent the infinite loop of cluster chain */
if (*fclus > limit) {
- fat_fs_panic(sb, "%s: detected the cluster chain loop"
+ fat_fs_error(sb, "%s: detected the cluster chain loop"
" (i_pos %lld)", __func__,
MSDOS_I(inode)->i_pos);
nr = -EIO;
@@ -252,7 +252,7 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
if (nr < 0)
goto out;
else if (nr == FAT_ENT_FREE) {
- fat_fs_panic(sb, "%s: invalid cluster chain"
+ fat_fs_error(sb, "%s: invalid cluster chain"
" (i_pos %lld)", __func__,
MSDOS_I(inode)->i_pos);
nr = -EIO;
@@ -285,7 +285,7 @@ static int fat_bmap_cluster(struct inode *inode, int cluster)
if (ret < 0)
return ret;
else if (ret == FAT_ENT_EOF) {
- fat_fs_panic(sb, "%s: request beyond EOF (i_pos %lld)",
+ fat_fs_error(sb, "%s: request beyond EOF (i_pos %lld)",
__func__, MSDOS_I(inode)->i_pos);
return -EIO;
}
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index f3500294eec..3b8e71b412f 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -1334,7 +1334,7 @@ found:
goto error_remove;
}
if (dir->i_size & (sbi->cluster_size - 1)) {
- fat_fs_panic(sb, "Odd directory size");
+ fat_fs_error(sb, "Odd directory size");
dir->i_size = (dir->i_size + sbi->cluster_size - 1)
& ~((loff_t)sbi->cluster_size - 1);
}
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index e4d88527b5d..adb0e72a176 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -17,6 +17,10 @@
#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */
#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */
+#define FAT_ERRORS_CONT 1 /* ignore error and continue */
+#define FAT_ERRORS_PANIC 2 /* panic on error */
+#define FAT_ERRORS_RO 3 /* remount r/o on error */
+
struct fat_mount_options {
uid_t fs_uid;
gid_t fs_gid;
@@ -26,6 +30,7 @@ struct fat_mount_options {
char *iocharset; /* Charset used for filename input/display */
unsigned short shortname; /* flags for shortname display/create rule */
unsigned char name_check; /* r = relaxed, n = normal, s = strict */
+ unsigned char errors; /* On error: continue, panic, remount-ro */
unsigned short allow_utime;/* permission for setting the [am]time */
unsigned quiet:1, /* set = fake successful chmods and chowns */
showexec:1, /* set = only set x bit for com/exe/bat */
@@ -316,7 +321,7 @@ extern int fat_fill_super(struct super_block *sb, void *data, int silent,
extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
struct inode *i2);
/* fat/misc.c */
-extern void fat_fs_panic(struct super_block *s, const char *fmt, ...)
+extern void fat_fs_error(struct super_block *s, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3))) __cold;
extern void fat_clusters_flush(struct super_block *sb);
extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index 618f5305c2e..a81037721a6 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -348,7 +348,7 @@ int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
if (entry < FAT_START_ENT || sbi->max_cluster <= entry) {
fatent_brelse(fatent);
- fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", entry);
+ fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry);
return -EIO;
}
@@ -560,7 +560,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
err = cluster;
goto error;
} else if (cluster == FAT_ENT_FREE) {
- fat_fs_panic(sb, "%s: deleting FAT entry beyond EOF",
+ fat_fs_error(sb, "%s: deleting FAT entry beyond EOF",
__func__);
err = -EIO;
goto error;
diff --git a/fs/fat/file.c b/fs/fat/file.c
index e955a56b4e5..b28ea646ff6 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -18,106 +18,112 @@
#include <linux/security.h>
#include "fat.h"
-int fat_generic_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+static int fat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
{
+ u32 attr;
+
+ mutex_lock(&inode->i_mutex);
+ attr = fat_make_attrs(inode);
+ mutex_unlock(&inode->i_mutex);
+
+ return put_user(attr, user_attr);
+}
+
+static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
- u32 __user *user_attr = (u32 __user *)arg;
+ int is_dir = S_ISDIR(inode->i_mode);
+ u32 attr, oldattr;
+ struct iattr ia;
+ int err;
- switch (cmd) {
- case FAT_IOCTL_GET_ATTRIBUTES:
- {
- u32 attr;
+ err = get_user(attr, user_attr);
+ if (err)
+ goto out;
- mutex_lock(&inode->i_mutex);
- attr = fat_make_attrs(inode);
- mutex_unlock(&inode->i_mutex);
+ mutex_lock(&inode->i_mutex);
+ err = mnt_want_write(file->f_path.mnt);
+ if (err)
+ goto out_unlock_inode;
- return put_user(attr, user_attr);
+ /*
+ * ATTR_VOLUME and ATTR_DIR cannot be changed; this also
+ * prevents the user from turning us into a VFAT
+ * longname entry. Also, we obviously can't set
+ * any of the NTFS attributes in the high 24 bits.
+ */
+ attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR);
+ /* Merge in ATTR_VOLUME and ATTR_DIR */
+ attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
+ (is_dir ? ATTR_DIR : 0);
+ oldattr = fat_make_attrs(inode);
+
+ /* Equivalent to a chmod() */
+ ia.ia_valid = ATTR_MODE | ATTR_CTIME;
+ ia.ia_ctime = current_fs_time(inode->i_sb);
+ if (is_dir)
+ ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
+ else {
+ ia.ia_mode = fat_make_mode(sbi, attr,
+ S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
}
- case FAT_IOCTL_SET_ATTRIBUTES:
- {
- u32 attr, oldattr;
- int err, is_dir = S_ISDIR(inode->i_mode);
- struct iattr ia;
- err = get_user(attr, user_attr);
- if (err)
- return err;
+ /* The root directory has no attributes */
+ if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
+ err = -EINVAL;
+ goto out_drop_write;
+ }
- mutex_lock(&inode->i_mutex);
-
- err = mnt_want_write(filp->f_path.mnt);
- if (err)
- goto up_no_drop_write;
-
- /*
- * ATTR_VOLUME and ATTR_DIR cannot be changed; this also
- * prevents the user from turning us into a VFAT
- * longname entry. Also, we obviously can't set
- * any of the NTFS attributes in the high 24 bits.
- */
- attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR);
- /* Merge in ATTR_VOLUME and ATTR_DIR */
- attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
- (is_dir ? ATTR_DIR : 0);
- oldattr = fat_make_attrs(inode);
-
- /* Equivalent to a chmod() */
- ia.ia_valid = ATTR_MODE | ATTR_CTIME;
- ia.ia_ctime = current_fs_time(inode->i_sb);
- if (is_dir)
- ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
- else {
- ia.ia_mode = fat_make_mode(sbi, attr,
- S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
- }
+ if (sbi->options.sys_immutable &&
+ ((attr | oldattr) & ATTR_SYS) &&
+ !capable(CAP_LINUX_IMMUTABLE)) {
+ err = -EPERM;
+ goto out_drop_write;
+ }
- /* The root directory has no attributes */
- if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
- err = -EINVAL;
- goto up;
- }
+ /*
+ * The security check is questionable... We single
+ * out the RO attribute for checking by the security
+ * module, just because it maps to a file mode.
+ */
+ err = security_inode_setattr(file->f_path.dentry, &ia);
+ if (err)
+ goto out_drop_write;
- if (sbi->options.sys_immutable) {
- if ((attr | oldattr) & ATTR_SYS) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto up;
- }
- }
- }
+ /* This MUST be done before doing anything irreversible... */
+ err = fat_setattr(file->f_path.dentry, &ia);
+ if (err)
+ goto out_drop_write;
+
+ fsnotify_change(file->f_path.dentry, ia.ia_valid);
+ if (sbi->options.sys_immutable) {
+ if (attr & ATTR_SYS)
+ inode->i_flags |= S_IMMUTABLE;
+ else
+ inode->i_flags &= S_IMMUTABLE;
+ }
- /*
- * The security check is questionable... We single
- * out the RO attribute for checking by the security
- * module, just because it maps to a file mode.
- */
- err = security_inode_setattr(filp->f_path.dentry, &ia);
- if (err)
- goto up;
-
- /* This MUST be done before doing anything irreversible... */
- err = fat_setattr(filp->f_path.dentry, &ia);
- if (err)
- goto up;
-
- fsnotify_change(filp->f_path.dentry, ia.ia_valid);
- if (sbi->options.sys_immutable) {
- if (attr & ATTR_SYS)
- inode->i_flags |= S_IMMUTABLE;
- else
- inode->i_flags &= S_IMMUTABLE;
- }
+ fat_save_attrs(inode, attr);
+ mark_inode_dirty(inode);
+out_drop_write:
+ mnt_drop_write(file->f_path.mnt);
+out_unlock_inode:
+ mutex_unlock(&inode->i_mutex);
+out:
+ return err;
+}
- fat_save_attrs(inode, attr);
- mark_inode_dirty(inode);
-up:
- mnt_drop_write(filp->f_path.mnt);
-up_no_drop_write:
- mutex_unlock(&inode->i_mutex);
- return err;
- }
+int fat_generic_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ u32 __user *user_attr = (u32 __user *)arg;
+
+ switch (cmd) {
+ case FAT_IOCTL_GET_ATTRIBUTES:
+ return fat_ioctl_get_attributes(inode, user_attr);
+ case FAT_IOCTL_SET_ATTRIBUTES:
+ return fat_ioctl_set_attributes(filp, user_attr);
default:
return -ENOTTY; /* Inappropriate ioctl for device */
}
@@ -225,7 +231,7 @@ static int fat_free(struct inode *inode, int skip)
fatent_brelse(&fatent);
return 0;
} else if (ret == FAT_ENT_FREE) {
- fat_fs_panic(sb,
+ fat_fs_error(sb,
"%s: invalid cluster chain (i_pos %lld)",
__func__, MSDOS_I(inode)->i_pos);
ret = -EIO;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 51a5ecf9000..304b411cb8b 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -76,7 +76,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock,
return 0;
if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
- fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
+ fat_fs_error(sb, "corrupted file size (i_pos %lld, %lld)",
MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
return -EIO;
}
@@ -856,6 +856,12 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt)
seq_puts(m, ",flush");
if (opts->tz_utc)
seq_puts(m, ",tz=UTC");
+ if (opts->errors == FAT_ERRORS_CONT)
+ seq_puts(m, ",errors=continue");
+ else if (opts->errors == FAT_ERRORS_PANIC)
+ seq_puts(m, ",errors=panic");
+ else
+ seq_puts(m, ",errors=remount-ro");
return 0;
}
@@ -868,7 +874,8 @@ enum {
Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
- Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err,
+ Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont,
+ Opt_err_panic, Opt_err_ro, Opt_err,
};
static const match_table_t fat_tokens = {
@@ -891,6 +898,11 @@ static const match_table_t fat_tokens = {
{Opt_showexec, "showexec"},
{Opt_debug, "debug"},
{Opt_immutable, "sys_immutable"},
+ {Opt_flush, "flush"},
+ {Opt_tz_utc, "tz=UTC"},
+ {Opt_err_cont, "errors=continue"},
+ {Opt_err_panic, "errors=panic"},
+ {Opt_err_ro, "errors=remount-ro"},
{Opt_obsolate, "conv=binary"},
{Opt_obsolate, "conv=text"},
{Opt_obsolate, "conv=auto"},
@@ -902,8 +914,6 @@ static const match_table_t fat_tokens = {
{Opt_obsolate, "cvf_format=%20s"},
{Opt_obsolate, "cvf_options=%100s"},
{Opt_obsolate, "posix"},
- {Opt_flush, "flush"},
- {Opt_tz_utc, "tz=UTC"},
{Opt_err, NULL},
};
static const match_table_t msdos_tokens = {
@@ -973,6 +983,7 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
opts->numtail = 1;
opts->usefree = opts->nocase = 0;
opts->tz_utc = 0;
+ opts->errors = FAT_ERRORS_RO;
*debug = 0;
if (!options)
@@ -1065,6 +1076,15 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
case Opt_tz_utc:
opts->tz_utc = 1;
break;
+ case Opt_err_cont:
+ opts->errors = FAT_ERRORS_CONT;
+ break;
+ case Opt_err_panic:
+ opts->errors = FAT_ERRORS_PANIC;
+ break;
+ case Opt_err_ro:
+ opts->errors = FAT_ERRORS_RO;
+ break;
/* msdos specific */
case Opt_dots:
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index ac39ebcc149..a6c20473dfd 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -12,14 +12,19 @@
#include "fat.h"
/*
- * fat_fs_panic reports a severe file system problem and sets the file system
- * read-only. The file system can be made writable again by remounting it.
+ * fat_fs_error reports a file system problem that might indicate fa data
+ * corruption/inconsistency. Depending on 'errors' mount option the
+ * panic() is called, or error message is printed FAT and nothing is done,
+ * or filesystem is remounted read-only (default behavior).
+ * In case the file system is remounted read-only, it can be made writable
+ * again by remounting it.
*/
-void fat_fs_panic(struct super_block *s, const char *fmt, ...)
+void fat_fs_error(struct super_block *s, const char *fmt, ...)
{
+ struct fat_mount_options *opts = &MSDOS_SB(s)->options;
va_list args;
- printk(KERN_ERR "FAT: Filesystem panic (dev %s)\n", s->s_id);
+ printk(KERN_ERR "FAT: Filesystem error (dev %s)\n", s->s_id);
printk(KERN_ERR " ");
va_start(args, fmt);
@@ -27,13 +32,14 @@ void fat_fs_panic(struct super_block *s, const char *fmt, ...)
va_end(args);
printk("\n");
- if (!(s->s_flags & MS_RDONLY)) {
+ if (opts->errors == FAT_ERRORS_PANIC)
+ panic(" FAT fs panic from previous error\n");
+ else if (opts->errors == FAT_ERRORS_RO && !(s->s_flags & MS_RDONLY)) {
s->s_flags |= MS_RDONLY;
printk(KERN_ERR " File system has been set read-only\n");
}
}
-
-EXPORT_SYMBOL_GPL(fat_fs_panic);
+EXPORT_SYMBOL_GPL(fat_fs_error);
/* Flushes the number of free clusters on FAT32 */
/* XXX: Need to write one per FSINFO block. Currently only writes 1 */
@@ -124,7 +130,7 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
mark_inode_dirty(inode);
}
if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
- fat_fs_panic(sb, "clusters badly computed (%d != %llu)",
+ fat_fs_error(sb, "clusters badly computed (%d != %llu)",
new_fclus,
(llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
fat_cache_inval_inode(inode);
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index 20f52286135..82f88733b68 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -608,7 +608,7 @@ error_inode:
sinfo.bh = NULL;
}
if (corrupt < 0) {
- fat_fs_panic(new_dir->i_sb,
+ fat_fs_error(new_dir->i_sb,
"%s: Filesystem corrupted (i_pos %lld)",
__func__, sinfo.i_pos);
}
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index b50ecbe97f8..8d6fdcfd41d 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -1030,7 +1030,7 @@ error_inode:
sinfo.bh = NULL;
}
if (corrupt < 0) {
- fat_fs_panic(new_dir->i_sb,
+ fat_fs_error(new_dir->i_sb,
"%s: Filesystem corrupted (i_pos %lld)",
__func__, sinfo.i_pos);
}
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 3589eab02a2..3260b73abe2 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1937,6 +1937,9 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
err = bdi_init(&c->bdi);
if (err)
goto out_close;
+ err = bdi_register(&c->bdi, NULL, "ubifs");
+ if (err)
+ goto out_bdi;
err = ubifs_parse_options(c, data, 0);
if (err)
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 4db89e98535..82ec6a3c050 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -47,7 +47,7 @@
/* Current ACPICA subsystem version in YYYYMMDD format */
-#define ACPI_CA_VERSION 0x20090320
+#define ACPI_CA_VERSION 0x20090521
#include "actypes.h"
#include "actbl.h"
@@ -201,6 +201,8 @@ acpi_evaluate_object_typed(acpi_handle object,
acpi_status
acpi_get_object_info(acpi_handle handle, struct acpi_buffer *return_buffer);
+acpi_status acpi_install_method(u8 *buffer);
+
acpi_status
acpi_get_next_object(acpi_object_type type,
acpi_handle parent,
@@ -375,7 +377,7 @@ acpi_status acpi_leave_sleep_state_prep(u8 sleep_state);
acpi_status acpi_leave_sleep_state(u8 sleep_state);
/*
- * Debug output
+ * Error/Warning output
*/
void ACPI_INTERNAL_VAR_XFACE
acpi_error(const char *module_name,
@@ -394,6 +396,9 @@ void ACPI_INTERNAL_VAR_XFACE
acpi_info(const char *module_name,
u32 line_number, const char *format, ...) ACPI_PRINTF_LIKE(3);
+/*
+ * Debug output
+ */
#ifdef ACPI_DEBUG_OUTPUT
void ACPI_INTERNAL_VAR_XFACE
diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
index f555d927f7c..37ba576d06e 100644
--- a/include/acpi/actypes.h
+++ b/include/acpi/actypes.h
@@ -429,20 +429,12 @@ typedef unsigned long long acpi_integer;
/* Data manipulation */
-#define ACPI_LOWORD(l) ((u16)(u32)(l))
-#define ACPI_HIWORD(l) ((u16)((((u32)(l)) >> 16) & 0xFFFF))
-#define ACPI_LOBYTE(l) ((u8)(u16)(l))
-#define ACPI_HIBYTE(l) ((u8)((((u16)(l)) >> 8) & 0xFF))
-
-/* Full 64-bit integer must be available on both 32-bit and 64-bit platforms */
-
-struct acpi_integer_overlay {
- u32 lo_dword;
- u32 hi_dword;
-};
-
-#define ACPI_LODWORD(integer) (ACPI_CAST_PTR (struct acpi_integer_overlay, &integer)->lo_dword)
-#define ACPI_HIDWORD(integer) (ACPI_CAST_PTR (struct acpi_integer_overlay, &integer)->hi_dword)
+#define ACPI_LOBYTE(integer) ((u8) (u16)(integer))
+#define ACPI_HIBYTE(integer) ((u8) (((u16)(integer)) >> 8))
+#define ACPI_LOWORD(integer) ((u16) (u32)(integer))
+#define ACPI_HIWORD(integer) ((u16)(((u32)(integer)) >> 16))
+#define ACPI_LODWORD(integer64) ((u32) (u64)(integer64))
+#define ACPI_HIDWORD(integer64) ((u32)(((u64)(integer64)) >> 32))
#define ACPI_SET_BIT(target,bit) ((target) |= (bit))
#define ACPI_CLEAR_BIT(target,bit) ((target) &= ~(bit))
diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h
index 8e2cdc57b19..935c5d7fc86 100644
--- a/include/acpi/platform/acgcc.h
+++ b/include/acpi/platform/acgcc.h
@@ -62,4 +62,8 @@
*/
#define ACPI_UNUSED_VAR __attribute__ ((unused))
+#ifdef _ANSI
+#define inline
+#endif
+
#endif /* __ACGCC_H__ */
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 6d49b2a498c..fcb8e4b159b 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Name: aclinux.h - OS specific defines, etc.
+ * Name: aclinux.h - OS specific defines, etc. for Linux
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2008, Intel Corp.
+ * Copyright (C) 2000 - 2009, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,10 +44,13 @@
#ifndef __ACLINUX_H__
#define __ACLINUX_H__
+/* Common (in-kernel/user-space) ACPICA configuration */
+
#define ACPI_USE_SYSTEM_CLIBRARY
#define ACPI_USE_DO_WHILE_0
#define ACPI_MUTEX_TYPE ACPI_BINARY_SEMAPHORE
+
#ifdef __KERNEL__
#include <linux/string.h>
@@ -63,15 +66,18 @@
#include <linux/spinlock_types.h>
#include <asm/current.h>
-/* Host-dependent types and defines */
+/* Host-dependent types and defines for in-kernel ACPICA */
#define ACPI_MACHINE_WIDTH BITS_PER_LONG
-#define acpi_cache_t struct kmem_cache
-#define acpi_spinlock spinlock_t *
#define ACPI_EXPORT_SYMBOL(symbol) EXPORT_SYMBOL(symbol);
#define strtoul simple_strtoul
-#else /* !__KERNEL__ */
+#define acpi_cache_t struct kmem_cache
+#define acpi_spinlock spinlock_t *
+#define acpi_cpu_flags unsigned long
+#define acpi_thread_id struct task_struct *
+
+#else /* !__KERNEL__ */
#include <stdarg.h>
#include <string.h>
@@ -79,6 +85,11 @@
#include <ctype.h>
#include <unistd.h>
+/* Host-dependent types and defines for user-space ACPICA */
+
+#define ACPI_FLUSH_CPU_CACHE()
+#define acpi_thread_id pthread_t
+
#if defined(__ia64__) || defined(__x86_64__)
#define ACPI_MACHINE_WIDTH 64
#define COMPILER_DEPENDENT_INT64 long
@@ -94,17 +105,17 @@
#define __cdecl
#endif
-#define ACPI_FLUSH_CPU_CACHE()
-#endif /* __KERNEL__ */
+#endif /* __KERNEL__ */
/* Linux uses GCC */
#include "acgcc.h"
-#define acpi_cpu_flags unsigned long
-
-#define acpi_thread_id struct task_struct *
+#ifdef __KERNEL__
+/*
+ * Overrides for in-kernel ACPICA
+ */
static inline acpi_thread_id acpi_os_get_thread_id(void)
{
return current;
@@ -119,30 +130,32 @@ static inline acpi_thread_id acpi_os_get_thread_id(void)
#include <acpi/actypes.h>
static inline void *acpi_os_allocate(acpi_size size)
{
- return kmalloc(size, irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
+ return kmalloc(size, irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
}
+
static inline void *acpi_os_allocate_zeroed(acpi_size size)
{
- return kzalloc(size, irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
+ return kzalloc(size, irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
}
static inline void *acpi_os_acquire_object(acpi_cache_t * cache)
{
return kmem_cache_zalloc(cache,
- irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
+ irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
}
-#define ACPI_ALLOCATE(a) acpi_os_allocate(a)
-#define ACPI_ALLOCATE_ZEROED(a) acpi_os_allocate_zeroed(a)
-#define ACPI_FREE(a) kfree(a)
+#define ACPI_ALLOCATE(a) acpi_os_allocate(a)
+#define ACPI_ALLOCATE_ZEROED(a) acpi_os_allocate_zeroed(a)
+#define ACPI_FREE(a) kfree(a)
-/*
- * We need to show where it is safe to preempt execution of ACPICA
- */
-#define ACPI_PREEMPTION_POINT() \
- do { \
- if (!irqs_disabled()) \
- cond_resched(); \
+/* Used within ACPICA to show where it is safe to preempt execution */
+
+#define ACPI_PREEMPTION_POINT() \
+ do { \
+ if (!irqs_disabled()) \
+ cond_resched(); \
} while (0)
-#endif /* __ACLINUX_H__ */
+#endif /* __KERNEL__ */
+
+#endif /* __ACLINUX_H__ */
diff --git a/include/asm-generic/atomic64.h b/include/asm-generic/atomic64.h
new file mode 100644
index 00000000000..b18ce4f9ee3
--- /dev/null
+++ b/include/asm-generic/atomic64.h
@@ -0,0 +1,42 @@
+/*
+ * Generic implementation of 64-bit atomics using spinlocks,
+ * useful on processors that don't have 64-bit atomic instructions.
+ *
+ * Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
+ *
+ * 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.
+ */
+#ifndef _ASM_GENERIC_ATOMIC64_H
+#define _ASM_GENERIC_ATOMIC64_H
+
+typedef struct {
+ long long counter;
+} atomic64_t;
+
+#define ATOMIC64_INIT(i) { (i) }
+
+extern long long atomic64_read(const atomic64_t *v);
+extern void atomic64_set(atomic64_t *v, long long i);
+extern void atomic64_add(long long a, atomic64_t *v);
+extern long long atomic64_add_return(long long a, atomic64_t *v);
+extern void atomic64_sub(long long a, atomic64_t *v);
+extern long long atomic64_sub_return(long long a, atomic64_t *v);
+extern long long atomic64_dec_if_positive(atomic64_t *v);
+extern long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n);
+extern long long atomic64_xchg(atomic64_t *v, long long new);
+extern int atomic64_add_unless(atomic64_t *v, long long a, long long u);
+
+#define atomic64_add_negative(a, v) (atomic64_add_return((a), (v)) < 0)
+#define atomic64_inc(v) atomic64_add(1LL, (v))
+#define atomic64_inc_return(v) atomic64_add_return(1LL, (v))
+#define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0)
+#define atomic64_sub_and_test(a, v) (atomic64_sub_return((a), (v)) == 0)
+#define atomic64_dec(v) atomic64_sub(1LL, (v))
+#define atomic64_dec_return(v) atomic64_sub_return(1LL, (v))
+#define atomic64_dec_and_test(v) (atomic64_dec_return((v)) == 0)
+#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1LL, 0LL)
+
+#endif /* _ASM_GENERIC_ATOMIC64_H */
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 12737be5860..2a04eb54c0d 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -590,6 +590,11 @@ static inline void bio_list_merge_head(struct bio_list *bl,
bl->head = bl2->head;
}
+static inline struct bio *bio_list_peek(struct bio_list *bl)
+{
+ return bl->head;
+}
+
static inline struct bio *bio_list_pop(struct bio_list *bl)
{
struct bio *bio = bl->head;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 0b1a6cae9de..8963d9149b5 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -926,6 +926,7 @@ extern void blk_queue_alignment_offset(struct request_queue *q,
unsigned int alignment);
extern void blk_queue_io_min(struct request_queue *q, unsigned int min);
extern void blk_queue_io_opt(struct request_queue *q, unsigned int opt);
+extern void blk_set_default_limits(struct queue_limits *lim);
extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
sector_t offset);
extern void disk_stack_limits(struct gendisk *disk, struct block_device *bdev,
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 883cd44ff76..1b2e1747df1 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -97,12 +97,14 @@ extern const char linux_proc_banner[];
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
+/* Use the default kernel loglevel */
+#define KERN_DEFAULT "<d>"
/*
* Annotation for a "continued" line of log printout (only done after a
* line that had no enclosing \n). Only to be used by core/arch code
* during early bootup (a continued line is not SMP-safe otherwise).
*/
-#define KERN_CONT ""
+#define KERN_CONT "<c>"
extern int console_printk[];
diff --git a/include/linux/mg_disk.h b/include/linux/mg_disk.h
new file mode 100644
index 00000000000..e11f4d9f1c2
--- /dev/null
+++ b/include/linux/mg_disk.h
@@ -0,0 +1,45 @@
+/*
+ * include/linux/mg_disk.c
+ *
+ * Private data for mflash platform driver
+ *
+ * (c) 2008 mGine Co.,LTD
+ * (c) 2008 unsik Kim <donari75@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MG_DISK_H__
+#define __MG_DISK_H__
+
+/* name for platform device */
+#define MG_DEV_NAME "mg_disk"
+
+/* names of GPIO resource */
+#define MG_RST_PIN "mg_rst"
+/* except MG_BOOT_DEV, reset-out pin should be assigned */
+#define MG_RSTOUT_PIN "mg_rstout"
+
+/* device attribution */
+/* use mflash as boot device */
+#define MG_BOOT_DEV (1 << 0)
+/* use mflash as storage device */
+#define MG_STORAGE_DEV (1 << 1)
+/* same as MG_STORAGE_DEV, but bootloader already done reset sequence */
+#define MG_STORAGE_DEV_SKIP_RST (1 << 2)
+
+/* private driver data */
+struct mg_drv_data {
+ /* disk resource */
+ u32 use_polling;
+
+ /* device attribution */
+ u32 dev_attr;
+
+ /* internally used */
+ void *host;
+};
+
+#endif
diff --git a/kernel/module.c b/kernel/module.c
index e4ab36ce767..215aaab09e9 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2899,7 +2899,7 @@ void print_modules(void)
struct module *mod;
char buf[8];
- printk("Modules linked in:");
+ printk(KERN_DEFAULT "Modules linked in:");
/* Most callers should already have preempt disabled, but make sure */
preempt_disable();
list_for_each_entry_rcu(mod, &modules, list)
diff --git a/kernel/printk.c b/kernel/printk.c
index 5052b5497c6..b4d97b54c1e 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -687,20 +687,35 @@ asmlinkage int vprintk(const char *fmt, va_list args)
sizeof(printk_buf) - printed_len, fmt, args);
+ p = printk_buf;
+
+ /* Do we have a loglevel in the string? */
+ if (p[0] == '<') {
+ unsigned char c = p[1];
+ if (c && p[2] == '>') {
+ switch (c) {
+ case '0' ... '7': /* loglevel */
+ current_log_level = c - '0';
+ /* Fallthrough - make sure we're on a new line */
+ case 'd': /* KERN_DEFAULT */
+ if (!new_text_line) {
+ emit_log_char('\n');
+ new_text_line = 1;
+ }
+ /* Fallthrough - skip the loglevel */
+ case 'c': /* KERN_CONT */
+ p += 3;
+ break;
+ }
+ }
+ }
+
/*
* Copy the output into log_buf. If the caller didn't provide
* appropriate log level tags, we insert them here
*/
- for (p = printk_buf; *p; p++) {
+ for ( ; *p; p++) {
if (new_text_line) {
- /* If a token, set current_log_level and skip over */
- if (p[0] == '<' && p[1] >= '0' && p[1] <= '7' &&
- p[2] == '>') {
- current_log_level = p[1] - '0';
- p += 3;
- printed_len -= 3;
- }
-
/* Always output the token */
emit_log_char('<');
emit_log_char(current_log_level + '0');
diff --git a/lib/Kconfig b/lib/Kconfig
index 9960be04cbb..bb1326d3839 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -194,4 +194,10 @@ config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
config NLATTR
bool
+#
+# Generic 64-bit atomic support is selected if needed
+#
+config GENERIC_ATOMIC64
+ bool
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 34c5c0e6222..8e9bcf9d326 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -95,6 +95,8 @@ obj-$(CONFIG_DMA_API_DEBUG) += dma-debug.o
obj-$(CONFIG_GENERIC_CSUM) += checksum.o
+obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h
diff --git a/lib/atomic64.c b/lib/atomic64.c
new file mode 100644
index 00000000000..c5e72556241
--- /dev/null
+++ b/lib/atomic64.c
@@ -0,0 +1,175 @@
+/*
+ * Generic implementation of 64-bit atomics using spinlocks,
+ * useful on processors that don't have 64-bit atomic instructions.
+ *
+ * Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/cache.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <asm/atomic.h>
+
+/*
+ * We use a hashed array of spinlocks to provide exclusive access
+ * to each atomic64_t variable. Since this is expected to used on
+ * systems with small numbers of CPUs (<= 4 or so), we use a
+ * relatively small array of 16 spinlocks to avoid wasting too much
+ * memory on the spinlock array.
+ */
+#define NR_LOCKS 16
+
+/*
+ * Ensure each lock is in a separate cacheline.
+ */
+static union {
+ spinlock_t lock;
+ char pad[L1_CACHE_BYTES];
+} atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp;
+
+static inline spinlock_t *lock_addr(const atomic64_t *v)
+{
+ unsigned long addr = (unsigned long) v;
+
+ addr >>= L1_CACHE_SHIFT;
+ addr ^= (addr >> 8) ^ (addr >> 16);
+ return &atomic64_lock[addr & (NR_LOCKS - 1)].lock;
+}
+
+long long atomic64_read(const atomic64_t *v)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+ long long val;
+
+ spin_lock_irqsave(lock, flags);
+ val = v->counter;
+ spin_unlock_irqrestore(lock, flags);
+ return val;
+}
+
+void atomic64_set(atomic64_t *v, long long i)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+
+ spin_lock_irqsave(lock, flags);
+ v->counter = i;
+ spin_unlock_irqrestore(lock, flags);
+}
+
+void atomic64_add(long long a, atomic64_t *v)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+
+ spin_lock_irqsave(lock, flags);
+ v->counter += a;
+ spin_unlock_irqrestore(lock, flags);
+}
+
+long long atomic64_add_return(long long a, atomic64_t *v)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+ long long val;
+
+ spin_lock_irqsave(lock, flags);
+ val = v->counter += a;
+ spin_unlock_irqrestore(lock, flags);
+ return val;
+}
+
+void atomic64_sub(long long a, atomic64_t *v)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+
+ spin_lock_irqsave(lock, flags);
+ v->counter -= a;
+ spin_unlock_irqrestore(lock, flags);
+}
+
+long long atomic64_sub_return(long long a, atomic64_t *v)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+ long long val;
+
+ spin_lock_irqsave(lock, flags);
+ val = v->counter -= a;
+ spin_unlock_irqrestore(lock, flags);
+ return val;
+}
+
+long long atomic64_dec_if_positive(atomic64_t *v)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+ long long val;
+
+ spin_lock_irqsave(lock, flags);
+ val = v->counter - 1;
+ if (val >= 0)
+ v->counter = val;
+ spin_unlock_irqrestore(lock, flags);
+ return val;
+}
+
+long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+ long long val;
+
+ spin_lock_irqsave(lock, flags);
+ val = v->counter;
+ if (val == o)
+ v->counter = n;
+ spin_unlock_irqrestore(lock, flags);
+ return val;
+}
+
+long long atomic64_xchg(atomic64_t *v, long long new)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+ long long val;
+
+ spin_lock_irqsave(lock, flags);
+ val = v->counter;
+ v->counter = new;
+ spin_unlock_irqrestore(lock, flags);
+ return val;
+}
+
+int atomic64_add_unless(atomic64_t *v, long long a, long long u)
+{
+ unsigned long flags;
+ spinlock_t *lock = lock_addr(v);
+ int ret = 1;
+
+ spin_lock_irqsave(lock, flags);
+ if (v->counter != u) {
+ v->counter += a;
+ ret = 0;
+ }
+ spin_unlock_irqrestore(lock, flags);
+ return ret;
+}
+
+static int init_atomic64_lock(void)
+{
+ int i;
+
+ for (i = 0; i < NR_LOCKS; ++i)
+ spin_lock_init(&atomic64_lock[i].lock);
+ return 0;
+}
+
+pure_initcall(init_atomic64_lock);
diff --git a/mm/Kconfig b/mm/Kconfig
index 71830ba7b98..6f4610a9ce5 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -128,11 +128,11 @@ config SPARSEMEM_VMEMMAP
config MEMORY_HOTPLUG
bool "Allow for memory hot-add"
depends on SPARSEMEM || X86_64_ACPI_NUMA
- depends on HOTPLUG && !HIBERNATION && ARCH_ENABLE_MEMORY_HOTPLUG
+ depends on HOTPLUG && !(HIBERNATION && !S390) && ARCH_ENABLE_MEMORY_HOTPLUG
depends on (IA64 || X86 || PPC64 || SUPERH || S390)
comment "Memory hotplug is currently incompatible with Software Suspend"
- depends on SPARSEMEM && HOTPLUG && HIBERNATION
+ depends on SPARSEMEM && HOTPLUG && HIBERNATION && !S390
config MEMORY_HOTPLUG_SPARSE
def_bool y
diff --git a/mm/bounce.c b/mm/bounce.c
index 4ebe3ea8379..a2b76a588e3 100644
--- a/mm/bounce.c
+++ b/mm/bounce.c
@@ -13,7 +13,6 @@
#include <linux/init.h>
#include <linux/hash.h>
#include <linux/highmem.h>
-#include <linux/blktrace_api.h>
#include <asm/tlbflush.h>
#include <trace/events/block.h>
diff --git a/mm/highmem.c b/mm/highmem.c
index 68eb1d9b63f..25878cc49da 100644
--- a/mm/highmem.c
+++ b/mm/highmem.c
@@ -26,7 +26,6 @@
#include <linux/init.h>
#include <linux/hash.h>
#include <linux/highmem.h>
-#include <linux/blktrace_api.h>
#include <asm/tlbflush.h>
/*
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index a9b3a6f9ea9..656cbd19582 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -1,11 +1,12 @@
/*
- * linux/net/iucv/af_iucv.c
- *
* IUCV protocol stack for Linux on zSeries
*
- * Copyright 2006 IBM Corporation
+ * Copyright IBM Corp. 2006, 2009
*
* Author(s): Jennifer Hunt <jenhunt@us.ibm.com>
+ * Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ * PM functions:
+ * Ursula Braun <ursula.braun@de.ibm.com>
*/
#define KMSG_COMPONENT "af_iucv"
@@ -90,6 +91,122 @@ static inline void low_nmcpy(unsigned char *dst, char *src)
memcpy(&dst[8], src, 8);
}
+static int afiucv_pm_prepare(struct device *dev)
+{
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "afiucv_pm_prepare\n");
+#endif
+ return 0;
+}
+
+static void afiucv_pm_complete(struct device *dev)
+{
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "afiucv_pm_complete\n");
+#endif
+ return;
+}
+
+/**
+ * afiucv_pm_freeze() - Freeze PM callback
+ * @dev: AFIUCV dummy device
+ *
+ * Sever all established IUCV communication pathes
+ */
+static int afiucv_pm_freeze(struct device *dev)
+{
+ struct iucv_sock *iucv;
+ struct sock *sk;
+ struct hlist_node *node;
+ int err = 0;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "afiucv_pm_freeze\n");
+#endif
+ read_lock(&iucv_sk_list.lock);
+ sk_for_each(sk, node, &iucv_sk_list.head) {
+ iucv = iucv_sk(sk);
+ skb_queue_purge(&iucv->send_skb_q);
+ skb_queue_purge(&iucv->backlog_skb_q);
+ switch (sk->sk_state) {
+ case IUCV_SEVERED:
+ case IUCV_DISCONN:
+ case IUCV_CLOSING:
+ case IUCV_CONNECTED:
+ if (iucv->path) {
+ err = iucv_path_sever(iucv->path, NULL);
+ iucv_path_free(iucv->path);
+ iucv->path = NULL;
+ }
+ break;
+ case IUCV_OPEN:
+ case IUCV_BOUND:
+ case IUCV_LISTEN:
+ case IUCV_CLOSED:
+ default:
+ break;
+ }
+ }
+ read_unlock(&iucv_sk_list.lock);
+ return err;
+}
+
+/**
+ * afiucv_pm_restore_thaw() - Thaw and restore PM callback
+ * @dev: AFIUCV dummy device
+ *
+ * socket clean up after freeze
+ */
+static int afiucv_pm_restore_thaw(struct device *dev)
+{
+ struct iucv_sock *iucv;
+ struct sock *sk;
+ struct hlist_node *node;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "afiucv_pm_restore_thaw\n");
+#endif
+ read_lock(&iucv_sk_list.lock);
+ sk_for_each(sk, node, &iucv_sk_list.head) {
+ iucv = iucv_sk(sk);
+ switch (sk->sk_state) {
+ case IUCV_CONNECTED:
+ sk->sk_err = EPIPE;
+ sk->sk_state = IUCV_DISCONN;
+ sk->sk_state_change(sk);
+ break;
+ case IUCV_DISCONN:
+ case IUCV_SEVERED:
+ case IUCV_CLOSING:
+ case IUCV_LISTEN:
+ case IUCV_BOUND:
+ case IUCV_OPEN:
+ default:
+ break;
+ }
+ }
+ read_unlock(&iucv_sk_list.lock);
+ return 0;
+}
+
+static struct dev_pm_ops afiucv_pm_ops = {
+ .prepare = afiucv_pm_prepare,
+ .complete = afiucv_pm_complete,
+ .freeze = afiucv_pm_freeze,
+ .thaw = afiucv_pm_restore_thaw,
+ .restore = afiucv_pm_restore_thaw,
+};
+
+static struct device_driver af_iucv_driver = {
+ .owner = THIS_MODULE,
+ .name = "afiucv",
+ .bus = &iucv_bus,
+ .pm = &afiucv_pm_ops,
+};
+
+/* dummy device used as trigger for PM functions */
+static struct device *af_iucv_dev;
+
/**
* iucv_msg_length() - Returns the length of an iucv message.
* @msg: Pointer to struct iucv_message, MUST NOT be NULL
@@ -1556,8 +1673,30 @@ static int __init afiucv_init(void)
err = sock_register(&iucv_sock_family_ops);
if (err)
goto out_proto;
+ /* establish dummy device */
+ err = driver_register(&af_iucv_driver);
+ if (err)
+ goto out_sock;
+ af_iucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!af_iucv_dev) {
+ err = -ENOMEM;
+ goto out_driver;
+ }
+ dev_set_name(af_iucv_dev, "af_iucv");
+ af_iucv_dev->bus = &iucv_bus;
+ af_iucv_dev->parent = iucv_root;
+ af_iucv_dev->release = (void (*)(struct device *))kfree;
+ af_iucv_dev->driver = &af_iucv_driver;
+ err = device_register(af_iucv_dev);
+ if (err)
+ goto out_driver;
+
return 0;
+out_driver:
+ driver_unregister(&af_iucv_driver);
+out_sock:
+ sock_unregister(PF_IUCV);
out_proto:
proto_unregister(&iucv_proto);
out_iucv:
@@ -1568,6 +1707,8 @@ out:
static void __exit afiucv_exit(void)
{
+ device_unregister(af_iucv_dev);
+ driver_unregister(&af_iucv_driver);
sock_unregister(PF_IUCV);
proto_unregister(&iucv_proto);
iucv_unregister(&af_iucv_handler, 0);
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index 61e8038a55e..c833481d32e 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -1,7 +1,8 @@
/*
* IUCV base infrastructure.
*
- * Copyright 2001, 2006 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 2001, 2009
+ *
* Author(s):
* Original source:
* Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000
@@ -10,6 +11,8 @@
* Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
* Rewritten for af_iucv:
* Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * PM functions:
+ * Ursula Braun (ursula.braun@de.ibm.com)
*
* Documentation used:
* The original source
@@ -45,6 +48,7 @@
#include <linux/err.h>
#include <linux/device.h>
#include <linux/cpu.h>
+#include <linux/reboot.h>
#include <net/iucv/iucv.h>
#include <asm/atomic.h>
#include <asm/ebcdic.h>
@@ -75,9 +79,24 @@ static int iucv_bus_match(struct device *dev, struct device_driver *drv)
return 0;
}
+static int iucv_pm_prepare(struct device *);
+static void iucv_pm_complete(struct device *);
+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 = {
+ .prepare = iucv_pm_prepare,
+ .complete = iucv_pm_complete,
+ .freeze = iucv_pm_freeze,
+ .thaw = iucv_pm_thaw,
+ .restore = iucv_pm_restore,
+};
+
struct bus_type iucv_bus = {
.name = "iucv",
.match = iucv_bus_match,
+ .pm = &iucv_pm_ops,
};
EXPORT_SYMBOL(iucv_bus);
@@ -147,6 +166,7 @@ enum iucv_command_codes {
IUCV_RESUME = 14,
IUCV_SEVER = 15,
IUCV_SETMASK = 16,
+ IUCV_SETCONTROLMASK = 17,
};
/*
@@ -364,6 +384,18 @@ static void iucv_allow_cpu(void *data)
parm->set_mask.ipmask = 0xf8;
iucv_call_b2f0(IUCV_SETMASK, parm);
+ /*
+ * Enable all iucv control interrupts.
+ * ipmask contains bits for the different interrupts
+ * 0x80 - Flag to allow pending connections interrupts
+ * 0x40 - Flag to allow connection complete interrupts
+ * 0x20 - Flag to allow connection severed interrupts
+ * 0x10 - Flag to allow connection quiesced interrupts
+ * 0x08 - Flag to allow connection resumed interrupts
+ */
+ memset(parm, 0, sizeof(union iucv_param));
+ parm->set_mask.ipmask = 0xf8;
+ iucv_call_b2f0(IUCV_SETCONTROLMASK, parm);
/* Set indication that iucv interrupts are allowed for this cpu. */
cpu_set(cpu, iucv_irq_cpumask);
}
@@ -389,6 +421,31 @@ static void iucv_block_cpu(void *data)
}
/**
+ * iucv_block_cpu_almost
+ * @data: unused
+ *
+ * Allow connection-severed interrupts only on this cpu.
+ */
+static void iucv_block_cpu_almost(void *data)
+{
+ int cpu = smp_processor_id();
+ union iucv_param *parm;
+
+ /* Allow iucv control interrupts only */
+ parm = iucv_param_irq[cpu];
+ memset(parm, 0, sizeof(union iucv_param));
+ parm->set_mask.ipmask = 0x08;
+ iucv_call_b2f0(IUCV_SETMASK, parm);
+ /* Allow iucv-severed interrupt only */
+ memset(parm, 0, sizeof(union iucv_param));
+ parm->set_mask.ipmask = 0x20;
+ iucv_call_b2f0(IUCV_SETCONTROLMASK, parm);
+
+ /* Clear indication that iucv interrupts are allowed for this cpu. */
+ cpu_clear(cpu, iucv_irq_cpumask);
+}
+
+/**
* iucv_declare_cpu
* @data: unused
*
@@ -758,6 +815,28 @@ void iucv_unregister(struct iucv_handler *handler, int smp)
}
EXPORT_SYMBOL(iucv_unregister);
+static int iucv_reboot_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ int i, rc;
+
+ get_online_cpus();
+ on_each_cpu(iucv_block_cpu, NULL, 1);
+ preempt_disable();
+ for (i = 0; i < iucv_max_pathid; i++) {
+ if (iucv_path_table[i])
+ rc = iucv_sever_pathid(i, NULL);
+ }
+ preempt_enable();
+ put_online_cpus();
+ iucv_disable();
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block iucv_reboot_notifier = {
+ .notifier_call = iucv_reboot_event,
+};
+
/**
* iucv_path_accept
* @path: address of iucv path structure
@@ -777,6 +856,10 @@ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
int rc;
local_bh_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
/* Prepare parameter block. */
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
@@ -792,6 +875,7 @@ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
path->msglim = parm->ctrl.ipmsglim;
path->flags = parm->ctrl.ipflags1;
}
+out:
local_bh_enable();
return rc;
}
@@ -821,6 +905,10 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
spin_lock_bh(&iucv_table_lock);
iucv_cleanup_queue();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
parm->ctrl.ipmsglim = path->msglim;
@@ -855,6 +943,7 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
rc = -EIO;
}
}
+out:
spin_unlock_bh(&iucv_table_lock);
return rc;
}
@@ -876,12 +965,17 @@ int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16])
int rc;
local_bh_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
if (userdata)
memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
parm->ctrl.ippathid = path->pathid;
rc = iucv_call_b2f0(IUCV_QUIESCE, parm);
+out:
local_bh_enable();
return rc;
}
@@ -903,12 +997,17 @@ int iucv_path_resume(struct iucv_path *path, u8 userdata[16])
int rc;
local_bh_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
if (userdata)
memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
parm->ctrl.ippathid = path->pathid;
rc = iucv_call_b2f0(IUCV_RESUME, parm);
+out:
local_bh_enable();
return rc;
}
@@ -927,6 +1026,10 @@ int iucv_path_sever(struct iucv_path *path, u8 userdata[16])
int rc;
preempt_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
if (iucv_active_cpu != smp_processor_id())
spin_lock_bh(&iucv_table_lock);
rc = iucv_sever_pathid(path->pathid, userdata);
@@ -934,6 +1037,7 @@ int iucv_path_sever(struct iucv_path *path, u8 userdata[16])
list_del_init(&path->list);
if (iucv_active_cpu != smp_processor_id())
spin_unlock_bh(&iucv_table_lock);
+out:
preempt_enable();
return rc;
}
@@ -956,6 +1060,10 @@ int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
int rc;
local_bh_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
parm->purge.ippathid = path->pathid;
@@ -967,6 +1075,7 @@ int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8;
msg->tag = parm->purge.ipmsgtag;
}
+out:
local_bh_enable();
return rc;
}
@@ -1043,6 +1152,10 @@ int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
if (msg->flags & IUCV_IPRMDATA)
return iucv_message_receive_iprmdata(path, msg, flags,
buffer, size, residual);
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
parm->db.ipbfadr1 = (u32)(addr_t) buffer;
@@ -1058,6 +1171,7 @@ int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
if (residual)
*residual = parm->db.ipbfln1f;
}
+out:
return rc;
}
EXPORT_SYMBOL(__iucv_message_receive);
@@ -1111,6 +1225,10 @@ int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg)
int rc;
local_bh_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
parm->db.ippathid = path->pathid;
@@ -1118,6 +1236,7 @@ int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg)
parm->db.iptrgcls = msg->class;
parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID);
rc = iucv_call_b2f0(IUCV_REJECT, parm);
+out:
local_bh_enable();
return rc;
}
@@ -1145,6 +1264,10 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
int rc;
local_bh_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
if (flags & IUCV_IPRMDATA) {
@@ -1162,6 +1285,7 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
parm->db.iptrgcls = msg->class;
}
rc = iucv_call_b2f0(IUCV_REPLY, parm);
+out:
local_bh_enable();
return rc;
}
@@ -1190,6 +1314,10 @@ int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
union iucv_param *parm;
int rc;
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
if (flags & IUCV_IPRMDATA) {
@@ -1212,6 +1340,7 @@ int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
rc = iucv_call_b2f0(IUCV_SEND, parm);
if (!rc)
msg->id = parm->db.ipmsgid;
+out:
return rc;
}
EXPORT_SYMBOL(__iucv_message_send);
@@ -1272,6 +1401,10 @@ int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg,
int rc;
local_bh_disable();
+ if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) {
+ rc = -EIO;
+ goto out;
+ }
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
if (flags & IUCV_IPRMDATA) {
@@ -1297,6 +1430,7 @@ int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg,
rc = iucv_call_b2f0(IUCV_SEND, parm);
if (!rc)
msg->id = parm->db.ipmsgid;
+out:
local_bh_enable();
return rc;
}
@@ -1687,6 +1821,130 @@ static void iucv_external_interrupt(u16 code)
spin_unlock(&iucv_queue_lock);
}
+static int iucv_pm_prepare(struct device *dev)
+{
+ int rc = 0;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_INFO "iucv_pm_prepare\n");
+#endif
+ if (dev->driver && dev->driver->pm && dev->driver->pm->prepare)
+ rc = dev->driver->pm->prepare(dev);
+ return rc;
+}
+
+static void iucv_pm_complete(struct device *dev)
+{
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_INFO "iucv_pm_complete\n");
+#endif
+ if (dev->driver && dev->driver->pm && dev->driver->pm->complete)
+ dev->driver->pm->complete(dev);
+}
+
+/**
+ * iucv_path_table_empty() - determine if iucv path table is empty
+ *
+ * Returns 0 if there are still iucv pathes defined
+ * 1 if there are no iucv pathes defined
+ */
+int iucv_path_table_empty(void)
+{
+ int i;
+
+ for (i = 0; i < iucv_max_pathid; i++) {
+ if (iucv_path_table[i])
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * iucv_pm_freeze() - Freeze PM callback
+ * @dev: iucv-based device
+ *
+ * disable iucv interrupts
+ * invoke callback function of the iucv-based driver
+ * shut down iucv, if no iucv-pathes are established anymore
+ */
+static int iucv_pm_freeze(struct device *dev)
+{
+ int cpu;
+ int rc = 0;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "iucv_pm_freeze\n");
+#endif
+ for_each_cpu_mask_nr(cpu, iucv_irq_cpumask)
+ smp_call_function_single(cpu, iucv_block_cpu_almost, NULL, 1);
+ if (dev->driver && dev->driver->pm && dev->driver->pm->freeze)
+ rc = dev->driver->pm->freeze(dev);
+ if (iucv_path_table_empty())
+ iucv_disable();
+ return rc;
+}
+
+/**
+ * iucv_pm_thaw() - Thaw PM callback
+ * @dev: iucv-based device
+ *
+ * make iucv ready for use again: allocate path table, declare interrupt buffers
+ * and enable iucv interrupts
+ * invoke callback function of the iucv-based driver
+ */
+static int iucv_pm_thaw(struct device *dev)
+{
+ int rc = 0;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "iucv_pm_thaw\n");
+#endif
+ if (!iucv_path_table) {
+ rc = iucv_enable();
+ if (rc)
+ goto out;
+ }
+ if (cpus_empty(iucv_irq_cpumask)) {
+ if (iucv_nonsmp_handler)
+ /* enable interrupts on one cpu */
+ iucv_allow_cpu(NULL);
+ else
+ /* enable interrupts on all cpus */
+ iucv_setmask_mp();
+ }
+ if (dev->driver && dev->driver->pm && dev->driver->pm->thaw)
+ rc = dev->driver->pm->thaw(dev);
+out:
+ return rc;
+}
+
+/**
+ * iucv_pm_restore() - Restore PM callback
+ * @dev: iucv-based device
+ *
+ * make iucv ready for use again: allocate path table, declare interrupt buffers
+ * and enable iucv interrupts
+ * invoke callback function of the iucv-based driver
+ */
+static int iucv_pm_restore(struct device *dev)
+{
+ int rc = 0;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "iucv_pm_restore %p\n", iucv_path_table);
+#endif
+ if (cpus_empty(iucv_irq_cpumask)) {
+ rc = iucv_query_maxconn();
+ rc = iucv_enable();
+ if (rc)
+ goto out;
+ }
+ if (dev->driver && dev->driver->pm && dev->driver->pm->restore)
+ rc = dev->driver->pm->restore(dev);
+out:
+ return rc;
+}
+
/**
* iucv_init
*
@@ -1740,15 +1998,20 @@ static int __init iucv_init(void)
rc = register_hotcpu_notifier(&iucv_cpu_notifier);
if (rc)
goto out_free;
+ rc = register_reboot_notifier(&iucv_reboot_notifier);
+ if (rc)
+ goto out_cpu;
ASCEBC(iucv_error_no_listener, 16);
ASCEBC(iucv_error_no_memory, 16);
ASCEBC(iucv_error_pathid, 16);
iucv_available = 1;
rc = bus_register(&iucv_bus);
if (rc)
- goto out_cpu;
+ goto out_reboot;
return 0;
+out_reboot:
+ unregister_reboot_notifier(&iucv_reboot_notifier);
out_cpu:
unregister_hotcpu_notifier(&iucv_cpu_notifier);
out_free:
@@ -1783,6 +2046,7 @@ static void __exit iucv_exit(void)
list_for_each_entry_safe(p, n, &iucv_work_queue, list)
kfree(p);
spin_unlock_irq(&iucv_queue_lock);
+ unregister_reboot_notifier(&iucv_reboot_notifier);
unregister_hotcpu_notifier(&iucv_cpu_notifier);
for_each_possible_cpu(cpu) {
kfree(iucv_param_irq[cpu]);