Merge branch 'fix/hda-samsung-p50' into for-linus
Takashi Iwai [Mon, 29 Jun 2009 16:05:04 +0000 (18:05 +0200)]
* fix/hda-samsung-p50:
  ALSA: hda - Fix support for Samsung P50 with AD1986A codec
  ALSA: hda - Generalize the pin-detect quirk for Lenovo N100
  ALSA: hda - Simplify AD1986A mixer definitions

300 files changed:
Documentation/device-mapper/dm-log.txt [new file with mode: 0644]
Documentation/device-mapper/dm-queue-length.txt [new file with mode: 0644]
Documentation/device-mapper/dm-service-time.txt [new file with mode: 0644]
Documentation/filesystems/Locking
Documentation/kernel-parameters.txt
Documentation/laptops/thinkpad-acpi.txt
MAINTAINERS
Makefile
arch/ia64/kernel/acpi-processor.c
arch/mips/Kconfig
arch/mips/cavium-octeon/octeon-irq.c
arch/mips/cavium-octeon/octeon_boot.h [new file with mode: 0644]
arch/mips/cavium-octeon/setup.c
arch/mips/cavium-octeon/smp.c
arch/mips/include/asm/bug.h
arch/mips/include/asm/bugs.h
arch/mips/include/asm/irq.h
arch/mips/include/asm/mmu_context.h
arch/mips/include/asm/smp-ops.h
arch/mips/include/asm/smp.h
arch/mips/include/asm/sn/addrs.h
arch/mips/jazz/irq.c
arch/mips/kernel/cevt-bcm1480.c
arch/mips/kernel/cevt-r4k.c
arch/mips/kernel/cevt-sb1250.c
arch/mips/kernel/cevt-smtc.c
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/i8253.c
arch/mips/kernel/irq-gic.c
arch/mips/kernel/kgdb.c
arch/mips/kernel/process.c
arch/mips/kernel/smp-cmp.c
arch/mips/kernel/smp-up.c
arch/mips/kernel/smp.c
arch/mips/kernel/smtc.c
arch/mips/kernel/topology.c
arch/mips/mipssim/sim_time.c
arch/mips/mm/c-octeon.c
arch/mips/mm/c-r3k.c
arch/mips/mm/c-r4k.c
arch/mips/mm/c-tx39.c
arch/mips/mm/highmem.c
arch/mips/mm/init.c
arch/mips/mm/page.c
arch/mips/mm/tlb-r3k.c
arch/mips/mm/tlb-r4k.c
arch/mips/mm/tlb-r8k.c
arch/mips/mm/tlbex.c
arch/mips/mti-malta/malta-int.c
arch/mips/pci/pci-ip27.c
arch/mips/pmc-sierra/yosemite/smp.c
arch/mips/power/hibernate.S
arch/mips/sgi-ip27/ip27-init.c
arch/mips/sgi-ip27/ip27-irq.c
arch/mips/sgi-ip27/ip27-timer.c
arch/mips/sgi-ip27/ip27-xtalk.c
arch/mips/sibyte/bcm1480/irq.c
arch/mips/sibyte/common/cfe_console.c
arch/mips/sni/time.c
arch/x86/include/asm/acpi.h
arch/x86/include/asm/pci_x86.h
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/acpi/cstate.c
arch/x86/kernel/acpi/processor.c
arch/x86/kernel/apic/io_apic.c
arch/x86/pci/acpi.c
arch/x86/pci/amd_bus.c
arch/x86/pci/common.c
arch/x86/pci/mmconfig-shared.c
drivers/acpi/ac.c
drivers/acpi/battery.c
drivers/acpi/blacklist.c
drivers/acpi/bus.c
drivers/acpi/glue.c
drivers/acpi/osl.c
drivers/acpi/pci_bind.c
drivers/acpi/pci_irq.c
drivers/acpi/pci_root.c
drivers/acpi/power.c
drivers/acpi/processor_core.c
drivers/acpi/processor_idle.c
drivers/acpi/scan.c
drivers/acpi/video.c
drivers/acpi/video_detect.c
drivers/char/mxser.c
drivers/char/nozomi.c
drivers/char/synclink_gt.c
drivers/char/tty_port.c
drivers/dma/txx9dmac.c
drivers/gpu/drm/i915/i915_opregion.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-designware.c [new file with mode: 0644]
drivers/md/Kconfig
drivers/md/Makefile
drivers/md/dm-crypt.c
drivers/md/dm-delay.c
drivers/md/dm-exception-store.c
drivers/md/dm-exception-store.h
drivers/md/dm-io.c
drivers/md/dm-ioctl.c
drivers/md/dm-linear.c
drivers/md/dm-log-userspace-base.c [new file with mode: 0644]
drivers/md/dm-log-userspace-transfer.c [new file with mode: 0644]
drivers/md/dm-log-userspace-transfer.h [new file with mode: 0644]
drivers/md/dm-log.c
drivers/md/dm-mpath.c
drivers/md/dm-path-selector.h
drivers/md/dm-queue-length.c [new file with mode: 0644]
drivers/md/dm-raid1.c
drivers/md/dm-region-hash.c
drivers/md/dm-round-robin.c
drivers/md/dm-service-time.c [new file with mode: 0644]
drivers/md/dm-snap-persistent.c
drivers/md/dm-snap.c
drivers/md/dm-stripe.c
drivers/md/dm-sysfs.c
drivers/md/dm-table.c
drivers/md/dm.c
drivers/md/dm.h
drivers/net/Kconfig
drivers/net/bnx2.c
drivers/net/can/Kconfig
drivers/net/netxen/netxen_nic_init.c
drivers/net/netxen/netxen_nic_main.c
drivers/net/qla3xxx.c
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/intel-iommu.c
drivers/pci/intr_remapping.c
drivers/pci/intr_remapping.h
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acerhdf.c [new file with mode: 0644]
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus_acpi.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/thinkpad_acpi.c
drivers/pnp/pnpacpi/rsparser.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/da9030_battery.c
drivers/power/ds2760_battery.c
drivers/power/max17040_battery.c [new file with mode: 0644]
drivers/serial/8250_pci.c
drivers/serial/icom.c
drivers/serial/jsm/jsm_tty.c
drivers/serial/serial_txx9.c
drivers/staging/octeon/Makefile
drivers/staging/octeon/ethernet-common.c [deleted file]
drivers/staging/octeon/ethernet-common.h [deleted file]
drivers/staging/octeon/ethernet-defines.h
drivers/staging/octeon/ethernet-rgmii.c
drivers/staging/octeon/ethernet-sgmii.c
drivers/staging/octeon/ethernet-spi.c
drivers/staging/octeon/ethernet-tx.c
drivers/staging/octeon/ethernet-tx.h
drivers/staging/octeon/ethernet-xaui.c
drivers/staging/octeon/ethernet.c
drivers/staging/octeon/octeon-ethernet.h
drivers/uwb/hwa-rc.c
drivers/uwb/wlp/txrx.c
drivers/w1/slaves/w1_ds2760.c
drivers/w1/slaves/w1_ds2760.h
fs/btrfs/acl.c
fs/btrfs/btrfs_inode.h
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/compat_ioctl.c
fs/devpts/inode.c
fs/ext2/acl.c
fs/ext2/acl.h
fs/ext2/ext2.h
fs/ext2/inode.c
fs/ext2/super.c
fs/ext3/acl.c
fs/ext3/acl.h
fs/ext3/inode.c
fs/ext3/super.c
fs/ext4/acl.c
fs/ext4/acl.h
fs/ext4/ext4.h
fs/ext4/inode.c
fs/ext4/super.c
fs/fs-writeback.c
fs/inode.c
fs/ioctl.c
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/jffs2_fs_i.h
fs/jffs2/os-linux.h
fs/jffs2/readinode.c
fs/jfs/acl.c
fs/jfs/jfs_incore.h
fs/jfs/super.c
fs/jfs/xattr.c
fs/namei.c
fs/namespace.c
fs/nilfs2/inode.c
fs/nilfs2/nilfs.h
fs/nilfs2/super.c
fs/ocfs2/dlmglue.c
fs/ocfs2/dlmglue.h
fs/ocfs2/file.c
fs/ocfs2/inode.c
fs/ocfs2/journal.c
fs/ocfs2/journal.h
fs/ocfs2/namei.c
fs/ocfs2/ocfs2.h
fs/ocfs2/stack_o2cb.c
fs/ocfs2/stack_user.c
fs/ocfs2/stackglue.c
fs/ocfs2/stackglue.h
fs/ocfs2/suballoc.c
fs/ocfs2/super.c
fs/ocfs2/sysfile.c
fs/open.c
fs/reiserfs/inode.c
fs/reiserfs/resize.c
fs/reiserfs/super.c
fs/reiserfs/xattr_acl.c
fs/super.c
fs/ubifs/xattr.c
fs/udf/balloc.c
fs/udf/lowlevel.c
fs/xfs/linux-2.6/xfs_acl.c
fs/xfs/xfs_acl.h
fs/xfs/xfs_iget.c
fs/xfs/xfs_inode.h
include/acpi/acpi_bus.h
include/acpi/acpi_drivers.h
include/acpi/processor.h
include/acpi/video.h
include/linux/Kbuild
include/linux/acpi.h
include/linux/audit.h
include/linux/connector.h
include/linux/device-mapper.h
include/linux/dm-ioctl.h
include/linux/dm-log-userspace.h [new file with mode: 0644]
include/linux/dmar.h
include/linux/ext3_fs_i.h
include/linux/falloc.h
include/linux/fs.h
include/linux/icmpv6.h
include/linux/lockdep.h
include/linux/max17040_battery.h [new file with mode: 0644]
include/linux/pci_hotplug.h
include/linux/posix_acl.h
include/linux/reiserfs_acl.h
include/linux/reiserfs_fs_i.h
include/linux/shmem_fs.h
include/net/protocol.h
include/net/rawv6.h
include/net/sctp/sctp.h
include/net/sock.h
include/net/xfrm.h
init/main.c
kernel/Makefile
kernel/audit.c
kernel/audit.h
kernel/audit_tree.c
kernel/audit_watch.c [new file with mode: 0644]
kernel/auditfilter.c
kernel/auditsc.c
kernel/futex.c
mm/page_alloc.c
mm/shmem.c
mm/shmem_acl.c
mm/slub.c
net/ax25/ax25_in.c
net/core/dev.c
net/dccp/ipv6.c
net/ipv4/route.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/icmp.c
net/ipv6/ip6_tunnel.c
net/ipv6/ipcomp6.c
net/ipv6/mip6.c
net/ipv6/raw.c
net/ipv6/route.c
net/ipv6/tcp_ipv6.c
net/ipv6/tunnel6.c
net/ipv6/udp.c
net/ipv6/udp_impl.h
net/ipv6/udplite.c
net/ipv6/xfrm6_tunnel.c
net/irda/af_irda.c
net/irda/ircomm/ircomm_lmp.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_log.c
net/netfilter/xt_NFQUEUE.c
net/netfilter/xt_cluster.c
net/netfilter/xt_quota.c
net/netfilter/xt_rateest.c
net/sctp/ipv6.c
sound/pci/hda/patch_realtek.c

diff --git a/Documentation/device-mapper/dm-log.txt b/Documentation/device-mapper/dm-log.txt
new file mode 100644 (file)
index 0000000..994dd75
--- /dev/null
@@ -0,0 +1,54 @@
+Device-Mapper Logging
+=====================
+The device-mapper logging code is used by some of the device-mapper
+RAID targets to track regions of the disk that are not consistent.
+A region (or portion of the address space) of the disk may be
+inconsistent because a RAID stripe is currently being operated on or
+a machine died while the region was being altered.  In the case of
+mirrors, a region would be considered dirty/inconsistent while you
+are writing to it because the writes need to be replicated for all
+the legs of the mirror and may not reach the legs at the same time.
+Once all writes are complete, the region is considered clean again.
+
+There is a generic logging interface that the device-mapper RAID
+implementations use to perform logging operations (see
+dm_dirty_log_type in include/linux/dm-dirty-log.h).  Various different
+logging implementations are available and provide different
+capabilities.  The list includes:
+
+Type           Files
+====           =====
+disk           drivers/md/dm-log.c
+core           drivers/md/dm-log.c
+userspace      drivers/md/dm-log-userspace* include/linux/dm-log-userspace.h
+
+The "disk" log type
+-------------------
+This log implementation commits the log state to disk.  This way, the
+logging state survives reboots/crashes.
+
+The "core" log type
+-------------------
+This log implementation keeps the log state in memory.  The log state
+will not survive a reboot or crash, but there may be a small boost in
+performance.  This method can also be used if no storage device is
+available for storing log state.
+
+The "userspace" log type
+------------------------
+This log type simply provides a way to export the log API to userspace,
+so log implementations can be done there.  This is done by forwarding most
+logging requests to userspace, where a daemon receives and processes the
+request.
+
+The structure used for communication between kernel and userspace are
+located in include/linux/dm-log-userspace.h.  Due to the frequency,
+diversity, and 2-way communication nature of the exchanges between
+kernel and userspace, 'connector' is used as the interface for
+communication.
+
+There are currently two userspace log implementations that leverage this
+framework - "clustered_disk" and "clustered_core".  These implementations
+provide a cluster-coherent log for shared-storage.  Device-mapper mirroring
+can be used in a shared-storage environment when the cluster log implementations
+are employed.
diff --git a/Documentation/device-mapper/dm-queue-length.txt b/Documentation/device-mapper/dm-queue-length.txt
new file mode 100644 (file)
index 0000000..f4db256
--- /dev/null
@@ -0,0 +1,39 @@
+dm-queue-length
+===============
+
+dm-queue-length is a path selector module for device-mapper targets,
+which selects a path with the least number of in-flight I/Os.
+The path selector name is 'queue-length'.
+
+Table parameters for each path: [<repeat_count>]
+       <repeat_count>: The number of I/Os to dispatch using the selected
+                       path before switching to the next path.
+                       If not given, internal default is used. To check
+                       the default value, see the activated table.
+
+Status for each path: <status> <fail-count> <in-flight>
+       <status>: 'A' if the path is active, 'F' if the path is failed.
+       <fail-count>: The number of path failures.
+       <in-flight>: The number of in-flight I/Os on the path.
+
+
+Algorithm
+=========
+
+dm-queue-length increments/decrements 'in-flight' when an I/O is
+dispatched/completed respectively.
+dm-queue-length selects a path with the minimum 'in-flight'.
+
+
+Examples
+========
+In case that 2 paths (sda and sdb) are used with repeat_count == 128.
+
+# echo "0 10 multipath 0 0 1 1 queue-length 0 2 1 8:0 128 8:16 128" \
+  dmsetup create test
+#
+# dmsetup table
+test: 0 10 multipath 0 0 1 1 queue-length 0 2 1 8:0 128 8:16 128
+#
+# dmsetup status
+test: 0 10 multipath 2 0 0 0 1 1 E 0 2 1 8:0 A 0 0 8:16 A 0 0
diff --git a/Documentation/device-mapper/dm-service-time.txt b/Documentation/device-mapper/dm-service-time.txt
new file mode 100644 (file)
index 0000000..7d00668
--- /dev/null
@@ -0,0 +1,91 @@
+dm-service-time
+===============
+
+dm-service-time is a path selector module for device-mapper targets,
+which selects a path with the shortest estimated service time for
+the incoming I/O.
+
+The service time for each path is estimated by dividing the total size
+of in-flight I/Os on a path with the performance value of the path.
+The performance value is a relative throughput value among all paths
+in a path-group, and it can be specified as a table argument.
+
+The path selector name is 'service-time'.
+
+Table parameters for each path: [<repeat_count> [<relative_throughput>]]
+       <repeat_count>: The number of I/Os to dispatch using the selected
+                       path before switching to the next path.
+                       If not given, internal default is used.  To check
+                       the default value, see the activated table.
+       <relative_throughput>: The relative throughput value of the path
+                       among all paths in the path-group.
+                       The valid range is 0-100.
+                       If not given, minimum value '1' is used.
+                       If '0' is given, the path isn't selected while
+                       other paths having a positive value are available.
+
+Status for each path: <status> <fail-count> <in-flight-size> \
+                     <relative_throughput>
+       <status>: 'A' if the path is active, 'F' if the path is failed.
+       <fail-count>: The number of path failures.
+       <in-flight-size>: The size of in-flight I/Os on the path.
+       <relative_throughput>: The relative throughput value of the path
+                       among all paths in the path-group.
+
+
+Algorithm
+=========
+
+dm-service-time adds the I/O size to 'in-flight-size' when the I/O is
+dispatched and substracts when completed.
+Basically, dm-service-time selects a path having minimum service time
+which is calculated by:
+
+       ('in-flight-size' + 'size-of-incoming-io') / 'relative_throughput'
+
+However, some optimizations below are used to reduce the calculation
+as much as possible.
+
+       1. If the paths have the same 'relative_throughput', skip
+          the division and just compare the 'in-flight-size'.
+
+       2. If the paths have the same 'in-flight-size', skip the division
+          and just compare the 'relative_throughput'.
+
+       3. If some paths have non-zero 'relative_throughput' and others
+          have zero 'relative_throughput', ignore those paths with zero
+          'relative_throughput'.
+
+If such optimizations can't be applied, calculate service time, and
+compare service time.
+If calculated service time is equal, the path having maximum
+'relative_throughput' may be better.  So compare 'relative_throughput'
+then.
+
+
+Examples
+========
+In case that 2 paths (sda and sdb) are used with repeat_count == 128
+and sda has an average throughput 1GB/s and sdb has 4GB/s,
+'relative_throughput' value may be '1' for sda and '4' for sdb.
+
+# echo "0 10 multipath 0 0 1 1 service-time 0 2 2 8:0 128 1 8:16 128 4" \
+  dmsetup create test
+#
+# dmsetup table
+test: 0 10 multipath 0 0 1 1 service-time 0 2 2 8:0 128 1 8:16 128 4
+#
+# dmsetup status
+test: 0 10 multipath 2 0 0 0 1 1 E 0 2 2 8:0 A 0 0 1 8:16 A 0 0 4
+
+
+Or '2' for sda and '8' for sdb would be also true.
+
+# echo "0 10 multipath 0 0 1 1 service-time 0 2 2 8:0 128 2 8:16 128 8" \
+  dmsetup create test
+#
+# dmsetup table
+test: 0 10 multipath 0 0 1 1 service-time 0 2 2 8:0 128 2 8:16 128 8
+#
+# dmsetup status
+test: 0 10 multipath 2 0 0 0 1 1 E 0 2 2 8:0 A 0 0 2 8:16 A 0 0 8
index 229d7b7..18b9d0c 100644 (file)
@@ -109,27 +109,28 @@ prototypes:
 
 locking rules:
        All may block.
-                       BKL     s_lock  s_umount
-alloc_inode:           no      no      no
-destroy_inode:         no
-dirty_inode:           no                              (must not sleep)
-write_inode:           no
-drop_inode:            no                              !!!inode_lock!!!
-delete_inode:          no
-put_super:             yes     yes     no
-write_super:           no      yes     read
-sync_fs:               no      no      read
-freeze_fs:             ?
-unfreeze_fs:           ?
-statfs:                        no      no      no
-remount_fs:            yes     yes     maybe           (see below)
-clear_inode:           no
-umount_begin:          yes     no      no
-show_options:          no                              (vfsmount->sem)
-quota_read:            no      no      no              (see below)
-quota_write:           no      no      no              (see below)
-
-->remount_fs() will have the s_umount lock if it's already mounted.
+       None have BKL
+                       s_umount
+alloc_inode:
+destroy_inode:
+dirty_inode:                           (must not sleep)
+write_inode:
+drop_inode:                            !!!inode_lock!!!
+delete_inode:
+put_super:             write
+write_super:           read
+sync_fs:               read
+freeze_fs:             read
+unfreeze_fs:           read
+statfs:                        no
+remount_fs:            maybe           (see below)
+clear_inode:
+umount_begin:          no
+show_options:          no              (namespace_sem)
+quota_read:            no              (see below)
+quota_write:           no              (see below)
+
+->remount_fs() will have the s_umount exclusive lock if it's already mounted.
 When called from get_sb_single, it does NOT have the s_umount lock.
 ->quota_read() and ->quota_write() functions are both guaranteed to
 be the only ones operating on the quota file by the quota code (via
index 92e1ab8..d08759a 100644 (file)
@@ -229,14 +229,6 @@ and is between 256 and 4096 characters. It is defined in the file
                        to assume that this machine's pmtimer latches its value
                        and always returns good values.
 
-       acpi.power_nocheck=     [HW,ACPI]
-                       Format: 1/0 enable/disable the check of power state.
-                       On some bogus BIOS the _PSC object/_STA object of
-                       power resource can't return the correct device power
-                       state. In such case it is unneccessary to check its
-                       power state again in power transition.
-                       1 : disable the power state check
-
        acpi_sci=       [HW,ACPI] ACPI System Control Interrupt trigger mode
                        Format: { level | edge | high | low }
 
@@ -1863,7 +1855,7 @@ and is between 256 and 4096 characters. It is defined in the file
                                IRQ routing is enabled.
                noacpi          [X86] Do not use ACPI for IRQ routing
                                or for PCI scanning.
-               nocrs           [X86] Don't use _CRS for PCI resource
+               use_crs         [X86] Use _CRS for PCI resource
                                allocation.
                routeirq        Do IRQ routing for all PCI devices.
                                This is normally done in pci_enable_device(),
index 78e354b..f2296ec 100644 (file)
@@ -920,7 +920,7 @@ The available commands are:
        echo '<LED number> off' >/proc/acpi/ibm/led
        echo '<LED number> blink' >/proc/acpi/ibm/led
 
-The <LED number> range is 0 to 7. The set of LEDs that can be
+The <LED number> range is 0 to 15. The set of LEDs that can be
 controlled varies from model to model. Here is the common ThinkPad
 mapping:
 
@@ -932,6 +932,11 @@ mapping:
        5 - UltraBase battery slot
        6 - (unknown)
        7 - standby
+       8 - dock status 1
+       9 - dock status 2
+       10, 11 - (unknown)
+       12 - thinkvantage
+       13, 14, 15 - (unknown)
 
 All of the above can be turned on and off and can be made to blink.
 
@@ -940,10 +945,12 @@ sysfs notes:
 The ThinkPad LED sysfs interface is described in detail by the LED class
 documentation, in Documentation/leds-class.txt.
 
-The leds are named (in LED ID order, from 0 to 7):
+The LEDs are named (in LED ID order, from 0 to 12):
 "tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt",
 "tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt",
-"tpacpi::unknown_led", "tpacpi::standby".
+"tpacpi::unknown_led", "tpacpi::standby", "tpacpi::dock_status1",
+"tpacpi::dock_status2", "tpacpi::unknown_led2", "tpacpi::unknown_led3",
+"tpacpi::thinkvantage".
 
 Due to limitations in the sysfs LED class, if the status of the LED
 indicators cannot be read due to an error, thinkpad-acpi will report it as
@@ -958,6 +965,12 @@ ThinkPad indicator LED should blink in hardware accelerated mode, use the
 "timer" trigger, and leave the delay_on and delay_off parameters set to
 zero (to request hardware acceleration autodetection).
 
+LEDs that are known not to exist in a given ThinkPad model are not
+made available through the sysfs interface.  If you have a dock and you
+notice there are LEDs listed for your ThinkPad that do not exist (and
+are not in the dock), or if you notice that there are missing LEDs,
+a report to ibm-acpi-devel@lists.sourceforge.net is appreciated.
+
 
 ACPI sounds -- /proc/acpi/ibm/beep
 ----------------------------------
@@ -1156,17 +1169,19 @@ may not be distinct.  Later Lenovo models that implement the ACPI
 display backlight brightness control methods have 16 levels, ranging
 from 0 to 15.
 
-There are two interfaces to the firmware for direct brightness control,
-EC and UCMS (or CMOS).  To select which one should be used, use the
-brightness_mode module parameter: brightness_mode=1 selects EC mode,
-brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
-mode with NVRAM backing (so that brightness changes are remembered
-across shutdown/reboot).
+For IBM ThinkPads, there are two interfaces to the firmware for direct
+brightness control, EC and UCMS (or CMOS).  To select which one should be
+used, use the brightness_mode module parameter: brightness_mode=1 selects
+EC mode, brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
+mode with NVRAM backing (so that brightness changes are remembered across
+shutdown/reboot).
 
 The driver tries to select which interface to use from a table of
 defaults for each ThinkPad model.  If it makes a wrong choice, please
 report this as a bug, so that we can fix it.
 
+Lenovo ThinkPads only support brightness_mode=2 (UCMS).
+
 When display backlight brightness controls are available through the
 standard ACPI interface, it is best to use it instead of this direct
 ThinkPad-specific interface.  The driver will disable its native
@@ -1254,7 +1269,7 @@ Fan control and monitoring: fan speed, fan enable/disable
 
 procfs: /proc/acpi/ibm/fan
 sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1,
-                         pwm1_enable
+                         pwm1_enable, fan2_input
 sysfs hwmon driver attributes: fan_watchdog
 
 NOTE NOTE NOTE: fan control operations are disabled by default for
@@ -1267,6 +1282,9 @@ from the hardware registers of the embedded controller.  This is known
 to work on later R, T, X and Z series ThinkPads but may show a bogus
 value on other models.
 
+Some Lenovo ThinkPads support a secondary fan.  This fan cannot be
+controlled separately, it shares the main fan control.
+
 Fan levels:
 
 Most ThinkPad fans work in "levels" at the firmware interface.  Level 0
@@ -1397,6 +1415,11 @@ hwmon device attribute fan1_input:
        which can take up to two minutes.  May return rubbish on older
        ThinkPads.
 
+hwmon device attribute fan2_input:
+       Fan tachometer reading, in RPM, for the secondary fan.
+       Available only on some ThinkPads.  If the secondary fan is
+       not installed, will always read 0.
+
 hwmon driver attribute fan_watchdog:
        Fan safety watchdog timer interval, in seconds.  Minimum is
        1 second, maximum is 120 seconds.  0 disables the watchdog.
@@ -1555,3 +1578,7 @@ Sysfs interface changelog:
 0x020300:      hotkey enable/disable support removed, attributes
                hotkey_bios_enabled and hotkey_enable deprecated and
                marked for removal.
+
+0x020400:      Marker for 16 LEDs support.  Also, LEDs that are known
+               to not exist in a given model are not registered with
+               the LED sysfs class anymore.
index 303129a..3ba68dc 100644 (file)
@@ -230,6 +230,13 @@ L: linux-acenic@sunsite.dk
 S:     Maintained
 F:     drivers/net/acenic*
 
+ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
+P: Peter Feuerer
+M: peter@piie.net
+W: http://piie.net/?section=acerhdf
+S: Maintained
+F: drivers/platform/x86/acerhdf.c
+
 ACER WMI LAPTOP EXTRAS
 P:     Carlos Corbacho
 M:     carlos@strangeworlds.co.uk
@@ -913,8 +920,7 @@ M:  corentincj@iksaif.net
 P:     Karol Kozimor
 M:     sziwan@users.sourceforge.net
 L:     acpi4asus-user@lists.sourceforge.net
-W:     http://sourceforge.net/projects/acpi4asus
-W:     http://xf.iksaif.net/acpi4asus
+W:     http://acpi4asus.sf.net
 S:     Maintained
 F:     arch/x86/kernel/acpi/boot.c
 F:     drivers/platform/x86/asus_acpi.c
@@ -930,8 +936,7 @@ ASUS LAPTOP EXTRAS DRIVER
 P:     Corentin Chary
 M:     corentincj@iksaif.net
 L:     acpi4asus-user@lists.sourceforge.net
-W:     http://sourceforge.net/projects/acpi4asus
-W:     http://xf.iksaif.net/acpi4asus
+W:     http://acpi4asus.sf.net
 S:     Maintained
 F:     drivers/platform/x86/asus-laptop.c
 
@@ -1636,7 +1641,7 @@ P:        Mikael Starvik
 M:     starvik@axis.com
 P:     Jesper Nilsson
 M:     jesper.nilsson@axis.com
-L:     dev-etrax@axis.com
+L:     linux-cris-kernel@axis.com
 W:     http://developer.axis.com
 S:     Maintained
 F:     arch/cris/
@@ -2110,7 +2115,7 @@ EEEPC LAPTOP EXTRAS DRIVER
 P:     Corentin Chary
 M:     corentincj@iksaif.net
 L:     acpi4asus-user@lists.sourceforge.net
-W:     http://sourceforge.net/projects/acpi4asus
+W:     http://acpi4asus.sf.net
 S:     Maintained
 F:     drivers/platform/x86/eeepc-laptop.c
 
@@ -5663,6 +5668,13 @@ F:       drivers/misc/tifm*
 F:     drivers/mmc/host/tifm_sd.c
 F:     include/linux/tifm.h
 
+TI TWL4030 SERIES SOC CODEC DRIVER
+P:     Peter Ujfalusi
+M:     peter.ujfalusi@nokia.com
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:     Maintained
+F:     sound/soc/codecs/twl4030*
+
 TIPC NETWORK LAYER
 P:     Per Liden
 M:     per.liden@ericsson.com
index 46e1c9d..d1216fe 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
-SUBLEVEL = 30
-EXTRAVERSION =
+SUBLEVEL = 31
+EXTRAVERSION = -rc1
 NAME = Man-Eating Seals of Antiquity
 
 # *DOCUMENTATION*
index cbe6cee..dbda7bd 100644 (file)
@@ -71,3 +71,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
 }
 
 EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
+
+void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr)
+{
+       if (pr->pdc) {
+               kfree(pr->pdc->pointer->buffer.pointer);
+               kfree(pr->pdc->pointer);
+               kfree(pr->pdc);
+               pr->pdc = NULL;
+       }
+}
+
+EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc);
index b29f028..8c4be1f 100644 (file)
@@ -601,6 +601,7 @@ config CAVIUM_OCTEON_SIMULATOR
        select SYS_SUPPORTS_64BIT_KERNEL
        select SYS_SUPPORTS_BIG_ENDIAN
        select SYS_SUPPORTS_HIGHMEM
+       select SYS_SUPPORTS_HOTPLUG_CPU
        select SYS_HAS_CPU_CAVIUM_OCTEON
        help
          The Octeon simulator is software performance model of the Cavium
@@ -615,6 +616,7 @@ config CAVIUM_OCTEON_REFERENCE_BOARD
        select SYS_SUPPORTS_64BIT_KERNEL
        select SYS_SUPPORTS_BIG_ENDIAN
        select SYS_SUPPORTS_HIGHMEM
+       select SYS_SUPPORTS_HOTPLUG_CPU
        select SYS_HAS_EARLY_PRINTK
        select SYS_HAS_CPU_CAVIUM_OCTEON
        select SWAP_IO_SPACE
@@ -784,8 +786,17 @@ config SYS_HAS_EARLY_PRINTK
        bool
 
 config HOTPLUG_CPU
+       bool "Support for hot-pluggable CPUs"
+       depends on SMP && HOTPLUG && SYS_SUPPORTS_HOTPLUG_CPU
+       help
+         Say Y here to allow turning CPUs off and on. CPUs can be
+         controlled through /sys/devices/system/cpu.
+         (Note: power management support will enable this option
+           automatically on SMP systems. )
+         Say N if you want to disable CPU hotplug.
+
+config SYS_SUPPORTS_HOTPLUG_CPU
        bool
-       default n
 
 config I8259
        bool
@@ -2136,11 +2147,11 @@ menu "Power management options"
 
 config ARCH_HIBERNATION_POSSIBLE
        def_bool y
-       depends on !SMP
+       depends on SYS_SUPPORTS_HOTPLUG_CPU
 
 config ARCH_SUSPEND_POSSIBLE
        def_bool y
-       depends on !SMP
+       depends on SYS_SUPPORTS_HOTPLUG_CPU
 
 source "kernel/power/Kconfig"
 
index 8dfa009..384f184 100644 (file)
@@ -7,7 +7,7 @@
  */
 #include <linux/irq.h>
 #include <linux/interrupt.h>
-#include <linux/hardirq.h>
+#include <linux/smp.h>
 
 #include <asm/octeon/octeon.h>
 #include <asm/octeon/cvmx-pexp-defs.h>
@@ -501,3 +501,62 @@ asmlinkage void plat_irq_dispatch(void)
                }
        }
 }
+
+#ifdef CONFIG_HOTPLUG_CPU
+static int is_irq_enabled_on_cpu(unsigned int irq, unsigned int cpu)
+{
+       unsigned int isset;
+#ifdef CONFIG_SMP
+       int coreid = cpu_logical_map(cpu);
+#else
+       int coreid = cvmx_get_core_num();
+#endif
+       int bit = (irq < OCTEON_IRQ_WDOG0) ?
+               irq - OCTEON_IRQ_WORKQ0 : irq - OCTEON_IRQ_WDOG0;
+       if (irq < 64) {
+               isset = (cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)) &
+                       (1ull << bit)) >> bit;
+       } else {
+              isset = (cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1)) &
+                       (1ull << bit)) >> bit;
+       }
+       return isset;
+}
+
+void fixup_irqs(void)
+{
+       int irq;
+
+       for (irq = OCTEON_IRQ_SW0; irq <= OCTEON_IRQ_TIMER; irq++)
+               octeon_irq_core_disable_local(irq);
+
+       for (irq = OCTEON_IRQ_WORKQ0; irq <= OCTEON_IRQ_GPIO15; irq++) {
+               if (is_irq_enabled_on_cpu(irq, smp_processor_id())) {
+                       /* ciu irq migrates to next cpu */
+                       octeon_irq_chip_ciu0.disable(irq);
+                       octeon_irq_ciu0_set_affinity(irq, &cpu_online_map);
+               }
+       }
+
+#if 0
+       for (irq = OCTEON_IRQ_MBOX0; irq <= OCTEON_IRQ_MBOX1; irq++)
+               octeon_irq_mailbox_mask(irq);
+#endif
+       for (irq = OCTEON_IRQ_UART0; irq <= OCTEON_IRQ_BOOTDMA; irq++) {
+               if (is_irq_enabled_on_cpu(irq, smp_processor_id())) {
+                       /* ciu irq migrates to next cpu */
+                       octeon_irq_chip_ciu0.disable(irq);
+                       octeon_irq_ciu0_set_affinity(irq, &cpu_online_map);
+               }
+       }
+
+       for (irq = OCTEON_IRQ_UART2; irq <= OCTEON_IRQ_RESERVED135; irq++) {
+               if (is_irq_enabled_on_cpu(irq, smp_processor_id())) {
+                       /* ciu irq migrates to next cpu */
+                       octeon_irq_chip_ciu1.disable(irq);
+                       octeon_irq_ciu1_set_affinity(irq, &cpu_online_map);
+               }
+       }
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/mips/cavium-octeon/octeon_boot.h b/arch/mips/cavium-octeon/octeon_boot.h
new file mode 100644 (file)
index 0000000..0f7f84a
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 2004, 2005 Cavium Networks
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __OCTEON_BOOT_H__
+#define __OCTEON_BOOT_H__
+
+#include <linux/types.h>
+
+struct boot_init_vector {
+       uint32_t stack_addr;
+       uint32_t code_addr;
+       uint32_t app_start_func_addr;
+       uint32_t k0_val;
+       uint32_t flags;
+       uint32_t boot_info_addr;
+       uint32_t pad;
+       uint32_t pad2;
+};
+
+/* similar to bootloader's linux_app_boot_info but without global data */
+struct linux_app_boot_info {
+       uint32_t labi_signature;
+       uint32_t start_core0_addr;
+       uint32_t avail_coremask;
+       uint32_t pci_console_active;
+       uint32_t icache_prefetch_disable;
+       uint32_t InitTLBStart_addr;
+       uint32_t start_app_addr;
+       uint32_t cur_exception_base;
+       uint32_t no_mark_private_data;
+       uint32_t compact_flash_common_base_addr;
+       uint32_t compact_flash_attribute_base_addr;
+       uint32_t led_display_base_addr;
+};
+
+/* If not to copy a lot of bootloader's structures
+   here is only offset of requested member */
+#define AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK    0x765c
+
+/* hardcoded in bootloader */
+#define  LABI_ADDR_IN_BOOTLOADER                         0x700
+
+#define LINUX_APP_BOOT_BLOCK_NAME "linux-app-boot"
+
+#define LABI_SIGNATURE 0xAABBCCDD
+
+/*  from uboot-headers/octeon_mem_map.h */
+#define EXCEPTION_BASE_INCR     (4 * 1024)
+                              /* Increment size for exception base addresses (4k minimum) */
+#define EXCEPTION_BASE_BASE     0
+#define BOOTLOADER_PRIV_DATA_BASE       (EXCEPTION_BASE_BASE + 0x800)
+#define BOOTLOADER_BOOT_VECTOR          (BOOTLOADER_PRIV_DATA_BASE)
+
+#endif /* __OCTEON_BOOT_H__ */
index 5f4e49b..da55924 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/serial.h>
+#include <linux/smp.h>
 #include <linux/types.h>
 #include <linux/string.h>      /* for memset */
 #include <linux/tty.h>
index 24e0ad6..0b891a9 100644 (file)
@@ -5,6 +5,7 @@
  *
  * Copyright (C) 2004-2008 Cavium Networks
  */
+#include <linux/cpu.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/smp.h>
 
 #include <asm/octeon/octeon.h>
 
+#include "octeon_boot.h"
+
 volatile unsigned long octeon_processor_boot = 0xff;
 volatile unsigned long octeon_processor_sp;
 volatile unsigned long octeon_processor_gp;
 
+#ifdef CONFIG_HOTPLUG_CPU
+static unsigned int InitTLBStart_addr;
+#endif
+
 static irqreturn_t mailbox_interrupt(int irq, void *dev_id)
 {
        const int coreid = cvmx_get_core_num();
@@ -67,8 +74,28 @@ static inline void octeon_send_ipi_mask(cpumask_t mask, unsigned int action)
 }
 
 /**
- * Detect available CPUs, populate phys_cpu_present_map
+ * Detect available CPUs, populate cpu_possible_map
  */
+static void octeon_smp_hotplug_setup(void)
+{
+#ifdef CONFIG_HOTPLUG_CPU
+       uint32_t labi_signature;
+
+       labi_signature =
+               cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                       LABI_ADDR_IN_BOOTLOADER +
+                                       offsetof(struct linux_app_boot_info,
+                                                   labi_signature)));
+       if (labi_signature != LABI_SIGNATURE)
+               pr_err("The bootloader version on this board is incorrect\n");
+       InitTLBStart_addr =
+               cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                  LABI_ADDR_IN_BOOTLOADER +
+                                          offsetof(struct linux_app_boot_info,
+                                                   InitTLBStart_addr)));
+#endif
+}
+
 static void octeon_smp_setup(void)
 {
        const int coreid = cvmx_get_core_num();
@@ -91,6 +118,9 @@ static void octeon_smp_setup(void)
                        cpus++;
                }
        }
+       cpu_present_map = cpu_possible_map;
+
+       octeon_smp_hotplug_setup();
 }
 
 /**
@@ -128,6 +158,17 @@ static void octeon_init_secondary(void)
        const int coreid = cvmx_get_core_num();
        union cvmx_ciu_intx_sum0 interrupt_enable;
 
+#ifdef CONFIG_HOTPLUG_CPU
+       unsigned int cur_exception_base;
+
+       cur_exception_base = cvmx_read64_uint32(
+               CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                            LABI_ADDR_IN_BOOTLOADER +
+                            offsetof(struct linux_app_boot_info,
+                                     cur_exception_base)));
+       /* cur_exception_base is incremented in bootloader after setting */
+       write_c0_ebase((unsigned int)(cur_exception_base - EXCEPTION_BASE_INCR));
+#endif
        octeon_check_cpu_bist();
        octeon_init_cvmcount();
        /*
@@ -199,6 +240,193 @@ static void octeon_cpus_done(void)
 #endif
 }
 
+#ifdef CONFIG_HOTPLUG_CPU
+
+/* State of each CPU. */
+DEFINE_PER_CPU(int, cpu_state);
+
+extern void fixup_irqs(void);
+
+static DEFINE_SPINLOCK(smp_reserve_lock);
+
+static int octeon_cpu_disable(void)
+{
+       unsigned int cpu = smp_processor_id();
+
+       if (cpu == 0)
+               return -EBUSY;
+
+       spin_lock(&smp_reserve_lock);
+
+       cpu_clear(cpu, cpu_online_map);
+       cpu_clear(cpu, cpu_callin_map);
+       local_irq_disable();
+       fixup_irqs();
+       local_irq_enable();
+
+       flush_cache_all();
+       local_flush_tlb_all();
+
+       spin_unlock(&smp_reserve_lock);
+
+       return 0;
+}
+
+static void octeon_cpu_die(unsigned int cpu)
+{
+       int coreid = cpu_logical_map(cpu);
+       uint32_t avail_coremask;
+       struct cvmx_bootmem_named_block_desc *block_desc;
+
+#ifdef CONFIG_CAVIUM_OCTEON_WATCHDOG
+       /* Disable the watchdog */
+       cvmx_ciu_wdogx_t ciu_wdog;
+       ciu_wdog.u64 = cvmx_read_csr(CVMX_CIU_WDOGX(cpu));
+       ciu_wdog.s.mode = 0;
+       cvmx_write_csr(CVMX_CIU_WDOGX(cpu), ciu_wdog.u64);
+#endif
+
+       while (per_cpu(cpu_state, cpu) != CPU_DEAD)
+               cpu_relax();
+
+       /*
+        * This is a bit complicated strategics of getting/settig available
+        * cores mask, copied from bootloader
+        */
+       /* LINUX_APP_BOOT_BLOCK is initialized in bootoct binary */
+       block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME);
+
+       if (!block_desc) {
+               avail_coremask =
+                       cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                                  LABI_ADDR_IN_BOOTLOADER +
+                                                  offsetof
+                                                  (struct linux_app_boot_info,
+                                                   avail_coremask)));
+       } else {                       /* alternative, already initialized */
+              avail_coremask =
+                  cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                                  block_desc->base_addr +
+                                                 AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK));
+       }
+
+       avail_coremask |= 1 << coreid;
+
+       /* Setting avail_coremask for bootoct binary */
+       if (!block_desc) {
+               cvmx_write64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                               LABI_ADDR_IN_BOOTLOADER +
+                                               offsetof(struct linux_app_boot_info,
+                                                        avail_coremask)),
+                                  avail_coremask);
+       } else {
+               cvmx_write64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                               block_desc->base_addr +
+                                               AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK),
+                                  avail_coremask);
+       }
+
+       pr_info("Reset core %d. Available Coremask = %x \n", coreid,
+               avail_coremask);
+       cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
+       cvmx_write_csr(CVMX_CIU_PP_RST, 0);
+}
+
+void play_dead(void)
+{
+       int coreid = cvmx_get_core_num();
+
+       idle_task_exit();
+       octeon_processor_boot = 0xff;
+       per_cpu(cpu_state, coreid) = CPU_DEAD;
+
+       while (1)       /* core will be reset here */
+               ;
+}
+
+extern void kernel_entry(unsigned long arg1, ...);
+
+static void start_after_reset(void)
+{
+       kernel_entry(0, 0, 0);  /* set a2 = 0 for secondary core */
+}
+
+int octeon_update_boot_vector(unsigned int cpu)
+{
+
+       int coreid = cpu_logical_map(cpu);
+       unsigned int avail_coremask;
+       struct cvmx_bootmem_named_block_desc *block_desc;
+       struct boot_init_vector *boot_vect =
+               (struct boot_init_vector *) cvmx_phys_to_ptr(0x0 +
+                                                 BOOTLOADER_BOOT_VECTOR);
+
+       block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME);
+
+       if (!block_desc) {
+               avail_coremask =
+                       cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                          LABI_ADDR_IN_BOOTLOADER +
+                                               offsetof(struct linux_app_boot_info,
+                                               avail_coremask)));
+       } else {                       /* alternative, already initialized */
+              avail_coremask =
+                  cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS,
+                                                  block_desc->base_addr +
+                                                  AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK));
+       }
+
+       if (!(avail_coremask & (1 << coreid))) {
+               /* core not available, assume, that catched by simple-executive */
+               cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid);
+               cvmx_write_csr(CVMX_CIU_PP_RST, 0);
+       }
+
+       boot_vect[coreid].app_start_func_addr =
+               (uint32_t) (unsigned long) start_after_reset;
+       boot_vect[coreid].code_addr = InitTLBStart_addr;
+
+       CVMX_SYNC;
+
+       cvmx_write_csr(CVMX_CIU_NMI, (1 << coreid) & avail_coremask);
+
+       return 0;
+}
+
+static int __cpuinit octeon_cpu_callback(struct notifier_block *nfb,
+       unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_UP_PREPARE:
+               octeon_update_boot_vector(cpu);
+               break;
+       case CPU_ONLINE:
+               pr_info("Cpu %d online\n", cpu);
+               break;
+       case CPU_DEAD:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata octeon_cpu_notifier = {
+       .notifier_call = octeon_cpu_callback,
+};
+
+static int __cpuinit register_cavium_notifier(void)
+{
+       register_hotcpu_notifier(&octeon_cpu_notifier);
+
+       return 0;
+}
+
+late_initcall(register_cavium_notifier);
+
+#endif  /* CONFIG_HOTPLUG_CPU */
+
 struct plat_smp_ops octeon_smp_ops = {
        .send_ipi_single        = octeon_send_ipi_single,
        .send_ipi_mask          = octeon_send_ipi_mask,
@@ -208,4 +436,8 @@ struct plat_smp_ops octeon_smp_ops = {
        .boot_secondary         = octeon_boot_secondary,
        .smp_setup              = octeon_smp_setup,
        .prepare_cpus           = octeon_prepare_cpus,
+#ifdef CONFIG_HOTPLUG_CPU
+       .cpu_disable            = octeon_cpu_disable,
+       .cpu_die                = octeon_cpu_die,
+#endif
 };
index 08ea468..6cf29c2 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __ASM_BUG_H
 #define __ASM_BUG_H
 
+#include <linux/compiler.h>
 #include <asm/sgidefs.h>
 
 #ifdef CONFIG_BUG
index 9dc10df..b160a70 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/bug.h>
 #include <linux/delay.h>
+#include <linux/smp.h>
 
 #include <asm/cpu.h>
 #include <asm/cpu-info.h>
index 4f1eed1..09b08d0 100644 (file)
@@ -10,6 +10,7 @@
 #define _ASM_IRQ_H
 
 #include <linux/linkage.h>
+#include <linux/smp.h>
 
 #include <asm/mipsmtregs.h>
 
index d7f3eb0..d3bea88 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/errno.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/slab.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
index 64ffc02..fd54554 100644 (file)
@@ -26,6 +26,10 @@ struct plat_smp_ops {
        void (*boot_secondary)(int cpu, struct task_struct *idle);
        void (*smp_setup)(void);
        void (*prepare_cpus)(unsigned int max_cpus);
+#ifdef CONFIG_HOTPLUG_CPU
+       int (*cpu_disable)(void);
+       void (*cpu_die)(unsigned int cpu);
+#endif
 };
 
 extern void register_smp_ops(struct plat_smp_ops *ops);
index 40e5ef1..aaa2d4a 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/bitops.h>
 #include <linux/linkage.h>
+#include <linux/smp.h>
 #include <linux/threads.h>
 #include <linux/cpumask.h>
 
@@ -40,6 +41,7 @@ extern int __cpu_logical_map[NR_CPUS];
 /* Octeon - Tell another core to flush its icache */
 #define SMP_ICACHE_FLUSH       0x4
 
+extern volatile cpumask_t cpu_callin_map;
 
 extern void asmlinkage smp_bootstrap(void);
 
@@ -55,6 +57,24 @@ static inline void smp_send_reschedule(int cpu)
        mp_ops->send_ipi_single(cpu, SMP_RESCHEDULE_YOURSELF);
 }
 
+#ifdef CONFIG_HOTPLUG_CPU
+static inline int __cpu_disable(void)
+{
+       extern struct plat_smp_ops *mp_ops;     /* private */
+
+       return mp_ops->cpu_disable();
+}
+
+static inline void __cpu_die(unsigned int cpu)
+{
+       extern struct plat_smp_ops *mp_ops;     /* private */
+
+       mp_ops->cpu_die(cpu);
+}
+
+extern void play_dead(void);
+#endif
+
 extern asmlinkage void smp_call_function_interrupt(void);
 
 extern void arch_send_call_function_single_ipi(int cpu);
index 3a56d90..2367b56 100644 (file)
@@ -11,6 +11,7 @@
 
 
 #ifndef __ASSEMBLY__
+#include <linux/smp.h>
 #include <linux/types.h>
 #endif /* !__ASSEMBLY__ */
 
index d9b6a5b..7fd170d 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/smp.h>
 #include <linux/spinlock.h>
 
 #include <asm/irq_cpu.h>
index a5182a2..e02f79b 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
 #include <linux/percpu.h>
+#include <linux/smp.h>
 
 #include <asm/addrspace.h>
 #include <asm/io.h>
index 0015e44..2652362 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
 #include <linux/percpu.h>
+#include <linux/smp.h>
 
 #include <asm/smtc_ipi.h>
 #include <asm/time.h>
index 340f53e..ac5903d 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
 #include <linux/percpu.h>
+#include <linux/smp.h>
 
 #include <asm/addrspace.h>
 #include <asm/io.h>
index df6f5bc..98bd7de 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
 #include <linux/percpu.h>
+#include <linux/smp.h>
 
 #include <asm/smtc_ipi.h>
 #include <asm/time.h>
index b13b8eb..1abe990 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/ptrace.h>
+#include <linux/smp.h>
 #include <linux/stddef.h>
 
 #include <asm/bugs.h>
index ed20e7f..f7d8d5d 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/module.h>
+#include <linux/smp.h>
 #include <linux/spinlock.h>
 
 #include <asm/delay.h>
index 3f43c2e..39000f1 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <linux/bitmap.h>
 #include <linux/init.h>
+#include <linux/smp.h>
 
 #include <asm/io.h>
 #include <asm/gic.h>
index 6e152c8..50c9bb8 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/kgdb.h>
 #include <linux/kdebug.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <asm/inst.h>
 #include <asm/fpu.h>
 #include <asm/cacheflush.h>
index 1eaaa45..c09d681 100644 (file)
  */
 void __noreturn cpu_idle(void)
 {
+       int cpu;
+
+       /* CPU is going idle. */
+       cpu = smp_processor_id();
+
        /* endless idle loop with no priority at all */
        while (1) {
                tick_nohz_stop_sched_tick(1);
-               while (!need_resched()) {
+               while (!need_resched() && cpu_online(cpu)) {
 #ifdef CONFIG_MIPS_MT_SMTC
                        extern void smtc_idle_loop_hook(void);
 
@@ -62,6 +67,12 @@ void __noreturn cpu_idle(void)
                        if (cpu_wait)
                                (*cpu_wait)();
                }
+#ifdef CONFIG_HOTPLUG_CPU
+               if (!cpu_online(cpu) && !cpu_isset(cpu, cpu_callin_map) &&
+                   (system_state == SYSTEM_RUNNING ||
+                    system_state == SYSTEM_BOOTING))
+                       play_dead();
+#endif
                tick_nohz_restart_sched_tick();
                preempt_enable_no_resched();
                schedule();
index f27beca..653be06 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/cpumask.h>
 #include <linux/interrupt.h>
 #include <linux/compiler.h>
index 878e373..2508d55 100644 (file)
@@ -55,6 +55,18 @@ static void __init up_prepare_cpus(unsigned int max_cpus)
 {
 }
 
+#ifdef CONFIG_HOTPLUG_CPU
+static int up_cpu_disable(void)
+{
+       return -ENOSYS;
+}
+
+static void up_cpu_die(unsigned int cpu)
+{
+       BUG();
+}
+#endif
+
 struct plat_smp_ops up_smp_ops = {
        .send_ipi_single        = up_send_ipi_single,
        .send_ipi_mask          = up_send_ipi_mask,
@@ -64,4 +76,8 @@ struct plat_smp_ops up_smp_ops = {
        .boot_secondary         = up_boot_secondary,
        .smp_setup              = up_smp_setup,
        .prepare_cpus           = up_prepare_cpus,
+#ifdef CONFIG_HOTPLUG_CPU
+       .cpu_disable            = up_cpu_disable,
+       .cpu_die                = up_cpu_die,
+#endif
 };
index c937506..bc7d9b0 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/smp.h>
 #include <linux/spinlock.h>
 #include <linux/threads.h>
 #include <linux/module.h>
@@ -44,7 +45,7 @@
 #include <asm/mipsmtregs.h>
 #endif /* CONFIG_MIPS_MT_SMTC */
 
-static volatile cpumask_t cpu_callin_map;      /* Bitmask of started secondaries */
+volatile cpumask_t cpu_callin_map;     /* Bitmask of started secondaries */
 int __cpu_number_map[NR_CPUS];         /* Map physical to logical */
 int __cpu_logical_map[NR_CPUS];                /* Map logical to physical */
 
@@ -200,6 +201,8 @@ void __devinit smp_prepare_boot_cpu(void)
  * and keep control until "cpu_online(cpu)" is set.  Note: cpu is
  * physical, not logical.
  */
+static struct task_struct *cpu_idle_thread[NR_CPUS];
+
 int __cpuinit __cpu_up(unsigned int cpu)
 {
        struct task_struct *idle;
@@ -209,9 +212,16 @@ int __cpuinit __cpu_up(unsigned int cpu)
         * The following code is purely to make sure
         * Linux can schedule processes on this slave.
         */
-       idle = fork_idle(cpu);
-       if (IS_ERR(idle))
-               panic(KERN_ERR "Fork failed for CPU %d", cpu);
+       if (!cpu_idle_thread[cpu]) {
+               idle = fork_idle(cpu);
+               cpu_idle_thread[cpu] = idle;
+
+               if (IS_ERR(idle))
+                       panic(KERN_ERR "Fork failed for CPU %d", cpu);
+       } else {
+               idle = cpu_idle_thread[cpu];
+               init_idle(idle, cpu);
+       }
 
        mp_ops->boot_secondary(cpu, idle);
 
index 37d51cd..8a0626c 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/clockchips.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/cpumask.h>
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
index 660e44e..cf3eb61 100644 (file)
@@ -17,7 +17,10 @@ static int __init topology_init(void)
 #endif /* CONFIG_NUMA */
 
        for_each_present_cpu(i) {
-               ret = register_cpu(&per_cpu(cpu_devices, i), i);
+               struct cpu *c = &per_cpu(cpu_devices, i);
+
+               c->hotpluggable = 1;
+               ret = register_cpu(c, i);
                if (ret)
                        printk(KERN_WARNING "topology_init: register_cpu %d "
                               "failed (%d)\n", i, ret);
index 881ecbc..0cea932 100644 (file)
@@ -91,6 +91,7 @@ unsigned __cpuinit get_c0_compare_int(void)
                mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
        } else {
 #endif
+              {
                if (cpu_has_vint)
                        set_vi_handler(cp0_compare_irq, mips_timer_dispatch);
                mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
index 44d01a0..b165cdc 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 #include <linux/bitops.h>
 #include <linux/cpu.h>
index 5500c20..54e5f7b 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 
 #include <asm/page.h>
index 71fe4cb..6721ee2 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/linkage.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/bitops.h>
index f7c8f9c..6515b44 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 
 #include <asm/cacheops.h>
index 2b1309b..e274fda 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/module.h>
 #include <linux/highmem.h>
+#include <linux/smp.h>
 #include <asm/fixmap.h>
 #include <asm/tlbflush.h>
 
index c551129..0e82050 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/string.h>
index 48060c6..f5c7375 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/proc_fs.h>
index 1c0048a..0f5ab23 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 
 #include <asm/page.h>
index f60fe51..cee502c 100644 (file)
@@ -10,6 +10,7 @@
  */
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 #include <linux/hugetlb.h>
 
index 4ec95cc..2b82f23 100644 (file)
@@ -10,6 +10,7 @@
  */
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 
 #include <asm/cpu.h>
index 8f606ea..9a17bf8 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/bug.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <linux/smp.h>
 #include <linux/string.h>
 #include <linux/init.h>
 
index ea17611..b4eaf13 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/init.h>
 #include <linux/irq.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
index dda6f20..a0e726e 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
+#include <linux/smp.h>
 #include <asm/sn/arch.h>
 #include <asm/pci/bridge.h>
 #include <asm/paccess.h>
index f78c29b..8ace277 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/linkage.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 
 #include <asm/pmon.h>
 #include <asm/titan_dep.h>
index 486bd3f..4b8174b 100644 (file)
@@ -43,15 +43,6 @@ LEAF(swsusp_arch_resume)
        bne t1, t3, 1b
        PTR_L t0, PBE_NEXT(t0)
        bnez t0, 0b
-       /* flush caches to make sure context is in memory */
-       PTR_L t0, __flush_cache_all
-       jalr t0
-       /* flush tlb entries */
-#ifdef CONFIG_SMP
-       jal     flush_tlb_all
-#else
-       jal     local_flush_tlb_all
-#endif
        PTR_LA t0, saved_regs
        PTR_L ra, PT_R31(t0)
        PTR_L sp, PT_R29(t0)
index 4a500e8..51d3a4f 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/cpumask.h>
index 1bb692a..c1c8e40 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/ioport.h>
 #include <linux/timex.h>
 #include <linux/slab.h>
+#include <linux/smp.h>
 #include <linux/random.h>
 #include <linux/kernel.h>
 #include <linux/kernel_stat.h>
index f10a7cd..6d0e59f 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
 #include <linux/param.h>
+#include <linux/smp.h>
 #include <linux/time.h>
 #include <linux/timex.h>
 #include <linux/mm.h>
index 6ae64e8..5e871e7 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/smp.h>
 #include <asm/sn/types.h>
 #include <asm/sn/klconfig.h>
 #include <asm/sn/hub.h>
index 690de06..ba59839 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/linkage.h>
 #include <linux/interrupt.h>
+#include <linux/smp.h>
 #include <linux/spinlock.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
index 81e3d54..1ad2da1 100644 (file)
@@ -51,12 +51,13 @@ static int cfe_console_setup(struct console *cons, char *str)
                        setleds("u0cn");
                } else if (!strcmp(consdev, "uart1")) {
                        setleds("u1cn");
+               } else
 #endif
 #ifdef CONFIG_VGA_CONSOLE
-               } else if (!strcmp(consdev, "pcconsole0")) {
-                       setleds("pccn");
-#endif
+                      if (!strcmp(consdev, "pcconsole0")) {
+                               setleds("pccn");
                } else
+#endif
                        return -ENODEV;
        }
        return 0;
index 69f5f88..0d9ec1a 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/types.h>
 #include <linux/interrupt.h>
+#include <linux/smp.h>
 #include <linux/time.h>
 #include <linux/clockchips.h>
 
index 4518dc5..20d1465 100644 (file)
@@ -144,6 +144,7 @@ static inline unsigned int acpi_processor_cstate_check(unsigned int max_cstate)
 
 #else /* !CONFIG_ACPI */
 
+#define acpi_disabled 1
 #define acpi_lapic 0
 #define acpi_ioapic 0
 static inline void acpi_noirq_set(void) { }
index cb739cc..b399988 100644 (file)
@@ -25,7 +25,7 @@
 #define PCI_BIOS_IRQ_SCAN      0x2000
 #define PCI_ASSIGN_ALL_BUSSES  0x4000
 #define PCI_CAN_SKIP_ISA_ALIGN 0x8000
-#define PCI_NO_ROOT_CRS                0x10000
+#define PCI_USE__CRS           0x10000
 #define PCI_CHECK_ENABLE_AMD_MMCONF    0x20000
 #define PCI_HAS_IO_ECS         0x40000
 #define PCI_NOASSIGN_ROMS      0x80000
@@ -121,6 +121,9 @@ extern int __init pcibios_init(void);
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
 
+extern struct acpi_mcfg_allocation *pci_mmcfg_config;
+extern int pci_mmcfg_config_num;
+
 /*
  * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
  * on their northbrige except through the * %eax register. As such, you MUST
index 6310861..6b8ca3a 100644 (file)
 
 static int __initdata acpi_force = 0;
 u32 acpi_rsdt_forced;
-#ifdef CONFIG_ACPI
-int acpi_disabled = 0;
-#else
-int acpi_disabled = 1;
-#endif
+int acpi_disabled;
 EXPORT_SYMBOL(acpi_disabled);
 
 #ifdef CONFIG_X86_64
@@ -122,72 +118,6 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
        early_iounmap(map, size);
 }
 
-#ifdef CONFIG_PCI_MMCONFIG
-
-static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
-
-/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
-struct acpi_mcfg_allocation *pci_mmcfg_config;
-int pci_mmcfg_config_num;
-
-static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
-{
-       if (!strcmp(mcfg->header.oem_id, "SGI"))
-               acpi_mcfg_64bit_base_addr = TRUE;
-
-       return 0;
-}
-
-int __init acpi_parse_mcfg(struct acpi_table_header *header)
-{
-       struct acpi_table_mcfg *mcfg;
-       unsigned long i;
-       int config_size;
-
-       if (!header)
-               return -EINVAL;
-
-       mcfg = (struct acpi_table_mcfg *)header;
-
-       /* how many config structures do we have */
-       pci_mmcfg_config_num = 0;
-       i = header->length - sizeof(struct acpi_table_mcfg);
-       while (i >= sizeof(struct acpi_mcfg_allocation)) {
-               ++pci_mmcfg_config_num;
-               i -= sizeof(struct acpi_mcfg_allocation);
-       };
-       if (pci_mmcfg_config_num == 0) {
-               printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
-               return -ENODEV;
-       }
-
-       config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
-       pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
-       if (!pci_mmcfg_config) {
-               printk(KERN_WARNING PREFIX
-                      "No memory for MCFG config tables\n");
-               return -ENOMEM;
-       }
-
-       memcpy(pci_mmcfg_config, &mcfg[1], config_size);
-
-       acpi_mcfg_oem_check(mcfg);
-
-       for (i = 0; i < pci_mmcfg_config_num; ++i) {
-               if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
-                   !acpi_mcfg_64bit_base_addr) {
-                       printk(KERN_ERR PREFIX
-                              "MMCONFIG not in low 4GB of memory\n");
-                       kfree(pci_mmcfg_config);
-                       pci_mmcfg_config_num = 0;
-                       return -ENODEV;
-               }
-       }
-
-       return 0;
-}
-#endif                         /* CONFIG_PCI_MMCONFIG */
-
 #ifdef CONFIG_X86_LOCAL_APIC
 static int __init acpi_parse_madt(struct acpi_table_header *table)
 {
@@ -1519,14 +1449,6 @@ static struct dmi_system_id __initdata acpi_dmi_table[] = {
         },
        {
         .callback = force_acpi_ht,
-        .ident = "ASUS P4B266",
-        .matches = {
-                    DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
-                    DMI_MATCH(DMI_BOARD_NAME, "P4B266"),
-                    },
-        },
-       {
-        .callback = force_acpi_ht,
         .ident = "ASUS P2B-DS",
         .matches = {
                     DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
index bbbe4bb..8c44c23 100644 (file)
@@ -34,12 +34,22 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
                flags->bm_check = 1;
        else if (c->x86_vendor == X86_VENDOR_INTEL) {
                /*
-                * Today all CPUs that support C3 share cache.
-                * TBD: This needs to look at cache shared map, once
-                * multi-core detection patch makes to the base.
+                * Today all MP CPUs that support C3 share cache.
+                * And caches should not be flushed by software while
+                * entering C3 type state.
                 */
                flags->bm_check = 1;
        }
+
+       /*
+        * On all recent Intel platforms, ARB_DISABLE is a nop.
+        * So, set bm_control to zero to indicate that ARB_DISABLE
+        * is not required while entering C3 type state on
+        * P4, Core and beyond CPUs
+        */
+       if (c->x86_vendor == X86_VENDOR_INTEL &&
+           (c->x86 > 0x6 || (c->x86 == 6 && c->x86_model >= 14)))
+                       flags->bm_control = 0;
 }
 EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
 
index 7c074ee..d296f4a 100644 (file)
@@ -72,6 +72,7 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c)
        return;
 }
 
+
 /* Initialize _PDC data based on the CPU vendor */
 void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
 {
@@ -85,3 +86,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
 }
 
 EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
+
+void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr)
+{
+       if (pr->pdc) {
+               kfree(pr->pdc->pointer->buffer.pointer);
+               kfree(pr->pdc->pointer);
+               kfree(pr->pdc);
+               pr->pdc = NULL;
+       }
+}
+
+EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc);
index b7a7920..4d0216f 100644 (file)
@@ -1414,6 +1414,9 @@ int setup_ioapic_entry(int apic_id, int irq,
                irte.vector = vector;
                irte.dest_id = IRTE_DEST(destination);
 
+               /* Set source-id of interrupt request */
+               set_ioapic_sid(&irte, apic_id);
+
                modify_irte(irq, &irte);
 
                ir_entry->index2 = (index >> 15) & 0x1;
@@ -3290,6 +3293,9 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms
                irte.vector = cfg->vector;
                irte.dest_id = IRTE_DEST(dest);
 
+               /* Set source-id of interrupt request */
+               set_msi_sid(&irte, pdev);
+
                modify_irte(irq, &irte);
 
                msg->address_hi = MSI_ADDR_BASE_HI;
index 16c3fda..b26626d 100644 (file)
@@ -238,7 +238,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int do
 #endif
        }
 
-       if (bus && !(pci_probe & PCI_NO_ROOT_CRS))
+       if (bus && (pci_probe & PCI_USE__CRS))
                get_current_resources(device, busnum, domain, bus);
        return bus;
 }
index 2255f88..f893d6a 100644 (file)
@@ -101,7 +101,7 @@ void x86_pci_root_bus_res_quirks(struct pci_bus *b)
        struct pci_root_info *info;
 
        /* don't go for it if _CRS is used */
-       if (!(pci_probe & PCI_NO_ROOT_CRS))
+       if (pci_probe & PCI_USE__CRS)
                return;
 
        /* if only one root bus, don't need to anything */
index 4740119..2202b62 100644 (file)
@@ -515,8 +515,8 @@ char * __devinit  pcibios_setup(char *str)
        } else if (!strcmp(str, "assign-busses")) {
                pci_probe |= PCI_ASSIGN_ALL_BUSSES;
                return NULL;
-       } else if (!strcmp(str, "nocrs")) {
-               pci_probe |= PCI_NO_ROOT_CRS;
+       } else if (!strcmp(str, "use_crs")) {
+               pci_probe |= PCI_USE__CRS;
                return NULL;
        } else if (!strcmp(str, "earlydump")) {
                pci_early_dump_regs = 1;
index 8766b0e..712443e 100644 (file)
@@ -523,6 +523,69 @@ reject:
 
 static int __initdata known_bridge;
 
+static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
+
+/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
+struct acpi_mcfg_allocation *pci_mmcfg_config;
+int pci_mmcfg_config_num;
+
+static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
+{
+       if (!strcmp(mcfg->header.oem_id, "SGI"))
+               acpi_mcfg_64bit_base_addr = TRUE;
+
+       return 0;
+}
+
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
+{
+       struct acpi_table_mcfg *mcfg;
+       unsigned long i;
+       int config_size;
+
+       if (!header)
+               return -EINVAL;
+
+       mcfg = (struct acpi_table_mcfg *)header;
+
+       /* how many config structures do we have */
+       pci_mmcfg_config_num = 0;
+       i = header->length - sizeof(struct acpi_table_mcfg);
+       while (i >= sizeof(struct acpi_mcfg_allocation)) {
+               ++pci_mmcfg_config_num;
+               i -= sizeof(struct acpi_mcfg_allocation);
+       };
+       if (pci_mmcfg_config_num == 0) {
+               printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
+               return -ENODEV;
+       }
+
+       config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
+       pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
+       if (!pci_mmcfg_config) {
+               printk(KERN_WARNING PREFIX
+                      "No memory for MCFG config tables\n");
+               return -ENOMEM;
+       }
+
+       memcpy(pci_mmcfg_config, &mcfg[1], config_size);
+
+       acpi_mcfg_oem_check(mcfg);
+
+       for (i = 0; i < pci_mmcfg_config_num; ++i) {
+               if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
+                   !acpi_mcfg_64bit_base_addr) {
+                       printk(KERN_ERR PREFIX
+                              "MMCONFIG not in low 4GB of memory\n");
+                       kfree(pci_mmcfg_config);
+                       pci_mmcfg_config_num = 0;
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
 static void __init __pci_mmcfg_init(int early)
 {
        /* MMCONFIG disabled */
@@ -543,7 +606,7 @@ static void __init __pci_mmcfg_init(int early)
        }
 
        if (!known_bridge)
-               acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
+               acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
 
        pci_mmcfg_reject_broken(early);
 
index 88e42ab..0df8fcb 100644 (file)
@@ -61,6 +61,7 @@ static int acpi_ac_open_fs(struct inode *inode, struct file *file);
 static int acpi_ac_add(struct acpi_device *device);
 static int acpi_ac_remove(struct acpi_device *device, int type);
 static int acpi_ac_resume(struct acpi_device *device);
+static void acpi_ac_notify(struct acpi_device *device, u32 event);
 
 static const struct acpi_device_id ac_device_ids[] = {
        {"ACPI0003", 0},
@@ -72,10 +73,12 @@ static struct acpi_driver acpi_ac_driver = {
        .name = "ac",
        .class = ACPI_AC_CLASS,
        .ids = ac_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = acpi_ac_add,
                .remove = acpi_ac_remove,
                .resume = acpi_ac_resume,
+               .notify = acpi_ac_notify,
                },
 };
 
@@ -220,16 +223,14 @@ static int acpi_ac_remove_fs(struct acpi_device *device)
                                    Driver Model
    -------------------------------------------------------------------------- */
 
-static void acpi_ac_notify(acpi_handle handle, u32 event, void *data)
+static void acpi_ac_notify(struct acpi_device *device, u32 event)
 {
-       struct acpi_ac *ac = data;
-       struct acpi_device *device = NULL;
+       struct acpi_ac *ac = acpi_driver_data(device);
 
 
        if (!ac)
                return;
 
-       device = ac->device;
        switch (event) {
        default:
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -253,7 +254,6 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data)
 static int acpi_ac_add(struct acpi_device *device)
 {
        int result = 0;
-       acpi_status status = AE_OK;
        struct acpi_ac *ac = NULL;
 
 
@@ -286,13 +286,6 @@ static int acpi_ac_add(struct acpi_device *device)
        ac->charger.get_property = get_ac_property;
        power_supply_register(&ac->device->dev, &ac->charger);
 #endif
-       status = acpi_install_notify_handler(device->handle,
-                                            ACPI_ALL_NOTIFY, acpi_ac_notify,
-                                            ac);
-       if (ACPI_FAILURE(status)) {
-               result = -ENODEV;
-               goto end;
-       }
 
        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
               acpi_device_name(device), acpi_device_bid(device),
@@ -328,7 +321,6 @@ static int acpi_ac_resume(struct acpi_device *device)
 
 static int acpi_ac_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = AE_OK;
        struct acpi_ac *ac = NULL;
 
 
@@ -337,8 +329,6 @@ static int acpi_ac_remove(struct acpi_device *device, int type)
 
        ac = acpi_driver_data(device);
 
-       status = acpi_remove_notify_handler(device->handle,
-                                           ACPI_ALL_NOTIFY, acpi_ac_notify);
 #ifdef CONFIG_ACPI_SYSFS_POWER
        if (ac->charger.dev)
                power_supply_unregister(&ac->charger);
index b0de631..58b4517 100644 (file)
@@ -796,13 +796,12 @@ static void acpi_battery_remove_fs(struct acpi_device *device)
                                  Driver Interface
    -------------------------------------------------------------------------- */
 
-static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
+static void acpi_battery_notify(struct acpi_device *device, u32 event)
 {
-       struct acpi_battery *battery = data;
-       struct acpi_device *device;
+       struct acpi_battery *battery = acpi_driver_data(device);
+
        if (!battery)
                return;
-       device = battery->device;
        acpi_battery_update(battery);
        acpi_bus_generate_proc_event(device, event,
                                     acpi_battery_present(battery));
@@ -819,7 +818,6 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
 static int acpi_battery_add(struct acpi_device *device)
 {
        int result = 0;
-       acpi_status status = 0;
        struct acpi_battery *battery = NULL;
        if (!device)
                return -EINVAL;
@@ -834,22 +832,12 @@ static int acpi_battery_add(struct acpi_device *device)
        acpi_battery_update(battery);
 #ifdef CONFIG_ACPI_PROCFS_POWER
        result = acpi_battery_add_fs(device);
-       if (result)
-               goto end;
 #endif
-       status = acpi_install_notify_handler(device->handle,
-                                            ACPI_ALL_NOTIFY,
-                                            acpi_battery_notify, battery);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
-               result = -ENODEV;
-               goto end;
-       }
-       printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
-              ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
-              device->status.battery_present ? "present" : "absent");
-      end:
-       if (result) {
+       if (!result) {
+               printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+                       ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+                       device->status.battery_present ? "present" : "absent");
+       } else {
 #ifdef CONFIG_ACPI_PROCFS_POWER
                acpi_battery_remove_fs(device);
 #endif
@@ -860,15 +848,11 @@ static int acpi_battery_add(struct acpi_device *device)
 
 static int acpi_battery_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = 0;
        struct acpi_battery *battery = NULL;
 
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
        battery = acpi_driver_data(device);
-       status = acpi_remove_notify_handler(device->handle,
-                                           ACPI_ALL_NOTIFY,
-                                           acpi_battery_notify);
 #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_battery_remove_fs(device);
 #endif
@@ -896,10 +880,12 @@ static struct acpi_driver acpi_battery_driver = {
        .name = "battery",
        .class = ACPI_BATTERY_CLASS,
        .ids = battery_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = acpi_battery_add,
                .resume = acpi_battery_resume,
                .remove = acpi_battery_remove,
+               .notify = acpi_battery_notify,
                },
 };
 
index 09c6980..f6baa77 100644 (file)
@@ -192,6 +192,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
                     DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
                },
        },
+       {
+       .callback = dmi_disable_osi_vista,
+       .ident = "Sony VGN-NS10J_S",
+       .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
+               },
+       },
+       {
+       .callback = dmi_disable_osi_vista,
+       .ident = "Sony VGN-SR290J",
+       .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "Sony VGN-SR290J"),
+               },
+       },
 
        /*
         * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
index ae862f1..2876fc7 100644 (file)
@@ -450,18 +450,16 @@ int acpi_bus_receive_event(struct acpi_bus_event *event)
                              Notification Handling
    -------------------------------------------------------------------------- */
 
-static int
-acpi_bus_check_device(struct acpi_device *device, int *status_changed)
+static void acpi_bus_check_device(acpi_handle handle)
 {
-       acpi_status status = 0;
+       struct acpi_device *device;
+       acpi_status status;
        struct acpi_device_status old_status;
 
-
+       if (acpi_bus_get_device(handle, &device))
+               return;
        if (!device)
-               return -EINVAL;
-
-       if (status_changed)
-               *status_changed = 0;
+               return;
 
        old_status = device->status;
 
@@ -471,22 +469,15 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed)
         */
        if (device->parent && !device->parent->status.present) {
                device->status = device->parent->status;
-               if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) {
-                       if (status_changed)
-                               *status_changed = 1;
-               }
-               return 0;
+               return;
        }
 
        status = acpi_bus_get_status(device);
        if (ACPI_FAILURE(status))
-               return -ENODEV;
+               return;
 
        if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
-               return 0;
-
-       if (status_changed)
-               *status_changed = 1;
+               return;
 
        /*
         * Device Insertion/Removal
@@ -498,33 +489,17 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed)
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
                /* TBD: Handle device removal */
        }
-
-       return 0;
 }
 
-static int acpi_bus_check_scope(struct acpi_device *device)
+static void acpi_bus_check_scope(acpi_handle handle)
 {
-       int result = 0;
-       int status_changed = 0;
-
-
-       if (!device)
-               return -EINVAL;
-
        /* Status Change? */
-       result = acpi_bus_check_device(device, &status_changed);
-       if (result)
-               return result;
-
-       if (!status_changed)
-               return 0;
+       acpi_bus_check_device(handle);
 
        /*
         * TBD: Enumerate child devices within this device's scope and
         *       run acpi_bus_check_device()'s on them.
         */
-
-       return 0;
 }
 
 static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list);
@@ -547,22 +522,19 @@ EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier);
  */
 static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
 {
-       int result = 0;
        struct acpi_device *device = NULL;
+       struct acpi_driver *driver;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
+                         type, handle));
 
        blocking_notifier_call_chain(&acpi_bus_notify_list,
                type, (void *)handle);
 
-       if (acpi_bus_get_device(handle, &device))
-               return;
-
        switch (type) {
 
        case ACPI_NOTIFY_BUS_CHECK:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received BUS CHECK notification for device [%s]\n",
-                                 device->pnp.bus_id));
-               result = acpi_bus_check_scope(device);
+               acpi_bus_check_scope(handle);
                /*
                 * TBD: We'll need to outsource certain events to non-ACPI
                 *      drivers via the device manager (device.c).
@@ -570,10 +542,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                break;
 
        case ACPI_NOTIFY_DEVICE_CHECK:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received DEVICE CHECK notification for device [%s]\n",
-                                 device->pnp.bus_id));
-               result = acpi_bus_check_device(device, NULL);
+               acpi_bus_check_device(handle);
                /*
                 * TBD: We'll need to outsource certain events to non-ACPI
                 *      drivers via the device manager (device.c).
@@ -581,44 +550,26 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                break;
 
        case ACPI_NOTIFY_DEVICE_WAKE:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received DEVICE WAKE notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_EJECT_REQUEST:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received EJECT REQUEST notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received DEVICE CHECK LIGHT notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD: Exactly what does 'light' mean? */
                break;
 
        case ACPI_NOTIFY_FREQUENCY_MISMATCH:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received FREQUENCY MISMATCH notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_BUS_MODE_MISMATCH:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received BUS MODE MISMATCH notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
        case ACPI_NOTIFY_POWER_FAULT:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Received POWER FAULT notification for device [%s]\n",
-                                 device->pnp.bus_id));
                /* TBD */
                break;
 
@@ -629,7 +580,13 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
                break;
        }
 
-       return;
+       acpi_bus_get_device(handle, &device);
+       if (device) {
+               driver = device->driver;
+               if (driver && driver->ops.notify &&
+                   (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
+                       driver->ops.notify(device, type);
+       }
 }
 
 /* --------------------------------------------------------------------------
index 8bd2c2a..a8a5c29 100644 (file)
@@ -140,46 +140,6 @@ struct device *acpi_get_physical_device(acpi_handle handle)
 
 EXPORT_SYMBOL(acpi_get_physical_device);
 
-/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge
- *       This should work in general, but did not on a Lenovo T61 for the
- *      graphics card. But this must be fixed when the PCI device is
- *       bound and the kernel device struct is attached to the acpi device
- * Note: A success call will increase reference count by one
- *       Do call put_device(dev) on the returned device then
- */
-struct device *acpi_get_physical_pci_device(acpi_handle handle)
-{
-       struct device *dev;
-       long long device_id;
-       acpi_status status;
-
-       status =
-               acpi_evaluate_integer(handle, "_ADR", NULL, &device_id);
-
-       if (ACPI_FAILURE(status))
-               return NULL;
-
-       /* We need to attempt to determine whether the _ADR refers to a
-          PCI device or not. There's no terribly good way to do this,
-          so the best we can hope for is to assume that there'll never
-          be a device in the host bridge */
-       if (device_id >= 0x10000) {
-               /* It looks like a PCI device. Does it exist? */
-               dev = acpi_get_physical_device(handle);
-       } else {
-               /* It doesn't look like a PCI device. Does its parent
-                  exist? */
-               acpi_handle phandle;
-               if (acpi_get_parent(handle, &phandle))
-                       return NULL;
-               dev = acpi_get_physical_device(phandle);
-       }
-       if (!dev)
-               return NULL;
-       return dev;
-}
-EXPORT_SYMBOL(acpi_get_physical_pci_device);
-
 static int acpi_bind_one(struct device *dev, acpi_handle handle)
 {
        struct acpi_device *acpi_dev;
index d916bea..7167071 100644 (file)
@@ -79,6 +79,7 @@ static acpi_osd_handler acpi_irq_handler;
 static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
 static struct workqueue_struct *kacpi_notify_wq;
+static struct workqueue_struct *kacpi_hotplug_wq;
 
 struct acpi_res_list {
        resource_size_t start;
@@ -192,8 +193,10 @@ acpi_status acpi_os_initialize1(void)
 {
        kacpid_wq = create_singlethread_workqueue("kacpid");
        kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
+       kacpi_hotplug_wq = create_singlethread_workqueue("kacpi_hotplug");
        BUG_ON(!kacpid_wq);
        BUG_ON(!kacpi_notify_wq);
+       BUG_ON(!kacpi_hotplug_wq);
        return AE_OK;
 }
 
@@ -206,6 +209,7 @@ acpi_status acpi_os_terminate(void)
 
        destroy_workqueue(kacpid_wq);
        destroy_workqueue(kacpi_notify_wq);
+       destroy_workqueue(kacpi_hotplug_wq);
 
        return AE_OK;
 }
@@ -716,6 +720,7 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
        acpi_status status = AE_OK;
        struct acpi_os_dpc *dpc;
        struct workqueue_struct *queue;
+       work_func_t func;
        int ret;
        ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
                          "Scheduling function [%p(%p)] for deferred execution.\n",
@@ -740,15 +745,17 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
        dpc->function = function;
        dpc->context = context;
 
-       if (!hp) {
-               INIT_WORK(&dpc->work, acpi_os_execute_deferred);
-               queue = (type == OSL_NOTIFY_HANDLER) ?
-                       kacpi_notify_wq : kacpid_wq;
-               ret = queue_work(queue, &dpc->work);
-       } else {
-               INIT_WORK(&dpc->work, acpi_os_execute_hp_deferred);
-               ret = schedule_work(&dpc->work);
-       }
+       /*
+        * We can't run hotplug code in keventd_wq/kacpid_wq/kacpid_notify_wq
+        * because the hotplug code may call driver .remove() functions,
+        * which invoke flush_scheduled_work/acpi_os_wait_events_complete
+        * to flush these workqueues.
+        */
+       queue = hp ? kacpi_hotplug_wq :
+               (type == OSL_NOTIFY_HANDLER ? kacpi_notify_wq : kacpid_wq);
+       func = hp ? acpi_os_execute_hp_deferred : acpi_os_execute_deferred;
+       INIT_WORK(&dpc->work, func);
+       ret = queue_work(queue, &dpc->work);
 
        if (!ret) {
                printk(KERN_ERR PREFIX
index bc46de3..a5a77b7 100644 (file)
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
 #include <linux/types.h>
-#include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/pm.h>
 #include <linux/pci.h>
 #include <linux/acpi.h>
 #include <acpi/acpi_bus.h>
 #define _COMPONENT             ACPI_PCI_COMPONENT
 ACPI_MODULE_NAME("pci_bind");
 
-struct acpi_pci_data {
-       struct acpi_pci_id id;
-       struct pci_bus *bus;
-       struct pci_dev *dev;
-};
-
-static int acpi_pci_unbind(struct acpi_device *device);
-
-static void acpi_pci_data_handler(acpi_handle handle, u32 function,
-                                 void *context)
-{
-
-       /* TBD: Anything we need to do here? */
-
-       return;
-}
-
-/**
- * acpi_get_pci_id
- * ------------------
- * This function is used by the ACPI Interpreter (a.k.a. Core Subsystem)
- * to resolve PCI information for ACPI-PCI devices defined in the namespace.
- * This typically occurs when resolving PCI operation region information.
- */
-acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id)
+static int acpi_pci_unbind(struct acpi_device *device)
 {
-       int result = 0;
-       acpi_status status = AE_OK;
-       struct acpi_device *device = NULL;
-       struct acpi_pci_data *data = NULL;
-
-
-       if (!id)
-               return AE_BAD_PARAMETER;
-
-       result = acpi_bus_get_device(handle, &device);
-       if (result) {
-               printk(KERN_ERR PREFIX
-                           "Invalid ACPI Bus context for device %s\n",
-                           acpi_device_bid(device));
-               return AE_NOT_EXIST;
-       }
-
-       status = acpi_get_data(handle, acpi_pci_data_handler, (void **)&data);
-       if (ACPI_FAILURE(status) || !data) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Invalid ACPI-PCI context for device %s",
-                               acpi_device_bid(device)));
-               return status;
-       }
+       struct pci_dev *dev;
 
-       *id = data->id;
+       dev = acpi_get_pci_dev(device->handle);
+       if (!dev || !dev->subordinate)
+               goto out;
 
-       /*
-          id->segment = data->id.segment;
-          id->bus = data->id.bus;
-          id->device = data->id.device;
-          id->function = data->id.function;
-        */
+       acpi_pci_irq_del_prt(dev->subordinate);
 
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                         "Device %s has PCI address %04x:%02x:%02x.%d\n",
-                         acpi_device_bid(device), id->segment, id->bus,
-                         id->device, id->function));
+       device->ops.bind = NULL;
+       device->ops.unbind = NULL;
 
-       return AE_OK;
+out:
+       pci_dev_put(dev);
+       return 0;
 }
 
-EXPORT_SYMBOL(acpi_get_pci_id);
-
-int acpi_pci_bind(struct acpi_device *device)
+static int acpi_pci_bind(struct acpi_device *device)
 {
-       int result = 0;
        acpi_status status;
-       struct acpi_pci_data *data;
-       struct acpi_pci_data *pdata;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
        acpi_handle handle;
+       struct pci_bus *bus;
+       struct pci_dev *dev;
 
-       if (!device || !device->parent)
-               return -EINVAL;
-
-       data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
-       if (ACPI_FAILURE(status)) {
-               kfree(data);
-               return -ENODEV;
-       }
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n",
-                         (char *)buffer.pointer));
-
-       /* 
-        * Segment & Bus
-        * -------------
-        * These are obtained via the parent device's ACPI-PCI context.
-        */
-       status = acpi_get_data(device->parent->handle, acpi_pci_data_handler,
-                              (void **)&pdata);
-       if (ACPI_FAILURE(status) || !pdata || !pdata->bus) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Invalid ACPI-PCI context for parent device %s",
-                               acpi_device_bid(device->parent)));
-               result = -ENODEV;
-               goto end;
-       }
-       data->id.segment = pdata->id.segment;
-       data->id.bus = pdata->bus->number;
-
-       /*
-        * Device & Function
-        * -----------------
-        * These are simply obtained from the device's _ADR method.  Note
-        * that a value of zero is valid.
-        */
-       data->id.device = device->pnp.bus_address >> 16;
-       data->id.function = device->pnp.bus_address & 0xFFFF;
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %04x:%02x:%02x.%d\n",
-                         data->id.segment, data->id.bus, data->id.device,
-                         data->id.function));
-
-       /*
-        * TBD: Support slot devices (e.g. function=0xFFFF).
-        */
-
-       /* 
-        * Locate PCI Device
-        * -----------------
-        * Locate matching device in PCI namespace.  If it doesn't exist
-        * this typically means that the device isn't currently inserted
-        * (e.g. docking station, port replicator, etc.).
-        */
-       data->dev = pci_get_slot(pdata->bus,
-                               PCI_DEVFN(data->id.device, data->id.function));
-       if (!data->dev) {
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Device %04x:%02x:%02x.%d not present in PCI namespace\n",
-                                 data->id.segment, data->id.bus,
-                                 data->id.device, data->id.function));
-               result = -ENODEV;
-               goto end;
-       }
-       if (!data->dev->bus) {
-               printk(KERN_ERR PREFIX
-                           "Device %04x:%02x:%02x.%d has invalid 'bus' field\n",
-                           data->id.segment, data->id.bus,
-                           data->id.device, data->id.function);
-               result = -ENODEV;
-               goto end;
-       }
+       dev = acpi_get_pci_dev(device->handle);
+       if (!dev)
+               return 0;
 
        /*
-        * PCI Bridge?
-        * -----------
-        * If so, set the 'bus' field and install the 'bind' function to 
-        * facilitate callbacks for all of its children.
+        * Install the 'bind' function to facilitate callbacks for
+        * children of the P2P bridge.
         */
-       if (data->dev->subordinate) {
+       if (dev->subordinate) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                  "Device %04x:%02x:%02x.%d is a PCI bridge\n",
-                                 data->id.segment, data->id.bus,
-                                 data->id.device, data->id.function));
-               data->bus = data->dev->subordinate;
+                                 pci_domain_nr(dev->bus), dev->bus->number,
+                                 PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)));
                device->ops.bind = acpi_pci_bind;
                device->ops.unbind = acpi_pci_unbind;
        }
 
        /*
-        * Attach ACPI-PCI Context
-        * -----------------------
-        * Thus binding the ACPI and PCI devices.
-        */
-       status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Unable to attach ACPI-PCI context to device %s",
-                               acpi_device_bid(device)));
-               result = -ENODEV;
-               goto end;
-       }
-
-       /*
-        * PCI Routing Table
-        * -----------------
-        * Evaluate and parse _PRT, if exists.  This code is independent of 
-        * PCI bridges (above) to allow parsing of _PRT objects within the
-        * scope of non-bridge devices.  Note that _PRTs within the scope of
-        * a PCI bridge assume the bridge's subordinate bus number.
+        * Evaluate and parse _PRT, if exists.  This code allows parsing of
+        * _PRT objects within the scope of non-bridge devices.  Note that
+        * _PRTs within the scope of a PCI bridge assume the bridge's
+        * subordinate bus number.
         *
         * TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
         */
        status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
-       if (ACPI_SUCCESS(status)) {
-               if (data->bus)  /* PCI-PCI bridge */
-                       acpi_pci_irq_add_prt(device->handle, data->id.segment,
-                                            data->bus->number);
-               else            /* non-bridge PCI device */
-                       acpi_pci_irq_add_prt(device->handle, data->id.segment,
-                                            data->id.bus);
-       }
-
-      end:
-       kfree(buffer.pointer);
-       if (result) {
-               pci_dev_put(data->dev);
-               kfree(data);
-       }
-       return result;
-}
-
-static int acpi_pci_unbind(struct acpi_device *device)
-{
-       int result = 0;
-       acpi_status status;
-       struct acpi_pci_data *data;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-
-
-       if (!device || !device->parent)
-               return -EINVAL;
-
-       status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
        if (ACPI_FAILURE(status))
-               return -ENODEV;
+               goto out;
 
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unbinding PCI device [%s]...\n",
-                         (char *) buffer.pointer));
-       kfree(buffer.pointer);
+       if (dev->subordinate)
+               bus = dev->subordinate;
+       else
+               bus = dev->bus;
 
-       status =
-           acpi_get_data(device->handle, acpi_pci_data_handler,
-                         (void **)&data);
-       if (ACPI_FAILURE(status)) {
-               result = -ENODEV;
-               goto end;
-       }
+       acpi_pci_irq_add_prt(device->handle, bus);
 
-       status = acpi_detach_data(device->handle, acpi_pci_data_handler);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Unable to detach data from device %s",
-                               acpi_device_bid(device)));
-               result = -ENODEV;
-               goto end;
-       }
-       if (data->dev->subordinate) {
-               acpi_pci_irq_del_prt(data->id.segment, data->bus->number);
-       }
-       pci_dev_put(data->dev);
-       kfree(data);
-
-      end:
-       return result;
+out:
+       pci_dev_put(dev);
+       return 0;
 }
 
-int
-acpi_pci_bind_root(struct acpi_device *device,
-                  struct acpi_pci_id *id, struct pci_bus *bus)
+int acpi_pci_bind_root(struct acpi_device *device)
 {
-       int result = 0;
-       acpi_status status;
-       struct acpi_pci_data *data = NULL;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-
-       if (!device || !id || !bus) {
-               return -EINVAL;
-       }
-
-       data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       data->id = *id;
-       data->bus = bus;
        device->ops.bind = acpi_pci_bind;
        device->ops.unbind = acpi_pci_unbind;
 
-       status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
-       if (ACPI_FAILURE(status)) {
-               kfree (data);
-               return -ENODEV;
-       }
-
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to "
-                       "%04x:%02x\n", (char *)buffer.pointer,
-                       id->segment, id->bus));
-
-       status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
-       if (ACPI_FAILURE(status)) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "Unable to attach ACPI-PCI context to device %s",
-                               (char *)buffer.pointer));
-               result = -ENODEV;
-               goto end;
-       }
-
-      end:
-       kfree(buffer.pointer);
-       if (result != 0)
-               kfree(data);
-
-       return result;
+       return 0;
 }
index 2faa9e2..b794eb8 100644 (file)
@@ -182,7 +182,7 @@ static void do_prt_fixups(struct acpi_prt_entry *entry,
        }
 }
 
-static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
+static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus,
                                  struct acpi_pci_routing_table *prt)
 {
        struct acpi_prt_entry *entry;
@@ -196,8 +196,8 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
         * 1=INTA, 2=INTB.  We use the PCI encoding throughout, so convert
         * it here.
         */
-       entry->id.segment = segment;
-       entry->id.bus = bus;
+       entry->id.segment = pci_domain_nr(bus);
+       entry->id.bus = bus->number;
        entry->id.device = (prt->address >> 16) & 0xFFFF;
        entry->pin = prt->pin + 1;
 
@@ -242,7 +242,7 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
        return 0;
 }
 
-int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
+int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus)
 {
        acpi_status status;
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -271,7 +271,7 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
 
        entry = buffer.pointer;
        while (entry && (entry->length > 0)) {
-               acpi_pci_irq_add_entry(handle, segment, bus, entry);
+               acpi_pci_irq_add_entry(handle, bus, entry);
                entry = (struct acpi_pci_routing_table *)
                    ((unsigned long)entry + entry->length);
        }
@@ -280,16 +280,17 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
        return 0;
 }
 
-void acpi_pci_irq_del_prt(int segment, int bus)
+void acpi_pci_irq_del_prt(struct pci_bus *bus)
 {
        struct acpi_prt_entry *entry, *tmp;
 
        printk(KERN_DEBUG
               "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n",
-              segment, bus);
+              pci_domain_nr(bus), bus->number);
        spin_lock(&acpi_prt_lock);
        list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) {
-               if (segment == entry->id.segment && bus == entry->id.bus) {
+               if (pci_domain_nr(bus) == entry->id.segment
+                       && bus->number == entry->id.bus) {
                        list_del(&entry->list);
                        kfree(entry);
                }
index 196f97d..8a5bf3b 100644 (file)
@@ -63,9 +63,10 @@ static struct acpi_driver acpi_pci_root_driver = {
 
 struct acpi_pci_root {
        struct list_head node;
-       struct acpi_device * device;
-       struct acpi_pci_id id;
+       struct acpi_device *device;
        struct pci_bus *bus;
+       u16 segment;
+       u8 bus_nr;
 
        u32 osc_support_set;    /* _OSC state of support bits */
        u32 osc_control_set;    /* _OSC state of control bits */
@@ -82,7 +83,7 @@ static DEFINE_MUTEX(osc_lock);
 int acpi_pci_register_driver(struct acpi_pci_driver *driver)
 {
        int n = 0;
-       struct list_head *entry;
+       struct acpi_pci_root *root;
 
        struct acpi_pci_driver **pptr = &sub_driver;
        while (*pptr)
@@ -92,9 +93,7 @@ int acpi_pci_register_driver(struct acpi_pci_driver *driver)
        if (!driver->add)
                return 0;
 
-       list_for_each(entry, &acpi_pci_roots) {
-               struct acpi_pci_root *root;
-               root = list_entry(entry, struct acpi_pci_root, node);
+       list_for_each_entry(root, &acpi_pci_roots, node) {
                driver->add(root->device->handle);
                n++;
        }
@@ -106,7 +105,7 @@ EXPORT_SYMBOL(acpi_pci_register_driver);
 
 void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
 {
-       struct list_head *entry;
+       struct acpi_pci_root *root;
 
        struct acpi_pci_driver **pptr = &sub_driver;
        while (*pptr) {
@@ -120,28 +119,48 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
        if (!driver->remove)
                return;
 
-       list_for_each(entry, &acpi_pci_roots) {
-               struct acpi_pci_root *root;
-               root = list_entry(entry, struct acpi_pci_root, node);
+       list_for_each_entry(root, &acpi_pci_roots, node)
                driver->remove(root->device->handle);
-       }
 }
 
 EXPORT_SYMBOL(acpi_pci_unregister_driver);
 
 acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
 {
-       struct acpi_pci_root *tmp;
+       struct acpi_pci_root *root;
        
-       list_for_each_entry(tmp, &acpi_pci_roots, node) {
-               if ((tmp->id.segment == (u16) seg) && (tmp->id.bus == (u16) bus))
-                       return tmp->device->handle;
-       }
+       list_for_each_entry(root, &acpi_pci_roots, node)
+               if ((root->segment == (u16) seg) && (root->bus_nr == (u16) bus))
+                       return root->device->handle;
        return NULL;            
 }
 
 EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
 
+/**
+ * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge
+ * @handle - the ACPI CA node in question.
+ *
+ * Note: we could make this API take a struct acpi_device * instead, but
+ * for now, it's more convenient to operate on an acpi_handle.
+ */
+int acpi_is_root_bridge(acpi_handle handle)
+{
+       int ret;
+       struct acpi_device *device;
+
+       ret = acpi_bus_get_device(handle, &device);
+       if (ret)
+               return 0;
+
+       ret = acpi_match_device_ids(device, root_device_ids);
+       if (ret)
+               return 0;
+       else
+               return 1;
+}
+EXPORT_SYMBOL_GPL(acpi_is_root_bridge);
+
 static acpi_status
 get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
 {
@@ -161,19 +180,22 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
        return AE_OK;
 }
 
-static acpi_status try_get_root_bridge_busnr(acpi_handle handle, int *busnum)
+static acpi_status try_get_root_bridge_busnr(acpi_handle handle,
+                                            unsigned long long *bus)
 {
        acpi_status status;
+       int busnum;
 
-       *busnum = -1;
+       busnum = -1;
        status =
            acpi_walk_resources(handle, METHOD_NAME__CRS,
-                               get_root_bridge_busnr_callback, busnum);
+                               get_root_bridge_busnr_callback, &busnum);
        if (ACPI_FAILURE(status))
                return status;
        /* Check if we really get a bus number from _CRS */
-       if (*busnum == -1)
+       if (busnum == -1)
                return AE_ERROR;
+       *bus = busnum;
        return AE_OK;
 }
 
@@ -298,6 +320,7 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
 static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
 {
        struct acpi_pci_root *root;
+
        list_for_each_entry(root, &acpi_pci_roots, node) {
                if (root->device->handle == handle)
                        return root;
@@ -305,6 +328,87 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
        return NULL;
 }
 
+struct acpi_handle_node {
+       struct list_head node;
+       acpi_handle handle;
+};
+
+/**
+ * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev
+ * @handle: the handle in question
+ *
+ * Given an ACPI CA handle, the desired PCI device is located in the
+ * list of PCI devices.
+ *
+ * If the device is found, its reference count is increased and this
+ * function returns a pointer to its data structure.  The caller must
+ * decrement the reference count by calling pci_dev_put().
+ * If no device is found, %NULL is returned.
+ */
+struct pci_dev *acpi_get_pci_dev(acpi_handle handle)
+{
+       int dev, fn;
+       unsigned long long adr;
+       acpi_status status;
+       acpi_handle phandle;
+       struct pci_bus *pbus;
+       struct pci_dev *pdev = NULL;
+       struct acpi_handle_node *node, *tmp;
+       struct acpi_pci_root *root;
+       LIST_HEAD(device_list);
+
+       /*
+        * Walk up the ACPI CA namespace until we reach a PCI root bridge.
+        */
+       phandle = handle;
+       while (!acpi_is_root_bridge(phandle)) {
+               node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL);
+               if (!node)
+                       goto out;
+
+               INIT_LIST_HEAD(&node->node);
+               node->handle = phandle;
+               list_add(&node->node, &device_list);
+
+               status = acpi_get_parent(phandle, &phandle);
+               if (ACPI_FAILURE(status))
+                       goto out;
+       }
+
+       root = acpi_pci_find_root(phandle);
+       if (!root)
+               goto out;
+
+       pbus = root->bus;
+
+       /*
+        * Now, walk back down the PCI device tree until we return to our
+        * original handle. Assumes that everything between the PCI root
+        * bridge and the device we're looking for must be a P2P bridge.
+        */
+       list_for_each_entry(node, &device_list, node) {
+               acpi_handle hnd = node->handle;
+               status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr);
+               if (ACPI_FAILURE(status))
+                       goto out;
+               dev = (adr >> 16) & 0xffff;
+               fn  = adr & 0xffff;
+
+               pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn));
+               if (hnd == handle)
+                       break;
+
+               pbus = pdev->subordinate;
+               pci_dev_put(pdev);
+       }
+out:
+       list_for_each_entry_safe(node, tmp, &device_list, node)
+               kfree(node);
+
+       return pdev;
+}
+EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
+
 /**
  * acpi_pci_osc_control_set - commit requested control to Firmware
  * @handle: acpi_handle for the target ACPI object
@@ -363,31 +467,46 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set);
 
 static int __devinit acpi_pci_root_add(struct acpi_device *device)
 {
-       int result = 0;
-       struct acpi_pci_root *root = NULL;
-       struct acpi_pci_root *tmp;
-       acpi_status status = AE_OK;
-       unsigned long long value = 0;
-       acpi_handle handle = NULL;
+       unsigned long long segment, bus;
+       acpi_status status;
+       int result;
+       struct acpi_pci_root *root;
+       acpi_handle handle;
        struct acpi_device *child;
        u32 flags, base_flags;
 
+       segment = 0;
+       status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL,
+                                      &segment);
+       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+               printk(KERN_ERR PREFIX "can't evaluate _SEG\n");
+               return -ENODEV;
+       }
 
-       if (!device)
-               return -EINVAL;
+       /* Check _CRS first, then _BBN.  If no _BBN, default to zero. */
+       bus = 0;
+       status = try_get_root_bridge_busnr(device->handle, &bus);
+       if (ACPI_FAILURE(status)) {
+               status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN,                                               NULL, &bus);
+               if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+                       printk(KERN_ERR PREFIX
+                            "no bus number in _CRS and can't evaluate _BBN\n");
+                       return -ENODEV;
+               }
+       }
 
        root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
        if (!root)
                return -ENOMEM;
-       INIT_LIST_HEAD(&root->node);
 
+       INIT_LIST_HEAD(&root->node);
        root->device = device;
+       root->segment = segment & 0xFFFF;
+       root->bus_nr = bus & 0xFF;
        strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
        strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
        device->driver_data = root;
 
-       device->ops.bind = acpi_pci_bind;
-
        /*
         * All supported architectures that use ACPI have support for
         * PCI domains, so we indicate this in _OSC support capabilities.
@@ -395,79 +514,6 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
        acpi_pci_osc_support(root, flags);
 
-       /* 
-        * Segment
-        * -------
-        * Obtained via _SEG, if exists, otherwise assumed to be zero (0).
-        */
-       status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL,
-                                      &value);
-       switch (status) {
-       case AE_OK:
-               root->id.segment = (u16) value;
-               break;
-       case AE_NOT_FOUND:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Assuming segment 0 (no _SEG)\n"));
-               root->id.segment = 0;
-               break;
-       default:
-               ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SEG"));
-               result = -ENODEV;
-               goto end;
-       }
-
-       /* 
-        * Bus
-        * ---
-        * Obtained via _BBN, if exists, otherwise assumed to be zero (0).
-        */
-       status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL,
-                                      &value);
-       switch (status) {
-       case AE_OK:
-               root->id.bus = (u16) value;
-               break;
-       case AE_NOT_FOUND:
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming bus 0 (no _BBN)\n"));
-               root->id.bus = 0;
-               break;
-       default:
-               ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BBN"));
-               result = -ENODEV;
-               goto end;
-       }
-
-       /* Some systems have wrong _BBN */
-       list_for_each_entry(tmp, &acpi_pci_roots, node) {
-               if ((tmp->id.segment == root->id.segment)
-                   && (tmp->id.bus == root->id.bus)) {
-                       int bus = 0;
-                       acpi_status status;
-
-                       printk(KERN_ERR PREFIX
-                                   "Wrong _BBN value, reboot"
-                                   " and use option 'pci=noacpi'\n");
-
-                       status = try_get_root_bridge_busnr(device->handle, &bus);
-                       if (ACPI_FAILURE(status))
-                               break;
-                       if (bus != root->id.bus) {
-                               printk(KERN_INFO PREFIX
-                                      "PCI _CRS %d overrides _BBN 0\n", bus);
-                               root->id.bus = bus;
-                       }
-                       break;
-               }
-       }
-       /*
-        * Device & Function
-        * -----------------
-        * Obtained from _ADR (which has already been evaluated for us).
-        */
-       root->id.device = device->pnp.bus_address >> 16;
-       root->id.function = device->pnp.bus_address & 0xFFFF;
-
        /*
         * TBD: Need PCI interface for enumeration/configuration of roots.
         */
@@ -477,7 +523,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
 
        printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n",
               acpi_device_name(device), acpi_device_bid(device),
-              root->id.segment, root->id.bus);
+              root->segment, root->bus_nr);
 
        /*
         * Scan the Root Bridge
@@ -486,11 +532,11 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
         * PCI namespace does not get created until this call is made (and 
         * thus the root bridge's pci_dev does not exist).
         */
-       root->bus = pci_acpi_scan_root(device, root->id.segment, root->id.bus);
+       root->bus = pci_acpi_scan_root(device, segment, bus);
        if (!root->bus) {
                printk(KERN_ERR PREFIX
                            "Bus %04x:%02x not present in PCI namespace\n",
-                           root->id.segment, root->id.bus);
+                           root->segment, root->bus_nr);
                result = -ENODEV;
                goto end;
        }
@@ -500,7 +546,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
         * -----------------------
         * Thus binding the ACPI and PCI devices.
         */
-       result = acpi_pci_bind_root(device, &root->id, root->bus);
+       result = acpi_pci_bind_root(device);
        if (result)
                goto end;
 
@@ -511,8 +557,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
         */
        status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
        if (ACPI_SUCCESS(status))
-               result = acpi_pci_irq_add_prt(device->handle, root->id.segment,
-                                             root->id.bus);
+               result = acpi_pci_irq_add_prt(device->handle, root->bus);
 
        /*
         * Scan and bind all _ADR-Based Devices
@@ -531,42 +576,28 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        if (flags != base_flags)
                acpi_pci_osc_support(root, flags);
 
-      end:
-       if (result) {
-               if (!list_empty(&root->node))
-                       list_del(&root->node);
-               kfree(root);
-       }
+       return 0;
 
+end:
+       if (!list_empty(&root->node))
+               list_del(&root->node);
+       kfree(root);
        return result;
 }
 
 static int acpi_pci_root_start(struct acpi_device *device)
 {
-       struct acpi_pci_root *root;
+       struct acpi_pci_root *root = acpi_driver_data(device);
 
-
-       list_for_each_entry(root, &acpi_pci_roots, node) {
-               if (root->device == device) {
-                       pci_bus_add_devices(root->bus);
-                       return 0;
-               }
-       }
-       return -ENODEV;
+       pci_bus_add_devices(root->bus);
+       return 0;
 }
 
 static int acpi_pci_root_remove(struct acpi_device *device, int type)
 {
-       struct acpi_pci_root *root = NULL;
-
-
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
-       root = acpi_driver_data(device);
+       struct acpi_pci_root *root = acpi_driver_data(device);
 
        kfree(root);
-
        return 0;
 }
 
index 56665a6..d74365d 100644 (file)
@@ -194,7 +194,7 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
 
 static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
 {
-       int result = 0, state;
+       int result = 0;
        int found = 0;
        acpi_status status = AE_OK;
        struct acpi_power_resource *resource = NULL;
@@ -236,18 +236,6 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
        if (ACPI_FAILURE(status))
                return -ENODEV;
 
-       if (!acpi_power_nocheck) {
-               /*
-                * If acpi_power_nocheck is set, it is unnecessary to check
-                * the power state after power transition.
-                */
-               result = acpi_power_get_state(resource->device->handle,
-                               &state);
-               if (result)
-                       return result;
-               if (state != ACPI_POWER_RESOURCE_STATE_ON)
-                       return -ENOEXEC;
-       }
        /* Update the power resource's _device_ power state */
        resource->device->power.state = ACPI_STATE_D0;
 
@@ -258,7 +246,7 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
 
 static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
 {
-       int result = 0, state;
+       int result = 0;
        acpi_status status = AE_OK;
        struct acpi_power_resource *resource = NULL;
        struct list_head *node, *next;
@@ -293,18 +281,6 @@ static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
        if (ACPI_FAILURE(status))
                return -ENODEV;
 
-       if (!acpi_power_nocheck) {
-               /*
-                * If acpi_power_nocheck is set, it is unnecessary to check
-                * the power state after power transition.
-                */
-               result = acpi_power_get_state(handle, &state);
-               if (result)
-                       return result;
-               if (state != ACPI_POWER_RESOURCE_STATE_OFF)
-                       return -ENOEXEC;
-       }
-
        /* Update the power resource's _device_ power state */
        resource->device->power.state = ACPI_STATE_D3;
 
index 23f0fb8..84e0f3c 100644 (file)
@@ -89,7 +89,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr);
 
 static const struct acpi_device_id processor_device_ids[] = {
        {ACPI_PROCESSOR_OBJECT_HID, 0},
-       {ACPI_PROCESSOR_HID, 0},
+       {"ACPI0007", 0},
        {"", 0},
 };
 MODULE_DEVICE_TABLE(acpi, processor_device_ids);
@@ -596,7 +596,21 @@ static int acpi_processor_get_info(struct acpi_device *device)
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                  "No bus mastering arbitration control\n"));
 
-       if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_HID)) {
+       if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
+               /* Declared with "Processor" statement; match ProcessorID */
+               status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
+               if (ACPI_FAILURE(status)) {
+                       printk(KERN_ERR PREFIX "Evaluating processor object\n");
+                       return -ENODEV;
+               }
+
+               /*
+                * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
+                *      >>> 'acpi_get_processor_id(acpi_id, &id)' in
+                *      arch/xxx/acpi.c
+                */
+               pr->acpi_id = object.processor.proc_id;
+       } else {
                /*
                 * Declared with "Device" statement; match _UID.
                 * Note that we don't handle string _UIDs yet.
@@ -611,20 +625,6 @@ static int acpi_processor_get_info(struct acpi_device *device)
                }
                device_declaration = 1;
                pr->acpi_id = value;
-       } else {
-               /* Declared with "Processor" statement; match ProcessorID */
-               status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
-               if (ACPI_FAILURE(status)) {
-                       printk(KERN_ERR PREFIX "Evaluating processor object\n");
-                       return -ENODEV;
-               }
-
-               /*
-                * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
-                *      >>> 'acpi_get_processor_id(acpi_id, &id)' in
-                *      arch/xxx/acpi.c
-                */
-               pr->acpi_id = object.processor.proc_id;
        }
        cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id);
 
@@ -649,7 +649,16 @@ static int acpi_processor_get_info(struct acpi_device *device)
                        return -ENODEV;
                }
        }
-
+       /*
+        * On some boxes several processors use the same processor bus id.
+        * But they are located in different scope. For example:
+        * \_SB.SCK0.CPU0
+        * \_SB.SCK1.CPU0
+        * Rename the processor device bus id. And the new bus id will be
+        * generated as the following format:
+        * CPU+CPU ID.
+        */
+       sprintf(acpi_device_bid(device), "CPU%X", pr->id);
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id,
                          pr->acpi_id));
 
@@ -731,6 +740,8 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device)
        /* _PDC call should be done before doing anything else (if reqd.). */
        arch_acpi_processor_init_pdc(pr);
        acpi_processor_set_pdc(pr);
+       arch_acpi_processor_cleanup_pdc(pr);
+
 #ifdef CONFIG_CPU_FREQ
        acpi_processor_ppc_has_changed(pr);
 #endif
index 10a2d91..0efa59e 100644 (file)
@@ -139,7 +139,7 @@ static void acpi_safe_halt(void)
  * are affected too. We pick the most conservative approach: we assume
  * that the local APIC stops in both C2 and C3.
  */
-static void acpi_timer_check_state(int state, struct acpi_processor *pr,
+static void lapic_timer_check_state(int state, struct acpi_processor *pr,
                                   struct acpi_processor_cx *cx)
 {
        struct acpi_processor_power *pwr = &pr->power;
@@ -162,7 +162,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr,
                pr->power.timer_broadcast_on_state = state;
 }
 
-static void acpi_propagate_timer_broadcast(struct acpi_processor *pr)
+static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)
 {
        unsigned long reason;
 
@@ -173,7 +173,7 @@ static void acpi_propagate_timer_broadcast(struct acpi_processor *pr)
 }
 
 /* Power(C) State timer broadcast control */
-static void acpi_state_timer_broadcast(struct acpi_processor *pr,
+static void lapic_timer_state_broadcast(struct acpi_processor *pr,
                                       struct acpi_processor_cx *cx,
                                       int broadcast)
 {
@@ -190,10 +190,10 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr,
 
 #else
 
-static void acpi_timer_check_state(int state, struct acpi_processor *pr,
+static void lapic_timer_check_state(int state, struct acpi_processor *pr,
                                   struct acpi_processor_cx *cstate) { }
-static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) { }
-static void acpi_state_timer_broadcast(struct acpi_processor *pr,
+static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { }
+static void lapic_timer_state_broadcast(struct acpi_processor *pr,
                                       struct acpi_processor_cx *cx,
                                       int broadcast)
 {
@@ -515,7 +515,8 @@ static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx)
 static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
                                           struct acpi_processor_cx *cx)
 {
-       static int bm_check_flag;
+       static int bm_check_flag = -1;
+       static int bm_control_flag = -1;
 
 
        if (!cx->address)
@@ -545,12 +546,14 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
        }
 
        /* All the logic here assumes flags.bm_check is same across all CPUs */
-       if (!bm_check_flag) {
+       if (bm_check_flag == -1) {
                /* Determine whether bm_check is needed based on CPU  */
                acpi_processor_power_init_bm_check(&(pr->flags), pr->id);
                bm_check_flag = pr->flags.bm_check;
+               bm_control_flag = pr->flags.bm_control;
        } else {
                pr->flags.bm_check = bm_check_flag;
+               pr->flags.bm_control = bm_control_flag;
        }
 
        if (pr->flags.bm_check) {
@@ -614,29 +617,25 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
                switch (cx->type) {
                case ACPI_STATE_C1:
                        cx->valid = 1;
-                       acpi_timer_check_state(i, pr, cx);
                        break;
 
                case ACPI_STATE_C2:
                        acpi_processor_power_verify_c2(cx);
-                       if (cx->valid)
-                               acpi_timer_check_state(i, pr, cx);
                        break;
 
                case ACPI_STATE_C3:
                        acpi_processor_power_verify_c3(pr, cx);
-                       if (cx->valid)
-                               acpi_timer_check_state(i, pr, cx);
                        break;
                }
-               if (cx->valid)
-                       tsc_check_state(cx->type);
+               if (!cx->valid)
+                       continue;
 
-               if (cx->valid)
-                       working++;
+               lapic_timer_check_state(i, pr, cx);
+               tsc_check_state(cx->type);
+               working++;
        }
 
-       acpi_propagate_timer_broadcast(pr);
+       lapic_timer_propagate_broadcast(pr);
 
        return (working);
 }
@@ -839,7 +838,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
                return 0;
        }
 
-       acpi_state_timer_broadcast(pr, cx, 1);
+       lapic_timer_state_broadcast(pr, cx, 1);
        kt1 = ktime_get_real();
        acpi_idle_do_entry(cx);
        kt2 = ktime_get_real();
@@ -847,7 +846,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
 
        local_irq_enable();
        cx->usage++;
-       acpi_state_timer_broadcast(pr, cx, 0);
+       lapic_timer_state_broadcast(pr, cx, 0);
 
        return idle_time;
 }
@@ -892,7 +891,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
         * Must be done before busmaster disable as we might need to
         * access HPET !
         */
-       acpi_state_timer_broadcast(pr, cx, 1);
+       lapic_timer_state_broadcast(pr, cx, 1);
 
        if (cx->type == ACPI_STATE_C3)
                ACPI_FLUSH_CPU_CACHE();
@@ -914,7 +913,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
 
        cx->usage++;
 
-       acpi_state_timer_broadcast(pr, cx, 0);
+       lapic_timer_state_broadcast(pr, cx, 0);
        cx->time += sleep_ticks;
        return idle_time;
 }
@@ -981,7 +980,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
         * Must be done before busmaster disable as we might need to
         * access HPET !
         */
-       acpi_state_timer_broadcast(pr, cx, 1);
+       lapic_timer_state_broadcast(pr, cx, 1);
 
        kt1 = ktime_get_real();
        /*
@@ -1026,7 +1025,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 
        cx->usage++;
 
-       acpi_state_timer_broadcast(pr, cx, 0);
+       lapic_timer_state_broadcast(pr, cx, 0);
        cx->time += sleep_ticks;
        return idle_time;
 }
index 8ff510b..781435d 100644 (file)
@@ -95,7 +95,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
 }
 static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
 
-static int acpi_bus_hot_remove_device(void *context)
+static void acpi_bus_hot_remove_device(void *context)
 {
        struct acpi_device *device;
        acpi_handle handle = context;
@@ -104,10 +104,10 @@ static int acpi_bus_hot_remove_device(void *context)
        acpi_status status = AE_OK;
 
        if (acpi_bus_get_device(handle, &device))
-               return 0;
+               return;
 
        if (!device)
-               return 0;
+               return;
 
        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                "Hot-removing device %s...\n", dev_name(&device->dev)));
@@ -115,7 +115,7 @@ static int acpi_bus_hot_remove_device(void *context)
        if (acpi_bus_trim(device, 1)) {
                printk(KERN_ERR PREFIX
                                "Removing device failed\n");
-               return -1;
+               return;
        }
 
        /* power off device */
@@ -142,9 +142,10 @@ static int acpi_bus_hot_remove_device(void *context)
         */
        status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
        if (ACPI_FAILURE(status))
-               return -ENODEV;
+               printk(KERN_WARNING PREFIX
+                               "Eject device failed\n");
 
-       return 0;
+       return;
 }
 
 static ssize_t
@@ -155,7 +156,6 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
        acpi_status status;
        acpi_object_type type = 0;
        struct acpi_device *acpi_device = to_acpi_device(d);
-       struct task_struct *task;
 
        if ((!count) || (buf[0] != '1')) {
                return -EINVAL;
@@ -172,11 +172,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
                goto err;
        }
 
-       /* remove the device in another thread to fix the deadlock issue */
-       task = kthread_run(acpi_bus_hot_remove_device,
-                               acpi_device->handle, "acpi_hot_remove_device");
-       if (IS_ERR(task))
-               ret = PTR_ERR(task);
+       acpi_os_hotplug_execute(acpi_bus_hot_remove_device, acpi_device->handle);
 err:
        return ret;
 }
@@ -198,12 +194,12 @@ acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *b
        int result;
 
        result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path);
-       if(result)
+       if (result)
                goto end;
 
        result = sprintf(buf, "%s\n", (char*)path.pointer);
        kfree(path.pointer);
-  end:
+end:
        return result;
 }
 static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL);
@@ -217,21 +213,21 @@ static int acpi_device_setup_files(struct acpi_device *dev)
        /*
         * Devices gotten from FADT don't have a "path" attribute
         */
-       if(dev->handle) {
+       if (dev->handle) {
                result = device_create_file(&dev->dev, &dev_attr_path);
-               if(result)
+               if (result)
                        goto end;
        }
 
-       if(dev->flags.hardware_id) {
+       if (dev->flags.hardware_id) {
                result = device_create_file(&dev->dev, &dev_attr_hid);
-               if(result)
+               if (result)
                        goto end;
        }
 
-       if (dev->flags.hardware_id || dev->flags.compatible_ids){
+       if (dev->flags.hardware_id || dev->flags.compatible_ids) {
                result = device_create_file(&dev->dev, &dev_attr_modalias);
-               if(result)
+               if (result)
                        goto end;
        }
 
@@ -242,7 +238,7 @@ static int acpi_device_setup_files(struct acpi_device *dev)
        status = acpi_get_handle(dev->handle, "_EJ0", &temp);
        if (ACPI_SUCCESS(status))
                result = device_create_file(&dev->dev, &dev_attr_eject);
-  end:
+end:
        return result;
 }
 
@@ -262,9 +258,9 @@ static void acpi_device_remove_files(struct acpi_device *dev)
        if (dev->flags.hardware_id || dev->flags.compatible_ids)
                device_remove_file(&dev->dev, &dev_attr_modalias);
 
-       if(dev->flags.hardware_id)
+       if (dev->flags.hardware_id)
                device_remove_file(&dev->dev, &dev_attr_hid);
-       if(dev->handle)
+       if (dev->handle)
                device_remove_file(&dev->dev, &dev_attr_path);
 }
 /* --------------------------------------------------------------------------
@@ -512,7 +508,7 @@ static int acpi_device_register(struct acpi_device *device,
                        break;
                }
        }
-       if(!found) {
+       if (!found) {
                acpi_device_bus_id = new_bus_id;
                strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device");
                acpi_device_bus_id->instance_no = 0;
@@ -530,22 +526,21 @@ static int acpi_device_register(struct acpi_device *device,
        if (device->parent)
                device->dev.parent = &parent->dev;
        device->dev.bus = &acpi_bus_type;
-       device_initialize(&device->dev);
        device->dev.release = &acpi_device_release;
-       result = device_add(&device->dev);
-       if(result) {
-               dev_err(&device->dev, "Error adding device\n");
+       result = device_register(&device->dev);
+       if (result) {
+               dev_err(&device->dev, "Error registering device\n");
                goto end;
        }
 
        result = acpi_device_setup_files(device);
-       if(result)
+       if (result)
                printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n",
                       dev_name(&device->dev));
 
        device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
        return 0;
-  end:
+end:
        mutex_lock(&acpi_device_lock);
        if (device->parent)
                list_del(&device->node);
@@ -577,7 +572,7 @@ static void acpi_device_unregister(struct acpi_device *device, int type)
  * @device: the device to add and initialize
  * @driver: driver for the device
  *
- * Used to initialize a device via its device driver.  Called whenever a 
+ * Used to initialize a device via its device driver.  Called whenever a
  * driver is bound to a device.  Invokes the driver's add() ops.
  */
 static int
@@ -585,7 +580,6 @@ acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver)
 {
        int result = 0;
 
-
        if (!device || !driver)
                return -EINVAL;
 
@@ -802,7 +796,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
        if (!acpi_match_device_ids(device, button_device_ids))
                device->wakeup.flags.run_wake = 1;
 
-      end:
+end:
        if (ACPI_FAILURE(status))
                device->flags.wake_capable = 0;
        return 0;
@@ -1070,7 +1064,7 @@ static void acpi_device_set_id(struct acpi_device *device,
                break;
        }
 
-       /* 
+       /*
         * \_SB
         * ----
         * Fix for the system root bus device -- the only root-level device.
@@ -1320,7 +1314,7 @@ acpi_add_single_object(struct acpi_device **child,
                        device->parent->ops.bind(device);
        }
 
-      end:
+end:
        if (!result)
                *child = device;
        else {
@@ -1464,7 +1458,6 @@ acpi_bus_add(struct acpi_device **child,
 
        return result;
 }
-
 EXPORT_SYMBOL(acpi_bus_add);
 
 int acpi_bus_start(struct acpi_device *device)
@@ -1484,7 +1477,6 @@ int acpi_bus_start(struct acpi_device *device)
        }
        return result;
 }
-
 EXPORT_SYMBOL(acpi_bus_start);
 
 int acpi_bus_trim(struct acpi_device *start, int rmdevice)
@@ -1542,7 +1534,6 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice)
 }
 EXPORT_SYMBOL_GPL(acpi_bus_trim);
 
-
 static int acpi_bus_scan_fixed(struct acpi_device *root)
 {
        int result = 0;
@@ -1610,6 +1601,6 @@ int __init acpi_scan_init(void)
        if (result)
                acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
 
-      Done:
+Done:
        return result;
 }
index 1bdfb37..8851315 100644 (file)
@@ -76,6 +76,7 @@ MODULE_LICENSE("GPL");
 static int brightness_switch_enabled = 1;
 module_param(brightness_switch_enabled, bool, 0644);
 
+static int register_count = 0;
 static int acpi_video_bus_add(struct acpi_device *device);
 static int acpi_video_bus_remove(struct acpi_device *device, int type);
 static int acpi_video_resume(struct acpi_device *device);
@@ -586,6 +587,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
                },
        },
+       {
+        .callback = video_set_bqc_offset,
+        .ident = "Acer Aspire 7720",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
+               },
+       },
        {}
 };
 
@@ -976,6 +985,11 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                device->backlight->props.max_brightness = device->brightness->count-3;
                kfree(name);
 
+               result = sysfs_create_link(&device->backlight->dev.kobj,
+                                          &device->dev->dev.kobj, "device");
+               if (result)
+                       printk(KERN_ERR PREFIX "Create sysfs link\n");
+
                device->cdev = thermal_cooling_device_register("LCD",
                                        device->dev, &video_cooling_ops);
                if (IS_ERR(device->cdev))
@@ -1054,15 +1068,15 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
 static int acpi_video_bus_check(struct acpi_video_bus *video)
 {
        acpi_status status = -ENOENT;
-       struct device *dev;
+       struct pci_dev *dev;
 
        if (!video)
                return -EINVAL;
 
-       dev = acpi_get_physical_pci_device(video->device->handle);
+       dev = acpi_get_pci_dev(video->device->handle);
        if (!dev)
                return -ENODEV;
-       put_device(dev);
+       pci_dev_put(dev);
 
        /* Since there is no HID, CID and so on for VGA driver, we have
         * to check well known required nodes.
@@ -1990,6 +2004,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
        status = acpi_remove_notify_handler(device->dev->handle,
                                            ACPI_DEVICE_NOTIFY,
                                            acpi_video_device_notify);
+       sysfs_remove_link(&device->backlight->dev.kobj, "device");
        backlight_device_unregister(device->backlight);
        if (device->cdev) {
                sysfs_remove_link(&device->dev->dev.kobj,
@@ -2318,6 +2333,13 @@ static int __init intel_opregion_present(void)
 int acpi_video_register(void)
 {
        int result = 0;
+       if (register_count) {
+               /*
+                * if the function of acpi_video_register is already called,
+                * don't register the acpi_vide_bus again and return no error.
+                */
+               return 0;
+       }
 
        acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
        if (!acpi_video_dir)
@@ -2329,10 +2351,35 @@ int acpi_video_register(void)
                return -ENODEV;
        }
 
+       /*
+        * When the acpi_video_bus is loaded successfully, increase
+        * the counter reference.
+        */
+       register_count = 1;
+
        return 0;
 }
 EXPORT_SYMBOL(acpi_video_register);
 
+void acpi_video_unregister(void)
+{
+       if (!register_count) {
+               /*
+                * If the acpi video bus is already unloaded, don't
+                * unload it again and return directly.
+                */
+               return;
+       }
+       acpi_bus_unregister_driver(&acpi_video_bus);
+
+       remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+
+       register_count = 0;
+
+       return;
+}
+EXPORT_SYMBOL(acpi_video_unregister);
+
 /*
  * This is kind of nasty. Hardware using Intel chipsets may require
  * the video opregion code to be run first in order to initialise
@@ -2350,16 +2397,12 @@ static int __init acpi_video_init(void)
        return acpi_video_register();
 }
 
-void acpi_video_exit(void)
+static void __exit acpi_video_exit(void)
 {
-
-       acpi_bus_unregister_driver(&acpi_video_bus);
-
-       remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+       acpi_video_unregister();
 
        return;
 }
-EXPORT_SYMBOL(acpi_video_exit);
 
 module_init(acpi_video_init);
 module_exit(acpi_video_exit);
index 0973727..7cd2b63 100644 (file)
@@ -10,7 +10,7 @@
  * assinged
  *
  * After PCI devices are glued with ACPI devices
- * acpi_get_physical_pci_device() can be called to identify ACPI graphics
+ * acpi_get_pci_dev() can be called to identify ACPI graphics
  * devices for which a real graphics card is plugged in
  *
  * Now acpi_video_get_capabilities() can be called to check which
@@ -36,6 +36,7 @@
 
 #include <linux/acpi.h>
 #include <linux/dmi.h>
+#include <linux/pci.h>
 
 ACPI_MODULE_NAME("video");
 #define _COMPONENT             ACPI_VIDEO_COMPONENT
@@ -109,7 +110,7 @@ static acpi_status
 find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
        long *cap = context;
-       struct device *dev;
+       struct pci_dev *dev;
        struct acpi_device *acpi_dev;
 
        const struct acpi_device_id video_ids[] = {
@@ -120,10 +121,10 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
                return AE_OK;
 
        if (!acpi_match_device_ids(acpi_dev, video_ids)) {
-               dev = acpi_get_physical_pci_device(handle);
+               dev = acpi_get_pci_dev(handle);
                if (!dev)
                        return AE_OK;
-               put_device(dev);
+               pci_dev_put(dev);
                *cap |= acpi_is_video_device(acpi_dev);
        }
        return AE_OK;
index 9533f43..52d953e 100644 (file)
@@ -1048,8 +1048,6 @@ static int mxser_open(struct tty_struct *tty, struct file *filp)
        if (retval)
                return retval;
 
-       /* unmark here for very high baud rate (ex. 921600 bps) used */
-       tty->low_latency = 1;
        return 0;
 }
 
index d6102b6..574f1c7 100644 (file)
@@ -1591,8 +1591,6 @@ static int ntty_open(struct tty_struct *tty, struct file *file)
 
        /* Enable interrupt downlink for channel */
        if (port->port.count == 1) {
-               /* FIXME: is this needed now ? */
-               tty->low_latency = 1;
                tty->driver_data = port;
                tty_port_tty_set(&port->port, tty);
                DBG1("open: %d", port->token_dl);
index 1386625..a2e67e6 100644 (file)
@@ -467,7 +467,6 @@ static unsigned int free_tbuf_count(struct slgt_info *info);
 static unsigned int tbuf_bytes(struct slgt_info *info);
 static void reset_tbufs(struct slgt_info *info);
 static void tdma_reset(struct slgt_info *info);
-static void tdma_start(struct slgt_info *info);
 static void tx_load(struct slgt_info *info, const char *buf, unsigned int count);
 
 static void get_signals(struct slgt_info *info);
@@ -795,6 +794,18 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
        }
 }
 
+static void update_tx_timer(struct slgt_info *info)
+{
+       /*
+        * use worst case speed of 1200bps to calculate transmit timeout
+        * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
+        */
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               int timeout  = (tbuf_bytes(info) * 7) + 1000;
+               mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
+       }
+}
+
 static int write(struct tty_struct *tty,
                 const unsigned char *buf, int count)
 {
@@ -838,8 +849,18 @@ start:
                spin_lock_irqsave(&info->lock,flags);
                if (!info->tx_active)
                        tx_start(info);
-               else
-                       tdma_start(info);
+               else if (!(rd_reg32(info, TDCSR) & BIT0)) {
+                       /* transmit still active but transmit DMA stopped */
+                       unsigned int i = info->tbuf_current;
+                       if (!i)
+                               i = info->tbuf_count;
+                       i--;
+                       /* if DMA buf unsent must try later after tx idle */
+                       if (desc_count(info->tbufs[i]))
+                               ret = 0;
+               }
+               if (ret > 0)
+                       update_tx_timer(info);
                spin_unlock_irqrestore(&info->lock,flags);
        }
 
@@ -1502,10 +1523,9 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
        /* save start time for transmit timeout detection */
        dev->trans_start = jiffies;
 
-       /* start hardware transmitter if necessary */
        spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active)
-               tx_start(info);
+       tx_start(info);
+       update_tx_timer(info);
        spin_unlock_irqrestore(&info->lock,flags);
 
        return 0;
@@ -3946,50 +3966,19 @@ static void tx_start(struct slgt_info *info)
                        slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
                        /* clear tx idle and underrun status bits */
                        wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
-                       if (info->params.mode == MGSL_MODE_HDLC)
-                               mod_timer(&info->tx_timer, jiffies +
-                                               msecs_to_jiffies(5000));
                } else {
                        slgt_irq_off(info, IRQ_TXDATA);
                        slgt_irq_on(info, IRQ_TXIDLE);
                        /* clear tx idle status bit */
                        wr_reg16(info, SSR, IRQ_TXIDLE);
                }
-               tdma_start(info);
+               /* set 1st descriptor address and start DMA */
+               wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
+               wr_reg32(info, TDCSR, BIT2 + BIT0);
                info->tx_active = true;
        }
 }
 
-/*
- * start transmit DMA if inactive and there are unsent buffers
- */
-static void tdma_start(struct slgt_info *info)
-{
-       unsigned int i;
-
-       if (rd_reg32(info, TDCSR) & BIT0)
-               return;
-
-       /* transmit DMA inactive, check for unsent buffers */
-       i = info->tbuf_start;
-       while (!desc_count(info->tbufs[i])) {
-               if (++i == info->tbuf_count)
-                       i = 0;
-               if (i == info->tbuf_current)
-                       return;
-       }
-       info->tbuf_start = i;
-
-       /* there are unsent buffers, start transmit DMA */
-
-       /* reset needed if previous error condition */
-       tdma_reset(info);
-
-       /* set 1st descriptor address */
-       wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
-       wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
-}
-
 static void tx_stop(struct slgt_info *info)
 {
        unsigned short val;
@@ -5004,8 +4993,7 @@ static void tx_timeout(unsigned long context)
                info->icount.txtimeout++;
        }
        spin_lock_irqsave(&info->lock,flags);
-       info->tx_active = false;
-       info->tx_count = 0;
+       tx_stop(info);
        spin_unlock_irqrestore(&info->lock,flags);
 
 #if SYNCLINK_GENERIC_HDLC
index 62dadfc..4e862a7 100644 (file)
@@ -193,7 +193,7 @@ int tty_port_block_til_ready(struct tty_port *port,
 {
        int do_clocal = 0, retval;
        unsigned long flags;
-       DECLARE_WAITQUEUE(wait, current);
+       DEFINE_WAIT(wait);
        int cd;
 
        /* block if port is in the process of being closed */
index 9aa9ea9..88dab52 100644 (file)
@@ -432,23 +432,27 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc,
        list_splice_init(&txd->tx_list, &dc->free_list);
        list_move(&desc->desc_node, &dc->free_list);
 
-       /*
-        * We use dma_unmap_page() regardless of how the buffers were
-        * mapped before they were submitted...
-        */
        if (!ds) {
                dma_addr_t dmaaddr;
                if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
                        dmaaddr = is_dmac64(dc) ?
                                desc->hwdesc.DAR : desc->hwdesc32.DAR;
-                       dma_unmap_page(chan2parent(&dc->chan), dmaaddr,
-                                      desc->len, DMA_FROM_DEVICE);
+                       if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+                               dma_unmap_single(chan2parent(&dc->chan),
+                                       dmaaddr, desc->len, DMA_FROM_DEVICE);
+                       else
+                               dma_unmap_page(chan2parent(&dc->chan),
+                                       dmaaddr, desc->len, DMA_FROM_DEVICE);
                }
                if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
                        dmaaddr = is_dmac64(dc) ?
                                desc->hwdesc.SAR : desc->hwdesc32.SAR;
-                       dma_unmap_page(chan2parent(&dc->chan), dmaaddr,
-                                      desc->len, DMA_TO_DEVICE);
+                       if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+                               dma_unmap_single(chan2parent(&dc->chan),
+                                       dmaaddr, desc->len, DMA_TO_DEVICE);
+                       else
+                               dma_unmap_page(chan2parent(&dc->chan),
+                                       dmaaddr, desc->len, DMA_TO_DEVICE);
                }
        }
 
index dc425e7..e4b4e88 100644 (file)
@@ -419,7 +419,7 @@ void intel_opregion_free(struct drm_device *dev, int suspend)
                return;
 
        if (!suspend)
-               acpi_video_exit();
+               acpi_video_unregister();
 
        opregion->acpi->drdy = 0;
 
index 3c259ee..aa87b6a 100644 (file)
@@ -326,6 +326,15 @@ config I2C_DAVINCI
          devices such as DaVinci NIC.
          For details please see http://www.ti.com/davinci
 
+config I2C_DESIGNWARE
+       tristate "Synopsys DesignWare"
+       help
+         If you say yes to this option, support will be included for the
+         Synopsys DesignWare I2C adapter. Only master mode is supported.
+
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-designware.
+
 config I2C_GPIO
        tristate "GPIO-based bitbanging I2C"
        depends on GENERIC_GPIO
index edeabf0..e654263 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_I2C_AU1550)      += i2c-au1550.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CPM)          += i2c-cpm.o
 obj-$(CONFIG_I2C_DAVINCI)      += i2c-davinci.o
+obj-$(CONFIG_I2C_DESIGNWARE)   += i2c-designware.o
 obj-$(CONFIG_I2C_GPIO)         += i2c-gpio.o
 obj-$(CONFIG_I2C_HIGHLANDER)   += i2c-highlander.o
 obj-$(CONFIG_I2C_IBM_IIC)      += i2c-ibm_iic.o
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
new file mode 100644 (file)
index 0000000..b444762
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ * Synopsys Designware I2C adapter driver (master only).
+ *
+ * Based on the TI DAVINCI I2C adapter driver.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2007 MontaVista Software Inc.
+ * Copyright (C) 2009 Provigent Ltd.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------------
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+/*
+ * Registers offset
+ */
+#define DW_IC_CON              0x0
+#define DW_IC_TAR              0x4
+#define DW_IC_DATA_CMD         0x10
+#define DW_IC_SS_SCL_HCNT      0x14
+#define DW_IC_SS_SCL_LCNT      0x18
+#define DW_IC_FS_SCL_HCNT      0x1c
+#define DW_IC_FS_SCL_LCNT      0x20
+#define DW_IC_INTR_STAT                0x2c
+#define DW_IC_INTR_MASK                0x30
+#define DW_IC_CLR_INTR         0x40
+#define DW_IC_ENABLE           0x6c
+#define DW_IC_STATUS           0x70
+#define DW_IC_TXFLR            0x74
+#define DW_IC_RXFLR            0x78
+#define DW_IC_COMP_PARAM_1     0xf4
+#define DW_IC_TX_ABRT_SOURCE   0x80
+
+#define DW_IC_CON_MASTER               0x1
+#define DW_IC_CON_SPEED_STD            0x2
+#define DW_IC_CON_SPEED_FAST           0x4
+#define DW_IC_CON_10BITADDR_MASTER     0x10
+#define DW_IC_CON_RESTART_EN           0x20
+#define DW_IC_CON_SLAVE_DISABLE                0x40
+
+#define DW_IC_INTR_TX_EMPTY    0x10
+#define DW_IC_INTR_TX_ABRT     0x40
+#define DW_IC_INTR_STOP_DET    0x200
+
+#define DW_IC_STATUS_ACTIVITY  0x1
+
+#define DW_IC_ERR_TX_ABRT      0x1
+
+/*
+ * status codes
+ */
+#define STATUS_IDLE                    0x0
+#define STATUS_WRITE_IN_PROGRESS       0x1
+#define STATUS_READ_IN_PROGRESS                0x2
+
+#define TIMEOUT                        20 /* ms */
+
+/*
+ * hardware abort codes from the DW_IC_TX_ABRT_SOURCE register
+ *
+ * only expected abort codes are listed here
+ * refer to the datasheet for the full list
+ */
+#define ABRT_7B_ADDR_NOACK     0
+#define ABRT_10ADDR1_NOACK     1
+#define ABRT_10ADDR2_NOACK     2
+#define ABRT_TXDATA_NOACK      3
+#define ABRT_GCALL_NOACK       4
+#define ABRT_GCALL_READ                5
+#define ABRT_SBYTE_ACKDET      7
+#define ABRT_SBYTE_NORSTRT     9
+#define ABRT_10B_RD_NORSTRT    10
+#define ARB_MASTER_DIS         11
+#define ARB_LOST               12
+
+static char *abort_sources[] = {
+       [ABRT_7B_ADDR_NOACK]    =
+               "slave address not acknowledged (7bit mode)",
+       [ABRT_10ADDR1_NOACK]    =
+               "first address byte not acknowledged (10bit mode)",
+       [ABRT_10ADDR2_NOACK]    =
+               "second address byte not acknowledged (10bit mode)",
+       [ABRT_TXDATA_NOACK]             =
+               "data not acknowledged",
+       [ABRT_GCALL_NOACK]              =
+               "no acknowledgement for a general call",
+       [ABRT_GCALL_READ]               =
+               "read after general call",
+       [ABRT_SBYTE_ACKDET]             =
+               "start byte acknowledged",
+       [ABRT_SBYTE_NORSTRT]    =
+               "trying to send start byte when restart is disabled",
+       [ABRT_10B_RD_NORSTRT]   =
+               "trying to read when restart is disabled (10bit mode)",
+       [ARB_MASTER_DIS]                =
+               "trying to use disabled adapter",
+       [ARB_LOST]                      =
+               "lost arbitration",
+};
+
+/**
+ * struct dw_i2c_dev - private i2c-designware data
+ * @dev: driver model device node
+ * @base: IO registers pointer
+ * @cmd_complete: tx completion indicator
+ * @pump_msg: continue in progress transfers
+ * @lock: protect this struct and IO registers
+ * @clk: input reference clock
+ * @cmd_err: run time hadware error code
+ * @msgs: points to an array of messages currently being transfered
+ * @msgs_num: the number of elements in msgs
+ * @msg_write_idx: the element index of the current tx message in the msgs
+ *     array
+ * @tx_buf_len: the length of the current tx buffer
+ * @tx_buf: the current tx buffer
+ * @msg_read_idx: the element index of the current rx message in the msgs
+ *     array
+ * @rx_buf_len: the length of the current rx buffer
+ * @rx_buf: the current rx buffer
+ * @msg_err: error status of the current transfer
+ * @status: i2c master status, one of STATUS_*
+ * @abort_source: copy of the TX_ABRT_SOURCE register
+ * @irq: interrupt number for the i2c master
+ * @adapter: i2c subsystem adapter node
+ * @tx_fifo_depth: depth of the hardware tx fifo
+ * @rx_fifo_depth: depth of the hardware rx fifo
+ */
+struct dw_i2c_dev {
+       struct device           *dev;
+       void __iomem            *base;
+       struct completion       cmd_complete;
+       struct tasklet_struct   pump_msg;
+       struct mutex            lock;
+       struct clk              *clk;
+       int                     cmd_err;
+       struct i2c_msg          *msgs;
+       int                     msgs_num;
+       int                     msg_write_idx;
+       u16                     tx_buf_len;
+       u8                      *tx_buf;
+       int                     msg_read_idx;
+       u16                     rx_buf_len;
+       u8                      *rx_buf;
+       int                     msg_err;
+       unsigned int            status;
+       u16                     abort_source;
+       int                     irq;
+       struct i2c_adapter      adapter;
+       unsigned int            tx_fifo_depth;
+       unsigned int            rx_fifo_depth;
+};
+
+/**
+ * i2c_dw_init() - initialize the designware i2c master hardware
+ * @dev: device private data
+ *
+ * This functions configures and enables the I2C master.
+ * This function is called during I2C init function, and in case of timeout at
+ * run time.
+ */
+static void i2c_dw_init(struct dw_i2c_dev *dev)
+{
+       u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
+       u16 ic_con;
+
+       /* Disable the adapter */
+       writeb(0, dev->base + DW_IC_ENABLE);
+
+       /* set standard and fast speed deviders for high/low periods */
+       writew((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */
+                       dev->base + DW_IC_SS_SCL_HCNT);
+       writew((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */
+                       dev->base + DW_IC_SS_SCL_LCNT);
+       writew((input_clock_khz *  6 / 10000)+1, /* fast speed high, 0.6us */
+                       dev->base + DW_IC_FS_SCL_HCNT);
+       writew((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */
+                       dev->base + DW_IC_FS_SCL_LCNT);
+
+       /* configure the i2c master */
+       ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+               DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
+       writew(ic_con, dev->base + DW_IC_CON);
+}
+
+/*
+ * Waiting for bus not busy
+ */
+static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
+{
+       int timeout = TIMEOUT;
+
+       while (readb(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+               if (timeout <= 0) {
+                       dev_warn(dev->dev, "timeout waiting for bus ready\n");
+                       return -ETIMEDOUT;
+               }
+               timeout--;
+               mdelay(1);
+       }
+
+       return 0;
+}
+
+/*
+ * Initiate low level master read/write transaction.
+ * This function is called from i2c_dw_xfer when starting a transfer.
+ * This function is also called from dw_i2c_pump_msg to continue a transfer
+ * that is longer than the size of the TX FIFO.
+ */
+static void
+i2c_dw_xfer_msg(struct i2c_adapter *adap)
+{
+       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+       struct i2c_msg *msgs = dev->msgs;
+       int num = dev->msgs_num;
+       u16 ic_con, intr_mask;
+       int tx_limit = dev->tx_fifo_depth - readb(dev->base + DW_IC_TXFLR);
+       int rx_limit = dev->rx_fifo_depth - readb(dev->base + DW_IC_RXFLR);
+       u16 addr = msgs[dev->msg_write_idx].addr;
+       u16 buf_len = dev->tx_buf_len;
+
+       if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
+               /* Disable the adapter */
+               writeb(0, dev->base + DW_IC_ENABLE);
+
+               /* set the slave (target) address */
+               writew(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+
+               /* if the slave address is ten bit address, enable 10BITADDR */
+               ic_con = readw(dev->base + DW_IC_CON);
+               if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
+                       ic_con |= DW_IC_CON_10BITADDR_MASTER;
+               else
+                       ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
+               writew(ic_con, dev->base + DW_IC_CON);
+
+               /* Enable the adapter */
+               writeb(1, dev->base + DW_IC_ENABLE);
+       }
+
+       for (; dev->msg_write_idx < num; dev->msg_write_idx++) {
+               /* if target address has changed, we need to
+                * reprogram the target address in the i2c
+                * adapter when we are done with this transfer
+                */
+               if (msgs[dev->msg_write_idx].addr != addr)
+                       return;
+
+               if (msgs[dev->msg_write_idx].len == 0) {
+                       dev_err(dev->dev,
+                               "%s: invalid message length\n", __func__);
+                       dev->msg_err = -EINVAL;
+                       return;
+               }
+
+               if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
+                       /* new i2c_msg */
+                       dev->tx_buf = msgs[dev->msg_write_idx].buf;
+                       buf_len = msgs[dev->msg_write_idx].len;
+               }
+
+               while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
+                       if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+                               writew(0x100, dev->base + DW_IC_DATA_CMD);
+                               rx_limit--;
+                       } else
+                               writew(*(dev->tx_buf++),
+                                               dev->base + DW_IC_DATA_CMD);
+                       tx_limit--; buf_len--;
+               }
+       }
+
+       intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
+       if (buf_len > 0) { /* more bytes to be written */
+               intr_mask |= DW_IC_INTR_TX_EMPTY;
+               dev->status |= STATUS_WRITE_IN_PROGRESS;
+       } else
+               dev->status &= ~STATUS_WRITE_IN_PROGRESS;
+       writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+
+       dev->tx_buf_len = buf_len;
+}
+
+static void
+i2c_dw_read(struct i2c_adapter *adap)
+{
+       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+       struct i2c_msg *msgs = dev->msgs;
+       int num = dev->msgs_num;
+       u16 addr = msgs[dev->msg_read_idx].addr;
+       int rx_valid = readw(dev->base + DW_IC_RXFLR);
+
+       for (; dev->msg_read_idx < num; dev->msg_read_idx++) {
+               u16 len;
+               u8 *buf;
+
+               if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
+                       continue;
+
+               /* different i2c client, reprogram the i2c adapter */
+               if (msgs[dev->msg_read_idx].addr != addr)
+                       return;
+
+               if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
+                       len = msgs[dev->msg_read_idx].len;
+                       buf = msgs[dev->msg_read_idx].buf;
+               } else {
+                       len = dev->rx_buf_len;
+                       buf = dev->rx_buf;
+               }
+
+               for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+                       *buf++ = readb(dev->base + DW_IC_DATA_CMD);
+
+               if (len > 0) {
+                       dev->status |= STATUS_READ_IN_PROGRESS;
+                       dev->rx_buf_len = len;
+                       dev->rx_buf = buf;
+                       return;
+               } else
+                       dev->status &= ~STATUS_READ_IN_PROGRESS;
+       }
+}
+
+/*
+ * Prepare controller for a transaction and call i2c_dw_xfer_msg
+ */
+static int
+i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+       int ret;
+
+       dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
+
+       mutex_lock(&dev->lock);
+
+       INIT_COMPLETION(dev->cmd_complete);
+       dev->msgs = msgs;
+       dev->msgs_num = num;
+       dev->cmd_err = 0;
+       dev->msg_write_idx = 0;
+       dev->msg_read_idx = 0;
+       dev->msg_err = 0;
+       dev->status = STATUS_IDLE;
+
+       ret = i2c_dw_wait_bus_not_busy(dev);
+       if (ret < 0)
+               goto done;
+
+       /* start the transfers */
+       i2c_dw_xfer_msg(adap);
+
+       /* wait for tx to complete */
+       ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
+       if (ret == 0) {
+               dev_err(dev->dev, "controller timed out\n");
+               i2c_dw_init(dev);
+               ret = -ETIMEDOUT;
+               goto done;
+       } else if (ret < 0)
+               goto done;
+
+       if (dev->msg_err) {
+               ret = dev->msg_err;
+               goto done;
+       }
+
+       /* no error */
+       if (likely(!dev->cmd_err)) {
+               /* read rx fifo, and disable the adapter */
+               do {
+                       i2c_dw_read(adap);
+               } while (dev->status & STATUS_READ_IN_PROGRESS);
+               writeb(0, dev->base + DW_IC_ENABLE);
+               ret = num;
+               goto done;
+       }
+
+       /* We have an error */
+       if (dev->cmd_err == DW_IC_ERR_TX_ABRT) {
+               unsigned long abort_source = dev->abort_source;
+               int i;
+
+               for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) {
+                   dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
+               }
+       }
+       ret = -EIO;
+
+done:
+       mutex_unlock(&dev->lock);
+
+       return ret;
+}
+
+static u32 i2c_dw_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
+}
+
+static void dw_i2c_pump_msg(unsigned long data)
+{
+       struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data;
+       u16 intr_mask;
+
+       i2c_dw_read(&dev->adapter);
+       i2c_dw_xfer_msg(&dev->adapter);
+
+       intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
+       if (dev->status & STATUS_WRITE_IN_PROGRESS)
+               intr_mask |= DW_IC_INTR_TX_EMPTY;
+       writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+}
+
+/*
+ * Interrupt service routine. This gets called whenever an I2C interrupt
+ * occurs.
+ */
+static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
+{
+       struct dw_i2c_dev *dev = dev_id;
+       u16 stat;
+
+       stat = readw(dev->base + DW_IC_INTR_STAT);
+       dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
+       if (stat & DW_IC_INTR_TX_ABRT) {
+               dev->abort_source = readw(dev->base + DW_IC_TX_ABRT_SOURCE);
+               dev->cmd_err |= DW_IC_ERR_TX_ABRT;
+               dev->status = STATUS_IDLE;
+       } else if (stat & DW_IC_INTR_TX_EMPTY)
+               tasklet_schedule(&dev->pump_msg);
+
+       readb(dev->base + DW_IC_CLR_INTR);      /* clear interrupts */
+       writew(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
+       if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
+               complete(&dev->cmd_complete);
+
+       return IRQ_HANDLED;
+}
+
+static struct i2c_algorithm i2c_dw_algo = {
+       .master_xfer    = i2c_dw_xfer,
+       .functionality  = i2c_dw_func,
+};
+
+static int __devinit dw_i2c_probe(struct platform_device *pdev)
+{
+       struct dw_i2c_dev *dev;
+       struct i2c_adapter *adap;
+       struct resource *mem, *irq, *ioarea;
+       int r;
+
+       /* NOTE: driver uses the static register mapping */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "no mem resource?\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!irq) {
+               dev_err(&pdev->dev, "no irq resource?\n");
+               return -EINVAL;
+       }
+
+       ioarea = request_mem_region(mem->start, resource_size(mem),
+                       pdev->name);
+       if (!ioarea) {
+               dev_err(&pdev->dev, "I2C region already claimed\n");
+               return -EBUSY;
+       }
+
+       dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
+       if (!dev) {
+               r = -ENOMEM;
+               goto err_release_region;
+       }
+
+       init_completion(&dev->cmd_complete);
+       tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev);
+       mutex_init(&dev->lock);
+       dev->dev = get_device(&pdev->dev);
+       dev->irq = irq->start;
+       platform_set_drvdata(pdev, dev);
+
+       dev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               r = -ENODEV;
+               goto err_free_mem;
+       }
+       clk_enable(dev->clk);
+
+       dev->base = ioremap(mem->start, resource_size(mem));
+       if (dev->base == NULL) {
+               dev_err(&pdev->dev, "failure mapping io resources\n");
+               r = -EBUSY;
+               goto err_unuse_clocks;
+       }
+       {
+               u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1);
+
+               dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
+               dev->rx_fifo_depth = ((param1 >> 8)  & 0xff) + 1;
+       }
+       i2c_dw_init(dev);
+
+       writew(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
+       r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, dev);
+       if (r) {
+               dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
+               goto err_iounmap;
+       }
+
+       adap = &dev->adapter;
+       i2c_set_adapdata(adap, dev);
+       adap->owner = THIS_MODULE;
+       adap->class = I2C_CLASS_HWMON;
+       strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
+                       sizeof(adap->name));
+       adap->algo = &i2c_dw_algo;
+       adap->dev.parent = &pdev->dev;
+
+       adap->nr = pdev->id;
+       r = i2c_add_numbered_adapter(adap);
+       if (r) {
+               dev_err(&pdev->dev, "failure adding adapter\n");
+               goto err_free_irq;
+       }
+
+       return 0;
+
+err_free_irq:
+       free_irq(dev->irq, dev);
+err_iounmap:
+       iounmap(dev->base);
+err_unuse_clocks:
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
+       dev->clk = NULL;
+err_free_mem:
+       platform_set_drvdata(pdev, NULL);
+       put_device(&pdev->dev);
+       kfree(dev);
+err_release_region:
+       release_mem_region(mem->start, resource_size(mem));
+
+       return r;
+}
+
+static int __devexit dw_i2c_remove(struct platform_device *pdev)
+{
+       struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+       struct resource *mem;
+
+       platform_set_drvdata(pdev, NULL);
+       i2c_del_adapter(&dev->adapter);
+       put_device(&pdev->dev);
+
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
+       dev->clk = NULL;
+
+       writeb(0, dev->base + DW_IC_ENABLE);
+       free_irq(dev->irq, dev);
+       kfree(dev);
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(mem->start, resource_size(mem));
+       return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:i2c_designware");
+
+static struct platform_driver dw_i2c_driver = {
+       .remove         = __devexit_p(dw_i2c_remove),
+       .driver         = {
+               .name   = "i2c_designware",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init dw_i2c_init_driver(void)
+{
+       return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
+}
+module_init(dw_i2c_init_driver);
+
+static void __exit dw_i2c_exit_driver(void)
+{
+       platform_driver_unregister(&dw_i2c_driver);
+}
+module_exit(dw_i2c_exit_driver);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
+MODULE_LICENSE("GPL");
index 36e0675..020f957 100644 (file)
@@ -231,6 +231,17 @@ config DM_MIRROR
          Allow volume managers to mirror logical volumes, also
          needed for live data migration tools such as 'pvmove'.
 
+config DM_LOG_USERSPACE
+       tristate "Mirror userspace logging (EXPERIMENTAL)"
+       depends on DM_MIRROR && EXPERIMENTAL && NET
+       select CONNECTOR
+       ---help---
+         The userspace logging module provides a mechanism for
+         relaying the dm-dirty-log API to userspace.  Log designs
+         which are more suited to userspace implementation (e.g.
+         shared storage logs) or experimental logs can be implemented
+         by leveraging this framework.
+
 config DM_ZERO
        tristate "Zero target"
        depends on BLK_DEV_DM
@@ -249,6 +260,25 @@ config DM_MULTIPATH
        ---help---
          Allow volume managers to support multipath hardware.
 
+config DM_MULTIPATH_QL
+       tristate "I/O Path Selector based on the number of in-flight I/Os"
+       depends on DM_MULTIPATH
+       ---help---
+         This path selector is a dynamic load balancer which selects
+         the path with the least number of in-flight I/Os.
+
+         If unsure, say N.
+
+config DM_MULTIPATH_ST
+       tristate "I/O Path Selector based on the service time"
+       depends on DM_MULTIPATH
+       ---help---
+         This path selector is a dynamic load balancer which selects
+         the path expected to complete the incoming I/O in the shortest
+         time.
+
+         If unsure, say N.
+
 config DM_DELAY
        tristate "I/O delaying target (EXPERIMENTAL)"
        depends on BLK_DEV_DM && EXPERIMENTAL
index 45cc595..1dc4185 100644 (file)
@@ -8,6 +8,8 @@ dm-multipath-y  += dm-path-selector.o dm-mpath.o
 dm-snapshot-y  += dm-snap.o dm-exception-store.o dm-snap-transient.o \
                    dm-snap-persistent.o
 dm-mirror-y    += dm-raid1.o
+dm-log-userspace-y \
+               += dm-log-userspace-base.o dm-log-userspace-transfer.o
 md-mod-y       += md.o bitmap.o
 raid456-y      += raid5.o
 raid6_pq-y     += raid6algos.o raid6recov.o raid6tables.o \
@@ -36,8 +38,11 @@ obj-$(CONFIG_BLK_DEV_DM)     += dm-mod.o
 obj-$(CONFIG_DM_CRYPT)         += dm-crypt.o
 obj-$(CONFIG_DM_DELAY)         += dm-delay.o
 obj-$(CONFIG_DM_MULTIPATH)     += dm-multipath.o dm-round-robin.o
+obj-$(CONFIG_DM_MULTIPATH_QL)  += dm-queue-length.o
+obj-$(CONFIG_DM_MULTIPATH_ST)  += dm-service-time.o
 obj-$(CONFIG_DM_SNAPSHOT)      += dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)                += dm-mirror.o dm-log.o dm-region-hash.o
+obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
 obj-$(CONFIG_DM_ZERO)          += dm-zero.o
 
 quiet_cmd_unroll = UNROLL  $@
index 53394e8..9933eb8 100644 (file)
@@ -1132,6 +1132,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                goto bad_crypt_queue;
        }
 
+       ti->num_flush_requests = 1;
        ti->private = cc;
        return 0;
 
@@ -1189,6 +1190,13 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
                     union map_info *map_context)
 {
        struct dm_crypt_io *io;
+       struct crypt_config *cc;
+
+       if (unlikely(bio_empty_barrier(bio))) {
+               cc = ti->private;
+               bio->bi_bdev = cc->dev->bdev;
+               return DM_MAPIO_REMAPPED;
+       }
 
        io = crypt_io_alloc(ti, bio, bio->bi_sector - ti->begin);
 
@@ -1305,9 +1313,17 @@ static int crypt_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
        return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
 }
 
+static int crypt_iterate_devices(struct dm_target *ti,
+                                iterate_devices_callout_fn fn, void *data)
+{
+       struct crypt_config *cc = ti->private;
+
+       return fn(ti, cc->dev, cc->start, data);
+}
+
 static struct target_type crypt_target = {
        .name   = "crypt",
-       .version= {1, 6, 0},
+       .version = {1, 7, 0},
        .module = THIS_MODULE,
        .ctr    = crypt_ctr,
        .dtr    = crypt_dtr,
@@ -1318,6 +1334,7 @@ static struct target_type crypt_target = {
        .resume = crypt_resume,
        .message = crypt_message,
        .merge  = crypt_merge,
+       .iterate_devices = crypt_iterate_devices,
 };
 
 static int __init dm_crypt_init(void)
index 559dbb5..4e5b843 100644 (file)
@@ -197,6 +197,7 @@ out:
        mutex_init(&dc->timer_lock);
        atomic_set(&dc->may_delay, 1);
 
+       ti->num_flush_requests = 1;
        ti->private = dc;
        return 0;
 
@@ -278,8 +279,9 @@ static int delay_map(struct dm_target *ti, struct bio *bio,
 
        if ((bio_data_dir(bio) == WRITE) && (dc->dev_write)) {
                bio->bi_bdev = dc->dev_write->bdev;
-               bio->bi_sector = dc->start_write +
-                                (bio->bi_sector - ti->begin);
+               if (bio_sectors(bio))
+                       bio->bi_sector = dc->start_write +
+                                        (bio->bi_sector - ti->begin);
 
                return delay_bio(dc, dc->write_delay, bio);
        }
@@ -316,9 +318,26 @@ static int delay_status(struct dm_target *ti, status_type_t type,
        return 0;
 }
 
+static int delay_iterate_devices(struct dm_target *ti,
+                                iterate_devices_callout_fn fn, void *data)
+{
+       struct delay_c *dc = ti->private;
+       int ret = 0;
+
+       ret = fn(ti, dc->dev_read, dc->start_read, data);
+       if (ret)
+               goto out;
+
+       if (dc->dev_write)
+               ret = fn(ti, dc->dev_write, dc->start_write, data);
+
+out:
+       return ret;
+}
+
 static struct target_type delay_target = {
        .name        = "delay",
-       .version     = {1, 0, 2},
+       .version     = {1, 1, 0},
        .module      = THIS_MODULE,
        .ctr         = delay_ctr,
        .dtr         = delay_dtr,
@@ -326,6 +345,7 @@ static struct target_type delay_target = {
        .presuspend  = delay_presuspend,
        .resume      = delay_resume,
        .status      = delay_status,
+       .iterate_devices = delay_iterate_devices,
 };
 
 static int __init dm_delay_init(void)
index 75d8081..c3ae515 100644 (file)
@@ -216,7 +216,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
                return -EINVAL;
        }
 
-       type = get_type(argv[1]);
+       type = get_type(&persistent);
        if (!type) {
                ti->error = "Exception store type not recognised";
                r = -EINVAL;
index c92701d..2442c8c 100644 (file)
@@ -156,7 +156,7 @@ static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
  */
 static inline sector_t get_dev_size(struct block_device *bdev)
 {
-       return bdev->bd_inode->i_size >> SECTOR_SHIFT;
+       return i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
 }
 
 static inline chunk_t sector_to_chunk(struct dm_exception_store *store,
index e73aabd..3a2e6a2 100644 (file)
@@ -22,6 +22,7 @@ struct dm_io_client {
 /* FIXME: can we shrink this ? */
 struct io {
        unsigned long error_bits;
+       unsigned long eopnotsupp_bits;
        atomic_t count;
        struct task_struct *sleeper;
        struct dm_io_client *client;
@@ -107,8 +108,11 @@ static inline unsigned bio_get_region(struct bio *bio)
  *---------------------------------------------------------------*/
 static void dec_count(struct io *io, unsigned int region, int error)
 {
-       if (error)
+       if (error) {
                set_bit(region, &io->error_bits);
+               if (error == -EOPNOTSUPP)
+                       set_bit(region, &io->eopnotsupp_bits);
+       }
 
        if (atomic_dec_and_test(&io->count)) {
                if (io->sleeper)
@@ -360,7 +364,9 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
                return -EIO;
        }
 
+retry:
        io.error_bits = 0;
+       io.eopnotsupp_bits = 0;
        atomic_set(&io.count, 1); /* see dispatch_io() */
        io.sleeper = current;
        io.client = client;
@@ -377,6 +383,11 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
        }
        set_current_state(TASK_RUNNING);
 
+       if (io.eopnotsupp_bits && (rw & (1 << BIO_RW_BARRIER))) {
+               rw &= ~(1 << BIO_RW_BARRIER);
+               goto retry;
+       }
+
        if (error_bits)
                *error_bits = io.error_bits;
 
@@ -397,6 +408,7 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions,
 
        io = mempool_alloc(client->pool, GFP_NOIO);
        io->error_bits = 0;
+       io->eopnotsupp_bits = 0;
        atomic_set(&io->count, 1); /* see dispatch_io() */
        io->sleeper = NULL;
        io->client = client;
index 1128d3f..7f77f18 100644 (file)
@@ -276,7 +276,7 @@ retry:
        up_write(&_hash_lock);
 }
 
-static int dm_hash_rename(const char *old, const char *new)
+static int dm_hash_rename(uint32_t cookie, const char *old, const char *new)
 {
        char *new_name, *old_name;
        struct hash_cell *hc;
@@ -333,7 +333,7 @@ static int dm_hash_rename(const char *old, const char *new)
                dm_table_put(table);
        }
 
-       dm_kobject_uevent(hc->md);
+       dm_kobject_uevent(hc->md, KOBJ_CHANGE, cookie);
 
        dm_put(hc->md);
        up_write(&_hash_lock);
@@ -680,6 +680,9 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
 
        __hash_remove(hc);
        up_write(&_hash_lock);
+
+       dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr);
+
        dm_put(md);
        param->data_size = 0;
        return 0;
@@ -715,7 +718,7 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size)
                return r;
 
        param->data_size = 0;
-       return dm_hash_rename(param->name, new_name);
+       return dm_hash_rename(param->event_nr, param->name, new_name);
 }
 
 static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
@@ -842,8 +845,11 @@ static int do_resume(struct dm_ioctl *param)
        if (dm_suspended(md))
                r = dm_resume(md);
 
-       if (!r)
+
+       if (!r) {
+               dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr);
                r = __dev_status(md, param);
+       }
 
        dm_put(md);
        return r;
@@ -1044,6 +1050,12 @@ static int populate_table(struct dm_table *table,
                next = spec->next;
        }
 
+       r = dm_table_set_type(table);
+       if (r) {
+               DMWARN("unable to set table type");
+               return r;
+       }
+
        return dm_table_complete(table);
 }
 
@@ -1089,6 +1101,13 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
                goto out;
        }
 
+       r = dm_table_alloc_md_mempools(t);
+       if (r) {
+               DMWARN("unable to allocate mempools for this table");
+               dm_table_destroy(t);
+               goto out;
+       }
+
        down_write(&_hash_lock);
        hc = dm_get_mdptr(md);
        if (!hc || hc->md != md) {
index 79fb53e..9184b6d 100644 (file)
@@ -53,6 +53,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                goto bad;
        }
 
+       ti->num_flush_requests = 1;
        ti->private = lc;
        return 0;
 
@@ -81,7 +82,8 @@ static void linear_map_bio(struct dm_target *ti, struct bio *bio)
        struct linear_c *lc = ti->private;
 
        bio->bi_bdev = lc->dev->bdev;
-       bio->bi_sector = linear_map_sector(ti, bio->bi_sector);
+       if (bio_sectors(bio))
+               bio->bi_sector = linear_map_sector(ti, bio->bi_sector);
 }
 
 static int linear_map(struct dm_target *ti, struct bio *bio,
@@ -132,9 +134,17 @@ static int linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
        return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
 }
 
+static int linear_iterate_devices(struct dm_target *ti,
+                                 iterate_devices_callout_fn fn, void *data)
+{
+       struct linear_c *lc = ti->private;
+
+       return fn(ti, lc->dev, lc->start, data);
+}
+
 static struct target_type linear_target = {
        .name   = "linear",
-       .version= {1, 0, 3},
+       .version = {1, 1, 0},
        .module = THIS_MODULE,
        .ctr    = linear_ctr,
        .dtr    = linear_dtr,
@@ -142,6 +152,7 @@ static struct target_type linear_target = {
        .status = linear_status,
        .ioctl  = linear_ioctl,
        .merge  = linear_merge,
+       .iterate_devices = linear_iterate_devices,
 };
 
 int __init dm_linear_init(void)
diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c
new file mode 100644 (file)
index 0000000..e69b965
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This file is released under the LGPL.
+ */
+
+#include <linux/bio.h>
+#include <linux/dm-dirty-log.h>
+#include <linux/device-mapper.h>
+#include <linux/dm-log-userspace.h>
+
+#include "dm-log-userspace-transfer.h"
+
+struct flush_entry {
+       int type;
+       region_t region;
+       struct list_head list;
+};
+
+struct log_c {
+       struct dm_target *ti;
+       uint32_t region_size;
+       region_t region_count;
+       char uuid[DM_UUID_LEN];
+
+       char *usr_argv_str;
+       uint32_t usr_argc;
+
+       /*
+        * in_sync_hint gets set when doing is_remote_recovering.  It
+        * represents the first region that needs recovery.  IOW, the
+        * first zero bit of sync_bits.  This can be useful for to limit
+        * traffic for calls like is_remote_recovering and get_resync_work,
+        * but be take care in its use for anything else.
+        */
+       uint64_t in_sync_hint;
+
+       spinlock_t flush_lock;
+       struct list_head flush_list;  /* only for clear and mark requests */
+};
+
+static mempool_t *flush_entry_pool;
+
+static void *flush_entry_alloc(gfp_t gfp_mask, void *pool_data)
+{
+       return kmalloc(sizeof(struct flush_entry), gfp_mask);
+}
+
+static void flush_entry_free(void *element, void *pool_data)
+{
+       kfree(element);
+}
+
+static int userspace_do_request(struct log_c *lc, const char *uuid,
+                               int request_type, char *data, size_t data_size,
+                               char *rdata, size_t *rdata_size)
+{
+       int r;
+
+       /*
+        * If the server isn't there, -ESRCH is returned,
+        * and we must keep trying until the server is
+        * restored.
+        */
+retry:
+       r = dm_consult_userspace(uuid, request_type, data,
+                                data_size, rdata, rdata_size);
+
+       if (r != -ESRCH)
+               return r;
+
+       DMERR(" Userspace log server not found.");
+       while (1) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(2*HZ);
+               DMWARN("Attempting to contact userspace log server...");
+               r = dm_consult_userspace(uuid, DM_ULOG_CTR, lc->usr_argv_str,
+                                        strlen(lc->usr_argv_str) + 1,
+                                        NULL, NULL);
+               if (!r)
+                       break;
+       }
+       DMINFO("Reconnected to userspace log server... DM_ULOG_CTR complete");
+       r = dm_consult_userspace(uuid, DM_ULOG_RESUME, NULL,
+                                0, NULL, NULL);
+       if (!r)
+               goto retry;
+
+       DMERR("Error trying to resume userspace log: %d", r);
+
+       return -ESRCH;
+}
+
+static int build_constructor_string(struct dm_target *ti,
+                                   unsigned argc, char **argv,
+                                   char **ctr_str)
+{
+       int i, str_size;
+       char *str = NULL;
+
+       *ctr_str = NULL;
+
+       for (i = 0, str_size = 0; i < argc; i++)
+               str_size += strlen(argv[i]) + 1; /* +1 for space between args */
+
+       str_size += 20; /* Max number of chars in a printed u64 number */
+
+       str = kzalloc(str_size, GFP_KERNEL);
+       if (!str) {
+               DMWARN("Unable to allocate memory for constructor string");
+               return -ENOMEM;
+       }
+
+       for (i = 0, str_size = 0; i < argc; i++)
+               str_size += sprintf(str + str_size, "%s ", argv[i]);
+       str_size += sprintf(str + str_size, "%llu",
+                           (unsigned long long)ti->len);
+
+       *ctr_str = str;
+       return str_size;
+}
+
+/*
+ * userspace_ctr
+ *
+ * argv contains:
+ *     <UUID> <other args>
+ * Where 'other args' is the userspace implementation specific log
+ * arguments.  An example might be:
+ *     <UUID> clustered_disk <arg count> <log dev> <region_size> [[no]sync]
+ *
+ * So, this module will strip off the <UUID> for identification purposes
+ * when communicating with userspace about a log; but will pass on everything
+ * else.
+ */
+static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
+                        unsigned argc, char **argv)
+{
+       int r = 0;
+       int str_size;
+       char *ctr_str = NULL;
+       struct log_c *lc = NULL;
+       uint64_t rdata;
+       size_t rdata_size = sizeof(rdata);
+
+       if (argc < 3) {
+               DMWARN("Too few arguments to userspace dirty log");
+               return -EINVAL;
+       }
+
+       lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+       if (!lc) {
+               DMWARN("Unable to allocate userspace log context.");
+               return -ENOMEM;
+       }
+
+       lc->ti = ti;
+
+       if (strlen(argv[0]) > (DM_UUID_LEN - 1)) {
+               DMWARN("UUID argument too long.");
+               kfree(lc);
+               return -EINVAL;
+       }
+
+       strncpy(lc->uuid, argv[0], DM_UUID_LEN);
+       spin_lock_init(&lc->flush_lock);
+       INIT_LIST_HEAD(&lc->flush_list);
+
+       str_size = build_constructor_string(ti, argc - 1, argv + 1, &ctr_str);
+       if (str_size < 0) {
+               kfree(lc);
+               return str_size;
+       }
+
+       /* Send table string */
+       r = dm_consult_userspace(lc->uuid, DM_ULOG_CTR,
+                                ctr_str, str_size, NULL, NULL);
+
+       if (r == -ESRCH) {
+               DMERR("Userspace log server not found");
+               goto out;
+       }
+
+       /* Since the region size does not change, get it now */
+       rdata_size = sizeof(rdata);
+       r = dm_consult_userspace(lc->uuid, DM_ULOG_GET_REGION_SIZE,
+                                NULL, 0, (char *)&rdata, &rdata_size);
+
+       if (r) {
+               DMERR("Failed to get region size of dirty log");
+               goto out;
+       }
+
+       lc->region_size = (uint32_t)rdata;
+       lc->region_count = dm_sector_div_up(ti->len, lc->region_size);
+
+out:
+       if (r) {
+               kfree(lc);
+               kfree(ctr_str);
+       } else {
+               lc->usr_argv_str = ctr_str;
+               lc->usr_argc = argc;
+               log->context = lc;
+       }
+
+       return r;
+}
+
+static void userspace_dtr(struct dm_dirty_log *log)
+{
+       int r;
+       struct log_c *lc = log->context;
+
+       r = dm_consult_userspace(lc->uuid, DM_ULOG_DTR,
+                                NULL, 0,
+                                NULL, NULL);
+
+       kfree(lc->usr_argv_str);
+       kfree(lc);
+
+       return;
+}
+
+static int userspace_presuspend(struct dm_dirty_log *log)
+{
+       int r;
+       struct log_c *lc = log->context;
+
+       r = dm_consult_userspace(lc->uuid, DM_ULOG_PRESUSPEND,
+                                NULL, 0,
+                                NULL, NULL);
+
+       return r;
+}
+
+static int userspace_postsuspend(struct dm_dirty_log *log)
+{
+       int r;
+       struct log_c *lc = log->context;
+
+       r = dm_consult_userspace(lc->uuid, DM_ULOG_POSTSUSPEND,
+                                NULL, 0,
+                                NULL, NULL);
+
+       return r;
+}
+
+static int userspace_resume(struct dm_dirty_log *log)
+{
+       int r;
+       struct log_c *lc = log->context;
+
+       lc->in_sync_hint = 0;
+       r = dm_consult_userspace(lc->uuid, DM_ULOG_RESUME,
+                                NULL, 0,
+                                NULL, NULL);
+
+       return r;
+}
+
+static uint32_t userspace_get_region_size(struct dm_dirty_log *log)
+{
+       struct log_c *lc = log->context;
+
+       return lc->region_size;
+}
+
+/*
+ * userspace_is_clean
+ *
+ * Check whether a region is clean.  If there is any sort of
+ * failure when consulting the server, we return not clean.
+ *
+ * Returns: 1 if clean, 0 otherwise
+ */
+static int userspace_is_clean(struct dm_dirty_log *log, region_t region)
+{
+       int r;
+       uint64_t region64 = (uint64_t)region;
+       int64_t is_clean;
+       size_t rdata_size;
+       struct log_c *lc = log->context;
+
+       rdata_size = sizeof(is_clean);
+       r = userspace_do_request(lc, lc->uuid, DM_ULOG_IS_CLEAN,
+                                (char *)&region64, sizeof(region64),
+                                (char *)&is_clean, &rdata_size);
+
+       return (r) ? 0 : (int)is_clean;
+}
+
+/*
+ * userspace_in_sync
+ *
+ * Check if the region is in-sync.  If there is any sort
+ * of failure when consulting the server, we assume that
+ * the region is not in sync.
+ *
+ * If 'can_block' is set, return immediately
+ *
+ * Returns: 1 if in-sync, 0 if not-in-sync, -EWOULDBLOCK
+ */
+static int userspace_in_sync(struct dm_dirty_log *log, region_t region,
+                            int can_block)
+{
+       int r;
+       uint64_t region64 = region;
+       int64_t in_sync;
+       size_t rdata_size;
+       struct log_c *lc = log->context;
+
+       /*
+        * We can never respond directly - even if in_sync_hint is
+        * set.  This is because another machine could see a device
+        * failure and mark the region out-of-sync.  If we don't go
+        * to userspace to ask, we might think the region is in-sync
+        * and allow a read to pick up data that is stale.  (This is
+        * very unlikely if a device actually fails; but it is very
+        * likely if a connection to one device from one machine fails.)
+        *
+        * There still might be a problem if the mirror caches the region
+        * state as in-sync... but then this call would not be made.  So,
+        * that is a mirror problem.
+        */
+       if (!can_block)
+               return -EWOULDBLOCK;
+
+       rdata_size = sizeof(in_sync);
+       r = userspace_do_request(lc, lc->uuid, DM_ULOG_IN_SYNC,
+                                (char *)&region64, sizeof(region64),
+                                (char *)&in_sync, &rdata_size);
+       return (r) ? 0 : (int)in_sync;
+}
+
+/*
+ * userspace_flush
+ *
+ * This function is ok to block.
+ * The flush happens in two stages.  First, it sends all
+ * clear/mark requests that are on the list.  Then it
+ * tells the server to commit them.  This gives the
+ * server a chance to optimise the commit, instead of
+ * doing it for every request.
+ *
+ * Additionally, we could implement another thread that
+ * sends the requests up to the server - reducing the
+ * load on flush.  Then the flush would have less in
+ * the list and be responsible for the finishing commit.
+ *
+ * Returns: 0 on success, < 0 on failure
+ */
+static int userspace_flush(struct dm_dirty_log *log)
+{
+       int r = 0;
+       unsigned long flags;
+       struct log_c *lc = log->context;
+       LIST_HEAD(flush_list);
+       struct flush_entry *fe, *tmp_fe;
+
+       spin_lock_irqsave(&lc->flush_lock, flags);
+       list_splice_init(&lc->flush_list, &flush_list);
+       spin_unlock_irqrestore(&lc->flush_lock, flags);
+
+       if (list_empty(&flush_list))
+               return 0;
+
+       /*
+        * FIXME: Count up requests, group request types,
+        * allocate memory to stick all requests in and
+        * send to server in one go.  Failing the allocation,
+        * do it one by one.
+        */
+
+       list_for_each_entry(fe, &flush_list, list) {
+               r = userspace_do_request(lc, lc->uuid, fe->type,
+                                        (char *)&fe->region,
+                                        sizeof(fe->region),
+                                        NULL, NULL);
+               if (r)
+                       goto fail;
+       }
+
+       r = userspace_do_request(lc, lc->uuid, DM_ULOG_FLUSH,
+                                NULL, 0, NULL, NULL);
+
+fail:
+       /*
+        * We can safely remove these entries, even if failure.
+        * Calling code will receive an error and will know that
+        * the log facility has failed.
+        */
+       list_for_each_entry_safe(fe, tmp_fe, &flush_list, list) {
+               list_del(&fe->list);
+               mempool_free(fe, flush_entry_pool);
+       }
+
+       if (r)
+               dm_table_event(lc->ti->table);
+
+       return r;
+}
+
+/*
+ * userspace_mark_region
+ *
+ * This function should avoid blocking unless absolutely required.
+ * (Memory allocation is valid for blocking.)
+ */
+static void userspace_mark_region(struct dm_dirty_log *log, region_t region)
+{
+       unsigned long flags;
+       struct log_c *lc = log->context;
+       struct flush_entry *fe;
+
+       /* Wait for an allocation, but _never_ fail */
+       fe = mempool_alloc(flush_entry_pool, GFP_NOIO);
+       BUG_ON(!fe);
+
+       spin_lock_irqsave(&lc->flush_lock, flags);
+       fe->type = DM_ULOG_MARK_REGION;
+       fe->region = region;
+       list_add(&fe->list, &lc->flush_list);
+       spin_unlock_irqrestore(&lc->flush_lock, flags);
+
+       return;
+}
+
+/*
+ * userspace_clear_region
+ *
+ * This function must not block.
+ * So, the alloc can't block.  In the worst case, it is ok to
+ * fail.  It would simply mean we can't clear the region.
+ * Does nothing to current sync context, but does mean
+ * the region will be re-sync'ed on a reload of the mirror
+ * even though it is in-sync.
+ */
+static void userspace_clear_region(struct dm_dirty_log *log, region_t region)
+{
+       unsigned long flags;
+       struct log_c *lc = log->context;
+       struct flush_entry *fe;
+
+       /*
+        * If we fail to allocate, we skip the clearing of
+        * the region.  This doesn't hurt us in any way, except
+        * to cause the region to be resync'ed when the
+        * device is activated next time.
+        */
+       fe = mempool_alloc(flush_entry_pool, GFP_ATOMIC);
+       if (!fe) {
+               DMERR("Failed to allocate memory to clear region.");
+               return;
+       }
+
+       spin_lock_irqsave(&lc->flush_lock, flags);
+       fe->type = DM_ULOG_CLEAR_REGION;
+       fe->region = region;
+       list_add(&fe->list, &lc->flush_list);
+       spin_unlock_irqrestore(&lc->flush_lock, flags);
+
+       return;
+}
+
+/*
+ * userspace_get_resync_work
+ *
+ * Get a region that needs recovery.  It is valid to return
+ * an error for this function.
+ *
+ * Returns: 1 if region filled, 0 if no work, <0 on error
+ */
+static int userspace_get_resync_work(struct dm_dirty_log *log, region_t *region)
+{
+       int r;
+       size_t rdata_size;
+       struct log_c *lc = log->context;
+       struct {
+               int64_t i; /* 64-bit for mix arch compatibility */
+               region_t r;
+       } pkg;
+
+       if (lc->in_sync_hint >= lc->region_count)
+               return 0;
+
+       rdata_size = sizeof(pkg);
+       r = userspace_do_request(lc, lc->uuid, DM_ULOG_GET_RESYNC_WORK,
+                                NULL, 0,
+                                (char *)&pkg, &rdata_size);
+
+       *region = pkg.r;
+       return (r) ? r : (int)pkg.i;
+}
+
+/*
+ * userspace_set_region_sync
+ *
+ * Set the sync status of a given region.  This function
+ * must not fail.
+ */
+static void userspace_set_region_sync(struct dm_dirty_log *log,
+                                     region_t region, int in_sync)
+{
+       int r;
+       struct log_c *lc = log->context;
+       struct {
+               region_t r;
+               int64_t i;
+       } pkg;
+
+       pkg.r = region;
+       pkg.i = (int64_t)in_sync;
+
+       r = userspace_do_request(lc, lc->uuid, DM_ULOG_SET_REGION_SYNC,
+                                (char *)&pkg, sizeof(pkg),
+                                NULL, NULL);
+
+       /*
+        * It would be nice to be able to report failures.
+        * However, it is easy emough to detect and resolve.
+        */
+       return;
+}
+
+/*
+ * userspace_get_sync_count
+ *
+ * If there is any sort of failure when consulting the server,
+ * we assume that the sync count is zero.
+ *
+ * Returns: sync count on success, 0 on failure
+ */
+static region_t userspace_get_sync_count(struct dm_dirty_log *log)
+{
+       int r;
+       size_t rdata_size;
+       uint64_t sync_count;
+       struct log_c *lc = log->context;
+
+       rdata_size = sizeof(sync_count);
+       r = userspace_do_request(lc, lc->uuid, DM_ULOG_GET_SYNC_COUNT,
+                                NULL, 0,
+                                (char *)&sync_count, &rdata_size);
+
+       if (r)
+               return 0;
+
+       if (sync_count >= lc->region_count)
+               lc->in_sync_hint = lc->region_count;
+
+       return (region_t)sync_count;
+}
+
+/*
+ * userspace_status
+ *
+ * Returns: amount of space consumed
+ */
+static int userspace_status(struct dm_dirty_log *log, status_type_t status_type,
+                           char *result, unsigned maxlen)
+{
+       int r = 0;
+       size_t sz = (size_t)maxlen;
+       struct log_c *lc = log->context;
+
+       switch (status_type) {
+       case STATUSTYPE_INFO:
+               r = userspace_do_request(lc, lc->uuid, DM_ULOG_STATUS_INFO,
+                                        NULL, 0,
+                                        result, &sz);
+
+               if (r) {
+                       sz = 0;
+                       DMEMIT("%s 1 COM_FAILURE", log->type->name);
+               }
+               break;
+       case STATUSTYPE_TABLE:
+               sz = 0;
+               DMEMIT("%s %u %s %s", log->type->name, lc->usr_argc + 1,
+                      lc->uuid, lc->usr_argv_str);
+               break;
+       }
+       return (r) ? 0 : (int)sz;
+}
+
+/*
+ * userspace_is_remote_recovering
+ *
+ * Returns: 1 if region recovering, 0 otherwise
+ */
+static int userspace_is_remote_recovering(struct dm_dirty_log *log,
+                                         region_t region)
+{
+       int r;
+       uint64_t region64 = region;
+       struct log_c *lc = log->context;
+       static unsigned long long limit;
+       struct {
+               int64_t is_recovering;
+               uint64_t in_sync_hint;
+       } pkg;
+       size_t rdata_size = sizeof(pkg);
+
+       /*
+        * Once the mirror has been reported to be in-sync,
+        * it will never again ask for recovery work.  So,
+        * we can safely say there is not a remote machine
+        * recovering if the device is in-sync.  (in_sync_hint
+        * must be reset at resume time.)
+        */
+       if (region < lc->in_sync_hint)
+               return 0;
+       else if (jiffies < limit)
+       &nb